@proteos/ui 0.31.0 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1302 -516
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +259 -1
- package/dist/index.d.ts +259 -1
- package/dist/index.js +1292 -520
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/draft-message-composer/create-draft-message-client.ts +27 -0
- package/src/components/draft-message-composer/draft-message-actions-store.test.ts +62 -0
- package/src/components/draft-message-composer/draft-message-actions-store.ts +81 -0
- package/src/components/draft-message-composer/draft-message-composer.stories.tsx +175 -0
- package/src/components/draft-message-composer/draft-message-composer.test.tsx +276 -0
- package/src/components/draft-message-composer/draft-message-composer.tsx +470 -0
- package/src/components/draft-message-composer/draft-message-outcome-store.test.ts +59 -0
- package/src/components/draft-message-composer/draft-message-outcome-store.ts +56 -0
- package/src/components/draft-message-composer/index.ts +19 -0
- package/src/components/draft-message-composer/types.ts +32 -0
- package/src/components/message-composer/index.ts +13 -0
- package/src/components/message-composer/message-composer.stories.tsx +155 -0
- package/src/components/message-composer/message-composer.test.tsx +320 -0
- package/src/components/message-composer/message-composer.tsx +480 -0
- package/src/components/recipient-select/index.ts +2 -0
- package/src/components/recipient-select/recipient-select.test.tsx +139 -0
- package/src/components/recipient-select/recipient-select.tsx +94 -0
- package/src/index.ts +3 -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, ChevronDown, GripVertical, CircleX, Download, ChevronRight as ChevronRight$1, CircleCheck } from 'lucide-react';
|
|
3
|
+
import { Paperclip, BookText, Banknote, User, Link2, Braces, Brackets, ToggleLeft, CircleDot, Calendar, Hash, Type, File, FileImage, FileText, FileSpreadsheet, FileArchive, Loader2, X, ChevronDown, GripVertical, SendHorizontal, CircleX, Download, ChevronRight as ChevronRight$1, CircleCheck } 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, 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,
|
|
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';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
import { lazy, createContext, useState, useRef, useEffect, useContext, useSyncExternalStore, useMemo, useLayoutEffect, Suspense, useId, Fragment as Fragment$1 } from 'react';
|
|
7
|
+
import { lazy, createContext, useState, useRef, useEffect, useContext, useSyncExternalStore, useMemo, 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';
|
|
@@ -573,7 +573,7 @@ function BadgeMultiSelect({
|
|
|
573
573
|
autoFocus = false,
|
|
574
574
|
className
|
|
575
575
|
}) {
|
|
576
|
-
const
|
|
576
|
+
const styles2 = fieldControlStyles({ size, invalid: isInvalid, appearance });
|
|
577
577
|
const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
|
|
578
578
|
const minHeight = appearance === "cell" ? "min-h-[29px]" : fieldMinHeight[size];
|
|
579
579
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -613,7 +613,7 @@ function BadgeMultiSelect({
|
|
|
613
613
|
"data-invalid": isInvalid || void 0,
|
|
614
614
|
"data-disabled": isDisabled || void 0,
|
|
615
615
|
className: cn(
|
|
616
|
-
|
|
616
|
+
styles2.box(),
|
|
617
617
|
"flex flex-wrap items-center gap-1.5 cursor-pointer",
|
|
618
618
|
minHeight,
|
|
619
619
|
fieldChipPadding[size],
|
|
@@ -1007,7 +1007,7 @@ function Combobox({
|
|
|
1007
1007
|
allowsEmptyCollection = true,
|
|
1008
1008
|
...props
|
|
1009
1009
|
}) {
|
|
1010
|
-
const
|
|
1010
|
+
const styles2 = fieldControlStyles({ size, appearance });
|
|
1011
1011
|
const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
|
|
1012
1012
|
return /* @__PURE__ */ jsxs(
|
|
1013
1013
|
ComboBox,
|
|
@@ -1026,7 +1026,7 @@ function Combobox({
|
|
|
1026
1026
|
className: cn(
|
|
1027
1027
|
// Box chrome on the wrapper; the inner <input> carries the padding
|
|
1028
1028
|
// + font so the chevron button sits inside the same border + ring.
|
|
1029
|
-
|
|
1029
|
+
styles2.box(),
|
|
1030
1030
|
"flex items-center gap-1 pr-2",
|
|
1031
1031
|
focusWithin,
|
|
1032
1032
|
inputWrapperClassName
|
|
@@ -1038,7 +1038,7 @@ function Combobox({
|
|
|
1038
1038
|
placeholder,
|
|
1039
1039
|
className: cn(
|
|
1040
1040
|
"flex-1 min-w-0 bg-transparent outline-none",
|
|
1041
|
-
|
|
1041
|
+
styles2.content(),
|
|
1042
1042
|
inputClassName
|
|
1043
1043
|
)
|
|
1044
1044
|
}
|
|
@@ -1478,16 +1478,16 @@ function CurrencyInput({
|
|
|
1478
1478
|
onChange({ amount: nextAmount, currency_code: nextCode });
|
|
1479
1479
|
};
|
|
1480
1480
|
const selectedSymbol = currencyOptions.find((option) => option.code === code)?.symbol ?? code;
|
|
1481
|
-
const
|
|
1481
|
+
const styles2 = fieldControlStyles({ size, appearance, ...isInvalid ? { invalid: true } : {} });
|
|
1482
1482
|
const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
|
|
1483
1483
|
return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-1.5", className), children: [
|
|
1484
|
-
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
1484
|
+
/* @__PURE__ */ jsxs("div", { className: cn(styles2.box(), "flex items-center gap-1 pr-1", focusWithin), children: [
|
|
1485
1485
|
/* @__PURE__ */ jsx(
|
|
1486
1486
|
"input",
|
|
1487
1487
|
{
|
|
1488
1488
|
className: cn(
|
|
1489
1489
|
"min-w-0 flex-1 bg-transparent text-ink outline-none tabular-nums",
|
|
1490
|
-
|
|
1490
|
+
styles2.content(),
|
|
1491
1491
|
// Sit a touch lower than a standard field by trimming the vertical
|
|
1492
1492
|
// padding (cell mode keeps the fixed 29px row-editor height).
|
|
1493
1493
|
appearance === "cell" ? "" : size === "sm" ? "py-1" : "py-1.5"
|
|
@@ -2561,7 +2561,7 @@ function DatePicker({
|
|
|
2561
2561
|
className,
|
|
2562
2562
|
...props
|
|
2563
2563
|
}) {
|
|
2564
|
-
const
|
|
2564
|
+
const styles2 = fieldControlStyles({ size, appearance });
|
|
2565
2565
|
const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
|
|
2566
2566
|
return /* @__PURE__ */ jsxs(
|
|
2567
2567
|
DatePicker$1,
|
|
@@ -2579,7 +2579,7 @@ function DatePicker({
|
|
|
2579
2579
|
className: cn(
|
|
2580
2580
|
// Box chrome on the wrapper; the DateInput inside carries padding +
|
|
2581
2581
|
// font so the calendar trigger sits inside the same border + ring.
|
|
2582
|
-
|
|
2582
|
+
styles2.box(),
|
|
2583
2583
|
"flex items-center gap-1 pr-1",
|
|
2584
2584
|
focusWithin
|
|
2585
2585
|
),
|
|
@@ -2589,7 +2589,7 @@ function DatePicker({
|
|
|
2589
2589
|
{
|
|
2590
2590
|
className: cn(
|
|
2591
2591
|
"flex-1 flex items-center min-w-0 text-ink",
|
|
2592
|
-
|
|
2592
|
+
styles2.content(),
|
|
2593
2593
|
// Tabular alignment for date segments — overrides content's
|
|
2594
2594
|
// font-sans on purpose.
|
|
2595
2595
|
"font-mono"
|
|
@@ -2711,7 +2711,7 @@ function DateTimePicker({
|
|
|
2711
2711
|
shouldCloseOnSelect = false,
|
|
2712
2712
|
...props
|
|
2713
2713
|
}) {
|
|
2714
|
-
const
|
|
2714
|
+
const styles2 = fieldControlStyles({ size, appearance });
|
|
2715
2715
|
const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
|
|
2716
2716
|
const timeGranularity = granularity === "day" ? "minute" : granularity;
|
|
2717
2717
|
const { hourCycle } = props;
|
|
@@ -2734,7 +2734,7 @@ function DateTimePicker({
|
|
|
2734
2734
|
className: cn(
|
|
2735
2735
|
// Box chrome on the wrapper; the DateInput inside carries padding +
|
|
2736
2736
|
// font so the calendar trigger sits inside the same border + ring.
|
|
2737
|
-
|
|
2737
|
+
styles2.box(),
|
|
2738
2738
|
"flex items-center gap-1 pr-1",
|
|
2739
2739
|
focusWithin
|
|
2740
2740
|
),
|
|
@@ -2744,7 +2744,7 @@ function DateTimePicker({
|
|
|
2744
2744
|
{
|
|
2745
2745
|
className: cn(
|
|
2746
2746
|
"flex-1 flex items-center min-w-0 text-ink",
|
|
2747
|
-
|
|
2747
|
+
styles2.content(),
|
|
2748
2748
|
// Tabular alignment for date/time segments — overrides content's
|
|
2749
2749
|
// font-sans on purpose.
|
|
2750
2750
|
"font-mono"
|
|
@@ -2804,6 +2804,1117 @@ function DateTimePicker({
|
|
|
2804
2804
|
}
|
|
2805
2805
|
);
|
|
2806
2806
|
}
|
|
2807
|
+
|
|
2808
|
+
// src/components/draft-message-composer/create-draft-message-client.ts
|
|
2809
|
+
function createDraftMessageClient(conversation) {
|
|
2810
|
+
return {
|
|
2811
|
+
getMessage: (messageId) => conversation.messages.get(messageId),
|
|
2812
|
+
getConversation: (conversationId) => conversation.conversations.get(conversationId),
|
|
2813
|
+
updateDraft: (messageId, patch) => conversation.messages.updateDraft(messageId, patch),
|
|
2814
|
+
sendDraft: (messageId) => conversation.messages.sendDraft(messageId),
|
|
2815
|
+
rejectDraft: (messageId) => conversation.messages.rejectDraft(messageId),
|
|
2816
|
+
searchRecipients: async (connectionId, q) => (await conversation.connections.listContactAddresses(connectionId, {
|
|
2817
|
+
page_size: 100,
|
|
2818
|
+
...q ? { q } : {}
|
|
2819
|
+
})).data
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
var actionsByMessageId = /* @__PURE__ */ new Map();
|
|
2823
|
+
var listenersByMessageId = /* @__PURE__ */ new Map();
|
|
2824
|
+
function notify(messageId) {
|
|
2825
|
+
for (const listener of listenersByMessageId.get(messageId) ?? []) listener();
|
|
2826
|
+
}
|
|
2827
|
+
function registerDraftMessageActions(messageId, actions) {
|
|
2828
|
+
actionsByMessageId.set(messageId, actions);
|
|
2829
|
+
notify(messageId);
|
|
2830
|
+
return () => {
|
|
2831
|
+
if (actionsByMessageId.get(messageId) === actions) {
|
|
2832
|
+
actionsByMessageId.delete(messageId);
|
|
2833
|
+
notify(messageId);
|
|
2834
|
+
}
|
|
2835
|
+
};
|
|
2836
|
+
}
|
|
2837
|
+
function useDraftMessageActions(messageId) {
|
|
2838
|
+
return useSyncExternalStore(
|
|
2839
|
+
(listener) => {
|
|
2840
|
+
let listeners3 = listenersByMessageId.get(messageId);
|
|
2841
|
+
if (!listeners3) {
|
|
2842
|
+
listeners3 = /* @__PURE__ */ new Set();
|
|
2843
|
+
listenersByMessageId.set(messageId, listeners3);
|
|
2844
|
+
}
|
|
2845
|
+
listeners3.add(listener);
|
|
2846
|
+
return () => {
|
|
2847
|
+
listeners3.delete(listener);
|
|
2848
|
+
if (listeners3.size === 0) listenersByMessageId.delete(messageId);
|
|
2849
|
+
};
|
|
2850
|
+
},
|
|
2851
|
+
() => actionsByMessageId.get(messageId) ?? null,
|
|
2852
|
+
() => null
|
|
2853
|
+
);
|
|
2854
|
+
}
|
|
2855
|
+
function __resetDraftMessageActions() {
|
|
2856
|
+
actionsByMessageId.clear();
|
|
2857
|
+
listenersByMessageId.clear();
|
|
2858
|
+
}
|
|
2859
|
+
function MultiSelect({
|
|
2860
|
+
label,
|
|
2861
|
+
description,
|
|
2862
|
+
errorMessage,
|
|
2863
|
+
placeholder = "Select\u2026",
|
|
2864
|
+
options,
|
|
2865
|
+
selectedKeys,
|
|
2866
|
+
onSelectionChange,
|
|
2867
|
+
isDisabled = false,
|
|
2868
|
+
isInvalid = false,
|
|
2869
|
+
hideSelectedOptions = false,
|
|
2870
|
+
isSearchable = false,
|
|
2871
|
+
onSearchChange,
|
|
2872
|
+
size = "md",
|
|
2873
|
+
appearance,
|
|
2874
|
+
className
|
|
2875
|
+
}) {
|
|
2876
|
+
const styles2 = fieldControlStyles({ size, invalid: isInvalid, appearance });
|
|
2877
|
+
const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
|
|
2878
|
+
const minHeight = appearance === "cell" ? "min-h-[29px]" : fieldMinHeight[size];
|
|
2879
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
2880
|
+
const [search, setSearch] = useState("");
|
|
2881
|
+
const triggerRef = useRef(null);
|
|
2882
|
+
const selectedSet = new Set(selectedKeys);
|
|
2883
|
+
const selectedOptions = options.filter((o) => selectedSet.has(o.id));
|
|
2884
|
+
const needle = search.trim().toLowerCase();
|
|
2885
|
+
const listboxOptions = (hideSelectedOptions ? options.filter((o) => !selectedSet.has(o.id)) : options).filter((o) => needle === "" || o.label.toLowerCase().includes(needle));
|
|
2886
|
+
const updateSearch = (next) => {
|
|
2887
|
+
setSearch(next);
|
|
2888
|
+
onSearchChange?.(next);
|
|
2889
|
+
};
|
|
2890
|
+
const openPopover = (open) => {
|
|
2891
|
+
setIsOpen(open);
|
|
2892
|
+
if (!open && search !== "") updateSearch("");
|
|
2893
|
+
};
|
|
2894
|
+
const remove = (id) => {
|
|
2895
|
+
const next = new Set(selectedSet);
|
|
2896
|
+
next.delete(id);
|
|
2897
|
+
onSelectionChange(next);
|
|
2898
|
+
};
|
|
2899
|
+
const onTriggerKeyDown = (e) => {
|
|
2900
|
+
if (isDisabled) return;
|
|
2901
|
+
if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
|
|
2902
|
+
e.preventDefault();
|
|
2903
|
+
openPopover(true);
|
|
2904
|
+
}
|
|
2905
|
+
};
|
|
2906
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-1.5", className), children: [
|
|
2907
|
+
label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
|
|
2908
|
+
/* @__PURE__ */ jsxs(
|
|
2909
|
+
"div",
|
|
2910
|
+
{
|
|
2911
|
+
ref: triggerRef,
|
|
2912
|
+
role: "combobox",
|
|
2913
|
+
"aria-expanded": isOpen,
|
|
2914
|
+
"aria-haspopup": "listbox",
|
|
2915
|
+
"aria-disabled": isDisabled || void 0,
|
|
2916
|
+
"aria-invalid": isInvalid || void 0,
|
|
2917
|
+
tabIndex: isDisabled ? -1 : 0,
|
|
2918
|
+
onClick: () => !isDisabled && openPopover(!isOpen),
|
|
2919
|
+
onKeyDown: onTriggerKeyDown,
|
|
2920
|
+
"data-invalid": isInvalid || void 0,
|
|
2921
|
+
"data-disabled": isDisabled || void 0,
|
|
2922
|
+
className: cn(
|
|
2923
|
+
styles2.box(),
|
|
2924
|
+
"flex flex-wrap items-center gap-1.5 cursor-pointer",
|
|
2925
|
+
minHeight,
|
|
2926
|
+
fieldChipPadding[size],
|
|
2927
|
+
"pr-2",
|
|
2928
|
+
// Let focus-within light up the ring on both the trigger and any
|
|
2929
|
+
// chip's remove button without any extra JS.
|
|
2930
|
+
focusWithin
|
|
2931
|
+
),
|
|
2932
|
+
children: [
|
|
2933
|
+
selectedOptions.length === 0 ? (
|
|
2934
|
+
// No vertical padding — wrapper's p-1.5 + min-h-[size] govern the
|
|
2935
|
+
// overall height so it matches the Input/Select trigger at the same
|
|
2936
|
+
// size.
|
|
2937
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-ink-4 font-sans px-1", fieldTextStyles[size]), children: placeholder })
|
|
2938
|
+
) : selectedOptions.map((o) => /* @__PURE__ */ jsx(Chip, { onRemove: () => remove(o.id), isDisabled, children: o.content ?? o.label }, o.id)),
|
|
2939
|
+
/* @__PURE__ */ jsx("span", { className: "ml-auto pr-1", children: /* @__PURE__ */ jsx(FieldCaret, {}) })
|
|
2940
|
+
]
|
|
2941
|
+
}
|
|
2942
|
+
),
|
|
2943
|
+
description ? /* @__PURE__ */ jsx("p", { className: fieldHintStyles, children: description }) : null,
|
|
2944
|
+
errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx("p", { className: fieldErrorStyles, children: errorMessage }) : null,
|
|
2945
|
+
/* @__PURE__ */ jsxs(
|
|
2946
|
+
Popover$1,
|
|
2947
|
+
{
|
|
2948
|
+
isOpen,
|
|
2949
|
+
onOpenChange: openPopover,
|
|
2950
|
+
triggerRef,
|
|
2951
|
+
offset: 6,
|
|
2952
|
+
placement: "bottom start",
|
|
2953
|
+
className: cn(
|
|
2954
|
+
"w-[var(--trigger-width)]",
|
|
2955
|
+
"rounded-md border border-rule bg-surface p-1",
|
|
2956
|
+
"shadow-[var(--shadow-raised)]",
|
|
2957
|
+
"entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
|
|
2958
|
+
"exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1"
|
|
2959
|
+
),
|
|
2960
|
+
children: [
|
|
2961
|
+
isSearchable ? /* @__PURE__ */ jsx("div", { className: "border-b border-rule-2 px-1 pb-1 mb-1", children: /* @__PURE__ */ jsx(
|
|
2962
|
+
"input",
|
|
2963
|
+
{
|
|
2964
|
+
autoFocus: true,
|
|
2965
|
+
value: search,
|
|
2966
|
+
onChange: (e) => updateSearch(e.target.value),
|
|
2967
|
+
placeholder: "Search\u2026",
|
|
2968
|
+
"aria-label": "Search options",
|
|
2969
|
+
className: cn(
|
|
2970
|
+
"w-full bg-transparent outline-none rounded-[6px] px-2 py-1.5",
|
|
2971
|
+
"font-sans text-[13px] text-ink placeholder:text-ink-4"
|
|
2972
|
+
)
|
|
2973
|
+
}
|
|
2974
|
+
) }) : null,
|
|
2975
|
+
/* @__PURE__ */ jsx(
|
|
2976
|
+
ListBox,
|
|
2977
|
+
{
|
|
2978
|
+
"aria-label": typeof label === "string" ? label : "Options",
|
|
2979
|
+
selectionMode: "multiple",
|
|
2980
|
+
selectedKeys: selectedSet,
|
|
2981
|
+
onSelectionChange: (keys) => {
|
|
2982
|
+
if (keys === "all") {
|
|
2983
|
+
onSelectionChange(new Set(options.map((o) => o.id)));
|
|
2984
|
+
return;
|
|
2985
|
+
}
|
|
2986
|
+
onSelectionChange(keys);
|
|
2987
|
+
},
|
|
2988
|
+
className: "outline-none max-h-72 overflow-auto",
|
|
2989
|
+
renderEmptyState: () => /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", fieldHintStyles), children: needle !== "" ? "No matches" : "All options selected" }),
|
|
2990
|
+
children: listboxOptions.map((option) => /* @__PURE__ */ jsxs(
|
|
2991
|
+
ListBoxItem,
|
|
2992
|
+
{
|
|
2993
|
+
id: option.id,
|
|
2994
|
+
textValue: option.label,
|
|
2995
|
+
...option.isDisabled ? { isDisabled: true } : {},
|
|
2996
|
+
className: cn(
|
|
2997
|
+
"group flex items-center gap-2.5",
|
|
2998
|
+
"rounded-[6px] px-3 py-2 text-[14px] text-ink-2",
|
|
2999
|
+
"cursor-pointer outline-none select-none",
|
|
3000
|
+
"data-[hovered]:bg-surface-2 data-[hovered]:text-ink",
|
|
3001
|
+
"data-[focused]:bg-surface-2 data-[focused]:text-ink",
|
|
3002
|
+
"data-[selected]:bg-surface-3 data-[selected]:text-ink",
|
|
3003
|
+
"data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
|
|
3004
|
+
),
|
|
3005
|
+
children: [
|
|
3006
|
+
/* @__PURE__ */ jsx(Checkmark, {}),
|
|
3007
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: option.content ?? option.label })
|
|
3008
|
+
]
|
|
3009
|
+
},
|
|
3010
|
+
option.id
|
|
3011
|
+
))
|
|
3012
|
+
}
|
|
3013
|
+
)
|
|
3014
|
+
]
|
|
3015
|
+
}
|
|
3016
|
+
)
|
|
3017
|
+
] });
|
|
3018
|
+
}
|
|
3019
|
+
function Chip({
|
|
3020
|
+
children,
|
|
3021
|
+
onRemove,
|
|
3022
|
+
isDisabled
|
|
3023
|
+
}) {
|
|
3024
|
+
return /* @__PURE__ */ jsxs(
|
|
3025
|
+
"span",
|
|
3026
|
+
{
|
|
3027
|
+
className: cn(
|
|
3028
|
+
"inline-flex items-center gap-1.5",
|
|
3029
|
+
"pl-2 pr-1 py-0.5 rounded-[6px]",
|
|
3030
|
+
"bg-surface-2 border border-rule",
|
|
3031
|
+
"font-sans text-[13px] text-ink"
|
|
3032
|
+
),
|
|
3033
|
+
children: [
|
|
3034
|
+
/* @__PURE__ */ jsx("span", { className: "inline-flex items-center", children }),
|
|
3035
|
+
/* @__PURE__ */ jsx(
|
|
3036
|
+
"button",
|
|
3037
|
+
{
|
|
3038
|
+
type: "button",
|
|
3039
|
+
"aria-label": "Remove",
|
|
3040
|
+
disabled: isDisabled,
|
|
3041
|
+
onClick: (e) => {
|
|
3042
|
+
e.stopPropagation();
|
|
3043
|
+
onRemove();
|
|
3044
|
+
},
|
|
3045
|
+
className: cn(
|
|
3046
|
+
"inline-flex items-center justify-center shrink-0",
|
|
3047
|
+
"size-4 rounded-[3px]",
|
|
3048
|
+
"text-ink-3 hover:text-ink hover:bg-surface-3 cursor-pointer",
|
|
3049
|
+
"outline-none focus-visible:shadow-[var(--shadow-focus)]",
|
|
3050
|
+
"disabled:opacity-45 disabled:cursor-not-allowed"
|
|
3051
|
+
),
|
|
3052
|
+
children: /* @__PURE__ */ jsx("svg", { "aria-hidden": true, viewBox: "0 0 10 10", className: "size-2.5", fill: "none", children: /* @__PURE__ */ jsx(
|
|
3053
|
+
"path",
|
|
3054
|
+
{
|
|
3055
|
+
d: "M2 2l6 6M8 2l-6 6",
|
|
3056
|
+
stroke: "currentColor",
|
|
3057
|
+
strokeWidth: "1.4",
|
|
3058
|
+
strokeLinecap: "round"
|
|
3059
|
+
}
|
|
3060
|
+
) })
|
|
3061
|
+
}
|
|
3062
|
+
)
|
|
3063
|
+
]
|
|
3064
|
+
}
|
|
3065
|
+
);
|
|
3066
|
+
}
|
|
3067
|
+
function Checkmark() {
|
|
3068
|
+
return /* @__PURE__ */ jsx(
|
|
3069
|
+
"span",
|
|
3070
|
+
{
|
|
3071
|
+
"aria-hidden": true,
|
|
3072
|
+
className: cn(
|
|
3073
|
+
"relative inline-flex items-center justify-center shrink-0",
|
|
3074
|
+
"size-[16px] rounded-[3px] border border-ink-4 bg-surface",
|
|
3075
|
+
"transition-[background-color,border-color] duration-150",
|
|
3076
|
+
"group-data-[selected]:bg-ink group-data-[selected]:border-ink"
|
|
3077
|
+
),
|
|
3078
|
+
children: /* @__PURE__ */ jsx(
|
|
3079
|
+
"svg",
|
|
3080
|
+
{
|
|
3081
|
+
viewBox: "0 0 12 12",
|
|
3082
|
+
className: cn(
|
|
3083
|
+
"size-3 text-surface transition-opacity duration-150",
|
|
3084
|
+
"opacity-0 group-data-[selected]:opacity-100"
|
|
3085
|
+
),
|
|
3086
|
+
"aria-hidden": true,
|
|
3087
|
+
children: /* @__PURE__ */ jsx(
|
|
3088
|
+
"path",
|
|
3089
|
+
{
|
|
3090
|
+
d: "M2.5 6.25 5 8.75 9.5 3.75",
|
|
3091
|
+
stroke: "currentColor",
|
|
3092
|
+
strokeWidth: "1.6",
|
|
3093
|
+
strokeLinecap: "round",
|
|
3094
|
+
strokeLinejoin: "round",
|
|
3095
|
+
fill: "none"
|
|
3096
|
+
}
|
|
3097
|
+
)
|
|
3098
|
+
}
|
|
3099
|
+
)
|
|
3100
|
+
}
|
|
3101
|
+
);
|
|
3102
|
+
}
|
|
3103
|
+
var SEARCH_DEBOUNCE_MS = 250;
|
|
3104
|
+
function RecipientSelect({
|
|
3105
|
+
connectionId,
|
|
3106
|
+
searchRecipients,
|
|
3107
|
+
value,
|
|
3108
|
+
onChange,
|
|
3109
|
+
placeholder
|
|
3110
|
+
}) {
|
|
3111
|
+
const [search, setSearch] = useState("");
|
|
3112
|
+
const [debouncedSearch, setDebouncedSearch] = useState("");
|
|
3113
|
+
useEffect(() => {
|
|
3114
|
+
const handle = window.setTimeout(() => setDebouncedSearch(search.trim()), SEARCH_DEBOUNCE_MS);
|
|
3115
|
+
return () => window.clearTimeout(handle);
|
|
3116
|
+
}, [search]);
|
|
3117
|
+
const [addresses, setAddresses] = useState([]);
|
|
3118
|
+
const requestIdRef = useRef(0);
|
|
3119
|
+
useEffect(() => {
|
|
3120
|
+
if (connectionId === "") return;
|
|
3121
|
+
const requestId = ++requestIdRef.current;
|
|
3122
|
+
searchRecipients(connectionId, debouncedSearch).then((results) => {
|
|
3123
|
+
if (requestId === requestIdRef.current) setAddresses(results);
|
|
3124
|
+
}).catch(() => {
|
|
3125
|
+
});
|
|
3126
|
+
}, [connectionId, debouncedSearch, searchRecipients]);
|
|
3127
|
+
const options = useMemo(() => {
|
|
3128
|
+
const map = /* @__PURE__ */ new Map();
|
|
3129
|
+
for (const recipient of value) {
|
|
3130
|
+
map.set(recipient.external_id, { id: recipient.external_id, label: recipient.external_id });
|
|
3131
|
+
}
|
|
3132
|
+
for (const address of addresses) {
|
|
3133
|
+
const identifier = address.raw_value || address.value;
|
|
3134
|
+
map.set(identifier, {
|
|
3135
|
+
id: identifier,
|
|
3136
|
+
label: address.name || identifier
|
|
3137
|
+
});
|
|
3138
|
+
}
|
|
3139
|
+
return Array.from(map.values());
|
|
3140
|
+
}, [addresses, value]);
|
|
3141
|
+
return /* @__PURE__ */ jsx(
|
|
3142
|
+
MultiSelect,
|
|
3143
|
+
{
|
|
3144
|
+
size: "sm",
|
|
3145
|
+
isSearchable: true,
|
|
3146
|
+
onSearchChange: setSearch,
|
|
3147
|
+
...placeholder !== void 0 ? { placeholder } : {},
|
|
3148
|
+
options,
|
|
3149
|
+
selectedKeys: value.map((recipient) => recipient.external_id),
|
|
3150
|
+
onSelectionChange: (keys) => onChange(
|
|
3151
|
+
Array.from(keys).map((id) => ({
|
|
3152
|
+
kind: "contact-address",
|
|
3153
|
+
external_id: String(id)
|
|
3154
|
+
}))
|
|
3155
|
+
)
|
|
3156
|
+
}
|
|
3157
|
+
);
|
|
3158
|
+
}
|
|
3159
|
+
function Select({
|
|
3160
|
+
label,
|
|
3161
|
+
description,
|
|
3162
|
+
errorMessage,
|
|
3163
|
+
placeholder = "Select\u2026",
|
|
3164
|
+
options,
|
|
3165
|
+
renderItem,
|
|
3166
|
+
size,
|
|
3167
|
+
appearance,
|
|
3168
|
+
triggerClassName,
|
|
3169
|
+
popoverClassName,
|
|
3170
|
+
className,
|
|
3171
|
+
...props
|
|
3172
|
+
}) {
|
|
3173
|
+
const styles2 = fieldControlStyles({ size, appearance });
|
|
3174
|
+
return /* @__PURE__ */ jsxs(
|
|
3175
|
+
Select$1,
|
|
3176
|
+
{
|
|
3177
|
+
...props,
|
|
3178
|
+
className: (renderProps) => cn(
|
|
3179
|
+
"grid grid-cols-1 gap-1.5",
|
|
3180
|
+
typeof className === "function" ? className(renderProps) : className
|
|
3181
|
+
),
|
|
3182
|
+
children: [
|
|
3183
|
+
label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
|
|
3184
|
+
/* @__PURE__ */ jsxs(
|
|
3185
|
+
Button$1,
|
|
3186
|
+
{
|
|
3187
|
+
className: cn(
|
|
3188
|
+
styles2.box(),
|
|
3189
|
+
styles2.content(),
|
|
3190
|
+
"flex items-center justify-between gap-3 text-left cursor-pointer",
|
|
3191
|
+
triggerClassName
|
|
3192
|
+
),
|
|
3193
|
+
children: [
|
|
3194
|
+
/* @__PURE__ */ jsx(SelectValue, { className: "truncate", children: ({ defaultChildren, isPlaceholder }) => isPlaceholder ? /* @__PURE__ */ jsx("span", { className: "text-ink-4", children: placeholder }) : defaultChildren }),
|
|
3195
|
+
/* @__PURE__ */ jsx(FieldCaret, {})
|
|
3196
|
+
]
|
|
3197
|
+
}
|
|
3198
|
+
),
|
|
3199
|
+
description ? /* @__PURE__ */ jsx(Text, { slot: "description", className: fieldHintStyles, children: description }) : null,
|
|
3200
|
+
errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx(FieldError, { className: fieldErrorStyles, children: errorMessage }) : null,
|
|
3201
|
+
/* @__PURE__ */ jsxs(
|
|
3202
|
+
Popover$1,
|
|
3203
|
+
{
|
|
3204
|
+
offset: 6,
|
|
3205
|
+
className: cn(
|
|
3206
|
+
// Match the trigger width exactly (RAC exposes --trigger-width on
|
|
3207
|
+
// the Popover when anchored to a Select button).
|
|
3208
|
+
"w-[var(--trigger-width)]",
|
|
3209
|
+
// Float above modals + full-screen editors (e.g. agent-studio's
|
|
3210
|
+
// .as-editor z-1000 / a raised modal overlay), matching Menu's z-[1200].
|
|
3211
|
+
"z-[1300]",
|
|
3212
|
+
"rounded-md border border-rule bg-surface p-1",
|
|
3213
|
+
"shadow-[var(--shadow-raised)]",
|
|
3214
|
+
"entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
|
|
3215
|
+
"exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1",
|
|
3216
|
+
popoverClassName
|
|
3217
|
+
),
|
|
3218
|
+
children: [
|
|
3219
|
+
/* @__PURE__ */ jsx(ListBox, { className: "outline-none max-h-72 overflow-auto", children: options.map((option) => /* @__PURE__ */ jsx(
|
|
3220
|
+
SelectItem,
|
|
3221
|
+
{
|
|
3222
|
+
id: option.id,
|
|
3223
|
+
...option.textValue ? { textValue: option.textValue } : {},
|
|
3224
|
+
...option.isDisabled ? { isDisabled: true } : {},
|
|
3225
|
+
children: renderItem ? renderItem(option) : option.label
|
|
3226
|
+
},
|
|
3227
|
+
option.id
|
|
3228
|
+
)) }),
|
|
3229
|
+
options.length === 0 ? /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", fieldHintStyles), children: placeholder }) : null
|
|
3230
|
+
]
|
|
3231
|
+
}
|
|
3232
|
+
)
|
|
3233
|
+
]
|
|
3234
|
+
}
|
|
3235
|
+
);
|
|
3236
|
+
}
|
|
3237
|
+
function SelectItem(props) {
|
|
3238
|
+
return /* @__PURE__ */ jsx(
|
|
3239
|
+
ListBoxItem,
|
|
3240
|
+
{
|
|
3241
|
+
...props,
|
|
3242
|
+
className: cn(
|
|
3243
|
+
"flex items-center justify-between gap-3",
|
|
3244
|
+
"rounded-[6px] px-3 py-2 text-[14px] text-ink-2",
|
|
3245
|
+
"cursor-pointer outline-none select-none",
|
|
3246
|
+
"data-[hovered]:bg-surface-2 data-[hovered]:text-ink",
|
|
3247
|
+
"data-[focused]:bg-surface-2 data-[focused]:text-ink",
|
|
3248
|
+
"data-[selected]:bg-surface-3 data-[selected]:text-ink",
|
|
3249
|
+
"data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
|
|
3250
|
+
)
|
|
3251
|
+
}
|
|
3252
|
+
);
|
|
3253
|
+
}
|
|
3254
|
+
var MAX_COMPOSER_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
3255
|
+
var messageComposerStyles = tv({
|
|
3256
|
+
base: [
|
|
3257
|
+
"rounded-[10px] border border-rule bg-surface shadow-[var(--shadow-inset)]",
|
|
3258
|
+
"transition-[border-color,box-shadow] duration-150",
|
|
3259
|
+
"focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]"
|
|
3260
|
+
]
|
|
3261
|
+
});
|
|
3262
|
+
var ROW_LABEL = "w-7 shrink-0 font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4";
|
|
3263
|
+
function MessageComposer({
|
|
3264
|
+
mode,
|
|
3265
|
+
value,
|
|
3266
|
+
onValueChange,
|
|
3267
|
+
placeholder,
|
|
3268
|
+
subjectPrefill,
|
|
3269
|
+
isSubjectReadOnly,
|
|
3270
|
+
disabledReason,
|
|
3271
|
+
onSend,
|
|
3272
|
+
connectionId,
|
|
3273
|
+
connectionPicker,
|
|
3274
|
+
showToField,
|
|
3275
|
+
showRecipientFields,
|
|
3276
|
+
searchRecipients,
|
|
3277
|
+
to: toProp,
|
|
3278
|
+
cc: ccProp,
|
|
3279
|
+
bcc: bccProp,
|
|
3280
|
+
onToChange,
|
|
3281
|
+
onCcChange,
|
|
3282
|
+
onBccChange,
|
|
3283
|
+
attachments,
|
|
3284
|
+
onAddFiles,
|
|
3285
|
+
onRemoveAttachment,
|
|
3286
|
+
hasUploadingAttachments,
|
|
3287
|
+
showSendButton,
|
|
3288
|
+
className
|
|
3289
|
+
}) {
|
|
3290
|
+
const [isSending, setIsSending] = useState(false);
|
|
3291
|
+
const [sendError, setSendError] = useState(null);
|
|
3292
|
+
const [internalTo, setInternalTo] = useState([]);
|
|
3293
|
+
const [internalCc, setInternalCc] = useState([]);
|
|
3294
|
+
const [internalBcc, setInternalBcc] = useState([]);
|
|
3295
|
+
const [showCcBcc, setShowCcBcc] = useState(false);
|
|
3296
|
+
const textareaRef = useRef(null);
|
|
3297
|
+
const to = toProp ?? internalTo;
|
|
3298
|
+
const cc = ccProp ?? internalCc;
|
|
3299
|
+
const bcc = bccProp ?? internalBcc;
|
|
3300
|
+
const setTo = (next) => {
|
|
3301
|
+
if (toProp === void 0) setInternalTo(next);
|
|
3302
|
+
onToChange?.(next);
|
|
3303
|
+
};
|
|
3304
|
+
const setCc = (next) => {
|
|
3305
|
+
if (ccProp === void 0) setInternalCc(next);
|
|
3306
|
+
onCcChange?.(next);
|
|
3307
|
+
};
|
|
3308
|
+
const setBcc = (next) => {
|
|
3309
|
+
if (bccProp === void 0) setInternalBcc(next);
|
|
3310
|
+
onBccChange?.(next);
|
|
3311
|
+
};
|
|
3312
|
+
const directory = searchRecipients !== void 0 && connectionId ? { searchRecipients, connectionId } : void 0;
|
|
3313
|
+
const canPickRecipients = mode === "email" && (showRecipientFields ?? false) && directory !== void 0;
|
|
3314
|
+
const needsTo = (showToField ?? false) && directory !== void 0;
|
|
3315
|
+
const isSendButtonShown = showSendButton ?? true;
|
|
3316
|
+
useEffect(() => {
|
|
3317
|
+
setInternalTo([]);
|
|
3318
|
+
setInternalCc([]);
|
|
3319
|
+
setInternalBcc([]);
|
|
3320
|
+
}, [connectionId]);
|
|
3321
|
+
const subject = value.subject ?? subjectPrefill ?? "";
|
|
3322
|
+
const readyCount = (attachments ?? []).filter(
|
|
3323
|
+
(attachment) => attachment.status === "ready"
|
|
3324
|
+
).length;
|
|
3325
|
+
const canSend = (value.text.trim() !== "" || readyCount > 0) && (mode !== "email" || subject.trim() !== "") && (!needsTo || to.length > 0) && !(hasUploadingAttachments ?? false);
|
|
3326
|
+
const submit = useCallback(async () => {
|
|
3327
|
+
if (!canSend || isSending) return;
|
|
3328
|
+
setIsSending(true);
|
|
3329
|
+
setSendError(null);
|
|
3330
|
+
try {
|
|
3331
|
+
await onSend({
|
|
3332
|
+
text: value.text.trim(),
|
|
3333
|
+
...mode === "email" ? { subject: subject.trim() } : {},
|
|
3334
|
+
...needsTo ? { to } : {},
|
|
3335
|
+
...cc.length > 0 ? { cc } : {},
|
|
3336
|
+
...bcc.length > 0 ? { bcc } : {}
|
|
3337
|
+
});
|
|
3338
|
+
onValueChange({ text: "" });
|
|
3339
|
+
setInternalTo([]);
|
|
3340
|
+
setInternalCc([]);
|
|
3341
|
+
setInternalBcc([]);
|
|
3342
|
+
} catch (error) {
|
|
3343
|
+
setSendError(error instanceof Error ? error.message : "Send failed");
|
|
3344
|
+
} finally {
|
|
3345
|
+
setIsSending(false);
|
|
3346
|
+
}
|
|
3347
|
+
}, [canSend, isSending, onSend, value.text, mode, subject, needsTo, to, cc, bcc, onValueChange]);
|
|
3348
|
+
if (disabledReason) {
|
|
3349
|
+
return /* @__PURE__ */ jsx("div", { className: "px-[18px] py-3", children: /* @__PURE__ */ jsx(
|
|
3350
|
+
"p",
|
|
3351
|
+
{
|
|
3352
|
+
className: cn(
|
|
3353
|
+
"rounded-[10px] border border-rule bg-surface-2 px-3.5 py-2.5",
|
|
3354
|
+
"text-[12.5px] text-ink-3 text-center"
|
|
3355
|
+
),
|
|
3356
|
+
children: disabledReason
|
|
3357
|
+
}
|
|
3358
|
+
) });
|
|
3359
|
+
}
|
|
3360
|
+
const onKeyDown2 = (event) => {
|
|
3361
|
+
if (!isSendButtonShown) return;
|
|
3362
|
+
const isSendCombo = mode === "email" ? event.key === "Enter" && (event.metaKey || event.ctrlKey) : event.key === "Enter" && !event.shiftKey;
|
|
3363
|
+
if (isSendCombo) {
|
|
3364
|
+
event.preventDefault();
|
|
3365
|
+
void submit();
|
|
3366
|
+
}
|
|
3367
|
+
};
|
|
3368
|
+
const ccBccReveal = canPickRecipients && !showCcBcc ? /* @__PURE__ */ jsx(
|
|
3369
|
+
Button$1,
|
|
3370
|
+
{
|
|
3371
|
+
onPress: () => setShowCcBcc(true),
|
|
3372
|
+
className: "shrink-0 font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4 cursor-pointer outline-none hover:text-ink-3 focus-visible:shadow-[var(--shadow-focus)] rounded",
|
|
3373
|
+
children: "Cc/Bcc"
|
|
3374
|
+
}
|
|
3375
|
+
) : null;
|
|
3376
|
+
return /* @__PURE__ */ jsxs("div", { className: "px-[18px] pb-3.5 pt-1", children: [
|
|
3377
|
+
/* @__PURE__ */ jsxs("div", { className: cn(messageComposerStyles(), className), children: [
|
|
3378
|
+
connectionPicker ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
|
|
3379
|
+
/* @__PURE__ */ jsx("span", { className: ROW_LABEL, children: "Via" }),
|
|
3380
|
+
/* @__PURE__ */ jsx(
|
|
3381
|
+
Select,
|
|
3382
|
+
{
|
|
3383
|
+
"aria-label": "Connection",
|
|
3384
|
+
size: "sm",
|
|
3385
|
+
options: connectionPicker.options.map((option) => ({
|
|
3386
|
+
id: option.id,
|
|
3387
|
+
label: option.label,
|
|
3388
|
+
textValue: option.textValue
|
|
3389
|
+
})),
|
|
3390
|
+
selectedKey: connectionPicker.selectedId,
|
|
3391
|
+
onSelectionChange: (key) => connectionPicker.onSelect(String(key)),
|
|
3392
|
+
triggerClassName: "font-mono text-[11px]",
|
|
3393
|
+
popoverClassName: "min-w-[var(--trigger-width)] w-max max-w-80"
|
|
3394
|
+
}
|
|
3395
|
+
)
|
|
3396
|
+
] }) : null,
|
|
3397
|
+
mode === "email" ? isSubjectReadOnly ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
|
|
3398
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: "Subject" }),
|
|
3399
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0 truncate text-[13px] text-ink", children: subject }),
|
|
3400
|
+
ccBccReveal
|
|
3401
|
+
] }) : /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
|
|
3402
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: "Subject" }),
|
|
3403
|
+
/* @__PURE__ */ jsx(
|
|
3404
|
+
"input",
|
|
3405
|
+
{
|
|
3406
|
+
value: subject,
|
|
3407
|
+
onChange: (event) => onValueChange({ ...value, subject: event.target.value }),
|
|
3408
|
+
placeholder: "Subject",
|
|
3409
|
+
className: "flex-1 bg-transparent outline-none text-[13px] text-ink placeholder:text-ink-4"
|
|
3410
|
+
}
|
|
3411
|
+
),
|
|
3412
|
+
ccBccReveal
|
|
3413
|
+
] }) : null,
|
|
3414
|
+
needsTo && directory ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
|
|
3415
|
+
/* @__PURE__ */ jsx("span", { className: ROW_LABEL, children: "To" }),
|
|
3416
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(
|
|
3417
|
+
RecipientSelect,
|
|
3418
|
+
{
|
|
3419
|
+
connectionId: directory.connectionId,
|
|
3420
|
+
searchRecipients: directory.searchRecipients,
|
|
3421
|
+
placeholder: "Pick recipients\u2026",
|
|
3422
|
+
value: to,
|
|
3423
|
+
onChange: setTo
|
|
3424
|
+
}
|
|
3425
|
+
) })
|
|
3426
|
+
] }) : null,
|
|
3427
|
+
canPickRecipients && directory && showCcBcc ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3428
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
|
|
3429
|
+
/* @__PURE__ */ jsx("span", { className: ROW_LABEL, children: "Cc" }),
|
|
3430
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(
|
|
3431
|
+
RecipientSelect,
|
|
3432
|
+
{
|
|
3433
|
+
connectionId: directory.connectionId,
|
|
3434
|
+
searchRecipients: directory.searchRecipients,
|
|
3435
|
+
placeholder: "Add cc\u2026",
|
|
3436
|
+
value: cc,
|
|
3437
|
+
onChange: setCc
|
|
3438
|
+
}
|
|
3439
|
+
) })
|
|
3440
|
+
] }),
|
|
3441
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b border-rule-2 px-3.5 py-1.5", children: [
|
|
3442
|
+
/* @__PURE__ */ jsx("span", { className: ROW_LABEL, children: "Bcc" }),
|
|
3443
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(
|
|
3444
|
+
RecipientSelect,
|
|
3445
|
+
{
|
|
3446
|
+
connectionId: directory.connectionId,
|
|
3447
|
+
searchRecipients: directory.searchRecipients,
|
|
3448
|
+
placeholder: "Add bcc\u2026",
|
|
3449
|
+
value: bcc,
|
|
3450
|
+
onChange: setBcc
|
|
3451
|
+
}
|
|
3452
|
+
) })
|
|
3453
|
+
] })
|
|
3454
|
+
] }) : null,
|
|
3455
|
+
attachments && attachments.length > 0 ? (
|
|
3456
|
+
// Single horizontally-scrollable row so the chips never grow the composer tall.
|
|
3457
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1.5 overflow-x-auto px-3.5 pt-2 pb-0.5", children: attachments.map((attachment) => /* @__PURE__ */ jsx(
|
|
3458
|
+
AttachmentChip,
|
|
3459
|
+
{
|
|
3460
|
+
name: attachment.name,
|
|
3461
|
+
status: attachment.status,
|
|
3462
|
+
...attachment.sizeInBytes !== void 0 ? { sizeInBytes: attachment.sizeInBytes } : {},
|
|
3463
|
+
...attachment.contentType ? { contentType: attachment.contentType } : {},
|
|
3464
|
+
...attachment.errorMessage ? { errorMessage: attachment.errorMessage } : {},
|
|
3465
|
+
...onRemoveAttachment ? { onRemove: () => onRemoveAttachment(attachment.localId) } : {},
|
|
3466
|
+
className: "shrink-0"
|
|
3467
|
+
},
|
|
3468
|
+
attachment.localId
|
|
3469
|
+
)) })
|
|
3470
|
+
) : null,
|
|
3471
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 px-3.5 py-2", children: [
|
|
3472
|
+
/* @__PURE__ */ jsx(
|
|
3473
|
+
"textarea",
|
|
3474
|
+
{
|
|
3475
|
+
ref: textareaRef,
|
|
3476
|
+
value: value.text,
|
|
3477
|
+
placeholder,
|
|
3478
|
+
rows: 1,
|
|
3479
|
+
onChange: (event) => {
|
|
3480
|
+
onValueChange({ ...value, text: event.target.value });
|
|
3481
|
+
autoGrow(event.currentTarget);
|
|
3482
|
+
},
|
|
3483
|
+
onKeyDown: onKeyDown2,
|
|
3484
|
+
className: cn(
|
|
3485
|
+
"w-full resize-none bg-transparent outline-none",
|
|
3486
|
+
"min-h-[40px] max-h-48 text-[14px] leading-[1.5] text-ink placeholder:text-ink-4"
|
|
3487
|
+
),
|
|
3488
|
+
"data-conversations-composer": true
|
|
3489
|
+
}
|
|
3490
|
+
),
|
|
3491
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
3492
|
+
onAddFiles ? /* @__PURE__ */ jsx(
|
|
3493
|
+
FileTrigger,
|
|
3494
|
+
{
|
|
3495
|
+
allowsMultiple: true,
|
|
3496
|
+
onSelect: (files) => {
|
|
3497
|
+
if (files) onAddFiles(Array.from(files));
|
|
3498
|
+
},
|
|
3499
|
+
children: /* @__PURE__ */ jsx(
|
|
3500
|
+
Button,
|
|
3501
|
+
{
|
|
3502
|
+
variant: "subtle",
|
|
3503
|
+
size: "icon-md",
|
|
3504
|
+
"aria-label": "Attach files",
|
|
3505
|
+
className: "shrink-0",
|
|
3506
|
+
children: /* @__PURE__ */ jsx(Paperclip, { className: "size-4", "aria-hidden": true })
|
|
3507
|
+
}
|
|
3508
|
+
)
|
|
3509
|
+
}
|
|
3510
|
+
) : /* @__PURE__ */ jsx("span", {}),
|
|
3511
|
+
isSendButtonShown ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3512
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-ink-4 select-none", "aria-hidden": true, children: mode === "email" ? "\u2318\u21B5" : "\u21B5" }),
|
|
3513
|
+
/* @__PURE__ */ jsx(
|
|
3514
|
+
Button$1,
|
|
3515
|
+
{
|
|
3516
|
+
"aria-label": "Send",
|
|
3517
|
+
isDisabled: !canSend || isSending,
|
|
3518
|
+
onPress: () => void submit(),
|
|
3519
|
+
className: cn(
|
|
3520
|
+
"grid place-items-center size-8 rounded-[8px] cursor-pointer",
|
|
3521
|
+
"bg-accent text-tile-fg transition-opacity duration-150",
|
|
3522
|
+
"disabled:opacity-40 disabled:cursor-default",
|
|
3523
|
+
"outline-none focus-visible:shadow-[var(--shadow-focus)]"
|
|
3524
|
+
),
|
|
3525
|
+
children: /* @__PURE__ */ jsx(SendHorizontal, { className: "size-3.5" })
|
|
3526
|
+
}
|
|
3527
|
+
)
|
|
3528
|
+
] }) : null
|
|
3529
|
+
] })
|
|
3530
|
+
] })
|
|
3531
|
+
] }),
|
|
3532
|
+
sendError ? /* @__PURE__ */ jsxs("p", { className: "mt-1.5 font-mono text-[11px] text-danger", children: [
|
|
3533
|
+
sendError,
|
|
3534
|
+
" \u2014 your message is kept above.",
|
|
3535
|
+
" ",
|
|
3536
|
+
/* @__PURE__ */ jsx(
|
|
3537
|
+
Button$1,
|
|
3538
|
+
{
|
|
3539
|
+
onPress: () => void submit(),
|
|
3540
|
+
className: "inline underline cursor-pointer outline-none focus-visible:shadow-[var(--shadow-focus)]",
|
|
3541
|
+
children: "retry"
|
|
3542
|
+
}
|
|
3543
|
+
)
|
|
3544
|
+
] }) : null
|
|
3545
|
+
] });
|
|
3546
|
+
}
|
|
3547
|
+
function autoGrow(node) {
|
|
3548
|
+
node.style.height = "auto";
|
|
3549
|
+
node.style.height = `${Math.min(node.scrollHeight, 192)}px`;
|
|
3550
|
+
}
|
|
3551
|
+
var listenersByMessageId2 = /* @__PURE__ */ new Map();
|
|
3552
|
+
var lastOutcomeByMessageId = /* @__PURE__ */ new Map();
|
|
3553
|
+
function publishDraftMessageOutcome(messageId, outcome) {
|
|
3554
|
+
lastOutcomeByMessageId.set(messageId, outcome);
|
|
3555
|
+
for (const listener of listenersByMessageId2.get(messageId) ?? []) listener();
|
|
3556
|
+
}
|
|
3557
|
+
function subscribeDraftMessageOutcome(messageId, listener) {
|
|
3558
|
+
let listeners3 = listenersByMessageId2.get(messageId);
|
|
3559
|
+
if (!listeners3) {
|
|
3560
|
+
listeners3 = /* @__PURE__ */ new Set();
|
|
3561
|
+
listenersByMessageId2.set(messageId, listeners3);
|
|
3562
|
+
}
|
|
3563
|
+
listeners3.add(listener);
|
|
3564
|
+
return () => {
|
|
3565
|
+
listeners3.delete(listener);
|
|
3566
|
+
if (listeners3.size === 0) listenersByMessageId2.delete(messageId);
|
|
3567
|
+
};
|
|
3568
|
+
}
|
|
3569
|
+
function useDraftMessageOutcome(messageId) {
|
|
3570
|
+
return useSyncExternalStore(
|
|
3571
|
+
(listener) => subscribeDraftMessageOutcome(messageId, listener),
|
|
3572
|
+
() => lastOutcomeByMessageId.get(messageId) ?? null,
|
|
3573
|
+
() => null
|
|
3574
|
+
);
|
|
3575
|
+
}
|
|
3576
|
+
function __resetDraftMessageOutcomes() {
|
|
3577
|
+
listenersByMessageId2.clear();
|
|
3578
|
+
lastOutcomeByMessageId.clear();
|
|
3579
|
+
}
|
|
3580
|
+
var draftMessageComposerStyles = tv({
|
|
3581
|
+
slots: {
|
|
3582
|
+
root: "flex flex-col gap-1.5",
|
|
3583
|
+
header: "flex items-center gap-2 px-[18px] pt-2",
|
|
3584
|
+
badge: [
|
|
3585
|
+
"inline-flex items-center gap-1 rounded-pill border border-rule px-2 py-0.5",
|
|
3586
|
+
"font-mono text-[10px] uppercase tracking-[0.08em] text-ink-3 bg-surface-2"
|
|
3587
|
+
],
|
|
3588
|
+
sender: "text-[12.5px] text-ink-3 truncate",
|
|
3589
|
+
actions: "flex items-center justify-between gap-2 px-[18px] pb-3",
|
|
3590
|
+
terminal: [
|
|
3591
|
+
"mx-[18px] my-3 rounded-[10px] border border-rule bg-surface-2 px-3.5 py-2.5",
|
|
3592
|
+
"text-[12.5px] text-ink-3"
|
|
3593
|
+
],
|
|
3594
|
+
error: "px-[18px] font-mono text-[11px] text-danger"
|
|
3595
|
+
}
|
|
3596
|
+
});
|
|
3597
|
+
var styles = draftMessageComposerStyles();
|
|
3598
|
+
function sendRecipientsFromMessage(message, role) {
|
|
3599
|
+
return (message.recipients ?? []).filter((recipient) => recipient.role === role && !!recipient.external_id).map((recipient) => ({
|
|
3600
|
+
kind: "contact-address",
|
|
3601
|
+
external_id: recipient.external_id
|
|
3602
|
+
}));
|
|
3603
|
+
}
|
|
3604
|
+
function terminalKindFor(status) {
|
|
3605
|
+
switch (status) {
|
|
3606
|
+
case "draft":
|
|
3607
|
+
return null;
|
|
3608
|
+
case "pending":
|
|
3609
|
+
case "sent":
|
|
3610
|
+
return "sent";
|
|
3611
|
+
case "rejected":
|
|
3612
|
+
return "rejected";
|
|
3613
|
+
case "failed":
|
|
3614
|
+
return "failed";
|
|
3615
|
+
default:
|
|
3616
|
+
return "not-draft";
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
function DraftMessageComposer({
|
|
3620
|
+
messageId,
|
|
3621
|
+
client,
|
|
3622
|
+
onSent,
|
|
3623
|
+
onRejected,
|
|
3624
|
+
showActions = true,
|
|
3625
|
+
className
|
|
3626
|
+
}) {
|
|
3627
|
+
const [phase, setPhase] = useState("loading");
|
|
3628
|
+
const [busy, setBusy] = useState("idle");
|
|
3629
|
+
const [terminalKind, setTerminalKind] = useState("not-draft");
|
|
3630
|
+
const [message, setMessage] = useState(null);
|
|
3631
|
+
const [conversation, setConversation] = useState(null);
|
|
3632
|
+
const [loadError, setLoadError] = useState(null);
|
|
3633
|
+
const [actionError, setActionError] = useState(null);
|
|
3634
|
+
const [isConfirmingReject, setIsConfirmingReject] = useState(false);
|
|
3635
|
+
const [value, setValue] = useState({ text: "" });
|
|
3636
|
+
const [to, setTo] = useState([]);
|
|
3637
|
+
const [cc, setCc] = useState([]);
|
|
3638
|
+
const [bcc, setBcc] = useState([]);
|
|
3639
|
+
const [snapshot, setSnapshot] = useState({
|
|
3640
|
+
text: "",
|
|
3641
|
+
subject: "",
|
|
3642
|
+
to: [],
|
|
3643
|
+
cc: [],
|
|
3644
|
+
bcc: []
|
|
3645
|
+
});
|
|
3646
|
+
const [loadNonce, setLoadNonce] = useState(0);
|
|
3647
|
+
const isEmail = message?.channel === "email";
|
|
3648
|
+
const isSubjectEditable = isEmail && conversation?.status === "draft";
|
|
3649
|
+
const areRecipientsEditable = isEmail && !conversation?.room_id;
|
|
3650
|
+
const seed = useCallback((draft, thread) => {
|
|
3651
|
+
const text = draft.content.find((block) => block.type === "text")?.text ?? "";
|
|
3652
|
+
const subject = thread?.subject ?? "";
|
|
3653
|
+
const seededTo = sendRecipientsFromMessage(draft, "to");
|
|
3654
|
+
const seededCc = sendRecipientsFromMessage(draft, "cc");
|
|
3655
|
+
const seededBcc = sendRecipientsFromMessage(draft, "bcc");
|
|
3656
|
+
setSnapshot({ text, subject, to: seededTo, cc: seededCc, bcc: seededBcc });
|
|
3657
|
+
setValue({ text, ...draft.channel === "email" ? { subject } : {} });
|
|
3658
|
+
setTo(seededTo);
|
|
3659
|
+
setCc(seededCc);
|
|
3660
|
+
setBcc(seededBcc);
|
|
3661
|
+
}, []);
|
|
3662
|
+
useEffect(() => {
|
|
3663
|
+
let isStale = false;
|
|
3664
|
+
setPhase("loading");
|
|
3665
|
+
setLoadError(null);
|
|
3666
|
+
setActionError(null);
|
|
3667
|
+
client.getMessage(messageId).then(async (draft) => {
|
|
3668
|
+
const thread = draft.conversation_id ? await client.getConversation(draft.conversation_id) : null;
|
|
3669
|
+
if (isStale) return;
|
|
3670
|
+
setMessage(draft);
|
|
3671
|
+
setConversation(thread);
|
|
3672
|
+
const terminal = terminalKindFor(draft.status);
|
|
3673
|
+
if (terminal) {
|
|
3674
|
+
setTerminalKind(terminal);
|
|
3675
|
+
setPhase("terminal");
|
|
3676
|
+
return;
|
|
3677
|
+
}
|
|
3678
|
+
seed(draft, thread);
|
|
3679
|
+
setPhase("ready");
|
|
3680
|
+
}).catch((error) => {
|
|
3681
|
+
if (isStale) return;
|
|
3682
|
+
setLoadError(error instanceof Error ? error.message : "Loading the draft failed");
|
|
3683
|
+
setPhase("load-error");
|
|
3684
|
+
});
|
|
3685
|
+
return () => {
|
|
3686
|
+
isStale = true;
|
|
3687
|
+
};
|
|
3688
|
+
}, [client, messageId, seed, loadNonce]);
|
|
3689
|
+
const isDirty = useMemo(() => {
|
|
3690
|
+
return value.text !== snapshot.text || isSubjectEditable && (value.subject ?? "") !== snapshot.subject || JSON.stringify(to) !== JSON.stringify(snapshot.to) || JSON.stringify(cc) !== JSON.stringify(snapshot.cc) || JSON.stringify(bcc) !== JSON.stringify(snapshot.bcc);
|
|
3691
|
+
}, [value, to, cc, bcc, snapshot, isSubjectEditable]);
|
|
3692
|
+
const buildPatch = useCallback(() => {
|
|
3693
|
+
const current = message;
|
|
3694
|
+
const isBodyEdited = value.text !== snapshot.text;
|
|
3695
|
+
const hasRecipientEdit = areRecipientsEditable && (JSON.stringify(to) !== JSON.stringify(snapshot.to) || JSON.stringify(cc) !== JSON.stringify(snapshot.cc) || JSON.stringify(bcc) !== JSON.stringify(snapshot.bcc));
|
|
3696
|
+
const attachmentRefs = (current.attachments ?? []).map((attachment) => attachment.file);
|
|
3697
|
+
return {
|
|
3698
|
+
// Body edits collapse to one text block; an untouched body echoes the
|
|
3699
|
+
// stored blocks so an agent-provided html part survives the edit.
|
|
3700
|
+
content: isBodyEdited ? [{ type: "text", text: value.text.trim() }] : current.content,
|
|
3701
|
+
...isSubjectEditable ? { subject: value.subject ?? "" } : {},
|
|
3702
|
+
// Presence-based replacement: any recipient edit ships ALL three groups
|
|
3703
|
+
// verbatim — [] is an explicit clear, an omitted group keeps the stored
|
|
3704
|
+
// one. Shipping only non-empty groups would silently resurrect removed
|
|
3705
|
+
// recipients at send time.
|
|
3706
|
+
...hasRecipientEdit ? { to, cc, bcc } : {},
|
|
3707
|
+
...attachmentRefs.length > 0 ? { attachments: attachmentRefs } : {}
|
|
3708
|
+
};
|
|
3709
|
+
}, [message, value, to, cc, bcc, snapshot, areRecipientsEditable, isSubjectEditable]);
|
|
3710
|
+
const persistDirtyEdits = useCallback(async () => {
|
|
3711
|
+
const current = message;
|
|
3712
|
+
if (!isDirty) return current;
|
|
3713
|
+
const updated = await client.updateDraft(messageId, buildPatch());
|
|
3714
|
+
setMessage(updated);
|
|
3715
|
+
setConversation(
|
|
3716
|
+
(thread) => thread && isSubjectEditable ? { ...thread, subject: value.subject ?? "" } : thread
|
|
3717
|
+
);
|
|
3718
|
+
setSnapshot({ text: value.text, subject: value.subject ?? "", to, cc, bcc });
|
|
3719
|
+
return updated;
|
|
3720
|
+
}, [message, isDirty, client, messageId, buildPatch, isSubjectEditable, value, to, cc, bcc]);
|
|
3721
|
+
const saveDraft = useCallback(async () => {
|
|
3722
|
+
if (phase !== "ready" || busy !== "idle") return;
|
|
3723
|
+
setBusy("saving");
|
|
3724
|
+
setActionError(null);
|
|
3725
|
+
try {
|
|
3726
|
+
await persistDirtyEdits();
|
|
3727
|
+
} catch (error) {
|
|
3728
|
+
setActionError(error instanceof Error ? error.message : "Saving the draft failed");
|
|
3729
|
+
} finally {
|
|
3730
|
+
setBusy("idle");
|
|
3731
|
+
}
|
|
3732
|
+
}, [phase, busy, persistDirtyEdits]);
|
|
3733
|
+
const send = useCallback(async () => {
|
|
3734
|
+
if (phase !== "ready" || busy !== "idle") return;
|
|
3735
|
+
if (areRecipientsEditable && conversation?.status === "draft" && to.length === 0) {
|
|
3736
|
+
setActionError("Add at least one recipient before sending");
|
|
3737
|
+
return;
|
|
3738
|
+
}
|
|
3739
|
+
setBusy("sending");
|
|
3740
|
+
setActionError(null);
|
|
3741
|
+
try {
|
|
3742
|
+
await persistDirtyEdits();
|
|
3743
|
+
const result = await client.sendDraft(messageId);
|
|
3744
|
+
setMessage(result);
|
|
3745
|
+
if (result.status === "failed") {
|
|
3746
|
+
setTerminalKind("failed");
|
|
3747
|
+
setPhase("terminal");
|
|
3748
|
+
return;
|
|
3749
|
+
}
|
|
3750
|
+
setTerminalKind("sent");
|
|
3751
|
+
setPhase("terminal");
|
|
3752
|
+
publishDraftMessageOutcome(messageId, { type: "sent", message: result });
|
|
3753
|
+
onSent?.(result);
|
|
3754
|
+
} catch (error) {
|
|
3755
|
+
setActionError(error instanceof Error ? error.message : "Sending the draft failed");
|
|
3756
|
+
throw error;
|
|
3757
|
+
} finally {
|
|
3758
|
+
setBusy("idle");
|
|
3759
|
+
}
|
|
3760
|
+
}, [
|
|
3761
|
+
phase,
|
|
3762
|
+
busy,
|
|
3763
|
+
persistDirtyEdits,
|
|
3764
|
+
client,
|
|
3765
|
+
messageId,
|
|
3766
|
+
onSent,
|
|
3767
|
+
areRecipientsEditable,
|
|
3768
|
+
conversation,
|
|
3769
|
+
to
|
|
3770
|
+
]);
|
|
3771
|
+
const reject = useCallback(
|
|
3772
|
+
async (isConfirmed) => {
|
|
3773
|
+
if (phase !== "ready" || busy !== "idle") return;
|
|
3774
|
+
if (!isConfirmed) {
|
|
3775
|
+
setIsConfirmingReject(true);
|
|
3776
|
+
return;
|
|
3777
|
+
}
|
|
3778
|
+
setBusy("rejecting");
|
|
3779
|
+
setActionError(null);
|
|
3780
|
+
try {
|
|
3781
|
+
const result = await client.rejectDraft(messageId);
|
|
3782
|
+
setMessage(result);
|
|
3783
|
+
setTerminalKind("rejected");
|
|
3784
|
+
setPhase("terminal");
|
|
3785
|
+
publishDraftMessageOutcome(messageId, { type: "rejected", message: result });
|
|
3786
|
+
onRejected?.(result);
|
|
3787
|
+
} catch (error) {
|
|
3788
|
+
setActionError(error instanceof Error ? error.message : "Rejecting the draft failed");
|
|
3789
|
+
} finally {
|
|
3790
|
+
setBusy("idle");
|
|
3791
|
+
setIsConfirmingReject(false);
|
|
3792
|
+
}
|
|
3793
|
+
},
|
|
3794
|
+
[phase, busy, client, messageId, onRejected]
|
|
3795
|
+
);
|
|
3796
|
+
const sendRef = useRef(send);
|
|
3797
|
+
const rejectRef = useRef(reject);
|
|
3798
|
+
const saveRef = useRef(saveDraft);
|
|
3799
|
+
sendRef.current = send;
|
|
3800
|
+
rejectRef.current = reject;
|
|
3801
|
+
saveRef.current = saveDraft;
|
|
3802
|
+
useEffect(() => {
|
|
3803
|
+
if (phase !== "ready") return;
|
|
3804
|
+
return registerDraftMessageActions(messageId, {
|
|
3805
|
+
// Failures surface via actionError/outcomes — external drivers get a
|
|
3806
|
+
// settled promise, not a rejection to remember to catch.
|
|
3807
|
+
send: () => sendRef.current().catch(() => void 0),
|
|
3808
|
+
// External UIs own their confirmation — bypass the inline confirm.
|
|
3809
|
+
reject: () => rejectRef.current(true),
|
|
3810
|
+
saveDraft: () => saveRef.current(),
|
|
3811
|
+
isBusy: busy !== "idle"
|
|
3812
|
+
});
|
|
3813
|
+
}, [messageId, phase, busy]);
|
|
3814
|
+
const attachments = useMemo(
|
|
3815
|
+
() => (message?.attachments ?? []).map((attachment) => ({
|
|
3816
|
+
localId: attachment.id,
|
|
3817
|
+
name: attachment.file.name || attachment.file.id,
|
|
3818
|
+
status: "ready",
|
|
3819
|
+
...attachment.file.content_type ? { contentType: attachment.file.content_type } : {}
|
|
3820
|
+
})),
|
|
3821
|
+
[message?.attachments]
|
|
3822
|
+
);
|
|
3823
|
+
if (phase === "loading") {
|
|
3824
|
+
return /* @__PURE__ */ jsx("div", { className: cn(styles.root(), className), children: /* @__PURE__ */ jsx("p", { className: styles.terminal(), children: "Loading draft\u2026" }) });
|
|
3825
|
+
}
|
|
3826
|
+
if (phase === "load-error") {
|
|
3827
|
+
return /* @__PURE__ */ jsx("div", { className: cn(styles.root(), className), children: /* @__PURE__ */ jsxs("p", { className: styles.terminal(), children: [
|
|
3828
|
+
loadError ?? "Loading the draft failed.",
|
|
3829
|
+
" ",
|
|
3830
|
+
/* @__PURE__ */ jsx(
|
|
3831
|
+
Button$1,
|
|
3832
|
+
{
|
|
3833
|
+
onPress: () => setLoadNonce((nonce) => nonce + 1),
|
|
3834
|
+
className: "inline underline cursor-pointer outline-none focus-visible:shadow-[var(--shadow-focus)]",
|
|
3835
|
+
children: "retry"
|
|
3836
|
+
}
|
|
3837
|
+
)
|
|
3838
|
+
] }) });
|
|
3839
|
+
}
|
|
3840
|
+
if (phase === "terminal" || !message) {
|
|
3841
|
+
const line = terminalKind === "sent" ? "This draft was sent." : terminalKind === "rejected" ? "This draft was rejected." : terminalKind === "failed" ? `Sending failed${message?.error ? ` \u2014 ${message.error}` : ""}.` : "This message is not a reviewable draft.";
|
|
3842
|
+
return /* @__PURE__ */ jsx("div", { className: cn(styles.root(), className), "data-draft-terminal": terminalKind, children: /* @__PURE__ */ jsx("p", { className: styles.terminal(), children: line }) });
|
|
3843
|
+
}
|
|
3844
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(styles.root(), className), children: [
|
|
3845
|
+
/* @__PURE__ */ jsxs("div", { className: styles.header(), children: [
|
|
3846
|
+
/* @__PURE__ */ jsx("span", { className: styles.badge(), children: "Draft \xB7 awaiting review" }),
|
|
3847
|
+
/* @__PURE__ */ jsxs("span", { className: styles.sender(), children: [
|
|
3848
|
+
"from ",
|
|
3849
|
+
message.sender.name
|
|
3850
|
+
] })
|
|
3851
|
+
] }),
|
|
3852
|
+
/* @__PURE__ */ jsx(
|
|
3853
|
+
MessageComposer,
|
|
3854
|
+
{
|
|
3855
|
+
mode: isEmail ? "email" : "chat",
|
|
3856
|
+
value,
|
|
3857
|
+
onValueChange: setValue,
|
|
3858
|
+
placeholder: "Draft body\u2026",
|
|
3859
|
+
...isEmail && !isSubjectEditable ? { isSubjectReadOnly: true } : {},
|
|
3860
|
+
onSend: async () => send(),
|
|
3861
|
+
...areRecipientsEditable && message.connection_id ? {
|
|
3862
|
+
connectionId: message.connection_id,
|
|
3863
|
+
searchRecipients: client.searchRecipients,
|
|
3864
|
+
showToField: true,
|
|
3865
|
+
showRecipientFields: true,
|
|
3866
|
+
to,
|
|
3867
|
+
cc,
|
|
3868
|
+
bcc,
|
|
3869
|
+
onToChange: setTo,
|
|
3870
|
+
onCcChange: setCc,
|
|
3871
|
+
onBccChange: setBcc
|
|
3872
|
+
} : {},
|
|
3873
|
+
...attachments.length > 0 ? { attachments } : {},
|
|
3874
|
+
showSendButton: showActions
|
|
3875
|
+
}
|
|
3876
|
+
),
|
|
3877
|
+
actionError ? /* @__PURE__ */ jsxs("p", { className: styles.error(), children: [
|
|
3878
|
+
actionError,
|
|
3879
|
+
" \u2014 your edits are kept."
|
|
3880
|
+
] }) : null,
|
|
3881
|
+
showActions ? /* @__PURE__ */ jsxs("div", { className: styles.actions(), children: [
|
|
3882
|
+
isConfirmingReject ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3883
|
+
/* @__PURE__ */ jsx("span", { className: "text-[12.5px] text-ink-3", children: "Reject draft?" }),
|
|
3884
|
+
/* @__PURE__ */ jsx(
|
|
3885
|
+
Button,
|
|
3886
|
+
{
|
|
3887
|
+
size: "sm",
|
|
3888
|
+
variant: "ghost",
|
|
3889
|
+
onPress: () => void reject(true),
|
|
3890
|
+
isDisabled: busy !== "idle",
|
|
3891
|
+
children: "Confirm reject"
|
|
3892
|
+
}
|
|
3893
|
+
),
|
|
3894
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "subtle", onPress: () => setIsConfirmingReject(false), children: "Cancel" })
|
|
3895
|
+
] }) : /* @__PURE__ */ jsx(
|
|
3896
|
+
Button,
|
|
3897
|
+
{
|
|
3898
|
+
size: "sm",
|
|
3899
|
+
variant: "ghost",
|
|
3900
|
+
onPress: () => void reject(false),
|
|
3901
|
+
isDisabled: busy !== "idle",
|
|
3902
|
+
children: "Reject"
|
|
3903
|
+
}
|
|
3904
|
+
),
|
|
3905
|
+
/* @__PURE__ */ jsx(
|
|
3906
|
+
Button,
|
|
3907
|
+
{
|
|
3908
|
+
size: "sm",
|
|
3909
|
+
variant: "subtle",
|
|
3910
|
+
onPress: () => void saveDraft(),
|
|
3911
|
+
isDisabled: busy !== "idle" || !isDirty,
|
|
3912
|
+
children: busy === "saving" ? "Saving\u2026" : "Save draft"
|
|
3913
|
+
}
|
|
3914
|
+
)
|
|
3915
|
+
] }) : null
|
|
3916
|
+
] });
|
|
3917
|
+
}
|
|
2807
3918
|
var emptyStateStyles = tv({
|
|
2808
3919
|
slots: {
|
|
2809
3920
|
root: "flex flex-col items-center text-center px-6",
|
|
@@ -2834,12 +3945,12 @@ function EmptyState({
|
|
|
2834
3945
|
size,
|
|
2835
3946
|
className
|
|
2836
3947
|
}) {
|
|
2837
|
-
const
|
|
2838
|
-
return /* @__PURE__ */ jsxs("div", { className: cn(
|
|
2839
|
-
icon ? /* @__PURE__ */ jsx("div", { className:
|
|
2840
|
-
/* @__PURE__ */ jsx("div", { className:
|
|
2841
|
-
description ? /* @__PURE__ */ jsx("div", { className:
|
|
2842
|
-
action ? /* @__PURE__ */ jsx("div", { className:
|
|
3948
|
+
const styles2 = emptyStateStyles({ size });
|
|
3949
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(styles2.root(), className), children: [
|
|
3950
|
+
icon ? /* @__PURE__ */ jsx("div", { className: styles2.art(), children: icon }) : null,
|
|
3951
|
+
/* @__PURE__ */ jsx("div", { className: styles2.title(), children: title }),
|
|
3952
|
+
description ? /* @__PURE__ */ jsx("div", { className: styles2.description(), children: description }) : null,
|
|
3953
|
+
action ? /* @__PURE__ */ jsx("div", { className: styles2.actions(), children: action }) : null
|
|
2843
3954
|
] });
|
|
2844
3955
|
}
|
|
2845
3956
|
function ErrorAnimation({ className }) {
|
|
@@ -3067,427 +4178,183 @@ function isPreviewableImageName(name) {
|
|
|
3067
4178
|
if (dot <= 0 || dot === name.length - 1) return false;
|
|
3068
4179
|
return PREVIEWABLE_EXTENSIONS.has(name.slice(dot + 1).toLowerCase());
|
|
3069
4180
|
}
|
|
3070
|
-
function Input({
|
|
3071
|
-
label,
|
|
3072
|
-
description,
|
|
3073
|
-
errorMessage,
|
|
3074
|
-
placeholder,
|
|
3075
|
-
size,
|
|
3076
|
-
appearance,
|
|
3077
|
-
inputProps,
|
|
3078
|
-
inputClassName,
|
|
3079
|
-
className,
|
|
3080
|
-
...props
|
|
3081
|
-
}) {
|
|
3082
|
-
const styles = fieldControlStyles({ size, appearance });
|
|
3083
|
-
return /* @__PURE__ */ jsxs(
|
|
3084
|
-
TextField,
|
|
3085
|
-
{
|
|
3086
|
-
...props,
|
|
3087
|
-
className: (renderProps) => cn(
|
|
3088
|
-
"grid grid-cols-1 gap-1.5",
|
|
3089
|
-
typeof className === "function" ? className(renderProps) : className
|
|
3090
|
-
),
|
|
3091
|
-
children: [
|
|
3092
|
-
label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
|
|
3093
|
-
/* @__PURE__ */ jsx(
|
|
3094
|
-
Input$1,
|
|
3095
|
-
{
|
|
3096
|
-
...inputProps,
|
|
3097
|
-
placeholder: placeholder ?? "",
|
|
3098
|
-
className: cn(styles.box(), styles.content(), inputClassName)
|
|
3099
|
-
}
|
|
3100
|
-
),
|
|
3101
|
-
description ? /* @__PURE__ */ jsx(Text, { slot: "description", className: fieldHintStyles, children: description }) : null,
|
|
3102
|
-
errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx(FieldError, { className: fieldErrorStyles, children: errorMessage }) : null
|
|
3103
|
-
]
|
|
3104
|
-
}
|
|
3105
|
-
);
|
|
3106
|
-
}
|
|
3107
|
-
var MenuTrigger = MenuTrigger$1;
|
|
3108
|
-
var SubmenuTrigger = SubmenuTrigger$1;
|
|
3109
|
-
function MenuPopover({ className, offset = 4, placement = "bottom start", ...props }) {
|
|
3110
|
-
return /* @__PURE__ */ jsx(
|
|
3111
|
-
Popover$1,
|
|
3112
|
-
{
|
|
3113
|
-
offset,
|
|
3114
|
-
placement,
|
|
3115
|
-
...props,
|
|
3116
|
-
className: (renderProps) => cn(
|
|
3117
|
-
"min-w-[200px] p-1 bg-surface border border-rule rounded-md",
|
|
3118
|
-
"shadow-[var(--shadow-float)] outline-none z-[1200]",
|
|
3119
|
-
"entering:animate-in entering:fade-in-0 entering:zoom-in-95",
|
|
3120
|
-
"exiting:animate-out exiting:fade-out-0 exiting:zoom-out-95",
|
|
3121
|
-
typeof className === "function" ? className(renderProps) : className
|
|
3122
|
-
)
|
|
3123
|
-
}
|
|
3124
|
-
);
|
|
3125
|
-
}
|
|
3126
|
-
function Menu({ className, ...props }) {
|
|
3127
|
-
return /* @__PURE__ */ jsx(
|
|
3128
|
-
Menu$1,
|
|
3129
|
-
{
|
|
3130
|
-
...props,
|
|
3131
|
-
className: (renderProps) => cn(
|
|
3132
|
-
"flex flex-col gap-px outline-none",
|
|
3133
|
-
typeof className === "function" ? className(renderProps) : className
|
|
3134
|
-
)
|
|
3135
|
-
}
|
|
3136
|
-
);
|
|
3137
|
-
}
|
|
3138
|
-
var menuItemStyles = cn(
|
|
3139
|
-
"flex items-center gap-2.5 px-2.5 py-1.5 rounded-md cursor-pointer outline-none",
|
|
3140
|
-
"font-sans text-[13px] text-ink-2 select-none",
|
|
3141
|
-
"hover:bg-surface-2 hover:text-ink focus:bg-surface-2 focus:text-ink",
|
|
3142
|
-
"data-[open]:bg-surface-2 data-[open]:text-ink",
|
|
3143
|
-
// submenu-parent stays lit while its flyout is open
|
|
3144
|
-
"aria-disabled:opacity-50 aria-disabled:cursor-default aria-disabled:hover:bg-transparent aria-disabled:hover:text-ink-2",
|
|
3145
|
-
"[&_svg]:size-3.5 [&_svg]:text-ink-3 hover:[&_svg]:text-ink focus:[&_svg]:text-ink"
|
|
3146
|
-
);
|
|
3147
|
-
function MenuItem({ icon, hasSubmenu, children, className, ...props }) {
|
|
3148
|
-
return /* @__PURE__ */ jsxs(
|
|
3149
|
-
MenuItem$1,
|
|
3150
|
-
{
|
|
3151
|
-
...props,
|
|
3152
|
-
className: (renderProps) => cn(menuItemStyles, typeof className === "function" ? className(renderProps) : className),
|
|
3153
|
-
children: [
|
|
3154
|
-
icon ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", children: icon }) : null,
|
|
3155
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children }),
|
|
3156
|
-
hasSubmenu ? /* @__PURE__ */ jsx(ChevronRight$1, { className: "ml-2 size-3 shrink-0 text-ink-4" }) : null
|
|
3157
|
-
]
|
|
3158
|
-
}
|
|
3159
|
-
);
|
|
3160
|
-
}
|
|
3161
|
-
function Modal(props) {
|
|
3162
|
-
return /* @__PURE__ */ jsx(DialogTrigger, { ...props });
|
|
3163
|
-
}
|
|
3164
|
-
function ModalContent({
|
|
3165
|
-
children,
|
|
4181
|
+
function Input({
|
|
4182
|
+
label,
|
|
4183
|
+
description,
|
|
4184
|
+
errorMessage,
|
|
4185
|
+
placeholder,
|
|
4186
|
+
size,
|
|
4187
|
+
appearance,
|
|
4188
|
+
inputProps,
|
|
4189
|
+
inputClassName,
|
|
3166
4190
|
className,
|
|
3167
|
-
modalClassName,
|
|
3168
|
-
isDismissable = true,
|
|
3169
4191
|
...props
|
|
3170
4192
|
}) {
|
|
3171
|
-
|
|
3172
|
-
|
|
4193
|
+
const styles2 = fieldControlStyles({ size, appearance });
|
|
4194
|
+
return /* @__PURE__ */ jsxs(
|
|
4195
|
+
TextField,
|
|
3173
4196
|
{
|
|
3174
|
-
isDismissable,
|
|
3175
4197
|
...props,
|
|
3176
4198
|
className: (renderProps) => cn(
|
|
3177
|
-
"
|
|
3178
|
-
// Warm ink scrim — stays in the Proteos palette (no pure black).
|
|
3179
|
-
"bg-[color-mix(in_oklab,var(--color-ink)_40%,transparent)]",
|
|
3180
|
-
"entering:animate-in entering:fade-in-0 entering:duration-150",
|
|
3181
|
-
"exiting:animate-out exiting:fade-out-0 exiting:duration-100",
|
|
4199
|
+
"grid grid-cols-1 gap-1.5",
|
|
3182
4200
|
typeof className === "function" ? className(renderProps) : className
|
|
3183
4201
|
),
|
|
3184
|
-
children:
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
modalClassName
|
|
3198
|
-
),
|
|
3199
|
-
children: /* @__PURE__ */ jsx(Dialog, { className: "outline-none flex flex-col min-h-0 overflow-hidden", children })
|
|
3200
|
-
}
|
|
3201
|
-
)
|
|
3202
|
-
}
|
|
3203
|
-
);
|
|
3204
|
-
}
|
|
3205
|
-
function ModalHeader({ className, ...props }) {
|
|
3206
|
-
return /* @__PURE__ */ jsx("div", { ...props, className: cn("shrink-0 px-5 pt-5 pb-3 flex flex-col gap-1.5", className) });
|
|
3207
|
-
}
|
|
3208
|
-
function ModalTitle({ className, ...props }) {
|
|
3209
|
-
return /* @__PURE__ */ jsx(
|
|
3210
|
-
Heading,
|
|
3211
|
-
{
|
|
3212
|
-
slot: "title",
|
|
3213
|
-
...props,
|
|
3214
|
-
className: cn(
|
|
3215
|
-
"font-sans font-medium text-[20px] leading-[1.25] tracking-[-0.015em] text-ink",
|
|
3216
|
-
className
|
|
3217
|
-
)
|
|
4202
|
+
children: [
|
|
4203
|
+
label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
|
|
4204
|
+
/* @__PURE__ */ jsx(
|
|
4205
|
+
Input$1,
|
|
4206
|
+
{
|
|
4207
|
+
...inputProps,
|
|
4208
|
+
placeholder: placeholder ?? "",
|
|
4209
|
+
className: cn(styles2.box(), styles2.content(), inputClassName)
|
|
4210
|
+
}
|
|
4211
|
+
),
|
|
4212
|
+
description ? /* @__PURE__ */ jsx(Text, { slot: "description", className: fieldHintStyles, children: description }) : null,
|
|
4213
|
+
errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx(FieldError, { className: fieldErrorStyles, children: errorMessage }) : null
|
|
4214
|
+
]
|
|
3218
4215
|
}
|
|
3219
4216
|
);
|
|
3220
4217
|
}
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
}
|
|
3224
|
-
function ModalBody({ className, ...props }) {
|
|
4218
|
+
var MenuTrigger = MenuTrigger$1;
|
|
4219
|
+
var SubmenuTrigger = SubmenuTrigger$1;
|
|
4220
|
+
function MenuPopover({ className, offset = 4, placement = "bottom start", ...props }) {
|
|
3225
4221
|
return /* @__PURE__ */ jsx(
|
|
3226
|
-
|
|
4222
|
+
Popover$1,
|
|
3227
4223
|
{
|
|
4224
|
+
offset,
|
|
4225
|
+
placement,
|
|
3228
4226
|
...props,
|
|
3229
|
-
className: cn(
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
"
|
|
3233
|
-
|
|
4227
|
+
className: (renderProps) => cn(
|
|
4228
|
+
"min-w-[200px] p-1 bg-surface border border-rule rounded-md",
|
|
4229
|
+
"shadow-[var(--shadow-float)] outline-none z-[1200]",
|
|
4230
|
+
"entering:animate-in entering:fade-in-0 entering:zoom-in-95",
|
|
4231
|
+
"exiting:animate-out exiting:fade-out-0 exiting:zoom-out-95",
|
|
4232
|
+
typeof className === "function" ? className(renderProps) : className
|
|
3234
4233
|
)
|
|
3235
4234
|
}
|
|
3236
4235
|
);
|
|
3237
4236
|
}
|
|
3238
|
-
function
|
|
4237
|
+
function Menu({ className, ...props }) {
|
|
3239
4238
|
return /* @__PURE__ */ jsx(
|
|
3240
|
-
|
|
3241
|
-
{
|
|
3242
|
-
...props,
|
|
3243
|
-
className:
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
description,
|
|
3250
|
-
errorMessage,
|
|
3251
|
-
placeholder = "Select\u2026",
|
|
3252
|
-
options,
|
|
3253
|
-
selectedKeys,
|
|
3254
|
-
onSelectionChange,
|
|
3255
|
-
isDisabled = false,
|
|
3256
|
-
isInvalid = false,
|
|
3257
|
-
hideSelectedOptions = false,
|
|
3258
|
-
isSearchable = false,
|
|
3259
|
-
onSearchChange,
|
|
3260
|
-
size = "md",
|
|
3261
|
-
appearance,
|
|
3262
|
-
className
|
|
3263
|
-
}) {
|
|
3264
|
-
const styles = fieldControlStyles({ size, invalid: isInvalid, appearance });
|
|
3265
|
-
const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
|
|
3266
|
-
const minHeight = appearance === "cell" ? "min-h-[29px]" : fieldMinHeight[size];
|
|
3267
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
3268
|
-
const [search, setSearch] = useState("");
|
|
3269
|
-
const triggerRef = useRef(null);
|
|
3270
|
-
const selectedSet = new Set(selectedKeys);
|
|
3271
|
-
const selectedOptions = options.filter((o) => selectedSet.has(o.id));
|
|
3272
|
-
const needle = search.trim().toLowerCase();
|
|
3273
|
-
const listboxOptions = (hideSelectedOptions ? options.filter((o) => !selectedSet.has(o.id)) : options).filter((o) => needle === "" || o.label.toLowerCase().includes(needle));
|
|
3274
|
-
const updateSearch = (next) => {
|
|
3275
|
-
setSearch(next);
|
|
3276
|
-
onSearchChange?.(next);
|
|
3277
|
-
};
|
|
3278
|
-
const openPopover = (open) => {
|
|
3279
|
-
setIsOpen(open);
|
|
3280
|
-
if (!open && search !== "") updateSearch("");
|
|
3281
|
-
};
|
|
3282
|
-
const remove = (id) => {
|
|
3283
|
-
const next = new Set(selectedSet);
|
|
3284
|
-
next.delete(id);
|
|
3285
|
-
onSelectionChange(next);
|
|
3286
|
-
};
|
|
3287
|
-
const onTriggerKeyDown = (e) => {
|
|
3288
|
-
if (isDisabled) return;
|
|
3289
|
-
if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
|
|
3290
|
-
e.preventDefault();
|
|
3291
|
-
openPopover(true);
|
|
3292
|
-
}
|
|
3293
|
-
};
|
|
3294
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-1.5", className), children: [
|
|
3295
|
-
label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
|
|
3296
|
-
/* @__PURE__ */ jsxs(
|
|
3297
|
-
"div",
|
|
3298
|
-
{
|
|
3299
|
-
ref: triggerRef,
|
|
3300
|
-
role: "combobox",
|
|
3301
|
-
"aria-expanded": isOpen,
|
|
3302
|
-
"aria-haspopup": "listbox",
|
|
3303
|
-
"aria-disabled": isDisabled || void 0,
|
|
3304
|
-
"aria-invalid": isInvalid || void 0,
|
|
3305
|
-
tabIndex: isDisabled ? -1 : 0,
|
|
3306
|
-
onClick: () => !isDisabled && openPopover(!isOpen),
|
|
3307
|
-
onKeyDown: onTriggerKeyDown,
|
|
3308
|
-
"data-invalid": isInvalid || void 0,
|
|
3309
|
-
"data-disabled": isDisabled || void 0,
|
|
3310
|
-
className: cn(
|
|
3311
|
-
styles.box(),
|
|
3312
|
-
"flex flex-wrap items-center gap-1.5 cursor-pointer",
|
|
3313
|
-
minHeight,
|
|
3314
|
-
fieldChipPadding[size],
|
|
3315
|
-
"pr-2",
|
|
3316
|
-
// Let focus-within light up the ring on both the trigger and any
|
|
3317
|
-
// chip's remove button without any extra JS.
|
|
3318
|
-
focusWithin
|
|
3319
|
-
),
|
|
3320
|
-
children: [
|
|
3321
|
-
selectedOptions.length === 0 ? (
|
|
3322
|
-
// No vertical padding — wrapper's p-1.5 + min-h-[size] govern the
|
|
3323
|
-
// overall height so it matches the Input/Select trigger at the same
|
|
3324
|
-
// size.
|
|
3325
|
-
/* @__PURE__ */ jsx("span", { className: cn("text-ink-4 font-sans px-1", fieldTextStyles[size]), children: placeholder })
|
|
3326
|
-
) : selectedOptions.map((o) => /* @__PURE__ */ jsx(Chip, { onRemove: () => remove(o.id), isDisabled, children: o.content ?? o.label }, o.id)),
|
|
3327
|
-
/* @__PURE__ */ jsx("span", { className: "ml-auto pr-1", children: /* @__PURE__ */ jsx(FieldCaret, {}) })
|
|
3328
|
-
]
|
|
3329
|
-
}
|
|
3330
|
-
),
|
|
3331
|
-
description ? /* @__PURE__ */ jsx("p", { className: fieldHintStyles, children: description }) : null,
|
|
3332
|
-
errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx("p", { className: fieldErrorStyles, children: errorMessage }) : null,
|
|
3333
|
-
/* @__PURE__ */ jsxs(
|
|
3334
|
-
Popover$1,
|
|
3335
|
-
{
|
|
3336
|
-
isOpen,
|
|
3337
|
-
onOpenChange: openPopover,
|
|
3338
|
-
triggerRef,
|
|
3339
|
-
offset: 6,
|
|
3340
|
-
placement: "bottom start",
|
|
3341
|
-
className: cn(
|
|
3342
|
-
"w-[var(--trigger-width)]",
|
|
3343
|
-
"rounded-md border border-rule bg-surface p-1",
|
|
3344
|
-
"shadow-[var(--shadow-raised)]",
|
|
3345
|
-
"entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
|
|
3346
|
-
"exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1"
|
|
3347
|
-
),
|
|
3348
|
-
children: [
|
|
3349
|
-
isSearchable ? /* @__PURE__ */ jsx("div", { className: "border-b border-rule-2 px-1 pb-1 mb-1", children: /* @__PURE__ */ jsx(
|
|
3350
|
-
"input",
|
|
3351
|
-
{
|
|
3352
|
-
autoFocus: true,
|
|
3353
|
-
value: search,
|
|
3354
|
-
onChange: (e) => updateSearch(e.target.value),
|
|
3355
|
-
placeholder: "Search\u2026",
|
|
3356
|
-
"aria-label": "Search options",
|
|
3357
|
-
className: cn(
|
|
3358
|
-
"w-full bg-transparent outline-none rounded-[6px] px-2 py-1.5",
|
|
3359
|
-
"font-sans text-[13px] text-ink placeholder:text-ink-4"
|
|
3360
|
-
)
|
|
3361
|
-
}
|
|
3362
|
-
) }) : null,
|
|
3363
|
-
/* @__PURE__ */ jsx(
|
|
3364
|
-
ListBox,
|
|
3365
|
-
{
|
|
3366
|
-
"aria-label": typeof label === "string" ? label : "Options",
|
|
3367
|
-
selectionMode: "multiple",
|
|
3368
|
-
selectedKeys: selectedSet,
|
|
3369
|
-
onSelectionChange: (keys) => {
|
|
3370
|
-
if (keys === "all") {
|
|
3371
|
-
onSelectionChange(new Set(options.map((o) => o.id)));
|
|
3372
|
-
return;
|
|
3373
|
-
}
|
|
3374
|
-
onSelectionChange(keys);
|
|
3375
|
-
},
|
|
3376
|
-
className: "outline-none max-h-72 overflow-auto",
|
|
3377
|
-
renderEmptyState: () => /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", fieldHintStyles), children: needle !== "" ? "No matches" : "All options selected" }),
|
|
3378
|
-
children: listboxOptions.map((option) => /* @__PURE__ */ jsxs(
|
|
3379
|
-
ListBoxItem,
|
|
3380
|
-
{
|
|
3381
|
-
id: option.id,
|
|
3382
|
-
textValue: option.label,
|
|
3383
|
-
...option.isDisabled ? { isDisabled: true } : {},
|
|
3384
|
-
className: cn(
|
|
3385
|
-
"group flex items-center gap-2.5",
|
|
3386
|
-
"rounded-[6px] px-3 py-2 text-[14px] text-ink-2",
|
|
3387
|
-
"cursor-pointer outline-none select-none",
|
|
3388
|
-
"data-[hovered]:bg-surface-2 data-[hovered]:text-ink",
|
|
3389
|
-
"data-[focused]:bg-surface-2 data-[focused]:text-ink",
|
|
3390
|
-
"data-[selected]:bg-surface-3 data-[selected]:text-ink",
|
|
3391
|
-
"data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
|
|
3392
|
-
),
|
|
3393
|
-
children: [
|
|
3394
|
-
/* @__PURE__ */ jsx(Checkmark, {}),
|
|
3395
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: option.content ?? option.label })
|
|
3396
|
-
]
|
|
3397
|
-
},
|
|
3398
|
-
option.id
|
|
3399
|
-
))
|
|
3400
|
-
}
|
|
3401
|
-
)
|
|
3402
|
-
]
|
|
3403
|
-
}
|
|
3404
|
-
)
|
|
3405
|
-
] });
|
|
4239
|
+
Menu$1,
|
|
4240
|
+
{
|
|
4241
|
+
...props,
|
|
4242
|
+
className: (renderProps) => cn(
|
|
4243
|
+
"flex flex-col gap-px outline-none",
|
|
4244
|
+
typeof className === "function" ? className(renderProps) : className
|
|
4245
|
+
)
|
|
4246
|
+
}
|
|
4247
|
+
);
|
|
3406
4248
|
}
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
4249
|
+
var menuItemStyles = cn(
|
|
4250
|
+
"flex items-center gap-2.5 px-2.5 py-1.5 rounded-md cursor-pointer outline-none",
|
|
4251
|
+
"font-sans text-[13px] text-ink-2 select-none",
|
|
4252
|
+
"hover:bg-surface-2 hover:text-ink focus:bg-surface-2 focus:text-ink",
|
|
4253
|
+
"data-[open]:bg-surface-2 data-[open]:text-ink",
|
|
4254
|
+
// submenu-parent stays lit while its flyout is open
|
|
4255
|
+
"aria-disabled:opacity-50 aria-disabled:cursor-default aria-disabled:hover:bg-transparent aria-disabled:hover:text-ink-2",
|
|
4256
|
+
"[&_svg]:size-3.5 [&_svg]:text-ink-3 hover:[&_svg]:text-ink focus:[&_svg]:text-ink"
|
|
4257
|
+
);
|
|
4258
|
+
function MenuItem({ icon, hasSubmenu, children, className, ...props }) {
|
|
3412
4259
|
return /* @__PURE__ */ jsxs(
|
|
3413
|
-
|
|
4260
|
+
MenuItem$1,
|
|
3414
4261
|
{
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
"pl-2 pr-1 py-0.5 rounded-[6px]",
|
|
3418
|
-
"bg-surface-2 border border-rule",
|
|
3419
|
-
"font-sans text-[13px] text-ink"
|
|
3420
|
-
),
|
|
4262
|
+
...props,
|
|
4263
|
+
className: (renderProps) => cn(menuItemStyles, typeof className === "function" ? className(renderProps) : className),
|
|
3421
4264
|
children: [
|
|
3422
|
-
/* @__PURE__ */ jsx("span", { className: "
|
|
3423
|
-
/* @__PURE__ */ jsx(
|
|
3424
|
-
|
|
3425
|
-
{
|
|
3426
|
-
type: "button",
|
|
3427
|
-
"aria-label": "Remove",
|
|
3428
|
-
disabled: isDisabled,
|
|
3429
|
-
onClick: (e) => {
|
|
3430
|
-
e.stopPropagation();
|
|
3431
|
-
onRemove();
|
|
3432
|
-
},
|
|
3433
|
-
className: cn(
|
|
3434
|
-
"inline-flex items-center justify-center shrink-0",
|
|
3435
|
-
"size-4 rounded-[3px]",
|
|
3436
|
-
"text-ink-3 hover:text-ink hover:bg-surface-3 cursor-pointer",
|
|
3437
|
-
"outline-none focus-visible:shadow-[var(--shadow-focus)]",
|
|
3438
|
-
"disabled:opacity-45 disabled:cursor-not-allowed"
|
|
3439
|
-
),
|
|
3440
|
-
children: /* @__PURE__ */ jsx("svg", { "aria-hidden": true, viewBox: "0 0 10 10", className: "size-2.5", fill: "none", children: /* @__PURE__ */ jsx(
|
|
3441
|
-
"path",
|
|
3442
|
-
{
|
|
3443
|
-
d: "M2 2l6 6M8 2l-6 6",
|
|
3444
|
-
stroke: "currentColor",
|
|
3445
|
-
strokeWidth: "1.4",
|
|
3446
|
-
strokeLinecap: "round"
|
|
3447
|
-
}
|
|
3448
|
-
) })
|
|
3449
|
-
}
|
|
3450
|
-
)
|
|
4265
|
+
icon ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", children: icon }) : null,
|
|
4266
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children }),
|
|
4267
|
+
hasSubmenu ? /* @__PURE__ */ jsx(ChevronRight$1, { className: "ml-2 size-3 shrink-0 text-ink-4" }) : null
|
|
3451
4268
|
]
|
|
3452
4269
|
}
|
|
3453
4270
|
);
|
|
3454
4271
|
}
|
|
3455
|
-
function
|
|
4272
|
+
function Modal(props) {
|
|
4273
|
+
return /* @__PURE__ */ jsx(DialogTrigger, { ...props });
|
|
4274
|
+
}
|
|
4275
|
+
function ModalContent({
|
|
4276
|
+
children,
|
|
4277
|
+
className,
|
|
4278
|
+
modalClassName,
|
|
4279
|
+
isDismissable = true,
|
|
4280
|
+
...props
|
|
4281
|
+
}) {
|
|
3456
4282
|
return /* @__PURE__ */ jsx(
|
|
3457
|
-
|
|
4283
|
+
ModalOverlay,
|
|
3458
4284
|
{
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
"
|
|
3463
|
-
|
|
3464
|
-
"
|
|
4285
|
+
isDismissable,
|
|
4286
|
+
...props,
|
|
4287
|
+
className: (renderProps) => cn(
|
|
4288
|
+
"fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
4289
|
+
// Warm ink scrim — stays in the Proteos palette (no pure black).
|
|
4290
|
+
"bg-[color-mix(in_oklab,var(--color-ink)_40%,transparent)]",
|
|
4291
|
+
"entering:animate-in entering:fade-in-0 entering:duration-150",
|
|
4292
|
+
"exiting:animate-out exiting:fade-out-0 exiting:duration-100",
|
|
4293
|
+
typeof className === "function" ? className(renderProps) : className
|
|
3465
4294
|
),
|
|
3466
4295
|
children: /* @__PURE__ */ jsx(
|
|
3467
|
-
|
|
4296
|
+
Modal$1,
|
|
3468
4297
|
{
|
|
3469
|
-
viewBox: "0 0 12 12",
|
|
3470
4298
|
className: cn(
|
|
3471
|
-
"
|
|
3472
|
-
|
|
4299
|
+
"w-full max-w-lg",
|
|
4300
|
+
// Cap the panel at the viewport (minus the overlay's p-4) and lay it
|
|
4301
|
+
// out as a column so a tall body scrolls instead of overflowing the
|
|
4302
|
+
// screen. Short modals are unaffected — height still tracks content.
|
|
4303
|
+
"flex flex-col max-h-[calc(100dvh-2rem)]",
|
|
4304
|
+
"bg-surface border border-rule rounded-lg",
|
|
4305
|
+
"shadow-[var(--shadow-float)]",
|
|
4306
|
+
"entering:animate-in entering:zoom-in-95 entering:slide-in-from-top-2 entering:duration-150",
|
|
4307
|
+
"exiting:animate-out exiting:zoom-out-95 exiting:duration-100",
|
|
4308
|
+
modalClassName
|
|
3473
4309
|
),
|
|
3474
|
-
"
|
|
3475
|
-
children: /* @__PURE__ */ jsx(
|
|
3476
|
-
"path",
|
|
3477
|
-
{
|
|
3478
|
-
d: "M2.5 6.25 5 8.75 9.5 3.75",
|
|
3479
|
-
stroke: "currentColor",
|
|
3480
|
-
strokeWidth: "1.6",
|
|
3481
|
-
strokeLinecap: "round",
|
|
3482
|
-
strokeLinejoin: "round",
|
|
3483
|
-
fill: "none"
|
|
3484
|
-
}
|
|
3485
|
-
)
|
|
4310
|
+
children: /* @__PURE__ */ jsx(Dialog, { className: "outline-none flex flex-col min-h-0 overflow-hidden", children })
|
|
3486
4311
|
}
|
|
3487
4312
|
)
|
|
3488
4313
|
}
|
|
3489
4314
|
);
|
|
3490
4315
|
}
|
|
4316
|
+
function ModalHeader({ className, ...props }) {
|
|
4317
|
+
return /* @__PURE__ */ jsx("div", { ...props, className: cn("shrink-0 px-5 pt-5 pb-3 flex flex-col gap-1.5", className) });
|
|
4318
|
+
}
|
|
4319
|
+
function ModalTitle({ className, ...props }) {
|
|
4320
|
+
return /* @__PURE__ */ jsx(
|
|
4321
|
+
Heading,
|
|
4322
|
+
{
|
|
4323
|
+
slot: "title",
|
|
4324
|
+
...props,
|
|
4325
|
+
className: cn(
|
|
4326
|
+
"font-sans font-medium text-[20px] leading-[1.25] tracking-[-0.015em] text-ink",
|
|
4327
|
+
className
|
|
4328
|
+
)
|
|
4329
|
+
}
|
|
4330
|
+
);
|
|
4331
|
+
}
|
|
4332
|
+
function ModalDescription({ className, ...props }) {
|
|
4333
|
+
return /* @__PURE__ */ jsx("p", { ...props, className: cn("text-[14px] leading-[1.55] text-ink-3", className) });
|
|
4334
|
+
}
|
|
4335
|
+
function ModalBody({ className, ...props }) {
|
|
4336
|
+
return /* @__PURE__ */ jsx(
|
|
4337
|
+
"div",
|
|
4338
|
+
{
|
|
4339
|
+
...props,
|
|
4340
|
+
className: cn(
|
|
4341
|
+
// The scroll region: caps at the panel's spare height and scrolls when
|
|
4342
|
+
// the content is taller, leaving header + footer pinned.
|
|
4343
|
+
"min-h-0 overflow-y-auto px-5 py-3 flex flex-col gap-3 text-[14px] text-ink-2",
|
|
4344
|
+
className
|
|
4345
|
+
)
|
|
4346
|
+
}
|
|
4347
|
+
);
|
|
4348
|
+
}
|
|
4349
|
+
function ModalFooter({ className, ...props }) {
|
|
4350
|
+
return /* @__PURE__ */ jsx(
|
|
4351
|
+
"div",
|
|
4352
|
+
{
|
|
4353
|
+
...props,
|
|
4354
|
+
className: cn("shrink-0 flex justify-end items-center gap-2", "px-5 pt-3 pb-5", className)
|
|
4355
|
+
}
|
|
4356
|
+
);
|
|
4357
|
+
}
|
|
3491
4358
|
var NavNestedContext = createContext(false);
|
|
3492
4359
|
function useIsNested() {
|
|
3493
4360
|
return useContext(NavNestedContext);
|
|
@@ -3974,7 +4841,7 @@ function SegmentedControl({
|
|
|
3974
4841
|
orientation = "horizontal",
|
|
3975
4842
|
...props
|
|
3976
4843
|
}) {
|
|
3977
|
-
const
|
|
4844
|
+
const styles2 = segmentedControlStyles({ size });
|
|
3978
4845
|
return /* @__PURE__ */ jsx(
|
|
3979
4846
|
RadioGroup$1,
|
|
3980
4847
|
{
|
|
@@ -3982,14 +4849,14 @@ function SegmentedControl({
|
|
|
3982
4849
|
orientation,
|
|
3983
4850
|
value: String(value),
|
|
3984
4851
|
onChange: (next) => onChange(next),
|
|
3985
|
-
className: cn(
|
|
4852
|
+
className: cn(styles2.group(), className),
|
|
3986
4853
|
children: options.map((option) => /* @__PURE__ */ jsx(
|
|
3987
4854
|
Radio$1,
|
|
3988
4855
|
{
|
|
3989
4856
|
value: String(option.id),
|
|
3990
4857
|
...option.isDisabled ? { isDisabled: true } : {},
|
|
3991
4858
|
...autoFocus && option.id === value ? { autoFocus: true } : {},
|
|
3992
|
-
className:
|
|
4859
|
+
className: styles2.segment(),
|
|
3993
4860
|
children: option.label
|
|
3994
4861
|
},
|
|
3995
4862
|
String(option.id)
|
|
@@ -3997,101 +4864,6 @@ function SegmentedControl({
|
|
|
3997
4864
|
}
|
|
3998
4865
|
);
|
|
3999
4866
|
}
|
|
4000
|
-
function Select({
|
|
4001
|
-
label,
|
|
4002
|
-
description,
|
|
4003
|
-
errorMessage,
|
|
4004
|
-
placeholder = "Select\u2026",
|
|
4005
|
-
options,
|
|
4006
|
-
renderItem,
|
|
4007
|
-
size,
|
|
4008
|
-
appearance,
|
|
4009
|
-
triggerClassName,
|
|
4010
|
-
popoverClassName,
|
|
4011
|
-
className,
|
|
4012
|
-
...props
|
|
4013
|
-
}) {
|
|
4014
|
-
const styles = fieldControlStyles({ size, appearance });
|
|
4015
|
-
return /* @__PURE__ */ jsxs(
|
|
4016
|
-
Select$1,
|
|
4017
|
-
{
|
|
4018
|
-
...props,
|
|
4019
|
-
className: (renderProps) => cn(
|
|
4020
|
-
"grid grid-cols-1 gap-1.5",
|
|
4021
|
-
typeof className === "function" ? className(renderProps) : className
|
|
4022
|
-
),
|
|
4023
|
-
children: [
|
|
4024
|
-
label ? /* @__PURE__ */ jsx(Label, { className: fieldLabelStyles, children: label }) : null,
|
|
4025
|
-
/* @__PURE__ */ jsxs(
|
|
4026
|
-
Button$1,
|
|
4027
|
-
{
|
|
4028
|
-
className: cn(
|
|
4029
|
-
styles.box(),
|
|
4030
|
-
styles.content(),
|
|
4031
|
-
"flex items-center justify-between gap-3 text-left cursor-pointer",
|
|
4032
|
-
triggerClassName
|
|
4033
|
-
),
|
|
4034
|
-
children: [
|
|
4035
|
-
/* @__PURE__ */ jsx(SelectValue, { className: "truncate", children: ({ defaultChildren, isPlaceholder }) => isPlaceholder ? /* @__PURE__ */ jsx("span", { className: "text-ink-4", children: placeholder }) : defaultChildren }),
|
|
4036
|
-
/* @__PURE__ */ jsx(FieldCaret, {})
|
|
4037
|
-
]
|
|
4038
|
-
}
|
|
4039
|
-
),
|
|
4040
|
-
description ? /* @__PURE__ */ jsx(Text, { slot: "description", className: fieldHintStyles, children: description }) : null,
|
|
4041
|
-
errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx(FieldError, { className: fieldErrorStyles, children: errorMessage }) : null,
|
|
4042
|
-
/* @__PURE__ */ jsxs(
|
|
4043
|
-
Popover$1,
|
|
4044
|
-
{
|
|
4045
|
-
offset: 6,
|
|
4046
|
-
className: cn(
|
|
4047
|
-
// Match the trigger width exactly (RAC exposes --trigger-width on
|
|
4048
|
-
// the Popover when anchored to a Select button).
|
|
4049
|
-
"w-[var(--trigger-width)]",
|
|
4050
|
-
// Float above modals + full-screen editors (e.g. agent-studio's
|
|
4051
|
-
// .as-editor z-1000 / a raised modal overlay), matching Menu's z-[1200].
|
|
4052
|
-
"z-[1300]",
|
|
4053
|
-
"rounded-md border border-rule bg-surface p-1",
|
|
4054
|
-
"shadow-[var(--shadow-raised)]",
|
|
4055
|
-
"entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
|
|
4056
|
-
"exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1",
|
|
4057
|
-
popoverClassName
|
|
4058
|
-
),
|
|
4059
|
-
children: [
|
|
4060
|
-
/* @__PURE__ */ jsx(ListBox, { className: "outline-none max-h-72 overflow-auto", children: options.map((option) => /* @__PURE__ */ jsx(
|
|
4061
|
-
SelectItem,
|
|
4062
|
-
{
|
|
4063
|
-
id: option.id,
|
|
4064
|
-
...option.textValue ? { textValue: option.textValue } : {},
|
|
4065
|
-
...option.isDisabled ? { isDisabled: true } : {},
|
|
4066
|
-
children: renderItem ? renderItem(option) : option.label
|
|
4067
|
-
},
|
|
4068
|
-
option.id
|
|
4069
|
-
)) }),
|
|
4070
|
-
options.length === 0 ? /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", fieldHintStyles), children: placeholder }) : null
|
|
4071
|
-
]
|
|
4072
|
-
}
|
|
4073
|
-
)
|
|
4074
|
-
]
|
|
4075
|
-
}
|
|
4076
|
-
);
|
|
4077
|
-
}
|
|
4078
|
-
function SelectItem(props) {
|
|
4079
|
-
return /* @__PURE__ */ jsx(
|
|
4080
|
-
ListBoxItem,
|
|
4081
|
-
{
|
|
4082
|
-
...props,
|
|
4083
|
-
className: cn(
|
|
4084
|
-
"flex items-center justify-between gap-3",
|
|
4085
|
-
"rounded-[6px] px-3 py-2 text-[14px] text-ink-2",
|
|
4086
|
-
"cursor-pointer outline-none select-none",
|
|
4087
|
-
"data-[hovered]:bg-surface-2 data-[hovered]:text-ink",
|
|
4088
|
-
"data-[focused]:bg-surface-2 data-[focused]:text-ink",
|
|
4089
|
-
"data-[selected]:bg-surface-3 data-[selected]:text-ink",
|
|
4090
|
-
"data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
|
|
4091
|
-
)
|
|
4092
|
-
}
|
|
4093
|
-
);
|
|
4094
|
-
}
|
|
4095
4867
|
function TagInput({
|
|
4096
4868
|
label,
|
|
4097
4869
|
description,
|
|
@@ -4107,7 +4879,7 @@ function TagInput({
|
|
|
4107
4879
|
autoFocus = false,
|
|
4108
4880
|
className
|
|
4109
4881
|
}) {
|
|
4110
|
-
const
|
|
4882
|
+
const styles2 = fieldControlStyles({ size, invalid: isInvalid, appearance });
|
|
4111
4883
|
const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
|
|
4112
4884
|
const minHeight = appearance === "cell" ? "min-h-[29px]" : fieldMinHeight[size];
|
|
4113
4885
|
const inputRef = useRef(null);
|
|
@@ -4148,7 +4920,7 @@ function TagInput({
|
|
|
4148
4920
|
"data-invalid": isInvalid || void 0,
|
|
4149
4921
|
"data-disabled": isDisabled || void 0,
|
|
4150
4922
|
className: cn(
|
|
4151
|
-
|
|
4923
|
+
styles2.box(),
|
|
4152
4924
|
"flex flex-wrap items-center gap-1",
|
|
4153
4925
|
minHeight,
|
|
4154
4926
|
fieldChipPadding[size],
|
|
@@ -5888,45 +6660,45 @@ var tabsStyles = tv({
|
|
|
5888
6660
|
defaultVariants: { size: "md" }
|
|
5889
6661
|
});
|
|
5890
6662
|
function Root({ size, className, children, ...props }) {
|
|
5891
|
-
const
|
|
6663
|
+
const styles2 = tabsStyles({ size });
|
|
5892
6664
|
return /* @__PURE__ */ jsx(
|
|
5893
6665
|
Tabs$1,
|
|
5894
6666
|
{
|
|
5895
6667
|
...props,
|
|
5896
|
-
className: (rp) => cn(
|
|
6668
|
+
className: (rp) => cn(styles2.root(), typeof className === "function" ? className(rp) : className),
|
|
5897
6669
|
children
|
|
5898
6670
|
}
|
|
5899
6671
|
);
|
|
5900
6672
|
}
|
|
5901
6673
|
function List({ size, className, children, ...props }) {
|
|
5902
|
-
const
|
|
6674
|
+
const styles2 = tabsStyles({ size });
|
|
5903
6675
|
return /* @__PURE__ */ jsx(
|
|
5904
6676
|
TabList,
|
|
5905
6677
|
{
|
|
5906
6678
|
...props,
|
|
5907
|
-
className: (rp) => cn(
|
|
6679
|
+
className: (rp) => cn(styles2.list(), typeof className === "function" ? className(rp) : className),
|
|
5908
6680
|
children
|
|
5909
6681
|
}
|
|
5910
6682
|
);
|
|
5911
6683
|
}
|
|
5912
6684
|
function Trigger({ size, className, children, ...props }) {
|
|
5913
|
-
const
|
|
6685
|
+
const styles2 = tabsStyles({ size });
|
|
5914
6686
|
return /* @__PURE__ */ jsx(
|
|
5915
6687
|
Tab,
|
|
5916
6688
|
{
|
|
5917
6689
|
...props,
|
|
5918
|
-
className: (rp) => cn(
|
|
6690
|
+
className: (rp) => cn(styles2.trigger(), typeof className === "function" ? className(rp) : className),
|
|
5919
6691
|
children
|
|
5920
6692
|
}
|
|
5921
6693
|
);
|
|
5922
6694
|
}
|
|
5923
6695
|
function Panel({ className, children, ...props }) {
|
|
5924
|
-
const
|
|
6696
|
+
const styles2 = tabsStyles({});
|
|
5925
6697
|
return /* @__PURE__ */ jsx(
|
|
5926
6698
|
TabPanel,
|
|
5927
6699
|
{
|
|
5928
6700
|
...props,
|
|
5929
|
-
className: (rp) => cn(
|
|
6701
|
+
className: (rp) => cn(styles2.panel(), typeof className === "function" ? className(rp) : className),
|
|
5930
6702
|
children
|
|
5931
6703
|
}
|
|
5932
6704
|
);
|
|
@@ -5950,7 +6722,7 @@ function Textarea({
|
|
|
5950
6722
|
className,
|
|
5951
6723
|
...props
|
|
5952
6724
|
}) {
|
|
5953
|
-
const
|
|
6725
|
+
const styles2 = fieldControlStyles({ size, appearance });
|
|
5954
6726
|
return /* @__PURE__ */ jsxs(
|
|
5955
6727
|
TextField,
|
|
5956
6728
|
{
|
|
@@ -5970,8 +6742,8 @@ function Textarea({
|
|
|
5970
6742
|
className: cn(
|
|
5971
6743
|
// Same hairline chrome as Input, but allow vertical resize and set a
|
|
5972
6744
|
// sensible minimum height so short content doesn't collapse.
|
|
5973
|
-
|
|
5974
|
-
|
|
6745
|
+
styles2.box(),
|
|
6746
|
+
styles2.content(),
|
|
5975
6747
|
"min-h-[100px] resize-y",
|
|
5976
6748
|
textareaClassName
|
|
5977
6749
|
)
|
|
@@ -5992,7 +6764,7 @@ function TimeField({
|
|
|
5992
6764
|
className,
|
|
5993
6765
|
...props
|
|
5994
6766
|
}) {
|
|
5995
|
-
const
|
|
6767
|
+
const styles2 = fieldControlStyles({ size, appearance });
|
|
5996
6768
|
const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
|
|
5997
6769
|
return /* @__PURE__ */ jsxs(
|
|
5998
6770
|
TimeField$1,
|
|
@@ -6008,8 +6780,8 @@ function TimeField({
|
|
|
6008
6780
|
DateInput,
|
|
6009
6781
|
{
|
|
6010
6782
|
className: cn(
|
|
6011
|
-
|
|
6012
|
-
|
|
6783
|
+
styles2.box(),
|
|
6784
|
+
styles2.content(),
|
|
6013
6785
|
"flex items-center min-w-0 text-ink",
|
|
6014
6786
|
// Tabular alignment for time segments — overrides content's
|
|
6015
6787
|
// font-sans on purpose.
|
|
@@ -6064,6 +6836,6 @@ function Tooltip({ className, offset = 6, ...props }) {
|
|
|
6064
6836
|
);
|
|
6065
6837
|
}
|
|
6066
6838
|
|
|
6067
|
-
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, Checkbox, CheckboxGroup, ChipValue, Combobox, Command, CommandHint, CurrencyInput, DataTable, DataTableEmpty, DataTableError, DataTableSkeleton, DateCell, DatePicker, DateTimeCell, DateTimePicker, EmptyState, ErrorAnimation, FieldCaret, FileField, FileViewer, FileViewerFallbackCard, FilterRow, FilterValueInput, ImageAttachment, ImageLightbox, Input, Kbd, LinkCell, Menu, MenuItem, MenuPopover, MenuTrigger, Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, MultiSelect, NavGroup, NavItem, NavSection, OPERATORS, OperatorPicker, PROTECTED_COMBOS, Popover, PopoverContent, PopoverPanelBody, PopoverPanelFooter, PopoverPanelHeader, PressableCard, ProgressBar, Radio, RadioGroup, 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, addLeaf as addFilterLeaf, addPendingLeaf, addSubgroup, arityOf, attachmentChipStyles, avatarGroupStyles, avatarStyles, badgeStyles, cardStyles2 as cardStyles, countLeaves, defaultValueFor, emptyStateStyles, fieldControlStyles, fieldErrorStyles, fieldHintStyles, fieldLabelStyles, fileViewerFallbackCardStyles, fileViewerStyles, formatFileSize, formatValue, iconForContentType, imageAttachmentStyles, imageLightboxStyles, isColumnDragData, isEditableTarget, isPreviewableImageName, makeActionsColumn, navItemStyles, operatorById, operatorsForType, parseCombo, progressBarStyles, rebaseRowOnFieldChange, rebaseRowOnOperatorChange, removeLeaf, removeSubgroup, replaceLeaf, segmentedControlStyles, setJoiner, tableCellStyles, tableHeaderCellStyles, tabsStyles, updateLeaf, updateSubgroup, useAttributeTypeIcon, useCommand, useDiscoveryActive, useEscapeKey, useInfiniteScrollSentinel, useRenderChipItem, useRenderValueInput, useShowDetails, viewerKindForContentType, wrapLeafInGroup };
|
|
6839
|
+
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, Checkbox, CheckboxGroup, ChipValue, Combobox, Command, CommandHint, CurrencyInput, DataTable, DataTableEmpty, DataTableError, DataTableSkeleton, DateCell, DatePicker, DateTimeCell, DateTimePicker, DraftMessageComposer, 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, countLeaves, createDraftMessageClient, defaultValueFor, draftMessageComposerStyles, emptyStateStyles, fieldControlStyles, fieldErrorStyles, fieldHintStyles, fieldLabelStyles, fileViewerFallbackCardStyles, fileViewerStyles, formatFileSize, formatValue, iconForContentType, imageAttachmentStyles, imageLightboxStyles, isColumnDragData, isEditableTarget, isPreviewableImageName, makeActionsColumn, messageComposerStyles, navItemStyles, operatorById, operatorsForType, parseCombo, progressBarStyles, publishDraftMessageOutcome, rebaseRowOnFieldChange, rebaseRowOnOperatorChange, registerDraftMessageActions, removeLeaf, removeSubgroup, replaceLeaf, segmentedControlStyles, setJoiner, subscribeDraftMessageOutcome, tableCellStyles, tableHeaderCellStyles, tabsStyles, updateLeaf, updateSubgroup, useAttributeTypeIcon, useCommand, useDiscoveryActive, useDraftMessageActions, useDraftMessageOutcome, useEscapeKey, useInfiniteScrollSentinel, useRenderChipItem, useRenderValueInput, useShowDetails, viewerKindForContentType, wrapLeafInGroup };
|
|
6068
6840
|
//# sourceMappingURL=index.js.map
|
|
6069
6841
|
//# sourceMappingURL=index.js.map
|