@ngutil/data 0.0.10 → 0.0.12

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.
Files changed (43) hide show
  1. package/esm2022/index.mjs +6 -3
  2. package/esm2022/provider/array.mjs +9 -0
  3. package/esm2022/provider/index.mjs +5 -0
  4. package/esm2022/provider/local.mjs +29 -0
  5. package/esm2022/provider/observable.mjs +8 -0
  6. package/esm2022/provider/provider.mjs +17 -0
  7. package/esm2022/query/index.mjs +1 -1
  8. package/esm2022/query/sorter.mjs +1 -1
  9. package/esm2022/source/index.mjs +3 -0
  10. package/esm2022/source/properties/abstract.mjs +33 -0
  11. package/esm2022/source/properties/filter.mjs +16 -0
  12. package/esm2022/source/properties/grouper.mjs +16 -0
  13. package/esm2022/source/properties/index.mjs +5 -0
  14. package/esm2022/source/properties/slimer.mjs +16 -0
  15. package/esm2022/source/properties/sorter.mjs +16 -0
  16. package/esm2022/source/proxy.directive.mjs +152 -0
  17. package/esm2022/source/source.mjs +109 -0
  18. package/esm2022/store/collection-store.mjs +3 -0
  19. package/esm2022/store/index.mjs +3 -0
  20. package/esm2022/store/memory-store.mjs +79 -0
  21. package/fesm2022/ngutil-data.mjs +967 -166
  22. package/fesm2022/ngutil-data.mjs.map +1 -1
  23. package/index.d.ts +5 -2
  24. package/package.json +2 -2
  25. package/provider/array.d.ts +7 -0
  26. package/provider/index.d.ts +4 -0
  27. package/provider/local.d.ts +16 -0
  28. package/provider/observable.d.ts +7 -0
  29. package/provider/provider.d.ts +35 -0
  30. package/query/index.d.ts +1 -1
  31. package/query/sorter.d.ts +4 -4
  32. package/source/index.d.ts +2 -0
  33. package/source/properties/abstract.d.ts +17 -0
  34. package/source/properties/filter.d.ts +11 -0
  35. package/source/properties/grouper.d.ts +11 -0
  36. package/source/properties/index.d.ts +4 -0
  37. package/source/properties/slimer.d.ts +11 -0
  38. package/source/properties/sorter.d.ts +11 -0
  39. package/source/proxy.directive.d.ts +57 -0
  40. package/source/source.d.ts +36 -0
  41. package/store/collection-store.d.ts +49 -0
  42. package/store/index.d.ts +2 -0
  43. package/store/memory-store.d.ts +16 -0
@@ -1,5 +1,11 @@
1
- import { flattenDepth, flattenDeep, isEqual, intersection } from 'lodash';
2
- import { isPlainObject, deepClone } from '@ngutil/common';
1
+ import { flattenDepth, intersection, flattenDeep, isEqual, sortBy as sortBy$1, groupBy as groupBy$1, merge } from 'lodash';
2
+ import * as i1 from '@ngutil/common';
3
+ import { isPlainObject, deepClone, toSorted, deepFreeze } from '@ngutil/common';
4
+ import { BehaviorSubject, map, combineLatest, take, of, shareReplay, ReplaySubject, switchMap, distinctUntilChanged, Subject, tap, debounceTime, finalize, Observable, takeUntil, share, Subscription, throwError } from 'rxjs';
5
+ import { DataSource as DataSource$1 } from '@angular/cdk/collections';
6
+ import * as i0 from '@angular/core';
7
+ import { Directive, Optional, Input } from '@angular/core';
8
+ import { toSignal } from '@angular/core/rxjs-interop';
3
9
 
4
10
  function pathGetterCompile(path) {
5
11
  if (!path || path.length === 0) {
@@ -18,169 +24,6 @@ function makeGetter(part, parent) {
18
24
  }
19
25
  }
20
26
 
21
- /**
22
- * @example
23
- *```ts
24
- * items.toSorted(sortBy([{"author.name": "asc"}]))
25
- * ```
26
- */
27
- function sortBy(sorters) {
28
- if (sorters.length === 0) {
29
- throw new Error("Empty sorter");
30
- }
31
- return _sorterCompile(sorters);
32
- }
33
- /**
34
- * Normalize sorter definition
35
- *
36
- * @example
37
- * ```ts
38
- * normalizeSorter([{id: "asc"}]) -> [{path: "id", isAsc: true, emptyFirst: true}]
39
- * normalizeSorter([{id: {dir: "desc", emptyFirst: false}}]) -> [{path: "id", isAsc: false, emptyFirst: false}]
40
- * ```
41
- */
42
- function sorterNormalize(sorters) {
43
- return flattenDeep(sorters.map((s) => Object.entries(s).map(([k, v]) => {
44
- if (typeof v === "string") {
45
- const isAsc = v.toLowerCase() === "asc";
46
- return { path: k, isAsc, emptyFirst: isAsc ? false : true };
47
- }
48
- else if (isPlainObject(v)) {
49
- return {
50
- path: k,
51
- isAsc: (v.dir || "asc").toLowerCase() === "asc",
52
- emptyFirst: v.emptyFirst == null ? true : !!v.emptyFirst
53
- };
54
- }
55
- else {
56
- throw new Error(`Invalid sorter: ${v}`);
57
- }
58
- })));
59
- }
60
- function _sorterCompile(sorters) {
61
- if (sorters.length === 0) {
62
- return (_a, _b) => 0;
63
- }
64
- const norm = sorterNormalize(sorters).map(createComparator);
65
- if (norm.length === 1) {
66
- return norm[0];
67
- }
68
- const initial = norm.pop();
69
- return norm.reduceRight((next, curr) => (a, b) => {
70
- const r = curr(a, b);
71
- return r === 0 ? next(a, b) : r;
72
- }, initial);
73
- }
74
- function createComparator({ path, isAsc, emptyFirst }) {
75
- const getter = pathGetterCompile(path);
76
- if (isAsc) {
77
- return (a, b) => compare(getter(a), getter(b), emptyFirst);
78
- }
79
- else {
80
- return (a, b) => compare(getter(a), getter(b), !emptyFirst) * -1;
81
- }
82
- }
83
- function compare(a, b, emptyFirst) {
84
- // console.log("COMPARE", a, b)
85
- if (a == null && b != null) {
86
- return emptyFirst === true ? -1 : 1;
87
- }
88
- else if (a != null && b == null) {
89
- return emptyFirst === true ? 1 : -1;
90
- }
91
- else if (a == null && b == null) {
92
- return 0;
93
- }
94
- else if (isEqual(a, b)) {
95
- return 0;
96
- }
97
- else if (typeof a === "number" && typeof b === "number") {
98
- return a - b;
99
- }
100
- else if (typeof a === "string" && typeof b === "string") {
101
- const al = a.length;
102
- const bl = b.length;
103
- // if both lengths is 0 the code execution not reach that point, because a === b
104
- if (emptyFirst === true) {
105
- if (al === 0) {
106
- return -1;
107
- }
108
- else if (bl === 0) {
109
- return 1;
110
- }
111
- }
112
- else {
113
- if (al === 0) {
114
- return 1;
115
- }
116
- else if (bl === 0) {
117
- return -1;
118
- }
119
- }
120
- return a.localeCompare(b);
121
- }
122
- else if (Array.isArray(a) && Array.isArray(b)) {
123
- const al = a.length;
124
- const bl = b.length;
125
- const l = Math.min(al, bl);
126
- for (let i = 0; i < l; i++) {
127
- const res = compare(a[i], b[i], emptyFirst);
128
- if (res !== 0) {
129
- return res;
130
- }
131
- }
132
- if (al === bl) {
133
- return 0;
134
- }
135
- if (emptyFirst === true) {
136
- if (al === 0) {
137
- return -1;
138
- }
139
- else if (bl === 0) {
140
- return 1;
141
- }
142
- }
143
- else {
144
- if (al === 0) {
145
- return 1;
146
- }
147
- else if (bl === 0) {
148
- return -1;
149
- }
150
- }
151
- return al - bl;
152
- }
153
- else if (isPlainObject(a) && isPlainObject(b)) {
154
- return JSON.stringify(a).localeCompare(JSON.stringify(b));
155
- }
156
- return a > b ? -1 : 1;
157
- }
158
- function sorterMerge(...sorters) {
159
- let result;
160
- for (const sorter of sorters) {
161
- if (sorter == null) {
162
- continue;
163
- }
164
- if (result == null) {
165
- result = deepClone(sorter);
166
- continue;
167
- }
168
- for (const sentry of sorter) {
169
- for (const [k, v] of Object.entries(sentry)) {
170
- const existing = result.find((value) => value[k] != null);
171
- if (existing) {
172
- ;
173
- existing[k] = deepClone(v);
174
- }
175
- else {
176
- result.push({ [k]: deepClone(v) });
177
- }
178
- }
179
- }
180
- }
181
- return result;
182
- }
183
-
184
27
  const OPERATORS = [
185
28
  "==" /* FilterOp.Eq */,
186
29
  "===" /* FilterOp.EqStrict */,
@@ -466,9 +309,967 @@ function filterMerge(...filters) {
466
309
  return result;
467
310
  }
468
311
 
312
+ function groupBy(grouper) {
313
+ return grouperCompile(grouper);
314
+ }
315
+ function grouperCompile(grouper) {
316
+ return _ => undefined;
317
+ }
318
+ function grouperMerge(...groupers) {
319
+ return undefined;
320
+ }
321
+ // import { Primitive } from "utility-types"
322
+ // import { Eval, Flatten } from "@ngutil/common"
323
+ // import { type Model } from "./query"
324
+ // import { SorterFn } from "./sorter"
325
+ // export interface Grouped<T extends Model> {
326
+ // items: T[]
327
+ // groups: Group<T>
328
+ // }
329
+ // export type GrouperFn<T extends Model> = (items: T[]) => any
330
+ // export type GroupKey<T extends Model> = (item: T, index: number) => Primitive[]
331
+ // export type GroupItemIsMatch<T extends Model> = (item: T, index: number) => boolean
332
+ // export interface GrouperX<T extends Model> {
333
+ // name: string
334
+ // title?: string
335
+ // isMatch?: GroupKey<T>
336
+ // [key: string]: any
337
+ // }
338
+ // export interface GrouperXNormalized<T extends Model> extends GrouperX<T> {
339
+ // key: GroupKey<T>
340
+ // }
341
+ // type _Groupers<T extends Model, F> = keyof F | Grouper<T>
342
+ // export type Grouper<T extends Model, F = Flatten<T>> = Eval<Array<_Groupers<T, F>>>
343
+ // export type GrouperNormalized<T extends Model> = { [key: string]: GrouperXNormalized<T> }
344
+ // export interface Group<T extends Model, G extends Grouper<T> = Grouper<T>> {
345
+ // grouper: G
346
+ // begin: number
347
+ // end: number
348
+ // }
349
+ // /**
350
+ // * @example
351
+ // * ```ts
352
+ // * const grouped = groupBy(["status"])(items)
353
+ // * // expexted: {[status]: [...items]}
354
+ // *
355
+ // * const grouped = groupBy(["name", "age"])(items)
356
+ // * // expected {[status]: {[age]: [...items]}}
357
+ // *
358
+ // * const STATUS_LABELS = {active: "Active", inactive: "Inactive"}
359
+ // * const grouped = groupBy([(item) => (STATUS_LABELS[item.status] || "Undefined")])(items)
360
+ // * // expected {["Active" | "Inactive" | "Undefined"]: [...items]}
361
+ // *
362
+ // * const grouped = groupBy([(item) => (STATUS_LABELS[item.status] || "Undefined"), "age"])(items)
363
+ // * // expected {["Active" | "Inactive" | "Undefined"]: {[age]: [...items]}}
364
+ // * ```
365
+ // */
366
+ // export function groupBy<T extends Model>(groupers: Grouper<T>): SorterFn<T> {
367
+ // return (_a, _b) => 0
368
+ // }
369
+ // export function grouperNormalize() {}
370
+ // export function grouperCompile<T extends Model>(groupers: Grouper<T>): GrouperFn<T> {
371
+ // return _ => undefined
372
+ // }
373
+ // export function grouperMerge<T extends Model, F = Flatten<T>>(
374
+ // ...groupers: Array<Grouper<T, F> | undefined | null>
375
+ // ): Grouper<T, F> | undefined {
376
+ // return undefined
377
+ // }
378
+
379
+ /**
380
+ * @example
381
+ *```ts
382
+ * items.toSorted(sortBy([{"author.name": "asc"}]))
383
+ * ```
384
+ */
385
+ function sortBy(sorters) {
386
+ if (sorters.length === 0) {
387
+ throw new Error("Empty sorter");
388
+ }
389
+ return _sorterCompile(sorters);
390
+ }
391
+ /**
392
+ * Normalize sorter definition
393
+ *
394
+ * @example
395
+ * ```ts
396
+ * normalizeSorter([{id: "asc"}]) -> [{path: "id", isAsc: true, emptyFirst: true}]
397
+ * normalizeSorter([{id: {dir: "desc", emptyFirst: false}}]) -> [{path: "id", isAsc: false, emptyFirst: false}]
398
+ * ```
399
+ */
400
+ function sorterNormalize(sorters) {
401
+ return flattenDeep(sorters.map((s) => Object.entries(s).map(([k, v]) => {
402
+ if (typeof v === "string") {
403
+ const isAsc = v.toLowerCase() === "asc";
404
+ return { path: k, isAsc, emptyFirst: isAsc ? false : true };
405
+ }
406
+ else if (isPlainObject(v)) {
407
+ return {
408
+ path: k,
409
+ isAsc: (v.dir || "asc").toLowerCase() === "asc",
410
+ emptyFirst: v.emptyFirst == null ? true : !!v.emptyFirst
411
+ };
412
+ }
413
+ else {
414
+ throw new Error(`Invalid sorter: ${v}`);
415
+ }
416
+ })));
417
+ }
418
+ function _sorterCompile(sorters) {
419
+ if (sorters.length === 0) {
420
+ return (_a, _b) => 0;
421
+ }
422
+ const norm = sorterNormalize(sorters).map(createComparator);
423
+ if (norm.length === 1) {
424
+ return norm[0];
425
+ }
426
+ const initial = norm.pop();
427
+ return norm.reduceRight((next, curr) => (a, b) => {
428
+ const r = curr(a, b);
429
+ return r === 0 ? next(a, b) : r;
430
+ }, initial);
431
+ }
432
+ function createComparator({ path, isAsc, emptyFirst }) {
433
+ const getter = pathGetterCompile(path);
434
+ if (isAsc) {
435
+ return (a, b) => compare(getter(a), getter(b), emptyFirst);
436
+ }
437
+ else {
438
+ return (a, b) => compare(getter(a), getter(b), !emptyFirst) * -1;
439
+ }
440
+ }
441
+ function compare(a, b, emptyFirst) {
442
+ // console.log("COMPARE", a, b)
443
+ if (a == null && b != null) {
444
+ return emptyFirst === true ? -1 : 1;
445
+ }
446
+ else if (a != null && b == null) {
447
+ return emptyFirst === true ? 1 : -1;
448
+ }
449
+ else if (a == null && b == null) {
450
+ return 0;
451
+ }
452
+ else if (isEqual(a, b)) {
453
+ return 0;
454
+ }
455
+ else if (typeof a === "number" && typeof b === "number") {
456
+ return a - b;
457
+ }
458
+ else if (typeof a === "string" && typeof b === "string") {
459
+ const al = a.length;
460
+ const bl = b.length;
461
+ // if both lengths is 0 the code execution not reach that point, because a === b
462
+ if (emptyFirst === true) {
463
+ if (al === 0) {
464
+ return -1;
465
+ }
466
+ else if (bl === 0) {
467
+ return 1;
468
+ }
469
+ }
470
+ else {
471
+ if (al === 0) {
472
+ return 1;
473
+ }
474
+ else if (bl === 0) {
475
+ return -1;
476
+ }
477
+ }
478
+ return a.localeCompare(b);
479
+ }
480
+ else if (Array.isArray(a) && Array.isArray(b)) {
481
+ const al = a.length;
482
+ const bl = b.length;
483
+ const l = Math.min(al, bl);
484
+ for (let i = 0; i < l; i++) {
485
+ const res = compare(a[i], b[i], emptyFirst);
486
+ if (res !== 0) {
487
+ return res;
488
+ }
489
+ }
490
+ if (al === bl) {
491
+ return 0;
492
+ }
493
+ if (emptyFirst === true) {
494
+ if (al === 0) {
495
+ return -1;
496
+ }
497
+ else if (bl === 0) {
498
+ return 1;
499
+ }
500
+ }
501
+ else {
502
+ if (al === 0) {
503
+ return 1;
504
+ }
505
+ else if (bl === 0) {
506
+ return -1;
507
+ }
508
+ }
509
+ return al - bl;
510
+ }
511
+ else if (isPlainObject(a) && isPlainObject(b)) {
512
+ return JSON.stringify(a).localeCompare(JSON.stringify(b));
513
+ }
514
+ return a > b ? -1 : 1;
515
+ }
516
+ function sorterMerge(...sorters) {
517
+ let result;
518
+ for (const sorter of sorters) {
519
+ if (sorter == null) {
520
+ continue;
521
+ }
522
+ if (result == null) {
523
+ result = deepClone(sorter);
524
+ continue;
525
+ }
526
+ for (const sentry of sorter) {
527
+ for (const [k, v] of Object.entries(sentry)) {
528
+ const existing = result.find((value) => value[k] != null);
529
+ if (existing) {
530
+ ;
531
+ existing[k] = deepClone(v);
532
+ }
533
+ else {
534
+ result.push({ [k]: deepClone(v) });
535
+ }
536
+ }
537
+ }
538
+ }
539
+ return result;
540
+ }
541
+
542
+ function slimBy(slimer) { }
543
+ function slimerNormalize(slimer) { }
544
+ function slimerCompile(slimer) {
545
+ return item => item;
546
+ }
547
+ function slimerMerge(...slimers) { }
548
+
549
+ function sliceMerge(...slices) {
550
+ let result = undefined;
551
+ for (const slice of slices) {
552
+ if (slice == null) {
553
+ continue;
554
+ }
555
+ if (result == null) {
556
+ result = deepClone(slice);
557
+ }
558
+ else {
559
+ result = { ...result, ...slice };
560
+ }
561
+ }
562
+ return result;
563
+ }
564
+ /**
565
+ * Apply slice to array, and force the result length to equal with Slice length,
566
+ * so fill array with undefined if not enought elements
567
+ */
568
+ function sliceApply(slice, array) {
569
+ const result = array.slice(slice.start, slice.end);
570
+ result.length = slice.end - slice.start;
571
+ return result;
572
+ }
573
+ /**
574
+ * @returns Page numbers, eg.: `[10, 11, 12, 13, ...]`
575
+ */
576
+ function sliceToPages(slice) {
577
+ if (slice.pageSize == null) {
578
+ throw new Error("Missing `step` from slice");
579
+ }
580
+ const start = Math.floor(slice.start / slice.pageSize);
581
+ const end = Math.ceil(slice.end / slice.pageSize);
582
+ return Array.from({ length: end - start }, (_, i) => start + i);
583
+ }
584
+ function sliceInsert(slice, array, newItems) {
585
+ if (slice.start === slice.end) {
586
+ return array;
587
+ }
588
+ let result = array.slice(0, slice.start);
589
+ result.length = slice.start;
590
+ result = result.concat(newItems);
591
+ result.length = slice.end;
592
+ return result.concat(array.slice(slice.end));
593
+ }
594
+ function sliceClamp(slice, constraint) {
595
+ return {
596
+ start: Math.max(slice.start, constraint.start),
597
+ end: Math.min(slice.end, constraint.end),
598
+ pageSize: slice.pageSize
599
+ };
600
+ }
601
+ function sliceEq(a, b) {
602
+ return a.start === b.start && a.end === b.end && a.pageSize === b.pageSize;
603
+ }
604
+ // TODO: sliceOverlap(other: Slice): Slice
605
+ // TODO: sliceIncludes(other: Slice): boolean
606
+ // TODO: sliceConcat(...slices: Slice[]): Slice[]
607
+ // TODO: sliceDiff(slice: Slice): Slice[]
608
+
609
+ const INPUT = Symbol("INPUT");
610
+ function queryExecutor(query, previous) {
611
+ const executors = {
612
+ filterFn: filterExecutor(query.filter, previous?.filterFn),
613
+ sorterFn: sorterExecutor(query.sorter, previous?.sorterFn),
614
+ grouperFn: grouperExecutor(query.grouper, previous?.grouperFn),
615
+ slicerFn: sliceExecutor(query.slice, previous?.slicerFn)
616
+ };
617
+ const changed = previous == null || Object.entries(executors).some(([k, v]) => previous[k] !== v);
618
+ if (!changed) {
619
+ return previous;
620
+ }
621
+ const executor = (items) => {
622
+ if (items == null || items.length === 0) {
623
+ return { items: [], total: 0 };
624
+ }
625
+ let result = items;
626
+ if (executors.filterFn) {
627
+ result = result.filter(executors.filterFn);
628
+ }
629
+ if (executors.sorterFn) {
630
+ result = toSorted(result, executors.sorterFn);
631
+ }
632
+ const total = result.length;
633
+ // TODO: grouper
634
+ if (executors.slicerFn) {
635
+ result = executors.slicerFn(result);
636
+ }
637
+ return { items: result, total };
638
+ };
639
+ for (const [k, v] of Object.entries(executors)) {
640
+ Object.defineProperty(executor, k, {
641
+ value: v,
642
+ enumerable: true,
643
+ writable: false
644
+ });
645
+ }
646
+ return executor;
647
+ }
648
+ function filterExecutor(filter, prev) {
649
+ return compileExec(filter, prev, filterBy);
650
+ }
651
+ function sorterExecutor(sorter, prev) {
652
+ return compileExec(sorter, prev, sortBy$1);
653
+ }
654
+ function grouperExecutor(sorter, prev) {
655
+ return compileExec(sorter, prev, groupBy$1);
656
+ }
657
+ function sliceExecutor(slice, prev) {
658
+ return compileExec(slice, prev, sliceBy);
659
+ }
660
+ function sliceBy(slice) {
661
+ return (items) => items.slice(slice.start, slice.end);
662
+ }
663
+ function compileExec(input, prev, compiler) {
664
+ if (input == null) {
665
+ return undefined;
666
+ }
667
+ if (prev != null) {
668
+ // thorically input is readonly
669
+ if (prev[INPUT] === input || isEqual(prev[INPUT], input)) {
670
+ return prev;
671
+ }
672
+ }
673
+ const exec = compiler(input);
674
+ Object.defineProperty(exec, INPUT, {
675
+ value: input,
676
+ enumerable: true,
677
+ writable: false
678
+ });
679
+ return exec;
680
+ }
681
+
682
+ class ModelMeta {
683
+ static coerce(value) {
684
+ if (value instanceof ModelMeta) {
685
+ return value;
686
+ }
687
+ else {
688
+ return new ModelMeta(value);
689
+ }
690
+ }
691
+ #getKey;
692
+ constructor(props) {
693
+ this.keys = props.keys;
694
+ if (this.keys.length > 0) {
695
+ const getters = this.keys.map(pathGetterCompile);
696
+ if (props.trackBy == null) {
697
+ this.trackBy = (index, item) => getters.map(p => p(item)).join("∀");
698
+ }
699
+ else {
700
+ this.trackBy = props.trackBy;
701
+ }
702
+ this.#getKey = (item) => getters.map(p => p(item)).join("∀");
703
+ }
704
+ else {
705
+ if (props.trackBy == null) {
706
+ throw new Error("Can't compile track by function without `keys` declaration");
707
+ }
708
+ else {
709
+ this.trackBy = props.trackBy;
710
+ }
711
+ }
712
+ }
713
+ isEqual(a, b) {
714
+ return isEqual(a, b);
715
+ }
716
+ isEqualByTrack(a, b) {
717
+ if (a == null || b == null) {
718
+ return a == null && b == null;
719
+ }
720
+ if (a.index == null || b.index == null) {
721
+ return a.index == null && b.index == null;
722
+ }
723
+ if (a.model == null || b.model == null) {
724
+ return a.model == null && b.model == null;
725
+ }
726
+ return this.trackBy(a.index, a.model) === this.trackBy(b.index, b.model);
727
+ }
728
+ isEqualByKey(a, b) {
729
+ if (this.#getKey != null) {
730
+ if (a == null || b == null) {
731
+ return a == null && b == null;
732
+ }
733
+ return this.#getKey(a) === this.#getKey(b);
734
+ }
735
+ console.warn("Primary keys is not defined for", a, b);
736
+ return false;
737
+ }
738
+ normalizeRef(ref) {
739
+ if (ref instanceof ModelRefNorm) {
740
+ return ref;
741
+ }
742
+ if (ref.key != null && ref.index != null) {
743
+ throw new Error("Only provide `pk` or `index` value not both");
744
+ }
745
+ if (ref.key != null) {
746
+ const keyValue = (Array.isArray(ref.key) ? ref.key : [ref.key]);
747
+ if (keyValue.length > 0) {
748
+ if (this.keys.length === 0) {
749
+ throw new Error("Can't normalize ref without `keys`");
750
+ }
751
+ if (keyValue.length !== this.keys.length) {
752
+ throw new Error(`Wrong number of \`key\` values for this keys: [${this.keys.join(",")}]`);
753
+ }
754
+ return new ModelRefByKey(keyValue, this.keys);
755
+ }
756
+ else {
757
+ console.warn("Empty key in ModelRef", ref);
758
+ }
759
+ }
760
+ if (ref.index != null) {
761
+ return new ModelRefByIndex(ref.index);
762
+ }
763
+ throw new Error("Missing `key` or `index` value");
764
+ }
765
+ }
766
+ class ModelRefNorm {
767
+ #filter;
768
+ toFilter() {
769
+ if (this.#filter == null) {
770
+ return (this.#filter = this._asFilter());
771
+ }
772
+ return this.#filter;
773
+ }
774
+ }
775
+ class ModelRefByKey extends ModelRefNorm {
776
+ #keys;
777
+ constructor(key, keys) {
778
+ super();
779
+ this.key = key;
780
+ this.#keys = keys;
781
+ }
782
+ _asFilter() {
783
+ const filter = {};
784
+ for (let i = 0; i < this.#keys.length; i++) {
785
+ filter[this.#keys[i]] = this.key[i];
786
+ }
787
+ return filterBy(filter);
788
+ }
789
+ }
790
+ class ModelRefByIndex extends ModelRefNorm {
791
+ constructor(index) {
792
+ super();
793
+ this.index = index;
794
+ }
795
+ _asFilter() {
796
+ return (item, index) => this.index === index;
797
+ }
798
+ }
799
+ class UnknownMeta extends ModelMeta {
800
+ constructor() {
801
+ super({ keys: [], trackBy: (index) => index });
802
+ }
803
+ }
804
+
805
+ class CollectionStore {
806
+ }
807
+
808
+ class MemoryStore extends CollectionStore {
809
+ #data = new BehaviorSubject([]);
810
+ insertSlice(slice, items) {
811
+ this.#data.next(sliceInsert(slice, this.#data.value, items));
812
+ return this.#data;
813
+ }
814
+ hasSlice(slice) {
815
+ return this.#data.pipe(map(data => {
816
+ if (sliceEq(slice, sliceClamp(slice, { start: 0, end: data.length }))) {
817
+ for (let i = slice.start; i < slice.end; i++) {
818
+ if (data[i] == null) {
819
+ return false;
820
+ }
821
+ }
822
+ return true;
823
+ }
824
+ else {
825
+ return false;
826
+ }
827
+ }));
828
+ }
829
+ getSlice(slice) {
830
+ return this.#data.pipe(map(data => sliceApply(slice, data)));
831
+ }
832
+ get(ref) {
833
+ return this.#data.pipe(map(data => data.find(ref.toFilter())));
834
+ }
835
+ indexOf(ref) {
836
+ return this.#data.pipe(map(data => data.findIndex(ref.toFilter())));
837
+ }
838
+ update(ref, item) {
839
+ return combineLatest({
840
+ index: this.indexOf(ref),
841
+ data: this.#data
842
+ }).pipe(take(1), map(({ index, data }) => {
843
+ if (index < 0) {
844
+ return index;
845
+ }
846
+ this.#data.next(sliceInsert({ start: index, end: index + 1 }, data, [item]));
847
+ return index;
848
+ }));
849
+ }
850
+ updateOrInsert(ref, item, position) {
851
+ return combineLatest({
852
+ index: this.indexOf(ref),
853
+ data: this.#data
854
+ }).pipe(take(1), map(({ index, data }) => {
855
+ if (index < 0) {
856
+ index = position == null ? data.length : position < 0 ? data.length : position;
857
+ }
858
+ this.#data.next(sliceInsert({ start: index, end: index + 1 }, data, [item]));
859
+ return index;
860
+ }));
861
+ }
862
+ del(ref) {
863
+ return combineLatest({
864
+ index: this.indexOf(ref),
865
+ data: this.#data
866
+ }).pipe(take(1), map(({ index, data }) => {
867
+ if (index < 0) {
868
+ return index;
869
+ }
870
+ const result = data.slice(0);
871
+ result.splice(index, 1);
872
+ this.#data.next(result);
873
+ return index;
874
+ }));
875
+ }
876
+ clear() {
877
+ if (this.#data.value.length > 0) {
878
+ this.#data.next([]);
879
+ }
880
+ return of(undefined);
881
+ }
882
+ }
883
+
884
+ class Property extends BehaviorSubject {
885
+ // readonly #signal: Signal<DeepReadonly<T> | undefined> = toSignal(this, { requireSync: true })
886
+ set(value, clone = true) {
887
+ if (!isEqual(this.value, value)) {
888
+ if (value != null) {
889
+ this.next(deepFreeze(clone ? deepClone(value) : value));
890
+ }
891
+ else {
892
+ this.next(undefined);
893
+ }
894
+ }
895
+ }
896
+ get() {
897
+ return this.value;
898
+ }
899
+ del() {
900
+ this.set(undefined);
901
+ }
902
+ }
903
+ class PropertyCombined {
904
+ }
905
+ function mergedProperty(merger, ...props) {
906
+ if (props.length > 1) {
907
+ return combineLatest(props).pipe(map(values => deepFreeze(merger(...values))));
908
+ }
909
+ else {
910
+ return props[0];
911
+ }
912
+ }
913
+
914
+ class FilterProperty extends Property {
915
+ update(other) {
916
+ this.set(filterMerge(this.value, other), false);
917
+ }
918
+ }
919
+ class FilterCombined extends PropertyCombined {
920
+ constructor() {
921
+ super(...arguments);
922
+ this.normal = new FilterProperty(undefined);
923
+ this.forced = new FilterProperty(undefined);
924
+ this.merged$ = mergedProperty(filterMerge, this.normal, this.forced);
925
+ }
926
+ }
927
+
928
+ class SorterProperty extends Property {
929
+ update(other) {
930
+ this.set(sorterMerge(this.value, other), false);
931
+ }
932
+ }
933
+ class SorterCombined extends PropertyCombined {
934
+ constructor() {
935
+ super(...arguments);
936
+ this.normal = new SorterProperty(undefined);
937
+ this.forced = new SorterProperty(undefined);
938
+ this.merged$ = mergedProperty(sorterMerge, this.normal, this.forced);
939
+ }
940
+ }
941
+
942
+ class SlimerProperty extends Property {
943
+ update(other) {
944
+ this.set(slimerMerge(this.value, other), false);
945
+ }
946
+ }
947
+ class SlimerCombined extends PropertyCombined {
948
+ constructor() {
949
+ super(...arguments);
950
+ this.normal = new SlimerProperty(undefined);
951
+ this.forced = new SlimerProperty(undefined);
952
+ this.merged$ = mergedProperty(slimerMerge, this.normal, this.forced);
953
+ }
954
+ }
955
+
956
+ class GrouperProperty extends Property {
957
+ update(other) {
958
+ this.set(grouperMerge(this.value, other), false);
959
+ }
960
+ }
961
+ class GrouperCombined extends PropertyCombined {
962
+ constructor() {
963
+ super(...arguments);
964
+ this.normal = new GrouperProperty(undefined);
965
+ this.forced = new GrouperProperty(undefined);
966
+ this.merged$ = mergedProperty(grouperMerge, this.normal, this.forced);
967
+ }
968
+ }
969
+
970
+ const DEBOUNCE_TIME = 50;
971
+ class DataSource extends DataSource$1 {
972
+ #queryBase;
973
+ #slice;
974
+ #reload;
975
+ constructor(provider, store) {
976
+ super();
977
+ this.provider = provider;
978
+ this.busy$ = new BehaviorSubject(false);
979
+ this.total$ = new BehaviorSubject(undefined);
980
+ this.filter = new FilterCombined();
981
+ this.sorter = new SorterCombined();
982
+ this.slimer = new SlimerCombined();
983
+ this.grouper = new GrouperCombined();
984
+ this.#queryBase = combineLatest({
985
+ filter: this.filter.merged$,
986
+ sorter: this.sorter.merged$,
987
+ slimer: this.slimer.merged$,
988
+ grouper: this.grouper.merged$
989
+ }).pipe(shareReplay(1));
990
+ this.#slice = new ReplaySubject(1);
991
+ this.slice$ = this.#slice.pipe(switchMap(slice => this.provider.clampSlice(slice)), distinctUntilChanged(isEqual), map(slice => deepFreeze(deepClone(slice))), shareReplay(1));
992
+ this.#reload = new Subject();
993
+ this.reset$ = merge(this.#queryBase, this.#reload).pipe(tap(() => this.#setBusy(true)), switchMap(() => this.store.clear()), shareReplay(1));
994
+ this.query$ = combineLatest({ base: this.#queryBase, slice: this.slice$ }).pipe(map(({ base, slice }) => {
995
+ return { ...base, slice };
996
+ }), shareReplay(1));
997
+ this.items$ = combineLatest({ query: this.query$, reset: this.reset$ }).pipe(tap(() => this.#setBusy(true)), debounceTime(DEBOUNCE_TIME), switchMap(({ query }) => {
998
+ return this.store.hasSlice(query.slice).pipe(switchMap(hasSlice => {
999
+ if (hasSlice) {
1000
+ return this.store.getSlice(query.slice);
1001
+ }
1002
+ else {
1003
+ return this.provider.queryList(query).pipe(switchMap(result => {
1004
+ if (result.total != null) {
1005
+ this.total$.next(result.total);
1006
+ }
1007
+ return this.store.insertSlice(query.slice, result.items);
1008
+ }), take(1));
1009
+ }
1010
+ }));
1011
+ }), finalize(() => this.#setBusy(false)), shareReplay(1));
1012
+ this.#cvSubs = new Map();
1013
+ if (store == null) {
1014
+ store = new MemoryStore();
1015
+ }
1016
+ this.store = store;
1017
+ }
1018
+ setSlice(slice) {
1019
+ this.#slice.next(slice);
1020
+ return this;
1021
+ }
1022
+ all() {
1023
+ return this.setSlice({ start: 0, end: Infinity });
1024
+ }
1025
+ realod() {
1026
+ this.#reload.next();
1027
+ }
1028
+ getItem(ref) {
1029
+ const refn = this.provider.meta.normalizeRef(ref);
1030
+ return this.#storeFirst(query => this.store.get(refn), query => this.provider.queryItem(refn, query));
1031
+ }
1032
+ getItemPosition(ref) {
1033
+ const refn = this.provider.meta.normalizeRef(ref);
1034
+ return this.#storeFirst(query => this.store.indexOf(refn).pipe(map(i => (i < 0 ? undefined : i))), query => this.provider.queryPosition(refn, query));
1035
+ }
1036
+ realodItem(ref, insertPosition) {
1037
+ const refn = this.provider.meta.normalizeRef(ref);
1038
+ return this.query$.pipe(switchMap(query => this.provider.queryItem(refn, query)), switchMap(item => item != null ? this.store.updateOrInsert(refn, item, insertPosition).pipe(map(() => item)) : of(item)), take(1));
1039
+ }
1040
+ #storeFirst(storeFn, selfFn) {
1041
+ return this.query$.pipe(take(1), switchMap(query => storeFn(query).pipe(switchMap(result => (result == null ? selfFn(query) : of(result))))));
1042
+ }
1043
+ #setBusy(busy) {
1044
+ if (this.provider.isAsync) {
1045
+ if (this.busy$.value !== busy) {
1046
+ this.busy$.next(busy);
1047
+ }
1048
+ }
1049
+ }
1050
+ #cvSubs;
1051
+ connect(collectionViewer) {
1052
+ const until = new Subject();
1053
+ this.#cvSubs.get(collectionViewer)?.next();
1054
+ this.#cvSubs.set(collectionViewer, until);
1055
+ return new Observable((subscriber) => {
1056
+ const sub1 = collectionViewer.viewChange.subscribe(this.#slice);
1057
+ const sub2 = this.items$.subscribe(subscriber);
1058
+ return () => {
1059
+ if (this.#cvSubs.get(collectionViewer) === until) {
1060
+ this.#cvSubs.delete(collectionViewer);
1061
+ }
1062
+ sub1.unsubscribe();
1063
+ sub2.unsubscribe();
1064
+ };
1065
+ }).pipe(takeUntil(until));
1066
+ }
1067
+ disconnect(collectionViewer) {
1068
+ this.#cvSubs.get(collectionViewer)?.next();
1069
+ this.#cvSubs.delete(collectionViewer);
1070
+ }
1071
+ }
1072
+
1073
+ /**
1074
+ * @example
1075
+ * ```html
1076
+ * <table [nuDataSource]="..." [filter]="{isActive: true}" [sorter]="[{name: 'asc'}]"></table>
1077
+ * ```
1078
+ *
1079
+ * ```ts
1080
+ * @Component({
1081
+ * template: `<table [nuDataSource]="users$"></table>`
1082
+ * })
1083
+ * class UserTable {
1084
+ * readonly userService = inject(UserService)
1085
+ * readonly users$ = UserService.all()
1086
+ * }
1087
+ * ```
1088
+ *
1089
+ * ```ts
1090
+ * @Component({
1091
+ * selector: "table.my-table",
1092
+ * template: `
1093
+ * <cdk-virtual-scroll-viewport itemSize="50" class="example-viewport">
1094
+ * <div *cdkVirtualFor="let item of dataSource" class="example-item">{{item}}</div>
1095
+ * </cdk-virtual-scroll-viewport>
1096
+ * `
1097
+ * })
1098
+ * class TableComponent {
1099
+ * readonly dataSource = inject(DataSourceProxy)
1100
+ * }
1101
+ * ```
1102
+ */
1103
+ class DataSourceProxy extends DataSource$1 {
1104
+ set source(value) {
1105
+ this.#valueSub?.unsubscribe();
1106
+ this.#valueSub = coerceDataSource(value)
1107
+ .pipe(tap(v => {
1108
+ if (v == null) {
1109
+ throw new Error("Missing DataSource");
1110
+ }
1111
+ }))
1112
+ .subscribe(this.source$);
1113
+ }
1114
+ #valueSub;
1115
+ set filter(value) {
1116
+ this.#filter.next(value);
1117
+ }
1118
+ #filter;
1119
+ // TODO: maybe mergedFilter$ = this.value$.pipe(switchMap(value => value.filter.merged$))
1120
+ set sorter(value) {
1121
+ this.#sorter.next(value);
1122
+ }
1123
+ #sorter;
1124
+ set grouper(value) {
1125
+ this.#grouper.next(value);
1126
+ }
1127
+ #grouper;
1128
+ set slimer(value) {
1129
+ this.#slimer.next(value);
1130
+ }
1131
+ #slimer;
1132
+ #subs;
1133
+ constructor(busy) {
1134
+ super();
1135
+ this.source$ = new ReplaySubject(1);
1136
+ this.items$ = this.source$.pipe(switchMap(value => value.items$), share());
1137
+ this.busy$ = this.source$.pipe(switchMap(value => value.busy$), share());
1138
+ this.isBusy = toSignal(this.busy$, { rejectErrors: true, initialValue: false });
1139
+ this.#filter = new ReplaySubject(1);
1140
+ this.#sorter = new ReplaySubject(1);
1141
+ this.#grouper = new ReplaySubject(1);
1142
+ this.#slimer = new ReplaySubject(1);
1143
+ this.#subs = new Subscription();
1144
+ this.#cvSubs = new Map();
1145
+ if (busy != null) {
1146
+ this.#subs.add(busy.connect(this.busy$).subscribe());
1147
+ }
1148
+ this.#subs.add(combineLatest({ src: this.source$, filter: this.#filter }).subscribe(({ src, filter }) => {
1149
+ src.filter.forced.set(filter);
1150
+ }));
1151
+ this.#subs.add(combineLatest({ src: this.source$, sorter: this.#sorter }).subscribe(({ src, sorter }) => {
1152
+ src.sorter.forced.set(sorter);
1153
+ }));
1154
+ this.#subs.add(combineLatest({ src: this.source$, grouper: this.#grouper }).subscribe(({ src, grouper }) => {
1155
+ src.grouper.forced.set(grouper);
1156
+ }));
1157
+ this.#subs.add(combineLatest({ src: this.source$, slimer: this.#slimer }).subscribe(({ src, slimer }) => {
1158
+ src.slimer.forced.set(slimer);
1159
+ }));
1160
+ }
1161
+ #cvSubs;
1162
+ connect(collectionViewer) {
1163
+ const until = new Subject();
1164
+ this.#cvSubs.get(collectionViewer)?.next();
1165
+ this.#cvSubs.set(collectionViewer, until);
1166
+ return this.source$.pipe(switchMap(value => value.connect(collectionViewer)), takeUntil(until), finalize(() => this.#cvSubs.delete(collectionViewer)));
1167
+ }
1168
+ disconnect(collectionViewer) {
1169
+ this.#cvSubs.get(collectionViewer)?.next();
1170
+ this.#cvSubs.delete(collectionViewer);
1171
+ }
1172
+ ngOnDestroy() {
1173
+ this.#valueSub?.unsubscribe();
1174
+ this.#valueSub = undefined;
1175
+ this.#subs.unsubscribe();
1176
+ }
1177
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: DataSourceProxy, deps: [{ token: i1.Busy, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); }
1178
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.6", type: DataSourceProxy, isStandalone: true, selector: "[nuDataSource]", inputs: { source: ["nuDataSource", "source"], filter: "filter", sorter: "sorter", grouper: "grouper", slimer: "slimer" }, exportAs: ["nuDataSource"], usesInheritance: true, ngImport: i0 }); }
1179
+ }
1180
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.6", ngImport: i0, type: DataSourceProxy, decorators: [{
1181
+ type: Directive,
1182
+ args: [{
1183
+ standalone: true,
1184
+ selector: "[nuDataSource]",
1185
+ exportAs: "nuDataSource"
1186
+ }]
1187
+ }], ctorParameters: () => [{ type: i1.Busy, decorators: [{
1188
+ type: Optional
1189
+ }] }], propDecorators: { source: [{
1190
+ type: Input,
1191
+ args: [{ required: true, alias: "nuDataSource" }]
1192
+ }], filter: [{
1193
+ type: Input
1194
+ }], sorter: [{
1195
+ type: Input
1196
+ }], grouper: [{
1197
+ type: Input
1198
+ }], slimer: [{
1199
+ type: Input
1200
+ }] } });
1201
+ function coerceDataSource(value) {
1202
+ if (value instanceof DataSourceProxy) {
1203
+ return value.source$;
1204
+ }
1205
+ else if (value instanceof DataSource) {
1206
+ return of(value);
1207
+ }
1208
+ else if (value instanceof DataProvider) {
1209
+ return of(value.toDataSource());
1210
+ }
1211
+ else {
1212
+ return throwError(() => new Error("Invalid DataSource value"));
1213
+ }
1214
+ }
1215
+
1216
+ class DataProvider {
1217
+ /**
1218
+ * Froce Slice boundaries, useful in array, or obeservable providers
1219
+ */
1220
+ clampSlice(slice) {
1221
+ return of(slice);
1222
+ }
1223
+ /**
1224
+ * @returns New data source instance
1225
+ */
1226
+ toDataSource(store) {
1227
+ return new DataSource(this, store);
1228
+ }
1229
+ }
1230
+
1231
+ class LocalProvider extends DataProvider {
1232
+ #executor;
1233
+ constructor(meta) {
1234
+ super();
1235
+ this.isAsync = false;
1236
+ this.meta = ModelMeta.coerce(meta);
1237
+ }
1238
+ executor(request) {
1239
+ return (this.#executor = queryExecutor(request, this.#executor));
1240
+ }
1241
+ queryList(request) {
1242
+ const exec = this.executor(request);
1243
+ return this.items$.pipe(map(items => exec(items)));
1244
+ }
1245
+ queryItem(ref, request) {
1246
+ return this.queryList(request).pipe(map(list => list.items.find(ref.toFilter())));
1247
+ }
1248
+ queryPosition(ref, request) {
1249
+ return this.queryList(request).pipe(map(list => list.items.findIndex(ref.toFilter())));
1250
+ }
1251
+ clampSlice(slice) {
1252
+ return this.items$.pipe(take(1), map(items => sliceClamp(slice, { start: 0, end: items.length })));
1253
+ }
1254
+ }
1255
+
1256
+ class ArrayProvider extends LocalProvider {
1257
+ constructor(meta, items) {
1258
+ super(meta);
1259
+ this.items$ = of(items);
1260
+ }
1261
+ }
1262
+
1263
+ class ObservableProvider extends LocalProvider {
1264
+ constructor(meta, src) {
1265
+ super(meta);
1266
+ this.items$ = src;
1267
+ }
1268
+ }
1269
+
469
1270
  /**
470
1271
  * Generated bundle index. Do not edit.
471
1272
  */
472
1273
 
473
- export { filterBy, sortBy };
1274
+ export { ArrayProvider, CollectionStore, DataProvider, DataSource, DataSourceProxy, LocalProvider, MemoryStore, ModelMeta, ModelRefByIndex, ModelRefByKey, ModelRefNorm, ObservableProvider, UnknownMeta, filterBy, filterMerge, groupBy, grouperMerge, pathGetterCompile, queryExecutor, sliceApply, sliceClamp, sliceEq, sliceInsert, sliceMerge, sliceToPages, slimBy, slimerMerge, sortBy, sorterMerge };
474
1275
  //# sourceMappingURL=ngutil-data.mjs.map