@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,221 @@
|
|
|
1
|
+
import { useResolvedInstance, mergeStyle, cornerStyle } from '../chunk-7XB47YST.js';
|
|
2
|
+
import { useId, useState, useRef, useCallback, useSyncExternalStore, Fragment } from 'react';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var SIM_FIELDS = [
|
|
6
|
+
{ key: "gravity", label: "Gravity", min: 0, max: 1, step: 0.01, fallback: 0.25 },
|
|
7
|
+
{ key: "repulsion", label: "Repulsion", min: 0, max: 2, step: 0.01, fallback: 1 },
|
|
8
|
+
{ key: "friction", label: "Friction", min: 0, max: 1, step: 0.01, fallback: 0.85 },
|
|
9
|
+
{ key: "linkDistance", label: "Link distance", min: 1, max: 100, step: 1, fallback: 10 },
|
|
10
|
+
{ key: "linkSpring", label: "Link spring", min: 0, max: 2, step: 0.01, fallback: 1 },
|
|
11
|
+
// v0.10.2: the rest of the §10 tunable set. `collision` and `center` ship
|
|
12
|
+
// OFF at the engine default, so their sliders start at 0 — moving one is
|
|
13
|
+
// what turns the force on.
|
|
14
|
+
{ key: "collision", label: "Collision", min: 0, max: 2, step: 0.01, fallback: 0 },
|
|
15
|
+
{ key: "decay", label: "Cool-down", min: 100, max: 2e4, step: 100, fallback: 5e3 },
|
|
16
|
+
{ key: "repulsionTheta", label: "Repulsion \u03B8", min: 0.3, max: 2, step: 0.05, fallback: 1.15 },
|
|
17
|
+
{ key: "center", label: "Centering", min: 0, max: 1, step: 0.01, fallback: 0 }
|
|
18
|
+
];
|
|
19
|
+
var SPEED_NORMAL_DECAY = 5e3;
|
|
20
|
+
var SPEED_FAST_DECAY = 1e3;
|
|
21
|
+
function stepDecimals(step) {
|
|
22
|
+
const s = String(step);
|
|
23
|
+
const dot = s.indexOf(".");
|
|
24
|
+
return dot === -1 ? 0 : s.length - dot - 1;
|
|
25
|
+
}
|
|
26
|
+
function quantize(field, raw) {
|
|
27
|
+
const clamped = Math.min(field.max, Math.max(field.min, raw));
|
|
28
|
+
return Number(clamped.toFixed(stepDecimals(field.step)));
|
|
29
|
+
}
|
|
30
|
+
function keyboardTarget(field, value, key) {
|
|
31
|
+
switch (key) {
|
|
32
|
+
case "ArrowRight":
|
|
33
|
+
case "ArrowUp":
|
|
34
|
+
return quantize(field, value + field.step);
|
|
35
|
+
case "ArrowLeft":
|
|
36
|
+
case "ArrowDown":
|
|
37
|
+
return quantize(field, value - field.step);
|
|
38
|
+
case "PageUp":
|
|
39
|
+
return quantize(field, value + field.step * 10);
|
|
40
|
+
case "PageDown":
|
|
41
|
+
return quantize(field, value - field.step * 10);
|
|
42
|
+
case "Home":
|
|
43
|
+
return field.min;
|
|
44
|
+
case "End":
|
|
45
|
+
return field.max;
|
|
46
|
+
default:
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
var PANEL_STYLE = {
|
|
51
|
+
width: 250,
|
|
52
|
+
pointerEvents: "auto",
|
|
53
|
+
display: "flex",
|
|
54
|
+
flexDirection: "column",
|
|
55
|
+
gap: 6,
|
|
56
|
+
padding: 10,
|
|
57
|
+
borderRadius: 8,
|
|
58
|
+
background: "rgba(23, 25, 32, 0.96)",
|
|
59
|
+
border: "1px solid rgba(255, 255, 255, 0.14)",
|
|
60
|
+
color: "#e8eaf0",
|
|
61
|
+
font: "12px/1.4 system-ui, sans-serif"
|
|
62
|
+
};
|
|
63
|
+
var TITLE_STYLE = { fontWeight: 600, fontSize: 13 };
|
|
64
|
+
var ROW_STYLE = { display: "flex", alignItems: "center", gap: 6 };
|
|
65
|
+
var LABEL_STYLE = { flex: "none", width: 86, opacity: 0.8 };
|
|
66
|
+
var SLIDER_STYLE = { flex: 1, minWidth: 0 };
|
|
67
|
+
var VALUE_STYLE = {
|
|
68
|
+
flex: "none",
|
|
69
|
+
width: 38,
|
|
70
|
+
textAlign: "right",
|
|
71
|
+
fontVariantNumeric: "tabular-nums"
|
|
72
|
+
};
|
|
73
|
+
var BUTTON_STYLE = {
|
|
74
|
+
appearance: "none",
|
|
75
|
+
background: "transparent",
|
|
76
|
+
border: "1px solid rgba(255, 255, 255, 0.2)",
|
|
77
|
+
borderRadius: 6,
|
|
78
|
+
color: "inherit",
|
|
79
|
+
cursor: "pointer",
|
|
80
|
+
font: "inherit",
|
|
81
|
+
padding: "4px 10px"
|
|
82
|
+
};
|
|
83
|
+
var STATUS_STYLE = { opacity: 0.65 };
|
|
84
|
+
function GraphSimControls(props) {
|
|
85
|
+
const instance = useResolvedInstance(props.instance, "<GraphSimControls>");
|
|
86
|
+
const baseId = useId();
|
|
87
|
+
const [mirror, setMirror] = useState(() => props.simulation ?? {});
|
|
88
|
+
const [seededFrom, setSeededFrom] = useState(props.simulation);
|
|
89
|
+
if (seededFrom !== props.simulation) {
|
|
90
|
+
setSeededFrom(props.simulation);
|
|
91
|
+
setMirror(props.simulation ?? {});
|
|
92
|
+
}
|
|
93
|
+
const preSpeedDecayRef = useRef(void 0);
|
|
94
|
+
const subscribe = useCallback(
|
|
95
|
+
(onStoreChange) => instance.store.subscribe(onStoreChange),
|
|
96
|
+
[instance]
|
|
97
|
+
);
|
|
98
|
+
const getRunning = useCallback(
|
|
99
|
+
() => instance.store.getState().simulationRunning,
|
|
100
|
+
[instance]
|
|
101
|
+
);
|
|
102
|
+
const simulationRunning = useSyncExternalStore(subscribe, getRunning, getRunning);
|
|
103
|
+
if ((props.layout ?? "force") !== "force") return null;
|
|
104
|
+
const write = (next) => {
|
|
105
|
+
setMirror(next);
|
|
106
|
+
instance.applyHostUpdate({ simulation: next });
|
|
107
|
+
props.onSimulationChange?.(next);
|
|
108
|
+
};
|
|
109
|
+
const setField = (key, value) => {
|
|
110
|
+
const next = { ...mirror };
|
|
111
|
+
next[key] = value;
|
|
112
|
+
write(next);
|
|
113
|
+
};
|
|
114
|
+
const speedOn = mirror.decay === SPEED_FAST_DECAY;
|
|
115
|
+
const toggleSpeed = () => {
|
|
116
|
+
if (speedOn) {
|
|
117
|
+
const restored = preSpeedDecayRef.current ?? SPEED_NORMAL_DECAY;
|
|
118
|
+
preSpeedDecayRef.current = void 0;
|
|
119
|
+
setField("decay", restored);
|
|
120
|
+
} else {
|
|
121
|
+
preSpeedDecayRef.current = mirror.decay;
|
|
122
|
+
setField("decay", SPEED_FAST_DECAY);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const panelLabel = props.label ?? "Simulation controls";
|
|
126
|
+
const speedId = `${baseId}-speed`;
|
|
127
|
+
return /* @__PURE__ */ jsxs(
|
|
128
|
+
"div",
|
|
129
|
+
{
|
|
130
|
+
"data-orbit-simcontrols": "",
|
|
131
|
+
role: "group",
|
|
132
|
+
"aria-label": panelLabel,
|
|
133
|
+
className: props.className,
|
|
134
|
+
style: props.className !== void 0 ? props.style : mergeStyle(
|
|
135
|
+
{ ...cornerStyle(props.position ?? "bottom-left", props.offset), ...PANEL_STYLE },
|
|
136
|
+
props.style
|
|
137
|
+
),
|
|
138
|
+
children: [
|
|
139
|
+
/* @__PURE__ */ jsx("div", { "data-orbit-simcontrols-title": "", style: TITLE_STYLE, children: panelLabel }),
|
|
140
|
+
SIM_FIELDS.map((field) => {
|
|
141
|
+
const value = mirror[field.key] ?? field.fallback;
|
|
142
|
+
const inputId = `${baseId}-${field.key}`;
|
|
143
|
+
const set = (v) => {
|
|
144
|
+
setField(field.key, quantize(field, v));
|
|
145
|
+
};
|
|
146
|
+
if (props.renderField !== void 0) {
|
|
147
|
+
return /* @__PURE__ */ jsx(Fragment, { children: props.renderField({ field, value, inputId, set }) }, field.key);
|
|
148
|
+
}
|
|
149
|
+
return /* @__PURE__ */ jsxs("div", { "data-orbit-simcontrols-field": field.key, style: ROW_STYLE, children: [
|
|
150
|
+
/* @__PURE__ */ jsx("label", { htmlFor: inputId, style: LABEL_STYLE, children: field.label }),
|
|
151
|
+
/* @__PURE__ */ jsx(
|
|
152
|
+
"input",
|
|
153
|
+
{
|
|
154
|
+
id: inputId,
|
|
155
|
+
type: "range",
|
|
156
|
+
"data-orbit-simcontrols-input": field.key,
|
|
157
|
+
min: field.min,
|
|
158
|
+
max: field.max,
|
|
159
|
+
step: field.step,
|
|
160
|
+
value,
|
|
161
|
+
onChange: (e) => {
|
|
162
|
+
const v = Number(e.target.value);
|
|
163
|
+
if (Number.isFinite(v)) set(v);
|
|
164
|
+
},
|
|
165
|
+
onKeyDown: (e) => {
|
|
166
|
+
const target = keyboardTarget(field, value, e.key);
|
|
167
|
+
if (target === null) return;
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
if (target !== value) setField(field.key, target);
|
|
170
|
+
},
|
|
171
|
+
style: SLIDER_STYLE
|
|
172
|
+
}
|
|
173
|
+
),
|
|
174
|
+
/* @__PURE__ */ jsx(
|
|
175
|
+
"output",
|
|
176
|
+
{
|
|
177
|
+
htmlFor: inputId,
|
|
178
|
+
"data-orbit-simcontrols-value": field.key,
|
|
179
|
+
style: VALUE_STYLE,
|
|
180
|
+
children: String(value)
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
] }, field.key);
|
|
184
|
+
}),
|
|
185
|
+
/* @__PURE__ */ jsxs("div", { "data-orbit-simcontrols-speed-row": "", style: ROW_STYLE, children: [
|
|
186
|
+
/* @__PURE__ */ jsx(
|
|
187
|
+
"input",
|
|
188
|
+
{
|
|
189
|
+
id: speedId,
|
|
190
|
+
type: "checkbox",
|
|
191
|
+
"data-orbit-simcontrols-speed": "",
|
|
192
|
+
checked: speedOn,
|
|
193
|
+
onChange: toggleSpeed
|
|
194
|
+
}
|
|
195
|
+
),
|
|
196
|
+
/* @__PURE__ */ jsx("label", { htmlFor: speedId, children: "Speed boost" })
|
|
197
|
+
] }),
|
|
198
|
+
/* @__PURE__ */ jsxs("div", { style: ROW_STYLE, children: [
|
|
199
|
+
/* @__PURE__ */ jsx(
|
|
200
|
+
"button",
|
|
201
|
+
{
|
|
202
|
+
type: "button",
|
|
203
|
+
"data-orbit-simcontrols-reheat": "",
|
|
204
|
+
style: BUTTON_STYLE,
|
|
205
|
+
onClick: () => {
|
|
206
|
+
instance.resumeSimulation();
|
|
207
|
+
},
|
|
208
|
+
children: "Reheat"
|
|
209
|
+
}
|
|
210
|
+
),
|
|
211
|
+
/* @__PURE__ */ jsx("span", { "data-orbit-simcontrols-status": "", style: STATUS_STYLE, children: simulationRunning ? "simulation running" : "settled" })
|
|
212
|
+
] })
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
var __ORBIT_SIMCONTROLS_SENTINEL__ = "orbit-react/components/SimControls";
|
|
218
|
+
|
|
219
|
+
export { GraphSimControls, SIM_FIELDS, SPEED_FAST_DECAY, SPEED_NORMAL_DECAY, __ORBIT_SIMCONTROLS_SENTINEL__ };
|
|
220
|
+
//# sourceMappingURL=SimControls.js.map
|
|
221
|
+
//# sourceMappingURL=SimControls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/SimControls/index.tsx"],"names":[],"mappings":";;;;AAkFO,IAAM,UAAA,GAA4C;AAAA,EACvD,EAAE,GAAA,EAAK,SAAA,EAAW,KAAA,EAAO,SAAA,EAAW,GAAA,EAAK,CAAA,EAAG,GAAA,EAAK,CAAA,EAAG,IAAA,EAAM,IAAA,EAAM,QAAA,EAAU,IAAA,EAAK;AAAA,EAC/E,EAAE,GAAA,EAAK,WAAA,EAAa,KAAA,EAAO,WAAA,EAAa,GAAA,EAAK,CAAA,EAAG,GAAA,EAAK,CAAA,EAAG,IAAA,EAAM,IAAA,EAAM,QAAA,EAAU,CAAA,EAAE;AAAA,EAChF,EAAE,GAAA,EAAK,UAAA,EAAY,KAAA,EAAO,UAAA,EAAY,GAAA,EAAK,CAAA,EAAG,GAAA,EAAK,CAAA,EAAG,IAAA,EAAM,IAAA,EAAM,QAAA,EAAU,IAAA,EAAK;AAAA,EACjF,EAAE,GAAA,EAAK,cAAA,EAAgB,KAAA,EAAO,eAAA,EAAiB,GAAA,EAAK,CAAA,EAAG,GAAA,EAAK,GAAA,EAAK,IAAA,EAAM,CAAA,EAAG,QAAA,EAAU,EAAA,EAAG;AAAA,EACvF,EAAE,GAAA,EAAK,YAAA,EAAc,KAAA,EAAO,aAAA,EAAe,GAAA,EAAK,CAAA,EAAG,GAAA,EAAK,CAAA,EAAG,IAAA,EAAM,IAAA,EAAM,QAAA,EAAU,CAAA,EAAE;AAAA;AAAA;AAAA;AAAA,EAInF,EAAE,GAAA,EAAK,WAAA,EAAa,KAAA,EAAO,WAAA,EAAa,GAAA,EAAK,CAAA,EAAG,GAAA,EAAK,CAAA,EAAG,IAAA,EAAM,IAAA,EAAM,QAAA,EAAU,CAAA,EAAE;AAAA,EAChF,EAAE,GAAA,EAAK,OAAA,EAAS,KAAA,EAAO,WAAA,EAAa,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,GAAA,EAAO,IAAA,EAAM,GAAA,EAAK,QAAA,EAAU,GAAA,EAAK;AAAA,EACpF,EAAE,GAAA,EAAK,gBAAA,EAAkB,KAAA,EAAO,kBAAA,EAAe,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,CAAA,EAAG,IAAA,EAAM,IAAA,EAAM,QAAA,EAAU,IAAA,EAAK;AAAA,EAC5F,EAAE,GAAA,EAAK,QAAA,EAAU,KAAA,EAAO,WAAA,EAAa,GAAA,EAAK,CAAA,EAAG,GAAA,EAAK,CAAA,EAAG,IAAA,EAAM,IAAA,EAAM,QAAA,EAAU,CAAA;AAC7E;AAIO,IAAM,kBAAA,GAAqB;AAG3B,IAAM,gBAAA,GAAmB;AAsChC,SAAS,aAAa,IAAA,EAAsB;AAC1C,EAAA,MAAM,CAAA,GAAI,OAAO,IAAI,CAAA;AACrB,EAAA,MAAM,GAAA,GAAM,CAAA,CAAE,OAAA,CAAQ,GAAG,CAAA;AACzB,EAAA,OAAO,GAAA,KAAQ,EAAA,GAAK,CAAA,GAAI,CAAA,CAAE,SAAS,GAAA,GAAM,CAAA;AAC3C;AAIA,SAAS,QAAA,CAAS,OAA2B,GAAA,EAAqB;AAChE,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,GAAA,EAAK,KAAK,GAAA,CAAI,KAAA,CAAM,GAAA,EAAK,GAAG,CAAC,CAAA;AAC5D,EAAA,OAAO,OAAO,OAAA,CAAQ,OAAA,CAAQ,aAAa,KAAA,CAAM,IAAI,CAAC,CAAC,CAAA;AACzD;AAGA,SAAS,cAAA,CAAe,KAAA,EAA2B,KAAA,EAAe,GAAA,EAA4B;AAC5F,EAAA,QAAQ,GAAA;AAAK,IACX,KAAK,YAAA;AAAA,IACL,KAAK,SAAA;AACH,MAAA,OAAO,QAAA,CAAS,KAAA,EAAO,KAAA,GAAQ,KAAA,CAAM,IAAI,CAAA;AAAA,IAC3C,KAAK,WAAA;AAAA,IACL,KAAK,WAAA;AACH,MAAA,OAAO,QAAA,CAAS,KAAA,EAAO,KAAA,GAAQ,KAAA,CAAM,IAAI,CAAA;AAAA,IAC3C,KAAK,QAAA;AACH,MAAA,OAAO,QAAA,CAAS,KAAA,EAAO,KAAA,GAAQ,KAAA,CAAM,OAAO,EAAE,CAAA;AAAA,IAChD,KAAK,UAAA;AACH,MAAA,OAAO,QAAA,CAAS,KAAA,EAAO,KAAA,GAAQ,KAAA,CAAM,OAAO,EAAE,CAAA;AAAA,IAChD,KAAK,MAAA;AACH,MAAA,OAAO,KAAA,CAAM,GAAA;AAAA,IACf,KAAK,KAAA;AACH,MAAA,OAAO,KAAA,CAAM,GAAA;AAAA,IACf;AACE,MAAA,OAAO,IAAA;AAAA;AAEb;AAGA,IAAM,WAAA,GAA6B;AAAA,EACjC,KAAA,EAAO,GAAA;AAAA,EACP,aAAA,EAAe,MAAA;AAAA,EACf,OAAA,EAAS,MAAA;AAAA,EACT,aAAA,EAAe,QAAA;AAAA,EACf,GAAA,EAAK,CAAA;AAAA,EACL,OAAA,EAAS,EAAA;AAAA,EACT,YAAA,EAAc,CAAA;AAAA,EACd,UAAA,EAAY,wBAAA;AAAA,EACZ,MAAA,EAAQ,qCAAA;AAAA,EACR,KAAA,EAAO,SAAA;AAAA,EACP,IAAA,EAAM;AACR,CAAA;AACA,IAAM,WAAA,GAA6B,EAAE,UAAA,EAAY,GAAA,EAAK,UAAU,EAAA,EAAG;AACnE,IAAM,YAA2B,EAAE,OAAA,EAAS,QAAQ,UAAA,EAAY,QAAA,EAAU,KAAK,CAAA,EAAE;AACjF,IAAM,cAA6B,EAAE,IAAA,EAAM,QAAQ,KAAA,EAAO,EAAA,EAAI,SAAS,GAAA,EAAI;AAC3E,IAAM,YAAA,GAA8B,EAAE,IAAA,EAAM,CAAA,EAAG,UAAU,CAAA,EAAE;AAC3D,IAAM,WAAA,GAA6B;AAAA,EACjC,IAAA,EAAM,MAAA;AAAA,EACN,KAAA,EAAO,EAAA;AAAA,EACP,SAAA,EAAW,OAAA;AAAA,EACX,kBAAA,EAAoB;AACtB,CAAA;AACA,IAAM,YAAA,GAA8B;AAAA,EAClC,UAAA,EAAY,MAAA;AAAA,EACZ,UAAA,EAAY,aAAA;AAAA,EACZ,MAAA,EAAQ,oCAAA;AAAA,EACR,YAAA,EAAc,CAAA;AAAA,EACd,KAAA,EAAO,SAAA;AAAA,EACP,MAAA,EAAQ,SAAA;AAAA,EACR,IAAA,EAAM,SAAA;AAAA,EACN,OAAA,EAAS;AACX,CAAA;AACA,IAAM,YAAA,GAA8B,EAAE,OAAA,EAAS,IAAA,EAAK;AAE7C,SAAS,iBAAiB,KAAA,EAAmD;AAClF,EAAA,MAAM,QAAA,GAAW,mBAAA,CAAoB,KAAA,CAAM,QAAA,EAAU,oBAAoB,CAAA;AACzE,EAAA,MAAM,SAAS,KAAA,EAAM;AAKrB,EAAA,MAAM,CAAC,QAAQ,SAAS,CAAA,GAAI,SAA2B,MAAM,KAAA,CAAM,UAAA,IAAc,EAAE,CAAA;AACnF,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,QAAA,CAAS,MAAM,UAAU,CAAA;AAC7D,EAAA,IAAI,UAAA,KAAe,MAAM,UAAA,EAAY;AACnC,IAAA,aAAA,CAAc,MAAM,UAAU,CAAA;AAC9B,IAAA,SAAA,CAAU,KAAA,CAAM,UAAA,IAAc,EAAE,CAAA;AAAA,EAClC;AAIA,EAAA,MAAM,gBAAA,GAAmB,OAA2B,MAAS,CAAA;AAG7D,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,aAAA,KAA8B,QAAA,CAAS,KAAA,CAAM,UAAU,aAAa,CAAA;AAAA,IACrE,CAAC,QAAQ;AAAA,GACX;AACA,EAAA,MAAM,UAAA,GAAa,WAAA;AAAA,IACjB,MAAM,QAAA,CAAS,KAAA,CAAM,QAAA,EAAS,CAAE,iBAAA;AAAA,IAChC,CAAC,QAAQ;AAAA,GACX;AACA,EAAA,MAAM,iBAAA,GAAoB,oBAAA,CAAqB,SAAA,EAAW,UAAA,EAAY,UAAU,CAAA;AAGhF,EAAA,IAAA,CAAK,KAAA,CAAM,MAAA,IAAU,OAAA,MAAa,OAAA,EAAS,OAAO,IAAA;AAGlD,EAAA,MAAM,KAAA,GAAQ,CAAC,IAAA,KAAiC;AAC9C,IAAA,SAAA,CAAU,IAAI,CAAA;AACd,IAAA,QAAA,CAAS,eAAA,CAAgB,EAAE,UAAA,EAAY,IAAA,EAAM,CAAA;AAC7C,IAAA,KAAA,CAAM,qBAAqB,IAAI,CAAA;AAAA,EACjC,CAAA;AAEA,EAAA,MAAM,QAAA,GAAW,CAAC,GAAA,EAAkB,KAAA,KAAwB;AAC1D,IAAA,MAAM,IAAA,GAAyB,EAAE,GAAG,MAAA,EAAO;AAC3C,IAAA,IAAA,CAAK,GAAG,CAAA,GAAI,KAAA;AACZ,IAAA,KAAA,CAAM,IAAI,CAAA;AAAA,EACZ,CAAA;AAIA,EAAA,MAAM,OAAA,GAAU,OAAO,KAAA,KAAU,gBAAA;AACjC,EAAA,MAAM,cAAc,MAAY;AAC9B,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,MAAM,QAAA,GAAW,iBAAiB,OAAA,IAAW,kBAAA;AAC7C,MAAA,gBAAA,CAAiB,OAAA,GAAU,MAAA;AAC3B,MAAA,QAAA,CAAS,SAAS,QAAQ,CAAA;AAAA,IAC5B,CAAA,MAAO;AACL,MAAA,gBAAA,CAAiB,UAAU,MAAA,CAAO,KAAA;AAClC,MAAA,QAAA,CAAS,SAAS,gBAAgB,CAAA;AAAA,IACpC;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,UAAA,GAAa,MAAM,KAAA,IAAS,qBAAA;AAClC,EAAA,MAAM,OAAA,GAAU,GAAG,MAAM,CAAA,MAAA,CAAA;AAEzB,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,wBAAA,EAAuB,EAAA;AAAA,MACvB,IAAA,EAAK,OAAA;AAAA,MACL,YAAA,EAAY,UAAA;AAAA,MACZ,WAAW,KAAA,CAAM,SAAA;AAAA,MACjB,KAAA,EACE,KAAA,CAAM,SAAA,KAAc,MAAA,GAChB,MAAM,KAAA,GACN,UAAA;AAAA,QACE,EAAE,GAAG,WAAA,CAAY,KAAA,CAAM,QAAA,IAAY,eAAe,KAAA,CAAM,MAAM,CAAA,EAAG,GAAG,WAAA,EAAY;AAAA,QAChF,KAAA,CAAM;AAAA,OACR;AAAA,MAGN,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,8BAAA,EAA6B,EAAA,EAAG,KAAA,EAAO,aACzC,QAAA,EAAA,UAAA,EACH,CAAA;AAAA,QAEC,UAAA,CAAW,GAAA,CAAI,CAAC,KAAA,KAAU;AACzB,UAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,GAAG,KAAK,KAAA,CAAM,QAAA;AACzC,UAAA,MAAM,OAAA,GAAU,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,MAAM,GAAG,CAAA,CAAA;AACtC,UAAA,MAAM,GAAA,GAAM,CAAC,CAAA,KAAoB;AAC/B,YAAA,QAAA,CAAS,KAAA,CAAM,GAAA,EAAK,QAAA,CAAS,KAAA,EAAO,CAAC,CAAC,CAAA;AAAA,UACxC,CAAA;AACA,UAAA,IAAI,KAAA,CAAM,gBAAgB,MAAA,EAAW;AACnC,YAAA,uBACE,GAAA,CAAC,QAAA,EAAA,EACE,QAAA,EAAA,KAAA,CAAM,WAAA,CAAY,EAAE,KAAA,EAAO,KAAA,EAAO,OAAA,EAAS,GAAA,EAAK,CAAA,EAAA,EADpC,KAAA,CAAM,GAErB,CAAA;AAAA,UAEJ;AACA,UAAA,4BACG,KAAA,EAAA,EAAoB,8BAAA,EAA8B,KAAA,CAAM,GAAA,EAAK,OAAO,SAAA,EACnE,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,WAAM,OAAA,EAAS,OAAA,EAAS,KAAA,EAAO,WAAA,EAC7B,gBAAM,KAAA,EACT,CAAA;AAAA,4BACA,GAAA;AAAA,cAAC,OAAA;AAAA,cAAA;AAAA,gBACC,EAAA,EAAI,OAAA;AAAA,gBACJ,IAAA,EAAK,OAAA;AAAA,gBACL,gCAA8B,KAAA,CAAM,GAAA;AAAA,gBACpC,KAAK,KAAA,CAAM,GAAA;AAAA,gBACX,KAAK,KAAA,CAAM,GAAA;AAAA,gBACX,MAAM,KAAA,CAAM,IAAA;AAAA,gBACZ,KAAA;AAAA,gBACA,QAAA,EAAU,CAAC,CAAA,KAAM;AACf,kBAAA,MAAM,CAAA,GAAI,MAAA,CAAO,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA;AAC/B,kBAAA,IAAI,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,MAAO,CAAC,CAAA;AAAA,gBAC/B,CAAA;AAAA,gBACA,SAAA,EAAW,CAAC,CAAA,KAAM;AAChB,kBAAA,MAAM,MAAA,GAAS,cAAA,CAAe,KAAA,EAAO,KAAA,EAAO,EAAE,GAAG,CAAA;AACjD,kBAAA,IAAI,WAAW,IAAA,EAAM;AAGrB,kBAAA,CAAA,CAAE,cAAA,EAAe;AACjB,kBAAA,IAAI,MAAA,KAAW,KAAA,EAAO,QAAA,CAAS,KAAA,CAAM,KAAK,MAAM,CAAA;AAAA,gBAClD,CAAA;AAAA,gBACA,KAAA,EAAO;AAAA;AAAA,aACT;AAAA,4BACA,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,OAAA,EAAS,OAAA;AAAA,gBACT,gCAA8B,KAAA,CAAM,GAAA;AAAA,gBACpC,KAAA,EAAO,WAAA;AAAA,gBAEN,iBAAO,KAAK;AAAA;AAAA;AACf,WAAA,EAAA,EAhCQ,MAAM,GAiChB,CAAA;AAAA,QAEJ,CAAC,CAAA;AAAA,wBAED,IAAA,CAAC,KAAA,EAAA,EAAI,kCAAA,EAAiC,EAAA,EAAG,OAAO,SAAA,EAC9C,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,OAAA;AAAA,YAAA;AAAA,cACC,EAAA,EAAI,OAAA;AAAA,cACJ,IAAA,EAAK,UAAA;AAAA,cACL,8BAAA,EAA6B,EAAA;AAAA,cAC7B,OAAA,EAAS,OAAA;AAAA,cACT,QAAA,EAAU;AAAA;AAAA,WACZ;AAAA,0BACA,GAAA,CAAC,OAAA,EAAA,EAAM,OAAA,EAAS,OAAA,EAAS,QAAA,EAAA,aAAA,EAAW;AAAA,SAAA,EACtC,CAAA;AAAA,wBAEA,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,SAAA,EACV,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,+BAAA,EAA8B,EAAA;AAAA,cAC9B,KAAA,EAAO,YAAA;AAAA,cACP,SAAS,MAAM;AACb,gBAAA,QAAA,CAAS,gBAAA,EAAiB;AAAA,cAC5B,CAAA;AAAA,cACD,QAAA,EAAA;AAAA;AAAA,WAED;AAAA,0BACA,GAAA,CAAC,UAAK,+BAAA,EAA8B,EAAA,EAAG,OAAO,YAAA,EAC3C,QAAA,EAAA,iBAAA,GAAoB,uBAAuB,SAAA,EAC9C;AAAA,SAAA,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;AAGO,IAAM,8BAAA,GAAiC","file":"SimControls.js","sourcesContent":["/**\n * `@modernrelay/orbit-react/components/SimControls` — `<GraphSimControls>`,\n * the §16.11 simulation control panel (S12-T12).\n *\n * Live sliders over every `SimulationConfig` field (gravity, repulsion,\n * friction, linkDistance, linkSpring, collision, decay, repulsionTheta,\n * center — the full §10 tunable set), a speed\n * preset toggle, and a reheat button. Every control write is ONE atomic\n * host update — `applyHostUpdate({ simulation: merged })`, the same lane the\n * `<Graph>` `simulation` prop uses — which sinks as a single config-only\n * `engine.commit`: no structure, no buffers, no restart, positions untouched\n * (R-16.11-02, E1).\n *\n * ## Current-value resolution (v0.10, investigated)\n * The store publishes NO simulation/layout lane and the instance exposes no\n * config getter — `layout`/`simulation` are instance-local in\n * `createGraphInstance`; `GraphStoreState` carries only `simulationRunning`.\n * The panel therefore mirrors the caller's `simulation` prop (the same\n * object handed to `<Graph>`) and merges control writes over that mirror; a\n * prop identity change re-seeds the mirror (the external writer wins). Like\n * `subgraph` (§9.2), the lane is last-writer-wins: the panel and the\n * `<Graph>` prop write the same state. `onSimulationChange` fires with the\n * full merged config after every write so controlled callers can loop it\n * back through their `simulation` prop (the spec's `onLayoutChange` seam\n * traffics in the LayoutSpec object form and is post-v0.10).\n *\n * ## Applicability (R-16.11-04)\n * Rendered ONLY under layout `'force'` — the `layout` prop mirrors the\n * caller's `<Graph>` prop for the same no-observable-lane reason, defaulting\n * to `'force'` (the instance construction default). Under `'fixed'` the\n * panel renders nothing. The spec's third case — `static` while its\n * convergence run is active — is N/A in v0.10: `LayoutKind` is\n * `'force' | 'fixed'` (core types.ts) and the store exposes no\n * convergence-run state; wire it when the `static` variant lands.\n *\n * ## Reheat\n * The spec names `reheat(alpha)`; the shipped instance surface is\n * `resumeSimulation()` (§16.1 simulation controls — sinks to\n * `engine.start()` and publishes `simulationRunning`). The button calls\n * that; the alpha parameter follows when core grows the reheat API.\n *\n * ## Speed toggle\n * A preset over the `decay` lane — the §10 cool-down tunable (cosmos\n * `simulationDecay`), which core exposes as of v0.10.2. ON writes a smaller\n * coefficient so the run cools SLOWER and stays lively; OFF restores the\n * pre-toggle value, or the engine default when the field was unset. The\n * toggle and the Cool-down slider are two views over one lane, so moving\n * either is reflected by the other. Same config-only commit discipline as\n * every slider. (Before v0.10.2 this proxied through `friction` because\n * `decay` had no core lane — that workaround is now retired.)\n *\n * ## Keyboard & ARIA (§15.1)\n * Native `<input type=\"range\">`/checkbox controls with `<label htmlFor>`\n * wiring. Arrow/Home/End/Page keys are handled in `onKeyDown` with\n * `preventDefault()` so stepping is deterministic both in browsers (never\n * double-steps against the native range keyboard model) and under jsdom\n * (which has no native range keyboard model at all).\n */\n\nimport { Fragment, useCallback, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport type { CSSProperties, ReactElement, ReactNode } from 'react';\nimport type { LayoutKind, SimulationConfig } from '@modernrelay/orbit-core';\nimport type { AnyGraphInstance } from '../../GraphProvider';\nimport { cornerStyle, mergeStyle, useResolvedInstance } from '../shared';\nimport type { GraphCorner } from '../shared';\n\nexport type SimFieldKey = keyof SimulationConfig;\n\nexport interface SimFieldDescriptor {\n key: SimFieldKey;\n /** Visible label text (rendered as a text node). */\n label: string;\n min: number;\n max: number;\n step: number;\n /** Displayed when the field is unset — the engine default the simulation\n * is actually running with (cosmos 3.3.0 `defaultConfigValues`). Never\n * WRITTEN unless the user moves the control. */\n fallback: number;\n}\n\n/** §16.11 slider roster = the full v0.10 `SimulationConfig` field list. */\nexport const SIM_FIELDS: readonly SimFieldDescriptor[] = [\n { key: 'gravity', label: 'Gravity', min: 0, max: 1, step: 0.01, fallback: 0.25 },\n { key: 'repulsion', label: 'Repulsion', min: 0, max: 2, step: 0.01, fallback: 1 },\n { key: 'friction', label: 'Friction', min: 0, max: 1, step: 0.01, fallback: 0.85 },\n { key: 'linkDistance', label: 'Link distance', min: 1, max: 100, step: 1, fallback: 10 },\n { key: 'linkSpring', label: 'Link spring', min: 0, max: 2, step: 0.01, fallback: 1 },\n // v0.10.2: the rest of the §10 tunable set. `collision` and `center` ship\n // OFF at the engine default, so their sliders start at 0 — moving one is\n // what turns the force on.\n { key: 'collision', label: 'Collision', min: 0, max: 2, step: 0.01, fallback: 0 },\n { key: 'decay', label: 'Cool-down', min: 100, max: 20000, step: 100, fallback: 5000 },\n { key: 'repulsionTheta', label: 'Repulsion θ', min: 0.3, max: 2, step: 0.05, fallback: 1.15 },\n { key: 'center', label: 'Centering', min: 0, max: 1, step: 0.01, fallback: 0 },\n];\n\n/** Speed-preset friction values (module JSDoc: the toggle's documented\n * simulation field is `friction` until core ships `decay`). */\nexport const SPEED_NORMAL_DECAY = 5000;\n/** A smaller decay coefficient cools SLOWER, so the run keeps moving —\n * \"fast\" here means \"stays lively\", matching the §10 semantics. */\nexport const SPEED_FAST_DECAY = 1000;\n\nexport interface GraphSimControlsProps {\n /** Explicit instance (multi-instance pages); ambient context otherwise. */\n instance?: AnyGraphInstance;\n /** Active layout kind — mirror of the caller's <Graph> `layout` prop\n * (no observable lane exists; module JSDoc). Default 'force'. */\n layout?: LayoutKind;\n /** Current simulation config seed — the same object passed to <Graph>.\n * An identity change re-seeds the panel's mirror. */\n simulation?: SimulationConfig;\n /** Fires with the FULL merged SimulationConfig after every write. */\n onSimulationChange?: (next: SimulationConfig) => void;\n /** Render-prop REPLACEMENT for a field row; the default renders\n * label + native range input + value readout. `set` clamps/quantizes to\n * the descriptor's bounds and step before writing. */\n renderField?: (ctx: {\n field: SimFieldDescriptor;\n value: number;\n /** Stable id for `htmlFor` wiring in custom rows. */\n inputId: string;\n set: (value: number) => void;\n }) => ReactNode;\n /** Accessible name of the panel. Default 'Simulation controls'. */\n label?: string;\n /** Corner this panel anchors to. Default 'bottom-left'. */\n position?: GraphCorner;\n /** Distance from the two anchored edges in CSS px. Default 12. */\n offset?: number;\n /** Class hook for the root; providing it drops the default styles\n * INCLUDING placement — position it yourself in CSS. */\n className?: string;\n /** Style overrides merged over the defaults; an inset you set here\n * wins over the one `position` implies (resolved per axis). */\n style?: CSSProperties;\n}\n\n/** Decimal places of `step` — the quantization grid for keyboard stepping. */\nfunction stepDecimals(step: number): number {\n const s = String(step);\n const dot = s.indexOf('.');\n return dot === -1 ? 0 : s.length - dot - 1;\n}\n\n/** Clamp to [min, max] and quantize to the step's decimal grid so repeated\n * keyboard steps never accumulate float noise (0.25 + 0.01 stays 0.26). */\nfunction quantize(field: SimFieldDescriptor, raw: number): number {\n const clamped = Math.min(field.max, Math.max(field.min, raw));\n return Number(clamped.toFixed(stepDecimals(field.step)));\n}\n\n/** §15.1 keyboard model for a slider; null = key not handled (let it pass). */\nfunction keyboardTarget(field: SimFieldDescriptor, value: number, key: string): number | null {\n switch (key) {\n case 'ArrowRight':\n case 'ArrowUp':\n return quantize(field, value + field.step);\n case 'ArrowLeft':\n case 'ArrowDown':\n return quantize(field, value - field.step);\n case 'PageUp':\n return quantize(field, value + field.step * 10);\n case 'PageDown':\n return quantize(field, value - field.step * 10);\n case 'Home':\n return field.min;\n case 'End':\n return field.max;\n default:\n return null;\n }\n}\n\n// --- styling defaults (headless-styleable: className/style override) ---\nconst PANEL_STYLE: CSSProperties = {\n width: 250,\n pointerEvents: 'auto',\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n padding: 10,\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 TITLE_STYLE: CSSProperties = { fontWeight: 600, fontSize: 13 };\nconst ROW_STYLE: CSSProperties = { display: 'flex', alignItems: 'center', gap: 6 };\nconst LABEL_STYLE: CSSProperties = { flex: 'none', width: 86, opacity: 0.8 };\nconst SLIDER_STYLE: CSSProperties = { flex: 1, minWidth: 0 };\nconst VALUE_STYLE: CSSProperties = {\n flex: 'none',\n width: 38,\n textAlign: 'right',\n fontVariantNumeric: 'tabular-nums',\n};\nconst BUTTON_STYLE: CSSProperties = {\n appearance: 'none',\n background: 'transparent',\n border: '1px solid rgba(255, 255, 255, 0.2)',\n borderRadius: 6,\n color: 'inherit',\n cursor: 'pointer',\n font: 'inherit',\n padding: '4px 10px',\n};\nconst STATUS_STYLE: CSSProperties = { opacity: 0.65 };\n\nexport function GraphSimControls(props: GraphSimControlsProps): ReactElement | null {\n const instance = useResolvedInstance(props.instance, '<GraphSimControls>');\n const baseId = useId();\n\n // Mirror of the last-known SimulationConfig (module JSDoc: no store lane\n // exists to subscribe to). Prop identity change re-seeds — external\n // writer wins (the React-documented derive-during-render pattern).\n const [mirror, setMirror] = useState<SimulationConfig>(() => props.simulation ?? {});\n const [seededFrom, setSeededFrom] = useState(props.simulation);\n if (seededFrom !== props.simulation) {\n setSeededFrom(props.simulation);\n setMirror(props.simulation ?? {});\n }\n\n /** Decay the speed preset restores on release; undefined = the field was\n * unset pre-toggle (restore the engine-default constant). */\n const preSpeedDecayRef = useRef<number | undefined>(undefined);\n\n // The one simulation lane the store DOES publish (§16.1).\n const subscribe = useCallback(\n (onStoreChange: () => void) => instance.store.subscribe(onStoreChange),\n [instance],\n );\n const getRunning = useCallback(\n () => instance.store.getState().simulationRunning,\n [instance],\n );\n const simulationRunning = useSyncExternalStore(subscribe, getRunning, getRunning);\n\n // R-16.11-04 applicability gate — after hooks, per the rules of hooks.\n if ((props.layout ?? 'force') !== 'force') return null;\n\n /** ONE atomic config-only host update per control write (R-16.11-02). */\n const write = (next: SimulationConfig): void => {\n setMirror(next);\n instance.applyHostUpdate({ simulation: next });\n props.onSimulationChange?.(next);\n };\n\n const setField = (key: SimFieldKey, value: number): void => {\n const next: SimulationConfig = { ...mirror };\n next[key] = value;\n write(next);\n };\n\n // The preset holds exactly while decay sits at the fast value — the toggle\n // and the cool-down slider are two views over the same lane.\n const speedOn = mirror.decay === SPEED_FAST_DECAY;\n const toggleSpeed = (): void => {\n if (speedOn) {\n const restored = preSpeedDecayRef.current ?? SPEED_NORMAL_DECAY;\n preSpeedDecayRef.current = undefined;\n setField('decay', restored);\n } else {\n preSpeedDecayRef.current = mirror.decay;\n setField('decay', SPEED_FAST_DECAY);\n }\n };\n\n const panelLabel = props.label ?? 'Simulation controls';\n const speedId = `${baseId}-speed`;\n\n return (\n <div\n data-orbit-simcontrols=\"\"\n role=\"group\"\n aria-label={panelLabel}\n className={props.className}\n style={\n props.className !== undefined\n ? props.style\n : mergeStyle(\n { ...cornerStyle(props.position ?? 'bottom-left', props.offset), ...PANEL_STYLE },\n props.style,\n )\n }\n >\n <div data-orbit-simcontrols-title=\"\" style={TITLE_STYLE}>\n {panelLabel}\n </div>\n\n {SIM_FIELDS.map((field) => {\n const value = mirror[field.key] ?? field.fallback;\n const inputId = `${baseId}-${field.key}`;\n const set = (v: number): void => {\n setField(field.key, quantize(field, v));\n };\n if (props.renderField !== undefined) {\n return (\n <Fragment key={field.key}>\n {props.renderField({ field, value, inputId, set })}\n </Fragment>\n );\n }\n return (\n <div key={field.key} data-orbit-simcontrols-field={field.key} style={ROW_STYLE}>\n <label htmlFor={inputId} style={LABEL_STYLE}>\n {field.label}\n </label>\n <input\n id={inputId}\n type=\"range\"\n data-orbit-simcontrols-input={field.key}\n min={field.min}\n max={field.max}\n step={field.step}\n value={value}\n onChange={(e) => {\n const v = Number(e.target.value);\n if (Number.isFinite(v)) set(v);\n }}\n onKeyDown={(e) => {\n const target = keyboardTarget(field, value, e.key);\n if (target === null) return;\n // Suppress the browser's native range step so the write goes\n // through this handler exactly once (module JSDoc).\n e.preventDefault();\n if (target !== value) setField(field.key, target);\n }}\n style={SLIDER_STYLE}\n />\n <output\n htmlFor={inputId}\n data-orbit-simcontrols-value={field.key}\n style={VALUE_STYLE}\n >\n {String(value)}\n </output>\n </div>\n );\n })}\n\n <div data-orbit-simcontrols-speed-row=\"\" style={ROW_STYLE}>\n <input\n id={speedId}\n type=\"checkbox\"\n data-orbit-simcontrols-speed=\"\"\n checked={speedOn}\n onChange={toggleSpeed}\n />\n <label htmlFor={speedId}>Speed boost</label>\n </div>\n\n <div style={ROW_STYLE}>\n <button\n type=\"button\"\n data-orbit-simcontrols-reheat=\"\"\n style={BUTTON_STYLE}\n onClick={() => {\n instance.resumeSimulation();\n }}\n >\n Reheat\n </button>\n <span data-orbit-simcontrols-status=\"\" style={STATUS_STYLE}>\n {simulationRunning ? 'simulation running' : 'settled'}\n </span>\n </div>\n </div>\n );\n}\n\n/** S0-T02 tree-shake probe — must never reach a root-only consumer bundle. */\nexport const __ORBIT_SIMCONTROLS_SENTINEL__ = 'orbit-react/components/SimControls';\n"]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, CSSProperties } from 'react';
|
|
3
|
+
import { GraphEdge, GraphNode } from '@modernrelay/orbit-core';
|
|
4
|
+
import { A as AnyGraphInstance } from '../GraphProvider-B8ITDXS0.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Pure row/column/sort/filter helpers for `<GraphTable>` (§16.10).
|
|
8
|
+
* Framework-free so the §8 sort-coercion contract and the bounded column
|
|
9
|
+
* derivation stay unit-testable without a DOM.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
type GraphTableMode = 'nodes' | 'edges';
|
|
13
|
+
interface GraphTableColumn {
|
|
14
|
+
/** Column key: 'id', 'source'/'target' (edge mode), or an attr key. */
|
|
15
|
+
key: string;
|
|
16
|
+
/** Header text; defaults to the key. Rendered as a TEXT NODE (§14). */
|
|
17
|
+
label?: string;
|
|
18
|
+
}
|
|
19
|
+
interface GraphTableSort {
|
|
20
|
+
key: string;
|
|
21
|
+
direction: 'asc' | 'desc';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One table row. Node rows: `key === id === NodeId`. Edge rows carry the
|
|
25
|
+
* caller edge; `id` is null when the caller supplied no edge id (such rows
|
|
26
|
+
* render and export but cannot write edge selection — the core-synthesized
|
|
27
|
+
* id is not observable through the public surface).
|
|
28
|
+
*/
|
|
29
|
+
interface GraphTableRowRef {
|
|
30
|
+
/** Stable render identity (node id, edge id, or a positional edge key). */
|
|
31
|
+
key: string;
|
|
32
|
+
/** Public id usable for selection writes; null = unaddressable edge row. */
|
|
33
|
+
id: string | null;
|
|
34
|
+
edge?: GraphEdge<any>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** S0-T02 tree-shake probe: importing `<Graph>` alone must not pull this. */
|
|
38
|
+
declare const __ORBIT_TABLE_SENTINEL__ = "orbit-react/components/Table";
|
|
39
|
+
/** Default crossfilter dimension key the table filter registers on. */
|
|
40
|
+
declare const TABLE_FILTER_DIMENSION_DEFAULT = "table";
|
|
41
|
+
interface GraphTableCellContext {
|
|
42
|
+
row: GraphTableRowRef;
|
|
43
|
+
/** Resolved node for node rows (undefined for edge rows / unknown ids). */
|
|
44
|
+
node?: GraphNode<any>;
|
|
45
|
+
column: GraphTableColumn;
|
|
46
|
+
value: unknown;
|
|
47
|
+
/** The default display text (always safe to render as a text node). */
|
|
48
|
+
text: string;
|
|
49
|
+
}
|
|
50
|
+
interface GraphTableHandle {
|
|
51
|
+
/** RFC-4180 CSV over the current filtered+sorted rows (see module doc). */
|
|
52
|
+
exportCsv(): string;
|
|
53
|
+
}
|
|
54
|
+
interface GraphTableProps {
|
|
55
|
+
/** Explicit instance (multi-instance pages); ambient context otherwise. */
|
|
56
|
+
instance?: AnyGraphInstance;
|
|
57
|
+
/** Row source. Default 'nodes'. */
|
|
58
|
+
mode?: GraphTableMode;
|
|
59
|
+
/** Edge-mode rows (documented compromise — see module doc). */
|
|
60
|
+
edges?: readonly GraphEdge<any>[];
|
|
61
|
+
/** Explicit column pick; replaces derivation entirely. */
|
|
62
|
+
columns?: readonly (string | GraphTableColumn)[];
|
|
63
|
+
/** Bounded derivation prefix (rows sampled for the attr-key union). Default 200. */
|
|
64
|
+
columnSample?: number;
|
|
65
|
+
/** Controlled filter text; uncontrolled via the built-in input otherwise. */
|
|
66
|
+
filterText?: string;
|
|
67
|
+
onFilterTextChange?: (text: string) => void;
|
|
68
|
+
/** Composed AND with the text filter; receives the underlying item. */
|
|
69
|
+
filterPredicate?: (item: GraphNode<any> | GraphEdge<any>) => boolean;
|
|
70
|
+
/** Crossfilter dimension key the filter registers on. Default 'table'. */
|
|
71
|
+
filterDimension?: string;
|
|
72
|
+
/** CSV formula-injection neutralization opt-out. Default true (safe). */
|
|
73
|
+
neutralizeFormulas?: boolean;
|
|
74
|
+
/** Row height in CSS px (mechanical — drives the window math). Default 28. */
|
|
75
|
+
rowHeight?: number;
|
|
76
|
+
/** Scroll viewport height in CSS px (mechanical). Default 320. */
|
|
77
|
+
height?: number;
|
|
78
|
+
/** Extra rows mounted on each side of the viewport. Default 8. */
|
|
79
|
+
overscan?: number;
|
|
80
|
+
/** Replaces default cell content (rendered inside the cell element). */
|
|
81
|
+
renderCell?: (ctx: GraphTableCellContext) => ReactNode;
|
|
82
|
+
/** Accessible name of the table. Default 'Graph table'. */
|
|
83
|
+
label?: string;
|
|
84
|
+
/** Class hook for the root; providing it drops the decorative styles. */
|
|
85
|
+
className?: string;
|
|
86
|
+
style?: CSSProperties;
|
|
87
|
+
/** Class hook for body rows; providing it drops their decorative styles. */
|
|
88
|
+
rowClassName?: string;
|
|
89
|
+
}
|
|
90
|
+
declare const GraphTable: react.ForwardRefExoticComponent<GraphTableProps & react.RefAttributes<GraphTableHandle>>;
|
|
91
|
+
|
|
92
|
+
export { GraphTable, type GraphTableCellContext, type GraphTableColumn, type GraphTableHandle, type GraphTableMode, type GraphTableProps, type GraphTableRowRef, type GraphTableSort, TABLE_FILTER_DIMENSION_DEFAULT, __ORBIT_TABLE_SENTINEL__ };
|