@modernrelay/orbit-react 0.2.0
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/LICENSE +21 -0
- package/dist/GraphProvider-B8ITDXS0.d.ts +11 -0
- package/dist/chunk-7XB47YST.js +69 -0
- package/dist/chunk-7XB47YST.js.map +1 -0
- package/dist/chunk-DGFLU2SN.js +108 -0
- package/dist/chunk-DGFLU2SN.js.map +1 -0
- package/dist/chunk-JN7QCMFW.js +93 -0
- package/dist/chunk-JN7QCMFW.js.map +1 -0
- package/dist/chunk-QJ7JXQVK.js +119 -0
- package/dist/chunk-QJ7JXQVK.js.map +1 -0
- package/dist/components/ContextMenu.d.ts +73 -0
- package/dist/components/ContextMenu.js +367 -0
- package/dist/components/ContextMenu.js.map +1 -0
- package/dist/components/Histogram.d.ts +33 -0
- package/dist/components/Histogram.js +216 -0
- package/dist/components/Histogram.js.map +1 -0
- package/dist/components/Inspector.d.ts +63 -0
- package/dist/components/Inspector.js +246 -0
- package/dist/components/Inspector.js.map +1 -0
- package/dist/components/Legend.d.ts +45 -0
- package/dist/components/Legend.js +185 -0
- package/dist/components/Legend.js.map +1 -0
- package/dist/components/Minimap.d.ts +63 -0
- package/dist/components/Minimap.js +222 -0
- package/dist/components/Minimap.js.map +1 -0
- package/dist/components/Navigator.d.ts +90 -0
- package/dist/components/Navigator.js +381 -0
- package/dist/components/Navigator.js.map +1 -0
- package/dist/components/Search.d.ts +70 -0
- package/dist/components/Search.js +203 -0
- package/dist/components/Search.js.map +1 -0
- package/dist/components/SelectionActions.d.ts +36 -0
- package/dist/components/SelectionActions.js +126 -0
- package/dist/components/SelectionActions.js.map +1 -0
- package/dist/components/SimControls.d.ts +124 -0
- package/dist/components/SimControls.js +221 -0
- package/dist/components/SimControls.js.map +1 -0
- package/dist/components/Table.d.ts +92 -0
- package/dist/components/Table.js +505 -0
- package/dist/components/Table.js.map +1 -0
- package/dist/components/Timeline.d.ts +34 -0
- package/dist/components/Timeline.js +182 -0
- package/dist/components/Timeline.js.map +1 -0
- package/dist/components/Toolbar.d.ts +60 -0
- package/dist/components/Toolbar.js +220 -0
- package/dist/components/Toolbar.js.map +1 -0
- package/dist/components/Tooltip.d.ts +65 -0
- package/dist/components/Tooltip.js +178 -0
- package/dist/components/Tooltip.js.map +1 -0
- package/dist/index.d.ts +488 -0
- package/dist/index.js +827 -0
- package/dist/index.js.map +1 -0
- package/dist/shared-Bj3Gk219.d.ts +43 -0
- package/package.json +99 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import { useResolvedInstance } from '../chunk-7XB47YST.js';
|
|
2
|
+
import { forwardRef, useState, useMemo, useCallback, useRef, useEffect, useImperativeHandle, useSyncExternalStore } from 'react';
|
|
3
|
+
import { coerceNumeric } from '@modernrelay/orbit-core';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
function cellText(value) {
|
|
7
|
+
if (value === null || value === void 0) return "";
|
|
8
|
+
if (typeof value === "string") return value;
|
|
9
|
+
if (typeof value === "object") {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.stringify(value) ?? String(value);
|
|
12
|
+
} catch {
|
|
13
|
+
return String(value);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return String(value);
|
|
17
|
+
}
|
|
18
|
+
function nodeCellValue(node, id, key) {
|
|
19
|
+
if (key === "id") return id;
|
|
20
|
+
const attrs = node?.attrs;
|
|
21
|
+
return attrs?.[key];
|
|
22
|
+
}
|
|
23
|
+
function edgeCellValue(edge, key) {
|
|
24
|
+
if (key === "id") return edge.id ?? "";
|
|
25
|
+
if (key === "source") return edge.source;
|
|
26
|
+
if (key === "target") return edge.target;
|
|
27
|
+
const attrs = edge.attrs;
|
|
28
|
+
return attrs?.[key];
|
|
29
|
+
}
|
|
30
|
+
function normalizeColumns(pick) {
|
|
31
|
+
return pick.map((c) => typeof c === "string" ? { key: c } : c);
|
|
32
|
+
}
|
|
33
|
+
function deriveColumns(mode, attrsSample) {
|
|
34
|
+
const keys = mode === "edges" ? ["id", "source", "target"] : ["id"];
|
|
35
|
+
const seen = new Set(keys);
|
|
36
|
+
for (const attrs of attrsSample) {
|
|
37
|
+
if (attrs === void 0) continue;
|
|
38
|
+
for (const key of Object.keys(attrs)) {
|
|
39
|
+
if (!seen.has(key)) {
|
|
40
|
+
seen.add(key);
|
|
41
|
+
keys.push(key);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return keys.map((key) => ({ key }));
|
|
46
|
+
}
|
|
47
|
+
var NULL_KEY = { tier: 2, num: 0, str: "" };
|
|
48
|
+
function isSentinelString(value) {
|
|
49
|
+
const lower = value.trim().toLowerCase();
|
|
50
|
+
return lower === "" || lower === "nan" || lower === "infinity" || lower === "-infinity" || lower === "+infinity";
|
|
51
|
+
}
|
|
52
|
+
function sortKeyOf(value) {
|
|
53
|
+
if (value === null || value === void 0) return NULL_KEY;
|
|
54
|
+
const num = coerceNumeric(value);
|
|
55
|
+
if (num !== null) return { tier: 0, num, str: "" };
|
|
56
|
+
if (typeof value === "number") return NULL_KEY;
|
|
57
|
+
if (typeof value === "string") {
|
|
58
|
+
return isSentinelString(value) ? NULL_KEY : { tier: 1, num: 0, str: value };
|
|
59
|
+
}
|
|
60
|
+
return { tier: 1, num: 0, str: cellText(value) };
|
|
61
|
+
}
|
|
62
|
+
function compareSortKeys(a, b, direction) {
|
|
63
|
+
if (a.tier !== b.tier) {
|
|
64
|
+
if (a.tier === 2) return 1;
|
|
65
|
+
if (b.tier === 2) return -1;
|
|
66
|
+
return a.tier - b.tier;
|
|
67
|
+
}
|
|
68
|
+
if (a.tier === 2) return 0;
|
|
69
|
+
if (a.tier === 0) return direction * (a.num < b.num ? -1 : a.num > b.num ? 1 : 0);
|
|
70
|
+
return direction * (a.str < b.str ? -1 : a.str > b.str ? 1 : 0);
|
|
71
|
+
}
|
|
72
|
+
function textMatches(texts, queryLower) {
|
|
73
|
+
for (const text of texts) {
|
|
74
|
+
if (text.toLowerCase().includes(queryLower)) return true;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/components/Table/csv.ts
|
|
80
|
+
var FORMULA_LEADS = "=+-@ \r";
|
|
81
|
+
function csvCell(value, neutralize) {
|
|
82
|
+
const numericExempt = typeof value === "number" && Number.isFinite(value);
|
|
83
|
+
let text = cellText(value);
|
|
84
|
+
if (neutralize && !numericExempt && text.length > 0 && FORMULA_LEADS.includes(text[0])) {
|
|
85
|
+
text = `'${text}`;
|
|
86
|
+
}
|
|
87
|
+
if (/[",\r\n]/.test(text)) {
|
|
88
|
+
text = `"${text.replaceAll('"', '""')}"`;
|
|
89
|
+
}
|
|
90
|
+
return text;
|
|
91
|
+
}
|
|
92
|
+
function toCsvString(header, rows, neutralize) {
|
|
93
|
+
const records = [header.map((h) => csvCell(h, neutralize)).join(",")];
|
|
94
|
+
for (const row of rows) {
|
|
95
|
+
records.push(row.map((cell) => csvCell(cell, neutralize)).join(","));
|
|
96
|
+
}
|
|
97
|
+
return records.join("\r\n");
|
|
98
|
+
}
|
|
99
|
+
var __ORBIT_TABLE_SENTINEL__ = "orbit-react/components/Table";
|
|
100
|
+
var COLUMN_SAMPLE_DEFAULT = 200;
|
|
101
|
+
var ROW_HEIGHT_DEFAULT = 28;
|
|
102
|
+
var HEIGHT_DEFAULT = 320;
|
|
103
|
+
var OVERSCAN_DEFAULT = 8;
|
|
104
|
+
var TABLE_FILTER_DIMENSION_DEFAULT = "table";
|
|
105
|
+
function useTableVersion(instance) {
|
|
106
|
+
const subscribe = useCallback(
|
|
107
|
+
(onChange) => {
|
|
108
|
+
let sessionUnsub = null;
|
|
109
|
+
const attach = () => {
|
|
110
|
+
sessionUnsub?.();
|
|
111
|
+
sessionUnsub = null;
|
|
112
|
+
const session = instance.getCrossfilterSession();
|
|
113
|
+
if (session !== null) sessionUnsub = session.subscribe(onChange);
|
|
114
|
+
};
|
|
115
|
+
attach();
|
|
116
|
+
const storeUnsub = instance.store.subscribe(() => {
|
|
117
|
+
attach();
|
|
118
|
+
onChange();
|
|
119
|
+
});
|
|
120
|
+
return () => {
|
|
121
|
+
storeUnsub();
|
|
122
|
+
sessionUnsub?.();
|
|
123
|
+
sessionUnsub = null;
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
[instance]
|
|
127
|
+
);
|
|
128
|
+
const getSnapshot = useCallback(() => {
|
|
129
|
+
const s = instance.store.getState();
|
|
130
|
+
const hasSession = instance.getCrossfilterSession() !== null ? 1 : 0;
|
|
131
|
+
return `${s.status}:${s.revisions.model}:${s.revisions.scope}:${s.revisions.render}:${s.visible.nodes}:${s.visible.edges}:${s.nodeCount}:${s.edgeCount}:${hasSession}`;
|
|
132
|
+
}, [instance]);
|
|
133
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
134
|
+
}
|
|
135
|
+
function useSelection(instance) {
|
|
136
|
+
const subscribe = useCallback(
|
|
137
|
+
(onChange) => instance.store.subscribe(onChange),
|
|
138
|
+
[instance]
|
|
139
|
+
);
|
|
140
|
+
const getSnapshot = useCallback(
|
|
141
|
+
() => instance.store.getState().selection,
|
|
142
|
+
[instance]
|
|
143
|
+
);
|
|
144
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
145
|
+
}
|
|
146
|
+
var ROOT_STYLE = {
|
|
147
|
+
display: "flex",
|
|
148
|
+
flexDirection: "column",
|
|
149
|
+
gap: 6,
|
|
150
|
+
padding: 8,
|
|
151
|
+
borderRadius: 8,
|
|
152
|
+
background: "rgba(23, 25, 32, 0.96)",
|
|
153
|
+
border: "1px solid rgba(255, 255, 255, 0.14)",
|
|
154
|
+
color: "#e8eaf0",
|
|
155
|
+
font: "12px/1.4 system-ui, sans-serif"
|
|
156
|
+
};
|
|
157
|
+
var TOOLBAR_STYLE = { display: "flex", alignItems: "center", gap: 8 };
|
|
158
|
+
var FILTER_STYLE = {
|
|
159
|
+
flex: 1,
|
|
160
|
+
minWidth: 0,
|
|
161
|
+
background: "rgba(255, 255, 255, 0.06)",
|
|
162
|
+
border: "1px solid rgba(255, 255, 255, 0.14)",
|
|
163
|
+
borderRadius: 6,
|
|
164
|
+
color: "inherit",
|
|
165
|
+
font: "inherit",
|
|
166
|
+
padding: "3px 8px"
|
|
167
|
+
};
|
|
168
|
+
var COUNT_STYLE = { opacity: 0.65, whiteSpace: "nowrap" };
|
|
169
|
+
var HEADER_ROW_STYLE = { borderBottom: "1px solid rgba(255, 255, 255, 0.2)" };
|
|
170
|
+
var HEADER_BUTTON_STYLE = {
|
|
171
|
+
appearance: "none",
|
|
172
|
+
background: "transparent",
|
|
173
|
+
border: "none",
|
|
174
|
+
color: "inherit",
|
|
175
|
+
cursor: "pointer",
|
|
176
|
+
font: "inherit",
|
|
177
|
+
fontWeight: 600,
|
|
178
|
+
padding: "2px 4px",
|
|
179
|
+
textAlign: "left",
|
|
180
|
+
width: "100%",
|
|
181
|
+
overflow: "hidden",
|
|
182
|
+
textOverflow: "ellipsis",
|
|
183
|
+
whiteSpace: "nowrap"
|
|
184
|
+
};
|
|
185
|
+
var ROW_STYLE = { cursor: "pointer" };
|
|
186
|
+
var ROW_SELECTED_STYLE = {
|
|
187
|
+
...ROW_STYLE,
|
|
188
|
+
background: "rgba(122, 162, 255, 0.22)"
|
|
189
|
+
};
|
|
190
|
+
var CELL_STYLE = {
|
|
191
|
+
overflow: "hidden",
|
|
192
|
+
textOverflow: "ellipsis",
|
|
193
|
+
whiteSpace: "nowrap",
|
|
194
|
+
padding: "0 4px"
|
|
195
|
+
};
|
|
196
|
+
var EMPTY_STYLE = { fontStyle: "italic", opacity: 0.8, padding: 4 };
|
|
197
|
+
var CELL_MECHANICS = { flex: 1, minWidth: 0 };
|
|
198
|
+
function GraphTableInner(props, ref) {
|
|
199
|
+
const instance = useResolvedInstance(props.instance, "<GraphTable>");
|
|
200
|
+
const version = useTableVersion(instance);
|
|
201
|
+
const selection = useSelection(instance);
|
|
202
|
+
const mode = props.mode ?? "nodes";
|
|
203
|
+
const rowHeight = props.rowHeight ?? ROW_HEIGHT_DEFAULT;
|
|
204
|
+
const height = props.height ?? HEIGHT_DEFAULT;
|
|
205
|
+
const overscan = props.overscan ?? OVERSCAN_DEFAULT;
|
|
206
|
+
const dimensionKey = props.filterDimension ?? TABLE_FILTER_DIMENSION_DEFAULT;
|
|
207
|
+
const neutralize = props.neutralizeFormulas ?? true;
|
|
208
|
+
const predicate = props.filterPredicate;
|
|
209
|
+
const [internalFilter, setInternalFilter] = useState("");
|
|
210
|
+
const filterText = props.filterText ?? internalFilter;
|
|
211
|
+
const query = filterText.trim().toLowerCase();
|
|
212
|
+
const [sort, setSort] = useState(null);
|
|
213
|
+
const [scrollTop, setScrollTop] = useState(0);
|
|
214
|
+
const edgesProp = props.edges;
|
|
215
|
+
const baseRows = useMemo(() => {
|
|
216
|
+
if (mode === "edges") {
|
|
217
|
+
const visible = new Set(instance.getVisibleNodeIds());
|
|
218
|
+
const out = [];
|
|
219
|
+
(edgesProp ?? []).forEach((edge, i) => {
|
|
220
|
+
if (!visible.has(edge.source) || !visible.has(edge.target)) return;
|
|
221
|
+
if (edge.id !== void 0) out.push({ key: edge.id, id: edge.id, edge });
|
|
222
|
+
else out.push({ key: `${i}:${edge.source}\u2192${edge.target}`, id: null, edge });
|
|
223
|
+
});
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
226
|
+
return instance.getVisibleNodeIds().map((id) => ({ key: id, id }));
|
|
227
|
+
}, [instance, version, mode, edgesProp]);
|
|
228
|
+
const columnSample = props.columnSample ?? COLUMN_SAMPLE_DEFAULT;
|
|
229
|
+
const columnsProp = props.columns;
|
|
230
|
+
const columns = useMemo(() => {
|
|
231
|
+
if (columnsProp !== void 0) return normalizeColumns(columnsProp);
|
|
232
|
+
const sample = baseRows.slice(0, columnSample).map((row) => {
|
|
233
|
+
return row.edge !== void 0 ? row.edge.attrs : instance.getNode(row.key)?.attrs;
|
|
234
|
+
});
|
|
235
|
+
return deriveColumns(mode, sample);
|
|
236
|
+
}, [instance, baseRows, mode, columnsProp, columnSample]);
|
|
237
|
+
const valueOf = useCallback(
|
|
238
|
+
(row, key) => row.edge !== void 0 ? edgeCellValue(row.edge, key) : nodeCellValue(instance.getNode(row.key), row.key, key),
|
|
239
|
+
[instance]
|
|
240
|
+
);
|
|
241
|
+
const matchesRow = useCallback(
|
|
242
|
+
(row) => {
|
|
243
|
+
if (predicate !== void 0) {
|
|
244
|
+
const item = row.edge !== void 0 ? row.edge : instance.getNode(row.key);
|
|
245
|
+
if (item === void 0 || !predicate(item)) return false;
|
|
246
|
+
}
|
|
247
|
+
if (query === "") return true;
|
|
248
|
+
return textMatches(
|
|
249
|
+
columns.map((col) => cellText(valueOf(row, col.key))),
|
|
250
|
+
query
|
|
251
|
+
);
|
|
252
|
+
},
|
|
253
|
+
[instance, predicate, query, columns, valueOf]
|
|
254
|
+
);
|
|
255
|
+
const filterActive = query !== "" || predicate !== void 0;
|
|
256
|
+
const filteredRows = useMemo(
|
|
257
|
+
() => filterActive ? baseRows.filter(matchesRow) : baseRows,
|
|
258
|
+
[baseRows, filterActive, matchesRow]
|
|
259
|
+
);
|
|
260
|
+
const sortedRows = useMemo(() => {
|
|
261
|
+
if (sort === null || !columns.some((c) => c.key === sort.key)) return filteredRows;
|
|
262
|
+
const direction = sort.direction === "asc" ? 1 : -1;
|
|
263
|
+
const decorated = filteredRows.map((row) => ({
|
|
264
|
+
row,
|
|
265
|
+
key: sortKeyOf(valueOf(row, sort.key))
|
|
266
|
+
}));
|
|
267
|
+
decorated.sort((a, b) => compareSortKeys(a.key, b.key, direction));
|
|
268
|
+
return decorated.map((d) => d.row);
|
|
269
|
+
}, [filteredRows, sort, columns, valueOf]);
|
|
270
|
+
const wroteBrushRef = useRef(null);
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
if (mode !== "nodes") return;
|
|
273
|
+
const session = instance.getCrossfilterSession();
|
|
274
|
+
if (session === null) return;
|
|
275
|
+
try {
|
|
276
|
+
session.getBrush(dimensionKey);
|
|
277
|
+
} catch {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
let brush = null;
|
|
281
|
+
if (filterActive) {
|
|
282
|
+
const excluded = [];
|
|
283
|
+
for (const id of instance.getSceneNodeIds()) {
|
|
284
|
+
if (!matchesRow({ key: id, id })) excluded.push(id);
|
|
285
|
+
}
|
|
286
|
+
if (excluded.length > 0) brush = { excluded };
|
|
287
|
+
}
|
|
288
|
+
if (brush !== null) wroteBrushRef.current = { instance, key: dimensionKey };
|
|
289
|
+
void session.setBrush(dimensionKey, brush).catch(() => void 0);
|
|
290
|
+
}, [instance, version, mode, dimensionKey, filterActive, matchesRow]);
|
|
291
|
+
useEffect(
|
|
292
|
+
() => () => {
|
|
293
|
+
const wrote = wroteBrushRef.current;
|
|
294
|
+
if (wrote === null) return;
|
|
295
|
+
wroteBrushRef.current = null;
|
|
296
|
+
const session = wrote.instance.getCrossfilterSession();
|
|
297
|
+
if (session !== null) void session.setBrush(wrote.key, null).catch(() => void 0);
|
|
298
|
+
},
|
|
299
|
+
[]
|
|
300
|
+
);
|
|
301
|
+
const selectedNodes = useMemo(() => new Set(selection.nodeIds), [selection]);
|
|
302
|
+
const selectedEdges = useMemo(() => new Set(selection.edgeIds), [selection]);
|
|
303
|
+
const isSelected = (row) => row.edge !== void 0 ? row.id !== null && selectedEdges.has(row.id) : selectedNodes.has(row.key);
|
|
304
|
+
const activateRow = (row, additive) => {
|
|
305
|
+
if (row.edge !== void 0) {
|
|
306
|
+
if (row.id === null) return;
|
|
307
|
+
const id = row.id;
|
|
308
|
+
if (additive) {
|
|
309
|
+
const current = selection.edgeIds;
|
|
310
|
+
instance.selectEdges(
|
|
311
|
+
current.includes(id) ? current.filter((e) => e !== id) : [...current, id]
|
|
312
|
+
);
|
|
313
|
+
} else {
|
|
314
|
+
instance.selectEdges([id]);
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (additive) {
|
|
319
|
+
const current = selection.nodeIds;
|
|
320
|
+
instance.selectNodes(
|
|
321
|
+
current.includes(row.key) ? current.filter((n) => n !== row.key) : [...current, row.key]
|
|
322
|
+
);
|
|
323
|
+
} else {
|
|
324
|
+
instance.selectNodes([row.key]);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
const exportStateRef = useRef({ columns, rows: sortedRows, valueOf, neutralize });
|
|
328
|
+
exportStateRef.current = { columns, rows: sortedRows, valueOf, neutralize };
|
|
329
|
+
useImperativeHandle(
|
|
330
|
+
ref,
|
|
331
|
+
() => ({
|
|
332
|
+
exportCsv: () => {
|
|
333
|
+
const s = exportStateRef.current;
|
|
334
|
+
return toCsvString(
|
|
335
|
+
s.columns.map((col) => col.label ?? col.key),
|
|
336
|
+
s.rows.map((row) => s.columns.map((col) => s.valueOf(row, col.key))),
|
|
337
|
+
s.neutralize
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}),
|
|
341
|
+
[]
|
|
342
|
+
);
|
|
343
|
+
const total = sortedRows.length;
|
|
344
|
+
const first = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
|
|
345
|
+
const windowSize = Math.ceil(height / rowHeight) + overscan * 2;
|
|
346
|
+
const windowRows = sortedRows.slice(first, first + windowSize);
|
|
347
|
+
const onScroll = (e) => {
|
|
348
|
+
setScrollTop(e.currentTarget.scrollTop);
|
|
349
|
+
};
|
|
350
|
+
const cycleSort = (key) => {
|
|
351
|
+
setSort((prev) => {
|
|
352
|
+
if (prev === null || prev.key !== key) return { key, direction: "asc" };
|
|
353
|
+
if (prev.direction === "asc") return { key, direction: "desc" };
|
|
354
|
+
return null;
|
|
355
|
+
});
|
|
356
|
+
};
|
|
357
|
+
const onFilterChange = (text) => {
|
|
358
|
+
if (props.filterText === void 0) setInternalFilter(text);
|
|
359
|
+
props.onFilterTextChange?.(text);
|
|
360
|
+
};
|
|
361
|
+
const styled = props.className === void 0;
|
|
362
|
+
const rowStyled = props.rowClassName === void 0;
|
|
363
|
+
const label = props.label ?? "Graph table";
|
|
364
|
+
const renderRow = (row, index) => {
|
|
365
|
+
const selected = isSelected(row);
|
|
366
|
+
const node = row.edge === void 0 ? instance.getNode(row.key) : void 0;
|
|
367
|
+
const mechanics = {
|
|
368
|
+
height: rowHeight,
|
|
369
|
+
display: "flex",
|
|
370
|
+
alignItems: "center",
|
|
371
|
+
boxSizing: "border-box"
|
|
372
|
+
};
|
|
373
|
+
return /* @__PURE__ */ jsx(
|
|
374
|
+
"div",
|
|
375
|
+
{
|
|
376
|
+
role: "row",
|
|
377
|
+
"aria-rowindex": first + index + 2,
|
|
378
|
+
"aria-selected": selected,
|
|
379
|
+
"data-orbit-table-row": row.key,
|
|
380
|
+
tabIndex: 0,
|
|
381
|
+
className: props.rowClassName,
|
|
382
|
+
style: rowStyled ? { ...mechanics, ...selected ? ROW_SELECTED_STYLE : ROW_STYLE } : mechanics,
|
|
383
|
+
onClick: (e) => {
|
|
384
|
+
activateRow(row, e.metaKey || e.ctrlKey);
|
|
385
|
+
},
|
|
386
|
+
onKeyDown: (e) => {
|
|
387
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
388
|
+
e.preventDefault();
|
|
389
|
+
activateRow(row, e.metaKey || e.ctrlKey);
|
|
390
|
+
},
|
|
391
|
+
children: columns.map((col) => {
|
|
392
|
+
const value = valueOf(row, col.key);
|
|
393
|
+
const text = cellText(value);
|
|
394
|
+
const ctx = node !== void 0 ? { row, node, column: col, value, text } : { row, column: col, value, text };
|
|
395
|
+
return /* @__PURE__ */ jsx(
|
|
396
|
+
"div",
|
|
397
|
+
{
|
|
398
|
+
role: "cell",
|
|
399
|
+
"data-orbit-table-cell": col.key,
|
|
400
|
+
style: styled ? { ...CELL_MECHANICS, ...CELL_STYLE } : CELL_MECHANICS,
|
|
401
|
+
children: props.renderCell !== void 0 ? props.renderCell(ctx) : text
|
|
402
|
+
},
|
|
403
|
+
col.key
|
|
404
|
+
);
|
|
405
|
+
})
|
|
406
|
+
},
|
|
407
|
+
row.key
|
|
408
|
+
);
|
|
409
|
+
};
|
|
410
|
+
return /* @__PURE__ */ jsxs(
|
|
411
|
+
"div",
|
|
412
|
+
{
|
|
413
|
+
"data-orbit-table": mode,
|
|
414
|
+
role: "group",
|
|
415
|
+
"aria-label": label,
|
|
416
|
+
className: props.className,
|
|
417
|
+
style: styled ? { ...ROOT_STYLE, ...props.style } : props.style,
|
|
418
|
+
children: [
|
|
419
|
+
/* @__PURE__ */ jsxs("div", { "data-orbit-table-toolbar": "", style: styled ? TOOLBAR_STYLE : void 0, children: [
|
|
420
|
+
/* @__PURE__ */ jsx(
|
|
421
|
+
"input",
|
|
422
|
+
{
|
|
423
|
+
type: "text",
|
|
424
|
+
"data-orbit-table-filter": "",
|
|
425
|
+
"aria-label": "Filter rows",
|
|
426
|
+
value: filterText,
|
|
427
|
+
style: styled ? FILTER_STYLE : void 0,
|
|
428
|
+
onChange: (e) => {
|
|
429
|
+
onFilterChange(e.currentTarget.value);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
),
|
|
433
|
+
/* @__PURE__ */ jsx("span", { "data-orbit-table-count": "", style: styled ? COUNT_STYLE : void 0, children: `${total} of ${baseRows.length} rows` })
|
|
434
|
+
] }),
|
|
435
|
+
/* @__PURE__ */ jsxs("div", { role: "table", "aria-label": `${label} rows`, "aria-rowcount": total + 1, children: [
|
|
436
|
+
/* @__PURE__ */ jsx("div", { role: "rowgroup", children: /* @__PURE__ */ jsx(
|
|
437
|
+
"div",
|
|
438
|
+
{
|
|
439
|
+
role: "row",
|
|
440
|
+
"aria-rowindex": 1,
|
|
441
|
+
"data-orbit-table-head": "",
|
|
442
|
+
style: styled ? { display: "flex", ...HEADER_ROW_STYLE } : { display: "flex" },
|
|
443
|
+
children: columns.map((col) => {
|
|
444
|
+
const active = sort !== null && sort.key === col.key;
|
|
445
|
+
const ariaSort = active ? sort.direction === "asc" ? "ascending" : "descending" : "none";
|
|
446
|
+
return /* @__PURE__ */ jsx(
|
|
447
|
+
"div",
|
|
448
|
+
{
|
|
449
|
+
role: "columnheader",
|
|
450
|
+
"aria-sort": ariaSort,
|
|
451
|
+
"data-orbit-table-header": col.key,
|
|
452
|
+
style: CELL_MECHANICS,
|
|
453
|
+
children: /* @__PURE__ */ jsx(
|
|
454
|
+
"button",
|
|
455
|
+
{
|
|
456
|
+
type: "button",
|
|
457
|
+
"data-orbit-table-sort": col.key,
|
|
458
|
+
style: styled ? HEADER_BUTTON_STYLE : void 0,
|
|
459
|
+
onClick: () => {
|
|
460
|
+
cycleSort(col.key);
|
|
461
|
+
},
|
|
462
|
+
children: col.label ?? col.key
|
|
463
|
+
}
|
|
464
|
+
)
|
|
465
|
+
},
|
|
466
|
+
col.key
|
|
467
|
+
);
|
|
468
|
+
})
|
|
469
|
+
}
|
|
470
|
+
) }),
|
|
471
|
+
/* @__PURE__ */ jsx(
|
|
472
|
+
"div",
|
|
473
|
+
{
|
|
474
|
+
role: "rowgroup",
|
|
475
|
+
"data-orbit-table-viewport": "",
|
|
476
|
+
onScroll,
|
|
477
|
+
style: { height, overflowY: "auto", position: "relative" },
|
|
478
|
+
children: total === 0 ? /* @__PURE__ */ jsx("div", { "data-orbit-table-empty": "", style: styled ? EMPTY_STYLE : void 0, children: "No rows" }) : /* @__PURE__ */ jsx(
|
|
479
|
+
"div",
|
|
480
|
+
{
|
|
481
|
+
"data-orbit-table-spacer": "",
|
|
482
|
+
style: { height: total * rowHeight, position: "relative" },
|
|
483
|
+
children: /* @__PURE__ */ jsx(
|
|
484
|
+
"div",
|
|
485
|
+
{
|
|
486
|
+
"data-orbit-table-window": "",
|
|
487
|
+
style: { position: "absolute", top: first * rowHeight, left: 0, right: 0 },
|
|
488
|
+
children: windowRows.map((row, i) => renderRow(row, i))
|
|
489
|
+
}
|
|
490
|
+
)
|
|
491
|
+
}
|
|
492
|
+
)
|
|
493
|
+
}
|
|
494
|
+
)
|
|
495
|
+
] })
|
|
496
|
+
]
|
|
497
|
+
}
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
var GraphTable = forwardRef(GraphTableInner);
|
|
501
|
+
GraphTable.displayName = "GraphTable";
|
|
502
|
+
|
|
503
|
+
export { GraphTable, TABLE_FILTER_DIMENSION_DEFAULT, __ORBIT_TABLE_SENTINEL__ };
|
|
504
|
+
//# sourceMappingURL=Table.js.map
|
|
505
|
+
//# sourceMappingURL=Table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/Table/model.ts","../../src/components/Table/csv.ts","../../src/components/Table/index.tsx"],"names":[],"mappings":"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","file":"Table.js","sourcesContent":["/**\n * Pure row/column/sort/filter helpers for `<GraphTable>` (§16.10).\n * Framework-free so the §8 sort-coercion contract and the bounded column\n * derivation stay unit-testable without a DOM.\n */\n\nimport { coerceNumeric } from '@modernrelay/orbit-core';\nimport type { GraphEdge, GraphNode } from '@modernrelay/orbit-core';\n\nexport type GraphTableMode = 'nodes' | 'edges';\n\nexport interface GraphTableColumn {\n /** Column key: 'id', 'source'/'target' (edge mode), or an attr key. */\n key: string;\n /** Header text; defaults to the key. Rendered as a TEXT NODE (§14). */\n label?: string;\n}\n\nexport interface GraphTableSort {\n key: string;\n direction: 'asc' | 'desc';\n}\n\n/**\n * One table row. Node rows: `key === id === NodeId`. Edge rows carry the\n * caller edge; `id` is null when the caller supplied no edge id (such rows\n * render and export but cannot write edge selection — the core-synthesized\n * id is not observable through the public surface).\n */\nexport interface GraphTableRowRef {\n /** Stable render identity (node id, edge id, or a positional edge key). */\n key: string;\n /** Public id usable for selection writes; null = unaddressable edge row. */\n id: string | null;\n edge?: GraphEdge<any>;\n}\n\n/** Attr value → display TEXT (never markup): strings verbatim, numbers via\n * String, null/undefined empty, structured values JSON (cycles degrade to\n * the String form) — the Inspector's §14 convention. */\nexport function cellText(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n}\n\nexport function nodeCellValue(\n node: GraphNode<any> | undefined,\n id: string,\n key: string,\n): unknown {\n if (key === 'id') return id;\n const attrs = node?.attrs as Record<string, unknown> | undefined;\n return attrs?.[key];\n}\n\nexport function edgeCellValue(edge: GraphEdge<any>, key: string): unknown {\n if (key === 'id') return edge.id ?? '';\n if (key === 'source') return edge.source;\n if (key === 'target') return edge.target;\n const attrs = edge.attrs as Record<string, unknown> | undefined;\n return attrs?.[key];\n}\n\nexport function normalizeColumns(\n pick: readonly (string | GraphTableColumn)[],\n): readonly GraphTableColumn[] {\n return pick.map((c) => (typeof c === 'string' ? { key: c } : c));\n}\n\n/**\n * Derived columns: the identity keys for the mode ('id', plus\n * 'source'/'target' for edges) followed by the union of attr keys over the\n * SAMPLED PREFIX the caller passes (bounded — attrs seen only past the\n * sample do not create columns; use the `columns` prop to pin them).\n * First-seen key order.\n */\nexport function deriveColumns(\n mode: GraphTableMode,\n attrsSample: readonly (Record<string, unknown> | undefined)[],\n): readonly GraphTableColumn[] {\n const keys: string[] = mode === 'edges' ? ['id', 'source', 'target'] : ['id'];\n const seen = new Set(keys);\n for (const attrs of attrsSample) {\n if (attrs === undefined) continue;\n for (const key of Object.keys(attrs)) {\n if (!seen.has(key)) {\n seen.add(key);\n keys.push(key);\n }\n }\n }\n return keys.map((key) => ({ key }));\n}\n\n// ---------------------------------------------------------------------------\n// §8 sort coercion. Three tiers:\n// 0 numeric — values `coerceNumeric` admits (numbers, numeric strings);\n// 1 string — everything else with content (plain strings, booleans,\n// structured values by display text);\n// 2 null — null/undefined, non-finite numbers, empty strings, and the\n// §8 sentinel strings 'NaN'/'Infinity'/'±Infinity'.\n// Tier order is fixed (numeric block, then string block); `direction` flips\n// ordering WITHIN each tier; the null tier is ALWAYS LAST regardless of\n// direction. Ties preserve base row order (stable sort).\n// ---------------------------------------------------------------------------\n\nexport interface SortKey {\n tier: 0 | 1 | 2;\n num: number;\n str: string;\n}\n\nconst NULL_KEY: SortKey = { tier: 2, num: 0, str: '' };\n\n/** Case-insensitive §8 hygiene sentinels (mirrors `coerceNumeric`'s list). */\nfunction isSentinelString(value: string): boolean {\n const lower = value.trim().toLowerCase();\n return (\n lower === '' ||\n lower === 'nan' ||\n lower === 'infinity' ||\n lower === '-infinity' ||\n lower === '+infinity'\n );\n}\n\nexport function sortKeyOf(value: unknown): SortKey {\n if (value === null || value === undefined) return NULL_KEY;\n const num = coerceNumeric(value);\n if (num !== null) return { tier: 0, num, str: '' };\n if (typeof value === 'number') return NULL_KEY; // non-finite\n if (typeof value === 'string') {\n return isSentinelString(value) ? NULL_KEY : { tier: 1, num: 0, str: value };\n }\n return { tier: 1, num: 0, str: cellText(value) };\n}\n\nexport function compareSortKeys(a: SortKey, b: SortKey, direction: 1 | -1): number {\n if (a.tier !== b.tier) {\n if (a.tier === 2) return 1; // nulls last regardless of direction\n if (b.tier === 2) return -1;\n return a.tier - b.tier; // numeric block before string block, fixed\n }\n if (a.tier === 2) return 0;\n if (a.tier === 0) return direction * (a.num < b.num ? -1 : a.num > b.num ? 1 : 0);\n return direction * (a.str < b.str ? -1 : a.str > b.str ? 1 : 0);\n}\n\n/** Case-insensitive substring match over a row's rendered cell texts. */\nexport function textMatches(texts: readonly string[], queryLower: string): boolean {\n for (const text of texts) {\n if (text.toLowerCase().includes(queryLower)) return true;\n }\n return false;\n}\n","/**\n * RFC-4180 CSV serialization with formula-injection neutralization\n * (§16.10 R-16.10-04, S12-T11).\n *\n * Contract:\n * - records join with CRLF (no trailing record terminator);\n * - a cell is quoted iff its text contains a double quote, comma, CR, or\n * LF; embedded double quotes double;\n * - NEUTRALIZATION (default on): a NON-NUMERIC cell whose text begins with\n * `=`, `+`, `-`, `@`, TAB, or CR gets a leading apostrophe — attr values\n * are untrusted (§14/§19) and must never export as live spreadsheet\n * formulas. Cells whose RAW value is a finite number are exempt and\n * serialize as plain numbers (`-5` stays `-5`). Neutralization runs\n * BEFORE quoting, so a prefixed cell still quotes correctly.\n * - header cells are strings and follow the same rules (attr KEYS are\n * untrusted too).\n *\n * The documented opt-out (`neutralizeFormulas: false`) is for callers\n * exporting legitimate formulas; the default stays safe.\n */\n\nimport { cellText } from './model';\n\nconst FORMULA_LEADS = '=+-@\\t\\r';\n\nexport function csvCell(value: unknown, neutralize: boolean): string {\n const numericExempt = typeof value === 'number' && Number.isFinite(value);\n let text = cellText(value);\n if (neutralize && !numericExempt && text.length > 0 && FORMULA_LEADS.includes(text[0]!)) {\n text = `'${text}`;\n }\n if (/[\",\\r\\n]/.test(text)) {\n text = `\"${text.replaceAll('\"', '\"\"')}\"`;\n }\n return text;\n}\n\nexport function toCsvString(\n header: readonly string[],\n rows: readonly (readonly unknown[])[],\n neutralize: boolean,\n): string {\n const records: string[] = [header.map((h) => csvCell(h, neutralize)).join(',')];\n for (const row of rows) {\n records.push(row.map((cell) => csvCell(cell, neutralize)).join(','));\n }\n return records.join('\\r\\n');\n}\n","/**\n * `@modernrelay/orbit-react/components/Table` — `<GraphTable>`, the §16.10\n * virtualized crossfiltered tabular view (S12-T10/T11).\n *\n * Rows are nodes (default) or edges; columns derive from the union of attr\n * keys over a BOUNDED sampled prefix (`columnSample`, default 200) plus the\n * identity keys, or are picked explicitly via `columns`. Every cell renders\n * as a TEXT NODE (§14 untrusted content); `renderCell` replaces cell content.\n *\n * ## Virtualization\n * Hand-rolled windowing (no external dependency): a fixed-height scroll\n * viewport over a full-height spacer with an absolutely positioned window of\n * `ceil(height/rowHeight) + 2*overscan` rows — the mounted row count is\n * BOUNDED regardless of row count (100K rows mount ~30 DOM rows). Row\n * geometry is prop-driven (`rowHeight`, `height`), never measured, so the\n * math is deterministic under jsdom; class hooks drop decorative styles but\n * MECHANICAL styles (viewport height, spacer, window offset, row height,\n * cell flex) always apply — geometry changes go through the props.\n *\n * ## Bidirectional sync\n * - graph → table: rows follow `instance.getVisibleNodeIds()` (scope ∧ §9.1\n * mask) via a store subscription; brushes, filters, legend toggles, and\n * hide/show narrow the rows. An edge row lists iff BOTH endpoints are\n * visible (the public surface exposes no per-edge visibility read —\n * documented approximation).\n * - table → graph: the text/predicate filter registers through the §16.6\n * crossfilter session as a brush on `filterDimension` (default 'table') —\n * the host opts in by configuring an ID-KEYED categorical dimension\n * `{ key: filterDimension, kind: 'categorical', get: (n) => n.id }`; the\n * filter then writes `{ excluded: nonMatchingIds }` over the scene roster\n * (null when the filter clears / matches everything), narrowing the graph\n * AND every other crossfilter view through the standard §9.1 mask fast\n * path, with standard brush history coalescing. DEGRADATION is graceful\n * and documented: no session (host never set `crossfilter`), an unknown\n * dimension key, or edge mode filter rows LOCALLY only — the graph is\n * untouched. A dimension keyed by anything other than row ids degrades the\n * same way (unknown excluded keys mask nothing). Unmounting clears any\n * brush this table wrote.\n * - selection: row click (or Enter/Space) writes the §16.2 SelectionState\n * through `selectNodes`/`selectEdges` (replace; meta/ctrl toggles\n * membership), so controlled selection degrades to intent per §6.4;\n * selected rows carry `aria-selected=\"true\"`.\n *\n * ## Sorting (§8 coercion)\n * Header click cycles none → asc → desc → none. Values sort in three tiers —\n * numeric (`coerceNumeric`-admitted), string (everything else with content),\n * null (null/undefined, non-finite, empty, and the 'NaN'/'Infinity' sentinel\n * strings). Tier order is fixed; direction flips order within a tier; the\n * null tier is ALWAYS LAST regardless of direction.\n *\n * ## CSV export (T11)\n * `exportCsv()` on the forwarded ref returns an RFC-4180 string over the\n * CURRENT filtered + sorted rows and visible columns: CRLF records, quote\n * doubling, and formula-injection neutralization (see ./csv.ts). The\n * documented opt-out is `neutralizeFormulas={false}`.\n *\n * Edge-mode row sourcing (documented v0.10 compromise): the public instance\n * surface exposes no edge roster, so `mode=\"edges\"` reads rows from the\n * `edges` prop — the same array the host passed in its snapshot. Edge rows\n * without a caller-supplied id render and export but cannot write selection.\n */\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport type {\n CSSProperties,\n ForwardedRef,\n KeyboardEvent,\n MouseEvent,\n ReactElement,\n ReactNode,\n UIEvent,\n} from 'react';\nimport type {\n BrushState,\n GraphEdge,\n GraphNode,\n GraphStoreState,\n SelectionState,\n} from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { useResolvedInstance } from '../shared';\nimport {\n cellText,\n compareSortKeys,\n deriveColumns,\n edgeCellValue,\n nodeCellValue,\n normalizeColumns,\n sortKeyOf,\n textMatches,\n} from './model';\nimport type { GraphTableColumn, GraphTableMode, GraphTableRowRef, GraphTableSort } from './model';\nimport { toCsvString } from './csv';\n\nexport type { GraphTableColumn, GraphTableMode, GraphTableRowRef, GraphTableSort } from './model';\n\n/** S0-T02 tree-shake probe: importing `<Graph>` alone must not pull this. */\nexport const __ORBIT_TABLE_SENTINEL__ = 'orbit-react/components/Table';\n\nconst COLUMN_SAMPLE_DEFAULT = 200;\nconst ROW_HEIGHT_DEFAULT = 28;\nconst HEIGHT_DEFAULT = 320;\nconst OVERSCAN_DEFAULT = 8;\n/** Default crossfilter dimension key the table filter registers on. */\nexport const TABLE_FILTER_DIMENSION_DEFAULT = 'table';\n\nexport interface GraphTableCellContext {\n row: GraphTableRowRef;\n /** Resolved node for node rows (undefined for edge rows / unknown ids). */\n node?: GraphNode<any>;\n column: GraphTableColumn;\n value: unknown;\n /** The default display text (always safe to render as a text node). */\n text: string;\n}\n\nexport interface GraphTableHandle {\n /** RFC-4180 CSV over the current filtered+sorted rows (see module doc). */\n exportCsv(): string;\n}\n\nexport interface GraphTableProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Row source. Default 'nodes'. */\n mode?: GraphTableMode;\n /** Edge-mode rows (documented compromise — see module doc). */\n edges?: readonly GraphEdge<any>[];\n /** Explicit column pick; replaces derivation entirely. */\n columns?: readonly (string | GraphTableColumn)[];\n /** Bounded derivation prefix (rows sampled for the attr-key union). Default 200. */\n columnSample?: number;\n /** Controlled filter text; uncontrolled via the built-in input otherwise. */\n filterText?: string;\n onFilterTextChange?: (text: string) => void;\n /** Composed AND with the text filter; receives the underlying item. */\n filterPredicate?: (item: GraphNode<any> | GraphEdge<any>) => boolean;\n /** Crossfilter dimension key the filter registers on. Default 'table'. */\n filterDimension?: string;\n /** CSV formula-injection neutralization opt-out. Default true (safe). */\n neutralizeFormulas?: boolean;\n /** Row height in CSS px (mechanical — drives the window math). Default 28. */\n rowHeight?: number;\n /** Scroll viewport height in CSS px (mechanical). Default 320. */\n height?: number;\n /** Extra rows mounted on each side of the viewport. Default 8. */\n overscan?: number;\n /** Replaces default cell content (rendered inside the cell element). */\n renderCell?: (ctx: GraphTableCellContext) => ReactNode;\n /** Accessible name of the table. Default 'Graph table'. */\n label?: string;\n /** Class hook for the root; providing it drops the decorative styles. */\n className?: string;\n style?: CSSProperties;\n /** Class hook for body rows; providing it drops their decorative styles. */\n rowClassName?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Store subscriptions\n// ---------------------------------------------------------------------------\n\n/**\n * Version string that changes whenever the row model could: revisions,\n * visible counts, accepted counts, status, and session availability. The\n * session listener re-attaches on every store publication (the\n * _shared/crossfilter convention — engine rebuilds swap subscribers).\n */\nfunction useTableVersion(instance: AnyGraphInstance): string {\n const subscribe = useCallback(\n (onChange: () => void) => {\n let sessionUnsub: (() => void) | null = null;\n const attach = (): void => {\n sessionUnsub?.();\n sessionUnsub = null;\n const session = instance.getCrossfilterSession();\n if (session !== null) sessionUnsub = session.subscribe(onChange);\n };\n attach();\n const storeUnsub = instance.store.subscribe(() => {\n attach();\n onChange();\n });\n return () => {\n storeUnsub();\n sessionUnsub?.();\n sessionUnsub = null;\n };\n },\n [instance],\n );\n const getSnapshot = useCallback((): string => {\n const s: GraphStoreState = instance.store.getState();\n const hasSession = instance.getCrossfilterSession() !== null ? 1 : 0;\n return `${s.status}:${s.revisions.model}:${s.revisions.scope}:${s.revisions.render}:${s.visible.nodes}:${s.visible.edges}:${s.nodeCount}:${s.edgeCount}:${hasSession}`;\n }, [instance]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** The store keeps `selection` referentially stable across unrelated\n * publishes, so the raw field is a valid useSyncExternalStore snapshot. */\nfunction useSelection(instance: AnyGraphInstance): SelectionState {\n const subscribe = useCallback(\n (onChange: () => void) => instance.store.subscribe(onChange),\n [instance],\n );\n const getSnapshot = useCallback(\n (): SelectionState => instance.store.getState().selection,\n [instance],\n );\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n// ---------------------------------------------------------------------------\n// Styling defaults (decorative styles drop when the class hooks are given;\n// mechanical geometry always applies — see module doc).\n// ---------------------------------------------------------------------------\n\nconst ROOT_STYLE: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n padding: 8,\n borderRadius: 8,\n background: 'rgba(23, 25, 32, 0.96)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n color: '#e8eaf0',\n font: '12px/1.4 system-ui, sans-serif',\n};\nconst TOOLBAR_STYLE: CSSProperties = { display: 'flex', alignItems: 'center', gap: 8 };\nconst FILTER_STYLE: CSSProperties = {\n flex: 1,\n minWidth: 0,\n background: 'rgba(255, 255, 255, 0.06)',\n border: '1px solid rgba(255, 255, 255, 0.14)',\n borderRadius: 6,\n color: 'inherit',\n font: 'inherit',\n padding: '3px 8px',\n};\nconst COUNT_STYLE: CSSProperties = { opacity: 0.65, whiteSpace: 'nowrap' };\nconst HEADER_ROW_STYLE: CSSProperties = { borderBottom: '1px solid rgba(255, 255, 255, 0.2)' };\nconst HEADER_BUTTON_STYLE: CSSProperties = {\n appearance: 'none',\n background: 'transparent',\n border: 'none',\n color: 'inherit',\n cursor: 'pointer',\n font: 'inherit',\n fontWeight: 600,\n padding: '2px 4px',\n textAlign: 'left',\n width: '100%',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n};\nconst ROW_STYLE: CSSProperties = { cursor: 'pointer' };\nconst ROW_SELECTED_STYLE: CSSProperties = {\n ...ROW_STYLE,\n background: 'rgba(122, 162, 255, 0.22)',\n};\nconst CELL_STYLE: CSSProperties = {\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n padding: '0 4px',\n};\nconst EMPTY_STYLE: CSSProperties = { fontStyle: 'italic', opacity: 0.8, padding: 4 };\n\n/** Mechanical (always applied) flex geometry for header/body cells. */\nconst CELL_MECHANICS: CSSProperties = { flex: 1, minWidth: 0 };\n\nfunction GraphTableInner(\n props: GraphTableProps,\n ref: ForwardedRef<GraphTableHandle>,\n): ReactElement {\n const instance = useResolvedInstance(props.instance, '<GraphTable>');\n const version = useTableVersion(instance);\n const selection = useSelection(instance);\n\n const mode: GraphTableMode = props.mode ?? 'nodes';\n const rowHeight = props.rowHeight ?? ROW_HEIGHT_DEFAULT;\n const height = props.height ?? HEIGHT_DEFAULT;\n const overscan = props.overscan ?? OVERSCAN_DEFAULT;\n const dimensionKey = props.filterDimension ?? TABLE_FILTER_DIMENSION_DEFAULT;\n const neutralize = props.neutralizeFormulas ?? true;\n const predicate = props.filterPredicate;\n\n const [internalFilter, setInternalFilter] = useState('');\n const filterText = props.filterText ?? internalFilter;\n const query = filterText.trim().toLowerCase();\n const [sort, setSort] = useState<GraphTableSort | null>(null);\n const [scrollTop, setScrollTop] = useState(0);\n\n // --- row model -------------------------------------------------------------\n\n const edgesProp = props.edges;\n const baseRows = useMemo<readonly GraphTableRowRef[]>(() => {\n void version; // rebuild on every relevant store publication\n if (mode === 'edges') {\n const visible = new Set(instance.getVisibleNodeIds());\n const out: GraphTableRowRef[] = [];\n (edgesProp ?? []).forEach((edge, i) => {\n if (!visible.has(edge.source) || !visible.has(edge.target)) return;\n if (edge.id !== undefined) out.push({ key: edge.id, id: edge.id, edge });\n else out.push({ key: `${i}:${edge.source}→${edge.target}`, id: null, edge });\n });\n return out;\n }\n return instance.getVisibleNodeIds().map((id) => ({ key: id, id }));\n }, [instance, version, mode, edgesProp]);\n\n const columnSample = props.columnSample ?? COLUMN_SAMPLE_DEFAULT;\n const columnsProp = props.columns;\n const columns = useMemo<readonly GraphTableColumn[]>(() => {\n if (columnsProp !== undefined) return normalizeColumns(columnsProp);\n const sample = baseRows.slice(0, columnSample).map((row) => {\n return row.edge !== undefined\n ? (row.edge.attrs as Record<string, unknown> | undefined)\n : ((instance.getNode(row.key) as GraphNode<any> | undefined)?.attrs as\n | Record<string, unknown>\n | undefined);\n });\n return deriveColumns(mode, sample);\n }, [instance, baseRows, mode, columnsProp, columnSample]);\n\n const valueOf = useCallback(\n (row: GraphTableRowRef, key: string): unknown =>\n row.edge !== undefined\n ? edgeCellValue(row.edge, key)\n : nodeCellValue(instance.getNode(row.key) as GraphNode<any> | undefined, row.key, key),\n [instance],\n );\n\n /** Text/predicate match for one row over the visible columns. */\n const matchesRow = useCallback(\n (row: GraphTableRowRef): boolean => {\n if (predicate !== undefined) {\n const item =\n row.edge !== undefined\n ? row.edge\n : (instance.getNode(row.key) as GraphNode<any> | undefined);\n if (item === undefined || !predicate(item)) return false;\n }\n if (query === '') return true;\n return textMatches(\n columns.map((col) => cellText(valueOf(row, col.key))),\n query,\n );\n },\n [instance, predicate, query, columns, valueOf],\n );\n\n const filterActive = query !== '' || predicate !== undefined;\n const filteredRows = useMemo<readonly GraphTableRowRef[]>(\n () => (filterActive ? baseRows.filter(matchesRow) : baseRows),\n [baseRows, filterActive, matchesRow],\n );\n\n const sortedRows = useMemo<readonly GraphTableRowRef[]>(() => {\n if (sort === null || !columns.some((c) => c.key === sort.key)) return filteredRows;\n const direction: 1 | -1 = sort.direction === 'asc' ? 1 : -1;\n const decorated = filteredRows.map((row) => ({\n row,\n key: sortKeyOf(valueOf(row, sort.key)),\n }));\n decorated.sort((a, b) => compareSortKeys(a.key, b.key, direction)); // stable → base-order ties\n return decorated.map((d) => d.row);\n }, [filteredRows, sort, columns, valueOf]);\n\n // --- table → graph: crossfilter registration (see module doc) ---------------\n\n /** Set once this table wrote a non-null brush; unmount clears it. */\n const wroteBrushRef = useRef<{ instance: AnyGraphInstance; key: string } | null>(null);\n\n useEffect(() => {\n if (mode !== 'nodes') return;\n const session = instance.getCrossfilterSession();\n if (session === null) return;\n try {\n session.getBrush(dimensionKey);\n } catch {\n return; // dimension not configured — degrade to local-only filtering\n }\n let brush: BrushState = null;\n if (filterActive) {\n const excluded: string[] = [];\n for (const id of instance.getSceneNodeIds()) {\n if (!matchesRow({ key: id, id })) excluded.push(id);\n }\n if (excluded.length > 0) brush = { excluded };\n }\n if (brush !== null) wroteBrushRef.current = { instance, key: dimensionKey };\n // Deep-equal brushes no-op inside the engine, so re-runs are cheap.\n void session.setBrush(dimensionKey, brush).catch(() => undefined);\n }, [instance, version, mode, dimensionKey, filterActive, matchesRow]);\n\n useEffect(\n () => () => {\n const wrote = wroteBrushRef.current;\n if (wrote === null) return;\n wroteBrushRef.current = null;\n const session = wrote.instance.getCrossfilterSession();\n if (session !== null) void session.setBrush(wrote.key, null).catch(() => undefined);\n },\n [],\n );\n\n // --- selection ---------------------------------------------------------------\n\n const selectedNodes = useMemo(() => new Set(selection.nodeIds), [selection]);\n const selectedEdges = useMemo(() => new Set(selection.edgeIds), [selection]);\n const isSelected = (row: GraphTableRowRef): boolean =>\n row.edge !== undefined\n ? row.id !== null && selectedEdges.has(row.id)\n : selectedNodes.has(row.key);\n\n const activateRow = (row: GraphTableRowRef, additive: boolean): void => {\n if (row.edge !== undefined) {\n if (row.id === null) return; // unaddressable (no caller edge id)\n const id = row.id;\n if (additive) {\n const current = selection.edgeIds;\n instance.selectEdges(\n current.includes(id) ? current.filter((e) => e !== id) : [...current, id],\n );\n } else {\n instance.selectEdges([id]);\n }\n return;\n }\n if (additive) {\n const current = selection.nodeIds;\n instance.selectNodes(\n current.includes(row.key) ? current.filter((n) => n !== row.key) : [...current, row.key],\n );\n } else {\n instance.selectNodes([row.key]);\n }\n };\n\n // --- CSV export (T11) ----------------------------------------------------------\n\n const exportStateRef = useRef({ columns, rows: sortedRows, valueOf, neutralize });\n exportStateRef.current = { columns, rows: sortedRows, valueOf, neutralize };\n useImperativeHandle(\n ref,\n (): GraphTableHandle => ({\n exportCsv: (): string => {\n const s = exportStateRef.current;\n return toCsvString(\n s.columns.map((col) => col.label ?? col.key),\n s.rows.map((row) => s.columns.map((col) => s.valueOf(row, col.key))),\n s.neutralize,\n );\n },\n }),\n [],\n );\n\n // --- windowing -----------------------------------------------------------------\n\n const total = sortedRows.length;\n const first = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);\n const windowSize = Math.ceil(height / rowHeight) + overscan * 2;\n const windowRows = sortedRows.slice(first, first + windowSize);\n\n const onScroll = (e: UIEvent<HTMLDivElement>): void => {\n setScrollTop(e.currentTarget.scrollTop);\n };\n\n const cycleSort = (key: string): void => {\n setSort((prev) => {\n if (prev === null || prev.key !== key) return { key, direction: 'asc' };\n if (prev.direction === 'asc') return { key, direction: 'desc' };\n return null;\n });\n };\n\n const onFilterChange = (text: string): void => {\n if (props.filterText === undefined) setInternalFilter(text);\n props.onFilterTextChange?.(text);\n };\n\n const styled = props.className === undefined;\n const rowStyled = props.rowClassName === undefined;\n const label = props.label ?? 'Graph table';\n\n const renderRow = (row: GraphTableRowRef, index: number): ReactElement => {\n const selected = isSelected(row);\n const node =\n row.edge === undefined\n ? (instance.getNode(row.key) as GraphNode<any> | undefined)\n : undefined;\n const mechanics: CSSProperties = {\n height: rowHeight,\n display: 'flex',\n alignItems: 'center',\n boxSizing: 'border-box',\n };\n return (\n <div\n key={row.key}\n role=\"row\"\n aria-rowindex={first + index + 2}\n aria-selected={selected}\n data-orbit-table-row={row.key}\n tabIndex={0}\n className={props.rowClassName}\n style={\n rowStyled ? { ...mechanics, ...(selected ? ROW_SELECTED_STYLE : ROW_STYLE) } : mechanics\n }\n onClick={(e: MouseEvent<HTMLDivElement>) => {\n activateRow(row, e.metaKey || e.ctrlKey);\n }}\n onKeyDown={(e: KeyboardEvent<HTMLDivElement>) => {\n if (e.key !== 'Enter' && e.key !== ' ') return;\n e.preventDefault();\n activateRow(row, e.metaKey || e.ctrlKey);\n }}\n >\n {columns.map((col) => {\n const value = valueOf(row, col.key);\n const text = cellText(value);\n const ctx: GraphTableCellContext =\n node !== undefined\n ? { row, node, column: col, value, text }\n : { row, column: col, value, text };\n return (\n <div\n key={col.key}\n role=\"cell\"\n data-orbit-table-cell={col.key}\n style={styled ? { ...CELL_MECHANICS, ...CELL_STYLE } : CELL_MECHANICS}\n >\n {props.renderCell !== undefined ? props.renderCell(ctx) : text}\n </div>\n );\n })}\n </div>\n );\n };\n\n return (\n <div\n data-orbit-table={mode}\n role=\"group\"\n aria-label={label}\n className={props.className}\n style={styled ? { ...ROOT_STYLE, ...props.style } : props.style}\n >\n <div data-orbit-table-toolbar=\"\" style={styled ? TOOLBAR_STYLE : undefined}>\n <input\n type=\"text\"\n data-orbit-table-filter=\"\"\n aria-label=\"Filter rows\"\n value={filterText}\n style={styled ? FILTER_STYLE : undefined}\n onChange={(e) => {\n onFilterChange(e.currentTarget.value);\n }}\n />\n <span data-orbit-table-count=\"\" style={styled ? COUNT_STYLE : undefined}>\n {`${total} of ${baseRows.length} rows`}\n </span>\n </div>\n <div role=\"table\" aria-label={`${label} rows`} aria-rowcount={total + 1}>\n <div role=\"rowgroup\">\n <div\n role=\"row\"\n aria-rowindex={1}\n data-orbit-table-head=\"\"\n style={styled ? { display: 'flex', ...HEADER_ROW_STYLE } : { display: 'flex' }}\n >\n {columns.map((col) => {\n const active = sort !== null && sort.key === col.key;\n const ariaSort = active\n ? sort.direction === 'asc'\n ? 'ascending'\n : 'descending'\n : 'none';\n return (\n <div\n key={col.key}\n role=\"columnheader\"\n aria-sort={ariaSort}\n data-orbit-table-header={col.key}\n style={CELL_MECHANICS}\n >\n <button\n type=\"button\"\n data-orbit-table-sort={col.key}\n style={styled ? HEADER_BUTTON_STYLE : undefined}\n onClick={() => {\n cycleSort(col.key);\n }}\n >\n {col.label ?? col.key}\n </button>\n </div>\n );\n })}\n </div>\n </div>\n <div\n role=\"rowgroup\"\n data-orbit-table-viewport=\"\"\n onScroll={onScroll}\n style={{ height, overflowY: 'auto', position: 'relative' }}\n >\n {total === 0 ? (\n <div data-orbit-table-empty=\"\" style={styled ? EMPTY_STYLE : undefined}>\n No rows\n </div>\n ) : (\n <div\n data-orbit-table-spacer=\"\"\n style={{ height: total * rowHeight, position: 'relative' }}\n >\n <div\n data-orbit-table-window=\"\"\n style={{ position: 'absolute', top: first * rowHeight, left: 0, right: 0 }}\n >\n {windowRows.map((row, i) => renderRow(row, i))}\n </div>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n\nexport const GraphTable = forwardRef<GraphTableHandle, GraphTableProps>(GraphTableInner);\nGraphTable.displayName = 'GraphTable';\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CSSProperties, ReactElement } from 'react';
|
|
2
|
+
import { TimelinePlayback } from '@modernrelay/orbit-core';
|
|
3
|
+
import { A as AnyGraphInstance } from '../GraphProvider-B8ITDXS0.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* <GraphTimeline> — §16.6 timeline playback control (S9).
|
|
7
|
+
*
|
|
8
|
+
* A slim horizontal band over one numeric/temporal crossfilter dimension:
|
|
9
|
+
* the same pointer-drag brushing as the histogram (shared module), a
|
|
10
|
+
* play/pause button wired to instance.playTimeline/pauseTimeline (the button
|
|
11
|
+
* mirrors store `timeline.playingKey === dimension`), and a translucent
|
|
12
|
+
* current-window indicator. While PLAYING, a user drag pauses playback (the
|
|
13
|
+
* core folds the pause into the same brush publication). Headless-styleable:
|
|
14
|
+
* providing `className` drops the built-in dark-theme inline styles.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface GraphTimelineProps {
|
|
18
|
+
/** Crossfilter dimension key — numeric/temporal to be playable (§16.6). */
|
|
19
|
+
dimension: string;
|
|
20
|
+
/** Playback options forwarded to instance.playTimeline. */
|
|
21
|
+
playback?: Partial<TimelinePlayback>;
|
|
22
|
+
/** Band height in CSS px. Default 24. */
|
|
23
|
+
height?: number;
|
|
24
|
+
/** Explicit instance; defaults to the ambient <Graph>/<GraphProvider> one. */
|
|
25
|
+
instance?: AnyGraphInstance;
|
|
26
|
+
/** Class hook for the root; providing it drops the default styles. */
|
|
27
|
+
className?: string;
|
|
28
|
+
style?: CSSProperties;
|
|
29
|
+
/** Class hook for the play/pause button; providing it drops its defaults. */
|
|
30
|
+
buttonClassName?: string;
|
|
31
|
+
}
|
|
32
|
+
declare function GraphTimeline(props: GraphTimelineProps): ReactElement;
|
|
33
|
+
|
|
34
|
+
export { GraphTimeline, type GraphTimelineProps };
|