@assure-one/design-system 1.25.0 → 1.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +8 -0
- package/dist/index.js +125 -101
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -5662,6 +5662,14 @@ interface DisplayMenuSection {
|
|
|
5662
5662
|
trailing?: ReactNode;
|
|
5663
5663
|
/** Force the flyout search box. Auto-enabled past SEARCH_THRESHOLD options. */
|
|
5664
5664
|
searchable?: boolean;
|
|
5665
|
+
/** Escape hatch: renders in the flyout *instead of* the option list, for
|
|
5666
|
+
* dimensions a list can't express (a date range, a numeric bound). Sections
|
|
5667
|
+
* using this supply their own controls and are skipped by root search —
|
|
5668
|
+
* there are no option labels to match. Prefer `options` where it fits. */
|
|
5669
|
+
content?: ReactNode;
|
|
5670
|
+
/** Value summary on the root row. `single` sections derive this from the
|
|
5671
|
+
* selected option; `content` sections have no options, so pass it here. */
|
|
5672
|
+
summary?: string;
|
|
5665
5673
|
}
|
|
5666
5674
|
interface DisplayMenuProps {
|
|
5667
5675
|
sections: DisplayMenuSection[];
|
package/dist/index.js
CHANGED
|
@@ -18249,6 +18249,8 @@ var SEARCH_THRESHOLD = 8;
|
|
|
18249
18249
|
var HOVER_OPEN_MS = 90;
|
|
18250
18250
|
var HOVER_CLOSE_MS = 220;
|
|
18251
18251
|
var FLYOUT_WIDTH = 224;
|
|
18252
|
+
var MIN_FLYOUT_HEIGHT = 120;
|
|
18253
|
+
var VIEWPORT_MARGIN = 24;
|
|
18252
18254
|
var DEFAULT_GUTTER_PX = 24;
|
|
18253
18255
|
function DisplayMenu({
|
|
18254
18256
|
sections,
|
|
@@ -18265,7 +18267,8 @@ function DisplayMenu({
|
|
|
18265
18267
|
const [openKey, setOpenKey] = useState(null);
|
|
18266
18268
|
const [flyout, setFlyout] = useState({
|
|
18267
18269
|
top: 0,
|
|
18268
|
-
side: "left"
|
|
18270
|
+
side: "left",
|
|
18271
|
+
maxHeight: 0
|
|
18269
18272
|
});
|
|
18270
18273
|
const [panelShift, setPanelShift] = useState(0);
|
|
18271
18274
|
const [rootQuery, setRootQuery] = useState("");
|
|
@@ -18289,10 +18292,14 @@ function DisplayMenu({
|
|
|
18289
18292
|
if (!panel || !row) return;
|
|
18290
18293
|
const panelRect = panel.getBoundingClientRect();
|
|
18291
18294
|
const rowRect = row.getBoundingClientRect();
|
|
18292
|
-
const maxTop = Math.max(0, window.innerHeight - panelRect.top - 24 -
|
|
18295
|
+
const maxTop = Math.max(0, window.innerHeight - panelRect.top - 24 - MIN_FLYOUT_HEIGHT);
|
|
18293
18296
|
const top = Math.min(Math.max(rowRect.top - panelRect.top - 4, 0), maxTop);
|
|
18297
|
+
const maxHeight = Math.max(
|
|
18298
|
+
MIN_FLYOUT_HEIGHT,
|
|
18299
|
+
window.innerHeight - (panelRect.top + top) - VIEWPORT_MARGIN
|
|
18300
|
+
);
|
|
18294
18301
|
const side = panelRect.left >= FLYOUT_WIDTH + 16 ? "left" : "right";
|
|
18295
|
-
setFlyout({ top, side });
|
|
18302
|
+
setFlyout({ top, side, maxHeight });
|
|
18296
18303
|
}, []);
|
|
18297
18304
|
const openSection = useCallback(
|
|
18298
18305
|
(key) => {
|
|
@@ -18353,6 +18360,7 @@ function DisplayMenu({
|
|
|
18353
18360
|
if (section.label.toLowerCase().includes(q)) rows.push({ kind: "section", section });
|
|
18354
18361
|
}
|
|
18355
18362
|
for (const section of sections) {
|
|
18363
|
+
if (section.content) continue;
|
|
18356
18364
|
for (const option of section.options) {
|
|
18357
18365
|
if (option.label.toLowerCase().includes(q)) rows.push({ kind: "option", section, option });
|
|
18358
18366
|
}
|
|
@@ -18364,7 +18372,7 @@ function DisplayMenu({
|
|
|
18364
18372
|
const q = sectionQuery.trim().toLowerCase();
|
|
18365
18373
|
return q ? activeSection.options.filter((o) => o.label.toLowerCase().includes(q)) : activeSection.options;
|
|
18366
18374
|
}, [activeSection, sectionQuery]);
|
|
18367
|
-
const showFlyoutSearch = !!activeSection && (activeSection.searchable ?? activeSection.options.length > SEARCH_THRESHOLD);
|
|
18375
|
+
const showFlyoutSearch = !!activeSection && !activeSection.content && (activeSection.searchable ?? activeSection.options.length > SEARCH_THRESHOLD);
|
|
18368
18376
|
const anySelected = sections.some((s) => s.mode === "multi" && s.selected.size > 0);
|
|
18369
18377
|
const showDot = !isAtDefault || anySelected;
|
|
18370
18378
|
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
|
|
@@ -18422,100 +18430,108 @@ function DisplayMenu({
|
|
|
18422
18430
|
}
|
|
18423
18431
|
)
|
|
18424
18432
|
] }),
|
|
18425
|
-
/* @__PURE__ */ jsx(
|
|
18426
|
-
|
|
18427
|
-
|
|
18428
|
-
|
|
18429
|
-
|
|
18430
|
-
|
|
18431
|
-
|
|
18432
|
-
{
|
|
18433
|
-
|
|
18434
|
-
|
|
18435
|
-
"
|
|
18436
|
-
|
|
18437
|
-
|
|
18438
|
-
|
|
18439
|
-
|
|
18440
|
-
|
|
18441
|
-
|
|
18442
|
-
|
|
18443
|
-
|
|
18444
|
-
|
|
18445
|
-
|
|
18446
|
-
|
|
18447
|
-
|
|
18448
|
-
|
|
18449
|
-
className: "shrink-0 text-muted-foreground",
|
|
18450
|
-
"
|
|
18451
|
-
|
|
18452
|
-
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
`${section2.key}:${option.key}`
|
|
18458
|
-
);
|
|
18459
|
-
}
|
|
18460
|
-
const { section } = row;
|
|
18461
|
-
const count = section.selected.size;
|
|
18462
|
-
const valueLabel = section.mode === "single" ? section.options.find((o) => section.selected.has(o.key))?.label ?? "" : "";
|
|
18463
|
-
const isOpen = section.key === openKey;
|
|
18464
|
-
return (
|
|
18465
|
-
/* Hover handlers sit on the wrapper so the cursor can cross
|
|
18466
|
-
* the gap to `section.trailing` without closing the flyout.
|
|
18467
|
-
* The wrapper is not itself a control — the button inside is,
|
|
18468
|
-
* and it carries the keyboard equivalent (ArrowRight / Enter /
|
|
18469
|
-
* Space open the same flyout). Nothing here is mouse-only. */
|
|
18470
|
-
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
18471
|
-
/* @__PURE__ */ jsxs(
|
|
18472
|
-
"div",
|
|
18473
|
-
{
|
|
18474
|
-
ref: (el) => {
|
|
18475
|
-
if (el) rowRefs.current.set(section.key, el);
|
|
18476
|
-
else rowRefs.current.delete(section.key);
|
|
18477
|
-
},
|
|
18478
|
-
className: "flex items-center gap-0.5",
|
|
18479
|
-
onMouseEnter: () => scheduleOpen(section.key),
|
|
18480
|
-
onMouseLeave: scheduleClose,
|
|
18481
|
-
children: [
|
|
18482
|
-
/* @__PURE__ */ jsxs(
|
|
18483
|
-
"button",
|
|
18484
|
-
{
|
|
18485
|
-
type: "button",
|
|
18486
|
-
role: "menuitem",
|
|
18487
|
-
"aria-haspopup": "menu",
|
|
18488
|
-
"aria-expanded": isOpen,
|
|
18489
|
-
onClick: () => openSection(section.key),
|
|
18490
|
-
onKeyDown: (e) => {
|
|
18491
|
-
if (e.key === "ArrowRight" || e.key === "Enter" || e.key === " ") {
|
|
18492
|
-
e.preventDefault();
|
|
18493
|
-
openSection(section.key);
|
|
18494
|
-
requestAnimationFrame(
|
|
18495
|
-
() => flyoutRef.current?.querySelector("input, [role='menuitemcheckbox'], [role='menuitemradio']")?.focus()
|
|
18496
|
-
);
|
|
18433
|
+
/* @__PURE__ */ jsx(
|
|
18434
|
+
"div",
|
|
18435
|
+
{
|
|
18436
|
+
role: "menu",
|
|
18437
|
+
"aria-label": triggerLabel,
|
|
18438
|
+
className: "scrollbar-thin max-h-[min(32rem,70vh)] overflow-y-auto p-1",
|
|
18439
|
+
children: rootRows.length === 0 ? /* @__PURE__ */ jsx(EmptyRow, {}) : rootRows.map((row) => {
|
|
18440
|
+
if (row.kind === "option") {
|
|
18441
|
+
const { section: section2, option } = row;
|
|
18442
|
+
const isActive = section2.selected.has(option.key);
|
|
18443
|
+
const isMulti = section2.mode === "multi";
|
|
18444
|
+
return /* @__PURE__ */ jsxs(
|
|
18445
|
+
"button",
|
|
18446
|
+
{
|
|
18447
|
+
type: "button",
|
|
18448
|
+
role: isMulti ? "menuitemcheckbox" : "menuitemradio",
|
|
18449
|
+
"aria-checked": isActive,
|
|
18450
|
+
onMouseEnter: () => {
|
|
18451
|
+
clearTimers();
|
|
18452
|
+
setOpenKey(null);
|
|
18453
|
+
},
|
|
18454
|
+
onClick: () => section2.onSelect(option.key),
|
|
18455
|
+
className: "flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-left text-[13px] transition-colors hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-none",
|
|
18456
|
+
children: [
|
|
18457
|
+
section2.icon && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", "aria-hidden": "true", children: section2.icon }),
|
|
18458
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 truncate text-muted-foreground", children: section2.label }),
|
|
18459
|
+
/* @__PURE__ */ jsx(
|
|
18460
|
+
ChevronRightIcon,
|
|
18461
|
+
{
|
|
18462
|
+
size: 12,
|
|
18463
|
+
className: "shrink-0 text-muted-foreground",
|
|
18464
|
+
"aria-hidden": "true"
|
|
18497
18465
|
}
|
|
18498
|
-
},
|
|
18499
|
-
className: cn(
|
|
18500
|
-
"flex min-w-0 flex-1 items-center gap-2 rounded-md px-2 py-1.5 text-left text-[13px] text-foreground transition-colors hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-none",
|
|
18501
|
-
isOpen && "bg-muted/60"
|
|
18502
18466
|
),
|
|
18503
|
-
children:
|
|
18504
|
-
|
|
18505
|
-
|
|
18506
|
-
|
|
18507
|
-
|
|
18508
|
-
|
|
18509
|
-
|
|
18510
|
-
|
|
18511
|
-
|
|
18512
|
-
|
|
18513
|
-
|
|
18514
|
-
|
|
18515
|
-
|
|
18516
|
-
|
|
18517
|
-
|
|
18518
|
-
|
|
18467
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-foreground", children: option.label }),
|
|
18468
|
+
isActive && /* @__PURE__ */ jsx(CheckIcon, { size: 14, className: "shrink-0 text-brand" })
|
|
18469
|
+
]
|
|
18470
|
+
},
|
|
18471
|
+
`${section2.key}:${option.key}`
|
|
18472
|
+
);
|
|
18473
|
+
}
|
|
18474
|
+
const { section } = row;
|
|
18475
|
+
const count = section.selected.size;
|
|
18476
|
+
const valueLabel = section.summary ?? (section.mode === "single" ? section.options.find((o) => section.selected.has(o.key))?.label ?? "" : "");
|
|
18477
|
+
const isOpen = section.key === openKey;
|
|
18478
|
+
return (
|
|
18479
|
+
/* Hover handlers sit on the wrapper so the cursor can cross
|
|
18480
|
+
* the gap to `section.trailing` without closing the flyout.
|
|
18481
|
+
* The wrapper is not itself a control — the button inside is,
|
|
18482
|
+
* and it carries the keyboard equivalent (ArrowRight / Enter /
|
|
18483
|
+
* Space open the same flyout). Nothing here is mouse-only. */
|
|
18484
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
18485
|
+
/* @__PURE__ */ jsxs(
|
|
18486
|
+
"div",
|
|
18487
|
+
{
|
|
18488
|
+
ref: (el) => {
|
|
18489
|
+
if (el) rowRefs.current.set(section.key, el);
|
|
18490
|
+
else rowRefs.current.delete(section.key);
|
|
18491
|
+
},
|
|
18492
|
+
className: "flex items-center gap-0.5",
|
|
18493
|
+
onMouseEnter: () => scheduleOpen(section.key),
|
|
18494
|
+
onMouseLeave: scheduleClose,
|
|
18495
|
+
children: [
|
|
18496
|
+
/* @__PURE__ */ jsxs(
|
|
18497
|
+
"button",
|
|
18498
|
+
{
|
|
18499
|
+
type: "button",
|
|
18500
|
+
role: "menuitem",
|
|
18501
|
+
"aria-haspopup": "menu",
|
|
18502
|
+
"aria-expanded": isOpen,
|
|
18503
|
+
onClick: () => openSection(section.key),
|
|
18504
|
+
onKeyDown: (e) => {
|
|
18505
|
+
if (e.key === "ArrowRight" || e.key === "Enter" || e.key === " ") {
|
|
18506
|
+
e.preventDefault();
|
|
18507
|
+
openSection(section.key);
|
|
18508
|
+
requestAnimationFrame(
|
|
18509
|
+
() => flyoutRef.current?.querySelector("input, [role='menuitemcheckbox'], [role='menuitemradio']")?.focus()
|
|
18510
|
+
);
|
|
18511
|
+
}
|
|
18512
|
+
},
|
|
18513
|
+
className: cn(
|
|
18514
|
+
"flex min-w-0 flex-1 items-center gap-2 rounded-md px-2 py-1.5 text-left text-[13px] text-foreground transition-colors hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:outline-none",
|
|
18515
|
+
isOpen && "bg-muted/60"
|
|
18516
|
+
),
|
|
18517
|
+
children: [
|
|
18518
|
+
section.icon && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", "aria-hidden": "true", children: section.icon }),
|
|
18519
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: section.label }),
|
|
18520
|
+
section.mode === "multi" && count > 0 && /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-full bg-brand-bg px-1.5 text-[11px] font-medium text-brand", children: count }),
|
|
18521
|
+
section.mode !== "multi" && valueLabel && /* @__PURE__ */ jsx("span", { className: "max-w-24 shrink-0 truncate text-[13px] text-muted-foreground", children: valueLabel }),
|
|
18522
|
+
/* @__PURE__ */ jsx(ChevronRightIcon, { size: 13, className: "shrink-0 text-muted-foreground" })
|
|
18523
|
+
]
|
|
18524
|
+
}
|
|
18525
|
+
),
|
|
18526
|
+
section.trailing
|
|
18527
|
+
]
|
|
18528
|
+
},
|
|
18529
|
+
section.key
|
|
18530
|
+
)
|
|
18531
|
+
);
|
|
18532
|
+
})
|
|
18533
|
+
}
|
|
18534
|
+
),
|
|
18519
18535
|
/* @__PURE__ */ jsx("div", { className: "border-t border-border p-1", children: /* @__PURE__ */ jsxs(
|
|
18520
18536
|
Button,
|
|
18521
18537
|
{
|
|
@@ -18537,9 +18553,12 @@ function DisplayMenu({
|
|
|
18537
18553
|
role: "menu",
|
|
18538
18554
|
"aria-label": activeSection.label,
|
|
18539
18555
|
tabIndex: -1,
|
|
18540
|
-
style: { top: flyout.top, width: FLYOUT_WIDTH },
|
|
18556
|
+
style: { top: flyout.top, width: FLYOUT_WIDTH, maxHeight: flyout.maxHeight },
|
|
18541
18557
|
className: cn(
|
|
18542
|
-
|
|
18558
|
+
// Column + overflow-hidden so the option list (the only flexible
|
|
18559
|
+
// part) absorbs the height cap, while the search box and Clear
|
|
18560
|
+
// footer stay pinned and fully visible.
|
|
18561
|
+
"absolute z-50 flex flex-col overflow-hidden rounded-lg border border-border bg-popover shadow-md",
|
|
18543
18562
|
// -mx-1 lets the flyout tuck against the panel so there's no
|
|
18544
18563
|
// dead gap for the cursor to fall through on the way over.
|
|
18545
18564
|
flyout.side === "left" ? "right-full -mr-1" : "left-full -ml-1"
|
|
@@ -18547,7 +18566,7 @@ function DisplayMenu({
|
|
|
18547
18566
|
onMouseEnter: clearTimers,
|
|
18548
18567
|
onMouseLeave: scheduleClose,
|
|
18549
18568
|
children: [
|
|
18550
|
-
showFlyoutSearch && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 border-b border-border px-2.5 py-1.5", children: [
|
|
18569
|
+
showFlyoutSearch && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1.5 border-b border-border px-2.5 py-1.5", children: [
|
|
18551
18570
|
/* @__PURE__ */ jsx(SearchIcon, { size: 13, className: "shrink-0 text-muted-foreground", "aria-hidden": "true" }),
|
|
18552
18571
|
/* @__PURE__ */ jsx(
|
|
18553
18572
|
"input",
|
|
@@ -18561,7 +18580,12 @@ function DisplayMenu({
|
|
|
18561
18580
|
}
|
|
18562
18581
|
)
|
|
18563
18582
|
] }),
|
|
18564
|
-
/* @__PURE__ */ jsx("div", { className: "max-h-
|
|
18583
|
+
/* @__PURE__ */ jsx("div", { className: "scrollbar-thin min-h-0 flex-1 max-h-[min(24rem,60vh)] overflow-y-auto p-1", children: activeSection.content ? (
|
|
18584
|
+
/* Consumer-supplied controls (date range, numeric bound).
|
|
18585
|
+
* Rendered raw — the section owns its own labelling and
|
|
18586
|
+
* layout; the flyout only provides the surface. */
|
|
18587
|
+
/* @__PURE__ */ jsx("div", { className: "p-1.5", children: activeSection.content })
|
|
18588
|
+
) : sectionRows.length === 0 ? /* @__PURE__ */ jsx(
|
|
18565
18589
|
EmptyRow,
|
|
18566
18590
|
{
|
|
18567
18591
|
label: activeSection.options.length === 0 ? "Nothing to filter by" : "No matches"
|
|
@@ -18609,7 +18633,7 @@ function DisplayMenu({
|
|
|
18609
18633
|
option.key
|
|
18610
18634
|
);
|
|
18611
18635
|
}) }),
|
|
18612
|
-
activeSection.mode === "multi" && activeSection.selected.size > 0 && activeSection.onClear && /* @__PURE__ */ jsx("div", { className: "border-t border-border p-1", children: /* @__PURE__ */ jsxs(
|
|
18636
|
+
activeSection.mode === "multi" && activeSection.selected.size > 0 && activeSection.onClear && /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-border p-1", children: /* @__PURE__ */ jsxs(
|
|
18613
18637
|
Button,
|
|
18614
18638
|
{
|
|
18615
18639
|
variant: "ghost",
|