all repos

onasty @ 312d08fc09acfbc338373ec3b6257fc6d6fb4118

a one-time notes service

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

Olexandr Smirnov Olexandr Smirnov
ss2316544@gmail.com
feat(web): add account settings (#190)..., 9 months ago
1
module Components.Form exposing (ButtonStyle(..), CanBeClicked, InputStyle(..), button, input, 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
94
95
button : { text : String, disabled : Bool, onClick : msg, style : ButtonStyle } -> Html msg
96
button opts =
97
    H.button
98
        [ A.type_ "button"
99
        , E.onClick opts.onClick
100
        , A.class (buttonStyleToClass opts.style "")
101
        , A.disabled opts.disabled
102
        ]
103
        [ H.text opts.text ]
104
105
106
submitButton : { text : String, disabled : Bool, class : String, style : ButtonStyle } -> Html msg
107
submitButton opts =
108
    H.button
109
        [ A.type_ "submit"
110
        , A.class (buttonStyleToClass opts.style opts.class)
111
        , A.disabled opts.disabled
112
        ]
113
        [ H.text opts.text ]
114
115
116
buttonStyleToClass : ButtonStyle -> String -> String
117
buttonStyleToClass style appendClasses =
118
    case style of
119
        Primary canBeClicked ->
120
            getButtonClasses canBeClicked
121
                appendClasses
122
                "px-6 py-2 bg-gray-300 text-gray-500 rounded-md cursor-not-allowed transition-colors"
123
                "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"
124
125
        PrimaryReverse canBeClicked ->
126
            getButtonClasses canBeClicked
127
                appendClasses
128
                "items-center gap-3 px-3 py-2 text-left rounded-md transition-colors bg-black text-white"
129
                "items-center gap-3 px-3 py-2 text-left rounded-md transition-colors text-gray-700 hover:bg-gray-100"
130
131
        SecondaryDanger ->
132
            "text-gray-600 hover:text-red-600 transition-colors"
133
134
        Secondary canBeClicked ->
135
            getButtonClasses canBeClicked
136
                appendClasses
137
                "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"
138
                "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"
139
140
        SecondaryDisabled canBeClicked ->
141
            getButtonClasses canBeClicked
142
                appendClasses
143
                "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"
144
                "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"
145
146
147
getButtonClasses : Bool -> String -> String -> String -> String
148
getButtonClasses cond extend whenTrue whenFalse =
149
    let
150
        cls =
151
            if String.isEmpty extend then
152
                ""
153
154
            else
155
                " " ++ extend
156
    in
157
    if cond then
158
        whenTrue ++ cls
159
160
    else
161
        whenFalse ++ cls