@usefidel/design-system 0.17.0 → 0.19.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
@@ -358,7 +358,11 @@ var Select = forwardRef(function Select2({
358
358
  id,
359
359
  name,
360
360
  className,
361
- containerClassName
361
+ containerClassName,
362
+ variant = "field",
363
+ size = "medium",
364
+ align = "start",
365
+ "aria-label": ariaLabel
362
366
  }, ref) {
363
367
  const genId = useId();
364
368
  const triggerId = id ?? genId;
@@ -377,7 +381,7 @@ var Select = forwardRef(function Select2({
377
381
  if (!searchable || !query.trim()) return options;
378
382
  const q = query.trim().toLowerCase();
379
383
  return options.filter(
380
- (o) => o.label.toLowerCase().includes(q) || (o.secondary?.toLowerCase().includes(q) ?? false)
384
+ (o) => o.label.toLowerCase().includes(q) || (o.secondary?.toLowerCase().includes(q) ?? false) || (o.description?.toLowerCase().includes(q) ?? false)
381
385
  );
382
386
  }, [options, searchable, query]);
383
387
  const hasMessage = Boolean(error ?? hint);
@@ -460,6 +464,8 @@ var Select = forwardRef(function Select2({
460
464
  };
461
465
  const triggerClasses = [
462
466
  "fds-select__trigger",
467
+ variant === "inline" ? "fds-select__trigger--inline" : "",
468
+ size === "small" ? "fds-select__trigger--small" : "",
463
469
  error ? "fds-select__trigger--error" : "",
464
470
  open ? "fds-select__trigger--open" : "",
465
471
  className
@@ -468,7 +474,7 @@ var Select = forwardRef(function Select2({
468
474
  "div",
469
475
  {
470
476
  ref: rootRef,
471
- className: ["fds-select-field", containerClassName].filter(Boolean).join(" "),
477
+ className: ["fds-select-field", variant === "inline" ? "fds-select-field--inline" : "", containerClassName].filter(Boolean).join(" "),
472
478
  children: [
473
479
  label != null && /* @__PURE__ */ jsx5("label", { htmlFor: triggerId, className: "fds-select__label", children: label }),
474
480
  /* @__PURE__ */ jsxs5("div", { className: "fds-select", children: [
@@ -485,6 +491,7 @@ var Select = forwardRef(function Select2({
485
491
  "aria-controls": open ? listId : void 0,
486
492
  "aria-invalid": error ? true : void 0,
487
493
  "aria-describedby": hasMessage ? msgId : void 0,
494
+ "aria-label": ariaLabel,
488
495
  onClick: () => !disabled && setOpen((o) => !o),
489
496
  onKeyDown,
490
497
  children: [
@@ -500,7 +507,7 @@ var Select = forwardRef(function Select2({
500
507
  ]
501
508
  }
502
509
  ),
503
- open && /* @__PURE__ */ jsxs5("div", { className: "fds-select__popover", children: [
510
+ open && /* @__PURE__ */ jsxs5("div", { className: `fds-select__popover fds-select__popover--${align}`, children: [
504
511
  searchable && /* @__PURE__ */ jsx5("div", { className: "fds-select__search", children: /* @__PURE__ */ jsx5(
505
512
  "input",
506
513
  {
@@ -545,9 +552,12 @@ var Select = forwardRef(function Select2({
545
552
  onClick: () => choose(i),
546
553
  children: [
547
554
  o.avatar != null && /* @__PURE__ */ jsx5("span", { className: "fds-select__optAvatar", children: o.avatar }),
548
- /* @__PURE__ */ jsxs5("span", { className: "fds-select__optText", children: [
549
- /* @__PURE__ */ jsx5("span", { className: "fds-select__optLabel", children: o.label }),
550
- o.secondary != null && /* @__PURE__ */ jsx5("span", { className: "fds-select__optSecondary", children: o.secondary })
555
+ /* @__PURE__ */ jsxs5("span", { className: "fds-select__optBody", children: [
556
+ /* @__PURE__ */ jsxs5("span", { className: "fds-select__optText", children: [
557
+ /* @__PURE__ */ jsx5("span", { className: "fds-select__optLabel", children: o.label }),
558
+ o.secondary != null && /* @__PURE__ */ jsx5("span", { className: "fds-select__optSecondary", children: o.secondary })
559
+ ] }),
560
+ o.description != null && /* @__PURE__ */ jsx5("span", { className: "fds-select__optDescription", children: o.description })
551
561
  ] }),
552
562
  o.value === value && /* @__PURE__ */ jsx5("span", { className: "fds-select__check", children: /* @__PURE__ */ jsx5(CheckIcon, {}) })
553
563
  ]
@@ -809,6 +819,7 @@ function SegmentedControl({
809
819
  value,
810
820
  onChange,
811
821
  "aria-label": ariaLabel,
822
+ variant = "underline",
812
823
  className
813
824
  }) {
814
825
  const refs = useRef4([]);
@@ -825,7 +836,7 @@ function SegmentedControl({
825
836
  onChange(options[next].value);
826
837
  refs.current[next]?.focus();
827
838
  };
828
- const classes = ["fds-segmented", className].filter(Boolean).join(" ");
839
+ const classes = ["fds-segmented", variant === "pill" ? "fds-segmented--pill" : "", className].filter(Boolean).join(" ");
829
840
  return /* @__PURE__ */ jsx11("div", { role: "tablist", "aria-label": ariaLabel, className: classes, children: options.map((option, i) => {
830
841
  const selected = option.value === value;
831
842
  return /* @__PURE__ */ jsxs10(
@@ -940,9 +951,10 @@ function Badge({ tone = "neutral", icon, className, children, ...rest }) {
940
951
 
941
952
  // src/components/Tag/Tag.tsx
942
953
  import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
943
- function Tag({ dot, className, children, ...rest }) {
954
+ function Tag({ dot, icon, className, children, ...rest }) {
944
955
  return /* @__PURE__ */ jsxs14("span", { className: ["fds-tag", className].filter(Boolean).join(" "), ...rest, children: [
945
956
  dot && /* @__PURE__ */ jsx16("span", { className: `fds-dot fds-dot--${dot}`, "aria-hidden": "true" }),
957
+ icon != null && /* @__PURE__ */ jsx16("span", { className: "fds-tag__icon", "aria-hidden": "true", children: icon }),
946
958
  children
947
959
  ] });
948
960
  }
@@ -953,11 +965,17 @@ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
953
965
  function ChevronIcon2() {
954
966
  return /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 20 20", width: "20", height: "20", fill: "none", "aria-hidden": "true", className: "fds-multiselect__chevronIcon", children: /* @__PURE__ */ jsx17("path", { d: "M6 8l4 4 4-4", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) });
955
967
  }
968
+ function RemoveIcon() {
969
+ return /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 12 12", width: "10", height: "10", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx17("path", { d: "M3 3l6 6M9 3L3 9", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) });
970
+ }
956
971
  function CheckIcon2() {
957
972
  return /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 12 12", width: "12", height: "12", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx17("path", { d: "M10 3L4.5 8.5 2 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
958
973
  }
959
974
  function MultiSelect({
960
975
  label,
976
+ variant = "filter",
977
+ placeholder,
978
+ showActions,
961
979
  options,
962
980
  value,
963
981
  onChange,
@@ -1028,8 +1046,59 @@ function MultiSelect({
1028
1046
  }
1029
1047
  };
1030
1048
  const triggerClasses = ["fds-multiselect__trigger", open ? "fds-multiselect__trigger--open" : ""].filter(Boolean).join(" ");
1031
- return /* @__PURE__ */ jsxs15("div", { ref: rootRef, className: ["fds-multiselect", className].filter(Boolean).join(" "), children: [
1032
- /* @__PURE__ */ jsxs15(
1049
+ const isField = variant === "field";
1050
+ const withActions = showActions ?? !isField;
1051
+ const chosen = options.filter((o) => selected.has(o.value));
1052
+ const fieldBox = /* @__PURE__ */ jsxs15(
1053
+ "div",
1054
+ {
1055
+ className: ["fds-multiselect__fieldBox", open ? "fds-multiselect__fieldBox--open" : "", disabled ? "fds-multiselect__fieldBox--disabled" : ""].filter(Boolean).join(" "),
1056
+ onClick: (e) => {
1057
+ if (disabled || e.target.closest("button")) return;
1058
+ setActiveIndex(0);
1059
+ setOpen((o) => !o);
1060
+ },
1061
+ children: [
1062
+ /* @__PURE__ */ jsxs15("span", { className: "fds-multiselect__chips", children: [
1063
+ chosen.length === 0 && placeholder != null && /* @__PURE__ */ jsx17("span", { className: "fds-multiselect__placeholder", children: placeholder }),
1064
+ chosen.map((o) => /* @__PURE__ */ jsxs15("span", { className: "fds-multiselect__chip", children: [
1065
+ o.label,
1066
+ /* @__PURE__ */ jsx17(
1067
+ "button",
1068
+ {
1069
+ type: "button",
1070
+ className: "fds-multiselect__chipRemove",
1071
+ "aria-label": `Remove ${o.label}`,
1072
+ disabled,
1073
+ onClick: () => toggle(o),
1074
+ children: /* @__PURE__ */ jsx17(RemoveIcon, {})
1075
+ }
1076
+ )
1077
+ ] }, o.value))
1078
+ ] }),
1079
+ /* @__PURE__ */ jsx17(
1080
+ "button",
1081
+ {
1082
+ type: "button",
1083
+ id: triggerId,
1084
+ ref: triggerRef,
1085
+ className: "fds-multiselect__fieldBoxToggle",
1086
+ disabled,
1087
+ "aria-haspopup": "listbox",
1088
+ "aria-expanded": open,
1089
+ "aria-controls": open ? listId : void 0,
1090
+ "aria-label": `${label}: ${chosen.length === 0 ? "none chosen" : `${chosen.length} chosen`}`,
1091
+ onClick: () => setOpen((o) => !o),
1092
+ onKeyDown: onTriggerKeyDown,
1093
+ children: /* @__PURE__ */ jsx17("span", { className: `fds-multiselect__chevron${open ? " fds-multiselect__chevron--open" : ""}`, children: /* @__PURE__ */ jsx17(ChevronIcon2, {}) })
1094
+ }
1095
+ )
1096
+ ]
1097
+ }
1098
+ );
1099
+ return /* @__PURE__ */ jsxs15("div", { ref: rootRef, className: ["fds-multiselect", isField ? "fds-multiselect--field" : "", className].filter(Boolean).join(" "), children: [
1100
+ isField && /* @__PURE__ */ jsx17("label", { htmlFor: triggerId, className: "fds-multiselect__fieldLabel", children: label }),
1101
+ isField ? fieldBox : /* @__PURE__ */ jsxs15(
1033
1102
  "button",
1034
1103
  {
1035
1104
  type: "button",
@@ -1095,7 +1164,7 @@ function MultiSelect({
1095
1164
  })
1096
1165
  }
1097
1166
  ),
1098
- /* @__PURE__ */ jsxs15("div", { className: "fds-multiselect__footer", children: [
1167
+ withActions && /* @__PURE__ */ jsxs15("div", { className: "fds-multiselect__footer", children: [
1099
1168
  /* @__PURE__ */ jsx17("button", { type: "button", className: "fds-multiselect__action", onClick: () => onChange(enabled.map((o) => o.value)), children: selectAllLabel }),
1100
1169
  /* @__PURE__ */ jsx17("button", { type: "button", className: "fds-multiselect__action", onClick: () => onChange([]), children: clearLabel })
1101
1170
  ] })
@@ -1179,8 +1248,8 @@ function Pagination({ page, pageCount, onPageChange, total, pageSize, variant =
1179
1248
 
1180
1249
  // src/components/EmptyState/EmptyState.tsx
1181
1250
  import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
1182
- function EmptyState({ illustration, title, description, action, footer, className, ...rest }) {
1183
- return /* @__PURE__ */ jsxs18("div", { className: ["fds-empty", className].filter(Boolean).join(" "), ...rest, children: [
1251
+ function EmptyState({ illustration, title, description, action, footer, size = "default", className, ...rest }) {
1252
+ return /* @__PURE__ */ jsxs18("div", { className: ["fds-empty", size === "compact" ? "fds-empty--compact" : "", className].filter(Boolean).join(" "), ...rest, children: [
1184
1253
  illustration != null && /* @__PURE__ */ jsx20("div", { className: "fds-empty__illustration", children: illustration }),
1185
1254
  /* @__PURE__ */ jsxs18("div", { className: "fds-empty__text", children: [
1186
1255
  /* @__PURE__ */ jsx20("h2", { className: "fds-empty__title", children: title }),
@@ -1261,20 +1330,453 @@ function Skeleton({ shape = "rect", width, height, lines = 1, className, style,
1261
1330
  function SkeletonGroup({ label = "Loading", className, children, ...rest }) {
1262
1331
  return /* @__PURE__ */ jsx22("div", { role: "status", "aria-busy": "true", "aria-label": label, className, ...rest, children });
1263
1332
  }
1333
+
1334
+ // src/components/Card/Card.tsx
1335
+ import { jsx as jsx23 } from "react/jsx-runtime";
1336
+ function Card({ tone = "default", padding = "md", className, children, ...rest }) {
1337
+ const classes = ["fds-card", `fds-card--${tone}`, `fds-card--pad-${padding}`, className].filter(Boolean).join(" ");
1338
+ return /* @__PURE__ */ jsx23("div", { className: classes, ...rest, children });
1339
+ }
1340
+
1341
+ // src/components/Stat/Stat.tsx
1342
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
1343
+ function Stat({ label, value, children, className, ...rest }) {
1344
+ return /* @__PURE__ */ jsxs20("div", { className: ["fds-stat", className].filter(Boolean).join(" "), ...rest, children: [
1345
+ /* @__PURE__ */ jsx24("p", { className: "fds-stat__label", children: label }),
1346
+ /* @__PURE__ */ jsx24("p", { className: "fds-stat__value", children: value }),
1347
+ children != null && /* @__PURE__ */ jsx24("div", { className: "fds-stat__detail", children })
1348
+ ] });
1349
+ }
1350
+
1351
+ // src/components/Checkbox/Checkbox.tsx
1352
+ import {
1353
+ forwardRef as forwardRef4,
1354
+ useEffect as useEffect5,
1355
+ useId as useId7,
1356
+ useImperativeHandle as useImperativeHandle2,
1357
+ useRef as useRef6
1358
+ } from "react";
1359
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
1360
+ var Checkbox = forwardRef4(function Checkbox2({ label, description, indeterminate = false, className, id, disabled, ...rest }, ref) {
1361
+ const generatedId = useId7();
1362
+ const inputId = id ?? generatedId;
1363
+ const inputRef = useRef6(null);
1364
+ useImperativeHandle2(ref, () => inputRef.current);
1365
+ useEffect5(() => {
1366
+ if (inputRef.current) inputRef.current.indeterminate = indeterminate;
1367
+ }, [indeterminate]);
1368
+ return /* @__PURE__ */ jsxs21(
1369
+ "div",
1370
+ {
1371
+ className: ["fds-checkbox", className].filter(Boolean).join(" "),
1372
+ "data-disabled": disabled ? "" : void 0,
1373
+ children: [
1374
+ /* @__PURE__ */ jsx25(
1375
+ "input",
1376
+ {
1377
+ ...rest,
1378
+ id: inputId,
1379
+ ref: inputRef,
1380
+ type: "checkbox",
1381
+ disabled,
1382
+ className: "fds-checkbox__input"
1383
+ }
1384
+ ),
1385
+ /* @__PURE__ */ jsxs21("label", { htmlFor: inputId, className: "fds-checkbox__control", children: [
1386
+ /* @__PURE__ */ jsxs21("span", { className: "fds-checkbox__box", "aria-hidden": "true", children: [
1387
+ /* @__PURE__ */ jsx25("svg", { className: "fds-checkbox__tick", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx25("path", { d: "M10 3L4.5 8.5 2 6", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1388
+ /* @__PURE__ */ jsx25("svg", { className: "fds-checkbox__dash", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx25("path", { d: "M3 6h6", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }) })
1389
+ ] }),
1390
+ (label != null || description != null) && /* @__PURE__ */ jsxs21("span", { className: "fds-checkbox__text", children: [
1391
+ label != null && /* @__PURE__ */ jsx25("span", { className: "fds-checkbox__label", children: label }),
1392
+ description != null && /* @__PURE__ */ jsx25("span", { className: "fds-checkbox__description", children: description })
1393
+ ] })
1394
+ ] })
1395
+ ]
1396
+ }
1397
+ );
1398
+ });
1399
+
1400
+ // src/components/Radio/Radio.tsx
1401
+ import { forwardRef as forwardRef5, useId as useId8 } from "react";
1402
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
1403
+ var Radio = forwardRef5(function Radio2({ label, description, className, id, disabled, ...rest }, ref) {
1404
+ const generatedId = useId8();
1405
+ const inputId = id ?? generatedId;
1406
+ return /* @__PURE__ */ jsxs22("div", { className: ["fds-radio", className].filter(Boolean).join(" "), "data-disabled": disabled ? "" : void 0, children: [
1407
+ /* @__PURE__ */ jsx26("input", { ...rest, id: inputId, ref, type: "radio", disabled, className: "fds-radio__input" }),
1408
+ /* @__PURE__ */ jsxs22("label", { htmlFor: inputId, className: "fds-radio__control", children: [
1409
+ /* @__PURE__ */ jsx26("span", { className: "fds-radio__circle", "aria-hidden": "true" }),
1410
+ (label != null || description != null) && /* @__PURE__ */ jsxs22("span", { className: "fds-radio__text", children: [
1411
+ label != null && /* @__PURE__ */ jsx26("span", { className: "fds-radio__label", children: label }),
1412
+ description != null && /* @__PURE__ */ jsx26("span", { className: "fds-radio__description", children: description })
1413
+ ] })
1414
+ ] })
1415
+ ] });
1416
+ });
1417
+
1418
+ // src/components/Accordion/Accordion.tsx
1419
+ import { useId as useId9, useState as useState7 } from "react";
1420
+ import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
1421
+ function Accordion({
1422
+ summary,
1423
+ actions,
1424
+ open: openProp,
1425
+ defaultOpen = false,
1426
+ onOpenChange,
1427
+ children,
1428
+ className,
1429
+ ...rest
1430
+ }) {
1431
+ const [openState, setOpenState] = useState7(defaultOpen);
1432
+ const open = openProp ?? openState;
1433
+ const bodyId = useId9();
1434
+ const toggle = () => {
1435
+ const next = !open;
1436
+ if (openProp === void 0) setOpenState(next);
1437
+ onOpenChange?.(next);
1438
+ };
1439
+ return /* @__PURE__ */ jsxs23(
1440
+ "div",
1441
+ {
1442
+ className: ["fds-accordion", className].filter(Boolean).join(" "),
1443
+ "data-open": open ? "" : void 0,
1444
+ ...rest,
1445
+ children: [
1446
+ /* @__PURE__ */ jsxs23("div", { className: "fds-accordion__header", children: [
1447
+ /* @__PURE__ */ jsx27(
1448
+ "button",
1449
+ {
1450
+ type: "button",
1451
+ className: "fds-accordion__trigger",
1452
+ "aria-expanded": open,
1453
+ "aria-controls": bodyId,
1454
+ onClick: toggle,
1455
+ children: summary
1456
+ }
1457
+ ),
1458
+ actions != null && /* @__PURE__ */ jsx27("div", { className: "fds-accordion__actions", children: actions }),
1459
+ /* @__PURE__ */ jsx27("span", { className: "fds-accordion__chevron", "aria-hidden": "true", onClick: toggle, children: /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", children: /* @__PURE__ */ jsx27("path", { d: "M6 9l6 6 6-6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
1460
+ ] }),
1461
+ /* @__PURE__ */ jsx27("div", { id: bodyId, className: "fds-accordion__body", hidden: !open, children })
1462
+ ]
1463
+ }
1464
+ );
1465
+ }
1466
+
1467
+ // src/components/StepList/StepList.tsx
1468
+ import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
1469
+ var STATUS_TEXT = {
1470
+ done: "Done",
1471
+ active: "In progress",
1472
+ pending: "Not started",
1473
+ failed: "Failed"
1474
+ };
1475
+ function StepIcon({ status }) {
1476
+ const common = { viewBox: "0 0 24 24", width: 18, height: 18, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
1477
+ if (status === "done") {
1478
+ return /* @__PURE__ */ jsxs24("svg", { ...common, children: [
1479
+ /* @__PURE__ */ jsx28("circle", { cx: "12", cy: "12", r: "9" }),
1480
+ /* @__PURE__ */ jsx28("path", { d: "M8 12.5l2.5 2.5 5-5.5" })
1481
+ ] });
1482
+ }
1483
+ if (status === "active") {
1484
+ return /* @__PURE__ */ jsx28("svg", { ...common, className: "fds-steps__spinner", children: /* @__PURE__ */ jsx28("path", { d: "M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1" }) });
1485
+ }
1486
+ if (status === "failed") {
1487
+ return /* @__PURE__ */ jsxs24("svg", { ...common, children: [
1488
+ /* @__PURE__ */ jsx28("circle", { cx: "12", cy: "12", r: "9" }),
1489
+ /* @__PURE__ */ jsx28("path", { d: "M12 7.5v5.5M12 16.5v.01" })
1490
+ ] });
1491
+ }
1492
+ return /* @__PURE__ */ jsx28("svg", { ...common, children: /* @__PURE__ */ jsx28("circle", { cx: "12", cy: "12", r: "9" }) });
1493
+ }
1494
+ function StepList({ steps, className, ...rest }) {
1495
+ return /* @__PURE__ */ jsx28("ol", { className: ["fds-steps", className].filter(Boolean).join(" "), ...rest, children: steps.map((step) => /* @__PURE__ */ jsxs24(
1496
+ "li",
1497
+ {
1498
+ className: `fds-steps__step fds-steps__step--${step.status}`,
1499
+ "aria-current": step.status === "active" ? "step" : void 0,
1500
+ children: [
1501
+ /* @__PURE__ */ jsx28("span", { className: "fds-steps__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx28(StepIcon, { status: step.status }) }),
1502
+ /* @__PURE__ */ jsx28("span", { className: "fds-steps__label", children: step.label }),
1503
+ /* @__PURE__ */ jsxs24("span", { className: "fds-steps__sr", children: [
1504
+ ", ",
1505
+ STATUS_TEXT[step.status]
1506
+ ] })
1507
+ ]
1508
+ },
1509
+ step.id
1510
+ )) });
1511
+ }
1512
+
1513
+ // src/components/Input/Input.tsx
1514
+ import { forwardRef as forwardRef6, useId as useId10 } from "react";
1515
+ import { jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
1516
+ var Input = forwardRef6(function Input2({ label, hint, error, leadingIcon, trailing, id, className, containerClassName, readOnly, disabled, ...rest }, ref) {
1517
+ const genId = useId10();
1518
+ const inputId = id ?? genId;
1519
+ const msgId = `${genId}-msg`;
1520
+ const message = error ?? hint;
1521
+ const boxClasses = [
1522
+ "fds-input__box",
1523
+ error ? "fds-input__box--error" : "",
1524
+ readOnly || disabled ? "fds-input__box--locked" : ""
1525
+ ].filter(Boolean).join(" ");
1526
+ return /* @__PURE__ */ jsxs25("div", { className: ["fds-input", containerClassName].filter(Boolean).join(" "), children: [
1527
+ label != null && /* @__PURE__ */ jsx29("label", { htmlFor: inputId, className: "fds-input__label", children: label }),
1528
+ /* @__PURE__ */ jsxs25("div", { className: boxClasses, children: [
1529
+ leadingIcon != null && /* @__PURE__ */ jsx29("span", { className: "fds-input__icon", "aria-hidden": "true", children: leadingIcon }),
1530
+ /* @__PURE__ */ jsx29(
1531
+ "input",
1532
+ {
1533
+ ref,
1534
+ id: inputId,
1535
+ className: ["fds-input__control", className].filter(Boolean).join(" "),
1536
+ readOnly,
1537
+ disabled,
1538
+ "aria-invalid": error ? true : void 0,
1539
+ "aria-describedby": message != null ? msgId : void 0,
1540
+ ...rest
1541
+ }
1542
+ ),
1543
+ trailing != null && /* @__PURE__ */ jsx29("span", { className: "fds-input__trailing", children: trailing })
1544
+ ] }),
1545
+ message != null && /* @__PURE__ */ jsx29("p", { id: msgId, className: error ? "fds-input__error" : "fds-input__hint", children: message })
1546
+ ] });
1547
+ });
1548
+
1549
+ // src/components/IconButton/IconButton.tsx
1550
+ import { jsx as jsx30 } from "react/jsx-runtime";
1551
+ function IconButton({
1552
+ icon,
1553
+ variant = "outlined",
1554
+ size = "medium",
1555
+ type = "button",
1556
+ className,
1557
+ ...rest
1558
+ }) {
1559
+ const classes = ["fds-icon-button", `fds-icon-button--${variant}`, `fds-icon-button--${size}`, className].filter(Boolean).join(" ");
1560
+ return /* @__PURE__ */ jsx30("button", { type, className: classes, ...rest, children: /* @__PURE__ */ jsx30("span", { className: "fds-icon-button__icon", "aria-hidden": "true", children: icon }) });
1561
+ }
1562
+
1563
+ // src/components/Menu/Menu.tsx
1564
+ import {
1565
+ cloneElement,
1566
+ useEffect as useEffect6,
1567
+ useId as useId11,
1568
+ useRef as useRef7,
1569
+ useState as useState8
1570
+ } from "react";
1571
+ import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
1572
+ function Menu({ trigger, items, align = "end", className }) {
1573
+ const listId = useId11();
1574
+ const rootRef = useRef7(null);
1575
+ const listRef = useRef7(null);
1576
+ const [open, setOpen] = useState8(false);
1577
+ const [active, setActive] = useState8(0);
1578
+ const enabled = items.map((it, i) => it.disabled ? -1 : i).filter((i) => i >= 0);
1579
+ useEffect6(() => {
1580
+ if (!open) return;
1581
+ const onDown = (e) => {
1582
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
1583
+ };
1584
+ document.addEventListener("mousedown", onDown);
1585
+ const raf = requestAnimationFrame(() => listRef.current?.focus());
1586
+ return () => {
1587
+ document.removeEventListener("mousedown", onDown);
1588
+ cancelAnimationFrame(raf);
1589
+ };
1590
+ }, [open]);
1591
+ const triggerEl = () => rootRef.current?.querySelector('[aria-haspopup="menu"]');
1592
+ const close = () => {
1593
+ setOpen(false);
1594
+ triggerEl()?.focus();
1595
+ };
1596
+ const openAt = (index) => {
1597
+ setActive(index);
1598
+ setOpen(true);
1599
+ };
1600
+ const choose = (i) => {
1601
+ const it = items[i];
1602
+ if (!it || it.disabled) return;
1603
+ close();
1604
+ it.onSelect();
1605
+ };
1606
+ const step = (dir) => {
1607
+ if (enabled.length === 0) return;
1608
+ const at = enabled.indexOf(active);
1609
+ setActive(enabled[(at + dir + enabled.length) % enabled.length]);
1610
+ };
1611
+ const onListKeyDown = (e) => {
1612
+ if (e.key === "ArrowDown") {
1613
+ e.preventDefault();
1614
+ step(1);
1615
+ } else if (e.key === "ArrowUp") {
1616
+ e.preventDefault();
1617
+ step(-1);
1618
+ } else if (e.key === "Home") {
1619
+ e.preventDefault();
1620
+ setActive(enabled[0] ?? 0);
1621
+ } else if (e.key === "End") {
1622
+ e.preventDefault();
1623
+ setActive(enabled[enabled.length - 1] ?? 0);
1624
+ } else if (e.key === "Enter" || e.key === " ") {
1625
+ e.preventDefault();
1626
+ choose(active);
1627
+ } else if (e.key === "Escape") {
1628
+ e.preventDefault();
1629
+ close();
1630
+ } else if (e.key === "Tab") setOpen(false);
1631
+ };
1632
+ const own = trigger.props;
1633
+ const wired = cloneElement(trigger, {
1634
+ "aria-haspopup": "menu",
1635
+ "aria-expanded": open,
1636
+ "aria-controls": open ? listId : void 0,
1637
+ onClick: (e) => {
1638
+ own.onClick?.(e);
1639
+ if (open) setOpen(false);
1640
+ else openAt(enabled[0] ?? 0);
1641
+ },
1642
+ onKeyDown: (e) => {
1643
+ own.onKeyDown?.(e);
1644
+ if (!open && (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ")) {
1645
+ e.preventDefault();
1646
+ openAt(enabled[0] ?? 0);
1647
+ } else if (!open && e.key === "ArrowUp") {
1648
+ e.preventDefault();
1649
+ openAt(enabled[enabled.length - 1] ?? 0);
1650
+ }
1651
+ }
1652
+ });
1653
+ return /* @__PURE__ */ jsxs26("div", { ref: rootRef, className: ["fds-menu", className].filter(Boolean).join(" "), children: [
1654
+ wired,
1655
+ open && /* @__PURE__ */ jsx31(
1656
+ "div",
1657
+ {
1658
+ ref: listRef,
1659
+ id: listId,
1660
+ role: "menu",
1661
+ tabIndex: -1,
1662
+ className: `fds-menu__list fds-menu__list--${align}`,
1663
+ "aria-activedescendant": `${listId}-item-${active}`,
1664
+ onKeyDown: onListKeyDown,
1665
+ children: items.map((it, i) => /* @__PURE__ */ jsxs26(
1666
+ "div",
1667
+ {
1668
+ id: `${listId}-item-${i}`,
1669
+ role: "menuitem",
1670
+ "aria-disabled": it.disabled || void 0,
1671
+ className: [
1672
+ "fds-menu__item",
1673
+ it.tone === "danger" ? "fds-menu__item--danger" : "",
1674
+ i === active ? "fds-menu__item--active" : "",
1675
+ it.disabled ? "fds-menu__item--disabled" : ""
1676
+ ].filter(Boolean).join(" "),
1677
+ onMouseEnter: () => !it.disabled && setActive(i),
1678
+ onMouseDown: (e) => e.preventDefault(),
1679
+ onClick: () => choose(i),
1680
+ children: [
1681
+ it.icon != null && /* @__PURE__ */ jsx31("span", { className: "fds-menu__icon", "aria-hidden": "true", children: it.icon }),
1682
+ it.label
1683
+ ]
1684
+ },
1685
+ i
1686
+ ))
1687
+ }
1688
+ )
1689
+ ] });
1690
+ }
1691
+
1692
+ // src/components/Table/Table.tsx
1693
+ import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
1694
+ function Table({ columns, rows, rowKey, empty, className, "aria-label": ariaLabel, ...rest }) {
1695
+ return /* @__PURE__ */ jsx32("div", { className: ["fds-table", className].filter(Boolean).join(" "), ...rest, children: /* @__PURE__ */ jsxs27("table", { className: "fds-table__table", "aria-label": ariaLabel, children: [
1696
+ /* @__PURE__ */ jsx32("thead", { children: /* @__PURE__ */ jsx32("tr", { children: columns.map((c) => /* @__PURE__ */ jsx32(
1697
+ "th",
1698
+ {
1699
+ scope: "col",
1700
+ className: c.align === "end" ? "fds-table__cell--end" : void 0,
1701
+ style: c.width ? { width: c.width } : void 0,
1702
+ children: c.header
1703
+ },
1704
+ c.key
1705
+ )) }) }),
1706
+ /* @__PURE__ */ jsxs27("tbody", { children: [
1707
+ rows.map((row) => /* @__PURE__ */ jsx32("tr", { children: columns.map((c) => /* @__PURE__ */ jsx32("td", { className: c.align === "end" ? "fds-table__cell--end" : void 0, children: c.cell(row) }, c.key)) }, rowKey(row))),
1708
+ rows.length === 0 && empty != null && /* @__PURE__ */ jsx32("tr", { children: /* @__PURE__ */ jsx32("td", { colSpan: columns.length, className: "fds-table__empty", children: empty }) })
1709
+ ] })
1710
+ ] }) });
1711
+ }
1712
+
1713
+ // src/components/PricingCard/PricingCard.tsx
1714
+ import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
1715
+ function CheckIcon3() {
1716
+ return /* @__PURE__ */ jsx33("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx33("path", { d: "M3.5 8.5l3 3 6-7", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) });
1717
+ }
1718
+ function PricingCard({
1719
+ eyebrow,
1720
+ title,
1721
+ badge,
1722
+ price,
1723
+ period,
1724
+ description,
1725
+ action,
1726
+ featuresTitle = "Features",
1727
+ featuresIntro,
1728
+ features = [],
1729
+ highlighted = false,
1730
+ className,
1731
+ ...rest
1732
+ }) {
1733
+ const classes = ["fds-pricing-card", highlighted ? "fds-pricing-card--highlighted" : "", className].filter(Boolean).join(" ");
1734
+ return /* @__PURE__ */ jsxs28("article", { className: classes, ...rest, children: [
1735
+ /* @__PURE__ */ jsxs28("div", { className: "fds-pricing-card__top", children: [
1736
+ eyebrow != null && /* @__PURE__ */ jsx33("span", { className: "fds-pricing-card__eyebrow", children: eyebrow }),
1737
+ /* @__PURE__ */ jsxs28("h3", { className: "fds-pricing-card__title", children: [
1738
+ title,
1739
+ badge
1740
+ ] }),
1741
+ /* @__PURE__ */ jsxs28("p", { className: "fds-pricing-card__price", children: [
1742
+ /* @__PURE__ */ jsx33("span", { className: "fds-pricing-card__amount", children: price }),
1743
+ period != null && /* @__PURE__ */ jsx33("span", { className: "fds-pricing-card__period", children: period })
1744
+ ] }),
1745
+ description != null && /* @__PURE__ */ jsx33("p", { className: "fds-pricing-card__description", children: description }),
1746
+ action != null && /* @__PURE__ */ jsx33("div", { className: "fds-pricing-card__action", children: action })
1747
+ ] }),
1748
+ features.length > 0 && /* @__PURE__ */ jsxs28("div", { className: "fds-pricing-card__features", children: [
1749
+ /* @__PURE__ */ jsx33("p", { className: "fds-pricing-card__featuresTitle", children: featuresTitle }),
1750
+ featuresIntro != null && /* @__PURE__ */ jsx33("p", { className: "fds-pricing-card__intro", children: featuresIntro }),
1751
+ /* @__PURE__ */ jsx33("ul", { className: "fds-pricing-card__list", children: features.map((f, i) => /* @__PURE__ */ jsxs28("li", { className: "fds-pricing-card__feature", children: [
1752
+ /* @__PURE__ */ jsx33("span", { className: "fds-pricing-card__check", children: /* @__PURE__ */ jsx33(CheckIcon3, {}) }),
1753
+ f
1754
+ ] }, i)) })
1755
+ ] })
1756
+ ] });
1757
+ }
1264
1758
  export {
1759
+ Accordion,
1265
1760
  AppNav,
1266
1761
  Avatar,
1267
1762
  Badge,
1268
1763
  Button,
1269
1764
  ButtonLink,
1270
1765
  Callout,
1766
+ Card,
1767
+ Checkbox,
1271
1768
  DiffPropertyList,
1272
1769
  DiffSection,
1273
1770
  EmptyState,
1771
+ IconButton,
1772
+ Input,
1773
+ Menu,
1274
1774
  MismatchCard,
1275
1775
  Modal,
1276
1776
  MultiSelect,
1277
1777
  Pagination,
1778
+ PricingCard,
1779
+ Radio,
1278
1780
  SearchInput,
1279
1781
  SectionHeader,
1280
1782
  SegmentedControl,
@@ -1284,7 +1786,10 @@ export {
1284
1786
  SkeletonGroup,
1285
1787
  Split,
1286
1788
  StackedCardsIllustration,
1789
+ Stat,
1790
+ StepList,
1287
1791
  TabNav,
1792
+ Table,
1288
1793
  Tag,
1289
1794
  Toggle,
1290
1795
  breakpoints
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefidel/design-system",
3
- "version": "0.17.0",
3
+ "version": "0.19.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",