@pythia-software/query-table-react 0.9.6 → 0.9.8
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 +66 -0
- package/dist/index.d.ts +79 -7
- package/dist/index.js +404 -94
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
// src/useQueryFilters.ts
|
|
2
2
|
import { useCallback, useMemo } from "react";
|
|
3
3
|
import {
|
|
4
|
+
branchesOf,
|
|
4
5
|
isOrGroup,
|
|
5
|
-
|
|
6
|
+
isWhereConjunction,
|
|
6
7
|
negateClause,
|
|
7
8
|
opsForField
|
|
8
9
|
} from "@pythia-software/query-table-core";
|
|
10
|
+
function editBranchPredicate(term, predIndex, edit) {
|
|
11
|
+
let k = 0;
|
|
12
|
+
const any = [];
|
|
13
|
+
for (const member of term.any) {
|
|
14
|
+
const kept = [];
|
|
15
|
+
for (const clause of isWhereConjunction(member) ? member.all : [member]) {
|
|
16
|
+
const next = k++ === predIndex ? edit(clause) : clause;
|
|
17
|
+
if (next) kept.push(next);
|
|
18
|
+
}
|
|
19
|
+
if (kept.length === 1) any.push(kept[0]);
|
|
20
|
+
else if (kept.length > 1) any.push({ all: kept });
|
|
21
|
+
}
|
|
22
|
+
if (any.length > 1) return [{ any }];
|
|
23
|
+
const only = any[0];
|
|
24
|
+
return !only ? [] : isWhereConjunction(only) ? only.all : [only];
|
|
25
|
+
}
|
|
9
26
|
function useQueryFilters(schema, setQuery) {
|
|
10
27
|
const byName = useMemo(
|
|
11
28
|
() => new Map(schema.fields.map((field) => [field.name, field])),
|
|
@@ -45,8 +62,9 @@ function useQueryFilters(schema, setQuery) {
|
|
|
45
62
|
return {
|
|
46
63
|
any: term.any.map((c, j) => j === predIndex ? clause : c)
|
|
47
64
|
};
|
|
65
|
+
if ("any" in term) return editBranchPredicate(term, predIndex, () => clause);
|
|
48
66
|
return clause;
|
|
49
|
-
})
|
|
67
|
+
}).flat()
|
|
50
68
|
})),
|
|
51
69
|
[setQuery]
|
|
52
70
|
);
|
|
@@ -64,6 +82,10 @@ function useQueryFilters(schema, setQuery) {
|
|
|
64
82
|
else if (members.length > 1) where.push({ any: members });
|
|
65
83
|
return;
|
|
66
84
|
}
|
|
85
|
+
if ("any" in term) {
|
|
86
|
+
where.push(...editBranchPredicate(term, predIndex, () => null));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
67
89
|
});
|
|
68
90
|
return { ...q, offset: 0, where };
|
|
69
91
|
}),
|
|
@@ -86,8 +108,9 @@ function useQueryFilters(schema, setQuery) {
|
|
|
86
108
|
return {
|
|
87
109
|
any: term.any.map((c, j) => j === predIndex ? negateOne(c) : c)
|
|
88
110
|
};
|
|
111
|
+
if ("any" in term) return editBranchPredicate(term, predIndex, negateOne);
|
|
89
112
|
return negateOne(term);
|
|
90
|
-
})
|
|
113
|
+
}).flat()
|
|
91
114
|
})),
|
|
92
115
|
[setQuery, negateOne]
|
|
93
116
|
);
|
|
@@ -108,7 +131,9 @@ function useQueryFilters(schema, setQuery) {
|
|
|
108
131
|
const target = q.where[targetIndex];
|
|
109
132
|
if (!source || !target) return q;
|
|
110
133
|
const merged = {
|
|
111
|
-
any: [...
|
|
134
|
+
any: [...branchesOf(target), ...branchesOf(source)].map(
|
|
135
|
+
(branch) => branch.length === 1 ? branch[0] : { all: branch }
|
|
136
|
+
)
|
|
112
137
|
};
|
|
113
138
|
const where = [];
|
|
114
139
|
q.where.forEach((term, i) => {
|
|
@@ -135,9 +160,10 @@ function useQueryFilters(schema, setQuery) {
|
|
|
135
160
|
// src/useGrouping.ts
|
|
136
161
|
import { useEffect, useRef, useState } from "react";
|
|
137
162
|
import {
|
|
163
|
+
groupKeyFields,
|
|
138
164
|
MAX_SUMMARY_COLUMNS,
|
|
139
165
|
normalizeQueryState,
|
|
140
|
-
predicatesOf
|
|
166
|
+
predicatesOf,
|
|
141
167
|
withGroupingKeys,
|
|
142
168
|
withoutGrouping,
|
|
143
169
|
queriesEqual
|
|
@@ -150,7 +176,8 @@ function useGroupingController(api, source, model, inputs, supported, viewRevisi
|
|
|
150
176
|
const view = {
|
|
151
177
|
version: 2,
|
|
152
178
|
mode: stage ? "groups" : "rows",
|
|
153
|
-
keys: stage?.keys ?? [],
|
|
179
|
+
keys: groupKeyFields(stage?.keys ?? []),
|
|
180
|
+
groupKeys: stage?.keys ?? [],
|
|
154
181
|
summaries: stage?.summaries ?? [],
|
|
155
182
|
representative: stage?.representative ?? null,
|
|
156
183
|
input: source.query,
|
|
@@ -205,7 +232,7 @@ function useGroupingController(api, source, model, inputs, supported, viewRevisi
|
|
|
205
232
|
offset: 0,
|
|
206
233
|
aggregations: [],
|
|
207
234
|
where: q.where.filter(
|
|
208
|
-
(term) => !
|
|
235
|
+
(term) => !predicatesOf(term).some((p) => changed.has(p.field))
|
|
209
236
|
),
|
|
210
237
|
orderBy: q.orderBy.filter((term) => !changed.has(term.field))
|
|
211
238
|
} : {}
|
|
@@ -213,7 +240,7 @@ function useGroupingController(api, source, model, inputs, supported, viewRevisi
|
|
|
213
240
|
);
|
|
214
241
|
setNotice(
|
|
215
242
|
changed.size && (api.query.where.some(
|
|
216
|
-
(term) =>
|
|
243
|
+
(term) => predicatesOf(term).some((p) => changed.has(p.field))
|
|
217
244
|
) || api.query.orderBy.some((term) => changed.has(term.field)) || api.query.aggregations?.length) ? "Columns updated. Affected filters, sorts, and metrics cleared; Undo restores them." : ""
|
|
218
245
|
);
|
|
219
246
|
}
|
|
@@ -321,6 +348,7 @@ import { ITEM_DETAILS_COLUMN, itemPageFields } from "@pythia-software/query-tabl
|
|
|
321
348
|
|
|
322
349
|
// src/useItemDetails.ts
|
|
323
350
|
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
351
|
+
import { isPropertyFieldLoaded, jsonInspectionScopeKey, MAX_JSON_INSPECTIONS } from "@pythia-software/query-table-core";
|
|
324
352
|
|
|
325
353
|
// src/usePropertyView.ts
|
|
326
354
|
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
@@ -509,7 +537,7 @@ function initialDetailModes(config) {
|
|
|
509
537
|
const available = ALL_MODES.filter((mode) => !config?.modes || config.modes.includes(mode));
|
|
510
538
|
return config?.defaultModes ? available.filter((mode) => config.defaultModes.includes(mode)) : available.includes("modal") ? ["modal"] : available.slice(0, 1);
|
|
511
539
|
}
|
|
512
|
-
function useDetailProperties(mode, schema, config, query, setQuery, enabled) {
|
|
540
|
+
function useDetailProperties(mode, schema, config, query, setQuery, enabled, readOnly = false) {
|
|
513
541
|
const snapshot = query.itemDetails?.views?.[mode] ?? query.itemDetails?.view;
|
|
514
542
|
const seed = useRef3(snapshot);
|
|
515
543
|
const properties = usePropertyView({
|
|
@@ -539,7 +567,7 @@ function useDetailProperties(mode, schema, config, query, setQuery, enabled) {
|
|
|
539
567
|
if (target !== localViewKey) properties.setView(next);
|
|
540
568
|
} else if (pendingView.current && pendingView.current.query === queryViewKey) {
|
|
541
569
|
if (pendingView.current.local === localViewKey) pendingView.current = null;
|
|
542
|
-
} else if (!properties.loading && localViewKey !== queryViewKey && (queryViewKey !== void 0 || localViewKey !== previous.local)) {
|
|
570
|
+
} else if (!readOnly && !properties.loading && localViewKey !== queryViewKey && (queryViewKey !== void 0 || localViewKey !== previous.local)) {
|
|
543
571
|
setQuery((current) => ({ ...current, itemDetails: {
|
|
544
572
|
modes: current.itemDetails?.modes ?? initialDetailModes(config),
|
|
545
573
|
...current.itemDetails,
|
|
@@ -561,7 +589,9 @@ function useItemDetails({
|
|
|
561
589
|
refreshKey,
|
|
562
590
|
links,
|
|
563
591
|
queryReady = true,
|
|
564
|
-
queryParameter: queryParameter2
|
|
592
|
+
queryParameter: queryParameter2,
|
|
593
|
+
lookupRow,
|
|
594
|
+
subject
|
|
565
595
|
}) {
|
|
566
596
|
const enabled = Boolean(options);
|
|
567
597
|
const config = typeof options === "object" ? options : void 0;
|
|
@@ -573,9 +603,10 @@ function useItemDetails({
|
|
|
573
603
|
const sharedModal = requested?.mode === "modal" && query.itemDetails?.modes.includes("modal") && (availableModes.includes("hover") || availableModes.includes("page") && !page.urlTemplate.trim());
|
|
574
604
|
const active = requested && (modes.includes(requested.mode) || sharedModal) ? requested : null;
|
|
575
605
|
const syncProperties = enabled && queryReady;
|
|
576
|
-
const
|
|
577
|
-
const
|
|
578
|
-
const
|
|
606
|
+
const readOnly = subject === "group";
|
|
607
|
+
const modalProperties = useDetailProperties("modal", detailSchema, config, query, setQuery, syncProperties, readOnly);
|
|
608
|
+
const hoverProperties = useDetailProperties("hover", detailSchema, config, query, setQuery, syncProperties, readOnly);
|
|
609
|
+
const expandProperties = useDetailProperties("expand", detailSchema, config, query, setQuery, syncProperties, readOnly);
|
|
579
610
|
const propertiesByMode = { modal: modalProperties, hover: hoverProperties, expand: expandProperties };
|
|
580
611
|
const views = {
|
|
581
612
|
modal: modalProperties.view,
|
|
@@ -585,10 +616,14 @@ function useItemDetails({
|
|
|
585
616
|
const customLoad = config?.loadRow;
|
|
586
617
|
const customLoadRef = useRef3(customLoad);
|
|
587
618
|
customLoadRef.current = customLoad;
|
|
619
|
+
const lookupRef = useRef3(lookupRow);
|
|
620
|
+
lookupRef.current = lookupRow;
|
|
621
|
+
const hasLookup = Boolean(lookupRow);
|
|
588
622
|
const hasCustomLoad = Boolean(customLoad);
|
|
589
623
|
const sourceType = config?.objectType ?? detailSchema.name;
|
|
590
624
|
const fallbackRows = !customLoad && !clientRows && !transport?.fetchRow ? rows : void 0;
|
|
591
625
|
const loadRow = useCallback3(async (id, signal) => {
|
|
626
|
+
if (lookupRef.current) return lookupRef.current(id) ?? null;
|
|
592
627
|
if (customLoadRef.current) return customLoadRef.current(id, signal);
|
|
593
628
|
if (clientRows) {
|
|
594
629
|
const row2 = clientRows.find((row3) => rowId(row3) === id);
|
|
@@ -600,10 +635,10 @@ function useItemDetails({
|
|
|
600
635
|
}
|
|
601
636
|
const row = rows.find((row2) => rowId(row2) === id);
|
|
602
637
|
return row ? { row, loadedFields: Object.keys(row) } : null;
|
|
603
|
-
}, [hasCustomLoad, sourceType, clientRows, transport?.fetchRow, fallbackRows, rowId]);
|
|
638
|
+
}, [hasLookup, hasCustomLoad, sourceType, clientRows, transport?.fetchRow, fallbackRows, rowId]);
|
|
604
639
|
const [retryKey, setRetryKey] = useState3(0);
|
|
605
640
|
const [result, setResult] = useState3({ source: void 0, epoch: "", id: void 0, record: null, loading: false, error: null });
|
|
606
|
-
const activeId = enabled ? active?.id : void 0;
|
|
641
|
+
const activeId = enabled && !lookupRow ? active?.id : void 0;
|
|
607
642
|
const epoch = `${refreshKey}:${retryKey}`;
|
|
608
643
|
const currentResult = result.id === activeId && result.source === loadRow && result.epoch === epoch;
|
|
609
644
|
useEffect3(() => {
|
|
@@ -612,7 +647,7 @@ function useItemDetails({
|
|
|
612
647
|
setResult((previous) => previous.id === void 0 && !previous.loading && !previous.error && !previous.record ? previous : { source: void 0, epoch: "", id: void 0, record: null, loading: false, error: null });
|
|
613
648
|
return;
|
|
614
649
|
}
|
|
615
|
-
setResult({ source: loadRow, epoch, id: activeId, record: null, loading: true, error: null });
|
|
650
|
+
setResult((previous) => ({ source: loadRow, epoch, id: activeId, record: previous.id === activeId && previous.source === loadRow ? previous.record : null, loading: true, error: null }));
|
|
616
651
|
void Promise.resolve().then(() => loadRow(activeId, controller.signal)).then((record) => {
|
|
617
652
|
if (!controller.signal.aborted) setResult({ source: loadRow, epoch, id: activeId, record, loading: false, error: null });
|
|
618
653
|
}).catch((error) => {
|
|
@@ -627,9 +662,66 @@ function useItemDetails({
|
|
|
627
662
|
});
|
|
628
663
|
return () => controller.abort();
|
|
629
664
|
}, [activeId, loadRow, refreshKey, retryKey]);
|
|
665
|
+
const preserveFocus = useRef3(false);
|
|
666
|
+
const attentionSequence = useRef3(0);
|
|
667
|
+
const [attentionRequest, setAttentionRequest] = useState3({ revision: 0, pending: false });
|
|
668
|
+
const attentionLatest = useRef3({ loadRow, setQuery, modes, views, active, query });
|
|
669
|
+
attentionLatest.current = { loadRow, setQuery, modes, views, active, query };
|
|
670
|
+
useEffect3(() => {
|
|
671
|
+
const registry = config?.attention;
|
|
672
|
+
if (!enabled || !registry) return;
|
|
673
|
+
const provider = { reveal: async (target, signal) => {
|
|
674
|
+
if (target.rowId === void 0 || signal.aborted) return false;
|
|
675
|
+
const current = attentionLatest.current;
|
|
676
|
+
const record = await current.loadRow(target.rowId, signal);
|
|
677
|
+
if (!record || signal.aborted) return false;
|
|
678
|
+
if (target.field !== void 0 && !detailSchema.fields.some((field) => field.name === target.field && field.property?.enabled !== false && isPropertyFieldLoaded(field, record.loadedFields))) return false;
|
|
679
|
+
const mode = current.active?.id === target.rowId ? current.active.mode : current.modes.includes("expand") ? "expand" : current.modes.includes("modal") ? "modal" : null;
|
|
680
|
+
if (!mode || target.field !== void 0 && !current.views[mode].properties.some((property) => property.field === target.field)) return false;
|
|
681
|
+
preserveFocus.current = true;
|
|
682
|
+
const revision = ++attentionSequence.current;
|
|
683
|
+
setAttentionRequest({ revision, pending: true });
|
|
684
|
+
try {
|
|
685
|
+
current.setQuery((previous) => ({ ...previous, itemDetails: {
|
|
686
|
+
...previous.itemDetails,
|
|
687
|
+
modes: previous.itemDetails?.modes ?? current.modes,
|
|
688
|
+
views: current.views,
|
|
689
|
+
active: { id: target.rowId, mode }
|
|
690
|
+
} }));
|
|
691
|
+
const mounted = await registry.whenRegistered({ dataset: detailSchema.name, rowId: target.rowId }, signal);
|
|
692
|
+
if (mounted.ready) await mounted.ready(signal);
|
|
693
|
+
return signal.aborted ? false : await mounted.reveal(target, signal);
|
|
694
|
+
} finally {
|
|
695
|
+
setAttentionRequest((previous) => previous.revision === revision ? { revision, pending: false } : previous);
|
|
696
|
+
}
|
|
697
|
+
} };
|
|
698
|
+
return registry.register({ dataset: detailSchema.name }, provider);
|
|
699
|
+
}, [enabled, config?.attention, detailSchema]);
|
|
630
700
|
if (!enabled) return;
|
|
701
|
+
const looked = lookupRow && active ? lookupRow(active.id) : null;
|
|
631
702
|
return {
|
|
703
|
+
...subject ? { subject } : {},
|
|
632
704
|
...links ? { links } : {},
|
|
705
|
+
...config?.attention ? { attention: config.attention } : {},
|
|
706
|
+
preserveFocus: preserveFocus.current,
|
|
707
|
+
attentionRevision: attentionRequest.revision,
|
|
708
|
+
attentionPending: attentionRequest.pending,
|
|
709
|
+
jsonInspections: query.itemDetails?.jsonInspections ?? [],
|
|
710
|
+
setJsonInspection(entry) {
|
|
711
|
+
if (entry.dataset !== detailSchema.name) return;
|
|
712
|
+
setQuery((current) => {
|
|
713
|
+
const inspections = (current.itemDetails?.jsonInspections ?? []).filter((previous) => jsonInspectionScopeKey(previous) !== jsonInspectionScopeKey(entry));
|
|
714
|
+
while (inspections.length >= MAX_JSON_INSPECTIONS) {
|
|
715
|
+
const closed = inspections.findIndex((previous) => !previous.state.open);
|
|
716
|
+
inspections.splice(closed < 0 ? 0 : closed, 1);
|
|
717
|
+
}
|
|
718
|
+
return { ...current, itemDetails: {
|
|
719
|
+
...current.itemDetails,
|
|
720
|
+
modes: current.itemDetails?.modes ?? modes,
|
|
721
|
+
jsonInspections: [...inspections, entry]
|
|
722
|
+
} };
|
|
723
|
+
});
|
|
724
|
+
},
|
|
633
725
|
schema: detailSchema,
|
|
634
726
|
queryParameter: queryParameter2 ?? config?.queryParameter ?? "q",
|
|
635
727
|
properties: modalProperties,
|
|
@@ -639,9 +731,9 @@ function useItemDetails({
|
|
|
639
731
|
active,
|
|
640
732
|
page,
|
|
641
733
|
pageSchema: schema,
|
|
642
|
-
record:
|
|
643
|
-
loading: activeId !== void 0 && (!currentResult || result.loading),
|
|
644
|
-
error: currentResult ? result.error : null,
|
|
734
|
+
record: lookupRow ? looked ?? null : result.id === activeId && result.source === loadRow ? result.record : null,
|
|
735
|
+
loading: lookupRow ? looked === void 0 : activeId !== void 0 && (!currentResult || result.loading),
|
|
736
|
+
error: lookupRow ? null : currentResult ? result.error : null,
|
|
645
737
|
...config?.relationships ? { relationships: config.relationships } : {},
|
|
646
738
|
loadRow,
|
|
647
739
|
retry: () => setRetryKey((value) => value + 1),
|
|
@@ -654,6 +746,8 @@ function useItemDetails({
|
|
|
654
746
|
});
|
|
655
747
|
},
|
|
656
748
|
open(id, mode) {
|
|
749
|
+
config?.attention?.cancel();
|
|
750
|
+
preserveFocus.current = false;
|
|
657
751
|
if (!modes.includes(mode)) return;
|
|
658
752
|
setQuery((current) => ({ ...current, itemDetails: {
|
|
659
753
|
...current.itemDetails,
|
|
@@ -663,6 +757,7 @@ function useItemDetails({
|
|
|
663
757
|
} }));
|
|
664
758
|
},
|
|
665
759
|
close() {
|
|
760
|
+
config?.attention?.cancel();
|
|
666
761
|
setQuery((current) => {
|
|
667
762
|
if (!current.itemDetails?.active) return current;
|
|
668
763
|
const { active: _active, ...state } = current.itemDetails;
|
|
@@ -674,6 +769,7 @@ function useItemDetails({
|
|
|
674
769
|
|
|
675
770
|
// src/computedExecution.ts
|
|
676
771
|
import {
|
|
772
|
+
predicatesOf as predicatesOf2,
|
|
677
773
|
isComputedField,
|
|
678
774
|
readFieldValue
|
|
679
775
|
} from "@pythia-software/query-table-core";
|
|
@@ -689,7 +785,7 @@ function validateComputedResponse(request, result, schema) {
|
|
|
689
785
|
const executed = /* @__PURE__ */ new Set([
|
|
690
786
|
...request.select.filter(isComputedField),
|
|
691
787
|
...request.orderBy.map((term) => term.field).filter(isComputedField),
|
|
692
|
-
...request.where.flatMap((term) => (
|
|
788
|
+
...request.where.flatMap((term) => predicatesOf2(term).map((predicate) => predicate.field)).filter(isComputedField)
|
|
693
789
|
]);
|
|
694
790
|
for (const [name, revision] of Object.entries(request.expectedRevisions)) {
|
|
695
791
|
const id = name.replace(/^@computed\//, "");
|
|
@@ -724,6 +820,7 @@ function validateComputedResponse(request, result, schema) {
|
|
|
724
820
|
|
|
725
821
|
// src/previewRows.ts
|
|
726
822
|
import {
|
|
823
|
+
predicatesOf as predicatesOf3,
|
|
727
824
|
sourceExecutionSchema,
|
|
728
825
|
toQueryRequestV3,
|
|
729
826
|
toServerQuery,
|
|
@@ -734,13 +831,13 @@ async function fetchPreviewRows(transport, query, schema, execution, definitions
|
|
|
734
831
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
735
832
|
const sourceSchema = sourceExecutionSchema(baseSchema, execution, definitions);
|
|
736
833
|
let result;
|
|
737
|
-
const computedFilter = query.where.some((term) => (
|
|
834
|
+
const computedFilter = query.where.some((term) => predicatesOf3(term).some((predicate) => predicate.field.startsWith("@computed/")));
|
|
738
835
|
if (transport.fetchRowsV2 && execution && !query.grouping && !computedFilter) {
|
|
739
836
|
const request = toServerQueryV2(query, sourceSchema, execution);
|
|
740
837
|
if (request.diagnostics.length)
|
|
741
838
|
throw Error(request.diagnostics.map((issue) => issue.message).join(" "));
|
|
742
839
|
for (const definition of definitions) {
|
|
743
|
-
if (executionRevision(execution, definition.id) !== definition.revision && (request.select.includes(`@computed/${definition.id}`) || request.orderBy.some((term) => term.field === `@computed/${definition.id}`) || request.where.some((term) => (
|
|
840
|
+
if (executionRevision(execution, definition.id) !== definition.revision && (request.select.includes(`@computed/${definition.id}`) || request.orderBy.some((term) => term.field === `@computed/${definition.id}`) || request.where.some((term) => predicatesOf3(term).some((predicate) => predicate.field === `@computed/${definition.id}`))))
|
|
744
841
|
throw Error("Computed definitions changed. Refresh the server execution handshake.");
|
|
745
842
|
}
|
|
746
843
|
const response = await transport.fetchRowsV2(request, signal);
|
|
@@ -772,6 +869,7 @@ async function fetchPreviewRows(transport, query, schema, execution, definitions
|
|
|
772
869
|
// src/useComputedColumns.ts
|
|
773
870
|
import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
|
|
774
871
|
import {
|
|
872
|
+
predicatesOf as predicatesOf4,
|
|
775
873
|
isGroupLinkField,
|
|
776
874
|
applyQuery,
|
|
777
875
|
compileFormula,
|
|
@@ -1173,7 +1271,7 @@ function useComputedColumns(options) {
|
|
|
1173
1271
|
offset: 0
|
|
1174
1272
|
};
|
|
1175
1273
|
const now = options.now ?? Date.now();
|
|
1176
|
-
const computedFilter = request.where.some((term) => (
|
|
1274
|
+
const computedFilter = request.where.some((term) => predicatesOf4(term).some((predicate) => isComputedField2(predicate.field)));
|
|
1177
1275
|
const v3 = !(transport.fetchRowsV2 && options.execution && !request.grouping && !computedFilter) && transport.fetchQueryV3 && transport.queryCapabilities?.version === 3;
|
|
1178
1276
|
const pageSize = Math.min(500, v3 ? transport.queryCapabilities.maxRows : 500);
|
|
1179
1277
|
if (!Number.isInteger(pageSize) || pageSize < 1) throw Error("Invalid negotiated preview row limit.");
|
|
@@ -1262,17 +1360,22 @@ import {
|
|
|
1262
1360
|
applyQuery as applyQuery2,
|
|
1263
1361
|
memoryStorageAdapter,
|
|
1264
1362
|
normalizeQueryState as normalizeQueryState3,
|
|
1363
|
+
parseHostQueryState,
|
|
1265
1364
|
readFieldValue as readFieldValue4,
|
|
1266
1365
|
selectedFields as selectedFields2,
|
|
1267
1366
|
toServerQuery as toServerQuery2,
|
|
1268
1367
|
queriesEqual as queriesEqual2,
|
|
1269
|
-
|
|
1368
|
+
isWhereConjunction as isWhereConjunction2,
|
|
1369
|
+
isWhereGroup,
|
|
1370
|
+
predicatesOf as predicatesOf6,
|
|
1270
1371
|
metricPlans as metricPlans2,
|
|
1271
1372
|
validateMetric as validateMetric2,
|
|
1272
|
-
metricDependencies,
|
|
1373
|
+
metricDependencies as metricDependencies2,
|
|
1273
1374
|
isComputedField as isComputedField4,
|
|
1274
1375
|
computedFieldName as computedFieldName2,
|
|
1275
|
-
toServerQueryV2 as toServerQueryV22
|
|
1376
|
+
toServerQueryV2 as toServerQueryV22,
|
|
1377
|
+
groupKeyField,
|
|
1378
|
+
groupKeyFields as groupKeyFields2
|
|
1276
1379
|
} from "@pythia-software/query-table-core";
|
|
1277
1380
|
|
|
1278
1381
|
// src/useRequestActivity.ts
|
|
@@ -1385,11 +1488,12 @@ import {
|
|
|
1385
1488
|
normalizeQueryState as normalizeQueryState2,
|
|
1386
1489
|
evaluateMetrics,
|
|
1387
1490
|
metricPlans,
|
|
1491
|
+
metricDependencies,
|
|
1388
1492
|
hasRegex as hasRegex2,
|
|
1389
1493
|
metricPopulationError,
|
|
1390
1494
|
hasMetricSeries,
|
|
1391
1495
|
planMetricSeries,
|
|
1392
|
-
predicatesOf as
|
|
1496
|
+
predicatesOf as predicatesOf5,
|
|
1393
1497
|
toAggregationQuery,
|
|
1394
1498
|
toMetricQuery,
|
|
1395
1499
|
validateMetric,
|
|
@@ -1545,11 +1649,10 @@ async function executeMetrics(query, context, signal) {
|
|
|
1545
1649
|
shown.flatMap((clause) => [
|
|
1546
1650
|
...clause.groupBy,
|
|
1547
1651
|
...(clause.where ?? []).flatMap(
|
|
1548
|
-
(term) =>
|
|
1652
|
+
(term) => predicatesOf5(term).map((predicate) => predicate.field)
|
|
1549
1653
|
),
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
)
|
|
1654
|
+
// Includes a measured minimum's field, which the page must also hold.
|
|
1655
|
+
...metricDependencies(clause, context.schema, context.resolveComputed)
|
|
1553
1656
|
])
|
|
1554
1657
|
);
|
|
1555
1658
|
for (const name of names) {
|
|
@@ -2134,7 +2237,7 @@ function useSavedQueries(key, currentQuery, applyQueryState, storage, now, snaps
|
|
|
2134
2237
|
// src/useQueryLinks.ts
|
|
2135
2238
|
import { useCallback as useCallback11, useEffect as useEffect9, useRef as useRef8, useState as useState10 } from "react";
|
|
2136
2239
|
import { encodeQuery } from "@pythia-software/query-table-core";
|
|
2137
|
-
function useQueryLinks(query, service, syncUrl, ready, parameter, maxUrlBytes, onUrlChange) {
|
|
2240
|
+
function useQueryLinks(query, service, syncUrl, ready, parameter, maxUrlBytes, onUrlChange, decorate = (url) => url) {
|
|
2138
2241
|
const latest = useRef8(query);
|
|
2139
2242
|
latest.current = query;
|
|
2140
2243
|
const [kind, setKind] = useState10(null);
|
|
@@ -2153,14 +2256,15 @@ function useQueryLinks(query, service, syncUrl, ready, parameter, maxUrlBytes, o
|
|
|
2153
2256
|
if (!syncUrl || !ready || typeof window === "undefined") return;
|
|
2154
2257
|
try {
|
|
2155
2258
|
const result = service.local(query, window.location.href, parameter);
|
|
2156
|
-
|
|
2157
|
-
|
|
2259
|
+
const url = decorate(result.url, query);
|
|
2260
|
+
window.history.replaceState(window.history.state, "", url);
|
|
2261
|
+
onUrlChange(url);
|
|
2158
2262
|
setKind(result.kind);
|
|
2159
2263
|
setError(null);
|
|
2160
2264
|
} catch (e) {
|
|
2161
2265
|
setError(e instanceof Error ? e : Error(String(e)));
|
|
2162
2266
|
}
|
|
2163
|
-
}, [queryKey, syncUrl, ready, service, parameter, onUrlChange]);
|
|
2267
|
+
}, [queryKey, syncUrl, ready, service, parameter, onUrlChange, decorate]);
|
|
2164
2268
|
const snapshot = useCallback11(async (q) => {
|
|
2165
2269
|
const captured = encodeQuery(q);
|
|
2166
2270
|
const base = typeof window !== "undefined" ? window.location.href : void 0;
|
|
@@ -2174,8 +2278,9 @@ function useQueryLinks(query, service, syncUrl, ready, parameter, maxUrlBytes, o
|
|
|
2174
2278
|
const url = new URL(base);
|
|
2175
2279
|
url.searchParams.set(parameter, "@" + saved.code);
|
|
2176
2280
|
const result = service.local(q, url.toString(), parameter);
|
|
2177
|
-
|
|
2178
|
-
|
|
2281
|
+
const next = decorate(result.url, q);
|
|
2282
|
+
window.history.replaceState(window.history.state, "", next);
|
|
2283
|
+
onUrlChange(next);
|
|
2179
2284
|
setKind("durable");
|
|
2180
2285
|
}
|
|
2181
2286
|
return saved;
|
|
@@ -2186,7 +2291,7 @@ function useQueryLinks(query, service, syncUrl, ready, parameter, maxUrlBytes, o
|
|
|
2186
2291
|
operations.current--;
|
|
2187
2292
|
if (mounted.current) setBusy(operations.current > 0);
|
|
2188
2293
|
}
|
|
2189
|
-
}, [service, syncUrl, ready, parameter, onUrlChange]);
|
|
2294
|
+
}, [service, syncUrl, ready, parameter, onUrlChange, decorate]);
|
|
2190
2295
|
const create = useCallback11(async (q = latest.current, options = {}) => {
|
|
2191
2296
|
const baseUrl = options.baseUrl ?? window.location.href;
|
|
2192
2297
|
const param = options.parameter ?? parameter;
|
|
@@ -2196,7 +2301,8 @@ function useQueryLinks(query, service, syncUrl, ready, parameter, maxUrlBytes, o
|
|
|
2196
2301
|
setError(null);
|
|
2197
2302
|
try {
|
|
2198
2303
|
if (!ready) throw Error("Resolve or reset the query link before sharing it.");
|
|
2199
|
-
const
|
|
2304
|
+
const durable = await service.durable(q, baseUrl, param);
|
|
2305
|
+
const result = param === parameter ? { ...durable, url: decorate(durable.url, q) } : durable;
|
|
2200
2306
|
if (mounted.current && syncUrl && ready && param === parameter && window.location.href === baseUrl && encodeQuery(latest.current) === captured) {
|
|
2201
2307
|
window.history.replaceState(window.history.state, "", result.url);
|
|
2202
2308
|
onUrlChange(result.url);
|
|
@@ -2210,8 +2316,11 @@ function useQueryLinks(query, service, syncUrl, ready, parameter, maxUrlBytes, o
|
|
|
2210
2316
|
operations.current--;
|
|
2211
2317
|
if (mounted.current) setBusy(operations.current > 0);
|
|
2212
2318
|
}
|
|
2213
|
-
}, [service, syncUrl, ready, parameter, onUrlChange]);
|
|
2214
|
-
const local = useCallback11((q, base = window.location.href, param = parameter) =>
|
|
2319
|
+
}, [service, syncUrl, ready, parameter, onUrlChange, decorate]);
|
|
2320
|
+
const local = useCallback11((q, base = window.location.href, param = parameter) => {
|
|
2321
|
+
const result = service.local(q, base, param);
|
|
2322
|
+
return param === parameter ? { ...result, url: decorate(result.url, q) } : result;
|
|
2323
|
+
}, [service, parameter, decorate]);
|
|
2215
2324
|
return { maxUrlBytes, kind, busy, error, create, local, snapshot };
|
|
2216
2325
|
}
|
|
2217
2326
|
|
|
@@ -2248,7 +2357,7 @@ function defaultsFor(schema, details = false) {
|
|
|
2248
2357
|
});
|
|
2249
2358
|
}
|
|
2250
2359
|
function cloneWhereTerm(term) {
|
|
2251
|
-
const copy =
|
|
2360
|
+
const copy = isWhereGroup(term) ? { ...term, any: term.any.map((c) => isWhereConjunction2(c) ? { all: c.all.map((m) => ({ ...m })) } : { ...c }) } : { ...term };
|
|
2252
2361
|
if (term.setFilter)
|
|
2253
2362
|
copy.setFilter = { ...term.setFilter, values: [...term.setFilter.values] };
|
|
2254
2363
|
return copy;
|
|
@@ -2264,6 +2373,7 @@ function cloneQueryState(q) {
|
|
|
2264
2373
|
offset: q.offset
|
|
2265
2374
|
};
|
|
2266
2375
|
if (q.fitToScreen) clone.fitToScreen = true;
|
|
2376
|
+
if (q.hostState) clone.hostState = { version: q.hostState.version, params: Object.fromEntries(Object.entries(q.hostState.params).map(([name, values]) => [name, [...values]])) };
|
|
2267
2377
|
if (q.aggregations)
|
|
2268
2378
|
clone.aggregations = JSON.parse(
|
|
2269
2379
|
JSON.stringify(q.aggregations)
|
|
@@ -2325,16 +2435,61 @@ function hasQueryLink(url, parameter) {
|
|
|
2325
2435
|
function queryParameter(opts) {
|
|
2326
2436
|
return opts.queryParameter ?? (typeof opts.itemDetails === "object" ? opts.itemDetails.queryParameter : void 0) ?? "q";
|
|
2327
2437
|
}
|
|
2438
|
+
function withHostState(query, host) {
|
|
2439
|
+
if (!host) {
|
|
2440
|
+
if (query.hostState) throw Error("This query includes host state that this table does not support.");
|
|
2441
|
+
return query;
|
|
2442
|
+
}
|
|
2443
|
+
const raw = query.hostState ?? host.defaults;
|
|
2444
|
+
if (raw.version === 0) throw Error("This query's host state could not be read.");
|
|
2445
|
+
const state = parseHostQueryState(host.normalize ? host.normalize(parseHostQueryState(raw)) : raw);
|
|
2446
|
+
return { ...query, hostState: state };
|
|
2447
|
+
}
|
|
2448
|
+
function withoutHostState(query) {
|
|
2449
|
+
const { hostState: _hostState, ...rest } = query;
|
|
2450
|
+
return rest;
|
|
2451
|
+
}
|
|
2452
|
+
function hostStateFromUrl(location, host) {
|
|
2453
|
+
const url = new URL(location);
|
|
2454
|
+
const params = {};
|
|
2455
|
+
for (const name of host.ownedParameters) {
|
|
2456
|
+
const values = url.searchParams.getAll(name);
|
|
2457
|
+
if (values.length) params[name] = values;
|
|
2458
|
+
}
|
|
2459
|
+
return Object.keys(params).length ? { version: host.defaults.version, params } : void 0;
|
|
2460
|
+
}
|
|
2461
|
+
function writeHostParameters(url, host, state) {
|
|
2462
|
+
const next = new URL(url);
|
|
2463
|
+
for (const name of host.ownedParameters) next.searchParams.delete(name);
|
|
2464
|
+
for (const name of host.ownedParameters)
|
|
2465
|
+
for (const value of state?.params[name] ?? []) next.searchParams.append(name, value);
|
|
2466
|
+
return next.toString();
|
|
2467
|
+
}
|
|
2468
|
+
var HOST_REQUEST_METHODS = /* @__PURE__ */ new Set(["fetchRows", "fetchRowsV2", "fetchQueryV3", "fetchMetrics", "fetchAggregations", "fetchDistinctValues"]);
|
|
2469
|
+
function withHostTransport(transport, hostStateKey) {
|
|
2470
|
+
if (!transport || !hostStateKey) return transport;
|
|
2471
|
+
const hostState = JSON.parse(hostStateKey);
|
|
2472
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
2473
|
+
return new Proxy(Object.create(transport), {
|
|
2474
|
+
get(_target, key) {
|
|
2475
|
+
const method = Reflect.get(transport, key, transport);
|
|
2476
|
+
if (!HOST_REQUEST_METHODS.has(String(key)) || typeof method !== "function") return method;
|
|
2477
|
+
if (!wrappers.has(key))
|
|
2478
|
+
wrappers.set(key, (request, signal) => method.call(transport, { ...request, hostState }, signal));
|
|
2479
|
+
return wrappers.get(key);
|
|
2480
|
+
}
|
|
2481
|
+
});
|
|
2482
|
+
}
|
|
2328
2483
|
function resolveInitial(opts) {
|
|
2329
|
-
const defaults = defaultsFor(opts.schema, Boolean(opts.itemDetails));
|
|
2484
|
+
const defaults = withHostState(defaultsFor(opts.schema, Boolean(opts.itemDetails)), opts.hostQuery);
|
|
2330
2485
|
if (opts.initialQuery)
|
|
2331
2486
|
return {
|
|
2332
|
-
q: normalizeQueryState3(opts.initialQuery, defaults),
|
|
2487
|
+
q: withHostState(normalizeQueryState3(opts.initialQuery, defaults), opts.hostQuery),
|
|
2333
2488
|
fromUrl: false
|
|
2334
2489
|
};
|
|
2335
2490
|
if (opts.syncUrl === true && opts.active !== false && typeof window !== "undefined") {
|
|
2336
2491
|
const parameter = queryParameter(opts);
|
|
2337
|
-
if (hasQueryLink(window.location.href, parameter))
|
|
2492
|
+
if (hasQueryLink(window.location.href, parameter) || opts.hostQuery && hostStateFromUrl(window.location.href, opts.hostQuery))
|
|
2338
2493
|
return {
|
|
2339
2494
|
q: defaults,
|
|
2340
2495
|
fromUrl: true
|
|
@@ -2353,8 +2508,13 @@ function useQueryTable(opts) {
|
|
|
2353
2508
|
const active = opts.active !== false;
|
|
2354
2509
|
const urlSyncEnabled = opts.syncUrl === true;
|
|
2355
2510
|
const syncUrl = urlSyncEnabled && active;
|
|
2356
|
-
const { transport:
|
|
2357
|
-
const
|
|
2511
|
+
const { transport: activityTransport, metricTransport: activityMetricTransport, activity: requestActivity } = useRequestActivity(sourceTransport, opts.metricTransport);
|
|
2512
|
+
const host = opts.hostQuery;
|
|
2513
|
+
const hostRef = useRef9(host);
|
|
2514
|
+
hostRef.current = host;
|
|
2515
|
+
const hostDefaultsKey = host ? JSON.stringify(host.defaults) : "";
|
|
2516
|
+
if (host?.ownedParameters.includes(queryParameter(opts)))
|
|
2517
|
+
throw Error(`hostQuery.ownedParameters cannot include the table's own "${queryParameter(opts)}" parameter.`);
|
|
2358
2518
|
const storage = useMemo9(
|
|
2359
2519
|
() => opts.storage ?? memoryStorageAdapter(),
|
|
2360
2520
|
[opts.storage]
|
|
@@ -2367,7 +2527,10 @@ function useQueryTable(opts) {
|
|
|
2367
2527
|
const now = opts.now ?? Date.now;
|
|
2368
2528
|
const nowRef = useRef9(now);
|
|
2369
2529
|
const onRefreshRef = useRef9(opts.onRefresh);
|
|
2370
|
-
const defaults = useMemo9(
|
|
2530
|
+
const defaults = useMemo9(
|
|
2531
|
+
() => withHostState(defaultsFor(sourceSchema, Boolean(opts.itemDetails)), hostRef.current),
|
|
2532
|
+
[sourceSchema, Boolean(opts.itemDetails), hostDefaultsKey]
|
|
2533
|
+
);
|
|
2371
2534
|
const initRef = useRef9(resolveInitial(opts));
|
|
2372
2535
|
const [restoration, setRestoration] = useState11({ loading: initRef.current.fromUrl, error: null });
|
|
2373
2536
|
const [storageReady, setStorageReady] = useState11(Boolean(initialQuery));
|
|
@@ -2404,6 +2567,10 @@ function useQueryTable(opts) {
|
|
|
2404
2567
|
() => opts.canonicalizeQuery?.(storedQuery) ?? storedQuery,
|
|
2405
2568
|
[storedQuery, opts.canonicalizeQuery]
|
|
2406
2569
|
);
|
|
2570
|
+
const hostStateKey = host && query.hostState ? JSON.stringify(query.hostState) : "";
|
|
2571
|
+
const metadataTransport = useMemo9(() => withHostTransport(activityTransport, hostStateKey), [activityTransport, hostStateKey]);
|
|
2572
|
+
const metricTransport = useMemo9(() => withHostTransport(activityMetricTransport, hostStateKey), [activityMetricTransport, hostStateKey]);
|
|
2573
|
+
const transport = metadataTransport;
|
|
2407
2574
|
const sourceCatalogue = useMemo9(() => sourceExecutionSchema2(sourceSchema, opts.computedExecution), [sourceSchema, opts.computedExecution]);
|
|
2408
2575
|
const sourceRows = providedRows;
|
|
2409
2576
|
const groupingModel = useMemo9(() => createGroupingModel(sourceCatalogue), [sourceCatalogue]);
|
|
@@ -2433,6 +2600,7 @@ function useQueryTable(opts) {
|
|
|
2433
2600
|
const executionQuery = useMemo9(() => withoutGrouping2(query), [query]);
|
|
2434
2601
|
const byName = useMemo9(() => new Map(schema.fields.map((field) => [field.name, field])), [schema]);
|
|
2435
2602
|
const detailsEnabled = Boolean(opts.itemDetails) && !query.grouping;
|
|
2603
|
+
const groupDetailsEnabled = Boolean(opts.itemDetails) && Boolean(query.grouping);
|
|
2436
2604
|
const validateExecution = useCallback12((q) => {
|
|
2437
2605
|
if (prepared?.error) throw prepared.error;
|
|
2438
2606
|
opts.validateQuery?.(query.grouping ? { ...q, grouping: query.grouping } : q);
|
|
@@ -2445,6 +2613,7 @@ function useQueryTable(opts) {
|
|
|
2445
2613
|
const [loading, setLoading] = useState11(false);
|
|
2446
2614
|
const [error, setError] = useState11(null);
|
|
2447
2615
|
const [committedPage, setCommittedPage] = useState11(null);
|
|
2616
|
+
const [failedPage, setFailedPage] = useState11(null);
|
|
2448
2617
|
const [committedSource, setCommittedSource] = useState11(null);
|
|
2449
2618
|
const rows = useMemo9(() => committedSource?.schema === schema ? committedRows : [], [committedSource?.schema, schema, committedRows]);
|
|
2450
2619
|
const [autoRefreshStatus, setAutoRefreshStatus] = useState11(null);
|
|
@@ -2464,7 +2633,8 @@ function useQueryTable(opts) {
|
|
|
2464
2633
|
updateQueryHistory((history) => {
|
|
2465
2634
|
const prev = history.query;
|
|
2466
2635
|
const candidate = typeof next === "function" ? next(prev) : next;
|
|
2467
|
-
const
|
|
2636
|
+
const normalized = normalizeQueryState3(candidate, defaults);
|
|
2637
|
+
const nextQuery = withHostState(normalized.hostState || !prev.hostState ? normalized : { ...normalized, hostState: prev.hostState }, hostRef.current);
|
|
2468
2638
|
if (queriesEqual2(prev, nextQuery)) return history;
|
|
2469
2639
|
return { query: cloneQueryState(nextQuery), past: [...history.past, cloneQueryState(prev)], future: [] };
|
|
2470
2640
|
});
|
|
@@ -2481,9 +2651,15 @@ function useQueryTable(opts) {
|
|
|
2481
2651
|
}, [setQuery]);
|
|
2482
2652
|
const sourceFilters = useQueryFilters(sourceCatalogue, setSourceQuery);
|
|
2483
2653
|
const observedQueryUrl = useRef9(null);
|
|
2654
|
+
const tableUrlKey = (url) => {
|
|
2655
|
+
const params = new URL(url).searchParams;
|
|
2656
|
+
return JSON.stringify([params.getAll(parameter), ...(hostRef.current?.ownedParameters ?? []).map((name) => params.getAll(name))]);
|
|
2657
|
+
};
|
|
2484
2658
|
const recordQueryUrl = useCallback12((url) => {
|
|
2485
|
-
observedQueryUrl.current =
|
|
2659
|
+
observedQueryUrl.current = tableUrlKey(url);
|
|
2486
2660
|
}, [parameter]);
|
|
2661
|
+
const ownedParametersKey = JSON.stringify(host?.ownedParameters ?? null);
|
|
2662
|
+
const decorateLink = useCallback12((url, q) => hostRef.current ? writeHostParameters(url, hostRef.current, q.hostState) : url, [ownedParametersKey]);
|
|
2487
2663
|
const links = useQueryLinks(
|
|
2488
2664
|
query,
|
|
2489
2665
|
linkService,
|
|
@@ -2491,7 +2667,8 @@ function useQueryTable(opts) {
|
|
|
2491
2667
|
storageReady && !restoration.loading && !restoration.error,
|
|
2492
2668
|
parameter,
|
|
2493
2669
|
opts.links?.maxUrlBytes ?? 2e3,
|
|
2494
|
-
recordQueryUrl
|
|
2670
|
+
recordQueryUrl,
|
|
2671
|
+
decorateLink
|
|
2495
2672
|
);
|
|
2496
2673
|
const restoreSources = useRef9({ storage, linkService, defaults, initialQuery });
|
|
2497
2674
|
restoreSources.current = { storage, linkService, defaults, initialQuery };
|
|
@@ -2513,17 +2690,21 @@ function useQueryTable(opts) {
|
|
|
2513
2690
|
signal?.addEventListener("abort", abort2, { once: true });
|
|
2514
2691
|
if (signal?.aborted) currentController.abort();
|
|
2515
2692
|
setStorageReady(false);
|
|
2516
|
-
const
|
|
2693
|
+
const host2 = hostRef.current;
|
|
2694
|
+
const hostFromUrl = host2 && location && (navigation || urlSyncEnabled) ? hostStateFromUrl(location, host2) : void 0;
|
|
2695
|
+
const explicitLink = !!location && (navigation || urlSyncEnabled) && (hasQueryLink(location, parameter) || !!hostFromUrl);
|
|
2517
2696
|
setRestoration({ loading: true, error: null });
|
|
2518
2697
|
try {
|
|
2519
2698
|
if (currentController.signal.aborted) throw new DOMException("Query restoration cancelled.", "AbortError");
|
|
2520
2699
|
const fromUrl = location && (navigation || urlSyncEnabled) ? await linkService2.resolve(location, parameter, currentController.signal) : null;
|
|
2521
|
-
const
|
|
2522
|
-
const
|
|
2700
|
+
const linked = fromUrl ?? (hostFromUrl ? defaults2 : null);
|
|
2701
|
+
const defaultSaved = linked ? null : await storage2.loadDefaultSaved?.(sourceSchema.name);
|
|
2702
|
+
let restored = linked ?? defaultSaved?.query ?? (navigation ? defaults2 : await storage2.loadLast(sourceSchema.name));
|
|
2523
2703
|
if (currentController.signal.aborted) throw new DOMException("Query restoration cancelled.", "AbortError");
|
|
2704
|
+
if (linked && !fromUrl?.hostState && hostFromUrl) restored = { ...linked, hostState: hostFromUrl };
|
|
2524
2705
|
if (!cancelled && run === generation && epoch === manualQueryEpoch.current) {
|
|
2525
2706
|
if (restored) {
|
|
2526
|
-
const normalized = normalizeQueryState3(restored, defaults2);
|
|
2707
|
+
const normalized = withHostState(normalizeQueryState3(restored, defaults2), host2);
|
|
2527
2708
|
savedBaseline.current = defaultSaved || fromUrl?.grouping ? { source: sourceSchema.name, query: cloneQueryState(normalized) } : null;
|
|
2528
2709
|
viewRevision.current++;
|
|
2529
2710
|
updateQueryHistory(() => ({ query: normalized, past: [], future: [] }));
|
|
@@ -2548,7 +2729,7 @@ function useQueryTable(opts) {
|
|
|
2548
2729
|
}
|
|
2549
2730
|
restoreHandler.current = async (location, signal) => {
|
|
2550
2731
|
location = new URL(location, typeof window !== "undefined" ? window.location.href : void 0).href;
|
|
2551
|
-
const key =
|
|
2732
|
+
const key = tableUrlKey(location);
|
|
2552
2733
|
const current = restorationCurrent.current;
|
|
2553
2734
|
if (key === observedQueryUrl.current && current.storageReady && !current.restoration.loading && !current.restoration.error) return;
|
|
2554
2735
|
recordQueryUrl(location);
|
|
@@ -2558,7 +2739,7 @@ function useQueryTable(opts) {
|
|
|
2558
2739
|
if (typeof window !== "undefined") recordQueryUrl(window.location.href);
|
|
2559
2740
|
const navigate = () => {
|
|
2560
2741
|
if (!restorationCurrent.current.active) return;
|
|
2561
|
-
const key =
|
|
2742
|
+
const key = tableUrlKey(window.location.href);
|
|
2562
2743
|
if (key === observedQueryUrl.current) return;
|
|
2563
2744
|
observedQueryUrl.current = key;
|
|
2564
2745
|
void restore(true).catch(() => {
|
|
@@ -2618,7 +2799,7 @@ function useQueryTable(opts) {
|
|
|
2618
2799
|
const definitionKey = JSON.stringify(computedRevisions);
|
|
2619
2800
|
const queryKey = useMemo9(() => JSON.stringify(query), [query]);
|
|
2620
2801
|
const serverRowQuery = useMemo9(() => {
|
|
2621
|
-
const
|
|
2802
|
+
const execution2 = opts.computedExecution;
|
|
2622
2803
|
const detailConfig = typeof opts.itemDetails === "object" ? opts.itemDetails : void 0;
|
|
2623
2804
|
const page = query.itemDetails?.page ?? detailConfig?.page;
|
|
2624
2805
|
const pageEnabled = Boolean(opts.itemDetails) && (query.itemDetails?.modes ?? initialDetailModes(detailConfig)).includes("page") && (!detailConfig?.modes || detailConfig.modes.includes("page"));
|
|
@@ -2627,13 +2808,13 @@ function useQueryTable(opts) {
|
|
|
2627
2808
|
...query.select.length ? query.select : defaults.select,
|
|
2628
2809
|
...pageFields.filter((name) => displaySchema.fields.some((field) => field.name === name)).map((field) => ({ field }))
|
|
2629
2810
|
] } : executionQuery;
|
|
2630
|
-
const v2Request =
|
|
2811
|
+
const v2Request = execution2 && transport?.fetchRowsV2 ? toServerQueryV22(requestQuery, displaySchema, execution2) : void 0;
|
|
2631
2812
|
const request = v2Request ?? toServerQuery2(requestQuery, displaySchema);
|
|
2632
2813
|
if (v2Request)
|
|
2633
2814
|
for (const definition of computed.definitions) {
|
|
2634
|
-
if ((
|
|
2815
|
+
if ((execution2?.fields[computedFieldName2(definition.id)]?.select || v2Request.orderBy.some(
|
|
2635
2816
|
(term) => term.field === computedFieldName2(definition.id)
|
|
2636
|
-
)) && executionRevision(
|
|
2817
|
+
)) && executionRevision(execution2, definition.id) !== void 0 && executionRevision(execution2, definition.id) !== definition.revision)
|
|
2637
2818
|
v2Request.diagnostics.push({
|
|
2638
2819
|
from: 0,
|
|
2639
2820
|
to: 1,
|
|
@@ -2644,7 +2825,7 @@ function useQueryTable(opts) {
|
|
|
2644
2825
|
for (const clause of query.aggregations ?? [])
|
|
2645
2826
|
if (clause.scope === "shownRows") {
|
|
2646
2827
|
try {
|
|
2647
|
-
for (const name of
|
|
2828
|
+
for (const name of metricDependencies2(
|
|
2648
2829
|
clause,
|
|
2649
2830
|
schema,
|
|
2650
2831
|
resolveComputed
|
|
@@ -2694,7 +2875,7 @@ function useQueryTable(opts) {
|
|
|
2694
2875
|
]
|
|
2695
2876
|
);
|
|
2696
2877
|
const executionKey = JSON.stringify(opts.computedExecution ?? null);
|
|
2697
|
-
const pageContext = `${schema.name}:${rowQueryKey}:${nonce}:${executionKey}`;
|
|
2878
|
+
const pageContext = `${schema.name}:${rowQueryKey}:${nonce}:${executionKey}:${hostStateKey}`;
|
|
2698
2879
|
useEffect10(() => {
|
|
2699
2880
|
if (!active || !storageReady || restoration.loading || restoration.error) {
|
|
2700
2881
|
setRows((previous) => previous.length ? [] : previous);
|
|
@@ -2708,10 +2889,11 @@ function useQueryTable(opts) {
|
|
|
2708
2889
|
const run = async () => {
|
|
2709
2890
|
setLoading(true);
|
|
2710
2891
|
setError(null);
|
|
2892
|
+
setFailedPage(null);
|
|
2711
2893
|
try {
|
|
2712
2894
|
validateExecution(query);
|
|
2713
2895
|
if (transport) {
|
|
2714
|
-
if (transport.fetchQueryV3 && transport.queryCapabilities?.version === 3 && (query.grouping || query.select.some((column) => isComputedField4(column.field)) || query.orderBy.some((term) => isComputedField4(term.field)) || query.where.some((term) => (
|
|
2896
|
+
if (transport.fetchQueryV3 && transport.queryCapabilities?.version === 3 && (query.grouping || query.select.some((column) => isComputedField4(column.field)) || query.orderBy.some((term) => isComputedField4(term.field)) || query.where.some((term) => predicatesOf6(term).some((predicate) => isComputedField4(predicate.field))) || !transport.fetchRows && !(transport.fetchRowsV2 && opts.computedExecution))) {
|
|
2715
2897
|
const request = toQueryRequestV32(
|
|
2716
2898
|
{ ...query, aggregations: [], select: [
|
|
2717
2899
|
...query.select.filter((column) => isComputedField4(column.field)),
|
|
@@ -2792,6 +2974,7 @@ function useQueryTable(opts) {
|
|
|
2792
2974
|
} catch (e) {
|
|
2793
2975
|
if (!cancelled && !ac.signal.aborted) {
|
|
2794
2976
|
setError(e);
|
|
2977
|
+
setFailedPage(pageContext);
|
|
2795
2978
|
setRows([]);
|
|
2796
2979
|
setTotal(null);
|
|
2797
2980
|
setCommittedPage(null);
|
|
@@ -2820,11 +3003,13 @@ function useQueryTable(opts) {
|
|
|
2820
3003
|
active,
|
|
2821
3004
|
storageReady
|
|
2822
3005
|
]);
|
|
3006
|
+
const persistLast = opts.persistLast ?? "always";
|
|
3007
|
+
const hostStorageUnsupported = Boolean(host) && !storage.storesHostState;
|
|
2823
3008
|
useEffect10(() => {
|
|
2824
|
-
if (!storageReady || restoration.loading || restoration.error) return;
|
|
3009
|
+
if (persistLast !== "always" || !storageReady || restoration.loading || restoration.error || hostStorageUnsupported) return;
|
|
2825
3010
|
const t = setTimeout(() => void storage.saveLast(sourceSchema.name, query), 400);
|
|
2826
3011
|
return () => clearTimeout(t);
|
|
2827
|
-
}, [queryKey, storage, sourceSchema.name, query, restoration, storageReady]);
|
|
3012
|
+
}, [persistLast, queryKey, storage, sourceSchema.name, query, restoration, storageReady, hostStorageUnsupported]);
|
|
2828
3013
|
const idField = useMemo9(
|
|
2829
3014
|
() => schema.fields.find((f) => f.name === schema.idField),
|
|
2830
3015
|
[schema]
|
|
@@ -2845,7 +3030,7 @@ function useQueryTable(opts) {
|
|
|
2845
3030
|
useEffect10(() => {
|
|
2846
3031
|
selection.clear();
|
|
2847
3032
|
}, [identityScope, selection.clear]);
|
|
2848
|
-
const columnSchema = useMemo9(() => detailColumnSchema(displaySchema, detailsEnabled), [displaySchema, detailsEnabled]);
|
|
3033
|
+
const columnSchema = useMemo9(() => detailColumnSchema(displaySchema, detailsEnabled || groupDetailsEnabled), [displaySchema, detailsEnabled, groupDetailsEnabled]);
|
|
2849
3034
|
const resultDefaults = useMemo9(() => query.grouping ? defaultsFor(schema) : defaults, [Boolean(query.grouping), schema, defaults]);
|
|
2850
3035
|
const select = useSelect(query, setQuery, columnSchema);
|
|
2851
3036
|
const columnDrag = useColumnDrag();
|
|
@@ -2853,7 +3038,7 @@ function useQueryTable(opts) {
|
|
|
2853
3038
|
sourceSchema.name,
|
|
2854
3039
|
query,
|
|
2855
3040
|
(q) => {
|
|
2856
|
-
const normalized = normalizeQueryState3(q, defaults);
|
|
3041
|
+
const normalized = withHostState(normalizeQueryState3(q, defaults), hostRef.current);
|
|
2857
3042
|
savedBaseline.current = { source: sourceSchema.name, query: cloneQueryState(normalized) };
|
|
2858
3043
|
viewRevision.current++;
|
|
2859
3044
|
setQuery(normalized);
|
|
@@ -2862,11 +3047,23 @@ function useQueryTable(opts) {
|
|
|
2862
3047
|
now,
|
|
2863
3048
|
links.snapshot
|
|
2864
3049
|
);
|
|
2865
|
-
const
|
|
3050
|
+
const hostStorageError = host && !storage.storesHostState ? "This storage adapter cannot save host query state (set StorageAdapter.storesHostState once it keeps QueryState.hostState)." : null;
|
|
3051
|
+
const savedItems = useMemo9(() => !host ? savedQueries.items : savedQueries.items.map((item) => {
|
|
3052
|
+
try {
|
|
3053
|
+
return { ...item, query: withHostState(normalizeQueryState3(item.query, defaults), hostRef.current) };
|
|
3054
|
+
} catch {
|
|
3055
|
+
return item;
|
|
3056
|
+
}
|
|
3057
|
+
}), [savedQueries.items, Boolean(host), defaults]);
|
|
3058
|
+
const saved = { ...savedQueries, items: savedItems, save: async (name) => {
|
|
3059
|
+
if (hostStorageError) throw Error(hostStorageError);
|
|
2866
3060
|
const item = await savedQueries.save(name);
|
|
2867
|
-
savedBaseline.current = { source: sourceSchema.name, query: cloneQueryState(normalizeQueryState3(item.query, defaults)) };
|
|
3061
|
+
savedBaseline.current = { source: sourceSchema.name, query: cloneQueryState(withHostState(normalizeQueryState3(item.query, defaults), hostRef.current)) };
|
|
2868
3062
|
viewRevision.current++;
|
|
2869
3063
|
return item;
|
|
3064
|
+
}, update: async (id) => {
|
|
3065
|
+
if (hostStorageError) throw Error(hostStorageError);
|
|
3066
|
+
return savedQueries.update(id);
|
|
2870
3067
|
} };
|
|
2871
3068
|
const pageReady = committedPage === pageContext && committedSource?.schema === schema && committedSource?.transport === transport && committedSource?.clientRows === clientRows && !error;
|
|
2872
3069
|
const dataToken = useRef9(0);
|
|
@@ -2886,7 +3083,7 @@ function useQueryTable(opts) {
|
|
|
2886
3083
|
const capabilities = metricTransport.queryCapabilities;
|
|
2887
3084
|
const refused = /* @__PURE__ */ new Map();
|
|
2888
3085
|
for (const clause of metricQuery.aggregations ?? []) {
|
|
2889
|
-
const issue = metricPopulationError2(clause, { filters: capabilities.metricFilters, minCount: capabilities.metricMinCount });
|
|
3086
|
+
const issue = metricPopulationError2(clause, { filters: capabilities.metricFilters, minCount: capabilities.metricMinCount, minCountMeasures: capabilities.metricMinCountMeasures });
|
|
2890
3087
|
if (issue) refused.set(clause.id, issue);
|
|
2891
3088
|
}
|
|
2892
3089
|
const runnable = (metricQuery.aggregations ?? []).filter((clause) => !refused.has(clause.id));
|
|
@@ -2938,7 +3135,7 @@ function useQueryTable(opts) {
|
|
|
2938
3135
|
nonce,
|
|
2939
3136
|
validateExecution,
|
|
2940
3137
|
{
|
|
2941
|
-
...metricTransport?.fetchQueryV3 && metricTransport.queryCapabilities?.version === 3 && (query.grouping || query.aggregations?.some((metric) => metric.dateGrouping) || query.where.some((term) => (
|
|
3138
|
+
...metricTransport?.fetchQueryV3 && metricTransport.queryCapabilities?.version === 3 && (query.grouping || query.aggregations?.some((metric) => metric.dateGrouping) || query.where.some((term) => predicatesOf6(term).some((predicate) => isComputedField4(predicate.field)))) ? { executeV3: executeV3Metrics, queryContextKey: groupKey } : {},
|
|
2942
3139
|
...groupingNow !== void 0 ? { now: groupingNow, strictLocalMetrics: true } : {},
|
|
2943
3140
|
shownRows: rows,
|
|
2944
3141
|
rowsReady: metricRowsReady,
|
|
@@ -3239,6 +3436,62 @@ function useQueryTable(opts) {
|
|
|
3239
3436
|
query.where
|
|
3240
3437
|
]
|
|
3241
3438
|
);
|
|
3439
|
+
const executionIdentity = JSON.stringify([pageContext, query.aggregations ?? [], query.where, groupKey, nonce]);
|
|
3440
|
+
const executionGeneration = useRef9({ key: "", generation: 0 });
|
|
3441
|
+
if (executionGeneration.current.key !== executionIdentity)
|
|
3442
|
+
executionGeneration.current = { key: executionIdentity, generation: executionGeneration.current.generation + 1 };
|
|
3443
|
+
const execution = useMemo9(() => {
|
|
3444
|
+
if (!active || !storageReady || restoration.loading || restoration.error) return null;
|
|
3445
|
+
const rowsStatus = pageReady ? "succeeded" : error && failedPage === pageContext ? "failed" : "running";
|
|
3446
|
+
const metricError = aggState.error ?? aggState.results?.metrics.find((entry) => entry.error)?.error;
|
|
3447
|
+
const metricsStatus = !query.aggregations?.length ? "none" : aggState.loading || !aggState.results && !aggState.error ? "running" : metricError ? "failed" : "succeeded";
|
|
3448
|
+
const status = rowsStatus === "failed" || metricsStatus === "failed" ? "failed" : rowsStatus === "succeeded" && metricsStatus !== "running" ? "succeeded" : "running";
|
|
3449
|
+
return {
|
|
3450
|
+
query,
|
|
3451
|
+
key: executionIdentity,
|
|
3452
|
+
generation: executionGeneration.current.generation,
|
|
3453
|
+
status,
|
|
3454
|
+
rows: rowsStatus,
|
|
3455
|
+
metrics: metricsStatus,
|
|
3456
|
+
error: rowsStatus === "failed" ? error : metricError ? metricError instanceof Error ? metricError : Error(String(metricError)) : null
|
|
3457
|
+
};
|
|
3458
|
+
}, [
|
|
3459
|
+
active,
|
|
3460
|
+
storageReady,
|
|
3461
|
+
restoration.loading,
|
|
3462
|
+
restoration.error,
|
|
3463
|
+
pageReady,
|
|
3464
|
+
error,
|
|
3465
|
+
failedPage,
|
|
3466
|
+
pageContext,
|
|
3467
|
+
aggState.error,
|
|
3468
|
+
aggState.results,
|
|
3469
|
+
aggState.loading,
|
|
3470
|
+
query,
|
|
3471
|
+
executionIdentity
|
|
3472
|
+
]);
|
|
3473
|
+
const succeededQuery = execution?.status === "succeeded" ? execution.query : null;
|
|
3474
|
+
const pendingLast = useRef9(null);
|
|
3475
|
+
const savedLast = useRef9(null);
|
|
3476
|
+
useEffect10(() => {
|
|
3477
|
+
if (persistLast !== "succeeded" || !succeededQuery || hostStorageUnsupported) return;
|
|
3478
|
+
const key = JSON.stringify([sourceSchema.name, succeededQuery]);
|
|
3479
|
+
if (pendingLast.current?.key === key || !pendingLast.current && savedLast.current === key) return;
|
|
3480
|
+
if (pendingLast.current) clearTimeout(pendingLast.current.timer);
|
|
3481
|
+
const save = () => {
|
|
3482
|
+
pendingLast.current = null;
|
|
3483
|
+
savedLast.current = key;
|
|
3484
|
+
void storage.saveLast(sourceSchema.name, succeededQuery);
|
|
3485
|
+
};
|
|
3486
|
+
pendingLast.current = { key, save, timer: setTimeout(save, 400) };
|
|
3487
|
+
}, [persistLast, succeededQuery, storage, sourceSchema.name, hostStorageUnsupported]);
|
|
3488
|
+
useEffect10(() => () => {
|
|
3489
|
+
const pending = pendingLast.current;
|
|
3490
|
+
if (pending) {
|
|
3491
|
+
clearTimeout(pending.timer);
|
|
3492
|
+
pending.save();
|
|
3493
|
+
}
|
|
3494
|
+
}, []);
|
|
3242
3495
|
const sourceRowId = useCallback12((row) => {
|
|
3243
3496
|
const field = sourceSchema.fields.find((f) => f.name === sourceSchema.idField);
|
|
3244
3497
|
const value = field ? readFieldValue4(field, row) : row[sourceSchema.idField];
|
|
@@ -3258,16 +3511,71 @@ function useQueryTable(opts) {
|
|
|
3258
3511
|
queryParameter: parameter,
|
|
3259
3512
|
queryReady: storageReady && !restoration.loading && !restoration.error
|
|
3260
3513
|
});
|
|
3514
|
+
const groupPopulation = query.grouping ? JSON.stringify([query.grouping, query.where, nonce, hostStateKey]) : "";
|
|
3515
|
+
const groupRecords = useRef9({ population: "", source: [], rows: /* @__PURE__ */ new Map() });
|
|
3516
|
+
const groupSource = [clientRows, transport, schema];
|
|
3517
|
+
if (groupRecords.current.population !== groupPopulation || groupSource.some((part, i) => part !== groupRecords.current.source[i]))
|
|
3518
|
+
groupRecords.current = { population: groupPopulation, source: groupSource, rows: /* @__PURE__ */ new Map() };
|
|
3519
|
+
const groupsSettled = groupDetailsEnabled && pageReady && !restoration.loading;
|
|
3520
|
+
if (groupsSettled) for (const row of rows) {
|
|
3521
|
+
const id = rowId(row);
|
|
3522
|
+
if (id != null) groupRecords.current.rows.set(id, row);
|
|
3523
|
+
}
|
|
3524
|
+
const groupDetailOptions = useMemo9(() => {
|
|
3525
|
+
if (!groupDetailsEnabled) return void 0;
|
|
3526
|
+
const config = typeof opts.itemDetails === "object" ? opts.itemDetails : void 0;
|
|
3527
|
+
const modes = (config?.modes ?? ["hover", "modal", "expand"]).filter((mode) => mode !== "page");
|
|
3528
|
+
return {
|
|
3529
|
+
modes,
|
|
3530
|
+
defaultModes: initialDetailModes(config).filter((mode) => modes.includes(mode)),
|
|
3531
|
+
objectType: `${config?.objectType ?? sourceSchema.name}:groups`,
|
|
3532
|
+
...config?.storage ? { storage: config.storage } : {}
|
|
3533
|
+
};
|
|
3534
|
+
}, [groupDetailsEnabled, opts.itemDetails, sourceSchema.name]);
|
|
3535
|
+
const groupDetailSchema = useMemo9(() => ({
|
|
3536
|
+
...displaySchema,
|
|
3537
|
+
defaultSelect: displaySchema.fields.filter((field) => field.name !== displaySchema.idField).map((field) => ({ field: field.name })),
|
|
3538
|
+
fields: displaySchema.fields.map((field) => field.name === displaySchema.idField ? { ...field, property: { enabled: false } } : field)
|
|
3539
|
+
}), [displaySchema]);
|
|
3540
|
+
const setGroupQuery = useCallback12((next) => setQuery((current) => current.grouping ? typeof next === "function" ? next(current) : next : current), [setQuery]);
|
|
3541
|
+
const groupDetails = useItemDetails({
|
|
3542
|
+
schema: groupDetailSchema,
|
|
3543
|
+
query,
|
|
3544
|
+
setQuery: setGroupQuery,
|
|
3545
|
+
options: groupDetailOptions,
|
|
3546
|
+
rows,
|
|
3547
|
+
clientRows: void 0,
|
|
3548
|
+
rowId,
|
|
3549
|
+
transport: void 0,
|
|
3550
|
+
refreshKey: nonce,
|
|
3551
|
+
links,
|
|
3552
|
+
queryParameter: parameter,
|
|
3553
|
+
queryReady: storageReady && !restoration.loading && !restoration.error,
|
|
3554
|
+
lookupRow: (id) => {
|
|
3555
|
+
const row = groupRecords.current.rows.get(id);
|
|
3556
|
+
return row ? { row } : groupsSettled || error ? null : void 0;
|
|
3557
|
+
},
|
|
3558
|
+
subject: "group"
|
|
3559
|
+
});
|
|
3261
3560
|
const api = {
|
|
3262
3561
|
restoration: {
|
|
3263
3562
|
status: restoration.error ? "error" : !storageReady || restoration.loading ? "resolving" : "ready",
|
|
3264
3563
|
ready: active && storageReady && !restoration.loading && !restoration.error,
|
|
3265
3564
|
error: restoration.error
|
|
3266
3565
|
},
|
|
3566
|
+
execution,
|
|
3567
|
+
...host && query.hostState ? {
|
|
3568
|
+
definition: { query: withoutHostState(query), hostState: query.hostState },
|
|
3569
|
+
setDefinition: (next) => {
|
|
3570
|
+
const current = queryHistoryRef.current.query;
|
|
3571
|
+
const value = typeof next === "function" ? next({ query: withoutHostState(current), hostState: current.hostState }) : next;
|
|
3572
|
+
setQuery(withHostState({ ...normalizeQueryState3(value.query, defaults), hostState: value.hostState }, hostRef.current));
|
|
3573
|
+
}
|
|
3574
|
+
} : {},
|
|
3267
3575
|
restoreLocation,
|
|
3268
3576
|
links,
|
|
3269
3577
|
schema: columnSchema,
|
|
3270
|
-
...itemDetails && detailsEnabled ? { itemDetails } : {},
|
|
3578
|
+
...itemDetails && detailsEnabled ? { itemDetails } : groupDetails ? { itemDetails: groupDetails } : {},
|
|
3271
3579
|
query,
|
|
3272
3580
|
defaults: resultDefaults,
|
|
3273
3581
|
setQuery,
|
|
@@ -3310,6 +3618,25 @@ function useQueryTable(opts) {
|
|
|
3310
3618
|
visibleFields,
|
|
3311
3619
|
rowId
|
|
3312
3620
|
};
|
|
3621
|
+
const sourceFilterValues = useCallback12(async (name, search) => {
|
|
3622
|
+
if (metadataTransport?.fetchDistinctValues) return metadataTransport.fetchDistinctValues({ field: name, search });
|
|
3623
|
+
const field = groupingModel.sourceField(name);
|
|
3624
|
+
return field && sourceRows ? { ...distinctValuesFromRows(field, sourceRows, search), hasNull: fieldHasNullInRows(field, sourceRows) } : { values: [], hasMore: false };
|
|
3625
|
+
}, [metadataTransport, groupingModel, sourceRows]);
|
|
3626
|
+
const sourceFieldStats = useCallback12(async (names, signal) => {
|
|
3627
|
+
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
3628
|
+
const requested = [...new Set(names)];
|
|
3629
|
+
if (metadataTransport?.fetchFieldStats) {
|
|
3630
|
+
const backendFields = requested.filter(
|
|
3631
|
+
(name) => sourceCatalogue.fields.find((field) => field.name === name)?.source.kind === "backend"
|
|
3632
|
+
);
|
|
3633
|
+
return backendFields.length ? metadataTransport.fetchFieldStats(backendFields, signal) : {};
|
|
3634
|
+
}
|
|
3635
|
+
return Object.fromEntries(requested.flatMap((name) => {
|
|
3636
|
+
const field = groupingModel.sourceField(name);
|
|
3637
|
+
return field && sourceRows ? [[name, { distinct: new Set(sourceRows.map((row) => JSON.stringify(readFieldValue4(field, row)))).size }]] : [];
|
|
3638
|
+
}));
|
|
3639
|
+
}, [metadataTransport, groupingModel, sourceRows, sourceCatalogue]);
|
|
3313
3640
|
const sourceApi = {
|
|
3314
3641
|
...sourceFilters,
|
|
3315
3642
|
loading,
|
|
@@ -3318,25 +3645,8 @@ function useQueryTable(opts) {
|
|
|
3318
3645
|
setQuery: setSourceQuery,
|
|
3319
3646
|
...itemDetails ? { itemDetails } : {},
|
|
3320
3647
|
schema: sourceCatalogue,
|
|
3321
|
-
filterValues:
|
|
3322
|
-
|
|
3323
|
-
const field = groupingModel.sourceField(name);
|
|
3324
|
-
return field && sourceRows ? { ...distinctValuesFromRows(field, sourceRows, search), hasNull: fieldHasNullInRows(field, sourceRows) } : { values: [], hasMore: false };
|
|
3325
|
-
},
|
|
3326
|
-
fieldStats: async (names, signal) => {
|
|
3327
|
-
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
3328
|
-
const requested = [...new Set(names)];
|
|
3329
|
-
if (metadataTransport?.fetchFieldStats) {
|
|
3330
|
-
const backendFields = requested.filter(
|
|
3331
|
-
(name) => sourceCatalogue.fields.find((field) => field.name === name)?.source.kind === "backend"
|
|
3332
|
-
);
|
|
3333
|
-
return backendFields.length ? metadataTransport.fetchFieldStats(backendFields, signal) : {};
|
|
3334
|
-
}
|
|
3335
|
-
return Object.fromEntries(requested.flatMap((name) => {
|
|
3336
|
-
const field = groupingModel.sourceField(name);
|
|
3337
|
-
return field && sourceRows ? [[name, { distinct: new Set(sourceRows.map((row) => JSON.stringify(readFieldValue4(field, row)))).size }]] : [];
|
|
3338
|
-
}));
|
|
3339
|
-
}
|
|
3648
|
+
filterValues: sourceFilterValues,
|
|
3649
|
+
fieldStats: sourceFieldStats
|
|
3340
3650
|
};
|
|
3341
3651
|
const grouping = useGroupingController(
|
|
3342
3652
|
api,
|
|
@@ -3350,7 +3660,7 @@ function useQueryTable(opts) {
|
|
|
3350
3660
|
return {
|
|
3351
3661
|
...api,
|
|
3352
3662
|
...groupingSupported || query.grouping ? { grouping } : {},
|
|
3353
|
-
select: { ...select, hide: (field) => query.grouping
|
|
3663
|
+
select: { ...select, hide: (field) => query.grouping && groupKeyFields2(query.grouping.keys).includes(field) ? grouping.changeKeys(query.grouping.keys.filter((key) => groupKeyField(key) !== field)) : select.hide(field) }
|
|
3354
3664
|
};
|
|
3355
3665
|
}
|
|
3356
3666
|
function nextOrderBy(existing, field, additive) {
|