@usefidel/design-system 0.13.0 → 0.14.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.
@@ -10,6 +10,6 @@
10
10
  "webapp.css"
11
11
  ],
12
12
  "storybookUrl": null,
13
- "packageVersion": "0.13.0",
14
- "resolvedSha": "a9ed7fb5a355ee414279eb17bf75df5cc18a7dc1"
13
+ "packageVersion": "0.14.0",
14
+ "resolvedSha": "2c6d6c9b1c4ca4dc54ec8a92f2a9d2d60dcaf34d"
15
15
  }
package/dist/index.cjs CHANGED
@@ -20,12 +20,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ AppNav: () => AppNav,
23
24
  Button: () => Button,
24
25
  DiffPropertyList: () => DiffPropertyList,
25
26
  DiffSection: () => DiffSection,
26
27
  MismatchCard: () => MismatchCard,
28
+ Modal: () => Modal,
29
+ SegmentedControl: () => SegmentedControl,
27
30
  Select: () => Select,
28
- Toggle: () => Toggle
31
+ SettingsSection: () => SettingsSection,
32
+ TabNav: () => TabNav,
33
+ Toggle: () => Toggle,
34
+ breakpoints: () => breakpoints
29
35
  });
30
36
  module.exports = __toCommonJS(index_exports);
31
37
 
@@ -594,12 +600,285 @@ var Toggle = (0, import_react4.forwardRef)(function Toggle2({ size = "medium", l
594
600
  ] })
595
601
  ] });
596
602
  });
603
+
604
+ // src/components/AppNav/AppNav.tsx
605
+ var import_react5 = require("react");
606
+ var import_jsx_runtime6 = require("react/jsx-runtime");
607
+ function AppNav({
608
+ logo,
609
+ actions,
610
+ children,
611
+ menuLabel = "Menu",
612
+ className,
613
+ ...rest
614
+ }) {
615
+ const [open, setOpen] = (0, import_react5.useState)(false);
616
+ const linksId = (0, import_react5.useId)();
617
+ const navRef = (0, import_react5.useRef)(null);
618
+ const toggleRef = (0, import_react5.useRef)(null);
619
+ (0, import_react5.useEffect)(() => {
620
+ if (!open) return;
621
+ const onKey = (e) => {
622
+ if (e.key === "Escape") {
623
+ setOpen(false);
624
+ toggleRef.current?.focus();
625
+ }
626
+ };
627
+ const onPointer = (e) => {
628
+ if (!navRef.current?.contains(e.target)) setOpen(false);
629
+ };
630
+ window.addEventListener("keydown", onKey);
631
+ document.addEventListener("mousedown", onPointer);
632
+ return () => {
633
+ window.removeEventListener("keydown", onKey);
634
+ document.removeEventListener("mousedown", onPointer);
635
+ };
636
+ }, [open]);
637
+ const classes = ["fds-app-nav", className].filter(Boolean).join(" ");
638
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("nav", { ref: navRef, className: classes, ...rest, children: [
639
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "fds-app-nav__logo", children: logo }),
640
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
641
+ "div",
642
+ {
643
+ id: linksId,
644
+ className: "fds-app-nav__links",
645
+ "data-open": open ? "" : void 0,
646
+ onClick: (e) => {
647
+ if (e.target.closest("a, button")) setOpen(false);
648
+ },
649
+ children
650
+ }
651
+ ),
652
+ actions != null && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "fds-app-nav__actions", children: actions }),
653
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
654
+ "button",
655
+ {
656
+ ref: toggleRef,
657
+ type: "button",
658
+ className: "fds-app-nav__toggle",
659
+ "aria-label": menuLabel,
660
+ "aria-expanded": open,
661
+ "aria-controls": linksId,
662
+ onClick: () => setOpen((v) => !v),
663
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: open ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M5 5l10 10M15 5L5 15", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M3 6h14M3 10h14M3 14h14", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }) })
664
+ }
665
+ )
666
+ ] });
667
+ }
668
+
669
+ // src/components/TabNav/TabNav.tsx
670
+ var import_jsx_runtime7 = require("react/jsx-runtime");
671
+ function TabNav({ children, className, ...rest }) {
672
+ const classes = ["fds-tab-nav", className].filter(Boolean).join(" ");
673
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("nav", { className: classes, ...rest, children });
674
+ }
675
+
676
+ // src/components/SettingsSection/SettingsSection.tsx
677
+ var import_react6 = require("react");
678
+ var import_jsx_runtime8 = require("react/jsx-runtime");
679
+ function SettingsSection({
680
+ title,
681
+ description,
682
+ children,
683
+ className,
684
+ ...rest
685
+ }) {
686
+ const titleId = (0, import_react6.useId)();
687
+ const classes = ["fds-settings-section", className].filter(Boolean).join(" ");
688
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { className: classes, "aria-labelledby": titleId, ...rest, children: [
689
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "fds-settings-section__head", children: [
690
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { id: titleId, className: "fds-settings-section__title", children: title }),
691
+ description != null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "fds-settings-section__description", children: description })
692
+ ] }),
693
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "fds-settings-section__body", children })
694
+ ] });
695
+ }
696
+
697
+ // src/components/Modal/Modal.tsx
698
+ var import_react7 = require("react");
699
+ var import_react_dom = require("react-dom");
700
+ var import_jsx_runtime9 = require("react/jsx-runtime");
701
+ var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
702
+ function Modal({
703
+ open,
704
+ onClose,
705
+ title,
706
+ description,
707
+ children,
708
+ actions,
709
+ size = "small",
710
+ dismissible = true,
711
+ tone = "default",
712
+ className
713
+ }) {
714
+ const titleId = (0, import_react7.useId)();
715
+ const descriptionId = (0, import_react7.useId)();
716
+ const dialogRef = (0, import_react7.useRef)(null);
717
+ const onCloseRef = (0, import_react7.useRef)(onClose);
718
+ onCloseRef.current = onClose;
719
+ const dismissibleRef = (0, import_react7.useRef)(dismissible);
720
+ dismissibleRef.current = dismissible;
721
+ (0, import_react7.useEffect)(() => {
722
+ if (!open) return;
723
+ const dialog = dialogRef.current;
724
+ const previouslyFocused = document.activeElement;
725
+ const initial = dialog?.querySelector("[data-autofocus]") ?? dialog?.querySelector(`.fds-modal__content ${FOCUSABLE}, .fds-modal__actions ${FOCUSABLE}`) ?? dialog;
726
+ initial?.focus();
727
+ const previousOverflow = document.body.style.overflow;
728
+ document.body.style.overflow = "hidden";
729
+ const onKey = (e) => {
730
+ if (e.key === "Escape" && dismissibleRef.current) {
731
+ e.preventDefault();
732
+ onCloseRef.current();
733
+ return;
734
+ }
735
+ if (e.key === "Tab" && dialog) {
736
+ const focusable = Array.from(dialog.querySelectorAll(FOCUSABLE));
737
+ if (focusable.length === 0) {
738
+ e.preventDefault();
739
+ return;
740
+ }
741
+ const first = focusable[0];
742
+ const last = focusable[focusable.length - 1];
743
+ if (e.shiftKey && (document.activeElement === first || document.activeElement === dialog)) {
744
+ e.preventDefault();
745
+ last.focus();
746
+ } else if (!e.shiftKey && document.activeElement === last) {
747
+ e.preventDefault();
748
+ first.focus();
749
+ }
750
+ }
751
+ };
752
+ window.addEventListener("keydown", onKey);
753
+ return () => {
754
+ window.removeEventListener("keydown", onKey);
755
+ document.body.style.overflow = previousOverflow;
756
+ previouslyFocused?.focus?.();
757
+ };
758
+ }, [open]);
759
+ if (!open || typeof document === "undefined") return null;
760
+ const classes = ["fds-modal", `fds-modal--${size}`, tone === "danger" && "fds-modal--danger", className].filter(Boolean).join(" ");
761
+ return (0, import_react_dom.createPortal)(
762
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
763
+ "div",
764
+ {
765
+ className: "fds-modal__scrim",
766
+ onMouseDown: (e) => {
767
+ if (e.target === e.currentTarget && dismissible) onClose();
768
+ },
769
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
770
+ "div",
771
+ {
772
+ ref: dialogRef,
773
+ className: classes,
774
+ role: "dialog",
775
+ "aria-modal": "true",
776
+ "aria-labelledby": titleId,
777
+ "aria-describedby": description != null ? descriptionId : void 0,
778
+ tabIndex: -1,
779
+ children: [
780
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
781
+ "button",
782
+ {
783
+ type: "button",
784
+ className: "fds-modal__close",
785
+ onClick: onClose,
786
+ disabled: !dismissible,
787
+ "aria-label": "Close",
788
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M4 4l8 8M12 4l-8 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
789
+ }
790
+ ),
791
+ tone === "danger" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "fds-modal__icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [
792
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M10 3.5L17.5 16.5H2.5L10 3.5z", stroke: "currentColor", strokeWidth: "1.5", strokeLinejoin: "round" }),
793
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M10 8.5v3.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
794
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "10", cy: "14.25", r: "0.9", fill: "currentColor" })
795
+ ] }) }),
796
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h2", { id: titleId, className: "fds-modal__title", children: title }),
797
+ description != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { id: descriptionId, className: "fds-modal__description", children: description }),
798
+ children != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "fds-modal__content", children }),
799
+ actions != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "fds-modal__actions", children: actions })
800
+ ]
801
+ }
802
+ )
803
+ }
804
+ ),
805
+ document.body
806
+ );
807
+ }
808
+
809
+ // src/components/SegmentedControl/SegmentedControl.tsx
810
+ var import_react8 = require("react");
811
+ var import_jsx_runtime10 = require("react/jsx-runtime");
812
+ function SegmentedControl({
813
+ options,
814
+ value,
815
+ onChange,
816
+ "aria-label": ariaLabel,
817
+ className
818
+ }) {
819
+ const refs = (0, import_react8.useRef)([]);
820
+ const enabled = options.map((o, i) => o.disabled ? -1 : i).filter((i) => i >= 0);
821
+ const onKeyDown = (e, index) => {
822
+ const at = enabled.indexOf(index);
823
+ let next;
824
+ if (e.key === "ArrowRight") next = enabled[(at + 1) % enabled.length];
825
+ else if (e.key === "ArrowLeft") next = enabled[(at - 1 + enabled.length) % enabled.length];
826
+ else if (e.key === "Home") next = enabled[0];
827
+ else if (e.key === "End") next = enabled[enabled.length - 1];
828
+ if (next === void 0) return;
829
+ e.preventDefault();
830
+ onChange(options[next].value);
831
+ refs.current[next]?.focus();
832
+ };
833
+ const classes = ["fds-segmented", className].filter(Boolean).join(" ");
834
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { role: "tablist", "aria-label": ariaLabel, className: classes, children: options.map((option, i) => {
835
+ const selected = option.value === value;
836
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
837
+ "button",
838
+ {
839
+ ref: (el) => {
840
+ refs.current[i] = el;
841
+ },
842
+ type: "button",
843
+ role: "tab",
844
+ "aria-selected": selected,
845
+ "aria-controls": option.controls,
846
+ tabIndex: selected ? 0 : -1,
847
+ disabled: option.disabled,
848
+ className: "fds-segmented__option",
849
+ onClick: () => onChange(option.value),
850
+ onKeyDown: (e) => onKeyDown(e, i),
851
+ children: [
852
+ option.label,
853
+ option.count != null && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "fds-segmented__count", children: option.count })
854
+ ]
855
+ },
856
+ option.value
857
+ );
858
+ }) });
859
+ }
860
+
861
+ // src/breakpoints.ts
862
+ var breakpoints = {
863
+ /** Phones. */
864
+ sm: 480,
865
+ /** Large phones, small tablets. */
866
+ md: 640,
867
+ /** Tablets, narrow windows. */
868
+ lg: 900
869
+ };
597
870
  // Annotate the CommonJS export names for ESM import in node:
598
871
  0 && (module.exports = {
872
+ AppNav,
599
873
  Button,
600
874
  DiffPropertyList,
601
875
  DiffSection,
602
876
  MismatchCard,
877
+ Modal,
878
+ SegmentedControl,
603
879
  Select,
604
- Toggle
880
+ SettingsSection,
881
+ TabNav,
882
+ Toggle,
883
+ breakpoints
605
884
  });