mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-23 10:58:26 +00:00
230 lines
5.3 KiB
Elm
230 lines
5.3 KiB
Elm
{-
|
|
Copyright 2020 Eike K. & Contributors
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
-}
|
|
|
|
|
|
module Comp.FixedDropdown exposing
|
|
( Item
|
|
, Model
|
|
, Msg
|
|
, ViewSettings
|
|
, init
|
|
, update
|
|
, viewStyled2
|
|
)
|
|
|
|
import Data.DropdownStyle as DS
|
|
import Html exposing (..)
|
|
import Html.Attributes exposing (..)
|
|
import Html.Events exposing (onClick)
|
|
import Styles as S
|
|
import Util.Html exposing (KeyCode(..), onKeyUpCode)
|
|
import Util.List
|
|
|
|
|
|
type alias Item a =
|
|
{ id : a
|
|
}
|
|
|
|
|
|
type alias Model a =
|
|
{ options : List (Item a)
|
|
, menuOpen : Bool
|
|
, selected : Maybe a
|
|
}
|
|
|
|
|
|
type Msg a
|
|
= SelectItem2 (Item a)
|
|
| ToggleMenu
|
|
| KeyPress (Maybe KeyCode)
|
|
|
|
|
|
initItems : List (Item a) -> Model a
|
|
initItems options =
|
|
{ options = options
|
|
, menuOpen = False
|
|
, selected = Nothing
|
|
}
|
|
|
|
|
|
init : List a -> Model a
|
|
init els =
|
|
List.map Item els |> initItems
|
|
|
|
|
|
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 model =
|
|
case msg of
|
|
ToggleMenu ->
|
|
( { model | menuOpen = not model.menuOpen }, Nothing )
|
|
|
|
SelectItem2 item ->
|
|
( { model | menuOpen = False }, Just item.id )
|
|
|
|
KeyPress (Just Space) ->
|
|
update ToggleMenu model
|
|
|
|
KeyPress (Just Enter) ->
|
|
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 )
|
|
|
|
|
|
|
|
--- View2
|
|
|
|
|
|
type alias ViewSettings a =
|
|
{ display : a -> String
|
|
, icon : a -> Maybe String
|
|
, selectPlaceholder : String
|
|
, style : DS.DropdownStyle
|
|
}
|
|
|
|
|
|
viewStyled2 : ViewSettings a -> Bool -> Maybe a -> Model a -> Html (Msg a)
|
|
viewStyled2 cfg error sel model =
|
|
let
|
|
iconItem id =
|
|
span
|
|
[ classList [ ( "hidden", cfg.icon id == Nothing ) ]
|
|
, class (Maybe.withDefault "" (cfg.icon id))
|
|
, class "mr-2"
|
|
]
|
|
[]
|
|
|
|
renderItem item =
|
|
a
|
|
[ href "#"
|
|
, class cfg.style.item
|
|
, classList
|
|
[ ( cfg.style.itemActive, isSelected model item )
|
|
, ( "font-semibold", Just item.id == sel )
|
|
]
|
|
, onClick (SelectItem2 item)
|
|
]
|
|
[ iconItem item.id
|
|
, text (cfg.display item.id)
|
|
]
|
|
|
|
selIcon =
|
|
Maybe.map iconItem sel
|
|
|> Maybe.withDefault (span [ class "hidden" ] [])
|
|
in
|
|
div
|
|
[ class ("relative " ++ cfg.style.root)
|
|
, onKeyUpCode KeyPress
|
|
]
|
|
[ a
|
|
[ class cfg.style.link
|
|
, classList [ ( S.inputErrorBorder, error ) ]
|
|
, tabindex 0
|
|
, onClick ToggleMenu
|
|
, href "#"
|
|
]
|
|
[ div
|
|
[ class "flex-grow"
|
|
, classList
|
|
[ ( "opacity-50", sel == Nothing )
|
|
]
|
|
]
|
|
[ selIcon
|
|
, Maybe.map cfg.display sel
|
|
|> Maybe.withDefault cfg.selectPlaceholder
|
|
|> text
|
|
]
|
|
, div
|
|
[ class "rounded cursor-pointer ml-2 absolute right-2"
|
|
]
|
|
[ i [ class "fa fa-angle-down px-2" ] []
|
|
]
|
|
]
|
|
, div
|
|
[ class cfg.style.menu
|
|
, classList [ ( "hidden", not model.menuOpen ) ]
|
|
]
|
|
(List.map renderItem model.options)
|
|
]
|