@stackone/malachite 2.37.1 → 2.39.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.
Files changed (102) hide show
  1. package/COMPONENTS.md +7 -2
  2. package/dist/ShadowRootWrapper.js +33 -59
  3. package/dist/ShadowRootWrapper.js.map +1 -1
  4. package/dist/assets/icons/account.svg.js +2 -2
  5. package/dist/assets/icons/account.svg.js.map +1 -1
  6. package/dist/assets/icons/building.svg.js +6 -0
  7. package/dist/assets/icons/building.svg.js.map +1 -0
  8. package/dist/assets/icons/chevrons_up_down.svg.js +6 -0
  9. package/dist/assets/icons/chevrons_up_down.svg.js.map +1 -0
  10. package/dist/assets/icons/connector.svg.js +6 -0
  11. package/dist/assets/icons/connector.svg.js.map +1 -0
  12. package/dist/assets/icons/external_link.svg.js +6 -0
  13. package/dist/assets/icons/external_link.svg.js.map +1 -0
  14. package/dist/assets/icons/field_coverage.svg.js +6 -0
  15. package/dist/assets/icons/field_coverage.svg.js.map +1 -0
  16. package/dist/assets/icons/layout.svg.js +6 -0
  17. package/dist/assets/icons/layout.svg.js.map +1 -0
  18. package/dist/assets/icons/link.svg.js +6 -0
  19. package/dist/assets/icons/link.svg.js.map +1 -0
  20. package/dist/assets/icons/playground.svg.js +6 -0
  21. package/dist/assets/icons/playground.svg.js.map +1 -0
  22. package/dist/components/ActionMenu.js +142 -171
  23. package/dist/components/ActionMenu.js.map +1 -1
  24. package/dist/components/ActionMenuTagHost.js +19 -0
  25. package/dist/components/ActionMenuTagHost.js.map +1 -0
  26. package/dist/components/Breadcrumbs.js +7 -7
  27. package/dist/components/Breadcrumbs.js.map +1 -1
  28. package/dist/components/Chat/ChatModelPicker.js +64 -60
  29. package/dist/components/Chat/ChatModelPicker.js.map +1 -1
  30. package/dist/components/CustomIcons/BuildingIcon.js +16 -0
  31. package/dist/components/CustomIcons/BuildingIcon.js.map +1 -0
  32. package/dist/components/CustomIcons/ChevronsUpDownIcon.js +16 -0
  33. package/dist/components/CustomIcons/ChevronsUpDownIcon.js.map +1 -0
  34. package/dist/components/CustomIcons/ConnectorIcon.js +16 -0
  35. package/dist/components/CustomIcons/ConnectorIcon.js.map +1 -0
  36. package/dist/components/CustomIcons/ExternalLinkIcon.js +16 -0
  37. package/dist/components/CustomIcons/ExternalLinkIcon.js.map +1 -0
  38. package/dist/components/CustomIcons/FieldCoverageIcon.js +16 -0
  39. package/dist/components/CustomIcons/FieldCoverageIcon.js.map +1 -0
  40. package/dist/components/CustomIcons/LayoutIcon.js +16 -0
  41. package/dist/components/CustomIcons/LayoutIcon.js.map +1 -0
  42. package/dist/components/CustomIcons/LinkIcon.js +16 -0
  43. package/dist/components/CustomIcons/LinkIcon.js.map +1 -0
  44. package/dist/components/CustomIcons/PlaygroundIcon.js +16 -0
  45. package/dist/components/CustomIcons/PlaygroundIcon.js.map +1 -0
  46. package/dist/components/CustomIcons/index.js +303 -279
  47. package/dist/components/CustomIcons/index.js.map +1 -1
  48. package/dist/components/NavItem.js +158 -0
  49. package/dist/components/NavItem.js.map +1 -0
  50. package/dist/components/Popover.js +262 -0
  51. package/dist/components/Popover.js.map +1 -0
  52. package/dist/components/PopoverParts.js +46 -0
  53. package/dist/components/PopoverParts.js.map +1 -0
  54. package/dist/components/QueryInput.js +103 -97
  55. package/dist/components/QueryInput.js.map +1 -1
  56. package/dist/components/SearchTrigger.js +37 -0
  57. package/dist/components/SearchTrigger.js.map +1 -0
  58. package/dist/components/Sidebar.js +174 -0
  59. package/dist/components/Sidebar.js.map +1 -0
  60. package/dist/components/Tooltip.js +97 -92
  61. package/dist/components/Tooltip.js.map +1 -1
  62. package/dist/components/UsageCard.js +44 -0
  63. package/dist/components/UsageCard.js.map +1 -0
  64. package/dist/components/actionMenuKeyboard.js +23 -0
  65. package/dist/components/actionMenuKeyboard.js.map +1 -0
  66. package/dist/components/actionMenuStyles.js +88 -0
  67. package/dist/components/actionMenuStyles.js.map +1 -0
  68. package/dist/components/ariaState.js +5 -0
  69. package/dist/components/ariaState.js.map +1 -0
  70. package/dist/components/chromeStyles.js +6 -0
  71. package/dist/components/chromeStyles.js.map +1 -0
  72. package/dist/components/composeRefs.js +27 -0
  73. package/dist/components/composeRefs.js.map +1 -0
  74. package/dist/components/floatingMenuGeometry.js +74 -35
  75. package/dist/components/floatingMenuGeometry.js.map +1 -1
  76. package/dist/components/overlayBehaviour.js +90 -84
  77. package/dist/components/overlayBehaviour.js.map +1 -1
  78. package/dist/components/popoverTree.js +14 -0
  79. package/dist/components/popoverTree.js.map +1 -0
  80. package/dist/components/portalOverlay.js +28 -22
  81. package/dist/components/portalOverlay.js.map +1 -1
  82. package/dist/components/sidebarContext.js +9 -0
  83. package/dist/components/sidebarContext.js.map +1 -0
  84. package/dist/components/topLayer.js +18 -18
  85. package/dist/components/topLayer.js.map +1 -1
  86. package/dist/components/useActionMenuPosition.js +80 -0
  87. package/dist/components/useActionMenuPosition.js.map +1 -0
  88. package/dist/components/useHoverClose.js +18 -0
  89. package/dist/components/useHoverClose.js.map +1 -0
  90. package/dist/components/useLatestRef.js +11 -0
  91. package/dist/components/useLatestRef.js.map +1 -0
  92. package/dist/components/usePopoverPosition.js +62 -0
  93. package/dist/components/usePopoverPosition.js.map +1 -0
  94. package/dist/components/usePopoverTree.js +34 -0
  95. package/dist/components/usePopoverTree.js.map +1 -0
  96. package/dist/index.d.ts +394 -100
  97. package/dist/index.js +365 -335
  98. package/dist/index.js.map +1 -1
  99. package/dist/tailwind.css.js +1 -1
  100. package/dist/tailwindSheet.js +35 -0
  101. package/dist/tailwindSheet.js.map +1 -0
  102. package/package.json +1 -1
@@ -1,182 +1,153 @@
1
- import { jsxs as p, Fragment as I, jsx as i } from "react/jsx-runtime";
2
- import { useState as S, useRef as g, useCallback as T, useEffect as _, useLayoutEffect as B, useInsertionEffect as O } from "react";
3
- import { createPortal as W } from "react-dom";
4
- import { Button as H } from "./Button.js";
5
- import N from "./CustomIcons/MoreHorizontalIcon.js";
6
- import { pushEscapeLayer as Y } from "./escapeLayers.js";
7
- import { measureContentWidth as j, measureMenuGeometry as A, MENU_SPECS as l } from "./floatingMenuGeometry.js";
8
- import { ensureOverlayStyles as D, overlayIdentity as G } from "./portalOverlay.js";
9
- import { useTopLayer as $ } from "./topLayer.js";
10
- const F = {
11
- minWidth: 0,
12
- overflow: "hidden",
13
- textOverflow: "ellipsis"
14
- }, U = {
15
- padding: "6px 10px",
16
- fontSize: "var(--malachite-font-size-small, 13px)",
17
- color: "var(--malachite-text-dimmed, rgb(161, 161, 161))",
18
- userSelect: "none"
19
- }, Z = {
20
- small: { icon: 16, iconBox: "text-[16px] [&_svg]:size-[16px]!" },
21
- xsmall: { icon: 11, iconBox: "text-[11px] [&_svg]:size-[11px]!" }
22
- }, k = (o) => o.type === "divider" ? {
23
- height: 1,
24
- background: "var(--malachite-card-border, rgb(232, 232, 232))",
25
- margin: "4px 0"
26
- } : {
27
- display: "flex",
28
- alignItems: "center",
29
- gap: 8,
30
- padding: "6px 10px",
31
- borderRadius: 8,
32
- fontSize: "var(--malachite-font-size-small, 13px)",
33
- fontWeight: 400,
34
- color: o.danger ? "var(--malachite-text-destructive, rgb(237, 53, 54))" : "var(--malachite-text-primary, rgb(10, 10, 10))",
35
- cursor: o.disabled ? "not-allowed" : "pointer",
36
- opacity: o.disabled ? 0.5 : 1,
37
- userSelect: "none",
38
- whiteSpace: "nowrap",
39
- overflow: "hidden",
40
- background: "transparent",
41
- border: "none",
42
- textAlign: "left",
43
- width: "100%"
44
- }, re = ({
45
- items: o,
46
- label: h = "More actions",
47
- triggerIcon: L,
48
- minWidth: m,
1
+ import { jsxs as h, jsx as r, Fragment as B } from "react/jsx-runtime";
2
+ import S, { useState as j, useRef as E, useEffect as z, useMemo as G } from "react";
3
+ import { createPortal as $ } from "react-dom";
4
+ import { Button as X } from "./Button.js";
5
+ import Z from "./CustomIcons/MoreHorizontalIcon.js";
6
+ import { focusEdge as _, edgeForTriggerKey as q, itemForMenuKey as J, enabledItems as Q } from "./actionMenuKeyboard.js";
7
+ import { TRIGGER_SIZES as U, EMPTY_ITEM_STYLE as V, menuStyle as W, itemStyle as x, TEXT_ITEM_STYLE as H, ITEM_CLASS as ee, ITEM_ICON_STYLE as ne, LABEL_STYLE as oe } from "./actionMenuStyles.js";
8
+ import { getDeepActiveElement as re } from "./activeElement.js";
9
+ import { elementRef as te, composeRefs as le, chainHandler as b } from "./composeRefs.js";
10
+ import { pushEscapeLayer as se } from "./escapeLayers.js";
11
+ import { overlayIdentity as ae } from "./portalOverlay.js";
12
+ import { useActionMenuPosition as ce } from "./useActionMenuPosition.js";
13
+ const ie = S.lazy(() => import("./ActionMenuTagHost.js")), ue = (n) => n != null && n !== !1, g = (n) => n.stopPropagation(), de = ({ item: n, tone: f, onSelect: M }) => n.type === "divider" ? /* @__PURE__ */ r("div", { style: x(n) }) : n.type === "text" ? /* @__PURE__ */ r("div", { role: "menuitem", "aria-disabled": "true", style: H, children: n.label }) : /* @__PURE__ */ h(
14
+ "button",
15
+ {
16
+ type: "button",
17
+ role: "menuitem",
18
+ disabled: n.disabled,
19
+ title: n.tooltip,
20
+ "aria-current": n.current ? "page" : void 0,
21
+ className: ee,
22
+ onClick: () => M(n),
23
+ style: x(n, f),
24
+ children: [
25
+ n.icon && /* @__PURE__ */ r("span", { style: ne, children: n.icon }),
26
+ /* @__PURE__ */ r("span", { style: oe, children: n.label }),
27
+ ue(n.tag) ? /* @__PURE__ */ r(S.Suspense, { fallback: null, children: /* @__PURE__ */ r(ie, { children: n.tag }) }) : null
28
+ ]
29
+ }
30
+ ), ke = ({
31
+ items: n,
32
+ label: f = "More actions",
33
+ triggerIcon: M,
34
+ minWidth: K,
49
35
  size: v = "small",
50
- showLabel: d = !1,
51
- variant: M = "outline"
36
+ showLabel: y = !1,
37
+ variant: C = "outline",
38
+ trigger: a,
39
+ placement: I = "bottom-end",
40
+ offset: L,
41
+ tone: P = "default"
52
42
  }) => {
53
- const y = Z[v], [r, c] = S(!1), [t, x] = S(null), f = g(null), a = g(null), u = g(void 0);
54
- $(a, r);
55
- const s = T(() => {
56
- const e = f.current;
57
- e && (u.current = j(a.current) ?? u.current, x(
58
- A(a.current, {
59
- triggerRect: e.getBoundingClientRect(),
60
- preset: "options",
61
- minWidth: m,
62
- contentWidth: u.current,
63
- viewportWidth: window.innerWidth,
64
- viewportHeight: window.innerHeight
65
- })
66
- ));
67
- }, [m]);
68
- _(() => {
69
- if (!r) {
70
- x(null), u.current = void 0;
43
+ const T = U[v], [t, c] = j(!1), u = E(null), i = E(null), p = E(null), d = E(!1), O = ce(t, u, i, {
44
+ minWidth: K,
45
+ placement: I,
46
+ offset: L
47
+ });
48
+ z(() => {
49
+ if (!t)
71
50
  return;
72
- }
73
- s();
74
- const e = () => s(), n = (P) => {
75
- var w, E;
76
- const b = P.target;
77
- !((w = f.current) != null && w.contains(b)) && !((E = a.current) != null && E.contains(b)) && c(!1);
78
- }, C = Y(() => c(!1));
79
- return window.addEventListener("scroll", e, !0), window.addEventListener("resize", e), document.addEventListener("mousedown", n, !0), () => {
80
- window.removeEventListener("scroll", e, !0), window.removeEventListener("resize", e), document.removeEventListener("mousedown", n, !0), C();
51
+ p.current && (_(i.current, p.current), p.current = null);
52
+ const e = (s) => {
53
+ const m = s.composedPath();
54
+ [u.current, i.current].some(
55
+ (R) => R && m.includes(R)
56
+ ) || (d.current = !1, c(!1));
57
+ };
58
+ document.addEventListener("mousedown", e, !0);
59
+ const o = se(() => c(!1));
60
+ return () => {
61
+ var m;
62
+ document.removeEventListener("mousedown", e, !0), o();
63
+ const s = re();
64
+ d.current && (!s || s === document.body) && ((m = u.current) == null || m.focus({ preventScroll: !0 })), d.current = !1;
81
65
  };
82
- }, [r, s]), B(() => {
83
- r && a.current && s();
84
- }, [r, s]);
85
- const R = (e) => {
86
- var n;
87
- e.type === "divider" || e.disabled || ((n = e.onClick) == null || n.call(e), c(!1));
88
- };
89
- O(() => {
90
- r && D();
91
- }, [r]);
92
- const z = {
93
- position: "fixed",
94
- top: (t == null ? void 0 : t.top) ?? 0,
95
- left: (t == null ? void 0 : t.left) ?? 0,
96
- width: t == null ? void 0 : t.width,
97
- maxHeight: t == null ? void 0 : t.maxHeight,
98
- overflowY: "auto",
99
- background: l.options.background,
100
- border: l.options.border,
101
- borderRadius: l.options.radius,
102
- boxShadow: l.options.shadow,
103
- padding: l.options.padding,
104
- display: "flex",
105
- flexDirection: "column",
106
- gap: 2,
107
- visibility: t ? "visible" : "hidden",
108
- opacity: t ? 1 : 0
109
- };
110
- return /* @__PURE__ */ p(I, { children: [
111
- /* @__PURE__ */ p(
112
- H,
113
- {
114
- ref: f,
115
- type: "button",
116
- variant: M,
117
- size: v,
118
- iconOnly: !d,
119
- "aria-label": d ? void 0 : h,
120
- "aria-haspopup": "menu",
121
- "aria-expanded": r,
122
- className: `shrink-0${d ? " gap-2" : ""}`,
123
- onClick: (e) => {
124
- e.stopPropagation(), c((n) => !n);
125
- },
126
- onMouseDown: (e) => e.stopPropagation(),
127
- children: [
128
- /* @__PURE__ */ i("span", { className: `inline-flex items-center justify-center ${y.iconBox}`, children: L ?? /* @__PURE__ */ i(N, { size: y.icon }) }),
129
- d && /* @__PURE__ */ i("span", { children: h })
130
- ]
66
+ }, [t]);
67
+ const k = (e) => {
68
+ e.stopPropagation();
69
+ const o = !t && e.detail === 0;
70
+ p.current = o ? "first" : null, c((s) => !s);
71
+ }, D = (e) => {
72
+ if (e.key === "Tab" && t) {
73
+ c(!1);
74
+ return;
75
+ }
76
+ const o = q(e.key, I);
77
+ if (o) {
78
+ if (e.preventDefault(), t) {
79
+ _(i.current, o);
80
+ return;
131
81
  }
132
- ),
133
- r && W(
134
- /* @__PURE__ */ p(
82
+ p.current = o, c(!0);
83
+ }
84
+ }, A = (e) => {
85
+ var s;
86
+ if (e.key === "Tab") {
87
+ d.current = !1, (s = u.current) == null || s.focus({ preventScroll: !0 }), c(!1);
88
+ return;
89
+ }
90
+ const o = J(Q(i.current), e.target, e.key);
91
+ o && (e.preventDefault(), o.focus());
92
+ }, F = (e) => {
93
+ var o;
94
+ e.type === "divider" || e.type === "text" || e.disabled || ((o = e.onClick) == null || o.call(e), c(!1));
95
+ }, w = a ? te(a) : void 0, N = G(
96
+ () => le(w, u),
97
+ [w]
98
+ ), l = a == null ? void 0 : a.props, Y = a ? S.cloneElement(a, {
99
+ ref: N,
100
+ "aria-haspopup": "menu",
101
+ "aria-expanded": t,
102
+ onClick: b(l == null ? void 0 : l.onClick, k),
103
+ onMouseDown: b(l == null ? void 0 : l.onMouseDown, g),
104
+ onKeyDown: b(l == null ? void 0 : l.onKeyDown, D)
105
+ }) : /* @__PURE__ */ h(
106
+ X,
107
+ {
108
+ ref: u,
109
+ type: "button",
110
+ variant: C,
111
+ size: v,
112
+ iconOnly: !y,
113
+ "aria-label": y ? void 0 : f,
114
+ "aria-haspopup": "menu",
115
+ "aria-expanded": t,
116
+ className: `shrink-0${y ? " gap-2" : ""}`,
117
+ onClick: k,
118
+ onMouseDown: g,
119
+ onKeyDown: D,
120
+ children: [
121
+ /* @__PURE__ */ r("span", { className: `inline-flex items-center justify-center ${T.iconBox}`, children: M ?? /* @__PURE__ */ r(Z, { size: T.icon }) }),
122
+ y && /* @__PURE__ */ r("span", { children: f })
123
+ ]
124
+ }
125
+ );
126
+ return /* @__PURE__ */ h(B, { children: [
127
+ Y,
128
+ t && $(
129
+ /* @__PURE__ */ h(
135
130
  "div",
136
131
  {
137
- ref: a,
132
+ ref: i,
138
133
  role: "menu",
139
- ...G("actionmenu"),
140
- style: z,
141
- onMouseDown: (e) => e.stopPropagation(),
142
- onClick: (e) => e.stopPropagation(),
134
+ tabIndex: -1,
135
+ "aria-label": f,
136
+ ...ae("actionmenu"),
137
+ style: W(O),
138
+ onMouseDown: g,
139
+ onClick: g,
140
+ onKeyDown: A,
141
+ onFocus: () => {
142
+ d.current = !0;
143
+ },
144
+ onBlur: (e) => {
145
+ var o;
146
+ (o = i.current) != null && o.contains(e.relatedTarget) || (d.current = !1);
147
+ },
143
148
  children: [
144
- o.length === 0 && // role="menu" takes menuitem children only, so the notice is one.
145
- /* @__PURE__ */ i("div", { role: "menuitem", "aria-disabled": "true", style: U, children: "No actions available" }),
146
- o.map(
147
- (e) => e.type === "divider" ? /* @__PURE__ */ i("div", { style: k(e) }, e.key) : /* @__PURE__ */ p(
148
- "button",
149
- {
150
- type: "button",
151
- role: "menuitem",
152
- disabled: e.disabled,
153
- title: e.tooltip,
154
- onClick: () => R(e),
155
- onMouseEnter: (n) => {
156
- e.disabled || (n.currentTarget.style.background = "rgba(0, 0, 0, 0.04)");
157
- },
158
- onMouseLeave: (n) => {
159
- n.currentTarget.style.background = "transparent";
160
- },
161
- style: k(e),
162
- children: [
163
- e.icon && /* @__PURE__ */ i(
164
- "span",
165
- {
166
- style: {
167
- display: "inline-flex",
168
- flexShrink: 0,
169
- color: "var(--malachite-icon-secondary, rgb(148, 148, 148))"
170
- },
171
- children: e.icon
172
- }
173
- ),
174
- /* @__PURE__ */ i("span", { style: F, children: e.label })
175
- ]
176
- },
177
- e.key
178
- )
179
- )
149
+ n.length === 0 && /* @__PURE__ */ r("div", { role: "menuitem", "aria-disabled": "true", style: V, children: "No actions available" }),
150
+ n.map((e) => /* @__PURE__ */ r(de, { item: e, tone: P, onSelect: F }, e.key))
180
151
  ]
181
152
  }
182
153
  ),
@@ -185,6 +156,6 @@ const F = {
185
156
  ] });
186
157
  };
187
158
  export {
188
- re as default
159
+ ke as default
189
160
  };
190
161
  //# sourceMappingURL=ActionMenu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ActionMenu.js","sources":["../../src/components/ActionMenu.tsx"],"sourcesContent":["import React, {\n useState,\n useRef,\n useEffect,\n useInsertionEffect,\n useLayoutEffect,\n useCallback,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport Button from './Button';\nimport type { ButtonVariant } from './Button';\nimport MoreHorizontalIcon from './CustomIcons/MoreHorizontalIcon';\nimport { pushEscapeLayer } from './escapeLayers';\nimport {\n MENU_SPECS,\n type MenuGeometry,\n measureContentWidth,\n measureMenuGeometry,\n} from './floatingMenuGeometry';\nimport { ensureOverlayStyles, overlayIdentity } from './portalOverlay';\nimport { useTopLayer } from './topLayer';\n\nconst LABEL_STYLE: React.CSSProperties = {\n minWidth: 0,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n};\n\nconst EMPTY_ITEM_STYLE: React.CSSProperties = {\n padding: '6px 10px',\n fontSize: 'var(--malachite-font-size-small, 13px)',\n color: 'var(--malachite-text-dimmed, rgb(161, 161, 161))',\n userSelect: 'none',\n};\n\nexport type ActionMenuItem =\n | {\n type?: 'item';\n key: string;\n label: React.ReactNode;\n onClick?: () => void;\n disabled?: boolean;\n danger?: boolean;\n tooltip?: string;\n icon?: React.ReactNode;\n }\n | {\n type: 'divider';\n key: string;\n };\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}\n\n// No box here: pinning `--malachite-control-height-small` beat Button's own `size-[28px]`, so a\n// theme that moved that token moved the kebab and left the Button behind. Only the glyph is\n// left to size, Button sizing one only for its `icon` prop.\nconst TRIGGER_SIZES: Record<'small' | 'xsmall', { icon: number; iconBox: string }> = {\n small: { icon: 16, iconBox: 'text-[16px] [&_svg]:size-[16px]!' },\n xsmall: { icon: 11, iconBox: 'text-[11px] [&_svg]:size-[11px]!' },\n};\n\nconst itemStyle = (item: ActionMenuItem): React.CSSProperties => {\n if (item.type === 'divider') {\n return {\n height: 1,\n background: 'var(--malachite-card-border, rgb(232, 232, 232))',\n margin: '4px 0',\n };\n }\n return {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '6px 10px',\n borderRadius: 8,\n fontSize: 'var(--malachite-font-size-small, 13px)',\n fontWeight: 400,\n color: item.danger\n ? 'var(--malachite-text-destructive, rgb(237, 53, 54))'\n : 'var(--malachite-text-primary, rgb(10, 10, 10))',\n cursor: item.disabled ? 'not-allowed' : 'pointer',\n opacity: item.disabled ? 0.5 : 1,\n userSelect: 'none',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n background: 'transparent',\n border: 'none',\n textAlign: 'left',\n width: '100%',\n };\n};\n\n/**\n * ActionMenu is a kebab-style overflow action menu. The trigger is a square\n * icon-only outline Button (three-dot icon by default); on click, a popover\n * appears with the given items. The popover is rendered to document.body via\n * createPortal so it escapes any Shadow DOM or overflow:hidden ancestors —\n * which is also why the popover is styled inline rather than with Tailwind:\n * the scoped stylesheet does not reach document.body.\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}) => {\n const trigger = TRIGGER_SIZES[size];\n const [open, setOpen] = useState(false);\n const [position, setPosition] = useState<MenuGeometry | null>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const popoverRef = useRef<HTMLDivElement>(null);\n const contentWidthRef = useRef<number | undefined>(undefined);\n\n useTopLayer(popoverRef, open);\n\n const updatePosition = useCallback(() => {\n const trigger = triggerRef.current;\n if (!trigger) {\n return;\n }\n contentWidthRef.current =\n measureContentWidth(popoverRef.current) ?? contentWidthRef.current;\n\n setPosition(\n measureMenuGeometry(popoverRef.current, {\n triggerRect: trigger.getBoundingClientRect(),\n preset: 'options',\n minWidth,\n contentWidth: contentWidthRef.current,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n }),\n );\n }, [minWidth]);\n\n useEffect(() => {\n if (!open) {\n setPosition(null);\n contentWidthRef.current = undefined;\n return;\n }\n updatePosition();\n const handleScrollOrResize = () => updatePosition();\n const handleClickOutside = (e: MouseEvent) => {\n const target = e.target as Node;\n if (!triggerRef.current?.contains(target) && !popoverRef.current?.contains(target)) {\n setOpen(false);\n }\n };\n // ESC via the shared layer stack: closes only the topmost overlay.\n const popEscapeLayer = pushEscapeLayer(() => setOpen(false));\n window.addEventListener('scroll', handleScrollOrResize, true);\n window.addEventListener('resize', handleScrollOrResize);\n document.addEventListener('mousedown', handleClickOutside, true);\n return () => {\n window.removeEventListener('scroll', handleScrollOrResize, true);\n window.removeEventListener('resize', handleScrollOrResize);\n document.removeEventListener('mousedown', handleClickOutside, true);\n popEscapeLayer();\n };\n }, [open, updatePosition]);\n\n useLayoutEffect(() => {\n if (open && popoverRef.current) {\n updatePosition();\n }\n }, [open, updatePosition]);\n\n const handleItemClick = (item: ActionMenuItem) => {\n if (item.type === 'divider' || item.disabled) {\n return;\n }\n item.onClick?.();\n setOpen(false);\n };\n\n useInsertionEffect(() => {\n if (open) {\n ensureOverlayStyles();\n }\n }, [open]);\n\n const popoverStyle: React.CSSProperties = {\n position: 'fixed',\n top: position?.top ?? 0,\n left: position?.left ?? 0,\n width: position?.width,\n maxHeight: position?.maxHeight,\n overflowY: 'auto',\n background: MENU_SPECS.options.background,\n border: MENU_SPECS.options.border,\n borderRadius: MENU_SPECS.options.radius,\n boxShadow: MENU_SPECS.options.shadow,\n padding: MENU_SPECS.options.padding,\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n visibility: position ? 'visible' : 'hidden',\n opacity: position ? 1 : 0,\n };\n\n return (\n <>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant={variant}\n size={size}\n iconOnly={!showLabel}\n // With the label drawn, it is already the accessible name; repeating it here\n // would have a screen reader announce it twice.\n aria-label={showLabel ? undefined : label}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n // The engaged border while open is each variant's own, keyed off aria-expanded\n // in the stylesheet — pinning the outline token here stripped it from every\n // other variant.\n //\n // Button only spaces the icon it owns via its `icon` prop; this one is a child,\n // so the gap is set here rather than left to collapse against the label. `gap-2`\n // because the stylesheet is precompiled: a utility no source file already uses\n // is never emitted, and the class then does nothing with nothing to show for it.\n className={`shrink-0${showLabel ? ' gap-2' : ''}`}\n onClick={(e) => {\n e.stopPropagation();\n setOpen((v) => !v);\n }}\n onMouseDown={(e) => e.stopPropagation()}\n >\n {/* `!important` because every CustomIcon writes its size inline; `text-[…]` for a\n glyph that is not an svg at all, which the svg rule never matches. */}\n <span className={`inline-flex items-center justify-center ${trigger.iconBox}`}>\n {triggerIcon ?? <MoreHorizontalIcon size={trigger.icon} />}\n </span>\n {showLabel && <span>{label}</span>}\n </Button>\n {open &&\n createPortal(\n <div\n ref={popoverRef}\n role=\"menu\"\n {...overlayIdentity('actionmenu')}\n style={popoverStyle}\n onMouseDown={(e) => e.stopPropagation()}\n onClick={(e) => e.stopPropagation()}\n >\n {items.length === 0 && (\n // role=\"menu\" takes menuitem children only, so the notice is one.\n <div role=\"menuitem\" aria-disabled=\"true\" style={EMPTY_ITEM_STYLE}>\n No actions available\n </div>\n )}\n {items.map((item) =>\n item.type === 'divider' ? (\n <div key={item.key} style={itemStyle(item)} />\n ) : (\n <button\n key={item.key}\n type=\"button\"\n role=\"menuitem\"\n disabled={item.disabled}\n title={item.tooltip}\n onClick={() => handleItemClick(item)}\n onMouseEnter={(e) => {\n if (!item.disabled) {\n (\n e.currentTarget as HTMLButtonElement\n ).style.background = 'rgba(0, 0, 0, 0.04)';\n }\n }}\n onMouseLeave={(e) => {\n (e.currentTarget as HTMLButtonElement).style.background =\n 'transparent';\n }}\n style={itemStyle(item)}\n >\n {/* Leading: every caller's icon names the action itself —\n copy, open, delete — and an icon that is part of an\n item's identity belongs where the eye starts, not\n right-aligned where it reads as a status column. */}\n {item.icon && (\n <span\n style={{\n display: 'inline-flex',\n flexShrink: 0,\n color: 'var(--malachite-icon-secondary, rgb(148, 148, 148))',\n }}\n >\n {item.icon}\n </span>\n )}\n <span style={LABEL_STYLE}>{item.label}</span>\n </button>\n ),\n )}\n </div>,\n document.body,\n )}\n </>\n );\n};\n\nexport default ActionMenu;\n"],"names":["LABEL_STYLE","EMPTY_ITEM_STYLE","TRIGGER_SIZES","itemStyle","item","ActionMenu","items","label","triggerIcon","minWidth","size","showLabel","variant","trigger","open","setOpen","useState","position","setPosition","triggerRef","useRef","popoverRef","contentWidthRef","useTopLayer","updatePosition","useCallback","measureContentWidth","measureMenuGeometry","useEffect","handleScrollOrResize","handleClickOutside","e","target","_a","_b","popEscapeLayer","pushEscapeLayer","useLayoutEffect","handleItemClick","useInsertionEffect","ensureOverlayStyles","popoverStyle","MENU_SPECS","jsxs","Fragment","Button","v","jsx","MoreHorizontalIcon","createPortal","overlayIdentity"],"mappings":";;;;;;;;;AAsBA,MAAMA,IAAmC;AAAA,EACrC,UAAU;AAAA,EACV,UAAU;AAAA,EACV,cAAc;AAClB,GAEMC,IAAwC;AAAA,EAC1C,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AAAA,EACP,YAAY;AAChB,GAqCMC,IAA+E;AAAA,EACjF,OAAO,EAAE,MAAM,IAAI,SAAS,mCAAA;AAAA,EAC5B,QAAQ,EAAE,MAAM,IAAI,SAAS,mCAAA;AACjC,GAEMC,IAAY,CAACC,MACXA,EAAK,SAAS,YACP;AAAA,EACH,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,QAAQ;AAAA,IAGT;AAAA,EACH,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,SAAS;AAAA,EACT,cAAc;AAAA,EACd,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,OAAOA,EAAK,SACN,wDACA;AAAA,EACN,QAAQA,EAAK,WAAW,gBAAgB;AAAA,EACxC,SAASA,EAAK,WAAW,MAAM;AAAA,EAC/B,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,OAAO;AAAA,GAYTC,KAAwC,CAAC;AAAA,EAC3C,OAAAC;AAAA,EACA,OAAAC,IAAQ;AAAA,EACR,aAAAC;AAAA,EACA,UAAAC;AAAA,EACA,MAAAC,IAAO;AAAA,EACP,WAAAC,IAAY;AAAA,EACZ,SAAAC,IAAU;AACd,MAAM;AACF,QAAMC,IAAUX,EAAcQ,CAAI,GAC5B,CAACI,GAAMC,CAAO,IAAIC,EAAS,EAAK,GAChC,CAACC,GAAUC,CAAW,IAAIF,EAA8B,IAAI,GAC5DG,IAAaC,EAA0B,IAAI,GAC3CC,IAAaD,EAAuB,IAAI,GACxCE,IAAkBF,EAA2B,MAAS;AAE5D,EAAAG,EAAYF,GAAYP,CAAI;AAE5B,QAAMU,IAAiBC,EAAY,MAAM;AACrC,UAAMZ,IAAUM,EAAW;AAC3B,IAAKN,MAGLS,EAAgB,UACZI,EAAoBL,EAAW,OAAO,KAAKC,EAAgB,SAE/DJ;AAAA,MACIS,EAAoBN,EAAW,SAAS;AAAA,QACpC,aAAaR,EAAQ,sBAAA;AAAA,QACrB,QAAQ;AAAA,QACR,UAAAJ;AAAA,QACA,cAAca,EAAgB;AAAA,QAC9B,eAAe,OAAO;AAAA,QACtB,gBAAgB,OAAO;AAAA,MAAA,CAC1B;AAAA,IAAA;AAAA,EAET,GAAG,CAACb,CAAQ,CAAC;AAEb,EAAAmB,EAAU,MAAM;AACZ,QAAI,CAACd,GAAM;AACP,MAAAI,EAAY,IAAI,GAChBI,EAAgB,UAAU;AAC1B;AAAA,IACJ;AACA,IAAAE,EAAA;AACA,UAAMK,IAAuB,MAAML,EAAA,GAC7BM,IAAqB,CAACC,MAAkB;;AAC1C,YAAMC,IAASD,EAAE;AACjB,MAAI,GAACE,IAAAd,EAAW,YAAX,QAAAc,EAAoB,SAASD,OAAW,GAACE,IAAAb,EAAW,YAAX,QAAAa,EAAoB,SAASF,OACvEjB,EAAQ,EAAK;AAAA,IAErB,GAEMoB,IAAiBC,EAAgB,MAAMrB,EAAQ,EAAK,CAAC;AAC3D,kBAAO,iBAAiB,UAAUc,GAAsB,EAAI,GAC5D,OAAO,iBAAiB,UAAUA,CAAoB,GACtD,SAAS,iBAAiB,aAAaC,GAAoB,EAAI,GACxD,MAAM;AACT,aAAO,oBAAoB,UAAUD,GAAsB,EAAI,GAC/D,OAAO,oBAAoB,UAAUA,CAAoB,GACzD,SAAS,oBAAoB,aAAaC,GAAoB,EAAI,GAClEK,EAAA;AAAA,IACJ;AAAA,EACJ,GAAG,CAACrB,GAAMU,CAAc,CAAC,GAEzBa,EAAgB,MAAM;AAClB,IAAIvB,KAAQO,EAAW,WACnBG,EAAA;AAAA,EAER,GAAG,CAACV,GAAMU,CAAc,CAAC;AAEzB,QAAMc,IAAkB,CAAClC,MAAyB;;AAC9C,IAAIA,EAAK,SAAS,aAAaA,EAAK,cAGpC6B,IAAA7B,EAAK,YAAL,QAAA6B,EAAA,KAAA7B,IACAW,EAAQ,EAAK;AAAA,EACjB;AAEA,EAAAwB,EAAmB,MAAM;AACrB,IAAIzB,KACA0B,EAAA;AAAA,EAER,GAAG,CAAC1B,CAAI,CAAC;AAET,QAAM2B,IAAoC;AAAA,IACtC,UAAU;AAAA,IACV,MAAKxB,KAAA,gBAAAA,EAAU,QAAO;AAAA,IACtB,OAAMA,KAAA,gBAAAA,EAAU,SAAQ;AAAA,IACxB,OAAOA,KAAA,gBAAAA,EAAU;AAAA,IACjB,WAAWA,KAAA,gBAAAA,EAAU;AAAA,IACrB,WAAW;AAAA,IACX,YAAYyB,EAAW,QAAQ;AAAA,IAC/B,QAAQA,EAAW,QAAQ;AAAA,IAC3B,cAAcA,EAAW,QAAQ;AAAA,IACjC,WAAWA,EAAW,QAAQ;AAAA,IAC9B,SAASA,EAAW,QAAQ;AAAA,IAC5B,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,IACL,YAAYzB,IAAW,YAAY;AAAA,IACnC,SAASA,IAAW,IAAI;AAAA,EAAA;AAG5B,SACI,gBAAA0B,EAAAC,GAAA,EACI,UAAA;AAAA,IAAA,gBAAAD;AAAA,MAACE;AAAA,MAAA;AAAA,QACG,KAAK1B;AAAA,QACL,MAAK;AAAA,QACL,SAAAP;AAAA,QACA,MAAAF;AAAA,QACA,UAAU,CAACC;AAAA,QAGX,cAAYA,IAAY,SAAYJ;AAAA,QACpC,iBAAc;AAAA,QACd,iBAAeO;AAAA,QASf,WAAW,WAAWH,IAAY,WAAW,EAAE;AAAA,QAC/C,SAAS,CAAC,MAAM;AACZ,YAAE,gBAAA,GACFI,EAAQ,CAAC+B,MAAM,CAACA,CAAC;AAAA,QACrB;AAAA,QACA,aAAa,CAAC,MAAM,EAAE,gBAAA;AAAA,QAItB,UAAA;AAAA,UAAA,gBAAAC,EAAC,QAAA,EAAK,WAAW,2CAA2ClC,EAAQ,OAAO,IACtE,UAAAL,KAAe,gBAAAuC,EAACC,GAAA,EAAmB,MAAMnC,EAAQ,KAAA,CAAM,GAC5D;AAAA,UACCF,KAAa,gBAAAoC,EAAC,QAAA,EAAM,UAAAxC,EAAA,CAAM;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAE9BO,KACGmC;AAAA,MACI,gBAAAN;AAAA,QAAC;AAAA,QAAA;AAAA,UACG,KAAKtB;AAAA,UACL,MAAK;AAAA,UACJ,GAAG6B,EAAgB,YAAY;AAAA,UAChC,OAAOT;AAAA,UACP,aAAa,CAAC,MAAM,EAAE,gBAAA;AAAA,UACtB,SAAS,CAAC,MAAM,EAAE,gBAAA;AAAA,UAEjB,UAAA;AAAA,YAAAnC,EAAM,WAAW;AAAA,YAEd,gBAAAyC,EAAC,SAAI,MAAK,YAAW,iBAAc,QAAO,OAAO9C,GAAkB,UAAA,uBAAA,CAEnE;AAAA,YAEHK,EAAM;AAAA,cAAI,CAACF,MACRA,EAAK,SAAS,YACV,gBAAA2C,EAAC,OAAA,EAAmB,OAAO5C,EAAUC,CAAI,EAAA,GAA/BA,EAAK,GAA6B,IAE5C,gBAAAuC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBAEG,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,UAAUvC,EAAK;AAAA,kBACf,OAAOA,EAAK;AAAA,kBACZ,SAAS,MAAMkC,EAAgBlC,CAAI;AAAA,kBACnC,cAAc,CAAC2B,MAAM;AACjB,oBAAK3B,EAAK,aAEF2B,EAAE,cACJ,MAAM,aAAa;AAAA,kBAE7B;AAAA,kBACA,cAAc,CAACA,MAAM;AAChB,oBAAAA,EAAE,cAAoC,MAAM,aACzC;AAAA,kBACR;AAAA,kBACA,OAAO5B,EAAUC,CAAI;AAAA,kBAMpB,UAAA;AAAA,oBAAAA,EAAK,QACF,gBAAA2C;AAAA,sBAAC;AAAA,sBAAA;AAAA,wBACG,OAAO;AAAA,0BACH,SAAS;AAAA,0BACT,YAAY;AAAA,0BACZ,OAAO;AAAA,wBAAA;AAAA,wBAGV,UAAA3C,EAAK;AAAA,sBAAA;AAAA,oBAAA;AAAA,oBAGd,gBAAA2C,EAAC,QAAA,EAAK,OAAO/C,GAAc,YAAK,MAAA,CAAM;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAlCjCI,EAAK;AAAA,cAAA;AAAA,YAmCd;AAAA,UAER;AAAA,QAAA;AAAA,MAAA;AAAA,MAEJ,SAAS;AAAA,IAAA;AAAA,EACb,GACR;AAER;"}
1
+ {"version":3,"file":"ActionMenu.js","sources":["../../src/components/ActionMenu.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport Button from './Button';\nimport type { ButtonVariant } from './Button';\nimport MoreHorizontalIcon from './CustomIcons/MoreHorizontalIcon';\nimport {\n type MenuEdge,\n edgeForTriggerKey,\n enabledItems,\n focusEdge,\n itemForMenuKey,\n} from './actionMenuKeyboard';\nimport {\n EMPTY_ITEM_STYLE,\n ITEM_CLASS,\n ITEM_ICON_STYLE,\n LABEL_STYLE,\n TEXT_ITEM_STYLE,\n TRIGGER_SIZES,\n type ActionMenuTone,\n itemStyle,\n menuStyle,\n} from './actionMenuStyles';\nimport type { ActionMenuItem, ActionMenuPlacement } from './actionMenuTypes';\nimport { getDeepActiveElement } from './activeElement';\nimport { chainHandler, composeRefs, elementRef } from './composeRefs';\nimport { pushEscapeLayer } from './escapeLayers';\nimport type { FloatingOffset } from './floatingMenuGeometry';\nimport { overlayIdentity } from './portalOverlay';\nimport { useActionMenuPosition } from './useActionMenuPosition';\n\nexport type { ActionMenuItem, ActionMenuPlacement, ActionMenuTone };\n\nexport interface ActionMenuProps {\n items: ActionMenuItem[];\n label?: string;\n triggerIcon?: React.ReactNode;\n minWidth?: number;\n size?: 'small' | 'xsmall';\n /**\n * Shows `label` beside the icon instead of leaving it to the accessible name. For a menu\n * that is the point of its corner rather than an overflow for a row that already reads —\n * a kebab nobody can name is only safe when the row it belongs to says what it acts on.\n */\n showLabel?: boolean;\n /** Trigger's Button variant. Defaults to `outline`, the overflow-kebab look. */\n variant?: ButtonVariant;\n trigger?: React.ReactElement;\n placement?: ActionMenuPlacement;\n offset?: FloatingOffset;\n /** `muted` reads items in the secondary text colour, as in a sidebar's menus. */\n tone?: ActionMenuTone;\n}\n\ntype TriggerHandlers = {\n onClick?: (event: React.MouseEvent) => void;\n onMouseDown?: (event: React.MouseEvent) => void;\n onKeyDown?: (event: React.KeyboardEvent) => void;\n};\n\n// Loaded only when an item has a tag: its shadow host pulls in the Tailwind sheet, which a menu\n// without tags should not pay for.\nconst ActionMenuTagHost = React.lazy(() => import('./ActionMenuTagHost'));\n\nconst hasTag = (tag: React.ReactNode) => tag !== undefined && tag !== null && tag !== false;\n\nconst stopPropagation = (event: React.SyntheticEvent) => event.stopPropagation();\n\nconst MenuItemRow: React.FC<{\n item: ActionMenuItem;\n tone: ActionMenuTone;\n onSelect: (item: ActionMenuItem) => void;\n}> = ({ item, tone, onSelect }) => {\n if (item.type === 'divider') {\n return <div style={itemStyle(item)} />;\n }\n if (item.type === 'text') {\n return (\n <div role=\"menuitem\" aria-disabled=\"true\" style={TEXT_ITEM_STYLE}>\n {item.label}\n </div>\n );\n }\n return (\n <button\n type=\"button\"\n role=\"menuitem\"\n disabled={item.disabled}\n title={item.tooltip}\n aria-current={item.current ? 'page' : undefined}\n className={ITEM_CLASS}\n onClick={() => onSelect(item)}\n style={itemStyle(item, tone)}\n >\n {item.icon && <span style={ITEM_ICON_STYLE}>{item.icon}</span>}\n <span style={LABEL_STYLE}>{item.label}</span>\n {hasTag(item.tag) ? (\n <React.Suspense fallback={null}>\n <ActionMenuTagHost>{item.tag}</ActionMenuTagHost>\n </React.Suspense>\n ) : null}\n </button>\n );\n};\n\n/**\n * ActionMenu is an overflow action menu. The trigger is a three-dot `Button` by default, or\n * your own control via `trigger`. The menu is portaled to document.body, so it escapes any\n * Shadow DOM or overflow:hidden ancestor, which is also why it is styled inline.\n */\nconst ActionMenu: React.FC<ActionMenuProps> = ({\n items,\n label = 'More actions',\n triggerIcon,\n minWidth,\n size = 'small',\n showLabel = false,\n variant = 'outline',\n trigger: customTrigger,\n placement = 'bottom-end',\n offset,\n tone = 'default',\n}) => {\n const triggerSize = TRIGGER_SIZES[size];\n const [open, setOpen] = useState(false);\n const triggerRef = useRef<HTMLElement | null>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const focusOnOpen = useRef<MenuEdge | null>(null);\n const focusInMenu = useRef(false);\n\n const position = useActionMenuPosition(open, triggerRef, menuRef, {\n minWidth,\n placement,\n offset,\n });\n\n useEffect(() => {\n if (!open) {\n return;\n }\n if (focusOnOpen.current) {\n focusEdge(menuRef.current, focusOnOpen.current);\n focusOnOpen.current = null;\n }\n const closeOnOutsidePress = (event: MouseEvent) => {\n const path = event.composedPath();\n const inside = [triggerRef.current, menuRef.current].some(\n (node) => node && path.includes(node),\n );\n if (!inside) {\n focusInMenu.current = false;\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', closeOnOutsidePress, true);\n const popEscapeLayer = pushEscapeLayer(() => setOpen(false));\n return () => {\n document.removeEventListener('mousedown', closeOnOutsidePress, true);\n popEscapeLayer();\n const active = getDeepActiveElement();\n if (focusInMenu.current && (!active || active === document.body)) {\n triggerRef.current?.focus({ preventScroll: true });\n }\n focusInMenu.current = false;\n };\n }, [open]);\n\n const toggleFromTrigger = (event: React.MouseEvent) => {\n event.stopPropagation();\n const openedFromKeyboard = !open && event.detail === 0;\n focusOnOpen.current = openedFromKeyboard ? 'first' : null;\n setOpen((v) => !v);\n };\n\n const handleTriggerKeyDown = (event: React.KeyboardEvent) => {\n if (event.key === 'Tab' && open) {\n setOpen(false);\n return;\n }\n const edge = edgeForTriggerKey(event.key, placement);\n if (!edge) {\n return;\n }\n event.preventDefault();\n if (open) {\n focusEdge(menuRef.current, edge);\n return;\n }\n focusOnOpen.current = edge;\n setOpen(true);\n };\n\n const handleMenuKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Tab') {\n focusInMenu.current = false;\n triggerRef.current?.focus({ preventScroll: true });\n setOpen(false);\n return;\n }\n const target = itemForMenuKey(enabledItems(menuRef.current), event.target, event.key);\n if (target) {\n event.preventDefault();\n target.focus();\n }\n };\n\n const selectItem = (item: ActionMenuItem) => {\n if (item.type === 'divider' || item.type === 'text' || item.disabled) {\n return;\n }\n item.onClick?.();\n setOpen(false);\n };\n\n const ownTriggerRef = customTrigger ? elementRef<HTMLElement>(customTrigger) : undefined;\n const composedTriggerRef = useMemo(\n () => composeRefs(ownTriggerRef, triggerRef),\n [ownTriggerRef],\n );\n const customHandlers = customTrigger?.props as TriggerHandlers | undefined;\n\n const triggerElement = customTrigger ? (\n React.cloneElement(customTrigger, {\n ref: composedTriggerRef,\n 'aria-haspopup': 'menu',\n 'aria-expanded': open,\n onClick: chainHandler(customHandlers?.onClick, toggleFromTrigger),\n onMouseDown: chainHandler(customHandlers?.onMouseDown, stopPropagation),\n onKeyDown: chainHandler(customHandlers?.onKeyDown, handleTriggerKeyDown),\n } as Record<string, unknown>)\n ) : (\n <Button\n ref={triggerRef as React.RefObject<HTMLButtonElement>}\n type=\"button\"\n variant={variant}\n size={size}\n iconOnly={!showLabel}\n aria-label={showLabel ? undefined : label}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n className={`shrink-0${showLabel ? ' gap-2' : ''}`}\n onClick={toggleFromTrigger}\n onMouseDown={stopPropagation}\n onKeyDown={handleTriggerKeyDown}\n >\n <span className={`inline-flex items-center justify-center ${triggerSize.iconBox}`}>\n {triggerIcon ?? <MoreHorizontalIcon size={triggerSize.icon} />}\n </span>\n {showLabel && <span>{label}</span>}\n </Button>\n );\n\n return (\n <>\n {triggerElement}\n {open &&\n createPortal(\n <div\n ref={menuRef}\n role=\"menu\"\n tabIndex={-1}\n aria-label={label}\n {...overlayIdentity('actionmenu')}\n style={menuStyle(position)}\n onMouseDown={stopPropagation}\n onClick={stopPropagation}\n onKeyDown={handleMenuKeyDown}\n onFocus={() => {\n focusInMenu.current = true;\n }}\n onBlur={(e) => {\n if (!menuRef.current?.contains(e.relatedTarget as Node | null)) {\n focusInMenu.current = false;\n }\n }}\n >\n {items.length === 0 && (\n <div role=\"menuitem\" aria-disabled=\"true\" style={EMPTY_ITEM_STYLE}>\n No actions available\n </div>\n )}\n {items.map((item) => (\n <MenuItemRow key={item.key} item={item} tone={tone} onSelect={selectItem} />\n ))}\n </div>,\n document.body,\n )}\n </>\n );\n};\n\nexport default ActionMenu;\n"],"names":["ActionMenuTagHost","React","hasTag","tag","stopPropagation","event","MenuItemRow","item","tone","onSelect","jsx","itemStyle","TEXT_ITEM_STYLE","jsxs","ITEM_CLASS","ITEM_ICON_STYLE","LABEL_STYLE","ActionMenu","items","label","triggerIcon","minWidth","size","showLabel","variant","customTrigger","placement","offset","triggerSize","TRIGGER_SIZES","open","setOpen","useState","triggerRef","useRef","menuRef","focusOnOpen","focusInMenu","position","useActionMenuPosition","useEffect","focusEdge","closeOnOutsidePress","path","node","popEscapeLayer","pushEscapeLayer","active","getDeepActiveElement","_a","toggleFromTrigger","openedFromKeyboard","v","handleTriggerKeyDown","edge","edgeForTriggerKey","handleMenuKeyDown","target","itemForMenuKey","enabledItems","selectItem","ownTriggerRef","elementRef","composedTriggerRef","useMemo","composeRefs","customHandlers","triggerElement","chainHandler","Button","MoreHorizontalIcon","Fragment","createPortal","overlayIdentity","menuStyle","EMPTY_ITEM_STYLE"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { jsx as n } from "react/jsx-runtime";
2
+ import { useRef as f, useState as c, useLayoutEffect as m } from "react";
3
+ import { createPortal as u } from "react-dom";
4
+ import { adoptTailwindSheet as d } from "../tailwindSheet.js";
5
+ import { TAG_HOST_PROPS as h, TAG_HOST_STYLE as i } from "./actionMenuStyles.js";
6
+ const R = ({ children: s }) => {
7
+ const t = f(null), [e, a] = c(null);
8
+ return m(() => {
9
+ const o = t.current;
10
+ if (!o)
11
+ return;
12
+ const r = o.shadowRoot ?? o.attachShadow({ mode: "open" });
13
+ d(r), a(r);
14
+ }, []), /* @__PURE__ */ n("span", { ref: t, style: i, ...h, children: e && u(s, e) });
15
+ };
16
+ export {
17
+ R as default
18
+ };
19
+ //# sourceMappingURL=ActionMenuTagHost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionMenuTagHost.js","sources":["../../src/components/ActionMenuTagHost.tsx"],"sourcesContent":["import type React from 'react';\nimport { useLayoutEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { adoptTailwindSheet } from '../tailwindSheet';\nimport { TAG_HOST_PROPS, TAG_HOST_STYLE } from './actionMenuStyles';\n\n// Only the Tailwind sheet: fonts and theme variables already reach the menu from the document,\n// so ShadowRootWrapper's font and theme payload would be dead weight here.\nconst ActionMenuTagHost: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const hostRef = useRef<HTMLSpanElement>(null);\n const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null);\n\n useLayoutEffect(() => {\n const host = hostRef.current;\n if (!host) {\n return;\n }\n const shadow = host.shadowRoot ?? host.attachShadow({ mode: 'open' });\n adoptTailwindSheet(shadow);\n setShadowRoot(shadow);\n }, []);\n\n return (\n <span ref={hostRef} style={TAG_HOST_STYLE} {...TAG_HOST_PROPS}>\n {shadowRoot && createPortal(children, shadowRoot)}\n </span>\n );\n};\n\nexport default ActionMenuTagHost;\n"],"names":["ActionMenuTagHost","children","hostRef","useRef","shadowRoot","setShadowRoot","useState","useLayoutEffect","host","shadow","adoptTailwindSheet","jsx","TAG_HOST_STYLE","TAG_HOST_PROPS","createPortal"],"mappings":";;;;;AAQA,MAAMA,IAA6D,CAAC,EAAE,UAAAC,QAAe;AACjF,QAAMC,IAAUC,EAAwB,IAAI,GACtC,CAACC,GAAYC,CAAa,IAAIC,EAA4B,IAAI;AAEpE,SAAAC,EAAgB,MAAM;AAClB,UAAMC,IAAON,EAAQ;AACrB,QAAI,CAACM;AACD;AAEJ,UAAMC,IAASD,EAAK,cAAcA,EAAK,aAAa,EAAE,MAAM,QAAQ;AACpE,IAAAE,EAAmBD,CAAM,GACzBJ,EAAcI,CAAM;AAAA,EACxB,GAAG,CAAA,CAAE,GAGD,gBAAAE,EAAC,QAAA,EAAK,KAAKT,GAAS,OAAOU,GAAiB,GAAGC,GAC1C,UAAAT,KAAcU,EAAab,GAAUG,CAAU,EAAA,CACpD;AAER;"}
@@ -1,12 +1,12 @@
1
1
  import { jsxs as u, jsx as a } from "react/jsx-runtime";
2
2
  import { Button as v } from "./Button.js";
3
- import N from "./CustomIcons/ArrowLeftIcon.js";
4
- import b from "./CustomIcons/CaretRightIcon.js";
3
+ import b from "./CustomIcons/ArrowLeftIcon.js";
4
+ import N from "./CustomIcons/CaretRightIcon.js";
5
5
  import B from "./Typography/Text.js";
6
6
  import { hasBackButton as $ } from "./breadcrumbsNav.js";
7
7
  import { FOCUS_RING as x } from "./focusRing.js";
8
8
  import { isSafeHref as y, safeHref as S } from "./safeHref.js";
9
- const A = `no-underline rounded-xs cursor-pointer transition-colors duration-200 ease-in-out hover:text-[var(--malachite-accent-foreground)] ${x}`, C = `flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--malachite-radius-10,10px)] no-underline bg-[var(--malachite-number-tag-background)] text-[var(--malachite-text-primary)] transition-colors duration-200 ease-in-out hover:bg-[var(--malachite-number-tag-emphasis-background)] ${x}`, g = (e, r) => y(e.href) ? r ? r(e) : /* @__PURE__ */ a("a", { ...e }) : e.children, f = (e, r) => typeof e == "string" ? /* @__PURE__ */ a(B, { size: "medium", fontWeight: "semi-bold", color: r, children: e }) : e, _ = ({
9
+ const A = `no-underline rounded-xs cursor-pointer transition-colors duration-200 ease-in-out hover:text-[var(--malachite-accent-foreground,rgb(2,122,66))] ${x}`, C = `flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--malachite-radius-10,10px)] no-underline bg-[var(--malachite-number-tag-background,rgba(0,0,0,0.08))] text-[var(--malachite-text-primary,rgb(10,10,10))] transition-colors duration-200 ease-in-out hover:bg-[var(--malachite-number-tag-emphasis-background,rgba(0,0,0,0.15))] ${x}`, g = (e, r) => y(e.href) ? r ? r(e) : /* @__PURE__ */ a("a", { ...e }) : e.children, f = (e, r) => typeof e == "string" ? /* @__PURE__ */ a(B, { size: "medium", fontWeight: "semi-bold", color: r, children: e }) : e, _ = ({
10
10
  label: e,
11
11
  onClick: r,
12
12
  disabled: m,
@@ -56,7 +56,7 @@ const A = `no-underline rounded-xs cursor-pointer transition-colors duration-200
56
56
  href: t.url,
57
57
  "aria-label": typeof l == "string" ? `Back to ${l}` : "Go back",
58
58
  className: C,
59
- children: /* @__PURE__ */ a(N, { size: 12 })
59
+ children: /* @__PURE__ */ a(b, { size: 12 })
60
60
  },
61
61
  m
62
62
  ),
@@ -72,7 +72,7 @@ const A = `no-underline rounded-xs cursor-pointer transition-colors duration-200
72
72
  {
73
73
  href: i.url,
74
74
  "aria-current": o ? "page" : void 0,
75
- className: `${A} ${o ? "text-[var(--malachite-text-primary)]" : "text-[var(--malachite-text-muted)]"}`,
75
+ className: `${A} ${o ? "text-[var(--malachite-text-primary,rgb(10,10,10))]" : "text-[var(--malachite-text-muted,rgb(76,75,75))]"}`,
76
76
  children: f(
77
77
  i.name,
78
78
  "inherit"
@@ -84,8 +84,8 @@ const A = `no-underline rounded-xs cursor-pointer transition-colors duration-200
84
84
  "span",
85
85
  {
86
86
  "aria-hidden": "true",
87
- className: "flex items-center text-[var(--malachite-text-dimmed)]",
88
- children: /* @__PURE__ */ a(b, { size: 13 })
87
+ className: "flex items-center text-[var(--malachite-text-dimmed,rgb(161,161,161))]",
88
+ children: /* @__PURE__ */ a(N, { size: 13 })
89
89
  }
90
90
  )
91
91
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"Breadcrumbs.js","sources":["../../src/components/Breadcrumbs.tsx"],"sourcesContent":["import React from 'react';\nimport Button from './Button';\nimport ArrowLeftIcon from './CustomIcons/ArrowLeftIcon';\nimport CaretRightIcon from './CustomIcons/CaretRightIcon';\nimport type { BreadcrumbLink, PageAction, RenderBreadcrumbLink } from './PageLayout/types';\nimport Text, { type TextColor } from './Typography/Text';\nimport { hasBackButton } from './breadcrumbsNav';\nimport { FOCUS_RING } from './focusRing';\nimport { isSafeHref, safeHref } from './safeHref';\n\nexport interface BreadcrumbsProps {\n links: BreadcrumbLink[];\n actions?: PageAction[];\n renderLink?: RenderBreadcrumbLink;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst CRUMB_LINK_CLASSES = `no-underline rounded-xs cursor-pointer transition-colors duration-200 ease-in-out hover:text-[var(--malachite-accent-foreground)] ${FOCUS_RING}`;\n\nconst BACK_BUTTON_CLASSES = `flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--malachite-radius-10,10px)] no-underline bg-[var(--malachite-number-tag-background)] text-[var(--malachite-text-primary)] transition-colors duration-200 ease-in-out hover:bg-[var(--malachite-number-tag-emphasis-background)] ${FOCUS_RING}`;\n\ntype LinkProps = Parameters<RenderBreadcrumbLink>[0];\n\nconst renderCrumbLink = (props: LinkProps, renderLink?: RenderBreadcrumbLink): React.ReactNode => {\n // An unsafe url degrades to plain text rather than a link that could run a script.\n if (!isSafeHref(props.href)) {\n return props.children;\n }\n return renderLink ? renderLink(props) : <a {...props} />;\n};\n\nconst renderCrumbName = (name: React.ReactNode, color: TextColor): React.ReactNode =>\n typeof name === 'string' ? (\n <Text size=\"medium\" fontWeight=\"semi-bold\" color={color}>\n {name}\n </Text>\n ) : (\n name\n );\n\nconst renderAction = (\n {\n label,\n onClick,\n disabled,\n variant,\n icon,\n href,\n target,\n tooltip,\n tooltipPlacement,\n }: PageAction,\n index: number,\n) => {\n const safe = safeHref(href);\n return (\n <Button\n key={`${index}-${label}`}\n size=\"small\"\n variant={variant ?? 'outline'}\n disabled={disabled}\n icon={icon}\n tooltip={tooltip}\n tooltipPlacement={tooltipPlacement}\n href={safe}\n target={safe ? (target ?? '_blank') : undefined}\n onClick={safe ? undefined : onClick}\n >\n {label}\n </Button>\n );\n};\n\nconst Breadcrumbs: React.FC<BreadcrumbsProps> = ({\n links,\n actions,\n renderLink,\n className = '',\n style,\n}) => {\n // PageLayout mounts this unconditionally, so returning markup would leave an empty\n // navigation landmark on every page without crumbs.\n if (links.length === 0 && !actions?.length) {\n return null;\n }\n\n const isTitle = links.length === 1 && Boolean(links[0].isRoot);\n const parentCrumb = hasBackButton(links) ? links[links.length - 2] : undefined;\n const parentName = parentCrumb?.name;\n const hasActions = Boolean(actions?.length);\n\n return (\n <div\n className={`flex items-center ${hasActions ? 'justify-between' : ''} ${className}`}\n style={style}\n >\n {isTitle ? (\n renderCrumbName(links[0].name, 'primary')\n ) : (\n <div className=\"flex items-center gap-4\">\n {parentCrumb?.url &&\n renderCrumbLink(\n {\n href: parentCrumb.url,\n 'aria-label':\n typeof parentName === 'string'\n ? `Back to ${parentName}`\n : 'Go back',\n className: BACK_BUTTON_CLASSES,\n children: <ArrowLeftIcon size={12} />,\n },\n renderLink,\n )}\n {links.length > 0 && (\n <nav aria-label=\"Breadcrumb\">\n <ol className=\"flex flex-wrap items-center gap-2 m-0 p-0 list-none\">\n {links.map((link, index) => {\n const isLast = index === links.length - 1;\n const color: TextColor = isLast ? 'primary' : 'muted';\n\n return (\n <li\n key={`${link.url ?? ''}-${index}`}\n className=\"flex items-center gap-2\"\n aria-current={isLast && !link.url ? 'page' : undefined}\n >\n {link.url\n ? renderCrumbLink(\n {\n href: link.url,\n 'aria-current': isLast\n ? 'page'\n : undefined,\n className: `${CRUMB_LINK_CLASSES} ${\n isLast\n ? 'text-[var(--malachite-text-primary)]'\n : 'text-[var(--malachite-text-muted)]'\n }`,\n children: renderCrumbName(\n link.name,\n 'inherit',\n ),\n },\n renderLink,\n )\n : renderCrumbName(link.name, color)}\n {!isLast && (\n <span\n aria-hidden=\"true\"\n className=\"flex items-center text-[var(--malachite-text-dimmed)]\"\n >\n <CaretRightIcon size={13} />\n </span>\n )}\n </li>\n );\n })}\n </ol>\n </nav>\n )}\n </div>\n )}\n {hasActions && (\n <div className=\"flex items-center gap-2\">{actions?.map(renderAction)}</div>\n )}\n </div>\n );\n};\n\nexport { Breadcrumbs };\nexport default Breadcrumbs;\n"],"names":["CRUMB_LINK_CLASSES","FOCUS_RING","BACK_BUTTON_CLASSES","renderCrumbLink","props","renderLink","isSafeHref","jsx","renderCrumbName","name","color","Text","renderAction","label","onClick","disabled","variant","icon","href","target","tooltip","tooltipPlacement","index","safe","safeHref","Button","Breadcrumbs","links","actions","className","style","isTitle","parentCrumb","hasBackButton","parentName","hasActions","jsxs","ArrowLeftIcon","link","isLast","CaretRightIcon"],"mappings":"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"}
1
+ {"version":3,"file":"Breadcrumbs.js","sources":["../../src/components/Breadcrumbs.tsx"],"sourcesContent":["import React from 'react';\nimport Button from './Button';\nimport ArrowLeftIcon from './CustomIcons/ArrowLeftIcon';\nimport CaretRightIcon from './CustomIcons/CaretRightIcon';\nimport type { BreadcrumbLink, PageAction, RenderBreadcrumbLink } from './PageLayout/types';\nimport Text, { type TextColor } from './Typography/Text';\nimport { hasBackButton } from './breadcrumbsNav';\nimport { FOCUS_RING } from './focusRing';\nimport { isSafeHref, safeHref } from './safeHref';\n\nexport interface BreadcrumbsProps {\n links: BreadcrumbLink[];\n actions?: PageAction[];\n renderLink?: RenderBreadcrumbLink;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst CRUMB_LINK_CLASSES = `no-underline rounded-xs cursor-pointer transition-colors duration-200 ease-in-out hover:text-[var(--malachite-accent-foreground,rgb(2,122,66))] ${FOCUS_RING}`;\n\nconst BACK_BUTTON_CLASSES = `flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--malachite-radius-10,10px)] no-underline bg-[var(--malachite-number-tag-background,rgba(0,0,0,0.08))] text-[var(--malachite-text-primary,rgb(10,10,10))] transition-colors duration-200 ease-in-out hover:bg-[var(--malachite-number-tag-emphasis-background,rgba(0,0,0,0.15))] ${FOCUS_RING}`;\n\ntype LinkProps = Parameters<RenderBreadcrumbLink>[0];\n\nconst renderCrumbLink = (props: LinkProps, renderLink?: RenderBreadcrumbLink): React.ReactNode => {\n // An unsafe url degrades to plain text rather than a link that could run a script.\n if (!isSafeHref(props.href)) {\n return props.children;\n }\n return renderLink ? renderLink(props) : <a {...props} />;\n};\n\nconst renderCrumbName = (name: React.ReactNode, color: TextColor): React.ReactNode =>\n typeof name === 'string' ? (\n <Text size=\"medium\" fontWeight=\"semi-bold\" color={color}>\n {name}\n </Text>\n ) : (\n name\n );\n\nconst renderAction = (\n {\n label,\n onClick,\n disabled,\n variant,\n icon,\n href,\n target,\n tooltip,\n tooltipPlacement,\n }: PageAction,\n index: number,\n) => {\n const safe = safeHref(href);\n return (\n <Button\n key={`${index}-${label}`}\n size=\"small\"\n variant={variant ?? 'outline'}\n disabled={disabled}\n icon={icon}\n tooltip={tooltip}\n tooltipPlacement={tooltipPlacement}\n href={safe}\n target={safe ? (target ?? '_blank') : undefined}\n onClick={safe ? undefined : onClick}\n >\n {label}\n </Button>\n );\n};\n\nconst Breadcrumbs: React.FC<BreadcrumbsProps> = ({\n links,\n actions,\n renderLink,\n className = '',\n style,\n}) => {\n // PageLayout mounts this unconditionally, so returning markup would leave an empty\n // navigation landmark on every page without crumbs.\n if (links.length === 0 && !actions?.length) {\n return null;\n }\n\n const isTitle = links.length === 1 && Boolean(links[0].isRoot);\n const parentCrumb = hasBackButton(links) ? links[links.length - 2] : undefined;\n const parentName = parentCrumb?.name;\n const hasActions = Boolean(actions?.length);\n\n return (\n <div\n className={`flex items-center ${hasActions ? 'justify-between' : ''} ${className}`}\n style={style}\n >\n {isTitle ? (\n renderCrumbName(links[0].name, 'primary')\n ) : (\n <div className=\"flex items-center gap-4\">\n {parentCrumb?.url &&\n renderCrumbLink(\n {\n href: parentCrumb.url,\n 'aria-label':\n typeof parentName === 'string'\n ? `Back to ${parentName}`\n : 'Go back',\n className: BACK_BUTTON_CLASSES,\n children: <ArrowLeftIcon size={12} />,\n },\n renderLink,\n )}\n {links.length > 0 && (\n <nav aria-label=\"Breadcrumb\">\n <ol className=\"flex flex-wrap items-center gap-2 m-0 p-0 list-none\">\n {links.map((link, index) => {\n const isLast = index === links.length - 1;\n const color: TextColor = isLast ? 'primary' : 'muted';\n\n return (\n <li\n key={`${link.url ?? ''}-${index}`}\n className=\"flex items-center gap-2\"\n aria-current={isLast && !link.url ? 'page' : undefined}\n >\n {link.url\n ? renderCrumbLink(\n {\n href: link.url,\n 'aria-current': isLast\n ? 'page'\n : undefined,\n className: `${CRUMB_LINK_CLASSES} ${\n isLast\n ? 'text-[var(--malachite-text-primary,rgb(10,10,10))]'\n : 'text-[var(--malachite-text-muted,rgb(76,75,75))]'\n }`,\n children: renderCrumbName(\n link.name,\n 'inherit',\n ),\n },\n renderLink,\n )\n : renderCrumbName(link.name, color)}\n {!isLast && (\n <span\n aria-hidden=\"true\"\n className=\"flex items-center text-[var(--malachite-text-dimmed,rgb(161,161,161))]\"\n >\n <CaretRightIcon size={13} />\n </span>\n )}\n </li>\n );\n })}\n </ol>\n </nav>\n )}\n </div>\n )}\n {hasActions && (\n <div className=\"flex items-center gap-2\">{actions?.map(renderAction)}</div>\n )}\n </div>\n );\n};\n\nexport { Breadcrumbs };\nexport default Breadcrumbs;\n"],"names":["CRUMB_LINK_CLASSES","FOCUS_RING","BACK_BUTTON_CLASSES","renderCrumbLink","props","renderLink","isSafeHref","jsx","renderCrumbName","name","color","Text","renderAction","label","onClick","disabled","variant","icon","href","target","tooltip","tooltipPlacement","index","safe","safeHref","Button","Breadcrumbs","links","actions","className","style","isTitle","parentCrumb","hasBackButton","parentName","hasActions","jsxs","ArrowLeftIcon","link","isLast","CaretRightIcon"],"mappings":"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"}