Allow keyboard navigation in FixedDropdown

This commit is contained in:
Eike Kettner
2020-09-21 23:27:09 +02:00
parent 87d824c367
commit 22d70b4997
2 changed files with 139 additions and 6 deletions

View File

@ -15,6 +15,8 @@ module Comp.FixedDropdown exposing
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (onClick) import Html.Events exposing (onClick)
import Util.Html exposing (KeyCode(..), onKeyUpCode)
import Util.List
type alias Item a = type alias Item a =
@ -26,18 +28,21 @@ type alias Item a =
type alias Model a = type alias Model a =
{ options : List (Item a) { options : List (Item a)
, menuOpen : Bool , menuOpen : Bool
, selected : Maybe a
} }
type Msg a type Msg a
= SelectItem (Item a) = SelectItem (Item a)
| ToggleMenu | ToggleMenu
| KeyPress (Maybe KeyCode)
init : List (Item a) -> Model a init : List (Item a) -> Model a
init options = init options =
{ options = options { options = options
, menuOpen = False , menuOpen = False
, selected = Nothing
} }
@ -60,6 +65,54 @@ initTuple tuples =
init <| List.map mkItem tuples init <| List.map mkItem tuples
isSelected : Model a -> Item a -> Bool
isSelected model item =
model.selected == Just item.id
movePrevious : Model a -> ( Model a, Maybe a )
movePrevious model =
let
prev =
Util.List.findPrev (isSelected model) model.options
in
case prev of
Just p ->
( { model | selected = Just p.id, menuOpen = True }, Nothing )
Nothing ->
( { model
| selected =
List.reverse model.options
|> List.head
|> Maybe.map .id
, menuOpen = True
}
, Nothing
)
moveNext : Model a -> ( Model a, Maybe a )
moveNext model =
let
next =
Util.List.findNext (isSelected model) model.options
in
case next of
Just n ->
( { model | selected = Just n.id, menuOpen = True }, Nothing )
Nothing ->
( { model
| selected =
List.head model.options
|> Maybe.map .id
, menuOpen = True
}
, Nothing
)
update : Msg a -> Model a -> ( Model a, Maybe a ) update : Msg a -> Model a -> ( Model a, Maybe a )
update msg model = update msg model =
case msg of case msg of
@ -69,6 +122,49 @@ update msg model =
SelectItem item -> SelectItem item ->
( model, Just item.id ) ( model, Just item.id )
KeyPress (Just Space) ->
update ToggleMenu model
KeyPress (Just Enter) ->
if not model.menuOpen then
update ToggleMenu model
else
let
selected =
Util.List.find (isSelected model) model.options
in
case selected of
Just i ->
( { model | menuOpen = False }, Just i.id )
Nothing ->
( model, Nothing )
KeyPress (Just Up) ->
movePrevious model
KeyPress (Just Letter_P) ->
movePrevious model
KeyPress (Just Letter_K) ->
movePrevious model
KeyPress (Just Down) ->
moveNext model
KeyPress (Just Letter_N) ->
moveNext model
KeyPress (Just Letter_J) ->
moveNext model
KeyPress (Just ESC) ->
( { model | menuOpen = False }, Nothing )
KeyPress _ ->
( model, Nothing )
viewStyled : String -> Maybe (Item a) -> Model a -> Html (Msg a) viewStyled : String -> Maybe (Item a) -> Model a -> Html (Msg a)
viewStyled classes selected model = viewStyled classes selected model =
@ -78,7 +174,9 @@ viewStyled classes selected model =
, ( classes, True ) , ( classes, True )
, ( "open", model.menuOpen ) , ( "open", model.menuOpen )
] ]
, tabindex 0
, onClick ToggleMenu , onClick ToggleMenu
, onKeyUpCode KeyPress
] ]
[ input [ type_ "hidden" ] [] [ input [ type_ "hidden" ] []
, i [ class "dropdown icon" ] [] , i [ class "dropdown icon" ] []
@ -100,7 +198,7 @@ viewStyled classes selected model =
] ]
] ]
<| <|
List.map renderItems model.options List.map (renderItems model) model.options
] ]
@ -114,8 +212,14 @@ viewString selected model =
view (Maybe.map (\s -> Item s s) selected) model view (Maybe.map (\s -> Item s s) selected) model
renderItems : Item a -> Html (Msg a) renderItems : Model a -> Item a -> Html (Msg a)
renderItems item = renderItems model item =
div [ class "item", onClick (SelectItem item) ] div
[ classList
[ ( "item", True )
, ( "selected", isSelected model item )
]
, onClick (SelectItem item)
]
[ text item.display [ text item.display
] ]

View File

@ -45,6 +45,14 @@ type KeyCode
| Right | Right
| Enter | Enter
| Space | Space
| ESC
| Letter_N
| Letter_P
| Letter_H
| Letter_J
| Letter_K
| Letter_L
| Code Int
intToKeyCode : Int -> Maybe KeyCode intToKeyCode : Int -> Maybe KeyCode
@ -68,8 +76,29 @@ intToKeyCode code =
32 -> 32 ->
Just Space Just Space
_ -> 27 ->
Nothing Just ESC
72 ->
Just Letter_H
74 ->
Just Letter_J
75 ->
Just Letter_K
76 ->
Just Letter_L
78 ->
Just Letter_N
80 ->
Just Letter_P
n ->
Just (Code n)
onKeyUp : (Int -> msg) -> Attribute msg onKeyUp : (Int -> msg) -> Attribute msg