@estiva-app/ui 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 +882 -763
- package/dist/index.js.map +4 -4
- 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 +117 -27
- package/src/Field.tsx +86 -58
- 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 +341 -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 +153 -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 +198 -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/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,16 @@ 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";
|
|
381
455
|
|
|
382
456
|
// src/SectionLabel.tsx
|
|
383
|
-
import { jsx as
|
|
457
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
384
458
|
function SectionLabel({ children, className }) {
|
|
385
|
-
return /* @__PURE__ */
|
|
459
|
+
return /* @__PURE__ */ jsx12(
|
|
386
460
|
"span",
|
|
387
461
|
{
|
|
388
462
|
className: cn(
|
|
@@ -395,224 +469,216 @@ function SectionLabel({ children, className }) {
|
|
|
395
469
|
}
|
|
396
470
|
|
|
397
471
|
// src/Menu.tsx
|
|
398
|
-
import { jsx as
|
|
399
|
-
var
|
|
472
|
+
import { Fragment, jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
473
|
+
var MenuContext = createContext(null);
|
|
400
474
|
function MenuPanel({ children, className, ...props }) {
|
|
401
|
-
return /* @__PURE__ */
|
|
475
|
+
return /* @__PURE__ */ jsx13(
|
|
402
476
|
"div",
|
|
403
477
|
{
|
|
404
|
-
className: cn(
|
|
478
|
+
className: cn(
|
|
479
|
+
"flex flex-col rounded-lg border border-border-default bg-bg-elevated p-2 shadow-lg",
|
|
480
|
+
/*
|
|
481
|
+
* A divider in a menu runs the width of the rows it separates.
|
|
482
|
+
*
|
|
483
|
+
* `Divider` is inset 12px each side, which is right in a page and
|
|
484
|
+
* wrong here: measured in a 180px menu, the rule started 21px from the
|
|
485
|
+
* panel's edge where the rows start at 9px — 12px narrower on each side
|
|
486
|
+
* than the things it divides. `IdentityMenu` was already cancelling it
|
|
487
|
+
* by hand with `mx-0`, which is the sign it belonged here (Katerina,
|
|
488
|
+
* 2026-09-08). Direct children only, so a divider a caller puts inside
|
|
489
|
+
* a row keeps its own spacing.
|
|
490
|
+
*/
|
|
491
|
+
"[&>[role=separator]]:mx-0",
|
|
492
|
+
className
|
|
493
|
+
),
|
|
405
494
|
...props,
|
|
406
495
|
children
|
|
407
496
|
}
|
|
408
497
|
);
|
|
409
498
|
}
|
|
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,
|
|
499
|
+
var isProduction = () => globalThis.process?.env?.NODE_ENV === "production";
|
|
500
|
+
var GAP3 = 4;
|
|
501
|
+
var VIEWPORT_PAD2 = 8;
|
|
502
|
+
var HOVER_OPEN_DELAY = 0;
|
|
503
|
+
var HOVER_CLOSE_DELAY = 150;
|
|
504
|
+
function Menu({ trigger, align = "left", openOnHover = false, open, onOpenChange, actionsRef, children, className }) {
|
|
505
|
+
return /* @__PURE__ */ jsxs7(
|
|
506
|
+
BaseMenu.Root,
|
|
477
507
|
{
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
508
|
+
open,
|
|
509
|
+
onOpenChange: onOpenChange ? (next) => onOpenChange(next) : void 0,
|
|
510
|
+
actionsRef,
|
|
511
|
+
modal: false,
|
|
512
|
+
children: [
|
|
513
|
+
/* @__PURE__ */ jsx13(
|
|
514
|
+
BaseMenu.Trigger,
|
|
515
|
+
{
|
|
516
|
+
render: trigger,
|
|
517
|
+
openOnHover,
|
|
518
|
+
delay: HOVER_OPEN_DELAY,
|
|
519
|
+
closeDelay: HOVER_CLOSE_DELAY
|
|
520
|
+
}
|
|
521
|
+
),
|
|
522
|
+
/* @__PURE__ */ jsx13(BaseMenu.Portal, { children: /* @__PURE__ */ jsx13(
|
|
523
|
+
BaseMenu.Positioner,
|
|
524
|
+
{
|
|
525
|
+
side: "bottom",
|
|
526
|
+
align: align === "right" ? "end" : "start",
|
|
527
|
+
sideOffset: GAP3,
|
|
528
|
+
collisionPadding: VIEWPORT_PAD2,
|
|
529
|
+
className: "z-50 data-[anchor-hidden]:hidden",
|
|
530
|
+
children: /* @__PURE__ */ jsx13(
|
|
531
|
+
BaseMenu.Popup,
|
|
532
|
+
{
|
|
533
|
+
"data-interactive": true,
|
|
534
|
+
className: cn("min-w-[180px] max-h-[var(--available-height)] overflow-y-auto outline-none", className),
|
|
535
|
+
render: /* @__PURE__ */ jsx13(MenuPanel, {}),
|
|
536
|
+
children: /* @__PURE__ */ jsx13(MenuContext.Provider, { value: { openOnHover }, children })
|
|
537
|
+
}
|
|
538
|
+
)
|
|
539
|
+
}
|
|
540
|
+
) })
|
|
541
|
+
]
|
|
491
542
|
}
|
|
492
|
-
)
|
|
493
|
-
return portalled ? createPortal2(node, document.body) : node;
|
|
543
|
+
);
|
|
494
544
|
}
|
|
495
545
|
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
|
-
})
|
|
546
|
+
const menu = useContext(MenuContext);
|
|
547
|
+
if (!isProduction() && menu?.openOnHover) {
|
|
548
|
+
console.error(
|
|
549
|
+
'[@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
550
|
);
|
|
525
|
-
}
|
|
526
|
-
return /* @__PURE__ */
|
|
527
|
-
/* @__PURE__ */
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
551
|
+
}
|
|
552
|
+
return /* @__PURE__ */ jsxs7(BaseMenu.SubmenuRoot, { children: [
|
|
553
|
+
/* @__PURE__ */ jsx13(
|
|
554
|
+
BaseMenu.SubmenuTrigger,
|
|
555
|
+
{
|
|
556
|
+
openOnHover: true,
|
|
557
|
+
delay: 0,
|
|
558
|
+
closeDelay: 150,
|
|
559
|
+
nativeButton: true,
|
|
560
|
+
render: /* @__PURE__ */ jsx13("button", { type: "button" }),
|
|
561
|
+
className: menuItemClassName({ size: "default", selected }),
|
|
562
|
+
children: /* @__PURE__ */ jsx13(MenuItemBody, { label, leading, submenu: true })
|
|
563
|
+
}
|
|
564
|
+
),
|
|
565
|
+
/* @__PURE__ */ jsx13(BaseMenu.Portal, { children: /* @__PURE__ */ jsx13(
|
|
566
|
+
BaseMenu.Positioner,
|
|
567
|
+
{
|
|
568
|
+
side: "inline-end",
|
|
569
|
+
align: "start",
|
|
570
|
+
sideOffset: GAP3,
|
|
571
|
+
collisionPadding: VIEWPORT_PAD2,
|
|
572
|
+
className: "z-50 data-[anchor-hidden]:hidden",
|
|
573
|
+
children: /* @__PURE__ */ jsx13(BaseMenu.Popup, { className: cn("w-[160px]", className), "data-interactive": true, render: /* @__PURE__ */ jsx13(MenuPanel, {}), children })
|
|
574
|
+
}
|
|
575
|
+
) })
|
|
576
|
+
] });
|
|
577
|
+
}
|
|
578
|
+
function menuItemClassName({ size, selected, className }) {
|
|
579
|
+
return cn(
|
|
580
|
+
// shrink-0: a menu is a flex column that scrolls at its max height,
|
|
581
|
+
// and a flex child shrinks before its container does — so every row
|
|
582
|
+
// in an overflowing menu was squashed to its `min-h`, and a row given
|
|
583
|
+
// an explicit height silently lost it (Peek's `[` menu: h-12 rows
|
|
584
|
+
// measured 40px). The same fix NavItem took on 2026-09-02.
|
|
585
|
+
// `group`: the `hint` slot reveals itself from this row's own :hover,
|
|
586
|
+
// so the hint and the fill are one CSS state change, not two engines.
|
|
587
|
+
//
|
|
588
|
+
// No transition on the fill (Katerina, 2026-09-05). It faded over
|
|
589
|
+
// 150ms, and anything appearing with it had to fade too or arrive
|
|
590
|
+
// ahead of it — which, sweeping a pointer down a list, read as the
|
|
591
|
+
// hint flickering in and out. Both are instant now: they still change
|
|
592
|
+
// on exactly the same :hover, so they cannot come apart, and a row
|
|
593
|
+
// lights and unlights crisply as the pointer crosses it.
|
|
594
|
+
"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",
|
|
595
|
+
// tall: as tall as its content, never shorter than 40px (Katerina,
|
|
596
|
+
// 2026-09-01) — a single-line picker row sits at 40, a row with a
|
|
597
|
+
// 32px face and a role line comes out at its natural 48. One rule,
|
|
598
|
+
// not a hand-picked height per file.
|
|
599
|
+
size === "tall" ? "min-h-10 gap-3 px-3 py-1.5" : "gap-2 px-2 py-1.5",
|
|
600
|
+
selected && "bg-bg-hover",
|
|
601
|
+
className
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
function MenuItemBody({ label, children, size = "default", description, leading, trailing, hint, shortcut, submenu, destructive, selected }) {
|
|
605
|
+
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);
|
|
606
|
+
return /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
607
|
+
leading && /* @__PURE__ */ jsx13("span", { className: "flex shrink-0 items-center", children: leading }),
|
|
608
|
+
children ?? /* @__PURE__ */ jsxs7("span", { className: cn("flex min-w-0 flex-1 flex-col", size === "tall" && "gap-[2px]"), children: [
|
|
609
|
+
/* @__PURE__ */ jsx13("span", { className: cn("truncate text-[14px] leading-[140%]", destructive ? "text-error-default" : "text-text-primary"), children: label }),
|
|
610
|
+
description && /* @__PURE__ */ jsx13("span", { className: "truncate text-[12px] leading-[120%] text-text-secondary", children: description })
|
|
611
|
+
] }),
|
|
612
|
+
(edge || hint) && // One grid cell holding both, right-aligned: the slot is as wide as
|
|
613
|
+
// the wider of the two and never changes, so revealing the hint moves
|
|
614
|
+
// nothing. No transition here either — the hint switches on the same
|
|
615
|
+
// :hover / `selected` as the fill, in the same frame.
|
|
616
|
+
/* @__PURE__ */ jsxs7("span", { className: "grid shrink-0 items-center justify-items-end [&>*]:col-start-1 [&>*]:row-start-1", children: [
|
|
617
|
+
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 }),
|
|
618
|
+
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 })
|
|
619
|
+
] })
|
|
544
620
|
] });
|
|
545
621
|
}
|
|
546
622
|
function MenuItem({ label, children, size = "default", description, leading, trailing, hint, shortcut, submenu, destructive, selected, className, ...props }) {
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
"button",
|
|
623
|
+
const body = /* @__PURE__ */ jsx13(
|
|
624
|
+
MenuItemBody,
|
|
550
625
|
{
|
|
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
|
-
),
|
|
626
|
+
label,
|
|
627
|
+
size,
|
|
628
|
+
description,
|
|
629
|
+
leading,
|
|
630
|
+
trailing,
|
|
631
|
+
hint,
|
|
632
|
+
shortcut,
|
|
633
|
+
submenu,
|
|
634
|
+
destructive,
|
|
635
|
+
selected,
|
|
636
|
+
children
|
|
637
|
+
}
|
|
638
|
+
);
|
|
639
|
+
const rowClassName = menuItemClassName({ size, selected, className });
|
|
640
|
+
if (!useContext(MenuContext)) {
|
|
641
|
+
return /* @__PURE__ */ jsx13("button", { type: "button", className: rowClassName, ...props, children: body });
|
|
642
|
+
}
|
|
643
|
+
return /* @__PURE__ */ jsx13(
|
|
644
|
+
BaseMenu.Item,
|
|
645
|
+
{
|
|
646
|
+
nativeButton: true,
|
|
647
|
+
render: /* @__PURE__ */ jsx13("button", { type: "button" }),
|
|
648
|
+
className: rowClassName,
|
|
577
649
|
...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
|
-
]
|
|
650
|
+
children: body
|
|
593
651
|
}
|
|
594
652
|
);
|
|
595
653
|
}
|
|
596
654
|
function EnterHint({ target }) {
|
|
597
|
-
return /* @__PURE__ */
|
|
598
|
-
/* @__PURE__ */
|
|
599
|
-
target && /* @__PURE__ */
|
|
655
|
+
return /* @__PURE__ */ jsxs7("span", { className: "flex shrink-0 items-center gap-1.5 text-text-muted", children: [
|
|
656
|
+
/* @__PURE__ */ jsx13(Kbd, { children: "\u21A9 Enter" }),
|
|
657
|
+
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
658
|
] });
|
|
601
659
|
}
|
|
602
660
|
function MenuSection({ label, children, className }) {
|
|
603
|
-
|
|
604
|
-
|
|
661
|
+
const inMenu = useContext(MenuContext) !== null;
|
|
662
|
+
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 }) });
|
|
663
|
+
if (!inMenu) {
|
|
664
|
+
return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col", children: [
|
|
665
|
+
heading,
|
|
666
|
+
children
|
|
667
|
+
] });
|
|
668
|
+
}
|
|
669
|
+
return /* @__PURE__ */ jsxs7(BaseMenu.Group, { className: "flex flex-col", children: [
|
|
670
|
+
/* @__PURE__ */ jsx13(BaseMenu.GroupLabel, { render: heading }),
|
|
605
671
|
children
|
|
606
672
|
] });
|
|
607
673
|
}
|
|
608
674
|
function MenuRow({ children, className }) {
|
|
609
|
-
return /* @__PURE__ */
|
|
675
|
+
return /* @__PURE__ */ jsx13("div", { className: cn("flex items-center gap-2 px-2 py-1.5", className), children });
|
|
610
676
|
}
|
|
611
677
|
|
|
612
678
|
// src/ChipInput.tsx
|
|
613
|
-
import { jsx as
|
|
679
|
+
import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
614
680
|
function InputChip({ label, leading, onRemove, className }) {
|
|
615
|
-
return /* @__PURE__ */
|
|
681
|
+
return /* @__PURE__ */ jsxs8(
|
|
616
682
|
"div",
|
|
617
683
|
{
|
|
618
684
|
className: cn(
|
|
@@ -628,9 +694,9 @@ function InputChip({ label, leading, onRemove, className }) {
|
|
|
628
694
|
className
|
|
629
695
|
),
|
|
630
696
|
children: [
|
|
631
|
-
leading && /* @__PURE__ */
|
|
632
|
-
/* @__PURE__ */
|
|
633
|
-
onRemove && /* @__PURE__ */
|
|
697
|
+
leading && /* @__PURE__ */ jsx14("span", { className: "flex shrink-0 items-center", children: leading }),
|
|
698
|
+
/* @__PURE__ */ jsx14("span", { className: "text-caption font-medium text-text-primary", children: label }),
|
|
699
|
+
onRemove && /* @__PURE__ */ jsx14(
|
|
634
700
|
"button",
|
|
635
701
|
{
|
|
636
702
|
type: "button",
|
|
@@ -640,7 +706,7 @@ function InputChip({ label, leading, onRemove, className }) {
|
|
|
640
706
|
},
|
|
641
707
|
className: "size-4 flex items-center justify-center rounded-full hover:bg-bg-hover text-text-secondary",
|
|
642
708
|
"aria-label": `Remove ${label}`,
|
|
643
|
-
children: /* @__PURE__ */
|
|
709
|
+
children: /* @__PURE__ */ jsx14(IconX2, { size: 10, stroke: 1.5 })
|
|
644
710
|
}
|
|
645
711
|
)
|
|
646
712
|
]
|
|
@@ -655,14 +721,15 @@ function ChipInput({
|
|
|
655
721
|
autoFocus,
|
|
656
722
|
excludeIds = [],
|
|
657
723
|
chipLeading,
|
|
658
|
-
rowLeading
|
|
724
|
+
rowLeading,
|
|
725
|
+
...aria
|
|
659
726
|
}) {
|
|
660
|
-
const [query, setQuery] =
|
|
661
|
-
const [highlight, setHighlight] =
|
|
662
|
-
const [isFocused, setIsFocused] =
|
|
663
|
-
const [anchorRect, setAnchorRect] =
|
|
664
|
-
const wrapperRef =
|
|
665
|
-
const inputRef =
|
|
727
|
+
const [query, setQuery] = useState2("");
|
|
728
|
+
const [highlight, setHighlight] = useState2(0);
|
|
729
|
+
const [isFocused, setIsFocused] = useState2(false);
|
|
730
|
+
const [anchorRect, setAnchorRect] = useState2(null);
|
|
731
|
+
const wrapperRef = useRef(null);
|
|
732
|
+
const inputRef = useRef(null);
|
|
666
733
|
const matches = useMemo(() => {
|
|
667
734
|
const selectedIds = new Set(value.map((o) => o.id));
|
|
668
735
|
const excludedIds = new Set(excludeIds);
|
|
@@ -674,11 +741,11 @@ function ChipInput({
|
|
|
674
741
|
return o.label.toLowerCase().includes(q) || (o.description ?? "").toLowerCase().includes(q);
|
|
675
742
|
});
|
|
676
743
|
}, [query, value, excludeIds, options]);
|
|
677
|
-
|
|
744
|
+
useEffect(() => {
|
|
678
745
|
setHighlight(0);
|
|
679
746
|
}, [query, matches.length]);
|
|
680
747
|
const showDropdown = isFocused && query.trim().length > 0 && matches.length > 0;
|
|
681
|
-
|
|
748
|
+
useLayoutEffect(() => {
|
|
682
749
|
if (!showDropdown) return;
|
|
683
750
|
const update = () => {
|
|
684
751
|
if (wrapperRef.current) setAnchorRect(wrapperRef.current.getBoundingClientRect());
|
|
@@ -728,16 +795,16 @@ function ChipInput({
|
|
|
728
795
|
}
|
|
729
796
|
}
|
|
730
797
|
}
|
|
731
|
-
return /* @__PURE__ */
|
|
732
|
-
/* @__PURE__ */
|
|
798
|
+
return /* @__PURE__ */ jsxs8("div", { className: "relative", children: [
|
|
799
|
+
/* @__PURE__ */ jsxs8(
|
|
733
800
|
"div",
|
|
734
801
|
{
|
|
735
802
|
ref: wrapperRef,
|
|
736
803
|
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
804
|
onClick: () => inputRef.current?.focus(),
|
|
738
805
|
children: [
|
|
739
|
-
value.map((o) => /* @__PURE__ */
|
|
740
|
-
/* @__PURE__ */
|
|
806
|
+
value.map((o) => /* @__PURE__ */ jsx14(InputChip, { label: o.label, leading: chipLeading?.(o), onRemove: () => removeOption(o.id) }, o.id)),
|
|
807
|
+
/* @__PURE__ */ jsx14(
|
|
741
808
|
"input",
|
|
742
809
|
{
|
|
743
810
|
ref: inputRef,
|
|
@@ -751,14 +818,15 @@ function ChipInput({
|
|
|
751
818
|
setTimeout(() => setIsFocused(false), 150);
|
|
752
819
|
},
|
|
753
820
|
placeholder: value.length === 0 ? placeholder : "",
|
|
821
|
+
"aria-required": aria["aria-required"],
|
|
754
822
|
className: "flex-1 min-w-[120px] bg-transparent text-body-2 text-text-primary placeholder:text-text-muted outline-none border-none"
|
|
755
823
|
}
|
|
756
824
|
)
|
|
757
825
|
]
|
|
758
826
|
}
|
|
759
827
|
),
|
|
760
|
-
showDropdown && anchorRect &&
|
|
761
|
-
/* @__PURE__ */
|
|
828
|
+
showDropdown && anchorRect && createPortal(
|
|
829
|
+
/* @__PURE__ */ jsx14(
|
|
762
830
|
"div",
|
|
763
831
|
{
|
|
764
832
|
className: "fixed z-[60] overflow-y-auto bg-bg-elevated border border-border-default rounded-lg shadow-lg",
|
|
@@ -771,7 +839,7 @@ function ChipInput({
|
|
|
771
839
|
}),
|
|
772
840
|
width: anchorRect.width
|
|
773
841
|
},
|
|
774
|
-
children: matches.map((o, i) => /* @__PURE__ */
|
|
842
|
+
children: matches.map((o, i) => /* @__PURE__ */ jsx14(
|
|
775
843
|
MenuItem,
|
|
776
844
|
{
|
|
777
845
|
size: "tall",
|
|
@@ -795,51 +863,8 @@ function ChipInput({
|
|
|
795
863
|
] });
|
|
796
864
|
}
|
|
797
865
|
|
|
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
866
|
// src/IdentityMenu.tsx
|
|
842
|
-
import {
|
|
867
|
+
import { useCallback, useRef as useRef2 } from "react";
|
|
843
868
|
|
|
844
869
|
// src/Divider.tsx
|
|
845
870
|
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
@@ -855,13 +880,13 @@ function Divider({ orientation = "horizontal", className }) {
|
|
|
855
880
|
}
|
|
856
881
|
|
|
857
882
|
// src/Person.tsx
|
|
858
|
-
import { jsx as jsx16, jsxs as
|
|
883
|
+
import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
859
884
|
function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
|
|
860
885
|
return (
|
|
861
886
|
// gap-2: the face-to-name distance is one rule (8px) wherever a person
|
|
862
887
|
// appears — Katerina, 2026-09-01, set on Person so PersonTrigger and every
|
|
863
888
|
// row agree by construction.
|
|
864
|
-
/* @__PURE__ */
|
|
889
|
+
/* @__PURE__ */ jsxs9("span", { className: cn("flex min-w-0 items-center gap-2", className), children: [
|
|
865
890
|
/* @__PURE__ */ jsx16(Avatar, { name, src: picture, size }),
|
|
866
891
|
/* @__PURE__ */ jsx16("span", { className: cn("truncate", !name && "text-text-muted"), children: name ?? fallback })
|
|
867
892
|
] })
|
|
@@ -871,7 +896,7 @@ function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
|
|
|
871
896
|
// src/PersonTrigger.tsx
|
|
872
897
|
import { Button as BaseButton3 } from "@base-ui/react/button";
|
|
873
898
|
import { IconChevronDown } from "@tabler/icons-react";
|
|
874
|
-
import { jsx as jsx17, jsxs as
|
|
899
|
+
import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
875
900
|
function PersonTrigger({ name, picture, fallback, size, open = false, compact = false, className, ...props }) {
|
|
876
901
|
if (compact) {
|
|
877
902
|
return /* @__PURE__ */ jsx17(
|
|
@@ -880,13 +905,14 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
|
|
|
880
905
|
type: "button",
|
|
881
906
|
"aria-haspopup": "menu",
|
|
882
907
|
"aria-expanded": open,
|
|
883
|
-
|
|
908
|
+
"aria-label": props["aria-label"] ?? name ?? fallback,
|
|
909
|
+
className: cn("cursor-pointer rounded-full", className),
|
|
884
910
|
...props,
|
|
885
911
|
children: /* @__PURE__ */ jsx17(Avatar, { name, src: picture, size: size ?? 36 })
|
|
886
912
|
}
|
|
887
913
|
);
|
|
888
914
|
}
|
|
889
|
-
return /* @__PURE__ */
|
|
915
|
+
return /* @__PURE__ */ jsxs10(
|
|
890
916
|
BaseButton3,
|
|
891
917
|
{
|
|
892
918
|
type: "button",
|
|
@@ -895,6 +921,11 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
|
|
|
895
921
|
className: cn(
|
|
896
922
|
"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
923
|
open && "bg-bg-hover",
|
|
924
|
+
// Under a `Menu` the open state is Base UI's, not a prop: the trigger
|
|
925
|
+
// carries `data-popup-open` while its menu is up, and sets its own
|
|
926
|
+
// `aria-expanded`. Both spellings hold the fill, so this works whether
|
|
927
|
+
// the caller drives it or the menu does.
|
|
928
|
+
"data-[popup-open]:bg-bg-hover",
|
|
898
929
|
className
|
|
899
930
|
),
|
|
900
931
|
...props,
|
|
@@ -907,30 +938,34 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
|
|
|
907
938
|
}
|
|
908
939
|
|
|
909
940
|
// src/IdentityMenu.tsx
|
|
910
|
-
import { Fragment, jsx as jsx18, jsxs as
|
|
911
|
-
function IdentityPanel({
|
|
941
|
+
import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
942
|
+
function IdentityPanel({ className, onClose = () => {
|
|
943
|
+
}, ...rest }) {
|
|
944
|
+
return /* @__PURE__ */ jsx18(MenuPanel, { className: cn("w-72", className), children: /* @__PURE__ */ jsx18(IdentityRows, { ...rest, onClose }) });
|
|
945
|
+
}
|
|
946
|
+
function IdentityRows({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, onClose, children }) {
|
|
912
947
|
const act = (action) => () => {
|
|
913
948
|
onClose();
|
|
914
949
|
action();
|
|
915
950
|
};
|
|
916
951
|
const appRows = typeof children === "function" ? children(onClose) : children;
|
|
917
|
-
return /* @__PURE__ */
|
|
918
|
-
/* @__PURE__ */
|
|
952
|
+
return /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
953
|
+
/* @__PURE__ */ jsxs11(MenuSection, { label: signedIn ? "Signed in as" : "Acting as", children: [
|
|
919
954
|
/* @__PURE__ */ jsx18(MenuRow, { children: /* @__PURE__ */ jsx18(Person, { name: me.name, picture: me.picture, fallback: "Anonymous", size: 28, className: "text-body-2-strong" }) }),
|
|
920
955
|
/* @__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__ */
|
|
956
|
+
me.email && /* @__PURE__ */ jsxs11(Note, { children: [
|
|
922
957
|
me.email,
|
|
923
958
|
" \xB7 known to Estiva, never published to the relay"
|
|
924
959
|
] })
|
|
925
960
|
] }),
|
|
926
|
-
relayUrl && /* @__PURE__ */
|
|
961
|
+
relayUrl && /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
927
962
|
/* @__PURE__ */ jsx18(Divider, { className: "mx-0 my-2" }),
|
|
928
|
-
/* @__PURE__ */
|
|
963
|
+
/* @__PURE__ */ jsxs11(MenuSection, { label: "Workspace", children: [
|
|
929
964
|
/* @__PURE__ */ jsx18(MenuRow, { children: /* @__PURE__ */ jsx18("span", { className: "break-all font-mono text-caption text-text-primary", children: relayUrl }) }),
|
|
930
965
|
/* @__PURE__ */ jsx18(Note, { children: "Everyone on this relay shares this workspace, in every app." })
|
|
931
966
|
] })
|
|
932
967
|
] }),
|
|
933
|
-
appRows && /* @__PURE__ */
|
|
968
|
+
appRows && /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
934
969
|
/* @__PURE__ */ jsx18(Divider, { className: "mx-0 my-2" }),
|
|
935
970
|
appRows
|
|
936
971
|
] }),
|
|
@@ -947,280 +982,179 @@ function IdentityPanel({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, o
|
|
|
947
982
|
] });
|
|
948
983
|
}
|
|
949
984
|
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,
|
|
985
|
+
const actions = useRef2(null);
|
|
986
|
+
const close = useCallback(() => actions.current?.close(), []);
|
|
987
|
+
return (
|
|
988
|
+
/* The wrapper carries the caller's `className` and nothing else. It used
|
|
989
|
+
to be `relative`, because the panel was positioned against it; the panel
|
|
990
|
+
hangs from the trigger and portals now, so a positioning context here
|
|
991
|
+
would only be a lie about what this box does. */
|
|
992
|
+
/* @__PURE__ */ jsx18("div", { className, children: /* @__PURE__ */ jsx18(
|
|
993
|
+
Menu,
|
|
969
994
|
{
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
995
|
+
align: "right",
|
|
996
|
+
actionsRef: actions,
|
|
997
|
+
className: "w-72",
|
|
998
|
+
trigger: /* @__PURE__ */ jsx18(
|
|
999
|
+
PersonTrigger,
|
|
1000
|
+
{
|
|
1001
|
+
name: me.name,
|
|
1002
|
+
picture: me.picture,
|
|
1003
|
+
fallback: "Anonymous",
|
|
1004
|
+
compact,
|
|
1005
|
+
size: compact ? 36 : void 0,
|
|
1006
|
+
"aria-label": compact ? "Account menu" : void 0
|
|
1007
|
+
}
|
|
1008
|
+
),
|
|
1009
|
+
children: /* @__PURE__ */ jsx18(
|
|
1010
|
+
IdentityRows,
|
|
1011
|
+
{
|
|
1012
|
+
me,
|
|
1013
|
+
signedIn,
|
|
1014
|
+
relayUrl,
|
|
1015
|
+
idBase,
|
|
1016
|
+
onCopyKey,
|
|
1017
|
+
onSignOut,
|
|
1018
|
+
onClose: close,
|
|
1019
|
+
children
|
|
1020
|
+
}
|
|
1021
|
+
)
|
|
979
1022
|
}
|
|
980
|
-
)
|
|
981
|
-
|
|
1023
|
+
) })
|
|
1024
|
+
);
|
|
982
1025
|
}
|
|
983
1026
|
function Note({ children }) {
|
|
984
1027
|
return /* @__PURE__ */ jsx18("span", { className: "px-2 pb-1.5 text-caption text-text-secondary", children });
|
|
985
1028
|
}
|
|
986
1029
|
|
|
987
1030
|
// src/Field.tsx
|
|
988
|
-
import {
|
|
989
|
-
import {
|
|
990
|
-
|
|
991
|
-
function
|
|
992
|
-
const
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
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 })
|
|
1031
|
+
import { cloneElement, isValidElement } from "react";
|
|
1032
|
+
import { Field as BaseField } from "@base-ui/react/field";
|
|
1033
|
+
import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1034
|
+
function Field({ label, required = false, helper, error, children }) {
|
|
1035
|
+
const line = error ?? helper;
|
|
1036
|
+
const control = required && isValidElement(children) ? cloneElement(children, {
|
|
1037
|
+
"aria-required": children.props["aria-required"] ?? true
|
|
1038
|
+
}) : children;
|
|
1039
|
+
return /* @__PURE__ */ jsxs12(BaseField.Root, { invalid: !!error, className: "flex flex-col gap-2", children: [
|
|
1040
|
+
/* @__PURE__ */ jsxs12(BaseField.Label, { className: cn("text-input-label text-text-primary", required && "flex items-center"), children: [
|
|
1041
|
+
label,
|
|
1042
|
+
required && /* @__PURE__ */ jsx19("span", { className: "text-error-default ml-0.5", children: "*" })
|
|
1043
|
+
] }),
|
|
1044
|
+
line == null ? control : /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1.5", children: [
|
|
1045
|
+
control,
|
|
1046
|
+
error != null ? /* @__PURE__ */ jsx19(BaseField.Error, { match: true, className: "text-caption text-error-default", children: error }) : /* @__PURE__ */ jsx19(BaseField.Description, { className: "text-caption text-text-muted", children: helper })
|
|
1047
|
+
] })
|
|
1011
1048
|
] });
|
|
1012
1049
|
}
|
|
1013
1050
|
|
|
1014
1051
|
// src/Select.tsx
|
|
1015
1052
|
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
|
-
};
|
|
1053
|
+
import { Select as BaseSelect } from "@base-ui/react/select";
|
|
1054
|
+
import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1055
|
+
var GAP4 = 4;
|
|
1056
|
+
var VIEWPORT_PAD3 = 8;
|
|
1057
|
+
function Select({ value, onChange, options, size = "default", ariaLabel, placeholder = "Select\u2026", disabled, className, ...aria }) {
|
|
1122
1058
|
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
|
-
|
|
1059
|
+
return /* @__PURE__ */ jsxs13(
|
|
1060
|
+
BaseSelect.Root,
|
|
1061
|
+
{
|
|
1062
|
+
value,
|
|
1063
|
+
onValueChange: (next) => onChange(next),
|
|
1064
|
+
disabled,
|
|
1065
|
+
modal: false,
|
|
1066
|
+
children: [
|
|
1067
|
+
/* @__PURE__ */ jsxs13(
|
|
1068
|
+
BaseSelect.Trigger,
|
|
1069
|
+
{
|
|
1070
|
+
"aria-label": ariaLabel,
|
|
1071
|
+
"aria-required": aria["aria-required"],
|
|
1072
|
+
className: cn(
|
|
1073
|
+
/*
|
|
1074
|
+
* `min-w-0 max-w-full`: a trigger must never outgrow its container
|
|
1075
|
+
* (Katerina, 2026-09-01 — a long label stretched the files panel's
|
|
1076
|
+
* Lead row past its card). In a flex row the default min-width:auto
|
|
1077
|
+
* forbids shrinking below the label's width, which is what kept
|
|
1078
|
+
* `truncate` from ever engaging; in a block container max-w-full is
|
|
1079
|
+
* the cap. Full-width callers are unaffected.
|
|
1080
|
+
*/
|
|
1081
|
+
"flex w-full min-w-0 max-w-full items-center justify-between gap-2 rounded-lg border bg-bg-inset text-left",
|
|
1082
|
+
"border-border-default text-text-primary outline-none transition-colors",
|
|
1083
|
+
// The focused border survives a hover: hover alone strengthens the
|
|
1084
|
+
// hairline, but hover while focused must not grey the focus colour —
|
|
1085
|
+
// the stacked variant outranks plain hover by specificity.
|
|
1086
|
+
"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",
|
|
1087
|
+
"focus-visible:border-border-focus aria-expanded:border-border-focus",
|
|
1088
|
+
"signal:transition-shadow signal:focus-visible:shadow-focus-ring",
|
|
1089
|
+
size === "default" && "px-3 py-2 text-[14px] leading-[1.4] font-normal",
|
|
1090
|
+
size === "small" && "h-6 px-2 text-[12px] leading-[1.4] font-normal",
|
|
1091
|
+
className
|
|
1092
|
+
),
|
|
1093
|
+
children: [
|
|
1094
|
+
/* @__PURE__ */ jsxs13("span", { className: cn("flex min-w-0 items-center gap-2", !selected && "text-text-muted"), children: [
|
|
1095
|
+
selected?.leading && /* @__PURE__ */ jsx20("span", { className: "flex shrink-0 items-center", children: selected.leading }),
|
|
1096
|
+
/* @__PURE__ */ jsx20(BaseSelect.Value, { className: "truncate", children: () => selected?.label ?? placeholder })
|
|
1097
|
+
] }),
|
|
1098
|
+
/* @__PURE__ */ jsx20(
|
|
1099
|
+
BaseSelect.Icon,
|
|
1100
|
+
{
|
|
1101
|
+
render: /* @__PURE__ */ jsx20(IconChevronDown2, { size: size === "small" ? 14 : 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
|
|
1102
|
+
}
|
|
1103
|
+
)
|
|
1104
|
+
]
|
|
1105
|
+
}
|
|
1155
1106
|
),
|
|
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
|
-
] });
|
|
1107
|
+
/* @__PURE__ */ jsx20(BaseSelect.Portal, { children: /* @__PURE__ */ jsx20(
|
|
1108
|
+
BaseSelect.Positioner,
|
|
1109
|
+
{
|
|
1110
|
+
side: "bottom",
|
|
1111
|
+
align: "start",
|
|
1112
|
+
sideOffset: GAP4,
|
|
1113
|
+
collisionPadding: VIEWPORT_PAD3,
|
|
1114
|
+
alignItemWithTrigger: false,
|
|
1115
|
+
className: "z-50 data-[anchor-hidden]:hidden",
|
|
1116
|
+
children: /* @__PURE__ */ jsx20(
|
|
1117
|
+
BaseSelect.Popup,
|
|
1118
|
+
{
|
|
1119
|
+
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",
|
|
1120
|
+
children: options.map((option) => /* @__PURE__ */ jsxs13(
|
|
1121
|
+
BaseSelect.Item,
|
|
1122
|
+
{
|
|
1123
|
+
value: option.value,
|
|
1124
|
+
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",
|
|
1125
|
+
children: [
|
|
1126
|
+
/* @__PURE__ */ jsxs13("span", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1127
|
+
option.leading && /* @__PURE__ */ jsx20("span", { className: "flex shrink-0 items-center", children: option.leading }),
|
|
1128
|
+
/* @__PURE__ */ jsx20(BaseSelect.ItemText, { className: "truncate", children: option.label })
|
|
1129
|
+
] }),
|
|
1130
|
+
/* @__PURE__ */ jsx20(
|
|
1131
|
+
BaseSelect.ItemIndicator,
|
|
1132
|
+
{
|
|
1133
|
+
render: /* @__PURE__ */ jsx20(IconCheck2, { size: 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
|
|
1134
|
+
}
|
|
1135
|
+
)
|
|
1136
|
+
]
|
|
1137
|
+
},
|
|
1138
|
+
option.value
|
|
1139
|
+
))
|
|
1140
|
+
}
|
|
1141
|
+
)
|
|
1142
|
+
}
|
|
1143
|
+
) })
|
|
1144
|
+
]
|
|
1145
|
+
}
|
|
1146
|
+
);
|
|
1212
1147
|
}
|
|
1213
1148
|
|
|
1214
1149
|
// src/TextInput.tsx
|
|
1215
1150
|
import { forwardRef } from "react";
|
|
1151
|
+
import { Input } from "@base-ui/react/input";
|
|
1216
1152
|
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1217
|
-
var TextInput = forwardRef(function TextInput2({ className, type = "text",
|
|
1218
|
-
const controlId = useFieldControlId(id);
|
|
1153
|
+
var TextInput = forwardRef(function TextInput2({ className, type = "text", ...props }, ref) {
|
|
1219
1154
|
return /* @__PURE__ */ jsx21(
|
|
1220
|
-
|
|
1155
|
+
Input,
|
|
1221
1156
|
{
|
|
1222
1157
|
ref,
|
|
1223
|
-
id: controlId,
|
|
1224
1158
|
type,
|
|
1225
1159
|
className: cn(
|
|
1226
1160
|
"bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2",
|
|
@@ -1237,14 +1171,14 @@ var TextInput = forwardRef(function TextInput2({ className, type = "text", id, .
|
|
|
1237
1171
|
|
|
1238
1172
|
// src/Textarea.tsx
|
|
1239
1173
|
import { forwardRef as forwardRef2 } from "react";
|
|
1174
|
+
import { Field as BaseField2 } from "@base-ui/react/field";
|
|
1240
1175
|
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1241
|
-
var Textarea = forwardRef2(function Textarea2({ className,
|
|
1242
|
-
const controlId = useFieldControlId(id);
|
|
1176
|
+
var Textarea = forwardRef2(function Textarea2({ className, ...props }, ref) {
|
|
1243
1177
|
return /* @__PURE__ */ jsx22(
|
|
1244
|
-
|
|
1178
|
+
BaseField2.Control,
|
|
1245
1179
|
{
|
|
1246
1180
|
ref,
|
|
1247
|
-
|
|
1181
|
+
render: /* @__PURE__ */ jsx22("textarea", {}),
|
|
1248
1182
|
className: cn(
|
|
1249
1183
|
"bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2",
|
|
1250
1184
|
"text-[14px] leading-[1.4] font-normal text-text-primary placeholder:text-text-muted",
|
|
@@ -1259,51 +1193,58 @@ var Textarea = forwardRef2(function Textarea2({ className, id, ...props }, ref)
|
|
|
1259
1193
|
});
|
|
1260
1194
|
|
|
1261
1195
|
// src/ConfirmDialog.tsx
|
|
1262
|
-
import { useState as
|
|
1196
|
+
import { useState as useState3 } from "react";
|
|
1263
1197
|
|
|
1264
1198
|
// 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
|
-
|
|
1199
|
+
import { useRef as useRef3 } from "react";
|
|
1200
|
+
import { Dialog } from "@base-ui/react/dialog";
|
|
1201
|
+
import { AlertDialog } from "@base-ui/react/alert-dialog";
|
|
1202
|
+
import { IconX as IconX3 } from "@tabler/icons-react";
|
|
1203
|
+
import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1204
|
+
function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, width = 502, alert = false }) {
|
|
1205
|
+
const Parts = alert ? AlertDialog : Dialog;
|
|
1206
|
+
const popupRef = useRef3(null);
|
|
1207
|
+
return /* @__PURE__ */ jsx23(
|
|
1208
|
+
Parts.Root,
|
|
1209
|
+
{
|
|
1210
|
+
open: true,
|
|
1211
|
+
onOpenChange: (open) => {
|
|
1212
|
+
if (!open) onClose();
|
|
1213
|
+
},
|
|
1214
|
+
children: /* @__PURE__ */ jsxs14(Parts.Portal, { children: [
|
|
1215
|
+
/* @__PURE__ */ jsx23(Parts.Backdrop, { className: "fixed inset-0 z-40 bg-scrim" }),
|
|
1216
|
+
/* @__PURE__ */ jsx23("div", { className: "fixed inset-0 z-50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs14(
|
|
1217
|
+
Parts.Popup,
|
|
1218
|
+
{
|
|
1219
|
+
ref: popupRef,
|
|
1220
|
+
initialFocus: popupRef,
|
|
1221
|
+
"aria-label": headerContent != null ? title : void 0,
|
|
1222
|
+
className: "bg-bg-elevated border border-border-subtle rounded-lg shadow-lg pointer-events-auto flex flex-col overflow-hidden outline-none",
|
|
1223
|
+
style: { width },
|
|
1224
|
+
children: [
|
|
1225
|
+
/* @__PURE__ */ jsxs14("div", { className: "h-12 flex items-center justify-between pl-5 pr-4 border-b border-border-subtle shrink-0", children: [
|
|
1226
|
+
headerContent ?? /* @__PURE__ */ jsx23(Parts.Title, { className: "text-h4 text-text-primary", render: /* @__PURE__ */ jsx23("span", {}), children: title }),
|
|
1227
|
+
/* @__PURE__ */ jsx23(
|
|
1228
|
+
Parts.Close,
|
|
1229
|
+
{
|
|
1230
|
+
render: /* @__PURE__ */ jsx23(IconButton, { tooltip: "Close", "aria-label": "Close", children: /* @__PURE__ */ jsx23(IconX3, { size: 16, stroke: 1.5 }) })
|
|
1231
|
+
}
|
|
1232
|
+
)
|
|
1233
|
+
] }),
|
|
1234
|
+
/* @__PURE__ */ jsx23("div", { className: cn("pl-5 pr-4 py-4", footer != null && "border-b border-border-subtle", bodyClassName), children }),
|
|
1235
|
+
footer != null && /* @__PURE__ */ jsx23("div", { className: "h-12 flex items-center justify-end gap-2 pl-5 pr-4 shrink-0", children: footer })
|
|
1236
|
+
]
|
|
1237
|
+
}
|
|
1238
|
+
) })
|
|
1239
|
+
] })
|
|
1240
|
+
}
|
|
1300
1241
|
);
|
|
1301
1242
|
}
|
|
1302
1243
|
|
|
1303
1244
|
// src/ConfirmDialog.tsx
|
|
1304
|
-
import { Fragment as
|
|
1245
|
+
import { Fragment as Fragment3, jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1305
1246
|
function ConfirmDialog({ title, children, confirmLabel, destructive = false, onConfirm, onClose }) {
|
|
1306
|
-
const [busy, setBusy] =
|
|
1247
|
+
const [busy, setBusy] = useState3(false);
|
|
1307
1248
|
const confirm = async () => {
|
|
1308
1249
|
setBusy(true);
|
|
1309
1250
|
try {
|
|
@@ -1316,10 +1257,11 @@ function ConfirmDialog({ title, children, confirmLabel, destructive = false, onC
|
|
|
1316
1257
|
return /* @__PURE__ */ jsx24(
|
|
1317
1258
|
DialogShell,
|
|
1318
1259
|
{
|
|
1260
|
+
alert: true,
|
|
1319
1261
|
title,
|
|
1320
1262
|
onClose,
|
|
1321
1263
|
bodyClassName: "flex flex-col gap-3 text-body-2 text-text-primary",
|
|
1322
|
-
footer: /* @__PURE__ */
|
|
1264
|
+
footer: /* @__PURE__ */ jsxs15(Fragment3, { children: [
|
|
1323
1265
|
/* @__PURE__ */ jsx24(Button, { variant: "muted", onClick: onClose, disabled: busy, children: "Cancel" }),
|
|
1324
1266
|
/* @__PURE__ */ jsx24(Button, { variant: destructive ? "destructive" : "primary", onClick: () => void confirm(), disabled: busy, children: confirmLabel })
|
|
1325
1267
|
] }),
|
|
@@ -1329,14 +1271,16 @@ function ConfirmDialog({ title, children, confirmLabel, destructive = false, onC
|
|
|
1329
1271
|
}
|
|
1330
1272
|
|
|
1331
1273
|
// src/EditableText.tsx
|
|
1332
|
-
import { useEffect as
|
|
1274
|
+
import { useEffect as useEffect2, useRef as useRef4, useState as useState4 } from "react";
|
|
1275
|
+
import { Field as BaseField3 } from "@base-ui/react/field";
|
|
1276
|
+
import { Input as Input2 } from "@base-ui/react/input";
|
|
1333
1277
|
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
1334
1278
|
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
|
-
|
|
1279
|
+
const [editing, setEditing] = useState4(false);
|
|
1280
|
+
const [draft, setDraft] = useState4(value);
|
|
1281
|
+
const [busy, setBusy] = useState4(false);
|
|
1282
|
+
const fieldRef = useRef4(null);
|
|
1283
|
+
useEffect2(() => {
|
|
1340
1284
|
if (editing) {
|
|
1341
1285
|
fieldRef.current?.focus();
|
|
1342
1286
|
fieldRef.current?.select();
|
|
@@ -1381,23 +1325,30 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
1381
1325
|
className
|
|
1382
1326
|
);
|
|
1383
1327
|
if (readOnly) {
|
|
1384
|
-
return
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1328
|
+
return (
|
|
1329
|
+
/*
|
|
1330
|
+
No `aria-label` here. It sat on a bare `<div>`, which has no role, and
|
|
1331
|
+
ARIA does not let an author name a generic element — so the name was
|
|
1332
|
+
written and never read. Read-only, this is a line of text: whatever
|
|
1333
|
+
names the region around it (a `Field`, a `Property`) names this too.
|
|
1334
|
+
*/
|
|
1335
|
+
/* @__PURE__ */ jsx25(
|
|
1336
|
+
"div",
|
|
1337
|
+
{
|
|
1338
|
+
className: cn("w-full px-2 py-1", multiline && !displayNode && "whitespace-pre-wrap", display ?? value ? "text-text-primary" : "text-text-muted", className),
|
|
1339
|
+
children: display ?? value ? displayNode ?? (display ?? value) : placeholder
|
|
1340
|
+
}
|
|
1341
|
+
)
|
|
1391
1342
|
);
|
|
1392
1343
|
}
|
|
1393
1344
|
if (editing) {
|
|
1394
1345
|
return multiline ? /* @__PURE__ */ jsx25(
|
|
1395
|
-
|
|
1346
|
+
BaseField3.Control,
|
|
1396
1347
|
{
|
|
1397
1348
|
ref: fieldRef,
|
|
1349
|
+
render: /* @__PURE__ */ jsx25("textarea", { rows: 4 }),
|
|
1398
1350
|
"aria-label": label,
|
|
1399
1351
|
value: draft,
|
|
1400
|
-
rows: 4,
|
|
1401
1352
|
disabled: busy,
|
|
1402
1353
|
onChange: (event) => setDraft(event.target.value),
|
|
1403
1354
|
onKeyDown,
|
|
@@ -1405,7 +1356,7 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
1405
1356
|
className: fieldClass
|
|
1406
1357
|
}
|
|
1407
1358
|
) : /* @__PURE__ */ jsx25(
|
|
1408
|
-
|
|
1359
|
+
Input2,
|
|
1409
1360
|
{
|
|
1410
1361
|
ref: fieldRef,
|
|
1411
1362
|
"aria-label": label,
|
|
@@ -1423,7 +1374,6 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
1423
1374
|
{
|
|
1424
1375
|
type: "button",
|
|
1425
1376
|
onClick: open,
|
|
1426
|
-
title: "Click to edit",
|
|
1427
1377
|
"aria-label": `Edit ${label.toLowerCase()}`,
|
|
1428
1378
|
className: cn(
|
|
1429
1379
|
"w-full rounded-md border border-transparent px-2 py-1 text-left transition-colors hover:border-border-default",
|
|
@@ -1438,22 +1388,22 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
|
|
|
1438
1388
|
|
|
1439
1389
|
// src/EmptyState.tsx
|
|
1440
1390
|
import { IconMessage2 } from "@tabler/icons-react";
|
|
1441
|
-
import { jsx as jsx26, jsxs as
|
|
1391
|
+
import { jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1442
1392
|
function EmptyState({ icon, message, className }) {
|
|
1443
|
-
return /* @__PURE__ */
|
|
1393
|
+
return /* @__PURE__ */ jsxs16("div", { className: cn("flex flex-col gap-2 items-center", className), children: [
|
|
1444
1394
|
/* @__PURE__ */ jsx26("span", { className: "text-text-secondary", children: icon ?? /* @__PURE__ */ jsx26(IconMessage2, { size: 16, stroke: 1.5 }) }),
|
|
1445
1395
|
/* @__PURE__ */ jsx26("p", { className: "text-body-2 text-text-secondary text-center", children: message })
|
|
1446
1396
|
] });
|
|
1447
1397
|
}
|
|
1448
1398
|
|
|
1449
1399
|
// src/Skeleton.tsx
|
|
1450
|
-
import { jsx as jsx27, jsxs as
|
|
1400
|
+
import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1451
1401
|
function SkeletonBar({ className, style }) {
|
|
1452
1402
|
return /* @__PURE__ */ jsx27("div", { className: cn("bg-bg-inset rounded-sm animate-pulse", className), style });
|
|
1453
1403
|
}
|
|
1454
1404
|
var ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160];
|
|
1455
1405
|
function SkeletonRow({ barWidth = 130 }) {
|
|
1456
|
-
return /* @__PURE__ */
|
|
1406
|
+
return /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-2 px-2 h-[32px] rounded-lg", children: [
|
|
1457
1407
|
/* @__PURE__ */ jsx27(SkeletonBar, { className: "w-4 h-4 shrink-0" }),
|
|
1458
1408
|
/* @__PURE__ */ jsx27(SkeletonBar, { className: "h-3.5", style: { width: barWidth } })
|
|
1459
1409
|
] });
|
|
@@ -1463,20 +1413,20 @@ function SkeletonList({ rows = 8, className }) {
|
|
|
1463
1413
|
}
|
|
1464
1414
|
|
|
1465
1415
|
// src/Breadcrumb.tsx
|
|
1466
|
-
import { Fragment as
|
|
1467
|
-
import { jsx as jsx28, jsxs as
|
|
1416
|
+
import { Fragment as Fragment4, useCallback as useCallback2, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState5 } from "react";
|
|
1417
|
+
import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1468
1418
|
function Breadcrumb({ items, className }) {
|
|
1469
|
-
const navRef =
|
|
1470
|
-
const labelRefs =
|
|
1471
|
-
const [truncated, setTruncated] =
|
|
1472
|
-
const measure =
|
|
1419
|
+
const navRef = useRef5(null);
|
|
1420
|
+
const labelRefs = useRef5([]);
|
|
1421
|
+
const [truncated, setTruncated] = useState5(/* @__PURE__ */ new Set());
|
|
1422
|
+
const measure = useCallback2(() => {
|
|
1473
1423
|
const next = /* @__PURE__ */ new Set();
|
|
1474
1424
|
labelRefs.current.forEach((el, index) => {
|
|
1475
1425
|
if (el && el.scrollWidth > el.clientWidth) next.add(index);
|
|
1476
1426
|
});
|
|
1477
1427
|
setTruncated((prev) => prev.size === next.size && [...next].every((i) => prev.has(i)) ? prev : next);
|
|
1478
1428
|
}, []);
|
|
1479
|
-
|
|
1429
|
+
useLayoutEffect2(() => {
|
|
1480
1430
|
measure();
|
|
1481
1431
|
const nav = navRef.current;
|
|
1482
1432
|
if (!nav || typeof ResizeObserver === "undefined") return;
|
|
@@ -1497,8 +1447,8 @@ function Breadcrumb({ items, className }) {
|
|
|
1497
1447
|
const setLabelRef = (el) => {
|
|
1498
1448
|
labelRefs.current[index] = el;
|
|
1499
1449
|
};
|
|
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__ */
|
|
1450
|
+
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 });
|
|
1451
|
+
return /* @__PURE__ */ jsxs18(Fragment4, { children: [
|
|
1502
1452
|
index > 0 && /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", className: "shrink-0 text-text-muted", children: "/" }),
|
|
1503
1453
|
truncated.has(index) ? (
|
|
1504
1454
|
// `min-w-0 shrink` undoes the wrapper's own shrink-0 — the crumb
|
|
@@ -1511,9 +1461,9 @@ function Breadcrumb({ items, className }) {
|
|
|
1511
1461
|
}
|
|
1512
1462
|
|
|
1513
1463
|
// src/NavItem.tsx
|
|
1514
|
-
import { jsx as jsx29, jsxs as
|
|
1464
|
+
import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1515
1465
|
function NavItem({ label, href, count, countLabel, active = false, icon, className, ...props }) {
|
|
1516
|
-
return /* @__PURE__ */
|
|
1466
|
+
return /* @__PURE__ */ jsxs19(
|
|
1517
1467
|
"a",
|
|
1518
1468
|
{
|
|
1519
1469
|
href,
|
|
@@ -1536,16 +1486,171 @@ function NavItem({ label, href, count, countLabel, active = false, icon, classNa
|
|
|
1536
1486
|
);
|
|
1537
1487
|
}
|
|
1538
1488
|
|
|
1489
|
+
// src/Popover.tsx
|
|
1490
|
+
import { useMemo as useMemo2 } from "react";
|
|
1491
|
+
import { Popover as BasePopover } from "@base-ui/react/popover";
|
|
1492
|
+
import { jsx as jsx30, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1493
|
+
var GAP5 = 4;
|
|
1494
|
+
var VIEWPORT_PAD4 = 8;
|
|
1495
|
+
function Popover({ trigger, anchor, align = "left", side = "bottom", open, onOpenChange, finalFocus, actionsRef, ariaLabel, children, className }) {
|
|
1496
|
+
const anchorTarget = useMemo2(() => {
|
|
1497
|
+
if (!anchor) return void 0;
|
|
1498
|
+
if (anchor instanceof Element) return anchor;
|
|
1499
|
+
return { getBoundingClientRect: () => anchor };
|
|
1500
|
+
}, [anchor]);
|
|
1501
|
+
return /* @__PURE__ */ jsxs20(
|
|
1502
|
+
BasePopover.Root,
|
|
1503
|
+
{
|
|
1504
|
+
open,
|
|
1505
|
+
onOpenChange: onOpenChange ? (next) => onOpenChange(next) : void 0,
|
|
1506
|
+
actionsRef,
|
|
1507
|
+
modal: false,
|
|
1508
|
+
children: [
|
|
1509
|
+
trigger && /* @__PURE__ */ jsx30(BasePopover.Trigger, { render: trigger }),
|
|
1510
|
+
/* @__PURE__ */ jsx30(BasePopover.Portal, { children: /* @__PURE__ */ jsx30(
|
|
1511
|
+
BasePopover.Positioner,
|
|
1512
|
+
{
|
|
1513
|
+
anchor: anchorTarget,
|
|
1514
|
+
side,
|
|
1515
|
+
align: align === "right" ? "end" : "start",
|
|
1516
|
+
sideOffset: GAP5,
|
|
1517
|
+
collisionPadding: VIEWPORT_PAD4,
|
|
1518
|
+
className: "z-50 data-[anchor-hidden]:hidden",
|
|
1519
|
+
children: /* @__PURE__ */ jsx30(
|
|
1520
|
+
BasePopover.Popup,
|
|
1521
|
+
{
|
|
1522
|
+
"aria-label": ariaLabel,
|
|
1523
|
+
initialFocus: trigger ? void 0 : false,
|
|
1524
|
+
finalFocus,
|
|
1525
|
+
className: cn("min-w-[180px] max-h-[var(--available-height)] overflow-y-auto outline-none", className),
|
|
1526
|
+
render: /* @__PURE__ */ jsx30(MenuPanel, {}),
|
|
1527
|
+
children
|
|
1528
|
+
}
|
|
1529
|
+
)
|
|
1530
|
+
}
|
|
1531
|
+
) })
|
|
1532
|
+
]
|
|
1533
|
+
}
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
// src/Toolbar.tsx
|
|
1538
|
+
import { Toolbar as BaseToolbar } from "@base-ui/react/toolbar";
|
|
1539
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
1540
|
+
function Toolbar({ "aria-label": ariaLabel, orientation = "horizontal", loopFocus = true, surface = true, children, className }) {
|
|
1541
|
+
const strip = /* @__PURE__ */ jsx31(
|
|
1542
|
+
BaseToolbar.Root,
|
|
1543
|
+
{
|
|
1544
|
+
"aria-label": ariaLabel,
|
|
1545
|
+
orientation,
|
|
1546
|
+
loopFocus,
|
|
1547
|
+
className: cn("flex items-center gap-1", orientation === "vertical" && "flex-col", className),
|
|
1548
|
+
children
|
|
1549
|
+
}
|
|
1550
|
+
);
|
|
1551
|
+
if (!surface) return strip;
|
|
1552
|
+
return /* @__PURE__ */ jsx31(MenuPanel, { className: "w-fit p-1", children: strip });
|
|
1553
|
+
}
|
|
1554
|
+
function ToolbarButton({ ref, disabled, disabledReason, ...props }) {
|
|
1555
|
+
return /* @__PURE__ */ jsx31(
|
|
1556
|
+
BaseToolbar.Button,
|
|
1557
|
+
{
|
|
1558
|
+
ref,
|
|
1559
|
+
disabled: disabled || !!disabledReason,
|
|
1560
|
+
focusableWhenDisabled: true,
|
|
1561
|
+
render: /* @__PURE__ */ jsx31(IconButton, { disabled, disabledReason, ...props })
|
|
1562
|
+
}
|
|
1563
|
+
);
|
|
1564
|
+
}
|
|
1565
|
+
function ToolbarInput(props) {
|
|
1566
|
+
return /* @__PURE__ */ jsx31(BaseToolbar.Input, { render: /* @__PURE__ */ jsx31(TextInput, {}), ...props });
|
|
1567
|
+
}
|
|
1568
|
+
function ToolbarSeparator({ className }) {
|
|
1569
|
+
return /* @__PURE__ */ jsx31(
|
|
1570
|
+
BaseToolbar.Separator,
|
|
1571
|
+
{
|
|
1572
|
+
orientation: "vertical",
|
|
1573
|
+
className: cn("mx-1 h-4 w-px shrink-0 bg-border-default", className)
|
|
1574
|
+
}
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
// src/ReactionPicker.tsx
|
|
1579
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
1580
|
+
function ReactionPicker({ options, onSelect, "aria-label": ariaLabel = "Reactions", surface = true, className }) {
|
|
1581
|
+
return /* @__PURE__ */ jsx32(Toolbar, { "aria-label": ariaLabel, surface, className: cn("gap-0.5", className), children: options.map((option) => (
|
|
1582
|
+
/*
|
|
1583
|
+
A `ToolbarButton`, not a button in a tooltip wrapper. The wrapper
|
|
1584
|
+
would become the toolbar's item and the button inside it would never
|
|
1585
|
+
join the walk — and it is unnecessary since stage 4, because an
|
|
1586
|
+
`IconButton` with a `tooltip` IS the trigger.
|
|
1587
|
+
|
|
1588
|
+
The tooltip names it for a pointer, `aria-label` for everything else,
|
|
1589
|
+
and both say the meaning rather than the glyph.
|
|
1590
|
+
*/
|
|
1591
|
+
/* @__PURE__ */ jsx32(
|
|
1592
|
+
ToolbarButton,
|
|
1593
|
+
{
|
|
1594
|
+
"aria-label": option.label,
|
|
1595
|
+
tooltip: option.label,
|
|
1596
|
+
onClick: () => onSelect(option.emoji),
|
|
1597
|
+
className: "size-7 text-[18px] leading-none",
|
|
1598
|
+
children: /* @__PURE__ */ jsx32("span", { "aria-hidden": "true", children: option.emoji })
|
|
1599
|
+
},
|
|
1600
|
+
option.emoji
|
|
1601
|
+
)
|
|
1602
|
+
)) });
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
// src/PreviewCard.tsx
|
|
1606
|
+
import { PreviewCard as BasePreviewCard } from "@base-ui/react/preview-card";
|
|
1607
|
+
import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1608
|
+
var GAP6 = 12;
|
|
1609
|
+
var VIEWPORT_PAD5 = 8;
|
|
1610
|
+
var OPEN_DELAY2 = 350;
|
|
1611
|
+
var CLOSE_DELAY = 200;
|
|
1612
|
+
function PreviewCard({ content, children, side = "right", delay = OPEN_DELAY2, closeDelay = CLOSE_DELAY, className, wrapperClassName }) {
|
|
1613
|
+
return /* @__PURE__ */ jsxs21(BasePreviewCard.Root, { children: [
|
|
1614
|
+
/* @__PURE__ */ jsx33(
|
|
1615
|
+
BasePreviewCard.Trigger,
|
|
1616
|
+
{
|
|
1617
|
+
delay,
|
|
1618
|
+
closeDelay,
|
|
1619
|
+
render: /* @__PURE__ */ jsx33("span", { className: cn("inline-flex", wrapperClassName) }),
|
|
1620
|
+
children
|
|
1621
|
+
}
|
|
1622
|
+
),
|
|
1623
|
+
/* @__PURE__ */ jsx33(BasePreviewCard.Portal, { children: /* @__PURE__ */ jsx33(
|
|
1624
|
+
BasePreviewCard.Positioner,
|
|
1625
|
+
{
|
|
1626
|
+
side,
|
|
1627
|
+
align: "start",
|
|
1628
|
+
sideOffset: GAP6,
|
|
1629
|
+
collisionPadding: VIEWPORT_PAD5,
|
|
1630
|
+
className: "z-50 data-[anchor-hidden]:hidden",
|
|
1631
|
+
children: /* @__PURE__ */ jsx33(
|
|
1632
|
+
BasePreviewCard.Popup,
|
|
1633
|
+
{
|
|
1634
|
+
className: cn("w-[360px] max-h-[min(300px,var(--available-height))] gap-3 overflow-y-auto p-3 outline-none [&>*]:shrink-0", className),
|
|
1635
|
+
render: /* @__PURE__ */ jsx33(MenuPanel, {}),
|
|
1636
|
+
children: content
|
|
1637
|
+
}
|
|
1638
|
+
)
|
|
1639
|
+
}
|
|
1640
|
+
) })
|
|
1641
|
+
] });
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1539
1644
|
// src/Rail.tsx
|
|
1540
|
-
import { jsx as
|
|
1645
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
1541
1646
|
function Rail({ "aria-label": ariaLabel = "Navigation", children, className }) {
|
|
1542
|
-
return /* @__PURE__ */
|
|
1647
|
+
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
1648
|
}
|
|
1544
1649
|
|
|
1545
1650
|
// src/RailItem.tsx
|
|
1546
|
-
import { jsx as
|
|
1651
|
+
import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1547
1652
|
function RailItem({ href, icon, label, active = false, className, ...props }) {
|
|
1548
|
-
return /* @__PURE__ */
|
|
1653
|
+
return /* @__PURE__ */ jsxs22(
|
|
1549
1654
|
"a",
|
|
1550
1655
|
{
|
|
1551
1656
|
href,
|
|
@@ -1553,14 +1658,14 @@ function RailItem({ href, icon, label, active = false, className, ...props }) {
|
|
|
1553
1658
|
className: cn("group flex w-full shrink-0 flex-col items-center gap-0.5 px-2 py-0.5", className),
|
|
1554
1659
|
...props,
|
|
1555
1660
|
children: [
|
|
1556
|
-
/* @__PURE__ */
|
|
1661
|
+
/* @__PURE__ */ jsx35(
|
|
1557
1662
|
"div",
|
|
1558
1663
|
{
|
|
1559
1664
|
className: cn(
|
|
1560
1665
|
"flex items-center justify-center rounded-lg p-2 transition-colors",
|
|
1561
1666
|
active ? "bg-bg-selected" : "group-hover:bg-bg-hover"
|
|
1562
1667
|
),
|
|
1563
|
-
children: /* @__PURE__ */
|
|
1668
|
+
children: /* @__PURE__ */ jsx35(
|
|
1564
1669
|
"span",
|
|
1565
1670
|
{
|
|
1566
1671
|
className: cn(
|
|
@@ -1572,16 +1677,16 @@ function RailItem({ href, icon, label, active = false, className, ...props }) {
|
|
|
1572
1677
|
)
|
|
1573
1678
|
}
|
|
1574
1679
|
),
|
|
1575
|
-
/* @__PURE__ */
|
|
1680
|
+
/* @__PURE__ */ jsx35("span", { className: cn("text-center text-[9px] font-medium leading-[115%]", active ? "text-text-primary" : "text-text-secondary"), children: label })
|
|
1576
1681
|
]
|
|
1577
1682
|
}
|
|
1578
1683
|
);
|
|
1579
1684
|
}
|
|
1580
1685
|
|
|
1581
1686
|
// src/Sidebar.tsx
|
|
1582
|
-
import { jsx as
|
|
1687
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
1583
1688
|
function Sidebar({ "aria-label": ariaLabel = "Workspace", children, className }) {
|
|
1584
|
-
return /* @__PURE__ */
|
|
1689
|
+
return /* @__PURE__ */ jsx36(
|
|
1585
1690
|
"nav",
|
|
1586
1691
|
{
|
|
1587
1692
|
"aria-label": ariaLabel,
|
|
@@ -1595,29 +1700,29 @@ function Sidebar({ "aria-label": ariaLabel = "Workspace", children, className })
|
|
|
1595
1700
|
}
|
|
1596
1701
|
|
|
1597
1702
|
// src/Property.tsx
|
|
1598
|
-
import { jsx as
|
|
1703
|
+
import { jsx as jsx37, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1599
1704
|
function Property({ label, layout = "row", children, className }) {
|
|
1600
1705
|
if (layout === "stacked") {
|
|
1601
1706
|
return (
|
|
1602
1707
|
// gap-3 — the one label-above-content distance (Katerina, 2026-09-01):
|
|
1603
1708
|
// Peek's topic-details sections already sit at 12px, Ship's rail was
|
|
1604
1709
|
// 6px, and unifying means the bigger, calmer one.
|
|
1605
|
-
/* @__PURE__ */
|
|
1606
|
-
/* @__PURE__ */
|
|
1710
|
+
/* @__PURE__ */ jsxs23("div", { className: cn("flex flex-col gap-3", className), children: [
|
|
1711
|
+
/* @__PURE__ */ jsx37("span", { className: "text-menu uppercase tracking-[0.08em] text-text-secondary", children: label }),
|
|
1607
1712
|
children
|
|
1608
1713
|
] })
|
|
1609
1714
|
);
|
|
1610
1715
|
}
|
|
1611
|
-
return /* @__PURE__ */
|
|
1612
|
-
/* @__PURE__ */
|
|
1716
|
+
return /* @__PURE__ */ jsxs23("div", { className: cn("flex items-center gap-2", className), children: [
|
|
1717
|
+
/* @__PURE__ */ jsx37("span", { className: "w-[68px] shrink-0 text-caption text-text-secondary", children: label }),
|
|
1613
1718
|
children
|
|
1614
1719
|
] });
|
|
1615
1720
|
}
|
|
1616
1721
|
|
|
1617
1722
|
// src/Reaction.tsx
|
|
1618
|
-
import { jsx as
|
|
1723
|
+
import { jsx as jsx38, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1619
1724
|
function Reaction({ emoji, count, pressed = false, className, type, ...props }) {
|
|
1620
|
-
return /* @__PURE__ */
|
|
1725
|
+
return /* @__PURE__ */ jsxs24(
|
|
1621
1726
|
"button",
|
|
1622
1727
|
{
|
|
1623
1728
|
type: type ?? "button",
|
|
@@ -1633,8 +1738,8 @@ function Reaction({ emoji, count, pressed = false, className, type, ...props })
|
|
|
1633
1738
|
),
|
|
1634
1739
|
...props,
|
|
1635
1740
|
children: [
|
|
1636
|
-
/* @__PURE__ */
|
|
1637
|
-
/* @__PURE__ */
|
|
1741
|
+
/* @__PURE__ */ jsx38("span", { "aria-hidden": "true", className: "shrink-0 text-[16px] leading-none", children: emoji }),
|
|
1742
|
+
/* @__PURE__ */ jsx38("span", { className: "text-chip signal:font-mono signal:text-[10px] signal:font-semibold signal:tabular-nums", children: count })
|
|
1638
1743
|
]
|
|
1639
1744
|
}
|
|
1640
1745
|
);
|
|
@@ -1642,9 +1747,10 @@ function Reaction({ emoji, count, pressed = false, className, type, ...props })
|
|
|
1642
1747
|
|
|
1643
1748
|
// src/SearchInput.tsx
|
|
1644
1749
|
import "react";
|
|
1645
|
-
import {
|
|
1750
|
+
import { Input as Input3 } from "@base-ui/react/input";
|
|
1751
|
+
import { jsx as jsx39, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
1646
1752
|
function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...props }) {
|
|
1647
|
-
return /* @__PURE__ */
|
|
1753
|
+
return /* @__PURE__ */ jsxs25(
|
|
1648
1754
|
"div",
|
|
1649
1755
|
{
|
|
1650
1756
|
className: cn(
|
|
@@ -1654,27 +1760,27 @@ function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...pro
|
|
|
1654
1760
|
className
|
|
1655
1761
|
),
|
|
1656
1762
|
children: [
|
|
1657
|
-
/* @__PURE__ */
|
|
1658
|
-
|
|
1763
|
+
/* @__PURE__ */ jsx39(
|
|
1764
|
+
Input3,
|
|
1659
1765
|
{
|
|
1660
1766
|
className: "flex-1 min-w-0 bg-transparent text-input-value text-text-primary placeholder:text-text-muted outline-none",
|
|
1661
1767
|
placeholder,
|
|
1662
1768
|
...props
|
|
1663
1769
|
}
|
|
1664
1770
|
),
|
|
1665
|
-
shortcut && /* @__PURE__ */
|
|
1771
|
+
shortcut && /* @__PURE__ */ jsx39(Kbd, { children: shortcut })
|
|
1666
1772
|
]
|
|
1667
1773
|
}
|
|
1668
1774
|
);
|
|
1669
1775
|
}
|
|
1670
1776
|
|
|
1671
1777
|
// src/SectionHeader.tsx
|
|
1672
|
-
import { useState as
|
|
1778
|
+
import { useState as useState6 } from "react";
|
|
1673
1779
|
import { IconChevronRight as IconChevronRight2 } from "@tabler/icons-react";
|
|
1674
|
-
import { jsx as
|
|
1780
|
+
import { jsx as jsx40, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
1675
1781
|
function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, actions, showActions = "hover", className }) {
|
|
1676
|
-
const [isHovered, setIsHovered] =
|
|
1677
|
-
return /* @__PURE__ */
|
|
1782
|
+
const [isHovered, setIsHovered] = useState6(false);
|
|
1783
|
+
return /* @__PURE__ */ jsxs26(
|
|
1678
1784
|
"div",
|
|
1679
1785
|
{
|
|
1680
1786
|
className: cn(
|
|
@@ -1687,8 +1793,8 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, ac
|
|
|
1687
1793
|
onMouseEnter: () => setIsHovered(true),
|
|
1688
1794
|
onMouseLeave: () => setIsHovered(false),
|
|
1689
1795
|
children: [
|
|
1690
|
-
/* @__PURE__ */
|
|
1691
|
-
chevron && /* @__PURE__ */
|
|
1796
|
+
/* @__PURE__ */ jsxs26("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
1797
|
+
chevron && /* @__PURE__ */ jsx40(
|
|
1692
1798
|
IconChevronRight2,
|
|
1693
1799
|
{
|
|
1694
1800
|
size: 12,
|
|
@@ -1696,9 +1802,9 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, ac
|
|
|
1696
1802
|
className: cn("text-text-secondary transition-transform duration-150", isExpanded && "rotate-90")
|
|
1697
1803
|
}
|
|
1698
1804
|
),
|
|
1699
|
-
/* @__PURE__ */
|
|
1805
|
+
/* @__PURE__ */ jsx40(SectionLabel, { children: title })
|
|
1700
1806
|
] }),
|
|
1701
|
-
(showActions === "always" || isHovered) && actions && actions.length > 0 && /* @__PURE__ */
|
|
1807
|
+
(showActions === "always" || isHovered) && actions && actions.length > 0 && /* @__PURE__ */ jsx40("div", { className: "flex items-center gap-1", children: actions.map((action) => /* @__PURE__ */ jsx40(
|
|
1702
1808
|
IconButton,
|
|
1703
1809
|
{
|
|
1704
1810
|
tooltip: action.tooltip,
|
|
@@ -1718,9 +1824,9 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, ac
|
|
|
1718
1824
|
|
|
1719
1825
|
// src/Tabs.tsx
|
|
1720
1826
|
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__ */
|
|
1827
|
+
import { jsx as jsx41, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
1828
|
+
function Tabs({ tabs, active, onChange, size = "default", className, ...aria }) {
|
|
1829
|
+
return /* @__PURE__ */ jsx41(
|
|
1724
1830
|
BaseTabs.Root,
|
|
1725
1831
|
{
|
|
1726
1832
|
value: active,
|
|
@@ -1728,36 +1834,45 @@ function Tabs({ tabs, active, onChange, size = "default", className }) {
|
|
|
1728
1834
|
if (details.reason === "none") onChange(value);
|
|
1729
1835
|
},
|
|
1730
1836
|
className,
|
|
1731
|
-
children: /* @__PURE__ */
|
|
1732
|
-
BaseTabs.
|
|
1837
|
+
children: /* @__PURE__ */ jsx41(
|
|
1838
|
+
BaseTabs.List,
|
|
1733
1839
|
{
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1840
|
+
activateOnFocus: true,
|
|
1841
|
+
"aria-label": aria["aria-label"],
|
|
1842
|
+
"aria-labelledby": aria["aria-labelledby"],
|
|
1843
|
+
className: "flex items-center gap-2",
|
|
1844
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsxs27(
|
|
1845
|
+
BaseTabs.Tab,
|
|
1846
|
+
{
|
|
1847
|
+
value: tab.id,
|
|
1848
|
+
className: (state) => cn(
|
|
1849
|
+
"flex cursor-pointer items-center transition-colors",
|
|
1850
|
+
// gap: default is Ship's 6px; small keeps Peek's original 4px, or
|
|
1851
|
+
// "small is Peek's geometry" stops being true.
|
|
1852
|
+
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",
|
|
1853
|
+
state.active ? "bg-bg-active text-text-primary" : "text-text-secondary hover:bg-bg-hover"
|
|
1854
|
+
),
|
|
1855
|
+
children: [
|
|
1856
|
+
tab.icon,
|
|
1857
|
+
/* @__PURE__ */ jsxs27("span", { className: "flex items-baseline", children: [
|
|
1858
|
+
tab.label,
|
|
1859
|
+
tab.count !== void 0 ? /* @__PURE__ */ jsx41("span", { className: "ml-2.5 font-mono text-caption tabular-nums text-text-secondary", children: tab.count }) : null
|
|
1860
|
+
] })
|
|
1861
|
+
]
|
|
1862
|
+
},
|
|
1863
|
+
tab.id
|
|
1864
|
+
))
|
|
1865
|
+
}
|
|
1866
|
+
)
|
|
1752
1867
|
}
|
|
1753
1868
|
);
|
|
1754
1869
|
}
|
|
1755
1870
|
|
|
1756
1871
|
// src/Toast.tsx
|
|
1757
|
-
import { createContext as
|
|
1758
|
-
import { createPortal as
|
|
1872
|
+
import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect3, useMemo as useMemo3, useState as useState7 } from "react";
|
|
1873
|
+
import { createPortal as createPortal2 } from "react-dom";
|
|
1759
1874
|
import { IconCircleCheck } from "@tabler/icons-react";
|
|
1760
|
-
import { jsx as
|
|
1875
|
+
import { jsx as jsx42, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
1761
1876
|
var SURFACE_STYLES = {
|
|
1762
1877
|
success: "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
|
|
1763
1878
|
brand: "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
|
|
@@ -1775,7 +1890,7 @@ var ACTION_BORDER_STYLES = {
|
|
|
1775
1890
|
};
|
|
1776
1891
|
function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAction, className }) {
|
|
1777
1892
|
const hasAction = !!(actionLabel && onAction);
|
|
1778
|
-
return /* @__PURE__ */
|
|
1893
|
+
return /* @__PURE__ */ jsxs28(
|
|
1779
1894
|
"div",
|
|
1780
1895
|
{
|
|
1781
1896
|
className: cn(
|
|
@@ -1787,11 +1902,11 @@ function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAct
|
|
|
1787
1902
|
className
|
|
1788
1903
|
),
|
|
1789
1904
|
children: [
|
|
1790
|
-
/* @__PURE__ */
|
|
1791
|
-
leadingIcon && /* @__PURE__ */
|
|
1792
|
-
/* @__PURE__ */
|
|
1905
|
+
/* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
1906
|
+
leadingIcon && /* @__PURE__ */ jsx42(IconCircleCheck, { size: 16, stroke: 1.5, className: cn("text-text-primary shrink-0", ICON_STYLES[type]) }),
|
|
1907
|
+
/* @__PURE__ */ jsx42("span", { className: "font-normal text-[14px] leading-[1.4] text-text-primary whitespace-nowrap", children: label })
|
|
1793
1908
|
] }),
|
|
1794
|
-
hasAction && /* @__PURE__ */
|
|
1909
|
+
hasAction && /* @__PURE__ */ jsx42(
|
|
1795
1910
|
"button",
|
|
1796
1911
|
{
|
|
1797
1912
|
type: "button",
|
|
@@ -1801,24 +1916,24 @@ function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAct
|
|
|
1801
1916
|
ACTION_BORDER_STYLES[type],
|
|
1802
1917
|
type === "neutral" ? "hover:border-border-strong" : "hover:opacity-80"
|
|
1803
1918
|
),
|
|
1804
|
-
children: /* @__PURE__ */
|
|
1919
|
+
children: /* @__PURE__ */ jsx42("span", { className: "font-medium text-[12px] leading-[12px] text-text-primary whitespace-nowrap", children: actionLabel })
|
|
1805
1920
|
}
|
|
1806
1921
|
)
|
|
1807
1922
|
]
|
|
1808
1923
|
}
|
|
1809
1924
|
);
|
|
1810
1925
|
}
|
|
1811
|
-
var ToastContext =
|
|
1926
|
+
var ToastContext = createContext2(null);
|
|
1812
1927
|
var toastSeq = 0;
|
|
1813
1928
|
function ToastProvider({ children }) {
|
|
1814
|
-
const [toast, setToast] =
|
|
1815
|
-
const showToast =
|
|
1929
|
+
const [toast, setToast] = useState7(null);
|
|
1930
|
+
const showToast = useCallback3((opts) => {
|
|
1816
1931
|
setToast({ ...opts, id: ++toastSeq });
|
|
1817
1932
|
}, []);
|
|
1818
|
-
const dismissToast =
|
|
1933
|
+
const dismissToast = useCallback3(() => {
|
|
1819
1934
|
setToast(null);
|
|
1820
1935
|
}, []);
|
|
1821
|
-
|
|
1936
|
+
useEffect3(() => {
|
|
1822
1937
|
if (!toast) return;
|
|
1823
1938
|
const duration = toast.durationMs ?? 5e3;
|
|
1824
1939
|
if (duration <= 0) return;
|
|
@@ -1828,10 +1943,10 @@ function ToastProvider({ children }) {
|
|
|
1828
1943
|
return () => clearTimeout(timer);
|
|
1829
1944
|
}, [toast]);
|
|
1830
1945
|
const value = useMemo3(() => ({ showToast, dismissToast }), [showToast, dismissToast]);
|
|
1831
|
-
return /* @__PURE__ */
|
|
1946
|
+
return /* @__PURE__ */ jsxs28(ToastContext.Provider, { value, children: [
|
|
1832
1947
|
children,
|
|
1833
|
-
toast &&
|
|
1834
|
-
/* @__PURE__ */
|
|
1948
|
+
toast && createPortal2(
|
|
1949
|
+
/* @__PURE__ */ jsx42("div", { className: "fixed bottom-4 left-4 z-[100] pointer-events-none", children: /* @__PURE__ */ jsx42(
|
|
1835
1950
|
Toast,
|
|
1836
1951
|
{
|
|
1837
1952
|
className: "pointer-events-auto",
|
|
@@ -1850,7 +1965,7 @@ function ToastProvider({ children }) {
|
|
|
1850
1965
|
] });
|
|
1851
1966
|
}
|
|
1852
1967
|
function useToast() {
|
|
1853
|
-
const ctx =
|
|
1968
|
+
const ctx = useContext2(ToastContext);
|
|
1854
1969
|
if (!ctx) throw new Error("useToast must be used within ToastProvider");
|
|
1855
1970
|
return ctx;
|
|
1856
1971
|
}
|
|
@@ -1885,10 +2000,13 @@ export {
|
|
|
1885
2000
|
NavItem,
|
|
1886
2001
|
Person,
|
|
1887
2002
|
PersonTrigger,
|
|
2003
|
+
Popover,
|
|
2004
|
+
PreviewCard,
|
|
1888
2005
|
Property,
|
|
1889
2006
|
Rail,
|
|
1890
2007
|
RailItem,
|
|
1891
2008
|
Reaction,
|
|
2009
|
+
ReactionPicker,
|
|
1892
2010
|
SearchInput,
|
|
1893
2011
|
SectionHeader,
|
|
1894
2012
|
SectionLabel,
|
|
@@ -1902,16 +2020,17 @@ export {
|
|
|
1902
2020
|
Textarea,
|
|
1903
2021
|
Toast,
|
|
1904
2022
|
ToastProvider,
|
|
2023
|
+
Toolbar,
|
|
2024
|
+
ToolbarButton,
|
|
2025
|
+
ToolbarInput,
|
|
2026
|
+
ToolbarSeparator,
|
|
1905
2027
|
Tooltip,
|
|
2028
|
+
TooltipProvider,
|
|
1906
2029
|
TopBar,
|
|
1907
2030
|
WithTooltip,
|
|
1908
|
-
clampBox,
|
|
1909
2031
|
cn,
|
|
1910
|
-
fitMenu,
|
|
1911
|
-
fitSubmenu,
|
|
1912
2032
|
hueFor,
|
|
1913
2033
|
initialsFor,
|
|
1914
|
-
useFieldControlId,
|
|
1915
2034
|
useToast
|
|
1916
2035
|
};
|
|
1917
2036
|
//# sourceMappingURL=index.js.map
|