Last touches

This commit is contained in:
Unknown 2020-03-21 19:19:02 +01:00
parent a5d03e6d26
commit 3a7eaff6c6
7 changed files with 453 additions and 337 deletions

View File

@ -1,9 +1,9 @@
module Component exposing (Model,Msg(..),init,update,view) module Component exposing (Model, Msg(..), init, update, view)
import Browser import Browser
import Element exposing (Element,Color) import Element exposing (Color, Element)
import Element.Input as Input
import Element.Background as Background import Element.Background as Background
import Element.Input as Input
import Framework import Framework
import Framework.Button as Button import Framework.Button as Button
import Framework.Card as Card import Framework.Card as Card
@ -13,63 +13,64 @@ import Framework.Group as Group
import Framework.Heading as Heading import Framework.Heading as Heading
import Framework.Input as Input import Framework.Input as Input
import Framework.Tag as Tag import Framework.Tag as Tag
import Heroicons.Solid as Heroicons
import Html exposing (Html) import Html exposing (Html)
import Html.Attributes as Attributes import Html.Attributes as Attributes
import Set exposing (Set) import Set exposing (Set)
import Time
import Widget import Widget
import Widget.FilterSelect as FilterSelect import Widget.FilterSelect as FilterSelect
import Widget.ScrollingNav as ScrollingNav import Widget.ScrollingNav as ScrollingNav
import Widget.ValidatedInput as ValidatedInput
import Widget.Snackbar as Snackbar import Widget.Snackbar as Snackbar
import Time import Widget.ValidatedInput as ValidatedInput
import Heroicons.Solid as Heroicons
type alias Model = type alias Model =
{ filterSelect : FilterSelect.Model { filterSelect : FilterSelect.Model
, validatedInput : ValidatedInput.Model () (( String, String )) , validatedInput : ValidatedInput.Model () ( String, String )
} }
type Msg type Msg
= FilterSelectSpecific FilterSelect.Msg = FilterSelectSpecific FilterSelect.Msg
| ValidatedInputSpecific ValidatedInput.Msg | ValidatedInputSpecific ValidatedInput.Msg
init : Model init : Model
init = init =
{ filterSelect = { filterSelect =
[ "Apple" [ "Apple"
, "Kiwi" , "Kiwi"
, "Strawberry" , "Strawberry"
, "Pineapple" , "Pineapple"
, "Mango" , "Mango"
, "Grapes" , "Grapes"
, "Watermelon" , "Watermelon"
, "Orange" , "Orange"
, "Lemon" , "Lemon"
, "Blueberry" , "Blueberry"
, "Grapefruit" , "Grapefruit"
, "Coconut" , "Coconut"
, "Cherry" , "Cherry"
, "Banana" , "Banana"
] ]
|> Set.fromList |> Set.fromList
|> FilterSelect.init |> FilterSelect.init
, validatedInput = , validatedInput =
ValidatedInput.init ValidatedInput.init
{ value = ("John","Doe") { value = ( "John", "Doe" )
, validator = , validator =
\string -> \string ->
case string |> String.split " " of case string |> String.split " " of
[ first, second ] -> [ first, second ] ->
Ok (( first, second )) Ok ( first, second )
_ -> _ ->
Err () Err ()
, toString = , toString =
(\( first, second ) -> first ++ " " ++ second) \( first, second ) -> first ++ " " ++ second
} }
} }
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Cmd Msg )
@ -89,6 +90,7 @@ update msg model =
, Cmd.none , Cmd.none
) )
filterSelect : FilterSelect.Model -> Element Msg filterSelect : FilterSelect.Model -> Element Msg
filterSelect model = filterSelect model =
Element.column (Grid.simple ++ Card.large) <| Element.column (Grid.simple ++ Card.large) <|
@ -99,7 +101,7 @@ filterSelect model =
[ Element.el (Tag.simple ++ Group.left) <| Element.text <| selected [ Element.el (Tag.simple ++ Group.left) <| Element.text <| selected
, Input.button (Tag.simple ++ Group.right ++ Color.danger) , Input.button (Tag.simple ++ Group.right ++ Color.danger)
{ onPress = Just <| FilterSelectSpecific <| FilterSelect.Selected Nothing { onPress = Just <| FilterSelectSpecific <| FilterSelect.Selected Nothing
, label = Element.html <| Heroicons.x [Attributes.width 16] , label = Element.html <| Heroicons.x [ Attributes.width 16 ]
} }
] ]
@ -108,8 +110,11 @@ filterSelect model =
[ FilterSelect.viewInput Input.simple [ FilterSelect.viewInput Input.simple
model model
{ msgMapper = FilterSelectSpecific { msgMapper = FilterSelectSpecific
, placeholder = Just <| Input.placeholder [] <| Element.text <| , placeholder =
"Fruit" Just <|
Input.placeholder [] <|
Element.text <|
"Fruit"
, label = "Fruit" , label = "Fruit"
} }
, model , model
@ -123,11 +128,10 @@ filterSelect model =
) )
|> Element.wrappedRow [ Element.spacing 10 ] |> Element.wrappedRow [ Element.spacing 10 ]
] ]
] ]
validatedInput : ValidatedInput.Model () ( ( String, String )) -> Element Msg validatedInput : ValidatedInput.Model () ( String, String ) -> Element Msg
validatedInput model = validatedInput model =
Element.column (Grid.simple ++ Card.large) <| Element.column (Grid.simple ++ Card.large) <|
[ Element.el Heading.h3 <| Element.text "Validated Input" [ Element.el Heading.h3 <| Element.text "Validated Input"
@ -136,42 +140,40 @@ validatedInput model =
{ label = "First Name, Sir Name" { label = "First Name, Sir Name"
, msgMapper = ValidatedInputSpecific , msgMapper = ValidatedInputSpecific
, placeholder = Nothing , placeholder = Nothing
, readOnly = \maybeTuple -> , readOnly =
Element.row Grid.compact \maybeTuple ->
[ maybeTuple Element.row Grid.compact
|> (\(a,b) -> a ++ " " ++ b) [ maybeTuple
|> Element.text |> (\( a, b ) -> a ++ " " ++ b)
|> Element.text
|> Element.el (Tag.simple ++ Group.left) |> Element.el (Tag.simple ++ Group.left)
, Heroicons.pencil [Attributes.width 16] , Heroicons.pencil [ Attributes.width 16 ]
|> Element.html |> Element.html
|>Element.el (Tag.simple ++ Group.right ++ Color.primary) |> Element.el (Tag.simple ++ Group.right ++ Color.primary)
] ]
} }
] ]
scrollingNavCard : Element msg scrollingNavCard : Element msg
scrollingNavCard = scrollingNavCard =
[Element.el Heading.h3 <| Element.text "Scrolling Nav" [ Element.el Heading.h3 <| Element.text "Scrolling Nav"
, Element.text "Resize the screen and use the scrollbar to see the scrolling navigation in action." , Element.text "Resize the screen and use the scrollbar to see the scrolling navigation in action."
|> List.singleton |> List.singleton
|> Element.paragraph [] |> Element.paragraph []
] ]
|> Element.column (Grid.simple ++ Card.large) |> Element.column (Grid.simple ++ Card.large)
view : Model -> Element Msg view : Model -> Element Msg
view model = view model =
Element.column (Grid.section ++ [Element.centerX]) Element.column (Grid.section ++ [ Element.centerX ])
[ Element.el Heading.h2 <| Element.text "Components" [ Element.el Heading.h2 <| Element.text "Components"
, "Components have a Model, an Update- and sometimes even a Subscription-function. It takes some time to set them up correctly." , "Components have a Model, an Update- and sometimes even a Subscription-function. It takes some time to set them up correctly."
|> Element.text |> Element.text
|> List.singleton |> List.singleton
|> Element.paragraph [] |> Element.paragraph []
, Element.wrappedRow (Grid.simple ++ [Element.centerX]) , Element.wrappedRow (Grid.simple ++ [ Element.centerX ]) <|
<|
[ filterSelect model.filterSelect [ filterSelect model.filterSelect
, validatedInput model.validatedInput , validatedInput model.validatedInput
, scrollingNavCard , scrollingNavCard

View File

@ -1,6 +1,7 @@
module Example exposing (main) module Example exposing (main)
import Browser import Browser
import Component
import Element exposing (Element) import Element exposing (Element)
import Element.Input as Input import Element.Input as Input
import Framework import Framework
@ -14,22 +15,23 @@ import Framework.Input as Input
import Framework.Tag as Tag import Framework.Tag as Tag
import Html exposing (Html) import Html exposing (Html)
import Html.Attributes as Attributes import Html.Attributes as Attributes
import Reusable
import Set exposing (Set) import Set exposing (Set)
import Stateless
import Time
import Widget import Widget
import Widget.FilterSelect as FilterSelect import Widget.FilterSelect as FilterSelect
import Widget.ScrollingNav as ScrollingNav import Widget.ScrollingNav as ScrollingNav
import Widget.ValidatedInput as ValidatedInput
import Widget.Snackbar as Snackbar import Widget.Snackbar as Snackbar
import Stateless import Widget.ValidatedInput as ValidatedInput
import Reusable
import Component
import Time
type Section type Section
= ComponentViews = ComponentViews
| ReusableViews | ReusableViews
| StatelessViews | StatelessViews
type alias Model = type alias Model =
{ component : Component.Model { component : Component.Model
, stateless : Stateless.Model , stateless : Stateless.Model
@ -39,6 +41,7 @@ type alias Model =
, displayDialog : Bool , displayDialog : Bool
} }
type Msg type Msg
= StatelessSpecific Stateless.Msg = StatelessSpecific Stateless.Msg
| ReusableSpecific Reusable.Msg | ReusableSpecific Reusable.Msg
@ -59,6 +62,7 @@ init () =
case section of case section of
ComponentViews -> ComponentViews ->
"Component Views" "Component Views"
ReusableViews -> ReusableViews ->
"Reusable Views" "Reusable Views"
@ -67,37 +71,35 @@ init () =
, arrangement = [ StatelessViews, ReusableViews, ComponentViews ] , arrangement = [ StatelessViews, ReusableViews, ComponentViews ]
} }
in in
({ component = Component.init ( { component = Component.init
, stateless = Stateless.init , stateless = Stateless.init
, reusable = Reusable.init , reusable = Reusable.init
, scrollingNav = scrollingNav , scrollingNav = scrollingNav
, snackbar = Snackbar.init , snackbar = Snackbar.init
, displayDialog = False , displayDialog = False
} }
, cmd |> Cmd.map ScrollingNavSpecific , cmd |> Cmd.map ScrollingNavSpecific
) )
view : Model -> Html Msg view : Model -> Html Msg
view model = view model =
[ Element.el [ Element.height <| Element.px <| 42 ] <| Element.none [ Element.el [ Element.height <| Element.px <| 42 ] <| Element.none
, [ Element.el Heading.h1 <| Element.text "Elm-Ui-Widgets" , [ Element.el Heading.h1 <| Element.text "Elm-Ui-Widgets"
, model.scrollingNav , model.scrollingNav
|> ScrollingNav.view |> ScrollingNav.view
(\section -> (\section ->
case section of case section of
ComponentViews -> ComponentViews ->
model.component model.component
|> Component.view |> Component.view
|> Element.map ComponentSpecific |> Element.map ComponentSpecific
ReusableViews -> ReusableViews ->
Reusable.view Reusable.view
{addSnackbar = AddSnackbar { addSnackbar = AddSnackbar
,model = model.reusable , model = model.reusable
,msgMapper = ReusableSpecific , msgMapper = ReusableSpecific
} }
StatelessViews -> StatelessViews ->
@ -107,7 +109,7 @@ view model =
} }
model.stateless model.stateless
) )
] ]
|> Element.column Framework.container |> Element.column Framework.container
] ]
|> Element.column Grid.compact |> Element.column Grid.compact
@ -120,6 +122,7 @@ view model =
case string of case string of
"Component Views" -> "Component Views" ->
Just ComponentViews Just ComponentViews
"Reusable Views" -> "Reusable Views" ->
Just ReusableViews Just ReusableViews
@ -130,14 +133,16 @@ view model =
Nothing Nothing
, label = Element.text , label = Element.text
, msgMapper = ScrollingNavSpecific , msgMapper = ScrollingNavSpecific
, attributes = \selected -> Button.simple , attributes =
++ Group.center \selected ->
++ (if selected then Button.simple
Color.primary ++ Group.center
++ (if selected then
Color.primary
else else
Color.dark Color.dark
) )
} }
|> Widget.select |> Widget.select
|> Element.row |> Element.row
@ -157,38 +162,41 @@ view model =
) )
) )
, Element.inFront <| , Element.inFront <|
( model.snackbar (model.snackbar
|> Snackbar.current |> Snackbar.current
|> Maybe.map |> Maybe.map
(Element.text >> (Element.text
List.singleton >> >> List.singleton
Element.paragraph (Card.simple ++ Color.dark) >> Element.paragraph (Card.simple ++ Color.dark)
>> Element.el [Element.padding 8,Element.alignBottom >> Element.el
, Element.alignRight] [ Element.padding 8
, Element.alignBottom
, Element.alignRight
]
) )
|> Maybe.withDefault Element.none |> Maybe.withDefault Element.none
) )
, Element.inFront <| , Element.inFront <|
if model.displayDialog then if model.displayDialog then
Widget.dialog { Widget.dialog
onDismiss = Just <| ToggleDialog False { onDismiss = Just <| ToggleDialog False
,content = , content =
[ Element.el Heading.h3 <| Element.text "Dialog" [ Element.el Heading.h3 <| Element.text "Dialog"
, "This is a dialog window" , "This is a dialog window"
|> Element.text |> Element.text
|> List.singleton |> List.singleton
|> Element.paragraph [] |> Element.paragraph []
, Input.button (Button.simple ++ [Element.alignRight]) , Input.button (Button.simple ++ [ Element.alignRight ])
{onPress = Just <| ToggleDialog False { onPress = Just <| ToggleDialog False
, label = Element.text "Ok" , label = Element.text "Ok"
} }
] ]
|>Element.column (Grid.simple ++ Card.large) |> Element.column (Grid.simple ++ Card.large)
} else Element.none }
]
else
Element.none
]
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Cmd Msg )
@ -206,13 +214,15 @@ update msg model =
(Cmd.map ComponentSpecific) (Cmd.map ComponentSpecific)
ReusableSpecific m -> ReusableSpecific m ->
(model.reusable ( model.reusable
|> Reusable.update m |> Reusable.update m
|> (\reusable -> |> (\reusable ->
{ model { model
| reusable = reusable | reusable = reusable
} }
),Cmd.none) )
, Cmd.none
)
StatelessSpecific m -> StatelessSpecific m ->
model.stateless model.stateless
@ -225,7 +235,6 @@ update msg model =
) )
(Cmd.map StatelessSpecific) (Cmd.map StatelessSpecific)
ScrollingNavSpecific m -> ScrollingNavSpecific m ->
model.scrollingNav model.scrollingNav
|> ScrollingNav.update m |> ScrollingNav.update m
@ -238,12 +247,14 @@ update msg model =
(Cmd.map ScrollingNavSpecific) (Cmd.map ScrollingNavSpecific)
TimePassed int -> TimePassed int ->
({ model ( { model
| snackbar = model.snackbar |> Snackbar.timePassed int | snackbar = model.snackbar |> Snackbar.timePassed int
},Cmd.none) }
, Cmd.none
)
AddSnackbar string -> AddSnackbar string ->
( { model | snackbar = model.snackbar |> Snackbar.insert string} ( { model | snackbar = model.snackbar |> Snackbar.insert string }
, Cmd.none , Cmd.none
) )
@ -252,13 +263,14 @@ update msg model =
, Cmd.none , Cmd.none
) )
subscriptions : Model -> Sub Msg subscriptions : Model -> Sub Msg
subscriptions model= subscriptions model =
Sub.batch Sub.batch
[ScrollingNav.subscriptions [ ScrollingNav.subscriptions
|> Sub.map ScrollingNavSpecific |> Sub.map ScrollingNavSpecific
, Time.every 50 (always ( TimePassed 50)) , Time.every 50 (always (TimePassed 50))
] ]
main : Program () Model Msg main : Program () Model Msg

View File

@ -1,9 +1,10 @@
module Reusable exposing (Model,Msg,init,view,update) module Reusable exposing (Model, Msg, init, update, view)
import Browser import Browser
import Element exposing (Element,Color) import Element exposing (Color, Element)
import Element.Input as Input
import Element.Background as Background import Element.Background as Background
import Element.Font as Font
import Element.Input as Input
import Framework import Framework
import Framework.Button as Button import Framework.Button as Button
import Framework.Card as Card import Framework.Card as Card
@ -13,63 +14,70 @@ import Framework.Group as Group
import Framework.Heading as Heading import Framework.Heading as Heading
import Framework.Input as Input import Framework.Input as Input
import Framework.Tag as Tag import Framework.Tag as Tag
import Heroicons.Solid as Heroicons
import Html exposing (Html) import Html exposing (Html)
import Html.Attributes as Attributes import Html.Attributes as Attributes
import Set exposing (Set) import Set exposing (Set)
import Time
import Widget import Widget
import Widget.FilterSelect as FilterSelect import Widget.FilterSelect as FilterSelect
import Widget.ScrollingNav as ScrollingNav import Widget.ScrollingNav as ScrollingNav
import Widget.ValidatedInput as ValidatedInput
import Widget.Snackbar as Snackbar import Widget.Snackbar as Snackbar
import Widget.SortTable as SortTable import Widget.SortTable as SortTable
import Time import Widget.ValidatedInput as ValidatedInput
import Heroicons.Solid as Heroicons
import Element.Font as Font
type alias Model = type alias Model =
SortTable.Model SortTable.Model
type Msg =
SortBy {title : String, asc : Bool } type Msg
= SortBy { title : String, asc : Bool }
type alias Item = type alias Item =
{name : String { name : String
,amount : Int , amount : Int
,price : Float , price : Float
} }
update : Msg -> Model -> Model update : Msg -> Model -> Model
update msg model = update msg model =
case msg of case msg of
SortBy m -> SortBy m ->
m m
init : Model init : Model
init = init =
SortTable.sortBy {title="Name",asc=True} SortTable.sortBy { title = "Name", asc = True }
snackbar : (String -> msg) -> Element msg snackbar : (String -> msg) -> Element msg
snackbar addSnackbar = snackbar addSnackbar =
[ Element.el Heading.h3 <| Element.text "Snackbar" [ Element.el Heading.h3 <| Element.text "Snackbar"
, Input.button Button.simple , Input.button Button.simple
{ onPress = Just <| addSnackbar "This is a notification. It will disappear after 10 seconds." { onPress = Just <| addSnackbar "This is a notification. It will disappear after 10 seconds."
, label = "Add Notification" , label =
|> Element.text "Add Notification"
|> List.singleton |> Element.text
|> Element.paragraph [] |> List.singleton
|> Element.paragraph []
} }
] ]
|> Element.column (Grid.simple ++ Card.large) |> Element.column (Grid.simple ++ Card.large)
sortTable : SortTable.Model -> Element Msg sortTable : SortTable.Model -> Element Msg
sortTable model = sortTable model =
[ Element.el Heading.h3 <| Element.text "Sort Table" [ Element.el Heading.h3 <| Element.text "Sort Table"
, SortTable.view , SortTable.view
{ content = { content =
[ {id = 1, name = "Antonio", rating = 2.456} [ { id = 1, name = "Antonio", rating = 2.456 }
, {id = 2, name = "Ana", rating = 1.34} , { id = 2, name = "Ana", rating = 1.34 }
, {id = 3, name = "Alfred", rating = 4.22} , { id = 3, name = "Alfred", rating = 4.22 }
, {id = 4, name = "Thomas", rating = 3 } , { id = 4, name = "Thomas", rating = 3 }
] ]
, columns = , columns =
[ SortTable.intColumn [ SortTable.intColumn
@ -90,54 +98,65 @@ sortTable model =
] ]
, model = model , model = model
} }
|> (\{data,columns} -> |> (\{ data, columns } ->
{data = data { data = data
,columns = columns , columns =
|> List.map (\config-> columns
{ header = |> List.map
Input.button [Font.bold] (\config ->
{ onPress = {title = config.header { header =
,asc = if config.header == model.title then Input.button [ Font.bold ]
not model.asc { onPress =
else { title = config.header
True , asc =
}|> SortBy |> Just if config.header == model.title then
not model.asc
else
True
}
|> SortBy
|> Just
, label = , label =
if config.header == model.title then if config.header == model.title then
[config.header |> Element.text [ config.header |> Element.text
, Element.html <|if model.asc then , Element.html <|
Heroicons.cheveronUp [Attributes.width 16] if model.asc then
Heroicons.cheveronUp [ Attributes.width 16 ]
else else
Heroicons.cheveronDown [Attributes.width 16] Heroicons.cheveronDown [ Attributes.width 16 ]
] ]
|> Element.row (Grid.simple ++ [Font.bold]) |> Element.row (Grid.simple ++ [ Font.bold ])
else
config.header |> Element.text else
config.header |> Element.text
}
, view = config.view >> Element.text
, width = Element.fill
} }
)
, view = config.view >> Element.text }
, width = Element.fill )
}
)
})
|> Element.table Grid.simple |> Element.table Grid.simple
] ]
|> Element.column (Grid.simple ++ Card.large) |> Element.column (Grid.simple ++ Card.large)
view : { addSnackbar : String -> msg
view :
{ addSnackbar : String -> msg
, msgMapper : Msg -> msg , msgMapper : Msg -> msg
, model : Model } -> Element msg , model : Model
view {addSnackbar,msgMapper,model} = }
Element.column (Grid.section ++ [Element.centerX]) -> Element msg
view { addSnackbar, msgMapper, model } =
Element.column (Grid.section ++ [ Element.centerX ])
[ Element.el Heading.h2 <| Element.text "Reusable Views" [ Element.el Heading.h2 <| Element.text "Reusable Views"
, "Reusable views have an internal state but no update function. You will need to do some wiring, but nothing complicated." , "Reusable views have an internal state but no update function. You will need to do some wiring, but nothing complicated."
|> Element.text |> Element.text
|> List.singleton |> List.singleton
|> Element.paragraph [] |> Element.paragraph []
, Element.wrappedRow (Grid.simple ++ [Element.centerX]) , Element.wrappedRow (Grid.simple ++ [ Element.centerX ]) <|
<|
[ snackbar addSnackbar [ snackbar addSnackbar
, sortTable model |> Element.map msgMapper , sortTable model |> Element.map msgMapper
] ]

View File

@ -1,23 +1,24 @@
module Stateless exposing (Model,Msg,init,update,view) module Stateless exposing (Model, Msg, init, update, view)
import Array exposing (Array)
import Element exposing (Element) import Element exposing (Element)
import Element.Input as Input
import Element.Background as Background import Element.Background as Background
import Element.Border as Border import Element.Border as Border
import Set exposing (Set) import Element.Input as Input
import Framework.Grid as Grid
import Framework.Button as Button import Framework.Button as Button
import Framework.Card as Card import Framework.Card as Card
import Framework.Color as Color import Framework.Color as Color
import Framework.Grid as Grid
import Framework.Group as Group import Framework.Group as Group
import Framework.Heading as Heading import Framework.Heading as Heading
import Framework.Input as Input import Framework.Input as Input
import Framework.Tag as Tag import Framework.Tag as Tag
import Heroicons.Solid as Heroicons import Heroicons.Solid as Heroicons
import Widget
import Html exposing (Html) import Html exposing (Html)
import Html.Attributes as Attributes import Html.Attributes as Attributes
import Array exposing (Array) import Set exposing (Set)
import Widget
type alias Model = type alias Model =
{ selected : Maybe Int { selected : Maybe Int
@ -27,13 +28,15 @@ type alias Model =
, tab : Int , tab : Int
} }
type Msg =
ChangedSelected Int type Msg
= ChangedSelected Int
| ChangedMultiSelected Int | ChangedMultiSelected Int
| ToggleCollapsable Bool | ToggleCollapsable Bool
| ChangedTab Int | ChangedTab Int
| SetCarousel Int | SetCarousel Int
init : Model init : Model
init = init =
{ selected = Nothing { selected = Nothing
@ -43,7 +46,8 @@ init =
, tab = 1 , tab = 1
} }
update : Msg -> Model -> (Model,Cmd Msg)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model = update msg model =
case msg of case msg of
ChangedSelected int -> ChangedSelected int ->
@ -56,32 +60,38 @@ update msg model =
ChangedMultiSelected int -> ChangedMultiSelected int ->
( { model ( { model
| multiSelected = | multiSelected =
model.multiSelected |> model.multiSelected
if model.multiSelected |> Set.member int then |> (if model.multiSelected |> Set.member int then
Set.remove int Set.remove int
else
Set.insert int else
} Set.insert int
, Cmd.none ) )
}
, Cmd.none
)
ToggleCollapsable bool -> ToggleCollapsable bool ->
( { model ( { model
| isCollapsed = bool | isCollapsed = bool
} }
, Cmd.none) , Cmd.none
)
SetCarousel int -> SetCarousel int ->
( if (int < 0) || (int > 3) then ( if (int < 0) || (int > 3) then
model model
else else
{model { model
| carousel = int | carousel = int
} }
, Cmd.none , Cmd.none
) )
ChangedTab int -> ChangedTab int ->
( {model | tab = int }, Cmd.none ) ( { model | tab = int }, Cmd.none )
select : Model -> Element Msg select : Model -> Element Msg
select model = select model =
@ -91,20 +101,40 @@ select model =
, options = [ 1, 2, 42 ] , options = [ 1, 2, 42 ]
, label = String.fromInt >> Element.text , label = String.fromInt >> Element.text
, onChange = ChangedSelected , onChange = ChangedSelected
, attributes = \selected -> , attributes =
Button.simple \selected ->
++ Group.center Button.simple
++ (if selected then ++ [ Border.width 0
Color.primary , Border.rounded 0
]
++ (if selected then
Color.primary
else else
[] []
) )
} }
|> Element.row (Grid.compact) |> List.indexedMap
(\i ->
Element.el
(Button.simple
++ [ Element.padding <| 0 ]
++ (if i == 0 then
Group.left
else if i == 2 then
Group.right
else
Group.center
)
)
)
|> Element.row Grid.compact
] ]
|> Element.column (Grid.simple ++ Card.large) |> Element.column (Grid.simple ++ Card.large)
multiSelect : Model -> Element Msg multiSelect : Model -> Element Msg
multiSelect model = multiSelect model =
[ Element.el Heading.h3 <| Element.text "Multi Select" [ Element.el Heading.h3 <| Element.text "Multi Select"
@ -113,16 +143,35 @@ multiSelect model =
, options = [ 1, 2, 42 ] , options = [ 1, 2, 42 ]
, label = String.fromInt >> Element.text , label = String.fromInt >> Element.text
, onChange = ChangedMultiSelected , onChange = ChangedMultiSelected
, attributes = \selected -> , attributes =
(Button.simple \selected ->
++ Group.center Button.simple
++ if selected then ++ [ Border.width 0
Color.primary , Border.rounded 0
]
++ (if selected then
Color.primary
else else
[] []
) )
} }
|> List.indexedMap
(\i ->
Element.el
(Button.simple
++ [ Element.padding <| 0 ]
++ (if i == 0 then
Group.left
else if i == 2 then
Group.right
else
Group.center
)
)
)
|> Element.row Grid.compact |> Element.row Grid.compact
] ]
|> Element.column (Grid.simple ++ Card.large) |> Element.column (Grid.simple ++ Card.large)
@ -130,55 +179,64 @@ multiSelect model =
collapsable : Model -> Element Msg collapsable : Model -> Element Msg
collapsable model = collapsable model =
[Element.el Heading.h3 <| Element.text "Collapsable" [ Element.el Heading.h3 <| Element.text "Collapsable"
,Widget.collapsable , Widget.collapsable
{onToggle = ToggleCollapsable { onToggle = ToggleCollapsable
,isCollapsed = model.isCollapsed , isCollapsed = model.isCollapsed
,label = Element.row Grid.compact , label =
[ Element.html <| Element.row Grid.compact
if model.isCollapsed then [ Element.html <|
Heroicons.cheveronRight [ Attributes.width 20] if model.isCollapsed then
else Heroicons.cheveronRight [ Attributes.width 20 ]
Heroicons.cheveronDown [ Attributes.width 20]
, Element.el Heading.h4 <|Element.text <| "Title" else
] Heroicons.cheveronDown [ Attributes.width 20 ]
,content = Element.text <| "Hello World" , Element.el Heading.h4 <| Element.text <| "Title"
]
, content = Element.text <| "Hello World"
} }
] ]
|> Element.column (Grid.simple ++ Card.large) |> Element.column (Grid.simple ++ Card.large)
tab : Model -> Element Msg tab : Model -> Element Msg
tab model = tab model =
[Element.el Heading.h3 <| Element.text "Tab" [ Element.el Heading.h3 <| Element.text "Tab"
,Widget.tab Grid.simple , Widget.tab Grid.simple
{ selected = model.tab { selected = model.tab
, options = [1,2,3] , options = [ 1, 2, 3 ]
, onChange = ChangedTab , onChange = ChangedTab
, label = \int -> "Tab " ++ String.fromInt int |> Element.text , label = \int -> "Tab " ++ String.fromInt int |> Element.text
, content = \selected -> , content =
(case selected of \selected ->
1 -> (case selected of
"This is Tab 1" 1 ->
2 -> "This is Tab 1"
"This is the second tab"
3 -> 2 ->
"The thrid and last tab" "This is the second tab"
_ ->
"Please select a tab" 3 ->
"The thrid and last tab"
_ ->
"Please select a tab"
) )
|>Element.text |> Element.text
|> Element.el (Card.small ++ Group.bottom) |> Element.el (Card.small ++ Group.bottom)
, attributes = \selected -> , attributes =
(Button.simple \selected ->
++ Group.top Button.simple
++ if selected then ++ Group.top
Color.primary ++ (if selected then
else Color.primary
[]
) else
[]
)
} }
] ]
|> Element.column (Grid.simple ++ Card.large) |> Element.column (Grid.simple ++ Card.large)
dialog : msg -> Model -> Element msg dialog : msg -> Model -> Element msg
@ -189,42 +247,48 @@ dialog showDialog model =
, label = Element.text <| "Show Dialog" , label = Element.text <| "Show Dialog"
} }
] ]
|> Element.column (Grid.simple ++ Card.large) |> Element.column (Grid.simple ++ Card.large)
carousel : Model -> Element Msg carousel : Model -> Element Msg
carousel model = carousel model =
[ Element.el Heading.h3 <| Element.text "Carousel" [ Element.el Heading.h3 <| Element.text "Carousel"
, Widget.carousel , Widget.carousel
{content = (Color.cyan,[Color.yellow, Color.green , Color.red ]|> Array.fromList) { content = ( Color.cyan, [ Color.yellow, Color.green, Color.red ] |> Array.fromList )
,current = model.carousel , current = model.carousel
, label = \c -> , label =
[ Input.button [Element.centerY] \c ->
[ Input.button [ Element.centerY ]
{ onPress = Just <| SetCarousel <| model.carousel - 1 { onPress = Just <| SetCarousel <| model.carousel - 1
, label = Heroicons.cheveronLeft [Attributes.width 20] , label =
|> Element.html Heroicons.cheveronLeft [ Attributes.width 20 ]
|> Element.html
} }
, Element.el , Element.el
(Card.simple (Card.simple
++ [ Background.color <| c ++ [ Background.color <| c
, Element.height <| Element.px <| 100 , Element.height <| Element.px <| 100
, Element.width <| Element.px <| 100 , Element.width <| Element.px <| 100
] ]
) <| Element.none )
, Input.button [Element.centerY] <|
Element.none
, Input.button [ Element.centerY ]
{ onPress = Just <| SetCarousel <| model.carousel + 1 { onPress = Just <| SetCarousel <| model.carousel + 1
, label = Heroicons.cheveronRight [Attributes.width 20] , label =
|> Element.html Heroicons.cheveronRight [ Attributes.width 20 ]
|> Element.html
} }
] ]
|> Element.row (Grid.simple ++ [Element.centerX, Element.width<| Element.shrink]) |> Element.row (Grid.simple ++ [ Element.centerX, Element.width <| Element.shrink ])
} }
] ]
|> Element.column (Grid.simple ++ Card.large) |> Element.column (Grid.simple ++ Card.large)
view : { msgMapper : Msg -> msg, showDialog : msg} -> Model -> Element msg view : { msgMapper : Msg -> msg, showDialog : msg } -> Model -> Element msg
view {msgMapper,showDialog} model = view { msgMapper, showDialog } model =
Element.column (Grid.section) Element.column Grid.section
[ Element.el Heading.h2 <| Element.text "Stateless Views" [ Element.el Heading.h2 <| Element.text "Stateless Views"
, "Stateless views are simple functions that view some content. No wiring required." , "Stateless views are simple functions that view some content. No wiring required."
|> Element.text |> Element.text
@ -232,7 +296,7 @@ view {msgMapper,showDialog} model =
|> Element.paragraph [] |> Element.paragraph []
, Element.wrappedRow , Element.wrappedRow
Grid.simple Grid.simple
<| <|
[ select model |> Element.map msgMapper [ select model |> Element.map msgMapper
, multiSelect model |> Element.map msgMapper , multiSelect model |> Element.map msgMapper
, collapsable model |> Element.map msgMapper , collapsable model |> Element.map msgMapper

View File

@ -11,7 +11,7 @@ import Element.Input as Input exposing (Placeholder)
import Set exposing (Set) import Set exposing (Set)
{-| The Model {-| The Model containing the raw value, the selected value and all the possible options.
-} -}
type alias Model = type alias Model =
{ raw : String { raw : String

View File

@ -28,29 +28,30 @@ import Time
{-| -} {-| -}
type alias Model elem = type alias Model section =
{ labels : elem -> String { labels : section -> String
, positions : IntDict String , positions : IntDict String
, arrangement : List elem , arrangement : List section
, scrollPos : Int , scrollPos : Int
} }
{-| -} {-| -}
type Msg elem type Msg section
= GotHeaderPos elem (Result Dom.Error Int) = GotHeaderPos section (Result Dom.Error Int)
| ChangedViewport (Result Dom.Error ()) | ChangedViewport (Result Dom.Error ())
| SyncPosition Int | SyncPosition Int
| JumpTo elem | JumpTo section
| TimePassed | TimePassed
{-| -} {-| The intial state include the labels and the arrangement of the sections
-}
init : init :
{ labels : elem -> String { labels : section -> String
, arrangement : List elem , arrangement : List section
} }
-> ( Model elem, Cmd (Msg elem) ) -> ( Model section, Cmd (Msg section) )
init { labels, arrangement } = init { labels, arrangement } =
{ labels = labels { labels = labels
, positions = IntDict.empty , positions = IntDict.empty
@ -65,7 +66,7 @@ init { labels, arrangement } =
{-| -} {-| -}
update : Msg elem -> Model elem -> ( Model elem, Cmd (Msg elem) ) update : Msg section -> Model section -> ( Model section, Cmd (Msg section) )
update msg model = update msg model =
case msg of case msg of
GotHeaderPos label result -> GotHeaderPos label result ->
@ -113,8 +114,9 @@ subscriptions =
Time.every 1000 (always TimePassed) Time.every 1000 (always TimePassed)
{-| -} {-| scrolls the screen to the respective section
jumpTo : elem -> Model elem -> Cmd (Msg msg) -}
jumpTo : section -> Model section -> Cmd (Msg msg)
jumpTo section { labels } = jumpTo section { labels } =
Dom.getElement (section |> labels) Dom.getElement (section |> labels)
|> Task.andThen |> Task.andThen
@ -125,7 +127,7 @@ jumpTo section { labels } =
{-| -} {-| -}
syncPositions : Model elem -> Cmd (Msg elem) syncPositions : Model section -> Cmd (Msg section)
syncPositions { labels, arrangement } = syncPositions { labels, arrangement } =
arrangement arrangement
|> List.map |> List.map
@ -145,16 +147,16 @@ syncPositions { labels, arrangement } =
{-| -} {-| -}
viewSections : viewSections :
{ label : String -> Element msg { label : String -> Element msg
, fromString : String -> Maybe elem , fromString : String -> Maybe section
, msgMapper : Msg elem -> msg , msgMapper : Msg section -> msg
, attributes : Bool -> List (Attribute msg) , attributes : Bool -> List (Attribute msg)
} }
-> Model elem -> Model section
-> ->
{ selected : Maybe elem { selected : Maybe section
, options : List elem , options : List section
, label : elem -> Element msg , label : section -> Element msg
, onChange : elem -> msg , onChange : section -> msg
, attributes : Bool -> List (Attribute msg) , attributes : Bool -> List (Attribute msg)
} }
viewSections { label, fromString, msgMapper, attributes } { arrangement, scrollPos, labels, positions } = viewSections { label, fromString, msgMapper, attributes } { arrangement, scrollPos, labels, positions } =
@ -177,8 +179,8 @@ viewSections { label, fromString, msgMapper, attributes } { arrangement, scrollP
{-| -} {-| -}
view : view :
(elem -> Element msg) (section -> Element msg)
-> Model elem -> Model section
-> Element msg -> Element msg
view asElement { labels, arrangement } = view asElement { labels, arrangement } =
arrangement arrangement

View File

@ -34,7 +34,8 @@ type Model err a
} }
{-| -} {-| returns the raw value (the value that the user currently sees)
-}
getRaw : Model err a -> String getRaw : Model err a -> String
getRaw (Model { raw, value, toString }) = getRaw (Model { raw, value, toString }) =
case raw of case raw of
@ -45,13 +46,15 @@ getRaw (Model { raw, value, toString }) =
value |> toString value |> toString
{-| -} {-| returns the value (the value that has been last successfully validated)
-}
getValue : Model err a -> a getValue : Model err a -> a
getValue (Model { value }) = getValue (Model { value }) =
value value
{-| -} {-| returns the error (if one exists)
-}
getError : Model err a -> Maybe err getError : Model err a -> Maybe err
getError (Model { err }) = getError (Model { err }) =
err err
@ -64,7 +67,13 @@ type Msg
| StartEditing | 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 : { value : a, validator : String -> Result err a, toString : a -> String } -> Model err a
init { validator, toString, value } = init { validator, toString, value } =
Model Model
@ -116,7 +125,15 @@ update msg (Model model) =
Model model 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 : view :
List (Attribute msg) List (Attribute msg)
-> Model err a -> Model err a