1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
module Lib.DropDown exposing (Config, init, sub, toggle, view)
import Browser.Events as E
import Json.Decode as JD
import Html exposing (..)
import Html.Attributes exposing (..)
import Lib.Api as Api
import Lib.Html exposing (..)
type alias Config msg =
{ id : String
, opened : Bool
, toggle : Bool -> msg
}
-- Returns True if the element matches the target id.
onClickOutsideParse : String -> JD.Decoder Bool
onClickOutsideParse id =
JD.oneOf
[ JD.field "id" JD.string |> JD.andThen (\s -> if id == s then JD.succeed True else JD.fail "")
, JD.field "parentNode" <| JD.lazy <| \_ -> onClickOutsideParse id
, JD.succeed False
]
-- onClick subscription that only fires when the click was outside of the element with the given id
onClickOutside : String -> msg -> Sub msg
onClickOutside id msg =
E.onClick (JD.field "target" (onClickOutsideParse id) |> JD.andThen (\b -> if b then JD.fail "" else JD.succeed msg))
init : String -> (Bool -> msg) -> Config msg
init id msg =
{ id = id
, opened = False
, toggle = msg
}
sub : Config msg -> Sub msg
sub conf = if conf.opened then onClickOutside conf.id (conf.toggle False) else Sub.none
toggle : Config msg -> Bool -> Config msg
toggle conf opened = { conf | opened = opened }
view : Config msg -> Api.State -> Html msg -> (() -> List (Html msg)) -> Html msg
view conf status lbl cont =
div [ class "elm_dd", id conf.id ]
[ a [ href "#", onClickD (conf.toggle (not conf.opened)) ] <|
case status of
Api.Normal -> [ lbl, span [] [ i [] [ text "▾" ] ] ]
Api.Loading -> [ lbl, span [] [ span [ class "spinner" ] [] ] ]
Api.Error e -> [ b [ class "standout" ] [ text "error" ], span [] [ i [] [ text "▾" ] ] ]
, div [ classList [("hidden", not conf.opened)] ]
<| if conf.opened then cont () else [ text "" ]
]
|