@varialkit/checkbox 0.1.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/docs.md +31 -0
- package/examples.tsx +110 -0
- package/package.json +24 -0
- package/src/Checkbox.scss +222 -0
- package/src/Checkbox.tsx +138 -0
- package/src/Checkbox.types.ts +43 -0
- package/src/index.ts +2 -0
package/docs.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Checkbox
|
|
2
|
+
|
|
3
|
+
Checkboxes let users toggle a boolean option on or off. Use them for independent choices or multi-select lists.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Checkbox } from "@solara/checkbox";
|
|
9
|
+
|
|
10
|
+
export function Example() {
|
|
11
|
+
return (
|
|
12
|
+
<Checkbox
|
|
13
|
+
label="Email me updates"
|
|
14
|
+
helperText="We send product news twice a month."
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
| --- | --- | --- | --- |
|
|
24
|
+
| `label` | `string` | — | Label text next to the checkbox. |
|
|
25
|
+
| `helperText` | `string` | — | Helper text below the checkbox. |
|
|
26
|
+
| `size` | `"small" | "medium" | "large"` | `"medium"` | Controls checkbox sizing. |
|
|
27
|
+
| `isInvalid` | `boolean` | `false` | Shows an error state. |
|
|
28
|
+
| `isDisabled` | `boolean` | `false` | Disables the checkbox. |
|
|
29
|
+
| `indeterminate` | `boolean` | `false` | Shows a mixed state. |
|
|
30
|
+
|
|
31
|
+
Checkbox also accepts standard input props like `checked`, `defaultChecked`, and `onChange`.
|
package/examples.tsx
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Checkbox } from "./src/Checkbox";
|
|
3
|
+
import type { CheckboxProps } from "./src/Checkbox.types";
|
|
4
|
+
|
|
5
|
+
export const stories = {
|
|
6
|
+
playground: {
|
|
7
|
+
title: "Playground",
|
|
8
|
+
description: "Tweak the props to explore the Checkbox API.",
|
|
9
|
+
render: (props: CheckboxProps & { showLabel?: boolean; showHelper?: boolean }) => {
|
|
10
|
+
const { showLabel = true, showHelper = true, ...checkboxProps } = props;
|
|
11
|
+
const [checked, setChecked] = React.useState<boolean>(
|
|
12
|
+
checkboxProps.checked ?? checkboxProps.defaultChecked ?? false
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
if (checkboxProps.checked !== undefined) {
|
|
17
|
+
setChecked(Boolean(checkboxProps.checked));
|
|
18
|
+
}
|
|
19
|
+
}, [checkboxProps.checked]);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Checkbox
|
|
23
|
+
{...checkboxProps}
|
|
24
|
+
checked={checked}
|
|
25
|
+
onChange={(event) => setChecked(event.currentTarget.checked)}
|
|
26
|
+
label={showLabel ? checkboxProps.label : undefined}
|
|
27
|
+
helperText={showHelper ? checkboxProps.helperText : undefined}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
controls: [
|
|
32
|
+
{ name: "showLabel", type: "boolean", label: "Show Label" },
|
|
33
|
+
{ name: "label", type: "text" },
|
|
34
|
+
{ name: "showHelper", type: "boolean", label: "Show Helper" },
|
|
35
|
+
{ name: "helperText", type: "text" },
|
|
36
|
+
{
|
|
37
|
+
name: "size",
|
|
38
|
+
type: "select",
|
|
39
|
+
options: ["small", "medium", "large"],
|
|
40
|
+
},
|
|
41
|
+
{ name: "checked", type: "boolean" },
|
|
42
|
+
{ name: "indeterminate", type: "boolean" },
|
|
43
|
+
{ name: "isInvalid", type: "boolean" },
|
|
44
|
+
{ name: "isDisabled", type: "boolean" },
|
|
45
|
+
],
|
|
46
|
+
initialProps: {
|
|
47
|
+
showLabel: false,
|
|
48
|
+
label: "Notify me about updates",
|
|
49
|
+
showHelper: false,
|
|
50
|
+
helperText: "You can change this later.",
|
|
51
|
+
size: "medium",
|
|
52
|
+
checked: false,
|
|
53
|
+
indeterminate: false,
|
|
54
|
+
isInvalid: false,
|
|
55
|
+
isDisabled: false,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
states: {
|
|
59
|
+
title: "States",
|
|
60
|
+
description: "Checked, indeterminate, and disabled checkboxes.",
|
|
61
|
+
showProps: false,
|
|
62
|
+
render: () => (
|
|
63
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
|
|
64
|
+
<Checkbox label="Unchecked" />
|
|
65
|
+
<Checkbox label="Checked" defaultChecked />
|
|
66
|
+
<Checkbox label="Indeterminate" indeterminate />
|
|
67
|
+
<Checkbox label="Disabled" isDisabled />
|
|
68
|
+
<Checkbox label="Invalid" isInvalid helperText="Required" />
|
|
69
|
+
</div>
|
|
70
|
+
),
|
|
71
|
+
code: `import { Checkbox } from "@solara/checkbox";
|
|
72
|
+
|
|
73
|
+
export function Example() {
|
|
74
|
+
return (
|
|
75
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
|
|
76
|
+
<Checkbox label="Unchecked" />
|
|
77
|
+
<Checkbox label="Checked" defaultChecked />
|
|
78
|
+
<Checkbox label="Indeterminate" indeterminate />
|
|
79
|
+
<Checkbox label="Disabled" isDisabled />
|
|
80
|
+
<Checkbox label="Invalid" isInvalid helperText="Required" />
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
`,
|
|
85
|
+
},
|
|
86
|
+
sizes: {
|
|
87
|
+
title: "Sizes",
|
|
88
|
+
description: "Checkbox size options.",
|
|
89
|
+
showProps: false,
|
|
90
|
+
render: () => (
|
|
91
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
|
|
92
|
+
<Checkbox label="Small" size="small" />
|
|
93
|
+
<Checkbox label="Medium" size="medium" />
|
|
94
|
+
<Checkbox label="Large" size="large" />
|
|
95
|
+
</div>
|
|
96
|
+
),
|
|
97
|
+
code: `import { Checkbox } from "@solara/checkbox";
|
|
98
|
+
|
|
99
|
+
export function Example() {
|
|
100
|
+
return (
|
|
101
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
|
|
102
|
+
<Checkbox label="Small" size="small" />
|
|
103
|
+
<Checkbox label="Medium" size="medium" />
|
|
104
|
+
<Checkbox label="Large" size="large" />
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
`,
|
|
109
|
+
},
|
|
110
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@varialkit/checkbox",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./examples": "./examples/index.tsx"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src",
|
|
13
|
+
"docs.md",
|
|
14
|
+
"examples",
|
|
15
|
+
"examples.tsx"
|
|
16
|
+
],
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"react": "^19.0.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/react": "19.0.10",
|
|
22
|
+
"react": "19.0.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
.solara-checkbox {
|
|
2
|
+
--checkbox-size: 18px;
|
|
3
|
+
--checkbox-font-size: var(--font-size-body-scaled);
|
|
4
|
+
--checkbox-helper-font-size: var(--font-size-footnote-scaled);
|
|
5
|
+
--checkbox-gap: var(--space-2);
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: calc(var(--space-1) * var(--spacing-multiplier));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.solara-checkbox--size-small {
|
|
12
|
+
--checkbox-size: 16px;
|
|
13
|
+
--checkbox-font-size: var(--font-size-caption-scaled);
|
|
14
|
+
--checkbox-helper-font-size: var(--font-size-footnote-scaled);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.solara-checkbox--size-large {
|
|
18
|
+
--checkbox-size: 20px;
|
|
19
|
+
--checkbox-font-size: var(--font-size-h5-scaled);
|
|
20
|
+
--checkbox-helper-font-size: var(--font-size-caption-scaled);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.solara-checkbox__control {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: flex-start;
|
|
26
|
+
gap: calc(var(--checkbox-gap) * var(--spacing-multiplier));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.solara-checkbox__input {
|
|
30
|
+
position: absolute;
|
|
31
|
+
opacity: 0;
|
|
32
|
+
width: 0;
|
|
33
|
+
height: 0;
|
|
34
|
+
margin: 0;
|
|
35
|
+
padding: 0;
|
|
36
|
+
border: none;
|
|
37
|
+
appearance: none;
|
|
38
|
+
outline: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.solara-checkbox__label {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: flex-start;
|
|
44
|
+
gap: calc(var(--checkbox-gap) * var(--spacing-multiplier));
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
user-select: none;
|
|
47
|
+
font-size: var(--checkbox-font-size);
|
|
48
|
+
color: var(--color-text-primary);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.solara-checkbox__box {
|
|
52
|
+
--checkbox-border-color: var(--color-surface-400);
|
|
53
|
+
--checkbox-bg-color: var(--color-surface-0);
|
|
54
|
+
--checkbox-hover-border-color: var(--color-primary);
|
|
55
|
+
--checkbox-hover-bg-color: var(--color-surface-200);
|
|
56
|
+
--checkbox-focus-border-color: var(--color-primary);
|
|
57
|
+
--checkbox-focus-halo-color: var(--color-primary-focus);
|
|
58
|
+
position: relative;
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
width: var(--checkbox-size);
|
|
63
|
+
height: var(--checkbox-size);
|
|
64
|
+
border-radius: 6px;
|
|
65
|
+
border: 2px solid var(--checkbox-border-color);
|
|
66
|
+
background-color: var(--checkbox-bg-color);
|
|
67
|
+
transition: border-color 0.2s ease, background-color 0.2s ease,
|
|
68
|
+
box-shadow 0.2s ease, transform 0.2s ease;
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
.solara-checkbox__box::before {
|
|
75
|
+
content: "";
|
|
76
|
+
position: absolute;
|
|
77
|
+
width: 60%;
|
|
78
|
+
height: 2px;
|
|
79
|
+
background-color: var(--color-on-primary);
|
|
80
|
+
opacity: 0;
|
|
81
|
+
transition: opacity 0.15s ease;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.solara-checkbox--custom-icon .solara-checkbox__box::before,
|
|
85
|
+
.solara-checkbox--custom-icon .solara-checkbox__box::after {
|
|
86
|
+
opacity: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.solara-checkbox__icon {
|
|
90
|
+
position: absolute;
|
|
91
|
+
inset: 0;
|
|
92
|
+
display: inline-flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
justify-content: center;
|
|
95
|
+
opacity: 0;
|
|
96
|
+
transform: translateY(-5%);
|
|
97
|
+
transition: opacity 0.15s ease;
|
|
98
|
+
color: var(--color-on-primary);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.solara-checkbox__text {
|
|
102
|
+
line-height: 1.4;
|
|
103
|
+
margin-top: 1px;
|
|
104
|
+
color: var(--color-text-primary);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.solara-checkbox__helper {
|
|
108
|
+
font-size: var(--checkbox-helper-font-size);
|
|
109
|
+
color: var(--color-text-secondary);
|
|
110
|
+
margin-left: calc(
|
|
111
|
+
var(--checkbox-size) +
|
|
112
|
+
(var(--checkbox-gap) * var(--spacing-multiplier))
|
|
113
|
+
);
|
|
114
|
+
line-height: 1.4;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.solara-checkbox__helper--invalid {
|
|
118
|
+
color: var(--color-text-alert);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.solara-checkbox__input:checked + .solara-checkbox__label .solara-checkbox__box {
|
|
122
|
+
background-color: var(--color-primary);
|
|
123
|
+
border-color: var(--color-primary);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
.solara-checkbox__input:checked
|
|
129
|
+
+ .solara-checkbox__label
|
|
130
|
+
.solara-checkbox__icon--check {
|
|
131
|
+
opacity: 1;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Fallback for controlled state or environments where :checked styles are unreliable. */
|
|
135
|
+
.solara-checkbox--checked .solara-checkbox__box {
|
|
136
|
+
background-color: var(--color-primary);
|
|
137
|
+
border-color: var(--color-primary);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
.solara-checkbox--checked .solara-checkbox__icon--check {
|
|
145
|
+
opacity: 1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.solara-checkbox--indeterminate .solara-checkbox__box {
|
|
149
|
+
background-color: var(--color-primary);
|
|
150
|
+
border-color: var(--color-primary);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.solara-checkbox--indeterminate .solara-checkbox__box::before {
|
|
154
|
+
opacity: 1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
.solara-checkbox--indeterminate .solara-checkbox__icon--check {
|
|
160
|
+
opacity: 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.solara-checkbox--indeterminate .solara-checkbox__icon--indeterminate {
|
|
164
|
+
opacity: 1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.solara-checkbox__input:focus-visible
|
|
168
|
+
+ .solara-checkbox__label
|
|
169
|
+
.solara-checkbox__box {
|
|
170
|
+
border-color: var(--checkbox-focus-border-color);
|
|
171
|
+
box-shadow: 0 0 0 2px var(--color-surface-0), 0 0 0 4px var(--checkbox-focus-halo-color);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.solara-checkbox__input:checked:focus-visible
|
|
175
|
+
+ .solara-checkbox__label
|
|
176
|
+
.solara-checkbox__box,
|
|
177
|
+
.solara-checkbox__input:indeterminate:focus-visible
|
|
178
|
+
+ .solara-checkbox__label
|
|
179
|
+
.solara-checkbox__box {
|
|
180
|
+
border-color: var(--checkbox-focus-border-color);
|
|
181
|
+
box-shadow: 0 0 0 2px var(--color-surface-0), 0 0 0 4px var(--checkbox-focus-halo-color);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.solara-checkbox__input:not(:disabled)
|
|
185
|
+
+ .solara-checkbox__label:hover
|
|
186
|
+
.solara-checkbox__box {
|
|
187
|
+
border-color: var(--checkbox-hover-border-color);
|
|
188
|
+
background-color: var(--checkbox-hover-bg-color);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.solara-checkbox__input:active:not(:disabled)
|
|
192
|
+
+ .solara-checkbox__label
|
|
193
|
+
.solara-checkbox__box {
|
|
194
|
+
transform: scale(0.98);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.solara-checkbox--invalid .solara-checkbox__box {
|
|
198
|
+
border-color: var(--color-destructive);
|
|
199
|
+
--checkbox-focus-border-color: var(--color-destructive);
|
|
200
|
+
--checkbox-focus-halo-color: var(--color-destructive-focus);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.solara-checkbox--invalid .solara-checkbox__input:checked
|
|
204
|
+
+ .solara-checkbox__label
|
|
205
|
+
.solara-checkbox__box {
|
|
206
|
+
background-color: var(--color-destructive);
|
|
207
|
+
border-color: var(--color-destructive);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.solara-checkbox--disabled .solara-checkbox__label {
|
|
211
|
+
cursor: not-allowed;
|
|
212
|
+
opacity: 0.6;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.solara-checkbox--disabled .solara-checkbox__box {
|
|
216
|
+
background-color: var(--color-surface-200);
|
|
217
|
+
border-color: var(--color-surface-300);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.solara-checkbox--disabled .solara-checkbox__text {
|
|
221
|
+
color: var(--color-text-secondary);
|
|
222
|
+
}
|
package/src/Checkbox.tsx
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Icon } from "@solara/icons";
|
|
2
|
+
import React, { forwardRef, useEffect, useRef } from "react";
|
|
3
|
+
import type { CheckboxProps, CheckboxSize } from "./Checkbox.types";
|
|
4
|
+
import "./Checkbox.scss";
|
|
5
|
+
|
|
6
|
+
const sizeAliasMap: Record<CheckboxSize, "small" | "medium" | "large"> = {
|
|
7
|
+
sm: "small",
|
|
8
|
+
md: "medium",
|
|
9
|
+
lg: "large",
|
|
10
|
+
small: "small",
|
|
11
|
+
medium: "medium",
|
|
12
|
+
large: "large",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Checkbox component for boolean input.
|
|
17
|
+
*/
|
|
18
|
+
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
19
|
+
(
|
|
20
|
+
{
|
|
21
|
+
className,
|
|
22
|
+
size = "medium",
|
|
23
|
+
label,
|
|
24
|
+
helperText,
|
|
25
|
+
error = false,
|
|
26
|
+
isInvalid,
|
|
27
|
+
isDisabled,
|
|
28
|
+
indeterminate = false,
|
|
29
|
+
checkIcon,
|
|
30
|
+
indeterminateIcon,
|
|
31
|
+
disabled,
|
|
32
|
+
id,
|
|
33
|
+
checked,
|
|
34
|
+
defaultChecked,
|
|
35
|
+
onChange,
|
|
36
|
+
...restProps
|
|
37
|
+
},
|
|
38
|
+
ref
|
|
39
|
+
) => {
|
|
40
|
+
const resolvedSize = sizeAliasMap[size];
|
|
41
|
+
const inputId = id ?? React.useId();
|
|
42
|
+
const internalRef = useRef<HTMLInputElement>(null);
|
|
43
|
+
const mergedRef = (node: HTMLInputElement | null) => {
|
|
44
|
+
internalRef.current = node;
|
|
45
|
+
if (typeof ref === "function") {
|
|
46
|
+
ref(node);
|
|
47
|
+
} else if (ref) {
|
|
48
|
+
ref.current = node;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const resolvedDisabled = isDisabled ?? disabled ?? false;
|
|
53
|
+
const resolvedInvalid = isInvalid ?? error ?? false;
|
|
54
|
+
const isControlled = checked !== undefined;
|
|
55
|
+
const [uncontrolledChecked, setUncontrolledChecked] = React.useState<boolean>(
|
|
56
|
+
Boolean(defaultChecked)
|
|
57
|
+
);
|
|
58
|
+
const resolvedChecked = isControlled ? checked : uncontrolledChecked;
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (internalRef.current) {
|
|
62
|
+
internalRef.current.indeterminate = Boolean(indeterminate);
|
|
63
|
+
}
|
|
64
|
+
}, [indeterminate]);
|
|
65
|
+
|
|
66
|
+
const hasCustomIcons = Boolean(checkIcon || indeterminateIcon);
|
|
67
|
+
|
|
68
|
+
const rootClasses = [
|
|
69
|
+
"solara-checkbox",
|
|
70
|
+
`solara-checkbox--size-${resolvedSize}`,
|
|
71
|
+
resolvedChecked ? "solara-checkbox--checked" : null,
|
|
72
|
+
resolvedDisabled ? "solara-checkbox--disabled" : null,
|
|
73
|
+
resolvedInvalid ? "solara-checkbox--invalid" : null,
|
|
74
|
+
indeterminate ? "solara-checkbox--indeterminate" : null,
|
|
75
|
+
hasCustomIcons ? "solara-checkbox--custom-icon" : null,
|
|
76
|
+
className,
|
|
77
|
+
]
|
|
78
|
+
.filter(Boolean)
|
|
79
|
+
.join(" ");
|
|
80
|
+
|
|
81
|
+
const helperClasses = [
|
|
82
|
+
"solara-checkbox__helper",
|
|
83
|
+
resolvedInvalid ? "solara-checkbox__helper--invalid" : null,
|
|
84
|
+
]
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
.join(" ");
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className={rootClasses}>
|
|
90
|
+
<div className="solara-checkbox__control">
|
|
91
|
+
<input
|
|
92
|
+
{...restProps}
|
|
93
|
+
ref={mergedRef}
|
|
94
|
+
type="checkbox"
|
|
95
|
+
id={inputId}
|
|
96
|
+
className="solara-checkbox__input"
|
|
97
|
+
disabled={resolvedDisabled}
|
|
98
|
+
aria-invalid={resolvedInvalid || undefined}
|
|
99
|
+
aria-checked={indeterminate ? "mixed" : undefined}
|
|
100
|
+
readOnly={isControlled && !onChange}
|
|
101
|
+
checked={resolvedChecked}
|
|
102
|
+
onChange={(event) => {
|
|
103
|
+
if (!isControlled) {
|
|
104
|
+
setUncontrolledChecked(event.currentTarget.checked);
|
|
105
|
+
}
|
|
106
|
+
onChange?.(event);
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
<label className="solara-checkbox__label" htmlFor={inputId}>
|
|
110
|
+
<span className="solara-checkbox__box">
|
|
111
|
+
{checkIcon ? (
|
|
112
|
+
<span className="solara-checkbox__icon solara-checkbox__icon--check">
|
|
113
|
+
{checkIcon}
|
|
114
|
+
</span>
|
|
115
|
+
) : (
|
|
116
|
+
<Icon
|
|
117
|
+
className="solara-checkbox__icon solara-checkbox__icon--check"
|
|
118
|
+
name="checkmark_16"
|
|
119
|
+
style={{ color: "white" }}
|
|
120
|
+
/>
|
|
121
|
+
)}
|
|
122
|
+
{indeterminateIcon ? (
|
|
123
|
+
<span className="solara-checkbox__icon solara-checkbox__icon--indeterminate">
|
|
124
|
+
{indeterminateIcon}
|
|
125
|
+
</span>
|
|
126
|
+
) : null}
|
|
127
|
+
</span>
|
|
128
|
+
{label ? <span className="solara-checkbox__text">{label}</span> : null}
|
|
129
|
+
</label>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
{helperText ? <p className={helperClasses}>{helperText}</p> : null}
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
Checkbox.displayName = "Checkbox";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export type CheckboxSize = "small" | "medium" | "large" | "sm" | "md" | "lg";
|
|
4
|
+
|
|
5
|
+
export interface CheckboxProps
|
|
6
|
+
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
|
|
7
|
+
/**
|
|
8
|
+
* Size of the checkbox.
|
|
9
|
+
*/
|
|
10
|
+
size?: CheckboxSize;
|
|
11
|
+
/**
|
|
12
|
+
* Label for the checkbox.
|
|
13
|
+
*/
|
|
14
|
+
label?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Helper text to display below the checkbox.
|
|
17
|
+
*/
|
|
18
|
+
helperText?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Error state.
|
|
21
|
+
*/
|
|
22
|
+
error?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Invalid state (preferred over `error`).
|
|
25
|
+
*/
|
|
26
|
+
isInvalid?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Disabled state (preferred over `disabled`).
|
|
29
|
+
*/
|
|
30
|
+
isDisabled?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Indeterminate state (shows dash instead of check).
|
|
33
|
+
*/
|
|
34
|
+
indeterminate?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Custom check icon.
|
|
37
|
+
*/
|
|
38
|
+
checkIcon?: React.ReactNode;
|
|
39
|
+
/**
|
|
40
|
+
* Custom indeterminate icon.
|
|
41
|
+
*/
|
|
42
|
+
indeterminateIcon?: React.ReactNode;
|
|
43
|
+
}
|
package/src/index.ts
ADDED