@wardic/dom 0.0.0-stage → 1.0.0-rc.2
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/LICENSE +21 -0
- package/README.md +8 -2
- package/THIRD_PARTY_NOTICES.md +145 -0
- package/dist/Appear.d.ts +24 -0
- package/dist/Appear.d.ts.map +1 -0
- package/dist/Appear.js +25 -0
- package/dist/Appear.js.map +1 -0
- package/dist/Color.d.ts +14 -0
- package/dist/Color.d.ts.map +1 -0
- package/dist/Color.js +16 -0
- package/dist/Color.js.map +1 -0
- package/dist/DomAnnouncer.d.ts +13 -0
- package/dist/DomAnnouncer.d.ts.map +1 -0
- package/dist/DomAnnouncer.js +14 -0
- package/dist/DomAnnouncer.js.map +1 -0
- package/dist/DomClock.d.ts +11 -0
- package/dist/DomClock.d.ts.map +1 -0
- package/dist/DomClock.js +12 -0
- package/dist/DomClock.js.map +1 -0
- package/dist/DomDefaultTransition.d.ts +12 -0
- package/dist/DomDefaultTransition.d.ts.map +1 -0
- package/dist/DomDefaultTransition.js +13 -0
- package/dist/DomDefaultTransition.js.map +1 -0
- package/dist/DomInput.d.ts +13 -0
- package/dist/DomInput.d.ts.map +1 -0
- package/dist/DomInput.js +15 -0
- package/dist/DomInput.js.map +1 -0
- package/dist/DomInterpolators.d.ts +13 -0
- package/dist/DomInterpolators.d.ts.map +1 -0
- package/dist/DomInterpolators.js +14 -0
- package/dist/DomInterpolators.js.map +1 -0
- package/dist/DomKeyRules.d.ts +13 -0
- package/dist/DomKeyRules.d.ts.map +1 -0
- package/dist/DomKeyRules.js +14 -0
- package/dist/DomKeyRules.js.map +1 -0
- package/dist/DomPositionalKeys.d.ts +13 -0
- package/dist/DomPositionalKeys.d.ts.map +1 -0
- package/dist/DomPositionalKeys.js +14 -0
- package/dist/DomPositionalKeys.js.map +1 -0
- package/dist/DomProbe.d.ts +13 -0
- package/dist/DomProbe.d.ts.map +1 -0
- package/dist/DomProbe.js +14 -0
- package/dist/DomProbe.js.map +1 -0
- package/dist/DomProjectionSystem.d.ts +28 -0
- package/dist/DomProjectionSystem.d.ts.map +1 -0
- package/dist/DomProjectionSystem.js +41 -0
- package/dist/DomProjectionSystem.js.map +1 -0
- package/dist/DomRenderer.d.ts +13 -0
- package/dist/DomRenderer.d.ts.map +1 -0
- package/dist/DomRenderer.js +15 -0
- package/dist/DomRenderer.js.map +1 -0
- package/dist/FlushGate.d.ts +48 -0
- package/dist/FlushGate.d.ts.map +1 -0
- package/dist/FlushGate.js +62 -0
- package/dist/FlushGate.js.map +1 -0
- package/dist/MeasuredPair.d.ts +14 -0
- package/dist/MeasuredPair.d.ts.map +1 -0
- package/dist/MeasuredPair.js +16 -0
- package/dist/MeasuredPair.js.map +1 -0
- package/dist/OwnValues.d.ts +14 -0
- package/dist/OwnValues.d.ts.map +1 -0
- package/dist/OwnValues.js +15 -0
- package/dist/OwnValues.js.map +1 -0
- package/dist/ProjectionSystem.d.ts +34 -0
- package/dist/ProjectionSystem.d.ts.map +1 -0
- package/dist/ProjectionSystem.js +29 -0
- package/dist/ProjectionSystem.js.map +1 -0
- package/dist/ReorderController.d.ts +41 -0
- package/dist/ReorderController.d.ts.map +1 -0
- package/dist/ReorderController.js +44 -0
- package/dist/ReorderController.js.map +1 -0
- package/dist/Report.d.ts +12 -0
- package/dist/Report.d.ts.map +1 -0
- package/dist/Report.js +13 -0
- package/dist/Report.js.map +1 -0
- package/dist/ScrollOffset.d.ts +34 -0
- package/dist/ScrollOffset.d.ts.map +1 -0
- package/dist/ScrollOffset.js +42 -0
- package/dist/ScrollOffset.js.map +1 -0
- package/dist/Style.d.ts +15 -0
- package/dist/Style.d.ts.map +1 -0
- package/dist/Style.js +17 -0
- package/dist/Style.js.map +1 -0
- package/dist/Svg.d.ts +13 -0
- package/dist/Svg.d.ts.map +1 -0
- package/dist/Svg.js +14 -0
- package/dist/Svg.js.map +1 -0
- package/dist/Variables.d.ts +13 -0
- package/dist/Variables.d.ts.map +1 -0
- package/dist/Variables.js +14 -0
- package/dist/Variables.js.map +1 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/announcer.d.ts +50 -0
- package/dist/internal/announcer.d.ts.map +1 -0
- package/dist/internal/announcer.js +115 -0
- package/dist/internal/announcer.js.map +1 -0
- package/dist/internal/appear.d.ts +169 -0
- package/dist/internal/appear.d.ts.map +1 -0
- package/dist/internal/appear.js +499 -0
- package/dist/internal/appear.js.map +1 -0
- package/dist/internal/auto-scroll.d.ts +24 -0
- package/dist/internal/auto-scroll.d.ts.map +1 -0
- package/dist/internal/auto-scroll.js +124 -0
- package/dist/internal/auto-scroll.js.map +1 -0
- package/dist/internal/clock.d.ts +19 -0
- package/dist/internal/clock.d.ts.map +1 -0
- package/dist/internal/clock.js +39 -0
- package/dist/internal/clock.js.map +1 -0
- package/dist/internal/colors.d.ts +45 -0
- package/dist/internal/colors.d.ts.map +1 -0
- package/dist/internal/colors.js +193 -0
- package/dist/internal/colors.js.map +1 -0
- package/dist/internal/complex.d.ts +24 -0
- package/dist/internal/complex.d.ts.map +1 -0
- package/dist/internal/complex.js +28 -0
- package/dist/internal/complex.js.map +1 -0
- package/dist/internal/content-watch.d.ts +20 -0
- package/dist/internal/content-watch.d.ts.map +1 -0
- package/dist/internal/content-watch.js +76 -0
- package/dist/internal/content-watch.js.map +1 -0
- package/dist/internal/default-transition.d.ts +25 -0
- package/dist/internal/default-transition.d.ts.map +1 -0
- package/dist/internal/default-transition.js +41 -0
- package/dist/internal/default-transition.js.map +1 -0
- package/dist/internal/drag-defaults.d.ts +18 -0
- package/dist/internal/drag-defaults.d.ts.map +1 -0
- package/dist/internal/drag-defaults.js +38 -0
- package/dist/internal/drag-defaults.js.map +1 -0
- package/dist/internal/gate.d.ts +186 -0
- package/dist/internal/gate.d.ts.map +1 -0
- package/dist/internal/gate.js +453 -0
- package/dist/internal/gate.js.map +1 -0
- package/dist/internal/input.d.ts +41 -0
- package/dist/internal/input.d.ts.map +1 -0
- package/dist/internal/input.js +476 -0
- package/dist/internal/input.js.map +1 -0
- package/dist/internal/interpolators.d.ts +19 -0
- package/dist/internal/interpolators.d.ts.map +1 -0
- package/dist/internal/interpolators.js +30 -0
- package/dist/internal/interpolators.js.map +1 -0
- package/dist/internal/key-rules.d.ts +11 -0
- package/dist/internal/key-rules.d.ts.map +1 -0
- package/dist/internal/key-rules.js +244 -0
- package/dist/internal/key-rules.js.map +1 -0
- package/dist/internal/measure.d.ts +150 -0
- package/dist/internal/measure.d.ts.map +1 -0
- package/dist/internal/measure.js +278 -0
- package/dist/internal/measure.js.map +1 -0
- package/dist/internal/measured-pairs.d.ts +38 -0
- package/dist/internal/measured-pairs.d.ts.map +1 -0
- package/dist/internal/measured-pairs.js +251 -0
- package/dist/internal/measured-pairs.js.map +1 -0
- package/dist/internal/own-values.d.ts +35 -0
- package/dist/internal/own-values.d.ts.map +1 -0
- package/dist/internal/own-values.js +117 -0
- package/dist/internal/own-values.js.map +1 -0
- package/dist/internal/pass-ends.d.ts +21 -0
- package/dist/internal/pass-ends.d.ts.map +1 -0
- package/dist/internal/pass-ends.js +14 -0
- package/dist/internal/pass-ends.js.map +1 -0
- package/dist/internal/positional-keys.d.ts +16 -0
- package/dist/internal/positional-keys.d.ts.map +1 -0
- package/dist/internal/positional-keys.js +21 -0
- package/dist/internal/positional-keys.js.map +1 -0
- package/dist/internal/probe.d.ts +10 -0
- package/dist/internal/probe.d.ts.map +1 -0
- package/dist/internal/probe.js +117 -0
- package/dist/internal/probe.js.map +1 -0
- package/dist/internal/projection-defaults.d.ts +34 -0
- package/dist/internal/projection-defaults.d.ts.map +1 -0
- package/dist/internal/projection-defaults.js +21 -0
- package/dist/internal/projection-defaults.js.map +1 -0
- package/dist/internal/projection.d.ts +529 -0
- package/dist/internal/projection.d.ts.map +1 -0
- package/dist/internal/projection.js +4990 -0
- package/dist/internal/projection.js.map +1 -0
- package/dist/internal/renderer.d.ts +86 -0
- package/dist/internal/renderer.d.ts.map +1 -0
- package/dist/internal/renderer.js +616 -0
- package/dist/internal/renderer.js.map +1 -0
- package/dist/internal/reorder.d.ts +159 -0
- package/dist/internal/reorder.d.ts.map +1 -0
- package/dist/internal/reorder.js +437 -0
- package/dist/internal/reorder.js.map +1 -0
- package/dist/internal/report.d.ts +30 -0
- package/dist/internal/report.d.ts.map +1 -0
- package/dist/internal/report.js +60 -0
- package/dist/internal/report.js.map +1 -0
- package/dist/internal/require-global.d.ts +11 -0
- package/dist/internal/require-global.d.ts.map +1 -0
- package/dist/internal/require-global.js +31 -0
- package/dist/internal/require-global.js.map +1 -0
- package/dist/internal/reveal.d.ts +16 -0
- package/dist/internal/reveal.d.ts.map +1 -0
- package/dist/internal/reveal.js +66 -0
- package/dist/internal/reveal.js.map +1 -0
- package/dist/internal/runner.d.ts +21 -0
- package/dist/internal/runner.d.ts.map +1 -0
- package/dist/internal/runner.js +48 -0
- package/dist/internal/runner.js.map +1 -0
- package/dist/internal/scale-correction.d.ts +80 -0
- package/dist/internal/scale-correction.d.ts.map +1 -0
- package/dist/internal/scale-correction.js +227 -0
- package/dist/internal/scale-correction.js.map +1 -0
- package/dist/internal/scroll.d.ts +35 -0
- package/dist/internal/scroll.d.ts.map +1 -0
- package/dist/internal/scroll.js +244 -0
- package/dist/internal/scroll.js.map +1 -0
- package/dist/internal/scrollers.d.ts +35 -0
- package/dist/internal/scrollers.d.ts.map +1 -0
- package/dist/internal/scrollers.js +38 -0
- package/dist/internal/scrollers.js.map +1 -0
- package/dist/internal/starting-style.d.ts +141 -0
- package/dist/internal/starting-style.d.ts.map +1 -0
- package/dist/internal/starting-style.js +300 -0
- package/dist/internal/starting-style.js.map +1 -0
- package/dist/internal/svg.d.ts +60 -0
- package/dist/internal/svg.d.ts.map +1 -0
- package/dist/internal/svg.js +257 -0
- package/dist/internal/svg.js.map +1 -0
- package/dist/internal/variables.d.ts +45 -0
- package/dist/internal/variables.d.ts.map +1 -0
- package/dist/internal/variables.js +92 -0
- package/dist/internal/variables.js.map +1 -0
- package/package.json +65 -4
- package/src/Appear.ts +45 -0
- package/src/Color.ts +24 -0
- package/src/DomAnnouncer.ts +22 -0
- package/src/DomClock.ts +13 -0
- package/src/DomDefaultTransition.ts +16 -0
- package/src/DomInput.ts +22 -0
- package/src/DomInterpolators.ts +16 -0
- package/src/DomKeyRules.ts +17 -0
- package/src/DomPositionalKeys.ts +16 -0
- package/src/DomProbe.ts +15 -0
- package/src/DomProjectionSystem.ts +44 -0
- package/src/DomRenderer.ts +26 -0
- package/src/FlushGate.ts +81 -0
- package/src/MeasuredPair.ts +15 -0
- package/src/OwnValues.ts +19 -0
- package/src/ProjectionSystem.ts +51 -0
- package/src/ReorderController.ts +72 -0
- package/src/Report.ts +17 -0
- package/src/ScrollOffset.ts +54 -0
- package/src/Style.ts +37 -0
- package/src/Svg.ts +21 -0
- package/src/Variables.ts +17 -0
- package/src/index.ts +106 -0
- package/src/internal/announcer.ts +132 -0
- package/src/internal/appear.ts +739 -0
- package/src/internal/auto-scroll.ts +176 -0
- package/src/internal/clock.ts +45 -0
- package/src/internal/colors.ts +258 -0
- package/src/internal/complex.ts +29 -0
- package/src/internal/content-watch.ts +84 -0
- package/src/internal/culori.d.ts +58 -0
- package/src/internal/default-transition.ts +50 -0
- package/src/internal/drag-defaults.ts +54 -0
- package/src/internal/gate.ts +593 -0
- package/src/internal/input.ts +579 -0
- package/src/internal/interpolators.ts +33 -0
- package/src/internal/key-rules.ts +357 -0
- package/src/internal/measure.ts +357 -0
- package/src/internal/measured-pairs.ts +297 -0
- package/src/internal/own-values.ts +166 -0
- package/src/internal/pass-ends.ts +23 -0
- package/src/internal/positional-keys.ts +24 -0
- package/src/internal/probe.ts +176 -0
- package/src/internal/projection-defaults.ts +45 -0
- package/src/internal/projection.ts +5999 -0
- package/src/internal/renderer.ts +646 -0
- package/src/internal/reorder.ts +604 -0
- package/src/internal/report.ts +69 -0
- package/src/internal/require-global.ts +46 -0
- package/src/internal/reveal.ts +69 -0
- package/src/internal/runner.ts +64 -0
- package/src/internal/scale-correction.ts +310 -0
- package/src/internal/scroll.ts +356 -0
- package/src/internal/scrollers.ts +51 -0
- package/src/internal/starting-style.ts +396 -0
- package/src/internal/svg.ts +300 -0
- package/src/internal/variables.ts +145 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Projection system: what runs the DOM's layout animations, one per Environment a binding builds. A binding registers
|
|
3
|
+
* each layout element with it, and when a layout changes it measures their boxes, draws each where it was
|
|
4
|
+
* with a transform, and animates it to where it is now; elements that share a `layoutId` fly one into the
|
|
5
|
+
* other. This module is its key and its types, which every element's code reads: the engine itself is
|
|
6
|
+
* `DomProjectionSystem`'s, so a page that never lays out ships none of it. `toJSON()` gives its tree as
|
|
7
|
+
* plain data, for a diagnostic view. A server render builds none.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import * as ProjectionSystem from "@wardic/dom/ProjectionSystem"
|
|
11
|
+
*
|
|
12
|
+
* const system = yield* ProjectionSystem.ProjectionSystem
|
|
13
|
+
* const node = system.register({ id: "card", element: card, transition: () => Option.none() })
|
|
14
|
+
* system.animateLayout(() => expand(card)) // measured before and after, and animated between
|
|
15
|
+
*/
|
|
16
|
+
// The **Projection system**, the DOM's engine for the **Projection** (ADR-0009, ADR-0010, ADR-0018): it
|
|
17
|
+
// holds every Projection node, runs the Measure pass and the per-frame projection pass, and owns `layoutId`
|
|
18
|
+
// matching. `@wardic/dom/ProjectionSystem` (#132, ADR-0041). The key apart from the engine: #548, ADR-0067
|
|
19
|
+
// *Amendment (#548)*. An Effect `Inspectable` (#384): `console.log(system)` shows the tree, as `Tree`,
|
|
20
|
+
// each node with its parent, its `layoutId` twin's part and what it's doing; the layout lab draws it. A
|
|
21
|
+
// server render builds none (ADR-0021).
|
|
22
|
+
import * as Context from "effect/Context"
|
|
23
|
+
import type * as internal from "./internal/projection.ts"
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
type RegisterNodeOptions,
|
|
27
|
+
type ProjectionNodeHandle as NodeHandle,
|
|
28
|
+
type DragProjectionHandle as DragHandle,
|
|
29
|
+
type AnimateLayoutOptions,
|
|
30
|
+
type Tree,
|
|
31
|
+
type TreeNode,
|
|
32
|
+
} from "./internal/projection.ts"
|
|
33
|
+
export { DEFAULT_DRAG_TRANSITION, DEFAULT_LAYOUT_TRANSITION, type DragTransition, type LayoutTransition } from "./internal/projection-defaults.ts"
|
|
34
|
+
export type { ScaleCorrectorEntry as ScaleCorrector, CorrectorInfo } from "./internal/scale-correction.ts"
|
|
35
|
+
export { dragBoundsWithin, measureLayoutBox } from "./internal/measure.ts"
|
|
36
|
+
|
|
37
|
+
/** The engine: what a binding registers its layout elements with. */
|
|
38
|
+
export type Shape = Omit<internal.ProjectionSystem, "dispose" | "onPassEnd">
|
|
39
|
+
|
|
40
|
+
/** What the engine writes each node's projection through: a `FlushGate`'s `write` and `flushPass`. */
|
|
41
|
+
export type Sink = internal.ProjectionSink
|
|
42
|
+
|
|
43
|
+
/** What a binding hands its engine: whether reduced motion applies now, and the app's scale correctors. */
|
|
44
|
+
// ADR-0029; #443.
|
|
45
|
+
export type Options = internal.ProjectionSystemOptions
|
|
46
|
+
|
|
47
|
+
/** The services the engine needs from its Environment. */
|
|
48
|
+
export type Services = internal.ProjectionServices
|
|
49
|
+
|
|
50
|
+
/** The Environment's Projection system. */
|
|
51
|
+
export class ProjectionSystem extends /*#__PURE__*/ Context.Service<ProjectionSystem, Shape>()("@wardic/dom/ProjectionSystem") {}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Reorder group on the DOM: the group, its items' drags, its keyboard and its announcements, one
|
|
3
|
+
* controller a group. `make` builds one over the context's Projection system and `Announcer`; a binding
|
|
4
|
+
* attaches its group element, gets a driver for each item, and closing the Scope ends the controller. A
|
|
5
|
+
* server render gets `inert`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import * as ReorderController from "@wardic/dom/ReorderController"
|
|
9
|
+
*
|
|
10
|
+
* const controller = yield* ReorderController.make({ values: () => items, onReorder: (next) => (items = next) })
|
|
11
|
+
* controller.attach(list)
|
|
12
|
+
* const driver = controller.item(item, () => Option.some(element))
|
|
13
|
+
*/
|
|
14
|
+
// A **Reorder group**'s controller (ADR-0019 *Amendment (Reorder)*, ADR-0037); a server render's `inert`
|
|
15
|
+
// (ADR-0021). `@wardic/dom/ReorderController` (#132, ADR-0041). A scoped constructor, one per group (#132's
|
|
16
|
+
// PR 3): `make` logs its `wardic.reorder:` events through the context's Logger, and ends with its Scope,
|
|
17
|
+
// which stops its listening to Measure passes, resets its Auto-scroll and clears a pending announcement.
|
|
18
|
+
// Its items' drivers stay plain, each with its own `dispose`, since one is made per element.
|
|
19
|
+
import * as Announcer from "@wardic/core/Announcer"
|
|
20
|
+
import * as Effect from "effect/Effect"
|
|
21
|
+
import type * as Scope from "effect/Scope"
|
|
22
|
+
import { PassEnds } from "./internal/pass-ends.ts"
|
|
23
|
+
import * as internal from "./internal/reorder.ts"
|
|
24
|
+
import { makeRunner } from "./internal/runner.ts"
|
|
25
|
+
import * as ProjectionSystem from "./ProjectionSystem.ts"
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
type ReorderEnd as End,
|
|
29
|
+
type ReorderHoldEnd as HoldEnd,
|
|
30
|
+
type ReorderHoldHow as HoldHow,
|
|
31
|
+
type ReorderHoldStart as HoldStart,
|
|
32
|
+
type ReorderItemDriver as ItemDriver,
|
|
33
|
+
DEFAULT_REORDER_INSTRUCTIONS as DEFAULT_INSTRUCTIONS,
|
|
34
|
+
REORDER_GROUP_STYLE as GROUP_STYLE,
|
|
35
|
+
} from "./internal/reorder.ts"
|
|
36
|
+
|
|
37
|
+
/** A **Reorder group** on the DOM: what a binding attaches its group element and items to. */
|
|
38
|
+
export type ReorderController<V> = Omit<internal.ReorderController<V>, "dispose">
|
|
39
|
+
|
|
40
|
+
/** What a binding hands its group: the app's values, its axis, its keyboard and its announcements. */
|
|
41
|
+
export type Options<V> = internal.ReorderControllerOptions<V>
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The services a controller takes from its context: the projection system its items are nodes of,
|
|
45
|
+
* with what tells it each Measure pass's end (`DomProjectionSystem`'s Layer provides both), and the
|
|
46
|
+
* Environment's `Announcer`.
|
|
47
|
+
*/
|
|
48
|
+
export type Services = ProjectionSystem.ProjectionSystem | PassEnds | Announcer.Announcer
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Build a group's controller over the context's projection system, announcing through its
|
|
52
|
+
* `Announcer`, in the current Scope: closing it ends the controller.
|
|
53
|
+
*/
|
|
54
|
+
export const make: <V>(options: Options<V>) => Effect.Effect<ReorderController<V>, never, Services | Scope.Scope> = Effect.fnUntraced(
|
|
55
|
+
function* <V>(options: Options<V>) {
|
|
56
|
+
const projection = yield* ProjectionSystem.ProjectionSystem
|
|
57
|
+
const passEnds = yield* PassEnds
|
|
58
|
+
const announcer = yield* Announcer.Announcer
|
|
59
|
+
const controller = internal.makeReorderController(
|
|
60
|
+
{ runtime: yield* makeRunner<never>(), projection, announce: announcer.announce, onPassEnd: passEnds.onPassEnd },
|
|
61
|
+
options,
|
|
62
|
+
)
|
|
63
|
+
yield* Effect.addFinalizer(() => Effect.sync(controller.dispose))
|
|
64
|
+
return controller
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The controller a server render gets: its group has no values, so every operation on it does nothing,
|
|
69
|
+
* its axis is `"y"`, and an item's driver does nothing too.
|
|
70
|
+
*/
|
|
71
|
+
// ADR-0021.
|
|
72
|
+
export const inert: ReorderController<never> = internal.INERT_REORDER_CONTROLLER
|
package/src/Report.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reporting a failure where a fiber stops: logged at Error and handed to the Environment's
|
|
3
|
+
* `ErrorReporter`s, so an app's error tracking sees it. An interruption is neither: it's how a run is
|
|
4
|
+
* removed.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import * as Report from "@wardic/dom/Report"
|
|
8
|
+
*
|
|
9
|
+
* yield* Report.failure("wardic: an animation failed", cause)
|
|
10
|
+
*/
|
|
11
|
+
// `@wardic/dom/Report` (#132, ADR-0041): ADR-0030, decision 4.
|
|
12
|
+
export {
|
|
13
|
+
reportFailure as failure,
|
|
14
|
+
debugKept,
|
|
15
|
+
debugOf,
|
|
16
|
+
type DebugLog,
|
|
17
|
+
} from "./internal/report.ts"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scroll offsets, the DOM's: Motion's syntax for where a target's progress through its scroll container
|
|
3
|
+
* runs, from 0 to 1, which the DOM's `Probe.scroll` resolves. Core takes an offset as its Environment's
|
|
4
|
+
* own and passes it through, so its syntax, its names and its units are the DOM's.
|
|
5
|
+
*
|
|
6
|
+
* - **An edge** is a name (`start`, `center`, `end`), a fraction of a length (`0.5`), or a CSS length:
|
|
7
|
+
* `"100px"` a distance, `"50%"` a fraction, `"10vw"` or `"10vh"` a share of the viewport.
|
|
8
|
+
* - **A stop** is a pair of edges, `[target, container]`: the scroll position where the target's edge
|
|
9
|
+
* meets the container's. A string holds both (`"start end"`); one edge alone is both for a name or a
|
|
10
|
+
* number (`"end"`, `0.5`), else the target's against the container's start (`"100px"`).
|
|
11
|
+
* - **An offset** is two stops or more: progress runs from 0 at the first to 1 at the last, evenly
|
|
12
|
+
* between them, and holds past them.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* import * as ScrollOffset from "@wardic/dom/ScrollOffset"
|
|
16
|
+
*
|
|
17
|
+
* // From the target's top meeting the container's bottom, to its bottom meeting the container's top:
|
|
18
|
+
* const across: ScrollOffset.ScrollOffset = ["start end", "end start"]
|
|
19
|
+
*/
|
|
20
|
+
// #234, ADR-0043 *Amendment (#233, #234)*; resolved in `internal/scroll.ts`. `@wardic/dom/ScrollOffset`
|
|
21
|
+
// (ADR-0041): the type a binding gives its `scroll` helper's `offset`.
|
|
22
|
+
|
|
23
|
+
/** One edge of a stop: a name, a fraction, or a CSS length (see the module's doc). */
|
|
24
|
+
export type Edge = string | number
|
|
25
|
+
|
|
26
|
+
/** One stop: `[target edge, container edge]`, a string of both, or one edge. */
|
|
27
|
+
export type Stop = Edge | readonly [Edge, Edge]
|
|
28
|
+
|
|
29
|
+
/** Where a target's progress runs: two stops or more. */
|
|
30
|
+
export type ScrollOffset = ReadonlyArray<Stop>
|
|
31
|
+
|
|
32
|
+
/** Motion's presets: while the target comes into the container. */
|
|
33
|
+
export const Enter: ScrollOffset = [
|
|
34
|
+
[0, 1],
|
|
35
|
+
[1, 1],
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
/** While the target leaves the container. */
|
|
39
|
+
export const Exit: ScrollOffset = [
|
|
40
|
+
[0, 0],
|
|
41
|
+
[1, 0],
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
/** While any of the target is in the container. */
|
|
45
|
+
export const Any: ScrollOffset = [
|
|
46
|
+
[1, 0],
|
|
47
|
+
[0, 1],
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
/** While all of the target is in the container: the offset with a target and none given. */
|
|
51
|
+
export const All: ScrollOffset = [
|
|
52
|
+
[0, 0],
|
|
53
|
+
[1, 1],
|
|
54
|
+
]
|
package/src/Style.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An element's style as a binding builds it: its starting style, what it's drawn with before any
|
|
3
|
+
* animation runs, from its `initial` (or its `animate`, with `initial: false`); how an app's own style and
|
|
4
|
+
* Wardic's are layered (`over`, `without`); and a drag element's default style. A server render draws the
|
|
5
|
+
* starting style, so an element is drawn where it starts before any code runs.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import * as Style from "@wardic/dom/Style"
|
|
9
|
+
*
|
|
10
|
+
* const start = Style.resolveInitial({ initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 } })
|
|
11
|
+
* Style.starting(undefined, start) // { style: { transform: "translateY(8px)", opacity: 0 }, attrs: {} }
|
|
12
|
+
*/
|
|
13
|
+
// `@wardic/dom/Style` (#132, ADR-0041): the **Starting style**, the merges of an app's style with
|
|
14
|
+
// Wardic's, and a drag element's defaults (ADR-0019, ADR-0021, ADR-0034).
|
|
15
|
+
export {
|
|
16
|
+
startingStyle as starting,
|
|
17
|
+
resolveInitial,
|
|
18
|
+
type InitialConfig,
|
|
19
|
+
keyframesOf,
|
|
20
|
+
splitValues,
|
|
21
|
+
type SplitValues,
|
|
22
|
+
type StopAt,
|
|
23
|
+
type Stops,
|
|
24
|
+
toValues,
|
|
25
|
+
valuesAt,
|
|
26
|
+
type StyleInput as Input,
|
|
27
|
+
type StyleRecord as Style,
|
|
28
|
+
over,
|
|
29
|
+
without,
|
|
30
|
+
heldTouches,
|
|
31
|
+
} from "./internal/starting-style.ts"
|
|
32
|
+
export {
|
|
33
|
+
dragDefaultStyle as dragDefaults,
|
|
34
|
+
type DragDefaultStyle as DragDefaults,
|
|
35
|
+
DRAG_DEFAULT_ATTRIBUTES,
|
|
36
|
+
reorderTouchAction,
|
|
37
|
+
} from "./internal/drag-defaults.ts"
|
package/src/Svg.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG: which elements are SVG's, and a style patch split into the CSS an SVG element takes and the
|
|
3
|
+
* attributes it takes instead. An `<svg>` root is styled as HTML is; an element inside it takes most keys
|
|
4
|
+
* as attributes, and a transform about its own centre, as an HTML element's is.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import * as Svg from "@wardic/dom/Svg"
|
|
8
|
+
*
|
|
9
|
+
* Svg.splitPatchForTag("circle", { r: 10, opacity: 0.5, transform: "scale(2)" })
|
|
10
|
+
* // style: transform, transform-box and transform-origin; attrs: { r: "10", opacity: "0.5" }
|
|
11
|
+
*/
|
|
12
|
+
// `@wardic/dom/Svg` (#132, ADR-0041): ADR-0014.
|
|
13
|
+
export {
|
|
14
|
+
isSvgRoot as isRoot,
|
|
15
|
+
isSvgInner as isInner,
|
|
16
|
+
isSvgInnerTag as isInnerTag,
|
|
17
|
+
setSvgAttrs as setAttrs,
|
|
18
|
+
splitPatch,
|
|
19
|
+
splitPatchForTag,
|
|
20
|
+
type SplitPatch,
|
|
21
|
+
} from "./internal/svg.ts"
|
package/src/Variables.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS variables in animated values: a value that's wholly a `var()` is resolved against an element, so
|
|
3
|
+
* `var(--accent)` animates from or to the colour it stands for. `values` is the Key rule a binding adds
|
|
4
|
+
* for an element's own animations.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import * as Variables from "@wardic/dom/Variables"
|
|
8
|
+
*
|
|
9
|
+
* Variables.isValue("var(--gap)") // true
|
|
10
|
+
* Variables.resolve(element, "var(--gap)") // what `--gap` computes to on `element`
|
|
11
|
+
*/
|
|
12
|
+
// `@wardic/dom/Variables` (#132, ADR-0041): #55, ADR-0035.
|
|
13
|
+
export {
|
|
14
|
+
isVariableValue as isValue,
|
|
15
|
+
resolveVariable as resolve,
|
|
16
|
+
variableValues as values,
|
|
17
|
+
} from "./internal/variables.ts"
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@wardic/dom`: the browser Environment, and the framework-free pieces a binding builds on. It's for
|
|
3
|
+
* binding authors, Wardic's own and any other, and it's Effect-typed.
|
|
4
|
+
*
|
|
5
|
+
* - **An app** imports {@link DomLive}, the composed Environment it hands its binding, and
|
|
6
|
+
* {@link CheckFrames}, to check its Frames in development.
|
|
7
|
+
* - **A binding** imports each module by its subpath, as a namespace
|
|
8
|
+
* (`import * as FlushGate from "@wardic/dom/FlushGate"`), and the root re-exports each one:
|
|
9
|
+
* - the DOM's implementations of core's services, each with its `layer` (Effect's platform
|
|
10
|
+
* convention: `DomRenderer`, `DomInput`, `DomProbe`, `DomClock`, `DomAnnouncer`,
|
|
11
|
+
* `DomInterpolators`, `DomPositionalKeys`, `DomDefaultTransition`), and `DomKeyRules`, the Key
|
|
12
|
+
* rules `DomInterpolators.layer` installs, which has none of its own;
|
|
13
|
+
* - dom's own concepts, by the glossary's names: `FlushGate`, `ProjectionSystem`,
|
|
14
|
+
* `ReorderController`, `Style`, `Svg`, `Color`, `Variables`, `ScrollOffset`, `Appear`, `OwnValues`
|
|
15
|
+
* and `Report`.
|
|
16
|
+
*
|
|
17
|
+
* Everything under `internal/` is dom's own and not exported.
|
|
18
|
+
*/
|
|
19
|
+
// `@wardic/dom` (#132, ADR-0041), Tier 2 (ADR-0008); CheckFrames re-exported for an app (#586).
|
|
20
|
+
import type * as Clock from "@wardic/core/Clock"
|
|
21
|
+
import * as FrameLoop from "@wardic/core/FrameLoop"
|
|
22
|
+
import type * as InterpolatorRegistry from "@wardic/core/InterpolatorRegistry"
|
|
23
|
+
import type * as Announcer from "@wardic/core/Announcer"
|
|
24
|
+
import type * as DefaultTransition from "@wardic/core/DefaultTransition"
|
|
25
|
+
import type * as ReducedMotion from "@wardic/core/ReducedMotion"
|
|
26
|
+
import type * as Probe from "@wardic/core/Probe"
|
|
27
|
+
import type * as RefreshFrameSource from "@wardic/core/RefreshFrameSource"
|
|
28
|
+
import type * as Renderer from "@wardic/core/Renderer"
|
|
29
|
+
import * as Layer from "effect/Layer"
|
|
30
|
+
import { AtomRegistry } from "effect/reactivity"
|
|
31
|
+
import * as DomAnnouncer from "./DomAnnouncer.ts"
|
|
32
|
+
import * as DomClock from "./DomClock.ts"
|
|
33
|
+
import * as DomDefaultTransition from "./DomDefaultTransition.ts"
|
|
34
|
+
import * as DomInterpolators from "./DomInterpolators.ts"
|
|
35
|
+
import * as DomPositionalKeys from "./DomPositionalKeys.ts"
|
|
36
|
+
import * as DomProbe from "./DomProbe.ts"
|
|
37
|
+
import * as DomRenderer from "./DomRenderer.ts"
|
|
38
|
+
|
|
39
|
+
export const VERSION = "1.0.0-rc.2"
|
|
40
|
+
|
|
41
|
+
export * as DomClock from "./DomClock.ts"
|
|
42
|
+
export * as DomInput from "./DomInput.ts"
|
|
43
|
+
export * as DomProbe from "./DomProbe.ts"
|
|
44
|
+
export * as DomRenderer from "./DomRenderer.ts"
|
|
45
|
+
export * as DomAnnouncer from "./DomAnnouncer.ts"
|
|
46
|
+
export * as DomInterpolators from "./DomInterpolators.ts"
|
|
47
|
+
export * as DomPositionalKeys from "./DomPositionalKeys.ts"
|
|
48
|
+
export * as DomDefaultTransition from "./DomDefaultTransition.ts"
|
|
49
|
+
export * as DomKeyRules from "./DomKeyRules.ts"
|
|
50
|
+
export * as FlushGate from "./FlushGate.ts"
|
|
51
|
+
export * as ProjectionSystem from "./ProjectionSystem.ts"
|
|
52
|
+
export * as DomProjectionSystem from "./DomProjectionSystem.ts"
|
|
53
|
+
export * as ReorderController from "./ReorderController.ts"
|
|
54
|
+
export * as Style from "./Style.ts"
|
|
55
|
+
export * as Svg from "./Svg.ts"
|
|
56
|
+
export * as Color from "./Color.ts"
|
|
57
|
+
export * as Variables from "./Variables.ts"
|
|
58
|
+
export * as ScrollOffset from "./ScrollOffset.ts"
|
|
59
|
+
export * as Appear from "./Appear.ts"
|
|
60
|
+
export * as OwnValues from "./OwnValues.ts"
|
|
61
|
+
export * as Report from "./Report.ts"
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Core's development check for Frames that aren't finite (`FrameLoop.CheckFrames`), here for an app,
|
|
65
|
+
* which installs `@wardic/dom` and not `@wardic/core`. Off by default; merged beside {@link DomLive} as
|
|
66
|
+
* `Layer.succeed(CheckFrames, true)`, it fails an animation whose Frame holds a NaN or an Infinity,
|
|
67
|
+
* naming its key and value, where the page would drop it without a word. It checks every Frame, so
|
|
68
|
+
* it's for development: see https://wardic.dev/guides/observability.
|
|
69
|
+
*/
|
|
70
|
+
// ADR-0030 decision 7; re-exported here for an app (#586).
|
|
71
|
+
export { CheckFrames } from "@wardic/core/FrameLoop"
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The browser Environment, composed: the DOM's services, core's interpolators with the DOM's Key
|
|
75
|
+
* rules, and the Frame loop over the DOM's Clock, needing nothing else. Hand it to `WardicProvider`'s
|
|
76
|
+
* `env` to run the engine on the page.
|
|
77
|
+
*
|
|
78
|
+
* It has no input: a binding's gestures build `DomInput.layer` with the first one an app uses, so an
|
|
79
|
+
* app with none ships no input code.
|
|
80
|
+
*
|
|
81
|
+
* Client-only: a server render builds no runtime, and building DomLive on a server fails with an
|
|
82
|
+
* error naming the service and the missing global: see https://wardic.dev/guides/server-rendering.
|
|
83
|
+
*/
|
|
84
|
+
// Key rules: ADR-0035. Frame loop: ADR-0027. `DomClock.layer` also provides the `RefreshFrameSource`
|
|
85
|
+
// the flush gate requests its flushes from (ADR-0031). `DomProbe.layer` writes the `viewport` and
|
|
86
|
+
// `prefersReducedMotion` Atoms through the Environment's `AtomRegistry`, and consumers read them
|
|
87
|
+
// through the same registry, so `AtomRegistry` is provided to the Layers with `Layer.provideMerge` and
|
|
88
|
+
// kept in the output, as the headless `testLayer` (packages/test) does: R = never. No InputSource:
|
|
89
|
+
// #511, ADR-0067 decision 14. Client-only: ADR-0021, decision 7.
|
|
90
|
+
export const DomLive: Layer.Layer<
|
|
91
|
+
| Clock.Clock
|
|
92
|
+
| RefreshFrameSource.RefreshFrameSource
|
|
93
|
+
| FrameLoop.FrameLoop
|
|
94
|
+
| Renderer.Renderer
|
|
95
|
+
| Probe.Probe
|
|
96
|
+
| InterpolatorRegistry.InterpolatorRegistry
|
|
97
|
+
| ReducedMotion.PositionalKeys
|
|
98
|
+
| DefaultTransition.DefaultTransition
|
|
99
|
+
| Announcer.Announcer
|
|
100
|
+
| AtomRegistry.AtomRegistry
|
|
101
|
+
> = FrameLoop.layer.pipe(
|
|
102
|
+
Layer.provideMerge(
|
|
103
|
+
Layer.mergeAll(DomClock.layer, DomRenderer.layer, DomProbe.layer, DomInterpolators.layer, DomPositionalKeys.layer, DomDefaultTransition.layer, DomAnnouncer.layer),
|
|
104
|
+
),
|
|
105
|
+
Layer.provideMerge(AtomRegistry.layer),
|
|
106
|
+
)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The DOM's **Announcement** channel (ADR-0037): `AnnouncerDomLive`, core's `Announcer.Announcer` as
|
|
3
|
+
* one visually hidden live region per document, `aria-live="assertive"` and `aria-atomic`, shared by
|
|
4
|
+
* every Reorder group on the page. Assertive, because each announcement answers a key just pressed;
|
|
5
|
+
* the caller thins a held arrow's repeats to the last. Each announcement replaces the last, and one
|
|
6
|
+
* that repeats the last (an arrow at an end re-announcing the position) gets a trailing no-break
|
|
7
|
+
* space, so a screen reader sees a change and speaks it again.
|
|
8
|
+
*
|
|
9
|
+
* A screen reader watches a live region from when it's in the page, so the region has to be there
|
|
10
|
+
* before the first announcement: {@link prepareAnnouncer} adds it (a Reorder group's controller calls
|
|
11
|
+
* it when its element attaches), and an announcement adds it too if nothing did.
|
|
12
|
+
*
|
|
13
|
+
* {@link instructionsId} gives the id of a hidden element holding a group's instructions, one per
|
|
14
|
+
* distinct text per document, for its items' `aria-describedby`. {@link instructionsIdOf} gives the
|
|
15
|
+
* id alone, and {@link showInstructions} adds its element.
|
|
16
|
+
*
|
|
17
|
+
* Client-only (ADR-0021, decision 7): building the layer on a server fails with the client-only error.
|
|
18
|
+
*
|
|
19
|
+
* Tier 2 (ADR-0008): for DOM binding authors.
|
|
20
|
+
*/
|
|
21
|
+
import * as Announcer from "@wardic/core/Announcer"
|
|
22
|
+
import * as Bool from "effect/Boolean"
|
|
23
|
+
import * as Effect from "effect/Effect"
|
|
24
|
+
import * as Layer from "effect/Layer"
|
|
25
|
+
import * as Option from "effect/Option"
|
|
26
|
+
import * as Predicate from "effect/Predicate"
|
|
27
|
+
import { requireGlobal } from "./require-global.ts"
|
|
28
|
+
|
|
29
|
+
/** The attribute that marks the live region, for a test or a stylesheet to find it. */
|
|
30
|
+
export const ANNOUNCER_ATTRIBUTE = "data-wardic-announcer"
|
|
31
|
+
|
|
32
|
+
/** Hidden from sight, not from assistive technology: the usual visually hidden rule. */
|
|
33
|
+
const VISUALLY_HIDDEN =
|
|
34
|
+
"position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap"
|
|
35
|
+
|
|
36
|
+
/** Each document's live region, once added. */
|
|
37
|
+
// Module state (#460, Q7): each document's live region, held weakly by the document.
|
|
38
|
+
const regions = new WeakMap<Document, HTMLElement>()
|
|
39
|
+
|
|
40
|
+
/** `doc`'s live region, added to its body when it has none. */
|
|
41
|
+
const regionOf = (doc: Document): HTMLElement =>
|
|
42
|
+
Option.getOrElse(
|
|
43
|
+
Option.filter(Option.fromUndefinedOr(regions.get(doc)), (region) => region.isConnected),
|
|
44
|
+
() => addRegion(doc),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
/** Add `doc`'s live region to its body. */
|
|
48
|
+
const addRegion = (doc: Document): HTMLElement => {
|
|
49
|
+
const region = doc.createElement("div")
|
|
50
|
+
region.setAttribute(ANNOUNCER_ATTRIBUTE, "")
|
|
51
|
+
region.setAttribute("aria-live", "assertive")
|
|
52
|
+
region.setAttribute("aria-atomic", "true")
|
|
53
|
+
region.setAttribute("style", VISUALLY_HIDDEN)
|
|
54
|
+
doc.body.appendChild(region)
|
|
55
|
+
regions.set(doc, region)
|
|
56
|
+
return region
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The live document `node` is in: its own when that has a window, else the page's. A Solid ref
|
|
61
|
+
* runs while a new element still belongs to its template's inert document (no window, no body),
|
|
62
|
+
* before it's inserted.
|
|
63
|
+
*/
|
|
64
|
+
export const documentOf = (node: Node): Document => {
|
|
65
|
+
const doc = node.ownerDocument
|
|
66
|
+
if (doc === null || doc.defaultView === null) return document
|
|
67
|
+
return doc
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Adds `doc`'s live region now, before its first announcement, so a screen reader is already watching it. */
|
|
71
|
+
export const prepareAnnouncer = (doc: Document): void => {
|
|
72
|
+
regionOf(doc)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Speaks `message` in `doc`'s live region, replacing the last. A repeat of the last gets a trailing
|
|
77
|
+
* no-break space, so it's read out again.
|
|
78
|
+
*/
|
|
79
|
+
export const announceIn = (doc: Document, message: string): void => {
|
|
80
|
+
const region = regionOf(doc)
|
|
81
|
+
region.textContent = Bool.match(region.textContent === message, { onTrue: () => `${message}\u00A0`, onFalse: () => message })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Each document's instructions ids, by text. */
|
|
85
|
+
// Module state (#460, Q7): each document's instructions, held weakly by the document.
|
|
86
|
+
const instructions = new WeakMap<Document, Map<string, string>>()
|
|
87
|
+
// Module state (#460, Q7): an id counter; an id need only be unique in its document.
|
|
88
|
+
let nextInstructions = 1
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The id of `doc`'s hidden element holding `text`, one per distinct text, for the `aria-describedby` of
|
|
92
|
+
* every item that shows it. Given here, with no element added: a binding works it out as it renders, and
|
|
93
|
+
* adds the element with `showInstructions` in an effect.
|
|
94
|
+
*/
|
|
95
|
+
// #320.
|
|
96
|
+
export const instructionsIdOf = (doc: Document, text: string): string => {
|
|
97
|
+
const byText = Option.getOrElse(Option.fromUndefinedOr(instructions.get(doc)), () => {
|
|
98
|
+
const added = new Map<string, string>()
|
|
99
|
+
instructions.set(doc, added)
|
|
100
|
+
return added
|
|
101
|
+
})
|
|
102
|
+
return Option.getOrElse(Option.fromUndefinedOr(byText.get(text)), () => {
|
|
103
|
+
const id = `wardic-instructions-${nextInstructions++}`
|
|
104
|
+
byText.set(text, id)
|
|
105
|
+
return id
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Adds `doc`'s hidden element holding `text`, under `instructionsIdOf`'s id, unless it's there already. */
|
|
110
|
+
export const showInstructions = (doc: Document, text: string): void => {
|
|
111
|
+
const id = instructionsIdOf(doc, text)
|
|
112
|
+
if (Predicate.isNotNull(doc.getElementById(id))) return
|
|
113
|
+
const el = doc.createElement("div")
|
|
114
|
+
el.id = id
|
|
115
|
+
el.hidden = true
|
|
116
|
+
el.textContent = text
|
|
117
|
+
doc.body.appendChild(el)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** `instructionsIdOf`, with its element added now: added again if it was removed. */
|
|
121
|
+
export const instructionsId = (doc: Document, text: string): string => {
|
|
122
|
+
showInstructions(doc, text)
|
|
123
|
+
return instructionsIdOf(doc, text)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Core's `Announcer`, speaking in the page's `document`'s live region. */
|
|
127
|
+
export const AnnouncerDomLive: Layer.Layer<Announcer.Announcer> = Layer.effect(Announcer.Announcer)(
|
|
128
|
+
Effect.gen(function* () {
|
|
129
|
+
yield* requireGlobal("DomAnnouncer.layer", "document")
|
|
130
|
+
return Announcer.Announcer.of({ announce: (message) => announceIn(document, message) })
|
|
131
|
+
}),
|
|
132
|
+
)
|