mirror of
https://github.com/nunntom/elm-ui-select.git
synced 2024-11-22 11:54:11 +03:00
Fix docs and remove UpdateConfig
This commit is contained in:
parent
e7f90fb21c
commit
ac945d0d92
31
README.md
31
README.md
@ -1,6 +1,6 @@
|
||||
# Elm-Ui Select
|
||||
|
||||
A select widget for elm-ui.
|
||||
A select/dropdown widget for elm-ui.
|
||||
|
||||
## Features:
|
||||
|
||||
@ -9,7 +9,7 @@ A select widget for elm-ui.
|
||||
- Automatically attempts to size and place the menu based on the position of the input in the viewport.
|
||||
- Customisable: Supply your own attributes for the input, menu or your own option Element.
|
||||
- Make HTTP requests to retrieve matching options (The package does not make the requests, but you supply the Cmd/Effect - no elm/http dependency)
|
||||
- Can be used with the Effect pattern and [elm-program-test](https://package.elm-lang.org/packages/avh4/elm-program-test/3.6.3/)
|
||||
- Can be used with the Effect pattern and [elm-program-test](https://package.elm-lang.org/packages/avh4/elm-program-test/3.6.3/) to simulate input to the select.
|
||||
|
||||
## Example
|
||||
|
||||
@ -60,6 +60,31 @@ view model =
|
||||
|
||||
```
|
||||
|
||||
Note that if you have the list of items in your model already, you can also pass them directly in the view instead of setting the items in init and it will work just as well:
|
||||
|
||||
```elm
|
||||
|
||||
|
||||
init : () -> ( Model, Cmd Msg )
|
||||
init _ =
|
||||
( { countrySelect = Select.init "country-select" }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
|
||||
view : Model -> Element Msg
|
||||
view model =
|
||||
Select.view []
|
||||
{ onChange = CountrySelectMsg
|
||||
, label = Input.labelAbove [] (text "Choose a country")
|
||||
, placeholder = Just (Input.placeholder [] (text "Type to search"))
|
||||
, itemToString = \c -> c.flag ++ " " ++ c.name
|
||||
}
|
||||
|> Select.toElement (Select.setItems Countries.all model.countrySelect)
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
There are issues when the input is placed within a parent element that has overflow scroll or auto: the menu may be clipped by the parent. This can be overcome by using [Select.withMenuPositionFixed](https://package.elm-lang.org/packages/nunntom/elm-ui-select/1.0.0/Select#withMenuPositionFixed), but if the parent also has a transform applied, it gets clipped again. This means any parent with e.g. Element.scrollBarY + Element.moveDown/moveLeft etc. can cause issues. This is due to [a feature of the current CSS spec](https://bugs.chromium.org/p/chromium/issues/detail?id=20574).
|
||||
There are issues when the input is placed within a parent element that has overflow scroll or auto: the menu may be clipped by the parent. This can be overcome by using [Select.withMenuPositionFixed](https://package.elm-lang.org/packages/nunntom/elm-ui-select/2.0.0/Select/#withMenuPositionFixed), but if the parent also has a transform applied, it gets clipped again. This means any parent with e.g. Element.scrollBarY + Element.moveDown/moveLeft etc. can cause issues. This is due to [a feature of the current CSS spec](https://bugs.chromium.org/p/chromium/issues/detail?id=20574).
|
||||
|
6
elm.json
6
elm.json
@ -3,7 +3,7 @@
|
||||
"name": "nunntom/elm-ui-select",
|
||||
"summary": "A select widget for Elm Ui",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.0",
|
||||
"exposed-modules": [
|
||||
"Select",
|
||||
"Select.Filter",
|
||||
@ -12,11 +12,11 @@
|
||||
],
|
||||
"elm-version": "0.19.0 <= v < 0.20.0",
|
||||
"dependencies": {
|
||||
"elm/browser": "1.0.0 <= v < 2.0.0",
|
||||
"elm/core": "1.0.0 <= v < 2.0.0",
|
||||
"elm/html": "1.0.0 <= v < 2.0.0",
|
||||
"elm/json": "1.0.0 <= v < 2.0.0",
|
||||
"elm/browser": "1.0.0 <= v < 2.0.0",
|
||||
"mdgriffith/elm-ui": "1.0.0 <= v < 2.0.0"
|
||||
},
|
||||
"test-dependencies": {}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,15 @@
|
||||
# Elm-ui Select examples
|
||||
|
||||
to run:
|
||||
## to run:
|
||||
|
||||
```console
|
||||
elm reactor
|
||||
```
|
||||
|
||||
And choose from one of the examples in the src directory
|
||||
|
||||
## run tests on the examples using elm-program-test:
|
||||
|
||||
```console
|
||||
npx elm-test
|
||||
```
|
||||
|
@ -45,13 +45,21 @@ view model =
|
||||
, Element.padding 30
|
||||
]
|
||||
[ Select.view []
|
||||
{ onChange = CountrySelectMsg
|
||||
, label = Input.labelAbove [ Element.htmlAttribute <| Html.Attributes.for (Select.toInputElementId model.countrySelect) ] (Element.text "Choose a country")
|
||||
, placeholder = Just (Input.placeholder [] (Element.text "Type to search"))
|
||||
, itemToString = \c -> c.flag ++ " " ++ c.name
|
||||
}
|
||||
|> Select.withClearButton (Just Resources.ClearButton.clearButton)
|
||||
|> Select.toElement model.countrySelect
|
||||
{ onChange = CountrySelectMsg
|
||||
, label = Input.labelAbove [ Element.htmlAttribute <| Html.Attributes.for (Select.toInputElementId model.countrySelect) ] (Element.text "Choose a country")
|
||||
, placeholder = Just (Input.placeholder [] (Element.text "Type to search"))
|
||||
, itemToString = \c -> c.flag ++ " " ++ c.name
|
||||
}
|
||||
|> Select.withClearButton
|
||||
(Just
|
||||
(Select.clearButton [
|
||||
Element.alignRight
|
||||
, Element.centerY
|
||||
, Element.moveLeft 12 ]
|
||||
(Element.el [ Element.Region.description "clear selection") ] (Element.text "❌"))
|
||||
)
|
||||
)
|
||||
|> Select.toElement model.countrySelect
|
||||
, Maybe.map (\{ name } -> Element.text ("You chose " ++ name)) (Select.toValue model.countrySelect)
|
||||
|> Maybe.withDefault Element.none
|
||||
]
|
||||
|
@ -2,11 +2,11 @@ module Resources.ClearButton exposing (clearButton)
|
||||
|
||||
import Element
|
||||
import Element.Font as Font
|
||||
import Html.Attributes
|
||||
import Element.Region as Region
|
||||
import Select
|
||||
|
||||
|
||||
clearButton : Select.ClearButton msg
|
||||
clearButton =
|
||||
Select.clearButton [ Element.alignRight, Element.centerY, Element.moveLeft 12 ]
|
||||
(Element.el [ Font.size 10, Element.htmlAttribute (Html.Attributes.title "clear selection") ] (Element.text "❌"))
|
||||
(Element.el [ Font.size 10, Region.description "clear selection" ] (Element.text "❌"))
|
||||
|
139
src/Select.elm
139
src/Select.elm
@ -3,8 +3,8 @@ module Select exposing
|
||||
, setItems, setSelected, setInputValue, closeMenu
|
||||
, toValue, toInputValue, toInputElementId, toMenuElementId
|
||||
, isMenuOpen, isLoading, isRequestFailed
|
||||
, Msg, update, updateWithRequest, UpdateConfig, Request, request, gotRequestResponse
|
||||
, ViewConfig, view, withMenuAttributes, MenuPlacement(..), withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur
|
||||
, Msg, update, updateWithRequest, Request, request, gotRequestResponse
|
||||
, ViewConfig, view, withMenuAttributes, MenuPlacement(..), withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPlacementAuto, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur
|
||||
, toElement
|
||||
, Effect
|
||||
)
|
||||
@ -34,12 +34,12 @@ module Select exposing
|
||||
|
||||
# Update and Requests
|
||||
|
||||
@docs Msg, update, updateWithRequest, UpdateConfig, Request, request, gotRequestResponse
|
||||
@docs Msg, update, updateWithRequest, Request, request, gotRequestResponse
|
||||
|
||||
|
||||
# Configure View
|
||||
|
||||
@docs ViewConfig, view, withMenuAttributes, MenuPlacement, withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur
|
||||
@docs ViewConfig, view, withMenuAttributes, MenuPlacement, withMenuMaxHeight, withMenuMaxWidth, withNoMatchElement, withOptionElement, OptionState, withClearButton, ClearButton, clearButton, withFilter, withMenuAlwaysAbove, withMenuAlwaysBelow, withMenuPlacementAuto, withMenuPositionFixed, withClearInputValueOnBlur, withSelectExactMatchOnBlur
|
||||
|
||||
|
||||
# Element
|
||||
@ -84,6 +84,30 @@ init =
|
||||
|
||||
|
||||
{-| Set the list of items
|
||||
|
||||
You can do this on init:
|
||||
|
||||
init : List String -> ( Model, Cmd Msg )
|
||||
init things =
|
||||
( { select =
|
||||
Select.init "thing-select"
|
||||
|> Select.setItems things
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
Or you can do it in your view if you prefer to keep your items in your own model.
|
||||
|
||||
view : Model -> Element Msg
|
||||
view model =
|
||||
Select.view []
|
||||
{ onChange = SelectMsg
|
||||
, label = Input.labelAbove [] (text "Choose a thing")
|
||||
, placeholder = Just (Input.placeholder [] (text "Type to search"))
|
||||
, itemToString = identity
|
||||
}
|
||||
|> Select.toElement (Select.setItems model.things model.select)
|
||||
|
||||
-}
|
||||
setItems : List a -> Select a -> Select a
|
||||
setItems =
|
||||
@ -218,20 +242,6 @@ updateWithRequest req tagger msg select =
|
||||
|> Tuple.mapSecond (Effect.perform identity >> Cmd.map tagger)
|
||||
|
||||
|
||||
{-| Configuration options for updateWith.
|
||||
|
||||
- Should the input be cleared if on blur if nothing is selected?
|
||||
- Should an exact string match (case insensitive) be selected automatically on blur if nothing is already selected?
|
||||
|
||||
If you do not provide a config, both default to False.
|
||||
|
||||
-}
|
||||
type alias UpdateConfig =
|
||||
{ clearInputValueOnBlur : Bool
|
||||
, selectExactMatchOnBlur : Bool
|
||||
}
|
||||
|
||||
|
||||
{-| A Request. See [Select.Request](Select-Request) for configuration options.
|
||||
-}
|
||||
type alias Request a =
|
||||
@ -260,6 +270,17 @@ gotRequestResponse inputValue =
|
||||
|
||||
|
||||
{-| The View Configuration
|
||||
|
||||
view : Model -> Element Msg
|
||||
view model =
|
||||
Select.view []
|
||||
{ onChange = SelectMsg
|
||||
, label = Input.labelAbove [] (text "Choose a thing")
|
||||
, placeholder = Just (Input.placeholder [] (text "Type to search"))
|
||||
, itemToString = .name
|
||||
}
|
||||
|> Select.toElement model.thingsSelect
|
||||
|
||||
-}
|
||||
type ViewConfig a msg
|
||||
= ViewConfig (ViewConfigInternal a msg)
|
||||
@ -302,6 +323,14 @@ withMenuAlwaysAbove (ViewConfig config) =
|
||||
ViewConfig { config | menuPlacement = Just Placement.Above }
|
||||
|
||||
|
||||
{-| Menu decides whether to appear above or below based on how much space is available. This is the default.
|
||||
You'd only use this function if you're passing around a config and need to reset this option.
|
||||
-}
|
||||
withMenuPlacementAuto : ViewConfig a msg -> ViewConfig a msg
|
||||
withMenuPlacementAuto (ViewConfig config) =
|
||||
ViewConfig { config | menuPlacement = Nothing }
|
||||
|
||||
|
||||
{-| Set a maximum height for the menu
|
||||
-}
|
||||
withMenuMaxHeight : Maybe Int -> ViewConfig a msg -> ViewConfig a msg
|
||||
@ -318,6 +347,28 @@ withMenuMaxWidth width (ViewConfig config) =
|
||||
|
||||
{-| Set arbitrary attributes for the menu element. You can call this multiple times and it will accumulate attributes.
|
||||
You can define different attributes based on whether the menu appears above or below the input.
|
||||
|
||||
Select.view []
|
||||
{ onChange = SelectMsg
|
||||
, label = Input.labelAbove [] (Element.text "Choose a thing")
|
||||
, placeholder = Just (Input.placeholder [] (Element.text "Type to search"))
|
||||
, itemToString = .name
|
||||
}
|
||||
|> Select.withMenuAttributes
|
||||
(\placement ->
|
||||
[ Element.Font.size 16
|
||||
, Element.Border.width 2
|
||||
]
|
||||
++ (case placement of
|
||||
Select.MenuAbove ->
|
||||
[ Element.moveUp 10 ]
|
||||
|
||||
Select.MenuBelow ->
|
||||
[ Element.moveDown 10 ]
|
||||
)
|
||||
)
|
||||
|> Select.toElement model.select
|
||||
|
||||
-}
|
||||
withMenuAttributes : (MenuPlacement -> List (Attribute msg)) -> ViewConfig a msg -> ViewConfig a msg
|
||||
withMenuAttributes attribs (ViewConfig config) =
|
||||
@ -332,6 +383,36 @@ type MenuPlacement
|
||||
|
||||
|
||||
{-| Provide your own element for the options in the menu, based on the current [state](#OptionState) of the option.
|
||||
|
||||
Select.view []
|
||||
{ onChange = SelectMsg
|
||||
, label = Input.labelAbove [] (Element.text "Choose a thing")
|
||||
, placeholder = Just (Input.placeholder [] (Element.text "Type to search"))
|
||||
, itemToString = .name
|
||||
}
|
||||
|> Select.withOptionElement
|
||||
(\state item ->
|
||||
Element.el
|
||||
[ Element.width Element.fill
|
||||
, Element.paddingXY 14 10
|
||||
, Background.color <|
|
||||
case optionState of
|
||||
Idle ->
|
||||
Element.rgb 1 1 1
|
||||
|
||||
Highlighted ->
|
||||
Element.rgb 0.95 0.95 0.95
|
||||
|
||||
Selected ->
|
||||
Element.rgba 0.64 0.83 0.97 0.8
|
||||
|
||||
SelectedAndHighlighted ->
|
||||
Element.rgba 0.64 0.83 0.97 1
|
||||
]
|
||||
(Element.text item.name)
|
||||
)
|
||||
|> Select.toElement model.select
|
||||
|
||||
-}
|
||||
withOptionElement : (OptionState -> a -> Element msg) -> ViewConfig a msg -> ViewConfig a msg
|
||||
withOptionElement toEl (ViewConfig config) =
|
||||
@ -355,6 +436,25 @@ withNoMatchElement element (ViewConfig config) =
|
||||
|
||||
|
||||
{-| Add a button to clear the input. This element is positioned as Element.inFront.
|
||||
|
||||
Select.view []
|
||||
{ onChange = SelectMsg
|
||||
, label = Input.labelAbove [] (Element.text "Choose a thing")
|
||||
, placeholder = Just (Input.placeholder [] (Element.text "Type to search"))
|
||||
, itemToString = .name
|
||||
}
|
||||
|> Select.withClearButton
|
||||
(Just
|
||||
(Select.clearButton
|
||||
[ Element.alignRight
|
||||
, Element.centerY
|
||||
, Element.moveLeft 12
|
||||
]
|
||||
(Element.el [ Element.Region.description "clear selection" ] (Element.text "❌"))
|
||||
)
|
||||
)
|
||||
|> Select.toElement model.select
|
||||
|
||||
-}
|
||||
withClearButton : Maybe (ClearButton msg) -> ViewConfig a msg -> ViewConfig a msg
|
||||
withClearButton cb (ViewConfig config) =
|
||||
@ -411,7 +511,8 @@ toElement model (ViewConfig config) =
|
||||
-- EFFECT
|
||||
|
||||
|
||||
{-| For use with the [Effect pattern](https://sporto.github.io/elm-patterns/architecture/effects.html)
|
||||
{-| For use with the [Effect pattern](https://sporto.github.io/elm-patterns/architecture/effects.html) and [elm-program-test](https://package.elm-lang.org/packages/avh4/elm-program-test/3.6.3/),
|
||||
see [Select.Effect](Select-Effect).
|
||||
-}
|
||||
type alias Effect effect msg =
|
||||
Effect.Effect effect msg
|
||||
|
@ -251,7 +251,7 @@ you need to provide some of the functions from those packages here.
|
||||
|
||||
selectTest : Test
|
||||
selectTest =
|
||||
Test.test "Type in United and choose United Kingdom with a mouse click" <|
|
||||
Test.test "Typing United and clicking United Kingdom option selects United Kingdom" <|
|
||||
\() ->
|
||||
ProgramTest.createElement
|
||||
{ init = Example.init
|
||||
@ -260,7 +260,7 @@ you need to provide some of the functions from those packages here.
|
||||
}
|
||||
|> ProgramTest.withSimulatedEffects simulateEffect
|
||||
|> ProgramTest.start ()
|
||||
|> Select.Effect.simulateFillIn simulateConfig model.select "United"
|
||||
|> ProgramTest.fillIn "" "Choose a country" "United Kingdom"
|
||||
|> Select.Effect.simulateClickOption simulateConfig model.select "United Kingdom"
|
||||
|> ProgramTest.expectViewHas [ Selector.text "You chose United Kingdom" ]
|
||||
|
||||
|
@ -6,13 +6,10 @@ module Select.Request exposing (withDelay, withMinLength)
|
||||
update msg model =
|
||||
case msg of
|
||||
SelectMsg subMsg ->
|
||||
Select.updateWith
|
||||
(Select.UpdateConfig.default
|
||||
|> Select.UpdateConfig.withRequest
|
||||
(Select.request fetchThings
|
||||
|> Select.Request.withDelay 200
|
||||
|> Select.Request.withMinLength 4
|
||||
)
|
||||
Select.updateWithRequest
|
||||
(Select.request fetchThings
|
||||
|> Select.Request.withDelay 200
|
||||
|> Select.Request.withMinLength 4
|
||||
)
|
||||
SelectMsg
|
||||
subMsg
|
||||
|
Loading…
Reference in New Issue
Block a user