@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/README.md +97 -19
- package/dist/codemirror-6REE3TUN.js +383 -0
- package/dist/codemirror-6REE3TUN.js.map +1 -0
- package/dist/editor.d.ts +55 -0
- package/dist/editor.js +174 -0
- package/dist/editor.js.map +1 -0
- package/dist/index.d.ts +1436 -62
- package/dist/index.js +2554 -88
- package/dist/index.js.map +1 -1
- package/dist/markdown-5O5GUVBJ.js +10 -0
- package/dist/markdown-5O5GUVBJ.js.map +1 -0
- package/dist/styles.css +3803 -41
- package/package.json +59 -6
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
|
-
|
|
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({
|
|
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__ */
|
|
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
|
-
|
|
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 =
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
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 {
|
|
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
|
|
867
|
+
function useItemBody({
|
|
668
868
|
icon,
|
|
869
|
+
description,
|
|
669
870
|
trailing,
|
|
670
871
|
children
|
|
671
|
-
}) {
|
|
672
|
-
|
|
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
|
-
|
|
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({
|
|
679
|
-
|
|
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({
|
|
682
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
1070
|
-
|
|
1071
|
-
|
|
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(
|
|
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] =
|
|
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
|
|
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 ? "" :
|
|
1598
|
+
"data-truncate": truncate === true ? "" : clamp2 !== void 0 ? "clamp" : void 0,
|
|
1298
1599
|
"data-numeric": numeric || void 0,
|
|
1299
|
-
|
|
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({
|
|
1354
|
-
|
|
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":
|
|
1712
|
+
"data-selected": isSelected || void 0,
|
|
1359
1713
|
"data-interactive": interactive || void 0,
|
|
1360
|
-
"aria-selected":
|
|
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
|
|
2128
|
+
import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1438
2129
|
var trendIcons = {
|
|
1439
|
-
up: /* @__PURE__ */
|
|
1440
|
-
down: /* @__PURE__ */
|
|
1441
|
-
flat: /* @__PURE__ */
|
|
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__ */
|
|
1466
|
-
/* @__PURE__ */
|
|
1467
|
-
icon ? /* @__PURE__ */
|
|
1468
|
-
/* @__PURE__ */
|
|
1469
|
-
delta != null ? /* @__PURE__ */
|
|
1470
|
-
trend ? /* @__PURE__ */
|
|
1471
|
-
trend ? /* @__PURE__ */
|
|
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__ */
|
|
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) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[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
|