@mmstack/primitives 21.0.19 → 21.0.21
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 +72 -12
- package/fesm2022/mmstack-primitives.mjs +130 -29
- package/fesm2022/mmstack-primitives.mjs.map +1 -1
- package/package.json +4 -2
- 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`).
|
|
@@ -59,6 +60,7 @@ import { FormsModule } from '@angular/forms';
|
|
|
59
60
|
})
|
|
60
61
|
export class SearchComponent {
|
|
61
62
|
searchTerm = debounced('', { ms: 300 }); // Debounce for 300ms
|
|
63
|
+
example2 = debounce(signal(''), { ms: 300 }); // pattern for adding debounce to an existing signal
|
|
62
64
|
|
|
63
65
|
constructor() {
|
|
64
66
|
effect(() => {
|
|
@@ -93,7 +95,6 @@ import { JsonPipe } from '@angular/common';
|
|
|
93
95
|
|
|
94
96
|
@Component({
|
|
95
97
|
selector: 'app-throttle-demo',
|
|
96
|
-
standalone: true,
|
|
97
98
|
imports: [JsonPipe],
|
|
98
99
|
template: `
|
|
99
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>
|
|
@@ -195,7 +196,6 @@ import { stored } from '@mmstack/primitives';
|
|
|
195
196
|
|
|
196
197
|
@Component({
|
|
197
198
|
selector: 'app-theme-selector',
|
|
198
|
-
standalone: true,
|
|
199
199
|
// imports: [FormsModule], // Import if using ngModel
|
|
200
200
|
template: `
|
|
201
201
|
Theme:
|
|
@@ -273,7 +273,6 @@ import { JsonPipe } from '@angular/common';
|
|
|
273
273
|
|
|
274
274
|
@Component({
|
|
275
275
|
selector: 'app-store-demo',
|
|
276
|
-
standalone: true,
|
|
277
276
|
imports: [FormsModule, JsonPipe],
|
|
278
277
|
template: `
|
|
279
278
|
<h3>User Profile</h3>
|
|
@@ -657,6 +656,75 @@ export class HeavyListComponent {
|
|
|
657
656
|
}
|
|
658
657
|
```
|
|
659
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
|
+
|
|
660
728
|
### tabSync
|
|
661
729
|
|
|
662
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.
|
|
@@ -680,7 +748,7 @@ import { tabSync } from '@mmstack/primitives';
|
|
|
680
748
|
template: `
|
|
681
749
|
<p>Open this page in two tabs!</p>
|
|
682
750
|
|
|
683
|
-
<button (click)="counter.update(n => n + 1)">Count: {{ counter() }}</button>
|
|
751
|
+
<button (click)="counter.update((n) => n + 1)">Count: {{ counter() }}</button>
|
|
684
752
|
|
|
685
753
|
<select [ngModel]="theme()" (ngModelChange)="theme.set($event)">
|
|
686
754
|
<option value="light">Light</option>
|
|
@@ -713,7 +781,6 @@ import { Component, signal, effect } from '@angular/core';
|
|
|
713
781
|
|
|
714
782
|
@Component({
|
|
715
783
|
selector: 'app-history-demo',
|
|
716
|
-
standalone: true,
|
|
717
784
|
imports: [FormsModule, JsonPipe],
|
|
718
785
|
template: `
|
|
719
786
|
<h4>Simple Text Editor</h4>
|
|
@@ -780,7 +847,6 @@ import { JsonPipe } from '@angular/common';
|
|
|
780
847
|
|
|
781
848
|
@Component({
|
|
782
849
|
selector: 'app-mouse-tracker',
|
|
783
|
-
standalone: true,
|
|
784
850
|
imports: [JsonPipe],
|
|
785
851
|
template: `
|
|
786
852
|
<div (mousemove)="onMouseMove($event)" style="width: 300px; height: 200px; border: 1px solid black; padding: 10px; user-select: none;">Move mouse here...</div>
|
|
@@ -818,7 +884,6 @@ import { DatePipe } from '@angular/common';
|
|
|
818
884
|
|
|
819
885
|
@Component({
|
|
820
886
|
selector: 'app-network-info',
|
|
821
|
-
standalone: true,
|
|
822
887
|
imports: [DatePipe],
|
|
823
888
|
template: `
|
|
824
889
|
@if (netStatus()) {
|
|
@@ -849,7 +914,6 @@ import { sensor } from '@mmstack/primitives'; // Or import { pageVisibility }
|
|
|
849
914
|
|
|
850
915
|
@Component({
|
|
851
916
|
selector: 'app-visibility-logger',
|
|
852
|
-
standalone: true,
|
|
853
917
|
template: `<p>Page is currently: {{ visibility() }}</p>`,
|
|
854
918
|
})
|
|
855
919
|
export class VisibilityLoggerComponent {
|
|
@@ -876,7 +940,6 @@ import { sensor } from '@mmstack/primitives'; // Or import { windowSize }
|
|
|
876
940
|
|
|
877
941
|
@Component({
|
|
878
942
|
selector: 'app-responsive-display',
|
|
879
|
-
standalone: true,
|
|
880
943
|
template: `
|
|
881
944
|
<p>Current Window Size: {{ winSize().width }}px x {{ winSize().height }}px</p>
|
|
882
945
|
<p>Unthrottled: W: {{ winSize.unthrottled().width }} H: {{ winSize.unthrottled().height }}</p>
|
|
@@ -910,7 +973,6 @@ import { JsonPipe } from '@angular/common';
|
|
|
910
973
|
|
|
911
974
|
@Component({
|
|
912
975
|
selector: 'app-scroll-indicator',
|
|
913
|
-
standalone: true,
|
|
914
976
|
imports: [JsonPipe],
|
|
915
977
|
template: `
|
|
916
978
|
<div style="height: 100px; border-bottom: 2px solid red; position: fixed; top: 0; left: 0; width: 100%; background: white; z-index: 10;">
|
|
@@ -944,7 +1006,6 @@ import { mediaQuery, prefersDarkMode, prefersReducedMotion } from '@mmstack/prim
|
|
|
944
1006
|
|
|
945
1007
|
@Component({
|
|
946
1008
|
selector: 'app-layout-checker',
|
|
947
|
-
standalone: true,
|
|
948
1009
|
template: `
|
|
949
1010
|
@if (isLargeScreen()) {
|
|
950
1011
|
<p>Using large screen layout.</p>
|
|
@@ -1006,7 +1067,6 @@ import { elementVisibility } from '@mmstack/primitives';
|
|
|
1006
1067
|
|
|
1007
1068
|
@Component({
|
|
1008
1069
|
selector: 'app-lazy-load-item',
|
|
1009
|
-
standalone: true,
|
|
1010
1070
|
template: `
|
|
1011
1071
|
<div #itemToObserve style="height: 300px; margin-top: 100vh; border: 2px solid green;">
|
|
1012
1072
|
@if (intersectionEntry.visible()) {
|
|
@@ -138,6 +138,9 @@ function nestedEffect(effectFn, options) {
|
|
|
138
138
|
manualCleanup: options?.manualCleanup ?? !!parent,
|
|
139
139
|
});
|
|
140
140
|
});
|
|
141
|
+
let unregisterCleanup;
|
|
142
|
+
if (!parent && !options?.manualCleanup)
|
|
143
|
+
unregisterCleanup = injector.get(DestroyRef).onDestroy(() => ref.destroy());
|
|
141
144
|
const ref = {
|
|
142
145
|
destroy: () => {
|
|
143
146
|
if (isDestroyed)
|
|
@@ -145,10 +148,10 @@ function nestedEffect(effectFn, options) {
|
|
|
145
148
|
isDestroyed = true;
|
|
146
149
|
parent?.children.delete(ref);
|
|
147
150
|
srcRef.destroy();
|
|
151
|
+
unregisterCleanup?.();
|
|
148
152
|
},
|
|
149
153
|
};
|
|
150
154
|
parent?.children.add(ref);
|
|
151
|
-
injector.get(DestroyRef).onDestroy(() => ref.destroy());
|
|
152
155
|
return ref;
|
|
153
156
|
}
|
|
154
157
|
|
|
@@ -194,7 +197,7 @@ function chunked(source, options) {
|
|
|
194
197
|
return () => clearTimeout(num);
|
|
195
198
|
};
|
|
196
199
|
}
|
|
197
|
-
const internal = linkedSignal({ ...(ngDevMode ? { debugName: "internal" } : {}), source,
|
|
200
|
+
const internal = linkedSignal({ ...(ngDevMode ? { debugName: "internal" } : /* istanbul ignore next */ {}), source,
|
|
198
201
|
computation: (items) => items.slice(0, chunkSize),
|
|
199
202
|
equal });
|
|
200
203
|
nestedEffect((cleanup) => {
|
|
@@ -308,7 +311,7 @@ function debounced(initial, opt) {
|
|
|
308
311
|
*/
|
|
309
312
|
function debounce(source, opt) {
|
|
310
313
|
const ms = opt?.ms ?? 0;
|
|
311
|
-
const trigger = signal(false, ...(ngDevMode ? [{ debugName: "trigger" }] : []));
|
|
314
|
+
const trigger = signal(false, ...(ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []));
|
|
312
315
|
let timeout;
|
|
313
316
|
try {
|
|
314
317
|
const destroyRef = opt?.destroyRef ?? inject(DestroyRef, { optional: true });
|
|
@@ -537,7 +540,7 @@ function createItemSignal(source, index, setter, opt) {
|
|
|
537
540
|
}
|
|
538
541
|
function indexArray(source, map, opt = {}) {
|
|
539
542
|
const data = isSignal(source) ? source : computed(source);
|
|
540
|
-
const len = computed(() => data().length, ...(ngDevMode ? [{ debugName: "len" }] : []));
|
|
543
|
+
const len = computed(() => data().length, ...(ngDevMode ? [{ debugName: "len" }] : /* istanbul ignore next */ []));
|
|
541
544
|
const setter = createSetter(data);
|
|
542
545
|
const writableData = isWritableSignal(data)
|
|
543
546
|
? data
|
|
@@ -640,7 +643,7 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
640
643
|
for (j = 0; j < newLen; j++) {
|
|
641
644
|
item = newItems[j];
|
|
642
645
|
items[j] = item;
|
|
643
|
-
const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : []));
|
|
646
|
+
const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : /* istanbul ignore next */ []));
|
|
644
647
|
newIndexes[j] = indexSignal;
|
|
645
648
|
newMapped[j] = mapFn(item, indexSignal);
|
|
646
649
|
}
|
|
@@ -663,14 +666,14 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
663
666
|
for (j = newEnd; j >= start; j--) {
|
|
664
667
|
item = newItems[j];
|
|
665
668
|
key = getKey(item);
|
|
666
|
-
i = newIndices.get(key);
|
|
669
|
+
i = newIndices.get(key) ?? -1;
|
|
667
670
|
newIndicesNext[j] = i === undefined ? -1 : i;
|
|
668
671
|
newIndices.set(key, j);
|
|
669
672
|
}
|
|
670
673
|
for (i = start; i <= end; i++) {
|
|
671
674
|
item = items[i];
|
|
672
675
|
key = getKey(item);
|
|
673
|
-
j = newIndices.get(key);
|
|
676
|
+
j = newIndices.get(key) ?? -1;
|
|
674
677
|
if (j !== undefined && j !== -1) {
|
|
675
678
|
temp[j] = mapped[i];
|
|
676
679
|
tempIndexes[j] = indexes[i];
|
|
@@ -690,7 +693,7 @@ function keyArray(source, mapFn, options = {}) {
|
|
|
690
693
|
newIndexes[j].set(j);
|
|
691
694
|
}
|
|
692
695
|
else {
|
|
693
|
-
const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : []));
|
|
696
|
+
const indexSignal = signal(j, ...(ngDevMode ? [{ debugName: "indexSignal" }] : /* istanbul ignore next */ []));
|
|
694
697
|
newIndexes[j] = indexSignal;
|
|
695
698
|
newMapped[j] = mapFn(newItems[j], indexSignal);
|
|
696
699
|
}
|
|
@@ -837,6 +840,102 @@ function piped(initial, opt) {
|
|
|
837
840
|
return pipeable(signal(initial, opt));
|
|
838
841
|
}
|
|
839
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
|
+
let otherFresh = opt.eager;
|
|
878
|
+
let currentFresh = opt.eager;
|
|
879
|
+
return computed(() => {
|
|
880
|
+
let next;
|
|
881
|
+
let nextFresh;
|
|
882
|
+
if (other !== undefined) {
|
|
883
|
+
next = other;
|
|
884
|
+
nextFresh = !!otherFresh;
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
next = untracked(() => create());
|
|
888
|
+
nextFresh = true;
|
|
889
|
+
}
|
|
890
|
+
if (current !== undefined) {
|
|
891
|
+
other = current;
|
|
892
|
+
otherFresh = currentFresh;
|
|
893
|
+
}
|
|
894
|
+
current = next;
|
|
895
|
+
// the buffer is about to be mutated by `computation`, so it's no longer fresh
|
|
896
|
+
currentFresh = false;
|
|
897
|
+
const clean = nextFresh ? next : (untracked(() => reset(next)) ?? next);
|
|
898
|
+
return computation(clean);
|
|
899
|
+
}, opt);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function toPooledOptions(optOrComputation, create, reset, signalOpt) {
|
|
903
|
+
const opt = typeof optOrComputation === 'object' ? optOrComputation : signalOpt;
|
|
904
|
+
const computation = typeof optOrComputation === 'function'
|
|
905
|
+
? optOrComputation
|
|
906
|
+
: optOrComputation.computation;
|
|
907
|
+
return {
|
|
908
|
+
create,
|
|
909
|
+
reset,
|
|
910
|
+
computation,
|
|
911
|
+
...opt,
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
function createEmptyArray() {
|
|
915
|
+
return [];
|
|
916
|
+
}
|
|
917
|
+
function resetArray(arr) {
|
|
918
|
+
arr.length = 0;
|
|
919
|
+
}
|
|
920
|
+
function pooledArray(optOrComputation, signalOpt) {
|
|
921
|
+
return pooled(toPooledOptions(optOrComputation, createEmptyArray, resetArray, signalOpt));
|
|
922
|
+
}
|
|
923
|
+
function createEmptySet() {
|
|
924
|
+
return new Set();
|
|
925
|
+
}
|
|
926
|
+
function resetClearable(clearable) {
|
|
927
|
+
clearable.clear();
|
|
928
|
+
}
|
|
929
|
+
function pooledSet(optOrComputation, signalOpt) {
|
|
930
|
+
return pooled(toPooledOptions(optOrComputation, createEmptySet, resetClearable, signalOpt));
|
|
931
|
+
}
|
|
932
|
+
function createEmptyMap() {
|
|
933
|
+
return new Map();
|
|
934
|
+
}
|
|
935
|
+
function pooledMap(optOrComputation, signalOpt) {
|
|
936
|
+
return pooled(toPooledOptions(optOrComputation, createEmptyMap, resetClearable, signalOpt));
|
|
937
|
+
}
|
|
938
|
+
|
|
840
939
|
function observerSupported$1() {
|
|
841
940
|
return typeof ResizeObserver !== 'undefined';
|
|
842
941
|
}
|
|
@@ -996,7 +1095,7 @@ function elementVisibility(target = inject(ElementRef), opt) {
|
|
|
996
1095
|
const base = computed(() => undefined, {
|
|
997
1096
|
debugName: opt?.debugName,
|
|
998
1097
|
});
|
|
999
|
-
base.visible = computed(() => false, ...(ngDevMode ? [{ debugName: "visible" }] : []));
|
|
1098
|
+
base.visible = computed(() => false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
1000
1099
|
return base;
|
|
1001
1100
|
}
|
|
1002
1101
|
const state = signal(undefined, {
|
|
@@ -1033,7 +1132,7 @@ function elementVisibility(target = inject(ElementRef), opt) {
|
|
|
1033
1132
|
if (!s)
|
|
1034
1133
|
return false;
|
|
1035
1134
|
return s.isIntersecting;
|
|
1036
|
-
}, ...(ngDevMode ? [{ debugName: "visible" }] : []));
|
|
1135
|
+
}, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
1037
1136
|
return base;
|
|
1038
1137
|
}
|
|
1039
1138
|
|
|
@@ -1198,7 +1297,7 @@ function throttled(initial, opt) {
|
|
|
1198
1297
|
*/
|
|
1199
1298
|
function throttle(source, opt) {
|
|
1200
1299
|
const ms = opt?.ms ?? 0;
|
|
1201
|
-
const trigger = signal(false, ...(ngDevMode ? [{ debugName: "trigger" }] : []));
|
|
1300
|
+
const trigger = signal(false, ...(ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []));
|
|
1202
1301
|
let timeout;
|
|
1203
1302
|
try {
|
|
1204
1303
|
const destroyRef = opt?.destroyRef ?? inject(DestroyRef, { optional: true });
|
|
@@ -1344,11 +1443,11 @@ function networkStatus(debugName = 'networkStatus') {
|
|
|
1344
1443
|
const sig = computed(() => true, {
|
|
1345
1444
|
debugName,
|
|
1346
1445
|
});
|
|
1347
|
-
sig.since = computed(() => serverDate, ...(ngDevMode ? [{ debugName: "since" }] : []));
|
|
1446
|
+
sig.since = computed(() => serverDate, ...(ngDevMode ? [{ debugName: "since" }] : /* istanbul ignore next */ []));
|
|
1348
1447
|
return sig;
|
|
1349
1448
|
}
|
|
1350
|
-
const state = signal(navigator.onLine, { ...(ngDevMode ? { debugName: "state" } : {}), debugName });
|
|
1351
|
-
const since = signal(new Date(), ...(ngDevMode ? [{ debugName: "since" }] : []));
|
|
1449
|
+
const state = signal(navigator.onLine, { ...(ngDevMode ? { debugName: "state" } : /* istanbul ignore next */ {}), debugName });
|
|
1450
|
+
const since = signal(new Date(), ...(ngDevMode ? [{ debugName: "since" }] : /* istanbul ignore next */ []));
|
|
1352
1451
|
const goOnline = () => {
|
|
1353
1452
|
state.set(true);
|
|
1354
1453
|
since.set(new Date());
|
|
@@ -1408,7 +1507,7 @@ function pageVisibility(debugName = 'pageVisibility') {
|
|
|
1408
1507
|
if (isPlatformServer(inject(PLATFORM_ID))) {
|
|
1409
1508
|
return computed(() => 'visible', { debugName });
|
|
1410
1509
|
}
|
|
1411
|
-
const visibility = signal(document.visibilityState, { ...(ngDevMode ? { debugName: "visibility" } : {}), debugName });
|
|
1510
|
+
const visibility = signal(document.visibilityState, { ...(ngDevMode ? { debugName: "visibility" } : /* istanbul ignore next */ {}), debugName });
|
|
1412
1511
|
const onVisibilityChange = () => visibility.set(document.visibilityState);
|
|
1413
1512
|
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
1414
1513
|
inject(DestroyRef).onDestroy(() => document.removeEventListener('visibilitychange', onVisibilityChange));
|
|
@@ -1471,13 +1570,16 @@ function scrollPosition(opt) {
|
|
|
1471
1570
|
base.unthrottled = base;
|
|
1472
1571
|
return base;
|
|
1473
1572
|
}
|
|
1474
|
-
const { target = window, throttle = 100, debugName = 'scrollPosition', } = opt || {};
|
|
1573
|
+
const { target = globalThis.window, throttle = 100, debugName = 'scrollPosition', } = opt || {};
|
|
1475
1574
|
let element;
|
|
1476
1575
|
let getScrollPosition;
|
|
1477
|
-
if (target
|
|
1576
|
+
if (target === globalThis.window || target.window === target) {
|
|
1478
1577
|
element = target;
|
|
1479
1578
|
getScrollPosition = () => {
|
|
1480
|
-
return {
|
|
1579
|
+
return {
|
|
1580
|
+
x: target.scrollX ?? target.pageXOffset ?? 0,
|
|
1581
|
+
y: target.scrollY ?? target.pageYOffset ?? 0,
|
|
1582
|
+
};
|
|
1481
1583
|
};
|
|
1482
1584
|
}
|
|
1483
1585
|
else if (target instanceof ElementRef) {
|
|
@@ -1669,7 +1771,7 @@ function toArrayStore(source, injector) {
|
|
|
1669
1771
|
if (!Array.isArray(v))
|
|
1670
1772
|
return 0;
|
|
1671
1773
|
return v.length;
|
|
1672
|
-
}, ...(ngDevMode ? [{ debugName: "lengthSignal" }] : []));
|
|
1774
|
+
}, ...(ngDevMode ? [{ debugName: "lengthSignal" }] : /* istanbul ignore next */ []));
|
|
1673
1775
|
return new Proxy(source, {
|
|
1674
1776
|
has(_, prop) {
|
|
1675
1777
|
if (prop === 'length')
|
|
@@ -1934,7 +2036,6 @@ const noopStore = {
|
|
|
1934
2036
|
*
|
|
1935
2037
|
* @Component({
|
|
1936
2038
|
* selector: 'app-settings',
|
|
1937
|
-
* standalone: true,
|
|
1938
2039
|
* template: `
|
|
1939
2040
|
* Theme:
|
|
1940
2041
|
* <select [ngModel]="theme()" (ngModelChange)="theme.set($event)">
|
|
@@ -1992,7 +2093,7 @@ function stored(fallback, { key, store: providedStore, serialize = JSON.stringif
|
|
|
1992
2093
|
equal,
|
|
1993
2094
|
};
|
|
1994
2095
|
const initialKey = untracked(keySig);
|
|
1995
|
-
const internal = signal(getValue(initialKey), { ...(ngDevMode ? { debugName: "internal" } : {}), ...opt,
|
|
2096
|
+
const internal = signal(getValue(initialKey), { ...(ngDevMode ? { debugName: "internal" } : /* istanbul ignore next */ {}), ...opt,
|
|
1996
2097
|
equal: (a, b) => {
|
|
1997
2098
|
if (a === null && b === null)
|
|
1998
2099
|
return true;
|
|
@@ -2077,10 +2178,10 @@ class MessageBus {
|
|
|
2077
2178
|
this.channel.removeEventListener('message', listener);
|
|
2078
2179
|
this.listeners.delete(id);
|
|
2079
2180
|
}
|
|
2080
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
2081
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
2181
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: MessageBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2182
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: MessageBus, providedIn: 'root' });
|
|
2082
2183
|
}
|
|
2083
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2184
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: MessageBus, decorators: [{
|
|
2084
2185
|
type: Injectable,
|
|
2085
2186
|
args: [{
|
|
2086
2187
|
providedIn: 'root',
|
|
@@ -2157,7 +2258,7 @@ function tabSync(sig, opt) {
|
|
|
2157
2258
|
return;
|
|
2158
2259
|
}
|
|
2159
2260
|
post(val);
|
|
2160
|
-
}, ...(ngDevMode ? [{ debugName: "effectRef" }] : []));
|
|
2261
|
+
}, ...(ngDevMode ? [{ debugName: "effectRef" }] : /* istanbul ignore next */ []));
|
|
2161
2262
|
inject(DestroyRef).onDestroy(() => {
|
|
2162
2263
|
effectRef.destroy();
|
|
2163
2264
|
unsub();
|
|
@@ -2353,9 +2454,9 @@ function withHistory(source, opt) {
|
|
|
2353
2454
|
history.set([]);
|
|
2354
2455
|
redoArray.set([]);
|
|
2355
2456
|
};
|
|
2356
|
-
internal.canUndo = computed(() => history().length > 0, ...(ngDevMode ? [{ debugName: "canUndo" }] : []));
|
|
2357
|
-
internal.canRedo = computed(() => redoArray().length > 0, ...(ngDevMode ? [{ debugName: "canRedo" }] : []));
|
|
2358
|
-
internal.canClear = computed(() => internal.canUndo() || internal.canRedo(), ...(ngDevMode ? [{ debugName: "canClear" }] : []));
|
|
2457
|
+
internal.canUndo = computed(() => history().length > 0, ...(ngDevMode ? [{ debugName: "canUndo" }] : /* istanbul ignore next */ []));
|
|
2458
|
+
internal.canRedo = computed(() => redoArray().length > 0, ...(ngDevMode ? [{ debugName: "canRedo" }] : /* istanbul ignore next */ []));
|
|
2459
|
+
internal.canClear = computed(() => internal.canUndo() || internal.canRedo(), ...(ngDevMode ? [{ debugName: "canClear" }] : /* istanbul ignore next */ []));
|
|
2359
2460
|
return internal;
|
|
2360
2461
|
}
|
|
2361
2462
|
|
|
@@ -2363,5 +2464,5 @@ function withHistory(source, opt) {
|
|
|
2363
2464
|
* Generated bundle index. Do not edit.
|
|
2364
2465
|
*/
|
|
2365
2466
|
|
|
2366
|
-
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 };
|
|
2467
|
+
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 };
|
|
2367
2468
|
//# sourceMappingURL=mmstack-primitives.mjs.map
|