@salemcobalt/ui 0.2.0-rc.3 → 0.2.0-rc.30

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
@@ -14,6 +14,8 @@ function Button({
14
14
  iconEnd,
15
15
  loading = false,
16
16
  fullWidth = false,
17
+ shortcut,
18
+ iconOnly = false,
17
19
  type = "button",
18
20
  className,
19
21
  children,
@@ -36,6 +38,7 @@ function Button({
36
38
  "data-size": size,
37
39
  "data-loading": loading || void 0,
38
40
  "data-full-width": fullWidth || void 0,
41
+ "data-icon-only": iconOnly || void 0,
39
42
  "aria-busy": loading || void 0,
40
43
  onClick: handleClick,
41
44
  ...rest,
@@ -43,6 +46,7 @@ function Button({
43
46
  iconStart ? /* @__PURE__ */ jsx("span", { className: "sc-button__icon", "aria-hidden": "true", children: iconStart }) : null,
44
47
  /* @__PURE__ */ jsx("span", { className: "sc-button__label", children }),
45
48
  iconEnd ? /* @__PURE__ */ jsx("span", { className: "sc-button__icon", "aria-hidden": "true", children: iconEnd }) : null,
49
+ shortcut ? /* @__PURE__ */ jsx("span", { className: "sc-button__shortcut", "aria-hidden": "true", children: shortcut }) : null,
46
50
  loading ? /* @__PURE__ */ jsx("span", { className: "sc-button__spinner", "aria-hidden": "true" }) : null
47
51
  ]
48
52
  }
@@ -163,6 +167,141 @@ var Kbd = Object.assign(KbdBase, { Group: KbdGroup });
163
167
 
164
168
  // src/components/Dialog/Dialog.tsx
165
169
  import { Dialog as BaseDialog } from "@base-ui/react/dialog";
170
+ import { createContext, useCallback, useContext } from "react";
171
+
172
+ // src/components/Dialog/sheetSwipe.ts
173
+ var SHEET_SWIPE = {
174
+ /** Height in px, from the sheet's top edge, where a drag may start: the handle and the header. */
175
+ zone: 64,
176
+ /** Movement in px before a press becomes a drag, so taps stay taps. */
177
+ slop: 6,
178
+ /** Fraction of the sheet's height past which a release dismisses. */
179
+ distance: 0.3,
180
+ /** Release speed in px/ms: this fast downward dismisses at any distance, this fast upward keeps it open. */
181
+ velocity: 0.5,
182
+ /** Only samples this recent (ms) before the release count toward its speed. */
183
+ window: 100
184
+ };
185
+ function dragIntent(dx, dy, slop = SHEET_SWIPE.slop) {
186
+ if (Math.abs(dx) < slop && Math.abs(dy) < slop) return "pending";
187
+ return dy > 0 && dy >= Math.abs(dx) ? "drag" : "cancel";
188
+ }
189
+ function releaseVelocity(samples, window2 = SHEET_SWIPE.window) {
190
+ const last = samples[samples.length - 1];
191
+ if (!last) return 0;
192
+ const first = samples.find((s) => last.t - s.t <= window2) ?? last;
193
+ const dt = last.t - first.t;
194
+ return dt > 0 ? (last.y - first.y) / dt : 0;
195
+ }
196
+ function shouldDismissSheet({ offset, height, velocity }) {
197
+ if (offset <= 0 || height <= 0) return false;
198
+ if (velocity >= SHEET_SWIPE.velocity) return true;
199
+ if (velocity <= -SHEET_SWIPE.velocity) return false;
200
+ return offset >= height * SHEET_SWIPE.distance;
201
+ }
202
+ var IGNORE = 'a,button,input,select,textarea,label,[role="button"],[contenteditable]';
203
+ function attachSheetSwipe(handle) {
204
+ const popup = handle.parentElement;
205
+ if (!popup) return () => {
206
+ };
207
+ let press = null;
208
+ let dragging = false;
209
+ let height = 0;
210
+ let samples = [];
211
+ let suppressClick = false;
212
+ const startsDrag = (e) => {
213
+ if (!e.isPrimary || e.pointerType === "mouse" && e.button !== 0) return false;
214
+ if (handle.getClientRects().length === 0) return false;
215
+ const target = e.target instanceof Element ? e.target : null;
216
+ if (!target || handle.contains(target)) return true;
217
+ if (target.closest(IGNORE) || target.closest(".sc-dialog__body")) return false;
218
+ return e.clientY - popup.getBoundingClientRect().top <= SHEET_SWIPE.zone;
219
+ };
220
+ const settle = () => {
221
+ delete popup.dataset.swiping;
222
+ popup.style.removeProperty("transform");
223
+ };
224
+ const onDown = (e) => {
225
+ if (press || !startsDrag(e)) return;
226
+ press = { id: e.pointerId, x: e.clientX, y: e.clientY };
227
+ samples = [{ t: e.timeStamp, y: e.clientY }];
228
+ };
229
+ const onMove = (e) => {
230
+ if (!press || e.pointerId !== press.id) return;
231
+ const dy = e.clientY - press.y;
232
+ if (!dragging) {
233
+ const intent = dragIntent(e.clientX - press.x, dy);
234
+ if (intent === "pending") return;
235
+ if (intent === "cancel") {
236
+ press = null;
237
+ return;
238
+ }
239
+ dragging = true;
240
+ height = popup.getBoundingClientRect().height;
241
+ popup.dataset.swiping = "";
242
+ popup.setPointerCapture?.(e.pointerId);
243
+ window.getSelection()?.removeAllRanges();
244
+ }
245
+ e.preventDefault();
246
+ samples.push({ t: e.timeStamp, y: e.clientY });
247
+ if (samples.length > 32) samples = samples.slice(-16);
248
+ popup.style.transform = `translateY(${Math.max(0, dy)}px)`;
249
+ };
250
+ const end = (e, cancelled) => {
251
+ if (!press || e.pointerId !== press.id) return;
252
+ const startY = press.y;
253
+ const wasDragging = dragging;
254
+ press = null;
255
+ dragging = false;
256
+ if (!wasDragging) return;
257
+ window.setTimeout(() => {
258
+ suppressClick = false;
259
+ }, 0);
260
+ if (cancelled) {
261
+ suppressClick = true;
262
+ settle();
263
+ return;
264
+ }
265
+ samples.push({ t: e.timeStamp, y: e.clientY });
266
+ const release = {
267
+ offset: Math.max(0, e.clientY - startY),
268
+ height,
269
+ velocity: releaseVelocity(samples)
270
+ };
271
+ if (shouldDismissSheet(release)) {
272
+ delete popup.dataset.swiping;
273
+ handle.click();
274
+ suppressClick = true;
275
+ window.requestAnimationFrame(() => {
276
+ if (popup.isConnected && popup.hasAttribute("data-open")) settle();
277
+ });
278
+ } else {
279
+ suppressClick = true;
280
+ settle();
281
+ }
282
+ };
283
+ const onUp = (e) => end(e, false);
284
+ const onCancel = (e) => end(e, true);
285
+ const onClick = (e) => {
286
+ if (!suppressClick) return;
287
+ suppressClick = false;
288
+ e.preventDefault();
289
+ e.stopPropagation();
290
+ };
291
+ popup.addEventListener("pointerdown", onDown);
292
+ popup.addEventListener("pointermove", onMove);
293
+ popup.addEventListener("pointerup", onUp);
294
+ popup.addEventListener("pointercancel", onCancel);
295
+ popup.addEventListener("click", onClick, true);
296
+ return () => {
297
+ popup.removeEventListener("pointerdown", onDown);
298
+ popup.removeEventListener("pointermove", onMove);
299
+ popup.removeEventListener("pointerup", onUp);
300
+ popup.removeEventListener("pointercancel", onCancel);
301
+ popup.removeEventListener("click", onClick, true);
302
+ settle();
303
+ };
304
+ }
166
305
 
167
306
  // src/components/Icon/Icon.tsx
168
307
  import { jsx as jsx4 } from "react/jsx-runtime";
@@ -292,6 +431,14 @@ function EyeIcon(props) {
292
431
  /* @__PURE__ */ jsx5("circle", { cx: "8", cy: "8", r: "2" })
293
432
  ] });
294
433
  }
434
+ function EyeOffIcon(props) {
435
+ return /* @__PURE__ */ jsxs4(Icon, { ...props, children: [
436
+ /* @__PURE__ */ jsx5("path", { d: "M6.37 3.99C6.87 3.84 7.42 3.75 8 3.75C12 3.75 14.25 8 14.25 8C14.25 8 13.65 9.13 12.53 10.22" }),
437
+ /* @__PURE__ */ jsx5("path", { d: "M9.63 12.01C9.13 12.16 8.58 12.25 8 12.25C4 12.25 1.75 8 1.75 8C1.75 8 2.35 6.87 3.47 5.78" }),
438
+ /* @__PURE__ */ jsx5("path", { d: "M9.41 9.41A2 2 0 0 1 6.59 6.59" }),
439
+ /* @__PURE__ */ jsx5("path", { d: "M2.5 2.5l11 11" })
440
+ ] });
441
+ }
295
442
  function FilterIcon(props) {
296
443
  return /* @__PURE__ */ jsx5(Icon, { ...props, children: /* @__PURE__ */ jsx5("path", { d: "M2.5 3h11l-4.25 5v4.25L6.75 13.5V8z" }) });
297
444
  }
@@ -438,7 +585,20 @@ function XIcon(props) {
438
585
 
439
586
  // src/components/Dialog/Dialog.tsx
440
587
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
441
- function Root({ dismissible = true, onOpenChange, open, defaultOpen, children }) {
588
+ var REQUESTABLE = /* @__PURE__ */ new Set([
589
+ "escape-key",
590
+ "outside-press",
591
+ "close-press"
592
+ ]);
593
+ var DismissibleContext = createContext(true);
594
+ function Root({
595
+ dismissible = true,
596
+ onOpenChange,
597
+ onRequestClose,
598
+ open,
599
+ defaultOpen,
600
+ children
601
+ }) {
442
602
  return /* @__PURE__ */ jsx6(
443
603
  BaseDialog.Root,
444
604
  {
@@ -450,28 +610,56 @@ function Root({ dismissible = true, onOpenChange, open, defaultOpen, children })
450
610
  details.cancel();
451
611
  return;
452
612
  }
613
+ if (!next && onRequestClose && REQUESTABLE.has(details.reason)) {
614
+ details.cancel();
615
+ onRequestClose(details.reason);
616
+ return;
617
+ }
453
618
  onOpenChange?.(next);
454
619
  },
455
- children
620
+ children: /* @__PURE__ */ jsx6(DismissibleContext.Provider, { value: dismissible, children })
456
621
  }
457
622
  );
458
623
  }
459
624
  var Trigger = BaseDialog.Trigger;
460
- function Content({ size = "md", className, children, ...rest }) {
625
+ function Content({
626
+ size = "md",
627
+ variant = "dialog",
628
+ dismissOnSwipe = true,
629
+ className,
630
+ children,
631
+ ...rest
632
+ }) {
633
+ const dismissible = useContext(DismissibleContext);
634
+ const isSheet = variant === "sheet";
635
+ const swipe = isSheet && dismissOnSwipe && dismissible;
461
636
  return /* @__PURE__ */ jsxs5(BaseDialog.Portal, { children: [
462
- /* @__PURE__ */ jsx6(BaseDialog.Backdrop, { className: "sc-dialog__backdrop" }),
463
- /* @__PURE__ */ jsx6(
637
+ /* @__PURE__ */ jsx6(BaseDialog.Backdrop, { className: "sc-dialog__backdrop", forceRender: true }),
638
+ /* @__PURE__ */ jsxs5(
464
639
  BaseDialog.Popup,
465
640
  {
466
641
  className: cx("sc-dialog__popup", className),
467
642
  "data-size": size,
643
+ "data-variant": variant,
644
+ "data-swipe-dismiss": swipe ? "" : void 0,
468
645
  "aria-modal": "true",
469
646
  ...rest,
470
- children
647
+ children: [
648
+ isSheet && /* @__PURE__ */ jsx6(SheetHandle, { swipe }),
649
+ children
650
+ ]
471
651
  }
472
652
  )
473
653
  ] });
474
654
  }
655
+ function SheetHandle({ swipe }) {
656
+ const attach = useCallback(
657
+ (node) => node ? attachSheetSwipe(node) : void 0,
658
+ []
659
+ );
660
+ if (!swipe) return /* @__PURE__ */ jsx6("div", { className: "sc-dialog__handle", "aria-hidden": "true" });
661
+ return /* @__PURE__ */ jsx6(BaseDialog.Close, { ref: attach, className: "sc-dialog__handle", "aria-label": "Dismiss" });
662
+ }
475
663
  function Title({ className, ...rest }) {
476
664
  return /* @__PURE__ */ jsx6(BaseDialog.Title, { className: cx("sc-dialog__title", className), ...rest });
477
665
  }
@@ -516,6 +704,7 @@ var iconNames = [
516
704
  "edit",
517
705
  "external-link",
518
706
  "eye",
707
+ "eye-off",
519
708
  "filter",
520
709
  "flag",
521
710
  "inbox",
@@ -545,6 +734,9 @@ var iconNames = [
545
734
  "x"
546
735
  ];
547
736
 
737
+ // src/components/IconButton/IconButton.tsx
738
+ import { useRender as useRender2 } from "@base-ui/react/use-render";
739
+
548
740
  // src/components/Tooltip/Tooltip.tsx
549
741
  import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
550
742
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
@@ -582,7 +774,7 @@ var Provider = BaseTooltip.Provider;
582
774
  var Tooltip = Object.assign(TooltipBase, { Provider });
583
775
 
584
776
  // src/components/IconButton/IconButton.tsx
585
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
777
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
586
778
  function IconButton({
587
779
  label,
588
780
  children,
@@ -592,9 +784,12 @@ function IconButton({
592
784
  tooltipSide = "top",
593
785
  round = false,
594
786
  loading = false,
595
- type = "button",
787
+ dot,
788
+ type,
596
789
  className,
597
790
  onClick,
791
+ render,
792
+ ref,
598
793
  ...rest
599
794
  }) {
600
795
  const handleClick = (event) => {
@@ -604,10 +799,13 @@ function IconButton({
604
799
  }
605
800
  onClick?.(event);
606
801
  };
607
- const button = /* @__PURE__ */ jsxs7(
608
- "button",
609
- {
610
- type,
802
+ const button = useRender2({
803
+ render,
804
+ ref,
805
+ defaultTagName: "button",
806
+ props: {
807
+ // `type` only means something on a real button; a rendered link keeps its own attributes.
808
+ type: render ? type : type ?? "button",
611
809
  className: cx("sc-button", "sc-icon-button", className),
612
810
  "data-variant": variant,
613
811
  "data-size": size,
@@ -617,19 +815,21 @@ function IconButton({
617
815
  "aria-label": label,
618
816
  onClick: handleClick,
619
817
  ...rest,
620
- children: [
818
+ children: /* @__PURE__ */ jsxs7(Fragment2, { children: [
621
819
  /* @__PURE__ */ jsx8("span", { className: "sc-button__icon", "aria-hidden": "true", children }),
622
- loading ? /* @__PURE__ */ jsx8("span", { className: "sc-button__spinner", "aria-hidden": "true" }) : null
623
- ]
820
+ loading ? /* @__PURE__ */ jsx8("span", { className: "sc-button__spinner", "aria-hidden": "true" }) : null,
821
+ dot ? /* @__PURE__ */ jsx8("span", { className: "sc-icon-button__dot", "data-tone": dot, "aria-hidden": "true" }) : null
822
+ ] })
624
823
  }
625
- );
824
+ });
626
825
  if (!tooltip) return button;
627
826
  return /* @__PURE__ */ jsx8(Tooltip, { content: tooltip === true ? label : tooltip, side: tooltipSide, children: button });
628
827
  }
629
828
 
630
829
  // src/components/Menu/Menu.tsx
631
830
  import { Menu as BaseMenu } from "@base-ui/react/menu";
632
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
831
+ import { useId } from "react";
832
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
633
833
  function Root2({ open, defaultOpen, onOpenChange, disabled, modal = true, children }) {
634
834
  return /* @__PURE__ */ jsx9(
635
835
  BaseMenu.Root,
@@ -664,22 +864,75 @@ function Content2({
664
864
  }) {
665
865
  return /* @__PURE__ */ jsx9(BaseMenu.Portal, { children: /* @__PURE__ */ jsx9(BaseMenu.Positioner, { side, align, sideOffset, className: "sc-menu__positioner", children: /* @__PURE__ */ jsx9(BaseMenu.Popup, { className: cx("sc-menu", className), ...rest, children }) }) });
666
866
  }
667
- function ItemBody({
867
+ function useItemBody({
668
868
  icon,
869
+ description,
669
870
  trailing,
670
871
  children
671
- }) {
672
- return /* @__PURE__ */ jsxs8(Fragment2, { children: [
872
+ }, aria) {
873
+ const id = useId();
874
+ const hasDescription = description !== void 0 && description !== null && description !== false;
875
+ const hasTrailing = trailing !== void 0;
876
+ const join = (...ids) => ids.filter(Boolean).join(" ") || void 0;
877
+ const body = /* @__PURE__ */ jsxs8(Fragment3, { children: [
673
878
  /* @__PURE__ */ jsx9("span", { className: "sc-menu__icon", "aria-hidden": "true", children: icon }),
674
- /* @__PURE__ */ jsx9("span", { className: "sc-menu__label", children }),
675
- trailing !== void 0 ? /* @__PURE__ */ jsx9("span", { className: "sc-menu__trailing", children: trailing }) : null
879
+ /* @__PURE__ */ jsx9("span", { className: "sc-menu__label", id: hasDescription ? `${id}-label` : void 0, children }),
880
+ hasDescription ? /* @__PURE__ */ jsx9("span", { className: "sc-menu__description", id: `${id}-description`, children: description }) : null,
881
+ hasTrailing ? /* @__PURE__ */ jsx9("span", { className: "sc-menu__trailing", id: hasDescription ? `${id}-trailing` : void 0, children: trailing }) : null
676
882
  ] });
883
+ if (!hasDescription) return [body, aria];
884
+ return [
885
+ body,
886
+ {
887
+ "aria-labelledby": aria["aria-labelledby"] ?? join(`${id}-label`, hasTrailing && `${id}-trailing`),
888
+ "aria-describedby": join(aria["aria-describedby"], `${id}-description`)
889
+ }
890
+ ];
677
891
  }
678
- function Item({ icon, trailing, tone = "default", className, children, ...rest }) {
679
- return /* @__PURE__ */ jsx9(BaseMenu.Item, { className: cx("sc-menu__item", className), "data-tone": tone, ...rest, children: /* @__PURE__ */ jsx9(ItemBody, { icon, trailing, children }) });
892
+ function Item({
893
+ icon,
894
+ description,
895
+ trailing,
896
+ tone = "default",
897
+ className,
898
+ children,
899
+ "aria-labelledby": labelledBy,
900
+ "aria-describedby": describedBy,
901
+ ...rest
902
+ }) {
903
+ const [body, aria] = useItemBody(
904
+ { icon, description, trailing, children },
905
+ { "aria-labelledby": labelledBy, "aria-describedby": describedBy }
906
+ );
907
+ return /* @__PURE__ */ jsx9(BaseMenu.Item, { className: cx("sc-menu__item", className), "data-tone": tone, ...aria, ...rest, children: body });
680
908
  }
681
- function LinkItem({ icon, trailing, tone = "default", className, children, ...rest }) {
682
- return /* @__PURE__ */ jsx9(BaseMenu.LinkItem, { className: cx("sc-menu__item", className), "data-tone": tone, ...rest, children: /* @__PURE__ */ jsx9(ItemBody, { icon, trailing, children }) });
909
+ function LinkItem({
910
+ icon,
911
+ description,
912
+ trailing,
913
+ tone = "default",
914
+ className,
915
+ closeOnClick = true,
916
+ children,
917
+ "aria-labelledby": labelledBy,
918
+ "aria-describedby": describedBy,
919
+ ...rest
920
+ }) {
921
+ const [body, aria] = useItemBody(
922
+ { icon, description, trailing, children },
923
+ { "aria-labelledby": labelledBy, "aria-describedby": describedBy }
924
+ );
925
+ return /* @__PURE__ */ jsx9(
926
+ BaseMenu.LinkItem,
927
+ {
928
+ className: cx("sc-menu__item", className),
929
+ "data-tone": tone,
930
+ closeOnClick,
931
+ ...aria,
932
+ ...rest,
933
+ children: body
934
+ }
935
+ );
683
936
  }
684
937
  function CheckboxItem({ trailing, onCheckedChange, className, children, ...rest }) {
685
938
  return /* @__PURE__ */ jsxs8(
@@ -856,9 +1109,14 @@ function Textarea({
856
1109
  className,
857
1110
  rows = 3,
858
1111
  resize = "vertical",
1112
+ variant = "default",
859
1113
  onValueChange,
1114
+ spellCheck,
1115
+ autoCorrect,
1116
+ autoCapitalize,
860
1117
  ...rest
861
1118
  }) {
1119
+ const code = variant === "code";
862
1120
  return /* @__PURE__ */ jsx12(
863
1121
  FieldShell,
864
1122
  {
@@ -874,9 +1132,19 @@ function Textarea({
874
1132
  children: /* @__PURE__ */ jsx12(
875
1133
  Field3.Control,
876
1134
  {
877
- render: /* @__PURE__ */ jsx12("textarea", { rows, ...rest }),
1135
+ render: /* @__PURE__ */ jsx12(
1136
+ "textarea",
1137
+ {
1138
+ rows,
1139
+ spellCheck: spellCheck ?? (code ? false : void 0),
1140
+ autoCorrect: autoCorrect ?? (code ? "off" : void 0),
1141
+ autoCapitalize: autoCapitalize ?? (code ? "off" : void 0),
1142
+ ...rest
1143
+ }
1144
+ ),
878
1145
  className: "sc-input sc-textarea",
879
1146
  "data-resize": resize,
1147
+ "data-variant": code ? "code" : void 0,
880
1148
  required,
881
1149
  onValueChange: onValueChange ? (value) => onValueChange(value) : void 0
882
1150
  }
@@ -887,6 +1155,7 @@ function Textarea({
887
1155
 
888
1156
  // src/components/Select/Select.tsx
889
1157
  import { Select as BaseSelect } from "@base-ui/react/select";
1158
+ import { useState } from "react";
890
1159
  import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
891
1160
  function isGroup(entry) {
892
1161
  return "options" in entry;
@@ -913,11 +1182,27 @@ function Select({
913
1182
  defaultValue,
914
1183
  onValueChange,
915
1184
  placeholder = "Choose\u2026",
1185
+ clearable = false,
1186
+ clearLabel = "Clear",
916
1187
  readOnly,
917
1188
  fullWidth = false,
918
1189
  id
919
1190
  }) {
920
1191
  const items = options.flatMap((entry) => isGroup(entry) ? entry.options : [entry]);
1192
+ const [internal, setInternal] = useState(defaultValue ?? null);
1193
+ const controlled = value !== void 0;
1194
+ const current = controlled ? value : internal;
1195
+ const setValue = (next) => {
1196
+ if (!controlled) setInternal(next);
1197
+ onValueChange?.(next);
1198
+ };
1199
+ const canClear = clearable && current != null && !disabled && !readOnly;
1200
+ const onTriggerKeyDown = (event) => {
1201
+ if (canClear && (event.key === "Backspace" || event.key === "Delete")) {
1202
+ event.preventDefault();
1203
+ setValue(null);
1204
+ }
1205
+ };
921
1206
  return /* @__PURE__ */ jsx13(
922
1207
  FieldShell,
923
1208
  {
@@ -935,26 +1220,39 @@ function Select({
935
1220
  BaseSelect.Root,
936
1221
  {
937
1222
  items,
938
- value,
939
- defaultValue,
940
- onValueChange: onValueChange ? (next) => onValueChange(next) : void 0,
1223
+ value: current,
1224
+ onValueChange: (next) => setValue(next),
941
1225
  disabled,
942
1226
  required,
943
1227
  readOnly,
944
1228
  children: [
945
- /* @__PURE__ */ jsxs11(
946
- BaseSelect.Trigger,
947
- {
948
- id,
949
- className: "sc-select",
950
- "data-size": size,
951
- "data-full-width": fullWidth || void 0,
952
- children: [
953
- /* @__PURE__ */ jsx13(BaseSelect.Value, { className: "sc-select__value", placeholder }),
954
- /* @__PURE__ */ jsx13(BaseSelect.Icon, { className: "sc-select__icon", children: /* @__PURE__ */ jsx13(ChevronDownIcon, {}) })
955
- ]
956
- }
957
- ),
1229
+ /* @__PURE__ */ jsxs11("span", { className: "sc-select__wrap", "data-full-width": fullWidth || void 0, children: [
1230
+ /* @__PURE__ */ jsxs11(
1231
+ BaseSelect.Trigger,
1232
+ {
1233
+ id,
1234
+ className: "sc-select",
1235
+ "data-size": size,
1236
+ "data-full-width": fullWidth || void 0,
1237
+ "data-clearable": canClear || void 0,
1238
+ onKeyDown: onTriggerKeyDown,
1239
+ children: [
1240
+ /* @__PURE__ */ jsx13(BaseSelect.Value, { className: "sc-select__value", placeholder }),
1241
+ /* @__PURE__ */ jsx13(BaseSelect.Icon, { className: "sc-select__icon", children: /* @__PURE__ */ jsx13(ChevronDownIcon, {}) })
1242
+ ]
1243
+ }
1244
+ ),
1245
+ canClear ? /* @__PURE__ */ jsx13(
1246
+ "button",
1247
+ {
1248
+ type: "button",
1249
+ className: "sc-select__clear",
1250
+ "aria-label": clearLabel,
1251
+ onClick: () => setValue(null),
1252
+ children: /* @__PURE__ */ jsx13(XIcon, { size: "sm" })
1253
+ }
1254
+ ) : null
1255
+ ] }),
958
1256
  /* @__PURE__ */ jsx13(BaseSelect.Portal, { children: /* @__PURE__ */ jsx13(
959
1257
  BaseSelect.Positioner,
960
1258
  {
@@ -978,7 +1276,7 @@ function Select({
978
1276
 
979
1277
  // src/components/Checkbox/Checkbox.tsx
980
1278
  import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
981
- import { useId } from "react";
1279
+ import { useId as useId2 } from "react";
982
1280
  import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
983
1281
  function Checkbox({
984
1282
  label,
@@ -988,7 +1286,7 @@ function Checkbox({
988
1286
  disabled,
989
1287
  ...rest
990
1288
  }) {
991
- const descriptionId = useId();
1289
+ const descriptionId = useId2();
992
1290
  return /* @__PURE__ */ jsxs12("div", { className: cx("sc-choice", className), "data-disabled": disabled || void 0, children: [
993
1291
  /* @__PURE__ */ jsxs12("label", { className: "sc-choice__control", children: [
994
1292
  /* @__PURE__ */ jsx14(
@@ -1013,7 +1311,7 @@ function Checkbox({
1013
1311
 
1014
1312
  // src/components/Switch/Switch.tsx
1015
1313
  import { Switch as BaseSwitch } from "@base-ui/react/switch";
1016
- import { useId as useId2 } from "react";
1314
+ import { useId as useId3 } from "react";
1017
1315
  import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1018
1316
  function Switch({
1019
1317
  label,
@@ -1024,7 +1322,7 @@ function Switch({
1024
1322
  disabled,
1025
1323
  ...rest
1026
1324
  }) {
1027
- const descriptionId = useId2();
1325
+ const descriptionId = useId3();
1028
1326
  return /* @__PURE__ */ jsxs13(
1029
1327
  "div",
1030
1328
  {
@@ -1054,7 +1352,7 @@ function Switch({
1054
1352
 
1055
1353
  // src/components/Tabs/Tabs.tsx
1056
1354
  import { Tabs as BaseTabs } from "@base-ui/react/tabs";
1057
- import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1355
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1058
1356
  function Root3({ className, onValueChange, ...rest }) {
1059
1357
  return /* @__PURE__ */ jsx16(
1060
1358
  BaseTabs.Root,
@@ -1065,17 +1363,26 @@ function Root3({ className, onValueChange, ...rest }) {
1065
1363
  }
1066
1364
  );
1067
1365
  }
1068
- function List({ fill = false, className, children, ...rest }) {
1069
- return /* @__PURE__ */ jsxs14(BaseTabs.List, { className: cx("sc-tabs__list", className), "data-fill": fill || void 0, ...rest, children: [
1070
- children,
1071
- /* @__PURE__ */ jsx16(BaseTabs.Indicator, { className: "sc-tabs__indicator" })
1072
- ] });
1366
+ function List({ fill = false, variant = "line", className, children, ...rest }) {
1367
+ return /* @__PURE__ */ jsxs14(
1368
+ BaseTabs.List,
1369
+ {
1370
+ className: cx("sc-tabs__list", className),
1371
+ "data-fill": fill || void 0,
1372
+ "data-variant": variant,
1373
+ ...rest,
1374
+ children: [
1375
+ children,
1376
+ /* @__PURE__ */ jsx16(BaseTabs.Indicator, { className: "sc-tabs__indicator" })
1377
+ ]
1378
+ }
1379
+ );
1073
1380
  }
1074
1381
  function Tab({ icon, count, className, children, ...rest }) {
1075
1382
  return /* @__PURE__ */ jsxs14(BaseTabs.Tab, { className: cx("sc-tabs__tab", className), ...rest, children: [
1076
1383
  icon ? /* @__PURE__ */ jsx16("span", { className: "sc-tabs__icon", "aria-hidden": "true", children: icon }) : null,
1077
1384
  /* @__PURE__ */ jsx16("span", { className: "sc-tabs__label", children }),
1078
- count != null ? /* @__PURE__ */ jsxs14(Fragment3, { children: [
1385
+ count != null ? /* @__PURE__ */ jsxs14(Fragment4, { children: [
1079
1386
  " ",
1080
1387
  /* @__PURE__ */ jsx16("span", { className: "sc-tabs__count", children: count })
1081
1388
  ] }) : null
@@ -1107,13 +1414,14 @@ function Separator2({ className, ...rest }) {
1107
1414
  return /* @__PURE__ */ jsx17(BaseToolbar.Separator, { className: cx("sc-toolbar__separator", className), ...rest });
1108
1415
  }
1109
1416
  var Button2 = BaseToolbar.Button;
1417
+ var Link = BaseToolbar.Link;
1110
1418
  function Spacer({ className }) {
1111
1419
  return /* @__PURE__ */ jsx17("div", { className: cx("sc-toolbar__spacer", className), "aria-hidden": "true" });
1112
1420
  }
1113
- var Toolbar = { Root: Root4, Group: Group2, Separator: Separator2, Button: Button2, Spacer };
1421
+ var Toolbar = { Root: Root4, Group: Group2, Separator: Separator2, Button: Button2, Link, Spacer };
1114
1422
 
1115
1423
  // src/components/Banner/Banner.tsx
1116
- import { useState } from "react";
1424
+ import { useState as useState2 } from "react";
1117
1425
  import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1118
1426
  var toneIcons = {
1119
1427
  info: /* @__PURE__ */ jsx18(InfoIcon, {}),
@@ -1135,7 +1443,7 @@ function Banner({
1135
1443
  children,
1136
1444
  ...rest
1137
1445
  }) {
1138
- const [dismissed, setDismissed] = useState(false);
1446
+ const [dismissed, setDismissed] = useState2(false);
1139
1447
  const visible = open ?? !dismissed;
1140
1448
  if (!visible) return null;
1141
1449
  const glyph = icon === false ? null : icon ?? toneIcons[tone];
@@ -1255,19 +1563,11 @@ var sizeByLevel = {
1255
1563
  5: "md",
1256
1564
  6: "sm"
1257
1565
  };
1258
- function Heading({
1259
- level = 2,
1260
- size,
1261
- as,
1262
- tone = "default",
1263
- truncate = false,
1264
- className,
1265
- ...rest
1266
- }) {
1566
+ function Heading({ level = 2, size, as, tone, truncate = false, className, ...rest }) {
1267
1567
  return createElement3(as ?? `h${level}`, {
1268
1568
  className: cx("sc-heading", className),
1269
1569
  "data-size": size ?? sizeByLevel[level],
1270
- "data-tone": tone,
1570
+ "data-tone": tone ?? (size === "overline" ? "muted" : "default"),
1271
1571
  "data-truncate": truncate || void 0,
1272
1572
  ...rest
1273
1573
  });
@@ -1283,26 +1583,53 @@ function Text({
1283
1583
  align,
1284
1584
  truncate = false,
1285
1585
  numeric = false,
1586
+ family = "sans",
1286
1587
  className,
1287
1588
  style,
1288
1589
  ...rest
1289
1590
  }) {
1290
- const clamp = typeof truncate === "number" ? truncate : void 0;
1591
+ const clamp2 = typeof truncate === "number" ? truncate : void 0;
1291
1592
  return createElement4(as, {
1292
1593
  className: cx("sc-text", className),
1293
1594
  "data-size": size,
1294
1595
  "data-tone": tone,
1295
1596
  "data-weight": weight,
1296
1597
  "data-align": align,
1297
- "data-truncate": truncate === true ? "" : clamp !== void 0 ? "clamp" : void 0,
1598
+ "data-truncate": truncate === true ? "" : clamp2 !== void 0 ? "clamp" : void 0,
1298
1599
  "data-numeric": numeric || void 0,
1299
- style: clamp !== void 0 ? { ...style, "--sc-text-lines": clamp } : style,
1600
+ "data-family": family === "mono" ? "mono" : void 0,
1601
+ style: clamp2 !== void 0 ? { ...style, "--sc-text-lines": clamp2 } : style,
1300
1602
  ...rest
1301
1603
  });
1302
1604
  }
1303
1605
 
1304
1606
  // src/components/Table/Table.tsx
1607
+ import { Checkbox as BaseCheckbox2 } from "@base-ui/react/checkbox";
1608
+ import {
1609
+ createContext as createContext2,
1610
+ useContext as useContext2
1611
+ } from "react";
1305
1612
  import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
1613
+ var SelectionContext = createContext2(null);
1614
+ var RowIdContext = createContext2(void 0);
1615
+ function useSelection(part) {
1616
+ const selection = useContext2(SelectionContext);
1617
+ if (!selection) throw new Error(`${part} needs Table.Root selection={useRowSelection(ids)}`);
1618
+ return selection;
1619
+ }
1620
+ function isTextEntry(target) {
1621
+ if (!(target instanceof HTMLElement)) return false;
1622
+ if (target.isContentEditable || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement)
1623
+ return true;
1624
+ return target instanceof HTMLInputElement && !["checkbox", "radio", "button", "submit", "reset", "image", "file", "color", "range"].includes(
1625
+ target.type
1626
+ );
1627
+ }
1628
+ var CONTROLS = 'a[href], button, input, select, textarea, label, summary, [role="checkbox"], [role="button"], [role="link"], [contenteditable="true"]';
1629
+ function fromControl(event) {
1630
+ const control = event.target instanceof Element ? event.target.closest(CONTROLS) : null;
1631
+ return control !== null && event.currentTarget.contains(control);
1632
+ }
1306
1633
  function Root5({
1307
1634
  stickyHeader = false,
1308
1635
  maxHeight,
@@ -1314,8 +1641,23 @@ function Root5({
1314
1641
  className,
1315
1642
  style,
1316
1643
  children,
1644
+ selection,
1645
+ onKeyDown,
1317
1646
  ...rest
1318
1647
  }) {
1648
+ const handleKeyDown = (event) => {
1649
+ onKeyDown?.(event);
1650
+ if (!selection || event.defaultPrevented) return;
1651
+ if (!(event.target instanceof Node) || !event.currentTarget.contains(event.target)) return;
1652
+ if (isTextEntry(event.target)) return;
1653
+ if ((event.metaKey || event.ctrlKey) && !event.altKey && event.key.toLowerCase() === "a") {
1654
+ event.preventDefault();
1655
+ selection.selectAll();
1656
+ } else if (event.key === "Escape" && selection.count > 0) {
1657
+ event.preventDefault();
1658
+ selection.clear();
1659
+ }
1660
+ };
1319
1661
  return /* @__PURE__ */ jsx21(
1320
1662
  "div",
1321
1663
  {
@@ -1331,8 +1673,9 @@ function Root5({
1331
1673
  "data-divided": divided || void 0,
1332
1674
  "data-layout": layout,
1333
1675
  style,
1676
+ onKeyDown: selection ? handleKeyDown : onKeyDown,
1334
1677
  ...rest,
1335
- children
1678
+ children: /* @__PURE__ */ jsx21(SelectionContext.Provider, { value: selection ?? null, children })
1336
1679
  }
1337
1680
  )
1338
1681
  }
@@ -1350,17 +1693,117 @@ function Body2({ className, ...rest }) {
1350
1693
  function Foot({ className, ...rest }) {
1351
1694
  return /* @__PURE__ */ jsx21("tfoot", { className: cx("sc-table__foot", className), ...rest });
1352
1695
  }
1353
- function Row({ selected = false, interactive = false, className, ...rest }) {
1354
- return /* @__PURE__ */ jsx21(
1696
+ function Row({
1697
+ selected,
1698
+ interactive = false,
1699
+ selectionId,
1700
+ className,
1701
+ onClick,
1702
+ onMouseDown,
1703
+ ...rest
1704
+ }) {
1705
+ const selection = useContext2(SelectionContext);
1706
+ const selectable = selection !== null && selectionId !== void 0;
1707
+ const isSelected = selected ?? (selectable && selection.isSelected(selectionId));
1708
+ const row = /* @__PURE__ */ jsx21(
1355
1709
  "tr",
1356
1710
  {
1357
1711
  className: cx("sc-table__row", className),
1358
- "data-selected": selected || void 0,
1712
+ "data-selected": isSelected || void 0,
1359
1713
  "data-interactive": interactive || void 0,
1360
- "aria-selected": selected || void 0,
1714
+ "aria-selected": selectable ? isSelected : isSelected || void 0,
1715
+ onClick: selectable ? (event) => {
1716
+ if ((event.shiftKey || event.metaKey || event.ctrlKey) && !fromControl(event)) {
1717
+ event.preventDefault();
1718
+ if (event.shiftKey) selection.toggleRange(selectionId);
1719
+ else selection.toggle(selectionId);
1720
+ return;
1721
+ }
1722
+ onClick?.(event);
1723
+ } : onClick,
1724
+ onMouseDown: selectable ? (event) => {
1725
+ if (event.shiftKey && !fromControl(event)) event.preventDefault();
1726
+ onMouseDown?.(event);
1727
+ } : onMouseDown,
1361
1728
  ...rest
1362
1729
  }
1363
1730
  );
1731
+ return selectionId === void 0 ? row : /* @__PURE__ */ jsx21(RowIdContext.Provider, { value: selectionId, children: row });
1732
+ }
1733
+ function SelectionBox({
1734
+ label,
1735
+ ...rest
1736
+ }) {
1737
+ return /* @__PURE__ */ jsx21(BaseCheckbox2.Root, { className: "sc-checkbox", "aria-label": label, ...rest, children: /* @__PURE__ */ jsxs18(BaseCheckbox2.Indicator, { className: "sc-checkbox__indicator", children: [
1738
+ /* @__PURE__ */ jsx21(CheckIcon, { size: "sm", className: "sc-checkbox__check" }),
1739
+ /* @__PURE__ */ jsx21(MinusIcon, { size: "sm", className: "sc-checkbox__dash" })
1740
+ ] }) });
1741
+ }
1742
+ var onCheckbox = (event) => event.target instanceof Element && event.target.closest('[role="checkbox"]') !== null;
1743
+ function SelectionHeader({
1744
+ label = "Select all rows",
1745
+ className,
1746
+ onClick,
1747
+ ...rest
1748
+ }) {
1749
+ const selection = useSelection("Table.SelectionHeader");
1750
+ const disabled = selection.ids.length === 0;
1751
+ const toggleAll = () => selection.headerState === "all" ? selection.clear() : selection.selectAll();
1752
+ return /* @__PURE__ */ jsx21(
1753
+ "th",
1754
+ {
1755
+ scope: "col",
1756
+ className: cx("sc-table__header", "sc-table__select", className),
1757
+ "data-disabled": disabled || void 0,
1758
+ onClick: (event) => {
1759
+ onClick?.(event);
1760
+ if (!disabled && !onCheckbox(event)) toggleAll();
1761
+ },
1762
+ ...rest,
1763
+ children: /* @__PURE__ */ jsx21(
1764
+ SelectionBox,
1765
+ {
1766
+ label,
1767
+ checked: selection.headerState === "all",
1768
+ indeterminate: selection.headerState === "some",
1769
+ disabled,
1770
+ onCheckedChange: toggleAll
1771
+ }
1772
+ )
1773
+ }
1774
+ );
1775
+ }
1776
+ function SelectionCell({ label, className, onClick, onClickCapture, ...rest }) {
1777
+ const selection = useSelection("Table.SelectionCell");
1778
+ const id = useContext2(RowIdContext);
1779
+ if (id === void 0) throw new Error("Table.SelectionCell goes inside a Table.Row with selectionId");
1780
+ return /* @__PURE__ */ jsx21(
1781
+ "td",
1782
+ {
1783
+ className: cx("sc-table__cell", "sc-table__select", className),
1784
+ onClickCapture: (event) => {
1785
+ onClickCapture?.(event);
1786
+ if (!event.shiftKey) return;
1787
+ event.preventDefault();
1788
+ event.stopPropagation();
1789
+ selection.toggleRange(id);
1790
+ },
1791
+ onClick: (event) => {
1792
+ onClick?.(event);
1793
+ event.stopPropagation();
1794
+ if (!onCheckbox(event)) selection.toggle(id);
1795
+ },
1796
+ ...rest,
1797
+ children: /* @__PURE__ */ jsx21(
1798
+ SelectionBox,
1799
+ {
1800
+ label,
1801
+ checked: selection.isSelected(id),
1802
+ onCheckedChange: () => selection.toggle(id)
1803
+ }
1804
+ )
1805
+ }
1806
+ );
1364
1807
  }
1365
1808
  function Header({
1366
1809
  align,
@@ -1430,15 +1873,263 @@ function Cell({
1430
1873
  }
1431
1874
  );
1432
1875
  }
1433
- var Table = { Root: Root5, Caption, Head, Body: Body2, Foot, Row, Header, Cell };
1876
+ var Table = { Root: Root5, Caption, Head, Body: Body2, Foot, Row, Header, Cell, SelectionHeader, SelectionCell };
1877
+
1878
+ // src/components/Table/useRowSelection.ts
1879
+ import { useMemo, useReducer } from "react";
1880
+
1881
+ // src/components/Table/rowSelection.ts
1882
+ function initRowSelection(ids) {
1883
+ return { ids, selected: /* @__PURE__ */ new Set(), anchor: null };
1884
+ }
1885
+ function sameIds(a, b) {
1886
+ if (a === b) return true;
1887
+ if (a.length !== b.length) return false;
1888
+ for (let i = 0; i < a.length; i += 1) if (a[i] !== b[i]) return false;
1889
+ return true;
1890
+ }
1891
+ function toggle(state, id) {
1892
+ if (!state.ids.includes(id)) return state;
1893
+ const selected = new Set(state.selected);
1894
+ if (selected.has(id)) selected.delete(id);
1895
+ else selected.add(id);
1896
+ return { ...state, selected, anchor: id };
1897
+ }
1898
+ function toggleRange(state, id) {
1899
+ const to = state.ids.indexOf(id);
1900
+ if (to < 0) return state;
1901
+ const from = state.anchor === null ? -1 : state.ids.indexOf(state.anchor);
1902
+ if (from < 0 || from === to) return toggle(state, id);
1903
+ const on = !state.selected.has(id);
1904
+ const selected = new Set(state.selected);
1905
+ const [start, end] = from < to ? [from, to] : [to, from];
1906
+ for (let i = start; i <= end; i += 1) {
1907
+ const rowId = state.ids[i];
1908
+ if (on) selected.add(rowId);
1909
+ else selected.delete(rowId);
1910
+ }
1911
+ return { ...state, selected, anchor: id };
1912
+ }
1913
+ function sync(state, ids) {
1914
+ if (sameIds(state.ids, ids)) return state;
1915
+ const present = new Set(ids);
1916
+ let selected = state.selected;
1917
+ for (const id of state.selected) {
1918
+ if (present.has(id)) continue;
1919
+ selected = new Set([...state.selected].filter((x) => present.has(x)));
1920
+ break;
1921
+ }
1922
+ const anchor = state.anchor !== null && present.has(state.anchor) ? state.anchor : null;
1923
+ return { ids, selected, anchor };
1924
+ }
1925
+ function rowSelectionReducer(state, event) {
1926
+ switch (event.type) {
1927
+ case "selection.sync":
1928
+ return sync(state, event.ids);
1929
+ case "selection.toggle":
1930
+ return toggle(state, event.id);
1931
+ case "selection.toggleRange":
1932
+ return toggleRange(state, event.id);
1933
+ case "selection.selectAll":
1934
+ return state.selected.size === state.ids.length ? state : { ...state, selected: new Set(state.ids) };
1935
+ case "selection.clear":
1936
+ return state.selected.size === 0 && state.anchor === null ? state : { ...state, selected: /* @__PURE__ */ new Set(), anchor: null };
1937
+ }
1938
+ }
1939
+ function headerState(state) {
1940
+ if (state.selected.size === 0) return "none";
1941
+ return state.selected.size === state.ids.length ? "all" : "some";
1942
+ }
1943
+ function selectedIds(state) {
1944
+ return state.ids.filter((id) => state.selected.has(id));
1945
+ }
1946
+
1947
+ // src/components/Table/useRowSelection.ts
1948
+ function useRowSelection(ids) {
1949
+ const [stored, dispatch] = useReducer(rowSelectionReducer, ids, initRowSelection);
1950
+ const state = sameIds(stored.ids, ids) ? stored : rowSelectionReducer(stored, { type: "selection.sync", ids });
1951
+ if (state !== stored) dispatch({ type: "selection.sync", ids });
1952
+ const actions = useMemo(
1953
+ () => ({
1954
+ toggle: (id) => dispatch({ type: "selection.toggle", id }),
1955
+ toggleRange: (id) => dispatch({ type: "selection.toggleRange", id }),
1956
+ selectAll: () => dispatch({ type: "selection.selectAll" }),
1957
+ clear: () => dispatch({ type: "selection.clear" })
1958
+ }),
1959
+ []
1960
+ );
1961
+ return useMemo(
1962
+ () => ({
1963
+ ids: state.ids,
1964
+ selected: state.selected,
1965
+ selectedIds: selectedIds(state),
1966
+ count: state.selected.size,
1967
+ anchor: state.anchor,
1968
+ headerState: headerState(state),
1969
+ isSelected: (id) => state.selected.has(id),
1970
+ ...actions
1971
+ }),
1972
+ [state, actions]
1973
+ );
1974
+ }
1975
+
1976
+ // src/components/SelectionBar/SelectionBar.tsx
1977
+ import { useEffect, useLayoutEffect, useRef, useState as useState3 } from "react";
1978
+ import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
1979
+ var defaultCountLabel = (count) => `${count} selected`;
1980
+ var LEAVE_MS = 200;
1981
+ function SelectionBar({
1982
+ count,
1983
+ onClear,
1984
+ children,
1985
+ "aria-label": ariaLabel = "Bulk actions",
1986
+ countLabel = defaultCountLabel,
1987
+ clearLabel = "Clear",
1988
+ clearedMessage = "Selection cleared",
1989
+ className
1990
+ }) {
1991
+ const open = count > 0;
1992
+ const [hadSelection, setHadSelection] = useState3(open);
1993
+ if (open && !hadSelection) setHadSelection(true);
1994
+ const message = open ? countLabel(count) : hadSelection ? clearedMessage : "";
1995
+ const [rendered, setRendered] = useState3(open);
1996
+ const [shownCount, setShownCount] = useState3(count);
1997
+ if (open && !rendered) setRendered(true);
1998
+ if (open && shownCount !== count) setShownCount(count);
1999
+ useEffect(() => {
2000
+ if (open || !rendered) return;
2001
+ const timer = setTimeout(() => setRendered(false), LEAVE_MS);
2002
+ return () => clearTimeout(timer);
2003
+ }, [open, rendered]);
2004
+ const barRef = useRef(null);
2005
+ const returnTo = useRef(null);
2006
+ useLayoutEffect(() => {
2007
+ const bar = barRef.current;
2008
+ if (open || !bar?.contains(bar.ownerDocument.activeElement)) return;
2009
+ if (returnTo.current?.isConnected) returnTo.current.focus();
2010
+ }, [open]);
2011
+ const [height, setHeight] = useState3(0);
2012
+ useEffect(() => {
2013
+ const bar = barRef.current;
2014
+ if (!rendered || !bar || typeof ResizeObserver === "undefined") return;
2015
+ const observer = new ResizeObserver(([entry]) => setHeight(entry.borderBoxSize[0]?.blockSize ?? 0));
2016
+ observer.observe(bar);
2017
+ return () => observer.disconnect();
2018
+ }, [rendered]);
2019
+ return /* @__PURE__ */ jsxs19(Fragment5, { children: [
2020
+ /* @__PURE__ */ jsx22("span", { role: "status", className: "sc-visually-hidden", children: message }),
2021
+ rendered ? /* @__PURE__ */ jsxs19(Fragment5, { children: [
2022
+ open ? /* @__PURE__ */ jsx22(
2023
+ "div",
2024
+ {
2025
+ className: "sc-selection-bar__spacer",
2026
+ style: { "--sc-selection-bar-height": `${height}px` },
2027
+ "aria-hidden": "true"
2028
+ }
2029
+ ) : null,
2030
+ /* @__PURE__ */ jsx22(
2031
+ "div",
2032
+ {
2033
+ ref: barRef,
2034
+ className: cx("sc-selection-bar", className),
2035
+ "data-open": open || void 0,
2036
+ onFocus: (event) => {
2037
+ const from = event.relatedTarget;
2038
+ if (from instanceof HTMLElement && !event.currentTarget.contains(from)) returnTo.current = from;
2039
+ },
2040
+ children: /* @__PURE__ */ jsxs19(Toolbar.Root, { "aria-label": ariaLabel, className: "sc-selection-bar__toolbar", children: [
2041
+ /* @__PURE__ */ jsx22("span", { className: "sc-selection-bar__count", children: countLabel(shownCount) }),
2042
+ /* @__PURE__ */ jsx22(Toolbar.Separator, {}),
2043
+ children,
2044
+ /* @__PURE__ */ jsx22(Toolbar.Spacer, {}),
2045
+ /* @__PURE__ */ jsx22(Toolbar.Button, { render: /* @__PURE__ */ jsx22(Button, { variant: "ghost", size: "sm" }), onClick: onClear, children: clearLabel })
2046
+ ] })
2047
+ }
2048
+ )
2049
+ ] }) : null
2050
+ ] });
2051
+ }
2052
+
2053
+ // src/components/Toast/Toast.tsx
2054
+ import { Toast as BaseToast } from "@base-ui/react/toast";
2055
+ import { createContext as createContext3, useContext as useContext3, useMemo as useMemo2 } from "react";
2056
+ import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
2057
+ var InsideProvider = createContext3(false);
2058
+ function Provider2({ children, timeout = 6e3, limit = 3 }) {
2059
+ if (useContext3(InsideProvider)) return children;
2060
+ return /* @__PURE__ */ jsx23(InsideProvider.Provider, { value: true, children: /* @__PURE__ */ jsxs20(BaseToast.Provider, { timeout, limit, children: [
2061
+ children,
2062
+ /* @__PURE__ */ jsx23(BaseToast.Portal, { children: /* @__PURE__ */ jsx23(Viewport, {}) })
2063
+ ] }) });
2064
+ }
2065
+ function Viewport() {
2066
+ const { toasts, close } = BaseToast.useToastManager();
2067
+ return /* @__PURE__ */ jsx23(BaseToast.Viewport, { className: "sc-toast-viewport", children: toasts.map((toast) => /* @__PURE__ */ jsxs20(BaseToast.Root, { toast, className: "sc-toast", "data-tone": toast.type ?? "neutral", children: [
2068
+ /* @__PURE__ */ jsxs20(BaseToast.Content, { className: "sc-toast__content", children: [
2069
+ /* @__PURE__ */ jsx23(BaseToast.Title, { className: "sc-toast__title" }),
2070
+ toast.description != null ? /* @__PURE__ */ jsx23(BaseToast.Description, { className: "sc-toast__description" }) : null
2071
+ ] }),
2072
+ toast.actionProps ? /* @__PURE__ */ jsx23(
2073
+ BaseToast.Action,
2074
+ {
2075
+ className: "sc-toast__action",
2076
+ render: /* @__PURE__ */ jsx23(Button, { variant: "secondary", size: "sm" })
2077
+ }
2078
+ ) : null,
2079
+ toast.data?.dismissLabel ? /* @__PURE__ */ jsx23(Button, { variant: "ghost", size: "sm", className: "sc-toast__close", onClick: () => close(toast.id), children: toast.data.dismissLabel }) : /* @__PURE__ */ jsx23(IconButton, { label: "Dismiss", size: "sm", className: "sc-toast__close", onClick: () => close(toast.id), children: /* @__PURE__ */ jsx23(XIcon, {}) })
2080
+ ] }, toast.id)) });
2081
+ }
2082
+ function useToast() {
2083
+ const manager = BaseToast.useToastManager();
2084
+ return useMemo2(
2085
+ () => ({
2086
+ add: ({
2087
+ title,
2088
+ description,
2089
+ tone = "neutral",
2090
+ action,
2091
+ timeout,
2092
+ priority,
2093
+ id: given,
2094
+ dismissLabel,
2095
+ onClose
2096
+ }) => {
2097
+ let id = given ?? "";
2098
+ id = manager.add({
2099
+ title,
2100
+ description,
2101
+ type: tone,
2102
+ data: { dismissLabel },
2103
+ ...given ? { id: given } : {},
2104
+ ...timeout !== void 0 ? { timeout } : {},
2105
+ ...priority ? { priority } : {},
2106
+ ...onClose ? { onClose } : {},
2107
+ ...action ? {
2108
+ actionProps: {
2109
+ children: action.label,
2110
+ onClick: () => {
2111
+ action.onClick();
2112
+ if (!action.keepOpen) manager.close(id);
2113
+ }
2114
+ }
2115
+ } : {}
2116
+ });
2117
+ return id;
2118
+ },
2119
+ close: manager.close
2120
+ }),
2121
+ [manager]
2122
+ );
2123
+ }
2124
+ var Toast = { Provider: Provider2 };
1434
2125
 
1435
2126
  // src/components/Stat/Stat.tsx
1436
2127
  import { createElement as createElement5 } from "react";
1437
- import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
2128
+ import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
1438
2129
  var trendIcons = {
1439
- up: /* @__PURE__ */ jsx22(ArrowUpIcon, { size: "xs" }),
1440
- down: /* @__PURE__ */ jsx22(ArrowDownIcon, { size: "xs" }),
1441
- flat: /* @__PURE__ */ jsx22(ArrowRightIcon, { size: "xs" })
2130
+ up: /* @__PURE__ */ jsx24(ArrowUpIcon, { size: "xs" }),
2131
+ down: /* @__PURE__ */ jsx24(ArrowDownIcon, { size: "xs" }),
2132
+ flat: /* @__PURE__ */ jsx24(ArrowRightIcon, { size: "xs" })
1442
2133
  };
1443
2134
  function Stat({
1444
2135
  label,
@@ -1462,23 +2153,1776 @@ function Stat({
1462
2153
  "data-size": size,
1463
2154
  ...rest
1464
2155
  },
1465
- /* @__PURE__ */ jsxs19(Fragment4, { children: [
1466
- /* @__PURE__ */ jsx22("div", { className: "sc-stat__label", children: label }),
1467
- icon ? /* @__PURE__ */ jsx22("span", { className: "sc-stat__icon", "aria-hidden": "true", children: icon }) : null,
1468
- /* @__PURE__ */ jsx22("div", { className: "sc-stat__value", children: value }),
1469
- delta != null ? /* @__PURE__ */ jsxs19("div", { className: "sc-stat__delta", "data-tone": deltaTone, children: [
1470
- trend ? /* @__PURE__ */ jsx22("span", { className: "sc-stat__trend", "aria-hidden": "true", children: trendIcons[trend] }) : null,
1471
- trend ? /* @__PURE__ */ jsxs19("span", { className: "sc-visually-hidden", children: [
2156
+ /* @__PURE__ */ jsxs21(Fragment6, { children: [
2157
+ /* @__PURE__ */ jsx24("div", { className: "sc-stat__label", children: label }),
2158
+ icon ? /* @__PURE__ */ jsx24("span", { className: "sc-stat__icon", "aria-hidden": "true", children: icon }) : null,
2159
+ /* @__PURE__ */ jsx24("div", { className: "sc-stat__value", children: value }),
2160
+ delta != null ? /* @__PURE__ */ jsxs21("div", { className: "sc-stat__delta", "data-tone": deltaTone, children: [
2161
+ trend ? /* @__PURE__ */ jsx24("span", { className: "sc-stat__trend", "aria-hidden": "true", children: trendIcons[trend] }) : null,
2162
+ trend ? /* @__PURE__ */ jsxs21("span", { className: "sc-visually-hidden", children: [
1472
2163
  trend === "flat" ? "unchanged" : trend,
1473
2164
  " "
1474
2165
  ] }) : null,
1475
2166
  delta
1476
2167
  ] }) : null,
1477
- hint != null ? /* @__PURE__ */ jsx22("div", { className: "sc-stat__hint", children: hint }) : null
2168
+ hint != null ? /* @__PURE__ */ jsx24("div", { className: "sc-stat__hint", children: hint }) : null
1478
2169
  ] })
1479
2170
  );
1480
2171
  }
1481
2172
 
2173
+ // src/components/Chip/Chip.tsx
2174
+ import { Fragment as Fragment7, jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
2175
+ function Chip({
2176
+ children,
2177
+ icon,
2178
+ tone = "neutral",
2179
+ size = "md",
2180
+ selected = false,
2181
+ onClick,
2182
+ onRemove,
2183
+ removeLabel,
2184
+ disabled = false,
2185
+ className,
2186
+ ...rest
2187
+ }) {
2188
+ const interactive = onClick !== void 0;
2189
+ const removeName = removeLabel ?? (typeof children === "string" ? `Remove ${children}` : "Remove");
2190
+ const handleRemoveKey = (event) => {
2191
+ if (onRemove && !disabled && (event.key === "Backspace" || event.key === "Delete")) {
2192
+ event.preventDefault();
2193
+ onRemove();
2194
+ }
2195
+ };
2196
+ const shared = {
2197
+ className: cx("sc-chip", className),
2198
+ "data-tone": tone,
2199
+ "data-size": size,
2200
+ "data-selected": selected || void 0,
2201
+ "data-removable": onRemove ? "" : void 0,
2202
+ "data-disabled": disabled || void 0
2203
+ };
2204
+ const body = /* @__PURE__ */ jsxs22(Fragment7, { children: [
2205
+ icon ? /* @__PURE__ */ jsx25("span", { className: "sc-chip__icon", "aria-hidden": "true", children: icon }) : null,
2206
+ /* @__PURE__ */ jsx25("span", { className: "sc-chip__label", children })
2207
+ ] });
2208
+ const remove = onRemove ? /* @__PURE__ */ jsx25(
2209
+ "button",
2210
+ {
2211
+ type: "button",
2212
+ className: "sc-chip__remove",
2213
+ "aria-label": removeName,
2214
+ disabled,
2215
+ onClick: (event) => {
2216
+ event.stopPropagation();
2217
+ onRemove();
2218
+ },
2219
+ onKeyDown: handleRemoveKey,
2220
+ children: /* @__PURE__ */ jsx25(XIcon, { size: "xs" })
2221
+ }
2222
+ ) : null;
2223
+ if (interactive) {
2224
+ return /* @__PURE__ */ jsxs22("span", { ...shared, ...rest, children: [
2225
+ /* @__PURE__ */ jsx25(
2226
+ "button",
2227
+ {
2228
+ type: "button",
2229
+ className: "sc-chip__action",
2230
+ "aria-pressed": selected,
2231
+ disabled,
2232
+ onClick,
2233
+ onKeyDown: handleRemoveKey,
2234
+ children: body
2235
+ }
2236
+ ),
2237
+ remove
2238
+ ] });
2239
+ }
2240
+ return /* @__PURE__ */ jsxs22("span", { ...shared, ...rest, children: [
2241
+ body,
2242
+ remove
2243
+ ] });
2244
+ }
2245
+
2246
+ // src/components/Disclosure/Disclosure.tsx
2247
+ import {
2248
+ useEffect as useEffect2,
2249
+ useRef as useRef2,
2250
+ useState as useState4
2251
+ } from "react";
2252
+ import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
2253
+ function Disclosure({
2254
+ summary,
2255
+ summaryOpen,
2256
+ open,
2257
+ defaultOpen = false,
2258
+ onOpenChange,
2259
+ previewLines,
2260
+ summaryPosition,
2261
+ className,
2262
+ style,
2263
+ children,
2264
+ ref,
2265
+ ...rest
2266
+ }) {
2267
+ const controlled = open !== void 0;
2268
+ const [internal, setInternal] = useState4(defaultOpen);
2269
+ const isOpen = controlled ? open : internal;
2270
+ const inner = useRef2(null);
2271
+ useEffect2(() => {
2272
+ if (controlled && inner.current && inner.current.open !== open) inner.current.open = open;
2273
+ });
2274
+ const onToggle = (event) => {
2275
+ const next = event.currentTarget.open;
2276
+ if (next === isOpen) return;
2277
+ if (!controlled) setInternal(next);
2278
+ onOpenChange?.(next);
2279
+ };
2280
+ const position = summaryPosition ?? (previewLines ? "end" : "start");
2281
+ return /* @__PURE__ */ jsxs23(
2282
+ "details",
2283
+ {
2284
+ ref: (node) => {
2285
+ inner.current = node;
2286
+ if (typeof ref === "function") ref(node);
2287
+ else if (ref) ref.current = node;
2288
+ },
2289
+ className: cx("sc-disclosure", className),
2290
+ open: isOpen,
2291
+ onToggle,
2292
+ "data-summary-position": position,
2293
+ "data-preview": previewLines ? "" : void 0,
2294
+ style: previewLines ? { ...style, "--_lines": previewLines } : style,
2295
+ ...rest,
2296
+ children: [
2297
+ /* @__PURE__ */ jsxs23("summary", { className: "sc-disclosure__summary", children: [
2298
+ /* @__PURE__ */ jsx26(ChevronDownIcon, { className: "sc-disclosure__chevron" }),
2299
+ /* @__PURE__ */ jsx26("span", { className: "sc-disclosure__summary-text", children: isOpen && summaryOpen != null ? summaryOpen : summary })
2300
+ ] }),
2301
+ /* @__PURE__ */ jsx26("div", { className: "sc-disclosure__content", children })
2302
+ ]
2303
+ }
2304
+ );
2305
+ }
2306
+
2307
+ // src/components/EmptyState/EmptyState.tsx
2308
+ import { createElement as createElement6 } from "react";
2309
+ import { jsx as jsx27 } from "react/jsx-runtime";
2310
+ function EmptyState({
2311
+ title,
2312
+ description,
2313
+ icon,
2314
+ children,
2315
+ size = "md",
2316
+ as = "div",
2317
+ className,
2318
+ ...rest
2319
+ }) {
2320
+ return createElement6(
2321
+ as,
2322
+ { className: cx("sc-empty-state", className), "data-size": size, ...rest },
2323
+ icon ? /* @__PURE__ */ jsx27("span", { className: "sc-empty-state__icon", "aria-hidden": "true", children: icon }) : null,
2324
+ /* @__PURE__ */ jsx27("p", { className: "sc-empty-state__title", children: title }),
2325
+ description != null ? /* @__PURE__ */ jsx27("p", { className: "sc-empty-state__description", children: description }) : null,
2326
+ children != null ? /* @__PURE__ */ jsx27("div", { className: "sc-empty-state__actions", children }) : null
2327
+ );
2328
+ }
2329
+
2330
+ // src/components/Skeleton/Skeleton.tsx
2331
+ import { jsx as jsx28 } from "react/jsx-runtime";
2332
+ function Skeleton({
2333
+ variant = "text",
2334
+ width,
2335
+ height,
2336
+ lines = 1,
2337
+ still = false,
2338
+ className,
2339
+ style,
2340
+ ...rest
2341
+ }) {
2342
+ const size = {
2343
+ ...style,
2344
+ ...width !== void 0 ? { inlineSize: width } : {},
2345
+ ...height !== void 0 ? { blockSize: height } : {}
2346
+ };
2347
+ if (variant === "text" && lines > 1) {
2348
+ return /* @__PURE__ */ jsx28("span", { className: cx("sc-skeleton-lines", className), "aria-hidden": "true", style, ...rest, children: Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx28(
2349
+ "span",
2350
+ {
2351
+ className: "sc-skeleton",
2352
+ "data-variant": "text",
2353
+ "data-still": still || void 0,
2354
+ "data-last": i === lines - 1 || void 0,
2355
+ style: width !== void 0 ? { inlineSize: width } : void 0
2356
+ },
2357
+ i
2358
+ )) });
2359
+ }
2360
+ return /* @__PURE__ */ jsx28(
2361
+ "span",
2362
+ {
2363
+ className: cx("sc-skeleton", className),
2364
+ "data-variant": variant,
2365
+ "data-still": still || void 0,
2366
+ "aria-hidden": "true",
2367
+ style: size,
2368
+ ...rest
2369
+ }
2370
+ );
2371
+ }
2372
+
2373
+ // src/components/FileInput/FileInput.tsx
2374
+ import { useEffect as useEffect4, useId as useId4, useRef as useRef4, useState as useState6 } from "react";
2375
+
2376
+ // src/components/FilePreviewDialog/FilePreviewDialog.tsx
2377
+ import { useEffect as useEffect3, useRef as useRef3, useState as useState5 } from "react";
2378
+
2379
+ // src/components/FileInput/accept.ts
2380
+ function parseAccept(accept) {
2381
+ if (accept == null) return [];
2382
+ const list = typeof accept === "string" ? accept.split(",") : accept;
2383
+ return list.map((entry) => entry.trim().toLowerCase()).filter(Boolean);
2384
+ }
2385
+ function matchesAccept(file, accept) {
2386
+ if (accept.length === 0) return true;
2387
+ const name = file.name.toLowerCase();
2388
+ const type = file.type.toLowerCase();
2389
+ return accept.some((entry) => {
2390
+ if (entry.startsWith(".")) return name.endsWith(entry);
2391
+ if (entry.endsWith("/*")) return type.startsWith(entry.slice(0, -1));
2392
+ return type === entry;
2393
+ });
2394
+ }
2395
+ var UNITS = { b: 1, kb: 1e3, mb: 1e6, gb: 1e9 };
2396
+ function parseSize(size) {
2397
+ if (size == null) return void 0;
2398
+ if (typeof size === "number") return size;
2399
+ const match = /^\s*(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)\s*$/i.exec(size);
2400
+ if (!match) throw new Error(`FileInput: maxFileSize "${size}" is not a size like "10MB".`);
2401
+ return Math.round(Number(match[1]) * UNITS[match[2].toLowerCase()]);
2402
+ }
2403
+ function formatSize(bytes) {
2404
+ const [unit, base] = bytes >= 1e9 ? ["GB", 1e9] : bytes >= 1e6 ? ["MB", 1e6] : bytes >= 1e3 ? ["KB", 1e3] : ["bytes", 1];
2405
+ const value = bytes / base;
2406
+ return `${Number.isInteger(value) ? value : Number(value.toFixed(1))} ${unit}`;
2407
+ }
2408
+ function formatListSize(bytes) {
2409
+ const n = Math.round(Math.abs(bytes));
2410
+ const trim = (value, decimals) => value.toFixed(decimals).replace(/\.0+$/, "");
2411
+ if (n < 1e3) return `${n} bytes`;
2412
+ if (n < 1e6) return `${Math.floor(n / 1e3)} KB`;
2413
+ if (n < 1e9) return `${trim(n / 1e6, 1)} MB`;
2414
+ return `${trim(n / 1e9, 2)} GB`;
2415
+ }
2416
+ var MIME_NAMES = {
2417
+ "application/pdf": "PDF",
2418
+ "application/msword": "DOC",
2419
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "DOCX",
2420
+ "application/json": "JSON",
2421
+ "text/plain": "plain text",
2422
+ "text/markdown": "Markdown",
2423
+ "text/csv": "CSV",
2424
+ "image/png": "PNG",
2425
+ "image/jpeg": "JPEG"
2426
+ };
2427
+ function describeAccept(accept) {
2428
+ const names = accept.map((entry) => {
2429
+ if (entry.startsWith(".")) return entry.slice(1).toUpperCase();
2430
+ if (entry.endsWith("/*")) return `any ${entry.slice(0, -2)}`;
2431
+ return MIME_NAMES[entry] ?? entry.split("/").pop().toUpperCase();
2432
+ });
2433
+ const unique = [...new Set(names)];
2434
+ if (unique.length <= 1) return unique[0] ?? "";
2435
+ return `${unique.slice(0, -1).join(", ")} or ${unique.at(-1)}`;
2436
+ }
2437
+ function rejectionMessage(reason, detail) {
2438
+ switch (reason) {
2439
+ case "type":
2440
+ return `${detail.name} is not an accepted file type. Use ${detail.acceptLabel}.`;
2441
+ case "size":
2442
+ return `${detail.name} is ${formatSize(detail.size ?? 0)}, over the ${formatSize(detail.maxFileSize ?? 0)} limit.`;
2443
+ case "count":
2444
+ return detail.maxFiles === 1 ? "Choose one file." : `Up to ${detail.maxFiles} files. Remove one to add another.`;
2445
+ }
2446
+ }
2447
+ function escapeHtml(text) {
2448
+ return text.replace(
2449
+ /[&<>"']/g,
2450
+ (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[ch]
2451
+ );
2452
+ }
2453
+ var extensionOf = (name) => {
2454
+ const dot = name.lastIndexOf(".");
2455
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
2456
+ };
2457
+ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set(["txt", "text", "md", "markdown"]);
2458
+ var TEXT_TYPES = /* @__PURE__ */ new Set(["text/plain", "text/markdown", "text/x-markdown"]);
2459
+ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "webp", "avif", "svg", "bmp"]);
2460
+ var UNDRAWABLE_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/heic", "image/heif", "image/tiff"]);
2461
+ function previewKind(file) {
2462
+ const ext = extensionOf(file.name);
2463
+ const type = file.type.toLowerCase();
2464
+ if (ext === "pdf" || type === "application/pdf") return "pdf";
2465
+ if (TEXT_EXTENSIONS.has(ext) || TEXT_TYPES.has(type)) return "text";
2466
+ if (UNDRAWABLE_IMAGE_TYPES.has(type)) return null;
2467
+ if (IMAGE_EXTENSIONS.has(ext) || type.startsWith("image/")) return "image";
2468
+ return null;
2469
+ }
2470
+ function fileTypeLabel(file) {
2471
+ const ext = extensionOf(file.name);
2472
+ if (ext) return ext.toUpperCase();
2473
+ return MIME_NAMES[file.type.toLowerCase()] ?? (file.type.split("/").pop()?.toUpperCase() || "File");
2474
+ }
2475
+
2476
+ // src/utils/useMediaQuery.ts
2477
+ import { useCallback as useCallback2, useSyncExternalStore } from "react";
2478
+ function useMediaQuery(query) {
2479
+ const subscribe = useCallback2(
2480
+ (onChange) => {
2481
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => {
2482
+ };
2483
+ const list = window.matchMedia(query);
2484
+ list.addEventListener?.("change", onChange);
2485
+ return () => list.removeEventListener?.("change", onChange);
2486
+ },
2487
+ [query]
2488
+ );
2489
+ return useSyncExternalStore(
2490
+ subscribe,
2491
+ () => typeof window.matchMedia === "function" && window.matchMedia(query).matches,
2492
+ () => false
2493
+ );
2494
+ }
2495
+
2496
+ // src/components/FilePreviewDialog/FilePreviewDialog.tsx
2497
+ import { Fragment as Fragment8, jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2498
+ var TEXT_LIMIT = 1e6;
2499
+ var NARROW = "(max-width: 48rem)";
2500
+ function FilePreviewDialog({ file, open, onOpenChange, finalFocus }) {
2501
+ const kind = file ? previewKind(file) : null;
2502
+ const narrow = useMediaQuery(NARROW);
2503
+ const pdfInTab = kind === "pdf" && narrow;
2504
+ return /* @__PURE__ */ jsx29(Dialog.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs24(
2505
+ Dialog.Content,
2506
+ {
2507
+ size: "lg",
2508
+ className: "sc-file-preview",
2509
+ "data-kind": kind ?? "none",
2510
+ finalFocus,
2511
+ children: [
2512
+ /* @__PURE__ */ jsx29(Dialog.Close, {}),
2513
+ file ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
2514
+ /* @__PURE__ */ jsx29(Dialog.Title, { children: file.name }),
2515
+ /* @__PURE__ */ jsxs24(Dialog.Description, { children: [
2516
+ fileTypeLabel(file),
2517
+ ", ",
2518
+ formatListSize(file.size)
2519
+ ] }),
2520
+ /* @__PURE__ */ jsx29(Dialog.Body, { children: /* @__PURE__ */ jsx29(PreviewBody, { file, kind, open, pdfInTab }) })
2521
+ ] }) : /* @__PURE__ */ jsx29(Dialog.Title, { children: "Preview" })
2522
+ ]
2523
+ }
2524
+ ) });
2525
+ }
2526
+ function PreviewBody({
2527
+ file,
2528
+ kind,
2529
+ open,
2530
+ pdfInTab
2531
+ }) {
2532
+ const frameRef = useRef3(null);
2533
+ const imageRef = useRef3(null);
2534
+ const linkRef = useRef3(null);
2535
+ const [text, setText] = useState5(null);
2536
+ useEffect3(() => {
2537
+ if (!open || kind !== "pdf" && kind !== "image") return void 0;
2538
+ const url = URL.createObjectURL(kind === "pdf" ? new Blob([file], { type: "application/pdf" }) : file);
2539
+ if (frameRef.current) frameRef.current.src = url;
2540
+ if (imageRef.current) imageRef.current.src = url;
2541
+ if (linkRef.current) linkRef.current.href = url;
2542
+ return () => URL.revokeObjectURL(url);
2543
+ }, [open, file, kind, pdfInTab]);
2544
+ useEffect3(() => {
2545
+ if (!open || kind !== "text") return void 0;
2546
+ let cancelled = false;
2547
+ void file.slice(0, TEXT_LIMIT).text().then((value) => {
2548
+ if (!cancelled) setText({ file, value });
2549
+ });
2550
+ return () => {
2551
+ cancelled = true;
2552
+ };
2553
+ }, [open, file, kind]);
2554
+ if (kind === "pdf") {
2555
+ const link = (
2556
+ // eslint-disable-next-line jsx-a11y/anchor-is-valid -- the effect above sets href to the object URL on mount
2557
+ /* @__PURE__ */ jsxs24(
2558
+ "a",
2559
+ {
2560
+ ref: linkRef,
2561
+ className: "sc-button",
2562
+ "data-variant": pdfInTab ? "primary" : "secondary",
2563
+ "data-size": "md",
2564
+ target: "_blank",
2565
+ rel: "noopener",
2566
+ children: [
2567
+ "Open in a new tab",
2568
+ /* @__PURE__ */ jsx29(ExternalLinkIcon, {})
2569
+ ]
2570
+ }
2571
+ )
2572
+ );
2573
+ return pdfInTab ? /* @__PURE__ */ jsxs24("div", { className: "sc-file-preview__tab", children: [
2574
+ /* @__PURE__ */ jsx29("p", { className: "sc-file-preview__note", children: "On a small screen the PDF reads best in its own tab." }),
2575
+ link
2576
+ ] }) : /* @__PURE__ */ jsxs24(Fragment8, { children: [
2577
+ /* @__PURE__ */ jsx29("iframe", { ref: frameRef, className: "sc-file-preview__frame", title: `Preview of ${file.name}` }),
2578
+ /* @__PURE__ */ jsx29("div", { className: "sc-file-preview__actions", children: link })
2579
+ ] });
2580
+ }
2581
+ if (kind === "text") {
2582
+ const value = text?.file === file ? text.value : null;
2583
+ return /* @__PURE__ */ jsxs24(Fragment8, { children: [
2584
+ /* @__PURE__ */ jsx29(
2585
+ "pre",
2586
+ {
2587
+ className: "sc-file-preview__text",
2588
+ role: "region",
2589
+ tabIndex: 0,
2590
+ "aria-label": `Contents of ${file.name}`,
2591
+ "aria-busy": value == null || void 0,
2592
+ children: value
2593
+ }
2594
+ ),
2595
+ file.size > TEXT_LIMIT ? /* @__PURE__ */ jsxs24("p", { className: "sc-file-preview__note", children: [
2596
+ "Showing the first ",
2597
+ formatSize(TEXT_LIMIT),
2598
+ "."
2599
+ ] }) : null
2600
+ ] });
2601
+ }
2602
+ if (kind === "image") {
2603
+ return /* @__PURE__ */ jsx29("img", { ref: imageRef, className: "sc-file-preview__image", alt: file.name });
2604
+ }
2605
+ return /* @__PURE__ */ jsxs24("p", { className: "sc-file-preview__note", children: [
2606
+ "There is no preview for ",
2607
+ fileTypeLabel(file),
2608
+ " files."
2609
+ ] });
2610
+ }
2611
+
2612
+ // src/components/FileInput/FileInput.tsx
2613
+ import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
2614
+ var REMOVE_ICON = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg>';
2615
+ var sameFiles = (a, b) => a.length === b.length && a.every((file, i) => file === b[i]);
2616
+ function FileInput({
2617
+ label,
2618
+ hideLabel = false,
2619
+ description,
2620
+ error,
2621
+ invalid = false,
2622
+ required = false,
2623
+ disabled = false,
2624
+ name,
2625
+ size = "md",
2626
+ className,
2627
+ accept,
2628
+ acceptLabel,
2629
+ maxFileSize,
2630
+ multiple = false,
2631
+ maxFiles,
2632
+ files,
2633
+ defaultFiles,
2634
+ onFilesChange,
2635
+ idleLabel,
2636
+ browseLabel = "browse",
2637
+ preview = true
2638
+ }) {
2639
+ const id = useId4();
2640
+ const labelId = `${id}label`;
2641
+ const descriptionId = `${id}description`;
2642
+ const messagesId = `${id}messages`;
2643
+ const controlled = files !== void 0;
2644
+ const [uncontrolledFiles, setUncontrolledFiles] = useState6(defaultFiles ?? []);
2645
+ const current = controlled ? files : uncontrolledFiles;
2646
+ const [rejections, setRejections] = useState6([]);
2647
+ const [inputId, setInputId] = useState6();
2648
+ const [previewFile, setPreviewFile] = useState6(null);
2649
+ const [previewOpen, setPreviewOpen] = useState6(false);
2650
+ const previewTriggerRef = useRef4(null);
2651
+ const [pondVersion, setPondVersion] = useState6(0);
2652
+ const hostRef = useRef4(null);
2653
+ const pondRef = useRef4(null);
2654
+ const moduleRef = useRef4(null);
2655
+ const patchRef = useRef4(() => {
2656
+ });
2657
+ const settledRef = useRef4(current);
2658
+ const targetRef = useRef4(null);
2659
+ const acceptList = parseAccept(accept);
2660
+ const acceptAttr = acceptList.join(",");
2661
+ const maxBytes = parseSize(maxFileSize);
2662
+ const messages = [...rejections, ...error != null ? [error] : []];
2663
+ const isInvalid = invalid || messages.length > 0;
2664
+ const idleText = idleLabel ?? (multiple ? "Drop files here or" : current.length > 0 ? "Drop another file to replace it, or" : "Drop a file here or");
2665
+ const live = {
2666
+ acceptList,
2667
+ acceptLabel: acceptLabel ?? describeAccept(acceptList),
2668
+ maxBytes,
2669
+ maxFiles: multiple ? maxFiles : 1,
2670
+ multiple,
2671
+ current,
2672
+ controlled,
2673
+ onFilesChange,
2674
+ labelId,
2675
+ describedBy: [description != null ? descriptionId : null, messages.length > 0 ? messagesId : null],
2676
+ isInvalid,
2677
+ acceptAttr,
2678
+ preview: preview && !disabled,
2679
+ openPreview: (file, trigger) => {
2680
+ previewTriggerRef.current = trigger;
2681
+ setPreviewFile(file);
2682
+ setPreviewOpen(true);
2683
+ }
2684
+ };
2685
+ const liveRef = useRef4(live);
2686
+ useEffect4(() => {
2687
+ liveRef.current = live;
2688
+ });
2689
+ useEffect4(() => {
2690
+ let cancelled = false;
2691
+ let pond = null;
2692
+ let observer = null;
2693
+ const host = hostRef.current;
2694
+ if (!host) return void 0;
2695
+ const reject = (reason, file) => {
2696
+ const l = liveRef.current;
2697
+ const message = rejectionMessage(reason, {
2698
+ name: file?.name,
2699
+ size: file?.size,
2700
+ acceptLabel: l.acceptLabel,
2701
+ maxFileSize: l.maxBytes,
2702
+ maxFiles: l.maxFiles
2703
+ });
2704
+ setRejections((list) => list.includes(message) ? list : [...list, message]);
2705
+ if (file && targetRef.current) {
2706
+ targetRef.current = targetRef.current.filter((f) => f !== file);
2707
+ queueMicrotask(emit);
2708
+ }
2709
+ };
2710
+ const startAttempt = () => setRejections((list) => list.length ? [] : list);
2711
+ let removeIntent = false;
2712
+ const acceptedIds = /* @__PURE__ */ new Set();
2713
+ const noteRemove = (event) => {
2714
+ if (event.target?.closest?.(".filepond--action-remove-item")) removeIntent = true;
2715
+ };
2716
+ const onPreviewClick = (event) => {
2717
+ const button = event.target?.closest?.(
2718
+ ".sc-file-input__preview"
2719
+ );
2720
+ const file = button?.dataset.fileId ? pond?.getFile(button.dataset.fileId)?.file : null;
2721
+ if (!button || !(file instanceof File)) return;
2722
+ event.stopPropagation();
2723
+ liveRef.current.openPreview(file, button);
2724
+ };
2725
+ const emit = () => {
2726
+ const mod = moduleRef.current;
2727
+ if (!pond || !mod) return;
2728
+ const idle = pond.getFiles().filter((item) => item.status === mod.FileStatus.IDLE).map((item) => item.file);
2729
+ const accepted = liveRef.current.multiple ? idle : idle.slice(-1);
2730
+ const target = targetRef.current;
2731
+ if (target) {
2732
+ if (!sameFiles(accepted, target)) return;
2733
+ targetRef.current = null;
2734
+ settledRef.current = accepted;
2735
+ if (!sameFiles(accepted, liveRef.current.current)) {
2736
+ if (!liveRef.current.controlled) setUncontrolledFiles(accepted);
2737
+ liveRef.current.onFilesChange?.(accepted);
2738
+ }
2739
+ return;
2740
+ }
2741
+ if (sameFiles(accepted, settledRef.current)) return;
2742
+ settledRef.current = accepted;
2743
+ if (!liveRef.current.controlled) setUncontrolledFiles(accepted);
2744
+ liveRef.current.onFilesChange?.(accepted);
2745
+ };
2746
+ const patch = () => {
2747
+ if (!pond) return;
2748
+ const l = liveRef.current;
2749
+ const root = pond.element;
2750
+ if (!root) return;
2751
+ const set = (el, attr, value) => {
2752
+ if (!el || el.getAttribute(attr) === value) return;
2753
+ if (value == null) el.removeAttribute(attr);
2754
+ else el.setAttribute(attr, value);
2755
+ };
2756
+ const input = root.querySelector("input.filepond--browser");
2757
+ const dropLabel = root.querySelector(".filepond--drop-label label");
2758
+ set(input, "aria-labelledby", l.labelId);
2759
+ set(input, "aria-describedby", [dropLabel?.id, ...l.describedBy].filter(Boolean).join(" ") || null);
2760
+ set(input, "aria-invalid", l.isInvalid ? "true" : null);
2761
+ set(input, "accept", l.acceptAttr || null);
2762
+ set(input, "multiple", l.multiple ? "" : null);
2763
+ set(root.querySelector(".filepond--label-action"), "tabindex", null);
2764
+ for (const item of root.querySelectorAll(".filepond--item")) {
2765
+ const fileName = item.querySelector(".filepond--file-info-main")?.textContent?.trim();
2766
+ const remove = item.querySelector(".filepond--action-remove-item");
2767
+ if (fileName) set(remove, "aria-label", `Remove ${fileName}`);
2768
+ patchPreview(item, remove);
2769
+ }
2770
+ setInputId(input?.id || void 0);
2771
+ };
2772
+ const patchPreview = (item, remove) => {
2773
+ const row = item.querySelector(".filepond--file");
2774
+ if (!row) return;
2775
+ let button = row.querySelector(":scope > .sc-file-input__preview");
2776
+ const id2 = item.id.replace(/^filepond--item-/, "");
2777
+ const file = pond?.getFile(id2)?.file;
2778
+ if (!liveRef.current.preview || !(file instanceof File) || previewKind(file) == null) {
2779
+ button?.remove();
2780
+ return;
2781
+ }
2782
+ if (!button) {
2783
+ button = document.createElement("button");
2784
+ button.type = "button";
2785
+ button.className = "sc-file-input__preview";
2786
+ row.insertBefore(button, remove ?? row.firstChild);
2787
+ }
2788
+ if (button.dataset.fileId !== id2) button.dataset.fileId = id2;
2789
+ if (button.textContent !== file.name) button.textContent = file.name;
2790
+ const name2 = `Preview ${file.name}`;
2791
+ if (button.getAttribute("aria-label") !== name2) button.setAttribute("aria-label", name2);
2792
+ };
2793
+ void import("filepond").then((mod) => {
2794
+ if (cancelled || !mod.supported()) return;
2795
+ moduleRef.current = mod;
2796
+ const options = {
2797
+ credits: false,
2798
+ allowPaste: false,
2799
+ allowReorder: false,
2800
+ allowProcess: false,
2801
+ allowRevert: false,
2802
+ instantUpload: false,
2803
+ dropOnPage: false,
2804
+ // Always multi: FilePond's own single mode replaces the current file *before* validating the new
2805
+ // one, so a rejected file would discard a good one. Replacement happens in `addfile` instead.
2806
+ allowMultiple: true,
2807
+ allowReplace: false,
2808
+ itemInsertLocation: "after",
2809
+ fileSizeBase: 1e3,
2810
+ iconRemove: REMOVE_ICON,
2811
+ labelButtonRemoveItem: "Remove",
2812
+ styleButtonRemoveItemPosition: "right",
2813
+ beforeAddFile: (item) => {
2814
+ const l = liveRef.current;
2815
+ const file = item.file;
2816
+ if (!matchesAccept(file, l.acceptList)) return reject("type", file), false;
2817
+ if (l.maxBytes != null && file.size > l.maxBytes) return reject("size", file), false;
2818
+ return true;
2819
+ }
2820
+ };
2821
+ pond = mod.create(options);
2822
+ const root = pond.element;
2823
+ if (!root) return;
2824
+ host.appendChild(root);
2825
+ pondRef.current = pond;
2826
+ patchRef.current = patch;
2827
+ pond.on("addfile", (error2, added) => {
2828
+ if (!error2) acceptedIds.add(added.id);
2829
+ if (!error2 && !liveRef.current.multiple) {
2830
+ for (const other of pond?.getFiles() ?? []) if (other.id !== added.id) pond?.removeFile(other.id);
2831
+ }
2832
+ emit();
2833
+ });
2834
+ pond.on("removefile", (_error, removed) => {
2835
+ const active = document.activeElement;
2836
+ const inRemovedRow = active?.closest(".filepond--item")?.id === `filepond--item-${removed.id}`;
2837
+ if (inRemovedRow || removeIntent && (active == null || active === document.body))
2838
+ root.querySelector("input.filepond--browser")?.focus();
2839
+ removeIntent = false;
2840
+ if (acceptedIds.delete(removed.id)) startAttempt();
2841
+ emit();
2842
+ });
2843
+ pond.on("updatefiles", emit);
2844
+ pond.on("warning", (warning) => {
2845
+ if (warning?.body === "Max files") reject("count");
2846
+ });
2847
+ observer = new MutationObserver(patch);
2848
+ observer.observe(root, {
2849
+ subtree: true,
2850
+ childList: true,
2851
+ attributes: true,
2852
+ attributeFilter: ["tabindex", "aria-labelledby", "aria-describedby", "accept", "id", "multiple"]
2853
+ });
2854
+ host.addEventListener("drop", startAttempt, true);
2855
+ host.addEventListener("change", startAttempt, true);
2856
+ host.addEventListener("click", noteRemove, true);
2857
+ host.addEventListener("click", onPreviewClick);
2858
+ settledRef.current = [];
2859
+ setPondVersion((v) => v + 1);
2860
+ });
2861
+ return () => {
2862
+ cancelled = true;
2863
+ host.removeEventListener("drop", startAttempt, true);
2864
+ host.removeEventListener("change", startAttempt, true);
2865
+ host.removeEventListener("click", noteRemove, true);
2866
+ host.removeEventListener("click", onPreviewClick);
2867
+ observer?.disconnect();
2868
+ if (pond) {
2869
+ pond.element?.remove();
2870
+ pond.destroy();
2871
+ }
2872
+ pondRef.current = null;
2873
+ patchRef.current = () => {
2874
+ };
2875
+ };
2876
+ }, []);
2877
+ useEffect4(() => {
2878
+ const pond = pondRef.current;
2879
+ if (!pond) return;
2880
+ pond.setOptions({
2881
+ name: name ?? "",
2882
+ storeAsFile: name != null,
2883
+ disabled,
2884
+ required,
2885
+ maxFiles: multiple ? maxFiles ?? null : null,
2886
+ allowRemove: !disabled,
2887
+ labelIdle: `${escapeHtml(idleText)} <span class="filepond--label-action">${escapeHtml(browseLabel)}</span>`
2888
+ });
2889
+ patchRef.current();
2890
+ }, [
2891
+ pondVersion,
2892
+ name,
2893
+ disabled,
2894
+ required,
2895
+ multiple,
2896
+ maxFiles,
2897
+ idleText,
2898
+ browseLabel,
2899
+ labelId,
2900
+ acceptAttr,
2901
+ isInvalid,
2902
+ description,
2903
+ messages.length,
2904
+ preview
2905
+ ]);
2906
+ useEffect4(() => {
2907
+ const pond = pondRef.current;
2908
+ const mod = moduleRef.current;
2909
+ if (!pond || !mod) return;
2910
+ const items = pond.getFiles();
2911
+ const idle = items.filter((item) => item.status === mod.FileStatus.IDLE);
2912
+ const inPond = idle.map((item) => item.file);
2913
+ if (sameFiles(inPond, current)) {
2914
+ settledRef.current = current;
2915
+ return;
2916
+ }
2917
+ targetRef.current = current;
2918
+ for (const item of idle) if (!current.includes(item.file)) pond.removeFile(item.id);
2919
+ const toAdd = current.filter((file) => !inPond.includes(file));
2920
+ if (toAdd.length > 0) void pond.addFiles(toAdd);
2921
+ }, [current, pondVersion]);
2922
+ const ready = pondVersion > 0;
2923
+ return /* @__PURE__ */ jsxs25(
2924
+ "div",
2925
+ {
2926
+ className: cx("sc-field", "sc-file-input-field", className),
2927
+ "data-size": size,
2928
+ "data-disabled": disabled ? "" : void 0,
2929
+ "data-invalid": isInvalid ? "" : void 0,
2930
+ children: [
2931
+ /* @__PURE__ */ jsxs25(
2932
+ "label",
2933
+ {
2934
+ id: labelId,
2935
+ htmlFor: inputId,
2936
+ className: cx("sc-field__label", hideLabel && "sc-visually-hidden"),
2937
+ children: [
2938
+ label,
2939
+ required ? /* @__PURE__ */ jsx30("span", { className: "sc-field__required", "aria-hidden": "true", children: "*" }) : null
2940
+ ]
2941
+ }
2942
+ ),
2943
+ /* @__PURE__ */ jsxs25(
2944
+ "div",
2945
+ {
2946
+ className: "sc-file-input",
2947
+ "data-ready": ready ? "" : void 0,
2948
+ "data-disabled": disabled ? "" : void 0,
2949
+ "data-invalid": isInvalid ? "" : void 0,
2950
+ "data-multiple": multiple ? "" : void 0,
2951
+ children: [
2952
+ ready ? null : (
2953
+ // Server render and first paint: the same drop zone, inert until FilePond takes over.
2954
+ /* @__PURE__ */ jsxs25("div", { className: "sc-file-input__placeholder", "aria-hidden": "true", children: [
2955
+ idleText,
2956
+ " ",
2957
+ /* @__PURE__ */ jsx30("span", { className: "sc-file-input__browse", children: browseLabel })
2958
+ ] })
2959
+ ),
2960
+ /* @__PURE__ */ jsx30("div", { ref: hostRef, className: "sc-file-input__pond" })
2961
+ ]
2962
+ }
2963
+ ),
2964
+ description != null ? /* @__PURE__ */ jsx30("p", { id: descriptionId, className: "sc-field__description", children: description }) : null,
2965
+ /* @__PURE__ */ jsx30("div", { id: messagesId, className: "sc-file-input__messages", "aria-live": "polite", children: messages.map((message, i) => /* @__PURE__ */ jsx30("p", { className: "sc-field__error", children: message }, i)) }),
2966
+ previewFile ? /* @__PURE__ */ jsx30(
2967
+ FilePreviewDialog,
2968
+ {
2969
+ file: previewFile,
2970
+ open: previewOpen,
2971
+ onOpenChange: setPreviewOpen,
2972
+ finalFocus: () => {
2973
+ const trigger = previewTriggerRef.current;
2974
+ if (trigger?.isConnected) return trigger;
2975
+ return hostRef.current?.querySelector("input.filepond--browser") ?? true;
2976
+ }
2977
+ }
2978
+ ) : null
2979
+ ]
2980
+ }
2981
+ );
2982
+ }
2983
+
2984
+ // src/components/PriorityNav/PriorityNav.tsx
2985
+ import {
2986
+ useEffect as useEffect5,
2987
+ useId as useId5,
2988
+ useLayoutEffect as useLayoutEffect2,
2989
+ useRef as useRef5,
2990
+ useState as useState7
2991
+ } from "react";
2992
+
2993
+ // src/components/PriorityNav/fit.ts
2994
+ var EPSILON = 0.5;
2995
+ function rowWidth(widths, gap) {
2996
+ let total = 0;
2997
+ for (const w of widths) total += w;
2998
+ return widths.length > 1 ? total + gap * (widths.length - 1) : total;
2999
+ }
3000
+ function fitCount({ widths, current, available, more, gap }) {
3001
+ const n = widths.length;
3002
+ const hasCurrent = current >= 0 && current < n;
3003
+ const others = hasCurrent ? n - 1 : n;
3004
+ if (rowWidth(widths, gap) <= available + EPSILON) return others;
3005
+ let used = more + (hasCurrent ? widths[current] + gap : 0);
3006
+ let count = 0;
3007
+ for (let i = 0; i < n; i++) {
3008
+ if (i === current) continue;
3009
+ const next = used + widths[i] + gap;
3010
+ if (next > available + EPSILON) break;
3011
+ used = next;
3012
+ count++;
3013
+ }
3014
+ return count;
3015
+ }
3016
+ function inlineFlags(length, current, count) {
3017
+ const flags = [];
3018
+ let seen = 0;
3019
+ for (let i = 0; i < length; i++) {
3020
+ if (i === current) flags.push(true);
3021
+ else flags.push(seen++ < count);
3022
+ }
3023
+ return flags;
3024
+ }
3025
+ function isOverflowing({ widths, gap, available, was, hysteresis }) {
3026
+ const total = rowWidth(widths, gap);
3027
+ return was ? total + Math.max(0, hysteresis) > available + EPSILON : total > available + EPSILON;
3028
+ }
3029
+
3030
+ // src/components/PriorityNav/place.ts
3031
+ var clamp = (value, min, max) => Math.max(min, Math.min(value, max));
3032
+ function placePanel({ anchor, panel, viewport, gutter, offset, rtl = false }) {
3033
+ const endAligned = rtl ? anchor.left : anchor.right - panel.width;
3034
+ const startAligned = rtl ? anchor.right - panel.width : anchor.left;
3035
+ const fitsX = (x2) => x2 >= gutter && x2 + panel.width <= viewport.width - gutter;
3036
+ const x = fitsX(endAligned) ? endAligned : fitsX(startAligned) ? startAligned : endAligned;
3037
+ const left = clamp(x, gutter, Math.max(gutter, viewport.width - gutter - panel.width));
3038
+ const below = anchor.bottom + offset;
3039
+ const above = anchor.top - offset - panel.height;
3040
+ const fitsBelow = below + panel.height <= viewport.height - gutter;
3041
+ const top = fitsBelow || above < gutter ? below : above;
3042
+ return { left, top };
3043
+ }
3044
+
3045
+ // src/components/PriorityNav/PriorityNav.tsx
3046
+ import { Fragment as Fragment9, jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
3047
+ var defaultCountLabel2 = (n) => n === 1 ? "1 link" : `${n} links`;
3048
+ var defaultLink = ({ children, ...props }) => /* @__PURE__ */ jsx31("a", { ...props, children });
3049
+ var keyOf = (item) => item.id ?? item.href;
3050
+ var badgeOf = (item) => item.badge === "dot" ? "dot" : typeof item.badge === "number" && item.badge > 0 && Number.isFinite(item.badge) ? item.badge : null;
3051
+ var badgeTextOf = (item) => {
3052
+ const badge = badgeOf(item);
3053
+ if (badge === null) return null;
3054
+ return item.badgeLabel ?? (badge === "dot" ? null : String(badge));
3055
+ };
3056
+ function Badge2({ value }) {
3057
+ return /* @__PURE__ */ jsx31("span", { className: "sc-priority-nav__badge", "data-kind": value === "dot" ? "dot" : "count", "aria-hidden": "true", children: value === "dot" ? null : value });
3058
+ }
3059
+ var supportsAnchor = () => typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("anchor-name: --a");
3060
+ var supportsPopover = (el) => typeof el.showPopover === "function";
3061
+ function signatureOf(items) {
3062
+ return items.map((item) => {
3063
+ const text = typeof item.label === "string" || typeof item.label === "number" ? item.label : "";
3064
+ return `${keyOf(item)}${text}${badgeOf(item) ?? ""}`;
3065
+ }).join("");
3066
+ }
3067
+ function PriorityNav({
3068
+ items,
3069
+ moreLabel = "More",
3070
+ moreCountLabel = defaultCountLabel2,
3071
+ renderLink = defaultLink,
3072
+ onOverflowChange,
3073
+ overflowHysteresis = 0,
3074
+ className,
3075
+ style,
3076
+ ...rest
3077
+ }) {
3078
+ const signature = signatureOf(items);
3079
+ const current = items.findIndex((item) => item.current);
3080
+ const [fit, setFit] = useState7({ signature: null, current: -1, count: 0, overflowing: false });
3081
+ const [open, setOpen] = useState7(false);
3082
+ const panelId = useId5();
3083
+ const listRef = useRef5(null);
3084
+ const moreRef = useRef5(null);
3085
+ const toggleRef = useRef5(null);
3086
+ const panelRef = useRef5(null);
3087
+ const measured = useRef5(null);
3088
+ const currentRef = useRef5(current);
3089
+ const fitRef = useRef5(fit);
3090
+ const hysteresisRef = useRef5(overflowHysteresis);
3091
+ const onOverflowRef = useRef5(onOverflowChange);
3092
+ const solve = (m, cur, was) => ({
3093
+ count: fitCount({ ...m, current: cur }),
3094
+ overflowing: isOverflowing({ ...m, was, hysteresis: hysteresisRef.current })
3095
+ });
3096
+ const measuring = fit.signature !== signature;
3097
+ useLayoutEffect2(() => {
3098
+ currentRef.current = current;
3099
+ fitRef.current = fit;
3100
+ hysteresisRef.current = overflowHysteresis;
3101
+ onOverflowRef.current = onOverflowChange;
3102
+ });
3103
+ useLayoutEffect2(() => {
3104
+ const list = listRef.current;
3105
+ const more = moreRef.current;
3106
+ if (!list || !more) return;
3107
+ if (measuring) {
3108
+ const widths = Array.from(
3109
+ list.querySelectorAll(":scope > .sc-priority-nav__item"),
3110
+ (el) => el.getBoundingClientRect().width
3111
+ );
3112
+ measured.current = {
3113
+ widths,
3114
+ more: more.getBoundingClientRect().width,
3115
+ gap: parseFloat(getComputedStyle(list).columnGap) || 0,
3116
+ available: list.clientWidth
3117
+ };
3118
+ } else if (fit.current === current) return;
3119
+ const m = measured.current;
3120
+ if (!m) return;
3121
+ setFit({ signature, current, ...solve(m, current, fit.overflowing) });
3122
+ }, [measuring, signature, current, fit]);
3123
+ useEffect5(() => {
3124
+ const list = listRef.current;
3125
+ const more = moreRef.current;
3126
+ if (!list || !more || typeof ResizeObserver === "undefined") return;
3127
+ const observer = new ResizeObserver((entries) => {
3128
+ const m = measured.current;
3129
+ if (!m) return;
3130
+ for (const entry of entries) {
3131
+ if (entry.target === list)
3132
+ m.available = entry.contentBoxSize?.[0]?.inlineSize ?? entry.contentRect.width;
3133
+ else {
3134
+ const width = entry.borderBoxSize?.[0]?.inlineSize ?? 0;
3135
+ if (width > 0) m.more = width;
3136
+ }
3137
+ }
3138
+ const prev = fitRef.current;
3139
+ if (prev.signature === null) return;
3140
+ const next = solve(m, currentRef.current, prev.overflowing);
3141
+ if (prev.count !== next.count || prev.overflowing !== next.overflowing) setFit({ ...prev, ...next });
3142
+ });
3143
+ observer.observe(list);
3144
+ observer.observe(more);
3145
+ return () => observer.disconnect();
3146
+ }, []);
3147
+ const reported = useRef5(false);
3148
+ useEffect5(() => {
3149
+ if (reported.current === fit.overflowing) return;
3150
+ reported.current = fit.overflowing;
3151
+ onOverflowRef.current?.(fit.overflowing);
3152
+ }, [fit.overflowing]);
3153
+ useEffect5(() => {
3154
+ const fonts = typeof document === "undefined" ? void 0 : document.fonts;
3155
+ if (!fonts || fonts.status === "loaded") return;
3156
+ let live = true;
3157
+ void fonts.ready.then(() => {
3158
+ if (live) setFit((prev) => ({ ...prev, signature: null }));
3159
+ });
3160
+ return () => {
3161
+ live = false;
3162
+ };
3163
+ }, []);
3164
+ const flags = measuring ? null : inlineFlags(items.length, current, fit.count);
3165
+ const overflow = flags ? items.filter((_, i) => !flags[i]) : [];
3166
+ const expanded = open && overflow.length > 0;
3167
+ const overflowBadges = overflow.map(badgeOf).filter((b) => b !== null);
3168
+ const overflowCount = overflowBadges.reduce((sum, b) => sum + (b === "dot" ? 0 : b), 0);
3169
+ const moreBadge = overflowCount > 0 ? overflowCount : overflowBadges.length > 0 ? "dot" : null;
3170
+ const moreBadgeText = overflow.map(badgeTextOf).filter((text) => text !== null).map((text) => `, ${text}`).join("");
3171
+ useLayoutEffect2(() => {
3172
+ const panel = panelRef.current;
3173
+ if (!panel || !supportsPopover(panel)) return;
3174
+ if (!panel.hasAttribute("popover")) panel.setAttribute("popover", "manual");
3175
+ const shown = panel.matches(":popover-open");
3176
+ if (expanded && !shown) panel.showPopover();
3177
+ else if (!expanded && shown) panel.hidePopover();
3178
+ }, [expanded]);
3179
+ useLayoutEffect2(() => {
3180
+ const panel = panelRef.current;
3181
+ const toggle2 = toggleRef.current;
3182
+ if (!expanded || !panel || !toggle2 || supportsAnchor()) return;
3183
+ const place = () => {
3184
+ const anchor = toggle2.getBoundingClientRect();
3185
+ const box = panel.getBoundingClientRect();
3186
+ const css = getComputedStyle(panel);
3187
+ const { left, top } = placePanel({
3188
+ anchor,
3189
+ panel: { width: box.width, height: box.height },
3190
+ viewport: { width: document.documentElement.clientWidth, height: window.innerHeight },
3191
+ gutter: parseFloat(css.getPropertyValue("--sc-spacing-md")) || 16,
3192
+ offset: parseFloat(css.getPropertyValue("--sc-spacing-2xs")) || 4,
3193
+ rtl: css.direction === "rtl"
3194
+ });
3195
+ panel.style.left = `${left}px`;
3196
+ panel.style.top = `${top}px`;
3197
+ };
3198
+ place();
3199
+ window.addEventListener("resize", place, { passive: true });
3200
+ window.addEventListener("scroll", place, { passive: true, capture: true });
3201
+ return () => {
3202
+ window.removeEventListener("resize", place);
3203
+ window.removeEventListener("scroll", place, { capture: true });
3204
+ panel.style.left = "";
3205
+ panel.style.top = "";
3206
+ };
3207
+ }, [expanded]);
3208
+ useEffect5(() => {
3209
+ if (!expanded) return;
3210
+ const onPointerDown = (event) => {
3211
+ if (!moreRef.current?.contains(event.target)) setOpen(false);
3212
+ };
3213
+ const onKeyDown = (event) => {
3214
+ if (event.key !== "Escape") return;
3215
+ const active = document.activeElement;
3216
+ setOpen(false);
3217
+ if (!active || active === document.body || moreRef.current?.contains(active))
3218
+ toggleRef.current?.focus();
3219
+ };
3220
+ document.addEventListener("pointerdown", onPointerDown);
3221
+ document.addEventListener("keydown", onKeyDown);
3222
+ return () => {
3223
+ document.removeEventListener("pointerdown", onPointerDown);
3224
+ document.removeEventListener("keydown", onKeyDown);
3225
+ };
3226
+ }, [expanded]);
3227
+ const onBlur = (event) => {
3228
+ if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
3229
+ };
3230
+ const link = (item, inPanel) => {
3231
+ const badge = badgeOf(item);
3232
+ const badgeText = badgeTextOf(item);
3233
+ return renderLink(
3234
+ {
3235
+ href: item.href,
3236
+ className: "sc-priority-nav__link",
3237
+ "aria-current": item.current ? "page" : void 0,
3238
+ onClick: inPanel ? () => setOpen(false) : void 0,
3239
+ children: /* @__PURE__ */ jsxs26(Fragment9, { children: [
3240
+ /* @__PURE__ */ jsx31("span", { className: "sc-priority-nav__label", children: item.label }),
3241
+ badge !== null ? /* @__PURE__ */ jsx31(Badge2, { value: badge }) : null,
3242
+ badgeText !== null ? /* @__PURE__ */ jsxs26("span", { className: "sc-visually-hidden", children: [
3243
+ ", ",
3244
+ badgeText
3245
+ ] }) : null
3246
+ ] })
3247
+ },
3248
+ item
3249
+ );
3250
+ };
3251
+ return /* @__PURE__ */ jsx31(
3252
+ "nav",
3253
+ {
3254
+ className: cx("sc-priority-nav", className),
3255
+ "data-measuring": measuring || void 0,
3256
+ "data-overflowing": fit.overflowing || void 0,
3257
+ style: { ...style, "--_anchor": `--sc-priority-nav${panelId.replace(/[^\w-]/g, "")}` },
3258
+ ...rest,
3259
+ children: /* @__PURE__ */ jsxs26("ul", { ref: listRef, className: "sc-priority-nav__list", children: [
3260
+ items.map(
3261
+ (item, i) => flags && !flags[i] ? null : /* @__PURE__ */ jsx31("li", { className: "sc-priority-nav__item", "data-current": item.current || void 0, children: link(item, false) }, keyOf(item))
3262
+ ),
3263
+ /* @__PURE__ */ jsxs26(
3264
+ "li",
3265
+ {
3266
+ ref: moreRef,
3267
+ className: "sc-priority-nav__more",
3268
+ hidden: measuring || overflow.length === 0,
3269
+ onBlur,
3270
+ children: [
3271
+ /* @__PURE__ */ jsxs26(
3272
+ "button",
3273
+ {
3274
+ ref: toggleRef,
3275
+ type: "button",
3276
+ className: "sc-priority-nav__toggle",
3277
+ "aria-expanded": expanded,
3278
+ "aria-controls": panelId,
3279
+ onClick: () => setOpen((o) => !o),
3280
+ children: [
3281
+ /* @__PURE__ */ jsx31(MoreHorizontalIcon, { className: "sc-priority-nav__ellipsis" }),
3282
+ /* @__PURE__ */ jsx31("span", { className: "sc-priority-nav__toggle-label", children: moreLabel }),
3283
+ /* @__PURE__ */ jsxs26("span", { className: "sc-visually-hidden", children: [
3284
+ ", ",
3285
+ moreCountLabel(overflow.length),
3286
+ moreBadgeText
3287
+ ] }),
3288
+ moreBadge !== null ? /* @__PURE__ */ jsx31(Badge2, { value: moreBadge }) : null,
3289
+ /* @__PURE__ */ jsx31(ChevronDownIcon, { size: "sm", className: "sc-priority-nav__chevron" })
3290
+ ]
3291
+ }
3292
+ ),
3293
+ /* @__PURE__ */ jsx31("ul", { ref: panelRef, id: panelId, className: "sc-priority-nav__panel", hidden: !expanded, children: overflow.map((item) => /* @__PURE__ */ jsx31("li", { children: link(item, true) }, keyOf(item))) })
3294
+ ]
3295
+ }
3296
+ )
3297
+ ] })
3298
+ }
3299
+ );
3300
+ }
3301
+
3302
+ // src/components/Popover/Popover.tsx
3303
+ import { Popover as BasePopover } from "@base-ui/react/popover";
3304
+ import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
3305
+ function Root6({ open, defaultOpen, onOpenChange, modal = false, children }) {
3306
+ return /* @__PURE__ */ jsx32(
3307
+ BasePopover.Root,
3308
+ {
3309
+ open,
3310
+ defaultOpen,
3311
+ onOpenChange: onOpenChange ? (next) => onOpenChange(next) : void 0,
3312
+ modal,
3313
+ children
3314
+ }
3315
+ );
3316
+ }
3317
+ function Trigger3({ render, className, children, ...rest }) {
3318
+ return /* @__PURE__ */ jsx32(
3319
+ BasePopover.Trigger,
3320
+ {
3321
+ render: render ?? /* @__PURE__ */ jsx32(Button, { variant: "secondary", className }),
3322
+ className: render ? className : void 0,
3323
+ ...rest,
3324
+ children
3325
+ }
3326
+ );
3327
+ }
3328
+ var COLLISION_AVOIDANCE = { fallbackAxisSide: "none" };
3329
+ function Content3({
3330
+ side = "bottom",
3331
+ align = "center",
3332
+ sideOffset,
3333
+ arrow = false,
3334
+ anchor,
3335
+ className,
3336
+ children,
3337
+ ...rest
3338
+ }) {
3339
+ return /* @__PURE__ */ jsx32(BasePopover.Portal, { children: /* @__PURE__ */ jsx32(
3340
+ BasePopover.Positioner,
3341
+ {
3342
+ side,
3343
+ align,
3344
+ sideOffset: sideOffset ?? (arrow ? 8 : 4),
3345
+ anchor,
3346
+ collisionAvoidance: COLLISION_AVOIDANCE,
3347
+ className: "sc-popover__positioner",
3348
+ children: /* @__PURE__ */ jsxs27(BasePopover.Popup, { className: cx("sc-popover", className), ...rest, children: [
3349
+ arrow ? /* @__PURE__ */ jsx32(BasePopover.Arrow, { className: "sc-popover__arrow" }) : null,
3350
+ children
3351
+ ] })
3352
+ }
3353
+ ) });
3354
+ }
3355
+ function Title2({ className, ...rest }) {
3356
+ return /* @__PURE__ */ jsx32(BasePopover.Title, { className: cx("sc-popover__title", className), ...rest });
3357
+ }
3358
+ function Description2({ className, ...rest }) {
3359
+ return /* @__PURE__ */ jsx32(BasePopover.Description, { className: cx("sc-popover__description", className), ...rest });
3360
+ }
3361
+ function Close2({ className, children, render, ...rest }) {
3362
+ if (children === void 0 && render === void 0) {
3363
+ return /* @__PURE__ */ jsx32(BasePopover.Close, { className: cx("sc-popover__close", className), "aria-label": "Close", ...rest, children: /* @__PURE__ */ jsx32(XIcon, {}) });
3364
+ }
3365
+ return /* @__PURE__ */ jsx32(BasePopover.Close, { className, render, ...rest, children });
3366
+ }
3367
+ var Popover = { Root: Root6, Trigger: Trigger3, Content: Content3, Title: Title2, Description: Description2, Close: Close2 };
3368
+
3369
+ // src/components/BroomIcon/BroomIcon.tsx
3370
+ import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
3371
+ function BroomIcon({ sweeping = false, size = "lg", title, className, ...rest }) {
3372
+ return /* @__PURE__ */ jsxs28(
3373
+ "svg",
3374
+ {
3375
+ className: cx("sc-icon", "sc-broom", className),
3376
+ "data-size": size,
3377
+ "data-sweeping": sweeping || void 0,
3378
+ viewBox: "0 0 24 24",
3379
+ fill: "none",
3380
+ focusable: "false",
3381
+ "aria-hidden": title ? void 0 : true,
3382
+ role: title ? "img" : void 0,
3383
+ "aria-label": title,
3384
+ ...rest,
3385
+ children: [
3386
+ /* @__PURE__ */ jsxs28(
3387
+ "g",
3388
+ {
3389
+ className: "sc-broom__body",
3390
+ fill: "none",
3391
+ stroke: "currentColor",
3392
+ strokeLinecap: "round",
3393
+ strokeLinejoin: "round",
3394
+ children: [
3395
+ /* @__PURE__ */ jsx33("path", { d: "M16.1 10.48 L19.36 1.86 A0.87 0.87 0 0 1 20.98 2.47 L17.84 10.78" }),
3396
+ /* @__PURE__ */ jsx33("path", { d: "M13.33 12 C14.23 10.67 16.73 9.87 18.17 11 C19.07 12.67 18.93 18.33 18.4 21.4 C16.07 22.4 11.07 22.07 6.4 18.07 C9.07 16.67 11.8 15 13.33 12 Z" }),
3397
+ /* @__PURE__ */ jsx33("path", { className: "sc-broom__band", d: "M13.47 12.13 L18.4 13.5 L18.33 14.1 L13.27 12.77 Z" }),
3398
+ /* @__PURE__ */ jsx33("path", { d: "M12.87 13.73 L14.4 14.47 M16.2 15.27 L18.47 15.87" }),
3399
+ /* @__PURE__ */ jsx33("path", { d: "M12.8 16.87 C11.86 18.24 10.75 19.38 9.57 20.23 M15.6 17.4 C15.22 18.96 14.6 20.44 13.77 21.71" })
3400
+ ]
3401
+ }
3402
+ ),
3403
+ /* @__PURE__ */ jsxs28("g", { className: "sc-broom__dust", fill: "none", stroke: "currentColor", children: [
3404
+ /* @__PURE__ */ jsx33("circle", { cx: "4.77", cy: "13.53", r: "0.5" }),
3405
+ /* @__PURE__ */ jsx33("circle", { cx: "2.7", cy: "15.53", r: "0.87" }),
3406
+ /* @__PURE__ */ jsx33("circle", { cx: "4.1", cy: "18.27", r: "0.5" })
3407
+ ] })
3408
+ ]
3409
+ }
3410
+ );
3411
+ }
3412
+
3413
+ // src/components/FullScreenFlow/FullScreenFlow.tsx
3414
+ import {
3415
+ Children as Children2,
3416
+ createContext as createContext4,
3417
+ useCallback as useCallback3,
3418
+ useContext as useContext4,
3419
+ useEffect as useEffect6,
3420
+ useId as useId6,
3421
+ useMemo as useMemo3,
3422
+ useRef as useRef6,
3423
+ useState as useState8
3424
+ } from "react";
3425
+ import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
3426
+ var FlowContext = createContext4({ announce: () => {
3427
+ } });
3428
+ var FrameContext = createContext4(void 0);
3429
+ var ENTER_IGNORES = 'textarea, button, a[href], summary, select, [contenteditable], [role="combobox"], [data-flow-keys="local"]';
3430
+ var ARROW_IGNORES = 'input, textarea, select, button, a[href], summary, [contenteditable], [role="radiogroup"], [role="slider"], [role="listbox"], [role="tablist"], [role="menu"], [role="grid"], [role="combobox"], [data-flow-keys="local"]';
3431
+ function Root7({
3432
+ open = true,
3433
+ onRequestClose,
3434
+ onNext,
3435
+ onBack,
3436
+ decoration,
3437
+ className,
3438
+ children
3439
+ }) {
3440
+ const popupRef = useRef6(null);
3441
+ const [message, setMessage] = useState8("");
3442
+ const timer = useRef6(void 0);
3443
+ const announce = useCallback3((next) => {
3444
+ setMessage("");
3445
+ clearTimeout(timer.current);
3446
+ timer.current = setTimeout(() => setMessage(next), 30);
3447
+ }, []);
3448
+ useEffect6(() => () => clearTimeout(timer.current), []);
3449
+ const context = useMemo3(() => ({ announce }), [announce]);
3450
+ const handleKeyDown = (event) => {
3451
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
3452
+ if (event.nativeEvent.isComposing) return;
3453
+ const target = event.target;
3454
+ if (!event.currentTarget.contains(target)) return;
3455
+ if (event.key === "Enter") {
3456
+ if (!onNext || target.closest(ENTER_IGNORES)) return;
3457
+ event.preventDefault();
3458
+ onNext();
3459
+ return;
3460
+ }
3461
+ if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return;
3462
+ if (target.closest(ARROW_IGNORES)) return;
3463
+ const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
3464
+ const forward = event.key === "ArrowRight" !== rtl;
3465
+ const handler = forward ? onNext : onBack;
3466
+ if (!handler) return;
3467
+ event.preventDefault();
3468
+ handler();
3469
+ };
3470
+ return /* @__PURE__ */ jsx34(
3471
+ Dialog.Root,
3472
+ {
3473
+ open,
3474
+ onRequestClose: (reason) => {
3475
+ if (reason !== "outside-press") onRequestClose(reason);
3476
+ },
3477
+ children: /* @__PURE__ */ jsx34(
3478
+ Dialog.Content,
3479
+ {
3480
+ ref: popupRef,
3481
+ variant: "fullscreen",
3482
+ className: cx("sc-flow", className),
3483
+ initialFocus: () => popupRef.current?.querySelector(".sc-flow__step-title") ?? true,
3484
+ onKeyDown: handleKeyDown,
3485
+ children: /* @__PURE__ */ jsxs29(FlowContext.Provider, { value: context, children: [
3486
+ decoration ? /* @__PURE__ */ jsx34("div", { className: "sc-flow__decoration", "aria-hidden": "true", children: decoration }) : null,
3487
+ children,
3488
+ /* @__PURE__ */ jsx34("div", { className: "sc-visually-hidden", "aria-live": "polite", "aria-atomic": "true", children: message })
3489
+ ] })
3490
+ }
3491
+ )
3492
+ }
3493
+ );
3494
+ }
3495
+ function Header2({
3496
+ title,
3497
+ logo,
3498
+ compactTitle,
3499
+ status,
3500
+ closeLabel = "Close",
3501
+ className,
3502
+ children,
3503
+ ...rest
3504
+ }) {
3505
+ return /* @__PURE__ */ jsxs29("div", { className: cx("sc-flow__header", className), ...rest, children: [
3506
+ /* @__PURE__ */ jsxs29("div", { className: "sc-flow__heading", "data-compact": compactTitle ? "" : void 0, children: [
3507
+ logo ? /* @__PURE__ */ jsx34("span", { className: "sc-flow__logo", "aria-hidden": "true", children: logo }) : null,
3508
+ /* @__PURE__ */ jsx34(Dialog.Title, { className: "sc-flow__title", children: title }),
3509
+ compactTitle ? /* @__PURE__ */ jsx34("span", { className: "sc-flow__compact-title", children: compactTitle }) : null
3510
+ ] }),
3511
+ children,
3512
+ status ? /* @__PURE__ */ jsx34("div", { className: "sc-flow__status", children: status }) : null,
3513
+ /* @__PURE__ */ jsx34(
3514
+ Dialog.Close,
3515
+ {
3516
+ className: "sc-flow__close",
3517
+ render: /* @__PURE__ */ jsx34(IconButton, { label: closeLabel, "aria-keyshortcuts": "Escape" }),
3518
+ children: /* @__PURE__ */ jsx34(XIcon, { size: "lg" })
3519
+ }
3520
+ )
3521
+ ] });
3522
+ }
3523
+ function Progress2({
3524
+ chapters,
3525
+ step,
3526
+ label,
3527
+ listLabel = "Progress",
3528
+ className,
3529
+ ...rest
3530
+ }) {
3531
+ const starts = chapters.reduce(
3532
+ (acc, chapter, i) => [...acc, i === 0 ? 0 : acc[i - 1] + chapters[i - 1].steps],
3533
+ []
3534
+ );
3535
+ return /* @__PURE__ */ jsxs29("div", { className: cx("sc-flow__progress", className), ...rest, children: [
3536
+ label ? /* @__PURE__ */ jsx34("div", { className: "sc-flow__progress-label", children: label }) : null,
3537
+ /* @__PURE__ */ jsx34("ol", { className: "sc-flow__segments", "aria-label": listLabel, children: chapters.map((chapter, i) => {
3538
+ const from = starts[i];
3539
+ const current = step > from && step <= from + chapter.steps;
3540
+ const done = Math.min(chapter.steps, Math.max(0, step - 1 - from)) + (current ? 0.5 : 0);
3541
+ const percent = chapter.steps > 0 ? Math.min(100, done / chapter.steps * 100) : 0;
3542
+ return /* @__PURE__ */ jsxs29(
3543
+ "li",
3544
+ {
3545
+ className: "sc-flow__segment",
3546
+ style: { flexGrow: chapter.steps },
3547
+ "aria-current": current ? "step" : void 0,
3548
+ children: [
3549
+ /* @__PURE__ */ jsx34("span", { className: "sc-flow__segment-fill", style: { inlineSize: `${percent}%` } }),
3550
+ /* @__PURE__ */ jsx34("span", { className: "sc-visually-hidden", children: chapter.label })
3551
+ ]
3552
+ },
3553
+ chapter.label
3554
+ );
3555
+ }) })
3556
+ ] });
3557
+ }
3558
+ function Body3({ className, ...rest }) {
3559
+ return /* @__PURE__ */ jsx34("div", { className: cx("sc-flow__body", className), ...rest });
3560
+ }
3561
+ function Rail({
3562
+ "aria-label": ariaLabel = "Steps",
3563
+ footer,
3564
+ className,
3565
+ children,
3566
+ ...rest
3567
+ }) {
3568
+ return /* @__PURE__ */ jsxs29("nav", { className: cx("sc-flow__rail", className), "aria-label": ariaLabel, ...rest, children: [
3569
+ children,
3570
+ footer ? /* @__PURE__ */ jsx34("div", { className: "sc-flow__rail-note", children: footer }) : null
3571
+ ] });
3572
+ }
3573
+ function RailGroup({ label, className, children, ...rest }) {
3574
+ const id = useId6();
3575
+ return /* @__PURE__ */ jsxs29("div", { className: cx("sc-flow__rail-group", className), ...rest, children: [
3576
+ /* @__PURE__ */ jsx34("span", { className: "sc-flow__rail-label", id, children: label }),
3577
+ /* @__PURE__ */ jsx34("ol", { className: "sc-flow__rail-list", "aria-labelledby": id, children })
3578
+ ] });
3579
+ }
3580
+ function RailItem({
3581
+ current = false,
3582
+ done = false,
3583
+ doneLabel = "done",
3584
+ adornment,
3585
+ type = "button",
3586
+ className,
3587
+ children,
3588
+ ...rest
3589
+ }) {
3590
+ return /* @__PURE__ */ jsx34("li", { className: "sc-flow__rail-entry", children: /* @__PURE__ */ jsxs29(
3591
+ "button",
3592
+ {
3593
+ type,
3594
+ className: cx("sc-flow__rail-item", className),
3595
+ "aria-current": current ? "step" : void 0,
3596
+ "data-done": done || void 0,
3597
+ ...rest,
3598
+ children: [
3599
+ /* @__PURE__ */ jsx34("span", { className: "sc-flow__rail-mark", "aria-hidden": "true", children: done ? /* @__PURE__ */ jsx34(CheckIcon, { size: "sm" }) : null }),
3600
+ /* @__PURE__ */ jsxs29("span", { className: "sc-flow__rail-text", children: [
3601
+ children,
3602
+ done ? /* @__PURE__ */ jsxs29("span", { className: "sc-visually-hidden", children: [
3603
+ ", ",
3604
+ doneLabel
3605
+ ] }) : null
3606
+ ] }),
3607
+ adornment ? /* @__PURE__ */ jsx34("span", { className: "sc-flow__rail-adornment", children: adornment }) : null
3608
+ ]
3609
+ }
3610
+ ) });
3611
+ }
3612
+ function Viewport2({ direction = "forward", className, children, ...rest }) {
3613
+ const element = Children2.only(children);
3614
+ const key = String(element.key ?? "");
3615
+ const [state, setState] = useState8({
3616
+ current: { key, element, direction },
3617
+ leaving: null,
3618
+ moved: false
3619
+ });
3620
+ if (state.current.key !== key) {
3621
+ setState({
3622
+ current: { key, element, direction },
3623
+ leaving: { ...state.current, direction },
3624
+ moved: true
3625
+ });
3626
+ } else if (state.current.element !== element) {
3627
+ setState((s) => ({ ...s, current: { ...s.current, element } }));
3628
+ }
3629
+ const finishExit = useCallback3((leftKey) => {
3630
+ setState((s) => s.leaving?.key === leftKey ? { ...s, leaving: null } : s);
3631
+ }, []);
3632
+ const ref = useRef6(null);
3633
+ useEffect6(() => {
3634
+ if (state.moved && ref.current) ref.current.scrollTop = 0;
3635
+ }, [key, state.moved]);
3636
+ const frames = [];
3637
+ if (state.leaving && state.leaving.key !== key) frames.push({ frame: state.leaving, phase: "exit" });
3638
+ frames.push({
3639
+ frame: { key, element, direction: state.current.direction },
3640
+ phase: state.moved ? "enter" : void 0
3641
+ });
3642
+ return /* @__PURE__ */ jsx34("div", { ref, className: cx("sc-flow__viewport", className), ...rest, children: frames.map(({ frame, phase }) => /* @__PURE__ */ jsx34(
3643
+ FrameView,
3644
+ {
3645
+ frameKey: frame.key,
3646
+ phase,
3647
+ direction: frame.direction,
3648
+ onExited: finishExit,
3649
+ children: frame.element
3650
+ },
3651
+ frame.key
3652
+ )) });
3653
+ }
3654
+ function FrameView({
3655
+ frameKey,
3656
+ phase,
3657
+ direction,
3658
+ onExited,
3659
+ children
3660
+ }) {
3661
+ const ref = useRef6(null);
3662
+ useEffect6(() => {
3663
+ if (phase !== "exit") return;
3664
+ let cancelled = false;
3665
+ const el = ref.current;
3666
+ const animations = el && typeof el.getAnimations === "function" ? el.getAnimations() : [];
3667
+ const done = () => {
3668
+ if (!cancelled) onExited(frameKey);
3669
+ };
3670
+ Promise.all(animations.map((a) => a.finished)).then(done, done);
3671
+ return () => {
3672
+ cancelled = true;
3673
+ };
3674
+ }, [phase, frameKey, onExited]);
3675
+ return /* @__PURE__ */ jsx34(
3676
+ "div",
3677
+ {
3678
+ ref,
3679
+ className: "sc-flow__frame",
3680
+ "data-phase": phase,
3681
+ "data-direction": direction,
3682
+ inert: phase === "exit",
3683
+ children: /* @__PURE__ */ jsx34(FrameContext.Provider, { value: phase, children })
3684
+ }
3685
+ );
3686
+ }
3687
+ function Step({
3688
+ title,
3689
+ eyebrow,
3690
+ description,
3691
+ announcement,
3692
+ className,
3693
+ children,
3694
+ ...rest
3695
+ }) {
3696
+ const id = useId6();
3697
+ const titleRef = useRef6(null);
3698
+ const phase = useContext4(FrameContext);
3699
+ const { announce } = useContext4(FlowContext);
3700
+ const message = announcement ?? (typeof title === "string" ? title : void 0);
3701
+ const latest = useRef6({ message, announce });
3702
+ useEffect6(() => {
3703
+ latest.current = { message, announce };
3704
+ });
3705
+ useEffect6(() => {
3706
+ if (phase !== "enter") return;
3707
+ titleRef.current?.focus({ preventScroll: true });
3708
+ if (latest.current.message) latest.current.announce(latest.current.message);
3709
+ }, [phase]);
3710
+ return /* @__PURE__ */ jsxs29("section", { className: cx("sc-flow__step", className), "aria-labelledby": id, ...rest, children: [
3711
+ /* @__PURE__ */ jsxs29("div", { className: "sc-flow__step-head", children: [
3712
+ eyebrow ? /* @__PURE__ */ jsx34("p", { className: "sc-flow__eyebrow", children: eyebrow }) : null,
3713
+ /* @__PURE__ */ jsx34("h2", { className: "sc-flow__step-title", id, ref: titleRef, tabIndex: -1, children: title }),
3714
+ description ? /* @__PURE__ */ jsx34("div", { className: "sc-flow__step-description", children: description }) : null
3715
+ ] }),
3716
+ children
3717
+ ] });
3718
+ }
3719
+ function Footer({ back, hint, className, children, ...rest }) {
3720
+ return /* @__PURE__ */ jsxs29("div", { className: cx("sc-flow__footer", className), ...rest, children: [
3721
+ back ? /* @__PURE__ */ jsx34("div", { className: "sc-flow__footer-back", children: back }) : null,
3722
+ hint ? /* @__PURE__ */ jsx34("div", { className: "sc-flow__footer-hint", children: hint }) : null,
3723
+ /* @__PURE__ */ jsx34("div", { className: "sc-flow__footer-actions", children })
3724
+ ] });
3725
+ }
3726
+ var FullScreenFlow = {
3727
+ Root: Root7,
3728
+ Header: Header2,
3729
+ Progress: Progress2,
3730
+ Body: Body3,
3731
+ Rail,
3732
+ RailGroup,
3733
+ RailItem,
3734
+ Viewport: Viewport2,
3735
+ Step,
3736
+ Footer
3737
+ };
3738
+
3739
+ // src/components/Score/Score.tsx
3740
+ import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
3741
+ function Score({
3742
+ value,
3743
+ max = 100,
3744
+ tone = "accent",
3745
+ variant = "tile",
3746
+ size = "sm",
3747
+ caption,
3748
+ label = "Score",
3749
+ valueText,
3750
+ selected = false,
3751
+ className,
3752
+ style,
3753
+ ...rest
3754
+ }) {
3755
+ const scored = value !== null;
3756
+ const fraction = scored && max > 0 ? Math.min(1, Math.max(0, value / max)) : 0;
3757
+ return /* @__PURE__ */ jsxs30(
3758
+ "span",
3759
+ {
3760
+ className: cx("sc-score", className),
3761
+ "data-variant": variant,
3762
+ "data-size": variant === "tile" ? size : void 0,
3763
+ "data-tone": scored ? tone : void 0,
3764
+ "data-unscored": scored ? void 0 : true,
3765
+ "data-selected": selected || void 0,
3766
+ style: variant === "ring" ? { ...style, "--sc-score-fill": (fraction * 100).toFixed(2) } : style,
3767
+ ...rest,
3768
+ children: [
3769
+ /* @__PURE__ */ jsx35("span", { className: "sc-visually-hidden", children: valueText ?? (scored ? `${label} ${value} out of ${max}` : `${label}: not scored`) }),
3770
+ variant === "ring" ? /* @__PURE__ */ jsxs30("svg", { className: "sc-score__ring", viewBox: "0 0 64 64", "aria-hidden": "true", focusable: "false", children: [
3771
+ /* @__PURE__ */ jsx35("circle", { className: "sc-score__track", cx: "32", cy: "32", r: "29", pathLength: 100 }),
3772
+ fraction > 0 ? /* @__PURE__ */ jsx35("circle", { className: "sc-score__arc", cx: "32", cy: "32", r: "29", pathLength: 100 }) : null
3773
+ ] }) : null,
3774
+ /* @__PURE__ */ jsxs30("span", { className: "sc-score__body", "aria-hidden": "true", children: [
3775
+ /* @__PURE__ */ jsx35("span", { className: "sc-score__value", children: scored ? value : "\u2013" }),
3776
+ caption != null ? /* @__PURE__ */ jsx35("span", { className: "sc-score__caption", children: caption }) : null
3777
+ ] })
3778
+ ]
3779
+ }
3780
+ );
3781
+ }
3782
+
3783
+ // src/components/ReasonList/ReasonList.tsx
3784
+ import { jsx as jsx36 } from "react/jsx-runtime";
3785
+ function ReasonListRoot({ tone, size = "md", divider = false, className, ...rest }) {
3786
+ return /* @__PURE__ */ jsx36(
3787
+ "ul",
3788
+ {
3789
+ className: cx("sc-reason-list", className),
3790
+ "data-tone": tone,
3791
+ "data-size": size,
3792
+ "data-divider": divider || void 0,
3793
+ ...rest
3794
+ }
3795
+ );
3796
+ }
3797
+ function ReasonListItem({ className, ...rest }) {
3798
+ return /* @__PURE__ */ jsx36("li", { className: cx("sc-reason-list__item", className), ...rest });
3799
+ }
3800
+ var ReasonList = Object.assign(ReasonListRoot, { Item: ReasonListItem });
3801
+
3802
+ // src/components/WordDiff/WordDiff.tsx
3803
+ import { createElement as createElement7, Fragment as Fragment10 } from "react";
3804
+ import { Fragment as Fragment11, jsx as jsx37, jsxs as jsxs31 } from "react/jsx-runtime";
3805
+ var edges = /^(\s*)([\s\S]*?)(\s*)$/;
3806
+ function Change({ kind, text, label }) {
3807
+ const [, lead = "", core = "", trail = ""] = edges.exec(text) ?? [];
3808
+ if (!core) return /* @__PURE__ */ jsx37(Fragment11, { children: text });
3809
+ const Tag = kind === "added" ? "ins" : "del";
3810
+ return /* @__PURE__ */ jsxs31(Fragment11, { children: [
3811
+ lead,
3812
+ /* @__PURE__ */ jsxs31(Tag, { className: `sc-word-diff__${kind}`, children: [
3813
+ /* @__PURE__ */ jsxs31("span", { className: "sc-visually-hidden", children: [
3814
+ "[",
3815
+ label,
3816
+ ": "
3817
+ ] }),
3818
+ core,
3819
+ /* @__PURE__ */ jsx37("span", { className: "sc-visually-hidden", children: "]" })
3820
+ ] }),
3821
+ trail
3822
+ ] });
3823
+ }
3824
+ function WordDiff({
3825
+ parts,
3826
+ as = "p",
3827
+ size = "sm",
3828
+ family = "sans",
3829
+ addedLabel = "added",
3830
+ removedLabel = "removed",
3831
+ className,
3832
+ ...rest
3833
+ }) {
3834
+ const children = parts.map(
3835
+ (part, i) => part.kind === "same" ? /* @__PURE__ */ jsx37(Fragment10, { children: part.text }, i) : /* @__PURE__ */ jsx37(
3836
+ Change,
3837
+ {
3838
+ kind: part.kind,
3839
+ text: part.text,
3840
+ label: part.kind === "added" ? addedLabel : removedLabel
3841
+ },
3842
+ i
3843
+ )
3844
+ );
3845
+ return createElement7(
3846
+ as,
3847
+ {
3848
+ className: cx("sc-word-diff", className),
3849
+ "data-size": size,
3850
+ ...family === "mono" ? { "data-family": "mono" } : {},
3851
+ ...rest
3852
+ },
3853
+ children
3854
+ );
3855
+ }
3856
+
3857
+ // src/components/Quote/Quote.tsx
3858
+ import { jsx as jsx38, jsxs as jsxs32 } from "react/jsx-runtime";
3859
+ function Quote({ label, className, children, ...rest }) {
3860
+ return /* @__PURE__ */ jsxs32("figure", { className: cx("sc-quote", className), ...rest, children: [
3861
+ /* @__PURE__ */ jsx38("figcaption", { className: "sc-quote__label", children: label }),
3862
+ /* @__PURE__ */ jsx38("blockquote", { className: "sc-quote__text", children })
3863
+ ] });
3864
+ }
3865
+
3866
+ // src/components/Avatar/Avatar.tsx
3867
+ import { Avatar as BaseAvatar } from "@base-ui/react/avatar";
3868
+ import { useRender as useRender3 } from "@base-ui/react/use-render";
3869
+ import { jsx as jsx39, jsxs as jsxs33 } from "react/jsx-runtime";
3870
+ function avatarInitials(name) {
3871
+ const value = name?.trim() ?? "";
3872
+ const words = (value.includes("@") ? (value.split("@")[0] ?? "").split(/[._+\-\d]+/) : value.split(/\s+/)).filter(Boolean);
3873
+ const first = (w) => w ? Array.from(w)[0] ?? "" : "";
3874
+ const letters = words.length > 1 ? first(words[0]) + first(words[words.length - 1]) : first(words[0]);
3875
+ return letters.toUpperCase();
3876
+ }
3877
+ function Letters({ name, initials }) {
3878
+ const letters = initials?.trim() ? Array.from(initials.trim()).slice(0, 2).join("") : avatarInitials(name);
3879
+ return letters ? /* @__PURE__ */ jsx39("span", { className: "sc-avatar__initials", children: letters }) : /* @__PURE__ */ jsx39(UserIcon, { className: "sc-avatar__icon", size: "em" });
3880
+ }
3881
+ function Avatar({ name, initials, src, size = "md", label, className, ...rest }) {
3882
+ return /* @__PURE__ */ jsxs33(
3883
+ BaseAvatar.Root,
3884
+ {
3885
+ className: cx("sc-avatar", className),
3886
+ "data-size": size,
3887
+ role: label ? "img" : void 0,
3888
+ "aria-label": label,
3889
+ "aria-hidden": label ? void 0 : true,
3890
+ ...rest,
3891
+ children: [
3892
+ src ? /* @__PURE__ */ jsx39(BaseAvatar.Image, { className: "sc-avatar__image", src, alt: "" }) : null,
3893
+ /* @__PURE__ */ jsx39(BaseAvatar.Fallback, { className: "sc-avatar__fallback", children: /* @__PURE__ */ jsx39(Letters, { name, initials }) })
3894
+ ]
3895
+ }
3896
+ );
3897
+ }
3898
+ function AvatarButton({
3899
+ label,
3900
+ name,
3901
+ initials,
3902
+ src,
3903
+ size = "md",
3904
+ type,
3905
+ className,
3906
+ render,
3907
+ ref,
3908
+ ...rest
3909
+ }) {
3910
+ return useRender3({
3911
+ render,
3912
+ ref,
3913
+ defaultTagName: "button",
3914
+ props: {
3915
+ // `type` only means something on a real button; a rendered link keeps its own attributes.
3916
+ type: render ? type : type ?? "button",
3917
+ className: cx("sc-avatar-button", className),
3918
+ "data-size": size,
3919
+ "aria-label": label,
3920
+ ...rest,
3921
+ children: /* @__PURE__ */ jsx39(Avatar, { name, initials, src, size })
3922
+ }
3923
+ });
3924
+ }
3925
+
1482
3926
  // src/index.ts
1483
3927
  var version = "0.1.1";
1484
3928
  export {
@@ -1487,10 +3931,13 @@ export {
1487
3931
  ArrowLeftIcon,
1488
3932
  ArrowRightIcon,
1489
3933
  ArrowUpIcon,
3934
+ Avatar,
3935
+ AvatarButton,
1490
3936
  Badge,
1491
3937
  Banner,
1492
3938
  BellIcon,
1493
3939
  BriefcaseIcon,
3940
+ BroomIcon,
1494
3941
  Button,
1495
3942
  CalendarIcon,
1496
3943
  Card,
@@ -1500,18 +3947,25 @@ export {
1500
3947
  ChevronLeftIcon,
1501
3948
  ChevronRightIcon,
1502
3949
  ChevronUpIcon,
3950
+ Chip,
1503
3951
  CircleCheckIcon,
1504
3952
  CircleXIcon,
1505
3953
  ClockIcon,
1506
3954
  CopyIcon,
1507
3955
  Dialog,
3956
+ Disclosure,
1508
3957
  DocumentIcon,
1509
3958
  DownloadIcon,
1510
3959
  EditIcon,
3960
+ EmptyState,
1511
3961
  ExternalLinkIcon,
1512
3962
  EyeIcon,
3963
+ EyeOffIcon,
3964
+ FileInput,
3965
+ FilePreviewDialog,
1513
3966
  FilterIcon,
1514
3967
  FlagIcon,
3968
+ FullScreenFlow,
1515
3969
  Heading,
1516
3970
  Icon,
1517
3971
  IconButton,
@@ -1532,11 +3986,18 @@ export {
1532
3986
  PauseIcon,
1533
3987
  PlayIcon,
1534
3988
  PlusIcon,
3989
+ Popover,
3990
+ PriorityNav,
1535
3991
  Progress,
3992
+ Quote,
3993
+ ReasonList,
1536
3994
  RefreshIcon,
3995
+ Score,
1537
3996
  SearchIcon,
1538
3997
  Select,
3998
+ SelectionBar,
1539
3999
  SettingsIcon,
4000
+ Skeleton,
1540
4001
  Spinner,
1541
4002
  Stack,
1542
4003
  StarIcon,
@@ -1549,14 +4010,19 @@ export {
1549
4010
  Textarea,
1550
4011
  ThumbsDownIcon,
1551
4012
  ThumbsUpIcon,
4013
+ Toast,
1552
4014
  Toolbar,
1553
4015
  Tooltip,
1554
4016
  TrashIcon,
1555
4017
  UndoIcon,
1556
4018
  UserIcon,
1557
4019
  WarningIcon,
4020
+ WordDiff,
1558
4021
  XIcon,
4022
+ avatarInitials,
1559
4023
  iconNames,
4024
+ useRowSelection,
4025
+ useToast,
1560
4026
  version
1561
4027
  };
1562
4028
  //# sourceMappingURL=index.js.map