mirror of
https://github.com/nunntom/elm-ui-select.git
synced 2024-11-26 13:27:26 +03:00
Fix for filtering running unnecessarily if items set from view
This commit is contained in:
parent
39a3959c86
commit
4254c3eb96
@ -3,6 +3,7 @@ module Internal.Model exposing
|
||||
, blur
|
||||
, clear
|
||||
, closeMenu
|
||||
, currentFilteredOptions
|
||||
, highlightIndex
|
||||
, init
|
||||
, isFocused
|
||||
@ -127,9 +128,9 @@ wasHighlightedByMouse (Model { highlightedByMouse }) =
|
||||
highlightedByMouse
|
||||
|
||||
|
||||
toFilteredOptions : Maybe Int -> (a -> String) -> Maybe (Filter a) -> Model a -> List (Option a)
|
||||
toFilteredOptions minInputLength itemToString filter (Model model) =
|
||||
if not model.focused then
|
||||
toFilteredOptions : Bool -> Maybe Int -> (a -> String) -> Maybe (Filter a) -> Model a -> List (Option a)
|
||||
toFilteredOptions onlyIfFocused minInputLength itemToString filter (Model model) =
|
||||
if onlyIfFocused && not model.focused then
|
||||
[]
|
||||
|
||||
else
|
||||
@ -160,6 +161,11 @@ toFilteredOptions_ itemToString filter (Model model) =
|
||||
|> Filter.filterOptions model.inputValue filter
|
||||
|
||||
|
||||
currentFilteredOptions : Model a -> List (Option a)
|
||||
currentFilteredOptions (Model { filteredOptions }) =
|
||||
Maybe.withDefault [] filteredOptions
|
||||
|
||||
|
||||
toInputElementId : Model a -> String
|
||||
toInputElementId (Model { id }) =
|
||||
id ++ "-input"
|
||||
@ -297,6 +303,7 @@ selectOption opt (Model model) =
|
||||
{ model
|
||||
| menuOpen = False
|
||||
, selected = Just (Option.toItem opt)
|
||||
, highlighted = Nothing
|
||||
, inputValue = Option.toString opt
|
||||
, applyFilter = False
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ import Internal.Option exposing (Option)
|
||||
type Msg a
|
||||
= InputChanged String (List (Option a))
|
||||
| OptionClicked (Option a)
|
||||
| InputFocused (Maybe Int)
|
||||
| GotNewFilteredOptions (List (Option a))
|
||||
| InputClicked (Maybe Int)
|
||||
| InputFocused (Maybe ( List a, List (Option a) ))
|
||||
| GotNewFilteredOptions ( List a, List (Option a) )
|
||||
| InputClicked
|
||||
| InputLostFocus
|
||||
{ clearInputValue : Bool
|
||||
, selectExactMatch : Bool
|
||||
|
@ -3,7 +3,7 @@ module Internal.Update exposing (update)
|
||||
import Internal.Effect as Effect exposing (Effect)
|
||||
import Internal.Model as Model exposing (Model)
|
||||
import Internal.Msg exposing (Msg(..))
|
||||
import Internal.Option exposing (Option)
|
||||
import Internal.Option as Option exposing (Option)
|
||||
import Internal.RequestState exposing (RequestState(..))
|
||||
import Internal.UpdateOptions exposing (UpdateOptions)
|
||||
|
||||
@ -73,20 +73,29 @@ update_ { request, requestMinInputLength, debounceRequest, onFocus, onLoseFocus,
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
InputFocused maybeIdx ->
|
||||
onFocusMenu tagger maybeIdx (request /= Nothing) model
|
||||
InputFocused maybeOptions ->
|
||||
(case maybeOptions of
|
||||
Just ( items, options ) ->
|
||||
Model.setItems items model
|
||||
|> Model.setFilteredOptions options
|
||||
|
||||
Nothing ->
|
||||
model
|
||||
)
|
||||
|> onFocusMenu tagger (request /= Nothing)
|
||||
|> withEffect (\_ -> Effect.emitJust onFocus)
|
||||
|
||||
InputClicked maybeIdx ->
|
||||
onFocusMenu tagger maybeIdx (request /= Nothing) model
|
||||
InputClicked ->
|
||||
onFocusMenu tagger (request /= Nothing) model
|
||||
|
||||
InputLostFocus config filteredOptions ->
|
||||
( Model.blur config (request /= Nothing) filteredOptions model
|
||||
, Effect.emitJust onLoseFocus
|
||||
)
|
||||
|
||||
GotNewFilteredOptions options ->
|
||||
( Model.setFilteredOptions options model
|
||||
GotNewFilteredOptions ( items, options ) ->
|
||||
( Model.setItems items model
|
||||
|> Model.setFilteredOptions options
|
||||
, Effect.None
|
||||
)
|
||||
|
||||
@ -97,7 +106,7 @@ update_ { request, requestMinInputLength, debounceRequest, onFocus, onLoseFocus,
|
||||
|
||||
KeyDown selectOnTab filteredOptions key ->
|
||||
Model.setFilteredOptions filteredOptions model
|
||||
|> handleKey selectOnTab tagger key filteredOptions
|
||||
|> handleKey selectOnTab tagger (request /= Nothing) key filteredOptions
|
||||
|
||||
GotContainerAndMenuElements maybeIdx result ->
|
||||
( model
|
||||
@ -167,14 +176,19 @@ update_ { request, requestMinInputLength, debounceRequest, onFocus, onLoseFocus,
|
||||
( model, Effect.none )
|
||||
|
||||
|
||||
onFocusMenu : (Msg a -> msg) -> Maybe Int -> Bool -> Model a -> ( Model a, Effect effect msg )
|
||||
onFocusMenu tagger maybeOptionIdx hasRequest model =
|
||||
onFocusMenu : (Msg a -> msg) -> Bool -> Model a -> ( Model a, Effect effect msg )
|
||||
onFocusMenu tagger hasRequest model =
|
||||
let
|
||||
selectedIdx =
|
||||
Model.toValue model
|
||||
|> Maybe.andThen (Option.findIndex (Model.currentFilteredOptions model))
|
||||
in
|
||||
( Model.setFocused True model
|
||||
|> Model.highlightIndex maybeOptionIdx False
|
||||
|> Model.highlightIndex selectedIdx False
|
||||
, if not hasRequest || Model.toRequestState model == Just Success then
|
||||
Effect.batch
|
||||
[ Effect.ScrollMenuToTop (tagger NoOp) (Model.toMenuElementId model)
|
||||
, getContainerAndMenuElementsEffect maybeOptionIdx tagger model
|
||||
, getContainerAndMenuElementsEffect selectedIdx tagger model
|
||||
]
|
||||
|
||||
else
|
||||
@ -182,8 +196,8 @@ onFocusMenu tagger maybeOptionIdx hasRequest model =
|
||||
)
|
||||
|
||||
|
||||
handleKey : Bool -> (Msg a -> msg) -> String -> List (Option a) -> Model a -> ( Model a, Effect effect msg )
|
||||
handleKey selectOnTab tagger key filteredOptions model =
|
||||
handleKey : Bool -> (Msg a -> msg) -> Bool -> String -> List (Option a) -> Model a -> ( Model a, Effect effect msg )
|
||||
handleKey selectOnTab tagger hasRequest key filteredOptions model =
|
||||
let
|
||||
selectHighlighted =
|
||||
case Model.toHighlighted model |> Maybe.andThen (\idx -> getAt idx filteredOptions) of
|
||||
@ -195,16 +209,16 @@ handleKey selectOnTab tagger key filteredOptions model =
|
||||
in
|
||||
case key of
|
||||
"ArrowDown" ->
|
||||
moveHighlight tagger (Basics.min (List.length filteredOptions - 1) (Maybe.withDefault -1 (Model.toHighlighted model) + 1)) model
|
||||
moveHighlight tagger hasRequest (Basics.min (List.length filteredOptions - 1) (Maybe.withDefault -1 (Model.toHighlighted model) + 1)) model
|
||||
|
||||
"ArrowUp" ->
|
||||
moveHighlight tagger (Basics.max 0 (Maybe.withDefault 0 (Model.toHighlighted model) - 1)) model
|
||||
moveHighlight tagger hasRequest (Basics.max 0 (Maybe.withDefault 0 (Model.toHighlighted model) - 1)) model
|
||||
|
||||
"PageDown" ->
|
||||
moveHighlight tagger (Basics.min (List.length filteredOptions - 1) (Maybe.withDefault -1 (Model.toHighlighted model) + 10)) model
|
||||
moveHighlight tagger hasRequest (Basics.min (List.length filteredOptions - 1) (Maybe.withDefault -1 (Model.toHighlighted model) + 10)) model
|
||||
|
||||
"PageUp" ->
|
||||
moveHighlight tagger (Basics.max 0 (Maybe.withDefault 0 (Model.toHighlighted model) - 10)) model
|
||||
moveHighlight tagger hasRequest (Basics.max 0 (Maybe.withDefault 0 (Model.toHighlighted model) - 10)) model
|
||||
|
||||
"Enter" ->
|
||||
selectHighlighted
|
||||
@ -223,17 +237,15 @@ handleKey selectOnTab tagger key filteredOptions model =
|
||||
( model, Effect.none )
|
||||
|
||||
|
||||
moveHighlight : (Msg a -> msg) -> Int -> Model a -> ( Model a, Effect effect msg )
|
||||
moveHighlight tagger newHighlighted model =
|
||||
moveHighlight : (Msg a -> msg) -> Bool -> Int -> Model a -> ( Model a, Effect effect msg )
|
||||
moveHighlight tagger hasRequest newHighlighted model =
|
||||
if Model.isOpen model then
|
||||
( Model.highlightIndex (Just newHighlighted) False model
|
||||
, getContainerAndMenuElementsEffect (Just newHighlighted) tagger model
|
||||
)
|
||||
|
||||
else
|
||||
( model
|
||||
, getContainerAndMenuElementsEffect Nothing tagger model
|
||||
)
|
||||
onFocusMenu tagger hasRequest model
|
||||
|
||||
|
||||
getContainerAndMenuElementsEffect : Maybe Int -> (Msg a -> msg) -> Model a -> Effect effect msg
|
||||
|
@ -81,7 +81,7 @@ view attribs v =
|
||||
toElement : Model a -> ViewConfigInternal a msg -> Element msg
|
||||
toElement model config =
|
||||
toElement_ (Model.toMenuPlacement config.menuMaxHeight config.menuPlacement model)
|
||||
(Model.toFilteredOptions config.minInputLength config.itemToString config.filter model)
|
||||
(Model.toFilteredOptions True config.minInputLength config.itemToString config.filter model)
|
||||
model
|
||||
config
|
||||
|
||||
@ -141,9 +141,14 @@ toElement_ placement filteredOptions model config =
|
||||
|
||||
else
|
||||
[]
|
||||
, if Model.isFocused model && Model.requiresNewFilteredOptions model then
|
||||
[ Element.htmlAttribute (Html.Events.on "focusin" (Decode.succeed (GotNewFilteredOptions filteredOptions |> config.onChange)))
|
||||
, Element.htmlAttribute (Html.Events.on "mousemove" (Decode.succeed (GotNewFilteredOptions filteredOptions |> config.onChange)))
|
||||
, if Model.requiresNewFilteredOptions model then
|
||||
let
|
||||
options _ =
|
||||
optionsUpdate model config filteredOptions
|
||||
in
|
||||
[ Element.htmlAttribute <| Html.Events.on "keydown" (Decode.lazy (\_ -> Decode.succeed (GotNewFilteredOptions (options ()) |> config.onChange)))
|
||||
, Element.htmlAttribute <| Html.Events.on "touchstart" (Decode.lazy (\_ -> Decode.succeed (GotNewFilteredOptions (options ()) |> config.onChange)))
|
||||
, Element.htmlAttribute <| Html.Events.on "mousemove" (Decode.lazy (\_ -> Decode.succeed (GotNewFilteredOptions (options ()) |> config.onChange)))
|
||||
]
|
||||
|
||||
else
|
||||
@ -155,16 +160,15 @@ toElement_ placement filteredOptions model config =
|
||||
|
||||
inputView : List (Option a) -> Model a -> ViewConfigInternal a msg -> Element msg
|
||||
inputView filteredOptions model config =
|
||||
let
|
||||
selectedIdx =
|
||||
Model.toValue model
|
||||
|> Maybe.andThen (Option.findIndex filteredOptions)
|
||||
in
|
||||
Input.text
|
||||
(List.concat
|
||||
[ config.inputAttribs
|
||||
, [ Events.onFocus (InputFocused selectedIdx |> config.onChange)
|
||||
, Events.onClick (InputClicked selectedIdx |> config.onChange)
|
||||
, if Model.requiresNewFilteredOptions model then
|
||||
[ Element.htmlAttribute (Html.Events.on "focus" (Decode.lazy (\_ -> Decode.succeed (InputFocused (Just <| optionsUpdate model config filteredOptions) |> config.onChange)))) ]
|
||||
|
||||
else
|
||||
[ Events.onFocus (InputFocused Nothing |> config.onChange) ]
|
||||
, [ Events.onClick (InputClicked |> config.onChange)
|
||||
, Events.onLoseFocus
|
||||
(config.onChange
|
||||
(InputLostFocus
|
||||
@ -190,7 +194,7 @@ inputView filteredOptions model config =
|
||||
\v ->
|
||||
InputChanged v
|
||||
(Model.setInputValue v model
|
||||
|> Model.toFilteredOptions config.minInputLength config.itemToString config.filter
|
||||
|> Model.toFilteredOptions False config.minInputLength config.itemToString config.filter
|
||||
)
|
||||
|> config.onChange
|
||||
, text =
|
||||
@ -205,6 +209,21 @@ inputView filteredOptions model config =
|
||||
}
|
||||
|
||||
|
||||
optionsUpdate : Model a -> ViewConfigInternal a msg -> List (Option a) -> ( List a, List (Option a) )
|
||||
optionsUpdate model config filteredOptions =
|
||||
( Model.toItems model
|
||||
, if List.isEmpty filteredOptions then
|
||||
Model.toFilteredOptions False config.minInputLength config.itemToString config.filter model
|
||||
|
||||
else
|
||||
filteredOptions
|
||||
)
|
||||
|
||||
|
||||
|
||||
--)
|
||||
|
||||
|
||||
inputAccessibilityAttributes : List (Option a) -> Model a -> List (Attribute msg)
|
||||
inputAccessibilityAttributes filteredOptions model =
|
||||
[ htmlAttribute "role" "combobox"
|
||||
|
Loading…
Reference in New Issue
Block a user