@mmstack/primitives 21.0.20 → 21.0.22
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/README.md +71 -12
- package/fesm2022/mmstack-primitives.mjs +114 -23
- package/fesm2022/mmstack-primitives.mjs.map +1 -1
- package/package.json +2 -1
- package/types/mmstack-primitives.d.ts +124 -3
package/README.md
CHANGED
|
@@ -29,6 +29,7 @@ This library provides the following primitives:
|
|
|
29
29
|
- `toWritable` - Converts a read-only signal to writable using custom write logic.
|
|
30
30
|
- `derived` - Creates a signal with two-way binding to a source signal.
|
|
31
31
|
- `chunked` - Creates a signal that time-slices an array into chunked values & emits thats array based on the provided options.
|
|
32
|
+
- `pooled` / `pooledArray` / `pooledMap` / `pooledSet` - Double-buffered object pools for `computed` signals; recycle the output container to remove allocation pressure in high-frequency recomputation.
|
|
32
33
|
- `tabSync` - Low level primitive to "share" the value of a WritableSignal accross tabs via the BroadcastChannel api.
|
|
33
34
|
- `sensor` - A facade function to create various reactive sensor signals (e.g., mouse position, network status, page visibility, dark mode preference)." (This was the suggestion from before; it just reads a little smoother and more accurately reflects what the facade creates directly).
|
|
34
35
|
- `mediaQuery` - A generic primitive that tracks a CSS media query (forms the basis for `prefersDarkMode` and `prefersReducedMotion`).
|
|
@@ -94,7 +95,6 @@ import { JsonPipe } from '@angular/common';
|
|
|
94
95
|
|
|
95
96
|
@Component({
|
|
96
97
|
selector: 'app-throttle-demo',
|
|
97
|
-
standalone: true,
|
|
98
98
|
imports: [JsonPipe],
|
|
99
99
|
template: `
|
|
100
100
|
<div (mousemove)="onMouseMove($event)" style="width: 300px; height: 200px; border: 1px solid black; padding: 10px; user-select: none;">Move mouse here to see updates...</div>
|
|
@@ -196,7 +196,6 @@ import { stored } from '@mmstack/primitives';
|
|
|
196
196
|
|
|
197
197
|
@Component({
|
|
198
198
|
selector: 'app-theme-selector',
|
|
199
|
-
standalone: true,
|
|
200
199
|
// imports: [FormsModule], // Import if using ngModel
|
|
201
200
|
template: `
|
|
202
201
|
Theme:
|
|
@@ -274,7 +273,6 @@ import { JsonPipe } from '@angular/common';
|
|
|
274
273
|
|
|
275
274
|
@Component({
|
|
276
275
|
selector: 'app-store-demo',
|
|
277
|
-
standalone: true,
|
|
278
276
|
imports: [FormsModule, JsonPipe],
|
|
279
277
|
template: `
|
|
280
278
|
<h3>User Profile</h3>
|
|
@@ -658,6 +656,75 @@ export class HeavyListComponent {
|
|
|
658
656
|
}
|
|
659
657
|
```
|
|
660
658
|
|
|
659
|
+
### pooled / pooledArray / pooledMap / pooledSet
|
|
660
|
+
|
|
661
|
+
A double-buffered object pool for `computed` signal outputs. After a brief warm-up the pool reaches steady state with **zero allocations per recomputation** — two buffers are swapped on every read, with `reset` invoked before each `computation`. Each read returns a different identity from the previous read, so default `Object.is` equality still flags changes correctly. Most users will reach for the preset helpers (`pooledArray`, `pooledMap`, `pooledSet`); drop down to `pooled` only when you need a custom buffer type.
|
|
662
|
+
|
|
663
|
+
> **Retention contract:** the value returned from a pooled signal is only valid until the next read of that signal. The container is reused on the second-next read and will be `reset` first, mutating any reference you still hold. Do not store the result in component state, async closures, or anywhere outside the same reactive tick. Treat it as scratch output consumed synchronously.
|
|
664
|
+
|
|
665
|
+
Use these when a computed is recomputed at high frequency and produces a large allocation (filter/map outputs over big arrays, lookup indices, RAF-driven computeds). For typical UI computeds over small data, just use `computed` — the docs cost and footgun aren't worth saving an allocation that doesn't show up in a profile.
|
|
666
|
+
|
|
667
|
+
```typescript
|
|
668
|
+
import { Component, signal } from '@angular/core';
|
|
669
|
+
import { pooledArray, pooledMap, pooledSet } from '@mmstack/primitives';
|
|
670
|
+
|
|
671
|
+
@Component({
|
|
672
|
+
selector: 'app-pooled-demo',
|
|
673
|
+
template: `<p>Active: {{ activeIds().length }} / {{ items().length }}</p>`,
|
|
674
|
+
})
|
|
675
|
+
export class PooledDemoComponent {
|
|
676
|
+
readonly items = signal(Array.from({ length: 10_000 }, (_, i) => ({ id: i, active: i % 2 === 0 })));
|
|
677
|
+
|
|
678
|
+
// Recycles a single number[] across recomputations.
|
|
679
|
+
readonly activeIds = pooledArray<number[]>((buf) => {
|
|
680
|
+
for (const item of this.items()) {
|
|
681
|
+
if (item.active) buf.push(item.id);
|
|
682
|
+
}
|
|
683
|
+
return buf;
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
// Recycles a Map for fast id → item lookups.
|
|
687
|
+
readonly byId = pooledMap<Map<number, { id: number; active: boolean }>>((buf) => {
|
|
688
|
+
for (const item of this.items()) buf.set(item.id, item);
|
|
689
|
+
return buf;
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// Recycles a Set of distinct values.
|
|
693
|
+
readonly distinctFlags = pooledSet<Set<boolean>>((buf) => {
|
|
694
|
+
for (const item of this.items()) buf.add(item.active);
|
|
695
|
+
return buf;
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
Need a custom buffer type (typed array, your own struct)? Use `pooled` directly:
|
|
701
|
+
|
|
702
|
+
```typescript
|
|
703
|
+
import { signal } from '@angular/core';
|
|
704
|
+
import { pooled } from '@mmstack/primitives';
|
|
705
|
+
|
|
706
|
+
const source = signal<{ active: boolean }[]>([]);
|
|
707
|
+
|
|
708
|
+
// Pre-allocate both slots at construction (eager) — useful when create() is expensive.
|
|
709
|
+
const counters = pooled<{ total: number; active: number }>({
|
|
710
|
+
create: () => ({ total: 0, active: 0 }),
|
|
711
|
+
reset: (c) => {
|
|
712
|
+
c.total = 0;
|
|
713
|
+
c.active = 0;
|
|
714
|
+
},
|
|
715
|
+
computation: (c) => {
|
|
716
|
+
for (const item of source()) {
|
|
717
|
+
c.total++;
|
|
718
|
+
if (item.active) c.active++;
|
|
719
|
+
}
|
|
720
|
+
return c;
|
|
721
|
+
},
|
|
722
|
+
eager: true,
|
|
723
|
+
});
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
Complementary to `linkedSignal` (which carries previous *state* forward, not the *container*) and `chunked` (which time-slices large outputs across frames).
|
|
727
|
+
|
|
661
728
|
### tabSync
|
|
662
729
|
|
|
663
730
|
A low-level primitive that synchronizes a WritableSignal across multiple browser tabs or windows of the same application using the BroadcastChannel API. Used by the cache in @mmstack/resource & the stored signal.
|
|
@@ -681,7 +748,7 @@ import { tabSync } from '@mmstack/primitives';
|
|
|
681
748
|
template: `
|
|
682
749
|
<p>Open this page in two tabs!</p>
|
|
683
750
|
|
|
684
|
-
<button (click)="counter.update(n => n + 1)">Count: {{ counter() }}</button>
|
|
751
|
+
<button (click)="counter.update((n) => n + 1)">Count: {{ counter() }}</button>
|
|
685
752
|
|
|
686
753
|
<select [ngModel]="theme()" (ngModelChange)="theme.set($event)">
|
|
687
754
|
<option value="light">Light</option>
|
|
@@ -714,7 +781,6 @@ import { Component, signal, effect } from '@angular/core';
|
|
|
714
781
|
|
|
715
782
|
@Component({
|
|
716
783
|
selector: 'app-history-demo',
|
|
717
|
-
standalone: true,
|
|
718
784
|
imports: [FormsModule, JsonPipe],
|
|
719
785
|
template: `
|
|
720
786
|
<h4>Simple Text Editor</h4>
|
|
@@ -781,7 +847,6 @@ import { JsonPipe } from '@angular/common';
|
|
|
781
847
|
|
|
782
848
|
@Component({
|
|
783
849
|
selector: 'app-mouse-tracker',
|
|
784
|
-
standalone: true,
|
|
785
850
|
imports: [JsonPipe],
|
|
786
851
|
template: `
|
|
787
852
|
<div (mousemove)="onMouseMove($event)" style="width: 300px; height: 200px; border: 1px solid black; padding: 10px; user-select: none;">Move mouse here...</div>
|
|
@@ -819,7 +884,6 @@ import { DatePipe } from '@angular/common';
|
|
|
819
884
|
|
|
820
885
|
@Component({
|
|
821
886
|
selector: 'app-network-info',
|
|
822
|
-
standalone: true,
|
|
823
887
|
imports: [DatePipe],
|
|
824
888
|
template: `
|
|
825
889
|
@if (netStatus()) {
|
|
@@ -850,7 +914,6 @@ import { sensor } from '@mmstack/primitives'; // Or import { pageVisibility }
|
|
|
850
914
|
|
|
851
915
|
@Component({
|
|
852
916
|
selector: 'app-visibility-logger',
|
|
853
|
-
standalone: true,
|
|
854
917
|
template: `<p>Page is currently: {{ visibility() }}</p>`,
|
|
855
918
|
})
|
|
856
919
|
export class VisibilityLoggerComponent {
|
|
@@ -877,7 +940,6 @@ import { sensor } from '@mmstack/primitives'; // Or import { windowSize }
|
|
|
877
940
|
|
|
878
941
|
@Component({
|
|
879
942
|
selector: 'app-responsive-display',
|
|
880
|
-
standalone: true,
|
|
881
943
|
template: `
|
|
882
944
|
<p>Current Window Size: {{ winSize().width }}px x {{ winSize().height }}px</p>
|
|
883
945
|
<p>Unthrottled: W: {{ winSize.unthrottled().width }} H: {{ winSize.unthrottled().height }}</p>
|
|
@@ -911,7 +973,6 @@ import { JsonPipe } from '@angular/common';
|
|
|
911
973
|
|
|
912
974
|
@Component({
|
|
913
975
|
selector: 'app-scroll-indicator',
|
|
914
|
-
standalone: true,
|
|
915
976
|
imports: [JsonPipe],
|
|
916
977
|
template: `
|
|
917
978
|
<div style="height: 100px; border-bottom: 2px solid red; position: fixed; top: 0; left: 0; width: 100%; background: white; z-index: 10;">
|
|
@@ -945,7 +1006,6 @@ import { mediaQuery, prefersDarkMode, prefersReducedMotion } from '@mmstack/prim
|
|
|
945
1006
|
|
|
946
1007
|
@Component({
|
|
947
1008
|
selector: 'app-layout-checker',
|
|
948
|
-
standalone: true,
|
|
949
1009
|
template: `
|
|
950
1010
|
@if (isLargeScreen()) {
|
|
951
1011
|
<p>Using large screen layout.</p>
|
|
@@ -1007,7 +1067,6 @@ import { elementVisibility } from '@mmstack/primitives';
|
|
|
1007
1067
|
|
|
1008
1068
|
@Component({
|
|
1009
1069
|
selector: 'app-lazy-load-item',
|
|
1010
|
-
standalone: true,
|
|
1011
1070
|
template: `
|
|
1012
1071
|
<div #itemToObserve style="height: 300px; margin-top: 100vh; border: 2px solid green;">
|
|
1013
1072
|
@if (intersectionEntry.visible()) {
|
|
@@ -197,7 +197,7 @@ function chunked(source, options) {
|
|
|
197
197
|
return () => clearTimeout(num);
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
|
-
const internal = linkedSignal({ ...(ngDevMode ? { debugName: "internal" } : {}), source,
|
|
200
|
+
const internal = linkedSignal({ ...(ngDevMode ? { debugName: "internal" } : /* istanbul ignore next */ {}), source,
|
|
201
201
|
computation: (items) => items.slice(0, chunkSize),
|
|
202
202
|
equal });
|
|
203
203
|
nestedEffect((cleanup) => {
|
|
@@ -311,7 +311,7 @@ function debounced(initial, opt) {
|
|
|
311
311
|
*/
|
|
312
312
|
function debounce(source, opt) {
|
|
313
313
|
const ms = opt?.ms ?? 0;
|
|
314
|
-
const trigger = signal(false, ...(ngDevMode ? [{ debugName: "trigger" }] : []));
|
|
314
|
+
const trigger = signal(false, ...(ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []));
|
|
315
315
|
let timeout;
|
|
316
316
|
try {
|
|
317
317
|
const destroyRef = opt?.destroyRef ?? inject(DestroyRef, { optional: true });
|
|
@@ -540,7 +540,7 @@ function createItemSignal(source, index, setter, opt) {
|
|
|
540
540
|
}
|
|
541
541
|
function indexArray(source, map, opt = {}) {
|
|
542
542
|
const data = isSignal(source) ? source : computed(source);
|
|
543
|
-
const len = computed(() => data().length, ...(ngDevMode ? [{ debugName: "len" }] : []));
|
|
543
|
+
const len = computed(() => data().length, ...(ngDevMode ? [{ debugName: "len" }] : /* istanbul ignore next */ []));
|
|
544
544
|
const setter = createSetter(data);
|
|
545
545
|
const writableData = isWritableSignal(data)
|
|
546
546
|
? data
|
|
@@ -643,7 +643,7 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
643
643
|
for (j = 0; j < newLen; j++) {
|
|
644
644
|
item = newItems[j];
|
|
645
645
|
items[j] = item;
|
|
646
|
-
const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : []));
|
|
646
|
+
const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : /* istanbul ignore next */ []));
|
|
647
647
|
newIndexes[j] = indexSignal;
|
|
648
648
|
newMapped[j] = mapFn(item, indexSignal);
|
|
649
649
|
}
|
|
@@ -693,7 +693,7 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
693
693
|
newIndexes[j].set(j);
|
|
694
694
|
}
|
|
695
695
|
else {
|
|
696
|
-
const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : []));
|
|
696
|
+
const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : /* istanbul ignore next */ []));
|
|
697
697
|
newIndexes[j] = indexSignal;
|
|
698
698
|
newMapped[j] = mapFn(newItems[j], indexSignal);
|
|
699
699
|
}
|
|
@@ -840,6 +840,98 @@ function piped(initial, opt) {
|
|
|
840
840
|
return pipeable(signal(initial, opt));
|
|
841
841
|
}
|
|
842
842
|
|
|
843
|
+
/**
|
|
844
|
+
* A `Signal<U>` backed by a two-slot object pool: `create` is called at most
|
|
845
|
+
* twice over the pool's lifetime, and the two `T` instances are swapped on
|
|
846
|
+
* every recomputation with `reset` invoked on the dirty one before
|
|
847
|
+
* `computation` writes into it. Consecutive reads return different identities,
|
|
848
|
+
* so the default `Object.is` equality still flags changes.
|
|
849
|
+
*
|
|
850
|
+
* **Retention contract:** the returned value is only valid until the next
|
|
851
|
+
* recomputation of this signal. The container is recycled and `reset`,
|
|
852
|
+
* mutating any reference you still hold — do not store the result, pass it to
|
|
853
|
+
* async code, or hand it to consumers that outlive the current reactive tick.
|
|
854
|
+
*
|
|
855
|
+
* For collection buffers prefer the presets: {@link pooledArray},
|
|
856
|
+
* {@link pooledMap}, {@link pooledSet}.
|
|
857
|
+
*
|
|
858
|
+
* @see [Angular `linkedSignal`](https://angular.dev/api/core/linkedSignal) — carries previous *state* forward; complementary, not a substitute.
|
|
859
|
+
*
|
|
860
|
+
* @example
|
|
861
|
+
* ```ts
|
|
862
|
+
* const source = signal<{ active: boolean }[]>([]);
|
|
863
|
+
*
|
|
864
|
+
* const counters = pooled<{ total: number; active: number }>({
|
|
865
|
+
* create: () => ({ total: 0, active: 0 }),
|
|
866
|
+
* reset: (c) => { c.total = 0; c.active = 0; },
|
|
867
|
+
* computation: (c) => {
|
|
868
|
+
* for (const item of source()) { c.total++; if (item.active) c.active++; }
|
|
869
|
+
* return c;
|
|
870
|
+
* },
|
|
871
|
+
* });
|
|
872
|
+
* ```
|
|
873
|
+
*/
|
|
874
|
+
function pooled({ create, reset, computation, ...opt }) {
|
|
875
|
+
let other = opt.eager ? create() : undefined;
|
|
876
|
+
let current = opt.eager ? create() : undefined;
|
|
877
|
+
// only relevant for eager mode: the pre-allocated `current` is fresh until its first demote
|
|
878
|
+
let currentFresh = opt.eager;
|
|
879
|
+
return computed(() => {
|
|
880
|
+
const next = other ?? untracked(() => create());
|
|
881
|
+
if (current !== undefined) {
|
|
882
|
+
if (currentFresh) {
|
|
883
|
+
// never-mutated buffer leaving the active slot; nothing to clean
|
|
884
|
+
other = current;
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
// reset on release: clean the dirty buffer as it goes back into the pool
|
|
888
|
+
// (also threads the swap-return correctly into the pool's spare slot)
|
|
889
|
+
other = untracked(() => reset(current)) ?? current;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
current = next;
|
|
893
|
+
currentFresh = false;
|
|
894
|
+
return computation(next);
|
|
895
|
+
}, opt);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function toPooledOptions(optOrComputation, create, reset, signalOpt) {
|
|
899
|
+
const opt = typeof optOrComputation === 'object' ? optOrComputation : signalOpt;
|
|
900
|
+
const computation = typeof optOrComputation === 'function'
|
|
901
|
+
? optOrComputation
|
|
902
|
+
: optOrComputation.computation;
|
|
903
|
+
return {
|
|
904
|
+
create,
|
|
905
|
+
reset,
|
|
906
|
+
computation,
|
|
907
|
+
...opt,
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function createEmptyArray() {
|
|
911
|
+
return [];
|
|
912
|
+
}
|
|
913
|
+
function resetArray(arr) {
|
|
914
|
+
arr.length = 0;
|
|
915
|
+
}
|
|
916
|
+
function pooledArray(optOrComputation, signalOpt) {
|
|
917
|
+
return pooled(toPooledOptions(optOrComputation, createEmptyArray, resetArray, signalOpt));
|
|
918
|
+
}
|
|
919
|
+
function createEmptySet() {
|
|
920
|
+
return new Set();
|
|
921
|
+
}
|
|
922
|
+
function resetClearable(clearable) {
|
|
923
|
+
clearable.clear();
|
|
924
|
+
}
|
|
925
|
+
function pooledSet(optOrComputation, signalOpt) {
|
|
926
|
+
return pooled(toPooledOptions(optOrComputation, createEmptySet, resetClearable, signalOpt));
|
|
927
|
+
}
|
|
928
|
+
function createEmptyMap() {
|
|
929
|
+
return new Map();
|
|
930
|
+
}
|
|
931
|
+
function pooledMap(optOrComputation, signalOpt) {
|
|
932
|
+
return pooled(toPooledOptions(optOrComputation, createEmptyMap, resetClearable, signalOpt));
|
|
933
|
+
}
|
|
934
|
+
|
|
843
935
|
function observerSupported$1() {
|
|
844
936
|
return typeof ResizeObserver !== 'undefined';
|
|
845
937
|
}
|
|
@@ -999,7 +1091,7 @@ function elementVisibility(target = inject(ElementRef), opt) {
|
|
|
999
1091
|
const base = computed(() => undefined, {
|
|
1000
1092
|
debugName: opt?.debugName,
|
|
1001
1093
|
});
|
|
1002
|
-
base.visible = computed(() => false, ...(ngDevMode ? [{ debugName: "visible" }] : []));
|
|
1094
|
+
base.visible = computed(() => false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
1003
1095
|
return base;
|
|
1004
1096
|
}
|
|
1005
1097
|
const state = signal(undefined, {
|
|
@@ -1036,7 +1128,7 @@ function elementVisibility(target = inject(ElementRef), opt) {
|
|
|
1036
1128
|
if (!s)
|
|
1037
1129
|
return false;
|
|
1038
1130
|
return s.isIntersecting;
|
|
1039
|
-
}, ...(ngDevMode ? [{ debugName: "visible" }] : []));
|
|
1131
|
+
}, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
1040
1132
|
return base;
|
|
1041
1133
|
}
|
|
1042
1134
|
|
|
@@ -1201,7 +1293,7 @@ function throttled(initial, opt) {
|
|
|
1201
1293
|
*/
|
|
1202
1294
|
function throttle(source, opt) {
|
|
1203
1295
|
const ms = opt?.ms ?? 0;
|
|
1204
|
-
const trigger = signal(false, ...(ngDevMode ? [{ debugName: "trigger" }] : []));
|
|
1296
|
+
const trigger = signal(false, ...(ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []));
|
|
1205
1297
|
let timeout;
|
|
1206
1298
|
try {
|
|
1207
1299
|
const destroyRef = opt?.destroyRef ?? inject(DestroyRef, { optional: true });
|
|
@@ -1347,11 +1439,11 @@ function networkStatus(debugName = 'networkStatus') {
|
|
|
1347
1439
|
const sig = computed(() => true, {
|
|
1348
1440
|
debugName,
|
|
1349
1441
|
});
|
|
1350
|
-
sig.since = computed(() => serverDate, ...(ngDevMode ? [{ debugName: "since" }] : []));
|
|
1442
|
+
sig.since = computed(() => serverDate, ...(ngDevMode ? [{ debugName: "since" }] : /* istanbul ignore next */ []));
|
|
1351
1443
|
return sig;
|
|
1352
1444
|
}
|
|
1353
|
-
const state = signal(navigator.onLine, { ...(ngDevMode ? { debugName: "state" } : {}), debugName });
|
|
1354
|
-
const since = signal(new Date(), ...(ngDevMode ? [{ debugName: "since" }] : []));
|
|
1445
|
+
const state = signal(navigator.onLine, { ...(ngDevMode ? { debugName: "state" } : /* istanbul ignore next */ {}), debugName });
|
|
1446
|
+
const since = signal(new Date(), ...(ngDevMode ? [{ debugName: "since" }] : /* istanbul ignore next */ []));
|
|
1355
1447
|
const goOnline = () => {
|
|
1356
1448
|
state.set(true);
|
|
1357
1449
|
since.set(new Date());
|
|
@@ -1411,7 +1503,7 @@ function pageVisibility(debugName = 'pageVisibility') {
|
|
|
1411
1503
|
if (isPlatformServer(inject(PLATFORM_ID))) {
|
|
1412
1504
|
return computed(() => 'visible', { debugName });
|
|
1413
1505
|
}
|
|
1414
|
-
const visibility = signal(document.visibilityState, { ...(ngDevMode ? { debugName: "visibility" } : {}), debugName });
|
|
1506
|
+
const visibility = signal(document.visibilityState, { ...(ngDevMode ? { debugName: "visibility" } : /* istanbul ignore next */ {}), debugName });
|
|
1415
1507
|
const onVisibilityChange = () => visibility.set(document.visibilityState);
|
|
1416
1508
|
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
1417
1509
|
inject(DestroyRef).onDestroy(() => document.removeEventListener('visibilitychange', onVisibilityChange));
|
|
@@ -1675,7 +1767,7 @@ function toArrayStore(source, injector) {
|
|
|
1675
1767
|
if (!Array.isArray(v))
|
|
1676
1768
|
return 0;
|
|
1677
1769
|
return v.length;
|
|
1678
|
-
}, ...(ngDevMode ? [{ debugName: "lengthSignal" }] : []));
|
|
1770
|
+
}, ...(ngDevMode ? [{ debugName: "lengthSignal" }] : /* istanbul ignore next */ []));
|
|
1679
1771
|
return new Proxy(source, {
|
|
1680
1772
|
has(_, prop) {
|
|
1681
1773
|
if (prop === 'length')
|
|
@@ -1940,7 +2032,6 @@ const noopStore = {
|
|
|
1940
2032
|
*
|
|
1941
2033
|
* @Component({
|
|
1942
2034
|
* selector: 'app-settings',
|
|
1943
|
-
* standalone: true,
|
|
1944
2035
|
* template: `
|
|
1945
2036
|
* Theme:
|
|
1946
2037
|
* <select [ngModel]="theme()" (ngModelChange)="theme.set($event)">
|
|
@@ -1998,7 +2089,7 @@ function stored(fallback, { key, store: providedStore, serialize = JSON.stringif
|
|
|
1998
2089
|
equal,
|
|
1999
2090
|
};
|
|
2000
2091
|
const initialKey = untracked(keySig);
|
|
2001
|
-
const internal = signal(getValue(initialKey), { ...(ngDevMode ? { debugName: "internal" } : {}), ...opt,
|
|
2092
|
+
const internal = signal(getValue(initialKey), { ...(ngDevMode ? { debugName: "internal" } : /* istanbul ignore next */ {}), ...opt,
|
|
2002
2093
|
equal: (a, b) => {
|
|
2003
2094
|
if (a === null && b === null)
|
|
2004
2095
|
return true;
|
|
@@ -2083,10 +2174,10 @@ class MessageBus {
|
|
|
2083
2174
|
this.channel.removeEventListener('message', listener);
|
|
2084
2175
|
this.listeners.delete(id);
|
|
2085
2176
|
}
|
|
2086
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
2087
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
2177
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: MessageBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2178
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: MessageBus, providedIn: 'root' });
|
|
2088
2179
|
}
|
|
2089
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2180
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: MessageBus, decorators: [{
|
|
2090
2181
|
type: Injectable,
|
|
2091
2182
|
args: [{
|
|
2092
2183
|
providedIn: 'root',
|
|
@@ -2163,7 +2254,7 @@ function tabSync(sig, opt) {
|
|
|
2163
2254
|
return;
|
|
2164
2255
|
}
|
|
2165
2256
|
post(val);
|
|
2166
|
-
}, ...(ngDevMode ? [{ debugName: "effectRef" }] : []));
|
|
2257
|
+
}, ...(ngDevMode ? [{ debugName: "effectRef" }] : /* istanbul ignore next */ []));
|
|
2167
2258
|
inject(DestroyRef).onDestroy(() => {
|
|
2168
2259
|
effectRef.destroy();
|
|
2169
2260
|
unsub();
|
|
@@ -2359,9 +2450,9 @@ function withHistory(source, opt) {
|
|
|
2359
2450
|
history.set([]);
|
|
2360
2451
|
redoArray.set([]);
|
|
2361
2452
|
};
|
|
2362
|
-
internal.canUndo = computed(() => history().length > 0, ...(ngDevMode ? [{ debugName: "canUndo" }] : []));
|
|
2363
|
-
internal.canRedo = computed(() => redoArray().length > 0, ...(ngDevMode ? [{ debugName: "canRedo" }] : []));
|
|
2364
|
-
internal.canClear = computed(() => internal.canUndo() || internal.canRedo(), ...(ngDevMode ? [{ debugName: "canClear" }] : []));
|
|
2453
|
+
internal.canUndo = computed(() => history().length > 0, ...(ngDevMode ? [{ debugName: "canUndo" }] : /* istanbul ignore next */ []));
|
|
2454
|
+
internal.canRedo = computed(() => redoArray().length > 0, ...(ngDevMode ? [{ debugName: "canRedo" }] : /* istanbul ignore next */ []));
|
|
2455
|
+
internal.canClear = computed(() => internal.canUndo() || internal.canRedo(), ...(ngDevMode ? [{ debugName: "canClear" }] : /* istanbul ignore next */ []));
|
|
2365
2456
|
return internal;
|
|
2366
2457
|
}
|
|
2367
2458
|
|
|
@@ -2369,5 +2460,5 @@ function withHistory(source, opt) {
|
|
|
2369
2460
|
* Generated bundle index. Do not edit.
|
|
2370
2461
|
*/
|
|
2371
2462
|
|
|
2372
|
-
export { chunked, combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, indexArray, isDerivation, isMutable, isStore, keyArray, map, mapArray, mapObject, mediaQuery, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, pageVisibility, pipeable, piped, prefersDarkMode, prefersReducedMotion, scrollPosition, select, sensor, sensors, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, windowSize, withHistory };
|
|
2463
|
+
export { chunked, combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, indexArray, isDerivation, isMutable, isStore, keyArray, map, mapArray, mapObject, mediaQuery, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, pageVisibility, pipeable, piped, pooled, pooledArray, pooledMap, pooledSet, prefersDarkMode, prefersReducedMotion, scrollPosition, select, sensor, sensors, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, windowSize, withHistory };
|
|
2373
2464
|
//# sourceMappingURL=mmstack-primitives.mjs.map
|