@stackone/malachite 2.54.0 → 2.55.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/COMPONENTS.md +3 -3
- package/dist/components/ActionMenu.js +141 -134
- package/dist/components/ActionMenu.js.map +1 -1
- package/dist/components/PageLayout/regularLayout.js +58 -61
- package/dist/components/PageLayout/regularLayout.js.map +1 -1
- package/dist/components/Tag.js +22 -21
- package/dist/components/Tag.js.map +1 -1
- package/dist/components/Tooltip.js +97 -93
- package/dist/components/Tooltip.js.map +1 -1
- package/dist/components/actionMenuStyles.js +28 -19
- package/dist/components/actionMenuStyles.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/package.json +1 -1
package/COMPONENTS.md
CHANGED
|
@@ -38,14 +38,14 @@ The catalog of components exported from `@stackone/malachite`. **Reach for one o
|
|
|
38
38
|
- **`IconIndicator`** — A non-interactive icon that carries a meaning, such as a capability shown on a page that cannot switch it on. Same square as an icon-only `Button` at each `size`, so it lines up where a sibling view shows the button. Renders `role="img"`, named by `aria-label`; with a `tooltip` it takes focus so the tooltip is reachable by keyboard. Reach for it instead of a hand-sized `<span>` around an icon. _Key props:_ `icon`, `aria-label`, `tooltip`, `size`.
|
|
39
39
|
- **`StatusBadge`** — Large status indicator (e.g. success checkmark) for terminal/confirmation screens. _Key props:_ `type`.
|
|
40
40
|
- **`StatBadge`** — Pill showing a numeric value with a label (e.g. "3 accounts selected"). _Key props:_ `count`, `label`.
|
|
41
|
-
- **`Tag`** — Small status/label tag. Types: success, error, warning, default. _Key props:_ `value`, `type`, `paddingSize`, `textTransform`, `WidthSize`, `tooltip`, `
|
|
41
|
+
- **`Tag`** — Small status/label tag. Types: success, error, warning, default. _Key props:_ `value`, `type`, `paddingSize`, `textTransform`, `WidthSize`, `tooltip` (hover-only; wrap in `Tooltip focusableTrigger` for keyboard reach), `tooltipDescribesTag`, `onClick` (makes it a button, unless `onRemove` is set; keep `value`/`icon` non-interactive), `onRemove`.
|
|
42
42
|
- **`NumberTag`** — Compact numeric badge/counter.
|
|
43
43
|
- **`UsageCard`** — A small metered card: a title (optionally a link via `titleLink`, drawn with a trailing arrow), a right-aligned `detail` such as "700/1,000", and a body, usually a `Progress`. Used by `Sidebar.Usage`, and fine on its own, e.g. on a billing page. _Key props:_ `title`, `titleLink`, `detail`, `children`, `aria-label` (required; names the group), `busy` (sets `aria-busy` only).
|
|
44
44
|
- **`Progress`** — Progress bar.
|
|
45
45
|
- **`LoadingBar`** — Indeterminate full-width loading line; the variant colour sweeps across a pale track for work of unknown duration. Toggle `active` rather than unmounting, so the reserved height prevents layout shift. _Key props:_ `active`, `LoadingBarVariant` (`default` | `warning` | `error`), `LoadingBarSize` (`small` | `medium` | `large`), `aria-label`, `aria-labelledby`, `className`, `style`.
|
|
46
46
|
- **`Spinner`** — Loading spinner.
|
|
47
47
|
- **`Skeleton`** — Loading placeholders. Includes `Skeleton.Avatar` and `Skeleton.Input`.
|
|
48
|
-
- **`Tooltip`** — Hover/focus tooltip; also works on disabled controls (see CLAUDE.md). It describes its trigger once shown, so `describesTrigger={false}` where the same sentence is already on screen and referenced — otherwise a screen reader reads it twice. _Key props:_ `text`, `placement`, `delay`, `maxWidth`, `shouldShow`, `triggerAs`, `describesTrigger`, `isShrinkable`, `fill`, `wrapChildren`, `suppressed` (holds the tooltip back, e.g. while the popup its trigger opens is up), `showOnFocus` (false where script can move focus onto the trigger, e.g. an option a panel focuses on open).
|
|
48
|
+
- **`Tooltip`** — Hover/focus tooltip; also works on disabled controls (see CLAUDE.md). It describes its trigger once shown, so `describesTrigger={false}` where the same sentence is already on screen and referenced — otherwise a screen reader reads it twice. _Key props:_ `text`, `placement`, `delay`, `maxWidth`, `shouldShow`, `triggerAs`, `describesTrigger`, `isShrinkable`, `fill`, `wrapChildren`, `suppressed` (holds the tooltip back, e.g. while the popup its trigger opens is up), `showOnFocus` (false where script can move focus onto the trigger, e.g. an option a panel focuses on open), `focusableTrigger` (makes a trigger that cannot take focus — text, a static `Tag` — a tab stop, so the tooltip is not mouse-only; not for focusable children).
|
|
49
49
|
- **`TruncatedTooltip`** — Shows a tooltip only when its text is truncated.
|
|
50
50
|
- **`Confetti`** — Celebratory confetti burst for success moments. _Key props:_ `pieces`.
|
|
51
51
|
- **`ConfirmationDialog`** — Asks the customer to confirm one consequential action — a preset over `Modal`, not a second dialog implementation. `variant` picks a whole coherent set at once (badge fill, tray tint, caption colour, badge icon and confirm-button variant), so `destructive` cannot end up with a non-destructive confirm button; `destructive` also renders as `role="alertdialog"`, and `description` is wired to the dialog through `aria-describedby`, so the consequence is announced and not just the title. `confirmPhrase` gates the confirm action on typing that exact phrase, for a consequence severe enough that one click should not be able to cause it — the typed value is trimmed, since copying a name picks up a trailing space, but stays case-sensitive, and the field clears each time the dialog opens, being a per-decision gate rather than a value. Neither a destructive dialog nor one carrying a `confirmPhrase` dismisses on a scrim click, and `loading` suppresses the pointer routes — the scrim and the close button — as well as disabling the buttons. It never suppresses ESC: that is the only guaranteed keyboard exit from a focus trap, so it always reaches `onClose` and whether to abort is left to the caller. ESC reaches it whenever it is the topmost overlay, so it can be opened over a panel. `callout` renders an `Alert` in a full-width band at the top of the body — it scrolls with the body rather than being pinned under the header — and its `type` is chosen independently of `variant` — the frames pair a green badge with an amber callout. `titleAdornment` names the entity beside the title, `badgeIcon` overrides the variant’s glyph for an action-specific one, and `tip` is the `{ label?, message }` caption on the tray beneath the card, which `Modal` renders and describes. `children` is where extra body content goes. `onConfirm` does not close the dialog, so the caller can hold it open on `loading` while the request is in flight. `confirmPhraseLabel` also takes `{ prefix, suffix }`, which renders the phrase itself as a copyable token between the two — the gate is case-sensitive and space-exact, so a customer who has identified the right entity should not be locked out by a glyph they cannot tell apart. The copy control sits outside the element that names the field, so “Copy…” never lands in the field’s accessible name; `confirmPhraseCopyLabel` names it and `onPhraseCopied` reports a completed write with the phrase it copied. A rejected write says so in the field rather than failing silently, and the control is disabled where the Clipboard API is unavailable. A near miss is named rather than left to a disabled button: typing the phrase with the wrong case or the wrong spacing says which, once the whole phrase is there, and that message is the field’s `aria-describedby`. _Key props:_ `open`, `onClose`, `onConfirm`, `title`, `description`, `titleAdornment`, `badgeIcon`, `variant` (`default` | `warning` | `destructive` | `success`), `callout`, `confirmPhrase`, `confirmPhraseLabel`, `confirmPhraseCopyLabel`, `confirmPhrasePlaceholder`, `onPhraseCopied`, `confirmLabel`, `cancelLabel`, `loading`, `tip`, `size`, `aria-label`, `aria-labelledby`.
|
|
@@ -59,7 +59,7 @@ The catalog of components exported from `@stackone/malachite`. **Reach for one o
|
|
|
59
59
|
- **`PreviewTreeRow`** — A single labelled row within a PreviewTree. _Key props:_ `label`.
|
|
60
60
|
- **`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`.
|
|
61
61
|
- **`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`.
|
|
62
|
-
- **`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 an `href` is a link: Cmd/Ctrl-click or a middle click opens it in a new tab and closes the menu, while a plain click runs `onClick` in its place when one is given (so a router can navigate in-app); Enter and Space both activate it. 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
|
+
- **`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 an `href` is a link: Cmd/Ctrl-click or a middle click opens it in a new tab and closes the menu, while a plain click runs `onClick` in its place when one is given (so a router can navigate in-app); Enter and Space both activate it. `target: '_blank'` opens it in a new tab, adds `noopener noreferrer` and tells screen readers it opens a new tab. 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.
|
|
63
63
|
- **`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`.
|
|
64
64
|
- **`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, and always on a touch screen, which has no hover to reveal it. A workspace, organization or account name cut off in the expanded sidebar shows in full on hover. Notices, the divider and the usage card hide while collapsed. Render it inside `<MalachiteContext layer="sidebar">`, or the page paints over the edge toggle. _Key props:_ `collapsed`, `onToggleCollapse`, `expandLabel`, `collapseLabel`, `aria-label`; `Sidebar.Switcher`: `logo`, `title`, `subtitle`, `titleTooltip`/`subtitleTooltip` (shown when the name is cut off, the name by default; pass a node to wrap it, e.g. to keep it out of session recordings) + button attributes; `Sidebar.Account`: `avatar` or `initials` (a small chip when there is no photo), `name`, `tooltip` (on the collapsed rail, and expanded when the name is cut off; `name` by default; pass a node to wrap it) + 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; `SIDEBAR_TOOLTIP_DELAY_S` is the 0.2s delay its name and rail tooltips use, for tooltips a consumer adds inside it.
|
|
65
65
|
- **`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,193 +1,200 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import
|
|
3
|
-
import { createPortal as
|
|
4
|
-
import { Button as
|
|
5
|
-
import
|
|
6
|
-
import { focusEdge as x, edgeForTriggerKey as
|
|
7
|
-
import { TRIGGER_SIZES as
|
|
8
|
-
import { getDeepActiveElement as
|
|
9
|
-
import { elementRef as ce, composeRefs as
|
|
10
|
-
import { pushEscapeLayer as
|
|
11
|
-
import { isPlainClick as
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
return /* @__PURE__ */ o("div", {
|
|
20
|
-
|
|
1
|
+
import { jsxs as E, jsx as o, Fragment as N } from "react/jsx-runtime";
|
|
2
|
+
import _, { useState as z, useRef as v, useEffect as G, useMemo as U } from "react";
|
|
3
|
+
import { createPortal as $ } from "react-dom";
|
|
4
|
+
import { Button as H } from "./Button.js";
|
|
5
|
+
import V from "./CustomIcons/MoreHorizontalIcon.js";
|
|
6
|
+
import { focusEdge as x, edgeForTriggerKey as X, itemForMenuKey as Z, enabledItems as q } from "./actionMenuKeyboard.js";
|
|
7
|
+
import { TRIGGER_SIZES as J, EMPTY_ITEM_STYLE as Q, menuStyle as W, itemStyle as A, TEXT_ITEM_STYLE as ee, ITEM_ICON_LAYOUT as ne, ITEM_ICON_STYLE as re, ITEM_ICON_CLASS as oe, LABEL_STYLE as te, VISUALLY_HIDDEN_STYLE as ae, ITEM_CLASS as le } from "./actionMenuStyles.js";
|
|
8
|
+
import { getDeepActiveElement as se } from "./activeElement.js";
|
|
9
|
+
import { elementRef as ce, composeRefs as ie, chainHandler as S } from "./composeRefs.js";
|
|
10
|
+
import { pushEscapeLayer as ue } from "./escapeLayers.js";
|
|
11
|
+
import { isPlainClick as de } from "./linkClick.js";
|
|
12
|
+
import { resolveRel as fe } from "./linkRel.js";
|
|
13
|
+
import { usePopupLayer as pe } from "./overlayLayers.js";
|
|
14
|
+
import { safeHref as ye } from "./safeHref.js";
|
|
15
|
+
import { useActionMenuPosition as me } from "./useActionMenuPosition.js";
|
|
16
|
+
const he = _.lazy(() => import("./ActionMenuTagHost.js")), ge = (e) => e != null && e !== !1, I = (e) => e.stopPropagation(), Ee = ({ item: e, tone: d, onSelect: b, onFollow: M }) => {
|
|
17
|
+
var h;
|
|
18
|
+
if (e.type === "divider")
|
|
19
|
+
return /* @__PURE__ */ o("div", { style: A(e) });
|
|
20
|
+
if (e.type === "text")
|
|
21
|
+
return /* @__PURE__ */ o("div", { role: "menuitem", "aria-disabled": "true", style: ee, children: e.label });
|
|
22
|
+
const m = {
|
|
21
23
|
role: "menuitem",
|
|
22
|
-
title:
|
|
23
|
-
"aria-current":
|
|
24
|
-
className:
|
|
25
|
-
"data-danger":
|
|
26
|
-
"data-tone":
|
|
27
|
-
style:
|
|
28
|
-
},
|
|
29
|
-
|
|
24
|
+
title: e.tooltip,
|
|
25
|
+
"aria-current": e.current ? "page" : void 0,
|
|
26
|
+
className: le,
|
|
27
|
+
"data-danger": e.danger === "hover" ? "hover" : void 0,
|
|
28
|
+
"data-tone": d,
|
|
29
|
+
style: A(e, d)
|
|
30
|
+
}, c = e.disabled ? void 0 : ye(e.href), T = c !== void 0 && ((h = e.target) == null ? void 0 : h.toLowerCase()) === "_blank", t = /* @__PURE__ */ E(N, { children: [
|
|
31
|
+
e.icon && /* @__PURE__ */ o(
|
|
30
32
|
"span",
|
|
31
33
|
{
|
|
32
34
|
className: oe,
|
|
33
|
-
style:
|
|
34
|
-
children:
|
|
35
|
+
style: e.danger === "hover" ? ne : re,
|
|
36
|
+
children: e.icon
|
|
35
37
|
}
|
|
36
38
|
),
|
|
37
|
-
/* @__PURE__ */
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
/* @__PURE__ */ E("span", { style: te, children: [
|
|
40
|
+
e.label,
|
|
41
|
+
T && /* @__PURE__ */ o("span", { style: ae, children: " (opens in a new tab)" })
|
|
42
|
+
] }),
|
|
43
|
+
ge(e.tag) ? /* @__PURE__ */ o(_.Suspense, { fallback: null, children: /* @__PURE__ */ o(he, { children: e.tag }) }) : null
|
|
44
|
+
] });
|
|
45
|
+
return c !== void 0 ? /* @__PURE__ */ o(
|
|
41
46
|
"a",
|
|
42
47
|
{
|
|
43
|
-
...
|
|
44
|
-
href:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
...m,
|
|
49
|
+
href: c,
|
|
50
|
+
target: e.target,
|
|
51
|
+
rel: fe(void 0, e.target),
|
|
52
|
+
onClick: (f) => {
|
|
53
|
+
if (de(f) && e.onClick) {
|
|
54
|
+
f.preventDefault(), b(e);
|
|
48
55
|
return;
|
|
49
56
|
}
|
|
50
|
-
|
|
57
|
+
M();
|
|
51
58
|
},
|
|
52
|
-
onAuxClick: (
|
|
53
|
-
|
|
59
|
+
onAuxClick: (f) => {
|
|
60
|
+
f.button === 1 && M();
|
|
54
61
|
},
|
|
55
|
-
children:
|
|
62
|
+
children: t
|
|
56
63
|
}
|
|
57
|
-
) : /* @__PURE__ */ o("button", { ...
|
|
58
|
-
},
|
|
59
|
-
items:
|
|
60
|
-
label:
|
|
61
|
-
triggerIcon:
|
|
62
|
-
minWidth:
|
|
63
|
-
size:
|
|
64
|
-
showLabel:
|
|
65
|
-
variant:
|
|
64
|
+
) : /* @__PURE__ */ o("button", { ...m, type: "button", disabled: e.disabled, onClick: () => b(e), children: t });
|
|
65
|
+
}, Oe = ({
|
|
66
|
+
items: e,
|
|
67
|
+
label: d = "More actions",
|
|
68
|
+
triggerIcon: b,
|
|
69
|
+
minWidth: M,
|
|
70
|
+
size: m = "small",
|
|
71
|
+
showLabel: c = !1,
|
|
72
|
+
variant: T = "outline",
|
|
66
73
|
trigger: t,
|
|
67
|
-
placement:
|
|
68
|
-
offset:
|
|
69
|
-
tone:
|
|
74
|
+
placement: h = "bottom-end",
|
|
75
|
+
offset: f,
|
|
76
|
+
tone: O = "default"
|
|
70
77
|
}) => {
|
|
71
|
-
const
|
|
72
|
-
minWidth:
|
|
73
|
-
placement:
|
|
74
|
-
offset:
|
|
78
|
+
const D = J[m], [a, i] = z(!1), p = v(null), u = v(null), g = v(null), y = v(!1), P = pe("actionmenu", a, u), K = me(a, p, u, {
|
|
79
|
+
minWidth: M,
|
|
80
|
+
placement: h,
|
|
81
|
+
offset: f
|
|
75
82
|
});
|
|
76
83
|
G(() => {
|
|
77
84
|
if (!a)
|
|
78
85
|
return;
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
[
|
|
83
|
-
(
|
|
84
|
-
) || (
|
|
86
|
+
g.current && (x(u.current, g.current), g.current = null);
|
|
87
|
+
const n = (s) => {
|
|
88
|
+
const k = s.composedPath();
|
|
89
|
+
[p.current, u.current].some(
|
|
90
|
+
(R) => R && k.includes(R)
|
|
91
|
+
) || (y.current = !1, i(!1));
|
|
85
92
|
};
|
|
86
|
-
document.addEventListener("mousedown",
|
|
87
|
-
const r =
|
|
93
|
+
document.addEventListener("mousedown", n, !0);
|
|
94
|
+
const r = ue(() => i(!1));
|
|
88
95
|
return () => {
|
|
89
|
-
var
|
|
90
|
-
document.removeEventListener("mousedown",
|
|
91
|
-
const
|
|
92
|
-
|
|
96
|
+
var k;
|
|
97
|
+
document.removeEventListener("mousedown", n, !0), r();
|
|
98
|
+
const s = se();
|
|
99
|
+
y.current && (!s || s === document.body) && ((k = p.current) == null || k.focus({ preventScroll: !0 })), y.current = !1;
|
|
93
100
|
};
|
|
94
101
|
}, [a]);
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
const r = !a &&
|
|
98
|
-
|
|
99
|
-
},
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
+
const C = (n) => {
|
|
103
|
+
n.stopPropagation();
|
|
104
|
+
const r = !a && n.detail === 0;
|
|
105
|
+
g.current = r ? "first" : null, i((s) => !s);
|
|
106
|
+
}, L = (n) => {
|
|
107
|
+
if (n.key === "Tab" && a) {
|
|
108
|
+
i(!1);
|
|
102
109
|
return;
|
|
103
110
|
}
|
|
104
|
-
const r =
|
|
111
|
+
const r = X(n.key, h);
|
|
105
112
|
if (r) {
|
|
106
|
-
if (
|
|
107
|
-
x(
|
|
113
|
+
if (n.preventDefault(), a) {
|
|
114
|
+
x(u.current, r);
|
|
108
115
|
return;
|
|
109
116
|
}
|
|
110
|
-
|
|
117
|
+
g.current = r, i(!0);
|
|
111
118
|
}
|
|
112
|
-
},
|
|
113
|
-
var
|
|
114
|
-
if (
|
|
115
|
-
|
|
119
|
+
}, Y = (n) => {
|
|
120
|
+
var s;
|
|
121
|
+
if (n.key === "Tab") {
|
|
122
|
+
y.current = !1, (s = p.current) == null || s.focus({ preventScroll: !0 }), i(!1);
|
|
116
123
|
return;
|
|
117
124
|
}
|
|
118
|
-
if (
|
|
119
|
-
|
|
125
|
+
if (n.key === " " && n.target instanceof HTMLAnchorElement) {
|
|
126
|
+
n.preventDefault(), n.target.click();
|
|
120
127
|
return;
|
|
121
128
|
}
|
|
122
|
-
const r =
|
|
123
|
-
r && (
|
|
124
|
-
},
|
|
129
|
+
const r = Z(q(u.current), n.target, n.key);
|
|
130
|
+
r && (n.preventDefault(), r.focus());
|
|
131
|
+
}, F = (n) => {
|
|
125
132
|
var r;
|
|
126
|
-
|
|
127
|
-
},
|
|
128
|
-
() =>
|
|
129
|
-
[
|
|
130
|
-
), l = t == null ? void 0 : t.props, j = t ?
|
|
133
|
+
n.type === "divider" || n.type === "text" || n.disabled || ((r = n.onClick) == null || r.call(n), i(!1));
|
|
134
|
+
}, w = t ? ce(t) : void 0, B = U(
|
|
135
|
+
() => ie(w, p),
|
|
136
|
+
[w]
|
|
137
|
+
), l = t == null ? void 0 : t.props, j = t ? _.cloneElement(t, {
|
|
131
138
|
ref: B,
|
|
132
139
|
"aria-haspopup": "menu",
|
|
133
140
|
"aria-expanded": a,
|
|
134
|
-
onClick:
|
|
135
|
-
onMouseDown:
|
|
136
|
-
onKeyDown:
|
|
137
|
-
}) : /* @__PURE__ */
|
|
138
|
-
|
|
141
|
+
onClick: S(l == null ? void 0 : l.onClick, C),
|
|
142
|
+
onMouseDown: S(l == null ? void 0 : l.onMouseDown, I),
|
|
143
|
+
onKeyDown: S(l == null ? void 0 : l.onKeyDown, L)
|
|
144
|
+
}) : /* @__PURE__ */ E(
|
|
145
|
+
H,
|
|
139
146
|
{
|
|
140
|
-
ref:
|
|
147
|
+
ref: p,
|
|
141
148
|
type: "button",
|
|
142
|
-
variant:
|
|
143
|
-
size:
|
|
144
|
-
iconOnly: !
|
|
145
|
-
"aria-label":
|
|
149
|
+
variant: T,
|
|
150
|
+
size: m,
|
|
151
|
+
iconOnly: !c,
|
|
152
|
+
"aria-label": c ? void 0 : d,
|
|
146
153
|
"aria-haspopup": "menu",
|
|
147
154
|
"aria-expanded": a,
|
|
148
|
-
className: `shrink-0${
|
|
149
|
-
onClick:
|
|
150
|
-
onMouseDown:
|
|
151
|
-
onKeyDown:
|
|
155
|
+
className: `shrink-0${c ? " gap-2" : ""}`,
|
|
156
|
+
onClick: C,
|
|
157
|
+
onMouseDown: I,
|
|
158
|
+
onKeyDown: L,
|
|
152
159
|
children: [
|
|
153
|
-
/* @__PURE__ */ o("span", { className: `inline-flex items-center justify-center ${
|
|
154
|
-
|
|
160
|
+
/* @__PURE__ */ o("span", { className: `inline-flex items-center justify-center ${D.iconBox}`, children: b ?? /* @__PURE__ */ o(V, { size: D.icon }) }),
|
|
161
|
+
c && /* @__PURE__ */ o("span", { children: d })
|
|
155
162
|
]
|
|
156
163
|
}
|
|
157
164
|
);
|
|
158
|
-
return /* @__PURE__ */
|
|
165
|
+
return /* @__PURE__ */ E(N, { children: [
|
|
159
166
|
j,
|
|
160
|
-
a &&
|
|
161
|
-
/* @__PURE__ */
|
|
167
|
+
a && $(
|
|
168
|
+
/* @__PURE__ */ E(
|
|
162
169
|
"div",
|
|
163
170
|
{
|
|
164
|
-
ref:
|
|
171
|
+
ref: u,
|
|
165
172
|
role: "menu",
|
|
166
173
|
tabIndex: -1,
|
|
167
|
-
"aria-label":
|
|
168
|
-
...
|
|
169
|
-
style: W(
|
|
170
|
-
onMouseDown:
|
|
171
|
-
onClick:
|
|
172
|
-
onKeyDown:
|
|
174
|
+
"aria-label": d,
|
|
175
|
+
...P,
|
|
176
|
+
style: W(K),
|
|
177
|
+
onMouseDown: I,
|
|
178
|
+
onClick: I,
|
|
179
|
+
onKeyDown: Y,
|
|
173
180
|
onFocus: () => {
|
|
174
|
-
|
|
181
|
+
y.current = !0;
|
|
175
182
|
},
|
|
176
|
-
onBlur: (
|
|
183
|
+
onBlur: (n) => {
|
|
177
184
|
var r;
|
|
178
|
-
(r =
|
|
185
|
+
(r = u.current) != null && r.contains(n.relatedTarget) || (y.current = !1);
|
|
179
186
|
},
|
|
180
187
|
children: [
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
188
|
+
e.length === 0 && /* @__PURE__ */ o("div", { role: "menuitem", "aria-disabled": "true", style: Q, children: "No actions available" }),
|
|
189
|
+
e.map((n) => /* @__PURE__ */ o(
|
|
190
|
+
Ee,
|
|
184
191
|
{
|
|
185
|
-
item:
|
|
186
|
-
tone:
|
|
187
|
-
onSelect:
|
|
188
|
-
onFollow: () =>
|
|
192
|
+
item: n,
|
|
193
|
+
tone: O,
|
|
194
|
+
onSelect: F,
|
|
195
|
+
onFollow: () => i(!1)
|
|
189
196
|
},
|
|
190
|
-
|
|
197
|
+
n.key
|
|
191
198
|
))
|
|
192
199
|
]
|
|
193
200
|
}
|
|
@@ -197,6 +204,6 @@ const ye = v.lazy(() => import("./ActionMenuTagHost.js")), me = (n) => n != null
|
|
|
197
204
|
] });
|
|
198
205
|
};
|
|
199
206
|
export {
|
|
200
|
-
|
|
207
|
+
Oe as default
|
|
201
208
|
};
|
|
202
209
|
//# 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 type ActionMenuTone,\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 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 { isPlainClick } from './linkClick';\nimport { usePopupLayer } from './overlayLayers';\nimport { safeHref } from './safeHref';\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 onFollow: () => void;\n}> = ({ item, tone, onSelect, onFollow }) => {\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 const shared = {\n role: 'menuitem',\n title: item.tooltip,\n 'aria-current': item.current ? ('page' as const) : undefined,\n className: ITEM_CLASS,\n 'data-danger': item.danger === 'hover' ? 'hover' : undefined,\n 'data-tone': tone,\n style: itemStyle(item, tone),\n };\n const content = (\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 </>\n );\n const href = item.disabled ? undefined : safeHref(item.href);\n if (href !== undefined) {\n return (\n <a\n {...shared}\n href={href}\n onClick={(event) => {\n if (isPlainClick(event) && item.onClick) {\n event.preventDefault();\n onSelect(item);\n return;\n }\n onFollow();\n }}\n onAuxClick={(event) => {\n if (event.button === 1) {\n onFollow();\n }\n }}\n >\n {content}\n </a>\n );\n }\n return (\n <button {...shared} type=\"button\" disabled={item.disabled} onClick={() => onSelect(item)}>\n {content}\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 const popupIdentity = usePopupLayer('actionmenu', open, menuRef);\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 if (event.key === ' ' && event.target instanceof HTMLAnchorElement) {\n event.preventDefault();\n event.target.click();\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 {...popupIdentity}\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\n key={item.key}\n item={item}\n tone={tone}\n onSelect={selectItem}\n onFollow={() => setOpen(false)}\n />\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","onFollow","jsx","itemStyle","TEXT_ITEM_STYLE","shared","ITEM_CLASS","content","jsxs","Fragment","ITEM_ICON_CLASS","ITEM_ICON_LAYOUT","ITEM_ICON_STYLE","LABEL_STYLE","href","safeHref","isPlainClick","ActionMenu","items","label","triggerIcon","minWidth","size","showLabel","variant","customTrigger","placement","offset","triggerSize","TRIGGER_SIZES","open","setOpen","useState","triggerRef","useRef","menuRef","focusOnOpen","focusInMenu","popupIdentity","usePopupLayer","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","createPortal","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 type ActionMenuTone,\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 VISUALLY_HIDDEN_STYLE,\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 { isPlainClick } from './linkClick';\nimport { resolveRel } from './linkRel';\nimport { usePopupLayer } from './overlayLayers';\nimport { safeHref } from './safeHref';\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 onFollow: () => void;\n}> = ({ item, tone, onSelect, onFollow }) => {\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 const shared = {\n role: 'menuitem',\n title: item.tooltip,\n 'aria-current': item.current ? ('page' as const) : undefined,\n className: ITEM_CLASS,\n 'data-danger': item.danger === 'hover' ? 'hover' : undefined,\n 'data-tone': tone,\n style: itemStyle(item, tone),\n };\n const href = item.disabled ? undefined : safeHref(item.href);\n const opensNewTab = href !== undefined && item.target?.toLowerCase() === '_blank';\n const content = (\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}>\n {item.label}\n {opensNewTab && <span style={VISUALLY_HIDDEN_STYLE}> (opens in a new tab)</span>}\n </span>\n {hasTag(item.tag) ? (\n <React.Suspense fallback={null}>\n <ActionMenuTagHost>{item.tag}</ActionMenuTagHost>\n </React.Suspense>\n ) : null}\n </>\n );\n if (href !== undefined) {\n return (\n <a\n {...shared}\n href={href}\n target={item.target}\n rel={resolveRel(undefined, item.target)}\n onClick={(event) => {\n if (isPlainClick(event) && item.onClick) {\n event.preventDefault();\n onSelect(item);\n return;\n }\n onFollow();\n }}\n onAuxClick={(event) => {\n if (event.button === 1) {\n onFollow();\n }\n }}\n >\n {content}\n </a>\n );\n }\n return (\n <button {...shared} type=\"button\" disabled={item.disabled} onClick={() => onSelect(item)}>\n {content}\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 const popupIdentity = usePopupLayer('actionmenu', open, menuRef);\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 if (event.key === ' ' && event.target instanceof HTMLAnchorElement) {\n event.preventDefault();\n event.target.click();\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 {...popupIdentity}\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\n key={item.key}\n item={item}\n tone={tone}\n onSelect={selectItem}\n onFollow={() => setOpen(false)}\n />\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","onFollow","jsx","itemStyle","TEXT_ITEM_STYLE","shared","ITEM_CLASS","href","safeHref","opensNewTab","_a","content","jsxs","Fragment","ITEM_ICON_CLASS","ITEM_ICON_LAYOUT","ITEM_ICON_STYLE","LABEL_STYLE","VISUALLY_HIDDEN_STYLE","resolveRel","isPlainClick","ActionMenu","items","label","triggerIcon","minWidth","size","showLabel","variant","customTrigger","placement","offset","triggerSize","TRIGGER_SIZES","open","setOpen","useState","triggerRef","useRef","menuRef","focusOnOpen","focusInMenu","popupIdentity","usePopupLayer","position","useActionMenuPosition","useEffect","focusEdge","closeOnOutsidePress","path","node","popEscapeLayer","pushEscapeLayer","active","getDeepActiveElement","toggleFromTrigger","openedFromKeyboard","v","handleTriggerKeyDown","edge","edgeForTriggerKey","handleMenuKeyDown","target","itemForMenuKey","enabledItems","selectItem","ownTriggerRef","elementRef","composedTriggerRef","useMemo","composeRefs","customHandlers","triggerElement","chainHandler","Button","MoreHorizontalIcon","createPortal","menuStyle","e","EMPTY_ITEM_STYLE"],"mappings":";;;;;;;;;;;;;;;AAoEA,MAAMA,KAAoBC,EAAM,KAAK,MAAM,OAAO,wBAAqB,CAAC,GAElEC,KAAS,CAACC,MAA8CA,KAAQ,QAAQA,MAAQ,IAEhFC,IAAkB,CAACC,MAAgCA,EAAM,gBAAA,GAEzDC,KAKD,CAAC,EAAE,MAAAC,GAAM,MAAAC,GAAM,UAAAC,GAAU,UAAAC,QAAe;;AACzC,MAAIH,EAAK,SAAS;AACd,WAAO,gBAAAI,EAAC,OAAA,EAAI,OAAOC,EAAUL,CAAI,GAAG;AAExC,MAAIA,EAAK,SAAS;AACd,WACI,gBAAAI,EAAC,SAAI,MAAK,YAAW,iBAAc,QAAO,OAAOE,IAC5C,UAAAN,EAAK,MAAA,CACV;AAGR,QAAMO,IAAS;AAAA,IACX,MAAM;AAAA,IACN,OAAOP,EAAK;AAAA,IACZ,gBAAgBA,EAAK,UAAW,SAAmB;AAAA,IACnD,WAAWQ;AAAA,IACX,eAAeR,EAAK,WAAW,UAAU,UAAU;AAAA,IACnD,aAAaC;AAAA,IACb,OAAOI,EAAUL,GAAMC,CAAI;AAAA,EAAA,GAEzBQ,IAAOT,EAAK,WAAW,SAAYU,GAASV,EAAK,IAAI,GACrDW,IAAcF,MAAS,YAAaG,IAAAZ,EAAK,WAAL,gBAAAY,EAAa,mBAAkB,UACnEC,IACF,gBAAAC,EAAAC,GAAA,EACK,UAAA;AAAA,IAAAf,EAAK,QACF,gBAAAI;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,WAAWY;AAAA,QACX,OAAOhB,EAAK,WAAW,UAAUiB,KAAmBC;AAAA,QAEnD,UAAAlB,EAAK;AAAA,MAAA;AAAA,IAAA;AAAA,IAGd,gBAAAc,EAAC,QAAA,EAAK,OAAOK,IACR,UAAA;AAAA,MAAAnB,EAAK;AAAA,MACLW,KAAe,gBAAAP,EAAC,QAAA,EAAK,OAAOgB,IAAuB,UAAA,wBAAA,CAAqB;AAAA,IAAA,GAC7E;AAAA,IACCzB,GAAOK,EAAK,GAAG,sBACXN,EAAM,UAAN,EAAe,UAAU,MACtB,UAAA,gBAAAU,EAACX,IAAA,EAAmB,UAAAO,EAAK,IAAA,CAAI,GACjC,IACA;AAAA,EAAA,GACR;AAEJ,SAAIS,MAAS,SAEL,gBAAAL;AAAA,IAAC;AAAA,IAAA;AAAA,MACI,GAAGG;AAAA,MACJ,MAAAE;AAAA,MACA,QAAQT,EAAK;AAAA,MACb,KAAKqB,GAAW,QAAWrB,EAAK,MAAM;AAAA,MACtC,SAAS,CAACF,MAAU;AAChB,YAAIwB,GAAaxB,CAAK,KAAKE,EAAK,SAAS;AACrC,UAAAF,EAAM,eAAA,GACNI,EAASF,CAAI;AACb;AAAA,QACJ;AACA,QAAAG,EAAA;AAAA,MACJ;AAAA,MACA,YAAY,CAACL,MAAU;AACnB,QAAIA,EAAM,WAAW,KACjBK,EAAA;AAAA,MAER;AAAA,MAEC,UAAAU;AAAA,IAAA;AAAA,EAAA,IAKT,gBAAAT,EAAC,UAAA,EAAQ,GAAGG,GAAQ,MAAK,UAAS,UAAUP,EAAK,UAAU,SAAS,MAAME,EAASF,CAAI,GAClF,UAAAa,GACL;AAER,GAOMU,KAAwC,CAAC;AAAA,EAC3C,OAAAC;AAAA,EACA,OAAAC,IAAQ;AAAA,EACR,aAAAC;AAAA,EACA,UAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,WAAAC,IAAY;AAAA,EACZ,SAAAC,IAAU;AAAA,EACV,SAASC;AAAA,EACT,WAAAC,IAAY;AAAA,EACZ,QAAAC;AAAA,EACA,MAAAhC,IAAO;AACX,MAAM;AACF,QAAMiC,IAAcC,EAAcP,CAAI,GAChC,CAACQ,GAAMC,CAAO,IAAIC,EAAS,EAAK,GAChCC,IAAaC,EAA2B,IAAI,GAC5CC,IAAUD,EAAuB,IAAI,GACrCE,IAAcF,EAAwB,IAAI,GAC1CG,IAAcH,EAAO,EAAK,GAC1BI,IAAgBC,GAAc,cAAcT,GAAMK,CAAO,GAEzDK,IAAWC,GAAsBX,GAAMG,GAAYE,GAAS;AAAA,IAC9D,UAAAd;AAAA,IACA,WAAAK;AAAA,IACA,QAAAC;AAAA,EAAA,CACH;AAED,EAAAe,EAAU,MAAM;AACZ,QAAI,CAACZ;AACD;AAEJ,IAAIM,EAAY,YACZO,EAAUR,EAAQ,SAASC,EAAY,OAAO,GAC9CA,EAAY,UAAU;AAE1B,UAAMQ,IAAsB,CAACpD,MAAsB;AAC/C,YAAMqD,IAAOrD,EAAM,aAAA;AAInB,MAHe,CAACyC,EAAW,SAASE,EAAQ,OAAO,EAAE;AAAA,QACjD,CAACW,MAASA,KAAQD,EAAK,SAASC,CAAI;AAAA,MAAA,MAGpCT,EAAY,UAAU,IACtBN,EAAQ,EAAK;AAAA,IAErB;AACA,aAAS,iBAAiB,aAAaa,GAAqB,EAAI;AAChE,UAAMG,IAAiBC,GAAgB,MAAMjB,EAAQ,EAAK,CAAC;AAC3D,WAAO,MAAM;;AACT,eAAS,oBAAoB,aAAaa,GAAqB,EAAI,GACnEG,EAAA;AACA,YAAME,IAASC,GAAA;AACf,MAAIb,EAAY,YAAY,CAACY,KAAUA,MAAW,SAAS,WACvD3C,IAAA2B,EAAW,YAAX,QAAA3B,EAAoB,MAAM,EAAE,eAAe,QAE/C+B,EAAY,UAAU;AAAA,IAC1B;AAAA,EACJ,GAAG,CAACP,CAAI,CAAC;AAET,QAAMqB,IAAoB,CAAC3D,MAA4B;AACnD,IAAAA,EAAM,gBAAA;AACN,UAAM4D,IAAqB,CAACtB,KAAQtC,EAAM,WAAW;AACrD,IAAA4C,EAAY,UAAUgB,IAAqB,UAAU,MACrDrB,EAAQ,CAACsB,MAAM,CAACA,CAAC;AAAA,EACrB,GAEMC,IAAuB,CAAC9D,MAA+B;AACzD,QAAIA,EAAM,QAAQ,SAASsC,GAAM;AAC7B,MAAAC,EAAQ,EAAK;AACb;AAAA,IACJ;AACA,UAAMwB,IAAOC,EAAkBhE,EAAM,KAAKkC,CAAS;AACnD,QAAK6B,GAIL;AAAA,UADA/D,EAAM,eAAA,GACFsC,GAAM;AACN,QAAAa,EAAUR,EAAQ,SAASoB,CAAI;AAC/B;AAAA,MACJ;AACA,MAAAnB,EAAY,UAAUmB,GACtBxB,EAAQ,EAAI;AAAA;AAAA,EAChB,GAEM0B,IAAoB,CAACjE,MAA+C;;AACtE,QAAIA,EAAM,QAAQ,OAAO;AACrB,MAAA6C,EAAY,UAAU,KACtB/B,IAAA2B,EAAW,YAAX,QAAA3B,EAAoB,MAAM,EAAE,eAAe,OAC3CyB,EAAQ,EAAK;AACb;AAAA,IACJ;AACA,QAAIvC,EAAM,QAAQ,OAAOA,EAAM,kBAAkB,mBAAmB;AAChE,MAAAA,EAAM,eAAA,GACNA,EAAM,OAAO,MAAA;AACb;AAAA,IACJ;AACA,UAAMkE,IAASC,EAAeC,EAAazB,EAAQ,OAAO,GAAG3C,EAAM,QAAQA,EAAM,GAAG;AACpF,IAAIkE,MACAlE,EAAM,eAAA,GACNkE,EAAO,MAAA;AAAA,EAEf,GAEMG,IAAa,CAACnE,MAAyB;;AACzC,IAAIA,EAAK,SAAS,aAAaA,EAAK,SAAS,UAAUA,EAAK,cAG5DY,IAAAZ,EAAK,YAAL,QAAAY,EAAA,KAAAZ,IACAqC,EAAQ,EAAK;AAAA,EACjB,GAEM+B,IAAgBrC,IAAgBsC,GAAwBtC,CAAa,IAAI,QACzEuC,IAAqBC;AAAA,IACvB,MAAMC,GAAYJ,GAAe7B,CAAU;AAAA,IAC3C,CAAC6B,CAAa;AAAA,EAAA,GAEZK,IAAiB1C,KAAA,gBAAAA,EAAe,OAEhC2C,IAAiB3C,IACnBrC,EAAM,aAAaqC,GAAe;AAAA,IAC9B,KAAKuC;AAAA,IACL,iBAAiB;AAAA,IACjB,iBAAiBlC;AAAA,IACjB,SAASuC,EAAaF,KAAA,gBAAAA,EAAgB,SAAShB,CAAiB;AAAA,IAChE,aAAakB,EAAaF,KAAA,gBAAAA,EAAgB,aAAa5E,CAAe;AAAA,IACtE,WAAW8E,EAAaF,KAAA,gBAAAA,EAAgB,WAAWb,CAAoB;AAAA,EAAA,CAC/C,IAE5B,gBAAA9C;AAAA,IAAC8D;AAAA,IAAA;AAAA,MACG,KAAKrC;AAAA,MACL,MAAK;AAAA,MACL,SAAAT;AAAA,MACA,MAAAF;AAAA,MACA,UAAU,CAACC;AAAA,MACX,cAAYA,IAAY,SAAYJ;AAAA,MACpC,iBAAc;AAAA,MACd,iBAAeW;AAAA,MACf,WAAW,WAAWP,IAAY,WAAW,EAAE;AAAA,MAC/C,SAAS4B;AAAA,MACT,aAAa5D;AAAA,MACb,WAAW+D;AAAA,MAEX,UAAA;AAAA,QAAA,gBAAAxD,EAAC,QAAA,EAAK,WAAW,2CAA2C8B,EAAY,OAAO,IAC1E,UAAAR,KAAe,gBAAAtB,EAACyE,GAAA,EAAmB,MAAM3C,EAAY,KAAA,CAAM,GAChE;AAAA,QACCL,KAAa,gBAAAzB,EAAC,QAAA,EAAM,UAAAqB,EAAA,CAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAInC,SACI,gBAAAX,EAAAC,GAAA,EACK,UAAA;AAAA,IAAA2D;AAAA,IACAtC,KACG0C;AAAA,MACI,gBAAAhE;AAAA,QAAC;AAAA,QAAA;AAAA,UACG,KAAK2B;AAAA,UACL,MAAK;AAAA,UACL,UAAU;AAAA,UACV,cAAYhB;AAAA,UACX,GAAGmB;AAAA,UACJ,OAAOmC,EAAUjC,CAAQ;AAAA,UACzB,aAAajD;AAAA,UACb,SAASA;AAAA,UACT,WAAWkE;AAAA,UACX,SAAS,MAAM;AACX,YAAApB,EAAY,UAAU;AAAA,UAC1B;AAAA,UACA,QAAQ,CAACqC,MAAM;;AACX,aAAKpE,IAAA6B,EAAQ,YAAR,QAAA7B,EAAiB,SAASoE,EAAE,mBAC7BrC,EAAY,UAAU;AAAA,UAE9B;AAAA,UAEC,UAAA;AAAA,YAAAnB,EAAM,WAAW,KACd,gBAAApB,EAAC,OAAA,EAAI,MAAK,YAAW,iBAAc,QAAO,OAAO6E,GAAkB,UAAA,uBAAA,CAEnE;AAAA,YAEHzD,EAAM,IAAI,CAACxB,MACR,gBAAAI;AAAA,cAACL;AAAA,cAAA;AAAA,gBAEG,MAAAC;AAAA,gBACA,MAAAC;AAAA,gBACA,UAAUkE;AAAA,gBACV,UAAU,MAAM9B,EAAQ,EAAK;AAAA,cAAA;AAAA,cAJxBrC,EAAK;AAAA,YAAA,CAMjB;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,MAEL,SAAS;AAAA,IAAA;AAAA,EACb,GACR;AAER;"}
|