@estiva-app/ui 0.9.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Avatar.d.ts +13 -1
- package/dist/Avatar.d.ts.map +1 -1
- package/dist/AvatarGroup.d.ts.map +1 -1
- package/dist/Banner.d.ts +16 -1
- package/dist/Banner.d.ts.map +1 -1
- package/dist/Breadcrumb.d.ts +9 -0
- package/dist/Breadcrumb.d.ts.map +1 -1
- package/dist/Button.d.ts +12 -2
- package/dist/Button.d.ts.map +1 -1
- package/dist/Checkbox.d.ts +2 -0
- package/dist/Checkbox.d.ts.map +1 -1
- package/dist/Chip.d.ts +3 -2
- package/dist/Chip.d.ts.map +1 -1
- package/dist/ChipInput.d.ts +3 -1
- package/dist/ChipInput.d.ts.map +1 -1
- package/dist/ConfirmDialog.d.ts +5 -0
- package/dist/ConfirmDialog.d.ts.map +1 -1
- package/dist/DialogShell.d.ts +27 -3
- package/dist/DialogShell.d.ts.map +1 -1
- package/dist/EditableText.d.ts +6 -0
- package/dist/EditableText.d.ts.map +1 -1
- package/dist/Field.d.ts +42 -25
- package/dist/Field.d.ts.map +1 -1
- package/dist/IconButton.d.ts +3 -2
- package/dist/IconButton.d.ts.map +1 -1
- package/dist/IdentityMenu.d.ts +25 -13
- package/dist/IdentityMenu.d.ts.map +1 -1
- package/dist/Menu.d.ts +65 -82
- package/dist/Menu.d.ts.map +1 -1
- package/dist/PersonTrigger.d.ts.map +1 -1
- package/dist/Popover.d.ts +86 -0
- package/dist/Popover.d.ts.map +1 -0
- package/dist/PreviewCard.d.ts +39 -0
- package/dist/PreviewCard.d.ts.map +1 -0
- package/dist/Reaction.d.ts.map +1 -1
- package/dist/ReactionPicker.d.ts +55 -0
- package/dist/ReactionPicker.d.ts.map +1 -0
- package/dist/SearchInput.d.ts +5 -0
- package/dist/SearchInput.d.ts.map +1 -1
- package/dist/Select.d.ts +26 -16
- package/dist/Select.d.ts.map +1 -1
- package/dist/Tabs.d.ts +11 -1
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/TextInput.d.ts +7 -1
- package/dist/TextInput.d.ts.map +1 -1
- package/dist/Textarea.d.ts +8 -1
- package/dist/Textarea.d.ts.map +1 -1
- package/dist/Toolbar.d.ts +95 -0
- package/dist/Toolbar.d.ts.map +1 -0
- package/dist/Tooltip.d.ts +36 -10
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/cn.d.ts.map +1 -1
- package/dist/fit.d.ts +7 -72
- package/dist/fit.d.ts.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +888 -763
- package/dist/index.js.map +4 -4
- package/dist/triggerDisabled.d.ts +10 -0
- package/dist/triggerDisabled.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/Avatar.mdx +17 -0
- package/src/Avatar.name.test.tsx +99 -0
- package/src/Avatar.tsx +25 -3
- package/src/AvatarGroup.tsx +4 -1
- package/src/Banner.mdx +10 -2
- package/src/Banner.stories.tsx +29 -0
- package/src/Banner.test.tsx +60 -0
- package/src/Banner.tsx +35 -3
- package/src/Breadcrumb.mdx +5 -2
- package/src/Breadcrumb.test.tsx +44 -0
- package/src/Breadcrumb.tsx +10 -2
- package/src/Button.compose.test.tsx +119 -0
- package/src/Button.mdx +7 -5
- package/src/Button.test.tsx +2 -1
- package/src/Button.tsx +21 -5
- package/src/Checkbox.tsx +3 -0
- package/src/Chip.tsx +3 -2
- package/src/ChipInput.tsx +4 -0
- package/src/ConfirmDialog.mdx +20 -1
- package/src/ConfirmDialog.test.tsx +109 -0
- package/src/ConfirmDialog.tsx +6 -0
- package/src/DialogShell.mdx +18 -4
- package/src/DialogShell.stories.tsx +56 -6
- package/src/DialogShell.test.tsx +184 -0
- package/src/DialogShell.tsx +108 -39
- package/src/EditableText.mdx +6 -1
- package/src/EditableText.test.tsx +156 -0
- package/src/EditableText.tsx +23 -5
- package/src/Field.mdx +23 -3
- package/src/Field.stories.tsx +35 -0
- package/src/Field.test.tsx +144 -26
- package/src/Field.tsx +94 -59
- package/src/IconButton.mdx +6 -2
- package/src/IconButton.test.tsx +5 -2
- package/src/IconButton.tsx +12 -6
- package/src/IdentityMenu.mdx +29 -6
- package/src/IdentityMenu.stories.tsx +41 -6
- package/src/IdentityMenu.tsx +74 -47
- package/src/Kbd.stories.tsx +3 -3
- package/src/Menu.mdx +88 -75
- package/src/Menu.stories.tsx +120 -52
- package/src/Menu.test.tsx +315 -0
- package/src/Menu.tsx +347 -251
- package/src/MenuItem.stories.tsx +135 -98
- package/src/NavItem.mdx +1 -1
- package/src/PersonTrigger.mdx +20 -14
- package/src/PersonTrigger.tsx +23 -1
- package/src/Popover.mdx +130 -0
- package/src/Popover.stories.tsx +222 -0
- package/src/Popover.test.tsx +188 -0
- package/src/Popover.tsx +156 -0
- package/src/PreviewCard.mdx +98 -0
- package/src/PreviewCard.stories.tsx +168 -0
- package/src/PreviewCard.test.tsx +83 -0
- package/src/PreviewCard.tsx +91 -0
- package/src/Rail.mdx +63 -0
- package/src/Rail.stories.tsx +81 -0
- package/src/Reaction.mdx +4 -2
- package/src/Reaction.tsx +4 -2
- package/src/ReactionPicker.mdx +85 -0
- package/src/ReactionPicker.stories.tsx +120 -0
- package/src/ReactionPicker.test.tsx +118 -0
- package/src/ReactionPicker.tsx +88 -0
- package/src/SearchInput.mdx +6 -2
- package/src/SearchInput.tsx +7 -1
- package/src/Select.mdx +16 -5
- package/src/Select.test.tsx +157 -0
- package/src/Select.tsx +89 -214
- package/src/Sidebar.mdx +6 -2
- package/src/Tabs.mdx +5 -0
- package/src/Tabs.test.tsx +15 -0
- package/src/Tabs.tsx +17 -2
- package/src/TextInput.mdx +8 -2
- package/src/TextInput.tsx +10 -7
- package/src/Textarea.mdx +6 -2
- package/src/Textarea.tsx +14 -9
- package/src/Toolbar.mdx +104 -0
- package/src/Toolbar.stories.tsx +142 -0
- package/src/Toolbar.test.tsx +253 -0
- package/src/Toolbar.tsx +158 -0
- package/src/Tooltip.mdx +43 -5
- package/src/Tooltip.stories.tsx +26 -0
- package/src/Tooltip.test.tsx +195 -0
- package/src/Tooltip.tsx +150 -48
- package/src/cn.ts +1 -1
- package/src/fit.test.ts +101 -0
- package/src/fit.ts +19 -63
- package/src/index.ts +6 -3
- package/src/triggerDisabled.ts +13 -0
- package/stories/Choosing.mdx +8 -3
- package/tailwind-preset.js +7 -0
- package/src/Menu.fit.test.ts +0 -90
- package/src/Select.fit.test.ts +0 -101
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ var FONT_SIZE_TOKENS = [
|
|
|
19
19
|
"input-helper",
|
|
20
20
|
"chip"
|
|
21
21
|
];
|
|
22
|
-
var BOX_SHADOW_TOKENS = ["sm", "md", "lg", "focus-ring", "glow-warning", "highlight-inset"];
|
|
22
|
+
var BOX_SHADOW_TOKENS = ["sm", "md", "lg", "focus-ring", "glow-warning", "glow-success", "glow-accent", "highlight-inset"];
|
|
23
23
|
var DROP_SHADOW_TOKENS = ["glow-success", "glow-accent"];
|
|
24
24
|
var twMerge = extendTailwindMerge({
|
|
25
25
|
extend: {
|
|
@@ -119,22 +119,33 @@ var initialsFor = (name) => {
|
|
|
119
119
|
const initials = words.map((w) => w[0]).join("") || name.trim().charAt(0);
|
|
120
120
|
return initials.toUpperCase();
|
|
121
121
|
};
|
|
122
|
-
function Avatar({ src, name, alt = "", size = 36, className }) {
|
|
122
|
+
function Avatar({ src, name, alt = "", size = 36, label: spoken, className }) {
|
|
123
123
|
const [broken, setBroken] = useState(false);
|
|
124
124
|
const label = name || alt;
|
|
125
125
|
const picture = src && !broken ? src : void 0;
|
|
126
|
-
return /* @__PURE__ */ jsx3(
|
|
126
|
+
return /* @__PURE__ */ jsx3(
|
|
127
127
|
"div",
|
|
128
128
|
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
...spoken ? { role: "img", "aria-label": spoken } : { "aria-hidden": true },
|
|
130
|
+
className: cn("rounded-sm overflow-hidden shrink-0 bg-bg-inset", className),
|
|
131
|
+
style: { width: size, height: size },
|
|
132
|
+
children: picture ? /* @__PURE__ */ jsx3("img", { src: picture, alt: "", className: "w-full h-full object-cover", onError: () => setBroken(true) }) : label ? /* @__PURE__ */ jsx3(
|
|
133
|
+
"div",
|
|
134
|
+
{
|
|
135
|
+
className: "w-full h-full flex items-center justify-center font-semibold leading-none",
|
|
136
|
+
style: {
|
|
137
|
+
// The one ink colour that reads on all eight hues, which are a
|
|
138
|
+
// palette rather than tokens (see the note at the top) — so its
|
|
139
|
+
// ink cannot be a token either.
|
|
140
|
+
color: "#08121c",
|
|
141
|
+
fontSize: Math.round(size * 0.36),
|
|
142
|
+
background: `linear-gradient(160deg, color-mix(in srgb, ${hueFor(label)} 92%, #fff) 0%, color-mix(in srgb, ${hueFor(label)} 70%, #0b0d11) 100%)`
|
|
143
|
+
},
|
|
144
|
+
children: initialsFor(label)
|
|
145
|
+
}
|
|
146
|
+
) : /* @__PURE__ */ jsx3("div", { className: "w-full h-full bg-accent-muted flex items-center justify-center text-text-muted", children: /* @__PURE__ */ jsx3(IconUserFilled, { size: Math.round(size * 0.5) }) })
|
|
136
147
|
}
|
|
137
|
-
)
|
|
148
|
+
);
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
// src/AvatarGroup.tsx
|
|
@@ -154,7 +165,7 @@ function AvatarGroup({ members, size = 24 }) {
|
|
|
154
165
|
{
|
|
155
166
|
className: "relative flex rounded-sm",
|
|
156
167
|
style: { marginRight: -overlap, boxShadow: `0 0 0 ${ring}px var(--bg-surface)` },
|
|
157
|
-
children: /* @__PURE__ */ jsx4(Avatar, { size, name: member.name, src: member.picture,
|
|
168
|
+
children: /* @__PURE__ */ jsx4(Avatar, { size, name: member.name, src: member.picture, label: member.name })
|
|
158
169
|
},
|
|
159
170
|
i
|
|
160
171
|
)
|
|
@@ -162,28 +173,18 @@ function AvatarGroup({ members, size = 24 }) {
|
|
|
162
173
|
}
|
|
163
174
|
|
|
164
175
|
// src/Banner.tsx
|
|
165
|
-
import {
|
|
166
|
-
var TONE_STYLES = {
|
|
167
|
-
ok: "bg-success-muted text-success-default",
|
|
168
|
-
error: "bg-error-muted text-error-default",
|
|
169
|
-
info: "bg-info-muted text-info-default",
|
|
170
|
-
warning: "bg-warning-muted text-warning-default"
|
|
171
|
-
};
|
|
172
|
-
function Banner({ tone, children, className }) {
|
|
173
|
-
return /* @__PURE__ */ jsx5("div", { role: tone === "error" ? "alert" : "status", className: cn("px-4 py-2 text-body-2", TONE_STYLES[tone], className), children });
|
|
174
|
-
}
|
|
176
|
+
import { IconX } from "@tabler/icons-react";
|
|
175
177
|
|
|
176
|
-
// src/
|
|
178
|
+
// src/IconButton.tsx
|
|
177
179
|
import { Button as BaseButton } from "@base-ui/react/button";
|
|
178
180
|
|
|
179
181
|
// src/Tooltip.tsx
|
|
180
|
-
import {
|
|
181
|
-
import { createPortal } from "react-dom";
|
|
182
|
+
import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
|
|
182
183
|
|
|
183
184
|
// src/Kbd.tsx
|
|
184
|
-
import { jsx as
|
|
185
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
185
186
|
function Kbd({ children, className }) {
|
|
186
|
-
return /* @__PURE__ */
|
|
187
|
+
return /* @__PURE__ */ jsx5(
|
|
187
188
|
"kbd",
|
|
188
189
|
{
|
|
189
190
|
className: cn(
|
|
@@ -198,48 +199,133 @@ function Kbd({ children, className }) {
|
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
// src/Tooltip.tsx
|
|
201
|
-
import { jsx as
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
202
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
203
|
+
var OPEN_DELAY = 300;
|
|
204
|
+
function TooltipProvider({ delay = OPEN_DELAY, closeDelay, timeout, children }) {
|
|
205
|
+
return /* @__PURE__ */ jsx6(BaseTooltip.Provider, { delay, closeDelay, timeout, children });
|
|
206
|
+
}
|
|
207
|
+
function Tooltip({ label, shortcut, className, ...props }) {
|
|
208
|
+
return /* @__PURE__ */ jsxs3("div", { role: "tooltip", className: cn("bg-bg-elevated border border-border-default rounded-lg h-[30px] flex items-center justify-center gap-1.5 px-2 shadow-lg", className), ...props, children: [
|
|
209
|
+
/* @__PURE__ */ jsx6("span", { className: "text-caption text-text-primary whitespace-nowrap", children: label }),
|
|
210
|
+
shortcut && /* @__PURE__ */ jsx6(Kbd, { children: shortcut })
|
|
206
211
|
] });
|
|
207
212
|
}
|
|
208
213
|
var GAP = 6;
|
|
209
214
|
var VIEWPORT_PAD = 8;
|
|
215
|
+
function TooltipSurface({ label, shortcut, placement }) {
|
|
216
|
+
return /* @__PURE__ */ jsx6(BaseTooltip.Portal, { children: /* @__PURE__ */ jsx6(
|
|
217
|
+
BaseTooltip.Positioner,
|
|
218
|
+
{
|
|
219
|
+
side: placement,
|
|
220
|
+
align: "center",
|
|
221
|
+
sideOffset: GAP,
|
|
222
|
+
collisionPadding: VIEWPORT_PAD,
|
|
223
|
+
positionMethod: "fixed",
|
|
224
|
+
className: "pointer-events-none z-[9999]",
|
|
225
|
+
children: /* @__PURE__ */ jsx6(
|
|
226
|
+
BaseTooltip.Popup,
|
|
227
|
+
{
|
|
228
|
+
render: /* @__PURE__ */ jsx6(
|
|
229
|
+
Tooltip,
|
|
230
|
+
{
|
|
231
|
+
label,
|
|
232
|
+
shortcut,
|
|
233
|
+
className: cn(
|
|
234
|
+
"transition-[opacity,transform] duration-[120ms] ease-out motion-reduce:transition-none",
|
|
235
|
+
"data-[ending-style]:duration-[80ms] data-[instant]:duration-0",
|
|
236
|
+
"data-[starting-style]:opacity-0 data-[ending-style]:opacity-0",
|
|
237
|
+
"data-[side=top]:data-[starting-style]:translate-y-[4px] data-[side=top]:data-[ending-style]:translate-y-[4px]",
|
|
238
|
+
"data-[side=bottom]:data-[starting-style]:-translate-y-[4px] data-[side=bottom]:data-[ending-style]:-translate-y-[4px]"
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
) });
|
|
246
|
+
}
|
|
210
247
|
function WithTooltip({ label, shortcut, placement = "top", wrapperClassName, children }) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const tipRect = tip.getBoundingClientRect();
|
|
221
|
-
let top = placement === "bottom" ? triggerRect.bottom + GAP : triggerRect.top - tipRect.height - GAP;
|
|
222
|
-
if (placement === "top" && top < VIEWPORT_PAD) top = triggerRect.bottom + GAP;
|
|
223
|
-
else if (placement === "bottom" && top + tipRect.height > window.innerHeight - VIEWPORT_PAD) top = triggerRect.top - tipRect.height - GAP;
|
|
224
|
-
top = Math.max(VIEWPORT_PAD, Math.min(top, window.innerHeight - tipRect.height - VIEWPORT_PAD));
|
|
225
|
-
let left = triggerRect.left + triggerRect.width / 2 - tipRect.width / 2;
|
|
226
|
-
left = Math.max(VIEWPORT_PAD, Math.min(left, window.innerWidth - tipRect.width - VIEWPORT_PAD));
|
|
227
|
-
setStyle({ position: "fixed", top, left, zIndex: 9999, pointerEvents: "none", visibility: "visible" });
|
|
228
|
-
}, [placement]);
|
|
229
|
-
useLayoutEffect(() => {
|
|
230
|
-
if (show) reposition();
|
|
231
|
-
}, [show, reposition]);
|
|
232
|
-
return /* @__PURE__ */ jsxs3("div", { ref, className: cn("inline-flex shrink-0", wrapperClassName), onMouseEnter: () => setShow(true), onMouseLeave: () => setShow(false), children: [
|
|
233
|
-
children,
|
|
234
|
-
show && createPortal(
|
|
235
|
-
/* @__PURE__ */ jsx7("div", { ref: tooltipRef, style, children: /* @__PURE__ */ jsx7(Tooltip, { label, shortcut }) }),
|
|
236
|
-
document.body
|
|
237
|
-
)
|
|
248
|
+
return /* @__PURE__ */ jsxs3(BaseTooltip.Root, { disableHoverablePopup: true, children: [
|
|
249
|
+
/* @__PURE__ */ jsx6(BaseTooltip.Trigger, { delay: OPEN_DELAY, render: /* @__PURE__ */ jsx6("div", { className: cn("inline-flex shrink-0", wrapperClassName) }), children }),
|
|
250
|
+
/* @__PURE__ */ jsx6(TooltipSurface, { label, shortcut, placement })
|
|
251
|
+
] });
|
|
252
|
+
}
|
|
253
|
+
function TooltipTrigger({ label, shortcut, placement = "top", children }) {
|
|
254
|
+
return /* @__PURE__ */ jsxs3(BaseTooltip.Root, { disableHoverablePopup: true, children: [
|
|
255
|
+
/* @__PURE__ */ jsx6(BaseTooltip.Trigger, { delay: OPEN_DELAY, render: children }),
|
|
256
|
+
/* @__PURE__ */ jsx6(TooltipSurface, { label, shortcut, placement })
|
|
238
257
|
] });
|
|
239
258
|
}
|
|
240
259
|
|
|
241
|
-
// src/
|
|
260
|
+
// src/IconButton.tsx
|
|
261
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
262
|
+
function IconButton({
|
|
263
|
+
variant = "muted",
|
|
264
|
+
className,
|
|
265
|
+
children,
|
|
266
|
+
disabled,
|
|
267
|
+
disabledReason,
|
|
268
|
+
tooltip,
|
|
269
|
+
tooltipShortcut,
|
|
270
|
+
tooltipPlacement,
|
|
271
|
+
type = "button",
|
|
272
|
+
...props
|
|
273
|
+
}) {
|
|
274
|
+
const button = /* @__PURE__ */ jsx7(
|
|
275
|
+
BaseButton,
|
|
276
|
+
{
|
|
277
|
+
type,
|
|
278
|
+
disabled: disabled || !!disabledReason,
|
|
279
|
+
focusableWhenDisabled: !!disabledReason,
|
|
280
|
+
className: (state) => cn(
|
|
281
|
+
"flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 cursor-pointer",
|
|
282
|
+
!state.disabled && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse",
|
|
283
|
+
!state.disabled && variant === "muted" && "text-text-secondary hover:bg-bg-hover hover:text-text-primary",
|
|
284
|
+
!state.disabled && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-secondary",
|
|
285
|
+
state.disabled && variant === "primary" && "bg-bg-disabled text-text-disabled",
|
|
286
|
+
state.disabled && variant === "muted" && "text-text-disabled",
|
|
287
|
+
state.disabled && variant === "outlined" && "border border-border-default text-text-disabled",
|
|
288
|
+
// `pointer-events-none` only where the button is truly out of reach:
|
|
289
|
+
// with a `disabledReason` the button IS the tooltip's trigger, and a
|
|
290
|
+
// trigger the pointer cannot land on never opens one. Base UI already
|
|
291
|
+
// swallows the click.
|
|
292
|
+
state.disabled && !disabledReason && "pointer-events-none",
|
|
293
|
+
state.disabled && "cursor-not-allowed",
|
|
294
|
+
className
|
|
295
|
+
),
|
|
296
|
+
...props,
|
|
297
|
+
children
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
const label = disabledReason ?? tooltip;
|
|
301
|
+
if (label) {
|
|
302
|
+
return /* @__PURE__ */ jsx7(TooltipTrigger, { label, shortcut: disabledReason ? void 0 : tooltipShortcut, placement: tooltipPlacement, children: button });
|
|
303
|
+
}
|
|
304
|
+
return button;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/Banner.tsx
|
|
242
308
|
import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
309
|
+
var TONE_STYLES = {
|
|
310
|
+
ok: "bg-success-muted text-success-default",
|
|
311
|
+
error: "bg-error-muted text-error-default",
|
|
312
|
+
info: "bg-info-muted text-info-default",
|
|
313
|
+
warning: "bg-warning-muted text-warning-default"
|
|
314
|
+
};
|
|
315
|
+
function Banner({ tone, children, onDismiss, dismissLabel = "Dismiss", className }) {
|
|
316
|
+
const role = tone === "error" ? "alert" : "status";
|
|
317
|
+
if (!onDismiss) {
|
|
318
|
+
return /* @__PURE__ */ jsx8("div", { role, className: cn("px-4 py-2 text-body-2", TONE_STYLES[tone], className), children });
|
|
319
|
+
}
|
|
320
|
+
return /* @__PURE__ */ jsxs4("div", { role, className: cn("flex items-center gap-3 px-4 py-2 text-body-2", TONE_STYLES[tone], className), children: [
|
|
321
|
+
/* @__PURE__ */ jsx8("span", { className: "min-w-0 flex-1", children }),
|
|
322
|
+
/* @__PURE__ */ jsx8(IconButton, { "aria-label": dismissLabel, onClick: onDismiss, className: "-mr-1 shrink-0 text-current hover:text-current", children: /* @__PURE__ */ jsx8(IconX, { size: 16, stroke: 1.5 }) })
|
|
323
|
+
] });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/Button.tsx
|
|
327
|
+
import { Button as BaseButton2 } from "@base-ui/react/button";
|
|
328
|
+
import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
243
329
|
function Button({
|
|
244
330
|
variant = "muted",
|
|
245
331
|
size = "default",
|
|
@@ -252,8 +338,8 @@ function Button({
|
|
|
252
338
|
...props
|
|
253
339
|
}) {
|
|
254
340
|
const hasLeadingIcon = !!leadingIcon;
|
|
255
|
-
const button = /* @__PURE__ */
|
|
256
|
-
|
|
341
|
+
const button = /* @__PURE__ */ jsxs5(
|
|
342
|
+
BaseButton2,
|
|
257
343
|
{
|
|
258
344
|
type,
|
|
259
345
|
disabled: disabled || !!disabledReason,
|
|
@@ -269,7 +355,13 @@ function Button({
|
|
|
269
355
|
!state.disabled && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-primary cursor-pointer",
|
|
270
356
|
!state.disabled && variant === "muted" && "hover:bg-bg-hover text-text-primary cursor-pointer",
|
|
271
357
|
!state.disabled && variant === "destructive" && "hover:bg-error-muted text-error-default cursor-pointer",
|
|
272
|
-
state.disabled && "bg-bg-disabled text-text-disabled
|
|
358
|
+
state.disabled && "bg-bg-disabled text-text-disabled",
|
|
359
|
+
// `pointer-events-none` only where the button is truly out of reach.
|
|
360
|
+
// With a `disabledReason` the button IS the tooltip's trigger, and a
|
|
361
|
+
// trigger the pointer cannot land on never opens one — Base UI already
|
|
362
|
+
// swallows the click (`focusableWhenDisabled` gives `aria-disabled`
|
|
363
|
+
// and a prevented `onClick`), so nothing else needs it.
|
|
364
|
+
state.disabled && !disabledReason && "pointer-events-none",
|
|
273
365
|
state.disabled && variant === "outlined" && "border border-border-default",
|
|
274
366
|
className
|
|
275
367
|
),
|
|
@@ -280,13 +372,13 @@ function Button({
|
|
|
280
372
|
]
|
|
281
373
|
}
|
|
282
374
|
);
|
|
283
|
-
return disabledReason ? /* @__PURE__ */
|
|
375
|
+
return disabledReason ? /* @__PURE__ */ jsx9(TooltipTrigger, { label: disabledReason, children: button }) : button;
|
|
284
376
|
}
|
|
285
377
|
|
|
286
378
|
// src/Checkbox.tsx
|
|
287
379
|
import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
|
|
288
380
|
import { IconCheck } from "@tabler/icons-react";
|
|
289
|
-
import { jsx as
|
|
381
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
290
382
|
function squareClasses(checked, disabled, interactive, className) {
|
|
291
383
|
return cn(
|
|
292
384
|
"inline-flex items-center justify-center size-4 shrink-0 rounded-[4px] border transition-colors",
|
|
@@ -299,24 +391,25 @@ function squareClasses(checked, disabled, interactive, className) {
|
|
|
299
391
|
var tickClasses = (checked) => cn("flex", !checked && "invisible");
|
|
300
392
|
function Checkbox({ checked, onChange, disabled = false, className, ...aria }) {
|
|
301
393
|
if (!onChange) {
|
|
302
|
-
return /* @__PURE__ */
|
|
394
|
+
return /* @__PURE__ */ jsx10("span", { "aria-hidden": "true", className: squareClasses(checked, disabled, false, className), children: /* @__PURE__ */ jsx10("span", { className: tickClasses(checked), children: /* @__PURE__ */ jsx10(IconCheck, { size: 12, stroke: 3 }) }) });
|
|
303
395
|
}
|
|
304
|
-
return /* @__PURE__ */
|
|
396
|
+
return /* @__PURE__ */ jsx10(
|
|
305
397
|
BaseCheckbox.Root,
|
|
306
398
|
{
|
|
307
399
|
checked,
|
|
308
400
|
disabled,
|
|
309
401
|
"aria-label": aria["aria-label"],
|
|
402
|
+
"aria-required": aria["aria-required"],
|
|
310
403
|
onCheckedChange: (next) => onChange(next),
|
|
311
404
|
onClick: (e) => e.stopPropagation(),
|
|
312
405
|
className: (state) => squareClasses(state.checked, state.disabled, true, className),
|
|
313
|
-
children: /* @__PURE__ */
|
|
406
|
+
children: /* @__PURE__ */ jsx10(BaseCheckbox.Indicator, { keepMounted: true, className: (state) => tickClasses(state.checked), children: /* @__PURE__ */ jsx10(IconCheck, { size: 12, stroke: 3 }) })
|
|
314
407
|
}
|
|
315
408
|
);
|
|
316
409
|
}
|
|
317
410
|
|
|
318
411
|
// src/Chip.tsx
|
|
319
|
-
import { jsx as
|
|
412
|
+
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
320
413
|
var typeStyles = {
|
|
321
414
|
neutral: "bg-bg-inset text-text-primary",
|
|
322
415
|
brand: "bg-accent-muted text-accent-primary signal:border signal:border-accent-outline",
|
|
@@ -326,17 +419,17 @@ var typeStyles = {
|
|
|
326
419
|
error: "bg-error-muted text-error-default signal:border signal:border-error-outline"
|
|
327
420
|
};
|
|
328
421
|
function Chip({ type = "neutral", label, leadingIcon, trailingIcon, className }) {
|
|
329
|
-
return /* @__PURE__ */
|
|
330
|
-
leadingIcon && /* @__PURE__ */
|
|
331
|
-
label && /* @__PURE__ */
|
|
332
|
-
trailingIcon && /* @__PURE__ */
|
|
422
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn("inline-flex items-center justify-center gap-1.5 rounded-full max-h-[20px] min-w-[16px] px-2 py-1", typeStyles[type], className), children: [
|
|
423
|
+
leadingIcon && /* @__PURE__ */ jsx11("span", { className: "flex size-3 shrink-0 items-center justify-center", children: leadingIcon }),
|
|
424
|
+
label && /* @__PURE__ */ jsx11("span", { className: "text-chip whitespace-nowrap signal:font-mono signal:text-[10px] signal:font-semibold signal:tracking-[0.02em] signal:tabular-nums", children: label }),
|
|
425
|
+
trailingIcon && /* @__PURE__ */ jsx11("span", { className: "flex size-3 shrink-0 items-center justify-center", children: trailingIcon })
|
|
333
426
|
] });
|
|
334
427
|
}
|
|
335
428
|
|
|
336
429
|
// src/ChipInput.tsx
|
|
337
|
-
import { useState as
|
|
338
|
-
import { createPortal
|
|
339
|
-
import { IconX } from "@tabler/icons-react";
|
|
430
|
+
import { useState as useState2, useRef, useMemo, useEffect, useLayoutEffect } from "react";
|
|
431
|
+
import { createPortal } from "react-dom";
|
|
432
|
+
import { IconX as IconX2 } from "@tabler/icons-react";
|
|
340
433
|
|
|
341
434
|
// src/fit.ts
|
|
342
435
|
var MARGIN = 8;
|
|
@@ -354,35 +447,22 @@ function fitMenu({
|
|
|
354
447
|
const maxHeight = Math.max(Math.min(cap, openUp ? above : below), 120);
|
|
355
448
|
return openUp ? { left, bottom: viewport.height - anchor.top + GAP2, maxHeight } : { left, top: anchor.bottom + GAP2, maxHeight };
|
|
356
449
|
}
|
|
357
|
-
function clampBox({
|
|
358
|
-
box,
|
|
359
|
-
viewport
|
|
360
|
-
}) {
|
|
361
|
-
const left = Math.max(MARGIN, Math.min(box.left, viewport.width - box.width - MARGIN));
|
|
362
|
-
const maxHeight = Math.max(Math.min(box.height, viewport.height - 2 * MARGIN), 120);
|
|
363
|
-
const top = Math.max(MARGIN, Math.min(box.top, viewport.height - maxHeight - MARGIN));
|
|
364
|
-
return { left, top, maxHeight };
|
|
365
|
-
}
|
|
366
|
-
function fitSubmenu({
|
|
367
|
-
row,
|
|
368
|
-
panel,
|
|
369
|
-
viewport
|
|
370
|
-
}) {
|
|
371
|
-
const fitsRight = row.right + GAP2 + panel.width <= viewport.width - MARGIN;
|
|
372
|
-
const left = Math.max(MARGIN, fitsRight ? row.right + GAP2 : row.left - GAP2 - panel.width);
|
|
373
|
-
const top = Math.max(MARGIN, Math.min(row.top, viewport.height - panel.height - MARGIN));
|
|
374
|
-
return { left, top };
|
|
375
|
-
}
|
|
376
450
|
|
|
377
451
|
// src/Menu.tsx
|
|
378
|
-
import { createContext,
|
|
452
|
+
import { createContext, useContext } from "react";
|
|
379
453
|
import { IconChevronRight } from "@tabler/icons-react";
|
|
380
|
-
import {
|
|
454
|
+
import { Menu as BaseMenu } from "@base-ui/react/menu";
|
|
455
|
+
|
|
456
|
+
// src/triggerDisabled.ts
|
|
457
|
+
function triggerDisabled(trigger) {
|
|
458
|
+
const props = trigger.props;
|
|
459
|
+
return !!(props.disabled || props.disabledReason);
|
|
460
|
+
}
|
|
381
461
|
|
|
382
462
|
// src/SectionLabel.tsx
|
|
383
|
-
import { jsx as
|
|
463
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
384
464
|
function SectionLabel({ children, className }) {
|
|
385
|
-
return /* @__PURE__ */
|
|
465
|
+
return /* @__PURE__ */ jsx12(
|
|
386
466
|
"span",
|
|
387
467
|
{
|
|
388
468
|
className: cn(
|
|
@@ -395,224 +475,217 @@ function SectionLabel({ children, className }) {
|
|
|
395
475
|
}
|
|
396
476
|
|
|
397
477
|
// src/Menu.tsx
|
|
398
|
-
import { jsx as
|
|
399
|
-
var
|
|
478
|
+
import { Fragment, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
479
|
+
var MenuContext = createContext(null);
|
|
400
480
|
function MenuPanel({ children, className, ...props }) {
|
|
401
|
-
return /* @__PURE__ */
|
|
481
|
+
return /* @__PURE__ */ jsx13(
|
|
402
482
|
"div",
|
|
403
483
|
{
|
|
404
|
-
className: cn(
|
|
484
|
+
className: cn(
|
|
485
|
+
"flex flex-col rounded-lg border border-border-default bg-bg-elevated p-2 shadow-lg",
|
|
486
|
+
/*
|
|
487
|
+
* A divider in a menu runs the width of the rows it separates.
|
|
488
|
+
*
|
|
489
|
+
* `Divider` is inset 12px each side, which is right in a page and
|
|
490
|
+
* wrong here: measured in a 180px menu, the rule started 21px from the
|
|
491
|
+
* panel's edge where the rows start at 9px — 12px narrower on each side
|
|
492
|
+
* than the things it divides. `IdentityMenu` was already cancelling it
|
|
493
|
+
* by hand with `mx-0`, which is the sign it belonged here (Katerina,
|
|
494
|
+
* 2026-09-08). Direct children only, so a divider a caller puts inside
|
|
495
|
+
* a row keeps its own spacing.
|
|
496
|
+
*/
|
|
497
|
+
"[&>[role=separator]]:mx-0",
|
|
498
|
+
className
|
|
499
|
+
),
|
|
405
500
|
...props,
|
|
406
501
|
children
|
|
407
502
|
}
|
|
408
503
|
);
|
|
409
504
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}, [closeOnLeave, onClose]);
|
|
419
|
-
useEffect(() => () => clearTimeout(leaveTimer.current), []);
|
|
420
|
-
const portalled = Boolean(anchor || position);
|
|
421
|
-
const [placed, setPlaced] = useState3(null);
|
|
422
|
-
useEffect(() => {
|
|
423
|
-
const onDown = (event) => {
|
|
424
|
-
if (!ref.current?.contains(event.target)) onClose();
|
|
425
|
-
};
|
|
426
|
-
const onKey = (event) => {
|
|
427
|
-
if (event.key === "Escape") onClose();
|
|
428
|
-
};
|
|
429
|
-
document.addEventListener("mousedown", onDown);
|
|
430
|
-
document.addEventListener("keydown", onKey);
|
|
431
|
-
return () => {
|
|
432
|
-
document.removeEventListener("mousedown", onDown);
|
|
433
|
-
document.removeEventListener("keydown", onKey);
|
|
434
|
-
};
|
|
435
|
-
}, [onClose]);
|
|
436
|
-
const posLeft = position && "left" in position ? position.left : void 0;
|
|
437
|
-
const posRight = position && "right" in position ? position.right : void 0;
|
|
438
|
-
const posTop = position?.top;
|
|
439
|
-
useLayoutEffect2(() => {
|
|
440
|
-
if (!portalled || !ref.current) return;
|
|
441
|
-
const viewport = { width: window.innerWidth, height: window.innerHeight };
|
|
442
|
-
const menu = ref.current;
|
|
443
|
-
if (anchor) {
|
|
444
|
-
const rect = anchor instanceof Element ? anchor.getBoundingClientRect() : anchor;
|
|
445
|
-
const left = align === "right" ? rect.right - menu.offsetWidth : rect.left;
|
|
446
|
-
setPlaced(
|
|
447
|
-
fitMenu({
|
|
448
|
-
anchor: { left, top: rect.top, bottom: rect.bottom },
|
|
449
|
-
menu: { width: menu.offsetWidth, contentHeight: menu.scrollHeight },
|
|
450
|
-
viewport
|
|
451
|
-
})
|
|
452
|
-
);
|
|
453
|
-
} else if (posTop !== void 0) {
|
|
454
|
-
const left = posLeft ?? viewport.width - (posRight ?? 0) - menu.offsetWidth;
|
|
455
|
-
setPlaced(clampBox({ box: { left, top: posTop, width: menu.offsetWidth, height: menu.offsetHeight }, viewport }));
|
|
456
|
-
}
|
|
457
|
-
}, [portalled, anchor, align, posLeft, posRight, posTop]);
|
|
458
|
-
useEffect(() => {
|
|
459
|
-
if (!anchor) return;
|
|
460
|
-
const onScroll = (event) => {
|
|
461
|
-
if (event.target instanceof Node && ref.current?.contains(event.target)) return;
|
|
462
|
-
onClose();
|
|
463
|
-
};
|
|
464
|
-
window.addEventListener("resize", onClose);
|
|
465
|
-
window.addEventListener("scroll", onScroll, true);
|
|
466
|
-
return () => {
|
|
467
|
-
window.removeEventListener("resize", onClose);
|
|
468
|
-
window.removeEventListener("scroll", onScroll, true);
|
|
469
|
-
};
|
|
470
|
-
}, [anchor, onClose]);
|
|
471
|
-
const style = portalled ? placed ? { left: placed.left, top: placed.top, bottom: placed.bottom, maxHeight: placed.maxHeight } : (
|
|
472
|
-
// The provisional render: measured by the layout effect, never seen.
|
|
473
|
-
{ left: 0, top: 0, visibility: "hidden" }
|
|
474
|
-
) : void 0;
|
|
475
|
-
const node = /* @__PURE__ */ jsx12(MenuHoverContext.Provider, { value: { hold, release }, children: /* @__PURE__ */ jsx12(
|
|
476
|
-
MenuPanel,
|
|
505
|
+
var isProduction = () => globalThis.process?.env?.NODE_ENV === "production";
|
|
506
|
+
var GAP3 = 4;
|
|
507
|
+
var VIEWPORT_PAD2 = 8;
|
|
508
|
+
var HOVER_OPEN_DELAY = 0;
|
|
509
|
+
var HOVER_CLOSE_DELAY = 150;
|
|
510
|
+
function Menu({ trigger, align = "left", openOnHover = false, open, onOpenChange, actionsRef, children, className }) {
|
|
511
|
+
return /* @__PURE__ */ jsxs7(
|
|
512
|
+
BaseMenu.Root,
|
|
477
513
|
{
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
514
|
+
open,
|
|
515
|
+
onOpenChange: onOpenChange ? (next) => onOpenChange(next) : void 0,
|
|
516
|
+
actionsRef,
|
|
517
|
+
modal: false,
|
|
518
|
+
children: [
|
|
519
|
+
/* @__PURE__ */ jsx13(
|
|
520
|
+
BaseMenu.Trigger,
|
|
521
|
+
{
|
|
522
|
+
render: trigger,
|
|
523
|
+
disabled: triggerDisabled(trigger),
|
|
524
|
+
openOnHover,
|
|
525
|
+
delay: HOVER_OPEN_DELAY,
|
|
526
|
+
closeDelay: HOVER_CLOSE_DELAY
|
|
527
|
+
}
|
|
528
|
+
),
|
|
529
|
+
/* @__PURE__ */ jsx13(BaseMenu.Portal, { children: /* @__PURE__ */ jsx13(
|
|
530
|
+
BaseMenu.Positioner,
|
|
531
|
+
{
|
|
532
|
+
side: "bottom",
|
|
533
|
+
align: align === "right" ? "end" : "start",
|
|
534
|
+
sideOffset: GAP3,
|
|
535
|
+
collisionPadding: VIEWPORT_PAD2,
|
|
536
|
+
className: "z-50 data-[anchor-hidden]:hidden",
|
|
537
|
+
children: /* @__PURE__ */ jsx13(
|
|
538
|
+
BaseMenu.Popup,
|
|
539
|
+
{
|
|
540
|
+
"data-interactive": true,
|
|
541
|
+
className: cn("min-w-[180px] max-h-[var(--available-height)] overflow-y-auto outline-none", className),
|
|
542
|
+
render: /* @__PURE__ */ jsx13(MenuPanel, {}),
|
|
543
|
+
children: /* @__PURE__ */ jsx13(MenuContext.Provider, { value: { openOnHover }, children })
|
|
544
|
+
}
|
|
545
|
+
)
|
|
546
|
+
}
|
|
547
|
+
) })
|
|
548
|
+
]
|
|
491
549
|
}
|
|
492
|
-
)
|
|
493
|
-
return portalled ? createPortal2(node, document.body) : node;
|
|
550
|
+
);
|
|
494
551
|
}
|
|
495
552
|
function MenuSub({ label, leading, selected, children, className }) {
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
const [placed, setPlaced] = useState3(null);
|
|
501
|
-
const menuHover = useContext(MenuHoverContext);
|
|
502
|
-
const enter = () => {
|
|
503
|
-
clearTimeout(closeTimer.current);
|
|
504
|
-
menuHover?.hold();
|
|
505
|
-
setOpen(true);
|
|
506
|
-
};
|
|
507
|
-
const leave = () => {
|
|
508
|
-
closeTimer.current = setTimeout(() => setOpen(false), 150);
|
|
509
|
-
menuHover?.release();
|
|
510
|
-
};
|
|
511
|
-
useEffect(() => () => clearTimeout(closeTimer.current), []);
|
|
512
|
-
useLayoutEffect2(() => {
|
|
513
|
-
if (!open || !rowRef.current || !panelRef.current) {
|
|
514
|
-
setPlaced(null);
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
const row = rowRef.current.getBoundingClientRect();
|
|
518
|
-
setPlaced(
|
|
519
|
-
fitSubmenu({
|
|
520
|
-
row: { left: row.left, right: row.right, top: row.top },
|
|
521
|
-
panel: { width: panelRef.current.offsetWidth, height: panelRef.current.offsetHeight },
|
|
522
|
-
viewport: { width: window.innerWidth, height: window.innerHeight }
|
|
523
|
-
})
|
|
553
|
+
const menu = useContext(MenuContext);
|
|
554
|
+
if (!isProduction() && menu?.openOnHover) {
|
|
555
|
+
console.error(
|
|
556
|
+
'[@estiva-app/ui] Menu: `openOnHover` and `MenuSub` cannot be used together \u2014 leaving the submenu panel strands the menu open. Open this menu on a press instead. See Menu.mdx, "Hover-opened menus".'
|
|
524
557
|
);
|
|
525
|
-
}
|
|
526
|
-
return /* @__PURE__ */
|
|
527
|
-
/* @__PURE__ */
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
558
|
+
}
|
|
559
|
+
return /* @__PURE__ */ jsxs7(BaseMenu.SubmenuRoot, { children: [
|
|
560
|
+
/* @__PURE__ */ jsx13(
|
|
561
|
+
BaseMenu.SubmenuTrigger,
|
|
562
|
+
{
|
|
563
|
+
openOnHover: true,
|
|
564
|
+
delay: 0,
|
|
565
|
+
closeDelay: 150,
|
|
566
|
+
nativeButton: true,
|
|
567
|
+
render: /* @__PURE__ */ jsx13("button", { type: "button" }),
|
|
568
|
+
className: menuItemClassName({ size: "default", selected }),
|
|
569
|
+
children: /* @__PURE__ */ jsx13(MenuItemBody, { label, leading, submenu: true })
|
|
570
|
+
}
|
|
571
|
+
),
|
|
572
|
+
/* @__PURE__ */ jsx13(BaseMenu.Portal, { children: /* @__PURE__ */ jsx13(
|
|
573
|
+
BaseMenu.Positioner,
|
|
574
|
+
{
|
|
575
|
+
side: "inline-end",
|
|
576
|
+
align: "start",
|
|
577
|
+
sideOffset: GAP3,
|
|
578
|
+
collisionPadding: VIEWPORT_PAD2,
|
|
579
|
+
className: "z-50 data-[anchor-hidden]:hidden",
|
|
580
|
+
children: /* @__PURE__ */ jsx13(BaseMenu.Popup, { className: cn("w-[160px]", className), "data-interactive": true, render: /* @__PURE__ */ jsx13(MenuPanel, {}), children })
|
|
581
|
+
}
|
|
582
|
+
) })
|
|
583
|
+
] });
|
|
584
|
+
}
|
|
585
|
+
function menuItemClassName({ size, selected, className }) {
|
|
586
|
+
return cn(
|
|
587
|
+
// shrink-0: a menu is a flex column that scrolls at its max height,
|
|
588
|
+
// and a flex child shrinks before its container does — so every row
|
|
589
|
+
// in an overflowing menu was squashed to its `min-h`, and a row given
|
|
590
|
+
// an explicit height silently lost it (Peek's `[` menu: h-12 rows
|
|
591
|
+
// measured 40px). The same fix NavItem took on 2026-09-02.
|
|
592
|
+
// `group`: the `hint` slot reveals itself from this row's own :hover,
|
|
593
|
+
// so the hint and the fill are one CSS state change, not two engines.
|
|
594
|
+
//
|
|
595
|
+
// No transition on the fill (Katerina, 2026-09-05). It faded over
|
|
596
|
+
// 150ms, and anything appearing with it had to fade too or arrive
|
|
597
|
+
// ahead of it — which, sweeping a pointer down a list, read as the
|
|
598
|
+
// hint flickering in and out. Both are instant now: they still change
|
|
599
|
+
// on exactly the same :hover, so they cannot come apart, and a row
|
|
600
|
+
// lights and unlights crisply as the pointer crosses it.
|
|
601
|
+
"group flex w-full shrink-0 cursor-pointer items-center rounded-lg text-left outline-none hover:bg-bg-hover data-[highlighted]:bg-bg-hover",
|
|
602
|
+
// tall: as tall as its content, never shorter than 40px (Katerina,
|
|
603
|
+
// 2026-09-01) — a single-line picker row sits at 40, a row with a
|
|
604
|
+
// 32px face and a role line comes out at its natural 48. One rule,
|
|
605
|
+
// not a hand-picked height per file.
|
|
606
|
+
size === "tall" ? "min-h-10 gap-3 px-3 py-1.5" : "gap-2 px-2 py-1.5",
|
|
607
|
+
selected && "bg-bg-hover",
|
|
608
|
+
className
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
function MenuItemBody({ label, children, size = "default", description, leading, trailing, hint, shortcut, submenu, destructive, selected }) {
|
|
612
|
+
const edge = trailing ?? (shortcut ? /* @__PURE__ */ jsx13(Kbd, { children: shortcut }) : submenu ? /* @__PURE__ */ jsx13(IconChevronRight, { size: 16, stroke: 1.5, className: "shrink-0 text-text-muted" }) : null);
|
|
613
|
+
return /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
614
|
+
leading && /* @__PURE__ */ jsx13("span", { className: "flex shrink-0 items-center", children: leading }),
|
|
615
|
+
children ?? /* @__PURE__ */ jsxs7("span", { className: cn("flex min-w-0 flex-1 flex-col", size === "tall" && "gap-[2px]"), children: [
|
|
616
|
+
/* @__PURE__ */ jsx13("span", { className: cn("truncate text-[14px] leading-[140%]", destructive ? "text-error-default" : "text-text-primary"), children: label }),
|
|
617
|
+
description && /* @__PURE__ */ jsx13("span", { className: "truncate text-[12px] leading-[120%] text-text-secondary", children: description })
|
|
618
|
+
] }),
|
|
619
|
+
(edge || hint) && // One grid cell holding both, right-aligned: the slot is as wide as
|
|
620
|
+
// the wider of the two and never changes, so revealing the hint moves
|
|
621
|
+
// nothing. No transition here either — the hint switches on the same
|
|
622
|
+
// :hover / `selected` as the fill, in the same frame.
|
|
623
|
+
/* @__PURE__ */ jsxs7("span", { className: "grid shrink-0 items-center justify-items-end [&>*]:col-start-1 [&>*]:row-start-1", children: [
|
|
624
|
+
edge && /* @__PURE__ */ jsx13("span", { className: cn("flex items-center", hint && "group-hover:opacity-0 group-data-[highlighted]:opacity-0", hint && selected && "opacity-0"), children: edge }),
|
|
625
|
+
hint && /* @__PURE__ */ jsx13("span", { className: cn("flex items-center opacity-0 group-hover:opacity-100 group-data-[highlighted]:opacity-100", selected && "opacity-100"), children: hint })
|
|
626
|
+
] })
|
|
544
627
|
] });
|
|
545
628
|
}
|
|
546
629
|
function MenuItem({ label, children, size = "default", description, leading, trailing, hint, shortcut, submenu, destructive, selected, className, ...props }) {
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
"button",
|
|
630
|
+
const body = /* @__PURE__ */ jsx13(
|
|
631
|
+
MenuItemBody,
|
|
550
632
|
{
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
selected && "bg-bg-hover",
|
|
575
|
-
className
|
|
576
|
-
),
|
|
633
|
+
label,
|
|
634
|
+
size,
|
|
635
|
+
description,
|
|
636
|
+
leading,
|
|
637
|
+
trailing,
|
|
638
|
+
hint,
|
|
639
|
+
shortcut,
|
|
640
|
+
submenu,
|
|
641
|
+
destructive,
|
|
642
|
+
selected,
|
|
643
|
+
children
|
|
644
|
+
}
|
|
645
|
+
);
|
|
646
|
+
const rowClassName = menuItemClassName({ size, selected, className });
|
|
647
|
+
if (!useContext(MenuContext)) {
|
|
648
|
+
return /* @__PURE__ */ jsx13("button", { type: "button", className: rowClassName, ...props, children: body });
|
|
649
|
+
}
|
|
650
|
+
return /* @__PURE__ */ jsx13(
|
|
651
|
+
BaseMenu.Item,
|
|
652
|
+
{
|
|
653
|
+
nativeButton: true,
|
|
654
|
+
render: /* @__PURE__ */ jsx13("button", { type: "button" }),
|
|
655
|
+
className: rowClassName,
|
|
577
656
|
...props,
|
|
578
|
-
children:
|
|
579
|
-
leading && /* @__PURE__ */ jsx12("span", { className: "flex shrink-0 items-center", children: leading }),
|
|
580
|
-
children ?? /* @__PURE__ */ jsxs6("span", { className: cn("flex min-w-0 flex-1 flex-col", size === "tall" && "gap-[2px]"), children: [
|
|
581
|
-
/* @__PURE__ */ jsx12("span", { className: cn("truncate text-[14px] leading-[140%]", destructive ? "text-error-default" : "text-text-primary"), children: label }),
|
|
582
|
-
description && /* @__PURE__ */ jsx12("span", { className: "truncate text-[12px] leading-[120%] text-text-secondary", children: description })
|
|
583
|
-
] }),
|
|
584
|
-
(edge || hint) && // One grid cell holding both, right-aligned: the slot is as wide as
|
|
585
|
-
// the wider of the two and never changes, so revealing the hint moves
|
|
586
|
-
// nothing. No transition here either — the hint switches on the same
|
|
587
|
-
// :hover / `selected` as the fill, in the same frame.
|
|
588
|
-
/* @__PURE__ */ jsxs6("span", { className: "grid shrink-0 items-center justify-items-end [&>*]:col-start-1 [&>*]:row-start-1", children: [
|
|
589
|
-
edge && /* @__PURE__ */ jsx12("span", { className: cn("flex items-center", hint && "group-hover:opacity-0", hint && selected && "opacity-0"), children: edge }),
|
|
590
|
-
hint && /* @__PURE__ */ jsx12("span", { className: cn("flex items-center opacity-0 group-hover:opacity-100", selected && "opacity-100"), children: hint })
|
|
591
|
-
] })
|
|
592
|
-
]
|
|
657
|
+
children: body
|
|
593
658
|
}
|
|
594
659
|
);
|
|
595
660
|
}
|
|
596
661
|
function EnterHint({ target }) {
|
|
597
|
-
return /* @__PURE__ */
|
|
598
|
-
/* @__PURE__ */
|
|
599
|
-
target && /* @__PURE__ */
|
|
662
|
+
return /* @__PURE__ */ jsxs7("span", { className: "flex shrink-0 items-center gap-1.5 text-text-muted", children: [
|
|
663
|
+
/* @__PURE__ */ jsx13(Kbd, { children: "\u21A9 Enter" }),
|
|
664
|
+
target && /* @__PURE__ */ jsx13("span", { className: "text-[9px] font-medium leading-[115%] signal:font-mono signal:text-[9.5px] signal:tracking-[0.04em]", children: target })
|
|
600
665
|
] });
|
|
601
666
|
}
|
|
602
667
|
function MenuSection({ label, children, className }) {
|
|
603
|
-
|
|
604
|
-
|
|
668
|
+
const inMenu = useContext(MenuContext) !== null;
|
|
669
|
+
const heading = /* @__PURE__ */ jsx13("div", { className: cn("flex h-8 items-center px-2", className), children: /* @__PURE__ */ jsx13(SectionLabel, { className: "text-text-secondary", children: label }) });
|
|
670
|
+
if (!inMenu) {
|
|
671
|
+
return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col", children: [
|
|
672
|
+
heading,
|
|
673
|
+
children
|
|
674
|
+
] });
|
|
675
|
+
}
|
|
676
|
+
return /* @__PURE__ */ jsxs7(BaseMenu.Group, { className: "flex flex-col", children: [
|
|
677
|
+
/* @__PURE__ */ jsx13(BaseMenu.GroupLabel, { render: heading }),
|
|
605
678
|
children
|
|
606
679
|
] });
|
|
607
680
|
}
|
|
608
681
|
function MenuRow({ children, className }) {
|
|
609
|
-
return /* @__PURE__ */
|
|
682
|
+
return /* @__PURE__ */ jsx13("div", { className: cn("flex items-center gap-2 px-2 py-1.5", className), children });
|
|
610
683
|
}
|
|
611
684
|
|
|
612
685
|
// src/ChipInput.tsx
|
|
613
|
-
import { jsx as
|
|
686
|
+
import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
614
687
|
function InputChip({ label, leading, onRemove, className }) {
|
|
615
|
-
return /* @__PURE__ */
|
|
688
|
+
return /* @__PURE__ */ jsxs8(
|
|
616
689
|
"div",
|
|
617
690
|
{
|
|
618
691
|
className: cn(
|
|
@@ -628,9 +701,9 @@ function InputChip({ label, leading, onRemove, className }) {
|
|
|
628
701
|
className
|
|
629
702
|
),
|
|
630
703
|
children: [
|
|
631
|
-
leading && /* @__PURE__ */
|
|
632
|
-
/* @__PURE__ */
|
|
633
|
-
onRemove && /* @__PURE__ */
|
|
704
|
+
leading && /* @__PURE__ */ jsx14("span", { className: "flex shrink-0 items-center", children: leading }),
|
|
705
|
+
/* @__PURE__ */ jsx14("span", { className: "text-caption font-medium text-text-primary", children: label }),
|
|
706
|
+
onRemove && /* @__PURE__ */ jsx14(
|
|
634
707
|
"button",
|
|
635
708
|
{
|
|
636
709
|
type: "button",
|
|
@@ -640,7 +713,7 @@ function InputChip({ label, leading, onRemove, className }) {
|
|
|
640
713
|
},
|
|
641
714
|
className: "size-4 flex items-center justify-center rounded-full hover:bg-bg-hover text-text-secondary",
|
|
642
715
|
"aria-label": `Remove ${label}`,
|
|
643
|
-
children: /* @__PURE__ */
|
|
716
|
+
children: /* @__PURE__ */ jsx14(IconX2, { size: 10, stroke: 1.5 })
|
|
644
717
|
}
|
|
645
718
|
)
|
|
646
719
|
]
|
|
@@ -655,14 +728,15 @@ function ChipInput({
|
|
|
655
728
|
autoFocus,
|
|
656
729
|
excludeIds = [],
|
|
657
730
|
chipLeading,
|
|
658
|
-
rowLeading
|
|
731
|
+
rowLeading,
|
|
732
|
+
...aria
|
|
659
733
|
}) {
|
|
660
|
-
const [query, setQuery] =
|
|
661
|
-
const [highlight, setHighlight] =
|
|
662
|
-
const [isFocused, setIsFocused] =
|
|
663
|
-
const [anchorRect, setAnchorRect] =
|
|
664
|
-
const wrapperRef =
|
|
665
|
-
const inputRef =
|
|
734
|
+
const [query, setQuery] = useState2("");
|
|
735
|
+
const [highlight, setHighlight] = useState2(0);
|
|
736
|
+
const [isFocused, setIsFocused] = useState2(false);
|
|
737
|
+
const [anchorRect, setAnchorRect] = useState2(null);
|
|
738
|
+
const wrapperRef = useRef(null);
|
|
739
|
+
const inputRef = useRef(null);
|
|
666
740
|
const matches = useMemo(() => {
|
|
667
741
|
const selectedIds = new Set(value.map((o) => o.id));
|
|
668
742
|
const excludedIds = new Set(excludeIds);
|
|
@@ -674,11 +748,11 @@ function ChipInput({
|
|
|
674
748
|
return o.label.toLowerCase().includes(q) || (o.description ?? "").toLowerCase().includes(q);
|
|
675
749
|
});
|
|
676
750
|
}, [query, value, excludeIds, options]);
|
|
677
|
-
|
|
751
|
+
useEffect(() => {
|
|
678
752
|
setHighlight(0);
|
|
679
753
|
}, [query, matches.length]);
|
|
680
754
|
const showDropdown = isFocused && query.trim().length > 0 && matches.length > 0;
|
|
681
|
-
|
|
755
|
+
useLayoutEffect(() => {
|
|
682
756
|
if (!showDropdown) return;
|
|
683
757
|
const update = () => {
|
|
684
758
|
if (wrapperRef.current) setAnchorRect(wrapperRef.current.getBoundingClientRect());
|
|
@@ -728,16 +802,16 @@ function ChipInput({
|
|
|
728
802
|
}
|
|
729
803
|
}
|
|
730
804
|
}
|
|
731
|
-
return /* @__PURE__ */
|
|
732
|
-
/* @__PURE__ */
|
|
805
|
+
return /* @__PURE__ */ jsxs8("div", { className: "relative", children: [
|
|
806
|
+
/* @__PURE__ */ jsxs8(
|
|
733
807
|
"div",
|
|
734
808
|
{
|
|
735
809
|
ref: wrapperRef,
|
|
736
810
|
className: "bg-bg-inset border border-border-default hover:border-border-strong focus-within:border-border-focus focus-within:hover:border-border-focus rounded-lg px-3 py-1.5 flex flex-wrap items-center gap-1.5 transition-colors min-h-[38px] cursor-text signal:transition-shadow signal:focus-within:shadow-focus-ring",
|
|
737
811
|
onClick: () => inputRef.current?.focus(),
|
|
738
812
|
children: [
|
|
739
|
-
value.map((o) => /* @__PURE__ */
|
|
740
|
-
/* @__PURE__ */
|
|
813
|
+
value.map((o) => /* @__PURE__ */ jsx14(InputChip, { label: o.label, leading: chipLeading?.(o), onRemove: () => removeOption(o.id) }, o.id)),
|
|
814
|
+
/* @__PURE__ */ jsx14(
|
|
741
815
|
"input",
|
|
742
816
|
{
|
|
743
817
|
ref: inputRef,
|
|
@@ -751,14 +825,15 @@ function ChipInput({
|
|
|
751
825
|
setTimeout(() => setIsFocused(false), 150);
|
|
752
826
|
},
|
|
753
827
|
placeholder: value.length === 0 ? placeholder : "",
|
|
828
|
+
"aria-required": aria["aria-required"],
|
|
754
829
|
className: "flex-1 min-w-[120px] bg-transparent text-body-2 text-text-primary placeholder:text-text-muted outline-none border-none"
|
|
755
830
|
}
|
|
756
831
|
)
|
|
757
832
|
]
|
|
758
833
|
}
|
|
759
834
|
),
|
|
760
|
-
showDropdown && anchorRect &&
|
|
761
|
-
/* @__PURE__ */
|
|
835
|
+
showDropdown && anchorRect && createPortal(
|
|
836
|
+
/* @__PURE__ */ jsx14(
|
|
762
837
|
"div",
|
|
763
838
|
{
|
|
764
839
|
className: "fixed z-[60] overflow-y-auto bg-bg-elevated border border-border-default rounded-lg shadow-lg",
|
|
@@ -771,7 +846,7 @@ function ChipInput({
|
|
|
771
846
|
}),
|
|
772
847
|
width: anchorRect.width
|
|
773
848
|
},
|
|
774
|
-
children: matches.map((o, i) => /* @__PURE__ */
|
|
849
|
+
children: matches.map((o, i) => /* @__PURE__ */ jsx14(
|
|
775
850
|
MenuItem,
|
|
776
851
|
{
|
|
777
852
|
size: "tall",
|
|
@@ -795,51 +870,8 @@ function ChipInput({
|
|
|
795
870
|
] });
|
|
796
871
|
}
|
|
797
872
|
|
|
798
|
-
// src/IconButton.tsx
|
|
799
|
-
import { Button as BaseButton2 } from "@base-ui/react/button";
|
|
800
|
-
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
801
|
-
function IconButton({
|
|
802
|
-
variant = "muted",
|
|
803
|
-
className,
|
|
804
|
-
children,
|
|
805
|
-
disabled,
|
|
806
|
-
disabledReason,
|
|
807
|
-
tooltip,
|
|
808
|
-
tooltipShortcut,
|
|
809
|
-
tooltipPlacement,
|
|
810
|
-
type = "button",
|
|
811
|
-
...props
|
|
812
|
-
}) {
|
|
813
|
-
const button = /* @__PURE__ */ jsx14(
|
|
814
|
-
BaseButton2,
|
|
815
|
-
{
|
|
816
|
-
type,
|
|
817
|
-
disabled: disabled || !!disabledReason,
|
|
818
|
-
focusableWhenDisabled: !!disabledReason,
|
|
819
|
-
className: (state) => cn(
|
|
820
|
-
"flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 cursor-pointer",
|
|
821
|
-
!state.disabled && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse",
|
|
822
|
-
!state.disabled && variant === "muted" && "text-text-secondary hover:bg-bg-hover hover:text-text-primary",
|
|
823
|
-
!state.disabled && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-secondary",
|
|
824
|
-
state.disabled && variant === "primary" && "bg-bg-disabled text-text-disabled",
|
|
825
|
-
state.disabled && variant === "muted" && "text-text-disabled",
|
|
826
|
-
state.disabled && variant === "outlined" && "border border-border-default text-text-disabled",
|
|
827
|
-
state.disabled && "pointer-events-none cursor-not-allowed",
|
|
828
|
-
className
|
|
829
|
-
),
|
|
830
|
-
...props,
|
|
831
|
-
children
|
|
832
|
-
}
|
|
833
|
-
);
|
|
834
|
-
const label = disabledReason ?? tooltip;
|
|
835
|
-
if (label) {
|
|
836
|
-
return /* @__PURE__ */ jsx14(WithTooltip, { label, shortcut: disabledReason ? void 0 : tooltipShortcut, placement: tooltipPlacement, children: button });
|
|
837
|
-
}
|
|
838
|
-
return button;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
873
|
// src/IdentityMenu.tsx
|
|
842
|
-
import {
|
|
874
|
+
import { useCallback, useRef as useRef2 } from "react";
|
|
843
875
|
|
|
844
876
|
// src/Divider.tsx
|
|
845
877
|
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
@@ -855,13 +887,13 @@ function Divider({ orientation = "horizontal", className }) {
|
|
|
855
887
|
}
|
|
856
888
|
|
|
857
889
|
// src/Person.tsx
|
|
858
|
-
import { jsx as jsx16, jsxs as
|
|
890
|
+
import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
859
891
|
function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
|
|
860
892
|
return (
|
|
861
893
|
// gap-2: the face-to-name distance is one rule (8px) wherever a person
|
|
862
894
|
// appears — Katerina, 2026-09-01, set on Person so PersonTrigger and every
|
|
863
895
|
// row agree by construction.
|
|
864
|
-
/* @__PURE__ */
|
|
896
|
+
/* @__PURE__ */ jsxs9("span", { className: cn("flex min-w-0 items-center gap-2", className), children: [
|
|
865
897
|
/* @__PURE__ */ jsx16(Avatar, { name, src: picture, size }),
|
|
866
898
|
/* @__PURE__ */ jsx16("span", { className: cn("truncate", !name && "text-text-muted"), children: name ?? fallback })
|
|
867
899
|
] })
|
|
@@ -871,7 +903,7 @@ function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
|
|
|
871
903
|
// src/PersonTrigger.tsx
|
|
872
904
|
import { Button as BaseButton3 } from "@base-ui/react/button";
|
|
873
905
|
import { IconChevronDown } from "@tabler/icons-react";
|
|
874
|
-
import { jsx as jsx17, jsxs as
|
|
906
|
+
import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
875
907
|
function PersonTrigger({ name, picture, fallback, size, open = false, compact = false, className, ...props }) {
|
|
876
908
|
if (compact) {
|
|
877
909
|
return /* @__PURE__ */ jsx17(
|
|
@@ -880,13 +912,14 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
|
|
|
880
912
|
type: "button",
|
|
881
913
|
"aria-haspopup": "menu",
|
|
882
914
|
"aria-expanded": open,
|
|
883
|
-
|
|
915
|
+
"aria-label": props["aria-label"] ?? name ?? fallback,
|
|
916
|
+
className: cn("cursor-pointer rounded-full", className),
|
|
884
917
|
...props,
|
|
885
918
|
children: /* @__PURE__ */ jsx17(Avatar, { name, src: picture, size: size ?? 36 })
|
|
886
919
|
}
|
|
887
920
|
);
|
|
888
921
|
}
|
|
889
|
-
return /* @__PURE__ */
|
|
922
|
+
return /* @__PURE__ */ jsxs10(
|
|
890
923
|
BaseButton3,
|
|
891
924
|
{
|
|
892
925
|
type: "button",
|
|
@@ -895,6 +928,11 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
|
|
|
895
928
|
className: cn(
|
|
896
929
|
"flex h-8 cursor-pointer items-center gap-1.5 rounded-md pl-1.5 pr-1.5 text-body-2 text-text-primary transition-colors hover:bg-bg-hover",
|
|
897
930
|
open && "bg-bg-hover",
|
|
931
|
+
// Under a `Menu` the open state is Base UI's, not a prop: the trigger
|
|
932
|
+
// carries `data-popup-open` while its menu is up, and sets its own
|
|
933
|
+
// `aria-expanded`. Both spellings hold the fill, so this works whether
|
|
934
|
+
// the caller drives it or the menu does.
|
|
935
|
+
"data-[popup-open]:bg-bg-hover",
|
|
898
936
|
className
|
|
899
937
|
),
|
|
900
938
|
...props,
|
|
@@ -907,30 +945,34 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
|
|
|
907
945
|
}
|
|
908
946
|
|
|
909
947
|
// src/IdentityMenu.tsx
|
|
910
|
-
import { Fragment, jsx as jsx18, jsxs as
|
|
911
|
-
function IdentityPanel({
|
|
948
|
+
import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
949
|
+
function IdentityPanel({ className, onClose = () => {
|
|
950
|
+
}, ...rest }) {
|
|
951
|
+
return /* @__PURE__ */ jsx18(MenuPanel, { className: cn("w-72", className), children: /* @__PURE__ */ jsx18(IdentityRows, { ...rest, onClose }) });
|
|
952
|
+
}
|
|
953
|
+
function IdentityRows({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, onClose, children }) {
|
|
912
954
|
const act = (action) => () => {
|
|
913
955
|
onClose();
|
|
914
956
|
action();
|
|
915
957
|
};
|
|
916
958
|
const appRows = typeof children === "function" ? children(onClose) : children;
|
|
917
|
-
return /* @__PURE__ */
|
|
918
|
-
/* @__PURE__ */
|
|
959
|
+
return /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
960
|
+
/* @__PURE__ */ jsxs11(MenuSection, { label: signedIn ? "Signed in as" : "Acting as", children: [
|
|
919
961
|
/* @__PURE__ */ jsx18(MenuRow, { children: /* @__PURE__ */ jsx18(Person, { name: me.name, picture: me.picture, fallback: "Anonymous", size: 28, className: "text-body-2-strong" }) }),
|
|
920
962
|
/* @__PURE__ */ jsx18(Note, { children: signedIn ? "Your Estiva ID. The same person you are in every Estiva app." : "A key held by this browser only. Nobody else knows who you are." }),
|
|
921
|
-
me.email && /* @__PURE__ */
|
|
963
|
+
me.email && /* @__PURE__ */ jsxs11(Note, { children: [
|
|
922
964
|
me.email,
|
|
923
965
|
" \xB7 known to Estiva, never published to the relay"
|
|
924
966
|
] })
|
|
925
967
|
] }),
|
|
926
|
-
relayUrl && /* @__PURE__ */
|
|
968
|
+
relayUrl && /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
927
969
|
/* @__PURE__ */ jsx18(Divider, { className: "mx-0 my-2" }),
|
|
928
|
-
/* @__PURE__ */
|
|
970
|
+
/* @__PURE__ */ jsxs11(MenuSection, { label: "Workspace", children: [
|
|
929
971
|
/* @__PURE__ */ jsx18(MenuRow, { children: /* @__PURE__ */ jsx18("span", { className: "break-all font-mono text-caption text-text-primary", children: relayUrl }) }),
|
|
930
972
|
/* @__PURE__ */ jsx18(Note, { children: "Everyone on this relay shares this workspace, in every app." })
|
|
931
973
|
] })
|
|
932
974
|
] }),
|
|
933
|
-
appRows && /* @__PURE__ */
|
|
975
|
+
appRows && /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
934
976
|
/* @__PURE__ */ jsx18(Divider, { className: "mx-0 my-2" }),
|
|
935
977
|
appRows
|
|
936
978
|
] }),
|
|
@@ -947,280 +989,178 @@ function IdentityPanel({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, o
|
|
|
947
989
|
] });
|
|
948
990
|
}
|
|
949
991
|
function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, compact = false, className, children }) {
|
|
950
|
-
const
|
|
951
|
-
const
|
|
952
|
-
return
|
|
953
|
-
/*
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
compact,
|
|
960
|
-
size: compact ? 36 : void 0,
|
|
961
|
-
open,
|
|
962
|
-
onMouseDown: (event) => event.stopPropagation(),
|
|
963
|
-
onClick: () => setOpen((value) => !value),
|
|
964
|
-
"aria-label": compact ? "Account menu" : void 0
|
|
965
|
-
}
|
|
966
|
-
),
|
|
967
|
-
open && /* @__PURE__ */ jsx18(
|
|
968
|
-
IdentityPanel,
|
|
992
|
+
const actions = useRef2(null);
|
|
993
|
+
const close = useCallback(() => actions.current?.close(), []);
|
|
994
|
+
return (
|
|
995
|
+
/* The wrapper carries the caller's `className` and nothing else. It used
|
|
996
|
+
to be `relative`, because the panel was positioned against it; the panel
|
|
997
|
+
hangs from the trigger and portals now, so a positioning context here
|
|
998
|
+
would only be a lie about what this box does. */
|
|
999
|
+
/* @__PURE__ */ jsx18("div", { className, children: /* @__PURE__ */ jsx18(
|
|
1000
|
+
Menu,
|
|
969
1001
|
{
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1002
|
+
align: "right",
|
|
1003
|
+
actionsRef: actions,
|
|
1004
|
+
className: "w-72",
|
|
1005
|
+
trigger: /* @__PURE__ */ jsx18(
|
|
1006
|
+
PersonTrigger,
|
|
1007
|
+
{
|
|
1008
|
+
name: me.name,
|
|
1009
|
+
picture: me.picture,
|
|
1010
|
+
fallback: "Anonymous",
|
|
1011
|
+
compact,
|
|
1012
|
+
size: compact ? 36 : void 0,
|
|
1013
|
+
"aria-label": compact ? "Account menu" : void 0
|
|
1014
|
+
}
|
|
1015
|
+
),
|
|
1016
|
+
children: /* @__PURE__ */ jsx18(
|
|
1017
|
+
IdentityRows,
|
|
1018
|
+
{
|
|
1019
|
+
me,
|
|
1020
|
+
signedIn,
|
|
1021
|
+
relayUrl,
|
|
1022
|
+
idBase,
|
|
1023
|
+
onCopyKey,
|
|
1024
|
+
onSignOut,
|
|
1025
|
+
onClose: close,
|
|
1026
|
+
children
|
|
1027
|
+
}
|
|
1028
|
+
)
|
|
979
1029
|
}
|
|
980
|
-
)
|
|
981
|
-
|
|
1030
|
+
) })
|
|
1031
|
+
);
|
|
982
1032
|
}
|
|
983
1033
|
function Note({ children }) {
|
|
984
1034
|
return /* @__PURE__ */ jsx18("span", { className: "px-2 pb-1.5 text-caption text-text-secondary", children });
|
|
985
1035
|
}
|
|
986
1036
|
|
|
987
1037
|
// src/Field.tsx
|
|
988
|
-
import {
|
|
989
|
-
import {
|
|
990
|
-
|
|
991
|
-
function
|
|
992
|
-
const
|
|
993
|
-
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
children: [
|
|
1005
|
-
label,
|
|
1006
|
-
required && /* @__PURE__ */ jsx19("span", { className: "text-error-default ml-0.5", children: "*" })
|
|
1007
|
-
]
|
|
1008
|
-
}
|
|
1009
|
-
),
|
|
1010
|
-
/* @__PURE__ */ jsx19(FieldControlIdContext.Provider, { value: id, children })
|
|
1038
|
+
import { cloneElement, isValidElement } from "react";
|
|
1039
|
+
import { Field as BaseField } from "@base-ui/react/field";
|
|
1040
|
+
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1041
|
+
function Field({ label, required = false, helper, error, children }) {
|
|
1042
|
+
const control = required && isValidElement(children) ? cloneElement(children, {
|
|
1043
|
+
"aria-required": children.props["aria-required"] ?? true
|
|
1044
|
+
}) : children;
|
|
1045
|
+
return /* @__PURE__ */ jsxs12(BaseField.Root, { invalid: !!error, className: "flex flex-col gap-2", children: [
|
|
1046
|
+
/* @__PURE__ */ jsxs12(BaseField.Label, { className: cn("text-input-label text-text-primary", required && "flex items-center"), children: [
|
|
1047
|
+
label,
|
|
1048
|
+
required && /* @__PURE__ */ jsx19("span", { "aria-hidden": "true", className: "text-error-default ml-0.5", children: "*" })
|
|
1049
|
+
] }),
|
|
1050
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1.5", children: [
|
|
1051
|
+
control,
|
|
1052
|
+
error != null ? /* @__PURE__ */ jsx19(BaseField.Error, { match: true, className: "text-caption text-error-default", children: error }) : helper != null ? /* @__PURE__ */ jsx19(BaseField.Description, { className: "text-caption text-text-muted", children: helper }) : null
|
|
1053
|
+
] })
|
|
1011
1054
|
] });
|
|
1012
1055
|
}
|
|
1013
1056
|
|
|
1014
1057
|
// src/Select.tsx
|
|
1015
1058
|
import { IconCheck as IconCheck2, IconChevronDown as IconChevronDown2 } from "@tabler/icons-react";
|
|
1016
|
-
import {
|
|
1017
|
-
import {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
const triggerRef = useRef5(null);
|
|
1022
|
-
const menuRef = useRef5(null);
|
|
1023
|
-
const [rect, setRect] = useState6(null);
|
|
1024
|
-
const [placement, setPlacement] = useState6(null);
|
|
1025
|
-
const open = rect !== null;
|
|
1026
|
-
const selectedIndex = useMemo2(() => Math.max(0, options.findIndex((o) => o.value === value)), [options, value]);
|
|
1027
|
-
const [activeIndex, setActiveIndex] = useState6(selectedIndex);
|
|
1028
|
-
const close = useCallback3((refocus) => {
|
|
1029
|
-
setRect(null);
|
|
1030
|
-
if (refocus) triggerRef.current?.focus();
|
|
1031
|
-
}, []);
|
|
1032
|
-
const openMenu = useCallback3(() => {
|
|
1033
|
-
setActiveIndex(selectedIndex);
|
|
1034
|
-
setRect(triggerRef.current?.getBoundingClientRect() ?? null);
|
|
1035
|
-
}, [selectedIndex]);
|
|
1036
|
-
useLayoutEffect4(() => {
|
|
1037
|
-
if (!rect || !menuRef.current) {
|
|
1038
|
-
setPlacement(null);
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
setPlacement(
|
|
1042
|
-
fitMenu({
|
|
1043
|
-
anchor: { left: rect.left, top: rect.top, bottom: rect.bottom },
|
|
1044
|
-
menu: { width: menuRef.current.offsetWidth, contentHeight: menuRef.current.scrollHeight },
|
|
1045
|
-
viewport: { width: window.innerWidth, height: window.innerHeight },
|
|
1046
|
-
// The option list keeps its classic height (the old max-h-72); a menu
|
|
1047
|
-
// panel passes no cap and stands as tall as the room it opens into.
|
|
1048
|
-
cap: 288
|
|
1049
|
-
})
|
|
1050
|
-
);
|
|
1051
|
-
}, [rect, options.length]);
|
|
1052
|
-
useEffect3(() => {
|
|
1053
|
-
if (!open) return;
|
|
1054
|
-
document.getElementById(`${id}-${activeIndex}`)?.scrollIntoView?.({ block: "nearest" });
|
|
1055
|
-
}, [open, activeIndex, id]);
|
|
1056
|
-
useEffect3(() => {
|
|
1057
|
-
if (!open) return;
|
|
1058
|
-
const onDown = (e) => {
|
|
1059
|
-
if (menuRef.current?.contains(e.target)) return;
|
|
1060
|
-
if (triggerRef.current?.contains(e.target)) return;
|
|
1061
|
-
setRect(null);
|
|
1062
|
-
};
|
|
1063
|
-
const onResize = () => setRect(null);
|
|
1064
|
-
const onScroll = (e) => {
|
|
1065
|
-
if (e.target instanceof Node && menuRef.current?.contains(e.target)) return;
|
|
1066
|
-
setRect(null);
|
|
1067
|
-
};
|
|
1068
|
-
document.addEventListener("mousedown", onDown);
|
|
1069
|
-
window.addEventListener("resize", onResize);
|
|
1070
|
-
window.addEventListener("scroll", onScroll, true);
|
|
1071
|
-
return () => {
|
|
1072
|
-
document.removeEventListener("mousedown", onDown);
|
|
1073
|
-
window.removeEventListener("resize", onResize);
|
|
1074
|
-
window.removeEventListener("scroll", onScroll, true);
|
|
1075
|
-
};
|
|
1076
|
-
}, [open]);
|
|
1077
|
-
const pick = (index) => {
|
|
1078
|
-
const option = options[index];
|
|
1079
|
-
if (!option) return;
|
|
1080
|
-
onChange(option.value);
|
|
1081
|
-
close(true);
|
|
1082
|
-
};
|
|
1083
|
-
const onKeyDown = (e) => {
|
|
1084
|
-
if (!open) {
|
|
1085
|
-
if (["ArrowDown", "ArrowUp", "Enter", " "].includes(e.key)) {
|
|
1086
|
-
e.preventDefault();
|
|
1087
|
-
openMenu();
|
|
1088
|
-
}
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
switch (e.key) {
|
|
1092
|
-
case "Escape":
|
|
1093
|
-
e.preventDefault();
|
|
1094
|
-
close(true);
|
|
1095
|
-
break;
|
|
1096
|
-
case "ArrowDown":
|
|
1097
|
-
e.preventDefault();
|
|
1098
|
-
setActiveIndex((i) => Math.min(options.length - 1, i + 1));
|
|
1099
|
-
break;
|
|
1100
|
-
case "ArrowUp":
|
|
1101
|
-
e.preventDefault();
|
|
1102
|
-
setActiveIndex((i) => Math.max(0, i - 1));
|
|
1103
|
-
break;
|
|
1104
|
-
case "Home":
|
|
1105
|
-
e.preventDefault();
|
|
1106
|
-
setActiveIndex(0);
|
|
1107
|
-
break;
|
|
1108
|
-
case "End":
|
|
1109
|
-
e.preventDefault();
|
|
1110
|
-
setActiveIndex(options.length - 1);
|
|
1111
|
-
break;
|
|
1112
|
-
case "Enter":
|
|
1113
|
-
case " ":
|
|
1114
|
-
e.preventDefault();
|
|
1115
|
-
pick(activeIndex);
|
|
1116
|
-
break;
|
|
1117
|
-
case "Tab":
|
|
1118
|
-
close(false);
|
|
1119
|
-
break;
|
|
1120
|
-
}
|
|
1121
|
-
};
|
|
1059
|
+
import { Select as BaseSelect } from "@base-ui/react/select";
|
|
1060
|
+
import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1061
|
+
var GAP4 = 4;
|
|
1062
|
+
var VIEWPORT_PAD3 = 8;
|
|
1063
|
+
function Select({ value, onChange, options, size = "default", ariaLabel, placeholder = "Select\u2026", disabled, className, ...aria }) {
|
|
1122
1064
|
const selected = options.find((o) => o.value === value);
|
|
1123
|
-
return /* @__PURE__ */
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1065
|
+
return /* @__PURE__ */ jsxs13(
|
|
1066
|
+
BaseSelect.Root,
|
|
1067
|
+
{
|
|
1068
|
+
value,
|
|
1069
|
+
onValueChange: (next) => onChange(next),
|
|
1070
|
+
disabled,
|
|
1071
|
+
modal: false,
|
|
1072
|
+
children: [
|
|
1073
|
+
/* @__PURE__ */ jsxs13(
|
|
1074
|
+
BaseSelect.Trigger,
|
|
1075
|
+
{
|
|
1076
|
+
"aria-label": ariaLabel,
|
|
1077
|
+
"aria-required": aria["aria-required"],
|
|
1078
|
+
className: cn(
|
|
1079
|
+
/*
|
|
1080
|
+
* `min-w-0 max-w-full`: a trigger must never outgrow its container
|
|
1081
|
+
* (Katerina, 2026-09-01 — a long label stretched the files panel's
|
|
1082
|
+
* Lead row past its card). In a flex row the default min-width:auto
|
|
1083
|
+
* forbids shrinking below the label's width, which is what kept
|
|
1084
|
+
* `truncate` from ever engaging; in a block container max-w-full is
|
|
1085
|
+
* the cap. Full-width callers are unaffected.
|
|
1086
|
+
*/
|
|
1087
|
+
"flex w-full min-w-0 max-w-full items-center justify-between gap-2 rounded-lg border bg-bg-inset text-left",
|
|
1088
|
+
"border-border-default text-text-primary outline-none transition-colors",
|
|
1089
|
+
// The focused border survives a hover: hover alone strengthens the
|
|
1090
|
+
// hairline, but hover while focused must not grey the focus colour —
|
|
1091
|
+
// the stacked variant outranks plain hover by specificity.
|
|
1092
|
+
"hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled",
|
|
1093
|
+
"focus-visible:border-border-focus aria-expanded:border-border-focus",
|
|
1094
|
+
"signal:transition-shadow signal:focus-visible:shadow-focus-ring",
|
|
1095
|
+
size === "default" && "px-3 py-2 text-[14px] leading-[1.4] font-normal",
|
|
1096
|
+
size === "small" && "h-6 px-2 text-[12px] leading-[1.4] font-normal",
|
|
1097
|
+
className
|
|
1098
|
+
),
|
|
1099
|
+
children: [
|
|
1100
|
+
/* @__PURE__ */ jsxs13("span", { className: cn("flex min-w-0 items-center gap-2", !selected && "text-text-muted"), children: [
|
|
1101
|
+
selected?.leading && /* @__PURE__ */ jsx20("span", { className: "flex shrink-0 items-center", children: selected.leading }),
|
|
1102
|
+
/* @__PURE__ */ jsx20(BaseSelect.Value, { className: "truncate", children: () => selected?.label ?? placeholder })
|
|
1103
|
+
] }),
|
|
1104
|
+
/* @__PURE__ */ jsx20(
|
|
1105
|
+
BaseSelect.Icon,
|
|
1106
|
+
{
|
|
1107
|
+
render: /* @__PURE__ */ jsx20(IconChevronDown2, { size: size === "small" ? 14 : 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
|
|
1108
|
+
}
|
|
1109
|
+
)
|
|
1110
|
+
]
|
|
1111
|
+
}
|
|
1155
1112
|
),
|
|
1156
|
-
children:
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
),
|
|
1197
|
-
children: [
|
|
1198
|
-
/* @__PURE__ */ jsxs12("span", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1199
|
-
option.leading && /* @__PURE__ */ jsx20("span", { className: "flex shrink-0 items-center", children: option.leading }),
|
|
1200
|
-
/* @__PURE__ */ jsx20("span", { className: "truncate", children: option.label })
|
|
1201
|
-
] }),
|
|
1202
|
-
option.value === value && /* @__PURE__ */ jsx20(IconCheck2, { size: 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
|
|
1203
|
-
]
|
|
1204
|
-
},
|
|
1205
|
-
option.value
|
|
1206
|
-
))
|
|
1207
|
-
}
|
|
1208
|
-
),
|
|
1209
|
-
document.body
|
|
1210
|
-
)
|
|
1211
|
-
] });
|
|
1113
|
+
/* @__PURE__ */ jsx20(BaseSelect.Portal, { children: /* @__PURE__ */ jsx20(
|
|
1114
|
+
BaseSelect.Positioner,
|
|
1115
|
+
{
|
|
1116
|
+
side: "bottom",
|
|
1117
|
+
align: "start",
|
|
1118
|
+
sideOffset: GAP4,
|
|
1119
|
+
collisionPadding: VIEWPORT_PAD3,
|
|
1120
|
+
alignItemWithTrigger: false,
|
|
1121
|
+
className: "z-50 data-[anchor-hidden]:hidden",
|
|
1122
|
+
children: /* @__PURE__ */ jsx20(
|
|
1123
|
+
BaseSelect.Popup,
|
|
1124
|
+
{
|
|
1125
|
+
className: "max-h-[min(288px,var(--available-height))] min-w-[var(--anchor-width)] overflow-y-auto rounded-lg border border-border-default bg-bg-elevated p-1 shadow-lg",
|
|
1126
|
+
children: options.map((option) => /* @__PURE__ */ jsxs13(
|
|
1127
|
+
BaseSelect.Item,
|
|
1128
|
+
{
|
|
1129
|
+
value: option.value,
|
|
1130
|
+
className: "flex h-9 cursor-pointer items-center justify-between gap-2 rounded-lg px-3 text-[14px] font-normal leading-[1.4] text-text-primary transition-colors data-[highlighted]:bg-bg-hover data-[selected]:font-medium",
|
|
1131
|
+
children: [
|
|
1132
|
+
/* @__PURE__ */ jsxs13("span", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1133
|
+
option.leading && /* @__PURE__ */ jsx20("span", { className: "flex shrink-0 items-center", children: option.leading }),
|
|
1134
|
+
/* @__PURE__ */ jsx20(BaseSelect.ItemText, { className: "truncate", children: option.label })
|
|
1135
|
+
] }),
|
|
1136
|
+
/* @__PURE__ */ jsx20(
|
|
1137
|
+
BaseSelect.ItemIndicator,
|
|
1138
|
+
{
|
|
1139
|
+
render: /* @__PURE__ */ jsx20(IconCheck2, { size: 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
|
|
1140
|
+
}
|
|
1141
|
+
)
|
|
1142
|
+
]
|
|
1143
|
+
},
|
|
1144
|
+
option.value
|
|
1145
|
+
))
|
|
1146
|
+
}
|
|
1147
|
+
)
|
|
1148
|
+
}
|
|
1149
|
+
) })
|
|
1150
|
+
]
|
|
1151
|
+
}
|
|
1152
|
+
);
|
|
1212
1153
|
}
|
|
1213
1154
|
|
|
1214
1155
|
// src/TextInput.tsx
|
|
1215
1156
|
import { forwardRef } from "react";
|
|
1157
|
+
import { Input } from "@base-ui/react/input";
|
|
1216
1158
|
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1217
|
-
var TextInput = forwardRef(function TextInput2({ className, type = "text",
|
|
1218
|
-
const controlId = useFieldControlId(id);
|
|
1159
|
+
var TextInput = forwardRef(function TextInput2({ className, type = "text", ...props }, ref) {
|
|
1219
1160
|
return /* @__PURE__ */ jsx21(
|
|
1220
|
-
|
|
1161
|
+
Input,
|
|
1221
1162
|
{
|
|
1222
1163
|
ref,
|
|
1223
|
-
id: controlId,
|
|
1224
1164
|
type,
|
|
1225
1165
|
className: cn(
|
|
1226
1166
|
"bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2",
|
|
@@ -1237,14 +1177,14 @@ var TextInput = forwardRef(function TextInput2({ className, type = "text", id, .
|
|
|
1237
1177
|
|
|
1238
1178
|
// src/Textarea.tsx
|
|
1239
1179
|
import { forwardRef as forwardRef2 } from "react";
|
|
1180
|
+
import { Field as BaseField2 } from "@base-ui/react/field";
|
|
1240
1181
|
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1241
|
-
var Textarea = forwardRef2(function Textarea2({ className,
|
|
1242
|
-
const controlId = useFieldControlId(id);
|
|
1182
|
+
var Textarea = forwardRef2(function Textarea2({ className, ...props }, ref) {
|
|
1243
1183
|
return /* @__PURE__ */ jsx22(
|
|
1244
|
-
|
|
1184
|
+
BaseField2.Control,
|
|
1245
1185
|
{
|
|
1246
1186
|
ref,
|
|
1247
|
-
|
|
1187
|
+
render: /* @__PURE__ */ jsx22("textarea", {}),
|
|
1248
1188
|
className: cn(
|
|
1249
1189
|
"bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2",
|
|
1250
1190
|
"text-[14px] leading-[1.4] font-normal text-text-primary placeholder:text-text-muted",
|
|
@@ -1259,51 +1199,58 @@ var Textarea = forwardRef2(function Textarea2({ className, id, ...props }, ref)
|
|
|
1259
1199
|
});
|
|
1260
1200
|
|
|
1261
1201
|
// src/ConfirmDialog.tsx
|
|
1262
|
-
import { useState as
|
|
1202
|
+
import { useState as useState3 } from "react";
|
|
1263
1203
|
|
|
1264
1204
|
// src/DialogShell.tsx
|
|
1265
|
-
import {
|
|
1266
|
-
import {
|
|
1267
|
-
import {
|
|
1268
|
-
import {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
/* @__PURE__ */
|
|
1281
|
-
"
|
|
1282
|
-
{
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
/* @__PURE__ */
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1205
|
+
import { useRef as useRef3 } from "react";
|
|
1206
|
+
import { Dialog } from "@base-ui/react/dialog";
|
|
1207
|
+
import { AlertDialog } from "@base-ui/react/alert-dialog";
|
|
1208
|
+
import { IconX as IconX3 } from "@tabler/icons-react";
|
|
1209
|
+
import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1210
|
+
function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, width = 502, alert = false }) {
|
|
1211
|
+
const Parts = alert ? AlertDialog : Dialog;
|
|
1212
|
+
const popupRef = useRef3(null);
|
|
1213
|
+
return /* @__PURE__ */ jsx23(
|
|
1214
|
+
Parts.Root,
|
|
1215
|
+
{
|
|
1216
|
+
open: true,
|
|
1217
|
+
onOpenChange: (open) => {
|
|
1218
|
+
if (!open) onClose();
|
|
1219
|
+
},
|
|
1220
|
+
children: /* @__PURE__ */ jsxs14(Parts.Portal, { children: [
|
|
1221
|
+
/* @__PURE__ */ jsx23(Parts.Backdrop, { className: "fixed inset-0 z-40 bg-scrim" }),
|
|
1222
|
+
/* @__PURE__ */ jsx23("div", { className: "fixed inset-0 z-50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs14(
|
|
1223
|
+
Parts.Popup,
|
|
1224
|
+
{
|
|
1225
|
+
ref: popupRef,
|
|
1226
|
+
initialFocus: popupRef,
|
|
1227
|
+
"aria-label": headerContent != null ? title : void 0,
|
|
1228
|
+
className: "bg-bg-elevated border border-border-subtle rounded-lg shadow-lg pointer-events-auto flex flex-col overflow-hidden outline-none",
|
|
1229
|
+
style: { width },
|
|
1230
|
+
children: [
|
|
1231
|
+
/* @__PURE__ */ jsxs14("div", { className: "h-12 flex items-center justify-between pl-5 pr-4 border-b border-border-subtle shrink-0", children: [
|
|
1232
|
+
headerContent ?? /* @__PURE__ */ jsx23(Parts.Title, { className: "text-h4 text-text-primary", render: /* @__PURE__ */ jsx23("span", {}), children: title }),
|
|
1233
|
+
/* @__PURE__ */ jsx23(
|
|
1234
|
+
Parts.Close,
|
|
1235
|
+
{
|
|
1236
|
+
render: /* @__PURE__ */ jsx23(IconButton, { tooltip: "Close", "aria-label": "Close", children: /* @__PURE__ */ jsx23(IconX3, { size: 16, stroke: 1.5 }) })
|
|
1237
|
+
}
|
|
1238
|
+
)
|
|
1239
|
+
] }),
|
|
1240
|
+
/* @__PURE__ */ jsx23("div", { className: cn("pl-5 pr-4 py-4", footer != null && "border-b border-border-subtle", bodyClassName), children }),
|
|
1241
|
+
footer != null && /* @__PURE__ */ jsx23("div", { className: "h-12 flex items-center justify-end gap-2 pl-5 pr-4 shrink-0", children: footer })
|
|
1242
|
+
]
|
|
1243
|
+
}
|
|
1244
|
+
) })
|
|
1245
|
+
] })
|
|
1246
|
+
}
|
|
1300
1247
|
);
|
|
1301
1248
|
}
|
|
1302
1249
|
|
|
1303
1250
|
// src/ConfirmDialog.tsx
|
|
1304
|
-
import { Fragment as
|
|
1251
|
+
import { Fragment as Fragment3, jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1305
1252
|
function ConfirmDialog({ title, children, confirmLabel, destructive = false, onConfirm, onClose }) {
|
|
1306
|
-
const [busy, setBusy] =
|
|
1253
|
+
const [busy, setBusy] = useState3(false);
|
|
1307
1254
|
const confirm = async () => {
|
|
1308
1255
|
setBusy(true);
|
|
1309
1256
|
try {
|
|
@@ -1316,10 +1263,11 @@ function ConfirmDialog({ title, children, confirmLabel, destructive = false, onC
|
|
|
1316
1263
|
return /* @__PURE__ */ jsx24(
|
|
1317
1264
|
DialogShell,
|
|
1318
1265
|
{
|
|
1266
|
+
alert: true,
|
|
1319
1267
|
title,
|
|
1320
1268
|
onClose,
|
|
1321
1269
|
bodyClassName: "flex flex-col gap-3 text-body-2 text-text-primary",
|
|
1322
|
-
footer: /* @__PURE__ */
|
|
1270
|
+
footer: /* @__PURE__ */ jsxs15(Fragment3, { children: [
|
|
1323
1271
|
/* @__PURE__ */ jsx24(Button, { variant: "muted", onClick: onClose, disabled: busy, children: "Cancel" }),
|
|
1324
1272
|
/* @__PURE__ */ jsx24(Button, { variant: destructive ? "destructive" : "primary", onClick: () => void confirm(), disabled: busy, children: confirmLabel })
|
|
1325
1273
|
] }),
|
|
@@ -1329,14 +1277,16 @@ function ConfirmDialog({ title, children, confirmLabel, destructive = false, onC
|
|
|
1329
1277
|
}
|
|
1330
1278
|
|
|
1331
1279
|
// src/EditableText.tsx
|
|
1332
|
-
import { useEffect as
|
|
1280
|
+
import { useEffect as useEffect2, useRef as useRef4, useState as useState4 } from "react";
|
|
1281
|
+
import { Field as BaseField3 } from "@base-ui/react/field";
|
|
1282
|
+
import { Input as Input2 } from "@base-ui/react/input";
|
|
1333
1283
|
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
1334
1284
|
function EditableText({ value, display, displayNode, placeholder, onCommit, multiline = false, className, label, readOnly = false }) {
|
|
1335
|
-
const [editing, setEditing] =
|
|
1336
|
-
const [draft, setDraft] =
|
|
1337
|
-
const [busy, setBusy] =
|
|
1338
|
-
const fieldRef =
|
|
1339
|
-
|
|
1285
|
+
const [editing, setEditing] = useState4(false);
|
|
1286
|
+
const [draft, setDraft] = useState4(value);
|
|
1287
|
+
const [busy, setBusy] = useState4(false);
|
|
1288
|
+
const fieldRef = useRef4(null);
|
|
1289
|
+
useEffect2(() => {
|
|
1340
1290
|
if (editing) {
|
|
1341
1291
|
fieldRef.current?.focus();
|
|
1342
1292
|
fieldRef.current?.select();
|
|
@@ -1381,23 +1331,30 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
1381
1331
|
className
|
|
1382
1332
|
);
|
|
1383
1333
|
if (readOnly) {
|
|
1384
|
-
return
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1334
|
+
return (
|
|
1335
|
+
/*
|
|
1336
|
+
No `aria-label` here. It sat on a bare `<div>`, which has no role, and
|
|
1337
|
+
ARIA does not let an author name a generic element — so the name was
|
|
1338
|
+
written and never read. Read-only, this is a line of text: whatever
|
|
1339
|
+
names the region around it (a `Field`, a `Property`) names this too.
|
|
1340
|
+
*/
|
|
1341
|
+
/* @__PURE__ */ jsx25(
|
|
1342
|
+
"div",
|
|
1343
|
+
{
|
|
1344
|
+
className: cn("w-full px-2 py-1", multiline && !displayNode && "whitespace-pre-wrap", display ?? value ? "text-text-primary" : "text-text-muted", className),
|
|
1345
|
+
children: display ?? value ? displayNode ?? (display ?? value) : placeholder
|
|
1346
|
+
}
|
|
1347
|
+
)
|
|
1391
1348
|
);
|
|
1392
1349
|
}
|
|
1393
1350
|
if (editing) {
|
|
1394
1351
|
return multiline ? /* @__PURE__ */ jsx25(
|
|
1395
|
-
|
|
1352
|
+
BaseField3.Control,
|
|
1396
1353
|
{
|
|
1397
1354
|
ref: fieldRef,
|
|
1355
|
+
render: /* @__PURE__ */ jsx25("textarea", { rows: 4 }),
|
|
1398
1356
|
"aria-label": label,
|
|
1399
1357
|
value: draft,
|
|
1400
|
-
rows: 4,
|
|
1401
1358
|
disabled: busy,
|
|
1402
1359
|
onChange: (event) => setDraft(event.target.value),
|
|
1403
1360
|
onKeyDown,
|
|
@@ -1405,7 +1362,7 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
1405
1362
|
className: fieldClass
|
|
1406
1363
|
}
|
|
1407
1364
|
) : /* @__PURE__ */ jsx25(
|
|
1408
|
-
|
|
1365
|
+
Input2,
|
|
1409
1366
|
{
|
|
1410
1367
|
ref: fieldRef,
|
|
1411
1368
|
"aria-label": label,
|
|
@@ -1423,7 +1380,6 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
1423
1380
|
{
|
|
1424
1381
|
type: "button",
|
|
1425
1382
|
onClick: open,
|
|
1426
|
-
title: "Click to edit",
|
|
1427
1383
|
"aria-label": `Edit ${label.toLowerCase()}`,
|
|
1428
1384
|
className: cn(
|
|
1429
1385
|
"w-full rounded-md border border-transparent px-2 py-1 text-left transition-colors hover:border-border-default",
|
|
@@ -1438,22 +1394,22 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
1438
1394
|
|
|
1439
1395
|
// src/EmptyState.tsx
|
|
1440
1396
|
import { IconMessage2 } from "@tabler/icons-react";
|
|
1441
|
-
import { jsx as jsx26, jsxs as
|
|
1397
|
+
import { jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1442
1398
|
function EmptyState({ icon, message, className }) {
|
|
1443
|
-
return /* @__PURE__ */
|
|
1399
|
+
return /* @__PURE__ */ jsxs16("div", { className: cn("flex flex-col gap-2 items-center", className), children: [
|
|
1444
1400
|
/* @__PURE__ */ jsx26("span", { className: "text-text-secondary", children: icon ?? /* @__PURE__ */ jsx26(IconMessage2, { size: 16, stroke: 1.5 }) }),
|
|
1445
1401
|
/* @__PURE__ */ jsx26("p", { className: "text-body-2 text-text-secondary text-center", children: message })
|
|
1446
1402
|
] });
|
|
1447
1403
|
}
|
|
1448
1404
|
|
|
1449
1405
|
// src/Skeleton.tsx
|
|
1450
|
-
import { jsx as jsx27, jsxs as
|
|
1406
|
+
import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1451
1407
|
function SkeletonBar({ className, style }) {
|
|
1452
1408
|
return /* @__PURE__ */ jsx27("div", { className: cn("bg-bg-inset rounded-sm animate-pulse", className), style });
|
|
1453
1409
|
}
|
|
1454
1410
|
var ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160];
|
|
1455
1411
|
function SkeletonRow({ barWidth = 130 }) {
|
|
1456
|
-
return /* @__PURE__ */
|
|
1412
|
+
return /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2 px-2 h-[32px] rounded-lg", children: [
|
|
1457
1413
|
/* @__PURE__ */ jsx27(SkeletonBar, { className: "w-4 h-4 shrink-0" }),
|
|
1458
1414
|
/* @__PURE__ */ jsx27(SkeletonBar, { className: "h-3.5", style: { width: barWidth } })
|
|
1459
1415
|
] });
|
|
@@ -1463,20 +1419,20 @@ function SkeletonList({ rows = 8, className }) {
|
|
|
1463
1419
|
}
|
|
1464
1420
|
|
|
1465
1421
|
// src/Breadcrumb.tsx
|
|
1466
|
-
import { Fragment as
|
|
1467
|
-
import { jsx as jsx28, jsxs as
|
|
1422
|
+
import { Fragment as Fragment4, useCallback as useCallback2, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState5 } from "react";
|
|
1423
|
+
import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1468
1424
|
function Breadcrumb({ items, className }) {
|
|
1469
|
-
const navRef =
|
|
1470
|
-
const labelRefs =
|
|
1471
|
-
const [truncated, setTruncated] =
|
|
1472
|
-
const measure =
|
|
1425
|
+
const navRef = useRef5(null);
|
|
1426
|
+
const labelRefs = useRef5([]);
|
|
1427
|
+
const [truncated, setTruncated] = useState5(/* @__PURE__ */ new Set());
|
|
1428
|
+
const measure = useCallback2(() => {
|
|
1473
1429
|
const next = /* @__PURE__ */ new Set();
|
|
1474
1430
|
labelRefs.current.forEach((el, index) => {
|
|
1475
1431
|
if (el && el.scrollWidth > el.clientWidth) next.add(index);
|
|
1476
1432
|
});
|
|
1477
1433
|
setTruncated((prev) => prev.size === next.size && [...next].every((i) => prev.has(i)) ? prev : next);
|
|
1478
1434
|
}, []);
|
|
1479
|
-
|
|
1435
|
+
useLayoutEffect2(() => {
|
|
1480
1436
|
measure();
|
|
1481
1437
|
const nav = navRef.current;
|
|
1482
1438
|
if (!nav || typeof ResizeObserver === "undefined") return;
|
|
@@ -1497,8 +1453,8 @@ function Breadcrumb({ items, className }) {
|
|
|
1497
1453
|
const setLabelRef = (el) => {
|
|
1498
1454
|
labelRefs.current[index] = el;
|
|
1499
1455
|
};
|
|
1500
|
-
const crumb = item.href ? /* @__PURE__ */ jsx28("a", { ref: setLabelRef, href: item.href, className: cn(text, "hover:text-text-primary"), children: item.label }) : /* @__PURE__ */ jsx28("span", { ref: setLabelRef, className: text, "aria-current": last ? "page" : void 0, children: item.label });
|
|
1501
|
-
return /* @__PURE__ */
|
|
1456
|
+
const crumb = item.href ? /* @__PURE__ */ jsx28("a", { ref: setLabelRef, href: item.href, onClick: item.onClick, className: cn(text, "hover:text-text-primary"), children: item.label }) : /* @__PURE__ */ jsx28("span", { ref: setLabelRef, className: text, "aria-current": last ? "page" : void 0, children: item.label });
|
|
1457
|
+
return /* @__PURE__ */ jsxs18(Fragment4, { children: [
|
|
1502
1458
|
index > 0 && /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", className: "shrink-0 text-text-muted", children: "/" }),
|
|
1503
1459
|
truncated.has(index) ? (
|
|
1504
1460
|
// `min-w-0 shrink` undoes the wrapper's own shrink-0 — the crumb
|
|
@@ -1511,9 +1467,9 @@ function Breadcrumb({ items, className }) {
|
|
|
1511
1467
|
}
|
|
1512
1468
|
|
|
1513
1469
|
// src/NavItem.tsx
|
|
1514
|
-
import { jsx as jsx29, jsxs as
|
|
1470
|
+
import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1515
1471
|
function NavItem({ label, href, count, countLabel, active = false, icon, className, ...props }) {
|
|
1516
|
-
return /* @__PURE__ */
|
|
1472
|
+
return /* @__PURE__ */ jsxs19(
|
|
1517
1473
|
"a",
|
|
1518
1474
|
{
|
|
1519
1475
|
href,
|
|
@@ -1536,16 +1492,171 @@ function NavItem({ label, href, count, countLabel, active = false, icon, classNa
|
|
|
1536
1492
|
);
|
|
1537
1493
|
}
|
|
1538
1494
|
|
|
1495
|
+
// src/Popover.tsx
|
|
1496
|
+
import { useMemo as useMemo2 } from "react";
|
|
1497
|
+
import { Popover as BasePopover } from "@base-ui/react/popover";
|
|
1498
|
+
import { jsx as jsx30, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1499
|
+
var GAP5 = 4;
|
|
1500
|
+
var VIEWPORT_PAD4 = 8;
|
|
1501
|
+
function Popover({ trigger, anchor, align = "left", side = "bottom", open, onOpenChange, finalFocus, actionsRef, ariaLabel, children, className }) {
|
|
1502
|
+
const anchorTarget = useMemo2(() => {
|
|
1503
|
+
if (!anchor) return void 0;
|
|
1504
|
+
if (anchor instanceof Element) return anchor;
|
|
1505
|
+
return { getBoundingClientRect: () => anchor };
|
|
1506
|
+
}, [anchor]);
|
|
1507
|
+
return /* @__PURE__ */ jsxs20(
|
|
1508
|
+
BasePopover.Root,
|
|
1509
|
+
{
|
|
1510
|
+
open,
|
|
1511
|
+
onOpenChange: onOpenChange ? (next) => onOpenChange(next) : void 0,
|
|
1512
|
+
actionsRef,
|
|
1513
|
+
modal: false,
|
|
1514
|
+
children: [
|
|
1515
|
+
trigger && /* @__PURE__ */ jsx30(BasePopover.Trigger, { render: trigger, disabled: triggerDisabled(trigger) }),
|
|
1516
|
+
/* @__PURE__ */ jsx30(BasePopover.Portal, { children: /* @__PURE__ */ jsx30(
|
|
1517
|
+
BasePopover.Positioner,
|
|
1518
|
+
{
|
|
1519
|
+
anchor: anchorTarget,
|
|
1520
|
+
side,
|
|
1521
|
+
align: align === "right" ? "end" : "start",
|
|
1522
|
+
sideOffset: GAP5,
|
|
1523
|
+
collisionPadding: VIEWPORT_PAD4,
|
|
1524
|
+
className: "z-50 data-[anchor-hidden]:hidden",
|
|
1525
|
+
children: /* @__PURE__ */ jsx30(
|
|
1526
|
+
BasePopover.Popup,
|
|
1527
|
+
{
|
|
1528
|
+
"aria-label": ariaLabel,
|
|
1529
|
+
initialFocus: trigger ? void 0 : false,
|
|
1530
|
+
finalFocus,
|
|
1531
|
+
className: cn("min-w-[180px] max-h-[var(--available-height)] overflow-y-auto outline-none", className),
|
|
1532
|
+
render: /* @__PURE__ */ jsx30(MenuPanel, {}),
|
|
1533
|
+
children
|
|
1534
|
+
}
|
|
1535
|
+
)
|
|
1536
|
+
}
|
|
1537
|
+
) })
|
|
1538
|
+
]
|
|
1539
|
+
}
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// src/Toolbar.tsx
|
|
1544
|
+
import { Toolbar as BaseToolbar } from "@base-ui/react/toolbar";
|
|
1545
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
1546
|
+
function Toolbar({ "aria-label": ariaLabel, orientation = "horizontal", loopFocus = true, surface = true, children, className }) {
|
|
1547
|
+
const strip = /* @__PURE__ */ jsx31(
|
|
1548
|
+
BaseToolbar.Root,
|
|
1549
|
+
{
|
|
1550
|
+
"aria-label": ariaLabel,
|
|
1551
|
+
orientation,
|
|
1552
|
+
loopFocus,
|
|
1553
|
+
className: cn("flex items-center gap-1", orientation === "vertical" && "flex-col", className),
|
|
1554
|
+
children
|
|
1555
|
+
}
|
|
1556
|
+
);
|
|
1557
|
+
if (!surface) return strip;
|
|
1558
|
+
return /* @__PURE__ */ jsx31(MenuPanel, { className: "w-fit p-1", children: strip });
|
|
1559
|
+
}
|
|
1560
|
+
function ToolbarButton({ ref, disabled, disabledReason, ...props }) {
|
|
1561
|
+
return /* @__PURE__ */ jsx31(
|
|
1562
|
+
BaseToolbar.Button,
|
|
1563
|
+
{
|
|
1564
|
+
ref,
|
|
1565
|
+
disabled: disabled || !!disabledReason,
|
|
1566
|
+
focusableWhenDisabled: true,
|
|
1567
|
+
render: /* @__PURE__ */ jsx31(IconButton, { disabled, disabledReason, ...props })
|
|
1568
|
+
}
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
function ToolbarInput(props) {
|
|
1572
|
+
return /* @__PURE__ */ jsx31(BaseToolbar.Input, { render: /* @__PURE__ */ jsx31(TextInput, {}), ...props });
|
|
1573
|
+
}
|
|
1574
|
+
function ToolbarSeparator({ className }) {
|
|
1575
|
+
return /* @__PURE__ */ jsx31(
|
|
1576
|
+
BaseToolbar.Separator,
|
|
1577
|
+
{
|
|
1578
|
+
orientation: "vertical",
|
|
1579
|
+
className: cn("mx-1 h-4 w-px shrink-0 bg-border-default", className)
|
|
1580
|
+
}
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// src/ReactionPicker.tsx
|
|
1585
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
1586
|
+
function ReactionPicker({ options, onSelect, "aria-label": ariaLabel = "Reactions", surface = true, className }) {
|
|
1587
|
+
return /* @__PURE__ */ jsx32(Toolbar, { "aria-label": ariaLabel, surface, className: cn("gap-0.5", className), children: options.map((option) => (
|
|
1588
|
+
/*
|
|
1589
|
+
A `ToolbarButton`, not a button in a tooltip wrapper. The wrapper
|
|
1590
|
+
would become the toolbar's item and the button inside it would never
|
|
1591
|
+
join the walk — and it is unnecessary since stage 4, because an
|
|
1592
|
+
`IconButton` with a `tooltip` IS the trigger.
|
|
1593
|
+
|
|
1594
|
+
The tooltip names it for a pointer, `aria-label` for everything else,
|
|
1595
|
+
and both say the meaning rather than the glyph.
|
|
1596
|
+
*/
|
|
1597
|
+
/* @__PURE__ */ jsx32(
|
|
1598
|
+
ToolbarButton,
|
|
1599
|
+
{
|
|
1600
|
+
"aria-label": option.label,
|
|
1601
|
+
tooltip: option.label,
|
|
1602
|
+
onClick: () => onSelect(option.emoji),
|
|
1603
|
+
className: "size-7 text-[18px] leading-none",
|
|
1604
|
+
children: /* @__PURE__ */ jsx32("span", { "aria-hidden": "true", children: option.emoji })
|
|
1605
|
+
},
|
|
1606
|
+
option.emoji
|
|
1607
|
+
)
|
|
1608
|
+
)) });
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// src/PreviewCard.tsx
|
|
1612
|
+
import { PreviewCard as BasePreviewCard } from "@base-ui/react/preview-card";
|
|
1613
|
+
import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1614
|
+
var GAP6 = 12;
|
|
1615
|
+
var VIEWPORT_PAD5 = 8;
|
|
1616
|
+
var OPEN_DELAY2 = 350;
|
|
1617
|
+
var CLOSE_DELAY = 200;
|
|
1618
|
+
function PreviewCard({ content, children, side = "right", delay = OPEN_DELAY2, closeDelay = CLOSE_DELAY, className, wrapperClassName }) {
|
|
1619
|
+
return /* @__PURE__ */ jsxs21(BasePreviewCard.Root, { children: [
|
|
1620
|
+
/* @__PURE__ */ jsx33(
|
|
1621
|
+
BasePreviewCard.Trigger,
|
|
1622
|
+
{
|
|
1623
|
+
delay,
|
|
1624
|
+
closeDelay,
|
|
1625
|
+
render: /* @__PURE__ */ jsx33("span", { className: cn("inline-flex", wrapperClassName) }),
|
|
1626
|
+
children
|
|
1627
|
+
}
|
|
1628
|
+
),
|
|
1629
|
+
/* @__PURE__ */ jsx33(BasePreviewCard.Portal, { children: /* @__PURE__ */ jsx33(
|
|
1630
|
+
BasePreviewCard.Positioner,
|
|
1631
|
+
{
|
|
1632
|
+
side,
|
|
1633
|
+
align: "start",
|
|
1634
|
+
sideOffset: GAP6,
|
|
1635
|
+
collisionPadding: VIEWPORT_PAD5,
|
|
1636
|
+
className: "z-50 data-[anchor-hidden]:hidden",
|
|
1637
|
+
children: /* @__PURE__ */ jsx33(
|
|
1638
|
+
BasePreviewCard.Popup,
|
|
1639
|
+
{
|
|
1640
|
+
className: cn("w-[360px] max-h-[min(300px,var(--available-height))] gap-3 overflow-y-auto p-3 outline-none [&>*]:shrink-0", className),
|
|
1641
|
+
render: /* @__PURE__ */ jsx33(MenuPanel, {}),
|
|
1642
|
+
children: content
|
|
1643
|
+
}
|
|
1644
|
+
)
|
|
1645
|
+
}
|
|
1646
|
+
) })
|
|
1647
|
+
] });
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1539
1650
|
// src/Rail.tsx
|
|
1540
|
-
import { jsx as
|
|
1651
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
1541
1652
|
function Rail({ "aria-label": ariaLabel = "Navigation", children, className }) {
|
|
1542
|
-
return /* @__PURE__ */
|
|
1653
|
+
return /* @__PURE__ */ jsx34("nav", { "aria-label": ariaLabel, className: cn("flex w-16 shrink-0 flex-col items-start gap-2 px-2 py-3", className), children });
|
|
1543
1654
|
}
|
|
1544
1655
|
|
|
1545
1656
|
// src/RailItem.tsx
|
|
1546
|
-
import { jsx as
|
|
1657
|
+
import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1547
1658
|
function RailItem({ href, icon, label, active = false, className, ...props }) {
|
|
1548
|
-
return /* @__PURE__ */
|
|
1659
|
+
return /* @__PURE__ */ jsxs22(
|
|
1549
1660
|
"a",
|
|
1550
1661
|
{
|
|
1551
1662
|
href,
|
|
@@ -1553,14 +1664,14 @@ function RailItem({ href, icon, label, active = false, className, ...props }) {
|
|
|
1553
1664
|
className: cn("group flex w-full shrink-0 flex-col items-center gap-0.5 px-2 py-0.5", className),
|
|
1554
1665
|
...props,
|
|
1555
1666
|
children: [
|
|
1556
|
-
/* @__PURE__ */
|
|
1667
|
+
/* @__PURE__ */ jsx35(
|
|
1557
1668
|
"div",
|
|
1558
1669
|
{
|
|
1559
1670
|
className: cn(
|
|
1560
1671
|
"flex items-center justify-center rounded-lg p-2 transition-colors",
|
|
1561
1672
|
active ? "bg-bg-selected" : "group-hover:bg-bg-hover"
|
|
1562
1673
|
),
|
|
1563
|
-
children: /* @__PURE__ */
|
|
1674
|
+
children: /* @__PURE__ */ jsx35(
|
|
1564
1675
|
"span",
|
|
1565
1676
|
{
|
|
1566
1677
|
className: cn(
|
|
@@ -1572,16 +1683,16 @@ function RailItem({ href, icon, label, active = false, className, ...props }) {
|
|
|
1572
1683
|
)
|
|
1573
1684
|
}
|
|
1574
1685
|
),
|
|
1575
|
-
/* @__PURE__ */
|
|
1686
|
+
/* @__PURE__ */ jsx35("span", { className: cn("text-center text-[9px] font-medium leading-[115%]", active ? "text-text-primary" : "text-text-secondary"), children: label })
|
|
1576
1687
|
]
|
|
1577
1688
|
}
|
|
1578
1689
|
);
|
|
1579
1690
|
}
|
|
1580
1691
|
|
|
1581
1692
|
// src/Sidebar.tsx
|
|
1582
|
-
import { jsx as
|
|
1693
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
1583
1694
|
function Sidebar({ "aria-label": ariaLabel = "Workspace", children, className }) {
|
|
1584
|
-
return /* @__PURE__ */
|
|
1695
|
+
return /* @__PURE__ */ jsx36(
|
|
1585
1696
|
"nav",
|
|
1586
1697
|
{
|
|
1587
1698
|
"aria-label": ariaLabel,
|
|
@@ -1595,29 +1706,29 @@ function Sidebar({ "aria-label": ariaLabel = "Workspace", children, className })
|
|
|
1595
1706
|
}
|
|
1596
1707
|
|
|
1597
1708
|
// src/Property.tsx
|
|
1598
|
-
import { jsx as
|
|
1709
|
+
import { jsx as jsx37, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1599
1710
|
function Property({ label, layout = "row", children, className }) {
|
|
1600
1711
|
if (layout === "stacked") {
|
|
1601
1712
|
return (
|
|
1602
1713
|
// gap-3 — the one label-above-content distance (Katerina, 2026-09-01):
|
|
1603
1714
|
// Peek's topic-details sections already sit at 12px, Ship's rail was
|
|
1604
1715
|
// 6px, and unifying means the bigger, calmer one.
|
|
1605
|
-
/* @__PURE__ */
|
|
1606
|
-
/* @__PURE__ */
|
|
1716
|
+
/* @__PURE__ */ jsxs23("div", { className: cn("flex flex-col gap-3", className), children: [
|
|
1717
|
+
/* @__PURE__ */ jsx37("span", { className: "text-menu uppercase tracking-[0.08em] text-text-secondary", children: label }),
|
|
1607
1718
|
children
|
|
1608
1719
|
] })
|
|
1609
1720
|
);
|
|
1610
1721
|
}
|
|
1611
|
-
return /* @__PURE__ */
|
|
1612
|
-
/* @__PURE__ */
|
|
1722
|
+
return /* @__PURE__ */ jsxs23("div", { className: cn("flex items-center gap-2", className), children: [
|
|
1723
|
+
/* @__PURE__ */ jsx37("span", { className: "w-[68px] shrink-0 text-caption text-text-secondary", children: label }),
|
|
1613
1724
|
children
|
|
1614
1725
|
] });
|
|
1615
1726
|
}
|
|
1616
1727
|
|
|
1617
1728
|
// src/Reaction.tsx
|
|
1618
|
-
import { jsx as
|
|
1729
|
+
import { jsx as jsx38, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1619
1730
|
function Reaction({ emoji, count, pressed = false, className, type, ...props }) {
|
|
1620
|
-
return /* @__PURE__ */
|
|
1731
|
+
return /* @__PURE__ */ jsxs24(
|
|
1621
1732
|
"button",
|
|
1622
1733
|
{
|
|
1623
1734
|
type: type ?? "button",
|
|
@@ -1633,8 +1744,8 @@ function Reaction({ emoji, count, pressed = false, className, type, ...props })
|
|
|
1633
1744
|
),
|
|
1634
1745
|
...props,
|
|
1635
1746
|
children: [
|
|
1636
|
-
/* @__PURE__ */
|
|
1637
|
-
/* @__PURE__ */
|
|
1747
|
+
/* @__PURE__ */ jsx38("span", { "aria-hidden": "true", className: "shrink-0 text-[16px] leading-none", children: emoji }),
|
|
1748
|
+
/* @__PURE__ */ jsx38("span", { className: "text-chip signal:font-mono signal:text-[10px] signal:font-semibold signal:tabular-nums", children: count })
|
|
1638
1749
|
]
|
|
1639
1750
|
}
|
|
1640
1751
|
);
|
|
@@ -1642,9 +1753,10 @@ function Reaction({ emoji, count, pressed = false, className, type, ...props })
|
|
|
1642
1753
|
|
|
1643
1754
|
// src/SearchInput.tsx
|
|
1644
1755
|
import "react";
|
|
1645
|
-
import {
|
|
1756
|
+
import { Input as Input3 } from "@base-ui/react/input";
|
|
1757
|
+
import { jsx as jsx39, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
1646
1758
|
function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...props }) {
|
|
1647
|
-
return /* @__PURE__ */
|
|
1759
|
+
return /* @__PURE__ */ jsxs25(
|
|
1648
1760
|
"div",
|
|
1649
1761
|
{
|
|
1650
1762
|
className: cn(
|
|
@@ -1654,27 +1766,27 @@ function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...pro
|
|
|
1654
1766
|
className
|
|
1655
1767
|
),
|
|
1656
1768
|
children: [
|
|
1657
|
-
/* @__PURE__ */
|
|
1658
|
-
|
|
1769
|
+
/* @__PURE__ */ jsx39(
|
|
1770
|
+
Input3,
|
|
1659
1771
|
{
|
|
1660
1772
|
className: "flex-1 min-w-0 bg-transparent text-input-value text-text-primary placeholder:text-text-muted outline-none",
|
|
1661
1773
|
placeholder,
|
|
1662
1774
|
...props
|
|
1663
1775
|
}
|
|
1664
1776
|
),
|
|
1665
|
-
shortcut && /* @__PURE__ */
|
|
1777
|
+
shortcut && /* @__PURE__ */ jsx39(Kbd, { children: shortcut })
|
|
1666
1778
|
]
|
|
1667
1779
|
}
|
|
1668
1780
|
);
|
|
1669
1781
|
}
|
|
1670
1782
|
|
|
1671
1783
|
// src/SectionHeader.tsx
|
|
1672
|
-
import { useState as
|
|
1784
|
+
import { useState as useState6 } from "react";
|
|
1673
1785
|
import { IconChevronRight as IconChevronRight2 } from "@tabler/icons-react";
|
|
1674
|
-
import { jsx as
|
|
1786
|
+
import { jsx as jsx40, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
1675
1787
|
function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, actions, showActions = "hover", className }) {
|
|
1676
|
-
const [isHovered, setIsHovered] =
|
|
1677
|
-
return /* @__PURE__ */
|
|
1788
|
+
const [isHovered, setIsHovered] = useState6(false);
|
|
1789
|
+
return /* @__PURE__ */ jsxs26(
|
|
1678
1790
|
"div",
|
|
1679
1791
|
{
|
|
1680
1792
|
className: cn(
|
|
@@ -1687,8 +1799,8 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, ac
|
|
|
1687
1799
|
onMouseEnter: () => setIsHovered(true),
|
|
1688
1800
|
onMouseLeave: () => setIsHovered(false),
|
|
1689
1801
|
children: [
|
|
1690
|
-
/* @__PURE__ */
|
|
1691
|
-
chevron && /* @__PURE__ */
|
|
1802
|
+
/* @__PURE__ */ jsxs26("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
1803
|
+
chevron && /* @__PURE__ */ jsx40(
|
|
1692
1804
|
IconChevronRight2,
|
|
1693
1805
|
{
|
|
1694
1806
|
size: 12,
|
|
@@ -1696,9 +1808,9 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, ac
|
|
|
1696
1808
|
className: cn("text-text-secondary transition-transform duration-150", isExpanded && "rotate-90")
|
|
1697
1809
|
}
|
|
1698
1810
|
),
|
|
1699
|
-
/* @__PURE__ */
|
|
1811
|
+
/* @__PURE__ */ jsx40(SectionLabel, { children: title })
|
|
1700
1812
|
] }),
|
|
1701
|
-
(showActions === "always" || isHovered) && actions && actions.length > 0 && /* @__PURE__ */
|
|
1813
|
+
(showActions === "always" || isHovered) && actions && actions.length > 0 && /* @__PURE__ */ jsx40("div", { className: "flex items-center gap-1", children: actions.map((action) => /* @__PURE__ */ jsx40(
|
|
1702
1814
|
IconButton,
|
|
1703
1815
|
{
|
|
1704
1816
|
tooltip: action.tooltip,
|
|
@@ -1718,9 +1830,9 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, ac
|
|
|
1718
1830
|
|
|
1719
1831
|
// src/Tabs.tsx
|
|
1720
1832
|
import { Tabs as BaseTabs } from "@base-ui/react/tabs";
|
|
1721
|
-
import { jsx as
|
|
1722
|
-
function Tabs({ tabs, active, onChange, size = "default", className }) {
|
|
1723
|
-
return /* @__PURE__ */
|
|
1833
|
+
import { jsx as jsx41, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
1834
|
+
function Tabs({ tabs, active, onChange, size = "default", className, ...aria }) {
|
|
1835
|
+
return /* @__PURE__ */ jsx41(
|
|
1724
1836
|
BaseTabs.Root,
|
|
1725
1837
|
{
|
|
1726
1838
|
value: active,
|
|
@@ -1728,36 +1840,45 @@ function Tabs({ tabs, active, onChange, size = "default", className }) {
|
|
|
1728
1840
|
if (details.reason === "none") onChange(value);
|
|
1729
1841
|
},
|
|
1730
1842
|
className,
|
|
1731
|
-
children: /* @__PURE__ */
|
|
1732
|
-
BaseTabs.
|
|
1843
|
+
children: /* @__PURE__ */ jsx41(
|
|
1844
|
+
BaseTabs.List,
|
|
1733
1845
|
{
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1846
|
+
activateOnFocus: true,
|
|
1847
|
+
"aria-label": aria["aria-label"],
|
|
1848
|
+
"aria-labelledby": aria["aria-labelledby"],
|
|
1849
|
+
className: "flex items-center gap-2",
|
|
1850
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsxs27(
|
|
1851
|
+
BaseTabs.Tab,
|
|
1852
|
+
{
|
|
1853
|
+
value: tab.id,
|
|
1854
|
+
className: (state) => cn(
|
|
1855
|
+
"flex cursor-pointer items-center transition-colors",
|
|
1856
|
+
// gap: default is Ship's 6px; small keeps Peek's original 4px, or
|
|
1857
|
+
// "small is Peek's geometry" stops being true.
|
|
1858
|
+
size === "default" ? "gap-1.5 rounded-md px-2 py-1 text-body-2" : "gap-1 rounded px-1.5 py-0.5 text-caption",
|
|
1859
|
+
state.active ? "bg-bg-active text-text-primary" : "text-text-secondary hover:bg-bg-hover"
|
|
1860
|
+
),
|
|
1861
|
+
children: [
|
|
1862
|
+
tab.icon,
|
|
1863
|
+
/* @__PURE__ */ jsxs27("span", { className: "flex items-baseline", children: [
|
|
1864
|
+
tab.label,
|
|
1865
|
+
tab.count !== void 0 ? /* @__PURE__ */ jsx41("span", { className: "ml-2.5 font-mono text-caption tabular-nums text-text-secondary", children: tab.count }) : null
|
|
1866
|
+
] })
|
|
1867
|
+
]
|
|
1868
|
+
},
|
|
1869
|
+
tab.id
|
|
1870
|
+
))
|
|
1871
|
+
}
|
|
1872
|
+
)
|
|
1752
1873
|
}
|
|
1753
1874
|
);
|
|
1754
1875
|
}
|
|
1755
1876
|
|
|
1756
1877
|
// src/Toast.tsx
|
|
1757
|
-
import { createContext as
|
|
1758
|
-
import { createPortal as
|
|
1878
|
+
import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect3, useMemo as useMemo3, useState as useState7 } from "react";
|
|
1879
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
1759
1880
|
import { IconCircleCheck } from "@tabler/icons-react";
|
|
1760
|
-
import { jsx as
|
|
1881
|
+
import { jsx as jsx42, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
1761
1882
|
var SURFACE_STYLES = {
|
|
1762
1883
|
success: "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
|
|
1763
1884
|
brand: "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
|
|
@@ -1775,7 +1896,7 @@ var ACTION_BORDER_STYLES = {
|
|
|
1775
1896
|
};
|
|
1776
1897
|
function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAction, className }) {
|
|
1777
1898
|
const hasAction = !!(actionLabel && onAction);
|
|
1778
|
-
return /* @__PURE__ */
|
|
1899
|
+
return /* @__PURE__ */ jsxs28(
|
|
1779
1900
|
"div",
|
|
1780
1901
|
{
|
|
1781
1902
|
className: cn(
|
|
@@ -1787,11 +1908,11 @@ function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAct
|
|
|
1787
1908
|
className
|
|
1788
1909
|
),
|
|
1789
1910
|
children: [
|
|
1790
|
-
/* @__PURE__ */
|
|
1791
|
-
leadingIcon && /* @__PURE__ */
|
|
1792
|
-
/* @__PURE__ */
|
|
1911
|
+
/* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
1912
|
+
leadingIcon && /* @__PURE__ */ jsx42(IconCircleCheck, { size: 16, stroke: 1.5, className: cn("text-text-primary shrink-0", ICON_STYLES[type]) }),
|
|
1913
|
+
/* @__PURE__ */ jsx42("span", { className: "font-normal text-[14px] leading-[1.4] text-text-primary whitespace-nowrap", children: label })
|
|
1793
1914
|
] }),
|
|
1794
|
-
hasAction && /* @__PURE__ */
|
|
1915
|
+
hasAction && /* @__PURE__ */ jsx42(
|
|
1795
1916
|
"button",
|
|
1796
1917
|
{
|
|
1797
1918
|
type: "button",
|
|
@@ -1801,24 +1922,24 @@ function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAct
|
|
|
1801
1922
|
ACTION_BORDER_STYLES[type],
|
|
1802
1923
|
type === "neutral" ? "hover:border-border-strong" : "hover:opacity-80"
|
|
1803
1924
|
),
|
|
1804
|
-
children: /* @__PURE__ */
|
|
1925
|
+
children: /* @__PURE__ */ jsx42("span", { className: "font-medium text-[12px] leading-[12px] text-text-primary whitespace-nowrap", children: actionLabel })
|
|
1805
1926
|
}
|
|
1806
1927
|
)
|
|
1807
1928
|
]
|
|
1808
1929
|
}
|
|
1809
1930
|
);
|
|
1810
1931
|
}
|
|
1811
|
-
var ToastContext =
|
|
1932
|
+
var ToastContext = createContext2(null);
|
|
1812
1933
|
var toastSeq = 0;
|
|
1813
1934
|
function ToastProvider({ children }) {
|
|
1814
|
-
const [toast, setToast] =
|
|
1815
|
-
const showToast =
|
|
1935
|
+
const [toast, setToast] = useState7(null);
|
|
1936
|
+
const showToast = useCallback3((opts) => {
|
|
1816
1937
|
setToast({ ...opts, id: ++toastSeq });
|
|
1817
1938
|
}, []);
|
|
1818
|
-
const dismissToast =
|
|
1939
|
+
const dismissToast = useCallback3(() => {
|
|
1819
1940
|
setToast(null);
|
|
1820
1941
|
}, []);
|
|
1821
|
-
|
|
1942
|
+
useEffect3(() => {
|
|
1822
1943
|
if (!toast) return;
|
|
1823
1944
|
const duration = toast.durationMs ?? 5e3;
|
|
1824
1945
|
if (duration <= 0) return;
|
|
@@ -1828,10 +1949,10 @@ function ToastProvider({ children }) {
|
|
|
1828
1949
|
return () => clearTimeout(timer);
|
|
1829
1950
|
}, [toast]);
|
|
1830
1951
|
const value = useMemo3(() => ({ showToast, dismissToast }), [showToast, dismissToast]);
|
|
1831
|
-
return /* @__PURE__ */
|
|
1952
|
+
return /* @__PURE__ */ jsxs28(ToastContext.Provider, { value, children: [
|
|
1832
1953
|
children,
|
|
1833
|
-
toast &&
|
|
1834
|
-
/* @__PURE__ */
|
|
1954
|
+
toast && createPortal2(
|
|
1955
|
+
/* @__PURE__ */ jsx42("div", { className: "fixed bottom-4 left-4 z-[100] pointer-events-none", children: /* @__PURE__ */ jsx42(
|
|
1835
1956
|
Toast,
|
|
1836
1957
|
{
|
|
1837
1958
|
className: "pointer-events-auto",
|
|
@@ -1850,7 +1971,7 @@ function ToastProvider({ children }) {
|
|
|
1850
1971
|
] });
|
|
1851
1972
|
}
|
|
1852
1973
|
function useToast() {
|
|
1853
|
-
const ctx =
|
|
1974
|
+
const ctx = useContext2(ToastContext);
|
|
1854
1975
|
if (!ctx) throw new Error("useToast must be used within ToastProvider");
|
|
1855
1976
|
return ctx;
|
|
1856
1977
|
}
|
|
@@ -1885,10 +2006,13 @@ export {
|
|
|
1885
2006
|
NavItem,
|
|
1886
2007
|
Person,
|
|
1887
2008
|
PersonTrigger,
|
|
2009
|
+
Popover,
|
|
2010
|
+
PreviewCard,
|
|
1888
2011
|
Property,
|
|
1889
2012
|
Rail,
|
|
1890
2013
|
RailItem,
|
|
1891
2014
|
Reaction,
|
|
2015
|
+
ReactionPicker,
|
|
1892
2016
|
SearchInput,
|
|
1893
2017
|
SectionHeader,
|
|
1894
2018
|
SectionLabel,
|
|
@@ -1902,16 +2026,17 @@ export {
|
|
|
1902
2026
|
Textarea,
|
|
1903
2027
|
Toast,
|
|
1904
2028
|
ToastProvider,
|
|
2029
|
+
Toolbar,
|
|
2030
|
+
ToolbarButton,
|
|
2031
|
+
ToolbarInput,
|
|
2032
|
+
ToolbarSeparator,
|
|
1905
2033
|
Tooltip,
|
|
2034
|
+
TooltipProvider,
|
|
1906
2035
|
TopBar,
|
|
1907
2036
|
WithTooltip,
|
|
1908
|
-
clampBox,
|
|
1909
2037
|
cn,
|
|
1910
|
-
fitMenu,
|
|
1911
|
-
fitSubmenu,
|
|
1912
2038
|
hueFor,
|
|
1913
2039
|
initialsFor,
|
|
1914
|
-
useFieldControlId,
|
|
1915
2040
|
useToast
|
|
1916
2041
|
};
|
|
1917
2042
|
//# sourceMappingURL=index.js.map
|