@thednp/tween 0.0.3 → 0.0.4

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/README.md CHANGED
@@ -3,48 +3,12 @@
3
3
  [![Coverage Status](https://coveralls.io/repos/github/thednp/tween/badge.svg)](https://coveralls.io/github/thednp/tween)
4
4
  [![NPM Version](https://img.shields.io/npm/v/@thednp/tween.svg)](https://www.npmjs.com/package/@thednp/tween)
5
5
  [![CI](https://github.com/thednp/tween/actions/workflows/ci.yml/badge.svg)](https://github.com/thednp/tween/actions/workflows/ci.yml)
6
+ [![CDN](https://img.shields.io/jsdelivr/npm/hw/@thednp/tween)](https://www.jsdelivr.com/package/npm/@thednp/tween)
6
7
 
7
8
 
8
- A Typescript sourced tweening engine forked from the excellent [@tweenjs/tweenjs](https://github.com/tweenjs/tween.js). This package has some strongly opinionated implementations for the best performance and DX.
9
+ A TypeScript-first tweening engine forked from the excellent [@tweenjs/tweenjs](https://github.com/tweenjs/tween.js).
9
10
 
10
- Major popular frameworks supported:
11
-
12
- [<img width="32" height="32" src="wiki/assets/react.svg" alt="React" />](wiki/React.md)
13
- [<img width="32" height="32" src="wiki/assets/solid.svg" alt="SolidJS" />](wiki/Solid.md)
14
- [<img width="32" height="32" src="wiki/assets/preact.svg" alt="Preact" />](wiki/Preact.md)
15
- [<img width="32" height="32" src="wiki/assets/svelte.svg" alt="Svelte" />](wiki/Svelte.md)
16
- [<img width="32" height="32" src="wiki/assets/vue.svg" alt="Vue" />](wiki/Vue.md)
17
-
18
-
19
- ## Why @thednp/tween?
20
-
21
- It seems **we got it all wrong regarding tweening** since the beginning. We've been working with unpredictable patterns and high risk for errors. We've been doing too much value checking during hot update loop or sometimes got too close to the DOM, to the point that makes it hard to integrate in modern tech. What if... we can change all that?
22
-
23
- **Why you would like it?**
24
-
25
- * It's really easy to use thanks to the built in support for popular UI frameworks via SSR compatible hooks/primitives/composables (hooks only provide initial values required for server rendering runtime and initialization is skipped entirely, hooks only work with hydration of server rendered HTML or single page apps - SPA).
26
- * It's the solution for a predictable outcome: validate values before and never at the update runtime, explicit specification instead of a guessing game, no object lookup or any kind of overhead.
27
- * While you can do quite complex animations with CSS3 alone, it's really poor DX to go back and forth and write complex animations to which you have little to no control.
28
- * The package comes with a feature rich validation system, all to make sure your animations run smooth and never break your app. Values aren't valid? Animation won't start. Missed a configuration step? You will be provided with feedback.
29
- * SVG math can be troublesome because of its own coordinates system, SVG transforms are also a thing, but with a little learning and our tweening engine you can do anything you really want.
30
-
31
-
32
- ## Features
33
- * The package includes `Tween` class for creating most simple tween objects, with most essential controls, callbacks and methods for sequencing.
34
- * For more complex scheduling and easier sequencing this package has a simple to use `Timeline` class with similar controls, callbacks, but most importantly it allows setting **per-property** duration, start time / delay and easing.
35
- * There are also a series of custom hooks/primitives for popular frameworks like React/SolidJS/Svelte/Preact/Vue (more to come), with proper documentation and detailed user guides.
36
- * Yoyo and generally reverse playback works naturally with inverted easing function (without a `reverseEasing` option) and without re-assigning a `valuesStart` object on repeat iteration end like the original library.
37
- * By default, only `number` values are supported, which is fine for most use cases, but both `Tween` and `Timeline` provide a way to extend beyond the original prototype. You can use the built-in [extensions](wiki/Extend.md) or creare your own to provide per property validation and interpolation with your design specification. The only limit is that objects are limited to a one level nesting and must be plain objects.
38
- * All values are **always** validated on initialization from a given `initialValues` object, which, once validated, it becomes the source of truth to the type of values coming later from `to()` / `from()`.
39
- * Automatic `requestAnimationFrame` loop (you won't need to handle it yourself), it starts when you call `tween.start()` or `timeline.play()` and stops when all tweens are complete.
40
- * The package has some micro-optimizations for maximum performance:
41
- - All loops are executed with `while`;
42
- - No value validation of any kind during the update loop;
43
- - Supported frameworks (via hooks/primitives/composables) use a `miniStore` designed to store tween values and trigger updates/effects in your UI efficiently, with zero GC pressure, no object lookup or re-assignment, just pure linear interpolation;
44
- - The hot update runtime consists of tuples, to optimize GC presure and eliminate object lookup.
45
-
46
-
47
- ### Take a minute to check a quick demo
11
+ Popular UI frameworks supported:
48
12
 
49
13
  [<img width="32" height="32" src="wiki/assets/react.svg" alt="React" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/react)
50
14
  [<img width="32" height="32" src="wiki/assets/solid.svg" alt="SolidJS" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/solid)
@@ -55,18 +19,45 @@ It seems **we got it all wrong regarding tweening** since the beginning. We've b
55
19
  Your favorite framework isn't listed? [Let us know](https://github.com/thednp/tween/issues/new)!
56
20
 
57
21
 
22
+ ## Why @thednp/tween?
23
+
24
+ **State-first architecture** with validation before runtime, not during. Explicit configuration, zero guesswork, minimal overhead.
25
+
26
+ ### Key Benefits
27
+
28
+ * **SSR-compatible hooks** for React, SolidJS, Svelte, Preact, and Vue — provide initial values for server rendering, skip initialization entirely on the server
29
+ * **Predictable outcomes** through upfront value validation — invalid values prevent animation start with clear feedback
30
+ * **Production-ready validation system** catches configuration errors before they break your app
31
+ * **Natural reverse playback** via inverted easing (no `reverseEasing` option or `valuesStart` reassignment)
32
+ * **Extensible interpolation** with built-in [extensions](wiki/Extend.md) or custom per-property validators and interpolators
33
+
34
+ ### Performance Optimizations
35
+
36
+ * **Single shared `requestAnimationFrame` loop** for all tweens/timelines with automatic start/stop
37
+ * **Zero GC pressure** via specialized `miniStore` for framework integrations
38
+ * **Tuple-based hot update runtime** eliminates object lookup
39
+ * **No validation during updates**—all checks happen at initialization
40
+ * **`while` loops throughout** for maximum speed
41
+
58
42
  ### Documentation
43
+
44
+ #### Core Features
59
45
  * [Tween Guide](wiki/Tween.md) - the official `Tween` documentation
60
46
  * [Timeline Guide](wiki/Timeline.md) - the official `Timeline` documentation
61
47
  * [Easing Guide](wiki/Easing.md) - the easing functions documentation
62
48
  * [Extend Guide](wiki/Extend.md) - the extensions documentation
63
- * [React Guide](wiki/React.md) - the React documentation (custom hooks)
64
- * [SolidJS Guide](wiki/Solid.md) - the SolidJS documentation (custom primitives, tips)
65
- * [Vue Guide](wiki/Vue.md) - the Vue documentation (composables, tips)
66
- * [Preact Guide](wiki/Preact.md) - the Preact documentation (custom hooks, tips)
67
- * [Svelte Guide](wiki/Svelte.md) - the Svelte documentation (runes)
49
+ * [Troubleshooting](wiki/Troubleshooting.md) - a quick check on issues and how to solve them.
50
+ * [Ministore](wiki/Ministore.md) - an inside look at `miniStore`.
51
+
52
+ #### UI Frameworks
53
+ [<img width="32" height="32" src="wiki/assets/react.svg" alt="React" />](wiki/React.md)
54
+ [<img width="32" height="32" src="wiki/assets/solid.svg" alt="SolidJS" />](wiki/Solid.md)
55
+ [<img width="32" height="32" src="wiki/assets/preact.svg" alt="Preact" />](wiki/Preact.md)
56
+ [<img width="32" height="32" src="wiki/assets/svelte.svg" alt="Svelte" />](wiki/Svelte.md)
57
+ [<img width="32" height="32" src="wiki/assets/vue.svg" alt="Vue" />](wiki/Vue.md)
58
+
59
+ #### Other Sources
68
60
  * [The original Tween.js User Guide](https://github.com/tweenjs/tween.js/blob/main/docs/user_guide.md) can also provide valuable tips.
69
- * [Troubleshooting](wiki/Troubleshooting) - a quick check on issues and how to solve them.
70
61
 
71
62
 
72
63
  ### Installation
@@ -86,11 +77,21 @@ deno add @thednp/tween
86
77
  bun add @thednp/tween
87
78
  ```
88
79
 
80
+ ### Load From CDN
81
+
82
+ ```html
83
+ <script src="https://cdn.jsdelivr.net/npm/@thednp/tween/dist/tween.min.js"></script>
84
+ <script>
85
+ const { Tween, Easing } = TWEEN;
86
+ const tween = new Tween({ x: 0 });
87
+ </script>
88
+ ```
89
89
 
90
90
  ### Usage
91
91
 
92
92
  To use `Tween` and `Timeline` with UI frameworks please check the dedicated sections: [React](wiki/React.md), [SolidJS](wiki/Solid.md), [Svelte](wiki/Svelte.md), [Preact](wiki/Preact.md) and [Vue](wiki/Vue.md).
93
93
 
94
+
94
95
  #### Using Tween
95
96
  ```ts
96
97
  import { Tween, Easing } from '@thednp/tween';
@@ -165,91 +166,76 @@ button.onclick = myTimeline.play();
165
166
  For an extended guide, check the [Timeline Wiki](wiki/Timeline.md).
166
167
 
167
168
 
168
- ### What is Different from Original?
169
-
170
- #### Looking Forward
171
- On the surface, most changes seem superficial, but our `Tween` version is very different from the current source version. It was developed to create easy to use hooks for major popular UI frameworks and deliver a trully predictable outcome.
172
-
173
- #### New Features
174
- This package comes with `Timeline`, which works like a regular `Tween` under the hood, but it provides additional control methods like `seek()` or `label()` and allows per property easing, duration and start time / delay options.
175
-
176
- Both `Tween` and `Timeline` have a method to add a custom **per property extension** that consists of a function to validate and another to interpolate, which is a unique way to extend beyond the original design and very different from the original library.
177
-
178
- **Great DX**: Along with type safety, `Tween` and `Timeline` will validate your values on initialization or (re)configuration to enforce it. Calling `Tween.to()`, `Tween.from()` or `Timeline.to()` will use the **validation system** to provide feedback on what went wrong and how to fix, in most cases issues with invalid/incompatible values are mapped internally and only cleared once validated/re-validated. This is to make sure to **never crash** your app.
179
-
180
- #### Other Notable Changes
181
- * The `chain` feature is **not** implemented;
182
- * Callback options like `onEveryStart`, `onFirstStart` are **not** implemented;
183
- * The `duration()`, `delay()`, `repeatDelay()` or `seek()` methods accept values in seconds and convert them to milliseconds;
184
- * The update loop which consists of `requestAnimationFrame` is automatic and a queue system is integrated in the `Tween` and `Timeline` methods;
185
- * The original [Tween.js](https://tweenjs.github.io/tween.js/examples/06_array_interpolation.html) array interpolation is **not** supported;
186
- * Deeply nested objects are **not supported**, actively discouraged, objects in general are known to have very bad performance metrics;
187
- * Dynamic end values like the original [Tween.js](https://tweenjs.github.io/tween.js/examples/07_dynamic_to.html) cannot be supported due to the intrinsic changes in the update runtime.
188
-
189
-
190
- ### Technical Notes & Design Choices
191
-
192
- **@thednp/tween** is intentionally designed as a **state-first** tweening engine. Here's why certain choices were made and how the system works under the hood.
193
-
194
- #### Mini-Stores for Supported Frameworks
195
-
196
- Each supported framework make use of a highly specialized `miniStore` to hold tween values and update your UI. This is to ensure great DX and eliminate GC pressure. Even React can work amazing. Check the [Ministore wiki](wiki/Ministore.md) for details.
197
-
198
- #### How the Global Update Loop Works
169
+ ### Core Features
199
170
 
200
- All tween objects and timelines share **one single `requestAnimationFrame` loop** managed by `Runtime.ts`.
171
+ #### Tween
172
+ Simple tween objects with essential controls, callbacks, and sequencing methods.
201
173
 
202
- - When you call `tween.start()` / `timeline.play()`, the instance is added to a global `Queue`
203
- - `Runtime()` runs every frame → calls `.update(time)` on every queued item
204
- - If `.update()` returns `false` (finished/stopped), the item is removed from the Queue
205
- - When `Queue` becomes empty → `cancelAnimationFrame` is called automatically → loop stops completely.
174
+ #### Timeline
175
+ Complex scheduling with per-property duration, delay, and easing. Includes `seek()` and `label()` for precise control.
206
176
 
207
- **Benefits**:
208
- - Only **one** `requestAnimationFrame` subscription for the entire app extremely efficient
209
- - No manual start/stop of animation loop per tween/timeline
210
- - Zero overhead when nothing is animating
177
+ #### Extensions
178
+ Built-in and custom per-property validators and interpolators. Single-level plain objects only.
211
179
 
212
- This shared loop is why you never need to worry about starting/stopping individual `requestAnimationFrame` calls.
180
+ #### Validation System
181
+ All values validated on initialization from `initialValues` (source of truth). Invalid configurations prevent execution with actionable feedback.
213
182
 
183
+ #### Automatic RAF Loop
184
+ Shared `requestAnimationFrame` loop starts on first `start()` / `play()`, stops when queue empties.
214
185
 
215
- #### Async Nature of `requestAnimationFrame`
216
186
 
217
- All updates are **async** by nature:
187
+ #### Key Differences from Original
218
188
 
219
- 1. You call `tween.start()` / `timeline.play()` → instance is queued (added to the main update loop)
220
- 2. Next `requestAnimationFrame` tick → `Runtime()` calls `instance.update(time)` → interpolates values → determines whether to call `cancelAnimationFrame` or continue
221
- 3. DOM/state updates happen **on the next frame(s)** — never synchronous
189
+ **Not Implemented**
190
+ * `chain()` feature
191
+ * `onEveryStart`, `onFirstStart` callbacks
192
+ * The original Tween.js [array interpolation](https://tweenjs.github.io/tween.js/examples/06_array_interpolation.html)
193
+ * Deeply nested objects
194
+ * The original Tween.js [dynamic end values](https://tweenjs.github.io/tween.js/examples/07_dynamic_to.html)
222
195
 
223
- This means:
224
- - Visual changes are always **smooth** and **tied to the display refresh rate**
225
- - Very low risk of partial/inconsistent frames (most calculations happen before paint, depending on the stack size and GC queue)
196
+ **Changes**
197
+ * `duration()`, `delay()`, `repeatDelay()`, `seek()` accept values in **seconds** (converted to milliseconds internally)
198
+ * Automatic RAF queue system (you don't need to define a global `requestAnimationFrame` update loop yourself)
199
+ * Reverse playback via inverted easing (no `reverseEasing` option required)
200
+ * Per-property extensions via `.use('propName', extensionConfig)`
226
201
 
227
- #### Server-Side Rendering (SSR) compatibility
228
202
 
229
- **@thednp/tween** is **SSR-safe** out of the box:
203
+ ### Architecture Notes
230
204
 
231
- - No DOM access anywhere in the core
232
- - `requestAnimationFrame` / `cancelAnimationFrame` are only called in browser (via `Runtime()`)
233
- - `now()` defaults to `performance.now()`, but you can switch to `Date.now()` to create safe fallbacks in Node
234
- - All supported UI frameworks have consistent guards to prevent execution during server rendering, but also provide values required in the rendered HTML.
205
+ #### Global Update Loop
206
+ Single `requestAnimationFrame` loop managed by `Runtime.ts`:
235
207
 
236
- Just make sure to **not call `tween.start()` / `timeline.play()`** during SSR (if you're using custom hooks outside those included in this package).
208
+ * `tween.start()` / `timeline.play()` adds instance to global queue
209
+ * `Runtime()` calls `.update(time)` on all queued items each frame
210
+ * Instances returning false (finished/stopped) are removed
211
+ * Empty queue triggers automatic `cancelAnimationFrame`
237
212
 
213
+ #### Async Nature
214
+ Updates are async by design:
238
215
 
239
- #### Working Around Limitations
216
+ * `start()` / `play()` queues instance
217
+ * Next RAF tick → `Runtime()` → `update(time)` → interpolation
218
+ * DOM/state updates on subsequent frames
219
+ * Visual changes sync with display refresh rate for smooth animations.
240
220
 
241
- - **Chaining** — use callbacks to start other `Tween` / `Timeline` (like the above, use `onComplete` callback to trigger the start of other tween/timeline instances)
242
- - **Custom extensions** register per instance per property with `.use('propName', extensionConfig)` eliminates the guessing game completely, your values are validated and interpolated exactly how you want it.
243
- - The original Tween.js **array interpolation** - we have a way to add custom interpolators, might worth a try integrating them later.
221
+ #### SSR Compatibility
222
+ * No DOM access in core
223
+ * RAF calls browser-only (via `Runtime()`)
224
+ * `now()` defaults to `performance.now()` (can fallback to Date.now() for Node)
225
+ * Framework hooks include SSR guards and provide values for server-rendered HTML
226
+ * **Important**: Don't call `start()` / `play()` during SSR.
244
227
 
228
+ #### Workarounds
229
+ * **Chaining** — use `onComplete` callback to trigger next tween/timeline
230
+ * **Custom interpolation** — register per-property extensions with `.use()`
245
231
 
246
232
  ### Contributing
247
- This is a work in progress. For any issue or unclear guides, please [file an issue](https://github.com/thednp/tween/issues/new) and help make this guide better. Or feel free to submit a PR! Thank you!
233
+ For any issue or unclear guides, please [file an issue](https://github.com/thednp/tween/issues/new) and help make this guide better. Or feel free to submit a PR! Thank you!
248
234
 
249
235
  **How to contribute**:
250
236
  * fork the project
251
237
  * change code/docs & update tests
252
- * submit PR.
238
+ * submit PR
253
239
 
254
240
 
255
241
  ### Credits
@@ -259,4 +245,4 @@ This is a work in progress. For any issue or unclear guides, please [file an iss
259
245
 
260
246
 
261
247
  ### License
262
- **@thednp/tween** is released under [MIT License](LICENSE).
248
+ [MIT License](LICENSE).
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @thednp/tween hooks for Preact v0.0.3 (https://github.com/thednp/tween)
2
+ * @thednp/tween hooks for Preact v0.0.4 (https://github.com/thednp/tween)
3
3
  * Copyright 2026 © thednp
4
4
  * Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
5
5
  */
@@ -48,7 +48,7 @@ declare function useTween<T extends TweenProps>(initialValues: T): readonly [T,
48
48
  * const [state, timeline] = useTimeline({ x: 0, y: 0 })
49
49
  *
50
50
  * useEffect(() => {
51
- * timeline.to({ x: 100, y: 100 }).start()
51
+ * timeline.to({ x: 100, y: 100 }).play()
52
52
  * }, [])
53
53
  *
54
54
  * return (
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @thednp/tween hooks for Preact v0.0.3 (https://github.com/thednp/tween)
2
+ * @thednp/tween hooks for Preact v0.0.4 (https://github.com/thednp/tween)
3
3
  * Copyright 2026 © thednp
4
4
  * Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
5
5
  */
@@ -155,7 +155,7 @@ function useTween(initialValues) {
155
155
  * const [state, timeline] = useTimeline({ x: 0, y: 0 })
156
156
  *
157
157
  * useEffect(() => {
158
- * timeline.to({ x: 100, y: 100 }).start()
158
+ * timeline.to({ x: 100, y: 100 }).play()
159
159
  * }, [])
160
160
  *
161
161
  * return (
@@ -1 +1 @@
1
- {"version":3,"file":"preact.mjs","names":[],"sources":["../../src/preact/miniStore.ts","../../src/preact/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"preact/hooks\";\nimport {\n type ArrayVal,\n isArray,\n isPlainObject,\n objectHasProp,\n type TweenProps,\n} from \"@thednp/tween\";\n\nconst STATE_PROXY = \"_proxy\";\nconst proxyProps = {\n value: 1,\n enumerable: false,\n configurable: false,\n writable: false,\n};\n\ntype Listener<T> = (state: T) => void;\n\nfunction defineArrayProxy<T extends ArrayVal>(\n index: number,\n value: T[number] | ArrayVal,\n target: T | ArrayVal | ArrayVal[],\n sourceLen: number,\n notifyListeners: () => void,\n) {\n const itemIsLast = index === sourceLen - 1;\n\n if (isArray(value)) {\n const subArray: typeof value = [];\n const valueLen = value.length;\n\n value.forEach((itm, idx) => {\n const subItemIsLast = itemIsLast && idx === valueLen - 1;\n\n let currentItem = itm;\n Object.defineProperty(subArray, idx, {\n get: () => currentItem,\n set: (newValue: typeof itm) => {\n currentItem = newValue;\n\n // Only notify on last element to batch updates\n if (subItemIsLast) {\n notifyListeners();\n }\n },\n enumerable: true,\n });\n });\n target[index] = subArray;\n } else {\n let currentValue = value;\n const getter = () => currentValue;\n const setter = (newVal: typeof value) => {\n currentValue = newVal;\n if (itemIsLast) {\n notifyListeners();\n }\n };\n Object.defineProperties(target, {\n [index]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n }\n}\n\nfunction defineStateProxy<T extends Omit<TweenProps, \"_proxy\">>(\n key: number | keyof T,\n value: T[keyof T],\n target: T | ArrayVal,\n notifyListeners: () => void,\n) {\n const valueIsArray = isArray(value);\n let currentValue = value as ArrayVal | ArrayVal[];\n\n const getter = () => currentValue;\n let setter;\n\n if (valueIsArray) {\n // Build array proxy structure\n const arrayProxy: ArrayVal | ArrayVal[] = [];\n const valLength = value.length;\n\n for (let i = 0; i < valLength; i++) {\n defineArrayProxy(\n i,\n (value as ArrayVal)[i],\n arrayProxy as ArrayVal,\n valLength,\n notifyListeners,\n );\n }\n currentValue = arrayProxy;\n } else {\n setter = (newValue: typeof currentValue) => {\n if (currentValue !== newValue) {\n currentValue = newValue;\n notifyListeners();\n }\n };\n }\n\n Object.defineProperties(target, {\n [STATE_PROXY]: proxyProps,\n [key]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n}\n\nfunction createMiniState<T extends TweenProps>(\n obj: T,\n parentReceiver: TweenProps,\n notifyListeners: () => void,\n) {\n if (objectHasProp(obj, STATE_PROXY)) return obj;\n\n for (const [key, value] of Object.entries(obj)) {\n if (isPlainObject(value)) {\n parentReceiver[key] = createMiniState(value, {}, notifyListeners);\n } else {\n defineStateProxy(key, value, parentReceiver, notifyListeners);\n }\n }\n\n return parentReceiver as T;\n}\n\nexport function miniStore<T extends TweenProps>(init: T) {\n const listeners = new Set<Listener<T>>();\n const notifyListeners = () => {\n listeners.forEach((listener) => listener(store));\n };\n\n const store = createMiniState(init, {}, notifyListeners) as T;\n\n return {\n get state() {\n return store;\n },\n subscribe: (listener: Listener<T>) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\nexport function useMiniStore<T extends TweenProps>(initialValue: T) {\n const storeRef = useRef<ReturnType<typeof miniStore<T>>>(null);\n const [, setVersion] = useState(0);\n\n // istanbul ignore else @preserve\n if (!storeRef.current) {\n storeRef.current = miniStore(initialValue);\n }\n\n useEffect(\n () =>\n storeRef.current!.subscribe(() => setVersion((v) => v === 2 ? 0 : v + 1)),\n [],\n );\n\n return storeRef.current!.state;\n}\n","import { useEffect, useRef } from \"preact/hooks\";\nimport {\n dummyInstance,\n isServer,\n Timeline,\n Tween,\n type TweenProps,\n} from \"@thednp/tween\";\nimport { useMiniStore } from \"./miniStore.ts\";\n\nexport { Timeline, Tween, useMiniStore };\n\n/**\n * Hook for updating values with Tween.\n *\n * **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.\n * This has two important aspects: never configure or start update loop in SSR\n * and only configure or start the loop when component is mounted in the client.\n *\n * @param initialValues - Initial tween values\n * @returns [store, tween] Tuple of reactive store and Tween instance\n * @example\n * const App = () => {\n * const [state, tween] = useTween({ x: 0, y: 0 })\n *\n * useEffect(() => {\n * tween.to({ x: 100, y: 100 }, 1000).start()\n * }, [])\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function useTween<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Tween<T>] as const;\n }\n const store = useMiniStore(initialValues);\n const tweenRef = useRef<Tween<T>>(null);\n\n // istanbul ignore else @preserve\n if (!tweenRef.current) {\n tweenRef.current = new Tween(store);\n }\n\n const dispose = () => {\n tweenRef.current!.stop();\n tweenRef.current!.clear();\n };\n useEffect(() => dispose, []);\n\n return [store, tweenRef.current] as [T, Tween<T>];\n}\n\n/**\n * Hook for sequencing values update with Timeline.\n *\n * **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.\n * This has two important aspects: never configure or start update loop in SSR\n * and only configure or start the loop when component is mounted in the client.\n *\n * @param initialValues - Initial tween values\n * @returns [store, timeline] Tuple of reactive store and Timeline instance\n * @example\n * const App = () => {\n * const [state, timeline] = useTimeline({ x: 0, y: 0 })\n *\n * useEffect(() => {\n * timeline.to({ x: 100, y: 100 }).start()\n * }, [])\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function useTimeline<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Timeline<T>] as const;\n }\n const store = useMiniStore(initialValues);\n const timelineRef = useRef<Timeline<T>>(null);\n\n // istanbul ignore else @preserve\n if (!timelineRef.current) {\n timelineRef.current = new Timeline(store);\n }\n\n const dispose = () => {\n timelineRef.current!.stop();\n timelineRef.current!.clear();\n };\n useEffect(() => dispose, []);\n\n return [store, timelineRef.current] as [T, Timeline<T>];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"preact.mjs","names":[],"sources":["../../src/preact/miniStore.ts","../../src/preact/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"preact/hooks\";\nimport {\n type ArrayVal,\n isArray,\n isPlainObject,\n objectHasProp,\n type TweenProps,\n} from \"@thednp/tween\";\n\nconst STATE_PROXY = \"_proxy\";\nconst proxyProps = {\n value: 1,\n enumerable: false,\n configurable: false,\n writable: false,\n};\n\ntype Listener<T> = (state: T) => void;\n\nfunction defineArrayProxy<T extends ArrayVal>(\n index: number,\n value: T[number] | ArrayVal,\n target: T | ArrayVal | ArrayVal[],\n sourceLen: number,\n notifyListeners: () => void,\n) {\n const itemIsLast = index === sourceLen - 1;\n\n if (isArray(value)) {\n const subArray: typeof value = [];\n const valueLen = value.length;\n\n value.forEach((itm, idx) => {\n const subItemIsLast = itemIsLast && idx === valueLen - 1;\n\n let currentItem = itm;\n Object.defineProperty(subArray, idx, {\n get: () => currentItem,\n set: (newValue: typeof itm) => {\n currentItem = newValue;\n\n // Only notify on last element to batch updates\n if (subItemIsLast) {\n notifyListeners();\n }\n },\n enumerable: true,\n });\n });\n target[index] = subArray;\n } else {\n let currentValue = value;\n const getter = () => currentValue;\n const setter = (newVal: typeof value) => {\n currentValue = newVal;\n if (itemIsLast) {\n notifyListeners();\n }\n };\n Object.defineProperties(target, {\n [index]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n }\n}\n\nfunction defineStateProxy<T extends Omit<TweenProps, \"_proxy\">>(\n key: number | keyof T,\n value: T[keyof T],\n target: T | ArrayVal,\n notifyListeners: () => void,\n) {\n const valueIsArray = isArray(value);\n let currentValue = value as ArrayVal | ArrayVal[];\n\n const getter = () => currentValue;\n let setter;\n\n if (valueIsArray) {\n // Build array proxy structure\n const arrayProxy: ArrayVal | ArrayVal[] = [];\n const valLength = value.length;\n\n for (let i = 0; i < valLength; i++) {\n defineArrayProxy(\n i,\n (value as ArrayVal)[i],\n arrayProxy as ArrayVal,\n valLength,\n notifyListeners,\n );\n }\n currentValue = arrayProxy;\n } else {\n setter = (newValue: typeof currentValue) => {\n if (currentValue !== newValue) {\n currentValue = newValue;\n notifyListeners();\n }\n };\n }\n\n Object.defineProperties(target, {\n [STATE_PROXY]: proxyProps,\n [key]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n}\n\nfunction createMiniState<T extends TweenProps>(\n obj: T,\n parentReceiver: TweenProps,\n notifyListeners: () => void,\n) {\n if (objectHasProp(obj, STATE_PROXY)) return obj;\n\n for (const [key, value] of Object.entries(obj)) {\n if (isPlainObject(value)) {\n parentReceiver[key] = createMiniState(value, {}, notifyListeners);\n } else {\n defineStateProxy(key, value, parentReceiver, notifyListeners);\n }\n }\n\n return parentReceiver as T;\n}\n\nexport function miniStore<T extends TweenProps>(init: T) {\n const listeners = new Set<Listener<T>>();\n const notifyListeners = () => {\n listeners.forEach((listener) => listener(store));\n };\n\n const store = createMiniState(init, {}, notifyListeners) as T;\n\n return {\n get state() {\n return store;\n },\n subscribe: (listener: Listener<T>) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\nexport function useMiniStore<T extends TweenProps>(initialValue: T) {\n const storeRef = useRef<ReturnType<typeof miniStore<T>>>(null);\n const [, setVersion] = useState(0);\n\n // istanbul ignore else @preserve\n if (!storeRef.current) {\n storeRef.current = miniStore(initialValue);\n }\n\n useEffect(\n () =>\n storeRef.current!.subscribe(() => setVersion((v) => v === 2 ? 0 : v + 1)),\n [],\n );\n\n return storeRef.current!.state;\n}\n","import { useEffect, useRef } from \"preact/hooks\";\nimport {\n dummyInstance,\n isServer,\n Timeline,\n Tween,\n type TweenProps,\n} from \"@thednp/tween\";\nimport { useMiniStore } from \"./miniStore.ts\";\n\nexport { Timeline, Tween, useMiniStore };\n\n/**\n * Hook for updating values with Tween.\n *\n * **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.\n * This has two important aspects: never configure or start update loop in SSR\n * and only configure or start the loop when component is mounted in the client.\n *\n * @param initialValues - Initial tween values\n * @returns [store, tween] Tuple of reactive store and Tween instance\n * @example\n * const App = () => {\n * const [state, tween] = useTween({ x: 0, y: 0 })\n *\n * useEffect(() => {\n * tween.to({ x: 100, y: 100 }, 1000).start()\n * }, [])\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function useTween<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Tween<T>] as const;\n }\n const store = useMiniStore(initialValues);\n const tweenRef = useRef<Tween<T>>(null);\n\n // istanbul ignore else @preserve\n if (!tweenRef.current) {\n tweenRef.current = new Tween(store);\n }\n\n const dispose = () => {\n tweenRef.current!.stop();\n tweenRef.current!.clear();\n };\n useEffect(() => dispose, []);\n\n return [store, tweenRef.current] as [T, Tween<T>];\n}\n\n/**\n * Hook for sequencing values update with Timeline.\n *\n * **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.\n * This has two important aspects: never configure or start update loop in SSR\n * and only configure or start the loop when component is mounted in the client.\n *\n * @param initialValues - Initial tween values\n * @returns [store, timeline] Tuple of reactive store and Timeline instance\n * @example\n * const App = () => {\n * const [state, timeline] = useTimeline({ x: 0, y: 0 })\n *\n * useEffect(() => {\n * timeline.to({ x: 100, y: 100 }).play()\n * }, [])\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function useTimeline<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Timeline<T>] as const;\n }\n const store = useMiniStore(initialValues);\n const timelineRef = useRef<Timeline<T>>(null);\n\n // istanbul ignore else @preserve\n if (!timelineRef.current) {\n timelineRef.current = new Timeline(store);\n }\n\n const dispose = () => {\n timelineRef.current!.stop();\n timelineRef.current!.clear();\n };\n useEffect(() => dispose, []);\n\n return [store, timelineRef.current] as [T, Timeline<T>];\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @thednp/tween hooks for React v0.0.3 (https://github.com/thednp/tween)
2
+ * @thednp/tween hooks for React v0.0.4 (https://github.com/thednp/tween)
3
3
  * Copyright 2026 © thednp
4
4
  * Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
5
5
  */
@@ -48,7 +48,7 @@ declare const useTween: <T extends TweenProps>(initialValues: T) => readonly [T,
48
48
  * const [state, timeline] = useTimeline({ x: 0, y: 0 })
49
49
  *
50
50
  * useEffect(() => {
51
- * timeline.to({ x: 100, y: 100 }).start()
51
+ * timeline.to({ x: 100, y: 100 }).play()
52
52
  * }, [])
53
53
  *
54
54
  * return (
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @thednp/tween hooks for React v0.0.3 (https://github.com/thednp/tween)
2
+ * @thednp/tween hooks for React v0.0.4 (https://github.com/thednp/tween)
3
3
  * Copyright 2026 © thednp
4
4
  * Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
5
5
  */
@@ -156,7 +156,7 @@ const useTween = (initialValues) => {
156
156
  * const [state, timeline] = useTimeline({ x: 0, y: 0 })
157
157
  *
158
158
  * useEffect(() => {
159
- * timeline.to({ x: 100, y: 100 }).start()
159
+ * timeline.to({ x: 100, y: 100 }).play()
160
160
  * }, [])
161
161
  *
162
162
  * return (
@@ -1 +1 @@
1
- {"version":3,"file":"react.mjs","names":[],"sources":["../../src/react/miniStore.ts","../../src/react/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport {\n type ArrayVal,\n isArray,\n isPlainObject,\n objectHasProp,\n type TweenProps,\n} from \"@thednp/tween\";\n\nconst STATE_PROXY = \"_proxy\";\nconst proxyProps = {\n value: 1,\n enumerable: false,\n configurable: false,\n writable: false,\n};\n\ntype Listener<T> = (state: T) => void;\n\nfunction defineArrayProxy<T extends ArrayVal>(\n index: number,\n value: T[number] | ArrayVal,\n target: T | ArrayVal | ArrayVal[],\n sourceLen: number,\n notifyListeners: () => void,\n) {\n const itemIsLast = index === sourceLen - 1;\n\n if (isArray(value)) {\n const subArray: typeof value = [];\n const valueLen = value.length;\n\n value.forEach((itm, idx) => {\n const subItemIsLast = itemIsLast && idx === valueLen - 1;\n\n let currentItem = itm;\n Object.defineProperty(subArray, idx, {\n get: () => currentItem,\n set: (newValue: typeof itm) => {\n currentItem = newValue;\n\n // Only notify on last element to batch updates\n if (subItemIsLast) {\n notifyListeners();\n }\n },\n enumerable: true,\n });\n });\n target[index] = subArray;\n } else {\n let currentValue = value;\n const getter = () => currentValue;\n const setter = (newVal: typeof value) => {\n currentValue = newVal;\n if (itemIsLast) {\n notifyListeners();\n }\n };\n Object.defineProperties(target, {\n [index]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n }\n}\n\nfunction defineStateProxy<T extends Omit<TweenProps, \"_proxy\">>(\n key: number | keyof T,\n value: T[keyof T],\n target: T | ArrayVal,\n notifyListeners: () => void,\n) {\n const valueIsArray = isArray(value);\n let currentValue = value as ArrayVal | ArrayVal[];\n\n const getter = () => currentValue;\n let setter;\n\n if (valueIsArray) {\n // Build array proxy structure\n const arrayProxy: ArrayVal | ArrayVal[] = [];\n const valLength = value.length;\n\n for (let i = 0; i < valLength; i++) {\n defineArrayProxy(\n i,\n (value as ArrayVal)[i],\n arrayProxy as ArrayVal,\n valLength,\n notifyListeners,\n );\n }\n currentValue = arrayProxy;\n } else {\n setter = (newValue: typeof currentValue) => {\n if (currentValue !== newValue) {\n currentValue = newValue;\n notifyListeners();\n }\n };\n }\n\n Object.defineProperties(target, {\n [STATE_PROXY]: proxyProps,\n [key]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n}\n\nfunction createMiniState<T extends TweenProps>(\n obj: T,\n parentReceiver: TweenProps,\n notifyListeners: () => void,\n) {\n if (objectHasProp(obj, STATE_PROXY)) return obj;\n\n for (const [key, value] of Object.entries(obj)) {\n if (isPlainObject(value)) {\n parentReceiver[key] = createMiniState(value, {}, notifyListeners);\n } else {\n defineStateProxy(key, value, parentReceiver, notifyListeners);\n }\n }\n\n return parentReceiver as T;\n}\n\nexport function miniStore<T extends TweenProps>(init: T) {\n const listeners = new Set<Listener<T>>();\n const notifyListeners = () => {\n listeners.forEach((listener) => listener(store));\n };\n\n const store = createMiniState(init, {}, notifyListeners) as T;\n\n return {\n get state() {\n return store;\n },\n subscribe: (listener: Listener<T>) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\nexport function useMiniStore<T extends TweenProps>(initialValue: T) {\n const storeRef = useRef<ReturnType<typeof miniStore<T>>>(null);\n const [, setVersion] = useState(0);\n\n // istanbul ignore else @preserve\n if (!storeRef.current) {\n storeRef.current = miniStore(initialValue);\n }\n\n useEffect(\n () =>\n storeRef.current!.subscribe(() =>\n setVersion((v) => v === 2 ? /* istanbul ignore next */ 0 : v + 1)\n ),\n [],\n );\n\n return storeRef.current!.state;\n}\n","import { useEffect, useRef } from \"react\";\nimport {\n dummyInstance,\n isServer,\n Timeline,\n Tween,\n type TweenProps,\n} from \"@thednp/tween\";\nimport { useMiniStore } from \"./miniStore.ts\";\n\nexport { Timeline, Tween, useMiniStore };\n\n/**\n * Hook for updating values with Tween.\n *\n * **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.\n * This has two important aspects: never configure or start update loop in SSR\n * and only configure or start the loop when component is mounted in the client.\n *\n * @param initialValues - Initial tween values\n * @returns [store, tween] Tuple of reactive store and Tween instance\n * @example\n * const App = () => {\n * const [state, tween] = useTween({ x: 0, y: 0 })\n *\n * useEffect(() => {\n * tween.to({ x: 100, y: 100 }).start()\n * }, [])\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport const useTween = <T extends TweenProps>(initialValues: T) => {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Tween<T>] as const;\n }\n const tweenRef = useRef<Tween<T> | null>(null);\n const state = useMiniStore(initialValues);\n\n // istanbul ignore else @preserve\n if (!tweenRef.current) {\n tweenRef.current = new Tween(state);\n }\n\n useEffect(() => {\n return () => {\n tweenRef.current?.stop();\n tweenRef.current?.clear();\n };\n }, []);\n\n return [state, tweenRef.current] as [T, Tween<T>];\n};\n\n/**\n * Hook for sequencing values update with Timeline.\n *\n * **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.\n * This has two important aspects: never configure or start update loop in SSR\n * and only configure or start the loop when component is mounted in the client.\n *\n * @param initialValues - Initial tween values\n * @returns [store, timeline] Tuple of reactive store and Timeline instance\n * @example\n * const App = () => {\n * const [state, timeline] = useTimeline({ x: 0, y: 0 })\n *\n * useEffect(() => {\n * timeline.to({ x: 100, y: 100 }).start()\n * }, [])\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function useTimeline<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Timeline<T>] as const;\n }\n const timelineRef = useRef<Timeline<T> | null>(null);\n const state = useMiniStore(initialValues);\n\n // istanbul ignore else @preserve\n if (!timelineRef.current) {\n timelineRef.current = new Timeline(state);\n }\n\n useEffect(() => {\n return () => {\n timelineRef.current?.clear();\n timelineRef.current?.stop();\n };\n }, []);\n\n return [state, timelineRef.current] as [T, Timeline<T>];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"react.mjs","names":[],"sources":["../../src/react/miniStore.ts","../../src/react/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport {\n type ArrayVal,\n isArray,\n isPlainObject,\n objectHasProp,\n type TweenProps,\n} from \"@thednp/tween\";\n\nconst STATE_PROXY = \"_proxy\";\nconst proxyProps = {\n value: 1,\n enumerable: false,\n configurable: false,\n writable: false,\n};\n\ntype Listener<T> = (state: T) => void;\n\nfunction defineArrayProxy<T extends ArrayVal>(\n index: number,\n value: T[number] | ArrayVal,\n target: T | ArrayVal | ArrayVal[],\n sourceLen: number,\n notifyListeners: () => void,\n) {\n const itemIsLast = index === sourceLen - 1;\n\n if (isArray(value)) {\n const subArray: typeof value = [];\n const valueLen = value.length;\n\n value.forEach((itm, idx) => {\n const subItemIsLast = itemIsLast && idx === valueLen - 1;\n\n let currentItem = itm;\n Object.defineProperty(subArray, idx, {\n get: () => currentItem,\n set: (newValue: typeof itm) => {\n currentItem = newValue;\n\n // Only notify on last element to batch updates\n if (subItemIsLast) {\n notifyListeners();\n }\n },\n enumerable: true,\n });\n });\n target[index] = subArray;\n } else {\n let currentValue = value;\n const getter = () => currentValue;\n const setter = (newVal: typeof value) => {\n currentValue = newVal;\n if (itemIsLast) {\n notifyListeners();\n }\n };\n Object.defineProperties(target, {\n [index]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n }\n}\n\nfunction defineStateProxy<T extends Omit<TweenProps, \"_proxy\">>(\n key: number | keyof T,\n value: T[keyof T],\n target: T | ArrayVal,\n notifyListeners: () => void,\n) {\n const valueIsArray = isArray(value);\n let currentValue = value as ArrayVal | ArrayVal[];\n\n const getter = () => currentValue;\n let setter;\n\n if (valueIsArray) {\n // Build array proxy structure\n const arrayProxy: ArrayVal | ArrayVal[] = [];\n const valLength = value.length;\n\n for (let i = 0; i < valLength; i++) {\n defineArrayProxy(\n i,\n (value as ArrayVal)[i],\n arrayProxy as ArrayVal,\n valLength,\n notifyListeners,\n );\n }\n currentValue = arrayProxy;\n } else {\n setter = (newValue: typeof currentValue) => {\n if (currentValue !== newValue) {\n currentValue = newValue;\n notifyListeners();\n }\n };\n }\n\n Object.defineProperties(target, {\n [STATE_PROXY]: proxyProps,\n [key]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n}\n\nfunction createMiniState<T extends TweenProps>(\n obj: T,\n parentReceiver: TweenProps,\n notifyListeners: () => void,\n) {\n if (objectHasProp(obj, STATE_PROXY)) return obj;\n\n for (const [key, value] of Object.entries(obj)) {\n if (isPlainObject(value)) {\n parentReceiver[key] = createMiniState(value, {}, notifyListeners);\n } else {\n defineStateProxy(key, value, parentReceiver, notifyListeners);\n }\n }\n\n return parentReceiver as T;\n}\n\nexport function miniStore<T extends TweenProps>(init: T) {\n const listeners = new Set<Listener<T>>();\n const notifyListeners = () => {\n listeners.forEach((listener) => listener(store));\n };\n\n const store = createMiniState(init, {}, notifyListeners) as T;\n\n return {\n get state() {\n return store;\n },\n subscribe: (listener: Listener<T>) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\nexport function useMiniStore<T extends TweenProps>(initialValue: T) {\n const storeRef = useRef<ReturnType<typeof miniStore<T>>>(null);\n const [, setVersion] = useState(0);\n\n // istanbul ignore else @preserve\n if (!storeRef.current) {\n storeRef.current = miniStore(initialValue);\n }\n\n useEffect(\n () =>\n storeRef.current!.subscribe(() =>\n setVersion((v) => v === 2 ? /* istanbul ignore next */ 0 : v + 1)\n ),\n [],\n );\n\n return storeRef.current!.state;\n}\n","import { useEffect, useRef } from \"react\";\nimport {\n dummyInstance,\n isServer,\n Timeline,\n Tween,\n type TweenProps,\n} from \"@thednp/tween\";\nimport { useMiniStore } from \"./miniStore.ts\";\n\nexport { Timeline, Tween, useMiniStore };\n\n/**\n * Hook for updating values with Tween.\n *\n * **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.\n * This has two important aspects: never configure or start update loop in SSR\n * and only configure or start the loop when component is mounted in the client.\n *\n * @param initialValues - Initial tween values\n * @returns [store, tween] Tuple of reactive store and Tween instance\n * @example\n * const App = () => {\n * const [state, tween] = useTween({ x: 0, y: 0 })\n *\n * useEffect(() => {\n * tween.to({ x: 100, y: 100 }).start()\n * }, [])\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport const useTween = <T extends TweenProps>(initialValues: T) => {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Tween<T>] as const;\n }\n const tweenRef = useRef<Tween<T> | null>(null);\n const state = useMiniStore(initialValues);\n\n // istanbul ignore else @preserve\n if (!tweenRef.current) {\n tweenRef.current = new Tween(state);\n }\n\n useEffect(() => {\n return () => {\n tweenRef.current?.stop();\n tweenRef.current?.clear();\n };\n }, []);\n\n return [state, tweenRef.current] as [T, Tween<T>];\n};\n\n/**\n * Hook for sequencing values update with Timeline.\n *\n * **NOTE**: - configuration must be wrapped in `useEffect` or `eventListener`.\n * This has two important aspects: never configure or start update loop in SSR\n * and only configure or start the loop when component is mounted in the client.\n *\n * @param initialValues - Initial tween values\n * @returns [store, timeline] Tuple of reactive store and Timeline instance\n * @example\n * const App = () => {\n * const [state, timeline] = useTimeline({ x: 0, y: 0 })\n *\n * useEffect(() => {\n * timeline.to({ x: 100, y: 100 }).play()\n * }, [])\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function useTimeline<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Timeline<T>] as const;\n }\n const timelineRef = useRef<Timeline<T> | null>(null);\n const state = useMiniStore(initialValues);\n\n // istanbul ignore else @preserve\n if (!timelineRef.current) {\n timelineRef.current = new Timeline(state);\n }\n\n useEffect(() => {\n return () => {\n timelineRef.current?.clear();\n timelineRef.current?.stop();\n };\n }, []);\n\n return [state, timelineRef.current] as [T, Timeline<T>];\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @thednp/tween primitives for SolidJS v0.0.3 (https://github.com/thednp/tween)
2
+ * @thednp/tween primitives for SolidJS v0.0.4 (https://github.com/thednp/tween)
3
3
  * Copyright 2026 © thednp
4
4
  * Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
5
5
  */
@@ -46,7 +46,7 @@ declare function createTween<T extends TweenProps>(initialValues: T): readonly [
46
46
  * timeline.to({ x: 100, y: 100 })
47
47
  *
48
48
  * onMount(() => {
49
- * timeline.start()
49
+ * timeline.play()
50
50
  * })
51
51
  *
52
52
  * return (
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @thednp/tween primitives for SolidJS v0.0.3 (https://github.com/thednp/tween)
2
+ * @thednp/tween primitives for SolidJS v0.0.4 (https://github.com/thednp/tween)
3
3
  * Copyright 2026 © thednp
4
4
  * Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
5
5
  */
@@ -133,7 +133,7 @@ function createTween(initialValues) {
133
133
  * timeline.to({ x: 100, y: 100 })
134
134
  *
135
135
  * onMount(() => {
136
- * timeline.start()
136
+ * timeline.play()
137
137
  * })
138
138
  *
139
139
  * return (
@@ -1 +1 @@
1
- {"version":3,"file":"solid.mjs","names":[],"sources":["../../src/solid/miniStore.ts","../../src/solid/index.ts"],"sourcesContent":["import { createSignal } from \"solid-js\";\nimport {\n type ArrayVal,\n isArray,\n isPlainObject,\n objectHasProp,\n type TweenProps,\n} from \"@thednp/tween\";\n\nconst STATE_PROXY = \"_proxy\";\nconst proxyProps = {\n value: 1,\n enumerable: false,\n configurable: false,\n writable: false,\n};\n\nfunction defineArrayProxy<T extends ArrayVal>(\n index: number,\n value: T[number] | ArrayVal,\n target: T | ArrayVal | ArrayVal[],\n sourceLen: number,\n notifyListeners: () => void,\n) {\n const itemIsLast = index === sourceLen - 1;\n\n if (isArray(value)) {\n const subArray: typeof value = [];\n const valueLen = value.length;\n\n value.forEach((itm, idx) => {\n const subItemIsLast = itemIsLast && idx === valueLen - 1;\n\n let currentItem = itm;\n Object.defineProperty(subArray, idx, {\n get: () => currentItem,\n set: (newValue: typeof itm) => {\n currentItem = newValue;\n\n // Only notify on last element to batch updates\n if (subItemIsLast) {\n notifyListeners();\n }\n },\n enumerable: true,\n });\n });\n target[index] = subArray;\n } else {\n let currentValue = value;\n const getter = () => currentValue;\n const setter = (newVal: typeof value) => {\n currentValue = newVal;\n if (itemIsLast) {\n notifyListeners();\n }\n };\n Object.defineProperties(target, {\n [index]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n }\n}\n\nfunction defineStateProxy<T extends Omit<TweenProps, \"_proxy\">>(\n key: number | keyof T,\n value: T[keyof T] | ArrayVal,\n target: T | ArrayVal,\n) {\n const [get, set] = createSignal(value);\n let getter = get;\n let setter;\n\n if (isArray(value)) {\n const arrayProxy: typeof value = [];\n const valLength = value.length;\n const [version, setVersion] = createSignal(0);\n for (let i = 0; i < valLength; i++) {\n defineArrayProxy(i, (value as ArrayVal)[i], arrayProxy, valLength, () => {\n setVersion((v) => 1 - v);\n });\n }\n getter = () => {\n version();\n return get();\n };\n\n set(arrayProxy);\n } else {\n setter = set;\n set(value as never);\n }\n\n Object.defineProperties(target, {\n [STATE_PROXY]: proxyProps,\n [key]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n}\n\nfunction createMiniState<T extends TweenProps>(\n obj: T,\n parentReceiver: TweenProps | number[] | [string, ...number[]][],\n) {\n if (objectHasProp(obj, STATE_PROXY)) return obj;\n\n for (const [key, value] of Object.entries(obj)) {\n if (isPlainObject(value)) {\n (parentReceiver as TweenProps)[key] = createMiniState(value, {});\n } else {\n defineStateProxy(key, value, parentReceiver);\n }\n }\n\n return parentReceiver as T;\n}\n\nexport function miniStore<T extends TweenProps>(init: T) {\n return createMiniState(init, {}) as T;\n}\n","import {\n dummyInstance,\n isServer,\n Timeline,\n Tween,\n type TweenProps,\n} from \"@thednp/tween\";\nimport { onCleanup } from \"solid-js\";\nimport { miniStore } from \"./miniStore.ts\";\n\nexport { miniStore, Timeline, Tween };\n\n/**\n * SolidJS primitive for updating values with Tween.\n *\n * @param initialValues - Initial tween values\n * @returns [store, tween] Tuple of reactive store and Tween instance\n * @example\n * const App = () => {\n * const [state, tween] = createTween({ x: 0, y: 0 })\n *\n * // configuration is free-form, no re-render ever happens\n * tween.to({ x: 100, y: 100 })\n *\n * onMount(() => {\n * tween.start()\n * })\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function createTween<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Tween<T>] as const;\n }\n const store = miniStore(initialValues);\n const tween = new Tween(store);\n\n onCleanup(() => {\n tween.stop();\n tween.clear();\n });\n\n return [store, tween] as [T, Tween<T>];\n}\n\n/**\n * SolidJS primitive for sequencing values update with Timeline.\n *\n * @param initialValues - Initial tween values\n * @returns [store, timeline] Tuple of reactive store and Timeline instance\n * @example\n * const App = () => {\n * const [state, timeline] = createTimeline({ x: 0, y: 0 })\n *\n * // configuration is free-form\n * timeline.to({ x: 100, y: 100 })\n *\n * onMount(() => {\n * timeline.start()\n * })\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function createTimeline<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Timeline<T>] as const;\n }\n const store = miniStore(initialValues);\n const timeline = new Timeline(store);\n\n onCleanup(() => {\n timeline.stop();\n timeline.clear();\n });\n\n return [store, timeline] as [T, Timeline<T>];\n}\n"],"mappings":";;;;;;;;;;;AASA,MAAM,cAAc;AACpB,MAAM,aAAa;CACjB,OAAO;CACP,YAAY;CACZ,cAAc;CACd,UAAU;CACX;AAED,SAAS,iBACP,OACA,OACA,QACA,WACA,iBACA;CACA,MAAM,aAAa,UAAU,YAAY;AAEzC,KAAI,QAAQ,MAAM,EAAE;EAClB,MAAM,WAAyB,EAAE;EACjC,MAAM,WAAW,MAAM;AAEvB,QAAM,SAAS,KAAK,QAAQ;GAC1B,MAAM,gBAAgB,cAAc,QAAQ,WAAW;GAEvD,IAAI,cAAc;AAClB,UAAO,eAAe,UAAU,KAAK;IACnC,WAAW;IACX,MAAM,aAAyB;AAC7B,mBAAc;AAGd,SAAI,cACF,kBAAiB;;IAGrB,YAAY;IACb,CAAC;IACF;AACF,SAAO,SAAS;QACX;EACL,IAAI,eAAe;EACnB,MAAM,eAAe;EACrB,MAAM,UAAU,WAAyB;AACvC,kBAAe;AACf,OAAI,WACF,kBAAiB;;AAGrB,SAAO,iBAAiB,QAAQ,GAC7B,QAAQ;GACP,KAAK;GACL,KAAK;GACL,YAAY;GACb,EACF,CAAC;;;AAIN,SAAS,iBACP,KACA,OACA,QACA;CACA,MAAM,CAAC,KAAK,OAAO,aAAa,MAAM;CACtC,IAAI,SAAS;CACb,IAAI;AAEJ,KAAI,QAAQ,MAAM,EAAE;EAClB,MAAM,aAA2B,EAAE;EACnC,MAAM,YAAY,MAAM;EACxB,MAAM,CAAC,SAAS,cAAc,aAAa,EAAE;AAC7C,OAAK,IAAI,IAAI,GAAG,IAAI,WAAW,IAC7B,kBAAiB,GAAI,MAAmB,IAAI,YAAY,iBAAiB;AACvE,eAAY,MAAM,IAAI,EAAE;IACxB;AAEJ,iBAAe;AACb,YAAS;AACT,UAAO,KAAK;;AAGd,MAAI,WAAW;QACV;AACL,WAAS;AACT,MAAI,MAAe;;AAGrB,QAAO,iBAAiB,QAAQ;GAC7B,cAAc;GACd,MAAM;GACL,KAAK;GACL,KAAK;GACL,YAAY;GACb;EACF,CAAC;;AAGJ,SAAS,gBACP,KACA,gBACA;AACA,KAAI,cAAc,KAAK,YAAY,CAAE,QAAO;AAE5C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,CAC5C,KAAI,cAAc,MAAM,CACtB,CAAC,eAA8B,OAAO,gBAAgB,OAAO,EAAE,CAAC;KAEhE,kBAAiB,KAAK,OAAO,eAAe;AAIhD,QAAO;;AAGT,SAAgB,UAAgC,MAAS;AACvD,QAAO,gBAAgB,MAAM,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;AC3FlC,SAAgB,YAAkC,eAAkB;AAClE,KAAI,SACF,QAAO,CAAC,eAAe,cAAqC;CAE9D,MAAM,QAAQ,UAAU,cAAc;CACtC,MAAM,QAAQ,IAAI,MAAM,MAAM;AAE9B,iBAAgB;AACd,QAAM,MAAM;AACZ,QAAM,OAAO;GACb;AAEF,QAAO,CAAC,OAAO,MAAM;;;;;;;;;;;;;;;;;;;;;;;AAwBvB,SAAgB,eAAqC,eAAkB;AACrE,KAAI,SACF,QAAO,CAAC,eAAe,cAAwC;CAEjE,MAAM,QAAQ,UAAU,cAAc;CACtC,MAAM,WAAW,IAAI,SAAS,MAAM;AAEpC,iBAAgB;AACd,WAAS,MAAM;AACf,WAAS,OAAO;GAChB;AAEF,QAAO,CAAC,OAAO,SAAS"}
1
+ {"version":3,"file":"solid.mjs","names":[],"sources":["../../src/solid/miniStore.ts","../../src/solid/index.ts"],"sourcesContent":["import { createSignal } from \"solid-js\";\nimport {\n type ArrayVal,\n isArray,\n isPlainObject,\n objectHasProp,\n type TweenProps,\n} from \"@thednp/tween\";\n\nconst STATE_PROXY = \"_proxy\";\nconst proxyProps = {\n value: 1,\n enumerable: false,\n configurable: false,\n writable: false,\n};\n\nfunction defineArrayProxy<T extends ArrayVal>(\n index: number,\n value: T[number] | ArrayVal,\n target: T | ArrayVal | ArrayVal[],\n sourceLen: number,\n notifyListeners: () => void,\n) {\n const itemIsLast = index === sourceLen - 1;\n\n if (isArray(value)) {\n const subArray: typeof value = [];\n const valueLen = value.length;\n\n value.forEach((itm, idx) => {\n const subItemIsLast = itemIsLast && idx === valueLen - 1;\n\n let currentItem = itm;\n Object.defineProperty(subArray, idx, {\n get: () => currentItem,\n set: (newValue: typeof itm) => {\n currentItem = newValue;\n\n // Only notify on last element to batch updates\n if (subItemIsLast) {\n notifyListeners();\n }\n },\n enumerable: true,\n });\n });\n target[index] = subArray;\n } else {\n let currentValue = value;\n const getter = () => currentValue;\n const setter = (newVal: typeof value) => {\n currentValue = newVal;\n if (itemIsLast) {\n notifyListeners();\n }\n };\n Object.defineProperties(target, {\n [index]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n }\n}\n\nfunction defineStateProxy<T extends Omit<TweenProps, \"_proxy\">>(\n key: number | keyof T,\n value: T[keyof T] | ArrayVal,\n target: T | ArrayVal,\n) {\n const [get, set] = createSignal(value);\n let getter = get;\n let setter;\n\n if (isArray(value)) {\n const arrayProxy: typeof value = [];\n const valLength = value.length;\n const [version, setVersion] = createSignal(0);\n for (let i = 0; i < valLength; i++) {\n defineArrayProxy(i, (value as ArrayVal)[i], arrayProxy, valLength, () => {\n setVersion((v) => 1 - v);\n });\n }\n getter = () => {\n version();\n return get();\n };\n\n set(arrayProxy);\n } else {\n setter = set;\n set(value as never);\n }\n\n Object.defineProperties(target, {\n [STATE_PROXY]: proxyProps,\n [key]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n}\n\nfunction createMiniState<T extends TweenProps>(\n obj: T,\n parentReceiver: TweenProps | number[] | [string, ...number[]][],\n) {\n if (objectHasProp(obj, STATE_PROXY)) return obj;\n\n for (const [key, value] of Object.entries(obj)) {\n if (isPlainObject(value)) {\n (parentReceiver as TweenProps)[key] = createMiniState(value, {});\n } else {\n defineStateProxy(key, value, parentReceiver);\n }\n }\n\n return parentReceiver as T;\n}\n\nexport function miniStore<T extends TweenProps>(init: T) {\n return createMiniState(init, {}) as T;\n}\n","import {\n dummyInstance,\n isServer,\n Timeline,\n Tween,\n type TweenProps,\n} from \"@thednp/tween\";\nimport { onCleanup } from \"solid-js\";\nimport { miniStore } from \"./miniStore.ts\";\n\nexport { miniStore, Timeline, Tween };\n\n/**\n * SolidJS primitive for updating values with Tween.\n *\n * @param initialValues - Initial tween values\n * @returns [store, tween] Tuple of reactive store and Tween instance\n * @example\n * const App = () => {\n * const [state, tween] = createTween({ x: 0, y: 0 })\n *\n * // configuration is free-form, no re-render ever happens\n * tween.to({ x: 100, y: 100 })\n *\n * onMount(() => {\n * tween.start()\n * })\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function createTween<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Tween<T>] as const;\n }\n const store = miniStore(initialValues);\n const tween = new Tween(store);\n\n onCleanup(() => {\n tween.stop();\n tween.clear();\n });\n\n return [store, tween] as [T, Tween<T>];\n}\n\n/**\n * SolidJS primitive for sequencing values update with Timeline.\n *\n * @param initialValues - Initial tween values\n * @returns [store, timeline] Tuple of reactive store and Timeline instance\n * @example\n * const App = () => {\n * const [state, timeline] = createTimeline({ x: 0, y: 0 })\n *\n * // configuration is free-form\n * timeline.to({ x: 100, y: 100 })\n *\n * onMount(() => {\n * timeline.play()\n * })\n *\n * return (\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n * );\n * }\n */\nexport function createTimeline<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Timeline<T>] as const;\n }\n const store = miniStore(initialValues);\n const timeline = new Timeline(store);\n\n onCleanup(() => {\n timeline.stop();\n timeline.clear();\n });\n\n return [store, timeline] as [T, Timeline<T>];\n}\n"],"mappings":";;;;;;;;;;;AASA,MAAM,cAAc;AACpB,MAAM,aAAa;CACjB,OAAO;CACP,YAAY;CACZ,cAAc;CACd,UAAU;CACX;AAED,SAAS,iBACP,OACA,OACA,QACA,WACA,iBACA;CACA,MAAM,aAAa,UAAU,YAAY;AAEzC,KAAI,QAAQ,MAAM,EAAE;EAClB,MAAM,WAAyB,EAAE;EACjC,MAAM,WAAW,MAAM;AAEvB,QAAM,SAAS,KAAK,QAAQ;GAC1B,MAAM,gBAAgB,cAAc,QAAQ,WAAW;GAEvD,IAAI,cAAc;AAClB,UAAO,eAAe,UAAU,KAAK;IACnC,WAAW;IACX,MAAM,aAAyB;AAC7B,mBAAc;AAGd,SAAI,cACF,kBAAiB;;IAGrB,YAAY;IACb,CAAC;IACF;AACF,SAAO,SAAS;QACX;EACL,IAAI,eAAe;EACnB,MAAM,eAAe;EACrB,MAAM,UAAU,WAAyB;AACvC,kBAAe;AACf,OAAI,WACF,kBAAiB;;AAGrB,SAAO,iBAAiB,QAAQ,GAC7B,QAAQ;GACP,KAAK;GACL,KAAK;GACL,YAAY;GACb,EACF,CAAC;;;AAIN,SAAS,iBACP,KACA,OACA,QACA;CACA,MAAM,CAAC,KAAK,OAAO,aAAa,MAAM;CACtC,IAAI,SAAS;CACb,IAAI;AAEJ,KAAI,QAAQ,MAAM,EAAE;EAClB,MAAM,aAA2B,EAAE;EACnC,MAAM,YAAY,MAAM;EACxB,MAAM,CAAC,SAAS,cAAc,aAAa,EAAE;AAC7C,OAAK,IAAI,IAAI,GAAG,IAAI,WAAW,IAC7B,kBAAiB,GAAI,MAAmB,IAAI,YAAY,iBAAiB;AACvE,eAAY,MAAM,IAAI,EAAE;IACxB;AAEJ,iBAAe;AACb,YAAS;AACT,UAAO,KAAK;;AAGd,MAAI,WAAW;QACV;AACL,WAAS;AACT,MAAI,MAAe;;AAGrB,QAAO,iBAAiB,QAAQ;GAC7B,cAAc;GACd,MAAM;GACL,KAAK;GACL,KAAK;GACL,YAAY;GACb;EACF,CAAC;;AAGJ,SAAS,gBACP,KACA,gBACA;AACA,KAAI,cAAc,KAAK,YAAY,CAAE,QAAO;AAE5C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,CAC5C,KAAI,cAAc,MAAM,CACtB,CAAC,eAA8B,OAAO,gBAAgB,OAAO,EAAE,CAAC;KAEhE,kBAAiB,KAAK,OAAO,eAAe;AAIhD,QAAO;;AAGT,SAAgB,UAAgC,MAAS;AACvD,QAAO,gBAAgB,MAAM,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;AC3FlC,SAAgB,YAAkC,eAAkB;AAClE,KAAI,SACF,QAAO,CAAC,eAAe,cAAqC;CAE9D,MAAM,QAAQ,UAAU,cAAc;CACtC,MAAM,QAAQ,IAAI,MAAM,MAAM;AAE9B,iBAAgB;AACd,QAAM,MAAM;AACZ,QAAM,OAAO;GACb;AAEF,QAAO,CAAC,OAAO,MAAM;;;;;;;;;;;;;;;;;;;;;;;AAwBvB,SAAgB,eAAqC,eAAkB;AACrE,KAAI,SACF,QAAO,CAAC,eAAe,cAAwC;CAEjE,MAAM,QAAQ,UAAU,cAAc;CACtC,MAAM,WAAW,IAAI,SAAS,MAAM;AAEpC,iBAAgB;AACd,WAAS,MAAM;AACf,WAAS,OAAO;GAChB;AAEF,QAAO,CAAC,OAAO,SAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"svelte.mjs","names":[],"sources":["../../src/svelte/miniStore.svelte.ts","../../src/svelte/index.svelte.ts"],"sourcesContent":["import {\n type ArrayVal,\n isArray,\n isPlainObject,\n objectHasProp,\n type TweenProps,\n} from \"@thednp/tween\";\n\nconst STATE_PROXY = \"_proxy\";\nconst proxyProps = {\n value: 1,\n enumerable: false,\n configurable: false,\n writable: false,\n};\n\nfunction defineArrayProxy<T extends ArrayVal>(\n index: number,\n value: T[number] | ArrayVal,\n target: T | ArrayVal | ArrayVal[],\n sourceLen: number,\n notifyListeners: () => void,\n) {\n const itemIsLast = index === sourceLen - 1;\n\n if (isArray(value)) {\n const subArray: typeof value = [];\n const valueLen = value.length;\n\n value.forEach((itm, idx) => {\n const subItemIsLast = itemIsLast && idx === valueLen - 1;\n let currentItem = itm;\n\n Object.defineProperty(subArray, idx, {\n get: () => currentItem,\n set: (newValue: typeof itm) => {\n currentItem = newValue;\n\n // Only notify on last element to batch updates\n if (subItemIsLast) {\n notifyListeners();\n }\n },\n enumerable: true,\n });\n });\n target[index] = subArray;\n } else {\n let currentValue = value;\n const getter = () => currentValue;\n const setter = (newVal: typeof value) => {\n currentValue = newVal;\n if (itemIsLast) {\n notifyListeners();\n }\n };\n Object.defineProperties(target, {\n [index]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n }\n}\n\nfunction defineStateProxy<T extends Omit<TweenProps, \"_proxy\">>(\n key: number | keyof T,\n value: T[keyof T] | ArrayVal,\n target: T | ArrayVal,\n) {\n let state = $state.raw(value);\n let getter = () => state;\n let setter;\n\n if (isArray(value)) {\n const arrayProxy: typeof value = [];\n const valLength = value.length;\n let version = $state.raw(0);\n const getVersion = () => version;\n for (let i = 0; i < valLength; i++) {\n defineArrayProxy(i, (value as ArrayVal)[i], arrayProxy, valLength, () => {\n version = 1 - version;\n });\n }\n getter = () => {\n getVersion();\n return state;\n };\n\n state = arrayProxy;\n } else {\n setter = (newVal: typeof value) => state = newVal;\n state = value;\n }\n\n Object.defineProperties(target, {\n [STATE_PROXY]: proxyProps,\n [key]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n}\n\nfunction createMiniState<T extends TweenProps>(\n obj: T,\n parentReceiver: TweenProps | number[] | [string, ...number[]][],\n) {\n if (objectHasProp(obj, STATE_PROXY)) return obj;\n\n for (const [key, value] of Object.entries(obj)) {\n if (isPlainObject(value)) {\n (parentReceiver as TweenProps)[key] = createMiniState(value, {});\n } else {\n defineStateProxy(key, value, parentReceiver);\n }\n }\n\n return parentReceiver as T;\n}\n\nexport function miniStore<T extends TweenProps>(init: T) {\n return createMiniState(init, {}) as T;\n}\n","import {\n dummyInstance,\n isServer,\n Timeline,\n Tween,\n type TweenProps,\n} from \"@thednp/tween\";\nimport { onDestroy } from \"svelte\";\nimport { miniStore } from \"./miniStore.svelte.ts\";\n\nexport { miniStore, Timeline, Tween };\n\n/**\n * Svelte hook for updating values with Tween.\n *\n * @param initialValues - Initial tween values\n * @returns [store, tween] Tuple of reactive store and Tween instance\n *\n * @example\n * <script lang=\"ts\">\n * const [state, tween] = createTween({ x: 0, y: 0 })\n *\n * // configuration is free-form, no re-render ever happens\n * tween.to({ x: 100, y: 100 })\n *\n * onMount(() => {\n * tween.start()\n * })\n * </script>\n *\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n */\nexport function createTween<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Tween<T>] as const;\n }\n const store = miniStore(initialValues);\n const tween = new Tween(store);\n\n onDestroy(() => {\n tween.stop();\n tween.clear();\n });\n\n return [store, tween] as [T, Tween<T>];\n}\n\n/**\n * Svelte hook for sequencing values update with Timeline.\n *\n * @param initialValues - Initial tween values\n * @returns [store, timeline] Tuple of reactive store and Timeline instance\n *\n * @example\n * <script lang=\"ts\">\n * const [state, timeline] = createTimeline({ x: 0, y: 0 })\n *\n * // configuration is free-form\n * timeline.to({ x: 100, y: 100 })\n *\n * onMount(() => {\n * timeline.start()\n * })\n * </script>\n *\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n */\nexport function createTimeline<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Timeline<T>] as const;\n }\n const store = miniStore(initialValues);\n const timeline = new Timeline(store);\n\n onDestroy(() => {\n timeline.stop();\n timeline.clear();\n });\n\n return [store, timeline] as [T, Timeline<T>];\n}\n"],"mappings":";;;;;;;;;;;AAQA,MAAM,cAAc;AACpB,MAAM,aAAa;CACjB,OAAO;CACP,YAAY;CACZ,cAAc;CACd,UAAU;CACX;AAED,SAAS,iBACP,OACA,OACA,QACA,WACA,iBACA;CACA,MAAM,aAAa,UAAU,YAAY;AAEzC,KAAI,QAAQ,MAAM,EAAE;EAClB,MAAM,WAAyB,EAAE;EACjC,MAAM,WAAW,MAAM;AAEvB,QAAM,SAAS,KAAK,QAAQ;GAC1B,MAAM,gBAAgB,cAAc,QAAQ,WAAW;GACvD,IAAI,cAAc;AAElB,UAAO,eAAe,UAAU,KAAK;IACnC,WAAW;IACX,MAAM,aAAyB;AAC7B,mBAAc;AAGd,SAAI,cACF,kBAAiB;;IAGrB,YAAY;IACb,CAAC;IACF;AACF,SAAO,SAAS;QACX;EACL,IAAI,eAAe;EACnB,MAAM,eAAe;EACrB,MAAM,UAAU,WAAyB;AACvC,kBAAe;AACf,OAAI,WACF,kBAAiB;;AAGrB,SAAO,iBAAiB,QAAQ,GAC7B,QAAQ;GACP,KAAK;GACL,KAAK;GACL,YAAY;GACb,EACF,CAAC;;;AAIN,SAAS,iBACP,KACA,OACA,QACA;CACA,IAAI,QAAQ,OAAO,IAAI,MAAM;CAC7B,IAAI,eAAe;CACnB,IAAI;AAEJ,KAAI,QAAQ,MAAM,EAAE;EAClB,MAAM,aAA2B,EAAE;EACnC,MAAM,YAAY,MAAM;EACxB,IAAI,UAAU,OAAO,IAAI,EAAE;EAC3B,MAAM,mBAAmB;AACzB,OAAK,IAAI,IAAI,GAAG,IAAI,WAAW,IAC7B,kBAAiB,GAAI,MAAmB,IAAI,YAAY,iBAAiB;AACvE,aAAU,IAAI;IACd;AAEJ,iBAAe;AACb,eAAY;AACZ,UAAO;;AAGT,UAAQ;QACH;AACL,YAAU,WAAyB,QAAQ;AAC3C,UAAQ;;AAGV,QAAO,iBAAiB,QAAQ;GAC7B,cAAc;GACd,MAAM;GACL,KAAK;GACL,KAAK;GACL,YAAY;GACb;EACF,CAAC;;AAGJ,SAAS,gBACP,KACA,gBACA;AACA,KAAI,cAAc,KAAK,YAAY,CAAE,QAAO;AAE5C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,CAC5C,KAAI,cAAc,MAAM,CACtB,CAAC,eAA8B,OAAO,gBAAgB,OAAO,EAAE,CAAC;KAEhE,kBAAiB,KAAK,OAAO,eAAe;AAIhD,QAAO;;AAGT,SAAgB,UAAgC,MAAS;AACvD,QAAO,gBAAgB,MAAM,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AC5FlC,SAAgB,YAAkC,eAAkB;AAClE,KAAI,SACF,QAAO,CAAC,eAAe,cAAqC;CAE9D,MAAM,QAAQ,UAAU,cAAc;CACtC,MAAM,QAAQ,IAAI,MAAM,MAAM;AAE9B,iBAAgB;AACd,QAAM,MAAM;AACZ,QAAM,OAAO;GACb;AAEF,QAAO,CAAC,OAAO,MAAM;;;;;;;;;;;;;;;;;;;;;;AAuBvB,SAAgB,eAAqC,eAAkB;AACrE,KAAI,SACF,QAAO,CAAC,eAAe,cAAwC;CAEjE,MAAM,QAAQ,UAAU,cAAc;CACtC,MAAM,WAAW,IAAI,SAAS,MAAM;AAEpC,iBAAgB;AACd,WAAS,MAAM;AACf,WAAS,OAAO;GAChB;AAEF,QAAO,CAAC,OAAO,SAAS"}
1
+ {"version":3,"file":"svelte.mjs","names":[],"sources":["../../src/svelte/miniStore.svelte.ts","../../src/svelte/index.svelte.ts"],"sourcesContent":["import {\n type ArrayVal,\n isArray,\n isPlainObject,\n objectHasProp,\n type TweenProps,\n} from \"@thednp/tween\";\n\nconst STATE_PROXY = \"_proxy\";\nconst proxyProps = {\n value: 1,\n enumerable: false,\n configurable: false,\n writable: false,\n};\n\nfunction defineArrayProxy<T extends ArrayVal>(\n index: number,\n value: T[number] | ArrayVal,\n target: T | ArrayVal | ArrayVal[],\n sourceLen: number,\n notifyListeners: () => void,\n) {\n const itemIsLast = index === sourceLen - 1;\n\n if (isArray(value)) {\n const subArray: typeof value = [];\n const valueLen = value.length;\n\n value.forEach((itm, idx) => {\n const subItemIsLast = itemIsLast && idx === valueLen - 1;\n let currentItem = itm;\n\n Object.defineProperty(subArray, idx, {\n get: () => currentItem,\n set: (newValue: typeof itm) => {\n currentItem = newValue;\n\n // Only notify on last element to batch updates\n if (subItemIsLast) {\n notifyListeners();\n }\n },\n enumerable: true,\n });\n });\n target[index] = subArray;\n } else {\n let currentValue = value;\n const getter = () => currentValue;\n const setter = (newVal: typeof value) => {\n currentValue = newVal;\n if (itemIsLast) {\n notifyListeners();\n }\n };\n Object.defineProperties(target, {\n [index]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n }\n}\n\nfunction defineStateProxy<T extends Omit<TweenProps, \"_proxy\">>(\n key: number | keyof T,\n value: T[keyof T] | ArrayVal,\n target: T | ArrayVal,\n) {\n let state = $state.raw(value);\n let getter = () => state;\n let setter;\n\n if (isArray(value)) {\n const arrayProxy: typeof value = [];\n const valLength = value.length;\n let version = $state.raw(0);\n const getVersion = () => version;\n for (let i = 0; i < valLength; i++) {\n defineArrayProxy(i, (value as ArrayVal)[i], arrayProxy, valLength, () => {\n version = 1 - version;\n });\n }\n getter = () => {\n getVersion();\n return state;\n };\n\n state = arrayProxy;\n } else {\n setter = (newVal: typeof value) => state = newVal;\n state = value;\n }\n\n Object.defineProperties(target, {\n [STATE_PROXY]: proxyProps,\n [key]: {\n get: getter,\n set: setter,\n enumerable: true,\n },\n });\n}\n\nfunction createMiniState<T extends TweenProps>(\n obj: T,\n parentReceiver: TweenProps | number[] | [string, ...number[]][],\n) {\n if (objectHasProp(obj, STATE_PROXY)) return obj;\n\n for (const [key, value] of Object.entries(obj)) {\n if (isPlainObject(value)) {\n (parentReceiver as TweenProps)[key] = createMiniState(value, {});\n } else {\n defineStateProxy(key, value, parentReceiver);\n }\n }\n\n return parentReceiver as T;\n}\n\nexport function miniStore<T extends TweenProps>(init: T) {\n return createMiniState(init, {}) as T;\n}\n","import {\n dummyInstance,\n isServer,\n Timeline,\n Tween,\n type TweenProps,\n} from \"@thednp/tween\";\nimport { onDestroy } from \"svelte\";\nimport { miniStore } from \"./miniStore.svelte.ts\";\n\nexport { miniStore, Timeline, Tween };\n\n/**\n * Svelte hook for updating values with Tween.\n *\n * @param initialValues - Initial tween values\n * @returns [store, tween] Tuple of reactive store and Tween instance\n *\n * @example\n * <script lang=\"ts\">\n * const [state, tween] = createTween({ x: 0, y: 0 })\n *\n * // configuration is free-form, no re-render ever happens\n * tween.to({ x: 100, y: 100 })\n *\n * onMount(() => {\n * tween.start()\n * })\n * </script>\n *\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n */\nexport function createTween<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Tween<T>] as const;\n }\n const store = miniStore(initialValues);\n const tween = new Tween(store);\n\n onDestroy(() => {\n tween.stop();\n tween.clear();\n });\n\n return [store, tween] as [T, Tween<T>];\n}\n\n/**\n * Svelte hook for sequencing values update with Timeline.\n *\n * @param initialValues - Initial tween values\n * @returns [store, timeline] Tuple of reactive store and Timeline instance\n *\n * @example\n * <script lang=\"ts\">\n * const [state, timeline] = createTimeline({ x: 0, y: 0 })\n *\n * // configuration is free-form\n * timeline.to({ x: 100, y: 100 })\n *\n * onMount(() => {\n * timeline.play()\n * })\n * </script>\n *\n * <div style={{ translate: `${state.x}px ${state.y}px` }} />\n */\nexport function createTimeline<T extends TweenProps>(initialValues: T) {\n if (isServer) {\n return [initialValues, dummyInstance as unknown as Timeline<T>] as const;\n }\n const store = miniStore(initialValues);\n const timeline = new Timeline(store);\n\n onDestroy(() => {\n timeline.stop();\n timeline.clear();\n });\n\n return [store, timeline] as [T, Timeline<T>];\n}\n"],"mappings":";;;;;;;;;;;AAQA,MAAM,cAAc;AACpB,MAAM,aAAa;CACjB,OAAO;CACP,YAAY;CACZ,cAAc;CACd,UAAU;CACX;AAED,SAAS,iBACP,OACA,OACA,QACA,WACA,iBACA;CACA,MAAM,aAAa,UAAU,YAAY;AAEzC,KAAI,QAAQ,MAAM,EAAE;EAClB,MAAM,WAAyB,EAAE;EACjC,MAAM,WAAW,MAAM;AAEvB,QAAM,SAAS,KAAK,QAAQ;GAC1B,MAAM,gBAAgB,cAAc,QAAQ,WAAW;GACvD,IAAI,cAAc;AAElB,UAAO,eAAe,UAAU,KAAK;IACnC,WAAW;IACX,MAAM,aAAyB;AAC7B,mBAAc;AAGd,SAAI,cACF,kBAAiB;;IAGrB,YAAY;IACb,CAAC;IACF;AACF,SAAO,SAAS;QACX;EACL,IAAI,eAAe;EACnB,MAAM,eAAe;EACrB,MAAM,UAAU,WAAyB;AACvC,kBAAe;AACf,OAAI,WACF,kBAAiB;;AAGrB,SAAO,iBAAiB,QAAQ,GAC7B,QAAQ;GACP,KAAK;GACL,KAAK;GACL,YAAY;GACb,EACF,CAAC;;;AAIN,SAAS,iBACP,KACA,OACA,QACA;CACA,IAAI,QAAQ,OAAO,IAAI,MAAM;CAC7B,IAAI,eAAe;CACnB,IAAI;AAEJ,KAAI,QAAQ,MAAM,EAAE;EAClB,MAAM,aAA2B,EAAE;EACnC,MAAM,YAAY,MAAM;EACxB,IAAI,UAAU,OAAO,IAAI,EAAE;EAC3B,MAAM,mBAAmB;AACzB,OAAK,IAAI,IAAI,GAAG,IAAI,WAAW,IAC7B,kBAAiB,GAAI,MAAmB,IAAI,YAAY,iBAAiB;AACvE,aAAU,IAAI;IACd;AAEJ,iBAAe;AACb,eAAY;AACZ,UAAO;;AAGT,UAAQ;QACH;AACL,YAAU,WAAyB,QAAQ;AAC3C,UAAQ;;AAGV,QAAO,iBAAiB,QAAQ;GAC7B,cAAc;GACd,MAAM;GACL,KAAK;GACL,KAAK;GACL,YAAY;GACb;EACF,CAAC;;AAGJ,SAAS,gBACP,KACA,gBACA;AACA,KAAI,cAAc,KAAK,YAAY,CAAE,QAAO;AAE5C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,CAC5C,KAAI,cAAc,MAAM,CACtB,CAAC,eAA8B,OAAO,gBAAgB,OAAO,EAAE,CAAC;KAEhE,kBAAiB,KAAK,OAAO,eAAe;AAIhD,QAAO;;AAGT,SAAgB,UAAgC,MAAS;AACvD,QAAO,gBAAgB,MAAM,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AC5FlC,SAAgB,YAAkC,eAAkB;AAClE,KAAI,SACF,QAAO,CAAC,eAAe,cAAqC;CAE9D,MAAM,QAAQ,UAAU,cAAc;CACtC,MAAM,QAAQ,IAAI,MAAM,MAAM;AAE9B,iBAAgB;AACd,QAAM,MAAM;AACZ,QAAM,OAAO;GACb;AAEF,QAAO,CAAC,OAAO,MAAM;;;;;;;;;;;;;;;;;;;;;;AAuBvB,SAAgB,eAAqC,eAAkB;AACrE,KAAI,SACF,QAAO,CAAC,eAAe,cAAwC;CAEjE,MAAM,QAAQ,UAAU,cAAc;CACtC,MAAM,WAAW,IAAI,SAAS,MAAM;AAEpC,iBAAgB;AACd,WAAS,MAAM;AACf,WAAS,OAAO;GAChB;AAEF,QAAO,CAAC,OAAO,SAAS"}
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @thednp/tween utils for Svelte v0.0.3 (https://github.com/thednp/tween)
2
+ * @thednp/tween utils for Svelte v0.0.4 (https://github.com/thednp/tween)
3
3
  * Copyright 2026 © thednp
4
4
  * Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
5
5
  */
@@ -46,7 +46,7 @@ declare function createTween<T extends TweenProps>(initialValues: T): readonly [
46
46
  * timeline.to({ x: 100, y: 100 })
47
47
  *
48
48
  * onMount(() => {
49
- * timeline.start()
49
+ * timeline.play()
50
50
  * })
51
51
  * </script>
52
52
  *
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @thednp/tween utils for Svelte v0.0.3 (https://github.com/thednp/tween)
2
+ * @thednp/tween utils for Svelte v0.0.4 (https://github.com/thednp/tween)
3
3
  * Copyright 2026 © thednp
4
4
  * Licensed under MIT (https://github.com/thednp/tween/blob/master/LICENSE)
5
5
  */
@@ -134,7 +134,7 @@ function createTween(initialValues) {
134
134
  * timeline.to({ x: 100, y: 100 })
135
135
  *
136
136
  * onMount(() => {
137
- * timeline.start()
137
+ * timeline.play()
138
138
  * })
139
139
  * <\/script>
140
140
  *