@stackone/malachite 2.43.0 → 2.45.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 CHANGED
@@ -25,7 +25,7 @@ The catalog of components exported from `@stackone/malachite`. **Reach for one o
25
25
  - **`Divider`** — Separator; supports a dashed line style. Horizontal by default and spanning its container; `orientation="vertical"` draws a rule between inline controls (e.g. groups in a toolbar) and holds its width in a tight flex row. `length` sizes it along its own axis on a fixed scale, and it never shrinks below 1px. _Key props:_ `orientation="horizontal" | "vertical"`, `length` (`small` | `medium` | `large`), `lineStyle="solid" | "dashed"`, `DividerVariant`, `SpacingSize`, `PaddingSize`.
26
26
  - **`OverflowRow`** — Horizontal row that collapses items that do not fit into an overflow affordance.
27
27
  - **`ActionBar`** — A row of actions that collapses into a single `ActionMenu` once its container drops below `collapseBelow`. Measures the nearest genuinely-wider ancestor rather than itself, since the row shrinks to its own content. `dividerBefore` renders a vertical rule in the row and a divider in the collapsed menu, so both forms group the actions the same way. _Key props:_ `buttons` (`label`, `onClick`, `icon`, `variant`, `size`, `radius`, `tooltip`, `disabled`, `dividerBefore`), `collapseBelow`, `menuLabel`, `className`.
28
- - **`Popover`** — An anchored, non-modal panel for arbitrary content — a workspace switcher, an org flyout. The panel renders in its own shadow root, so malachite components inside it are styled, and joins the top layer like the other overlays. It prefers its `placement` and flips to the opposite side when there is no room, clamping along the other axis. Click-opened panels take focus and hand it back to the trigger on close; `openOn="hover"` also opens on focus and stays open while the pointer or focus is in the panel. A nested Popover counts as inside its parent for outside-press and hover dismissal, and Escape closes the topmost only. A click panel also closes when focus moves somewhere outside it and its trigger, and on close it hands focus back to the trigger only if focus would otherwise fall to the page. _Key props:_ `trigger`, `children`, `open`, `defaultOpen`, `onOpenChange`, `placement` (`bottom-start` | `bottom-end` | `top-start` | `top-center` | `right-start` | `right-end` | `left-start`), `openOn` (`click` | `hover`), `width` (`content` | `trigger`), `padding` (`none` | `small` | `medium`), `anchorRef` (position against another element, e.g. the section a trigger row sits in), `offset` (`small` 8px | `medium` 12px | `large` 14px), `elevation` (`default` | `raised`), `initialFocusRef` (what takes focus on open, in place of the first focusable), `role` (`dialog` | `menu` | `listbox`; `menu` brings no roving focus of its own), `aria-label` or `aria-labelledby` (one is required), `className`. The panel is capped to the viewport width, and only overlays opened from inside it (menus, dropdowns, modals) count as inside for outside-press dismissal. A side placement stays level with its anchor and scrolls within the room below, sliding up only when under 160px is left. Parts for the panel body: `Popover.Header` (`title`, `action`), `Popover.Action` (`icon`, `aria-label`, `onClick`, `disabled`, `tooltip`), `Popover.List` (a scrolling list), `Popover.Section` (a bordered section) and `Popover.Empty` (an empty-state line).
28
+ - **`Popover`** — An anchored, non-modal panel for arbitrary content — a workspace switcher, an org flyout. The panel renders in its own shadow root, so malachite components inside it are styled, and joins the top layer like the other overlays. It prefers its `placement` and flips to the opposite side when there is no room, clamping along the other axis. Click-opened panels take focus and hand it back to the trigger on close; `openOn="hover"` also opens on focus and stays open while the pointer or focus is in the panel. A nested Popover counts as inside its parent for outside-press and hover dismissal, and Escape closes the topmost only. A click panel also closes when focus moves somewhere outside it and its trigger, and on close it hands focus back to the trigger only if focus would otherwise fall to the page. _Key props:_ `trigger`, `children`, `open`, `defaultOpen`, `onOpenChange`, `placement` (`bottom-start` | `bottom-end` | `top-start` | `top-center` | `right-start` | `right-end` | `left-start`), `openOn` (`click` | `hover`), `width` (`content` | `trigger`), `padding` (`none` | `small` | `medium`), `anchorRef` (position against another element, e.g. the section a trigger row sits in), `offset` (`small` 8px | `medium` 12px | `large` 14px), `elevation` (`default` | `raised`), `initialFocusRef` (what takes focus on open, in place of the first focusable), `role` (`dialog` | `menu` | `listbox`; `menu` brings no roving focus of its own), `aria-label` or `aria-labelledby` (one is required), `className`. The panel is capped to the viewport width, and only overlays opened from inside it (menus, dropdowns, modals) count as inside for outside-press dismissal. A side placement stays level with its anchor and scrolls within the room below, sliding up only when under 160px is left. Parts for the panel body: `Popover.Header` (`title`, `action`), `Popover.Action` (`icon`, `aria-label`, `onClick`, `href` (a link that opens in a new tab on Cmd/Ctrl-click or a middle click; a plain click runs `onClick` when one is given), `disabled`, `tooltip`), `Popover.List` (a scrolling list), `Popover.Section` (a bordered section) and `Popover.Empty` (an empty-state line).
29
29
  - **`Modal`** — Centred dialog over a scrim, with header slots (icon/title/subtitle/extra), a scrolling body and a pinned footer. Locks background scroll and dismisses on ESC, the close button, or a scrim click unless `closeOnOverlayClick` is off; `busy` suppresses the two pointer routes while a request is in flight and sets `aria-busy`, so a stray click cannot tear the dialog down mid-write — but never ESC, which is the only guaranteed keyboard exit from a focus trap and so always reaches `onClose`. ESC, the Tab focus trap and `aria-modal` all apply to the topmost overlay only, so these may be stacked — a dialog opened from a panel keeps Tab to itself and the panel stops claiming to be modal. The Tab trap follows focus into a malachite popup portaled out of the dialog’s own tree — a `MultiSelect` listbox or a `Popover` opened from the body — and traps Tab within that popup while it holds focus, so Tab walks and wraps that popup’s own stops rather than being yanked back to the dialog’s first control or escaping the dialog altogether. A `Dropdown` list or an `ActionMenu` is dismissed by Tab rather than trapping it: it closes and focus returns to its trigger, so Tab carries on from there. A consumer’s own portaled popup is not known to the trap and Tab will pull focus out of it. Set `role="alertdialog"` for a consequential choice, which asks assistive tech to announce the body and not just the name, and point `aria-describedby` at whichever element carries it. Width comes from the `size` union (`small` 420px | `medium` 612px | `large` 760px) rather than a raw pixel prop. `titleAdornment` sits immediately after the title behind a hairline divider, for naming the thing being acted on — distinct from `headerExtra`, which pins to the far right. `tray` paints a tinted plate around the card and hangs a `caption` beneath it; the plate is the dialog element itself, so that caption stays inside the `aria-modal` subtree rather than being stranded outside it by the visual nesting, and it is added to `aria-describedby` automatically because it says what the dialog is asking. It takes a tone (`neutral` | `warning` | `error` | `success`) rather than a colour, so no consumer can paint it off-palette — and the tone sets the plate and its caption colour together, so the two cannot disagree. The caption is a `{ label?, message }` pair rather than a node, because its row height, type scale and colour belong to the plate. `size` names the card, and the plate’s 4px ring is added on top. Like `SidePanel` it sits in a z-index band from 1000 — one step per stacked overlay, so what paints on top is what holds the keyboard — and stays out of the browser top layer, so a host app’s own dialogs can still cover it. For a confirmation use `ConfirmationDialog`, which is a preset over this. _Key props:_ `open`, `onClose`, `title`, `subtitle`, `icon`, `headerExtra`, `titleAdornment`, `tray`, `headerDivider`, `footer`, `size`, `showCloseButton`, `closeOnOverlayClick`, `bodyPadding`, `busy`, `role` (`dialog` | `alertdialog`), `aria-label`, `aria-labelledby`, `aria-describedby`.
30
30
  - **`SidePanel`** — Right-hand slide-in overlay panel (drawer) with header slots (back/icon/title/subtitle/extra), scrollable body and optional footer. Locks background scroll and dismisses on ESC, an overlay click or close. ESC and the Tab focus trap apply to the topmost overlay only, so a `Modal` may be stacked over it. The Tab trap follows focus into a malachite popup portaled out of the panel’s own tree — a `MultiSelect` listbox or a `Popover` opened from the body — and traps Tab within that popup while it holds focus, so Tab walks and wraps that popup’s own stops rather than being yanked back to the panel’s first control or escaping the panel altogether. A `Dropdown` list or an `ActionMenu` is dismissed by Tab rather than trapping it: it closes and focus returns to its trigger, so Tab carries on from there. A consumer’s own portaled popup is not known to the trap and Tab will pull focus out of it.
31
31
  - **`useHasOpenOverlay`** — Hook reporting whether any modal-type overlay - `SidePanel` or `Modal` - is open anywhere in the app. For host chrome that has to get out of an overlay’s way, a floating support widget being the case it was added for: overlays render through a portal, so a host has no DOM signal of its own to read, and reading the library’s internal markers would couple it to something it does not own. Dropdowns and tooltips are deliberately not counted - they do not trap focus, and chrome does not need to move for them. _Key props:_ No arguments. Returns a boolean.
@@ -58,9 +58,9 @@ 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; `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.
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 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
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
- - **`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. 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` + 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.
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, 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.
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`).
65
65
  - **`FooterNav`** — Footer navigation bar of links. _Key props:_ `FooterNavLink[]`.
66
66
  - **`FooterLinks`** — Inline list of footer links (e.g. terms, privacy, support).
@@ -83,7 +83,7 @@ The catalog of components exported from `@stackone/malachite`. **Reach for one o
83
83
  - **`Search`** — Search input with a leading icon; `filled` and `muted` variants. `width` fixes the field's width, the same prop `Dropdown` and `MultiSelect` take, so a toolbar can put its controls on one width rather than letting each size itself; without it the field fills its container, and a container narrower than the value still wins. _Key props:_ `value`, `onChange`, `placeholder`, `variant`, `size`, `width`, `disabled`.
84
84
  - **`Checkbox`** — Checkbox supporting checked/indeterminate/disabled. Pass `presentational` to render a visual-only, non-focusable indicator for use inside a clickable row/card. _Key props:_ `checked`, `indeterminate`, `disabled`, `onChange`, `presentational`.
85
85
  - **`Radio`** — Single radio control with hover state; parent manages the group. Pass `presentational` to render a visual-only, non-focusable indicator for use inside a clickable row/card. _Key props:_ `checked`, `onChange`, `disabled`, `size`, `presentational`.
86
- - **`SegmentedControl`** — A rounded track of mutually-exclusive options — the segmented “one of these” control. Owns the track background, the selected-thumb chrome and the `radiogroup` semantics, so consumers never hand-roll a track around `PillButton` (whose `default` variant tokens are tuned for standalone filter pills, not a sunken track). Single tab stop with arrow-key move-and-select; selection is conveyed via `aria-checked` and a foreground change, not background colour alone. Restyle via the `segmentedControl` theme tokens. _Key props:_ `options` (`value`, `label`, `icon`, `disabled`), `value`, `onChange`, `SegmentedControlSize` (`small` | `medium` | `large`), `fullWidth`, `disabled`, `aria-label`, `aria-labelledby`, `className`.
86
+ - **`SegmentedControl`** — A rounded track of mutually-exclusive options — the segmented “one of these” control. Owns the track background, the selected-thumb chrome and the `radiogroup` semantics, so consumers never hand-roll a track around `PillButton` (whose `default` variant tokens are tuned for standalone filter pills, not a sunken track). Single tab stop with arrow-key move-and-select; selection is conveyed via `aria-checked` and a foreground change, not background colour alone. An option `icon` sits before the label in a fixed 16px slot, and an option `badge` follows the label at whatever width it needs, for a mark such as a compact `Tag` naming a saving; it renders inside the segment button, so it must not be interactive. Restyle via the `segmentedControl` theme tokens. _Key props:_ `options` (`value`, `label`, `icon`, `badge`, `disabled`), `value`, `onChange`, `SegmentedControlSize` (`small` | `medium` | `large`), `fullWidth`, `disabled`, `aria-label`, `aria-labelledby`, `className`.
87
87
  - **`Toggle`** — On/off switch. Add `label` for a visible caption, and `variant` to give it standalone toolbar-filter chrome — pick the family it sits beside: `pill` matches the filter-bar inputs (`Search`, `MultiSelect`), `button` renders the whole control as an outline `Button` with the switch as its trailing icon. `bordered` is shorthand for `variant="pill"`. `tone="pending"` paints the checked track amber, for a switch whose change has not been written yet — pair it with a `SelectionBar` reporting the same diff. The unchecked track never changes: nothing is pending about a switch that is off. Forwards a ref to its root element, so it can anchor an `InlineConfirm`. _Key props:_ `checked`, `onChange`, `disabled`, `ToggleSize` (`small` | `medium`), `ToggleTone` (`default` | `pending`), `label`, `ToggleVariant` (`pill` | `button`), `bordered`, `aria-label`, `aria-labelledby`, `aria-describedby`, `id`.
88
88
  - **`Dropdown`** — Single-select dropdown. `DropdownSize` is `small` (28px) | `medium` (32px) | `large` (42px), matching Button; `fullWidth` fills the container instead of sizing to content, which is what a table cell or a toolbar slot needs — without it the field column sizes to its content, so a consumer has to compute a width; `width` fixes that width outright, the way `MultiSelect` does, for a toolbar lining the two up rather than letting each hug its own label — a narrower container still wins, since the width clamps to it; Until this release a `search` trigger could not go below the intrinsic width of its bare `input`, so a `width` under roughly 200px was ignored; it now shrinks with the rest. The open menu fills the trigger and left-aligns under it, as Figma draws every tier of `Dropdown Component - Standard`, so `width` sizes both — the menu is the width of the control that opened it and does not widen for a long option, which ellipsises instead, so a row of controls and their open menus stay on one set of edges; below 160px the menu keeps that floor rather than shrinking to a chip nobody can read, and `minWidth` raises that floor without widening the trigger — for a picker a column pins narrow whose options carry a subtitle the trigger never shows; near a viewport edge it anchors to the trigger's other side before falling back to the viewport, so the two share an edge wherever one fits; `small` is the chip that sits beside a page title and rounds tighter (10px) than the taller sizes. These names each moved up a tier in 0.71 — the old `xsmall`/`small`/`medium` are now `small`/`medium`/`large`. Pass `value` for a controlled selection whose source of truth lives elsewhere (URL or app state): the trigger follows it without the consumer remounting via `key`, which used to discard focus mid-interaction. `defaultValue` keeps the uncontrolled behaviour. `clearable` adds a clear control to the trigger once something is selected and reports it as `onItemSelected(undefined)` — without it a selection cannot be undone, so filter bars had to invent a sentinel "any" item to carry that meaning. `requiredIndicator` switches the label between the library default of naming the optional fields, an `asterisk` on the required ones for designs drawn that way, and `none` for a field whose value is supplied rather than asked for — a locked control is still required, but a marker only sends the reader looking for something to fill in. Visual only: `aria-required` is set from `required` whichever is chosen. `showSubtitleInTrigger` promotes the selected item's `subtitle` into the closed trigger, on one line under the value, so the consequence of a choice stays on screen once the menu shuts — the trigger trades its fixed height for a floor and grows by that line, and either line too long for it ellipsises, with a single tooltip carrying whichever lines are cut off, value first. It does not combine with `search`, whose trigger is a single-line text input with nowhere to put a second line, so the type rejects the pair rather than letting a consumer ask for a subtitle and get none. `subtitleLayout` decides how a subtitle sits in the open menu: the default `inline` keeps it on the label's row after a divider, which suits a short qualifier, while `stacked` gives it its own line beneath the label — one line either way, ellipsised rather than wrapped, so inline a sentence-length subtitle is cut back to a few words. Every option row carries a tooltip for whatever it had to cut, across all three of label, subtitle and `rightSideText`, so nothing the row hides is unreadable. Passing `formControl` is enough to show validation on the same terms as `Input`, though only validation: the trigger still paints `value` or `defaultValue`, never the value the form holds. The resolver's message renders on its own line beneath the field in the destructive colour, politely announced, with `aria-invalid` and `aria-describedby` wired, while an explicit `helperText` keeps its own line above it and stays dimmed, and `error={false}` suppresses the resolver outright. An item's `subtitle` renders inline markdown in the option, trigger and tooltip, with links drawn but inert, since the option is itself the click target; a subtitle with no markdown syntax stays a plain text node. _Key props:_ `DropdownItem[]`, `DropdownSize`, `fullWidth`, `width`, `value`, `defaultValue`, `label`, `search`, `showSubtitleInTrigger`, `subtitleLayout`, `clearable`, `required`, `requiredIndicator`, `error`, `helperText`, `loading`, `formControl`, `aria-label`, `aria-labelledby`, `id`.
89
89
  - **`MultiSelect`** — Multi-select dropdown. `width` fixes the trigger, as `Dropdown` and `Search` take it, so a toolbar can put its controls on one width — and, as with those two, a container narrower than the value still wins; the menu fills the trigger and left-aligns under it, so that one prop sizes both; `minWidth` raises the 160px floor a narrow trigger falls back to. An option label too long for the menu ellipsises, and hovering it shows the full label — above the description in `hover` mode — as `Dropdown` does for its options. `requiredIndicator` follows the same three-way convention the single-select controls do, so a form choosing asterisks does not get "(Optional)" on one of its fields. _Key props:_ `MultiSelectOption[]`, `MultiSelectSize`, `width`, `minWidth`, `label`, `searchable`, `inlineSearch`, `chips`, `chipsRemovable`, `required`, `requiredIndicator`, `aria-label`, `aria-labelledby`, `id`.
@@ -1,162 +1,194 @@
1
- import { jsxs as h, jsx as r, Fragment as j } from "react/jsx-runtime";
2
- import I, { useState as z, useRef as E, useEffect as G, useMemo as $ } from "react";
1
+ import { jsxs as I, jsx as o, Fragment as A } from "react/jsx-runtime";
2
+ import v, { useState as z, useRef as b, useEffect as G, useMemo as $ } from "react";
3
3
  import { createPortal as U } from "react-dom";
4
4
  import { Button as X } from "./Button.js";
5
5
  import Z from "./CustomIcons/MoreHorizontalIcon.js";
6
- import { focusEdge as C, edgeForTriggerKey as q, itemForMenuKey as J, enabledItems as Q } from "./actionMenuKeyboard.js";
7
- import { TRIGGER_SIZES as V, EMPTY_ITEM_STYLE as W, menuStyle as H, itemStyle as L, TEXT_ITEM_STYLE as ee, ITEM_CLASS as ne, ITEM_ICON_LAYOUT as oe, ITEM_ICON_STYLE as re, ITEM_ICON_CLASS as te, LABEL_STYLE as ae } from "./actionMenuStyles.js";
8
- import { getDeepActiveElement as se } from "./activeElement.js";
9
- import { elementRef as le, composeRefs as ce, chainHandler as v } from "./composeRefs.js";
6
+ import { focusEdge as x, edgeForTriggerKey as q, itemForMenuKey as H, 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 ee, ITEM_ICON_LAYOUT as ne, ITEM_ICON_STYLE as re, ITEM_ICON_CLASS as oe, LABEL_STYLE as te, ITEM_CLASS as ae } from "./actionMenuStyles.js";
8
+ import { getDeepActiveElement as le } from "./activeElement.js";
9
+ import { elementRef as ce, composeRefs as se, chainHandler as T } from "./composeRefs.js";
10
10
  import { pushEscapeLayer as ie } from "./escapeLayers.js";
11
- import { usePopupLayer as ue } from "./overlayLayers.js";
12
- import { useActionMenuPosition as de } from "./useActionMenuPosition.js";
13
- const pe = I.lazy(() => import("./ActionMenuTagHost.js")), fe = (n) => n != null && n !== !1, g = (n) => n.stopPropagation(), ye = ({ item: n, tone: u, onSelect: M }) => n.type === "divider" ? /* @__PURE__ */ r("div", { style: L(n) }) : n.type === "text" ? /* @__PURE__ */ r("div", { role: "menuitem", "aria-disabled": "true", style: ee, children: n.label }) : /* @__PURE__ */ h(
14
- "button",
15
- {
16
- type: "button",
11
+ import { isPlainClick as ue } from "./linkClick.js";
12
+ import { usePopupLayer as fe } from "./overlayLayers.js";
13
+ import { safeHref as de } from "./safeHref.js";
14
+ import { useActionMenuPosition as pe } from "./useActionMenuPosition.js";
15
+ const ye = v.lazy(() => import("./ActionMenuTagHost.js")), me = (n) => n != null && n !== !1, k = (n) => n.stopPropagation(), he = ({ item: n, tone: f, onSelect: h, onFollow: g }) => {
16
+ if (n.type === "divider")
17
+ return /* @__PURE__ */ o("div", { style: R(n) });
18
+ if (n.type === "text")
19
+ return /* @__PURE__ */ o("div", { role: "menuitem", "aria-disabled": "true", style: ee, children: n.label });
20
+ const y = {
17
21
  role: "menuitem",
18
- disabled: n.disabled,
19
22
  title: n.tooltip,
20
23
  "aria-current": n.current ? "page" : void 0,
21
- className: ne,
24
+ className: ae,
22
25
  "data-danger": n.danger === "hover" ? "hover" : void 0,
23
- "data-tone": u,
24
- onClick: () => M(n),
25
- style: L(n, u),
26
- children: [
27
- n.icon && /* @__PURE__ */ r(
28
- "span",
29
- {
30
- className: te,
31
- style: n.danger === "hover" ? oe : re,
32
- children: n.icon
26
+ "data-tone": f,
27
+ style: R(n, f)
28
+ }, u = /* @__PURE__ */ I(A, { children: [
29
+ n.icon && /* @__PURE__ */ o(
30
+ "span",
31
+ {
32
+ className: oe,
33
+ style: n.danger === "hover" ? ne : re,
34
+ children: n.icon
35
+ }
36
+ ),
37
+ /* @__PURE__ */ o("span", { style: te, children: n.label }),
38
+ me(n.tag) ? /* @__PURE__ */ o(v.Suspense, { fallback: null, children: /* @__PURE__ */ o(ye, { children: n.tag }) }) : null
39
+ ] }), E = n.disabled ? void 0 : de(n.href);
40
+ return E !== void 0 ? /* @__PURE__ */ o(
41
+ "a",
42
+ {
43
+ ...y,
44
+ href: E,
45
+ onClick: (t) => {
46
+ if (ue(t) && n.onClick) {
47
+ t.preventDefault(), h(n);
48
+ return;
33
49
  }
34
- ),
35
- /* @__PURE__ */ r("span", { style: ae, children: n.label }),
36
- fe(n.tag) ? /* @__PURE__ */ r(I.Suspense, { fallback: null, children: /* @__PURE__ */ r(pe, { children: n.tag }) }) : null
37
- ]
38
- }
39
- ), we = ({
50
+ g();
51
+ },
52
+ onAuxClick: (t) => {
53
+ t.button === 1 && g();
54
+ },
55
+ children: u
56
+ }
57
+ ) : /* @__PURE__ */ o("button", { ...y, type: "button", disabled: n.disabled, onClick: () => h(n), children: u });
58
+ }, Re = ({
40
59
  items: n,
41
- label: u = "More actions",
42
- triggerIcon: M,
43
- minWidth: R,
44
- size: S = "small",
45
- showLabel: y = !1,
46
- variant: x = "outline",
47
- trigger: c,
48
- placement: b = "bottom-end",
60
+ label: f = "More actions",
61
+ triggerIcon: h,
62
+ minWidth: g,
63
+ size: y = "small",
64
+ showLabel: u = !1,
65
+ variant: E = "outline",
66
+ trigger: t,
67
+ placement: S = "bottom-end",
49
68
  offset: O,
50
- tone: K = "default"
69
+ tone: P = "default"
51
70
  }) => {
52
- const T = V[S], [t, i] = z(!1), d = E(null), l = E(null), f = E(null), p = E(!1), P = ue("actionmenu", t, l), A = de(t, d, l, {
53
- minWidth: R,
54
- placement: b,
71
+ const C = Q[y], [a, s] = z(!1), d = b(null), i = b(null), m = b(null), p = b(!1), K = fe("actionmenu", a, i), N = pe(a, d, i, {
72
+ minWidth: g,
73
+ placement: S,
55
74
  offset: O
56
75
  });
57
76
  G(() => {
58
- if (!t)
77
+ if (!a)
59
78
  return;
60
- f.current && (C(l.current, f.current), f.current = null);
61
- const e = (s) => {
62
- const m = s.composedPath();
63
- [d.current, l.current].some(
64
- (w) => w && m.includes(w)
65
- ) || (p.current = !1, i(!1));
79
+ m.current && (x(i.current, m.current), m.current = null);
80
+ const e = (c) => {
81
+ const M = c.composedPath();
82
+ [d.current, i.current].some(
83
+ (w) => w && M.includes(w)
84
+ ) || (p.current = !1, s(!1));
66
85
  };
67
86
  document.addEventListener("mousedown", e, !0);
68
- const o = ie(() => i(!1));
87
+ const r = ie(() => s(!1));
69
88
  return () => {
70
- var m;
71
- document.removeEventListener("mousedown", e, !0), o();
72
- const s = se();
73
- p.current && (!s || s === document.body) && ((m = d.current) == null || m.focus({ preventScroll: !0 })), p.current = !1;
89
+ var M;
90
+ document.removeEventListener("mousedown", e, !0), r();
91
+ const c = le();
92
+ p.current && (!c || c === document.body) && ((M = d.current) == null || M.focus({ preventScroll: !0 })), p.current = !1;
74
93
  };
75
- }, [t]);
76
- const _ = (e) => {
94
+ }, [a]);
95
+ const D = (e) => {
77
96
  e.stopPropagation();
78
- const o = !t && e.detail === 0;
79
- f.current = o ? "first" : null, i((s) => !s);
80
- }, k = (e) => {
81
- if (e.key === "Tab" && t) {
82
- i(!1);
97
+ const r = !a && e.detail === 0;
98
+ m.current = r ? "first" : null, s((c) => !c);
99
+ }, _ = (e) => {
100
+ if (e.key === "Tab" && a) {
101
+ s(!1);
83
102
  return;
84
103
  }
85
- const o = q(e.key, b);
86
- if (o) {
87
- if (e.preventDefault(), t) {
88
- C(l.current, o);
104
+ const r = q(e.key, S);
105
+ if (r) {
106
+ if (e.preventDefault(), a) {
107
+ x(i.current, r);
89
108
  return;
90
109
  }
91
- f.current = o, i(!0);
110
+ m.current = r, s(!0);
92
111
  }
93
- }, N = (e) => {
94
- var s;
112
+ }, F = (e) => {
113
+ var c;
95
114
  if (e.key === "Tab") {
96
- p.current = !1, (s = d.current) == null || s.focus({ preventScroll: !0 }), i(!1);
115
+ p.current = !1, (c = d.current) == null || c.focus({ preventScroll: !0 }), s(!1);
97
116
  return;
98
117
  }
99
- const o = J(Q(l.current), e.target, e.key);
100
- o && (e.preventDefault(), o.focus());
101
- }, F = (e) => {
102
- var o;
103
- e.type === "divider" || e.type === "text" || e.disabled || ((o = e.onClick) == null || o.call(e), i(!1));
104
- }, D = c ? le(c) : void 0, Y = $(
105
- () => ce(D, d),
106
- [D]
107
- ), a = c == null ? void 0 : c.props, B = c ? I.cloneElement(c, {
108
- ref: Y,
118
+ if (e.key === " " && e.target instanceof HTMLAnchorElement) {
119
+ e.preventDefault(), e.target.click();
120
+ return;
121
+ }
122
+ const r = H(J(i.current), e.target, e.key);
123
+ r && (e.preventDefault(), r.focus());
124
+ }, Y = (e) => {
125
+ var r;
126
+ e.type === "divider" || e.type === "text" || e.disabled || ((r = e.onClick) == null || r.call(e), s(!1));
127
+ }, L = t ? ce(t) : void 0, B = $(
128
+ () => se(L, d),
129
+ [L]
130
+ ), l = t == null ? void 0 : t.props, j = t ? v.cloneElement(t, {
131
+ ref: B,
109
132
  "aria-haspopup": "menu",
110
- "aria-expanded": t,
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)
114
- }) : /* @__PURE__ */ h(
133
+ "aria-expanded": a,
134
+ onClick: T(l == null ? void 0 : l.onClick, D),
135
+ onMouseDown: T(l == null ? void 0 : l.onMouseDown, k),
136
+ onKeyDown: T(l == null ? void 0 : l.onKeyDown, _)
137
+ }) : /* @__PURE__ */ I(
115
138
  X,
116
139
  {
117
140
  ref: d,
118
141
  type: "button",
119
- variant: x,
120
- size: S,
121
- iconOnly: !y,
122
- "aria-label": y ? void 0 : u,
142
+ variant: E,
143
+ size: y,
144
+ iconOnly: !u,
145
+ "aria-label": u ? void 0 : f,
123
146
  "aria-haspopup": "menu",
124
- "aria-expanded": t,
125
- className: `shrink-0${y ? " gap-2" : ""}`,
126
- onClick: _,
127
- onMouseDown: g,
128
- onKeyDown: k,
147
+ "aria-expanded": a,
148
+ className: `shrink-0${u ? " gap-2" : ""}`,
149
+ onClick: D,
150
+ onMouseDown: k,
151
+ onKeyDown: _,
129
152
  children: [
130
- /* @__PURE__ */ r("span", { className: `inline-flex items-center justify-center ${T.iconBox}`, children: M ?? /* @__PURE__ */ r(Z, { size: T.icon }) }),
131
- y && /* @__PURE__ */ r("span", { children: u })
153
+ /* @__PURE__ */ o("span", { className: `inline-flex items-center justify-center ${C.iconBox}`, children: h ?? /* @__PURE__ */ o(Z, { size: C.icon }) }),
154
+ u && /* @__PURE__ */ o("span", { children: f })
132
155
  ]
133
156
  }
134
157
  );
135
- return /* @__PURE__ */ h(j, { children: [
136
- B,
137
- t && U(
138
- /* @__PURE__ */ h(
158
+ return /* @__PURE__ */ I(A, { children: [
159
+ j,
160
+ a && U(
161
+ /* @__PURE__ */ I(
139
162
  "div",
140
163
  {
141
- ref: l,
164
+ ref: i,
142
165
  role: "menu",
143
166
  tabIndex: -1,
144
- "aria-label": u,
145
- ...P,
146
- style: H(A),
147
- onMouseDown: g,
148
- onClick: g,
149
- onKeyDown: N,
167
+ "aria-label": f,
168
+ ...K,
169
+ style: W(N),
170
+ onMouseDown: k,
171
+ onClick: k,
172
+ onKeyDown: F,
150
173
  onFocus: () => {
151
174
  p.current = !0;
152
175
  },
153
176
  onBlur: (e) => {
154
- var o;
155
- (o = l.current) != null && o.contains(e.relatedTarget) || (p.current = !1);
177
+ var r;
178
+ (r = i.current) != null && r.contains(e.relatedTarget) || (p.current = !1);
156
179
  },
157
180
  children: [
158
- n.length === 0 && /* @__PURE__ */ r("div", { role: "menuitem", "aria-disabled": "true", style: W, children: "No actions available" }),
159
- n.map((e) => /* @__PURE__ */ r(ye, { item: e, tone: K, onSelect: F }, e.key))
181
+ n.length === 0 && /* @__PURE__ */ o("div", { role: "menuitem", "aria-disabled": "true", style: V, children: "No actions available" }),
182
+ n.map((e) => /* @__PURE__ */ o(
183
+ he,
184
+ {
185
+ item: e,
186
+ tone: P,
187
+ onSelect: Y,
188
+ onFollow: () => s(!1)
189
+ },
190
+ e.key
191
+ ))
160
192
  ]
161
193
  }
162
194
  ),
@@ -165,6 +197,6 @@ const pe = I.lazy(() => import("./ActionMenuTagHost.js")), fe = (n) => n != null
165
197
  ] });
166
198
  };
167
199
  export {
168
- we as default
200
+ Re as default
169
201
  };
170
202
  //# 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_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 { usePopupLayer } from './overlayLayers';\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 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 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 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","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","Fragment","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 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,4 +1,4 @@
1
- import { jsx as e, jsxs as b, Fragment as R } from "react/jsx-runtime";
1
+ import { jsx as e, jsxs as b, Fragment as w } from "react/jsx-runtime";
2
2
  import U, { forwardRef as W } from "react";
3
3
  import V from "./CustomIcons/CaretRightIcon.js";
4
4
  import Y from "./Tooltip.js";
@@ -8,7 +8,8 @@ import { chainHandler as M, composeRefs as Q, elementRef as X } from "./composeR
8
8
  import { FOCUS_RING_INSET as Z } from "./focusRing.js";
9
9
  import { resolveRel as ee } from "./linkRel.js";
10
10
  import { safeHref as ae } from "./safeHref.js";
11
- const re = 0.2, te = "group relative flex items-center box-border no-underline border-0 m-0 cursor-pointer select-none text-left font-sans font-medium text-[length:var(--malachite-font-size-small,13px)] leading-[17px] rounded-[var(--malachite-radius-xl,12px)] transition-[background-color,color] duration-200", I = {
11
+ import { SIDEBAR_TOOLTIP_DELAY_S as re } from "./sidebarContext.js";
12
+ const te = "group relative flex items-center box-border no-underline border-0 m-0 cursor-pointer select-none text-left font-sans font-medium text-[length:var(--malachite-font-size-small,13px)] leading-[17px] rounded-[var(--malachite-radius-xl,12px)] transition-[background-color,color] duration-200", I = {
12
13
  item: "w-full min-h-[37px] gap-3 py-[10px] px-3",
13
14
  compact: "w-full min-h-9 gap-3 py-2 px-3",
14
15
  subitem: "w-full gap-3 py-2 pr-3 pl-9",
@@ -32,18 +33,18 @@ const re = 0.2, te = "group relative flex items-center box-border no-underline b
32
33
  collapsed: d = !1,
33
34
  variant: u = "item",
34
35
  size: A = "medium",
35
- danger: L = !1,
36
+ danger: S = !1,
36
37
  trailing: _,
37
38
  badge: t,
38
39
  badgeLabel: r,
39
- tag: T,
40
+ tag: E,
40
41
  disclosure: m,
41
- tooltip: S,
42
- accessibleLabel: H,
43
- className: D = "",
44
- asChild: K,
42
+ tooltip: L,
43
+ accessibleLabel: D,
44
+ className: H = "",
45
+ asChild: B,
45
46
  ...v
46
- } = j, k = L ? "danger" : O ? "active" : z ? "activeChild" : "idle", P = u === "subitem", B = d ? I.collapsed : I[u === "item" && A === "small" ? "compact" : u], x = `${te} ${B} ${ne[k]} ${Z} ${D}`.trim(), p = d ? S ?? (typeof l == "string" ? l : void 0) : void 0, F = p ? [p, r].filter(Boolean).join(", ") : void 0, i = H ?? (d ? F : void 0), w = v, E = O ? "page" : w["aria-current"], N = d ? /* @__PURE__ */ b(R, { children: [
47
+ } = j, R = S ? "danger" : O ? "active" : z ? "activeChild" : "idle", K = u === "subitem", P = d ? I.collapsed : I[u === "item" && A === "small" ? "compact" : u], x = `${te} ${P} ${ne[R]} ${Z} ${H}`.trim(), p = d ? L ?? (typeof l == "string" ? l : void 0) : void 0, F = p ? [p, r].filter(Boolean).join(", ") : void 0, i = D ?? (d ? F : void 0), T = v, k = O ? "page" : T["aria-current"], N = d ? /* @__PURE__ */ b(w, { children: [
47
48
  c && /* @__PURE__ */ e("span", { "aria-hidden": "true", className: $, children: c }),
48
49
  !i && /* @__PURE__ */ b("span", { className: "sr-only", children: [
49
50
  l,
@@ -57,17 +58,17 @@ const re = 0.2, te = "group relative flex items-center box-border no-underline b
57
58
  children: t
58
59
  }
59
60
  )
60
- ] }) : /* @__PURE__ */ b(R, { children: [
61
- P ? /* @__PURE__ */ e(
61
+ ] }) : /* @__PURE__ */ b(w, { children: [
62
+ K ? /* @__PURE__ */ e(
62
63
  "span",
63
64
  {
64
65
  "aria-hidden": "true",
65
- className: `absolute left-[14px] top-1/2 -mt-[2px] size-1 rounded-full transition-colors duration-200 ${se[k]}`
66
+ className: `absolute left-[14px] top-1/2 -mt-[2px] size-1 rounded-full transition-colors duration-200 ${se[R]}`
66
67
  }
67
68
  ) : c && /* @__PURE__ */ e("span", { "aria-hidden": "true", className: $, children: c }),
68
69
  /* @__PURE__ */ b("span", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
69
70
  /* @__PURE__ */ e("span", { className: "min-w-0 truncate", children: l }),
70
- T && /* @__PURE__ */ e("span", { className: "flex shrink-0", children: T })
71
+ E && /* @__PURE__ */ e("span", { className: "flex shrink-0", children: E })
71
72
  ] }),
72
73
  t && /* @__PURE__ */ e("span", { "aria-hidden": r ? !0 : void 0, className: "flex shrink-0", children: t }),
73
74
  t && r && !i && /* @__PURE__ */ e("span", { className: "sr-only", children: r }),
@@ -86,7 +87,7 @@ const re = 0.2, te = "group relative flex items-center box-border no-underline b
86
87
  )
87
88
  ] });
88
89
  let h;
89
- if (K) {
90
+ if (B) {
90
91
  const { children: n, ...s } = v, y = J(n, x, void 0, N), a = n.props, g = Object.fromEntries(
91
92
  Object.entries(s).map(
92
93
  ([o, C]) => o.startsWith("on") && typeof C == "function" && typeof a[o] == "function" ? [
@@ -101,7 +102,7 @@ const re = 0.2, te = "group relative flex items-center box-border no-underline b
101
102
  h = U.cloneElement(y, {
102
103
  ...g,
103
104
  ref: Q(X(n), f),
104
- "aria-current": E ?? a["aria-current"],
105
+ "aria-current": k ?? a["aria-current"],
105
106
  "aria-label": i ?? s["aria-label"] ?? a["aria-label"]
106
107
  });
107
108
  } else {
@@ -119,7 +120,7 @@ const re = 0.2, te = "group relative flex items-center box-border no-underline b
119
120
  href: g,
120
121
  target: s,
121
122
  rel: ee(y, s),
122
- "aria-current": E,
123
+ "aria-current": k,
123
124
  "aria-label": i ?? a["aria-label"],
124
125
  className: x,
125
126
  children: N
@@ -137,7 +138,7 @@ const re = 0.2, te = "group relative flex items-center box-border no-underline b
137
138
  }
138
139
  );
139
140
  }
140
- const G = q(w["aria-expanded"]);
141
+ const G = q(T["aria-expanded"]);
141
142
  return p ? /* @__PURE__ */ e(
142
143
  Y,
143
144
  {