2020-05-07 22:46:21 +03:00
|
|
|
module Examples.Menu exposing (Msg, State, example)
|
|
|
|
|
|
|
|
{-|
|
|
|
|
|
|
|
|
@docs Msg, State, example
|
|
|
|
|
|
|
|
-}
|
|
|
|
|
2020-05-07 23:22:49 +03:00
|
|
|
import Accessibility.Styled as Html exposing (..)
|
2020-06-19 23:41:28 +03:00
|
|
|
import AtomicDesignType exposing (AtomicDesignType(..))
|
2021-02-11 03:01:27 +03:00
|
|
|
import Browser.Dom as Dom
|
2020-05-07 22:46:21 +03:00
|
|
|
import Category exposing (Category(..))
|
|
|
|
import Css
|
2020-05-07 23:22:49 +03:00
|
|
|
import Debug.Control as Control exposing (Control)
|
2020-05-07 22:46:21 +03:00
|
|
|
import Example exposing (Example)
|
2020-05-07 23:22:49 +03:00
|
|
|
import Html.Styled.Attributes exposing (css)
|
2020-06-20 00:45:32 +03:00
|
|
|
import KeyboardSupport exposing (Direction(..), Key(..))
|
2021-02-11 04:30:44 +03:00
|
|
|
import Nri.Ui.ClickableSvg.V2 as ClickableSvg
|
2020-05-07 23:22:49 +03:00
|
|
|
import Nri.Ui.ClickableText.V3 as ClickableText
|
2020-05-07 22:46:21 +03:00
|
|
|
import Nri.Ui.Heading.V2 as Heading
|
2021-02-11 02:51:55 +03:00
|
|
|
import Nri.Ui.Menu.V2 as Menu
|
2020-05-07 23:22:49 +03:00
|
|
|
import Nri.Ui.Svg.V1 as Svg exposing (Svg)
|
2021-02-11 04:30:44 +03:00
|
|
|
import Nri.Ui.Tooltip.V2 as Tooltip
|
2020-05-07 23:22:49 +03:00
|
|
|
import Nri.Ui.UiIcon.V1 as UiIcon
|
|
|
|
import Set exposing (Set)
|
2021-02-11 03:01:27 +03:00
|
|
|
import Task
|
2020-05-07 22:46:21 +03:00
|
|
|
|
|
|
|
|
|
|
|
{-| -}
|
|
|
|
example : Example State Msg
|
|
|
|
example =
|
2020-09-09 21:43:10 +03:00
|
|
|
{ name = "Menu"
|
2021-02-11 02:51:55 +03:00
|
|
|
, version = 2
|
2020-05-07 22:46:21 +03:00
|
|
|
, state = init
|
|
|
|
, update = update
|
|
|
|
, subscriptions = \_ -> Sub.none
|
|
|
|
, categories = [ Widgets ]
|
2020-06-20 00:16:10 +03:00
|
|
|
, atomicDesignType = Molecule
|
2021-02-11 02:50:33 +03:00
|
|
|
, keyboardSupport =
|
|
|
|
[ { keys = [ Space ], result = "Opens the menu" }
|
|
|
|
, { keys = [ Enter ], result = "Opens the menu" }
|
|
|
|
, { keys = [ Tab ], result = "Takes focus out of the menu to the next focusable element." }
|
|
|
|
, { keys = [ Tab, Shift ], result = "Takes focus out of the menu to the previous focusable element." }
|
|
|
|
, { keys = [ Arrow KeyboardSupport.Up ]
|
2021-03-05 00:47:18 +03:00
|
|
|
, result = "If menu is closed, opens the menu & selects the last menu item.\nIf menu is open, moves the focus to the previous menu item."
|
2021-02-11 02:50:33 +03:00
|
|
|
}
|
|
|
|
, { keys = [ Arrow KeyboardSupport.Down ]
|
2021-03-05 00:47:18 +03:00
|
|
|
, result = "If menu is closed, opens the menu & selects the first menu item.\nIf menu is open, moves the focus to the next menu item."
|
2021-02-11 02:50:33 +03:00
|
|
|
}
|
|
|
|
, { keys = [ Esc ], result = "Closes the menu" }
|
|
|
|
]
|
2020-05-07 23:22:49 +03:00
|
|
|
, view = view
|
2020-05-07 22:46:21 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-07 23:22:49 +03:00
|
|
|
view : State -> List (Html Msg)
|
|
|
|
view state =
|
|
|
|
let
|
|
|
|
viewConfiguration =
|
|
|
|
Control.currentValue state.viewConfiguration
|
|
|
|
|
2021-02-11 04:30:44 +03:00
|
|
|
viewCustomConfiguration =
|
|
|
|
Control.currentValue state.viewCustomConfiguration
|
2020-05-07 23:22:49 +03:00
|
|
|
|
|
|
|
isOpen name =
|
|
|
|
case state.openMenu of
|
|
|
|
Just open ->
|
|
|
|
open == name
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
False
|
|
|
|
in
|
|
|
|
[ div [ css [ Css.displayFlex, Css.flexWrap Css.wrap ] ]
|
|
|
|
[ Html.h3 [ css [ Css.width (Css.pct 100) ] ] [ Html.text "Nri.Menu.view" ]
|
2020-05-07 23:32:30 +03:00
|
|
|
, viewControl SetViewConfiguration state.viewConfiguration
|
2020-05-07 23:22:49 +03:00
|
|
|
, Menu.view
|
|
|
|
{ isOpen = isOpen "1stPeriodEnglish"
|
2021-02-11 23:26:36 +03:00
|
|
|
, focusAndToggle = FocusAndToggle "1stPeriodEnglish"
|
2021-02-11 04:06:10 +03:00
|
|
|
, buttonId = "1stPeriodEnglish__button"
|
|
|
|
, menuId = "1stPeriodEnglish__menu"
|
2020-05-07 23:22:49 +03:00
|
|
|
, alignment = viewConfiguration.alignment
|
|
|
|
, isDisabled = viewConfiguration.isDisabled
|
|
|
|
, menuWidth = viewConfiguration.menuWidth
|
2020-05-08 00:12:37 +03:00
|
|
|
, entries =
|
2021-03-05 00:47:18 +03:00
|
|
|
[ Menu.entry "hello-button" <|
|
|
|
|
\attrs ->
|
|
|
|
ClickableText.button "Hello"
|
|
|
|
[ ClickableText.onClick (ConsoleLog "Hello")
|
|
|
|
, ClickableText.small
|
|
|
|
, ClickableText.custom attrs
|
|
|
|
]
|
|
|
|
, Menu.group "Menu group"
|
|
|
|
[ Menu.entry "gift-button" <|
|
2021-02-11 04:53:41 +03:00
|
|
|
\attrs ->
|
2021-03-05 00:47:18 +03:00
|
|
|
ClickableText.button "Gift"
|
|
|
|
[ ClickableText.onClick (ConsoleLog "Gift")
|
2021-02-11 04:53:41 +03:00
|
|
|
, ClickableText.small
|
|
|
|
, ClickableText.custom attrs
|
2021-03-05 00:47:18 +03:00
|
|
|
, ClickableText.icon UiIcon.gift
|
2021-02-11 04:53:41 +03:00
|
|
|
]
|
2021-03-05 00:47:18 +03:00
|
|
|
, Menu.entry "null-button" <|
|
2021-02-11 04:53:41 +03:00
|
|
|
\attrs ->
|
2021-03-05 00:47:18 +03:00
|
|
|
ClickableText.button "Nope!"
|
|
|
|
[ ClickableText.onClick (ConsoleLog "Nope!")
|
2021-02-11 04:53:41 +03:00
|
|
|
, ClickableText.small
|
|
|
|
, ClickableText.custom attrs
|
2021-03-05 00:47:18 +03:00
|
|
|
, ClickableText.icon UiIcon.null
|
2021-02-11 04:53:41 +03:00
|
|
|
]
|
2021-03-05 02:04:05 +03:00
|
|
|
, Menu.entry "no-icon-button" <|
|
|
|
|
\attrs ->
|
|
|
|
ClickableText.button "Skip"
|
|
|
|
[ ClickableText.onClick (ConsoleLog "Skip")
|
|
|
|
, ClickableText.small
|
|
|
|
, ClickableText.custom attrs
|
|
|
|
]
|
2020-05-08 00:12:37 +03:00
|
|
|
]
|
2021-03-05 02:04:05 +03:00
|
|
|
, Menu.entry "performance-button" <|
|
|
|
|
\attrs ->
|
|
|
|
ClickableText.button "Performance"
|
|
|
|
[ ClickableText.onClick (ConsoleLog "Performance")
|
|
|
|
, ClickableText.small
|
|
|
|
, ClickableText.custom attrs
|
|
|
|
]
|
2020-05-08 00:12:37 +03:00
|
|
|
]
|
2020-05-07 23:22:49 +03:00
|
|
|
}
|
2021-02-11 04:35:34 +03:00
|
|
|
{ title = "1st Period English with Mx. Trainer"
|
|
|
|
, icon = viewConfiguration.icon
|
|
|
|
, hasBorder = viewConfiguration.hasBorder
|
|
|
|
, wrapping = viewConfiguration.wrapping
|
|
|
|
, buttonWidth = viewConfiguration.buttonWidth
|
|
|
|
}
|
2020-05-07 23:22:49 +03:00
|
|
|
]
|
|
|
|
, div
|
|
|
|
[ css [ Css.displayFlex, Css.flexWrap Css.wrap ] ]
|
2021-02-11 04:30:44 +03:00
|
|
|
[ Html.h3 [ css [ Css.width (Css.pct 100) ] ] [ Html.text "Nri.Menu.viewCustom" ]
|
|
|
|
, viewControl SetIconButtonWithMenuConfiguration state.viewCustomConfiguration
|
2021-03-05 02:04:05 +03:00
|
|
|
|
|
|
|
--, Menu.viewCustom
|
|
|
|
-- { buttonId = "icon-button-with-menu__button"
|
|
|
|
-- , menuId = "icon-button-with-menu__menu"
|
|
|
|
-- , isOpen = isOpen "icon-button-with-menu"
|
|
|
|
-- , focusAndToggle = FocusAndToggle "icon-button-with-menu"
|
|
|
|
-- , alignment = viewCustomConfiguration.alignment
|
|
|
|
-- , isDisabled = viewCustomConfiguration.isDisabled
|
|
|
|
-- , menuWidth = viewCustomConfiguration.menuWidth
|
|
|
|
-- , entries = []
|
|
|
|
-- }
|
|
|
|
-- <|
|
|
|
|
-- \buttonAttributes ->
|
|
|
|
-- Tooltip.view
|
|
|
|
-- { trigger =
|
|
|
|
-- \attrs ->
|
|
|
|
-- ClickableSvg.button "Menu.viewCustom: Click me!"
|
|
|
|
-- viewCustomConfiguration.icon
|
|
|
|
-- [ ClickableSvg.disabled viewCustomConfiguration.isDisabled
|
|
|
|
-- , ClickableSvg.custom (attrs ++ buttonAttributes)
|
|
|
|
-- , ClickableSvg.exactWidth 25
|
|
|
|
-- , ClickableSvg.exactHeight 25
|
|
|
|
-- , ClickableSvg.css [ Css.marginLeft (Css.px 10) ]
|
|
|
|
-- ]
|
|
|
|
-- , id = "viewCustom-example-tooltip"
|
|
|
|
-- }
|
|
|
|
-- [ Tooltip.plaintext "Menu.viewCustom: Click me!"
|
|
|
|
-- , Tooltip.primaryLabel
|
|
|
|
-- , Tooltip.onHover (ShowTooltip "viewCustom")
|
|
|
|
-- , Tooltip.open (Set.member "viewCustom" state.openTooltips)
|
|
|
|
-- , Tooltip.smallPadding
|
|
|
|
-- , Tooltip.fitToContent
|
|
|
|
-- ]
|
2020-05-07 23:22:49 +03:00
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2020-05-07 23:32:30 +03:00
|
|
|
viewControl : (Control a -> Msg) -> Control a -> Html Msg
|
|
|
|
viewControl setControl control =
|
|
|
|
code
|
|
|
|
[ css [ Css.minWidth (Css.px 300), Css.marginRight (Css.px 20) ] ]
|
|
|
|
[ Control.view setControl control
|
|
|
|
|> fromUnstyled
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2020-05-07 22:46:21 +03:00
|
|
|
{-| -}
|
|
|
|
init : State
|
|
|
|
init =
|
2020-05-07 23:22:49 +03:00
|
|
|
{ openMenu = Nothing
|
|
|
|
, checkboxChecked = False
|
|
|
|
, openTooltips = Set.empty
|
|
|
|
, viewConfiguration = initViewConfiguration
|
2021-02-11 04:30:44 +03:00
|
|
|
, viewCustomConfiguration = initIconButtonWithMenuConfiguration
|
2020-05-07 23:22:49 +03:00
|
|
|
}
|
2020-05-07 22:46:21 +03:00
|
|
|
|
|
|
|
|
|
|
|
{-| -}
|
|
|
|
type alias State =
|
2020-05-07 23:22:49 +03:00
|
|
|
{ openMenu : Maybe Id
|
|
|
|
, checkboxChecked : Bool
|
|
|
|
, openTooltips : Set String
|
|
|
|
, viewConfiguration : Control ViewConfiguration
|
2021-02-11 04:30:44 +03:00
|
|
|
, viewCustomConfiguration : Control IconButtonWithMenuConfiguration
|
2020-05-07 23:22:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type alias ViewConfiguration =
|
|
|
|
{ isDisabled : Bool
|
|
|
|
, hasBorder : Bool
|
|
|
|
, alignment : Menu.Alignment
|
|
|
|
, wrapping : Menu.TitleWrapping
|
|
|
|
, buttonWidth : Maybe Int
|
|
|
|
, menuWidth : Maybe Int
|
|
|
|
, icon : Maybe Svg
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
initViewConfiguration : Control ViewConfiguration
|
|
|
|
initViewConfiguration =
|
|
|
|
Control.record ViewConfiguration
|
|
|
|
|> Control.field "isDisabled" (Control.bool False)
|
|
|
|
|> Control.field "hasBorder" (Control.bool True)
|
|
|
|
|> Control.field "alignment"
|
|
|
|
(Control.choice
|
|
|
|
[ ( "Right", Control.value Menu.Right )
|
|
|
|
, ( "Left", Control.value Menu.Left )
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|> Control.field "wrapping"
|
|
|
|
(Control.choice
|
|
|
|
[ ( "WrapAndExpandTitle", Control.value Menu.WrapAndExpandTitle )
|
|
|
|
, ( "TruncateTitle", Control.value Menu.TruncateTitle )
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|> Control.field "buttonWidth"
|
|
|
|
(Control.maybe False (Control.choice [ ( "220", Control.value 220 ) ]))
|
|
|
|
|> Control.field "menuWidth"
|
|
|
|
(Control.maybe False (Control.choice [ ( "180", Control.value 220 ) ]))
|
|
|
|
|> Control.field "icon"
|
|
|
|
(Control.maybe False
|
|
|
|
(Control.choice
|
|
|
|
[ ( "gift", Control.value UiIcon.gift )
|
|
|
|
, ( "hat", Control.value UiIcon.hat )
|
|
|
|
, ( "star", Control.value UiIcon.star )
|
|
|
|
]
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type alias IconButtonWithMenuConfiguration =
|
|
|
|
{ isDisabled : Bool
|
|
|
|
, alignment : Menu.Alignment
|
|
|
|
, menuWidth : Maybe Int
|
|
|
|
, icon : Svg
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
initIconButtonWithMenuConfiguration : Control IconButtonWithMenuConfiguration
|
|
|
|
initIconButtonWithMenuConfiguration =
|
|
|
|
Control.record IconButtonWithMenuConfiguration
|
|
|
|
|> Control.field "isDisabled" (Control.bool False)
|
|
|
|
|> Control.field "alignment"
|
|
|
|
(Control.choice
|
|
|
|
[ ( "Left", Control.value Menu.Left )
|
|
|
|
, ( "Right", Control.value Menu.Right )
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|> Control.field "menuWidth"
|
|
|
|
(Control.maybe False (Control.choice [ ( "180", Control.value 220 ) ]))
|
|
|
|
|> Control.field "icon"
|
|
|
|
(Control.choice
|
|
|
|
[ ( "edit", Control.value UiIcon.edit )
|
|
|
|
, ( "share", Control.value UiIcon.share )
|
|
|
|
, ( "gear", Control.value UiIcon.gear )
|
|
|
|
]
|
|
|
|
)
|
2020-05-07 22:46:21 +03:00
|
|
|
|
|
|
|
|
|
|
|
{-| -}
|
|
|
|
type Msg
|
2021-02-11 23:26:36 +03:00
|
|
|
= ShowTooltip String Bool
|
2020-05-07 23:22:49 +03:00
|
|
|
| ConsoleLog String
|
|
|
|
| SetViewConfiguration (Control ViewConfiguration)
|
|
|
|
| SetIconButtonWithMenuConfiguration (Control IconButtonWithMenuConfiguration)
|
2021-02-11 23:26:36 +03:00
|
|
|
| FocusAndToggle String { isOpen : Bool, focus : Maybe String }
|
2021-02-11 03:01:27 +03:00
|
|
|
| Focused (Result Dom.Error ())
|
2020-05-07 23:22:49 +03:00
|
|
|
| NoOp
|
2020-05-07 22:46:21 +03:00
|
|
|
|
|
|
|
|
|
|
|
{-| -}
|
|
|
|
update : Msg -> State -> ( State, Cmd Msg )
|
|
|
|
update msg state =
|
|
|
|
case msg of
|
2020-05-07 23:22:49 +03:00
|
|
|
ShowTooltip key isOpen ->
|
|
|
|
( { state
|
|
|
|
| openTooltips =
|
|
|
|
if isOpen then
|
|
|
|
Set.insert key state.openTooltips
|
|
|
|
|
|
|
|
else
|
|
|
|
Set.remove key state.openTooltips
|
|
|
|
}
|
|
|
|
, Cmd.none
|
|
|
|
)
|
|
|
|
|
|
|
|
ConsoleLog message ->
|
|
|
|
let
|
|
|
|
_ =
|
|
|
|
Debug.log "Menu Example" message
|
|
|
|
in
|
|
|
|
( state, Cmd.none )
|
|
|
|
|
|
|
|
SetViewConfiguration configuration ->
|
|
|
|
( { state | viewConfiguration = configuration }, Cmd.none )
|
|
|
|
|
|
|
|
SetIconButtonWithMenuConfiguration configuration ->
|
2021-02-11 04:30:44 +03:00
|
|
|
( { state | viewCustomConfiguration = configuration }, Cmd.none )
|
2020-05-07 23:22:49 +03:00
|
|
|
|
2021-02-11 23:26:36 +03:00
|
|
|
FocusAndToggle id { isOpen, focus } ->
|
|
|
|
( { state
|
|
|
|
| openMenu =
|
|
|
|
if isOpen then
|
|
|
|
Just id
|
|
|
|
|
|
|
|
else
|
|
|
|
Nothing
|
|
|
|
}
|
|
|
|
, Maybe.map (\idString -> Task.attempt Focused (Dom.focus idString)) focus
|
|
|
|
|> Maybe.withDefault Cmd.none
|
|
|
|
)
|
2021-02-11 03:01:27 +03:00
|
|
|
|
|
|
|
Focused _ ->
|
|
|
|
( state, Cmd.none )
|
|
|
|
|
|
|
|
NoOp ->
|
|
|
|
( state, Cmd.none )
|
|
|
|
|
2020-05-07 23:22:49 +03:00
|
|
|
|
|
|
|
|
|
|
|
-- INTERNAL
|
|
|
|
|
|
|
|
|
|
|
|
type alias Id =
|
|
|
|
String
|
|
|
|
|
|
|
|
|
|
|
|
type alias Value =
|
|
|
|
String
|