coily 0.12.2 → 0.14.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/dist/vue.js CHANGED
@@ -1,46 +1,79 @@
1
- import { n as SpringConfig, t as createSpringSystem } from "./system-Bzrx4uso.js";
2
- import { computed, customRef, defineComponent, getCurrentScope, inject, onBeforeUnmount, onMounted, onScopeDispose, provide, toRefs, toValue, watchSyncEffect } from "vue";
1
+ import { a as SpringDefinition, n as SpringSourceSymbol, r as isSpringSource, t as createSpringSystem } from "./system-BXxAHM2a.js";
2
+ import { computed, customRef, defineComponent, getCurrentInstance, getCurrentScope, inject, onBeforeUnmount, onMounted, onScopeDispose, provide, toRefs, toValue, watchSyncEffect } from "vue";
3
+ //#region src/vue/local.ts
4
+ const localProvides = /* @__PURE__ */ new WeakMap();
5
+ function provideLocal(key, value) {
6
+ const instance = getCurrentInstance();
7
+ if (instance) {
8
+ let values = localProvides.get(instance);
9
+ if (!values) {
10
+ values = /* @__PURE__ */ new Map();
11
+ localProvides.set(instance, values);
12
+ }
13
+ values.set(key, value);
14
+ }
15
+ provide(key, value);
16
+ }
17
+ function injectLocal(key) {
18
+ const instance = getCurrentInstance();
19
+ if (instance) {
20
+ const values = localProvides.get(instance);
21
+ if (values?.has(key)) return values.get(key);
22
+ }
23
+ return inject(key, void 0);
24
+ }
25
+ //#endregion
3
26
  //#region src/vue/system.ts
4
27
  const SpringSystemKey = Symbol.for("coily/spring-system");
28
+ /**
29
+ * Supplies a spring system you created and manage yourself — coily never
30
+ * starts or stops it. With `app`, the system is installed app-wide
31
+ * (what the Nuxt module does); without, it is provided to the current
32
+ * component and its descendants, including this component's own
33
+ * `useSpring` calls. Mostly useful in tests.
34
+ */
5
35
  function provideSpringSystem(system, app) {
6
36
  if (app) app.provide(SpringSystemKey, system);
7
- else provide(SpringSystemKey, system);
37
+ else provideLocal(SpringSystemKey, system);
8
38
  }
39
+ /**
40
+ * Returns the spring system provided by this component or an ancestor,
41
+ * creating one when none exists. A created system is provided to this
42
+ * component and its descendants and started/stopped with the component's
43
+ * mount lifecycle.
44
+ *
45
+ * Idempotent: call it near the root and again in any descendant.
46
+ * `options` only applies when the call actually creates a system.
47
+ * Throws outside `setup()`.
48
+ */
9
49
  function useSpringSystem(options) {
50
+ if (!getCurrentInstance()) throw new Error("useSpringSystem must be called inside setup()");
51
+ const existing = injectLocal(SpringSystemKey);
52
+ if (existing) return existing;
10
53
  const system = createSpringSystem(options);
11
- provideSpringSystem(system);
12
- onMounted(() => {
13
- system.start();
14
- });
15
- onBeforeUnmount(() => {
16
- system.stop();
17
- });
54
+ provideLocal(SpringSystemKey, system);
55
+ onMounted(() => system.start());
56
+ onBeforeUnmount(() => system.stop());
57
+ return system;
18
58
  }
19
59
  //#endregion
20
- //#region src/vue/spring.ts
21
- /** @internal Symbol to access the underlying Spring from a SpringRef */
22
- const SpringInstanceKey = Symbol("spring");
23
- function hasSpringInstance(value) {
24
- return typeof value === "object" && value !== null && SpringInstanceKey in value;
25
- }
26
- function useSpring(target, options) {
27
- if (Array.isArray(target)) return Array.from(target, (t) => createSpringRef(t, options));
28
- if (hasSpringInstance(target)) return createLinkedSpringRef(target, options);
29
- return createSpringRef(target, options);
60
+ //#region src/vue/reactive-spring.ts
61
+ const divergentRef = customRef;
62
+ function injectSpringSystem() {
63
+ const system = injectLocal(SpringSystemKey);
64
+ if (!system) throw new Error("No SpringSystem found — install the coily/nuxt module or call useSpringSystem() in an ancestor component");
65
+ return system;
30
66
  }
31
- function createSpringRef(target, options) {
32
- const system = inject(SpringSystemKey);
33
- if (!system) throw new Error("No SpringSystem found");
34
- const config = computed(() => {
35
- const opts = toValue(options);
36
- if (opts instanceof SpringConfig) return opts;
37
- return opts ? new SpringConfig(opts) : void 0;
67
+ function resolveSpringConfig(config) {
68
+ return computed(() => {
69
+ const resolved = toValue(config);
70
+ if (resolved instanceof SpringDefinition) return resolved;
71
+ return resolved ? new SpringDefinition(resolved) : void 0;
38
72
  });
39
- const spring = system.createSpring(toValue(target), config.value ?? void 0);
73
+ }
74
+ function createReactiveSpringRef(spring, config, instanceKey) {
40
75
  watchSyncEffect(() => {
41
- const c = config.value;
42
- if (c) spring.config = c;
43
- else spring.config = null;
76
+ spring.config = config.value ?? null;
44
77
  });
45
78
  let triggerValue;
46
79
  let triggerVelocity;
@@ -50,25 +83,25 @@ function createSpringRef(target, options) {
50
83
  triggerVelocity?.();
51
84
  triggerTimeRemaining?.();
52
85
  });
53
- const value = customRef((track, trigger) => ({
86
+ const value = divergentRef((track, trigger) => ({
54
87
  get() {
55
88
  triggerValue ??= trigger;
56
89
  track();
57
90
  return spring.value;
58
91
  },
59
- set(value) {
60
- spring.value = value;
92
+ set(next) {
93
+ spring.value = next;
61
94
  trigger();
62
95
  }
63
96
  }));
64
- const velocity = customRef((track, trigger) => ({
97
+ const velocity = divergentRef((track, trigger) => ({
65
98
  get() {
66
99
  triggerVelocity ??= trigger;
67
100
  track();
68
101
  return spring.velocity;
69
102
  },
70
- set(value) {
71
- spring.velocity = value;
103
+ set(next) {
104
+ spring.velocity = next;
72
105
  trigger();
73
106
  }
74
107
  }));
@@ -91,262 +124,121 @@ function createSpringRef(target, options) {
91
124
  set() {}
92
125
  };
93
126
  });
94
- watchSyncEffect(() => {
95
- spring.target = toValue(target);
96
- });
97
127
  if (getCurrentScope()) onScopeDispose(() => spring.dispose());
98
128
  const ref = Object.assign(value, {
99
129
  velocity,
100
130
  timeRemaining,
101
131
  isResting,
102
- jumpTo: (value) => spring.jumpTo(value)
132
+ jumpTo: (next) => spring.jumpTo(next)
103
133
  });
104
- Object.defineProperty(ref, SpringInstanceKey, { value: spring });
134
+ Object.defineProperty(ref, "settled", { get: () => spring.settled });
135
+ Object.defineProperty(ref, instanceKey, { value: spring });
136
+ Object.defineProperty(ref, SpringSourceSymbol, { value: spring[SpringSourceSymbol] });
105
137
  return ref;
106
138
  }
107
- function createLinkedSpringRef(leaderRef, options) {
108
- const system = inject(SpringSystemKey);
109
- if (!system) throw new Error("No SpringSystem found");
110
- const leaderSpring = leaderRef[SpringInstanceKey];
111
- const config = computed(() => {
112
- const opts = toValue(options);
113
- if (!opts) return void 0;
114
- if (opts instanceof SpringConfig) return opts;
115
- return new SpringConfig(opts);
116
- });
117
- const spring = system.createSpring({ target: leaderSpring }, config.value);
139
+ //#endregion
140
+ //#region src/vue/composite-spring.ts
141
+ const CompositeSpringInstanceKey = Symbol("composite-spring");
142
+ function hasCompositeSpringInstance(value) {
143
+ return typeof value === "object" && value !== null && CompositeSpringInstanceKey in value;
144
+ }
145
+ function resolveConfigShape(config) {
146
+ return computed(() => toValue(config));
147
+ }
148
+ function createCompositeSpringRef(value, config, options) {
149
+ const system = injectSpringSystem();
150
+ const resolvedConfig = resolveConfigShape(config);
151
+ const spring = system.createSpring(toValue(value), resolvedConfig.value, options);
152
+ const ref = createReactiveSpringRef(spring, resolvedConfig, CompositeSpringInstanceKey);
118
153
  watchSyncEffect(() => {
119
- const c = config.value;
120
- if (c) spring.config = c;
121
- else spring.config = null;
122
- });
123
- let triggerValue;
124
- let triggerVelocity;
125
- let triggerTimeRemaining;
126
- spring.onUpdate(() => {
127
- triggerValue?.();
128
- triggerVelocity?.();
129
- triggerTimeRemaining?.();
130
- });
131
- const value = customRef((track, trigger) => ({
132
- get() {
133
- triggerValue ??= trigger;
134
- track();
135
- return spring.value;
136
- },
137
- set(value) {
138
- spring.value = value;
139
- trigger();
140
- }
141
- }));
142
- const velocity = customRef((track, trigger) => ({
143
- get() {
144
- triggerVelocity ??= trigger;
145
- track();
146
- return spring.velocity;
147
- },
148
- set(value) {
149
- spring.velocity = value;
150
- trigger();
151
- }
152
- }));
153
- const timeRemaining = customRef((track, trigger) => ({
154
- get() {
155
- triggerTimeRemaining ??= trigger;
156
- track();
157
- return spring.timeRemaining;
158
- },
159
- set() {}
160
- }));
161
- const isResting = customRef((track, trigger) => {
162
- spring.onStart(trigger);
163
- spring.onStop(trigger);
164
- return {
165
- get() {
166
- track();
167
- return spring.isResting;
168
- },
169
- set() {}
170
- };
171
- });
172
- if (getCurrentScope()) onScopeDispose(() => spring.dispose());
173
- const ref = Object.assign(value, {
174
- velocity,
175
- timeRemaining,
176
- isResting,
177
- jumpTo: (value) => spring.jumpTo(value)
154
+ spring.target = toValue(value);
178
155
  });
179
- Object.defineProperty(ref, SpringInstanceKey, { value: spring });
180
156
  return ref;
181
157
  }
158
+ function createLinkedCompositeSpringRef(leaderRef, config, options) {
159
+ const system = injectSpringSystem();
160
+ const leader = leaderRef[CompositeSpringInstanceKey];
161
+ const resolvedConfig = resolveConfigShape(config);
162
+ const spring = system.createSpring(leader.value, resolvedConfig.value, options);
163
+ spring.target = leader;
164
+ return createReactiveSpringRef(spring, resolvedConfig, CompositeSpringInstanceKey);
165
+ }
182
166
  //#endregion
183
- //#region src/vue/spring2d.ts
184
- /** @internal Symbol to access the underlying Spring2D from a SpringRef2D */
185
- const Spring2DInstanceKey = Symbol("spring2d");
186
- function hasSpring2DInstance(value) {
187
- return typeof value === "object" && value !== null && Spring2DInstanceKey in value;
167
+ //#region src/vue/spring.ts
168
+ const SpringInstanceKey = Symbol("spring");
169
+ function hasSpringInstance(value) {
170
+ return typeof value === "object" && value !== null && SpringInstanceKey in value;
188
171
  }
189
- function useSpring2D(target, options) {
190
- if (Array.isArray(target)) return Array.from(target, (t) => createSpringRef2D(t, options));
191
- if (hasSpring2DInstance(target)) return createLinkedSpringRef2D(target, options);
192
- return createSpringRef2D(target, options);
172
+ function useSpring(target, config, options) {
173
+ if (hasSpringInstance(target)) return createLinkedSpringRef(target, config, options);
174
+ if (hasCompositeSpringInstance(target)) return createLinkedCompositeSpringRef(target, config, options);
175
+ const resolved = toValue(target);
176
+ if (typeof resolved === "number" || isSpringSource(resolved)) return createSpringRef(target, config, options);
177
+ return createCompositeSpringRef(target, config, options);
193
178
  }
194
- function createSpringRef2D(target, options) {
195
- const system = inject(SpringSystemKey);
196
- if (!system) throw new Error("No SpringSystem found");
197
- const config = computed(() => {
198
- const opts = toValue(options);
199
- if (opts instanceof SpringConfig) return opts;
200
- return opts ? new SpringConfig(opts) : void 0;
201
- });
202
- const spring = system.createSpring2D(toValue(target), config.value ?? void 0);
203
- watchSyncEffect(() => {
204
- const c = config.value;
205
- if (c) spring.config = c;
206
- else spring.config = null;
207
- });
208
- let triggerValue;
209
- let triggerVelocity;
210
- let triggerTimeRemaining;
211
- spring.onUpdate(() => {
212
- triggerValue?.();
213
- triggerVelocity?.();
214
- triggerTimeRemaining?.();
215
- });
216
- const value = customRef((track, trigger) => ({
217
- get() {
218
- triggerValue ??= trigger;
219
- track();
220
- return spring.value;
221
- },
222
- set(value) {
223
- spring.value = value;
224
- trigger();
225
- }
226
- }));
227
- const velocity = customRef((track, trigger) => ({
228
- get() {
229
- triggerVelocity ??= trigger;
230
- track();
231
- return spring.velocity;
232
- },
233
- set(value) {
234
- spring.velocity = value;
235
- trigger();
236
- }
237
- }));
238
- const timeRemaining = customRef((track, trigger) => ({
239
- get() {
240
- triggerTimeRemaining ??= trigger;
241
- track();
242
- return spring.timeRemaining;
243
- },
244
- set() {}
245
- }));
246
- const isResting = customRef((track, trigger) => {
247
- spring.onStart(trigger);
248
- spring.onStop(trigger);
249
- return {
250
- get() {
251
- track();
252
- return spring.isResting;
253
- },
254
- set() {}
255
- };
256
- });
179
+ function createSpringRef(target, config, options) {
180
+ const system = injectSpringSystem();
181
+ const resolvedConfig = resolveSpringConfig(config);
182
+ const initial = toValue(target);
183
+ const spring = system.createSpring(typeof initial === "number" ? initial : initial[SpringSourceSymbol].value, resolvedConfig.value, options);
184
+ const ref = createReactiveSpringRef(spring, resolvedConfig, SpringInstanceKey);
257
185
  watchSyncEffect(() => {
258
186
  spring.target = toValue(target);
259
187
  });
260
- if (getCurrentScope()) onScopeDispose(() => spring.dispose());
261
- const ref = Object.assign(value, {
262
- velocity,
263
- timeRemaining,
264
- isResting,
265
- jumpTo: (value) => spring.jumpTo(value)
266
- });
267
- Object.defineProperty(ref, Spring2DInstanceKey, { value: spring });
268
188
  return ref;
269
189
  }
270
- function createLinkedSpringRef2D(leaderRef, options) {
271
- const system = inject(SpringSystemKey);
272
- if (!system) throw new Error("No SpringSystem found");
273
- const leaderSpring = leaderRef[Spring2DInstanceKey];
274
- const config = computed(() => {
275
- const opts = toValue(options);
276
- if (!opts) return void 0;
277
- if (opts instanceof SpringConfig) return opts;
278
- return new SpringConfig(opts);
279
- });
280
- const spring = system.createSpring2D({ target: leaderSpring }, config.value);
281
- watchSyncEffect(() => {
282
- const c = config.value;
283
- if (c) spring.config = c;
284
- else spring.config = null;
285
- });
286
- let triggerValue;
287
- let triggerVelocity;
288
- let triggerTimeRemaining;
289
- spring.onUpdate(() => {
290
- triggerValue?.();
291
- triggerVelocity?.();
292
- triggerTimeRemaining?.();
293
- });
294
- const value = customRef((track, trigger) => ({
295
- get() {
296
- triggerValue ??= trigger;
297
- track();
298
- return spring.value;
299
- },
300
- set(value) {
301
- spring.value = value;
302
- trigger();
303
- }
304
- }));
305
- const velocity = customRef((track, trigger) => ({
306
- get() {
307
- triggerVelocity ??= trigger;
308
- track();
309
- return spring.velocity;
310
- },
311
- set(value) {
312
- spring.velocity = value;
313
- trigger();
314
- }
315
- }));
316
- const timeRemaining = customRef((track, trigger) => ({
317
- get() {
318
- triggerTimeRemaining ??= trigger;
319
- track();
320
- return spring.timeRemaining;
321
- },
322
- set() {}
323
- }));
324
- const isResting = customRef((track, trigger) => {
325
- spring.onStart(trigger);
326
- spring.onStop(trigger);
327
- return {
328
- get() {
329
- track();
330
- return spring.isResting;
331
- },
332
- set() {}
333
- };
334
- });
335
- if (getCurrentScope()) onScopeDispose(() => spring.dispose());
336
- const ref = Object.assign(value, {
337
- velocity,
338
- timeRemaining,
339
- isResting,
340
- jumpTo: (value) => spring.jumpTo(value)
341
- });
342
- Object.defineProperty(ref, Spring2DInstanceKey, { value: spring });
343
- return ref;
190
+ function createLinkedSpringRef(leaderRef, config, options) {
191
+ const system = injectSpringSystem();
192
+ const leader = leaderRef[SpringInstanceKey];
193
+ const resolvedConfig = resolveSpringConfig(config);
194
+ return createReactiveSpringRef(system.createSpring(leader, resolvedConfig.value, options), resolvedConfig, SpringInstanceKey);
195
+ }
196
+ //#endregion
197
+ //#region src/vue/pool.ts
198
+ /**
199
+ * Returns spring factories bound to the provided spring system, with
200
+ * lifetimes tied to the current effect scope: every pooled spring still
201
+ * alive at scope teardown is disposed, so leaked motions are
202
+ * structurally impossible.
203
+ *
204
+ * Call it during `setup()` — or inside an effect scope — below a
205
+ * provided spring system. Outside a scope, pooled springs are only
206
+ * disposed manually.
207
+ */
208
+ function useSpringPool() {
209
+ const system = injectSpringSystem();
210
+ const live = /* @__PURE__ */ new Set();
211
+ const adopt = (spring) => {
212
+ live.add(spring);
213
+ spring.onDispose(() => {
214
+ live.delete(spring);
215
+ });
216
+ return spring;
217
+ };
218
+ onScopeDispose(() => {
219
+ for (const spring of live) spring.dispose();
220
+ }, true);
221
+ const create = system.createSpring.bind(system);
222
+ const createSpring = ((value, config, options) => adopt(create(value, config, options)));
223
+ return { createSpring };
344
224
  }
345
225
  //#endregion
346
226
  //#region src/vue/component.ts
227
+ /**
228
+ * Renderless component exposing one springed number through its default
229
+ * slot — the template-only sibling of `useSpring`. Must sit below a
230
+ * provided spring system.
231
+ *
232
+ * @example
233
+ * ```vue
234
+ * <SpringValue :target="expanded ? 1 : 0" purpose="appearance" v-slot="{ value }">
235
+ * <div :style="{ opacity: value }" />
236
+ * </SpringValue>
237
+ * ```
238
+ */
347
239
  const SpringValue = defineComponent((props, { slots, expose }) => {
348
240
  const { target, config } = toRefs(props);
349
- const spring = useSpring(target, config);
241
+ const spring = useSpring(target, config, { purpose: props.purpose });
350
242
  expose({
351
243
  value: spring,
352
244
  velocity: spring.velocity,
@@ -363,7 +255,11 @@ const SpringValue = defineComponent((props, { slots, expose }) => {
363
255
  }) ?? null;
364
256
  }, {
365
257
  name: "SpringValue",
366
- props: ["target", "config"]
258
+ props: [
259
+ "target",
260
+ "config",
261
+ "purpose"
262
+ ]
367
263
  });
368
264
  //#endregion
369
- export { SpringValue, provideSpringSystem, useSpring, useSpring2D, useSpringSystem };
265
+ export { SpringValue, provideSpringSystem, useSpring, useSpringPool, useSpringSystem };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coily",
3
- "version": "0.12.2",
3
+ "version": "0.14.0",
4
4
  "description": "Simulate values using spring physics",
5
5
  "homepage": "https://github.com/tkofh/coily#readme",
6
6
  "bugs": {
@@ -27,22 +27,24 @@
27
27
  "access": "public"
28
28
  },
29
29
  "devDependencies": {
30
- "@nuxt/kit": "^4.4.2",
31
- "@nuxt/schema": "^4.4.2",
32
- "@vitejs/plugin-vue": "^6.0.5",
33
- "@vitest/browser-playwright": "^4.1.0",
34
- "@vitest/coverage-v8": "^4.1.0",
35
- "@vue/test-utils": "^2.4.6",
36
- "fast-check": "^4.6.0",
37
- "happy-dom": "^20.8.4",
38
- "tsdown": "^0.21.3",
39
- "vitest": "^4.1.0",
30
+ "@nuxt/kit": "^4.4.8",
31
+ "@nuxt/schema": "^4.4.8",
32
+ "@vitejs/plugin-vue": "^6.0.7",
33
+ "@vitest/browser-playwright": "^4.1.10",
34
+ "@vitest/coverage-v8": "^4.1.10",
35
+ "@vue/test-utils": "^2.4.11",
36
+ "fast-check": "^4.9.0",
37
+ "happy-dom": "^20.10.6",
38
+ "playwright": "1.61.1",
39
+ "tsdown": "^0.22.4",
40
+ "typescript": "^6.0.0",
41
+ "vitest": "^4.1.10",
40
42
  "vitest-browser-vue": "^2.1.0",
41
- "vue": "^3.5.30"
43
+ "vue": "^3.5.39"
42
44
  },
43
45
  "peerDependencies": {
44
46
  "@nuxt/kit": "^4.0.0",
45
- "vue": "^3.4.0"
47
+ "vue": "^3.5.0"
46
48
  },
47
49
  "peerDependenciesMeta": {
48
50
  "vue": {
@@ -52,11 +54,9 @@
52
54
  "optional": true
53
55
  }
54
56
  },
55
- "engines": {
56
- "node": ">=24.10.0"
57
- },
58
57
  "scripts": {
59
58
  "build": "tsdown",
59
+ "check": "tsc -p .",
60
60
  "test": "vitest run",
61
61
  "test:coverage": "vitest run --coverage",
62
62
  "bench": "vitest bench --run",