@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.
- package/fesm2022/mmstack-primitives.mjs +137 -88
- package/fesm2022/mmstack-primitives.mjs.map +1 -1
- package/index.d.ts +121 -96
- package/package.json +3 -3
|
@@ -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
|
-
? (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
return
|
|
1113
|
-
|
|
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
|
|