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)
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,22 +13,24 @@ 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
@ -36,7 +38,6 @@ type Msg
init : Model
init =
{ filterSelect =
[ "Apple"
, "Kiwi"
@ -62,12 +63,12 @@ init =
\string ->
case string |> String.split " " of
[ first, second ] ->
Ok (( first, second ))
Ok ( first, second )
_ ->
Err ()
, toString =
(\( first, second ) -> first ++ " " ++ second)
\( first, second ) -> first ++ " " ++ second
}
}
@ -89,6 +90,7 @@ update msg model =
, Cmd.none
)
filterSelect : FilterSelect.Model -> Element Msg
filterSelect model =
Element.column (Grid.simple ++ Card.large) <|
@ -108,7 +110,10 @@ filterSelect model =
[ FilterSelect.viewInput Input.simple
model
{ msgMapper = FilterSelectSpecific
, placeholder = Just <| Input.placeholder [] <| Element.text <|
, placeholder =
Just <|
Input.placeholder [] <|
Element.text <|
"Fruit"
, label = "Fruit"
}
@ -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,12 +140,12 @@ validatedInput model =
{ label = "First Name, Sir Name"
, msgMapper = ValidatedInputSpecific
, placeholder = Nothing
, readOnly = \maybeTuple ->
, 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
@ -151,8 +155,6 @@ validatedInput model =
]
scrollingNavCard : Element msg
scrollingNavCard =
[ Element.el Heading.h3 <| Element.text "Scrolling Nav"
@ -162,6 +164,7 @@ scrollingNavCard =
]
|> Element.column (Grid.simple ++ Card.large)
view : Model -> Element Msg
view model =
Element.column (Grid.section ++ [ Element.centerX ])
@ -170,8 +173,7 @@ view model =
|> Element.text
|> List.singleton
|> Element.paragraph []
, Element.wrappedRow (Grid.simple ++ [Element.centerX])
<|
, Element.wrappedRow (Grid.simple ++ [ Element.centerX ]) <|
[ filterSelect model.filterSelect
, validatedInput model.validatedInput
, scrollingNavCard

View File

@ -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"
@ -74,13 +78,10 @@ init () =
, snackbar = Snackbar.init
, displayDialog = False
}
, cmd |> Cmd.map ScrollingNavSpecific
)
view : Model -> Html Msg
view model =
[ Element.el [ Element.height <| Element.px <| 42 ] <| Element.none
@ -93,6 +94,7 @@ view model =
model.component
|> Component.view
|> Element.map ComponentSpecific
ReusableViews ->
Reusable.view
{ addSnackbar = AddSnackbar
@ -120,6 +122,7 @@ view model =
case string of
"Component Views" ->
Just ComponentViews
"Reusable Views" ->
Just ReusableViews
@ -130,7 +133,9 @@ view model =
Nothing
, label = Element.text
, msgMapper = ScrollingNavSpecific
, attributes = \selected -> Button.simple
, attributes =
\selected ->
Button.simple
++ Group.center
++ (if selected then
Color.primary
@ -160,20 +165,21 @@ view model =
(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
Widget.dialog
{ onDismiss = Just <| ToggleDialog False
, content =
[ Element.el Heading.h3 <| Element.text "Dialog"
, "This is a dialog window"
@ -186,9 +192,11 @@ view model =
}
]
|> Element.column (Grid.simple ++ Card.large)
} else Element.none
]
}
else
Element.none
]
update : Msg -> Model -> ( Model, Cmd Msg )
@ -212,7 +220,9 @@ update msg model =
{ model
| reusable = reusable
}
),Cmd.none)
)
, Cmd.none
)
StatelessSpecific m ->
model.stateless
@ -225,7 +235,6 @@ update msg model =
)
(Cmd.map StatelessSpecific)
ScrollingNavSpecific m ->
model.scrollingNav
|> ScrollingNav.update m
@ -240,7 +249,9 @@ update msg model =
TimePassed int ->
( { model
| snackbar = model.snackbar |> Snackbar.timePassed int
},Cmd.none)
}
, Cmd.none
)
AddSnackbar string ->
( { model | snackbar = model.snackbar |> Snackbar.insert string }
@ -252,6 +263,7 @@ update msg model =
, Cmd.none
)
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.batch

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 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,24 +14,26 @@ 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
@ -38,22 +41,26 @@ type alias Item =
, price : Float
}
update : Msg -> Model -> Model
update msg model =
case msg of
SortBy m ->
m
init : Model
init =
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"
, label =
"Add Notification"
|> Element.text
|> List.singleton
|> Element.paragraph []
@ -61,6 +68,7 @@ snackbar addSnackbar =
]
|> Element.column (Grid.simple ++ Card.large)
sortTable : SortTable.Model -> Element Msg
sortTable model =
[ Element.el Heading.h3 <| Element.text "Sort Table"
@ -92,43 +100,55 @@ sortTable model =
}
|> (\{ data, columns } ->
{ data = data
,columns = columns
|> List.map (\config->
, columns =
columns
|> List.map
(\config ->
{ header =
Input.button [ Font.bold ]
{ onPress = {title = config.header
,asc = if config.header == model.title then
{ onPress =
{ title = config.header
, asc =
if config.header == model.title then
not model.asc
else
True
}|> SortBy |> Just
}
|> SortBy
|> Just
, label =
if config.header == model.title then
[ config.header |> Element.text
, Element.html <|if model.asc then
, Element.html <|
if model.asc then
Heroicons.cheveronUp [ Attributes.width 16 ]
else
Heroicons.cheveronDown [ Attributes.width 16 ]
]
|> Element.row (Grid.simple ++ [ Font.bold ])
else
else
config.header |> Element.text
}
, view = config.view >> Element.text
, width = Element.fill
}
)
})
}
)
|> Element.table Grid.simple
]
|> Element.column (Grid.simple ++ Card.large)
view : { addSnackbar : String -> msg
view :
{ addSnackbar : String -> msg
, msgMapper : Msg -> msg
, model : Model } -> Element msg
, model : Model
}
-> Element msg
view { addSnackbar, msgMapper, model } =
Element.column (Grid.section ++ [ Element.centerX ])
[ Element.el Heading.h2 <| Element.text "Reusable Views"
@ -136,8 +156,7 @@ view {addSnackbar,msgMapper,model} =
|> 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
]

View File

@ -1,23 +1,24 @@
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 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,6 +46,7 @@ init =
, tab = 1
}
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
@ -56,23 +60,28 @@ update msg model =
ChangedMultiSelected int ->
( { model
| multiSelected =
model.multiSelected |>
if model.multiSelected |> Set.member int then
model.multiSelected
|> (if model.multiSelected |> Set.member int then
Set.remove int
else
Set.insert int
)
}
, Cmd.none )
, Cmd.none
)
ToggleCollapsable bool ->
( { model
| isCollapsed = bool
}
, Cmd.none)
, Cmd.none
)
SetCarousel int ->
( if (int < 0) || (int > 3) then
model
else
{ model
| carousel = int
@ -83,6 +92,7 @@ update msg model =
ChangedTab int ->
( { model | tab = int }, Cmd.none )
select : Model -> Element Msg
select model =
[ Element.el Heading.h3 <| Element.text "Select"
@ -91,9 +101,12 @@ select model =
, options = [ 1, 2, 42 ]
, label = String.fromInt >> Element.text
, onChange = ChangedSelected
, attributes = \selected ->
, attributes =
\selected ->
Button.simple
++ Group.center
++ [ Border.width 0
, Border.rounded 0
]
++ (if selected then
Color.primary
@ -101,10 +114,27 @@ select model =
[]
)
}
|> 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,16 +143,35 @@ multiSelect model =
, options = [ 1, 2, 42 ]
, label = String.fromInt >> Element.text
, onChange = ChangedMultiSelected
, attributes = \selected ->
(Button.simple
++ Group.center
++ if selected then
, attributes =
\selected ->
Button.simple
++ [ Border.width 0
, Border.rounded 0
]
++ (if selected then
Color.primary
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)
@ -134,10 +183,12 @@ collapsable model =
, Widget.collapsable
{ onToggle = ToggleCollapsable
, isCollapsed = model.isCollapsed
,label = Element.row Grid.compact
, 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"
@ -147,6 +198,7 @@ collapsable model =
]
|> Element.column (Grid.simple ++ Card.large)
tab : Model -> Element Msg
tab model =
[ Element.el Heading.h3 <| Element.text "Tab"
@ -155,24 +207,30 @@ tab model =
, options = [ 1, 2, 3 ]
, onChange = ChangedTab
, label = \int -> "Tab " ++ String.fromInt int |> Element.text
, content = \selected ->
, 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
, attributes =
\selected ->
Button.simple
++ Group.top
++ if selected then
++ (if selected then
Color.primary
else
[]
)
@ -191,16 +249,19 @@ dialog showDialog model =
]
|> 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 ->
, label =
\c ->
[ Input.button [ Element.centerY ]
{ onPress = Just <| SetCarousel <| model.carousel - 1
, label = Heroicons.cheveronLeft [Attributes.width 20]
, label =
Heroicons.cheveronLeft [ Attributes.width 20 ]
|> Element.html
}
, Element.el
@ -209,10 +270,13 @@ carousel model =
, Element.height <| Element.px <| 100
, Element.width <| Element.px <| 100
]
) <| Element.none
)
<|
Element.none
, Input.button [ Element.centerY ]
{ onPress = Just <| SetCarousel <| model.carousel + 1
, label = Heroicons.cheveronRight [Attributes.width 20]
, label =
Heroicons.cheveronRight [ Attributes.width 20 ]
|> Element.html
}
]
@ -224,7 +288,7 @@ carousel model =
view : { msgMapper : Msg -> msg, showDialog : msg } -> Model -> Element msg
view { msgMapper, showDialog } model =
Element.column (Grid.section)
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

View File

@ -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

View File

@ -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

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 { 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