@mirror-physics/fractal-ui 0.2.0-next.77 → 0.2.0-next.78

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.
@@ -0,0 +1,382 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/components/ColumnEditor/entry.ts
31
+ var entry_exports = {};
32
+ __export(entry_exports, {
33
+ ColumnEditor: () => ColumnEditor
34
+ });
35
+ module.exports = __toCommonJS(entry_exports);
36
+
37
+ // src/components/ColumnEditor/ColumnEditor.tsx
38
+ var React2 = __toESM(require("react"), 1);
39
+ var import_fractal_icons = require("@mirror-physics/fractal-icons");
40
+
41
+ // src/components/Button/Button.tsx
42
+ var React = __toESM(require("react"), 1);
43
+
44
+ // src/utils/cn.ts
45
+ var import_clsx = require("clsx");
46
+ function cn(...inputs) {
47
+ return (0, import_clsx.clsx)(inputs);
48
+ }
49
+
50
+ // src/components/Button/Button.tsx
51
+ var import_jsx_runtime = require("react/jsx-runtime");
52
+ var colorPalette = {
53
+ blue: {
54
+ 0: "var(--blue-0)",
55
+ 50: "var(--blue-50)",
56
+ 100: "var(--blue-100)",
57
+ 200: "var(--blue-200)",
58
+ 500: "var(--blue-500)",
59
+ 600: "var(--blue-600)",
60
+ 700: "var(--blue-700)"
61
+ },
62
+ pink: {
63
+ 0: "var(--pink-0)",
64
+ 50: "var(--pink-50)",
65
+ 100: "var(--pink-100)",
66
+ 200: "var(--pink-200)",
67
+ 500: "var(--pink-500)",
68
+ 600: "var(--pink-600)",
69
+ 700: "var(--pink-700)"
70
+ },
71
+ orange: {
72
+ 0: "var(--orange-0)",
73
+ 50: "var(--orange-50)",
74
+ 100: "var(--orange-100)",
75
+ 200: "var(--orange-200)",
76
+ 500: "var(--orange-500)",
77
+ 600: "var(--orange-600)",
78
+ 700: "var(--orange-700)"
79
+ },
80
+ green: {
81
+ 0: "var(--green-0)",
82
+ 50: "var(--green-50)",
83
+ 100: "var(--green-100)",
84
+ 200: "var(--green-200)",
85
+ 500: "var(--green-500)",
86
+ 600: "var(--green-600)",
87
+ 700: "var(--green-700)"
88
+ }
89
+ };
90
+ function getButtonStyles(buttonType, isHovered, isActive, disabled, color, hasIconOnly) {
91
+ const palette = colorPalette[color];
92
+ const base = {
93
+ borderRadius: hasIconOnly ? "var(--radius-lg, 8px)" : "var(--radius-full, 9999px)",
94
+ cursor: disabled ? "not-allowed" : "pointer",
95
+ transition: "background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease"
96
+ };
97
+ if (hasIconOnly) {
98
+ base.justifyContent = "center";
99
+ }
100
+ if (disabled) {
101
+ if (buttonType === "ghost") {
102
+ return { ...base, backgroundColor: "transparent", border: "1px solid transparent", color: "var(--fg-disabled)" };
103
+ }
104
+ return { ...base, backgroundColor: "transparent", border: "1px solid var(--fg-disabled)", color: "var(--fg-disabled)" };
105
+ }
106
+ switch (buttonType) {
107
+ // Primary with text: dark pill. Icon-only: color palette (Synapse style).
108
+ case "primary":
109
+ if (hasIconOnly) {
110
+ return {
111
+ ...base,
112
+ backgroundColor: isActive ? palette[700] : isHovered ? palette[500] : palette[600],
113
+ border: "1px solid transparent",
114
+ color: "var(--fg-inverse)"
115
+ };
116
+ }
117
+ return {
118
+ ...base,
119
+ backgroundColor: "var(--fg-primary)",
120
+ border: "1px solid var(--fg-primary)",
121
+ color: "var(--fg-inverse)",
122
+ ...isHovered || isActive ? { opacity: 0.85 } : {}
123
+ };
124
+ // Secondary: neutral. Rest=canvas, hover=surface-1 (darken), pressed=surface-2. Subtle border, dark text.
125
+ case "secondary":
126
+ return {
127
+ ...base,
128
+ backgroundColor: isActive ? "var(--bg-surface-2)" : isHovered ? "var(--bg-surface-1)" : "var(--bg-canvas)",
129
+ border: "1px solid var(--border-subtle)",
130
+ color: "var(--fg-primary)"
131
+ };
132
+ // Ghost: no border. Rest=transparent, hover/press=surface-1.
133
+ case "ghost":
134
+ return {
135
+ ...base,
136
+ backgroundColor: isHovered || isActive ? "var(--bg-surface-1)" : "transparent",
137
+ border: "1px solid transparent",
138
+ color: "var(--fg-primary)"
139
+ };
140
+ // Danger: low-intensity destructive action. Text stays steady; hover deepens the light red surface.
141
+ case "danger":
142
+ return {
143
+ ...base,
144
+ backgroundColor: isHovered || isActive ? "var(--danger-button-surface-hover)" : "var(--danger-button-surface)",
145
+ border: "1px solid transparent",
146
+ color: "var(--danger-button-fg)"
147
+ };
148
+ default:
149
+ return base;
150
+ }
151
+ }
152
+ var Button = React.forwardRef(
153
+ ({ className, buttonType = "primary", buttonSize = "md", controlSize, color = "blue", icon, hasIconOnly, iconPosition = "left", style, disabled, children, ...props }, ref) => {
154
+ const [isHovered, setIsHovered] = React.useState(false);
155
+ const [isActive, setIsActive] = React.useState(false);
156
+ const typeStyles = getButtonStyles(buttonType, isHovered, isActive, !!disabled, color, hasIconOnly);
157
+ const hasIconAndText = icon && children && !hasIconOnly;
158
+ const finalStyles = hasIconAndText ? { ...typeStyles, gap: "var(--space-2, 8px)", ...style } : { ...typeStyles, ...style };
159
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
160
+ "button",
161
+ {
162
+ ref,
163
+ type: "button",
164
+ "data-control-size": controlSize ?? buttonSize,
165
+ "data-icon-only": hasIconOnly || void 0,
166
+ className: cn("fractal-control", "fractal-button", `fractal-button--${buttonType}`, className),
167
+ style: finalStyles,
168
+ disabled,
169
+ onMouseEnter: (e) => {
170
+ setIsHovered(true);
171
+ props.onMouseEnter?.(e);
172
+ },
173
+ onMouseLeave: (e) => {
174
+ setIsHovered(false);
175
+ setIsActive(false);
176
+ props.onMouseLeave?.(e);
177
+ },
178
+ onMouseDown: (e) => {
179
+ setIsActive(true);
180
+ props.onMouseDown?.(e);
181
+ },
182
+ onMouseUp: (e) => {
183
+ setIsActive(false);
184
+ props.onMouseUp?.(e);
185
+ },
186
+ ...props,
187
+ children: [
188
+ iconPosition !== "right" && icon,
189
+ !hasIconOnly && children,
190
+ iconPosition === "right" && icon
191
+ ]
192
+ }
193
+ );
194
+ }
195
+ );
196
+ Button.displayName = "Button";
197
+
198
+ // src/components/ColumnEditor/ColumnEditor.tsx
199
+ var import_jsx_runtime2 = require("react/jsx-runtime");
200
+ var equal = (a, b) => a.length === b.length && a.every((id, index) => id === b[index]);
201
+ function ColumnEditor({ items, value, onChange, label = "Edit columns", hiddenLabel = "Hidden columns", className }) {
202
+ const [open, setOpen] = React2.useState(false);
203
+ const [preview, setPreview] = React2.useState(null);
204
+ const [announcement, setAnnouncement] = React2.useState("");
205
+ const root = React2.useRef(null);
206
+ const trigger = React2.useRef(null);
207
+ const panel = React2.useRef(null);
208
+ const drag = React2.useRef(null);
209
+ const positions = React2.useRef(null);
210
+ const id = React2.useId();
211
+ function capturePositions() {
212
+ positions.current = new Map(Array.from(panel.current?.querySelectorAll("[data-column-editor-item]") ?? []).map((row2) => [row2.dataset.columnEditorItem, row2.getBoundingClientRect()]));
213
+ }
214
+ React2.useLayoutEffect(() => {
215
+ const before = positions.current;
216
+ positions.current = null;
217
+ if (!before || !panel.current || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
218
+ const styles = getComputedStyle(panel.current);
219
+ const token = styles.getPropertyValue("--duration-normal").trim();
220
+ const duration = parseFloat(token) * (token.endsWith("ms") ? 1 : 1e3);
221
+ const easing = styles.getPropertyValue("--ease-out").trim();
222
+ for (const row2 of panel.current.querySelectorAll("[data-column-editor-item]")) {
223
+ const previous = before.get(row2.dataset.columnEditorItem);
224
+ row2.getAnimations().filter((animation) => animation.id === "fractal-column-position").forEach((animation) => animation.cancel());
225
+ if (!previous) continue;
226
+ const next = row2.getBoundingClientRect();
227
+ const x = previous.left - next.left, y = previous.top - next.top;
228
+ if (Math.abs(x) < 1 && Math.abs(y) < 1) continue;
229
+ row2.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: "translate(0, 0)" }], { duration, easing, id: "fractal-column-position" });
230
+ }
231
+ });
232
+ const valid = [...new Set(value)].filter((key) => items.some((item) => item.id === key));
233
+ const committed = [...valid, ...items.filter((item) => item.required && !valid.includes(item.id)).map((item) => item.id)];
234
+ const visible = preview?.visible ?? committed;
235
+ const hidden = preview?.hidden ?? items.filter((item) => !committed.includes(item.id)).map((item) => item.id);
236
+ function cancel() {
237
+ capturePositions();
238
+ drag.current = null;
239
+ setPreview(null);
240
+ }
241
+ React2.useEffect(() => {
242
+ if (!open) return;
243
+ panel.current?.focus();
244
+ function outside(event) {
245
+ if (!drag.current && !root.current?.contains(event.target)) setOpen(false);
246
+ }
247
+ function escape(event) {
248
+ if (event.key !== "Escape") return;
249
+ event.preventDefault();
250
+ if (drag.current) {
251
+ capturePositions();
252
+ drag.current = null;
253
+ setPreview(null);
254
+ setAnnouncement("Reordering cancelled");
255
+ } else {
256
+ setOpen(false);
257
+ trigger.current?.focus();
258
+ }
259
+ }
260
+ function blur(event) {
261
+ if (event.relatedTarget instanceof Node && !root.current?.contains(event.relatedTarget)) setOpen(false);
262
+ }
263
+ const element = root.current;
264
+ document.addEventListener("pointerdown", outside);
265
+ document.addEventListener("keydown", escape);
266
+ element?.addEventListener("focusout", blur);
267
+ return () => {
268
+ document.removeEventListener("pointerdown", outside);
269
+ document.removeEventListener("keydown", escape);
270
+ element?.removeEventListener("focusout", blur);
271
+ };
272
+ }, [open]);
273
+ function commit(next, message) {
274
+ capturePositions();
275
+ onChange(next);
276
+ setAnnouncement(message);
277
+ }
278
+ function move(key, offset) {
279
+ const index = committed.indexOf(key);
280
+ const next = committed.filter((item) => item !== key);
281
+ if (offset > 0 && index === committed.length - 1 && !items.find((item) => item.id === key)?.required) {
282
+ commit(next, `${items.find((item) => item.id === key)?.label} hidden`);
283
+ return;
284
+ }
285
+ next.splice(Math.max(0, Math.min(next.length, index + offset)), 0, key);
286
+ commit(next, `${items.find((item) => item.id === key)?.label} moved ${offset < 0 ? "up" : "down"}`);
287
+ }
288
+ function start(event, key) {
289
+ if (event.button !== 0 || event.target.closest("button")) return;
290
+ drag.current = { id: key, pointerId: event.pointerId, x: event.clientX, y: event.clientY, active: false, visible: [...committed], hidden: [...hidden] };
291
+ panel.current?.setPointerCapture(event.pointerId);
292
+ event.preventDefault();
293
+ }
294
+ function update(event) {
295
+ const current = drag.current;
296
+ if (!current || event.pointerId !== current.pointerId) return;
297
+ if (!current.active && Math.hypot(event.clientX - current.x, event.clientY - current.y) < 4) return;
298
+ current.active = true;
299
+ const container = panel.current;
300
+ if (!container) return;
301
+ const bounds = container.getBoundingClientRect();
302
+ const hiddenGroup = container.querySelector('[data-column-editor-group="hidden"]');
303
+ const inside = event.clientX >= bounds.left && event.clientX <= bounds.right && event.clientY >= bounds.top && event.clientY <= bounds.bottom;
304
+ if (!inside) return;
305
+ const destination = event.clientY >= hiddenGroup.getBoundingClientRect().top ? "hidden" : "visible";
306
+ if (destination === "hidden" && items.find((item) => item.id === current.id)?.required) return;
307
+ const slots = Array.from(container.querySelectorAll(`[data-column-editor-group="${destination}"] [data-column-editor-item]`));
308
+ const localY = event.clientY - bounds.top + container.scrollTop - container.clientTop;
309
+ const ownSlot = slots.find((row2) => row2.dataset.columnEditorItem === current.id);
310
+ if (ownSlot && localY >= ownSlot.offsetTop && localY <= ownSlot.offsetTop + ownSlot.offsetHeight) return;
311
+ const nextVisible = current.visible.filter((key) => key !== current.id);
312
+ let nextHidden = current.hidden.filter((key) => key !== current.id);
313
+ if (destination === "visible") {
314
+ const index = slots.filter((row2) => row2.dataset.columnEditorItem !== current.id && localY > row2.offsetTop + row2.offsetHeight / 2).length;
315
+ nextVisible.splice(index, 0, current.id);
316
+ } else {
317
+ nextHidden.push(current.id);
318
+ nextHidden = items.filter((item) => nextHidden.includes(item.id)).map((item) => item.id);
319
+ }
320
+ if (!equal(nextVisible, current.visible) || !equal(nextHidden, current.hidden)) {
321
+ capturePositions();
322
+ current.visible = nextVisible;
323
+ current.hidden = nextHidden;
324
+ }
325
+ setPreview({ visible: current.visible, hidden: current.hidden, active: current.id });
326
+ if (event.clientY < bounds.top + 32) container.scrollTop -= 12;
327
+ else if (event.clientY > bounds.bottom - 32) container.scrollTop += 12;
328
+ }
329
+ function finish(event) {
330
+ const current = drag.current;
331
+ if (!current || current.pointerId !== event.pointerId) return;
332
+ const hit = document.elementFromPoint(event.clientX, event.clientY);
333
+ if (current.active && hit && panel.current?.contains(hit)) commit(current.visible, "Column order updated");
334
+ cancel();
335
+ if (panel.current?.hasPointerCapture(event.pointerId)) panel.current.releasePointerCapture(event.pointerId);
336
+ }
337
+ function row(key, isHidden, index) {
338
+ const item = items.find((item2) => item2.id === key);
339
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
340
+ "li",
341
+ {
342
+ className: "fractal-column-editor__row",
343
+ "data-column-editor-item": key,
344
+ "data-hidden": isHidden || void 0,
345
+ "data-dragging": preview?.active === key || void 0,
346
+ onPointerDown: (event) => start(event, key),
347
+ children: [
348
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_fractal_icons.DotsSixVertical, { className: "fractal-column-editor__grip", "aria-hidden": "true" }),
349
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "fractal-column-editor__name", children: item.label }),
350
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "fractal-column-editor__actions", onPointerDown: (event) => event.stopPropagation(), children: isHidden ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { buttonType: "ghost", controlSize: "sm", hasIconOnly: true, icon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_fractal_icons.Eye, { "aria-hidden": "true" }), "aria-label": `Show ${item.label} column`, onClick: () => commit([...committed, key], `${item.label} shown`) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
351
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { buttonType: "ghost", controlSize: "sm", hasIconOnly: true, icon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_fractal_icons.ArrowUp, { "aria-hidden": "true" }), "aria-label": `Move ${item.label} up`, disabled: index === 0, onClick: () => move(key, -1) }),
352
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { buttonType: "ghost", controlSize: "sm", hasIconOnly: true, icon: index === visible.length - 1 && !item.required ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_fractal_icons.EyeSlash, { "aria-hidden": "true" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_fractal_icons.ArrowDown, { "aria-hidden": "true" }), "aria-label": index === visible.length - 1 && !item.required ? `Hide ${item.label} column` : `Move ${item.label} down`, disabled: index === visible.length - 1 && item.required, onClick: () => move(key, 1) })
353
+ ] }) })
354
+ ]
355
+ },
356
+ key
357
+ );
358
+ }
359
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: cn("fractal-column-editor", className), ref: root, children: [
360
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { ref: trigger, controlSize: "lg", buttonType: "secondary", icon: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_fractal_icons.Gear, { "aria-hidden": "true" }), iconPosition: "right", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": id, onClick: () => {
361
+ cancel();
362
+ setOpen((current) => !current);
363
+ }, children: label }),
364
+ open ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { id, className: "fractal-column-editor__panel", ref: panel, role: "dialog", "data-dragging": Boolean(preview) || void 0, "aria-label": label, tabIndex: -1, onPointerMove: update, onPointerUp: finish, onPointerCancel: cancel, onLostPointerCapture: cancel, children: [
365
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ol", { className: "fractal-column-editor__list", "data-column-editor-group": "visible", "aria-label": "Visible columns", children: visible.map((key, index) => row(key, false, index)) }),
366
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { "data-column-editor-group": "hidden", className: "fractal-column-editor__hidden", "aria-label": hiddenLabel, children: [
367
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "fractal-column-editor__divider", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
368
+ hiddenLabel,
369
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_fractal_icons.EyeSlash, { "aria-hidden": "true" })
370
+ ] }) }),
371
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ol", { className: "fractal-column-editor__list", children: hidden.map((key, index) => row(key, true, index)) }),
372
+ !hidden.length ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "fractal-column-editor__empty", children: "Drag columns here to hide them." }) : null
373
+ ] }),
374
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "fractal-column-editor__announcement", role: "status", "aria-live": "polite", children: announcement })
375
+ ] }) : null
376
+ ] });
377
+ }
378
+ // Annotate the CommonJS export names for ESM import in node:
379
+ 0 && (module.exports = {
380
+ ColumnEditor
381
+ });
382
+ //# sourceMappingURL=column-editor.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/ColumnEditor/entry.ts","../src/components/ColumnEditor/ColumnEditor.tsx","../src/components/Button/Button.tsx","../src/utils/cn.ts"],"sourcesContent":["export { ColumnEditor } from './ColumnEditor'\nexport type { ColumnEditorItem, ColumnEditorProps } from './ColumnEditor'\n","import * as React from 'react'\nimport { ArrowUp, ArrowDown, DotsSixVertical, Eye, EyeSlash, Gear } from '@mirror-physics/fractal-icons'\nimport { Button } from '../Button'\nimport { cn } from '../../utils/cn'\nimport './ColumnEditor.css'\n\nexport interface ColumnEditorItem { id: string; label: string; required?: boolean }\nexport interface ColumnEditorProps {\n items: ColumnEditorItem[]\n /** Visible column IDs in display order. Required items cannot be hidden. */\n value: string[]\n onChange: (value: string[]) => void\n label?: string\n hiddenLabel?: string\n className?: string\n}\ninterface DragState {\n id: string; pointerId: number; x: number; y: number; active: boolean\n visible: string[]; hidden: string[]\n}\nconst equal = (a: string[], b: string[]) => a.length === b.length && a.every((id, index) => id === b[index])\n\n/** Reorderable visible/hidden columns, with pointer preview and keyboard actions. */\nexport function ColumnEditor({ items, value, onChange, label = 'Edit columns', hiddenLabel = 'Hidden columns', className }: ColumnEditorProps) {\n const [open, setOpen] = React.useState(false)\n const [preview, setPreview] = React.useState<{ visible: string[]; hidden: string[]; active: string } | null>(null)\n const [announcement, setAnnouncement] = React.useState('')\n const root = React.useRef<HTMLDivElement>(null)\n const trigger = React.useRef<HTMLButtonElement>(null)\n const panel = React.useRef<HTMLDivElement>(null)\n const drag = React.useRef<DragState | null>(null)\n const positions = React.useRef<Map<string, DOMRect> | null>(null)\n const id = React.useId()\n function capturePositions() {\n positions.current = new Map(Array.from(panel.current?.querySelectorAll<HTMLElement>('[data-column-editor-item]') ?? []).map(row => [row.dataset.columnEditorItem!, row.getBoundingClientRect()]))\n }\n // FLIP from each row's current visual position, including interrupted animations.\n React.useLayoutEffect(() => {\n const before = positions.current\n positions.current = null\n if (!before || !panel.current || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return\n const styles = getComputedStyle(panel.current)\n const token = styles.getPropertyValue('--duration-normal').trim()\n const duration = parseFloat(token) * (token.endsWith('ms') ? 1 : 1000)\n const easing = styles.getPropertyValue('--ease-out').trim()\n for (const row of panel.current.querySelectorAll<HTMLElement>('[data-column-editor-item]')) {\n const previous = before.get(row.dataset.columnEditorItem!)\n row.getAnimations().filter(animation => animation.id === 'fractal-column-position').forEach(animation => animation.cancel())\n if (!previous) continue\n const next = row.getBoundingClientRect()\n const x = previous.left - next.left, y = previous.top - next.top\n if (Math.abs(x) < 1 && Math.abs(y) < 1) continue\n row.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: 'translate(0, 0)' }], { duration, easing, id: 'fractal-column-position' })\n }\n })\n const valid = [...new Set(value)].filter(key => items.some(item => item.id === key))\n const committed = [...valid, ...items.filter(item => item.required && !valid.includes(item.id)).map(item => item.id)]\n const visible = preview?.visible ?? committed\n const hidden = preview?.hidden ?? items.filter(item => !committed.includes(item.id)).map(item => item.id)\n\n function cancel() { capturePositions(); drag.current = null; setPreview(null) }\n React.useEffect(() => {\n if (!open) return\n panel.current?.focus()\n function outside(event: PointerEvent) { if (!drag.current && !root.current?.contains(event.target as Node)) setOpen(false) }\n function escape(event: KeyboardEvent) {\n if (event.key !== 'Escape') return\n event.preventDefault()\n if (drag.current) { capturePositions(); drag.current = null; setPreview(null); setAnnouncement('Reordering cancelled') }\n else { setOpen(false); trigger.current?.focus() }\n }\n function blur(event: FocusEvent) { if (event.relatedTarget instanceof Node && !root.current?.contains(event.relatedTarget)) setOpen(false) }\n const element = root.current\n document.addEventListener('pointerdown', outside); document.addEventListener('keydown', escape); element?.addEventListener('focusout', blur)\n return () => { document.removeEventListener('pointerdown', outside); document.removeEventListener('keydown', escape); element?.removeEventListener('focusout', blur) }\n }, [open])\n\n function commit(next: string[], message: string) { capturePositions(); onChange(next); setAnnouncement(message) }\n function move(key: string, offset: number) {\n const index = committed.indexOf(key)\n const next = committed.filter(item => item !== key)\n if (offset > 0 && index === committed.length - 1 && !items.find(item => item.id === key)?.required) {\n commit(next, `${items.find(item => item.id === key)?.label} hidden`); return\n }\n next.splice(Math.max(0, Math.min(next.length, index + offset)), 0, key)\n commit(next, `${items.find(item => item.id === key)?.label} moved ${offset < 0 ? 'up' : 'down'}`)\n }\n function start(event: React.PointerEvent<HTMLLIElement>, key: string) {\n if (event.button !== 0 || (event.target as Element).closest('button')) return\n drag.current = { id: key, pointerId: event.pointerId, x: event.clientX, y: event.clientY, active: false, visible: [...committed], hidden: [...hidden] }\n panel.current?.setPointerCapture(event.pointerId)\n event.preventDefault()\n }\n function update(event: React.PointerEvent<HTMLDivElement>) {\n const current = drag.current\n if (!current || event.pointerId !== current.pointerId) return\n if (!current.active && Math.hypot(event.clientX - current.x, event.clientY - current.y) < 4) return\n current.active = true\n const container = panel.current\n if (!container) return\n const bounds = container.getBoundingClientRect()\n const hiddenGroup = container.querySelector<HTMLElement>('[data-column-editor-group=\"hidden\"]')!\n const inside = event.clientX >= bounds.left && event.clientX <= bounds.right && event.clientY >= bounds.top && event.clientY <= bounds.bottom\n if (!inside) return\n const destination = event.clientY >= hiddenGroup.getBoundingClientRect().top ? 'hidden' : 'visible'\n if (destination === 'hidden' && items.find(item => item.id === current.id)?.required) return\n // Hit-test layout slots, not animated row positions, so moving rows cannot flicker back and forth.\n const slots = Array.from(container.querySelectorAll<HTMLElement>(`[data-column-editor-group=\"${destination}\"] [data-column-editor-item]`))\n const localY = event.clientY - bounds.top + container.scrollTop - container.clientTop\n const ownSlot = slots.find(row => row.dataset.columnEditorItem === current.id)\n if (ownSlot && localY >= ownSlot.offsetTop && localY <= ownSlot.offsetTop + ownSlot.offsetHeight) return\n const nextVisible = current.visible.filter(key => key !== current.id)\n let nextHidden = current.hidden.filter(key => key !== current.id)\n if (destination === 'visible') {\n const index = slots.filter(row => row.dataset.columnEditorItem !== current.id && localY > row.offsetTop + row.offsetHeight / 2).length\n nextVisible.splice(index, 0, current.id)\n } else {\n nextHidden.push(current.id)\n nextHidden = items.filter(item => nextHidden.includes(item.id)).map(item => item.id)\n }\n if (!equal(nextVisible, current.visible) || !equal(nextHidden, current.hidden)) {\n capturePositions()\n current.visible = nextVisible; current.hidden = nextHidden\n }\n setPreview({ visible: current.visible, hidden: current.hidden, active: current.id })\n // Scroll the popover when dragging near its visible edges.\n if (event.clientY < bounds.top + 32) container.scrollTop -= 12\n else if (event.clientY > bounds.bottom - 32) container.scrollTop += 12\n }\n function finish(event: React.PointerEvent<HTMLDivElement>) {\n const current = drag.current\n if (!current || current.pointerId !== event.pointerId) return\n const hit = document.elementFromPoint(event.clientX, event.clientY)\n if (current.active && hit && panel.current?.contains(hit)) commit(current.visible, 'Column order updated')\n cancel()\n if (panel.current?.hasPointerCapture(event.pointerId)) panel.current.releasePointerCapture(event.pointerId)\n }\n function row(key: string, isHidden: boolean, index: number) {\n const item = items.find(item => item.id === key)!\n return <li key={key} className=\"fractal-column-editor__row\" data-column-editor-item={key} data-hidden={isHidden || undefined} data-dragging={preview?.active === key || undefined}\n onPointerDown={event => start(event, key)}>\n <DotsSixVertical className=\"fractal-column-editor__grip\" aria-hidden=\"true\" />\n <span className=\"fractal-column-editor__name\">{item.label}</span>\n <div className=\"fractal-column-editor__actions\" onPointerDown={event => event.stopPropagation()}>\n {isHidden ? <Button buttonType=\"ghost\" controlSize=\"sm\" hasIconOnly icon={<Eye aria-hidden=\"true\" />} aria-label={`Show ${item.label} column`} onClick={() => commit([...committed, key], `${item.label} shown`)} /> : <>\n <Button buttonType=\"ghost\" controlSize=\"sm\" hasIconOnly icon={<ArrowUp aria-hidden=\"true\" />} aria-label={`Move ${item.label} up`} disabled={index === 0} onClick={() => move(key, -1)} />\n <Button buttonType=\"ghost\" controlSize=\"sm\" hasIconOnly icon={index === visible.length - 1 && !item.required ? <EyeSlash aria-hidden=\"true\" /> : <ArrowDown aria-hidden=\"true\" />} aria-label={index === visible.length - 1 && !item.required ? `Hide ${item.label} column` : `Move ${item.label} down`} disabled={index === visible.length - 1 && item.required} onClick={() => move(key, 1)} />\n </>}\n </div>\n </li>\n }\n return <div className={cn('fractal-column-editor', className)} ref={root}>\n <Button ref={trigger} controlSize=\"lg\" buttonType=\"secondary\" icon={<Gear aria-hidden=\"true\" />} iconPosition=\"right\" aria-haspopup=\"dialog\" aria-expanded={open} aria-controls={id} onClick={() => { cancel(); setOpen(current => !current) }}>{label}</Button>\n {open ? <div id={id} className=\"fractal-column-editor__panel\" ref={panel} role=\"dialog\" data-dragging={Boolean(preview) || undefined} aria-label={label} tabIndex={-1} onPointerMove={update} onPointerUp={finish} onPointerCancel={cancel} onLostPointerCapture={cancel}>\n <ol className=\"fractal-column-editor__list\" data-column-editor-group=\"visible\" aria-label=\"Visible columns\">{visible.map((key, index) => row(key, false, index))}</ol>\n <section data-column-editor-group=\"hidden\" className=\"fractal-column-editor__hidden\" aria-label={hiddenLabel}>\n <div className=\"fractal-column-editor__divider\"><span>{hiddenLabel}<EyeSlash aria-hidden=\"true\" /></span></div>\n <ol className=\"fractal-column-editor__list\">{hidden.map((key, index) => row(key, true, index))}</ol>\n {!hidden.length ? <p className=\"fractal-column-editor__empty\">Drag columns here to hide them.</p> : null}\n </section>\n <span className=\"fractal-column-editor__announcement\" role=\"status\" aria-live=\"polite\">{announcement}</span>\n </div> : null}\n </div>\n}\n","import * as React from 'react'\nimport { cn } from '../../utils/cn'\nimport type { ControlSize } from '../../utils/control-size'\nimport '../../styles/control.css'\nimport './Button.css'\n\nexport type ButtonType = 'primary' | 'secondary' | 'ghost' | 'danger'\nexport type ButtonSize = ControlSize\nexport type ButtonColor = 'blue' | 'pink' | 'orange' | 'green'\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual hierarchy. Primary = strongest action, Secondary = neutral bordered, Ghost = minimal, Danger = destructive. */\n buttonType?: ButtonType\n /** Shared visual density; takes precedence over the buttonSize alias. */\n controlSize?: ControlSize\n /** Backward-compatible alias for controlSize. */\n buttonSize?: ButtonSize\n /** Color theme — maps to the design system's color scales. */\n color?: ButtonColor\n /** Icon element. */\n icon?: React.ReactNode\n /** Whether the button displays only an icon (no text). Must include aria-label. */\n hasIconOnly?: boolean\n /** Position of the icon relative to the label. */\n iconPosition?: 'left' | 'right'\n}\n\n/*\n * Color palette mapping per the color guide's semantic stop roles:\n * 0 = bg (non-interactive reading surfaces)\n * 50 = surface (tertiary button fill — interactive)\n * 100 = surface-hover (tertiary button hover — darken)\n * 200 = surface-stroke (tertiary pressed, borders)\n * 500 = md-stroke (strong chromatic hover — lighten from 600)\n * 600 = strong (strong chromatic fill — rest)\n * 700 = strong-pressed (strong chromatic pressed — darken)\n */\nconst colorPalette = {\n blue: {\n 0: 'var(--blue-0)',\n 50: 'var(--blue-50)',\n 100: 'var(--blue-100)',\n 200: 'var(--blue-200)',\n 500: 'var(--blue-500)',\n 600: 'var(--blue-600)',\n 700: 'var(--blue-700)',\n },\n pink: {\n 0: 'var(--pink-0)',\n 50: 'var(--pink-50)',\n 100: 'var(--pink-100)',\n 200: 'var(--pink-200)',\n 500: 'var(--pink-500)',\n 600: 'var(--pink-600)',\n 700: 'var(--pink-700)',\n },\n orange: {\n 0: 'var(--orange-0)',\n 50: 'var(--orange-50)',\n 100: 'var(--orange-100)',\n 200: 'var(--orange-200)',\n 500: 'var(--orange-500)',\n 600: 'var(--orange-600)',\n 700: 'var(--orange-700)',\n },\n green: {\n 0: 'var(--green-0)',\n 50: 'var(--green-50)',\n 100: 'var(--green-100)',\n 200: 'var(--green-200)',\n 500: 'var(--green-500)',\n 600: 'var(--green-600)',\n 700: 'var(--green-700)',\n },\n}\n\nfunction getButtonStyles(\n buttonType: ButtonType,\n isHovered: boolean,\n isActive: boolean,\n disabled: boolean,\n color: ButtonColor,\n hasIconOnly: boolean | undefined,\n): React.CSSProperties {\n const palette = colorPalette[color]\n\n const base: React.CSSProperties = {\n borderRadius: hasIconOnly ? 'var(--radius-lg, 8px)' : 'var(--radius-full, 9999px)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: 'background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease',\n }\n\n if (hasIconOnly) {\n base.justifyContent = 'center'\n }\n\n // Disabled: bordered buttons get transparent bg + disabled border + disabled text.\n // Others just get opacity: 0.5 (handled by CSS).\n // Per principles.md Interaction States > Disabled\n if (disabled) {\n if (buttonType === 'ghost') {\n return { ...base, backgroundColor: 'transparent', border: '1px solid transparent', color: 'var(--fg-disabled)' }\n }\n return { ...base, backgroundColor: 'transparent', border: '1px solid var(--fg-disabled)', color: 'var(--fg-disabled)' }\n }\n\n switch (buttonType) {\n // Primary with text: dark pill. Icon-only: color palette (Synapse style).\n case 'primary':\n if (hasIconOnly) {\n return {\n ...base,\n backgroundColor: isActive ? palette[700] : isHovered ? palette[500] : palette[600],\n border: '1px solid transparent',\n color: 'var(--fg-inverse)',\n }\n }\n return {\n ...base,\n backgroundColor: 'var(--fg-primary)',\n border: '1px solid var(--fg-primary)',\n color: 'var(--fg-inverse)',\n ...(isHovered || isActive ? { opacity: 0.85 } : {}),\n }\n\n // Secondary: neutral. Rest=canvas, hover=surface-1 (darken), pressed=surface-2. Subtle border, dark text.\n case 'secondary':\n return {\n ...base,\n backgroundColor: isActive ? 'var(--bg-surface-2)' : isHovered ? 'var(--bg-surface-1)' : 'var(--bg-canvas)',\n border: '1px solid var(--border-subtle)',\n color: 'var(--fg-primary)',\n }\n\n // Ghost: no border. Rest=transparent, hover/press=surface-1.\n case 'ghost':\n return {\n ...base,\n backgroundColor: isHovered || isActive ? 'var(--bg-surface-1)' : 'transparent',\n border: '1px solid transparent',\n color: 'var(--fg-primary)',\n }\n\n // Danger: low-intensity destructive action. Text stays steady; hover deepens the light red surface.\n case 'danger':\n return {\n ...base,\n backgroundColor: isHovered || isActive ? 'var(--danger-button-surface-hover)' : 'var(--danger-button-surface)',\n border: '1px solid transparent',\n color: 'var(--danger-button-fg)',\n }\n\n default:\n return base\n }\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, buttonType = 'primary', buttonSize = 'md', controlSize, color = 'blue', icon, hasIconOnly, iconPosition = 'left', style, disabled, children, ...props }, ref) => {\n const [isHovered, setIsHovered] = React.useState(false)\n const [isActive, setIsActive] = React.useState(false)\n\n const typeStyles = getButtonStyles(buttonType, isHovered, isActive, !!disabled, color, hasIconOnly)\n const hasIconAndText = icon && children && !hasIconOnly\n const finalStyles = hasIconAndText\n ? { ...typeStyles, gap: 'var(--space-2, 8px)', ...style }\n : { ...typeStyles, ...style }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n data-control-size={controlSize ?? buttonSize}\n data-icon-only={hasIconOnly || undefined}\n className={cn('fractal-control', 'fractal-button', `fractal-button--${buttonType}`, className)}\n style={finalStyles}\n disabled={disabled}\n onMouseEnter={(e) => { setIsHovered(true); props.onMouseEnter?.(e) }}\n onMouseLeave={(e) => { setIsHovered(false); setIsActive(false); props.onMouseLeave?.(e) }}\n onMouseDown={(e) => { setIsActive(true); props.onMouseDown?.(e) }}\n onMouseUp={(e) => { setIsActive(false); props.onMouseUp?.(e) }}\n {...props}\n >\n {iconPosition !== 'right' && icon}\n {!hasIconOnly && children}\n {iconPosition === 'right' && icon}\n </button>\n )\n }\n)\nButton.displayName = 'Button'\n\nexport { Button }\n","import { clsx, type ClassValue } from 'clsx'\n\nexport function cn(...inputs: ClassValue[]) {\n return clsx(inputs)\n}\n"],"mappings":"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","names":["React","import_jsx_runtime","row","item"]}
@@ -0,0 +1,36 @@
1
+ .fractal-column-editor { position: relative; }
2
+ .fractal-column-editor__panel {
3
+ position: absolute; z-index: 60; top: calc(100% + var(--space-2)); right: 0;
4
+ width: min(440px, calc(100vw - var(--space-7))); box-sizing: border-box;
5
+ padding: var(--space-4); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
6
+ background: var(--bg-canvas); box-shadow: var(--shadow-md); max-height: 72dvh; overflow-y: auto;
7
+ font-family: var(--font-sans); font-size: .875rem;
8
+ }
9
+ .fractal-column-editor__panel:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
10
+ .fractal-column-editor__list { display: grid; gap: var(--space-2); padding: 0; margin: 0; list-style: none; min-height: var(--space-4); }
11
+ .fractal-column-editor__row {
12
+ display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2);
13
+ border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
14
+ color: var(--fg-primary); background: var(--bg-canvas); cursor: grab;
15
+ user-select: none; touch-action: none;
16
+ transition: background-color var(--duration-fast) var(--ease-out), border-color var(--duration-fast) var(--ease-out);
17
+ }
18
+ .fractal-column-editor__row[data-hidden] { color: var(--fg-disabled); background: var(--bg-surface-1); }
19
+ .fractal-column-editor__row:hover { background: var(--hover-surface); }
20
+ .fractal-column-editor__row[data-dragging] { cursor: grabbing; background: var(--nav-selected); border-color: var(--border-focus); }
21
+ .fractal-column-editor__grip { width: 20px; height: 20px; flex-shrink: 0; color: inherit; pointer-events: none; }
22
+ .fractal-column-editor__name { min-width: 0; pointer-events: none; }
23
+ .fractal-column-editor__actions { display: flex; flex-shrink: 0; margin-left: auto; cursor: default; }
24
+ /* An arrow has its own stronger hover against the hovered row. */
25
+ .fractal-column-editor__actions .fractal-button { cursor: pointer !important; color: var(--fg-secondary); }
26
+ .fractal-column-editor__actions .fractal-button:hover:not(:disabled) { background: var(--bg-surface-2) !important; }
27
+ .fractal-column-editor__actions .fractal-button:disabled { pointer-events: auto; cursor: pointer !important; }
28
+ .fractal-column-editor__divider { display: flex; align-items: center; gap: var(--space-3); margin-block: var(--space-6); color: var(--fg-tertiary); }
29
+ .fractal-column-editor__divider::before, .fractal-column-editor__divider::after { content: ''; flex: 1; border-top: 1px solid var(--border-subtle); }
30
+ .fractal-column-editor__divider > span { display: flex; gap: var(--space-2); align-items: center; white-space: nowrap; }
31
+ .fractal-column-editor__divider svg { width: 20px; height: 20px; }
32
+ .fractal-column-editor__empty { margin: 0; padding-block: var(--space-4); color: var(--fg-tertiary); }
33
+ .fractal-column-editor__announcement { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
34
+ @media (prefers-reduced-motion: reduce) { .fractal-column-editor__row { transition: none; } }
35
+
36
+ .fractal-column-editor__panel[data-dragging], .fractal-column-editor__panel[data-dragging] .fractal-column-editor__row { cursor: grabbing; }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/styles/control.css","../src/components/Button/Button.css","../src/components/ColumnEditor/ColumnEditor.css"],"sourcesContent":["/* Shared metrics are canonical Fractal tokens; choose them per control. */\n[data-control-size=\"xs\"] {\n --control-font-size: var(--control-xs-font-size);\n --control-line-height: var(--control-xs-line-height);\n --control-padding-block: var(--control-xs-padding-block);\n --control-padding-inline: var(--control-xs-padding-inline);\n --control-icon-size: var(--control-xs-icon-size);\n}\n[data-control-size=\"sm\"] {\n --control-font-size: var(--control-sm-font-size);\n --control-line-height: var(--control-sm-line-height);\n --control-padding-block: var(--control-sm-padding-block);\n --control-padding-inline: var(--control-sm-padding-inline);\n --control-icon-size: var(--control-sm-icon-size);\n}\n[data-control-size=\"md\"] {\n --control-font-size: var(--control-md-font-size);\n --control-line-height: var(--control-md-line-height);\n --control-padding-block: var(--control-md-padding-block);\n --control-padding-inline: var(--control-md-padding-inline);\n --control-icon-size: var(--control-md-icon-size);\n}\n[data-control-size=\"lg\"] {\n --control-font-size: var(--control-lg-font-size);\n --control-line-height: var(--control-lg-line-height);\n --control-padding-block: var(--control-lg-padding-block);\n --control-padding-inline: var(--control-lg-padding-inline);\n --control-icon-size: var(--control-lg-icon-size);\n}\n[data-control-size=\"xl\"] {\n --control-font-size: var(--control-xl-font-size);\n --control-line-height: var(--control-xl-line-height);\n --control-padding-block: var(--control-xl-padding-block);\n --control-padding-inline: var(--control-xl-padding-inline);\n --control-icon-size: var(--control-xl-icon-size);\n}\n\n[data-control-size] {\n --control-min-block-size: calc(var(--control-line-height) + 2 * var(--control-padding-block) + 2 * var(--control-border-width));\n}\n\n.fractal-control {\n box-sizing: border-box;\n height: auto;\n min-block-size: var(--control-min-block-size);\n margin: 0;\n padding: var(--control-padding-block) var(--control-padding-inline);\n font-family: var(--font-sans);\n font-size: var(--control-font-size);\n font-weight: 400;\n line-height: var(--control-line-height);\n letter-spacing: normal;\n}\n\n.fractal-field {\n display: grid;\n align-content: start;\n gap: var(--control-label-gap);\n min-inline-size: 0;\n}\n",".fractal-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n white-space: nowrap;\n font-weight: 400;\n font-family: var(--font-sans);\n text-decoration: none;\n box-sizing: border-box;\n transform: scale(1);\n transition: transform 120ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n\n/* Press: subtle shrink, then expand on release. 120ms / ease-out per motion guide. */\n.fractal-button--primary:active:not(:disabled),\n.fractal-button--secondary:active:not(:disabled),\n.fractal-button--ghost:active:not(:disabled),\n.fractal-button--danger:active:not(:disabled) {\n transform: scale(0.97);\n}\n\n/* Focus ring: 2px --border-focus, 2px offset. Per principles.md Focus section. */\n.fractal-button:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--bg-canvas, #fff), 0 0 0 4px var(--border-focus, var(--blue-500));\n}\n\n.fractal-button:disabled {\n opacity: 0.5;\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .fractal-button {\n transition-duration: 0.01ms;\n }\n}\n\n.fractal-button { gap: var(--control-icon-gap); }\n.fractal-button svg { inline-size: var(--control-icon-size); block-size: var(--control-icon-size); flex-shrink: 0; }\n.fractal-button[data-icon-only] {\n inline-size: var(--control-min-block-size);\n padding-inline: 0;\n}\n",".fractal-column-editor { position: relative; }\n.fractal-column-editor__panel {\n position: absolute; z-index: 60; top: calc(100% + var(--space-2)); right: 0;\n width: min(440px, calc(100vw - var(--space-7))); box-sizing: border-box;\n padding: var(--space-4); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);\n background: var(--bg-canvas); box-shadow: var(--shadow-md); max-height: 72dvh; overflow-y: auto;\n font-family: var(--font-sans); font-size: .875rem;\n}\n.fractal-column-editor__panel:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }\n.fractal-column-editor__list { display: grid; gap: var(--space-2); padding: 0; margin: 0; list-style: none; min-height: var(--space-4); }\n.fractal-column-editor__row {\n display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2);\n border: 1px solid var(--border-subtle); border-radius: var(--radius-md);\n color: var(--fg-primary); background: var(--bg-canvas); cursor: grab;\n user-select: none; touch-action: none;\n transition: background-color var(--duration-fast) var(--ease-out), border-color var(--duration-fast) var(--ease-out);\n}\n.fractal-column-editor__row[data-hidden] { color: var(--fg-disabled); background: var(--bg-surface-1); }\n.fractal-column-editor__row:hover { background: var(--hover-surface); }\n.fractal-column-editor__row[data-dragging] { cursor: grabbing; background: var(--nav-selected); border-color: var(--border-focus); }\n.fractal-column-editor__grip { width: 20px; height: 20px; flex-shrink: 0; color: inherit; pointer-events: none; }\n.fractal-column-editor__name { min-width: 0; pointer-events: none; }\n.fractal-column-editor__actions { display: flex; flex-shrink: 0; margin-left: auto; cursor: default; }\n/* An arrow has its own stronger hover against the hovered row. */\n.fractal-column-editor__actions .fractal-button { cursor: pointer !important; color: var(--fg-secondary); }\n.fractal-column-editor__actions .fractal-button:hover:not(:disabled) { background: var(--bg-surface-2) !important; }\n.fractal-column-editor__actions .fractal-button:disabled { pointer-events: auto; cursor: pointer !important; }\n.fractal-column-editor__divider { display: flex; align-items: center; gap: var(--space-3); margin-block: var(--space-6); color: var(--fg-tertiary); }\n.fractal-column-editor__divider::before, .fractal-column-editor__divider::after { content: ''; flex: 1; border-top: 1px solid var(--border-subtle); }\n.fractal-column-editor__divider > span { display: flex; gap: var(--space-2); align-items: center; white-space: nowrap; }\n.fractal-column-editor__divider svg { width: 20px; height: 20px; }\n.fractal-column-editor__empty { margin: 0; padding-block: var(--space-4); color: var(--fg-tertiary); }\n.fractal-column-editor__announcement { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n@media (prefers-reduced-motion: reduce) { .fractal-column-editor__row { transition: none; } }\n\n.fractal-column-editor__panel[data-dragging], .fractal-column-editor__panel[data-dragging] .fractal-column-editor__row { cursor: grabbing; }\n"],"mappings":"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","names":[]}
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+
3
+ interface ColumnEditorItem {
4
+ id: string;
5
+ label: string;
6
+ required?: boolean;
7
+ }
8
+ interface ColumnEditorProps {
9
+ items: ColumnEditorItem[];
10
+ /** Visible column IDs in display order. Required items cannot be hidden. */
11
+ value: string[];
12
+ onChange: (value: string[]) => void;
13
+ label?: string;
14
+ hiddenLabel?: string;
15
+ className?: string;
16
+ }
17
+ /** Reorderable visible/hidden columns, with pointer preview and keyboard actions. */
18
+ declare function ColumnEditor({ items, value, onChange, label, hiddenLabel, className }: ColumnEditorProps): React.JSX.Element;
19
+
20
+ export { ColumnEditor, type ColumnEditorItem, type ColumnEditorProps };
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+
3
+ interface ColumnEditorItem {
4
+ id: string;
5
+ label: string;
6
+ required?: boolean;
7
+ }
8
+ interface ColumnEditorProps {
9
+ items: ColumnEditorItem[];
10
+ /** Visible column IDs in display order. Required items cannot be hidden. */
11
+ value: string[];
12
+ onChange: (value: string[]) => void;
13
+ label?: string;
14
+ hiddenLabel?: string;
15
+ className?: string;
16
+ }
17
+ /** Reorderable visible/hidden columns, with pointer preview and keyboard actions. */
18
+ declare function ColumnEditor({ items, value, onChange, label, hiddenLabel, className }: ColumnEditorProps): React.JSX.Element;
19
+
20
+ export { ColumnEditor, type ColumnEditorItem, type ColumnEditorProps };
@@ -0,0 +1,7 @@
1
+ import {
2
+ ColumnEditor
3
+ } from "./chunk-QZX655IA.js";
4
+ export {
5
+ ColumnEditor
6
+ };
7
+ //# sourceMappingURL=column-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}