@mirror-physics/fractal-ui 0.2.0-next.8 → 0.2.0-next.80
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 +68 -0
- package/dist/chunk-UNMOINWV.js +339 -0
- package/dist/chunk-UNMOINWV.js.map +1 -0
- package/dist/column-editor.cjs +377 -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 +2685 -385
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +2517 -190
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +461 -22
- package/dist/index.d.ts +461 -22
- package/dist/index.js +2445 -512
- package/dist/index.js.map +1 -1
- package/dist/lattice-FAYE5KWW.svg +3 -0
- package/dist/tokens.css +114 -9
- package/package.json +16 -3
package/dist/index.js
CHANGED
|
@@ -1,208 +1,418 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
function cn(...inputs) {
|
|
7
|
-
return clsx(inputs);
|
|
8
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
ColumnEditor,
|
|
4
|
+
cn
|
|
5
|
+
} from "./chunk-UNMOINWV.js";
|
|
9
6
|
|
|
10
|
-
// src/components/
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
7
|
+
// src/components/CodeBlock/CodeBlock.tsx
|
|
8
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
9
|
+
import CodeMirror from "@uiw/react-codemirror";
|
|
10
|
+
import { HighlightStyle, foldGutter, foldService, syntaxHighlighting } from "@codemirror/language";
|
|
11
|
+
import { json } from "@codemirror/lang-json";
|
|
12
|
+
import {
|
|
13
|
+
Decoration,
|
|
14
|
+
EditorView,
|
|
15
|
+
MatchDecorator,
|
|
16
|
+
ViewPlugin,
|
|
17
|
+
lineNumbers as lineNumberGutter
|
|
18
|
+
} from "@codemirror/view";
|
|
19
|
+
import { tags } from "@lezer/highlight";
|
|
20
|
+
import { Check, Copy } from "@mirror-physics/fractal-icons";
|
|
21
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
22
|
+
var codeTheme = EditorView.theme({
|
|
23
|
+
"&": {
|
|
24
|
+
backgroundColor: "transparent",
|
|
25
|
+
color: "var(--fg-primary)",
|
|
26
|
+
fontSize: "0.75rem"
|
|
21
27
|
},
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
500: "var(--pink-500)",
|
|
28
|
-
600: "var(--pink-600)",
|
|
29
|
-
700: "var(--pink-700)"
|
|
28
|
+
"&.cm-focused": { outline: "none" },
|
|
29
|
+
".cm-scroller": {
|
|
30
|
+
fontFamily: "var(--font-mono)",
|
|
31
|
+
lineHeight: "1.55",
|
|
32
|
+
overscrollBehavior: "none"
|
|
30
33
|
},
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
200: "var(--orange-200)",
|
|
36
|
-
500: "var(--orange-500)",
|
|
37
|
-
600: "var(--orange-600)",
|
|
38
|
-
700: "var(--orange-700)"
|
|
34
|
+
".cm-content": { padding: "10px 0" },
|
|
35
|
+
".cm-line": {
|
|
36
|
+
padding: "0 12px 0 calc(12px + var(--fractal-code-wrap-indent, 0ch))",
|
|
37
|
+
textIndent: "calc(0px - var(--fractal-code-wrap-indent, 0ch))"
|
|
39
38
|
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
transition: "
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
39
|
+
".cm-cursor, .cm-dropCursor": { borderLeftColor: "var(--fg-primary)" },
|
|
40
|
+
".cm-gutters": {
|
|
41
|
+
backgroundColor: "var(--bg-canvas)",
|
|
42
|
+
borderRight: "1px solid var(--border-subtle)",
|
|
43
|
+
color: "var(--fg-tertiary)"
|
|
44
|
+
},
|
|
45
|
+
".cm-lineNumbers .cm-gutterElement": { padding: "0 10px 0 4px" },
|
|
46
|
+
".cm-foldGutter": { width: "14px" },
|
|
47
|
+
".cm-foldGutter .cm-gutterElement": {
|
|
48
|
+
position: "relative",
|
|
49
|
+
display: "block",
|
|
50
|
+
width: "14px",
|
|
51
|
+
padding: "0"
|
|
52
|
+
},
|
|
53
|
+
".cm-foldGutter .cm-gutterElement > span": {
|
|
54
|
+
position: "absolute",
|
|
55
|
+
top: "2px",
|
|
56
|
+
left: "0",
|
|
57
|
+
display: "inline-flex",
|
|
58
|
+
width: "14px",
|
|
59
|
+
height: "14px",
|
|
60
|
+
alignItems: "center",
|
|
61
|
+
justifyContent: "center",
|
|
62
|
+
padding: "0",
|
|
63
|
+
color: "var(--fg-tertiary)",
|
|
64
|
+
lineHeight: "14px",
|
|
65
|
+
transition: "color 120ms ease"
|
|
66
|
+
},
|
|
67
|
+
'.cm-foldGutter .cm-gutterElement > span[title="Fold line"]': { transform: "translateY(-3px)" },
|
|
68
|
+
".cm-foldGutter .cm-gutterElement > span:hover": { color: "var(--fg-secondary)" },
|
|
69
|
+
".fractal-code-block__escape": { color: "var(--fg-secondary)" },
|
|
70
|
+
".cm-activeLine, .cm-activeLineGutter": { backgroundColor: "var(--bg-surface-1)" },
|
|
71
|
+
".cm-selectionBackground, &.cm-focused .cm-selectionBackground, ::selection": {
|
|
72
|
+
backgroundColor: "var(--accent-surface-stroke) !important"
|
|
73
|
+
},
|
|
74
|
+
".cm-searchMatch": {
|
|
75
|
+
backgroundColor: "var(--warning-surface)",
|
|
76
|
+
outline: "1px solid var(--warning-surface-stroke)"
|
|
77
|
+
},
|
|
78
|
+
".cm-searchMatch.cm-searchMatch-selected": { backgroundColor: "var(--warning-subtle)" },
|
|
79
|
+
".cm-panels": { backgroundColor: "var(--bg-surface-1)", color: "var(--fg-primary)" },
|
|
80
|
+
".cm-tooltip": {
|
|
81
|
+
borderColor: "var(--border-subtle)",
|
|
82
|
+
backgroundColor: "var(--bg-canvas)",
|
|
83
|
+
color: "var(--fg-primary)"
|
|
71
84
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
85
|
+
});
|
|
86
|
+
var fractalHighlightStyle = HighlightStyle.define([
|
|
87
|
+
{ tag: tags.comment, color: "var(--fg-tertiary)", fontStyle: "italic" },
|
|
88
|
+
{ tag: [tags.keyword, tags.controlKeyword, tags.operatorKeyword], color: "var(--purple-default)" },
|
|
89
|
+
{ tag: [tags.string, tags.special(tags.string)], color: "var(--purple-default)" },
|
|
90
|
+
{ tag: [tags.number, tags.bool, tags.null], color: "var(--orange-default)" },
|
|
91
|
+
{ tag: [tags.function(tags.variableName), tags.labelName], color: "var(--accent-default)" },
|
|
92
|
+
{ tag: [tags.typeName, tags.className, tags.namespace], color: "var(--pink-default)" },
|
|
93
|
+
{ tag: [tags.propertyName, tags.attributeName], color: "var(--success-default)" },
|
|
94
|
+
{ tag: [tags.operator, tags.punctuation], color: "var(--fg-secondary)" },
|
|
95
|
+
{ tag: tags.bracket, color: "var(--accent-default)" },
|
|
96
|
+
{ tag: [tags.heading, tags.strong], color: "var(--fg-primary)", fontWeight: "600" },
|
|
97
|
+
{ tag: tags.emphasis, fontStyle: "italic" },
|
|
98
|
+
{ tag: tags.link, color: "var(--accent-default)", textDecoration: "underline" },
|
|
99
|
+
{ tag: tags.invalid, color: "var(--error-default)", textDecoration: "underline wavy" }
|
|
100
|
+
]);
|
|
101
|
+
var jsonSupport = json();
|
|
102
|
+
var JSON_LANGUAGE_NAMES = /* @__PURE__ */ new Set(["json", "jsonl", "ndjson", "geojson"]);
|
|
103
|
+
var JSON_LINES_LANGUAGE_NAMES = /* @__PURE__ */ new Set(["jsonl", "ndjson"]);
|
|
104
|
+
var JSON_LINES_PRETTY_PRINT_THRESHOLD = 160;
|
|
105
|
+
var JSON_PROPERTY_PREFIX = /^(\s*"(?:\\.|[^"\\])*"\s*:\s*)/;
|
|
106
|
+
function countVisualColumns(value) {
|
|
107
|
+
let columns = 0;
|
|
108
|
+
for (const character of value) columns += character === " " ? 2 : 1;
|
|
109
|
+
return columns;
|
|
110
|
+
}
|
|
111
|
+
function getWrappedLineIndent(line) {
|
|
112
|
+
const leadingWhitespace = line.match(/^\s*/)?.[0] ?? "";
|
|
113
|
+
if (JSON_PROPERTY_PREFIX.test(line)) return Math.min(countVisualColumns(leadingWhitespace), 24);
|
|
114
|
+
return Math.min(countVisualColumns(leadingWhitespace) + 2, 12);
|
|
115
|
+
}
|
|
116
|
+
function buildWrappedLineIndentation(view) {
|
|
117
|
+
const decorations = [];
|
|
118
|
+
const decoratedLines = /* @__PURE__ */ new Set();
|
|
119
|
+
for (const visibleRange of view.visibleRanges) {
|
|
120
|
+
let position = visibleRange.from;
|
|
121
|
+
while (position <= visibleRange.to) {
|
|
122
|
+
const line = view.state.doc.lineAt(position);
|
|
123
|
+
if (!decoratedLines.has(line.from)) {
|
|
124
|
+
const indent = getWrappedLineIndent(line.text);
|
|
125
|
+
decorations.push(Decoration.line({
|
|
126
|
+
attributes: { style: `--fractal-code-wrap-indent: ${indent}ch` }
|
|
127
|
+
}).range(line.from));
|
|
128
|
+
decoratedLines.add(line.from);
|
|
129
|
+
}
|
|
130
|
+
if (line.to >= visibleRange.to) break;
|
|
131
|
+
position = line.to + 1;
|
|
75
132
|
}
|
|
76
|
-
return { ...base, backgroundColor: "transparent", border: "1px solid var(--fg-disabled)", color: "var(--fg-disabled)" };
|
|
77
133
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
...base,
|
|
84
|
-
backgroundColor: isActive ? palette[700] : isHovered ? palette[500] : palette[600],
|
|
85
|
-
border: "none",
|
|
86
|
-
color: "var(--fg-inverse)"
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
...base,
|
|
91
|
-
backgroundColor: "var(--fg-primary)",
|
|
92
|
-
border: "1px solid var(--fg-primary)",
|
|
93
|
-
color: "var(--fg-inverse)",
|
|
94
|
-
...isHovered || isActive ? { opacity: 0.85 } : {}
|
|
95
|
-
};
|
|
96
|
-
// Secondary: neutral. Rest=canvas, hover=surface-1 (darken), pressed=surface-2. Subtle border, dark text.
|
|
97
|
-
case "secondary":
|
|
98
|
-
return {
|
|
99
|
-
...base,
|
|
100
|
-
backgroundColor: isActive ? "var(--bg-surface-2)" : isHovered ? "var(--bg-surface-1)" : "var(--bg-canvas)",
|
|
101
|
-
border: "1px solid var(--border-subtle)",
|
|
102
|
-
color: "var(--fg-primary)"
|
|
103
|
-
};
|
|
104
|
-
// Ghost: no border. Rest=transparent, hover/press=surface-1.
|
|
105
|
-
case "ghost":
|
|
106
|
-
return {
|
|
107
|
-
...base,
|
|
108
|
-
backgroundColor: isHovered || isActive ? "var(--bg-surface-1)" : "transparent",
|
|
109
|
-
border: "none",
|
|
110
|
-
color: "var(--fg-primary)"
|
|
111
|
-
};
|
|
112
|
-
// Danger: low-intensity destructive action. Text stays steady; hover deepens the light red surface.
|
|
113
|
-
case "danger":
|
|
114
|
-
return {
|
|
115
|
-
...base,
|
|
116
|
-
backgroundColor: isHovered || isActive ? "var(--danger-button-surface-hover)" : "var(--danger-button-surface)",
|
|
117
|
-
border: "none",
|
|
118
|
-
color: "var(--danger-button-fg)"
|
|
119
|
-
};
|
|
120
|
-
default:
|
|
121
|
-
return base;
|
|
134
|
+
return Decoration.set(decorations, true);
|
|
135
|
+
}
|
|
136
|
+
var wrappedLineIndentation = ViewPlugin.fromClass(class {
|
|
137
|
+
constructor(view) {
|
|
138
|
+
this.decorations = buildWrappedLineIndentation(view);
|
|
122
139
|
}
|
|
140
|
+
update(update) {
|
|
141
|
+
this.decorations = buildWrappedLineIndentation(update.view);
|
|
142
|
+
}
|
|
143
|
+
}, {
|
|
144
|
+
decorations: (plugin) => plugin.decorations
|
|
145
|
+
});
|
|
146
|
+
var jsonEscapeMatcher = new MatchDecorator({
|
|
147
|
+
regexp: /\\(?:["\\/bfnrt]|u[0-9a-fA-F]{4})/g,
|
|
148
|
+
decoration: Decoration.mark({ class: "fractal-code-block__escape" })
|
|
149
|
+
});
|
|
150
|
+
var jsonEscapeHighlighting = ViewPlugin.fromClass(class {
|
|
151
|
+
constructor(view) {
|
|
152
|
+
this.decorations = jsonEscapeMatcher.createDeco(view);
|
|
153
|
+
}
|
|
154
|
+
update(update) {
|
|
155
|
+
this.decorations = jsonEscapeMatcher.updateDeco(update, this.decorations);
|
|
156
|
+
}
|
|
157
|
+
}, {
|
|
158
|
+
decorations: (plugin) => plugin.decorations
|
|
159
|
+
});
|
|
160
|
+
function formatJsonLinesForDisplay(value) {
|
|
161
|
+
return value.split(/\r?\n/).map((line) => {
|
|
162
|
+
const firstContentIndex = line.search(/\S/);
|
|
163
|
+
if (firstContentIndex < 0 || line.length - firstContentIndex <= JSON_LINES_PRETTY_PRINT_THRESHOLD) return line;
|
|
164
|
+
const indentation = line.slice(0, firstContentIndex);
|
|
165
|
+
try {
|
|
166
|
+
const parsed = JSON.parse(line.slice(firstContentIndex));
|
|
167
|
+
if (parsed === null || typeof parsed !== "object") return line;
|
|
168
|
+
return JSON.stringify(parsed, null, 2).split("\n").map((formattedLine) => `${indentation}${formattedLine}`).join("\n");
|
|
169
|
+
} catch {
|
|
170
|
+
return line;
|
|
171
|
+
}
|
|
172
|
+
}).join("\n");
|
|
123
173
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
{
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
onMouseEnter: (e) => {
|
|
140
|
-
setIsHovered(true);
|
|
141
|
-
props.onMouseEnter?.(e);
|
|
142
|
-
},
|
|
143
|
-
onMouseLeave: (e) => {
|
|
144
|
-
setIsHovered(false);
|
|
145
|
-
setIsActive(false);
|
|
146
|
-
props.onMouseLeave?.(e);
|
|
147
|
-
},
|
|
148
|
-
onMouseDown: (e) => {
|
|
149
|
-
setIsActive(true);
|
|
150
|
-
props.onMouseDown?.(e);
|
|
151
|
-
},
|
|
152
|
-
onMouseUp: (e) => {
|
|
153
|
-
setIsActive(false);
|
|
154
|
-
props.onMouseUp?.(e);
|
|
155
|
-
},
|
|
156
|
-
...props,
|
|
157
|
-
children: [
|
|
158
|
-
iconPosition !== "right" && icon,
|
|
159
|
-
!hasIconOnly && children,
|
|
160
|
-
iconPosition === "right" && icon
|
|
161
|
-
]
|
|
174
|
+
function findJsonContainerEnd(document2, start) {
|
|
175
|
+
const openingCharacter = document2[start];
|
|
176
|
+
if (openingCharacter !== "{" && openingCharacter !== "[") return null;
|
|
177
|
+
const closingCharacters = [];
|
|
178
|
+
let isInsideString = false;
|
|
179
|
+
let isEscaped = false;
|
|
180
|
+
for (let index = start; index < document2.length; index += 1) {
|
|
181
|
+
const character = document2[index];
|
|
182
|
+
if (isInsideString) {
|
|
183
|
+
if (isEscaped) {
|
|
184
|
+
isEscaped = false;
|
|
185
|
+
} else if (character === "\\") {
|
|
186
|
+
isEscaped = true;
|
|
187
|
+
} else if (character === '"') {
|
|
188
|
+
isInsideString = false;
|
|
162
189
|
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (character === '"') {
|
|
193
|
+
isInsideString = true;
|
|
194
|
+
} else if (character === "{") {
|
|
195
|
+
closingCharacters.push("}");
|
|
196
|
+
} else if (character === "[") {
|
|
197
|
+
closingCharacters.push("]");
|
|
198
|
+
} else if (character === "}" || character === "]") {
|
|
199
|
+
if (closingCharacters.pop() !== character) return null;
|
|
200
|
+
if (closingCharacters.length === 0) return index;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
var jsonLinesFoldSupport = foldService.of((state, lineStart, lineEnd) => {
|
|
206
|
+
const line = state.doc.sliceString(lineStart, lineEnd);
|
|
207
|
+
const firstContentIndex = line.search(/\S/);
|
|
208
|
+
if (firstContentIndex < 0) return null;
|
|
209
|
+
const containerStart = lineStart + firstContentIndex;
|
|
210
|
+
const openingCharacter = line[firstContentIndex];
|
|
211
|
+
if (openingCharacter !== "{" && openingCharacter !== "[") return null;
|
|
212
|
+
const document2 = state.doc.toString();
|
|
213
|
+
const containerEnd = findJsonContainerEnd(document2, containerStart);
|
|
214
|
+
if (containerEnd === null || containerEnd - containerStart < 2) return null;
|
|
215
|
+
try {
|
|
216
|
+
const parsed = JSON.parse(document2.slice(containerStart, containerEnd + 1));
|
|
217
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
from: containerStart + 1,
|
|
223
|
+
to: containerEnd
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
function resolveLanguage(language, fileName) {
|
|
227
|
+
const normalized = language?.trim().toLowerCase();
|
|
228
|
+
const extension = fileName?.split(/[?#]/, 1)[0].split(".").pop()?.toLowerCase();
|
|
229
|
+
if (normalized && JSON_LANGUAGE_NAMES.has(normalized) || extension && JSON_LANGUAGE_NAMES.has(extension)) {
|
|
230
|
+
const isJsonLines = Boolean(
|
|
231
|
+
normalized && JSON_LINES_LANGUAGE_NAMES.has(normalized) || extension && JSON_LINES_LANGUAGE_NAMES.has(extension)
|
|
163
232
|
);
|
|
233
|
+
return { label: isJsonLines ? "JSONL" : "JSON", support: jsonSupport, isJsonLines };
|
|
164
234
|
}
|
|
165
|
-
)
|
|
166
|
-
|
|
235
|
+
if (normalized && !["text", "txt", "plaintext", "plain"].includes(normalized)) {
|
|
236
|
+
return { label: language?.trim(), support: null, isJsonLines: false };
|
|
237
|
+
}
|
|
238
|
+
return { support: null, isJsonLines: false };
|
|
239
|
+
}
|
|
240
|
+
function CodeBlock({
|
|
241
|
+
value,
|
|
242
|
+
language,
|
|
243
|
+
fileName,
|
|
244
|
+
label,
|
|
245
|
+
editable = false,
|
|
246
|
+
onChange,
|
|
247
|
+
lineNumbers = true,
|
|
248
|
+
wrap = true,
|
|
249
|
+
copyable = true,
|
|
250
|
+
ariaLabel,
|
|
251
|
+
height,
|
|
252
|
+
minHeight,
|
|
253
|
+
maxHeight = "28rem",
|
|
254
|
+
placeholder,
|
|
255
|
+
autoFocus,
|
|
256
|
+
className,
|
|
257
|
+
...props
|
|
258
|
+
}) {
|
|
259
|
+
const resolvedLanguage = useMemo(() => resolveLanguage(language, fileName), [fileName, language]);
|
|
260
|
+
const displayValue = useMemo(
|
|
261
|
+
() => !editable && resolvedLanguage.isJsonLines ? formatJsonLinesForDisplay(value) : value,
|
|
262
|
+
[editable, resolvedLanguage.isJsonLines, value]
|
|
263
|
+
);
|
|
264
|
+
const [copyStatus, setCopyStatus] = useState("idle");
|
|
265
|
+
const copyResetTimer = useRef(null);
|
|
266
|
+
useEffect(() => () => {
|
|
267
|
+
if (copyResetTimer.current) clearTimeout(copyResetTimer.current);
|
|
268
|
+
}, []);
|
|
269
|
+
const extensions = useMemo(() => [
|
|
270
|
+
codeTheme,
|
|
271
|
+
syntaxHighlighting(fractalHighlightStyle),
|
|
272
|
+
...resolvedLanguage.support ? [resolvedLanguage.support] : [],
|
|
273
|
+
...resolvedLanguage.support ? [jsonEscapeHighlighting] : [],
|
|
274
|
+
...resolvedLanguage.isJsonLines ? [jsonLinesFoldSupport] : [],
|
|
275
|
+
...lineNumbers ? [foldGutter(), lineNumberGutter()] : [],
|
|
276
|
+
...wrap ? [EditorView.lineWrapping, wrappedLineIndentation] : []
|
|
277
|
+
], [lineNumbers, resolvedLanguage.isJsonLines, resolvedLanguage.support, wrap]);
|
|
278
|
+
const languageLabel = resolvedLanguage.label;
|
|
279
|
+
const toolbarLabel = label ?? fileName ?? languageLabel;
|
|
280
|
+
const surfaceLabel = ariaLabel ?? (fileName ? `${fileName} ${editable ? "editor" : "viewer"}` : `Code ${editable ? "editor" : "viewer"}`);
|
|
281
|
+
const copyCode = async () => {
|
|
282
|
+
try {
|
|
283
|
+
await navigator.clipboard.writeText(value);
|
|
284
|
+
setCopyStatus("copied");
|
|
285
|
+
} catch {
|
|
286
|
+
setCopyStatus("error");
|
|
287
|
+
}
|
|
288
|
+
if (copyResetTimer.current) clearTimeout(copyResetTimer.current);
|
|
289
|
+
copyResetTimer.current = setTimeout(() => setCopyStatus("idle"), 1800);
|
|
290
|
+
};
|
|
291
|
+
const showToolbar = toolbarLabel !== void 0 || copyable || editable;
|
|
292
|
+
return /* @__PURE__ */ jsxs(
|
|
293
|
+
"div",
|
|
294
|
+
{
|
|
295
|
+
className: cn(
|
|
296
|
+
"fractal-code-block",
|
|
297
|
+
editable && "fractal-code-block--editable",
|
|
298
|
+
!lineNumbers && "fractal-code-block--no-line-numbers",
|
|
299
|
+
className
|
|
300
|
+
),
|
|
301
|
+
...props,
|
|
302
|
+
children: [
|
|
303
|
+
showToolbar && /* @__PURE__ */ jsxs("div", { className: "fractal-code-block__toolbar", children: [
|
|
304
|
+
/* @__PURE__ */ jsxs("div", { className: "fractal-code-block__identity", children: [
|
|
305
|
+
toolbarLabel !== void 0 && /* @__PURE__ */ jsx("span", { className: "fractal-code-block__label", title: typeof toolbarLabel === "string" ? toolbarLabel : void 0, children: toolbarLabel }),
|
|
306
|
+
fileName && languageLabel && /* @__PURE__ */ jsx("span", { className: "fractal-code-block__language", children: languageLabel }),
|
|
307
|
+
editable && /* @__PURE__ */ jsx("span", { className: "fractal-code-block__mode", children: "Editing" })
|
|
308
|
+
] }),
|
|
309
|
+
copyable && /* @__PURE__ */ jsxs(
|
|
310
|
+
"button",
|
|
311
|
+
{
|
|
312
|
+
type: "button",
|
|
313
|
+
className: "fractal-code-block__copy",
|
|
314
|
+
"aria-label": copyStatus === "copied" ? "Code copied" : "Copy code",
|
|
315
|
+
onClick: () => void copyCode(),
|
|
316
|
+
children: [
|
|
317
|
+
copyStatus === "copied" ? /* @__PURE__ */ jsx(Check, { size: 14 }) : /* @__PURE__ */ jsx(Copy, { size: 14 }),
|
|
318
|
+
/* @__PURE__ */ jsx("span", { children: copyStatus === "copied" ? "Copied" : copyStatus === "error" ? "Copy failed" : "Copy" })
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
)
|
|
322
|
+
] }),
|
|
323
|
+
/* @__PURE__ */ jsx("div", { className: "fractal-code-block__surface", children: /* @__PURE__ */ jsx(
|
|
324
|
+
CodeMirror,
|
|
325
|
+
{
|
|
326
|
+
"aria-label": surfaceLabel,
|
|
327
|
+
value: displayValue,
|
|
328
|
+
editable,
|
|
329
|
+
readOnly: !editable,
|
|
330
|
+
onChange,
|
|
331
|
+
extensions,
|
|
332
|
+
theme: "none",
|
|
333
|
+
basicSetup: {
|
|
334
|
+
lineNumbers: false,
|
|
335
|
+
foldGutter: false,
|
|
336
|
+
drawSelection: editable,
|
|
337
|
+
highlightActiveLine: editable,
|
|
338
|
+
highlightActiveLineGutter: editable,
|
|
339
|
+
highlightSelectionMatches: editable,
|
|
340
|
+
autocompletion: editable,
|
|
341
|
+
history: editable
|
|
342
|
+
},
|
|
343
|
+
indentWithTab: editable,
|
|
344
|
+
height,
|
|
345
|
+
minHeight,
|
|
346
|
+
maxHeight,
|
|
347
|
+
placeholder,
|
|
348
|
+
autoFocus
|
|
349
|
+
}
|
|
350
|
+
) }),
|
|
351
|
+
/* @__PURE__ */ jsx("span", { className: "fractal-code-block__status", "aria-live": "polite", children: copyStatus === "copied" ? "Code copied to clipboard." : copyStatus === "error" ? "Code could not be copied." : "" })
|
|
352
|
+
]
|
|
353
|
+
}
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/components/Breadcrumbs/Breadcrumbs.tsx
|
|
358
|
+
import { ChevronRight } from "@mirror-physics/fractal-icons";
|
|
359
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
360
|
+
function Breadcrumbs({
|
|
361
|
+
items,
|
|
362
|
+
ariaLabel = "Breadcrumbs",
|
|
363
|
+
separator = /* @__PURE__ */ jsx2(ChevronRight, { "aria-hidden": "true", size: 14 }),
|
|
364
|
+
className,
|
|
365
|
+
...props
|
|
366
|
+
}) {
|
|
367
|
+
if (items.length === 0) return null;
|
|
368
|
+
return /* @__PURE__ */ jsx2("nav", { "aria-label": ariaLabel, className: cn("fractal-breadcrumbs", className), ...props, children: /* @__PURE__ */ jsx2("ol", { className: "fractal-breadcrumbs__list", children: items.map((item, index) => {
|
|
369
|
+
const isCurrent = index === items.length - 1;
|
|
370
|
+
const content = isCurrent ? /* @__PURE__ */ jsx2("span", { "aria-current": "page", className: "fractal-breadcrumbs__current", children: item.label }) : /* @__PURE__ */ jsx2("a", { className: "fractal-breadcrumbs__link", href: item.href ?? "#", onClick: item.onClick, children: item.label });
|
|
371
|
+
return /* @__PURE__ */ jsxs2("li", { className: "fractal-breadcrumbs__item", children: [
|
|
372
|
+
index > 0 && /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", className: "fractal-breadcrumbs__separator", children: separator }),
|
|
373
|
+
content
|
|
374
|
+
] }, index);
|
|
375
|
+
}) }) });
|
|
376
|
+
}
|
|
167
377
|
|
|
168
378
|
// src/components/Card/Card.tsx
|
|
169
|
-
import * as
|
|
379
|
+
import * as React from "react";
|
|
170
380
|
import { Folder as FolderIcon } from "@mirror-physics/fractal-icons";
|
|
171
|
-
import { jsx, jsxs as
|
|
172
|
-
var Card =
|
|
173
|
-
({ className, ...props }, ref) => /* @__PURE__ */
|
|
381
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
382
|
+
var Card = React.forwardRef(
|
|
383
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx3("div", { ref, "data-fractal-border-surface": "surface-1", className: cn("fractal-card", className), ...props })
|
|
174
384
|
);
|
|
175
385
|
Card.displayName = "Card";
|
|
176
|
-
var CardHeader =
|
|
177
|
-
({ className, ...props }, ref) => /* @__PURE__ */
|
|
386
|
+
var CardHeader = React.forwardRef(
|
|
387
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx3("div", { ref, className: cn("fractal-card-header", className), ...props })
|
|
178
388
|
);
|
|
179
389
|
CardHeader.displayName = "CardHeader";
|
|
180
|
-
var CardTitle =
|
|
181
|
-
({ className, ...props }, ref) => /* @__PURE__ */
|
|
390
|
+
var CardTitle = React.forwardRef(
|
|
391
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx3("h3", { ref, className: cn("fractal-card-title", className), ...props })
|
|
182
392
|
);
|
|
183
393
|
CardTitle.displayName = "CardTitle";
|
|
184
|
-
var CardDescription =
|
|
185
|
-
({ className, ...props }, ref) => /* @__PURE__ */
|
|
394
|
+
var CardDescription = React.forwardRef(
|
|
395
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx3("p", { ref, className: cn("fractal-card-description", className), ...props })
|
|
186
396
|
);
|
|
187
397
|
CardDescription.displayName = "CardDescription";
|
|
188
|
-
var CardContent =
|
|
189
|
-
({ className, ...props }, ref) => /* @__PURE__ */
|
|
398
|
+
var CardContent = React.forwardRef(
|
|
399
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx3("div", { ref, className: cn("fractal-card-content", className), ...props })
|
|
190
400
|
);
|
|
191
401
|
CardContent.displayName = "CardContent";
|
|
192
|
-
var CardFooter =
|
|
193
|
-
({ className, ...props }, ref) => /* @__PURE__ */
|
|
402
|
+
var CardFooter = React.forwardRef(
|
|
403
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx3("div", { ref, className: cn("fractal-card-footer", className), ...props })
|
|
194
404
|
);
|
|
195
405
|
CardFooter.displayName = "CardFooter";
|
|
196
|
-
var FeatureCard =
|
|
197
|
-
({ icon, title, description, className, ...props }, ref) => /* @__PURE__ */
|
|
198
|
-
icon && /* @__PURE__ */
|
|
199
|
-
/* @__PURE__ */
|
|
200
|
-
/* @__PURE__ */
|
|
406
|
+
var FeatureCard = React.forwardRef(
|
|
407
|
+
({ icon, title, description, className, ...props }, ref) => /* @__PURE__ */ jsxs3("div", { ref, className: cn("fractal-feature-card", className), ...props, children: [
|
|
408
|
+
icon && /* @__PURE__ */ jsx3("div", { className: "fractal-feature-card__icon", children: icon }),
|
|
409
|
+
/* @__PURE__ */ jsx3("h3", { className: "fractal-feature-card__title", children: title }),
|
|
410
|
+
/* @__PURE__ */ jsx3("p", { className: "fractal-feature-card__description", children: description })
|
|
201
411
|
] })
|
|
202
412
|
);
|
|
203
413
|
FeatureCard.displayName = "FeatureCard";
|
|
204
|
-
var FolderCard =
|
|
205
|
-
({ name, sessionCount, selected, className, onClick, ...props }, ref) => /* @__PURE__ */
|
|
414
|
+
var FolderCard = React.forwardRef(
|
|
415
|
+
({ name, sessionCount, selected, className, onClick, ...props }, ref) => /* @__PURE__ */ jsxs3(
|
|
206
416
|
"div",
|
|
207
417
|
{
|
|
208
418
|
ref,
|
|
@@ -218,11 +428,11 @@ var FolderCard = React2.forwardRef(
|
|
|
218
428
|
className: cn("fractal-folder-card", selected && "fractal-folder-card--selected", className),
|
|
219
429
|
...props,
|
|
220
430
|
children: [
|
|
221
|
-
/* @__PURE__ */
|
|
222
|
-
/* @__PURE__ */
|
|
223
|
-
/* @__PURE__ */
|
|
431
|
+
/* @__PURE__ */ jsxs3("div", { className: "fractal-folder-card__header", children: [
|
|
432
|
+
/* @__PURE__ */ jsx3(FolderIcon, { size: 16, className: "fractal-folder-card__icon", "aria-hidden": true }),
|
|
433
|
+
/* @__PURE__ */ jsx3("span", { className: "fractal-folder-card__name", children: name })
|
|
224
434
|
] }),
|
|
225
|
-
sessionCount !== void 0 && /* @__PURE__ */
|
|
435
|
+
sessionCount !== void 0 && /* @__PURE__ */ jsx3("div", { className: "fractal-folder-card__footer", children: /* @__PURE__ */ jsxs3("span", { className: "fractal-folder-card__sessions", children: [
|
|
226
436
|
sessionCount,
|
|
227
437
|
" ",
|
|
228
438
|
sessionCount === 1 ? "session" : "sessions"
|
|
@@ -234,10 +444,10 @@ var FolderCard = React2.forwardRef(
|
|
|
234
444
|
FolderCard.displayName = "FolderCard";
|
|
235
445
|
|
|
236
446
|
// src/components/PromptCard/PromptCard.tsx
|
|
237
|
-
import * as
|
|
238
|
-
import { jsx as
|
|
447
|
+
import * as React2 from "react";
|
|
448
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
239
449
|
var titleToPrompt = (title) => typeof title === "string" || typeof title === "number" ? String(title) : "";
|
|
240
|
-
var PromptCard =
|
|
450
|
+
var PromptCard = React2.forwardRef(
|
|
241
451
|
({
|
|
242
452
|
title,
|
|
243
453
|
icon,
|
|
@@ -256,7 +466,7 @@ var PromptCard = React3.forwardRef(
|
|
|
256
466
|
onPromptSelect?.(prompt ?? titleToPrompt(title));
|
|
257
467
|
}
|
|
258
468
|
};
|
|
259
|
-
return /* @__PURE__ */
|
|
469
|
+
return /* @__PURE__ */ jsxs4(
|
|
260
470
|
"button",
|
|
261
471
|
{
|
|
262
472
|
ref,
|
|
@@ -271,16 +481,16 @@ var PromptCard = React3.forwardRef(
|
|
|
271
481
|
onClick: handleClick,
|
|
272
482
|
...props,
|
|
273
483
|
children: [
|
|
274
|
-
icon && /* @__PURE__ */
|
|
275
|
-
/* @__PURE__ */
|
|
484
|
+
icon && /* @__PURE__ */ jsx4("span", { className: "fractal-prompt-card__icon", "aria-hidden": "true", children: icon }),
|
|
485
|
+
/* @__PURE__ */ jsx4("span", { className: "fractal-prompt-card__title", children: title })
|
|
276
486
|
]
|
|
277
487
|
}
|
|
278
488
|
);
|
|
279
489
|
}
|
|
280
490
|
);
|
|
281
491
|
PromptCard.displayName = "PromptCard";
|
|
282
|
-
var PromptGrid =
|
|
283
|
-
({ compact, padded, className, ...props }, ref) => /* @__PURE__ */
|
|
492
|
+
var PromptGrid = React2.forwardRef(
|
|
493
|
+
({ compact, padded, className, ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
284
494
|
"div",
|
|
285
495
|
{
|
|
286
496
|
ref,
|
|
@@ -297,29 +507,356 @@ var PromptGrid = React3.forwardRef(
|
|
|
297
507
|
PromptGrid.displayName = "PromptGrid";
|
|
298
508
|
|
|
299
509
|
// src/components/Input/Input.tsx
|
|
510
|
+
import * as React3 from "react";
|
|
511
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
512
|
+
var Input = React3.forwardRef(
|
|
513
|
+
({ className, type, controlSize = "lg", fieldLabel, id, placeholder, ...props }, ref) => {
|
|
514
|
+
const generatedId = React3.useId();
|
|
515
|
+
const fieldId = id ?? generatedId;
|
|
516
|
+
const floatingLabel = type === "search" ? void 0 : fieldLabel;
|
|
517
|
+
const input = /* @__PURE__ */ jsx5(
|
|
518
|
+
"input",
|
|
519
|
+
{
|
|
520
|
+
type,
|
|
521
|
+
ref,
|
|
522
|
+
id: floatingLabel ? fieldId : id,
|
|
523
|
+
placeholder: floatingLabel ? placeholder || " " : placeholder,
|
|
524
|
+
"data-control-size": controlSize,
|
|
525
|
+
className: cn("fractal-control", "fractal-input", className),
|
|
526
|
+
...props,
|
|
527
|
+
"aria-label": props["aria-label"] ?? (type === "search" && !props["aria-labelledby"] ? fieldLabel ?? "Search" : void 0)
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
if (!floatingLabel) return input;
|
|
531
|
+
return /* @__PURE__ */ jsxs5(
|
|
532
|
+
"div",
|
|
533
|
+
{
|
|
534
|
+
className: "fractal-floating-field",
|
|
535
|
+
"data-control-size": controlSize,
|
|
536
|
+
"data-always-float": ["date", "datetime-local", "time", "month", "week", "color", "file", "range"].includes(type ?? "") || void 0,
|
|
537
|
+
children: [
|
|
538
|
+
input,
|
|
539
|
+
/* @__PURE__ */ jsx5("label", { className: "fractal-floating-field__label", htmlFor: fieldId, children: fieldLabel })
|
|
540
|
+
]
|
|
541
|
+
}
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
);
|
|
545
|
+
Input.displayName = "Input";
|
|
546
|
+
|
|
547
|
+
// src/components/TableSearch/TableSearch.tsx
|
|
300
548
|
import * as React4 from "react";
|
|
301
|
-
import {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
549
|
+
import { MagnifyingGlass, MagnifyingGlassLines } from "@mirror-physics/fractal-icons";
|
|
550
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
551
|
+
var TableSearch = React4.forwardRef(
|
|
552
|
+
({
|
|
553
|
+
searchType = "text",
|
|
554
|
+
controlSize = "lg",
|
|
555
|
+
className,
|
|
556
|
+
inputClassName,
|
|
557
|
+
placeholder,
|
|
558
|
+
title,
|
|
559
|
+
...props
|
|
560
|
+
}, ref) => {
|
|
561
|
+
const isGlob = searchType === "glob";
|
|
562
|
+
const SearchIcon = isGlob ? MagnifyingGlassLines : MagnifyingGlass;
|
|
563
|
+
return /* @__PURE__ */ jsxs6(
|
|
564
|
+
"div",
|
|
565
|
+
{
|
|
566
|
+
className: cn("fractal-table-search", className),
|
|
567
|
+
"data-search-type": searchType,
|
|
568
|
+
"data-control-size": controlSize,
|
|
569
|
+
children: [
|
|
570
|
+
/* @__PURE__ */ jsx6(SearchIcon, { className: "fractal-table-search__icon", size: 17, "aria-hidden": "true" }),
|
|
571
|
+
/* @__PURE__ */ jsx6(
|
|
572
|
+
Input,
|
|
573
|
+
{
|
|
574
|
+
...props,
|
|
575
|
+
controlSize,
|
|
576
|
+
ref,
|
|
577
|
+
type: "search",
|
|
578
|
+
className: cn("fractal-table-search__input", inputClassName),
|
|
579
|
+
placeholder: placeholder ?? (isGlob ? "Glob search \u2014 use *, ?, or [a-z]" : "Search"),
|
|
580
|
+
title: title ?? (isGlob ? "Glob pattern: *, **, ?, [abc], [a-z], and [!abc]" : void 0)
|
|
581
|
+
}
|
|
582
|
+
)
|
|
583
|
+
]
|
|
584
|
+
}
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
);
|
|
588
|
+
TableSearch.displayName = "TableSearch";
|
|
589
|
+
|
|
590
|
+
// src/components/NumberInput/NumberInput.tsx
|
|
591
|
+
import * as React5 from "react";
|
|
592
|
+
import { Minus, Plus } from "@mirror-physics/fractal-icons";
|
|
593
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
594
|
+
function NumberInput({
|
|
595
|
+
unit,
|
|
596
|
+
controlSize = "lg",
|
|
597
|
+
className,
|
|
598
|
+
value,
|
|
599
|
+
defaultValue,
|
|
600
|
+
disabled,
|
|
601
|
+
readOnly,
|
|
602
|
+
min,
|
|
603
|
+
max,
|
|
604
|
+
...props
|
|
605
|
+
}) {
|
|
606
|
+
const inputRef = React5.useRef(null);
|
|
607
|
+
const numericValue = Number(value ?? defaultValue);
|
|
608
|
+
const characterCount = Math.max(String(value ?? defaultValue ?? "").length, 1);
|
|
609
|
+
const controlLabel = typeof props["aria-label"] === "string" ? props["aria-label"] : props.fieldLabel ?? "value";
|
|
610
|
+
const decrementDisabled = disabled || readOnly || Number.isFinite(numericValue) && min !== void 0 && numericValue <= Number(min);
|
|
611
|
+
const incrementDisabled = disabled || readOnly || Number.isFinite(numericValue) && max !== void 0 && numericValue >= Number(max);
|
|
612
|
+
const changeByStep = (direction) => {
|
|
613
|
+
const input = inputRef.current;
|
|
614
|
+
if (!input) return;
|
|
615
|
+
if (direction === -1) input.stepDown();
|
|
616
|
+
else input.stepUp();
|
|
617
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
618
|
+
input.focus();
|
|
619
|
+
};
|
|
620
|
+
return /* @__PURE__ */ jsxs7("div", { "data-control-size": controlSize, className: cn("fractal-number-input", unit && "fractal-number-input--with-unit", className), children: [
|
|
621
|
+
/* @__PURE__ */ jsx7(
|
|
622
|
+
Input,
|
|
623
|
+
{
|
|
624
|
+
ref: inputRef,
|
|
625
|
+
controlSize,
|
|
626
|
+
type: "number",
|
|
627
|
+
value,
|
|
628
|
+
defaultValue,
|
|
629
|
+
disabled,
|
|
630
|
+
readOnly,
|
|
631
|
+
min,
|
|
632
|
+
max,
|
|
633
|
+
...props
|
|
634
|
+
}
|
|
635
|
+
),
|
|
636
|
+
unit && /* @__PURE__ */ jsx7(
|
|
637
|
+
"span",
|
|
638
|
+
{
|
|
639
|
+
className: "fractal-number-input-unit",
|
|
640
|
+
style: { left: `calc(var(--control-padding-inline) + ${characterCount}ch)` },
|
|
641
|
+
"aria-hidden": "true",
|
|
642
|
+
children: unit
|
|
643
|
+
}
|
|
644
|
+
),
|
|
645
|
+
/* @__PURE__ */ jsxs7("div", { className: "fractal-number-input-controls", children: [
|
|
646
|
+
/* @__PURE__ */ jsx7(
|
|
647
|
+
"button",
|
|
648
|
+
{
|
|
649
|
+
type: "button",
|
|
650
|
+
className: "fractal-number-input-step",
|
|
651
|
+
"aria-label": `Decrease ${controlLabel}`,
|
|
652
|
+
disabled: decrementDisabled,
|
|
653
|
+
onClick: () => changeByStep(-1),
|
|
654
|
+
children: /* @__PURE__ */ jsx7(Minus, { "aria-hidden": "true", size: 16 })
|
|
655
|
+
}
|
|
656
|
+
),
|
|
657
|
+
/* @__PURE__ */ jsx7(
|
|
658
|
+
"button",
|
|
659
|
+
{
|
|
660
|
+
type: "button",
|
|
661
|
+
className: "fractal-number-input-step",
|
|
662
|
+
"aria-label": `Increase ${controlLabel}`,
|
|
663
|
+
disabled: incrementDisabled,
|
|
664
|
+
onClick: () => changeByStep(1),
|
|
665
|
+
children: /* @__PURE__ */ jsx7(Plus, { "aria-hidden": "true", size: 16 })
|
|
666
|
+
}
|
|
667
|
+
)
|
|
668
|
+
] })
|
|
669
|
+
] });
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// src/components/Slider/Slider.tsx
|
|
673
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
674
|
+
function Slider(props) {
|
|
675
|
+
const {
|
|
676
|
+
min = 0,
|
|
677
|
+
max = 100,
|
|
678
|
+
step = 1,
|
|
679
|
+
disabled = false,
|
|
680
|
+
className,
|
|
681
|
+
showValues = true,
|
|
682
|
+
formatValue = (value) => value,
|
|
683
|
+
knobs = 1
|
|
684
|
+
} = props;
|
|
685
|
+
const span = Math.max(max - min, 1);
|
|
686
|
+
const rawValues = knobs === 2 ? props.value : [props.value];
|
|
687
|
+
const firstValue = clamp(rawValues[0], min, max);
|
|
688
|
+
const secondValue = knobs === 2 ? clamp(rawValues[1], firstValue, max) : firstValue;
|
|
689
|
+
const selectionStart = knobs === 2 ? firstValue : min;
|
|
690
|
+
const selectionEnd = secondValue;
|
|
691
|
+
const selectionStartPercent = (selectionStart - min) / span * 100;
|
|
692
|
+
const selectionEndPercent = (selectionEnd - min) / span * 100;
|
|
693
|
+
const changeSingleValue = (value) => {
|
|
694
|
+
;
|
|
695
|
+
props.onValueChange(clamp(value, min, max));
|
|
696
|
+
};
|
|
697
|
+
const changeRangeValue = (nextLower, nextUpper) => {
|
|
698
|
+
;
|
|
699
|
+
props.onValueChange([
|
|
700
|
+
clamp(nextLower, min, nextUpper),
|
|
701
|
+
clamp(nextUpper, nextLower, max)
|
|
702
|
+
]);
|
|
703
|
+
};
|
|
704
|
+
const getKeyboardValue = (event, value) => {
|
|
705
|
+
const pageStep = step * 10;
|
|
706
|
+
const adjustments = {
|
|
707
|
+
ArrowDown: -step,
|
|
708
|
+
ArrowLeft: -step,
|
|
709
|
+
ArrowRight: step,
|
|
710
|
+
ArrowUp: step,
|
|
711
|
+
PageDown: -pageStep,
|
|
712
|
+
PageUp: pageStep
|
|
713
|
+
};
|
|
714
|
+
if (event.key === "Home") return min;
|
|
715
|
+
if (event.key === "End") return max;
|
|
716
|
+
if (!(event.key in adjustments)) return null;
|
|
717
|
+
return value + adjustments[event.key];
|
|
718
|
+
};
|
|
719
|
+
return /* @__PURE__ */ jsxs8(
|
|
720
|
+
"div",
|
|
305
721
|
{
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
722
|
+
className: cn("fractal-slider", disabled && "fractal-slider--disabled", className),
|
|
723
|
+
"data-disabled": disabled || void 0,
|
|
724
|
+
"data-knobs": knobs,
|
|
725
|
+
children: [
|
|
726
|
+
showValues && /* @__PURE__ */ jsxs8("div", { className: "fractal-slider-values", "aria-hidden": "true", children: [
|
|
727
|
+
/* @__PURE__ */ jsx8("output", { children: formatValue(firstValue) }),
|
|
728
|
+
knobs === 2 && /* @__PURE__ */ jsx8("output", { children: formatValue(secondValue) })
|
|
729
|
+
] }),
|
|
730
|
+
/* @__PURE__ */ jsxs8(
|
|
731
|
+
"div",
|
|
732
|
+
{
|
|
733
|
+
className: "fractal-slider-control",
|
|
734
|
+
style: {
|
|
735
|
+
"--fractal-slider-selection-start": `${selectionStartPercent}%`,
|
|
736
|
+
"--fractal-slider-selection-end": `${selectionEndPercent}%`
|
|
737
|
+
},
|
|
738
|
+
children: [
|
|
739
|
+
/* @__PURE__ */ jsx8("span", { className: "fractal-slider-track", "aria-hidden": "true" }),
|
|
740
|
+
/* @__PURE__ */ jsx8("span", { className: "fractal-slider-selection", "aria-hidden": "true" }),
|
|
741
|
+
/* @__PURE__ */ jsx8(
|
|
742
|
+
"input",
|
|
743
|
+
{
|
|
744
|
+
"aria-label": knobs === 2 ? props.lowerAriaLabel ?? "Lower value" : props.ariaLabel ?? "Slider value",
|
|
745
|
+
disabled,
|
|
746
|
+
max,
|
|
747
|
+
min,
|
|
748
|
+
onChange: (event) => {
|
|
749
|
+
const nextValue = event.currentTarget.valueAsNumber;
|
|
750
|
+
if (knobs === 2) changeRangeValue(Math.min(nextValue, secondValue), secondValue);
|
|
751
|
+
else changeSingleValue(nextValue);
|
|
752
|
+
},
|
|
753
|
+
onKeyDown: (event) => {
|
|
754
|
+
const nextValue = getKeyboardValue(event, firstValue);
|
|
755
|
+
if (nextValue === null) return;
|
|
756
|
+
event.preventDefault();
|
|
757
|
+
if (knobs === 2) changeRangeValue(Math.min(nextValue, secondValue), secondValue);
|
|
758
|
+
else changeSingleValue(nextValue);
|
|
759
|
+
},
|
|
760
|
+
step,
|
|
761
|
+
type: "range",
|
|
762
|
+
value: firstValue
|
|
763
|
+
}
|
|
764
|
+
),
|
|
765
|
+
knobs === 2 && /* @__PURE__ */ jsx8(
|
|
766
|
+
"input",
|
|
767
|
+
{
|
|
768
|
+
"aria-label": props.upperAriaLabel ?? "Upper value",
|
|
769
|
+
disabled,
|
|
770
|
+
max,
|
|
771
|
+
min,
|
|
772
|
+
onChange: (event) => {
|
|
773
|
+
const nextValue = event.currentTarget.valueAsNumber;
|
|
774
|
+
changeRangeValue(firstValue, Math.max(nextValue, firstValue));
|
|
775
|
+
},
|
|
776
|
+
onKeyDown: (event) => {
|
|
777
|
+
const nextValue = getKeyboardValue(event, secondValue);
|
|
778
|
+
if (nextValue === null) return;
|
|
779
|
+
event.preventDefault();
|
|
780
|
+
changeRangeValue(firstValue, Math.max(nextValue, firstValue));
|
|
781
|
+
},
|
|
782
|
+
step,
|
|
783
|
+
type: "range",
|
|
784
|
+
value: secondValue
|
|
785
|
+
}
|
|
786
|
+
)
|
|
787
|
+
]
|
|
788
|
+
}
|
|
789
|
+
)
|
|
790
|
+
]
|
|
310
791
|
}
|
|
311
|
-
)
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
function clamp(value, minimum, maximum) {
|
|
795
|
+
return Math.min(maximum, Math.max(minimum, value));
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// src/components/PasswordInput/PasswordInput.tsx
|
|
799
|
+
import * as React6 from "react";
|
|
800
|
+
import { Eye, EyeSlash } from "@mirror-physics/fractal-icons";
|
|
801
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
802
|
+
var PasswordInput = React6.forwardRef(
|
|
803
|
+
({
|
|
804
|
+
className,
|
|
805
|
+
controlSize = "lg",
|
|
806
|
+
disabled,
|
|
807
|
+
showPasswordLabel = "Show password",
|
|
808
|
+
hidePasswordLabel = "Hide password",
|
|
809
|
+
...props
|
|
810
|
+
}, ref) => {
|
|
811
|
+
const [passwordVisible, setPasswordVisible] = React6.useState(false);
|
|
812
|
+
const toggleLabel = passwordVisible ? hidePasswordLabel : showPasswordLabel;
|
|
813
|
+
return /* @__PURE__ */ jsxs9(
|
|
814
|
+
"div",
|
|
815
|
+
{
|
|
816
|
+
className: cn("fractal-password-input", className),
|
|
817
|
+
"data-control-size": controlSize,
|
|
818
|
+
"data-password-visible": passwordVisible ? "" : void 0,
|
|
819
|
+
children: [
|
|
820
|
+
/* @__PURE__ */ jsx9(
|
|
821
|
+
Input,
|
|
822
|
+
{
|
|
823
|
+
...props,
|
|
824
|
+
controlSize,
|
|
825
|
+
ref,
|
|
826
|
+
className: "fractal-password-input-field",
|
|
827
|
+
disabled,
|
|
828
|
+
type: passwordVisible ? "text" : "password"
|
|
829
|
+
}
|
|
830
|
+
),
|
|
831
|
+
/* @__PURE__ */ jsx9(
|
|
832
|
+
"button",
|
|
833
|
+
{
|
|
834
|
+
type: "button",
|
|
835
|
+
className: "fractal-password-input-toggle",
|
|
836
|
+
"aria-label": toggleLabel,
|
|
837
|
+
"aria-pressed": passwordVisible,
|
|
838
|
+
disabled,
|
|
839
|
+
title: toggleLabel,
|
|
840
|
+
onClick: () => setPasswordVisible((visible) => !visible),
|
|
841
|
+
children: passwordVisible ? /* @__PURE__ */ jsx9(EyeSlash, { "aria-hidden": "true", size: 18 }) : /* @__PURE__ */ jsx9(Eye, { "aria-hidden": "true", size: 18 })
|
|
842
|
+
}
|
|
843
|
+
)
|
|
844
|
+
]
|
|
845
|
+
}
|
|
846
|
+
);
|
|
847
|
+
}
|
|
312
848
|
);
|
|
313
|
-
|
|
849
|
+
PasswordInput.displayName = "PasswordInput";
|
|
314
850
|
|
|
315
851
|
// src/components/Label/Label.tsx
|
|
316
|
-
import * as
|
|
317
|
-
import { jsx as
|
|
318
|
-
var Label =
|
|
319
|
-
({ className, ...props }, ref) => /* @__PURE__ */
|
|
852
|
+
import * as React7 from "react";
|
|
853
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
854
|
+
var Label = React7.forwardRef(
|
|
855
|
+
({ className, controlSize = "lg", ...props }, ref) => /* @__PURE__ */ jsx10(
|
|
320
856
|
"label",
|
|
321
857
|
{
|
|
322
858
|
ref,
|
|
859
|
+
"data-control-size": controlSize,
|
|
323
860
|
className: cn("fractal-label", className),
|
|
324
861
|
...props
|
|
325
862
|
}
|
|
@@ -328,44 +865,147 @@ var Label = React5.forwardRef(
|
|
|
328
865
|
Label.displayName = "Label";
|
|
329
866
|
|
|
330
867
|
// src/components/Alert/Alert.tsx
|
|
331
|
-
import * as
|
|
332
|
-
import {
|
|
868
|
+
import * as React8 from "react";
|
|
869
|
+
import { CircleCheck, CircleClose, CircleInfo, Warning } from "@mirror-physics/fractal-icons";
|
|
870
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
333
871
|
var defaultIcons = {
|
|
334
|
-
default:
|
|
335
|
-
info:
|
|
336
|
-
success:
|
|
337
|
-
warning:
|
|
338
|
-
error:
|
|
872
|
+
default: CircleInfo,
|
|
873
|
+
info: CircleInfo,
|
|
874
|
+
success: CircleCheck,
|
|
875
|
+
warning: Warning,
|
|
876
|
+
error: CircleClose
|
|
877
|
+
};
|
|
878
|
+
var Alert = React8.forwardRef(
|
|
879
|
+
({ className, variant = "default", icon, children, ...props }, ref) => {
|
|
880
|
+
const DefaultIcon = defaultIcons[variant];
|
|
881
|
+
return /* @__PURE__ */ jsxs10(
|
|
882
|
+
"div",
|
|
883
|
+
{
|
|
884
|
+
ref,
|
|
885
|
+
role: "alert",
|
|
886
|
+
className: cn("fractal-alert", `fractal-alert--${variant}`, className),
|
|
887
|
+
...props,
|
|
888
|
+
children: [
|
|
889
|
+
/* @__PURE__ */ jsx11("span", { className: "fractal-alert-icon", "aria-hidden": true, children: icon ?? /* @__PURE__ */ jsx11(DefaultIcon, { size: 18 }) }),
|
|
890
|
+
/* @__PURE__ */ jsx11("div", { className: "fractal-alert-content", children })
|
|
891
|
+
]
|
|
892
|
+
}
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
);
|
|
896
|
+
Alert.displayName = "Alert";
|
|
897
|
+
var AlertTitle = React8.forwardRef(
|
|
898
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx11("h5", { ref, className: cn("fractal-alert-title", className), ...props })
|
|
899
|
+
);
|
|
900
|
+
AlertTitle.displayName = "AlertTitle";
|
|
901
|
+
var AlertDescription = React8.forwardRef(
|
|
902
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx11("div", { ref, className: cn("fractal-alert-description", className), ...props })
|
|
903
|
+
);
|
|
904
|
+
AlertDescription.displayName = "AlertDescription";
|
|
905
|
+
|
|
906
|
+
// src/components/Toast/Toast.tsx
|
|
907
|
+
import * as React9 from "react";
|
|
908
|
+
import { createPortal } from "react-dom";
|
|
909
|
+
import { CircleCheck as CircleCheck2, CircleClose as CircleClose2, CircleInfo as CircleInfo2, Close, Warning as Warning2 } from "@mirror-physics/fractal-icons";
|
|
910
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
911
|
+
var defaultIcons2 = {
|
|
912
|
+
default: CircleInfo2,
|
|
913
|
+
info: CircleInfo2,
|
|
914
|
+
success: CircleCheck2,
|
|
915
|
+
warning: Warning2,
|
|
916
|
+
error: CircleClose2
|
|
339
917
|
};
|
|
340
|
-
var
|
|
341
|
-
|
|
918
|
+
var Toast = React9.forwardRef(function Toast2({
|
|
919
|
+
action,
|
|
920
|
+
children,
|
|
921
|
+
className,
|
|
922
|
+
dismissLabel = "Dismiss notification",
|
|
923
|
+
duration,
|
|
924
|
+
icon,
|
|
925
|
+
onBlurCapture,
|
|
926
|
+
onDismiss,
|
|
927
|
+
onFocusCapture,
|
|
928
|
+
onPointerEnter,
|
|
929
|
+
onPointerLeave,
|
|
930
|
+
role,
|
|
931
|
+
variant = "default",
|
|
932
|
+
...props
|
|
933
|
+
}, ref) {
|
|
934
|
+
const [paused, setPaused] = React9.useState(false);
|
|
935
|
+
const dismissRef = React9.useRef(onDismiss);
|
|
936
|
+
const DefaultIcon = defaultIcons2[variant];
|
|
937
|
+
const resolvedDuration = duration === void 0 ? variant === "error" ? null : 5e3 : duration;
|
|
938
|
+
React9.useEffect(() => {
|
|
939
|
+
dismissRef.current = onDismiss;
|
|
940
|
+
}, [onDismiss]);
|
|
941
|
+
React9.useEffect(() => {
|
|
942
|
+
if (!dismissRef.current || paused || resolvedDuration === null || resolvedDuration <= 0) return;
|
|
943
|
+
const timeout = window.setTimeout(() => dismissRef.current?.(), resolvedDuration);
|
|
944
|
+
return () => window.clearTimeout(timeout);
|
|
945
|
+
}, [paused, resolvedDuration]);
|
|
946
|
+
const announcementRole = role ?? (variant === "error" ? "alert" : "status");
|
|
947
|
+
return /* @__PURE__ */ jsxs11(
|
|
342
948
|
"div",
|
|
343
949
|
{
|
|
344
950
|
ref,
|
|
345
|
-
|
|
346
|
-
|
|
951
|
+
"aria-atomic": "true",
|
|
952
|
+
"aria-live": announcementRole === "alert" ? "assertive" : "polite",
|
|
953
|
+
className: cn("fractal-toast", `fractal-toast--${variant}`, className),
|
|
954
|
+
role: announcementRole,
|
|
955
|
+
onBlurCapture: (event) => {
|
|
956
|
+
onBlurCapture?.(event);
|
|
957
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setPaused(false);
|
|
958
|
+
},
|
|
959
|
+
onFocusCapture: (event) => {
|
|
960
|
+
onFocusCapture?.(event);
|
|
961
|
+
setPaused(true);
|
|
962
|
+
},
|
|
963
|
+
onPointerEnter: (event) => {
|
|
964
|
+
onPointerEnter?.(event);
|
|
965
|
+
setPaused(true);
|
|
966
|
+
},
|
|
967
|
+
onPointerLeave: (event) => {
|
|
968
|
+
onPointerLeave?.(event);
|
|
969
|
+
setPaused(false);
|
|
970
|
+
},
|
|
347
971
|
...props,
|
|
348
972
|
children: [
|
|
349
|
-
/* @__PURE__ */
|
|
350
|
-
/* @__PURE__ */
|
|
973
|
+
/* @__PURE__ */ jsx12("span", { "aria-hidden": "true", className: "fractal-toast__icon", children: icon ?? /* @__PURE__ */ jsx12(DefaultIcon, { size: 18 }) }),
|
|
974
|
+
/* @__PURE__ */ jsx12("div", { className: "fractal-toast__content", children }),
|
|
975
|
+
action && /* @__PURE__ */ jsx12("div", { className: "fractal-toast__action", children: action }),
|
|
976
|
+
/* @__PURE__ */ jsx12(
|
|
977
|
+
"button",
|
|
978
|
+
{
|
|
979
|
+
"aria-label": dismissLabel,
|
|
980
|
+
className: "fractal-toast__dismiss",
|
|
981
|
+
onClick: onDismiss,
|
|
982
|
+
type: "button",
|
|
983
|
+
children: /* @__PURE__ */ jsx12(Close, { "aria-hidden": "true", size: 14 })
|
|
984
|
+
}
|
|
985
|
+
)
|
|
351
986
|
]
|
|
352
987
|
}
|
|
353
|
-
)
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
988
|
+
);
|
|
989
|
+
});
|
|
990
|
+
var ToastViewport = React9.forwardRef(function ToastViewport2({ children, className, portal = true, position = "top-center", ...props }, ref) {
|
|
991
|
+
const viewport = /* @__PURE__ */ jsx12(
|
|
992
|
+
"div",
|
|
993
|
+
{
|
|
994
|
+
ref,
|
|
995
|
+
className: cn("fractal-toast-viewport", `fractal-toast-viewport--${position}`, className),
|
|
996
|
+
"data-position": position,
|
|
997
|
+
...props,
|
|
998
|
+
children
|
|
999
|
+
}
|
|
1000
|
+
);
|
|
1001
|
+
if (!portal || typeof document === "undefined") return viewport;
|
|
1002
|
+
return createPortal(viewport, document.body);
|
|
1003
|
+
});
|
|
364
1004
|
|
|
365
1005
|
// src/components/Toggle/Toggle.tsx
|
|
366
|
-
import { jsx as
|
|
1006
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
367
1007
|
function Toggle({ checked, onChange, label, className }) {
|
|
368
|
-
return /* @__PURE__ */
|
|
1008
|
+
return /* @__PURE__ */ jsxs12(
|
|
369
1009
|
"button",
|
|
370
1010
|
{
|
|
371
1011
|
type: "button",
|
|
@@ -374,18 +1014,19 @@ function Toggle({ checked, onChange, label, className }) {
|
|
|
374
1014
|
className: cn("fractal-toggle", className),
|
|
375
1015
|
onClick: () => onChange(!checked),
|
|
376
1016
|
children: [
|
|
377
|
-
/* @__PURE__ */
|
|
378
|
-
label && /* @__PURE__ */
|
|
1017
|
+
/* @__PURE__ */ jsx13("span", { className: cn("fractal-toggle-track", checked && "fractal-toggle-track--checked"), children: /* @__PURE__ */ jsx13("span", { className: cn("fractal-toggle-thumb", checked && "fractal-toggle-thumb--checked") }) }),
|
|
1018
|
+
label && /* @__PURE__ */ jsx13("span", { className: "fractal-toggle-label", children: label })
|
|
379
1019
|
]
|
|
380
1020
|
}
|
|
381
1021
|
);
|
|
382
1022
|
}
|
|
383
1023
|
|
|
384
1024
|
// src/components/Modal/Modal.tsx
|
|
385
|
-
import
|
|
1025
|
+
import * as React10 from "react";
|
|
1026
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
386
1027
|
|
|
387
1028
|
// src/hooks/useEscapeDismiss.ts
|
|
388
|
-
import { useEffect, useRef } from "react";
|
|
1029
|
+
import { useEffect as useEffect3, useRef as useRef4 } from "react";
|
|
389
1030
|
var nextId = 0;
|
|
390
1031
|
var stack = [];
|
|
391
1032
|
function insertSorted(entry) {
|
|
@@ -418,9 +1059,9 @@ function ensureGlobalListener() {
|
|
|
418
1059
|
);
|
|
419
1060
|
}
|
|
420
1061
|
function useEscapeDismiss(priority, handler, enabled = true) {
|
|
421
|
-
const handlerRef =
|
|
1062
|
+
const handlerRef = useRef4(handler);
|
|
422
1063
|
handlerRef.current = handler;
|
|
423
|
-
|
|
1064
|
+
useEffect3(() => {
|
|
424
1065
|
if (!enabled) return;
|
|
425
1066
|
ensureGlobalListener();
|
|
426
1067
|
const id = nextId++;
|
|
@@ -434,27 +1075,99 @@ function useEscapeDismiss(priority, handler, enabled = true) {
|
|
|
434
1075
|
}, [priority, enabled]);
|
|
435
1076
|
}
|
|
436
1077
|
|
|
1078
|
+
// src/components/UILoader/UILoader.tsx
|
|
1079
|
+
import { useId as useId2 } from "react";
|
|
1080
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1081
|
+
var LEFT = "M0 53C0 51 1.6 49.5 3.6 49.5h16.5c1 0 1.9.4 2.5 1L42.1 69.6c.7.7 1.1 1.6 1.1 2.5v16.2c0 2-1.6 3.5-3.6 3.5H23.1c-1 0-1.9-.4-2.5-1L1.1 71.7C.4 71 0 70.1 0 69.2V53Z";
|
|
1082
|
+
var CENTER = "M0 3.5C0 1.6 1.6 0 3.6 0h16.5c1 0 1.9.4 2.5 1L92.5 69.6c.7.7 1 1.6 1 2.5v16.2c0 2-1.6 3.5-3.6 3.5H73.4c-1 0-1.9-.4-2.5-1L1.1 22.2C.4 21.6 0 20.7 0 19.7V3.5Z";
|
|
1083
|
+
var RIGHT = "M50.4 3.5C50.4 1.6 52 0 54 0h16.5c1 0 1.9.4 2.5 1l69.8 68.6c.7.7 1.1 1.6 1.1 2.5v16.2c0 2-1.6 3.5-3.6 3.5h-16.5c-1 0-1.9-.4-2.5-1L51.4 22.2c-.7-.6-1-1.5-1-2.5V3.5Z";
|
|
1084
|
+
function UILoader({ size = 48, tone = "neutral", className, style, label = "Loading", ariaHidden = false }) {
|
|
1085
|
+
const rawId = useId2().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
1086
|
+
const clipId = `fractal-ui-loader-clip-${rawId}`;
|
|
1087
|
+
const gradientId = `fractal-ui-loader-gradient-${rawId}`;
|
|
1088
|
+
const height = typeof size === "number" ? `${size}px` : size;
|
|
1089
|
+
return /* @__PURE__ */ jsx14(
|
|
1090
|
+
"span",
|
|
1091
|
+
{
|
|
1092
|
+
"aria-hidden": ariaHidden || void 0,
|
|
1093
|
+
"aria-label": ariaHidden ? void 0 : label,
|
|
1094
|
+
className: cn("fractal-ui-loader", `fractal-ui-loader--${tone}`, className),
|
|
1095
|
+
role: ariaHidden ? void 0 : "status",
|
|
1096
|
+
style,
|
|
1097
|
+
children: /* @__PURE__ */ jsxs13("svg", { "aria-hidden": "true", fill: "none", viewBox: "0 0 144 92", style: { height, width: "auto" }, children: [
|
|
1098
|
+
/* @__PURE__ */ jsxs13("defs", { children: [
|
|
1099
|
+
/* @__PURE__ */ jsxs13("linearGradient", { id: gradientId, x1: "59", x2: "94.3", y1: "0", y2: "89.1", gradientUnits: "userSpaceOnUse", children: [
|
|
1100
|
+
/* @__PURE__ */ jsx14("stop", { className: "fractal-ui-loader__stop-start", offset: "0" }),
|
|
1101
|
+
/* @__PURE__ */ jsx14("stop", { className: "fractal-ui-loader__stop-end", offset: "1" })
|
|
1102
|
+
] }),
|
|
1103
|
+
/* @__PURE__ */ jsxs13("linearGradient", { id: `${gradientId}-shine`, x1: "0", x2: "1", y1: "0", y2: "0", children: [
|
|
1104
|
+
/* @__PURE__ */ jsx14("stop", { offset: "0.4", stopColor: "white", stopOpacity: "0" }),
|
|
1105
|
+
/* @__PURE__ */ jsx14("stop", { offset: "0.5", stopColor: "white", stopOpacity: "0.62" }),
|
|
1106
|
+
/* @__PURE__ */ jsx14("stop", { offset: "0.6", stopColor: "white", stopOpacity: "0" })
|
|
1107
|
+
] }),
|
|
1108
|
+
/* @__PURE__ */ jsxs13("clipPath", { id: clipId, children: [
|
|
1109
|
+
/* @__PURE__ */ jsx14("path", { d: LEFT }),
|
|
1110
|
+
/* @__PURE__ */ jsx14("path", { d: CENTER }),
|
|
1111
|
+
/* @__PURE__ */ jsx14("path", { d: RIGHT })
|
|
1112
|
+
] })
|
|
1113
|
+
] }),
|
|
1114
|
+
/* @__PURE__ */ jsx14("path", { d: CENTER, fill: `url(#${gradientId})` }),
|
|
1115
|
+
/* @__PURE__ */ jsx14("path", { d: RIGHT, fill: `url(#${gradientId})` }),
|
|
1116
|
+
/* @__PURE__ */ jsx14("path", { d: LEFT, fill: `url(#${gradientId})` }),
|
|
1117
|
+
/* @__PURE__ */ jsx14("g", { clipPath: `url(#${clipId})`, children: /* @__PURE__ */ jsx14("g", { className: "fractal-ui-loader__shine-axis", transform: "translate(72 46) rotate(68.4)", children: /* @__PURE__ */ jsx14("rect", { className: "fractal-ui-loader__shine", x: "-160", y: "-100", width: "320", height: "200", fill: `url(#${gradientId}-shine)` }) }) })
|
|
1118
|
+
] })
|
|
1119
|
+
}
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
437
1123
|
// src/components/Modal/Modal.tsx
|
|
438
|
-
import { jsx as
|
|
439
|
-
var CloseIcon = () => /* @__PURE__ */
|
|
440
|
-
function Modal({ open, onClose, title, children, danger, warn, footer, noClose, size = "sm", className }) {
|
|
441
|
-
|
|
1124
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1125
|
+
var CloseIcon = () => /* @__PURE__ */ jsx15("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx15("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
1126
|
+
function Modal({ open, onClose, title, children, danger, warn, footer, noClose, closeDisabled = false, size = "sm", loading = false, loadingLabel = "Loading", scrollable = false, className }) {
|
|
1127
|
+
const [bodyScrolled, setBodyScrolled] = React10.useState(false);
|
|
1128
|
+
useEscapeDismiss(80, onClose, open && !noClose && !closeDisabled);
|
|
1129
|
+
React10.useEffect(() => {
|
|
1130
|
+
if (open) setBodyScrolled(false);
|
|
1131
|
+
}, [open]);
|
|
442
1132
|
if (!open) return null;
|
|
443
1133
|
const sizeClass = `fractal-modal-panel--${size}`;
|
|
444
1134
|
const accentClass = danger ? "fractal-modal-panel--danger" : warn ? "fractal-modal-panel--warn" : "";
|
|
445
|
-
return
|
|
446
|
-
/* @__PURE__ */
|
|
1135
|
+
return createPortal2(
|
|
1136
|
+
/* @__PURE__ */ jsx15("div", { className: "fractal-modal-overlay", onClick: noClose || closeDisabled ? void 0 : onClose, children: /* @__PURE__ */ jsxs14(
|
|
447
1137
|
"div",
|
|
448
1138
|
{
|
|
449
|
-
className: cn("fractal-modal-panel", sizeClass, accentClass, className),
|
|
1139
|
+
className: cn("fractal-modal-panel", sizeClass, accentClass, scrollable && "fractal-modal-panel--scrollable", className),
|
|
450
1140
|
onClick: (e) => e.stopPropagation(),
|
|
451
1141
|
children: [
|
|
452
|
-
/* @__PURE__ */
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
1142
|
+
/* @__PURE__ */ jsxs14(
|
|
1143
|
+
"div",
|
|
1144
|
+
{
|
|
1145
|
+
className: cn(
|
|
1146
|
+
"fractal-modal-title-row",
|
|
1147
|
+
scrollable && "fractal-modal-title-row--scrollable",
|
|
1148
|
+
scrollable && bodyScrolled && "fractal-modal-title-row--scrolled"
|
|
1149
|
+
),
|
|
1150
|
+
children: [
|
|
1151
|
+
/* @__PURE__ */ jsx15("h3", { className: cn("fractal-modal-title", danger && "fractal-modal-title--danger", warn && "fractal-modal-title--warn"), children: title }),
|
|
1152
|
+
!noClose && /* @__PURE__ */ jsx15("button", { type: "button", onClick: onClose, title: "Close", disabled: closeDisabled, className: "fractal-modal-close", children: /* @__PURE__ */ jsx15(CloseIcon, {}) })
|
|
1153
|
+
]
|
|
1154
|
+
}
|
|
1155
|
+
),
|
|
1156
|
+
/* @__PURE__ */ jsx15(
|
|
1157
|
+
"div",
|
|
1158
|
+
{
|
|
1159
|
+
"aria-busy": loading || void 0,
|
|
1160
|
+
className: cn(
|
|
1161
|
+
"fractal-modal-body",
|
|
1162
|
+
size === "lg" && "fractal-modal-body--lg",
|
|
1163
|
+
loading && "fractal-modal-body--loading",
|
|
1164
|
+
scrollable && "fractal-modal-body--scrollable"
|
|
1165
|
+
),
|
|
1166
|
+
onScroll: scrollable ? (event) => setBodyScrolled(event.currentTarget.scrollTop > 0) : void 0,
|
|
1167
|
+
children: loading ? /* @__PURE__ */ jsx15(UILoader, { label: loadingLabel }) : children
|
|
1168
|
+
}
|
|
1169
|
+
),
|
|
1170
|
+
footer && /* @__PURE__ */ jsx15("div", { className: cn("fractal-modal-footer", scrollable && "fractal-modal-footer--divided"), children: footer })
|
|
458
1171
|
]
|
|
459
1172
|
}
|
|
460
1173
|
) }),
|
|
@@ -462,94 +1175,441 @@ function Modal({ open, onClose, title, children, danger, warn, footer, noClose,
|
|
|
462
1175
|
);
|
|
463
1176
|
}
|
|
464
1177
|
|
|
1178
|
+
// src/components/TableLayout/TableOverflowMenu.tsx
|
|
1179
|
+
import { useId as useId3, useLayoutEffect, useRef as useRef5, useState as useState5 } from "react";
|
|
1180
|
+
import { createPortal as createPortal3 } from "react-dom";
|
|
1181
|
+
import { Ellipsis } from "@mirror-physics/fractal-icons";
|
|
1182
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1183
|
+
function TableOverflowMenu({ items, label, disabled, onSelect }) {
|
|
1184
|
+
const id = useId3();
|
|
1185
|
+
const trigger = useRef5(null);
|
|
1186
|
+
const panel = useRef5(null);
|
|
1187
|
+
const initialLast = useRef5(false);
|
|
1188
|
+
const [open, setOpen] = useState5(false);
|
|
1189
|
+
const [position, setPosition] = useState5({ top: 0, left: 0 });
|
|
1190
|
+
const buttons = () => [...panel.current?.querySelectorAll("button:not(:disabled)") ?? []];
|
|
1191
|
+
const close = (restore = true) => {
|
|
1192
|
+
setOpen(false);
|
|
1193
|
+
if (restore) trigger.current?.focus({ preventScroll: true });
|
|
1194
|
+
};
|
|
1195
|
+
useEscapeDismiss(100, () => close(), open);
|
|
1196
|
+
useLayoutEffect(() => {
|
|
1197
|
+
if (!open || !trigger.current || !panel.current) return;
|
|
1198
|
+
const place = () => {
|
|
1199
|
+
const anchor = trigger.current.getBoundingClientRect();
|
|
1200
|
+
const menu = panel.current.getBoundingClientRect();
|
|
1201
|
+
const above = window.innerHeight - anchor.bottom < menu.height + 8 && anchor.top > window.innerHeight - anchor.bottom;
|
|
1202
|
+
setPosition({
|
|
1203
|
+
top: Math.max(8, Math.min(above ? anchor.top - menu.height - 4 : anchor.bottom + 4, window.innerHeight - menu.height - 8)),
|
|
1204
|
+
left: Math.max(8, Math.min(anchor.right - menu.width, window.innerWidth - menu.width - 8))
|
|
1205
|
+
});
|
|
1206
|
+
};
|
|
1207
|
+
place();
|
|
1208
|
+
const enabled = buttons();
|
|
1209
|
+
(initialLast.current ? enabled[enabled.length - 1] : enabled[0])?.focus({ preventScroll: true });
|
|
1210
|
+
if (enabled.length === 0) panel.current.focus({ preventScroll: true });
|
|
1211
|
+
const outside = (event) => {
|
|
1212
|
+
if (!panel.current?.contains(event.target) && !trigger.current?.contains(event.target)) setOpen(false);
|
|
1213
|
+
};
|
|
1214
|
+
const scroll = (event) => {
|
|
1215
|
+
if (!panel.current?.contains(event.target)) setOpen(false);
|
|
1216
|
+
};
|
|
1217
|
+
const observer = new ResizeObserver(place);
|
|
1218
|
+
observer.observe(panel.current);
|
|
1219
|
+
document.addEventListener("pointerdown", outside);
|
|
1220
|
+
window.addEventListener("scroll", scroll, true);
|
|
1221
|
+
window.addEventListener("resize", place);
|
|
1222
|
+
return () => {
|
|
1223
|
+
observer.disconnect();
|
|
1224
|
+
document.removeEventListener("pointerdown", outside);
|
|
1225
|
+
window.removeEventListener("scroll", scroll, true);
|
|
1226
|
+
window.removeEventListener("resize", place);
|
|
1227
|
+
};
|
|
1228
|
+
}, [open]);
|
|
1229
|
+
useLayoutEffect(() => {
|
|
1230
|
+
if (disabled || items.length === 0) setOpen(false);
|
|
1231
|
+
}, [disabled, items.length]);
|
|
1232
|
+
if (items.length === 0) return null;
|
|
1233
|
+
return /* @__PURE__ */ jsxs15("div", { className: "fractal-table-overflow-trigger", children: [
|
|
1234
|
+
/* @__PURE__ */ jsx16(
|
|
1235
|
+
Button,
|
|
1236
|
+
{
|
|
1237
|
+
ref: trigger,
|
|
1238
|
+
buttonType: "ghost",
|
|
1239
|
+
controlSize: "sm",
|
|
1240
|
+
hasIconOnly: true,
|
|
1241
|
+
icon: /* @__PURE__ */ jsx16(Ellipsis, { "aria-hidden": true }),
|
|
1242
|
+
"aria-label": label,
|
|
1243
|
+
"aria-haspopup": "menu",
|
|
1244
|
+
"aria-expanded": open,
|
|
1245
|
+
"aria-controls": open ? id : void 0,
|
|
1246
|
+
disabled,
|
|
1247
|
+
onClick: () => {
|
|
1248
|
+
initialLast.current = false;
|
|
1249
|
+
setOpen(!open);
|
|
1250
|
+
},
|
|
1251
|
+
onKeyDown: (event) => {
|
|
1252
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
1253
|
+
event.preventDefault();
|
|
1254
|
+
initialLast.current = event.key === "ArrowUp";
|
|
1255
|
+
setOpen(true);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
),
|
|
1260
|
+
open && createPortal3(/* @__PURE__ */ jsx16(
|
|
1261
|
+
"div",
|
|
1262
|
+
{
|
|
1263
|
+
ref: panel,
|
|
1264
|
+
id,
|
|
1265
|
+
role: "menu",
|
|
1266
|
+
tabIndex: -1,
|
|
1267
|
+
"aria-label": label,
|
|
1268
|
+
className: "fractal-table-overflow-menu",
|
|
1269
|
+
style: position,
|
|
1270
|
+
onClick: (event) => event.stopPropagation(),
|
|
1271
|
+
onKeyDown: (event) => {
|
|
1272
|
+
event.stopPropagation();
|
|
1273
|
+
const enabled = buttons(), index = enabled.indexOf(document.activeElement);
|
|
1274
|
+
if (event.key === "Escape") {
|
|
1275
|
+
event.preventDefault();
|
|
1276
|
+
close();
|
|
1277
|
+
} else if (event.key === "Tab") {
|
|
1278
|
+
close();
|
|
1279
|
+
} else if (["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) {
|
|
1280
|
+
event.preventDefault();
|
|
1281
|
+
const next = event.key === "Home" ? 0 : event.key === "End" ? enabled.length - 1 : (index + (event.key === "ArrowDown" ? 1 : -1) + enabled.length) % enabled.length;
|
|
1282
|
+
enabled[next]?.focus();
|
|
1283
|
+
} else if (event.key.length === 1 && /\S/.test(event.key)) {
|
|
1284
|
+
const ordered = [...enabled.slice(index + 1), ...enabled.slice(0, index + 1)];
|
|
1285
|
+
ordered.find((button) => button.textContent?.trim().toLocaleLowerCase().startsWith(event.key.toLocaleLowerCase()))?.focus();
|
|
1286
|
+
}
|
|
1287
|
+
},
|
|
1288
|
+
children: items.map((item) => /* @__PURE__ */ jsxs15(
|
|
1289
|
+
"button",
|
|
1290
|
+
{
|
|
1291
|
+
type: "button",
|
|
1292
|
+
role: "menuitem",
|
|
1293
|
+
tabIndex: -1,
|
|
1294
|
+
disabled: item.disabled,
|
|
1295
|
+
onClick: () => {
|
|
1296
|
+
onSelect(item.value);
|
|
1297
|
+
close();
|
|
1298
|
+
},
|
|
1299
|
+
children: [
|
|
1300
|
+
item.icon && /* @__PURE__ */ jsx16("span", { "aria-hidden": "true", children: item.icon }),
|
|
1301
|
+
item.label
|
|
1302
|
+
]
|
|
1303
|
+
},
|
|
1304
|
+
item.value
|
|
1305
|
+
))
|
|
1306
|
+
}
|
|
1307
|
+
), document.body)
|
|
1308
|
+
] });
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/components/TableLayout/TableLayout.tsx
|
|
1312
|
+
import { useLayoutEffect as useLayoutEffect2, useRef as useRef6, useState as useState6 } from "react";
|
|
1313
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
1314
|
+
var length = (value) => typeof value === "number" ? `${value}px` : value;
|
|
1315
|
+
function columnStyle(column) {
|
|
1316
|
+
return {
|
|
1317
|
+
width: column.width,
|
|
1318
|
+
"--table-column-width": !column.width || column.width.includes("%") ? void 0 : `calc(${column.width} - 1px)`,
|
|
1319
|
+
"--table-column-min": length(column.minWidth),
|
|
1320
|
+
"--table-column-max": length(column.maxWidth)
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
function useOverflow() {
|
|
1324
|
+
const ref = useRef6(null);
|
|
1325
|
+
const [overflow, setOverflow] = useState6(false);
|
|
1326
|
+
useLayoutEffect2(() => {
|
|
1327
|
+
const el = ref.current;
|
|
1328
|
+
if (!el) return;
|
|
1329
|
+
const measure = () => setOverflow(el.scrollWidth > el.clientWidth + 1 || el.scrollHeight > el.clientHeight + 1);
|
|
1330
|
+
const resize = new ResizeObserver(measure);
|
|
1331
|
+
const observe = () => {
|
|
1332
|
+
resize.disconnect();
|
|
1333
|
+
resize.observe(el);
|
|
1334
|
+
for (const child of el.children) resize.observe(child);
|
|
1335
|
+
measure();
|
|
1336
|
+
};
|
|
1337
|
+
const mutation = new MutationObserver(observe);
|
|
1338
|
+
mutation.observe(el, { childList: true, subtree: true, characterData: true });
|
|
1339
|
+
observe();
|
|
1340
|
+
return () => {
|
|
1341
|
+
resize.disconnect();
|
|
1342
|
+
mutation.disconnect();
|
|
1343
|
+
};
|
|
1344
|
+
}, []);
|
|
1345
|
+
return { ref, overflow };
|
|
1346
|
+
}
|
|
1347
|
+
function TableViewport({ children, maxHeight, label }) {
|
|
1348
|
+
const { ref, overflow } = useOverflow();
|
|
1349
|
+
return /* @__PURE__ */ jsx17("div", { ref, className: "fractal-table-viewport", style: { maxHeight }, tabIndex: overflow ? 0 : void 0, role: overflow ? "region" : void 0, "aria-label": overflow ? `${label}, scrollable table` : void 0, children });
|
|
1350
|
+
}
|
|
1351
|
+
function TableCellContent({ children, label }) {
|
|
1352
|
+
const { ref, overflow } = useOverflow();
|
|
1353
|
+
useLayoutEffect2(() => {
|
|
1354
|
+
const cell = ref.current;
|
|
1355
|
+
if (!cell) return;
|
|
1356
|
+
const wheel = (event) => {
|
|
1357
|
+
if (event.defaultPrevented || event.ctrlKey || !event.cancelable || Math.abs(event.deltaX) <= Math.abs(event.deltaY)) return;
|
|
1358
|
+
const range = cell.scrollWidth - cell.clientWidth;
|
|
1359
|
+
if (range <= 1) return;
|
|
1360
|
+
const rtl = getComputedStyle(cell).direction === "rtl";
|
|
1361
|
+
const left = rtl ? -range : 0, right = rtl ? 0 : range;
|
|
1362
|
+
if (event.deltaX > 0 ? cell.scrollLeft < right - 1 : cell.scrollLeft > left + 1) return;
|
|
1363
|
+
const table = cell.closest(".fractal-table-viewport");
|
|
1364
|
+
if (!table) return;
|
|
1365
|
+
const tableRange = table.scrollWidth - table.clientWidth;
|
|
1366
|
+
const tableRtl = getComputedStyle(table).direction === "rtl";
|
|
1367
|
+
const tableLeft = tableRtl ? -tableRange : 0, tableRight = tableRtl ? 0 : tableRange;
|
|
1368
|
+
if (event.deltaX > 0 ? table.scrollLeft >= tableRight - 1 : table.scrollLeft <= tableLeft + 1) return;
|
|
1369
|
+
const unit = event.deltaMode === 1 ? parseFloat(getComputedStyle(cell).lineHeight) || 16 : event.deltaMode === 2 ? table.clientWidth : 1;
|
|
1370
|
+
event.preventDefault();
|
|
1371
|
+
table.scrollLeft += event.deltaX * unit;
|
|
1372
|
+
};
|
|
1373
|
+
cell.addEventListener("wheel", wheel, { passive: false });
|
|
1374
|
+
return () => cell.removeEventListener("wheel", wheel);
|
|
1375
|
+
}, [ref]);
|
|
1376
|
+
return /* @__PURE__ */ jsx17(
|
|
1377
|
+
"div",
|
|
1378
|
+
{
|
|
1379
|
+
ref,
|
|
1380
|
+
className: "fractal-table-cell-scroll",
|
|
1381
|
+
"data-overflow": overflow || void 0,
|
|
1382
|
+
tabIndex: overflow ? 0 : void 0,
|
|
1383
|
+
role: overflow ? "region" : void 0,
|
|
1384
|
+
"aria-label": overflow ? `${label}, scroll horizontally for full content` : void 0,
|
|
1385
|
+
onKeyDown: (event) => {
|
|
1386
|
+
if (!overflow || event.target !== event.currentTarget) return;
|
|
1387
|
+
if (["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) {
|
|
1388
|
+
event.preventDefault();
|
|
1389
|
+
event.stopPropagation();
|
|
1390
|
+
const el = event.currentTarget;
|
|
1391
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") el.scrollBy({ left: event.key === "ArrowLeft" ? -40 : 40 });
|
|
1392
|
+
else el.scrollLeft = event.key === "Home" ? 0 : el.scrollWidth * (getComputedStyle(el).direction === "rtl" ? -1 : 1);
|
|
1393
|
+
} else if (event.key === " " || event.key === "Enter") event.stopPropagation();
|
|
1394
|
+
},
|
|
1395
|
+
children: /* @__PURE__ */ jsx17("div", { className: "fractal-table-cell-content", children })
|
|
1396
|
+
}
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
465
1400
|
// src/components/DataTable/DataTable.tsx
|
|
466
|
-
import { jsx as
|
|
467
|
-
function DataTable({ columns, data, emptyMessage, header, size = "md", className }) {
|
|
468
|
-
return /* @__PURE__ */
|
|
469
|
-
header && /* @__PURE__ */
|
|
470
|
-
data.length === 0 ? /* @__PURE__ */
|
|
471
|
-
/* @__PURE__ */
|
|
472
|
-
|
|
1401
|
+
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1402
|
+
function DataTable({ columns, data, emptyMessage, header, size = "md", className, overflowMenu, layout = "fill", stickyHeader = false, stickyFirstColumn = false, maxHeight, "aria-label": ariaLabel = "Data table" }) {
|
|
1403
|
+
return /* @__PURE__ */ jsx18("div", { className: cn("fractal-table-layout", "fractal-datatable", `fractal-datatable--${size}`, className), "data-overflow-menu": Boolean(overflowMenu), "data-table-layout": layout, "data-sticky-first": stickyFirstColumn, "data-sticky-header": stickyHeader, children: /* @__PURE__ */ jsxs16("div", { className: "fractal-datatable-surface", children: [
|
|
1404
|
+
header && /* @__PURE__ */ jsx18("div", { className: "fractal-datatable-header", children: header }),
|
|
1405
|
+
data.length === 0 ? /* @__PURE__ */ jsx18("div", { className: "fractal-datatable-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsx18(TableViewport, { maxHeight, label: ariaLabel, children: /* @__PURE__ */ jsxs16("table", { "aria-label": ariaLabel, className: "fractal-datatable-table", children: [
|
|
1406
|
+
/* @__PURE__ */ jsx18("thead", { children: /* @__PURE__ */ jsxs16("tr", { className: "fractal-datatable-thead-row", children: [
|
|
1407
|
+
columns.map((col, columnIndex) => /* @__PURE__ */ jsx18("th", { scope: "col", className: "fractal-datatable-th", style: columnStyle(col), "data-pinned-column": columnIndex === 0 ? "data" : void 0, children: /* @__PURE__ */ jsx18(TableCellContent, { label: `${col.header} column`, children: col.header }) }, col.key)),
|
|
1408
|
+
overflowMenu && /* @__PURE__ */ jsx18("th", { scope: "col", "aria-label": "Row actions", className: "fractal-datatable-th fractal-table-overflow-cell", "data-pinned-column": "overflow" })
|
|
1409
|
+
] }) }),
|
|
1410
|
+
/* @__PURE__ */ jsx18("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ jsxs16(
|
|
473
1411
|
"tr",
|
|
474
1412
|
{
|
|
475
1413
|
className: "fractal-datatable-row",
|
|
476
|
-
children:
|
|
1414
|
+
children: [
|
|
1415
|
+
columns.map((col, columnIndex) => /* @__PURE__ */ jsx18("td", { className: "fractal-datatable-td", style: columnStyle(col), "data-pinned-column": columnIndex === 0 ? "data" : void 0, children: /* @__PURE__ */ jsx18(TableCellContent, { label: `${col.header || "Value"}, row ${rowIndex + 1}`, children: col.render(row, rowIndex) }) }, col.key)),
|
|
1416
|
+
overflowMenu && /* @__PURE__ */ jsx18("td", { className: "fractal-datatable-td fractal-table-overflow-cell", "data-pinned-column": "overflow", children: /* @__PURE__ */ jsx18(TableOverflowMenu, { items: overflowMenu.items(row, rowIndex), label: overflowMenu.getLabel?.(row, rowIndex) ?? `Actions for row ${rowIndex + 1}`, disabled: overflowMenu.isDisabled?.(row, rowIndex), onSelect: (value) => overflowMenu.onSelect(value, row, rowIndex) }) })
|
|
1417
|
+
]
|
|
477
1418
|
},
|
|
478
1419
|
rowIndex
|
|
479
1420
|
)) })
|
|
480
|
-
] })
|
|
1421
|
+
] }) })
|
|
481
1422
|
] }) });
|
|
482
1423
|
}
|
|
483
1424
|
|
|
484
1425
|
// src/components/Dropdown/Dropdown.tsx
|
|
485
|
-
import * as
|
|
486
|
-
import { ChevronDown, ChevronUp } from "@mirror-physics/fractal-icons";
|
|
487
|
-
|
|
488
|
-
|
|
1426
|
+
import * as React11 from "react";
|
|
1427
|
+
import { Check as Check2, MagnifyingGlass as MagnifyingGlass2, ChevronDown, ChevronLeft, ChevronRight as ChevronRight2, ChevronUp } from "@mirror-physics/fractal-icons";
|
|
1428
|
+
|
|
1429
|
+
// src/components/Dropdown/position.ts
|
|
1430
|
+
function positionDropdown(trigger, panel, bounds, direction, align) {
|
|
1431
|
+
const gap = 4;
|
|
1432
|
+
const width = Math.min(panel.width, Math.max(0, bounds.right - bounds.left));
|
|
1433
|
+
let maxHeight = Math.max(0, bounds.bottom - bounds.top);
|
|
1434
|
+
let left;
|
|
1435
|
+
let top;
|
|
1436
|
+
if (direction === "left" || direction === "right") {
|
|
1437
|
+
const after = bounds.right - trigger.right - gap;
|
|
1438
|
+
const before = trigger.left - bounds.left - gap;
|
|
1439
|
+
let placeRight = direction === "right";
|
|
1440
|
+
if (width > (placeRight ? after : before) && (placeRight ? before > after : after > before)) {
|
|
1441
|
+
placeRight = !placeRight;
|
|
1442
|
+
}
|
|
1443
|
+
left = placeRight ? trigger.right + gap : trigger.left - width - gap;
|
|
1444
|
+
top = trigger.top;
|
|
1445
|
+
} else {
|
|
1446
|
+
const below = Math.max(0, bounds.bottom - trigger.bottom - gap);
|
|
1447
|
+
const above = Math.max(0, trigger.top - bounds.top - gap);
|
|
1448
|
+
let placeAbove = direction === "up";
|
|
1449
|
+
if (panel.height > (placeAbove ? above : below) && (placeAbove ? below > above : above > below)) {
|
|
1450
|
+
placeAbove = !placeAbove;
|
|
1451
|
+
}
|
|
1452
|
+
maxHeight = Math.min(maxHeight, placeAbove ? above : below);
|
|
1453
|
+
top = placeAbove ? trigger.top - Math.min(panel.height, maxHeight) - gap : trigger.bottom + gap;
|
|
1454
|
+
left = align === "right" ? trigger.right - width : trigger.left;
|
|
1455
|
+
}
|
|
1456
|
+
const height = Math.min(panel.height, maxHeight);
|
|
1457
|
+
return {
|
|
1458
|
+
left: Math.max(bounds.left, Math.min(left, bounds.right - width)),
|
|
1459
|
+
top: Math.max(bounds.top, Math.min(top, bounds.bottom - height)),
|
|
1460
|
+
maxHeight
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
// src/components/Dropdown/Dropdown.tsx
|
|
1465
|
+
import { Fragment as Fragment2, jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
489
1466
|
function Dropdown({
|
|
490
1467
|
items,
|
|
491
|
-
selectedValue,
|
|
1468
|
+
selectedValue = "",
|
|
492
1469
|
onSelect,
|
|
493
|
-
|
|
1470
|
+
multiple = false,
|
|
1471
|
+
selectedValues = [],
|
|
1472
|
+
onSelectionChange,
|
|
1473
|
+
searchable = false,
|
|
1474
|
+
searchPlaceholder = "Find an option",
|
|
1475
|
+
emptyMessage = "No matching options.",
|
|
1476
|
+
triggerLabel,
|
|
1477
|
+
fieldLabel,
|
|
494
1478
|
direction = "down",
|
|
495
1479
|
align = "left",
|
|
1480
|
+
maxHeight = 320,
|
|
1481
|
+
collisionPadding = 8,
|
|
1482
|
+
triggerOrientation = "horizontal",
|
|
496
1483
|
triggerContent,
|
|
497
1484
|
label,
|
|
1485
|
+
iconOnly = false,
|
|
498
1486
|
noChevron = false,
|
|
499
|
-
size = "
|
|
1487
|
+
size: sizeAlias = "lg",
|
|
1488
|
+
controlSize,
|
|
1489
|
+
triggerId,
|
|
500
1490
|
triggerVariant = "outline",
|
|
501
1491
|
className,
|
|
502
1492
|
triggerClassName,
|
|
503
1493
|
panelClassName,
|
|
504
|
-
closeOnSelect = true
|
|
1494
|
+
closeOnSelect = true,
|
|
1495
|
+
disabled = false
|
|
505
1496
|
}) {
|
|
506
|
-
const [
|
|
507
|
-
const
|
|
508
|
-
const
|
|
509
|
-
const
|
|
510
|
-
const
|
|
1497
|
+
const [query, setQuery] = React11.useState("");
|
|
1498
|
+
const searchRef = React11.useRef(null);
|
|
1499
|
+
const accessibleLabel = triggerLabel ?? fieldLabel ?? "Options";
|
|
1500
|
+
const size = controlSize ?? sizeAlias;
|
|
1501
|
+
const [open, setOpen] = React11.useState(false);
|
|
1502
|
+
React11.useEffect(() => {
|
|
1503
|
+
if (!open) setQuery("");
|
|
1504
|
+
}, [open]);
|
|
1505
|
+
const rootRef = React11.useRef(null);
|
|
1506
|
+
const menuId = React11.useId();
|
|
1507
|
+
const focusOnOpen = React11.useRef(false);
|
|
1508
|
+
const triggerRef = React11.useRef(null);
|
|
1509
|
+
const panelRef = React11.useRef(null);
|
|
1510
|
+
const [panelStyle, setPanelStyle] = React11.useState({});
|
|
1511
|
+
const renderableItems = items.filter(
|
|
511
1512
|
(item) => item.icon != null || item.description != null && (typeof item.description === "string" ? item.description !== "" : true)
|
|
512
1513
|
);
|
|
513
|
-
|
|
1514
|
+
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
1515
|
+
let groupTitle = "";
|
|
1516
|
+
const matching = new Set(renderableItems.filter((item) => {
|
|
1517
|
+
if (item.kind === "header") {
|
|
1518
|
+
groupTitle = String(item.description ?? "");
|
|
1519
|
+
return false;
|
|
1520
|
+
}
|
|
1521
|
+
return `${groupTitle} ${typeof item.description === "string" ? item.description : item.value}`.toLocaleLowerCase().includes(normalizedQuery);
|
|
1522
|
+
}).map((item) => item.value));
|
|
1523
|
+
const visibleItems = renderableItems.filter((item, index) => {
|
|
1524
|
+
if (item.kind !== "header") return matching.has(item.value);
|
|
1525
|
+
const nextHeader = renderableItems.findIndex((next, i) => i > index && next.kind === "header");
|
|
1526
|
+
return renderableItems.slice(index + 1, nextHeader < 0 ? void 0 : nextHeader).some((next) => matching.has(next.value));
|
|
1527
|
+
});
|
|
514
1528
|
const selectableItems = visibleItems.filter((i) => i.kind !== "header");
|
|
515
1529
|
const selected = items.find((i) => i.value === selectedValue && i.kind !== "header") ?? selectableItems[0] ?? visibleItems[0];
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
const
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
1530
|
+
const ChevronIcon = direction === "up" ? ChevronUp : direction === "left" ? ChevronLeft : direction === "right" ? ChevronRight2 : ChevronDown;
|
|
1531
|
+
useEscapeDismiss(100, () => {
|
|
1532
|
+
setOpen(false);
|
|
1533
|
+
triggerRef.current?.focus();
|
|
1534
|
+
}, open);
|
|
1535
|
+
React11.useLayoutEffect(() => {
|
|
1536
|
+
const root = rootRef.current;
|
|
1537
|
+
const trigger = triggerRef.current;
|
|
1538
|
+
const panel = panelRef.current;
|
|
1539
|
+
if (!open || !root || !trigger || !panel) return;
|
|
1540
|
+
const updatePosition = () => {
|
|
1541
|
+
const viewport = window.visualViewport;
|
|
1542
|
+
const bounds = {
|
|
1543
|
+
left: viewport?.offsetLeft ?? 0,
|
|
1544
|
+
top: viewport?.offsetTop ?? 0,
|
|
1545
|
+
right: (viewport?.offsetLeft ?? 0) + (viewport?.width ?? document.documentElement.clientWidth),
|
|
1546
|
+
bottom: (viewport?.offsetTop ?? 0) + (viewport?.height ?? document.documentElement.clientHeight)
|
|
1547
|
+
};
|
|
1548
|
+
for (let ancestor = root.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
1549
|
+
const style = getComputedStyle(ancestor);
|
|
1550
|
+
const rect = ancestor.getBoundingClientRect();
|
|
1551
|
+
if (/(auto|scroll|hidden|clip)/.test(style.overflowX)) {
|
|
1552
|
+
bounds.left = Math.max(bounds.left, rect.left + ancestor.clientLeft);
|
|
1553
|
+
bounds.right = Math.min(bounds.right, rect.left + ancestor.clientLeft + ancestor.clientWidth);
|
|
1554
|
+
}
|
|
1555
|
+
if (/(auto|scroll|hidden|clip)/.test(style.overflowY)) {
|
|
1556
|
+
bounds.top = Math.max(bounds.top, rect.top + ancestor.clientTop);
|
|
1557
|
+
bounds.bottom = Math.min(bounds.bottom, rect.top + ancestor.clientTop + ancestor.clientHeight);
|
|
1558
|
+
}
|
|
541
1559
|
}
|
|
542
|
-
|
|
543
|
-
left
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
1560
|
+
const padding = Math.max(0, collisionPadding);
|
|
1561
|
+
bounds.left += padding;
|
|
1562
|
+
bounds.right = Math.max(bounds.left, bounds.right - padding);
|
|
1563
|
+
bounds.top += padding;
|
|
1564
|
+
bounds.bottom = Math.max(bounds.top, bounds.bottom - padding);
|
|
1565
|
+
const triggerRect = trigger.getBoundingClientRect();
|
|
1566
|
+
const rootRect = root.getBoundingClientRect();
|
|
1567
|
+
const availableWidth = bounds.right - bounds.left;
|
|
1568
|
+
const heightLimit = Math.max(0, maxHeight);
|
|
1569
|
+
panel.style.maxWidth = `${availableWidth}px`;
|
|
1570
|
+
panel.style.minWidth = `${Math.min(triggerRect.width, availableWidth)}px`;
|
|
1571
|
+
const panelRect = panel.getBoundingClientRect();
|
|
1572
|
+
const desiredHeight = Math.min(heightLimit, panel.scrollHeight + panel.offsetHeight - panel.clientHeight);
|
|
1573
|
+
const position = positionDropdown(triggerRect, { width: panelRect.width, height: desiredHeight }, bounds, direction, align);
|
|
1574
|
+
const nextStyle = {
|
|
1575
|
+
left: position.left - rootRect.left - root.clientLeft + root.scrollLeft,
|
|
1576
|
+
top: position.top - rootRect.top - root.clientTop + root.scrollTop,
|
|
1577
|
+
minWidth: Math.min(triggerRect.width, availableWidth),
|
|
1578
|
+
maxWidth: availableWidth,
|
|
1579
|
+
maxHeight: Math.min(heightLimit, position.maxHeight)
|
|
1580
|
+
};
|
|
1581
|
+
for (const key of Object.keys(nextStyle)) {
|
|
1582
|
+
panel.style[key] = `${nextStyle[key]}px`;
|
|
548
1583
|
}
|
|
1584
|
+
setPanelStyle((previous) => Object.entries(nextStyle).every(([key, value]) => previous[key] === value) ? previous : nextStyle);
|
|
1585
|
+
};
|
|
1586
|
+
updatePosition();
|
|
1587
|
+
if (searchable && !panel.contains(document.activeElement)) searchRef.current?.focus({ preventScroll: true });
|
|
1588
|
+
if (focusOnOpen.current) {
|
|
1589
|
+
focusOnOpen.current = false;
|
|
1590
|
+
const option = panel.querySelector('[aria-selected="true"]') ?? panel.querySelector('[role="option"]');
|
|
1591
|
+
option?.focus({ preventScroll: true });
|
|
1592
|
+
option?.scrollIntoView({ block: "nearest" });
|
|
549
1593
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
1594
|
+
const resize = new ResizeObserver(updatePosition);
|
|
1595
|
+
resize.observe(root);
|
|
1596
|
+
resize.observe(panel);
|
|
1597
|
+
for (let ancestor = root.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
1598
|
+
resize.observe(ancestor);
|
|
1599
|
+
}
|
|
1600
|
+
window.addEventListener("resize", updatePosition);
|
|
1601
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
1602
|
+
window.visualViewport?.addEventListener("resize", updatePosition);
|
|
1603
|
+
window.visualViewport?.addEventListener("scroll", updatePosition);
|
|
1604
|
+
return () => {
|
|
1605
|
+
resize.disconnect();
|
|
1606
|
+
window.removeEventListener("resize", updatePosition);
|
|
1607
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
1608
|
+
window.visualViewport?.removeEventListener("resize", updatePosition);
|
|
1609
|
+
window.visualViewport?.removeEventListener("scroll", updatePosition);
|
|
1610
|
+
};
|
|
1611
|
+
}, [open, direction, align, maxHeight, collisionPadding, items, label, query, searchable]);
|
|
1612
|
+
React11.useEffect(() => {
|
|
553
1613
|
if (!open) return;
|
|
554
1614
|
const handleClickOutside = (e) => {
|
|
555
1615
|
const target = e.target;
|
|
@@ -559,75 +1619,120 @@ function Dropdown({
|
|
|
559
1619
|
document.addEventListener("mousedown", handleClickOutside);
|
|
560
1620
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
561
1621
|
}, [open]);
|
|
562
|
-
|
|
563
|
-
|
|
1622
|
+
if (renderableItems.length === 0) return null;
|
|
1623
|
+
return /* @__PURE__ */ jsxs17("div", { ref: rootRef, "data-control-size": size, "data-filled": multiple ? selectedValues.length > 0 : Boolean(selectedValue), "data-open": open, className: cn("fractal-dropdown", fieldLabel && "fractal-floating-field fractal-floating-field--dropdown", className), children: [
|
|
1624
|
+
/* @__PURE__ */ jsxs17(
|
|
564
1625
|
"button",
|
|
565
1626
|
{
|
|
566
1627
|
ref: triggerRef,
|
|
1628
|
+
id: triggerId ?? `${menuId}-trigger`,
|
|
1629
|
+
"data-control-size": size,
|
|
567
1630
|
type: "button",
|
|
568
|
-
|
|
1631
|
+
disabled,
|
|
1632
|
+
"aria-haspopup": searchable ? "dialog" : "listbox",
|
|
569
1633
|
"aria-expanded": open,
|
|
570
|
-
"aria-
|
|
1634
|
+
"aria-controls": open ? searchable ? menuId : `${menuId}-options` : void 0,
|
|
1635
|
+
onKeyDown: (event) => {
|
|
1636
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
1637
|
+
event.preventDefault();
|
|
1638
|
+
if (open) {
|
|
1639
|
+
const option = panelRef.current?.querySelector('[aria-selected="true"]') ?? panelRef.current?.querySelector('[role="option"]');
|
|
1640
|
+
option?.focus({ preventScroll: true });
|
|
1641
|
+
option?.scrollIntoView({ block: "nearest" });
|
|
1642
|
+
} else {
|
|
1643
|
+
focusOnOpen.current = true;
|
|
1644
|
+
setOpen(true);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
"aria-label": accessibleLabel,
|
|
571
1649
|
onClick: () => setOpen((o) => !o),
|
|
572
1650
|
className: cn(
|
|
1651
|
+
"fractal-control",
|
|
573
1652
|
"fractal-dropdown-trigger",
|
|
574
1653
|
`fractal-dropdown-trigger--${size}`,
|
|
575
1654
|
`fractal-dropdown-trigger--${triggerVariant}`,
|
|
1655
|
+
`fractal-dropdown-trigger--${triggerOrientation}`,
|
|
1656
|
+
iconOnly && "fractal-dropdown-trigger--icon-only",
|
|
576
1657
|
triggerClassName
|
|
577
1658
|
),
|
|
578
1659
|
children: [
|
|
579
|
-
triggerContent != null ? triggerContent : /* @__PURE__ */
|
|
580
|
-
selected
|
|
581
|
-
selected
|
|
582
|
-
] }),
|
|
583
|
-
!noChevron && /* @__PURE__ */
|
|
1660
|
+
/* @__PURE__ */ jsx19("span", { className: fieldLabel ? "fractal-dropdown-field-value" : void 0, style: fieldLabel ? void 0 : { display: "contents" }, children: triggerContent != null ? triggerContent : /* @__PURE__ */ jsxs17(Fragment2, { children: [
|
|
1661
|
+
selected?.icon != null && /* @__PURE__ */ jsx19("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected?.icon }),
|
|
1662
|
+
selected?.description != null && (typeof selected?.description !== "string" || selected?.description !== "") && /* @__PURE__ */ jsx19("span", { className: "fractal-dropdown-selected-text", children: selected?.description })
|
|
1663
|
+
] }) }),
|
|
1664
|
+
!noChevron && !iconOnly && /* @__PURE__ */ jsx19("span", { className: "fractal-dropdown-chevron", "aria-hidden": true, children: /* @__PURE__ */ jsx19(ChevronIcon, { size: size === "sm" ? 14 : 16 }) })
|
|
584
1665
|
]
|
|
585
1666
|
}
|
|
586
1667
|
),
|
|
587
|
-
|
|
1668
|
+
fieldLabel ? /* @__PURE__ */ jsx19("label", { className: "fractal-floating-field__label", htmlFor: triggerId ?? `${menuId}-trigger`, children: fieldLabel }) : null,
|
|
1669
|
+
open && /* @__PURE__ */ jsxs17(
|
|
588
1670
|
"div",
|
|
589
1671
|
{
|
|
590
1672
|
ref: panelRef,
|
|
591
|
-
|
|
592
|
-
|
|
1673
|
+
id: menuId,
|
|
1674
|
+
onKeyDown: (event) => {
|
|
1675
|
+
if (event.target === searchRef.current && event.key !== "ArrowDown") return;
|
|
1676
|
+
if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
|
|
1677
|
+
event.preventDefault();
|
|
1678
|
+
const options = Array.from(panelRef.current?.querySelectorAll('[role="option"]') ?? []);
|
|
1679
|
+
const current = options.indexOf(document.activeElement);
|
|
1680
|
+
const index = event.key === "Home" ? 0 : event.key === "End" ? options.length - 1 : (current + (event.key === "ArrowDown" ? 1 : -1) + options.length) % options.length;
|
|
1681
|
+
options[index]?.focus({ preventScroll: true });
|
|
1682
|
+
options[index]?.scrollIntoView({ block: "nearest" });
|
|
1683
|
+
},
|
|
1684
|
+
role: searchable ? "dialog" : void 0,
|
|
1685
|
+
"aria-label": accessibleLabel,
|
|
593
1686
|
className: cn("fractal-dropdown-panel", `fractal-dropdown-panel--${size}`, panelClassName),
|
|
594
1687
|
style: panelStyle,
|
|
595
1688
|
children: [
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
1689
|
+
searchable && /* @__PURE__ */ jsxs17("div", { className: "fractal-dropdown-search", children: [
|
|
1690
|
+
/* @__PURE__ */ jsx19(MagnifyingGlass2, { "aria-hidden": "true" }),
|
|
1691
|
+
/* @__PURE__ */ jsx19("input", { ref: searchRef, type: "search", "aria-label": searchPlaceholder, placeholder: searchPlaceholder, value: query, onChange: (event) => setQuery(event.target.value) })
|
|
1692
|
+
] }),
|
|
1693
|
+
/* @__PURE__ */ jsxs17("div", { role: "listbox", id: `${menuId}-options`, "aria-label": accessibleLabel, "aria-multiselectable": multiple || void 0, children: [
|
|
1694
|
+
label != null && /* @__PURE__ */ jsx19("div", { className: "fractal-dropdown-label", children: label }),
|
|
1695
|
+
visibleItems.map((item, index) => {
|
|
1696
|
+
const topDivider = item.border === "top" ? /* @__PURE__ */ jsx19("div", { className: "fractal-dropdown-separator" }) : null;
|
|
1697
|
+
const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ jsx19("div", { className: "fractal-dropdown-separator" }) : null;
|
|
1698
|
+
if (item.kind === "header") {
|
|
1699
|
+
return /* @__PURE__ */ jsxs17(React11.Fragment, { children: [
|
|
1700
|
+
topDivider,
|
|
1701
|
+
/* @__PURE__ */ jsx19("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
|
|
1702
|
+
bottomDivider
|
|
1703
|
+
] }, `header-${index}`);
|
|
1704
|
+
}
|
|
1705
|
+
const isSelected = multiple ? selectedValues.includes(item.value) : item.value === selectedValue;
|
|
1706
|
+
return /* @__PURE__ */ jsxs17(React11.Fragment, { children: [
|
|
602
1707
|
topDivider,
|
|
603
|
-
/* @__PURE__ */
|
|
1708
|
+
/* @__PURE__ */ jsxs17(
|
|
1709
|
+
"button",
|
|
1710
|
+
{
|
|
1711
|
+
type: "button",
|
|
1712
|
+
role: "option",
|
|
1713
|
+
"aria-selected": isSelected,
|
|
1714
|
+
onClick: () => {
|
|
1715
|
+
if (multiple) onSelectionChange?.(isSelected ? selectedValues.filter((value) => value !== item.value) : [...selectedValues, item.value]);
|
|
1716
|
+
else onSelect?.(item.value);
|
|
1717
|
+
if (!multiple && closeOnSelect) {
|
|
1718
|
+
setOpen(false);
|
|
1719
|
+
triggerRef.current?.focus();
|
|
1720
|
+
}
|
|
1721
|
+
},
|
|
1722
|
+
className: "fractal-dropdown-option",
|
|
1723
|
+
children: [
|
|
1724
|
+
multiple && /* @__PURE__ */ jsx19("span", { className: "fractal-dropdown-checkbox", "data-checked": isSelected, "aria-hidden": "true", children: isSelected && /* @__PURE__ */ jsx19(Check2, {}) }),
|
|
1725
|
+
item.icon != null && /* @__PURE__ */ jsx19("span", { className: "fractal-dropdown-icon", children: item.icon }),
|
|
1726
|
+
item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ jsx19("span", { className: "fractal-dropdown-option-text", children: item.description }),
|
|
1727
|
+
item.trailing != null && /* @__PURE__ */ jsx19("span", { className: "fractal-dropdown-trailing", children: item.trailing })
|
|
1728
|
+
]
|
|
1729
|
+
}
|
|
1730
|
+
),
|
|
604
1731
|
bottomDivider
|
|
605
|
-
] },
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
topDivider,
|
|
610
|
-
/* @__PURE__ */ jsxs8(
|
|
611
|
-
"button",
|
|
612
|
-
{
|
|
613
|
-
type: "button",
|
|
614
|
-
role: "option",
|
|
615
|
-
"aria-selected": isSelected,
|
|
616
|
-
onClick: () => {
|
|
617
|
-
onSelect(item.value);
|
|
618
|
-
if (closeOnSelect) setOpen(false);
|
|
619
|
-
},
|
|
620
|
-
className: "fractal-dropdown-option",
|
|
621
|
-
children: [
|
|
622
|
-
item.icon != null && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-icon", children: item.icon }),
|
|
623
|
-
item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-option-text", children: item.description }),
|
|
624
|
-
item.trailing != null && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-trailing", children: item.trailing })
|
|
625
|
-
]
|
|
626
|
-
}
|
|
627
|
-
),
|
|
628
|
-
bottomDivider
|
|
629
|
-
] }, item.value);
|
|
630
|
-
})
|
|
1732
|
+
] }, item.value);
|
|
1733
|
+
})
|
|
1734
|
+
] }),
|
|
1735
|
+
!selectableItems.length && /* @__PURE__ */ jsx19("p", { role: "status", className: "fractal-dropdown-label", children: emptyMessage })
|
|
631
1736
|
]
|
|
632
1737
|
}
|
|
633
1738
|
)
|
|
@@ -635,10 +1740,10 @@ function Dropdown({
|
|
|
635
1740
|
}
|
|
636
1741
|
|
|
637
1742
|
// src/components/Link/Link.tsx
|
|
638
|
-
import * as
|
|
639
|
-
import { jsx as
|
|
640
|
-
var Link =
|
|
641
|
-
({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */
|
|
1743
|
+
import * as React12 from "react";
|
|
1744
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
1745
|
+
var Link = React12.forwardRef(
|
|
1746
|
+
({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */ jsx20(
|
|
642
1747
|
"a",
|
|
643
1748
|
{
|
|
644
1749
|
ref,
|
|
@@ -650,10 +1755,10 @@ var Link = React8.forwardRef(
|
|
|
650
1755
|
Link.displayName = "Link";
|
|
651
1756
|
|
|
652
1757
|
// src/components/Chip/Chip.tsx
|
|
653
|
-
import * as
|
|
654
|
-
import { jsx as
|
|
655
|
-
var Chip =
|
|
656
|
-
({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */
|
|
1758
|
+
import * as React13 from "react";
|
|
1759
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1760
|
+
var Chip = React13.forwardRef(
|
|
1761
|
+
({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */ jsx21(
|
|
657
1762
|
"span",
|
|
658
1763
|
{
|
|
659
1764
|
ref,
|
|
@@ -664,36 +1769,128 @@ var Chip = React9.forwardRef(
|
|
|
664
1769
|
);
|
|
665
1770
|
Chip.displayName = "Chip";
|
|
666
1771
|
|
|
1772
|
+
// src/components/Checkbox/Checkbox.tsx
|
|
1773
|
+
import { Checkbox as CheckboxIcon, CheckboxChecked, Minus as Minus2 } from "@mirror-physics/fractal-icons";
|
|
1774
|
+
import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1775
|
+
function Checkbox({
|
|
1776
|
+
checked,
|
|
1777
|
+
indeterminate = false,
|
|
1778
|
+
onCheckedChange,
|
|
1779
|
+
className,
|
|
1780
|
+
disabled,
|
|
1781
|
+
onClick,
|
|
1782
|
+
onKeyDown,
|
|
1783
|
+
...props
|
|
1784
|
+
}) {
|
|
1785
|
+
return /* @__PURE__ */ jsx22(
|
|
1786
|
+
"button",
|
|
1787
|
+
{
|
|
1788
|
+
...props,
|
|
1789
|
+
className: cn(
|
|
1790
|
+
"fractal-checkbox",
|
|
1791
|
+
(checked || indeterminate) && "fractal-checkbox--checked",
|
|
1792
|
+
indeterminate && "fractal-checkbox--indeterminate",
|
|
1793
|
+
className
|
|
1794
|
+
),
|
|
1795
|
+
type: "button",
|
|
1796
|
+
role: "checkbox",
|
|
1797
|
+
"aria-checked": indeterminate ? "mixed" : checked,
|
|
1798
|
+
disabled,
|
|
1799
|
+
onClick: (event) => {
|
|
1800
|
+
onClick?.(event);
|
|
1801
|
+
if (!event.defaultPrevented) onCheckedChange(indeterminate || !checked);
|
|
1802
|
+
},
|
|
1803
|
+
onKeyDown: (event) => {
|
|
1804
|
+
onKeyDown?.(event);
|
|
1805
|
+
if (event.key === " " || event.key === "Enter") event.stopPropagation();
|
|
1806
|
+
},
|
|
1807
|
+
children: indeterminate ? /* @__PURE__ */ jsxs18("span", { className: "fractal-checkbox__indeterminate-icon", "aria-hidden": "true", children: [
|
|
1808
|
+
/* @__PURE__ */ jsx22(CheckboxIcon, { size: 18 }),
|
|
1809
|
+
/* @__PURE__ */ jsx22(Minus2, { className: "fractal-checkbox__indeterminate-mark", size: 12 })
|
|
1810
|
+
] }) : checked ? /* @__PURE__ */ jsx22(CheckboxChecked, { "aria-hidden": "true", size: 18 }) : /* @__PURE__ */ jsx22(CheckboxIcon, { "aria-hidden": "true", size: 18 })
|
|
1811
|
+
}
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
// src/components/Radio/Radio.tsx
|
|
1816
|
+
import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1817
|
+
function Radio({
|
|
1818
|
+
checked,
|
|
1819
|
+
className,
|
|
1820
|
+
description,
|
|
1821
|
+
disabled,
|
|
1822
|
+
label,
|
|
1823
|
+
onCheckedChange,
|
|
1824
|
+
...props
|
|
1825
|
+
}) {
|
|
1826
|
+
return /* @__PURE__ */ jsxs19("label", { className: cn("fractal-radio", disabled && "fractal-radio--disabled", className), children: [
|
|
1827
|
+
/* @__PURE__ */ jsx23(
|
|
1828
|
+
"input",
|
|
1829
|
+
{
|
|
1830
|
+
...props,
|
|
1831
|
+
checked,
|
|
1832
|
+
className: "fractal-radio__input",
|
|
1833
|
+
disabled,
|
|
1834
|
+
onChange: (event) => onCheckedChange?.(event.currentTarget.checked),
|
|
1835
|
+
type: "radio"
|
|
1836
|
+
}
|
|
1837
|
+
),
|
|
1838
|
+
/* @__PURE__ */ jsx23("span", { "aria-hidden": "true", className: "fractal-radio__control", children: /* @__PURE__ */ jsx23("span", { className: "fractal-radio__dot" }) }),
|
|
1839
|
+
/* @__PURE__ */ jsxs19("span", { className: "fractal-radio__content", children: [
|
|
1840
|
+
/* @__PURE__ */ jsx23("span", { className: "fractal-radio__label", children: label }),
|
|
1841
|
+
description && /* @__PURE__ */ jsx23("span", { className: "fractal-radio__description", children: description })
|
|
1842
|
+
] })
|
|
1843
|
+
] });
|
|
1844
|
+
}
|
|
1845
|
+
|
|
667
1846
|
// src/components/SortableTable/SortableTable.tsx
|
|
668
|
-
import { useMemo, useState as
|
|
1847
|
+
import { useMemo as useMemo2, useState as useState8 } from "react";
|
|
669
1848
|
import { ArrowUp, ArrowDown, ArrowsUpDown } from "@mirror-physics/fractal-icons";
|
|
670
|
-
import { jsx as
|
|
1849
|
+
import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
671
1850
|
function SortableTable({
|
|
672
1851
|
columns,
|
|
673
1852
|
data,
|
|
1853
|
+
"aria-label": ariaLabel,
|
|
674
1854
|
emptyMessage,
|
|
675
1855
|
header,
|
|
676
1856
|
size = "md",
|
|
1857
|
+
embedded = false,
|
|
1858
|
+
layout = "fill",
|
|
1859
|
+
stickyHeader = false,
|
|
1860
|
+
stickyFirstColumn = false,
|
|
1861
|
+
maxHeight,
|
|
1862
|
+
overflowMenu,
|
|
677
1863
|
className,
|
|
1864
|
+
defaultSort,
|
|
678
1865
|
defaultSortKey = void 0,
|
|
679
1866
|
defaultSortDirection = null,
|
|
680
1867
|
sortKey: controlledKey,
|
|
681
1868
|
sortDirection: controlledDirection,
|
|
682
1869
|
onSortChange,
|
|
1870
|
+
pagination,
|
|
683
1871
|
highlightRow,
|
|
684
|
-
|
|
1872
|
+
selection,
|
|
1873
|
+
isRowDisabled,
|
|
1874
|
+
isRowSelectionDisabled,
|
|
1875
|
+
onRowClick,
|
|
1876
|
+
onRowDoubleClick,
|
|
1877
|
+
rowAction
|
|
685
1878
|
}) {
|
|
686
|
-
const [internalKey, setInternalKey] =
|
|
687
|
-
const [internalDirection, setInternalDirection] =
|
|
1879
|
+
const [internalKey, setInternalKey] = useState8(defaultSortKey ?? null);
|
|
1880
|
+
const [internalDirection, setInternalDirection] = useState8(defaultSortDirection);
|
|
688
1881
|
const isControlled = controlledKey !== void 0 && controlledDirection !== void 0;
|
|
689
1882
|
const activeKey = isControlled ? controlledKey : internalKey;
|
|
690
1883
|
const activeDirection = isControlled ? controlledDirection : internalDirection;
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
1884
|
+
const baselineKey = defaultSort?.key ?? null;
|
|
1885
|
+
const baselineDirection = defaultSort?.direction ?? null;
|
|
1886
|
+
const effectiveKey = activeKey && activeDirection ? activeKey : baselineKey;
|
|
1887
|
+
const effectiveDirection = activeKey && activeDirection ? activeDirection : baselineDirection;
|
|
1888
|
+
const sorted = useMemo2(() => {
|
|
1889
|
+
if (!effectiveKey || !effectiveDirection) return data;
|
|
1890
|
+
const col = columns.find((c) => c.key === effectiveKey);
|
|
694
1891
|
if (!col) return data;
|
|
695
1892
|
const accessor = col.sortAccessor ?? ((row) => {
|
|
696
|
-
const value = row[
|
|
1893
|
+
const value = row[effectiveKey];
|
|
697
1894
|
return typeof value === "string" || typeof value === "number" ? value : null;
|
|
698
1895
|
});
|
|
699
1896
|
const copy = [...data];
|
|
@@ -704,26 +1901,41 @@ function SortableTable({
|
|
|
704
1901
|
if (av == null) return 1;
|
|
705
1902
|
if (bv == null) return -1;
|
|
706
1903
|
if (typeof av === "number" && typeof bv === "number") {
|
|
707
|
-
return
|
|
1904
|
+
return effectiveDirection === "asc" ? av - bv : bv - av;
|
|
708
1905
|
}
|
|
709
1906
|
const as = String(av).toLowerCase();
|
|
710
1907
|
const bs = String(bv).toLowerCase();
|
|
711
|
-
if (as < bs) return
|
|
712
|
-
if (as > bs) return
|
|
1908
|
+
if (as < bs) return effectiveDirection === "asc" ? -1 : 1;
|
|
1909
|
+
if (as > bs) return effectiveDirection === "asc" ? 1 : -1;
|
|
713
1910
|
return 0;
|
|
714
1911
|
});
|
|
715
1912
|
return copy;
|
|
716
|
-
}, [data, columns,
|
|
1913
|
+
}, [data, columns, effectiveKey, effectiveDirection]);
|
|
1914
|
+
const visibleRows = useMemo2(() => {
|
|
1915
|
+
if (!pagination) return sorted;
|
|
1916
|
+
const pageSize = Math.max(1, pagination.pageSize);
|
|
1917
|
+
const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize));
|
|
1918
|
+
const page = Math.min(Math.max(1, pagination.page), totalPages);
|
|
1919
|
+
const startIndex = (page - 1) * pageSize;
|
|
1920
|
+
return sorted.slice(startIndex, startIndex + pageSize);
|
|
1921
|
+
}, [pagination, sorted]);
|
|
1922
|
+
const selectedRows = selection ? sorted.filter(selection.isSelected) : [];
|
|
1923
|
+
const selectableRows = selection ? sorted.filter((row) => !isRowDisabled?.(row) && !isRowSelectionDisabled?.(row)) : [];
|
|
1924
|
+
const selectedRowCount = selection ? selectableRows.filter(selection.isSelected).length : 0;
|
|
1925
|
+
const allRowsSelected = selectableRows.length > 0 && selectedRowCount === selectableRows.length;
|
|
1926
|
+
const someRowsSelected = selectedRows.length > 0 && !allRowsSelected;
|
|
717
1927
|
const handleSortClick = (key) => {
|
|
718
1928
|
let nextDirection;
|
|
719
|
-
if (activeKey
|
|
1929
|
+
if (!activeKey || !activeDirection) {
|
|
1930
|
+
nextDirection = baselineKey === key && baselineDirection ? baselineDirection : "asc";
|
|
1931
|
+
} else if (activeKey !== key) {
|
|
720
1932
|
nextDirection = "asc";
|
|
1933
|
+
} else if (baselineKey === key && baselineDirection) {
|
|
1934
|
+
nextDirection = activeDirection === baselineDirection ? baselineDirection === "asc" ? "desc" : "asc" : null;
|
|
721
1935
|
} else if (activeDirection === "asc") {
|
|
722
1936
|
nextDirection = "desc";
|
|
723
|
-
} else if (activeDirection === "desc") {
|
|
724
|
-
nextDirection = null;
|
|
725
1937
|
} else {
|
|
726
|
-
nextDirection =
|
|
1938
|
+
nextDirection = null;
|
|
727
1939
|
}
|
|
728
1940
|
const nextKey = nextDirection === null ? null : key;
|
|
729
1941
|
if (isControlled) {
|
|
@@ -734,101 +1946,200 @@ function SortableTable({
|
|
|
734
1946
|
onSortChange?.(nextKey, nextDirection);
|
|
735
1947
|
}
|
|
736
1948
|
};
|
|
737
|
-
return /* @__PURE__ */
|
|
738
|
-
header && /* @__PURE__ */
|
|
739
|
-
|
|
740
|
-
/* @__PURE__ */
|
|
741
|
-
|
|
742
|
-
if (!col.sortable) {
|
|
743
|
-
return /* @__PURE__ */ jsx12(
|
|
744
|
-
"th",
|
|
745
|
-
{
|
|
746
|
-
className: "fractal-sortable-table-th",
|
|
747
|
-
style: { width: col.width },
|
|
748
|
-
children: col.header
|
|
749
|
-
},
|
|
750
|
-
col.key
|
|
751
|
-
);
|
|
752
|
-
}
|
|
753
|
-
return /* @__PURE__ */ jsx12(
|
|
1949
|
+
return /* @__PURE__ */ jsx24("div", { className: cn("fractal-table-layout", "fractal-sortable-table", `fractal-sortable-table--${size}`, embedded && "fractal-sortable-table--embedded", className), "data-overflow-menu": Boolean(overflowMenu), "data-table-layout": layout, "data-sticky-first": stickyFirstColumn, "data-sticky-header": stickyHeader, "data-table-selection": Boolean(selection), children: /* @__PURE__ */ jsxs20("div", { className: "fractal-sortable-table-surface", children: [
|
|
1950
|
+
header && /* @__PURE__ */ jsx24("div", { className: "fractal-sortable-table-header", children: header }),
|
|
1951
|
+
visibleRows.length === 0 ? /* @__PURE__ */ jsx24("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsx24(TableViewport, { maxHeight, label: ariaLabel || "Data table", children: /* @__PURE__ */ jsxs20("table", { "aria-label": ariaLabel, className: "fractal-sortable-table-table", children: [
|
|
1952
|
+
/* @__PURE__ */ jsx24("thead", { children: /* @__PURE__ */ jsxs20("tr", { className: "fractal-sortable-table-thead-row", children: [
|
|
1953
|
+
selection && /* @__PURE__ */ jsx24(
|
|
754
1954
|
"th",
|
|
755
1955
|
{
|
|
756
|
-
className: "fractal-sortable-table-th fractal-sortable-table-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
1956
|
+
className: "fractal-sortable-table-th fractal-sortable-table-selection-cell",
|
|
1957
|
+
"data-pinned-column": "selection",
|
|
1958
|
+
scope: "col",
|
|
1959
|
+
children: /* @__PURE__ */ jsx24(
|
|
1960
|
+
Checkbox,
|
|
760
1961
|
{
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
)
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1962
|
+
"aria-label": selection.selectAllLabel ?? "Select all rows",
|
|
1963
|
+
checked: allRowsSelected,
|
|
1964
|
+
disabled: selectableRows.length === 0 && selectedRows.length === 0,
|
|
1965
|
+
indeterminate: someRowsSelected,
|
|
1966
|
+
onCheckedChange: (checked) => {
|
|
1967
|
+
const rows = checked ? selectableRows : selectedRows;
|
|
1968
|
+
if (selection.onSelectAllChange) {
|
|
1969
|
+
selection.onSelectAllChange(rows, checked);
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
rows.forEach((row) => {
|
|
1973
|
+
if (selection.isSelected(row) !== checked) {
|
|
1974
|
+
selection.onSelectedChange(row, checked);
|
|
1975
|
+
}
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
772
1978
|
}
|
|
773
1979
|
)
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
)
|
|
777
|
-
|
|
778
|
-
|
|
1980
|
+
}
|
|
1981
|
+
),
|
|
1982
|
+
columns.map((col, columnIndex) => {
|
|
1983
|
+
const isActive = activeKey === col.key && activeDirection !== null;
|
|
1984
|
+
if (!col.sortable) {
|
|
1985
|
+
return /* @__PURE__ */ jsx24(
|
|
1986
|
+
"th",
|
|
1987
|
+
{
|
|
1988
|
+
className: "fractal-sortable-table-th",
|
|
1989
|
+
scope: "col",
|
|
1990
|
+
style: columnStyle(col),
|
|
1991
|
+
"data-pinned-column": columnIndex === 0 ? "data" : void 0,
|
|
1992
|
+
children: /* @__PURE__ */ jsx24(TableCellContent, { label: `${col.header} column`, children: col.header })
|
|
1993
|
+
},
|
|
1994
|
+
col.key
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
return /* @__PURE__ */ jsx24(
|
|
1998
|
+
"th",
|
|
1999
|
+
{
|
|
2000
|
+
className: "fractal-sortable-table-th fractal-sortable-table-th--sortable",
|
|
2001
|
+
scope: "col",
|
|
2002
|
+
style: columnStyle(col),
|
|
2003
|
+
"data-pinned-column": columnIndex === 0 ? "data" : void 0,
|
|
2004
|
+
children: /* @__PURE__ */ jsx24(TableCellContent, { label: `${col.header} column`, children: /* @__PURE__ */ jsxs20(
|
|
2005
|
+
"button",
|
|
2006
|
+
{
|
|
2007
|
+
type: "button",
|
|
2008
|
+
className: cn(
|
|
2009
|
+
"fractal-sortable-table-sort-button",
|
|
2010
|
+
isActive && "fractal-sortable-table-sort-button--active"
|
|
2011
|
+
),
|
|
2012
|
+
onClick: () => handleSortClick(col.key),
|
|
2013
|
+
"aria-sort": isActive ? activeDirection === "asc" ? "ascending" : "descending" : "none",
|
|
2014
|
+
children: [
|
|
2015
|
+
/* @__PURE__ */ jsx24("span", { children: col.header }),
|
|
2016
|
+
/* @__PURE__ */ jsx24("span", { className: "fractal-sortable-table-sort-icon", "aria-hidden": "true", children: isActive && activeDirection === "asc" ? /* @__PURE__ */ jsx24(ArrowUp, { size: 14 }) : isActive && activeDirection === "desc" ? /* @__PURE__ */ jsx24(ArrowDown, { size: 14 }) : /* @__PURE__ */ jsx24(ArrowsUpDown, { size: 14 }) })
|
|
2017
|
+
]
|
|
2018
|
+
}
|
|
2019
|
+
) })
|
|
2020
|
+
},
|
|
2021
|
+
col.key
|
|
2022
|
+
);
|
|
2023
|
+
}),
|
|
2024
|
+
overflowMenu && /* @__PURE__ */ jsx24("th", { scope: "col", "aria-label": "Row actions", className: "fractal-sortable-table-th fractal-table-overflow-cell", "data-pinned-column": "overflow" })
|
|
2025
|
+
] }) }),
|
|
2026
|
+
/* @__PURE__ */ jsx24("tbody", { children: visibleRows.map((row, rowIndex) => {
|
|
779
2027
|
const highlighted = highlightRow?.(row, rowIndex) ?? false;
|
|
780
|
-
|
|
2028
|
+
const disabled = isRowDisabled?.(row) ?? false;
|
|
2029
|
+
const selectionDisabled = disabled || (isRowSelectionDisabled?.(row) ?? false);
|
|
2030
|
+
const interactive = !disabled && Boolean(onRowClick || onRowDoubleClick);
|
|
2031
|
+
const action = !disabled && rowAction ? {
|
|
2032
|
+
label: rowAction.label(row, rowIndex),
|
|
2033
|
+
icon: rowAction.icon(row, rowIndex)
|
|
2034
|
+
} : null;
|
|
2035
|
+
return /* @__PURE__ */ jsxs20(
|
|
781
2036
|
"tr",
|
|
782
2037
|
{
|
|
783
2038
|
className: cn(
|
|
784
2039
|
"fractal-sortable-table-row",
|
|
785
2040
|
highlighted && "fractal-sortable-table-row--highlighted",
|
|
786
|
-
|
|
2041
|
+
interactive && "fractal-sortable-table-row--interactive",
|
|
2042
|
+
disabled && "fractal-sortable-table-row--disabled"
|
|
787
2043
|
),
|
|
788
|
-
|
|
789
|
-
|
|
2044
|
+
"data-disabled": disabled || void 0,
|
|
2045
|
+
onClick: !disabled && onRowClick ? () => onRowClick(row, rowIndex) : void 0,
|
|
2046
|
+
onDoubleClick: !disabled && onRowDoubleClick ? () => onRowDoubleClick(row, rowIndex) : void 0,
|
|
2047
|
+
onKeyDown: interactive ? (event) => {
|
|
790
2048
|
if (event.key === "Enter" || event.key === " ") {
|
|
791
2049
|
event.preventDefault();
|
|
792
|
-
onRowClick
|
|
2050
|
+
const activateRow = onRowClick ?? onRowDoubleClick;
|
|
2051
|
+
activateRow?.(row, rowIndex);
|
|
793
2052
|
}
|
|
794
2053
|
} : void 0,
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
2054
|
+
"aria-label": action?.label,
|
|
2055
|
+
"aria-disabled": disabled || void 0,
|
|
2056
|
+
role: interactive ? "button" : void 0,
|
|
2057
|
+
tabIndex: interactive ? 0 : void 0,
|
|
2058
|
+
children: [
|
|
2059
|
+
selection && /* @__PURE__ */ jsx24("td", { className: "fractal-sortable-table-td fractal-sortable-table-selection-cell", "data-pinned-column": "selection", children: /* @__PURE__ */ jsx24(
|
|
2060
|
+
Checkbox,
|
|
2061
|
+
{
|
|
2062
|
+
"aria-label": selection.getRowLabel(row),
|
|
2063
|
+
checked: !selectionDisabled && selection.isSelected(row),
|
|
2064
|
+
disabled: selectionDisabled,
|
|
2065
|
+
onClick: (event) => event.stopPropagation(),
|
|
2066
|
+
onCheckedChange: (checked) => {
|
|
2067
|
+
if (!selectionDisabled) selection.onSelectedChange(row, checked);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
) }),
|
|
2071
|
+
columns.map((col, columnIndex) => /* @__PURE__ */ jsxs20(
|
|
2072
|
+
"td",
|
|
2073
|
+
{
|
|
2074
|
+
className: cn(
|
|
2075
|
+
"fractal-sortable-table-td",
|
|
2076
|
+
action && columnIndex === columns.length - 1 && "fractal-sortable-table-td--row-action"
|
|
2077
|
+
),
|
|
2078
|
+
style: { ...columnStyle(col), textAlign: col.align ?? "left" },
|
|
2079
|
+
"data-column-align": col.align,
|
|
2080
|
+
"data-pinned-column": columnIndex === 0 ? "data" : void 0,
|
|
2081
|
+
children: [
|
|
2082
|
+
/* @__PURE__ */ jsx24(TableCellContent, { label: `${col.header || "Value"}, row ${rowIndex + 1}`, children: col.render(row, rowIndex) }),
|
|
2083
|
+
action && columnIndex === columns.length - 1 && /* @__PURE__ */ jsx24("span", { className: "fractal-sortable-table-row-action", "aria-hidden": "true", children: /* @__PURE__ */ jsx24("span", { className: "fractal-sortable-table-row-action__icon", children: action.icon }) })
|
|
2084
|
+
]
|
|
2085
|
+
},
|
|
2086
|
+
col.key
|
|
2087
|
+
)),
|
|
2088
|
+
overflowMenu && /* @__PURE__ */ jsx24(
|
|
2089
|
+
"td",
|
|
2090
|
+
{
|
|
2091
|
+
className: "fractal-sortable-table-td fractal-table-overflow-cell",
|
|
2092
|
+
"data-pinned-column": "overflow",
|
|
2093
|
+
onClick: (event) => event.stopPropagation(),
|
|
2094
|
+
onDoubleClick: (event) => event.stopPropagation(),
|
|
2095
|
+
onKeyDown: (event) => event.stopPropagation(),
|
|
2096
|
+
children: /* @__PURE__ */ jsx24(TableOverflowMenu, { items: overflowMenu.items(row, rowIndex), label: overflowMenu.getLabel?.(row, rowIndex) ?? `Actions for row ${rowIndex + 1}`, disabled: disabled || overflowMenu.isDisabled?.(row, rowIndex), onSelect: (value) => overflowMenu.onSelect(value, row, rowIndex) })
|
|
2097
|
+
}
|
|
2098
|
+
)
|
|
2099
|
+
]
|
|
806
2100
|
},
|
|
807
2101
|
rowIndex
|
|
808
2102
|
);
|
|
809
2103
|
}) })
|
|
810
|
-
] })
|
|
2104
|
+
] }) })
|
|
811
2105
|
] }) });
|
|
812
2106
|
}
|
|
813
2107
|
|
|
814
2108
|
// src/components/Tabs/Tabs.tsx
|
|
815
|
-
import { useId, useRef as
|
|
816
|
-
import { jsx as
|
|
2109
|
+
import { useId as useId5, useLayoutEffect as useLayoutEffect4, useRef as useRef8, useState as useState9 } from "react";
|
|
2110
|
+
import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
817
2111
|
function Tabs({
|
|
818
2112
|
items,
|
|
819
2113
|
defaultValue,
|
|
820
2114
|
value: controlledValue,
|
|
821
2115
|
onValueChange,
|
|
822
2116
|
className,
|
|
823
|
-
ariaLabel
|
|
2117
|
+
ariaLabel,
|
|
2118
|
+
divider = false,
|
|
2119
|
+
variant = "line"
|
|
824
2120
|
}) {
|
|
825
|
-
const reactId =
|
|
826
|
-
const [internalValue, setInternalValue] =
|
|
2121
|
+
const reactId = useId5();
|
|
2122
|
+
const [internalValue, setInternalValue] = useState9(
|
|
827
2123
|
defaultValue ?? items.find((i) => !i.disabled)?.id ?? items[0]?.id ?? ""
|
|
828
2124
|
);
|
|
829
2125
|
const isControlled = controlledValue !== void 0;
|
|
830
2126
|
const activeId = isControlled ? controlledValue : internalValue;
|
|
831
|
-
const
|
|
2127
|
+
const tabListRef = useRef8(null);
|
|
2128
|
+
const tabRefs = useRef8({});
|
|
2129
|
+
const [indicator, setIndicator] = useState9(null);
|
|
2130
|
+
useLayoutEffect4(() => {
|
|
2131
|
+
const updateIndicator = () => {
|
|
2132
|
+
const activeTab = tabRefs.current[activeId];
|
|
2133
|
+
if (!activeTab) return;
|
|
2134
|
+
setIndicator({ offset: activeTab.offsetLeft, width: activeTab.offsetWidth });
|
|
2135
|
+
};
|
|
2136
|
+
updateIndicator();
|
|
2137
|
+
const list = tabListRef.current;
|
|
2138
|
+
if (!list || typeof ResizeObserver === "undefined") return;
|
|
2139
|
+
const observer = new ResizeObserver(updateIndicator);
|
|
2140
|
+
observer.observe(list);
|
|
2141
|
+
return () => observer.disconnect();
|
|
2142
|
+
}, [activeId, items.length]);
|
|
832
2143
|
const selectTab = (id) => {
|
|
833
2144
|
if (isControlled) {
|
|
834
2145
|
onValueChange?.(id);
|
|
@@ -838,6 +2149,10 @@ function Tabs({
|
|
|
838
2149
|
}
|
|
839
2150
|
};
|
|
840
2151
|
const focusable = items.filter((i) => !i.disabled);
|
|
2152
|
+
const indicatorStyle = indicator ? {
|
|
2153
|
+
"--fractal-tabs-indicator-offset": `${indicator.offset}px`,
|
|
2154
|
+
"--fractal-tabs-indicator-width": `${indicator.width}px`
|
|
2155
|
+
} : void 0;
|
|
841
2156
|
const onKeyDown = (event) => {
|
|
842
2157
|
const currentIndex = focusable.findIndex((i) => i.id === activeId);
|
|
843
2158
|
if (currentIndex === -1) return;
|
|
@@ -863,41 +2178,52 @@ function Tabs({
|
|
|
863
2178
|
selectTab(nextId2);
|
|
864
2179
|
requestAnimationFrame(() => tabRefs.current[nextId2]?.focus());
|
|
865
2180
|
};
|
|
866
|
-
return /* @__PURE__ */
|
|
867
|
-
/* @__PURE__ */
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
"
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
id
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
2181
|
+
return /* @__PURE__ */ jsxs21("div", { className: cn("fractal-tabs", `fractal-tabs--${variant}`, divider && "fractal-tabs--divider", className), children: [
|
|
2182
|
+
/* @__PURE__ */ jsx25(
|
|
2183
|
+
"div",
|
|
2184
|
+
{
|
|
2185
|
+
ref: tabListRef,
|
|
2186
|
+
role: "tablist",
|
|
2187
|
+
"aria-label": ariaLabel,
|
|
2188
|
+
className: "fractal-tabs-list",
|
|
2189
|
+
"data-indicator-ready": indicator ? "true" : void 0,
|
|
2190
|
+
style: indicatorStyle,
|
|
2191
|
+
children: items.map((item) => {
|
|
2192
|
+
const isActive = item.id === activeId;
|
|
2193
|
+
const tabId = `${reactId}-tab-${item.id}`;
|
|
2194
|
+
const panelId = `${reactId}-panel-${item.id}`;
|
|
2195
|
+
return /* @__PURE__ */ jsx25(
|
|
2196
|
+
"button",
|
|
2197
|
+
{
|
|
2198
|
+
ref: (el) => {
|
|
2199
|
+
tabRefs.current[item.id] = el;
|
|
2200
|
+
},
|
|
2201
|
+
id: tabId,
|
|
2202
|
+
role: "tab",
|
|
2203
|
+
type: "button",
|
|
2204
|
+
"aria-selected": isActive,
|
|
2205
|
+
"aria-controls": panelId,
|
|
2206
|
+
tabIndex: isActive ? 0 : -1,
|
|
2207
|
+
disabled: item.disabled,
|
|
2208
|
+
className: cn(
|
|
2209
|
+
"fractal-tabs-trigger",
|
|
2210
|
+
isActive && "fractal-tabs-trigger--active",
|
|
2211
|
+
item.disabled && "fractal-tabs-trigger--disabled"
|
|
2212
|
+
),
|
|
2213
|
+
onClick: () => !item.disabled && selectTab(item.id),
|
|
2214
|
+
onKeyDown,
|
|
2215
|
+
children: item.label
|
|
2216
|
+
},
|
|
2217
|
+
item.id
|
|
2218
|
+
);
|
|
2219
|
+
})
|
|
2220
|
+
}
|
|
2221
|
+
),
|
|
896
2222
|
items.map((item) => {
|
|
897
2223
|
const tabId = `${reactId}-tab-${item.id}`;
|
|
898
2224
|
const panelId = `${reactId}-panel-${item.id}`;
|
|
899
2225
|
const isActive = item.id === activeId;
|
|
900
|
-
return /* @__PURE__ */
|
|
2226
|
+
return /* @__PURE__ */ jsx25(
|
|
901
2227
|
"div",
|
|
902
2228
|
{
|
|
903
2229
|
id: panelId,
|
|
@@ -913,10 +2239,112 @@ function Tabs({
|
|
|
913
2239
|
] });
|
|
914
2240
|
}
|
|
915
2241
|
|
|
2242
|
+
// src/components/ContentSwitcher/ContentSwitcher.tsx
|
|
2243
|
+
import * as React14 from "react";
|
|
2244
|
+
import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2245
|
+
function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = false, className }) {
|
|
2246
|
+
const switcherRef = React14.useRef(null);
|
|
2247
|
+
const buttonRefs = React14.useRef([]);
|
|
2248
|
+
const indicatorMotionReadyRef = React14.useRef(false);
|
|
2249
|
+
const activeIndex = items.findIndex((item) => item.value === value);
|
|
2250
|
+
React14.useLayoutEffect(() => {
|
|
2251
|
+
const switcher = switcherRef.current;
|
|
2252
|
+
const activeButton = buttonRefs.current[activeIndex];
|
|
2253
|
+
let motionFrame = 0;
|
|
2254
|
+
if (!switcher || !activeButton) {
|
|
2255
|
+
switcher?.removeAttribute("data-indicator-ready");
|
|
2256
|
+
switcher?.removeAttribute("data-indicator-animate");
|
|
2257
|
+
indicatorMotionReadyRef.current = false;
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
const updateIndicator = () => {
|
|
2261
|
+
const switcherRect = switcher.getBoundingClientRect();
|
|
2262
|
+
const activeButtonRect = activeButton.getBoundingClientRect();
|
|
2263
|
+
switcher.style.setProperty("--fractal-content-switcher-indicator-x", `${activeButtonRect.left - switcherRect.left - switcher.clientLeft}px`);
|
|
2264
|
+
switcher.style.setProperty("--fractal-content-switcher-indicator-y", `${activeButtonRect.top - switcherRect.top - switcher.clientTop}px`);
|
|
2265
|
+
switcher.style.setProperty("--fractal-content-switcher-indicator-width", `${activeButtonRect.width}px`);
|
|
2266
|
+
switcher.style.setProperty("--fractal-content-switcher-indicator-height", `${activeButtonRect.height}px`);
|
|
2267
|
+
switcher.setAttribute("data-indicator-ready", "true");
|
|
2268
|
+
if (!indicatorMotionReadyRef.current && motionFrame === 0) {
|
|
2269
|
+
motionFrame = window.requestAnimationFrame(() => {
|
|
2270
|
+
indicatorMotionReadyRef.current = true;
|
|
2271
|
+
switcher.setAttribute("data-indicator-animate", "true");
|
|
2272
|
+
motionFrame = 0;
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2275
|
+
};
|
|
2276
|
+
updateIndicator();
|
|
2277
|
+
if (typeof ResizeObserver === "undefined") {
|
|
2278
|
+
return () => {
|
|
2279
|
+
if (motionFrame !== 0) window.cancelAnimationFrame(motionFrame);
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
const resizeObserver = new ResizeObserver(updateIndicator);
|
|
2283
|
+
resizeObserver.observe(switcher);
|
|
2284
|
+
buttonRefs.current.forEach((button) => {
|
|
2285
|
+
if (button) resizeObserver.observe(button);
|
|
2286
|
+
});
|
|
2287
|
+
return () => {
|
|
2288
|
+
resizeObserver.disconnect();
|
|
2289
|
+
if (motionFrame !== 0) window.cancelAnimationFrame(motionFrame);
|
|
2290
|
+
};
|
|
2291
|
+
}, [activeIndex, items.length]);
|
|
2292
|
+
const moveFocus = (index, direction) => {
|
|
2293
|
+
let nextIndex = index;
|
|
2294
|
+
do {
|
|
2295
|
+
nextIndex = (nextIndex + direction + items.length) % items.length;
|
|
2296
|
+
} while (items[nextIndex]?.disabled && nextIndex !== index);
|
|
2297
|
+
if (!items[nextIndex]?.disabled) {
|
|
2298
|
+
buttonRefs.current[nextIndex]?.focus();
|
|
2299
|
+
onValueChange(items[nextIndex].value);
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
return /* @__PURE__ */ jsxs22(
|
|
2303
|
+
"div",
|
|
2304
|
+
{
|
|
2305
|
+
className: cn("fractal-content-switcher", fullWidth && "fractal-content-switcher--full-width", className),
|
|
2306
|
+
role: "group",
|
|
2307
|
+
"aria-label": ariaLabel,
|
|
2308
|
+
ref: switcherRef,
|
|
2309
|
+
children: [
|
|
2310
|
+
/* @__PURE__ */ jsx26("span", { className: "fractal-content-switcher__indicator", "aria-hidden": true }),
|
|
2311
|
+
items.map((item, index) => {
|
|
2312
|
+
const active = item.value === value;
|
|
2313
|
+
return /* @__PURE__ */ jsx26(
|
|
2314
|
+
"button",
|
|
2315
|
+
{
|
|
2316
|
+
className: cn("fractal-content-switcher__item", active && "fractal-content-switcher__item--active"),
|
|
2317
|
+
type: "button",
|
|
2318
|
+
"aria-pressed": active,
|
|
2319
|
+
disabled: item.disabled,
|
|
2320
|
+
onClick: () => onValueChange(item.value),
|
|
2321
|
+
onKeyDown: (event) => {
|
|
2322
|
+
if (event.key === "ArrowRight") {
|
|
2323
|
+
event.preventDefault();
|
|
2324
|
+
moveFocus(index, 1);
|
|
2325
|
+
}
|
|
2326
|
+
if (event.key === "ArrowLeft") {
|
|
2327
|
+
event.preventDefault();
|
|
2328
|
+
moveFocus(index, -1);
|
|
2329
|
+
}
|
|
2330
|
+
},
|
|
2331
|
+
ref: (element) => {
|
|
2332
|
+
buttonRefs.current[index] = element;
|
|
2333
|
+
},
|
|
2334
|
+
children: item.label
|
|
2335
|
+
},
|
|
2336
|
+
item.value
|
|
2337
|
+
);
|
|
2338
|
+
})
|
|
2339
|
+
]
|
|
2340
|
+
}
|
|
2341
|
+
);
|
|
2342
|
+
}
|
|
2343
|
+
|
|
916
2344
|
// src/components/Disclosure/Disclosure.tsx
|
|
917
|
-
import { useId as
|
|
918
|
-
import { ChevronRight, ChevronDown as ChevronDown2 } from "@mirror-physics/fractal-icons";
|
|
919
|
-
import { jsx as
|
|
2345
|
+
import { useId as useId6, useState as useState10 } from "react";
|
|
2346
|
+
import { ChevronRight as ChevronRight3, ChevronDown as ChevronDown2 } from "@mirror-physics/fractal-icons";
|
|
2347
|
+
import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
920
2348
|
function Disclosure({
|
|
921
2349
|
title,
|
|
922
2350
|
meta,
|
|
@@ -924,16 +2352,18 @@ function Disclosure({
|
|
|
924
2352
|
defaultOpen = false,
|
|
925
2353
|
open: controlledOpen,
|
|
926
2354
|
onOpenChange,
|
|
2355
|
+
disabled = false,
|
|
927
2356
|
className,
|
|
928
2357
|
variant = "card"
|
|
929
2358
|
}) {
|
|
930
|
-
const reactId =
|
|
2359
|
+
const reactId = useId6();
|
|
931
2360
|
const headerId = `${reactId}-header`;
|
|
932
2361
|
const panelId = `${reactId}-panel`;
|
|
933
|
-
const [internalOpen, setInternalOpen] =
|
|
2362
|
+
const [internalOpen, setInternalOpen] = useState10(defaultOpen);
|
|
934
2363
|
const isControlled = controlledOpen !== void 0;
|
|
935
2364
|
const isOpen = isControlled ? controlledOpen : internalOpen;
|
|
936
2365
|
const toggle = () => {
|
|
2366
|
+
if (disabled) return;
|
|
937
2367
|
const next = !isOpen;
|
|
938
2368
|
if (isControlled) {
|
|
939
2369
|
onOpenChange?.(next);
|
|
@@ -942,17 +2372,18 @@ function Disclosure({
|
|
|
942
2372
|
onOpenChange?.(next);
|
|
943
2373
|
}
|
|
944
2374
|
};
|
|
945
|
-
return /* @__PURE__ */
|
|
2375
|
+
return /* @__PURE__ */ jsxs23(
|
|
946
2376
|
"div",
|
|
947
2377
|
{
|
|
948
2378
|
className: cn(
|
|
949
2379
|
"fractal-disclosure",
|
|
950
2380
|
variant === "card" && "fractal-disclosure--card",
|
|
951
2381
|
isOpen && "fractal-disclosure--open",
|
|
2382
|
+
disabled && "fractal-disclosure--disabled",
|
|
952
2383
|
className
|
|
953
2384
|
),
|
|
954
2385
|
children: [
|
|
955
|
-
/* @__PURE__ */
|
|
2386
|
+
/* @__PURE__ */ jsxs23(
|
|
956
2387
|
"button",
|
|
957
2388
|
{
|
|
958
2389
|
type: "button",
|
|
@@ -960,15 +2391,16 @@ function Disclosure({
|
|
|
960
2391
|
className: "fractal-disclosure-trigger",
|
|
961
2392
|
"aria-expanded": isOpen,
|
|
962
2393
|
"aria-controls": panelId,
|
|
2394
|
+
disabled,
|
|
963
2395
|
onClick: toggle,
|
|
964
2396
|
children: [
|
|
965
|
-
/* @__PURE__ */
|
|
966
|
-
/* @__PURE__ */
|
|
967
|
-
meta && /* @__PURE__ */
|
|
2397
|
+
/* @__PURE__ */ jsx27("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ jsx27(ChevronDown2, { size: 16 }) : /* @__PURE__ */ jsx27(ChevronRight3, { size: 16 }) }),
|
|
2398
|
+
/* @__PURE__ */ jsx27("span", { className: "fractal-disclosure-title", children: title }),
|
|
2399
|
+
meta && /* @__PURE__ */ jsx27("span", { className: "fractal-disclosure-meta", children: meta })
|
|
968
2400
|
]
|
|
969
2401
|
}
|
|
970
2402
|
),
|
|
971
|
-
/* @__PURE__ */
|
|
2403
|
+
/* @__PURE__ */ jsx27(
|
|
972
2404
|
"div",
|
|
973
2405
|
{
|
|
974
2406
|
id: panelId,
|
|
@@ -976,7 +2408,7 @@ function Disclosure({
|
|
|
976
2408
|
"aria-labelledby": headerId,
|
|
977
2409
|
className: "fractal-disclosure-panel",
|
|
978
2410
|
hidden: !isOpen,
|
|
979
|
-
children: /* @__PURE__ */
|
|
2411
|
+
children: /* @__PURE__ */ jsx27("div", { className: "fractal-disclosure-panel-inner", children })
|
|
980
2412
|
}
|
|
981
2413
|
)
|
|
982
2414
|
]
|
|
@@ -984,39 +2416,260 @@ function Disclosure({
|
|
|
984
2416
|
);
|
|
985
2417
|
}
|
|
986
2418
|
|
|
2419
|
+
// src/components/LatticeLoader/LatticeLoader.tsx
|
|
2420
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
2421
|
+
var LATTICE_COUNT = 160;
|
|
2422
|
+
var WAVE_PERIOD = 80;
|
|
2423
|
+
function LatticeLoader({ className, label = "Loading", ...props }) {
|
|
2424
|
+
return /* @__PURE__ */ jsx28(
|
|
2425
|
+
"span",
|
|
2426
|
+
{
|
|
2427
|
+
"aria-label": label,
|
|
2428
|
+
className: cn("fractal-lattice-loader", className),
|
|
2429
|
+
role: "progressbar",
|
|
2430
|
+
...props,
|
|
2431
|
+
children: Array.from({ length: LATTICE_COUNT }, (_, index) => /* @__PURE__ */ jsx28(
|
|
2432
|
+
"span",
|
|
2433
|
+
{
|
|
2434
|
+
"aria-hidden": "true",
|
|
2435
|
+
className: "fractal-lattice-loader__cell",
|
|
2436
|
+
style: { "--fractal-lattice-index": index % WAVE_PERIOD }
|
|
2437
|
+
},
|
|
2438
|
+
index
|
|
2439
|
+
))
|
|
2440
|
+
}
|
|
2441
|
+
);
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
// src/components/Ghost/Ghost.tsx
|
|
2445
|
+
import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2446
|
+
var toCssLength = (value) => typeof value === "number" ? `${value}px` : value;
|
|
2447
|
+
function Ghost({ className, width, height, radius, style, ...props }) {
|
|
2448
|
+
return /* @__PURE__ */ jsx29(
|
|
2449
|
+
"div",
|
|
2450
|
+
{
|
|
2451
|
+
"aria-hidden": "true",
|
|
2452
|
+
className: cn("fractal-ghost", className),
|
|
2453
|
+
style: {
|
|
2454
|
+
width: toCssLength(width),
|
|
2455
|
+
height: toCssLength(height),
|
|
2456
|
+
borderRadius: toCssLength(radius),
|
|
2457
|
+
...style
|
|
2458
|
+
},
|
|
2459
|
+
...props
|
|
2460
|
+
}
|
|
2461
|
+
);
|
|
2462
|
+
}
|
|
2463
|
+
function GhostLine({ className, size = "md", width = "100%", ...props }) {
|
|
2464
|
+
return /* @__PURE__ */ jsx29(Ghost, { className: cn("fractal-ghost-line", `fractal-ghost-line--${size}`, className), width, ...props });
|
|
2465
|
+
}
|
|
2466
|
+
function ButtonGhost({ className, size = "lg", controlSize, iconOnly = false, width, ...props }) {
|
|
2467
|
+
return /* @__PURE__ */ jsx29(Ghost, { "data-control-size": controlSize ?? size, className: cn("fractal-ghost-button", `fractal-ghost-button--${size}`, iconOnly && "fractal-ghost-button--icon-only", className), width, ...props });
|
|
2468
|
+
}
|
|
2469
|
+
function CardGhost({ className, lines = 3, showHeader = true, ...props }) {
|
|
2470
|
+
return /* @__PURE__ */ jsxs24("div", { "aria-hidden": "true", className: cn("fractal-ghost-card", className), ...props, children: [
|
|
2471
|
+
showHeader && /* @__PURE__ */ jsx29(GhostLine, { width: "38%" }),
|
|
2472
|
+
/* @__PURE__ */ jsx29("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx29(GhostLine, { width: index === lines - 1 ? "62%" : "100%" }, index)) })
|
|
2473
|
+
] });
|
|
2474
|
+
}
|
|
2475
|
+
function DataTableGhost({
|
|
2476
|
+
className,
|
|
2477
|
+
showHeader = true,
|
|
2478
|
+
headers,
|
|
2479
|
+
columns = 4,
|
|
2480
|
+
rows = 5,
|
|
2481
|
+
columnWidths,
|
|
2482
|
+
size = "md",
|
|
2483
|
+
...props
|
|
2484
|
+
}) {
|
|
2485
|
+
const cells = Array.from({ length: columns }, (_, index) => index);
|
|
2486
|
+
return /* @__PURE__ */ jsx29("div", { "aria-busy": "true", "aria-label": "Loading table", className: cn("fractal-ghost-table", `fractal-ghost-table--${size}`, className), role: "status", ...props, children: /* @__PURE__ */ jsxs24("table", { className: "fractal-ghost-table__table", children: [
|
|
2487
|
+
(showHeader || headers) && /* @__PURE__ */ jsx29("thead", { children: /* @__PURE__ */ jsx29("tr", { className: "fractal-ghost-table__head-row", children: cells.map((index) => /* @__PURE__ */ jsx29("th", { style: { width: columnWidths?.[index] }, children: headers?.[index] ?? /* @__PURE__ */ jsx29(GhostLine, { size: "sm", width: index === columns - 1 ? "62%" : "76%" }) }, index)) }) }),
|
|
2488
|
+
/* @__PURE__ */ jsx29("tbody", { children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ jsx29("tr", { className: "fractal-ghost-table__row", children: cells.map((columnIndex) => /* @__PURE__ */ jsx29("td", { style: { width: columnWidths?.[columnIndex] }, children: /* @__PURE__ */ jsx29(GhostLine, { width: `${[72, 54, 82, 63, 46][(rowIndex + columnIndex) % 5]}%` }) }, columnIndex)) }, rowIndex)) })
|
|
2489
|
+
] }) });
|
|
2490
|
+
}
|
|
2491
|
+
function InputGhost({ className, labelWidth = "26%", ...props }) {
|
|
2492
|
+
return /* @__PURE__ */ jsx29(FormFieldGhost, { className, labelWidth, ...props });
|
|
2493
|
+
}
|
|
2494
|
+
function TextareaGhost({ className, labelWidth = "26%", ...props }) {
|
|
2495
|
+
return /* @__PURE__ */ jsx29(FormFieldGhost, { className, labelWidth, multiline: true, ...props });
|
|
2496
|
+
}
|
|
2497
|
+
function LabelGhost({ className, width = "26%", ...props }) {
|
|
2498
|
+
return /* @__PURE__ */ jsx29(GhostLine, { className: cn("fractal-ghost-label", className), size: "sm", width, ...props });
|
|
2499
|
+
}
|
|
2500
|
+
function FormFieldGhost({ className, labelWidth = "26%", multiline = false, controlSize = "lg", ...props }) {
|
|
2501
|
+
return /* @__PURE__ */ jsxs24("div", { "aria-hidden": "true", "data-control-size": controlSize, className: cn("fractal-ghost-field", className), ...props, children: [
|
|
2502
|
+
/* @__PURE__ */ jsx29(LabelGhost, { width: labelWidth }),
|
|
2503
|
+
/* @__PURE__ */ jsx29(Ghost, { className: cn("fractal-ghost-field__control", multiline && "fractal-ghost-field__control--multiline") })
|
|
2504
|
+
] });
|
|
2505
|
+
}
|
|
2506
|
+
function AlertGhost({ className, ...props }) {
|
|
2507
|
+
return /* @__PURE__ */ jsxs24("div", { "aria-hidden": "true", className: cn("fractal-ghost-alert", className), ...props, children: [
|
|
2508
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "30%" }),
|
|
2509
|
+
/* @__PURE__ */ jsx29(GhostLine, { size: "sm", width: "78%" })
|
|
2510
|
+
] });
|
|
2511
|
+
}
|
|
2512
|
+
function CheckboxGhost({ className, ...props }) {
|
|
2513
|
+
return /* @__PURE__ */ jsxs24("div", { "aria-hidden": "true", className: cn("fractal-ghost-choice", className), ...props, children: [
|
|
2514
|
+
/* @__PURE__ */ jsx29(Ghost, { className: "fractal-ghost-choice__control" }),
|
|
2515
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "42%" })
|
|
2516
|
+
] });
|
|
2517
|
+
}
|
|
2518
|
+
var ToggleGhost = CheckboxGhost;
|
|
2519
|
+
function ChipGhost({ className, width = 74, ...props }) {
|
|
2520
|
+
return /* @__PURE__ */ jsx29(Ghost, { className: cn("fractal-ghost-chip", className), height: 24, width, ...props });
|
|
2521
|
+
}
|
|
2522
|
+
function LinkGhost({ className, width = 96, ...props }) {
|
|
2523
|
+
return /* @__PURE__ */ jsx29(GhostLine, { className: cn("fractal-ghost-link", className), size: "sm", width, ...props });
|
|
2524
|
+
}
|
|
2525
|
+
var TextButtonGhost = LinkGhost;
|
|
2526
|
+
function DropdownGhost({ className, controlSize = "lg", ...props }) {
|
|
2527
|
+
return /* @__PURE__ */ jsxs24("div", { "aria-hidden": "true", "data-control-size": controlSize, className: cn("fractal-ghost-dropdown", className), ...props, children: [
|
|
2528
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "38%" }),
|
|
2529
|
+
/* @__PURE__ */ jsx29(Ghost, { className: "fractal-ghost-dropdown__chevron" })
|
|
2530
|
+
] });
|
|
2531
|
+
}
|
|
2532
|
+
function ContentSwitcherGhost({ className, options = 3, ...props }) {
|
|
2533
|
+
return /* @__PURE__ */ jsx29("div", { "aria-hidden": "true", className: cn("fractal-ghost-switcher", className), ...props, children: Array.from({ length: options }, (_, index) => /* @__PURE__ */ jsx29(Ghost, {}, index)) });
|
|
2534
|
+
}
|
|
2535
|
+
var TabsGhost = ContentSwitcherGhost;
|
|
2536
|
+
function DisclosureGhost({ className, ...props }) {
|
|
2537
|
+
return /* @__PURE__ */ jsxs24("div", { "aria-hidden": "true", className: cn("fractal-ghost-disclosure", className), ...props, children: [
|
|
2538
|
+
/* @__PURE__ */ jsx29(Ghost, { className: "fractal-ghost-disclosure__icon" }),
|
|
2539
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "42%" })
|
|
2540
|
+
] });
|
|
2541
|
+
}
|
|
2542
|
+
function PaginationGhost({ className, pages = 5, ...props }) {
|
|
2543
|
+
return /* @__PURE__ */ jsx29("div", { "aria-hidden": "true", className: cn("fractal-ghost-pagination", className), ...props, children: Array.from({ length: pages }, (_, index) => /* @__PURE__ */ jsx29(Ghost, {}, index)) });
|
|
2544
|
+
}
|
|
2545
|
+
function FileDropzoneGhost({ className, ...props }) {
|
|
2546
|
+
return /* @__PURE__ */ jsxs24("div", { "aria-hidden": "true", className: cn("fractal-ghost-dropzone", className), ...props, children: [
|
|
2547
|
+
/* @__PURE__ */ jsx29(Ghost, { className: "fractal-ghost-dropzone__icon" }),
|
|
2548
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "26%" }),
|
|
2549
|
+
/* @__PURE__ */ jsx29(GhostLine, { size: "sm", width: "46%" })
|
|
2550
|
+
] });
|
|
2551
|
+
}
|
|
2552
|
+
function PromptCardGhost({ className, ...props }) {
|
|
2553
|
+
return /* @__PURE__ */ jsx29(CardGhost, { className: cn("fractal-ghost-prompt-card", className), lines: 4, ...props });
|
|
2554
|
+
}
|
|
2555
|
+
function ConversationGhost({
|
|
2556
|
+
className,
|
|
2557
|
+
"aria-label": ariaLabel = "Loading conversation",
|
|
2558
|
+
...props
|
|
2559
|
+
}) {
|
|
2560
|
+
return /* @__PURE__ */ jsxs24(
|
|
2561
|
+
"div",
|
|
2562
|
+
{
|
|
2563
|
+
"aria-busy": "true",
|
|
2564
|
+
"aria-label": ariaLabel,
|
|
2565
|
+
className: cn("fractal-ghost-conversation", className),
|
|
2566
|
+
role: "status",
|
|
2567
|
+
...props,
|
|
2568
|
+
children: [
|
|
2569
|
+
/* @__PURE__ */ jsx29("div", { "aria-hidden": "true", className: "fractal-ghost-conversation__message fractal-ghost-conversation__message--user", children: /* @__PURE__ */ jsxs24("div", { className: "fractal-ghost-conversation__bubble", children: [
|
|
2570
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "100%" }),
|
|
2571
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "86%" }),
|
|
2572
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "64%" })
|
|
2573
|
+
] }) }),
|
|
2574
|
+
/* @__PURE__ */ jsxs24("div", { "aria-hidden": "true", className: "fractal-ghost-conversation__message fractal-ghost-conversation__message--assistant", children: [
|
|
2575
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "92%" }),
|
|
2576
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "78%" }),
|
|
2577
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "58%" })
|
|
2578
|
+
] })
|
|
2579
|
+
]
|
|
2580
|
+
}
|
|
2581
|
+
);
|
|
2582
|
+
}
|
|
2583
|
+
function SortableTableGhost(props) {
|
|
2584
|
+
return /* @__PURE__ */ jsx29(DataTableGhost, { ...props });
|
|
2585
|
+
}
|
|
2586
|
+
function SidebarGhost({ className, items = 6, ...props }) {
|
|
2587
|
+
return /* @__PURE__ */ jsxs24("aside", { "aria-hidden": "true", className: cn("fractal-ghost-sidebar", className), ...props, children: [
|
|
2588
|
+
/* @__PURE__ */ jsx29(Ghost, { className: "fractal-ghost-sidebar__brand" }),
|
|
2589
|
+
/* @__PURE__ */ jsx29("div", { className: "fractal-ghost-sidebar__items", children: Array.from({ length: items }, (_, index) => /* @__PURE__ */ jsxs24("div", { className: "fractal-ghost-sidebar__item", children: [
|
|
2590
|
+
/* @__PURE__ */ jsx29(Ghost, {}),
|
|
2591
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: `${[54, 68, 44, 61][index % 4]}%` })
|
|
2592
|
+
] }, index)) })
|
|
2593
|
+
] });
|
|
2594
|
+
}
|
|
2595
|
+
function ModalGhost({ className, title, lines = 3, ...props }) {
|
|
2596
|
+
return /* @__PURE__ */ jsx29(SurfaceGhost, { className: cn("fractal-ghost-modal", className), title, lines, ...props });
|
|
2597
|
+
}
|
|
2598
|
+
function SidePanelGhost({ className, title, lines = 5, ...props }) {
|
|
2599
|
+
return /* @__PURE__ */ jsx29(SurfaceGhost, { className: cn("fractal-ghost-side-panel", className), title, lines, ...props });
|
|
2600
|
+
}
|
|
2601
|
+
function SurfaceGhost({ className, title, lines = 3, ...props }) {
|
|
2602
|
+
return /* @__PURE__ */ jsxs24("div", { "aria-hidden": "true", className: cn("fractal-ghost-surface", className), ...props, children: [
|
|
2603
|
+
/* @__PURE__ */ jsxs24("div", { className: "fractal-ghost-surface__header", children: [
|
|
2604
|
+
/* @__PURE__ */ jsx29(GhostLine, { width: "42%" }),
|
|
2605
|
+
/* @__PURE__ */ jsx29(Ghost, { className: "fractal-ghost-surface__close" })
|
|
2606
|
+
] }),
|
|
2607
|
+
title && /* @__PURE__ */ jsx29("div", { className: "fractal-ghost-surface__title", children: title }),
|
|
2608
|
+
/* @__PURE__ */ jsx29("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx29(GhostLine, { width: index === lines - 1 ? "64%" : "100%" }, index)) })
|
|
2609
|
+
] });
|
|
2610
|
+
}
|
|
2611
|
+
|
|
987
2612
|
// src/components/Sidebar/Sidebar.tsx
|
|
988
|
-
import * as
|
|
989
|
-
import { jsx as
|
|
990
|
-
var Sidebar =
|
|
991
|
-
({ className, width, style, ...props }, ref) => /* @__PURE__ */
|
|
2613
|
+
import * as React15 from "react";
|
|
2614
|
+
import { Fragment as Fragment3, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2615
|
+
var Sidebar = React15.forwardRef(
|
|
2616
|
+
({ className, width, compact = false, style, ...props }, ref) => /* @__PURE__ */ jsx30(
|
|
992
2617
|
"aside",
|
|
993
2618
|
{
|
|
994
2619
|
ref,
|
|
995
2620
|
className: cn("fractal-sidebar", className),
|
|
2621
|
+
"data-compact": compact || void 0,
|
|
996
2622
|
style: { ...style, ...width ? { "--fractal-sidebar-width": width } : {} },
|
|
997
2623
|
...props
|
|
998
2624
|
}
|
|
999
2625
|
)
|
|
1000
2626
|
);
|
|
1001
2627
|
Sidebar.displayName = "Sidebar";
|
|
1002
|
-
var SidebarHeader =
|
|
1003
|
-
({ className, ...props }, ref) => /* @__PURE__ */
|
|
2628
|
+
var SidebarHeader = React15.forwardRef(
|
|
2629
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx30("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
|
|
1004
2630
|
);
|
|
1005
2631
|
SidebarHeader.displayName = "SidebarHeader";
|
|
1006
|
-
var SidebarBrand =
|
|
1007
|
-
({ className, type = "button", ...props }, ref) => /* @__PURE__ */
|
|
2632
|
+
var SidebarBrand = React15.forwardRef(
|
|
2633
|
+
({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx30("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
|
|
1008
2634
|
);
|
|
1009
2635
|
SidebarBrand.displayName = "SidebarBrand";
|
|
1010
|
-
|
|
1011
|
-
|
|
2636
|
+
function SidebarAccountMenu({
|
|
2637
|
+
name,
|
|
2638
|
+
items,
|
|
2639
|
+
onSelect,
|
|
2640
|
+
triggerLabel = "Account menu",
|
|
2641
|
+
icon,
|
|
2642
|
+
className
|
|
2643
|
+
}) {
|
|
2644
|
+
return /* @__PURE__ */ jsx30(
|
|
2645
|
+
Dropdown,
|
|
2646
|
+
{
|
|
2647
|
+
align: "left",
|
|
2648
|
+
className: cn("fractal-sidebar__account", className),
|
|
2649
|
+
items,
|
|
2650
|
+
onSelect,
|
|
2651
|
+
panelClassName: "fractal-sidebar__account-panel",
|
|
2652
|
+
selectedValue: "",
|
|
2653
|
+
triggerClassName: "fractal-sidebar__account-trigger",
|
|
2654
|
+
triggerContent: /* @__PURE__ */ jsxs25(Fragment3, { children: [
|
|
2655
|
+
icon && /* @__PURE__ */ jsx30("span", { className: "fractal-sidebar__account-icon", "aria-hidden": "true", children: icon }),
|
|
2656
|
+
/* @__PURE__ */ jsx30("span", { className: "fractal-sidebar__account-name", children: name })
|
|
2657
|
+
] }),
|
|
2658
|
+
triggerLabel,
|
|
2659
|
+
triggerVariant: "tertiary"
|
|
2660
|
+
}
|
|
2661
|
+
);
|
|
2662
|
+
}
|
|
2663
|
+
var SidebarNav = React15.forwardRef(
|
|
2664
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx30("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
|
|
1012
2665
|
);
|
|
1013
2666
|
SidebarNav.displayName = "SidebarNav";
|
|
1014
|
-
var SidebarSection =
|
|
1015
|
-
({ className, ...props }, ref) => /* @__PURE__ */
|
|
2667
|
+
var SidebarSection = React15.forwardRef(
|
|
2668
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx30("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
|
|
1016
2669
|
);
|
|
1017
2670
|
SidebarSection.displayName = "SidebarSection";
|
|
1018
|
-
var SidebarNavItem =
|
|
1019
|
-
({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */
|
|
2671
|
+
var SidebarNavItem = React15.forwardRef(
|
|
2672
|
+
({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs25(
|
|
1020
2673
|
"button",
|
|
1021
2674
|
{
|
|
1022
2675
|
ref,
|
|
@@ -1026,28 +2679,60 @@ var SidebarNavItem = React10.forwardRef(
|
|
|
1026
2679
|
"aria-current": active ? "page" : void 0,
|
|
1027
2680
|
...props,
|
|
1028
2681
|
children: [
|
|
1029
|
-
icon && /* @__PURE__ */
|
|
1030
|
-
/* @__PURE__ */
|
|
1031
|
-
endAdornment && /* @__PURE__ */
|
|
2682
|
+
icon && /* @__PURE__ */ jsx30("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
|
|
2683
|
+
/* @__PURE__ */ jsx30("span", { className: "fractal-sidebar__nav-item-label", children }),
|
|
2684
|
+
endAdornment && /* @__PURE__ */ jsx30("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
|
|
1032
2685
|
]
|
|
1033
2686
|
}
|
|
1034
2687
|
)
|
|
1035
2688
|
);
|
|
1036
2689
|
SidebarNavItem.displayName = "SidebarNavItem";
|
|
1037
|
-
var SidebarFooter =
|
|
1038
|
-
({ className, ...props }, ref) => /* @__PURE__ */
|
|
2690
|
+
var SidebarFooter = React15.forwardRef(
|
|
2691
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx30("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
|
|
1039
2692
|
);
|
|
1040
2693
|
SidebarFooter.displayName = "SidebarFooter";
|
|
1041
2694
|
|
|
1042
2695
|
// src/components/SidePanel/SidePanel.tsx
|
|
1043
|
-
import
|
|
1044
|
-
import {
|
|
1045
|
-
import {
|
|
1046
|
-
|
|
2696
|
+
import * as React16 from "react";
|
|
2697
|
+
import { createPortal as createPortal4 } from "react-dom";
|
|
2698
|
+
import { Close as Close2 } from "@mirror-physics/fractal-icons";
|
|
2699
|
+
import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2700
|
+
function SidePanel({ open, onClose, title, description, children, footer, size = "md", loading = false, loadingLabel = "Loading", className }) {
|
|
1047
2701
|
useEscapeDismiss(80, onClose, open);
|
|
2702
|
+
const bodyRef = React16.useRef(null);
|
|
2703
|
+
const [scrollEdges, setScrollEdges] = React16.useState({ above: false, below: false });
|
|
2704
|
+
React16.useLayoutEffect(() => {
|
|
2705
|
+
const body = bodyRef.current;
|
|
2706
|
+
if (!open || !body) return;
|
|
2707
|
+
const measure = () => {
|
|
2708
|
+
const maxScroll = Math.max(0, body.scrollHeight - body.clientHeight);
|
|
2709
|
+
const top = Math.max(0, Math.min(body.scrollTop, maxScroll));
|
|
2710
|
+
const above = maxScroll > 1 && top > 0;
|
|
2711
|
+
const below = maxScroll - top > 1;
|
|
2712
|
+
setScrollEdges((previous) => previous.above === above && previous.below === below ? previous : { above, below });
|
|
2713
|
+
};
|
|
2714
|
+
const resize = new ResizeObserver(measure);
|
|
2715
|
+
const observeContent = () => {
|
|
2716
|
+
resize.disconnect();
|
|
2717
|
+
resize.observe(body);
|
|
2718
|
+
for (const child of body.children) resize.observe(child);
|
|
2719
|
+
measure();
|
|
2720
|
+
};
|
|
2721
|
+
const mutations = new MutationObserver(observeContent);
|
|
2722
|
+
mutations.observe(body, { childList: true, subtree: true, characterData: true, attributes: true });
|
|
2723
|
+
body.addEventListener("scroll", measure, { passive: true });
|
|
2724
|
+
body.addEventListener("load", measure, true);
|
|
2725
|
+
observeContent();
|
|
2726
|
+
return () => {
|
|
2727
|
+
resize.disconnect();
|
|
2728
|
+
mutations.disconnect();
|
|
2729
|
+
body.removeEventListener("scroll", measure);
|
|
2730
|
+
body.removeEventListener("load", measure, true);
|
|
2731
|
+
};
|
|
2732
|
+
}, [open, loading]);
|
|
1048
2733
|
if (!open) return null;
|
|
1049
|
-
return
|
|
1050
|
-
/* @__PURE__ */
|
|
2734
|
+
return createPortal4(
|
|
2735
|
+
/* @__PURE__ */ jsx31("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs26(
|
|
1051
2736
|
"aside",
|
|
1052
2737
|
{
|
|
1053
2738
|
className: cn("fractal-side-panel", `fractal-side-panel--${size}`, className),
|
|
@@ -1056,15 +2741,15 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
|
|
|
1056
2741
|
role: "dialog",
|
|
1057
2742
|
onMouseDown: (event) => event.stopPropagation(),
|
|
1058
2743
|
children: [
|
|
1059
|
-
/* @__PURE__ */
|
|
1060
|
-
/* @__PURE__ */
|
|
1061
|
-
/* @__PURE__ */
|
|
1062
|
-
description && /* @__PURE__ */
|
|
2744
|
+
/* @__PURE__ */ jsxs26("header", { className: "fractal-side-panel__header", "data-scroll-divider": scrollEdges.above, children: [
|
|
2745
|
+
/* @__PURE__ */ jsxs26("div", { className: "fractal-side-panel__heading", children: [
|
|
2746
|
+
typeof title === "string" ? /* @__PURE__ */ jsx31("h2", { className: "fractal-side-panel__title", children: title }) : /* @__PURE__ */ jsx31("div", { className: "fractal-side-panel__title", children: title }),
|
|
2747
|
+
description && /* @__PURE__ */ jsx31("p", { className: "fractal-side-panel__description", children: description })
|
|
1063
2748
|
] }),
|
|
1064
|
-
/* @__PURE__ */
|
|
2749
|
+
/* @__PURE__ */ jsx31("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx31(Close2, { size: 18 }) })
|
|
1065
2750
|
] }),
|
|
1066
|
-
/* @__PURE__ */
|
|
1067
|
-
footer && /* @__PURE__ */
|
|
2751
|
+
/* @__PURE__ */ jsx31("div", { ref: bodyRef, "aria-busy": loading || void 0, className: cn("fractal-side-panel__body", loading && "fractal-side-panel__body--loading"), children: loading ? /* @__PURE__ */ jsx31(UILoader, { label: loadingLabel }) : children }),
|
|
2752
|
+
footer && /* @__PURE__ */ jsx31("footer", { className: "fractal-side-panel__footer", "data-scroll-divider": scrollEdges.below, children: footer })
|
|
1068
2753
|
]
|
|
1069
2754
|
}
|
|
1070
2755
|
) }),
|
|
@@ -1073,47 +2758,295 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
|
|
|
1073
2758
|
}
|
|
1074
2759
|
|
|
1075
2760
|
// src/components/Textarea/Textarea.tsx
|
|
1076
|
-
import * as
|
|
1077
|
-
import { jsx as
|
|
1078
|
-
var Textarea =
|
|
1079
|
-
({ className,
|
|
2761
|
+
import * as React17 from "react";
|
|
2762
|
+
import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2763
|
+
var Textarea = React17.forwardRef(
|
|
2764
|
+
({ className, fieldLabel, textStyle = "source", id, placeholder, ...props }, ref) => {
|
|
2765
|
+
const generatedId = React17.useId();
|
|
2766
|
+
const fieldId = id ?? generatedId;
|
|
2767
|
+
const input = /* @__PURE__ */ jsx32(
|
|
2768
|
+
"textarea",
|
|
2769
|
+
{
|
|
2770
|
+
ref,
|
|
2771
|
+
id: fieldLabel ? fieldId : id,
|
|
2772
|
+
placeholder: fieldLabel ? placeholder || " " : placeholder,
|
|
2773
|
+
className: cn("fractal-textarea", className),
|
|
2774
|
+
"data-text-style": textStyle,
|
|
2775
|
+
...props
|
|
2776
|
+
}
|
|
2777
|
+
);
|
|
2778
|
+
return fieldLabel ? /* @__PURE__ */ jsxs27("div", { className: "fractal-floating-field fractal-floating-field--textarea", "data-control-size": "md", children: [
|
|
2779
|
+
input,
|
|
2780
|
+
/* @__PURE__ */ jsx32("label", { className: "fractal-floating-field__label", htmlFor: fieldId, children: fieldLabel })
|
|
2781
|
+
] }) : input;
|
|
2782
|
+
}
|
|
1080
2783
|
);
|
|
1081
2784
|
Textarea.displayName = "Textarea";
|
|
2785
|
+
|
|
2786
|
+
// src/components/TextButton/TextButton.tsx
|
|
2787
|
+
import * as React18 from "react";
|
|
2788
|
+
import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2789
|
+
var TextButton = React18.forwardRef(
|
|
2790
|
+
({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ jsxs28(
|
|
2791
|
+
"button",
|
|
2792
|
+
{
|
|
2793
|
+
ref,
|
|
2794
|
+
className: cn("fractal-text-button", `fractal-text-button--${size}`, `fractal-text-button--${tone}`, className),
|
|
2795
|
+
type: "button",
|
|
2796
|
+
...props,
|
|
2797
|
+
children: [
|
|
2798
|
+
icon && iconPosition === "start" && /* @__PURE__ */ jsx33("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon }),
|
|
2799
|
+
/* @__PURE__ */ jsx33("span", { children }),
|
|
2800
|
+
icon && iconPosition === "end" && /* @__PURE__ */ jsx33("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon })
|
|
2801
|
+
]
|
|
2802
|
+
}
|
|
2803
|
+
)
|
|
2804
|
+
);
|
|
2805
|
+
TextButton.displayName = "TextButton";
|
|
2806
|
+
|
|
2807
|
+
// src/components/FileDropzone/FileDropzone.tsx
|
|
2808
|
+
import * as React19 from "react";
|
|
2809
|
+
import { Close as Close3, FileArrowUp } from "@mirror-physics/fractal-icons";
|
|
2810
|
+
import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2811
|
+
function FileDropzone({
|
|
2812
|
+
files,
|
|
2813
|
+
onFilesChange,
|
|
2814
|
+
accept,
|
|
2815
|
+
multiple = true,
|
|
2816
|
+
disabled = false,
|
|
2817
|
+
title = "Drop files here or click to browse",
|
|
2818
|
+
description = "Files stay attached to this record.",
|
|
2819
|
+
className
|
|
2820
|
+
}) {
|
|
2821
|
+
const inputId = React19.useId();
|
|
2822
|
+
const inputRef = React19.useRef(null);
|
|
2823
|
+
const [isDragging, setIsDragging] = React19.useState(false);
|
|
2824
|
+
const addFiles = (nextFiles) => {
|
|
2825
|
+
if (disabled) return;
|
|
2826
|
+
const additions = Array.from(nextFiles);
|
|
2827
|
+
if (additions.length === 0) return;
|
|
2828
|
+
onFilesChange(multiple ? [...files, ...additions] : additions.slice(0, 1));
|
|
2829
|
+
};
|
|
2830
|
+
return /* @__PURE__ */ jsxs29("div", { className: cn("fractal-file-dropzone", className), children: [
|
|
2831
|
+
/* @__PURE__ */ jsx34(
|
|
2832
|
+
"input",
|
|
2833
|
+
{
|
|
2834
|
+
ref: inputRef,
|
|
2835
|
+
id: inputId,
|
|
2836
|
+
className: "fractal-file-dropzone__input",
|
|
2837
|
+
type: "file",
|
|
2838
|
+
accept,
|
|
2839
|
+
multiple,
|
|
2840
|
+
disabled,
|
|
2841
|
+
onChange: (event) => {
|
|
2842
|
+
if (event.target.files) addFiles(event.target.files);
|
|
2843
|
+
event.target.value = "";
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
),
|
|
2847
|
+
/* @__PURE__ */ jsxs29(
|
|
2848
|
+
"button",
|
|
2849
|
+
{
|
|
2850
|
+
className: "fractal-file-dropzone__target",
|
|
2851
|
+
"data-dragging": isDragging || void 0,
|
|
2852
|
+
type: "button",
|
|
2853
|
+
disabled,
|
|
2854
|
+
onClick: () => inputRef.current?.click(),
|
|
2855
|
+
onDragEnter: (event) => {
|
|
2856
|
+
event.preventDefault();
|
|
2857
|
+
if (!disabled) setIsDragging(true);
|
|
2858
|
+
},
|
|
2859
|
+
onDragOver: (event) => event.preventDefault(),
|
|
2860
|
+
onDragLeave: (event) => {
|
|
2861
|
+
event.preventDefault();
|
|
2862
|
+
setIsDragging(false);
|
|
2863
|
+
},
|
|
2864
|
+
onDrop: (event) => {
|
|
2865
|
+
event.preventDefault();
|
|
2866
|
+
setIsDragging(false);
|
|
2867
|
+
addFiles(event.dataTransfer.files);
|
|
2868
|
+
},
|
|
2869
|
+
children: [
|
|
2870
|
+
/* @__PURE__ */ jsx34(FileArrowUp, { "aria-hidden": "true", size: 20 }),
|
|
2871
|
+
/* @__PURE__ */ jsxs29("span", { className: "fractal-file-dropzone__copy", children: [
|
|
2872
|
+
/* @__PURE__ */ jsx34("span", { className: "fractal-file-dropzone__title", children: title }),
|
|
2873
|
+
description && /* @__PURE__ */ jsx34("span", { className: "fractal-file-dropzone__description", children: description })
|
|
2874
|
+
] })
|
|
2875
|
+
]
|
|
2876
|
+
}
|
|
2877
|
+
),
|
|
2878
|
+
files.length > 0 && /* @__PURE__ */ jsx34("ul", { className: "fractal-file-dropzone__files", "aria-label": "Attached files", children: files.map((file, index) => /* @__PURE__ */ jsxs29("li", { className: "fractal-file-dropzone__file", children: [
|
|
2879
|
+
/* @__PURE__ */ jsx34("span", { className: "fractal-file-dropzone__file-name", children: file.name }),
|
|
2880
|
+
/* @__PURE__ */ jsx34("span", { className: "fractal-file-dropzone__file-meta", children: formatFileSize(file.size) }),
|
|
2881
|
+
/* @__PURE__ */ jsx34(
|
|
2882
|
+
"button",
|
|
2883
|
+
{
|
|
2884
|
+
className: "fractal-file-dropzone__remove",
|
|
2885
|
+
type: "button",
|
|
2886
|
+
"aria-label": `Remove ${file.name}`,
|
|
2887
|
+
disabled,
|
|
2888
|
+
onClick: () => onFilesChange(files.filter((_, fileIndex) => fileIndex !== index)),
|
|
2889
|
+
children: /* @__PURE__ */ jsx34(Close3, { "aria-hidden": "true", size: 14 })
|
|
2890
|
+
}
|
|
2891
|
+
)
|
|
2892
|
+
] }, `${file.name}-${file.size}-${index}`)) })
|
|
2893
|
+
] });
|
|
2894
|
+
}
|
|
2895
|
+
function formatFileSize(bytes) {
|
|
2896
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
2897
|
+
if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
|
|
2898
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
// src/components/Pagination/Pagination.tsx
|
|
2902
|
+
import { ArrowLeft, ArrowRight } from "@mirror-physics/fractal-icons";
|
|
2903
|
+
import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2904
|
+
function Pagination({
|
|
2905
|
+
page,
|
|
2906
|
+
pageSize,
|
|
2907
|
+
totalItems,
|
|
2908
|
+
onPageChange,
|
|
2909
|
+
pageSizeOptions,
|
|
2910
|
+
onPageSizeChange,
|
|
2911
|
+
ariaLabel = "Pagination",
|
|
2912
|
+
className
|
|
2913
|
+
}) {
|
|
2914
|
+
const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
|
2915
|
+
const currentPage = Math.min(Math.max(page, 1), totalPages);
|
|
2916
|
+
const start = totalItems === 0 ? 0 : (currentPage - 1) * pageSize + 1;
|
|
2917
|
+
const end = Math.min(currentPage * pageSize, totalItems);
|
|
2918
|
+
const canChangePageSize = pageSizeOptions != null && pageSizeOptions.length > 0 && onPageSizeChange != null;
|
|
2919
|
+
return /* @__PURE__ */ jsxs30("nav", { className: cn("fractal-pagination", className), "aria-label": ariaLabel, children: [
|
|
2920
|
+
/* @__PURE__ */ jsx35("span", { className: "fractal-pagination__summary", children: totalItems === 0 ? "No results" : `${start}-${end} of ${totalItems}` }),
|
|
2921
|
+
/* @__PURE__ */ jsxs30("div", { className: "fractal-pagination__controls", children: [
|
|
2922
|
+
canChangePageSize && /* @__PURE__ */ jsx35(
|
|
2923
|
+
Dropdown,
|
|
2924
|
+
{
|
|
2925
|
+
align: "right",
|
|
2926
|
+
className: "fractal-pagination__page-size",
|
|
2927
|
+
direction: "up",
|
|
2928
|
+
items: pageSizeOptions.map((option) => ({
|
|
2929
|
+
value: String(option),
|
|
2930
|
+
description: `${option} per page`
|
|
2931
|
+
})),
|
|
2932
|
+
onSelect: (value) => onPageSizeChange(Number(value)),
|
|
2933
|
+
selectedValue: String(pageSize),
|
|
2934
|
+
size: "sm",
|
|
2935
|
+
triggerLabel: "Results per page",
|
|
2936
|
+
triggerVariant: "tertiary"
|
|
2937
|
+
}
|
|
2938
|
+
),
|
|
2939
|
+
/* @__PURE__ */ jsx35(
|
|
2940
|
+
"button",
|
|
2941
|
+
{
|
|
2942
|
+
className: "fractal-pagination__button",
|
|
2943
|
+
type: "button",
|
|
2944
|
+
"aria-label": "Previous page",
|
|
2945
|
+
title: "Previous page",
|
|
2946
|
+
disabled: currentPage === 1 || totalItems === 0,
|
|
2947
|
+
onClick: () => onPageChange(currentPage - 1),
|
|
2948
|
+
children: /* @__PURE__ */ jsx35(ArrowLeft, { "aria-hidden": "true", size: 16 })
|
|
2949
|
+
}
|
|
2950
|
+
),
|
|
2951
|
+
/* @__PURE__ */ jsxs30("span", { className: "fractal-pagination__page", "aria-current": "page", children: [
|
|
2952
|
+
currentPage,
|
|
2953
|
+
" of ",
|
|
2954
|
+
totalPages
|
|
2955
|
+
] }),
|
|
2956
|
+
/* @__PURE__ */ jsx35(
|
|
2957
|
+
"button",
|
|
2958
|
+
{
|
|
2959
|
+
className: "fractal-pagination__button",
|
|
2960
|
+
type: "button",
|
|
2961
|
+
"aria-label": "Next page",
|
|
2962
|
+
title: "Next page",
|
|
2963
|
+
disabled: currentPage === totalPages || totalItems === 0,
|
|
2964
|
+
onClick: () => onPageChange(currentPage + 1),
|
|
2965
|
+
children: /* @__PURE__ */ jsx35(ArrowRight, { "aria-hidden": "true", size: 16 })
|
|
2966
|
+
}
|
|
2967
|
+
)
|
|
2968
|
+
] })
|
|
2969
|
+
] });
|
|
2970
|
+
}
|
|
1082
2971
|
export {
|
|
1083
2972
|
Alert,
|
|
1084
2973
|
AlertDescription,
|
|
2974
|
+
AlertGhost,
|
|
1085
2975
|
AlertTitle,
|
|
2976
|
+
Breadcrumbs,
|
|
1086
2977
|
Button,
|
|
2978
|
+
ButtonGhost,
|
|
1087
2979
|
Card,
|
|
1088
2980
|
CardContent,
|
|
1089
2981
|
CardDescription,
|
|
1090
2982
|
CardFooter,
|
|
2983
|
+
CardGhost,
|
|
1091
2984
|
CardHeader,
|
|
1092
2985
|
CardTitle,
|
|
2986
|
+
Checkbox,
|
|
2987
|
+
CheckboxGhost,
|
|
1093
2988
|
Chip,
|
|
2989
|
+
ChipGhost,
|
|
2990
|
+
CodeBlock,
|
|
2991
|
+
ColumnEditor,
|
|
2992
|
+
ContentSwitcher,
|
|
2993
|
+
ContentSwitcherGhost,
|
|
2994
|
+
ConversationGhost,
|
|
1094
2995
|
DataTable,
|
|
2996
|
+
DataTableGhost,
|
|
1095
2997
|
Disclosure,
|
|
2998
|
+
DisclosureGhost,
|
|
1096
2999
|
Dropdown,
|
|
3000
|
+
DropdownGhost,
|
|
1097
3001
|
FeatureCard,
|
|
3002
|
+
FileDropzone,
|
|
3003
|
+
FileDropzoneGhost,
|
|
1098
3004
|
FolderCard,
|
|
3005
|
+
Ghost,
|
|
3006
|
+
GhostLine,
|
|
1099
3007
|
Input,
|
|
3008
|
+
InputGhost,
|
|
1100
3009
|
Label,
|
|
3010
|
+
LabelGhost,
|
|
3011
|
+
LatticeLoader,
|
|
1101
3012
|
Link,
|
|
3013
|
+
LinkGhost,
|
|
1102
3014
|
Modal,
|
|
3015
|
+
ModalGhost,
|
|
3016
|
+
NumberInput,
|
|
3017
|
+
Pagination,
|
|
3018
|
+
PaginationGhost,
|
|
3019
|
+
PasswordInput,
|
|
1103
3020
|
PromptCard,
|
|
3021
|
+
PromptCardGhost,
|
|
1104
3022
|
PromptGrid,
|
|
3023
|
+
Radio,
|
|
1105
3024
|
SidePanel,
|
|
3025
|
+
SidePanelGhost,
|
|
1106
3026
|
Sidebar,
|
|
3027
|
+
SidebarAccountMenu,
|
|
1107
3028
|
SidebarBrand,
|
|
1108
3029
|
SidebarFooter,
|
|
3030
|
+
SidebarGhost,
|
|
1109
3031
|
SidebarHeader,
|
|
1110
3032
|
SidebarNav,
|
|
1111
3033
|
SidebarNavItem,
|
|
1112
3034
|
SidebarSection,
|
|
3035
|
+
Slider,
|
|
1113
3036
|
SortableTable,
|
|
3037
|
+
SortableTableGhost,
|
|
3038
|
+
TableSearch,
|
|
1114
3039
|
Tabs,
|
|
3040
|
+
TabsGhost,
|
|
3041
|
+
TextButton,
|
|
3042
|
+
TextButtonGhost,
|
|
1115
3043
|
Textarea,
|
|
3044
|
+
TextareaGhost,
|
|
3045
|
+
Toast,
|
|
3046
|
+
ToastViewport,
|
|
1116
3047
|
Toggle,
|
|
3048
|
+
ToggleGhost,
|
|
3049
|
+
UILoader,
|
|
1117
3050
|
cn,
|
|
1118
3051
|
useEscapeDismiss
|
|
1119
3052
|
};
|