@usefidel/design-system 0.25.0 → 0.26.0

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
@@ -950,12 +950,23 @@ function Badge({ tone = "neutral", icon, className, children, ...rest }) {
950
950
  }
951
951
 
952
952
  // src/components/Tag/Tag.tsx
953
+ import { X } from "lucide-react";
953
954
  import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
954
- function Tag({ dot, icon, className, children, ...rest }) {
955
+ function Tag({ dot, icon, onRemove, removeLabel, className, children, ...rest }) {
955
956
  return /* @__PURE__ */ jsxs14("span", { className: ["fds-tag", className].filter(Boolean).join(" "), ...rest, children: [
956
957
  dot && /* @__PURE__ */ jsx16("span", { className: `fds-dot fds-dot--${dot}`, "aria-hidden": "true" }),
957
958
  icon != null && /* @__PURE__ */ jsx16("span", { className: "fds-tag__icon", "aria-hidden": "true", children: icon }),
958
- children
959
+ children,
960
+ onRemove && /* @__PURE__ */ jsx16(
961
+ "button",
962
+ {
963
+ type: "button",
964
+ className: "fds-tag__remove",
965
+ "aria-label": `Remove ${removeLabel ?? (typeof children === "string" ? children : "")}`.trim(),
966
+ onClick: onRemove,
967
+ children: /* @__PURE__ */ jsx16(X, { "aria-hidden": "true" })
968
+ }
969
+ )
959
970
  ] });
960
971
  }
961
972
 
@@ -1248,15 +1259,18 @@ function Pagination({ page, pageCount, onPageChange, total, pageSize, variant =
1248
1259
 
1249
1260
  // src/components/EmptyState/EmptyState.tsx
1250
1261
  import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
1251
- function EmptyState({ illustration, title, description, action, footer, size = "default", className, ...rest }) {
1262
+ function EmptyState({ illustration, title, description, action, secondaryAction, footer, size = "default", className, ...rest }) {
1252
1263
  return /* @__PURE__ */ jsxs18("div", { className: ["fds-empty", size === "compact" ? "fds-empty--compact" : "", className].filter(Boolean).join(" "), ...rest, children: [
1253
1264
  illustration != null && /* @__PURE__ */ jsx20("div", { className: "fds-empty__illustration", children: illustration }),
1254
1265
  /* @__PURE__ */ jsxs18("div", { className: "fds-empty__text", children: [
1255
1266
  /* @__PURE__ */ jsx20("h2", { className: "fds-empty__title", children: title }),
1256
1267
  description != null && /* @__PURE__ */ jsx20("p", { className: "fds-empty__description", children: description })
1257
1268
  ] }),
1258
- (action != null || footer != null) && /* @__PURE__ */ jsxs18("div", { className: "fds-empty__actions", children: [
1259
- action,
1269
+ (action != null || secondaryAction != null || footer != null) && /* @__PURE__ */ jsxs18("div", { className: "fds-empty__actions", children: [
1270
+ (action != null || secondaryAction != null) && /* @__PURE__ */ jsxs18("div", { className: "fds-empty__buttons", children: [
1271
+ action,
1272
+ secondaryAction
1273
+ ] }),
1260
1274
  footer != null && /* @__PURE__ */ jsx20("div", { className: "fds-empty__footer", children: footer })
1261
1275
  ] })
1262
1276
  ] });
@@ -2301,7 +2315,7 @@ function PageHeader({ title, description, breadcrumb, actions, className, childr
2301
2315
 
2302
2316
  // src/components/Toast/Toast.tsx
2303
2317
  import { useEffect as useEffect9 } from "react";
2304
- import { X } from "lucide-react";
2318
+ import { X as X2 } from "lucide-react";
2305
2319
  import { jsx as jsx48, jsxs as jsxs39 } from "react/jsx-runtime";
2306
2320
  function ToastRegion({ className, children, ...rest }) {
2307
2321
  return /* @__PURE__ */ jsx48("div", { className: ["fds-toast-region", className].filter(Boolean).join(" "), "aria-live": "polite", ...rest, children });
@@ -2315,9 +2329,254 @@ function Toast({ children, action, onDismiss, duration, className, ...rest }) {
2315
2329
  return /* @__PURE__ */ jsxs39("div", { role: "status", className: ["fds-toast", className].filter(Boolean).join(" "), ...rest, children: [
2316
2330
  /* @__PURE__ */ jsx48("span", { className: "fds-toast__message", children }),
2317
2331
  action && /* @__PURE__ */ jsx48("button", { type: "button", className: "fds-toast__action", onClick: action.onClick, children: action.label }),
2318
- onDismiss && /* @__PURE__ */ jsx48("button", { type: "button", className: "fds-toast__close", "aria-label": "Dismiss", onClick: onDismiss, children: /* @__PURE__ */ jsx48(X, { "aria-hidden": "true" }) })
2332
+ onDismiss && /* @__PURE__ */ jsx48("button", { type: "button", className: "fds-toast__close", "aria-label": "Dismiss", onClick: onDismiss, children: /* @__PURE__ */ jsx48(X2, { "aria-hidden": "true" }) })
2333
+ ] });
2334
+ }
2335
+
2336
+ // src/components/Stepper/Stepper.tsx
2337
+ import { Children, isValidElement } from "react";
2338
+ import { Check as Check2 } from "lucide-react";
2339
+ import { jsx as jsx49, jsxs as jsxs40 } from "react/jsx-runtime";
2340
+ function StepperStep(_) {
2341
+ return null;
2342
+ }
2343
+ function StepperRoot({ className, children, ...rest }) {
2344
+ const steps = Children.toArray(children).filter(isValidElement);
2345
+ return /* @__PURE__ */ jsx49("ol", { className: ["fds-stepper", className].filter(Boolean).join(" "), ...rest, children: steps.map((step, i) => {
2346
+ const { status, children: label } = step.props;
2347
+ return /* @__PURE__ */ jsxs40(
2348
+ "li",
2349
+ {
2350
+ className: `fds-stepper__step fds-stepper__step--${status}`,
2351
+ "aria-current": status === "current" ? "step" : void 0,
2352
+ children: [
2353
+ /* @__PURE__ */ jsx49("span", { className: "fds-stepper__marker", "aria-hidden": "true", children: status === "done" ? /* @__PURE__ */ jsx49(Check2, {}) : i + 1 }),
2354
+ /* @__PURE__ */ jsx49("span", { className: "fds-stepper__label", children: label })
2355
+ ]
2356
+ },
2357
+ i
2358
+ );
2359
+ }) });
2360
+ }
2361
+ var Stepper = Object.assign(StepperRoot, { Step: StepperStep });
2362
+
2363
+ // src/components/NumberedSteps/NumberedSteps.tsx
2364
+ import { Children as Children2 } from "react";
2365
+ import { jsx as jsx50, jsxs as jsxs41 } from "react/jsx-runtime";
2366
+ function NumberedStepsRoot({ className, children, ...rest }) {
2367
+ return /* @__PURE__ */ jsx50("ol", { className: ["fds-numbered-steps", className].filter(Boolean).join(" "), ...rest, children: Children2.toArray(children) });
2368
+ }
2369
+ function NumberedStepsItem({ className, children, ...rest }) {
2370
+ return /* @__PURE__ */ jsxs41("li", { className: ["fds-numbered-steps__item", className].filter(Boolean).join(" "), ...rest, children: [
2371
+ /* @__PURE__ */ jsx50("span", { className: "fds-numbered-steps__marker", "aria-hidden": "true" }),
2372
+ /* @__PURE__ */ jsx50("div", { className: "fds-numbered-steps__body", children })
2373
+ ] });
2374
+ }
2375
+ var NumberedSteps = Object.assign(NumberedStepsRoot, { Item: NumberedStepsItem });
2376
+
2377
+ // src/components/OptionList/OptionList.tsx
2378
+ import { jsx as jsx51, jsxs as jsxs42 } from "react/jsx-runtime";
2379
+ function OptionListRoot({ className, children, ...rest }) {
2380
+ return /* @__PURE__ */ jsx51("div", { className: ["fds-option-list", className].filter(Boolean).join(" "), ...rest, children });
2381
+ }
2382
+ function OptionListLabel({ className, children, ...rest }) {
2383
+ return /* @__PURE__ */ jsx51("div", { className: ["fds-option-list__label", className].filter(Boolean).join(" "), ...rest, children });
2384
+ }
2385
+ function OptionRow({ control = "radio", title, description, meta, className, checked, disabled, ...rest }) {
2386
+ const Control = control === "checkbox" ? Checkbox : Radio;
2387
+ return /* @__PURE__ */ jsxs42(
2388
+ "div",
2389
+ {
2390
+ className: ["fds-option-row", checked ? "fds-option-row--checked" : "", className].filter(Boolean).join(" "),
2391
+ "data-disabled": disabled ? "" : void 0,
2392
+ children: [
2393
+ /* @__PURE__ */ jsx51(Control, { label: title, description, checked, disabled, ...rest }),
2394
+ meta != null && /* @__PURE__ */ jsx51("span", { className: "fds-option-row__meta", children: meta })
2395
+ ]
2396
+ }
2397
+ );
2398
+ }
2399
+ var OptionList = Object.assign(OptionListRoot, { Label: OptionListLabel, Row: OptionRow });
2400
+
2401
+ // src/components/Checklist/Checklist.tsx
2402
+ import { Check as Check3 } from "lucide-react";
2403
+ import { jsx as jsx52, jsxs as jsxs43 } from "react/jsx-runtime";
2404
+ function ChecklistRoot({ title, className, children, ...rest }) {
2405
+ return /* @__PURE__ */ jsxs43("section", { className: ["fds-checklist", className].filter(Boolean).join(" "), ...rest, children: [
2406
+ title != null && /* @__PURE__ */ jsx52("h3", { className: "fds-checklist__title", children: title }),
2407
+ /* @__PURE__ */ jsx52("ul", { className: "fds-checklist__items", children })
2319
2408
  ] });
2320
2409
  }
2410
+ function ChecklistItem({ done, label, value }) {
2411
+ return /* @__PURE__ */ jsxs43("li", { className: ["fds-checklist__item", done ? "fds-checklist__item--done" : ""].filter(Boolean).join(" "), children: [
2412
+ /* @__PURE__ */ jsx52("span", { className: "fds-checklist__mark", "aria-hidden": "true", children: done && /* @__PURE__ */ jsx52(Check3, {}) }),
2413
+ /* @__PURE__ */ jsxs43("span", { className: "fds-checklist__text", children: [
2414
+ /* @__PURE__ */ jsx52("span", { className: "fds-checklist__label", children: label }),
2415
+ /* @__PURE__ */ jsx52("span", { className: "fds-checklist__value", children: value })
2416
+ ] })
2417
+ ] });
2418
+ }
2419
+ var Checklist = Object.assign(ChecklistRoot, { Item: ChecklistItem });
2420
+
2421
+ // src/components/Drawer/Drawer.tsx
2422
+ import { useEffect as useEffect10, useId as useId14, useRef as useRef10 } from "react";
2423
+ import { createPortal as createPortal3 } from "react-dom";
2424
+ import { X as X3 } from "lucide-react";
2425
+ import { jsx as jsx53, jsxs as jsxs44 } from "react/jsx-runtime";
2426
+ var FOCUSABLE2 = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
2427
+ function Drawer({ open, onClose, title, description, children, footer, dismissible = true }) {
2428
+ const titleId = useId14();
2429
+ const descriptionId = useId14();
2430
+ const panelRef = useRef10(null);
2431
+ const onCloseRef = useRef10(onClose);
2432
+ onCloseRef.current = onClose;
2433
+ const dismissibleRef = useRef10(dismissible);
2434
+ dismissibleRef.current = dismissible;
2435
+ useEffect10(() => {
2436
+ if (!open) return;
2437
+ const panel = panelRef.current;
2438
+ const previouslyFocused = document.activeElement;
2439
+ (panel?.querySelector("[data-autofocus]") ?? panel?.querySelector(`.fds-drawer__body ${FOCUSABLE2}`) ?? panel)?.focus();
2440
+ const previousOverflow = document.body.style.overflow;
2441
+ document.body.style.overflow = "hidden";
2442
+ const onKey = (e) => {
2443
+ if (e.key === "Escape" && dismissibleRef.current) {
2444
+ e.preventDefault();
2445
+ onCloseRef.current();
2446
+ return;
2447
+ }
2448
+ if (e.key === "Tab" && panel) {
2449
+ const focusable = Array.from(panel.querySelectorAll(FOCUSABLE2));
2450
+ if (focusable.length === 0) return;
2451
+ const first = focusable[0];
2452
+ const last = focusable[focusable.length - 1];
2453
+ if (e.shiftKey && (document.activeElement === first || document.activeElement === panel)) {
2454
+ e.preventDefault();
2455
+ last.focus();
2456
+ } else if (!e.shiftKey && document.activeElement === last) {
2457
+ e.preventDefault();
2458
+ first.focus();
2459
+ }
2460
+ }
2461
+ };
2462
+ window.addEventListener("keydown", onKey);
2463
+ return () => {
2464
+ window.removeEventListener("keydown", onKey);
2465
+ document.body.style.overflow = previousOverflow;
2466
+ previouslyFocused?.focus?.();
2467
+ };
2468
+ }, [open]);
2469
+ if (!open || typeof document === "undefined") return null;
2470
+ return createPortal3(
2471
+ /* @__PURE__ */ jsx53(
2472
+ "div",
2473
+ {
2474
+ className: "fds-drawer__scrim",
2475
+ onMouseDown: (e) => {
2476
+ if (e.target === e.currentTarget && dismissible) onClose();
2477
+ },
2478
+ children: /* @__PURE__ */ jsxs44(
2479
+ "div",
2480
+ {
2481
+ ref: panelRef,
2482
+ className: "fds-drawer",
2483
+ role: "dialog",
2484
+ "aria-modal": "true",
2485
+ "aria-labelledby": titleId,
2486
+ "aria-describedby": description != null ? descriptionId : void 0,
2487
+ tabIndex: -1,
2488
+ children: [
2489
+ /* @__PURE__ */ jsxs44("header", { className: "fds-drawer__header", children: [
2490
+ /* @__PURE__ */ jsxs44("div", { className: "fds-drawer__heading", children: [
2491
+ /* @__PURE__ */ jsx53("h2", { id: titleId, className: "fds-drawer__title", children: title }),
2492
+ description != null && /* @__PURE__ */ jsx53("p", { id: descriptionId, className: "fds-drawer__description", children: description })
2493
+ ] }),
2494
+ /* @__PURE__ */ jsx53("button", { type: "button", className: "fds-drawer__close", onClick: onClose, disabled: !dismissible, "aria-label": "Close", children: /* @__PURE__ */ jsx53(X3, { "aria-hidden": "true" }) })
2495
+ ] }),
2496
+ /* @__PURE__ */ jsx53("div", { className: "fds-drawer__body", children }),
2497
+ footer != null && /* @__PURE__ */ jsx53("footer", { className: "fds-drawer__footer", children: footer })
2498
+ ]
2499
+ }
2500
+ )
2501
+ }
2502
+ ),
2503
+ document.body
2504
+ );
2505
+ }
2506
+
2507
+ // src/components/ResourceCard/ResourceCard.tsx
2508
+ import { jsx as jsx54, jsxs as jsxs45 } from "react/jsx-runtime";
2509
+ function ResourceCard({ icon, title, badge, meta, stats, action, variant = "default", className, ...rest }) {
2510
+ return /* @__PURE__ */ jsxs45("div", { className: ["fds-resource-card", `fds-resource-card--${variant}`, className].filter(Boolean).join(" "), ...rest, children: [
2511
+ /* @__PURE__ */ jsxs45("div", { className: "fds-resource-card__main", children: [
2512
+ /* @__PURE__ */ jsxs45("div", { className: "fds-resource-card__title", children: [
2513
+ icon != null && /* @__PURE__ */ jsx54("span", { className: "fds-resource-card__icon", "aria-hidden": "true", children: icon }),
2514
+ /* @__PURE__ */ jsx54("span", { children: title }),
2515
+ badge
2516
+ ] }),
2517
+ meta?.map((line, i) => /* @__PURE__ */ jsx54("div", { className: "fds-resource-card__meta", children: line }, i))
2518
+ ] }),
2519
+ stats && stats.length > 0 && /* @__PURE__ */ jsx54("dl", { className: "fds-resource-card__stats", children: stats.map((s, i) => /* @__PURE__ */ jsxs45("div", { className: "fds-resource-card__stat", children: [
2520
+ /* @__PURE__ */ jsx54("dt", { children: s.label }),
2521
+ /* @__PURE__ */ jsx54("dd", { children: s.value })
2522
+ ] }, i)) }),
2523
+ action != null && /* @__PURE__ */ jsx54("div", { className: "fds-resource-card__action", children: action })
2524
+ ] });
2525
+ }
2526
+
2527
+ // src/components/PageSkeleton/PageSkeleton.tsx
2528
+ import { jsx as jsx55, jsxs as jsxs46 } from "react/jsx-runtime";
2529
+ function PageSkeletonRoot({ label = "Loading", children }) {
2530
+ return /* @__PURE__ */ jsx55(SkeletonGroup, { label, className: "fds-page-skeleton", children });
2531
+ }
2532
+ function PageSkeletonHeader({ title, description, breadcrumb, action, tabs = 0 }) {
2533
+ return /* @__PURE__ */ jsx55(
2534
+ PageHeader,
2535
+ {
2536
+ breadcrumb: breadcrumb ? /* @__PURE__ */ jsx55(Skeleton, { shape: "text", width: 140 }) : void 0,
2537
+ title: title ?? /* @__PURE__ */ jsx55(Skeleton, { width: 220, height: 28 }),
2538
+ description: description === true ? /* @__PURE__ */ jsx55(Skeleton, { shape: "text", width: 320 }) : description || void 0,
2539
+ actions: action ? /* @__PURE__ */ jsx55(Skeleton, { width: 128, height: 36 }) : void 0,
2540
+ children: tabs > 0 && /* @__PURE__ */ jsx55("div", { className: "fds-page-skeleton__tabs", children: Array.from({ length: tabs }, (_, i) => /* @__PURE__ */ jsx55(Skeleton, { shape: "text", width: 80 }, i)) })
2541
+ }
2542
+ );
2543
+ }
2544
+ function PageSkeletonToolbar({ filters = 2 }) {
2545
+ return /* @__PURE__ */ jsxs46("div", { className: "fds-page-skeleton__toolbar", children: [
2546
+ /* @__PURE__ */ jsx55(Skeleton, { width: 280, height: 36 }),
2547
+ Array.from({ length: filters }, (_, i) => /* @__PURE__ */ jsx55(Skeleton, { width: 140, height: 36 }, i))
2548
+ ] });
2549
+ }
2550
+ function PageSkeletonTable({ rows = 5, columns = 5 }) {
2551
+ const cells = (key, width) => Array.from({ length: columns }, (_, c) => /* @__PURE__ */ jsx55(Skeleton, { shape: "text", width: c === 0 ? "60%" : width }, `${key}-${c}`));
2552
+ return /* @__PURE__ */ jsxs46("div", { className: "fds-page-skeleton__table", style: { ["--fds-page-skeleton-columns"]: columns }, children: [
2553
+ /* @__PURE__ */ jsx55("div", { className: "fds-page-skeleton__row fds-page-skeleton__row--head", children: cells("h", "40%") }),
2554
+ Array.from({ length: rows }, (_, r) => /* @__PURE__ */ jsx55("div", { className: "fds-page-skeleton__row", children: cells(`r${r}`, "50%") }, r))
2555
+ ] });
2556
+ }
2557
+ function PageSkeletonCards({ count = 3, height = 120, columns = 3 }) {
2558
+ return /* @__PURE__ */ jsx55("div", { className: "fds-page-skeleton__cards", style: { ["--fds-page-skeleton-columns"]: columns }, children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx55(Skeleton, { height, className: "fds-page-skeleton__card" }, i)) });
2559
+ }
2560
+ function PageSkeletonForm({ fields = 3 }) {
2561
+ return /* @__PURE__ */ jsx55("div", { className: "fds-page-skeleton__form", children: Array.from({ length: fields }, (_, i) => /* @__PURE__ */ jsxs46("div", { className: "fds-page-skeleton__field", children: [
2562
+ /* @__PURE__ */ jsx55(Skeleton, { shape: "text", width: 120 }),
2563
+ /* @__PURE__ */ jsx55(Skeleton, { height: 36 })
2564
+ ] }, i)) });
2565
+ }
2566
+ function PageSkeletonSplit({ children, aside }) {
2567
+ return /* @__PURE__ */ jsxs46("div", { className: "fds-page-skeleton__split", children: [
2568
+ /* @__PURE__ */ jsx55("div", { className: "fds-page-skeleton__main", children }),
2569
+ /* @__PURE__ */ jsx55("div", { className: "fds-page-skeleton__aside", children: aside })
2570
+ ] });
2571
+ }
2572
+ var PageSkeleton = Object.assign(PageSkeletonRoot, {
2573
+ Header: PageSkeletonHeader,
2574
+ Toolbar: PageSkeletonToolbar,
2575
+ Table: PageSkeletonTable,
2576
+ Cards: PageSkeletonCards,
2577
+ Form: PageSkeletonForm,
2578
+ Split: PageSkeletonSplit
2579
+ });
2321
2580
  export {
2322
2581
  Accordion,
2323
2582
  AppLayout,
@@ -2331,9 +2590,11 @@ export {
2331
2590
  Callout,
2332
2591
  Card,
2333
2592
  Checkbox,
2593
+ Checklist,
2334
2594
  DiffPropertyList,
2335
2595
  DiffSection,
2336
2596
  Divider,
2597
+ Drawer,
2337
2598
  EmptyState,
2338
2599
  IconButton,
2339
2600
  Input,
@@ -2342,11 +2603,16 @@ export {
2342
2603
  MismatchCard,
2343
2604
  Modal,
2344
2605
  MultiSelect,
2606
+ NumberedSteps,
2607
+ OptionList,
2608
+ OptionRow,
2345
2609
  PageHeader,
2610
+ PageSkeleton,
2346
2611
  Pagination,
2347
2612
  PricingCard,
2348
2613
  Radio,
2349
2614
  RadioCard,
2615
+ ResourceCard,
2350
2616
  SearchInput,
2351
2617
  SectionHeader,
2352
2618
  SegmentedControl,
@@ -2362,6 +2628,7 @@ export {
2362
2628
  Stat,
2363
2629
  StatusDot,
2364
2630
  StepList,
2631
+ Stepper,
2365
2632
  Swatch,
2366
2633
  TabNav,
2367
2634
  Table,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefidel/design-system",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "description": "Fidel brand design tokens + React component library — DTCG token source of truth, generated per-surface CSS + Tailwind-v4 parser-compatibility output, and on-brand React components (Button, MismatchCard, DiffSection).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",