@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 +31 -0
- package/dist/chunk-QZX655IA.js +344 -0
- package/dist/chunk-QZX655IA.js.map +1 -0
- package/dist/column-editor.cjs +382 -0
- package/dist/column-editor.cjs.map +1 -0
- package/dist/column-editor.css +36 -0
- package/dist/column-editor.css.map +1 -0
- package/dist/column-editor.d.cts +20 -0
- package/dist/column-editor.d.ts +20 -0
- package/dist/column-editor.js +7 -0
- package/dist/column-editor.js.map +1 -0
- package/dist/index.cjs +814 -285
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +427 -35
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +51 -8
- package/dist/index.d.ts +51 -8
- package/dist/index.js +701 -507
- package/dist/index.js.map +1 -1
- package/dist/tokens.css +34 -0
- package/package.json +10 -3
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|