all repos

onasty @ fe34b44ed64971ea123c26077bd570d15d2352f9

a one-time notes service

onasty/web/src/Components/Form.elm (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
feat(web): add oauth login (#213)..., 8 months ago
1
module Components.Form exposing (ButtonStyle(..), CanBeClicked, InputStyle(..), button, input, oauthButton, submitButton)
2
3
import Html as H exposing (Html)
4
import Html.Attributes as A
5
import Html.Events as E
6
7
8
9
-- INPUT
10
11
12
type InputStyle
13
    = Simple
14
    | Complex
15
        { prefix : String
16
        , helpText : String
17
        }
18
19
20
input :
21
    { id : String
22
    , field : field
23
    , type_ : String
24
    , value : String
25
    , label : String
26
    , placeholder : String
27
    , required : Bool
28
    , onInput : String -> msg
29
    , style : InputStyle
30
    , error : Maybe String
31
    }
32
    -> Html msg
33
input opts =
34
    let
35
        style =
36
            case opts.style of
37
                Simple ->
38
                    { prefix = H.text "", help = H.text "" }
39
40
                Complex complex ->
41
                    { prefix = H.span [ A.class "text-gray-500 text-md whitespace-nowrap" ] [ H.text complex.prefix ]
42
                    , help = H.p [ A.class "text-xs text-gray-500 mt-1" ] [ H.text complex.helpText ]
43
                    }
44
45
        error =
46
            case opts.error of
47
                Nothing ->
48
                    { element = H.text "", inputAdditionalClasses = "border-gray-300 focus:ring-black " }
49
50
                Just err ->
51
                    { element = H.p [ A.class "text-red-600 text-xs mt-1" ] [ H.text err ]
52
                    , inputAdditionalClasses = " border-red-400 focus:ring-red-500"
53
                    }
54
    in
55
    H.div [ A.class "space-y-2" ]
56
        [ H.label
57
            [ A.for opts.id
58
            , A.class "block text-sm font-medium text-gray-700"
59
            ]
60
            [ H.text opts.label ]
61
        , H.div [ A.class "flex items-center" ]
62
            [ style.prefix
63
            , H.input
64
                [ A.class ("w-full px-3 py-2 border rounded-md shadow-sm focus:outline-none focus:ring-2 focus:border-transparent transition-colors" ++ error.inputAdditionalClasses)
65
                , A.type_ opts.type_
66
                , A.value opts.value
67
                , A.id opts.id
68
                , A.placeholder opts.placeholder
69
                , A.required opts.required
70
                , E.onInput opts.onInput
71
                ]
72
                []
73
            ]
74
        , error.element
75
        , style.help
76
        ]
77
78
79
80
-- BUTTON
81
82
83
type alias CanBeClicked =
84
    Bool
85
86
87
type ButtonStyle
88
    = Primary CanBeClicked
89
    | PrimaryReverse CanBeClicked
90
    | Secondary CanBeClicked
91
    | SecondaryDisabled CanBeClicked
92
    | SecondaryDanger
93
    | OauthButton CanBeClicked
94
95
96
button : { text : String, disabled : Bool, onClick : msg, style : ButtonStyle } -> Html msg
97
button opts =
98
    H.button
99
        [ A.type_ "button"
100
        , E.onClick opts.onClick
101
        , A.class (buttonStyleToClass opts.style "")
102
        , A.disabled opts.disabled
103
        ]
104
        [ H.text opts.text ]
105
106
107
submitButton : { text : String, disabled : Bool, class : String, style : ButtonStyle } -> Html msg
108
submitButton opts =
109
    H.button
110
        [ A.type_ "submit"
111
        , A.class (buttonStyleToClass opts.style opts.class)
112
        , A.disabled opts.disabled
113
        ]
114
        [ H.text opts.text ]
115
116
117
oauthButton : { text : String, disabled : Bool, onClick : msg, iconURL : String } -> Html msg
118
oauthButton { text, disabled, onClick, iconURL } =
119
    H.button
120
        [ A.type_ "button"
121
        , A.class (buttonStyleToClass (OauthButton (not disabled)) "mt-2")
122
        , A.disabled disabled
123
        , E.onClick onClick
124
        ]
125
        [ H.div [ A.class "flex" ]
126
            [ H.img [ A.class "w-5 h-5 mr-3", A.src iconURL ] []
127
            , H.text text
128
            ]
129
        ]
130
131
132
buttonStyleToClass : ButtonStyle -> String -> String
133
buttonStyleToClass style appendClasses =
134
    case style of
135
        Primary canBeClicked ->
136
            getButtonClasses canBeClicked
137
                appendClasses
138
                "px-6 py-2 bg-gray-300 text-gray-500 rounded-md cursor-not-allowed transition-colors"
139
                "px-6 py-2 bg-black text-white rounded-md hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 transition-colors"
140
141
        PrimaryReverse canBeClicked ->
142
            getButtonClasses canBeClicked
143
                appendClasses
144
                "items-center gap-3 px-3 py-2 text-left rounded-md transition-colors bg-black text-white"
145
                "items-center gap-3 px-3 py-2 text-left rounded-md transition-colors text-gray-700 hover:bg-gray-100"
146
147
        SecondaryDanger ->
148
            "text-gray-600 hover:text-red-600 transition-colors"
149
150
        Secondary canBeClicked ->
151
            getButtonClasses canBeClicked
152
                appendClasses
153
                "px-4 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 transition-colors bg-green-100 border-green-300 text-green-700"
154
                "px-4 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 transition-colors border-gray-300 text-gray-700 hover:bg-gray-50"
155
156
        SecondaryDisabled canBeClicked ->
157
            getButtonClasses canBeClicked
158
                appendClasses
159
                "w-full px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 transition-colors mt-3 border border-gray-300 text-gray-400 cursor-not-allowed"
160
                "w-full px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 transition-colors mt-3 border border-gray-300 text-gray-700 hover:bg-gray-50"
161
162
        OauthButton canBeClicked ->
163
            getButtonClasses canBeClicked
164
                appendClasses
165
                "w-full flex items-center justify-center gap-3 px-4 py-2 rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 bg-white hover:bg-gray-50 border border-gray-300 text-gray-700"
166
                "w-full flex items-center justify-center gap-3 px-4 py-2 rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 bg-white hover:bg-gray-50 border border-gray-300 text-gray-700 opacity-50 cursor-not-allowed"
167
168
169
getButtonClasses : Bool -> String -> String -> String -> String
170
getButtonClasses cond extend whenTrue whenFalse =
171
    let
172
        cls =
173
            if String.isEmpty extend then
174
                ""
175
176
            else
177
                " " ++ extend
178
    in
179
    if cond then
180
        whenTrue ++ cls
181
182
    else
183
        whenFalse ++ cls