mirror of
https://github.com/Orasund/elm-ui-widgets.git
synced 2024-11-26 07:34:39 +03:00
added Search, refactored scrollingNav
This commit is contained in:
parent
57a221aa45
commit
82b59530fd
@ -154,17 +154,6 @@ validatedInput model =
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
scrollingNavCard : Element msg
|
||||
scrollingNavCard =
|
||||
[ Element.el Heading.h3 <| Element.text "Scrolling Nav"
|
||||
, Element.text "Resize the screen and open the side-menu. Then start scrolling to see the scrolling navigation in action."
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
]
|
||||
|> Element.column (Grid.simple ++ Card.large ++ [Element.height <| Element.fill])
|
||||
|
||||
|
||||
view : Model -> Element Msg
|
||||
view model =
|
||||
Element.column (Grid.section ++ [ Element.centerX ])
|
||||
@ -176,6 +165,6 @@ view model =
|
||||
, Element.wrappedRow (Grid.simple ++ [Element.height <| Element.shrink]) <|
|
||||
[ filterSelect model.filterSelect
|
||||
, validatedInput model.validatedInput
|
||||
, scrollingNavCard
|
||||
|
||||
]
|
||||
]
|
||||
|
@ -21,7 +21,7 @@ import Framework.Tag as Tag
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as Attributes
|
||||
import Icons
|
||||
import Layout exposing (Direction, Layout)
|
||||
import Layout exposing (Part, Layout)
|
||||
import Core.Style exposing (Style)
|
||||
import Reusable
|
||||
import Set exposing (Set)
|
||||
@ -43,6 +43,7 @@ type alias LoadedModel =
|
||||
, layout : Layout
|
||||
, displayDialog : Bool
|
||||
, deviceClass : DeviceClass
|
||||
, search : String
|
||||
}
|
||||
|
||||
|
||||
@ -55,14 +56,16 @@ type LoadedMsg
|
||||
= StatelessSpecific Stateless.Msg
|
||||
| ReusableSpecific Reusable.Msg
|
||||
| ComponentSpecific Component.Msg
|
||||
| ScrollingNavSpecific (ScrollingNav.Msg Section)
|
||||
| UpdateScrollingNav (ScrollingNav.Model Section -> ScrollingNav.Model Section)
|
||||
| TimePassed Int
|
||||
| AddSnackbar String
|
||||
| ToggleDialog Bool
|
||||
| ChangedSidebar (Maybe Direction)
|
||||
| ChangedSidebar (Maybe Part)
|
||||
| Resized { width : Int, height : Int }
|
||||
| Load String
|
||||
| JumpTo Section
|
||||
| ChangedSearch String
|
||||
| Idle
|
||||
|
||||
|
||||
type Msg
|
||||
@ -109,6 +112,11 @@ style =
|
||||
, moreVerticalIcon =
|
||||
Icons.moreVertical |> Element.html |> Element.el []
|
||||
, spacing = 8
|
||||
, title = Heading.h2
|
||||
, searchIcon =
|
||||
Icons.search |> Element.html |> Element.el []
|
||||
, search =
|
||||
Color.simple ++ [Font.color <| Element.rgb255 0 0 0 ]
|
||||
}
|
||||
|
||||
|
||||
@ -119,6 +127,12 @@ initialModel { viewport } =
|
||||
ScrollingNav.init
|
||||
{ labels = Section.toString
|
||||
, arrangement = Section.asList
|
||||
, toMsg = \result ->
|
||||
case result of
|
||||
Ok fun ->
|
||||
UpdateScrollingNav fun
|
||||
Err _ ->
|
||||
Idle
|
||||
}
|
||||
in
|
||||
( { component = Component.init
|
||||
@ -133,8 +147,9 @@ initialModel { viewport } =
|
||||
}
|
||||
|> Element.classifyDevice
|
||||
|> .class
|
||||
, search = ""
|
||||
}
|
||||
, cmd |> Cmd.map ScrollingNavSpecific
|
||||
, cmd
|
||||
)
|
||||
|
||||
|
||||
@ -261,16 +276,15 @@ view model =
|
||||
]
|
||||
, onChangedSidebar = ChangedSidebar
|
||||
, title =
|
||||
(if m.deviceClass == Phone || m.deviceClass == Tablet then
|
||||
m.scrollingNav
|
||||
|> ScrollingNav.current Section.fromString
|
||||
|> Maybe.map Section.toString
|
||||
|> Maybe.withDefault "Elm-Ui-Widgets"
|
||||
else
|
||||
"Elm-Ui-Widgets"
|
||||
)
|
||||
"Elm-Ui-Widgets"
|
||||
|> Element.text
|
||||
|> Element.el Heading.h1
|
||||
, search =
|
||||
Just
|
||||
{ text = m.search
|
||||
, onChange = ChangedSearch
|
||||
, label = "Search"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -310,22 +324,17 @@ updateLoaded msg model =
|
||||
)
|
||||
(Cmd.map StatelessSpecific)
|
||||
|
||||
ScrollingNavSpecific m ->
|
||||
model.scrollingNav
|
||||
|> ScrollingNav.update m
|
||||
|> Tuple.mapBoth
|
||||
(\scrollingNav ->
|
||||
{ model
|
||||
| scrollingNav = scrollingNav
|
||||
}
|
||||
)
|
||||
(Cmd.map ScrollingNavSpecific)
|
||||
UpdateScrollingNav fun ->
|
||||
( { model | scrollingNav = model.scrollingNav |> fun}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
TimePassed int ->
|
||||
( { model
|
||||
| layout = model.layout |> Layout.timePassed int
|
||||
}
|
||||
, Cmd.none
|
||||
, ScrollingNav.getPos
|
||||
|> Task.perform UpdateScrollingNav
|
||||
)
|
||||
|
||||
AddSnackbar string ->
|
||||
@ -344,7 +353,7 @@ updateLoaded msg model =
|
||||
)
|
||||
|
||||
ChangedSidebar sidebar ->
|
||||
( { model | layout = model.layout |> Layout.setSidebar sidebar }
|
||||
( { model | layout = model.layout |> Layout.activate sidebar }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
@ -354,10 +363,18 @@ updateLoaded msg model =
|
||||
JumpTo section ->
|
||||
( model
|
||||
, model.scrollingNav
|
||||
|> ScrollingNav.jumpTo section
|
||||
|> Cmd.map ScrollingNavSpecific
|
||||
|> ScrollingNav.jumpTo
|
||||
{ section = section
|
||||
, onChange = always Idle
|
||||
}
|
||||
)
|
||||
|
||||
ChangedSearch string ->
|
||||
( { model | search = string},Cmd.none)
|
||||
|
||||
Idle ->
|
||||
( model , Cmd.none)
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
@ -377,9 +394,7 @@ update msg model =
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Sub.batch
|
||||
[ ScrollingNav.subscriptions
|
||||
|> Sub.map ScrollingNavSpecific
|
||||
, Time.every 50 (always (TimePassed 50))
|
||||
[ Time.every 50 (always (TimePassed 50))
|
||||
, Events.onResize (\h w -> Resized { height = h, width = w })
|
||||
]
|
||||
|> Sub.map LoadedSpecific
|
||||
|
@ -6,6 +6,7 @@ module Icons exposing
|
||||
, circle
|
||||
, triangle
|
||||
, square
|
||||
, search
|
||||
)
|
||||
|
||||
import Html exposing (Html)
|
||||
@ -76,3 +77,10 @@ square =
|
||||
svgFeatherIcon "square"
|
||||
[ Svg.rect [ Svg.Attributes.x "3", y "3", width "18", height "18", rx "2", ry "2" ] []
|
||||
]
|
||||
|
||||
search : Html msg
|
||||
search =
|
||||
svgFeatherIcon "search"
|
||||
[ Svg.circle [ cx "11", cy "11", r "8" ] []
|
||||
, Svg.line [ x1 "21", y1 "21", x2 "16.65", y2 "16.65" ] []
|
||||
]
|
@ -142,6 +142,15 @@ sortTable model =
|
||||
]
|
||||
|> Element.column (Grid.simple ++ Card.large ++ [Element.height <| Element.fill])
|
||||
|
||||
scrollingNavCard : Element msg
|
||||
scrollingNavCard =
|
||||
[ Element.el Heading.h3 <| Element.text "Scrolling Nav"
|
||||
, Element.text "Resize the screen and open the side-menu. Then start scrolling to see the scrolling navigation in action."
|
||||
|> List.singleton
|
||||
|> Element.paragraph []
|
||||
]
|
||||
|> Element.column (Grid.simple ++ Card.large ++ [Element.height <| Element.fill])
|
||||
|
||||
|
||||
view :
|
||||
{ addSnackbar : String -> msg
|
||||
@ -159,5 +168,6 @@ view { addSnackbar, msgMapper, model } =
|
||||
, Element.wrappedRow (Grid.simple ++ [Element.height <| Element.shrink]) <|
|
||||
[ snackbar addSnackbar
|
||||
, sortTable model |> Element.map msgMapper
|
||||
, scrollingNavCard
|
||||
]
|
||||
]
|
||||
|
@ -18,7 +18,7 @@ import Html exposing (Html)
|
||||
import Html.Attributes as Attributes
|
||||
import Set exposing (Set)
|
||||
import Widget
|
||||
import Layout exposing (Direction(..))
|
||||
import Layout exposing (Part(..))
|
||||
|
||||
|
||||
type alias Model =
|
||||
@ -242,7 +242,7 @@ tab model =
|
||||
|
||||
scrim :
|
||||
{ showDialog : msg
|
||||
, changedSheet : Maybe Direction -> msg
|
||||
, changedSheet : Maybe Part -> msg
|
||||
} -> Model -> Element msg
|
||||
scrim {showDialog,changedSheet} model =
|
||||
[ Element.el Heading.h3 <| Element.text "Scrim"
|
||||
@ -251,11 +251,11 @@ scrim {showDialog,changedSheet} model =
|
||||
, label = Element.text <| "Show dialog"
|
||||
}
|
||||
, Input.button Button.simple
|
||||
{ onPress = Just <| changedSheet <| Just Left
|
||||
{ onPress = Just <| changedSheet <| Just LeftSheet
|
||||
, label = Element.text <| "show left sheet"
|
||||
}
|
||||
, Input.button Button.simple
|
||||
{ onPress = Just <| changedSheet <| Just Right
|
||||
{ onPress = Just <| changedSheet <| Just RightSheet
|
||||
, label = Element.text <| "show right sheet"
|
||||
}
|
||||
]
|
||||
@ -301,7 +301,7 @@ carousel model =
|
||||
view :
|
||||
{ msgMapper : Msg -> msg
|
||||
, showDialog : msg
|
||||
, changedSheet : Maybe Direction -> msg
|
||||
, changedSheet : Maybe Part -> msg
|
||||
} -> Model -> Element msg
|
||||
view { msgMapper, showDialog, changedSheet } model =
|
||||
Element.column (Grid.section )
|
||||
|
@ -1,4 +1,4 @@
|
||||
module Core.Style exposing (Style,menuTabButtonSelected,menuTabButton, menuButton, menuButtonSelected, menuIconButton, sheetButton, sheetButtonSelected)
|
||||
module Core.Style exposing (Style, menuButton, menuButtonSelected, menuIconButton, menuTabButton, menuTabButtonSelected, sheetButton, sheetButtonSelected)
|
||||
|
||||
import Element exposing (Attribute, Element)
|
||||
import Element.Input as Input
|
||||
@ -19,10 +19,20 @@ type alias Style msg =
|
||||
, menuIcon : Element Never
|
||||
, moreVerticalIcon : Element Never
|
||||
, spacing : Int
|
||||
, title : List (Attribute msg)
|
||||
, searchIcon : Element Never
|
||||
, search : List (Attribute msg)
|
||||
}
|
||||
|
||||
|
||||
menuButtonSelected : Style msg -> { label : String, icon : Element Never, onPress : Maybe msg } -> Element msg
|
||||
type alias ButtonInfo msg =
|
||||
{ label : String
|
||||
, icon : Element Never
|
||||
, onPress : Maybe msg
|
||||
}
|
||||
|
||||
|
||||
menuButtonSelected : Style msg -> ButtonInfo msg -> Element msg
|
||||
menuButtonSelected config { label, icon, onPress } =
|
||||
Input.button (config.menuButton ++ config.menuButtonSelected)
|
||||
{ onPress = onPress
|
||||
@ -34,7 +44,7 @@ menuButtonSelected config { label, icon, onPress } =
|
||||
}
|
||||
|
||||
|
||||
menuButton : Style msg -> { label : String, icon : Element Never, onPress : Maybe msg } -> Element msg
|
||||
menuButton : Style msg -> ButtonInfo msg -> Element msg
|
||||
menuButton config { label, icon, onPress } =
|
||||
Input.button config.menuButton
|
||||
{ onPress = onPress
|
||||
@ -46,7 +56,7 @@ menuButton config { label, icon, onPress } =
|
||||
}
|
||||
|
||||
|
||||
menuIconButton : Style msg -> { label : String, icon : Element Never, onPress : Maybe msg } -> Element msg
|
||||
menuIconButton : Style msg -> ButtonInfo msg -> Element msg
|
||||
menuIconButton config { label, icon, onPress } =
|
||||
Input.button config.menuButton
|
||||
{ onPress = onPress
|
||||
@ -54,7 +64,7 @@ menuIconButton config { label, icon, onPress } =
|
||||
}
|
||||
|
||||
|
||||
sheetButton : Style msg -> { label : String, icon : Element Never, onPress : Maybe msg } -> Element msg
|
||||
sheetButton : Style msg -> ButtonInfo msg -> Element msg
|
||||
sheetButton config { label, icon, onPress } =
|
||||
Input.button config.sheetButton
|
||||
{ onPress = onPress
|
||||
@ -66,7 +76,7 @@ sheetButton config { label, icon, onPress } =
|
||||
}
|
||||
|
||||
|
||||
sheetButtonSelected : Style msg -> { label : String, icon : Element Never, onPress : Maybe msg } -> Element msg
|
||||
sheetButtonSelected : Style msg -> ButtonInfo msg -> Element msg
|
||||
sheetButtonSelected config { label, icon, onPress } =
|
||||
Input.button (config.sheetButton ++ config.sheetButtonSelected)
|
||||
{ onPress = onPress
|
||||
@ -77,7 +87,8 @@ sheetButtonSelected config { label, icon, onPress } =
|
||||
]
|
||||
}
|
||||
|
||||
menuTabButton : Style msg -> { label : String, icon : Element Never, onPress : Maybe msg } -> Element msg
|
||||
|
||||
menuTabButton : Style msg -> ButtonInfo msg -> Element msg
|
||||
menuTabButton config { label, icon, onPress } =
|
||||
Input.button (config.menuButton ++ config.tabButton)
|
||||
{ onPress = onPress
|
||||
@ -88,7 +99,8 @@ menuTabButton config { label, icon, onPress } =
|
||||
]
|
||||
}
|
||||
|
||||
menuTabButtonSelected : Style msg -> { label : String, icon : Element Never, onPress : Maybe msg } -> Element msg
|
||||
|
||||
menuTabButtonSelected : Style msg -> ButtonInfo msg -> Element msg
|
||||
menuTabButtonSelected config { label, icon, onPress } =
|
||||
Input.button (config.menuButton ++ config.tabButton ++ config.tabButtonSelected)
|
||||
{ onPress = onPress
|
||||
|
123
src/Layout.elm
123
src/Layout.elm
@ -1,5 +1,6 @@
|
||||
module Layout exposing (Direction(..), Layout, init, queueMessage, setSidebar, timePassed, view)
|
||||
module Layout exposing (Layout, Part(..), activate, init, queueMessage, timePassed, view)
|
||||
|
||||
import Array
|
||||
import Browser.Dom exposing (Viewport)
|
||||
import Core.Style as Style exposing (Style)
|
||||
import Element exposing (Attribute, DeviceClass(..), Element)
|
||||
@ -12,21 +13,22 @@ import Widget
|
||||
import Widget.Snackbar as Snackbar
|
||||
|
||||
|
||||
type Direction
|
||||
= Left
|
||||
| Right
|
||||
type Part
|
||||
= LeftSheet
|
||||
| RightSheet
|
||||
| Search
|
||||
|
||||
|
||||
type alias Layout =
|
||||
{ snackbar : Snackbar.Model String
|
||||
, sheet : Maybe Direction
|
||||
, active : Maybe Part
|
||||
}
|
||||
|
||||
|
||||
init : Layout
|
||||
init =
|
||||
{ snackbar = Snackbar.init
|
||||
, sheet = Nothing
|
||||
, active = Nothing
|
||||
}
|
||||
|
||||
|
||||
@ -37,24 +39,27 @@ queueMessage message layout =
|
||||
}
|
||||
|
||||
|
||||
setSidebar : Maybe Direction -> Layout -> Layout
|
||||
setSidebar direction layout =
|
||||
activate : Maybe Part -> Layout -> Layout
|
||||
activate part layout =
|
||||
{ layout
|
||||
| sheet = direction
|
||||
| active = part
|
||||
}
|
||||
|
||||
|
||||
timePassed : Int -> Layout -> Layout
|
||||
timePassed sec layout =
|
||||
case layout.sheet of
|
||||
Nothing ->
|
||||
case layout.active of
|
||||
Just LeftSheet ->
|
||||
layout
|
||||
|
||||
Just RightSheet ->
|
||||
layout
|
||||
|
||||
_ ->
|
||||
{ layout
|
||||
| snackbar = layout.snackbar |> Snackbar.timePassed sec
|
||||
}
|
||||
|
||||
_ ->
|
||||
layout
|
||||
|
||||
|
||||
view :
|
||||
List (Attribute msg)
|
||||
@ -68,12 +73,18 @@ view :
|
||||
{ selected : Int
|
||||
, items : List { label : String, icon : Element Never, onPress : Maybe msg }
|
||||
}
|
||||
, search :
|
||||
Maybe
|
||||
{ onChange : String -> msg
|
||||
, text : String
|
||||
, label : String
|
||||
}
|
||||
, actions : List { label : String, icon : Element Never, onPress : Maybe msg }
|
||||
, onChangedSidebar : Maybe Direction -> msg
|
||||
, onChangedSidebar : Maybe Part -> msg
|
||||
, style : Style msg
|
||||
}
|
||||
-> Html msg
|
||||
view attributes { title, onChangedSidebar, menu, actions, deviceClass, dialog, content, style, layout } =
|
||||
view attributes { search, title, onChangedSidebar, menu, actions, deviceClass, dialog, content, style, layout } =
|
||||
let
|
||||
( primaryActions, moreActions ) =
|
||||
( if (actions |> List.length) > 4 then
|
||||
@ -107,10 +118,14 @@ view attributes { title, onChangedSidebar, menu, actions, deviceClass, dialog, c
|
||||
|| ((menu.items |> List.length) > 5)
|
||||
then
|
||||
[ Input.button style.menuButton
|
||||
{ onPress = Just <| onChangedSidebar <| Just Left
|
||||
{ onPress = Just <| onChangedSidebar <| Just LeftSheet
|
||||
, label = style.menuIcon |> Element.map never
|
||||
}
|
||||
, title
|
||||
, menu.items
|
||||
|> Array.fromList
|
||||
|> Array.get menu.selected
|
||||
|> Maybe.map (.label >> Element.text >> Element.el style.title)
|
||||
|> Maybe.withDefault title
|
||||
]
|
||||
|
||||
else
|
||||
@ -133,7 +148,40 @@ view attributes { title, onChangedSidebar, menu, actions, deviceClass, dialog, c
|
||||
[ Element.width <| Element.shrink
|
||||
, Element.spacing 8
|
||||
]
|
||||
, [ primaryActions
|
||||
, if deviceClass == Phone then
|
||||
Element.none
|
||||
|
||||
else
|
||||
search
|
||||
|> Maybe.map
|
||||
(\{ onChange, text, label } ->
|
||||
Input.text style.search
|
||||
{ onChange = onChange
|
||||
, text = text
|
||||
, placeholder =
|
||||
Just <|
|
||||
Input.placeholder [] <|
|
||||
Element.text label
|
||||
, label = Input.labelHidden label
|
||||
}
|
||||
)
|
||||
|> Maybe.withDefault Element.none
|
||||
, [ if deviceClass == Phone then
|
||||
search
|
||||
|> Maybe.map
|
||||
(\{ label } ->
|
||||
[ Style.menuButton style
|
||||
{ onPress = Just <| onChangedSidebar <| Just Search
|
||||
, icon = style.searchIcon
|
||||
, label = label
|
||||
}
|
||||
]
|
||||
)
|
||||
|> Maybe.withDefault []
|
||||
|
||||
else
|
||||
[]
|
||||
, primaryActions
|
||||
|> List.map
|
||||
(if deviceClass == Phone then
|
||||
Style.menuIconButton style
|
||||
@ -146,7 +194,7 @@ view attributes { title, onChangedSidebar, menu, actions, deviceClass, dialog, c
|
||||
|
||||
else
|
||||
[ Style.menuButton style
|
||||
{ onPress = Just <| onChangedSidebar <| Just Right
|
||||
{ onPress = Just <| onChangedSidebar <| Just RightSheet
|
||||
, icon = style.moreVerticalIcon
|
||||
, label = ""
|
||||
}
|
||||
@ -182,10 +230,13 @@ view attributes { title, onChangedSidebar, menu, actions, deviceClass, dialog, c
|
||||
]
|
||||
)
|
||||
|> Maybe.withDefault Element.none
|
||||
|
||||
sheet =
|
||||
case layout.sheet of
|
||||
Just Left ->
|
||||
menu.items
|
||||
case layout.active of
|
||||
Just LeftSheet ->
|
||||
[ [ title
|
||||
]
|
||||
, menu.items
|
||||
|> List.indexedMap
|
||||
(\i ->
|
||||
if i == menu.selected then
|
||||
@ -194,6 +245,8 @@ view attributes { title, onChangedSidebar, menu, actions, deviceClass, dialog, c
|
||||
else
|
||||
Style.sheetButton style
|
||||
)
|
||||
]
|
||||
|> List.concat
|
||||
|> Element.column [ Element.width <| Element.fill ]
|
||||
|> Element.el
|
||||
(style.sheet
|
||||
@ -202,7 +255,7 @@ view attributes { title, onChangedSidebar, menu, actions, deviceClass, dialog, c
|
||||
]
|
||||
)
|
||||
|
||||
Just Right ->
|
||||
Just RightSheet ->
|
||||
moreActions
|
||||
|> List.map (Style.sheetButton style)
|
||||
|> Element.column [ Element.width <| Element.fill ]
|
||||
@ -213,6 +266,26 @@ view attributes { title, onChangedSidebar, menu, actions, deviceClass, dialog, c
|
||||
]
|
||||
)
|
||||
|
||||
Just Search ->
|
||||
case search of
|
||||
Just { onChange, text, label } ->
|
||||
Input.text style.search
|
||||
{ onChange = onChange
|
||||
, text = text
|
||||
, placeholder =
|
||||
Just <|
|
||||
Input.placeholder [] <|
|
||||
Element.text label
|
||||
, label = Input.labelHidden label
|
||||
}
|
||||
|> Element.el
|
||||
[ Element.alignTop
|
||||
, Element.width <| Element.fill
|
||||
]
|
||||
|
||||
Nothing ->
|
||||
Element.none
|
||||
|
||||
Nothing ->
|
||||
Element.none
|
||||
in
|
||||
@ -223,7 +296,7 @@ view attributes { title, onChangedSidebar, menu, actions, deviceClass, dialog, c
|
||||
, [ Element.inFront nav
|
||||
, Element.inFront snackbar
|
||||
]
|
||||
, if (layout.sheet /= Nothing) || (dialog /= Nothing) then
|
||||
, if (layout.active /= Nothing) || (dialog /= Nothing) then
|
||||
Widget.scrim
|
||||
{ onDismiss =
|
||||
Just <|
|
||||
|
@ -1,7 +1,7 @@
|
||||
module Widget.ScrollingNav exposing
|
||||
( Model, Msg, init, update, subscriptions, view, viewSections, current
|
||||
( Model, init, view, viewSections, current
|
||||
, jumpTo, syncPositions
|
||||
, jumpToWithOffset
|
||||
, getPos, jumpToWithOffset, setPos
|
||||
)
|
||||
|
||||
{-| The Scrolling Nav is a navigation bar thats updates while you scroll through
|
||||
@ -24,8 +24,7 @@ import Element exposing (Attribute, Element)
|
||||
import Framework.Grid as Grid
|
||||
import Html.Attributes as Attributes
|
||||
import IntDict exposing (IntDict)
|
||||
import Task
|
||||
import Time
|
||||
import Task exposing (Task)
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -37,23 +36,15 @@ type alias Model section =
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
type Msg section
|
||||
= GotHeaderPos section (Result Dom.Error Int)
|
||||
| ChangedViewport (Result Dom.Error ())
|
||||
| SyncPosition Int
|
||||
| JumpTo section
|
||||
| TimePassed
|
||||
|
||||
|
||||
{-| The intial state include the labels and the arrangement of the sections
|
||||
-}
|
||||
init :
|
||||
{ labels : section -> String
|
||||
, arrangement : List section
|
||||
, toMsg : Result Dom.Error (Model section -> Model section) -> msg
|
||||
}
|
||||
-> ( Model section, Cmd (Msg section) )
|
||||
init { labels, arrangement } =
|
||||
-> ( Model section, Cmd msg )
|
||||
init { labels, arrangement, toMsg } =
|
||||
{ labels = labels
|
||||
, positions = IntDict.empty
|
||||
, arrangement = arrangement
|
||||
@ -62,97 +53,91 @@ init { labels, arrangement } =
|
||||
|> (\a ->
|
||||
( a
|
||||
, syncPositions a
|
||||
|> Task.attempt toMsg
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
update : Msg section -> Model section -> ( Model section, Cmd (Msg section) )
|
||||
update msg model =
|
||||
case msg of
|
||||
GotHeaderPos label result ->
|
||||
( case result of
|
||||
Ok pos ->
|
||||
{ model
|
||||
| positions =
|
||||
model.positions
|
||||
|> IntDict.insert pos
|
||||
(label |> model.labels)
|
||||
}
|
||||
|
||||
Err _ ->
|
||||
model
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
ChangedViewport _ ->
|
||||
( model, Cmd.none )
|
||||
|
||||
SyncPosition pos ->
|
||||
( { model
|
||||
| scrollPos = pos
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
TimePassed ->
|
||||
( model
|
||||
, Dom.getViewport
|
||||
|> Task.map (.viewport >> .y >> round)
|
||||
|> Task.perform SyncPosition
|
||||
)
|
||||
|
||||
JumpTo elem ->
|
||||
( model
|
||||
, model
|
||||
|> jumpTo elem
|
||||
getPos : Task x (Model selection -> Model selection)
|
||||
getPos =
|
||||
Dom.getViewport
|
||||
|> Task.map
|
||||
(\int model ->
|
||||
{ model
|
||||
| scrollPos = int.viewport.y |> round
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
subscriptions : Sub (Msg msg)
|
||||
subscriptions =
|
||||
Time.every 100 (always TimePassed)
|
||||
setPos : Int -> Model section -> Model section
|
||||
setPos pos model =
|
||||
{ model | scrollPos = pos }
|
||||
|
||||
|
||||
{-| scrolls the screen to the respective section
|
||||
-}
|
||||
jumpTo : section -> Model section -> Cmd (Msg msg)
|
||||
jumpTo section { labels } =
|
||||
jumpTo :
|
||||
{ section : section
|
||||
, onChange : Result Dom.Error () -> msg
|
||||
}
|
||||
-> Model section
|
||||
-> Cmd msg
|
||||
jumpTo { section, onChange } { labels } =
|
||||
Dom.getElement (section |> labels)
|
||||
|> Task.andThen
|
||||
(\{ element } ->
|
||||
Dom.setViewport 0 (element.y)
|
||||
Dom.setViewport 0 element.y
|
||||
)
|
||||
|> Task.attempt ChangedViewport
|
||||
|> Task.attempt onChange
|
||||
|
||||
|
||||
{-| scrolls the screen to the respective section with some offset
|
||||
-}
|
||||
jumpToWithOffset : Float -> section -> Model section -> Cmd (Msg msg)
|
||||
jumpToWithOffset offset section { labels } =
|
||||
jumpToWithOffset :
|
||||
{ offset : Float
|
||||
, section : section
|
||||
, onChange : Result Dom.Error () -> msg
|
||||
}
|
||||
-> Model section
|
||||
-> Cmd msg
|
||||
jumpToWithOffset { offset, section, onChange } { labels } =
|
||||
Dom.getElement (section |> labels)
|
||||
|> Task.andThen
|
||||
(\{ element } ->
|
||||
Dom.setViewport 0 (element.y - offset)
|
||||
)
|
||||
|> Task.attempt ChangedViewport
|
||||
|> Task.attempt onChange
|
||||
|
||||
|
||||
{-| -}
|
||||
syncPositions : Model section -> Cmd (Msg section)
|
||||
syncPositions : Model section -> Task Dom.Error (Model section -> Model section)
|
||||
syncPositions { labels, arrangement } =
|
||||
arrangement
|
||||
|> List.map
|
||||
(\label ->
|
||||
Dom.getElement (labels label)
|
||||
|> Task.map
|
||||
(.element
|
||||
>> .y
|
||||
>> round
|
||||
(\x ->
|
||||
( x.element.y |> round
|
||||
, label
|
||||
)
|
||||
)
|
||||
|> Task.attempt
|
||||
(GotHeaderPos label)
|
||||
)
|
||||
|> Cmd.batch
|
||||
|> Task.sequence
|
||||
|> Task.map
|
||||
(\list m ->
|
||||
list
|
||||
|> List.foldl
|
||||
(\( pos, label ) model ->
|
||||
{ model
|
||||
| positions =
|
||||
model.positions
|
||||
|> IntDict.insert pos
|
||||
(label |> model.labels)
|
||||
}
|
||||
)
|
||||
m
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
@ -170,7 +155,7 @@ current fromString { positions, scrollPos } =
|
||||
viewSections :
|
||||
{ label : String -> Element msg
|
||||
, fromString : String -> Maybe section
|
||||
, msgMapper : Msg section -> msg
|
||||
, onSelect : section -> msg
|
||||
, attributes : Bool -> List (Attribute msg)
|
||||
}
|
||||
-> Model section
|
||||
@ -181,11 +166,11 @@ viewSections :
|
||||
, onChange : section -> msg
|
||||
, attributes : Bool -> List (Attribute msg)
|
||||
}
|
||||
viewSections { label, fromString, msgMapper, attributes } ({ arrangement, scrollPos, labels, positions } as model) =
|
||||
viewSections { label, fromString, onSelect, attributes } ({ arrangement, labels } as model) =
|
||||
{ selected = model |> current fromString
|
||||
, options = arrangement
|
||||
, label = \elem -> label (elem |> labels)
|
||||
, onChange = JumpTo >> msgMapper
|
||||
, onChange = onSelect
|
||||
, attributes = attributes
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user