@pythia-software/query-table-ui 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/README.md +4 -1
- package/dist/index.d.ts +35 -15
- package/dist/index.js +272 -70
- package/dist/index.js.map +1 -1
- package/dist/theme.css +4 -0
- package/dist/theme.css.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -389,7 +389,8 @@ function metricFormatFromMeasure(clause, measure) {
|
|
|
389
389
|
}
|
|
390
390
|
|
|
391
391
|
// src/JsonCell.tsx
|
|
392
|
-
import { useEffect as useEffect3, useMemo, useRef as useRef2, useState as useState3 } from "react";
|
|
392
|
+
import { useEffect as useEffect3, useLayoutEffect as useLayoutEffect2, useMemo, useRef as useRef2, useState as useState3 } from "react";
|
|
393
|
+
import { jsonInspectionScopeKey } from "@pythia-software/query-table-core";
|
|
393
394
|
|
|
394
395
|
// src/AdaptiveOverlay.tsx
|
|
395
396
|
import {
|
|
@@ -467,7 +468,8 @@ function ModalSurface({
|
|
|
467
468
|
chrome = true,
|
|
468
469
|
returnFocus,
|
|
469
470
|
backdropClassName,
|
|
470
|
-
initialFocus
|
|
471
|
+
initialFocus,
|
|
472
|
+
preserveFocus = false
|
|
471
473
|
}) {
|
|
472
474
|
const mobile = useMobileLayout();
|
|
473
475
|
const titleId = useId();
|
|
@@ -506,7 +508,7 @@ function ModalSurface({
|
|
|
506
508
|
document.body.style.overflow = "hidden";
|
|
507
509
|
}
|
|
508
510
|
modalStack.push(element);
|
|
509
|
-
(element.querySelector(initialFocus ?? ":not(*)") ?? element).focus();
|
|
511
|
+
if (!preserveFocus) (element.querySelector(initialFocus ?? ":not(*)") ?? element).focus();
|
|
510
512
|
const isTop = () => modalStack[modalStack.length - 1] === element;
|
|
511
513
|
const focusable = () => Array.from(
|
|
512
514
|
element.querySelectorAll(
|
|
@@ -551,7 +553,7 @@ function ModalSurface({
|
|
|
551
553
|
}
|
|
552
554
|
};
|
|
553
555
|
const onFocus = (event) => {
|
|
554
|
-
if (isTop() && !element.contains(event.target)) element.focus();
|
|
556
|
+
if (!preserveFocus && isTop() && !element.contains(event.target)) element.focus();
|
|
555
557
|
};
|
|
556
558
|
document.addEventListener("keydown", onKey);
|
|
557
559
|
document.addEventListener("focusin", onFocus);
|
|
@@ -562,7 +564,7 @@ function ModalSurface({
|
|
|
562
564
|
if (modalStack.length === 0)
|
|
563
565
|
document.body.style.overflow = originalOverflow;
|
|
564
566
|
const destination = focusDestination.current ?? previous;
|
|
565
|
-
if (destination?.isConnected) destination.focus({ preventScroll: true });
|
|
567
|
+
if (!preserveFocus && destination?.isConnected) destination.focus({ preventScroll: true });
|
|
566
568
|
};
|
|
567
569
|
}, [mobile]);
|
|
568
570
|
const content = /* @__PURE__ */ jsx2(
|
|
@@ -853,9 +855,37 @@ function jsonSearchMatches(entries, search) {
|
|
|
853
855
|
|
|
854
856
|
// src/JsonCell.tsx
|
|
855
857
|
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
856
|
-
function JsonCell(
|
|
857
|
-
const
|
|
858
|
+
function JsonCell(props) {
|
|
859
|
+
const { value, label = "JSON", display: display2 } = props;
|
|
858
860
|
const inspection = useMemo(() => inspectJsonValue(value, display2, false), [value, display2]);
|
|
861
|
+
const [state, change] = useInspectionState(props, inspection);
|
|
862
|
+
const preserveFocus = useRef2(false);
|
|
863
|
+
const latest = useRef2({ state, change, controlled: props.inspectionState !== void 0 });
|
|
864
|
+
latest.current = { state, change, controlled: props.inspectionState !== void 0 };
|
|
865
|
+
const initialized = useRef2(null);
|
|
866
|
+
const sessionKey = props.inspectionScope ? jsonInspectionScopeKey(props.inspectionScope) : inspection.raw + JSON.stringify(display2);
|
|
867
|
+
const openInspector = (preserve, full = inspectJsonValue(value, display2)) => {
|
|
868
|
+
const current = latest.current;
|
|
869
|
+
const next = current.controlled || initialized.current === sessionKey ? current.state : createJsonInspectionState(full, display2);
|
|
870
|
+
initialized.current = sessionKey;
|
|
871
|
+
preserveFocus.current = preserve;
|
|
872
|
+
current.change({ ...next, open: true });
|
|
873
|
+
};
|
|
874
|
+
const scopeKey = props.inspectionScope ? jsonInspectionScopeKey(props.inspectionScope) : "";
|
|
875
|
+
useEffect3(() => {
|
|
876
|
+
if (!props.attention || !props.inspectionScope || state.open || value == null || value === "") return;
|
|
877
|
+
const registry = props.attention;
|
|
878
|
+
const target = props.inspectionScope;
|
|
879
|
+
const provider = { reveal: async (destination, signal) => {
|
|
880
|
+
if (signal.aborted) return false;
|
|
881
|
+
const full = inspectJsonValue(value, display2);
|
|
882
|
+
if (destination.path && !full.entries.some((entry) => entry.path === destination.path)) return false;
|
|
883
|
+
openInspector(true, full);
|
|
884
|
+
const mounted = await registry.whenRegistered(target, signal, provider);
|
|
885
|
+
return signal.aborted ? false : mounted.reveal(destination, signal);
|
|
886
|
+
} };
|
|
887
|
+
return registry.register(target, provider);
|
|
888
|
+
}, [props.attention, scopeKey, state.open, inspection]);
|
|
859
889
|
if (value == null || value === "") return /* @__PURE__ */ jsx3("span", { className: "qt-muted", children: "\u2014" });
|
|
860
890
|
return /* @__PURE__ */ jsxs3("div", { className: "qt-json-cell", children: [
|
|
861
891
|
/* @__PURE__ */ jsx3(
|
|
@@ -866,28 +896,54 @@ function JsonCell({ value, label = "JSON", display: display2 }) {
|
|
|
866
896
|
title: inspection.error ?? "Open in JSON Viewer",
|
|
867
897
|
"aria-label": `Inspect ${label}`,
|
|
868
898
|
"aria-haspopup": "dialog",
|
|
899
|
+
"aria-expanded": state.open,
|
|
869
900
|
onClick: (event) => {
|
|
870
901
|
event.stopPropagation();
|
|
871
|
-
|
|
902
|
+
openInspector(false);
|
|
872
903
|
},
|
|
873
904
|
onDoubleClick: (event) => event.stopPropagation(),
|
|
874
905
|
children: /* @__PURE__ */ jsx3("span", { className: "qt-json-badge", children: inspection.error ? "JSON?" : "JSON" })
|
|
875
906
|
}
|
|
876
907
|
),
|
|
877
908
|
/* @__PURE__ */ jsx3("code", { className: "qt-json-preview-text", title: inspection.error ?? inspection.preview, children: inspection.preview }),
|
|
878
|
-
open && /* @__PURE__ */ jsx3(JsonViewerModal, {
|
|
909
|
+
state.open && /* @__PURE__ */ jsx3(JsonViewerModal, { ...props, preserveFocus: preserveFocus.current, inspectionState: state, onInspectionStateChange: change, onClose: () => change({ ...state, open: false }) })
|
|
879
910
|
] });
|
|
880
911
|
}
|
|
881
|
-
function JsonViewerModal({
|
|
912
|
+
function JsonViewerModal({ onClose, returnFocus, preserveFocus, ...props }) {
|
|
913
|
+
const { value, label = "JSON", display: display2 } = props;
|
|
882
914
|
const inspection = useMemo(() => inspectJsonValue(value, display2), [value, display2]);
|
|
883
|
-
|
|
915
|
+
const [state, change] = useInspectionState(props, inspection);
|
|
916
|
+
return /* @__PURE__ */ jsx3("span", { onClick: (event) => event.stopPropagation(), onDoubleClick: (event) => event.stopPropagation(), onContextMenu: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx3(ModalSurface, { title: `${label} \xB7 JSON`, onClose, returnFocus, preserveFocus, chrome: false, className: "qt-modal qt-json-inspector", children: /* @__PURE__ */ jsx3(JsonValueContent, { ...props, inspection, state, change, label, onClose }) }) });
|
|
917
|
+
}
|
|
918
|
+
function createJsonInspectionState(inspection, display2, compact = false) {
|
|
919
|
+
return {
|
|
920
|
+
version: 1,
|
|
921
|
+
open: false,
|
|
922
|
+
search: "",
|
|
923
|
+
raw: Boolean(inspection.error),
|
|
924
|
+
visibleEntryLimit: 200,
|
|
925
|
+
collapsedPaths: inspection.entries.filter((entry) => entry.children > 0 && (compact || entry.depth > 0 && (display2?.expandDepth === void 0 ? entry.children >= 4 : entry.depth >= boundedJsonOption(display2.expandDepth, 1, 0, 8)))).map((entry) => entry.path),
|
|
926
|
+
expandedBuckets: [],
|
|
927
|
+
searchMode: "filter",
|
|
928
|
+
searchOverrides: []
|
|
929
|
+
};
|
|
884
930
|
}
|
|
885
|
-
function
|
|
931
|
+
function useInspectionState(props, inspection, compact = false) {
|
|
932
|
+
const key = props.inspectionScope ? jsonInspectionScopeKey(props.inspectionScope) : inspection.raw + JSON.stringify(props.display);
|
|
933
|
+
const [local, setLocal] = useState3(() => ({ key, state: createJsonInspectionState(inspection, props.display, compact) }));
|
|
934
|
+
const state = props.inspectionState ?? (local.key === key ? local.state : createJsonInspectionState(inspection, props.display, compact));
|
|
935
|
+
const change = (next) => {
|
|
936
|
+
if (props.inspectionState === void 0) setLocal({ key, state: next });
|
|
937
|
+
props.onInspectionStateChange?.(next);
|
|
938
|
+
};
|
|
939
|
+
return [state, change];
|
|
940
|
+
}
|
|
941
|
+
function JsonValueView(props) {
|
|
942
|
+
const { value, label = "JSON", display: display2, compact = false } = props;
|
|
886
943
|
const inspection = useMemo(() => inspectJsonValue(value, display2), [value, display2]);
|
|
944
|
+
const [state, change] = useInspectionState(props, inspection, compact);
|
|
887
945
|
if (value == null || value === "") return /* @__PURE__ */ jsx3("span", { className: "qt-muted", children: "\u2014" });
|
|
888
|
-
return /* @__PURE__ */ jsx3("div", { className: `qt-json-inline${compact ? " qt-json-inline--compact" : ""}`, "data-qt-json-inline": true, children: /* @__PURE__ */ jsx3(JsonValueContent, { inspection,
|
|
889
|
-
${JSON.stringify(display2)}
|
|
890
|
-
${compact}`) });
|
|
946
|
+
return /* @__PURE__ */ jsx3("div", { className: `qt-json-inline${compact ? " qt-json-inline--compact" : ""}`, "data-qt-json-inline": true, children: /* @__PURE__ */ jsx3(JsonValueContent, { ...props, inspection, state, change, label, compact }) });
|
|
891
947
|
}
|
|
892
948
|
function highlight(text, search, entry, part, active) {
|
|
893
949
|
const needle = search.trim().toLowerCase();
|
|
@@ -902,22 +958,20 @@ function highlight(text, search, entry, part, active) {
|
|
|
902
958
|
parts.push(text.slice(start));
|
|
903
959
|
return parts;
|
|
904
960
|
}
|
|
905
|
-
function JsonValueContent({ inspection, label,
|
|
906
|
-
const
|
|
907
|
-
const
|
|
961
|
+
function JsonValueContent({ inspection, label, onClose, compact = false, state, change, inspectionScope, attention, onInspectionResolution }) {
|
|
962
|
+
const { search, raw, searchMode, visibleEntryLimit: pageSize } = state;
|
|
963
|
+
const collapsed = useMemo(() => new Set(state.collapsedPaths), [state.collapsedPaths]);
|
|
964
|
+
const expandedBuckets = useMemo(() => new Set(state.expandedBuckets), [state.expandedBuckets]);
|
|
965
|
+
const searchOverrides = useMemo(() => new Map(state.searchOverrides.map((entry) => [entry.path, entry.expanded])), [state.searchOverrides]);
|
|
908
966
|
const [copyStatus, setCopyStatus] = useState3("");
|
|
909
|
-
const [pageSize, setPageSize] = useState3(200);
|
|
910
|
-
const [collapsed, setCollapsed] = useState3(() => new Set(inspection.entries.filter((entry) => entry.children > 0 && (compact || entry.depth > 0 && (display2?.expandDepth === void 0 ? entry.children >= 4 : entry.depth >= boundedJsonOption(display2.expandDepth, 1, 0, 8)))).map((entry) => entry.path)));
|
|
911
|
-
const [expandedBuckets, setExpandedBuckets] = useState3(/* @__PURE__ */ new Set());
|
|
912
|
-
const [searchMode, setSearchMode] = useState3("filter");
|
|
913
|
-
const [matchIndex, setMatchIndex] = useState3(0);
|
|
914
967
|
const [navigationRequest, setNavigationRequest] = useState3(0);
|
|
915
|
-
const [searchOverrides, setSearchOverrides] = useState3(/* @__PURE__ */ new Map());
|
|
916
968
|
const contentRef = useRef2(null);
|
|
917
969
|
const searching = Boolean(search.trim());
|
|
918
970
|
const matches = useMemo(() => searchJsonEntries(inspection.entries, search), [inspection, search]);
|
|
919
971
|
const occurrences = useMemo(() => jsonSearchMatches(inspection.entries, search), [inspection, search]);
|
|
920
|
-
const
|
|
972
|
+
const selectedIndex = state.selectedMatch ? occurrences.findIndex((match) => match.path === state.selectedMatch.path && match.part === state.selectedMatch.part && match.start === state.selectedMatch.start) : 0;
|
|
973
|
+
const matchIndex = Math.max(0, selectedIndex);
|
|
974
|
+
const activeMatch = selectedIndex < 0 ? void 0 : occurrences[matchIndex];
|
|
921
975
|
const matchedPaths = useMemo(() => new Set(matches.map((entry) => entry.path)), [matches]);
|
|
922
976
|
const byPath = useMemo(() => new Map(inspection.entries.map((entry) => [entry.path, entry])), [inspection]);
|
|
923
977
|
const presentation = useMemo(() => {
|
|
@@ -949,6 +1003,69 @@ function JsonValueContent({ inspection, label, display: display2, onClose, compa
|
|
|
949
1003
|
presentation.expandedBuckets,
|
|
950
1004
|
searching ? JSON_MAX_ENTRIES * 2 : pageSize
|
|
951
1005
|
), [inspection, searching, searchMode, matches, presentation, pageSize]);
|
|
1006
|
+
const missingPaths = [...state.collapsedPaths, ...state.expandedBuckets, ...state.searchOverrides.map((entry) => entry.path)].filter((path) => {
|
|
1007
|
+
if (byPath.has(path)) return false;
|
|
1008
|
+
const split = path.lastIndexOf(":bucket:");
|
|
1009
|
+
const parent = byPath.get(path.slice(0, split));
|
|
1010
|
+
return !(split > 0 && parent?.type === "array" && parent.children > Number(path.slice(split + 8)));
|
|
1011
|
+
});
|
|
1012
|
+
const resolutionKey = JSON.stringify([missingPaths, selectedIndex < 0]);
|
|
1013
|
+
const resolutionCallback = useRef2(onInspectionResolution);
|
|
1014
|
+
resolutionCallback.current = onInspectionResolution;
|
|
1015
|
+
useEffect3(() => {
|
|
1016
|
+
resolutionCallback.current?.({ status: missingPaths.length || selectedIndex < 0 ? "unavailable" : "ready", missingPaths, selectedMatchUnavailable: selectedIndex < 0 });
|
|
1017
|
+
}, [resolutionKey]);
|
|
1018
|
+
const [attentionPath, setAttentionPath] = useState3(null);
|
|
1019
|
+
const pendingReveal = useRef2(null);
|
|
1020
|
+
const latest = useRef2({ state, change, byPath });
|
|
1021
|
+
latest.current = { state, change, byPath };
|
|
1022
|
+
const scopeKey = inspectionScope ? jsonInspectionScopeKey(inspectionScope) : "";
|
|
1023
|
+
useEffect3(() => {
|
|
1024
|
+
if (!attention || !inspectionScope) return;
|
|
1025
|
+
return attention.register(inspectionScope, { reveal: (target, signal) => {
|
|
1026
|
+
const path = target.path ?? "$";
|
|
1027
|
+
const current = latest.current;
|
|
1028
|
+
let entry = current.byPath.get(path);
|
|
1029
|
+
if (!entry || signal.aborted) return false;
|
|
1030
|
+
const folded = new Set(current.state.collapsedPaths), buckets = new Set(current.state.expandedBuckets);
|
|
1031
|
+
while (entry) {
|
|
1032
|
+
folded.delete(entry.path);
|
|
1033
|
+
const parent = entry.parent ? current.byPath.get(entry.parent) : void 0;
|
|
1034
|
+
if (parent?.type === "array" && Number(entry.key) >= JSON_ARRAY_BUCKET_SIZE) buckets.add(jsonBucketId(parent.path, Number(entry.key)));
|
|
1035
|
+
entry = parent;
|
|
1036
|
+
}
|
|
1037
|
+
return new Promise((resolve) => {
|
|
1038
|
+
const abort = () => {
|
|
1039
|
+
if (pendingReveal.current?.signal === signal) pendingReveal.current = null;
|
|
1040
|
+
resolve(false);
|
|
1041
|
+
};
|
|
1042
|
+
const finish = (found) => {
|
|
1043
|
+
signal.removeEventListener("abort", abort);
|
|
1044
|
+
resolve(found);
|
|
1045
|
+
};
|
|
1046
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
1047
|
+
pendingReveal.current?.finish(false);
|
|
1048
|
+
pendingReveal.current = { path, signal, finish };
|
|
1049
|
+
const { selectedMatch: _match, ...next } = current.state;
|
|
1050
|
+
current.change({ ...next, raw: false, search: "", searchOverrides: [], collapsedPaths: [...folded], expandedBuckets: [...buckets], visibleEntryLimit: 1e4 });
|
|
1051
|
+
setAttentionPath(path);
|
|
1052
|
+
setNavigationRequest((request) => request + 1);
|
|
1053
|
+
});
|
|
1054
|
+
} });
|
|
1055
|
+
}, [attention, scopeKey]);
|
|
1056
|
+
useLayoutEffect2(() => {
|
|
1057
|
+
const request = pendingReveal.current;
|
|
1058
|
+
if (!request || request.signal.aborted) return;
|
|
1059
|
+
const element = [...contentRef.current?.querySelectorAll(".qt-json-entry") ?? []].find((node) => node.dataset.jsonPath === request.path);
|
|
1060
|
+
if (!element) return;
|
|
1061
|
+
pendingReveal.current = null;
|
|
1062
|
+
element.scrollIntoView?.({ block: "nearest", behavior: "instant" });
|
|
1063
|
+
request.finish(true);
|
|
1064
|
+
});
|
|
1065
|
+
useEffect3(() => () => {
|
|
1066
|
+
pendingReveal.current?.finish(false);
|
|
1067
|
+
pendingReveal.current = null;
|
|
1068
|
+
}, []);
|
|
952
1069
|
useEffect3(() => {
|
|
953
1070
|
if (!searching || raw) return;
|
|
954
1071
|
const content = contentRef.current;
|
|
@@ -957,12 +1074,11 @@ function JsonValueContent({ inspection, label, display: display2, onClose, compa
|
|
|
957
1074
|
const bounds = content.getBoundingClientRect();
|
|
958
1075
|
const target = selected.getBoundingClientRect();
|
|
959
1076
|
if (target.top < bounds.top || target.bottom > bounds.bottom) content.scrollTop += target.top - bounds.top - (bounds.height - target.height) / 2;
|
|
960
|
-
}, [searching, raw, activeMatch, searchMode, navigationRequest]);
|
|
1077
|
+
}, [searching, raw, activeMatch?.path, activeMatch?.part, activeMatch?.start, searchMode, navigationRequest]);
|
|
961
1078
|
const navigate = (direction) => {
|
|
962
1079
|
if (!occurrences.length) return;
|
|
963
|
-
|
|
1080
|
+
change({ ...state, selectedMatch: occurrences[selectedIndex < 0 ? direction > 0 ? 0 : occurrences.length - 1 : (matchIndex + direction + occurrences.length) % occurrences.length], searchOverrides: [] });
|
|
964
1081
|
setNavigationRequest((current) => current + 1);
|
|
965
|
-
setSearchOverrides(/* @__PURE__ */ new Map());
|
|
966
1082
|
};
|
|
967
1083
|
const copy2 = async (text, message) => {
|
|
968
1084
|
try {
|
|
@@ -974,19 +1090,19 @@ function JsonValueContent({ inspection, label, display: display2, onClose, compa
|
|
|
974
1090
|
};
|
|
975
1091
|
const toggle = (path, bucket = false) => {
|
|
976
1092
|
const expanded = bucket ? presentation.expandedBuckets.has(path) : !presentation.collapsed.has(path);
|
|
977
|
-
if (searching)
|
|
978
|
-
else
|
|
979
|
-
const next = new Set(
|
|
1093
|
+
if (searching) change({ ...state, searchOverrides: [...searchOverrides].filter(([key]) => key !== path).map(([path2, expanded2]) => ({ path: path2, expanded: expanded2 })).concat({ path, expanded: !expanded }) });
|
|
1094
|
+
else {
|
|
1095
|
+
const next = new Set(bucket ? expandedBuckets : collapsed);
|
|
980
1096
|
if (next.has(path)) next.delete(path);
|
|
981
1097
|
else next.add(path);
|
|
982
|
-
|
|
983
|
-
}
|
|
1098
|
+
change({ ...state, ...bucket ? { expandedBuckets: [...next] } : { collapsedPaths: [...next] } });
|
|
1099
|
+
}
|
|
984
1100
|
};
|
|
985
1101
|
return /* @__PURE__ */ jsxs3("section", { className: "qt-json-value-view", "aria-label": `${label} JSON value`, children: [
|
|
986
1102
|
!compact && /* @__PURE__ */ jsxs3("div", { className: "qt-json-toolbar", children: [
|
|
987
1103
|
onClose && /* @__PURE__ */ jsx3("strong", { className: "qt-json-title", children: label }),
|
|
988
1104
|
/* @__PURE__ */ jsxs3("div", { className: "qt-json-actions", children: [
|
|
989
|
-
/* @__PURE__ */ jsx3("button", { type: "button", className: "qt-link-btn qt-json-action", "aria-label": raw ? "Show JSON" : "Show original", title: raw ? "Show pretty-printed JSON" : "Show original source", "aria-pressed": raw, disabled: Boolean(inspection.error), onClick: () =>
|
|
1105
|
+
/* @__PURE__ */ jsx3("button", { type: "button", className: "qt-link-btn qt-json-action", "aria-label": raw ? "Show JSON" : "Show original", title: raw ? "Show pretty-printed JSON" : "Show original source", "aria-pressed": raw, disabled: Boolean(inspection.error), onClick: () => change({ ...state, raw: !raw }), children: /* @__PURE__ */ jsx3(Icon, { name: "panelDefinition", size: 14 }) }),
|
|
990
1106
|
/* @__PURE__ */ jsx3("button", { type: "button", className: "qt-link-btn qt-json-action", "aria-label": "Copy original", title: "Copy original source", onClick: () => copy2(inspection.raw, "Original value copied"), children: /* @__PURE__ */ jsx3(Icon, { name: "panelReference", size: 14 }) }),
|
|
991
1107
|
/* @__PURE__ */ jsx3("button", { type: "button", className: "qt-link-btn qt-json-action", "aria-label": "Copy JSON", title: "Copy JSON object or value", disabled: Boolean(inspection.error), onClick: () => copy2(inspection.formatted, "Formatted JSON copied"), children: /* @__PURE__ */ jsx3(Icon, { name: "copy", size: 14 }) })
|
|
992
1108
|
] }),
|
|
@@ -1000,12 +1116,11 @@ function JsonValueContent({ inspection, label, display: display2, onClose, compa
|
|
|
1000
1116
|
value: searchMode,
|
|
1001
1117
|
disabled: raw || Boolean(inspection.error),
|
|
1002
1118
|
onChange: (event) => {
|
|
1003
|
-
|
|
1004
|
-
setSearchOverrides(/* @__PURE__ */ new Map());
|
|
1119
|
+
change({ ...state, searchMode: event.target.value, searchOverrides: [] });
|
|
1005
1120
|
},
|
|
1006
1121
|
children: [
|
|
1007
1122
|
/* @__PURE__ */ jsx3("option", { value: "filter", children: "Filter" }),
|
|
1008
|
-
/* @__PURE__ */ jsx3("option", { value: "
|
|
1123
|
+
/* @__PURE__ */ jsx3("option", { value: "find", children: "Find" })
|
|
1009
1124
|
]
|
|
1010
1125
|
}
|
|
1011
1126
|
),
|
|
@@ -1019,12 +1134,11 @@ function JsonValueContent({ inspection, label, display: display2, onClose, compa
|
|
|
1019
1134
|
title: "Search keys, paths and values",
|
|
1020
1135
|
disabled: raw || Boolean(inspection.error),
|
|
1021
1136
|
placeholder: "Search JSON",
|
|
1137
|
+
maxLength: 1e4,
|
|
1022
1138
|
onChange: (event) => {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
setSearchOverrides(/* @__PURE__ */ new Map());
|
|
1139
|
+
const { selectedMatch: _selected, ...next } = state;
|
|
1140
|
+
change({ ...next, search: event.target.value, searchOverrides: [], visibleEntryLimit: 200 });
|
|
1026
1141
|
setCopyStatus("");
|
|
1027
|
-
setPageSize(200);
|
|
1028
1142
|
},
|
|
1029
1143
|
onKeyDown: (event) => {
|
|
1030
1144
|
if (event.key === "Enter") {
|
|
@@ -1035,7 +1149,7 @@ function JsonValueContent({ inspection, label, display: display2, onClose, compa
|
|
|
1035
1149
|
}
|
|
1036
1150
|
),
|
|
1037
1151
|
searching && /* @__PURE__ */ jsxs3("span", { className: "qt-json-search-count", "aria-live": "polite", children: [
|
|
1038
|
-
|
|
1152
|
+
activeMatch ? matchIndex + 1 : 0,
|
|
1039
1153
|
"/",
|
|
1040
1154
|
occurrences.length
|
|
1041
1155
|
] }),
|
|
@@ -1048,6 +1162,7 @@ function JsonValueContent({ inspection, label, display: display2, onClose, compa
|
|
|
1048
1162
|
] })
|
|
1049
1163
|
] }),
|
|
1050
1164
|
/* @__PURE__ */ jsxs3("div", { className: "qt-json-content", ref: contentRef, children: [
|
|
1165
|
+
state.selectedMatch && !activeMatch && /* @__PURE__ */ jsx3("p", { className: "qt-json-notice", role: "status", children: "Selected JSON match is unavailable." }),
|
|
1051
1166
|
inspection.error && /* @__PURE__ */ jsx3("p", { className: "qt-json-notice", role: "alert", children: inspection.error }),
|
|
1052
1167
|
inspection.raw.length > JSON_MAX_CHARACTERS && /* @__PURE__ */ jsx3("p", { className: "qt-json-notice", children: "Original text is truncated in this view. Copy original retains the full source." }),
|
|
1053
1168
|
inspection.limited && /* @__PURE__ */ jsx3("p", { className: "qt-json-notice", children: "Inspection is limited to 5,000 entries and 64 levels. Search covers the inspected entries only; Copy JSON includes the full value." }),
|
|
@@ -1075,7 +1190,7 @@ function JsonValueContent({ inspection, label, display: display2, onClose, compa
|
|
|
1075
1190
|
return /* @__PURE__ */ jsxs3(
|
|
1076
1191
|
"div",
|
|
1077
1192
|
{
|
|
1078
|
-
className: `qt-json-line${kind === "value" ? " qt-json-entry" : ""}${searching && kind === "value" && matchedPaths.has(entry.path) ? " qt-json-match" : ""}${searching && kind === "value" && activeMatch?.path === entry.path ? " qt-json-match--active" : ""}`,
|
|
1193
|
+
className: `qt-json-line${kind === "value" && attentionPath === entry.path ? " qt-attention-target" : ""}${kind === "value" ? " qt-json-entry" : ""}${searching && kind === "value" && matchedPaths.has(entry.path) ? " qt-json-match" : ""}${searching && kind === "value" && activeMatch?.path === entry.path ? " qt-json-match--active" : ""}`,
|
|
1079
1194
|
"data-json-path": entry.path,
|
|
1080
1195
|
title: entry.path,
|
|
1081
1196
|
style: { paddingInlineStart: `${Math.min(entry.depth + (kind === "omitted" ? 1 : 0), 32) * 16}px` },
|
|
@@ -1110,7 +1225,7 @@ function JsonValueContent({ inspection, label, display: display2, onClose, compa
|
|
|
1110
1225
|
);
|
|
1111
1226
|
}),
|
|
1112
1227
|
lines.length === 0 && /* @__PURE__ */ jsx3("p", { className: "qt-json-notice", children: "No matching keys or values." }),
|
|
1113
|
-
remaining > 0 && /* @__PURE__ */ jsx3("button", { type: "button", className: "qt-btn qt-json-more", onClick: () =>
|
|
1228
|
+
remaining > 0 && /* @__PURE__ */ jsx3("button", { type: "button", className: "qt-btn qt-json-more", onClick: () => change({ ...state, visibleEntryLimit: Math.min(1e4, pageSize + 200) }), children: "Show more" })
|
|
1114
1229
|
] })
|
|
1115
1230
|
] }),
|
|
1116
1231
|
!compact && /* @__PURE__ */ jsxs3("footer", { className: "qt-json-footer", children: [
|
|
@@ -1374,9 +1489,11 @@ import { encodeQuery, renderItemPageTemplate } from "@pythia-software/query-tabl
|
|
|
1374
1489
|
import { usePropertyView } from "@pythia-software/query-table-react";
|
|
1375
1490
|
|
|
1376
1491
|
// src/ObjectProperties.tsx
|
|
1377
|
-
import { cloneElement, isValidElement, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef3, useState as useState4 } from "react";
|
|
1492
|
+
import { cloneElement, isValidElement, useEffect as useEffect4, useLayoutEffect as useLayoutEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState4 } from "react";
|
|
1378
1493
|
import {
|
|
1379
1494
|
EMPTY_QUERY,
|
|
1495
|
+
isPropertyFieldLoaded,
|
|
1496
|
+
jsonInspectionScopeKey as jsonInspectionScopeKey2,
|
|
1380
1497
|
readFieldValue,
|
|
1381
1498
|
readObjectReference,
|
|
1382
1499
|
objectReferenceKey
|
|
@@ -1648,14 +1765,52 @@ function ObjectProperties(props) {
|
|
|
1648
1765
|
const drag = useRef3(null);
|
|
1649
1766
|
const [copied, setCopied] = useState4(null);
|
|
1650
1767
|
const [copyError, setCopyError] = useState4("");
|
|
1651
|
-
const [
|
|
1768
|
+
const [localInspections, setLocalInspections] = useState4([]);
|
|
1769
|
+
const inspections = [...props.jsonInspections ?? [], ...localInspections];
|
|
1770
|
+
const idField = schema.fields.find((field) => field.name === schema.idField);
|
|
1771
|
+
const identity = props.recordId ?? (idField ? readFieldValue(idField, row) : row[schema.idField]);
|
|
1772
|
+
const rowId = typeof identity === "string" || typeof identity === "number" && Number.isFinite(identity) ? identity : void 0;
|
|
1773
|
+
const [anonymousInspection, setAnonymousInspection] = useState4(() => ({ row, states: /* @__PURE__ */ new Map() }));
|
|
1774
|
+
const triggers = useRef3(/* @__PURE__ */ new Map());
|
|
1775
|
+
const latest = useRef3(props);
|
|
1776
|
+
latest.current = props;
|
|
1777
|
+
const renderedJson = /* @__PURE__ */ new Map();
|
|
1778
|
+
const jsonTargets = useRef3(renderedJson);
|
|
1779
|
+
useLayoutEffect3(() => {
|
|
1780
|
+
jsonTargets.current = renderedJson;
|
|
1781
|
+
});
|
|
1782
|
+
useEffect4(() => {
|
|
1783
|
+
if (!props.attention || rowId === void 0) return;
|
|
1784
|
+
return props.attention.register({ dataset: schema.name, rowId }, { reveal: async (target, signal) => {
|
|
1785
|
+
if (signal.aborted) return false;
|
|
1786
|
+
if (target.field !== void 0) {
|
|
1787
|
+
const property = latest.current.view.properties.find((entry) => entry.field === target.field);
|
|
1788
|
+
const field = latest.current.schema.fields.find((entry) => entry.name === target.field && entry.property?.enabled !== false);
|
|
1789
|
+
if (!property || !field || !isPropertyFieldLoaded(field, latest.current.loadedFields)) return false;
|
|
1790
|
+
if (target.path !== void 0) {
|
|
1791
|
+
const json = jsonTargets.current.get(target.field);
|
|
1792
|
+
if (!json || !inspectJsonValue(json.value, json.display).entries.some((entry) => entry.path === target.path)) return false;
|
|
1793
|
+
const provider = await props.attention.whenRegistered({ dataset: schema.name, rowId, field: target.field }, signal);
|
|
1794
|
+
return signal.aborted ? false : provider.reveal(target, signal);
|
|
1795
|
+
}
|
|
1796
|
+
const element = [...surface.current?.querySelectorAll("[data-property-field]") ?? []].find((element2) => element2.dataset.propertyField === target.field);
|
|
1797
|
+
if (!element) return false;
|
|
1798
|
+
element.scrollIntoView?.({ block: "nearest", behavior: "instant" });
|
|
1799
|
+
element.classList.add("qt-attention-target");
|
|
1800
|
+
return true;
|
|
1801
|
+
}
|
|
1802
|
+
surface.current?.scrollIntoView?.({ block: "nearest", behavior: "instant" });
|
|
1803
|
+
return Boolean(surface.current);
|
|
1804
|
+
} });
|
|
1805
|
+
}, [props.attention, schema.name, rowId]);
|
|
1806
|
+
const changeInspection = (entry) => {
|
|
1807
|
+
if (!props.jsonInspections || rowId === void 0) setLocalInspections((current) => [...current.filter((previous) => jsonInspectionScopeKey2(previous) !== jsonInspectionScopeKey2(entry)), entry]);
|
|
1808
|
+
if (rowId !== void 0) props.onJsonInspectionChange?.(entry);
|
|
1809
|
+
};
|
|
1652
1810
|
useEffect4(() => {
|
|
1653
1811
|
setCopied(null);
|
|
1654
1812
|
setCopyError("");
|
|
1655
1813
|
}, [row]);
|
|
1656
|
-
useEffect4(() => {
|
|
1657
|
-
setJsonViewer(null);
|
|
1658
|
-
}, [row]);
|
|
1659
1814
|
useEffect4(() => {
|
|
1660
1815
|
if (!copied) return;
|
|
1661
1816
|
const timer = setTimeout(() => setCopied(null), 1800);
|
|
@@ -1777,7 +1932,7 @@ function ObjectProperties(props) {
|
|
|
1777
1932
|
let jsonProps;
|
|
1778
1933
|
if (!field || field.property?.enabled === false)
|
|
1779
1934
|
content = /* @__PURE__ */ jsx6("span", { className: "qt-property-unavailable", children: "Field unavailable" });
|
|
1780
|
-
else if (
|
|
1935
|
+
else if (!isPropertyFieldLoaded(field, loadedFields))
|
|
1781
1936
|
content = /* @__PURE__ */ jsx6("span", { className: "qt-property-unavailable", children: "Not loaded" });
|
|
1782
1937
|
else {
|
|
1783
1938
|
const value = readFieldValue(field, row);
|
|
@@ -1788,8 +1943,23 @@ function ObjectProperties(props) {
|
|
|
1788
1943
|
const href = result?.href ? safeLinkHref(result.href) : null;
|
|
1789
1944
|
let display2 = result?.label ?? propertyContent(field, property, value, props, now);
|
|
1790
1945
|
if (isValidElement(display2) && display2.type === JsonValueView) {
|
|
1791
|
-
if (value != null && value !== "")
|
|
1792
|
-
|
|
1946
|
+
if (display2.props.value != null && display2.props.value !== "") {
|
|
1947
|
+
const scope = rowId === void 0 ? void 0 : { dataset: schema.name, rowId, field: property.field };
|
|
1948
|
+
const state = scope ? inspections.find((entry) => jsonInspectionScopeKey2(entry) === jsonInspectionScopeKey2(scope))?.state : anonymousInspection.row === row ? anonymousInspection.states.get(property.field) : void 0;
|
|
1949
|
+
jsonProps = {
|
|
1950
|
+
...display2.props,
|
|
1951
|
+
label: property.label ?? field.label,
|
|
1952
|
+
inspectionState: state ?? createJsonInspectionState(inspectJsonValue(display2.props.value, display2.props.display), display2.props.display, true),
|
|
1953
|
+
inspectionScope: scope,
|
|
1954
|
+
onInspectionStateChange: (state2) => {
|
|
1955
|
+
if (scope) changeInspection({ ...scope, state: state2 });
|
|
1956
|
+
else setAnonymousInspection((previous) => ({ row, states: new Map(previous.row === row ? previous.states : []).set(property.field, state2) }));
|
|
1957
|
+
},
|
|
1958
|
+
attention: props.attention
|
|
1959
|
+
};
|
|
1960
|
+
renderedJson.set(property.field, jsonProps);
|
|
1961
|
+
}
|
|
1962
|
+
display2 = cloneElement(display2, { ...jsonProps, compact: true });
|
|
1793
1963
|
}
|
|
1794
1964
|
content = href ? /* @__PURE__ */ jsxs4(
|
|
1795
1965
|
"a",
|
|
@@ -1820,7 +1990,7 @@ function ObjectProperties(props) {
|
|
|
1820
1990
|
}
|
|
1821
1991
|
) : display2;
|
|
1822
1992
|
}
|
|
1823
|
-
return /* @__PURE__ */ jsxs4("div", { className: "qt-property", "data-property-type": field?.type, children: [
|
|
1993
|
+
return /* @__PURE__ */ jsxs4("div", { className: "qt-property", "data-property-field": property.field, "data-property-type": field?.type, children: [
|
|
1824
1994
|
/* @__PURE__ */ jsxs4("dt", { children: [
|
|
1825
1995
|
/* @__PURE__ */ jsx6("span", { children: property.label ?? field?.label ?? property.field }),
|
|
1826
1996
|
jsonProps && /* @__PURE__ */ jsx6(
|
|
@@ -1830,7 +2000,10 @@ function ObjectProperties(props) {
|
|
|
1830
2000
|
className: "qt-link-btn qt-property-json-open",
|
|
1831
2001
|
"aria-label": `Open ${property.label ?? field?.label ?? property.field} in JSON Viewer`,
|
|
1832
2002
|
title: "Open in JSON Viewer",
|
|
1833
|
-
onClick: (event) =>
|
|
2003
|
+
onClick: (event) => {
|
|
2004
|
+
triggers.current.set(property.field, event.currentTarget);
|
|
2005
|
+
jsonProps.onInspectionStateChange?.({ ...jsonProps.inspectionState, open: true });
|
|
2006
|
+
},
|
|
1834
2007
|
children: /* @__PURE__ */ jsx6(Icon, { name: "panelDefinition", size: 14 })
|
|
1835
2008
|
}
|
|
1836
2009
|
)
|
|
@@ -1859,10 +2032,18 @@ function ObjectProperties(props) {
|
|
|
1859
2032
|
] })
|
|
1860
2033
|
]
|
|
1861
2034
|
}
|
|
2035
|
+
),
|
|
2036
|
+
jsonProps?.inspectionState?.open && entries.find((entry) => entry.property.field === property.field)?.property.id === property.id && /* @__PURE__ */ jsx6(
|
|
2037
|
+
JsonViewerModal,
|
|
2038
|
+
{
|
|
2039
|
+
...jsonProps,
|
|
2040
|
+
returnFocus: triggers.current.get(property.field),
|
|
2041
|
+
preserveFocus: props.preserveFocus && !triggers.current.get(property.field)?.isConnected,
|
|
2042
|
+
onClose: () => jsonProps.onInspectionStateChange?.({ ...jsonProps.inspectionState, open: false })
|
|
2043
|
+
}
|
|
1862
2044
|
)
|
|
1863
|
-
] }, property.id);
|
|
2045
|
+
] }, JSON.stringify([rowId, property.id]));
|
|
1864
2046
|
}) }),
|
|
1865
|
-
jsonViewer && /* @__PURE__ */ jsx6(JsonViewerModal, { ...jsonViewer.props, onClose: () => setJsonViewer(null), returnFocus: jsonViewer.trigger }),
|
|
1866
2047
|
copyError && /* @__PURE__ */ jsx6("p", { className: "qt-property-copy-error", role: "status", children: copyError }),
|
|
1867
2048
|
resolved.error ? /* @__PURE__ */ jsxs4("p", { role: "status", className: "qt-property-unavailable", children: [
|
|
1868
2049
|
"Relationship labels unavailable: ",
|
|
@@ -1884,7 +2065,7 @@ import { useMemo as useMemo3, useState as useState7, useEffect as useEffect6, us
|
|
|
1884
2065
|
import {
|
|
1885
2066
|
createContext,
|
|
1886
2067
|
useContext,
|
|
1887
|
-
useLayoutEffect as
|
|
2068
|
+
useLayoutEffect as useLayoutEffect4,
|
|
1888
2069
|
useRef as useRef4,
|
|
1889
2070
|
useState as useState5
|
|
1890
2071
|
} from "react";
|
|
@@ -1907,7 +2088,7 @@ function PickerSurface({
|
|
|
1907
2088
|
const returnFocus = useRef4(null);
|
|
1908
2089
|
close.current = onClose;
|
|
1909
2090
|
const [position, setPosition] = useState5({ top: 0, left: 0, maxHeight: 440 });
|
|
1910
|
-
|
|
2091
|
+
useLayoutEffect4(() => {
|
|
1911
2092
|
if (mobile) return;
|
|
1912
2093
|
const element = surface.current;
|
|
1913
2094
|
if (!element) return;
|
|
@@ -2614,7 +2795,7 @@ function useMetricResize() {
|
|
|
2614
2795
|
}
|
|
2615
2796
|
|
|
2616
2797
|
// src/useEditorWorkbench.ts
|
|
2617
|
-
import { useLayoutEffect as
|
|
2798
|
+
import { useLayoutEffect as useLayoutEffect5, useRef as useRef8, useState as useState8 } from "react";
|
|
2618
2799
|
function fitPanes(previous, available, visible, minimums) {
|
|
2619
2800
|
const total = Math.max(
|
|
2620
2801
|
minimums.reduce((sum, width, i) => sum + (visible[i] ? width : 0), 0),
|
|
@@ -2670,7 +2851,7 @@ function useEditorWorkbench(panes5, isEditor = true) {
|
|
|
2670
2851
|
(visible, i) => visible ? [i] : []
|
|
2671
2852
|
);
|
|
2672
2853
|
const widths = fitPanes(preferredWidths, available, visiblePanes, minimums);
|
|
2673
|
-
|
|
2854
|
+
useLayoutEffect5(() => {
|
|
2674
2855
|
const element = root.current;
|
|
2675
2856
|
if (!element || !isEditor) return;
|
|
2676
2857
|
const fit = () => {
|
|
@@ -3752,6 +3933,11 @@ function DetailContent({ api, record, loading, error, onRetry, renderers, mode,
|
|
|
3752
3933
|
{
|
|
3753
3934
|
schema: api.schema,
|
|
3754
3935
|
row: record.row,
|
|
3936
|
+
recordId: id,
|
|
3937
|
+
...api.jsonInspections ? { jsonInspections: api.jsonInspections } : {},
|
|
3938
|
+
...api.setJsonInspection ? { onJsonInspectionChange: api.setJsonInspection } : {},
|
|
3939
|
+
attention: api.attention,
|
|
3940
|
+
preserveFocus: api.preserveFocus,
|
|
3755
3941
|
...api.subject === "group" ? { query } : {},
|
|
3756
3942
|
propertyApi: api.propertiesByMode[mode],
|
|
3757
3943
|
viewScope: mode === "hover" ? "Popover" : mode === "expand" ? "Row expand" : "Modal",
|
|
@@ -3806,7 +3992,7 @@ function ItemDetailsPanel({ api, query, renderers, outsidePage = false }) {
|
|
|
3806
3992
|
if (!api.active) return null;
|
|
3807
3993
|
const { id, mode } = api.active;
|
|
3808
3994
|
const content = /* @__PURE__ */ jsx17(DetailContent, { api, record: api.record, loading: api.loading, error: api.error, onRetry: api.retry, renderers, mode, query, id, onClose: api.close });
|
|
3809
|
-
if (mode === "modal") return /* @__PURE__ */ jsx17(ModalSurface, { title: api.subject === "group" ? "Group details" : "Item details", className: "qt-modal qt-item-detail-modal", chrome: false, onClose: api.close, children: content });
|
|
3995
|
+
if (mode === "modal") return /* @__PURE__ */ jsx17(ModalSurface, { title: api.subject === "group" ? "Group details" : "Item details", className: "qt-modal qt-item-detail-modal", preserveFocus: api.preserveFocus, chrome: false, onClose: api.close, children: content });
|
|
3810
3996
|
const noun = api.subject === "group" ? "group" : "item";
|
|
3811
3997
|
return /* @__PURE__ */ jsxs13("section", { className: "qt-item-expanded", "aria-label": `Expanded ${noun} ${id}`, children: [
|
|
3812
3998
|
outsidePage && /* @__PURE__ */ jsxs13("p", { className: "qt-item-detail-note", children: [
|
|
@@ -4017,8 +4203,8 @@ function ItemHoverCard({ api, query, id, renderers, anchor, onClose }) {
|
|
|
4017
4203
|
|
|
4018
4204
|
// src/DataTable.tsx
|
|
4019
4205
|
import { GROUP_ID_FIELD, GROUP_LINK_RENDER, ITEM_DETAILS_COLUMN, groupKeyFields, isComputedCellError, isGroupLinkValue as isGroupLinkValue2 } from "@pythia-software/query-table-core";
|
|
4020
|
-
import { useId as useId4, useCallback as useCallback3, useEffect as useEffect12, useLayoutEffect as
|
|
4021
|
-
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
4206
|
+
import { useId as useId4, useCallback as useCallback3, useEffect as useEffect12, useLayoutEffect as useLayoutEffect6, useMemo as useMemo5, useRef as useRef14, useState as useState16 } from "react";
|
|
4207
|
+
import { defaultRangeExtractor, useVirtualizer } from "@tanstack/react-virtual";
|
|
4022
4208
|
import { isSortable, readFieldValue as readFieldValue2 } from "@pythia-software/query-table-core";
|
|
4023
4209
|
import { useColumnDrag } from "@pythia-software/query-table-react";
|
|
4024
4210
|
|
|
@@ -4769,8 +4955,14 @@ function DataTable(props) {
|
|
|
4769
4955
|
const estimateVirtualRow = useCallback3((index) => entries[index]?.detail ? 400 : normalizedEstimate, [normalizedEstimate, entries]);
|
|
4770
4956
|
const initialViewportHeight = typeof maxHeight === "number" && maxHeight > 0 ? maxHeight : DEFAULT_MAX_HEIGHT;
|
|
4771
4957
|
const initialRect = useMemo5(() => ({ width: 0, height: initialViewportHeight }), [initialViewportHeight]);
|
|
4958
|
+
const [attentionRange, setAttentionRange] = useState16(null);
|
|
4959
|
+
const holdingAttention = attentionRange?.index === expandedRowIndex && attentionRange?.revision === details?.attentionRevision;
|
|
4960
|
+
const detailRange = useCallback3((range) => {
|
|
4961
|
+
return defaultRangeExtractor((details?.attentionPending || holdingAttention) && expandedRowIndex >= 0 ? { ...range, startIndex: expandedRowIndex, endIndex: expandedRowIndex + 1 } : range);
|
|
4962
|
+
}, [details?.attentionPending, holdingAttention, expandedRowIndex]);
|
|
4772
4963
|
const rowVirtualizer = useVirtualizer({
|
|
4773
4964
|
count: entries.length,
|
|
4965
|
+
rangeExtractor: detailRange,
|
|
4774
4966
|
getScrollElement: () => tableWrapRef.current,
|
|
4775
4967
|
estimateSize: estimateVirtualRow,
|
|
4776
4968
|
getItemKey: getVirtualRowKey,
|
|
@@ -4782,9 +4974,17 @@ function DataTable(props) {
|
|
|
4782
4974
|
// Row refs measure during React's commit phase; schedule size updates normally.
|
|
4783
4975
|
useFlushSync: false
|
|
4784
4976
|
});
|
|
4977
|
+
useLayoutEffect6(() => {
|
|
4978
|
+
if (details?.attentionPending && expandedRowIndex >= 0) {
|
|
4979
|
+
setAttentionRange((previous) => previous?.index === expandedRowIndex && previous.revision === details.attentionRevision ? previous : { index: expandedRowIndex, revision: details.attentionRevision });
|
|
4980
|
+
} else {
|
|
4981
|
+
const range = rowVirtualizer.range;
|
|
4982
|
+
if (expandedRowIndex < 0 || range && range.startIndex - normalizedOverscan <= expandedRowIndex + 1 && range.endIndex + normalizedOverscan >= expandedRowIndex + 1) setAttentionRange(null);
|
|
4983
|
+
}
|
|
4984
|
+
}, [details?.attentionPending, details?.attentionRevision, expandedRowIndex, rowVirtualizer.range?.startIndex, rowVirtualizer.range?.endIndex, normalizedOverscan]);
|
|
4785
4985
|
useEffect12(() => {
|
|
4786
4986
|
if (expandedRowIndex >= 0) rowVirtualizer.scrollToIndex(expandedRowIndex, { align: "start" });
|
|
4787
|
-
}, [expandedRowIndex]);
|
|
4987
|
+
}, [expandedRowIndex, details?.attentionRevision]);
|
|
4788
4988
|
useEffect12(() => {
|
|
4789
4989
|
return () => {
|
|
4790
4990
|
if (copiedCellTimerRef.current) {
|
|
@@ -5085,7 +5285,7 @@ function DataTable(props) {
|
|
|
5085
5285
|
const renderedColumnNames = useMemo5(() => columnDrag.preview(tableColumnNames), [columnDrag, tableColumnNames]);
|
|
5086
5286
|
const dragPreviewRef = useRef14(null);
|
|
5087
5287
|
const dropRevisionRef = useRef14(columnDrag.dropRevision);
|
|
5088
|
-
const useCommitLayoutEffect = typeof window === "undefined" ? useEffect12 :
|
|
5288
|
+
const useCommitLayoutEffect = typeof window === "undefined" ? useEffect12 : useLayoutEffect6;
|
|
5089
5289
|
useCommitLayoutEffect(() => {
|
|
5090
5290
|
if (columnDrag.active) {
|
|
5091
5291
|
dragPreviewRef.current = renderedColumnNames;
|
|
@@ -5233,6 +5433,7 @@ function DataTable(props) {
|
|
|
5233
5433
|
onWheel: onTableWheel,
|
|
5234
5434
|
style: { maxHeight, overflowY: "auto" },
|
|
5235
5435
|
onScroll: () => {
|
|
5436
|
+
if (!details?.attentionPending) setAttentionRange(null);
|
|
5236
5437
|
const wrap = tableWrapRef.current;
|
|
5237
5438
|
if (!wrap) return;
|
|
5238
5439
|
setShowHorizontalCue(wrap.scrollWidth - wrap.clientWidth - wrap.scrollLeft > 2);
|
|
@@ -6160,7 +6361,7 @@ function ColumnPicker({
|
|
|
6160
6361
|
// src/grouping/GroupedColumnsEditor.tsx
|
|
6161
6362
|
import {
|
|
6162
6363
|
useEffect as useEffect14,
|
|
6163
|
-
useLayoutEffect as
|
|
6364
|
+
useLayoutEffect as useLayoutEffect7,
|
|
6164
6365
|
useId as useId6,
|
|
6165
6366
|
useMemo as useMemo7,
|
|
6166
6367
|
useRef as useRef16,
|
|
@@ -7238,7 +7439,7 @@ function GroupedColumnsEditor({
|
|
|
7238
7439
|
const workbench = useEditorWorkbench(panes2.map(
|
|
7239
7440
|
(pane, index) => index === 2 ? { ...pane, defaultVisible: editingAggregate } : pane
|
|
7240
7441
|
));
|
|
7241
|
-
|
|
7442
|
+
useLayoutEffect7(() => {
|
|
7242
7443
|
if (editingAggregate) workbench.showPane(2);
|
|
7243
7444
|
else {
|
|
7244
7445
|
if (workbench.visibleIndices.length === 1 && workbench.visiblePanes[2])
|
|
@@ -8621,7 +8822,7 @@ import {
|
|
|
8621
8822
|
Suspense as Suspense2,
|
|
8622
8823
|
useEffect as useEffect17,
|
|
8623
8824
|
useId as useId8,
|
|
8624
|
-
useLayoutEffect as
|
|
8825
|
+
useLayoutEffect as useLayoutEffect8,
|
|
8625
8826
|
useMemo as useMemo11,
|
|
8626
8827
|
useRef as useRef18,
|
|
8627
8828
|
useState as useState27
|
|
@@ -10625,7 +10826,7 @@ function MetricsEditor({
|
|
|
10625
10826
|
retry,
|
|
10626
10827
|
api.aggregations.preview
|
|
10627
10828
|
]);
|
|
10628
|
-
|
|
10829
|
+
useLayoutEffect8(() => {
|
|
10629
10830
|
setInspectOpen(false);
|
|
10630
10831
|
setInspectedKey(null);
|
|
10631
10832
|
pendingInsertion.current = null;
|
|
@@ -10657,7 +10858,7 @@ function MetricsEditor({
|
|
|
10657
10858
|
observer.observe(element);
|
|
10658
10859
|
return () => observer.disconnect();
|
|
10659
10860
|
}, [view, !!selected]);
|
|
10660
|
-
|
|
10861
|
+
useLayoutEffect8(() => {
|
|
10661
10862
|
if (view === "dashboard") {
|
|
10662
10863
|
const card2 = Array.from(
|
|
10663
10864
|
canvasElement.current?.querySelectorAll(
|
|
@@ -16645,6 +16846,7 @@ export {
|
|
|
16645
16846
|
SelectColumnEditor,
|
|
16646
16847
|
SelectionToolbar,
|
|
16647
16848
|
createGroupLinkRenderer,
|
|
16849
|
+
createJsonInspectionState,
|
|
16648
16850
|
createMetricColorResolver,
|
|
16649
16851
|
defaultRenderers,
|
|
16650
16852
|
formatMetricOutput,
|