@rafaelaugusto/jolt 0.1.0-beta.14 → 0.1.0-beta.15

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/size.js ADDED
@@ -0,0 +1,251 @@
1
+ import { Application } from '@nativescript/core';
2
+ import { toView } from './animate.js';
3
+ import { onReducedMotion, reducedMotion } from './config.js';
4
+ import { getDriver } from './driver/index.js';
5
+ import { createClock } from './playback.js';
6
+ import { createTrack } from './track.js';
7
+ import { resolveTransition, springs, } from './transition.js';
8
+ import { dimension, sizeLayout, } from './size-layout.js';
9
+ const owners = new WeakMap();
10
+ export function animateSize(target, dimensions, changeOrOptions, options) {
11
+ const view = toView(target);
12
+ if (!dimensions || typeof dimensions !== 'object' || Array.isArray(dimensions))
13
+ throw new TypeError('animateSize takes a width/height object.');
14
+ const axes = Object.keys(dimensions);
15
+ if (!axes.length || axes.some((axis) => axis !== 'width' && axis !== 'height'))
16
+ throw new TypeError('animateSize needs width and/or height.');
17
+ for (const axis of axes)
18
+ dimension(dimensions[axis], axis);
19
+ const destination = { ...dimensions };
20
+ const change = typeof changeOrOptions === 'function' ? changeOrOptions : undefined;
21
+ if (change?.constructor.name === 'AsyncFunction')
22
+ throw new TypeError('animateSize changes must be synchronous.');
23
+ const transition = normalize(change ? options : changeOrOptions);
24
+ // Solve before ownership changes, including custom easing/physical-spring validation.
25
+ const validation = createTrack('x', [0, 100], transition, 0);
26
+ if (!Number.isFinite(validation.end) || !validation.values.every(Number.isFinite))
27
+ throw new RangeError('animateSize needs a finite curve with finite samples.');
28
+ const previous = owners.get(view);
29
+ const driver = getDriver();
30
+ const layout = sizeLayout(view, axes, previous?.axes, () => {
31
+ const frame = driver.layoutFrame?.(view);
32
+ return frame ? { width: frame.width, height: frame.height } : view.getActualSize();
33
+ });
34
+ // Release the previous overlay only after every caller-supplied input was validated.
35
+ const start = layout.read();
36
+ previous?.animation.stop({ restore: true });
37
+ let resolve;
38
+ let reject;
39
+ const finished = new Promise((done, fail) => {
40
+ resolve = done;
41
+ reject = fail;
42
+ });
43
+ let settled = false;
44
+ let paused = false;
45
+ let initialized = false;
46
+ let cancelFrame;
47
+ let unwatch;
48
+ let clock;
49
+ let tracks = new Map();
50
+ let endpoint;
51
+ const cleanup = () => {
52
+ cancelFrame?.();
53
+ cancelFrame = undefined;
54
+ clock?.cancel();
55
+ clock = undefined;
56
+ unwatch?.();
57
+ layout.dispose();
58
+ view.off('unloaded', unloaded);
59
+ Application.off(Application.suspendEvent, unloaded);
60
+ if (owners.get(view)?.animation === animation)
61
+ owners.delete(view);
62
+ };
63
+ const finish = (success, error) => {
64
+ if (settled)
65
+ return;
66
+ settled = true;
67
+ cleanup();
68
+ if (error !== undefined)
69
+ reject(error);
70
+ else
71
+ resolve(success);
72
+ };
73
+ const complete = () => {
74
+ layout.commit(destination);
75
+ layout.layout();
76
+ finish(true);
77
+ };
78
+ const stop = (restore = false) => {
79
+ if (settled)
80
+ return;
81
+ layout.commit(restore ? undefined : layout.read());
82
+ finish(false);
83
+ view.requestLayout();
84
+ };
85
+ function unloaded() {
86
+ stop(true);
87
+ }
88
+ const schedule = () => {
89
+ if (!cancelFrame && !settled && !paused)
90
+ cancelFrame = driver.frame(tick);
91
+ };
92
+ function tick() {
93
+ cancelFrame = undefined;
94
+ if (settled || paused)
95
+ return;
96
+ try {
97
+ if (!view.isLoaded || view.visibility === 'collapse' || !view.nativeViewProtected) {
98
+ stop(true);
99
+ return;
100
+ }
101
+ const next = layout.measure(destination);
102
+ if (settled)
103
+ return;
104
+ if (axes.some((axis) => !Number.isFinite(next[axis]) || next[axis] < 0))
105
+ throw new Error('animateSize measured an invalid native size.');
106
+ if (reducedMotion()) {
107
+ complete();
108
+ return;
109
+ }
110
+ if (!initialized &&
111
+ axes.every((axis) => Math.abs(next[axis] - start[axis]) < 0.5)) {
112
+ complete();
113
+ return;
114
+ }
115
+ const elapsed = clock?.time() ?? 0;
116
+ if (!endpoint ||
117
+ axes.some((axis) => Math.abs(next[axis] - endpoint[axis]) > 0.5)) {
118
+ const from = initialized ? layout.read() : start;
119
+ const fresh = new Map();
120
+ for (const axis of axes)
121
+ fresh.set(axis, createTrack('x', [from[axis], next[axis]], initialized ? { ...transition, delay: 0 } : transition, tracks.get(axis)?.velocityAt(elapsed) ?? 0));
122
+ tracks = fresh;
123
+ endpoint = next;
124
+ clock?.cancel();
125
+ const end = Math.max(...[...tracks.values()].map((track) => track.end));
126
+ clock = driver.clock?.(end) ?? createClock(() => driver.now());
127
+ initialized = true;
128
+ }
129
+ const time = clock.time();
130
+ if ([...tracks.values()].every((track) => time >= track.end)) {
131
+ complete();
132
+ return;
133
+ }
134
+ const values = {};
135
+ for (const [axis, track] of tracks)
136
+ values[axis] = Math.max(0, track.valueAt(time));
137
+ layout.write(values);
138
+ layout.layout();
139
+ if (settled)
140
+ return;
141
+ schedule();
142
+ }
143
+ catch (error) {
144
+ layout.commit();
145
+ view.requestLayout();
146
+ finish(false, error);
147
+ }
148
+ }
149
+ const animation = {
150
+ finished,
151
+ then: (done, fail) => finished.then(done, fail),
152
+ get paused() {
153
+ return paused;
154
+ },
155
+ stop: (options) => stop(options?.restore === true),
156
+ pause() {
157
+ if (settled || paused)
158
+ return;
159
+ paused = true;
160
+ clock?.pause();
161
+ cancelFrame?.();
162
+ cancelFrame = undefined;
163
+ },
164
+ play() {
165
+ if (settled || !paused)
166
+ return;
167
+ paused = false;
168
+ clock?.play();
169
+ schedule();
170
+ },
171
+ };
172
+ owners.set(view, { animation, axes });
173
+ try {
174
+ layout.watch(() => stop(true));
175
+ layout.write(start);
176
+ view.on('unloaded', unloaded);
177
+ Application.on(Application.suspendEvent, unloaded);
178
+ unwatch = onReducedMotion((reduced) => {
179
+ if (reduced && !settled) {
180
+ try {
181
+ complete();
182
+ }
183
+ catch (error) {
184
+ finish(false, error);
185
+ }
186
+ }
187
+ });
188
+ const result = change?.();
189
+ if (result && typeof result.then === 'function')
190
+ throw new TypeError('animateSize changes must be synchronous.');
191
+ if (!view.isLoaded || view.visibility === 'collapse' || !layout.ready()) {
192
+ layout.commit(destination);
193
+ finish(false);
194
+ }
195
+ else
196
+ schedule();
197
+ }
198
+ catch (error) {
199
+ stop(true);
200
+ throw error;
201
+ }
202
+ return animation;
203
+ }
204
+ function normalize(input) {
205
+ if (typeof input === 'string') {
206
+ if (!Object.hasOwn(springs, input))
207
+ throw new TypeError(`Unknown size spring "${input}".`);
208
+ input = springs[input];
209
+ }
210
+ if (input !== undefined &&
211
+ (!input || typeof input !== 'object' || Array.isArray(input)))
212
+ throw new TypeError('Invalid animateSize options.');
213
+ const allowed = new Set([
214
+ 'type',
215
+ 'duration',
216
+ 'ease',
217
+ 'stiffness',
218
+ 'damping',
219
+ 'mass',
220
+ 'bounce',
221
+ 'velocity',
222
+ 'delay',
223
+ ]);
224
+ for (const [key, value] of Object.entries(input ?? {})) {
225
+ if (!allowed.has(key))
226
+ throw new TypeError(`Unsupported animateSize option "${key}"; repeat and seek are not supported.`);
227
+ if (value === undefined)
228
+ continue;
229
+ if (key !== 'type' &&
230
+ key !== 'ease' &&
231
+ (typeof value !== 'number' || !Number.isFinite(value)))
232
+ throw new RangeError(`animateSize ${key} must be finite.`);
233
+ if (key === 'type' && value !== 'spring' && value !== 'tween')
234
+ throw new TypeError('Invalid size transition type.');
235
+ if (typeof value === 'number') {
236
+ if (['duration', 'delay', 'damping'].includes(key) && value < 0)
237
+ throw new RangeError(`${key} must be nonnegative.`);
238
+ if (['mass', 'stiffness'].includes(key) && value <= 0)
239
+ throw new RangeError(`${key} must be positive.`);
240
+ if (key === 'bounce' && (value < -1 || value > 1))
241
+ throw new RangeError('bounce must be between -1 and 1.');
242
+ }
243
+ }
244
+ const resolved = resolveTransition(input ?? {}, 'x', 2);
245
+ if (resolved.curve.type === 'spring' &&
246
+ input?.duration === 0 &&
247
+ !('stiffness' in input || 'damping' in input || 'mass' in input))
248
+ throw new RangeError('Use a zero-duration tween for immediate sizing.');
249
+ return resolved;
250
+ }
251
+ //# sourceMappingURL=size.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"size.js","sourceRoot":"","sources":["../src/size.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rafaelaugusto/jolt",
3
- "version": "0.1.0-beta.14",
3
+ "version": "0.1.0-beta.15",
4
4
  "description": "Native animations for NativeScript: presets, springs, keyframes and stagger in one call.",
5
5
  "keywords": [
6
6
  "nativescript",
package/src/index.ts CHANGED
@@ -104,3 +104,5 @@ export type {
104
104
 
105
105
  export { animateLayout } from './layout.js'
106
106
  export type { LayoutOptions } from './layout.js'
107
+ export { animateSize } from './size.js'
108
+ export type { SizeAnimation, SizeDimensions, SizeOptions } from './size.js'
@@ -108,6 +108,9 @@ declare class JoltPropertyPlayer extends NSObject {
108
108
  cancel(): void
109
109
  }
110
110
  declare class JoltReferenceProperties extends NSObject {
111
+ static sceneFieldOfView(camera: NSObject): JoltPropertySink
112
+ static sceneCameraSinkCount(): number
113
+ static sceneCameraWriteCount(): number
111
114
  static mapCoordinate(target: NSObject): JoltPropertySink
112
115
  static mapCamera(target: NSObject): JoltPropertySink
113
116
  static scenePose(target: NSObject): JoltPropertySink
@@ -3,6 +3,7 @@ import { press } from './press.js'
3
3
  import { drag } from './drag.js'
4
4
  import { gradient } from './gradient.js'
5
5
  import { animateLayout, type LayoutOptions } from './layout.js'
6
+ import { animateSize, type SizeDimensions, type SizeOptions } from './size.js'
6
7
  import type { SpringName } from './transition.js'
7
8
  import { sequence, timeline } from './sequence.js'
8
9
  import {
@@ -35,6 +36,7 @@ export interface MotionScope {
35
36
  /** Owned handles/subscriptions, including suspended subscription factories. */
36
37
  readonly size: number
37
38
  animateLayout: typeof animateLayout
39
+ animateSize: typeof animateSize
38
40
  animate: typeof animate
39
41
  decay: typeof decay
40
42
  sequence: typeof sequence
@@ -195,6 +197,18 @@ export function motionScope(): MotionScope {
195
197
  },
196
198
  animate: ((...args: Parameters<typeof animate>) =>
197
199
  startOwned(() => animate(...args))) as typeof animate,
200
+ animateSize(
201
+ target: Target,
202
+ dimensions: SizeDimensions,
203
+ changeOrOptions?: (() => void) | SizeOptions | SpringName,
204
+ options?: SizeOptions | SpringName,
205
+ ) {
206
+ return startOwned(() =>
207
+ typeof changeOrOptions === 'function'
208
+ ? animateSize(target, dimensions, changeOrOptions, options)
209
+ : animateSize(target, dimensions, changeOrOptions),
210
+ )
211
+ },
198
212
  decay(...args) {
199
213
  return startOwned(() => decay(...args))
200
214
  },
@@ -1,6 +1,7 @@
1
1
  export { registerNativeProperty, checkNativeProperty } from '../native-property.js'
2
2
  export type { NativeProperty, NativePropertyDefinition } from '../native-property.js'
3
3
  export type { NativeSink } from '../native-property-driver.js'
4
+ export { sceneKitFieldOfView } from '../scene-camera.js'
4
5
  export {
5
6
  mapKitCoordinate,
6
7
  mapKitCamera,
@@ -0,0 +1,30 @@
1
+ /// <reference types="@nativescript/types-ios/lib/ios/objc-x86_64/objc!SceneKit" preserve="true" />
2
+ import { registerNativeProperty, type NativeProperty } from './native-property.js'
3
+
4
+ /** Perspective SCNCamera field of view in degrees (1..179), on its projectionDirection axis. */
5
+ export const sceneKitFieldOfView: (camera: SCNCamera) => NativeProperty<number> =
6
+ registerNativeProperty<number>({
7
+ name: 'jolt.scenekit/field-of-view',
8
+ units: ['degrees on the camera projectionDirection axis'],
9
+ platforms: ['ios'],
10
+ spatial: true,
11
+ encode: (value) => [value],
12
+ decode: (values) => values[0]!,
13
+ validate(value) {
14
+ if (!Number.isFinite(value) || value < 1 || value > 179)
15
+ throw new RangeError(
16
+ 'SceneKit field of view needs a finite angle in 1..179 degrees.',
17
+ )
18
+ },
19
+ interpolate: (a, b, t) => Math.max(1, Math.min(179, a + (b - a) * t)),
20
+ create(target) {
21
+ if (
22
+ typeof JoltReferenceProperties === 'undefined' ||
23
+ typeof JoltReferenceProperties.sceneFieldOfView !== 'function'
24
+ )
25
+ throw new Error(
26
+ 'SceneKit field of view requires an updated @rafaelaugusto/jolt-native plugin. Update both packages and rebuild the native app.',
27
+ )
28
+ return JoltReferenceProperties.sceneFieldOfView(target as NSObject)
29
+ },
30
+ })
@@ -0,0 +1,237 @@
1
+ import { GridLayout, StackLayout, Utils, View, unsetValue } from '@nativescript/core'
2
+
3
+ export type SizeAxis = 'width' | 'height'
4
+ export type SizeValue = number | 'auto'
5
+ export type SizeValues = Partial<Record<SizeAxis, SizeValue>>
6
+ export type SizePixels = Record<SizeAxis, number>
7
+
8
+ type MeasuredView = View & {
9
+ _currentWidthMeasureSpec?: number
10
+ _currentHeightMeasureSpec?: number
11
+ _oldLeft?: number
12
+ _oldTop?: number
13
+ _oldRight?: number
14
+ _oldBottom?: number
15
+ }
16
+ const overlays = new WeakMap<View, Map<SizeAxis, object>>()
17
+
18
+ /** One live tree: speculative measurement never lays out or duplicates its views. */
19
+ export function sizeLayout(
20
+ view: View,
21
+ axes: readonly SizeAxis[],
22
+ replacing: readonly SizeAxis[] = [],
23
+ read: () => SizePixels = () => view.getActualSize(),
24
+ ) {
25
+ const parent = view.parent
26
+ if (
27
+ view.isLoaded &&
28
+ !(parent instanceof StackLayout) &&
29
+ !(parent instanceof GridLayout)
30
+ )
31
+ throw new TypeError('animateSize needs a child of StackLayout or GridLayout.')
32
+ const style = view.style as unknown as Record<string, unknown>
33
+ for (const axis of axes) {
34
+ const initial = view[axis]
35
+ if (
36
+ typeof initial === 'object' &&
37
+ initial !== null &&
38
+ (initial.unit === 'dip' || initial.unit === 'px')
39
+ )
40
+ dimension(initial.value, `initial ${axis}`)
41
+ else dimension(initial, `initial ${axis}`)
42
+ const key = `keyframe:${axis}`
43
+ if (
44
+ !replacing.includes(axis) &&
45
+ style[key] !== unsetValue &&
46
+ style[key] !== undefined
47
+ )
48
+ throw new Error(`animateSize cannot own ${axis} during a CSS keyframe animation.`)
49
+ }
50
+ let writing = false
51
+ let disposed = false
52
+ let claimed = false
53
+ const identity = {}
54
+ const claim = () => {
55
+ if (claimed || disposed) return
56
+ claimed = true
57
+ const current = overlays.get(view) ?? new Map<SizeAxis, object>()
58
+ for (const axis of axes) current.set(axis, identity)
59
+ overlays.set(view, current)
60
+ }
61
+ const owns = (axis: SizeAxis) => overlays.get(view)?.get(axis) === identity
62
+ let releaseHooks = () => {}
63
+ const write = (values: SizeValues) => {
64
+ if (disposed) return
65
+ claim()
66
+ writing = true
67
+ try {
68
+ for (const axis of axes)
69
+ if (owns(axis) && values[axis] !== undefined)
70
+ style[`keyframe:${axis}`] = bounded(view, axis, values[axis]!)
71
+ } finally {
72
+ writing = false
73
+ }
74
+ }
75
+ const clear = () => {
76
+ writing = true
77
+ try {
78
+ for (const axis of axes) if (owns(axis)) style[`keyframe:${axis}`] = unsetValue
79
+ } finally {
80
+ writing = false
81
+ }
82
+ }
83
+ const root = () => {
84
+ let node = view as MeasuredView
85
+ while (node.parent instanceof View && node.parent.typeName !== 'Page')
86
+ node = node.parent as MeasuredView
87
+ return node
88
+ }
89
+ const specs = (node: MeasuredView) => {
90
+ // Core's last layout allocation excludes transforms; UIView.frame does not.
91
+ const cached = [node._oldLeft, node._oldTop, node._oldRight, node._oldBottom]
92
+ const bounds =
93
+ cached.every((value) => Number.isFinite(value)) &&
94
+ cached[2]! > cached[0]! &&
95
+ cached[3]! > cached[1]!
96
+ ? { left: cached[0]!, top: cached[1]!, right: cached[2]!, bottom: cached[3]! }
97
+ : node._getCurrentLayoutBounds()
98
+ const layout = Utils.layout
99
+ return {
100
+ bounds,
101
+ width:
102
+ node._currentWidthMeasureSpec ??
103
+ layout.makeMeasureSpec(Math.max(0, bounds.right - bounds.left), layout.EXACTLY),
104
+ height:
105
+ node._currentHeightMeasureSpec ??
106
+ layout.makeMeasureSpec(Math.max(0, bounds.bottom - bounds.top), layout.EXACTLY),
107
+ }
108
+ }
109
+ const measure = () => {
110
+ const node = root()
111
+ const spec = specs(node)
112
+ view.requestLayout()
113
+ node.measure(spec.width, spec.height)
114
+ return { node, ...spec }
115
+ }
116
+ return {
117
+ read,
118
+ write,
119
+ ready() {
120
+ const { bounds } = specs(root())
121
+ return bounds.right > bounds.left && bounds.bottom > bounds.top
122
+ },
123
+ measure(destination: SizeValues): SizePixels {
124
+ const held = Object.fromEntries(
125
+ axes.map((axis) => [axis, style[`keyframe:${axis}`]]),
126
+ )
127
+ try {
128
+ write(destination)
129
+ measure()
130
+ const dip = Utils.layout.toDeviceIndependentPixels
131
+ return {
132
+ width: dip(view.getMeasuredWidth()),
133
+ height: dip(view.getMeasuredHeight()),
134
+ }
135
+ } finally {
136
+ writing = true
137
+ try {
138
+ for (const axis of axes)
139
+ if (!disposed && owns(axis)) style[`keyframe:${axis}`] = held[axis]
140
+ } finally {
141
+ writing = false
142
+ }
143
+ }
144
+ },
145
+ /** Flush the restored/current declarations before any draw can see probe measurements. */
146
+ layout() {
147
+ const { node, bounds } = measure()
148
+ node.layout(bounds.left, bounds.top, bounds.right, bounds.bottom)
149
+ },
150
+ commit(values?: SizeValues) {
151
+ writing = true
152
+ try {
153
+ if (values)
154
+ for (const axis of axes)
155
+ if (owns(axis) && values[axis] !== undefined)
156
+ view[axis] = bounded(view, axis, values[axis]!)
157
+ } finally {
158
+ writing = false
159
+ clear()
160
+ }
161
+ },
162
+ watch(takeover: () => void) {
163
+ claim()
164
+ const undo: (() => void)[] = []
165
+ releaseHooks = () =>
166
+ undo
167
+ .splice(0)
168
+ .reverse()
169
+ .forEach((off) => off())
170
+ // A local/CSS write hidden under the animation override still takes ownership.
171
+ for (const axis of axes)
172
+ for (const name of [axis, `css:${axis}`, `keyframe:${axis}`]) {
173
+ const own = Object.getOwnPropertyDescriptor(style, name)
174
+ let source: object | null = style
175
+ let descriptor: PropertyDescriptor | undefined
176
+ while (source && !descriptor) {
177
+ descriptor = Object.getOwnPropertyDescriptor(source, name)
178
+ source = Object.getPrototypeOf(source)
179
+ }
180
+ if (!descriptor?.set || !descriptor.get || own?.configurable === false)
181
+ throw new Error(`animateSize cannot observe ownership of ${name}.`)
182
+ const original = descriptor
183
+ const set = function (this: object, value: unknown) {
184
+ if (!writing) takeover()
185
+ original.set!.call(this, value)
186
+ }
187
+ Object.defineProperty(style, name, {
188
+ configurable: true,
189
+ get: original.get,
190
+ set,
191
+ })
192
+ undo.push(() => {
193
+ if (Object.getOwnPropertyDescriptor(style, name)?.set !== set) return
194
+ if (own) Object.defineProperty(style, name, own)
195
+ else delete style[name]
196
+ })
197
+ }
198
+ },
199
+ dispose() {
200
+ disposed = true
201
+ releaseHooks()
202
+ clear()
203
+ const current = overlays.get(view)
204
+ for (const axis of axes) if (owns(axis)) current!.delete(axis)
205
+ if (!current?.size) overlays.delete(view)
206
+ },
207
+ }
208
+ }
209
+
210
+ function bounded(view: View, axis: SizeAxis, value: SizeValue): SizeValue {
211
+ if (value === 'auto') return value
212
+ const minimum = axis === 'width' ? view.minWidth : view.minHeight
213
+ const maximum = axis === 'width' ? view.maxWidth : view.maxHeight
214
+ const dip = Utils.layout.toDeviceIndependentPixels
215
+ const min =
216
+ typeof minimum === 'number'
217
+ ? minimum
218
+ : dip((axis === 'width' ? view.effectiveMinWidth : view.effectiveMinHeight) ?? 0)
219
+ const max =
220
+ typeof maximum === 'number'
221
+ ? maximum
222
+ : dip(
223
+ (axis === 'width' ? view.effectiveMaxWidth : view.effectiveMaxHeight) ??
224
+ Infinity,
225
+ )
226
+ return Math.min(max, Math.max(0, min, value))
227
+ }
228
+
229
+ export function dimension(value: unknown, label: string): asserts value is SizeValue {
230
+ if (
231
+ value !== 'auto' &&
232
+ (typeof value !== 'number' || !Number.isFinite(value) || value < 0)
233
+ )
234
+ throw new RangeError(
235
+ `animateSize ${label} must be a nonnegative finite DIP number or "auto"; percentages and unit objects are unsupported.`,
236
+ )
237
+ }