Merge pull request #279 from eikek/keyboard-support

Keyboard support
This commit is contained in:
mergify[bot]
2020-09-22 21:45:24 +00:00
committed by GitHub
15 changed files with 497 additions and 61 deletions

View File

@ -195,7 +195,7 @@ updateItemDetail lmsg model =
Page.ItemDetail.Update.update Page.ItemDetail.Update.update
model.key model.key
model.flags model.flags
inav.next inav
model.uiSettings model.uiSettings
lmsg lmsg
model.itemDetailModel model.itemDetailModel

View File

@ -313,7 +313,7 @@ isDropdownChangeMsg cm =
KeyPress code -> KeyPress code ->
Util.Html.intToKeyCode code Util.Html.intToKeyCode code
|> Maybe.map (\c -> c == Util.Html.Enter) |> Maybe.map (\c -> c == Util.Html.Enter || c == Util.Html.ESC)
|> Maybe.withDefault False |> Maybe.withDefault False
_ -> _ ->
@ -352,7 +352,10 @@ update msg model =
m = m =
deselectItem model e |> applyFilter "" deselectItem model e |> applyFilter ""
in in
( { m | menuOpen = False }, Cmd.none ) ( -- Setting to True, because parent click sets it to False… ugly
{ m | menuOpen = True }
, Cmd.none
)
Filter str -> Filter str ->
let let
@ -369,9 +372,40 @@ update msg model =
Just Util.Html.Up -> Just Util.Html.Up ->
( makeNextActive (\n -> n - 1) model, Cmd.none ) ( makeNextActive (\n -> n - 1) model, Cmd.none )
Just Util.Html.Letter_P ->
( makeNextActive (\n -> n - 1) model, Cmd.none )
Just Util.Html.Letter_K ->
( makeNextActive (\n -> n - 1) model, Cmd.none )
Just Util.Html.Down -> Just Util.Html.Down ->
( makeNextActive ((+) 1) model, Cmd.none ) ( makeNextActive ((+) 1) model, Cmd.none )
Just Util.Html.Letter_N ->
( makeNextActive ((+) 1) model, Cmd.none )
Just Util.Html.Letter_J ->
( makeNextActive ((+) 1) model, Cmd.none )
Just Util.Html.ESC ->
if model.menuOpen then
( model, Cmd.none )
else
case model.selected of
[ e ] ->
let
( m_, c_ ) =
update (RemoveItem e) model
in
( { m_ | menuOpen = False }, c_ )
_ ->
( model, Cmd.none )
Just Util.Html.Space ->
update ToggleMenu model
Just Util.Html.Enter -> Just Util.Html.Enter ->
let let
m = m =
@ -404,7 +438,11 @@ viewSingle model =
[ class "message" [ class "message"
, style "display" "inline-block !important" , style "display" "inline-block !important"
] ]
[ i [ class "delete icon", onClick (RemoveItem item) ] [] [ i
[ class "delete icon"
, onClick (RemoveItem item)
]
[]
, text item.option.text , text item.option.text
] ]
@ -420,7 +458,6 @@ viewSingle model =
[ class "search" [ class "search"
, placeholder "Search" , placeholder "Search"
, onInput Filter , onInput Filter
, onKeyUp KeyPress
, value model.filterString , value model.filterString
] ]
[] []
@ -433,10 +470,15 @@ viewSingle model =
, ( "open", model.menuOpen ) , ( "open", model.menuOpen )
] ]
:: (if model.menuOpen then :: (if model.menuOpen then
[] [ tabindex 0
, onKeyUp KeyPress
]
else else
[ onClick ToggleMenu ] [ onClick ToggleMenu
, tabindex 0
, onKeyUp KeyPress
]
) )
) )
(List.append (List.append
@ -482,6 +524,8 @@ viewMultiple settings model =
[ ( "ui search dropdown multiple selection", True ) [ ( "ui search dropdown multiple selection", True )
, ( "open", model.menuOpen ) , ( "open", model.menuOpen )
] ]
, tabindex 0
, onKeyUp KeyPress
] ]
(List.concat (List.concat
[ [ i [ class "dropdown icon", onClick ToggleMenu ] [] [ [ i [ class "dropdown icon", onClick ToggleMenu ] []
@ -492,7 +536,6 @@ viewMultiple settings model =
[ class "search" [ class "search"
, placeholder "Search" , placeholder "Search"
, onInput Filter , onInput Filter
, onKeyUp KeyPress
, value model.filterString , value model.filterString
] ]
[] []

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

@ -10,6 +10,7 @@ import Comp.ItemDetail.Model exposing (Msg(..))
import Comp.ItemDetail.Update import Comp.ItemDetail.Update
import Comp.ItemDetail.View exposing (..) import Comp.ItemDetail.View exposing (..)
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.ItemNav exposing (ItemNav)
import Data.UiSettings exposing (UiSettings) import Data.UiSettings exposing (UiSettings)
import Html exposing (..) import Html exposing (..)
import Page exposing (Page(..)) import Page exposing (Page(..))
@ -24,11 +25,11 @@ emptyModel =
Comp.ItemDetail.Model.emptyModel Comp.ItemDetail.Model.emptyModel
update : Nav.Key -> Flags -> Maybe String -> UiSettings -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) update : Nav.Key -> Flags -> ItemNav -> UiSettings -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
update = update =
Comp.ItemDetail.Update.update Comp.ItemDetail.Update.update
view : { prev : Maybe String, next : Maybe String } -> UiSettings -> Model -> Html Msg view : ItemNav -> UiSettings -> Model -> Html Msg
view = view =
Comp.ItemDetail.View.view Comp.ItemDetail.View.view

View File

@ -25,6 +25,7 @@ import Comp.DetailEdit
import Comp.Dropdown import Comp.Dropdown
import Comp.Dropzone import Comp.Dropzone
import Comp.ItemMail import Comp.ItemMail
import Comp.KeyInput
import Comp.MarkdownInput import Comp.MarkdownInput
import Comp.SentMails import Comp.SentMails
import Comp.YesNoDimmer import Comp.YesNoDimmer
@ -85,6 +86,7 @@ type alias Model =
, attachDD : DD.Model String String , attachDD : DD.Model String String
, modalEdit : Maybe Comp.DetailEdit.Model , modalEdit : Maybe Comp.DetailEdit.Model
, attachRename : Maybe AttachmentRename , attachRename : Maybe AttachmentRename
, keyInputModel : Comp.KeyInput.Model
} }
@ -184,6 +186,7 @@ emptyModel =
, attachDD = DD.init , attachDD = DD.init
, modalEdit = Nothing , modalEdit = Nothing
, attachRename = Nothing , attachRename = Nothing
, keyInputModel = Comp.KeyInput.init
} }
@ -265,6 +268,7 @@ type Msg
| ResetHiddenMsg Field (Result Http.Error BasicResult) | ResetHiddenMsg Field (Result Http.Error BasicResult)
| SaveNameResp (Result Http.Error BasicResult) | SaveNameResp (Result Http.Error BasicResult)
| UpdateThrottle | UpdateThrottle
| KeyInputMsg Comp.KeyInput.Msg
type SaveNameState type SaveNameState

View File

@ -28,14 +28,16 @@ import Comp.ItemDetail.Model
, isEditNotes , isEditNotes
) )
import Comp.ItemMail import Comp.ItemMail
import Comp.KeyInput
import Comp.MarkdownInput import Comp.MarkdownInput
import Comp.OrgForm import Comp.OrgForm
import Comp.PersonForm import Comp.PersonForm
import Comp.SentMails import Comp.SentMails
import Comp.YesNoDimmer import Comp.YesNoDimmer
import Data.Direction exposing (Direction) import Data.Direction
import Data.Fields exposing (Field) import Data.Fields exposing (Field)
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.ItemNav exposing (ItemNav)
import Data.UiSettings exposing (UiSettings) import Data.UiSettings exposing (UiSettings)
import DatePicker import DatePicker
import Dict import Dict
@ -56,8 +58,8 @@ import Util.Maybe
import Util.String import Util.String
update : Nav.Key -> Flags -> Maybe String -> UiSettings -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) update : Nav.Key -> Flags -> ItemNav -> UiSettings -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
update key flags next settings msg model = update key flags inav settings msg model =
case msg of case msg of
Init -> Init ->
let let
@ -83,7 +85,7 @@ update key flags next settings msg model =
( m1, c1, s1 ) = ( m1, c1, s1 ) =
update key update key
flags flags
next inav
settings settings
(TagDropdownMsg (Comp.Dropdown.SetSelection item.tags)) (TagDropdownMsg (Comp.Dropdown.SetSelection item.tags))
model model
@ -91,7 +93,7 @@ update key flags next settings msg model =
( m2, c2, s2 ) = ( m2, c2, s2 ) =
update key update key
flags flags
next inav
settings settings
(DirDropdownMsg (DirDropdownMsg
(Comp.Dropdown.SetSelection (Comp.Dropdown.SetSelection
@ -106,7 +108,7 @@ update key flags next settings msg model =
( m3, c3, s3 ) = ( m3, c3, s3 ) =
update key update key
flags flags
next inav
settings settings
(OrgDropdownMsg (OrgDropdownMsg
(Comp.Dropdown.SetSelection (Comp.Dropdown.SetSelection
@ -121,7 +123,7 @@ update key flags next settings msg model =
( m4, c4, s4 ) = ( m4, c4, s4 ) =
update key update key
flags flags
next inav
settings settings
(CorrPersonMsg (CorrPersonMsg
(Comp.Dropdown.SetSelection (Comp.Dropdown.SetSelection
@ -136,7 +138,7 @@ update key flags next settings msg model =
( m5, c5, s5 ) = ( m5, c5, s5 ) =
update key update key
flags flags
next inav
settings settings
(ConcPersonMsg (ConcPersonMsg
(Comp.Dropdown.SetSelection (Comp.Dropdown.SetSelection
@ -151,7 +153,7 @@ update key flags next settings msg model =
( m6, c6, s6 ) = ( m6, c6, s6 ) =
update key update key
flags flags
next inav
settings settings
(ConcEquipMsg (ConcEquipMsg
(Comp.Dropdown.SetSelection (Comp.Dropdown.SetSelection
@ -164,12 +166,12 @@ update key flags next settings msg model =
m5 m5
( m7, c7, s7 ) = ( m7, c7, s7 ) =
update key flags next settings AddFilesReset m6 update key flags inav settings AddFilesReset m6
( m8, c8, s8 ) = ( m8, c8, s8 ) =
update key update key
flags flags
next inav
settings settings
(FolderDropdownMsg (FolderDropdownMsg
(Comp.Dropdown.SetSelection (Comp.Dropdown.SetSelection
@ -498,7 +500,7 @@ update key flags next settings msg model =
noSub ( { model | deleteItemConfirm = cm }, cmd ) noSub ( { model | deleteItemConfirm = cm }, cmd )
RequestDelete -> RequestDelete ->
update key flags next settings (DeleteItemConfirm Comp.YesNoDimmer.activate) model update key flags inav settings (DeleteItemConfirm Comp.YesNoDimmer.activate) model
SetCorrOrgSuggestion idname -> SetCorrOrgSuggestion idname ->
noSub ( model, setCorrOrg flags model (Just idname) ) noSub ( model, setCorrOrg flags model (Just idname) )
@ -537,7 +539,7 @@ update key flags next settings msg model =
|> List.map mkIdName |> List.map mkIdName
|> Comp.Dropdown.SetOptions |> Comp.Dropdown.SetOptions
in in
update key flags next settings (FolderDropdownMsg opts) model_ update key flags inav settings (FolderDropdownMsg opts) model_
GetFolderResp (Err _) -> GetFolderResp (Err _) ->
noSub ( model, Cmd.none ) noSub ( model, Cmd.none )
@ -548,7 +550,7 @@ update key flags next settings msg model =
Comp.Dropdown.SetOptions tags.items Comp.Dropdown.SetOptions tags.items
( m1, c1, s1 ) = ( m1, c1, s1 ) =
update key flags next settings (TagDropdownMsg tagList) model update key flags inav settings (TagDropdownMsg tagList) model
in in
( m1, c1, s1 ) ( m1, c1, s1 )
@ -560,7 +562,7 @@ update key flags next settings msg model =
opts = opts =
Comp.Dropdown.SetOptions orgs.items Comp.Dropdown.SetOptions orgs.items
in in
update key flags next settings (OrgDropdownMsg opts) model update key flags inav settings (OrgDropdownMsg opts) model
GetOrgResp (Err _) -> GetOrgResp (Err _) ->
noSub ( model, Cmd.none ) noSub ( model, Cmd.none )
@ -571,10 +573,10 @@ update key flags next settings msg model =
Comp.Dropdown.SetOptions ps.items Comp.Dropdown.SetOptions ps.items
( m1, c1, s1 ) = ( m1, c1, s1 ) =
update key flags next settings (CorrPersonMsg opts) model update key flags inav settings (CorrPersonMsg opts) model
( m2, c2, s2 ) = ( m2, c2, s2 ) =
update key flags next settings (ConcPersonMsg opts) m1 update key flags inav settings (ConcPersonMsg opts) m1
in in
( m2, Cmd.batch [ c1, c2 ], Sub.batch [ s1, s2 ] ) ( m2, Cmd.batch [ c1, c2 ], Sub.batch [ s1, s2 ] )
@ -589,7 +591,7 @@ update key flags next settings msg model =
equips.items equips.items
) )
in in
update key flags next settings (ConcEquipMsg opts) model update key flags inav settings (ConcEquipMsg opts) model
GetEquipResp (Err _) -> GetEquipResp (Err _) ->
noSub ( model, Cmd.none ) noSub ( model, Cmd.none )
@ -625,7 +627,7 @@ update key flags next settings msg model =
DeleteResp (Ok res) -> DeleteResp (Ok res) ->
if res.success then if res.success then
case next of case inav.next of
Just id -> Just id ->
noSub ( model, Page.set key (ItemDetailPage id) ) noSub ( model, Page.set key (ItemDetailPage id) )
@ -639,7 +641,7 @@ update key flags next settings msg model =
noSub ( model, Cmd.none ) noSub ( model, Cmd.none )
GetItemResp (Ok item) -> GetItemResp (Ok item) ->
update key flags next settings (SetItem item) model update key flags inav settings (SetItem item) model
GetItemResp (Err _) -> GetItemResp (Err _) ->
noSub ( model, Cmd.none ) noSub ( model, Cmd.none )
@ -834,7 +836,7 @@ update key flags next settings msg model =
DeleteAttachResp (Ok res) -> DeleteAttachResp (Ok res) ->
if res.success then if res.success then
update key flags next settings ReloadItem model update key flags inav settings ReloadItem model
else else
noSub ( model, Cmd.none ) noSub ( model, Cmd.none )
@ -845,7 +847,7 @@ update key flags next settings msg model =
RequestDeleteAttachment id -> RequestDeleteAttachment id ->
update key update key
flags flags
next inav
settings settings
(DeleteAttachConfirm id Comp.YesNoDimmer.activate) (DeleteAttachConfirm id Comp.YesNoDimmer.activate)
model model
@ -1228,6 +1230,42 @@ update key flags next settings msg model =
in in
withSub ( { model | nameSaveThrottle = newThrottle }, cmd ) withSub ( { model | nameSaveThrottle = newThrottle }, cmd )
KeyInputMsg lm ->
let
( km, keys ) =
Comp.KeyInput.update lm model.keyInputModel
model_ =
{ model | keyInputModel = km }
in
if keys == Just Comp.KeyInput.ctrlC then
if model.item.state == "created" then
update key flags inav settings ConfirmItem model_
else
update key flags inav settings UnconfirmItem model_
else if keys == Just Comp.KeyInput.ctrlPoint then
case inav.next of
Just id ->
noSub ( model_, Page.set key (ItemDetailPage id) )
Nothing ->
noSub ( model_, Cmd.none )
else if keys == Just Comp.KeyInput.ctrlComma then
case inav.prev of
Just id ->
noSub ( model_, Page.set key (ItemDetailPage id) )
Nothing ->
noSub ( model_, Cmd.none )
else
-- withSub because the keypress may be inside the name
-- field and requires to activate the throttle
withSub ( model_, Cmd.none )
--- Helper --- Helper

View File

@ -8,12 +8,14 @@ import Comp.Dropdown
import Comp.Dropzone import Comp.Dropzone
import Comp.ItemDetail.Model exposing (Model, Msg(..), NotesField(..), SaveNameState(..)) import Comp.ItemDetail.Model exposing (Model, Msg(..), NotesField(..), SaveNameState(..))
import Comp.ItemMail import Comp.ItemMail
import Comp.KeyInput
import Comp.MarkdownInput import Comp.MarkdownInput
import Comp.SentMails import Comp.SentMails
import Comp.YesNoDimmer import Comp.YesNoDimmer
import Data.Direction import Data.Direction
import Data.Fields import Data.Fields
import Data.Icons as Icons import Data.Icons as Icons
import Data.ItemNav exposing (ItemNav)
import Data.UiSettings exposing (UiSettings) import Data.UiSettings exposing (UiSettings)
import DatePicker import DatePicker
import Dict import Dict
@ -34,11 +36,12 @@ import Util.String
import Util.Time import Util.Time
view : { prev : Maybe String, next : Maybe String } -> UiSettings -> Model -> Html Msg view : ItemNav -> UiSettings -> Model -> Html Msg
view inav settings model = view inav settings model =
div [] div
[]
[ renderItemInfo settings model [ renderItemInfo settings model
, renderDetailMenu inav model , renderDetailMenu settings inav model
, renderMailForm settings model , renderMailForm settings model
, renderAddFilesForm model , renderAddFilesForm model
, div [ class "ui grid" ] , div [ class "ui grid" ]
@ -87,8 +90,16 @@ view inav settings model =
--- Helper --- Helper
renderDetailMenu : { prev : Maybe String, next : Maybe String } -> Model -> Html Msg renderDetailMenu : UiSettings -> ItemNav -> Model -> Html Msg
renderDetailMenu inav model = renderDetailMenu settings inav model =
let
keyDescr name =
if settings.itemDetailShortcuts && model.menuOpen then
" Key '" ++ name ++ "'."
else
""
in
div div
[ classList [ classList
[ ( "ui ablue-comp menu", True ) [ ( "ui ablue-comp menu", True )
@ -109,6 +120,7 @@ renderDetailMenu inav model =
, Maybe.map ItemDetailPage inav.prev , Maybe.map ItemDetailPage inav.prev
|> Maybe.map Page.href |> Maybe.map Page.href
|> Maybe.withDefault (href "#") |> Maybe.withDefault (href "#")
, title ("Previous item." ++ keyDescr "Ctrl-,")
] ]
[ i [ class "caret square left outline icon" ] [] [ i [ class "caret square left outline icon" ] []
] ]
@ -120,6 +132,7 @@ renderDetailMenu inav model =
, Maybe.map ItemDetailPage inav.next , Maybe.map ItemDetailPage inav.next
|> Maybe.map Page.href |> Maybe.map Page.href
|> Maybe.withDefault (href "#") |> Maybe.withDefault (href "#")
, title ("Next item." ++ keyDescr "Ctrl-.")
] ]
[ i [ class "caret square right outline icon" ] [] [ i [ class "caret square right outline icon" ] []
] ]
@ -703,22 +716,36 @@ renderTags settings model =
renderEditMenu : UiSettings -> Model -> List (Html Msg) renderEditMenu : UiSettings -> Model -> List (Html Msg)
renderEditMenu settings model = renderEditMenu settings model =
[ Html.map ModalEditMsg (Comp.DetailEdit.viewModal settings model.modalEdit) [ Html.map ModalEditMsg (Comp.DetailEdit.viewModal settings model.modalEdit)
, div [] , div
[ renderEditButtons model (if settings.itemDetailShortcuts then
Comp.KeyInput.eventsM KeyInputMsg
else
[]
)
[ renderEditButtons settings model
, renderEditForm settings model , renderEditForm settings model
] ]
] ]
renderEditButtons : Model -> Html Msg renderEditButtons : UiSettings -> Model -> Html Msg
renderEditButtons model = renderEditButtons settings model =
let
keyDescr name =
if settings.itemDetailShortcuts then
" Key '" ++ name ++ "'."
else
""
in
div [ class "ui top attached icon ablue-comp menu" ] div [ class "ui top attached icon ablue-comp menu" ]
[ a [ a
[ classList [ classList
[ ( "borderless item", True ) [ ( "borderless item", True )
, ( "invisible", model.item.state /= "created" ) , ( "invisible", model.item.state /= "created" )
] ]
, title "Confirm metadata" , title ("Confirm metadata." ++ keyDescr "Ctrl-c")
, href "#" , href "#"
, onClick ConfirmItem , onClick ConfirmItem
] ]
@ -730,7 +757,7 @@ renderEditButtons model =
, ( "invisible", model.item.state /= "confirmed" ) , ( "invisible", model.item.state /= "confirmed" )
] ]
, href "#" , href "#"
, title "Unconfirm metadata" , title ("Unconfirm metadata." ++ keyDescr "Ctrl-c")
, onClick UnconfirmItem , onClick UnconfirmItem
] ]
[ i [ class "eye slash outline icon" ] [] [ i [ class "eye slash outline icon" ] []

View File

@ -0,0 +1,116 @@
module Comp.KeyInput exposing
( KeyInput
, Model
, Msg
, ctrlC
, ctrlComma
, ctrlN
, ctrlPoint
, ctrlU
, events
, eventsM
, init
, update
)
import Html exposing (Attribute)
import Html.Attributes
import Util.Html exposing (KeyCode(..))
type alias KeyInput =
{ down : List KeyCode
, up : KeyCode
}
ctrlPlus : KeyCode -> KeyInput
ctrlPlus code =
{ down = [ Ctrl ]
, up = code
}
ctrlN : KeyInput
ctrlN =
ctrlPlus Letter_N
ctrlC : KeyInput
ctrlC =
ctrlPlus Letter_C
ctrlU : KeyInput
ctrlU =
ctrlPlus Letter_U
ctrlPoint : KeyInput
ctrlPoint =
ctrlPlus Point
ctrlComma : KeyInput
ctrlComma =
ctrlPlus Comma
type alias Model =
List KeyCode
init : Model
init =
[]
type Msg
= KeyDown (Maybe KeyCode)
| KeyUp (Maybe KeyCode)
events : List (Attribute Msg)
events =
[ Util.Html.onKeyUpCode KeyUp
, Util.Html.onKeyDownCode KeyDown
]
eventsM : (Msg -> msg) -> List (Attribute msg)
eventsM tagger =
List.map (Html.Attributes.map tagger) events
update : Msg -> Model -> ( Model, Maybe KeyInput )
update msg model =
case msg of
KeyDown (Just code) ->
( insert code model, Nothing )
KeyUp (Just code) ->
let
m_ =
remove code model
in
( m_, Just <| KeyInput m_ code )
KeyDown Nothing ->
( model, Nothing )
KeyUp Nothing ->
( model, Nothing )
insert : a -> List a -> List a
insert el list =
if List.member el list then
list
else
el :: list
remove : a -> List a -> List a
remove el list =
List.filter (\e -> e /= el) list

View File

@ -39,6 +39,7 @@ type alias Model =
, searchMenuTagCatCount : Maybe Int , searchMenuTagCatCount : Maybe Int
, searchMenuTagCatCountModel : Comp.IntField.Model , searchMenuTagCatCountModel : Comp.IntField.Model
, formFields : List Field , formFields : List Field
, itemDetailShortcuts : Bool
} }
@ -87,6 +88,7 @@ init flags settings =
False False
"Number of categories in search menu" "Number of categories in search menu"
, formFields = settings.formFields , formFields = settings.formFields
, itemDetailShortcuts = settings.itemDetailShortcuts
} }
, Api.getTags flags "" GetTagsResp , Api.getTags flags "" GetTagsResp
) )
@ -103,6 +105,7 @@ type Msg
| SearchMenuTagMsg Comp.IntField.Msg | SearchMenuTagMsg Comp.IntField.Msg
| SearchMenuTagCatMsg Comp.IntField.Msg | SearchMenuTagCatMsg Comp.IntField.Msg
| FieldListMsg Comp.FieldListSelect.Msg | FieldListMsg Comp.FieldListSelect.Msg
| ToggleItemDetailShortcuts
@ -261,6 +264,15 @@ update sett msg model =
Nothing Nothing
) )
ToggleItemDetailShortcuts ->
let
flag =
not model.itemDetailShortcuts
in
( { model | itemDetailShortcuts = flag }
, Just { sett | itemDetailShortcuts = flag }
)
--- View --- View
@ -342,6 +354,19 @@ view flags _ model =
] ]
] ]
] ]
, div [ class "field" ]
[ div [ class "ui checkbox" ]
[ input
[ type_ "checkbox"
, onCheck (\_ -> ToggleItemDetailShortcuts)
, checked model.itemDetailShortcuts
]
[]
, label []
[ text "Use keyboard shortcuts for navigation and confirm/unconfirm with open edit menu."
]
]
]
, div [ class "grouped fields" ] , div [ class "grouped fields" ]
[ label [] [ text "Position of item notes" ] [ label [] [ text "Position of item notes" ]
, div [ class "field" ] , div [ class "field" ]

View File

@ -0,0 +1,7 @@
module Data.ItemNav exposing (ItemNav)
type alias ItemNav =
{ prev : Maybe String
, next : Maybe String
}

View File

@ -40,6 +40,7 @@ type alias StoredUiSettings =
, searchMenuTagCount : Maybe Int , searchMenuTagCount : Maybe Int
, searchMenuTagCatCount : Maybe Int , searchMenuTagCatCount : Maybe Int
, formFields : Maybe (List String) , formFields : Maybe (List String)
, itemDetailShortcuts : Bool
} }
@ -60,6 +61,7 @@ type alias UiSettings =
, searchMenuTagCount : Int , searchMenuTagCount : Int
, searchMenuTagCatCount : Int , searchMenuTagCatCount : Int
, formFields : List Field , formFields : List Field
, itemDetailShortcuts : Bool
} }
@ -102,6 +104,7 @@ defaults =
, searchMenuTagCount = 6 , searchMenuTagCount = 6
, searchMenuTagCatCount = 3 , searchMenuTagCatCount = 3
, formFields = Data.Fields.all , formFields = Data.Fields.all
, itemDetailShortcuts = False
} }
@ -134,6 +137,7 @@ merge given fallback =
choose choose
(Maybe.map Data.Fields.fromList given.formFields) (Maybe.map Data.Fields.fromList given.formFields)
fallback.formFields fallback.formFields
, itemDetailShortcuts = given.itemDetailShortcuts
} }
@ -157,6 +161,7 @@ toStoredUiSettings settings =
, formFields = , formFields =
List.map Data.Fields.toString settings.formFields List.map Data.Fields.toString settings.formFields
|> Just |> Just
, itemDetailShortcuts = settings.itemDetailShortcuts
} }

View File

@ -18,6 +18,7 @@ import Comp.FixedDropdown
import Comp.ItemCardList import Comp.ItemCardList
import Comp.SearchMenu import Comp.SearchMenu
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.ItemNav exposing (ItemNav)
import Data.Items import Data.Items
import Data.UiSettings exposing (UiSettings) import Data.UiSettings exposing (UiSettings)
import Http import Http
@ -119,7 +120,7 @@ searchTypeString st =
"Contents Only" "Contents Only"
itemNav : String -> Model -> { prev : Maybe String, next : Maybe String } itemNav : String -> Model -> ItemNav
itemNav id model = itemNav id model =
let let
prev = prev =

View File

@ -5,21 +5,22 @@ import Browser.Navigation as Nav
import Comp.ItemDetail import Comp.ItemDetail
import Comp.ItemDetail.Model import Comp.ItemDetail.Model
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.ItemNav exposing (ItemNav)
import Data.UiSettings exposing (UiSettings) import Data.UiSettings exposing (UiSettings)
import Page.ItemDetail.Data exposing (Model, Msg(..)) import Page.ItemDetail.Data exposing (Model, Msg(..))
import Scroll import Scroll
import Task import Task
update : Nav.Key -> Flags -> Maybe String -> UiSettings -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) update : Nav.Key -> Flags -> ItemNav -> UiSettings -> Msg -> Model -> ( Model, Cmd Msg, Sub Msg )
update key flags next settings msg model = update key flags inav settings msg model =
case msg of case msg of
Init id -> Init id ->
let let
( lm, lc, ls ) = ( lm, lc, ls ) =
Comp.ItemDetail.update key Comp.ItemDetail.update key
flags flags
next inav
settings settings
Comp.ItemDetail.Model.Init Comp.ItemDetail.Model.Init
model.detail model.detail
@ -39,7 +40,7 @@ update key flags next settings msg model =
ItemDetailMsg lmsg -> ItemDetailMsg lmsg ->
let let
( lm, lc, ls ) = ( lm, lc, ls ) =
Comp.ItemDetail.update key flags next settings lmsg model.detail Comp.ItemDetail.update key flags inav settings lmsg model.detail
in in
( { model | detail = lm } ( { model | detail = lm }
, Cmd.map ItemDetailMsg lc , Cmd.map ItemDetailMsg lc
@ -51,7 +52,7 @@ update key flags next settings msg model =
lmsg = lmsg =
Comp.ItemDetail.Model.SetItem item Comp.ItemDetail.Model.SetItem item
in in
update key flags next settings (ItemDetailMsg lmsg) model update key flags inav settings (ItemDetailMsg lmsg) model
ItemResp (Err _) -> ItemResp (Err _) ->
( model, Cmd.none, Sub.none ) ( model, Cmd.none, Sub.none )

View File

@ -1,18 +1,13 @@
module Page.ItemDetail.View exposing (view) module Page.ItemDetail.View exposing (view)
import Comp.ItemDetail import Comp.ItemDetail
import Data.ItemNav exposing (ItemNav)
import Data.UiSettings exposing (UiSettings) import Data.UiSettings exposing (UiSettings)
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Page.ItemDetail.Data exposing (Model, Msg(..)) import Page.ItemDetail.Data exposing (Model, Msg(..))
type alias ItemNav =
{ prev : Maybe String
, next : Maybe String
}
view : ItemNav -> UiSettings -> Model -> Html Msg view : ItemNav -> UiSettings -> Model -> Html Msg
view inav settings model = view inav settings model =
div [ class "ui fluid container item-detail-page" ] div [ class "ui fluid container item-detail-page" ]

View File

@ -8,6 +8,8 @@ module Util.Html exposing
, onDragLeave , onDragLeave
, onDragOver , onDragOver
, onDropFiles , onDropFiles
, onKeyDown
, onKeyDownCode
, onKeyUp , onKeyUp
, onKeyUpCode , onKeyUpCode
) )
@ -45,11 +47,35 @@ type KeyCode
| Right | Right
| Enter | Enter
| Space | Space
| ESC
| Letter_C
| Letter_N
| Letter_P
| Letter_H
| Letter_J
| Letter_K
| Letter_L
| Letter_U
| Point
| Comma
| Shift
| Ctrl
| Super
| Code Int
intToKeyCode : Int -> Maybe KeyCode intToKeyCode : Int -> Maybe KeyCode
intToKeyCode code = intToKeyCode code =
case code of case code of
16 ->
Just Shift
17 ->
Just Ctrl
91 ->
Just Super
38 -> 38 ->
Just Up Just Up
@ -68,8 +94,41 @@ intToKeyCode code =
32 -> 32 ->
Just Space Just Space
_ -> 27 ->
Nothing Just ESC
67 ->
Just Letter_C
72 ->
Just Letter_H
74 ->
Just Letter_J
75 ->
Just Letter_K
76 ->
Just Letter_L
78 ->
Just Letter_N
80 ->
Just Letter_P
85 ->
Just Letter_U
188 ->
Just Comma
190 ->
Just Point
n ->
Just (Code n)
onKeyUp : (Int -> msg) -> Attribute msg onKeyUp : (Int -> msg) -> Attribute msg
@ -77,11 +136,21 @@ onKeyUp tagger =
on "keyup" (D.map tagger keyCode) on "keyup" (D.map tagger keyCode)
onKeyDown : (Int -> msg) -> Attribute msg
onKeyDown tagger =
on "keydown" (D.map tagger keyCode)
onKeyUpCode : (Maybe KeyCode -> msg) -> Attribute msg onKeyUpCode : (Maybe KeyCode -> msg) -> Attribute msg
onKeyUpCode tagger = onKeyUpCode tagger =
onKeyUp (intToKeyCode >> tagger) onKeyUp (intToKeyCode >> tagger)
onKeyDownCode : (Maybe KeyCode -> msg) -> Attribute msg
onKeyDownCode tagger =
onKeyDown (intToKeyCode >> tagger)
onClickk : msg -> Attribute msg onClickk : msg -> Attribute msg
onClickk msg = onClickk msg =
Html.Events.preventDefaultOn "click" (D.map alwaysPreventDefault (D.succeed msg)) Html.Events.preventDefaultOn "click" (D.map alwaysPreventDefault (D.succeed msg))