mirror of
https://github.com/Orasund/elm-ui-widgets.git
synced 2024-09-11 06:25:32 +03:00
Last touches
This commit is contained in:
parent
a5d03e6d26
commit
3a7eaff6c6
@ -1,9 +1,9 @@
|
||||
module Component exposing (Model,Msg(..),init,update,view)
|
||||
module Component exposing (Model, Msg(..), init, update, view)
|
||||
|
||||
import Browser
|
||||
import Element exposing (Element,Color)
|
||||
import Element.Input as Input
|
||||
import Element exposing (Color, Element)
|
||||
import Element.Background as Background
|
||||
import Element.Input as Input
|
||||
import Framework
|
||||
import Framework.Button as Button
|
||||
import Framework.Card as Card
|
||||
@ -13,64 +13,65 @@ 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.FilterSelect as FilterSelect
|
||||
import Widget.ScrollingNav as ScrollingNav
|
||||
import Widget.ValidatedInput as ValidatedInput
|
||||
import Widget.Snackbar as Snackbar
|
||||
import Time
|
||||
import Heroicons.Solid as Heroicons
|
||||
import Widget.ValidatedInput as ValidatedInput
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ filterSelect : FilterSelect.Model
|
||||
, validatedInput : ValidatedInput.Model () (( String, String ))
|
||||
, validatedInput : ValidatedInput.Model () ( String, String )
|
||||
}
|
||||
|
||||
|
||||
type Msg
|
||||
= FilterSelectSpecific FilterSelect.Msg
|
||||
| ValidatedInputSpecific ValidatedInput.Msg
|
||||
|
||||
|
||||
init : Model
|
||||
init =
|
||||
|
||||
|
||||
init : Model
|
||||
init =
|
||||
{ filterSelect =
|
||||
[ "Apple"
|
||||
, "Kiwi"
|
||||
, "Strawberry"
|
||||
, "Pineapple"
|
||||
, "Mango"
|
||||
, "Grapes"
|
||||
, "Watermelon"
|
||||
, "Orange"
|
||||
, "Lemon"
|
||||
, "Blueberry"
|
||||
, "Grapefruit"
|
||||
, "Coconut"
|
||||
, "Cherry"
|
||||
, "Banana"
|
||||
]
|
||||
|> Set.fromList
|
||||
|> FilterSelect.init
|
||||
, validatedInput =
|
||||
ValidatedInput.init
|
||||
{ value = ("John","Doe")
|
||||
, validator =
|
||||
\string ->
|
||||
case string |> String.split " " of
|
||||
[ first, second ] ->
|
||||
Ok (( first, second ))
|
||||
[ "Apple"
|
||||
, "Kiwi"
|
||||
, "Strawberry"
|
||||
, "Pineapple"
|
||||
, "Mango"
|
||||
, "Grapes"
|
||||
, "Watermelon"
|
||||
, "Orange"
|
||||
, "Lemon"
|
||||
, "Blueberry"
|
||||
, "Grapefruit"
|
||||
, "Coconut"
|
||||
, "Cherry"
|
||||
, "Banana"
|
||||
]
|
||||
|> Set.fromList
|
||||
|> FilterSelect.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
|
||||
}
|
||||
}
|
||||
|
||||
_ ->
|
||||
Err ()
|
||||
, toString =
|
||||
(\( first, second ) -> first ++ " " ++ second)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
@ -89,6 +90,7 @@ update msg model =
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
|
||||
filterSelect : FilterSelect.Model -> Element Msg
|
||||
filterSelect model =
|
||||
Element.column (Grid.simple ++ Card.large) <|
|
||||
@ -99,7 +101,7 @@ filterSelect model =
|
||||
[ 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]
|
||||
, label = Element.html <| Heroicons.x [ Attributes.width 16 ]
|
||||
}
|
||||
]
|
||||
|
||||
@ -108,8 +110,11 @@ filterSelect model =
|
||||
[ FilterSelect.viewInput Input.simple
|
||||
model
|
||||
{ msgMapper = FilterSelectSpecific
|
||||
, placeholder = Just <| Input.placeholder [] <| Element.text <|
|
||||
"Fruit"
|
||||
, placeholder =
|
||||
Just <|
|
||||
Input.placeholder [] <|
|
||||
Element.text <|
|
||||
"Fruit"
|
||||
, label = "Fruit"
|
||||
}
|
||||
, model
|
||||
@ -123,11 +128,10 @@ filterSelect model =
|
||||
)
|
||||
|> Element.wrappedRow [ Element.spacing 10 ]
|
||||
]
|
||||
|
||||
]
|
||||
|
||||
|
||||
validatedInput : ValidatedInput.Model () ( ( String, String )) -> Element Msg
|
||||
validatedInput : ValidatedInput.Model () ( String, String ) -> Element Msg
|
||||
validatedInput model =
|
||||
Element.column (Grid.simple ++ Card.large) <|
|
||||
[ Element.el Heading.h3 <| Element.text "Validated Input"
|
||||
@ -136,44 +140,42 @@ validatedInput 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)
|
||||
]
|
||||
, 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)
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
scrollingNavCard : Element msg
|
||||
scrollingNavCard =
|
||||
[Element.el Heading.h3 <| Element.text "Scrolling Nav"
|
||||
scrollingNavCard =
|
||||
[ Element.el Heading.h3 <| Element.text "Scrolling Nav"
|
||||
, Element.text "Resize the screen and use the scrollbar to see the scrolling navigation in action."
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
]
|
||||
|> Element.column (Grid.simple ++ Card.large)
|
||||
|> Element.column (Grid.simple ++ Card.large)
|
||||
|
||||
|
||||
view : Model -> Element Msg
|
||||
view model =
|
||||
Element.column (Grid.section ++ [Element.centerX])
|
||||
Element.column (Grid.section ++ [ Element.centerX ])
|
||||
[ 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."
|
||||
|> Element.text
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
, Element.wrappedRow (Grid.simple ++ [Element.centerX])
|
||||
<|
|
||||
[ filterSelect model.filterSelect
|
||||
, validatedInput model.validatedInput
|
||||
, scrollingNavCard
|
||||
, Element.wrappedRow (Grid.simple ++ [ Element.centerX ]) <|
|
||||
[ filterSelect model.filterSelect
|
||||
, validatedInput model.validatedInput
|
||||
, scrollingNavCard
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -1,6 +1,7 @@
|
||||
module Example exposing (main)
|
||||
|
||||
import Browser
|
||||
import Component
|
||||
import Element exposing (Element)
|
||||
import Element.Input as Input
|
||||
import Framework
|
||||
@ -14,22 +15,23 @@ import Framework.Input as Input
|
||||
import Framework.Tag as Tag
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as Attributes
|
||||
import Reusable
|
||||
import Set exposing (Set)
|
||||
import Stateless
|
||||
import Time
|
||||
import Widget
|
||||
import Widget.FilterSelect as FilterSelect
|
||||
import Widget.ScrollingNav as ScrollingNav
|
||||
import Widget.ValidatedInput as ValidatedInput
|
||||
import Widget.Snackbar as Snackbar
|
||||
import Stateless
|
||||
import Reusable
|
||||
import Component
|
||||
import Time
|
||||
import Widget.ValidatedInput as ValidatedInput
|
||||
|
||||
|
||||
type Section
|
||||
= ComponentViews
|
||||
| ReusableViews
|
||||
| StatelessViews
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ component : Component.Model
|
||||
, stateless : Stateless.Model
|
||||
@ -39,6 +41,7 @@ type alias Model =
|
||||
, displayDialog : Bool
|
||||
}
|
||||
|
||||
|
||||
type Msg
|
||||
= StatelessSpecific Stateless.Msg
|
||||
| ReusableSpecific Reusable.Msg
|
||||
@ -59,6 +62,7 @@ init () =
|
||||
case section of
|
||||
ComponentViews ->
|
||||
"Component Views"
|
||||
|
||||
ReusableViews ->
|
||||
"Reusable Views"
|
||||
|
||||
@ -67,39 +71,37 @@ init () =
|
||||
, arrangement = [ StatelessViews, ReusableViews, ComponentViews ]
|
||||
}
|
||||
in
|
||||
({ component = Component.init
|
||||
( { component = Component.init
|
||||
, stateless = Stateless.init
|
||||
, reusable = Reusable.init
|
||||
, scrollingNav = scrollingNav
|
||||
, snackbar = Snackbar.init
|
||||
, displayDialog = False
|
||||
}
|
||||
|
||||
, cmd |> Cmd.map ScrollingNavSpecific
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
[ Element.el [ Element.height <| Element.px <| 42 ] <| Element.none
|
||||
, [ Element.el Heading.h1 <| Element.text "Elm-Ui-Widgets"
|
||||
, model.scrollingNav
|
||||
, model.scrollingNav
|
||||
|> ScrollingNav.view
|
||||
(\section ->
|
||||
case section of
|
||||
ComponentViews ->
|
||||
model.component
|
||||
|> Component.view
|
||||
|> Element.map ComponentSpecific
|
||||
|> Component.view
|
||||
|> Element.map ComponentSpecific
|
||||
|
||||
ReusableViews ->
|
||||
Reusable.view
|
||||
{addSnackbar = AddSnackbar
|
||||
,model = model.reusable
|
||||
,msgMapper = ReusableSpecific
|
||||
Reusable.view
|
||||
{ addSnackbar = AddSnackbar
|
||||
, model = model.reusable
|
||||
, msgMapper = ReusableSpecific
|
||||
}
|
||||
|
||||
|
||||
StatelessViews ->
|
||||
Stateless.view
|
||||
{ msgMapper = StatelessSpecific
|
||||
@ -107,7 +109,7 @@ view model =
|
||||
}
|
||||
model.stateless
|
||||
)
|
||||
]
|
||||
]
|
||||
|> Element.column Framework.container
|
||||
]
|
||||
|> Element.column Grid.compact
|
||||
@ -120,6 +122,7 @@ view model =
|
||||
case string of
|
||||
"Component Views" ->
|
||||
Just ComponentViews
|
||||
|
||||
"Reusable Views" ->
|
||||
Just ReusableViews
|
||||
|
||||
@ -128,16 +131,18 @@ view model =
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
, label = Element.text
|
||||
, label = Element.text
|
||||
, msgMapper = ScrollingNavSpecific
|
||||
, attributes = \selected -> Button.simple
|
||||
++ Group.center
|
||||
++ (if selected then
|
||||
Color.primary
|
||||
, attributes =
|
||||
\selected ->
|
||||
Button.simple
|
||||
++ Group.center
|
||||
++ (if selected then
|
||||
Color.primary
|
||||
|
||||
else
|
||||
Color.dark
|
||||
)
|
||||
else
|
||||
Color.dark
|
||||
)
|
||||
}
|
||||
|> Widget.select
|
||||
|> Element.row
|
||||
@ -157,38 +162,41 @@ view model =
|
||||
)
|
||||
)
|
||||
, Element.inFront <|
|
||||
( model.snackbar
|
||||
(model.snackbar
|
||||
|> Snackbar.current
|
||||
|> Maybe.map
|
||||
(Element.text >>
|
||||
List.singleton >>
|
||||
Element.paragraph (Card.simple ++ Color.dark)
|
||||
>> Element.el [Element.padding 8,Element.alignBottom
|
||||
, Element.alignRight]
|
||||
|
||||
|
||||
(Element.text
|
||||
>> List.singleton
|
||||
>> Element.paragraph (Card.simple ++ Color.dark)
|
||||
>> Element.el
|
||||
[ Element.padding 8
|
||||
, Element.alignBottom
|
||||
, Element.alignRight
|
||||
]
|
||||
)
|
||||
|> Maybe.withDefault Element.none
|
||||
)
|
||||
, Element.inFront <|
|
||||
if model.displayDialog then
|
||||
Widget.dialog {
|
||||
onDismiss = Just <| ToggleDialog False
|
||||
,content =
|
||||
Widget.dialog
|
||||
{ onDismiss = Just <| ToggleDialog False
|
||||
, content =
|
||||
[ Element.el Heading.h3 <| Element.text "Dialog"
|
||||
, "This is a dialog window"
|
||||
|> Element.text
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
, Input.button (Button.simple ++ [Element.alignRight])
|
||||
{onPress = Just <| ToggleDialog False
|
||||
|> Element.text
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
, Input.button (Button.simple ++ [ Element.alignRight ])
|
||||
{ onPress = Just <| ToggleDialog False
|
||||
, label = Element.text "Ok"
|
||||
}
|
||||
]
|
||||
|>Element.column (Grid.simple ++ Card.large)
|
||||
} else Element.none
|
||||
|> Element.column (Grid.simple ++ Card.large)
|
||||
}
|
||||
|
||||
else
|
||||
Element.none
|
||||
]
|
||||
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
@ -206,14 +214,16 @@ update msg model =
|
||||
(Cmd.map ComponentSpecific)
|
||||
|
||||
ReusableSpecific m ->
|
||||
(model.reusable
|
||||
( model.reusable
|
||||
|> Reusable.update m
|
||||
|> (\reusable ->
|
||||
{ model
|
||||
| reusable = reusable
|
||||
}
|
||||
),Cmd.none)
|
||||
|
||||
)
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
StatelessSpecific m ->
|
||||
model.stateless
|
||||
|> Stateless.update m
|
||||
@ -224,7 +234,6 @@ update msg model =
|
||||
}
|
||||
)
|
||||
(Cmd.map StatelessSpecific)
|
||||
|
||||
|
||||
ScrollingNavSpecific m ->
|
||||
model.scrollingNav
|
||||
@ -236,29 +245,32 @@ update msg model =
|
||||
}
|
||||
)
|
||||
(Cmd.map ScrollingNavSpecific)
|
||||
|
||||
|
||||
TimePassed int ->
|
||||
({ model
|
||||
| snackbar = model.snackbar |> Snackbar.timePassed int
|
||||
},Cmd.none)
|
||||
|
||||
AddSnackbar string ->
|
||||
( { model | snackbar = model.snackbar |> Snackbar.insert string}
|
||||
( { model
|
||||
| snackbar = model.snackbar |> Snackbar.timePassed int
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
ToggleDialog bool ->
|
||||
( { model | displayDialog = bool }
|
||||
, Cmd.none
|
||||
|
||||
AddSnackbar string ->
|
||||
( { model | snackbar = model.snackbar |> Snackbar.insert string }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
ToggleDialog bool ->
|
||||
( { model | displayDialog = bool }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model=
|
||||
subscriptions model =
|
||||
Sub.batch
|
||||
[ScrollingNav.subscriptions
|
||||
|> Sub.map ScrollingNavSpecific
|
||||
, Time.every 50 (always ( TimePassed 50))
|
||||
]
|
||||
[ ScrollingNav.subscriptions
|
||||
|> Sub.map ScrollingNavSpecific
|
||||
, Time.every 50 (always (TimePassed 50))
|
||||
]
|
||||
|
||||
|
||||
main : Program () Model Msg
|
||||
|
@ -1,9 +1,10 @@
|
||||
module Reusable exposing (Model,Msg,init,view,update)
|
||||
module Reusable exposing (Model, Msg, init, update, view)
|
||||
|
||||
import Browser
|
||||
import Element exposing (Element,Color)
|
||||
import Element.Input as Input
|
||||
import Element exposing (Color, Element)
|
||||
import Element.Background as Background
|
||||
import Element.Font as Font
|
||||
import Element.Input as Input
|
||||
import Framework
|
||||
import Framework.Button as Button
|
||||
import Framework.Card as Card
|
||||
@ -13,66 +14,73 @@ 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.FilterSelect as FilterSelect
|
||||
import Widget.ScrollingNav as ScrollingNav
|
||||
import Widget.ValidatedInput as ValidatedInput
|
||||
import Widget.Snackbar as Snackbar
|
||||
import Widget.SortTable as SortTable
|
||||
import Time
|
||||
import Heroicons.Solid as Heroicons
|
||||
import Element.Font as Font
|
||||
import Widget.ValidatedInput as ValidatedInput
|
||||
|
||||
|
||||
type alias Model =
|
||||
SortTable.Model
|
||||
|
||||
type Msg =
|
||||
SortBy {title : String, asc : Bool }
|
||||
|
||||
type Msg
|
||||
= SortBy { title : String, asc : Bool }
|
||||
|
||||
|
||||
type alias Item =
|
||||
{name : String
|
||||
,amount : Int
|
||||
,price : Float
|
||||
{ name : String
|
||||
, amount : Int
|
||||
, price : Float
|
||||
}
|
||||
|
||||
|
||||
update : Msg -> Model -> Model
|
||||
update msg model =
|
||||
case msg of
|
||||
SortBy m ->
|
||||
m
|
||||
|
||||
|
||||
init : Model
|
||||
init =
|
||||
SortTable.sortBy {title="Name",asc=True}
|
||||
SortTable.sortBy { title = "Name", asc = True }
|
||||
|
||||
|
||||
snackbar : (String -> msg) -> Element msg
|
||||
snackbar addSnackbar =
|
||||
[ Element.el Heading.h3 <| Element.text "Snackbar"
|
||||
, Input.button Button.simple
|
||||
{ onPress = Just <| addSnackbar "This is a notification. It will disappear after 10 seconds."
|
||||
, label = "Add Notification"
|
||||
|> Element.text
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
, label =
|
||||
"Add Notification"
|
||||
|> Element.text
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
}
|
||||
]
|
||||
|> Element.column (Grid.simple ++ Card.large)
|
||||
|> Element.column (Grid.simple ++ Card.large)
|
||||
|
||||
|
||||
sortTable : SortTable.Model -> Element Msg
|
||||
sortTable model =
|
||||
[ Element.el Heading.h3 <| Element.text "Sort Table"
|
||||
, SortTable.view
|
||||
{ content =
|
||||
[ {id = 1, name = "Antonio", rating = 2.456}
|
||||
, {id = 2, name = "Ana", rating = 1.34}
|
||||
, {id = 3, name = "Alfred", rating = 4.22}
|
||||
, {id = 4, name = "Thomas", rating = 3 }
|
||||
[ { id = 1, name = "Antonio", rating = 2.456 }
|
||||
, { id = 2, name = "Ana", rating = 1.34 }
|
||||
, { id = 3, name = "Alfred", rating = 4.22 }
|
||||
, { id = 4, name = "Thomas", rating = 3 }
|
||||
]
|
||||
, columns =
|
||||
[ SortTable.intColumn
|
||||
[ SortTable.intColumn
|
||||
{ title = "Id"
|
||||
, value = .id
|
||||
, toString = \int -> "#" ++ String.fromInt int
|
||||
@ -90,55 +98,66 @@ sortTable model =
|
||||
]
|
||||
, model = model
|
||||
}
|
||||
|> (\{data,columns} ->
|
||||
{data = data
|
||||
,columns = columns
|
||||
|> List.map (\config->
|
||||
{ header =
|
||||
Input.button [Font.bold]
|
||||
{ onPress = {title = config.header
|
||||
,asc = if config.header == model.title then
|
||||
not model.asc
|
||||
else
|
||||
True
|
||||
}|> SortBy |> Just
|
||||
, label =
|
||||
if config.header == model.title then
|
||||
[config.header |> Element.text
|
||||
, Element.html <|if model.asc then
|
||||
Heroicons.cheveronUp [Attributes.width 16]
|
||||
|
||||
else
|
||||
Heroicons.cheveronDown [Attributes.width 16]
|
||||
]
|
||||
|> Element.row (Grid.simple ++ [Font.bold])
|
||||
else
|
||||
|
||||
config.header |> Element.text
|
||||
|> (\{ data, columns } ->
|
||||
{ data = data
|
||||
, columns =
|
||||
columns
|
||||
|> List.map
|
||||
(\config ->
|
||||
{ header =
|
||||
Input.button [ Font.bold ]
|
||||
{ onPress =
|
||||
{ title = config.header
|
||||
, asc =
|
||||
if config.header == model.title then
|
||||
not model.asc
|
||||
|
||||
else
|
||||
True
|
||||
}
|
||||
|> SortBy
|
||||
|> Just
|
||||
, label =
|
||||
if config.header == model.title then
|
||||
[ config.header |> Element.text
|
||||
, Element.html <|
|
||||
if model.asc then
|
||||
Heroicons.cheveronUp [ Attributes.width 16 ]
|
||||
|
||||
else
|
||||
Heroicons.cheveronDown [ Attributes.width 16 ]
|
||||
]
|
||||
|> Element.row (Grid.simple ++ [ Font.bold ])
|
||||
|
||||
else
|
||||
config.header |> Element.text
|
||||
}
|
||||
, view = config.view >> Element.text
|
||||
, width = Element.fill
|
||||
}
|
||||
|
||||
, view = config.view >> Element.text
|
||||
, width = Element.fill
|
||||
}
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
|> 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
|
||||
, model : Model } -> Element msg
|
||||
view {addSnackbar,msgMapper,model} =
|
||||
Element.column (Grid.section ++ [Element.centerX])
|
||||
, model : Model
|
||||
}
|
||||
-> Element msg
|
||||
view { addSnackbar, msgMapper, model } =
|
||||
Element.column (Grid.section ++ [ Element.centerX ])
|
||||
[ 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."
|
||||
|> Element.text
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
, Element.wrappedRow (Grid.simple ++ [Element.centerX])
|
||||
<|
|
||||
, Element.wrappedRow (Grid.simple ++ [ Element.centerX ]) <|
|
||||
[ snackbar addSnackbar
|
||||
, sortTable model |> Element.map msgMapper
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -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.Input as Input
|
||||
import Element.Background as Background
|
||||
import Element.Border as Border
|
||||
import Set exposing (Set)
|
||||
import Framework.Grid as Grid
|
||||
import Element.Input as Input
|
||||
import Framework.Button as Button
|
||||
import Framework.Card as Card
|
||||
import Framework.Color as Color
|
||||
import Framework.Grid as Grid
|
||||
import Framework.Group as Group
|
||||
import Framework.Heading as Heading
|
||||
import Framework.Input as Input
|
||||
import Framework.Tag as Tag
|
||||
import Heroicons.Solid as Heroicons
|
||||
import Widget
|
||||
import Heroicons.Solid as Heroicons
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as Attributes
|
||||
import Array exposing (Array)
|
||||
import Set exposing (Set)
|
||||
import Widget
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ selected : Maybe Int
|
||||
@ -27,13 +28,15 @@ type alias Model =
|
||||
, tab : Int
|
||||
}
|
||||
|
||||
type Msg =
|
||||
ChangedSelected Int
|
||||
|
||||
type Msg
|
||||
= ChangedSelected Int
|
||||
| ChangedMultiSelected Int
|
||||
| ToggleCollapsable Bool
|
||||
| ChangedTab Int
|
||||
| SetCarousel Int
|
||||
|
||||
|
||||
init : Model
|
||||
init =
|
||||
{ selected = Nothing
|
||||
@ -43,8 +46,9 @@ init =
|
||||
, tab = 1
|
||||
}
|
||||
|
||||
update : Msg -> Model -> (Model,Cmd Msg)
|
||||
update msg model =
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
ChangedSelected int ->
|
||||
( { model
|
||||
@ -52,36 +56,42 @@ update msg model =
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
|
||||
ChangedMultiSelected int ->
|
||||
( { model
|
||||
| multiSelected =
|
||||
model.multiSelected |>
|
||||
if model.multiSelected |> Set.member int then
|
||||
Set.remove int
|
||||
else
|
||||
Set.insert int
|
||||
}
|
||||
, Cmd.none )
|
||||
|
||||
| multiSelected =
|
||||
model.multiSelected
|
||||
|> (if model.multiSelected |> Set.member int then
|
||||
Set.remove int
|
||||
|
||||
else
|
||||
Set.insert int
|
||||
)
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
ToggleCollapsable bool ->
|
||||
( { model
|
||||
( { model
|
||||
| isCollapsed = bool
|
||||
}
|
||||
, Cmd.none)
|
||||
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
SetCarousel int ->
|
||||
( if (int < 0) || (int > 3) then
|
||||
model
|
||||
|
||||
else
|
||||
{model
|
||||
| carousel = int
|
||||
{ model
|
||||
| carousel = int
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
|
||||
ChangedTab int ->
|
||||
( {model | tab = int }, Cmd.none )
|
||||
( { model | tab = int }, Cmd.none )
|
||||
|
||||
|
||||
select : Model -> Element Msg
|
||||
select model =
|
||||
@ -91,20 +101,40 @@ select model =
|
||||
, options = [ 1, 2, 42 ]
|
||||
, label = String.fromInt >> Element.text
|
||||
, onChange = ChangedSelected
|
||||
, attributes = \selected ->
|
||||
Button.simple
|
||||
++ Group.center
|
||||
++ (if selected then
|
||||
Color.primary
|
||||
, attributes =
|
||||
\selected ->
|
||||
Button.simple
|
||||
++ [ Border.width 0
|
||||
, 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)
|
||||
|
||||
|
||||
multiSelect : Model -> Element Msg
|
||||
multiSelect model =
|
||||
[ Element.el Heading.h3 <| Element.text "Multi Select"
|
||||
@ -113,72 +143,100 @@ multiSelect model =
|
||||
, options = [ 1, 2, 42 ]
|
||||
, label = String.fromInt >> Element.text
|
||||
, onChange = ChangedMultiSelected
|
||||
, attributes = \selected ->
|
||||
(Button.simple
|
||||
++ Group.center
|
||||
++ if selected then
|
||||
Color.primary
|
||||
, attributes =
|
||||
\selected ->
|
||||
Button.simple
|
||||
++ [ Border.width 0
|
||||
, 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.column (Grid.simple ++ Card.large)
|
||||
|
||||
|
||||
|
||||
collapsable : Model -> Element Msg
|
||||
collapsable model =
|
||||
[Element.el Heading.h3 <| Element.text "Collapsable"
|
||||
,Widget.collapsable
|
||||
{onToggle = ToggleCollapsable
|
||||
,isCollapsed = model.isCollapsed
|
||||
,label = Element.row Grid.compact
|
||||
[ Element.html <|
|
||||
if model.isCollapsed then
|
||||
Heroicons.cheveronRight [ Attributes.width 20]
|
||||
else
|
||||
Heroicons.cheveronDown [ Attributes.width 20]
|
||||
, Element.el Heading.h4 <|Element.text <| "Title"
|
||||
]
|
||||
,content = Element.text <| "Hello World"
|
||||
[ Element.el Heading.h3 <| Element.text "Collapsable"
|
||||
, Widget.collapsable
|
||||
{ onToggle = ToggleCollapsable
|
||||
, isCollapsed = model.isCollapsed
|
||||
, label =
|
||||
Element.row Grid.compact
|
||||
[ Element.html <|
|
||||
if model.isCollapsed then
|
||||
Heroicons.cheveronRight [ Attributes.width 20 ]
|
||||
|
||||
else
|
||||
Heroicons.cheveronDown [ Attributes.width 20 ]
|
||||
, 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.el Heading.h3 <| Element.text "Tab"
|
||||
,Widget.tab Grid.simple
|
||||
[ Element.el Heading.h3 <| Element.text "Tab"
|
||||
, Widget.tab Grid.simple
|
||||
{ selected = model.tab
|
||||
, options = [1,2,3]
|
||||
, options = [ 1, 2, 3 ]
|
||||
, onChange = ChangedTab
|
||||
, label = \int -> "Tab " ++ String.fromInt int |> Element.text
|
||||
, content = \selected ->
|
||||
(case selected of
|
||||
1 ->
|
||||
"This is Tab 1"
|
||||
2 ->
|
||||
"This is the second tab"
|
||||
3 ->
|
||||
"The thrid and last tab"
|
||||
_ ->
|
||||
"Please select a tab"
|
||||
, content =
|
||||
\selected ->
|
||||
(case selected of
|
||||
1 ->
|
||||
"This is Tab 1"
|
||||
|
||||
2 ->
|
||||
"This is the second tab"
|
||||
|
||||
3 ->
|
||||
"The thrid and last tab"
|
||||
|
||||
_ ->
|
||||
"Please select a tab"
|
||||
)
|
||||
|>Element.text
|
||||
|> Element.el (Card.small ++ Group.bottom)
|
||||
, attributes = \selected ->
|
||||
(Button.simple
|
||||
++ Group.top
|
||||
++ if selected then
|
||||
Color.primary
|
||||
else
|
||||
[]
|
||||
)
|
||||
|> Element.text
|
||||
|> Element.el (Card.small ++ Group.bottom)
|
||||
, attributes =
|
||||
\selected ->
|
||||
Button.simple
|
||||
++ Group.top
|
||||
++ (if selected then
|
||||
Color.primary
|
||||
|
||||
else
|
||||
[]
|
||||
)
|
||||
}
|
||||
]
|
||||
|> Element.column (Grid.simple ++ Card.large)
|
||||
|> Element.column (Grid.simple ++ Card.large)
|
||||
|
||||
|
||||
dialog : msg -> Model -> Element msg
|
||||
@ -189,42 +247,48 @@ dialog showDialog model =
|
||||
, label = Element.text <| "Show Dialog"
|
||||
}
|
||||
]
|
||||
|> Element.column (Grid.simple ++ Card.large)
|
||||
|> Element.column (Grid.simple ++ Card.large)
|
||||
|
||||
|
||||
carousel : Model -> Element Msg
|
||||
carousel model =
|
||||
[ Element.el Heading.h3 <| Element.text "Carousel"
|
||||
, Widget.carousel
|
||||
{content = (Color.cyan,[Color.yellow, Color.green , Color.red ]|> Array.fromList)
|
||||
,current = model.carousel
|
||||
, label = \c ->
|
||||
[ Input.button [Element.centerY]
|
||||
{ content = ( Color.cyan, [ Color.yellow, Color.green, Color.red ] |> Array.fromList )
|
||||
, current = model.carousel
|
||||
, label =
|
||||
\c ->
|
||||
[ Input.button [ Element.centerY ]
|
||||
{ onPress = Just <| SetCarousel <| model.carousel - 1
|
||||
, label = Heroicons.cheveronLeft [Attributes.width 20]
|
||||
|> Element.html
|
||||
, label =
|
||||
Heroicons.cheveronLeft [ Attributes.width 20 ]
|
||||
|> Element.html
|
||||
}
|
||||
, Element.el
|
||||
, Element.el
|
||||
(Card.simple
|
||||
++ [ Background.color <| c
|
||||
, Element.height <| Element.px <| 100
|
||||
, Element.width <| Element.px <| 100
|
||||
]
|
||||
) <| Element.none
|
||||
, Input.button [Element.centerY]
|
||||
++ [ Background.color <| c
|
||||
, Element.height <| Element.px <| 100
|
||||
, Element.width <| Element.px <| 100
|
||||
]
|
||||
)
|
||||
<|
|
||||
Element.none
|
||||
, Input.button [ Element.centerY ]
|
||||
{ onPress = Just <| SetCarousel <| model.carousel + 1
|
||||
, label = Heroicons.cheveronRight [Attributes.width 20]
|
||||
|> Element.html
|
||||
, label =
|
||||
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,showDialog} model =
|
||||
Element.column (Grid.section)
|
||||
|
||||
view : { msgMapper : Msg -> msg, showDialog : msg } -> Model -> Element msg
|
||||
view { msgMapper, showDialog } model =
|
||||
Element.column Grid.section
|
||||
[ Element.el Heading.h2 <| Element.text "Stateless Views"
|
||||
, "Stateless views are simple functions that view some content. No wiring required."
|
||||
|> Element.text
|
||||
@ -232,7 +296,7 @@ view {msgMapper,showDialog} model =
|
||||
|> Element.paragraph []
|
||||
, Element.wrappedRow
|
||||
Grid.simple
|
||||
<|
|
||||
<|
|
||||
[ select model |> Element.map msgMapper
|
||||
, multiSelect model |> Element.map msgMapper
|
||||
, collapsable model |> Element.map msgMapper
|
||||
@ -240,4 +304,4 @@ view {msgMapper,showDialog} model =
|
||||
, carousel model |> Element.map msgMapper
|
||||
, tab model |> Element.map msgMapper
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -11,7 +11,7 @@ import Element.Input as Input exposing (Placeholder)
|
||||
import Set exposing (Set)
|
||||
|
||||
|
||||
{-| The Model
|
||||
{-| The Model containing the raw value, the selected value and all the possible options.
|
||||
-}
|
||||
type alias Model =
|
||||
{ raw : String
|
||||
|
@ -28,29 +28,30 @@ import Time
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Model elem =
|
||||
{ labels : elem -> String
|
||||
type alias Model section =
|
||||
{ labels : section -> String
|
||||
, positions : IntDict String
|
||||
, arrangement : List elem
|
||||
, arrangement : List section
|
||||
, scrollPos : Int
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
type Msg elem
|
||||
= GotHeaderPos elem (Result Dom.Error Int)
|
||||
type Msg section
|
||||
= GotHeaderPos section (Result Dom.Error Int)
|
||||
| ChangedViewport (Result Dom.Error ())
|
||||
| SyncPosition Int
|
||||
| JumpTo elem
|
||||
| JumpTo section
|
||||
| TimePassed
|
||||
|
||||
|
||||
{-| -}
|
||||
{-| The intial state include the labels and the arrangement of the sections
|
||||
-}
|
||||
init :
|
||||
{ labels : elem -> String
|
||||
, arrangement : List elem
|
||||
{ labels : section -> String
|
||||
, arrangement : List section
|
||||
}
|
||||
-> ( Model elem, Cmd (Msg elem) )
|
||||
-> ( Model section, Cmd (Msg section) )
|
||||
init { labels, arrangement } =
|
||||
{ labels = labels
|
||||
, 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 =
|
||||
case msg of
|
||||
GotHeaderPos label result ->
|
||||
@ -113,8 +114,9 @@ subscriptions =
|
||||
Time.every 1000 (always TimePassed)
|
||||
|
||||
|
||||
{-| -}
|
||||
jumpTo : elem -> Model elem -> Cmd (Msg msg)
|
||||
{-| scrolls the screen to the respective section
|
||||
-}
|
||||
jumpTo : section -> Model section -> Cmd (Msg msg)
|
||||
jumpTo section { labels } =
|
||||
Dom.getElement (section |> labels)
|
||||
|> Task.andThen
|
||||
@ -125,7 +127,7 @@ jumpTo section { labels } =
|
||||
|
||||
|
||||
{-| -}
|
||||
syncPositions : Model elem -> Cmd (Msg elem)
|
||||
syncPositions : Model section -> Cmd (Msg section)
|
||||
syncPositions { labels, arrangement } =
|
||||
arrangement
|
||||
|> List.map
|
||||
@ -145,16 +147,16 @@ syncPositions { labels, arrangement } =
|
||||
{-| -}
|
||||
viewSections :
|
||||
{ label : String -> Element msg
|
||||
, fromString : String -> Maybe elem
|
||||
, msgMapper : Msg elem -> msg
|
||||
, fromString : String -> Maybe section
|
||||
, msgMapper : Msg section -> msg
|
||||
, attributes : Bool -> List (Attribute msg)
|
||||
}
|
||||
-> Model elem
|
||||
-> Model section
|
||||
->
|
||||
{ selected : Maybe elem
|
||||
, options : List elem
|
||||
, label : elem -> Element msg
|
||||
, onChange : elem -> msg
|
||||
{ selected : Maybe section
|
||||
, options : List section
|
||||
, label : section -> Element msg
|
||||
, onChange : section -> msg
|
||||
, attributes : Bool -> List (Attribute msg)
|
||||
}
|
||||
viewSections { label, fromString, msgMapper, attributes } { arrangement, scrollPos, labels, positions } =
|
||||
@ -177,8 +179,8 @@ viewSections { label, fromString, msgMapper, attributes } { arrangement, scrollP
|
||||
|
||||
{-| -}
|
||||
view :
|
||||
(elem -> Element msg)
|
||||
-> Model elem
|
||||
(section -> Element msg)
|
||||
-> Model section
|
||||
-> Element msg
|
||||
view asElement { labels, arrangement } =
|
||||
arrangement
|
||||
|
@ -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 { raw, value, toString }) =
|
||||
case raw of
|
||||
@ -45,13 +46,15 @@ getRaw (Model { raw, value, toString }) =
|
||||
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
|
||||
@ -64,7 +67,13 @@ type Msg
|
||||
| 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
|
||||
@ -116,7 +125,15 @@ update msg (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 :
|
||||
List (Attribute msg)
|
||||
-> Model err a
|
||||
|
Loading…
Reference in New Issue
Block a user