elmnt-scrollpicker provides an scroll picker with some animation. elmnt
is stands for Element
so you can use the View(widget) as an element in elm-ui.
import Elmnt.BaseScrollPicker as ScrollPicker
type ExampleMsg
= ScrollPickerMessage String (Msg Int ExampleMsg)Unfortuneately, some states of picker are required to store in internal record. you might need to declare your own message wrapper constructor ScrollPickerMessage is an wrapper constructor (or map function) to create messages which is compatible to your own module message.
Let's say we are making a simple time picker, we need two seprate pickers for hour and minute value.
type alias ExampleModel -- which is your own model
= { firstPickerModel : MinimalModel Int ExampleMsg
, secondPickerModel : MinimalModel Int ExampleMsg
, messageMapWith : String -> (Msg Int ExampleMsg) -> ExampleMsg
, pickerDirection : Direction -- Horizontal or Vertical
}exampleView shows how to reveal the model on the page by using elm-ui.
check out which settings you can change defaultTheme
exampleView : ExampleModel -> Html ExampleMsg
exampleView model
= let
theme
= ScrollPicker.defaultTheme
picker
= scrollPicker model theme
in
layout [ Background.color <|
theme.palette.toElmUiColor theme.palette.surface
] <|
row [ spacing 1
, centerX
, centerY
]
[ picker model.firstPickerModel
, picker model.secondPickerModel
]MinimalModel
you can still use any other API in the module to work with your own picker
model As most of API use partial record type. for example setOptions
function has the definition like below
setOptions : (vt -> String) ->
List (vt, Element msg) ->
{ state |
idString : String
, optionIds : List String
, optionIdToRecordDict : Dict String (Option vt msg)
} -> -- At least, those fields are required in the state record
{ state |
idString : String
, optionIds : List String
, optionIdToRecordDict : Dict String (Option vt msg)
} --> will return the same structure of the state recordLet's initialise our example model. Each picker model(or state) could be
initialised with initMinimalModel and setOptions
exampleInit : () -> ( ExampleModel, Cmd ExampleMsg )
exampleInit flags
= ( { firstPickerModel -- for hour value
= initMinimalModel "firstScrollPicker"
|> setOptions
(String.fromInt)
(List.range 1 12
|> List.map
( \n -> ( n
, n |> ( String.fromInt >> text )
)
)
)
|> setScrollStopCheckTime 75
, secondPickerModel -- for minute value
= initMinimalModel "secondScrollPicker"
|> setOptions
(String.fromInt)
(List.range 0 59
|> List.map
( \n -> ( n
, n |> ( String.fromInt
>> String.padLeft 2 '0'
>> text
)
)
)
)
, messageMapWith = ScrollPickerMessage
-- ^ a map function to wrap the picker messages into the ExampleMsg
, pickerDirection = Vertical
} In your own update function, you might need to check picker Id and update the matched picker state(or model) accordingly.
exampleUpdate : ExampleMsg -> ExampleModel -> ( ExampleModel, Cmd ExampleMsg )
exampleUpdate msg model
= let update
= updateWith model
in
case msg of
ScrollPickerMessage idString pickerMsg ->
case idString of
"firstScrollPicker" ->
let ( firstPickerModel, cmd )
= update pickerMsg model.firstPickerModel
newModel
= { model |
firstPickerModel
= firstPickerModel
}
in ( case anyNewOptionSelected pickerMsg of
Just option ->
{ newModel |
hourValue = option.value
}
Nothing ->
newModel
, cmd
)
"secondScrollPicker" ->
let ( secondPickerModel, cmd )
= update pickerMsg model.secondPickerModel
newModel
= { model |
secondPickerModel
= secondPickerModel
}
in ( case anyNewOptionSelected pickerMsg of
Just option ->
{ newModel |
minuteValue = option.value
}
Nothing ->
newModel
, cmd
)
_ ->
( model, Cmd.none )And picker model itself does not hold selected 'option', you also need to
check some message Elmnt.BasePickerModel.ScrollPickerSuccess
Note: anyNewOptionSelected function can be useful to set new value
from the picker. you can also check this out from below code.
Here is an example
exampleView : ExampleModel -> Html ExampleMsg
exampleView model
= let
theme
= defaultTheme
pickerHelper
= viewScrollPicker model theme
in
layout [ Background.color (theme.palette.on.surface -- use same color as shade
|> theme.palette.toElmUiColor)
] <|
column [ centerX
, centerY
]
[ row [ spacing 1
]
[ pickerHelper model.firstPickerModel
, pickerHelper model.secondPickerModel
]
, el [ MAttr.paddingTop 20
, Font.size
( defaultFontSize
|> toFloat
|> (*) 0.7
|> truncate
)
, Font.color
( theme.palette.secondary
|> theme.palette.toElmUiColor )
, centerX
] <|
text <| "It's " ++
(model.hourValue |> String.fromInt) ++ ":" ++
(model.minuteValue |> String.fromInt )
]Scroll picker relies on animation by using elm-style-animation and subscriptions is essential to see actual animation on going now.
exampleSubscriptions : ExampleModel -> Sub ExampleMsg
exampleSubscriptions model
= model |>
subscriptionsWith
[ model.firstPickerModel
, model.secondPickerModel
]Finally you can make main funciton with all the function above.
main : Program () ExampleModel ExampleMsg
main
= Browser.element
{ init = exampleInit
, view = exampleView
, update = exampleUpdate
, subscriptions = exampleSubscriptions
}I'm a chef and but still using Linux since 2001. I don't have enough chance to check on Apple product.
Gnome Web EpiphanyWhy elm-style-animation? elm-style-animation
is not quite designed for low level animation. but you could use the module
for any other css-style based animation.
elm-animation was also considered,
and it is pretty straight-foward.
However the module cannot live together in the same application due to name
colision.