@stackone/malachite 2.55.1 → 2.57.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 (31) hide show
  1. package/COMPONENTS.md +5 -4
  2. package/dist/components/Alert.js +77 -59
  3. package/dist/components/Alert.js.map +1 -1
  4. package/dist/components/Breadcrumbs.js.map +1 -1
  5. package/dist/components/CalloutStrip.js +7 -0
  6. package/dist/components/CalloutStrip.js.map +1 -0
  7. package/dist/components/ConfirmationDialog.js +100 -108
  8. package/dist/components/ConfirmationDialog.js.map +1 -1
  9. package/dist/components/IconBadge.js +16 -0
  10. package/dist/components/IconBadge.js.map +1 -0
  11. package/dist/components/PageLayout/index.js.map +1 -1
  12. package/dist/components/PageLayout/regularLayout.js.map +1 -1
  13. package/dist/components/SidePanel.js +99 -82
  14. package/dist/components/SidePanel.js.map +1 -1
  15. package/dist/components/Skeleton.js +45 -42
  16. package/dist/components/Skeleton.js.map +1 -1
  17. package/dist/components/Typography/MarkdownBlock.js +89 -87
  18. package/dist/components/Typography/MarkdownBlock.js.map +1 -1
  19. package/dist/components/Typography/Text.js +14 -13
  20. package/dist/components/Typography/Text.js.map +1 -1
  21. package/dist/components/Typography/index.js.map +1 -1
  22. package/dist/components/Typography/textStyles.js.map +1 -1
  23. package/dist/components/confirmationDialogVariants.js +2 -6
  24. package/dist/components/confirmationDialogVariants.js.map +1 -1
  25. package/dist/components/iconBadgeVariants.js +11 -0
  26. package/dist/components/iconBadgeVariants.js.map +1 -0
  27. package/dist/index.d.ts +39 -5
  28. package/dist/index.js +447 -445
  29. package/dist/index.js.map +1 -1
  30. package/dist/tailwind.css.js +1 -1
  31. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
- import { jsxs as d, jsx as r } from "react/jsx-runtime";
2
- import { useState as w, useId as g } from "react";
3
- import { Alert as H } from "./Alert.js";
4
- import { Button as N } from "./Button.js";
1
+ import { jsxs as d, jsx as o } from "react/jsx-runtime";
2
+ import { useState as S, useId as h } from "react";
3
+ import { Button as T } from "./Button.js";
4
+ import H from "./CalloutStrip.js";
5
5
  import Z from "./CustomIcons/CheckCircleFilled.js";
6
6
  import J from "./CustomIcons/CheckmarkIcon.js";
7
7
  import K from "./CustomIcons/CopyIcon.js";
@@ -9,187 +9,179 @@ import Q from "./CustomIcons/ErrorCircleFilled.js";
9
9
  import U from "./CustomIcons/InfoCircleFilled.js";
10
10
  import X from "./CustomIcons/WarningCircleFilled.js";
11
11
  import { FieldMessage as P } from "./FieldMessage.js";
12
- import { Modal as ee } from "./Modal.js";
13
- import { Typography as S } from "./Typography/index.js";
14
- import { useClipboardCopy as ie } from "./useClipboardCopy.js";
15
- import { CONFIRMATION_DIALOG_VARIANTS as re } from "./confirmationDialogVariants.js";
16
- import oe from "./Input/Input.js";
17
- const v = 12, C = (e) => e.replace(/\s+/g, " "), te = (e, i) => {
18
- const o = e.trim();
19
- if (!(!i || o === "" || o === i) && C(o).toLowerCase() === C(i).toLowerCase())
20
- return C(o) === C(i) ? "The spacing has to match exactly." : "This is case-sensitive.";
21
- }, ae = "Could not copy. Enter the phrase manually.", le = (e, i) => {
22
- const o = typeof e == "object" && e !== null ? e : void 0;
23
- return o ? { parts: o, text: [o.prefix, i, o.suffix].filter(Boolean).join(" ") } : { text: typeof e == "string" ? e : `Type ${i} to confirm` };
24
- }, se = (e, i) => e === "copied" ? `${i} copied to clipboard` : "", ne = (e, i, o, s) => {
25
- const { outcome: p, canCopy: m, copy: n } = ie(`${i}|${e}`), l = async () => {
26
- e && await n(e) && queueMicrotask(() => s == null ? void 0 : s(e));
12
+ import { IconBadge as ee } from "./IconBadge.js";
13
+ import { Modal as ie } from "./Modal.js";
14
+ import { Typography as j } from "./Typography/index.js";
15
+ import { useClipboardCopy as oe } from "./useClipboardCopy.js";
16
+ import { CONFIRMATION_DIALOG_VARIANTS as te } from "./confirmationDialogVariants.js";
17
+ import le from "./Input/Input.js";
18
+ const C = 12, v = (e) => e.replace(/\s+/g, " "), re = (e, i) => {
19
+ const t = e.trim();
20
+ if (!(!i || t === "" || t === i) && v(t).toLowerCase() === v(i).toLowerCase())
21
+ return v(t) === v(i) ? "The spacing has to match exactly." : "This is case-sensitive.";
22
+ }, se = "Could not copy. Enter the phrase manually.", ae = (e, i) => {
23
+ const t = typeof e == "object" && e !== null ? e : void 0;
24
+ return t ? { parts: t, text: [t.prefix, i, t.suffix].filter(Boolean).join(" ") } : { text: typeof e == "string" ? e : `Type ${i} to confirm` };
25
+ }, ne = (e, i) => e === "copied" ? `${i} copied to clipboard` : "", ce = (e, i, t, a) => {
26
+ const { outcome: p, canCopy: m, copy: n } = oe(`${i}|${e}`), r = async () => {
27
+ e && await n(e) && queueMicrotask(() => a == null ? void 0 : a(e));
27
28
  };
28
- return { outcome: p, canCopy: m && !o, copy: () => void l() };
29
- }, j = ({ children: e }) => /* @__PURE__ */ r(S.Text, { size: "xsmall", color: "primary", fontWeight: "semi-bold", children: e }), ce = ({
29
+ return { outcome: p, canCopy: m && !t, copy: () => void r() };
30
+ }, k = ({ children: e }) => /* @__PURE__ */ o(j.Text, { size: "xsmall", color: "primary", fontWeight: "semi-bold", children: e }), de = ({
30
31
  phrase: e,
31
32
  parts: i,
32
- plainLabel: o,
33
- placeholder: s,
33
+ plainLabel: t,
34
+ placeholder: a,
34
35
  value: p,
35
36
  onChange: m,
36
37
  disabled: n,
37
- copyLabel: l,
38
+ copyLabel: r,
38
39
  copyOutcome: c,
39
- canCopy: t,
40
- onCopy: I,
40
+ canCopy: l,
41
+ onCopy: g,
41
42
  nearMissMessage: f
42
43
  }) => {
43
- const u = g(), y = g(), b = g(), x = [f, c === "failed" ? ae : void 0].filter(Boolean).join(" ") || void 0, a = i ? [u, i.suffix ? y : void 0].filter(Boolean).join(" ") : void 0;
44
+ const u = h(), y = h(), x = h(), b = [f, c === "failed" ? se : void 0].filter(Boolean).join(" ") || void 0, s = i ? [u, i.suffix ? y : void 0].filter(Boolean).join(" ") : void 0;
44
45
  return /* @__PURE__ */ d("div", { className: "flex flex-col gap-[6px]", children: [
45
46
  i ? /* @__PURE__ */ d("div", { className: "flex flex-wrap items-center gap-[6px] px-2", children: [
46
47
  /* @__PURE__ */ d("span", { id: u, children: [
47
- /* @__PURE__ */ r(j, { children: i.prefix }),
48
+ /* @__PURE__ */ o(k, { children: i.prefix }),
48
49
  " ",
49
- /* @__PURE__ */ r(S.CodeText, { variant: "inline", size: "small", children: e })
50
+ /* @__PURE__ */ o(j.CodeText, { variant: "inline", size: "small", children: e })
50
51
  ] }),
51
- /* @__PURE__ */ r(
52
- N,
52
+ /* @__PURE__ */ o(
53
+ T,
53
54
  {
54
55
  variant: "ghost",
55
56
  size: "small",
56
57
  iconOnly: !0,
57
- "aria-label": `${l} ${e}`,
58
- icon: c === "copied" ? /* @__PURE__ */ r(J, { size: 13 }) : /* @__PURE__ */ r(K, { size: 13 }),
59
- onClick: I,
60
- disabled: !t
58
+ "aria-label": `${r} ${e}`,
59
+ icon: c === "copied" ? /* @__PURE__ */ o(J, { size: 13 }) : /* @__PURE__ */ o(K, { size: 13 }),
60
+ onClick: g,
61
+ disabled: !l
61
62
  }
62
63
  ),
63
- i.suffix ? /* @__PURE__ */ r("span", { id: y, children: /* @__PURE__ */ r(j, { children: i.suffix }) }) : null
64
+ i.suffix ? /* @__PURE__ */ o("span", { id: y, children: /* @__PURE__ */ o(k, { children: i.suffix }) }) : null
64
65
  ] }) : null,
65
- /* @__PURE__ */ r(
66
- oe,
66
+ /* @__PURE__ */ o(
67
+ le,
67
68
  {
68
69
  name: "confirmation-phrase",
69
70
  size: "large",
70
71
  value: p,
71
72
  onChange: m,
72
- label: i ? void 0 : o,
73
- "aria-labelledby": a,
74
- placeholder: s,
73
+ label: i ? void 0 : t,
74
+ "aria-labelledby": s,
75
+ placeholder: a,
75
76
  requiredIndicator: "none",
76
77
  error: !!f,
77
- "aria-describedby": x ? b : void 0,
78
+ "aria-describedby": b ? x : void 0,
78
79
  disabled: n
79
80
  }
80
81
  ),
81
- /* @__PURE__ */ r(P, { id: b, message: x }),
82
- i ? /* @__PURE__ */ r("span", { role: "status", className: "sr-only", children: se(c, e) }) : null
82
+ /* @__PURE__ */ o(P, { id: x, message: b }),
83
+ i ? /* @__PURE__ */ o("span", { role: "status", className: "sr-only", children: ne(c, e) }) : null
83
84
  ] });
84
- }, de = {
85
- default: /* @__PURE__ */ r(U, { size: v }),
86
- warning: /* @__PURE__ */ r(X, { size: v }),
87
- destructive: /* @__PURE__ */ r(Q, { size: v }),
88
- success: /* @__PURE__ */ r(Z, { size: v })
89
- }, ke = ({
85
+ }, pe = {
86
+ default: /* @__PURE__ */ o(U, { size: C }),
87
+ warning: /* @__PURE__ */ o(X, { size: C }),
88
+ destructive: /* @__PURE__ */ o(Q, { size: C }),
89
+ success: /* @__PURE__ */ o(Z, { size: C })
90
+ }, we = ({
90
91
  open: e,
91
92
  onClose: i,
92
- onConfirm: o,
93
- title: s,
93
+ onConfirm: t,
94
+ title: a,
94
95
  titleAdornment: p,
95
96
  badgeIcon: m,
96
97
  description: n,
97
- variant: l = "default",
98
+ variant: r = "default",
98
99
  callout: c,
99
- confirmPhrase: t,
100
- confirmPhraseLabel: I,
100
+ confirmPhrase: l,
101
+ confirmPhraseLabel: g,
101
102
  confirmPhraseCopyLabel: f = "Copy",
102
103
  onPhraseCopied: u,
103
104
  confirmPhrasePlaceholder: y,
104
- confirmLabel: b = "Confirm",
105
- cancelLabel: x = "Cancel",
106
- loading: a = !1,
107
- tip: E,
108
- size: M = "medium",
105
+ confirmLabel: x = "Confirm",
106
+ cancelLabel: b = "Cancel",
107
+ loading: s = !1,
108
+ tip: B,
109
+ size: E = "medium",
109
110
  "aria-label": O,
110
111
  "aria-labelledby": _,
111
- children: B
112
+ children: D
112
113
  }) => {
113
- const [z, A] = w(""), T = g(), h = re[l], [k, D] = w({ open: e, confirmPhrase: t });
114
- (k.open !== e || k.confirmPhrase !== t) && (D({ open: e, confirmPhrase: t }), e && A(""));
115
- const { parts: $, text: F } = le(
116
- I,
117
- t
114
+ const [I, A] = S(""), F = h(), z = te[r], [N, M] = S({ open: e, confirmPhrase: l });
115
+ (N.open !== e || N.confirmPhrase !== l) && (M({ open: e, confirmPhrase: l }), e && A(""));
116
+ const { parts: $, text: L } = ae(
117
+ g,
118
+ l
118
119
  ), {
119
120
  outcome: G,
120
121
  canCopy: R,
121
122
  copy: V
122
- } = ne(t, e, a, u), L = !t || z.trim() === t, q = te(z, t), W = a || !L, Y = /* @__PURE__ */ d("div", { className: "flex w-full flex-wrap items-center justify-end gap-[8px]", children: [
123
- /* @__PURE__ */ r(N, { variant: "outline", size: "small", onClick: i, disabled: a, children: x }),
124
- /* @__PURE__ */ r(
125
- N,
123
+ } = ce(l, e, s, u), w = !l || I.trim() === l, q = re(I, l), W = s || !w, Y = /* @__PURE__ */ d("div", { className: "flex w-full flex-wrap items-center justify-end gap-[8px]", children: [
124
+ /* @__PURE__ */ o(T, { variant: "outline", size: "small", onClick: i, disabled: s, children: b }),
125
+ /* @__PURE__ */ o(
126
+ T,
126
127
  {
127
- variant: h.confirmVariant,
128
+ variant: z.confirmVariant,
128
129
  size: "small",
129
- onClick: o,
130
+ onClick: t,
130
131
  disabled: W,
131
- loading: a,
132
- tooltip: !a && !L ? F : void 0,
133
- children: b
132
+ loading: s,
133
+ tooltip: !s && !w ? L : void 0,
134
+ children: x
134
135
  }
135
136
  )
136
137
  ] });
137
138
  return /* @__PURE__ */ d(
138
- ee,
139
+ ie,
139
140
  {
140
141
  open: e,
141
142
  onClose: i,
142
- title: s,
143
- size: M,
143
+ title: a,
144
+ size: E,
144
145
  footer: Y,
145
- busy: a,
146
- role: l === "destructive" ? "alertdialog" : "dialog",
146
+ busy: s,
147
+ role: r === "destructive" ? "alertdialog" : "dialog",
147
148
  "aria-label": O,
148
149
  "aria-labelledby": _,
149
- "aria-describedby": n ? T : void 0,
150
- closeOnOverlayClick: l !== "destructive" && !t,
150
+ "aria-describedby": n ? F : void 0,
151
+ closeOnOverlayClick: r !== "destructive" && !l,
151
152
  headerDivider: !0,
152
153
  titleAdornment: p,
153
154
  bodyPadding: !1,
154
- tray: { tone: h.tray, caption: E },
155
- icon: /* @__PURE__ */ r(
156
- "span",
155
+ tray: { tone: z.tray, caption: B },
156
+ icon: /* @__PURE__ */ o(
157
+ ee,
157
158
  {
158
- "aria-hidden": "true",
159
- className: "confirmation-badge flex size-[18px] shrink-0 items-center justify-center rounded-[7px] text-[var(--malachite-card-background)]",
160
- style: { backgroundColor: h.badge },
161
- children: m ?? de[l]
159
+ variant: r,
160
+ icon: m ?? pe[r]
162
161
  }
163
162
  ),
164
163
  children: [
165
- c ? /* @__PURE__ */ r("div", { className: "border-0 border-b border-solid border-[var(--malachite-card-subtle-divider,var(--malachite-card-border))] p-[10px]", children: /* @__PURE__ */ r(
166
- H,
167
- {
168
- type: c.type ?? h.calloutType,
169
- message: c.message,
170
- hasMargin: !1
171
- }
172
- ) }) : null,
164
+ c ? /* @__PURE__ */ o(H, { type: c.type ?? z.calloutType, message: c.message }) : null,
173
165
  /* @__PURE__ */ d("div", { className: "flex flex-col gap-[12px] p-[28px]", children: [
174
- n ? /* @__PURE__ */ r(
166
+ n ? /* @__PURE__ */ o(
175
167
  "div",
176
168
  {
177
- id: T,
169
+ id: F,
178
170
  className: "text-[13px] leading-[1.6] text-[var(--malachite-text-secondary)]",
179
171
  children: n
180
172
  }
181
173
  ) : null,
182
- B,
183
- t ? /* @__PURE__ */ r(
184
- ce,
174
+ D,
175
+ l ? /* @__PURE__ */ o(
176
+ de,
185
177
  {
186
- phrase: t,
178
+ phrase: l,
187
179
  parts: $,
188
- plainLabel: F,
180
+ plainLabel: L,
189
181
  placeholder: y,
190
- value: z,
182
+ value: I,
191
183
  onChange: A,
192
- disabled: a,
184
+ disabled: s,
193
185
  copyLabel: f,
194
186
  copyOutcome: G,
195
187
  canCopy: R,
@@ -203,7 +195,7 @@ const v = 12, C = (e) => e.replace(/\s+/g, " "), te = (e, i) => {
203
195
  );
204
196
  };
205
197
  export {
206
- ke as ConfirmationDialog,
207
- ke as default
198
+ we as ConfirmationDialog,
199
+ we as default
208
200
  };
209
201
  //# sourceMappingURL=ConfirmationDialog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmationDialog.js","sources":["../../src/components/ConfirmationDialog.tsx"],"sourcesContent":["import type React from 'react';\nimport { useId, useState } from 'react';\nimport Alert, { type AlertType } from './Alert';\nimport Button from './Button';\nimport CheckCircleFilled from './CustomIcons/CheckCircleFilled';\nimport CheckmarkIcon from './CustomIcons/CheckmarkIcon';\nimport CopyIcon from './CustomIcons/CopyIcon';\nimport ErrorCircleFilled from './CustomIcons/ErrorCircleFilled';\nimport InfoCircleFilled from './CustomIcons/InfoCircleFilled';\nimport WarningCircleFilled from './CustomIcons/WarningCircleFilled';\nimport FieldMessage from './FieldMessage';\nimport { Input } from './Input';\nimport Modal, { type ModalSize } from './Modal';\nimport Typography from './Typography';\nimport { type CopyOutcome, useClipboardCopy } from './useClipboardCopy';\nimport {\n CONFIRMATION_DIALOG_VARIANTS,\n type ConfirmationDialogVariant,\n} from './confirmationDialogVariants';\n\n/** Kept beside the component: an icon is a node, not a value the variant table can hold. */\nconst BADGE_GLYPH_SIZE = 12;\n\nconst collapseSpaces = (value: string) => value.replace(/\\s+/g, ' ');\n\nconst nearMissAgainst = (typedPhrase: string, confirmPhrase?: string): string | undefined => {\n const typed = typedPhrase.trim();\n if (!confirmPhrase || typed === '' || typed === confirmPhrase) {\n return undefined;\n }\n if (collapseSpaces(typed).toLowerCase() !== collapseSpaces(confirmPhrase).toLowerCase()) {\n return undefined;\n }\n return collapseSpaces(typed) === collapseSpaces(confirmPhrase)\n ? 'The spacing has to match exactly.'\n : 'This is case-sensitive.';\n};\n\ntype PhraseLabel = string | { prefix: string; suffix?: string };\n\nconst COPY_FAILED_MESSAGE = 'Could not copy. Enter the phrase manually.';\n\nconst resolvePhraseLabel = (\n label: PhraseLabel | undefined,\n phrase?: string,\n): { parts?: { prefix: string; suffix?: string }; text: string } => {\n const parts = typeof label === 'object' && label !== null ? label : undefined;\n if (parts) {\n return { parts, text: [parts.prefix, phrase, parts.suffix].filter(Boolean).join(' ') };\n }\n return { text: typeof label === 'string' ? label : `Type ${phrase} to confirm` };\n};\n\nconst copyStatusFor = (outcome: CopyOutcome, phrase: string): string =>\n outcome === 'copied' ? `${phrase} copied to clipboard` : '';\n\nconst usePhraseCopy = (\n phrase: string | undefined,\n open: boolean,\n disabled: boolean,\n onCopied?: (phrase: string) => void,\n): { outcome: CopyOutcome; canCopy: boolean; copy: () => void } => {\n const { outcome, canCopy, copy } = useClipboardCopy(`${open}|${phrase}`);\n\n const run = async () => {\n if (phrase && (await copy(phrase))) {\n queueMicrotask(() => onCopied?.(phrase));\n }\n };\n\n return { outcome, canCopy: canCopy && !disabled, copy: () => void run() };\n};\n\nconst LabelText: React.FC<{ children: React.ReactNode }> = ({ children }) => (\n <Typography.Text size=\"xsmall\" color=\"primary\" fontWeight=\"semi-bold\">\n {children}\n </Typography.Text>\n);\n\ninterface PhraseFieldProps {\n phrase: string;\n parts?: { prefix: string; suffix?: string };\n plainLabel: string;\n placeholder?: string;\n value: string;\n onChange: (value: string) => void;\n disabled: boolean;\n copyLabel: string;\n copyOutcome: CopyOutcome;\n canCopy: boolean;\n onCopy: () => void;\n nearMissMessage?: string;\n}\n\nconst PhraseField: React.FC<PhraseFieldProps> = ({\n phrase,\n parts,\n plainLabel,\n placeholder,\n value,\n onChange,\n disabled,\n copyLabel,\n copyOutcome,\n canCopy,\n onCopy,\n nearMissMessage,\n}) => {\n const beforeId = useId();\n const afterId = useId();\n const messageId = useId();\n const message =\n [nearMissMessage, copyOutcome === 'failed' ? COPY_FAILED_MESSAGE : undefined]\n .filter(Boolean)\n .join(' ') || undefined;\n const labelledBy = parts\n ? [beforeId, parts.suffix ? afterId : undefined].filter(Boolean).join(' ')\n : undefined;\n\n return (\n <div className=\"flex flex-col gap-[6px]\">\n {parts ? (\n <div className=\"flex flex-wrap items-center gap-[6px] px-2\">\n <span id={beforeId}>\n <LabelText>{parts.prefix}</LabelText>{' '}\n <Typography.CodeText variant=\"inline\" size=\"small\">\n {phrase}\n </Typography.CodeText>\n </span>\n <Button\n variant=\"ghost\"\n size=\"small\"\n iconOnly\n aria-label={`${copyLabel} ${phrase}`}\n icon={\n copyOutcome === 'copied' ? (\n <CheckmarkIcon size={13} />\n ) : (\n <CopyIcon size={13} />\n )\n }\n onClick={onCopy}\n disabled={!canCopy}\n />\n {parts.suffix ? (\n <span id={afterId}>\n <LabelText>{parts.suffix}</LabelText>\n </span>\n ) : null}\n </div>\n ) : null}\n <Input\n name=\"confirmation-phrase\"\n size=\"large\"\n value={value}\n onChange={onChange}\n label={parts ? undefined : plainLabel}\n aria-labelledby={labelledBy}\n placeholder={placeholder}\n requiredIndicator=\"none\"\n error={!!nearMissMessage}\n aria-describedby={message ? messageId : undefined}\n disabled={disabled}\n />\n <FieldMessage id={messageId} message={message} />\n {parts ? (\n <span role=\"status\" className=\"sr-only\">\n {copyStatusFor(copyOutcome, phrase)}\n </span>\n ) : null}\n </div>\n );\n};\n\nconst VARIANT_ICON: Record<ConfirmationDialogVariant, React.ReactNode> = {\n default: <InfoCircleFilled size={BADGE_GLYPH_SIZE} />,\n warning: <WarningCircleFilled size={BADGE_GLYPH_SIZE} />,\n destructive: <ErrorCircleFilled size={BADGE_GLYPH_SIZE} />,\n success: <CheckCircleFilled size={BADGE_GLYPH_SIZE} />,\n};\n\nexport interface ConfirmationDialogProps {\n open: boolean;\n /** Dismiss handler — cancel, close button, ESC or scrim. `loading` suppresses every\n * route except ESC, which always reaches this so a keyboard user is never stranded. */\n onClose: () => void;\n /** Runs when the confirm action is taken. Closing is left to the consumer. */\n onConfirm: () => void;\n title: React.ReactNode;\n titleAdornment?: React.ReactNode;\n badgeIcon?: React.ReactNode;\n /** The sentence saying what is about to happen. */\n description?: React.ReactNode;\n variant?: ConfirmationDialogVariant;\n /** Coloured callout below the body. Defaults to the variant's own type. */\n callout?: { type?: AlertType; message: string };\n /**\n * Requires this exact phrase to be typed before the confirm action enables — for a\n * consequence severe enough that a single click should not be able to cause it.\n */\n confirmPhrase?: string;\n /** Label above the type-to-confirm field. `{ prefix, suffix }` makes the phrase copyable. */\n confirmPhraseLabel?: PhraseLabel;\n /** Names the copy control, e.g. `Copy profile name`. Default: `Copy`. */\n confirmPhraseCopyLabel?: string;\n /** Runs after the phrase reaches the clipboard, with what was copied. */\n onPhraseCopied?: (phrase: string) => void;\n confirmPhrasePlaceholder?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n /**\n * Disables the actions, spins the confirm button and suppresses the pointer dismissal\n * routes — but never ESC, which always reaches `onClose`.\n */\n loading?: boolean;\n tip?: { label?: string; message: string };\n size?: ModalSize;\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 /** Extra body content between the description and the callout, e.g. an entity chip. */\n children?: React.ReactNode;\n}\n\nconst ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({\n open,\n onClose,\n onConfirm,\n title,\n titleAdornment,\n badgeIcon,\n description,\n variant = 'default',\n callout,\n confirmPhrase,\n confirmPhraseLabel,\n confirmPhraseCopyLabel = 'Copy',\n onPhraseCopied,\n confirmPhrasePlaceholder,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n loading = false,\n tip,\n size = 'medium',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n children,\n}) => {\n const [typedPhrase, setTypedPhrase] = useState('');\n const descriptionId = useId();\n const base = CONFIRMATION_DIALOG_VARIANTS[variant];\n\n // On open, not close: the dialog outlives `open` by its exit transition, so resetting on\n // close empties the field in front of the user. Adjusted during render rather than from an\n // effect, or a reopen paints the previous answer once with confirm already enabled.\n const [answeredFor, setAnsweredFor] = useState({ open, confirmPhrase });\n if (answeredFor.open !== open || answeredFor.confirmPhrase !== confirmPhrase) {\n setAnsweredFor({ open, confirmPhrase });\n if (open) {\n setTypedPhrase('');\n }\n }\n\n const { parts: phraseParts, text: phraseLabel } = resolvePhraseLabel(\n confirmPhraseLabel,\n confirmPhrase,\n );\n const {\n outcome: copyOutcome,\n canCopy: canCopyPhrase,\n copy: handleCopyPhrase,\n } = usePhraseCopy(confirmPhrase, open, loading, onPhraseCopied);\n // Trimmed because copying an entity name commonly picks up a trailing space; the gate is\n // still exact, and deliberately case-sensitive.\n const phraseSatisfied = !confirmPhrase || typedPhrase.trim() === confirmPhrase;\n const nearMissMessage = nearMissAgainst(typedPhrase, confirmPhrase);\n const confirmDisabled = loading || !phraseSatisfied;\n\n const footer = (\n <div className=\"flex w-full flex-wrap items-center justify-end gap-[8px]\">\n <Button variant=\"outline\" size=\"small\" onClick={onClose} disabled={loading}>\n {cancelLabel}\n </Button>\n <Button\n variant={base.confirmVariant}\n size=\"small\"\n onClick={onConfirm}\n disabled={confirmDisabled}\n loading={loading}\n // A disabled control emits no hover, so the tooltip explains the gate from the\n // wrapper Button renders for exactly this case.\n tooltip={!loading && !phraseSatisfied ? phraseLabel : undefined}\n >\n {confirmLabel}\n </Button>\n </div>\n );\n\n return (\n <Modal\n open={open}\n onClose={onClose}\n title={title}\n size={size}\n footer={footer}\n busy={loading}\n // A consequential choice is an alertdialog: it asks assistive tech to announce the\n // body, which is where the consequence is written.\n role={variant === 'destructive' ? 'alertdialog' : 'dialog'}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={description ? descriptionId : undefined}\n // A choice this deliberate should not be undone by a stray click, and a scrim\n // click must not discard a phrase the customer has already typed.\n closeOnOverlayClick={variant !== 'destructive' && !confirmPhrase}\n headerDivider\n titleAdornment={titleAdornment}\n bodyPadding={false}\n tray={{ tone: base.tray, caption: tip }}\n icon={\n <span\n aria-hidden=\"true\"\n className=\"confirmation-badge flex size-[18px] shrink-0 items-center justify-center rounded-[7px] text-[var(--malachite-card-background)]\"\n style={{ backgroundColor: base.badge }}\n >\n {badgeIcon ?? VARIANT_ICON[variant]}\n </span>\n }\n >\n {callout ? (\n <div className=\"border-0 border-b border-solid border-[var(--malachite-card-subtle-divider,var(--malachite-card-border))] p-[10px]\">\n <Alert\n type={callout.type ?? base.calloutType}\n message={callout.message}\n hasMargin={false}\n />\n </div>\n ) : null}\n <div className=\"flex flex-col gap-[12px] p-[28px]\">\n {description ? (\n <div\n id={descriptionId}\n className=\"text-[13px] leading-[1.6] text-[var(--malachite-text-secondary)]\"\n >\n {description}\n </div>\n ) : null}\n {children}\n {confirmPhrase ? (\n <PhraseField\n phrase={confirmPhrase}\n parts={phraseParts}\n plainLabel={phraseLabel}\n placeholder={confirmPhrasePlaceholder}\n value={typedPhrase}\n onChange={setTypedPhrase}\n disabled={loading}\n copyLabel={confirmPhraseCopyLabel}\n copyOutcome={copyOutcome}\n canCopy={canCopyPhrase}\n onCopy={handleCopyPhrase}\n nearMissMessage={nearMissMessage}\n />\n ) : null}\n </div>\n </Modal>\n );\n};\n\nexport default ConfirmationDialog;\nexport { ConfirmationDialog };\nexport type { ConfirmationDialogVariant };\n"],"names":["BADGE_GLYPH_SIZE","collapseSpaces","value","nearMissAgainst","typedPhrase","confirmPhrase","typed","COPY_FAILED_MESSAGE","resolvePhraseLabel","label","phrase","parts","copyStatusFor","outcome","usePhraseCopy","open","disabled","onCopied","canCopy","copy","useClipboardCopy","run","LabelText","children","jsx","Typography","PhraseField","plainLabel","placeholder","onChange","copyLabel","copyOutcome","onCopy","nearMissMessage","beforeId","useId","afterId","messageId","message","labelledBy","jsxs","Button","CheckmarkIcon","CopyIcon","Input","FieldMessage","VARIANT_ICON","InfoCircleFilled","WarningCircleFilled","ErrorCircleFilled","CheckCircleFilled","ConfirmationDialog","onClose","onConfirm","title","titleAdornment","badgeIcon","description","variant","callout","confirmPhraseLabel","confirmPhraseCopyLabel","onPhraseCopied","confirmPhrasePlaceholder","confirmLabel","cancelLabel","loading","tip","size","ariaLabel","ariaLabelledBy","setTypedPhrase","useState","descriptionId","base","CONFIRMATION_DIALOG_VARIANTS","answeredFor","setAnsweredFor","phraseParts","phraseLabel","canCopyPhrase","handleCopyPhrase","phraseSatisfied","confirmDisabled","footer","Modal","Alert"],"mappings":"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"}
1
+ {"version":3,"file":"ConfirmationDialog.js","sources":["../../src/components/ConfirmationDialog.tsx"],"sourcesContent":["import type React from 'react';\nimport { useId, useState } from 'react';\nimport type { AlertType } from './Alert';\nimport Button from './Button';\nimport CalloutStrip from './CalloutStrip';\nimport CheckCircleFilled from './CustomIcons/CheckCircleFilled';\nimport CheckmarkIcon from './CustomIcons/CheckmarkIcon';\nimport CopyIcon from './CustomIcons/CopyIcon';\nimport ErrorCircleFilled from './CustomIcons/ErrorCircleFilled';\nimport InfoCircleFilled from './CustomIcons/InfoCircleFilled';\nimport WarningCircleFilled from './CustomIcons/WarningCircleFilled';\nimport FieldMessage from './FieldMessage';\nimport IconBadge from './IconBadge';\nimport { Input } from './Input';\nimport Modal, { type ModalSize } from './Modal';\nimport Typography from './Typography';\nimport { type CopyOutcome, useClipboardCopy } from './useClipboardCopy';\nimport {\n CONFIRMATION_DIALOG_VARIANTS,\n type ConfirmationDialogVariant,\n} from './confirmationDialogVariants';\n\n/** Kept beside the component: an icon is a node, not a value the variant table can hold. */\nconst BADGE_GLYPH_SIZE = 12;\n\nconst collapseSpaces = (value: string) => value.replace(/\\s+/g, ' ');\n\nconst nearMissAgainst = (typedPhrase: string, confirmPhrase?: string): string | undefined => {\n const typed = typedPhrase.trim();\n if (!confirmPhrase || typed === '' || typed === confirmPhrase) {\n return undefined;\n }\n if (collapseSpaces(typed).toLowerCase() !== collapseSpaces(confirmPhrase).toLowerCase()) {\n return undefined;\n }\n return collapseSpaces(typed) === collapseSpaces(confirmPhrase)\n ? 'The spacing has to match exactly.'\n : 'This is case-sensitive.';\n};\n\ntype PhraseLabel = string | { prefix: string; suffix?: string };\n\nconst COPY_FAILED_MESSAGE = 'Could not copy. Enter the phrase manually.';\n\nconst resolvePhraseLabel = (\n label: PhraseLabel | undefined,\n phrase?: string,\n): { parts?: { prefix: string; suffix?: string }; text: string } => {\n const parts = typeof label === 'object' && label !== null ? label : undefined;\n if (parts) {\n return { parts, text: [parts.prefix, phrase, parts.suffix].filter(Boolean).join(' ') };\n }\n return { text: typeof label === 'string' ? label : `Type ${phrase} to confirm` };\n};\n\nconst copyStatusFor = (outcome: CopyOutcome, phrase: string): string =>\n outcome === 'copied' ? `${phrase} copied to clipboard` : '';\n\nconst usePhraseCopy = (\n phrase: string | undefined,\n open: boolean,\n disabled: boolean,\n onCopied?: (phrase: string) => void,\n): { outcome: CopyOutcome; canCopy: boolean; copy: () => void } => {\n const { outcome, canCopy, copy } = useClipboardCopy(`${open}|${phrase}`);\n\n const run = async () => {\n if (phrase && (await copy(phrase))) {\n queueMicrotask(() => onCopied?.(phrase));\n }\n };\n\n return { outcome, canCopy: canCopy && !disabled, copy: () => void run() };\n};\n\nconst LabelText: React.FC<{ children: React.ReactNode }> = ({ children }) => (\n <Typography.Text size=\"xsmall\" color=\"primary\" fontWeight=\"semi-bold\">\n {children}\n </Typography.Text>\n);\n\ninterface PhraseFieldProps {\n phrase: string;\n parts?: { prefix: string; suffix?: string };\n plainLabel: string;\n placeholder?: string;\n value: string;\n onChange: (value: string) => void;\n disabled: boolean;\n copyLabel: string;\n copyOutcome: CopyOutcome;\n canCopy: boolean;\n onCopy: () => void;\n nearMissMessage?: string;\n}\n\nconst PhraseField: React.FC<PhraseFieldProps> = ({\n phrase,\n parts,\n plainLabel,\n placeholder,\n value,\n onChange,\n disabled,\n copyLabel,\n copyOutcome,\n canCopy,\n onCopy,\n nearMissMessage,\n}) => {\n const beforeId = useId();\n const afterId = useId();\n const messageId = useId();\n const message =\n [nearMissMessage, copyOutcome === 'failed' ? COPY_FAILED_MESSAGE : undefined]\n .filter(Boolean)\n .join(' ') || undefined;\n const labelledBy = parts\n ? [beforeId, parts.suffix ? afterId : undefined].filter(Boolean).join(' ')\n : undefined;\n\n return (\n <div className=\"flex flex-col gap-[6px]\">\n {parts ? (\n <div className=\"flex flex-wrap items-center gap-[6px] px-2\">\n <span id={beforeId}>\n <LabelText>{parts.prefix}</LabelText>{' '}\n <Typography.CodeText variant=\"inline\" size=\"small\">\n {phrase}\n </Typography.CodeText>\n </span>\n <Button\n variant=\"ghost\"\n size=\"small\"\n iconOnly\n aria-label={`${copyLabel} ${phrase}`}\n icon={\n copyOutcome === 'copied' ? (\n <CheckmarkIcon size={13} />\n ) : (\n <CopyIcon size={13} />\n )\n }\n onClick={onCopy}\n disabled={!canCopy}\n />\n {parts.suffix ? (\n <span id={afterId}>\n <LabelText>{parts.suffix}</LabelText>\n </span>\n ) : null}\n </div>\n ) : null}\n <Input\n name=\"confirmation-phrase\"\n size=\"large\"\n value={value}\n onChange={onChange}\n label={parts ? undefined : plainLabel}\n aria-labelledby={labelledBy}\n placeholder={placeholder}\n requiredIndicator=\"none\"\n error={!!nearMissMessage}\n aria-describedby={message ? messageId : undefined}\n disabled={disabled}\n />\n <FieldMessage id={messageId} message={message} />\n {parts ? (\n <span role=\"status\" className=\"sr-only\">\n {copyStatusFor(copyOutcome, phrase)}\n </span>\n ) : null}\n </div>\n );\n};\n\nconst VARIANT_ICON: Record<ConfirmationDialogVariant, React.ReactNode> = {\n default: <InfoCircleFilled size={BADGE_GLYPH_SIZE} />,\n warning: <WarningCircleFilled size={BADGE_GLYPH_SIZE} />,\n destructive: <ErrorCircleFilled size={BADGE_GLYPH_SIZE} />,\n success: <CheckCircleFilled size={BADGE_GLYPH_SIZE} />,\n};\n\nexport interface ConfirmationDialogProps {\n open: boolean;\n /** Dismiss handler — cancel, close button, ESC or scrim. `loading` suppresses every\n * route except ESC, which always reaches this so a keyboard user is never stranded. */\n onClose: () => void;\n /** Runs when the confirm action is taken. Closing is left to the consumer. */\n onConfirm: () => void;\n title: React.ReactNode;\n titleAdornment?: React.ReactNode;\n badgeIcon?: React.ReactNode;\n /** The sentence saying what is about to happen. */\n description?: React.ReactNode;\n variant?: ConfirmationDialogVariant;\n /** Coloured callout below the body. Defaults to the variant's own type. */\n callout?: { type?: AlertType; message: string };\n /**\n * Requires this exact phrase to be typed before the confirm action enables — for a\n * consequence severe enough that a single click should not be able to cause it.\n */\n confirmPhrase?: string;\n /** Label above the type-to-confirm field. `{ prefix, suffix }` makes the phrase copyable. */\n confirmPhraseLabel?: PhraseLabel;\n /** Names the copy control, e.g. `Copy profile name`. Default: `Copy`. */\n confirmPhraseCopyLabel?: string;\n /** Runs after the phrase reaches the clipboard, with what was copied. */\n onPhraseCopied?: (phrase: string) => void;\n confirmPhrasePlaceholder?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n /**\n * Disables the actions, spins the confirm button and suppresses the pointer dismissal\n * routes — but never ESC, which always reaches `onClose`.\n */\n loading?: boolean;\n tip?: { label?: string; message: string };\n size?: ModalSize;\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 /** Extra body content between the description and the callout, e.g. an entity chip. */\n children?: React.ReactNode;\n}\n\nconst ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({\n open,\n onClose,\n onConfirm,\n title,\n titleAdornment,\n badgeIcon,\n description,\n variant = 'default',\n callout,\n confirmPhrase,\n confirmPhraseLabel,\n confirmPhraseCopyLabel = 'Copy',\n onPhraseCopied,\n confirmPhrasePlaceholder,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n loading = false,\n tip,\n size = 'medium',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n children,\n}) => {\n const [typedPhrase, setTypedPhrase] = useState('');\n const descriptionId = useId();\n const base = CONFIRMATION_DIALOG_VARIANTS[variant];\n\n // On open, not close: the dialog outlives `open` by its exit transition, so resetting on\n // close empties the field in front of the user. Adjusted during render rather than from an\n // effect, or a reopen paints the previous answer once with confirm already enabled.\n const [answeredFor, setAnsweredFor] = useState({ open, confirmPhrase });\n if (answeredFor.open !== open || answeredFor.confirmPhrase !== confirmPhrase) {\n setAnsweredFor({ open, confirmPhrase });\n if (open) {\n setTypedPhrase('');\n }\n }\n\n const { parts: phraseParts, text: phraseLabel } = resolvePhraseLabel(\n confirmPhraseLabel,\n confirmPhrase,\n );\n const {\n outcome: copyOutcome,\n canCopy: canCopyPhrase,\n copy: handleCopyPhrase,\n } = usePhraseCopy(confirmPhrase, open, loading, onPhraseCopied);\n // Trimmed because copying an entity name commonly picks up a trailing space; the gate is\n // still exact, and deliberately case-sensitive.\n const phraseSatisfied = !confirmPhrase || typedPhrase.trim() === confirmPhrase;\n const nearMissMessage = nearMissAgainst(typedPhrase, confirmPhrase);\n const confirmDisabled = loading || !phraseSatisfied;\n\n const footer = (\n <div className=\"flex w-full flex-wrap items-center justify-end gap-[8px]\">\n <Button variant=\"outline\" size=\"small\" onClick={onClose} disabled={loading}>\n {cancelLabel}\n </Button>\n <Button\n variant={base.confirmVariant}\n size=\"small\"\n onClick={onConfirm}\n disabled={confirmDisabled}\n loading={loading}\n // A disabled control emits no hover, so the tooltip explains the gate from the\n // wrapper Button renders for exactly this case.\n tooltip={!loading && !phraseSatisfied ? phraseLabel : undefined}\n >\n {confirmLabel}\n </Button>\n </div>\n );\n\n return (\n <Modal\n open={open}\n onClose={onClose}\n title={title}\n size={size}\n footer={footer}\n busy={loading}\n // A consequential choice is an alertdialog: it asks assistive tech to announce the\n // body, which is where the consequence is written.\n role={variant === 'destructive' ? 'alertdialog' : 'dialog'}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={description ? descriptionId : undefined}\n // A choice this deliberate should not be undone by a stray click, and a scrim\n // click must not discard a phrase the customer has already typed.\n closeOnOverlayClick={variant !== 'destructive' && !confirmPhrase}\n headerDivider\n titleAdornment={titleAdornment}\n bodyPadding={false}\n tray={{ tone: base.tray, caption: tip }}\n icon={\n <IconBadge\n variant={variant}\n icon={badgeIcon ?? VARIANT_ICON[variant]}\n />\n }\n >\n {callout ? (\n <CalloutStrip type={callout.type ?? base.calloutType} message={callout.message} />\n ) : null}\n <div className=\"flex flex-col gap-[12px] p-[28px]\">\n {description ? (\n <div\n id={descriptionId}\n className=\"text-[13px] leading-[1.6] text-[var(--malachite-text-secondary)]\"\n >\n {description}\n </div>\n ) : null}\n {children}\n {confirmPhrase ? (\n <PhraseField\n phrase={confirmPhrase}\n parts={phraseParts}\n plainLabel={phraseLabel}\n placeholder={confirmPhrasePlaceholder}\n value={typedPhrase}\n onChange={setTypedPhrase}\n disabled={loading}\n copyLabel={confirmPhraseCopyLabel}\n copyOutcome={copyOutcome}\n canCopy={canCopyPhrase}\n onCopy={handleCopyPhrase}\n nearMissMessage={nearMissMessage}\n />\n ) : null}\n </div>\n </Modal>\n );\n};\n\nexport default ConfirmationDialog;\nexport { ConfirmationDialog };\nexport type { ConfirmationDialogVariant };\n"],"names":["BADGE_GLYPH_SIZE","collapseSpaces","value","nearMissAgainst","typedPhrase","confirmPhrase","typed","COPY_FAILED_MESSAGE","resolvePhraseLabel","label","phrase","parts","copyStatusFor","outcome","usePhraseCopy","open","disabled","onCopied","canCopy","copy","useClipboardCopy","run","LabelText","children","jsx","Typography","PhraseField","plainLabel","placeholder","onChange","copyLabel","copyOutcome","onCopy","nearMissMessage","beforeId","useId","afterId","messageId","message","labelledBy","jsxs","Button","CheckmarkIcon","CopyIcon","Input","FieldMessage","VARIANT_ICON","InfoCircleFilled","WarningCircleFilled","ErrorCircleFilled","CheckCircleFilled","ConfirmationDialog","onClose","onConfirm","title","titleAdornment","badgeIcon","description","variant","callout","confirmPhraseLabel","confirmPhraseCopyLabel","onPhraseCopied","confirmPhrasePlaceholder","confirmLabel","cancelLabel","loading","tip","size","ariaLabel","ariaLabelledBy","setTypedPhrase","useState","descriptionId","base","CONFIRMATION_DIALOG_VARIANTS","answeredFor","setAnsweredFor","phraseParts","phraseLabel","canCopyPhrase","handleCopyPhrase","phraseSatisfied","confirmDisabled","footer","Modal","IconBadge","CalloutStrip"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { jsx as o } from "react/jsx-runtime";
2
+ import { ICON_BADGE_FILL as t } from "./iconBadgeVariants.js";
3
+ const n = "icon-badge flex size-[18px] shrink-0 items-center justify-center rounded-[7px] text-[var(--malachite-card-background)]", d = ({ icon: e, variant: r = "default", className: a }) => /* @__PURE__ */ o(
4
+ "span",
5
+ {
6
+ "aria-hidden": "true",
7
+ className: [n, a].filter(Boolean).join(" "),
8
+ style: { backgroundColor: t[r] },
9
+ children: e
10
+ }
11
+ );
12
+ export {
13
+ d as IconBadge,
14
+ d as default
15
+ };
16
+ //# sourceMappingURL=IconBadge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IconBadge.js","sources":["../../src/components/IconBadge.tsx"],"sourcesContent":["import type React from 'react';\nimport { ICON_BADGE_FILL, type IconBadgeVariant } from './iconBadgeVariants';\n\nexport interface IconBadgeProps {\n /** Drawn at 12px in the card background colour; an svg painting with `currentColor`. */\n icon: React.ReactNode;\n /** `accent` is the brand's interaction colour, so a rebrand repaints it. */\n variant?: IconBadgeVariant;\n className?: string;\n}\n\n/** A small filled tile carrying a glyph, for a dialog or panel header. Decorative: the title\n * beside it says what the glyph shows, so it is always hidden from assistive tech. */\n// A string literal of its own: Tailwind's scanner misses a class glued to a `${…}` interpolation.\nconst BADGE_CLASSES =\n 'icon-badge flex size-[18px] shrink-0 items-center justify-center rounded-[7px] text-[var(--malachite-card-background)]';\n\nconst IconBadge: React.FC<IconBadgeProps> = ({ icon, variant = 'default', className }) => (\n <span\n aria-hidden=\"true\"\n className={[BADGE_CLASSES, className].filter(Boolean).join(' ')}\n style={{ backgroundColor: ICON_BADGE_FILL[variant] }}\n >\n {icon}\n </span>\n);\n\nexport default IconBadge;\nexport { IconBadge };\nexport type { IconBadgeVariant };\n"],"names":["BADGE_CLASSES","IconBadge","icon","variant","className","jsx","ICON_BADGE_FILL"],"mappings":";;AAcA,MAAMA,IACF,0HAEEC,IAAsC,CAAC,EAAE,MAAAC,GAAM,SAAAC,IAAU,WAAW,WAAAC,QACtE,gBAAAC;AAAA,EAAC;AAAA,EAAA;AAAA,IACG,eAAY;AAAA,IACZ,WAAW,CAACL,GAAeI,CAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,IAC9D,OAAO,EAAE,iBAAiBE,EAAgBH,CAAO,EAAA;AAAA,IAEhD,UAAAD;AAAA,EAAA;AACL;"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/components/PageLayout/index.tsx"],"sourcesContent":["import Empty from './emptyLayout';\nimport Regular from './regularLayout';\n\nexport const PageLayout = { Regular, Empty };\n\nexport default PageLayout;\nexport type { BreadcrumbLink, PageAction, RenderBreadcrumbLink } from './types';\nexport { PageActions } from './regularLayout';\nexport type { PageContentGap, RegularLayoutProps } from './regularLayout';\nexport type { EmptyLayoutProps } from './emptyLayout';\n"],"names":["PageLayout","Regular","Empty"],"mappings":";;;AAGO,MAAMA,IAAa,EAAA,SAAEC,UAASC,EAAA;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/PageLayout/index.tsx"],"sourcesContent":["import Empty from './emptyLayout';\nimport Regular from './regularLayout';\n\nexport const PageLayout = { Regular, Empty };\n\nexport default PageLayout;\nexport type { BreadcrumbLink, PageAction, RenderLink } from './types';\nexport { PageActions } from './regularLayout';\nexport type { PageContentGap, RegularLayoutProps } from './regularLayout';\nexport type { EmptyLayoutProps } from './emptyLayout';\n"],"names":["PageLayout","Regular","Empty"],"mappings":";;;AAGO,MAAMA,IAAa,EAAA,SAAEC,UAASC,EAAA;"}
@@ -1 +1 @@
1
- {"version":3,"file":"regularLayout.js","sources":["../../../src/components/PageLayout/regularLayout.tsx"],"sourcesContent":["import React from 'react';\nimport ActionMenu, { type ActionMenuItem } from '../ActionMenu';\nimport Alert, { type AlertType } from '../Alert';\nimport Breadcrumbs from '../Breadcrumbs';\nimport { hasBackButton } from '../breadcrumbsNav';\nimport Button from '../Button';\nimport OverflowRow from '../OverflowRow';\nimport Skeleton from '../Skeleton';\nimport PageTitle from '../Typography/PageTitle';\nimport Text from '../Typography/Text';\nimport { safeHref } from '../safeHref';\nimport {\n CARD_CLASSES,\n CARD_MIN_HEIGHT,\n CARD_RADIUS,\n CONTENT_INSET_VAR,\n CONTENT_MIN_HEIGHT,\n DIVIDER_CLASSES,\n HEADER_HEIGHT,\n} from './constants';\nimport type { BreadcrumbLink, PageAction, RenderBreadcrumbLink } from './types';\n\nexport type PageContentGap = 'small' | 'medium' | 'large' | 'xlarge';\n\nexport interface RegularLayoutProps {\n breadcrumbLinks?: BreadcrumbLink[];\n breadcrumbActions?: PageAction[];\n renderLink?: RenderBreadcrumbLink;\n title?: React.ReactNode;\n titleSuffix?: React.ReactNode;\n subtitle?: string;\n description?: React.ReactNode;\n buttons?: PageAction[];\n /**\n * Hides the buttons and skeletons the title. `subtitle` and `description` are only\n * skeletoned when those props are also passed.\n */\n loading?: boolean;\n children: React.ReactNode;\n centered?: boolean;\n noPadding?: boolean;\n contentGap?: PageContentGap;\n alert?: { message: string; type?: AlertType };\n /** Demotes the default `main` landmark — pass e.g. `region` when the app owns its own. */\n role?: React.AriaRole;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst CONTENT_GAPS: Record<PageContentGap, number> = {\n small: 8,\n medium: 16,\n large: 24,\n xlarge: 40,\n};\n\nconst isDanger = (button: PageAction): boolean =>\n button.variant === 'destructive' || button.variant === 'error';\n\nconst toMenuItems = (buttons: PageAction[]): ActionMenuItem[] =>\n buttons.flatMap((button, index) => {\n const href = safeHref(button.href);\n const item: ActionMenuItem = {\n key: `${index}-${button.label}`,\n label: button.label,\n ...(href ? { href, target: button.target ?? '_blank' } : { onClick: button.onClick }),\n disabled: button.disabled,\n icon: button.icon,\n tooltip: button.tooltip,\n danger: isDanger(button),\n };\n return button.dividerBefore && index > 0\n ? [{ type: 'divider' as const, key: `divider-${index}-${button.label}` }, item]\n : [item];\n });\n\n// Not `ActionBar`: it measures by walking ancestors for `clientWidth`, and this chrome\n// mounts on every page. OverflowRow batches its measurement instead.\n// A divider must live in the same OverflowRow child as its button — a standalone child\n// would survive its hidden button and break the child-index-to-button mapping.\n/**\n * The page's action buttons, collapsing into an overflow menu as the row narrows. Exported so\n * a page that renders its own header block — e.g. one that scrolls the title away — keeps the\n * same behaviour instead of hand-rolling `buttons.map(...)` and losing the collapse, the\n * dividers and the href handling.\n */\nexport const PageActions: React.FC<{ buttons: PageAction[] }> = ({ buttons }) => {\n if (buttons.length === 0) {\n return null;\n }\n\n // min-w-0 gives OverflowRow a shrinkable container to collapse against.\n return (\n <div className=\"min-w-0\">\n <OverflowRow\n gap={8}\n justify=\"end\"\n renderOverflow={(hiddenCount) => (\n <ActionMenu\n items={toMenuItems(\n buttons.slice(Math.max(0, buttons.length - hiddenCount)),\n )}\n />\n )}\n >\n {buttons.map((button, index) => {\n const href = safeHref(button.href);\n return (\n <div key={`${index}-${button.label}`} className=\"flex items-center gap-2\">\n {button.dividerBefore && index > 0 ? (\n <span\n aria-hidden=\"true\"\n className=\"h-4 w-px bg-[var(--malachite-card-border)]\"\n />\n ) : null}\n <Button\n variant={button.variant ?? 'filled'}\n size=\"medium\"\n disabled={button.disabled}\n icon={button.icon}\n tooltip={button.tooltip}\n tooltipPlacement={button.tooltipPlacement}\n href={href}\n target={href ? (button.target ?? '_blank') : undefined}\n onClick={href ? undefined : button.onClick}\n >\n {button.label}\n </Button>\n </div>\n );\n })}\n </OverflowRow>\n </div>\n );\n};\n\nconst NO_LINKS: BreadcrumbLink[] = [];\nconst NO_BUTTONS: PageAction[] = [];\n\nconst RegularLayout: React.FC<RegularLayoutProps> = ({\n breadcrumbLinks = NO_LINKS,\n breadcrumbActions,\n renderLink,\n title,\n titleSuffix,\n subtitle,\n description,\n buttons = NO_BUTTONS,\n loading = false,\n children,\n centered = false,\n noPadding = false,\n contentGap = 'small',\n alert,\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n style,\n}) => {\n const hasBack = hasBackButton(breadcrumbLinks);\n const contentPadding = noPadding ? 0 : centered ? '5vh 14vw' : '40px';\n // Published so a full-bleed child (CardStrip's bottom rule) can cancel it and re-apply\n // it as its own padding, instead of hardcoding the gutter at the call site.\n const contentInset = noPadding ? '0px' : centered ? '14vw' : '40px';\n const hasHeaderBlock = Boolean(\n loading || title || subtitle || description || buttons.length > 0,\n );\n\n const subtitleNode = subtitle ? (\n loading ? (\n <Skeleton.Input width={120} height={24} />\n ) : (\n <Text color=\"secondary\" size=\"small\">\n {subtitle}\n </Text>\n )\n ) : null;\n\n const titleNode = loading ? (\n <Skeleton.Input width={200} height={40} />\n ) : title ? (\n <div className=\"flex items-center\" style={{ gap: 12 }}>\n <PageTitle>{title}</PageTitle>\n {titleSuffix}\n </div>\n ) : null;\n\n const descriptionNode = description ? (\n loading ? (\n <Skeleton.Input width={120} height={24} />\n ) : (\n <Text color=\"secondary\" style={{ lineHeight: 1.6 }}>\n {description}\n </Text>\n )\n ) : null;\n\n return (\n <main\n className={`${CARD_CLASSES} ${className}`}\n style={{ borderRadius: CARD_RADIUS, minHeight: CARD_MIN_HEIGHT, ...style }}\n role={role}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-busy={loading || undefined}\n >\n <div\n className={`flex items-center ${DIVIDER_CLASSES}`}\n style={{\n height: HEADER_HEIGHT,\n paddingInlineStart: hasBack ? 16 : 28,\n paddingInlineEnd: 16,\n }}\n >\n <Breadcrumbs\n links={breadcrumbLinks}\n actions={breadcrumbActions}\n renderLink={renderLink}\n className=\"flex-1 min-w-0\"\n />\n </div>\n\n {alert ? (\n <div className={DIVIDER_CLASSES} style={{ padding: '10px 16px' }}>\n <Alert message={alert.message} type={alert.type} hasMargin={false} />\n </div>\n ) : null}\n\n <div\n className=\"flex flex-col\"\n style={\n {\n padding: contentPadding,\n minHeight: CONTENT_MIN_HEIGHT,\n gap: CONTENT_GAPS[contentGap],\n [CONTENT_INSET_VAR]: contentInset,\n } as React.CSSProperties\n }\n >\n {hasHeaderBlock ? (\n <div className=\"flex flex-col\" style={{ gap: 12 }}>\n {titleNode || subtitleNode || buttons.length > 0 ? (\n <div\n className=\"flex flex-wrap items-center justify-between\"\n style={{ gap: 12 }}\n >\n <div\n className=\"flex flex-col flex-auto min-w-0 overflow-hidden\"\n style={{ gap: 4 }}\n >\n {subtitleNode}\n {titleNode}\n </div>\n {loading ? null : <PageActions buttons={buttons} />}\n </div>\n ) : null}\n {descriptionNode}\n </div>\n ) : null}\n {children}\n </div>\n </main>\n );\n};\n\nexport default RegularLayout;\nexport { RegularLayout };\n"],"names":["CONTENT_GAPS","isDanger","button","toMenuItems","buttons","index","href","safeHref","item","PageActions","jsx","OverflowRow","hiddenCount","ActionMenu","jsxs","Button","NO_LINKS","NO_BUTTONS","RegularLayout","breadcrumbLinks","breadcrumbActions","renderLink","title","titleSuffix","subtitle","description","loading","children","centered","noPadding","contentGap","alert","role","ariaLabel","ariaLabelledBy","className","style","hasBack","hasBackButton","contentPadding","contentInset","hasHeaderBlock","subtitleNode","Skeleton","Text","titleNode","PageTitle","descriptionNode","CARD_CLASSES","CARD_RADIUS","CARD_MIN_HEIGHT","DIVIDER_CLASSES","HEADER_HEIGHT","Breadcrumbs","Alert","CONTENT_MIN_HEIGHT","CONTENT_INSET_VAR"],"mappings":"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"}
1
+ {"version":3,"file":"regularLayout.js","sources":["../../../src/components/PageLayout/regularLayout.tsx"],"sourcesContent":["import React from 'react';\nimport ActionMenu, { type ActionMenuItem } from '../ActionMenu';\nimport Alert, { type AlertType } from '../Alert';\nimport Breadcrumbs from '../Breadcrumbs';\nimport { hasBackButton } from '../breadcrumbsNav';\nimport Button from '../Button';\nimport OverflowRow from '../OverflowRow';\nimport Skeleton from '../Skeleton';\nimport PageTitle from '../Typography/PageTitle';\nimport Text from '../Typography/Text';\nimport { safeHref } from '../safeHref';\nimport {\n CARD_CLASSES,\n CARD_MIN_HEIGHT,\n CARD_RADIUS,\n CONTENT_INSET_VAR,\n CONTENT_MIN_HEIGHT,\n DIVIDER_CLASSES,\n HEADER_HEIGHT,\n} from './constants';\nimport type { BreadcrumbLink, PageAction, RenderLink } from './types';\n\nexport type PageContentGap = 'small' | 'medium' | 'large' | 'xlarge';\n\nexport interface RegularLayoutProps {\n breadcrumbLinks?: BreadcrumbLink[];\n breadcrumbActions?: PageAction[];\n renderLink?: RenderLink;\n title?: React.ReactNode;\n titleSuffix?: React.ReactNode;\n subtitle?: string;\n description?: React.ReactNode;\n buttons?: PageAction[];\n /**\n * Hides the buttons and skeletons the title. `subtitle` and `description` are only\n * skeletoned when those props are also passed.\n */\n loading?: boolean;\n children: React.ReactNode;\n centered?: boolean;\n noPadding?: boolean;\n contentGap?: PageContentGap;\n alert?: { message: string; type?: AlertType };\n /** Demotes the default `main` landmark — pass e.g. `region` when the app owns its own. */\n role?: React.AriaRole;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\nconst CONTENT_GAPS: Record<PageContentGap, number> = {\n small: 8,\n medium: 16,\n large: 24,\n xlarge: 40,\n};\n\nconst isDanger = (button: PageAction): boolean =>\n button.variant === 'destructive' || button.variant === 'error';\n\nconst toMenuItems = (buttons: PageAction[]): ActionMenuItem[] =>\n buttons.flatMap((button, index) => {\n const href = safeHref(button.href);\n const item: ActionMenuItem = {\n key: `${index}-${button.label}`,\n label: button.label,\n ...(href ? { href, target: button.target ?? '_blank' } : { onClick: button.onClick }),\n disabled: button.disabled,\n icon: button.icon,\n tooltip: button.tooltip,\n danger: isDanger(button),\n };\n return button.dividerBefore && index > 0\n ? [{ type: 'divider' as const, key: `divider-${index}-${button.label}` }, item]\n : [item];\n });\n\n// Not `ActionBar`: it measures by walking ancestors for `clientWidth`, and this chrome\n// mounts on every page. OverflowRow batches its measurement instead.\n// A divider must live in the same OverflowRow child as its button — a standalone child\n// would survive its hidden button and break the child-index-to-button mapping.\n/**\n * The page's action buttons, collapsing into an overflow menu as the row narrows. Exported so\n * a page that renders its own header block — e.g. one that scrolls the title away — keeps the\n * same behaviour instead of hand-rolling `buttons.map(...)` and losing the collapse, the\n * dividers and the href handling.\n */\nexport const PageActions: React.FC<{ buttons: PageAction[] }> = ({ buttons }) => {\n if (buttons.length === 0) {\n return null;\n }\n\n // min-w-0 gives OverflowRow a shrinkable container to collapse against.\n return (\n <div className=\"min-w-0\">\n <OverflowRow\n gap={8}\n justify=\"end\"\n renderOverflow={(hiddenCount) => (\n <ActionMenu\n items={toMenuItems(\n buttons.slice(Math.max(0, buttons.length - hiddenCount)),\n )}\n />\n )}\n >\n {buttons.map((button, index) => {\n const href = safeHref(button.href);\n return (\n <div key={`${index}-${button.label}`} className=\"flex items-center gap-2\">\n {button.dividerBefore && index > 0 ? (\n <span\n aria-hidden=\"true\"\n className=\"h-4 w-px bg-[var(--malachite-card-border)]\"\n />\n ) : null}\n <Button\n variant={button.variant ?? 'filled'}\n size=\"medium\"\n disabled={button.disabled}\n icon={button.icon}\n tooltip={button.tooltip}\n tooltipPlacement={button.tooltipPlacement}\n href={href}\n target={href ? (button.target ?? '_blank') : undefined}\n onClick={href ? undefined : button.onClick}\n >\n {button.label}\n </Button>\n </div>\n );\n })}\n </OverflowRow>\n </div>\n );\n};\n\nconst NO_LINKS: BreadcrumbLink[] = [];\nconst NO_BUTTONS: PageAction[] = [];\n\nconst RegularLayout: React.FC<RegularLayoutProps> = ({\n breadcrumbLinks = NO_LINKS,\n breadcrumbActions,\n renderLink,\n title,\n titleSuffix,\n subtitle,\n description,\n buttons = NO_BUTTONS,\n loading = false,\n children,\n centered = false,\n noPadding = false,\n contentGap = 'small',\n alert,\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n style,\n}) => {\n const hasBack = hasBackButton(breadcrumbLinks);\n const contentPadding = noPadding ? 0 : centered ? '5vh 14vw' : '40px';\n // Published so a full-bleed child (CardStrip's bottom rule) can cancel it and re-apply\n // it as its own padding, instead of hardcoding the gutter at the call site.\n const contentInset = noPadding ? '0px' : centered ? '14vw' : '40px';\n const hasHeaderBlock = Boolean(\n loading || title || subtitle || description || buttons.length > 0,\n );\n\n const subtitleNode = subtitle ? (\n loading ? (\n <Skeleton.Input width={120} height={24} />\n ) : (\n <Text color=\"secondary\" size=\"small\">\n {subtitle}\n </Text>\n )\n ) : null;\n\n const titleNode = loading ? (\n <Skeleton.Input width={200} height={40} />\n ) : title ? (\n <div className=\"flex items-center\" style={{ gap: 12 }}>\n <PageTitle>{title}</PageTitle>\n {titleSuffix}\n </div>\n ) : null;\n\n const descriptionNode = description ? (\n loading ? (\n <Skeleton.Input width={120} height={24} />\n ) : (\n <Text color=\"secondary\" style={{ lineHeight: 1.6 }}>\n {description}\n </Text>\n )\n ) : null;\n\n return (\n <main\n className={`${CARD_CLASSES} ${className}`}\n style={{ borderRadius: CARD_RADIUS, minHeight: CARD_MIN_HEIGHT, ...style }}\n role={role}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-busy={loading || undefined}\n >\n <div\n className={`flex items-center ${DIVIDER_CLASSES}`}\n style={{\n height: HEADER_HEIGHT,\n paddingInlineStart: hasBack ? 16 : 28,\n paddingInlineEnd: 16,\n }}\n >\n <Breadcrumbs\n links={breadcrumbLinks}\n actions={breadcrumbActions}\n renderLink={renderLink}\n className=\"flex-1 min-w-0\"\n />\n </div>\n\n {alert ? (\n <div className={DIVIDER_CLASSES} style={{ padding: '10px 16px' }}>\n <Alert message={alert.message} type={alert.type} hasMargin={false} />\n </div>\n ) : null}\n\n <div\n className=\"flex flex-col\"\n style={\n {\n padding: contentPadding,\n minHeight: CONTENT_MIN_HEIGHT,\n gap: CONTENT_GAPS[contentGap],\n [CONTENT_INSET_VAR]: contentInset,\n } as React.CSSProperties\n }\n >\n {hasHeaderBlock ? (\n <div className=\"flex flex-col\" style={{ gap: 12 }}>\n {titleNode || subtitleNode || buttons.length > 0 ? (\n <div\n className=\"flex flex-wrap items-center justify-between\"\n style={{ gap: 12 }}\n >\n <div\n className=\"flex flex-col flex-auto min-w-0 overflow-hidden\"\n style={{ gap: 4 }}\n >\n {subtitleNode}\n {titleNode}\n </div>\n {loading ? null : <PageActions buttons={buttons} />}\n </div>\n ) : null}\n {descriptionNode}\n </div>\n ) : null}\n {children}\n </div>\n </main>\n );\n};\n\nexport default RegularLayout;\nexport { RegularLayout };\n"],"names":["CONTENT_GAPS","isDanger","button","toMenuItems","buttons","index","href","safeHref","item","PageActions","jsx","OverflowRow","hiddenCount","ActionMenu","jsxs","Button","NO_LINKS","NO_BUTTONS","RegularLayout","breadcrumbLinks","breadcrumbActions","renderLink","title","titleSuffix","subtitle","description","loading","children","centered","noPadding","contentGap","alert","role","ariaLabel","ariaLabelledBy","className","style","hasBack","hasBackButton","contentPadding","contentInset","hasHeaderBlock","subtitleNode","Skeleton","Text","titleNode","PageTitle","descriptionNode","CARD_CLASSES","CARD_RADIUS","CARD_MIN_HEIGHT","DIVIDER_CLASSES","HEADER_HEIGHT","Breadcrumbs","Alert","CONTENT_MIN_HEIGHT","CONTENT_INSET_VAR"],"mappings":"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"}