@rescui/select 0.17.1 → 0.17.2-RUI-296-Build-docs-md-files-for-each-docs-section-b138dd5bf.15

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.
Files changed (51) hide show
  1. package/README.md +3 -1
  2. package/lib/_virtual/index.css.js +12 -12
  3. package/lib/common-prop-types.d.ts +29 -29
  4. package/lib/constants.d.ts +6 -6
  5. package/lib/hooks/use-a11y.d.ts +30 -30
  6. package/lib/hooks/use-actual-size-from-css.d.ts +10 -10
  7. package/lib/hooks/use-actual-size-from-css.js +1 -1
  8. package/lib/hooks/use-focus.d.ts +10 -10
  9. package/lib/hooks/use-hover.d.ts +10 -10
  10. package/lib/hooks/use-label-focus.d.ts +9 -9
  11. package/lib/hooks/use-multi-handlers.d.ts +11 -11
  12. package/lib/hooks/use-options.d.ts +13 -14
  13. package/lib/hooks/use-options.js +5 -5
  14. package/lib/hooks/use-primary-contrast-css-var-transfer.d.ts +11 -11
  15. package/lib/hooks/use-primary-contrast-css-var-transfer.js +2 -2
  16. package/lib/hooks/use-safe-id.d.ts +6 -6
  17. package/lib/index.css +189 -189
  18. package/lib/index.d.ts +4 -4
  19. package/lib/multiselect.d.ts +3 -3
  20. package/lib/multiselect.js +1 -0
  21. package/lib/native-select.d.ts +4 -4
  22. package/lib/parts/foldable.d.ts +34 -33
  23. package/lib/parts/form-trigger-wrapper.d.ts +34 -34
  24. package/lib/parts/hidden-select.d.ts +13 -13
  25. package/lib/parts/icons.d.ts +17 -17
  26. package/lib/parts/list-box.d.ts +13 -13
  27. package/lib/parts/multi-trigger.d.ts +12 -12
  28. package/lib/parts/native-select-options-with-groups.d.ts +8 -8
  29. package/lib/parts/popup-content.d.ts +20 -20
  30. package/lib/select-basic.d.ts +3 -3
  31. package/lib/select-searchable.d.ts +3 -3
  32. package/lib/select.d.ts +28 -28
  33. package/lib/styles/common.p.module.css.js +29 -29
  34. package/lib/styles/form-trigger.p.module.css.js +12 -12
  35. package/lib/styles/list-box.p.module.css.js +13 -13
  36. package/lib/types.d.ts +202 -202
  37. package/lib/utils/calculate-max-height-by-items.d.ts +13 -13
  38. package/lib/utils/calculate-max-height-by-items.js +11 -11
  39. package/lib/utils/get-empty-message.d.ts +6 -6
  40. package/lib/utils/get-option-value.d.ts +2 -2
  41. package/lib/utils/get-real-items-cnt.d.ts +2 -2
  42. package/lib/utils/get-text-from-nodes.d.ts +2 -2
  43. package/lib/utils/is-option-a-group.d.ts +2 -2
  44. package/lib/utils/item-to-string.d.ts +2 -2
  45. package/lib/utils/merge-props.d.ts +19 -19
  46. package/lib/utils/merge-props.js +17 -17
  47. package/lib/utils/throttle.d.ts +1 -1
  48. package/llm/components/multiselect.md +237 -0
  49. package/llm/components/select.md +549 -0
  50. package/llm/index.md +3 -0
  51. package/package.json +11 -10
@@ -0,0 +1,549 @@
1
+ # Select
2
+
3
+ Select is a control that allows users to select one option from a longer list.
4
+
5
+ ## Usage
6
+
7
+ ### Features showcase
8
+
9
+ ### Usage
10
+
11
+ Countries:
12
+
13
+ ```tsx
14
+ import { useState } from 'react';
15
+ import { Select } from '@rescui/select';
16
+ import COUNTRIES from '@/parts/select/countries';
17
+
18
+ const options = COUNTRIES.map(country => ({
19
+ label: country,
20
+ value: country
21
+ }));
22
+
23
+ const MySelect = () => {
24
+ const [selected, setSelected] = useState(options[42]);
25
+
26
+ return (
27
+ <div style={{ maxWidth: '380px' }}>
28
+ <Select
29
+ options={options}
30
+ value={selected}
31
+ onChange={value => setSelected(value)}
32
+ size="s"
33
+ isSearchable
34
+ label="Select country"
35
+ />
36
+ </div>
37
+ );
38
+ };
39
+
40
+ render(<MySelect />);
41
+ ```
42
+
43
+ Default value:
44
+
45
+ ```tsx
46
+ import { useState } from 'react';
47
+ import { Select } from '@rescui/select';
48
+
49
+ const options = [
50
+ { label: 'Light', value: 'light' },
51
+ { label: 'Regular', value: 'regular' },
52
+ { label: 'Medium', value: 'medium' },
53
+ { label: 'Bold', value: 'bold' }
54
+ ];
55
+ const MySelect = () => {
56
+ const [selected, setSelected] = useState({
57
+ label: 'Medium',
58
+ value: 'medium'
59
+ });
60
+
61
+ return (
62
+ <div style={{ maxWidth: '380px' }}>
63
+ <Select
64
+ options={options}
65
+ value={selected}
66
+ onChange={value => setSelected(value)}
67
+ size="m"
68
+ placeholder="Select font weight..."
69
+ aria-label="Select font weight"
70
+ />
71
+ </div>
72
+ );
73
+ };
74
+
75
+ render(<MySelect />);
76
+ ```
77
+
78
+ #### With fallback to NativeSelect on iOS and Android devices
79
+
80
+ ```tsx
81
+ import { useEffect, useState } from 'react';
82
+ import Sniffr from 'sniffr';
83
+ import { Select, NativeSelect } from '@rescui/select';
84
+ import COUNTRIES from '@/parts/select/countries';
85
+
86
+ const options = COUNTRIES.map(country => ({ label: country, value: country }));
87
+
88
+ const FallbackSelect = props => {
89
+ const [os, setOs] = useState(null);
90
+
91
+ useEffect(() => {
92
+ const sniffer = new Sniffr();
93
+ const os = sniffer.sniff(navigator.userAgent).os.name;
94
+ setOs(os);
95
+ }, []);
96
+
97
+ return os === 'ios' || os === 'android' ? (
98
+ <NativeSelect {...props} />
99
+ ) : (
100
+ <Select {...props} />
101
+ );
102
+ };
103
+
104
+ const MySelect = () => {
105
+ const [selected, setSelected] = useState(null);
106
+
107
+ return (
108
+ <div style={{ maxWidth: '380px' }}>
109
+ <FallbackSelect
110
+ options={options}
111
+ value={selected}
112
+ onChange={setSelected}
113
+ size="s"
114
+ placeholder="Select country..."
115
+ aria-label="Select country"
116
+ />
117
+ </div>
118
+ );
119
+ };
120
+
121
+ render(<MySelect />);
122
+ ```
123
+
124
+ #### GroupOptions example with headers
125
+
126
+ ```tsx
127
+ import { useEffect, useState } from 'react';
128
+ import Sniffr from 'sniffr';
129
+ import { Select, NativeSelect } from '@rescui/select';
130
+ import COUNTRIES from '@/parts/select/countries';
131
+
132
+ const COUNTRIES_WITH_OFFICES = [
133
+ 'Czech Republic',
134
+ 'Germany',
135
+ 'Netherlands',
136
+ 'United States'
137
+ ];
138
+
139
+ const popularCountries = COUNTRIES_WITH_OFFICES.map(country => ({
140
+ label: country,
141
+ value: country
142
+ }));
143
+
144
+ const allCountries = COUNTRIES.map(country => ({
145
+ label: country,
146
+ value: country
147
+ }));
148
+
149
+ const options = [
150
+ { label: 'Popular countries', options: popularCountries },
151
+ { label: 'All countries', options: allCountries }
152
+ ];
153
+
154
+ const FallbackSelect = props => {
155
+ const [os, setOs] = useState(null);
156
+
157
+ useEffect(() => {
158
+ const sniffer = new Sniffr();
159
+ const os = sniffer.sniff(navigator.userAgent).os.name;
160
+ setOs(os);
161
+ }, []);
162
+
163
+ return os === 'ios' || os === 'android' ? (
164
+ <NativeSelect {...props} />
165
+ ) : (
166
+ <Select {...props} />
167
+ );
168
+ };
169
+
170
+ const MySelect = () => {
171
+ const [selected, setSelected] = useState(null);
172
+
173
+ return (
174
+ <div style={{ maxWidth: '380px' }}>
175
+ <FallbackSelect
176
+ options={options}
177
+ value={selected}
178
+ onChange={value => setSelected(value)}
179
+ size="s"
180
+ placeholder="Select country..."
181
+ aria-label="Select country"
182
+ />
183
+ </div>
184
+ );
185
+ };
186
+
187
+ render(<MySelect />);
188
+ ```
189
+
190
+ #### Why pass `SelectOption` as value instead of `option.value`?
191
+
192
+ There are two main options of passing values:
193
+
194
+ 1. Passing only `value={option.value}`
195
+ 2. Passing the entire option as `value={option}`
196
+
197
+ We've adopted a second option. Here are the main reasons:
198
+
199
+ - _Async Options Loading:_ The first method is not suitable when options are loaded asynchronously and a default option is already known.
200
+
201
+ - _Easy Wrapper Implementation:_ It’s easier to create a wrapper that turns the second option into the first option, than the other way around.
202
+
203
+ ##### Example Wrapper:
204
+
205
+ Here's a concise example of a wrapper that modifies the API to a key-only approach, offering a cleaner interface.
206
+
207
+ ```tsx
208
+ import { useState } from 'react';
209
+ import { Select as RSelect } from '@rescui/select';
210
+
211
+ const Select = ({ value, options, onChange, ...restProps }) => {
212
+ const selectedOption = options.find(o => o.value === value);
213
+
214
+ const handleChange = option => onChange(option.value);
215
+
216
+ return (
217
+ <RSelect
218
+ {...restProps}
219
+ value={selectedOption}
220
+ onChange={handleChange}
221
+ options={options}
222
+ />
223
+ );
224
+ };
225
+
226
+ // -------- End of Wrapper --------
227
+
228
+ // -------- Code for Demo --------
229
+
230
+ const options = [
231
+ { label: 'Munich', value: 'MUC' },
232
+ { label: 'Amsterdam', value: 'AMS' },
233
+ { label: 'Berlin', value: 'BER' },
234
+ { label: 'Paphos', value: 'PFO' },
235
+ { label: 'Belgrade', value: 'BEG' },
236
+ { label: 'Yerevan', value: 'EVN' }
237
+ ];
238
+
239
+ const MySelect = () => {
240
+ const [selected, setSelected] = useState('AMS');
241
+
242
+ return (
243
+ <div style={{ maxWidth: '380px' }}>
244
+ <Select
245
+ options={options}
246
+ value={selected}
247
+ onChange={value => setSelected(value)}
248
+ size="s"
249
+ placeholder="Select city..."
250
+ aria-label="Select city"
251
+ />
252
+ </div>
253
+ );
254
+ };
255
+
256
+ render(<MySelect />);
257
+ ```
258
+
259
+ #### Contrast primary color
260
+
261
+ If your primary color background requires dark text color, use `--rs-select-contrast-primary` CSS variable.
262
+
263
+ ```tsx
264
+ import { useState } from 'react';
265
+ import { Select } from '@rescui/select';
266
+
267
+ const options = [
268
+ { label: 'Light', value: 'light' },
269
+ { label: 'Regular', value: 'regular' },
270
+ { label: 'Bold', value: 'bold' }
271
+ ];
272
+ const MySelect = () => {
273
+ const [selected, setSelected] = useState(options[1]);
274
+
275
+ return (
276
+ <div style={{ maxWidth: '380px' }} className="light-green-primary">
277
+ <Select
278
+ options={options}
279
+ value={selected}
280
+ onChange={value => setSelected(value)}
281
+ className="select-with-contrast-primary"
282
+ size="m"
283
+ placeholder="Select font weight..."
284
+ aria-label="Select font weight"
285
+ />
286
+ </div>
287
+ );
288
+ };
289
+
290
+ render(<MySelect />);
291
+ ```
292
+
293
+ ```postcss
294
+ .select-with-contrast-primary {
295
+ --rs-select-contrast-primary: 1;
296
+ }
297
+
298
+ .light-green-primary {
299
+ --rs-color-primary-light-theme: rgb(71, 224, 84);
300
+ --rs-color-primary-t-dim-light-theme: rgba(71, 224, 84, 0.8);
301
+ --rs-color-primary-t-fog-light-theme: rgba(71, 224, 84, 0.2);
302
+
303
+ --rs-color-primary-dark-theme: rgb(71, 224, 84);
304
+ --rs-color-primary-t-dim-dark-theme: rgba(71, 224, 84, 0.8);
305
+ --rs-color-primary-t-fog-dark-theme: rgba(71, 224, 84, 0.2);
306
+ }
307
+ ```
308
+
309
+ ## Native
310
+
311
+ ### Features showcase
312
+
313
+ #### Example:
314
+
315
+ ```tsx
316
+ import { useState } from 'react';
317
+ import { NativeSelect } from '@rescui/select';
318
+
319
+ const options = [
320
+ { label: 'Light', value: 'light' },
321
+ { label: 'Regular', value: 'regular' },
322
+ { label: 'Bold', value: 'bold' }
323
+ ];
324
+
325
+ const MySelect = () => {
326
+ const [selected, setSelected] = useState(options[1]);
327
+
328
+ return (
329
+ <div style={{ maxWidth: '380px' }}>
330
+ <NativeSelect
331
+ options={options}
332
+ value={selected}
333
+ onChange={value => setSelected(value)}
334
+ size="m"
335
+ placeholder="Select font weight..."
336
+ aria-label="Select font weight"
337
+ />
338
+ </div>
339
+ );
340
+ };
341
+
342
+ render(<MySelect />);
343
+ ```
344
+
345
+ #### Custom trigger for NativeSelect
346
+
347
+ ```tsx
348
+ import { useState } from 'react';
349
+ import { NativeSelect } from '@rescui/select';
350
+
351
+ const options = [
352
+ { label: 'Light', value: '300' },
353
+ { label: 'Regular', value: '400' },
354
+ { label: 'Bold', value: '600' }
355
+ ];
356
+ const MySelect = () => {
357
+ const [selected, setSelected] = useState(options[0]);
358
+
359
+ return (
360
+ <div style={{ maxWidth: '380px' }}>
361
+ <NativeSelect
362
+ options={options}
363
+ value={selected}
364
+ onChange={value => setSelected(value)}
365
+ placeholder="Select font weight..."
366
+ aria-label="Select font weight"
367
+ trigger={({ selectedLabel, selectedValue, hovered, focused }) => {
368
+ return (
369
+ <div
370
+ style={{
371
+ fontWeight: selectedValue,
372
+ borderRadius: '30px',
373
+ padding: '15px',
374
+ border: `1px solid ${focused || hovered ? '#aaa' : '#ddd'}`
375
+ }}
376
+ >
377
+ Hey, I am {selectedLabel}
378
+ </div>
379
+ );
380
+ }}
381
+ />
382
+ </div>
383
+ );
384
+ };
385
+
386
+ render(<MySelect />);
387
+ ```
388
+
389
+ ## CSS Mixins API
390
+
391
+ #### `Select` mixins
392
+
393
+ | Mixin name | Details |
394
+ | --- | --- |
395
+ | `rs-select-size-s` | prop: `size`<br><br>value: `"s"` |
396
+ | `rs-select-size-m` | prop: `size`<br><br>value: `"m"` |
397
+ | `rs-select-size-l` | prop: `size`<br><br>value: `"l"` |
398
+
399
+ ##### Demo
400
+
401
+ Resize the grey area below to see the effect.
402
+
403
+ You can use `@media` queries instead of `@container` queries:
404
+
405
+ ```tsx
406
+ import { useState } from 'react';
407
+ import { Select } from '@rescui/select';
408
+
409
+ const options = [
410
+ { label: 'St. Petersburg', value: 'spb' },
411
+ { label: 'Moscow', value: 'msk' },
412
+ { label: 'Novosibirsk', value: 'nsk' },
413
+ { label: 'Prague', value: 'prg' },
414
+ { label: 'Boston', value: 'bos' },
415
+ { label: 'Munich', value: 'muc' }
416
+ ];
417
+
418
+ const MySelect = ({ className }) => {
419
+ const [selected, setSelected] = useState(options[0]);
420
+
421
+ return (
422
+ <div style={{ maxWidth: '380px' }}>
423
+ <Select
424
+ options={options}
425
+ value={selected}
426
+ onChange={value => setSelected(value)}
427
+ size="m"
428
+ placeholder="Сhoose your favorite city..."
429
+ aria-label="Choose your favorite city"
430
+ className={className}
431
+ />
432
+ </div>
433
+ );
434
+ };
435
+
436
+ render(
437
+ <div className="select-preview-container">
438
+ <p style={{ position: 'absolute', right: 12, bottom: 12 }}>Resize me</p>
439
+ <MySelect className="my-select" />
440
+ </div>
441
+ );
442
+ ```
443
+
444
+ ```postcss attached
445
+ @import '@rescui/select/lib/public-api.p.css';
446
+
447
+ .select-preview-container {
448
+ position: relative;
449
+ container-type: inline-size;
450
+ resize: horizontal;
451
+ overflow: auto;
452
+ padding: 12px;
453
+ background-color: var(--rs-color-black-t5);
454
+ max-width: 800px;
455
+ }
456
+
457
+ .my-select {
458
+ @container (max-width: 640px) {
459
+ @mixin rs-select-size-l;
460
+ }
461
+ @container (max-width: 500px) {
462
+ @mixin rs-select-size-s;
463
+ }
464
+ }
465
+ ```
466
+
467
+ #### `Select` CSS Vars
468
+
469
+ | Variable | Syntax | Description |
470
+ | --- | --- | --- |
471
+ | `--rs-select-contrast-primary` | `0 \| 1` | The flag whenever the current primary is contrast enough and highlighted text color should be inverted |
472
+
473
+ Check out [this example](/components/select/#contrast-primary-color) for contrast primary.
474
+
475
+ #### `NativeSelect` mixins
476
+
477
+ | Mixin name | Details |
478
+ | --- | --- |
479
+ | `rs-native-select-size-s` | prop: `size`<br><br>value: `"s"` |
480
+ | `rs-native-select-size-m` | prop: `size`<br><br>value: `"m"` |
481
+ | `rs-native-select-size-l` | prop: `size`<br><br>value: `"l"` |
482
+
483
+ ##### Demo
484
+
485
+ Resize the grey area below to see the effect.
486
+
487
+ You can use `@media` queries instead of `@container` queries:
488
+
489
+ ```tsx
490
+ import { useState } from 'react';
491
+ import { NativeSelect } from '@rescui/select';
492
+
493
+ const options = [
494
+ { label: 'St. Petersburg', value: 'spb' },
495
+ { label: 'Moscow', value: 'msk' },
496
+ { label: 'Novosibirsk', value: 'nsk' },
497
+ { label: 'Prague', value: 'prg' },
498
+ { label: 'Boston', value: 'bos' },
499
+ { label: 'Munich', value: 'muc' }
500
+ ];
501
+
502
+ const MyNativeSelect = ({ className }) => {
503
+ const [selected, setSelected] = useState(options[0]);
504
+
505
+ return (
506
+ <div style={{ maxWidth: '380px' }}>
507
+ <NativeSelect
508
+ options={options}
509
+ value={selected}
510
+ onChange={value => setSelected(value)}
511
+ size="m"
512
+ placeholder="Сhoose your favorite city..."
513
+ aria-label="Choose your favorite city"
514
+ className={className}
515
+ />
516
+ </div>
517
+ );
518
+ };
519
+
520
+ render(
521
+ <div className="native-select-preview-container">
522
+ <p style={{ position: 'absolute', right: 12, bottom: 12 }}>Resize me</p>
523
+ <MyNativeSelect className="my-native-select" />
524
+ </div>
525
+ );
526
+ ```
527
+
528
+ ```postcss
529
+ @import '@rescui/select/lib/public-api.p.css';
530
+
531
+ .native-select-preview-container {
532
+ position: relative;
533
+ container-type: inline-size;
534
+ resize: horizontal;
535
+ overflow: auto;
536
+ padding: 12px;
537
+ background-color: var(--rs-color-black-t5);
538
+ max-width: 800px;
539
+ }
540
+
541
+ .my-native-select {
542
+ @container (max-width: 640px) {
543
+ @mixin rs-native-select-size-l;
544
+ }
545
+ @container (max-width: 500px) {
546
+ @mixin rs-native-select-size-s;
547
+ }
548
+ }
549
+ ```
package/llm/index.md ADDED
@@ -0,0 +1,3 @@
1
+ - Components and Hooks
2
+ - [Multiselect](./components/multiselect.md)
3
+ - [Select](./components/select.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rescui/select",
3
- "version": "0.17.1",
3
+ "version": "0.17.2-RUI-296-Build-docs-md-files-for-each-docs-section-b138dd5bf.15+b138dd5bf",
4
4
  "description": "",
5
5
  "license": "Apache-2.0",
6
6
  "author": "JetBrains",
@@ -9,6 +9,7 @@
9
9
  "src:main": "src/index.ts",
10
10
  "files": [
11
11
  "lib",
12
+ "llm",
12
13
  "LICENSE.txt"
13
14
  ],
14
15
  "publishConfig": {
@@ -16,10 +17,10 @@
16
17
  },
17
18
  "dependencies": {
18
19
  "@babel/runtime-corejs3": "^7.26.0",
19
- "@rescui/dropdown": "^0.10.1",
20
- "@rescui/form-field": "^0.1.3",
21
- "@rescui/tag": "^0.13.9",
22
- "@rescui/use-resize-observer": "^0.3.0",
20
+ "@rescui/dropdown": "0.10.2-RUI-296-Build-docs-md-files-for-each-docs-section-b138dd5bf.15+b138dd5bf",
21
+ "@rescui/form-field": "0.1.4-RUI-296-Build-docs-md-files-for-each-docs-section-b138dd5bf.19+b138dd5bf",
22
+ "@rescui/tag": "0.13.10-RUI-296-Build-docs-md-files-for-each-docs-section-b138dd5bf.15+b138dd5bf",
23
+ "@rescui/use-resize-observer": "0.3.1-RUI-296-Build-docs-md-files-for-each-docs-section-b138dd5bf.166+b138dd5bf",
23
24
  "classnames": "^2.2.6",
24
25
  "downshift": "^9.3.2",
25
26
  "scroll-into-view-if-needed": "3.1.0"
@@ -30,10 +31,10 @@
30
31
  "react": ">=16.8.0 <20"
31
32
  },
32
33
  "devDependencies": {
33
- "@rescui/colors": "^0.3.0",
34
- "@rescui/scripts": "^0.5.1",
35
- "@rescui/typography": "^0.27.0",
36
- "@rescui/visual-regression": "^0.1.4",
34
+ "@rescui/colors": "0.3.1-RUI-296-Build-docs-md-files-for-each-docs-section-b138dd5bf.54+b138dd5bf",
35
+ "@rescui/scripts": "0.5.2",
36
+ "@rescui/typography": "0.27.1-RUI-296-Build-docs-md-files-for-each-docs-section-b138dd5bf.19+b138dd5bf",
37
+ "@rescui/visual-regression": "0.1.4",
37
38
  "sniffr": "^1.2.0"
38
39
  },
39
40
  "scripts": {
@@ -41,5 +42,5 @@
41
42
  "build-pcss-api": "rescui-scripts build-pcss-api --file public-api.p.css"
42
43
  },
43
44
  "nx": {},
44
- "gitHead": "44a67a99c883ea9157498860898acd2d0932321c"
45
+ "gitHead": "b138dd5bf3dbdca6c21fa339dd567517d5c5d1d9"
45
46
  }