@salutejs/plasma-new-hope 0.78.0-canary.1201.8983771283.0 → 0.78.0-canary.1201.8986419693.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. package/cjs/components/Dropdown/Dropdown.js +2 -2
  2. package/cjs/components/Dropdown/Dropdown.js.map +1 -1
  3. package/cjs/components/Dropdown/hooks/useHashMaps.js +2 -2
  4. package/cjs/components/Dropdown/hooks/useHashMaps.js.map +1 -1
  5. package/cjs/components/Dropdown/hooks/useKeyboardNavigation.js +185 -161
  6. package/cjs/components/Dropdown/hooks/useKeyboardNavigation.js.map +1 -1
  7. package/cjs/components/Dropdown/ui/DropdownInner/DropdownInner.js +1 -1
  8. package/cjs/components/Dropdown/ui/DropdownInner/DropdownInner.js.map +1 -1
  9. package/es/components/Dropdown/Dropdown.js +2 -2
  10. package/es/components/Dropdown/Dropdown.js.map +1 -1
  11. package/es/components/Dropdown/hooks/useHashMaps.js +2 -2
  12. package/es/components/Dropdown/hooks/useHashMaps.js.map +1 -1
  13. package/es/components/Dropdown/hooks/useKeyboardNavigation.js +185 -161
  14. package/es/components/Dropdown/hooks/useKeyboardNavigation.js.map +1 -1
  15. package/es/components/Dropdown/ui/DropdownInner/DropdownInner.js +1 -1
  16. package/es/components/Dropdown/ui/DropdownInner/DropdownInner.js.map +1 -1
  17. package/package.json +2 -2
  18. package/styled-components/cjs/components/Dropdown/Dropdown.js +2 -2
  19. package/styled-components/cjs/components/Dropdown/Dropdown.template-doc.mdx +339 -0
  20. package/styled-components/cjs/components/Dropdown/hooks/useHashMaps.js +2 -2
  21. package/styled-components/cjs/components/Dropdown/hooks/useKeyboardNavigation.js +189 -161
  22. package/styled-components/cjs/components/Dropdown/ui/DropdownInner/DropdownInner.js +1 -1
  23. package/styled-components/cjs/examples/plasma_web/components/Dropdown/Dropdown.config.js +25 -0
  24. package/styled-components/cjs/examples/plasma_web/components/Dropdown/Dropdown.js +11 -0
  25. package/styled-components/cjs/examples/plasma_web/components/Dropdown/Dropdown.stories.tsx +292 -0
  26. package/styled-components/es/components/Dropdown/Dropdown.js +2 -2
  27. package/styled-components/es/components/Dropdown/Dropdown.template-doc.mdx +339 -0
  28. package/styled-components/es/components/Dropdown/hooks/useHashMaps.js +2 -2
  29. package/styled-components/es/components/Dropdown/hooks/useKeyboardNavigation.js +189 -161
  30. package/styled-components/es/components/Dropdown/ui/DropdownInner/DropdownInner.js +1 -1
  31. package/styled-components/es/examples/plasma_web/components/Dropdown/Dropdown.config.js +19 -0
  32. package/styled-components/es/examples/plasma_web/components/Dropdown/Dropdown.js +5 -0
  33. package/styled-components/es/examples/plasma_web/components/Dropdown/Dropdown.stories.tsx +292 -0
  34. package/types/components/Dropdown/hooks/useKeyboardNavigation.d.ts.map +1 -1
  35. package/types/examples/plasma_web/components/Dropdown/Dropdown.config.d.ts +18 -0
  36. package/types/examples/plasma_web/components/Dropdown/Dropdown.config.d.ts.map +1 -0
  37. package/types/examples/plasma_web/components/Dropdown/Dropdown.d.ts +13 -0
  38. package/types/examples/plasma_web/components/Dropdown/Dropdown.d.ts.map +1 -0
@@ -0,0 +1,292 @@
1
+ import React from 'react';
2
+ import type { ComponentProps } from 'react';
3
+ import type { Meta, StoryObj } from '@storybook/react';
4
+ import { action } from '@storybook/addon-actions';
5
+
6
+ import { WithTheme } from '../../../_helpers';
7
+ import { Button } from '../Button/Button';
8
+ import type { DropdownPlacement, DropdownTrigger } from '../../../../components/Dropdown/Dropdown.types';
9
+
10
+ import { Dropdown } from './Dropdown';
11
+
12
+ type StoryDropdownProps = ComponentProps<typeof Dropdown>;
13
+
14
+ const placements: Array<DropdownPlacement> = ['top', 'bottom', 'right', 'left', 'auto'];
15
+ const triggers: Array<DropdownTrigger> = ['click', 'hover'];
16
+ const size = ['xs', 's', 'm', 'l'];
17
+ const variant = ['normal', 'tight'];
18
+
19
+ const meta: Meta<StoryDropdownProps> = {
20
+ title: 'plasma_web/Dropdown',
21
+ decorators: [WithTheme],
22
+ component: Dropdown,
23
+ argTypes: {
24
+ placement: {
25
+ options: placements,
26
+ control: {
27
+ type: 'select',
28
+ },
29
+ },
30
+ trigger: {
31
+ options: triggers,
32
+ control: {
33
+ type: 'select',
34
+ },
35
+ },
36
+ size: {
37
+ options: size,
38
+ control: {
39
+ type: 'select',
40
+ },
41
+ },
42
+ variant: {
43
+ options: variant,
44
+ control: {
45
+ type: 'select',
46
+ },
47
+ },
48
+ },
49
+ args: {
50
+ trigger: 'click',
51
+ offset: [0, 0],
52
+ listWidth: 'auto',
53
+ hasArrow: true,
54
+ closeOnOverlayClick: true,
55
+ closeOnSelect: true,
56
+ size: 'm',
57
+ variant: 'normal',
58
+ },
59
+ };
60
+
61
+ export default meta;
62
+
63
+ const items = [
64
+ {
65
+ value: 'north_america',
66
+ label: 'Северная Америка',
67
+ },
68
+ {
69
+ value: 'south_america',
70
+ label: 'Южная Америка',
71
+ items: [
72
+ {
73
+ value: 'brazil',
74
+ label: 'Бразилия',
75
+ items: [
76
+ {
77
+ value: 'rio_de_janeiro',
78
+ label: 'Рио-де-Жанейро',
79
+ },
80
+ {
81
+ value: 'sao_paulo',
82
+ label: 'Сан-Паулу',
83
+ },
84
+ ],
85
+ },
86
+ {
87
+ value: 'argentina',
88
+ label: 'Аргентина',
89
+ items: [
90
+ {
91
+ value: 'buenos_aires',
92
+ label: 'Буэнос-Айрес',
93
+ },
94
+ {
95
+ value: 'cordoba',
96
+ label: 'Кордова',
97
+ },
98
+ ],
99
+ },
100
+ {
101
+ value: 'colombia',
102
+ label: 'Колумбия',
103
+ items: [
104
+ {
105
+ value: 'bogota',
106
+ label: 'Богота',
107
+ },
108
+ {
109
+ value: 'medellin',
110
+ label: 'Медельин',
111
+ },
112
+ ],
113
+ },
114
+ ],
115
+ },
116
+ {
117
+ value: 'europe',
118
+ label: 'Европа',
119
+ items: [
120
+ {
121
+ value: 'france',
122
+ label: 'Франция',
123
+ items: [
124
+ {
125
+ value: 'paris',
126
+ label: 'Париж',
127
+ },
128
+ {
129
+ value: 'lyon',
130
+ label: 'Лион',
131
+ },
132
+ ],
133
+ },
134
+ {
135
+ value: 'germany',
136
+ label: 'Германия',
137
+ items: [
138
+ {
139
+ value: 'berlin',
140
+ label: 'Берлин',
141
+ },
142
+ {
143
+ value: 'munich',
144
+ label: 'Мюнхен',
145
+ },
146
+ ],
147
+ },
148
+ {
149
+ value: 'italy',
150
+ label: 'Италия',
151
+ items: [
152
+ {
153
+ value: 'rome',
154
+ label: 'Рим',
155
+ },
156
+ {
157
+ value: 'milan',
158
+ label: 'Милан',
159
+ },
160
+ ],
161
+ },
162
+ {
163
+ value: 'spain',
164
+ label: 'Испания',
165
+ items: [
166
+ {
167
+ value: 'madrid',
168
+ label: 'Мадрид',
169
+ },
170
+ {
171
+ value: 'barcelona',
172
+ label: 'Барселона',
173
+ },
174
+ ],
175
+ },
176
+ {
177
+ value: 'united_kingdom',
178
+ label: 'Великобритания',
179
+ items: [
180
+ {
181
+ value: 'london',
182
+ label: 'Лондон',
183
+ },
184
+ {
185
+ value: 'manchester',
186
+ label: 'Манчестер',
187
+ },
188
+ ],
189
+ },
190
+ ],
191
+ },
192
+ {
193
+ value: 'asia',
194
+ label: 'Азия',
195
+ items: [
196
+ {
197
+ value: 'china',
198
+ label: 'Китай',
199
+ items: [
200
+ {
201
+ value: 'beijing',
202
+ label: 'Пекин',
203
+ },
204
+ {
205
+ value: 'shanghai',
206
+ label: 'Шанхай',
207
+ },
208
+ ],
209
+ },
210
+ {
211
+ value: 'japan',
212
+ label: 'Япония',
213
+ items: [
214
+ {
215
+ value: 'tokyo',
216
+ label: 'Токио',
217
+ },
218
+ {
219
+ value: 'osaka',
220
+ label: 'Осака',
221
+ },
222
+ ],
223
+ },
224
+ {
225
+ value: 'india',
226
+ label: 'Индия',
227
+ items: [
228
+ {
229
+ value: 'delhi',
230
+ label: 'Дели',
231
+ },
232
+ {
233
+ value: 'mumbai',
234
+ label: 'Мумбаи',
235
+ },
236
+ ],
237
+ },
238
+ {
239
+ value: 'south_korea',
240
+ label: 'Южная Корея',
241
+ items: [
242
+ {
243
+ value: 'seoul',
244
+ label: 'Сеул',
245
+ },
246
+ {
247
+ value: 'busan',
248
+ label: 'Пусан',
249
+ },
250
+ ],
251
+ },
252
+ {
253
+ value: 'thailand',
254
+ label: 'Таиланд',
255
+ items: [
256
+ {
257
+ value: 'bangkok',
258
+ label: 'Бангкок',
259
+ },
260
+ {
261
+ value: 'phuket',
262
+ label: 'Пхукет',
263
+ },
264
+ ],
265
+ },
266
+ ],
267
+ },
268
+ {
269
+ value: 'africa',
270
+ label: 'Африка',
271
+ disabled: true,
272
+ },
273
+ ];
274
+
275
+ const StoryNormal = (args: StoryDropdownProps) => {
276
+ return (
277
+ <Dropdown
278
+ {...args}
279
+ items={items}
280
+ onToggle={action('onToggle')}
281
+ onHover={action('onHover')}
282
+ onItemSelect={action('onItemSelect')}
283
+ onItemClick={action('onItemClick')}
284
+ >
285
+ <Button text="Список стран" />
286
+ </Dropdown>
287
+ );
288
+ };
289
+
290
+ export const Default: StoryObj<StoryDropdownProps> = {
291
+ render: (args) => <StoryNormal {...args} />,
292
+ };
@@ -122,7 +122,7 @@ export var dropdownRoot = function dropdownRoot(Root) {
122
122
  isFocusTrapped: false,
123
123
  target: childrenWithProps(children, {
124
124
  role: 'combobox',
125
- 'aria-controls': 'listbox1',
125
+ 'aria-controls': 'tree_level_1',
126
126
  'aria-expanded': isCurrentListOpen,
127
127
  'aria-activedescendant': getActiveDescendant(),
128
128
  onKeyDown: onKeyDown
@@ -132,7 +132,7 @@ export var dropdownRoot = function dropdownRoot(Root) {
132
132
  listHeight: listHeight,
133
133
  listOverflow: listOverflow,
134
134
  role: "tree",
135
- id: "listbox1",
135
+ id: "tree_level_1",
136
136
  listWidth: listWidth
137
137
  }, items.map(function (item, index) {
138
138
  return /*#__PURE__*/React.createElement(DropdownInner, {
@@ -0,0 +1,339 @@
1
+ ---
2
+ id: dropdown
3
+ title: Dropdown
4
+ ---
5
+
6
+ import { PropsTable, Description, StorybookLink } from '@site/src/components';
7
+ import Tabs from '@theme/Tabs';
8
+ import TabItem from '@theme/TabItem';
9
+
10
+ # Dropdown
11
+ Выпадающий многоуровневый список.
12
+
13
+ <StorybookLink name="Dropdown" />
14
+
15
+ <Description name="Dropdown" />
16
+ <PropsTable name="Dropdown" />
17
+
18
+ ## Использование
19
+ Обязательным параметром является только массив `items`. Внутри items может быть такой же вложенный массив items. Формат следующий:
20
+
21
+ ```tsx
22
+ type Items = Array<{
23
+ /**
24
+ * Значение у item
25
+ */
26
+ value: string | number;
27
+ /**
28
+ * Метка-подпись к item
29
+ */
30
+ label: string;
31
+ /**
32
+ * Список дочерних items.
33
+ */
34
+ items?: Items;
35
+ /**
36
+ * Item не активен
37
+ */
38
+ disabled?: boolean;
39
+ /**
40
+ * Слот для контента слева
41
+ */
42
+ contentLeft?: ReactNode;
43
+ /**
44
+ * Слот для контента справа
45
+ */
46
+ contentRight?: ReactNode;
47
+ /**
48
+ * Выбранный item.
49
+ * @deprecated использовать ContentLeft || ContentRight
50
+ */
51
+ isActive?: boolean;
52
+ /**
53
+ * Кастомный цвет текста
54
+ * @deprecated
55
+ */
56
+ color?: string;
57
+ /**
58
+ * Айтем не активен
59
+ * @deprecated использовать disabled
60
+ */
61
+ isDisabled?: boolean;
62
+ }>;
63
+ ```
64
+
65
+ ## Примеры
66
+
67
+ <Tabs>
68
+ <TabItem value="default" label="Default" default>
69
+ ```tsx live
70
+ import React from 'react';
71
+ import { Button, Dropdown } from '@salutejs/plasma-web';
72
+
73
+ export function App() {
74
+ const items = [
75
+ {
76
+ value: 'north_america',
77
+ label: 'Северная Америка',
78
+ },
79
+ {
80
+ value: 'south_america',
81
+ label: 'Южная Америка',
82
+ items: [
83
+ {
84
+ value: 'brazil',
85
+ label: 'Бразилия',
86
+ items: [
87
+ {
88
+ value: 'rio_de_janeiro',
89
+ label: 'Рио-де-Жанейро',
90
+ },
91
+ {
92
+ value: 'sao_paulo',
93
+ label: 'Сан-Паулу',
94
+ },
95
+ ],
96
+ },
97
+ {
98
+ value: 'argentina',
99
+ label: 'Аргентина',
100
+ },
101
+ ],
102
+ },
103
+ ];
104
+
105
+
106
+ return (
107
+ <div style={{ height:"300px" }}>
108
+ <Dropdown
109
+ items={items}
110
+ >
111
+ <Button text="Список стран" />
112
+ </Dropdown>
113
+ </div>
114
+ );
115
+ }
116
+ ```
117
+ </TabItem>
118
+
119
+ <TabItem value="size" label="Size">
120
+ Размер `Dropdown` задается с помощью свойства `size`.<br />
121
+ Возможные значения свойства: `"l"`, `"m"`, `"s"` или `"xs"`.
122
+
123
+ ```tsx live
124
+ import React from 'react';
125
+ import { Button, Dropdown } from '@salutejs/plasma-web';
126
+
127
+ export function App() {
128
+ const items = [
129
+ {
130
+ value: 'north_america',
131
+ label: 'Северная Америка',
132
+ },
133
+ {
134
+ value: 'south_america',
135
+ label: 'Южная Америка',
136
+ items: [
137
+ {
138
+ value: 'brazil',
139
+ label: 'Бразилия',
140
+ items: [
141
+ {
142
+ value: 'rio_de_janeiro',
143
+ label: 'Рио-де-Жанейро',
144
+ },
145
+ {
146
+ value: 'sao_paulo',
147
+ label: 'Сан-Паулу',
148
+ },
149
+ ],
150
+ },
151
+ {
152
+ value: 'argentina',
153
+ label: 'Аргентина',
154
+ },
155
+ ],
156
+ },
157
+ ];
158
+
159
+ return (
160
+ <div style={{ height: "300px", display: "flex", gap: "50px" }}>
161
+ <Dropdown
162
+ items={items}
163
+ size="l"
164
+ >
165
+ <Button text="Список стран" />
166
+ </Dropdown>
167
+
168
+ <Dropdown
169
+ items={items}
170
+ size="m" // default
171
+ >
172
+ <Button text="Список стран" />
173
+ </Dropdown>
174
+
175
+ <Dropdown
176
+ items={items}
177
+ size="s"
178
+ >
179
+ <Button text="Список стран" />
180
+ </Dropdown>
181
+
182
+ <Dropdown
183
+ items={items}
184
+ size="xs"
185
+ >
186
+ <Button text="Список стран" />
187
+ </Dropdown>
188
+ </div>
189
+ );
190
+ }
191
+ ```
192
+ </TabItem>
193
+
194
+ <TabItem value="placement" label="Placement">
195
+ Параметр `placement` принимает либо строку: `"top"`, `"right"`, `"bottom"`, `"left"`, `"auto"`, либо массив этих же значений, например: `["top", "bottom"]`.<br />
196
+ Default value для placement - `"bottom"`.
197
+
198
+ ```tsx live
199
+ import React from 'react';
200
+ import { Button, Dropdown } from '@salutejs/plasma-web';
201
+
202
+ export function App() {
203
+ const items = [
204
+ {
205
+ value: 'north_america',
206
+ label: 'Северная Америка',
207
+ },
208
+ {
209
+ value: 'south_america',
210
+ label: 'Южная Америка',
211
+ items: [
212
+ {
213
+ value: 'brazil',
214
+ label: 'Бразилия',
215
+ items: [
216
+ {
217
+ value: 'rio_de_janeiro',
218
+ label: 'Рио-де-Жанейро',
219
+ },
220
+ {
221
+ value: 'sao_paulo',
222
+ label: 'Сан-Паулу',
223
+ },
224
+ ],
225
+ },
226
+ {
227
+ value: 'argentina',
228
+ label: 'Аргентина',
229
+ },
230
+ ],
231
+ },
232
+ ];
233
+
234
+ return (
235
+ <div style={{ height: "300px", display: "flex", gap: "50px" }}>
236
+ <Dropdown
237
+ items={items}
238
+ >
239
+ <Button text="Список стран" />
240
+ </Dropdown>
241
+
242
+ <Dropdown
243
+ items={items}
244
+ placement="right"
245
+ >
246
+ <Button text="Список стран" />
247
+ </Dropdown>
248
+
249
+ <Dropdown
250
+ items={items}
251
+ placement="left"
252
+ >
253
+ <Button text="Список стран" />
254
+ </Dropdown>
255
+
256
+ <Dropdown
257
+ items={items}
258
+ placement={['top', 'bottom']}
259
+ >
260
+ <Button text="Список стран" />
261
+ </Dropdown>
262
+ </div>
263
+ );
264
+ }
265
+ ```
266
+ </TabItem>
267
+
268
+ <TabItem value="trigger" label="Trigger">
269
+ Параметр `trigger` принимает строку: `"click"` или `"hover"`.<br />
270
+ Default value - `"click"`.<br />
271
+ Для примера показано значение `"hover"`.
272
+
273
+ ```tsx live
274
+ import React from 'react';
275
+ import { Button, Dropdown } from '@salutejs/plasma-web';
276
+
277
+ export function App() {
278
+ const items = [
279
+ {
280
+ value: 'north_america',
281
+ label: 'Северная Америка',
282
+ },
283
+ {
284
+ value: 'south_america',
285
+ label: 'Южная Америка',
286
+ items: [
287
+ {
288
+ value: 'brazil',
289
+ label: 'Бразилия',
290
+ items: [
291
+ {
292
+ value: 'rio_de_janeiro',
293
+ label: 'Рио-де-Жанейро',
294
+ },
295
+ {
296
+ value: 'sao_paulo',
297
+ label: 'Сан-Паулу',
298
+ },
299
+ ],
300
+ },
301
+ {
302
+ value: 'argentina',
303
+ label: 'Аргентина',
304
+ },
305
+ ],
306
+ },
307
+ ];
308
+
309
+
310
+ return (
311
+ <div style={{ height:"300px" }}>
312
+ <Dropdown
313
+ items={items}
314
+ trigger="hover"
315
+ >
316
+ <Button text="Список стран" />
317
+ </Dropdown>
318
+ </div>
319
+ );
320
+ }
321
+ ```
322
+ </TabItem>
323
+ </Tabs>
324
+
325
+ ## Клавиатурная навигация
326
+
327
+ Данный компонент соответствует требования W3C: [Combobox](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) и частично [TreeView](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/).
328
+
329
+ - `Tab` - закрывает дропдаун. Перемещает фокус на следующий элемент на странице;
330
+ - `Enter` - открывает/закрывает дропдаун. Если на элементе - выбирает его;
331
+ - `Space` - открывает/закрывает дропдаун. Если на элементе - выбирает его;
332
+ - `Home` - открывает дропдаун и перемещает фокус на первый элемент;
333
+ - `End` - открывает дропдаун и перемещает фокус на последний элемент;
334
+ - `PageUp` - перемещает фокус на 10 элементов выше либо в начало списка;
335
+ - `PageDown` - перемещает фокус на 10 элементов ниже либо в конце списка;
336
+ - `ArrowUp` - открывает дропдаун и перемещает фокус на первый элемент. Перемещает фокус на один элемент выше. Если фокус на первом элементе - то в конец списка;
337
+ - `ArrowDown` - открывает дропдаун и перемещает фокус на первый элемент. Перемещает фокус на один элемент ниже. Если фокус на последнем элементе - то в начало списка;
338
+ - `ArrowRight` - если фокус на элементе вложенного списка - открывает его и перемещает фокус на первый элемент;
339
+ - `ArrowLeft` - закрывает текущий список и перемещает фокус на предыдущий;
@@ -1,6 +1,6 @@
1
1
  import { useMemo } from 'react';
2
- // Данный хук рекурсивно проходится по дереву items и создаем 2 мапы: мапу путей и мапу фокусов.
3
- // Нужно для получения информации всей об item, зная только путь до нее.
2
+ // Данный хук рекурсивно проходится по дереву items и создает 2 мапы: мапу путей и мапу фокусов.
3
+ // Нужно для получения всей информации об item, зная только путь до него.
4
4
 
5
5
  export var useHashMaps = function useHashMaps(items) {
6
6
  return useMemo(function () {