@proteos/ui 0.45.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/index.cjs +720 -70
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +191 -8
  4. package/dist/index.d.ts +191 -8
  5. package/dist/index.js +715 -74
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -2
  8. package/src/components/attribute-type-icon/attribute-type-icon.tsx +5 -0
  9. package/src/components/avatar/avatar.stories.tsx +37 -1
  10. package/src/components/avatar/avatar.test.tsx +42 -0
  11. package/src/components/avatar/avatar.tsx +39 -4
  12. package/src/components/avatar-group/avatar-group.test.tsx +19 -0
  13. package/src/components/avatar-group/avatar-group.tsx +18 -3
  14. package/src/components/badge/badge.stories.tsx +27 -1
  15. package/src/components/badge/badge.test.tsx +26 -0
  16. package/src/components/badge/badge.tsx +48 -4
  17. package/src/components/badge-multi-select/badge-multi-select.stories.tsx +24 -0
  18. package/src/components/badge-multi-select/badge-multi-select.tsx +25 -4
  19. package/src/components/checkbox/checkbox.tsx +8 -1
  20. package/src/components/color-input/color-input.stories.tsx +72 -0
  21. package/src/components/color-input/color-input.test.tsx +95 -0
  22. package/src/components/color-input/color-input.tsx +208 -0
  23. package/src/components/color-input/index.ts +2 -0
  24. package/src/components/data-table/data-table.test.tsx +112 -0
  25. package/src/components/data-table/data-table.tsx +135 -11
  26. package/src/components/data-table/index.ts +1 -1
  27. package/src/components/draft-message-composer/draft-message-composer.stories.tsx +1 -1
  28. package/src/components/icon-picker/icon-picker.stories.tsx +77 -0
  29. package/src/components/icon-picker/icon-picker.test.tsx +88 -0
  30. package/src/components/icon-picker/icon-picker.tsx +336 -0
  31. package/src/components/icon-picker/index.ts +2 -0
  32. package/src/components/record-filter/chip-value.tsx +10 -3
  33. package/src/components/record-filter/filter-value-input.tsx +12 -2
  34. package/src/components/record-filter/operators.ts +6 -6
  35. package/src/components/record-filter/record-filter.stories.tsx +1 -1
  36. package/src/components/record-filter/types.ts +4 -0
  37. package/src/components/table/table.stories.tsx +12 -13
  38. package/src/components/toast/index.ts +9 -0
  39. package/src/components/toast/toast.stories.tsx +58 -0
  40. package/src/components/toast/toast.test.tsx +34 -0
  41. package/src/components/toast/toast.tsx +121 -0
  42. package/src/index.ts +3 -0
  43. package/src/test/setup.ts +30 -0
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { cn, Button, Skeleton } from './chunk-IKAX76MB.js';
2
2
  export { Button, Skeleton, buttonStyles, cn, skeletonStyles } from './chunk-IKAX76MB.js';
3
- import { Paperclip, BookText, Banknote, User, Link2, Braces, Brackets, ToggleLeft, CircleDot, Calendar, Hash, Type, File, FileImage, FileText, FileSpreadsheet, FileArchive, Loader2, X, AudioLines, Video, Mic, AtSign, MessagesSquare, Camera, Send, Linkedin, MessageCircle, Mail, ChevronDown, GripVertical, SendHorizontal, CircleX, Download, ChevronRight as ChevronRight$1, CircleCheck } from 'lucide-react';
3
+ import { icons, Paperclip, BookText, Banknote, UsersRound, User, Link2, Braces, Brackets, ToggleLeft, CircleDot, Calendar, Hash, Type, File, FileImage, FileText, FileSpreadsheet, FileArchive, Loader2, X, Shapes, Search, AudioLines, Video, Mic, AtSign, MessagesSquare, Camera, Send, Linkedin, MessageCircle, Mail, Pipette, ChevronDown, GripVertical, SendHorizontal, CircleX, Download, ChevronRight as ChevronRight$1, CircleCheck, CircleAlert, Info } from 'lucide-react';
4
4
  import { tv } from 'tailwind-variants';
5
- import { Tabs as Tabs$1, TabPanel, Tab, TabList, MenuTrigger as MenuTrigger$1, SubmenuTrigger as SubmenuTrigger$1, DialogTrigger, Popover as Popover$1, Dialog, DropZone, Text, FileTrigger, Heading, Label, ListBox, ListBoxItem, Breadcrumbs as Breadcrumbs$1, Breadcrumb, Link, Button as Button$1, Checkbox as Checkbox$1, CheckboxGroup as CheckboxGroup$1, FieldError, ComboBox, Input as Input$1, ComboBoxStateContext, Select as Select$1, DatePicker as DatePicker$1, Group, DateInput, DateSegment, SelectValue, ModalOverlay, Modal as Modal$1, TextField, Menu as Menu$1, MenuItem as MenuItem$1, Disclosure, DisclosurePanel, ProgressBar as ProgressBar$1, RadioGroup as RadioGroup$1, Radio as Radio$1, Separator as Separator$1, Switch as Switch$1, TextArea, TimeField as TimeField$1, TooltipTrigger as TooltipTrigger$1, Tooltip as Tooltip$1, Calendar as Calendar$1, CalendarGrid, CalendarGridHeader, CalendarHeaderCell, CalendarGridBody, CalendarCell, DatePickerStateContext } from 'react-aria-components';
5
+ import { Tabs as Tabs$1, TabPanel, Tab, TabList, MenuTrigger as MenuTrigger$1, SubmenuTrigger as SubmenuTrigger$1, DialogTrigger, Popover as Popover$1, Dialog, DropZone, Text, FileTrigger, Heading, Label, Input as Input$1, ListBox, ListBoxItem, Breadcrumbs as Breadcrumbs$1, Breadcrumb, Link, Button as Button$1, Checkbox as Checkbox$1, CheckboxGroup as CheckboxGroup$1, FieldError, TextField, ComboBox, ComboBoxStateContext, Select as Select$1, DatePicker as DatePicker$1, Group, DateInput, DateSegment, SelectValue, ModalOverlay, Modal as Modal$1, Menu as Menu$1, MenuItem as MenuItem$1, Disclosure, DisclosurePanel, ProgressBar as ProgressBar$1, RadioGroup as RadioGroup$1, Radio as Radio$1, Separator as Separator$1, Switch as Switch$1, TextArea, TimeField as TimeField$1, UNSTABLE_ToastQueue, UNSTABLE_ToastRegion, UNSTABLE_Toast, UNSTABLE_ToastContent, TooltipTrigger as TooltipTrigger$1, Tooltip as Tooltip$1, Calendar as Calendar$1, CalendarGrid, CalendarGridHeader, CalendarHeaderCell, CalendarGridBody, CalendarCell, DatePickerStateContext } from 'react-aria-components';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
- import { lazy, createContext, useState, useRef, useEffect, useContext, useSyncExternalStore, useMemo, useLayoutEffect, useCallback, Suspense, useId, Fragment as Fragment$1 } from 'react';
7
+ import { lazy, createContext, useState, useRef, useMemo, useEffect, useContext, useSyncExternalStore, useLayoutEffect, useCallback, Suspense, useId, Fragment as Fragment$1 } from 'react';
8
8
  import { formatAmount, parseAmount } from '@proteos/sdk';
9
9
  import { draggable, dropTargetForElements, monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
10
10
  import { extractClosestEdge, attachClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
@@ -355,6 +355,7 @@ var AttributeType = {
355
355
  Object: "object",
356
356
  Relation: "relation",
357
357
  User: "user",
358
+ Principal: "principal",
358
359
  Currency: "currency",
359
360
  KnowledgeText: "knowledge-text",
360
361
  File: "file"
@@ -371,6 +372,7 @@ var ATTRIBUTE_TYPE_ICONS = {
371
372
  [AttributeType.Object]: Braces,
372
373
  [AttributeType.Relation]: Link2,
373
374
  [AttributeType.User]: User,
375
+ [AttributeType.Principal]: UsersRound,
374
376
  [AttributeType.Currency]: Banknote,
375
377
  [AttributeType.KnowledgeText]: BookText,
376
378
  [AttributeType.File]: Paperclip
@@ -397,7 +399,7 @@ function AttributeTypeIcon({
397
399
  }
398
400
  var avatarStyles = tv({
399
401
  base: [
400
- "inline-grid place-items-center shrink-0 select-none",
402
+ "inline-grid place-items-center shrink-0 select-none overflow-hidden",
401
403
  "rounded-full border",
402
404
  "font-mono font-medium tracking-[-0.005em]"
403
405
  ],
@@ -405,7 +407,8 @@ var avatarStyles = tv({
405
407
  size: {
406
408
  sm: "size-[22px] text-[10px]",
407
409
  md: "size-7 text-[12px]",
408
- lg: "size-9 text-[13px]"
410
+ lg: "size-9 text-[13px]",
411
+ xl: "size-[120px] text-[34px]"
409
412
  },
410
413
  tone: {
411
414
  paper: "bg-surface-3 text-ink border-rule",
@@ -417,8 +420,27 @@ var avatarStyles = tv({
417
420
  },
418
421
  defaultVariants: { size: "md", tone: "paper" }
419
422
  });
420
- function Avatar({ size, tone, initials, className, children, ...props }) {
421
- return /* @__PURE__ */ jsx("span", { ...props, className: cn(avatarStyles({ size, tone }), className), children: children ?? initials });
423
+ function Avatar({
424
+ size,
425
+ tone,
426
+ initials,
427
+ src,
428
+ alt = "",
429
+ className,
430
+ children,
431
+ ...props
432
+ }) {
433
+ const [failedSrc, setFailedSrc] = useState(null);
434
+ const showsImage = Boolean(src) && failedSrc !== src;
435
+ return /* @__PURE__ */ jsx("span", { ...props, className: cn(avatarStyles({ size, tone }), className), children: showsImage ? /* @__PURE__ */ jsx(
436
+ "img",
437
+ {
438
+ src: src ?? void 0,
439
+ alt,
440
+ className: "size-full object-cover",
441
+ onError: () => setFailedSrc(src ?? null)
442
+ }
443
+ ) : children ?? initials });
422
444
  }
423
445
  var avatarGroupStyles = tv({
424
446
  base: "flex items-center",
@@ -431,53 +453,29 @@ var avatarGroupStyles = tv({
431
453
  },
432
454
  defaultVariants: { size: "md" }
433
455
  });
434
- function AvatarGroup({ avatars, max = 3, size = "md", className, ...props }) {
456
+ function AvatarGroup({
457
+ avatars,
458
+ max = 3,
459
+ size = "md",
460
+ className,
461
+ ...props
462
+ }) {
435
463
  const shown = avatars.slice(0, max);
436
464
  const overflow = avatars.length - shown.length;
437
465
  return /* @__PURE__ */ jsxs("span", { ...props, className: cn(avatarGroupStyles({ size }), className), children: [
438
- shown.map((avatar, index) => /* @__PURE__ */ jsx("span", { className: "rounded-full ring-[1.5px] ring-surface", children: /* @__PURE__ */ jsx(Avatar, { size, tone: avatar.tone ?? "paper", initials: avatar.initials }) }, avatar.key ?? index)),
466
+ shown.map((avatar, index) => /* @__PURE__ */ jsx("span", { className: "rounded-full ring-[1.5px] ring-surface", children: /* @__PURE__ */ jsx(
467
+ Avatar,
468
+ {
469
+ size,
470
+ tone: avatar.tone ?? "paper",
471
+ initials: avatar.initials,
472
+ src: avatar.src ?? null,
473
+ alt: avatar.alt ?? ""
474
+ }
475
+ ) }, avatar.key ?? index)),
439
476
  overflow > 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full ring-[1.5px] ring-surface", children: /* @__PURE__ */ jsx(Avatar, { size, tone: "paper", initials: `+${overflow}` }) })
440
477
  ] });
441
478
  }
442
- var badgeStyles = tv({
443
- base: [
444
- "inline-flex items-center gap-1.5",
445
- "font-mono text-[11.5px] tracking-[-0.005em]",
446
- "px-2.5 py-0.5 rounded-pill border",
447
- // Leading dot
448
- 'before:content-[""] before:block before:size-1.5 before:rounded-full'
449
- ],
450
- variants: {
451
- variant: {
452
- default: "border-rule bg-surface text-ink-3 before:bg-ink-4",
453
- neutral: "border-rule bg-surface-3 text-ink-2 before:bg-ink-3",
454
- active: [
455
- "text-accent-ink before:bg-accent",
456
- "border-[color-mix(in_oklab,var(--color-accent)_35%,var(--color-rule))]",
457
- "bg-[color-mix(in_oklab,var(--color-accent)_14%,var(--color-surface))]"
458
- ],
459
- success: [
460
- "text-success before:bg-success",
461
- "border-[color-mix(in_oklab,var(--color-success)_35%,var(--color-rule))]",
462
- "bg-[color-mix(in_oklab,var(--color-success)_12%,var(--color-surface))]"
463
- ],
464
- warning: [
465
- "text-warning before:bg-warning",
466
- "border-[color-mix(in_oklab,var(--color-warning)_35%,var(--color-rule))]",
467
- "bg-[color-mix(in_oklab,var(--color-warning)_14%,var(--color-surface))]"
468
- ],
469
- danger: [
470
- "text-danger before:bg-danger",
471
- "border-[color-mix(in_oklab,var(--color-danger)_40%,var(--color-rule))]",
472
- "bg-[color-mix(in_oklab,var(--color-danger)_12%,var(--color-surface))]"
473
- ]
474
- }
475
- },
476
- defaultVariants: { variant: "default" }
477
- });
478
- function Badge({ variant, className, ...props }) {
479
- return /* @__PURE__ */ jsx("span", { ...props, className: cn(badgeStyles({ variant }), className) });
480
- }
481
479
  var fieldControlStyles = tv({
482
480
  slots: {
483
481
  box: [
@@ -570,6 +568,318 @@ function FieldCaret() {
570
568
  }
571
569
  ) });
572
570
  }
571
+ var ICON_NAMES = Object.keys(icons);
572
+ var BY_NORMALIZED = new Map(
573
+ ICON_NAMES.map((name) => [normalize(name), name])
574
+ );
575
+ function normalize(name) {
576
+ return name.toLowerCase().replace(/[^a-z0-9]/g, "");
577
+ }
578
+ function resolveLucideIcon(name) {
579
+ const canonical = resolveLucideIconName(name);
580
+ return canonical ? icons[canonical] : null;
581
+ }
582
+ function resolveLucideIconName(name) {
583
+ if (!name) return null;
584
+ return BY_NORMALIZED.get(normalize(name)) ?? null;
585
+ }
586
+ var GRID_PAGE = 120;
587
+ var COMPACT_SIZE = {
588
+ sm: "size-8",
589
+ md: "size-[42px]"
590
+ };
591
+ function IconPicker({
592
+ label,
593
+ description,
594
+ errorMessage,
595
+ placeholder = "Choose an icon\u2026",
596
+ value,
597
+ onChange,
598
+ isDisabled = false,
599
+ isInvalid = false,
600
+ allowClear = true,
601
+ size = "md",
602
+ appearance,
603
+ compact = false,
604
+ "aria-label": ariaLabel,
605
+ className
606
+ }) {
607
+ const [isOpen, setIsOpen] = useState(false);
608
+ const [query, setQuery] = useState("");
609
+ const [limit, setLimit] = useState(GRID_PAGE);
610
+ const triggerRef = useRef(null);
611
+ const styles3 = fieldControlStyles({ size, invalid: isInvalid, appearance });
612
+ const selectedName = resolveLucideIconName(value);
613
+ const SelectedIcon = selectedName ? icons[selectedName] : null;
614
+ const matches2 = useMemo(() => {
615
+ const q = normalize(query);
616
+ return q ? ICON_NAMES.filter((name) => normalize(name).includes(q)) : ICON_NAMES;
617
+ }, [query]);
618
+ const visible = matches2.slice(0, limit);
619
+ const hiddenCount = matches2.length - visible.length;
620
+ const open = (next) => {
621
+ setIsOpen(next);
622
+ if (!next) {
623
+ setQuery("");
624
+ setLimit(GRID_PAGE);
625
+ }
626
+ };
627
+ const search = (next) => {
628
+ setQuery(next);
629
+ setLimit(GRID_PAGE);
630
+ };
631
+ const onGridScroll = (element) => {
632
+ if (hiddenCount === 0) return;
633
+ const remaining = element.scrollHeight - element.scrollTop - element.clientHeight;
634
+ if (remaining < 120) setLimit((current) => current + GRID_PAGE);
635
+ };
636
+ const pick = (name) => {
637
+ onChange(name);
638
+ open(false);
639
+ triggerRef.current?.focus();
640
+ };
641
+ return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-1.5", className), children: [
642
+ label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
643
+ compact ? /* @__PURE__ */ jsx(
644
+ "button",
645
+ {
646
+ ref: triggerRef,
647
+ type: "button",
648
+ "aria-haspopup": "dialog",
649
+ "aria-expanded": isOpen,
650
+ "aria-label": ariaLabel ?? (typeof label === "string" ? label : "Icon"),
651
+ title: selectedName ?? placeholder,
652
+ disabled: isDisabled,
653
+ onClick: () => open(!isOpen),
654
+ "data-invalid": isInvalid || void 0,
655
+ className: cn(
656
+ styles3.box(),
657
+ "flex shrink-0 items-center justify-center cursor-pointer",
658
+ appearance === "cell" ? "size-[29px]" : COMPACT_SIZE[size],
659
+ "disabled:cursor-not-allowed disabled:opacity-45"
660
+ ),
661
+ children: SelectedIcon ? /* @__PURE__ */ jsx(SelectedIcon, { "aria-hidden": true, className: "size-4 text-ink-2" }) : /* @__PURE__ */ jsx(Shapes, { "aria-hidden": true, className: "size-4 text-ink-4" })
662
+ }
663
+ ) : /* @__PURE__ */ jsxs(
664
+ "button",
665
+ {
666
+ ref: triggerRef,
667
+ type: "button",
668
+ "aria-haspopup": "dialog",
669
+ "aria-expanded": isOpen,
670
+ "aria-label": ariaLabel ?? (typeof label === "string" ? label : "Icon"),
671
+ disabled: isDisabled,
672
+ onClick: () => open(!isOpen),
673
+ "data-invalid": isInvalid || void 0,
674
+ className: cn(
675
+ // box carries no padding by contract — content is what gives a
676
+ // single-element control its interior (padding, font, text-size).
677
+ styles3.box(),
678
+ styles3.content(),
679
+ "flex w-full items-center gap-2 text-left cursor-pointer",
680
+ "disabled:cursor-not-allowed disabled:opacity-45"
681
+ ),
682
+ children: [
683
+ SelectedIcon ? /* @__PURE__ */ jsx(SelectedIcon, { "aria-hidden": true, className: "size-4 shrink-0 text-ink-2" }) : null,
684
+ /* @__PURE__ */ jsx("span", { className: cn("min-w-0 flex-1 truncate", selectedName ? "text-ink" : "text-ink-4"), children: selectedName ?? placeholder }),
685
+ /* @__PURE__ */ jsx(FieldCaret, {})
686
+ ]
687
+ }
688
+ ),
689
+ description ? /* @__PURE__ */ jsx("p", { className: fieldHintStyles, children: description }) : null,
690
+ errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx("p", { className: fieldErrorStyles, children: errorMessage }) : null,
691
+ /* @__PURE__ */ jsxs(
692
+ Popover$1,
693
+ {
694
+ isOpen,
695
+ onOpenChange: open,
696
+ triggerRef,
697
+ offset: 6,
698
+ placement: "bottom start",
699
+ className: cn(
700
+ "w-[316px] rounded-md border border-rule bg-surface p-2",
701
+ "shadow-[var(--shadow-raised)]",
702
+ "entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
703
+ "exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1"
704
+ ),
705
+ children: [
706
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 pb-2", children: [
707
+ /* @__PURE__ */ jsxs(
708
+ "div",
709
+ {
710
+ className: cn(
711
+ fieldControlStyles({ size: "sm" }).box(),
712
+ "flex min-w-0 flex-1 items-center gap-1.5 px-2.5 py-1.5",
713
+ "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]"
714
+ ),
715
+ children: [
716
+ /* @__PURE__ */ jsx(Search, { "aria-hidden": true, className: "size-3.5 shrink-0 text-ink-4" }),
717
+ /* @__PURE__ */ jsx(
718
+ Input$1,
719
+ {
720
+ autoFocus: true,
721
+ "aria-label": "Search icons",
722
+ value: query,
723
+ onChange: (event) => search(event.target.value),
724
+ placeholder: "Search icons\u2026",
725
+ className: cn(
726
+ "min-w-0 flex-1 bg-transparent font-sans outline-none",
727
+ "text-ink placeholder:text-ink-4",
728
+ fieldTextStyles.sm
729
+ )
730
+ }
731
+ )
732
+ ]
733
+ }
734
+ ),
735
+ allowClear && selectedName ? /* @__PURE__ */ jsxs(
736
+ "button",
737
+ {
738
+ type: "button",
739
+ onClick: () => pick(null),
740
+ className: cn(
741
+ "flex shrink-0 items-center gap-1 rounded-[6px] px-2 py-1.5 cursor-pointer",
742
+ "font-sans text-[12px] text-ink-3 transition-colors",
743
+ "hover:bg-surface-2 hover:text-ink",
744
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-accent)]"
745
+ ),
746
+ children: [
747
+ /* @__PURE__ */ jsx(X, { "aria-hidden": true, className: "size-3" }),
748
+ "Clear"
749
+ ]
750
+ }
751
+ ) : null
752
+ ] }),
753
+ /* @__PURE__ */ jsx(
754
+ "div",
755
+ {
756
+ className: "max-h-64 overflow-y-auto",
757
+ onScroll: (event) => onGridScroll(event.currentTarget),
758
+ children: /* @__PURE__ */ jsx(
759
+ ListBox,
760
+ {
761
+ "aria-label": "Icons",
762
+ layout: "grid",
763
+ selectionMode: "single",
764
+ selectedKeys: selectedName ? /* @__PURE__ */ new Set([selectedName]) : /* @__PURE__ */ new Set(),
765
+ onSelectionChange: (keys) => {
766
+ if (keys === "all") return;
767
+ const next = [...keys][0];
768
+ if (next !== void 0) pick(next);
769
+ },
770
+ className: "grid grid-cols-8 gap-0.5 outline-none",
771
+ renderEmptyState: () => /* @__PURE__ */ jsx("div", { className: cn("col-span-8 px-2 py-3 text-center", fieldHintStyles), children: "No icons match" }),
772
+ children: visible.map((name) => {
773
+ const Icon = icons[name];
774
+ return /* @__PURE__ */ jsx(
775
+ ListBoxItem,
776
+ {
777
+ id: name,
778
+ textValue: name,
779
+ "aria-label": name,
780
+ className: cn(
781
+ "flex size-[34px] items-center justify-center rounded-[6px]",
782
+ "cursor-pointer outline-none",
783
+ "data-[hovered]:bg-surface-2 data-[focused]:bg-surface-2",
784
+ "data-[focus-visible]:ring-2 data-[focus-visible]:ring-[var(--color-accent)]",
785
+ "data-[selected]:bg-[color-mix(in_oklab,var(--color-accent)_12%,var(--color-surface))]",
786
+ "data-[selected]:text-accent text-ink-2"
787
+ ),
788
+ children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: "size-4" })
789
+ },
790
+ name
791
+ );
792
+ })
793
+ }
794
+ )
795
+ }
796
+ ),
797
+ hiddenCount > 0 ? /* @__PURE__ */ jsxs("p", { className: cn("px-1 pt-2", fieldHintStyles), children: [
798
+ hiddenCount.toLocaleString(),
799
+ " more \u2014 keep scrolling, or search."
800
+ ] }) : null
801
+ ]
802
+ }
803
+ )
804
+ ] });
805
+ }
806
+ var badgeStyles = tv({
807
+ base: [
808
+ "inline-flex items-center gap-1.5",
809
+ "font-mono text-[11.5px] tracking-[-0.005em]",
810
+ "px-2.5 py-0.5 rounded-pill border",
811
+ // Leading dot
812
+ 'before:content-[""] before:block before:size-1.5 before:rounded-full'
813
+ ],
814
+ variants: {
815
+ variant: {
816
+ default: "border-rule bg-surface text-ink-3 before:bg-ink-4",
817
+ neutral: "border-rule bg-surface-3 text-ink-2 before:bg-ink-3",
818
+ active: [
819
+ "text-accent-ink before:bg-accent",
820
+ "border-[color-mix(in_oklab,var(--color-accent)_35%,var(--color-rule))]",
821
+ "bg-[color-mix(in_oklab,var(--color-accent)_14%,var(--color-surface))]"
822
+ ],
823
+ success: [
824
+ "text-success before:bg-success",
825
+ "border-[color-mix(in_oklab,var(--color-success)_35%,var(--color-rule))]",
826
+ "bg-[color-mix(in_oklab,var(--color-success)_12%,var(--color-surface))]"
827
+ ],
828
+ warning: [
829
+ "text-warning before:bg-warning",
830
+ "border-[color-mix(in_oklab,var(--color-warning)_35%,var(--color-rule))]",
831
+ "bg-[color-mix(in_oklab,var(--color-warning)_14%,var(--color-surface))]"
832
+ ],
833
+ danger: [
834
+ "text-danger before:bg-danger",
835
+ "border-[color-mix(in_oklab,var(--color-danger)_40%,var(--color-rule))]",
836
+ "bg-[color-mix(in_oklab,var(--color-danger)_12%,var(--color-surface))]"
837
+ ],
838
+ // Driven by the `--badge-color` custom property (set from the `color`
839
+ // prop). Chrome only — text stays ink so contrast never depends on the
840
+ // picked color.
841
+ custom: [
842
+ "text-ink-2 before:bg-[var(--badge-color)]",
843
+ "border-[color-mix(in_oklab,var(--badge-color)_35%,var(--color-rule))]",
844
+ "bg-[color-mix(in_oklab,var(--badge-color)_12%,var(--color-surface))]"
845
+ ]
846
+ }
847
+ },
848
+ defaultVariants: { variant: "default" }
849
+ });
850
+ var ICON_TONE = {
851
+ default: "text-ink-4",
852
+ neutral: "text-ink-3",
853
+ active: "text-accent",
854
+ success: "text-success",
855
+ warning: "text-warning",
856
+ danger: "text-danger",
857
+ custom: "text-[var(--badge-color)]"
858
+ };
859
+ function Badge({ variant, color, icon, className, style, children, ...props }) {
860
+ const Icon = icon ? resolveLucideIcon(icon) : null;
861
+ const register2 = color ? "custom" : variant ?? "default";
862
+ const mergedStyle = color ? { ...style, "--badge-color": color } : style;
863
+ return /* @__PURE__ */ jsxs(
864
+ "span",
865
+ {
866
+ ...props,
867
+ style: mergedStyle,
868
+ className: cn(badgeStyles({ variant: register2 }), Icon ? "before:hidden" : null, className),
869
+ children: [
870
+ Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: cn("size-3 shrink-0", ICON_TONE[register2]) }) : null,
871
+ children
872
+ ]
873
+ }
874
+ );
875
+ }
876
+ function badgeChrome(option) {
877
+ return {
878
+ ...option.variant ? { variant: option.variant } : {},
879
+ ...option.color ? { color: option.color } : {},
880
+ ...option.icon ? { icon: option.icon } : {}
881
+ };
882
+ }
573
883
  function BadgeMultiSelect({
574
884
  label,
575
885
  description,
@@ -642,7 +952,7 @@ function BadgeMultiSelect({
642
952
  ) : selectedOptions.map((o) => /* @__PURE__ */ jsx(
643
953
  RemovableBadge,
644
954
  {
645
- variant: o.variant,
955
+ ...badgeChrome(o),
646
956
  onRemove: () => remove(o.id),
647
957
  isDisabled,
648
958
  children: o.label
@@ -700,7 +1010,7 @@ function BadgeMultiSelect({
700
1010
  "data-[selected]:bg-surface-3",
701
1011
  "data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
702
1012
  ),
703
- children: /* @__PURE__ */ jsx(Badge, { variant: option.variant, children: option.label })
1013
+ children: /* @__PURE__ */ jsx(Badge, { ...badgeChrome(option), children: option.label })
704
1014
  },
705
1015
  option.id
706
1016
  ))
@@ -713,10 +1023,12 @@ function BadgeMultiSelect({
713
1023
  function RemovableBadge({
714
1024
  children,
715
1025
  variant,
1026
+ color,
1027
+ icon,
716
1028
  onRemove,
717
1029
  isDisabled
718
1030
  }) {
719
- return /* @__PURE__ */ jsxs(Badge, { variant, children: [
1031
+ return /* @__PURE__ */ jsxs(Badge, { ...badgeChrome({ variant, color, icon }), children: [
720
1032
  /* @__PURE__ */ jsx("span", { children }),
721
1033
  /* @__PURE__ */ jsx(
722
1034
  "button",
@@ -952,7 +1264,14 @@ function Checkbox({ children, className, ...props }) {
952
1264
  {
953
1265
  ...props,
954
1266
  className: (renderProps) => cn(
955
- "group inline-flex items-center gap-2.5 cursor-pointer",
1267
+ // `relative` so RAC's visually-hidden <input> — which is absolutely
1268
+ // positioned and is the element that actually receives focus — is
1269
+ // laid out against THIS checkbox. Without a positioned ancestor here
1270
+ // it resolves against a far-away container, ends up outside any
1271
+ // scroll clipping, and focusing it makes the browser scroll a
1272
+ // container the user cannot scroll back (the roles permission
1273
+ // matrix, where toggling a row threw the page off-screen).
1274
+ "group relative inline-flex items-center gap-2.5 cursor-pointer",
956
1275
  "font-sans text-[14px] text-ink-2 select-none",
957
1276
  "data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed",
958
1277
  typeof className === "function" ? className(renderProps) : className
@@ -1026,6 +1345,146 @@ function CheckboxGroup({
1026
1345
  }
1027
1346
  );
1028
1347
  }
1348
+ var HEX6 = /^#?([0-9a-f]{6})$/i;
1349
+ var HEX3 = /^#?([0-9a-f]{3})$/i;
1350
+ function normalizeHexColor(text) {
1351
+ const raw = (text ?? "").trim();
1352
+ if (raw === "") return null;
1353
+ const six = HEX6.exec(raw);
1354
+ if (six?.[1]) return `#${six[1].toLowerCase()}`;
1355
+ const three = HEX3.exec(raw);
1356
+ if (three?.[1]) {
1357
+ const [r, g, b] = three[1].toLowerCase();
1358
+ return `#${r}${r}${g}${g}${b}${b}`;
1359
+ }
1360
+ return null;
1361
+ }
1362
+ function ColorInput({
1363
+ label,
1364
+ description,
1365
+ errorMessage,
1366
+ placeholder = "#5b8def",
1367
+ value,
1368
+ onChange,
1369
+ isDisabled = false,
1370
+ isInvalid = false,
1371
+ size = "md",
1372
+ appearance,
1373
+ "aria-label": ariaLabel,
1374
+ className
1375
+ }) {
1376
+ const committed = normalizeHexColor(value);
1377
+ const [draft, setDraft] = useState(committed ?? "");
1378
+ const nativeRef = useRef(null);
1379
+ useEffect(() => {
1380
+ setDraft((current) => normalizeHexColor(current) === committed ? current : committed ?? "");
1381
+ }, [committed]);
1382
+ const isDraftInvalid = draft.trim() !== "" && normalizeHexColor(draft) === null;
1383
+ const invalid = isInvalid || isDraftInvalid;
1384
+ const styles3 = fieldControlStyles({ size, invalid, appearance });
1385
+ const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
1386
+ const edit = (next) => {
1387
+ setDraft(next);
1388
+ if (next.trim() === "") {
1389
+ if (committed !== null) onChange(null);
1390
+ return;
1391
+ }
1392
+ if (!HEX6.test(next.trim())) return;
1393
+ const hex = normalizeHexColor(next);
1394
+ if (hex !== null && hex !== committed) onChange(hex);
1395
+ };
1396
+ const settle = () => {
1397
+ const hex = normalizeHexColor(draft);
1398
+ if (hex === null) return;
1399
+ setDraft(hex);
1400
+ if (hex !== committed) onChange(hex);
1401
+ };
1402
+ const pick = (next) => {
1403
+ const hex = normalizeHexColor(next);
1404
+ if (hex === null) return;
1405
+ setDraft(hex);
1406
+ if (hex !== committed) onChange(hex);
1407
+ };
1408
+ return /* @__PURE__ */ jsxs(
1409
+ TextField,
1410
+ {
1411
+ value: draft,
1412
+ onChange: edit,
1413
+ isDisabled,
1414
+ isInvalid: invalid,
1415
+ onBlur: settle,
1416
+ ...ariaLabel ? { "aria-label": ariaLabel } : {},
1417
+ className: cn("grid grid-cols-1 gap-1.5", className),
1418
+ children: [
1419
+ label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
1420
+ /* @__PURE__ */ jsxs(
1421
+ "div",
1422
+ {
1423
+ "data-disabled": isDisabled || void 0,
1424
+ className: cn(
1425
+ styles3.box(),
1426
+ "flex items-center gap-2",
1427
+ size === "sm" ? "pl-2" : "pl-2.5",
1428
+ focusWithin
1429
+ ),
1430
+ children: [
1431
+ /* @__PURE__ */ jsxs(
1432
+ "button",
1433
+ {
1434
+ type: "button",
1435
+ "aria-label": "Pick color",
1436
+ disabled: isDisabled,
1437
+ onClick: () => nativeRef.current?.click(),
1438
+ className: cn(
1439
+ "relative flex shrink-0 items-center justify-center rounded-sm border border-rule",
1440
+ "cursor-pointer outline-none",
1441
+ "focus-visible:ring-2 focus-visible:ring-[var(--color-accent)]",
1442
+ "disabled:cursor-not-allowed",
1443
+ size === "sm" ? "size-4" : "size-5",
1444
+ committed ? null : "text-ink-4"
1445
+ ),
1446
+ style: committed ? { backgroundColor: committed } : void 0,
1447
+ children: [
1448
+ committed ? null : /* @__PURE__ */ jsx(Pipette, { "aria-hidden": true, className: "size-2.5" }),
1449
+ /* @__PURE__ */ jsx(
1450
+ "input",
1451
+ {
1452
+ ref: nativeRef,
1453
+ type: "color",
1454
+ tabIndex: -1,
1455
+ "aria-hidden": true,
1456
+ disabled: isDisabled,
1457
+ value: committed ?? "#000000",
1458
+ onChange: (event) => pick(event.target.value),
1459
+ className: "pointer-events-none absolute inset-0 size-full opacity-0"
1460
+ }
1461
+ )
1462
+ ]
1463
+ }
1464
+ ),
1465
+ /* @__PURE__ */ jsx(
1466
+ Input$1,
1467
+ {
1468
+ placeholder,
1469
+ spellCheck: false,
1470
+ autoComplete: "off",
1471
+ className: cn(
1472
+ styles3.content(),
1473
+ "min-w-0 flex-1 bg-transparent font-mono outline-none",
1474
+ "pl-0",
1475
+ fieldTextStyles[size]
1476
+ )
1477
+ }
1478
+ )
1479
+ ]
1480
+ }
1481
+ ),
1482
+ description ? /* @__PURE__ */ jsx(Text, { slot: "description", className: fieldHintStyles, children: description }) : null,
1483
+ errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx(FieldError, { className: fieldErrorStyles, children: errorMessage }) : null
1484
+ ]
1485
+ }
1486
+ );
1487
+ }
1029
1488
  function Combobox({
1030
1489
  label,
1031
1490
  description,
@@ -2161,6 +2620,7 @@ function useInfiniteScrollSentinel({
2161
2620
  return ref;
2162
2621
  }
2163
2622
  var DEFAULT_ROW_HEIGHT = 32;
2623
+ var SELECTION_COLUMN_ID = "__select__";
2164
2624
  function DataTable({
2165
2625
  query,
2166
2626
  columns,
@@ -2185,6 +2645,9 @@ function DataTable({
2185
2645
  extraRows,
2186
2646
  bodyBefore,
2187
2647
  bodyAfter,
2648
+ selectionMode = "none",
2649
+ selectedIds,
2650
+ onSelectionChange,
2188
2651
  columnBorders = false
2189
2652
  }) {
2190
2653
  const rows = useMemo(() => {
@@ -2195,19 +2658,112 @@ function DataTable({
2195
2658
  if (startExtras.length === 0 && endExtras.length === 0) return dataRows;
2196
2659
  return [...startExtras, ...dataRows, ...endExtras];
2197
2660
  }, [query.data, extraRows]);
2661
+ const resolveRowId = useMemo(
2662
+ () => getRowId ?? ((row, index) => {
2663
+ const r = row;
2664
+ if (typeof r.id === "string" || typeof r.id === "number") return String(r.id);
2665
+ if (typeof r.slug === "string") return r.slug;
2666
+ return String(index);
2667
+ }),
2668
+ [getRowId]
2669
+ );
2670
+ const selectionEnabled = selectionMode === "multiple";
2671
+ const selected = useMemo(() => selectedIds ?? /* @__PURE__ */ new Set(), [selectedIds]);
2672
+ const loadedIds = useMemo(
2673
+ () => selectionEnabled ? rows.map((row, index) => resolveRowId(row, index)) : [],
2674
+ [selectionEnabled, rows, resolveRowId]
2675
+ );
2676
+ const lastToggledIndexRef = useRef(null);
2677
+ const toggleRow = (id, index, isRange) => {
2678
+ if (!onSelectionChange) return;
2679
+ const next = new Set(selected);
2680
+ const anchor = lastToggledIndexRef.current;
2681
+ if (isRange && anchor !== null) {
2682
+ const [from, to] = anchor <= index ? [anchor, index] : [index, anchor];
2683
+ for (let i = from; i <= to; i += 1) {
2684
+ const rangeId = loadedIds[i];
2685
+ if (rangeId !== void 0) next.add(rangeId);
2686
+ }
2687
+ } else if (next.has(id)) {
2688
+ next.delete(id);
2689
+ } else {
2690
+ next.add(id);
2691
+ }
2692
+ lastToggledIndexRef.current = index;
2693
+ onSelectionChange(next);
2694
+ };
2695
+ const loadedSelectedCount = loadedIds.reduce((count, id) => count + (selected.has(id) ? 1 : 0), 0);
2696
+ const allLoadedSelected = loadedIds.length > 0 && loadedSelectedCount === loadedIds.length;
2697
+ const toggleAllLoaded = () => {
2698
+ if (!onSelectionChange) return;
2699
+ const next = new Set(selected);
2700
+ if (allLoadedSelected) {
2701
+ for (const id of loadedIds) next.delete(id);
2702
+ } else {
2703
+ for (const id of loadedIds) next.add(id);
2704
+ }
2705
+ lastToggledIndexRef.current = null;
2706
+ onSelectionChange(next);
2707
+ };
2708
+ const rangeModifierRef = useRef(false);
2709
+ const selectionColumn = useMemo(
2710
+ () => ({
2711
+ id: SELECTION_COLUMN_ID,
2712
+ size: 36,
2713
+ enableSorting: false,
2714
+ meta: { align: "center", sticky: "left", cellClassName: "px-0 justify-center" },
2715
+ header: () => /* @__PURE__ */ jsx("span", { className: "inline-flex items-center justify-center w-full", children: /* @__PURE__ */ jsx(
2716
+ Checkbox,
2717
+ {
2718
+ "aria-label": allLoadedSelected ? "Deselect all rows" : "Select all rows",
2719
+ isSelected: allLoadedSelected,
2720
+ isIndeterminate: loadedSelectedCount > 0 && !allLoadedSelected,
2721
+ isDisabled: loadedIds.length === 0,
2722
+ onChange: toggleAllLoaded
2723
+ }
2724
+ ) }),
2725
+ cell: ({ row }) => (
2726
+ // Stop the click here: selecting a row must never also open it.
2727
+ /* @__PURE__ */ jsx(
2728
+ "span",
2729
+ {
2730
+ className: "inline-flex items-center justify-center w-full",
2731
+ onClick: (event) => event.stopPropagation(),
2732
+ onKeyDown: (event) => {
2733
+ event.stopPropagation();
2734
+ rangeModifierRef.current = event.shiftKey;
2735
+ },
2736
+ onPointerDownCapture: (event) => {
2737
+ rangeModifierRef.current = event.shiftKey;
2738
+ },
2739
+ role: "presentation",
2740
+ children: /* @__PURE__ */ jsx(
2741
+ Checkbox,
2742
+ {
2743
+ "aria-label": `Select row ${row.index + 1}`,
2744
+ isSelected: selected.has(row.id),
2745
+ onChange: () => toggleRow(row.id, row.index, rangeModifierRef.current)
2746
+ }
2747
+ )
2748
+ }
2749
+ )
2750
+ )
2751
+ }),
2752
+ // biome-ignore lint/correctness/useExhaustiveDependencies: handlers read the latest selection via closure recreated on these inputs
2753
+ [selected, loadedIds, allLoadedSelected, loadedSelectedCount]
2754
+ );
2755
+ const tableColumns = useMemo(
2756
+ () => selectionEnabled ? [selectionColumn, ...columns] : columns,
2757
+ [selectionEnabled, selectionColumn, columns]
2758
+ );
2198
2759
  const table = useReactTable({
2199
2760
  data: rows,
2200
- columns,
2761
+ columns: tableColumns,
2201
2762
  state: { sorting: sorting ?? [] },
2202
2763
  ...onSortingChange ? { onSortingChange } : {},
2203
2764
  manualSorting: true,
2204
2765
  getCoreRowModel: getCoreRowModel(),
2205
- getRowId: getRowId ? (row, index) => getRowId(row, index) : (row, index) => {
2206
- const r = row;
2207
- if (typeof r.id === "string" || typeof r.id === "number") return String(r.id);
2208
- if (typeof r.slug === "string") return r.slug;
2209
- return String(index);
2210
- }
2766
+ getRowId: (row, index) => resolveRowId(row, index)
2211
2767
  });
2212
2768
  const tableRows = table.getRowModel().rows;
2213
2769
  const tableRowCount = tableRows.length;
@@ -2239,8 +2795,8 @@ function DataTable({
2239
2795
  enabled: !!query.hasNextPage
2240
2796
  });
2241
2797
  const totalColumnWidth = useMemo(
2242
- () => columns.reduce((sum, c) => sum + (c.size ?? 140), 0),
2243
- [columns]
2798
+ () => tableColumns.reduce((sum, c) => sum + (c.size ?? 140), 0),
2799
+ [tableColumns]
2244
2800
  );
2245
2801
  const columnsRef = useRef(columns);
2246
2802
  columnsRef.current = columns;
@@ -4876,25 +5432,25 @@ var OPERATORS = [
4876
5432
  id: "eq",
4877
5433
  label: "is",
4878
5434
  arity: 1,
4879
- types: ["string", "number", "integer", "currency", "datetime", "enum", "boolean", "relation", "user"]
5435
+ types: ["string", "number", "integer", "currency", "datetime", "enum", "boolean", "relation", "user", "principal"]
4880
5436
  },
4881
5437
  {
4882
5438
  id: "ne",
4883
5439
  label: "is not",
4884
5440
  arity: 1,
4885
- types: ["string", "number", "integer", "currency", "datetime", "enum", "boolean", "relation", "user"]
5441
+ types: ["string", "number", "integer", "currency", "datetime", "enum", "boolean", "relation", "user", "principal"]
4886
5442
  },
4887
5443
  {
4888
5444
  id: "in",
4889
5445
  label: "is any of",
4890
5446
  arity: "n",
4891
- types: ["string", "number", "integer", "currency", "enum", "relation", "user"]
5447
+ types: ["string", "number", "integer", "currency", "enum", "relation", "user", "principal"]
4892
5448
  },
4893
5449
  {
4894
5450
  id: "not_in",
4895
5451
  label: "is none of",
4896
5452
  arity: "n",
4897
- types: ["string", "number", "integer", "currency", "enum", "relation", "user"]
5453
+ types: ["string", "number", "integer", "currency", "enum", "relation", "user", "principal"]
4898
5454
  },
4899
5455
  { id: "contains", label: "contains", arity: 1, types: ["string", "array"] },
4900
5456
  { id: "starts_with", label: "starts with", arity: 1, types: ["string"] },
@@ -4907,13 +5463,13 @@ var OPERATORS = [
4907
5463
  id: "empty",
4908
5464
  label: "is empty",
4909
5465
  arity: 0,
4910
- types: ["string", "number", "integer", "currency", "datetime", "enum", "boolean", "array", "relation", "user", "file"]
5466
+ types: ["string", "number", "integer", "currency", "datetime", "enum", "boolean", "array", "relation", "user", "principal", "file"]
4911
5467
  },
4912
5468
  {
4913
5469
  id: "not_empty",
4914
5470
  label: "is not empty",
4915
5471
  arity: 0,
4916
- types: ["string", "number", "integer", "currency", "datetime", "enum", "boolean", "array", "relation", "user", "file"]
5472
+ types: ["string", "number", "integer", "currency", "datetime", "enum", "boolean", "array", "relation", "user", "principal", "file"]
4917
5473
  }
4918
5474
  ];
4919
5475
  var OP_LABEL_OVERRIDES = {
@@ -5001,9 +5557,16 @@ function ChipItem({
5001
5557
  if (custom !== void 0) return /* @__PURE__ */ jsx(Fragment, { children: custom });
5002
5558
  }
5003
5559
  if (field.type === "enum") {
5004
- const variant = field.options?.find((o) => o.value === value)?.variant;
5005
- const label = field.options?.find((o) => o.value === value)?.label ?? value;
5006
- return /* @__PURE__ */ jsx(Badge, { variant, children: label });
5560
+ const option = field.options?.find((o) => o.value === value);
5561
+ return /* @__PURE__ */ jsx(
5562
+ Badge,
5563
+ {
5564
+ variant: option?.variant,
5565
+ ...option?.color ? { color: option.color } : {},
5566
+ ...option?.icon ? { icon: option.icon } : {},
5567
+ children: option?.label ?? value
5568
+ }
5569
+ );
5007
5570
  }
5008
5571
  return /* @__PURE__ */ jsx("span", { className: cn("font-mono text-[12px] text-ink max-w-[10rem] truncate"), children: formatValue(field, operator, value) });
5009
5572
  }
@@ -5384,8 +5947,16 @@ function EnumSingleValue({
5384
5947
  selectedKey: value === "" ? null : value,
5385
5948
  onSelectionChange: (next) => onChange(next == null ? "" : String(next)),
5386
5949
  renderItem: (o) => {
5387
- const variant = field.options?.find((x) => x.value === o.id)?.variant;
5388
- return /* @__PURE__ */ jsx(Badge, { variant, children: o.label });
5950
+ const option = field.options?.find((x) => x.value === o.id);
5951
+ return /* @__PURE__ */ jsx(
5952
+ Badge,
5953
+ {
5954
+ variant: option?.variant,
5955
+ ...option?.color ? { color: option.color } : {},
5956
+ ...option?.icon ? { icon: option.icon } : {},
5957
+ children: o.label
5958
+ }
5959
+ );
5389
5960
  },
5390
5961
  ...isDisabled !== void 0 ? { isDisabled } : {},
5391
5962
  ...autoFocus ? { autoFocus: true } : {},
@@ -5404,7 +5975,9 @@ function EnumMultiValue({
5404
5975
  const options = (field.options ?? []).map((o) => ({
5405
5976
  id: o.value,
5406
5977
  label: o.label,
5407
- ...o.variant ? { variant: o.variant } : {}
5978
+ ...o.variant ? { variant: o.variant } : {},
5979
+ ...o.color ? { color: o.color } : {},
5980
+ ...o.icon ? { icon: o.icon } : {}
5408
5981
  }));
5409
5982
  return /* @__PURE__ */ jsx(
5410
5983
  BadgeMultiSelect,
@@ -7038,6 +7611,74 @@ function TimeField({
7038
7611
  }
7039
7612
  );
7040
7613
  }
7614
+ function createToastQueue(options = {}) {
7615
+ return new UNSTABLE_ToastQueue({ maxVisibleToasts: options.maxVisibleToasts ?? 3 });
7616
+ }
7617
+ var toastStyles = tv({
7618
+ slots: {
7619
+ root: [
7620
+ "flex items-start gap-3 w-[360px] max-w-[calc(100vw-32px)]",
7621
+ "rounded-lg border border-rule bg-surface shadow-float",
7622
+ "px-3.5 py-3 outline-none",
7623
+ "focus-visible:shadow-focus",
7624
+ "entering:animate-in entering:fade-in-0 entering:slide-in-from-bottom-2 entering:duration-200",
7625
+ "exiting:animate-out exiting:fade-out-0 exiting:duration-150"
7626
+ ],
7627
+ icon: "size-4 shrink-0 mt-0.5",
7628
+ title: "font-sans text-[13px] font-medium leading-[1.4] text-ink",
7629
+ description: "font-sans text-[12.5px] leading-[1.5] text-ink-3 break-words"
7630
+ },
7631
+ variants: {
7632
+ variant: {
7633
+ info: { icon: "text-ink-3" },
7634
+ success: { icon: "text-success" },
7635
+ danger: { icon: "text-danger" }
7636
+ }
7637
+ },
7638
+ defaultVariants: { variant: "info" }
7639
+ });
7640
+ var VARIANT_ICON = {
7641
+ info: Info,
7642
+ success: CircleCheck,
7643
+ danger: CircleAlert
7644
+ };
7645
+ function ToastRegion({ queue, className }) {
7646
+ return /* @__PURE__ */ jsx(
7647
+ UNSTABLE_ToastRegion,
7648
+ {
7649
+ queue,
7650
+ className: cn(
7651
+ "fixed bottom-4 right-4 z-[1300] flex flex-col-reverse gap-2 outline-none",
7652
+ className
7653
+ ),
7654
+ children: ({ toast }) => {
7655
+ const variant = toast.content.variant ?? "info";
7656
+ const Icon = VARIANT_ICON[variant];
7657
+ const { root, icon, title, description } = toastStyles({ variant });
7658
+ return /* @__PURE__ */ jsxs(UNSTABLE_Toast, { toast, className: root(), children: [
7659
+ /* @__PURE__ */ jsx(Icon, { className: icon(), "aria-hidden": true }),
7660
+ /* @__PURE__ */ jsxs(UNSTABLE_ToastContent, { className: "flex flex-col gap-0.5 flex-1 min-w-0", children: [
7661
+ /* @__PURE__ */ jsx(Text, { slot: "title", className: title(), children: toast.content.title }),
7662
+ toast.content.description ? /* @__PURE__ */ jsx(Text, { slot: "description", className: description(), children: toast.content.description }) : null
7663
+ ] }),
7664
+ /* @__PURE__ */ jsx(
7665
+ Button$1,
7666
+ {
7667
+ slot: "close",
7668
+ "aria-label": "Dismiss",
7669
+ className: cn(
7670
+ "shrink-0 inline-flex items-center justify-center size-6 rounded-md",
7671
+ "text-ink-4 hover:text-ink hover:bg-surface-2 cursor-pointer outline-none",
7672
+ "focus-visible:shadow-focus"
7673
+ ),
7674
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
7675
+ }
7676
+ )
7677
+ ] });
7678
+ }
7679
+ }
7680
+ );
7681
+ }
7041
7682
  function TooltipTrigger({
7042
7683
  delay = 500,
7043
7684
  closeDelay = 0,
@@ -7064,6 +7705,6 @@ function Tooltip({ className, offset = 6, ...props }) {
7064
7705
  );
7065
7706
  }
7066
7707
 
7067
- export { ATTRIBUTE_TYPES, ATTRIBUTE_TYPE_ICONS, AttachmentChip, AttributePicker, AttributeType, AttributeTypeIcon, Avatar, AvatarGroup, Badge, BadgeMultiSelect, BooleanCell, Breadcrumbs, COLUMN_DRAG_KIND, Card, CardBody, CardContent, CardEyebrow, CardFeatureTitle, CardFooter, CardHeader, CardTitle, ChannelIcon, Checkbox, CheckboxGroup, ChipValue, Combobox, Command, CommandHint, CurrencyInput, DRAFT_OUTCOME_LABEL, DataTable, DataTableEmpty, DataTableError, DataTableSkeleton, DateCell, DatePicker, DateTimeCell, DateTimePicker, DraftMessageCard, DraftMessageComposer, DraftMessageReview, EmptyState, ErrorAnimation, FieldCaret, FileField, FileViewer, FileViewerFallbackCard, FilterRow, FilterValueInput, ImageAttachment, ImageLightbox, Input, Kbd, LinkCell, MAX_COMPOSER_ATTACHMENT_BYTES, Menu, MenuItem, MenuPopover, MenuTrigger, MessageComposer, Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, MultiSelect, NavGroup, NavItem, NavSection, OPERATORS, OperatorPicker, PROTECTED_COMBOS, Popover, PopoverContent, PopoverPanelBody, PopoverPanelFooter, PopoverPanelHeader, PressableCard, ProgressBar, Radio, RadioGroup, RecipientSelect, RecordFilterBuilderPanel, RecordFilterButton, RecordFilterChips, RecordFilterEditor, RenderChipItemProvider, RenderValueInputProvider, SegmentedControl, Select, Separator, SubmenuTrigger, SuccessAnimation, Switch, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TableHeaderSpacer, TableRow, TableScroller, Tabs, TagInput, Textarea, TimeField, Tooltip, TooltipTrigger, TruncateCell, __resetDraftMessageActions, __resetDraftMessageOutcomes, addLeaf as addFilterLeaf, addPendingLeaf, addSubgroup, arityOf, attachmentChipStyles, avatarGroupStyles, avatarStyles, badgeStyles, cardStyles2 as cardStyles, channelIcon, countLeaves, createDraftMessageClient, defaultValueFor, draftMessageCardStyles, draftMessageComposerStyles, draftOutcomeFor, emptyStateStyles, fieldControlStyles, fieldErrorStyles, fieldHintStyles, fieldLabelStyles, fileViewerFallbackCardStyles, fileViewerStyles, formatFileSize, formatValue, iconForContentType, imageAttachmentStyles, imageLightboxStyles, isColumnDragData, isDraftReviewOutcome, isEditableTarget, isPreviewableImageName, makeActionsColumn, messageComposerStyles, navItemStyles, operatorById, operatorsForType, parseCombo, progressBarStyles, publishDraftMessageOutcome, rebaseRowOnFieldChange, rebaseRowOnOperatorChange, registerDraftMessageActions, removeLeaf, removeSubgroup, replaceLeaf, segmentedControlStyles, setJoiner, subscribeDraftMessageOutcome, tableCellStyles, tableHeaderCellStyles, tabsStyles, terminalKindFor, updateLeaf, updateSubgroup, useAttributeTypeIcon, useCommand, useDiscoveryActive, useDraftMessageActions, useDraftMessageOutcome, useEscapeKey, useInfiniteScrollSentinel, useRenderChipItem, useRenderValueInput, useShowDetails, viewerKindForContentType, wrapLeafInGroup };
7708
+ export { ATTRIBUTE_TYPES, ATTRIBUTE_TYPE_ICONS, AttachmentChip, AttributePicker, AttributeType, AttributeTypeIcon, Avatar, AvatarGroup, Badge, BadgeMultiSelect, BooleanCell, Breadcrumbs, COLUMN_DRAG_KIND, Card, CardBody, CardContent, CardEyebrow, CardFeatureTitle, CardFooter, CardHeader, CardTitle, ChannelIcon, Checkbox, CheckboxGroup, ChipValue, ColorInput, Combobox, Command, CommandHint, CurrencyInput, DRAFT_OUTCOME_LABEL, DataTable, DataTableEmpty, DataTableError, DataTableSkeleton, DateCell, DatePicker, DateTimeCell, DateTimePicker, DraftMessageCard, DraftMessageComposer, DraftMessageReview, EmptyState, ErrorAnimation, FieldCaret, FileField, FileViewer, FileViewerFallbackCard, FilterRow, FilterValueInput, IconPicker, ImageAttachment, ImageLightbox, Input, Kbd, LinkCell, MAX_COMPOSER_ATTACHMENT_BYTES, Menu, MenuItem, MenuPopover, MenuTrigger, MessageComposer, Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, MultiSelect, NavGroup, NavItem, NavSection, OPERATORS, OperatorPicker, PROTECTED_COMBOS, Popover, PopoverContent, PopoverPanelBody, PopoverPanelFooter, PopoverPanelHeader, PressableCard, ProgressBar, Radio, RadioGroup, RecipientSelect, RecordFilterBuilderPanel, RecordFilterButton, RecordFilterChips, RecordFilterEditor, RenderChipItemProvider, RenderValueInputProvider, SELECTION_COLUMN_ID, SegmentedControl, Select, Separator, SubmenuTrigger, SuccessAnimation, Switch, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TableHeaderSpacer, TableRow, TableScroller, Tabs, TagInput, Textarea, TimeField, ToastRegion, Tooltip, TooltipTrigger, TruncateCell, __resetDraftMessageActions, __resetDraftMessageOutcomes, addLeaf as addFilterLeaf, addPendingLeaf, addSubgroup, arityOf, attachmentChipStyles, avatarGroupStyles, avatarStyles, badgeStyles, cardStyles2 as cardStyles, channelIcon, countLeaves, createDraftMessageClient, createToastQueue, defaultValueFor, draftMessageCardStyles, draftMessageComposerStyles, draftOutcomeFor, emptyStateStyles, fieldControlStyles, fieldErrorStyles, fieldHintStyles, fieldLabelStyles, fileViewerFallbackCardStyles, fileViewerStyles, formatFileSize, formatValue, iconForContentType, imageAttachmentStyles, imageLightboxStyles, isColumnDragData, isDraftReviewOutcome, isEditableTarget, isPreviewableImageName, makeActionsColumn, messageComposerStyles, navItemStyles, normalizeHexColor, operatorById, operatorsForType, parseCombo, progressBarStyles, publishDraftMessageOutcome, rebaseRowOnFieldChange, rebaseRowOnOperatorChange, registerDraftMessageActions, removeLeaf, removeSubgroup, replaceLeaf, resolveLucideIcon, resolveLucideIconName, segmentedControlStyles, setJoiner, subscribeDraftMessageOutcome, tableCellStyles, tableHeaderCellStyles, tabsStyles, terminalKindFor, toastStyles, updateLeaf, updateSubgroup, useAttributeTypeIcon, useCommand, useDiscoveryActive, useDraftMessageActions, useDraftMessageOutcome, useEscapeKey, useInfiniteScrollSentinel, useRenderChipItem, useRenderValueInput, useShowDetails, viewerKindForContentType, wrapLeafInGroup };
7068
7709
  //# sourceMappingURL=index.js.map
7069
7710
  //# sourceMappingURL=index.js.map