📜 Items rolled up on both sides of a focus
→ good fit for dynamic choice selection: tabs, playlist, timeline... ↑ examples
Scroll
can even focus a gap
Down
or Up
from every item
Not what you were looking for? Check out alternatives
📜 Items rolled up on both sides of a focus
→ good fit for dynamic choice selection: tabs, playlist, ...
Scroll
can even focus a gap Down
and Up
every item:
🍍 🍓 <🍊> 🍉 🍇
: Scroll ... FocusGap Never
🍍 🍓 <?> 🍉 🍇
: Scroll ... FocusGap
Possibly
<?>
means both are possible:
🍍 🍓 <> 🍉 🍇
: a gap between items ... heh🍍 🍓 <🍊> 🍉 🍇
empty : Scroll item_ FocusGap Possibly
type alias Model =
RecordWithoutConstructorFunction
{ choice : Scroll Option FocusGap Never
}
(where RecordWithoutConstructorFunction
stops the compiler from creating a constructor function for Model
)
A word in every Scroll
type:
🍍 🍓 <🍊> 🍉 🍇
: Scroll ... FocusGap Never
🍍 🍓 <?> 🍉 🍇
: Scroll ... FocusGap
Possibly
<?>
means both are possible:
🍍 🍓 <> 🍉 🍇
: a gap between items ... Heh.🍍 🍓 <🍊> 🍉 🍇
Position in a Scroll
relative to its focus
import Linear exposing (Direction(..))
import Emptiable exposing (Emptiable, filled)
import Stack exposing (topBelow)
Scroll.one 0
|> Scroll.sideAlter Up
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.to (Scroll.AtSide Up 2)
|> Emptiable.map Scroll.focusFill
--> filled 3
--: Emptiable (Stacked number_) Possibly
nearest : Linear.Direction -> Location
The Location
directly
Down
|Up
the focus
import Emptiable exposing (Emptiable, filled)
import Stack exposing (onTopLay, topBelow)
import Scroll exposing (Scroll, FocusGap)
import Linear exposing (Direction(..))
Scroll.one "hello"
|> Scroll.sideAlter Up
(\_ -> topBelow "scrollable" [ "world" ])
|> Scroll.toEnd Up
|> Scroll.to (Down |> Scroll.nearest)
|> Emptiable.map Scroll.focusFill
--> filled "scrollable"
--: Emptiable (Scroll String FocusGap Never) Possibly
Scroll.empty
|> Scroll.sideAlter Down
(\_ -> topBelow "world" [ "scrollable" ])
|> Scroll.to (Down |> Scroll.nearest)
--> Scroll.one "world"
--> |> Scroll.sideAlter Down
--> (\_ -> Stack.one "scrollable")
--> |> filled
--: Emptiable (Scroll String FocusGap Never) Possibly
Scroll.empty
|> Scroll.sideAlter Up
(onTopLay "foo")
|> Scroll.to (Up |> Scroll.nearest)
--> filled (Scroll.one "foo")
--: Emptiable (Scroll String FocusGap Never) Possibly
nearest =
\side ->
Scroll.AtSide side 0
empty : Scroll item_ FocusGap Possibly
An empty Scroll
on a gap
with nothing before and after it.
It's the loneliest of all Scroll
s
<>
import Emptiable
Scroll.empty |> Scroll.toStack
--> Emptiable.empty
one : element -> Scroll element FocusGap never_
A Scroll
with a single focussed item in it,
nothing Down
and Up
it
🍊 -> <🍊>
import Stack
Scroll.one "wat" |> Scroll.focusFill
--> "wat"
Scroll.one "wat" |> Scroll.toStack
--> Stack.one "wat"
fuzz : Fuzzer item -> Fuzzer (Scroll item FocusGap Possibly)
Scroll item FocusGap Possibly
Fuzzer
with a given item
Fuzzer
.
Each side's generated length will be <= 32, The focus is either filled or not
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
import Fuzz
Scroll.fuzz (Fuzz.intRange 0 9) |> Fuzz.examples 2
--> [ Scroll.one 6
--> |> Scroll.sideAlter Down (\_ -> topBelow 0 [ 1, 6, 0, 5, 1, 7, 8, 8, 0, 5, 4, 9, 4, 9, 3, 7, 0, 3, 6, 4 ])
--> |> Scroll.sideAlter Up (\_ -> topBelow 5 [ 9 ])
--> , Scroll.empty
--> |> Scroll.sideAlter Down (\_ -> topBelow 3 [ 9, 7, 5, 0, 3 ])
--> |> Scroll.sideAlter Up (\_ -> topBelow 8 [ 4, 5, 6, 9, 6, 0, 6, 4, 3, 8, 0, 5, 4 ])
--> ]
To always fuzz the focus as filled → focusFilledFuzz
focusFilledFuzz : Fuzzer item -> Fuzzer (Scroll item FocusGap never_)
Scroll item FocusGap never_
Fuzzer
with a given item
Fuzzer
.
Each side's generated length will be <= 32, The focus is always filled
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
import Fuzz
Scroll.focusFilledFuzz (Fuzz.intRange 0 9)
|> Fuzz.examples 2
--> [ Scroll.one 5
--> |> Scroll.sideAlter Down (\_ -> topBelow 6 [ 7, 7, 5, 4, 1, 3, 9, 4, 0, 8, 8, 8, 5, 7, 9, 9 ])
--> |> Scroll.sideAlter Up (\_ -> topBelow 5 [ 9 ])
--> , Scroll.one 5
--> |> Scroll.sideAlter Down (\_ -> topBelow 9 [ 9, 6, 4, 4, 9, 8, 0, 5, 1, 2, 2, 5, 9, 9, 3, 0 ])
--> |> Scroll.sideAlter Up (\_ -> topBelow 2 [ 7, 8 ])
--> ]
To only sometimes fuzz the focus as filled → fuzz
focusFill : Scroll item FocusGap Basics.Never -> item
The focused item
🍍 🍓 <🍊> 🍉 🍇 -> 🍊
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
Scroll.one "hi there" |> Scroll.focusFill
--> "hi there"
Scroll.one 1
|> Scroll.sideAlter Up
(\_ -> topBelow 2 [ 3, 4 ])
|> Scroll.toEnd Up
|> Scroll.focusFill
--> 4
Short for
import Emptiable
Scroll.focusFill =
Scroll.focus >> Emptiable.fill
focus : Scroll item FocusGap possiblyOrNever -> Emptiable item possiblyOrNever
The focused item or gap
🍍 🍓 <🍊> 🍉 🍇 -> 🍊
🍍 🍓 <> 🍉 🍇 -> _
import Emptiable exposing (filled, fill)
Scroll.empty |> Scroll.focus
--> Emptiable.empty
Scroll.one "hi there" |> Scroll.focus |> fill
--> "hi there"
focusFill
is short for focus |> fill
side : Linear.Direction -> Scroll item FocusGap possiblyOrNever_ -> Emptiable (Stacked item) Possibly
The stack to one side of the focus
Down
🍍←🍓) <🍊> 🍉 🍇
Up
🍍 🍓 <🍊> (🍉→🍇
import Emptiable exposing (Emptiable)
import Stack exposing (Stacked, topBelow)
import Linear exposing (Direction(..))
Scroll.one 0
|> Scroll.sideAlter Up
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.to (Up |> Scroll.nearest)
|> Emptiable.mapFlat (Scroll.to (Up |> Scroll.nearest))
|> Emptiable.mapFlat (Scroll.side Down)
--> topBelow 1 [ 0 ]
--: Emptiable (Stacked number_) Possibly
Scroll.one 0
|> Scroll.sideAlter Up
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.to (Up |> Scroll.nearest)
|> Emptiable.mapFlat (Scroll.side Up)
--> topBelow 2 [ 3 ]
--: Emptiable (Stacked number_) Possibly
length : Scroll item_ FocusGap possiblyOrNever_ -> Basics.Int
Counting all contained items
import Stack exposing (topBelow)
import Linear exposing (Direction(..))
Scroll.one 0
|> Scroll.sideAlter Down
(\_ -> topBelow -1 [ -2 ])
|> Scroll.sideAlter Up
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.length
--> 6
Scroll.empty
|> Scroll.sideAlter Down
(\_ -> topBelow -1 [ -2 ])
|> Scroll.sideAlter Up
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.length
--> 5
O(n)
like Scroll.length
to : Location -> Scroll item FocusGap possiblyOrNever_ -> Emptiable (Scroll item FocusGap never_) Possibly
Try to move the focus
to the item at a given Scroll.Location
Scroll.to (Down |> Scroll.nearest)
🍊 <🍉> 🍇 -> <🍊> 🍉 🍇
import Linear exposing (Direction(..))
import Emptiable exposing (Emptiable, filled)
import Scroll exposing (Scroll, FocusGap)
Scroll.empty |> Scroll.to (Down |> Scroll.nearest)
--> Emptiable.empty
Scroll.one "hello"
|> Scroll.sideAlter Up
(\_ -> topBelow "scrollable" [ "world" ])
|> Scroll.toEnd Up
|> Scroll.to (Down |> Scroll.nearest)
|> Emptiable.map Scroll.focusFill
--> filled "scrollable"
--: Emptiable (Scroll String FocusGap Never) Possibly
This also works from within gaps:
🍊 🍉 <> 🍇 -> 🍊 <🍉> 🍇
import Linear exposing (Direction(..))
import Emptiable exposing (Emptiable, filled)
import Stack exposing (onTopLay)
import Scroll exposing (Scroll, FocusGap)
Scroll.empty
|> Scroll.sideAlter Down
(onTopLay "foo")
|> Scroll.to (Down |> Scroll.nearest)
--> filled (Scroll.one "foo")
--: Emptiable (Scroll String FocusGap Never) Possibly
Scroll.to (Up |> Scroll.nearest)
<🍊> 🍉 🍇 -> 🍊 <🍉> 🍇
import Linear exposing (Direction(..))
import Emptiable exposing (Emptiable, filled)
import Scroll exposing (Scroll, FocusGap)
Scroll.one 0
|> Scroll.sideAlter Up
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.to (Up |> Scroll.nearest)
|> Emptiable.map Scroll.focusFill
--> filled 1
--: Emptiable number_ Possibly
This also works from within gaps:
🍊 <> 🍉 🍇 -> 🍊 <🍉> 🍇
import Linear exposing (Direction(..))
import Emptiable exposing (Emptiable, filled)
import Stack exposing (onTopLay)
import Scroll exposing (Scroll, FocusGap)
Scroll.empty
|> Scroll.sideAlter Up
(\_ -> Stack.one "foo")
|> Scroll.to (Up |> Scroll.nearest)
--> filled (Scroll.one "foo")
--: Emptiable (Scroll String FocusGap Never) Possibly
If there is no next item,
the result is empty
import Linear exposing (Direction(..))
import Emptiable
import Stack exposing (topBelow)
Scroll.empty |> Scroll.to (Up |> Scroll.nearest)
--> Emptiable.empty
Scroll.one 0
|> Scroll.sideAlter Up
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.toEnd Up
|> Scroll.to (Up |> Scroll.nearest)
--> Emptiable.empty
Scroll.to Location
import Element as Ui
import Element.Input as UIn
import Emptiable
import RecordWithoutConstructorFunction exposing (RecordWithoutConstructorFunction)
import Scroll exposing (FocusGap, Scroll)
import Stack
type alias Model =
RecordWithoutConstructorFunction
{ numbers : Scroll Int FocusGap Never
}
type Event
= NumberClicked Scroll.Location
update : Event -> Model -> ( Model, Cmd Event )
update event model =
case event of
NumberClicked location ->
( { model
| numbers =
model.numbers
|> Scroll.to location
|> Emptiable.fillElseOnEmpty (\_ -> model.numbers)
|> Scroll.focusAlter (Emptiable.map (\n -> n + 1))
}
, Cmd.none
)
interface : Model -> Ui.Element Event
interface =
\{ numbers } ->
numbers
|> Scroll.map numberInterface
|> Scroll.toList
|> Ui.column []
numberInterface :
Scroll.Location
-> Int
-> Ui.Element Event
numberInterface location =
\number ->
UIn.button []
{ onPress = NumberClicked location |> Just
, label =
number
|> String.fromInt
|> Ui.text
}
The same functionality is often provided as duplicate : Scroll item ... -> Scroll (Scroll item) ...
List.NonEmpty.Zipper.duplicate
SelectList.selectedMap
Zipper.duplicate
Reference.List.unwrap
Saving a Scroll
with every item becomes expensive for long Scroll
s, though!
toGap : Linear.Direction -> Scroll item FocusGap Basics.Never -> Scroll item FocusGap Possibly
Move the focus to the gap directly Down|Up
.
Feel free to plug that gap right up!
Scroll.toGap Down
🍍 <🍊> 🍉 -> 🍍 <> 🍊 🍉
import Linear exposing (Direction(..))
import Emptiable exposing (filled)
import Stack exposing (topBelow)
Scroll.one "world"
|> Scroll.toGap Down
|> Scroll.focusAlter (\_ -> "hello" |> filled)
|> Scroll.toStack
--> topBelow "hello" [ "world" ]
Scroll.toGap Up
🍍 <🍊> 🍉 -> 🍍 🍊 <> 🍉
import Linear exposing (Direction(..))
import Emptiable exposing (filled)
import Stack exposing (topBelow)
Scroll.one "hello"
|> Scroll.sideAlter Up
(\_ -> Stack.one "world")
|> Scroll.toGap Up
|> Scroll.focusAlter (\_ -> filled "scrollable")
|> Scroll.toStack
--> topBelow "hello" [ "scrollable", "world" ]
toEnd : Linear.Direction -> Scroll item FocusGap possiblyOrNever -> Scroll item FocusGap possiblyOrNever
Focus the furthest item Down/Up
the focus
Down
🍍 🍓 <🍊> 🍉 -> <🍍> 🍓 🍊 🍉
Up
🍓 <🍊> 🍉 🍇 -> 🍓 🍊 🍉 <🍇>
import Linear exposing (Direction(..))
import Emptiable exposing (Emptiable)
import Stack exposing (Stacked, topBelow)
Scroll.one 1
|> Scroll.sideAlter Up
(\_ -> topBelow 2 [ 3, 4 ])
|> Scroll.sideAlter Down
(\_ -> topBelow 4 [ 3, 2 ])
|> Scroll.toEnd Down
|> Scroll.focusFill
--> 2
Scroll.one 1
|> Scroll.sideAlter Up
(\_ -> topBelow 2 [ 3, 4 ])
|> Scroll.toEnd Up
|> Scroll.focusFill
--> 4
Scroll.one 1
|> Scroll.sideAlter Up
(\_ -> topBelow 2 [ 3, 4 ])
|> Scroll.toEnd Up
|> Scroll.side Down
--> topBelow 3 [ 2, 1 ]
--: Emptiable (Stacked number_) Possibly
toEndGap : Linear.Direction -> Scroll item FocusGap possiblyOrNever_ -> Scroll item FocusGap Possibly
Focus the gap beyond the furthest item Down|Up
.
Remember that gaps surround everything!
Down
🍍 🍓 <🍊> 🍉 -> <> 🍍 🍓 🍊 🍉
Up
🍓 <🍊> 🍉 -> 🍓 🍊 🍉 <>
import Linear exposing (Direction(..))
import Emptiable exposing (filled)
import Stack exposing (topBelow)
Scroll.one 1
-- <1>
|> Scroll.sideAlter Up
(\_ -> topBelow 3 [ 4 ])
-- <1> 3 4
|> Scroll.toGap Up
-- 1 <> 3 4
|> Scroll.focusAlter (\_ -> filled 2)
-- 1 <2> 3 4
|> Scroll.toEndGap Down
-- <> 1 2 3 4
|> Scroll.focusAlter (\_ -> filled 0)
-- <0> 1 2 3 4
|> Scroll.toStack
--> topBelow 0 [ 1, 2, 3, 4 ]
Scroll.one 1
-- <1>
|> Scroll.sideAlter Up
(\_ -> topBelow 2 [ 3 ])
-- <1> 2 3
|> Scroll.toEndGap Up
-- 1 2 3 <>
|> Scroll.focusAlter (\_ -> filled 4)
-- 1 2 3 <4>
|> Scroll.toStack
--> topBelow 1 [ 2, 3, 4 ]
toWhen : Linear.Direction -> ({ index : Basics.Int } -> item -> Basics.Bool) -> Scroll item FocusGap possiblyOrNever_ -> Emptiable (Scroll item FocusGap never_) Possibly
Move the focus to the nearest item Down|Up
that matches a predicate.
If no such item was found,
Emptiable.empty
import Linear exposing (Direction(..))
import Emptiable exposing (filled)
import Stack exposing (topBelow)
import Linear exposing (Direction(..))
Scroll.one 4
|> Scroll.sideAlter Down
(\_ -> topBelow 2 [ -1, 0, 3 ])
|> Scroll.toWhen Down (\_ item -> item < 0)
|> Emptiable.map Scroll.focusFill
--> filled -1
Scroll.one 4
|> Scroll.sideAlter Up
(\_ -> topBelow 2 [ -1, 0, 3 ])
|> Scroll.toWhen Up (\_ item -> item < 0)
|> Emptiable.map focusFill
--> filled -1
Scroll.one -4
|> Scroll.sideAlter Up
(\_ -> topBelow 2 [ -1, 0, 3 ])
|> Scroll.toWhen Up (\_ item -> item < 0)
|> Emptiable.map focusFill
--> filled -4
dragFocus : Linear.Direction -> Scroll item FocusGap possiblyOrNever -> Emptiable (Scroll item FocusGap possiblyOrNever) Possibly
Try to move the focus to the nearest item
Down|Up
Down
🍊 🍉 <🍓> 🍇 -> 🍊 <🍓> 🍉 🍇
🍊 🍉 <> 🍇 -> 🍊 <> 🍉 🍇
Up
🍊 <🍓> 🍉 🍇 -> 🍊 🍉 <🍓> 🍇
🍊 <> 🍉 🍇 -> 🍊 🍉 <> 🍇
import Linear exposing (Direction(..))
import Emptiable exposing (Emptiable)
import Scroll exposing (Scroll, FocusGap)
Scroll.one 0
|> Scroll.sideAlter Down
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.dragFocus Down
|> Emptiable.mapFlat Scroll.toStack
--> topBelow 3 [ 2, 0, 1 ]
--: Emptiable (Stacked number_) Possibly
Scroll.one 0
|> Scroll.sideAlter Up
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.dragFocus Up
|> Emptiable.mapFlat Scroll.toStack
--> topBelow 1 [ 0, 2, 3 ]
--: Emptiable (Stacked number_) Possibly
If there is no nearest item, the result is
empty
import Linear exposing (Direction(..))
import Emptiable
import Stack exposing (topBelow)
Scroll.one 0
|> Scroll.sideAlter Up
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.dragFocus Down
--> Emptiable.empty
Scroll.one 0
|> Scroll.sideAlter Down
(\_ -> topBelow 1 [ 2, 3 ])
|> Scroll.dragFocus Up
--> Emptiable.empty
mirror : Scroll item FocusGap possiblyOrNever -> Scroll item FocusGap possiblyOrNever
Swap the stack
on the side Down
the focus
with the stack
on the side Up
🍓 <🍊> 🍉 🍇 <-> 🍇 🍉 <🍊> 🍓
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
Scroll.one 1
|> Scroll.sideAlter Up
(\_ -> topBelow 2 [ 3, 4 ])
|> Scroll.sideAlter Down
(\_ -> topBelow 4 [ 3, 2 ])
|> Scroll.mirror
--> Scroll.one 1
--> |> Scroll.sideAlter Down
--> (\_ -> topBelow 2 [ 3, 4 ])
--> |> Scroll.sideAlter Up
--> (\_ -> topBelow 4 [ 3, 2 ])
In contrast to List
or stack,
runtime is O(1)
focusAlter : (Emptiable item possiblyOrNever -> Emptiable item alteredPossiblyOrNever) -> Scroll item FocusGap possiblyOrNever -> Scroll item FocusGap alteredPossiblyOrNever
Alter the focus – item or gap – based on its current value
Scroll.focusAlter (\_ -> 🍊 |> filled)
🍊 -> 🍓 <?> 🍉 -> 🍓 <🍊> 🍉
import Linear exposing (Direction(..))
import Emptiable exposing (filled)
import Stack exposing (topBelow, onTopLay)
Scroll.empty
-- <>
|> Scroll.sideAlter Down
(onTopLay "🍓")
-- "🍓" <>
|> Scroll.sideAlter Up
(onTopLay "🍉")
-- "🍓" <> "🍉"
|> Scroll.focusAlter (\_ -> "🍊" |> filled)
-- "🍓" <"🍊"> "🍉"
|> Scroll.toStack
--> topBelow "🍓" [ "🍊", "🍉" ]
Scroll.focusAlter (\_ -> Emptiable.empty)
🍓 <?> 🍉 -> 🍓 <> 🍉
import Linear exposing (Direction(..))
import Emptiable exposing (filled)
import Stack exposing (topBelow)
Scroll.one "hello"
|> Scroll.sideAlter Up
(\_ -> topBelow "scrollable" [ "world" ])
|> Scroll.to (Up |> Scroll.nearest)
|> Emptiable.map (Scroll.focusAlter (\_ -> Emptiable.empty))
|> Emptiable.map Scroll.toList
--> filled [ "hello", "world" ]
Scroll.focusAlter (?🍒 -> ?🍊)
(?🍒 -> ?🍊) -> 🍓 <?🍒> 🍉 -> 🍓 <?🍊> 🍉
import Linear exposing (Direction(..))
import Emptiable exposing (filled)
import Stack exposing (topBelow, onTopLay)
Scroll.empty
-- <>
|> Scroll.sideAlter Down
(onTopLay "🍓")
-- "🍓" <>
|> Scroll.sideAlter Up
(onTopLay "🍉")
-- "🍓" <> "🍉"
|> Scroll.focusAlter
(\_ -> filled "🍊")
-- "🍓" <"🍊"> "🍉"
|> Scroll.toStack
--> topBelow "🍓" [ "🍊", "🍉" ]
Scroll.one "first"
|> Scroll.sideAlter Down
(\_ -> Stack.one "zeroth")
|> Scroll.sideAlter Up
(\_ -> topBelow "second" [ "third" ])
|> Scroll.focusAlter (Emptiable.map String.toUpper)
|> Scroll.toStack
--> topBelow "zeroth" [ "FIRST", "second", "third" ]
sideAlter : Linear.Direction -> (Emptiable (Stacked item) Possibly -> Emptiable (Stacked item) possiblyOrNever_) -> Scroll item FocusGap possiblyOrNever -> Scroll item FocusGap possiblyOrNever
Look Down|Up
the focus
and operate directly
an the stack
you see
sideAlter Linear.Direction (\_ -> 🍒🍋)
Down
🍓 <🍊> 🍉
↓
🍋 🍒 <🍊> 🍉
Up
🍍 🍓 <🍊> 🍉
↓
🍍 🍓 <🍊> 🍒 🍋
import Linear exposing (Direction(..))
import Emptiable
import Stack exposing (topBelow)
Scroll.one "selectoo"
|> Scroll.sideAlter Down
(\_ -> topBelow "earlee" [ "agua", "enutai" ])
|> Scroll.sideAlter Up
(\_ -> topBelow "orangloo" [ "iquipy", "oice" ])
|> Scroll.sideAlter Up
(\_ -> Emptiable.empty)
|> Scroll.toStack
--> topBelow "enutai" [ "agua", "earlee", "selectoo" ]
sideAlter Linear.Direction (Stack.map ...)
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
Scroll.one "second"
|> Scroll.sideAlter Down
(\_ -> topBelow "first" [ "zeroth" ])
|> Scroll.sideAlter Down
(Stack.map (\_ -> String.toUpper))
|> Scroll.toStack
--> topBelow "ZEROTH" [ "FIRST", "second" ]
Scroll.one "zeroth"
|> Scroll.sideAlter Up
(\_ -> topBelow "first" [ "second" ])
|> Scroll.sideAlter Up
(Stack.map (\_ -> String.toUpper))
|> Scroll.toStack
--> topBelow "zeroth" [ "FIRST", "SECOND" ]
Look to one side from the focus and slide items in directly at the nearest location
sideAlter Linear.Direction (Stack.attach 🍒🍋)
Down
🍒🍋
🍍 🍓 \↓/ <🍊> 🍉
Up
🍒🍋
🍓 <🍊> \↓/ 🍉 🍇
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
Scroll.one 0
|> Scroll.sideAlter Down
(Stack.attach Down ([ -4, -5 ] |> Stack.fromList))
|> Scroll.sideAlter Down
(Stack.attach Down (topBelow -1 [ -2, -3 ]))
|> Scroll.toStack
--> topBelow -5 [ -4, -3, -2, -1, 0 ]
Scroll.one 0
|> Scroll.sideAlter Up
(Stack.attach Down ([ 4, 5 ] |> Stack.fromList))
|> Scroll.sideAlter Up
(Stack.attach Down (topBelow 1 [ 2, 3 ]))
|> Scroll.toStack
--> topBelow 0 [ 1, 2, 3, 4, 5 ]
Scroll.sideAlter Linear.Direction (Stack.attach Up 🍒🍋)
Down
🍋🍒
\↓ 🍍 🍓 <🍊> 🍉
Up
🍒🍋
🍓 <🍊> 🍉 🍇 ↓/
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
Scroll.one 1
|> Scroll.sideAlter Up
(Stack.attach Up (topBelow 2 [ 3, 4 ]))
|> Scroll.toEnd Up
|> Scroll.sideAlter Down
(Stack.attach Up (topBelow 7 [ 6, 5 ]))
|> Scroll.toStack
--> topBelow 5 [ 6, 7, 1, 2, 3, 4 ]
Scroll.one 123
|> Scroll.sideAlter Up
(Stack.attach Up (Stack.one 456))
|> Scroll.sideAlter Up
(Stack.attach Up (topBelow 789 [ 0 ]))
|> Scroll.toStack
--> topBelow 123 [ 456, 789, 0 ]
sideAlter Linear.Direction (
Stack.onTopLay
...)
Down
🍒
🍍 🍓 ↓ <🍊> 🍉
Up
🍒
🍓 <🍊> ↓ 🍉 🍇
import Linear exposing (Direction(..))
import Stack exposing (topBelow, onTopLay)
Scroll.one 123
|> Scroll.sideAlter Down
(onTopLay 456)
|> Scroll.toStack
--> topBelow 456 [ 123 ]
Scroll.one 123
|> Scroll.sideAlter Up
(\_ -> Stack.one 789)
|> Scroll.sideAlter Up
(onTopLay 456)
|> Scroll.toStack
--> topBelow 123 [ 456, 789 ]
map : (Location -> item -> mappedItem) -> Scroll item FocusGap possiblyOrNever -> Scroll mappedItem FocusGap possiblyOrNever
Change every item based on its current value
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
Scroll.one "first"
|> Scroll.sideAlter Down
(\_ -> Stack.one "zeroth")
|> Scroll.sideAlter Up
(\_ -> topBelow "second" [ "third" ])
|> Scroll.map (\_ -> String.toUpper)
|> Scroll.toStack
--> topBelow "ZEROTH" [ "FIRST", "SECOND", "THIRD" ]
focusSidesMap
allows changing the individual parts separately
focusSidesMap : { focus : Emptiable item possiblyOrNever -> Emptiable mappedItem possiblyOrNeverMapped, side : Linear.Direction -> Emptiable (Stacked item) Possibly -> Emptiable (Stacked mappedItem) possiblyOrNeverMappedBefore_ } -> Scroll item FocusGap possiblyOrNever -> Scroll mappedItem FocusGap possiblyOrNeverMapped
Change the focus
,
the side
s Down
and Up
using different functions
import Linear exposing (Direction(..))
import Emptiable exposing (filled)
import Stack exposing (topBelow)
Scroll.one "first"
|> Scroll.sideAlter Up
(\_ -> Stack.one "second")
|> Scroll.toGap Up
|> Scroll.focusAlter (\_ -> filled "one-and-a-halfth")
|> Scroll.focusSidesMap
{ side =
\side ->
Stack.map
(\_ item ->
String.concat
[ side |> sideToString, ": ", item ]
)
, focus =
map (\item -> "focused item: " ++ item)
}
|> Scroll.toStack
--→
topBelow
"before: first"
[ "focused item: one-and-a-halfth"
, "after: second"
]
sideToString =
\side ->
case side of
Down ->
"before"
Up ->
"after"
map
transforms every item
focusFilled : Scroll item FocusGap possiblyOrNever -> Emptiable (Scroll item FocusGap never_) possiblyOrNever
If the current focussed thing is a gap,
Emptiable.empty
.
If it's an item,
Emptiable.filled
import Emptiable
import Stack exposing (topBelow)
import Linear exposing (Direction(..))
Scroll.one 3
|> Scroll.sideAlter Up
(\_ -> topBelow 2 [ 1 ])
|> Scroll.toGap Up
|> Scroll.focusFilled
--> Emptiable.empty
foldFrom : accumulationValue -> Linear.Direction -> (item -> accumulationValue -> accumulationValue) -> Scroll item FocusGap possiblyOrNever_ -> accumulationValue
Reduce in a direction
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
Scroll.one 'i'
|> Scroll.sideAlter Down
(\_ -> topBelow 'v' [ 'e' ])
|> Scroll.sideAlter Up
(\_ -> Stack.one 'l')
|> Scroll.foldFrom "" Down String.cons
--> "evil"
Scroll.one 'v'
|> Scroll.sideAlter Down
(\_ -> Stack.one 'e')
|> Scroll.sideAlter Up
(\_ -> topBelow 'i' [ 'l' ])
|> Scroll.foldFrom "" Up String.cons
--> "live"
Scroll.empty
|> Scroll.sideAlter Down
(\_ -> topBelow 'v' [ 'e' ])
|> Scroll.sideAlter Up
(\_ -> topBelow 'i' [ 'l' ])
|> Scroll.foldFrom "" Up String.cons
--> "live"
fold : Linear.Direction -> (item -> item -> item) -> Scroll item FocusGap Basics.Never -> item
Fold, starting from one end item as the initial accumulation value,
reducing in a given Direction
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
Scroll.one 234
|> Scroll.sideAlter Up
(\_ -> topBelow 345 [ 543 ])
|> Scroll.fold Up max
--> 543
To accumulate into anything that doesn't have the same type as the [Scroll
]s items,
foldFromOne
foldFromOne : (item -> accumulated) -> Linear.Direction -> (item -> accumulated -> accumulated) -> Scroll item FocusGap Basics.Never -> accumulated
Fold, starting from one end item transformed to the initial accumulation value,
reducing in a given Direction
import Linear exposing (Direction(..))
import Stack exposing (topBelow)
Scroll.one 234
|> Scroll.sideAlter Up
(\_ -> topBelow 345 [ 543 ])
|> Scroll.foldFromOne Stack.one Down Stack.onTopLay
--> topBelow 234 [ 345, 543 ]
A simpler version is
Scroll.fold =
Scroll.foldFromOne identity
toStack : Scroll item FocusGap possiblyOrNever -> Emptiable (Stacked item) possiblyOrNever
Roll out the Scroll
to both ends
into a Stack
import Linear exposing (Direction(..))
import Emptiable exposing (filled)
import Stack exposing (topBelow)
Scroll.empty
|> Scroll.toStack
--> Emptiable.empty
Scroll.one 123
|> Scroll.sideAlter Up
(\_ -> Stack.one 789)
|> Scroll.toGap Up
|> Scroll.focusAlter (\_-> filled 456)
|> Scroll.toStack
--> topBelow 123 [ 456, 789 ]
its type information gets carried over, so
Scroll.FocusGap Never -> Emptiable Never
Scroll.FocusGap Possibly -> Emptiable Possibly
toList : Scroll item FocusGap possiblyOrNever_ -> List item
Converts it to a List
, rolled out to both ends:
import Linear exposing (Direction(..))
import Stack
Scroll.one 456
|> Scroll.sideAlter Down
(\_ -> Stack.one 123)
|> Scroll.sideAlter Up
(\_ -> Stack.one 789)
|> Scroll.toList
--> [ 123, 456, 789 ]
Only use this if you need a list in the end.
Otherwise, use toStack
to preserve some information about its length
focusGapAdapt : (possiblyOrNever -> possiblyOrNeverAdapted) -> Scroll item FocusGap possiblyOrNever -> Scroll item FocusGap possiblyOrNeverAdapted
Change the possiblyOrNever
type
A Scroll ... FocusGap possiblyOrNever
can't be used as a Scroll ... Possibly
?
import Possibly exposing (Possibly(..))
Scroll.focusGapAdapt (always Possible)
A Scroll ... FocusGap Never
can't be unified with Scroll ... FocusGap Possibly
or an argument variable FocusGap possiblyOrNever
?
Scroll.focusGapAdapt never
Please read more
at Emptiable.emptyAdapt