@pythia-software/query-table-react 0.9.7 → 0.9.9
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/dist/index.d.ts +11 -1
- package/dist/index.js +61 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FieldSchema, WhereClause, QueryState, GroupedRow, GroupingModel, GroupingView, GroupKey, SummaryOp, RepresentativeRow, SummaryColumn, SelectColumn, PropertyViewState, SavedPropertyView, PropertyViewStorage, QueryLink, QuerySnapshot, ItemPropertyMode, ItemPageOptions, ItemDetailMode, ItemDetailsState, RelationshipAdapter, RowId, FormulaNode, FormulaValue, FormulaResult, ComputedColumn, FieldDef, ComputedColumnDraft, FormulaPlan, ColumnPreview, SavedQuery, StorageAdapter, AggOp, AggregationClause, AggregationResult, HostQueryState, DistinctValuesResult, FieldStats, OrderByClause, Transport, ComputedExecution, ComputedColumnStore, QueryLinkOptions, ObjectReference, ResolvedObjectReference } from '@pythia-software/query-table-core';
|
|
1
|
+
import { FieldSchema, WhereClause, QueryState, GroupedRow, GroupingModel, GroupingView, GroupKey, SummaryOp, RepresentativeRow, SummaryColumn, SelectColumn, PropertyViewState, SavedPropertyView, PropertyViewStorage, QueryLink, QuerySnapshot, ScopedJsonInspectionState, AttentionRegistry, ItemPropertyMode, ItemPageOptions, ItemDetailMode, ItemDetailsState, RelationshipAdapter, RowId, FormulaNode, FormulaValue, FormulaResult, ComputedColumn, FieldDef, ComputedColumnDraft, FormulaPlan, ColumnPreview, SavedQuery, StorageAdapter, AggOp, AggregationClause, AggregationResult, HostQueryState, DistinctValuesResult, FieldStats, OrderByClause, Transport, ComputedExecution, ComputedColumnStore, QueryLinkOptions, ObjectReference, ResolvedObjectReference } from '@pythia-software/query-table-core';
|
|
2
2
|
|
|
3
3
|
/** Filter controls for either the result query or grouping's source query. */
|
|
4
4
|
type QueryFilterApi<Row> = Pick<QueryTableApi<Row>, "query" | "setQuery" | "loading" | "addFilter" | "updateFilter" | "removeFilter" | "clearFilters" | "updatePredicate" | "removePredicate" | "negatePredicate" | "reorderFilters" | "mergeFilters" | "filterValues" | "fieldStats">;
|
|
@@ -101,6 +101,8 @@ interface ItemDetailRecord<Row> {
|
|
|
101
101
|
interface ItemDetailsOptions<Row> {
|
|
102
102
|
/** Detail catalogue; defaults to the table catalogue. Useful for non-column properties. */
|
|
103
103
|
schema?: FieldSchema<Row>;
|
|
104
|
+
/** Optional semantic reveal service, shared with host attention navigation. */
|
|
105
|
+
attention?: AttentionRegistry;
|
|
104
106
|
/** Legacy collection URL parameter. useQueryTable.queryParameter takes precedence. */
|
|
105
107
|
queryParameter?: string;
|
|
106
108
|
/** Creator-approved modalities; users can enable/disable these in Select columns. Defaults to all. */
|
|
@@ -119,6 +121,14 @@ interface ItemDetailsApi<Row> {
|
|
|
119
121
|
/** "group" details describe grouped result rows, identified by their group identity. */
|
|
120
122
|
subject?: "item" | "group";
|
|
121
123
|
links?: QueryLinksApi;
|
|
124
|
+
jsonInspections?: readonly ScopedJsonInspectionState[];
|
|
125
|
+
setJsonInspection?: (inspection: ScopedJsonInspectionState) => void;
|
|
126
|
+
attention?: AttentionRegistry;
|
|
127
|
+
preserveFocus?: boolean;
|
|
128
|
+
/** Ephemeral request counter; virtual tables rematerialize an expanded row per reveal. */
|
|
129
|
+
attentionRevision?: number;
|
|
130
|
+
/** Keep the requested virtual detail materialized until its reveal settles. */
|
|
131
|
+
attentionPending?: boolean;
|
|
122
132
|
schema: FieldSchema<Row>;
|
|
123
133
|
/** Collection catalogue used to resolve Page template fields. */
|
|
124
134
|
pageSchema: FieldSchema<Row>;
|
package/dist/index.js
CHANGED
|
@@ -348,6 +348,7 @@ import { ITEM_DETAILS_COLUMN, itemPageFields } from "@pythia-software/query-tabl
|
|
|
348
348
|
|
|
349
349
|
// src/useItemDetails.ts
|
|
350
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";
|
|
351
352
|
|
|
352
353
|
// src/usePropertyView.ts
|
|
353
354
|
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
@@ -646,7 +647,7 @@ function useItemDetails({
|
|
|
646
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 });
|
|
647
648
|
return;
|
|
648
649
|
}
|
|
649
|
-
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 }));
|
|
650
651
|
void Promise.resolve().then(() => loadRow(activeId, controller.signal)).then((record) => {
|
|
651
652
|
if (!controller.signal.aborted) setResult({ source: loadRow, epoch, id: activeId, record, loading: false, error: null });
|
|
652
653
|
}).catch((error) => {
|
|
@@ -661,11 +662,66 @@ function useItemDetails({
|
|
|
661
662
|
});
|
|
662
663
|
return () => controller.abort();
|
|
663
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]);
|
|
664
700
|
if (!enabled) return;
|
|
665
701
|
const looked = lookupRow && active ? lookupRow(active.id) : null;
|
|
666
702
|
return {
|
|
667
703
|
...subject ? { subject } : {},
|
|
668
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
|
+
},
|
|
669
725
|
schema: detailSchema,
|
|
670
726
|
queryParameter: queryParameter2 ?? config?.queryParameter ?? "q",
|
|
671
727
|
properties: modalProperties,
|
|
@@ -675,7 +731,7 @@ function useItemDetails({
|
|
|
675
731
|
active,
|
|
676
732
|
page,
|
|
677
733
|
pageSchema: schema,
|
|
678
|
-
record: lookupRow ? looked ?? null :
|
|
734
|
+
record: lookupRow ? looked ?? null : result.id === activeId && result.source === loadRow ? result.record : null,
|
|
679
735
|
loading: lookupRow ? looked === void 0 : activeId !== void 0 && (!currentResult || result.loading),
|
|
680
736
|
error: lookupRow ? null : currentResult ? result.error : null,
|
|
681
737
|
...config?.relationships ? { relationships: config.relationships } : {},
|
|
@@ -690,6 +746,8 @@ function useItemDetails({
|
|
|
690
746
|
});
|
|
691
747
|
},
|
|
692
748
|
open(id, mode) {
|
|
749
|
+
config?.attention?.cancel();
|
|
750
|
+
preserveFocus.current = false;
|
|
693
751
|
if (!modes.includes(mode)) return;
|
|
694
752
|
setQuery((current) => ({ ...current, itemDetails: {
|
|
695
753
|
...current.itemDetails,
|
|
@@ -699,6 +757,7 @@ function useItemDetails({
|
|
|
699
757
|
} }));
|
|
700
758
|
},
|
|
701
759
|
close() {
|
|
760
|
+
config?.attention?.cancel();
|
|
702
761
|
setQuery((current) => {
|
|
703
762
|
if (!current.itemDetails?.active) return current;
|
|
704
763
|
const { active: _active, ...state } = current.itemDetails;
|