@mirror-physics/fractal-ui 0.2.0-next.4 → 0.2.0-next.40

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/dist/index.js CHANGED
@@ -50,7 +50,7 @@ var colorPalette = {
50
50
  var sizeConfig = {
51
51
  xs: { height: "28px", padding: "4px 8px", iconOnly: "28px", fontSize: "0.75rem" },
52
52
  sm: { height: "32px", padding: "6px 12px", iconOnly: "32px", fontSize: "0.75rem" },
53
- md: { height: "38px", padding: "8px 16px", iconOnly: "38px", fontSize: "0.875rem" },
53
+ md: { height: "40px", padding: "8px 16px", iconOnly: "40px", fontSize: "0.875rem" },
54
54
  lg: { height: "44px", padding: "10px 20px", iconOnly: "44px", fontSize: "1rem" },
55
55
  xl: { height: "56px", padding: "14px 28px", iconOnly: "56px", fontSize: "1rem" }
56
56
  };
@@ -165,44 +165,65 @@ var Button = React.forwardRef(
165
165
  );
166
166
  Button.displayName = "Button";
167
167
 
168
+ // src/components/Breadcrumbs/Breadcrumbs.tsx
169
+ import { ChevronRight } from "@mirror-physics/fractal-icons";
170
+ import { jsx, jsxs as jsxs2 } from "react/jsx-runtime";
171
+ function Breadcrumbs({
172
+ items,
173
+ ariaLabel = "Breadcrumbs",
174
+ separator = /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true", size: 14 }),
175
+ className,
176
+ ...props
177
+ }) {
178
+ if (items.length === 0) return null;
179
+ return /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, className: cn("fractal-breadcrumbs", className), ...props, children: /* @__PURE__ */ jsx("ol", { className: "fractal-breadcrumbs__list", children: items.map((item, index) => {
180
+ const isCurrent = index === items.length - 1;
181
+ const content = isCurrent ? /* @__PURE__ */ jsx("span", { "aria-current": "page", className: "fractal-breadcrumbs__current", children: item.label }) : /* @__PURE__ */ jsx("a", { className: "fractal-breadcrumbs__link", href: item.href ?? "#", onClick: item.onClick, children: item.label });
182
+ return /* @__PURE__ */ jsxs2("li", { className: "fractal-breadcrumbs__item", children: [
183
+ index > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "fractal-breadcrumbs__separator", children: separator }),
184
+ content
185
+ ] }, index);
186
+ }) }) });
187
+ }
188
+
168
189
  // src/components/Card/Card.tsx
169
190
  import * as React2 from "react";
170
191
  import { Folder as FolderIcon } from "@mirror-physics/fractal-icons";
171
- import { jsx, jsxs as jsxs2 } from "react/jsx-runtime";
192
+ import { jsx as jsx2, jsxs as jsxs3 } from "react/jsx-runtime";
172
193
  var Card = React2.forwardRef(
173
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("fractal-card", className), ...props })
194
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx2("div", { ref, className: cn("fractal-card", className), ...props })
174
195
  );
175
196
  Card.displayName = "Card";
176
197
  var CardHeader = React2.forwardRef(
177
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("fractal-card-header", className), ...props })
198
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx2("div", { ref, className: cn("fractal-card-header", className), ...props })
178
199
  );
179
200
  CardHeader.displayName = "CardHeader";
180
201
  var CardTitle = React2.forwardRef(
181
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("fractal-card-title", className), ...props })
202
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx2("h3", { ref, className: cn("fractal-card-title", className), ...props })
182
203
  );
183
204
  CardTitle.displayName = "CardTitle";
184
205
  var CardDescription = React2.forwardRef(
185
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("fractal-card-description", className), ...props })
206
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx2("p", { ref, className: cn("fractal-card-description", className), ...props })
186
207
  );
187
208
  CardDescription.displayName = "CardDescription";
188
209
  var CardContent = React2.forwardRef(
189
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("fractal-card-content", className), ...props })
210
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx2("div", { ref, className: cn("fractal-card-content", className), ...props })
190
211
  );
191
212
  CardContent.displayName = "CardContent";
192
213
  var CardFooter = React2.forwardRef(
193
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("fractal-card-footer", className), ...props })
214
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx2("div", { ref, className: cn("fractal-card-footer", className), ...props })
194
215
  );
195
216
  CardFooter.displayName = "CardFooter";
196
217
  var FeatureCard = React2.forwardRef(
197
- ({ icon, title, description, className, ...props }, ref) => /* @__PURE__ */ jsxs2("div", { ref, className: cn("fractal-feature-card", className), ...props, children: [
198
- icon && /* @__PURE__ */ jsx("div", { className: "fractal-feature-card__icon", children: icon }),
199
- /* @__PURE__ */ jsx("h3", { className: "fractal-feature-card__title", children: title }),
200
- /* @__PURE__ */ jsx("p", { className: "fractal-feature-card__description", children: description })
218
+ ({ icon, title, description, className, ...props }, ref) => /* @__PURE__ */ jsxs3("div", { ref, className: cn("fractal-feature-card", className), ...props, children: [
219
+ icon && /* @__PURE__ */ jsx2("div", { className: "fractal-feature-card__icon", children: icon }),
220
+ /* @__PURE__ */ jsx2("h3", { className: "fractal-feature-card__title", children: title }),
221
+ /* @__PURE__ */ jsx2("p", { className: "fractal-feature-card__description", children: description })
201
222
  ] })
202
223
  );
203
224
  FeatureCard.displayName = "FeatureCard";
204
225
  var FolderCard = React2.forwardRef(
205
- ({ name, sessionCount, selected, className, onClick, ...props }, ref) => /* @__PURE__ */ jsxs2(
226
+ ({ name, sessionCount, selected, className, onClick, ...props }, ref) => /* @__PURE__ */ jsxs3(
206
227
  "div",
207
228
  {
208
229
  ref,
@@ -218,11 +239,11 @@ var FolderCard = React2.forwardRef(
218
239
  className: cn("fractal-folder-card", selected && "fractal-folder-card--selected", className),
219
240
  ...props,
220
241
  children: [
221
- /* @__PURE__ */ jsxs2("div", { className: "fractal-folder-card__header", children: [
222
- /* @__PURE__ */ jsx(FolderIcon, { size: 16, className: "fractal-folder-card__icon", "aria-hidden": true }),
223
- /* @__PURE__ */ jsx("span", { className: "fractal-folder-card__name", children: name })
242
+ /* @__PURE__ */ jsxs3("div", { className: "fractal-folder-card__header", children: [
243
+ /* @__PURE__ */ jsx2(FolderIcon, { size: 16, className: "fractal-folder-card__icon", "aria-hidden": true }),
244
+ /* @__PURE__ */ jsx2("span", { className: "fractal-folder-card__name", children: name })
224
245
  ] }),
225
- sessionCount !== void 0 && /* @__PURE__ */ jsx("div", { className: "fractal-folder-card__footer", children: /* @__PURE__ */ jsxs2("span", { className: "fractal-folder-card__sessions", children: [
246
+ sessionCount !== void 0 && /* @__PURE__ */ jsx2("div", { className: "fractal-folder-card__footer", children: /* @__PURE__ */ jsxs3("span", { className: "fractal-folder-card__sessions", children: [
226
247
  sessionCount,
227
248
  " ",
228
249
  sessionCount === 1 ? "session" : "sessions"
@@ -235,7 +256,7 @@ FolderCard.displayName = "FolderCard";
235
256
 
236
257
  // src/components/PromptCard/PromptCard.tsx
237
258
  import * as React3 from "react";
238
- import { jsx as jsx2, jsxs as jsxs3 } from "react/jsx-runtime";
259
+ import { jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
239
260
  var titleToPrompt = (title) => typeof title === "string" || typeof title === "number" ? String(title) : "";
240
261
  var PromptCard = React3.forwardRef(
241
262
  ({
@@ -256,7 +277,7 @@ var PromptCard = React3.forwardRef(
256
277
  onPromptSelect?.(prompt ?? titleToPrompt(title));
257
278
  }
258
279
  };
259
- return /* @__PURE__ */ jsxs3(
280
+ return /* @__PURE__ */ jsxs4(
260
281
  "button",
261
282
  {
262
283
  ref,
@@ -271,8 +292,8 @@ var PromptCard = React3.forwardRef(
271
292
  onClick: handleClick,
272
293
  ...props,
273
294
  children: [
274
- icon && /* @__PURE__ */ jsx2("span", { className: "fractal-prompt-card__icon", "aria-hidden": "true", children: icon }),
275
- /* @__PURE__ */ jsx2("span", { className: "fractal-prompt-card__title", children: title })
295
+ icon && /* @__PURE__ */ jsx3("span", { className: "fractal-prompt-card__icon", "aria-hidden": "true", children: icon }),
296
+ /* @__PURE__ */ jsx3("span", { className: "fractal-prompt-card__title", children: title })
276
297
  ]
277
298
  }
278
299
  );
@@ -280,7 +301,7 @@ var PromptCard = React3.forwardRef(
280
301
  );
281
302
  PromptCard.displayName = "PromptCard";
282
303
  var PromptGrid = React3.forwardRef(
283
- ({ compact, padded, className, ...props }, ref) => /* @__PURE__ */ jsx2(
304
+ ({ compact, padded, className, ...props }, ref) => /* @__PURE__ */ jsx3(
284
305
  "div",
285
306
  {
286
307
  ref,
@@ -298,9 +319,9 @@ PromptGrid.displayName = "PromptGrid";
298
319
 
299
320
  // src/components/Input/Input.tsx
300
321
  import * as React4 from "react";
301
- import { jsx as jsx3 } from "react/jsx-runtime";
322
+ import { jsx as jsx4 } from "react/jsx-runtime";
302
323
  var Input = React4.forwardRef(
303
- ({ className, type, ...props }, ref) => /* @__PURE__ */ jsx3(
324
+ ({ className, type, ...props }, ref) => /* @__PURE__ */ jsx4(
304
325
  "input",
305
326
  {
306
327
  type,
@@ -314,9 +335,9 @@ Input.displayName = "Input";
314
335
 
315
336
  // src/components/Label/Label.tsx
316
337
  import * as React5 from "react";
317
- import { jsx as jsx4 } from "react/jsx-runtime";
338
+ import { jsx as jsx5 } from "react/jsx-runtime";
318
339
  var Label = React5.forwardRef(
319
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
340
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx5(
320
341
  "label",
321
342
  {
322
343
  ref,
@@ -329,43 +350,47 @@ Label.displayName = "Label";
329
350
 
330
351
  // src/components/Alert/Alert.tsx
331
352
  import * as React6 from "react";
332
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
353
+ import { CircleCheck, CircleClose, CircleInfo, Warning } from "@mirror-physics/fractal-icons";
354
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
333
355
  var defaultIcons = {
334
- default: "\u2139",
335
- info: "\u2139",
336
- success: "\u2713",
337
- warning: "\u26A0",
338
- error: "\u2715"
356
+ default: CircleInfo,
357
+ info: CircleInfo,
358
+ success: CircleCheck,
359
+ warning: Warning,
360
+ error: CircleClose
339
361
  };
340
362
  var Alert = React6.forwardRef(
341
- ({ className, variant = "default", icon, children, ...props }, ref) => /* @__PURE__ */ jsxs4(
342
- "div",
343
- {
344
- ref,
345
- role: "alert",
346
- className: cn("fractal-alert", `fractal-alert--${variant}`, className),
347
- ...props,
348
- children: [
349
- /* @__PURE__ */ jsx5("span", { className: "fractal-alert-icon", "aria-hidden": true, children: icon ?? defaultIcons[variant] }),
350
- /* @__PURE__ */ jsx5("div", { className: "fractal-alert-content", children })
351
- ]
352
- }
353
- )
363
+ ({ className, variant = "default", icon, children, ...props }, ref) => {
364
+ const DefaultIcon = defaultIcons[variant];
365
+ return /* @__PURE__ */ jsxs5(
366
+ "div",
367
+ {
368
+ ref,
369
+ role: "alert",
370
+ className: cn("fractal-alert", `fractal-alert--${variant}`, className),
371
+ ...props,
372
+ children: [
373
+ /* @__PURE__ */ jsx6("span", { className: "fractal-alert-icon", "aria-hidden": true, children: icon ?? /* @__PURE__ */ jsx6(DefaultIcon, { size: 18 }) }),
374
+ /* @__PURE__ */ jsx6("div", { className: "fractal-alert-content", children })
375
+ ]
376
+ }
377
+ );
378
+ }
354
379
  );
355
380
  Alert.displayName = "Alert";
356
381
  var AlertTitle = React6.forwardRef(
357
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx5("h5", { ref, className: cn("fractal-alert-title", className), ...props })
382
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx6("h5", { ref, className: cn("fractal-alert-title", className), ...props })
358
383
  );
359
384
  AlertTitle.displayName = "AlertTitle";
360
385
  var AlertDescription = React6.forwardRef(
361
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx5("div", { ref, className: cn("fractal-alert-description", className), ...props })
386
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx6("div", { ref, className: cn("fractal-alert-description", className), ...props })
362
387
  );
363
388
  AlertDescription.displayName = "AlertDescription";
364
389
 
365
390
  // src/components/Toggle/Toggle.tsx
366
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
391
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
367
392
  function Toggle({ checked, onChange, label, className }) {
368
- return /* @__PURE__ */ jsxs5(
393
+ return /* @__PURE__ */ jsxs6(
369
394
  "button",
370
395
  {
371
396
  type: "button",
@@ -374,8 +399,8 @@ function Toggle({ checked, onChange, label, className }) {
374
399
  className: cn("fractal-toggle", className),
375
400
  onClick: () => onChange(!checked),
376
401
  children: [
377
- /* @__PURE__ */ jsx6("span", { className: cn("fractal-toggle-track", checked && "fractal-toggle-track--checked"), children: /* @__PURE__ */ jsx6("span", { className: cn("fractal-toggle-thumb", checked && "fractal-toggle-thumb--checked") }) }),
378
- label && /* @__PURE__ */ jsx6("span", { className: "fractal-toggle-label", children: label })
402
+ /* @__PURE__ */ jsx7("span", { className: cn("fractal-toggle-track", checked && "fractal-toggle-track--checked"), children: /* @__PURE__ */ jsx7("span", { className: cn("fractal-toggle-thumb", checked && "fractal-toggle-thumb--checked") }) }),
403
+ label && /* @__PURE__ */ jsx7("span", { className: "fractal-toggle-label", children: label })
379
404
  ]
380
405
  }
381
406
  );
@@ -434,27 +459,72 @@ function useEscapeDismiss(priority, handler, enabled = true) {
434
459
  }, [priority, enabled]);
435
460
  }
436
461
 
462
+ // src/components/UILoader/UILoader.tsx
463
+ import { useId } from "react";
464
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
465
+ 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";
466
+ 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";
467
+ 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";
468
+ function UILoader({ size = 48, tone = "neutral", className, style, label = "Loading", ariaHidden = false }) {
469
+ const rawId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
470
+ const clipId = `fractal-ui-loader-clip-${rawId}`;
471
+ const gradientId = `fractal-ui-loader-gradient-${rawId}`;
472
+ const height = typeof size === "number" ? `${size}px` : size;
473
+ return /* @__PURE__ */ jsx8(
474
+ "span",
475
+ {
476
+ "aria-hidden": ariaHidden || void 0,
477
+ "aria-label": ariaHidden ? void 0 : label,
478
+ className: cn("fractal-ui-loader", `fractal-ui-loader--${tone}`, className),
479
+ role: ariaHidden ? void 0 : "status",
480
+ style,
481
+ children: /* @__PURE__ */ jsxs7("svg", { "aria-hidden": "true", fill: "none", viewBox: "0 0 144 92", style: { height, width: "auto" }, children: [
482
+ /* @__PURE__ */ jsxs7("defs", { children: [
483
+ /* @__PURE__ */ jsxs7("linearGradient", { id: gradientId, x1: "59", x2: "94.3", y1: "0", y2: "89.1", gradientUnits: "userSpaceOnUse", children: [
484
+ /* @__PURE__ */ jsx8("stop", { className: "fractal-ui-loader__stop-start", offset: "0" }),
485
+ /* @__PURE__ */ jsx8("stop", { className: "fractal-ui-loader__stop-end", offset: "1" })
486
+ ] }),
487
+ /* @__PURE__ */ jsxs7("linearGradient", { id: `${gradientId}-shine`, x1: "0", x2: "1", y1: "0", y2: "0", children: [
488
+ /* @__PURE__ */ jsx8("stop", { offset: "0.4", stopColor: "white", stopOpacity: "0" }),
489
+ /* @__PURE__ */ jsx8("stop", { offset: "0.5", stopColor: "white", stopOpacity: "0.62" }),
490
+ /* @__PURE__ */ jsx8("stop", { offset: "0.6", stopColor: "white", stopOpacity: "0" })
491
+ ] }),
492
+ /* @__PURE__ */ jsxs7("clipPath", { id: clipId, children: [
493
+ /* @__PURE__ */ jsx8("path", { d: LEFT }),
494
+ /* @__PURE__ */ jsx8("path", { d: CENTER }),
495
+ /* @__PURE__ */ jsx8("path", { d: RIGHT })
496
+ ] })
497
+ ] }),
498
+ /* @__PURE__ */ jsx8("path", { d: CENTER, fill: `url(#${gradientId})` }),
499
+ /* @__PURE__ */ jsx8("path", { d: RIGHT, fill: `url(#${gradientId})` }),
500
+ /* @__PURE__ */ jsx8("path", { d: LEFT, fill: `url(#${gradientId})` }),
501
+ /* @__PURE__ */ jsx8("g", { clipPath: `url(#${clipId})`, children: /* @__PURE__ */ jsx8("g", { className: "fractal-ui-loader__shine-axis", transform: "translate(72 46) rotate(68.4)", children: /* @__PURE__ */ jsx8("rect", { className: "fractal-ui-loader__shine", x: "-160", y: "-100", width: "320", height: "200", fill: `url(#${gradientId}-shine)` }) }) })
502
+ ] })
503
+ }
504
+ );
505
+ }
506
+
437
507
  // src/components/Modal/Modal.tsx
438
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
439
- var CloseIcon = () => /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx7("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
440
- function Modal({ open, onClose, title, children, danger, warn, footer, noClose, size = "sm", className }) {
441
- useEscapeDismiss(80, onClose, open && !noClose);
508
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
509
+ var CloseIcon = () => /* @__PURE__ */ jsx9("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx9("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
510
+ function Modal({ open, onClose, title, children, danger, warn, footer, noClose, closeDisabled = false, size = "sm", loading = false, loadingLabel = "Loading", className }) {
511
+ useEscapeDismiss(80, onClose, open && !noClose && !closeDisabled);
442
512
  if (!open) return null;
443
513
  const sizeClass = `fractal-modal-panel--${size}`;
444
514
  const accentClass = danger ? "fractal-modal-panel--danger" : warn ? "fractal-modal-panel--warn" : "";
445
515
  return createPortal(
446
- /* @__PURE__ */ jsx7("div", { className: "fractal-modal-overlay", onClick: noClose ? void 0 : onClose, children: /* @__PURE__ */ jsxs6(
516
+ /* @__PURE__ */ jsx9("div", { className: "fractal-modal-overlay", onClick: noClose || closeDisabled ? void 0 : onClose, children: /* @__PURE__ */ jsxs8(
447
517
  "div",
448
518
  {
449
519
  className: cn("fractal-modal-panel", sizeClass, accentClass, className),
450
520
  onClick: (e) => e.stopPropagation(),
451
521
  children: [
452
- /* @__PURE__ */ jsxs6("div", { className: "fractal-modal-title-row", children: [
453
- /* @__PURE__ */ jsx7("h3", { className: cn("fractal-modal-title", danger && "fractal-modal-title--danger", warn && "fractal-modal-title--warn"), children: title }),
454
- !noClose && /* @__PURE__ */ jsx7("button", { type: "button", onClick: onClose, title: "Close", className: "fractal-modal-close", children: /* @__PURE__ */ jsx7(CloseIcon, {}) })
522
+ /* @__PURE__ */ jsxs8("div", { className: "fractal-modal-title-row", children: [
523
+ /* @__PURE__ */ jsx9("h3", { className: cn("fractal-modal-title", danger && "fractal-modal-title--danger", warn && "fractal-modal-title--warn"), children: title }),
524
+ !noClose && /* @__PURE__ */ jsx9("button", { type: "button", onClick: onClose, title: "Close", disabled: closeDisabled, className: "fractal-modal-close", children: /* @__PURE__ */ jsx9(CloseIcon, {}) })
455
525
  ] }),
456
- /* @__PURE__ */ jsx7("div", { className: cn("fractal-modal-body", size === "lg" && "fractal-modal-body--lg"), children }),
457
- footer && /* @__PURE__ */ jsx7("div", { className: "fractal-modal-footer", children: footer })
526
+ /* @__PURE__ */ jsx9("div", { "aria-busy": loading || void 0, className: cn("fractal-modal-body", size === "lg" && "fractal-modal-body--lg", loading && "fractal-modal-body--loading"), children: loading ? /* @__PURE__ */ jsx9(UILoader, { label: loadingLabel }) : children }),
527
+ footer && /* @__PURE__ */ jsx9("div", { className: "fractal-modal-footer", children: footer })
458
528
  ]
459
529
  }
460
530
  ) }),
@@ -463,17 +533,17 @@ function Modal({ open, onClose, title, children, danger, warn, footer, noClose,
463
533
  }
464
534
 
465
535
  // src/components/DataTable/DataTable.tsx
466
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
536
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
467
537
  function DataTable({ columns, data, emptyMessage, header, size = "md", className }) {
468
- return /* @__PURE__ */ jsx8("div", { className: cn("fractal-datatable", `fractal-datatable--${size}`, className), children: /* @__PURE__ */ jsxs7("div", { className: "fractal-datatable-surface", children: [
469
- header && /* @__PURE__ */ jsx8("div", { className: "fractal-datatable-header", children: header }),
470
- data.length === 0 ? /* @__PURE__ */ jsx8("div", { className: "fractal-datatable-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs7("table", { className: "fractal-datatable-table", children: [
471
- /* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsx8("tr", { className: "fractal-datatable-thead-row", children: columns.map((col) => /* @__PURE__ */ jsx8("th", { className: "fractal-datatable-th", style: { width: col.width }, children: col.header }, col.key)) }) }),
472
- /* @__PURE__ */ jsx8("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ jsx8(
538
+ return /* @__PURE__ */ jsx10("div", { className: cn("fractal-datatable", `fractal-datatable--${size}`, className), children: /* @__PURE__ */ jsxs9("div", { className: "fractal-datatable-surface", children: [
539
+ header && /* @__PURE__ */ jsx10("div", { className: "fractal-datatable-header", children: header }),
540
+ data.length === 0 ? /* @__PURE__ */ jsx10("div", { className: "fractal-datatable-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs9("table", { className: "fractal-datatable-table", children: [
541
+ /* @__PURE__ */ jsx10("thead", { children: /* @__PURE__ */ jsx10("tr", { className: "fractal-datatable-thead-row", children: columns.map((col) => /* @__PURE__ */ jsx10("th", { className: "fractal-datatable-th", style: { width: col.width }, children: col.header }, col.key)) }) }),
542
+ /* @__PURE__ */ jsx10("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ jsx10(
473
543
  "tr",
474
544
  {
475
545
  className: "fractal-datatable-row",
476
- children: columns.map((col) => /* @__PURE__ */ jsx8("td", { className: "fractal-datatable-td", style: { width: col.width }, children: col.render(row, rowIndex) }, col.key))
546
+ children: columns.map((col) => /* @__PURE__ */ jsx10("td", { className: "fractal-datatable-td", style: { width: col.width }, children: col.render(row, rowIndex) }, col.key))
477
547
  },
478
548
  rowIndex
479
549
  )) })
@@ -484,7 +554,7 @@ function DataTable({ columns, data, emptyMessage, header, size = "md", className
484
554
  // src/components/Dropdown/Dropdown.tsx
485
555
  import * as React7 from "react";
486
556
  import { ChevronDown, ChevronUp } from "@mirror-physics/fractal-icons";
487
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
557
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
488
558
  var MARGIN = 4;
489
559
  function Dropdown({
490
560
  items,
@@ -495,13 +565,15 @@ function Dropdown({
495
565
  align = "left",
496
566
  triggerContent,
497
567
  label,
568
+ iconOnly = false,
498
569
  noChevron = false,
499
570
  size = "md",
500
571
  triggerVariant = "outline",
501
572
  className,
502
573
  triggerClassName,
503
574
  panelClassName,
504
- closeOnSelect = true
575
+ closeOnSelect = true,
576
+ disabled = false
505
577
  }) {
506
578
  const [open, setOpen] = React7.useState(false);
507
579
  const triggerRef = React7.useRef(null);
@@ -559,12 +631,13 @@ function Dropdown({
559
631
  document.addEventListener("mousedown", handleClickOutside);
560
632
  return () => document.removeEventListener("mousedown", handleClickOutside);
561
633
  }, [open]);
562
- return /* @__PURE__ */ jsxs8("div", { className: cn("fractal-dropdown", className), children: [
563
- /* @__PURE__ */ jsxs8(
634
+ return /* @__PURE__ */ jsxs10("div", { className: cn("fractal-dropdown", className), children: [
635
+ /* @__PURE__ */ jsxs10(
564
636
  "button",
565
637
  {
566
638
  ref: triggerRef,
567
639
  type: "button",
640
+ disabled,
568
641
  "aria-haspopup": "listbox",
569
642
  "aria-expanded": open,
570
643
  "aria-label": triggerLabel,
@@ -573,18 +646,19 @@ function Dropdown({
573
646
  "fractal-dropdown-trigger",
574
647
  `fractal-dropdown-trigger--${size}`,
575
648
  `fractal-dropdown-trigger--${triggerVariant}`,
649
+ iconOnly && "fractal-dropdown-trigger--icon-only",
576
650
  triggerClassName
577
651
  ),
578
652
  children: [
579
- triggerContent != null ? triggerContent : /* @__PURE__ */ jsxs8(Fragment2, { children: [
580
- selected.icon != null && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected.icon }),
581
- selected.description != null && (typeof selected.description !== "string" || selected.description !== "") && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-selected-text", children: selected.description })
653
+ triggerContent != null ? triggerContent : /* @__PURE__ */ jsxs10(Fragment2, { children: [
654
+ selected.icon != null && /* @__PURE__ */ jsx11("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected.icon }),
655
+ selected.description != null && (typeof selected.description !== "string" || selected.description !== "") && /* @__PURE__ */ jsx11("span", { className: "fractal-dropdown-selected-text", children: selected.description })
582
656
  ] }),
583
- !noChevron && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-chevron", "aria-hidden": true, children: /* @__PURE__ */ jsx9(ChevronIcon, { size: size === "sm" ? 14 : 16 }) })
657
+ !noChevron && !iconOnly && /* @__PURE__ */ jsx11("span", { className: "fractal-dropdown-chevron", "aria-hidden": true, children: /* @__PURE__ */ jsx11(ChevronIcon, { size: size === "sm" ? 14 : 16 }) })
584
658
  ]
585
659
  }
586
660
  ),
587
- open && /* @__PURE__ */ jsxs8(
661
+ open && /* @__PURE__ */ jsxs10(
588
662
  "div",
589
663
  {
590
664
  ref: panelRef,
@@ -593,21 +667,21 @@ function Dropdown({
593
667
  className: cn("fractal-dropdown-panel", `fractal-dropdown-panel--${size}`, panelClassName),
594
668
  style: panelStyle,
595
669
  children: [
596
- label != null && /* @__PURE__ */ jsx9("div", { className: "fractal-dropdown-label", children: label }),
670
+ label != null && /* @__PURE__ */ jsx11("div", { className: "fractal-dropdown-label", children: label }),
597
671
  visibleItems.map((item, index) => {
598
- const topDivider = item.border === "top" ? /* @__PURE__ */ jsx9("div", { className: "fractal-dropdown-separator" }) : null;
599
- const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ jsx9("div", { className: "fractal-dropdown-separator" }) : null;
672
+ const topDivider = item.border === "top" ? /* @__PURE__ */ jsx11("div", { className: "fractal-dropdown-separator" }) : null;
673
+ const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ jsx11("div", { className: "fractal-dropdown-separator" }) : null;
600
674
  if (item.kind === "header") {
601
- return /* @__PURE__ */ jsxs8(React7.Fragment, { children: [
675
+ return /* @__PURE__ */ jsxs10(React7.Fragment, { children: [
602
676
  topDivider,
603
- /* @__PURE__ */ jsx9("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
677
+ /* @__PURE__ */ jsx11("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
604
678
  bottomDivider
605
679
  ] }, `header-${index}`);
606
680
  }
607
681
  const isSelected = item.value === selectedValue;
608
- return /* @__PURE__ */ jsxs8(React7.Fragment, { children: [
682
+ return /* @__PURE__ */ jsxs10(React7.Fragment, { children: [
609
683
  topDivider,
610
- /* @__PURE__ */ jsxs8(
684
+ /* @__PURE__ */ jsxs10(
611
685
  "button",
612
686
  {
613
687
  type: "button",
@@ -619,9 +693,9 @@ function Dropdown({
619
693
  },
620
694
  className: "fractal-dropdown-option",
621
695
  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 })
696
+ item.icon != null && /* @__PURE__ */ jsx11("span", { className: "fractal-dropdown-icon", children: item.icon }),
697
+ item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ jsx11("span", { className: "fractal-dropdown-option-text", children: item.description }),
698
+ item.trailing != null && /* @__PURE__ */ jsx11("span", { className: "fractal-dropdown-trailing", children: item.trailing })
625
699
  ]
626
700
  }
627
701
  ),
@@ -636,9 +710,9 @@ function Dropdown({
636
710
 
637
711
  // src/components/Link/Link.tsx
638
712
  import * as React8 from "react";
639
- import { jsx as jsx10 } from "react/jsx-runtime";
713
+ import { jsx as jsx12 } from "react/jsx-runtime";
640
714
  var Link = React8.forwardRef(
641
- ({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */ jsx10(
715
+ ({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */ jsx12(
642
716
  "a",
643
717
  {
644
718
  ref,
@@ -651,9 +725,9 @@ Link.displayName = "Link";
651
725
 
652
726
  // src/components/Chip/Chip.tsx
653
727
  import * as React9 from "react";
654
- import { jsx as jsx11 } from "react/jsx-runtime";
728
+ import { jsx as jsx13 } from "react/jsx-runtime";
655
729
  var Chip = React9.forwardRef(
656
- ({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */ jsx11(
730
+ ({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */ jsx13(
657
731
  "span",
658
732
  {
659
733
  ref,
@@ -664,35 +738,69 @@ var Chip = React9.forwardRef(
664
738
  );
665
739
  Chip.displayName = "Chip";
666
740
 
741
+ // src/components/Checkbox/Checkbox.tsx
742
+ import { Checkbox as CheckboxIcon, CheckboxChecked } from "@mirror-physics/fractal-icons";
743
+ import { jsx as jsx14 } from "react/jsx-runtime";
744
+ function Checkbox({ checked, onCheckedChange, className, disabled, onClick, onKeyDown, ...props }) {
745
+ return /* @__PURE__ */ jsx14(
746
+ "button",
747
+ {
748
+ ...props,
749
+ className: cn("fractal-checkbox", checked && "fractal-checkbox--checked", className),
750
+ type: "button",
751
+ role: "checkbox",
752
+ "aria-checked": checked,
753
+ disabled,
754
+ onClick: (event) => {
755
+ onClick?.(event);
756
+ if (!event.defaultPrevented) onCheckedChange(!checked);
757
+ },
758
+ onKeyDown: (event) => {
759
+ onKeyDown?.(event);
760
+ if (event.key === " " || event.key === "Enter") event.stopPropagation();
761
+ },
762
+ children: checked ? /* @__PURE__ */ jsx14(CheckboxChecked, { "aria-hidden": "true", size: 18 }) : /* @__PURE__ */ jsx14(CheckboxIcon, { "aria-hidden": "true", size: 18 })
763
+ }
764
+ );
765
+ }
766
+
667
767
  // src/components/SortableTable/SortableTable.tsx
668
768
  import { useMemo, useState as useState3 } from "react";
669
769
  import { ArrowUp, ArrowDown, ArrowsUpDown } from "@mirror-physics/fractal-icons";
670
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
770
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
671
771
  function SortableTable({
672
772
  columns,
673
773
  data,
674
774
  emptyMessage,
675
775
  header,
676
776
  size = "md",
777
+ embedded = false,
677
778
  className,
779
+ defaultSort,
678
780
  defaultSortKey = void 0,
679
781
  defaultSortDirection = null,
680
782
  sortKey: controlledKey,
681
783
  sortDirection: controlledDirection,
682
784
  onSortChange,
683
- highlightRow
785
+ highlightRow,
786
+ onRowClick,
787
+ rowAction
684
788
  }) {
685
789
  const [internalKey, setInternalKey] = useState3(defaultSortKey ?? null);
686
790
  const [internalDirection, setInternalDirection] = useState3(defaultSortDirection);
687
791
  const isControlled = controlledKey !== void 0 && controlledDirection !== void 0;
688
792
  const activeKey = isControlled ? controlledKey : internalKey;
689
793
  const activeDirection = isControlled ? controlledDirection : internalDirection;
794
+ const baselineKey = defaultSort?.key ?? null;
795
+ const baselineDirection = defaultSort?.direction ?? null;
796
+ const effectiveKey = activeKey && activeDirection ? activeKey : baselineKey;
797
+ const effectiveDirection = activeKey && activeDirection ? activeDirection : baselineDirection;
690
798
  const sorted = useMemo(() => {
691
- if (!activeKey || !activeDirection) return data;
692
- const col = columns.find((c) => c.key === activeKey);
799
+ if (!effectiveKey || !effectiveDirection) return data;
800
+ const col = columns.find((c) => c.key === effectiveKey);
693
801
  if (!col) return data;
694
802
  const accessor = col.sortAccessor ?? ((row) => {
695
- const value = row[activeKey];
803
+ const value = row[effectiveKey];
696
804
  return typeof value === "string" || typeof value === "number" ? value : null;
697
805
  });
698
806
  const copy = [...data];
@@ -703,26 +811,28 @@ function SortableTable({
703
811
  if (av == null) return 1;
704
812
  if (bv == null) return -1;
705
813
  if (typeof av === "number" && typeof bv === "number") {
706
- return activeDirection === "asc" ? av - bv : bv - av;
814
+ return effectiveDirection === "asc" ? av - bv : bv - av;
707
815
  }
708
816
  const as = String(av).toLowerCase();
709
817
  const bs = String(bv).toLowerCase();
710
- if (as < bs) return activeDirection === "asc" ? -1 : 1;
711
- if (as > bs) return activeDirection === "asc" ? 1 : -1;
818
+ if (as < bs) return effectiveDirection === "asc" ? -1 : 1;
819
+ if (as > bs) return effectiveDirection === "asc" ? 1 : -1;
712
820
  return 0;
713
821
  });
714
822
  return copy;
715
- }, [data, columns, activeKey, activeDirection]);
823
+ }, [data, columns, effectiveKey, effectiveDirection]);
716
824
  const handleSortClick = (key) => {
717
825
  let nextDirection;
718
- if (activeKey !== key) {
826
+ if (!activeKey || !activeDirection) {
827
+ nextDirection = baselineKey === key && baselineDirection ? baselineDirection : "asc";
828
+ } else if (activeKey !== key) {
719
829
  nextDirection = "asc";
830
+ } else if (baselineKey === key && baselineDirection) {
831
+ nextDirection = activeDirection === baselineDirection ? baselineDirection === "asc" ? "desc" : "asc" : null;
720
832
  } else if (activeDirection === "asc") {
721
833
  nextDirection = "desc";
722
- } else if (activeDirection === "desc") {
723
- nextDirection = null;
724
834
  } else {
725
- nextDirection = "asc";
835
+ nextDirection = null;
726
836
  }
727
837
  const nextKey = nextDirection === null ? null : key;
728
838
  if (isControlled) {
@@ -733,13 +843,13 @@ function SortableTable({
733
843
  onSortChange?.(nextKey, nextDirection);
734
844
  }
735
845
  };
736
- return /* @__PURE__ */ jsx12("div", { className: cn("fractal-sortable-table", `fractal-sortable-table--${size}`, className), children: /* @__PURE__ */ jsxs9("div", { className: "fractal-sortable-table-surface", children: [
737
- header && /* @__PURE__ */ jsx12("div", { className: "fractal-sortable-table-header", children: header }),
738
- sorted.length === 0 ? /* @__PURE__ */ jsx12("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs9("table", { className: "fractal-sortable-table-table", children: [
739
- /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsx12("tr", { className: "fractal-sortable-table-thead-row", children: columns.map((col) => {
846
+ return /* @__PURE__ */ jsx15("div", { className: cn("fractal-sortable-table", `fractal-sortable-table--${size}`, embedded && "fractal-sortable-table--embedded", className), children: /* @__PURE__ */ jsxs11("div", { className: "fractal-sortable-table-surface", children: [
847
+ header && /* @__PURE__ */ jsx15("div", { className: "fractal-sortable-table-header", children: header }),
848
+ sorted.length === 0 ? /* @__PURE__ */ jsx15("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs11("table", { className: "fractal-sortable-table-table", children: [
849
+ /* @__PURE__ */ jsx15("thead", { children: /* @__PURE__ */ jsx15("tr", { className: "fractal-sortable-table-thead-row", children: columns.map((col) => {
740
850
  const isActive = activeKey === col.key && activeDirection !== null;
741
851
  if (!col.sortable) {
742
- return /* @__PURE__ */ jsx12(
852
+ return /* @__PURE__ */ jsx15(
743
853
  "th",
744
854
  {
745
855
  className: "fractal-sortable-table-th",
@@ -749,12 +859,12 @@ function SortableTable({
749
859
  col.key
750
860
  );
751
861
  }
752
- return /* @__PURE__ */ jsx12(
862
+ return /* @__PURE__ */ jsx15(
753
863
  "th",
754
864
  {
755
865
  className: "fractal-sortable-table-th fractal-sortable-table-th--sortable",
756
866
  style: { width: col.width },
757
- children: /* @__PURE__ */ jsxs9(
867
+ children: /* @__PURE__ */ jsxs11(
758
868
  "button",
759
869
  {
760
870
  type: "button",
@@ -765,8 +875,8 @@ function SortableTable({
765
875
  onClick: () => handleSortClick(col.key),
766
876
  "aria-sort": isActive ? activeDirection === "asc" ? "ascending" : "descending" : "none",
767
877
  children: [
768
- /* @__PURE__ */ jsx12("span", { children: col.header }),
769
- /* @__PURE__ */ jsx12("span", { className: "fractal-sortable-table-sort-icon", "aria-hidden": "true", children: isActive && activeDirection === "asc" ? /* @__PURE__ */ jsx12(ArrowUp, { size: 14 }) : isActive && activeDirection === "desc" ? /* @__PURE__ */ jsx12(ArrowDown, { size: 14 }) : /* @__PURE__ */ jsx12(ArrowsUpDown, { size: 14 }) })
878
+ /* @__PURE__ */ jsx15("span", { children: col.header }),
879
+ /* @__PURE__ */ jsx15("span", { className: "fractal-sortable-table-sort-icon", "aria-hidden": "true", children: isActive && activeDirection === "asc" ? /* @__PURE__ */ jsx15(ArrowUp, { size: 14 }) : isActive && activeDirection === "desc" ? /* @__PURE__ */ jsx15(ArrowDown, { size: 14 }) : /* @__PURE__ */ jsx15(ArrowsUpDown, { size: 14 }) })
770
880
  ]
771
881
  }
772
882
  )
@@ -774,21 +884,42 @@ function SortableTable({
774
884
  col.key
775
885
  );
776
886
  }) }) }),
777
- /* @__PURE__ */ jsx12("tbody", { children: sorted.map((row, rowIndex) => {
887
+ /* @__PURE__ */ jsx15("tbody", { children: sorted.map((row, rowIndex) => {
778
888
  const highlighted = highlightRow?.(row, rowIndex) ?? false;
779
- return /* @__PURE__ */ jsx12(
889
+ const action = rowAction ? {
890
+ label: rowAction.label(row, rowIndex),
891
+ icon: rowAction.icon(row, rowIndex)
892
+ } : null;
893
+ return /* @__PURE__ */ jsx15(
780
894
  "tr",
781
895
  {
782
896
  className: cn(
783
897
  "fractal-sortable-table-row",
784
- highlighted && "fractal-sortable-table-row--highlighted"
898
+ highlighted && "fractal-sortable-table-row--highlighted",
899
+ onRowClick && "fractal-sortable-table-row--interactive"
785
900
  ),
786
- children: columns.map((col) => /* @__PURE__ */ jsx12(
901
+ onClick: onRowClick ? () => onRowClick(row, rowIndex) : void 0,
902
+ onKeyDown: onRowClick ? (event) => {
903
+ if (event.key === "Enter" || event.key === " ") {
904
+ event.preventDefault();
905
+ onRowClick(row, rowIndex);
906
+ }
907
+ } : void 0,
908
+ "aria-label": action?.label,
909
+ role: onRowClick ? "button" : void 0,
910
+ tabIndex: onRowClick ? 0 : void 0,
911
+ children: columns.map((col, columnIndex) => /* @__PURE__ */ jsxs11(
787
912
  "td",
788
913
  {
789
- className: "fractal-sortable-table-td",
914
+ className: cn(
915
+ "fractal-sortable-table-td",
916
+ action && columnIndex === columns.length - 1 && "fractal-sortable-table-td--row-action"
917
+ ),
790
918
  style: { width: col.width, textAlign: col.align ?? "left" },
791
- children: col.render(row, rowIndex)
919
+ children: [
920
+ col.render(row, rowIndex),
921
+ action && columnIndex === columns.length - 1 && /* @__PURE__ */ jsx15("span", { className: "fractal-sortable-table-row-action", "aria-hidden": "true", children: /* @__PURE__ */ jsx15("span", { className: "fractal-sortable-table-row-action__icon", children: action.icon }) })
922
+ ]
792
923
  },
793
924
  col.key
794
925
  ))
@@ -801,23 +932,40 @@ function SortableTable({
801
932
  }
802
933
 
803
934
  // src/components/Tabs/Tabs.tsx
804
- import { useId, useRef as useRef3, useState as useState4 } from "react";
805
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
935
+ import { useId as useId2, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
936
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
806
937
  function Tabs({
807
938
  items,
808
939
  defaultValue,
809
940
  value: controlledValue,
810
941
  onValueChange,
811
942
  className,
812
- ariaLabel
943
+ ariaLabel,
944
+ divider = false,
945
+ variant = "line"
813
946
  }) {
814
- const reactId = useId();
947
+ const reactId = useId2();
815
948
  const [internalValue, setInternalValue] = useState4(
816
949
  defaultValue ?? items.find((i) => !i.disabled)?.id ?? items[0]?.id ?? ""
817
950
  );
818
951
  const isControlled = controlledValue !== void 0;
819
952
  const activeId = isControlled ? controlledValue : internalValue;
953
+ const tabListRef = useRef3(null);
820
954
  const tabRefs = useRef3({});
955
+ const [indicator, setIndicator] = useState4(null);
956
+ useLayoutEffect2(() => {
957
+ const updateIndicator = () => {
958
+ const activeTab = tabRefs.current[activeId];
959
+ if (!activeTab) return;
960
+ setIndicator({ offset: activeTab.offsetLeft, width: activeTab.offsetWidth });
961
+ };
962
+ updateIndicator();
963
+ const list = tabListRef.current;
964
+ if (!list || typeof ResizeObserver === "undefined") return;
965
+ const observer = new ResizeObserver(updateIndicator);
966
+ observer.observe(list);
967
+ return () => observer.disconnect();
968
+ }, [activeId, items.length]);
821
969
  const selectTab = (id) => {
822
970
  if (isControlled) {
823
971
  onValueChange?.(id);
@@ -827,6 +975,10 @@ function Tabs({
827
975
  }
828
976
  };
829
977
  const focusable = items.filter((i) => !i.disabled);
978
+ const indicatorStyle = indicator ? {
979
+ "--fractal-tabs-indicator-offset": `${indicator.offset}px`,
980
+ "--fractal-tabs-indicator-width": `${indicator.width}px`
981
+ } : void 0;
830
982
  const onKeyDown = (event) => {
831
983
  const currentIndex = focusable.findIndex((i) => i.id === activeId);
832
984
  if (currentIndex === -1) return;
@@ -852,41 +1004,52 @@ function Tabs({
852
1004
  selectTab(nextId2);
853
1005
  requestAnimationFrame(() => tabRefs.current[nextId2]?.focus());
854
1006
  };
855
- return /* @__PURE__ */ jsxs10("div", { className: cn("fractal-tabs", className), children: [
856
- /* @__PURE__ */ jsx13("div", { role: "tablist", "aria-label": ariaLabel, className: "fractal-tabs-list", children: items.map((item) => {
857
- const isActive = item.id === activeId;
858
- const tabId = `${reactId}-tab-${item.id}`;
859
- const panelId = `${reactId}-panel-${item.id}`;
860
- return /* @__PURE__ */ jsx13(
861
- "button",
862
- {
863
- ref: (el) => {
864
- tabRefs.current[item.id] = el;
865
- },
866
- id: tabId,
867
- role: "tab",
868
- type: "button",
869
- "aria-selected": isActive,
870
- "aria-controls": panelId,
871
- tabIndex: isActive ? 0 : -1,
872
- disabled: item.disabled,
873
- className: cn(
874
- "fractal-tabs-trigger",
875
- isActive && "fractal-tabs-trigger--active",
876
- item.disabled && "fractal-tabs-trigger--disabled"
877
- ),
878
- onClick: () => !item.disabled && selectTab(item.id),
879
- onKeyDown,
880
- children: item.label
881
- },
882
- item.id
883
- );
884
- }) }),
1007
+ return /* @__PURE__ */ jsxs12("div", { className: cn("fractal-tabs", `fractal-tabs--${variant}`, divider && "fractal-tabs--divider", className), children: [
1008
+ /* @__PURE__ */ jsx16(
1009
+ "div",
1010
+ {
1011
+ ref: tabListRef,
1012
+ role: "tablist",
1013
+ "aria-label": ariaLabel,
1014
+ className: "fractal-tabs-list",
1015
+ "data-indicator-ready": indicator ? "true" : void 0,
1016
+ style: indicatorStyle,
1017
+ children: items.map((item) => {
1018
+ const isActive = item.id === activeId;
1019
+ const tabId = `${reactId}-tab-${item.id}`;
1020
+ const panelId = `${reactId}-panel-${item.id}`;
1021
+ return /* @__PURE__ */ jsx16(
1022
+ "button",
1023
+ {
1024
+ ref: (el) => {
1025
+ tabRefs.current[item.id] = el;
1026
+ },
1027
+ id: tabId,
1028
+ role: "tab",
1029
+ type: "button",
1030
+ "aria-selected": isActive,
1031
+ "aria-controls": panelId,
1032
+ tabIndex: isActive ? 0 : -1,
1033
+ disabled: item.disabled,
1034
+ className: cn(
1035
+ "fractal-tabs-trigger",
1036
+ isActive && "fractal-tabs-trigger--active",
1037
+ item.disabled && "fractal-tabs-trigger--disabled"
1038
+ ),
1039
+ onClick: () => !item.disabled && selectTab(item.id),
1040
+ onKeyDown,
1041
+ children: item.label
1042
+ },
1043
+ item.id
1044
+ );
1045
+ })
1046
+ }
1047
+ ),
885
1048
  items.map((item) => {
886
1049
  const tabId = `${reactId}-tab-${item.id}`;
887
1050
  const panelId = `${reactId}-panel-${item.id}`;
888
1051
  const isActive = item.id === activeId;
889
- return /* @__PURE__ */ jsx13(
1052
+ return /* @__PURE__ */ jsx16(
890
1053
  "div",
891
1054
  {
892
1055
  id: panelId,
@@ -902,10 +1065,112 @@ function Tabs({
902
1065
  ] });
903
1066
  }
904
1067
 
1068
+ // src/components/ContentSwitcher/ContentSwitcher.tsx
1069
+ import * as React10 from "react";
1070
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
1071
+ function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = false, className }) {
1072
+ const switcherRef = React10.useRef(null);
1073
+ const buttonRefs = React10.useRef([]);
1074
+ const indicatorMotionReadyRef = React10.useRef(false);
1075
+ const activeIndex = items.findIndex((item) => item.value === value);
1076
+ React10.useLayoutEffect(() => {
1077
+ const switcher = switcherRef.current;
1078
+ const activeButton = buttonRefs.current[activeIndex];
1079
+ let motionFrame = 0;
1080
+ if (!switcher || !activeButton) {
1081
+ switcher?.removeAttribute("data-indicator-ready");
1082
+ switcher?.removeAttribute("data-indicator-animate");
1083
+ indicatorMotionReadyRef.current = false;
1084
+ return;
1085
+ }
1086
+ const updateIndicator = () => {
1087
+ const switcherRect = switcher.getBoundingClientRect();
1088
+ const activeButtonRect = activeButton.getBoundingClientRect();
1089
+ switcher.style.setProperty("--fractal-content-switcher-indicator-x", `${activeButtonRect.left - switcherRect.left - switcher.clientLeft}px`);
1090
+ switcher.style.setProperty("--fractal-content-switcher-indicator-y", `${activeButtonRect.top - switcherRect.top - switcher.clientTop}px`);
1091
+ switcher.style.setProperty("--fractal-content-switcher-indicator-width", `${activeButtonRect.width}px`);
1092
+ switcher.style.setProperty("--fractal-content-switcher-indicator-height", `${activeButtonRect.height}px`);
1093
+ switcher.setAttribute("data-indicator-ready", "true");
1094
+ if (!indicatorMotionReadyRef.current && motionFrame === 0) {
1095
+ motionFrame = window.requestAnimationFrame(() => {
1096
+ indicatorMotionReadyRef.current = true;
1097
+ switcher.setAttribute("data-indicator-animate", "true");
1098
+ motionFrame = 0;
1099
+ });
1100
+ }
1101
+ };
1102
+ updateIndicator();
1103
+ if (typeof ResizeObserver === "undefined") {
1104
+ return () => {
1105
+ if (motionFrame !== 0) window.cancelAnimationFrame(motionFrame);
1106
+ };
1107
+ }
1108
+ const resizeObserver = new ResizeObserver(updateIndicator);
1109
+ resizeObserver.observe(switcher);
1110
+ buttonRefs.current.forEach((button) => {
1111
+ if (button) resizeObserver.observe(button);
1112
+ });
1113
+ return () => {
1114
+ resizeObserver.disconnect();
1115
+ if (motionFrame !== 0) window.cancelAnimationFrame(motionFrame);
1116
+ };
1117
+ }, [activeIndex, items.length]);
1118
+ const moveFocus = (index, direction) => {
1119
+ let nextIndex = index;
1120
+ do {
1121
+ nextIndex = (nextIndex + direction + items.length) % items.length;
1122
+ } while (items[nextIndex]?.disabled && nextIndex !== index);
1123
+ if (!items[nextIndex]?.disabled) {
1124
+ buttonRefs.current[nextIndex]?.focus();
1125
+ onValueChange(items[nextIndex].value);
1126
+ }
1127
+ };
1128
+ return /* @__PURE__ */ jsxs13(
1129
+ "div",
1130
+ {
1131
+ className: cn("fractal-content-switcher", fullWidth && "fractal-content-switcher--full-width", className),
1132
+ role: "group",
1133
+ "aria-label": ariaLabel,
1134
+ ref: switcherRef,
1135
+ children: [
1136
+ /* @__PURE__ */ jsx17("span", { className: "fractal-content-switcher__indicator", "aria-hidden": true }),
1137
+ items.map((item, index) => {
1138
+ const active = item.value === value;
1139
+ return /* @__PURE__ */ jsx17(
1140
+ "button",
1141
+ {
1142
+ className: cn("fractal-content-switcher__item", active && "fractal-content-switcher__item--active"),
1143
+ type: "button",
1144
+ "aria-pressed": active,
1145
+ disabled: item.disabled,
1146
+ onClick: () => onValueChange(item.value),
1147
+ onKeyDown: (event) => {
1148
+ if (event.key === "ArrowRight") {
1149
+ event.preventDefault();
1150
+ moveFocus(index, 1);
1151
+ }
1152
+ if (event.key === "ArrowLeft") {
1153
+ event.preventDefault();
1154
+ moveFocus(index, -1);
1155
+ }
1156
+ },
1157
+ ref: (element) => {
1158
+ buttonRefs.current[index] = element;
1159
+ },
1160
+ children: item.label
1161
+ },
1162
+ item.value
1163
+ );
1164
+ })
1165
+ ]
1166
+ }
1167
+ );
1168
+ }
1169
+
905
1170
  // src/components/Disclosure/Disclosure.tsx
906
- import { useId as useId2, useState as useState5 } from "react";
907
- import { ChevronRight, ChevronDown as ChevronDown2 } from "@mirror-physics/fractal-icons";
908
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1171
+ import { useId as useId3, useState as useState5 } from "react";
1172
+ import { ChevronRight as ChevronRight2, ChevronDown as ChevronDown2 } from "@mirror-physics/fractal-icons";
1173
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
909
1174
  function Disclosure({
910
1175
  title,
911
1176
  meta,
@@ -913,16 +1178,18 @@ function Disclosure({
913
1178
  defaultOpen = false,
914
1179
  open: controlledOpen,
915
1180
  onOpenChange,
1181
+ disabled = false,
916
1182
  className,
917
1183
  variant = "card"
918
1184
  }) {
919
- const reactId = useId2();
1185
+ const reactId = useId3();
920
1186
  const headerId = `${reactId}-header`;
921
1187
  const panelId = `${reactId}-panel`;
922
1188
  const [internalOpen, setInternalOpen] = useState5(defaultOpen);
923
1189
  const isControlled = controlledOpen !== void 0;
924
1190
  const isOpen = isControlled ? controlledOpen : internalOpen;
925
1191
  const toggle = () => {
1192
+ if (disabled) return;
926
1193
  const next = !isOpen;
927
1194
  if (isControlled) {
928
1195
  onOpenChange?.(next);
@@ -931,17 +1198,18 @@ function Disclosure({
931
1198
  onOpenChange?.(next);
932
1199
  }
933
1200
  };
934
- return /* @__PURE__ */ jsxs11(
1201
+ return /* @__PURE__ */ jsxs14(
935
1202
  "div",
936
1203
  {
937
1204
  className: cn(
938
1205
  "fractal-disclosure",
939
1206
  variant === "card" && "fractal-disclosure--card",
940
1207
  isOpen && "fractal-disclosure--open",
1208
+ disabled && "fractal-disclosure--disabled",
941
1209
  className
942
1210
  ),
943
1211
  children: [
944
- /* @__PURE__ */ jsxs11(
1212
+ /* @__PURE__ */ jsxs14(
945
1213
  "button",
946
1214
  {
947
1215
  type: "button",
@@ -949,15 +1217,16 @@ function Disclosure({
949
1217
  className: "fractal-disclosure-trigger",
950
1218
  "aria-expanded": isOpen,
951
1219
  "aria-controls": panelId,
1220
+ disabled,
952
1221
  onClick: toggle,
953
1222
  children: [
954
- /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ jsx14(ChevronDown2, { size: 16 }) : /* @__PURE__ */ jsx14(ChevronRight, { size: 16 }) }),
955
- /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-title", children: title }),
956
- meta && /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-meta", children: meta })
1223
+ /* @__PURE__ */ jsx18("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ jsx18(ChevronDown2, { size: 16 }) : /* @__PURE__ */ jsx18(ChevronRight2, { size: 16 }) }),
1224
+ /* @__PURE__ */ jsx18("span", { className: "fractal-disclosure-title", children: title }),
1225
+ meta && /* @__PURE__ */ jsx18("span", { className: "fractal-disclosure-meta", children: meta })
957
1226
  ]
958
1227
  }
959
1228
  ),
960
- /* @__PURE__ */ jsx14(
1229
+ /* @__PURE__ */ jsx18(
961
1230
  "div",
962
1231
  {
963
1232
  id: panelId,
@@ -965,7 +1234,7 @@ function Disclosure({
965
1234
  "aria-labelledby": headerId,
966
1235
  className: "fractal-disclosure-panel",
967
1236
  hidden: !isOpen,
968
- children: /* @__PURE__ */ jsx14("div", { className: "fractal-disclosure-panel-inner", children })
1237
+ children: /* @__PURE__ */ jsx18("div", { className: "fractal-disclosure-panel-inner", children })
969
1238
  }
970
1239
  )
971
1240
  ]
@@ -973,39 +1242,232 @@ function Disclosure({
973
1242
  );
974
1243
  }
975
1244
 
1245
+ // src/components/LatticeLoader/LatticeLoader.tsx
1246
+ import { jsx as jsx19 } from "react/jsx-runtime";
1247
+ var LATTICE_COUNT = 160;
1248
+ var WAVE_PERIOD = 80;
1249
+ function LatticeLoader({ className, label = "Loading", ...props }) {
1250
+ return /* @__PURE__ */ jsx19(
1251
+ "span",
1252
+ {
1253
+ "aria-label": label,
1254
+ className: cn("fractal-lattice-loader", className),
1255
+ role: "progressbar",
1256
+ ...props,
1257
+ children: Array.from({ length: LATTICE_COUNT }, (_, index) => /* @__PURE__ */ jsx19(
1258
+ "span",
1259
+ {
1260
+ "aria-hidden": "true",
1261
+ className: "fractal-lattice-loader__cell",
1262
+ style: { "--fractal-lattice-index": index % WAVE_PERIOD }
1263
+ },
1264
+ index
1265
+ ))
1266
+ }
1267
+ );
1268
+ }
1269
+
1270
+ // src/components/Ghost/Ghost.tsx
1271
+ import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
1272
+ var toCssLength = (value) => typeof value === "number" ? `${value}px` : value;
1273
+ function Ghost({ className, width, height, radius, style, ...props }) {
1274
+ return /* @__PURE__ */ jsx20(
1275
+ "div",
1276
+ {
1277
+ "aria-hidden": "true",
1278
+ className: cn("fractal-ghost", className),
1279
+ style: {
1280
+ width: toCssLength(width),
1281
+ height: toCssLength(height),
1282
+ borderRadius: toCssLength(radius),
1283
+ ...style
1284
+ },
1285
+ ...props
1286
+ }
1287
+ );
1288
+ }
1289
+ function GhostLine({ className, size = "md", width = "100%", ...props }) {
1290
+ return /* @__PURE__ */ jsx20(Ghost, { className: cn("fractal-ghost-line", `fractal-ghost-line--${size}`, className), width, ...props });
1291
+ }
1292
+ function ButtonGhost({ className, size = "md", iconOnly = false, width, ...props }) {
1293
+ return /* @__PURE__ */ jsx20(Ghost, { className: cn("fractal-ghost-button", `fractal-ghost-button--${size}`, iconOnly && "fractal-ghost-button--icon-only", className), width, ...props });
1294
+ }
1295
+ function CardGhost({ className, lines = 3, showHeader = true, ...props }) {
1296
+ return /* @__PURE__ */ jsxs15("div", { "aria-hidden": "true", className: cn("fractal-ghost-card", className), ...props, children: [
1297
+ showHeader && /* @__PURE__ */ jsx20(GhostLine, { width: "38%" }),
1298
+ /* @__PURE__ */ jsx20("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx20(GhostLine, { width: index === lines - 1 ? "62%" : "100%" }, index)) })
1299
+ ] });
1300
+ }
1301
+ function DataTableGhost({
1302
+ className,
1303
+ showHeader = true,
1304
+ headers,
1305
+ columns = 4,
1306
+ rows = 5,
1307
+ columnWidths,
1308
+ size = "md",
1309
+ ...props
1310
+ }) {
1311
+ const cells = Array.from({ length: columns }, (_, index) => index);
1312
+ return /* @__PURE__ */ jsx20("div", { "aria-busy": "true", "aria-label": "Loading table", className: cn("fractal-ghost-table", `fractal-ghost-table--${size}`, className), role: "status", ...props, children: /* @__PURE__ */ jsxs15("table", { className: "fractal-ghost-table__table", children: [
1313
+ (showHeader || headers) && /* @__PURE__ */ jsx20("thead", { children: /* @__PURE__ */ jsx20("tr", { className: "fractal-ghost-table__head-row", children: cells.map((index) => /* @__PURE__ */ jsx20("th", { style: { width: columnWidths?.[index] }, children: headers?.[index] ?? /* @__PURE__ */ jsx20(GhostLine, { size: "sm", width: index === columns - 1 ? "62%" : "76%" }) }, index)) }) }),
1314
+ /* @__PURE__ */ jsx20("tbody", { children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ jsx20("tr", { className: "fractal-ghost-table__row", children: cells.map((columnIndex) => /* @__PURE__ */ jsx20("td", { style: { width: columnWidths?.[columnIndex] }, children: /* @__PURE__ */ jsx20(GhostLine, { width: `${[72, 54, 82, 63, 46][(rowIndex + columnIndex) % 5]}%` }) }, columnIndex)) }, rowIndex)) })
1315
+ ] }) });
1316
+ }
1317
+ function InputGhost({ className, labelWidth = "26%", ...props }) {
1318
+ return /* @__PURE__ */ jsx20(FormFieldGhost, { className, labelWidth, ...props });
1319
+ }
1320
+ function TextareaGhost({ className, labelWidth = "26%", ...props }) {
1321
+ return /* @__PURE__ */ jsx20(FormFieldGhost, { className, labelWidth, multiline: true, ...props });
1322
+ }
1323
+ function LabelGhost({ className, width = "26%", ...props }) {
1324
+ return /* @__PURE__ */ jsx20(GhostLine, { className: cn("fractal-ghost-label", className), size: "sm", width, ...props });
1325
+ }
1326
+ function FormFieldGhost({ className, labelWidth = "26%", multiline = false, ...props }) {
1327
+ return /* @__PURE__ */ jsxs15("div", { "aria-hidden": "true", className: cn("fractal-ghost-field", className), ...props, children: [
1328
+ /* @__PURE__ */ jsx20(LabelGhost, { width: labelWidth }),
1329
+ /* @__PURE__ */ jsx20(Ghost, { className: cn("fractal-ghost-field__control", multiline && "fractal-ghost-field__control--multiline") })
1330
+ ] });
1331
+ }
1332
+ function AlertGhost({ className, ...props }) {
1333
+ return /* @__PURE__ */ jsxs15("div", { "aria-hidden": "true", className: cn("fractal-ghost-alert", className), ...props, children: [
1334
+ /* @__PURE__ */ jsx20(GhostLine, { width: "30%" }),
1335
+ /* @__PURE__ */ jsx20(GhostLine, { size: "sm", width: "78%" })
1336
+ ] });
1337
+ }
1338
+ function CheckboxGhost({ className, ...props }) {
1339
+ return /* @__PURE__ */ jsxs15("div", { "aria-hidden": "true", className: cn("fractal-ghost-choice", className), ...props, children: [
1340
+ /* @__PURE__ */ jsx20(Ghost, { className: "fractal-ghost-choice__control" }),
1341
+ /* @__PURE__ */ jsx20(GhostLine, { width: "42%" })
1342
+ ] });
1343
+ }
1344
+ var ToggleGhost = CheckboxGhost;
1345
+ function ChipGhost({ className, width = 74, ...props }) {
1346
+ return /* @__PURE__ */ jsx20(Ghost, { className: cn("fractal-ghost-chip", className), height: 24, width, ...props });
1347
+ }
1348
+ function LinkGhost({ className, width = 96, ...props }) {
1349
+ return /* @__PURE__ */ jsx20(GhostLine, { className: cn("fractal-ghost-link", className), size: "sm", width, ...props });
1350
+ }
1351
+ var TextButtonGhost = LinkGhost;
1352
+ function DropdownGhost({ className, ...props }) {
1353
+ return /* @__PURE__ */ jsxs15("div", { "aria-hidden": "true", className: cn("fractal-ghost-dropdown", className), ...props, children: [
1354
+ /* @__PURE__ */ jsx20(GhostLine, { width: "38%" }),
1355
+ /* @__PURE__ */ jsx20(Ghost, { className: "fractal-ghost-dropdown__chevron" })
1356
+ ] });
1357
+ }
1358
+ function ContentSwitcherGhost({ className, options = 3, ...props }) {
1359
+ return /* @__PURE__ */ jsx20("div", { "aria-hidden": "true", className: cn("fractal-ghost-switcher", className), ...props, children: Array.from({ length: options }, (_, index) => /* @__PURE__ */ jsx20(Ghost, {}, index)) });
1360
+ }
1361
+ var TabsGhost = ContentSwitcherGhost;
1362
+ function DisclosureGhost({ className, ...props }) {
1363
+ return /* @__PURE__ */ jsxs15("div", { "aria-hidden": "true", className: cn("fractal-ghost-disclosure", className), ...props, children: [
1364
+ /* @__PURE__ */ jsx20(Ghost, { className: "fractal-ghost-disclosure__icon" }),
1365
+ /* @__PURE__ */ jsx20(GhostLine, { width: "42%" })
1366
+ ] });
1367
+ }
1368
+ function PaginationGhost({ className, pages = 5, ...props }) {
1369
+ return /* @__PURE__ */ jsx20("div", { "aria-hidden": "true", className: cn("fractal-ghost-pagination", className), ...props, children: Array.from({ length: pages }, (_, index) => /* @__PURE__ */ jsx20(Ghost, {}, index)) });
1370
+ }
1371
+ function FileDropzoneGhost({ className, ...props }) {
1372
+ return /* @__PURE__ */ jsxs15("div", { "aria-hidden": "true", className: cn("fractal-ghost-dropzone", className), ...props, children: [
1373
+ /* @__PURE__ */ jsx20(Ghost, { className: "fractal-ghost-dropzone__icon" }),
1374
+ /* @__PURE__ */ jsx20(GhostLine, { width: "26%" }),
1375
+ /* @__PURE__ */ jsx20(GhostLine, { size: "sm", width: "46%" })
1376
+ ] });
1377
+ }
1378
+ function PromptCardGhost({ className, ...props }) {
1379
+ return /* @__PURE__ */ jsx20(CardGhost, { className: cn("fractal-ghost-prompt-card", className), lines: 4, ...props });
1380
+ }
1381
+ function SortableTableGhost(props) {
1382
+ return /* @__PURE__ */ jsx20(DataTableGhost, { ...props });
1383
+ }
1384
+ function SidebarGhost({ className, items = 6, ...props }) {
1385
+ return /* @__PURE__ */ jsxs15("aside", { "aria-hidden": "true", className: cn("fractal-ghost-sidebar", className), ...props, children: [
1386
+ /* @__PURE__ */ jsx20(Ghost, { className: "fractal-ghost-sidebar__brand" }),
1387
+ /* @__PURE__ */ jsx20("div", { className: "fractal-ghost-sidebar__items", children: Array.from({ length: items }, (_, index) => /* @__PURE__ */ jsxs15("div", { className: "fractal-ghost-sidebar__item", children: [
1388
+ /* @__PURE__ */ jsx20(Ghost, {}),
1389
+ /* @__PURE__ */ jsx20(GhostLine, { width: `${[54, 68, 44, 61][index % 4]}%` })
1390
+ ] }, index)) })
1391
+ ] });
1392
+ }
1393
+ function ModalGhost({ className, title, lines = 3, ...props }) {
1394
+ return /* @__PURE__ */ jsx20(SurfaceGhost, { className: cn("fractal-ghost-modal", className), title, lines, ...props });
1395
+ }
1396
+ function SidePanelGhost({ className, title, lines = 5, ...props }) {
1397
+ return /* @__PURE__ */ jsx20(SurfaceGhost, { className: cn("fractal-ghost-side-panel", className), title, lines, ...props });
1398
+ }
1399
+ function SurfaceGhost({ className, title, lines = 3, ...props }) {
1400
+ return /* @__PURE__ */ jsxs15("div", { "aria-hidden": "true", className: cn("fractal-ghost-surface", className), ...props, children: [
1401
+ /* @__PURE__ */ jsxs15("div", { className: "fractal-ghost-surface__header", children: [
1402
+ /* @__PURE__ */ jsx20(GhostLine, { width: "42%" }),
1403
+ /* @__PURE__ */ jsx20(Ghost, { className: "fractal-ghost-surface__close" })
1404
+ ] }),
1405
+ title && /* @__PURE__ */ jsx20("div", { className: "fractal-ghost-surface__title", children: title }),
1406
+ /* @__PURE__ */ jsx20("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx20(GhostLine, { width: index === lines - 1 ? "64%" : "100%" }, index)) })
1407
+ ] });
1408
+ }
1409
+
976
1410
  // src/components/Sidebar/Sidebar.tsx
977
- import * as React10 from "react";
978
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
979
- var Sidebar = React10.forwardRef(
980
- ({ className, width, style, ...props }, ref) => /* @__PURE__ */ jsx15(
1411
+ import * as React11 from "react";
1412
+ import { Fragment as Fragment3, jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
1413
+ var Sidebar = React11.forwardRef(
1414
+ ({ className, width, compact = false, style, ...props }, ref) => /* @__PURE__ */ jsx21(
981
1415
  "aside",
982
1416
  {
983
1417
  ref,
984
1418
  className: cn("fractal-sidebar", className),
1419
+ "data-compact": compact || void 0,
985
1420
  style: { ...style, ...width ? { "--fractal-sidebar-width": width } : {} },
986
1421
  ...props
987
1422
  }
988
1423
  )
989
1424
  );
990
1425
  Sidebar.displayName = "Sidebar";
991
- var SidebarHeader = React10.forwardRef(
992
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
1426
+ var SidebarHeader = React11.forwardRef(
1427
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx21("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
993
1428
  );
994
1429
  SidebarHeader.displayName = "SidebarHeader";
995
- var SidebarBrand = React10.forwardRef(
996
- ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx15("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
1430
+ var SidebarBrand = React11.forwardRef(
1431
+ ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx21("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
997
1432
  );
998
1433
  SidebarBrand.displayName = "SidebarBrand";
999
- var SidebarNav = React10.forwardRef(
1000
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
1434
+ function SidebarAccountMenu({
1435
+ name,
1436
+ items,
1437
+ onSelect,
1438
+ triggerLabel = "Account menu",
1439
+ icon,
1440
+ className
1441
+ }) {
1442
+ return /* @__PURE__ */ jsx21(
1443
+ Dropdown,
1444
+ {
1445
+ align: "left",
1446
+ className: cn("fractal-sidebar__account", className),
1447
+ items,
1448
+ onSelect,
1449
+ panelClassName: "fractal-sidebar__account-panel",
1450
+ selectedValue: "",
1451
+ triggerClassName: "fractal-sidebar__account-trigger",
1452
+ triggerContent: /* @__PURE__ */ jsxs16(Fragment3, { children: [
1453
+ icon && /* @__PURE__ */ jsx21("span", { className: "fractal-sidebar__account-icon", "aria-hidden": "true", children: icon }),
1454
+ /* @__PURE__ */ jsx21("span", { className: "fractal-sidebar__account-name", children: name })
1455
+ ] }),
1456
+ triggerLabel,
1457
+ triggerVariant: "tertiary"
1458
+ }
1459
+ );
1460
+ }
1461
+ var SidebarNav = React11.forwardRef(
1462
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx21("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
1001
1463
  );
1002
1464
  SidebarNav.displayName = "SidebarNav";
1003
- var SidebarSection = React10.forwardRef(
1004
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
1465
+ var SidebarSection = React11.forwardRef(
1466
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx21("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
1005
1467
  );
1006
1468
  SidebarSection.displayName = "SidebarSection";
1007
- var SidebarNavItem = React10.forwardRef(
1008
- ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs12(
1469
+ var SidebarNavItem = React11.forwardRef(
1470
+ ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs16(
1009
1471
  "button",
1010
1472
  {
1011
1473
  ref,
@@ -1015,66 +1477,314 @@ var SidebarNavItem = React10.forwardRef(
1015
1477
  "aria-current": active ? "page" : void 0,
1016
1478
  ...props,
1017
1479
  children: [
1018
- icon && /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
1019
- /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-label", children }),
1020
- endAdornment && /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
1480
+ icon && /* @__PURE__ */ jsx21("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
1481
+ /* @__PURE__ */ jsx21("span", { className: "fractal-sidebar__nav-item-label", children }),
1482
+ endAdornment && /* @__PURE__ */ jsx21("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
1021
1483
  ]
1022
1484
  }
1023
1485
  )
1024
1486
  );
1025
1487
  SidebarNavItem.displayName = "SidebarNavItem";
1026
- var SidebarFooter = React10.forwardRef(
1027
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1488
+ var SidebarFooter = React11.forwardRef(
1489
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx21("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1028
1490
  );
1029
1491
  SidebarFooter.displayName = "SidebarFooter";
1030
1492
 
1031
- // src/components/PageHeader/PageHeader.tsx
1032
- import * as React11 from "react";
1033
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1034
- var PageHeader = React11.forwardRef(
1035
- ({ title, description, actions, className, ...props }, ref) => /* @__PURE__ */ jsxs13("div", { ref, className: cn("fractal-page-header", className), ...props, children: [
1036
- /* @__PURE__ */ jsxs13("div", { className: "fractal-page-header__content", children: [
1037
- /* @__PURE__ */ jsx16("h1", { className: "fractal-page-header__title", children: title }),
1038
- description && /* @__PURE__ */ jsx16("p", { className: "fractal-page-header__description", children: description })
1039
- ] }),
1040
- actions && /* @__PURE__ */ jsx16("div", { className: "fractal-page-header__actions", children: actions })
1041
- ] })
1493
+ // src/components/SidePanel/SidePanel.tsx
1494
+ import { createPortal as createPortal2 } from "react-dom";
1495
+ import { Close } from "@mirror-physics/fractal-icons";
1496
+ import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
1497
+ function SidePanel({ open, onClose, title, description, children, footer, size = "md", loading = false, loadingLabel = "Loading", className }) {
1498
+ useEscapeDismiss(80, onClose, open);
1499
+ if (!open) return null;
1500
+ return createPortal2(
1501
+ /* @__PURE__ */ jsx22("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs17(
1502
+ "aside",
1503
+ {
1504
+ className: cn("fractal-side-panel", `fractal-side-panel--${size}`, className),
1505
+ "aria-modal": "true",
1506
+ "aria-label": typeof title === "string" ? title : "Side panel",
1507
+ role: "dialog",
1508
+ onMouseDown: (event) => event.stopPropagation(),
1509
+ children: [
1510
+ /* @__PURE__ */ jsxs17("header", { className: "fractal-side-panel__header", children: [
1511
+ /* @__PURE__ */ jsxs17("div", { className: "fractal-side-panel__heading", children: [
1512
+ typeof title === "string" ? /* @__PURE__ */ jsx22("h2", { className: "fractal-side-panel__title", children: title }) : /* @__PURE__ */ jsx22("div", { className: "fractal-side-panel__title", children: title }),
1513
+ description && /* @__PURE__ */ jsx22("p", { className: "fractal-side-panel__description", children: description })
1514
+ ] }),
1515
+ /* @__PURE__ */ jsx22("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx22(Close, { size: 18 }) })
1516
+ ] }),
1517
+ /* @__PURE__ */ jsx22("div", { "aria-busy": loading || void 0, className: cn("fractal-side-panel__body", loading && "fractal-side-panel__body--loading"), children: loading ? /* @__PURE__ */ jsx22(UILoader, { label: loadingLabel }) : children }),
1518
+ footer && /* @__PURE__ */ jsx22("footer", { className: "fractal-side-panel__footer", children: footer })
1519
+ ]
1520
+ }
1521
+ ) }),
1522
+ document.body
1523
+ );
1524
+ }
1525
+
1526
+ // src/components/Textarea/Textarea.tsx
1527
+ import * as React12 from "react";
1528
+ import { jsx as jsx23 } from "react/jsx-runtime";
1529
+ var Textarea = React12.forwardRef(
1530
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx23("textarea", { ref, className: cn("fractal-textarea", className), ...props })
1531
+ );
1532
+ Textarea.displayName = "Textarea";
1533
+
1534
+ // src/components/TextButton/TextButton.tsx
1535
+ import * as React13 from "react";
1536
+ import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
1537
+ var TextButton = React13.forwardRef(
1538
+ ({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ jsxs18(
1539
+ "button",
1540
+ {
1541
+ ref,
1542
+ className: cn("fractal-text-button", `fractal-text-button--${size}`, `fractal-text-button--${tone}`, className),
1543
+ type: "button",
1544
+ ...props,
1545
+ children: [
1546
+ icon && iconPosition === "start" && /* @__PURE__ */ jsx24("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon }),
1547
+ /* @__PURE__ */ jsx24("span", { children }),
1548
+ icon && iconPosition === "end" && /* @__PURE__ */ jsx24("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon })
1549
+ ]
1550
+ }
1551
+ )
1042
1552
  );
1043
- PageHeader.displayName = "PageHeader";
1553
+ TextButton.displayName = "TextButton";
1554
+
1555
+ // src/components/FileDropzone/FileDropzone.tsx
1556
+ import * as React14 from "react";
1557
+ import { Close as Close2, FileArrowUp } from "@mirror-physics/fractal-icons";
1558
+ import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
1559
+ function FileDropzone({
1560
+ files,
1561
+ onFilesChange,
1562
+ accept,
1563
+ multiple = true,
1564
+ disabled = false,
1565
+ title = "Drop files here or click to browse",
1566
+ description = "Files stay attached to this record.",
1567
+ className
1568
+ }) {
1569
+ const inputId = React14.useId();
1570
+ const inputRef = React14.useRef(null);
1571
+ const [isDragging, setIsDragging] = React14.useState(false);
1572
+ const addFiles = (nextFiles) => {
1573
+ if (disabled) return;
1574
+ const additions = Array.from(nextFiles);
1575
+ if (additions.length === 0) return;
1576
+ onFilesChange(multiple ? [...files, ...additions] : additions.slice(0, 1));
1577
+ };
1578
+ return /* @__PURE__ */ jsxs19("div", { className: cn("fractal-file-dropzone", className), children: [
1579
+ /* @__PURE__ */ jsx25(
1580
+ "input",
1581
+ {
1582
+ ref: inputRef,
1583
+ id: inputId,
1584
+ className: "fractal-file-dropzone__input",
1585
+ type: "file",
1586
+ accept,
1587
+ multiple,
1588
+ disabled,
1589
+ onChange: (event) => {
1590
+ if (event.target.files) addFiles(event.target.files);
1591
+ event.target.value = "";
1592
+ }
1593
+ }
1594
+ ),
1595
+ /* @__PURE__ */ jsxs19(
1596
+ "button",
1597
+ {
1598
+ className: "fractal-file-dropzone__target",
1599
+ "data-dragging": isDragging || void 0,
1600
+ type: "button",
1601
+ disabled,
1602
+ onClick: () => inputRef.current?.click(),
1603
+ onDragEnter: (event) => {
1604
+ event.preventDefault();
1605
+ if (!disabled) setIsDragging(true);
1606
+ },
1607
+ onDragOver: (event) => event.preventDefault(),
1608
+ onDragLeave: (event) => {
1609
+ event.preventDefault();
1610
+ setIsDragging(false);
1611
+ },
1612
+ onDrop: (event) => {
1613
+ event.preventDefault();
1614
+ setIsDragging(false);
1615
+ addFiles(event.dataTransfer.files);
1616
+ },
1617
+ children: [
1618
+ /* @__PURE__ */ jsx25(FileArrowUp, { "aria-hidden": "true", size: 20 }),
1619
+ /* @__PURE__ */ jsxs19("span", { className: "fractal-file-dropzone__copy", children: [
1620
+ /* @__PURE__ */ jsx25("span", { className: "fractal-file-dropzone__title", children: title }),
1621
+ description && /* @__PURE__ */ jsx25("span", { className: "fractal-file-dropzone__description", children: description })
1622
+ ] })
1623
+ ]
1624
+ }
1625
+ ),
1626
+ files.length > 0 && /* @__PURE__ */ jsx25("ul", { className: "fractal-file-dropzone__files", "aria-label": "Attached files", children: files.map((file, index) => /* @__PURE__ */ jsxs19("li", { className: "fractal-file-dropzone__file", children: [
1627
+ /* @__PURE__ */ jsx25("span", { className: "fractal-file-dropzone__file-name", children: file.name }),
1628
+ /* @__PURE__ */ jsx25("span", { className: "fractal-file-dropzone__file-meta", children: formatFileSize(file.size) }),
1629
+ /* @__PURE__ */ jsx25(
1630
+ "button",
1631
+ {
1632
+ className: "fractal-file-dropzone__remove",
1633
+ type: "button",
1634
+ "aria-label": `Remove ${file.name}`,
1635
+ disabled,
1636
+ onClick: () => onFilesChange(files.filter((_, fileIndex) => fileIndex !== index)),
1637
+ children: /* @__PURE__ */ jsx25(Close2, { "aria-hidden": "true", size: 14 })
1638
+ }
1639
+ )
1640
+ ] }, `${file.name}-${file.size}-${index}`)) })
1641
+ ] });
1642
+ }
1643
+ function formatFileSize(bytes) {
1644
+ if (bytes < 1024) return `${bytes} B`;
1645
+ if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
1646
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1647
+ }
1648
+
1649
+ // src/components/Pagination/Pagination.tsx
1650
+ import { ArrowLeft, ArrowRight } from "@mirror-physics/fractal-icons";
1651
+ import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
1652
+ function Pagination({
1653
+ page,
1654
+ pageSize,
1655
+ totalItems,
1656
+ onPageChange,
1657
+ pageSizeOptions,
1658
+ onPageSizeChange,
1659
+ ariaLabel = "Pagination",
1660
+ className
1661
+ }) {
1662
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
1663
+ const currentPage = Math.min(Math.max(page, 1), totalPages);
1664
+ const start = totalItems === 0 ? 0 : (currentPage - 1) * pageSize + 1;
1665
+ const end = Math.min(currentPage * pageSize, totalItems);
1666
+ const canChangePageSize = pageSizeOptions != null && pageSizeOptions.length > 0 && onPageSizeChange != null;
1667
+ return /* @__PURE__ */ jsxs20("nav", { className: cn("fractal-pagination", className), "aria-label": ariaLabel, children: [
1668
+ /* @__PURE__ */ jsx26("span", { className: "fractal-pagination__summary", children: totalItems === 0 ? "No results" : `${start}-${end} of ${totalItems}` }),
1669
+ /* @__PURE__ */ jsxs20("div", { className: "fractal-pagination__controls", children: [
1670
+ canChangePageSize && /* @__PURE__ */ jsx26(
1671
+ Dropdown,
1672
+ {
1673
+ align: "right",
1674
+ className: "fractal-pagination__page-size",
1675
+ direction: "up",
1676
+ items: pageSizeOptions.map((option) => ({
1677
+ value: String(option),
1678
+ description: `${option} per page`
1679
+ })),
1680
+ onSelect: (value) => onPageSizeChange(Number(value)),
1681
+ selectedValue: String(pageSize),
1682
+ size: "sm",
1683
+ triggerLabel: "Results per page",
1684
+ triggerVariant: "tertiary"
1685
+ }
1686
+ ),
1687
+ /* @__PURE__ */ jsx26(
1688
+ "button",
1689
+ {
1690
+ className: "fractal-pagination__button",
1691
+ type: "button",
1692
+ "aria-label": "Previous page",
1693
+ title: "Previous page",
1694
+ disabled: currentPage === 1 || totalItems === 0,
1695
+ onClick: () => onPageChange(currentPage - 1),
1696
+ children: /* @__PURE__ */ jsx26(ArrowLeft, { "aria-hidden": "true", size: 16 })
1697
+ }
1698
+ ),
1699
+ /* @__PURE__ */ jsxs20("span", { className: "fractal-pagination__page", "aria-current": "page", children: [
1700
+ currentPage,
1701
+ " of ",
1702
+ totalPages
1703
+ ] }),
1704
+ /* @__PURE__ */ jsx26(
1705
+ "button",
1706
+ {
1707
+ className: "fractal-pagination__button",
1708
+ type: "button",
1709
+ "aria-label": "Next page",
1710
+ title: "Next page",
1711
+ disabled: currentPage === totalPages || totalItems === 0,
1712
+ onClick: () => onPageChange(currentPage + 1),
1713
+ children: /* @__PURE__ */ jsx26(ArrowRight, { "aria-hidden": "true", size: 16 })
1714
+ }
1715
+ )
1716
+ ] })
1717
+ ] });
1718
+ }
1044
1719
  export {
1045
1720
  Alert,
1046
1721
  AlertDescription,
1722
+ AlertGhost,
1047
1723
  AlertTitle,
1724
+ Breadcrumbs,
1048
1725
  Button,
1726
+ ButtonGhost,
1049
1727
  Card,
1050
1728
  CardContent,
1051
1729
  CardDescription,
1052
1730
  CardFooter,
1731
+ CardGhost,
1053
1732
  CardHeader,
1054
1733
  CardTitle,
1734
+ Checkbox,
1735
+ CheckboxGhost,
1055
1736
  Chip,
1737
+ ChipGhost,
1738
+ ContentSwitcher,
1739
+ ContentSwitcherGhost,
1056
1740
  DataTable,
1741
+ DataTableGhost,
1057
1742
  Disclosure,
1743
+ DisclosureGhost,
1058
1744
  Dropdown,
1745
+ DropdownGhost,
1059
1746
  FeatureCard,
1747
+ FileDropzone,
1748
+ FileDropzoneGhost,
1060
1749
  FolderCard,
1750
+ Ghost,
1751
+ GhostLine,
1061
1752
  Input,
1753
+ InputGhost,
1062
1754
  Label,
1755
+ LabelGhost,
1756
+ LatticeLoader,
1063
1757
  Link,
1758
+ LinkGhost,
1064
1759
  Modal,
1065
- PageHeader,
1760
+ ModalGhost,
1761
+ Pagination,
1762
+ PaginationGhost,
1066
1763
  PromptCard,
1764
+ PromptCardGhost,
1067
1765
  PromptGrid,
1766
+ SidePanel,
1767
+ SidePanelGhost,
1068
1768
  Sidebar,
1769
+ SidebarAccountMenu,
1069
1770
  SidebarBrand,
1070
1771
  SidebarFooter,
1772
+ SidebarGhost,
1071
1773
  SidebarHeader,
1072
1774
  SidebarNav,
1073
1775
  SidebarNavItem,
1074
1776
  SidebarSection,
1075
1777
  SortableTable,
1778
+ SortableTableGhost,
1076
1779
  Tabs,
1780
+ TabsGhost,
1781
+ TextButton,
1782
+ TextButtonGhost,
1783
+ Textarea,
1784
+ TextareaGhost,
1077
1785
  Toggle,
1786
+ ToggleGhost,
1787
+ UILoader,
1078
1788
  cn,
1079
1789
  useEscapeDismiss
1080
1790
  };