@wardic/solid 1.0.0-rc.4 → 1.0.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/bridge.js +1 -1
- package/dist/browser/bridge.js.map +1 -1
- package/dist/browser/create-ward.js +1 -1
- package/dist/browser/create-ward.js.map +1 -1
- package/dist/browser/feature.js +10 -0
- package/dist/browser/feature.js.map +1 -1
- package/dist/browser/features/input.js +2 -5
- package/dist/browser/features/input.js.map +1 -1
- package/dist/browser/features/scroll.js +21 -0
- package/dist/browser/features/scroll.js.map +1 -0
- package/dist/browser/index.js +4 -2
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/layout.js +1 -1
- package/dist/browser/layout.js.map +1 -1
- package/dist/browser/live-scroll.js +115 -0
- package/dist/browser/live-scroll.js.map +1 -0
- package/dist/browser/live-style.js +2 -1
- package/dist/browser/live-style.js.map +1 -1
- package/dist/browser/live-value.js +12 -98
- package/dist/browser/live-value.js.map +1 -1
- package/dist/browser/reorder.js +14 -10
- package/dist/browser/reorder.js.map +1 -1
- package/dist/browser/runtime.js +1 -1
- package/dist/browser/runtime.js.map +1 -1
- package/dist/browser/spine.js +1 -1
- package/dist/browser/spine.js.map +1 -1
- package/dist/browser/unstable/v2/bridge.js +1 -1
- package/dist/browser/unstable/v2/bridge.js.map +1 -1
- package/dist/browser/unstable/v2/index.js +3 -1
- package/dist/browser/unstable/v2/index.js.map +1 -1
- package/dist/browser/unstable/v2/layout.js +1 -1
- package/dist/browser/unstable/v2/layout.js.map +1 -1
- package/dist/browser/unstable/v2/live-scroll.js +128 -0
- package/dist/browser/unstable/v2/live-scroll.js.map +1 -0
- package/dist/browser/unstable/v2/live-value.js +7 -105
- package/dist/browser/unstable/v2/live-value.js.map +1 -1
- package/dist/browser/unstable/v2/provider.js +13 -7
- package/dist/browser/unstable/v2/provider.js.map +1 -1
- package/dist/browser/unstable/v2/ward.js +1 -1
- package/dist/browser/unstable/v2/ward.js.map +1 -1
- package/dist/server/bridge.js +1 -1
- package/dist/server/bridge.js.map +1 -1
- package/dist/server/create-ward.js +1 -1
- package/dist/server/create-ward.js.map +1 -1
- package/dist/server/feature.js +10 -0
- package/dist/server/feature.js.map +1 -1
- package/dist/server/features/input.js +2 -5
- package/dist/server/features/input.js.map +1 -1
- package/dist/server/features/scroll.js +21 -0
- package/dist/server/features/scroll.js.map +1 -0
- package/dist/server/index.js +4 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/layout.js +1 -1
- package/dist/server/layout.js.map +1 -1
- package/dist/server/live-scroll.js +115 -0
- package/dist/server/live-scroll.js.map +1 -0
- package/dist/server/live-style.js +2 -1
- package/dist/server/live-style.js.map +1 -1
- package/dist/server/live-value.js +12 -98
- package/dist/server/live-value.js.map +1 -1
- package/dist/server/reorder.js +14 -10
- package/dist/server/reorder.js.map +1 -1
- package/dist/server/runtime.js +1 -1
- package/dist/server/runtime.js.map +1 -1
- package/dist/server/spine.js +1 -1
- package/dist/server/spine.js.map +1 -1
- package/dist/server/unstable/v2/bridge.js +1 -1
- package/dist/server/unstable/v2/bridge.js.map +1 -1
- package/dist/server/unstable/v2/index.js +3 -1
- package/dist/server/unstable/v2/index.js.map +1 -1
- package/dist/server/unstable/v2/layout.js +1 -1
- package/dist/server/unstable/v2/layout.js.map +1 -1
- package/dist/server/unstable/v2/live-scroll.js +128 -0
- package/dist/server/unstable/v2/live-scroll.js.map +1 -0
- package/dist/server/unstable/v2/live-value.js +7 -105
- package/dist/server/unstable/v2/live-value.js.map +1 -1
- package/dist/server/unstable/v2/provider.js +13 -7
- package/dist/server/unstable/v2/provider.js.map +1 -1
- package/dist/server/unstable/v2/ward.js +1 -1
- package/dist/server/unstable/v2/ward.js.map +1 -1
- package/dist/solid/bridge.d.ts +1 -1
- package/dist/solid/bridge.d.ts.map +1 -1
- package/dist/solid/bridge.js +1 -1
- package/dist/solid/bridge.js.map +1 -1
- package/dist/solid/create-ward.d.ts +1 -1
- package/dist/solid/create-ward.d.ts.map +1 -1
- package/dist/solid/create-ward.jsx +1 -1
- package/dist/solid/create-ward.jsx.map +1 -1
- package/dist/solid/feature.d.ts +5 -0
- package/dist/solid/feature.d.ts.map +1 -1
- package/dist/solid/feature.js +10 -0
- package/dist/solid/feature.js.map +1 -1
- package/dist/solid/features/input.d.ts.map +1 -1
- package/dist/solid/features/input.js +2 -6
- package/dist/solid/features/input.js.map +1 -1
- package/dist/solid/features/scroll.d.ts +16 -0
- package/dist/solid/features/scroll.d.ts.map +1 -0
- package/dist/solid/features/scroll.js +21 -0
- package/dist/solid/features/scroll.js.map +1 -0
- package/dist/solid/index.d.ts +5 -2
- package/dist/solid/index.d.ts.map +1 -1
- package/dist/solid/index.js +4 -2
- package/dist/solid/index.js.map +1 -1
- package/dist/solid/layout.jsx +1 -1
- package/dist/solid/layout.jsx.map +1 -1
- package/dist/solid/live-scroll.d.ts +47 -0
- package/dist/solid/live-scroll.d.ts.map +1 -0
- package/dist/solid/live-scroll.js +84 -0
- package/dist/solid/live-scroll.js.map +1 -0
- package/dist/solid/live-style.d.ts +2 -1
- package/dist/solid/live-style.d.ts.map +1 -1
- package/dist/solid/live-style.js +2 -1
- package/dist/solid/live-style.js.map +1 -1
- package/dist/solid/live-value.d.ts +4 -45
- package/dist/solid/live-value.d.ts.map +1 -1
- package/dist/solid/live-value.js +13 -67
- package/dist/solid/live-value.js.map +1 -1
- package/dist/solid/reorder.d.ts.map +1 -1
- package/dist/solid/reorder.jsx +11 -6
- package/dist/solid/reorder.jsx.map +1 -1
- package/dist/solid/runtime.d.ts +1 -1
- package/dist/solid/runtime.d.ts.map +1 -1
- package/dist/solid/runtime.js +1 -1
- package/dist/solid/runtime.js.map +1 -1
- package/dist/solid/spine.d.ts +1 -1
- package/dist/solid/spine.d.ts.map +1 -1
- package/dist/solid/spine.js +1 -1
- package/dist/solid/spine.js.map +1 -1
- package/dist/solid/unstable/v2/bridge.d.ts +2 -1
- package/dist/solid/unstable/v2/bridge.d.ts.map +1 -1
- package/dist/solid/unstable/v2/bridge.js +1 -1
- package/dist/solid/unstable/v2/bridge.js.map +1 -1
- package/dist/solid/unstable/v2/index.d.ts +4 -2
- package/dist/solid/unstable/v2/index.d.ts.map +1 -1
- package/dist/solid/unstable/v2/index.js +3 -1
- package/dist/solid/unstable/v2/index.js.map +1 -1
- package/dist/solid/unstable/v2/layout.jsx +1 -1
- package/dist/solid/unstable/v2/layout.jsx.map +1 -1
- package/dist/solid/unstable/v2/live-scroll.d.ts +47 -0
- package/dist/solid/unstable/v2/live-scroll.d.ts.map +1 -0
- package/dist/solid/unstable/v2/live-scroll.js +94 -0
- package/dist/solid/unstable/v2/live-scroll.js.map +1 -0
- package/dist/solid/unstable/v2/live-value.d.ts +4 -45
- package/dist/solid/unstable/v2/live-value.d.ts.map +1 -1
- package/dist/solid/unstable/v2/live-value.js +7 -71
- package/dist/solid/unstable/v2/live-value.js.map +1 -1
- package/dist/solid/unstable/v2/provider.d.ts.map +1 -1
- package/dist/solid/unstable/v2/provider.jsx +14 -7
- package/dist/solid/unstable/v2/provider.jsx.map +1 -1
- package/dist/solid/unstable/v2/ward.d.ts +1 -1
- package/dist/solid/unstable/v2/ward.d.ts.map +1 -1
- package/dist/solid/unstable/v2/ward.jsx +1 -1
- package/dist/solid/unstable/v2/ward.jsx.map +1 -1
- package/dist/solid/ward-keys.d.ts +1 -1
- package/dist/solid/ward-keys.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/bridge.ts +2 -1
- package/src/create-ward.tsx +1 -1
- package/src/feature.ts +17 -0
- package/src/features/input.ts +2 -10
- package/src/features/scroll.ts +21 -0
- package/src/index.ts +4 -5
- package/src/layout.tsx +1 -1
- package/src/live-scroll.ts +125 -0
- package/src/live-style.ts +2 -1
- package/src/live-value.ts +13 -107
- package/src/reorder.tsx +11 -6
- package/src/runtime.ts +1 -1
- package/src/spine.ts +1 -1
- package/src/unstable/v2/bridge.ts +2 -1
- package/src/unstable/v2/index.ts +3 -5
- package/src/unstable/v2/layout.tsx +1 -1
- package/src/unstable/v2/live-scroll.ts +137 -0
- package/src/unstable/v2/live-value.ts +7 -112
- package/src/unstable/v2/provider.tsx +19 -13
- package/src/unstable/v2/ward.tsx +1 -1
- package/src/ward-keys.ts +1 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createScroll` (#518): in a module of its own, so it brings the `scroll` Feature only to an app that
|
|
3
|
+
* reads a scroll, and on the pages that do (ADR-0067, *Amendments (#548, #518)*).
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import * as LiveValue from "@wardic/core/LiveValue"
|
|
8
|
+
import * as ScrollSource from "@wardic/core/ScrollSource"
|
|
9
|
+
import type * as ScrollOffset from "@wardic/dom/ScrollOffset"
|
|
10
|
+
import * as Arr from "effect/Array"
|
|
11
|
+
import * as Bool from "effect/Boolean"
|
|
12
|
+
import * as Effect from "effect/Effect"
|
|
13
|
+
import * as Option from "effect/Option"
|
|
14
|
+
import * as Predicate from "effect/Predicate"
|
|
15
|
+
import * as AtomRegistry from "effect/reactivity/AtomRegistry"
|
|
16
|
+
import * as Scope from "effect/Scope"
|
|
17
|
+
import * as UndefinedOr from "effect/UndefinedOr"
|
|
18
|
+
import { type Accessor, createEffect, createSignal, onCleanup } from "solid-js"
|
|
19
|
+
import type { Feature } from "./feature.ts"
|
|
20
|
+
import { scroll } from "./features/scroll.ts"
|
|
21
|
+
import { serviceOf } from "./gesture-features.ts"
|
|
22
|
+
import { CLOSE_FAILED, SERVER_ZERO } from "./live-value.ts"
|
|
23
|
+
import * as OwnerScope from "./owner-scope.ts"
|
|
24
|
+
import { useBrought, useWardicRuntime } from "./provider.tsx"
|
|
25
|
+
import { isServer } from "./server.ts"
|
|
26
|
+
|
|
27
|
+
/** What it brings (ADR-0067 decision 12): the `scroll` Feature, whatever its section has (#518). */
|
|
28
|
+
const SCROLL: ReadonlyArray<Feature> = [scroll]
|
|
29
|
+
|
|
30
|
+
/** An element, or an accessor of one, such as `() => box` for a ref: read once it's there. */
|
|
31
|
+
export type ElementSource = Element | Accessor<Element | undefined>
|
|
32
|
+
|
|
33
|
+
/** `createScroll`'s options, as Motion's `useScroll`'s. */
|
|
34
|
+
export interface ScrollOptions {
|
|
35
|
+
/** The scrolling element, as an accessor of its ref: `() => box`. The page's scroll when left out. */
|
|
36
|
+
readonly container?: ElementSource
|
|
37
|
+
/** An element whose way through the container the progress reads, between the `offset`s. */
|
|
38
|
+
readonly target?: ElementSource
|
|
39
|
+
/**
|
|
40
|
+
* Where the progress runs, as Motion's: each stop pairs an edge of the target with an edge of the
|
|
41
|
+
* container, `["start end", "end start"]`, 0 at the first and 1 at the last. An edge is `start`,
|
|
42
|
+
* `center` or `end`, a fraction, or a length. With a target, `["start start", "end end"]` by default.
|
|
43
|
+
*/
|
|
44
|
+
// The DOM's syntax (`@wardic/dom/ScrollOffset`); with a target and none given, its `All`.
|
|
45
|
+
readonly offset?: ScrollOffset.ScrollOffset
|
|
46
|
+
/** The axis `target` and `offset` read: `"y"` (the default) or `"x"`. */
|
|
47
|
+
readonly axis?: "x" | "y"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** `createScroll`'s values, Motion's four, each an accessor. */
|
|
51
|
+
export interface ScrollValues {
|
|
52
|
+
/** How far it's scrolled across, in pixels. */
|
|
53
|
+
readonly scrollX: Accessor<number>
|
|
54
|
+
/** How far it's scrolled down, in pixels. */
|
|
55
|
+
readonly scrollY: Accessor<number>
|
|
56
|
+
/** Its progress across, from 0 to 1: through its range, or, with a `target` or `offset`, through theirs. */
|
|
57
|
+
readonly scrollXProgress: Accessor<number>
|
|
58
|
+
/** Its progress down, from 0 to 1: through its range, or, with a `target` or `offset`, through theirs. */
|
|
59
|
+
readonly scrollYProgress: Accessor<number>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const SERVER_SCROLL: ScrollValues = { scrollX: SERVER_ZERO, scrollY: SERVER_ZERO, scrollXProgress: SERVER_ZERO, scrollYProgress: SERVER_ZERO }
|
|
63
|
+
|
|
64
|
+
/** An element source read now: `None` for an accessor whose ref hasn't arrived. */
|
|
65
|
+
const elementOf = (source: ElementSource): Option.Option<Element> =>
|
|
66
|
+
Bool.match(Predicate.isFunction(source), {
|
|
67
|
+
onTrue: () => Option.fromUndefinedOr((source as Accessor<Element | undefined>)()),
|
|
68
|
+
onFalse: () => Option.some(source as Element),
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A scroll as values: `scrollX` and `scrollY`, how far it's scrolled, in pixels, and `scrollXProgress`
|
|
73
|
+
* and `scrollYProgress`, from 0 to 1 through its range. The page's, or a `container`'s. With a
|
|
74
|
+
* `target`, the progress is the target's way through, between two `offset`s, on `axis`. Each is set in
|
|
75
|
+
* the scroll's own event, so a value derived from one paints in the frame that shows the scroll. A
|
|
76
|
+
* `container` or `target` given as an accessor of a ref reads 0s until the ref is there. 0s on a
|
|
77
|
+
* server. Motion's `useScroll`.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* let box!: HTMLDivElement
|
|
81
|
+
* const { scrollYProgress } = createScroll({ container: () => box })
|
|
82
|
+
* <div ref={box}>…</div>
|
|
83
|
+
* <w.div style={{ scaleX: scrollYProgress }} />
|
|
84
|
+
* @see https://wardic.dev/reference/solid/live-values/create-scroll
|
|
85
|
+
*/
|
|
86
|
+
// #234. An accessor `container` or `target` is read in an effect, and a change of either follows the
|
|
87
|
+
// new one.
|
|
88
|
+
export const createScroll = (options: ScrollOptions = {}): ScrollValues => {
|
|
89
|
+
const runtime = useWardicRuntime()
|
|
90
|
+
if (isServer) return SERVER_SCROLL
|
|
91
|
+
const services = useBrought(SCROLL)
|
|
92
|
+
const [scrollX, setScrollX] = createSignal(0)
|
|
93
|
+
const [scrollY, setScrollY] = createSignal(0)
|
|
94
|
+
const [scrollXProgress, setScrollXProgress] = createSignal(0)
|
|
95
|
+
const [scrollYProgress, setScrollYProgress] = createSignal(0)
|
|
96
|
+
createEffect(() => {
|
|
97
|
+
const container = UndefinedOr.match(options.container, { onUndefined: () => Option.some(undefined), onDefined: elementOf })
|
|
98
|
+
const target = UndefinedOr.match(options.target, { onUndefined: () => Option.some(undefined), onDefined: elementOf })
|
|
99
|
+
// A ref not there yet: the values hold until it is.
|
|
100
|
+
if (Bool.or(Option.isNone(container), Option.isNone(target))) return
|
|
101
|
+
const registry = runtime.runSync(AtomRegistry.AtomRegistry)
|
|
102
|
+
const scope = OwnerScope.open(runtime)
|
|
103
|
+
onCleanup(() => OwnerScope.close(runtime, scope, CLOSE_FAILED))
|
|
104
|
+
const values = runtime.runSync(
|
|
105
|
+
LiveValue.scroll({
|
|
106
|
+
...UndefinedOr.match(Option.getOrUndefined(container), { onUndefined: () => ({}), onDefined: (element) => ({ container: element }) }),
|
|
107
|
+
...UndefinedOr.match(Option.getOrUndefined(target), { onUndefined: () => ({}), onDefined: (element) => ({ target: element }) }),
|
|
108
|
+
...UndefinedOr.match(options.offset, { onUndefined: () => ({}), onDefined: (offset) => ({ offset }) }),
|
|
109
|
+
...UndefinedOr.match(options.axis, { onUndefined: () => ({}), onDefined: (axis) => ({ axis }) }),
|
|
110
|
+
}).pipe(Effect.provideService(ScrollSource.ScrollSource, serviceOf(services(), ScrollSource.ScrollSource)), Scope.provide(scope)),
|
|
111
|
+
)
|
|
112
|
+
// Each derived Atom is read at once: it tracks the scroll only once it's been read.
|
|
113
|
+
const stops = Arr.map(
|
|
114
|
+
[
|
|
115
|
+
[values.scrollX, setScrollX],
|
|
116
|
+
[values.scrollY, setScrollY],
|
|
117
|
+
[values.scrollXProgress, setScrollXProgress],
|
|
118
|
+
[values.scrollYProgress, setScrollYProgress],
|
|
119
|
+
] as const,
|
|
120
|
+
([atom, set]) => registry.subscribe(atom, (v) => void set(v), { immediate: true }),
|
|
121
|
+
)
|
|
122
|
+
onCleanup(() => Arr.forEach(stops, (stop) => stop()))
|
|
123
|
+
})
|
|
124
|
+
return { scrollX, scrollY, scrollXProgress, scrollYProgress }
|
|
125
|
+
}
|
package/src/live-style.ts
CHANGED
|
@@ -28,7 +28,8 @@ import * as Bool from "effect/Boolean"
|
|
|
28
28
|
import { constVoid } from "effect/Function"
|
|
29
29
|
import * as Option from "effect/Option"
|
|
30
30
|
import * as Predicate from "effect/Predicate"
|
|
31
|
-
import
|
|
31
|
+
import * as Atom from "effect/reactivity/Atom"
|
|
32
|
+
import * as AtomRegistry from "effect/reactivity/AtomRegistry"
|
|
32
33
|
import * as Rec from "effect/Record"
|
|
33
34
|
import * as UndefinedOr from "effect/UndefinedOr"
|
|
34
35
|
import type { WardicInertia } from "./config.ts"
|
package/src/live-value.ts
CHANGED
|
@@ -21,17 +21,15 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import type * as InterpolatorRegistry from "@wardic/core/InterpolatorRegistry"
|
|
23
23
|
import * as LiveValue from "@wardic/core/LiveValue"
|
|
24
|
-
import type * as ScrollOffset from "@wardic/dom/ScrollOffset"
|
|
25
24
|
import * as Bool from "effect/Boolean"
|
|
26
25
|
import * as Effect from "effect/Effect"
|
|
27
26
|
import { identity } from "effect/Function"
|
|
28
27
|
import * as Predicate from "effect/Predicate"
|
|
29
|
-
import
|
|
28
|
+
import * as Atom from "effect/reactivity/Atom"
|
|
29
|
+
import * as AtomRegistry from "effect/reactivity/AtomRegistry"
|
|
30
30
|
import * as UndefinedOr from "effect/UndefinedOr"
|
|
31
31
|
import * as Scope from "effect/Scope"
|
|
32
|
-
import
|
|
33
|
-
import * as Option from "effect/Option"
|
|
34
|
-
import { type Accessor, createComputed, createEffect, createMemo, createSignal, on, onCleanup, untrack } from "solid-js"
|
|
32
|
+
import { type Accessor, createComputed, createMemo, on, onCleanup, untrack } from "solid-js"
|
|
35
33
|
import { eachFrameOf } from "./animation-frame.ts"
|
|
36
34
|
import { fromAtom } from "./bridge.ts"
|
|
37
35
|
import type { FollowAccessor, FollowValueOptions, LiveSource, SpringAccessor, WillChange } from "./live-style.ts"
|
|
@@ -45,14 +43,16 @@ import { withServerInterpolators } from "./server-interpolators.ts"
|
|
|
45
43
|
/** A Live value as an accessor: an accessor as it is, an Atom read through the provider's registry. */
|
|
46
44
|
const accessorOf = <A>(source: LiveSource<A>): Accessor<A> =>
|
|
47
45
|
Bool.match(Atom.isAtom(source), {
|
|
48
|
-
onTrue: () =>
|
|
49
|
-
Bool.match(isServer, {
|
|
50
|
-
onTrue: (): Accessor<A> => () => readLive(source, serverRegistryOf, untrack) as A,
|
|
51
|
-
onFalse: () => fromAtom(source as Atom.Atom<A>),
|
|
52
|
-
}),
|
|
46
|
+
onTrue: () => atomAccessorOf(source as Atom.Atom<A>),
|
|
53
47
|
onFalse: () => source as Accessor<A>,
|
|
54
48
|
})
|
|
55
49
|
|
|
50
|
+
/** An Atom as an accessor: on a server, read once; on the client, followed. A plain `if`, so a client build drops the server's read (#518). */
|
|
51
|
+
const atomAccessorOf = <A>(atom: Atom.Atom<A>): Accessor<A> => {
|
|
52
|
+
if (isServer) return () => readLive(atom, serverRegistryOf, untrack) as A
|
|
53
|
+
return fromAtom(atom)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
56
|
/**
|
|
57
57
|
* A value mapped from another: through a function, `createTransform(x, (x) => x * 2)`, or through
|
|
58
58
|
* ranges, `createTransform(x, input, output)`: a source at `input[i]` gives `output[i]`, and one between
|
|
@@ -225,7 +225,8 @@ const liveOf = <A, R>(runtime: WardicRuntime, live: Effect.Effect<Atom.Atom<A>,
|
|
|
225
225
|
return carry(fromAtom(out), out)
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
/** A server render's value of a Live helper: 0, and no Atom. Internal. */
|
|
229
|
+
export const SERVER_ZERO: Accessor<number> = () => 0
|
|
229
230
|
|
|
230
231
|
/**
|
|
231
232
|
* A `will-change` for an element's `style`: `"auto"` until the element first animates a transform key,
|
|
@@ -270,101 +271,6 @@ export const createAnimationFrame = (callback: (time: number, delta: number) =>
|
|
|
270
271
|
runtime.runSync(eachFrameOf(runtime, callback).pipe(Scope.provide(scope)))
|
|
271
272
|
}
|
|
272
273
|
|
|
273
|
-
/** An element, or an accessor of one, such as `() => box` for a ref: read once it's there. */
|
|
274
|
-
export type ElementSource = Element | Accessor<Element | undefined>
|
|
275
|
-
|
|
276
|
-
/** `createScroll`'s options, as Motion's `useScroll`'s. */
|
|
277
|
-
export interface ScrollOptions {
|
|
278
|
-
/** The scrolling element, as an accessor of its ref: `() => box`. The page's scroll when left out. */
|
|
279
|
-
readonly container?: ElementSource
|
|
280
|
-
/** An element whose way through the container the progress reads, between the `offset`s. */
|
|
281
|
-
readonly target?: ElementSource
|
|
282
|
-
/**
|
|
283
|
-
* Where the progress runs, as Motion's: each stop pairs an edge of the target with an edge of the
|
|
284
|
-
* container, `["start end", "end start"]`, 0 at the first and 1 at the last. An edge is `start`,
|
|
285
|
-
* `center` or `end`, a fraction, or a length. With a target, `["start start", "end end"]` by default.
|
|
286
|
-
*/
|
|
287
|
-
// The DOM's syntax (`@wardic/dom/ScrollOffset`); with a target and none given, its `All`.
|
|
288
|
-
readonly offset?: ScrollOffset.ScrollOffset
|
|
289
|
-
/** The axis `target` and `offset` read: `"y"` (the default) or `"x"`. */
|
|
290
|
-
readonly axis?: "x" | "y"
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/** `createScroll`'s values, Motion's four, each an accessor. */
|
|
294
|
-
export interface ScrollValues {
|
|
295
|
-
/** How far it's scrolled across, in pixels. */
|
|
296
|
-
readonly scrollX: Accessor<number>
|
|
297
|
-
/** How far it's scrolled down, in pixels. */
|
|
298
|
-
readonly scrollY: Accessor<number>
|
|
299
|
-
/** Its progress across, from 0 to 1: through its range, or, with a `target` or `offset`, through theirs. */
|
|
300
|
-
readonly scrollXProgress: Accessor<number>
|
|
301
|
-
/** Its progress down, from 0 to 1: through its range, or, with a `target` or `offset`, through theirs. */
|
|
302
|
-
readonly scrollYProgress: Accessor<number>
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const SERVER_SCROLL: ScrollValues = { scrollX: SERVER_ZERO, scrollY: SERVER_ZERO, scrollXProgress: SERVER_ZERO, scrollYProgress: SERVER_ZERO }
|
|
306
|
-
|
|
307
|
-
/** An element source read now: `None` for an accessor whose ref hasn't arrived. */
|
|
308
|
-
const elementOf = (source: ElementSource): Option.Option<Element> =>
|
|
309
|
-
Bool.match(Predicate.isFunction(source), {
|
|
310
|
-
onTrue: () => Option.fromUndefinedOr((source as Accessor<Element | undefined>)()),
|
|
311
|
-
onFalse: () => Option.some(source as Element),
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* A scroll as values: `scrollX` and `scrollY`, how far it's scrolled, in pixels, and `scrollXProgress`
|
|
316
|
-
* and `scrollYProgress`, from 0 to 1 through its range. The page's, or a `container`'s. With a
|
|
317
|
-
* `target`, the progress is the target's way through, between two `offset`s, on `axis`. Each is set in
|
|
318
|
-
* the scroll's own event, so a value derived from one paints in the frame that shows the scroll. A
|
|
319
|
-
* `container` or `target` given as an accessor of a ref reads 0s until the ref is there. 0s on a
|
|
320
|
-
* server. Motion's `useScroll`.
|
|
321
|
-
*
|
|
322
|
-
* @example
|
|
323
|
-
* let box!: HTMLDivElement
|
|
324
|
-
* const { scrollYProgress } = createScroll({ container: () => box })
|
|
325
|
-
* <div ref={box}>…</div>
|
|
326
|
-
* <w.div style={{ scaleX: scrollYProgress }} />
|
|
327
|
-
* @see https://wardic.dev/reference/solid/live-values/create-scroll
|
|
328
|
-
*/
|
|
329
|
-
// #234. An accessor `container` or `target` is read in an effect, and a change of either follows the
|
|
330
|
-
// new one.
|
|
331
|
-
export const createScroll = (options: ScrollOptions = {}): ScrollValues => {
|
|
332
|
-
const runtime = useWardicRuntime()
|
|
333
|
-
if (isServer) return SERVER_SCROLL
|
|
334
|
-
const [scrollX, setScrollX] = createSignal(0)
|
|
335
|
-
const [scrollY, setScrollY] = createSignal(0)
|
|
336
|
-
const [scrollXProgress, setScrollXProgress] = createSignal(0)
|
|
337
|
-
const [scrollYProgress, setScrollYProgress] = createSignal(0)
|
|
338
|
-
createEffect(() => {
|
|
339
|
-
const container = UndefinedOr.match(options.container, { onUndefined: () => Option.some(undefined), onDefined: elementOf })
|
|
340
|
-
const target = UndefinedOr.match(options.target, { onUndefined: () => Option.some(undefined), onDefined: elementOf })
|
|
341
|
-
// A ref not there yet: the values hold until it is.
|
|
342
|
-
if (Bool.or(Option.isNone(container), Option.isNone(target))) return
|
|
343
|
-
const registry = runtime.runSync(AtomRegistry.AtomRegistry)
|
|
344
|
-
const scope = OwnerScope.open(runtime)
|
|
345
|
-
onCleanup(() => OwnerScope.close(runtime, scope, CLOSE_FAILED))
|
|
346
|
-
const values = runtime.runSync(
|
|
347
|
-
LiveValue.scroll({
|
|
348
|
-
...UndefinedOr.match(Option.getOrUndefined(container), { onUndefined: () => ({}), onDefined: (element) => ({ container: element }) }),
|
|
349
|
-
...UndefinedOr.match(Option.getOrUndefined(target), { onUndefined: () => ({}), onDefined: (element) => ({ target: element }) }),
|
|
350
|
-
...UndefinedOr.match(options.offset, { onUndefined: () => ({}), onDefined: (offset) => ({ offset }) }),
|
|
351
|
-
...UndefinedOr.match(options.axis, { onUndefined: () => ({}), onDefined: (axis) => ({ axis }) }),
|
|
352
|
-
}).pipe(Scope.provide(scope)),
|
|
353
|
-
)
|
|
354
|
-
// Each derived Atom is read at once: it tracks the scroll only once it's been read.
|
|
355
|
-
const stops = Arr.map(
|
|
356
|
-
[
|
|
357
|
-
[values.scrollX, setScrollX],
|
|
358
|
-
[values.scrollY, setScrollY],
|
|
359
|
-
[values.scrollXProgress, setScrollXProgress],
|
|
360
|
-
[values.scrollYProgress, setScrollYProgress],
|
|
361
|
-
] as const,
|
|
362
|
-
([atom, set]) => registry.subscribe(atom, (v) => void set(v), { immediate: true }),
|
|
363
|
-
)
|
|
364
|
-
onCleanup(() => Arr.forEach(stops, (stop) => stop()))
|
|
365
|
-
})
|
|
366
|
-
return { scrollX, scrollY, scrollXProgress, scrollYProgress }
|
|
367
|
-
}
|
|
368
274
|
|
|
369
275
|
/** An accessor as an Atom, set as it changes, and held in `registry` until its owner's disposal. */
|
|
370
276
|
const mirrorOf = <A>(accessor: Accessor<A>, registry: AtomRegistry.AtomRegistry): Atom.Writable<A> => {
|
|
@@ -375,4 +281,4 @@ const mirrorOf = <A>(accessor: Accessor<A>, registry: AtomRegistry.AtomRegistry)
|
|
|
375
281
|
}
|
|
376
282
|
|
|
377
283
|
/** What a helper's Scope reports when one of its finalizers fails as its owner goes. */
|
|
378
|
-
const CLOSE_FAILED = "wardic.liveValue: a finalizer failed"
|
|
284
|
+
export const CLOSE_FAILED = "wardic.liveValue: a finalizer failed"
|
package/src/reorder.tsx
CHANGED
|
@@ -324,6 +324,16 @@ const itemInternals = <V,>(group: ItemGroup<V>, value: V, element: Accessor<HTML
|
|
|
324
324
|
}
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
/**
|
|
328
|
+
* An item's internals: on a server, the drag defaults with its group's touch-action, joining no
|
|
329
|
+
* controller (ADR-0021); on the client, `itemInternals`. A plain `if`, so a client build drops the
|
|
330
|
+
* server's (#518).
|
|
331
|
+
*/
|
|
332
|
+
const internalsOf = <V,>(group: ItemGroup<V>, value: V, element: Accessor<HTMLElement | undefined>, listens: boolean): SpineInternals => {
|
|
333
|
+
if (isServer) return { brings: ITEM_BRINGS, dragAxis: () => true, baseStyle: touchActionOf(group, listens) }
|
|
334
|
+
return itemInternals(group, value, element, useWardicRuntime(), listens)
|
|
335
|
+
}
|
|
336
|
+
|
|
327
337
|
/**
|
|
328
338
|
* What an item adds to its element for the keyboard reorder (ADR-0037): `tabindex="0"` while the
|
|
329
339
|
* group's keyboard is on and the app gave no `tabIndex` of its own, and `aria-describedby` with
|
|
@@ -378,12 +388,7 @@ export function createReorderItem<V>(group: ReorderGroup<V>, config: ReorderItem
|
|
|
378
388
|
const value = untrack(cfg).value
|
|
379
389
|
const listens = untrack(cfg).dragListener !== false
|
|
380
390
|
const [element, setElement] = createSignal<HTMLElement>()
|
|
381
|
-
|
|
382
|
-
// joins no controller (ADR-0021).
|
|
383
|
-
const internals: SpineInternals = Bool.match(isServer, {
|
|
384
|
-
onTrue: (): SpineInternals => ({ brings: ITEM_BRINGS, dragAxis: () => true, baseStyle: touchActionOf(group, listens) }),
|
|
385
|
-
onFalse: () => itemInternals(group, value, element, useWardicRuntime(), listens),
|
|
386
|
-
})
|
|
391
|
+
const internals = internalsOf(group, value, element, listens)
|
|
387
392
|
const ward = createWardWith(() => ({ ...withoutValue(cfg()), layout: itemLayoutOf(cfg().layout) }), internals)
|
|
388
393
|
// The instructions element exists on the client only, so `aria-describedby` waits for the mount.
|
|
389
394
|
const [describedBy, setDescribedBy] = createSignal<string>()
|
package/src/runtime.ts
CHANGED
|
@@ -30,7 +30,7 @@ import * as Layer from "effect/Layer"
|
|
|
30
30
|
import * as ManagedRuntime from "effect/ManagedRuntime"
|
|
31
31
|
import * as Option from "effect/Option"
|
|
32
32
|
import * as Predicate from "effect/Predicate"
|
|
33
|
-
import
|
|
33
|
+
import * as AtomRegistry from "effect/reactivity/AtomRegistry"
|
|
34
34
|
import * as Result from "effect/Result"
|
|
35
35
|
import * as UndefinedOr from "effect/UndefinedOr"
|
|
36
36
|
import type { ServerAppear } from "./appear.ts"
|
package/src/spine.ts
CHANGED
|
@@ -48,7 +48,7 @@ import * as Match from "effect/Match"
|
|
|
48
48
|
import * as Option from "effect/Option"
|
|
49
49
|
import * as Predicate from "effect/Predicate"
|
|
50
50
|
import * as Rec from "effect/Record"
|
|
51
|
-
import
|
|
51
|
+
import * as AtomRegistry from "effect/reactivity/AtomRegistry"
|
|
52
52
|
import * as UndefinedOr from "effect/UndefinedOr"
|
|
53
53
|
import { enterServer, type Handoff, keyOf, type MountAnimation, repeatsForever, type ServerAppear, skippedField, startAtOf } from "./appear.ts"
|
|
54
54
|
import type {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import * as Effect from "effect/Effect"
|
|
11
11
|
import * as Predicate from "effect/Predicate"
|
|
12
|
-
import
|
|
12
|
+
import type * as Atom from "effect/reactivity/Atom"
|
|
13
|
+
import * as AtomRegistry from "effect/reactivity/AtomRegistry"
|
|
13
14
|
import * as Stream from "effect/Stream"
|
|
14
15
|
import { isServer } from "@solidjs/web"
|
|
15
16
|
import { type Accessor, createSignal, isHydrating, onCleanup } from "solid-js"
|
package/src/unstable/v2/index.ts
CHANGED
|
@@ -24,11 +24,12 @@ export {
|
|
|
24
24
|
* only those it uses; a Complete element (`wardic.*`, `ward()`) has every one. `colors`: every CSS
|
|
25
25
|
* colour space beyond a hex, `rgb()`, `hsl()` and the colour names. `layout`: layout animations, the
|
|
26
26
|
* props `layout`, `layoutId`, `layoutScroll` and `layoutRoot` (`LayoutGroup` and `Reorder` bring it
|
|
27
|
-
* themselves).
|
|
27
|
+
* themselves). `scroll`: a container's scroll, which `createScroll` brings itself (#518).
|
|
28
28
|
*/
|
|
29
29
|
export type { Feature, FeatureLoad, Features, FeaturesProp } from "../../feature.ts"
|
|
30
30
|
export { colors } from "../../features/colors.ts"
|
|
31
31
|
export { layout } from "../../features/layout.ts"
|
|
32
|
+
export { scroll } from "../../features/scroll.ts"
|
|
32
33
|
/**
|
|
33
34
|
* The gestures (#511, ADR-0067 decision 3), a Feature each: `hover`, `press` (its Keyboard presses
|
|
34
35
|
* included), `focus`, `inView`, `pan` and `drag` (`dragControls` and the release's inertia included).
|
|
@@ -209,16 +210,13 @@ export { createAnimationControls, type WardicAnimationControls, type WardicContr
|
|
|
209
210
|
export {
|
|
210
211
|
createAnimationFrame,
|
|
211
212
|
createFollowValue,
|
|
212
|
-
createScroll,
|
|
213
213
|
createSpring,
|
|
214
214
|
createTime,
|
|
215
215
|
createTransform,
|
|
216
216
|
createVelocity,
|
|
217
217
|
createWillChange,
|
|
218
|
-
type ElementSource,
|
|
219
|
-
type ScrollOptions,
|
|
220
|
-
type ScrollValues,
|
|
221
218
|
} from "./live-value.ts"
|
|
219
|
+
export { createScroll, type ElementSource, type ScrollOptions, type ScrollValues } from "./live-scroll.ts"
|
|
222
220
|
export type { FollowAccessor, FollowValueOptions, Live, LiveSource, SpringAccessor, WillChange } from "../../live-style.ts"
|
|
223
221
|
|
|
224
222
|
/**
|
|
@@ -67,7 +67,7 @@ export function LayoutGroup(props: LayoutGroupProps): JSX.Element {
|
|
|
67
67
|
const id = untrack(() => layoutGroupIdOf(outer, props.id, props.inherit))
|
|
68
68
|
// A server render has no projection system to re-measure (ADR-0021). Not at mount: nodes measure
|
|
69
69
|
// themselves as they register.
|
|
70
|
-
if (
|
|
70
|
+
if (!isServer) {
|
|
71
71
|
const projection = useWardicProjection()
|
|
72
72
|
// Compared before it measures, as Motion's is: an effect never compares (#618).
|
|
73
73
|
const dependency = createMemo(() => props.layoutDependency, CLIENT_ONLY)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createScroll` (#518): in a module of its own, so it brings the `scroll` Feature only to an app that
|
|
3
|
+
* reads a scroll, and on the pages that do (ADR-0067, *Amendments (#548, #518)*).
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import * as LiveValue from "@wardic/core/LiveValue"
|
|
8
|
+
import * as ScrollSource from "@wardic/core/ScrollSource"
|
|
9
|
+
import type * as ScrollOffset from "@wardic/dom/ScrollOffset"
|
|
10
|
+
import * as Arr from "effect/Array"
|
|
11
|
+
import * as Bool from "effect/Boolean"
|
|
12
|
+
import * as Effect from "effect/Effect"
|
|
13
|
+
import * as Option from "effect/Option"
|
|
14
|
+
import * as Predicate from "effect/Predicate"
|
|
15
|
+
import * as Atom from "effect/reactivity/Atom"
|
|
16
|
+
import * as AtomRegistry from "effect/reactivity/AtomRegistry"
|
|
17
|
+
import * as Rec from "effect/Record"
|
|
18
|
+
import * as Scope from "effect/Scope"
|
|
19
|
+
import * as UndefinedOr from "effect/UndefinedOr"
|
|
20
|
+
import { isServer } from "@solidjs/web"
|
|
21
|
+
import { type Accessor, createEffect, createSignal, onCleanup } from "solid-js"
|
|
22
|
+
import type { Feature } from "../../feature.ts"
|
|
23
|
+
import { scroll } from "../../features/scroll.ts"
|
|
24
|
+
import { serviceOf } from "../../gesture-features.ts"
|
|
25
|
+
import * as OwnerScope from "../../owner-scope.ts"
|
|
26
|
+
import { followAtom } from "./bridge.ts"
|
|
27
|
+
import { CLOSE_FAILED, SERVER_ZERO } from "./live-value.ts"
|
|
28
|
+
import { useBrought, useWardicRuntime } from "./provider.tsx"
|
|
29
|
+
import { CLIENT_ONLY, onSettledClientOnly } from "./solid-reactivity.ts"
|
|
30
|
+
|
|
31
|
+
/** What it brings (ADR-0067 decision 12): the `scroll` Feature, whatever its section has (#518). */
|
|
32
|
+
const SCROLL: ReadonlyArray<Feature> = [scroll]
|
|
33
|
+
|
|
34
|
+
/** An element, or an accessor of one, such as `() => box` for a ref: read once it's there. */
|
|
35
|
+
export type ElementSource = Element | Accessor<Element | undefined>
|
|
36
|
+
|
|
37
|
+
/** `createScroll`'s options, as Motion's `useScroll`'s. */
|
|
38
|
+
export interface ScrollOptions {
|
|
39
|
+
/** The scrolling element, as an accessor of its ref: `() => box`. The page's scroll when left out. */
|
|
40
|
+
readonly container?: ElementSource
|
|
41
|
+
/** An element whose way through the container the progress reads, between the `offset`s. */
|
|
42
|
+
readonly target?: ElementSource
|
|
43
|
+
/**
|
|
44
|
+
* Where the progress runs, as Motion's: each stop pairs an edge of the target with an edge of the
|
|
45
|
+
* container, `["start end", "end start"]`, 0 at the first and 1 at the last. An edge is `start`,
|
|
46
|
+
* `center` or `end`, a fraction, or a length. With a target, `["start start", "end end"]` by default.
|
|
47
|
+
*/
|
|
48
|
+
// The DOM's syntax (`@wardic/dom/ScrollOffset`); with a target and none given, its `All`.
|
|
49
|
+
readonly offset?: ScrollOffset.ScrollOffset
|
|
50
|
+
/** The axis `target` and `offset` read: `"y"` (the default) or `"x"`. */
|
|
51
|
+
readonly axis?: "x" | "y"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** `createScroll`'s values, Motion's four, each an accessor. */
|
|
55
|
+
export interface ScrollValues {
|
|
56
|
+
/** How far it's scrolled across, in pixels. */
|
|
57
|
+
readonly scrollX: Accessor<number>
|
|
58
|
+
/** How far it's scrolled down, in pixels. */
|
|
59
|
+
readonly scrollY: Accessor<number>
|
|
60
|
+
/** Its progress across, from 0 to 1: through its range, or, with a `target` or `offset`, through theirs. */
|
|
61
|
+
readonly scrollXProgress: Accessor<number>
|
|
62
|
+
/** Its progress down, from 0 to 1: through its range, or, with a `target` or `offset`, through theirs. */
|
|
63
|
+
readonly scrollYProgress: Accessor<number>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const SCROLL_KEYS = ["scrollX", "scrollY", "scrollXProgress", "scrollYProgress"] as const
|
|
67
|
+
/** `createScroll`'s elements before the render has settled: none yet. */
|
|
68
|
+
const NOT_YET = { container: Option.none(), target: Option.none() } as const
|
|
69
|
+
const SERVER_SCROLL: ScrollValues = { scrollX: SERVER_ZERO, scrollY: SERVER_ZERO, scrollXProgress: SERVER_ZERO, scrollYProgress: SERVER_ZERO }
|
|
70
|
+
|
|
71
|
+
/** An element source read now: `None` for an accessor whose ref hasn't arrived. */
|
|
72
|
+
const elementOf = (source: ElementSource | undefined): Option.Option<Element | undefined> =>
|
|
73
|
+
Bool.match(Predicate.isFunction(source), {
|
|
74
|
+
onTrue: () => Option.fromUndefinedOr((source as Accessor<Element | undefined>)()),
|
|
75
|
+
onFalse: () => Option.some(source as Element | undefined),
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A scroll as values: `scrollX` and `scrollY`, how far it's scrolled, in pixels, and `scrollXProgress`
|
|
80
|
+
* and `scrollYProgress`, from 0 to 1 through its range. The page's, or a `container`'s. With a
|
|
81
|
+
* `target`, the progress is the target's way through, between two `offset`s, on `axis`. Each is set in
|
|
82
|
+
* the scroll's own event, so a value derived from one paints in the frame that shows the scroll. A
|
|
83
|
+
* `container` or `target` given as an accessor of a ref reads 0s until the ref is there. 0s on a
|
|
84
|
+
* server. Motion's `useScroll`.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* let box!: HTMLDivElement
|
|
88
|
+
* const { scrollYProgress } = createScroll({ container: () => box })
|
|
89
|
+
* <div ref={box}>…</div>
|
|
90
|
+
* <w.div style={{ scaleX: scrollYProgress }} />
|
|
91
|
+
* @see https://wardic.dev/reference/solid/live-values/create-scroll
|
|
92
|
+
*/
|
|
93
|
+
// #234. An accessor `container` or `target` is read in an effect, and a change of either follows the
|
|
94
|
+
// new one. Each value carries one Atom for its life.
|
|
95
|
+
export const createScroll = (options: ScrollOptions = {}): ScrollValues => {
|
|
96
|
+
if (isServer) return SERVER_SCROLL
|
|
97
|
+
const runtime = useWardicRuntime()
|
|
98
|
+
const services = useBrought(SCROLL)
|
|
99
|
+
const registry = runtime.runSync(AtomRegistry.AtomRegistry)
|
|
100
|
+
// Its own four Atoms, set from the scroll of the elements there now.
|
|
101
|
+
const outs = Rec.fromIterableWith(SCROLL_KEYS, (key) => [key, Atom.make(0)] as const)
|
|
102
|
+
Arr.forEach(SCROLL_KEYS, (key) => onCleanup(registry.mount(outs[key])))
|
|
103
|
+
// Its elements are read once the render has settled: a ref given as `() => box` is set during the
|
|
104
|
+
// render, after this effect's first compute, and isn't a signal, so nothing would read it again.
|
|
105
|
+
const [settled, setSettled] = createSignal(false)
|
|
106
|
+
onSettledClientOnly(() => {
|
|
107
|
+
setSettled(true)
|
|
108
|
+
})
|
|
109
|
+
createEffect(
|
|
110
|
+
() =>
|
|
111
|
+
Bool.match(settled(), {
|
|
112
|
+
onTrue: () => ({ container: elementOf(options.container), target: elementOf(options.target) }),
|
|
113
|
+
onFalse: () => NOT_YET,
|
|
114
|
+
}),
|
|
115
|
+
({ container, target }) => {
|
|
116
|
+
// A ref not there yet: the values hold until it is.
|
|
117
|
+
if (Bool.or(Option.isNone(container), Option.isNone(target))) return undefined
|
|
118
|
+
const scope = OwnerScope.open(runtime)
|
|
119
|
+
const values = runtime.runSync(
|
|
120
|
+
LiveValue.scroll({
|
|
121
|
+
...UndefinedOr.match(Option.getOrUndefined(container), { onUndefined: () => ({}), onDefined: (element) => ({ container: element }) }),
|
|
122
|
+
...UndefinedOr.match(Option.getOrUndefined(target), { onUndefined: () => ({}), onDefined: (element) => ({ target: element }) }),
|
|
123
|
+
...UndefinedOr.match(options.offset, { onUndefined: () => ({}), onDefined: (offset) => ({ offset }) }),
|
|
124
|
+
...UndefinedOr.match(options.axis, { onUndefined: () => ({}), onDefined: (axis) => ({ axis }) }),
|
|
125
|
+
}).pipe(Effect.provideService(ScrollSource.ScrollSource, serviceOf(services(), ScrollSource.ScrollSource)), Scope.provide(scope)),
|
|
126
|
+
)
|
|
127
|
+
// Each derived Atom is read at once: it tracks the scroll only once it's been read.
|
|
128
|
+
const stops = Arr.map(SCROLL_KEYS, (key) => registry.subscribe(values[key], (v) => registry.set(outs[key], v), { immediate: true }))
|
|
129
|
+
return () => {
|
|
130
|
+
Arr.forEach(stops, (stop) => stop())
|
|
131
|
+
OwnerScope.close(runtime, scope, CLOSE_FAILED)
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
CLIENT_ONLY,
|
|
135
|
+
)
|
|
136
|
+
return Rec.map(outs, (atom) => followAtom(registry, atom)) as unknown as ScrollValues
|
|
137
|
+
}
|