@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.
Files changed (285) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +8 -2
  3. package/THIRD_PARTY_NOTICES.md +145 -0
  4. package/dist/Appear.d.ts +24 -0
  5. package/dist/Appear.d.ts.map +1 -0
  6. package/dist/Appear.js +25 -0
  7. package/dist/Appear.js.map +1 -0
  8. package/dist/Color.d.ts +14 -0
  9. package/dist/Color.d.ts.map +1 -0
  10. package/dist/Color.js +16 -0
  11. package/dist/Color.js.map +1 -0
  12. package/dist/DomAnnouncer.d.ts +13 -0
  13. package/dist/DomAnnouncer.d.ts.map +1 -0
  14. package/dist/DomAnnouncer.js +14 -0
  15. package/dist/DomAnnouncer.js.map +1 -0
  16. package/dist/DomClock.d.ts +11 -0
  17. package/dist/DomClock.d.ts.map +1 -0
  18. package/dist/DomClock.js +12 -0
  19. package/dist/DomClock.js.map +1 -0
  20. package/dist/DomDefaultTransition.d.ts +12 -0
  21. package/dist/DomDefaultTransition.d.ts.map +1 -0
  22. package/dist/DomDefaultTransition.js +13 -0
  23. package/dist/DomDefaultTransition.js.map +1 -0
  24. package/dist/DomInput.d.ts +13 -0
  25. package/dist/DomInput.d.ts.map +1 -0
  26. package/dist/DomInput.js +15 -0
  27. package/dist/DomInput.js.map +1 -0
  28. package/dist/DomInterpolators.d.ts +13 -0
  29. package/dist/DomInterpolators.d.ts.map +1 -0
  30. package/dist/DomInterpolators.js +14 -0
  31. package/dist/DomInterpolators.js.map +1 -0
  32. package/dist/DomKeyRules.d.ts +13 -0
  33. package/dist/DomKeyRules.d.ts.map +1 -0
  34. package/dist/DomKeyRules.js +14 -0
  35. package/dist/DomKeyRules.js.map +1 -0
  36. package/dist/DomPositionalKeys.d.ts +13 -0
  37. package/dist/DomPositionalKeys.d.ts.map +1 -0
  38. package/dist/DomPositionalKeys.js +14 -0
  39. package/dist/DomPositionalKeys.js.map +1 -0
  40. package/dist/DomProbe.d.ts +13 -0
  41. package/dist/DomProbe.d.ts.map +1 -0
  42. package/dist/DomProbe.js +14 -0
  43. package/dist/DomProbe.js.map +1 -0
  44. package/dist/DomProjectionSystem.d.ts +28 -0
  45. package/dist/DomProjectionSystem.d.ts.map +1 -0
  46. package/dist/DomProjectionSystem.js +41 -0
  47. package/dist/DomProjectionSystem.js.map +1 -0
  48. package/dist/DomRenderer.d.ts +13 -0
  49. package/dist/DomRenderer.d.ts.map +1 -0
  50. package/dist/DomRenderer.js +15 -0
  51. package/dist/DomRenderer.js.map +1 -0
  52. package/dist/FlushGate.d.ts +48 -0
  53. package/dist/FlushGate.d.ts.map +1 -0
  54. package/dist/FlushGate.js +62 -0
  55. package/dist/FlushGate.js.map +1 -0
  56. package/dist/MeasuredPair.d.ts +14 -0
  57. package/dist/MeasuredPair.d.ts.map +1 -0
  58. package/dist/MeasuredPair.js +16 -0
  59. package/dist/MeasuredPair.js.map +1 -0
  60. package/dist/OwnValues.d.ts +14 -0
  61. package/dist/OwnValues.d.ts.map +1 -0
  62. package/dist/OwnValues.js +15 -0
  63. package/dist/OwnValues.js.map +1 -0
  64. package/dist/ProjectionSystem.d.ts +34 -0
  65. package/dist/ProjectionSystem.d.ts.map +1 -0
  66. package/dist/ProjectionSystem.js +29 -0
  67. package/dist/ProjectionSystem.js.map +1 -0
  68. package/dist/ReorderController.d.ts +41 -0
  69. package/dist/ReorderController.d.ts.map +1 -0
  70. package/dist/ReorderController.js +44 -0
  71. package/dist/ReorderController.js.map +1 -0
  72. package/dist/Report.d.ts +12 -0
  73. package/dist/Report.d.ts.map +1 -0
  74. package/dist/Report.js +13 -0
  75. package/dist/Report.js.map +1 -0
  76. package/dist/ScrollOffset.d.ts +34 -0
  77. package/dist/ScrollOffset.d.ts.map +1 -0
  78. package/dist/ScrollOffset.js +42 -0
  79. package/dist/ScrollOffset.js.map +1 -0
  80. package/dist/Style.d.ts +15 -0
  81. package/dist/Style.d.ts.map +1 -0
  82. package/dist/Style.js +17 -0
  83. package/dist/Style.js.map +1 -0
  84. package/dist/Svg.d.ts +13 -0
  85. package/dist/Svg.d.ts.map +1 -0
  86. package/dist/Svg.js +14 -0
  87. package/dist/Svg.js.map +1 -0
  88. package/dist/Variables.d.ts +13 -0
  89. package/dist/Variables.d.ts.map +1 -0
  90. package/dist/Variables.js +14 -0
  91. package/dist/Variables.js.map +1 -0
  92. package/dist/index.d.ts +72 -0
  93. package/dist/index.d.ts.map +1 -0
  94. package/dist/index.js +60 -0
  95. package/dist/index.js.map +1 -0
  96. package/dist/internal/announcer.d.ts +50 -0
  97. package/dist/internal/announcer.d.ts.map +1 -0
  98. package/dist/internal/announcer.js +115 -0
  99. package/dist/internal/announcer.js.map +1 -0
  100. package/dist/internal/appear.d.ts +169 -0
  101. package/dist/internal/appear.d.ts.map +1 -0
  102. package/dist/internal/appear.js +499 -0
  103. package/dist/internal/appear.js.map +1 -0
  104. package/dist/internal/auto-scroll.d.ts +24 -0
  105. package/dist/internal/auto-scroll.d.ts.map +1 -0
  106. package/dist/internal/auto-scroll.js +124 -0
  107. package/dist/internal/auto-scroll.js.map +1 -0
  108. package/dist/internal/clock.d.ts +19 -0
  109. package/dist/internal/clock.d.ts.map +1 -0
  110. package/dist/internal/clock.js +39 -0
  111. package/dist/internal/clock.js.map +1 -0
  112. package/dist/internal/colors.d.ts +45 -0
  113. package/dist/internal/colors.d.ts.map +1 -0
  114. package/dist/internal/colors.js +193 -0
  115. package/dist/internal/colors.js.map +1 -0
  116. package/dist/internal/complex.d.ts +24 -0
  117. package/dist/internal/complex.d.ts.map +1 -0
  118. package/dist/internal/complex.js +28 -0
  119. package/dist/internal/complex.js.map +1 -0
  120. package/dist/internal/content-watch.d.ts +20 -0
  121. package/dist/internal/content-watch.d.ts.map +1 -0
  122. package/dist/internal/content-watch.js +76 -0
  123. package/dist/internal/content-watch.js.map +1 -0
  124. package/dist/internal/default-transition.d.ts +25 -0
  125. package/dist/internal/default-transition.d.ts.map +1 -0
  126. package/dist/internal/default-transition.js +41 -0
  127. package/dist/internal/default-transition.js.map +1 -0
  128. package/dist/internal/drag-defaults.d.ts +18 -0
  129. package/dist/internal/drag-defaults.d.ts.map +1 -0
  130. package/dist/internal/drag-defaults.js +38 -0
  131. package/dist/internal/drag-defaults.js.map +1 -0
  132. package/dist/internal/gate.d.ts +186 -0
  133. package/dist/internal/gate.d.ts.map +1 -0
  134. package/dist/internal/gate.js +453 -0
  135. package/dist/internal/gate.js.map +1 -0
  136. package/dist/internal/input.d.ts +41 -0
  137. package/dist/internal/input.d.ts.map +1 -0
  138. package/dist/internal/input.js +476 -0
  139. package/dist/internal/input.js.map +1 -0
  140. package/dist/internal/interpolators.d.ts +19 -0
  141. package/dist/internal/interpolators.d.ts.map +1 -0
  142. package/dist/internal/interpolators.js +30 -0
  143. package/dist/internal/interpolators.js.map +1 -0
  144. package/dist/internal/key-rules.d.ts +11 -0
  145. package/dist/internal/key-rules.d.ts.map +1 -0
  146. package/dist/internal/key-rules.js +244 -0
  147. package/dist/internal/key-rules.js.map +1 -0
  148. package/dist/internal/measure.d.ts +150 -0
  149. package/dist/internal/measure.d.ts.map +1 -0
  150. package/dist/internal/measure.js +278 -0
  151. package/dist/internal/measure.js.map +1 -0
  152. package/dist/internal/measured-pairs.d.ts +38 -0
  153. package/dist/internal/measured-pairs.d.ts.map +1 -0
  154. package/dist/internal/measured-pairs.js +251 -0
  155. package/dist/internal/measured-pairs.js.map +1 -0
  156. package/dist/internal/own-values.d.ts +35 -0
  157. package/dist/internal/own-values.d.ts.map +1 -0
  158. package/dist/internal/own-values.js +117 -0
  159. package/dist/internal/own-values.js.map +1 -0
  160. package/dist/internal/pass-ends.d.ts +21 -0
  161. package/dist/internal/pass-ends.d.ts.map +1 -0
  162. package/dist/internal/pass-ends.js +14 -0
  163. package/dist/internal/pass-ends.js.map +1 -0
  164. package/dist/internal/positional-keys.d.ts +16 -0
  165. package/dist/internal/positional-keys.d.ts.map +1 -0
  166. package/dist/internal/positional-keys.js +21 -0
  167. package/dist/internal/positional-keys.js.map +1 -0
  168. package/dist/internal/probe.d.ts +10 -0
  169. package/dist/internal/probe.d.ts.map +1 -0
  170. package/dist/internal/probe.js +117 -0
  171. package/dist/internal/probe.js.map +1 -0
  172. package/dist/internal/projection-defaults.d.ts +34 -0
  173. package/dist/internal/projection-defaults.d.ts.map +1 -0
  174. package/dist/internal/projection-defaults.js +21 -0
  175. package/dist/internal/projection-defaults.js.map +1 -0
  176. package/dist/internal/projection.d.ts +529 -0
  177. package/dist/internal/projection.d.ts.map +1 -0
  178. package/dist/internal/projection.js +4990 -0
  179. package/dist/internal/projection.js.map +1 -0
  180. package/dist/internal/renderer.d.ts +86 -0
  181. package/dist/internal/renderer.d.ts.map +1 -0
  182. package/dist/internal/renderer.js +616 -0
  183. package/dist/internal/renderer.js.map +1 -0
  184. package/dist/internal/reorder.d.ts +159 -0
  185. package/dist/internal/reorder.d.ts.map +1 -0
  186. package/dist/internal/reorder.js +437 -0
  187. package/dist/internal/reorder.js.map +1 -0
  188. package/dist/internal/report.d.ts +30 -0
  189. package/dist/internal/report.d.ts.map +1 -0
  190. package/dist/internal/report.js +60 -0
  191. package/dist/internal/report.js.map +1 -0
  192. package/dist/internal/require-global.d.ts +11 -0
  193. package/dist/internal/require-global.d.ts.map +1 -0
  194. package/dist/internal/require-global.js +31 -0
  195. package/dist/internal/require-global.js.map +1 -0
  196. package/dist/internal/reveal.d.ts +16 -0
  197. package/dist/internal/reveal.d.ts.map +1 -0
  198. package/dist/internal/reveal.js +66 -0
  199. package/dist/internal/reveal.js.map +1 -0
  200. package/dist/internal/runner.d.ts +21 -0
  201. package/dist/internal/runner.d.ts.map +1 -0
  202. package/dist/internal/runner.js +48 -0
  203. package/dist/internal/runner.js.map +1 -0
  204. package/dist/internal/scale-correction.d.ts +80 -0
  205. package/dist/internal/scale-correction.d.ts.map +1 -0
  206. package/dist/internal/scale-correction.js +227 -0
  207. package/dist/internal/scale-correction.js.map +1 -0
  208. package/dist/internal/scroll.d.ts +35 -0
  209. package/dist/internal/scroll.d.ts.map +1 -0
  210. package/dist/internal/scroll.js +244 -0
  211. package/dist/internal/scroll.js.map +1 -0
  212. package/dist/internal/scrollers.d.ts +35 -0
  213. package/dist/internal/scrollers.d.ts.map +1 -0
  214. package/dist/internal/scrollers.js +38 -0
  215. package/dist/internal/scrollers.js.map +1 -0
  216. package/dist/internal/starting-style.d.ts +141 -0
  217. package/dist/internal/starting-style.d.ts.map +1 -0
  218. package/dist/internal/starting-style.js +300 -0
  219. package/dist/internal/starting-style.js.map +1 -0
  220. package/dist/internal/svg.d.ts +60 -0
  221. package/dist/internal/svg.d.ts.map +1 -0
  222. package/dist/internal/svg.js +257 -0
  223. package/dist/internal/svg.js.map +1 -0
  224. package/dist/internal/variables.d.ts +45 -0
  225. package/dist/internal/variables.d.ts.map +1 -0
  226. package/dist/internal/variables.js +92 -0
  227. package/dist/internal/variables.js.map +1 -0
  228. package/package.json +65 -4
  229. package/src/Appear.ts +45 -0
  230. package/src/Color.ts +24 -0
  231. package/src/DomAnnouncer.ts +22 -0
  232. package/src/DomClock.ts +13 -0
  233. package/src/DomDefaultTransition.ts +16 -0
  234. package/src/DomInput.ts +22 -0
  235. package/src/DomInterpolators.ts +16 -0
  236. package/src/DomKeyRules.ts +17 -0
  237. package/src/DomPositionalKeys.ts +16 -0
  238. package/src/DomProbe.ts +15 -0
  239. package/src/DomProjectionSystem.ts +44 -0
  240. package/src/DomRenderer.ts +26 -0
  241. package/src/FlushGate.ts +81 -0
  242. package/src/MeasuredPair.ts +15 -0
  243. package/src/OwnValues.ts +19 -0
  244. package/src/ProjectionSystem.ts +51 -0
  245. package/src/ReorderController.ts +72 -0
  246. package/src/Report.ts +17 -0
  247. package/src/ScrollOffset.ts +54 -0
  248. package/src/Style.ts +37 -0
  249. package/src/Svg.ts +21 -0
  250. package/src/Variables.ts +17 -0
  251. package/src/index.ts +106 -0
  252. package/src/internal/announcer.ts +132 -0
  253. package/src/internal/appear.ts +739 -0
  254. package/src/internal/auto-scroll.ts +176 -0
  255. package/src/internal/clock.ts +45 -0
  256. package/src/internal/colors.ts +258 -0
  257. package/src/internal/complex.ts +29 -0
  258. package/src/internal/content-watch.ts +84 -0
  259. package/src/internal/culori.d.ts +58 -0
  260. package/src/internal/default-transition.ts +50 -0
  261. package/src/internal/drag-defaults.ts +54 -0
  262. package/src/internal/gate.ts +593 -0
  263. package/src/internal/input.ts +579 -0
  264. package/src/internal/interpolators.ts +33 -0
  265. package/src/internal/key-rules.ts +357 -0
  266. package/src/internal/measure.ts +357 -0
  267. package/src/internal/measured-pairs.ts +297 -0
  268. package/src/internal/own-values.ts +166 -0
  269. package/src/internal/pass-ends.ts +23 -0
  270. package/src/internal/positional-keys.ts +24 -0
  271. package/src/internal/probe.ts +176 -0
  272. package/src/internal/projection-defaults.ts +45 -0
  273. package/src/internal/projection.ts +5999 -0
  274. package/src/internal/renderer.ts +646 -0
  275. package/src/internal/reorder.ts +604 -0
  276. package/src/internal/report.ts +69 -0
  277. package/src/internal/require-global.ts +46 -0
  278. package/src/internal/reveal.ts +69 -0
  279. package/src/internal/runner.ts +64 -0
  280. package/src/internal/scale-correction.ts +310 -0
  281. package/src/internal/scroll.ts +356 -0
  282. package/src/internal/scrollers.ts +51 -0
  283. package/src/internal/starting-style.ts +396 -0
  284. package/src/internal/svg.ts +300 -0
  285. 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"
@@ -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
+ )