@zcomponent/core 1.13.0-beta → 1.14.1-beta

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.
@@ -51,12 +51,12 @@ export declare class Animation {
51
51
  tick(): void;
52
52
  registerEntityPath(path: string, entity: any, pathParts: (string | number)[]): void;
53
53
  evaluateTouchedPaths(paths: string[], t?: number): void;
54
+ private _evaluateLayer;
54
55
  get spotlightLayer(): Layer | undefined;
55
56
  set spotlightLayer(l: Layer | undefined);
56
57
  clearCachedDefault(entityID: string, prop: string): void;
57
58
  private _emitPendingEvents;
58
59
  private _resolvePath;
59
- private _evaluatePath;
60
60
  start(): void;
61
61
  }
62
62
  export interface EntityPath {
@@ -1,5 +1,4 @@
1
1
  import { Observable } from '../observable';
2
- import { clone } from '../data';
3
2
  import { Event } from '../event';
4
3
  /** @internal */
5
4
  export var AnimationEvents;
@@ -109,11 +108,58 @@ export class Animation {
109
108
  return;
110
109
  t = t ?? this.timeSource?.() ?? performance.now();
111
110
  const unique = new Set([...paths]);
112
- for (const entry of unique.values()) {
113
- this._evaluatePath(t, entry);
111
+ const uniqueAsArray = unique.values();
112
+ const resolvedPaths = [...uniqueAsArray].map(entry => this._resolvePath(entry));
113
+ for (const entry of resolvedPaths) {
114
+ if (!entry)
115
+ continue;
116
+ let byEntity = this._defaultValues.get(entry[0]);
117
+ if (!byEntity) {
118
+ byEntity = new Map();
119
+ this._defaultValues.set(entry[0], byEntity);
120
+ }
121
+ let observable = byEntity.get(entry[1][0]);
122
+ if (!observable) {
123
+ let val = entry[0][entry[1][0]];
124
+ if (val instanceof Observable)
125
+ val = val.value;
126
+ observable = new Observable(val);
127
+ byEntity.set(entry[1][0], observable);
128
+ }
129
+ entry[2] = observable;
130
+ }
131
+ // Reset our working values to default
132
+ for (const p of resolvedPaths) {
133
+ if (p?.[2])
134
+ p[2].value = undefined;
135
+ }
136
+ if (this._spotlightLayer)
137
+ this._evaluateLayer(t, this._spotlightLayer, resolvedPaths);
138
+ else {
139
+ for (const layer of this._layers)
140
+ this._evaluateLayer(t, layer, resolvedPaths);
141
+ }
142
+ for (const entry of resolvedPaths) {
143
+ if (!entry?.[2])
144
+ continue;
145
+ if (entry[0][entry[1][0]] instanceof Observable)
146
+ entry[0][entry[1][0]].value = entry[2].value;
147
+ else
148
+ entry[0][entry[1][0]] = entry[2].value;
114
149
  }
115
150
  this._emitPendingEvents();
116
151
  }
152
+ _evaluateLayer(t, layer, paths) {
153
+ for (const p of paths) {
154
+ if (!p?.[2])
155
+ continue;
156
+ if (!layer.influencedPaths.has(p[3]))
157
+ continue;
158
+ let val = getResolved(p[2], p[1]);
159
+ val = layer.computePathProperty(t, p[3], val);
160
+ setResolved(p[2], p[1], val);
161
+ }
162
+ }
117
163
  get spotlightLayer() {
118
164
  return this._spotlightLayer;
119
165
  }
@@ -153,38 +199,7 @@ export class Animation {
153
199
  const path = this._entityPaths.get(p);
154
200
  if (!path || path.pathParts.length === 0)
155
201
  return;
156
- let parent = path.entity;
157
- let currentKey = path.pathParts[0];
158
- if (parent[currentKey] instanceof Observable) {
159
- parent = parent[currentKey];
160
- currentKey = 'value';
161
- }
162
- for (let i = 1; i < path.pathParts.length; i++) {
163
- parent = parent[currentKey];
164
- currentKey = path.pathParts[i];
165
- }
166
- return [parent, currentKey];
167
- }
168
- _evaluatePath(t, p) {
169
- const resolved = this._resolvePath(p);
170
- if (!resolved)
171
- return;
172
- if (!this._defaultValues.has(p)) {
173
- this._defaultValues.set(p, clone(resolved[0][resolved[1]]));
174
- }
175
- let val = this._defaultValues.get(p);
176
- if (this._spotlightLayer) {
177
- if (this._spotlightLayer.influencedPaths.has(p))
178
- val = this._spotlightLayer.computePathProperty(t, p, val);
179
- }
180
- else {
181
- for (const layer of this._layers) {
182
- if (!layer.influencedPaths.has(p))
183
- continue;
184
- val = layer.computePathProperty(t, p, val);
185
- }
186
- }
187
- resolved[0][resolved[1]] = val;
202
+ return [path.entity, path.pathParts, undefined, p];
188
203
  }
189
204
  start() {
190
205
  for (const layer of this._layers) {
@@ -195,3 +210,18 @@ export class Animation {
195
210
  }
196
211
  }
197
212
  }
213
+ function getResolved(o, pathParts) {
214
+ let current = o.value;
215
+ for (let i = 1; i < pathParts.length; i++)
216
+ current = current[pathParts[i]];
217
+ return current;
218
+ }
219
+ function setResolved(o, pathParts, val) {
220
+ let target = o;
221
+ let key = 'value';
222
+ for (let i = 1; i < pathParts.length; i++) {
223
+ target = target[key];
224
+ key = pathParts[i];
225
+ }
226
+ target[key] = val;
227
+ }
@@ -34,5 +34,5 @@ export declare class Clip {
34
34
  streamPause(isActive: boolean, active: LayerClip | null | undefined): void;
35
35
  streamSeek(t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined): void;
36
36
  streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, stalled?: boolean, parentWeight?: number): void;
37
- stalled(): boolean;
37
+ isStalled(): boolean;
38
38
  }
@@ -131,9 +131,9 @@ export class Clip {
131
131
  track.streamTick(clipState, t, rate, isActive, active, force, stalled, parentWeight);
132
132
  }
133
133
  }
134
- stalled() {
134
+ isStalled() {
135
135
  for (const track of this._tracks) {
136
- if (track.stalled())
136
+ if (track.isStalled())
137
137
  return true;
138
138
  }
139
139
  return false;
@@ -45,7 +45,7 @@ export class Layer {
45
45
  this._queue.splice(i, 1);
46
46
  }
47
47
  if (entry.layerClip && entry === this._active) {
48
- if (next && (entry.layerClip.state === StreamState.Ended || next.fadeTime >= entry.layerClip.getRemainingTimeEstimate())) {
48
+ if (next && (entry.layerClip.state.value === StreamState.Ended || next.fadeTime >= entry.layerClip.getRemainingTimeEstimate())) {
49
49
  next.startTime = t;
50
50
  this._active = next;
51
51
  this.animation._pendingEvents.push({ evt: AnimationEvents.onLayerClipActive, args: [this._active.layerClip] });
@@ -1,4 +1,6 @@
1
1
  import { FadeParameters } from '../data';
2
+ import { Event } from '../event';
3
+ import { Observable } from '../observable';
2
4
  import { Animation } from './animation';
3
5
  import { AnimationState } from './animationstate';
4
6
  import { Clip } from './clips/clip';
@@ -24,13 +26,21 @@ export declare class LayerClip implements Stream {
24
26
  private _lastClipTime;
25
27
  private _lastLoop;
26
28
  private _stopped;
27
- state: StreamState;
29
+ private _state;
30
+ state: Observable<StreamState, never>;
31
+ playing: Observable<boolean, never>;
32
+ stalled: Observable<boolean, never>;
33
+ onPlaying: Event<[LayerClip]>;
34
+ onPaused: Event<[LayerClip]>;
35
+ onEnded: Event<[LayerClip]>;
36
+ onLoop: Event<[LayerClip]>;
28
37
  constructor(layer: Layer, clip: Clip, scriptName?: string | undefined, id?: string | undefined);
29
38
  private _clipDirty;
30
39
  tick(touchedPaths: string[], force?: boolean): void;
31
40
  computePathProperty(p: string, valueBefore: any): any;
32
41
  getRemainingTimeEstimate(): number;
33
42
  private _getTime;
43
+ private _stateUpdate;
34
44
  queue(opts?: LayerClipPlayOptions): void;
35
45
  length(): number;
36
46
  play(opts?: LayerClipPlayOptions): void;
@@ -43,7 +53,7 @@ export declare class LayerClip implements Stream {
43
53
  _restore(state: AnimationState): void;
44
54
  get clipTime(): number;
45
55
  stop(): void;
46
- stalled(): boolean;
56
+ isStalled(): boolean;
47
57
  }
48
58
  export interface LayerClipSeekOptions {
49
59
  fade?: FadeParameters | null;
@@ -1,3 +1,5 @@
1
+ import { Event } from '../event';
2
+ import { Observable } from '../observable';
1
3
  import { AnimationEvents } from './animation';
2
4
  import { StreamState } from './stream';
3
5
  import { resolveClipTimeLoops } from './tracks/track';
@@ -16,27 +18,51 @@ export class LayerClip {
16
18
  this._rate = 1;
17
19
  this._loop = false;
18
20
  this._stopped = false;
19
- this.state = StreamState.Paused;
21
+ this._state = StreamState.Paused;
22
+ this.state = new Observable(this._state);
23
+ this.playing = new Observable(false);
24
+ this.stalled = new Observable(false);
25
+ this.onPlaying = new Event();
26
+ this.onPaused = new Event();
27
+ this.onEnded = new Event();
28
+ this.onLoop = new Event();
20
29
  this._clipDirty = (v) => {
21
30
  if (v)
22
31
  this._lastClipTime = undefined;
23
32
  };
33
+ this._stateUpdate = (s) => {
34
+ this.playing.value = s === StreamState.Playing;
35
+ switch (s) {
36
+ case StreamState.Ended:
37
+ this.onEnded.emit(this);
38
+ break;
39
+ case StreamState.Playing:
40
+ this.onPlaying.emit(this);
41
+ break;
42
+ case StreamState.Paused:
43
+ this.onPaused.emit(this);
44
+ break;
45
+ }
46
+ };
24
47
  this.animation = layer.animation;
25
48
  this._t1 = clip.length;
26
49
  layer._registerLayerClip(this);
27
50
  this.clip.influencedPathsDirty.addListener(this._clipDirty);
28
51
  if (id)
29
52
  layer.animation.layerClipByID.set(id, this);
53
+ this.state.addListener(this._stateUpdate);
30
54
  }
31
55
  tick(touchedPaths, force) {
32
56
  const t = this._getTime();
33
57
  if (this._stallStartTime !== undefined) {
34
- if (!this.clip.stalled())
58
+ if (!this.clip.isStalled())
35
59
  this._cancelStall();
36
60
  }
37
- else if (this.state === StreamState.Playing) {
38
- if (this.clip.stalled())
61
+ else if (this._state === StreamState.Playing) {
62
+ if (this.clip.isStalled()) {
39
63
  this._stallStartTime = t;
64
+ this.stalled.value = true;
65
+ }
40
66
  }
41
67
  const t0 = this._t0 + (t - (this._pauseTime ?? t)) + (t - (this._stallStartTime ?? t));
42
68
  const t1 = this._t1 + (t - (this._pauseTime ?? t)) + (t - (this._stallStartTime ?? t));
@@ -45,14 +71,18 @@ export class LayerClip {
45
71
  if (ct !== this._lastClipTime || force) {
46
72
  touchedPaths.push(...this.clip.influencedPaths);
47
73
  }
48
- if (this.state === StreamState.Playing && t >= this._t1) {
74
+ if (this._state === StreamState.Playing && t >= this._t1) {
49
75
  this.pause();
50
- this.state = StreamState.Ended;
76
+ this._state = StreamState.Ended;
77
+ this.state.value = this._state;
51
78
  }
52
79
  else {
53
80
  const [bt, loop] = resolveClipTimeLoops(t, t0, this._rate >= 0 ? 0 : this.clip.length, this._rate, this.clip.length, this._rate < 0, t1);
54
- this.clip.streamTick(this.state, bt, this._rate, this.layer.active === this, this.layer.active, loop !== this._lastLoop, this._stallStartTime !== undefined, 1);
81
+ this.clip.streamTick(this._state, bt, this._rate, this.layer.active === this, this.layer.active, loop !== this._lastLoop, this._stallStartTime !== undefined, 1);
82
+ const isNewLoop = this._lastLoop !== undefined && this._lastLoop !== loop;
55
83
  this._lastLoop = loop;
84
+ if (isNewLoop)
85
+ this.onLoop.emit(this);
56
86
  }
57
87
  }
58
88
  computePathProperty(p, valueBefore) {
@@ -85,7 +115,7 @@ export class LayerClip {
85
115
  this._stopped = false;
86
116
  this._loop = opts?.loop ?? this.defaultLoop;
87
117
  const t = this._getTime();
88
- if (this.state === StreamState.Ended) {
118
+ if (this._state === StreamState.Ended) {
89
119
  this._t0 = t;
90
120
  }
91
121
  else if (this._pauseTime !== undefined) {
@@ -108,7 +138,7 @@ export class LayerClip {
108
138
  }
109
139
  const t0 = this._t0 + (t - (this._pauseTime ?? t));
110
140
  const t1 = this._t1 + (t - (this._pauseTime ?? t));
111
- this.state = StreamState.Playing;
141
+ this._state = StreamState.Playing;
112
142
  this.animation._pendingEvents.push({ evt: AnimationEvents.onLayerClipState, args: [this] });
113
143
  this._lastClipTime = undefined;
114
144
  this._lastTickTime = undefined;
@@ -116,21 +146,25 @@ export class LayerClip {
116
146
  this.layer._activateLayerClip(this, opts);
117
147
  this.layer._evaulate();
118
148
  }
119
- if (this.clip.stalled())
149
+ if (this.clip.isStalled()) {
120
150
  this._stallStartTime = t;
151
+ this.stalled.value = true;
152
+ }
121
153
  const [bt, loop] = resolveClipTimeLoops(t, t0, this._rate >= 0 ? 0 : this.clip.length, this._rate, this.clip.length, this._rate < 0, t1);
122
- this.clip.streamTick(this.state, bt, this._rate, this.layer.active === this, this.layer.active, loop !== this._lastLoop, this._stallStartTime !== undefined, 1);
154
+ this.clip.streamTick(this._state, bt, this._rate, this.layer.active === this, this.layer.active, loop !== this._lastLoop, this._stallStartTime !== undefined, 1);
123
155
  this._lastLoop = loop;
156
+ this.state.value = this._state;
124
157
  }
125
158
  pause() {
126
159
  delete this._lastLoop;
127
- if (this._pauseTime !== undefined || this.state !== StreamState.Playing)
160
+ if (this._pauseTime !== undefined || this._state !== StreamState.Playing)
128
161
  return;
129
- this.state = StreamState.Paused;
162
+ this._state = StreamState.Paused;
130
163
  this.animation._pendingEvents.push({ evt: AnimationEvents.onLayerClipState, args: [this] });
131
164
  this._pauseTime = this._getTime();
132
165
  this._cancelStall();
133
166
  this.clip.streamPause(this.layer.active === this, this.layer.active);
167
+ this.state.value = this._state;
134
168
  }
135
169
  _cancelStall() {
136
170
  if (this._stallStartTime === undefined)
@@ -139,6 +173,7 @@ export class LayerClip {
139
173
  this._t0 += t - this._stallStartTime;
140
174
  this._t1 += t - this._stallStartTime;
141
175
  delete this._stallStartTime;
176
+ this.stalled.value = false;
142
177
  }
143
178
  seek(ct, opts) {
144
179
  const t = this._getTime();
@@ -181,7 +216,7 @@ export class LayerClip {
181
216
  rate: this._rate,
182
217
  loop: this._loop,
183
218
  stopped: this._stopped,
184
- state: this.state,
219
+ state: this._state,
185
220
  };
186
221
  state.byLayerClip[this.id] = ret;
187
222
  }
@@ -199,7 +234,8 @@ export class LayerClip {
199
234
  this._rate = entry.rate;
200
235
  this._loop = entry.loop;
201
236
  this._stopped = entry.stopped;
202
- this.state = entry.state;
237
+ this._state = entry.state;
238
+ this.state.value = this._state;
203
239
  }
204
240
  get clipTime() {
205
241
  return this._lastTickTime !== undefined ? this._lastTickTime : this.defaultPlaySpeed > 0 ? 0 : this.clip.length;
@@ -213,10 +249,11 @@ export class LayerClip {
213
249
  delete this._stallStartTime;
214
250
  this._rate = 1;
215
251
  this.clip.streamTick(StreamState.Ended, this._t0, 1, this.layer.active === this, this.layer.active, true);
216
- this.state = StreamState.Ended;
252
+ this._state = StreamState.Ended;
217
253
  this.animation._pendingEvents.push({ evt: AnimationEvents.onLayerClipState, args: [this] });
254
+ this.state.value = this._state;
218
255
  }
219
- stalled() {
256
+ isStalled() {
220
257
  return this._stallStartTime !== undefined;
221
258
  }
222
259
  }
@@ -9,7 +9,7 @@ export interface Stream {
9
9
  seek: (t: number) => void;
10
10
  stop: () => void;
11
11
  length?: () => number | undefined;
12
- stalled?: () => boolean;
12
+ isStalled?: () => boolean;
13
13
  }
14
14
  export interface PlayOptions {
15
15
  speed?: number;
@@ -31,5 +31,5 @@ export declare class ClipTrack extends Track {
31
31
  streamSeek(t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined): void;
32
32
  streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, stalled?: boolean, parentWeight?: number): void;
33
33
  private _resolveBlock;
34
- stalled(): boolean;
34
+ isStalled(): boolean;
35
35
  }
@@ -113,7 +113,7 @@ export class ClipTrack extends Track {
113
113
  }
114
114
  return undefined;
115
115
  }
116
- stalled() {
117
- return this.clip.stalled();
116
+ isStalled() {
117
+ return this.clip.isStalled();
118
118
  }
119
119
  }
@@ -39,5 +39,5 @@ export declare class StreamTrack extends Track {
39
39
  streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, stalled?: boolean, parentWeight?: number): void;
40
40
  private _applyState;
41
41
  private _resolveBlock;
42
- stalled(): boolean;
42
+ isStalled(): boolean;
43
43
  }
@@ -158,7 +158,7 @@ export class StreamTrack extends Track {
158
158
  }
159
159
  return undefined;
160
160
  }
161
- stalled() {
162
- return this.stream?.stalled?.() ?? false;
161
+ isStalled() {
162
+ return this.stream?.isStalled?.() ?? false;
163
163
  }
164
164
  }
@@ -7,7 +7,7 @@ export declare abstract class Track {
7
7
  constructor(anim: Animation, id?: string | undefined);
8
8
  abstract get influencedPaths(): string[];
9
9
  abstract computePathProperty(t: number, p: string, valueBefore: any, parentWeight?: number): any;
10
- stalled(): boolean;
10
+ isStalled(): boolean;
11
11
  }
12
12
  export declare function resolveKeyframeValue(t: number, before: Keyframe, after: Keyframe | undefined): any;
13
13
  export declare function resolveKeyframes(keyframes: Keyframe[], t: number): [before: Keyframe | undefined, after: Keyframe | undefined];
@@ -8,7 +8,7 @@ export class Track {
8
8
  if (id)
9
9
  anim.trackByID.set(id, this);
10
10
  }
11
- stalled() {
11
+ isStalled() {
12
12
  return false;
13
13
  }
14
14
  }
package/lib/inflate.js CHANGED
@@ -152,7 +152,17 @@ export function updateClipFromData(zcomp, clip, data) {
152
152
  clip.defaultFadeParameters = resolvedFadeParameters(data.defaultFadeParameters, zcomp.animation.curves);
153
153
  clip.length = data.length;
154
154
  clip.clearTracks();
155
- for (const track of Object.values(data.tracks)) {
155
+ const tracks = Object.values(data.tracks);
156
+ tracks.sort((atrack, btrack) => {
157
+ let a = atrack.order ?? 'A0';
158
+ let b = btrack.order ?? 'A0';
159
+ if (a === b) {
160
+ a = atrack.id;
161
+ b = btrack.id;
162
+ }
163
+ return a < b ? -1 : 1;
164
+ });
165
+ for (const track of Object.values(tracks)) {
156
166
  if (!track)
157
167
  continue;
158
168
  const instance = createTrackFromData(zcomp, clip.animation, track);
@@ -195,7 +205,17 @@ export function populateAnimationTracksFromData(zcomp, anim, data) {
195
205
  const clip = anim.clipByID.get(clipData.id);
196
206
  if (!clip)
197
207
  continue;
198
- for (const track of Object.values(clipData.tracks)) {
208
+ const tracks = Object.values(clipData.tracks);
209
+ tracks.sort((atrack, btrack) => {
210
+ let a = atrack.order ?? 'A0';
211
+ let b = btrack.order ?? 'A0';
212
+ if (a === b) {
213
+ a = atrack.id;
214
+ b = btrack.id;
215
+ }
216
+ return a < b ? -1 : 1;
217
+ });
218
+ for (const track of Object.values(tracks)) {
199
219
  if (!track)
200
220
  continue;
201
221
  const instance = createTrackFromData(zcomp, anim, track);
@@ -33,10 +33,10 @@ type Resolved<T, M> = [T] extends [never] ? M : T;
33
33
  * @typeParam TypeInternal - An internal type used to improve automatic type detection
34
34
  */
35
35
  export declare class Observable<Type = never, TypeInternal extends any[] | [] | never = never> extends Emitter<[Resolved<Type, TypeInternal>]> {
36
- private _default;
37
36
  private _deep;
38
37
  private _proxies;
39
38
  private _value;
39
+ private _default;
40
40
  /**
41
41
  * Constructs a new Observable
42
42
  *
package/lib/observable.js CHANGED
@@ -32,21 +32,12 @@ import { Emitter } from "./emitter";
32
32
  * @typeParam TypeInternal - An internal type used to improve automatic type detection
33
33
  */
34
34
  export class Observable extends Emitter {
35
- constructor(_default, withHandler, _deep = true, callWithImmediately = false) {
35
+ constructor(def, withHandler, _deep = true, callWithImmediately = false) {
36
36
  super();
37
- this._default = _default;
38
37
  this._deep = _deep;
39
38
  this._proxies = new WeakMap();
40
- let initial = this._default;
41
- if (Array.isArray(this._default) && this._deep) {
42
- initial = this._default.slice();
43
- this._default = this._default.slice();
44
- }
45
- else if (typeof this._default === 'object' && this._deep && this._default !== null && Object.getPrototypeOf(this._default) !== Object.prototype) {
46
- initial = { ...this._default };
47
- this._default = { ...this._default };
48
- }
49
- this._value = this._wrap(initial);
39
+ this._default = clone(def, _deep);
40
+ this._value = this._wrap(clone(def, _deep));
50
41
  if (withHandler)
51
42
  this.addListener(withHandler, 0, callWithImmediately);
52
43
  }
@@ -59,48 +50,43 @@ export class Observable extends Emitter {
59
50
  return this._value.proxy;
60
51
  }
61
52
  _wrap(v) {
62
- if (!Array.isArray(v) || this._deep === false) {
63
- if (typeof v !== 'object' ||
64
- v === null ||
65
- Object.getPrototypeOf(v) !== Object.prototype ||
66
- this._deep === false ||
67
- typeof v === 'function') {
68
- return { proxy: v, target: v };
69
- }
70
- }
53
+ if (!shouldWrap(v) || !this._deep)
54
+ return { proxy: v, target: v };
71
55
  const that = this;
72
56
  return {
73
57
  target: v,
74
58
  proxy: new Proxy(v, {
75
59
  get(target, p, receiver) {
76
60
  const val = target[p];
61
+ if (!shouldWrap(val))
62
+ return val;
77
63
  if (Array.isArray(target) && p === "splice") {
78
- return function (...args) { val.apply(target, args); that._emitValue(); };
79
- }
80
- if (!Array.isArray(val)) {
81
- if (typeof val !== 'object')
82
- return val;
83
- if (val === null)
84
- return val;
85
- if (Object.getPrototypeOf(val) !== Object.prototype)
86
- return val;
87
- if (typeof val === 'function')
88
- return val;
64
+ return function (...args) {
65
+ for (let i = 2; i < args.length; i++) {
66
+ args[i] = clone(args[i], true);
67
+ }
68
+ val.apply(target, args);
69
+ that._emitValue();
70
+ };
89
71
  }
90
72
  if (val['__z_is_observable'])
91
73
  return val;
92
74
  const proxy = that._proxies.get(val) || that._wrap(val);
93
75
  that._proxies.set(val, proxy);
94
- return proxy;
76
+ return proxy.proxy;
95
77
  },
96
78
  set(target, p, val) {
97
- target[p] = val;
98
- that._emitValue();
79
+ const changed = target[p] !== val;
80
+ target[p] = clone(val, true);
81
+ if (changed)
82
+ that._emitValue();
99
83
  return true;
100
84
  },
101
85
  deleteProperty(target, p) {
86
+ const changed = target[p] !== undefined;
102
87
  delete target[p];
103
- that._emitValue();
88
+ if (changed)
89
+ that._emitValue();
104
90
  return true;
105
91
  }
106
92
  })
@@ -108,16 +94,13 @@ export class Observable extends Emitter {
108
94
  }
109
95
  set value(v) {
110
96
  v = v ?? this._default;
111
- if (Array.isArray(this._value.target) && Array.isArray(v)) {
112
- this._value.target.splice(0, Infinity, ...v);
97
+ const changed = v !== this._value.proxy;
98
+ this._value = this._wrap(clone(v, this._deep));
99
+ if (changed)
113
100
  this._emitValue();
114
- return;
115
- }
116
- this._value = this._wrap(Array.isArray(v) ? [...v] : v);
117
- this._emitValue();
118
101
  }
119
102
  addListener(fn, priority = 0, callImmediately = true) {
120
- super.addListener(fn);
103
+ super.addListener(fn, priority);
121
104
  if (!callImmediately)
122
105
  return;
123
106
  try {
@@ -131,3 +114,48 @@ export class Observable extends Emitter {
131
114
  this._emit(this._value.proxy);
132
115
  }
133
116
  }
117
+ function clone(a, deep) {
118
+ if (!deep) {
119
+ if (Array.isArray(a))
120
+ a.slice();
121
+ if (typeof a === 'object' && a !== null) {
122
+ const proto = Object.getPrototypeOf(a);
123
+ if (proto === Object.prototype || proto === null) {
124
+ return { ...a };
125
+ }
126
+ }
127
+ return a;
128
+ }
129
+ if (Array.isArray(a)) {
130
+ const ret = new Array(a.length);
131
+ for (let i = 0; i < ret.length; i++) {
132
+ ret[i] = clone(a[i], true);
133
+ }
134
+ return ret;
135
+ }
136
+ if (typeof a === 'object' && a !== null) {
137
+ const proto = Object.getPrototypeOf(a);
138
+ if (proto === Object.prototype || proto === null) {
139
+ const ret = Object.create(null);
140
+ for (const entry of Object.getOwnPropertyNames(a)) {
141
+ ret[entry] = clone(a[entry], true);
142
+ }
143
+ return ret;
144
+ }
145
+ }
146
+ return a;
147
+ }
148
+ function shouldWrap(a) {
149
+ if (Array.isArray(a))
150
+ return true;
151
+ if (a === null)
152
+ return false;
153
+ if (typeof a === 'object' && a !== null) {
154
+ const proto = Object.getPrototypeOf(a);
155
+ if (proto === Object.prototype || proto === null)
156
+ return true;
157
+ }
158
+ if (typeof a === 'function')
159
+ return false;
160
+ return false;
161
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zcomponent/core",
3
- "version": "1.13.0-beta",
3
+ "version": "1.14.1-beta",
4
4
  "description": "The core component model and built-in functionality for Mattercraft.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",