@xsolla/xui-input 0.149.1 → 0.151.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/README.md +89 -344
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,397 +1,142 @@
|
|
|
1
1
|
# Input
|
|
2
2
|
|
|
3
|
-
A cross-platform
|
|
3
|
+
A cross-platform text input with label, error, icon, clear and validation support; works on web and React Native via `XUIProvider` themed contexts.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @xsolla/xui-input
|
|
9
|
-
# or
|
|
10
|
-
yarn add @xsolla/xui-input
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
**Important:** Input requires `XUIProvider` from `@xsolla/xui-core` to be wrapped around your app for proper theming.
|
|
16
|
-
|
|
17
|
-
If your input appears white or washed out on a light background, you need to set the theme mode:
|
|
11
|
+
## Imports
|
|
18
12
|
|
|
19
13
|
```tsx
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
// For light/white page backgrounds
|
|
23
|
-
<XUIProvider initialMode="light">
|
|
24
|
-
<YourApp />
|
|
25
|
-
</XUIProvider>
|
|
26
|
-
|
|
27
|
-
// For dark page backgrounds (default)
|
|
28
|
-
<XUIProvider initialMode="dark">
|
|
29
|
-
<YourApp />
|
|
30
|
-
</XUIProvider>
|
|
14
|
+
import { Input } from '@xsolla/xui-input';
|
|
15
|
+
import type { InputProps } from '@xsolla/xui-input';
|
|
31
16
|
```
|
|
32
17
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
## Demo
|
|
36
|
-
|
|
37
|
-
### Basic Input
|
|
18
|
+
## Quick start
|
|
38
19
|
|
|
39
20
|
```tsx
|
|
40
|
-
|
|
41
|
-
import { Input } from '@xsolla/xui-input';
|
|
21
|
+
const [value, setValue] = useState('');
|
|
42
22
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
onChangeText={setValue}
|
|
50
|
-
placeholder="Enter text"
|
|
51
|
-
/>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
23
|
+
<Input
|
|
24
|
+
label="Email"
|
|
25
|
+
value={value}
|
|
26
|
+
onChangeText={setValue}
|
|
27
|
+
placeholder="you@example.com"
|
|
28
|
+
/>;
|
|
54
29
|
```
|
|
55
30
|
|
|
56
|
-
|
|
31
|
+
## API Reference
|
|
57
32
|
|
|
58
|
-
|
|
59
|
-
import * as React from 'react';
|
|
60
|
-
import { Input } from '@xsolla/xui-input';
|
|
33
|
+
### `<Input>`
|
|
61
34
|
|
|
62
|
-
|
|
63
|
-
|
|
35
|
+
| Prop | Type | Default | Description |
|
|
36
|
+
| --- | --- | --- | --- |
|
|
37
|
+
| `value` | `string` | — | Controlled value. |
|
|
38
|
+
| `placeholder` | `string` | — | Placeholder shown when empty. |
|
|
39
|
+
| `onChange` | `(e: ChangeEvent<HTMLInputElement>) => void` | — | Native change handler. |
|
|
40
|
+
| `onChangeText` | `(text: string) => void` | — | Simplified change handler receiving the text. |
|
|
41
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Control size. |
|
|
42
|
+
| `disabled` | `boolean` | `false` | Disable the input. |
|
|
43
|
+
| `label` | `string` | — | Label rendered above the input. |
|
|
44
|
+
| `error` | `boolean` | `false` | Apply error styling. |
|
|
45
|
+
| `errorMessage` | `string` | — | Error message; also marks the input invalid. |
|
|
46
|
+
| `iconLeft` | `ReactNode` | — | Icon on the left. |
|
|
47
|
+
| `iconRight` | `ReactNode` | — | Icon on the right. |
|
|
48
|
+
| `iconRightSize` | `number \| string` | — | Override the right-icon size. |
|
|
49
|
+
| `extraClear` | `boolean` | `false` | Show a clear button when the input has a value. |
|
|
50
|
+
| `onRemove` | `() => void` | — | Fired when the clear button is pressed. |
|
|
51
|
+
| `checked` | `boolean` | `false` | Show a success indicator (suppressed when `errorMessage` is set). |
|
|
52
|
+
| `checkedIcon` | `ReactNode` | `<CheckCr />` | Override the checked icon. |
|
|
53
|
+
| `borderTopLeftRadius` | `number` | — | Override top-left corner radius. |
|
|
54
|
+
| `borderTopRightRadius` | `number` | — | Override top-right corner radius. |
|
|
55
|
+
| `borderBottomLeftRadius` | `number` | — | Override bottom-left corner radius. |
|
|
56
|
+
| `borderBottomRightRadius` | `number` | — | Override bottom-right corner radius. |
|
|
57
|
+
| `backgroundColor` | `string` | — | Override the background colour. |
|
|
58
|
+
| `id` | `string` | `auto` | HTML id; also wires up the label. |
|
|
59
|
+
| `aria-label` | `string` | — | Accessible label when no visible label is present. |
|
|
60
|
+
| `testID` | `string` | — | Test identifier. |
|
|
61
|
+
|
|
62
|
+
`Input` also accepts the standard `InputHTMLAttributes` (excluding `size` and `onChange`, which are redefined above).
|
|
63
|
+
|
|
64
|
+
Inherits `ThemeOverrideProps` (`themeMode`, `themeProductContext`).
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
<Input
|
|
67
|
-
label="Email Address"
|
|
68
|
-
value={email}
|
|
69
|
-
onChangeText={setEmail}
|
|
70
|
-
placeholder="you@example.com"
|
|
71
|
-
/>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
```
|
|
66
|
+
## Examples
|
|
75
67
|
|
|
76
|
-
###
|
|
68
|
+
### Sizes
|
|
77
69
|
|
|
78
70
|
```tsx
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
85
|
-
<Input size="xs" placeholder="Extra Small" />
|
|
86
|
-
<Input size="sm" placeholder="Small" />
|
|
87
|
-
<Input size="md" placeholder="Medium (default)" />
|
|
88
|
-
<Input size="lg" placeholder="Large" />
|
|
89
|
-
<Input size="xl" placeholder="Extra Large" />
|
|
90
|
-
</div>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
71
|
+
<Input size="xs" placeholder="Extra small" />
|
|
72
|
+
<Input size="sm" placeholder="Small" />
|
|
73
|
+
<Input size="md" placeholder="Medium" />
|
|
74
|
+
<Input size="lg" placeholder="Large" />
|
|
75
|
+
<Input size="xl" placeholder="Extra large" />
|
|
93
76
|
```
|
|
94
77
|
|
|
95
|
-
###
|
|
78
|
+
### Icons
|
|
96
79
|
|
|
97
80
|
```tsx
|
|
98
|
-
import * as React from 'react';
|
|
99
|
-
import { Input } from '@xsolla/xui-input';
|
|
100
81
|
import { Search, Check } from '@xsolla/xui-icons';
|
|
101
|
-
import {
|
|
102
|
-
|
|
103
|
-
export default function InputWithIcons() {
|
|
104
|
-
return (
|
|
105
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
106
|
-
<Input
|
|
107
|
-
iconLeft={<Search />}
|
|
108
|
-
placeholder="Search..."
|
|
109
|
-
/>
|
|
110
|
-
<Input
|
|
111
|
-
iconRight={<Calendar />}
|
|
112
|
-
placeholder="Select date"
|
|
113
|
-
/>
|
|
114
|
-
<Input
|
|
115
|
-
iconLeft={<Mail />}
|
|
116
|
-
iconRight={<Check />}
|
|
117
|
-
placeholder="Email"
|
|
118
|
-
/>
|
|
119
|
-
</div>
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Input with Clear Button
|
|
125
|
-
|
|
126
|
-
```tsx
|
|
127
|
-
import * as React from 'react';
|
|
128
|
-
import { Input } from '@xsolla/xui-input';
|
|
129
|
-
|
|
130
|
-
export default function ClearableInput() {
|
|
131
|
-
const [value, setValue] = React.useState('Some text');
|
|
82
|
+
import { Mail } from '@xsolla/xui-icons-base';
|
|
132
83
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
value={value}
|
|
136
|
-
onChangeText={setValue}
|
|
137
|
-
extraClear
|
|
138
|
-
onRemove={() => setValue('')}
|
|
139
|
-
placeholder="Type something..."
|
|
140
|
-
/>
|
|
141
|
-
);
|
|
142
|
-
}
|
|
84
|
+
<Input iconLeft={<Search />} placeholder="Search..." />
|
|
85
|
+
<Input iconLeft={<Mail />} iconRight={<Check />} placeholder="Email" />
|
|
143
86
|
```
|
|
144
87
|
|
|
145
|
-
###
|
|
88
|
+
### Clear button
|
|
146
89
|
|
|
147
90
|
```tsx
|
|
148
|
-
|
|
149
|
-
import { Input } from '@xsolla/xui-input';
|
|
150
|
-
|
|
151
|
-
export default function ValidatedInput() {
|
|
152
|
-
const [email, setEmail] = React.useState('');
|
|
153
|
-
const [error, setError] = React.useState('');
|
|
154
|
-
|
|
155
|
-
const validateEmail = (value: string) => {
|
|
156
|
-
setEmail(value);
|
|
157
|
-
if (value && !value.includes('@')) {
|
|
158
|
-
setError('Please enter a valid email address');
|
|
159
|
-
} else {
|
|
160
|
-
setError('');
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
return (
|
|
165
|
-
<Input
|
|
166
|
-
label="Email"
|
|
167
|
-
value={email}
|
|
168
|
-
onChangeText={validateEmail}
|
|
169
|
-
error={!!error}
|
|
170
|
-
errorMessage={error}
|
|
171
|
-
placeholder="you@example.com"
|
|
172
|
-
/>
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
## Anatomy
|
|
178
|
-
|
|
179
|
-
Import the component and use it directly:
|
|
180
|
-
|
|
181
|
-
```jsx
|
|
182
|
-
import { Input } from '@xsolla/xui-input';
|
|
91
|
+
const [value, setValue] = useState('Some text');
|
|
183
92
|
|
|
184
93
|
<Input
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
extraClear // Shows clear button when has value
|
|
192
|
-
error // Error state boolean
|
|
193
|
-
errorMessage="Error text" // Error message below input
|
|
194
|
-
disabled // Disabled state
|
|
195
|
-
/>
|
|
94
|
+
value={value}
|
|
95
|
+
onChangeText={setValue}
|
|
96
|
+
extraClear
|
|
97
|
+
onRemove={() => setValue('')}
|
|
98
|
+
placeholder="Type something..."
|
|
99
|
+
/>;
|
|
196
100
|
```
|
|
197
101
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
### Disabled Input
|
|
102
|
+
### Validation
|
|
201
103
|
|
|
202
104
|
```tsx
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
export default function DisabledInput() {
|
|
207
|
-
return (
|
|
208
|
-
<Input
|
|
209
|
-
label="Disabled Field"
|
|
210
|
-
value="Cannot edit this"
|
|
211
|
-
disabled
|
|
212
|
-
/>
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Input with Success State
|
|
218
|
-
|
|
219
|
-
```tsx
|
|
220
|
-
import * as React from 'react';
|
|
221
|
-
import { Input } from '@xsolla/xui-input';
|
|
222
|
-
|
|
223
|
-
export default function SuccessInput() {
|
|
224
|
-
return (
|
|
225
|
-
<Input
|
|
226
|
-
label="Username"
|
|
227
|
-
value="johndoe"
|
|
228
|
-
checked
|
|
229
|
-
placeholder="Enter username"
|
|
230
|
-
/>
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### Custom Border Radius
|
|
105
|
+
const [email, setEmail] = useState('');
|
|
106
|
+
const error = email && !email.includes('@') ? 'Invalid email' : '';
|
|
236
107
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
borderTopLeftRadius={20}
|
|
246
|
-
borderTopRightRadius={20}
|
|
247
|
-
borderBottomLeftRadius={20}
|
|
248
|
-
borderBottomRightRadius={20}
|
|
249
|
-
/>
|
|
250
|
-
);
|
|
251
|
-
}
|
|
108
|
+
<Input
|
|
109
|
+
label="Email"
|
|
110
|
+
value={email}
|
|
111
|
+
onChangeText={setEmail}
|
|
112
|
+
error={!!error}
|
|
113
|
+
errorMessage={error}
|
|
114
|
+
placeholder="you@example.com"
|
|
115
|
+
/>;
|
|
252
116
|
```
|
|
253
117
|
|
|
254
|
-
###
|
|
118
|
+
### Disabled and success
|
|
255
119
|
|
|
256
120
|
```tsx
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
import { Search } from '@xsolla/xui-icons';
|
|
260
|
-
|
|
261
|
-
export default function SearchInput() {
|
|
262
|
-
const [query, setQuery] = React.useState('');
|
|
263
|
-
|
|
264
|
-
return (
|
|
265
|
-
<Input
|
|
266
|
-
value={query}
|
|
267
|
-
onChangeText={setQuery}
|
|
268
|
-
iconLeft={<Search />}
|
|
269
|
-
extraClear
|
|
270
|
-
onRemove={() => setQuery('')}
|
|
271
|
-
placeholder="Search..."
|
|
272
|
-
size="md"
|
|
273
|
-
/>
|
|
274
|
-
);
|
|
275
|
-
}
|
|
121
|
+
<Input label="Disabled" value="Cannot edit" disabled />
|
|
122
|
+
<Input label="Username" value="johndoe" checked />
|
|
276
123
|
```
|
|
277
124
|
|
|
278
|
-
###
|
|
125
|
+
### Custom border radius
|
|
279
126
|
|
|
280
127
|
```tsx
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
lastName: '',
|
|
289
|
-
email: '',
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
const updateField = (field: string) => (value: string) => {
|
|
293
|
-
setForm(prev => ({ ...prev, [field]: value }));
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
return (
|
|
297
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
298
|
-
<Input
|
|
299
|
-
label="First Name"
|
|
300
|
-
value={form.firstName}
|
|
301
|
-
onChangeText={updateField('firstName')}
|
|
302
|
-
placeholder="John"
|
|
303
|
-
/>
|
|
304
|
-
<Input
|
|
305
|
-
label="Last Name"
|
|
306
|
-
value={form.lastName}
|
|
307
|
-
onChangeText={updateField('lastName')}
|
|
308
|
-
placeholder="Doe"
|
|
309
|
-
/>
|
|
310
|
-
<Input
|
|
311
|
-
label="Email"
|
|
312
|
-
value={form.email}
|
|
313
|
-
onChangeText={updateField('email')}
|
|
314
|
-
iconLeft={<Mail />}
|
|
315
|
-
placeholder="john@example.com"
|
|
316
|
-
/>
|
|
317
|
-
</div>
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
## API Reference
|
|
323
|
-
|
|
324
|
-
### Input
|
|
325
|
-
|
|
326
|
-
The main input component. Renders a semantic `<input>` element with wrapper styling.
|
|
327
|
-
|
|
328
|
-
**Input Props:**
|
|
329
|
-
|
|
330
|
-
| Prop | Type | Default | Description |
|
|
331
|
-
| :--- | :--- | :------ | :---------- |
|
|
332
|
-
| value | `string` | - | The controlled value of the input. |
|
|
333
|
-
| placeholder | `string` | - | Placeholder text shown when input is empty. |
|
|
334
|
-
| onChange | `(e: ChangeEvent<HTMLInputElement>) => void` | - | Native change event handler. |
|
|
335
|
-
| onChangeText | `(text: string) => void` | - | Simplified change handler receiving text value. |
|
|
336
|
-
| size | `"xl" \| "lg" \| "md" \| "sm" \| "xs"` | `"md"` | The size of the input. |
|
|
337
|
-
| disabled | `boolean` | `false` | Whether the input is disabled. |
|
|
338
|
-
| label | `string` | - | Label text displayed above the input. |
|
|
339
|
-
| errorMessage | `string` | - | Error message displayed below the input. |
|
|
340
|
-
| error | `boolean` | `false` | Whether to show error styling. |
|
|
341
|
-
| iconLeft | `ReactNode` | - | Icon displayed on the left side. |
|
|
342
|
-
| iconRight | `ReactNode` | - | Icon displayed on the right side. |
|
|
343
|
-
| extraClear | `boolean` | `false` | Whether to show clear button when input has value. |
|
|
344
|
-
| onRemove | `() => void` | - | Callback when clear button is clicked. |
|
|
345
|
-
| checked | `boolean` | `false` | Whether to show success/check icon. |
|
|
346
|
-
| checkedIcon | `ReactNode` | `<Check />` | Custom icon for checked state. |
|
|
347
|
-
| iconRightSize | `number \| string` | - | Custom size for right icon. |
|
|
348
|
-
| borderTopLeftRadius | `number` | - | Custom top-left border radius. |
|
|
349
|
-
| borderTopRightRadius | `number` | - | Custom top-right border radius. |
|
|
350
|
-
| borderBottomLeftRadius | `number` | - | Custom bottom-left border radius. |
|
|
351
|
-
| borderBottomRightRadius | `number` | - | Custom bottom-right border radius. |
|
|
352
|
-
| backgroundColor | `string` | - | Custom background color. |
|
|
353
|
-
| aria-label | `string` | - | Accessible label for the input. |
|
|
354
|
-
| id | `string` | - | HTML id attribute (also links label). |
|
|
355
|
-
| testID | `string` | - | Test identifier for testing frameworks. |
|
|
356
|
-
|
|
357
|
-
**Size Configuration:**
|
|
358
|
-
|
|
359
|
-
| Size | Height | Padding (V/H) | Font Size | Icon Size |
|
|
360
|
-
| :--- | :----- | :------------ | :-------- | :-------- |
|
|
361
|
-
| xl | 56px | 12px / 12px | 16px | 18px |
|
|
362
|
-
| lg | 48px | 14px / 12px | 16px | 18px |
|
|
363
|
-
| md | 40px | 11px / 12px | 14px | 18px |
|
|
364
|
-
| sm | 32px | 7px / 10px | 14px | 16px |
|
|
365
|
-
| xs | 28px | 7px / 10px | 12px | 16px |
|
|
366
|
-
|
|
367
|
-
## Theming
|
|
368
|
-
|
|
369
|
-
Input components use the design system theme for colors and sizing:
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
// Colors accessed via theme
|
|
373
|
-
theme.colors.control.input.bg // Background color
|
|
374
|
-
theme.colors.control.input.bgHover // Hover background
|
|
375
|
-
theme.colors.control.input.bgFocus // Focus background
|
|
376
|
-
theme.colors.control.input.bgDisable // Disabled background
|
|
377
|
-
theme.colors.control.input.border // Border color
|
|
378
|
-
theme.colors.control.input.borderFocus // Focus border color
|
|
379
|
-
theme.colors.control.input.borderError // Error border color
|
|
380
|
-
theme.colors.control.input.text // Text color
|
|
381
|
-
theme.colors.control.input.placeholder // Placeholder color
|
|
382
|
-
|
|
383
|
-
// Sizing accessed via theme
|
|
384
|
-
theme.sizing.input(size).height
|
|
385
|
-
theme.sizing.input(size).paddingVertical
|
|
386
|
-
theme.sizing.input(size).paddingHorizontal
|
|
387
|
-
theme.sizing.input(size).fontSize
|
|
128
|
+
<Input
|
|
129
|
+
placeholder="Rounded"
|
|
130
|
+
borderTopLeftRadius={20}
|
|
131
|
+
borderTopRightRadius={20}
|
|
132
|
+
borderBottomLeftRadius={20}
|
|
133
|
+
borderBottomRightRadius={20}
|
|
134
|
+
/>
|
|
388
135
|
```
|
|
389
136
|
|
|
390
137
|
## Accessibility
|
|
391
138
|
|
|
392
|
-
-
|
|
393
|
-
-
|
|
394
|
-
-
|
|
395
|
-
-
|
|
396
|
-
- Disabled state properly communicated to assistive technology
|
|
397
|
-
- Clear button includes accessible label
|
|
139
|
+
- Renders a semantic `<input>` whose `aria-labelledby` points at the `id` of the rendered `<label>` element (rather than the standard `<label htmlFor>` pairing).
|
|
140
|
+
- Error messages are linked through `aria-describedby` and announced via `role="alert"`.
|
|
141
|
+
- `aria-invalid` and `aria-disabled` reflect error and disabled state.
|
|
142
|
+
- Pressing Escape blurs the input.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xsolla/xui-input",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.151.0",
|
|
4
4
|
"main": "./web/index.js",
|
|
5
5
|
"module": "./web/index.mjs",
|
|
6
6
|
"types": "./web/index.d.ts",
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"test:coverage": "vitest run --coverage"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@xsolla/xui-core": "0.
|
|
17
|
-
"@xsolla/xui-icons-base": "0.
|
|
18
|
-
"@xsolla/xui-primitives-core": "0.
|
|
16
|
+
"@xsolla/xui-core": "0.151.0",
|
|
17
|
+
"@xsolla/xui-icons-base": "0.151.0",
|
|
18
|
+
"@xsolla/xui-primitives-core": "0.151.0"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"react": ">=16.8.0",
|