📜 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
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_) PossiblyThe 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 0An empty Scroll on a gap
with nothing before and after it.
It's the loneliest of all Scrolls
<>import Emptiable
Scroll.empty |> Scroll.toStack
--> Emptiable.emptyA 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"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
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
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
--> 4Short for
import Emptiable
Scroll.focusFill =
Scroll.focus >> Emptiable.fillThe 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
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_) PossiblyCounting 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
--> 5O(n) like Scroll.length
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) PossiblyThis 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) PossiblyScroll.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_ PossiblyThis 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) PossiblyIf 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.emptyScroll.to Locationimport 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.duplicateSelectList.selectedMapZipper.duplicateReference.List.unwrapSaving a Scroll with every item becomes expensive for long Scrolls, though!
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" ]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_) PossiblyFocus 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 ]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 -4Try 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_) PossiblyIf 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.emptySwap 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)
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" ]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 ]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
Change the focus,
the sides 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
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.emptyReduce 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, 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
--> 543To accumulate into anything that doesn't have the same type as the [Scroll]s items,
foldFromOne
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 identityRoll 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 PossiblyConverts 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
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 neverPlease read more
at Emptiable.emptyAdapt
📜 Items rolled up on both sides of a focus
→ good fit for dynamic choice selection: tabs, playlist, ...
Scrollcan even focus a gapDownandUpevery item:🍍 🍓 <🍊> 🍉 🍇:Scroll ... FocusGap Never🍍 🍓 <?> 🍉 🍇:Scroll ... FocusGapPossibly<?>means both are possible:🍍 🍓 <> 🍉 🍇: a gap between items ... heh🍍 🍓 <🍊> 🍉 🍇in arguments
in types
(where
RecordWithoutConstructorFunctionstops the compiler from creating a constructor function forModel)