@estiva-app/ui 0.55.0 → 0.57.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/ChipInput.d.ts.map +1 -1
- package/dist/CollapsibleSection.d.ts +8 -2
- package/dist/CollapsibleSection.d.ts.map +1 -1
- package/dist/CommandPalette.d.ts.map +1 -1
- package/dist/ContainerHeader.d.ts +7 -1
- package/dist/ContainerHeader.d.ts.map +1 -1
- package/dist/IconButton.d.ts.map +1 -1
- package/dist/ListColumn.d.ts +2 -0
- package/dist/ListColumn.d.ts.map +1 -1
- package/dist/Menu.d.ts.map +1 -1
- package/dist/NavItem.d.ts.map +1 -1
- package/dist/NavTree.d.ts +11 -3
- package/dist/NavTree.d.ts.map +1 -1
- package/dist/PersonTrigger.d.ts.map +1 -1
- package/dist/SectionHeader.d.ts +24 -5
- package/dist/SectionHeader.d.ts.map +1 -1
- package/dist/SectionLabel.d.ts.map +1 -1
- package/dist/Select.d.ts.map +1 -1
- package/dist/SuggestionMenu.d.ts.map +1 -1
- package/dist/gates/index.js +1 -1
- package/dist/gates/index.js.map +2 -2
- package/dist/index.js +138 -77
- package/dist/index.js.map +4 -4
- package/dist/looks.d.ts +4 -0
- package/dist/looks.d.ts.map +1 -1
- package/package.json +1 -1
- package/registry.json +98 -26
- package/src/ChipInput.test.tsx +8 -0
- package/src/ChipInput.tsx +4 -2
- package/src/CollapsibleSection.mdx +1 -1
- package/src/CollapsibleSection.stories.tsx +14 -1
- package/src/CollapsibleSection.test.tsx +24 -0
- package/src/CollapsibleSection.tsx +11 -5
- package/src/CommandPalette.test.tsx +6 -0
- package/src/CommandPalette.tsx +3 -2
- package/src/ContainerHeader.mdx +4 -0
- package/src/ContainerHeader.stories.tsx +14 -0
- package/src/ContainerHeader.test.tsx +8 -0
- package/src/ContainerHeader.tsx +34 -6
- package/src/IconButton.tsx +2 -1
- package/src/ListColumn.test.tsx +8 -0
- package/src/ListColumn.tsx +4 -2
- package/src/Menu.tsx +5 -4
- package/src/NavItem.mdx +3 -1
- package/src/NavItem.test.tsx +32 -2
- package/src/NavItem.tsx +28 -12
- package/src/NavTree.mdx +16 -1
- package/src/NavTree.stories.tsx +49 -2
- package/src/NavTree.test.tsx +28 -0
- package/src/NavTree.tsx +27 -6
- package/src/PersonTrigger.tsx +6 -5
- package/src/SectionHeader.mdx +10 -3
- package/src/SectionHeader.stories.tsx +18 -0
- package/src/SectionHeader.test.tsx +89 -0
- package/src/SectionHeader.tsx +114 -26
- package/src/SectionLabel.tsx +6 -3
- package/src/Select.test.tsx +8 -0
- package/src/Select.tsx +5 -2
- package/src/Sidebar.tsx +1 -1
- package/src/SuggestionMenu.stories.tsx +15 -0
- package/src/SuggestionMenu.test.tsx +8 -0
- package/src/SuggestionMenu.tsx +4 -2
- package/src/eslint/no-copied-look.test.ts +2 -2
- package/src/gates/app-checks.ts +1 -1
- package/src/looks.ts +6 -0
package/dist/index.js
CHANGED
|
@@ -168,7 +168,7 @@ function Sidebar({ "aria-label": ariaLabel = "Workspace", children, className })
|
|
|
168
168
|
{
|
|
169
169
|
"aria-label": ariaLabel,
|
|
170
170
|
className: cn("flex w-60 shrink-0 flex-col", !onGround && "border-r border-border-default bg-bg-surface", className),
|
|
171
|
-
children: /* @__PURE__ */ jsx4(ScrollArea, { className: "min-h-0 flex-1", contentClassName: "flex flex-col gap-
|
|
171
|
+
children: /* @__PURE__ */ jsx4(ScrollArea, { className: "min-h-0 flex-1", contentClassName: "flex flex-col gap-0.5 px-2.5 py-3", children })
|
|
172
172
|
}
|
|
173
173
|
);
|
|
174
174
|
}
|
|
@@ -276,6 +276,25 @@ import {
|
|
|
276
276
|
// src/IconButton.tsx
|
|
277
277
|
import { Button as BaseButton } from "@base-ui/react/button";
|
|
278
278
|
|
|
279
|
+
// src/looks.ts
|
|
280
|
+
var FIELD_BOX_CLASSES = "bg-bg-field border border-border-field shadow-field hover:border-border-strong group-hover/field:border-border-strong focus:border-border-focus group-hover/field:focus:border-border-focus rounded-lg";
|
|
281
|
+
var FIELD_TEXT_CLASSES = "text-text-primary placeholder:text-text-muted";
|
|
282
|
+
var FIELD_DISABLED_CLASSES = "disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled disabled:border-border-field group-hover/field:disabled:border-border-field";
|
|
283
|
+
var FIELD_FOCUS_RING_CLASSES = "signal:transition-shadow signal:focus:shadow-focus-ring";
|
|
284
|
+
var FIELD_SIZE_CLASSES = {
|
|
285
|
+
default: "px-3 py-2 text-input-value",
|
|
286
|
+
small: "h-6 min-h-6 px-2 text-caption"
|
|
287
|
+
};
|
|
288
|
+
var FIELD_SHELL_CLASSES = "bg-bg-field border border-border-field shadow-field hover:border-border-strong group-hover/field:border-border-strong rounded-lg transition-colors";
|
|
289
|
+
var FLOATING_SURFACE_CLASSES = "rounded-lg border border-border-default bg-bg-elevated shadow-lg";
|
|
290
|
+
var CHIP_TEXT_CLASSES = "text-chip signal:font-mono signal:text-small signal:font-semibold signal:tabular-nums";
|
|
291
|
+
var BARE_INPUT_CLASSES = "bg-transparent text-text-primary placeholder:text-text-muted outline-none";
|
|
292
|
+
var SIDEBAR_ROW_CLASSES = "rounded-lg transition-colors";
|
|
293
|
+
var SIDEBAR_ROW_TEXT_CLASSES = "text-text-secondary hover:text-text-primary";
|
|
294
|
+
var MUTED_CONTROL_CLASSES = "text-text-secondary hover:bg-bg-hover hover:text-text-primary";
|
|
295
|
+
var MENU_TRIGGER_CLASSES = "cursor-pointer rounded-md transition-colors hover:bg-bg-hover data-[popup-open]:bg-bg-hover";
|
|
296
|
+
var COLLAPSIBLE_PANEL_CLASSES = "h-[var(--collapsible-panel-height)] overflow-hidden transition-[height] duration-150 ease-out motion-reduce:transition-none data-[starting-style]:h-0 data-[ending-style]:h-0";
|
|
297
|
+
|
|
279
298
|
// src/formBusy.ts
|
|
280
299
|
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
281
300
|
var FormBusyContext = createContext2(false);
|
|
@@ -306,23 +325,6 @@ function Kbd({ children, className, tone = "default" }) {
|
|
|
306
325
|
);
|
|
307
326
|
}
|
|
308
327
|
|
|
309
|
-
// src/looks.ts
|
|
310
|
-
var FIELD_BOX_CLASSES = "bg-bg-field border border-border-field shadow-field hover:border-border-strong group-hover/field:border-border-strong focus:border-border-focus group-hover/field:focus:border-border-focus rounded-lg";
|
|
311
|
-
var FIELD_TEXT_CLASSES = "text-text-primary placeholder:text-text-muted";
|
|
312
|
-
var FIELD_DISABLED_CLASSES = "disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled disabled:border-border-field group-hover/field:disabled:border-border-field";
|
|
313
|
-
var FIELD_FOCUS_RING_CLASSES = "signal:transition-shadow signal:focus:shadow-focus-ring";
|
|
314
|
-
var FIELD_SIZE_CLASSES = {
|
|
315
|
-
default: "px-3 py-2 text-input-value",
|
|
316
|
-
small: "h-6 min-h-6 px-2 text-caption"
|
|
317
|
-
};
|
|
318
|
-
var FIELD_SHELL_CLASSES = "bg-bg-field border border-border-field shadow-field hover:border-border-strong group-hover/field:border-border-strong rounded-lg transition-colors";
|
|
319
|
-
var FLOATING_SURFACE_CLASSES = "rounded-lg border border-border-default bg-bg-elevated shadow-lg";
|
|
320
|
-
var CHIP_TEXT_CLASSES = "text-chip signal:font-mono signal:text-small signal:font-semibold signal:tabular-nums";
|
|
321
|
-
var BARE_INPUT_CLASSES = "bg-transparent text-text-primary placeholder:text-text-muted outline-none";
|
|
322
|
-
var SIDEBAR_ROW_CLASSES = "rounded-lg transition-colors";
|
|
323
|
-
var SIDEBAR_ROW_TEXT_CLASSES = "text-text-secondary hover:text-text-primary";
|
|
324
|
-
var COLLAPSIBLE_PANEL_CLASSES = "h-[var(--collapsible-panel-height)] overflow-hidden transition-[height] duration-150 ease-out motion-reduce:transition-none data-[starting-style]:h-0 data-[ending-style]:h-0";
|
|
325
|
-
|
|
326
328
|
// src/Tooltip.tsx
|
|
327
329
|
import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
328
330
|
var OPEN_DELAY = 300;
|
|
@@ -410,7 +412,7 @@ function IconButton({
|
|
|
410
412
|
// stretched and carries no `self-center` — see the note there.
|
|
411
413
|
"flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 self-center cursor-pointer",
|
|
412
414
|
!off && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse",
|
|
413
|
-
!off && (variant === "muted" || variant === "resolve") &&
|
|
415
|
+
!off && (variant === "muted" || variant === "resolve") && MUTED_CONTROL_CLASSES,
|
|
414
416
|
!off && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-secondary",
|
|
415
417
|
!off && variant === "current" && "hover:bg-bg-hover",
|
|
416
418
|
off && variant === "primary" && "bg-bg-disabled text-text-disabled",
|
|
@@ -1008,9 +1010,12 @@ function SectionLabel({
|
|
|
1008
1010
|
"span",
|
|
1009
1011
|
{
|
|
1010
1012
|
className: cn(
|
|
1011
|
-
"text-h5 leading-3
|
|
1012
|
-
//
|
|
1013
|
-
//
|
|
1013
|
+
"text-h5 leading-3",
|
|
1014
|
+
// Ship's type in every theme but Leaf, whose section labels are the
|
|
1015
|
+
// micro-label (Katerina, 28 September) unless the group folds (2
|
|
1016
|
+
// October). Signal dropped its micro-label on 5 October: every one of
|
|
1017
|
+
// Peek's section labels is Ship's type, folding or not, so Desk's
|
|
1018
|
+
// Screener and its Urgent and Open work read as one.
|
|
1014
1019
|
!folds && "leaf:font-mono leaf:text-small leaf:uppercase leaf:tracking-widest",
|
|
1015
1020
|
tone === "secondary" ? "text-text-secondary" : tone === "muted" ? "text-text-muted" : "text-text-primary",
|
|
1016
1021
|
truncate && "min-w-0 truncate",
|
|
@@ -1029,7 +1034,8 @@ function MenuPanel({ children, className, ...props }) {
|
|
|
1029
1034
|
"div",
|
|
1030
1035
|
{
|
|
1031
1036
|
className: cn(
|
|
1032
|
-
|
|
1037
|
+
// 2px between rows, as a list column's and a sidebar's (Katerina, 5 October).
|
|
1038
|
+
"flex flex-col gap-0.5 p-2",
|
|
1033
1039
|
FLOATING_SURFACE_CLASSES,
|
|
1034
1040
|
/*
|
|
1035
1041
|
* A divider in a menu runs the width of the rows it separates.
|
|
@@ -1088,7 +1094,7 @@ function Menu({ trigger, align = "left", openOnHover = false, open, onOpenChange
|
|
|
1088
1094
|
"data-interactive": true,
|
|
1089
1095
|
className: cn("min-w-[180px] outline-none p-0", className),
|
|
1090
1096
|
render: /* @__PURE__ */ jsx18(MenuPanel, {}),
|
|
1091
|
-
children: /* @__PURE__ */ jsx18(ScrollArea, { viewportClassName: "max-h-[var(--available-height)]", contentClassName: cn("flex flex-col p-2", contentClassName), children: /* @__PURE__ */ jsx18(MenuContext.Provider, { value: { openOnHover }, children: /* @__PURE__ */ jsx18(DividerInPanel.Provider, { value: true, children }) }) })
|
|
1097
|
+
children: /* @__PURE__ */ jsx18(ScrollArea, { viewportClassName: "max-h-[var(--available-height)]", contentClassName: cn("flex flex-col gap-0.5 p-2", contentClassName), children: /* @__PURE__ */ jsx18(MenuContext.Provider, { value: { openOnHover }, children: /* @__PURE__ */ jsx18(DividerInPanel.Provider, { value: true, children }) }) })
|
|
1092
1098
|
}
|
|
1093
1099
|
)
|
|
1094
1100
|
}
|
|
@@ -1219,12 +1225,12 @@ function MenuSection({ label, children, className }) {
|
|
|
1219
1225
|
const inMenu = useContext4(MenuContext) !== null;
|
|
1220
1226
|
const heading = /* @__PURE__ */ jsx18("div", { className: cn("flex h-8 items-center px-2", className), children: /* @__PURE__ */ jsx18(SectionLabel, { tone: "secondary", children: label }) });
|
|
1221
1227
|
if (!inMenu) {
|
|
1222
|
-
return /* @__PURE__ */ jsxs11("div", { className: "flex flex-col", children: [
|
|
1228
|
+
return /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-0.5", children: [
|
|
1223
1229
|
heading,
|
|
1224
1230
|
children
|
|
1225
1231
|
] });
|
|
1226
1232
|
}
|
|
1227
|
-
return /* @__PURE__ */ jsxs11(BaseMenu.Group, { className: "flex flex-col", children: [
|
|
1233
|
+
return /* @__PURE__ */ jsxs11(BaseMenu.Group, { className: "flex flex-col gap-0.5", children: [
|
|
1228
1234
|
/* @__PURE__ */ jsx18(BaseMenu.GroupLabel, { render: heading }),
|
|
1229
1235
|
children
|
|
1230
1236
|
] });
|
|
@@ -1597,7 +1603,7 @@ function ChipInput({
|
|
|
1597
1603
|
sideOffset: GAP3,
|
|
1598
1604
|
collisionPadding: VIEWPORT_PAD3,
|
|
1599
1605
|
className: "z-50 data-[anchor-hidden]:hidden",
|
|
1600
|
-
children: /* @__PURE__ */ jsx22(Combobox.Popup, { className: "w-[var(--anchor-width)] p-0", render: /* @__PURE__ */ jsx22(MenuPanel, {}), children: /* @__PURE__ */ jsx22(ScrollArea, { viewportClassName: "max-h-[240px]", contentClassName: "flex flex-col p-2", children: /* @__PURE__ */ jsx22(Combobox.List, { children: (option) => /* @__PURE__ */ jsx22(
|
|
1606
|
+
children: /* @__PURE__ */ jsx22(Combobox.Popup, { className: "w-[var(--anchor-width)] p-0", render: /* @__PURE__ */ jsx22(MenuPanel, {}), children: /* @__PURE__ */ jsx22(ScrollArea, { viewportClassName: "max-h-[240px]", contentClassName: "flex flex-col p-2", children: /* @__PURE__ */ jsx22(Combobox.List, { className: "flex flex-col gap-0.5", children: (option) => /* @__PURE__ */ jsx22(
|
|
1601
1607
|
Combobox.Item,
|
|
1602
1608
|
{
|
|
1603
1609
|
value: option,
|
|
@@ -1953,7 +1959,7 @@ function CommandPaletteSearch({ query, onQueryChange, placeholder, groups, chip,
|
|
|
1953
1959
|
] }),
|
|
1954
1960
|
/* @__PURE__ */ jsxs19(ScrollArea, { viewportClassName: "max-h-[420px]", contentClassName: "flex flex-col p-2", children: [
|
|
1955
1961
|
children != null && /* @__PURE__ */ jsx27("div", { className: "flex flex-col gap-2 px-3 pb-2 pt-3", children }),
|
|
1956
|
-
/* @__PURE__ */ jsx27(Autocomplete.List, { className: "flex flex-col", children: (group) => /* @__PURE__ */ jsxs19(Autocomplete.Group, { items: group.items, className: "flex flex-col", children: [
|
|
1962
|
+
/* @__PURE__ */ jsx27(Autocomplete.List, { className: "flex flex-col", children: (group) => /* @__PURE__ */ jsxs19(Autocomplete.Group, { items: group.items, className: "flex flex-col gap-0.5", children: [
|
|
1957
1963
|
/* @__PURE__ */ jsx27(Autocomplete.GroupLabel, { className: "flex h-7 shrink-0 items-center px-3", children: /* @__PURE__ */ jsx27(SectionLabel, { tone: "secondary", children: group.value }) }),
|
|
1958
1964
|
/* @__PURE__ */ jsx27(Autocomplete.Collection, { children: (row) => (
|
|
1959
1965
|
/* The menu row, as the list's own option — the way Select
|
|
@@ -2304,13 +2310,13 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
|
|
|
2304
2310
|
"aria-haspopup": "menu",
|
|
2305
2311
|
"aria-expanded": open,
|
|
2306
2312
|
className: cn(
|
|
2307
|
-
"flex h-8 min-h-8
|
|
2313
|
+
"flex h-8 min-h-8 items-center gap-1.5 pl-1.5 pr-1.5 text-body-2 text-text-primary",
|
|
2314
|
+
MENU_TRIGGER_CLASSES,
|
|
2308
2315
|
open && "bg-bg-hover",
|
|
2309
2316
|
// Under a `Menu` the open state is Base UI's, not a prop: the trigger
|
|
2310
|
-
// carries `data-popup-open` while its menu is up
|
|
2311
|
-
//
|
|
2312
|
-
//
|
|
2313
|
-
"data-[popup-open]:bg-bg-hover",
|
|
2317
|
+
// carries `data-popup-open` while its menu is up (MENU_TRIGGER_CLASSES
|
|
2318
|
+
// holds the fill for it), and sets its own `aria-expanded`; `open`
|
|
2319
|
+
// holds it for a caller that drives it.
|
|
2314
2320
|
className
|
|
2315
2321
|
),
|
|
2316
2322
|
...props,
|
|
@@ -2520,7 +2526,7 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
|
|
|
2520
2526
|
{
|
|
2521
2527
|
render: /* @__PURE__ */ jsx34(MenuPanel, {}),
|
|
2522
2528
|
className: "min-w-[var(--anchor-width)] p-0",
|
|
2523
|
-
children: /* @__PURE__ */ jsx34(ScrollArea, { viewportClassName: "max-h-[min(288px,var(--available-height))]", contentClassName: "flex flex-col p-2", children: options.map((option) => /* @__PURE__ */ jsxs25(
|
|
2529
|
+
children: /* @__PURE__ */ jsx34(ScrollArea, { viewportClassName: "max-h-[min(288px,var(--available-height))]", contentClassName: "flex flex-col gap-0.5 p-2", children: options.map((option) => /* @__PURE__ */ jsxs25(
|
|
2524
2530
|
BaseSelect.Item,
|
|
2525
2531
|
{
|
|
2526
2532
|
value: option.value,
|
|
@@ -2615,16 +2621,30 @@ import { IconX as IconX5 } from "@tabler/icons-react";
|
|
|
2615
2621
|
|
|
2616
2622
|
// src/ContainerHeader.tsx
|
|
2617
2623
|
import { IconChevronDown as IconChevronDown3 } from "@tabler/icons-react";
|
|
2624
|
+
import { Button as BaseButton6 } from "@base-ui/react/button";
|
|
2618
2625
|
import { jsx as jsx37, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2619
|
-
function ContainerHeader({ title, chevron = false, actions, className }) {
|
|
2626
|
+
function ContainerHeader({ title, chevron = false, titleMenu, actions, className }) {
|
|
2620
2627
|
const text = typeof title === "string";
|
|
2621
|
-
|
|
2622
|
-
/* @__PURE__ */ jsx37("
|
|
2623
|
-
|
|
2624
|
-
chevron && /* @__PURE__ */ jsx37(IconChevronDown3, { size: 12, stroke: 1.5, className: "shrink-0 text-text-secondary" })
|
|
2625
|
-
] }) }),
|
|
2626
|
-
actions && /* @__PURE__ */ jsx37("div", { className: "flex shrink-0 items-center justify-end gap-3", children: /* @__PURE__ */ jsx37("div", { className: "flex shrink-0 items-center gap-1", children: actions }) })
|
|
2628
|
+
const name = /* @__PURE__ */ jsxs26("div", { className: cn("flex items-center gap-1", text ? "shrink-0" : "min-w-0 flex-1"), children: [
|
|
2629
|
+
text ? /* @__PURE__ */ jsx37("span", { className: "whitespace-nowrap text-body-2-strong text-text-primary", children: title }) : /* @__PURE__ */ jsx37("div", { className: "min-w-0 flex-1", children: title }),
|
|
2630
|
+
(chevron || titleMenu != null) && /* @__PURE__ */ jsx37(IconChevronDown3, { size: 12, stroke: 1.5, className: "shrink-0 text-text-secondary" })
|
|
2627
2631
|
] });
|
|
2632
|
+
return (
|
|
2633
|
+
// With a title menu the bar gives the button 6px of its left padding
|
|
2634
|
+
// (pl-3.5 + the button's px-1.5 = pl-5): the words stay where a title's
|
|
2635
|
+
// are, and the fill is inside the bar — a negative margin had it clipped
|
|
2636
|
+
// (Katerina, 5 October).
|
|
2637
|
+
/* @__PURE__ */ jsxs26("div", { className: cn("flex h-12 shrink-0 items-center justify-between overflow-hidden border-b border-border-subtle py-2 pr-4", titleMenu != null ? "pl-3.5" : "pl-5", className), children: [
|
|
2638
|
+
/* @__PURE__ */ jsx37("div", { className: cn("flex items-center gap-2 overflow-hidden", text ? "shrink-0" : "min-w-0 flex-1"), children: titleMenu != null ? /* @__PURE__ */ jsx37(
|
|
2639
|
+
Menu,
|
|
2640
|
+
{
|
|
2641
|
+
trigger: /* @__PURE__ */ jsx37(BaseButton6, { type: "button", className: cn("flex min-w-0 items-center px-1.5 py-1", MENU_TRIGGER_CLASSES), children: name }),
|
|
2642
|
+
children: titleMenu
|
|
2643
|
+
}
|
|
2644
|
+
) : name }),
|
|
2645
|
+
actions && /* @__PURE__ */ jsx37("div", { className: "flex shrink-0 items-center justify-end gap-3", children: /* @__PURE__ */ jsx37("div", { className: "flex shrink-0 items-center gap-1", children: actions }) })
|
|
2646
|
+
] })
|
|
2647
|
+
);
|
|
2628
2648
|
}
|
|
2629
2649
|
|
|
2630
2650
|
// src/DialogShell.tsx
|
|
@@ -2979,7 +2999,7 @@ function NavItem({
|
|
|
2979
2999
|
};
|
|
2980
3000
|
const besides = menu != null || hasRows;
|
|
2981
3001
|
const mark = unread ? urgent ? /* @__PURE__ */ jsx45(UrgentMark, {}) : /* @__PURE__ */ jsx45(UnreadDot, {}) : null;
|
|
2982
|
-
const stepsAside = menu != null && "transition-opacity group-hover/nav:opacity-0 group-focus-
|
|
3002
|
+
const stepsAside = menu != null && "transition-opacity group-hover/nav:opacity-0 group-has-[:focus-visible]/nav:opacity-0 group-has-[[data-nav-menu]_[aria-expanded=true]]/nav:opacity-0 [@media(hover:none)]:opacity-0";
|
|
2983
3003
|
const row = /* @__PURE__ */ jsxs30(
|
|
2984
3004
|
"a",
|
|
2985
3005
|
{
|
|
@@ -3007,14 +3027,14 @@ function NavItem({
|
|
|
3007
3027
|
className: cn(
|
|
3008
3028
|
// With rows under it the slot is kept even with no icon: the arrow lands in it.
|
|
3009
3029
|
"flex min-w-4 shrink-0 items-center",
|
|
3010
|
-
hasRows && "transition-opacity group-hover/nav:opacity-0 group-focus-
|
|
3030
|
+
hasRows && "transition-opacity group-hover/nav:opacity-0 group-has-[:focus-visible]/nav:opacity-0"
|
|
3011
3031
|
),
|
|
3012
3032
|
children: icon
|
|
3013
3033
|
}
|
|
3014
3034
|
),
|
|
3015
3035
|
/* @__PURE__ */ jsx45("span", { className: cn("flex-1 truncate", unread && "font-medium"), children: label }),
|
|
3016
3036
|
hint != null && // Takes room only while it shows, so the label gives way to it.
|
|
3017
|
-
/* @__PURE__ */ jsx45("span", { className: "hidden shrink-0 items-center group-hover/nav:flex group-focus-
|
|
3037
|
+
/* @__PURE__ */ jsx45("span", { className: "hidden shrink-0 items-center group-hover/nav:flex group-has-[:focus-visible]/nav:flex", children: hint }),
|
|
3018
3038
|
count ? /* @__PURE__ */ jsx45(WithTooltip, { label: countLabel ?? `${count} open`, children: /* @__PURE__ */ jsx45(
|
|
3019
3039
|
"span",
|
|
3020
3040
|
{
|
|
@@ -3026,11 +3046,19 @@ function NavItem({
|
|
|
3026
3046
|
children: count
|
|
3027
3047
|
}
|
|
3028
3048
|
) }) : null,
|
|
3029
|
-
mark ?
|
|
3049
|
+
mark ? (
|
|
3050
|
+
// `-mr-1`: the 24px slot's dot then sits on the axis of a count and
|
|
3051
|
+
// of the ⋮ (8px from the edge to the 16px box), one column with a
|
|
3052
|
+
// folder heading's dot and ⋮ (Katerina, 5 October).
|
|
3053
|
+
/* @__PURE__ */ jsx45("span", { className: cn("-mr-1 flex shrink-0", stepsAside), children: mark })
|
|
3054
|
+
) : menu != null && !count ? (
|
|
3030
3055
|
// Nothing at the right, but a menu: no room is kept at rest, so the
|
|
3031
3056
|
// label has the row's whole width; while the ⋮ shows, its 24px slot
|
|
3032
3057
|
// is kept, so a long label ends before it instead of running under it.
|
|
3033
|
-
|
|
3058
|
+
// `-mr-1`: the ⋮ sits at `right-1`, 4px into the row's padding, so the
|
|
3059
|
+
// slot does too — at the padding's edge it kept 4px the ⋮ never
|
|
3060
|
+
// covers, and the label stopped 12px short of the button, not 8.
|
|
3061
|
+
/* @__PURE__ */ jsx45("span", { "aria-hidden": "true", className: "-mr-1 hidden w-6 shrink-0 group-hover/nav:block group-has-[:focus-visible]/nav:block group-has-[[data-nav-menu]_[aria-expanded=true]]/nav:block [@media(hover:none)]:block" })
|
|
3034
3062
|
) : null
|
|
3035
3063
|
]
|
|
3036
3064
|
}
|
|
@@ -3041,7 +3069,7 @@ function NavItem({
|
|
|
3041
3069
|
row,
|
|
3042
3070
|
hasRows && // `left-1`: the 24px button's 16px arrow then sits where the icon
|
|
3043
3071
|
// sits (the row's px-2), so the icon turns into the arrow in place.
|
|
3044
|
-
/* @__PURE__ */ jsx45("div", { className: "absolute inset-y-0 left-1 flex items-center opacity-0 transition-opacity
|
|
3072
|
+
/* @__PURE__ */ jsx45("div", { className: "absolute inset-y-0 left-1 flex items-center opacity-0 transition-opacity group-hover/nav:opacity-100 group-has-[:focus-visible]/nav:opacity-100", children: /* @__PURE__ */ jsx45(
|
|
3045
3073
|
IconButton,
|
|
3046
3074
|
{
|
|
3047
3075
|
tooltip: open ? "Collapse" : "Expand",
|
|
@@ -3052,11 +3080,15 @@ function NavItem({
|
|
|
3052
3080
|
children: /* @__PURE__ */ jsx45(IconChevronRight2, { size: 16, stroke: 1.5, className: cn("transition-transform duration-150", open && "rotate-90") })
|
|
3053
3081
|
}
|
|
3054
3082
|
) }),
|
|
3055
|
-
menu != null && //
|
|
3056
|
-
//
|
|
3057
|
-
//
|
|
3058
|
-
//
|
|
3059
|
-
|
|
3083
|
+
menu != null && // `right-1`: the 24px button's 16px icon ends 8px from the row's
|
|
3084
|
+
// edge, where a count ends (px-2) — and where a SectionHeader's
|
|
3085
|
+
// actions and ⋮ end, so a folder's ⋮ and its rows' ⋮ are one column
|
|
3086
|
+
// (Katerina, 5 October: the rows move to the heading, not the
|
|
3087
|
+
// heading to the rows). Over a dot or the empty slot it sits 4px
|
|
3088
|
+
// right of the slot's centre.
|
|
3089
|
+
// On a screen with no hover it always shows: nothing there can point
|
|
3090
|
+
// at the row, and the menu has no other way in.
|
|
3091
|
+
/* @__PURE__ */ jsx45("div", { "data-nav-menu": true, className: cn("absolute inset-y-0 right-1 flex items-center", "opacity-0 transition-opacity group-hover/nav:opacity-100 group-has-[:focus-visible]/nav:opacity-100 has-[[aria-expanded=true]]:opacity-100 [@media(hover:none)]:opacity-100"), children: /* @__PURE__ */ jsx45(
|
|
3060
3092
|
Menu,
|
|
3061
3093
|
{
|
|
3062
3094
|
align: "right",
|
|
@@ -3074,7 +3106,7 @@ function NavItem({
|
|
|
3074
3106
|
id: panelId,
|
|
3075
3107
|
hiddenUntilFound: true,
|
|
3076
3108
|
className: COLLAPSIBLE_PANEL_CLASSES,
|
|
3077
|
-
children: /* @__PURE__ */ jsx45("div", { className: "flex flex-col gap-
|
|
3109
|
+
children: /* @__PURE__ */ jsx45("div", { className: "flex flex-col gap-0.5 pt-0.5 pl-6", children })
|
|
3078
3110
|
}
|
|
3079
3111
|
)
|
|
3080
3112
|
] });
|
|
@@ -3091,23 +3123,37 @@ import { useState as useState9 } from "react";
|
|
|
3091
3123
|
import { Collapsible as Collapsible2 } from "@base-ui/react/collapsible";
|
|
3092
3124
|
|
|
3093
3125
|
// src/SectionHeader.tsx
|
|
3094
|
-
import { IconChevronRight as IconChevronRight3 } from "@tabler/icons-react";
|
|
3126
|
+
import { IconChevronRight as IconChevronRight3, IconDotsVertical as IconDotsVertical2 } from "@tabler/icons-react";
|
|
3095
3127
|
import { useRender } from "@base-ui/react/use-render";
|
|
3096
3128
|
import { Fragment as Fragment11, jsx as jsx46, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
3097
|
-
function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, trailing, actions, showActions = "hover", hover = "fill", look = "heading", render, className }) {
|
|
3129
|
+
function SectionHeader({ title, chevron = false, icon, isExpanded = true, onToggle, trailing, actions, menu, menuLabel, showActions = "hover", hover = "fill", look = "heading", render, className }) {
|
|
3098
3130
|
const row = look === "row";
|
|
3131
|
+
const acts = actions && actions.length > 0 || menu != null;
|
|
3132
|
+
const trailingClasses = !acts ? "has-[[data-unread]]:-mr-1" : showActions === "hover" && "has-[[data-unread]]:pointer-events-none has-[[data-unread]]:absolute has-[[data-unread]]:inset-y-0 has-[[data-unread]]:right-1 has-[[data-unread]]:w-6 has-[[data-unread]]:justify-center has-[[data-unread]]:transition-opacity group-hover:has-[[data-unread]]:opacity-0 group-has-[:focus-visible]:has-[[data-unread]]:opacity-0 group-has-[[data-section-menu]_[aria-expanded=true]]:has-[[data-unread]]:opacity-0 [@media(hover:none)]:has-[[data-unread]]:opacity-0";
|
|
3133
|
+
const reveal = showActions === "hover" && "opacity-0 transition-opacity group-hover:opacity-100 group-has-[:focus-visible]:opacity-100 has-[[aria-expanded=true]]:opacity-100 [@media(hover:none)]:opacity-100";
|
|
3099
3134
|
const titleElement = useRender({
|
|
3100
3135
|
render: render ?? (chevron ? /* @__PURE__ */ jsx46("button", { type: "button", onClick: onToggle, "aria-expanded": isExpanded }) : /* @__PURE__ */ jsx46("span", {})),
|
|
3101
3136
|
props: {
|
|
3102
3137
|
// `text-left`: a button centres its text. `h-full` and `flex-1`: the
|
|
3103
3138
|
// whole row up to the actions is the hit target, as it was when the
|
|
3104
3139
|
// row itself carried the click.
|
|
3105
|
-
|
|
3140
|
+
// An icon is a row's: NavItem's 8px after it.
|
|
3141
|
+
className: cn("flex h-full min-w-0 flex-1 items-center text-left", row || chevron || icon != null ? "gap-2" : "gap-1"),
|
|
3106
3142
|
children: /* @__PURE__ */ jsxs31(Fragment11, { children: [
|
|
3107
|
-
|
|
3143
|
+
icon != null ? /* @__PURE__ */ jsxs31("span", { className: "relative flex size-4 shrink-0 items-center justify-center text-text-secondary", children: [
|
|
3144
|
+
/* @__PURE__ */ jsx46("span", { className: cn("flex", chevron && "transition-opacity group-hover:opacity-0 group-has-[:focus-visible]:opacity-0"), children: icon }),
|
|
3145
|
+
chevron && /* @__PURE__ */ jsx46(
|
|
3146
|
+
IconChevronRight3,
|
|
3147
|
+
{
|
|
3148
|
+
size: 16,
|
|
3149
|
+
stroke: 1.5,
|
|
3150
|
+
className: cn("absolute opacity-0 transition-[opacity,transform] duration-150 group-hover:opacity-100 group-has-[:focus-visible]:opacity-100", isExpanded && "rotate-90")
|
|
3151
|
+
}
|
|
3152
|
+
)
|
|
3153
|
+
] }) : chevron && /* @__PURE__ */ jsx46(
|
|
3108
3154
|
IconChevronRight3,
|
|
3109
3155
|
{
|
|
3110
|
-
size:
|
|
3156
|
+
size: 16,
|
|
3111
3157
|
stroke: 1.5,
|
|
3112
3158
|
className: cn("shrink-0 transition-transform duration-150", !row && "text-text-secondary", isExpanded && "rotate-90")
|
|
3113
3159
|
}
|
|
@@ -3123,7 +3169,8 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, tr
|
|
|
3123
3169
|
"div",
|
|
3124
3170
|
{
|
|
3125
3171
|
className: cn(
|
|
3126
|
-
|
|
3172
|
+
// `relative`: a dot in the last button's place is placed against the row.
|
|
3173
|
+
"group relative flex h-[32px] shrink-0 items-center gap-1 px-2",
|
|
3127
3174
|
!row && "transition-colors",
|
|
3128
3175
|
// A row is NavItem's shape and colour: 6px corners, secondary words that brighten on hover.
|
|
3129
3176
|
row ? cn(SIDEBAR_ROW_CLASSES, SIDEBAR_ROW_TEXT_CLASSES) : "rounded-lg",
|
|
@@ -3131,20 +3178,30 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, tr
|
|
|
3131
3178
|
// lights up, a fixed heading over rows does not (2026-09-09, the
|
|
3132
3179
|
// Sidebar's fixed group).
|
|
3133
3180
|
// A quiet label never lights up: it is a label; its button does.
|
|
3134
|
-
hover === "fill" && look !== "quiet" && (chevron ||
|
|
3181
|
+
hover === "fill" && look !== "quiet" && (chevron || acts) && "hover:bg-bg-hover",
|
|
3182
|
+
// While its menu is open the pointer is on the menu, not the row: the
|
|
3183
|
+
// row keeps its hover look, as a NavItem's does.
|
|
3184
|
+
hover === "fill" && look !== "quiet" && menu != null && "has-[[data-section-menu]_[aria-expanded=true]]:bg-bg-hover",
|
|
3135
3185
|
className
|
|
3136
3186
|
),
|
|
3137
3187
|
children: [
|
|
3138
3188
|
titleElement,
|
|
3139
|
-
trailing != null && /* @__PURE__ */ jsx46("div", { className: "flex shrink-0 items-center", children: trailing }),
|
|
3140
|
-
|
|
3189
|
+
trailing != null && /* @__PURE__ */ jsx46("div", { className: cn("flex shrink-0 items-center", trailingClasses), children: trailing }),
|
|
3190
|
+
acts && /* @__PURE__ */ jsx46(
|
|
3141
3191
|
"div",
|
|
3142
3192
|
{
|
|
3143
|
-
className:
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3193
|
+
className: "-mr-1 flex shrink-0 items-center",
|
|
3194
|
+
children: /* @__PURE__ */ jsxs31("div", { className: cn("flex items-center gap-1", reveal), children: [
|
|
3195
|
+
actions?.map((action) => /* @__PURE__ */ jsx46(IconButton, { tooltip: action.tooltip, "aria-label": action.tooltip, onClick: action.onClick, children: action.icon }, action.tooltip)),
|
|
3196
|
+
menu != null && /* @__PURE__ */ jsx46("div", { "data-section-menu": true, className: "flex items-center", children: /* @__PURE__ */ jsx46(
|
|
3197
|
+
Menu,
|
|
3198
|
+
{
|
|
3199
|
+
align: "right",
|
|
3200
|
+
trigger: /* @__PURE__ */ jsx46(IconButton, { tooltip: "More options", "aria-label": menuLabel ?? `More options for ${title}`, children: /* @__PURE__ */ jsx46(IconDotsVertical2, { size: 16, stroke: 1.5 }) }),
|
|
3201
|
+
children: menu
|
|
3202
|
+
}
|
|
3203
|
+
) })
|
|
3204
|
+
] })
|
|
3148
3205
|
}
|
|
3149
3206
|
)
|
|
3150
3207
|
]
|
|
@@ -3172,7 +3229,7 @@ function writeStored(key, open) {
|
|
|
3172
3229
|
} catch {
|
|
3173
3230
|
}
|
|
3174
3231
|
}
|
|
3175
|
-
function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenChange, storageKey, trailing, actions, showActions, look = "heading", indent = false, children, className, contentClassName }) {
|
|
3232
|
+
function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenChange, storageKey, icon, trailing, actions, menu, menuLabel, showActions, look = "heading", indent = false, children, className, contentClassName }) {
|
|
3176
3233
|
const [openState, setOpenState] = useState9(() => readStored(storageKey) ?? defaultOpen);
|
|
3177
3234
|
const open = openProp ?? openState;
|
|
3178
3235
|
const setOpen = (next) => {
|
|
@@ -3181,13 +3238,13 @@ function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenC
|
|
|
3181
3238
|
onOpenChange?.(next);
|
|
3182
3239
|
};
|
|
3183
3240
|
return /* @__PURE__ */ jsxs32(Collapsible2.Root, { open, onOpenChange: setOpen, className: cn("flex flex-col", className), children: [
|
|
3184
|
-
/* @__PURE__ */ jsx47(SectionHeader, { title, chevron: true, isExpanded: open, trailing, actions, showActions, look, className: "shrink-0", render: /* @__PURE__ */ jsx47(Collapsible2.Trigger, {}) }),
|
|
3241
|
+
/* @__PURE__ */ jsx47(SectionHeader, { title, chevron: true, icon, isExpanded: open, trailing, actions, menu, menuLabel, showActions, look, className: "shrink-0", render: /* @__PURE__ */ jsx47(Collapsible2.Trigger, {}) }),
|
|
3185
3242
|
/* @__PURE__ */ jsx47(
|
|
3186
3243
|
Collapsible2.Panel,
|
|
3187
3244
|
{
|
|
3188
3245
|
hiddenUntilFound: true,
|
|
3189
3246
|
className: COLLAPSIBLE_PANEL_CLASSES,
|
|
3190
|
-
children: /* @__PURE__ */ jsx47("div", { className: cn("flex flex-col", look === "row"
|
|
3247
|
+
children: /* @__PURE__ */ jsx47("div", { className: cn("flex flex-col pt-0.5", (look === "row" || indent) && "pl-6", contentClassName), children })
|
|
3191
3248
|
}
|
|
3192
3249
|
)
|
|
3193
3250
|
] });
|
|
@@ -3235,6 +3292,8 @@ function Group({ group, selected, onSelect, storageKey }) {
|
|
|
3235
3292
|
loading: group.nodes === null,
|
|
3236
3293
|
trailing: group.trailing,
|
|
3237
3294
|
actions: group.actions,
|
|
3295
|
+
menu: group.menu,
|
|
3296
|
+
icon: group.icon,
|
|
3238
3297
|
defaultOpen: group.defaultOpen ?? holds(group.nodes, selected),
|
|
3239
3298
|
open: group.open,
|
|
3240
3299
|
onOpenChange: group.onOpenChange,
|
|
@@ -3244,14 +3303,14 @@ function Group({ group, selected, onSelect, storageKey }) {
|
|
|
3244
3303
|
}
|
|
3245
3304
|
);
|
|
3246
3305
|
}
|
|
3247
|
-
function NavTreeSection({ id, title, message, loading = false, trailing, actions, defaultOpen = true, open, onOpenChange, storageKey, onIntent, children }) {
|
|
3306
|
+
function NavTreeSection({ id, title, message, loading = false, trailing, actions, menu, icon, defaultOpen = true, open, onOpenChange, storageKey, onIntent, children }) {
|
|
3248
3307
|
const timer = useRef10(void 0);
|
|
3249
3308
|
useEffect5(() => () => clearTimeout(timer.current), []);
|
|
3250
3309
|
const intent = onIntent;
|
|
3251
3310
|
return /* @__PURE__ */ jsx48(
|
|
3252
3311
|
"div",
|
|
3253
3312
|
{
|
|
3254
|
-
className: "shrink-0",
|
|
3313
|
+
className: "shrink-0 [&+&]:mt-1",
|
|
3255
3314
|
"data-nav-group": id,
|
|
3256
3315
|
onPointerEnter: intent ? () => {
|
|
3257
3316
|
clearTimeout(timer.current);
|
|
@@ -3270,8 +3329,10 @@ function NavTreeSection({ id, title, message, loading = false, trailing, actions
|
|
|
3270
3329
|
storageKey,
|
|
3271
3330
|
trailing,
|
|
3272
3331
|
actions,
|
|
3273
|
-
|
|
3274
|
-
|
|
3332
|
+
menu,
|
|
3333
|
+
icon,
|
|
3334
|
+
className: "shrink-0",
|
|
3335
|
+
contentClassName: "gap-0.5",
|
|
3275
3336
|
children: message !== void 0 ? /* @__PURE__ */ jsx48(Line, { message }) : loading ? /* @__PURE__ */ jsx48(SkeletonList, { rows: 3 }) : children
|
|
3276
3337
|
}
|
|
3277
3338
|
)
|
|
@@ -3428,7 +3489,7 @@ function SuggestionMenu({ rect, editorElement, ariaLabel, width, maxHeight, sect
|
|
|
3428
3489
|
)
|
|
3429
3490
|
);
|
|
3430
3491
|
};
|
|
3431
|
-
return /* @__PURE__ */ jsx50(Popover, { anchor: rect, open: rect !== null, side: "top", align: "left", className: width, maxHeight, ariaLabel, children: /* @__PURE__ */ jsx50("div", { id: listboxId, role: "listbox", "aria-label": ariaLabel, className: "flex flex-col", children: shown.map((section, i) => /* @__PURE__ */ jsxs35("div", { className: "contents", children: [
|
|
3492
|
+
return /* @__PURE__ */ jsx50(Popover, { anchor: rect, open: rect !== null, side: "top", align: "left", className: width, maxHeight, ariaLabel, children: /* @__PURE__ */ jsx50("div", { id: listboxId, role: "listbox", "aria-label": ariaLabel, className: "flex flex-col gap-0.5", children: shown.map((section, i) => /* @__PURE__ */ jsxs35("div", { className: "contents", children: [
|
|
3432
3493
|
i > 0 && /* @__PURE__ */ jsx50(MenuSeparator, {}),
|
|
3433
3494
|
section.label ? /* @__PURE__ */ jsx50("div", { role: "group", "aria-label": section.label, children: /* @__PURE__ */ jsx50(MenuSection, { label: section.label, className: section.className, children: section.items.map(drawRow) }) }) : section.items.map(drawRow)
|
|
3434
3495
|
] }, section.label ?? i)) }) });
|
|
@@ -3702,7 +3763,7 @@ function Panel(props) {
|
|
|
3702
3763
|
|
|
3703
3764
|
// src/ListColumn.tsx
|
|
3704
3765
|
import { jsx as jsx60, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
3705
|
-
function Column({ title, chevron = false, actions, above, collapsed = false, children, className, broken = false }) {
|
|
3766
|
+
function Column({ title, chevron = false, titleMenu, actions, above, collapsed = false, children, className, broken = false }) {
|
|
3706
3767
|
return /* @__PURE__ */ jsxs42(
|
|
3707
3768
|
"div",
|
|
3708
3769
|
{
|
|
@@ -3714,7 +3775,7 @@ function Column({ title, chevron = false, actions, above, collapsed = false, chi
|
|
|
3714
3775
|
),
|
|
3715
3776
|
"data-collapsed": collapsed || void 0,
|
|
3716
3777
|
children: [
|
|
3717
|
-
/* @__PURE__ */ jsx60(ContainerHeader, { title, chevron, actions }),
|
|
3778
|
+
/* @__PURE__ */ jsx60(ContainerHeader, { title, chevron, titleMenu, actions }),
|
|
3718
3779
|
above,
|
|
3719
3780
|
broken ? (
|
|
3720
3781
|
// The message takes the room the list had and centres itself in it (Katerina, 19 September).
|