@thednp/tween 0.0.5 → 0.1.0
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/AGENTS.md +112 -0
- package/CHANGELOG.md +96 -0
- package/README.md +2 -0
- package/dist/preact/preact.d.mts +1 -1
- package/dist/preact/preact.mjs +3 -5
- package/dist/preact/preact.mjs.map +1 -1
- package/dist/react/react.d.mts +1 -1
- package/dist/react/react.mjs +3 -5
- package/dist/react/react.mjs.map +1 -1
- package/dist/solid/solid.d.mts +1 -1
- package/dist/solid/solid.mjs +2 -4
- package/dist/solid/solid.mjs.map +1 -1
- package/dist/svelte/svelte.mjs.map +1 -1
- package/dist/svelte/tween.svelte.d.ts +1 -1
- package/dist/svelte/tween.svelte.js +2 -4
- package/dist/tween/index.d.mts +4 -18
- package/dist/tween/index.d.mts.map +1 -1
- package/dist/tween/index.mjs +18 -67
- 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/vanjs/vanjs.d.mts +56 -0
- package/dist/vanjs/vanjs.d.mts.map +1 -0
- package/dist/vanjs/vanjs.mjs +234 -0
- package/dist/vanjs/vanjs.mjs.map +1 -0
- package/dist/vue/vue.d.mts +1 -1
- package/dist/vue/vue.mjs +2 -4
- package/dist/vue/vue.mjs.map +1 -1
- package/package.json +25 -16
package/AGENTS.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# AGENTS.md — @thednp/tween
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
**@thednp/tween** is a TypeScript-first tweening engine with framework integrations for React, Preact, SolidJS, Svelte, Vue, and VanJS.
|
|
6
|
+
|
|
7
|
+
- **Core**: `src/Tween.ts`, `src/Timeline.ts` — animation engine with auto-managed RAF loop
|
|
8
|
+
- **Extensions**: `src/extend/` — array, object, path, transform interpolation
|
|
9
|
+
- **Framework integrations**: `src/{react,preact,solid,svelte,vue,vanjs}/`
|
|
10
|
+
- **Tests**: `test/*.spec.{ts,tsx}` with Vitest + happy-dom
|
|
11
|
+
- **Build**: tsdown (rolldown-based), outputs ESM + UMD
|
|
12
|
+
- **Package manager**: pnpm
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm test # run all tests (vitest)
|
|
18
|
+
pnpm test -- --run test/Vanjs # run specific test file
|
|
19
|
+
pnpm build # build all entries via tsdown
|
|
20
|
+
pnpm lint # deno lint src
|
|
21
|
+
pnpm lint:types # tsc --noEmit (pre-existing baseUrl deprecation warning is safe to ignore)
|
|
22
|
+
pnpm fix:ts # deno lint src --fix
|
|
23
|
+
pnpm format # deno fmt src
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Architecture
|
|
27
|
+
|
|
28
|
+
### Core Engine
|
|
29
|
+
|
|
30
|
+
- **Tween** (`src/Tween.ts`) — single-property or multi-property tween. Chainable API: `.to()`, `.from()`, `.duration()`, `.easing()`, `.start()`, `.stop()`, `.pause()`, `.reverse()`
|
|
31
|
+
- **Timeline** (`src/Timeline.ts`) — sequences multiple tweens with labels and offsets. API: `.to()`, `.play()`, `.pause()`, `.seek()`, `.stop()`
|
|
32
|
+
- **Runtime** (`src/Runtime.ts`) — auto-managed RAF loop, no manual update loop needed
|
|
33
|
+
- **Easing** (`src/Easing.ts`) — cubic-bezier and preset easing functions
|
|
34
|
+
|
|
35
|
+
### Framework Integration Pattern
|
|
36
|
+
|
|
37
|
+
Each framework integration follows the same structure:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
src/<framework>/
|
|
41
|
+
index.ts # createTween/createTimeline or useTween/useTimeline primitives
|
|
42
|
+
miniStore.ts # reactive store wrapping each property with framework-specific state primitive
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Common pattern**:
|
|
46
|
+
1. `miniStore(init)` creates a proxy object where each property getter/setter wraps a framework-specific state primitive
|
|
47
|
+
2. `createTween(initialValues)` / `createTimeline(initialValues)` returns `[store, tween/timeline]` tuple
|
|
48
|
+
3. Tween mutates store properties → framework reactivity triggers updates
|
|
49
|
+
4. Cleanup: framework-specific (useEffect, onCleanup, MutationObserver, etc.)
|
|
50
|
+
|
|
51
|
+
### VanJS Integration (latest)
|
|
52
|
+
|
|
53
|
+
- **`src/vanjs/lifecycle.ts`** — global MutationObserver + session-based state tracking
|
|
54
|
+
- `vanState(initial)` — wrapper around `van.state()` that collects states during a session
|
|
55
|
+
- `nextId()` → returns session ID, `mount(tween, id)` → registers instance
|
|
56
|
+
- `checkRemovedBindings()` — iterates tracked instances, stops tweens whose DOM bindings are all disconnected
|
|
57
|
+
- **`src/vanjs/miniStore.ts`** — uses `vanState()` instead of raw `van.state()`, stores State refs in `_states` property
|
|
58
|
+
- Auto-cleanup via MutationObserver on `document.body` (childList + subtree)
|
|
59
|
+
- SSR-safe: returns `dummyInstance` when `isServer` is true
|
|
60
|
+
|
|
61
|
+
### miniStore Design
|
|
62
|
+
|
|
63
|
+
Each framework's miniStore adapts to its reactivity model:
|
|
64
|
+
|
|
65
|
+
| Framework | State primitive | Update trigger |
|
|
66
|
+
|-----------|----------------|----------------|
|
|
67
|
+
| React | `@preact/signals-react` signal | Signal subscription |
|
|
68
|
+
| Preact | `useState` + version toggle | `setVersion(v => v + 1)` |
|
|
69
|
+
| Solid | `createSignal()` | Signal getter access |
|
|
70
|
+
| Svelte | `$state()` | Svelte 5 reactivity |
|
|
71
|
+
| Vue | `ref()` | Vue reactivity |
|
|
72
|
+
| VanJS | `van.state()` | `_bindings` tracking via MutationObserver |
|
|
73
|
+
|
|
74
|
+
Arrays use a **version-toggle trick**: a hidden version signal flips on last-element mutation, forcing reactivity without replacing the entire array.
|
|
75
|
+
|
|
76
|
+
## Testing Conventions
|
|
77
|
+
|
|
78
|
+
- Test files: `test/<Framework>.spec.ts` / `test/<Framework>.spec.tsx`
|
|
79
|
+
- SSR tests: `test/<Framework>-ssr.spec.ts` with `// @vitest-environment node`
|
|
80
|
+
- Test fixtures: `test/fixtures/<framework>.setup.{ts,tsx}` — provides `withHook()` and optionally `withDomBinding()`
|
|
81
|
+
- Use `vi.useFakeTimers()` / `vi.advanceTimersByTime()` for animation timing
|
|
82
|
+
- Use `vi.spyOn(Tween.prototype, 'stop')` for cleanup verification
|
|
83
|
+
- VanJS auto-cleanup tests use real timers (`vi.useRealTimers()`) since MutationObserver needs actual DOM events
|
|
84
|
+
|
|
85
|
+
## Build Configuration
|
|
86
|
+
|
|
87
|
+
- **`tsdown.config.ts`** — one config per entry point
|
|
88
|
+
- `neverBundle` list: all framework dependencies that should remain external
|
|
89
|
+
- Each entry outputs to `dist/<framework>/<framework>.mjs`
|
|
90
|
+
- Svelte output: `dist/svelte/tween.svelte.js` (renamed post-build)
|
|
91
|
+
|
|
92
|
+
## Adding a New Framework Integration
|
|
93
|
+
|
|
94
|
+
1. `pnpm add <framework-package>`
|
|
95
|
+
2. Create `src/<framework>/miniStore.ts` — reactive store using framework's state primitive
|
|
96
|
+
3. Create `src/<framework>/index.ts` — `createTween`/`createTimeline` or `useTween`/`useTimeline`
|
|
97
|
+
4. Add `"./<framework>": "./dist/<framework>/<framework>.mjs"` to `package.json` exports
|
|
98
|
+
5. Add build entry to `tsdown.config.ts`
|
|
99
|
+
6. Add framework to `neverBundle` in tsdown config
|
|
100
|
+
7. Create `test/fixtures/<framework>.setup.ts`
|
|
101
|
+
8. Create `test/<Framework>.spec.ts` and `test/<Framework>-ssr.spec.ts`
|
|
102
|
+
9. Create `playground/<framework>/` Vite app
|
|
103
|
+
10. Add `"vanjs"` to keywords in `package.json`
|
|
104
|
+
11. Create `wiki/<Framework>.md`
|
|
105
|
+
|
|
106
|
+
## Gotchas
|
|
107
|
+
|
|
108
|
+
- **Never chain `.to()`, `.duration()` etc. in component body** without safeguards — React/Preact re-renders will create duplicate tweens
|
|
109
|
+
- **Tween/Timeline initialProps is mandatory** — cannot create with empty object
|
|
110
|
+
- **`_bindings` is a VanJS internal property** — not part of public API, could change
|
|
111
|
+
- **playground dirs have their own node_modules** — excluded from type checking
|
|
112
|
+
- **pre-existing type errors** in playground files (JSX, CSS imports) are expected and safe to ignore
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.0.6] - 2026-04-06
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- VanJS integration with `createTween` and `createTimeline` primitives
|
|
9
|
+
- VanJS auto-cleanup via global MutationObserver and session-based state tracking
|
|
10
|
+
- VanJS playground and test suite
|
|
11
|
+
- `pnpm-workspace.yaml` for workspace management
|
|
12
|
+
- `pnpm fix:ts` command for auto-fixing lint issues
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Updated dependencies across all playgrounds
|
|
16
|
+
- Improved `Tween.ts` and `Util.ts` internals
|
|
17
|
+
- Extended `path.ts` extension functionality
|
|
18
|
+
- Updated `preact/miniStore.ts` and `react/miniStore.ts`
|
|
19
|
+
- Enhanced test fixtures for preact setup
|
|
20
|
+
- Updated `tsdown.config.ts` build configuration
|
|
21
|
+
- Updated `tsconfig.json`
|
|
22
|
+
- Updated Timeline and Tween wiki documentation
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
- `.npmignore` (replaced by `files` field in package.json)
|
|
26
|
+
|
|
27
|
+
## [0.0.5] - 2026-02-19
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- Svelte integration with `useTween` hook
|
|
31
|
+
- Preact integration with `useTween` hook
|
|
32
|
+
- Vue integration with `useTween` composable
|
|
33
|
+
- SSR compatibility for all framework hooks
|
|
34
|
+
- Demo links for all supported frameworks
|
|
35
|
+
- More wiki pages for framework-specific documentation
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
- Updated dependencies
|
|
39
|
+
- Updated playground configurations
|
|
40
|
+
- Improved miniStore implementation
|
|
41
|
+
- Updated tests instrumentation
|
|
42
|
+
- Fixed TypeScript issues
|
|
43
|
+
- Fixed prototype pollution vulnerabilities (CodeQL alerts #1, #2, #3)
|
|
44
|
+
- Added `objectHasProp` utility, fixed `deepAssign`
|
|
45
|
+
|
|
46
|
+
## [0.0.4] - 2026-01-31
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- React integration with `useTween` hook
|
|
50
|
+
- SolidJS integration with `createTween` primitive
|
|
51
|
+
- Playground for React and SolidJS
|
|
52
|
+
- Publish workflow
|
|
53
|
+
- CodeQL security scanning
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
- Fixed JSDocs
|
|
57
|
+
- Updated README and wiki
|
|
58
|
+
- Updated dependencies
|
|
59
|
+
- Version bump
|
|
60
|
+
|
|
61
|
+
## [0.0.3] - 2026-01-26
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
- `Easing.ts` with cubic-bezier and preset easing functions
|
|
65
|
+
- `extend/` directory for array, object, path, transform interpolation
|
|
66
|
+
- Wiki pages for Tween, Timeline, Easing, and Extend
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
- Fixed types
|
|
70
|
+
- Code formatting
|
|
71
|
+
- Typos fixed
|
|
72
|
+
- Updated description
|
|
73
|
+
|
|
74
|
+
## [0.0.2] - 2026-01-25
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
- `Timeline.ts` for sequencing multiple tweens
|
|
78
|
+
- `Runtime.ts` for auto-managed RAF loop
|
|
79
|
+
- `Now.ts` for time utilities
|
|
80
|
+
- `Version.ts` for version tracking
|
|
81
|
+
- `types.d.ts` for TypeScript definitions
|
|
82
|
+
- Initial test suite with Vitest + happy-dom
|
|
83
|
+
- Playground directory
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
- Initial commit structure refined
|
|
87
|
+
|
|
88
|
+
## [0.0.1] - 2026-01-25
|
|
89
|
+
|
|
90
|
+
### Added
|
|
91
|
+
- Initial release of `@thednp/tween`
|
|
92
|
+
- Core `Tween.ts` engine forked from [@tweenjs/tweenjs](https://github.com/tweenjs/tween.js)
|
|
93
|
+
- TypeScript-first architecture
|
|
94
|
+
- Chainable API: `.to()`, `.from()`, `.duration()`, `.easing()`, `.start()`, `.stop()`, `.pause()`, `.reverse()`
|
|
95
|
+
- Build configuration with tsdown
|
|
96
|
+
- Basic wiki documentation
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ Popular UI frameworks supported:
|
|
|
15
15
|
[<img width="32" height="32" src="wiki/assets/preact.svg" alt="Preact" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/preact)
|
|
16
16
|
[<img width="32" height="32" src="wiki/assets/svelte.svg" alt="Svelte" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/svelte)
|
|
17
17
|
[<img width="32" height="32" src="wiki/assets/vue.svg" alt="Vue" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/vue)
|
|
18
|
+
[<img width="32" height="32" src="wiki/assets/vanjs.svg" alt="VanJS" />](https://stackblitz.com/fork/github/thednp/tween/tree/master/playground/vanjs)
|
|
18
19
|
|
|
19
20
|
Your favorite framework isn't listed? [Let us know](https://github.com/thednp/tween/issues/new)!
|
|
20
21
|
|
|
@@ -55,6 +56,7 @@ Your favorite framework isn't listed? [Let us know](https://github.com/thednp/tw
|
|
|
55
56
|
[<img width="32" height="32" src="wiki/assets/preact.svg" alt="Preact" />](wiki/Preact.md)
|
|
56
57
|
[<img width="32" height="32" src="wiki/assets/svelte.svg" alt="Svelte" />](wiki/Svelte.md)
|
|
57
58
|
[<img width="32" height="32" src="wiki/assets/vue.svg" alt="Vue" />](wiki/Vue.md)
|
|
59
|
+
[<img width="32" height="32" src="wiki/assets/vanjs.svg" alt="VanJS" />](wiki/VanJS.md)
|
|
58
60
|
|
|
59
61
|
#### Other Sources
|
|
60
62
|
* [The original Tween.js User Guide](https://github.com/tweenjs/tween.js/blob/main/docs/user_guide.md) can also provide valuable tips.
|
package/dist/preact/preact.d.mts
CHANGED
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.1.0 (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
|
*/
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
import { useEffect, useRef, useState } from "preact/hooks";
|
|
9
9
|
import { Timeline, Tween, dummyInstance, isArray, isPlainObject, isServer, objectHasProp } from "@thednp/tween";
|
|
10
|
-
|
|
11
10
|
//#region src/preact/miniStore.ts
|
|
12
11
|
const STATE_PROXY = "_proxy";
|
|
13
12
|
const proxyProps = {
|
|
@@ -101,10 +100,9 @@ function useMiniStore(initialValue) {
|
|
|
101
100
|
const storeRef = useRef(null);
|
|
102
101
|
const [, setVersion] = useState(0);
|
|
103
102
|
if (!storeRef.current) storeRef.current = miniStore(initialValue);
|
|
104
|
-
useEffect(() => storeRef.current.subscribe(() => setVersion((v) => v
|
|
103
|
+
useEffect(() => storeRef.current.subscribe(() => setVersion((v) => (v + 1) % 3)), []);
|
|
105
104
|
return storeRef.current.state;
|
|
106
105
|
}
|
|
107
|
-
|
|
108
106
|
//#endregion
|
|
109
107
|
//#region src/preact/index.ts
|
|
110
108
|
/**
|
|
@@ -175,7 +173,7 @@ function useTimeline(initialValues) {
|
|
|
175
173
|
useEffect(() => dispose, []);
|
|
176
174
|
return [store, timelineRef.current];
|
|
177
175
|
}
|
|
178
|
-
|
|
179
176
|
//#endregion
|
|
180
177
|
export { Timeline, Tween, useMiniStore, useTimeline, useTween };
|
|
178
|
+
|
|
181
179
|
//# sourceMappingURL=preact.mjs.map
|
|
@@ -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 ()
|
|
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 () => storeRef.current!.subscribe(() => setVersion((v) => (v + 1) % 3)),\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
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.1.0 (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
|
*/
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
import { useEffect, useRef, useState } from "react";
|
|
9
9
|
import { Timeline, Tween, dummyInstance, isArray, isPlainObject, isServer, objectHasProp } from "@thednp/tween";
|
|
10
|
-
|
|
11
10
|
//#region src/react/miniStore.ts
|
|
12
11
|
const STATE_PROXY = "_proxy";
|
|
13
12
|
const proxyProps = {
|
|
@@ -101,10 +100,9 @@ function useMiniStore(initialValue) {
|
|
|
101
100
|
const storeRef = useRef(null);
|
|
102
101
|
const [, setVersion] = useState(0);
|
|
103
102
|
if (!storeRef.current) storeRef.current = miniStore(initialValue);
|
|
104
|
-
useEffect(() => storeRef.current.subscribe(() => setVersion((v) => v
|
|
103
|
+
useEffect(() => storeRef.current.subscribe(() => setVersion((v) => (v + 1) % 3)), []);
|
|
105
104
|
return storeRef.current.state;
|
|
106
105
|
}
|
|
107
|
-
|
|
108
106
|
//#endregion
|
|
109
107
|
//#region src/react/index.ts
|
|
110
108
|
/**
|
|
@@ -177,7 +175,7 @@ function useTimeline(initialValues) {
|
|
|
177
175
|
}, []);
|
|
178
176
|
return [state, timelineRef.current];
|
|
179
177
|
}
|
|
180
|
-
|
|
181
178
|
//#endregion
|
|
182
179
|
export { Timeline, Tween, useMiniStore, useTimeline, useTween };
|
|
180
|
+
|
|
183
181
|
//# sourceMappingURL=react.mjs.map
|
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 ()
|
|
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 () => storeRef.current!.subscribe(() => setVersion((v) => (v + 1) % 3)),\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.1.0 (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
|
*/
|
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.1.0 (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
|
*/
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
import { Timeline, Tween, dummyInstance, isArray, isPlainObject, isServer, objectHasProp } from "@thednp/tween";
|
|
9
9
|
import { createSignal, onCleanup } from "solid-js";
|
|
10
|
-
|
|
11
10
|
//#region src/solid/miniStore.ts
|
|
12
11
|
const STATE_PROXY = "_proxy";
|
|
13
12
|
const proxyProps = {
|
|
@@ -86,7 +85,6 @@ function createMiniState(obj, parentReceiver) {
|
|
|
86
85
|
function miniStore(init) {
|
|
87
86
|
return createMiniState(init, {});
|
|
88
87
|
}
|
|
89
|
-
|
|
90
88
|
//#endregion
|
|
91
89
|
//#region src/solid/index.ts
|
|
92
90
|
/**
|
|
@@ -151,7 +149,7 @@ function createTimeline(initialValues) {
|
|
|
151
149
|
});
|
|
152
150
|
return [store, timeline];
|
|
153
151
|
}
|
|
154
|
-
|
|
155
152
|
//#endregion
|
|
156
153
|
export { Timeline, Tween, createTimeline, createTween, miniStore };
|
|
154
|
+
|
|
157
155
|
//# sourceMappingURL=solid.mjs.map
|
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.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":"
|
|
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,CACrB,gBAA8B,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.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":"
|
|
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,CACrB,gBAA8B,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.1.0 (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
|
*/
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
import { Timeline, Tween, dummyInstance, isArray, isPlainObject, isServer, objectHasProp } from "@thednp/tween";
|
|
9
9
|
import { onDestroy } from "svelte";
|
|
10
|
-
|
|
11
10
|
//#region src/svelte/miniStore.svelte.ts
|
|
12
11
|
const STATE_PROXY = "_proxy";
|
|
13
12
|
const proxyProps = {
|
|
@@ -87,7 +86,6 @@ function createMiniState(obj, parentReceiver) {
|
|
|
87
86
|
function miniStore(init) {
|
|
88
87
|
return createMiniState(init, {});
|
|
89
88
|
}
|
|
90
|
-
|
|
91
89
|
//#endregion
|
|
92
90
|
//#region src/svelte/index.svelte.ts
|
|
93
91
|
/**
|
|
@@ -150,7 +148,7 @@ function createTimeline(initialValues) {
|
|
|
150
148
|
});
|
|
151
149
|
return [store, timeline];
|
|
152
150
|
}
|
|
153
|
-
|
|
154
151
|
//#endregion
|
|
155
152
|
export { Timeline, Tween, createTimeline, createTween, miniStore };
|
|
153
|
+
|
|
156
154
|
//# sourceMappingURL=svelte.mjs.map
|
package/dist/tween/index.d.mts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @thednp/tween v0.0
|
|
2
|
+
* @thednp/tween v0.1.0 (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
|
*/
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
|
+
import { equalizePaths, equalizeSegments, pathToString } from "svg-path-commander/util";
|
|
9
|
+
|
|
8
10
|
//#region src/Tween.d.ts
|
|
9
11
|
/**
|
|
10
12
|
* Lightweight tween engine for interpolating values over time.
|
|
@@ -653,13 +655,6 @@ declare const isServer: boolean;
|
|
|
653
655
|
*/
|
|
654
656
|
declare const dummyInstance: Record<string, typeof dummyMethod>;
|
|
655
657
|
declare function dummyMethod(this: typeof dummyInstance): Record<string, typeof dummyMethod>;
|
|
656
|
-
/**
|
|
657
|
-
* Utility to round numbers to a specified number of decimals.
|
|
658
|
-
* @param n Input number value
|
|
659
|
-
* @param round Number of decimals
|
|
660
|
-
* @returns The rounded number
|
|
661
|
-
*/
|
|
662
|
-
declare const roundTo: (n: number, round: number) => number;
|
|
663
658
|
declare const objectHasProp: <T extends object>(obj: T, prop: keyof T) => boolean;
|
|
664
659
|
/**
|
|
665
660
|
* A small utility to deep assign up to one level deep nested objects.
|
|
@@ -729,15 +724,6 @@ declare const arrayConfig: {
|
|
|
729
724
|
};
|
|
730
725
|
//#endregion
|
|
731
726
|
//#region src/extend/path.d.ts
|
|
732
|
-
/**
|
|
733
|
-
* Iterates a `PathArray` value and concatenates the values into a string to return it.
|
|
734
|
-
*
|
|
735
|
-
* **NOTE**: Segment values are rounded to 4 decimals by default.
|
|
736
|
-
* @param path A source PathArray
|
|
737
|
-
* @param round An optional parameter to round segment values to a number of decimals
|
|
738
|
-
* @returns A valid HTML `description` (d) path string value
|
|
739
|
-
*/
|
|
740
|
-
declare function pathToString(path: MorphPathArray, round?: number): string;
|
|
741
727
|
/**
|
|
742
728
|
* Interpolate `PathArray` values.
|
|
743
729
|
*
|
|
@@ -954,5 +940,5 @@ declare function removeFromQueue<T extends TweenProps>(removedItem: AnimationIte
|
|
|
954
940
|
//#region package.json.d.ts
|
|
955
941
|
declare let version: string;
|
|
956
942
|
//#endregion
|
|
957
|
-
export { AnimationItem, ArrayVal, BaseTweenProps, CubicValues, DeepObject, DeepPartial, Easing, EasingFunction, EasingFunctionGroup, InterpolatorFunction, LineValues, MorphPathArray, MorphPathSegment, PathLike, Position, PropConfig, QuadValues, Queue, Rotate, RotateAxisAngle, RotateZ, Runtime, Scale, Timeline, TimelineCallback, TimelineEntry, TimelineEntryConfig, TransformArray, TransformLike, TransformStep, TransformStepInternal, Translate, Tween, TweenCallback, TweenProps, TweenRuntime, TweenUpdateCallback, ValidationFunction, ValidationResultEntry, Vec3, addToQueue, arrayConfig, deepAssign, deproxy, dummyInstance, eulerToAxisAngle, interpolateArray, interpolateObject, interpolatePath, interpolateTransform, isArray, isDeepObject, isFunction, isNumber, isObject, isPathLike, isPlainObject, isServer, isString, isTransformLike, isValidArray, isValidPath, isValidTransformArray, now, objectConfig, objectHasProp, pathArrayConfig, pathToString, removeFromQueue,
|
|
943
|
+
export { AnimationItem, ArrayVal, BaseTweenProps, CubicValues, DeepObject, DeepPartial, Easing, EasingFunction, EasingFunctionGroup, InterpolatorFunction, LineValues, MorphPathArray, MorphPathSegment, PathLike, Position, PropConfig, QuadValues, Queue, Rotate, RotateAxisAngle, RotateZ, Runtime, Scale, Timeline, TimelineCallback, TimelineEntry, TimelineEntryConfig, TransformArray, TransformLike, TransformStep, TransformStepInternal, Translate, Tween, TweenCallback, TweenProps, TweenRuntime, TweenUpdateCallback, ValidationFunction, ValidationResultEntry, Vec3, addToQueue, arrayConfig, deepAssign, deproxy, dummyInstance, equalizePaths, equalizeSegments, eulerToAxisAngle, interpolateArray, interpolateObject, interpolatePath, interpolateTransform, isArray, isDeepObject, isFunction, isNumber, isObject, isPathLike, isPlainObject, isServer, isString, isTransformLike, isValidArray, isValidPath, isValidTransformArray, now, objectConfig, objectHasProp, pathArrayConfig, pathToString, removeFromQueue, setNow, transformConfig, transformToString, validateArray, validateObject, validatePath, validateTransform, validateValues, version };
|
|
958
944
|
//# sourceMappingURL=index.d.mts.map
|