@rescui/dropdown-menu 0.11.16 → 0.11.18

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 CHANGED
@@ -1,3 +1,5 @@
1
- # Dropdown Menu
1
+ # DropdownMenu
2
2
 
3
- * [Usage examples](./docs/demo.md)
3
+ ## LLM
4
+
5
+ To get the docs for the LLM, check out [this index](./llm/index.md).
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import React, { useRef, useState } from 'react';
2
+ import React, { useState } from 'react';
3
3
  import { useThemeWithUndefined } from '@rescui/ui-contexts';
4
4
  import { Dropdown } from '@rescui/dropdown';
5
5
  import { Menu } from '@rescui/menu';
@@ -33,18 +33,18 @@ const DropdownMenu = _ref => {
33
33
  className,
34
34
  ...restProps
35
35
  } = _ref;
36
- const menuWrapperRef = useRef(null);
36
+ const [menuWrapperEl, setMenuWrapperEl] = useState(null);
37
37
  const [height, setHeight] = useState(0);
38
38
  let itemHeightFromCss = null;
39
39
 
40
- if (menuWrapperRef.current) {
40
+ if (menuWrapperEl) {
41
41
  // In theory this part of the code might cause issues with SSR.
42
- // But in this specific context, because of checking ref "menuWrapperRef",
42
+ // But in this specific context, because of checking state ref "menuWrapperEl",
43
43
  // we are sure that the value will still be "null" on the first render.
44
44
  // Therefore, the first render on the client will be the same as on the server.
45
45
  // Also, there will be no layout shifts on the following renders because
46
46
  // it's a dropdown-content and in most cases it opens only on-demand
47
- const menuWrapperStyles = getComputedStyle(menuWrapperRef.current);
47
+ const menuWrapperStyles = getComputedStyle(menuWrapperEl);
48
48
  itemHeightFromCss = parseInt(menuWrapperStyles.getPropertyValue('--_rs-dropdown-menu-item-height'), 10) || null;
49
49
  }
50
50
 
@@ -69,7 +69,8 @@ const DropdownMenu = _ref => {
69
69
  maxHeight: maxHeight,
70
70
  className: cn(SIZE_TO_CLASS[size], styles.dropdownMenu, className)
71
71
  }, /*#__PURE__*/React.createElement(MenuWrapper, {
72
- ref: menuWrapperRef,
72
+ el: menuWrapperEl,
73
+ setEl: setMenuWrapperEl,
73
74
  onHeightUpdate: setHeight
74
75
  }, /*#__PURE__*/React.createElement(Menu, {
75
76
  size: size,
@@ -1,8 +1,8 @@
1
1
  var styles = {
2
- "sizeS": "_sizeS_xmd2pz_8",
3
- "sizeM": "_sizeM_xmd2pz_11",
4
- "sizeL": "_sizeL_xmd2pz_14",
5
- "dropdownMenu": "_dropdownMenu_xmd2pz_18",
6
- "menu": "_menu_xmd2pz_23"
2
+ "sizeS": "_sizeS_1iizzly_8",
3
+ "sizeM": "_sizeM_1iizzly_11",
4
+ "sizeL": "_sizeL_1iizzly_14",
5
+ "dropdownMenu": "_dropdownMenu_1iizzly_18",
6
+ "menu": "_menu_1iizzly_23"
7
7
  };
8
8
  export { styles as default };
package/lib/index.css CHANGED
@@ -1,29 +1,29 @@
1
- ._sizeS_xmd2pz_8{
1
+ ._sizeS_1iizzly_8{
2
2
  --_rs-dropdown-menu-min-width:316px;
3
3
  --_rs-dropdown-menu-max-width:389px;
4
4
  }
5
- ._sizeS_xmd2pz_8 [data-rs-internal='dropdown-menu__menu-wrapper']{
5
+ ._sizeS_1iizzly_8 [data-rs-internal='dropdown-menu__menu-wrapper']{
6
6
  --_rs-dropdown-menu-item-height:32px;
7
7
  }
8
- ._sizeM_xmd2pz_11{
8
+ ._sizeM_1iizzly_11{
9
9
  --_rs-dropdown-menu-min-width:316px;
10
10
  --_rs-dropdown-menu-max-width:494px;
11
11
  }
12
- ._sizeM_xmd2pz_11 [data-rs-internal='dropdown-menu__menu-wrapper']{
12
+ ._sizeM_1iizzly_11 [data-rs-internal='dropdown-menu__menu-wrapper']{
13
13
  --_rs-dropdown-menu-item-height:40px;
14
14
  }
15
- ._sizeL_xmd2pz_14{
15
+ ._sizeL_1iizzly_14{
16
16
  --_rs-dropdown-menu-min-width:316px;
17
17
  --_rs-dropdown-menu-max-width:494px;
18
18
  }
19
- ._sizeL_xmd2pz_14 [data-rs-internal='dropdown-menu__menu-wrapper']{
19
+ ._sizeL_1iizzly_14 [data-rs-internal='dropdown-menu__menu-wrapper']{
20
20
  --_rs-dropdown-menu-item-height:52px;
21
21
  }
22
- ._dropdownMenu_xmd2pz_18{
22
+ ._dropdownMenu_1iizzly_18{
23
23
  min-width:var(--_rs-dropdown-menu-min-width);
24
24
  max-width:var(--_rs-dropdown-menu-max-width);
25
25
  }
26
- ._menu_xmd2pz_23{
26
+ ._menu_1iizzly_23{
27
27
  margin:0;
28
28
  padding:0;
29
29
 
@@ -2,5 +2,10 @@ import React from 'react';
2
2
  export type MenuWrapperProps = {
3
3
  children?: React.ReactNode;
4
4
  onHeightUpdate?: (newHeight: number) => void;
5
+ el: HTMLDivElement | null;
6
+ setEl: (el: HTMLDivElement | null) => void;
7
+ };
8
+ export declare const MenuWrapper: {
9
+ ({ children, onHeightUpdate, setEl, el }: MenuWrapperProps): React.JSX.Element;
10
+ displayName: string;
5
11
  };
6
- export declare const MenuWrapper: React.ForwardRefExoticComponent<MenuWrapperProps & React.RefAttributes<HTMLDivElement>>;
@@ -1,15 +1,19 @@
1
1
  import React from 'react';
2
2
  import { useResizeObserver } from '@rescui/use-resize-observer';
3
- const MenuWrapper = /*#__PURE__*/React.forwardRef((_ref, ref) => {
3
+
4
+ const MenuWrapper = _ref => {
4
5
  let {
5
6
  children,
6
- onHeightUpdate
7
+ onHeightUpdate,
8
+ setEl,
9
+ el
7
10
  } = _ref;
8
- useResizeObserver(ref, entry => onHeightUpdate(entry.target.offsetHeight));
11
+ useResizeObserver(el, entry => onHeightUpdate(entry.target.offsetHeight));
9
12
  return /*#__PURE__*/React.createElement("div", {
10
- ref: ref,
13
+ ref: setEl,
11
14
  "data-rs-internal": 'dropdown-menu__menu-wrapper'
12
15
  }, children);
13
- });
16
+ };
17
+
14
18
  MenuWrapper.displayName = 'MenuWrapper';
15
19
  export { MenuWrapper };
@@ -0,0 +1,344 @@
1
+ # DropdownMenu
2
+
3
+ DropdownMenu is a special type of Dropdown component with predefined dimensions for correctly displaying overflowing menus. Compared to the standard Dropdown option, it also offers keyboard navigation and a simpler API for making menus.
4
+
5
+ ## Usage
6
+
7
+ ### Features showcase
8
+
9
+ #### Basic example
10
+
11
+ ```tsx
12
+ import { useState } from 'react';
13
+ import { DropdownMenu, MenuItem } from '@rescui/dropdown-menu';
14
+ import { Button } from '@rescui/button';
15
+
16
+ const Demo = () => {
17
+ const [isOpen, setIsOpen] = useState(false);
18
+
19
+ const toggleIsOpen = () => setIsOpen(s => !s);
20
+
21
+ return (
22
+ <DropdownMenu
23
+ isOpen={isOpen}
24
+ onRequestClose={() => setIsOpen(false)}
25
+ trigger={<Button onClick={toggleIsOpen}>Open Menu</Button>}
26
+ >
27
+ <MenuItem>Some text 1</MenuItem>
28
+ <MenuItem>Some text 2</MenuItem>
29
+ <MenuItem>Some text 3</MenuItem>
30
+ <MenuItem>Some text 4</MenuItem>
31
+ <MenuItem>Some text 5</MenuItem>
32
+ <MenuItem>Some text 6</MenuItem>
33
+ <MenuItem>Some text 7</MenuItem>
34
+ <MenuItem>Some text 8</MenuItem>
35
+ </DropdownMenu>
36
+ );
37
+ };
38
+
39
+ render(<Demo />);
40
+ ```
41
+
42
+ #### Links, buttons, divider
43
+
44
+ ```tsx
45
+ import { useState } from 'react';
46
+ import { DropdownMenu, MenuItem } from '@rescui/dropdown-menu';
47
+ import { Divider } from '@rescui/menu';
48
+ import { Button } from '@rescui/button';
49
+ import { ArrowTopRightOutlineIcon } from '@rescui/icons';
50
+
51
+ const Demo = () => {
52
+ const [isOpen, setIsOpen] = useState(false);
53
+
54
+ const toggleIsOpen = () => setIsOpen(s => !s);
55
+
56
+ return (
57
+ <DropdownMenu
58
+ isOpen={isOpen}
59
+ onRequestClose={() => setIsOpen(false)}
60
+ trigger={<Button onClick={toggleIsOpen}>Open Menu</Button>}
61
+ >
62
+ <MenuItem href="#somewhere1">Some link 1</MenuItem>
63
+ <MenuItem href="#somewhere2">Some link 2</MenuItem>
64
+ <MenuItem onClick={() => alert(3)}>Some button 3</MenuItem>
65
+ <MenuItem href="#somewhere4" onClick={() => alert(4)}>
66
+ Some link 4
67
+ </MenuItem>
68
+ <Divider />
69
+ <MenuItem
70
+ href="#somewhere5"
71
+ icon={<ArrowTopRightOutlineIcon />}
72
+ iconPlacement="right"
73
+ >
74
+ External link 5
75
+ </MenuItem>
76
+ <MenuItem onClick={() => alert(6)}>Some button 6</MenuItem>
77
+ </DropdownMenu>
78
+ );
79
+ };
80
+
81
+ render(<Demo />);
82
+ ```
83
+
84
+ #### Select first item on open by keyboard
85
+
86
+ ```tsx
87
+ import { useState } from 'react';
88
+ import { DropdownMenu, MenuItem } from '@rescui/dropdown-menu';
89
+ import { Button } from '@rescui/button';
90
+
91
+ const Demo = () => {
92
+ const [isOpenBy, setIsOpenBy] = useState(null);
93
+
94
+ const handleClick = () => setIsOpenBy(s => !s && 'mouse');
95
+
96
+ const handleKeyDown = e => {
97
+ if (e.code === 'ArrowDown' || e.code === 'Enter' || e.code === 'Space') {
98
+ e.preventDefault();
99
+ setIsOpenBy('keyboard');
100
+ }
101
+ };
102
+
103
+ return (
104
+ <DropdownMenu
105
+ isOpen={Boolean(isOpenBy)}
106
+ onRequestClose={() => setIsOpenBy(null)}
107
+ activateFirstItemOnOpen={isOpenBy === 'keyboard'}
108
+ trigger={
109
+ <Button onClick={handleClick} onKeyDown={handleKeyDown}>
110
+ Open Menu
111
+ </Button>
112
+ }
113
+ >
114
+ <MenuItem>Some text 1</MenuItem>
115
+ <MenuItem>Some text 2</MenuItem>
116
+ <MenuItem>Some text 3</MenuItem>
117
+ <MenuItem>Some text 4</MenuItem>
118
+ <MenuItem>Some text 5</MenuItem>
119
+ <MenuItem>Some text 6</MenuItem>
120
+ <MenuItem>Some text 7</MenuItem>
121
+ <MenuItem>Some text 8</MenuItem>
122
+ </DropdownMenu>
123
+ );
124
+ };
125
+
126
+ render(<Demo />);
127
+ ```
128
+
129
+ #### Rock with glow effect
130
+
131
+ ```tsx
132
+ import { useState } from 'react';
133
+ import { DropdownMenu, MenuItem } from '@rescui/dropdown-menu';
134
+ import { Button } from '@rescui/button';
135
+ import { useGlowHover } from '@rescui/use-glow-hover';
136
+
137
+ const GlowMenuItem = ({ theme, ...restProps }) => {
138
+ const ref = useGlowHover({
139
+ lightSizeEnterAnimationTime: 0,
140
+ lightSizeLeaveAnimationTime: 0,
141
+ preset: 'bright-pink',
142
+ enableBurst: true
143
+ });
144
+ return <MenuItem {...restProps} ref={ref} />;
145
+ };
146
+
147
+ const Demo = () => {
148
+ const [isOpen, setIsOpen] = useState(false);
149
+
150
+ const toggleIsOpen = () => setIsOpen(s => !s);
151
+
152
+ return (
153
+ <DropdownMenu
154
+ mode="rock"
155
+ isOpen={isOpen}
156
+ onRequestClose={() => setIsOpen(false)}
157
+ trigger={<Button onClick={toggleIsOpen}>Open Menu</Button>}
158
+ >
159
+ <GlowMenuItem>Some text 1</GlowMenuItem>
160
+ <GlowMenuItem>Some text 2</GlowMenuItem>
161
+ <GlowMenuItem>Some text 3</GlowMenuItem>
162
+ <GlowMenuItem>Some text 4</GlowMenuItem>
163
+ <GlowMenuItem>Some text 5</GlowMenuItem>
164
+ <GlowMenuItem>Some text 6</GlowMenuItem>
165
+ <GlowMenuItem>Some text 7</GlowMenuItem>
166
+ <GlowMenuItem>Some text 8</GlowMenuItem>
167
+ </DropdownMenu>
168
+ );
169
+ };
170
+
171
+ render(<Demo />);
172
+ ```
173
+
174
+ #### With glow effect and selected support
175
+
176
+ ```tsx
177
+ import { useState } from 'react';
178
+ import { DropdownMenu, MenuItem } from '@rescui/dropdown-menu';
179
+ import { Button } from '@rescui/button';
180
+ import { useGlowHover } from '@rescui/use-glow-hover';
181
+
182
+ const GlowMenuItem = ({ theme, selected, ...restProps }) => {
183
+ const ref = useGlowHover({
184
+ lightSizeEnterAnimationTime: 0,
185
+ lightSizeLeaveAnimationTime: 0,
186
+ preset: 'bright-pink',
187
+ disabled: selected,
188
+ enableBurst: true
189
+ });
190
+ return <MenuItem {...restProps} selected={selected} ref={ref} />;
191
+ };
192
+
193
+ const Demo = () => {
194
+ const [isOpen, setIsOpen] = useState(false);
195
+
196
+ const toggleIsOpen = () => setIsOpen(s => !s);
197
+
198
+ return (
199
+ <DropdownMenu
200
+ mode={'rock'}
201
+ isOpen={isOpen}
202
+ onRequestClose={() => setIsOpen(false)}
203
+ trigger={<Button onClick={toggleIsOpen}>Open Menu</Button>}
204
+ >
205
+ <GlowMenuItem>Some text 1</GlowMenuItem>
206
+ <GlowMenuItem>Some text 2</GlowMenuItem>
207
+ <GlowMenuItem selected>Some text 3</GlowMenuItem>
208
+ <GlowMenuItem>Some text 4</GlowMenuItem>
209
+ <GlowMenuItem>Some text 5</GlowMenuItem>
210
+ <GlowMenuItem>Some text 6</GlowMenuItem>
211
+ <GlowMenuItem>Some text 7</GlowMenuItem>
212
+ <GlowMenuItem>Some text 8</GlowMenuItem>
213
+ </DropdownMenu>
214
+ );
215
+ };
216
+
217
+ render(<Demo />);
218
+ ```
219
+
220
+ #### With `minWidth`
221
+
222
+ ```tsx
223
+ import { useState } from 'react';
224
+ import { DropdownMenu, MenuItem } from '@rescui/dropdown-menu';
225
+ import { Button } from '@rescui/button';
226
+
227
+ const Demo = () => {
228
+ const [isOpen, setIsOpen] = useState(false);
229
+
230
+ const toggleIsOpen = () => setIsOpen(s => !s);
231
+
232
+ return (
233
+ <DropdownMenu
234
+ isOpen={isOpen}
235
+ onRequestClose={() => setIsOpen(false)}
236
+ trigger={<Button onClick={toggleIsOpen}>Open Menu</Button>}
237
+ minWidth="trigger"
238
+ >
239
+ <MenuItem>Some text 1</MenuItem>
240
+ <MenuItem>Some text 2</MenuItem>
241
+ <MenuItem>Some text 3</MenuItem>
242
+ <MenuItem>Some text 4</MenuItem>
243
+ <MenuItem>Some text 5</MenuItem>
244
+ <MenuItem>Some text 6</MenuItem>
245
+ <MenuItem>Some text 7</MenuItem>
246
+ <MenuItem>Some text 8</MenuItem>
247
+ </DropdownMenu>
248
+ );
249
+ };
250
+
251
+ render(<Demo />);
252
+ ```
253
+
254
+ #### With icons
255
+
256
+ ```tsx
257
+ import { useState } from 'react';
258
+ import { DropdownMenu, MenuItem } from '@rescui/dropdown-menu';
259
+ import { Button } from '@rescui/button';
260
+ import { PrintOutlineIcon } from '@rescui/icons';
261
+
262
+ const Demo = () => {
263
+ const [isOpen, setIsOpen] = useState(false);
264
+
265
+ const toggleIsOpen = () => setIsOpen(s => !s);
266
+
267
+ return (
268
+ <DropdownMenu
269
+ isOpen={isOpen}
270
+ onRequestClose={() => setIsOpen(false)}
271
+ trigger={<Button onClick={toggleIsOpen}>Open Menu</Button>}
272
+ size="l"
273
+ >
274
+ <MenuItem icon={<PrintOutlineIcon />}>Some text 1</MenuItem>
275
+ <MenuItem icon={<PrintOutlineIcon />}>Some text 2</MenuItem>
276
+ <MenuItem icon={<PrintOutlineIcon />}>Some text 3</MenuItem>
277
+ <MenuItem icon={<PrintOutlineIcon />}>Some text 4</MenuItem>
278
+ <MenuItem icon={<PrintOutlineIcon />}>Some text 5</MenuItem>
279
+ </DropdownMenu>
280
+ );
281
+ };
282
+
283
+ render(<Demo />);
284
+ ```
285
+
286
+ ## CSS Mixins API
287
+
288
+ #### `DropdownMenu` mixins
289
+
290
+ | Mixin name | Details |
291
+ | --- | --- |
292
+ | `rs-dropdown-menu-size-s` | prop: `size`<br><br>value: `"s"` |
293
+ | `rs-dropdown-menu-size-m` | prop: `size`<br><br>value: `"m"` |
294
+ | `rs-dropdown-menu-size-l` | prop: `size`<br><br>value: `"l"` |
295
+
296
+ ##### Demo
297
+
298
+ Use the controls to see the effect at different viewport widths.
299
+
300
+ ```tsx
301
+ import { useState } from 'react';
302
+ import { DropdownMenu, MenuItem } from '@rescui/dropdown-menu';
303
+ import { Button } from '@rescui/button';
304
+
305
+ const MyDropdownMenu = () => {
306
+ const [isOpen, setIsOpen] = useState(false);
307
+
308
+ const toggleIsOpen = () => setIsOpen(s => !s);
309
+ return (
310
+ <div style={{ height: 350 }}>
311
+ <DropdownMenu
312
+ isOpen={isOpen}
313
+ onRequestClose={() => setIsOpen(false)}
314
+ trigger={<Button onClick={toggleIsOpen}>Open Menu</Button>}
315
+ className="my-dropdown-menu"
316
+ >
317
+ <MenuItem>Some text 1</MenuItem>
318
+ <MenuItem>Some text 2</MenuItem>
319
+ <MenuItem>Some text 3</MenuItem>
320
+ <MenuItem>Some text 4</MenuItem>
321
+ <MenuItem>Some text 5</MenuItem>
322
+ <MenuItem>Some text 6</MenuItem>
323
+ <MenuItem>Some text 7</MenuItem>
324
+ <MenuItem>Some text 8</MenuItem>
325
+ </DropdownMenu>
326
+ </div>
327
+ );
328
+ };
329
+
330
+ render(<MyDropdownMenu />);
331
+ ```
332
+
333
+ ```postcss
334
+ @import '@rescui/dropdown-menu/lib/public-api.p.css';
335
+
336
+ .my-dropdown-menu {
337
+ @media (max-width: 640px) {
338
+ @mixin rs-dropdown-menu-size-l;
339
+ }
340
+ @media (max-width: 500px) {
341
+ @mixin rs-dropdown-menu-size-s;
342
+ }
343
+ }
344
+ ```
package/llm/index.md ADDED
@@ -0,0 +1,2 @@
1
+ - Components and Hooks
2
+ - [DropdownMenu](./components/dropdown-menu.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rescui/dropdown-menu",
3
- "version": "0.11.16",
3
+ "version": "0.11.18",
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,9 +17,9 @@
16
17
  },
17
18
  "dependencies": {
18
19
  "@babel/runtime-corejs3": "^7.26.0",
19
- "@rescui/dropdown": "^0.10.2",
20
- "@rescui/menu": "^0.13.6",
21
- "@rescui/use-resize-observer": "^0.3.1",
20
+ "@rescui/dropdown": "^0.11.0",
21
+ "@rescui/menu": "^0.13.7",
22
+ "@rescui/use-resize-observer": "^0.3.3",
22
23
  "classnames": "^2.2.6"
23
24
  },
24
25
  "peerDependencies": {
@@ -27,10 +28,10 @@
27
28
  "react": ">=16.8.0 <20"
28
29
  },
29
30
  "devDependencies": {
30
- "@rescui/colors": "^0.3.1",
31
- "@rescui/icons": "^1.8.1",
31
+ "@rescui/colors": "^0.3.2",
32
+ "@rescui/icons": "^1.8.2",
32
33
  "@rescui/scripts": "^0.5.2",
33
- "@rescui/typography": "^0.27.1",
34
+ "@rescui/typography": "^0.27.2",
34
35
  "@rescui/visual-regression": "^0.1.4"
35
36
  },
36
37
  "scripts": {
@@ -38,5 +39,5 @@
38
39
  "build-pcss-api": "rescui-scripts build-pcss-api --file public-api.p.css"
39
40
  },
40
41
  "nx": {},
41
- "gitHead": "a04f6790abce8e72834eedce3512ee83513c5e78"
42
+ "gitHead": "7aade8fced69c4a1ac61078a1daf2c1cd4f5a1c9"
42
43
  }