@voila.dev/motion 0.0.0-stage → 5.0.113

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 (273) hide show
  1. package/README.md +105 -2
  2. package/dist/accessibility/reduced-motion.d.ts +13 -0
  3. package/dist/accessibility/reduced-motion.d.ts.map +1 -0
  4. package/dist/accessibility/reduced-motion.js +56 -0
  5. package/dist/accessibility/reduced-motion.js.map +7 -0
  6. package/dist/animate/animate.d.ts +21 -0
  7. package/dist/animate/animate.d.ts.map +1 -0
  8. package/dist/animate/animate.js +76 -0
  9. package/dist/animate/animate.js.map +7 -0
  10. package/dist/animate/controls.d.ts +16 -0
  11. package/dist/animate/controls.d.ts.map +1 -0
  12. package/dist/animate/controls.js +62 -0
  13. package/dist/animate/controls.js.map +7 -0
  14. package/dist/animate/dom.d.ts +11 -0
  15. package/dist/animate/dom.d.ts.map +1 -0
  16. package/dist/animate/dom.js +112 -0
  17. package/dist/animate/dom.js.map +7 -0
  18. package/dist/animate/js-animation.d.ts +25 -0
  19. package/dist/animate/js-animation.d.ts.map +1 -0
  20. package/dist/animate/js-animation.js +117 -0
  21. package/dist/animate/js-animation.js.map +7 -0
  22. package/dist/animate/plan.d.ts +17 -0
  23. package/dist/animate/plan.d.ts.map +1 -0
  24. package/dist/animate/plan.js +48 -0
  25. package/dist/animate/plan.js.map +7 -0
  26. package/dist/animate/style.d.ts +27 -0
  27. package/dist/animate/style.d.ts.map +1 -0
  28. package/dist/animate/style.js +96 -0
  29. package/dist/animate/style.js.map +7 -0
  30. package/dist/animate/types.d.ts +62 -0
  31. package/dist/animate/types.d.ts.map +1 -0
  32. package/dist/animate/types.js +1 -0
  33. package/dist/animate/types.js.map +7 -0
  34. package/dist/animate/value.d.ts +13 -0
  35. package/dist/animate/value.d.ts.map +1 -0
  36. package/dist/animate/value.js +71 -0
  37. package/dist/animate/value.js.map +7 -0
  38. package/dist/animate/waapi.d.ts +20 -0
  39. package/dist/animate/waapi.d.ts.map +1 -0
  40. package/dist/animate/waapi.js +152 -0
  41. package/dist/animate/waapi.js.map +7 -0
  42. package/dist/dom.d.ts +12 -0
  43. package/dist/dom.d.ts.map +1 -0
  44. package/dist/dom.js +17 -0
  45. package/dist/dom.js.map +7 -0
  46. package/dist/easing/cubic-bezier.d.ts +4 -0
  47. package/dist/easing/cubic-bezier.d.ts.map +1 -0
  48. package/dist/easing/cubic-bezier.js +54 -0
  49. package/dist/easing/cubic-bezier.js.map +7 -0
  50. package/dist/easing/linear-css.d.ts +9 -0
  51. package/dist/easing/linear-css.d.ts.map +1 -0
  52. package/dist/easing/linear-css.js +30 -0
  53. package/dist/easing/linear-css.js.map +7 -0
  54. package/dist/easing/named.d.ts +16 -0
  55. package/dist/easing/named.d.ts.map +1 -0
  56. package/dist/easing/named.js +82 -0
  57. package/dist/easing/named.js.map +7 -0
  58. package/dist/easing/spring.d.ts +11 -0
  59. package/dist/easing/spring.d.ts.map +1 -0
  60. package/dist/easing/spring.js +18 -0
  61. package/dist/easing/spring.js.map +7 -0
  62. package/dist/easing/steps.d.ts +4 -0
  63. package/dist/easing/steps.d.ts.map +1 -0
  64. package/dist/easing/steps.js +14 -0
  65. package/dist/easing/steps.js.map +7 -0
  66. package/dist/easing/types.d.ts +5 -0
  67. package/dist/easing/types.d.ts.map +1 -0
  68. package/dist/easing/types.js +1 -0
  69. package/dist/easing/types.js.map +7 -0
  70. package/dist/frame/driver.d.ts +13 -0
  71. package/dist/frame/driver.d.ts.map +1 -0
  72. package/dist/frame/driver.js +42 -0
  73. package/dist/frame/driver.js.map +7 -0
  74. package/dist/frame/frame.d.ts +20 -0
  75. package/dist/frame/frame.d.ts.map +1 -0
  76. package/dist/frame/frame.js +77 -0
  77. package/dist/frame/frame.js.map +7 -0
  78. package/dist/generators/inertia.d.ts +20 -0
  79. package/dist/generators/inertia.d.ts.map +1 -0
  80. package/dist/generators/inertia.js +59 -0
  81. package/dist/generators/inertia.js.map +7 -0
  82. package/dist/generators/spring.d.ts +17 -0
  83. package/dist/generators/spring.d.ts.map +1 -0
  84. package/dist/generators/spring.js +87 -0
  85. package/dist/generators/spring.js.map +7 -0
  86. package/dist/generators/tween.d.ts +15 -0
  87. package/dist/generators/tween.d.ts.map +1 -0
  88. package/dist/generators/tween.js +51 -0
  89. package/dist/generators/tween.js.map +7 -0
  90. package/dist/generators/types.d.ts +30 -0
  91. package/dist/generators/types.d.ts.map +1 -0
  92. package/dist/generators/types.js +1 -0
  93. package/dist/generators/types.js.map +7 -0
  94. package/dist/index.d.ts +26 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +97 -0
  97. package/dist/index.js.map +7 -0
  98. package/dist/interpolate/array.d.ts +3 -0
  99. package/dist/interpolate/array.d.ts.map +1 -0
  100. package/dist/interpolate/array.js +12 -0
  101. package/dist/interpolate/array.js.map +7 -0
  102. package/dist/interpolate/color-parse.d.ts +14 -0
  103. package/dist/interpolate/color-parse.d.ts.map +1 -0
  104. package/dist/interpolate/color-parse.js +111 -0
  105. package/dist/interpolate/color-parse.js.map +7 -0
  106. package/dist/interpolate/color.d.ts +11 -0
  107. package/dist/interpolate/color.d.ts.map +1 -0
  108. package/dist/interpolate/color.js +74 -0
  109. package/dist/interpolate/color.js.map +7 -0
  110. package/dist/interpolate/complex.d.ts +7 -0
  111. package/dist/interpolate/complex.d.ts.map +1 -0
  112. package/dist/interpolate/complex.js +56 -0
  113. package/dist/interpolate/complex.js.map +7 -0
  114. package/dist/interpolate/interpolate.d.ts +16 -0
  115. package/dist/interpolate/interpolate.d.ts.map +1 -0
  116. package/dist/interpolate/interpolate.js +39 -0
  117. package/dist/interpolate/interpolate.js.map +7 -0
  118. package/dist/interpolate/mix.d.ts +8 -0
  119. package/dist/interpolate/mix.d.ts.map +1 -0
  120. package/dist/interpolate/mix.js +48 -0
  121. package/dist/interpolate/mix.js.map +7 -0
  122. package/dist/interpolate/number.d.ts +2 -0
  123. package/dist/interpolate/number.d.ts.map +1 -0
  124. package/dist/interpolate/number.js +7 -0
  125. package/dist/interpolate/number.js.map +7 -0
  126. package/dist/interpolate/object.d.ts +5 -0
  127. package/dist/interpolate/object.d.ts.map +1 -0
  128. package/dist/interpolate/object.js +17 -0
  129. package/dist/interpolate/object.js.map +7 -0
  130. package/dist/interpolate/path/flatten.d.ts +10 -0
  131. package/dist/interpolate/path/flatten.d.ts.map +1 -0
  132. package/dist/interpolate/path/flatten.js +135 -0
  133. package/dist/interpolate/path/flatten.js.map +7 -0
  134. package/dist/interpolate/path/morph.d.ts +9 -0
  135. package/dist/interpolate/path/morph.d.ts.map +1 -0
  136. package/dist/interpolate/path/morph.js +72 -0
  137. package/dist/interpolate/path/morph.js.map +7 -0
  138. package/dist/interpolate/path/parse.d.ts +24 -0
  139. package/dist/interpolate/path/parse.d.ts.map +1 -0
  140. package/dist/interpolate/path/parse.js +125 -0
  141. package/dist/interpolate/path/parse.js.map +7 -0
  142. package/dist/interpolate/path/rings.d.ts +13 -0
  143. package/dist/interpolate/path/rings.d.ts.map +1 -0
  144. package/dist/interpolate/path/rings.js +117 -0
  145. package/dist/interpolate/path/rings.js.map +7 -0
  146. package/dist/react/index.d.ts +7 -0
  147. package/dist/react/index.d.ts.map +1 -0
  148. package/dist/react/index.js +19 -0
  149. package/dist/react/index.js.map +7 -0
  150. package/dist/react/presence.d.ts +27 -0
  151. package/dist/react/presence.d.ts.map +1 -0
  152. package/dist/react/presence.js +135 -0
  153. package/dist/react/presence.js.map +7 -0
  154. package/dist/react/use-animate.d.ts +17 -0
  155. package/dist/react/use-animate.d.ts.map +1 -0
  156. package/dist/react/use-animate.js +34 -0
  157. package/dist/react/use-animate.js.map +7 -0
  158. package/dist/react/use-motion-value.d.ts +4 -0
  159. package/dist/react/use-motion-value.d.ts.map +1 -0
  160. package/dist/react/use-motion-value.js +11 -0
  161. package/dist/react/use-motion-value.js.map +7 -0
  162. package/dist/react/use-reduced-motion.d.ts +7 -0
  163. package/dist/react/use-reduced-motion.d.ts.map +1 -0
  164. package/dist/react/use-reduced-motion.js +19 -0
  165. package/dist/react/use-reduced-motion.js.map +7 -0
  166. package/dist/react/use-spring.d.ts +9 -0
  167. package/dist/react/use-spring.d.ts.map +1 -0
  168. package/dist/react/use-spring.js +27 -0
  169. package/dist/react/use-spring.js.map +7 -0
  170. package/dist/react/use-transform.d.ts +13 -0
  171. package/dist/react/use-transform.d.ts.map +1 -0
  172. package/dist/react/use-transform.js +44 -0
  173. package/dist/react/use-transform.js.map +7 -0
  174. package/dist/sequence/sequence.d.ts +25 -0
  175. package/dist/sequence/sequence.d.ts.map +1 -0
  176. package/dist/sequence/sequence.js +68 -0
  177. package/dist/sequence/sequence.js.map +7 -0
  178. package/dist/stagger/stagger.d.ts +12 -0
  179. package/dist/stagger/stagger.d.ts.map +1 -0
  180. package/dist/stagger/stagger.js +21 -0
  181. package/dist/stagger/stagger.js.map +7 -0
  182. package/dist/testing/manual-frames.d.ts +11 -0
  183. package/dist/testing/manual-frames.d.ts.map +1 -0
  184. package/dist/testing/manual-frames.js +40 -0
  185. package/dist/testing/manual-frames.js.map +7 -0
  186. package/dist/value/motion-value.d.ts +27 -0
  187. package/dist/value/motion-value.d.ts.map +1 -0
  188. package/dist/value/motion-value.js +88 -0
  189. package/dist/value/motion-value.js.map +7 -0
  190. package/dist/value/transform.d.ts +16 -0
  191. package/dist/value/transform.d.ts.map +1 -0
  192. package/dist/value/transform.js +28 -0
  193. package/dist/value/transform.js.map +7 -0
  194. package/dist/view/in-view.d.ts +19 -0
  195. package/dist/view/in-view.d.ts.map +1 -0
  196. package/dist/view/in-view.js +46 -0
  197. package/dist/view/in-view.js.map +7 -0
  198. package/dist/view/resize.d.ts +13 -0
  199. package/dist/view/resize.d.ts.map +1 -0
  200. package/dist/view/resize.js +72 -0
  201. package/dist/view/resize.js.map +7 -0
  202. package/dist/view/resolve-elements.d.ts +3 -0
  203. package/dist/view/resolve-elements.d.ts.map +1 -0
  204. package/dist/view/resolve-elements.js +10 -0
  205. package/dist/view/resolve-elements.js.map +7 -0
  206. package/dist/view/scroll-measure.d.ts +16 -0
  207. package/dist/view/scroll-measure.d.ts.map +1 -0
  208. package/dist/view/scroll-measure.js +51 -0
  209. package/dist/view/scroll-measure.js.map +7 -0
  210. package/dist/view/scroll-offset.d.ts +28 -0
  211. package/dist/view/scroll-offset.d.ts.map +1 -0
  212. package/dist/view/scroll-offset.js +55 -0
  213. package/dist/view/scroll-offset.js.map +7 -0
  214. package/dist/view/scroll.d.ts +34 -0
  215. package/dist/view/scroll.d.ts.map +1 -0
  216. package/dist/view/scroll.js +83 -0
  217. package/dist/view/scroll.js.map +7 -0
  218. package/package.json +57 -5
  219. package/src/accessibility/reduced-motion.ts +75 -0
  220. package/src/animate/animate.ts +155 -0
  221. package/src/animate/controls.ts +76 -0
  222. package/src/animate/dom.ts +166 -0
  223. package/src/animate/js-animation.ts +151 -0
  224. package/src/animate/plan.ts +74 -0
  225. package/src/animate/style.ts +131 -0
  226. package/src/animate/types.ts +69 -0
  227. package/src/animate/value.ts +93 -0
  228. package/src/animate/waapi.ts +212 -0
  229. package/src/dom.ts +35 -0
  230. package/src/easing/cubic-bezier.ts +64 -0
  231. package/src/easing/linear-css.ts +41 -0
  232. package/src/easing/named.ts +88 -0
  233. package/src/easing/spring.ts +25 -0
  234. package/src/easing/steps.ts +16 -0
  235. package/src/easing/types.ts +18 -0
  236. package/src/frame/driver.ts +56 -0
  237. package/src/frame/frame.ts +102 -0
  238. package/src/generators/inertia.ts +88 -0
  239. package/src/generators/spring.ts +117 -0
  240. package/src/generators/tween.ts +74 -0
  241. package/src/generators/types.ts +31 -0
  242. package/src/index.ts +91 -0
  243. package/src/interpolate/array.ts +13 -0
  244. package/src/interpolate/color-parse.ts +136 -0
  245. package/src/interpolate/color.ts +97 -0
  246. package/src/interpolate/complex.ts +80 -0
  247. package/src/interpolate/interpolate.ts +60 -0
  248. package/src/interpolate/mix.ts +74 -0
  249. package/src/interpolate/number.ts +3 -0
  250. package/src/interpolate/object.ts +20 -0
  251. package/src/interpolate/path/flatten.ts +185 -0
  252. package/src/interpolate/path/morph.ts +100 -0
  253. package/src/interpolate/path/parse.ts +162 -0
  254. package/src/interpolate/path/rings.ts +151 -0
  255. package/src/react/index.ts +11 -0
  256. package/src/react/presence.tsx +194 -0
  257. package/src/react/use-animate.ts +46 -0
  258. package/src/react/use-motion-value.ts +9 -0
  259. package/src/react/use-reduced-motion.ts +22 -0
  260. package/src/react/use-spring.ts +35 -0
  261. package/src/react/use-transform.ts +73 -0
  262. package/src/sequence/sequence.ts +129 -0
  263. package/src/stagger/stagger.ts +36 -0
  264. package/src/testing/browser-commands.d.ts +11 -0
  265. package/src/testing/manual-frames.ts +49 -0
  266. package/src/value/motion-value.ts +131 -0
  267. package/src/value/transform.ts +61 -0
  268. package/src/view/in-view.ts +74 -0
  269. package/src/view/resize.ts +101 -0
  270. package/src/view/resolve-elements.ts +11 -0
  271. package/src/view/scroll-measure.ts +83 -0
  272. package/src/view/scroll-offset.ts +100 -0
  273. package/src/view/scroll.ts +143 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/view/resolve-elements.ts"],
4
+ "sourcesContent": ["/** Elements named by a selector, a list or a single element. */\nexport function resolveElements(\n\ttarget: Element | string | readonly Element[],\n): readonly Element[] {\n\tif (typeof target === \"string\") {\n\t\treturn typeof document === \"undefined\"\n\t\t\t? []\n\t\t\t: Array.from(document.querySelectorAll(target));\n\t}\n\treturn \"length\" in target ? target : [target as Element];\n}\n"],
5
+ "mappings": "AACO,SAAS,gBACf,QACqB;AACrB,MAAI,OAAO,WAAW,UAAU;AAC/B,WAAO,OAAO,aAAa,cACxB,CAAC,IACD,MAAM,KAAK,SAAS,iBAAiB,MAAM,CAAC;AAAA,EAChD;AACA,SAAO,YAAY,SAAS,SAAS,CAAC,MAAiB;AACxD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,16 @@
1
+ import type { ScrollGeometry } from "./scroll-offset.js";
2
+ export type ScrollAxis = "x" | "y";
3
+ /** The page's own scroller, which reports its scroll events on `window`. */
4
+ export declare function documentScroller(): Element;
5
+ export interface ScrollMeasurement {
6
+ readonly scroll: number;
7
+ /** The furthest the container can scroll along the axis. */
8
+ readonly scrollLength: number;
9
+ readonly geometry: ScrollGeometry;
10
+ }
11
+ /**
12
+ * Reads layout, so it belongs in the frame's read phase only. Without a
13
+ * target, the target is the scrolled content itself.
14
+ */
15
+ export declare function measureScroll(container: Element, axis: ScrollAxis, target: Element | undefined): ScrollMeasurement;
16
+ //# sourceMappingURL=scroll-measure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-measure.d.ts","sourceRoot":"","sources":["../../src/view/scroll-measure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,UAAU,GAAG,GAAG,GAAG,GAAG,CAAC;AA8BnC,4EAA4E;AAC5E,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC5B,SAAS,EAAE,OAAO,EAClB,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,OAAO,GAAG,SAAS,GACzB,iBAAiB,CA8BnB"}
@@ -0,0 +1,51 @@
1
+ const AXES = {
2
+ y: {
3
+ scroll: "scrollTop",
4
+ scrollLength: "scrollHeight",
5
+ client: "clientHeight",
6
+ clientStart: "clientTop",
7
+ rectStart: "top",
8
+ rectLength: "height"
9
+ },
10
+ x: {
11
+ scroll: "scrollLeft",
12
+ scrollLength: "scrollWidth",
13
+ client: "clientWidth",
14
+ clientStart: "clientLeft",
15
+ rectStart: "left",
16
+ rectLength: "width"
17
+ }
18
+ };
19
+ function documentScroller() {
20
+ return document.scrollingElement ?? document.documentElement;
21
+ }
22
+ function measureScroll(container, axis, target) {
23
+ const keys = AXES[axis];
24
+ const scroll = container[keys.scroll];
25
+ const viewportLength = container[keys.client];
26
+ const contentLength = container[keys.scrollLength];
27
+ const scrollLength = Math.max(0, contentLength - viewportLength);
28
+ if (target === void 0) {
29
+ return {
30
+ scroll,
31
+ scrollLength,
32
+ geometry: { targetStart: 0, targetLength: contentLength, viewportLength }
33
+ };
34
+ }
35
+ const scrollportStart = container === documentScroller() ? 0 : container.getBoundingClientRect()[keys.rectStart] + container[keys.clientStart];
36
+ const targetRect = target.getBoundingClientRect();
37
+ return {
38
+ scroll,
39
+ scrollLength,
40
+ geometry: {
41
+ targetStart: targetRect[keys.rectStart] - scrollportStart + scroll,
42
+ targetLength: targetRect[keys.rectLength],
43
+ viewportLength
44
+ }
45
+ };
46
+ }
47
+ export {
48
+ documentScroller,
49
+ measureScroll
50
+ };
51
+ //# sourceMappingURL=scroll-measure.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/view/scroll-measure.ts"],
4
+ "sourcesContent": ["import type { ScrollGeometry } from \"#/view/scroll-offset.ts\";\n\nexport type ScrollAxis = \"x\" | \"y\";\n\ninterface AxisKeys {\n\treadonly scroll: \"scrollTop\" | \"scrollLeft\";\n\treadonly scrollLength: \"scrollHeight\" | \"scrollWidth\";\n\treadonly client: \"clientHeight\" | \"clientWidth\";\n\treadonly clientStart: \"clientTop\" | \"clientLeft\";\n\treadonly rectStart: \"top\" | \"left\";\n\treadonly rectLength: \"height\" | \"width\";\n}\n\nconst AXES: Readonly<Record<ScrollAxis, AxisKeys>> = {\n\ty: {\n\t\tscroll: \"scrollTop\",\n\t\tscrollLength: \"scrollHeight\",\n\t\tclient: \"clientHeight\",\n\t\tclientStart: \"clientTop\",\n\t\trectStart: \"top\",\n\t\trectLength: \"height\",\n\t},\n\tx: {\n\t\tscroll: \"scrollLeft\",\n\t\tscrollLength: \"scrollWidth\",\n\t\tclient: \"clientWidth\",\n\t\tclientStart: \"clientLeft\",\n\t\trectStart: \"left\",\n\t\trectLength: \"width\",\n\t},\n};\n\n/** The page's own scroller, which reports its scroll events on `window`. */\nexport function documentScroller(): Element {\n\treturn document.scrollingElement ?? document.documentElement;\n}\n\nexport interface ScrollMeasurement {\n\treadonly scroll: number;\n\t/** The furthest the container can scroll along the axis. */\n\treadonly scrollLength: number;\n\treadonly geometry: ScrollGeometry;\n}\n\n/**\n * Reads layout, so it belongs in the frame's read phase only. Without a\n * target, the target is the scrolled content itself.\n */\nexport function measureScroll(\n\tcontainer: Element,\n\taxis: ScrollAxis,\n\ttarget: Element | undefined,\n): ScrollMeasurement {\n\tconst keys = AXES[axis];\n\tconst scroll = container[keys.scroll];\n\tconst viewportLength = container[keys.client];\n\tconst contentLength = container[keys.scrollLength];\n\tconst scrollLength = Math.max(0, contentLength - viewportLength);\n\tif (target === undefined) {\n\t\treturn {\n\t\t\tscroll,\n\t\t\tscrollLength,\n\t\t\tgeometry: { targetStart: 0, targetLength: contentLength, viewportLength },\n\t\t};\n\t}\n\t// The page scroller's own rect moves with its scroll; its scrollport is\n\t// the viewport, which starts at 0.\n\tconst scrollportStart =\n\t\tcontainer === documentScroller()\n\t\t\t? 0\n\t\t\t: container.getBoundingClientRect()[keys.rectStart] +\n\t\t\t\tcontainer[keys.clientStart];\n\tconst targetRect = target.getBoundingClientRect();\n\treturn {\n\t\tscroll,\n\t\tscrollLength,\n\t\tgeometry: {\n\t\t\ttargetStart: targetRect[keys.rectStart] - scrollportStart + scroll,\n\t\t\ttargetLength: targetRect[keys.rectLength],\n\t\t\tviewportLength,\n\t\t},\n\t};\n}\n"],
5
+ "mappings": "AAaA,MAAM,OAA+C;AAAA,EACpD,GAAG;AAAA,IACF,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,WAAW;AAAA,IACX,YAAY;AAAA,EACb;AAAA,EACA,GAAG;AAAA,IACF,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,WAAW;AAAA,IACX,YAAY;AAAA,EACb;AACD;AAGO,SAAS,mBAA4B;AAC3C,SAAO,SAAS,oBAAoB,SAAS;AAC9C;AAaO,SAAS,cACf,WACA,MACA,QACoB;AACpB,QAAM,OAAO,KAAK,IAAI;AACtB,QAAM,SAAS,UAAU,KAAK,MAAM;AACpC,QAAM,iBAAiB,UAAU,KAAK,MAAM;AAC5C,QAAM,gBAAgB,UAAU,KAAK,YAAY;AACjD,QAAM,eAAe,KAAK,IAAI,GAAG,gBAAgB,cAAc;AAC/D,MAAI,WAAW,QAAW;AACzB,WAAO;AAAA,MACN;AAAA,MACA;AAAA,MACA,UAAU,EAAE,aAAa,GAAG,cAAc,eAAe,eAAe;AAAA,IACzE;AAAA,EACD;AAGA,QAAM,kBACL,cAAc,iBAAiB,IAC5B,IACA,UAAU,sBAAsB,EAAE,KAAK,SAAS,IACjD,UAAU,KAAK,WAAW;AAC7B,QAAM,aAAa,OAAO,sBAAsB;AAChD,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA,UAAU;AAAA,MACT,aAAa,WAAW,KAAK,SAAS,IAAI,kBAAkB;AAAA,MAC5D,cAAc,WAAW,KAAK,UAAU;AAAA,MACxC;AAAA,IACD;AAAA,EACD;AACD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Where a scroll animation starts and ends, as Motion writes it: each offset
3
+ * is "<target edge> <container edge>", the scroll position at which that
4
+ * point of the target meets that point of the scrollport. "start end" is the
5
+ * target's top touching the viewport's bottom.
6
+ */
7
+ export type ScrollEdge = "start" | "center" | "end" | number | `${number}px` | `${number}%`;
8
+ export type ScrollOffsetPoint = `${string} ${string}` | readonly [ScrollEdge, ScrollEdge];
9
+ export type ScrollOffset = readonly [ScrollOffsetPoint, ScrollOffsetPoint];
10
+ /** The target crossing the scrollport, from first pixel in to last pixel out. */
11
+ export declare const ENTER_TO_EXIT: ScrollOffset;
12
+ /** The whole scroll range, when there is no target. */
13
+ export declare const FULL_RANGE: ScrollOffset;
14
+ /** An edge as pixels along a length: named and bare numbers are fractions. */
15
+ export declare function resolveEdge(edge: ScrollEdge, length: number): number;
16
+ export declare function parseOffsetPoint(point: ScrollOffsetPoint): readonly [ScrollEdge, ScrollEdge];
17
+ export interface ScrollGeometry {
18
+ /** The target's start along the axis, in the container's content coordinates. */
19
+ readonly targetStart: number;
20
+ readonly targetLength: number;
21
+ /** The scrollport's visible length. */
22
+ readonly viewportLength: number;
23
+ }
24
+ /** The scroll position at which one offset point is reached. */
25
+ export declare function offsetPosition(point: ScrollOffsetPoint, geometry: ScrollGeometry): number;
26
+ /** Progress 0..1 of `scroll` between the two offsets, clamped. */
27
+ export declare function scrollProgress(scroll: number, offset: ScrollOffset, geometry: ScrollGeometry): number;
28
+ //# sourceMappingURL=scroll-offset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll-offset.d.ts","sourceRoot":"","sources":["../../src/view/scroll-offset.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GACnB,OAAO,GACP,QAAQ,GACR,KAAK,GACL,MAAM,GACN,GAAG,MAAM,IAAI,GACb,GAAG,MAAM,GAAG,CAAC;AAEhB,MAAM,MAAM,iBAAiB,GAC1B,GAAG,MAAM,IAAI,MAAM,EAAE,GACrB,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;AAErC,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;AAE3E,iFAAiF;AACjF,eAAO,MAAM,aAAa,EAAE,YAAyC,CAAC;AACtE,uDAAuD;AACvD,eAAO,MAAM,UAAU,EAAE,YAAyC,CAAC;AAQnE,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAgBpE;AAED,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE,iBAAiB,GACtB,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAMnC;AAQD,MAAM,WAAW,cAAc;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,uCAAuC;IACvC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CAChC;AAED,gEAAgE;AAChE,wBAAgB,cAAc,CAC7B,KAAK,EAAE,iBAAiB,EACxB,QAAQ,EAAE,cAAc,GACtB,MAAM,CAOR;AAED,kEAAkE;AAClE,wBAAgB,cAAc,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,cAAc,GACtB,MAAM,CAOR"}
@@ -0,0 +1,55 @@
1
+ const ENTER_TO_EXIT = ["start end", "end start"];
2
+ const FULL_RANGE = ["start start", "end end"];
3
+ const NAMED_EDGES = {
4
+ start: 0,
5
+ center: 0.5,
6
+ end: 1
7
+ };
8
+ function resolveEdge(edge, length) {
9
+ if (typeof edge === "number") {
10
+ return edge * length;
11
+ }
12
+ const named = NAMED_EDGES[edge];
13
+ if (named !== void 0) {
14
+ return named * length;
15
+ }
16
+ const value = Number.parseFloat(edge);
17
+ if (Number.isNaN(value)) {
18
+ throw new Error(`Unknown scroll edge: ${edge}`);
19
+ }
20
+ if (edge.endsWith("%")) {
21
+ return value / 100 * length;
22
+ }
23
+ return edge.endsWith("px") ? value : value * length;
24
+ }
25
+ function parseOffsetPoint(point) {
26
+ if (typeof point !== "string") {
27
+ return point;
28
+ }
29
+ const [target, container = target] = point.trim().split(/\s+/);
30
+ return [parseEdge(target), parseEdge(container)];
31
+ }
32
+ function parseEdge(text) {
33
+ return text in NAMED_EDGES || /[a-z%]$/.test(text) ? text : Number(text);
34
+ }
35
+ function offsetPosition(point, geometry) {
36
+ const [target, container] = parseOffsetPoint(point);
37
+ return geometry.targetStart + resolveEdge(target, geometry.targetLength) - resolveEdge(container, geometry.viewportLength);
38
+ }
39
+ function scrollProgress(scroll, offset, geometry) {
40
+ const from = offsetPosition(offset[0], geometry);
41
+ const to = offsetPosition(offset[1], geometry);
42
+ if (to === from) {
43
+ return scroll >= to ? 1 : 0;
44
+ }
45
+ return Math.min(1, Math.max(0, (scroll - from) / (to - from)));
46
+ }
47
+ export {
48
+ ENTER_TO_EXIT,
49
+ FULL_RANGE,
50
+ offsetPosition,
51
+ parseOffsetPoint,
52
+ resolveEdge,
53
+ scrollProgress
54
+ };
55
+ //# sourceMappingURL=scroll-offset.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/view/scroll-offset.ts"],
4
+ "sourcesContent": ["/**\n * Where a scroll animation starts and ends, as Motion writes it: each offset\n * is \"<target edge> <container edge>\", the scroll position at which that\n * point of the target meets that point of the scrollport. \"start end\" is the\n * target's top touching the viewport's bottom.\n */\nexport type ScrollEdge =\n\t| \"start\"\n\t| \"center\"\n\t| \"end\"\n\t| number\n\t| `${number}px`\n\t| `${number}%`;\n\nexport type ScrollOffsetPoint =\n\t| `${string} ${string}`\n\t| readonly [ScrollEdge, ScrollEdge];\n\nexport type ScrollOffset = readonly [ScrollOffsetPoint, ScrollOffsetPoint];\n\n/** The target crossing the scrollport, from first pixel in to last pixel out. */\nexport const ENTER_TO_EXIT: ScrollOffset = [\"start end\", \"end start\"];\n/** The whole scroll range, when there is no target. */\nexport const FULL_RANGE: ScrollOffset = [\"start start\", \"end end\"];\n\nconst NAMED_EDGES: Readonly<Record<string, number>> = {\n\tstart: 0,\n\tcenter: 0.5,\n\tend: 1,\n};\n\n/** An edge as pixels along a length: named and bare numbers are fractions. */\nexport function resolveEdge(edge: ScrollEdge, length: number): number {\n\tif (typeof edge === \"number\") {\n\t\treturn edge * length;\n\t}\n\tconst named = NAMED_EDGES[edge];\n\tif (named !== undefined) {\n\t\treturn named * length;\n\t}\n\tconst value = Number.parseFloat(edge);\n\tif (Number.isNaN(value)) {\n\t\tthrow new Error(`Unknown scroll edge: ${edge}`);\n\t}\n\tif (edge.endsWith(\"%\")) {\n\t\treturn (value / 100) * length;\n\t}\n\treturn edge.endsWith(\"px\") ? value : value * length;\n}\n\nexport function parseOffsetPoint(\n\tpoint: ScrollOffsetPoint,\n): readonly [ScrollEdge, ScrollEdge] {\n\tif (typeof point !== \"string\") {\n\t\treturn point;\n\t}\n\tconst [target, container = target] = point.trim().split(/\\s+/);\n\treturn [parseEdge(target), parseEdge(container)];\n}\n\nfunction parseEdge(text: string): ScrollEdge {\n\treturn text in NAMED_EDGES || /[a-z%]$/.test(text)\n\t\t? (text as ScrollEdge)\n\t\t: Number(text);\n}\n\nexport interface ScrollGeometry {\n\t/** The target's start along the axis, in the container's content coordinates. */\n\treadonly targetStart: number;\n\treadonly targetLength: number;\n\t/** The scrollport's visible length. */\n\treadonly viewportLength: number;\n}\n\n/** The scroll position at which one offset point is reached. */\nexport function offsetPosition(\n\tpoint: ScrollOffsetPoint,\n\tgeometry: ScrollGeometry,\n): number {\n\tconst [target, container] = parseOffsetPoint(point);\n\treturn (\n\t\tgeometry.targetStart +\n\t\tresolveEdge(target, geometry.targetLength) -\n\t\tresolveEdge(container, geometry.viewportLength)\n\t);\n}\n\n/** Progress 0..1 of `scroll` between the two offsets, clamped. */\nexport function scrollProgress(\n\tscroll: number,\n\toffset: ScrollOffset,\n\tgeometry: ScrollGeometry,\n): number {\n\tconst from = offsetPosition(offset[0], geometry);\n\tconst to = offsetPosition(offset[1], geometry);\n\tif (to === from) {\n\t\treturn scroll >= to ? 1 : 0;\n\t}\n\treturn Math.min(1, Math.max(0, (scroll - from) / (to - from)));\n}\n"],
5
+ "mappings": "AAqBO,MAAM,gBAA8B,CAAC,aAAa,WAAW;AAE7D,MAAM,aAA2B,CAAC,eAAe,SAAS;AAEjE,MAAM,cAAgD;AAAA,EACrD,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AACN;AAGO,SAAS,YAAY,MAAkB,QAAwB;AACrE,MAAI,OAAO,SAAS,UAAU;AAC7B,WAAO,OAAO;AAAA,EACf;AACA,QAAM,QAAQ,YAAY,IAAI;AAC9B,MAAI,UAAU,QAAW;AACxB,WAAO,QAAQ;AAAA,EAChB;AACA,QAAM,QAAQ,OAAO,WAAW,IAAI;AACpC,MAAI,OAAO,MAAM,KAAK,GAAG;AACxB,UAAM,IAAI,MAAM,wBAAwB,IAAI,EAAE;AAAA,EAC/C;AACA,MAAI,KAAK,SAAS,GAAG,GAAG;AACvB,WAAQ,QAAQ,MAAO;AAAA,EACxB;AACA,SAAO,KAAK,SAAS,IAAI,IAAI,QAAQ,QAAQ;AAC9C;AAEO,SAAS,iBACf,OACoC;AACpC,MAAI,OAAO,UAAU,UAAU;AAC9B,WAAO;AAAA,EACR;AACA,QAAM,CAAC,QAAQ,YAAY,MAAM,IAAI,MAAM,KAAK,EAAE,MAAM,KAAK;AAC7D,SAAO,CAAC,UAAU,MAAM,GAAG,UAAU,SAAS,CAAC;AAChD;AAEA,SAAS,UAAU,MAA0B;AAC5C,SAAO,QAAQ,eAAe,UAAU,KAAK,IAAI,IAC7C,OACD,OAAO,IAAI;AACf;AAWO,SAAS,eACf,OACA,UACS;AACT,QAAM,CAAC,QAAQ,SAAS,IAAI,iBAAiB,KAAK;AAClD,SACC,SAAS,cACT,YAAY,QAAQ,SAAS,YAAY,IACzC,YAAY,WAAW,SAAS,cAAc;AAEhD;AAGO,SAAS,eACf,QACA,QACA,UACS;AACT,QAAM,OAAO,eAAe,OAAO,CAAC,GAAG,QAAQ;AAC/C,QAAM,KAAK,eAAe,OAAO,CAAC,GAAG,QAAQ;AAC7C,MAAI,OAAO,MAAM;AAChB,WAAO,UAAU,KAAK,IAAI;AAAA,EAC3B;AACA,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,SAAS,SAAS,KAAK,KAAK,CAAC;AAC9D;",
6
+ "names": []
7
+ }
@@ -0,0 +1,34 @@
1
+ import { type ScrollAxis } from "./scroll-measure.js";
2
+ import { type ScrollOffset } from "./scroll-offset.js";
3
+ /** What `animate()` returns, seen from the scroll side. */
4
+ export interface ScrollDrivable {
5
+ time: number;
6
+ readonly duration: number;
7
+ /** Hands the native timeline to WAAPI animations; false when it can't (JS-driven). */
8
+ attachTimeline?(timeline: AnimationTimeline): boolean;
9
+ /** Scroll alone moves it once driven: its own clock must stop. */
10
+ pause?(): void;
11
+ }
12
+ export interface ScrollInfo {
13
+ readonly progress: number;
14
+ /** Pixels scrolled along the axis. */
15
+ readonly scroll: number;
16
+ /** The furthest the container can scroll along the axis. */
17
+ readonly scrollLength: number;
18
+ readonly axis: ScrollAxis;
19
+ }
20
+ export interface ScrollOptions {
21
+ /** The scrolling element; the page by default. */
22
+ readonly container?: Element;
23
+ /** Track this element crossing the container instead of the whole scroll range. */
24
+ readonly target?: Element;
25
+ readonly axis?: ScrollAxis;
26
+ readonly offset?: ScrollOffset;
27
+ }
28
+ export type OnScroll = (progress: number, info: ScrollInfo) => void;
29
+ /**
30
+ * Drives a callback, or an animation's time, from scroll progress. Returns a
31
+ * function that stops listening.
32
+ */
33
+ export declare function scroll(onScroll: OnScroll | ScrollDrivable, options?: ScrollOptions): () => void;
34
+ //# sourceMappingURL=scroll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll.d.ts","sourceRoot":"","sources":["../../src/view/scroll.ts"],"names":[],"mappings":"AAEA,OAAO,EAGN,KAAK,UAAU,EACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAGN,KAAK,YAAY,EAEjB,MAAM,yBAAyB,CAAC;AAEjC,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,sFAAsF;IACtF,cAAc,CAAC,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC;IACtD,kEAAkE;IAClE,KAAK,CAAC,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,sCAAsC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;CAC/B;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;AA8BpE;;;GAGG;AACH,wBAAgB,MAAM,CACrB,QAAQ,EAAE,QAAQ,GAAG,cAAc,EACnC,OAAO,GAAE,aAAkB,GACzB,MAAM,IAAI,CA+DZ"}
@@ -0,0 +1,83 @@
1
+ import { cancelFrame, frame } from "../frame/frame.js";
2
+ import { resize } from "./resize.js";
3
+ import {
4
+ documentScroller,
5
+ measureScroll
6
+ } from "./scroll-measure.js";
7
+ import {
8
+ ENTER_TO_EXIT,
9
+ FULL_RANGE,
10
+ scrollProgress
11
+ } from "./scroll-offset.js";
12
+ function sameOffset(a, b) {
13
+ return String(a[0]) === String(b[0]) && String(a[1]) === String(b[1]);
14
+ }
15
+ function nativeTimeline(container, axis, target, offset) {
16
+ const scope = globalThis;
17
+ const Timeline = target ? scope.ViewTimeline : scope.ScrollTimeline;
18
+ const expected = target ? ENTER_TO_EXIT : FULL_RANGE;
19
+ if (Timeline === void 0 || !sameOffset(offset, expected)) {
20
+ return void 0;
21
+ }
22
+ return target ? new Timeline({ subject: target, axis }) : new Timeline({ source: container, axis });
23
+ }
24
+ function scroll(onScroll, options = {}) {
25
+ const container = options.container ?? documentScroller();
26
+ const axis = options.axis ?? "y";
27
+ const target = options.target;
28
+ const offset = options.offset ?? (target ? ENTER_TO_EXIT : FULL_RANGE);
29
+ if (typeof onScroll !== "function" && onScroll.attachTimeline) {
30
+ const timeline = nativeTimeline(container, axis, target, offset);
31
+ if (timeline && onScroll.attachTimeline(timeline)) {
32
+ return () => {
33
+ };
34
+ }
35
+ }
36
+ if (typeof onScroll !== "function") onScroll.pause?.();
37
+ const apply = typeof onScroll === "function" ? onScroll : (progress) => {
38
+ onScroll.time = progress * onScroll.duration;
39
+ };
40
+ let info;
41
+ let scheduled = false;
42
+ function update() {
43
+ if (info) {
44
+ apply(info.progress, info);
45
+ }
46
+ }
47
+ function measure() {
48
+ scheduled = false;
49
+ const measured = measureScroll(container, axis, target);
50
+ info = {
51
+ progress: scrollProgress(measured.scroll, offset, measured.geometry),
52
+ scroll: measured.scroll,
53
+ scrollLength: measured.scrollLength,
54
+ axis
55
+ };
56
+ frame.update(update);
57
+ }
58
+ function schedule() {
59
+ if (!scheduled) {
60
+ scheduled = true;
61
+ frame.read(measure);
62
+ }
63
+ }
64
+ const eventSource = container === documentScroller() ? window : container;
65
+ eventSource.addEventListener("scroll", schedule, { passive: true });
66
+ const stopResize = [
67
+ resize(schedule),
68
+ resize(target ? [container, target] : [container], schedule)
69
+ ];
70
+ schedule();
71
+ return () => {
72
+ eventSource.removeEventListener("scroll", schedule);
73
+ for (const stop of stopResize) {
74
+ stop();
75
+ }
76
+ cancelFrame(measure);
77
+ cancelFrame(update);
78
+ };
79
+ }
80
+ export {
81
+ scroll
82
+ };
83
+ //# sourceMappingURL=scroll.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/view/scroll.ts"],
4
+ "sourcesContent": ["import { cancelFrame, frame } from \"#/frame/frame.ts\";\nimport { resize } from \"#/view/resize.ts\";\nimport {\n\tdocumentScroller,\n\tmeasureScroll,\n\ttype ScrollAxis,\n} from \"#/view/scroll-measure.ts\";\nimport {\n\tENTER_TO_EXIT,\n\tFULL_RANGE,\n\ttype ScrollOffset,\n\tscrollProgress,\n} from \"#/view/scroll-offset.ts\";\n\n/** What `animate()` returns, seen from the scroll side. */\nexport interface ScrollDrivable {\n\ttime: number;\n\treadonly duration: number;\n\t/** Hands the native timeline to WAAPI animations; false when it can't (JS-driven). */\n\tattachTimeline?(timeline: AnimationTimeline): boolean;\n\t/** Scroll alone moves it once driven: its own clock must stop. */\n\tpause?(): void;\n}\n\nexport interface ScrollInfo {\n\treadonly progress: number;\n\t/** Pixels scrolled along the axis. */\n\treadonly scroll: number;\n\t/** The furthest the container can scroll along the axis. */\n\treadonly scrollLength: number;\n\treadonly axis: ScrollAxis;\n}\n\nexport interface ScrollOptions {\n\t/** The scrolling element; the page by default. */\n\treadonly container?: Element;\n\t/** Track this element crossing the container instead of the whole scroll range. */\n\treadonly target?: Element;\n\treadonly axis?: ScrollAxis;\n\treadonly offset?: ScrollOffset;\n}\n\nexport type OnScroll = (progress: number, info: ScrollInfo) => void;\n\ntype TimelineConstructor = new (options: object) => AnimationTimeline;\n\nfunction sameOffset(a: ScrollOffset, b: ScrollOffset): boolean {\n\treturn String(a[0]) === String(b[0]) && String(a[1]) === String(b[1]);\n}\n\n/**\n * The browser's own timeline, which runs the animation off the main thread.\n * Only for the offsets a bare timeline means by itself: the full range for\n * a ScrollTimeline, enter-to-exit (\"cover\") for a ViewTimeline.\n */\nfunction nativeTimeline(\n\tcontainer: Element,\n\taxis: ScrollAxis,\n\ttarget: Element | undefined,\n\toffset: ScrollOffset,\n): AnimationTimeline | undefined {\n\tconst scope = globalThis as unknown as Record<string, TimelineConstructor>;\n\tconst Timeline = target ? scope.ViewTimeline : scope.ScrollTimeline;\n\tconst expected = target ? ENTER_TO_EXIT : FULL_RANGE;\n\tif (Timeline === undefined || !sameOffset(offset, expected)) {\n\t\treturn undefined;\n\t}\n\treturn target\n\t\t? new Timeline({ subject: target, axis })\n\t\t: new Timeline({ source: container, axis });\n}\n\n/**\n * Drives a callback, or an animation's time, from scroll progress. Returns a\n * function that stops listening.\n */\nexport function scroll(\n\tonScroll: OnScroll | ScrollDrivable,\n\toptions: ScrollOptions = {},\n): () => void {\n\tconst container = options.container ?? documentScroller();\n\tconst axis = options.axis ?? \"y\";\n\tconst target = options.target;\n\tconst offset = options.offset ?? (target ? ENTER_TO_EXIT : FULL_RANGE);\n\n\tif (typeof onScroll !== \"function\" && onScroll.attachTimeline) {\n\t\tconst timeline = nativeTimeline(container, axis, target, offset);\n\t\tif (timeline && onScroll.attachTimeline(timeline)) {\n\t\t\treturn () => {};\n\t\t}\n\t}\n\n\tif (typeof onScroll !== \"function\") onScroll.pause?.();\n\tconst apply: OnScroll =\n\t\ttypeof onScroll === \"function\"\n\t\t\t? onScroll\n\t\t\t: (progress) => {\n\t\t\t\t\tonScroll.time = progress * onScroll.duration;\n\t\t\t\t};\n\tlet info: ScrollInfo | undefined;\n\tlet scheduled = false;\n\n\tfunction update() {\n\t\tif (info) {\n\t\t\tapply(info.progress, info);\n\t\t}\n\t}\n\tfunction measure() {\n\t\tscheduled = false;\n\t\tconst measured = measureScroll(container, axis, target);\n\t\tinfo = {\n\t\t\tprogress: scrollProgress(measured.scroll, offset, measured.geometry),\n\t\t\tscroll: measured.scroll,\n\t\t\tscrollLength: measured.scrollLength,\n\t\t\taxis,\n\t\t};\n\t\tframe.update(update);\n\t}\n\tfunction schedule() {\n\t\tif (!scheduled) {\n\t\t\tscheduled = true;\n\t\t\tframe.read(measure);\n\t\t}\n\t}\n\n\t// The page scroller fires its scroll events on window, not on itself.\n\tconst eventSource: EventTarget =\n\t\tcontainer === documentScroller() ? window : container;\n\teventSource.addEventListener(\"scroll\", schedule, { passive: true });\n\tconst stopResize = [\n\t\tresize(schedule),\n\t\tresize(target ? [container, target] : [container], schedule),\n\t];\n\tschedule();\n\treturn () => {\n\t\teventSource.removeEventListener(\"scroll\", schedule);\n\t\tfor (const stop of stopResize) {\n\t\t\tstop();\n\t\t}\n\t\tcancelFrame(measure);\n\t\tcancelFrame(update);\n\t};\n}\n"],
5
+ "mappings": "AAAA,SAAS,aAAa,aAAa;AACnC,SAAS,cAAc;AACvB;AAAA,EACC;AAAA,EACA;AAAA,OAEM;AACP;AAAA,EACC;AAAA,EACA;AAAA,EAEA;AAAA,OACM;AAkCP,SAAS,WAAW,GAAiB,GAA0B;AAC9D,SAAO,OAAO,EAAE,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC;AACrE;AAOA,SAAS,eACR,WACA,MACA,QACA,QACgC;AAChC,QAAM,QAAQ;AACd,QAAM,WAAW,SAAS,MAAM,eAAe,MAAM;AACrD,QAAM,WAAW,SAAS,gBAAgB;AAC1C,MAAI,aAAa,UAAa,CAAC,WAAW,QAAQ,QAAQ,GAAG;AAC5D,WAAO;AAAA,EACR;AACA,SAAO,SACJ,IAAI,SAAS,EAAE,SAAS,QAAQ,KAAK,CAAC,IACtC,IAAI,SAAS,EAAE,QAAQ,WAAW,KAAK,CAAC;AAC5C;AAMO,SAAS,OACf,UACA,UAAyB,CAAC,GACb;AACb,QAAM,YAAY,QAAQ,aAAa,iBAAiB;AACxD,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,SAAS,QAAQ;AACvB,QAAM,SAAS,QAAQ,WAAW,SAAS,gBAAgB;AAE3D,MAAI,OAAO,aAAa,cAAc,SAAS,gBAAgB;AAC9D,UAAM,WAAW,eAAe,WAAW,MAAM,QAAQ,MAAM;AAC/D,QAAI,YAAY,SAAS,eAAe,QAAQ,GAAG;AAClD,aAAO,MAAM;AAAA,MAAC;AAAA,IACf;AAAA,EACD;AAEA,MAAI,OAAO,aAAa,WAAY,UAAS,QAAQ;AACrD,QAAM,QACL,OAAO,aAAa,aACjB,WACA,CAAC,aAAa;AACd,aAAS,OAAO,WAAW,SAAS;AAAA,EACrC;AACH,MAAI;AACJ,MAAI,YAAY;AAEhB,WAAS,SAAS;AACjB,QAAI,MAAM;AACT,YAAM,KAAK,UAAU,IAAI;AAAA,IAC1B;AAAA,EACD;AACA,WAAS,UAAU;AAClB,gBAAY;AACZ,UAAM,WAAW,cAAc,WAAW,MAAM,MAAM;AACtD,WAAO;AAAA,MACN,UAAU,eAAe,SAAS,QAAQ,QAAQ,SAAS,QAAQ;AAAA,MACnE,QAAQ,SAAS;AAAA,MACjB,cAAc,SAAS;AAAA,MACvB;AAAA,IACD;AACA,UAAM,OAAO,MAAM;AAAA,EACpB;AACA,WAAS,WAAW;AACnB,QAAI,CAAC,WAAW;AACf,kBAAY;AACZ,YAAM,KAAK,OAAO;AAAA,IACnB;AAAA,EACD;AAGA,QAAM,cACL,cAAc,iBAAiB,IAAI,SAAS;AAC7C,cAAY,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;AAClE,QAAM,aAAa;AAAA,IAClB,OAAO,QAAQ;AAAA,IACf,OAAO,SAAS,CAAC,WAAW,MAAM,IAAI,CAAC,SAAS,GAAG,QAAQ;AAAA,EAC5D;AACA,WAAS;AACT,SAAO,MAAM;AACZ,gBAAY,oBAAoB,UAAU,QAAQ;AAClD,eAAW,QAAQ,YAAY;AAC9B,WAAK;AAAA,IACN;AACA,gBAAY,OAAO;AACnB,gBAAY,MAAM;AAAA,EACnB;AACD;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,58 @@
1
1
  {
2
- "name": "@voila.dev/motion",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@voila.dev/motion",
3
+ "version": "5.0.113",
4
+ "type": "module",
5
+ "description": "Springs played by the browser, values on one frame loop, exits React waits for.",
6
+ "license": "MIT",
7
+ "homepage": "https://ui.voila.dev",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/voila-voila-dev/ui.git",
11
+ "directory": "packages/motion"
12
+ },
13
+ "keywords": [
14
+ "animation",
15
+ "spring",
16
+ "web-animations",
17
+ "motion",
18
+ "react"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "files": [
24
+ "src",
25
+ "dist",
26
+ "!src/**/*.test.ts",
27
+ "!src/**/*.test.tsx"
28
+ ],
29
+ "exports": {
30
+ ".": "./dist/index.js",
31
+ "./dom": "./dist/dom.js",
32
+ "./react": "./dist/react/index.js"
33
+ },
34
+ "imports": {
35
+ "#/*": "./src/*"
36
+ },
37
+ "scripts": {
38
+ "check": "biome check",
39
+ "check-types": "tsc --noEmit",
40
+ "check-bundle": "bun scripts/check-bundle.mjs",
41
+ "format": "biome format",
42
+ "lint": "biome lint",
43
+ "test": "vitest run && bun scripts/check-bundle.mjs"
44
+ },
45
+ "peerDependencies": {
46
+ "react": "^19.0.0",
47
+ "react-dom": "^19.0.0"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "react": {
51
+ "optional": true
52
+ },
53
+ "react-dom": {
54
+ "optional": true
55
+ }
56
+ },
57
+ "sideEffects": false
58
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * "user" follows `prefers-reduced-motion`; "always" and "never" override it
3
+ * for the whole page, for an app with its own setting.
4
+ */
5
+ export type ReducedMotionPolicy = "user" | "always" | "never";
6
+
7
+ const QUERY = "(prefers-reduced-motion: reduce)";
8
+
9
+ let policy: ReducedMotionPolicy = "user";
10
+ const listeners = new Set<(reduced: boolean) => void>();
11
+ let unwatch: (() => void) | undefined;
12
+
13
+ /** Read on every call, never at import: the server has no `matchMedia`. */
14
+ function mediaQuery(): MediaQueryList | undefined {
15
+ return typeof window === "undefined" ||
16
+ typeof window.matchMedia !== "function"
17
+ ? undefined
18
+ : window.matchMedia(QUERY);
19
+ }
20
+
21
+ export function prefersReducedMotion(): boolean {
22
+ return mediaQuery()?.matches ?? false;
23
+ }
24
+
25
+ export function reducedMotionPolicy(): ReducedMotionPolicy {
26
+ return policy;
27
+ }
28
+
29
+ const DECIDE: Record<ReducedMotionPolicy, () => boolean> = {
30
+ user: prefersReducedMotion,
31
+ always: () => true,
32
+ never: () => false,
33
+ };
34
+
35
+ /** `true`/`false` force it for one animation; a policy overrides the global one. */
36
+ export function shouldReduceMotion(
37
+ override?: ReducedMotionPolicy | boolean,
38
+ ): boolean {
39
+ if (typeof override === "boolean") return override;
40
+ return DECIDE[override ?? policy]();
41
+ }
42
+
43
+ function notify(): void {
44
+ const reduced = shouldReduceMotion();
45
+ for (const listener of listeners) listener(reduced);
46
+ }
47
+
48
+ /** Under "always" or "never" the user's setting changes nothing. */
49
+ function onMediaChange(): void {
50
+ if (policy === "user") notify();
51
+ }
52
+
53
+ export function setReducedMotion(next: ReducedMotionPolicy): void {
54
+ const before = shouldReduceMotion();
55
+ policy = next;
56
+ if (shouldReduceMotion() !== before) notify();
57
+ }
58
+
59
+ /** Fires with the effective answer when the user's setting or the policy changes it. */
60
+ export function onReducedMotionChange(
61
+ listener: (reduced: boolean) => void,
62
+ ): () => void {
63
+ listeners.add(listener);
64
+ if (!unwatch) {
65
+ const query = mediaQuery();
66
+ query?.addEventListener("change", onMediaChange);
67
+ unwatch = () => query?.removeEventListener("change", onMediaChange);
68
+ }
69
+ return () => {
70
+ listeners.delete(listener);
71
+ if (listeners.size > 0) return;
72
+ unwatch?.();
73
+ unwatch = undefined;
74
+ };
75
+ }
@@ -0,0 +1,155 @@
1
+ import { animateElements } from "#/animate/dom.ts";
2
+ import { jsAnimation } from "#/animate/js-animation.ts";
3
+ import type {
4
+ AnimationControls,
5
+ AnimationOptions,
6
+ DOMKeyframes,
7
+ ElementTarget,
8
+ Keyframes,
9
+ ValueAnimationOptions,
10
+ } from "#/animate/types.ts";
11
+ import { animateMotionValue, animateObject } from "#/animate/value.ts";
12
+ import { animateSequence, type Segment } from "#/sequence/sequence.ts";
13
+ import {
14
+ isMotionValue,
15
+ type MotionValue,
16
+ motionValue,
17
+ } from "#/value/motion-value.ts";
18
+
19
+ export interface Animate {
20
+ /** Elements: a selector, an element or a list. Played by the browser where it can. */
21
+ (
22
+ target: ElementTarget,
23
+ keyframes: DOMKeyframes,
24
+ options?: AnimationOptions,
25
+ ): AnimationControls;
26
+ /** A motion value, from wherever it is now. */
27
+ <T>(
28
+ value: MotionValue<T>,
29
+ target: Keyframes<T>,
30
+ options?: ValueAnimationOptions<T>,
31
+ ): AnimationControls;
32
+ /** A number, a colour, a path… from one value to another, read in `onUpdate`. */
33
+ <T extends number | string>(
34
+ from: T,
35
+ to: Keyframes<T>,
36
+ options: ValueAnimationOptions<T>,
37
+ ): AnimationControls;
38
+ /** The keys of a plain object, written back into it. */
39
+ <T extends object>(
40
+ object: T,
41
+ keyframes: { readonly [K in keyof T]?: Keyframes<T[K]> },
42
+ options?: ValueAnimationOptions<T>,
43
+ ): AnimationControls;
44
+ /** A timeline of segments. */
45
+ (sequence: readonly Segment[], options?: AnimationOptions): AnimationControls;
46
+ }
47
+
48
+ function isElementList(target: unknown): target is ArrayLike<Element> {
49
+ return (
50
+ typeof target === "object" &&
51
+ target !== null &&
52
+ "length" in target &&
53
+ (Array.from(target as ArrayLike<unknown>).every(
54
+ (item) => typeof Element !== "undefined" && item instanceof Element,
55
+ ) as boolean)
56
+ );
57
+ }
58
+
59
+ /** A label object is a plain `{ name }`: a form element also has a `name`. */
60
+ function isSegment(item: unknown): boolean {
61
+ return (
62
+ typeof item === "string" ||
63
+ Array.isArray(item) ||
64
+ (typeof item === "object" &&
65
+ item !== null &&
66
+ Object.getPrototypeOf(item) === Object.prototype &&
67
+ "name" in item)
68
+ );
69
+ }
70
+
71
+ function isSequence(target: unknown, second: unknown): target is Segment[] {
72
+ return (
73
+ Array.isArray(target) &&
74
+ (second === undefined || !Array.isArray(second)) &&
75
+ target.length > 0 &&
76
+ target.every(isSegment)
77
+ );
78
+ }
79
+
80
+ /** `animate`, with selectors looked up inside `scope` (the whole document by default). */
81
+ export function createAnimate(scope?: () => ParentNode | null): Animate {
82
+ function elementsOf(target: unknown): readonly Element[] {
83
+ if (typeof target === "string") {
84
+ const root =
85
+ scope?.() ?? (typeof document === "undefined" ? null : document);
86
+ return root ? Array.from(root.querySelectorAll(target)) : [];
87
+ }
88
+ if (typeof Element !== "undefined" && target instanceof Element)
89
+ return [target];
90
+ return Array.from(target as ArrayLike<Element>);
91
+ }
92
+
93
+ function animateOne(
94
+ target: unknown,
95
+ keyframes: unknown,
96
+ options: AnimationOptions = {},
97
+ ): AnimationControls {
98
+ if (isMotionValue(target)) {
99
+ return animateMotionValue(
100
+ target,
101
+ keyframes as Keyframes<unknown>,
102
+ options,
103
+ );
104
+ }
105
+ const domKeyframes =
106
+ typeof keyframes === "object" &&
107
+ keyframes !== null &&
108
+ !Array.isArray(keyframes);
109
+ if (
110
+ domKeyframes &&
111
+ (typeof target === "string" ||
112
+ (typeof Element !== "undefined" && target instanceof Element) ||
113
+ isElementList(target))
114
+ ) {
115
+ return animateElements(
116
+ elementsOf(target),
117
+ keyframes as DOMKeyframes,
118
+ options,
119
+ jsAnimation,
120
+ );
121
+ }
122
+ if (typeof target === "number" || typeof target === "string") {
123
+ return animateMotionValue(
124
+ motionValue(target),
125
+ keyframes as Keyframes<unknown>,
126
+ options,
127
+ );
128
+ }
129
+ return animateObject(
130
+ target as object,
131
+ keyframes as Record<string, Keyframes<unknown>>,
132
+ options,
133
+ );
134
+ }
135
+
136
+ function targetsOf(target: unknown): readonly object[] {
137
+ const isDom =
138
+ typeof target === "string" ||
139
+ (typeof Element !== "undefined" && target instanceof Element) ||
140
+ isElementList(target);
141
+ return isDom ? elementsOf(target) : [target as object];
142
+ }
143
+
144
+ return ((target: unknown, second?: unknown, options?: AnimationOptions) =>
145
+ isSequence(target, second)
146
+ ? animateSequence(
147
+ target,
148
+ (second as AnimationOptions) ?? {},
149
+ animateOne,
150
+ targetsOf,
151
+ )
152
+ : animateOne(target, second, options)) as Animate;
153
+ }
154
+
155
+ export const animate: Animate = createAnimate();