diff options
Diffstat (limited to 'elm/Lib/Autocomplete.elm')
-rw-r--r-- | elm/Lib/Autocomplete.elm | 157 |
1 files changed, 109 insertions, 48 deletions
diff --git a/elm/Lib/Autocomplete.elm b/elm/Lib/Autocomplete.elm index 5c5dd33d..4c465d7c 100644 --- a/elm/Lib/Autocomplete.elm +++ b/elm/Lib/Autocomplete.elm @@ -12,8 +12,12 @@ module Lib.Autocomplete exposing , staffSource , charSource , animeSource + , resolutionSource + , engineSource + , drmSource , init , clear + , refocus , update , view ) @@ -40,6 +44,9 @@ import Gen.Producers as GP import Gen.Staff as GS import Gen.Chars as GC import Gen.Anime as GA +import Gen.Resolutions as GR +import Gen.Engines as GE +import Gen.DRM as GDRM type alias Config m a = @@ -55,6 +62,8 @@ type alias Config m a = type SearchSource m a -- API endpoint to query for completion results + Function to decode results from the API = Endpoint (String -> (GApi.Response -> m) -> Cmd m) (GApi.Response -> Maybe (List a)) + -- API endpoint that returns the full list of possible results + Function to decode results from the API + Function to match results against a query + | LazyList ((GApi.Response -> m) -> Cmd m) (GApi.Response -> Maybe (List a)) (String -> List a -> List a) -- Pure function for instant completion results | Func (String -> List a) @@ -80,20 +89,20 @@ boardSource = , view = (\i -> [ text <| Maybe.withDefault "" (lookup i.btype boardTypes) ] ++ case i.title of - Just title -> [ b [ class "grayedout" ] [ text " > " ], text title ] + Just title -> [ small [] [ text " > " ], text title ] _ -> [] ) - , key = \i -> i.btype ++ String.fromInt i.iid + , key = \i -> Maybe.withDefault i.btype i.iid } -tagtraitStatus i = - case (i.searchable, i.applicable, i.state) of - (_, _, 0) -> b [ class "grayedout" ] [ text " (awaiting approval)" ] - (_, _, 1) -> b [ class "grayedout" ] [ text " (deleted)" ] -- (not returned by the API for now) - (False, False, _) -> b [ class "grayedout" ] [ text " (meta)" ] - (True, False, _) -> b [ class "grayedout" ] [ text " (not applicable)" ] - (False, True, _) -> b [ class "grayedout" ] [ text " (not searchable)" ] +ttStatus i = + case ((i.hidden, i.locked), i.searchable, i.applicable) of + ((True, False), _, _ ) -> small [] [ text " (awaiting approval)" ] + ((True, True ), _, _ ) -> small [] [ text " (deleted)" ] -- (not returned by the API for now) + (_, False, False) -> small [] [ text " (meta)" ] + (_, True, False) -> small [] [ text " (not applicable)" ] + (_, False, True ) -> small [] [ text " (not searchable)" ] _ -> text "" @@ -103,8 +112,8 @@ tagSource = <| \x -> case x of GApi.TagResult e -> Just e _ -> Nothing - , view = \i -> [ text i.name, tagtraitStatus i ] - , key = \i -> String.fromInt i.id + , view = \i -> [ text i.name, ttStatus i ] + , key = \i -> i.id } @@ -117,11 +126,11 @@ traitSource = , view = \i -> [ case i.group_name of Nothing -> text "" - Just g -> b [ class "grayedout" ] [ text <| g ++ " / " ] + Just g -> small [] [ text <| g ++ " / " ] , text i.name - , tagtraitStatus i + , ttStatus i ] - , key = \i -> String.fromInt i.id + , key = \i -> i.id } @@ -132,34 +141,37 @@ vnSource = GApi.VNResult e -> Just e _ -> Nothing , view = \i -> - [ b [ class "grayedout" ] [ text <| "v" ++ String.fromInt i.id ++ ": " ] + [ small [] [ text <| i.id ++ ": " ] , text i.title ] - , key = \i -> String.fromInt i.id + , key = \i -> i.id } producerSource : SourceConfig m GApi.ApiProducerResult producerSource = - { source = Endpoint (\s -> GP.send { search = [s], hidden = False }) + { source = Endpoint (\s -> GP.send { search = [s] }) <| \x -> case x of GApi.ProducerResult e -> Just e _ -> Nothing , view = \i -> - [ b [ class "grayedout" ] [ text <| "p" ++ String.fromInt i.id ++ ": " ] + [ small [] [ text <| i.id ++ ": " ] , text i.name ] - , key = \i -> String.fromInt i.id + , key = \i -> i.id } staffSource : SourceConfig m GApi.ApiStaffResult staffSource = - { source = Endpoint (\s -> GS.send { search = s }) + { source = Endpoint (\s -> GS.send { search = [s] }) <| \x -> case x of GApi.StaffResult e -> Just e _ -> Nothing , view = \i -> - [ b [ class "grayedout" ] [ text <| "s" ++ String.fromInt i.id ++ ": " ] - , text i.name ] + [ langIcon i.lang + , small [] [ text <| i.id ++ ": " ] + , text i.title + , if i.alttitle == i.title then text "" else small [] [ text " ", text i.alttitle ] + ] , key = \i -> String.fromInt i.aid } @@ -171,33 +183,73 @@ charSource = GApi.CharResult e -> Just e _ -> Nothing , view = \i -> - [ b [ class "grayedout" ] [ text <| "c" ++ String.fromInt i.id ++ ": " ] - , text i.name + [ small [] [ text <| i.id ++ ": " ] + , text i.title , Maybe.withDefault (text "") <| Maybe.map (\m -> - b [ class "grayedout" ] [ text <| " (instance of c" ++ String.fromInt m.id ++ ": " ++ m.name ] + small [] [ text <| " (instance of " ++ m.id ++ ": " ++ m.title ] ) i.main ] - , key = \i -> String.fromInt i.id + , key = \i -> i.id } -animeSource : SourceConfig m GApi.ApiAnimeResult -animeSource = - { source = Endpoint (\s -> GA.send { search = s }) +animeSource : Bool -> SourceConfig m GApi.ApiAnimeResult +animeSource ref = + { source = Endpoint (\s -> GA.send { search = s, ref = ref }) <| \x -> case x of GApi.AnimeResult e -> Just e _ -> Nothing , view = \i -> - [ b [ class "grayedout" ] [ text <| "a" ++ String.fromInt i.id ++ ": " ] + [ small [] [ text <| "a" ++ String.fromInt i.id ++ ": " ] , text i.title ] , key = \i -> String.fromInt i.id } +resolutionSource : SourceConfig m GApi.ApiResolutions +resolutionSource = + { source = LazyList + (GR.send {}) + (\x -> case x of + GApi.Resolutions e -> Just e + _ -> Nothing) + (\s l -> List.filter (\v -> String.contains (String.toLower s) (String.toLower v.resolution)) l |> List.take 10) + , view = \i -> [ text i.resolution, small [] [ text <| " (" ++ String.fromInt i.count ++ ")" ] ] + , key = \i -> i.resolution + } + + +engineSource : SourceConfig m GApi.ApiEngines +engineSource = + { source = LazyList + (GE.send {}) + (\x -> case x of + GApi.Engines e -> Just e + _ -> Nothing) + (\s l -> List.filter (\v -> String.contains (String.toLower s) (String.toLower v.engine)) l |> List.take 10) + , view = \i -> [ text i.engine, small [] [ text <| " (" ++ String.fromInt i.count ++ ")" ] ] + , key = \i -> i.engine + } + + +drmSource : SourceConfig m GApi.ApiDRM +drmSource = + { source = LazyList + (GDRM.send {}) + (\x -> case x of + GApi.DRM e -> Just e + _ -> Nothing) + (\s l -> List.filter (\v -> String.contains (String.toLower s) (String.toLower v.name)) l |> List.take 10) + , view = \i -> [ text i.name, small [] [ text <| " (" ++ String.fromInt i.count ++ ")" ] ] + , key = \i -> i.name + } + + type alias Model a = { visible : Bool , value : String , results : List a + , all : Maybe (List a) -- Used by LazyList , sel : String , default : String , loading : Bool @@ -210,6 +262,7 @@ init s = { visible = False , value = s , results = [] + , all = Nothing , sel = "" , default = s , loading = False @@ -252,26 +305,26 @@ select cfg offset model = { model | sel = Maybe.withDefault "" <| Maybe.map cfg.source.key <| get nextsel } +-- Blur and focus the input on enter. +refocus : Config m a -> Cmd m +refocus cfg = Dom.blur cfg.id + |> Task.andThen (always (Dom.focus cfg.id)) + |> Task.attempt (always (cfg.wrap Noop)) + + update : Config m a -> Msg a -> Model a -> (Model a, Cmd m, Maybe a) update cfg msg model = let mod m = (m, Cmd.none, Nothing) - -- Ugly hack: blur and focus the input on enter. This does two things: - -- 1. If the user clicked on an entry (resulting in the 'Enter' message), - -- then this will cause the input to be focussed again. This is - -- convenient when adding multiple entries. - refocus = Dom.blur cfg.id - |> Task.andThen (always (Dom.focus cfg.id)) - |> Task.attempt (always (cfg.wrap Noop)) in case msg of Noop -> mod model Blur -> mod { model | visible = False } Focus -> mod { model | loading = False, visible = True } Sel s -> mod { model | sel = s } - Enter r -> (model, refocus, Just r) + Enter r -> (model, refocus cfg, Just r) - Key "Enter" -> (model, refocus, + Key "Enter" -> (model, refocus cfg, case List.filter (\i -> cfg.source.key i == model.sel) model.results |> List.head of Just x -> Just x Nothing -> List.head model.results) @@ -280,26 +333,34 @@ update cfg msg model = Key _ -> mod model Input s -> + let m = { model | value = s, default = "" } + in if String.trim s == "" - then mod { model | value = s, default = "", loading = False, results = [] } - else case cfg.source.source of + then mod { m | loading = False, results = [] } + else case (cfg.source.source) of Endpoint _ _ -> - ( { model | value = s, default = "", loading = True, wait = model.wait + 1 } + ( { m | loading = True, wait = model.wait + 1 } , Task.perform (always <| cfg.wrap <| Search <| model.wait + 1) (Process.sleep 500) , Nothing ) - Func f -> mod { model | value = s, default = "", results = f s } + LazyList e _ f -> + case (model.loading, model.all) of + (_, Just l) -> mod { m | results = f s l } + (True, _) -> mod m + (False, _) -> ({ m | loading = True }, e (cfg.wrap << Results ""), Nothing) + Func f -> mod { m | results = f s } Search i -> if model.value == "" || model.wait /= i then mod model else case cfg.source.source of Endpoint e _ -> (model, e model.value (cfg.wrap << Results model.value), Nothing) + LazyList _ _ _ -> mod model Func _ -> mod model Results s r -> mod <| - if s /= model.value then model -- Discard stale results - else case cfg.source.source of - Endpoint _ d -> { model | loading = False, results = d r |> Maybe.withDefault [] } + case cfg.source.source of + Endpoint _ d -> if s /= model.value then model else { model | loading = False, results = d r |> Maybe.withDefault [] } + LazyList _ d f -> let all = d r in { model | loading = False, all = all, results = Maybe.map (\l -> f model.value l) all |> Maybe.withDefault [] } Func _ -> model @@ -340,7 +401,7 @@ view cfg model attrs = ) in div [ class "elm_dd", class "search", style "width" "300px" ] - [ div [ classList [("hidden", not visible)] ] [ Keyed.node "ul" [] <| msg ++ List.map item model.results ] - , input + [ div [ classList [("hidden", not visible)] ] [ div [] [ Keyed.node "ul" [] <| msg ++ List.map item model.results ] ] + , Html.form [] [ input ] , span [ class "spinner", classList [("hidden", not model.loading)] ] [] ] |