mirror of
https://github.com/NoRedInk/noredink-ui.git
synced 2025-01-07 06:56:53 +03:00
Merge pull request #691 from NoRedInk/kraken/accordion-v3
Kraken/accordion v3
This commit is contained in:
commit
0ed7f7e83b
1
elm.json
1
elm.json
@ -8,6 +8,7 @@
|
||||
"Nri.Ui",
|
||||
"Nri.Ui.Accordion.V1",
|
||||
"Nri.Ui.Accordion.V2",
|
||||
"Nri.Ui.Accordion.V3",
|
||||
"Nri.Ui.AssetPath",
|
||||
"Nri.Ui.AssignmentIcon.V2",
|
||||
"Nri.Ui.Button.V10",
|
||||
|
386
src/Nri/Ui/Accordion/V3.elm
Normal file
386
src/Nri/Ui/Accordion/V3.elm
Normal file
@ -0,0 +1,386 @@
|
||||
module Nri.Ui.Accordion.V3 exposing
|
||||
( view, HeaderLevel(..)
|
||||
, AccordionEntry(..), Entry
|
||||
, StyleOptions, styleAccordion
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
## Changes from V2
|
||||
|
||||
- Do not render collapsed content
|
||||
- Allow disclosure indicator to be top-aligned
|
||||
- Allow fully-customizable carets
|
||||
- Removes as many default styles as possible
|
||||
- Change header resets to inline styles
|
||||
- Replace custom passed-in styles with a class-based styling approach (for performance reasons -- getting elm-css class names is too slow.)
|
||||
- Prevent default browser event on arrow keys (so that the page doesn't scroll when the focus changes)
|
||||
- Support multiple levels of accordions
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
import Nri.Ui.DisclosureIndicator.V2 as DisclosureIndicator
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
div []
|
||||
[ Accordion.view
|
||||
{ entries =
|
||||
[ AccordionEntry
|
||||
{ caret = \isOpen -> DisclosureIndicator.large [ marginRight (px 8) ] isOpen
|
||||
, content = \() -> text "Accordion Content"
|
||||
, entryClass = "a-class-distinguishing-this-accordion-from-others-on-the-page"
|
||||
, headerContent = text "Accordion Header"
|
||||
, headerId = "a-unique-id-for-this-accordion-header-button"
|
||||
, headerLevel = Accordion.H1
|
||||
, isExpanded = model.isAccordionOpen
|
||||
, toggle = Just ToggleAccordion
|
||||
}
|
||||
[]
|
||||
]
|
||||
, focus = Focus
|
||||
}
|
||||
, Accordion.styleAccordion
|
||||
{ entryStyles = []
|
||||
, entryExpandedStyles = []
|
||||
, entryClosedStyles = []
|
||||
, headerStyles = []
|
||||
, headerExpandedStyles = []
|
||||
, headerClosedStyles = []
|
||||
, contentStyles = []
|
||||
}
|
||||
]
|
||||
|
||||
@docs view, HeaderLevel
|
||||
@docs AccordionEntry, Entry
|
||||
@docs StyleOptions, styleAccordion
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled exposing (Attribute, Html, button, div, section, text)
|
||||
import Accessibility.Styled.Aria as Aria
|
||||
import Accessibility.Styled.Key as Key
|
||||
import Accessibility.Styled.Role as Role
|
||||
import Accessibility.Styled.Widget as Widget
|
||||
import Css exposing (..)
|
||||
import Css.Global
|
||||
import Html.Styled.Attributes as Attributes
|
||||
import Html.Styled.Events as Events exposing (onClick)
|
||||
import Html.Styled.Keyed
|
||||
import Json.Decode as Decode
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Html.Attributes.V2 as AttributesExtra
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias StyleOptions =
|
||||
{ entryStyles : List Style
|
||||
, entryExpandedStyles : List Style
|
||||
, entryClosedStyles : List Style
|
||||
, headerStyles : List Style
|
||||
, headerExpandedStyles : List Style
|
||||
, headerClosedStyles : List Style
|
||||
, contentStyles : List Style
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
styleAccordion : StyleOptions -> Html msg
|
||||
styleAccordion styleOptions =
|
||||
Css.Global.global
|
||||
[ Css.Global.class accordionHeaderClass
|
||||
[ margin zero, padding zero ]
|
||||
, Css.Global.class accordionEntryHeaderClass
|
||||
([ displayFlex
|
||||
, alignItems center
|
||||
, boxSizing borderBox
|
||||
, minWidth (pct 100)
|
||||
|
||||
-- button resets
|
||||
, Css.Global.withAttribute "aria-disabled=false" [ cursor pointer ]
|
||||
, Css.backgroundColor Css.unset
|
||||
, borderWidth Css.zero
|
||||
, margin zero
|
||||
|
||||
-- fonts & text
|
||||
, textAlign left
|
||||
, Fonts.baseFont
|
||||
, fontSize (px 16)
|
||||
, fontWeight (int 600)
|
||||
, lineHeight (num 1.2)
|
||||
, Css.Global.withClass accordionEntryHeaderExpandedClass
|
||||
styleOptions.headerExpandedStyles
|
||||
, Css.Global.withClass accordionEntryHeaderCollapsedClass
|
||||
styleOptions.headerClosedStyles
|
||||
]
|
||||
++ styleOptions.headerStyles
|
||||
)
|
||||
, Css.Global.class accordionEntryClass
|
||||
([ marginBottom (px 10)
|
||||
, Css.Global.withClass accordionEntryExpandedClass
|
||||
styleOptions.entryExpandedStyles
|
||||
, Css.Global.withClass accordionEntryCollapsedClass
|
||||
styleOptions.entryClosedStyles
|
||||
]
|
||||
++ styleOptions.entryStyles
|
||||
)
|
||||
, Css.Global.class accordionEntryPanelClass
|
||||
styleOptions.contentStyles
|
||||
]
|
||||
|
||||
|
||||
accordionClass : String
|
||||
accordionClass =
|
||||
"accordion-v3"
|
||||
|
||||
|
||||
accordionHeaderClass : String
|
||||
accordionHeaderClass =
|
||||
"accordion-v3-header"
|
||||
|
||||
|
||||
accordionEntryHeaderClass : String
|
||||
accordionEntryHeaderClass =
|
||||
"accordion-v3-entry-header"
|
||||
|
||||
|
||||
accordionEntryHeaderExpandedClass : String
|
||||
accordionEntryHeaderExpandedClass =
|
||||
"accordion-v3-entry-header-expanded"
|
||||
|
||||
|
||||
accordionEntryHeaderCollapsedClass : String
|
||||
accordionEntryHeaderCollapsedClass =
|
||||
"accordion-v3-entry-header-collapsed"
|
||||
|
||||
|
||||
accordionEntryClass : String
|
||||
accordionEntryClass =
|
||||
"accordion-v3-entry"
|
||||
|
||||
|
||||
accordionEntryExpandedClass : String
|
||||
accordionEntryExpandedClass =
|
||||
"accordion-v3-entry-state-expanded"
|
||||
|
||||
|
||||
accordionEntryCollapsedClass : String
|
||||
accordionEntryCollapsedClass =
|
||||
"accordion-v3-entry-state-collapsed"
|
||||
|
||||
|
||||
accordionEntryPanelClass : String
|
||||
accordionEntryPanelClass =
|
||||
"accordion-v3-entry-panel"
|
||||
|
||||
|
||||
{-| Corresponds to h1, h2, h3 etc.
|
||||
Choose the correct header level given your page context.
|
||||
-}
|
||||
type HeaderLevel
|
||||
= H1
|
||||
| H2
|
||||
| H3
|
||||
| H4
|
||||
| H5
|
||||
| H6
|
||||
|
||||
|
||||
header : HeaderLevel -> Html msg -> Html msg
|
||||
header headerLevel content =
|
||||
case headerLevel of
|
||||
H1 ->
|
||||
Accessibility.Styled.h1 [ Attributes.class accordionHeaderClass ] [ content ]
|
||||
|
||||
H2 ->
|
||||
Accessibility.Styled.h2 [ Attributes.class accordionHeaderClass ] [ content ]
|
||||
|
||||
H3 ->
|
||||
Accessibility.Styled.h3 [ Attributes.class accordionHeaderClass ] [ content ]
|
||||
|
||||
H4 ->
|
||||
Accessibility.Styled.h4 [ Attributes.class accordionHeaderClass ] [ content ]
|
||||
|
||||
H5 ->
|
||||
Accessibility.Styled.h5 [ Attributes.class accordionHeaderClass ] [ content ]
|
||||
|
||||
H6 ->
|
||||
Accessibility.Styled.h6 [ Attributes.class accordionHeaderClass ] [ content ]
|
||||
|
||||
|
||||
{-| -}
|
||||
type AccordionEntry msg
|
||||
= AccordionEntry (Entry msg) (List (AccordionEntry msg))
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Entry msg =
|
||||
{ caret : Bool -> Html msg
|
||||
, content : () -> Html msg
|
||||
, entryClass : String
|
||||
, headerContent : Html msg
|
||||
, headerId : String
|
||||
, headerLevel : HeaderLevel
|
||||
, isExpanded : Bool
|
||||
, toggle : Maybe (Bool -> msg)
|
||||
}
|
||||
|
||||
|
||||
getHeaderId : AccordionEntry msg -> String
|
||||
getHeaderId entry =
|
||||
case entry of
|
||||
AccordionEntry { headerId } _ ->
|
||||
headerId
|
||||
|
||||
|
||||
{-| -}
|
||||
view :
|
||||
{ entries : List (AccordionEntry msg)
|
||||
, focus : String -> msg
|
||||
}
|
||||
-> Html msg
|
||||
view { entries, focus } =
|
||||
view_
|
||||
{ entries = entries
|
||||
, focus = focus
|
||||
, leftId = Nothing
|
||||
}
|
||||
|
||||
|
||||
view_ :
|
||||
{ entries : List (AccordionEntry msg)
|
||||
, focus : String -> msg
|
||||
, leftId : Maybe String
|
||||
}
|
||||
-> Html msg
|
||||
view_ { entries, focus, leftId } =
|
||||
let
|
||||
headerIds : List String
|
||||
headerIds =
|
||||
List.map getHeaderId entries
|
||||
|
||||
arrowUpIds : List (Maybe String)
|
||||
arrowUpIds =
|
||||
lastHeaderId :: List.map Just headerIds
|
||||
|
||||
firstHeaderId : Maybe String
|
||||
firstHeaderId =
|
||||
List.head headerIds
|
||||
|
||||
lastHeaderId : Maybe String
|
||||
lastHeaderId =
|
||||
List.head (List.reverse headerIds)
|
||||
in
|
||||
div
|
||||
[ Attributes.class accordionClass
|
||||
, Attributes.attribute "aria-live" "polite"
|
||||
]
|
||||
[ Html.Styled.Keyed.node "div"
|
||||
[]
|
||||
(entries
|
||||
|> List.map2 (\id nextEntry -> ( id, nextEntry )) arrowUpIds
|
||||
|> List.foldr
|
||||
(\( previousId, AccordionEntry entry_ children ) ( nextId, acc ) ->
|
||||
let
|
||||
node =
|
||||
( "keyed-section__" ++ entry_.headerId
|
||||
, viewEntry focus
|
||||
{ up = previousId
|
||||
, down = nextId
|
||||
, right = Maybe.map getHeaderId (List.head children)
|
||||
, left = leftId
|
||||
}
|
||||
entry_
|
||||
children
|
||||
)
|
||||
in
|
||||
( Just entry_.headerId
|
||||
, node :: acc
|
||||
)
|
||||
)
|
||||
( firstHeaderId, [] )
|
||||
|> Tuple.second
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
viewEntry :
|
||||
(String -> msg)
|
||||
->
|
||||
{ up : Maybe String
|
||||
, down : Maybe String
|
||||
, right : Maybe String
|
||||
, left : Maybe String
|
||||
}
|
||||
-> Entry msg
|
||||
-> List (AccordionEntry msg)
|
||||
-> Html msg
|
||||
viewEntry focus arrows ({ headerId, headerLevel, caret, headerContent, entryClass, content, isExpanded } as config) children =
|
||||
let
|
||||
panelId =
|
||||
"accordion-panel__" ++ headerId
|
||||
in
|
||||
div
|
||||
[ Attributes.classList
|
||||
[ ( accordionEntryClass, True )
|
||||
, ( entryClass, True )
|
||||
, ( accordionEntryExpandedClass, isExpanded )
|
||||
, ( accordionEntryCollapsedClass, not isExpanded )
|
||||
]
|
||||
]
|
||||
[ header headerLevel <|
|
||||
button
|
||||
[ Attributes.id headerId
|
||||
, Attributes.classList
|
||||
[ ( accordionEntryHeaderClass, True )
|
||||
, ( entryClass, True )
|
||||
, ( accordionEntryHeaderExpandedClass, isExpanded )
|
||||
, ( accordionEntryHeaderCollapsedClass, not isExpanded )
|
||||
]
|
||||
, Widget.disabled (config.toggle == Nothing)
|
||||
, Widget.expanded isExpanded
|
||||
, Aria.controls panelId
|
||||
, config.toggle
|
||||
|> Maybe.map (\toggle -> onClick (toggle (not isExpanded)))
|
||||
|> Maybe.withDefault AttributesExtra.none
|
||||
, Events.custom "keydown"
|
||||
([ Maybe.map (\id -> Key.up (focus id)) arrows.up
|
||||
, Maybe.map (\id -> Key.down (focus id)) arrows.down
|
||||
, Maybe.map (\id -> Key.right (focus id)) arrows.right
|
||||
, Maybe.map (\id -> Key.left (focus id)) arrows.left
|
||||
]
|
||||
|> List.filterMap identity
|
||||
|> Decode.oneOf
|
||||
|> Decode.map
|
||||
(\event ->
|
||||
{ message = event
|
||||
, stopPropagation = False
|
||||
, preventDefault = True
|
||||
}
|
||||
)
|
||||
)
|
||||
]
|
||||
[ caret isExpanded
|
||||
, headerContent
|
||||
]
|
||||
, section
|
||||
[ Attributes.id panelId
|
||||
, Aria.labelledBy headerId
|
||||
, Attributes.classList
|
||||
[ ( accordionEntryPanelClass, True )
|
||||
, ( entryClass, True )
|
||||
]
|
||||
, Attributes.hidden (not isExpanded)
|
||||
]
|
||||
(if isExpanded then
|
||||
[ content ()
|
||||
, view_ { focus = focus, entries = children, leftId = Just headerId }
|
||||
]
|
||||
|
||||
else
|
||||
[]
|
||||
)
|
||||
]
|
@ -10,20 +10,25 @@ module Examples.Accordion exposing
|
||||
|
||||
-}
|
||||
|
||||
import Accessibility.Styled as Html exposing (Html)
|
||||
import AtomicDesignType exposing (AtomicDesignType(..))
|
||||
import Browser.Dom as Dom
|
||||
import Category exposing (Category(..))
|
||||
import Css exposing (..)
|
||||
import Dict exposing (Dict)
|
||||
import Css.Global
|
||||
import Example exposing (Example)
|
||||
import Html.Styled as Html exposing (Html)
|
||||
import Html.Styled.Attributes exposing (css)
|
||||
import Html.Styled.Attributes as Attributes exposing (css, src)
|
||||
import KeyboardSupport exposing (Direction(..), Key(..))
|
||||
import Nri.Ui.Accordion.V2 as Accordion
|
||||
import Nri.Ui.Accordion.V3 as Accordion exposing (AccordionEntry(..))
|
||||
import Nri.Ui.Colors.Extra as ColorsExtra
|
||||
import Nri.Ui.Colors.V1 as Colors
|
||||
import Nri.Ui.DisclosureIndicator.V2 as DisclosureIndicator
|
||||
import Nri.Ui.Fonts.V1 as Fonts
|
||||
import Nri.Ui.Heading.V2 as Heading
|
||||
import Nri.Ui.Svg.V1 as Svg
|
||||
import Nri.Ui.Text.V4 as Text
|
||||
import Nri.Ui.UiIcon.V1 as UiIcon
|
||||
import Set exposing (Set)
|
||||
import Task
|
||||
|
||||
|
||||
@ -31,7 +36,7 @@ import Task
|
||||
example : Example State Msg
|
||||
example =
|
||||
{ name = "Accordion"
|
||||
, version = 2
|
||||
, version = 3
|
||||
, state = init
|
||||
, update = update
|
||||
, subscriptions = \_ -> Sub.none
|
||||
@ -45,6 +50,12 @@ example =
|
||||
, { keys = [ Arrow KeyboardSupport.Down ]
|
||||
, result = "Moves the focus to the next accordion header button (wraps focus to the first header button)"
|
||||
}
|
||||
, { 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -52,89 +63,176 @@ example =
|
||||
{-| -}
|
||||
view : State -> List (Html Msg)
|
||||
view model =
|
||||
[ Heading.h3 [] [ Html.text "Accordion.view with default styles" ]
|
||||
let
|
||||
defaultCaret =
|
||||
DisclosureIndicator.large [ Css.marginRight (Css.px 8) ]
|
||||
in
|
||||
[ Heading.h3 [] [ Html.text "Accordion.view" ]
|
||||
, Accordion.view
|
||||
{ entries =
|
||||
[ { id = 1, title = "Entry 1", content = "Content for the first accordion" }
|
||||
, { id = 2, title = "Entry 2", content = "Content for the second accordion" }
|
||||
, { id = 3, title = "Super long entry that is very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long", content = "Content for the third accordion" }
|
||||
]
|
||||
|> List.map
|
||||
(\entry ->
|
||||
{ headerId = "accordion-entry__" ++ String.fromInt entry.id
|
||||
, entry = entry
|
||||
, isExpanded = Dict.get entry.id model |> Maybe.withDefault False
|
||||
}
|
||||
)
|
||||
, headerLevel = Accordion.H4
|
||||
, viewHeader = .title >> Html.text
|
||||
, viewContent = \{ content } -> Text.smallBody [ Html.text content ]
|
||||
, customStyles = Nothing
|
||||
, toggle = \entry toExpand -> Toggle entry.id toExpand
|
||||
, focus = Focus
|
||||
, caret = Accordion.DefaultCaret
|
||||
}
|
||||
, Heading.h3 [] [ Html.text "Accordion.view with custom styles from peer reviews" ]
|
||||
, Accordion.view
|
||||
{ entries =
|
||||
[ { id = 4
|
||||
, title = "Firstname Lastname"
|
||||
, content =
|
||||
Html.div
|
||||
[ css [ Fonts.baseFont, fontSize (px 13) ]
|
||||
]
|
||||
[ Html.text "has not started writing" ]
|
||||
}
|
||||
, { id = 5
|
||||
, title = "LongFirstnameAnd EvenLongerLastname"
|
||||
, content =
|
||||
Html.div
|
||||
[ css [ Fonts.baseFont, fontSize (px 13) ] ]
|
||||
[ Html.text "has started writing" ]
|
||||
}
|
||||
]
|
||||
|> List.map
|
||||
(\entry ->
|
||||
{ headerId = "accordion-entry__" ++ String.fromInt entry.id
|
||||
, entry = entry
|
||||
, isExpanded = Dict.get entry.id model |> Maybe.withDefault False
|
||||
}
|
||||
)
|
||||
, headerLevel = Accordion.H4
|
||||
, viewHeader = .title >> Html.text
|
||||
, viewContent = .content
|
||||
, customStyles =
|
||||
Just
|
||||
(\_ ->
|
||||
{ entryStyles =
|
||||
[ borderTop3 (px 1) solid Colors.gray75
|
||||
, marginBottom zero
|
||||
, width (px 284)
|
||||
]
|
||||
, entryExpandedStyles = []
|
||||
, entryClosedStyles = []
|
||||
, headerStyles =
|
||||
[ height (px 46)
|
||||
, paddingLeft (px 8)
|
||||
, paddingRight (px 8)
|
||||
, Css.alignItems Css.center
|
||||
]
|
||||
, headerExpandedStyles =
|
||||
[ backgroundColor Colors.gray96
|
||||
, borderRadius zero
|
||||
]
|
||||
, headerClosedStyles = [ backgroundColor transparent ]
|
||||
, contentStyles =
|
||||
[ backgroundColor Colors.gray96
|
||||
, paddingLeft (px 8)
|
||||
, paddingRight (px 8)
|
||||
, paddingBottom (px 8)
|
||||
]
|
||||
[ AccordionEntry
|
||||
{ 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! 🍏"
|
||||
, entryClass = "accordion-example"
|
||||
, headerContent = Html.text "Apples"
|
||||
, headerId = "accordion-entry__1"
|
||||
, headerLevel = Accordion.H4
|
||||
, isExpanded = Set.member 1 model
|
||||
, 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" ]
|
||||
]
|
||||
, entryClass = "accordion-example-child"
|
||||
, headerContent = Html.text "Gala"
|
||||
, headerId = "accordion-entry__11"
|
||||
, headerLevel = Accordion.H5
|
||||
, isExpanded = Set.member 11 model
|
||||
, toggle = Just (Toggle 11)
|
||||
}
|
||||
)
|
||||
, toggle = \entry toExpand -> Toggle entry.id toExpand
|
||||
[]
|
||||
, 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" ]
|
||||
]
|
||||
, entryClass = "accordion-example-child"
|
||||
, headerContent = Html.text "Granny Smith"
|
||||
, headerId = "accordion-entry__12"
|
||||
, headerLevel = Accordion.H5
|
||||
, isExpanded = Set.member 12 model
|
||||
, 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" ]
|
||||
]
|
||||
, entryClass = "accordion-example-child"
|
||||
, headerContent = Html.text "Fuji"
|
||||
, headerId = "accordion-entry__13"
|
||||
, headerLevel = Accordion.H5
|
||||
, isExpanded = Set.member 13 model
|
||||
, toggle = Just (Toggle 13)
|
||||
}
|
||||
[]
|
||||
]
|
||||
, AccordionEntry
|
||||
{ caret = defaultCaret
|
||||
, content = \_ -> Html.text "🍊 I don't know anything about oranges! Except: YUM! 🍊"
|
||||
, entryClass = "accordion-example"
|
||||
, headerContent = Html.text "Oranges"
|
||||
, headerId = "accordion-entry__2"
|
||||
, headerLevel = Accordion.H4
|
||||
, isExpanded = Set.member 2 model
|
||||
, toggle = Just (Toggle 2)
|
||||
}
|
||||
[]
|
||||
, AccordionEntry
|
||||
{ caret = defaultCaret
|
||||
, content = \_ -> Html.text "There are many types of berries and all of them are delicious (or poisonous (or both)). Blackberries and mulberries are especially drool-worthy."
|
||||
, entryClass = "accordion-example"
|
||||
, headerContent = Html.text "Berries"
|
||||
, headerId = "accordion-entry__4"
|
||||
, headerLevel = Accordion.H5
|
||||
, isExpanded = Set.member 4 model
|
||||
, toggle = Just (Toggle 4)
|
||||
}
|
||||
[]
|
||||
, 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
|
||||
, content =
|
||||
\_ ->
|
||||
Html.div
|
||||
[ css
|
||||
[ Css.backgroundColor Colors.gray92
|
||||
, Css.minHeight (Css.vh 100)
|
||||
, Css.padding (Css.px 20)
|
||||
]
|
||||
]
|
||||
[ 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" ] ]
|
||||
]
|
||||
, entryClass = "fixed-positioning-accordion-example"
|
||||
, headerContent = Html.text "Advanced Example: Expand & Scroll!"
|
||||
, headerId = "accordion-entry__6"
|
||||
, headerLevel = Accordion.H4
|
||||
, isExpanded = Set.member 6 model
|
||||
, toggle = Just (Toggle 6)
|
||||
}
|
||||
[]
|
||||
]
|
||||
, focus = Focus
|
||||
, caret = Accordion.DefaultCaret
|
||||
}
|
||||
, Accordion.styleAccordion
|
||||
{ entryStyles =
|
||||
[ Css.Global.withClass "fixed-positioning-accordion-example"
|
||||
[ Css.marginLeft (Css.px -20)
|
||||
, Css.position Css.relative
|
||||
]
|
||||
, Css.Global.withClass "accordion-example-child"
|
||||
[ Css.marginLeft (Css.px 16) ]
|
||||
]
|
||||
, 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)
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
, 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 = []
|
||||
}
|
||||
]
|
||||
|
||||
@ -148,26 +246,26 @@ type Msg
|
||||
{-| -}
|
||||
init : State
|
||||
init =
|
||||
Dict.fromList
|
||||
[ ( 1, False )
|
||||
, ( 2, False )
|
||||
, ( 3, False )
|
||||
, ( 4, False )
|
||||
, ( 5, False )
|
||||
]
|
||||
Set.empty
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias State =
|
||||
Dict Int Bool
|
||||
Set Int
|
||||
|
||||
|
||||
{-| -}
|
||||
update : Msg -> State -> ( State, Cmd Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
Toggle id toExpanded ->
|
||||
( Dict.insert id toExpanded model, Cmd.none )
|
||||
Toggle id expand ->
|
||||
( if expand then
|
||||
Set.insert id model
|
||||
|
||||
else
|
||||
Set.remove id model
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
Focus id ->
|
||||
( model, Task.attempt Focused (Dom.focus id) )
|
||||
|
Loading…
Reference in New Issue
Block a user