@stackone/malachite 2.76.0 → 2.77.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
@@ -26,8 +26,8 @@ The catalog of components exported from `@stackone/malachite`. **Reach for one o
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
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-end` | `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`), `initialFocus` (a ref, or a function read when the panel is shown, for what takes focus in place of the first focusable; it is read again as the panel's content changes, so focus follows content that mounts late or a focused element that is removed, but never leaves an element the user moved to; `initialFocusRef` is its deprecated form, read on open only), `openOnArrowKey` (a click panel also opens, focus inside, on the arrow key its placement points at — ArrowDown for a `bottom-*` placement; a text field trigger keeps its arrow keys), `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`, `divider` — `false` drops the rule and sits it 4px over the list, as its label), `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; `scrollToSelected` scrolls the list, never the page, to its selected row, one marked `aria-current`, `aria-selected` or a checked radio, each time the popover is shown, once that row mounts), `Popover.Section` (a bordered section), `Popover.Empty` (an empty-state line), `Popover.Search` (pads a `Search` above the list without a second rule), `Popover.Note` (a footnote section in small secondary text) and `Popover.Status` (`message`, `tone` (`default` | `error`), `action` (`label`, `onClick`, `pending`), `focusable`): one `status` live region for the panel's life, announcing each message it is given and staying mounted, visually hidden, while there is none, so clear `message` rather than unmounting it. Errors announce through the same region. `focusable` with a ref makes it an `initialFocus` target; resolve to it only while it has a message, since an empty one is invisible. Reach for `Status` when the message changes while the panel is open or carries an action (loading, failed, sign-in needed), and `Empty` for a static line in a list that simply has nothing in it.
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. `callouts` stacks full-bleed `Alert` bands at the top of the body, as on `SidePanel`; they ignore `bodyPadding` and lead `aria-describedby`, ahead of the consumer’s own description and the tray caption. For a confirmation use `ConfirmationDialog`, which is a preset over this. _Key props:_ `open`, `onClose`, `title`, `subtitle`, `icon`, `headerExtra`, `titleAdornment`, `tray`, `callouts`, `renderLink`, `headerDivider`, `footer`, `size`, `showCloseButton`, `closeOnOverlayClick`, `bodyPadding`, `bodyHeight` (`auto` | `tall` — 600px, or the viewport less 160px when smaller, for an embedded iframe at `height: 100%`), `busy`, `role` (`dialog` | `alertdialog`), `aria-label`, `aria-labelledby`, `aria-describedby`.
30
- - **`SidePanel`** — Right-hand slide-in overlay panel (drawer) with header slots (back/icon/title/subtitle/extra), scrollable body and optional footer. `subtitlePlacement="below"` stacks the subtitle on its own line under the title row, so neither truncates the other in a narrow panel. `toolbar` pins content between the header and the scrolling body (16px sides, 16px above, 8px below, no divider), such as a Search over the list it filters. 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. `callouts` stacks full-bleed `Alert` bands at the top of the body, above `children` and unaffected by `bodyPadding` — the same band `ConfirmationDialog` gives its `callout` — and `renderLink` routes their root-relative markdown links through the app’s router. The callouts are announced as the panel’s description, ahead of `description`.
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 | `fullscreen`, which fills the window in both directions) 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. `callouts` stacks full-bleed `Alert` bands at the top of the body, as on `SidePanel`; they ignore `bodyPadding` and lead `aria-describedby`, ahead of the consumer’s own description and the tray caption. For a confirmation use `ConfirmationDialog`, which is a preset over this. _Key props:_ `open`, `onClose`, `title`, `subtitle`, `icon`, `headerExtra`, `titleAdornment`, `tray`, `callouts`, `renderLink`, `headerDivider`, `footer`, `size`, `showCloseButton`, `closeOnOverlayClick`, `bodyPadding`, `bodyHeight` (`auto` | `tall` — 600px, or the viewport less 160px when smaller, for an embedded iframe at `height: 100%`), `busy`, `role` (`dialog` | `alertdialog`), `aria-label`, `aria-labelledby`, `aria-describedby`.
30
+ - **`SidePanel`** — Right-hand slide-in overlay panel (drawer) with header slots (back/icon/title/subtitle/extra), scrollable body and optional footer. `subtitlePlacement="below"` stacks the subtitle on its own line under the title row, so neither truncates the other in a narrow panel. `toolbar` pins content between the header and the scrolling body (16px sides, 16px above, 8px below, no divider), such as a Search over the list it filters. 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. `callouts` stacks full-bleed `Alert` bands at the top of the body, above `children` and unaffected by `bodyPadding` — the same band `ConfirmationDialog` gives its `callout` — and `renderLink` routes their root-relative markdown links through the app’s router. The callouts are announced as the panel’s description, ahead of `description`. `aside` opens a full-height column to the left, as wide again as `maxWidth`, for help that sits beside the content — a setup guide beside a form. The panel’s own header, body and footer keep their width and place, and the aside’s `title` (with an optional `headerExtra`, such as a `Stepper`) and `footer` line up with the panel’s header and footer; `bodyPadding` works as the panel’s does. The aside is never narrower than 360px, so a window too narrow for both columns scrolls the panel sideways.
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.
32
32
  - **`SidePanelNavFooter`** — Keyboard-hint footer for SidePanel: up/down keycaps to step between records and an esc-to-close hint, with an optional right-aligned actions slot.
33
33
 
@@ -45,7 +45,7 @@ The catalog of components exported from `@stackone/malachite`. **Reach for one o
45
45
  - **`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).
46
46
  - **`Progress`** — Progress bar.
47
47
  - **`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`.
48
- - **`ToastProvider`** — Transient top-centre toasts for the outcome of an action. Wrap the app once in `ToastProvider` and call `useToast().show({ message, variant })` anywhere beneath it. Each toast auto-dismisses, pauses while hovered and dismisses on click; at most three show at once. Toasts are announced through always-mounted live regions (`status` for success, `alert` for error), and the pills themselves are hidden from assistive tech and never take focus, so they cannot fight a dialog's focus trap. The host mounts on the first toast and joins the top layer, so toasts paint above elevated side panels. _Key props:_ `ToastProvider`: `children`, `duration` (ms, default 3000). `useToast()` → `show({ message, variant })`, `ToastVariant` (`success` | `error`, default `success`).
48
+ - **`ToastProvider`** — Transient top-centre toasts for the outcome of an action. Wrap the app once in `ToastProvider` and call `useToast().show({ message, variant })` anywhere beneath it. Corners match a medium button. Each toast auto-dismisses, pauses while hovered and dismisses on click; at most three show at once. Toasts are announced through always-mounted live regions (`status` for success, `alert` for error), and the pills themselves are hidden from assistive tech and never take focus, so they cannot fight a dialog's focus trap. The host mounts on the first toast and joins the top layer, so toasts paint above elevated side panels. _Key props:_ `ToastProvider`: `children`, `duration` (ms, default 3000). `useToast()` → `show({ message, variant })`, `ToastVariant` (`success` | `error`, default `success`).
49
49
  - **`Spinner`** — Loading spinner.
50
50
  - **`Skeleton`** — Loading placeholders. Includes `Skeleton.Avatar` and `Skeleton.Input`. The shimmer stops under `prefers-reduced-motion`, leaving the plain placeholder.
51
51
  - **`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).
@@ -122,7 +122,7 @@ The catalog of components exported from `@stackone/malachite`. **Reach for one o
122
122
  - **`AppLogo`** — A connector's mark, optionally with its name and version tag. Builds on `Avatar`, so a missing or broken logo falls back to the generic "no logo available" placeholder, a neutral grey tile with a darker square, the same mark stackone-logos.com serves. The gap tightens below 18px so the mark and label still read as one unit. _Key props:_ `src`, `name`, `showName`, `size`, `nameSize`, `nameWeight`, `version`, `truncate`, `className`.
123
123
  - **`ConnectionHeader`** — Two tilted logo tiles (from → to) showing a connection between apps. _Key props:_ `from`, `to` (`ConnectionEndpoint`: `icon`, `label`, `background`).
124
124
  - **`CodeBlock`** — Syntax-styled code block. _Key props:_ `code`.
125
- - **`Stepper`** — Progress dots/segments for a multi-step flow; optionally clickable. _Key props:_ `steps`, `active`, `onChange`.
125
+ - **`Stepper`** — Progress dots/segments for a multi-step flow; optionally clickable. `groups` takes a long flow in labelled sections: the active step’s group opens into one bar per step across the full width and every other group folds to a short bar. Folded bars keep a fixed width and the open group shares what is left, so allow about 20px per other group and 12px per step in the open group. With `onChange` each folded bar is a button named after its group. _Key props:_ `steps` or `groups` (`{ label, steps }[]`), `active`, `onChange`.
126
126
  - **`Swatch`** — Colour swatch preview.
127
127
 
128
128
  ## Theming & utility
@@ -1,74 +1,84 @@
1
- import { jsx as e, jsxs as s } from "react/jsx-runtime";
2
- import { useId as z } from "react";
3
- import F from "./CustomIcons/ArrowUpRightIcon.js";
4
- import $ from "./CustomIcons/InfoFilledIcon.js";
5
- import { FormItem as w } from "./FormItem.js";
6
- import { Link as O } from "./Link.js";
7
- import { safeHref as _ } from "./safeHref.js";
1
+ import { jsx as e, jsxs as a } from "react/jsx-runtime";
2
+ import { useId as k } from "react";
3
+ import w from "./CustomIcons/ArrowUpRightIcon.js";
4
+ import F from "./CustomIcons/InfoFilledIcon.js";
5
+ import { FormItem as $ } from "./FormItem.js";
6
+ import { Link as y } from "./Link.js";
8
7
  import x from "./Spacer.js";
9
- import k from "./Tooltip.js";
10
- import H from "./Typography/MarkdownBlock.js";
8
+ import O from "./Tooltip.js";
9
+ import _ from "./Typography/MarkdownBlock.js";
11
10
  import h from "./Typography/Text.js";
12
- import { FOCUS_RING as R } from "./focusRing.js";
13
- import { renderFormItemChildren as y, NO_FIELD_ERROR as L } from "./formItemState.js";
11
+ import { FOCUS_RING as u } from "./focusRing.js";
12
+ import { renderFormItemChildren as H, NO_FIELD_ERROR as R } from "./formItemState.js";
13
+ import { safeHref as L } from "./safeHref.js";
14
14
  const T = 0.5, q = ({
15
- id: t,
16
- label: r,
17
- description: i,
15
+ id: r,
16
+ label: t,
17
+ description: o,
18
18
  hint: l,
19
- helperText: a = !1
19
+ helperText: s = !1
20
20
  }) => {
21
- const o = `mal-${z().replace(/[^a-zA-Z0-9_-]/g, "")}-`, p = t ?? `${o}control`, f = r ? `${o}label` : void 0, n = i ? `${o}description` : void 0, m = l ? `${o}hint` : void 0, c = a ? `${o}helper` : void 0, d = [n, m, c].filter(Boolean).join(" ") || void 0;
21
+ const i = `mal-${k().replace(/[^a-zA-Z0-9_-]/g, "")}-`, p = r ?? `${i}control`, f = t ? `${i}label` : void 0, n = o ? `${i}description` : void 0, m = l ? `${i}hint` : void 0, c = s ? `${i}helper` : void 0, d = [n, m, c].filter(Boolean).join(" ") || void 0;
22
22
  return { controlId: p, labelId: f, descriptionId: n, hintId: m, helperTextId: c, describedBy: d };
23
23
  }, ee = ({
24
- label: t,
25
- hint: r,
26
- rightSideContent: i,
24
+ label: r,
25
+ hint: t,
26
+ rightSideContent: o,
27
27
  tooltip: l,
28
- name: a,
29
- description: o,
28
+ name: s,
29
+ description: i,
30
30
  required: p = !1,
31
31
  requiredIndicator: f = "optional-suffix",
32
32
  loading: n = !1,
33
33
  formControl: m,
34
34
  loadingComponent: c,
35
35
  children: d,
36
- controlId: u,
37
- labelId: v,
36
+ controlId: v,
37
+ labelId: b,
38
38
  descriptionId: I,
39
39
  hintId: N,
40
- fullWidth: b = !1
40
+ fullWidth: g = !1
41
41
  }) => {
42
- const g = m ? /* @__PURE__ */ e(w, { control: m, name: a, children: d }) : y(d, L);
43
- return /* @__PURE__ */ s(x, { direction: "vertical", size: 6, align: "stretch", fullWidth: b, children: [
44
- (t || r || o || i) && /* @__PURE__ */ s(x, { direction: "vertical", size: 4, align: "stretch", children: [
45
- /* @__PURE__ */ s("div", { className: "flex justify-between items-start", children: [
42
+ const z = m ? /* @__PURE__ */ e($, { control: m, name: s, children: d }) : H(d, R);
43
+ return /* @__PURE__ */ a(x, { direction: "vertical", size: 6, align: "stretch", fullWidth: g, children: [
44
+ (r || t || i || o) && /* @__PURE__ */ a(x, { direction: "vertical", size: 4, align: "stretch", children: [
45
+ /* @__PURE__ */ a("div", { className: "flex justify-between items-start", children: [
46
46
  /* @__PURE__ */ e(
47
47
  E,
48
48
  {
49
- text: t,
49
+ text: r,
50
50
  required: p,
51
51
  requiredIndicator: f,
52
52
  tooltip: l,
53
- id: v,
54
- htmlFor: n ? void 0 : u
53
+ id: b,
54
+ htmlFor: n ? void 0 : v
55
55
  }
56
56
  ),
57
- (r || i) && /* @__PURE__ */ s("div", { className: "flex items-start gap-2", children: [
58
- r && /* @__PURE__ */ e(B, { text: r, id: N }),
59
- i && /* @__PURE__ */ e(j, { ...i })
57
+ (t || o) && /* @__PURE__ */ a("div", { className: "flex items-start gap-2", children: [
58
+ t && /* @__PURE__ */ e(B, { text: t, id: N }),
59
+ o && /* @__PURE__ */ e(j, { ...o })
60
60
  ] })
61
61
  ] }),
62
- o && /* @__PURE__ */ e(D, { text: o, id: I })
62
+ i && /* @__PURE__ */ e(D, { text: i, id: I })
63
63
  ] }),
64
- n ? c : g
64
+ n ? c : z
65
65
  ] });
66
- }, j = (t) => {
67
- const r = _(t.href);
68
- return r ? /* @__PURE__ */ s(
69
- O,
66
+ }, j = (r) => {
67
+ if (r.type === "button")
68
+ return /* @__PURE__ */ e(
69
+ "button",
70
+ {
71
+ type: "button",
72
+ onClick: r.onClick,
73
+ className: `relative top-[3px] shrink-0 cursor-pointer whitespace-nowrap border-0 bg-transparent p-0 text-[length:var(--malachite-font-size-xsmall)] font-medium text-[var(--malachite-accent-foreground)] hover:underline ${u}`,
74
+ children: r.text
75
+ }
76
+ );
77
+ const t = L(r.href);
78
+ return t ? /* @__PURE__ */ a(
79
+ y,
70
80
  {
71
- href: r,
81
+ href: t,
72
82
  target: "_blank",
73
83
  rel: "noopener noreferrer",
74
84
  color: "dimmed",
@@ -76,22 +86,22 @@ const T = 0.5, q = ({
76
86
  underline: !1,
77
87
  className: "shrink-0 whitespace-nowrap inline-flex items-center gap-1 relative top-[3px]",
78
88
  children: [
79
- t.text,
80
- /* @__PURE__ */ e(F, { size: 13 })
89
+ r.text,
90
+ /* @__PURE__ */ e(w, { size: 13 })
81
91
  ]
82
92
  }
83
93
  ) : null;
84
- }, B = ({ text: t, id: r }) => /* @__PURE__ */ e("span", { id: r, children: /* @__PURE__ */ e(h, { size: "xsmall", color: "dimmed", className: "pr-2 relative top-[3px]", children: t }) }), D = ({ text: t, id: r }) => /* @__PURE__ */ e("div", { className: "pl-2 text-left", id: r, children: /* @__PURE__ */ e(H, { color: "dimmed", size: "small", text: t ?? "", omitMargin: !0 }) }), E = ({
85
- text: t,
86
- required: r,
87
- requiredIndicator: i,
94
+ }, B = ({ text: r, id: t }) => /* @__PURE__ */ e("span", { id: t, children: /* @__PURE__ */ e(h, { size: "xsmall", color: "dimmed", className: "pr-2 relative top-[3px]", children: r }) }), D = ({ text: r, id: t }) => /* @__PURE__ */ e("div", { className: "pl-2 text-left", id: t, children: /* @__PURE__ */ e(_, { color: "dimmed", size: "small", text: r ?? "", omitMargin: !0 }) }), E = ({
95
+ text: r,
96
+ required: t,
97
+ requiredIndicator: o,
88
98
  tooltip: l,
89
- id: a,
90
- htmlFor: o
91
- }) => t ? /* @__PURE__ */ s("div", { className: "flex items-center", children: [
92
- /* @__PURE__ */ e("label", { id: a, htmlFor: o, children: /* @__PURE__ */ e(h, { size: "xsmall", color: "primary", fontWeight: "semi-bold", className: "pl-2", children: t }) }),
93
- i === "optional-suffix" && !r && /* @__PURE__ */ e("span", { "aria-hidden": "true", children: /* @__PURE__ */ e(h, { size: "xsmall", color: "dimmed", className: "ml-1", children: "(Optional)" }) }),
94
- i === "asterisk" && r && /* @__PURE__ */ e(
99
+ id: s,
100
+ htmlFor: i
101
+ }) => r ? /* @__PURE__ */ a("div", { className: "flex items-center", children: [
102
+ /* @__PURE__ */ e("label", { id: s, htmlFor: i, children: /* @__PURE__ */ e(h, { size: "xsmall", color: "primary", fontWeight: "semi-bold", className: "pl-2", children: r }) }),
103
+ o === "optional-suffix" && !t && /* @__PURE__ */ e("span", { "aria-hidden": "true", children: /* @__PURE__ */ e(h, { size: "xsmall", color: "dimmed", className: "ml-1", children: "(Optional)" }) }),
104
+ o === "asterisk" && t && /* @__PURE__ */ e(
95
105
  "span",
96
106
  {
97
107
  "aria-hidden": "true",
@@ -99,13 +109,13 @@ const T = 0.5, q = ({
99
109
  children: "*"
100
110
  }
101
111
  ),
102
- l && /* @__PURE__ */ e(k, { text: l, placement: "right", delay: T, children: /* @__PURE__ */ e(
112
+ l && /* @__PURE__ */ e(O, { text: l, placement: "right", delay: T, children: /* @__PURE__ */ e(
103
113
  "button",
104
114
  {
105
115
  type: "button",
106
- "aria-label": `More information about ${t}`,
107
- className: `ml-1 -mt-[2px] inline-flex items-center text-[var(--malachite-text-dimmed)] cursor-help ${R}`,
108
- children: /* @__PURE__ */ e($, { size: 14 })
116
+ "aria-label": `More information about ${r}`,
117
+ className: `ml-1 -mt-[2px] inline-flex items-center text-[var(--malachite-text-dimmed)] cursor-help ${u}`,
118
+ children: /* @__PURE__ */ e(F, { size: 14 })
109
119
  }
110
120
  ) })
111
121
  ] }) : /* @__PURE__ */ e("div", {});
@@ -1 +1 @@
1
- {"version":3,"file":"BaseFormComponent.js","sources":["../../src/components/BaseFormComponent.tsx"],"sourcesContent":["import React, { useId } from 'react';\nimport type { Control, FieldPath, FieldValues } from 'react-hook-form';\nimport ArrowUpRightIcon from './CustomIcons/ArrowUpRightIcon';\nimport InfoFilledIcon from './CustomIcons/InfoFilledIcon';\nimport FormItem from './FormItem';\nimport Link from './Link';\nimport { safeHref } from './safeHref';\nimport Spacer from './Spacer';\nimport Tooltip from './Tooltip';\nimport MarkdownBlock from './Typography/MarkdownBlock';\nimport Text from './Typography/Text';\nimport { FOCUS_RING } from './focusRing';\nimport { type FormItemChildren, NO_FIELD_ERROR, renderFormItemChildren } from './formItemState';\n\n/** Tooltip's own default of 1s reads as broken; 0.5 matches the rest of the library. */\nconst TOOLTIP_DELAY_SECONDS = 0.5;\n\n/** The ids that wire a form field's label / description / hint to its control. */\nexport interface FormFieldIds {\n controlId: string;\n labelId?: string;\n descriptionId?: string;\n hintId?: string;\n helperTextId?: string;\n describedBy?: string;\n}\n\ninterface FormFieldIdOptions {\n /** A caller-supplied id always wins, so consumers can label the control themselves. */\n id?: string;\n label?: string;\n description?: string;\n hint?: string;\n helperText?: boolean;\n}\n\n/**\n * Generates the stable ids a form field needs to be announced correctly. Shared by\n * `Input`, `TextArea`, `Dropdown` and `MultiSelect` so all four wire up identically.\n */\nexport const useFormFieldIds = ({\n id,\n label,\n description,\n hint,\n helperText = false,\n}: FormFieldIdOptions): FormFieldIds => {\n // useId() emits \":r0:\", and a colon is invalid in a CSS id selector, so a consumer's\n // querySelector(`#${id}`) throws. Stripping only the delimiters keeps ids unique.\n const generatedId = `mal-${useId().replace(/[^a-zA-Z0-9_-]/g, '')}-`;\n const controlId = id ?? `${generatedId}control`;\n const labelId = label ? `${generatedId}label` : undefined;\n const descriptionId = description ? `${generatedId}description` : undefined;\n const hintId = hint ? `${generatedId}hint` : undefined;\n const helperTextId = helperText ? `${generatedId}helper` : undefined;\n const describedBy =\n [descriptionId, hintId, helperTextId].filter(Boolean).join(' ') || undefined;\n\n return { controlId, labelId, descriptionId, hintId, helperTextId, describedBy };\n};\n\ninterface BaseFormComponentProps {\n label?: string;\n description?: string;\n hint?: string;\n rightSideContent?: RightSideContent;\n tooltip?: string;\n required?: boolean;\n requiredIndicator?: RequiredIndicator;\n name: FieldPath<FieldValues>;\n defaultValue?: string;\n loading?: boolean;\n formControl?: Control<FieldValues, unknown>;\n loadingComponent: React.ReactNode;\n children?: FormItemChildren;\n /** Accessibility wiring, from `useFormFieldIds` — see `FormFieldIds`. */\n controlId?: string;\n labelId?: string;\n descriptionId?: string;\n hintId?: string;\n fullWidth?: boolean;\n}\n\nconst BaseFormComponent: React.FC<BaseFormComponentProps> = ({\n label,\n hint,\n rightSideContent,\n tooltip,\n name,\n description,\n required = false,\n requiredIndicator = 'optional-suffix',\n loading = false,\n formControl,\n loadingComponent,\n children,\n controlId,\n labelId,\n descriptionId,\n hintId,\n fullWidth = false,\n}) => {\n const finalComponent = formControl ? (\n <FormItem control={formControl} name={name}>\n {children}\n </FormItem>\n ) : (\n renderFormItemChildren(children, NO_FIELD_ERROR)\n );\n\n const topHeader = () => (\n <Spacer direction=\"vertical\" size={4} align=\"stretch\">\n <div className=\"flex justify-between items-start\">\n <Label\n text={label}\n required={required}\n requiredIndicator={requiredIndicator}\n tooltip={tooltip}\n id={labelId}\n // No control is rendered while loading, so an htmlFor would be an orphan.\n htmlFor={loading ? undefined : controlId}\n />\n {(hint || rightSideContent) && (\n <div className=\"flex items-start gap-2\">\n {hint && <Hint text={hint} id={hintId} />}\n {rightSideContent && <RightSide {...rightSideContent} />}\n </div>\n )}\n </div>\n {description && <Description text={description} id={descriptionId} />}\n </Spacer>\n );\n\n const showHeader = label || hint || description || rightSideContent;\n\n return (\n <Spacer direction=\"vertical\" size={6} align=\"stretch\" fullWidth={fullWidth}>\n {showHeader && topHeader()}\n {!loading ? finalComponent : loadingComponent}\n </Spacer>\n );\n};\n\n/**\n * What sits at the far end of a field's label row. Data rather than a node, so nothing\n * arbitrary reaches the row; discriminated so a further kind is additive. Plain text is\n * already `hint`.\n */\nexport type RightSideContent = {\n type?: 'link';\n text: string;\n href: string;\n};\n\nconst RightSide = (content: RightSideContent) => {\n const safe = safeHref(content.href);\n\n if (!safe) {\n return null;\n }\n\n return (\n <Link\n href={safe}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n color=\"dimmed\"\n size=\"xsmall\"\n underline={false}\n className=\"shrink-0 whitespace-nowrap inline-flex items-center gap-1 relative top-[3px]\"\n >\n {content.text}\n <ArrowUpRightIcon size={13} />\n </Link>\n );\n};\n\nconst Hint = ({ text, id }: { text?: string; id?: string }) => {\n return (\n <span id={id}>\n <Text size=\"xsmall\" color=\"dimmed\" className=\"pr-2 relative top-[3px]\">\n {text}\n </Text>\n </span>\n );\n};\n\nconst Description = ({ text, id }: { text?: string; id?: string }) => {\n return (\n <div className=\"pl-2 text-left\" id={id}>\n <MarkdownBlock color=\"dimmed\" size=\"small\" text={text ?? ''} omitMargin={true} />\n </div>\n );\n};\n\nexport type RequiredIndicator = 'optional-suffix' | 'asterisk' | 'none';\n\nconst Label = ({\n text,\n required,\n requiredIndicator,\n tooltip,\n id,\n htmlFor,\n}: {\n text?: string;\n required: boolean;\n requiredIndicator: RequiredIndicator;\n tooltip?: string;\n id?: string;\n htmlFor?: string;\n}) => {\n return text ? (\n <div className=\"flex items-center\">\n <label id={id} htmlFor={htmlFor}>\n <Text size=\"xsmall\" color=\"primary\" fontWeight=\"semi-bold\" className=\"pl-2\">\n {text}\n </Text>\n </label>\n {/* Sibling of the <label>, not a child: Testing Library's getByLabelText matches\n on textContent, so a marker inside it breaks getByLabelText('<label>'). */}\n {requiredIndicator === 'optional-suffix' && !required && (\n <span aria-hidden=\"true\">\n <Text size=\"xsmall\" color=\"dimmed\" className=\"ml-1\">\n (Optional)\n </Text>\n </span>\n )}\n {requiredIndicator === 'asterisk' && required && (\n <span\n aria-hidden=\"true\"\n className=\"ml-1 text-[var(--malachite-destructive-foreground,rgb(196,62,62))]\"\n >\n *\n </span>\n )}\n {tooltip && (\n <Tooltip text={tooltip} placement=\"right\" delay={TOOLTIP_DELAY_SECONDS}>\n <button\n type=\"button\"\n aria-label={`More information about ${text}`}\n className={`ml-1 -mt-[2px] inline-flex items-center text-[var(--malachite-text-dimmed)] cursor-help ${FOCUS_RING}`}\n >\n <InfoFilledIcon size={14} />\n </button>\n </Tooltip>\n )}\n </div>\n ) : (\n <div />\n );\n};\n\nexport default BaseFormComponent;\n"],"names":["TOOLTIP_DELAY_SECONDS","useFormFieldIds","id","label","description","hint","helperText","generatedId","useId","controlId","labelId","descriptionId","hintId","helperTextId","describedBy","BaseFormComponent","rightSideContent","tooltip","name","required","requiredIndicator","loading","formControl","loadingComponent","children","fullWidth","finalComponent","jsx","FormItem","renderFormItemChildren","NO_FIELD_ERROR","jsxs","Spacer","Label","Hint","RightSide","Description","content","safe","safeHref","Link","ArrowUpRightIcon","text","Text","MarkdownBlock","htmlFor","Tooltip","FOCUS_RING","InfoFilledIcon"],"mappings":"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"}
1
+ {"version":3,"file":"BaseFormComponent.js","sources":["../../src/components/BaseFormComponent.tsx"],"sourcesContent":["import React, { useId } from 'react';\nimport type { Control, FieldPath, FieldValues } from 'react-hook-form';\nimport ArrowUpRightIcon from './CustomIcons/ArrowUpRightIcon';\nimport InfoFilledIcon from './CustomIcons/InfoFilledIcon';\nimport FormItem from './FormItem';\nimport Link from './Link';\nimport Spacer from './Spacer';\nimport Tooltip from './Tooltip';\nimport MarkdownBlock from './Typography/MarkdownBlock';\nimport Text from './Typography/Text';\nimport { FOCUS_RING } from './focusRing';\nimport { type FormItemChildren, NO_FIELD_ERROR, renderFormItemChildren } from './formItemState';\nimport { safeHref } from './safeHref';\n\n/** Tooltip's own default of 1s reads as broken; 0.5 matches the rest of the library. */\nconst TOOLTIP_DELAY_SECONDS = 0.5;\n\n/** The ids that wire a form field's label / description / hint to its control. */\nexport interface FormFieldIds {\n controlId: string;\n labelId?: string;\n descriptionId?: string;\n hintId?: string;\n helperTextId?: string;\n describedBy?: string;\n}\n\ninterface FormFieldIdOptions {\n /** A caller-supplied id always wins, so consumers can label the control themselves. */\n id?: string;\n label?: string;\n description?: string;\n hint?: string;\n helperText?: boolean;\n}\n\n/**\n * Generates the stable ids a form field needs to be announced correctly. Shared by\n * `Input`, `TextArea`, `Dropdown` and `MultiSelect` so all four wire up identically.\n */\nexport const useFormFieldIds = ({\n id,\n label,\n description,\n hint,\n helperText = false,\n}: FormFieldIdOptions): FormFieldIds => {\n // useId() emits \":r0:\", and a colon is invalid in a CSS id selector, so a consumer's\n // querySelector(`#${id}`) throws. Stripping only the delimiters keeps ids unique.\n const generatedId = `mal-${useId().replace(/[^a-zA-Z0-9_-]/g, '')}-`;\n const controlId = id ?? `${generatedId}control`;\n const labelId = label ? `${generatedId}label` : undefined;\n const descriptionId = description ? `${generatedId}description` : undefined;\n const hintId = hint ? `${generatedId}hint` : undefined;\n const helperTextId = helperText ? `${generatedId}helper` : undefined;\n const describedBy =\n [descriptionId, hintId, helperTextId].filter(Boolean).join(' ') || undefined;\n\n return { controlId, labelId, descriptionId, hintId, helperTextId, describedBy };\n};\n\ninterface BaseFormComponentProps {\n label?: string;\n description?: string;\n hint?: string;\n rightSideContent?: RightSideContent;\n tooltip?: string;\n required?: boolean;\n requiredIndicator?: RequiredIndicator;\n name: FieldPath<FieldValues>;\n defaultValue?: string;\n loading?: boolean;\n formControl?: Control<FieldValues, unknown>;\n loadingComponent: React.ReactNode;\n children?: FormItemChildren;\n /** Accessibility wiring, from `useFormFieldIds` — see `FormFieldIds`. */\n controlId?: string;\n labelId?: string;\n descriptionId?: string;\n hintId?: string;\n fullWidth?: boolean;\n}\n\nconst BaseFormComponent: React.FC<BaseFormComponentProps> = ({\n label,\n hint,\n rightSideContent,\n tooltip,\n name,\n description,\n required = false,\n requiredIndicator = 'optional-suffix',\n loading = false,\n formControl,\n loadingComponent,\n children,\n controlId,\n labelId,\n descriptionId,\n hintId,\n fullWidth = false,\n}) => {\n const finalComponent = formControl ? (\n <FormItem control={formControl} name={name}>\n {children}\n </FormItem>\n ) : (\n renderFormItemChildren(children, NO_FIELD_ERROR)\n );\n\n const topHeader = () => (\n <Spacer direction=\"vertical\" size={4} align=\"stretch\">\n <div className=\"flex justify-between items-start\">\n <Label\n text={label}\n required={required}\n requiredIndicator={requiredIndicator}\n tooltip={tooltip}\n id={labelId}\n // No control is rendered while loading, so an htmlFor would be an orphan.\n htmlFor={loading ? undefined : controlId}\n />\n {(hint || rightSideContent) && (\n <div className=\"flex items-start gap-2\">\n {hint && <Hint text={hint} id={hintId} />}\n {rightSideContent && <RightSide {...rightSideContent} />}\n </div>\n )}\n </div>\n {description && <Description text={description} id={descriptionId} />}\n </Spacer>\n );\n\n const showHeader = label || hint || description || rightSideContent;\n\n return (\n <Spacer direction=\"vertical\" size={6} align=\"stretch\" fullWidth={fullWidth}>\n {showHeader && topHeader()}\n {!loading ? finalComponent : loadingComponent}\n </Spacer>\n );\n};\n\n/**\n * What sits at the far end of a field's label row. Data rather than a node, so nothing\n * arbitrary reaches the row; discriminated so a further kind is additive. Plain text is\n * already `hint`.\n */\nexport type RightSideContent =\n | {\n type?: 'link';\n text: string;\n href: string;\n }\n | {\n /** An action on the form itself, such as showing a panel of help beside it. */\n type: 'button';\n text: string;\n onClick: () => void;\n };\n\nconst RightSide = (content: RightSideContent) => {\n if (content.type === 'button') {\n return (\n <button\n type=\"button\"\n onClick={content.onClick}\n className={`relative top-[3px] shrink-0 cursor-pointer whitespace-nowrap border-0 bg-transparent p-0 text-[length:var(--malachite-font-size-xsmall)] font-medium text-[var(--malachite-accent-foreground)] hover:underline ${FOCUS_RING}`}\n >\n {content.text}\n </button>\n );\n }\n\n const safe = safeHref(content.href);\n\n if (!safe) {\n return null;\n }\n\n return (\n <Link\n href={safe}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n color=\"dimmed\"\n size=\"xsmall\"\n underline={false}\n className=\"shrink-0 whitespace-nowrap inline-flex items-center gap-1 relative top-[3px]\"\n >\n {content.text}\n <ArrowUpRightIcon size={13} />\n </Link>\n );\n};\n\nconst Hint = ({ text, id }: { text?: string; id?: string }) => {\n return (\n <span id={id}>\n <Text size=\"xsmall\" color=\"dimmed\" className=\"pr-2 relative top-[3px]\">\n {text}\n </Text>\n </span>\n );\n};\n\nconst Description = ({ text, id }: { text?: string; id?: string }) => {\n return (\n <div className=\"pl-2 text-left\" id={id}>\n <MarkdownBlock color=\"dimmed\" size=\"small\" text={text ?? ''} omitMargin={true} />\n </div>\n );\n};\n\nexport type RequiredIndicator = 'optional-suffix' | 'asterisk' | 'none';\n\nconst Label = ({\n text,\n required,\n requiredIndicator,\n tooltip,\n id,\n htmlFor,\n}: {\n text?: string;\n required: boolean;\n requiredIndicator: RequiredIndicator;\n tooltip?: string;\n id?: string;\n htmlFor?: string;\n}) => {\n return text ? (\n <div className=\"flex items-center\">\n <label id={id} htmlFor={htmlFor}>\n <Text size=\"xsmall\" color=\"primary\" fontWeight=\"semi-bold\" className=\"pl-2\">\n {text}\n </Text>\n </label>\n {/* Sibling of the <label>, not a child: Testing Library's getByLabelText matches\n on textContent, so a marker inside it breaks getByLabelText('<label>'). */}\n {requiredIndicator === 'optional-suffix' && !required && (\n <span aria-hidden=\"true\">\n <Text size=\"xsmall\" color=\"dimmed\" className=\"ml-1\">\n (Optional)\n </Text>\n </span>\n )}\n {requiredIndicator === 'asterisk' && required && (\n <span\n aria-hidden=\"true\"\n className=\"ml-1 text-[var(--malachite-destructive-foreground,rgb(196,62,62))]\"\n >\n *\n </span>\n )}\n {tooltip && (\n <Tooltip text={tooltip} placement=\"right\" delay={TOOLTIP_DELAY_SECONDS}>\n <button\n type=\"button\"\n aria-label={`More information about ${text}`}\n className={`ml-1 -mt-[2px] inline-flex items-center text-[var(--malachite-text-dimmed)] cursor-help ${FOCUS_RING}`}\n >\n <InfoFilledIcon size={14} />\n </button>\n </Tooltip>\n )}\n </div>\n ) : (\n <div />\n );\n};\n\nexport default BaseFormComponent;\n"],"names":["TOOLTIP_DELAY_SECONDS","useFormFieldIds","id","label","description","hint","helperText","generatedId","useId","controlId","labelId","descriptionId","hintId","helperTextId","describedBy","BaseFormComponent","rightSideContent","tooltip","name","required","requiredIndicator","loading","formControl","loadingComponent","children","fullWidth","finalComponent","jsx","FormItem","renderFormItemChildren","NO_FIELD_ERROR","jsxs","Spacer","Label","Hint","RightSide","Description","content","FOCUS_RING","safe","safeHref","Link","ArrowUpRightIcon","text","Text","MarkdownBlock","htmlFor","Tooltip","InfoFilledIcon"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as e, jsxs as l, Fragment as V } from "react/jsx-runtime";
2
- import { useId as F, useState as b, useRef as n, useEffect as U, useCallback as W } from "react";
2
+ import { useId as F, useState as g, useRef as i, useEffect as U, useCallback as W } from "react";
3
3
  import { createPortal as ce } from "react-dom";
4
4
  import { ShadowRootWrapper as de } from "../ShadowRootWrapper.js";
5
5
  import me from "./CalloutStrip.js";
@@ -8,23 +8,24 @@ import { getDeepActiveElement as pe } from "./activeElement.js";
8
8
  import { GLASS_SURFACE_STYLE as fe } from "./glassSurface.js";
9
9
  import { MODAL_TRAY_BACKGROUND as xe, MODAL_TRAY_CAPTION_COLOR as he } from "./modalTray.js";
10
10
  import { useOverlayBehaviour as ve, OVERLAY_ICON_BUTTON_CLASSES as be } from "./overlayBehaviour.js";
11
- import { OVERLAY_ELEVATED_ATTR as ge, overlayIdentity as z } from "./portalOverlay.js";
12
- const G = {
11
+ import { OVERLAY_ELEVATED_ATTR as ge, overlayIdentity as G } from "./portalOverlay.js";
12
+ const q = {
13
13
  small: "420px",
14
14
  medium: "612px",
15
- large: "760px"
16
- }, g = 200, q = "flex-[0_1_auto] h-[min(600px,calc(100dvh-160px))]", Oe = ({
15
+ large: "760px",
16
+ fullscreen: "100%"
17
+ }, N = 200, z = "flex-[0_1_auto] h-[min(600px,calc(100dvh-160px))]", Oe = ({
17
18
  open: c,
18
19
  onClose: d,
19
20
  title: p,
20
21
  subtitle: f,
21
- icon: N,
22
- headerExtra: w,
23
- titleAdornment: y,
22
+ icon: w,
23
+ headerExtra: y,
24
+ titleAdornment: R,
24
25
  tray: K,
25
- headerDivider: R = !1,
26
- footer: _,
27
- size: A = "medium",
26
+ headerDivider: _ = !1,
27
+ footer: A,
28
+ size: x = "medium",
28
29
  surface: X = "card",
29
30
  showCloseButton: Z = !0,
30
31
  closeOnOverlayClick: H = !0,
@@ -37,19 +38,19 @@ const G = {
37
38
  "aria-labelledby": $,
38
39
  "aria-describedby": re,
39
40
  busy: o = !1,
40
- children: x
41
+ children: h
41
42
  }) => {
42
43
  var P;
43
- const k = F(), S = F(), m = X === "none", a = m ? void 0 : K, u = ((P = m ? void 0 : J) == null ? void 0 : P.filter((r) => r.message.trim())) ?? [], [h, O] = b(!1), [le, C] = b(!1), [te, E] = b(!1), L = n(null), s = n(null), t = n(null), D = n(o), M = n(d);
44
+ const k = F(), S = F(), m = X === "none", a = m ? void 0 : K, u = ((P = m ? void 0 : J) == null ? void 0 : P.filter((r) => r.message.trim())) ?? [], [v, O] = g(!1), [le, C] = g(!1), [te, E] = g(!1), L = i(null), s = i(null), t = i(null), D = i(o), M = i(d);
44
45
  U(() => {
45
46
  D.current = o, M.current = d;
46
47
  }, [o, d]);
47
48
  const B = W(() => {
48
49
  D.current || M.current();
49
- }, []), Y = n(null), {
50
+ }, []), Y = i(null), {
50
51
  ref: j,
51
- isTopmost: ie,
52
- zIndex: ne,
52
+ isTopmost: ne,
53
+ zIndex: ie,
53
54
  elevated: oe
54
55
  } = ve({
55
56
  open: c,
@@ -68,26 +69,26 @@ const G = {
68
69
  s.current = null, O(!0);
69
70
  });
70
71
  else {
71
- const i = pe();
72
- i && ((r = L.current) != null && r.contains(i)) && i.blur(), E(!0), O(!1), t.current = setTimeout(() => {
72
+ const n = pe();
73
+ n && ((r = L.current) != null && r.contains(n)) && n.blur(), E(!0), O(!1), t.current = setTimeout(() => {
73
74
  t.current = null, C(!1);
74
- }, g);
75
+ }, N);
75
76
  }
76
77
  return () => {
77
78
  s.current !== null && (cancelAnimationFrame(s.current), s.current = null), t.current && (clearTimeout(t.current), t.current = null);
78
79
  };
79
80
  }, [c]), !le)
80
81
  return null;
81
- let v = "relative flex max-h-full w-full flex-col overflow-hidden rounded-[20px] border border-solid border-[var(--malachite-card-border)] bg-[var(--malachite-card-background)] text-[var(--malachite-card-foreground)] outline-none [box-shadow:var(--malachite-shadow-glow,var(--malachite-shadow-lg))]";
82
- return m ? v = "relative flex max-h-full w-full flex-col outline-none" : a && (v = "relative flex max-h-full w-full flex-col gap-[4px] rounded-[20px] p-[4px] text-[var(--malachite-card-foreground)] outline-none [box-shadow:var(--malachite-shadow-glow,var(--malachite-shadow-lg))]"), ce(
82
+ let b = "relative flex max-h-full w-full flex-col overflow-hidden rounded-[20px] border border-solid border-[var(--malachite-card-border)] bg-[var(--malachite-card-background)] text-[var(--malachite-card-foreground)] outline-none [box-shadow:var(--malachite-shadow-glow,var(--malachite-shadow-lg))]";
83
+ return m ? b = "relative flex max-h-full w-full flex-col outline-none" : a && (b = "relative flex max-h-full w-full flex-col gap-[4px] rounded-[20px] p-[4px] text-[var(--malachite-card-foreground)] outline-none [box-shadow:var(--malachite-shadow-glow,var(--malachite-shadow-lg))]"), ce(
83
84
  // Host carries the stacking context — ShadowRootWrapper's `isolation` would otherwise
84
85
  // flatten our z-index. Not promoted to the top layer, so host dialogs can cover us.
85
86
  /* @__PURE__ */ e(
86
87
  de,
87
88
  {
88
89
  hostRef: Y,
89
- hostStyle: { position: "relative", zIndex: ne },
90
- hostProps: oe ? { ...z("modal"), [ge]: "" } : z("modal"),
90
+ hostStyle: { position: "relative", zIndex: ie },
91
+ hostProps: oe ? { ...G("modal"), [ge]: "" } : G("modal"),
91
92
  children: /* @__PURE__ */ l(
92
93
  "div",
93
94
  {
@@ -98,7 +99,7 @@ const G = {
98
99
  {
99
100
  "aria-hidden": "true",
100
101
  onClick: H ? B : void 0,
101
- className: `absolute inset-0 bg-[var(--malachite-overlay-scrim)] transition-opacity duration-200 ease-in-out ${h ? "opacity-100" : "opacity-0"}`
102
+ className: `absolute inset-0 bg-[var(--malachite-overlay-scrim)] transition-opacity duration-200 ease-in-out ${v ? "opacity-100" : "opacity-0"}`
102
103
  }
103
104
  ),
104
105
  /* @__PURE__ */ l(
@@ -107,9 +108,9 @@ const G = {
107
108
  ref: se,
108
109
  role: ee,
109
110
  "aria-hidden": te ? "true" : void 0,
110
- "aria-modal": ie ? "true" : void 0,
111
+ "aria-modal": ne ? "true" : void 0,
111
112
  "aria-describedby": [
112
- ...u.map((r, i) => `${S}-${i}`),
113
+ ...u.map((r, n) => `${S}-${n}`),
113
114
  re,
114
115
  a != null && a.caption ? k : null
115
116
  ].filter(Boolean).join(" ") || void 0,
@@ -117,17 +118,18 @@ const G = {
117
118
  tabIndex: -1,
118
119
  "aria-label": $ ? void 0 : ae ?? (typeof p == "string" ? p : void 0),
119
120
  "aria-labelledby": $,
120
- className: v,
121
+ className: b,
121
122
  style: {
122
- maxWidth: a ? `calc(${G[A]} + 8px)` : G[A],
123
+ maxWidth: a ? `calc(${q[x]} + 8px)` : q[x],
124
+ ...x === "fullscreen" ? { height: "100%" } : {},
123
125
  ...a ? {
124
126
  backgroundColor: xe[a.tone ?? "neutral"]
125
127
  } : {},
126
128
  // Inline: Tailwind scale/translate utilities silently break inside
127
129
  // shadow roots (see CLAUDE.md Shadow DOM caveats).
128
- transform: h ? "scale(1)" : "scale(0.96)",
129
- opacity: h ? 1 : 0,
130
- transition: `transform ${g}ms ease-out, opacity ${g}ms ease-out`
130
+ transform: v ? "scale(1)" : "scale(0.96)",
131
+ opacity: v ? 1 : 0,
132
+ transition: `transform ${N}ms ease-out, opacity ${N}ms ease-out`
131
133
  },
132
134
  children: [
133
135
  m ? (
@@ -136,29 +138,29 @@ const G = {
136
138
  /* @__PURE__ */ e(
137
139
  "div",
138
140
  {
139
- className: `min-h-0 ${I === "tall" ? q : "flex-1"} overflow-y-auto`,
140
- children: x
141
+ className: `min-h-0 ${I === "tall" ? z : "flex-1"} overflow-y-auto`,
142
+ children: h
141
143
  }
142
144
  )
143
145
  ) : /* @__PURE__ */ l(
144
146
  "div",
145
147
  {
146
- className: a ? "flex min-h-0 flex-col overflow-hidden rounded-[16px] border border-solid border-[var(--malachite-card-border)] bg-[var(--malachite-card-background)] [box-shadow:var(--malachite-shadow-glow-lg,var(--malachite-shadow-xl))]" : "contents",
148
+ className: a ? "flex min-h-0 flex-1 flex-col overflow-hidden rounded-[16px] border border-solid border-[var(--malachite-card-border)] bg-[var(--malachite-card-background)] [box-shadow:var(--malachite-shadow-glow-lg,var(--malachite-shadow-xl))]" : "contents",
147
149
  children: [
148
150
  /* @__PURE__ */ l(
149
151
  "div",
150
152
  {
151
- className: `flex gap-[20px] p-[20px] ${f ? "items-start" : "items-center"} ${R ? "border-0 border-b border-solid border-[var(--malachite-card-border)]" : ""}`,
153
+ className: `flex gap-[20px] p-[20px] ${f ? "items-start" : "items-center"} ${_ ? "border-0 border-b border-solid border-[var(--malachite-card-border)]" : ""}`,
152
154
  children: [
153
155
  /* @__PURE__ */ l("div", { className: "flex h-[18px] min-w-0 flex-1 items-center gap-[12px]", children: [
154
156
  /* @__PURE__ */ l("div", { className: "flex min-w-0 items-center gap-[8px]", children: [
155
- N ? /* @__PURE__ */ e("span", { className: "flex shrink-0 items-center", children: N }) : null,
157
+ w ? /* @__PURE__ */ e("span", { className: "flex shrink-0 items-center", children: w }) : null,
156
158
  /* @__PURE__ */ l("div", { className: "flex min-w-0 flex-col gap-[4px]", children: [
157
159
  /* @__PURE__ */ e("span", { className: "truncate text-[14px] font-medium leading-[1.25] tracking-[-0.14px] text-[var(--malachite-text-primary)]", children: p }),
158
160
  f ? /* @__PURE__ */ e("span", { className: "text-[13px] leading-[1.6] text-[var(--malachite-text-secondary)]", children: f }) : null
159
161
  ] })
160
162
  ] }),
161
- y ? /* @__PURE__ */ l(V, { children: [
163
+ R ? /* @__PURE__ */ l(V, { children: [
162
164
  /* @__PURE__ */ e(
163
165
  "span",
164
166
  {
@@ -166,10 +168,10 @@ const G = {
166
168
  className: "h-[12px] w-px shrink-0 bg-[var(--malachite-card-border)]"
167
169
  }
168
170
  ),
169
- /* @__PURE__ */ e("span", { className: "flex min-w-0 items-center overflow-hidden", children: y })
171
+ /* @__PURE__ */ e("span", { className: "flex min-w-0 items-center overflow-hidden", children: R })
170
172
  ] }) : null
171
173
  ] }),
172
- w ? /* @__PURE__ */ e("span", { className: "flex shrink-0 items-center", children: w }) : null,
174
+ y ? /* @__PURE__ */ e("span", { className: "flex shrink-0 items-center", children: y }) : null,
173
175
  Z ? /* @__PURE__ */ e(
174
176
  "button",
175
177
  {
@@ -187,34 +189,34 @@ const G = {
187
189
  /* @__PURE__ */ l(
188
190
  "div",
189
191
  {
190
- className: `min-h-0 ${I === "tall" ? q : "flex-1"} overflow-y-auto ${T && u.length === 0 ? "px-[20px]" : ""}`,
192
+ className: `min-h-0 ${I === "tall" ? z : "flex-1"} overflow-y-auto ${T && u.length === 0 ? "px-[20px]" : ""}`,
191
193
  children: [
192
- u.map((r, i) => /* @__PURE__ */ e(
194
+ u.map((r, n) => /* @__PURE__ */ e(
193
195
  me,
194
196
  {
195
- id: `${S}-${i}`,
197
+ id: `${S}-${n}`,
196
198
  type: r.type,
197
199
  message: r.message,
198
200
  renderLink: Q
199
201
  },
200
- i
202
+ n
201
203
  )),
202
204
  T ? /* @__PURE__ */ e(
203
205
  "div",
204
206
  {
205
- className: u.length > 0 ? "px-[20px] pt-[18px] pb-[20px]" : `pb-[20px] ${R ? "pt-[18px]" : ""}`,
206
- children: x
207
+ className: u.length > 0 ? "px-[20px] pt-[18px] pb-[20px]" : `pb-[20px] ${_ ? "pt-[18px]" : ""}`,
208
+ children: h
207
209
  }
208
- ) : x
210
+ ) : h
209
211
  ]
210
212
  }
211
213
  ),
212
- _ ? /* @__PURE__ */ e(
214
+ A ? /* @__PURE__ */ e(
213
215
  "div",
214
216
  {
215
217
  className: "flex min-h-[56px] shrink-0 items-center border-0 border-t border-solid border-[var(--malachite-card-border)] px-[12px]",
216
218
  style: fe,
217
- children: _
219
+ children: A
218
220
  }
219
221
  ) : null
220
222
  ]