@stackone/malachite 2.40.0 → 2.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +1 -1
- package/dist/components/ActionMenu.js +91 -82
- package/dist/components/ActionMenu.js.map +1 -1
- package/dist/components/Popover.js +68 -65
- package/dist/components/Popover.js.map +1 -1
- package/dist/components/actionMenuStyles.js +47 -33
- package/dist/components/actionMenuStyles.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/package.json +1 -1
package/COMPONENTS.md
CHANGED
|
@@ -58,7 +58,7 @@ The catalog of components exported from `@stackone/malachite`. **Reach for one o
|
|
|
58
58
|
- **`PreviewTreeRow`** — A single labelled row within a PreviewTree. _Key props:_ `label`.
|
|
59
59
|
- **`Tabs`** — Tabbed navigation switching between content panels. `trailing` puts content on the same row, settled against its end — a count, a scope picker, the state the tabs are showing — inside the rule this component already draws. Without it a consumer had to redraw that rule to carry it past the tabs, and the tab list, which cannot wrap, would not yield any width to what sat beside it. The tabs hold their width and the trailing content gives ground; the `tablist` role stays on the tabs alone, so the extra content is not announced as one. _Key props:_ `TabItem[]`, `activeKey`, `onChange`, `trailing`, `className`, `style`.
|
|
60
60
|
- **`Breadcrumbs`** — Breadcrumb trail with caret separators, an optional back button (shown only when there is more than one crumb AND the parent — the second-to-last — carries a `url` to target) and a right-aligned `actions` row. A lone crumb marked `isRoot` switches to title mode: plain emphasised text, no trail chrome, but the `actions` row still renders. Renders as a `nav`/`ol` with `aria-current="page"` on the last crumb. Pass `renderLink` to route crumbs through the app's own router instead of a plain `<a>`. Used by `PageLayout`, but standalone too. _Key props:_ `links[]` (`name`, `url`, `isRoot`), `actions[]`, `renderLink`, `className`, `style`.
|
|
61
|
-
- **`ActionMenu`** — Dropdown menu of actions (kebab/overflow style). The trigger is an icon-only `Button` by default, so `size` draws exactly the square a `Button` of that size draws beside it: `xsmall` is Figma's 20px "Button Element - Square (X-Small)", `small` the 28px rung above it. Both squares are fixed rather than read off the `controlHeights` tokens, so re-theming those does not resize the trigger. A supplied `triggerIcon` is held to the tier's glyph size — 11px at `xsmall`, 16px at `small`. At 20px the `xsmall` trigger meets WCAG 2.5.8 through the *Spacing* exception rather than the 24px minimum, so keep at least 4px between adjacent triggers. `showLabel` draws `label` beside the icon instead of leaving it to the accessible name — for a menu that is the point of its corner rather than an overflow for a row that already says what it acts on — and `variant` puts the trigger on any `Button` variant, so a menu handing work to the assistant can wear the assistant ground rather than the overflow-kebab outline. An item's `icon` leads its label: every caller's names the action itself — copy, open, delete — and an icon that is part of an item's identity belongs where the eye starts rather than right-aligned, where it reads as a status column. The menu is Figma's `Dropdown Component - Options List`: a fixed sized to its widest item between a 180px floor and the 280px Figma draws, by default under the trigger's right edge, 14px below it, flipping above or clamping sideways when the viewport has no room, rounding at the library's 12px and padding wider (8px) than the menu a `Dropdown` opens. Items read in the primary text colour, or the secondary one with `tone="muted"` (e.g. a sidebar's menus); destructive ones in red. A ceiling rather than a flat width: taken literally it gave a menu of four short verbs 130px of air and made every kebab in an app the same width whatever it held. `minWidth` only raises it above that ceiling, so it is the one way to go past 280px, and a label too long for the box truncates rather than wrapping. _Key props:_ `ActionMenuItem[]`, `size`, `label`, `triggerIcon`, `minWidth`, `showLabel`, `variant`, `tone` (`default` | `muted`), `trigger` (your own control in place of the kebab, cloned with the menu's ref and aria state), `placement` (`bottom-end` default | `bottom-start` | `right-start` | `top-start` | `top-center`), `offset` (`small` 8px | `medium` 12px | `large` 14px; applies to every placement; without it the default `bottom-end` keeps the options menu's 14px and the others use 8px). An item's `tag` (e.g. an "Early access" `Tag`) follows its label in a lazily loaded shadow host of its own, since the menu renders into `document.body`, and joins its accessible name; `current: true` marks the item for the page the user is on (`aria-current="page"` and the selected row paint; a `danger` item keeps its red text). An item with `type: 'text'` is context rather than an action (an account's email above its actions): secondary text the arrow keys skip. Opened from the keyboard, focus lands on the first item; Up/Down/Home/End move through enabled items, Tab closes the menu and moves on as usual; Escape or a picked item returns focus to the trigger.
|
|
61
|
+
- **`ActionMenu`** — Dropdown menu of actions (kebab/overflow style). The trigger is an icon-only `Button` by default, so `size` draws exactly the square a `Button` of that size draws beside it: `xsmall` is Figma's 20px "Button Element - Square (X-Small)", `small` the 28px rung above it. Both squares are fixed rather than read off the `controlHeights` tokens, so re-theming those does not resize the trigger. A supplied `triggerIcon` is held to the tier's glyph size — 11px at `xsmall`, 16px at `small`. At 20px the `xsmall` trigger meets WCAG 2.5.8 through the *Spacing* exception rather than the 24px minimum, so keep at least 4px between adjacent triggers. `showLabel` draws `label` beside the icon instead of leaving it to the accessible name — for a menu that is the point of its corner rather than an overflow for a row that already says what it acts on — and `variant` puts the trigger on any `Button` variant, so a menu handing work to the assistant can wear the assistant ground rather than the overflow-kebab outline. An item's `icon` leads its label: every caller's names the action itself — copy, open, delete — and an icon that is part of an item's identity belongs where the eye starts rather than right-aligned, where it reads as a status column. The menu is Figma's `Dropdown Component - Options List`: a fixed sized to its widest item between a 180px floor and the 280px Figma draws, by default under the trigger's right edge, 14px below it, flipping above or clamping sideways when the viewport has no room, rounding at the library's 12px and padding wider (8px) than the menu a `Dropdown` opens. Items read in the primary text colour, or the secondary one with `tone="muted"` (e.g. a sidebar's menus); destructive ones in red; `danger: 'hover'` keeps an item in its normal colour and turns it destructive (red text on a light red ground) only on hover or keyboard focus, for an action like signing out. A ceiling rather than a flat width: taken literally it gave a menu of four short verbs 130px of air and made every kebab in an app the same width whatever it held. `minWidth` only raises it above that ceiling, so it is the one way to go past 280px, and a label too long for the box truncates rather than wrapping. _Key props:_ `ActionMenuItem[]`, `size`, `label`, `triggerIcon`, `minWidth`, `showLabel`, `variant`, `tone` (`default` | `muted`), `trigger` (your own control in place of the kebab, cloned with the menu's ref and aria state), `placement` (`bottom-end` default | `bottom-start` | `right-start` | `top-start` | `top-center`), `offset` (`small` 8px | `medium` 12px | `large` 14px; applies to every placement; without it the default `bottom-end` keeps the options menu's 14px and the others use 8px). An item's `tag` (e.g. an "Early access" `Tag`) follows its label in a lazily loaded shadow host of its own, since the menu renders into `document.body`, and joins its accessible name; `current: true` marks the item for the page the user is on (`aria-current="page"` and the selected row paint; a `danger` item keeps its red text). An item with `type: 'text'` is context rather than an action (an account's email above its actions): secondary text the arrow keys skip. Opened from the keyboard, focus lands on the first item; Up/Down/Home/End move through enabled items, Tab closes the menu and moves on as usual; Escape or a picked item returns focus to the trigger.
|
|
62
62
|
- **`NavItem`** — A row in an app's side navigation. Idle rows read secondary and take the list hover; the `active` row wears the accent tint and sets `aria-current="page"` on a link. `collapsed` shrinks it to a 36px icon tile whose label becomes the accessible name and a right-hand tooltip, with any `badge` pinned to the tile's corner. `variant="subitem"` indents a child row behind a dot instead of an icon; `disclosure` makes a group header button with `aria-expanded` and a rotating chevron, and `hasActiveChild` colours its text while a child is current. Renders an `<a>` with `href`, the element passed with `asChild` (a router `Link`), or a `<button>`, and forwards its ref and extra attributes so it can be a `Popover` or `ActionMenu` trigger. _Key props:_ `label`, `icon`, `active`, `hasActiveChild`, `collapsed`, `variant` (`item` | `subitem`), `danger`, `trailing`, `badge`, `disclosure` (`expanded` | `collapsed`), `tooltip`, `accessibleLabel` (overrides the accessible name; collapsed it defaults to the tooltip text plus `badgeLabel`), `href`, `target`, `rel`, `onClick`, `asChild` + `children`, `className`, `size` (`medium` | `small`, the tighter row for lists inside a popover), `tag` (beside the label, e.g. early access), `badgeLabel` (read in place of the badge and added to the collapsed name). A passed `aria-expanded` (e.g. from a `Popover`) wins over `disclosure`, and a button row forwards a passed `aria-current`.
|
|
63
63
|
- **`Sidebar`** — An app's left navigation shell, pinned to the viewport's left edge: 260px wide, 60px `collapsed`, on the sunken page background. Composed from `Sidebar.Header` (a 56px band for `Sidebar.Switcher`, the logo + workspace + parent trigger), `Sidebar.Body` (the part that scrolls), `Sidebar.Notices`, `Sidebar.Search`, `Sidebar.Section` (rows of `NavItem`s at the gutter), `Sidebar.Group` + `Sidebar.GroupItems` (a disclosure header and its sub-items), `Sidebar.Divider`, `Sidebar.Usage` (a `UsageCard` placed at the sidebar's gutter) and `Sidebar.Footer` holding `Sidebar.Account`. Popovers opened from it (with `padding="none"`) wrap their body in `Sidebar.Panel`, sized so the popover comes to `SIDEBAR_POPOVER_WIDTH_PX`, and build it from the `Popover` parts. `onToggleCollapse` adds the edge toggle, shown in CSS while the pointer is over the sidebar or a keyboard focus is inside it. Notices, the divider and the usage card hide while collapsed. _Key props:_ `collapsed`, `onToggleCollapse`, `expandLabel`, `collapseLabel`, `aria-label`; `Sidebar.Switcher`: `logo`, `title`, `subtitle` + button attributes; `Sidebar.Account`: `avatar` or `initials` (a small chip when there is no photo), `name`, `tooltip` (the tooltip on the collapsed rail, `name` by default; pass a node to wrap it, e.g. to keep it out of session recordings) + button attributes; `Sidebar.Usage`: the `UsageCard` props; `Sidebar.Notices`: `aria-label` (required). `useSidebarCollapsed()` reads the state from inside; `SIDEBAR_WIDTH_PX` gives the page its offset beside the sidebar.
|
|
64
64
|
- **`SearchTrigger`** — A button drawn as a filled search field, for opening a command palette or search dialog from app chrome such as a sidebar. It is not an input — typing happens in what it opens. `shortcut` draws flat `<kbd>` chips at the end (not `Keycap`, whose raised shadow reads as a second button inside a filled field), kept out of the accessible name; `iconOnly` makes a 36px icon tile matching a collapsed `NavItem`, named by `label`. _Key props:_ `label` (default `Search`), `shortcut` (`ReactNode[]`), `iconOnly`, `aria-label`, `className`, plus native button attributes (`onClick`, `aria-haspopup`, `aria-expanded`, `aria-keyshortcuts`).
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { jsxs as h, jsx as
|
|
2
|
-
import
|
|
1
|
+
import { jsxs as h, jsx as o, Fragment as B } from "react/jsx-runtime";
|
|
2
|
+
import I, { useState as j, useRef as E, useEffect as z, useMemo as G } from "react";
|
|
3
3
|
import { createPortal as $ } from "react-dom";
|
|
4
|
-
import { Button as
|
|
5
|
-
import
|
|
6
|
-
import { focusEdge as
|
|
7
|
-
import { TRIGGER_SIZES as
|
|
8
|
-
import { getDeepActiveElement as
|
|
9
|
-
import { elementRef as
|
|
10
|
-
import { pushEscapeLayer as
|
|
11
|
-
import { overlayIdentity as
|
|
12
|
-
import { useActionMenuPosition as
|
|
13
|
-
const
|
|
4
|
+
import { Button as U } from "./Button.js";
|
|
5
|
+
import X from "./CustomIcons/MoreHorizontalIcon.js";
|
|
6
|
+
import { focusEdge as C, edgeForTriggerKey as Z, itemForMenuKey as q, enabledItems as J } from "./actionMenuKeyboard.js";
|
|
7
|
+
import { TRIGGER_SIZES as Q, EMPTY_ITEM_STYLE as V, menuStyle as W, itemStyle as R, TEXT_ITEM_STYLE as H, ITEM_CLASS as ee, ITEM_ICON_LAYOUT as ne, ITEM_ICON_STYLE as re, ITEM_ICON_CLASS as oe, LABEL_STYLE as te } from "./actionMenuStyles.js";
|
|
8
|
+
import { getDeepActiveElement as ae } from "./activeElement.js";
|
|
9
|
+
import { elementRef as le, composeRefs as se, chainHandler as v } from "./composeRefs.js";
|
|
10
|
+
import { pushEscapeLayer as ce } from "./escapeLayers.js";
|
|
11
|
+
import { overlayIdentity as ie } from "./portalOverlay.js";
|
|
12
|
+
import { useActionMenuPosition as ue } from "./useActionMenuPosition.js";
|
|
13
|
+
const de = I.lazy(() => import("./ActionMenuTagHost.js")), fe = (n) => n != null && n !== !1, g = (n) => n.stopPropagation(), pe = ({ item: n, tone: u, onSelect: M }) => n.type === "divider" ? /* @__PURE__ */ o("div", { style: R(n) }) : n.type === "text" ? /* @__PURE__ */ o("div", { role: "menuitem", "aria-disabled": "true", style: H, children: n.label }) : /* @__PURE__ */ h(
|
|
14
14
|
"button",
|
|
15
15
|
{
|
|
16
16
|
type: "button",
|
|
@@ -19,107 +19,116 @@ const ie = S.lazy(() => import("./ActionMenuTagHost.js")), ue = (n) => n != null
|
|
|
19
19
|
title: n.tooltip,
|
|
20
20
|
"aria-current": n.current ? "page" : void 0,
|
|
21
21
|
className: ee,
|
|
22
|
+
"data-danger": n.danger === "hover" ? "hover" : void 0,
|
|
23
|
+
"data-tone": u,
|
|
22
24
|
onClick: () => M(n),
|
|
23
|
-
style:
|
|
25
|
+
style: R(n, u),
|
|
24
26
|
children: [
|
|
25
|
-
n.icon && /* @__PURE__ */
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
n.icon && /* @__PURE__ */ o(
|
|
28
|
+
"span",
|
|
29
|
+
{
|
|
30
|
+
className: oe,
|
|
31
|
+
style: n.danger === "hover" ? ne : re,
|
|
32
|
+
children: n.icon
|
|
33
|
+
}
|
|
34
|
+
),
|
|
35
|
+
/* @__PURE__ */ o("span", { style: te, children: n.label }),
|
|
36
|
+
fe(n.tag) ? /* @__PURE__ */ o(I.Suspense, { fallback: null, children: /* @__PURE__ */ o(de, { children: n.tag }) }) : null
|
|
28
37
|
]
|
|
29
38
|
}
|
|
30
|
-
),
|
|
39
|
+
), De = ({
|
|
31
40
|
items: n,
|
|
32
|
-
label:
|
|
41
|
+
label: u = "More actions",
|
|
33
42
|
triggerIcon: M,
|
|
34
|
-
minWidth:
|
|
35
|
-
size:
|
|
43
|
+
minWidth: x,
|
|
44
|
+
size: S = "small",
|
|
36
45
|
showLabel: y = !1,
|
|
37
|
-
variant:
|
|
38
|
-
trigger:
|
|
39
|
-
placement:
|
|
40
|
-
offset:
|
|
41
|
-
tone:
|
|
46
|
+
variant: L = "outline",
|
|
47
|
+
trigger: s,
|
|
48
|
+
placement: b = "bottom-end",
|
|
49
|
+
offset: O,
|
|
50
|
+
tone: K = "default"
|
|
42
51
|
}) => {
|
|
43
|
-
const T =
|
|
44
|
-
minWidth:
|
|
45
|
-
placement:
|
|
46
|
-
offset:
|
|
52
|
+
const T = Q[S], [t, c] = j(!1), d = E(null), i = E(null), p = E(null), f = E(!1), A = ue(t, d, i, {
|
|
53
|
+
minWidth: x,
|
|
54
|
+
placement: b,
|
|
55
|
+
offset: O
|
|
47
56
|
});
|
|
48
57
|
z(() => {
|
|
49
58
|
if (!t)
|
|
50
59
|
return;
|
|
51
|
-
p.current && (
|
|
52
|
-
const e = (
|
|
53
|
-
const m =
|
|
54
|
-
[
|
|
55
|
-
(
|
|
56
|
-
) || (
|
|
60
|
+
p.current && (C(i.current, p.current), p.current = null);
|
|
61
|
+
const e = (l) => {
|
|
62
|
+
const m = l.composedPath();
|
|
63
|
+
[d.current, i.current].some(
|
|
64
|
+
(w) => w && m.includes(w)
|
|
65
|
+
) || (f.current = !1, c(!1));
|
|
57
66
|
};
|
|
58
67
|
document.addEventListener("mousedown", e, !0);
|
|
59
|
-
const
|
|
68
|
+
const r = ce(() => c(!1));
|
|
60
69
|
return () => {
|
|
61
70
|
var m;
|
|
62
|
-
document.removeEventListener("mousedown", e, !0),
|
|
63
|
-
const
|
|
64
|
-
|
|
71
|
+
document.removeEventListener("mousedown", e, !0), r();
|
|
72
|
+
const l = ae();
|
|
73
|
+
f.current && (!l || l === document.body) && ((m = d.current) == null || m.focus({ preventScroll: !0 })), f.current = !1;
|
|
65
74
|
};
|
|
66
75
|
}, [t]);
|
|
67
|
-
const
|
|
76
|
+
const _ = (e) => {
|
|
68
77
|
e.stopPropagation();
|
|
69
|
-
const
|
|
70
|
-
p.current =
|
|
71
|
-
},
|
|
78
|
+
const r = !t && e.detail === 0;
|
|
79
|
+
p.current = r ? "first" : null, c((l) => !l);
|
|
80
|
+
}, k = (e) => {
|
|
72
81
|
if (e.key === "Tab" && t) {
|
|
73
82
|
c(!1);
|
|
74
83
|
return;
|
|
75
84
|
}
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
85
|
+
const r = Z(e.key, b);
|
|
86
|
+
if (r) {
|
|
78
87
|
if (e.preventDefault(), t) {
|
|
79
|
-
|
|
88
|
+
C(i.current, r);
|
|
80
89
|
return;
|
|
81
90
|
}
|
|
82
|
-
p.current =
|
|
91
|
+
p.current = r, c(!0);
|
|
83
92
|
}
|
|
84
|
-
},
|
|
85
|
-
var
|
|
93
|
+
}, N = (e) => {
|
|
94
|
+
var l;
|
|
86
95
|
if (e.key === "Tab") {
|
|
87
|
-
|
|
96
|
+
f.current = !1, (l = d.current) == null || l.focus({ preventScroll: !0 }), c(!1);
|
|
88
97
|
return;
|
|
89
98
|
}
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
},
|
|
93
|
-
var
|
|
94
|
-
e.type === "divider" || e.type === "text" || e.disabled || ((
|
|
95
|
-
},
|
|
96
|
-
() =>
|
|
97
|
-
[
|
|
98
|
-
),
|
|
99
|
-
ref:
|
|
99
|
+
const r = q(J(i.current), e.target, e.key);
|
|
100
|
+
r && (e.preventDefault(), r.focus());
|
|
101
|
+
}, P = (e) => {
|
|
102
|
+
var r;
|
|
103
|
+
e.type === "divider" || e.type === "text" || e.disabled || ((r = e.onClick) == null || r.call(e), c(!1));
|
|
104
|
+
}, D = s ? le(s) : void 0, F = G(
|
|
105
|
+
() => se(D, d),
|
|
106
|
+
[D]
|
|
107
|
+
), a = s == null ? void 0 : s.props, Y = s ? I.cloneElement(s, {
|
|
108
|
+
ref: F,
|
|
100
109
|
"aria-haspopup": "menu",
|
|
101
110
|
"aria-expanded": t,
|
|
102
|
-
onClick:
|
|
103
|
-
onMouseDown:
|
|
104
|
-
onKeyDown:
|
|
111
|
+
onClick: v(a == null ? void 0 : a.onClick, _),
|
|
112
|
+
onMouseDown: v(a == null ? void 0 : a.onMouseDown, g),
|
|
113
|
+
onKeyDown: v(a == null ? void 0 : a.onKeyDown, k)
|
|
105
114
|
}) : /* @__PURE__ */ h(
|
|
106
|
-
|
|
115
|
+
U,
|
|
107
116
|
{
|
|
108
|
-
ref:
|
|
117
|
+
ref: d,
|
|
109
118
|
type: "button",
|
|
110
|
-
variant:
|
|
111
|
-
size:
|
|
119
|
+
variant: L,
|
|
120
|
+
size: S,
|
|
112
121
|
iconOnly: !y,
|
|
113
|
-
"aria-label": y ? void 0 :
|
|
122
|
+
"aria-label": y ? void 0 : u,
|
|
114
123
|
"aria-haspopup": "menu",
|
|
115
124
|
"aria-expanded": t,
|
|
116
125
|
className: `shrink-0${y ? " gap-2" : ""}`,
|
|
117
|
-
onClick:
|
|
126
|
+
onClick: _,
|
|
118
127
|
onMouseDown: g,
|
|
119
|
-
onKeyDown:
|
|
128
|
+
onKeyDown: k,
|
|
120
129
|
children: [
|
|
121
|
-
/* @__PURE__ */
|
|
122
|
-
y && /* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ o("span", { className: `inline-flex items-center justify-center ${T.iconBox}`, children: M ?? /* @__PURE__ */ o(X, { size: T.icon }) }),
|
|
131
|
+
y && /* @__PURE__ */ o("span", { children: u })
|
|
123
132
|
]
|
|
124
133
|
}
|
|
125
134
|
);
|
|
@@ -132,22 +141,22 @@ const ie = S.lazy(() => import("./ActionMenuTagHost.js")), ue = (n) => n != null
|
|
|
132
141
|
ref: i,
|
|
133
142
|
role: "menu",
|
|
134
143
|
tabIndex: -1,
|
|
135
|
-
"aria-label":
|
|
136
|
-
...
|
|
137
|
-
style: W(
|
|
144
|
+
"aria-label": u,
|
|
145
|
+
...ie("actionmenu"),
|
|
146
|
+
style: W(A),
|
|
138
147
|
onMouseDown: g,
|
|
139
148
|
onClick: g,
|
|
140
|
-
onKeyDown:
|
|
149
|
+
onKeyDown: N,
|
|
141
150
|
onFocus: () => {
|
|
142
|
-
|
|
151
|
+
f.current = !0;
|
|
143
152
|
},
|
|
144
153
|
onBlur: (e) => {
|
|
145
|
-
var
|
|
146
|
-
(
|
|
154
|
+
var r;
|
|
155
|
+
(r = i.current) != null && r.contains(e.relatedTarget) || (f.current = !1);
|
|
147
156
|
},
|
|
148
157
|
children: [
|
|
149
|
-
n.length === 0 && /* @__PURE__ */
|
|
150
|
-
n.map((e) => /* @__PURE__ */
|
|
158
|
+
n.length === 0 && /* @__PURE__ */ o("div", { role: "menuitem", "aria-disabled": "true", style: V, children: "No actions available" }),
|
|
159
|
+
n.map((e) => /* @__PURE__ */ o(pe, { item: e, tone: K, onSelect: P }, e.key))
|
|
151
160
|
]
|
|
152
161
|
}
|
|
153
162
|
),
|
|
@@ -156,6 +165,6 @@ const ie = S.lazy(() => import("./ActionMenuTagHost.js")), ue = (n) => n != null
|
|
|
156
165
|
] });
|
|
157
166
|
};
|
|
158
167
|
export {
|
|
159
|
-
|
|
168
|
+
De as default
|
|
160
169
|
};
|
|
161
170
|
//# sourceMappingURL=ActionMenu.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionMenu.js","sources":["../../src/components/ActionMenu.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport Button from './Button';\nimport type { ButtonVariant } from './Button';\nimport MoreHorizontalIcon from './CustomIcons/MoreHorizontalIcon';\nimport {\n type MenuEdge,\n edgeForTriggerKey,\n enabledItems,\n focusEdge,\n itemForMenuKey,\n} from './actionMenuKeyboard';\nimport {\n EMPTY_ITEM_STYLE,\n ITEM_CLASS,\n ITEM_ICON_STYLE,\n LABEL_STYLE,\n TEXT_ITEM_STYLE,\n TRIGGER_SIZES,\n type ActionMenuTone,\n itemStyle,\n menuStyle,\n} from './actionMenuStyles';\nimport type { ActionMenuItem, ActionMenuPlacement } from './actionMenuTypes';\nimport { getDeepActiveElement } from './activeElement';\nimport { chainHandler, composeRefs, elementRef } from './composeRefs';\nimport { pushEscapeLayer } from './escapeLayers';\nimport type { FloatingOffset } from './floatingMenuGeometry';\nimport { overlayIdentity } from './portalOverlay';\nimport { useActionMenuPosition } from './useActionMenuPosition';\n\nexport type { ActionMenuItem, ActionMenuPlacement, ActionMenuTone };\n\nexport interface ActionMenuProps {\n items: ActionMenuItem[];\n label?: string;\n triggerIcon?: React.ReactNode;\n minWidth?: number;\n size?: 'small' | 'xsmall';\n /**\n * Shows `label` beside the icon instead of leaving it to the accessible name. For a menu\n * that is the point of its corner rather than an overflow for a row that already reads —\n * a kebab nobody can name is only safe when the row it belongs to says what it acts on.\n */\n showLabel?: boolean;\n /** Trigger's Button variant. Defaults to `outline`, the overflow-kebab look. */\n variant?: ButtonVariant;\n trigger?: React.ReactElement;\n placement?: ActionMenuPlacement;\n offset?: FloatingOffset;\n /** `muted` reads items in the secondary text colour, as in a sidebar's menus. */\n tone?: ActionMenuTone;\n}\n\ntype TriggerHandlers = {\n onClick?: (event: React.MouseEvent) => void;\n onMouseDown?: (event: React.MouseEvent) => void;\n onKeyDown?: (event: React.KeyboardEvent) => void;\n};\n\n// Loaded only when an item has a tag: its shadow host pulls in the Tailwind sheet, which a menu\n// without tags should not pay for.\nconst ActionMenuTagHost = React.lazy(() => import('./ActionMenuTagHost'));\n\nconst hasTag = (tag: React.ReactNode) => tag !== undefined && tag !== null && tag !== false;\n\nconst stopPropagation = (event: React.SyntheticEvent) => event.stopPropagation();\n\nconst MenuItemRow: React.FC<{\n item: ActionMenuItem;\n tone: ActionMenuTone;\n onSelect: (item: ActionMenuItem) => void;\n}> = ({ item, tone, onSelect }) => {\n if (item.type === 'divider') {\n return <div style={itemStyle(item)} />;\n }\n if (item.type === 'text') {\n return (\n <div role=\"menuitem\" aria-disabled=\"true\" style={TEXT_ITEM_STYLE}>\n {item.label}\n </div>\n );\n }\n return (\n <button\n type=\"button\"\n role=\"menuitem\"\n disabled={item.disabled}\n title={item.tooltip}\n aria-current={item.current ? 'page' : undefined}\n className={ITEM_CLASS}\n onClick={() => onSelect(item)}\n style={itemStyle(item, tone)}\n >\n {item.icon && <span style={ITEM_ICON_STYLE}>{item.icon}</span>}\n <span style={LABEL_STYLE}>{item.label}</span>\n {hasTag(item.tag) ? (\n <React.Suspense fallback={null}>\n <ActionMenuTagHost>{item.tag}</ActionMenuTagHost>\n </React.Suspense>\n ) : null}\n </button>\n );\n};\n\n/**\n * ActionMenu is an overflow action menu. The trigger is a three-dot `Button` by default, or\n * your own control via `trigger`. The menu is portaled to document.body, so it escapes any\n * Shadow DOM or overflow:hidden ancestor, which is also why it is styled inline.\n */\nconst ActionMenu: React.FC<ActionMenuProps> = ({\n items,\n label = 'More actions',\n triggerIcon,\n minWidth,\n size = 'small',\n showLabel = false,\n variant = 'outline',\n trigger: customTrigger,\n placement = 'bottom-end',\n offset,\n tone = 'default',\n}) => {\n const triggerSize = TRIGGER_SIZES[size];\n const [open, setOpen] = useState(false);\n const triggerRef = useRef<HTMLElement | null>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const focusOnOpen = useRef<MenuEdge | null>(null);\n const focusInMenu = useRef(false);\n\n const position = useActionMenuPosition(open, triggerRef, menuRef, {\n minWidth,\n placement,\n offset,\n });\n\n useEffect(() => {\n if (!open) {\n return;\n }\n if (focusOnOpen.current) {\n focusEdge(menuRef.current, focusOnOpen.current);\n focusOnOpen.current = null;\n }\n const closeOnOutsidePress = (event: MouseEvent) => {\n const path = event.composedPath();\n const inside = [triggerRef.current, menuRef.current].some(\n (node) => node && path.includes(node),\n );\n if (!inside) {\n focusInMenu.current = false;\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', closeOnOutsidePress, true);\n const popEscapeLayer = pushEscapeLayer(() => setOpen(false));\n return () => {\n document.removeEventListener('mousedown', closeOnOutsidePress, true);\n popEscapeLayer();\n const active = getDeepActiveElement();\n if (focusInMenu.current && (!active || active === document.body)) {\n triggerRef.current?.focus({ preventScroll: true });\n }\n focusInMenu.current = false;\n };\n }, [open]);\n\n const toggleFromTrigger = (event: React.MouseEvent) => {\n event.stopPropagation();\n const openedFromKeyboard = !open && event.detail === 0;\n focusOnOpen.current = openedFromKeyboard ? 'first' : null;\n setOpen((v) => !v);\n };\n\n const handleTriggerKeyDown = (event: React.KeyboardEvent) => {\n if (event.key === 'Tab' && open) {\n setOpen(false);\n return;\n }\n const edge = edgeForTriggerKey(event.key, placement);\n if (!edge) {\n return;\n }\n event.preventDefault();\n if (open) {\n focusEdge(menuRef.current, edge);\n return;\n }\n focusOnOpen.current = edge;\n setOpen(true);\n };\n\n const handleMenuKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Tab') {\n focusInMenu.current = false;\n triggerRef.current?.focus({ preventScroll: true });\n setOpen(false);\n return;\n }\n const target = itemForMenuKey(enabledItems(menuRef.current), event.target, event.key);\n if (target) {\n event.preventDefault();\n target.focus();\n }\n };\n\n const selectItem = (item: ActionMenuItem) => {\n if (item.type === 'divider' || item.type === 'text' || item.disabled) {\n return;\n }\n item.onClick?.();\n setOpen(false);\n };\n\n const ownTriggerRef = customTrigger ? elementRef<HTMLElement>(customTrigger) : undefined;\n const composedTriggerRef = useMemo(\n () => composeRefs(ownTriggerRef, triggerRef),\n [ownTriggerRef],\n );\n const customHandlers = customTrigger?.props as TriggerHandlers | undefined;\n\n const triggerElement = customTrigger ? (\n React.cloneElement(customTrigger, {\n ref: composedTriggerRef,\n 'aria-haspopup': 'menu',\n 'aria-expanded': open,\n onClick: chainHandler(customHandlers?.onClick, toggleFromTrigger),\n onMouseDown: chainHandler(customHandlers?.onMouseDown, stopPropagation),\n onKeyDown: chainHandler(customHandlers?.onKeyDown, handleTriggerKeyDown),\n } as Record<string, unknown>)\n ) : (\n <Button\n ref={triggerRef as React.RefObject<HTMLButtonElement>}\n type=\"button\"\n variant={variant}\n size={size}\n iconOnly={!showLabel}\n aria-label={showLabel ? undefined : label}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={`shrink-0${showLabel ? ' gap-2' : ''}`}\n onClick={toggleFromTrigger}\n onMouseDown={stopPropagation}\n onKeyDown={handleTriggerKeyDown}\n >\n <span className={`inline-flex items-center justify-center ${triggerSize.iconBox}`}>\n {triggerIcon ?? <MoreHorizontalIcon size={triggerSize.icon} />}\n </span>\n {showLabel && <span>{label}</span>}\n </Button>\n );\n\n return (\n <>\n {triggerElement}\n {open &&\n createPortal(\n <div\n ref={menuRef}\n role=\"menu\"\n tabIndex={-1}\n aria-label={label}\n {...overlayIdentity('actionmenu')}\n style={menuStyle(position)}\n onMouseDown={stopPropagation}\n onClick={stopPropagation}\n onKeyDown={handleMenuKeyDown}\n onFocus={() => {\n focusInMenu.current = true;\n }}\n onBlur={(e) => {\n if (!menuRef.current?.contains(e.relatedTarget as Node | null)) {\n focusInMenu.current = false;\n }\n }}\n >\n {items.length === 0 && (\n <div role=\"menuitem\" aria-disabled=\"true\" style={EMPTY_ITEM_STYLE}>\n No actions available\n </div>\n )}\n {items.map((item) => (\n <MenuItemRow key={item.key} item={item} tone={tone} onSelect={selectItem} />\n ))}\n </div>,\n document.body,\n )}\n </>\n );\n};\n\nexport default ActionMenu;\n"],"names":["ActionMenuTagHost","React","hasTag","tag","stopPropagation","event","MenuItemRow","item","tone","onSelect","jsx","itemStyle","TEXT_ITEM_STYLE","jsxs","ITEM_CLASS","ITEM_ICON_STYLE","LABEL_STYLE","ActionMenu","items","label","triggerIcon","minWidth","size","showLabel","variant","customTrigger","placement","offset","triggerSize","TRIGGER_SIZES","open","setOpen","useState","triggerRef","useRef","menuRef","focusOnOpen","focusInMenu","position","useActionMenuPosition","useEffect","focusEdge","closeOnOutsidePress","path","node","popEscapeLayer","pushEscapeLayer","active","getDeepActiveElement","_a","toggleFromTrigger","openedFromKeyboard","v","handleTriggerKeyDown","edge","edgeForTriggerKey","handleMenuKeyDown","target","itemForMenuKey","enabledItems","selectItem","ownTriggerRef","elementRef","composedTriggerRef","useMemo","composeRefs","customHandlers","triggerElement","chainHandler","Button","MoreHorizontalIcon","Fragment","createPortal","overlayIdentity","menuStyle","EMPTY_ITEM_STYLE"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ActionMenu.js","sources":["../../src/components/ActionMenu.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport Button from './Button';\nimport type { ButtonVariant } from './Button';\nimport MoreHorizontalIcon from './CustomIcons/MoreHorizontalIcon';\nimport {\n type MenuEdge,\n edgeForTriggerKey,\n enabledItems,\n focusEdge,\n itemForMenuKey,\n} from './actionMenuKeyboard';\nimport {\n EMPTY_ITEM_STYLE,\n ITEM_CLASS,\n ITEM_ICON_CLASS,\n ITEM_ICON_LAYOUT,\n ITEM_ICON_STYLE,\n LABEL_STYLE,\n TEXT_ITEM_STYLE,\n TRIGGER_SIZES,\n type ActionMenuTone,\n itemStyle,\n menuStyle,\n} from './actionMenuStyles';\nimport type { ActionMenuItem, ActionMenuPlacement } from './actionMenuTypes';\nimport { getDeepActiveElement } from './activeElement';\nimport { chainHandler, composeRefs, elementRef } from './composeRefs';\nimport { pushEscapeLayer } from './escapeLayers';\nimport type { FloatingOffset } from './floatingMenuGeometry';\nimport { overlayIdentity } from './portalOverlay';\nimport { useActionMenuPosition } from './useActionMenuPosition';\n\nexport type { ActionMenuItem, ActionMenuPlacement, ActionMenuTone };\n\nexport interface ActionMenuProps {\n items: ActionMenuItem[];\n label?: string;\n triggerIcon?: React.ReactNode;\n minWidth?: number;\n size?: 'small' | 'xsmall';\n /**\n * Shows `label` beside the icon instead of leaving it to the accessible name. For a menu\n * that is the point of its corner rather than an overflow for a row that already reads —\n * a kebab nobody can name is only safe when the row it belongs to says what it acts on.\n */\n showLabel?: boolean;\n /** Trigger's Button variant. Defaults to `outline`, the overflow-kebab look. */\n variant?: ButtonVariant;\n trigger?: React.ReactElement;\n placement?: ActionMenuPlacement;\n offset?: FloatingOffset;\n /** `muted` reads items in the secondary text colour, as in a sidebar's menus. */\n tone?: ActionMenuTone;\n}\n\ntype TriggerHandlers = {\n onClick?: (event: React.MouseEvent) => void;\n onMouseDown?: (event: React.MouseEvent) => void;\n onKeyDown?: (event: React.KeyboardEvent) => void;\n};\n\n// Loaded only when an item has a tag: its shadow host pulls in the Tailwind sheet, which a menu\n// without tags should not pay for.\nconst ActionMenuTagHost = React.lazy(() => import('./ActionMenuTagHost'));\n\nconst hasTag = (tag: React.ReactNode) => tag !== undefined && tag !== null && tag !== false;\n\nconst stopPropagation = (event: React.SyntheticEvent) => event.stopPropagation();\n\nconst MenuItemRow: React.FC<{\n item: ActionMenuItem;\n tone: ActionMenuTone;\n onSelect: (item: ActionMenuItem) => void;\n}> = ({ item, tone, onSelect }) => {\n if (item.type === 'divider') {\n return <div style={itemStyle(item)} />;\n }\n if (item.type === 'text') {\n return (\n <div role=\"menuitem\" aria-disabled=\"true\" style={TEXT_ITEM_STYLE}>\n {item.label}\n </div>\n );\n }\n return (\n <button\n type=\"button\"\n role=\"menuitem\"\n disabled={item.disabled}\n title={item.tooltip}\n aria-current={item.current ? 'page' : undefined}\n className={ITEM_CLASS}\n data-danger={item.danger === 'hover' ? 'hover' : undefined}\n data-tone={tone}\n onClick={() => onSelect(item)}\n style={itemStyle(item, tone)}\n >\n {item.icon && (\n <span\n className={ITEM_ICON_CLASS}\n style={item.danger === 'hover' ? ITEM_ICON_LAYOUT : ITEM_ICON_STYLE}\n >\n {item.icon}\n </span>\n )}\n <span style={LABEL_STYLE}>{item.label}</span>\n {hasTag(item.tag) ? (\n <React.Suspense fallback={null}>\n <ActionMenuTagHost>{item.tag}</ActionMenuTagHost>\n </React.Suspense>\n ) : null}\n </button>\n );\n};\n\n/**\n * ActionMenu is an overflow action menu. The trigger is a three-dot `Button` by default, or\n * your own control via `trigger`. The menu is portaled to document.body, so it escapes any\n * Shadow DOM or overflow:hidden ancestor, which is also why it is styled inline.\n */\nconst ActionMenu: React.FC<ActionMenuProps> = ({\n items,\n label = 'More actions',\n triggerIcon,\n minWidth,\n size = 'small',\n showLabel = false,\n variant = 'outline',\n trigger: customTrigger,\n placement = 'bottom-end',\n offset,\n tone = 'default',\n}) => {\n const triggerSize = TRIGGER_SIZES[size];\n const [open, setOpen] = useState(false);\n const triggerRef = useRef<HTMLElement | null>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const focusOnOpen = useRef<MenuEdge | null>(null);\n const focusInMenu = useRef(false);\n\n const position = useActionMenuPosition(open, triggerRef, menuRef, {\n minWidth,\n placement,\n offset,\n });\n\n useEffect(() => {\n if (!open) {\n return;\n }\n if (focusOnOpen.current) {\n focusEdge(menuRef.current, focusOnOpen.current);\n focusOnOpen.current = null;\n }\n const closeOnOutsidePress = (event: MouseEvent) => {\n const path = event.composedPath();\n const inside = [triggerRef.current, menuRef.current].some(\n (node) => node && path.includes(node),\n );\n if (!inside) {\n focusInMenu.current = false;\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', closeOnOutsidePress, true);\n const popEscapeLayer = pushEscapeLayer(() => setOpen(false));\n return () => {\n document.removeEventListener('mousedown', closeOnOutsidePress, true);\n popEscapeLayer();\n const active = getDeepActiveElement();\n if (focusInMenu.current && (!active || active === document.body)) {\n triggerRef.current?.focus({ preventScroll: true });\n }\n focusInMenu.current = false;\n };\n }, [open]);\n\n const toggleFromTrigger = (event: React.MouseEvent) => {\n event.stopPropagation();\n const openedFromKeyboard = !open && event.detail === 0;\n focusOnOpen.current = openedFromKeyboard ? 'first' : null;\n setOpen((v) => !v);\n };\n\n const handleTriggerKeyDown = (event: React.KeyboardEvent) => {\n if (event.key === 'Tab' && open) {\n setOpen(false);\n return;\n }\n const edge = edgeForTriggerKey(event.key, placement);\n if (!edge) {\n return;\n }\n event.preventDefault();\n if (open) {\n focusEdge(menuRef.current, edge);\n return;\n }\n focusOnOpen.current = edge;\n setOpen(true);\n };\n\n const handleMenuKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Tab') {\n focusInMenu.current = false;\n triggerRef.current?.focus({ preventScroll: true });\n setOpen(false);\n return;\n }\n const target = itemForMenuKey(enabledItems(menuRef.current), event.target, event.key);\n if (target) {\n event.preventDefault();\n target.focus();\n }\n };\n\n const selectItem = (item: ActionMenuItem) => {\n if (item.type === 'divider' || item.type === 'text' || item.disabled) {\n return;\n }\n item.onClick?.();\n setOpen(false);\n };\n\n const ownTriggerRef = customTrigger ? elementRef<HTMLElement>(customTrigger) : undefined;\n const composedTriggerRef = useMemo(\n () => composeRefs(ownTriggerRef, triggerRef),\n [ownTriggerRef],\n );\n const customHandlers = customTrigger?.props as TriggerHandlers | undefined;\n\n const triggerElement = customTrigger ? (\n React.cloneElement(customTrigger, {\n ref: composedTriggerRef,\n 'aria-haspopup': 'menu',\n 'aria-expanded': open,\n onClick: chainHandler(customHandlers?.onClick, toggleFromTrigger),\n onMouseDown: chainHandler(customHandlers?.onMouseDown, stopPropagation),\n onKeyDown: chainHandler(customHandlers?.onKeyDown, handleTriggerKeyDown),\n } as Record<string, unknown>)\n ) : (\n <Button\n ref={triggerRef as React.RefObject<HTMLButtonElement>}\n type=\"button\"\n variant={variant}\n size={size}\n iconOnly={!showLabel}\n aria-label={showLabel ? undefined : label}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={`shrink-0${showLabel ? ' gap-2' : ''}`}\n onClick={toggleFromTrigger}\n onMouseDown={stopPropagation}\n onKeyDown={handleTriggerKeyDown}\n >\n <span className={`inline-flex items-center justify-center ${triggerSize.iconBox}`}>\n {triggerIcon ?? <MoreHorizontalIcon size={triggerSize.icon} />}\n </span>\n {showLabel && <span>{label}</span>}\n </Button>\n );\n\n return (\n <>\n {triggerElement}\n {open &&\n createPortal(\n <div\n ref={menuRef}\n role=\"menu\"\n tabIndex={-1}\n aria-label={label}\n {...overlayIdentity('actionmenu')}\n style={menuStyle(position)}\n onMouseDown={stopPropagation}\n onClick={stopPropagation}\n onKeyDown={handleMenuKeyDown}\n onFocus={() => {\n focusInMenu.current = true;\n }}\n onBlur={(e) => {\n if (!menuRef.current?.contains(e.relatedTarget as Node | null)) {\n focusInMenu.current = false;\n }\n }}\n >\n {items.length === 0 && (\n <div role=\"menuitem\" aria-disabled=\"true\" style={EMPTY_ITEM_STYLE}>\n No actions available\n </div>\n )}\n {items.map((item) => (\n <MenuItemRow key={item.key} item={item} tone={tone} onSelect={selectItem} />\n ))}\n </div>,\n document.body,\n )}\n </>\n );\n};\n\nexport default ActionMenu;\n"],"names":["ActionMenuTagHost","React","hasTag","tag","stopPropagation","event","MenuItemRow","item","tone","onSelect","jsx","itemStyle","TEXT_ITEM_STYLE","jsxs","ITEM_CLASS","ITEM_ICON_CLASS","ITEM_ICON_LAYOUT","ITEM_ICON_STYLE","LABEL_STYLE","ActionMenu","items","label","triggerIcon","minWidth","size","showLabel","variant","customTrigger","placement","offset","triggerSize","TRIGGER_SIZES","open","setOpen","useState","triggerRef","useRef","menuRef","focusOnOpen","focusInMenu","position","useActionMenuPosition","useEffect","focusEdge","closeOnOutsidePress","path","node","popEscapeLayer","pushEscapeLayer","active","getDeepActiveElement","_a","toggleFromTrigger","openedFromKeyboard","v","handleTriggerKeyDown","edge","edgeForTriggerKey","handleMenuKeyDown","target","itemForMenuKey","enabledItems","selectItem","ownTriggerRef","elementRef","composedTriggerRef","useMemo","composeRefs","customHandlers","triggerElement","chainHandler","Button","MoreHorizontalIcon","Fragment","createPortal","overlayIdentity","menuStyle","EMPTY_ITEM_STYLE"],"mappings":"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"}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { jsxs as me, Fragment as ve, jsx as C } from "react/jsx-runtime";
|
|
2
|
-
import he, { useContext as be, useState as U, useSyncExternalStore as
|
|
3
|
-
import { createPortal as
|
|
2
|
+
import he, { useContext as be, useState as U, useSyncExternalStore as Pe, useRef as h, useCallback as ge, useEffect as W, useMemo as xe } from "react";
|
|
3
|
+
import { createPortal as Ee } from "react-dom";
|
|
4
4
|
import { ShadowRootWrapper as we } from "../ShadowRootWrapper.js";
|
|
5
|
-
import { PopoverEmpty as Le, PopoverSection as
|
|
5
|
+
import { PopoverEmpty as Le, PopoverSection as Me, PopoverList as ye, PopoverAction as Se, PopoverHeader as Te } from "./PopoverParts.js";
|
|
6
6
|
import { getDeepActiveElement as j } from "./activeElement.js";
|
|
7
|
-
import { elementRef as _e, composeRefs as Ae, chainHandler as
|
|
7
|
+
import { elementRef as _e, composeRefs as Ae, chainHandler as v } from "./composeRefs.js";
|
|
8
8
|
import { pushEscapeLayer as Ce } from "./escapeLayers.js";
|
|
9
9
|
import { MENU_VIEWPORT_PADDING_PX as De } from "./floatingMenuGeometry.js";
|
|
10
|
-
import { FOCUSABLE_SELECTOR as
|
|
10
|
+
import { FOCUSABLE_SELECTOR as Oe } from "./overlayBehaviour.js";
|
|
11
11
|
import { PopoverTreeContext as q } from "./popoverTree.js";
|
|
12
|
-
import { overlayIdentity as
|
|
13
|
-
import { useHoverClose as
|
|
12
|
+
import { overlayIdentity as Ie } from "./portalOverlay.js";
|
|
13
|
+
import { useHoverClose as Ne } from "./useHoverClose.js";
|
|
14
14
|
import { useLatestRef as D } from "./useLatestRef.js";
|
|
15
15
|
import { usePopoverPosition as ke } from "./usePopoverPosition.js";
|
|
16
16
|
import { usePopoverTree as Re } from "./usePopoverTree.js";
|
|
@@ -37,13 +37,13 @@ const Ke = "font-sans box-border overflow-auto outline-none rounded-[var(--malac
|
|
|
37
37
|
const c = o.getRootNode();
|
|
38
38
|
return [o, ...c instanceof ShadowRoot ? V(c.host) : []];
|
|
39
39
|
}, We = () => () => {
|
|
40
|
-
}, je = () => !0, qe = () => !1, G = (o, c) => c ?? o.querySelector(
|
|
40
|
+
}, je = () => !0, qe = () => !1, G = (o, c) => c ?? o.querySelector(Oe) ?? o, Ge = ({
|
|
41
41
|
trigger: o,
|
|
42
42
|
children: c,
|
|
43
43
|
open: b,
|
|
44
44
|
defaultOpen: Y = !1,
|
|
45
45
|
onOpenChange: X,
|
|
46
|
-
placement:
|
|
46
|
+
placement: O = "bottom-start",
|
|
47
47
|
openOn: i = "click",
|
|
48
48
|
width: z = "content",
|
|
49
49
|
padding: F = "small",
|
|
@@ -51,36 +51,36 @@ const Ke = "font-sans box-border overflow-auto outline-none rounded-[var(--malac
|
|
|
51
51
|
offset: Q = "small",
|
|
52
52
|
elevation: Z = "default",
|
|
53
53
|
initialFocusRef: d,
|
|
54
|
-
role:
|
|
54
|
+
role: I = "dialog",
|
|
55
55
|
"aria-label": ee,
|
|
56
56
|
"aria-labelledby": re,
|
|
57
57
|
className: oe = ""
|
|
58
58
|
}) => {
|
|
59
|
-
const te = be(q), [ne, se] = U(Y), t = b ?? ne, ce =
|
|
59
|
+
const te = be(q), [ne, se] = U(Y), t = b ?? ne, ce = Pe(We, je, qe), P = h(null), E = h(null), [u, ue] = U(null), N = h(0), _ = h(!1), w = h(!1), L = h(!1), M = D(t), k = D(b), y = D(X), n = ge(
|
|
60
60
|
(e) => {
|
|
61
|
-
var
|
|
62
|
-
e !==
|
|
61
|
+
var m;
|
|
62
|
+
e !== M.current && (k.current === void 0 && se(e), (m = y.current) == null || m.call(y, e));
|
|
63
63
|
},
|
|
64
|
-
[
|
|
65
|
-
), { pointerInside:
|
|
66
|
-
isOpenRef:
|
|
67
|
-
heldByChildren:
|
|
64
|
+
[M, k, y]
|
|
65
|
+
), { pointerInside: g, focusInside: x, triggerFocused: R, cancelClose: f, scheduleClose: S } = Ne({
|
|
66
|
+
isOpenRef: M,
|
|
67
|
+
heldByChildren: N,
|
|
68
68
|
setOpen: n
|
|
69
69
|
}), { tree: ae, ownedSources: A } = Re({
|
|
70
70
|
parent: te,
|
|
71
71
|
isOpen: t,
|
|
72
72
|
openOn: i,
|
|
73
|
-
isOpenRef:
|
|
74
|
-
heldByChildren:
|
|
75
|
-
cancelClose:
|
|
73
|
+
isOpenRef: M,
|
|
74
|
+
heldByChildren: N,
|
|
75
|
+
cancelClose: f,
|
|
76
76
|
scheduleClose: S
|
|
77
77
|
}), r = ke({
|
|
78
78
|
isOpen: t,
|
|
79
79
|
panel: u,
|
|
80
|
-
hostRef:
|
|
81
|
-
triggerRef:
|
|
80
|
+
hostRef: E,
|
|
81
|
+
triggerRef: P,
|
|
82
82
|
anchorRef: J,
|
|
83
|
-
placement:
|
|
83
|
+
placement: O,
|
|
84
84
|
width: z,
|
|
85
85
|
offset: Q
|
|
86
86
|
});
|
|
@@ -90,40 +90,40 @@ const Ke = "font-sans box-border overflow-auto outline-none rounded-[var(--malac
|
|
|
90
90
|
const e = (a) => {
|
|
91
91
|
const l = a.composedPath();
|
|
92
92
|
He(l, [
|
|
93
|
+
P.current,
|
|
93
94
|
E.current,
|
|
94
|
-
g.current,
|
|
95
95
|
...[...A.current].map((pe) => pe())
|
|
96
96
|
]) || n(!1);
|
|
97
97
|
};
|
|
98
98
|
document.addEventListener("mousedown", e, !0);
|
|
99
|
-
const
|
|
99
|
+
const m = Ce(() => n(!1));
|
|
100
100
|
return () => {
|
|
101
101
|
var l;
|
|
102
|
-
document.removeEventListener("mousedown", e, !0),
|
|
102
|
+
document.removeEventListener("mousedown", e, !0), m(), g.current = !1;
|
|
103
103
|
const a = j();
|
|
104
|
-
|
|
104
|
+
x.current && (!a || a === document.body) && (w.current = !0, (l = P.current) == null || l.focus({ preventScroll: !0 }), w.current = !1), x.current = !1;
|
|
105
105
|
};
|
|
106
|
-
}, [t, n, A,
|
|
106
|
+
}, [t, n, A, g, x]);
|
|
107
107
|
const K = t && u !== null && r !== null;
|
|
108
108
|
W(() => {
|
|
109
109
|
const e = i === "click" || _.current;
|
|
110
110
|
!K || !u || !e || (_.current = !1, G(u, d == null ? void 0 : d.current).focus({ preventScroll: !0 }));
|
|
111
111
|
}, [K, u, i, d]);
|
|
112
112
|
const B = () => {
|
|
113
|
-
if (
|
|
113
|
+
if (f(), t && u) {
|
|
114
114
|
G(u, d == null ? void 0 : d.current).focus({ preventScroll: !0 });
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
_.current = !0, n(!0);
|
|
118
118
|
}, T = (e) => {
|
|
119
|
-
const
|
|
119
|
+
const m = [
|
|
120
|
+
P.current,
|
|
120
121
|
E.current,
|
|
121
|
-
g.current,
|
|
122
122
|
u,
|
|
123
123
|
...[...A.current].map((a) => a())
|
|
124
124
|
];
|
|
125
125
|
return V(e).some(
|
|
126
|
-
(a) =>
|
|
126
|
+
(a) => m.some((l) => l == null ? void 0 : l.contains(a))
|
|
127
127
|
);
|
|
128
128
|
}, $ = (e) => {
|
|
129
129
|
e && queueMicrotask(() => {
|
|
@@ -131,20 +131,23 @@ const Ke = "font-sans box-border overflow-auto outline-none rounded-[var(--malac
|
|
|
131
131
|
});
|
|
132
132
|
}, s = i === "hover" ? {
|
|
133
133
|
onMouseEnter: () => {
|
|
134
|
-
|
|
134
|
+
g.current = !0, f(), n(!0);
|
|
135
135
|
},
|
|
136
136
|
onMouseLeave: () => {
|
|
137
|
-
|
|
137
|
+
g.current = !1, S();
|
|
138
|
+
},
|
|
139
|
+
onPanelMouseOver: () => {
|
|
140
|
+
g.current = !0, f();
|
|
138
141
|
}
|
|
139
|
-
} : null,
|
|
140
|
-
() => Ae(H,
|
|
142
|
+
} : null, p = o.props, H = _e(o), le = xe(
|
|
143
|
+
() => Ae(H, P),
|
|
141
144
|
[H]
|
|
142
|
-
), ie = Ue[
|
|
145
|
+
), ie = Ue[O.split("-")[0]], de = he.cloneElement(o, {
|
|
143
146
|
ref: le,
|
|
144
|
-
"aria-haspopup":
|
|
147
|
+
"aria-haspopup": I,
|
|
145
148
|
"aria-expanded": t,
|
|
146
|
-
onClick:
|
|
147
|
-
|
|
149
|
+
onClick: v(
|
|
150
|
+
p.onClick,
|
|
148
151
|
(e) => {
|
|
149
152
|
if (i !== "hover") {
|
|
150
153
|
n(!t);
|
|
@@ -154,39 +157,39 @@ const Ke = "font-sans box-border overflow-auto outline-none rounded-[var(--malac
|
|
|
154
157
|
B();
|
|
155
158
|
return;
|
|
156
159
|
}
|
|
157
|
-
|
|
160
|
+
f(), n(!0);
|
|
158
161
|
}
|
|
159
162
|
),
|
|
160
163
|
...s ? {
|
|
161
|
-
onMouseEnter:
|
|
162
|
-
|
|
164
|
+
onMouseEnter: v(
|
|
165
|
+
p.onMouseEnter,
|
|
163
166
|
s.onMouseEnter
|
|
164
167
|
),
|
|
165
|
-
onMouseLeave:
|
|
166
|
-
|
|
168
|
+
onMouseLeave: v(
|
|
169
|
+
p.onMouseLeave,
|
|
167
170
|
s.onMouseLeave
|
|
168
171
|
),
|
|
169
|
-
onMouseDown:
|
|
170
|
-
|
|
172
|
+
onMouseDown: v(
|
|
173
|
+
p.onMouseDown,
|
|
171
174
|
() => {
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
L.current = !0, setTimeout(() => {
|
|
176
|
+
L.current = !1;
|
|
174
177
|
});
|
|
175
178
|
}
|
|
176
179
|
),
|
|
177
|
-
onFocus:
|
|
178
|
-
const e =
|
|
179
|
-
|
|
180
|
+
onFocus: v(p.onFocus, () => {
|
|
181
|
+
const e = L.current;
|
|
182
|
+
L.current = !1, R.current = !e && !w.current, !w.current && (f(), n(!0));
|
|
180
183
|
}),
|
|
181
|
-
onKeyDown:
|
|
182
|
-
|
|
184
|
+
onKeyDown: v(
|
|
185
|
+
p.onKeyDown,
|
|
183
186
|
(e) => {
|
|
184
187
|
e.key === ie && (e.preventDefault(), B());
|
|
185
188
|
}
|
|
186
189
|
)
|
|
187
190
|
} : {},
|
|
188
|
-
onBlur:
|
|
189
|
-
|
|
191
|
+
onBlur: v(
|
|
192
|
+
p.onBlur,
|
|
190
193
|
(e) => {
|
|
191
194
|
if (R.current = !1, !(!t || T(e.relatedTarget))) {
|
|
192
195
|
if (i === "hover") {
|
|
@@ -204,12 +207,12 @@ const Ke = "font-sans box-border overflow-auto outline-none rounded-[var(--malac
|
|
|
204
207
|
};
|
|
205
208
|
return /* @__PURE__ */ me(ve, { children: [
|
|
206
209
|
de,
|
|
207
|
-
t && ce &&
|
|
210
|
+
t && ce && Ee(
|
|
208
211
|
/* @__PURE__ */ C(q.Provider, { value: ae, children: /* @__PURE__ */ C(
|
|
209
212
|
we,
|
|
210
213
|
{
|
|
211
|
-
hostRef:
|
|
212
|
-
hostProps:
|
|
214
|
+
hostRef: E,
|
|
215
|
+
hostProps: Ie("popover"),
|
|
213
216
|
hostStyle: {
|
|
214
217
|
position: "fixed",
|
|
215
218
|
top: (r == null ? void 0 : r.top) ?? 0,
|
|
@@ -219,21 +222,21 @@ const Ke = "font-sans box-border overflow-auto outline-none rounded-[var(--malac
|
|
|
219
222
|
"div",
|
|
220
223
|
{
|
|
221
224
|
ref: ue,
|
|
222
|
-
role:
|
|
225
|
+
role: I,
|
|
223
226
|
"aria-label": ee,
|
|
224
227
|
"aria-labelledby": re,
|
|
225
228
|
"data-side": r == null ? void 0 : r.side,
|
|
226
229
|
tabIndex: -1,
|
|
227
230
|
className: `${Ke} ${Be[Z]} ${$e[F]} ${oe}`.trim(),
|
|
228
231
|
style: fe,
|
|
229
|
-
|
|
232
|
+
onMouseOver: s == null ? void 0 : s.onPanelMouseOver,
|
|
230
233
|
onMouseLeave: s == null ? void 0 : s.onMouseLeave,
|
|
231
234
|
onFocus: () => {
|
|
232
|
-
|
|
235
|
+
x.current = !0, f();
|
|
233
236
|
},
|
|
234
237
|
onBlur: (e) => {
|
|
235
238
|
if (!T(e.relatedTarget)) {
|
|
236
|
-
if (
|
|
239
|
+
if (x.current = !1, i === "hover") {
|
|
237
240
|
S();
|
|
238
241
|
return;
|
|
239
242
|
}
|
|
@@ -251,8 +254,8 @@ const Ke = "font-sans box-border overflow-auto outline-none rounded-[var(--malac
|
|
|
251
254
|
}, lr = Object.assign(Ge, {
|
|
252
255
|
Header: Te,
|
|
253
256
|
Action: Se,
|
|
254
|
-
List:
|
|
255
|
-
Section:
|
|
257
|
+
List: ye,
|
|
258
|
+
Section: Me,
|
|
256
259
|
Empty: Le
|
|
257
260
|
});
|
|
258
261
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Popover.js","sources":["../../src/components/Popover.tsx"],"sourcesContent":["import React, {\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { ShadowRootWrapper } from '../ShadowRootWrapper';\nimport {\n PopoverAction,\n PopoverEmpty,\n PopoverHeader,\n PopoverList,\n PopoverSection,\n} from './PopoverParts';\nimport { getDeepActiveElement } from './activeElement';\nimport { chainHandler, composeRefs, elementRef } from './composeRefs';\nimport { pushEscapeLayer } from './escapeLayers';\nimport {\n type FloatingOffset,\n type FloatingPlacement,\n MENU_VIEWPORT_PADDING_PX,\n} from './floatingMenuGeometry';\nimport { FOCUSABLE_SELECTOR } from './overlayBehaviour';\nimport { PopoverTreeContext } from './popoverTree';\nimport { overlayIdentity } from './portalOverlay';\nimport { useHoverClose } from './useHoverClose';\nimport { useLatestRef } from './useLatestRef';\nimport { usePopoverPosition } from './usePopoverPosition';\nimport { usePopoverTree } from './usePopoverTree';\n\nexport type PopoverPlacement = FloatingPlacement;\n\ninterface PopoverBaseProps {\n trigger: React.ReactElement;\n children?: React.ReactNode;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n placement?: PopoverPlacement;\n openOn?: 'click' | 'hover';\n width?: 'content' | 'trigger';\n padding?: 'none' | 'small' | 'medium';\n anchorRef?: React.RefObject<HTMLElement | null>;\n offset?: FloatingOffset;\n elevation?: 'default' | 'raised';\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n role?: 'dialog' | 'menu' | 'listbox';\n className?: string;\n}\n\ntype PopoverName =\n | { 'aria-label': string; 'aria-labelledby'?: string }\n | { 'aria-label'?: string; 'aria-labelledby': string };\n\nexport type PopoverProps = PopoverBaseProps & PopoverName;\n\nconst PANEL =\n 'font-sans box-border overflow-auto outline-none rounded-[var(--malachite-radius-xl,12px)] border border-solid border-[var(--malachite-card-border,rgb(232,232,232))] bg-[var(--malachite-card-background,rgb(255,255,255))] text-[var(--malachite-text-primary,rgb(10,10,10))]';\n\nconst ELEVATION: Record<NonNullable<PopoverBaseProps['elevation']>, string> = {\n default: '[box-shadow:0px_4px_10px_0px_var(--malachite-card-shadow,rgba(0,0,0,0.1))]',\n raised: '[box-shadow:0px_8px_24px_0px_var(--malachite-card-shadow,rgba(0,0,0,0.1))]',\n};\n\nconst PADDING: Record<NonNullable<PopoverBaseProps['padding']>, string> = {\n none: 'p-0',\n small: 'p-1',\n medium: 'p-2',\n};\n\nconst pathHits = (path: EventTarget[], nodes: Iterable<Element | null | undefined>): boolean => {\n for (const node of nodes) {\n if (node && path.includes(node)) {\n return true;\n }\n }\n return false;\n};\n\nconst KEYBOARD_OPEN_KEY: Record<string, string> = {\n bottom: 'ArrowDown',\n top: 'ArrowUp',\n right: 'ArrowRight',\n left: 'ArrowLeft',\n};\n\nconst composedPathOf = (node: EventTarget | null): Node[] => {\n if (!(node instanceof Node)) {\n return [];\n }\n const root = node.getRootNode();\n return [node, ...(root instanceof ShadowRoot ? composedPathOf(root.host) : [])];\n};\n\nconst subscribeToNothing = () => () => undefined;\nconst onClient = () => true;\nconst onServer = () => false;\n\nconst focusTarget = (panel: HTMLElement, initial: HTMLElement | null | undefined): HTMLElement =>\n initial ?? panel.querySelector<HTMLElement>(FOCUSABLE_SELECTOR) ?? panel;\n\n/** An anchored, non-modal panel; nested Popovers count as inside their parent. */\nconst PopoverRoot: React.FC<PopoverProps> = ({\n trigger,\n children,\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n placement = 'bottom-start',\n openOn = 'click',\n width = 'content',\n padding = 'small',\n anchorRef,\n offset = 'small',\n elevation = 'default',\n initialFocusRef,\n role = 'dialog',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n}) => {\n const parent = useContext(PopoverTreeContext);\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n const isOpen = controlledOpen ?? uncontrolledOpen;\n const isClient = useSyncExternalStore(subscribeToNothing, onClient, onServer);\n\n const triggerRef = useRef<HTMLElement | null>(null);\n const hostRef = useRef<HTMLDivElement | null>(null);\n const [panel, setPanel] = useState<HTMLDivElement | null>(null);\n const heldByChildren = useRef(0);\n const focusOnOpen = useRef(false);\n const returningFocus = useRef(false);\n const pointerPressedTrigger = useRef(false);\n\n const isOpenRef = useLatestRef(isOpen);\n const controlledRef = useLatestRef(controlledOpen);\n const onOpenChangeRef = useLatestRef(onOpenChange);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (next === isOpenRef.current) {\n return;\n }\n if (controlledRef.current === undefined) {\n setUncontrolledOpen(next);\n }\n onOpenChangeRef.current?.(next);\n },\n [isOpenRef, controlledRef, onOpenChangeRef],\n );\n\n const { pointerInside, focusInside, triggerFocused, cancelClose, scheduleClose } =\n useHoverClose({\n isOpenRef,\n heldByChildren,\n setOpen,\n });\n\n const { tree, ownedSources } = usePopoverTree({\n parent,\n isOpen,\n openOn,\n isOpenRef,\n heldByChildren,\n cancelClose,\n scheduleClose,\n });\n\n const geometry = usePopoverPosition({\n isOpen,\n panel,\n hostRef,\n triggerRef,\n anchorRef,\n placement,\n width,\n offset,\n });\n\n useEffect(() => {\n if (!isOpen) {\n return;\n }\n const closeOnOutsidePress = (event: MouseEvent) => {\n const path = event.composedPath();\n const inside = pathHits(path, [\n triggerRef.current,\n hostRef.current,\n ...[...ownedSources.current].map((source) => source()),\n ]);\n if (!inside) {\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', closeOnOutsidePress, true);\n const popEscapeLayer = pushEscapeLayer(() => setOpen(false));\n return () => {\n document.removeEventListener('mousedown', closeOnOutsidePress, true);\n popEscapeLayer();\n pointerInside.current = false;\n const active = getDeepActiveElement();\n if (focusInside.current && (!active || active === document.body)) {\n returningFocus.current = true;\n triggerRef.current?.focus({ preventScroll: true });\n returningFocus.current = false;\n }\n focusInside.current = false;\n };\n }, [isOpen, setOpen, ownedSources, pointerInside, focusInside]);\n\n const shown = isOpen && panel !== null && geometry !== null;\n\n useEffect(() => {\n const shouldFocus = openOn === 'click' || focusOnOpen.current;\n if (!shown || !panel || !shouldFocus) {\n return;\n }\n focusOnOpen.current = false;\n focusTarget(panel, initialFocusRef?.current).focus({ preventScroll: true });\n }, [shown, panel, openOn, initialFocusRef]);\n\n const openWithFocus = () => {\n cancelClose();\n if (isOpen && panel) {\n focusTarget(panel, initialFocusRef?.current).focus({ preventScroll: true });\n return;\n }\n focusOnOpen.current = true;\n setOpen(true);\n };\n\n const isInside = (node: EventTarget | null): boolean => {\n const containers = [\n triggerRef.current,\n hostRef.current,\n panel,\n ...[...ownedSources.current].map((source) => source()),\n ];\n return composedPathOf(node).some((step) =>\n containers.some((container) => container?.contains(step)),\n );\n };\n\n const closeIfFocusLeft = (next: EventTarget | null) => {\n if (!next) {\n return;\n }\n queueMicrotask(() => {\n if (!isInside(next) && !isInside(getDeepActiveElement())) {\n setOpen(false);\n }\n });\n };\n\n const hoverHandlers =\n openOn === 'hover'\n ? {\n onMouseEnter: () => {\n pointerInside.current = true;\n cancelClose();\n setOpen(true);\n },\n onMouseLeave: () => {\n pointerInside.current = false;\n scheduleClose();\n },\n }\n : null;\n\n const triggerProps = trigger.props as Record<string, unknown>;\n type Handler<E extends React.SyntheticEvent> = ((event: E) => void) | undefined;\n\n const ownTriggerRef = elementRef<HTMLElement>(trigger);\n const composedTriggerRef = useMemo(\n () => composeRefs(ownTriggerRef, triggerRef),\n [ownTriggerRef],\n );\n const openKey = KEYBOARD_OPEN_KEY[placement.split('-')[0]];\n\n const clonedTrigger = React.cloneElement(trigger, {\n ref: composedTriggerRef,\n 'aria-haspopup': role,\n 'aria-expanded': isOpen,\n onClick: chainHandler(\n triggerProps.onClick as Handler<React.MouseEvent>,\n (event: React.MouseEvent) => {\n if (openOn !== 'hover') {\n setOpen(!isOpen);\n return;\n }\n const fromKeyboard = event.detail === 0;\n if (fromKeyboard) {\n openWithFocus();\n return;\n }\n cancelClose();\n setOpen(true);\n },\n ),\n ...(hoverHandlers\n ? {\n onMouseEnter: chainHandler(\n triggerProps.onMouseEnter as Handler<React.MouseEvent>,\n hoverHandlers.onMouseEnter,\n ),\n onMouseLeave: chainHandler(\n triggerProps.onMouseLeave as Handler<React.MouseEvent>,\n hoverHandlers.onMouseLeave,\n ),\n onMouseDown: chainHandler(\n triggerProps.onMouseDown as Handler<React.MouseEvent>,\n () => {\n pointerPressedTrigger.current = true;\n setTimeout(() => {\n pointerPressedTrigger.current = false;\n });\n },\n ),\n onFocus: chainHandler(triggerProps.onFocus as Handler<React.FocusEvent>, () => {\n const fromPointer = pointerPressedTrigger.current;\n pointerPressedTrigger.current = false;\n triggerFocused.current = !fromPointer && !returningFocus.current;\n if (returningFocus.current) {\n return;\n }\n cancelClose();\n setOpen(true);\n }),\n onKeyDown: chainHandler(\n triggerProps.onKeyDown as Handler<React.KeyboardEvent>,\n (event: React.KeyboardEvent) => {\n if (event.key !== openKey) {\n return;\n }\n event.preventDefault();\n openWithFocus();\n },\n ),\n }\n : {}),\n onBlur: chainHandler(\n triggerProps.onBlur as Handler<React.FocusEvent>,\n (event: React.FocusEvent) => {\n triggerFocused.current = false;\n if (!isOpen || isInside(event.relatedTarget)) {\n return;\n }\n if (openOn === 'hover') {\n scheduleClose();\n return;\n }\n closeIfFocusLeft(event.relatedTarget);\n },\n ),\n } as Record<string, unknown>);\n\n const panelStyle: React.CSSProperties = {\n maxHeight: geometry?.maxHeight,\n maxWidth: `calc(100vw - ${MENU_VIEWPORT_PADDING_PX * 2}px)`,\n visibility: geometry ? 'visible' : 'hidden',\n };\n\n return (\n <>\n {clonedTrigger}\n {isOpen &&\n isClient &&\n createPortal(\n <PopoverTreeContext.Provider value={tree}>\n <ShadowRootWrapper\n hostRef={hostRef}\n hostProps={overlayIdentity('popover')}\n hostStyle={{\n position: 'fixed',\n top: geometry?.top ?? 0,\n left: geometry?.left ?? 0,\n }}\n >\n <div\n ref={setPanel}\n role={role}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n data-side={geometry?.side}\n tabIndex={-1}\n className={`${PANEL} ${ELEVATION[elevation]} ${PADDING[padding]} ${className}`.trim()}\n style={panelStyle}\n onMouseEnter={hoverHandlers?.onMouseEnter}\n onMouseLeave={hoverHandlers?.onMouseLeave}\n onFocus={() => {\n focusInside.current = true;\n cancelClose();\n }}\n onBlur={(event) => {\n if (isInside(event.relatedTarget)) {\n return;\n }\n focusInside.current = false;\n if (openOn === 'hover') {\n scheduleClose();\n return;\n }\n closeIfFocusLeft(event.relatedTarget);\n }}\n >\n {children}\n </div>\n </ShadowRootWrapper>\n </PopoverTreeContext.Provider>,\n document.body,\n )}\n </>\n );\n};\n\nexport const Popover = Object.assign(PopoverRoot, {\n Header: PopoverHeader,\n Action: PopoverAction,\n List: PopoverList,\n Section: PopoverSection,\n Empty: PopoverEmpty,\n});\n\nexport default Popover;\n"],"names":["PANEL","ELEVATION","PADDING","pathHits","path","nodes","node","KEYBOARD_OPEN_KEY","composedPathOf","root","subscribeToNothing","onClient","onServer","focusTarget","panel","initial","FOCUSABLE_SELECTOR","PopoverRoot","trigger","children","controlledOpen","defaultOpen","onOpenChange","placement","openOn","width","padding","anchorRef","offset","elevation","initialFocusRef","role","ariaLabel","ariaLabelledBy","className","parent","useContext","PopoverTreeContext","uncontrolledOpen","setUncontrolledOpen","useState","isOpen","isClient","useSyncExternalStore","triggerRef","useRef","hostRef","setPanel","heldByChildren","focusOnOpen","returningFocus","pointerPressedTrigger","isOpenRef","useLatestRef","controlledRef","onOpenChangeRef","setOpen","useCallback","next","_a","pointerInside","focusInside","triggerFocused","cancelClose","scheduleClose","useHoverClose","tree","ownedSources","usePopoverTree","geometry","usePopoverPosition","useEffect","closeOnOutsidePress","event","source","popEscapeLayer","pushEscapeLayer","active","getDeepActiveElement","shown","shouldFocus","openWithFocus","isInside","containers","step","container","closeIfFocusLeft","hoverHandlers","triggerProps","ownTriggerRef","elementRef","composedTriggerRef","useMemo","composeRefs","openKey","clonedTrigger","React","chainHandler","fromPointer","panelStyle","MENU_VIEWPORT_PADDING_PX","jsxs","Fragment","createPortal","jsx","ShadowRootWrapper","overlayIdentity","Popover","PopoverHeader","PopoverAction","PopoverList","PopoverSection","PopoverEmpty"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Popover.js","sources":["../../src/components/Popover.tsx"],"sourcesContent":["import React, {\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { ShadowRootWrapper } from '../ShadowRootWrapper';\nimport {\n PopoverAction,\n PopoverEmpty,\n PopoverHeader,\n PopoverList,\n PopoverSection,\n} from './PopoverParts';\nimport { getDeepActiveElement } from './activeElement';\nimport { chainHandler, composeRefs, elementRef } from './composeRefs';\nimport { pushEscapeLayer } from './escapeLayers';\nimport {\n type FloatingOffset,\n type FloatingPlacement,\n MENU_VIEWPORT_PADDING_PX,\n} from './floatingMenuGeometry';\nimport { FOCUSABLE_SELECTOR } from './overlayBehaviour';\nimport { PopoverTreeContext } from './popoverTree';\nimport { overlayIdentity } from './portalOverlay';\nimport { useHoverClose } from './useHoverClose';\nimport { useLatestRef } from './useLatestRef';\nimport { usePopoverPosition } from './usePopoverPosition';\nimport { usePopoverTree } from './usePopoverTree';\n\nexport type PopoverPlacement = FloatingPlacement;\n\ninterface PopoverBaseProps {\n trigger: React.ReactElement;\n children?: React.ReactNode;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n placement?: PopoverPlacement;\n openOn?: 'click' | 'hover';\n width?: 'content' | 'trigger';\n padding?: 'none' | 'small' | 'medium';\n anchorRef?: React.RefObject<HTMLElement | null>;\n offset?: FloatingOffset;\n elevation?: 'default' | 'raised';\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n role?: 'dialog' | 'menu' | 'listbox';\n className?: string;\n}\n\ntype PopoverName =\n | { 'aria-label': string; 'aria-labelledby'?: string }\n | { 'aria-label'?: string; 'aria-labelledby': string };\n\nexport type PopoverProps = PopoverBaseProps & PopoverName;\n\nconst PANEL =\n 'font-sans box-border overflow-auto outline-none rounded-[var(--malachite-radius-xl,12px)] border border-solid border-[var(--malachite-card-border,rgb(232,232,232))] bg-[var(--malachite-card-background,rgb(255,255,255))] text-[var(--malachite-text-primary,rgb(10,10,10))]';\n\nconst ELEVATION: Record<NonNullable<PopoverBaseProps['elevation']>, string> = {\n default: '[box-shadow:0px_4px_10px_0px_var(--malachite-card-shadow,rgba(0,0,0,0.1))]',\n raised: '[box-shadow:0px_8px_24px_0px_var(--malachite-card-shadow,rgba(0,0,0,0.1))]',\n};\n\nconst PADDING: Record<NonNullable<PopoverBaseProps['padding']>, string> = {\n none: 'p-0',\n small: 'p-1',\n medium: 'p-2',\n};\n\nconst pathHits = (path: EventTarget[], nodes: Iterable<Element | null | undefined>): boolean => {\n for (const node of nodes) {\n if (node && path.includes(node)) {\n return true;\n }\n }\n return false;\n};\n\nconst KEYBOARD_OPEN_KEY: Record<string, string> = {\n bottom: 'ArrowDown',\n top: 'ArrowUp',\n right: 'ArrowRight',\n left: 'ArrowLeft',\n};\n\nconst composedPathOf = (node: EventTarget | null): Node[] => {\n if (!(node instanceof Node)) {\n return [];\n }\n const root = node.getRootNode();\n return [node, ...(root instanceof ShadowRoot ? composedPathOf(root.host) : [])];\n};\n\nconst subscribeToNothing = () => () => undefined;\nconst onClient = () => true;\nconst onServer = () => false;\n\nconst focusTarget = (panel: HTMLElement, initial: HTMLElement | null | undefined): HTMLElement =>\n initial ?? panel.querySelector<HTMLElement>(FOCUSABLE_SELECTOR) ?? panel;\n\n/** An anchored, non-modal panel; nested Popovers count as inside their parent. */\nconst PopoverRoot: React.FC<PopoverProps> = ({\n trigger,\n children,\n open: controlledOpen,\n defaultOpen = false,\n onOpenChange,\n placement = 'bottom-start',\n openOn = 'click',\n width = 'content',\n padding = 'small',\n anchorRef,\n offset = 'small',\n elevation = 'default',\n initialFocusRef,\n role = 'dialog',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n}) => {\n const parent = useContext(PopoverTreeContext);\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n const isOpen = controlledOpen ?? uncontrolledOpen;\n const isClient = useSyncExternalStore(subscribeToNothing, onClient, onServer);\n\n const triggerRef = useRef<HTMLElement | null>(null);\n const hostRef = useRef<HTMLDivElement | null>(null);\n const [panel, setPanel] = useState<HTMLDivElement | null>(null);\n const heldByChildren = useRef(0);\n const focusOnOpen = useRef(false);\n const returningFocus = useRef(false);\n const pointerPressedTrigger = useRef(false);\n\n const isOpenRef = useLatestRef(isOpen);\n const controlledRef = useLatestRef(controlledOpen);\n const onOpenChangeRef = useLatestRef(onOpenChange);\n\n const setOpen = useCallback(\n (next: boolean) => {\n if (next === isOpenRef.current) {\n return;\n }\n if (controlledRef.current === undefined) {\n setUncontrolledOpen(next);\n }\n onOpenChangeRef.current?.(next);\n },\n [isOpenRef, controlledRef, onOpenChangeRef],\n );\n\n const { pointerInside, focusInside, triggerFocused, cancelClose, scheduleClose } =\n useHoverClose({\n isOpenRef,\n heldByChildren,\n setOpen,\n });\n\n const { tree, ownedSources } = usePopoverTree({\n parent,\n isOpen,\n openOn,\n isOpenRef,\n heldByChildren,\n cancelClose,\n scheduleClose,\n });\n\n const geometry = usePopoverPosition({\n isOpen,\n panel,\n hostRef,\n triggerRef,\n anchorRef,\n placement,\n width,\n offset,\n });\n\n useEffect(() => {\n if (!isOpen) {\n return;\n }\n const closeOnOutsidePress = (event: MouseEvent) => {\n const path = event.composedPath();\n const inside = pathHits(path, [\n triggerRef.current,\n hostRef.current,\n ...[...ownedSources.current].map((source) => source()),\n ]);\n if (!inside) {\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', closeOnOutsidePress, true);\n const popEscapeLayer = pushEscapeLayer(() => setOpen(false));\n return () => {\n document.removeEventListener('mousedown', closeOnOutsidePress, true);\n popEscapeLayer();\n pointerInside.current = false;\n const active = getDeepActiveElement();\n if (focusInside.current && (!active || active === document.body)) {\n returningFocus.current = true;\n triggerRef.current?.focus({ preventScroll: true });\n returningFocus.current = false;\n }\n focusInside.current = false;\n };\n }, [isOpen, setOpen, ownedSources, pointerInside, focusInside]);\n\n const shown = isOpen && panel !== null && geometry !== null;\n\n useEffect(() => {\n const shouldFocus = openOn === 'click' || focusOnOpen.current;\n if (!shown || !panel || !shouldFocus) {\n return;\n }\n focusOnOpen.current = false;\n focusTarget(panel, initialFocusRef?.current).focus({ preventScroll: true });\n }, [shown, panel, openOn, initialFocusRef]);\n\n const openWithFocus = () => {\n cancelClose();\n if (isOpen && panel) {\n focusTarget(panel, initialFocusRef?.current).focus({ preventScroll: true });\n return;\n }\n focusOnOpen.current = true;\n setOpen(true);\n };\n\n const isInside = (node: EventTarget | null): boolean => {\n const containers = [\n triggerRef.current,\n hostRef.current,\n panel,\n ...[...ownedSources.current].map((source) => source()),\n ];\n return composedPathOf(node).some((step) =>\n containers.some((container) => container?.contains(step)),\n );\n };\n\n const closeIfFocusLeft = (next: EventTarget | null) => {\n if (!next) {\n return;\n }\n queueMicrotask(() => {\n if (!isInside(next) && !isInside(getDeepActiveElement())) {\n setOpen(false);\n }\n });\n };\n\n const hoverHandlers =\n openOn === 'hover'\n ? {\n onMouseEnter: () => {\n pointerInside.current = true;\n cancelClose();\n setOpen(true);\n },\n onMouseLeave: () => {\n pointerInside.current = false;\n scheduleClose();\n },\n onPanelMouseOver: () => {\n pointerInside.current = true;\n cancelClose();\n },\n }\n : null;\n\n const triggerProps = trigger.props as Record<string, unknown>;\n type Handler<E extends React.SyntheticEvent> = ((event: E) => void) | undefined;\n\n const ownTriggerRef = elementRef<HTMLElement>(trigger);\n const composedTriggerRef = useMemo(\n () => composeRefs(ownTriggerRef, triggerRef),\n [ownTriggerRef],\n );\n const openKey = KEYBOARD_OPEN_KEY[placement.split('-')[0]];\n\n const clonedTrigger = React.cloneElement(trigger, {\n ref: composedTriggerRef,\n 'aria-haspopup': role,\n 'aria-expanded': isOpen,\n onClick: chainHandler(\n triggerProps.onClick as Handler<React.MouseEvent>,\n (event: React.MouseEvent) => {\n if (openOn !== 'hover') {\n setOpen(!isOpen);\n return;\n }\n const fromKeyboard = event.detail === 0;\n if (fromKeyboard) {\n openWithFocus();\n return;\n }\n cancelClose();\n setOpen(true);\n },\n ),\n ...(hoverHandlers\n ? {\n onMouseEnter: chainHandler(\n triggerProps.onMouseEnter as Handler<React.MouseEvent>,\n hoverHandlers.onMouseEnter,\n ),\n onMouseLeave: chainHandler(\n triggerProps.onMouseLeave as Handler<React.MouseEvent>,\n hoverHandlers.onMouseLeave,\n ),\n onMouseDown: chainHandler(\n triggerProps.onMouseDown as Handler<React.MouseEvent>,\n () => {\n pointerPressedTrigger.current = true;\n setTimeout(() => {\n pointerPressedTrigger.current = false;\n });\n },\n ),\n onFocus: chainHandler(triggerProps.onFocus as Handler<React.FocusEvent>, () => {\n const fromPointer = pointerPressedTrigger.current;\n pointerPressedTrigger.current = false;\n triggerFocused.current = !fromPointer && !returningFocus.current;\n if (returningFocus.current) {\n return;\n }\n cancelClose();\n setOpen(true);\n }),\n onKeyDown: chainHandler(\n triggerProps.onKeyDown as Handler<React.KeyboardEvent>,\n (event: React.KeyboardEvent) => {\n if (event.key !== openKey) {\n return;\n }\n event.preventDefault();\n openWithFocus();\n },\n ),\n }\n : {}),\n onBlur: chainHandler(\n triggerProps.onBlur as Handler<React.FocusEvent>,\n (event: React.FocusEvent) => {\n triggerFocused.current = false;\n if (!isOpen || isInside(event.relatedTarget)) {\n return;\n }\n if (openOn === 'hover') {\n scheduleClose();\n return;\n }\n closeIfFocusLeft(event.relatedTarget);\n },\n ),\n } as Record<string, unknown>);\n\n const panelStyle: React.CSSProperties = {\n maxHeight: geometry?.maxHeight,\n maxWidth: `calc(100vw - ${MENU_VIEWPORT_PADDING_PX * 2}px)`,\n visibility: geometry ? 'visible' : 'hidden',\n };\n\n return (\n <>\n {clonedTrigger}\n {isOpen &&\n isClient &&\n createPortal(\n <PopoverTreeContext.Provider value={tree}>\n <ShadowRootWrapper\n hostRef={hostRef}\n hostProps={overlayIdentity('popover')}\n hostStyle={{\n position: 'fixed',\n top: geometry?.top ?? 0,\n left: geometry?.left ?? 0,\n }}\n >\n <div\n ref={setPanel}\n role={role}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n data-side={geometry?.side}\n tabIndex={-1}\n className={`${PANEL} ${ELEVATION[elevation]} ${PADDING[padding]} ${className}`.trim()}\n style={panelStyle}\n // React's mouseenter stops at the shadow host, so the panel listens for mouseover.\n onMouseOver={hoverHandlers?.onPanelMouseOver}\n onMouseLeave={hoverHandlers?.onMouseLeave}\n onFocus={() => {\n focusInside.current = true;\n cancelClose();\n }}\n onBlur={(event) => {\n if (isInside(event.relatedTarget)) {\n return;\n }\n focusInside.current = false;\n if (openOn === 'hover') {\n scheduleClose();\n return;\n }\n closeIfFocusLeft(event.relatedTarget);\n }}\n >\n {children}\n </div>\n </ShadowRootWrapper>\n </PopoverTreeContext.Provider>,\n document.body,\n )}\n </>\n );\n};\n\nexport const Popover = Object.assign(PopoverRoot, {\n Header: PopoverHeader,\n Action: PopoverAction,\n List: PopoverList,\n Section: PopoverSection,\n Empty: PopoverEmpty,\n});\n\nexport default Popover;\n"],"names":["PANEL","ELEVATION","PADDING","pathHits","path","nodes","node","KEYBOARD_OPEN_KEY","composedPathOf","root","subscribeToNothing","onClient","onServer","focusTarget","panel","initial","FOCUSABLE_SELECTOR","PopoverRoot","trigger","children","controlledOpen","defaultOpen","onOpenChange","placement","openOn","width","padding","anchorRef","offset","elevation","initialFocusRef","role","ariaLabel","ariaLabelledBy","className","parent","useContext","PopoverTreeContext","uncontrolledOpen","setUncontrolledOpen","useState","isOpen","isClient","useSyncExternalStore","triggerRef","useRef","hostRef","setPanel","heldByChildren","focusOnOpen","returningFocus","pointerPressedTrigger","isOpenRef","useLatestRef","controlledRef","onOpenChangeRef","setOpen","useCallback","next","_a","pointerInside","focusInside","triggerFocused","cancelClose","scheduleClose","useHoverClose","tree","ownedSources","usePopoverTree","geometry","usePopoverPosition","useEffect","closeOnOutsidePress","event","source","popEscapeLayer","pushEscapeLayer","active","getDeepActiveElement","shown","shouldFocus","openWithFocus","isInside","containers","step","container","closeIfFocusLeft","hoverHandlers","triggerProps","ownTriggerRef","elementRef","composedTriggerRef","useMemo","composeRefs","openKey","clonedTrigger","React","chainHandler","fromPointer","panelStyle","MENU_VIEWPORT_PADDING_PX","jsxs","Fragment","createPortal","jsx","ShadowRootWrapper","overlayIdentity","Popover","PopoverHeader","PopoverAction","PopoverList","PopoverSection","PopoverEmpty"],"mappings":"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"}
|
|
@@ -1,37 +1,48 @@
|
|
|
1
|
-
import { MENU_SPECS as
|
|
1
|
+
import { MENU_SPECS as r } from "./floatingMenuGeometry.js";
|
|
2
2
|
import { injectStyleSheet as l } from "./injectStyleSheet.js";
|
|
3
|
-
import { ensureOverlayStyles as
|
|
4
|
-
import { SELECTED_ROW_BACKGROUND as
|
|
5
|
-
const
|
|
3
|
+
import { ensureOverlayStyles as s } from "./portalOverlay.js";
|
|
4
|
+
import { SELECTED_ROW_BACKGROUND as i, SELECTED_ROW_FOREGROUND as d } from "./selectedRow.js";
|
|
5
|
+
const y = {
|
|
6
6
|
small: { icon: 16, iconBox: "text-[16px] [&_svg]:size-[16px]!" },
|
|
7
7
|
xsmall: { icon: 11, iconBox: "text-[11px] [&_svg]:size-[11px]!" }
|
|
8
|
-
},
|
|
8
|
+
}, O = {
|
|
9
9
|
minWidth: 0,
|
|
10
10
|
overflow: "hidden",
|
|
11
11
|
textOverflow: "ellipsis"
|
|
12
|
-
},
|
|
12
|
+
}, m = {
|
|
13
13
|
padding: "6px 10px",
|
|
14
14
|
fontSize: "var(--malachite-font-size-small, 13px)",
|
|
15
15
|
color: "var(--malachite-text-dimmed, rgb(161, 161, 161))",
|
|
16
16
|
userSelect: "none"
|
|
17
|
-
},
|
|
18
|
-
...
|
|
17
|
+
}, u = {
|
|
18
|
+
...m,
|
|
19
19
|
color: "var(--malachite-text-secondary, rgb(123, 123, 123))",
|
|
20
20
|
overflow: "hidden",
|
|
21
21
|
textOverflow: "ellipsis",
|
|
22
22
|
whiteSpace: "nowrap"
|
|
23
|
-
},
|
|
24
|
-
|
|
25
|
-
flexShrink: 0,
|
|
23
|
+
}, h = { display: "inline-flex", flexShrink: 0 }, R = {
|
|
24
|
+
...h,
|
|
26
25
|
color: "var(--malachite-text-dimmed, rgb(161, 161, 161))"
|
|
27
|
-
}, a = "malachite-actionmenu-item",
|
|
26
|
+
}, n = "malachite-actionmenu-item-icon", a = "malachite-actionmenu-item", g = "data-malachite-actionmenu-tag", w = { display: "inline-flex", flexShrink: 0 }, I = { [g]: "" }, x = [
|
|
28
27
|
`.${a} { background: transparent; }`,
|
|
29
28
|
`.${a}:not(:disabled):hover { background: var(--malachite-card-dimmed-background, rgb(247, 247, 247)); }`,
|
|
30
29
|
`.${a}:focus-visible { outline: 2px solid var(--malachite-text-primary, rgb(10, 10, 10)); outline-offset: -2px; }`
|
|
31
|
-
],
|
|
30
|
+
], t = `.${a}[data-danger="hover"]`, c = `${t}:not(:disabled):is(:hover, :focus-visible)`, v = [
|
|
31
|
+
`${t} { color: var(--malachite-text-primary, rgb(10, 10, 10)); }`,
|
|
32
|
+
`${t}[data-tone="muted"] { color: var(--malachite-text-secondary, rgb(123, 123, 123)); }`,
|
|
33
|
+
`${t} > .${n} { color: var(--malachite-text-dimmed, rgb(161, 161, 161)); }`,
|
|
34
|
+
`${t}[aria-current] { color: ${d}; background: ${i}; }`,
|
|
35
|
+
`${c} { color: var(--malachite-text-destructive, rgb(237, 53, 54)); background: var(--malachite-error-background-glass, rgba(197, 63, 63, 0.08)); }`,
|
|
36
|
+
`${c} > .${n} { color: inherit; }`
|
|
37
|
+
], b = () => l("malachite-actionmenu-danger-hover-styles", v), A = () => (s(), b(), l("malachite-actionmenu-item-styles", x)), f = {
|
|
32
38
|
default: "var(--malachite-text-primary, rgb(10, 10, 10))",
|
|
33
39
|
muted: "var(--malachite-text-secondary, rgb(123, 123, 123))"
|
|
34
|
-
},
|
|
40
|
+
}, E = (e, o) => {
|
|
41
|
+
if (e.danger === !0)
|
|
42
|
+
return "var(--malachite-text-destructive, rgb(237, 53, 54))";
|
|
43
|
+
if (e.danger !== "hover")
|
|
44
|
+
return e.current ? d : f[o];
|
|
45
|
+
}, $ = (e, o = "default") => e.type === "text" ? u : e.type === "divider" ? {
|
|
35
46
|
height: 1,
|
|
36
47
|
background: "var(--malachite-card-border, rgb(232, 232, 232))",
|
|
37
48
|
margin: "4px 0"
|
|
@@ -43,8 +54,8 @@ const b = {
|
|
|
43
54
|
borderRadius: 8,
|
|
44
55
|
fontSize: "var(--malachite-font-size-small, 13px)",
|
|
45
56
|
fontWeight: 400,
|
|
46
|
-
color:
|
|
47
|
-
background: e.current ?
|
|
57
|
+
color: E(e, o),
|
|
58
|
+
background: e.current && e.danger !== "hover" ? i : void 0,
|
|
48
59
|
cursor: e.disabled ? "not-allowed" : "pointer",
|
|
49
60
|
opacity: e.disabled ? 0.5 : 1,
|
|
50
61
|
userSelect: "none",
|
|
@@ -53,18 +64,18 @@ const b = {
|
|
|
53
64
|
border: "none",
|
|
54
65
|
textAlign: "left",
|
|
55
66
|
width: "100%"
|
|
56
|
-
},
|
|
67
|
+
}, L = (e) => ({
|
|
57
68
|
position: "fixed",
|
|
58
69
|
top: (e == null ? void 0 : e.top) ?? 0,
|
|
59
70
|
left: (e == null ? void 0 : e.left) ?? 0,
|
|
60
71
|
width: e == null ? void 0 : e.width,
|
|
61
72
|
maxHeight: e == null ? void 0 : e.maxHeight,
|
|
62
73
|
overflowY: "auto",
|
|
63
|
-
background:
|
|
64
|
-
border:
|
|
65
|
-
borderRadius:
|
|
66
|
-
boxShadow:
|
|
67
|
-
padding:
|
|
74
|
+
background: r.options.background,
|
|
75
|
+
border: r.options.border,
|
|
76
|
+
borderRadius: r.options.radius,
|
|
77
|
+
boxShadow: r.options.shadow,
|
|
78
|
+
padding: r.options.padding,
|
|
68
79
|
display: "flex",
|
|
69
80
|
flexDirection: "column",
|
|
70
81
|
gap: 2,
|
|
@@ -72,17 +83,20 @@ const b = {
|
|
|
72
83
|
opacity: e ? 1 : 0
|
|
73
84
|
});
|
|
74
85
|
export {
|
|
75
|
-
|
|
86
|
+
m as EMPTY_ITEM_STYLE,
|
|
76
87
|
a as ITEM_CLASS,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
y as
|
|
86
|
-
|
|
88
|
+
n as ITEM_ICON_CLASS,
|
|
89
|
+
h as ITEM_ICON_LAYOUT,
|
|
90
|
+
R as ITEM_ICON_STYLE,
|
|
91
|
+
O as LABEL_STYLE,
|
|
92
|
+
g as TAG_HOST_ATTR,
|
|
93
|
+
I as TAG_HOST_PROPS,
|
|
94
|
+
w as TAG_HOST_STYLE,
|
|
95
|
+
u as TEXT_ITEM_STYLE,
|
|
96
|
+
y as TRIGGER_SIZES,
|
|
97
|
+
A as ensureActionMenuStyles,
|
|
98
|
+
b as ensureDangerOnHoverStyles,
|
|
99
|
+
$ as itemStyle,
|
|
100
|
+
L as menuStyle
|
|
87
101
|
};
|
|
88
102
|
//# sourceMappingURL=actionMenuStyles.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actionMenuStyles.js","sources":["../../src/components/actionMenuStyles.ts"],"sourcesContent":["import type React from 'react';\nimport type { ActionMenuItem } from './actionMenuTypes';\nimport { MENU_SPECS, type MenuGeometry } from './floatingMenuGeometry';\nimport { injectStyleSheet } from './injectStyleSheet';\nimport { ensureOverlayStyles } from './portalOverlay';\nimport { SELECTED_ROW_BACKGROUND, SELECTED_ROW_FOREGROUND } from './selectedRow';\n\nexport const TRIGGER_SIZES: Record<'small' | 'xsmall', { icon: number; iconBox: string }> = {\n small: { icon: 16, iconBox: 'text-[16px] [&_svg]:size-[16px]!' },\n xsmall: { icon: 11, iconBox: 'text-[11px] [&_svg]:size-[11px]!' },\n};\n\nexport const LABEL_STYLE: React.CSSProperties = {\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n\nexport const EMPTY_ITEM_STYLE: React.CSSProperties = {\n padding: '6px 10px',\n fontSize: 'var(--malachite-font-size-small, 13px)',\n color: 'var(--malachite-text-dimmed, rgb(161, 161, 161))',\n userSelect: 'none',\n};\n\nexport const TEXT_ITEM_STYLE: React.CSSProperties = {\n ...EMPTY_ITEM_STYLE,\n color: 'var(--malachite-text-secondary, rgb(123, 123, 123))',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n};\n\nexport const
|
|
1
|
+
{"version":3,"file":"actionMenuStyles.js","sources":["../../src/components/actionMenuStyles.ts"],"sourcesContent":["import type React from 'react';\nimport type { ActionMenuItem } from './actionMenuTypes';\nimport { MENU_SPECS, type MenuGeometry } from './floatingMenuGeometry';\nimport { injectStyleSheet } from './injectStyleSheet';\nimport { ensureOverlayStyles } from './portalOverlay';\nimport { SELECTED_ROW_BACKGROUND, SELECTED_ROW_FOREGROUND } from './selectedRow';\n\nexport const TRIGGER_SIZES: Record<'small' | 'xsmall', { icon: number; iconBox: string }> = {\n small: { icon: 16, iconBox: 'text-[16px] [&_svg]:size-[16px]!' },\n xsmall: { icon: 11, iconBox: 'text-[11px] [&_svg]:size-[11px]!' },\n};\n\nexport const LABEL_STYLE: React.CSSProperties = {\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n\nexport const EMPTY_ITEM_STYLE: React.CSSProperties = {\n padding: '6px 10px',\n fontSize: 'var(--malachite-font-size-small, 13px)',\n color: 'var(--malachite-text-dimmed, rgb(161, 161, 161))',\n userSelect: 'none',\n};\n\nexport const TEXT_ITEM_STYLE: React.CSSProperties = {\n ...EMPTY_ITEM_STYLE,\n color: 'var(--malachite-text-secondary, rgb(123, 123, 123))',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n};\n\nexport const ITEM_ICON_LAYOUT: React.CSSProperties = { display: 'inline-flex', flexShrink: 0 };\n\nexport const ITEM_ICON_STYLE: React.CSSProperties = {\n ...ITEM_ICON_LAYOUT,\n color: 'var(--malachite-text-dimmed, rgb(161, 161, 161))',\n};\n\nexport const ITEM_ICON_CLASS = 'malachite-actionmenu-item-icon';\n\nexport const ITEM_CLASS = 'malachite-actionmenu-item';\n\nexport const TAG_HOST_ATTR = 'data-malachite-actionmenu-tag' as const;\n\nexport const TAG_HOST_STYLE: React.CSSProperties = { display: 'inline-flex', flexShrink: 0 };\n\nexport const TAG_HOST_PROPS = { [TAG_HOST_ATTR]: '' };\n\nconst ITEM_RULES = [\n `.${ITEM_CLASS} { background: transparent; }`,\n `.${ITEM_CLASS}:not(:disabled):hover { background: var(--malachite-card-dimmed-background, rgb(247, 247, 247)); }`,\n `.${ITEM_CLASS}:focus-visible { outline: 2px solid var(--malachite-text-primary, rgb(10, 10, 10)); outline-offset: -2px; }`,\n];\n\nconst DANGER_ON_HOVER = `.${ITEM_CLASS}[data-danger=\"hover\"]`;\nconst DANGER_ON_HOVER_ACTIVE = `${DANGER_ON_HOVER}:not(:disabled):is(:hover, :focus-visible)`;\n\nconst DANGER_ON_HOVER_RULES = [\n `${DANGER_ON_HOVER} { color: var(--malachite-text-primary, rgb(10, 10, 10)); }`,\n `${DANGER_ON_HOVER}[data-tone=\"muted\"] { color: var(--malachite-text-secondary, rgb(123, 123, 123)); }`,\n `${DANGER_ON_HOVER} > .${ITEM_ICON_CLASS} { color: var(--malachite-text-dimmed, rgb(161, 161, 161)); }`,\n `${DANGER_ON_HOVER}[aria-current] { color: ${SELECTED_ROW_FOREGROUND}; background: ${SELECTED_ROW_BACKGROUND}; }`,\n `${DANGER_ON_HOVER_ACTIVE} { color: var(--malachite-text-destructive, rgb(237, 53, 54)); background: var(--malachite-error-background-glass, rgba(197, 63, 63, 0.08)); }`,\n `${DANGER_ON_HOVER_ACTIVE} > .${ITEM_ICON_CLASS} { color: inherit; }`,\n];\n\nexport const ensureDangerOnHoverStyles = (): CSSStyleSheet | null =>\n injectStyleSheet('malachite-actionmenu-danger-hover-styles', DANGER_ON_HOVER_RULES);\n\nexport const ensureActionMenuStyles = (): CSSStyleSheet | null => {\n ensureOverlayStyles();\n ensureDangerOnHoverStyles();\n return injectStyleSheet('malachite-actionmenu-item-styles', ITEM_RULES);\n};\n\nexport type ActionMenuTone = 'default' | 'muted';\n\nconst ITEM_TEXT: Record<ActionMenuTone, string> = {\n default: 'var(--malachite-text-primary, rgb(10, 10, 10))',\n muted: 'var(--malachite-text-secondary, rgb(123, 123, 123))',\n};\n\ntype ActionItem = Exclude<ActionMenuItem, { type: 'text' } | { type: 'divider' }>;\n\nconst actionItemColor = (item: ActionItem, tone: ActionMenuTone): string | undefined => {\n if (item.danger === true) {\n return 'var(--malachite-text-destructive, rgb(237, 53, 54))';\n }\n if (item.danger === 'hover') {\n return undefined;\n }\n if (item.current) {\n return SELECTED_ROW_FOREGROUND;\n }\n return ITEM_TEXT[tone];\n};\n\nexport const itemStyle = (\n item: ActionMenuItem,\n tone: ActionMenuTone = 'default',\n): React.CSSProperties => {\n if (item.type === 'text') {\n return TEXT_ITEM_STYLE;\n }\n if (item.type === 'divider') {\n return {\n height: 1,\n background: 'var(--malachite-card-border, rgb(232, 232, 232))',\n margin: '4px 0',\n };\n }\n return {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '6px 10px',\n borderRadius: 8,\n fontSize: 'var(--malachite-font-size-small, 13px)',\n fontWeight: 400,\n color: actionItemColor(item, tone),\n background: item.current && item.danger !== 'hover' ? SELECTED_ROW_BACKGROUND : undefined,\n cursor: item.disabled ? 'not-allowed' : 'pointer',\n opacity: item.disabled ? 0.5 : 1,\n userSelect: 'none',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n border: 'none',\n textAlign: 'left',\n width: '100%',\n };\n};\n\nexport const menuStyle = (position: MenuGeometry | null): React.CSSProperties => ({\n position: 'fixed',\n top: position?.top ?? 0,\n left: position?.left ?? 0,\n width: position?.width,\n maxHeight: position?.maxHeight,\n overflowY: 'auto',\n background: MENU_SPECS.options.background,\n border: MENU_SPECS.options.border,\n borderRadius: MENU_SPECS.options.radius,\n boxShadow: MENU_SPECS.options.shadow,\n padding: MENU_SPECS.options.padding,\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n visibility: position ? 'visible' : 'hidden',\n opacity: position ? 1 : 0,\n});\n"],"names":["TRIGGER_SIZES","LABEL_STYLE","EMPTY_ITEM_STYLE","TEXT_ITEM_STYLE","ITEM_ICON_LAYOUT","ITEM_ICON_STYLE","ITEM_ICON_CLASS","ITEM_CLASS","TAG_HOST_ATTR","TAG_HOST_STYLE","TAG_HOST_PROPS","ITEM_RULES","DANGER_ON_HOVER","DANGER_ON_HOVER_ACTIVE","DANGER_ON_HOVER_RULES","SELECTED_ROW_FOREGROUND","SELECTED_ROW_BACKGROUND","ensureDangerOnHoverStyles","injectStyleSheet","ensureActionMenuStyles","ensureOverlayStyles","ITEM_TEXT","actionItemColor","item","tone","itemStyle","menuStyle","position","MENU_SPECS"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -102,7 +102,8 @@ export declare type ActionMenuItem = {
|
|
|
102
102
|
label: default_2.ReactNode;
|
|
103
103
|
onClick?: () => void;
|
|
104
104
|
disabled?: boolean;
|
|
105
|
-
|
|
105
|
+
/** `'hover'` turns the item destructive only on hover or focus. */
|
|
106
|
+
danger?: boolean | 'hover';
|
|
106
107
|
tooltip?: string;
|
|
107
108
|
icon?: default_2.ReactNode;
|
|
108
109
|
/** Rendered after the label in its own styled host, e.g. a malachite `Tag`. Its text
|
package/package.json
CHANGED