lue-bird/elm-emptiness-typed/5.0.1

NOTE: the latest version is 9.2.1

Scroll

Items rolled up on both sides of a focus → good fit for dynamic choice selection: tabs, playlist, timeline...

Scroll can even focus a gap Down and Up every item.

  1. 🔎 focus on a gap between two items
  2. 🔌 plug that gap with a value
  3. 💰 profit
type Scroll item possiblyOrNever focusedOnGapTag
= BeforeFocusAfter
(Hand (Stacked item) Possibly Empty)
(Hand item possiblyOrNever Empty)
(Hand (Stacked item) Possibly Empty)

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 ... Never FocusGap

  • 🍍 🍓 <?> 🍉 🍇: Scroll ... Possibly FocusGap

    <?> means both are possible:

    • 🍍 🍓 <> 🍉 🍇: a gap between items ... Heh.
    • 🍍 🍓 <🍊> 🍉 🍇

in arguments

empty : Scroll item_ Possibly FocusGap

in types

type alias Model =
    RecordWithoutConstructorFunction
        { choice : Scroll Option Never FocusGap
        }

where RecordWithoutConstructorFunction stops the compiler from creating a constructor function for Model.

A word in every Scroll type:

  • 🍍 🍓 <🍊> 🍉 🍇: Scroll ... Never FocusGap

  • 🍍 🍓 <?> 🍉 🍇: Scroll ... Possibly FocusGap

    <?> means both are possible:

    • 🍍 🍓 <> 🍉 🍇: a gap between items ... Heh.
    • 🍍 🍓 <🍊> 🍉 🍇

position

= AtSide DirectionLinear Int
| AtFocus

Position in a Scroll relative to its focus.

import Linear exposing (DirectionLinear(..))
import Hand exposing (Hand, Empty, filled, fillMap, fillMapFlat)
import Stack exposing (topDown)


Scroll.only 0
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.to (Scroll.AtSide Up 2)
    |> fillMap Scroll.focusItem
--> filled 3
--: Hand (Stacked number_) Possibly Empty
nearest : DirectionLinear -> Location

The Location directly Down|Up the focus.

import Hand exposing (Hand, Empty, filled, fillMap)
import Stack exposing (onTopLay, topDown)
import Scroll exposing (Scroll, FocusGap)
import Linear exposing (DirectionLinear(..))


Scroll.only "hello"
    |> Scroll.sideAlter
        ( Up, \_ -> topDown "scrollable" [ "world" ] )
    |> Scroll.toEnd Up
    |> Scroll.to (Down |> Scroll.nearest)
    |> fillMap Scroll.focusItem
--> filled "scrollable"
--: Hand (Scroll String Never FocusGap) Possibly Empty


Scroll.empty
    |> Scroll.sideAlter
        ( Down, \_ -> topDown "world" [ "scrollable" ] )
    |> Scroll.to (Down |> Scroll.nearest)
--> Scroll.only "world"
-->     |> Scroll.sideAlter
-->         ( Down, \_ -> Stack.only "scrollable" )
-->     |> filled
--: Hand (Scroll String Never FocusGap) Possibly Empty


Scroll.empty
    |> Scroll.sideAlter
        ( Up, onTopLay "foo" )
    |> Scroll.to (Up |> Scroll.nearest)
--> filled (Scroll.only "foo")
--: Hand (Scroll String Never FocusGap) Possibly


nearest =
    \side ->
        Scroll.AtSide side 0

create

empty : Scroll item_ Possibly FocusGap

An empty Scroll on a gap with nothing before and after it. It's the loneliest of all Scrolls.

<>
import Hand


Scroll.empty |> Scroll.toStack
--> Hand.empty
only : element -> Scroll element never_ FocusGap

A Scroll with a single focussed item in it, nothing Down and Up it.

🍊  ->  <🍊>
import Stack


Scroll.only "wat" |> Scroll.focusItem
--> "wat"


Scroll.only "wat" |> Scroll.toStack
--> Stack.only "wat"

scan

focusItem : Scroll item Never FocusGap -> item

The focused item.

🍍 🍓 <🍊> 🍉 🍇  ->  🍊
import Stack exposing (topDown)
import Linear exposing (DirectionLinear(..))


Scroll.only "hi there" |> Scroll.focusItem
--> "hi there"


Scroll.only 1
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 2 [ 3, 4 ] )
    |> Scroll.toEnd Up
    |> Scroll.focusItem
--> 4
Scroll item possiblyOrNever FocusGap
-> Hand item possiblyOrNever Empty

The focused item or gap.

🍍 🍓 <🍊> 🍉 🍇  ->  🍊
🍍 🍓 <> 🍉 🍇  ->  _
import Hand exposing (filled, fill)


Scroll.empty |> Scroll.focus
--> Hand.empty


Scroll.only "hi there" |> Scroll.focus |> fill
--> "hi there"

focusItem is short for focus |> fill.

DirectionLinear
-> Scroll item possiblyOrNever_ FocusGap
-> Hand (Stacked item) Possibly Empty

The Stack to one side of the focus.

Down

🍍←🍓) <🍊> 🍉 🍇

Up

🍍 🍓 <🍊> (🍉→🍇
import Hand exposing (Hand, Empty, fillMapFlat)
import Stack exposing (Stacked, topDown)
import Linear exposing (DirectionLinear(..))


Scroll.only 0
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.to (Up |> Scroll.nearest)
    |> fillMapFlat (Scroll.to (Up |> Scroll.nearest))
    |> fillMapFlat (Scroll.side Down)
--> topDown 1 [ 0 ]
--: Hand (Stacked number_) Possibly Empty


Scroll.only 0
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.to (Up |> Scroll.nearest)
    |> fillMapFlat (Scroll.side Up)
--> topDown 2 [ 3 ]
--: Hand (Stacked number_) Possibly Empty
length : Scroll item_ possiblyOrNever_ FocusGap -> Int

Counting all contained items.

import Stack exposing (topDown)
import Linear exposing (DirectionLinear(..))


Scroll.only 0
    |> Scroll.sideAlter
        ( Down, \_ -> topDown -1 [ -2 ] )
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.length
--> 6


Scroll.empty
    |> Scroll.sideAlter
        ( Down, \_ -> topDown -1 [ -2 ] )
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.length
--> 5

move the focus

to :
-> Scroll item possiblyOrNever_ FocusGap
-> Hand (Scroll item never_ FocusGap) Possibly Empty

Try to move the focus to the item at a given Scroll.Location.

Scroll.to (Down |> Scroll.nearest)

🍊 <🍉> 🍇  ->  <🍊> 🍉 🍇
import Linear exposing (DirectionLinear(..))
import Hand exposing (Hand, Empty, filled, fillMap)
import Scroll exposing (Scroll, FocusGap)


Scroll.empty |> Scroll.to (Down |> Scroll.nearest)
--> Hand.empty


Scroll.only "hello"
    |> Scroll.sideAlter
        ( Up, \_ -> topDown "scrollable" [ "world" ] )
    |> Scroll.toEnd Up
    |> Scroll.to (Down |> Scroll.nearest)
    |> fillMap Scroll.focusItem
--> filled "scrollable"
--: Hand (Scroll String Never FocusGap) Possibly Empty

This also works from within gaps:

🍊 🍉 <> 🍇  ->  🍊 <🍉> 🍇
import Linear exposing (DirectionLinear(..))
import Hand exposing (Hand, Empty, filled)
import Stack exposing (onTopLay)
import Scroll exposing (Scroll, FocusGap)


Scroll.empty
    |> Scroll.sideAlter
        ( Down, onTopLay "foo" )
    |> Scroll.to (Down |> Scroll.nearest)
--> filled (Scroll.only "foo")
--: Hand (Scroll String Never FocusGap) Possibly Empty

Scroll.to (Up |> Scroll.nearest)

<🍊> 🍉 🍇  ->  🍊 <🍉> 🍇
import Linear exposing (DirectionLinear(..))
import Hand exposing (Hand, Empty, filled, fillMap)
import Scroll exposing (Scroll, FocusGap)


Scroll.only 0
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.to (Up |> Scroll.nearest)
    |> fillMap Scroll.focusItem
--> filled 1
--: Hand number_ Possibly Empty

This also works from within gaps:

🍊 <> 🍉 🍇  ->  🍊 <🍉> 🍇
import Linear exposing (DirectionLinear(..))
import Hand exposing (Hand, Empty, filled)
import Stack exposing (onTopLay)
import Scroll exposing (Scroll, FocusGap)


Scroll.empty
    |> Scroll.sideAlter
        ( Up, \_ -> Stack.only "foo" )
    |> Scroll.to (Up |> Scroll.nearest)
--> filled (Scroll.only "foo")
--: Hand (Scroll String Never FocusGap) Possibly

If there is no next item, the result is empty.

import Linear exposing (DirectionLinear(..))
import Hand
import Stack exposing (topDown)


Scroll.empty |> Scroll.to (Up |> Scroll.nearest)
--> Hand.empty


Scroll.only 0
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.toEnd Up
    |> Scroll.to (Up |> Scroll.nearest)
--> Hand.empty

Scroll.to Location

import Element as Ui
import Element.Input as UIn
import Hand exposing (fillMap)
import RecordWithoutConstructorFunction exposing (RecordWithoutConstructorFunction)
import Scroll exposing (FocusGap, Scroll)
import Stack


type alias Model =
    RecordWithoutConstructorFunction
        { numbers : Scroll Int Never FocusGap
        }


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
                        |> Hand.fillElseOnEmpty (\_ -> model.numbers)
                        |> Scroll.focusAlter (fillMap (\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) ...

Saving a Scroll with every item becomes expensive for long Scrolls, though!

DirectionLinear
-> Scroll item Never FocusGap
-> Scroll item Possibly FocusGap

Move the focus to the gap directly Down|Up. Feel free to plug that gap right up!

Scroll.toGap Down

🍍 <🍊> 🍉  ->  🍍 <> 🍊 🍉
import Linear exposing (DirectionLinear(..))
import Hand exposing (filled)
import Stack exposing (topDown)


Scroll.only "world"
    |> Scroll.toGap Down
    |> Scroll.focusAlter (\_ -> "hello" |> filled)
    |> Scroll.toStack
--> topDown "hello" [ "world" ]

Scroll.toGap Up

🍍 <🍊> 🍉  ->  🍍 🍊 <> 🍉
import Linear exposing (DirectionLinear(..))
import Hand exposing (filled)
import Stack exposing (topDown)


Scroll.only "hello"
    |> Scroll.sideAlter
        ( Up, \_ -> Stack.only "world" )
    |> Scroll.toGap Up
    |> Scroll.focusAlter (\_ -> filled "scrollable")
    |> Scroll.toStack
--> topDown "hello" [ "scrollable", "world" ]
DirectionLinear
-> Scroll item possiblyOrNever FocusGap
-> Scroll item possiblyOrNever FocusGap

Focus the furthest item Down/Up the focus.

Down

🍍 🍓 <🍊> 🍉  ->  <🍍> 🍓 🍊 🍉

Up

🍓 <🍊> 🍉 🍇  ->  🍓 🍊 🍉 <🍇>
import Linear exposing (DirectionLinear(..))
import Hand exposing (Hand, Empty)
import Stack exposing (Stacked, topDown)


Scroll.only 1
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 2 [ 3, 4 ] )
    |> Scroll.sideAlter
        ( Down, \_ -> topDown 4 [ 3, 2 ] )
    |> Scroll.toEnd Down
    |> Scroll.focusItem
--> 2


Scroll.only 1
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 2 [ 3, 4 ] )
    |> Scroll.toEnd Up
    |> Scroll.focusItem
--> 4


Scroll.only 1
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 2 [ 3, 4 ] )
    |> Scroll.toEnd Up
    |> Scroll.side Down
--> topDown 3 [ 2, 1 ]
--: Hand (Stacked number_) Possibly Empty
DirectionLinear
-> Scroll item possiblyOrNever_ FocusGap
-> Scroll item Possibly FocusGap

Focus the gap beyond the furthest item Down|Up. Remember that gaps surround everything!

Down

🍍 🍓 <🍊> 🍉  ->  <> 🍍 🍓 🍊 🍉

Up

🍓 <🍊> 🍉  ->  🍓 🍊 🍉 <>
import Linear exposing (DirectionLinear(..))
import Hand exposing (filled)
import Stack exposing (topDown)


Scroll.only 1
        -- <1>
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 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
--> topDown 0 [ 1, 2, 3, 4 ]


Scroll.only 1
        -- <1>
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 2 [ 3 ] )
        -- <1> 2 3
    |> Scroll.toEndGap Up
        -- 1 2 3 <>
    |> Scroll.focusAlter (\_ -> filled 4)
        -- 1 2 3 <4>
    |> Scroll.toStack
--> topDown 1 [ 2, 3, 4 ]
( DirectionLinear, { index : Int } -> item -> Bool )
-> Scroll item possiblyOrNever_ FocusGap
-> Hand (Scroll item never_ FocusGap) Possibly Empty

Move the focus to the nearest item Down|Up that matches a predicate. If no such item was found return with Hand.empty.

import Linear exposing (DirectionLinear(..))
import Hand exposing (filled, fillMap)
import Stack exposing (topDown)
import Linear exposing (DirectionLinear(..))


Scroll.only 4
    |> Scroll.sideAlter
        ( Down, \_ -> topDown 2 [ -1, 0, 3 ] )
    |> Scroll.toWhere ( Down, \_ item -> item < 0 )
    |> Hand.fillMap Scroll.focusItem
--> filled -1


Scroll.only 4
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 2 [ -1, 0, 3 ] )
    |> Scroll.toWhere ( Up, \_ item -> item < 0 )
    |> fillMap focusItem
--> filled -1


Scroll.only -4
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 2 [ -1, 0, 3 ] )
    |> Scroll.toWhere ( Up, \_ item -> item < 0 )
    |> fillMap focusItem
--> filled -4
DirectionLinear
-> Scroll item possiblyOrNever FocusGap
-> Hand (Scroll item possiblyOrNever FocusGap) Possibly Empty

Try to move the focus to the nearest item Down|Up.

Down

🍊 🍉 <🍓> 🍇  ->  🍊 <🍓> 🍉 🍇
🍊 🍉 <> 🍇  ->  🍊 <> 🍉 🍇

Up

🍊 <🍓> 🍉 🍇  ->  🍊 🍉 <🍓> 🍇
🍊 <> 🍉 🍇  ->  🍊 🍉 <> 🍇
import Linear exposing (DirectionLinear(..))
import Hand exposing (Hand, Empty, fillMapFlat)
import Scroll exposing (Scroll, FocusGap)


Scroll.only 0
    |> Scroll.sideAlter
        ( Down, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.focusDrag Down
    |> fillMapFlat Scroll.toStack
--> topDown 3 [ 2, 0, 1 ]
--: Hand (Stacked number_) Possibly Empty


Scroll.only 0
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.focusDrag Up
    |> fillMapFlat Scroll.toStack
--> topDown 1 [ 0, 2, 3 ]
--: Hand (Stacked number_) Possibly Empty

If there is no nearest item, the result is empty.

import Linear exposing (DirectionLinear(..))
import Hand
import Stack exposing (topDown)


Scroll.only 0
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.focusDrag Down
--> Hand.empty


Scroll.only 0
    |> Scroll.sideAlter
        ( Down, \_ -> topDown 1 [ 2, 3 ] )
    |> Scroll.focusDrag Up
--> Hand.empty

transform

Scroll item possiblyOrNever FocusGap
-> Hand (Scroll item never_ FocusGap) possiblyOrNever Empty

Hand.empty if the current focussed thing is a gap, Hand.filled if it's an item.

import Hand
import Stack exposing (topDown)
import Linear exposing (DirectionLinear(..))


Scroll.only 3
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 2 [ 1 ] )
    |> Scroll.toGap Up
    |> Scroll.focusItemTry
--> Hand.empty
map :
(Location -> item -> mappedItem)
-> Scroll item possiblyOrNever FocusGap
-> Scroll mappedItem possiblyOrNever FocusGap

Change every item based on its current value.

import Linear exposing (DirectionLinear(..))
import Stack exposing (topDown)


Scroll.only "first"
    |> Scroll.sideAlter
        ( Down, \_ -> Stack.only "zeroth" )
    |> Scroll.sideAlter
        ( Up, \_ -> topDown "second" [ "third" ] )
    |> Scroll.map (\_ -> String.toUpper)
    |> Scroll.toStack
--> topDown "ZEROTH" [ "FIRST", "SECOND", "THIRD" ]

focusSidesMap allows changing the individual parts separately.

{ focus :
Hand item possiblyOrNever Empty
-> Hand mappedItem possiblyOrNeverMapped Empty
, side :
DirectionLinear
-> Hand (Stacked item) Possibly Empty
-> Hand (Stacked mappedItem) possiblyOrNeverMappedBefore_ Empty
}
-> Scroll item possiblyOrNever FocusGap
-> Scroll mappedItem possiblyOrNeverMapped FocusGap

Change the focus, the sides Down and Up using different functions.

import Linear exposing (DirectionLinear(..))
import Hand exposing (filled, fillMap)
import Stack exposing (topDown)


Scroll.only "first"
    |> Scroll.sideAlter
        ( Up, \_ -> Stack.only "second" )
    |> Scroll.toGap Up
    |> Scroll.focusAlter (\_ -> filled "one-and-a-halfth")
    |> Scroll.focusSidesMap
        { side =
            \side ->
                Stack.map
                    (\_ item ->
                        String.concat
                            [ side |> sideToString, ": ", item ]
                    )
        , focus =
            fillMap (\item -> "focused item: " ++ item)
        }
    |> Scroll.toStack
--→
topDown
    "before: first"
    [ "focused item: one-and-a-halfth"
    , "after: second"
    ]


sideToString =
    \side ->
        case side of
            Down ->
                "before"


            Up ->
                "after"

map transforms every item.

( accumulationValue
, DirectionLinear
, item -> accumulationValue -> accumulationValue
)
-> Scroll item Never FocusGap
-> accumulationValue

Reduce in a direction.

import Linear exposing (DirectionLinear(..))
import Stack exposing (topDown)


Scroll.only 'e'
    |> Scroll.sideAlter
        ( Down, \_ -> topDown 'v' [ 'i', 'l' ] )
    |> Scroll.foldFrom ( "", Down, String.cons )
--> "live"


Scroll.only 'e'
    |> Scroll.sideAlter
        ( Down, \_ -> topDown 'v' [ 'i', 'l' ] )
    |> Scroll.foldFrom ( "", Up, String.cons )
--> "evil"
( DirectionLinear, item -> item -> item )
-> Scroll item Never FocusGap
-> item

Fold in a direction from the first item Up|Down as the initial accumulation value.

import Linear exposing (DirectionLinear(..))
import Stack exposing (topDown)


Scroll.only 234
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 345 [ 543 ] )
    |> Scroll.fold ( Up, max )
--> 543
Scroll item possiblyOrNever FocusGap
-> Hand (Stacked item) possiblyOrNever Empty

Roll out the Scroll to both ends into a Stack:

import Linear exposing (DirectionLinear(..))
import Hand exposing (filled)
import Stack exposing (topDown)


Scroll.empty
    |> Scroll.toStack
--> Hand.empty


Scroll.only 123
    |> Scroll.sideAlter
        ( Up, \_ -> Stack.only 789 )
    |> Scroll.toGap Up
    |> Scroll.focusAlter (\_-> filled 456)
    |> Scroll.toStack
--> topDown 123 [ 456, 789 ]

the type information gets carried over, so

Never Scroll.FocusGap -> Stacked Never Empty
Possibly Scroll.FocusGap -> Stacked Possibly Empty
toList : Scroll item possiblyOrNever_ FocusGap -> List item

Converts it to a List, rolled out to both ends:

import Linear exposing (DirectionLinear(..))
import Stack


Scroll.only 456
    |> Scroll.sideAlter
        ( Down, \_ -> Stack.only 123 )
    |> Scroll.sideAlter
        ( Up, \_ -> Stack.only 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.

alter

Scroll item possiblyOrNever FocusGap
-> Scroll item possiblyOrNever FocusGap

Swap the stack on the side Down the focus with the stack on the side Up.

🍓 <🍊> 🍉 🍇  <->  🍇 🍉 <🍊> 🍓
import Linear exposing (DirectionLinear(..))
import Stack exposing (topDown)


Scroll.only 1
    |> Scroll.sideAlter
        ( Up, \_ -> topDown 2 [ 3, 4 ] )
    |> Scroll.sideAlter
        ( Down, \_ -> topDown 4 [ 3, 2 ] )

In contrast to List or stack, this can be done in O(1) time.

( Hand item possiblyOrNever Empty
-> Hand item possiblyOrNeverAltered Empty
)
-> Scroll item possiblyOrNever FocusGap
-> Scroll item possiblyOrNeverAltered FocusGap

Alter the focus – item or gap – based on its current value.

Scroll.focusAlter (\_ -> 🍊 |> filled)

🍊  ->  🍓 <?> 🍉  ->  🍓 <🍊> 🍉
import Linear exposing (DirectionLinear(..))
import Hand exposing (filled, fillMap)
import Stack exposing (topDown, onTopLay)


Scroll.empty
        -- <>
    |> Scroll.sideAlter
        ( Down, onTopLay "🍓" )
        -- "🍓" <>
    |> Scroll.sideAlter
        ( Up, onTopLay "🍉" )
        -- "🍓" <> "🍉"
    |> Scroll.focusAlter (\_ -> "🍊" |> filled)
        -- "🍓" <"🍊"> "🍉"
    |> Scroll.toStack
--> topDown "🍓" [ "🍊", "🍉" ]

Scroll.focusAlter (\_ -> Hand.empty)

🍓 <?> 🍉  ->  🍓 <> 🍉
import Linear exposing (DirectionLinear(..))
import Hand exposing (filled, fillMap)
import Stack exposing (topDown)


Scroll.only "hello"
    |> Scroll.sideAlter
        ( Up, \_ -> topDown "scrollable" [ "world" ] )
    |> Scroll.to (Up |> Scroll.nearest)
    |> fillMap (Scroll.focusAlter (\_ -> Hand.empty))
    |> fillMap Scroll.toList
--> filled [ "hello", "world" ]

Scroll.focusAlter (?🍒 -> ?🍊)

(?🍒 -> ?🍊)  ->  🍓 <?🍒> 🍉  ->  🍓 <?🍊> 🍉
import Linear exposing (DirectionLinear(..))
import Hand exposing (filled, fillMap)
import Stack exposing (topDown, onTopLay)


Scroll.empty
        -- <>
    |> Scroll.sideAlter
        ( Down, onTopLay "🍓" )
        -- "🍓" <>
    |> Scroll.sideAlter
        ( Up, onTopLay "🍉" )
        -- "🍓" <> "🍉"
    |> Scroll.focusAlter
        (\_ -> filled "🍊")
        -- "🍓" <"🍊"> "🍉"
    |> Scroll.toStack
--> topDown "🍓" [ "🍊", "🍉" ]


Scroll.only "first"
    |> Scroll.sideAlter
        ( Down, \_ -> Stack.only "zeroth" )
    |> Scroll.sideAlter
        ( Up, \_ -> topDown "second" [ "third" ] )
    |> Scroll.focusAlter (fillMap String.toUpper)
    |> Scroll.toStack
--> topDown "zeroth" [ "FIRST", "second", "third" ]
( DirectionLinear
, Hand (Stacked item) Possibly Empty
-> Hand (Stacked item) possiblyOrNever_ Empty
)
-> Scroll item possiblyOrNever FocusGap
-> Scroll item possiblyOrNever FocusGap

Look Down|Up the focus and operate directly an the Stack you see.

sideAlter ( DirectionLinear, \_ -> 🍒🍋 )

Down

🍓 <🍊> 🍉
      ↓
🍋 🍒 <🍊> 🍉

Up

🍍 🍓 <🍊> 🍉
      ↓
🍍 🍓 <🍊> 🍒 🍋
import Linear exposing (DirectionLinear(..))
import Hand
import Stack exposing (topDown)


Scroll.only "selectoo"
    |> Scroll.sideAlter
        ( Down, \_ -> topDown "earlee" [ "agua", "enutai" ] )
    |> Scroll.sideAlter
        ( Up, \_ -> topDown "orangloo" [ "iquipy", "oice" ] )
    |> Scroll.sideAlter
        ( Up, \_ -> Hand.empty )
    |> Scroll.toStack
--> topDown "enutai" [ "agua", "earlee", "selectoo" ]

sideAlter ( DirectionLinear, Stack.map ... )

import Linear exposing (DirectionLinear(..))
import Stack exposing (topDown)


Scroll.only "second"
    |> Scroll.sideAlter
        ( Down, \_ -> topDown "first" [ "zeroth" ] )
    |> Scroll.sideAlter
        ( Down, Stack.map (\_ -> String.toUpper) )
    |> Scroll.toStack
--> topDown "ZEROTH" [ "FIRST", "second" ]


Scroll.only "zeroth"
    |> Scroll.sideAlter
        ( Up, \_ -> topDown "first" [ "second" ] )
    |> Scroll.sideAlter
        ( Up, Stack.map (\_ -> String.toUpper) )
    |> Scroll.toStack
--> topDown "zeroth" [ "FIRST", "SECOND" ]

Look to one side from the focus and slide items in directly at the nearest location.

sideAlter ( DirectionLinear, Stack.onTopGlue/onTopStack 🍒🍋 )

Down

      🍒🍋
🍍 🍓 \↓/ <🍊> 🍉

Up

        🍒🍋
🍓 <🍊> \↓/ 🍉 🍇
import Linear exposing (DirectionLinear(..))
import Stack exposing (topDown)


Scroll.only 0
    |> Scroll.sideAlter
        ( Down, Stack.onTopGlue [ -4, -5 ] )
    |> Scroll.sideAlter
        ( Down, Stack.onTopStack (topDown -1 [ -2, -3 ]) )
    |> Scroll.toStack
--> topDown -5 [ -4, -3, -2, -1, 0 ]


Scroll.only 0
    |> Scroll.sideAlter
        ( Up, Stack.onTopGlue [ 4, 5 ] )
    |> Scroll.sideAlter
        ( Up, Stack.onTopStack (topDown 1 [ 2, 3 ]) )
    |> Scroll.toStack
--> topDown 0 [ 1, 2, 3, 4, 5 ]

Scroll.sideAlter ( DirectionLinear, \side -> 🍒🍋 |> onTopStack side )

Down

🍋🍒
 \↓ 🍍 🍓 <🍊> 🍉

Up

              🍒🍋
🍓 <🍊> 🍉 🍇 ↓/
import Linear exposing (DirectionLinear(..))
import Stack exposing (topDown, onTopStack)


Scroll.only 1
    |> Scroll.sideAlter
        ( Up, \after -> topDown 2 [ 3, 4 ] |> onTopStack after )
    |> Scroll.toEnd Up
    |> Scroll.sideAlter
        ( Down, \before -> topDown 7 [ 6, 5 ] |> onTopStack before )
    |> Scroll.toStack
--> topDown 5 [ 6, 7, 1, 2, 3, 4 ]


Scroll.only 123
    |> Scroll.sideAlter
        ( Up, \after -> Stack.only 456 |> onTopStack after )
    |> Scroll.sideAlter
        ( Up, \after -> topDown 789 [ 0 ] |> onTopStack after )
    |> Scroll.toStack
--> topDown 123 [ 456, 789, 0 ]

sideAlter ( DirectionLinear,Stack.onTopLay ... )

Down

      🍒
🍍 🍓 ↓ <🍊> 🍉

Up

        🍒
🍓 <🍊> ↓ 🍉 🍇
import Linear exposing (DirectionLinear(..))
import Stack exposing (topDown, onTopLay)


Scroll.only 123
    |> Scroll.sideAlter
        ( Down, onTopLay 456 )
    |> Scroll.toStack
--> topDown 456 [ 123 ]


Scroll.only 123
    |> Scroll.sideAlter
        ( Up, \_ -> Stack.only 789 )
    |> Scroll.sideAlter
        ( Up, onTopLay 456 )
    |> Scroll.toStack
--> topDown 123 [ 456, 789 ]

type-level

(possiblyOrNever -> adaptedPossiblyOrNever)
-> Scroll item possiblyOrNever FocusGap
-> Scroll item adaptedPossiblyOrNever FocusGap

Change the possiblyOrNever type

  • A Scroll ... possiblyOrNever FocusGap can't be used as a Scroll ... Possibly?

    import Possibly exposing (Possibly(..))
    
    
    Scroll.focusGapAdapt (always Possible)
  • A Scroll ... Never FocusGap can't be unified with Scroll ... Possibly or possiblyOrNever FocusGap?

    Scroll.focusGapAdapt never

Please read more at Hand.emptyAdapt.