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