Moving Examples into own folder

This commit is contained in:
Lucas Payr 2020-05-12 21:15:44 +02:00
parent 732a18720d
commit bc2c86257f
16 changed files with 1418 additions and 610 deletions

View File

@ -17,6 +17,7 @@
"feathericons/elm-feather": "1.4.0",
"jasonliang512/elm-heroicons": "1.0.2",
"mdgriffith/elm-ui": "1.1.5",
"ryannhg/elm-spa": "4.1.0",
"turboMaCk/queue": "1.0.2",
"wernerdegroot/listzipper": "4.0.0"
},

View File

@ -0,0 +1,187 @@
module Data.Example exposing (Model, Msg, init, subscriptions, toCardList, update, view)
import Data.Style exposing (Style)
import Element exposing (Element)
import Example.Button as Button
import Example.ExpansionPanel as ExpansionPanel
import Example.MultiSelect as MultiSelect
import Example.Select as Select
import Example.Tab as Tab
import Framework.Grid as Grid
import View.Test as Test
type Msg
= Button Button.Msg
| Select Select.Msg
| MultiSelect MultiSelect.Msg
| ExpansionPanel ExpansionPanel.Msg
| Tab Tab.Msg
type alias Model =
{ button : Button.Model
, select : Select.Model
, multiSelect : MultiSelect.Model
, expansionPanel : ExpansionPanel.Model
, tab : Tab.Model
}
init : ( Model, Cmd Msg )
init =
let
( buttonModel, buttonMsg ) =
Button.init
( selectModel, selectMsg ) =
Select.init
( multiSelectModel, multiSelectMsg ) =
MultiSelect.init
( expansionPanelModel, expansionPanelMsg ) =
ExpansionPanel.init
( tabModel, tabMsg ) =
Tab.init
in
( { button = buttonModel
, select = selectModel
, multiSelect = multiSelectModel
, expansionPanel = expansionPanelModel
, tab = tabModel
}
, [ Cmd.map Button buttonMsg
, Cmd.map Select selectMsg
, Cmd.map MultiSelect multiSelectMsg
, Cmd.map Tab tabMsg
]
|> Cmd.batch
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Button buttonMsg ->
Button.update buttonMsg model.button
|> Tuple.mapBoth
(\a -> { model | button = a })
(Cmd.map Button)
Select selectMsg ->
Select.update selectMsg model.select
|> Tuple.mapBoth
(\a -> { model | select = a })
(Cmd.map Select)
MultiSelect multiSelectMsg ->
MultiSelect.update multiSelectMsg model.multiSelect
|> Tuple.mapBoth
(\a -> { model | multiSelect = a })
(Cmd.map MultiSelect)
ExpansionPanel expansionPanelMsg ->
ExpansionPanel.update expansionPanelMsg model.expansionPanel
|> Tuple.mapBoth
(\a -> { model | expansionPanel = a })
(Cmd.map ExpansionPanel)
Tab tabMsg ->
Tab.update tabMsg model.tab
|> Tuple.mapBoth
(\a -> { model | tab = a })
(Cmd.map Tab)
subscriptions : Model -> Sub Msg
subscriptions model =
[ Button.subscriptions model.button |> Sub.map Button
, Select.subscriptions model.select |> Sub.map Select
, MultiSelect.subscriptions model.multiSelect |> Sub.map MultiSelect
, ExpansionPanel.subscriptions model.expansionPanel |> Sub.map ExpansionPanel
, Tab.subscriptions model.tab |> Sub.map Tab
]
|> Sub.batch
view :
(Msg -> msg)
-> Style msg
-> Model
->
{ button : Element msg
, select : Element msg
, multiSelect : Element msg
, expansionPanel : Element msg
, tab : Element msg
}
view msgMapper style model =
{ button =
Button.view
(Button >> msgMapper)
style
model.button
, select =
Select.view
(Select >> msgMapper)
style
model.select
, multiSelect =
MultiSelect.view
(MultiSelect >> msgMapper)
style
model.multiSelect
, expansionPanel =
ExpansionPanel.view
(ExpansionPanel >> msgMapper)
style
model.expansionPanel
, tab =
Tab.view
(Tab >> msgMapper)
style
model.tab
}
toCardList :
{ idle : msg
, msgMapper : Msg -> msg
, style : Style msg
, model : Model
}
-> List ( String, Element msg, Element msg )
toCardList { idle, msgMapper, style, model } =
[ { title = "Icon Button"
, example = .button
, test = Test.iconButton
}
, { title = "Select"
, example = .select
, test = Test.select
}
, { title = "Multi Select"
, example = .multiSelect
, test = Test.multiSelect
}
, { title = "Expansion Panel"
, example = .expansionPanel
, test = Test.expansionPanel
}
, { title = "Tab"
, example = .tab
, test = Test.tab
}
]
|> List.map
(\{ title, example, test } ->
( title
, model
|> view msgMapper style
|> example
, test idle style
|> Element.column Grid.simple
)
)

View File

@ -29,7 +29,7 @@ textButton =
simpleButton : ButtonStyle msg
simpleButton =
{ container = Button.simple ++ Color.primary
{ container = Button.simple ++ Color.success
, labelRow = Grid.simple
, ifDisabled = Color.disabled
, ifActive = Color.primary
@ -211,7 +211,7 @@ row =
cardColumn : ColumnStyle msg
cardColumn =
{ containerColumn = Grid.compact
{ containerColumn = Grid.compact ++ [Element.height <| Element.fill]
, element = Card.large ++ [Element.height <| Element.fill]
, ifFirst = Group.top
, ifLast = Group.bottom

View File

@ -0,0 +1,83 @@
module Example.Button exposing (Model, Msg, init, subscriptions, update, view)
import Element exposing (Element)
import FeatherIcons
import Widget
import Widget.Style exposing (ButtonStyle)
type alias Style style msg =
{ style
| primaryButton : ButtonStyle msg
, button : ButtonStyle msg
}
type alias Model =
{ isButtonEnabled : Bool
}
type Msg
= ChangedButtonStatus Bool
init : ( Model, Cmd Msg )
init =
( { isButtonEnabled = True
}
, Cmd.none
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ChangedButtonStatus bool ->
( { model | isButtonEnabled = bool }
, Cmd.none
)
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
view : (Msg -> msg) -> Style style msg -> Model -> Element msg
view msgMapper style model =
[ Widget.button style.primaryButton
{ text = "disable me"
, icon =
FeatherIcons.slash
|> FeatherIcons.withSize 16
|> FeatherIcons.toHtml []
|> Element.html
|> Element.el []
, onPress =
if model.isButtonEnabled then
ChangedButtonStatus False
|> msgMapper
|> Just
else
Nothing
}
, Widget.iconButton style.button
{ text = "reset"
, icon =
FeatherIcons.repeat
|> FeatherIcons.withSize 16
|> FeatherIcons.toHtml []
|> Element.html
|> Element.el []
, onPress =
ChangedButtonStatus True
|> msgMapper
|> Just
}
]
|> Element.row
[ Element.spaceEvenly
, Element.width <| Element.fill
]

View File

@ -0,0 +1,53 @@
module Example.ExpansionPanel exposing (Model, Msg, init, subscriptions, update, view)
import Element exposing (Element)
import Widget
import Widget.Style exposing (ExpansionPanelStyle)
type alias Style style msg =
{ style
| expansionPanel : ExpansionPanelStyle msg
}
type alias Model =
{ isExpanded : Bool }
type Msg
= ToggleCollapsable Bool
init : ( Model, Cmd Msg )
init =
( { isExpanded = False }
, Cmd.none
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ToggleCollapsable bool ->
( { model
| isExpanded = bool
}
, Cmd.none
)
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
view : (Msg -> msg) -> Style style msg -> Model -> Element msg
view msgMapper style model =
{ onToggle = ToggleCollapsable >> msgMapper
, isExpanded = model.isExpanded
, icon = Element.none
, text = "Title"
, content = Element.text <| "Hello World"
}
|> Widget.expansionPanel style.expansionPanel

View File

@ -0,0 +1,72 @@
module Example.MultiSelect exposing (Model, Msg, init, subscriptions, update, view)
import Element exposing (Element)
import Set exposing (Set)
import Widget
import Widget.Style exposing (ButtonStyle, RowStyle)
type alias Style style msg =
{ style
| row : RowStyle msg
, button : ButtonStyle msg
}
type alias Model =
{ selected : Set Int
}
type Msg
= ChangedSelected Int
init : ( Model, Cmd Msg )
init =
( { selected = Set.empty }
, Cmd.none
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ChangedSelected int ->
( { model
| selected =
model.selected
|> (if model.selected |> Set.member int then
Set.remove int
else
Set.insert int
)
}
, Cmd.none
)
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
view : (Msg -> msg) -> Style style msg -> Model -> Element msg
view msgMapper style model =
{ selected = model.selected
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = ChangedSelected >> msgMapper >> Just
}
|> Widget.multiSelect
|> Widget.buttonRow
{ list = style.row
, button = style.button
}

View File

@ -0,0 +1,64 @@
module Example.Select exposing (Model, Msg, init, subscriptions, update, view)
import Element exposing (Attribute, Element)
import FeatherIcons
import Widget
import Widget.Style exposing (ButtonStyle, RowStyle)
type alias Style style msg =
{ style
| row : RowStyle msg
, button : ButtonStyle msg
}
type alias Model =
{ selected : Maybe Int }
type Msg
= ChangedSelected Int
init : ( Model, Cmd Msg )
init =
( { selected = Nothing }
, Cmd.none
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ChangedSelected int ->
( { model
| selected = Just int
}
, Cmd.none
)
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
view : (Msg -> msg) -> Style style msg -> Model -> Element msg
view msgMapper style model =
{ selected = model.selected
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = ChangedSelected >> msgMapper >> Just
}
|> Widget.select
|> Widget.buttonRow
{ list = style.row
, button = style.button
}

View File

@ -0,0 +1,76 @@
module Example.Tab exposing (Model, Msg, init, subscriptions, update, view)
import Element exposing (Element)
import Widget
import Widget.Style exposing (TabStyle)
type alias Style style msg =
{ style
| tab : TabStyle msg
}
type alias Model =
{ selected : Maybe Int
}
type Msg
= ChangedTab Int
init : ( Model, Cmd Msg )
init =
( { selected = Nothing
}
, Cmd.none
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ChangedTab int ->
( { model | selected = Just int }
, Cmd.none
)
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
view : (Msg -> msg) -> Style style msg -> Model -> Element msg
view msgMapper style model =
Widget.tab style.tab
{ tabs =
{ selected = model.selected
, options =
[ 1, 2, 3 ]
|> List.map
(\int ->
{ text = "Tab " ++ (int |> String.fromInt)
, icon = Element.none
}
)
, onSelect = ChangedTab >> msgMapper >> Just
}
, content =
\selected ->
(case selected of
Just 0 ->
"This is Tab 1"
Just 1 ->
"This is the second tab"
Just 2 ->
"The thrid and last tab"
_ ->
"Please select a tab"
)
|> Element.text
}

View File

@ -1,4 +1,4 @@
module Example exposing (main)
module Main exposing (main)
import Array
import Browser
@ -6,6 +6,8 @@ import Browser.Dom as Dom exposing (Viewport)
import Browser.Events as Events
import Browser.Navigation as Navigation
import Data.Section as Section exposing (Section(..))
import Data.Style as Style exposing (Style)
import Data.Theme as Theme exposing (Theme(..))
import Element exposing (Attribute, DeviceClass(..), Element)
import Element.Border as Border
import Element.Font as Font
@ -32,15 +34,10 @@ import Widget
import Widget.ScrollingNav as ScrollingNav
import Widget.Snackbar as Snackbar
import Widget.Style exposing (ButtonStyle)
import Data.Style as Style exposing (Style)
import Data.Theme as Theme exposing (Theme(..))
type alias LoadedModel =
{ stateless : Stateless.Model
, reusable : Reusable.Model
, scrollingNav : ScrollingNav.Model Section
, layout : Layout LoadedMsg
, displayDialog : Bool
@ -61,7 +58,6 @@ type Model
type LoadedMsg
= StatelessSpecific Stateless.Msg
| ReusableSpecific Reusable.Msg
| UpdateScrollingNav (ScrollingNav.Model Section -> ScrollingNav.Model Section)
| TimePassed Int
| AddSnackbar ( String, Bool )
@ -97,9 +93,11 @@ initialModel { viewport } =
Err _ ->
Idle
}
( stateless, statelessCmd ) =
Stateless.init
in
( { stateless = Stateless.init
, reusable = Reusable.init
( { stateless = stateless
, scrollingNav = scrollingNav
, layout = Layout.init
, displayDialog = False
@ -114,7 +112,10 @@ initialModel { viewport } =
}
, theme = ElmUiFramework
}
, cmd
, [ cmd
, statelessCmd |> Cmd.map StatelessSpecific
]
|> Cmd.batch
)
@ -127,7 +128,6 @@ init () =
view : Model -> Html Msg
view model =
case model of
Loading ->
Element.none |> Framework.responsiveLayout []
@ -173,8 +173,6 @@ view model =
ReusableViews ->
Reusable.view m.theme
{ addSnackbar = AddSnackbar
, model = m.reusable
, msgMapper = ReusableSpecific
}
StatelessViews ->
@ -198,7 +196,7 @@ view model =
, items
|> (if m.search.current /= "" then
List.filter
(\(a,_,_) ->
(\( a, _, _ ) ->
a
|> String.toLower
|> String.contains (m.search.current |> String.toLower)
@ -209,10 +207,13 @@ view model =
)
|> List.map
(\( name, elem, more ) ->
[ Element.text name
|> Element.el Heading.h3
[ [ Element.text name
|> Element.el (Heading.h3 ++ [ Element.height <| Element.shrink ])
, elem
]
|> Element.column Grid.simple
, more
|> Element.el [ Element.height <| Element.fill ]
]
|> Widget.column style.cardColumn
)
@ -226,7 +227,7 @@ view model =
|> Element.column Framework.container
]
|> Element.column Grid.compact
, style =style
, style = style
, layout = m.layout
, window = m.window
, menu =
@ -247,15 +248,19 @@ view model =
, text = "Github"
, icon = Icons.github |> Element.html |> Element.el []
}
, { onPress = if m.theme /= ElmUiFramework then
, { onPress =
if m.theme /= ElmUiFramework then
Just <| SetTheme <| ElmUiFramework
else
Nothing
, text = "Elm-Ui-Framework Theme"
, icon = Icons.penTool |> Element.html |> Element.el []
}
, { onPress = if m.theme /= Template then
, { onPress =
if m.theme /= Template then
Just <| SetTheme <| Template
else
Nothing
, text = "Template Theme"
@ -291,17 +296,6 @@ view model =
updateLoaded : LoadedMsg -> LoadedModel -> ( LoadedModel, Cmd LoadedMsg )
updateLoaded msg model =
case msg of
ReusableSpecific m ->
( model.reusable
|> Reusable.update m
|> (\reusable ->
{ model
| reusable = reusable
}
)
, Cmd.none
)
StatelessSpecific m ->
model.stateless
|> Stateless.update m

View File

@ -1,4 +1,4 @@
module Reusable exposing (Model, Msg, init, update, view)
module Reusable exposing ( view)
import Browser
import Element exposing (Color, Element)
@ -22,17 +22,9 @@ import Time
import Widget
import Widget.ScrollingNav as ScrollingNav
import Widget.Snackbar as Snackbar
import Widget.SortTable as SortTable
import Data.Style exposing (Style)
import Data.Theme as Theme exposing (Theme)
type alias Model =
SortTable.Model
type Msg
= SortBy { title : String, asc : Bool }
type alias Item =
{ name : String
@ -41,17 +33,6 @@ type alias Item =
}
update : Msg -> Model -> Model
update msg model =
case msg of
SortBy m ->
m
init : Model
init =
SortTable.sortBy { title = "Name", asc = True }
snackbar : Style msg -> (( String, Bool ) -> msg) -> ( String, Element msg,Element msg )
snackbar style addSnackbar =
@ -82,78 +63,7 @@ snackbar style addSnackbar =
)
sortTable : Style Msg -> SortTable.Model -> ( String, Element Msg,Element Msg )
sortTable style model =
( "Sort Table"
, SortTable.view
{ content =
[ { id = 1, name = "Antonio", rating = 2.456 }
, { id = 2, name = "Ana", rating = 1.34 }
, { id = 3, name = "Alfred", rating = 4.22 }
, { id = 4, name = "Thomas", rating = 3 }
]
, columns =
[ SortTable.intColumn
{ title = "Id"
, value = .id
, toString = \int -> "#" ++ String.fromInt int
}
, SortTable.stringColumn
{ title = "Name"
, value = .name
, toString = identity
}
, SortTable.floatColumn
{ title = "rating"
, value = .rating
, toString = String.fromFloat
}
]
, model = model
}
|> (\{ data, columns } ->
{ data = data
, columns =
columns
|> List.map
(\config ->
{ header =
Input.button [ Font.bold ]
{ onPress =
{ title = config.header
, asc =
if config.header == model.title then
not model.asc
else
True
}
|> SortBy
|> Just
, label =
if config.header == model.title then
[ config.header |> Element.text
, Element.html <|
if model.asc then
Heroicons.cheveronUp [ Attributes.width 16 ]
else
Heroicons.cheveronDown [ Attributes.width 16 ]
]
|> Element.row (Grid.simple ++ [ Font.bold ])
else
config.header |> Element.text
}
, view = config.view >> Element.text
, width = Element.fill
}
)
}
)
|> Element.table Grid.simple
, Element.none
)
scrollingNavCard : Style msg -> ( String, Element msg, Element msg )
@ -169,15 +79,13 @@ scrollingNavCard style =
view :
Theme ->
{ addSnackbar : ( String, Bool ) -> msg
, msgMapper : Msg -> msg
, model : Model
}
->
{ title : String
, description : String
, items : List ( String, Element msg,Element msg )
}
view theme { addSnackbar, msgMapper, model } =
view theme { addSnackbar } =
let
style = Theme.toStyle theme
in
@ -185,8 +93,6 @@ view theme { addSnackbar, msgMapper, model } =
, description = "Reusable views have an internal state but no update function. You will need to do some wiring, but nothing complicated."
, items =
[ snackbar style addSnackbar
, sortTable style model |> \(a,b,c) ->
(a,b |> Element.map msgMapper,c |> Element.map msgMapper)
, scrollingNavCard style
]
}

View File

@ -1,96 +1,66 @@
module Stateless exposing (Model, Msg, init, update, view)
import Array exposing (Array)
import Array
import Data.Example as Example
import Data.Style exposing (Style)
import Data.Theme as Theme exposing (Theme)
import Element exposing (Element)
import Element.Background as Background
import Element.Border as Border
import Element.Font as Font
import Element.Input as Input
import Framework.Button as Button
import Framework.Card as Card
import Framework.Color as Color
import Framework.Grid as Grid
import Framework.Group as Group
import Framework.Heading as Heading
import Framework.Input as Input
import Framework.Tag as Tag
import Heroicons.Solid as Heroicons
import Html exposing (Html)
import Html.Attributes as Attributes
import Icons
import Layout exposing (Part(..))
import Set exposing (Set)
import Widget
import Widget.Style exposing (ButtonStyle)
import Data.Theme as Theme exposing (Theme)
type alias Model =
{ selected : Maybe Int
, multiSelected : Set Int
, chipTextInput : Set String
, isExpanded : Bool
{ chipTextInput : Set String
, carousel : Int
, tab : Maybe Int
, button : Bool
, textInput : String
, table : { title : String, asc : Bool }
, example : Example.Model
}
type Msg
= ChangedSelected Int
| ChangedMultiSelected Int
| ToggleCollapsable Bool
| ToggleTextInputChip String
| ChangedTab Int
= ToggleTextInputChip String
| SetCarousel Int
| ToggleButton Bool
| SetTextInput String
| ChangedSorting String
| ExampleSpecific Example.Msg
| Idle
init : Model
init : ( Model, Cmd Msg )
init =
{ selected = Nothing
, multiSelected = Set.empty
, chipTextInput = Set.empty
, isExpanded = False
let
( example, cmd ) =
Example.init
in
( { chipTextInput = Set.empty
, carousel = 0
, tab = Just 1
, button = True
, textInput = ""
, table = { title = "Name", asc = True }
, example = example
}
, cmd |> Cmd.map ExampleSpecific
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ChangedSelected int ->
( { model
| selected = Just int
}
, Cmd.none
)
ChangedMultiSelected int ->
( { model
| multiSelected =
model.multiSelected
|> (if model.multiSelected |> Set.member int then
Set.remove int
else
Set.insert int
)
}
, Cmd.none
)
ToggleCollapsable bool ->
( { model
| isExpanded = bool
}
, Cmd.none
ExampleSpecific exampleMsg ->
let
( exampleModel, exampleCmd ) =
Example.update exampleMsg model.example
in
( { model | example = exampleModel }
, exampleCmd |> Cmd.map ExampleSpecific
)
ToggleTextInputChip string ->
@ -118,127 +88,30 @@ update msg model =
, Cmd.none
)
ChangedTab int ->
( { model | tab = Just int }, Cmd.none )
ToggleButton bool ->
( { model | button = bool }, Cmd.none )
SetTextInput string ->
( { model | textInput = string }, Cmd.none )
ChangedSorting string ->
( { model
| table =
{ title = string
, asc =
if model.table.title == string then
not model.table.asc
else
True
}
}
, Cmd.none
)
Idle ->
( model, Cmd.none )
select : Style Msg -> Model -> ( String, Element Msg,Element Msg )
select style model =
let
buttonStyle =
style.button
in
( "Select"
, { selected = model.selected
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = ChangedSelected >> Just
}
|> Widget.select
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
, Element.none
)
multiSelect : Style Msg -> Model -> ( String, Element Msg, Element Msg )
multiSelect style model =
let
buttonStyle =
style.button
in
( "Multi Select"
, { selected = model.multiSelected
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = ChangedMultiSelected >> Just
}
|> Widget.multiSelect
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
, Element.none
)
expansionPanel : Style Msg -> Model -> (String,Element Msg,Element Msg)
expansionPanel style model =
( "Expansion Panel"
, { onToggle = ToggleCollapsable
, isExpanded = model.isExpanded
, icon = Element.none
, text = "Title"
, content = Element.text <| "Hello World"
}
|>Widget.expansionPanel style.expansionPanel
, Element.none
)
tab : Style Msg -> Model -> ( String, Element Msg, Element Msg )
tab style model =
( "Tab"
, Widget.tab style.tab
{ tabs =
{ selected = model.tab
, options =
[ 1, 2, 3 ]
|> List.map
(\int ->
{ text = "Tab " ++ (int |> String.fromInt)
, icon = Element.none
}
)
, onSelect = ChangedTab >> Just
}
, content =
\selected ->
(case selected of
Just 0 ->
"This is Tab 1"
Just 1 ->
"This is the second tab"
Just 2 ->
"The thrid and last tab"
_ ->
"Please select a tab"
)
|> Element.text
}
, Element.none
)
modal : Style msg -> (Maybe Part -> msg) -> Model -> ( String, Element msg,Element msg )
modal style changedSheet model =
modal : Style msg -> (Maybe Part -> msg) -> Model -> ( String, Element msg, Element msg )
modal style changedSheet _ =
( "Modal"
, [ Widget.button style.button
{ onPress = Just <| changedSheet <| Just LeftSheet
@ -252,12 +125,12 @@ modal style changedSheet model =
}
]
|> Element.column Grid.simple
,Element.none
, Element.none
)
dialog : Style msg -> msg -> Model -> ( String, Element msg, Element msg )
dialog style showDialog model =
dialog style showDialog _ =
( "Dialog"
, Widget.button style.button
{ onPress = Just showDialog
@ -330,66 +203,6 @@ carousel style model =
)
iconButton : Style Msg -> Model -> ( String, Element Msg, Element Msg )
iconButton style model =
( "Icon Button"
, [ Widget.button style.primaryButton
{ text = "disable me"
, icon = Icons.slash |> Element.html |> Element.el []
, onPress =
if model.button then
Just <| ToggleButton False
else
Nothing
}
, Widget.iconButton style.button
{ text = "reset"
, icon = Icons.repeat |> Element.html |> Element.el []
, onPress = Just <| ToggleButton True
}
]
|> Element.row Grid.simple
, Element.column Grid.simple
[ Element.row Grid.spacedEvenly
[ "Button"
|> Element.text
, Widget.button style.button
{ text = "reset"
, icon = Icons.repeat |> Element.html |> Element.el []
, onPress = Just <| ToggleButton True
}
]
, Element.row Grid.spacedEvenly
[ "Text button"
|> Element.text
, Widget.textButton style.button
{ text = "reset"
, onPress = Just <| ToggleButton True
}
]
, Element.row Grid.spacedEvenly
[ "Button"
|> Element.text
, Widget.iconButton style.button
{ text = "reset"
, icon = Icons.repeat |> Element.html |> Element.el []
, onPress = Just <| ToggleButton True
}
]
, Element.row Grid.spacedEvenly
[ "Disabled button"
|> Element.text
, Widget.button style.button
{ text = "reset"
, icon = Icons.repeat |> Element.html |> Element.el []
, onPress = Nothing
}
]
]
)
textInput : Style Msg -> Model -> ( String, Element Msg, Element Msg )
textInput style model =
( "Chip Text Input"
@ -432,12 +245,108 @@ textInput style model =
|> Element.wrappedRow [ Element.spacing 10 ]
]
|> Element.column Grid.simple
, [ Element.row Grid.spacedEvenly
[ "Nothing Selected"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { chips = []
, text = ""
, placeholder = Nothing
, label = "Label"
, onChange = always Idle
}
|> Widget.textInput style.textInput
]
, Element.row Grid.spacedEvenly
[ "Some chips"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { chips =
[ { icon = Icons.triangle |> Element.html |> Element.el []
, text = "A"
, onPress = Just Idle
}
, { icon = Icons.circle |> Element.html |> Element.el []
, text = "B"
, onPress = Just Idle
}
]
, text = ""
, placeholder = Nothing
, label = "Label"
, onChange = always Idle
}
|> Widget.textInput style.textInput
]
]
|> Element.column Grid.simple
)
list : Style Msg -> Model -> ( String, Element Msg, Element Msg )
list style _ =
( "List"
, [ Element.text <| "A"
, Element.text <| "B"
, Element.text <| "C"
]
|> Widget.column style.cardColumn
, Element.none
)
sortTable : Style Msg -> Model -> ( String, Element Msg, Element Msg )
sortTable _ model =
( "Sort Table"
, Widget.sortTable
{ containerTable = Grid.simple
, headerButton =
{ container = []
, labelRow = []
, ifDisabled = []
, ifActive = []
}
, ascIcon = Heroicons.cheveronUp [ Attributes.width 16 ] |> Element.html
, descIcon = Heroicons.cheveronDown [ Attributes.width 16 ] |> Element.html
, defaultIcon = Element.none
}
{ content =
[ { id = 1, name = "Antonio", rating = 2.456 }
, { id = 2, name = "Ana", rating = 1.34 }
, { id = 3, name = "Alfred", rating = 4.22 }
, { id = 4, name = "Thomas", rating = 3 }
]
, columns =
[ Widget.intColumn
{ title = "Id"
, value = .id
, toString = \int -> "#" ++ String.fromInt int
, width = Element.fill
}
, Widget.stringColumn
{ title = "Name"
, value = .name
, toString = identity
, width = Element.fill
}
, Widget.floatColumn
{ title = "rating"
, value = .rating
, toString = String.fromFloat
, width = Element.fill
}
]
, asc = model.table.asc
, sortBy = model.table.title
, onChange = ChangedSorting
}
, Element.none
)
view :
Theme ->
Theme
->
{ msgMapper : Msg -> msg
, showDialog : msg
, changedSheet : Maybe Part -> msg
@ -450,9 +359,10 @@ view :
}
view theme { msgMapper, showDialog, changedSheet } model =
let
style = Theme.toStyle theme
style =
Theme.toStyle theme
map (a,b,c) =
map ( a, b, c ) =
( a
, b |> Element.map msgMapper
, c |> Element.map msgMapper
@ -461,14 +371,17 @@ view theme { msgMapper, showDialog, changedSheet } model =
{ title = "Stateless Views"
, description = "Stateless views are simple functions that view some content. No wiring required."
, items =
[ iconButton style model |> map
, select style model |> map
, multiSelect style model |> map
, expansionPanel style model |> map
, modal style changedSheet model
Example.toCardList
{ idle = Idle |> msgMapper
, msgMapper = ExampleSpecific >> msgMapper
, style = style
, model = model.example
}
++ [ modal style changedSheet model
, carousel style model |> map
, tab style model |> map
, dialog style showDialog model
, textInput style model |> map
, list style model |> map
, sortTable style model |> map
]
}

390
example/src/View/Test.elm Normal file
View File

@ -0,0 +1,390 @@
module View.Test exposing (expansionPanel, iconButton, multiSelect, select, tab)
import Array
import Data.Style exposing (Style)
import Data.Theme as Theme exposing (Theme)
import Element exposing (Element)
import Element.Background as Background
import Framework.Card as Card
import Framework.Color as Color
import Framework.Grid as Grid
import Heroicons.Solid as Heroicons
import Html.Attributes as Attributes
import Icons
import Layout exposing (Part(..))
import Set exposing (Set)
import Widget
iconButton : msg -> Style msg -> List (Element msg)
iconButton idle style =
[ Element.row Grid.spacedEvenly
[ "Button"
|> Element.text
, Widget.button style.button
{ text = "Button"
, icon = Icons.triangle |> Element.html |> Element.el []
, onPress = Just idle
}
]
, Element.row Grid.spacedEvenly
[ "Text button"
|> Element.text
, Widget.textButton style.button
{ text = "Button"
, onPress = Just idle
}
]
, Element.row Grid.spacedEvenly
[ "Icon button"
|> Element.text
, Widget.iconButton style.button
{ text = "Button"
, icon = Icons.triangle |> Element.html |> Element.el []
, onPress = Just idle
}
]
, Element.row Grid.spacedEvenly
[ "Disabled button"
|> Element.text
, Widget.button style.button
{ text = "Button"
, icon = Icons.triangle |> Element.html |> Element.el []
, onPress = Nothing
}
]
, Element.row Grid.spacedEvenly
[ "Inactive Select button"
|> Element.text
, Widget.selectButton style.button
( False
, { text = "Button"
, icon = Icons.triangle |> Element.html |> Element.el []
, onPress = Just idle
}
)
]
, Element.row Grid.spacedEvenly
[ "Active Select button"
|> Element.text
, Widget.selectButton style.button
( True
, { text = "Button"
, icon = Icons.triangle |> Element.html |> Element.el []
, onPress = Just idle
}
)
]
]
select : msg -> Style msg -> List (Element msg)
select idle style =
[ Element.row Grid.spacedEvenly
[ "First selected"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { selected = Just 0
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = always idle >> Just
}
|> Widget.select
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
]
, Element.row Grid.spacedEvenly
[ "Nothing selected"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { selected = Nothing
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = always idle >> Just
}
|> Widget.select
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
]
, Element.row Grid.spacedEvenly
[ "Invalid selection"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { selected = Just -1
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = always idle >> Just
}
|> Widget.select
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
]
, Element.row Grid.spacedEvenly
[ "Disabled selection"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { selected = Just 0
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = always Nothing
}
|> Widget.select
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
]
, Element.row Grid.spacedEvenly
[ "Empty Options"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { selected = Nothing
, options =
[]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = always idle >> Just
}
|> Widget.select
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
]
]
multiSelect : msg -> Style msg -> List (Element msg)
multiSelect idle style =
[ Element.row Grid.spacedEvenly
[ "Some selected"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { selected = Set.fromList [ 0, 1 ]
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = always idle >> Just
}
|> Widget.multiSelect
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
]
, Element.row Grid.spacedEvenly
[ "Nothing selected"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { selected = Set.empty
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = always idle >> Just
}
|> Widget.multiSelect
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
]
, Element.row Grid.spacedEvenly
[ "Invalid selection"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { selected = Set.singleton -1
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = always idle >> Just
}
|> Widget.multiSelect
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
]
, Element.row Grid.spacedEvenly
[ "Disabled selection"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { selected = Set.singleton 0
, options =
[ 1, 2, 42 ]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = always Nothing
}
|> Widget.multiSelect
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
]
, Element.row Grid.spacedEvenly
[ "Empty Options"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { selected = Set.empty
, options =
[]
|> List.map
(\int ->
{ text = String.fromInt int
, icon = Element.none
}
)
, onSelect = always idle >> Just
}
|> Widget.multiSelect
|> Widget.buttonRow
{ list = style.row
, button = style.button
}
]
]
expansionPanel : msg -> Style msg -> List (Element msg)
expansionPanel idle style =
[ Element.row Grid.spacedEvenly
[ "Collapsed"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { onToggle = always idle
, isExpanded = False
, icon = Icons.triangle |> Element.html |> Element.el []
, text = "Button"
, content = Element.text <| "Hidden Message"
}
|> Widget.expansionPanel style.expansionPanel
]
, Element.row Grid.spacedEvenly
[ "Expanded"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, { onToggle = always idle
, isExpanded = True
, icon = Icons.triangle |> Element.html |> Element.el []
, text = "Button"
, content = Element.text <| "Hidden Message"
}
|> Widget.expansionPanel style.expansionPanel
]
]
tab : msg -> Style msg -> List (Element msg)
tab idle style =
[ Element.row Grid.spacedEvenly
[ "Nothing selected"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, Widget.tab style.tab
{ tabs =
{ selected = Nothing
, options =
[ 1, 2, 3 ]
|> List.map
(\int ->
{ text = int |> String.fromInt
, icon = Element.none
}
)
, onSelect = always idle >> Just
}
, content =
\selected ->
(case selected of
Nothing ->
"Please select a tab"
_ ->
""
)
|> Element.text
}
]
, Element.row Grid.spacedEvenly
[ "Tab selected"
|> Element.text
|> Element.el [ Element.width <| Element.fill ]
, Widget.tab style.tab
{ tabs =
{ selected = Just 0
, options =
[ 1, 2, 3 ]
|> List.map
(\int ->
{ text = int |> String.fromInt
, icon = Element.none
}
)
, onSelect = always idle >> Just
}
, content =
\selected ->
(case selected of
Just 0 ->
"First Tab selected"
_ ->
"Please select a tab"
)
|> Element.text
}
]
]

181
src/Internal/SortTable.elm Normal file
View File

@ -0,0 +1,181 @@
module Internal.SortTable exposing
( Column
, ColumnType
, SortTable
, floatColumn
, intColumn
, sortTable
, stringColumn
, unsortableColumn
)
import Element exposing (Element, Length)
import Internal.Button as Button
import Widget.Style exposing (SortTableStyle)
{-| A Sortable list allows you to sort coulmn.
-}
type ColumnType a
= StringColumn { value : a -> String, toString : String -> String }
| IntColumn { value : a -> Int, toString : Int -> String }
| FloatColumn { value : a -> Float, toString : Float -> String }
| UnsortableColumn (a -> String)
{-| The Model contains the sorting column name and if ascending or descending.
-}
type alias SortTable a msg =
{ content : List a
, columns : List (Column a)
, sortBy : String
, asc : Bool
, onChange : String -> msg
}
type Column a
= Column
{ title : String
, content : ColumnType a
, width : Length
}
unsortableColumn : { title : String, toString : a -> String, width : Length } -> Column a
unsortableColumn { title, toString, width } =
Column
{ title = title
, content = UnsortableColumn toString
, width = width
}
{-| A Column containing a Int
-}
intColumn : { title : String, value : a -> Int, toString : Int -> String, width : Length } -> Column a
intColumn { title, value, toString, width } =
Column
{ title = title
, content = IntColumn { value = value, toString = toString }
, width = width
}
{-| A Column containing a Float
-}
floatColumn : { title : String, value : a -> Float, toString : Float -> String, width : Length } -> Column a
floatColumn { title, value, toString, width } =
Column
{ title = title
, content = FloatColumn { value = value, toString = toString }
, width = width
}
{-| A Column containing a String
-}
stringColumn : { title : String, value : a -> String, toString : String -> String, width : Length } -> Column a
stringColumn { title, value, toString, width } =
Column
{ title = title
, content = StringColumn { value = value, toString = toString }
, width = width
}
{-| The View
-}
sortTable :
SortTableStyle msg
-> SortTable a msg
-> Element msg
sortTable style model =
let
findTitle : List (Column a) -> Maybe (ColumnType a)
findTitle list =
case list of
[] ->
Nothing
(Column head) :: tail ->
if head.title == model.sortBy then
Just head.content
else
findTitle tail
in
Element.table style.containerTable
{ data =
model.content
|> (model.columns
|> findTitle
|> Maybe.andThen
(\c ->
case c of
StringColumn { value } ->
Just <| List.sortBy value
IntColumn { value } ->
Just <| List.sortBy value
FloatColumn { value } ->
Just <| List.sortBy value
UnsortableColumn _ ->
Nothing
)
|> Maybe.withDefault identity
)
|> (if model.asc then
identity
else
List.reverse
)
, columns =
model.columns
|> List.map
(\(Column column) ->
{ header =
Button.button style.headerButton
{ text = column.title
, icon =
if column.title == model.sortBy then
if model.asc then
style.ascIcon
else
style.descIcon
else
style.defaultIcon
, onPress =
case column.content of
UnsortableColumn _ ->
Nothing
_ ->
Just <| model.onChange <| column.title
}
, width = column.width
, view =
(case column.content of
IntColumn { value, toString } ->
value >> toString
FloatColumn { value, toString } ->
value >> toString
StringColumn { value, toString } ->
value >> toString
UnsortableColumn toString ->
toString
)
>> Element.text
>> List.singleton
>> Element.paragraph []
}
)
}

View File

@ -3,8 +3,10 @@ module Widget exposing
, Select, MultiSelect, selectButton, select, multiSelect
, Dialog, modal, dialog
, ExpansionPanel, expansionPanel
, row, column, buttonRow, buttonColumn
, ColumnType, sortTable, floatColumn, intColumn, stringColumn, unsortableColumn
, TextInputStyle, textInput, carousel, tab
, Tab, buttonColumn, buttonRow, column, row
, Tab
)
{-| This module contains functions for displaying data.
@ -30,6 +32,16 @@ module Widget exposing
@docs ExpansionPanel, expansionPanel
# List
@docs row, column, buttonRow, buttonColumn
# SortTable
@docs ColumnType, sortTable, floatColumn, intColumn, stringColumn, unsortableColumn
# Other Widgets
@docs TextInputStyle, textInput, carousel, tab
@ -37,16 +49,17 @@ module Widget exposing
-}
import Array exposing (Array)
import Element exposing (Attribute, Element)
import Element exposing (Attribute, Element, Length)
import Element.Input exposing (Placeholder)
import Internal.Button as Button
import Internal.Dialog as Dialog
import Internal.ExpansionPanel as ExpansionPanel
import Internal.List as List
import Internal.Select as Select
import Internal.SortTable as SortTable
import Internal.TextInput as TextInput
import Set exposing (Set)
import Widget.Style exposing (ButtonStyle, ColumnStyle, DialogStyle, ExpansionPanelStyle, RowStyle, TabStyle)
import Widget.Style exposing (ButtonStyle, ColumnStyle, DialogStyle, ExpansionPanelStyle, RowStyle, SortTableStyle, TabStyle)
@ -322,6 +335,87 @@ buttonColumn =
{----------------------------------------------------------
- SORT TABLE
----------------------------------------------------------}
{-| A Sortable list allows you to sort coulmn.
-}
type alias ColumnType a =
SortTable.ColumnType a
type alias Column a =
SortTable.Column a
unsortableColumn :
{ title : String
, toString : a -> String
, width : Length
}
-> Column a
unsortableColumn =
SortTable.unsortableColumn
{-| A Column containing a Int
-}
intColumn :
{ title : String
, value : a -> Int
, toString : Int -> String
, width : Length
}
-> Column a
intColumn =
SortTable.intColumn
{-| A Column containing a Float
-}
floatColumn :
{ title : String
, value : a -> Float
, toString : Float -> String
, width : Length
}
-> Column a
floatColumn =
SortTable.floatColumn
{-| A Column containing a String
-}
stringColumn :
{ title : String
, value : a -> String
, toString : String -> String
, width : Length
}
-> Column a
stringColumn =
SortTable.stringColumn
{-| The View
-}
sortTable :
SortTableStyle msg
->
{ content : List a
, columns : List (Column a)
, sortBy : String
, asc : Bool
, onChange : String -> msg
}
-> Element msg
sortTable =
SortTable.sortTable
{----------------------------------------------------------
- OTHER STATELESS WIDGETS
----------------------------------------------------------}

View File

@ -1,215 +0,0 @@
module Widget.SortTable exposing
( Model, init, view, sortBy
, intColumn, floatColumn, stringColumn
)
{-| A Sortable list allows you to sort coulmn.
SortTable.view
{ content =
[ {id = 1, name = "Antonio", rating = 2.456}
, {id = 2, name = "Ana", rating = 1.34}
, {id = 3, name = "Alfred", rating = 4.22}
, {id = 4, name = "Thomas", rating = 3 }
]
, columns =
[ SortTable.intColumn
{ title = "Id"
, value = .id
, toString = \int -> "#" ++ String.fromInt int
}
, SortTable.stringColumn
{ title = "Name"
, value = .name
, toString = identity
}
, SortTable.floatColumn
{ title = "rating"
, value = .rating
, toString = String.fromFloat
}
]
, model = model
}
|> (\{data,columns} ->
{data = data
,columns = columns
|> List.map (\config->
{ header =
Input.button [Font.bold]
{ onPress =
{ title = config.header
, asc =
if config.header == model.title then
not model.asc
else
True
}
|> SortBy
|> Just
, label =
if config.header == model.title then
[ config.header |> Element.text
, Element.text <|
if model.asc then
"/\"
else
"\/"
]
|> Element.row [Font.bold]
else
config.header |> Element.text
}
, view = config.view >> Element.text
, width = Element.fill
}
)
})
|> Element.table []
# Basics
@docs Model, init, view, sortBy
# Columns
@docs intColumn, floatColumn, stringColumn
-}
type ColumnType a
= StringColumn { value : a -> String, toString : String -> String }
| IntColumn { value : a -> Int, toString : Int -> String }
| FloatColumn { value : a -> Float, toString : Float -> String }
{-| The Model contains the sorting column name and if ascending or descending.
-}
type alias Model =
{ title : String
, asc : Bool
}
type alias Column a =
{ title : String
, content : ColumnType a
}
{-| The initial State setting the sorting column name to the empty string.
-}
init : Model
init =
{ title = "", asc = True }
{-| A Column containing a Int
-}
intColumn : { title : String, value : a -> Int, toString : Int -> String } -> Column a
intColumn { title, value, toString } =
{ title = title
, content = IntColumn { value = value, toString = toString }
}
{-| A Column containing a Float
-}
floatColumn : { title : String, value : a -> Float, toString : Float -> String } -> Column a
floatColumn { title, value, toString } =
{ title = title
, content = FloatColumn { value = value, toString = toString }
}
{-| A Column containing a String
-}
stringColumn : { title : String, value : a -> String, toString : String -> String } -> Column a
stringColumn { title, value, toString } =
{ title = title
, content = StringColumn { value = value, toString = toString }
}
{-| Change the sorting criteras.
sortBy =
identity
-}
sortBy : { title : String, asc : Bool } -> Model
sortBy =
identity
{-| The View
-}
view :
{ content : List a
, columns : List (Column a)
, model : Model
}
->
{ data : List a
, columns : List { header : String, view : a -> String }
}
view { content, columns, model } =
let
findTitle : List (Column a) -> Maybe (ColumnType a)
findTitle list =
case list of
[] ->
Nothing
head :: tail ->
if head.title == model.title then
Just head.content
else
findTitle tail
in
{ data =
content
|> (columns
|> findTitle
|> Maybe.map
(\c ->
case c of
StringColumn { value } ->
List.sortBy value
IntColumn { value } ->
List.sortBy value
FloatColumn { value } ->
List.sortBy value
)
|> Maybe.withDefault identity
)
|> (if model.asc then
identity
else
List.reverse
)
, columns =
columns
|> List.map
(\column ->
{ header = column.title
, view =
case column.content of
IntColumn { value, toString } ->
value >> toString
FloatColumn { value, toString } ->
value >> toString
StringColumn { value, toString } ->
value >> toString
}
)
}

View File

@ -1,4 +1,4 @@
module Widget.Style exposing (ButtonStyle, ColumnStyle, DialogStyle, ExpansionPanelStyle, RowStyle, SnackbarStyle, Style, TabStyle, TextInputStyle)
module Widget.Style exposing (ButtonStyle, ColumnStyle, DialogStyle, ExpansionPanelStyle, RowStyle, SnackbarStyle, SortTableStyle, Style, TabStyle, TextInputStyle)
import Element exposing (Attribute, Element)
import Html exposing (Html)
@ -72,6 +72,15 @@ type alias ColumnStyle msg =
}
type alias SortTableStyle msg =
{ containerTable : List (Attribute msg)
, headerButton : ButtonStyle msg
, ascIcon : Element Never
, descIcon : Element Never
, defaultIcon : Element Never
}
type alias Style style msg =
{ style
| snackbar : SnackbarStyle msg