@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 +94 -108
- package/dist/preact/preact.d.mts +2 -2
- package/dist/preact/preact.mjs +2 -2
- package/dist/preact/preact.mjs.map +1 -1
- package/dist/react/react.d.mts +2 -2
- package/dist/react/react.mjs +2 -2
- package/dist/react/react.mjs.map +1 -1
- package/dist/solid/solid.d.mts +2 -2
- package/dist/solid/solid.mjs +2 -2
- package/dist/solid/solid.mjs.map +1 -1
- package/dist/svelte/svelte.mjs.map +1 -1
- package/dist/svelte/tween.svelte.d.ts +2 -2
- package/dist/svelte/tween.svelte.js +2 -2
- package/dist/tween/index.d.mts +52 -33
- package/dist/tween/index.d.mts.map +1 -1
- package/dist/tween/index.mjs +66 -45
- package/dist/tween/index.mjs.map +1 -1
- package/dist/tween.min.js +4 -4
- package/dist/tween.min.js.map +1 -1
- package/dist/vue/vue.d.mts +2 -2
- package/dist/vue/vue.mjs +2 -2
- package/dist/vue/vue.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,48 +3,12 @@
|
|
|
3
3
|
[](https://coveralls.io/github/thednp/tween)
|
|
4
4
|
[](https://www.npmjs.com/package/@thednp/tween)
|
|
5
5
|
[](https://github.com/thednp/tween/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.jsdelivr.com/package/npm/@thednp/tween)
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
A
|
|
9
|
+
A TypeScript-first tweening engine forked from the excellent [@tweenjs/tweenjs](https://github.com/tweenjs/tween.js).
|
|
9
10
|
|
|
10
|
-
|
|
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
|
-
* [
|
|
64
|
-
* [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
171
|
+
#### Tween
|
|
172
|
+
Simple tween objects with essential controls, callbacks, and sequencing methods.
|
|
201
173
|
|
|
202
|
-
|
|
203
|
-
- `
|
|
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
|
-
|
|
208
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
187
|
+
#### Key Differences from Original
|
|
218
188
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
203
|
+
### Architecture Notes
|
|
230
204
|
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
248
|
+
[MIT License](LICENSE).
|
package/dist/preact/preact.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @thednp/tween hooks for Preact v0.0.
|
|
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 }).
|
|
51
|
+
* timeline.to({ x: 100, y: 100 }).play()
|
|
52
52
|
* }, [])
|
|
53
53
|
*
|
|
54
54
|
* return (
|
package/dist/preact/preact.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @thednp/tween hooks for Preact v0.0.
|
|
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 }).
|
|
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 }).
|
|
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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"}
|
package/dist/react/react.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @thednp/tween hooks for React v0.0.
|
|
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 }).
|
|
51
|
+
* timeline.to({ x: 100, y: 100 }).play()
|
|
52
52
|
* }, [])
|
|
53
53
|
*
|
|
54
54
|
* return (
|
package/dist/react/react.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @thednp/tween hooks for React v0.0.
|
|
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 }).
|
|
159
|
+
* timeline.to({ x: 100, y: 100 }).play()
|
|
160
160
|
* }, [])
|
|
161
161
|
*
|
|
162
162
|
* return (
|
package/dist/react/react.mjs.map
CHANGED
|
@@ -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 }).
|
|
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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"}
|
package/dist/solid/solid.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @thednp/tween primitives for SolidJS v0.0.
|
|
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.
|
|
49
|
+
* timeline.play()
|
|
50
50
|
* })
|
|
51
51
|
*
|
|
52
52
|
* return (
|
package/dist/solid/solid.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @thednp/tween primitives for SolidJS v0.0.
|
|
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.
|
|
136
|
+
* timeline.play()
|
|
137
137
|
* })
|
|
138
138
|
*
|
|
139
139
|
* return (
|
package/dist/solid/solid.mjs.map
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
49
|
+
* timeline.play()
|
|
50
50
|
* })
|
|
51
51
|
* </script>
|
|
52
52
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @thednp/tween utils for Svelte v0.0.
|
|
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.
|
|
137
|
+
* timeline.play()
|
|
138
138
|
* })
|
|
139
139
|
* <\/script>
|
|
140
140
|
*
|