noredink-ui/styleguide-app/Examples/Accordion.elm

416 lines
16 KiB
Elm
Raw Normal View History

module Examples.Accordion exposing
( example
2020-03-31 22:14:18 +03:00
, State, Msg
)
{-|
2020-03-31 22:14:18 +03:00
@docs example
@docs State, Msg
-}
import Accessibility.Styled as Html exposing (Html)
2020-09-11 23:13:18 +03:00
import Browser.Dom as Dom
import Category exposing (Category(..))
import CommonControls
import Css exposing (..)
2021-05-06 02:03:57 +03:00
import Css.Global
2022-04-01 01:52:15 +03:00
import Debug.Control as Control exposing (Control)
import Debug.Control.View as ControlView
import EllieLink
2020-03-31 23:20:03 +03:00
import Example exposing (Example)
import Html.Styled.Attributes as Attributes exposing (css, src)
2022-03-15 21:06:13 +03:00
import KeyboardSupport exposing (Key(..))
2021-05-06 02:03:57 +03:00
import Nri.Ui.Accordion.V3 as Accordion exposing (AccordionEntry(..))
import Nri.Ui.Colors.Extra as ColorsExtra
import Nri.Ui.Colors.V1 as Colors
2021-05-06 02:03:57 +03:00
import Nri.Ui.DisclosureIndicator.V2 as DisclosureIndicator
2019-10-24 20:32:26 +03:00
import Nri.Ui.Heading.V2 as Heading
2021-05-06 02:03:57 +03:00
import Nri.Ui.Svg.V1 as Svg
2021-11-06 01:38:35 +03:00
import Nri.Ui.Text.V6 as Text
2021-05-06 02:03:57 +03:00
import Nri.Ui.UiIcon.V1 as UiIcon
import Set exposing (Set)
2020-09-11 23:13:18 +03:00
import Task
2022-04-01 01:52:15 +03:00
moduleName : String
moduleName =
"Accordion"
version : Int
version =
3
{-| -}
2020-03-31 23:20:03 +03:00
example : Example State Msg
2020-03-31 22:14:18 +03:00
example =
2022-04-01 01:52:15 +03:00
{ name = moduleName
, version = version
, state = init
, update = update
2020-03-31 22:48:26 +03:00
, subscriptions = \_ -> Sub.none
2021-11-06 01:38:35 +03:00
, preview =
[ -- faking a mini version of the Accordion component to give styleguide users a sense of what the
-- component might look like
Html.div []
[ Html.div [ css [ Css.displayFlex, Css.alignItems Css.center ] ]
[ defaultCaret False
, Text.smallBody [ Text.plaintext "Closed" ]
]
, Html.div [ css [ Css.displayFlex, Css.alignItems Css.center ] ]
[ defaultCaret True
, Text.smallBody [ Text.plaintext "Open" ]
]
, Text.caption [ Text.plaintext "Accordion content." ]
]
]
, view = view
, categories = [ Layout ]
2020-09-11 23:13:18 +03:00
, keyboardSupport =
[ { keys = [ Arrow KeyboardSupport.Up ]
, result = "Moves the focus to the previous accordion header button (wraps focus to the last header button)"
}
, { keys = [ Arrow KeyboardSupport.Down ]
, result = "Moves the focus to the next accordion header button (wraps focus to the first header button)"
}
2021-05-06 02:03:57 +03:00
, { keys = [ Arrow KeyboardSupport.Right ]
, result = "Moves the focus to the first accordion header button in a nested list of accordions"
}
, { keys = [ Arrow KeyboardSupport.Left ]
, result = "Moves the focus to the parent accordion header button from a a nested accordion"
}
2020-09-11 23:13:18 +03:00
]
}
2021-11-06 01:38:35 +03:00
defaultCaret : Bool -> Html msg
defaultCaret =
DisclosureIndicator.large [ Css.marginRight (Css.px 8) ]
{-| -}
view : EllieLink.Config -> State -> List (Html Msg)
view ellieLinkConfig model =
let
settings_ =
Control.currentValue model.settings
in
2022-04-01 01:52:15 +03:00
[ ControlView.view
{ ellieLinkConfig = ellieLinkConfig
, name = moduleName
, version = version
, update = UpdateControls
, settings = model.settings
, mainType = "RootHtml.Html String"
2022-04-13 03:32:46 +03:00
, extraImports = [ "import Nri.Ui.DisclosureIndicator.V2 as DisclosureIndicator" ]
, toExampleCode =
\settings ->
[ { sectionName = "Partial example"
, code =
String.join "\n"
2022-04-13 03:37:59 +03:00
[ " div [] ["
, " Accordion.view"
, " { entries ="
, " [ Accordion.AccordionEntry"
, " { caret = " ++ Tuple.first settings.icon
, " , content = \\() -> " ++ Tuple.first settings.content
, " , entryClass = \"customizable-example\""
, " , headerContent = " ++ Tuple.first settings.headerContent
, " , headerId = \"customizable-example-header\""
, " , headerLevel = Accordion.H4"
, " , isExpanded = True"
, " , toggle = Nothing"
, " }"
, " []"
, " ]"
, " , -- When using Accordion, be sure to wire up Focus management correctly!"
, " focus = identity"
, " }"
, " , Accordion.styleAccordion"
, " { entryStyles = []"
, " , entryExpandedStyles = []"
, " , entryClosedStyles = []"
, " , headerStyles = []"
, " , headerExpandedStyles = []"
, " , headerClosedStyles = []"
, " , contentStyles = []"
, " }"
, " ]"
]
}
]
}
, Accordion.view
{ entries =
[ AccordionEntry
{ caret = Tuple.second settings_.icon
2022-04-13 03:17:55 +03:00
, content = \() -> Tuple.second settings_.content
, entryClass = "customizable-example"
2022-04-13 03:17:55 +03:00
, headerContent = Tuple.second settings_.headerContent
, headerId = "customizable-example-header"
, headerLevel = Accordion.H4
, isExpanded = Set.member 4 model.expanded
, toggle = Just (Toggle 4)
}
[]
, AccordionEntry
2021-05-06 02:03:57 +03:00
{ caret = defaultCaret
, content = \_ -> Html.text "🍎 There are many kinds of apples! Apples are more genetically diverse than humans. The genetic diversity of apples means that to preserve delicious apple varieties, growers must use grafting rather than seeds. In the apple market, clones have already taken over! 🍏"
2021-05-06 02:30:03 +03:00
, entryClass = "accordion-example"
, headerContent = Html.text "Apples"
2021-05-06 02:03:57 +03:00
, headerId = "accordion-entry__1"
, headerLevel = Accordion.H4
2022-04-01 01:52:15 +03:00
, isExpanded = Set.member 1 model.expanded
2021-05-06 02:03:57 +03:00
, toggle = Just (Toggle 1)
}
[ AccordionEntry
{ caret = defaultCaret
, content =
\_ ->
Html.div []
[ Html.img "Basket of Gala Apples"
[ css [ Css.maxWidth (Css.px 100), Css.maxHeight (Css.px 100) ]
, src "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4b/Malus-Gala.jpg/1205px-Malus-Gala.jpg"
]
, Html.a [ Attributes.href "https://en.wikipedia.org/wiki/Gala_(apple)" ]
[ Html.text "Wikipedia article on Gala Apples" ]
]
2021-05-06 02:30:03 +03:00
, entryClass = "accordion-example-child"
, headerContent = Html.text "Gala"
, headerId = "accordion-entry__11"
, headerLevel = Accordion.H5
2022-04-01 01:52:15 +03:00
, isExpanded = Set.member 11 model.expanded
, toggle = Just (Toggle 11)
}
[]
, AccordionEntry
{ caret = defaultCaret
, content =
\_ ->
Html.div []
[ Html.img "Freshly-washed Granny Smith Apple"
[ css [ Css.maxWidth (Css.px 100), Css.maxHeight (Css.px 100) ]
, src "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0e/One_Green_Apple.jpg/1280px-One_Green_Apple.jpg"
]
, Html.a [ Attributes.href "https://en.wikipedia.org/wiki/Granny_Smith" ]
[ Html.text "Wikipedia article on Granny Smith Apples" ]
]
2021-05-06 02:30:03 +03:00
, entryClass = "accordion-example-child"
, headerContent = Html.text "Granny Smith"
, headerId = "accordion-entry__12"
, headerLevel = Accordion.H5
2022-04-01 01:52:15 +03:00
, isExpanded = Set.member 12 model.expanded
, toggle = Just (Toggle 12)
}
[]
, AccordionEntry
{ caret = defaultCaret
, content =
\_ ->
Html.div []
[ Html.img "3 Fuji Apples resting on gingham fabric"
[ css [ Css.maxWidth (Css.px 100), Css.maxHeight (Css.px 100) ]
, src "https://upload.wikimedia.org/wikipedia/commons/thumb/8/81/Fuji_apples.jpg/1920px-Fuji_apples.jpg"
]
, Html.a [ Attributes.href "https://en.wikipedia.org/wiki/Fuji_(apple)" ]
[ Html.text "Wikipedia article on Fuji Apples" ]
]
2021-05-06 02:30:03 +03:00
, entryClass = "accordion-example-child"
, headerContent = Html.text "Fuji"
, headerId = "accordion-entry__13"
2021-05-06 02:03:57 +03:00
, headerLevel = Accordion.H5
2022-04-01 01:52:15 +03:00
, isExpanded = Set.member 13 model.expanded
, toggle = Just (Toggle 13)
2021-05-06 02:03:57 +03:00
}
[]
]
, AccordionEntry
{ caret = defaultCaret
, content = \_ -> Html.text "🍊 I don't know anything about oranges! Except: YUM! 🍊"
2021-05-06 02:30:03 +03:00
, entryClass = "accordion-example"
, headerContent = Html.text "Oranges"
, headerId = "accordion-entry__2"
2021-05-06 02:03:57 +03:00
, headerLevel = Accordion.H4
2022-04-01 01:52:15 +03:00
, isExpanded = Set.member 2 model.expanded
, toggle = Just (Toggle 2)
2021-05-06 02:03:57 +03:00
}
[]
, AccordionEntry
{ caret =
\isOpen ->
(if isOpen then
UiIcon.tree
else
UiIcon.sprout
)
|> Svg.withWidth (Css.px 30)
|> Svg.withCss [ Css.marginRight (Css.px 8) ]
|> Svg.toHtml
2021-05-06 02:03:57 +03:00
, content =
\_ ->
Html.div
[ css
[ Css.backgroundColor Colors.gray92
, Css.minHeight (Css.vh 100)
, Css.padding (Css.px 20)
]
]
2021-05-06 02:30:03 +03:00
[ Html.a [ Attributes.href "https://en.wikipedia.org/wiki/Apple#/media/File:95apple.jpeg" ]
[ Html.img "Wild Apple" [ src "https://upload.wikimedia.org/wikipedia/commons/9/92/95apple.jpeg" ] ]
2021-05-06 02:03:57 +03:00
]
, entryClass = "fixed-positioning-accordion-example"
, headerContent = Html.text "Advanced Example: Expand & Scroll!"
2021-05-06 02:03:57 +03:00
, headerId = "accordion-entry__6"
, headerLevel = Accordion.H4
2022-04-01 01:52:15 +03:00
, isExpanded = Set.member 6 model.expanded
2021-05-06 02:03:57 +03:00
, toggle = Just (Toggle 6)
}
[]
]
2020-09-11 23:13:18 +03:00
, focus = Focus
}
2021-05-06 02:03:57 +03:00
, Accordion.styleAccordion
{ entryStyles =
2021-05-06 02:30:03 +03:00
[ Css.Global.withClass "fixed-positioning-accordion-example"
[ Css.marginLeft (Css.px -20)
, Css.position Css.relative
2021-05-06 02:03:57 +03:00
]
2021-05-06 02:30:03 +03:00
, Css.Global.withClass "accordion-example-child"
[ Css.marginLeft (Css.px 16) ]
]
2021-05-06 02:03:57 +03:00
, entryExpandedStyles =
[ Css.Global.withClass "fixed-positioning-accordion-example"
[ Css.Global.children
[ Css.Global.h4
[ Css.position Css.sticky
, Css.property "position" "-webkit-sticky"
, Css.top (Css.px -8)
]
2021-05-06 02:03:57 +03:00
]
]
]
, entryClosedStyles = []
, headerStyles =
[ Css.Global.withClass "fixed-positioning-accordion-example"
[ Css.padding (Css.px 20)
]
]
, headerExpandedStyles =
[ Css.Global.withClass "fixed-positioning-accordion-example"
[ Css.backgroundColor Colors.gray96
, Css.borderRadius (Css.px 8)
, Css.boxShadow5 Css.zero Css.zero (px 10) zero (ColorsExtra.withAlpha 0.2 Colors.gray20)
]
]
, headerClosedStyles = []
, contentStyles = []
}
]
{-| -}
init : State
init =
2022-04-01 01:52:15 +03:00
{ settings = initSettings
, expanded = Set.empty
}
{-| -}
type alias State =
2022-04-01 01:52:15 +03:00
{ settings : Control Settings
, expanded : Set Int
}
type alias Settings =
{ icon : ( String, Bool -> Html Msg )
2022-04-13 03:17:55 +03:00
, headerContent : ( String, Html Msg )
, content : ( String, Html Msg )
}
2022-04-01 01:52:15 +03:00
initSettings : Control Settings
initSettings =
Control.record Settings
|> Control.field "icon" controlIcon
2022-04-13 03:17:55 +03:00
|> Control.field "headerContent" controlHeaderContent
|> Control.field "content" controlContent
controlIcon : Control ( String, Bool -> Html Msg )
controlIcon =
2022-04-13 03:23:24 +03:00
Control.choice
[ ( "DisclosureIndicator"
2022-04-13 03:23:24 +03:00
, Control.value
( "DisclosureIndicator.large [ Css.marginRight (Css.px 8) ]"
, DisclosureIndicator.large [ Css.marginRight (Css.px 8) ]
)
)
, ( "none", Control.value ( "\\_ -> text \"\"", \_ -> Html.text "" ) )
2022-04-13 03:23:24 +03:00
, ( "UiIcon"
, Control.map
(\( code, icon ) ->
( "\\_ -> Svg.toHtml " ++ code
, \_ -> Svg.toHtml icon
)
)
CommonControls.uiIcon
)
]
2022-04-01 01:52:15 +03:00
2022-04-13 03:17:55 +03:00
controlHeaderContent : Control ( String, Html Msg )
controlHeaderContent =
Control.map
(\v -> ( quoteF "text" v, Html.text v ))
(Control.string "Berries")
2022-04-13 03:17:55 +03:00
controlContent : Control ( String, Html Msg )
controlContent =
Control.map
(\v -> ( quoteF "text" v, Html.text v ))
(Control.stringTextarea "🍓 There are many types of berries and all of them are delicious (or poisonous (or both)). Blackberries and mulberries are especially drool-worthy.")
quoteF : String -> String -> String
quoteF f v =
f ++ " \"" ++ v ++ "\""
2022-04-13 03:17:55 +03:00
2022-04-01 01:52:15 +03:00
type Msg
= UpdateControls (Control Settings)
| Toggle Int Bool
| Focus String
| Focused (Result Dom.Error ())
{-| -}
update : Msg -> State -> ( State, Cmd Msg )
2020-09-11 23:13:18 +03:00
update msg model =
case msg of
2022-04-01 01:52:15 +03:00
UpdateControls settings ->
( { model | settings = settings }
, Cmd.none
)
2021-05-06 02:03:57 +03:00
Toggle id expand ->
( if expand then
2022-04-01 01:52:15 +03:00
{ model | expanded = Set.insert id model.expanded }
2021-05-06 02:03:57 +03:00
else
2022-04-01 01:52:15 +03:00
{ model | expanded = Set.remove id model.expanded }
2021-05-06 02:03:57 +03:00
, Cmd.none
)
2020-09-11 23:13:18 +03:00
Focus id ->
( model, Task.attempt Focused (Dom.focus id) )
Focused _ ->
( model, Cmd.none )