@mmstack/primitives 20.0.0 → 20.0.2

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.
@@ -101,7 +101,7 @@ function debounced(initial, opt) {
101
101
  */
102
102
  function debounce(source, opt) {
103
103
  const ms = opt?.ms ?? 0;
104
- const trigger = signal(false);
104
+ const trigger = signal(false, ...(ngDevMode ? [{ debugName: "trigger" }] : []));
105
105
  let timeout;
106
106
  try {
107
107
  const destroyRef = opt?.destroyRef ?? inject(DestroyRef, { optional: true });
@@ -136,25 +136,112 @@ function debounce(source, opt) {
136
136
  return writable;
137
137
  }
138
138
 
139
+ const { is } = Object;
140
+ function mutable(initial, opt) {
141
+ const baseEqual = opt?.equal ?? is;
142
+ let trigger = false;
143
+ const equal = (a, b) => {
144
+ if (trigger)
145
+ return false;
146
+ return baseEqual(a, b);
147
+ };
148
+ const sig = signal(initial, {
149
+ ...opt,
150
+ equal,
151
+ });
152
+ const internalUpdate = sig.update;
153
+ sig.mutate = (updater) => {
154
+ trigger = true;
155
+ internalUpdate(updater);
156
+ trigger = false;
157
+ };
158
+ sig.inline = (updater) => {
159
+ sig.mutate((prev) => {
160
+ updater(prev);
161
+ return prev;
162
+ });
163
+ };
164
+ return sig;
165
+ }
166
+ /**
167
+ * Type guard function to check if a given `WritableSignal` is a `MutableSignal`. This is useful
168
+ * for situations where you need to conditionally use the `mutate` or `inline` methods.
169
+ *
170
+ * @typeParam T - The type of the signal's value (optional, defaults to `any`).
171
+ * @param value - The `WritableSignal` to check.
172
+ * @returns `true` if the signal is a `MutableSignal`, `false` otherwise.
173
+ *
174
+ * @example
175
+ * const mySignal = signal(0);
176
+ * const myMutableSignal = mutable(0);
177
+ *
178
+ * if (isMutable(mySignal)) {
179
+ * mySignal.mutate(x => x + 1); // This would cause a type error, as mySignal is not a MutableSignal.
180
+ * }
181
+ *
182
+ * if (isMutable(myMutableSignal)) {
183
+ * myMutableSignal.mutate(x => x + 1); // This is safe.
184
+ * }
185
+ */
186
+ function isMutable(value) {
187
+ return 'mutate' in value && typeof value.mutate === 'function';
188
+ }
189
+
139
190
  function derived(source, optOrKey, opt) {
140
191
  const isArray = Array.isArray(untracked(source)) && typeof optOrKey === 'number';
141
192
  const from = typeof optOrKey === 'object' ? optOrKey.from : (v) => v[optOrKey];
142
193
  const onChange = typeof optOrKey === 'object'
143
194
  ? optOrKey.onChange
144
195
  : isArray
145
- ? (next) => {
146
- source.update((cur) => {
147
- const newArray = [...cur];
148
- newArray[optOrKey] = next;
149
- return newArray;
150
- });
151
- }
152
- : (next) => {
153
- source.update((cur) => ({ ...cur, [optOrKey]: next }));
154
- };
196
+ ? isMutable(source)
197
+ ? (next) => {
198
+ source.mutate((cur) => {
199
+ cur[optOrKey] = next;
200
+ return cur;
201
+ });
202
+ }
203
+ : (next) => {
204
+ source.update((cur) => {
205
+ const newArray = [...cur];
206
+ newArray[optOrKey] = next;
207
+ return newArray;
208
+ });
209
+ }
210
+ : isMutable(source)
211
+ ? (next) => {
212
+ source.mutate((cur) => {
213
+ cur[optOrKey] = next;
214
+ return cur;
215
+ });
216
+ }
217
+ : (next) => {
218
+ source.update((cur) => ({ ...cur, [optOrKey]: next }));
219
+ };
155
220
  const rest = typeof optOrKey === 'object' ? optOrKey : opt;
156
- const sig = toWritable(computed(() => from(source()), rest), (newVal) => onChange(newVal));
221
+ let baseEqual = rest?.equal ?? Object.is;
222
+ let trigger = false;
223
+ const equal = isMutable(source)
224
+ ? (a, b) => {
225
+ if (trigger)
226
+ return false;
227
+ return baseEqual(a, b);
228
+ }
229
+ : baseEqual;
230
+ const sig = toWritable(computed(() => from(source()), { ...rest, equal }), (newVal) => onChange(newVal));
157
231
  sig.from = from;
232
+ if (isMutable(source)) {
233
+ sig.mutate = (updater) => {
234
+ trigger = true;
235
+ sig.update(updater);
236
+ trigger = false;
237
+ };
238
+ sig.inline = (updater) => {
239
+ sig.mutate((prev) => {
240
+ updater(prev);
241
+ return prev;
242
+ });
243
+ };
244
+ }
158
245
  return sig;
159
246
  }
160
247
  /**
@@ -262,7 +349,7 @@ function elementVisibility(target = inject(ElementRef), opt) {
262
349
  const base = computed(() => undefined, {
263
350
  debugName: opt?.debugName,
264
351
  });
265
- base.visible = computed(() => false);
352
+ base.visible = computed(() => false, ...(ngDevMode ? [{ debugName: "visible" }] : []));
266
353
  return base;
267
354
  }
268
355
  const state = signal(undefined, {
@@ -299,7 +386,7 @@ function elementVisibility(target = inject(ElementRef), opt) {
299
386
  if (!s)
300
387
  return false;
301
388
  return s.isIntersecting;
302
- });
389
+ }, ...(ngDevMode ? [{ debugName: "visible" }] : []));
303
390
  return base;
304
391
  }
305
392
 
@@ -364,7 +451,7 @@ function elementVisibility(target = inject(ElementRef), opt) {
364
451
  */
365
452
  function mapArray(source, map, opt) {
366
453
  const data = isSignal(source) ? source : computed(source);
367
- const len = computed(() => data().length);
454
+ const len = computed(() => data().length, ...(ngDevMode ? [{ debugName: "len" }] : []));
368
455
  return linkedSignal({
369
456
  source: () => len(),
370
457
  computation: (len, prev) => {
@@ -373,7 +460,13 @@ function mapArray(source, map, opt) {
373
460
  if (len === prev.value.length)
374
461
  return prev.value;
375
462
  if (len < prev.value.length) {
376
- return prev.value.slice(0, len);
463
+ const slice = prev.value.slice(0, len);
464
+ if (opt?.onDestroy) {
465
+ for (let i = len; i < prev.value.length; i++) {
466
+ opt.onDestroy?.(prev.value[i]);
467
+ }
468
+ }
469
+ return slice;
377
470
  }
378
471
  else {
379
472
  const next = [...prev.value];
@@ -387,57 +480,6 @@ function mapArray(source, map, opt) {
387
480
  });
388
481
  }
389
482
 
390
- const { is } = Object;
391
- function mutable(initial, opt) {
392
- const baseEqual = opt?.equal ?? is;
393
- let trigger = false;
394
- const equal = (a, b) => {
395
- if (trigger)
396
- return false;
397
- return baseEqual(a, b);
398
- };
399
- const sig = signal(initial, {
400
- ...opt,
401
- equal,
402
- });
403
- const internalUpdate = sig.update;
404
- sig.mutate = (updater) => {
405
- trigger = true;
406
- internalUpdate(updater);
407
- trigger = false;
408
- };
409
- sig.inline = (updater) => {
410
- sig.mutate((prev) => {
411
- updater(prev);
412
- return prev;
413
- });
414
- };
415
- return sig;
416
- }
417
- /**
418
- * Type guard function to check if a given `WritableSignal` is a `MutableSignal`. This is useful
419
- * for situations where you need to conditionally use the `mutate` or `inline` methods.
420
- *
421
- * @typeParam T - The type of the signal's value (optional, defaults to `any`).
422
- * @param value - The `WritableSignal` to check.
423
- * @returns `true` if the signal is a `MutableSignal`, `false` otherwise.
424
- *
425
- * @example
426
- * const mySignal = signal(0);
427
- * const myMutableSignal = mutable(0);
428
- *
429
- * if (isMutable(mySignal)) {
430
- * mySignal.mutate(x => x + 1); // This would cause a type error, as mySignal is not a MutableSignal.
431
- * }
432
- *
433
- * if (isMutable(myMutableSignal)) {
434
- * myMutableSignal.mutate(x => x + 1); // This is safe.
435
- * }
436
- */
437
- function isMutable(value) {
438
- return 'mutate' in value && typeof value.mutate === 'function';
439
- }
440
-
441
483
  /**
442
484
  * Creates a read-only signal that reactively tracks whether a CSS media query
443
485
  * string currently matches.
@@ -486,7 +528,7 @@ function mediaQuery(query, debugName) {
486
528
  if (isPlatformServer(inject(PLATFORM_ID)))
487
529
  return computed(() => false, { debugName });
488
530
  const mediaQueryList = window.matchMedia(query);
489
- const state = signal(mediaQueryList.matches, { debugName });
531
+ const state = signal(mediaQueryList.matches, ...(ngDevMode ? [{ debugName: "state", debugName }] : [{ debugName }]));
490
532
  const handleChange = (event) => {
491
533
  state.set(event.matches);
492
534
  };
@@ -596,7 +638,7 @@ function throttled(initial, opt) {
596
638
  */
597
639
  function throttle(source, opt) {
598
640
  const ms = opt?.ms ?? 0;
599
- const trigger = signal(false);
641
+ const trigger = signal(false, ...(ngDevMode ? [{ debugName: "trigger" }] : []));
600
642
  let timeout;
601
643
  try {
602
644
  const destroyRef = opt?.destroyRef ?? inject(DestroyRef, { optional: true });
@@ -741,13 +783,13 @@ function networkStatus(debugName) {
741
783
  const sig = computed(() => true, {
742
784
  debugName,
743
785
  });
744
- sig.since = computed(() => serverDate);
786
+ sig.since = computed(() => serverDate, ...(ngDevMode ? [{ debugName: "since" }] : []));
745
787
  return sig;
746
788
  }
747
- const state = signal(navigator.onLine, {
748
- debugName,
749
- });
750
- const since = signal(new Date());
789
+ const state = signal(navigator.onLine, ...(ngDevMode ? [{ debugName: "state", debugName }] : [{
790
+ debugName,
791
+ }]));
792
+ const since = signal(new Date(), ...(ngDevMode ? [{ debugName: "since" }] : []));
751
793
  const goOnline = () => {
752
794
  state.set(true);
753
795
  since.set(new Date());
@@ -807,7 +849,7 @@ function pageVisibility(debugName) {
807
849
  if (isPlatformServer(inject(PLATFORM_ID))) {
808
850
  return computed(() => 'visible', { debugName });
809
851
  }
810
- const visibility = signal(document.visibilityState, { debugName });
852
+ const visibility = signal(document.visibilityState, ...(ngDevMode ? [{ debugName: "visibility", debugName }] : [{ debugName }]));
811
853
  const onVisibilityChange = () => visibility.set(document.visibilityState);
812
854
  document.addEventListener('visibilitychange', onVisibilityChange);
813
855
  inject(DestroyRef).onDestroy(() => document.removeEventListener('visibilitychange', onVisibilityChange));
@@ -1103,16 +1145,23 @@ function stored(fallback, { key, store: providedStore, serialize = JSON.stringif
1103
1145
  equal,
1104
1146
  };
1105
1147
  const initialKey = untracked(keySig);
1106
- const internal = signal(getValue(initialKey), {
1107
- ...opt,
1108
- equal: (a, b) => {
1109
- if (a === null && b === null)
1110
- return true;
1111
- if (a === null || b === null)
1112
- return false;
1113
- return equal(a, b);
1114
- },
1115
- });
1148
+ const internal = signal(getValue(initialKey), ...(ngDevMode ? [{ debugName: "internal", ...opt,
1149
+ equal: (a, b) => {
1150
+ if (a === null && b === null)
1151
+ return true;
1152
+ if (a === null || b === null)
1153
+ return false;
1154
+ return equal(a, b);
1155
+ } }] : [{
1156
+ ...opt,
1157
+ equal: (a, b) => {
1158
+ if (a === null && b === null)
1159
+ return true;
1160
+ if (a === null || b === null)
1161
+ return false;
1162
+ return equal(a, b);
1163
+ },
1164
+ }]));
1116
1165
  let prevKey = initialKey;
1117
1166
  if (onKeyChange === 'store') {
1118
1167
  effect(() => {
@@ -1381,9 +1430,9 @@ function withHistory(source, opt) {
1381
1430
  history.set([]);
1382
1431
  redoArray.set([]);
1383
1432
  };
1384
- internal.canUndo = computed(() => history().length > 0);
1385
- internal.canRedo = computed(() => redoArray().length > 0);
1386
- internal.canClear = computed(() => internal.canUndo() || internal.canRedo());
1433
+ internal.canUndo = computed(() => history().length > 0, ...(ngDevMode ? [{ debugName: "canUndo" }] : []));
1434
+ internal.canRedo = computed(() => redoArray().length > 0, ...(ngDevMode ? [{ debugName: "canRedo" }] : []));
1435
+ internal.canClear = computed(() => internal.canUndo() || internal.canRedo(), ...(ngDevMode ? [{ debugName: "canClear" }] : []));
1387
1436
  return internal;
1388
1437
  }
1389
1438