@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.
package/README.md CHANGED
@@ -51,3 +51,34 @@ Use `controlSize="xs" | "sm" | "md" | "lg" | "xl"` on Button, Dropdown, Input, N
51
51
  Compose a persistent label and control in `className="fractal-field"` to use the shared label gap. For dropdowns, connect `Label.htmlFor` to `Dropdown.triggerId` and provide a meaningful `triggerLabel`. Use matching sizes across a row. Icons add a shared leading slot; centered action labels remain centered.
52
52
 
53
53
  This changes the rendered geometry of existing size presets, including default controls. Remove product CSS that hardcodes the previous heights. Compact sizes suit pointer interfaces; choose an appropriately large target for touch. Custom multiline dropdown content may grow beyond the single-line minimum.
54
+
55
+ ### Table sizing and scrolling
56
+
57
+ `DataTable` and `SortableTable` default to `layout="fill"`: columns start at capped content-based widths, then share spare space when they fit inside the container. When their combined preferred width exceeds the container, those widths remain compact and the table scrolls horizontally. Selection and action columns stay fixed. Use `layout="content"` to explicitly opt out of filling available space. Cells and headers do not wrap. Cell scroll viewports reach the cell borders and include the full vertical hitbox. Padding lives inside the moving content, and pinned identity columns retain a trailing separator. Continuing a horizontal gesture at either cell edge scrolls the enclosing table in the same direction. Individual cell scrollbars are hidden; the table viewport retains its scrollbar. Overlong values can scroll within their own cell; overflowing cell and table regions support keyboard focus and horizontal arrow navigation.
58
+
59
+ Column `minWidth` and `maxWidth` accept pixel numbers or CSS lengths and include cell padding. `width` remains a preferred CSS width; percentage widths are supported by the default `layout="fill"`. The cap controls preferred content width before spare space is distributed. The default column caps are 256px for containers up to 640px, 320px up to 1024px, and 384px above that (at the standard root text size). These caps use the shared layout tokens and can be overridden per column. A pinned first column is additionally capped to 55% of the table container so it leaves other columns visible.
60
+
61
+ Set `stickyFirstColumn` to retain the first data column and any selection column during horizontal scroll. Set `stickyHeader` together with `maxHeight` (for example, `maxHeight={360}`) to keep headers visible during vertical scrolling inside the table. Header pinning is relative to the table viewport, not page scrolling.
62
+
63
+ This changes default table geometry and adds cell-content wrappers. Existing custom cell selectors may need updating. Move percentage-width compositions to `layout="fill"`, or remove those percentages to adopt content sizing. Neither layout mode wraps values.
64
+
65
+ Set `overflowMenu={{ items: row => [{ value: 'inspect', label: 'Inspect experiment' }], onSelect: (value, row) => { /* handle action */ }, getLabel: row => `Actions for ${row.id}` }}` on either table to add a 48px trailing column pinned to the viewport edge. It works independently of `stickyFirstColumn`; with both enabled, middle columns scroll between the two edges. Do not also add a custom actions column. Items can include an icon or `disabled`; `isDisabled(row, index)` disables the entire row menu, and SortableTable automatically respects disabled rows.
66
+
67
+ Menus render in a portal to escape table clipping, support keyboard navigation and Escape, and do not activate table rows. All table cells vertically center their content by default.
68
+
69
+ ### Aligned application headers
70
+
71
+ `SidebarHeader` and the exported CSS class `fractal-shell-header` use the same `--shell-header-height` token (56px at the default root, including the divider). Apply the class to the adjacent semantic `<header>` containing breadcrumbs or navigation. Keep page titles and descriptions below it. Import both package tokens and component CSS.
72
+
73
+ ```tsx
74
+ <div className="app-shell">
75
+ <Sidebar><SidebarHeader><SidebarBrand>Workspace</SidebarBrand></SidebarHeader></Sidebar>
76
+ <main><header className="fractal-shell-header">Navigation</header></main>
77
+ </div>
78
+ ```
79
+
80
+ No size prop is needed. If more room is required, override the token once on the common shell ancestor so both headers stay aligned. Remove local header height/min-height and vertical-padding overrides when adopting this default. The header does not clip overflow; keep content to a single navigation row and adapt the shared shell for enlarged text or controls.
81
+
82
+ ### Dropdown placement and scrolling
83
+
84
+ Dropdown menus default to a 320px maximum height and scroll internally. Set `maxHeight` (pixels) to change the cap. Available space can reduce the height further. `direction` and `align` express preferred placement; the menu flips and shifts within the viewport and ancestor scroll containers, with an 8px inset configurable through `collisionPadding`. Placement updates while scrolling or resizing, and long option text wraps to fit narrow viewports. Use Arrow keys, Home/End, and Escape to navigate or dismiss an open menu.
@@ -0,0 +1,344 @@
1
+ // src/utils/cn.ts
2
+ import { clsx } from "clsx";
3
+ function cn(...inputs) {
4
+ return clsx(inputs);
5
+ }
6
+
7
+ // src/components/Button/Button.tsx
8
+ import * as React from "react";
9
+ import { jsxs } from "react/jsx-runtime";
10
+ var colorPalette = {
11
+ blue: {
12
+ 0: "var(--blue-0)",
13
+ 50: "var(--blue-50)",
14
+ 100: "var(--blue-100)",
15
+ 200: "var(--blue-200)",
16
+ 500: "var(--blue-500)",
17
+ 600: "var(--blue-600)",
18
+ 700: "var(--blue-700)"
19
+ },
20
+ pink: {
21
+ 0: "var(--pink-0)",
22
+ 50: "var(--pink-50)",
23
+ 100: "var(--pink-100)",
24
+ 200: "var(--pink-200)",
25
+ 500: "var(--pink-500)",
26
+ 600: "var(--pink-600)",
27
+ 700: "var(--pink-700)"
28
+ },
29
+ orange: {
30
+ 0: "var(--orange-0)",
31
+ 50: "var(--orange-50)",
32
+ 100: "var(--orange-100)",
33
+ 200: "var(--orange-200)",
34
+ 500: "var(--orange-500)",
35
+ 600: "var(--orange-600)",
36
+ 700: "var(--orange-700)"
37
+ },
38
+ green: {
39
+ 0: "var(--green-0)",
40
+ 50: "var(--green-50)",
41
+ 100: "var(--green-100)",
42
+ 200: "var(--green-200)",
43
+ 500: "var(--green-500)",
44
+ 600: "var(--green-600)",
45
+ 700: "var(--green-700)"
46
+ }
47
+ };
48
+ function getButtonStyles(buttonType, isHovered, isActive, disabled, color, hasIconOnly) {
49
+ const palette = colorPalette[color];
50
+ const base = {
51
+ borderRadius: hasIconOnly ? "var(--radius-lg, 8px)" : "var(--radius-full, 9999px)",
52
+ cursor: disabled ? "not-allowed" : "pointer",
53
+ transition: "background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease"
54
+ };
55
+ if (hasIconOnly) {
56
+ base.justifyContent = "center";
57
+ }
58
+ if (disabled) {
59
+ if (buttonType === "ghost") {
60
+ return { ...base, backgroundColor: "transparent", border: "1px solid transparent", color: "var(--fg-disabled)" };
61
+ }
62
+ return { ...base, backgroundColor: "transparent", border: "1px solid var(--fg-disabled)", color: "var(--fg-disabled)" };
63
+ }
64
+ switch (buttonType) {
65
+ // Primary with text: dark pill. Icon-only: color palette (Synapse style).
66
+ case "primary":
67
+ if (hasIconOnly) {
68
+ return {
69
+ ...base,
70
+ backgroundColor: isActive ? palette[700] : isHovered ? palette[500] : palette[600],
71
+ border: "1px solid transparent",
72
+ color: "var(--fg-inverse)"
73
+ };
74
+ }
75
+ return {
76
+ ...base,
77
+ backgroundColor: "var(--fg-primary)",
78
+ border: "1px solid var(--fg-primary)",
79
+ color: "var(--fg-inverse)",
80
+ ...isHovered || isActive ? { opacity: 0.85 } : {}
81
+ };
82
+ // Secondary: neutral. Rest=canvas, hover=surface-1 (darken), pressed=surface-2. Subtle border, dark text.
83
+ case "secondary":
84
+ return {
85
+ ...base,
86
+ backgroundColor: isActive ? "var(--bg-surface-2)" : isHovered ? "var(--bg-surface-1)" : "var(--bg-canvas)",
87
+ border: "1px solid var(--border-subtle)",
88
+ color: "var(--fg-primary)"
89
+ };
90
+ // Ghost: no border. Rest=transparent, hover/press=surface-1.
91
+ case "ghost":
92
+ return {
93
+ ...base,
94
+ backgroundColor: isHovered || isActive ? "var(--bg-surface-1)" : "transparent",
95
+ border: "1px solid transparent",
96
+ color: "var(--fg-primary)"
97
+ };
98
+ // Danger: low-intensity destructive action. Text stays steady; hover deepens the light red surface.
99
+ case "danger":
100
+ return {
101
+ ...base,
102
+ backgroundColor: isHovered || isActive ? "var(--danger-button-surface-hover)" : "var(--danger-button-surface)",
103
+ border: "1px solid transparent",
104
+ color: "var(--danger-button-fg)"
105
+ };
106
+ default:
107
+ return base;
108
+ }
109
+ }
110
+ var Button = React.forwardRef(
111
+ ({ className, buttonType = "primary", buttonSize = "md", controlSize, color = "blue", icon, hasIconOnly, iconPosition = "left", style, disabled, children, ...props }, ref) => {
112
+ const [isHovered, setIsHovered] = React.useState(false);
113
+ const [isActive, setIsActive] = React.useState(false);
114
+ const typeStyles = getButtonStyles(buttonType, isHovered, isActive, !!disabled, color, hasIconOnly);
115
+ const hasIconAndText = icon && children && !hasIconOnly;
116
+ const finalStyles = hasIconAndText ? { ...typeStyles, gap: "var(--space-2, 8px)", ...style } : { ...typeStyles, ...style };
117
+ return /* @__PURE__ */ jsxs(
118
+ "button",
119
+ {
120
+ ref,
121
+ type: "button",
122
+ "data-control-size": controlSize ?? buttonSize,
123
+ "data-icon-only": hasIconOnly || void 0,
124
+ className: cn("fractal-control", "fractal-button", `fractal-button--${buttonType}`, className),
125
+ style: finalStyles,
126
+ disabled,
127
+ onMouseEnter: (e) => {
128
+ setIsHovered(true);
129
+ props.onMouseEnter?.(e);
130
+ },
131
+ onMouseLeave: (e) => {
132
+ setIsHovered(false);
133
+ setIsActive(false);
134
+ props.onMouseLeave?.(e);
135
+ },
136
+ onMouseDown: (e) => {
137
+ setIsActive(true);
138
+ props.onMouseDown?.(e);
139
+ },
140
+ onMouseUp: (e) => {
141
+ setIsActive(false);
142
+ props.onMouseUp?.(e);
143
+ },
144
+ ...props,
145
+ children: [
146
+ iconPosition !== "right" && icon,
147
+ !hasIconOnly && children,
148
+ iconPosition === "right" && icon
149
+ ]
150
+ }
151
+ );
152
+ }
153
+ );
154
+ Button.displayName = "Button";
155
+
156
+ // src/components/ColumnEditor/ColumnEditor.tsx
157
+ import * as React2 from "react";
158
+ import { ArrowUp, ArrowDown, DotsSixVertical, Eye, EyeSlash, Gear } from "@mirror-physics/fractal-icons";
159
+ import { Fragment, jsx, jsxs as jsxs2 } from "react/jsx-runtime";
160
+ var equal = (a, b) => a.length === b.length && a.every((id, index) => id === b[index]);
161
+ function ColumnEditor({ items, value, onChange, label = "Edit columns", hiddenLabel = "Hidden columns", className }) {
162
+ const [open, setOpen] = React2.useState(false);
163
+ const [preview, setPreview] = React2.useState(null);
164
+ const [announcement, setAnnouncement] = React2.useState("");
165
+ const root = React2.useRef(null);
166
+ const trigger = React2.useRef(null);
167
+ const panel = React2.useRef(null);
168
+ const drag = React2.useRef(null);
169
+ const positions = React2.useRef(null);
170
+ const id = React2.useId();
171
+ function capturePositions() {
172
+ positions.current = new Map(Array.from(panel.current?.querySelectorAll("[data-column-editor-item]") ?? []).map((row2) => [row2.dataset.columnEditorItem, row2.getBoundingClientRect()]));
173
+ }
174
+ React2.useLayoutEffect(() => {
175
+ const before = positions.current;
176
+ positions.current = null;
177
+ if (!before || !panel.current || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
178
+ const styles = getComputedStyle(panel.current);
179
+ const token = styles.getPropertyValue("--duration-normal").trim();
180
+ const duration = parseFloat(token) * (token.endsWith("ms") ? 1 : 1e3);
181
+ const easing = styles.getPropertyValue("--ease-out").trim();
182
+ for (const row2 of panel.current.querySelectorAll("[data-column-editor-item]")) {
183
+ const previous = before.get(row2.dataset.columnEditorItem);
184
+ row2.getAnimations().filter((animation) => animation.id === "fractal-column-position").forEach((animation) => animation.cancel());
185
+ if (!previous) continue;
186
+ const next = row2.getBoundingClientRect();
187
+ const x = previous.left - next.left, y = previous.top - next.top;
188
+ if (Math.abs(x) < 1 && Math.abs(y) < 1) continue;
189
+ row2.animate([{ transform: `translate(${x}px, ${y}px)` }, { transform: "translate(0, 0)" }], { duration, easing, id: "fractal-column-position" });
190
+ }
191
+ });
192
+ const valid = [...new Set(value)].filter((key) => items.some((item) => item.id === key));
193
+ const committed = [...valid, ...items.filter((item) => item.required && !valid.includes(item.id)).map((item) => item.id)];
194
+ const visible = preview?.visible ?? committed;
195
+ const hidden = preview?.hidden ?? items.filter((item) => !committed.includes(item.id)).map((item) => item.id);
196
+ function cancel() {
197
+ capturePositions();
198
+ drag.current = null;
199
+ setPreview(null);
200
+ }
201
+ React2.useEffect(() => {
202
+ if (!open) return;
203
+ panel.current?.focus();
204
+ function outside(event) {
205
+ if (!drag.current && !root.current?.contains(event.target)) setOpen(false);
206
+ }
207
+ function escape(event) {
208
+ if (event.key !== "Escape") return;
209
+ event.preventDefault();
210
+ if (drag.current) {
211
+ capturePositions();
212
+ drag.current = null;
213
+ setPreview(null);
214
+ setAnnouncement("Reordering cancelled");
215
+ } else {
216
+ setOpen(false);
217
+ trigger.current?.focus();
218
+ }
219
+ }
220
+ function blur(event) {
221
+ if (event.relatedTarget instanceof Node && !root.current?.contains(event.relatedTarget)) setOpen(false);
222
+ }
223
+ const element = root.current;
224
+ document.addEventListener("pointerdown", outside);
225
+ document.addEventListener("keydown", escape);
226
+ element?.addEventListener("focusout", blur);
227
+ return () => {
228
+ document.removeEventListener("pointerdown", outside);
229
+ document.removeEventListener("keydown", escape);
230
+ element?.removeEventListener("focusout", blur);
231
+ };
232
+ }, [open]);
233
+ function commit(next, message) {
234
+ capturePositions();
235
+ onChange(next);
236
+ setAnnouncement(message);
237
+ }
238
+ function move(key, offset) {
239
+ const index = committed.indexOf(key);
240
+ const next = committed.filter((item) => item !== key);
241
+ if (offset > 0 && index === committed.length - 1 && !items.find((item) => item.id === key)?.required) {
242
+ commit(next, `${items.find((item) => item.id === key)?.label} hidden`);
243
+ return;
244
+ }
245
+ next.splice(Math.max(0, Math.min(next.length, index + offset)), 0, key);
246
+ commit(next, `${items.find((item) => item.id === key)?.label} moved ${offset < 0 ? "up" : "down"}`);
247
+ }
248
+ function start(event, key) {
249
+ if (event.button !== 0 || event.target.closest("button")) return;
250
+ drag.current = { id: key, pointerId: event.pointerId, x: event.clientX, y: event.clientY, active: false, visible: [...committed], hidden: [...hidden] };
251
+ panel.current?.setPointerCapture(event.pointerId);
252
+ event.preventDefault();
253
+ }
254
+ function update(event) {
255
+ const current = drag.current;
256
+ if (!current || event.pointerId !== current.pointerId) return;
257
+ if (!current.active && Math.hypot(event.clientX - current.x, event.clientY - current.y) < 4) return;
258
+ current.active = true;
259
+ const container = panel.current;
260
+ if (!container) return;
261
+ const bounds = container.getBoundingClientRect();
262
+ const hiddenGroup = container.querySelector('[data-column-editor-group="hidden"]');
263
+ const inside = event.clientX >= bounds.left && event.clientX <= bounds.right && event.clientY >= bounds.top && event.clientY <= bounds.bottom;
264
+ if (!inside) return;
265
+ const destination = event.clientY >= hiddenGroup.getBoundingClientRect().top ? "hidden" : "visible";
266
+ if (destination === "hidden" && items.find((item) => item.id === current.id)?.required) return;
267
+ const slots = Array.from(container.querySelectorAll(`[data-column-editor-group="${destination}"] [data-column-editor-item]`));
268
+ const localY = event.clientY - bounds.top + container.scrollTop - container.clientTop;
269
+ const ownSlot = slots.find((row2) => row2.dataset.columnEditorItem === current.id);
270
+ if (ownSlot && localY >= ownSlot.offsetTop && localY <= ownSlot.offsetTop + ownSlot.offsetHeight) return;
271
+ const nextVisible = current.visible.filter((key) => key !== current.id);
272
+ let nextHidden = current.hidden.filter((key) => key !== current.id);
273
+ if (destination === "visible") {
274
+ const index = slots.filter((row2) => row2.dataset.columnEditorItem !== current.id && localY > row2.offsetTop + row2.offsetHeight / 2).length;
275
+ nextVisible.splice(index, 0, current.id);
276
+ } else {
277
+ nextHidden.push(current.id);
278
+ nextHidden = items.filter((item) => nextHidden.includes(item.id)).map((item) => item.id);
279
+ }
280
+ if (!equal(nextVisible, current.visible) || !equal(nextHidden, current.hidden)) {
281
+ capturePositions();
282
+ current.visible = nextVisible;
283
+ current.hidden = nextHidden;
284
+ }
285
+ setPreview({ visible: current.visible, hidden: current.hidden, active: current.id });
286
+ if (event.clientY < bounds.top + 32) container.scrollTop -= 12;
287
+ else if (event.clientY > bounds.bottom - 32) container.scrollTop += 12;
288
+ }
289
+ function finish(event) {
290
+ const current = drag.current;
291
+ if (!current || current.pointerId !== event.pointerId) return;
292
+ const hit = document.elementFromPoint(event.clientX, event.clientY);
293
+ if (current.active && hit && panel.current?.contains(hit)) commit(current.visible, "Column order updated");
294
+ cancel();
295
+ if (panel.current?.hasPointerCapture(event.pointerId)) panel.current.releasePointerCapture(event.pointerId);
296
+ }
297
+ function row(key, isHidden, index) {
298
+ const item = items.find((item2) => item2.id === key);
299
+ return /* @__PURE__ */ jsxs2(
300
+ "li",
301
+ {
302
+ className: "fractal-column-editor__row",
303
+ "data-column-editor-item": key,
304
+ "data-hidden": isHidden || void 0,
305
+ "data-dragging": preview?.active === key || void 0,
306
+ onPointerDown: (event) => start(event, key),
307
+ children: [
308
+ /* @__PURE__ */ jsx(DotsSixVertical, { className: "fractal-column-editor__grip", "aria-hidden": "true" }),
309
+ /* @__PURE__ */ jsx("span", { className: "fractal-column-editor__name", children: item.label }),
310
+ /* @__PURE__ */ jsx("div", { className: "fractal-column-editor__actions", onPointerDown: (event) => event.stopPropagation(), children: isHidden ? /* @__PURE__ */ jsx(Button, { buttonType: "ghost", controlSize: "sm", hasIconOnly: true, icon: /* @__PURE__ */ jsx(Eye, { "aria-hidden": "true" }), "aria-label": `Show ${item.label} column`, onClick: () => commit([...committed, key], `${item.label} shown`) }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
311
+ /* @__PURE__ */ jsx(Button, { buttonType: "ghost", controlSize: "sm", hasIconOnly: true, icon: /* @__PURE__ */ jsx(ArrowUp, { "aria-hidden": "true" }), "aria-label": `Move ${item.label} up`, disabled: index === 0, onClick: () => move(key, -1) }),
312
+ /* @__PURE__ */ jsx(Button, { buttonType: "ghost", controlSize: "sm", hasIconOnly: true, icon: index === visible.length - 1 && !item.required ? /* @__PURE__ */ jsx(EyeSlash, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(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) })
313
+ ] }) })
314
+ ]
315
+ },
316
+ key
317
+ );
318
+ }
319
+ return /* @__PURE__ */ jsxs2("div", { className: cn("fractal-column-editor", className), ref: root, children: [
320
+ /* @__PURE__ */ jsx(Button, { ref: trigger, controlSize: "lg", buttonType: "secondary", icon: /* @__PURE__ */ jsx(Gear, { "aria-hidden": "true" }), iconPosition: "right", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": id, onClick: () => {
321
+ cancel();
322
+ setOpen((current) => !current);
323
+ }, children: label }),
324
+ open ? /* @__PURE__ */ jsxs2("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: [
325
+ /* @__PURE__ */ 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)) }),
326
+ /* @__PURE__ */ jsxs2("section", { "data-column-editor-group": "hidden", className: "fractal-column-editor__hidden", "aria-label": hiddenLabel, children: [
327
+ /* @__PURE__ */ jsx("div", { className: "fractal-column-editor__divider", children: /* @__PURE__ */ jsxs2("span", { children: [
328
+ hiddenLabel,
329
+ /* @__PURE__ */ jsx(EyeSlash, { "aria-hidden": "true" })
330
+ ] }) }),
331
+ /* @__PURE__ */ jsx("ol", { className: "fractal-column-editor__list", children: hidden.map((key, index) => row(key, true, index)) }),
332
+ !hidden.length ? /* @__PURE__ */ jsx("p", { className: "fractal-column-editor__empty", children: "Drag columns here to hide them." }) : null
333
+ ] }),
334
+ /* @__PURE__ */ jsx("span", { className: "fractal-column-editor__announcement", role: "status", "aria-live": "polite", children: announcement })
335
+ ] }) : null
336
+ ] });
337
+ }
338
+
339
+ export {
340
+ cn,
341
+ Button,
342
+ ColumnEditor
343
+ };
344
+ //# sourceMappingURL=chunk-QZX655IA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/cn.ts","../src/components/Button/Button.tsx","../src/components/ColumnEditor/ColumnEditor.tsx"],"sourcesContent":["import { clsx, type ClassValue } from 'clsx'\n\nexport function cn(...inputs: ClassValue[]) {\n return clsx(inputs)\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 * 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"],"mappings":"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","names":["React","jsxs","row","item"]}