@stackone/malachite 2.33.2 → 2.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +3 -3
- package/dist/components/Alert.js +77 -72
- package/dist/components/Alert.js.map +1 -1
- package/dist/components/Button.js +27 -30
- package/dist/components/Button.js.map +1 -1
- package/dist/components/Input/Input.js +47 -46
- package/dist/components/Input/Input.js.map +1 -1
- package/dist/components/PillButton.js +52 -33
- package/dist/components/PillButton.js.map +1 -1
- package/dist/components/Surface.js +100 -94
- package/dist/components/Surface.js.map +1 -1
- package/dist/components/linkRel.js +9 -0
- package/dist/components/linkRel.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/tailwind.css.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as
|
|
1
|
+
import { jsx as a, 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 = (r, e) => {
|
|
30
|
+
switch (r) {
|
|
31
31
|
case "ghost":
|
|
32
32
|
return {
|
|
33
33
|
wrapper: [
|
|
@@ -50,19 +50,19 @@ const J = {
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
}, Je = ({
|
|
53
|
-
label:
|
|
53
|
+
label: r,
|
|
54
54
|
hint: e,
|
|
55
55
|
tooltip: i,
|
|
56
56
|
placeholder: x,
|
|
57
57
|
icon: m,
|
|
58
|
-
name:
|
|
58
|
+
name: o,
|
|
59
59
|
required: h,
|
|
60
60
|
requiredIndicator: g,
|
|
61
61
|
error: p,
|
|
62
62
|
description: R,
|
|
63
63
|
loading: Y = !1,
|
|
64
64
|
readOnly: V = !1,
|
|
65
|
-
type:
|
|
65
|
+
type: s = "text",
|
|
66
66
|
size: d = "large",
|
|
67
67
|
variant: F = "default",
|
|
68
68
|
defaultValue: $,
|
|
@@ -75,22 +75,22 @@ const J = {
|
|
|
75
75
|
onCopyClick: _,
|
|
76
76
|
showCopyButton: ee,
|
|
77
77
|
onChange: b,
|
|
78
|
-
value:
|
|
78
|
+
value: ae,
|
|
79
79
|
onBlur: w,
|
|
80
80
|
helperText: f,
|
|
81
81
|
showPasswordToggle: I = !0,
|
|
82
|
-
id:
|
|
82
|
+
id: re,
|
|
83
83
|
"aria-label": te,
|
|
84
84
|
"aria-labelledby": ie,
|
|
85
85
|
"aria-describedby": le
|
|
86
86
|
}) => {
|
|
87
|
-
const { controlId: D, labelId:
|
|
88
|
-
id:
|
|
89
|
-
label:
|
|
87
|
+
const { controlId: D, labelId: oe, descriptionId: P, hintId: A, helperTextId: y } = we({
|
|
88
|
+
id: re,
|
|
89
|
+
label: r,
|
|
90
90
|
description: R,
|
|
91
91
|
hint: e,
|
|
92
92
|
helperText: !0
|
|
93
|
-
}), [C,
|
|
93
|
+
}), [C, se] = j(!1), [de, ce] = j($ || ""), [N, G] = j(!1);
|
|
94
94
|
xe(() => {
|
|
95
95
|
if (N) {
|
|
96
96
|
const t = setTimeout(() => {
|
|
@@ -104,8 +104,8 @@ const J = {
|
|
|
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
|
-
name:
|
|
108
|
-
value:
|
|
107
|
+
name: o,
|
|
108
|
+
value: ae,
|
|
109
109
|
formValue: t,
|
|
110
110
|
defaultValue: $,
|
|
111
111
|
bound: !!Z,
|
|
@@ -113,7 +113,7 @@ const J = {
|
|
|
113
113
|
}), L = _ ? () => {
|
|
114
114
|
const t = B(H.current);
|
|
115
115
|
t && (navigator.clipboard.writeText(t), G(!0), _());
|
|
116
|
-
} : void 0, me =
|
|
116
|
+
} : void 0, me = s === "password" ? C ? "text" : "password" : s, he = m || M || T, pe = J[d], ue = (t) => {
|
|
117
117
|
const l = $e(F, t);
|
|
118
118
|
return [
|
|
119
119
|
"relative",
|
|
@@ -127,23 +127,23 @@ const J = {
|
|
|
127
127
|
J[d]
|
|
128
128
|
].filter(Boolean).join(" ");
|
|
129
129
|
};
|
|
130
|
-
return /* @__PURE__ */
|
|
130
|
+
return /* @__PURE__ */ a(
|
|
131
131
|
be,
|
|
132
132
|
{
|
|
133
|
-
label:
|
|
133
|
+
label: r,
|
|
134
134
|
hint: e,
|
|
135
135
|
tooltip: i,
|
|
136
|
-
name:
|
|
136
|
+
name: o,
|
|
137
137
|
required: h,
|
|
138
138
|
requiredIndicator: g,
|
|
139
139
|
loading: Y,
|
|
140
140
|
formControl: Z,
|
|
141
141
|
description: R,
|
|
142
142
|
controlId: D,
|
|
143
|
-
labelId:
|
|
143
|
+
labelId: oe,
|
|
144
144
|
descriptionId: P,
|
|
145
145
|
hintId: A,
|
|
146
|
-
loadingComponent: /* @__PURE__ */
|
|
146
|
+
loadingComponent: /* @__PURE__ */ a(
|
|
147
147
|
ze,
|
|
148
148
|
{
|
|
149
149
|
heightClass: pe,
|
|
@@ -153,7 +153,7 @@ const J = {
|
|
|
153
153
|
),
|
|
154
154
|
children: ({ error: t, message: l, onFieldChange: c, onFieldBlur: k, value: U }) => {
|
|
155
155
|
H.current = U;
|
|
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) ||
|
|
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) || s === "password" && I, q = `${y}-error`, fe = [
|
|
157
157
|
P,
|
|
158
158
|
A,
|
|
159
159
|
f ? y : void 0,
|
|
@@ -162,7 +162,7 @@ const J = {
|
|
|
162
162
|
].filter(Boolean).join(" ") || void 0;
|
|
163
163
|
return /* @__PURE__ */ n(K, { children: [
|
|
164
164
|
/* @__PURE__ */ n("div", { className: ue(z), children: [
|
|
165
|
-
he && /* @__PURE__ */
|
|
165
|
+
he && /* @__PURE__ */ a("div", { className: "flex items-center pl-3 pr-1 flex-shrink-0 pointer-events-none", children: /* @__PURE__ */ a(
|
|
166
166
|
Me,
|
|
167
167
|
{
|
|
168
168
|
prefixValue: M,
|
|
@@ -170,7 +170,7 @@ const J = {
|
|
|
170
170
|
icon: m
|
|
171
171
|
}
|
|
172
172
|
) }),
|
|
173
|
-
/* @__PURE__ */
|
|
173
|
+
/* @__PURE__ */ a(
|
|
174
174
|
"input",
|
|
175
175
|
{
|
|
176
176
|
id: D,
|
|
@@ -190,15 +190,15 @@ const J = {
|
|
|
190
190
|
"aria-required": h || void 0,
|
|
191
191
|
"aria-describedby": fe,
|
|
192
192
|
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 ${je[d]} ${Re[d]} ${O ? "min-w-0 truncate" : ""}`,
|
|
193
|
-
name:
|
|
193
|
+
name: o
|
|
194
194
|
}
|
|
195
195
|
),
|
|
196
196
|
ve && /* @__PURE__ */ n(
|
|
197
197
|
"div",
|
|
198
198
|
{
|
|
199
|
-
className: `flex items-center pl-1 flex-shrink-0 ${
|
|
199
|
+
className: `flex items-center pl-1 flex-shrink-0 ${s === "password" && I ? "pr-0" : "pr-3"}`,
|
|
200
200
|
children: [
|
|
201
|
-
/* @__PURE__ */
|
|
201
|
+
/* @__PURE__ */ a(
|
|
202
202
|
Te,
|
|
203
203
|
{
|
|
204
204
|
error: X,
|
|
@@ -206,18 +206,18 @@ const J = {
|
|
|
206
206
|
onCopyClick: L,
|
|
207
207
|
showCopyButton: W,
|
|
208
208
|
copied: N,
|
|
209
|
-
type:
|
|
209
|
+
type: s,
|
|
210
210
|
showPassword: C,
|
|
211
211
|
showPasswordToggle: I,
|
|
212
|
-
onTogglePassword: () =>
|
|
212
|
+
onTogglePassword: () => se(!C)
|
|
213
213
|
}
|
|
214
214
|
),
|
|
215
|
-
/* @__PURE__ */
|
|
215
|
+
/* @__PURE__ */ a(Ze, { buttons: v })
|
|
216
216
|
]
|
|
217
217
|
}
|
|
218
218
|
)
|
|
219
219
|
] }),
|
|
220
|
-
f && /* @__PURE__ */
|
|
220
|
+
f && /* @__PURE__ */ a(
|
|
221
221
|
"div",
|
|
222
222
|
{
|
|
223
223
|
id: y,
|
|
@@ -225,34 +225,34 @@ const J = {
|
|
|
225
225
|
children: f
|
|
226
226
|
}
|
|
227
227
|
),
|
|
228
|
-
/* @__PURE__ */
|
|
228
|
+
/* @__PURE__ */ a(Ee, { id: q, message: S, spaced: !0 })
|
|
229
229
|
] });
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
);
|
|
233
|
-
}, Me = ({ prefixValue:
|
|
234
|
-
i && /* @__PURE__ */
|
|
235
|
-
|
|
236
|
-
e && /* @__PURE__ */
|
|
233
|
+
}, Me = ({ prefixValue: r, prefixSecondaryValue: e, icon: i }) => /* @__PURE__ */ n("div", { className: "flex items-center gap-1", children: [
|
|
234
|
+
i && /* @__PURE__ */ a("span", { className: "flex items-center justify-center text-[var(--malachite-text-dimmed)] w-4 h-4 flex-shrink-0", children: i }),
|
|
235
|
+
r && /* @__PURE__ */ a("span", { className: "text-[length:var(--malachite-font-size-medium,14px)] leading-[1.4] text-[var(--malachite-text-dimmed)] whitespace-nowrap", children: r }),
|
|
236
|
+
e && /* @__PURE__ */ a("span", { className: "text-[length:var(--malachite-font-size-medium,14px)] leading-[1.4] text-[var(--malachite-text-dimmed)] whitespace-nowrap", children: e })
|
|
237
237
|
] }), Te = ({
|
|
238
|
-
error:
|
|
238
|
+
error: r,
|
|
239
239
|
defaultValue: e,
|
|
240
240
|
onCopyClick: i,
|
|
241
241
|
showCopyButton: x = !1,
|
|
242
242
|
copied: m = !1,
|
|
243
|
-
type:
|
|
243
|
+
type: o,
|
|
244
244
|
showPassword: h,
|
|
245
245
|
showPasswordToggle: g = !0,
|
|
246
246
|
onTogglePassword: p
|
|
247
247
|
}) => /* @__PURE__ */ n("div", { className: "flex items-center gap-1", children: [
|
|
248
|
-
|
|
248
|
+
r && // Decorative: the rejection is carried by the message beneath the field, so
|
|
249
249
|
// announcing an unnamed icon here would only repeat it as "info".
|
|
250
|
-
/* @__PURE__ */
|
|
250
|
+
/* @__PURE__ */ a(
|
|
251
251
|
"div",
|
|
252
252
|
{
|
|
253
253
|
"aria-hidden": "true",
|
|
254
254
|
className: "flex items-center justify-center w-5 h-5 flex-shrink-0",
|
|
255
|
-
children: /* @__PURE__ */
|
|
255
|
+
children: /* @__PURE__ */ a(
|
|
256
256
|
ke,
|
|
257
257
|
{
|
|
258
258
|
size: 16,
|
|
@@ -261,35 +261,36 @@ const J = {
|
|
|
261
261
|
)
|
|
262
262
|
}
|
|
263
263
|
),
|
|
264
|
-
x && i && e && /* @__PURE__ */
|
|
264
|
+
x && i && e && /* @__PURE__ */ a(
|
|
265
265
|
"button",
|
|
266
266
|
{
|
|
267
267
|
onClick: i,
|
|
268
268
|
type: "button",
|
|
269
|
+
"aria-label": "Copy",
|
|
269
270
|
className: "flex items-center justify-center w-5 h-5 hover:bg-[var(--malachite-primary-border)] rounded transition-colors flex-shrink-0 cursor-pointer",
|
|
270
|
-
children: m ? /* @__PURE__ */
|
|
271
|
+
children: m ? /* @__PURE__ */ a(
|
|
271
272
|
Ie,
|
|
272
273
|
{
|
|
273
274
|
size: 16,
|
|
274
275
|
style: { color: "var(--malachite-success-foreground)" }
|
|
275
276
|
}
|
|
276
|
-
) : /* @__PURE__ */
|
|
277
|
+
) : /* @__PURE__ */ a(ye, { size: 16 })
|
|
277
278
|
}
|
|
278
279
|
),
|
|
279
|
-
|
|
280
|
+
o === "password" && g && /* @__PURE__ */ a(Q, { variant: "no-style", onClick: p, type: "button", size: "small", children: h ? /* @__PURE__ */ a(
|
|
280
281
|
Ce,
|
|
281
282
|
{
|
|
282
283
|
size: 16,
|
|
283
284
|
style: { color: "var(--malachite-text-dimmed)" }
|
|
284
285
|
}
|
|
285
|
-
) : /* @__PURE__ */
|
|
286
|
+
) : /* @__PURE__ */ a(
|
|
286
287
|
Ne,
|
|
287
288
|
{
|
|
288
289
|
size: 16,
|
|
289
290
|
style: { color: "var(--malachite-text-dimmed)" }
|
|
290
291
|
}
|
|
291
292
|
) })
|
|
292
|
-
] }), Ze = ({ buttons:
|
|
293
|
+
] }), Ze = ({ buttons: r }) => r != null && r.length ? /* @__PURE__ */ a(K, { children: r.map((e) => /* @__PURE__ */ a(
|
|
293
294
|
Q,
|
|
294
295
|
{
|
|
295
296
|
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?: 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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"}
|
|
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 aria-label=\"Copy\"\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,12 +1,14 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { FOCUS_RING as
|
|
3
|
-
|
|
1
|
+
import { jsxs as p, jsx as x } from "react/jsx-runtime";
|
|
2
|
+
import { FOCUS_RING as S } from "./focusRing.js";
|
|
3
|
+
import { resolveRel as R } from "./linkRel.js";
|
|
4
|
+
import { safeHref as T } from "./safeHref.js";
|
|
5
|
+
const j = "inline-flex items-center justify-center gap-1.5 font-medium whitespace-nowrap shrink-0 transition-colors duration-150 ease-in-out", y = {
|
|
4
6
|
pill: "rounded-[var(--malachite-radius-10,10px)]",
|
|
5
7
|
tab: "rounded-b-[var(--malachite-radius-10,10px)] rounded-t-none"
|
|
6
|
-
},
|
|
8
|
+
}, B = {
|
|
7
9
|
default: "border-0",
|
|
8
10
|
assistant: "border border-solid border-[var(--malachite-tag-assistant-border)]"
|
|
9
|
-
},
|
|
11
|
+
}, $ = "bg-[var(--malachite-card-background)] text-[var(--malachite-assistant-foreground)]", w = {
|
|
10
12
|
// For a row that labels other content rather than being read on its own — a context
|
|
11
13
|
// strip above a chat, say. The only size that drops below the 13px the others share.
|
|
12
14
|
xsmall: {
|
|
@@ -24,7 +26,7 @@ const E = "inline-flex items-center justify-center gap-1.5 font-medium whitespac
|
|
|
24
26
|
padding: "px-2.5",
|
|
25
27
|
paddingWithIcon: "pl-1.5 pr-2"
|
|
26
28
|
}
|
|
27
|
-
},
|
|
29
|
+
}, z = "bg-[var(--malachite-pillbutton-static-background)] text-[var(--malachite-pillbutton-static-foreground)]", W = "active:opacity-60 cursor-pointer", H = {
|
|
28
30
|
default: {
|
|
29
31
|
base: "bg-[var(--malachite-pillbutton-background)] text-[var(--malachite-pillbutton-foreground)]",
|
|
30
32
|
selected: "bg-[var(--malachite-pillbutton-hover-background)] text-[var(--malachite-pillbutton-hover-foreground)]",
|
|
@@ -35,36 +37,53 @@ const E = "inline-flex items-center justify-center gap-1.5 font-medium whitespac
|
|
|
35
37
|
selected: "bg-[var(--malachite-pillbutton-inverted-hover-background)] text-[var(--malachite-pillbutton-inverted-hover-foreground)]",
|
|
36
38
|
hover: "hover:bg-[var(--malachite-pillbutton-inverted-hover-background)] hover:text-[var(--malachite-pillbutton-inverted-hover-foreground)] active:bg-[var(--malachite-pillbutton-inverted-pressed-background)] active:text-[var(--malachite-pillbutton-inverted-pressed-foreground)]"
|
|
37
39
|
}
|
|
38
|
-
},
|
|
39
|
-
label:
|
|
40
|
-
icon:
|
|
41
|
-
className:
|
|
42
|
-
selected:
|
|
43
|
-
disabled:
|
|
44
|
-
interactive:
|
|
45
|
-
size:
|
|
46
|
-
variant:
|
|
47
|
-
shape:
|
|
48
|
-
tone:
|
|
49
|
-
trailingIcon:
|
|
50
|
-
onClick:
|
|
40
|
+
}, K = ({
|
|
41
|
+
label: r = "Label",
|
|
42
|
+
icon: l,
|
|
43
|
+
className: k = "",
|
|
44
|
+
selected: A = !1,
|
|
45
|
+
disabled: m = !1,
|
|
46
|
+
interactive: v = !0,
|
|
47
|
+
size: C = "small",
|
|
48
|
+
variant: L = "default",
|
|
49
|
+
shape: I = "pill",
|
|
50
|
+
tone: b = "default",
|
|
51
|
+
trailingIcon: g,
|
|
52
|
+
onClick: n,
|
|
53
|
+
href: E,
|
|
54
|
+
target: f,
|
|
55
|
+
rel: _
|
|
51
56
|
}) => {
|
|
52
|
-
const
|
|
53
|
-
let
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
const i = w[C], N = l ? i.paddingWithIcon : i.padding, t = H[L], o = v && !m, s = T(E), a = o && (!!n || !!s), c = a ? ` ${W}` : "";
|
|
58
|
+
let e;
|
|
59
|
+
b === "assistant" ? e = a ? `${$}${c}` : $ : v ? A ? e = o ? `${t.selected}${c}` : t.selected : o ? e = `${t.base} ${t.hover}${c}` : e = t.base : e = z;
|
|
60
|
+
const O = m ? "opacity-50 cursor-not-allowed pointer-events-none" : "", d = `${j} ${y[I]} ${i.text} ${N} ${B[b]} ${e} ${O} ${k}`, u = l ? /* @__PURE__ */ x("span", { className: "inline-flex items-center justify-center w-4 h-4 shrink-0 [&>img]:max-w-full [&>img]:max-h-full [&>img]:object-contain", children: l }) : null, h = g ? /* @__PURE__ */ x("span", { className: "inline-flex shrink-0 items-center justify-center", children: g }) : null;
|
|
61
|
+
return a && s ? /* @__PURE__ */ p(
|
|
62
|
+
"a",
|
|
63
|
+
{
|
|
64
|
+
href: s,
|
|
65
|
+
target: f,
|
|
66
|
+
rel: R(_, f),
|
|
67
|
+
className: `${S} ${d} no-underline`,
|
|
68
|
+
onClick: n,
|
|
69
|
+
children: [
|
|
70
|
+
u,
|
|
71
|
+
r,
|
|
72
|
+
h
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
) : a ? /* @__PURE__ */ p("button", { type: "button", className: `${S} ${d}`, onClick: n, children: [
|
|
76
|
+
u,
|
|
77
|
+
r,
|
|
78
|
+
h
|
|
79
|
+
] }) : /* @__PURE__ */ p("div", { className: d, children: [
|
|
80
|
+
u,
|
|
81
|
+
r,
|
|
82
|
+
h
|
|
64
83
|
] });
|
|
65
84
|
};
|
|
66
85
|
export {
|
|
67
|
-
|
|
68
|
-
|
|
86
|
+
K as PillButton,
|
|
87
|
+
K as default
|
|
69
88
|
};
|
|
70
89
|
//# sourceMappingURL=PillButton.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PillButton.js","sources":["../../src/components/PillButton.tsx"],"sourcesContent":["import React from 'react';\nimport { FOCUS_RING } from './focusRing';\n\ntype PillButtonSize = 'xsmall' | 'small' | 'medium';\ntype PillButtonVariant = 'default' | 'inverted';\n/**\n * `tab` squares the top corners so the pill reads as attached to whatever sits above it —\n * a row of controls hanging under an input, rather than free-floating pills.\n */\ntype PillButtonShape = 'pill' | 'tab';\n/**\n * `assistant` is the chrome for AI-authored chips (e.g. `ChangeReview.Jump`): a fixed\n * violet-on-white look with its own border, independent of `variant`/`selected`.\n */\ntype PillButtonTone = 'default' | 'assistant';\n\ninterface PillButtonProps {\n label: string;\n icon?: React.ReactNode;\n className?: string;\n selected?: boolean;\n disabled?: boolean;\n interactive?: boolean;\n size?: PillButtonSize;\n variant?: PillButtonVariant;\n shape?: PillButtonShape;\n tone?: PillButtonTone;\n /** Sits after the label — a caret on a pill that opens a menu. */\n trailingIcon?: React.ReactNode;\n onClick?: () => void;\n}\n\nconst BASE_CLASSES =\n 'inline-flex items-center justify-center gap-1.5 font-medium whitespace-nowrap shrink-0 transition-colors duration-150 ease-in-out';\n\nconst SHAPE_CLASSES: Record<PillButtonShape, string> = {\n pill: 'rounded-[var(--malachite-radius-10,10px)]',\n tab: 'rounded-b-[var(--malachite-radius-10,10px)] rounded-t-none',\n};\n\n// Box-sizing is border-box, so trading `border-0` for a 1px border only eats into the padding\n// box — it never grows the element past the `h-[...]` each size already fixes. Only one of these\n// ever applies per render, so there's no `border-0`/`border` class collision to referee.\nconst TONE_BORDER_CLASSES: Record<PillButtonTone, string> = {\n default: 'border-0',\n assistant: 'border border-solid border-[var(--malachite-tag-assistant-border)]',\n};\n\nconst ASSISTANT_COLORS =\n 'bg-[var(--malachite-card-background)] text-[var(--malachite-assistant-foreground)]';\n\nconst SIZE_CLASSES: Record<\n PillButtonSize,\n { text: string; padding: string; paddingWithIcon: string }\n> = {\n // For a row that labels other content rather than being read on its own — a context\n // strip above a chat, say. The only size that drops below the 13px the others share.\n xsmall: {\n text: 'text-[length:var(--malachite-font-size-xsmall,11px)] h-[22px] leading-none',\n padding: 'px-2',\n paddingWithIcon: 'pl-1 pr-1.5',\n },\n small: {\n text: 'text-[length:var(--malachite-font-size-small,13px)] h-[var(--malachite-control-height-small,28px)] leading-none',\n padding: 'px-2.5',\n paddingWithIcon: 'pl-1.5 pr-2',\n },\n medium: {\n text: 'text-[length:var(--malachite-font-size-small,13px)] h-[var(--malachite-control-height-medium,32px)] leading-none',\n padding: 'px-2.5',\n paddingWithIcon: 'pl-1.5 pr-2',\n },\n};\n\nconst STATIC_COLORS =\n 'bg-[var(--malachite-pillbutton-static-background)] text-[var(--malachite-pillbutton-static-foreground)]';\n\nconst CLICKABLE_TAIL = 'active:opacity-60 cursor-pointer';\n\nconst VARIANT_COLORS: Record<PillButtonVariant, { base: string; selected: string; hover: string }> =\n {\n default: {\n base: 'bg-[var(--malachite-pillbutton-background)] text-[var(--malachite-pillbutton-foreground)]',\n selected:\n 'bg-[var(--malachite-pillbutton-hover-background)] text-[var(--malachite-pillbutton-hover-foreground)]',\n hover: 'hover:bg-[var(--malachite-pillbutton-hover-background)] hover:text-[var(--malachite-pillbutton-hover-foreground)] active:bg-[var(--malachite-pillbutton-pressed-background)] active:text-[var(--malachite-pillbutton-pressed-foreground)]',\n },\n inverted: {\n base: 'bg-[var(--malachite-pillbutton-inverted-background)] text-[var(--malachite-pillbutton-inverted-foreground)]',\n selected:\n 'bg-[var(--malachite-pillbutton-inverted-hover-background)] text-[var(--malachite-pillbutton-inverted-hover-foreground)]',\n hover: 'hover:bg-[var(--malachite-pillbutton-inverted-hover-background)] hover:text-[var(--malachite-pillbutton-inverted-hover-foreground)] active:bg-[var(--malachite-pillbutton-inverted-pressed-background)] active:text-[var(--malachite-pillbutton-inverted-pressed-foreground)]',\n },\n };\n\nexport const PillButton: React.FC<PillButtonProps> = ({\n label = 'Label',\n icon,\n className = '',\n selected = false,\n disabled = false,\n interactive = true,\n size = 'small',\n variant = 'default',\n shape = 'pill',\n tone = 'default',\n trailingIcon,\n onClick,\n}) => {\n const sizeBlock = SIZE_CLASSES[size];\n const padding = icon ? sizeBlock.paddingWithIcon : sizeBlock.padding;\n const variantBlock = VARIANT_COLORS[variant];\n const isInteractiveLook = interactive && !disabled;\n const isClickable = isInteractiveLook && !!onClick;\n const clickableTail = isClickable ? ` ${CLICKABLE_TAIL}` : '';\n\n let colorClasses: string;\n if (tone === 'assistant') {\n // Assistant chips read the same in every interaction state — the click affordance is\n // the shared `active:opacity-60` from `clickableTail`, not a colour swap.\n colorClasses = isClickable ? `${ASSISTANT_COLORS}${clickableTail}` : ASSISTANT_COLORS;\n } else if (!interactive) {\n colorClasses = STATIC_COLORS;\n } else if (selected) {\n colorClasses = isInteractiveLook\n ? `${variantBlock.selected}${clickableTail}`\n : variantBlock.selected;\n } else if (isInteractiveLook) {\n colorClasses = `${variantBlock.base} ${variantBlock.hover}${clickableTail}`;\n } else {\n colorClasses = variantBlock.base;\n }\n\n const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : '';\n\n const merged = `${BASE_CLASSES} ${SHAPE_CLASSES[shape]} ${sizeBlock.text} ${padding} ${TONE_BORDER_CLASSES[tone]} ${colorClasses} ${disabledClasses} ${className}`;\n\n const iconSlot = icon ? (\n <span className=\"inline-flex items-center justify-center w-4 h-4 shrink-0 [&>img]:max-w-full [&>img]:max-h-full [&>img]:object-contain\">\n {icon}\n </span>\n ) : null;\n\n const trailingSlot = trailingIcon ? (\n <span className=\"inline-flex shrink-0 items-center justify-center\">{trailingIcon}</span>\n ) : null;\n\n if (isClickable) {\n return (\n <button type=\"button\" className={`${FOCUS_RING} ${merged}`} onClick={onClick}>\n {iconSlot}\n {label}\n {trailingSlot}\n </button>\n );\n }\n\n return (\n <div className={merged}>\n {iconSlot}\n {label}\n {trailingSlot}\n </div>\n );\n};\n\nexport default PillButton;\n"],"names":["BASE_CLASSES","SHAPE_CLASSES","TONE_BORDER_CLASSES","ASSISTANT_COLORS","SIZE_CLASSES","STATIC_COLORS","CLICKABLE_TAIL","VARIANT_COLORS","PillButton","label","icon","className","selected","disabled","interactive","size","variant","shape","tone","trailingIcon","onClick","sizeBlock","padding","variantBlock","isInteractiveLook","isClickable","clickableTail","colorClasses","disabledClasses","merged","iconSlot","jsx","trailingSlot","jsxs","FOCUS_RING"],"mappings":";;AAgCA,MAAMA,IACF,qIAEEC,IAAiD;AAAA,EACnD,MAAM;AAAA,EACN,KAAK;AACT,GAKMC,IAAsD;AAAA,EACxD,SAAS;AAAA,EACT,WAAW;AACf,GAEMC,IACF,sFAEEC,IAGF;AAAA;AAAA;AAAA,EAGA,QAAQ;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,iBAAiB;AAAA,EAAA;AAAA,EAErB,OAAO;AAAA,IACH,MAAM;AAAA,IACN,SAAS;AAAA,IACT,iBAAiB;AAAA,EAAA;AAAA,EAErB,QAAQ;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,iBAAiB;AAAA,EAAA;AAEzB,GAEMC,IACF,2GAEEC,IAAiB,oCAEjBC,IACF;AAAA,EACI,SAAS;AAAA,IACL,MAAM;AAAA,IACN,UACI;AAAA,IACJ,OAAO;AAAA,EAAA;AAAA,EAEX,UAAU;AAAA,IACN,MAAM;AAAA,IACN,UACI;AAAA,IACJ,OAAO;AAAA,EAAA;AAEf,GAESC,IAAwC,CAAC;AAAA,EAClD,OAAAC,IAAQ;AAAA,EACR,MAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,UAAAC,IAAW;AAAA,EACX,UAAAC,IAAW;AAAA,EACX,aAAAC,IAAc;AAAA,EACd,MAAAC,IAAO;AAAA,EACP,SAAAC,IAAU;AAAA,EACV,OAAAC,IAAQ;AAAA,EACR,MAAAC,IAAO;AAAA,EACP,cAAAC;AAAA,EACA,SAAAC;AACJ,MAAM;AACF,QAAMC,IAAYjB,EAAaW,CAAI,GAC7BO,IAAUZ,IAAOW,EAAU,kBAAkBA,EAAU,SACvDE,IAAehB,EAAeS,CAAO,GACrCQ,IAAoBV,KAAe,CAACD,GACpCY,IAAcD,KAAqB,CAAC,CAACJ,GACrCM,IAAgBD,IAAc,IAAInB,CAAc,KAAK;AAE3D,MAAIqB;AACJ,EAAIT,MAAS,cAGTS,IAAeF,IAAc,GAAGtB,CAAgB,GAAGuB,CAAa,KAAKvB,IAC7DW,IAEDF,IACPe,IAAeH,IACT,GAAGD,EAAa,QAAQ,GAAGG,CAAa,KACxCH,EAAa,WACZC,IACPG,IAAe,GAAGJ,EAAa,IAAI,IAAIA,EAAa,KAAK,GAAGG,CAAa,KAEzEC,IAAeJ,EAAa,OAR5BI,IAAetB;AAWnB,QAAMuB,IAAkBf,IAAW,sDAAsD,IAEnFgB,IAAS,GAAG7B,CAAY,IAAIC,EAAcgB,CAAK,CAAC,IAAII,EAAU,IAAI,IAAIC,CAAO,IAAIpB,EAAoBgB,CAAI,CAAC,IAAIS,CAAY,IAAIC,CAAe,IAAIjB,CAAS,IAE1JmB,IAAWpB,IACb,gBAAAqB,EAAC,UAAK,WAAU,yHACX,aACL,IACA,MAEEC,IAAeb,IACjB,gBAAAY,EAAC,UAAK,WAAU,oDAAoD,aAAa,IACjF;AAEJ,SAAIN,IAEI,gBAAAQ,EAAC,UAAA,EAAO,MAAK,UAAS,WAAW,GAAGC,CAAU,IAAIL,CAAM,IAAI,SAAAT,GACvD,UAAA;AAAA,IAAAU;AAAA,IACArB;AAAA,IACAuB;AAAA,EAAA,GACL,IAKJ,gBAAAC,EAAC,OAAA,EAAI,WAAWJ,GACX,UAAA;AAAA,IAAAC;AAAA,IACArB;AAAA,IACAuB;AAAA,EAAA,GACL;AAER;"}
|
|
1
|
+
{"version":3,"file":"PillButton.js","sources":["../../src/components/PillButton.tsx"],"sourcesContent":["import React from 'react';\nimport { FOCUS_RING } from './focusRing';\nimport { resolveRel } from './linkRel';\nimport { safeHref } from './safeHref';\n\ntype PillButtonSize = 'xsmall' | 'small' | 'medium';\ntype PillButtonVariant = 'default' | 'inverted';\n/**\n * `tab` squares the top corners so the pill reads as attached to whatever sits above it —\n * a row of controls hanging under an input, rather than free-floating pills.\n */\ntype PillButtonShape = 'pill' | 'tab';\n/**\n * `assistant` is the chrome for AI-authored chips (e.g. `ChangeReview.Jump`): a fixed\n * violet-on-white look with its own border, independent of `variant`/`selected`.\n */\ntype PillButtonTone = 'default' | 'assistant';\n\ninterface PillButtonProps {\n label: string;\n icon?: React.ReactNode;\n className?: string;\n selected?: boolean;\n disabled?: boolean;\n interactive?: boolean;\n size?: PillButtonSize;\n variant?: PillButtonVariant;\n shape?: PillButtonShape;\n tone?: PillButtonTone;\n /** Sits after the label — a caret on a pill that opens a menu. */\n trailingIcon?: React.ReactNode;\n onClick?: (event: React.MouseEvent<HTMLElement>) => void;\n /** Renders the pill as a real anchor, so a link pill keeps cmd-click, copy-link and the status bar. */\n href?: string;\n target?: React.HTMLAttributeAnchorTarget;\n rel?: string;\n}\n\nconst BASE_CLASSES =\n 'inline-flex items-center justify-center gap-1.5 font-medium whitespace-nowrap shrink-0 transition-colors duration-150 ease-in-out';\n\nconst SHAPE_CLASSES: Record<PillButtonShape, string> = {\n pill: 'rounded-[var(--malachite-radius-10,10px)]',\n tab: 'rounded-b-[var(--malachite-radius-10,10px)] rounded-t-none',\n};\n\n// Box-sizing is border-box, so trading `border-0` for a 1px border only eats into the padding\n// box — it never grows the element past the `h-[...]` each size already fixes. Only one of these\n// ever applies per render, so there's no `border-0`/`border` class collision to referee.\nconst TONE_BORDER_CLASSES: Record<PillButtonTone, string> = {\n default: 'border-0',\n assistant: 'border border-solid border-[var(--malachite-tag-assistant-border)]',\n};\n\nconst ASSISTANT_COLORS =\n 'bg-[var(--malachite-card-background)] text-[var(--malachite-assistant-foreground)]';\n\nconst SIZE_CLASSES: Record<\n PillButtonSize,\n { text: string; padding: string; paddingWithIcon: string }\n> = {\n // For a row that labels other content rather than being read on its own — a context\n // strip above a chat, say. The only size that drops below the 13px the others share.\n xsmall: {\n text: 'text-[length:var(--malachite-font-size-xsmall,11px)] h-[22px] leading-none',\n padding: 'px-2',\n paddingWithIcon: 'pl-1 pr-1.5',\n },\n small: {\n text: 'text-[length:var(--malachite-font-size-small,13px)] h-[var(--malachite-control-height-small,28px)] leading-none',\n padding: 'px-2.5',\n paddingWithIcon: 'pl-1.5 pr-2',\n },\n medium: {\n text: 'text-[length:var(--malachite-font-size-small,13px)] h-[var(--malachite-control-height-medium,32px)] leading-none',\n padding: 'px-2.5',\n paddingWithIcon: 'pl-1.5 pr-2',\n },\n};\n\nconst STATIC_COLORS =\n 'bg-[var(--malachite-pillbutton-static-background)] text-[var(--malachite-pillbutton-static-foreground)]';\n\nconst CLICKABLE_TAIL = 'active:opacity-60 cursor-pointer';\n\nconst VARIANT_COLORS: Record<PillButtonVariant, { base: string; selected: string; hover: string }> =\n {\n default: {\n base: 'bg-[var(--malachite-pillbutton-background)] text-[var(--malachite-pillbutton-foreground)]',\n selected:\n 'bg-[var(--malachite-pillbutton-hover-background)] text-[var(--malachite-pillbutton-hover-foreground)]',\n hover: 'hover:bg-[var(--malachite-pillbutton-hover-background)] hover:text-[var(--malachite-pillbutton-hover-foreground)] active:bg-[var(--malachite-pillbutton-pressed-background)] active:text-[var(--malachite-pillbutton-pressed-foreground)]',\n },\n inverted: {\n base: 'bg-[var(--malachite-pillbutton-inverted-background)] text-[var(--malachite-pillbutton-inverted-foreground)]',\n selected:\n 'bg-[var(--malachite-pillbutton-inverted-hover-background)] text-[var(--malachite-pillbutton-inverted-hover-foreground)]',\n hover: 'hover:bg-[var(--malachite-pillbutton-inverted-hover-background)] hover:text-[var(--malachite-pillbutton-inverted-hover-foreground)] active:bg-[var(--malachite-pillbutton-inverted-pressed-background)] active:text-[var(--malachite-pillbutton-inverted-pressed-foreground)]',\n },\n };\n\nexport const PillButton: React.FC<PillButtonProps> = ({\n label = 'Label',\n icon,\n className = '',\n selected = false,\n disabled = false,\n interactive = true,\n size = 'small',\n variant = 'default',\n shape = 'pill',\n tone = 'default',\n trailingIcon,\n onClick,\n href,\n target,\n rel,\n}) => {\n const sizeBlock = SIZE_CLASSES[size];\n const padding = icon ? sizeBlock.paddingWithIcon : sizeBlock.padding;\n const variantBlock = VARIANT_COLORS[variant];\n const isInteractiveLook = interactive && !disabled;\n // A rejected href (e.g. `javascript:`) takes the button or static path, never an anchor.\n const linkHref = safeHref(href);\n const isClickable = isInteractiveLook && (!!onClick || !!linkHref);\n const clickableTail = isClickable ? ` ${CLICKABLE_TAIL}` : '';\n\n let colorClasses: string;\n if (tone === 'assistant') {\n // Assistant chips read the same in every interaction state — the click affordance is\n // the shared `active:opacity-60` from `clickableTail`, not a colour swap.\n colorClasses = isClickable ? `${ASSISTANT_COLORS}${clickableTail}` : ASSISTANT_COLORS;\n } else if (!interactive) {\n colorClasses = STATIC_COLORS;\n } else if (selected) {\n colorClasses = isInteractiveLook\n ? `${variantBlock.selected}${clickableTail}`\n : variantBlock.selected;\n } else if (isInteractiveLook) {\n colorClasses = `${variantBlock.base} ${variantBlock.hover}${clickableTail}`;\n } else {\n colorClasses = variantBlock.base;\n }\n\n const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : '';\n\n const merged = `${BASE_CLASSES} ${SHAPE_CLASSES[shape]} ${sizeBlock.text} ${padding} ${TONE_BORDER_CLASSES[tone]} ${colorClasses} ${disabledClasses} ${className}`;\n\n const iconSlot = icon ? (\n <span className=\"inline-flex items-center justify-center w-4 h-4 shrink-0 [&>img]:max-w-full [&>img]:max-h-full [&>img]:object-contain\">\n {icon}\n </span>\n ) : null;\n\n const trailingSlot = trailingIcon ? (\n <span className=\"inline-flex shrink-0 items-center justify-center\">{trailingIcon}</span>\n ) : null;\n\n // An anchor cannot be disabled, so a disabled link pill falls through to the static one.\n if (isClickable && linkHref) {\n return (\n <a\n href={linkHref}\n target={target}\n rel={resolveRel(rel, target)}\n className={`${FOCUS_RING} ${merged} no-underline`}\n onClick={onClick}\n >\n {iconSlot}\n {label}\n {trailingSlot}\n </a>\n );\n }\n\n if (isClickable) {\n return (\n <button type=\"button\" className={`${FOCUS_RING} ${merged}`} onClick={onClick}>\n {iconSlot}\n {label}\n {trailingSlot}\n </button>\n );\n }\n\n return (\n <div className={merged}>\n {iconSlot}\n {label}\n {trailingSlot}\n </div>\n );\n};\n\nexport default PillButton;\n"],"names":["BASE_CLASSES","SHAPE_CLASSES","TONE_BORDER_CLASSES","ASSISTANT_COLORS","SIZE_CLASSES","STATIC_COLORS","CLICKABLE_TAIL","VARIANT_COLORS","PillButton","label","icon","className","selected","disabled","interactive","size","variant","shape","tone","trailingIcon","onClick","href","target","rel","sizeBlock","padding","variantBlock","isInteractiveLook","linkHref","safeHref","isClickable","clickableTail","colorClasses","disabledClasses","merged","iconSlot","jsx","trailingSlot","jsxs","resolveRel","FOCUS_RING"],"mappings":";;;;AAsCA,MAAMA,IACF,qIAEEC,IAAiD;AAAA,EACnD,MAAM;AAAA,EACN,KAAK;AACT,GAKMC,IAAsD;AAAA,EACxD,SAAS;AAAA,EACT,WAAW;AACf,GAEMC,IACF,sFAEEC,IAGF;AAAA;AAAA;AAAA,EAGA,QAAQ;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,iBAAiB;AAAA,EAAA;AAAA,EAErB,OAAO;AAAA,IACH,MAAM;AAAA,IACN,SAAS;AAAA,IACT,iBAAiB;AAAA,EAAA;AAAA,EAErB,QAAQ;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,iBAAiB;AAAA,EAAA;AAEzB,GAEMC,IACF,2GAEEC,IAAiB,oCAEjBC,IACF;AAAA,EACI,SAAS;AAAA,IACL,MAAM;AAAA,IACN,UACI;AAAA,IACJ,OAAO;AAAA,EAAA;AAAA,EAEX,UAAU;AAAA,IACN,MAAM;AAAA,IACN,UACI;AAAA,IACJ,OAAO;AAAA,EAAA;AAEf,GAESC,IAAwC,CAAC;AAAA,EAClD,OAAAC,IAAQ;AAAA,EACR,MAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,UAAAC,IAAW;AAAA,EACX,UAAAC,IAAW;AAAA,EACX,aAAAC,IAAc;AAAA,EACd,MAAAC,IAAO;AAAA,EACP,SAAAC,IAAU;AAAA,EACV,OAAAC,IAAQ;AAAA,EACR,MAAAC,IAAO;AAAA,EACP,cAAAC;AAAA,EACA,SAAAC;AAAA,EACA,MAAAC;AAAA,EACA,QAAAC;AAAA,EACA,KAAAC;AACJ,MAAM;AACF,QAAMC,IAAYpB,EAAaW,CAAI,GAC7BU,IAAUf,IAAOc,EAAU,kBAAkBA,EAAU,SACvDE,IAAenB,EAAeS,CAAO,GACrCW,IAAoBb,KAAe,CAACD,GAEpCe,IAAWC,EAASR,CAAI,GACxBS,IAAcH,MAAsB,CAAC,CAACP,KAAW,CAAC,CAACQ,IACnDG,IAAgBD,IAAc,IAAIxB,CAAc,KAAK;AAE3D,MAAI0B;AACJ,EAAId,MAAS,cAGTc,IAAeF,IAAc,GAAG3B,CAAgB,GAAG4B,CAAa,KAAK5B,IAC7DW,IAEDF,IACPoB,IAAeL,IACT,GAAGD,EAAa,QAAQ,GAAGK,CAAa,KACxCL,EAAa,WACZC,IACPK,IAAe,GAAGN,EAAa,IAAI,IAAIA,EAAa,KAAK,GAAGK,CAAa,KAEzEC,IAAeN,EAAa,OAR5BM,IAAe3B;AAWnB,QAAM4B,IAAkBpB,IAAW,sDAAsD,IAEnFqB,IAAS,GAAGlC,CAAY,IAAIC,EAAcgB,CAAK,CAAC,IAAIO,EAAU,IAAI,IAAIC,CAAO,IAAIvB,EAAoBgB,CAAI,CAAC,IAAIc,CAAY,IAAIC,CAAe,IAAItB,CAAS,IAE1JwB,IAAWzB,IACb,gBAAA0B,EAAC,UAAK,WAAU,yHACX,aACL,IACA,MAEEC,IAAelB,IACjB,gBAAAiB,EAAC,UAAK,WAAU,oDAAoD,aAAa,IACjF;AAGJ,SAAIN,KAAeF,IAEX,gBAAAU;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,MAAMV;AAAA,MACN,QAAAN;AAAA,MACA,KAAKiB,EAAWhB,GAAKD,CAAM;AAAA,MAC3B,WAAW,GAAGkB,CAAU,IAAIN,CAAM;AAAA,MAClC,SAAAd;AAAA,MAEC,UAAA;AAAA,QAAAe;AAAA,QACA1B;AAAA,QACA4B;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,IAKTP,IAEI,gBAAAQ,EAAC,UAAA,EAAO,MAAK,UAAS,WAAW,GAAGE,CAAU,IAAIN,CAAM,IAAI,SAAAd,GACvD,UAAA;AAAA,IAAAe;AAAA,IACA1B;AAAA,IACA4B;AAAA,EAAA,GACL,IAKJ,gBAAAC,EAAC,OAAA,EAAI,WAAWJ,GACX,UAAA;AAAA,IAAAC;AAAA,IACA1B;AAAA,IACA4B;AAAA,EAAA,GACL;AAER;"}
|