mirror of
https://github.com/nunntom/elm-ui-select.git
synced 2024-11-26 13:27:26 +03:00
Tag request responses to avoid race conditions
This commit is contained in:
parent
43ee7ff1ba
commit
cda9089d43
@ -100,7 +100,7 @@ performEffect effect =
|
||||
Select.Effect.performWithRequest performEffect selectEffect
|
||||
|
||||
FetchCocktails query ->
|
||||
fetchCocktails (Select.gotRequestResponse >> SelectMsg) query
|
||||
fetchCocktails (Select.gotRequestResponse query >> SelectMsg) query
|
||||
|
||||
|
||||
|
||||
|
@ -71,7 +71,7 @@ fetchCocktails query =
|
||||
Http.get
|
||||
{ url = "https://thecocktaildb.com/api/json/v1/1/search.php?s=" ++ String.replace " " "+" query
|
||||
, expect =
|
||||
Http.expectJson Select.gotRequestResponse
|
||||
Http.expectJson (Select.gotRequestResponse query)
|
||||
(Decode.field "drinks"
|
||||
(Decode.oneOf
|
||||
[ Decode.list cocktailDecoder
|
||||
|
@ -5,12 +5,14 @@ module Internal.Model exposing
|
||||
, closeMenu
|
||||
, highlightIndex
|
||||
, init
|
||||
, isFocused
|
||||
, isLoading
|
||||
, isOpen
|
||||
, isRequestFailed
|
||||
, openMenu
|
||||
, selectOption
|
||||
, setElements
|
||||
, setFocused
|
||||
, setInputValue
|
||||
, setItems
|
||||
, setRequestState
|
||||
@ -59,6 +61,7 @@ type alias InternalState a =
|
||||
, menuElement : Maybe Dom.Element
|
||||
, requestState : Maybe RequestState
|
||||
, applyFilter : Bool
|
||||
, focused : Bool
|
||||
}
|
||||
|
||||
|
||||
@ -79,6 +82,7 @@ init id =
|
||||
, menuElement = Nothing
|
||||
, requestState = Nothing
|
||||
, applyFilter = False
|
||||
, focused = False
|
||||
}
|
||||
|
||||
|
||||
@ -144,7 +148,10 @@ toOptionElementId (Model { id }) idx =
|
||||
|
||||
toOptionState : Model a -> ( Int, a ) -> OptionState
|
||||
toOptionState (Model { highlighted, selected }) ( idx, a ) =
|
||||
if highlighted == idx then
|
||||
if highlighted == idx && selected == Just a then
|
||||
SelectedAndHighlighted
|
||||
|
||||
else if highlighted == idx then
|
||||
Highlighted
|
||||
|
||||
else if selected == Just a then
|
||||
@ -163,6 +170,11 @@ isOpen (Model { menuOpen }) =
|
||||
menuOpen
|
||||
|
||||
|
||||
isFocused : Model a -> Bool
|
||||
isFocused (Model { focused }) =
|
||||
focused
|
||||
|
||||
|
||||
isLoading : Model a -> Bool
|
||||
isLoading (Model { requestState }) =
|
||||
requestState == Just Loading
|
||||
@ -277,6 +289,11 @@ applyFilter v (Model model) =
|
||||
Model { model | applyFilter = v }
|
||||
|
||||
|
||||
setFocused : Bool -> Model a -> Model a
|
||||
setFocused v (Model model) =
|
||||
Model { model | focused = v }
|
||||
|
||||
|
||||
|
||||
-- INTERNAL
|
||||
|
||||
|
@ -15,5 +15,5 @@ type Msg a
|
||||
| GotContainerAndMenuElements (Result Dom.Error { menu : Element, container : Element })
|
||||
| ClearButtonPressed
|
||||
| InputDebounceReturned String
|
||||
| GotRequestResponse (Result () (List a))
|
||||
| GotRequestResponse String (Result () (List a))
|
||||
| NoOp
|
||||
|
@ -5,3 +5,4 @@ type OptionState
|
||||
= Idle
|
||||
| Highlighted
|
||||
| Selected
|
||||
| SelectedAndHighlighted
|
||||
|
@ -54,7 +54,8 @@ update tagger maybeRequest msg model =
|
||||
onFocusMenu tagger maybeRequest model
|
||||
|
||||
InputLostFocus ->
|
||||
( Model.closeMenu model
|
||||
( Model.setFocused False model
|
||||
|> Model.closeMenu
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
@ -99,17 +100,23 @@ update tagger maybeRequest msg model =
|
||||
else
|
||||
( model, Effect.none )
|
||||
|
||||
GotRequestResponse (Ok items) ->
|
||||
( model
|
||||
|> Model.setItems items
|
||||
|> Model.setRequestState (Just Success)
|
||||
, getContainerAndMenuElementsEffect tagger model
|
||||
)
|
||||
GotRequestResponse inputVal response ->
|
||||
if inputVal == Model.toInputValue model then
|
||||
case response of
|
||||
Ok items ->
|
||||
( model
|
||||
|> Model.setItems items
|
||||
|> Model.setRequestState (Just Success)
|
||||
, getContainerAndMenuElementsEffect tagger model
|
||||
)
|
||||
|
||||
GotRequestResponse (Err _) ->
|
||||
( Model.setRequestState (Just Failed) model
|
||||
, Effect.none
|
||||
)
|
||||
Err _ ->
|
||||
( Model.setRequestState (Just Failed) model
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
else
|
||||
( model, Effect.none )
|
||||
|
||||
NoOp ->
|
||||
( model, Effect.none )
|
||||
@ -117,7 +124,8 @@ update tagger maybeRequest msg model =
|
||||
|
||||
onFocusMenu : (Msg a -> msg) -> Maybe (Request effect) -> Model a -> ( Model a, Effect effect msg )
|
||||
onFocusMenu tagger maybeRequest model =
|
||||
( Model.highlightIndex 0 model
|
||||
( Model.setFocused True model
|
||||
|> Model.highlightIndex 0
|
||||
, if maybeRequest == Nothing || Model.toRequestState model == Just Success then
|
||||
getContainerAndMenuElementsEffect tagger model
|
||||
|
||||
|
@ -10,7 +10,6 @@ import Element exposing (Attribute, Element)
|
||||
import Element.Background as Background
|
||||
import Element.Border as Border
|
||||
import Element.Events as Events
|
||||
import Element.Font as Font
|
||||
import Element.Input as Input
|
||||
import Html.Attributes
|
||||
import Html.Events
|
||||
@ -140,7 +139,7 @@ inputView placement filteredOptions model config =
|
||||
)
|
||||
{ onChange = InputChanged >> config.onChange
|
||||
, text =
|
||||
if Model.isOpen model then
|
||||
if Model.isFocused model then
|
||||
Model.toInputValue model
|
||||
|
||||
else
|
||||
@ -307,35 +306,25 @@ positionFixedAttributes placement container =
|
||||
|
||||
defaultOptionElement : (a -> String) -> OptionState -> a -> Element msg
|
||||
defaultOptionElement toString optionState a =
|
||||
case optionState of
|
||||
Highlighted ->
|
||||
Element.el
|
||||
([ Background.color (Element.rgb 0.9 0.9 0.9)
|
||||
, Font.color (Element.rgb 0 0 0)
|
||||
]
|
||||
++ defaultOptionAttrs
|
||||
)
|
||||
(Element.text (toString a))
|
||||
Element.el
|
||||
[ Element.width Element.fill
|
||||
, Element.pointer
|
||||
, Element.paddingXY 14 10
|
||||
, Background.color <|
|
||||
case optionState of
|
||||
Highlighted ->
|
||||
Element.rgb 0.95 0.95 0.95
|
||||
|
||||
Selected ->
|
||||
Element.el
|
||||
([ Background.color (Element.rgb 0.65 0.84 0.98)
|
||||
, Font.color (Element.rgb 0 0 0)
|
||||
]
|
||||
++ defaultOptionAttrs
|
||||
)
|
||||
(Element.text (toString a))
|
||||
Selected ->
|
||||
Element.rgba 0.64 0.83 0.97 0.8
|
||||
|
||||
Idle ->
|
||||
Element.el defaultOptionAttrs (Element.text (toString a))
|
||||
SelectedAndHighlighted ->
|
||||
Element.rgba 0.64 0.83 0.97 1
|
||||
|
||||
|
||||
defaultOptionAttrs : List (Attribute msg)
|
||||
defaultOptionAttrs =
|
||||
[ Element.width Element.fill
|
||||
, Element.pointer
|
||||
, Element.paddingXY 14 10
|
||||
]
|
||||
Idle ->
|
||||
Element.rgb 1 1 1
|
||||
]
|
||||
(Element.text (toString a))
|
||||
|
||||
|
||||
defaultNoMatchElement : Element msg
|
||||
|
@ -194,7 +194,7 @@ Note that in order to avoid an elm/http dependency in this package, you will nee
|
||||
fetchThings query =
|
||||
Http.get
|
||||
{ url = "https://awesome-thing.api/things?search=" ++ query
|
||||
, expect = Http.expectJson Select.gotRequestResponse (Decode.list thingDecoder)
|
||||
, expect = Http.expectJson (Select.gotRequestResponse query) (Decode.list thingDecoder)
|
||||
}
|
||||
|
||||
-}
|
||||
@ -219,11 +219,12 @@ request =
|
||||
Request.request
|
||||
|
||||
|
||||
{-| Hook the request Cmd result back into update with this Msg
|
||||
{-| Hook the request Cmd result back into update with this Msg. You need to pass in the string query (input value)
|
||||
that was used for the request. This is used to match up the correct response to most recent request in case of race conditions.
|
||||
-}
|
||||
gotRequestResponse : Result err (List a) -> Msg a
|
||||
gotRequestResponse =
|
||||
Result.mapError (\_ -> ()) >> Msg.GotRequestResponse
|
||||
gotRequestResponse : String -> Result err (List a) -> Msg a
|
||||
gotRequestResponse inputValue =
|
||||
Result.mapError (\_ -> ()) >> Msg.GotRequestResponse inputValue
|
||||
|
||||
|
||||
|
||||
@ -307,6 +308,7 @@ type OptionState
|
||||
= Idle
|
||||
| Highlighted
|
||||
| Selected
|
||||
| SelectedAndHighlighted
|
||||
|
||||
|
||||
{-| Provide your own element to show when there are no matches based on the filter and input value. This appears below the input.
|
||||
@ -379,3 +381,6 @@ mapOptionState state =
|
||||
|
||||
OptionState.Selected ->
|
||||
Selected
|
||||
|
||||
OptionState.SelectedAndHighlighted ->
|
||||
SelectedAndHighlighted
|
||||
|
@ -86,7 +86,7 @@ update tagger =
|
||||
Select.Effect.performWithRequest performEffect selectEffect
|
||||
|
||||
FetchThings query ->
|
||||
fetchThings (Select.gotRequestResponse >> SelectMsg) query
|
||||
fetchThings (Select.gotRequestResponse query >> SelectMsg) query
|
||||
|
||||
fetchThings : (Result Http.Error (List thing) -> msg) -> String -> Cmd msg
|
||||
fetchThings tagger query =
|
||||
|
Loading…
Reference in New Issue
Block a user