mirror of
https://github.com/Orasund/elm-ui-widgets.git
synced 2024-11-22 22:33:33 +03:00
starting work on List-Widget
This commit is contained in:
parent
8bb1d17371
commit
732a18720d
@ -1,233 +0,0 @@
|
||||
module Component exposing (Model, Msg(..), init, update, view)
|
||||
|
||||
import Browser
|
||||
import Element exposing (Color, Element)
|
||||
import Element.Background as Background
|
||||
import Element.Input as Input
|
||||
import Element.Border as Border
|
||||
import Element.Font as Font
|
||||
import Framework
|
||||
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 Set exposing (Set)
|
||||
import Time
|
||||
import Widget
|
||||
import Widget.Style exposing (ButtonStyle)
|
||||
import Widget.FilterSelect as FilterSelect
|
||||
import Widget.FilterMultiSelect as FilterMultiSelect
|
||||
import Widget.ScrollingNav as ScrollingNav
|
||||
import Widget.Snackbar as Snackbar
|
||||
import Widget.ValidatedInput as ValidatedInput
|
||||
import Data.Style exposing (style)
|
||||
|
||||
type alias Model =
|
||||
{ filterSelect : FilterSelect.Model
|
||||
, filterMultiSelect : FilterMultiSelect.Model
|
||||
, validatedInput : ValidatedInput.Model () ( String, String )
|
||||
}
|
||||
|
||||
|
||||
type Msg
|
||||
= FilterSelectSpecific FilterSelect.Msg
|
||||
| FilterMultiSelectSpecific FilterMultiSelect.Msg
|
||||
| ValidatedInputSpecific ValidatedInput.Msg
|
||||
|
||||
|
||||
|
||||
init : Model
|
||||
init =
|
||||
{ filterSelect =
|
||||
[ "Apple"
|
||||
, "Kiwi"
|
||||
, "Strawberry"
|
||||
, "Pineapple"
|
||||
, "Mango"
|
||||
, "Grapes"
|
||||
, "Watermelon"
|
||||
, "Orange"
|
||||
, "Lemon"
|
||||
, "Blueberry"
|
||||
, "Grapefruit"
|
||||
, "Coconut"
|
||||
, "Cherry"
|
||||
, "Banana"
|
||||
]
|
||||
|> Set.fromList
|
||||
|> FilterSelect.init
|
||||
, filterMultiSelect =
|
||||
[ "Apple"
|
||||
, "Kiwi"
|
||||
, "Strawberry"
|
||||
, "Pineapple"
|
||||
, "Mango"
|
||||
, "Grapes"
|
||||
, "Watermelon"
|
||||
, "Orange"
|
||||
, "Lemon"
|
||||
, "Blueberry"
|
||||
, "Grapefruit"
|
||||
, "Coconut"
|
||||
, "Cherry"
|
||||
, "Banana"
|
||||
]
|
||||
|> Set.fromList
|
||||
|> FilterMultiSelect.init
|
||||
, validatedInput =
|
||||
ValidatedInput.init
|
||||
{ value = ( "John", "Doe" )
|
||||
, validator =
|
||||
\string ->
|
||||
case string |> String.split " " of
|
||||
[ first, second ] ->
|
||||
Ok ( first, second )
|
||||
|
||||
_ ->
|
||||
Err ()
|
||||
, toString =
|
||||
\( first, second ) -> first ++ " " ++ second
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
FilterSelectSpecific m ->
|
||||
( { model
|
||||
| filterSelect = model.filterSelect |> FilterSelect.update m
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
FilterMultiSelectSpecific m ->
|
||||
( { model
|
||||
| filterMultiSelect = model.filterMultiSelect |> FilterMultiSelect.update m
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
ValidatedInputSpecific m ->
|
||||
( { model
|
||||
| validatedInput = model.validatedInput |> ValidatedInput.update m
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
|
||||
filterSelect : FilterSelect.Model -> (String,Element Msg)
|
||||
filterSelect model =
|
||||
( "Filter Select"
|
||||
, case model.selected of
|
||||
Just selected ->
|
||||
Element.row Grid.compact
|
||||
[ Element.el (Tag.simple ++ Group.left) <| Element.text <| selected
|
||||
, Input.button (Tag.simple ++ Group.right ++ Color.danger)
|
||||
{ onPress = Just <| FilterSelectSpecific <| FilterSelect.Selected Nothing
|
||||
, label = Element.html <| Heroicons.x [ Attributes.width 16 ]
|
||||
}
|
||||
]
|
||||
|
||||
Nothing ->
|
||||
Element.column Grid.simple
|
||||
[ FilterSelect.viewInput Input.simple
|
||||
model
|
||||
{ msgMapper = FilterSelectSpecific
|
||||
, placeholder =
|
||||
Just <|
|
||||
Input.placeholder [] <|
|
||||
Element.text <|
|
||||
"Fruit"
|
||||
, label = "Fruit"
|
||||
}
|
||||
, model
|
||||
|> FilterSelect.viewOptions
|
||||
|> List.map
|
||||
(\string ->
|
||||
Input.button (Button.simple ++ Tag.simple)
|
||||
{ onPress = Just <| FilterSelectSpecific <| FilterSelect.Selected <| Just <| string
|
||||
, label = Element.text string
|
||||
}
|
||||
)
|
||||
|> Element.wrappedRow [ Element.spacing 10 ]
|
||||
]
|
||||
)
|
||||
|
||||
filterMultiSelect : FilterMultiSelect.Model -> (String,Element Msg)
|
||||
filterMultiSelect model =
|
||||
( "Filter Multi Select"
|
||||
, [ FilterMultiSelect.viewInput model
|
||||
{ msgMapper = FilterMultiSelectSpecific
|
||||
, placeholder =
|
||||
Just <|
|
||||
Input.placeholder [] <|
|
||||
Element.text <|
|
||||
"Fruit"
|
||||
, label = "Fruit"
|
||||
, toChip = \string ->
|
||||
{ text = string
|
||||
, onPress = Just <| FilterMultiSelectSpecific <| FilterMultiSelect.ToggleSelected <| string
|
||||
, icon = Element.none
|
||||
}
|
||||
}
|
||||
|> Widget.textInput style.textInput
|
||||
|
||||
, model
|
||||
|> FilterMultiSelect.viewOptions
|
||||
|> List.map
|
||||
(\string ->
|
||||
Input.button (Button.simple ++ Tag.simple)
|
||||
{ onPress = Just <| FilterMultiSelectSpecific <| FilterMultiSelect.ToggleSelected <| string
|
||||
, label = Element.text string
|
||||
}
|
||||
)
|
||||
|> Element.wrappedRow [ Element.spacing 10 ]
|
||||
]
|
||||
|> Element.column Grid.simple
|
||||
)
|
||||
|
||||
validatedInput : ValidatedInput.Model () ( String, String ) -> (String,Element Msg)
|
||||
validatedInput model =
|
||||
( "Validated Input"
|
||||
, ValidatedInput.view Input.simple
|
||||
model
|
||||
{ label = "First Name, Sir Name"
|
||||
, msgMapper = ValidatedInputSpecific
|
||||
, placeholder = Nothing
|
||||
, readOnly =
|
||||
\maybeTuple ->
|
||||
Element.row Grid.compact
|
||||
[ maybeTuple
|
||||
|> (\( a, b ) -> a ++ " " ++ b)
|
||||
|> Element.text
|
||||
|> Element.el (Tag.simple ++ Group.left)
|
||||
, Heroicons.pencil [ Attributes.width 16 ]
|
||||
|> Element.html
|
||||
|> Element.el (Tag.simple ++ Group.right ++ Color.primary)
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
view : (Msg -> msg) -> Model ->
|
||||
{ title : String
|
||||
, description : String
|
||||
, items : List (String,Element msg)
|
||||
}
|
||||
view msgMapper model =
|
||||
{ title = "Components"
|
||||
, description = "Components have a Model, an Update- and sometimes even a Subscription-function. It takes some time to set them up correctly."
|
||||
, items =
|
||||
[ filterSelect model.filterSelect
|
||||
, filterMultiSelect model.filterMultiSelect
|
||||
, validatedInput model.validatedInput
|
||||
]
|
||||
|> List.map (Tuple.mapSecond (Element.map msgMapper) )
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
module Data.Section exposing (Section(..),asList,toString,fromString)
|
||||
module Data.Section exposing (Section(..), asList, fromString, toString)
|
||||
|
||||
|
||||
type Section
|
||||
= ComponentViews
|
||||
| ReusableViews
|
||||
= ReusableViews
|
||||
| StatelessViews
|
||||
|
||||
|
||||
asList : List Section
|
||||
asList =
|
||||
[ StatelessViews, ReusableViews, ComponentViews ]
|
||||
[ StatelessViews, ReusableViews ]
|
||||
|
||||
|
||||
toString : Section -> String
|
||||
toString section =
|
||||
case section of
|
||||
ComponentViews ->
|
||||
"Component"
|
||||
|
||||
ReusableViews ->
|
||||
"Reusable"
|
||||
@ -21,11 +21,10 @@ toString section =
|
||||
StatelessViews ->
|
||||
"Stateless"
|
||||
|
||||
|
||||
fromString : String -> Maybe Section
|
||||
fromString string =
|
||||
case string of
|
||||
"Component" ->
|
||||
Just ComponentViews
|
||||
|
||||
"Reusable" ->
|
||||
Just ReusableViews
|
||||
|
@ -1,223 +1,20 @@
|
||||
module Data.Style exposing (style)
|
||||
module Data.Style exposing (Style)
|
||||
|
||||
import Widget exposing (TextInputStyle)
|
||||
import Widget.Style exposing (Style,ButtonStyle)
|
||||
import Element exposing (Attribute)
|
||||
import Element.Input as Input
|
||||
import Element.Font as Font
|
||||
import Element.Border as Border
|
||||
import Framework
|
||||
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 Icons
|
||||
import Widget.Style exposing (ButtonStyle, DialogStyle, ExpansionPanelStyle,
|
||||
SnackbarStyle ,RowStyle,ColumnStyle,TextInputStyle,TabStyle)
|
||||
|
||||
textButton : ButtonStyle msg
|
||||
textButton =
|
||||
{ container = Button.simple
|
||||
, label = Grid.simple
|
||||
, disabled = Color.disabled
|
||||
, active = Color.primary
|
||||
}
|
||||
|
||||
simpleButton : ButtonStyle msg
|
||||
simpleButton =
|
||||
{ container = Button.simple ++ Color.primary
|
||||
, label = Grid.simple
|
||||
, disabled = Color.disabled
|
||||
, active = Color.primary
|
||||
}
|
||||
|
||||
buttonStyle : ButtonStyle msg
|
||||
buttonStyle =
|
||||
{ label = [ Element.spacing 8]
|
||||
, container = Button.simple
|
||||
, disabled = Color.disabled
|
||||
, active = Color.primary
|
||||
}
|
||||
|
||||
tabButtonStyle :ButtonStyle msg
|
||||
tabButtonStyle=
|
||||
{ label = [ Element.spacing 8]
|
||||
, container = Button.simple ++ Group.top
|
||||
, disabled = Color.disabled
|
||||
, active = Color.primary
|
||||
}
|
||||
|
||||
textInputStyle =
|
||||
{ chip = chipButtonStyle
|
||||
, chipsRow =
|
||||
[ Element.width <| Element.shrink
|
||||
, Element.spacing <| 4
|
||||
, Element.paddingEach
|
||||
{ top = 8
|
||||
, left = 0
|
||||
, right = 0
|
||||
, bottom = 8
|
||||
}
|
||||
]
|
||||
, containerRow =
|
||||
Button.simple
|
||||
++ Color.light
|
||||
++ [ Border.color <| Element.rgb255 186 189 182
|
||||
, Font.alignLeft
|
||||
, Element.paddingXY 8 0
|
||||
, Element.height <| Element.px <|42
|
||||
]
|
||||
++ Grid.simple
|
||||
, input =
|
||||
Color.light
|
||||
++ [ Element.padding 8
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
chipButtonStyle : ButtonStyle msg
|
||||
chipButtonStyle =
|
||||
{ container = Tag.simple
|
||||
, disabled = []
|
||||
, label = Grid.simple
|
||||
, active = Color.primary
|
||||
}
|
||||
|
||||
style : Style
|
||||
{ dialog :
|
||||
{ containerColumn : List (Attribute msg)
|
||||
, title : List (Attribute msg)
|
||||
, buttonRow : List (Attribute msg)
|
||||
, accept : ButtonStyle msg
|
||||
, dismiss : ButtonStyle msg
|
||||
}
|
||||
type alias Style msg =
|
||||
Widget.Style.Style
|
||||
{ dialog : DialogStyle msg
|
||||
, expansionPanel : ExpansionPanelStyle msg
|
||||
, button : ButtonStyle msg
|
||||
, primaryButton : ButtonStyle msg
|
||||
, tabButton : ButtonStyle msg
|
||||
, tab : TabStyle msg
|
||||
, textInput : TextInputStyle msg
|
||||
, chipButton : ButtonStyle msg
|
||||
} msg
|
||||
style =
|
||||
{ button = buttonStyle
|
||||
, primaryButton = simpleButton
|
||||
, tabButton = tabButtonStyle
|
||||
, textInput = textInputStyle
|
||||
, chipButton = chipButtonStyle
|
||||
, dialog =
|
||||
{ containerColumn =
|
||||
Card.simple
|
||||
++ Grid.simple
|
||||
++ [ Element.width <| Element.minimum 280 <| Element.maximum 560 <| Element.fill ]
|
||||
, title = Heading.h3
|
||||
, buttonRow =
|
||||
Grid.simple ++
|
||||
[ Element.paddingEach
|
||||
{ top = 28
|
||||
, bottom = 0
|
||||
, left = 0
|
||||
, right = 0
|
||||
}
|
||||
]
|
||||
, accept = simpleButton
|
||||
, dismiss = textButton
|
||||
}
|
||||
, snackbar =
|
||||
{ row =
|
||||
Card.simple
|
||||
++ Color.dark
|
||||
++ Grid.simple
|
||||
++ [ Element.paddingXY 8 6
|
||||
, Element.height <| Element.px <|54]
|
||||
, button =
|
||||
{ label = Grid.simple
|
||||
, container = Button.simple ++ Color.dark
|
||||
, disabled = Color.disabled
|
||||
, active = Color.primary
|
||||
}
|
||||
, text = [Element.paddingXY 8 0]
|
||||
}
|
||||
, layout = Framework.responsiveLayout
|
||||
{--\a w ->
|
||||
Html.div []
|
||||
[ Html.node "meta"
|
||||
[ Attributes.attribute "name" "viewport"
|
||||
, Attributes.attribute "content" "width=device-width, initial-scale=1.0"
|
||||
]
|
||||
[]
|
||||
, Element.layoutWith
|
||||
{options = (Element.focusStyle
|
||||
{ borderColor = Nothing
|
||||
, backgroundColor = Nothing
|
||||
, shadow = Nothing
|
||||
}
|
||||
|> List.singleton)
|
||||
}
|
||||
(Framework.layoutAttributes ++ a) <| w
|
||||
]--}
|
||||
, header =
|
||||
Framework.container
|
||||
++ Color.dark
|
||||
++ [ Element.padding <| 0
|
||||
, Element.height <| Element.px <| 42
|
||||
]
|
||||
, menuButton =
|
||||
{ label = Grid.simple
|
||||
, container = Button.simple ++ Group.center ++ Color.dark
|
||||
, disabled = Color.disabled
|
||||
, active = Color.primary
|
||||
}
|
||||
, sheetButton =
|
||||
{ container =
|
||||
Button.fill
|
||||
++ Group.center
|
||||
++ Color.light
|
||||
++ [Font.alignLeft]
|
||||
, label = Grid.simple
|
||||
, disabled = Color.disabled
|
||||
, active = Color.primary
|
||||
}
|
||||
, menuTabButton =
|
||||
{ container =
|
||||
[ Element.height <| Element.px <| 42
|
||||
, Border.widthEach
|
||||
{ top = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
bottom = 4
|
||||
}
|
||||
, Element.paddingEach
|
||||
{ top = 12
|
||||
, left = 8
|
||||
, right = 8
|
||||
, bottom = 4
|
||||
}
|
||||
, Border.color Color.black
|
||||
]
|
||||
, label = Grid.simple
|
||||
, disabled = Color.disabled
|
||||
, active = [ Border.color Color.turquoise ]
|
||||
}
|
||||
, sheet =
|
||||
Color.light ++ [ Element.width <| Element.maximum 256 <| Element.fill]
|
||||
, menuIcon =
|
||||
Icons.menu |> Element.html |> Element.el []
|
||||
, moreVerticalIcon =
|
||||
Icons.moreVertical |> Element.html |> Element.el []
|
||||
, spacing = 8
|
||||
, title = Heading.h2
|
||||
, searchIcon =
|
||||
Icons.search |> Element.html |> Element.el []
|
||||
, search =
|
||||
Color.simple ++
|
||||
Card.large ++
|
||||
[Font.color <| Element.rgb255 0 0 0
|
||||
, Element.padding 6
|
||||
, Element.centerY
|
||||
, Element.alignRight
|
||||
]
|
||||
, searchFill =
|
||||
Color.light
|
||||
++ Group.center
|
||||
, row : RowStyle msg
|
||||
, column : ColumnStyle msg
|
||||
, cardColumn : ColumnStyle msg
|
||||
}
|
||||
msg
|
291
example/src/Data/Style/ElmUiFramework.elm
Normal file
291
example/src/Data/Style/ElmUiFramework.elm
Normal file
@ -0,0 +1,291 @@
|
||||
module Data.Style.ElmUiFramework exposing (style)
|
||||
|
||||
import Element exposing (Attribute)
|
||||
import Element.Border as Border
|
||||
import Element.Font as Font
|
||||
import Element.Input as Input
|
||||
import Framework
|
||||
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 Icons
|
||||
import Data.Style exposing (Style)
|
||||
import Widget.Style exposing (ButtonStyle, DialogStyle, ExpansionPanelStyle,
|
||||
SnackbarStyle ,RowStyle,ColumnStyle,TextInputStyle,TabStyle)
|
||||
|
||||
textButton : ButtonStyle msg
|
||||
textButton =
|
||||
{ container = Button.simple
|
||||
, labelRow = Grid.simple
|
||||
, ifDisabled = Color.disabled
|
||||
, ifActive = Color.primary
|
||||
}
|
||||
|
||||
|
||||
simpleButton : ButtonStyle msg
|
||||
simpleButton =
|
||||
{ container = Button.simple ++ Color.primary
|
||||
, labelRow = Grid.simple
|
||||
, ifDisabled = Color.disabled
|
||||
, ifActive = Color.primary
|
||||
}
|
||||
|
||||
|
||||
menuTabButton : ButtonStyle msg
|
||||
menuTabButton =
|
||||
{ container =
|
||||
[ Element.height <| Element.px <| 42
|
||||
, Border.widthEach
|
||||
{ top = 0
|
||||
, left = 0
|
||||
, right = 0
|
||||
, bottom = 4
|
||||
}
|
||||
, Element.paddingEach
|
||||
{ top = 12
|
||||
, left = 8
|
||||
, right = 8
|
||||
, bottom = 4
|
||||
}
|
||||
, Border.color Color.black
|
||||
]
|
||||
, labelRow = Grid.simple
|
||||
, ifDisabled = Color.disabled
|
||||
, ifActive = [ Border.color Color.turquoise ]
|
||||
}
|
||||
|
||||
|
||||
menuButton : ButtonStyle msg
|
||||
menuButton =
|
||||
{ labelRow = Grid.simple
|
||||
, container = Button.simple ++ Group.center ++ Color.dark
|
||||
, ifDisabled = Color.disabled
|
||||
, ifActive = Color.primary
|
||||
}
|
||||
|
||||
|
||||
sheetButton : ButtonStyle msg
|
||||
sheetButton =
|
||||
{ container =
|
||||
Button.fill
|
||||
++ Group.center
|
||||
++ Color.light
|
||||
++ [ Font.alignLeft ]
|
||||
, labelRow = Grid.simple
|
||||
, ifDisabled = Color.disabled
|
||||
, ifActive = Color.primary
|
||||
}
|
||||
|
||||
|
||||
buttonStyle : ButtonStyle msg
|
||||
buttonStyle =
|
||||
{ labelRow = [ Element.spacing 8 ]
|
||||
, container = Button.simple
|
||||
, ifDisabled = Color.disabled
|
||||
, ifActive = Color.primary
|
||||
}
|
||||
|
||||
|
||||
snackbarButton : ButtonStyle msg
|
||||
snackbarButton =
|
||||
{ labelRow = Grid.simple
|
||||
, container = Button.simple ++ Color.dark
|
||||
, ifDisabled = Color.disabled
|
||||
, ifActive = Color.primary
|
||||
}
|
||||
|
||||
|
||||
tabButtonStyle : ButtonStyle msg
|
||||
tabButtonStyle =
|
||||
{ labelRow = [ Element.spacing 8 ]
|
||||
, container = Button.simple ++ Group.top
|
||||
, ifDisabled = Color.disabled
|
||||
, ifActive = Color.primary
|
||||
}
|
||||
|
||||
textInputStyle : TextInputStyle msg
|
||||
textInputStyle =
|
||||
{ chipButton = chipButtonStyle
|
||||
, chipsRow =
|
||||
[ Element.width <| Element.shrink
|
||||
, Element.spacing <| 4
|
||||
, Element.paddingEach
|
||||
{ top = 8
|
||||
, left = 0
|
||||
, right = 0
|
||||
, bottom = 8
|
||||
}
|
||||
]
|
||||
, containerRow =
|
||||
Button.simple
|
||||
++ Color.light
|
||||
++ [ Border.color <| Element.rgb255 186 189 182
|
||||
, Font.alignLeft
|
||||
, Element.paddingXY 8 0
|
||||
, Element.height <| Element.px <| 42
|
||||
]
|
||||
++ Grid.simple
|
||||
, input =
|
||||
Color.light
|
||||
++ [ Element.padding 8
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
chipButtonStyle : ButtonStyle msg
|
||||
chipButtonStyle =
|
||||
{ container = Tag.simple
|
||||
, ifDisabled = []
|
||||
, labelRow = Grid.simple
|
||||
, ifActive = Color.primary
|
||||
}
|
||||
|
||||
expansionPanelStyle : ExpansionPanelStyle msg
|
||||
expansionPanelStyle =
|
||||
{ containerColumn = Card.simple ++ Grid.simple ++ [Element.height <| Element.shrink]
|
||||
, panelRow = Grid.spacedEvenly ++ [Element.height <| Element.shrink]
|
||||
, labelRow = Grid.simple ++ [Element.height <| Element.shrink]
|
||||
, content = []
|
||||
, expandIcon = Icons.chevronDown |> Element.html |> Element.el []
|
||||
, collapseIcon = Icons.chevronUp |> Element.html |> Element.el []
|
||||
}
|
||||
|
||||
|
||||
|
||||
dialog : DialogStyle msg
|
||||
dialog =
|
||||
{ containerColumn =
|
||||
Card.simple
|
||||
++ Grid.simple
|
||||
++ [ Element.centerY
|
||||
, Element.width <| Element.minimum 280 <| Element.maximum 560 <| Element.fill
|
||||
]
|
||||
, title = Heading.h3
|
||||
, buttonRow =
|
||||
Grid.simple
|
||||
++ [ Element.paddingEach
|
||||
{ top = 28
|
||||
, bottom = 0
|
||||
, left = 0
|
||||
, right = 0
|
||||
}
|
||||
]
|
||||
, acceptButton = simpleButton
|
||||
, dismissButton = textButton
|
||||
}
|
||||
|
||||
snackbar : SnackbarStyle msg
|
||||
snackbar =
|
||||
{ containerRow =
|
||||
Card.simple
|
||||
++ Color.dark
|
||||
++ Grid.simple
|
||||
++ [ Element.paddingXY 8 6
|
||||
, Element.height <| Element.px <| 54
|
||||
]
|
||||
, button = snackbarButton
|
||||
, text = [ Element.paddingXY 8 0 ]
|
||||
}
|
||||
|
||||
tab : TabStyle msg
|
||||
tab =
|
||||
{ button = tabButtonStyle
|
||||
, optionRow = Grid.simple
|
||||
, containerColumn = Grid.compact
|
||||
, content = (Card.small ++ Group.bottom)
|
||||
}
|
||||
|
||||
row : RowStyle msg
|
||||
row =
|
||||
{ containerRow = Grid.compact
|
||||
, element = []
|
||||
, ifFirst = Group.left
|
||||
, ifLast = Group.right
|
||||
, ifCenter = Group.center
|
||||
}
|
||||
|
||||
cardColumn : ColumnStyle msg
|
||||
cardColumn =
|
||||
{ containerColumn = Grid.compact
|
||||
, element = Card.large ++ [Element.height <| Element.fill]
|
||||
, ifFirst = Group.top
|
||||
, ifLast = Group.bottom
|
||||
, ifCenter = Group.center
|
||||
}
|
||||
|
||||
column : ColumnStyle msg
|
||||
column =
|
||||
{ containerColumn = Grid.compact
|
||||
, element = []
|
||||
, ifFirst = Group.top
|
||||
, ifLast = Group.bottom
|
||||
, ifCenter =Group.center
|
||||
}
|
||||
|
||||
style : Style msg
|
||||
style =
|
||||
{ row = row
|
||||
, cardColumn = cardColumn
|
||||
, column = column
|
||||
, button = buttonStyle
|
||||
, primaryButton = simpleButton
|
||||
, tab = tab
|
||||
, textInput = textInputStyle
|
||||
, chipButton = chipButtonStyle
|
||||
, expansionPanel = expansionPanelStyle
|
||||
, dialog = dialog
|
||||
, snackbar = snackbar
|
||||
, layout = Framework.responsiveLayout
|
||||
|
||||
{--\a w ->
|
||||
Html.div []
|
||||
[ Html.node "meta"
|
||||
[ Attributes.attribute "name" "viewport"
|
||||
, Attributes.attribute "content" "width=device-width, initial-scale=1.0"
|
||||
]
|
||||
[]
|
||||
, Element.layoutWith
|
||||
{options = (Element.focusStyle
|
||||
{ borderColor = Nothing
|
||||
, backgroundColor = Nothing
|
||||
, shadow = Nothing
|
||||
}
|
||||
|> List.singleton)
|
||||
}
|
||||
(Framework.layoutAttributes ++ a) <| w
|
||||
]--}
|
||||
, header =
|
||||
Framework.container
|
||||
++ Color.dark
|
||||
++ [ Element.padding <| 0
|
||||
, Element.height <| Element.px <| 42
|
||||
]
|
||||
, menuButton = menuButton
|
||||
, sheetButton = sheetButton
|
||||
, menuTabButton = menuTabButton
|
||||
, sheet =
|
||||
Color.light ++ [ Element.width <| Element.maximum 256 <| Element.fill ]
|
||||
, menuIcon =
|
||||
Icons.menu |> Element.html |> Element.el []
|
||||
, moreVerticalIcon =
|
||||
Icons.moreVertical |> Element.html |> Element.el []
|
||||
, spacing = 8
|
||||
, title = Heading.h2
|
||||
, searchIcon =
|
||||
Icons.search |> Element.html |> Element.el []
|
||||
, search =
|
||||
Color.simple
|
||||
++ Card.large
|
||||
++ [ Font.color <| Element.rgb255 0 0 0
|
||||
, Element.padding 6
|
||||
, Element.centerY
|
||||
, Element.alignRight
|
||||
]
|
||||
, searchFill =
|
||||
Color.light ++ Group.center
|
||||
}
|
142
example/src/Data/Style/Template.elm
Normal file
142
example/src/Data/Style/Template.elm
Normal file
@ -0,0 +1,142 @@
|
||||
module Data.Style.Template exposing (style)
|
||||
|
||||
import Data.Style exposing (Style)
|
||||
import Element exposing (Attribute,Element)
|
||||
import Element.Border as Border
|
||||
import Element.Font as Font
|
||||
import Element.Background as Background
|
||||
import Widget.Style exposing (ButtonStyle, DialogStyle, ExpansionPanelStyle,
|
||||
SnackbarStyle ,TextInputStyle,TabStyle,ColumnStyle,RowStyle)
|
||||
|
||||
|
||||
fontSize : Int
|
||||
fontSize = 10
|
||||
|
||||
box : String -> List (Attribute msg)
|
||||
box string =
|
||||
[ Border.width 1
|
||||
, Background.color <| Element.rgba 1 1 1 0.5
|
||||
, Element.padding 10
|
||||
, Element.spacing 10
|
||||
, Element.above <|
|
||||
Element.el [Font.size <| fontSize] <|
|
||||
Element.text string
|
||||
]
|
||||
|
||||
decoration : String -> List (Attribute msg)
|
||||
decoration string =
|
||||
[ Element.below <|
|
||||
Element.el [Font.size <| fontSize] <|
|
||||
Element.text string
|
||||
, Background.color <| Element.rgb 0.66 0.66 0.66
|
||||
]
|
||||
|
||||
|
||||
icon : String -> Element msg
|
||||
icon string =
|
||||
Element.none
|
||||
|> Element.el
|
||||
[ Element.width <| Element.px 12
|
||||
, Element.height <| Element.px 12
|
||||
, Border.rounded 6
|
||||
, Border.width 1
|
||||
, Element.above <|
|
||||
Element.el [Font.size <| fontSize] <|
|
||||
Element.text string
|
||||
]
|
||||
|
||||
button : String -> ButtonStyle msg
|
||||
button string =
|
||||
{ container = box <| string ++ ":container"
|
||||
, labelRow = box <| string ++ ":labelRow"
|
||||
, ifDisabled = decoration <| string ++ ":ifDisabled"
|
||||
, ifActive = decoration <| string ++ ":ifActive"
|
||||
}
|
||||
|
||||
snackbar : String -> SnackbarStyle msg
|
||||
snackbar string =
|
||||
{ containerRow = box <| string ++ ":containerRow"
|
||||
, button = button <| string ++ ":button"
|
||||
, text = box <| string ++ ":text"
|
||||
}
|
||||
|
||||
dialog : String -> DialogStyle msg
|
||||
dialog string =
|
||||
{ containerColumn = box <| string ++ ":containerColumn"
|
||||
, title = box <| string ++ ":title"
|
||||
, buttonRow = box <| string ++ ":buttonRow"
|
||||
, acceptButton = button <| string ++ ":acceptButton"
|
||||
, dismissButton = button <| string ++ ":dismissButton"
|
||||
}
|
||||
|
||||
expansionPanel : String -> ExpansionPanelStyle msg
|
||||
expansionPanel string =
|
||||
{ containerColumn = box <| string ++ ":containerColumn"
|
||||
, panelRow = box <| string ++ ":panelRow"
|
||||
, labelRow = box <| string ++ ":labelRow"
|
||||
, content = box <| string ++ ":content"
|
||||
, expandIcon = icon <| string ++ ":expandIcon"
|
||||
, collapseIcon = icon <| string ++ ":collapseIcon"
|
||||
}
|
||||
|
||||
textInput : String -> TextInputStyle msg
|
||||
textInput string =
|
||||
{ chipButton = button <| string ++ ":chipButton"
|
||||
, chipsRow = box <| string ++ ":chipsRow"
|
||||
, containerRow = box <| string ++ ":containerRow"
|
||||
, input = box <| string ++ ":input"
|
||||
}
|
||||
|
||||
tab : String -> TabStyle msg
|
||||
tab string =
|
||||
{ button = button <| string ++ ":button"
|
||||
, optionRow = box <| string ++ ":optionRow"
|
||||
, containerColumn = box <| string ++ ":containerColumn"
|
||||
, content = box <| string ++ ":content"
|
||||
}
|
||||
|
||||
row : String -> RowStyle msg
|
||||
row string =
|
||||
{ containerRow = box <| string ++ ":containerRow"
|
||||
, element = box <| string ++ ":element"
|
||||
, ifFirst = box <| string ++ ":ifFirst"
|
||||
, ifLast = box <| string ++ ":ifLast"
|
||||
, ifCenter = box <| string ++ ":ifCenter"
|
||||
}
|
||||
|
||||
column : String -> ColumnStyle msg
|
||||
column string =
|
||||
{ containerColumn = box <| string ++ ":containerColumn"
|
||||
, element = box <| string ++ ":element"
|
||||
, ifFirst = box <| string ++ ":ifFirst"
|
||||
, ifLast = box <| string ++ ":ifLast"
|
||||
, ifCenter = box <| string ++ ":ifCenter"
|
||||
}
|
||||
|
||||
style :Style msg
|
||||
style =
|
||||
{ row = row <| "row"
|
||||
, cardColumn = column <| "cardRow"
|
||||
, column = column <| "column"
|
||||
, button = button <| "button"
|
||||
, primaryButton = button <| "primaryButton"
|
||||
, tab = tab <| "tab"
|
||||
, textInput = textInput <| "textInput"
|
||||
, chipButton = button <| "chipButton"
|
||||
, expansionPanel = expansionPanel "expansionPanel"
|
||||
, dialog = dialog "dialog"
|
||||
, snackbar = snackbar "snackbar"
|
||||
, layout = Element.layout
|
||||
, header = box "header"
|
||||
, menuButton = button "menuButton"
|
||||
, sheetButton = button "sheetButton"
|
||||
, menuTabButton = button "menuTabButton"
|
||||
, sheet = box "sheet"
|
||||
, menuIcon = icon "menuIcon"
|
||||
, moreVerticalIcon = icon "moreVerticalIcon"
|
||||
, spacing = 8
|
||||
, title = box "title"
|
||||
, searchIcon = icon "searchIcon"
|
||||
, search = box "search"
|
||||
, searchFill = box "searchFill"
|
||||
}
|
17
example/src/Data/Theme.elm
Normal file
17
example/src/Data/Theme.elm
Normal file
@ -0,0 +1,17 @@
|
||||
module Data.Theme exposing (Theme(..),toStyle)
|
||||
|
||||
import Data.Style exposing (Style)
|
||||
import Data.Style.ElmUiFramework
|
||||
import Data.Style.Template
|
||||
|
||||
type Theme =
|
||||
ElmUiFramework
|
||||
| Template
|
||||
|
||||
toStyle : Theme -> Style msg
|
||||
toStyle theme =
|
||||
case theme of
|
||||
ElmUiFramework ->
|
||||
Data.Style.ElmUiFramework.style
|
||||
Template ->
|
||||
Data.Style.Template.style
|
@ -1,14 +1,15 @@
|
||||
module Example exposing (main)
|
||||
|
||||
import Array
|
||||
import Browser
|
||||
import Browser.Dom as Dom exposing (Viewport)
|
||||
import Browser.Events as Events
|
||||
import Browser.Navigation as Navigation
|
||||
import Component
|
||||
import Element exposing (DeviceClass(..), Element,Attribute)
|
||||
import Element.Input as Input
|
||||
import Element.Font as Font
|
||||
import Data.Section as Section exposing (Section(..))
|
||||
import Element exposing (Attribute, DeviceClass(..), Element)
|
||||
import Element.Border as Border
|
||||
import Element.Font as Font
|
||||
import Element.Input as Input
|
||||
import Framework
|
||||
import Framework.Button as Button
|
||||
import Framework.Card as Card
|
||||
@ -21,25 +22,24 @@ import Framework.Tag as Tag
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as Attributes
|
||||
import Icons
|
||||
import Layout exposing (Part, Layout)
|
||||
import Data.Style exposing (style)
|
||||
import Layout exposing (Layout, Part)
|
||||
import Reusable
|
||||
import Set exposing (Set)
|
||||
import Stateless
|
||||
import Task
|
||||
import Time
|
||||
import Widget
|
||||
import Widget.Style exposing (ButtonStyle)
|
||||
import Widget.FilterSelect as FilterSelect
|
||||
import Widget.ScrollingNav as ScrollingNav
|
||||
import Widget.Snackbar as Snackbar
|
||||
import Widget.ValidatedInput as ValidatedInput
|
||||
import Data.Section as Section exposing (Section(..))
|
||||
import Array
|
||||
import Widget.Style exposing (ButtonStyle)
|
||||
import Data.Style as Style exposing (Style)
|
||||
import Data.Theme as Theme exposing (Theme(..))
|
||||
|
||||
|
||||
|
||||
|
||||
type alias LoadedModel =
|
||||
{ component : Component.Model
|
||||
, stateless : Stateless.Model
|
||||
{ stateless : Stateless.Model
|
||||
, reusable : Reusable.Model
|
||||
, scrollingNav : ScrollingNav.Model Section
|
||||
, layout : Layout LoadedMsg
|
||||
@ -50,6 +50,7 @@ type alias LoadedModel =
|
||||
, current : String
|
||||
, remaining : Int
|
||||
}
|
||||
, theme : Theme
|
||||
}
|
||||
|
||||
|
||||
@ -61,16 +62,16 @@ type Model
|
||||
type LoadedMsg
|
||||
= StatelessSpecific Stateless.Msg
|
||||
| ReusableSpecific Reusable.Msg
|
||||
| ComponentSpecific Component.Msg
|
||||
| UpdateScrollingNav (ScrollingNav.Model Section -> ScrollingNav.Model Section)
|
||||
| TimePassed Int
|
||||
| AddSnackbar (String,Bool)
|
||||
| AddSnackbar ( String, Bool )
|
||||
| ToggleDialog Bool
|
||||
| ChangedSidebar (Maybe Part)
|
||||
| Resized { width : Int, height : Int }
|
||||
| Load String
|
||||
| JumpTo Section
|
||||
| ChangedSearch String
|
||||
| SetTheme Theme
|
||||
| Idle
|
||||
|
||||
|
||||
@ -87,16 +88,17 @@ initialModel { viewport } =
|
||||
{ toString = Section.toString
|
||||
, fromString = Section.fromString
|
||||
, arrangement = Section.asList
|
||||
, toMsg = \result ->
|
||||
, toMsg =
|
||||
\result ->
|
||||
case result of
|
||||
Ok fun ->
|
||||
UpdateScrollingNav fun
|
||||
|
||||
Err _ ->
|
||||
Idle
|
||||
}
|
||||
in
|
||||
( { component = Component.init
|
||||
, stateless = Stateless.init
|
||||
( { stateless = Stateless.init
|
||||
, reusable = Reusable.init
|
||||
, scrollingNav = scrollingNav
|
||||
, layout = Layout.init
|
||||
@ -110,6 +112,7 @@ initialModel { viewport } =
|
||||
, current = ""
|
||||
, remaining = 0
|
||||
}
|
||||
, theme = ElmUiFramework
|
||||
}
|
||||
, cmd
|
||||
)
|
||||
@ -124,11 +127,17 @@ init () =
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
|
||||
case model of
|
||||
Loading ->
|
||||
Element.none |> Framework.responsiveLayout []
|
||||
|
||||
Loaded m ->
|
||||
let
|
||||
style : Style msg
|
||||
style =
|
||||
Theme.toStyle m.theme
|
||||
in
|
||||
Html.map LoadedSpecific <|
|
||||
Layout.view []
|
||||
{ dialog =
|
||||
@ -139,11 +148,13 @@ view model =
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
, title = Just "Dialog"
|
||||
, accept = Just
|
||||
, accept =
|
||||
Just
|
||||
{ text = "Ok"
|
||||
, onPress = Just <| ToggleDialog False
|
||||
}
|
||||
, dismiss = Just
|
||||
, dismiss =
|
||||
Just
|
||||
{ text = "Dismiss"
|
||||
, onPress = Just <| ToggleDialog False
|
||||
}
|
||||
@ -158,57 +169,55 @@ view model =
|
||||
, [ m.scrollingNav
|
||||
|> ScrollingNav.view
|
||||
(\section ->
|
||||
( case section of
|
||||
ComponentViews ->
|
||||
m.component
|
||||
|> Component.view ComponentSpecific
|
||||
|
||||
|
||||
|
||||
(case section of
|
||||
ReusableViews ->
|
||||
Reusable.view
|
||||
Reusable.view m.theme
|
||||
{ addSnackbar = AddSnackbar
|
||||
, model = m.reusable
|
||||
, msgMapper = ReusableSpecific
|
||||
}
|
||||
|
||||
StatelessViews ->
|
||||
Stateless.view
|
||||
Stateless.view m.theme
|
||||
{ msgMapper = StatelessSpecific
|
||||
, showDialog = ToggleDialog True
|
||||
, changedSheet = ChangedSidebar
|
||||
}
|
||||
m.stateless
|
||||
) |> (\{title,description,items} ->
|
||||
)
|
||||
|> (\{ title, description, items } ->
|
||||
[ Element.el Heading.h2 <| Element.text <| title
|
||||
, if m.search.current == "" then
|
||||
description
|
||||
|> Element.text
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
else Element.none
|
||||
|
||||
else
|
||||
Element.none
|
||||
, items
|
||||
|> (if m.search.current /= "" then
|
||||
List.filter
|
||||
( Tuple.first
|
||||
>> String.toLower
|
||||
>> String.contains (m.search.current |> String.toLower)
|
||||
(\(a,_,_) ->
|
||||
a
|
||||
|> String.toLower
|
||||
|> String.contains (m.search.current |> String.toLower)
|
||||
)
|
||||
|
||||
else
|
||||
identity)
|
||||
identity
|
||||
)
|
||||
|> List.map
|
||||
(\(name,elem) ->
|
||||
(\( name, elem, more ) ->
|
||||
[ Element.text name
|
||||
|> Element.el Heading.h3
|
||||
, elem
|
||||
, more
|
||||
]
|
||||
|> Element.column
|
||||
(Grid.simple
|
||||
++ Card.large
|
||||
++ [Element.height <| Element.fill])
|
||||
|> Widget.column style.cardColumn
|
||||
)
|
||||
|> Element.wrappedRow
|
||||
(Grid.simple ++ [Element.height <| Element.shrink])
|
||||
(Grid.simple ++ [ Element.height <| Element.shrink ])
|
||||
]
|
||||
|> Element.column (Grid.section ++ [ Element.centerX ])
|
||||
)
|
||||
@ -217,7 +226,7 @@ view model =
|
||||
|> Element.column Framework.container
|
||||
]
|
||||
|> Element.column Grid.compact
|
||||
, style = style
|
||||
, style =style
|
||||
, layout = m.layout
|
||||
, window = m.window
|
||||
, menu =
|
||||
@ -232,23 +241,37 @@ view model =
|
||||
, actions =
|
||||
[ { onPress = Just <| Load "https://package.elm-lang.org/packages/Orasund/elm-ui-widgets/latest/"
|
||||
, text = "Docs"
|
||||
, icon = Icons.book|> Element.html |> Element.el []
|
||||
, icon = Icons.book |> Element.html |> Element.el []
|
||||
}
|
||||
, { onPress = Just <| Load "https://github.com/Orasund/elm-ui-widgets"
|
||||
, text = "Github"
|
||||
, icon = Icons.github|> Element.html |> Element.el []
|
||||
, icon = Icons.github |> Element.html |> Element.el []
|
||||
}
|
||||
, { 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
|
||||
Just <| SetTheme <| Template
|
||||
else
|
||||
Nothing
|
||||
, text = "Template Theme"
|
||||
, icon = Icons.penTool |> Element.html |> Element.el []
|
||||
}
|
||||
, { onPress = Nothing
|
||||
, text = "Placeholder"
|
||||
, icon = Icons.circle|> Element.html |> Element.el []
|
||||
, icon = Icons.circle |> Element.html |> Element.el []
|
||||
}
|
||||
, { onPress = Nothing
|
||||
, text = "Placeholder"
|
||||
, icon = Icons.triangle|> Element.html |> Element.el []
|
||||
, icon = Icons.triangle |> Element.html |> Element.el []
|
||||
}
|
||||
, { onPress = Nothing
|
||||
, text = "Placeholder"
|
||||
, icon = Icons.square|> Element.html |> Element.el []
|
||||
, icon = Icons.square |> Element.html |> Element.el []
|
||||
}
|
||||
]
|
||||
, onChangedSidebar = ChangedSidebar
|
||||
@ -268,17 +291,6 @@ view model =
|
||||
updateLoaded : LoadedMsg -> LoadedModel -> ( LoadedModel, Cmd LoadedMsg )
|
||||
updateLoaded msg model =
|
||||
case msg of
|
||||
ComponentSpecific m ->
|
||||
model.component
|
||||
|> Component.update m
|
||||
|> Tuple.mapBoth
|
||||
(\component ->
|
||||
{ model
|
||||
| component = component
|
||||
}
|
||||
)
|
||||
(Cmd.map ComponentSpecific)
|
||||
|
||||
ReusableSpecific m ->
|
||||
( model.reusable
|
||||
|> Reusable.update m
|
||||
@ -302,13 +314,14 @@ updateLoaded msg model =
|
||||
(Cmd.map StatelessSpecific)
|
||||
|
||||
UpdateScrollingNav fun ->
|
||||
( { model | scrollingNav = model.scrollingNav |> fun}
|
||||
( { model | scrollingNav = model.scrollingNav |> fun }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
TimePassed int ->
|
||||
let
|
||||
search = model.search
|
||||
search =
|
||||
model.search
|
||||
in
|
||||
( { model
|
||||
| layout = model.layout |> Layout.timePassed int
|
||||
@ -319,10 +332,12 @@ updateLoaded msg model =
|
||||
| current = search.raw
|
||||
, remaining = 0
|
||||
}
|
||||
|
||||
else
|
||||
{ search
|
||||
| remaining = search.remaining - int
|
||||
}
|
||||
|
||||
else
|
||||
model.search
|
||||
}
|
||||
@ -330,17 +345,21 @@ updateLoaded msg model =
|
||||
|> Task.perform UpdateScrollingNav
|
||||
)
|
||||
|
||||
AddSnackbar (string,bool) ->
|
||||
AddSnackbar ( string, bool ) ->
|
||||
( { model
|
||||
| layout = model.layout
|
||||
| layout =
|
||||
model.layout
|
||||
|> Layout.queueMessage
|
||||
{ text = string
|
||||
, button = if bool then
|
||||
, button =
|
||||
if bool then
|
||||
Just
|
||||
{ text = "Add"
|
||||
, onPress = Just <|
|
||||
(AddSnackbar ("This is another message", False))
|
||||
, onPress =
|
||||
Just <|
|
||||
AddSnackbar ( "This is another message", False )
|
||||
}
|
||||
|
||||
else
|
||||
Nothing
|
||||
}
|
||||
@ -364,7 +383,7 @@ updateLoaded msg model =
|
||||
)
|
||||
|
||||
Load string ->
|
||||
( model, Navigation.load string)
|
||||
( model, Navigation.load string )
|
||||
|
||||
JumpTo section ->
|
||||
( model
|
||||
@ -377,9 +396,11 @@ updateLoaded msg model =
|
||||
|
||||
ChangedSearch string ->
|
||||
let
|
||||
search = model.search
|
||||
search =
|
||||
model.search
|
||||
in
|
||||
( { model | search =
|
||||
( { model
|
||||
| search =
|
||||
{ search
|
||||
| raw = string
|
||||
, remaining = 300
|
||||
@ -388,8 +409,13 @@ updateLoaded msg model =
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
SetTheme theme ->
|
||||
( { model | theme = theme }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
Idle ->
|
||||
( model , Cmd.none)
|
||||
( model, Cmd.none )
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
|
@ -1,17 +1,19 @@
|
||||
module Icons exposing
|
||||
( book
|
||||
, chevronDown
|
||||
, chevronLeft
|
||||
, chevronRight
|
||||
, circle
|
||||
, github
|
||||
, menu
|
||||
, moreVertical
|
||||
, circle
|
||||
, triangle
|
||||
, square
|
||||
, repeat
|
||||
, search
|
||||
, slash
|
||||
, repeat
|
||||
, chevronDown
|
||||
, chevronRight
|
||||
, chevronLeft
|
||||
, square
|
||||
, triangle
|
||||
, chevronUp
|
||||
, penTool
|
||||
)
|
||||
|
||||
import Html exposing (Html)
|
||||
@ -33,6 +35,7 @@ svgFeatherIcon className =
|
||||
, width "16"
|
||||
]
|
||||
|
||||
|
||||
chevronDown : Html msg
|
||||
chevronDown =
|
||||
svgFeatherIcon "chevron-down"
|
||||
@ -45,12 +48,20 @@ chevronRight =
|
||||
svgFeatherIcon "chevron-right"
|
||||
[ Svg.polyline [ points "9 18 15 12 9 6" ] []
|
||||
]
|
||||
|
||||
|
||||
chevronLeft : Html msg
|
||||
chevronLeft =
|
||||
svgFeatherIcon "chevron-left"
|
||||
[ Svg.polyline [ points "15 18 9 12 15 6" ] []
|
||||
]
|
||||
|
||||
chevronUp : Html msg
|
||||
chevronUp =
|
||||
svgFeatherIcon "chevron-up"
|
||||
[ Svg.polyline [ points "18 15 12 9 6 15" ] []
|
||||
]
|
||||
|
||||
repeat : Html msg
|
||||
repeat =
|
||||
svgFeatherIcon "repeat"
|
||||
@ -60,6 +71,14 @@ repeat =
|
||||
, Svg.path [ d "M21 13v2a4 4 0 0 1-4 4H3" ] []
|
||||
]
|
||||
|
||||
penTool : Html msg
|
||||
penTool =
|
||||
svgFeatherIcon "pen-tool"
|
||||
[ Svg.path [ d "M12 19l7-7 3 3-7 7-3-3z" ] []
|
||||
, Svg.path [ d "M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z" ] []
|
||||
, Svg.path [ d "M2 2l7.586 7.586" ] []
|
||||
, Svg.circle [ cx "11", cy "11", r "2" ] []
|
||||
]
|
||||
|
||||
book : Html msg
|
||||
book =
|
||||
@ -84,6 +103,7 @@ menu =
|
||||
, Svg.line [ x1 "3", y1 "18", x2 "21", y2 "18" ] []
|
||||
]
|
||||
|
||||
|
||||
moreVertical : Html msg
|
||||
moreVertical =
|
||||
svgFeatherIcon "more-vertical"
|
||||
@ -92,12 +112,14 @@ moreVertical =
|
||||
, Svg.circle [ cx "12", cy "19", r "1" ] []
|
||||
]
|
||||
|
||||
|
||||
circle : Html msg
|
||||
circle =
|
||||
svgFeatherIcon "circle"
|
||||
[ Svg.circle [ cx "12", cy "12", r "10" ] []
|
||||
]
|
||||
|
||||
|
||||
slash : Html msg
|
||||
slash =
|
||||
svgFeatherIcon "slash"
|
||||
@ -105,18 +127,21 @@ slash =
|
||||
, Svg.line [ x1 "4.93", y1 "4.93", x2 "19.07", y2 "19.07" ] []
|
||||
]
|
||||
|
||||
|
||||
triangle : Html msg
|
||||
triangle =
|
||||
svgFeatherIcon "triangle"
|
||||
[ Svg.path [ d "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" ] []
|
||||
]
|
||||
|
||||
|
||||
square : Html msg
|
||||
square =
|
||||
svgFeatherIcon "square"
|
||||
[ Svg.rect [ Svg.Attributes.x "3", y "3", width "18", height "18", rx "2", ry "2" ] []
|
||||
]
|
||||
|
||||
|
||||
search : Html msg
|
||||
search =
|
||||
svgFeatherIcon "search"
|
||||
|
@ -20,12 +20,11 @@ import Html.Attributes as Attributes
|
||||
import Set exposing (Set)
|
||||
import Time
|
||||
import Widget
|
||||
import Widget.FilterSelect as FilterSelect
|
||||
import Widget.ScrollingNav as ScrollingNav
|
||||
import Widget.Snackbar as Snackbar
|
||||
import Widget.SortTable as SortTable
|
||||
import Widget.ValidatedInput as ValidatedInput
|
||||
|
||||
import Data.Style exposing (Style)
|
||||
import Data.Theme as Theme exposing (Theme)
|
||||
|
||||
type alias Model =
|
||||
SortTable.Model
|
||||
@ -54,36 +53,37 @@ init =
|
||||
SortTable.sortBy { title = "Name", asc = True }
|
||||
|
||||
|
||||
snackbar : ((String,Bool) -> msg) -> (String,Element msg)
|
||||
snackbar addSnackbar =
|
||||
snackbar : Style msg -> (( String, Bool ) -> msg) -> ( String, Element msg,Element msg )
|
||||
snackbar style addSnackbar =
|
||||
( "Snackbar"
|
||||
, [Input.button Button.simple
|
||||
{ onPress = Just <| addSnackbar <|
|
||||
("This is a notification. It will disappear after 10 seconds."
|
||||
, [ Widget.button style.button
|
||||
{ onPress =
|
||||
Just <|
|
||||
addSnackbar <|
|
||||
( "This is a notification. It will disappear after 10 seconds."
|
||||
, False
|
||||
)
|
||||
, label =
|
||||
"Add Notification"
|
||||
|> Element.text
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
, text = "Add Notification"
|
||||
, icon =Element.none
|
||||
}
|
||||
, Input.button Button.simple
|
||||
{ onPress = Just <| addSnackbar <|
|
||||
("You can add another notification if you want."
|
||||
, Widget.button style.button
|
||||
{ onPress =
|
||||
Just <|
|
||||
addSnackbar <|
|
||||
( "You can add another notification if you want."
|
||||
, True
|
||||
)
|
||||
, label =
|
||||
"Add Notification with Action"
|
||||
|> Element.text
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
, text = "Add Notification with Action"
|
||||
, icon = Element.none
|
||||
}
|
||||
] |> Element.column Grid.simple
|
||||
]
|
||||
|> Element.column Grid.simple
|
||||
, Element.none
|
||||
)
|
||||
|
||||
sortTable : SortTable.Model -> (String,Element Msg)
|
||||
sortTable model =
|
||||
|
||||
sortTable : Style Msg -> SortTable.Model -> ( String, Element Msg,Element Msg )
|
||||
sortTable style model =
|
||||
( "Sort Table"
|
||||
, SortTable.view
|
||||
{ content =
|
||||
@ -152,31 +152,41 @@ sortTable model =
|
||||
}
|
||||
)
|
||||
|> Element.table Grid.simple
|
||||
, Element.none
|
||||
)
|
||||
|
||||
scrollingNavCard : (String , Element msg )
|
||||
scrollingNavCard =
|
||||
("Scrolling Nav"
|
||||
|
||||
scrollingNavCard : Style msg -> ( String, Element msg, Element msg )
|
||||
scrollingNavCard style =
|
||||
( "Scrolling Nav"
|
||||
, Element.text "Resize the screen and open the side-menu. Then start scrolling to see the scrolling navigation in action."
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
, Element.none
|
||||
)
|
||||
|
||||
|
||||
view :
|
||||
{ addSnackbar : (String,Bool) -> msg
|
||||
Theme ->
|
||||
{ addSnackbar : ( String, Bool ) -> msg
|
||||
, msgMapper : Msg -> msg
|
||||
, model : Model
|
||||
}
|
||||
-> { title : String
|
||||
->
|
||||
{ title : String
|
||||
, description : String
|
||||
, items : List (String,Element msg)
|
||||
, items : List ( String, Element msg,Element msg )
|
||||
}
|
||||
view { addSnackbar, msgMapper, model } =
|
||||
view theme { addSnackbar, msgMapper, model } =
|
||||
let
|
||||
style = Theme.toStyle theme
|
||||
in
|
||||
{ title = "Reusable Views"
|
||||
, description = "Reusable views have an internal state but no update function. You will need to do some wiring, but nothing complicated."
|
||||
, items =
|
||||
[ snackbar addSnackbar
|
||||
, sortTable model |> Tuple.mapSecond (Element.map msgMapper)
|
||||
, scrollingNavCard
|
||||
[ snackbar style addSnackbar
|
||||
, sortTable style model |> \(a,b,c) ->
|
||||
(a,b |> Element.map msgMapper,c |> Element.map msgMapper)
|
||||
, scrollingNavCard style
|
||||
]
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
module Stateless exposing (Model, Msg, init, update, view)
|
||||
|
||||
import Array exposing (Array)
|
||||
import Data.Style exposing (Style)
|
||||
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
|
||||
@ -16,19 +18,18 @@ import Framework.Tag as Tag
|
||||
import Heroicons.Solid as Heroicons
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as Attributes
|
||||
import Set exposing (Set)
|
||||
import Widget.Style exposing (ButtonStyle)
|
||||
import Layout exposing (Part(..))
|
||||
import Icons
|
||||
import Layout exposing (Part(..))
|
||||
import Set exposing (Set)
|
||||
import Widget
|
||||
import Element.Font as Font
|
||||
import Data.Style exposing (style)
|
||||
import Widget.Style exposing (ButtonStyle)
|
||||
import Data.Theme as Theme exposing (Theme)
|
||||
|
||||
type alias Model =
|
||||
{ selected : Maybe Int
|
||||
, multiSelected : Set Int
|
||||
, chipTextInput : Set String
|
||||
, isCollapsed : Bool
|
||||
, isExpanded : Bool
|
||||
, carousel : Int
|
||||
, tab : Maybe Int
|
||||
, button : Bool
|
||||
@ -53,7 +54,7 @@ init =
|
||||
{ selected = Nothing
|
||||
, multiSelected = Set.empty
|
||||
, chipTextInput = Set.empty
|
||||
, isCollapsed = False
|
||||
, isExpanded = False
|
||||
, carousel = 0
|
||||
, tab = Just 1
|
||||
, button = True
|
||||
@ -87,7 +88,7 @@ update msg model =
|
||||
|
||||
ToggleCollapsable bool ->
|
||||
( { model
|
||||
| isCollapsed = bool
|
||||
| isExpanded = bool
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
@ -95,11 +96,13 @@ update msg model =
|
||||
ToggleTextInputChip string ->
|
||||
( { model
|
||||
| chipTextInput =
|
||||
model.chipTextInput |>
|
||||
if model.chipTextInput |> Set.member string then
|
||||
model.chipTextInput
|
||||
|> (if model.chipTextInput |> Set.member string then
|
||||
Set.remove string
|
||||
|
||||
else
|
||||
Set.insert string
|
||||
)
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
@ -122,22 +125,24 @@ update msg model =
|
||||
( { model | button = bool }, Cmd.none )
|
||||
|
||||
SetTextInput string ->
|
||||
( {model | textInput = string },Cmd.none)
|
||||
( { model | textInput = string }, Cmd.none )
|
||||
|
||||
Idle ->
|
||||
( model, Cmd.none)
|
||||
( model, Cmd.none )
|
||||
|
||||
|
||||
select : Model -> (String,Element Msg)
|
||||
select model =
|
||||
select : Style Msg -> Model -> ( String, Element Msg,Element Msg )
|
||||
select style model =
|
||||
let
|
||||
buttonStyle = style.button
|
||||
buttonStyle =
|
||||
style.button
|
||||
in
|
||||
( "Select"
|
||||
, { selected = model.selected
|
||||
, options =
|
||||
[ 1, 2, 42 ]
|
||||
|> List.map (\int ->
|
||||
|> List.map
|
||||
(\int ->
|
||||
{ text = String.fromInt int
|
||||
, icon = Element.none
|
||||
}
|
||||
@ -145,104 +150,74 @@ select model =
|
||||
, onSelect = ChangedSelected >> Just
|
||||
}
|
||||
|> Widget.select
|
||||
|> List.indexedMap
|
||||
(\i ->
|
||||
Widget.selectButton
|
||||
{ buttonStyle
|
||||
| container = buttonStyle.container
|
||||
++ (if i == 0 then
|
||||
Group.left
|
||||
|
||||
else if i == 2 then
|
||||
Group.right
|
||||
|
||||
else
|
||||
Group.center
|
||||
)
|
||||
|> Widget.buttonRow
|
||||
{ list = style.row
|
||||
, button = style.button
|
||||
}
|
||||
)
|
||||
|
||||
|> Element.row Grid.compact
|
||||
, Element.none
|
||||
)
|
||||
|
||||
|
||||
multiSelect : Model -> (String,Element Msg)
|
||||
multiSelect model =
|
||||
multiSelect : Style Msg -> Model -> ( String, Element Msg, Element Msg )
|
||||
multiSelect style model =
|
||||
let
|
||||
buttonStyle = style.button
|
||||
buttonStyle =
|
||||
style.button
|
||||
in
|
||||
( "Multi Select"
|
||||
, { selected = model.multiSelected
|
||||
, options =
|
||||
[ 1, 2, 42 ]
|
||||
|> List.map (\int ->
|
||||
|> List.map
|
||||
(\int ->
|
||||
{ text = String.fromInt int
|
||||
, icon = Element.none
|
||||
})
|
||||
}
|
||||
)
|
||||
, onSelect = ChangedMultiSelected >> Just
|
||||
}
|
||||
|> Widget.multiSelect
|
||||
|> List.indexedMap
|
||||
(\i ->
|
||||
Widget.selectButton
|
||||
{ buttonStyle
|
||||
| container = buttonStyle.container
|
||||
++ (if i == 0 then
|
||||
Group.left
|
||||
|
||||
else if i == 2 then
|
||||
Group.right
|
||||
|
||||
else
|
||||
Group.center
|
||||
)
|
||||
|> Widget.buttonRow
|
||||
{ list = style.row
|
||||
, button = style.button
|
||||
}
|
||||
)
|
||||
|> Element.row Grid.compact
|
||||
, Element.none
|
||||
)
|
||||
|
||||
collapsable : Model -> (String,Element Msg)
|
||||
collapsable model =
|
||||
( "Collapsable"
|
||||
expansionPanel : Style Msg -> Model -> (String,Element Msg,Element Msg)
|
||||
expansionPanel style model =
|
||||
( "Expansion Panel"
|
||||
, { onToggle = ToggleCollapsable
|
||||
, isCollapsed = model.isCollapsed
|
||||
, label =
|
||||
Element.row (Grid.simple ++ [Element.width<| Element.fill])
|
||||
[ if model.isCollapsed then
|
||||
Icons.chevronRight |> Element.html |> Element.el []
|
||||
|
||||
else
|
||||
Icons.chevronDown |> Element.html |> Element.el []
|
||||
, Element.text <| "Title"
|
||||
]
|
||||
, isExpanded = model.isExpanded
|
||||
, icon = Element.none
|
||||
, text = "Title"
|
||||
, content = Element.text <| "Hello World"
|
||||
}
|
||||
|>Widget.collapsable
|
||||
{ containerColumn = Card.simple ++ Grid.simple
|
||||
++ [ Element.padding 8 ]
|
||||
, button = []
|
||||
}
|
||||
|
||||
|>Widget.expansionPanel style.expansionPanel
|
||||
, Element.none
|
||||
)
|
||||
|
||||
tab : Model -> (String,Element Msg)
|
||||
tab model =
|
||||
|
||||
|
||||
|
||||
tab : Style Msg -> Model -> ( String, Element Msg, Element Msg )
|
||||
tab style model =
|
||||
( "Tab"
|
||||
, Widget.tab
|
||||
{ button = style.tabButton
|
||||
, optionRow = Grid.simple
|
||||
, containerColumn = Grid.compact
|
||||
}
|
||||
, Widget.tab style.tab
|
||||
{ tabs =
|
||||
{ selected = model.tab
|
||||
, options = [ 1, 2, 3 ]
|
||||
|> List.map (\int ->
|
||||
, options =
|
||||
[ 1, 2, 3 ]
|
||||
|> List.map
|
||||
(\int ->
|
||||
{ text = "Tab " ++ (int |> String.fromInt)
|
||||
, icon = Element.none
|
||||
}
|
||||
)
|
||||
, onSelect = ChangedTab >> Just
|
||||
} <|
|
||||
(\selected ->
|
||||
}
|
||||
, content =
|
||||
\selected ->
|
||||
(case selected of
|
||||
Just 0 ->
|
||||
"This is Tab 1"
|
||||
@ -257,51 +232,63 @@ tab model =
|
||||
"Please select a tab"
|
||||
)
|
||||
|> Element.text
|
||||
|> Element.el (Card.small ++ Group.bottom)
|
||||
)
|
||||
}
|
||||
, Element.none
|
||||
)
|
||||
|
||||
modal : (Maybe Part -> msg) -> Model -> (String,Element msg)
|
||||
modal changedSheet model =
|
||||
|
||||
modal : Style msg -> (Maybe Part -> msg) -> Model -> ( String, Element msg,Element msg )
|
||||
modal style changedSheet model =
|
||||
( "Modal"
|
||||
, [ Input.button Button.simple
|
||||
, [ Widget.button style.button
|
||||
{ onPress = Just <| changedSheet <| Just LeftSheet
|
||||
, label = Element.text <| "show left sheet"
|
||||
, text = "show left sheet"
|
||||
, icon = Element.none
|
||||
}
|
||||
, Input.button Button.simple
|
||||
, Widget.button style.button
|
||||
{ onPress = Just <| changedSheet <| Just RightSheet
|
||||
, label = Element.text <| "show right sheet"
|
||||
, text = "show right sheet"
|
||||
, icon = Element.none
|
||||
}
|
||||
] |> Element.column Grid.simple
|
||||
]
|
||||
|> Element.column Grid.simple
|
||||
,Element.none
|
||||
)
|
||||
|
||||
dialog : msg -> Model -> (String,Element msg)
|
||||
dialog showDialog model =
|
||||
|
||||
dialog : Style msg -> msg -> Model -> ( String, Element msg, Element msg )
|
||||
dialog style showDialog model =
|
||||
( "Dialog"
|
||||
, Input.button Button.simple
|
||||
, Widget.button style.button
|
||||
{ onPress = Just showDialog
|
||||
, label = Element.text <| "Show dialog"
|
||||
, text = "Show dialog"
|
||||
, icon = Element.none
|
||||
}
|
||||
, Element.none
|
||||
)
|
||||
|
||||
carousel : Model -> (String,Element Msg)
|
||||
carousel model =
|
||||
|
||||
carousel : Style Msg -> Model -> ( String, Element Msg, Element Msg )
|
||||
carousel style model =
|
||||
( "Carousel"
|
||||
, Widget.carousel
|
||||
{ content = ( Color.cyan, [ Color.yellow, Color.green, Color.red ] |> Array.fromList )
|
||||
, current = model.carousel
|
||||
, label =
|
||||
\c ->
|
||||
[ Element.el [Element.centerY] <|
|
||||
[ Element.el [ Element.centerY ] <|
|
||||
Widget.iconButton style.button
|
||||
{ onPress =
|
||||
model.carousel - 1
|
||||
|> \i ->
|
||||
model.carousel
|
||||
- 1
|
||||
|> (\i ->
|
||||
if i < 0 then
|
||||
Nothing
|
||||
|
||||
else
|
||||
SetCarousel i
|
||||
|> Just
|
||||
)
|
||||
, icon =
|
||||
Icons.chevronLeft
|
||||
|> Element.html
|
||||
@ -319,13 +306,17 @@ carousel model =
|
||||
Element.none
|
||||
, Element.el [ Element.centerY ] <|
|
||||
Widget.iconButton style.button
|
||||
{ onPress = model.carousel + 1
|
||||
|> \i ->
|
||||
{ onPress =
|
||||
model.carousel
|
||||
+ 1
|
||||
|> (\i ->
|
||||
if i >= 4 then
|
||||
Nothing
|
||||
|
||||
else
|
||||
SetCarousel i
|
||||
|> Just
|
||||
)
|
||||
, icon =
|
||||
Icons.chevronRight
|
||||
|> Element.html
|
||||
@ -335,16 +326,20 @@ carousel model =
|
||||
]
|
||||
|> Element.row (Grid.simple ++ [ Element.centerX, Element.width <| Element.shrink ])
|
||||
}
|
||||
, Element.none
|
||||
)
|
||||
|
||||
iconButton : Model -> (String,Element Msg)
|
||||
iconButton model =
|
||||
|
||||
iconButton : Style Msg -> Model -> ( String, Element Msg, Element Msg )
|
||||
iconButton style model =
|
||||
( "Icon Button"
|
||||
, [ [ Widget.button style.primaryButton
|
||||
, [ Widget.button style.primaryButton
|
||||
{ text = "disable me"
|
||||
, icon = Icons.slash |> Element.html |> Element.el [] , onPress =
|
||||
, icon = Icons.slash |> Element.html |> Element.el []
|
||||
, onPress =
|
||||
if model.button then
|
||||
Just <| ToggleButton False
|
||||
|
||||
else
|
||||
Nothing
|
||||
}
|
||||
@ -355,21 +350,54 @@ iconButton model =
|
||||
}
|
||||
]
|
||||
|> Element.row Grid.simple
|
||||
, Element.column Grid.simple
|
||||
[ Element.row Grid.spacedEvenly
|
||||
[ "Button"
|
||||
|> Element.text
|
||||
, Widget.button style.button
|
||||
{ text = "reset button"
|
||||
, icon = Element.none
|
||||
{ text = "reset"
|
||||
, icon = Icons.repeat |> Element.html |> Element.el []
|
||||
, onPress = Just <| ToggleButton True
|
||||
}
|
||||
] |> Element.column Grid.simple
|
||||
]
|
||||
, 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 : Model -> (String,Element Msg)
|
||||
textInput model =
|
||||
|
||||
textInput : Style Msg -> Model -> ( String, Element Msg, Element Msg )
|
||||
textInput style model =
|
||||
( "Chip Text Input"
|
||||
, [ { chips =
|
||||
model.chipTextInput
|
||||
|> Set.toList
|
||||
|> List.map (\string ->
|
||||
|> List.map
|
||||
(\string ->
|
||||
{ icon = Element.none
|
||||
, text = string
|
||||
, onPress =
|
||||
@ -386,45 +414,61 @@ textInput model =
|
||||
|> Widget.textInput style.textInput
|
||||
, model.chipTextInput
|
||||
|> Set.diff
|
||||
(["A","B","C"]
|
||||
([ "A", "B", "C" ]
|
||||
|> Set.fromList
|
||||
)
|
||||
|> Set.toList
|
||||
|> List.map
|
||||
(\string ->
|
||||
Input.button (Button.simple ++ Tag.simple)
|
||||
Widget.button style.textInput.chipButton
|
||||
{ onPress =
|
||||
string
|
||||
|> ToggleTextInputChip
|
||||
|> Just
|
||||
, label = Element.text string
|
||||
, text = string
|
||||
, icon = Element.none
|
||||
}
|
||||
)
|
||||
|> Element.wrappedRow [ Element.spacing 10 ]
|
||||
] |> Element.column Grid.simple
|
||||
]
|
||||
|> Element.column Grid.simple
|
||||
, Element.none
|
||||
)
|
||||
|
||||
|
||||
view :
|
||||
Theme ->
|
||||
{ msgMapper : Msg -> msg
|
||||
, showDialog : msg
|
||||
, changedSheet : Maybe Part -> msg
|
||||
} -> Model
|
||||
-> { title : String
|
||||
, description : String
|
||||
, items : List (String,Element msg)
|
||||
}
|
||||
view { msgMapper, showDialog, changedSheet } model =
|
||||
-> Model
|
||||
->
|
||||
{ title : String
|
||||
, description : String
|
||||
, items : List ( String, Element msg, Element msg )
|
||||
}
|
||||
view theme { msgMapper, showDialog, changedSheet } model =
|
||||
let
|
||||
style = Theme.toStyle theme
|
||||
|
||||
map (a,b,c) =
|
||||
( a
|
||||
, b |> Element.map msgMapper
|
||||
, c |> Element.map msgMapper
|
||||
)
|
||||
in
|
||||
{ title = "Stateless Views"
|
||||
, description = "Stateless views are simple functions that view some content. No wiring required."
|
||||
, items =
|
||||
[ iconButton model |> Tuple.mapSecond (Element.map msgMapper)
|
||||
, select model |> Tuple.mapSecond (Element.map msgMapper)
|
||||
, multiSelect model |> Tuple.mapSecond (Element.map msgMapper)
|
||||
, collapsable model |> Tuple.mapSecond (Element.map msgMapper)
|
||||
, modal changedSheet model
|
||||
, carousel model |> Tuple.mapSecond (Element.map msgMapper)
|
||||
, tab model |> Tuple.mapSecond (Element.map msgMapper)
|
||||
, dialog showDialog model
|
||||
, textInput model |> Tuple.mapSecond (Element.map msgMapper)
|
||||
[ iconButton style model |> map
|
||||
, select style model |> map
|
||||
, multiSelect style model |> map
|
||||
, expansionPanel style model |> map
|
||||
, modal style changedSheet model
|
||||
, carousel style model |> map
|
||||
, tab style model |> map
|
||||
, dialog style showDialog model
|
||||
, textInput style model |> map
|
||||
]
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ import Widget.Style exposing (ButtonStyle)
|
||||
|
||||
type alias Button msg =
|
||||
{ text : String
|
||||
, icon : Element Never
|
||||
, onPress : Maybe msg
|
||||
, icon : Element Never
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ iconButton style { onPress, text, icon } =
|
||||
Input.button
|
||||
(style.container
|
||||
++ (if onPress == Nothing then
|
||||
style.disabled
|
||||
style.ifDisabled
|
||||
|
||||
else
|
||||
[]
|
||||
@ -60,7 +60,7 @@ button style { onPress, text, icon } =
|
||||
Input.button
|
||||
(style.container
|
||||
++ (if onPress == Nothing then
|
||||
style.disabled
|
||||
style.ifDisabled
|
||||
|
||||
else
|
||||
[]
|
||||
@ -68,7 +68,7 @@ button style { onPress, text, icon } =
|
||||
)
|
||||
{ onPress = onPress
|
||||
, label =
|
||||
Element.row style.label
|
||||
Element.row style.labelRow
|
||||
[ icon |> Element.map never
|
||||
, Element.text text
|
||||
]
|
||||
|
@ -51,10 +51,10 @@ dialog style { title, body, accept, dismiss } =
|
||||
Nothing
|
||||
, content =
|
||||
Element.column
|
||||
(style.containerColumn
|
||||
++ [ Element.centerX
|
||||
([ Element.centerX
|
||||
, Element.centerY
|
||||
]
|
||||
++ style.containerColumn
|
||||
)
|
||||
[ title
|
||||
|> Maybe.map
|
||||
@ -64,22 +64,22 @@ dialog style { title, body, accept, dismiss } =
|
||||
|> Maybe.withDefault Element.none
|
||||
, body
|
||||
, Element.row
|
||||
(style.buttonRow
|
||||
++ [ Element.alignRight
|
||||
([ Element.alignRight
|
||||
, Element.width <| Element.shrink
|
||||
]
|
||||
++ style.buttonRow
|
||||
)
|
||||
(case ( accept, dismiss ) of
|
||||
( Just acceptButton, Nothing ) ->
|
||||
acceptButton
|
||||
|> Button.textButton style.accept
|
||||
|> Button.textButton style.acceptButton
|
||||
|> List.singleton
|
||||
|
||||
( Just acceptButton, Just dismissButton ) ->
|
||||
[ dismissButton
|
||||
|> Button.textButton style.dismiss
|
||||
|> Button.textButton style.dismissButton
|
||||
, acceptButton
|
||||
|> Button.textButton style.accept
|
||||
|> Button.textButton style.acceptButton
|
||||
]
|
||||
|
||||
_ ->
|
||||
|
46
src/Internal/ExpansionPanel.elm
Normal file
46
src/Internal/ExpansionPanel.elm
Normal file
@ -0,0 +1,46 @@
|
||||
module Internal.ExpansionPanel exposing (ExpansionPanel, expansionPanel)
|
||||
|
||||
{-| Part of Material Design Lists
|
||||
-}
|
||||
|
||||
import Element exposing (Element)
|
||||
import Element.Events as Events
|
||||
import Widget.Style exposing (ExpansionPanelStyle)
|
||||
|
||||
|
||||
type alias ExpansionPanel msg =
|
||||
{ onToggle : Bool -> msg
|
||||
, icon : Element Never
|
||||
, text : String
|
||||
, content : Element msg
|
||||
, isExpanded : Bool
|
||||
}
|
||||
|
||||
|
||||
expansionPanel :
|
||||
ExpansionPanelStyle msg
|
||||
-> ExpansionPanel msg
|
||||
-> Element msg
|
||||
expansionPanel style model =
|
||||
Element.column style.containerColumn <|
|
||||
[ Element.row
|
||||
((Events.onClick <| model.onToggle <| not model.isExpanded)
|
||||
:: style.panelRow
|
||||
)
|
||||
[ Element.row style.labelRow
|
||||
[ model.icon |> Element.map never
|
||||
, model.text |> Element.text
|
||||
]
|
||||
, Element.map never <|
|
||||
if model.isExpanded then
|
||||
style.collapseIcon
|
||||
|
||||
else
|
||||
style.expandIcon
|
||||
]
|
||||
, if model.isExpanded then
|
||||
Element.el style.content <| model.content
|
||||
|
||||
else
|
||||
Element.none
|
||||
]
|
108
src/Internal/List.elm
Normal file
108
src/Internal/List.elm
Normal file
@ -0,0 +1,108 @@
|
||||
module Internal.List exposing (buttonColumn, buttonRow, column, row)
|
||||
|
||||
import Element exposing (Attribute, Element)
|
||||
import Internal.Button exposing (Button)
|
||||
import Internal.Select as Select
|
||||
import Widget.Style exposing (ButtonStyle, ColumnStyle, RowStyle)
|
||||
|
||||
|
||||
internal :
|
||||
{ list
|
||||
| element : List (Attribute msg)
|
||||
, ifFirst : List (Attribute msg)
|
||||
, ifLast : List (Attribute msg)
|
||||
, ifCenter : List (Attribute msg)
|
||||
}
|
||||
-> List (Element msg)
|
||||
-> List (Element msg)
|
||||
internal style list =
|
||||
list
|
||||
|> List.indexedMap
|
||||
(\i ->
|
||||
Element.el <|
|
||||
style.element
|
||||
++ (if List.length list == 1 then
|
||||
[]
|
||||
|
||||
else if i == 0 then
|
||||
style.ifFirst
|
||||
|
||||
else if i == (List.length list - 1) then
|
||||
style.ifLast
|
||||
|
||||
else
|
||||
style.ifCenter
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
row : RowStyle msg -> List (Element msg) -> Element msg
|
||||
row style =
|
||||
internal style >> Element.row style.containerRow
|
||||
|
||||
|
||||
column : ColumnStyle msg -> List (Element msg) -> Element msg
|
||||
column style =
|
||||
internal style >> Element.column style.containerColumn
|
||||
|
||||
|
||||
internalButton :
|
||||
{ list :
|
||||
{ list
|
||||
| element : List (Attribute msg)
|
||||
, ifFirst : List (Attribute msg)
|
||||
, ifLast : List (Attribute msg)
|
||||
, ifCenter : List (Attribute msg)
|
||||
}
|
||||
, button : ButtonStyle msg
|
||||
}
|
||||
-> List ( Bool, Button msg )
|
||||
-> List (Element msg)
|
||||
internalButton style list =
|
||||
list
|
||||
|> List.indexedMap
|
||||
(\i ->
|
||||
Select.selectButton
|
||||
{ container =
|
||||
style.button.container
|
||||
++ style.list.element
|
||||
++ (if List.length list == 1 then
|
||||
[]
|
||||
|
||||
else if i == 0 then
|
||||
style.list.ifFirst
|
||||
|
||||
else if i == (List.length list - 1) then
|
||||
style.list.ifLast
|
||||
|
||||
else
|
||||
style.list.ifCenter
|
||||
)
|
||||
, labelRow =
|
||||
style.button.labelRow
|
||||
, ifDisabled =
|
||||
style.button.ifDisabled
|
||||
, ifActive =
|
||||
style.button.ifActive
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
buttonRow :
|
||||
{ list : RowStyle msg
|
||||
, button : ButtonStyle msg
|
||||
}
|
||||
-> List ( Bool, Button msg )
|
||||
-> Element msg
|
||||
buttonRow style =
|
||||
internalButton style >> Element.row style.list.containerRow
|
||||
|
||||
|
||||
buttonColumn :
|
||||
{ list : ColumnStyle msg
|
||||
, button : ButtonStyle msg
|
||||
}
|
||||
-> List ( Bool, Button msg )
|
||||
-> Element msg
|
||||
buttonColumn style =
|
||||
internalButton style >> Element.column style.list.containerColumn
|
@ -1,4 +1,4 @@
|
||||
module Internal.Select exposing (multiSelect, select, selectButton)
|
||||
module Internal.Select exposing (MultiSelect, Select, multiSelect, select, selectButton)
|
||||
|
||||
import Element exposing (Element)
|
||||
import Internal.Button as Button exposing (Button)
|
||||
@ -39,7 +39,7 @@ selectButton style ( selected, b ) =
|
||||
| container =
|
||||
style.container
|
||||
++ (if selected then
|
||||
style.active
|
||||
style.ifActive
|
||||
|
||||
else
|
||||
[]
|
||||
|
24
src/Internal/Tab.elm
Normal file
24
src/Internal/Tab.elm
Normal file
@ -0,0 +1,24 @@
|
||||
module Internal.Tab exposing (Tab, tab)
|
||||
|
||||
import Element exposing (Element)
|
||||
import Internal.Select as Select exposing (Select)
|
||||
import Widget.Style exposing (TabStyle)
|
||||
|
||||
|
||||
type alias Tab msg =
|
||||
{ tabs : Select msg
|
||||
, content : Maybe Int -> Element msg
|
||||
}
|
||||
|
||||
|
||||
tab : TabStyle msg -> Tab msg -> Element msg
|
||||
tab style { tabs, content } =
|
||||
[ tabs
|
||||
|> Select.select
|
||||
|> List.map (Select.selectButton style.button)
|
||||
|> Element.row style.optionRow
|
||||
, tabs.selected
|
||||
|> content
|
||||
|> Element.el style.content
|
||||
]
|
||||
|> Element.column style.containerColumn
|
34
src/Internal/TextInput.elm
Normal file
34
src/Internal/TextInput.elm
Normal file
@ -0,0 +1,34 @@
|
||||
module Internal.TextInput exposing (TextInput, textInput)
|
||||
|
||||
import Element exposing (Element)
|
||||
import Element.Input as Input exposing (Placeholder)
|
||||
import Internal.Button as Button exposing (Button)
|
||||
import Widget.Style exposing (TextInputStyle)
|
||||
|
||||
|
||||
type alias TextInput msg =
|
||||
{ chips : List (Button msg)
|
||||
, text : String
|
||||
, placeholder : Maybe (Placeholder msg)
|
||||
, label : String
|
||||
, onChange : String -> msg
|
||||
}
|
||||
|
||||
|
||||
textInput : TextInputStyle msg -> TextInput msg -> Element msg
|
||||
textInput style { chips, placeholder, label, text, onChange } =
|
||||
Element.row style.containerRow
|
||||
[ if chips |> List.isEmpty then
|
||||
Element.none
|
||||
|
||||
else
|
||||
chips
|
||||
|> List.map (Button.button style.chipButton)
|
||||
|> Element.row style.chipsRow
|
||||
, Input.text style.input
|
||||
{ onChange = onChange
|
||||
, text = text
|
||||
, placeholder = placeholder
|
||||
, label = Input.labelHidden label
|
||||
}
|
||||
]
|
149
src/Widget.elm
149
src/Widget.elm
@ -2,7 +2,9 @@ module Widget exposing
|
||||
( Button, TextButton, iconButton, textButton, button
|
||||
, Select, MultiSelect, selectButton, select, multiSelect
|
||||
, Dialog, modal, dialog
|
||||
, TextInputStyle, textInput, collapsable, carousel, tab
|
||||
, ExpansionPanel, expansionPanel
|
||||
, TextInputStyle, textInput, carousel, tab
|
||||
, Tab, buttonColumn, buttonRow, column, row
|
||||
)
|
||||
|
||||
{-| This module contains functions for displaying data.
|
||||
@ -23,20 +25,28 @@ module Widget exposing
|
||||
@docs Dialog, modal, dialog
|
||||
|
||||
|
||||
# ExpansionPanel
|
||||
|
||||
@docs ExpansionPanel, expansionPanel
|
||||
|
||||
|
||||
# Other Widgets
|
||||
|
||||
@docs TextInputStyle, textInput, collapsable, carousel, tab
|
||||
@docs TextInputStyle, textInput, carousel, tab
|
||||
|
||||
-}
|
||||
|
||||
import Array exposing (Array)
|
||||
import Element exposing (Attribute, Element)
|
||||
import Element.Input as Input exposing (Placeholder)
|
||||
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.TextInput as TextInput
|
||||
import Set exposing (Set)
|
||||
import Widget.Style exposing (ButtonStyle, DialogStyle)
|
||||
import Widget.Style exposing (ButtonStyle, ColumnStyle, DialogStyle, ExpansionPanelStyle, RowStyle, TabStyle)
|
||||
|
||||
|
||||
|
||||
@ -216,13 +226,44 @@ dialog =
|
||||
|
||||
|
||||
{----------------------------------------------------------
|
||||
- OTHER STATELESS WIDGETS
|
||||
- DIALOG
|
||||
----------------------------------------------------------}
|
||||
|
||||
|
||||
type alias ExpansionPanel msg =
|
||||
{ onToggle : Bool -> msg
|
||||
, icon : Element Never
|
||||
, text : String
|
||||
, expandIcon : Element Never
|
||||
, collapseIcon : Element Never
|
||||
, content : Element msg
|
||||
, isExpanded : Bool
|
||||
}
|
||||
|
||||
|
||||
expansionPanel :
|
||||
ExpansionPanelStyle msg
|
||||
->
|
||||
{ onToggle : Bool -> msg
|
||||
, icon : Element Never
|
||||
, text : String
|
||||
, content : Element msg
|
||||
, isExpanded : Bool
|
||||
}
|
||||
-> Element msg
|
||||
expansionPanel =
|
||||
ExpansionPanel.expansionPanel
|
||||
|
||||
|
||||
|
||||
{----------------------------------------------------------
|
||||
- TEXT INPUT
|
||||
----------------------------------------------------------}
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias TextInputStyle msg =
|
||||
{ chip : ButtonStyle msg
|
||||
{ chipButton : ButtonStyle msg
|
||||
, containerRow : List (Attribute msg)
|
||||
, chipsRow : List (Attribute msg)
|
||||
, input : List (Attribute msg)
|
||||
@ -240,65 +281,75 @@ textInput :
|
||||
, onChange : String -> msg
|
||||
}
|
||||
-> Element msg
|
||||
textInput style { chips, placeholder, label, text, onChange } =
|
||||
Element.row style.containerRow
|
||||
[ chips
|
||||
|> List.map (Button.button style.chip)
|
||||
|> Element.row style.chipsRow
|
||||
, Input.text style.input
|
||||
{ onChange = onChange
|
||||
, text = text
|
||||
, placeholder = placeholder
|
||||
, label = Input.labelHidden label
|
||||
}
|
||||
]
|
||||
textInput =
|
||||
TextInput.textInput
|
||||
|
||||
|
||||
{-| Some collapsable content.
|
||||
-}
|
||||
collapsable :
|
||||
{ containerColumn : List (Attribute msg)
|
||||
, button : List (Attribute msg)
|
||||
}
|
||||
->
|
||||
{ onToggle : Bool -> msg
|
||||
, isCollapsed : Bool
|
||||
, label : Element msg
|
||||
, content : Element msg
|
||||
|
||||
{----------------------------------------------------------
|
||||
- LIST
|
||||
----------------------------------------------------------}
|
||||
|
||||
|
||||
row : RowStyle msg -> List (Element msg) -> Element msg
|
||||
row =
|
||||
List.row
|
||||
|
||||
|
||||
column : ColumnStyle msg -> List (Element msg) -> Element msg
|
||||
column =
|
||||
List.column
|
||||
|
||||
|
||||
buttonRow :
|
||||
{ list : RowStyle msg
|
||||
, button : ButtonStyle msg
|
||||
}
|
||||
-> List ( Bool, Button msg )
|
||||
-> Element msg
|
||||
collapsable style { onToggle, isCollapsed, label, content } =
|
||||
Element.column style.containerColumn <|
|
||||
[ Input.button style.button
|
||||
{ onPress = Just <| onToggle <| not isCollapsed
|
||||
, label = label
|
||||
}
|
||||
]
|
||||
++ (if isCollapsed then
|
||||
[]
|
||||
buttonRow =
|
||||
List.buttonRow
|
||||
|
||||
else
|
||||
[ content ]
|
||||
)
|
||||
|
||||
buttonColumn :
|
||||
{ list : ColumnStyle msg
|
||||
, button : ButtonStyle msg
|
||||
}
|
||||
-> List ( Bool, Button msg )
|
||||
-> Element msg
|
||||
buttonColumn =
|
||||
List.buttonColumn
|
||||
|
||||
|
||||
|
||||
{----------------------------------------------------------
|
||||
- OTHER STATELESS WIDGETS
|
||||
----------------------------------------------------------}
|
||||
|
||||
|
||||
type alias Tab msg =
|
||||
{ tabs : Select msg
|
||||
, content : Maybe Int -> Element msg
|
||||
}
|
||||
|
||||
|
||||
{-| Displayes a list of contents in a tab
|
||||
-}
|
||||
tab :
|
||||
{ button : ButtonStyle msg
|
||||
, optionRow : List (Attribute msg)
|
||||
, containerColumn : List (Attribute msg)
|
||||
TabStyle msg
|
||||
->
|
||||
{ tabs : Select msg
|
||||
, content : Maybe Int -> Element msg
|
||||
}
|
||||
-> Select msg
|
||||
-> (Maybe Int -> Element msg)
|
||||
-> Element msg
|
||||
tab style options content =
|
||||
[ options
|
||||
tab style { tabs, content } =
|
||||
[ tabs
|
||||
|> select
|
||||
|> List.map (selectButton style.button)
|
||||
|> Element.row style.optionRow
|
||||
, options.selected
|
||||
, tabs.selected
|
||||
|> content
|
||||
|> Element.el style.content
|
||||
]
|
||||
|> Element.column style.containerColumn
|
||||
|
||||
|
@ -1,108 +0,0 @@
|
||||
module Widget.FilterMultiSelect exposing (Model, Msg(..), init, update, viewInput, viewOptions)
|
||||
|
||||
{-|
|
||||
|
||||
@docs Model, Msg, init, update, viewInput, viewOptions
|
||||
|
||||
-}
|
||||
|
||||
import Element.Input exposing (Placeholder)
|
||||
import Set exposing (Set)
|
||||
import Widget exposing (Button)
|
||||
|
||||
|
||||
{-| The Model containing the raw value, the selected value and all the possible options.
|
||||
-}
|
||||
type alias Model =
|
||||
{ raw : String
|
||||
, selected : Set String
|
||||
, options : Set String
|
||||
}
|
||||
|
||||
|
||||
{-| The Msg is exposed by design. You can unselect by sending `Selected Nothing`.
|
||||
-}
|
||||
type Msg
|
||||
= ChangedRaw String
|
||||
| ToggleSelected String
|
||||
|
||||
|
||||
{-| The initial state contains the set of possible options.
|
||||
-}
|
||||
init : Set String -> Model
|
||||
init options =
|
||||
{ raw = ""
|
||||
, selected = Set.empty
|
||||
, options = options
|
||||
}
|
||||
|
||||
|
||||
{-| Updates the Model
|
||||
-}
|
||||
update : Msg -> Model -> Model
|
||||
update msg model =
|
||||
case msg of
|
||||
ChangedRaw string ->
|
||||
{ model
|
||||
| raw = string
|
||||
}
|
||||
|
||||
ToggleSelected string ->
|
||||
if model.selected |> Set.member string then
|
||||
{ model
|
||||
| selected = model.selected |> Set.remove string
|
||||
}
|
||||
|
||||
else
|
||||
{ model
|
||||
| selected = model.selected |> Set.insert string
|
||||
, raw = ""
|
||||
}
|
||||
|
||||
|
||||
{-| A wrapper around Input.text.
|
||||
-}
|
||||
viewInput :
|
||||
Model
|
||||
->
|
||||
{ msgMapper : Msg -> msg
|
||||
, placeholder : Maybe (Placeholder msg)
|
||||
, label : String
|
||||
, toChip : String -> Button msg
|
||||
}
|
||||
->
|
||||
{ chips : List (Button msg)
|
||||
, text : String
|
||||
, placeholder : Maybe (Placeholder msg)
|
||||
, label : String
|
||||
, onChange : String -> msg
|
||||
}
|
||||
viewInput model { msgMapper, placeholder, label, toChip } =
|
||||
{ chips =
|
||||
model.selected
|
||||
|> Set.toList
|
||||
|> List.map toChip
|
||||
, text = model.raw
|
||||
, placeholder = placeholder
|
||||
, label = label
|
||||
, onChange = ChangedRaw >> msgMapper
|
||||
}
|
||||
|
||||
|
||||
{-| Returns a List of all options that matches the filter.
|
||||
-}
|
||||
viewOptions : Model -> List String
|
||||
viewOptions { raw, options, selected } =
|
||||
if raw == "" then
|
||||
[]
|
||||
|
||||
else
|
||||
options
|
||||
|> Set.filter (String.toUpper >> String.contains (raw |> String.toUpper))
|
||||
|> Set.filter
|
||||
(\string ->
|
||||
selected
|
||||
|> Set.member string
|
||||
|> not
|
||||
)
|
||||
|> Set.toList
|
@ -1,93 +0,0 @@
|
||||
module Widget.FilterSelect exposing (Model, Msg(..), init, update, viewInput, viewOptions)
|
||||
|
||||
{-|
|
||||
|
||||
@docs Model, Msg, init, update, viewInput, viewOptions
|
||||
|
||||
-}
|
||||
|
||||
import Element exposing (Attribute, Element)
|
||||
import Element.Input as Input exposing (Placeholder)
|
||||
import Set exposing (Set)
|
||||
|
||||
|
||||
{-| The Model containing the raw value, the selected value and all the possible options.
|
||||
-}
|
||||
type alias Model =
|
||||
{ raw : String
|
||||
, selected : Maybe String
|
||||
, options : Set String
|
||||
}
|
||||
|
||||
|
||||
{-| The Msg is exposed by design. You can unselect by sending `Selected Nothing`.
|
||||
-}
|
||||
type Msg
|
||||
= ChangedRaw String
|
||||
| Selected (Maybe String)
|
||||
|
||||
|
||||
{-| The initial state contains the set of possible options.
|
||||
-}
|
||||
init : Set String -> Model
|
||||
init options =
|
||||
{ raw = ""
|
||||
, selected = Nothing
|
||||
, options = options
|
||||
}
|
||||
|
||||
|
||||
{-| Updates the Model
|
||||
-}
|
||||
update : Msg -> Model -> Model
|
||||
update msg model =
|
||||
case msg of
|
||||
ChangedRaw string ->
|
||||
{ model
|
||||
| raw = string
|
||||
}
|
||||
|
||||
Selected maybe ->
|
||||
{ model
|
||||
| selected = maybe
|
||||
}
|
||||
|> (case maybe of
|
||||
Just string ->
|
||||
\m -> { m | raw = string }
|
||||
|
||||
Nothing ->
|
||||
identity
|
||||
)
|
||||
|
||||
|
||||
{-| A wrapper around Input.text.
|
||||
-}
|
||||
viewInput :
|
||||
List (Attribute msg)
|
||||
-> Model
|
||||
->
|
||||
{ msgMapper : Msg -> msg
|
||||
, placeholder : Maybe (Placeholder msg)
|
||||
, label : String
|
||||
}
|
||||
-> Element msg
|
||||
viewInput attributes model { msgMapper, placeholder, label } =
|
||||
Input.text attributes
|
||||
{ onChange = ChangedRaw >> msgMapper
|
||||
, text = model.raw
|
||||
, placeholder = placeholder
|
||||
, label = Input.labelHidden label
|
||||
}
|
||||
|
||||
|
||||
{-| Returns a List of all options that matches the filter.
|
||||
-}
|
||||
viewOptions : Model -> List String
|
||||
viewOptions { raw, options } =
|
||||
if raw == "" then
|
||||
[]
|
||||
|
||||
else
|
||||
options
|
||||
|> Set.filter (String.toUpper >> String.contains (raw |> String.toUpper))
|
||||
|> Set.toList
|
@ -18,10 +18,10 @@ module Widget.Snackbar exposing
|
||||
|
||||
-}
|
||||
|
||||
import Element exposing (Attribute, Element)
|
||||
import Element exposing (Element)
|
||||
import Queue exposing (Queue)
|
||||
import Widget exposing (TextButton)
|
||||
import Widget.Style exposing (ButtonStyle)
|
||||
import Widget.Style exposing (SnackbarStyle)
|
||||
|
||||
|
||||
type alias Message msg =
|
||||
@ -105,10 +105,7 @@ current model =
|
||||
|
||||
|
||||
view :
|
||||
{ row : List (Attribute msg)
|
||||
, text : List (Attribute msg)
|
||||
, button : ButtonStyle msg
|
||||
}
|
||||
SnackbarStyle msg
|
||||
-> (a -> Message msg)
|
||||
-> Model a
|
||||
-> Maybe (Element msg)
|
||||
@ -127,6 +124,6 @@ view style toMessage model =
|
||||
(Widget.textButton style.button)
|
||||
|> Maybe.withDefault Element.none
|
||||
]
|
||||
|> Element.row style.row
|
||||
|> Element.row style.containerRow
|
||||
)
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
module Widget.Style exposing (ButtonStyle, DialogStyle, Style)
|
||||
module Widget.Style exposing (ButtonStyle, ColumnStyle, DialogStyle, ExpansionPanelStyle, RowStyle, SnackbarStyle, Style, TabStyle, TextInputStyle)
|
||||
|
||||
import Element exposing (Attribute, Element)
|
||||
import Html exposing (Html)
|
||||
@ -6,9 +6,9 @@ import Html exposing (Html)
|
||||
|
||||
type alias ButtonStyle msg =
|
||||
{ container : List (Attribute msg)
|
||||
, disabled : List (Attribute msg)
|
||||
, label : List (Attribute msg)
|
||||
, active : List (Attribute msg)
|
||||
, labelRow : List (Attribute msg)
|
||||
, ifDisabled : List (Attribute msg)
|
||||
, ifActive : List (Attribute msg)
|
||||
}
|
||||
|
||||
|
||||
@ -16,18 +16,65 @@ type alias DialogStyle msg =
|
||||
{ containerColumn : List (Attribute msg)
|
||||
, title : List (Attribute msg)
|
||||
, buttonRow : List (Attribute msg)
|
||||
, accept : ButtonStyle msg
|
||||
, dismiss : ButtonStyle msg
|
||||
, acceptButton : ButtonStyle msg
|
||||
, dismissButton : ButtonStyle msg
|
||||
}
|
||||
|
||||
|
||||
type alias ExpansionPanelStyle msg =
|
||||
{ containerColumn : List (Attribute msg)
|
||||
, panelRow : List (Attribute msg)
|
||||
, labelRow : List (Attribute msg)
|
||||
, content : List (Attribute msg)
|
||||
, expandIcon : Element Never
|
||||
, collapseIcon : Element Never
|
||||
}
|
||||
|
||||
|
||||
type alias SnackbarStyle msg =
|
||||
{ containerRow : List (Attribute msg)
|
||||
, text : List (Attribute msg)
|
||||
, button : ButtonStyle msg
|
||||
}
|
||||
|
||||
|
||||
type alias TextInputStyle msg =
|
||||
{ chipButton : ButtonStyle msg
|
||||
, containerRow : List (Attribute msg)
|
||||
, chipsRow : List (Attribute msg)
|
||||
, input : List (Attribute msg)
|
||||
}
|
||||
|
||||
|
||||
type alias TabStyle msg =
|
||||
{ button : ButtonStyle msg
|
||||
, optionRow : List (Attribute msg)
|
||||
, containerColumn : List (Attribute msg)
|
||||
, content : List (Attribute msg)
|
||||
}
|
||||
|
||||
|
||||
type alias RowStyle msg =
|
||||
{ containerRow : List (Attribute msg)
|
||||
, element : List (Attribute msg)
|
||||
, ifFirst : List (Attribute msg)
|
||||
, ifLast : List (Attribute msg)
|
||||
, ifCenter : List (Attribute msg)
|
||||
}
|
||||
|
||||
|
||||
type alias ColumnStyle msg =
|
||||
{ containerColumn : List (Attribute msg)
|
||||
, element : List (Attribute msg)
|
||||
, ifFirst : List (Attribute msg)
|
||||
, ifLast : List (Attribute msg)
|
||||
, ifCenter : List (Attribute msg)
|
||||
}
|
||||
|
||||
|
||||
type alias Style style msg =
|
||||
{ style
|
||||
| snackbar :
|
||||
{ row : List (Attribute msg)
|
||||
, text : List (Attribute msg)
|
||||
, button : ButtonStyle msg
|
||||
}
|
||||
| snackbar : SnackbarStyle msg
|
||||
, layout : List (Attribute msg) -> Element msg -> Html msg
|
||||
, header : List (Attribute msg)
|
||||
, sheet : List (Attribute msg)
|
||||
|
@ -1,161 +0,0 @@
|
||||
module Widget.ValidatedInput exposing
|
||||
( Model, Msg, init, update, view
|
||||
, getError, getRaw, getValue
|
||||
)
|
||||
|
||||
{-| The validated Input is a wrapper around `Input.text`.
|
||||
They can validate the input and return an error if nessarry.
|
||||
|
||||
|
||||
# Basics
|
||||
|
||||
@docs Model, Msg, init, update, view
|
||||
|
||||
|
||||
# Access the Model
|
||||
|
||||
@docs getError, getRaw, getValue
|
||||
|
||||
-}
|
||||
|
||||
import Element exposing (Attribute, Element)
|
||||
import Element.Events as Events
|
||||
import Element.Input as Input exposing (Placeholder)
|
||||
|
||||
|
||||
{-| -}
|
||||
type Model err a
|
||||
= Model
|
||||
{ raw : Maybe String
|
||||
, value : a
|
||||
, err : Maybe err
|
||||
, validator : String -> Result err a
|
||||
, toString : a -> String
|
||||
}
|
||||
|
||||
|
||||
{-| returns the raw value (the value that the user currently sees)
|
||||
-}
|
||||
getRaw : Model err a -> String
|
||||
getRaw (Model { raw, value, toString }) =
|
||||
case raw of
|
||||
Just string ->
|
||||
string
|
||||
|
||||
Nothing ->
|
||||
value |> toString
|
||||
|
||||
|
||||
{-| returns the value (the value that has been last successfully validated)
|
||||
-}
|
||||
getValue : Model err a -> a
|
||||
getValue (Model { value }) =
|
||||
value
|
||||
|
||||
|
||||
{-| returns the error (if one exists)
|
||||
-}
|
||||
getError : Model err a -> Maybe err
|
||||
getError (Model { err }) =
|
||||
err
|
||||
|
||||
|
||||
{-| -}
|
||||
type Msg
|
||||
= ChangedRaw String
|
||||
| LostFocus
|
||||
| StartEditing
|
||||
|
||||
|
||||
{-| The initial state contains
|
||||
|
||||
- `value`: starting value
|
||||
- `validator`: a vaidation function (a decoder)
|
||||
- `toString`: a function that returns a string representation
|
||||
|
||||
-}
|
||||
init : { value : a, validator : String -> Result err a, toString : a -> String } -> Model err a
|
||||
init { validator, toString, value } =
|
||||
Model
|
||||
{ raw = Nothing
|
||||
, value = value
|
||||
, err = Nothing
|
||||
, validator = validator
|
||||
, toString = toString
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
update : Msg -> Model err a -> Model err a
|
||||
update msg (Model model) =
|
||||
case msg of
|
||||
StartEditing ->
|
||||
Model
|
||||
{ model
|
||||
| raw = model.value |> model.toString |> Just
|
||||
}
|
||||
|
||||
ChangedRaw string ->
|
||||
Model
|
||||
{ model
|
||||
| raw = Just string
|
||||
, err = Nothing
|
||||
}
|
||||
|
||||
LostFocus ->
|
||||
case model.raw of
|
||||
Just string ->
|
||||
case model.validator string of
|
||||
Ok value ->
|
||||
Model
|
||||
{ model
|
||||
| value = value
|
||||
, raw = Nothing
|
||||
, err = Nothing
|
||||
}
|
||||
|
||||
Err err ->
|
||||
Model
|
||||
{ model
|
||||
| raw = Nothing
|
||||
, err = Just err
|
||||
}
|
||||
|
||||
Nothing ->
|
||||
Model model
|
||||
|
||||
|
||||
{-| the view function, the parameters include
|
||||
|
||||
- `msgMapper`: A function wrapping the `Msg` into a `msg`
|
||||
- `placeholder`: See Element.text for more information
|
||||
- `label`: The (hidden) label of the input (needed for screen readers)
|
||||
- `readOnly`: a representation of the validated value
|
||||
(clicking on the element will turn on edit mode)
|
||||
|
||||
-}
|
||||
view :
|
||||
List (Attribute msg)
|
||||
-> Model err a
|
||||
->
|
||||
{ msgMapper : Msg -> msg
|
||||
, placeholder : Maybe (Placeholder msg)
|
||||
, label : String
|
||||
, readOnly : a -> Element msg
|
||||
}
|
||||
-> Element msg
|
||||
view attributes (Model model) { msgMapper, placeholder, label, readOnly } =
|
||||
case model.raw of
|
||||
Just string ->
|
||||
Input.text (attributes ++ [ Events.onLoseFocus <| msgMapper <| LostFocus ])
|
||||
{ onChange = ChangedRaw >> msgMapper
|
||||
, text = string
|
||||
, placeholder = placeholder
|
||||
, label = Input.labelHidden label
|
||||
}
|
||||
|
||||
Nothing ->
|
||||
Input.button []
|
||||
{ onPress = Just (StartEditing |> msgMapper)
|
||||
, label = model.value |> readOnly
|
||||
}
|
Loading…
Reference in New Issue
Block a user