@stackone/malachite 2.32.0 → 2.33.1
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/dist/components/FieldMessage.js.map +1 -1
- package/dist/components/Input/Input.js +65 -58
- package/dist/components/Input/Input.js.map +1 -1
- package/dist/components/SelectionBar.js +1 -1
- package/dist/components/SelectionBar.js.map +1 -1
- package/dist/components/SidePanel.js +60 -36
- package/dist/components/SidePanel.js.map +1 -1
- package/dist/components/TextArea/TextArea.js +1 -1
- package/dist/components/TextArea/TextArea.js.map +1 -1
- package/dist/components/Tooltip.js +57 -56
- package/dist/components/Tooltip.js.map +1 -1
- package/dist/components/Typography/CodeText.js +45 -22
- package/dist/components/Typography/CodeText.js.map +1 -1
- package/dist/components/Typography/EllipsisText.js +14 -12
- package/dist/components/Typography/EllipsisText.js.map +1 -1
- package/dist/components/selectionBarChrome.js +13 -13
- package/dist/components/selectionBarChrome.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/tailwind.css.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FieldMessage.js","sources":["../../src/components/FieldMessage.tsx"],"sourcesContent":["import type React from 'react';\n\nconst MESSAGE_CLASSES =\n 'text-[length:var(--malachite-font-size-small,13px)] text-left pl-2 text-[var(--malachite-destructive-foreground,rgb(196,62,62))]';\n\ninterface FieldMessageProps {\n id?: string;\n message?: string;\n /** The controls that sit their message under a helper line need the gap
|
|
1
|
+
{"version":3,"file":"FieldMessage.js","sources":["../../src/components/FieldMessage.tsx"],"sourcesContent":["import type React from 'react';\n\nconst MESSAGE_CLASSES =\n 'text-[length:var(--malachite-font-size-small,13px)] text-left pl-2 text-[var(--malachite-destructive-foreground,rgb(196,62,62))]';\n\ninterface FieldMessageProps {\n id?: string;\n message?: string;\n /** The controls that sit their message under a helper line need the gap. */\n spaced?: boolean;\n}\n\n/**\n * A field's validation message. The live region is mounted whether or not there is anything\n * to say, because a region inserted with its text already inside it is not a mutation and\n * screen readers stay silent — which would lose the first rejection, the one that matters.\n * Out of flow while empty, so a flex parent's `gap` reserves no space beside it.\n */\nexport const FieldMessage: React.FC<FieldMessageProps> = ({ id, message, spaced = false }) => (\n <div\n id={id}\n // Polite rather than assertive: the message is also the field's description via\n // aria-describedby, and an interrupting region says it a second time.\n aria-live=\"polite\"\n className={message ? `${spaced ? 'mt-1 ' : ''}${MESSAGE_CLASSES}` : 'absolute'}\n >\n {message}\n </div>\n);\n\nexport default FieldMessage;\n"],"names":["MESSAGE_CLASSES","FieldMessage","id","message","spaced","jsx"],"mappings":";AAEA,MAAMA,IACF,oIAeSC,IAA4C,CAAC,EAAE,IAAAC,GAAI,SAAAC,GAAS,QAAAC,IAAS,SAC9E,gBAAAC;AAAA,EAAC;AAAA,EAAA;AAAA,IACG,IAAAH;AAAA,IAGA,aAAU;AAAA,IACV,WAAWC,IAAU,GAAGC,IAAS,UAAU,EAAE,GAAGJ,CAAe,KAAK;AAAA,IAEnE,UAAAG;AAAA,EAAA;AACL;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as
|
|
1
|
+
import { jsx as r, jsxs as n, Fragment as K } from "react/jsx-runtime";
|
|
2
2
|
import { useState as j, useEffect as xe, useRef as ge } from "react";
|
|
3
3
|
import be, { useFormFieldIds as we } from "../BaseFormComponent.js";
|
|
4
4
|
import { Button as Q } from "../Button.js";
|
|
@@ -26,8 +26,8 @@ const J = {
|
|
|
26
26
|
small: "rounded-[var(--malachite-radius-10,10px)]",
|
|
27
27
|
medium: "rounded-xl",
|
|
28
28
|
large: "rounded-[var(--malachite-radius-xxl,14px)]"
|
|
29
|
-
}, $e = (
|
|
30
|
-
switch (
|
|
29
|
+
}, $e = (a, e) => {
|
|
30
|
+
switch (a) {
|
|
31
31
|
case "ghost":
|
|
32
32
|
return {
|
|
33
33
|
wrapper: [
|
|
@@ -50,7 +50,7 @@ const J = {
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
}, Je = ({
|
|
53
|
-
label:
|
|
53
|
+
label: a,
|
|
54
54
|
hint: e,
|
|
55
55
|
tooltip: i,
|
|
56
56
|
placeholder: x,
|
|
@@ -67,34 +67,34 @@ const J = {
|
|
|
67
67
|
variant: F = "default",
|
|
68
68
|
defaultValue: $,
|
|
69
69
|
disabled: u,
|
|
70
|
-
prefixValue:
|
|
71
|
-
prefixSecondaryValue:
|
|
70
|
+
prefixValue: M,
|
|
71
|
+
prefixSecondaryValue: T,
|
|
72
72
|
ellipsis: O = !1,
|
|
73
|
-
formControl:
|
|
73
|
+
formControl: Z,
|
|
74
74
|
buttons: v,
|
|
75
|
-
onCopyClick:
|
|
75
|
+
onCopyClick: _,
|
|
76
76
|
showCopyButton: ee,
|
|
77
77
|
onChange: b,
|
|
78
|
-
value:
|
|
78
|
+
value: re,
|
|
79
79
|
onBlur: w,
|
|
80
80
|
helperText: f,
|
|
81
81
|
showPasswordToggle: I = !0,
|
|
82
|
-
id:
|
|
82
|
+
id: ae,
|
|
83
83
|
"aria-label": te,
|
|
84
84
|
"aria-labelledby": ie,
|
|
85
85
|
"aria-describedby": le
|
|
86
86
|
}) => {
|
|
87
|
-
const { controlId:
|
|
88
|
-
id:
|
|
89
|
-
label:
|
|
87
|
+
const { controlId: D, labelId: se, descriptionId: P, hintId: A, helperTextId: y } = we({
|
|
88
|
+
id: ae,
|
|
89
|
+
label: a,
|
|
90
90
|
description: R,
|
|
91
91
|
hint: e,
|
|
92
92
|
helperText: !0
|
|
93
|
-
}), [C, oe] = j(!1), [de, ce] = j($ || ""), [N,
|
|
93
|
+
}), [C, oe] = j(!1), [de, ce] = j($ || ""), [N, G] = j(!1);
|
|
94
94
|
xe(() => {
|
|
95
95
|
if (N) {
|
|
96
96
|
const t = setTimeout(() => {
|
|
97
|
-
|
|
97
|
+
G(!1);
|
|
98
98
|
}, 1e3);
|
|
99
99
|
return () => clearTimeout(t);
|
|
100
100
|
}
|
|
@@ -102,18 +102,18 @@ const J = {
|
|
|
102
102
|
const ne = (t) => (l) => {
|
|
103
103
|
const c = l.target.value;
|
|
104
104
|
ce(c), b == null || b(c), t == null || t(c);
|
|
105
|
-
},
|
|
105
|
+
}, H = ge(void 0), B = (t) => Se({
|
|
106
106
|
component: "Input",
|
|
107
107
|
name: s,
|
|
108
|
-
value:
|
|
108
|
+
value: re,
|
|
109
109
|
formValue: t,
|
|
110
110
|
defaultValue: $,
|
|
111
|
-
bound: !!
|
|
111
|
+
bound: !!Z,
|
|
112
112
|
uncontrolledValue: de
|
|
113
|
-
}), L =
|
|
114
|
-
const t = B(
|
|
115
|
-
t && (navigator.clipboard.writeText(t),
|
|
116
|
-
} : void 0, me = o === "password" ? C ? "text" : "password" : o, he = m ||
|
|
113
|
+
}), L = _ ? () => {
|
|
114
|
+
const t = B(H.current);
|
|
115
|
+
t && (navigator.clipboard.writeText(t), G(!0), _());
|
|
116
|
+
} : void 0, me = o === "password" ? C ? "text" : "password" : o, he = m || M || T, pe = J[d], ue = (t) => {
|
|
117
117
|
const l = $e(F, t);
|
|
118
118
|
return [
|
|
119
119
|
"relative",
|
|
@@ -127,46 +127,53 @@ const J = {
|
|
|
127
127
|
J[d]
|
|
128
128
|
].filter(Boolean).join(" ");
|
|
129
129
|
};
|
|
130
|
-
return /* @__PURE__ */
|
|
130
|
+
return /* @__PURE__ */ r(
|
|
131
131
|
be,
|
|
132
132
|
{
|
|
133
|
-
label:
|
|
133
|
+
label: a,
|
|
134
134
|
hint: e,
|
|
135
135
|
tooltip: i,
|
|
136
136
|
name: s,
|
|
137
137
|
required: h,
|
|
138
138
|
requiredIndicator: g,
|
|
139
139
|
loading: Y,
|
|
140
|
-
formControl:
|
|
140
|
+
formControl: Z,
|
|
141
141
|
description: R,
|
|
142
|
-
controlId:
|
|
142
|
+
controlId: D,
|
|
143
143
|
labelId: se,
|
|
144
|
-
descriptionId:
|
|
145
|
-
hintId:
|
|
146
|
-
loadingComponent: /* @__PURE__ */
|
|
144
|
+
descriptionId: P,
|
|
145
|
+
hintId: A,
|
|
146
|
+
loadingComponent: /* @__PURE__ */ r(
|
|
147
|
+
ze,
|
|
148
|
+
{
|
|
149
|
+
heightClass: pe,
|
|
150
|
+
helperText: !!f,
|
|
151
|
+
helperTextMarginTop: !0
|
|
152
|
+
}
|
|
153
|
+
),
|
|
147
154
|
children: ({ error: t, message: l, onFieldChange: c, onFieldBlur: k, value: U }) => {
|
|
148
|
-
|
|
155
|
+
H.current = U;
|
|
149
156
|
const E = B(U), W = ee ?? !!(L && u && E), z = p ?? t, S = p === !1 ? void 0 : l, X = z && !S, ve = X || W || (v == null ? void 0 : v.length) || o === "password" && I, q = `${y}-error`, fe = [
|
|
157
|
+
P,
|
|
150
158
|
A,
|
|
151
|
-
G,
|
|
152
159
|
f ? y : void 0,
|
|
153
160
|
S ? q : void 0,
|
|
154
161
|
le
|
|
155
162
|
].filter(Boolean).join(" ") || void 0;
|
|
156
163
|
return /* @__PURE__ */ n(K, { children: [
|
|
157
164
|
/* @__PURE__ */ n("div", { className: ue(z), children: [
|
|
158
|
-
he && /* @__PURE__ */
|
|
159
|
-
|
|
165
|
+
he && /* @__PURE__ */ r("div", { className: "flex items-center pl-3 pr-1 flex-shrink-0 pointer-events-none", children: /* @__PURE__ */ r(
|
|
166
|
+
Me,
|
|
160
167
|
{
|
|
161
|
-
prefixValue:
|
|
162
|
-
prefixSecondaryValue:
|
|
168
|
+
prefixValue: M,
|
|
169
|
+
prefixSecondaryValue: T,
|
|
163
170
|
icon: m
|
|
164
171
|
}
|
|
165
172
|
) }),
|
|
166
|
-
/* @__PURE__ */
|
|
173
|
+
/* @__PURE__ */ r(
|
|
167
174
|
"input",
|
|
168
175
|
{
|
|
169
|
-
id:
|
|
176
|
+
id: D,
|
|
170
177
|
type: me,
|
|
171
178
|
placeholder: x,
|
|
172
179
|
value: E,
|
|
@@ -191,8 +198,8 @@ const J = {
|
|
|
191
198
|
{
|
|
192
199
|
className: `flex items-center pl-1 flex-shrink-0 ${o === "password" && I ? "pr-0" : "pr-3"}`,
|
|
193
200
|
children: [
|
|
194
|
-
/* @__PURE__ */
|
|
195
|
-
|
|
201
|
+
/* @__PURE__ */ r(
|
|
202
|
+
Te,
|
|
196
203
|
{
|
|
197
204
|
error: X,
|
|
198
205
|
defaultValue: E,
|
|
@@ -205,30 +212,30 @@ const J = {
|
|
|
205
212
|
onTogglePassword: () => oe(!C)
|
|
206
213
|
}
|
|
207
214
|
),
|
|
208
|
-
/* @__PURE__ */
|
|
215
|
+
/* @__PURE__ */ r(Ze, { buttons: v })
|
|
209
216
|
]
|
|
210
217
|
}
|
|
211
218
|
)
|
|
212
219
|
] }),
|
|
213
|
-
f && /* @__PURE__ */
|
|
220
|
+
f && /* @__PURE__ */ r(
|
|
214
221
|
"div",
|
|
215
222
|
{
|
|
216
223
|
id: y,
|
|
217
|
-
className: "text-[length:var(--malachite-font-size-small,13px)] text-left pl-2 text-[var(--malachite-text-dimmed,rgb(161,161,161))]",
|
|
224
|
+
className: "mt-1 text-[length:var(--malachite-font-size-small,13px)] text-left pl-2 text-[var(--malachite-text-dimmed,rgb(161,161,161))]",
|
|
218
225
|
children: f
|
|
219
226
|
}
|
|
220
227
|
),
|
|
221
|
-
/* @__PURE__ */
|
|
228
|
+
/* @__PURE__ */ r(Ee, { id: q, message: S, spaced: !0 })
|
|
222
229
|
] });
|
|
223
230
|
}
|
|
224
231
|
}
|
|
225
232
|
);
|
|
226
|
-
},
|
|
227
|
-
i && /* @__PURE__ */
|
|
228
|
-
|
|
229
|
-
e && /* @__PURE__ */
|
|
230
|
-
] }),
|
|
231
|
-
error:
|
|
233
|
+
}, Me = ({ prefixValue: a, prefixSecondaryValue: e, icon: i }) => /* @__PURE__ */ n("div", { className: "flex items-center gap-1", children: [
|
|
234
|
+
i && /* @__PURE__ */ r("span", { className: "flex items-center justify-center text-[var(--malachite-text-dimmed)] w-4 h-4 flex-shrink-0", children: i }),
|
|
235
|
+
a && /* @__PURE__ */ r("span", { className: "text-[length:var(--malachite-font-size-medium,14px)] leading-[1.4] text-[var(--malachite-text-dimmed)] whitespace-nowrap", children: a }),
|
|
236
|
+
e && /* @__PURE__ */ r("span", { className: "text-[length:var(--malachite-font-size-medium,14px)] leading-[1.4] text-[var(--malachite-text-dimmed)] whitespace-nowrap", children: e })
|
|
237
|
+
] }), Te = ({
|
|
238
|
+
error: a,
|
|
232
239
|
defaultValue: e,
|
|
233
240
|
onCopyClick: i,
|
|
234
241
|
showCopyButton: x = !1,
|
|
@@ -238,14 +245,14 @@ const J = {
|
|
|
238
245
|
showPasswordToggle: g = !0,
|
|
239
246
|
onTogglePassword: p
|
|
240
247
|
}) => /* @__PURE__ */ n("div", { className: "flex items-center gap-1", children: [
|
|
241
|
-
|
|
248
|
+
a && // Decorative: the rejection is carried by the message beneath the field, so
|
|
242
249
|
// announcing an unnamed icon here would only repeat it as "info".
|
|
243
|
-
/* @__PURE__ */
|
|
250
|
+
/* @__PURE__ */ r(
|
|
244
251
|
"div",
|
|
245
252
|
{
|
|
246
253
|
"aria-hidden": "true",
|
|
247
254
|
className: "flex items-center justify-center w-5 h-5 flex-shrink-0",
|
|
248
|
-
children: /* @__PURE__ */
|
|
255
|
+
children: /* @__PURE__ */ r(
|
|
249
256
|
ke,
|
|
250
257
|
{
|
|
251
258
|
size: 16,
|
|
@@ -254,35 +261,35 @@ const J = {
|
|
|
254
261
|
)
|
|
255
262
|
}
|
|
256
263
|
),
|
|
257
|
-
x && i && e && /* @__PURE__ */
|
|
264
|
+
x && i && e && /* @__PURE__ */ r(
|
|
258
265
|
"button",
|
|
259
266
|
{
|
|
260
267
|
onClick: i,
|
|
261
268
|
type: "button",
|
|
262
269
|
className: "flex items-center justify-center w-5 h-5 hover:bg-[var(--malachite-primary-border)] rounded transition-colors flex-shrink-0 cursor-pointer",
|
|
263
|
-
children: m ? /* @__PURE__ */
|
|
270
|
+
children: m ? /* @__PURE__ */ r(
|
|
264
271
|
Ie,
|
|
265
272
|
{
|
|
266
273
|
size: 16,
|
|
267
274
|
style: { color: "var(--malachite-success-foreground)" }
|
|
268
275
|
}
|
|
269
|
-
) : /* @__PURE__ */
|
|
276
|
+
) : /* @__PURE__ */ r(ye, { size: 16 })
|
|
270
277
|
}
|
|
271
278
|
),
|
|
272
|
-
s === "password" && g && /* @__PURE__ */
|
|
279
|
+
s === "password" && g && /* @__PURE__ */ r(Q, { variant: "no-style", onClick: p, type: "button", size: "small", children: h ? /* @__PURE__ */ r(
|
|
273
280
|
Ce,
|
|
274
281
|
{
|
|
275
282
|
size: 16,
|
|
276
283
|
style: { color: "var(--malachite-text-dimmed)" }
|
|
277
284
|
}
|
|
278
|
-
) : /* @__PURE__ */
|
|
285
|
+
) : /* @__PURE__ */ r(
|
|
279
286
|
Ne,
|
|
280
287
|
{
|
|
281
288
|
size: 16,
|
|
282
289
|
style: { color: "var(--malachite-text-dimmed)" }
|
|
283
290
|
}
|
|
284
291
|
) })
|
|
285
|
-
] }),
|
|
292
|
+
] }), Ze = ({ buttons: a }) => a != null && a.length ? /* @__PURE__ */ r(K, { children: a.map((e) => /* @__PURE__ */ r(
|
|
286
293
|
Q,
|
|
287
294
|
{
|
|
288
295
|
variant: e.variant || (e.danger ? "destructive" : "outline"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Input.js","sources":["../../../src/components/Input/Input.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport type { Control, FieldPath, FieldValues } from 'react-hook-form';\nimport BaseFormComponent, { type RequiredIndicator, useFormFieldIds } from '../BaseFormComponent';\nimport { Button } from '../Button';\nimport CheckCircleFilled from '../CustomIcons/CheckCircleFilled';\nimport CopyIcon from '../CustomIcons/CopyIcon';\nimport EyeClosedIcon from '../CustomIcons/EyeClosedIcon';\nimport EyeIcon from '../CustomIcons/EyeIcon';\nimport InfoIcon from '../CustomIcons/InfoIcon';\nimport FieldMessage from '../FieldMessage';\nimport LoadingInput from '../LoadingInput';\nimport { paintedFieldValue } from '../devWarning';\nimport type { ButtonDefinition, InputSize, InputType, InputVariant } from './types';\n\ninterface InputProps {\n label?: string;\n hint?: string;\n tooltip?: string;\n placeholder?: string;\n icon?: React.ReactNode;\n readOnly?: boolean;\n name: FieldPath<FieldValues> | string;\n type?: InputType;\n description?: string;\n size?: InputSize;\n variant?: InputVariant;\n required?: boolean;\n requiredIndicator?: RequiredIndicator;\n error?: boolean;\n loading?: boolean;\n defaultValue?: string;\n disabled?: boolean;\n prefixValue?: string;\n prefixSecondaryValue?: string;\n ellipsis?: boolean;\n formControl?: Control<FieldValues, unknown>;\n buttons?: ButtonDefinition[];\n onCopyClick?: () => void;\n showCopyButton?: boolean;\n onChange?: (value: string) => void;\n /** Controlled value, for a source of truth outside the form (URL, app state). */\n value?: string;\n onBlur?: () => void;\n helperText?: string;\n showPasswordToggle?: boolean;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n}\n\n// Figma specs one Input, at 42px / px-16 / 15px / radius 14 — the large tier. The smaller\n// tiers have no Input master, so they follow Search, which shares the field chrome.\nconst SIZE_HEIGHT: Record<InputSize, string> = {\n small: 'h-[var(--malachite-control-height-small,28px)]',\n medium: 'h-[var(--malachite-control-height-medium,32px)]',\n large: 'h-[var(--malachite-control-height-large,42px)]',\n};\n\nconst SIZE_TEXT: Record<InputSize, string> = {\n small: 'text-[length:var(--malachite-font-size-small,13px)]',\n medium: 'text-[length:var(--malachite-font-size-small,13px)]',\n large: 'text-[length:var(--malachite-font-size-15,15px)] leading-[1.4]',\n};\n\nconst SIZE_PADDING: Record<InputSize, string> = {\n small: 'px-[10px]',\n medium: 'px-[10px]',\n large: 'px-[16px]',\n};\n\nconst SIZE_RADIUS: Record<InputSize, string> = {\n small: 'rounded-[var(--malachite-radius-10,10px)]',\n medium: 'rounded-xl',\n large: 'rounded-[var(--malachite-radius-xxl,14px)]',\n};\n\nconst getVariantClasses = (\n variant: InputVariant,\n error?: boolean,\n): {\n wrapper: string[];\n shadow: string;\n borderRadius: string;\n} => {\n switch (variant) {\n case 'ghost':\n return {\n wrapper: [\n error\n ? 'border-0 border-b border-solid border-[var(--malachite-destructive-border)]'\n : '',\n error\n ? 'focus-within:border-[var(--malachite-destructive-border-hover)] hover:border-[var(--malachite-destructive-border-hover)]'\n : '',\n ],\n shadow: 'shadow-none',\n borderRadius: 'rounded-none',\n };\n case 'default':\n default:\n return {\n wrapper: [\n 'border border-solid',\n error\n ? 'border-[var(--malachite-destructive-border)]'\n : 'border-[var(--malachite-card-border)]',\n error\n ? 'focus-within:border-[var(--malachite-destructive-border-hover)] hover:border-[var(--malachite-destructive-border-hover)]'\n : 'focus-within:border-[var(--malachite-control-border-hover)] hover:border-[var(--malachite-control-border-hover)]',\n ],\n shadow: error ? 'shadow-error' : 'shadow-default',\n borderRadius: '',\n };\n }\n};\n\nconst Input: React.FC<InputProps> = ({\n label,\n hint,\n tooltip,\n placeholder,\n icon,\n name,\n required,\n requiredIndicator,\n error,\n description,\n loading = false,\n readOnly = false,\n type = 'text',\n size = 'large',\n variant = 'default',\n defaultValue,\n disabled,\n prefixValue,\n prefixSecondaryValue,\n ellipsis = false,\n formControl,\n buttons,\n onCopyClick,\n showCopyButton,\n onChange,\n value,\n onBlur,\n helperText,\n showPasswordToggle = true,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n}) => {\n const { controlId, labelId, descriptionId, hintId, helperTextId } = useFormFieldIds({\n id,\n label,\n description,\n hint,\n helperText: true,\n });\n const [showPassword, setShowPassword] = useState(false);\n const [inputValue, setInputValue] = useState(defaultValue || '');\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n if (copied) {\n const timer = setTimeout(() => {\n setCopied(false);\n }, 1000);\n return () => clearTimeout(timer);\n }\n }, [copied]);\n\n const handleChange =\n (reportToForm?: (value: unknown) => void) => (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setInputValue(newValue);\n onChange?.(newValue);\n reportToForm?.(newValue);\n };\n\n const formValueRef = useRef<unknown>(undefined);\n const paintedValue = (formValue: unknown): string =>\n paintedFieldValue({\n component: 'Input',\n name,\n value,\n formValue,\n defaultValue,\n bound: !!formControl,\n uncontrolledValue: inputValue,\n });\n\n const handleCopyClick = onCopyClick\n ? () => {\n const copyValue = paintedValue(formValueRef.current);\n if (copyValue) {\n navigator.clipboard.writeText(copyValue);\n setCopied(true);\n onCopyClick();\n }\n }\n : undefined;\n\n const actualInputType = type === 'password' ? (showPassword ? 'text' : 'password') : type;\n\n const hasPrefix = icon || prefixValue || prefixSecondaryValue;\n\n // The skeleton stands in for the field, so it reads the same height the wrapper does.\n const heightClass = SIZE_HEIGHT[size];\n\n const buildWrapperClasses = (isError: boolean): string => {\n const variantStyles = getVariantClasses(variant, isError);\n\n return [\n 'relative',\n 'flex items-center',\n 'w-full',\n variantStyles.borderRadius || SIZE_RADIUS[size],\n disabled\n ? 'bg-[var(--malachite-card-dimmed-background)]'\n : 'bg-[var(--malachite-card-background)]',\n 'transition-colors duration-200 ease-in-out',\n variantStyles.shadow,\n ...(disabled ? [] : variantStyles.wrapper),\n SIZE_HEIGHT[size],\n ]\n .filter(Boolean)\n .join(' ');\n };\n\n return (\n <BaseFormComponent\n label={label}\n hint={hint}\n tooltip={tooltip}\n name={name}\n required={required}\n requiredIndicator={requiredIndicator}\n loading={loading}\n formControl={formControl}\n description={description}\n controlId={controlId}\n labelId={labelId}\n descriptionId={descriptionId}\n hintId={hintId}\n loadingComponent={<LoadingInput heightClass={heightClass} helperText={!!helperText} />}\n >\n {({ error: fieldError, message, onFieldChange, onFieldBlur, value: formValue }) => {\n formValueRef.current = formValue;\n const displayValue = paintedValue(formValue);\n const shouldShowCopyButton =\n showCopyButton ?? !!(handleCopyClick && disabled && displayValue);\n const isError = error ?? fieldError;\n // `error={false}` opts out of the resolver entirely, message included: a\n // screen that has dealt with the rejection its own way would otherwise keep\n // reporting it in destructive text and through `aria-describedby`.\n // The message is its own line rather than a replacement for the helper text:\n // a field that explains where to find its value still has to say why what was\n // typed was rejected, and dropping one for the other left the field marked red\n // with nothing anywhere saying what was wrong.\n const errorMessage = error === false ? undefined : message;\n // The icon is the only signal a consumer-forced `error` has; beside a message it\n // is duplication, and it crowds the suffix next to a reveal or copy control.\n const showErrorIcon = isError && !errorMessage;\n const hasSuffix =\n showErrorIcon ||\n shouldShowCopyButton ||\n buttons?.length ||\n (type === 'password' && showPasswordToggle);\n const errorMessageId = `${helperTextId}-error`;\n const describedBy =\n [\n descriptionId,\n hintId,\n helperText ? helperTextId : undefined,\n errorMessage ? errorMessageId : undefined,\n ariaDescribedBy,\n ]\n .filter(Boolean)\n .join(' ') || undefined;\n\n return (\n <>\n <div className={buildWrapperClasses(isError)}>\n {hasPrefix && (\n <div className=\"flex items-center pl-3 pr-1 flex-shrink-0 pointer-events-none\">\n <CompositeIcon\n prefixValue={prefixValue}\n prefixSecondaryValue={prefixSecondaryValue}\n icon={icon}\n />\n </div>\n )}\n\n <input\n id={controlId}\n type={actualInputType}\n placeholder={placeholder}\n value={displayValue}\n onChange={handleChange(onFieldChange)}\n onBlur={() => {\n onBlur?.();\n onFieldBlur?.();\n }}\n disabled={disabled || readOnly}\n readOnly={readOnly}\n autoComplete=\"off\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-invalid={isError || undefined}\n aria-required={required || undefined}\n aria-describedby={describedBy}\n className={`flex-1 bg-transparent border-none outline-none text-[var(--malachite-text-primary)] placeholder:text-[var(--malachite-text-placeholder)] disabled:text-[var(--malachite-text-primary)] disabled:cursor-not-allowed ${SIZE_TEXT[size]} ${SIZE_PADDING[size]} ${ellipsis ? 'min-w-0 truncate' : ''}`}\n name={name}\n />\n\n {hasSuffix && (\n <div\n className={`flex items-center pl-1 flex-shrink-0 ${type === 'password' && showPasswordToggle ? 'pr-0' : 'pr-3'}`}\n >\n <Suffix\n error={showErrorIcon}\n defaultValue={displayValue}\n onCopyClick={handleCopyClick}\n showCopyButton={shouldShowCopyButton}\n copied={copied}\n type={type}\n showPassword={showPassword}\n showPasswordToggle={showPasswordToggle}\n onTogglePassword={() => setShowPassword(!showPassword)}\n />\n <InputButtons buttons={buttons} />\n </div>\n )}\n </div>\n {helperText && (\n <div\n id={helperTextId}\n className=\"text-[length:var(--malachite-font-size-small,13px)] text-left pl-2 text-[var(--malachite-text-dimmed,rgb(161,161,161))]\"\n >\n {helperText}\n </div>\n )}\n <FieldMessage id={errorMessageId} message={errorMessage} />\n </>\n );\n }}\n </BaseFormComponent>\n );\n};\n\nconst CompositeIcon: React.FC<{\n prefixValue?: string;\n prefixSecondaryValue?: string;\n icon?: React.ReactNode;\n}> = ({ prefixValue, prefixSecondaryValue, icon }) => {\n return (\n <div className=\"flex items-center gap-1\">\n {icon && (\n <span className=\"flex items-center justify-center text-[var(--malachite-text-dimmed)] w-4 h-4 flex-shrink-0\">\n {icon}\n </span>\n )}\n {prefixValue && (\n <span className=\"text-[length:var(--malachite-font-size-medium,14px)] leading-[1.4] text-[var(--malachite-text-dimmed)] whitespace-nowrap\">\n {prefixValue}\n </span>\n )}\n {prefixSecondaryValue && (\n <span className=\"text-[length:var(--malachite-font-size-medium,14px)] leading-[1.4] text-[var(--malachite-text-dimmed)] whitespace-nowrap\">\n {prefixSecondaryValue}\n </span>\n )}\n </div>\n );\n};\n\nconst Suffix: React.FC<{\n error?: boolean;\n defaultValue?: string;\n onCopyClick?: () => void;\n showCopyButton?: boolean;\n copied?: boolean;\n type?: InputType;\n showPassword?: boolean;\n showPasswordToggle?: boolean;\n onTogglePassword?: () => void;\n}> = ({\n error,\n defaultValue,\n onCopyClick,\n showCopyButton = false,\n copied = false,\n type,\n showPassword,\n showPasswordToggle = true,\n onTogglePassword,\n}) => {\n return (\n <div className=\"flex items-center gap-1\">\n {error && (\n // Decorative: the rejection is carried by the message beneath the field, so\n // announcing an unnamed icon here would only repeat it as \"info\".\n <div\n aria-hidden=\"true\"\n className=\"flex items-center justify-center w-5 h-5 flex-shrink-0\"\n >\n <InfoIcon\n size={16}\n className=\"text-[var(--malachite-destructive-foreground,rgb(196,62,62))]\"\n />\n </div>\n )}\n {showCopyButton && onCopyClick && defaultValue && (\n <button\n onClick={onCopyClick}\n type=\"button\"\n className=\"flex items-center justify-center w-5 h-5 hover:bg-[var(--malachite-primary-border)] rounded transition-colors flex-shrink-0 cursor-pointer\"\n >\n {copied ? (\n <CheckCircleFilled\n size={16}\n style={{ color: 'var(--malachite-success-foreground)' }}\n />\n ) : (\n <CopyIcon size={16} />\n )}\n </button>\n )}\n {type === 'password' && showPasswordToggle && (\n <Button variant=\"no-style\" onClick={onTogglePassword} type=\"button\" size=\"small\">\n {showPassword ? (\n <EyeClosedIcon\n size={16}\n style={{ color: 'var(--malachite-text-dimmed)' }}\n />\n ) : (\n <EyeIcon\n size={16}\n style={{ color: 'var(--malachite-text-dimmed)' }}\n />\n )}\n </Button>\n )}\n </div>\n );\n};\n\nconst InputButtons: React.FC<{\n buttons?: ButtonDefinition[];\n}> = ({ buttons }) => {\n if (!buttons?.length) {\n return null;\n }\n\n return (\n <>\n {buttons.map((button) => (\n <Button\n key={button.label}\n variant={button.variant || (button.danger ? 'destructive' : 'outline')}\n size={button.size || 'small'}\n disabled={button.disabled}\n icon={button.icon}\n tooltip={button.tooltip}\n onClick={button.onClick}\n className=\"ml-1\"\n >\n {button.label}\n </Button>\n ))}\n </>\n );\n};\n\nexport default Input;\n"],"names":["SIZE_HEIGHT","SIZE_TEXT","SIZE_PADDING","SIZE_RADIUS","getVariantClasses","variant","error","Input","label","hint","tooltip","placeholder","icon","name","required","requiredIndicator","description","loading","readOnly","type","size","defaultValue","disabled","prefixValue","prefixSecondaryValue","ellipsis","formControl","buttons","onCopyClick","showCopyButton","onChange","value","onBlur","helperText","showPasswordToggle","id","ariaLabel","ariaLabelledBy","ariaDescribedBy","controlId","labelId","descriptionId","hintId","helperTextId","useFormFieldIds","showPassword","setShowPassword","useState","inputValue","setInputValue","copied","setCopied","useEffect","timer","handleChange","reportToForm","e","newValue","formValueRef","useRef","paintedValue","formValue","paintedFieldValue","handleCopyClick","copyValue","actualInputType","hasPrefix","heightClass","buildWrapperClasses","isError","variantStyles","jsx","BaseFormComponent","LoadingInput","fieldError","message","onFieldChange","onFieldBlur","displayValue","shouldShowCopyButton","errorMessage","showErrorIcon","hasSuffix","errorMessageId","describedBy","jsxs","Fragment","CompositeIcon","Suffix","InputButtons","FieldMessage","onTogglePassword","InfoIcon","CheckCircleFilled","CopyIcon","Button","EyeClosedIcon","EyeIcon","button"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Input.js","sources":["../../../src/components/Input/Input.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport type { Control, FieldPath, FieldValues } from 'react-hook-form';\nimport BaseFormComponent, { type RequiredIndicator, useFormFieldIds } from '../BaseFormComponent';\nimport { Button } from '../Button';\nimport CheckCircleFilled from '../CustomIcons/CheckCircleFilled';\nimport CopyIcon from '../CustomIcons/CopyIcon';\nimport EyeClosedIcon from '../CustomIcons/EyeClosedIcon';\nimport EyeIcon from '../CustomIcons/EyeIcon';\nimport InfoIcon from '../CustomIcons/InfoIcon';\nimport FieldMessage from '../FieldMessage';\nimport LoadingInput from '../LoadingInput';\nimport { paintedFieldValue } from '../devWarning';\nimport type { ButtonDefinition, InputSize, InputType, InputVariant } from './types';\n\ninterface InputProps {\n label?: string;\n hint?: string;\n tooltip?: string;\n placeholder?: string;\n icon?: React.ReactNode;\n readOnly?: boolean;\n name: FieldPath<FieldValues> | string;\n type?: InputType;\n description?: string;\n size?: InputSize;\n variant?: InputVariant;\n required?: boolean;\n requiredIndicator?: RequiredIndicator;\n error?: boolean;\n loading?: boolean;\n defaultValue?: string;\n disabled?: boolean;\n prefixValue?: string;\n prefixSecondaryValue?: string;\n ellipsis?: boolean;\n formControl?: Control<FieldValues, unknown>;\n buttons?: ButtonDefinition[];\n onCopyClick?: () => void;\n showCopyButton?: boolean;\n onChange?: (value: string) => void;\n /** Controlled value, for a source of truth outside the form (URL, app state). */\n value?: string;\n onBlur?: () => void;\n helperText?: React.ReactNode;\n showPasswordToggle?: boolean;\n id?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n 'aria-describedby'?: string;\n}\n\n// Figma specs one Input, at 42px / px-16 / 15px / radius 14 — the large tier. The smaller\n// tiers have no Input master, so they follow Search, which shares the field chrome.\nconst SIZE_HEIGHT: Record<InputSize, string> = {\n small: 'h-[var(--malachite-control-height-small,28px)]',\n medium: 'h-[var(--malachite-control-height-medium,32px)]',\n large: 'h-[var(--malachite-control-height-large,42px)]',\n};\n\nconst SIZE_TEXT: Record<InputSize, string> = {\n small: 'text-[length:var(--malachite-font-size-small,13px)]',\n medium: 'text-[length:var(--malachite-font-size-small,13px)]',\n large: 'text-[length:var(--malachite-font-size-15,15px)] leading-[1.4]',\n};\n\nconst SIZE_PADDING: Record<InputSize, string> = {\n small: 'px-[10px]',\n medium: 'px-[10px]',\n large: 'px-[16px]',\n};\n\nconst SIZE_RADIUS: Record<InputSize, string> = {\n small: 'rounded-[var(--malachite-radius-10,10px)]',\n medium: 'rounded-xl',\n large: 'rounded-[var(--malachite-radius-xxl,14px)]',\n};\n\nconst getVariantClasses = (\n variant: InputVariant,\n error?: boolean,\n): {\n wrapper: string[];\n shadow: string;\n borderRadius: string;\n} => {\n switch (variant) {\n case 'ghost':\n return {\n wrapper: [\n error\n ? 'border-0 border-b border-solid border-[var(--malachite-destructive-border)]'\n : '',\n error\n ? 'focus-within:border-[var(--malachite-destructive-border-hover)] hover:border-[var(--malachite-destructive-border-hover)]'\n : '',\n ],\n shadow: 'shadow-none',\n borderRadius: 'rounded-none',\n };\n case 'default':\n default:\n return {\n wrapper: [\n 'border border-solid',\n error\n ? 'border-[var(--malachite-destructive-border)]'\n : 'border-[var(--malachite-card-border)]',\n error\n ? 'focus-within:border-[var(--malachite-destructive-border-hover)] hover:border-[var(--malachite-destructive-border-hover)]'\n : 'focus-within:border-[var(--malachite-control-border-hover)] hover:border-[var(--malachite-control-border-hover)]',\n ],\n shadow: error ? 'shadow-error' : 'shadow-default',\n borderRadius: '',\n };\n }\n};\n\nconst Input: React.FC<InputProps> = ({\n label,\n hint,\n tooltip,\n placeholder,\n icon,\n name,\n required,\n requiredIndicator,\n error,\n description,\n loading = false,\n readOnly = false,\n type = 'text',\n size = 'large',\n variant = 'default',\n defaultValue,\n disabled,\n prefixValue,\n prefixSecondaryValue,\n ellipsis = false,\n formControl,\n buttons,\n onCopyClick,\n showCopyButton,\n onChange,\n value,\n onBlur,\n helperText,\n showPasswordToggle = true,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n}) => {\n const { controlId, labelId, descriptionId, hintId, helperTextId } = useFormFieldIds({\n id,\n label,\n description,\n hint,\n helperText: true,\n });\n const [showPassword, setShowPassword] = useState(false);\n const [inputValue, setInputValue] = useState(defaultValue || '');\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n if (copied) {\n const timer = setTimeout(() => {\n setCopied(false);\n }, 1000);\n return () => clearTimeout(timer);\n }\n }, [copied]);\n\n const handleChange =\n (reportToForm?: (value: unknown) => void) => (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setInputValue(newValue);\n onChange?.(newValue);\n reportToForm?.(newValue);\n };\n\n const formValueRef = useRef<unknown>(undefined);\n const paintedValue = (formValue: unknown): string =>\n paintedFieldValue({\n component: 'Input',\n name,\n value,\n formValue,\n defaultValue,\n bound: !!formControl,\n uncontrolledValue: inputValue,\n });\n\n const handleCopyClick = onCopyClick\n ? () => {\n const copyValue = paintedValue(formValueRef.current);\n if (copyValue) {\n navigator.clipboard.writeText(copyValue);\n setCopied(true);\n onCopyClick();\n }\n }\n : undefined;\n\n const actualInputType = type === 'password' ? (showPassword ? 'text' : 'password') : type;\n\n const hasPrefix = icon || prefixValue || prefixSecondaryValue;\n\n // The skeleton stands in for the field, so it reads the same height the wrapper does.\n const heightClass = SIZE_HEIGHT[size];\n\n const buildWrapperClasses = (isError: boolean): string => {\n const variantStyles = getVariantClasses(variant, isError);\n\n return [\n 'relative',\n 'flex items-center',\n 'w-full',\n variantStyles.borderRadius || SIZE_RADIUS[size],\n disabled\n ? 'bg-[var(--malachite-card-dimmed-background)]'\n : 'bg-[var(--malachite-card-background)]',\n 'transition-colors duration-200 ease-in-out',\n variantStyles.shadow,\n ...(disabled ? [] : variantStyles.wrapper),\n SIZE_HEIGHT[size],\n ]\n .filter(Boolean)\n .join(' ');\n };\n\n return (\n <BaseFormComponent\n label={label}\n hint={hint}\n tooltip={tooltip}\n name={name}\n required={required}\n requiredIndicator={requiredIndicator}\n loading={loading}\n formControl={formControl}\n description={description}\n controlId={controlId}\n labelId={labelId}\n descriptionId={descriptionId}\n hintId={hintId}\n loadingComponent={\n <LoadingInput\n heightClass={heightClass}\n helperText={!!helperText}\n helperTextMarginTop\n />\n }\n >\n {({ error: fieldError, message, onFieldChange, onFieldBlur, value: formValue }) => {\n formValueRef.current = formValue;\n const displayValue = paintedValue(formValue);\n const shouldShowCopyButton =\n showCopyButton ?? !!(handleCopyClick && disabled && displayValue);\n const isError = error ?? fieldError;\n // `error={false}` opts out of the resolver entirely, message included: a\n // screen that has dealt with the rejection its own way would otherwise keep\n // reporting it in destructive text and through `aria-describedby`.\n // The message is its own line rather than a replacement for the helper text:\n // a field that explains where to find its value still has to say why what was\n // typed was rejected, and dropping one for the other left the field marked red\n // with nothing anywhere saying what was wrong.\n const errorMessage = error === false ? undefined : message;\n // The icon is the only signal a consumer-forced `error` has; beside a message it\n // is duplication, and it crowds the suffix next to a reveal or copy control.\n const showErrorIcon = isError && !errorMessage;\n const hasSuffix =\n showErrorIcon ||\n shouldShowCopyButton ||\n buttons?.length ||\n (type === 'password' && showPasswordToggle);\n const errorMessageId = `${helperTextId}-error`;\n const describedBy =\n [\n descriptionId,\n hintId,\n helperText ? helperTextId : undefined,\n errorMessage ? errorMessageId : undefined,\n ariaDescribedBy,\n ]\n .filter(Boolean)\n .join(' ') || undefined;\n\n return (\n <>\n <div className={buildWrapperClasses(isError)}>\n {hasPrefix && (\n <div className=\"flex items-center pl-3 pr-1 flex-shrink-0 pointer-events-none\">\n <CompositeIcon\n prefixValue={prefixValue}\n prefixSecondaryValue={prefixSecondaryValue}\n icon={icon}\n />\n </div>\n )}\n\n <input\n id={controlId}\n type={actualInputType}\n placeholder={placeholder}\n value={displayValue}\n onChange={handleChange(onFieldChange)}\n onBlur={() => {\n onBlur?.();\n onFieldBlur?.();\n }}\n disabled={disabled || readOnly}\n readOnly={readOnly}\n autoComplete=\"off\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-invalid={isError || undefined}\n aria-required={required || undefined}\n aria-describedby={describedBy}\n className={`flex-1 bg-transparent border-none outline-none text-[var(--malachite-text-primary)] placeholder:text-[var(--malachite-text-placeholder)] disabled:text-[var(--malachite-text-primary)] disabled:cursor-not-allowed ${SIZE_TEXT[size]} ${SIZE_PADDING[size]} ${ellipsis ? 'min-w-0 truncate' : ''}`}\n name={name}\n />\n\n {hasSuffix && (\n <div\n className={`flex items-center pl-1 flex-shrink-0 ${type === 'password' && showPasswordToggle ? 'pr-0' : 'pr-3'}`}\n >\n <Suffix\n error={showErrorIcon}\n defaultValue={displayValue}\n onCopyClick={handleCopyClick}\n showCopyButton={shouldShowCopyButton}\n copied={copied}\n type={type}\n showPassword={showPassword}\n showPasswordToggle={showPasswordToggle}\n onTogglePassword={() => setShowPassword(!showPassword)}\n />\n <InputButtons buttons={buttons} />\n </div>\n )}\n </div>\n {helperText && (\n <div\n id={helperTextId}\n className=\"mt-1 text-[length:var(--malachite-font-size-small,13px)] text-left pl-2 text-[var(--malachite-text-dimmed,rgb(161,161,161))]\"\n >\n {helperText}\n </div>\n )}\n <FieldMessage id={errorMessageId} message={errorMessage} spaced />\n </>\n );\n }}\n </BaseFormComponent>\n );\n};\n\nconst CompositeIcon: React.FC<{\n prefixValue?: string;\n prefixSecondaryValue?: string;\n icon?: React.ReactNode;\n}> = ({ prefixValue, prefixSecondaryValue, icon }) => {\n return (\n <div className=\"flex items-center gap-1\">\n {icon && (\n <span className=\"flex items-center justify-center text-[var(--malachite-text-dimmed)] w-4 h-4 flex-shrink-0\">\n {icon}\n </span>\n )}\n {prefixValue && (\n <span className=\"text-[length:var(--malachite-font-size-medium,14px)] leading-[1.4] text-[var(--malachite-text-dimmed)] whitespace-nowrap\">\n {prefixValue}\n </span>\n )}\n {prefixSecondaryValue && (\n <span className=\"text-[length:var(--malachite-font-size-medium,14px)] leading-[1.4] text-[var(--malachite-text-dimmed)] whitespace-nowrap\">\n {prefixSecondaryValue}\n </span>\n )}\n </div>\n );\n};\n\nconst Suffix: React.FC<{\n error?: boolean;\n defaultValue?: string;\n onCopyClick?: () => void;\n showCopyButton?: boolean;\n copied?: boolean;\n type?: InputType;\n showPassword?: boolean;\n showPasswordToggle?: boolean;\n onTogglePassword?: () => void;\n}> = ({\n error,\n defaultValue,\n onCopyClick,\n showCopyButton = false,\n copied = false,\n type,\n showPassword,\n showPasswordToggle = true,\n onTogglePassword,\n}) => {\n return (\n <div className=\"flex items-center gap-1\">\n {error && (\n // Decorative: the rejection is carried by the message beneath the field, so\n // announcing an unnamed icon here would only repeat it as \"info\".\n <div\n aria-hidden=\"true\"\n className=\"flex items-center justify-center w-5 h-5 flex-shrink-0\"\n >\n <InfoIcon\n size={16}\n className=\"text-[var(--malachite-destructive-foreground,rgb(196,62,62))]\"\n />\n </div>\n )}\n {showCopyButton && onCopyClick && defaultValue && (\n <button\n onClick={onCopyClick}\n type=\"button\"\n className=\"flex items-center justify-center w-5 h-5 hover:bg-[var(--malachite-primary-border)] rounded transition-colors flex-shrink-0 cursor-pointer\"\n >\n {copied ? (\n <CheckCircleFilled\n size={16}\n style={{ color: 'var(--malachite-success-foreground)' }}\n />\n ) : (\n <CopyIcon size={16} />\n )}\n </button>\n )}\n {type === 'password' && showPasswordToggle && (\n <Button variant=\"no-style\" onClick={onTogglePassword} type=\"button\" size=\"small\">\n {showPassword ? (\n <EyeClosedIcon\n size={16}\n style={{ color: 'var(--malachite-text-dimmed)' }}\n />\n ) : (\n <EyeIcon\n size={16}\n style={{ color: 'var(--malachite-text-dimmed)' }}\n />\n )}\n </Button>\n )}\n </div>\n );\n};\n\nconst InputButtons: React.FC<{\n buttons?: ButtonDefinition[];\n}> = ({ buttons }) => {\n if (!buttons?.length) {\n return null;\n }\n\n return (\n <>\n {buttons.map((button) => (\n <Button\n key={button.label}\n variant={button.variant || (button.danger ? 'destructive' : 'outline')}\n size={button.size || 'small'}\n disabled={button.disabled}\n icon={button.icon}\n tooltip={button.tooltip}\n onClick={button.onClick}\n className=\"ml-1\"\n >\n {button.label}\n </Button>\n ))}\n </>\n );\n};\n\nexport default Input;\n"],"names":["SIZE_HEIGHT","SIZE_TEXT","SIZE_PADDING","SIZE_RADIUS","getVariantClasses","variant","error","Input","label","hint","tooltip","placeholder","icon","name","required","requiredIndicator","description","loading","readOnly","type","size","defaultValue","disabled","prefixValue","prefixSecondaryValue","ellipsis","formControl","buttons","onCopyClick","showCopyButton","onChange","value","onBlur","helperText","showPasswordToggle","id","ariaLabel","ariaLabelledBy","ariaDescribedBy","controlId","labelId","descriptionId","hintId","helperTextId","useFormFieldIds","showPassword","setShowPassword","useState","inputValue","setInputValue","copied","setCopied","useEffect","timer","handleChange","reportToForm","e","newValue","formValueRef","useRef","paintedValue","formValue","paintedFieldValue","handleCopyClick","copyValue","actualInputType","hasPrefix","heightClass","buildWrapperClasses","isError","variantStyles","jsx","BaseFormComponent","LoadingInput","fieldError","message","onFieldChange","onFieldBlur","displayValue","shouldShowCopyButton","errorMessage","showErrorIcon","hasSuffix","errorMessageId","describedBy","jsxs","Fragment","CompositeIcon","Suffix","InputButtons","FieldMessage","onTogglePassword","InfoIcon","CheckCircleFilled","CopyIcon","Button","EyeClosedIcon","EyeIcon","button"],"mappings":"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"}
|
|
@@ -29,7 +29,7 @@ const T = (r) => String(r.value) === "" ? r.label : `${r.value} ${r.label}`, b =
|
|
|
29
29
|
a.current !== null && (cancelAnimationFrame(a.current), a.current = null), t.current && (clearTimeout(t.current), t.current = null);
|
|
30
30
|
};
|
|
31
31
|
}, [r]);
|
|
32
|
-
const y = N === "sticky", u = d !== void 0 && d !== "", x = `sticky bottom-0 z-30${f ? " pt-4 pb-6" : ""}`, $ = /* @__PURE__ */ n("div", { className: k, style: { boxShadow: g }, children: [
|
|
32
|
+
const y = N === "sticky", u = d !== void 0 && d !== "", x = `sticky bottom-0 z-30 mt-auto${f ? " pt-4 pb-6" : ""}`, $ = /* @__PURE__ */ n("div", { className: k, style: { boxShadow: g }, children: [
|
|
33
33
|
i.length > 0 && /* @__PURE__ */ l(
|
|
34
34
|
"p",
|
|
35
35
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectionBar.js","sources":["../../src/components/SelectionBar.tsx"],"sourcesContent":["import React, { Fragment, useEffect, useRef, useState } from 'react';\nimport {\n PILL_BUTTON_BASE,\n PILL_BUTTON_CLASS,\n PILL_DIVIDER,\n PILL_MUTED_TEXT,\n PILL_NOTICE_SHADOW,\n PILL_NOTICE_SLEEVE,\n PILL_NOTICE_TEXT,\n PILL_SHADOW,\n PILL_SHEET_CSS,\n PILL_SHEET_ID,\n PILL_SURFACE,\n PILL_TRACK_CLASS,\n PILL_TRANSITION_MS,\n type SelectionBarCount,\n type SelectionBarEmphasis,\n} from './selectionBarChrome';\nimport { useAdoptedStyleSheet } from './useAdoptedStyleSheet';\n\nexport type { SelectionBarCount, SelectionBarEmphasis };\n\nexport interface SelectionBarAction {\n label: string;\n onClick: () => void;\n /** 'primary' commits the pending change; 'secondary' (the default) is a filled chip;\n * 'quiet' is unfilled and takes a wash on hover. */\n emphasis?: SelectionBarEmphasis;\n icon?: React.ReactNode;\n iconPosition?: 'start' | 'end';\n disabled?: boolean;\n /** Renders a separator glyph before this action. */\n dividerBefore?: boolean;\n}\n\nexport interface SelectionBarProps {\n /** Drives the mount: the bar animates in on true and animates out on false. */\n open: boolean;\n counts: SelectionBarCount[];\n actions: SelectionBarAction[];\n /** 'sticky' keeps the bar in view while its container scrolls; render it as the last child\n * of that content, with no `overflow: hidden` on any ancestor. */\n placement?: 'sticky' | 'inline';\n /**\n * Wraps the bar in a warning sleeve carrying this message, for state the bar is\n * reporting rather than an action it offers — e.g. that the selection is unsaved.\n * It leads the announcement. An empty string renders no sleeve.\n */\n notice?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n}\n\n/** A count reads as one phrase; an empty `value` carries a bare message instead. */\nconst countText = (count: SelectionBarCount): string =>\n String(count.value) === '' ? count.label : `${count.value} ${count.label}`;\n\nconst summarise = (counts: SelectionBarCount[]): string => counts.map(countText).join(', ');\n\n/** A notice is the reason the bar is up, so it leads the announcement. */\nconst announcement = (notice: string | undefined, counts: SelectionBarCount[]): string =>\n notice === undefined || notice === ''\n ? summarise(counts)\n : `${notice} ${summarise(counts)}`.trim();\n\n/**\n * A bar reporting a pending, not-yet-written selection and the actions that resolve it. Every\n * action destroys the button just pressed, so an `onClick` that ends the pending state must\n * also move focus.\n */\nexport const SelectionBar: React.FC<SelectionBarProps> = ({\n open,\n counts,\n actions,\n placement = 'sticky',\n notice,\n 'aria-label': ariaLabel = 'Pending changes',\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n}) => {\n const trackRef = useRef<HTMLDivElement>(null);\n const [isMounted, setIsMounted] = useState(open);\n const [isVisible, setIsVisible] = useState(open);\n const frameRef = useRef<number | null>(null);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useAdoptedStyleSheet(trackRef, PILL_SHEET_CSS, PILL_SHEET_ID);\n\n useEffect(() => {\n if (open) {\n setIsMounted(true);\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n frameRef.current = requestAnimationFrame(() => {\n frameRef.current = null;\n setIsVisible(true);\n });\n } else {\n // The browser refuses `aria-hidden` on a subtree still holding focus, so a caller\n // that forgets to move it would trap the keyboard here.\n const active = document.activeElement;\n if (active instanceof HTMLElement && trackRef.current?.contains(active)) {\n active.blur();\n }\n setIsVisible(false);\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n setIsMounted(false);\n }, PILL_TRANSITION_MS);\n }\n return () => {\n if (frameRef.current !== null) {\n cancelAnimationFrame(frameRef.current);\n frameRef.current = null;\n }\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n }, [open]);\n\n const isSticky = placement === 'sticky';\n const hasNotice = notice !== undefined && notice !== '';\n const stickyClass = `sticky bottom-0 z-30${isMounted ? ' pt-4 pb-6' : ''}`;\n\n const pill = (\n <div className={PILL_SURFACE} style={{ boxShadow: PILL_SHADOW }}>\n {counts.length > 0 && (\n <p\n aria-hidden=\"true\"\n className=\"m-0 flex items-center gap-2 text-[13px] leading-[1.6] tracking-[0.13px]\"\n >\n {counts.map((count, index) => (\n <Fragment key={count.label}>\n {index > 0 && <span className={PILL_MUTED_TEXT}>•</span>}\n <span>{countText(count)}</span>\n </Fragment>\n ))}\n </p>\n )}\n {actions.map((action) => (\n <Fragment key={action.label}>\n {action.dividerBefore && (\n <span aria-hidden=\"true\" className={`${PILL_DIVIDER} ${PILL_MUTED_TEXT}`}>\n |\n </span>\n )}\n <button\n type=\"button\"\n disabled={action.disabled}\n tabIndex={isVisible ? undefined : -1}\n onClick={action.onClick}\n className={`${PILL_BUTTON_BASE} ${PILL_BUTTON_CLASS}--${action.emphasis ?? 'secondary'}`}\n >\n {action.icon && action.iconPosition !== 'end' && (\n <span aria-hidden=\"true\" className=\"flex items-center\">\n {action.icon}\n </span>\n )}\n {action.label}\n {action.icon && action.iconPosition === 'end' && (\n <span aria-hidden=\"true\" className=\"flex items-center\">\n {action.icon}\n </span>\n )}\n </button>\n </Fragment>\n ))}\n </div>\n );\n\n return (\n // The live region has to exist BEFORE its text changes or the first announcement is dropped.\n <div\n ref={trackRef}\n className={`${PILL_TRACK_CLASS} pointer-events-none flex w-full justify-center ${isSticky ? stickyClass : 'relative'} ${className}`.trim()}\n style={{ opacity: isVisible ? 1 : 0 }}\n >\n <p className=\"sr-only\" role=\"status\">\n {isVisible ? announcement(notice, counts) : ''}\n </p>\n {isMounted && (\n <div\n role=\"group\"\n aria-label={ariaLabelledBy ? undefined : ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-hidden={isVisible ? undefined : true}\n className={isVisible ? 'pointer-events-auto' : undefined}\n >\n {/* Keep the wrapper stable: changing the pill's parent remounts it and drops focus\n from the action that raised the notice. */}\n <div\n className={hasNotice ? PILL_NOTICE_SLEEVE : undefined}\n style={hasNotice ? { boxShadow: PILL_NOTICE_SHADOW } : undefined}\n >\n {hasNotice && (\n <p aria-hidden=\"true\" className={PILL_NOTICE_TEXT}>\n {notice}\n </p>\n )}\n {pill}\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport default SelectionBar;\n"],"names":["countText","count","summarise","counts","announcement","notice","SelectionBar","open","actions","placement","ariaLabel","ariaLabelledBy","className","trackRef","useRef","isMounted","setIsMounted","useState","isVisible","setIsVisible","frameRef","timerRef","useAdoptedStyleSheet","PILL_SHEET_CSS","PILL_SHEET_ID","useEffect","active","_a","PILL_TRANSITION_MS","isSticky","hasNotice","stickyClass","pill","PILL_SURFACE","PILL_SHADOW","jsx","index","Fragment","PILL_MUTED_TEXT","action","PILL_DIVIDER","jsxs","PILL_BUTTON_BASE","PILL_BUTTON_CLASS","PILL_TRACK_CLASS","PILL_NOTICE_SLEEVE","PILL_NOTICE_SHADOW","PILL_NOTICE_TEXT"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SelectionBar.js","sources":["../../src/components/SelectionBar.tsx"],"sourcesContent":["import React, { Fragment, useEffect, useRef, useState } from 'react';\nimport {\n PILL_BUTTON_BASE,\n PILL_BUTTON_CLASS,\n PILL_DIVIDER,\n PILL_MUTED_TEXT,\n PILL_NOTICE_SHADOW,\n PILL_NOTICE_SLEEVE,\n PILL_NOTICE_TEXT,\n PILL_SHADOW,\n PILL_SHEET_CSS,\n PILL_SHEET_ID,\n PILL_SURFACE,\n PILL_TRACK_CLASS,\n PILL_TRANSITION_MS,\n type SelectionBarCount,\n type SelectionBarEmphasis,\n} from './selectionBarChrome';\nimport { useAdoptedStyleSheet } from './useAdoptedStyleSheet';\n\nexport type { SelectionBarCount, SelectionBarEmphasis };\n\nexport interface SelectionBarAction {\n label: string;\n onClick: () => void;\n /** 'primary' commits the pending change; 'secondary' (the default) is a filled chip;\n * 'quiet' is unfilled and takes a wash on hover. */\n emphasis?: SelectionBarEmphasis;\n icon?: React.ReactNode;\n iconPosition?: 'start' | 'end';\n disabled?: boolean;\n /** Renders a separator glyph before this action. */\n dividerBefore?: boolean;\n}\n\nexport interface SelectionBarProps {\n /** Drives the mount: the bar animates in on true and animates out on false. */\n open: boolean;\n counts: SelectionBarCount[];\n actions: SelectionBarAction[];\n /** 'sticky' keeps the bar in view while its container scrolls; render it as the last child\n * of that content, with no `overflow: hidden` on any ancestor. */\n placement?: 'sticky' | 'inline';\n /**\n * Wraps the bar in a warning sleeve carrying this message, for state the bar is\n * reporting rather than an action it offers — e.g. that the selection is unsaved.\n * It leads the announcement. An empty string renders no sleeve.\n */\n notice?: string;\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n}\n\n/** A count reads as one phrase; an empty `value` carries a bare message instead. */\nconst countText = (count: SelectionBarCount): string =>\n String(count.value) === '' ? count.label : `${count.value} ${count.label}`;\n\nconst summarise = (counts: SelectionBarCount[]): string => counts.map(countText).join(', ');\n\n/** A notice is the reason the bar is up, so it leads the announcement. */\nconst announcement = (notice: string | undefined, counts: SelectionBarCount[]): string =>\n notice === undefined || notice === ''\n ? summarise(counts)\n : `${notice} ${summarise(counts)}`.trim();\n\n/**\n * A bar reporting a pending, not-yet-written selection and the actions that resolve it. Every\n * action destroys the button just pressed, so an `onClick` that ends the pending state must\n * also move focus.\n */\nexport const SelectionBar: React.FC<SelectionBarProps> = ({\n open,\n counts,\n actions,\n placement = 'sticky',\n notice,\n 'aria-label': ariaLabel = 'Pending changes',\n 'aria-labelledby': ariaLabelledBy,\n className = '',\n}) => {\n const trackRef = useRef<HTMLDivElement>(null);\n const [isMounted, setIsMounted] = useState(open);\n const [isVisible, setIsVisible] = useState(open);\n const frameRef = useRef<number | null>(null);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useAdoptedStyleSheet(trackRef, PILL_SHEET_CSS, PILL_SHEET_ID);\n\n useEffect(() => {\n if (open) {\n setIsMounted(true);\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n frameRef.current = requestAnimationFrame(() => {\n frameRef.current = null;\n setIsVisible(true);\n });\n } else {\n // The browser refuses `aria-hidden` on a subtree still holding focus, so a caller\n // that forgets to move it would trap the keyboard here.\n const active = document.activeElement;\n if (active instanceof HTMLElement && trackRef.current?.contains(active)) {\n active.blur();\n }\n setIsVisible(false);\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n setIsMounted(false);\n }, PILL_TRANSITION_MS);\n }\n return () => {\n if (frameRef.current !== null) {\n cancelAnimationFrame(frameRef.current);\n frameRef.current = null;\n }\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n }, [open]);\n\n const isSticky = placement === 'sticky';\n const hasNotice = notice !== undefined && notice !== '';\n const stickyClass = `sticky bottom-0 z-30 mt-auto${isMounted ? ' pt-4 pb-6' : ''}`;\n\n const pill = (\n <div className={PILL_SURFACE} style={{ boxShadow: PILL_SHADOW }}>\n {counts.length > 0 && (\n <p\n aria-hidden=\"true\"\n className=\"m-0 flex items-center gap-2 text-[13px] leading-[1.6] tracking-[0.13px]\"\n >\n {counts.map((count, index) => (\n <Fragment key={count.label}>\n {index > 0 && <span className={PILL_MUTED_TEXT}>•</span>}\n <span>{countText(count)}</span>\n </Fragment>\n ))}\n </p>\n )}\n {actions.map((action) => (\n <Fragment key={action.label}>\n {action.dividerBefore && (\n <span aria-hidden=\"true\" className={`${PILL_DIVIDER} ${PILL_MUTED_TEXT}`}>\n |\n </span>\n )}\n <button\n type=\"button\"\n disabled={action.disabled}\n tabIndex={isVisible ? undefined : -1}\n onClick={action.onClick}\n className={`${PILL_BUTTON_BASE} ${PILL_BUTTON_CLASS}--${action.emphasis ?? 'secondary'}`}\n >\n {action.icon && action.iconPosition !== 'end' && (\n <span aria-hidden=\"true\" className=\"flex items-center\">\n {action.icon}\n </span>\n )}\n {action.label}\n {action.icon && action.iconPosition === 'end' && (\n <span aria-hidden=\"true\" className=\"flex items-center\">\n {action.icon}\n </span>\n )}\n </button>\n </Fragment>\n ))}\n </div>\n );\n\n return (\n // The live region has to exist BEFORE its text changes or the first announcement is dropped.\n <div\n ref={trackRef}\n className={`${PILL_TRACK_CLASS} pointer-events-none flex w-full justify-center ${isSticky ? stickyClass : 'relative'} ${className}`.trim()}\n style={{ opacity: isVisible ? 1 : 0 }}\n >\n <p className=\"sr-only\" role=\"status\">\n {isVisible ? announcement(notice, counts) : ''}\n </p>\n {isMounted && (\n <div\n role=\"group\"\n aria-label={ariaLabelledBy ? undefined : ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-hidden={isVisible ? undefined : true}\n className={isVisible ? 'pointer-events-auto' : undefined}\n >\n {/* Keep the wrapper stable: changing the pill's parent remounts it and drops focus\n from the action that raised the notice. */}\n <div\n className={hasNotice ? PILL_NOTICE_SLEEVE : undefined}\n style={hasNotice ? { boxShadow: PILL_NOTICE_SHADOW } : undefined}\n >\n {hasNotice && (\n <p aria-hidden=\"true\" className={PILL_NOTICE_TEXT}>\n {notice}\n </p>\n )}\n {pill}\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport default SelectionBar;\n"],"names":["countText","count","summarise","counts","announcement","notice","SelectionBar","open","actions","placement","ariaLabel","ariaLabelledBy","className","trackRef","useRef","isMounted","setIsMounted","useState","isVisible","setIsVisible","frameRef","timerRef","useAdoptedStyleSheet","PILL_SHEET_CSS","PILL_SHEET_ID","useEffect","active","_a","PILL_TRANSITION_MS","isSticky","hasNotice","stickyClass","pill","PILL_SURFACE","PILL_SHADOW","jsx","index","Fragment","PILL_MUTED_TEXT","action","PILL_DIVIDER","jsxs","PILL_BUTTON_BASE","PILL_BUTTON_CLASS","PILL_TRACK_CLASS","PILL_NOTICE_SLEEVE","PILL_NOTICE_SHADOW","PILL_NOTICE_TEXT"],"mappings":"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"}
|