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

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