all repos

onasty @ e7717425f374fb41362ff0ec6cdccedbdfd6ba18

a one-time notes service

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

Olexandr Smirnov Olexandr Smirnov
ss2316544@gmail.com
web: form validation (#184)..., 10 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
    | Secondary CanBeClicked
90
    | SecondaryDisabled CanBeClicked
91
    | SecondaryDanger
92
93
94
button : { text : String, disabled : Bool, onClick : msg, style : ButtonStyle } -> Html msg
95
button opts =
96
    H.button
97
        [ A.type_ "button"
98
        , E.onClick opts.onClick
99
        , A.class (buttonStyleToClass opts.style "")
100
        , A.disabled opts.disabled
101
        ]
102
        [ H.text opts.text ]
103
104
105
submitButton : { text : String, disabled : Bool, class : String, style : ButtonStyle } -> Html msg
106
submitButton opts =
107
    H.button
108
        [ A.type_ "submit"
109
        , A.class (buttonStyleToClass opts.style opts.class)
110
        , A.disabled opts.disabled
111
        ]
112
        [ H.text opts.text ]
113
114
115
buttonStyleToClass : ButtonStyle -> String -> String
116
buttonStyleToClass style appendClasses =
117
    case style of
118
        Primary canBeClicked ->
119
            getButtonClasses canBeClicked
120
                appendClasses
121
                "px-6 py-2 bg-gray-300 text-gray-500 rounded-md cursor-not-allowed transition-colors"
122
                "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"
123
124
        SecondaryDanger ->
125
            "text-gray-600 hover:text-red-600 transition-colors"
126
127
        Secondary canBeClicked ->
128
            getButtonClasses canBeClicked
129
                appendClasses
130
                "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"
131
                "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"
132
133
        SecondaryDisabled canBeClicked ->
134
            getButtonClasses canBeClicked
135
                appendClasses
136
                "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"
137
                "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"
138
139
140
getButtonClasses : Bool -> String -> String -> String -> String
141
getButtonClasses cond extend whenTrue whenFalse =
142
    let
143
        cls =
144
            if String.isEmpty extend then
145
                ""
146
147
            else
148
                " " ++ extend
149
    in
150
    if cond then
151
        whenTrue ++ cls
152
153
    else
154
        whenFalse ++ cls