@stackone/malachite 2.75.0 → 2.76.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 +1 -1
- package/dist/components/Alert.js +139 -100
- package/dist/components/Alert.js.map +1 -1
- package/dist/components/Modal.js +132 -117
- package/dist/components/Modal.js.map +1 -1
- package/dist/index.d.ts +19 -0
- package/package.json +1 -1
package/COMPONENTS.md
CHANGED
|
@@ -34,7 +34,7 @@ The catalog of components exported from `@stackone/malachite`. **Reach for one o
|
|
|
34
34
|
## Feedback & status
|
|
35
35
|
|
|
36
36
|
- **`ChangeReview`** — Reviewing changes a machine made. `ChangeReview.Item` attaches to one changed thing: it names what changed with an `assistant` Tag, shows the value it displaced struck through, and offers approve and deny as two equally reachable icon buttons — a review affordance that made keeping easier than rejecting would not be one. Given `children`, it wraps the field the change is annotating: the tag/label/actions become a header band above it and the before/after line moves below, so "Was …" reads next to the value it describes instead of stacked with the header; without children it renders as the standalone card it always has. `ChangeReview.Summary` counts what is still undecided across the whole form, which is what a per-item control cannot do and what a caller gates a save on; at zero it either says so or collapses to nothing. `ChangeReview.Removed` is for something the assistant took out: a dashed, tinted card listing what would be lost, because a removal that simply vanished would leave nothing to reject. `ChangeReview.Jump` is a chip an assistant turn carries to point at a field it touched. _Key props:_ `Item`: `label`, `before`, `after`, `kind` (`changed` | `added` | `removed`), `origin`, `children`, `onApprove`, `onDeny`, `disabled`, `className`. `Summary`: `count`, `origin`, `onApproveAll`, `onDenyAll`, `disabled`, `settledMessage`, `className`. `Removed`: `title`, `details` (`{label, value}[]`), `onRestore`, `onDiscard`, `restoreLabel`, `discardLabel`, `origin`, `disabled`, `className`. `Jump`: `label`, `onClick`, `disabled`, `className`. `Item` also takes `onPreviewRevert(previewing)`, fired on hover and on focus.
|
|
37
|
-
- **`Alert`** — Inline alert/callout. Four severities (info, success, warning, error) plus `assistant` — a violet, sparkle-marked ground that says a machine wrote the message rather than that anything is wrong, so provenance never has to borrow a severity colour. `action` puts a control at the end — the link or button that resolves what the alert reports — and `icon` replaces the status icon; an svg that paints with `currentColor` (a CustomIcon) is drawn in the colour and size of the built-in one. _Key props:_ `type`, `message`, `lead` (a bold sentence the message runs on from; plain text), `layout` (`inline` | `banner` — a 48px page-wide band with a 16px icon, for `WizardLayout`'s `banner` slot; an expandable alert stays inline), `align` (`start` | `center`, the banner icon and text gathered mid-band), `action`, `icon`, `hasMargin`, `live` (makes the message a live region — `role="alert"` for error/warning, announced as it mounts; `status` otherwise, announced when its `message` changes, so keep it mounted; off by default), `renderLink` (routes root-relative links in `message` through the app’s router, as `MarkdownBlock` does), `children` (expandable body), `isExpanded`, `onToggle`, `className`.
|
|
37
|
+
- **`Alert`** — Inline alert/callout. Four severities (info, success, warning, error) plus `assistant` — a violet, sparkle-marked ground that says a machine wrote the message rather than that anything is wrong, so provenance never has to borrow a severity colour. `action` puts a control at the end — the link or button that resolves what the alert reports — and `icon` replaces the status icon; an svg that paints with `currentColor` (a CustomIcon) is drawn in the colour and size of the built-in one. _Key props:_ `type`, `message`, `lead` (a bold sentence the message runs on from; plain text), `address` (`{ text, emphasis? }`: an address the message runs on into, whole and in mono; `emphasis`, an optional substring to bold wherever it occurs, often the registrable domain) and `addressSuffix` (markdown after the address, joined without a space when it opens with any punctuation character), `layout` (`inline` | `banner` — a 48px page-wide band with a 16px icon, for `WizardLayout`'s `banner` slot; an expandable alert stays inline), `align` (`start` | `center`, the banner icon and text gathered mid-band), `action`, `icon`, `hasMargin`, `live` (makes the message a live region — `role="alert"` for error/warning, announced as it mounts; `status` otherwise, announced when its `message` changes, so keep it mounted; off by default), `renderLink` (routes root-relative links in `message` through the app’s router, as `MarkdownBlock` does), `children` (expandable body), `isExpanded`, `onToggle`, `className`.
|
|
38
38
|
- **`IconIndicator`** — A non-interactive icon that carries a meaning, such as a capability shown on a page that cannot switch it on. Same square as an icon-only `Button` at each `size`, so it lines up where a sibling view shows the button. Renders `role="img"`, named by `aria-label`; with a `tooltip` it takes focus so the tooltip is reachable by keyboard. Reach for it instead of a hand-sized `<span>` around an icon. _Key props:_ `icon`, `aria-label`, `tooltip`, `size`.
|
|
39
39
|
- **`IconBadge`** — A small filled tile carrying a 12px glyph, for a dialog or panel header such as `SidePanel`’s `icon`. Decorative and always `aria-hidden`: the title beside it carries the meaning, so reach for `IconIndicator` when the icon itself has to say something. `variant` picks a semantic fill (`default` neutral, `accent` the interaction colour, `warning`, `destructive`, `success`) rather than taking a colour, and the glyph is drawn in the card background. `ConfirmationDialog` draws its badge with it. _Key props:_ `icon`, `variant` (`default` | `accent` | `warning` | `destructive` | `success`), `className`.
|
|
40
40
|
- **`StatusBadge`** — Large status indicator (e.g. success checkmark) for terminal/confirmation screens. _Key props:_ `type`.
|
package/dist/components/Alert.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { Button as
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import { getTextColor as
|
|
13
|
-
const
|
|
1
|
+
import { jsxs as i, jsx as e, Fragment as u } from "react/jsx-runtime";
|
|
2
|
+
import M, { useState as P } from "react";
|
|
3
|
+
import { Button as U } from "./Button.js";
|
|
4
|
+
import Y from "./CustomIcons/CaretDownIcon.js";
|
|
5
|
+
import $ from "./CustomIcons/CaretUpIcon.js";
|
|
6
|
+
import D from "./CustomIcons/CheckCircleFilled.js";
|
|
7
|
+
import L from "./CustomIcons/ErrorCircleFilled.js";
|
|
8
|
+
import q from "./CustomIcons/InfoCircleFilled.js";
|
|
9
|
+
import G from "./CustomIcons/SparkleIcon.js";
|
|
10
|
+
import J from "./CustomIcons/WarningCircleFilled.js";
|
|
11
|
+
import E from "./Typography/MarkdownBlock.js";
|
|
12
|
+
import { getTextColor as z, getFontSize as K } from "./Typography/textStyles.js";
|
|
13
|
+
const g = (r) => {
|
|
14
14
|
switch (r) {
|
|
15
15
|
case "info":
|
|
16
16
|
return "primary";
|
|
@@ -27,147 +27,186 @@ const p = (r) => {
|
|
|
27
27
|
default:
|
|
28
28
|
return "primary";
|
|
29
29
|
}
|
|
30
|
-
},
|
|
30
|
+
}, m = {
|
|
31
31
|
width: "13px",
|
|
32
32
|
height: "13px",
|
|
33
33
|
flexShrink: 0
|
|
34
|
-
},
|
|
34
|
+
}, h = {
|
|
35
35
|
info: "var(--malachite-alert-info-foreground)",
|
|
36
36
|
success: "var(--malachite-alert-success-foreground)",
|
|
37
37
|
error: "var(--malachite-alert-error-foreground)",
|
|
38
38
|
warning: "var(--malachite-alert-warning-foreground)",
|
|
39
39
|
assistant: "var(--malachite-alert-assistant-foreground)"
|
|
40
|
-
},
|
|
41
|
-
info: /* @__PURE__ */ e(
|
|
42
|
-
success: /* @__PURE__ */ e(
|
|
43
|
-
error: /* @__PURE__ */ e(
|
|
44
|
-
warning: /* @__PURE__ */ e(
|
|
40
|
+
}, F = {
|
|
41
|
+
info: /* @__PURE__ */ e(q, { style: { ...m, color: h.info } }),
|
|
42
|
+
success: /* @__PURE__ */ e(D, { style: { ...m, color: h.success } }),
|
|
43
|
+
error: /* @__PURE__ */ e(L, { style: { ...m, color: h.error } }),
|
|
44
|
+
warning: /* @__PURE__ */ e(J, { style: { ...m, color: h.warning } }),
|
|
45
45
|
// The same sparkle the control that produced the message carries, so the two read as
|
|
46
46
|
// one thread rather than two unrelated affordances.
|
|
47
|
-
assistant: /* @__PURE__ */ e(
|
|
48
|
-
},
|
|
47
|
+
assistant: /* @__PURE__ */ e(G, { style: { ...m, color: h.assistant } })
|
|
48
|
+
}, Q = { ...m, width: "16px", height: "16px" }, S = (r, l, t = !1) => !l && !t ? F[r] : /* @__PURE__ */ e(
|
|
49
49
|
"span",
|
|
50
50
|
{
|
|
51
51
|
className: "alert-status-icon inline-flex shrink-0 [&_svg]:size-full!",
|
|
52
|
-
style: { ...
|
|
53
|
-
children:
|
|
52
|
+
style: { ...t ? Q : m, color: h[r] },
|
|
53
|
+
children: l || F[r]
|
|
54
54
|
}
|
|
55
|
-
),
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
children: l
|
|
65
|
-
}
|
|
66
|
-
),
|
|
67
|
-
" ",
|
|
68
|
-
/* @__PURE__ */ e(v, { text: r, type: t, renderLink: c })
|
|
69
|
-
] }) : /* @__PURE__ */ e(
|
|
70
|
-
q,
|
|
55
|
+
), V = ({ text: r, emphasis: l }) => {
|
|
56
|
+
if (!l)
|
|
57
|
+
return [{ text: r, bold: !1, offset: 0 }];
|
|
58
|
+
const t = [], a = (n, s, o) => {
|
|
59
|
+
n && t.push({ text: n, bold: s, offset: o });
|
|
60
|
+
}, c = r.split(l);
|
|
61
|
+
return c.reduce((n, s, o) => (a(s, !1, n), o < c.length - 1 && a(l, !0, n + s.length), n + s.length + l.length), 0), t;
|
|
62
|
+
}, X = ({ address: r, color: l }) => /* @__PURE__ */ e(
|
|
63
|
+
"code",
|
|
71
64
|
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
className: "alert-address",
|
|
66
|
+
style: {
|
|
67
|
+
fontFamily: "var(--malachite-font-mono, monospace)",
|
|
68
|
+
fontSize: "0.9em",
|
|
69
|
+
color: l,
|
|
70
|
+
overflowWrap: "anywhere"
|
|
71
|
+
},
|
|
72
|
+
children: V(r).map(
|
|
73
|
+
(t) => t.bold ? /* @__PURE__ */ e("strong", { style: { fontWeight: 600 }, children: t.text }, t.offset) : /* @__PURE__ */ e(M.Fragment, { children: t.text }, t.offset)
|
|
74
|
+
)
|
|
77
75
|
}
|
|
78
|
-
),
|
|
76
|
+
), Z = new RegExp("^\\p{P}", "u"), j = ({ text: r, type: l, lead: t, address: a, addressSuffix: c, renderLink: n }) => {
|
|
77
|
+
const s = z(g(l)), o = /* @__PURE__ */ e(
|
|
78
|
+
E,
|
|
79
|
+
{
|
|
80
|
+
text: r,
|
|
81
|
+
color: g(l),
|
|
82
|
+
size: "small",
|
|
83
|
+
omitMargin: !0,
|
|
84
|
+
renderLink: n
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
return !t && !a ? o : /* @__PURE__ */ i("div", { className: "alert-lead", children: [
|
|
88
|
+
t ? /* @__PURE__ */ i(u, { children: [
|
|
89
|
+
/* @__PURE__ */ e("strong", { style: { color: s, fontSize: K("small"), fontWeight: 600 }, children: t }),
|
|
90
|
+
" "
|
|
91
|
+
] }) : null,
|
|
92
|
+
o,
|
|
93
|
+
a ? /* @__PURE__ */ i(u, { children: [
|
|
94
|
+
" ",
|
|
95
|
+
/* @__PURE__ */ e(X, { address: a, color: s })
|
|
96
|
+
] }) : null,
|
|
97
|
+
a && c ? /* @__PURE__ */ i(u, { children: [
|
|
98
|
+
Z.test(c) ? "" : " ",
|
|
99
|
+
/* @__PURE__ */ e(
|
|
100
|
+
E,
|
|
101
|
+
{
|
|
102
|
+
text: c,
|
|
103
|
+
color: g(l),
|
|
104
|
+
size: "small",
|
|
105
|
+
omitMargin: !0,
|
|
106
|
+
renderLink: n
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
] }) : null
|
|
110
|
+
] });
|
|
111
|
+
}, he = ({
|
|
79
112
|
type: r = "info",
|
|
80
|
-
message:
|
|
81
|
-
hasMargin:
|
|
82
|
-
className:
|
|
83
|
-
children:
|
|
84
|
-
isExpanded:
|
|
85
|
-
onToggle:
|
|
86
|
-
action:
|
|
87
|
-
icon:
|
|
88
|
-
live:
|
|
89
|
-
renderLink:
|
|
90
|
-
layout:
|
|
113
|
+
message: l,
|
|
114
|
+
hasMargin: t = !0,
|
|
115
|
+
className: a,
|
|
116
|
+
children: c,
|
|
117
|
+
isExpanded: n,
|
|
118
|
+
onToggle: s,
|
|
119
|
+
action: o,
|
|
120
|
+
icon: N,
|
|
121
|
+
live: O = !1,
|
|
122
|
+
renderLink: w,
|
|
123
|
+
layout: W = "inline",
|
|
91
124
|
lead: C,
|
|
92
|
-
|
|
125
|
+
address: b,
|
|
126
|
+
addressSuffix: y,
|
|
127
|
+
align: k = "start"
|
|
93
128
|
}) => {
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
129
|
+
const d = !!c, p = W === "banner" && !d, v = p && k === "center", _ = O ? r === "error" || r === "warning" ? "alert" : "status" : void 0, [A, B] = P(!1), f = n !== void 0 ? n : A, T = () => {
|
|
130
|
+
const x = !f;
|
|
131
|
+
n === void 0 && B(x), s == null || s(x);
|
|
97
132
|
}, I = [
|
|
98
133
|
"alert-base",
|
|
99
134
|
`alert-${r}`,
|
|
100
135
|
"flex",
|
|
101
136
|
"w-full",
|
|
102
137
|
// Ensure full width
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
138
|
+
p ? "alert-banner" : d && f ? "flex-col" : "gap-2 items-center min-h-[2rem]",
|
|
139
|
+
t ? "" : "alert-no-margin",
|
|
140
|
+
v ? "text-center" : "text-left",
|
|
141
|
+
p ? "" : d ? "rounded-xl !p-0" : "!px-2.5 !py-1.5",
|
|
107
142
|
// Slightly more vertical padding for centering
|
|
108
|
-
|
|
109
|
-
].filter(Boolean).join(" "),
|
|
143
|
+
a
|
|
144
|
+
].filter(Boolean).join(" "), R = [
|
|
110
145
|
"flex items-center justify-between w-full",
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
].filter(Boolean).join(" "),
|
|
114
|
-
if (!
|
|
115
|
-
const
|
|
116
|
-
S(r,
|
|
117
|
-
/* @__PURE__ */ e("div", { className:
|
|
118
|
-
|
|
146
|
+
d ? "gap-2 px-2.5 py-1" : "gap-2",
|
|
147
|
+
d && f ? "rounded-t-xl" : d ? "rounded-xl" : ""
|
|
148
|
+
].filter(Boolean).join(" "), H = ["w-full", "overflow-hidden"].filter(Boolean).join(" ");
|
|
149
|
+
if (!d) {
|
|
150
|
+
const x = /* @__PURE__ */ i(u, { children: [
|
|
151
|
+
S(r, N, p),
|
|
152
|
+
/* @__PURE__ */ e("div", { className: v ? "flex items-center" : "flex-1 flex items-center", children: /* @__PURE__ */ e("div", { className: "[&>div]:mb-0", role: _, children: /* @__PURE__ */ e(
|
|
153
|
+
j,
|
|
119
154
|
{
|
|
120
|
-
text:
|
|
155
|
+
text: l,
|
|
121
156
|
type: r,
|
|
122
157
|
lead: C,
|
|
123
|
-
|
|
158
|
+
address: b,
|
|
159
|
+
addressSuffix: y,
|
|
160
|
+
renderLink: w
|
|
124
161
|
}
|
|
125
162
|
) }) })
|
|
126
163
|
] });
|
|
127
|
-
return /* @__PURE__ */
|
|
128
|
-
|
|
164
|
+
return /* @__PURE__ */ i("div", { className: I, children: [
|
|
165
|
+
v ? (
|
|
129
166
|
// Its own group, so an `action` stays at the end while this sits mid-band.
|
|
130
|
-
/* @__PURE__ */ e("div", { className: "alert-centred flex-1 flex items-center justify-center gap-[10px]", children:
|
|
131
|
-
) :
|
|
132
|
-
|
|
167
|
+
/* @__PURE__ */ e("div", { className: "alert-centred flex-1 flex items-center justify-center gap-[10px]", children: x })
|
|
168
|
+
) : x,
|
|
169
|
+
o && /* @__PURE__ */ e("div", { className: "alert-action shrink-0 flex items-center", children: o })
|
|
133
170
|
] });
|
|
134
171
|
}
|
|
135
|
-
return /* @__PURE__ */
|
|
136
|
-
/* @__PURE__ */
|
|
137
|
-
/* @__PURE__ */
|
|
138
|
-
S(r,
|
|
139
|
-
/* @__PURE__ */ e("div", { className: "flex-1 flex items-center", children: /* @__PURE__ */ e("div", { className: "[&>div]:mb-0", role:
|
|
140
|
-
|
|
172
|
+
return /* @__PURE__ */ i("div", { className: `${I} alert-expandable`, children: [
|
|
173
|
+
/* @__PURE__ */ i("div", { className: R, children: [
|
|
174
|
+
/* @__PURE__ */ i("div", { className: "flex-1 flex gap-2 items-center", children: [
|
|
175
|
+
S(r, N),
|
|
176
|
+
/* @__PURE__ */ e("div", { className: "flex-1 flex items-center", children: /* @__PURE__ */ e("div", { className: "[&>div]:mb-0", role: _, children: /* @__PURE__ */ e(
|
|
177
|
+
j,
|
|
141
178
|
{
|
|
142
|
-
text:
|
|
179
|
+
text: l,
|
|
143
180
|
lead: C,
|
|
181
|
+
address: b,
|
|
182
|
+
addressSuffix: y,
|
|
144
183
|
type: r,
|
|
145
|
-
renderLink:
|
|
184
|
+
renderLink: w
|
|
146
185
|
}
|
|
147
186
|
) }) })
|
|
148
187
|
] }),
|
|
149
|
-
/* @__PURE__ */
|
|
150
|
-
|
|
188
|
+
/* @__PURE__ */ i("div", { className: "flex items-center gap-2", children: [
|
|
189
|
+
o && /* @__PURE__ */ e("div", { className: "alert-action flex items-center", children: o }),
|
|
151
190
|
/* @__PURE__ */ e(
|
|
152
|
-
|
|
191
|
+
U,
|
|
153
192
|
{
|
|
154
193
|
variant: "no-style",
|
|
155
194
|
size: "xsmall",
|
|
156
|
-
onClick:
|
|
157
|
-
"aria-label":
|
|
195
|
+
onClick: T,
|
|
196
|
+
"aria-label": f ? "Collapse" : "Expand",
|
|
158
197
|
style: {
|
|
159
|
-
color:
|
|
198
|
+
color: z(g(r))
|
|
160
199
|
},
|
|
161
|
-
children:
|
|
200
|
+
children: f ? /* @__PURE__ */ e($, { style: { minWidth: "13px", minHeight: "13px" } }) : /* @__PURE__ */ e(Y, { style: { minWidth: "13px", minHeight: "13px" } })
|
|
162
201
|
}
|
|
163
202
|
)
|
|
164
203
|
] })
|
|
165
204
|
] }),
|
|
166
|
-
|
|
205
|
+
f && /* @__PURE__ */ e(u, { children: /* @__PURE__ */ e("div", { className: H, children: /* @__PURE__ */ e("div", { className: "bg-white rounded-[var(--malachite-radius-10,10px)] shadow-[0px_1px_0px_0px_rgba(0,0,0,0.15)] border border-[#e8e8e8] border-solid mx-2 mb-2 max-h-[553px] overflow-y-auto", children: c }) }) })
|
|
167
206
|
] });
|
|
168
207
|
};
|
|
169
208
|
export {
|
|
170
|
-
|
|
171
|
-
|
|
209
|
+
he as Alert,
|
|
210
|
+
he as default
|
|
172
211
|
};
|
|
173
212
|
//# sourceMappingURL=Alert.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Alert.js","sources":["../../src/components/Alert.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { Button, ButtonVariant } from './Button';\nimport CaretDownIcon from './CustomIcons/CaretDownIcon';\nimport CaretUpIcon from './CustomIcons/CaretUpIcon';\nimport CheckCircleFilled from './CustomIcons/CheckCircleFilled';\nimport ErrorCircleFilled from './CustomIcons/ErrorCircleFilled';\nimport InfoCircleFilled from './CustomIcons/InfoCircleFilled';\nimport SparkleIcon from './CustomIcons/SparkleIcon';\nimport WarningCircleFilled from './CustomIcons/WarningCircleFilled';\nimport type { RenderLink } from './PageLayout/types';\nimport MarkdownBlock from './Typography/MarkdownBlock';\nimport type { TextColor } from './Typography/Text';\nimport { getFontSize, getTextColor } from './Typography/textStyles';\n\nexport type AlertType = 'info' | 'success' | 'error' | 'warning' | 'assistant';\n\nexport type AlertLayout = 'inline' | 'banner';\n\ninterface AlertButtonConfig {\n label: string;\n onClick: () => void;\n variant?: ButtonVariant;\n icon?: React.ReactNode;\n}\n\ninterface AlertProps {\n type?: AlertType;\n message: string;\n hasMargin?: boolean;\n className?: string;\n children?: React.ReactNode;\n isExpanded?: boolean;\n onToggle?: (expanded: boolean) => void;\n /** A control at the end of the alert — a link or button that resolves what it reports. */\n action?: React.ReactNode;\n /** Replaces the type's status icon: an svg painting with `currentColor`, such as a CustomIcon,\n * is drawn in the type's colour at the same size. */\n icon?: React.ReactNode;\n /** Makes its message a live region: `role=\"alert\"` for error and warning, `status` otherwise.\n * Screen readers reliably announce an `alert` as it mounts; a `status` is read when its\n * `message` changes, so keep a live info or success alert mounted. Off by default. */\n live?: boolean;\n /** Routes root-relative links in `message` through the app's router; see `MarkdownBlock`. */\n renderLink?: RenderLink;\n /** `banner`: a 48px page-wide band. Applies only without `children`. */\n layout?: AlertLayout;\n /** `center` gathers a banner's icon and text in the middle of the band, for a wide page. */\n align?: 'start' | 'center';\n /** A bold sentence the message runs on from. Plain text, not markdown. */\n lead?: string;\n}\n\n// Distinct from the imported `getTextColor`, which maps a TextColor to a CSS variable.\n// This maps an AlertType to a TextColor token; both are used in this file.\nconst getAlertTextColor = (alertType: AlertType): TextColor => {\n switch (alertType) {\n case 'info':\n return 'primary';\n case 'success':\n return 'success';\n case 'error':\n return 'error';\n case 'warning':\n return 'warning';\n // The violet is on `.alert-assistant` itself; inheriting it keeps one definition\n // rather than a TextColor token that only this alert would ever name.\n case 'assistant':\n return 'inherit';\n default:\n return 'primary';\n }\n};\n\nconst ICON_STYLE = {\n width: '13px',\n height: '13px',\n flexShrink: 0,\n};\n\nconst ICON_COLOR: Record<AlertType, string> = {\n info: 'var(--malachite-alert-info-foreground)',\n success: 'var(--malachite-alert-success-foreground)',\n error: 'var(--malachite-alert-error-foreground)',\n warning: 'var(--malachite-alert-warning-foreground)',\n assistant: 'var(--malachite-alert-assistant-foreground)',\n};\n\nconst AlertIcon = {\n info: <InfoCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.info }} />,\n success: <CheckCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.success }} />,\n error: <ErrorCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.error }} />,\n warning: <WarningCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.warning }} />,\n // The same sparkle the control that produced the message carries, so the two read as\n // one thread rather than two unrelated affordances.\n assistant: <SparkleIcon style={{ ...ICON_STYLE, color: ICON_COLOR.assistant }} />,\n};\n\nconst BANNER_ICON_STYLE = { ...ICON_STYLE, width: '16px', height: '16px' };\n\nconst statusIcon = (type: AlertType, icon: React.ReactNode, banner = false) => {\n if (!icon && !banner) return AlertIcon[type];\n const style = banner ? BANNER_ICON_STYLE : ICON_STYLE;\n return (\n <span\n className=\"alert-status-icon inline-flex shrink-0 [&_svg]:size-full!\"\n style={{ ...style, color: ICON_COLOR[type] }}\n >\n {icon || AlertIcon[type]}\n </span>\n );\n};\n\nconst SimpleTextRenderer: React.FC<{\n text: string;\n type: AlertType;\n lead?: string;\n renderLink?: RenderLink;\n}> = ({ text, type, lead, renderLink }) => {\n if (lead) {\n return (\n <div className=\"alert-lead\">\n <strong\n style={{\n color: getTextColor(getAlertTextColor(type)),\n fontSize: getFontSize('small'),\n fontWeight: 600,\n }}\n >\n {lead}\n </strong>{' '}\n <SimpleTextRenderer text={text} type={type} renderLink={renderLink} />\n </div>\n );\n }\n return (\n <MarkdownBlock\n text={text}\n color={getAlertTextColor(type)}\n size=\"small\"\n omitMargin\n renderLink={renderLink}\n />\n );\n};\n\nconst Alert: React.FC<AlertProps> = ({\n type = 'info',\n message,\n hasMargin = true,\n className,\n children,\n isExpanded,\n onToggle,\n action,\n icon,\n live = false,\n renderLink,\n layout = 'inline',\n lead,\n align = 'start',\n}) => {\n const isExpandable = !!children;\n const banner = layout === 'banner' && !isExpandable;\n const centred = banner && align === 'center';\n const urgent = type === 'error' || type === 'warning';\n const liveRole = live ? (urgent ? 'alert' : 'status') : undefined;\n const [internalExpanded, setInternalExpanded] = useState(false);\n\n // Use controlled state if isExpanded is provided, otherwise use internal state\n const expanded = isExpanded !== undefined ? isExpanded : internalExpanded;\n\n const handleToggle = () => {\n const newExpanded = !expanded;\n if (isExpanded === undefined) {\n // Uncontrolled mode\n setInternalExpanded(newExpanded);\n }\n // Call onToggle if provided (for both controlled and uncontrolled)\n onToggle?.(newExpanded);\n };\n\n const alertClasses = [\n 'alert-base',\n `alert-${type}`,\n 'flex',\n 'w-full', // Ensure full width\n banner\n ? 'alert-banner'\n : isExpandable && expanded\n ? 'flex-col'\n : 'gap-2 items-center min-h-[2rem]',\n !hasMargin ? 'alert-no-margin' : '',\n centred ? 'text-center' : 'text-left',\n banner ? '' : isExpandable ? 'rounded-xl !p-0' : '!px-2.5 !py-1.5', // Slightly more vertical padding for centering\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const headerClasses = [\n 'flex items-center justify-between w-full',\n isExpandable ? 'gap-2 px-2.5 py-1' : 'gap-2',\n isExpandable && expanded ? 'rounded-t-xl' : isExpandable ? 'rounded-xl' : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n const expandableContentClasses = ['w-full', 'overflow-hidden'].filter(Boolean).join(' ');\n\n // Non-expandable alert (original behavior)\n if (!isExpandable) {\n const content = (\n <>\n {statusIcon(type, icon, banner)}\n <div className={centred ? 'flex items-center' : 'flex-1 flex items-center'}>\n <div className=\"[&>div]:mb-0\" role={liveRole}>\n <SimpleTextRenderer\n text={message}\n type={type}\n lead={lead}\n renderLink={renderLink}\n />\n </div>\n </div>\n </>\n );\n return (\n <div className={alertClasses}>\n {centred ? (\n // Its own group, so an `action` stays at the end while this sits mid-band.\n <div className=\"alert-centred flex-1 flex items-center justify-center gap-[10px]\">\n {content}\n </div>\n ) : (\n content\n )}\n {action && <div className=\"alert-action shrink-0 flex items-center\">{action}</div>}\n </div>\n );\n }\n\n // Expandable alert\n return (\n <div className={`${alertClasses} alert-expandable`}>\n {/* Header section with title and expand/collapse button */}\n <div className={headerClasses}>\n <div className=\"flex-1 flex gap-2 items-center\">\n {statusIcon(type, icon)}\n <div className=\"flex-1 flex items-center\">\n <div className=\"[&>div]:mb-0\" role={liveRole}>\n <SimpleTextRenderer\n text={message}\n lead={lead}\n type={type}\n renderLink={renderLink}\n />\n </div>\n </div>\n </div>\n\n {/* Right side buttons container */}\n <div className=\"flex items-center gap-2\">\n {action && <div className=\"alert-action flex items-center\">{action}</div>}\n {/* Expand/collapse button */}\n <Button\n variant=\"no-style\"\n size=\"xsmall\"\n onClick={handleToggle}\n aria-label={expanded ? 'Collapse' : 'Expand'}\n style={{\n color: getTextColor(getAlertTextColor(type)),\n }}\n >\n {expanded ? (\n <CaretUpIcon style={{ minWidth: '13px', minHeight: '13px' }} />\n ) : (\n <CaretDownIcon style={{ minWidth: '13px', minHeight: '13px' }} />\n )}\n </Button>\n </div>\n </div>\n\n {/* Expandable content */}\n {expanded && (\n <>\n <div className={expandableContentClasses}>\n <div className=\"bg-white rounded-[var(--malachite-radius-10,10px)] shadow-[0px_1px_0px_0px_rgba(0,0,0,0.15)] border border-[#e8e8e8] border-solid mx-2 mb-2 max-h-[553px] overflow-y-auto\">\n {children}\n </div>\n </div>\n </>\n )}\n </div>\n );\n};\n\nexport default Alert;\nexport { Alert };\nexport type { AlertButtonConfig };\n"],"names":["getAlertTextColor","alertType","ICON_STYLE","ICON_COLOR","AlertIcon","jsx","InfoCircleFilled","CheckCircleFilled","ErrorCircleFilled","WarningCircleFilled","SparkleIcon","BANNER_ICON_STYLE","statusIcon","type","icon","banner","SimpleTextRenderer","text","lead","renderLink","jsxs","getTextColor","getFontSize","MarkdownBlock","Alert","message","hasMargin","className","children","isExpanded","onToggle","action","live","layout","align","isExpandable","centred","liveRole","internalExpanded","setInternalExpanded","useState","expanded","handleToggle","newExpanded","alertClasses","headerClasses","expandableContentClasses","content","Fragment","Button","CaretUpIcon","CaretDownIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Alert.js","sources":["../../src/components/Alert.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { Button, ButtonVariant } from './Button';\nimport CaretDownIcon from './CustomIcons/CaretDownIcon';\nimport CaretUpIcon from './CustomIcons/CaretUpIcon';\nimport CheckCircleFilled from './CustomIcons/CheckCircleFilled';\nimport ErrorCircleFilled from './CustomIcons/ErrorCircleFilled';\nimport InfoCircleFilled from './CustomIcons/InfoCircleFilled';\nimport SparkleIcon from './CustomIcons/SparkleIcon';\nimport WarningCircleFilled from './CustomIcons/WarningCircleFilled';\nimport type { RenderLink } from './PageLayout/types';\nimport MarkdownBlock from './Typography/MarkdownBlock';\nimport type { TextColor } from './Typography/Text';\nimport { getFontSize, getTextColor } from './Typography/textStyles';\n\nexport type AlertType = 'info' | 'success' | 'error' | 'warning' | 'assistant';\n\nexport type AlertLayout = 'inline' | 'banner';\n\nexport interface AlertAddress {\n /** The full address, shown whole in mono; it wraps rather than truncates. */\n text: string;\n /** The part to set in bold, such as the registrable domain; every occurrence is bolded. */\n emphasis?: string;\n}\n\ninterface AlertButtonConfig {\n label: string;\n onClick: () => void;\n variant?: ButtonVariant;\n icon?: React.ReactNode;\n}\n\ninterface AlertProps {\n type?: AlertType;\n message: string;\n hasMargin?: boolean;\n className?: string;\n children?: React.ReactNode;\n isExpanded?: boolean;\n onToggle?: (expanded: boolean) => void;\n /** A control at the end of the alert — a link or button that resolves what it reports. */\n action?: React.ReactNode;\n /** Replaces the type's status icon: an svg painting with `currentColor`, such as a CustomIcon,\n * is drawn in the type's colour at the same size. */\n icon?: React.ReactNode;\n /** Makes its message a live region: `role=\"alert\"` for error and warning, `status` otherwise.\n * Screen readers reliably announce an `alert` as it mounts; a `status` is read when its\n * `message` changes, so keep a live info or success alert mounted. Off by default. */\n live?: boolean;\n /** Routes root-relative links in `message` through the app's router; see `MarkdownBlock`. */\n renderLink?: RenderLink;\n /** `banner`: a 48px page-wide band. Applies only without `children`. */\n layout?: AlertLayout;\n /** `center` gathers a banner's icon and text in the middle of the band, for a wide page. */\n align?: 'start' | 'center';\n /** A bold sentence the message runs on from. Plain text, not markdown. */\n lead?: string;\n /** An address the message runs on into, in mono; `emphasis`, an optional substring such as the registrable domain, is bold wherever it occurs. */\n address?: AlertAddress;\n /** Markdown that runs on after the address; it joins without a space when it opens with any punctuation. */\n addressSuffix?: string;\n}\n\n// Distinct from the imported `getTextColor`, which maps a TextColor to a CSS variable.\n// This maps an AlertType to a TextColor token; both are used in this file.\nconst getAlertTextColor = (alertType: AlertType): TextColor => {\n switch (alertType) {\n case 'info':\n return 'primary';\n case 'success':\n return 'success';\n case 'error':\n return 'error';\n case 'warning':\n return 'warning';\n // The violet is on `.alert-assistant` itself; inheriting it keeps one definition\n // rather than a TextColor token that only this alert would ever name.\n case 'assistant':\n return 'inherit';\n default:\n return 'primary';\n }\n};\n\nconst ICON_STYLE = {\n width: '13px',\n height: '13px',\n flexShrink: 0,\n};\n\nconst ICON_COLOR: Record<AlertType, string> = {\n info: 'var(--malachite-alert-info-foreground)',\n success: 'var(--malachite-alert-success-foreground)',\n error: 'var(--malachite-alert-error-foreground)',\n warning: 'var(--malachite-alert-warning-foreground)',\n assistant: 'var(--malachite-alert-assistant-foreground)',\n};\n\nconst AlertIcon = {\n info: <InfoCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.info }} />,\n success: <CheckCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.success }} />,\n error: <ErrorCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.error }} />,\n warning: <WarningCircleFilled style={{ ...ICON_STYLE, color: ICON_COLOR.warning }} />,\n // The same sparkle the control that produced the message carries, so the two read as\n // one thread rather than two unrelated affordances.\n assistant: <SparkleIcon style={{ ...ICON_STYLE, color: ICON_COLOR.assistant }} />,\n};\n\nconst BANNER_ICON_STYLE = { ...ICON_STYLE, width: '16px', height: '16px' };\n\nconst statusIcon = (type: AlertType, icon: React.ReactNode, banner = false) => {\n if (!icon && !banner) return AlertIcon[type];\n const style = banner ? BANNER_ICON_STYLE : ICON_STYLE;\n return (\n <span\n className=\"alert-status-icon inline-flex shrink-0 [&_svg]:size-full!\"\n style={{ ...style, color: ICON_COLOR[type] }}\n >\n {icon || AlertIcon[type]}\n </span>\n );\n};\n\ninterface AddressSegment {\n text: string;\n bold: boolean;\n offset: number;\n}\n\n// The text cut at every occurrence of the emphasis, each cut remembering where it started.\nconst addressSegments = ({ text, emphasis }: AlertAddress): AddressSegment[] => {\n if (!emphasis) {\n return [{ text, bold: false, offset: 0 }];\n }\n const segments: AddressSegment[] = [];\n const push = (part: string, bold: boolean, offset: number) => {\n if (part) segments.push({ text: part, bold, offset });\n };\n const cuts = text.split(emphasis);\n cuts.reduce((offset, cut, index) => {\n push(cut, false, offset);\n if (index < cuts.length - 1) push(emphasis, true, offset + cut.length);\n return offset + cut.length + emphasis.length;\n }, 0);\n return segments;\n};\n\nconst AddressText: React.FC<{ address: AlertAddress; color: string }> = ({ address, color }) => (\n <code\n className=\"alert-address\"\n style={{\n fontFamily: 'var(--malachite-font-mono, monospace)',\n fontSize: '0.9em',\n color,\n overflowWrap: 'anywhere',\n }}\n >\n {addressSegments(address).map((segment) =>\n segment.bold ? (\n <strong key={segment.offset} style={{ fontWeight: 600 }}>\n {segment.text}\n </strong>\n ) : (\n <React.Fragment key={segment.offset}>{segment.text}</React.Fragment>\n ),\n )}\n </code>\n);\n\nconst OPENS_WITH_PUNCTUATION = /^\\p{P}/u;\n\nconst SimpleTextRenderer: React.FC<{\n text: string;\n type: AlertType;\n lead?: string;\n address?: AlertAddress;\n addressSuffix?: string;\n renderLink?: RenderLink;\n}> = ({ text, type, lead, address, addressSuffix, renderLink }) => {\n const color = getTextColor(getAlertTextColor(type));\n const message = (\n <MarkdownBlock\n text={text}\n color={getAlertTextColor(type)}\n size=\"small\"\n omitMargin\n renderLink={renderLink}\n />\n );\n if (!lead && !address) {\n return message;\n }\n return (\n <div className=\"alert-lead\">\n {lead ? (\n <>\n <strong style={{ color, fontSize: getFontSize('small'), fontWeight: 600 }}>\n {lead}\n </strong>{' '}\n </>\n ) : null}\n {message}\n {address ? (\n <>\n {' '}\n <AddressText address={address} color={color} />\n </>\n ) : null}\n {address && addressSuffix ? (\n <>\n {OPENS_WITH_PUNCTUATION.test(addressSuffix) ? '' : ' '}\n <MarkdownBlock\n text={addressSuffix}\n color={getAlertTextColor(type)}\n size=\"small\"\n omitMargin\n renderLink={renderLink}\n />\n </>\n ) : null}\n </div>\n );\n};\n\nconst Alert: React.FC<AlertProps> = ({\n type = 'info',\n message,\n hasMargin = true,\n className,\n children,\n isExpanded,\n onToggle,\n action,\n icon,\n live = false,\n renderLink,\n layout = 'inline',\n lead,\n address,\n addressSuffix,\n align = 'start',\n}) => {\n const isExpandable = !!children;\n const banner = layout === 'banner' && !isExpandable;\n const centred = banner && align === 'center';\n const urgent = type === 'error' || type === 'warning';\n const liveRole = live ? (urgent ? 'alert' : 'status') : undefined;\n const [internalExpanded, setInternalExpanded] = useState(false);\n\n // Use controlled state if isExpanded is provided, otherwise use internal state\n const expanded = isExpanded !== undefined ? isExpanded : internalExpanded;\n\n const handleToggle = () => {\n const newExpanded = !expanded;\n if (isExpanded === undefined) {\n // Uncontrolled mode\n setInternalExpanded(newExpanded);\n }\n // Call onToggle if provided (for both controlled and uncontrolled)\n onToggle?.(newExpanded);\n };\n\n const alertClasses = [\n 'alert-base',\n `alert-${type}`,\n 'flex',\n 'w-full', // Ensure full width\n banner\n ? 'alert-banner'\n : isExpandable && expanded\n ? 'flex-col'\n : 'gap-2 items-center min-h-[2rem]',\n !hasMargin ? 'alert-no-margin' : '',\n centred ? 'text-center' : 'text-left',\n banner ? '' : isExpandable ? 'rounded-xl !p-0' : '!px-2.5 !py-1.5', // Slightly more vertical padding for centering\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const headerClasses = [\n 'flex items-center justify-between w-full',\n isExpandable ? 'gap-2 px-2.5 py-1' : 'gap-2',\n isExpandable && expanded ? 'rounded-t-xl' : isExpandable ? 'rounded-xl' : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n const expandableContentClasses = ['w-full', 'overflow-hidden'].filter(Boolean).join(' ');\n\n // Non-expandable alert (original behavior)\n if (!isExpandable) {\n const content = (\n <>\n {statusIcon(type, icon, banner)}\n <div className={centred ? 'flex items-center' : 'flex-1 flex items-center'}>\n <div className=\"[&>div]:mb-0\" role={liveRole}>\n <SimpleTextRenderer\n text={message}\n type={type}\n lead={lead}\n address={address}\n addressSuffix={addressSuffix}\n renderLink={renderLink}\n />\n </div>\n </div>\n </>\n );\n return (\n <div className={alertClasses}>\n {centred ? (\n // Its own group, so an `action` stays at the end while this sits mid-band.\n <div className=\"alert-centred flex-1 flex items-center justify-center gap-[10px]\">\n {content}\n </div>\n ) : (\n content\n )}\n {action && <div className=\"alert-action shrink-0 flex items-center\">{action}</div>}\n </div>\n );\n }\n\n // Expandable alert\n return (\n <div className={`${alertClasses} alert-expandable`}>\n {/* Header section with title and expand/collapse button */}\n <div className={headerClasses}>\n <div className=\"flex-1 flex gap-2 items-center\">\n {statusIcon(type, icon)}\n <div className=\"flex-1 flex items-center\">\n <div className=\"[&>div]:mb-0\" role={liveRole}>\n <SimpleTextRenderer\n text={message}\n lead={lead}\n address={address}\n addressSuffix={addressSuffix}\n type={type}\n renderLink={renderLink}\n />\n </div>\n </div>\n </div>\n\n {/* Right side buttons container */}\n <div className=\"flex items-center gap-2\">\n {action && <div className=\"alert-action flex items-center\">{action}</div>}\n {/* Expand/collapse button */}\n <Button\n variant=\"no-style\"\n size=\"xsmall\"\n onClick={handleToggle}\n aria-label={expanded ? 'Collapse' : 'Expand'}\n style={{\n color: getTextColor(getAlertTextColor(type)),\n }}\n >\n {expanded ? (\n <CaretUpIcon style={{ minWidth: '13px', minHeight: '13px' }} />\n ) : (\n <CaretDownIcon style={{ minWidth: '13px', minHeight: '13px' }} />\n )}\n </Button>\n </div>\n </div>\n\n {/* Expandable content */}\n {expanded && (\n <>\n <div className={expandableContentClasses}>\n <div className=\"bg-white rounded-[var(--malachite-radius-10,10px)] shadow-[0px_1px_0px_0px_rgba(0,0,0,0.15)] border border-[#e8e8e8] border-solid mx-2 mb-2 max-h-[553px] overflow-y-auto\">\n {children}\n </div>\n </div>\n </>\n )}\n </div>\n );\n};\n\nexport default Alert;\nexport { Alert };\nexport type { AlertButtonConfig };\n"],"names":["getAlertTextColor","alertType","ICON_STYLE","ICON_COLOR","AlertIcon","jsx","InfoCircleFilled","CheckCircleFilled","ErrorCircleFilled","WarningCircleFilled","SparkleIcon","BANNER_ICON_STYLE","statusIcon","type","icon","banner","addressSegments","text","emphasis","segments","push","part","bold","offset","cuts","cut","index","AddressText","address","color","segment","React","OPENS_WITH_PUNCTUATION","SimpleTextRenderer","lead","addressSuffix","renderLink","getTextColor","message","MarkdownBlock","jsxs","Fragment","getFontSize","Alert","hasMargin","className","children","isExpanded","onToggle","action","live","layout","align","isExpandable","centred","liveRole","internalExpanded","setInternalExpanded","useState","expanded","handleToggle","newExpanded","alertClasses","headerClasses","expandableContentClasses","content","Button","CaretUpIcon","CaretDownIcon"],"mappings":"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"}
|
package/dist/components/Modal.js
CHANGED
|
@@ -1,169 +1,184 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { useId as
|
|
3
|
-
import { createPortal as
|
|
4
|
-
import { ShadowRootWrapper as
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import { getDeepActiveElement as
|
|
8
|
-
import { GLASS_SURFACE_STYLE as
|
|
9
|
-
import { MODAL_TRAY_BACKGROUND as
|
|
10
|
-
import { useOverlayBehaviour as
|
|
11
|
-
import { OVERLAY_ELEVATED_ATTR as
|
|
12
|
-
const
|
|
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";
|
|
3
|
+
import { createPortal as ce } from "react-dom";
|
|
4
|
+
import { ShadowRootWrapper as de } from "../ShadowRootWrapper.js";
|
|
5
|
+
import me from "./CalloutStrip.js";
|
|
6
|
+
import ue from "./CustomIcons/DismissIcon.js";
|
|
7
|
+
import { getDeepActiveElement as pe } from "./activeElement.js";
|
|
8
|
+
import { GLASS_SURFACE_STYLE as fe } from "./glassSurface.js";
|
|
9
|
+
import { MODAL_TRAY_BACKGROUND as xe, MODAL_TRAY_CAPTION_COLOR as he } from "./modalTray.js";
|
|
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 = {
|
|
13
13
|
small: "420px",
|
|
14
14
|
medium: "612px",
|
|
15
15
|
large: "760px"
|
|
16
|
-
},
|
|
16
|
+
}, g = 200, q = "flex-[0_1_auto] h-[min(600px,calc(100dvh-160px))]", Oe = ({
|
|
17
17
|
open: c,
|
|
18
18
|
onClose: d,
|
|
19
|
-
title:
|
|
20
|
-
subtitle:
|
|
21
|
-
icon:
|
|
22
|
-
headerExtra:
|
|
23
|
-
titleAdornment:
|
|
24
|
-
tray:
|
|
25
|
-
headerDivider:
|
|
26
|
-
footer:
|
|
27
|
-
size:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"aria-
|
|
37
|
-
"aria-
|
|
19
|
+
title: p,
|
|
20
|
+
subtitle: f,
|
|
21
|
+
icon: N,
|
|
22
|
+
headerExtra: w,
|
|
23
|
+
titleAdornment: y,
|
|
24
|
+
tray: K,
|
|
25
|
+
headerDivider: R = !1,
|
|
26
|
+
footer: _,
|
|
27
|
+
size: A = "medium",
|
|
28
|
+
surface: X = "card",
|
|
29
|
+
showCloseButton: Z = !0,
|
|
30
|
+
closeOnOverlayClick: H = !0,
|
|
31
|
+
bodyPadding: T = !0,
|
|
32
|
+
bodyHeight: I = "auto",
|
|
33
|
+
callouts: J,
|
|
34
|
+
renderLink: Q,
|
|
35
|
+
role: ee = "dialog",
|
|
36
|
+
"aria-label": ae,
|
|
37
|
+
"aria-labelledby": $,
|
|
38
|
+
"aria-describedby": re,
|
|
38
39
|
busy: o = !1,
|
|
39
|
-
children:
|
|
40
|
+
children: x
|
|
40
41
|
}) => {
|
|
41
|
-
|
|
42
|
-
P(() =>
|
|
43
|
-
|
|
42
|
+
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
|
+
U(() => {
|
|
45
|
+
D.current = o, M.current = d;
|
|
44
46
|
}, [o, d]);
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
}, []),
|
|
48
|
-
ref:
|
|
49
|
-
isTopmost:
|
|
50
|
-
zIndex:
|
|
51
|
-
elevated:
|
|
52
|
-
} =
|
|
47
|
+
const B = W(() => {
|
|
48
|
+
D.current || M.current();
|
|
49
|
+
}, []), Y = n(null), {
|
|
50
|
+
ref: j,
|
|
51
|
+
isTopmost: ie,
|
|
52
|
+
zIndex: ne,
|
|
53
|
+
elevated: oe
|
|
54
|
+
} = ve({
|
|
53
55
|
open: c,
|
|
54
56
|
onClose: d,
|
|
55
|
-
hostRef:
|
|
56
|
-
}),
|
|
57
|
-
(
|
|
58
|
-
|
|
57
|
+
hostRef: Y
|
|
58
|
+
}), se = W(
|
|
59
|
+
(r) => {
|
|
60
|
+
L.current = r, j(r);
|
|
59
61
|
},
|
|
60
|
-
[
|
|
62
|
+
[j]
|
|
61
63
|
);
|
|
62
|
-
|
|
63
|
-
var
|
|
64
|
+
if (U(() => {
|
|
65
|
+
var r;
|
|
64
66
|
if (c)
|
|
65
|
-
|
|
66
|
-
s.current = null,
|
|
67
|
+
C(!0), E(!1), t.current && (clearTimeout(t.current), t.current = null), s.current = requestAnimationFrame(() => {
|
|
68
|
+
s.current = null, O(!0);
|
|
67
69
|
});
|
|
68
70
|
else {
|
|
69
|
-
const i =
|
|
70
|
-
i && ((
|
|
71
|
-
t.current = null,
|
|
72
|
-
},
|
|
71
|
+
const i = pe();
|
|
72
|
+
i && ((r = L.current) != null && r.contains(i)) && i.blur(), E(!0), O(!1), t.current = setTimeout(() => {
|
|
73
|
+
t.current = null, C(!1);
|
|
74
|
+
}, g);
|
|
73
75
|
}
|
|
74
76
|
return () => {
|
|
75
77
|
s.current !== null && (cancelAnimationFrame(s.current), s.current = null), t.current && (clearTimeout(t.current), t.current = null);
|
|
76
78
|
};
|
|
77
|
-
}, [c]),
|
|
79
|
+
}, [c]), !le)
|
|
80
|
+
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(
|
|
78
83
|
// Host carries the stacking context — ShadowRootWrapper's `isolation` would otherwise
|
|
79
84
|
// flatten our z-index. Not promoted to the top layer, so host dialogs can cover us.
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
-
|
|
85
|
+
/* @__PURE__ */ e(
|
|
86
|
+
de,
|
|
82
87
|
{
|
|
83
|
-
hostRef:
|
|
84
|
-
hostStyle: { position: "relative", zIndex:
|
|
85
|
-
hostProps:
|
|
88
|
+
hostRef: Y,
|
|
89
|
+
hostStyle: { position: "relative", zIndex: ne },
|
|
90
|
+
hostProps: oe ? { ...z("modal"), [ge]: "" } : z("modal"),
|
|
86
91
|
children: /* @__PURE__ */ l(
|
|
87
92
|
"div",
|
|
88
93
|
{
|
|
89
94
|
className: `fixed inset-0 z-[1000] flex items-center justify-center p-[16px] ${c ? "" : "pointer-events-none"}`,
|
|
90
95
|
children: [
|
|
91
|
-
/* @__PURE__ */
|
|
96
|
+
/* @__PURE__ */ e(
|
|
92
97
|
"div",
|
|
93
98
|
{
|
|
94
99
|
"aria-hidden": "true",
|
|
95
|
-
onClick:
|
|
96
|
-
className: `absolute inset-0 bg-[var(--malachite-overlay-scrim)] transition-opacity duration-200 ease-in-out ${
|
|
100
|
+
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"}`
|
|
97
102
|
}
|
|
98
103
|
),
|
|
99
104
|
/* @__PURE__ */ l(
|
|
100
105
|
"div",
|
|
101
106
|
{
|
|
102
|
-
ref:
|
|
103
|
-
role:
|
|
104
|
-
"aria-hidden":
|
|
105
|
-
"aria-modal":
|
|
107
|
+
ref: se,
|
|
108
|
+
role: ee,
|
|
109
|
+
"aria-hidden": te ? "true" : void 0,
|
|
110
|
+
"aria-modal": ie ? "true" : void 0,
|
|
106
111
|
"aria-describedby": [
|
|
107
|
-
...
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
...u.map((r, i) => `${S}-${i}`),
|
|
113
|
+
re,
|
|
114
|
+
a != null && a.caption ? k : null
|
|
110
115
|
].filter(Boolean).join(" ") || void 0,
|
|
111
116
|
"aria-busy": o || void 0,
|
|
112
117
|
tabIndex: -1,
|
|
113
|
-
"aria-label":
|
|
114
|
-
"aria-labelledby":
|
|
115
|
-
className:
|
|
118
|
+
"aria-label": $ ? void 0 : ae ?? (typeof p == "string" ? p : void 0),
|
|
119
|
+
"aria-labelledby": $,
|
|
120
|
+
className: v,
|
|
116
121
|
style: {
|
|
117
|
-
maxWidth:
|
|
118
|
-
...
|
|
119
|
-
backgroundColor:
|
|
122
|
+
maxWidth: a ? `calc(${G[A]} + 8px)` : G[A],
|
|
123
|
+
...a ? {
|
|
124
|
+
backgroundColor: xe[a.tone ?? "neutral"]
|
|
120
125
|
} : {},
|
|
121
126
|
// Inline: Tailwind scale/translate utilities silently break inside
|
|
122
127
|
// shadow roots (see CLAUDE.md Shadow DOM caveats).
|
|
123
|
-
transform:
|
|
124
|
-
opacity:
|
|
125
|
-
transition: `transform ${
|
|
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`
|
|
126
131
|
},
|
|
127
132
|
children: [
|
|
128
|
-
|
|
133
|
+
m ? (
|
|
134
|
+
// Still a scroll body: the page behind is locked, so content taller
|
|
135
|
+
// than the viewport has nowhere else to scroll.
|
|
136
|
+
/* @__PURE__ */ e(
|
|
137
|
+
"div",
|
|
138
|
+
{
|
|
139
|
+
className: `min-h-0 ${I === "tall" ? q : "flex-1"} overflow-y-auto`,
|
|
140
|
+
children: x
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
) : /* @__PURE__ */ l(
|
|
129
144
|
"div",
|
|
130
145
|
{
|
|
131
|
-
className:
|
|
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",
|
|
132
147
|
children: [
|
|
133
148
|
/* @__PURE__ */ l(
|
|
134
149
|
"div",
|
|
135
150
|
{
|
|
136
|
-
className: `flex gap-[20px] p-[20px] ${
|
|
151
|
+
className: `flex gap-[20px] p-[20px] ${f ? "items-start" : "items-center"} ${R ? "border-0 border-b border-solid border-[var(--malachite-card-border)]" : ""}`,
|
|
137
152
|
children: [
|
|
138
153
|
/* @__PURE__ */ l("div", { className: "flex h-[18px] min-w-0 flex-1 items-center gap-[12px]", children: [
|
|
139
154
|
/* @__PURE__ */ l("div", { className: "flex min-w-0 items-center gap-[8px]", children: [
|
|
140
|
-
|
|
155
|
+
N ? /* @__PURE__ */ e("span", { className: "flex shrink-0 items-center", children: N }) : null,
|
|
141
156
|
/* @__PURE__ */ l("div", { className: "flex min-w-0 flex-col gap-[4px]", children: [
|
|
142
|
-
/* @__PURE__ */
|
|
143
|
-
|
|
157
|
+
/* @__PURE__ */ e("span", { className: "truncate text-[14px] font-medium leading-[1.25] tracking-[-0.14px] text-[var(--malachite-text-primary)]", children: p }),
|
|
158
|
+
f ? /* @__PURE__ */ e("span", { className: "text-[13px] leading-[1.6] text-[var(--malachite-text-secondary)]", children: f }) : null
|
|
144
159
|
] })
|
|
145
160
|
] }),
|
|
146
|
-
|
|
147
|
-
/* @__PURE__ */
|
|
161
|
+
y ? /* @__PURE__ */ l(V, { children: [
|
|
162
|
+
/* @__PURE__ */ e(
|
|
148
163
|
"span",
|
|
149
164
|
{
|
|
150
165
|
"aria-hidden": "true",
|
|
151
166
|
className: "h-[12px] w-px shrink-0 bg-[var(--malachite-card-border)]"
|
|
152
167
|
}
|
|
153
168
|
),
|
|
154
|
-
/* @__PURE__ */
|
|
169
|
+
/* @__PURE__ */ e("span", { className: "flex min-w-0 items-center overflow-hidden", children: y })
|
|
155
170
|
] }) : null
|
|
156
171
|
] }),
|
|
157
|
-
|
|
158
|
-
|
|
172
|
+
w ? /* @__PURE__ */ e("span", { className: "flex shrink-0 items-center", children: w }) : null,
|
|
173
|
+
Z ? /* @__PURE__ */ e(
|
|
159
174
|
"button",
|
|
160
175
|
{
|
|
161
176
|
type: "button",
|
|
162
177
|
"aria-label": "Close",
|
|
163
|
-
onClick:
|
|
178
|
+
onClick: B,
|
|
164
179
|
disabled: o,
|
|
165
|
-
className:
|
|
166
|
-
children: /* @__PURE__ */
|
|
180
|
+
className: be,
|
|
181
|
+
children: /* @__PURE__ */ e(ue, { size: 10 })
|
|
167
182
|
}
|
|
168
183
|
) : null
|
|
169
184
|
]
|
|
@@ -172,51 +187,51 @@ const U = {
|
|
|
172
187
|
/* @__PURE__ */ l(
|
|
173
188
|
"div",
|
|
174
189
|
{
|
|
175
|
-
className: `min-h-0 ${
|
|
190
|
+
className: `min-h-0 ${I === "tall" ? q : "flex-1"} overflow-y-auto ${T && u.length === 0 ? "px-[20px]" : ""}`,
|
|
176
191
|
children: [
|
|
177
|
-
|
|
178
|
-
|
|
192
|
+
u.map((r, i) => /* @__PURE__ */ e(
|
|
193
|
+
me,
|
|
179
194
|
{
|
|
180
195
|
id: `${S}-${i}`,
|
|
181
|
-
type:
|
|
182
|
-
message:
|
|
183
|
-
renderLink:
|
|
196
|
+
type: r.type,
|
|
197
|
+
message: r.message,
|
|
198
|
+
renderLink: Q
|
|
184
199
|
},
|
|
185
200
|
i
|
|
186
201
|
)),
|
|
187
|
-
|
|
202
|
+
T ? /* @__PURE__ */ e(
|
|
188
203
|
"div",
|
|
189
204
|
{
|
|
190
|
-
className:
|
|
191
|
-
children:
|
|
205
|
+
className: u.length > 0 ? "px-[20px] pt-[18px] pb-[20px]" : `pb-[20px] ${R ? "pt-[18px]" : ""}`,
|
|
206
|
+
children: x
|
|
192
207
|
}
|
|
193
|
-
) :
|
|
208
|
+
) : x
|
|
194
209
|
]
|
|
195
210
|
}
|
|
196
211
|
),
|
|
197
|
-
|
|
212
|
+
_ ? /* @__PURE__ */ e(
|
|
198
213
|
"div",
|
|
199
214
|
{
|
|
200
215
|
className: "flex min-h-[56px] shrink-0 items-center border-0 border-t border-solid border-[var(--malachite-card-border)] px-[12px]",
|
|
201
|
-
style:
|
|
202
|
-
children:
|
|
216
|
+
style: fe,
|
|
217
|
+
children: _
|
|
203
218
|
}
|
|
204
219
|
) : null
|
|
205
220
|
]
|
|
206
221
|
}
|
|
207
222
|
),
|
|
208
|
-
|
|
223
|
+
a != null && a.caption ? /* @__PURE__ */ e(
|
|
209
224
|
"div",
|
|
210
225
|
{
|
|
211
226
|
id: k,
|
|
212
227
|
className: "flex h-[48px] shrink-0 items-center justify-center px-[16px] text-[13px] tracking-[0.13px]",
|
|
213
|
-
style: { color:
|
|
228
|
+
style: { color: he[a.tone ?? "neutral"] },
|
|
214
229
|
children: /* @__PURE__ */ l("span", { children: [
|
|
215
|
-
|
|
216
|
-
/* @__PURE__ */
|
|
230
|
+
a.caption.label ? /* @__PURE__ */ l(V, { children: [
|
|
231
|
+
/* @__PURE__ */ e("span", { className: "font-medium", children: a.caption.label }),
|
|
217
232
|
" "
|
|
218
233
|
] }) : null,
|
|
219
|
-
|
|
234
|
+
a.caption.message
|
|
220
235
|
] })
|
|
221
236
|
}
|
|
222
237
|
) : null
|
|
@@ -229,10 +244,10 @@ const U = {
|
|
|
229
244
|
}
|
|
230
245
|
),
|
|
231
246
|
document.body
|
|
232
|
-
)
|
|
247
|
+
);
|
|
233
248
|
};
|
|
234
249
|
export {
|
|
235
|
-
|
|
236
|
-
|
|
250
|
+
Oe as Modal,
|
|
251
|
+
Oe as default
|
|
237
252
|
};
|
|
238
253
|
//# sourceMappingURL=Modal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.js","sources":["../../src/components/Modal.tsx"],"sourcesContent":["import type React from 'react';\nimport { useCallback, useEffect, useId, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ShadowRootWrapper } from '../ShadowRootWrapper';\nimport type { AlertType } from './Alert';\nimport CalloutStrip from './CalloutStrip';\nimport DismissIcon from './CustomIcons/DismissIcon';\nimport type { RenderLink } from './PageLayout/types';\nimport { getDeepActiveElement } from './activeElement';\nimport { GLASS_SURFACE_STYLE } from './glassSurface';\nimport { MODAL_TRAY_BACKGROUND, MODAL_TRAY_CAPTION_COLOR, type ModalTrayTone } from './modalTray';\nimport { OVERLAY_ICON_BUTTON_CLASSES, useOverlayBehaviour } from './overlayBehaviour';\nimport { OVERLAY_ELEVATED_ATTR, overlayIdentity } from './portalOverlay';\n\nexport type ModalSize = 'small' | 'medium' | 'large';\n\n/** Widths keyed to the size union — a raw `width` prop would let a consumer off the scale. */\nconst SIZE_MAX_WIDTH: Record<ModalSize, string> = {\n small: '420px',\n medium: '612px',\n large: '760px',\n};\n\nconst TRANSITION_MS = 200;\n\n// A basis of its own: a `flex-1` body's zero basis would override the height in Chrome.\nconst TALL_BODY = 'flex-[0_1_auto] h-[min(600px,calc(100dvh-160px))]';\n\nexport interface ModalProps {\n open: boolean;\n /** Dismiss handler — close button, ESC, or overlay click. */\n onClose: () => void;\n title: React.ReactNode;\n /** Secondary line beneath the title. */\n subtitle?: React.ReactNode;\n /** Leading header slot, typically an icon or logo. */\n icon?: React.ReactNode;\n /** Content pinned right in the header, before the close button. */\n headerExtra?: React.ReactNode;\n titleAdornment?: React.ReactNode;\n tray?: { tone?: ModalTrayTone; caption?: { label?: string; message: string } };\n /** Divider under the header. Default: false. */\n headerDivider?: boolean;\n /** Pinned action row below the scrolling body. */\n footer?: React.ReactNode;\n size?: ModalSize;\n /** Default: true. */\n showCloseButton?: boolean;\n /** Default: true. Turn off for a dialog whose choice must be made deliberately. */\n closeOnOverlayClick?: boolean;\n /** Whether the body gets default padding. Default: true. */\n bodyPadding?: boolean;\n /** `tall`: 600px, or the viewport less 160px, for content that sizes to its box. */\n bodyHeight?: 'auto' | 'tall';\n /** Full-width Alert bands at the top of the body, above `children`, in order. Unaffected by\n * `bodyPadding`, and announced ahead of the dialog's description. The message is markdown. */\n callouts?: { type?: AlertType; message: string }[];\n /** Routes root-relative links in `callouts` through the app's router; see `MarkdownBlock`. */\n renderLink?: RenderLink;\n /**\n * `alertdialog` for a dialog interrupting the user over a consequential choice — it\n * tells assistive tech to announce the body, not just the name.\n */\n role?: 'dialog' | 'alertdialog';\n /** Accessible dialog name; required when `title` isn't a plain string. */\n 'aria-label'?: string;\n /** ID of a visible element naming the dialog (alternative to `aria-label`). */\n 'aria-labelledby'?: string;\n /** ID of the element describing what the dialog is asking. */\n 'aria-describedby'?: string;\n /**\n * Work is in flight: suppresses the pointer dismissal routes and announces the busy\n * state. Never ESC — that is the only guaranteed keyboard exit from a focus trap, so it\n * always reaches `onClose` and whether to abort is left to the caller.\n */\n busy?: boolean;\n children: React.ReactNode;\n}\n\nconst Modal: React.FC<ModalProps> = ({\n open,\n onClose,\n title,\n subtitle,\n icon,\n headerExtra,\n titleAdornment,\n tray,\n headerDivider = false,\n footer,\n size = 'medium',\n showCloseButton = true,\n closeOnOverlayClick = true,\n bodyPadding = true,\n bodyHeight = 'auto',\n callouts,\n renderLink,\n role = 'dialog',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n busy = false,\n children,\n}) => {\n const captionId = useId();\n const calloutId = useId();\n const shownCallouts = callouts?.filter((callout) => callout.message.trim()) ?? [];\n const [isVisible, setIsVisible] = useState(false);\n // Starts false even when `open` does: portals are unsupported on the server, so the\n // dialog must reach `document.body` from the client effect below and nowhere else.\n const [isMounted, setIsMounted] = useState(false);\n // Distinct from `!isVisible`, which is also false on the frame the dialog opens.\n const [isExiting, setIsExiting] = useState(false);\n const panelRef = useRef<HTMLElement | null>(null);\n const frameRef = useRef<number | null>(null);\n const unmountTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const busyRef = useRef(busy);\n const onCloseRef = useRef(onClose);\n useEffect(() => {\n busyRef.current = busy;\n onCloseRef.current = onClose;\n }, [busy, onClose]);\n\n // Pointer routes only. Removing ESC too would leave a focus-trapped keyboard user no\n // exit while a request hangs (WCAG 2.1.2), so ESC always reaches onClose.\n const dismissByPointer = useCallback(() => {\n if (!busyRef.current) {\n onCloseRef.current();\n }\n }, []);\n const hostRef = useRef<HTMLDivElement | null>(null);\n // No prop of its own: it stays in the base band unless a SidePanel elevated the stack,\n // and follows it up when one has.\n const {\n ref: takePanelRef,\n isTopmost,\n zIndex,\n elevated,\n } = useOverlayBehaviour({\n open,\n onClose,\n hostRef,\n });\n const handlePanelRef = useCallback(\n (node: HTMLElement | null) => {\n panelRef.current = node;\n takePanelRef(node);\n },\n [takePanelRef],\n );\n\n // Mounted outlives `open` by one transition, so the dialog can animate out.\n useEffect(() => {\n if (open) {\n setIsMounted(true);\n setIsExiting(false);\n if (unmountTimerRef.current) {\n clearTimeout(unmountTimerRef.current);\n unmountTimerRef.current = null;\n }\n frameRef.current = requestAnimationFrame(() => {\n frameRef.current = null;\n setIsVisible(true);\n });\n } else {\n // A browser refuses `aria-hidden` on a subtree still holding focus, so the\n // attribute waits for this point — after useOverlayBehaviour's cleanup has\n // returned focus to the trigger — and blurs what is left if the trigger has gone.\n const active = getDeepActiveElement();\n if (active && panelRef.current?.contains(active)) {\n active.blur();\n }\n setIsExiting(true);\n setIsVisible(false);\n unmountTimerRef.current = setTimeout(() => {\n unmountTimerRef.current = null;\n setIsMounted(false);\n }, TRANSITION_MS);\n }\n return () => {\n if (frameRef.current !== null) {\n cancelAnimationFrame(frameRef.current);\n frameRef.current = null;\n }\n if (unmountTimerRef.current) {\n clearTimeout(unmountTimerRef.current);\n unmountTimerRef.current = null;\n }\n };\n }, [open]);\n\n if (!isMounted) {\n return null;\n }\n\n return createPortal(\n // Host carries the stacking context — ShadowRootWrapper's `isolation` would otherwise\n // flatten our z-index. Not promoted to the top layer, so host dialogs can cover us.\n <ShadowRootWrapper\n hostRef={hostRef}\n hostStyle={{ position: 'relative', zIndex }}\n hostProps={\n elevated\n ? { ...overlayIdentity('modal'), [OVERLAY_ELEVATED_ATTR]: '' }\n : overlayIdentity('modal')\n }\n >\n {/* The exit transition outlives `open`, so a closed-but-mounted dialog has to stop\n taking clicks and leave the a11y tree — a faded opacity does neither. */}\n <div\n className={`fixed inset-0 z-[1000] flex items-center justify-center p-[16px] ${\n open ? '' : 'pointer-events-none'\n }`}\n >\n <div\n aria-hidden=\"true\"\n onClick={closeOnOverlayClick ? dismissByPointer : undefined}\n className={`absolute inset-0 bg-[var(--malachite-overlay-scrim)] transition-opacity duration-200 ease-in-out ${\n isVisible ? 'opacity-100' : 'opacity-0'\n }`}\n />\n <div\n ref={handlePanelRef}\n role={role}\n aria-hidden={isExiting ? 'true' : undefined}\n aria-modal={isTopmost ? 'true' : undefined}\n aria-describedby={\n [\n ...shownCallouts.map((_, index) => `${calloutId}-${index}`),\n ariaDescribedBy,\n tray?.caption ? captionId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined\n }\n aria-busy={busy || undefined}\n tabIndex={-1}\n aria-label={\n ariaLabelledBy\n ? undefined\n : (ariaLabel ?? (typeof title === 'string' ? title : undefined))\n }\n aria-labelledby={ariaLabelledBy}\n className={\n tray\n ? '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))]'\n : '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))]'\n }\n style={{\n maxWidth: tray\n ? `calc(${SIZE_MAX_WIDTH[size]} + 8px)`\n : SIZE_MAX_WIDTH[size],\n ...(tray\n ? {\n backgroundColor: MODAL_TRAY_BACKGROUND[tray.tone ?? 'neutral'],\n }\n : {}),\n // Inline: Tailwind scale/translate utilities silently break inside\n // shadow roots (see CLAUDE.md Shadow DOM caveats).\n transform: isVisible ? 'scale(1)' : 'scale(0.96)',\n opacity: isVisible ? 1 : 0,\n transition: `transform ${TRANSITION_MS}ms ease-out, opacity ${TRANSITION_MS}ms ease-out`,\n }}\n >\n <div\n className={\n tray\n ? '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))]'\n : 'contents'\n }\n >\n <div\n className={`flex gap-[20px] p-[20px] ${\n subtitle ? 'items-start' : 'items-center'\n } ${\n headerDivider\n ? 'border-0 border-b border-solid border-[var(--malachite-card-border)]'\n : ''\n }`}\n >\n <div className=\"flex h-[18px] min-w-0 flex-1 items-center gap-[12px]\">\n <div className=\"flex min-w-0 items-center gap-[8px]\">\n {icon ? (\n <span className=\"flex shrink-0 items-center\">{icon}</span>\n ) : null}\n <div className=\"flex min-w-0 flex-col gap-[4px]\">\n <span className=\"truncate text-[14px] font-medium leading-[1.25] tracking-[-0.14px] text-[var(--malachite-text-primary)]\">\n {title}\n </span>\n {subtitle ? (\n <span className=\"text-[13px] leading-[1.6] text-[var(--malachite-text-secondary)]\">\n {subtitle}\n </span>\n ) : null}\n </div>\n </div>\n {titleAdornment ? (\n <>\n <span\n aria-hidden=\"true\"\n className=\"h-[12px] w-px shrink-0 bg-[var(--malachite-card-border)]\"\n />\n <span className=\"flex min-w-0 items-center overflow-hidden\">\n {titleAdornment}\n </span>\n </>\n ) : null}\n </div>\n {headerExtra ? (\n <span className=\"flex shrink-0 items-center\">{headerExtra}</span>\n ) : null}\n {showCloseButton ? (\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={dismissByPointer}\n disabled={busy}\n className={OVERLAY_ICON_BUTTON_CLASSES}\n >\n <DismissIcon size={10} />\n </button>\n ) : null}\n </div>\n <div\n className={`min-h-0 ${bodyHeight === 'tall' ? TALL_BODY : 'flex-1'} overflow-y-auto ${\n bodyPadding && shownCallouts.length === 0 ? 'px-[20px]' : ''\n }`}\n >\n {shownCallouts.map((callout, index) => (\n <CalloutStrip\n key={index}\n id={`${calloutId}-${index}`}\n type={callout.type}\n message={callout.message}\n renderLink={renderLink}\n />\n ))}\n {bodyPadding ? (\n <div\n className={\n shownCallouts.length > 0\n ? 'px-[20px] pt-[18px] pb-[20px]'\n : `pb-[20px] ${headerDivider ? 'pt-[18px]' : ''}`\n }\n >\n {children}\n </div>\n ) : (\n children\n )}\n </div>\n {footer ? (\n <div\n className=\"flex min-h-[56px] shrink-0 items-center border-0 border-t border-solid border-[var(--malachite-card-border)] px-[12px]\"\n style={GLASS_SURFACE_STYLE}\n >\n {footer}\n </div>\n ) : null}\n </div>\n {tray?.caption ? (\n <div\n id={captionId}\n className=\"flex h-[48px] shrink-0 items-center justify-center px-[16px] text-[13px] tracking-[0.13px]\"\n style={{ color: MODAL_TRAY_CAPTION_COLOR[tray.tone ?? 'neutral'] }}\n >\n <span>\n {tray.caption.label ? (\n <>\n <span className=\"font-medium\">\n {tray.caption.label}\n </span>{' '}\n </>\n ) : null}\n {tray.caption.message}\n </span>\n </div>\n ) : null}\n </div>\n </div>\n </ShadowRootWrapper>,\n document.body,\n );\n};\n\nexport default Modal;\nexport { Modal };\n"],"names":["SIZE_MAX_WIDTH","TRANSITION_MS","TALL_BODY","Modal","open","onClose","title","subtitle","icon","headerExtra","titleAdornment","tray","headerDivider","footer","size","showCloseButton","closeOnOverlayClick","bodyPadding","bodyHeight","callouts","renderLink","role","ariaLabel","ariaLabelledBy","ariaDescribedBy","busy","children","captionId","useId","calloutId","shownCallouts","callout","isVisible","setIsVisible","useState","isMounted","setIsMounted","isExiting","setIsExiting","panelRef","useRef","frameRef","unmountTimerRef","busyRef","onCloseRef","useEffect","dismissByPointer","useCallback","hostRef","takePanelRef","isTopmost","zIndex","elevated","useOverlayBehaviour","handlePanelRef","node","active","getDeepActiveElement","_a","createPortal","jsx","ShadowRootWrapper","overlayIdentity","OVERLAY_ELEVATED_ATTR","jsxs","_","index","MODAL_TRAY_BACKGROUND","Fragment","OVERLAY_ICON_BUTTON_CLASSES","DismissIcon","CalloutStrip","GLASS_SURFACE_STYLE","MODAL_TRAY_CAPTION_COLOR"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Modal.js","sources":["../../src/components/Modal.tsx"],"sourcesContent":["import type React from 'react';\nimport { useCallback, useEffect, useId, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { ShadowRootWrapper } from '../ShadowRootWrapper';\nimport type { AlertType } from './Alert';\nimport CalloutStrip from './CalloutStrip';\nimport DismissIcon from './CustomIcons/DismissIcon';\nimport type { RenderLink } from './PageLayout/types';\nimport { getDeepActiveElement } from './activeElement';\nimport { GLASS_SURFACE_STYLE } from './glassSurface';\nimport { MODAL_TRAY_BACKGROUND, MODAL_TRAY_CAPTION_COLOR, type ModalTrayTone } from './modalTray';\nimport { OVERLAY_ICON_BUTTON_CLASSES, useOverlayBehaviour } from './overlayBehaviour';\nimport { OVERLAY_ELEVATED_ATTR, overlayIdentity } from './portalOverlay';\n\nexport type ModalSize = 'small' | 'medium' | 'large';\nexport type ModalSurface = 'card' | 'none';\n\n/** Widths keyed to the size union — a raw `width` prop would let a consumer off the scale. */\nconst SIZE_MAX_WIDTH: Record<ModalSize, string> = {\n small: '420px',\n medium: '612px',\n large: '760px',\n};\n\nconst TRANSITION_MS = 200;\n\n// A basis of its own: a `flex-1` body's zero basis would override the height in Chrome.\nconst TALL_BODY = 'flex-[0_1_auto] h-[min(600px,calc(100dvh-160px))]';\n\nexport interface ModalProps {\n open: boolean;\n /** Dismiss handler — close button, ESC, or overlay click. */\n onClose: () => void;\n title: React.ReactNode;\n /** Secondary line beneath the title. */\n subtitle?: React.ReactNode;\n /** Leading header slot, typically an icon or logo. */\n icon?: React.ReactNode;\n /** Content pinned right in the header, before the close button. */\n headerExtra?: React.ReactNode;\n titleAdornment?: React.ReactNode;\n tray?: { tone?: ModalTrayTone; caption?: { label?: string; message: string } };\n /** Divider under the header. Default: false. */\n headerDivider?: boolean;\n /** Pinned action row below the scrolling body. */\n footer?: React.ReactNode;\n size?: ModalSize;\n /**\n * `none` draws no card: no header, close button, footer, callouts or tray, only the\n * overlay and a scroll body around content that brings its own chrome, such as the Hub.\n * `title` then only names the dialog for assistive tech. Default: `card`.\n */\n surface?: ModalSurface;\n /** Default: true. */\n showCloseButton?: boolean;\n /** Default: true. Turn off for a dialog whose choice must be made deliberately. */\n closeOnOverlayClick?: boolean;\n /** Whether the body gets default padding. Default: true. */\n bodyPadding?: boolean;\n /** `tall`: 600px, or the viewport less 160px, for content that sizes to its box. */\n bodyHeight?: 'auto' | 'tall';\n /** Full-width Alert bands at the top of the body, above `children`, in order. Unaffected by\n * `bodyPadding`, and announced ahead of the dialog's description. The message is markdown. */\n callouts?: { type?: AlertType; message: string }[];\n /** Routes root-relative links in `callouts` through the app's router; see `MarkdownBlock`. */\n renderLink?: RenderLink;\n /**\n * `alertdialog` for a dialog interrupting the user over a consequential choice — it\n * tells assistive tech to announce the body, not just the name.\n */\n role?: 'dialog' | 'alertdialog';\n /** Accessible dialog name; required when `title` isn't a plain string. */\n 'aria-label'?: string;\n /** ID of a visible element naming the dialog (alternative to `aria-label`). */\n 'aria-labelledby'?: string;\n /** ID of the element describing what the dialog is asking. */\n 'aria-describedby'?: string;\n /**\n * Work is in flight: suppresses the pointer dismissal routes and announces the busy\n * state. Never ESC — that is the only guaranteed keyboard exit from a focus trap, so it\n * always reaches `onClose` and whether to abort is left to the caller.\n */\n busy?: boolean;\n children: React.ReactNode;\n}\n\nconst Modal: React.FC<ModalProps> = ({\n open,\n onClose,\n title,\n subtitle,\n icon,\n headerExtra,\n titleAdornment,\n tray: trayProp,\n headerDivider = false,\n footer,\n size = 'medium',\n surface = 'card',\n showCloseButton = true,\n closeOnOverlayClick = true,\n bodyPadding = true,\n bodyHeight = 'auto',\n callouts,\n renderLink,\n role = 'dialog',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n busy = false,\n children,\n}) => {\n const captionId = useId();\n const calloutId = useId();\n const isBare = surface === 'none';\n // A bare dialog has no card to ring or caption, so the tray is not drawn either.\n const tray = isBare ? undefined : trayProp;\n const shownCallouts =\n (isBare ? undefined : callouts)?.filter((callout) => callout.message.trim()) ?? [];\n const [isVisible, setIsVisible] = useState(false);\n // Starts false even when `open` does: portals are unsupported on the server, so the\n // dialog must reach `document.body` from the client effect below and nowhere else.\n const [isMounted, setIsMounted] = useState(false);\n // Distinct from `!isVisible`, which is also false on the frame the dialog opens.\n const [isExiting, setIsExiting] = useState(false);\n const panelRef = useRef<HTMLElement | null>(null);\n const frameRef = useRef<number | null>(null);\n const unmountTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const busyRef = useRef(busy);\n const onCloseRef = useRef(onClose);\n useEffect(() => {\n busyRef.current = busy;\n onCloseRef.current = onClose;\n }, [busy, onClose]);\n\n // Pointer routes only. Removing ESC too would leave a focus-trapped keyboard user no\n // exit while a request hangs (WCAG 2.1.2), so ESC always reaches onClose.\n const dismissByPointer = useCallback(() => {\n if (!busyRef.current) {\n onCloseRef.current();\n }\n }, []);\n const hostRef = useRef<HTMLDivElement | null>(null);\n // No prop of its own: it stays in the base band unless a SidePanel elevated the stack,\n // and follows it up when one has.\n const {\n ref: takePanelRef,\n isTopmost,\n zIndex,\n elevated,\n } = useOverlayBehaviour({\n open,\n onClose,\n hostRef,\n });\n const handlePanelRef = useCallback(\n (node: HTMLElement | null) => {\n panelRef.current = node;\n takePanelRef(node);\n },\n [takePanelRef],\n );\n\n // Mounted outlives `open` by one transition, so the dialog can animate out.\n useEffect(() => {\n if (open) {\n setIsMounted(true);\n setIsExiting(false);\n if (unmountTimerRef.current) {\n clearTimeout(unmountTimerRef.current);\n unmountTimerRef.current = null;\n }\n frameRef.current = requestAnimationFrame(() => {\n frameRef.current = null;\n setIsVisible(true);\n });\n } else {\n // A browser refuses `aria-hidden` on a subtree still holding focus, so the\n // attribute waits for this point — after useOverlayBehaviour's cleanup has\n // returned focus to the trigger — and blurs what is left if the trigger has gone.\n const active = getDeepActiveElement();\n if (active && panelRef.current?.contains(active)) {\n active.blur();\n }\n setIsExiting(true);\n setIsVisible(false);\n unmountTimerRef.current = setTimeout(() => {\n unmountTimerRef.current = null;\n setIsMounted(false);\n }, TRANSITION_MS);\n }\n return () => {\n if (frameRef.current !== null) {\n cancelAnimationFrame(frameRef.current);\n frameRef.current = null;\n }\n if (unmountTimerRef.current) {\n clearTimeout(unmountTimerRef.current);\n unmountTimerRef.current = null;\n }\n };\n }, [open]);\n\n if (!isMounted) {\n return null;\n }\n\n let panelClassName =\n '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))]';\n if (isBare) {\n panelClassName = 'relative flex max-h-full w-full flex-col outline-none';\n } else if (tray) {\n panelClassName =\n '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))]';\n }\n\n return createPortal(\n // Host carries the stacking context — ShadowRootWrapper's `isolation` would otherwise\n // flatten our z-index. Not promoted to the top layer, so host dialogs can cover us.\n <ShadowRootWrapper\n hostRef={hostRef}\n hostStyle={{ position: 'relative', zIndex }}\n hostProps={\n elevated\n ? { ...overlayIdentity('modal'), [OVERLAY_ELEVATED_ATTR]: '' }\n : overlayIdentity('modal')\n }\n >\n {/* The exit transition outlives `open`, so a closed-but-mounted dialog has to stop\n taking clicks and leave the a11y tree — a faded opacity does neither. */}\n <div\n className={`fixed inset-0 z-[1000] flex items-center justify-center p-[16px] ${\n open ? '' : 'pointer-events-none'\n }`}\n >\n <div\n aria-hidden=\"true\"\n onClick={closeOnOverlayClick ? dismissByPointer : undefined}\n className={`absolute inset-0 bg-[var(--malachite-overlay-scrim)] transition-opacity duration-200 ease-in-out ${\n isVisible ? 'opacity-100' : 'opacity-0'\n }`}\n />\n <div\n ref={handlePanelRef}\n role={role}\n aria-hidden={isExiting ? 'true' : undefined}\n aria-modal={isTopmost ? 'true' : undefined}\n aria-describedby={\n [\n ...shownCallouts.map((_, index) => `${calloutId}-${index}`),\n ariaDescribedBy,\n tray?.caption ? captionId : null,\n ]\n .filter(Boolean)\n .join(' ') || undefined\n }\n aria-busy={busy || undefined}\n tabIndex={-1}\n aria-label={\n ariaLabelledBy\n ? undefined\n : (ariaLabel ?? (typeof title === 'string' ? title : undefined))\n }\n aria-labelledby={ariaLabelledBy}\n className={panelClassName}\n style={{\n maxWidth: tray\n ? `calc(${SIZE_MAX_WIDTH[size]} + 8px)`\n : SIZE_MAX_WIDTH[size],\n ...(tray\n ? {\n backgroundColor: MODAL_TRAY_BACKGROUND[tray.tone ?? 'neutral'],\n }\n : {}),\n // Inline: Tailwind scale/translate utilities silently break inside\n // shadow roots (see CLAUDE.md Shadow DOM caveats).\n transform: isVisible ? 'scale(1)' : 'scale(0.96)',\n opacity: isVisible ? 1 : 0,\n transition: `transform ${TRANSITION_MS}ms ease-out, opacity ${TRANSITION_MS}ms ease-out`,\n }}\n >\n {isBare ? (\n // Still a scroll body: the page behind is locked, so content taller\n // than the viewport has nowhere else to scroll.\n <div\n className={`min-h-0 ${bodyHeight === 'tall' ? TALL_BODY : 'flex-1'} overflow-y-auto`}\n >\n {children}\n </div>\n ) : (\n <div\n className={\n tray\n ? '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))]'\n : 'contents'\n }\n >\n <div\n className={`flex gap-[20px] p-[20px] ${\n subtitle ? 'items-start' : 'items-center'\n } ${\n headerDivider\n ? 'border-0 border-b border-solid border-[var(--malachite-card-border)]'\n : ''\n }`}\n >\n <div className=\"flex h-[18px] min-w-0 flex-1 items-center gap-[12px]\">\n <div className=\"flex min-w-0 items-center gap-[8px]\">\n {icon ? (\n <span className=\"flex shrink-0 items-center\">\n {icon}\n </span>\n ) : null}\n <div className=\"flex min-w-0 flex-col gap-[4px]\">\n <span className=\"truncate text-[14px] font-medium leading-[1.25] tracking-[-0.14px] text-[var(--malachite-text-primary)]\">\n {title}\n </span>\n {subtitle ? (\n <span className=\"text-[13px] leading-[1.6] text-[var(--malachite-text-secondary)]\">\n {subtitle}\n </span>\n ) : null}\n </div>\n </div>\n {titleAdornment ? (\n <>\n <span\n aria-hidden=\"true\"\n className=\"h-[12px] w-px shrink-0 bg-[var(--malachite-card-border)]\"\n />\n <span className=\"flex min-w-0 items-center overflow-hidden\">\n {titleAdornment}\n </span>\n </>\n ) : null}\n </div>\n {headerExtra ? (\n <span className=\"flex shrink-0 items-center\">\n {headerExtra}\n </span>\n ) : null}\n {showCloseButton ? (\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={dismissByPointer}\n disabled={busy}\n className={OVERLAY_ICON_BUTTON_CLASSES}\n >\n <DismissIcon size={10} />\n </button>\n ) : null}\n </div>\n <div\n className={`min-h-0 ${bodyHeight === 'tall' ? TALL_BODY : 'flex-1'} overflow-y-auto ${\n bodyPadding && shownCallouts.length === 0 ? 'px-[20px]' : ''\n }`}\n >\n {shownCallouts.map((callout, index) => (\n <CalloutStrip\n key={index}\n id={`${calloutId}-${index}`}\n type={callout.type}\n message={callout.message}\n renderLink={renderLink}\n />\n ))}\n {bodyPadding ? (\n <div\n className={\n shownCallouts.length > 0\n ? 'px-[20px] pt-[18px] pb-[20px]'\n : `pb-[20px] ${headerDivider ? 'pt-[18px]' : ''}`\n }\n >\n {children}\n </div>\n ) : (\n children\n )}\n </div>\n {footer ? (\n <div\n className=\"flex min-h-[56px] shrink-0 items-center border-0 border-t border-solid border-[var(--malachite-card-border)] px-[12px]\"\n style={GLASS_SURFACE_STYLE}\n >\n {footer}\n </div>\n ) : null}\n </div>\n )}\n {tray?.caption ? (\n <div\n id={captionId}\n className=\"flex h-[48px] shrink-0 items-center justify-center px-[16px] text-[13px] tracking-[0.13px]\"\n style={{ color: MODAL_TRAY_CAPTION_COLOR[tray.tone ?? 'neutral'] }}\n >\n <span>\n {tray.caption.label ? (\n <>\n <span className=\"font-medium\">\n {tray.caption.label}\n </span>{' '}\n </>\n ) : null}\n {tray.caption.message}\n </span>\n </div>\n ) : null}\n </div>\n </div>\n </ShadowRootWrapper>,\n document.body,\n );\n};\n\nexport default Modal;\nexport { Modal };\n"],"names":["SIZE_MAX_WIDTH","TRANSITION_MS","TALL_BODY","Modal","open","onClose","title","subtitle","icon","headerExtra","titleAdornment","trayProp","headerDivider","footer","size","surface","showCloseButton","closeOnOverlayClick","bodyPadding","bodyHeight","callouts","renderLink","role","ariaLabel","ariaLabelledBy","ariaDescribedBy","busy","children","captionId","useId","calloutId","isBare","tray","shownCallouts","_a","callout","isVisible","setIsVisible","useState","isMounted","setIsMounted","isExiting","setIsExiting","panelRef","useRef","frameRef","unmountTimerRef","busyRef","onCloseRef","useEffect","dismissByPointer","useCallback","hostRef","takePanelRef","isTopmost","zIndex","elevated","useOverlayBehaviour","handlePanelRef","node","active","getDeepActiveElement","panelClassName","createPortal","jsx","ShadowRootWrapper","overlayIdentity","OVERLAY_ELEVATED_ATTR","jsxs","_","index","MODAL_TRAY_BACKGROUND","Fragment","OVERLAY_ICON_BUTTON_CLASSES","DismissIcon","CalloutStrip","GLASS_SURFACE_STYLE","MODAL_TRAY_CAPTION_COLOR"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -185,6 +185,13 @@ export declare const AdvancedLogsIcon: React.FC<IconProps_4>;
|
|
|
185
185
|
|
|
186
186
|
export declare const Alert: default_2.FC<AlertProps>;
|
|
187
187
|
|
|
188
|
+
declare interface AlertAddress {
|
|
189
|
+
/** The full address, shown whole in mono; it wraps rather than truncates. */
|
|
190
|
+
text: string;
|
|
191
|
+
/** The part to set in bold, such as the registrable domain; every occurrence is bolded. */
|
|
192
|
+
emphasis?: string;
|
|
193
|
+
}
|
|
194
|
+
|
|
188
195
|
export declare type AlertLayout = 'inline' | 'banner';
|
|
189
196
|
|
|
190
197
|
declare interface AlertProps {
|
|
@@ -212,6 +219,10 @@ declare interface AlertProps {
|
|
|
212
219
|
align?: 'start' | 'center';
|
|
213
220
|
/** A bold sentence the message runs on from. Plain text, not markdown. */
|
|
214
221
|
lead?: string;
|
|
222
|
+
/** An address the message runs on into, in mono; `emphasis`, an optional substring such as the registrable domain, is bold wherever it occurs. */
|
|
223
|
+
address?: AlertAddress;
|
|
224
|
+
/** Markdown that runs on after the address; it joins without a space when it opens with any punctuation. */
|
|
225
|
+
addressSuffix?: string;
|
|
215
226
|
}
|
|
216
227
|
|
|
217
228
|
export declare type AlertType = 'info' | 'success' | 'error' | 'warning' | 'assistant';
|
|
@@ -3607,6 +3618,12 @@ export declare interface ModalProps {
|
|
|
3607
3618
|
/** Pinned action row below the scrolling body. */
|
|
3608
3619
|
footer?: default_2.ReactNode;
|
|
3609
3620
|
size?: ModalSize;
|
|
3621
|
+
/**
|
|
3622
|
+
* `none` draws no card: no header, close button, footer, callouts or tray, only the
|
|
3623
|
+
* overlay and a scroll body around content that brings its own chrome, such as the Hub.
|
|
3624
|
+
* `title` then only names the dialog for assistive tech. Default: `card`.
|
|
3625
|
+
*/
|
|
3626
|
+
surface?: ModalSurface;
|
|
3610
3627
|
/** Default: true. */
|
|
3611
3628
|
showCloseButton?: boolean;
|
|
3612
3629
|
/** Default: true. Turn off for a dialog whose choice must be made deliberately. */
|
|
@@ -3645,6 +3662,8 @@ export declare interface ModalProps {
|
|
|
3645
3662
|
|
|
3646
3663
|
export declare type ModalSize = 'small' | 'medium' | 'large';
|
|
3647
3664
|
|
|
3665
|
+
export declare type ModalSurface = 'card' | 'none';
|
|
3666
|
+
|
|
3648
3667
|
export declare type ModalTrayTone = 'neutral' | 'warning' | 'error' | 'success';
|
|
3649
3668
|
|
|
3650
3669
|
export declare const MoreHorizontalIcon: React.FC<IconProps_77>;
|
package/package.json
CHANGED