@helloimjolopez-pathway/pathway-components 0.3.2 → 0.5.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/bottom-sheet/bottom-sheet-demo.jsx +145 -0
- package/components/bottom-sheet/bottom-sheet.jsx +433 -0
- package/components/checkbox/checkbox.jsx +392 -0
- package/components/dropdown/dropdown.jsx +482 -0
- package/components/scroll-to-enable/scroll-to-enable-demo.jsx +125 -0
- package/components/scroll-to-enable/scroll-to-enable.jsx +500 -0
- package/package.json +5 -2
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkbox, Pathway Design System
|
|
3
|
+
*
|
|
4
|
+
* Importable React component module. Source of truth for the Checkbox
|
|
5
|
+
* implementation; the standalone demo (checkbox.html) and the Storybook
|
|
6
|
+
* stories both consume this.
|
|
7
|
+
*
|
|
8
|
+
* Spec: components/checkbox/checkbox-spec.md
|
|
9
|
+
* Figma: https://www.figma.com/design/3sw45aVcngFAmpbP6cfrXP/?node-id=40002324-54532
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import React, { useRef, useEffect, useState } from "react";
|
|
13
|
+
import { useReducedMotion } from "../_shared/use-reduced-motion.jsx";
|
|
14
|
+
|
|
15
|
+
// ─── SEMANTIC TOKEN CSS VARIABLES ─────────────────────────────────────────────
|
|
16
|
+
// All colours and radii come from semantic tokens only. No raw hex, no
|
|
17
|
+
// primitive vars. See checkbox-spec.md §5 for the full token table.
|
|
18
|
+
//
|
|
19
|
+
// Note: some tokens in §5.3 of the spec are missing from the token file.
|
|
20
|
+
// Fallbacks are used where noted and will be updated once Figma is re-synced.
|
|
21
|
+
const V = {
|
|
22
|
+
// Box fill, standard checked / indeterminate
|
|
23
|
+
fillCheckedBase: "var(--semantic-color-fill-action-primary-strong-rest)",
|
|
24
|
+
fillCheckedHover: "var(--semantic-color-fill-action-primary-strong-hover)",
|
|
25
|
+
fillCheckedFocused: "var(--semantic-color-fill-action-primary-strong-hover)",
|
|
26
|
+
fillCheckedPressed: "var(--semantic-color-fill-action-primary-strong-pressed)",
|
|
27
|
+
fillCheckedDisabled: "var(--semantic-color-fill-action-disabled)",
|
|
28
|
+
|
|
29
|
+
// Box border, standard unchecked
|
|
30
|
+
borderBase: "var(--semantic-color-stroke-action-secondary-rest)",
|
|
31
|
+
borderHover: "var(--semantic-color-stroke-action-secondary-hover)",
|
|
32
|
+
borderFocused: "var(--semantic-color-stroke-action-secondary-hover)",
|
|
33
|
+
borderPressed: "var(--semantic-color-stroke-action-secondary-pressed)",
|
|
34
|
+
borderDisabled: "var(--semantic-color-stroke-action-disabled)",
|
|
35
|
+
|
|
36
|
+
// Checkmark / dash icon, standard.
|
|
37
|
+
//
|
|
38
|
+
// MUST be an ON-STRONG foreground, because the box it sits in is filled with
|
|
39
|
+
// fill-action-primary-strong-*. This was foreground-action-primary-ON-SUBTLE-rest,
|
|
40
|
+
// which is #345499 sitting on #3a5aaa: the same blue, so the whole square
|
|
41
|
+
// read as a solid blue block with no visible tick. Reported 2026-10-02.
|
|
42
|
+
//
|
|
43
|
+
// Figma binds Foreground/Static/Neutral/MONO here instead, which is correct
|
|
44
|
+
// in Light (#ffffff) and wrong in Midnight: mono is an INVERSION ANCHOR, not
|
|
45
|
+
// a rung, so it flips to #181b2b and would put a near-black tick on a blue
|
|
46
|
+
// box. on-strong is #ffffff in both modes, which is what a foreground on a
|
|
47
|
+
// strong fill is for. Recorded in the manifest as a Figma defect.
|
|
48
|
+
iconPrimary: "var(--semantic-color-foreground-action-primary-on-strong)",
|
|
49
|
+
|
|
50
|
+
// Checkmark / dash icon: error. Same fix: the error box is filled with
|
|
51
|
+
// fill-action-status-negative-strong-*, so the tick needs the on-strong
|
|
52
|
+
// foreground. Was on-subtle-rest: #722121 on #b03a3a.
|
|
53
|
+
iconError: "var(--semantic-color-foreground-action-status-negative-on-strong)",
|
|
54
|
+
|
|
55
|
+
// State-layer, unchecked hover / focused
|
|
56
|
+
stateLayerUncheckedHover: "var(--semantic-color-fill-action-secondary-hover)",
|
|
57
|
+
|
|
58
|
+
// State-layer, checked hover / focused / pressed
|
|
59
|
+
stateLayerCheckedHover: "var(--semantic-color-fill-action-primary-subtle-hover)",
|
|
60
|
+
stateLayerCheckedFocused: "var(--semantic-color-fill-action-primary-subtle-hover)",
|
|
61
|
+
stateLayerCheckedPressed: "var(--semantic-color-fill-action-primary-subtle-pressed)",
|
|
62
|
+
|
|
63
|
+
// Box fill: error checked / indeterminate
|
|
64
|
+
fillErrorBase: "var(--semantic-color-fill-action-status-negative-strong-rest)",
|
|
65
|
+
fillErrorHover: "var(--semantic-color-fill-action-status-negative-strong-hover)",
|
|
66
|
+
fillErrorFocused: "var(--semantic-color-fill-action-status-negative-strong-hover)",
|
|
67
|
+
fillErrorPressed: "var(--semantic-color-fill-action-status-negative-strong-pressed)",
|
|
68
|
+
fillErrorDisabled: "var(--semantic-color-fill-action-disabled)",
|
|
69
|
+
|
|
70
|
+
// Box border: error unchecked
|
|
71
|
+
borderErrorBase: "var(--semantic-color-stroke-action-status-negative-rest)",
|
|
72
|
+
borderErrorHover: "var(--semantic-color-stroke-action-status-negative-hover)",
|
|
73
|
+
borderErrorFocused: "var(--semantic-color-stroke-action-status-negative-hover)",
|
|
74
|
+
borderErrorPressed: "var(--semantic-color-stroke-action-status-negative-pressed)",
|
|
75
|
+
borderErrorDisabled: "var(--semantic-color-stroke-action-disabled)",
|
|
76
|
+
|
|
77
|
+
// State-layer: error hover (both unchecked and checked)
|
|
78
|
+
stateLayerErrorHover: "var(--semantic-color-fill-action-status-negative-subtle-hover)",
|
|
79
|
+
|
|
80
|
+
// Highlight.
|
|
81
|
+
//
|
|
82
|
+
// THIS IS NOT A RESTING TINT. It used to be one here, and that was wrong in
|
|
83
|
+
// both directions: it painted a background the Figma variant does not have,
|
|
84
|
+
// and it did nothing on hover, which is the only place the variant actually
|
|
85
|
+
// differs. Reported 2026-10-02.
|
|
86
|
+
//
|
|
87
|
+
// Figma has three Highlight variants, all Unselected:
|
|
88
|
+
// Type=Highlight Unselected, State=Active no state layer at all
|
|
89
|
+
// Type=Highlight Hovered, State=Hovered state layer #e2e9f7
|
|
90
|
+
// Type=Hhighlight Focused, State=Focused state layer #e2e9f7 (sic)
|
|
91
|
+
// The box fill and both border colours are IDENTICAL to the standard
|
|
92
|
+
// unselected variants (#77726b rest, #67625c hover). So the entire meaning of
|
|
93
|
+
// Highlight is one swap: the unchecked hover/focus state layer goes from the
|
|
94
|
+
// warm grey Fill/Action/Secondary/Hover (#f3f0ec) to the pale blue
|
|
95
|
+
// Fill/Action/Primary/Subtle/Hover (#e2e9f7). It marks a row as brand-live on
|
|
96
|
+
// interaction; it does not mark it at rest.
|
|
97
|
+
stateLayerHighlightHover: "var(--semantic-color-fill-action-primary-subtle-hover)",
|
|
98
|
+
|
|
99
|
+
// Label text
|
|
100
|
+
labelColor: "var(--semantic-color-foreground-static-neutral-base)",
|
|
101
|
+
|
|
102
|
+
// Geometry
|
|
103
|
+
// cornerradius.small = 4px, matches the 4px box radius in the Figma spec.
|
|
104
|
+
// (cornerradius.xsmall = 2px which is too small; small is the correct value.)
|
|
105
|
+
//
|
|
106
|
+
// NOT wrapped in calc(... * 1px). The layout tokens already carry their unit
|
|
107
|
+
// (`--semantic-layout-units-cornerradius-small: 4px`), so multiplying by 1px
|
|
108
|
+
// produced calc(4px * 1px): px times px is not a length, so the browser
|
|
109
|
+
// dropped the declaration and the box rendered with square corners. Silent,
|
|
110
|
+
// because an invalid value falls back to the initial one rather than erroring.
|
|
111
|
+
// Style Dictionary emits layout tokens as unitless numbers, so we multiply
|
|
112
|
+
// by 1px inside calc() to get a valid CSS length.
|
|
113
|
+
radius: "var(--semantic-layout-units-cornerradius-small)",
|
|
114
|
+
radiusFull: "100px", // state-layer pill: no dedicated token, 100px safely rounds any 44px circle
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// ─── GEOMETRY ─────────────────────────────────────────────────────────────────
|
|
118
|
+
export const BOX_SIZE = { default: 18, s: 16 };
|
|
119
|
+
export const TARGET_SIZE = 44; // touch target, always 44px (WCAG 2.5.5)
|
|
120
|
+
export const BORDER_WIDTH = 1.5; // px
|
|
121
|
+
export const LABEL_GAP = 16; // px, gap between state-layer and label text
|
|
122
|
+
|
|
123
|
+
// ─── ICONS ────────────────────────────────────────────────────────────────────
|
|
124
|
+
// Both icons use the same 18×18 viewBox so they scale cleanly when size="s".
|
|
125
|
+
// Paths are centred in the viewBox to match Figma geometry.
|
|
126
|
+
|
|
127
|
+
export function IconCheck({ color, size }) {
|
|
128
|
+
return (
|
|
129
|
+
<svg
|
|
130
|
+
width={size} height={size} viewBox="0 0 18 18"
|
|
131
|
+
fill="none" aria-hidden="true" focusable="false"
|
|
132
|
+
>
|
|
133
|
+
<path
|
|
134
|
+
d="M4 9.5L7.5 13L14 6"
|
|
135
|
+
stroke={color}
|
|
136
|
+
strokeWidth={BORDER_WIDTH}
|
|
137
|
+
strokeLinecap="round"
|
|
138
|
+
strokeLinejoin="round"
|
|
139
|
+
/>
|
|
140
|
+
</svg>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function IconDash({ color, size }) {
|
|
145
|
+
return (
|
|
146
|
+
<svg
|
|
147
|
+
width={size} height={size} viewBox="0 0 18 18"
|
|
148
|
+
fill="none" aria-hidden="true" focusable="false"
|
|
149
|
+
>
|
|
150
|
+
<path
|
|
151
|
+
d="M5 9H13"
|
|
152
|
+
stroke={color}
|
|
153
|
+
strokeWidth={BORDER_WIDTH}
|
|
154
|
+
strokeLinecap="round"
|
|
155
|
+
/>
|
|
156
|
+
</svg>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── CHECKBOX ─────────────────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Checkbox
|
|
164
|
+
*
|
|
165
|
+
* @param {boolean} checked - Whether the checkbox is checked
|
|
166
|
+
* @param {boolean} indeterminate - Shows dash icon; overrides checked visually
|
|
167
|
+
* @param {boolean} error - Error / negative styling
|
|
168
|
+
* @param {boolean} highlight - Brand-tinted hover/focus state-layer for selectable
|
|
169
|
+
* rows. Unchecked only, and no effect at rest.
|
|
170
|
+
* @param {boolean} secondary - Secondary indeterminate (muted colour), use with indeterminate only
|
|
171
|
+
* @param {boolean} disabled - Non-interactive
|
|
172
|
+
* @param {"default"|"s"} size - Visual size of the control
|
|
173
|
+
* @param {string} label - Optional visible label
|
|
174
|
+
* @param {function} onChange - (checked: boolean) => void
|
|
175
|
+
* @param {string} id - For <label htmlFor> association
|
|
176
|
+
* @param {string} name - Form field name
|
|
177
|
+
* @param {string} value - Form field value
|
|
178
|
+
* @param {string} className - Additional CSS class on the root element
|
|
179
|
+
* @param {string} [describedBy] - aria-describedby for error messages (error state)
|
|
180
|
+
*/
|
|
181
|
+
export function Checkbox({
|
|
182
|
+
checked = false,
|
|
183
|
+
indeterminate = false,
|
|
184
|
+
error = false,
|
|
185
|
+
highlight = false,
|
|
186
|
+
secondary = false,
|
|
187
|
+
disabled = false,
|
|
188
|
+
size = "default",
|
|
189
|
+
label,
|
|
190
|
+
onChange,
|
|
191
|
+
id,
|
|
192
|
+
name,
|
|
193
|
+
value,
|
|
194
|
+
className = "",
|
|
195
|
+
describedBy,
|
|
196
|
+
}) {
|
|
197
|
+
const noMotion = useReducedMotion();
|
|
198
|
+
const inputRef = useRef(null);
|
|
199
|
+
const [hovered, setHovered] = useState(false);
|
|
200
|
+
const [focused, setFocused] = useState(false);
|
|
201
|
+
const [pressed, setPressed] = useState(false);
|
|
202
|
+
|
|
203
|
+
// The indeterminate state can only be set on the DOM element: not via HTML.
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (inputRef.current) {
|
|
206
|
+
inputRef.current.indeterminate = indeterminate;
|
|
207
|
+
}
|
|
208
|
+
}, [indeterminate]);
|
|
209
|
+
|
|
210
|
+
const boxSize = BOX_SIZE[size] || BOX_SIZE.default;
|
|
211
|
+
|
|
212
|
+
// ─── Derived box fill ──────────────────────────────────────────────────────
|
|
213
|
+
let boxFill = "transparent";
|
|
214
|
+
if (disabled && (checked || indeterminate)) {
|
|
215
|
+
boxFill = error ? V.fillErrorDisabled : V.fillCheckedDisabled;
|
|
216
|
+
} else if (!disabled && (checked || indeterminate)) {
|
|
217
|
+
if (error) {
|
|
218
|
+
boxFill = pressed ? V.fillErrorPressed
|
|
219
|
+
: focused ? V.fillErrorFocused
|
|
220
|
+
: hovered ? V.fillErrorHover
|
|
221
|
+
: V.fillErrorBase;
|
|
222
|
+
} else {
|
|
223
|
+
boxFill = pressed ? V.fillCheckedPressed
|
|
224
|
+
: focused ? V.fillCheckedFocused
|
|
225
|
+
: hovered ? V.fillCheckedHover
|
|
226
|
+
: V.fillCheckedBase;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ─── Derived box border colour ─────────────────────────────────────────────
|
|
231
|
+
// When checked or indeterminate, border matches fill (visually absorbed).
|
|
232
|
+
// When unchecked, border shows the action.secondary (or negative) stroke token.
|
|
233
|
+
let borderColor;
|
|
234
|
+
if (checked || indeterminate) {
|
|
235
|
+
borderColor = boxFill; // border hidden under fill
|
|
236
|
+
} else if (disabled) {
|
|
237
|
+
borderColor = error ? V.borderErrorDisabled : V.borderDisabled;
|
|
238
|
+
} else if (error) {
|
|
239
|
+
borderColor = pressed ? V.borderErrorPressed
|
|
240
|
+
: focused ? V.borderErrorFocused
|
|
241
|
+
: hovered ? V.borderErrorHover
|
|
242
|
+
: V.borderErrorBase;
|
|
243
|
+
} else {
|
|
244
|
+
borderColor = pressed ? V.borderPressed
|
|
245
|
+
: focused ? V.borderFocused
|
|
246
|
+
: hovered ? V.borderHover
|
|
247
|
+
: V.borderBase;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ─── Derived state-layer background ───────────────────────────────────────
|
|
251
|
+
let stateLayerBg = "transparent";
|
|
252
|
+
if (!disabled) {
|
|
253
|
+
if (pressed) {
|
|
254
|
+
// pressed: same tint as hover: no separate pressed state-layer in spec
|
|
255
|
+
stateLayerBg = (checked || indeterminate)
|
|
256
|
+
? V.stateLayerCheckedPressed
|
|
257
|
+
: (error ? V.stateLayerErrorHover : V.stateLayerUncheckedHover);
|
|
258
|
+
} else if (hovered || focused) {
|
|
259
|
+
if (checked || indeterminate) {
|
|
260
|
+
stateLayerBg = error
|
|
261
|
+
? V.stateLayerErrorHover
|
|
262
|
+
: (focused ? V.stateLayerCheckedFocused : V.stateLayerCheckedHover);
|
|
263
|
+
} else {
|
|
264
|
+
// Unchecked hover/focus. Highlight swaps the warm grey layer for the
|
|
265
|
+
// pale blue one; error still wins over both. See V.stateLayerHighlightHover.
|
|
266
|
+
stateLayerBg = error
|
|
267
|
+
? V.stateLayerErrorHover
|
|
268
|
+
: (highlight ? V.stateLayerHighlightHover : V.stateLayerUncheckedHover);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// No `else if (highlight)` branch: Figma's highlight variant has no resting
|
|
272
|
+
// state layer, so at rest a highlighted checkbox is indistinguishable from
|
|
273
|
+
// a standard one. That is the design.
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ─── Focus ring ───────────────────────────────────────────────────────────
|
|
277
|
+
// Error: spec defines a 3px rgba red shadow on the state-layer.
|
|
278
|
+
// Standard: border colour change on the box is sufficient (above).
|
|
279
|
+
const focusShadow = focused && error
|
|
280
|
+
? "0 0 0 3px var(--semantic-color-fill-static-negative-subtle)"
|
|
281
|
+
// Was a hand-mixed red at 10 percent. Fill/Static/Negative/Subtle is the
|
|
282
|
+
// tone's own pale tint and it inverts with the theme, so the ring stays
|
|
283
|
+
// visible on a dark canvas instead of vanishing into it.
|
|
284
|
+
: "none";
|
|
285
|
+
|
|
286
|
+
// ─── Icon colour ──────────────────────────────────────────────────────────
|
|
287
|
+
const iconColor = error ? V.iconError : V.iconPrimary;
|
|
288
|
+
const showIcon = checked || indeterminate;
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
<label
|
|
292
|
+
className={`pds-checkbox${size === "s" ? " pds-checkbox--s" : ""}${error ? " pds-checkbox--error" : ""}${highlight ? " pds-checkbox--highlight" : ""}${disabled ? " pds-checkbox--disabled" : ""}${className ? ` ${className}` : ""}`}
|
|
293
|
+
style={{
|
|
294
|
+
display: "inline-flex",
|
|
295
|
+
alignItems: "center",
|
|
296
|
+
gap: label ? LABEL_GAP : 0,
|
|
297
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
298
|
+
userSelect: "none",
|
|
299
|
+
fontFamily: "'Red Hat Text', sans-serif",
|
|
300
|
+
}}
|
|
301
|
+
onMouseEnter={() => !disabled && setHovered(true)}
|
|
302
|
+
onMouseLeave={() => { setHovered(false); setPressed(false); }}
|
|
303
|
+
onMouseDown={() => !disabled && setPressed(true)}
|
|
304
|
+
onMouseUp={() => setPressed(false)}
|
|
305
|
+
>
|
|
306
|
+
{/* 44×44 touch target / state-layer */}
|
|
307
|
+
<span
|
|
308
|
+
aria-hidden="true"
|
|
309
|
+
style={{
|
|
310
|
+
position: "relative",
|
|
311
|
+
display: "inline-flex",
|
|
312
|
+
alignItems: "center",
|
|
313
|
+
justifyContent: "center",
|
|
314
|
+
width: TARGET_SIZE,
|
|
315
|
+
height: TARGET_SIZE,
|
|
316
|
+
borderRadius: V.radiusFull,
|
|
317
|
+
backgroundColor: stateLayerBg,
|
|
318
|
+
transition: noMotion ? "none" : "background-color var(--motion-duration-3) var(--motion-easing-standard)",
|
|
319
|
+
boxShadow: focusShadow,
|
|
320
|
+
flexShrink: 0,
|
|
321
|
+
}}
|
|
322
|
+
>
|
|
323
|
+
{/* Visually hidden native input, provides semantics and keyboard behaviour */}
|
|
324
|
+
<input
|
|
325
|
+
ref={inputRef}
|
|
326
|
+
type="checkbox"
|
|
327
|
+
id={id}
|
|
328
|
+
name={name}
|
|
329
|
+
value={value}
|
|
330
|
+
checked={indeterminate ? false : checked}
|
|
331
|
+
disabled={disabled}
|
|
332
|
+
aria-describedby={describedBy}
|
|
333
|
+
onChange={e => onChange && onChange(e.target.checked)}
|
|
334
|
+
onFocus={() => setFocused(true)}
|
|
335
|
+
onBlur={() => setFocused(false)}
|
|
336
|
+
style={{
|
|
337
|
+
position: "absolute",
|
|
338
|
+
width: boxSize,
|
|
339
|
+
height: boxSize,
|
|
340
|
+
opacity: 0,
|
|
341
|
+
margin: 0,
|
|
342
|
+
padding: 0,
|
|
343
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
344
|
+
zIndex: 1,
|
|
345
|
+
}}
|
|
346
|
+
/>
|
|
347
|
+
|
|
348
|
+
{/* Visual checkbox box */}
|
|
349
|
+
<span
|
|
350
|
+
aria-hidden="true"
|
|
351
|
+
style={{
|
|
352
|
+
display: "inline-flex",
|
|
353
|
+
alignItems: "center",
|
|
354
|
+
justifyContent: "center",
|
|
355
|
+
width: boxSize,
|
|
356
|
+
height: boxSize,
|
|
357
|
+
borderRadius: V.radius,
|
|
358
|
+
border: `${BORDER_WIDTH}px solid ${borderColor}`,
|
|
359
|
+
backgroundColor: boxFill,
|
|
360
|
+
boxSizing: "border-box",
|
|
361
|
+
transition: noMotion ? "none" : "background-color var(--motion-duration-2) var(--motion-easing-decelerate), border-color var(--motion-duration-2) var(--motion-easing-decelerate)",
|
|
362
|
+
flexShrink: 0,
|
|
363
|
+
}}
|
|
364
|
+
>
|
|
365
|
+
{showIcon && (
|
|
366
|
+
indeterminate
|
|
367
|
+
? <IconDash color={iconColor} size={boxSize} />
|
|
368
|
+
: <IconCheck color={iconColor} size={boxSize} />
|
|
369
|
+
)}
|
|
370
|
+
</span>
|
|
371
|
+
</span>
|
|
372
|
+
|
|
373
|
+
{/* Label text */}
|
|
374
|
+
{label && (
|
|
375
|
+
<span
|
|
376
|
+
style={{
|
|
377
|
+
fontSize: "var(--semantic-type-font-size-s)",
|
|
378
|
+
lineHeight: "var(--semantic-type-line-height-s-single)",
|
|
379
|
+
fontWeight: 400,
|
|
380
|
+
color: disabled
|
|
381
|
+
? "var(--semantic-color-foreground-static-neutral-base)"
|
|
382
|
+
: V.labelColor,
|
|
383
|
+
}}
|
|
384
|
+
>
|
|
385
|
+
{label}
|
|
386
|
+
</span>
|
|
387
|
+
)}
|
|
388
|
+
</label>
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export default Checkbox;
|