Skip to content

Commit 07c9bd8

Browse files
authored
feat: new Menu implementation (#2106)
1 parent 1743a02 commit 07c9bd8

24 files changed

+1587
-28
lines changed

src/components/Breadcrumbs/BreadcrumbsItem.tsx

+7-8
Original file line numberDiff line numberDiff line change
@@ -100,22 +100,21 @@ function BreadcrumbsItem(props: BreadcrumbsItemProps, ref: React.ForwardedRef<HT
100100
const active = !disabled && activeIndex === index;
101101
return (
102102
<ListItemView
103-
{...getItemProps({
104-
...restProps,
105-
...domProps,
106-
...linkProps,
107-
role: 'menuitem',
108-
active,
109-
})}
110103
ref={(node: HTMLElement | null) => {
111104
listItemsRef.current[index ?? 0] = node;
112105
}}
113106
nestedLevel={popupStyle === 'staircase' ? index : undefined}
114-
tabIndex={active ? 0 : -1}
115107
active={active}
116108
size="m"
117109
className={b('menu-link', props.className)}
118110
component={Element}
111+
componentProps={getItemProps({
112+
...restProps,
113+
...domProps,
114+
...linkProps,
115+
role: 'menuitem',
116+
tabIndex: active ? 0 : -1,
117+
})}
119118
disabled={disabled}
120119
>
121120
{children}

src/components/Button/Button.scss

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ $block: '.#{variables.$ns}button';
4949
transition: background-color 0.15s linear;
5050
}
5151

52-
&:hover {
52+
&:hover,
53+
&[aria-haspopup][aria-expanded='true'] {
5354
color: var(--g-button-text-color-hover, var(--_--text-color-hover));
5455

5556
&::before {

src/components/Button/README-ru.md

+27-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ LANDING_BLOCK-->
266266

267267
`loading` — когда в фоновом режиме выполняются асинхронные процессы.
268268

269-
`selected` — когда пользователь может может включить (**Enable**) или отключить (**Disable**) кнопку.
269+
`selected` — когда пользователь может включить (**Enable**) или отключить (**Disable**) кнопку.
270270

271271
<!--LANDING_BLOCK
272272
@@ -294,6 +294,32 @@ LANDING_BLOCK-->
294294

295295
<!--/GITHUB_BLOCK-->
296296

297+
### Переключатель меню
298+
299+
`Button` автоматически меняет свой внешний вид при передаче специальных aria-атрибутов (`aria-haspopup`, `aria-expanded`):
300+
301+
<!--LANDING_BLOCK
302+
303+
<ExampleBlock
304+
code={`
305+
<Button aria-haspopup="menu" aria-expanded="true">Menu</Button>
306+
`}
307+
>
308+
<UIKit.Button aria-haspopup="menu" aria-expanded="true">Menu</UIKit.Button>
309+
</ExampleBlock>
310+
311+
LANDING_BLOCK-->
312+
313+
<!--GITHUB_BLOCK-->
314+
315+
```tsx
316+
<Button aria-haspopup="menu" aria-expanded="true">
317+
Menu
318+
</Button>
319+
```
320+
321+
<!--/GITHUB_BLOCK-->
322+
297323
## Размер
298324

299325
Размер `Button` можно настроить с помощью свойства `size`. Размер по умолчанию — `m`.

src/components/Button/README.md

+26
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,32 @@ LANDING_BLOCK-->
294294

295295
<!--/GITHUB_BLOCK-->
296296

297+
### Menu trigger
298+
299+
`Button` automatically changes its appearance when corresponding aria-attributes (`aria-haspopup`, `aria-expanded`) are passed:
300+
301+
<!--LANDING_BLOCK
302+
303+
<ExampleBlock
304+
code={`
305+
<Button aria-haspopup="menu" aria-expanded="true">Menu</Button>
306+
`}
307+
>
308+
<UIKit.Button aria-haspopup="menu" aria-expanded="true">Menu</UIKit.Button>
309+
</ExampleBlock>
310+
311+
LANDING_BLOCK-->
312+
313+
<!--GITHUB_BLOCK-->
314+
315+
```tsx
316+
<Button aria-haspopup="menu" aria-expanded="true">
317+
Menu
318+
</Button>
319+
```
320+
321+
<!--/GITHUB_BLOCK-->
322+
297323
## Size
298324

299325
Use the `size` property to manage the `Button` size. The default size is `m`.

src/components/lab/ListItemView/ListItemView.scss

+19-11
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ $block: '.#{variables.$ns}lab-list-item-view';
2121
*/
2222
#{$block} {
2323
/* Sizes */
24-
--_--min-height: var(--g-list-item-view-min-height, #{variables.$m-height});
24+
--_--min-height: var(--g-list-item-view-min-height, 28px);
2525
--_--border-radius: var(--g-list-item-view-border-radius, var(--g-border-radius-m));
2626
--_--padding-inline: var(--g-list-item-view-padding-inline, var(--g-spacing-2));
2727
--_--padding-block: var(--g-list-item-view-padding-block, var(--g-spacing-1));
2828
--_--line-height: var(--g-list-item-view-line-height, 18px);
2929

3030
--_--controls-gap: var(--g-list-item-view-controls-gap, var(--g-spacing-1));
31-
--_--controls-size: var(--g-list-item-view-controls-size, #{variables.$s-height});
31+
--_--controls-size: var(--g-list-item-view-controls-size, 20px);
3232
--_--controls-border-radius: var(
3333
--g-list-item-view-controls-border-radius,
3434
var(--g-border-radius-s)
@@ -69,6 +69,9 @@ $block: '.#{variables.$ns}lab-list-item-view';
6969
}
7070

7171
#{$block} {
72+
@include mixins.button-reset();
73+
@include mixins.link-reset();
74+
7275
display: grid;
7376
box-sizing: border-box;
7477
grid-template:
@@ -81,6 +84,7 @@ $block: '.#{variables.$ns}lab-list-item-view';
8184
background: var(--_--background-color);
8285
color: var(--_--text-color);
8386

87+
width: 100%;
8488
padding-inline: var(--_--padding-inline);
8589
padding-block: var(--_--padding-block);
8690
outline: none;
@@ -166,48 +170,48 @@ $block: '.#{variables.$ns}lab-list-item-view';
166170

167171
&_size {
168172
&_s {
169-
--_--min-height: #{variables.$s-height};
173+
--_--min-height: 24px;
170174
--_--border-radius: var(--g-border-radius-s);
171175
--_--padding-inline: var(--g-spacing-2);
172176
--_--padding-block: var(--g-spacing-half);
173177

174178
--_--controls-gap: var(--g-spacing-2);
175-
--_--controls-size: #{variables.$xs-height};
179+
--_--controls-size: 20px;
176180
--_--controls-border-radius: var(--g-border-radius-xs);
177181
--_--controls-icon-size: 12px;
178182
}
179183

180184
&_m {
181-
--_--min-height: #{variables.$m-height};
185+
--_--min-height: 28px;
182186
--_--border-radius: var(--g-border-radius-m);
183187
--_--padding-inline: var(--g-spacing-2);
184188
--_--padding-block: var(--g-spacing-1);
185189

186190
--_--controls-gap: var(--g-spacing-2);
187-
--_--controls-size: #{variables.$s-height};
191+
--_--controls-size: 24px;
188192
--_--controls-border-radius: var(--g-border-radius-s);
189193
--_--controls-icon-size: 16px;
190194
}
191195

192196
&_l {
193-
--_--min-height: #{variables.$l-height};
197+
--_--min-height: 32px;
194198
--_--border-radius: var(--g-border-radius-l);
195199
--_--padding-inline: var(--g-spacing-2);
196200
--_--padding-block: var(--g-spacing-2);
197201
--_--controls-gap: var(--g-spacing-2);
198-
--_--controls-size: #{variables.$m-height};
202+
--_--controls-size: 24px;
199203
--_--controls-border-radius: var(--g-border-radius-m);
200204
--_--controls-icon-size: 16px;
201205
}
202206

203207
&_xl {
204-
--_--min-height: #{variables.$xl-height};
208+
--_--min-height: 36px;
205209
--_--border-radius: var(--g-border-radius-xl);
206210
--_--padding-inline: var(--g-spacing-2);
207211
--_--padding-block: var(--g-spacing-3);
208212

209213
--_--controls-gap: var(--g-spacing-2);
210-
--_--controls-size: #{variables.$l-height};
214+
--_--controls-size: 24px;
211215
--_--controls-border-radius: var(--g-border-radius-l);
212216
--_--controls-icon-size: 16px;
213217

@@ -216,6 +220,7 @@ $block: '.#{variables.$ns}lab-list-item-view';
216220
}
217221

218222
&_is-container {
223+
display: block;
219224
--_--padding-inline: 0;
220225
--_--padding-block: 0;
221226
}
@@ -224,7 +229,8 @@ $block: '.#{variables.$ns}lab-list-item-view';
224229
--_--background-color: var(--_--background-color-hover);
225230
}
226231

227-
&:hover {
232+
&:not(&_hovered_no):hover,
233+
&_hovered_yes {
228234
--_--background-color: var(--_--background-color-hover);
229235
}
230236

@@ -239,6 +245,8 @@ $block: '.#{variables.$ns}lab-list-item-view';
239245
}
240246

241247
&_disabled {
248+
pointer-events: none;
249+
242250
&,
243251
&:hover,
244252
&:focus {

src/components/lab/ListItemView/ListItemView.tsx

+15-7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface ListItemViewProps<T extends React.ElementType = 'div'> extends
2121
size?: 's' | 'm' | 'l' | 'xl';
2222
selected?: boolean;
2323
active?: boolean;
24+
hovered?: boolean;
2425
onClick?: (e: React.MouseEvent) => void;
2526
disabled?: boolean;
2627
selectionStyle?: 'check' | 'highlight' | 'none';
@@ -34,6 +35,7 @@ export interface ListItemViewProps<T extends React.ElementType = 'div'> extends
3435
endContent?: React.ReactNode;
3536
isContainer?: boolean;
3637
component?: T;
38+
componentProps?: React.ComponentProps<T>;
3739
}
3840

3941
export const ListItemView = React.forwardRef(ListItemViewComponent) as <
@@ -50,6 +52,7 @@ export function ListItemViewComponent(
5052
size,
5153
active,
5254
selected,
55+
hovered,
5356
disabled,
5457
onClick,
5558
selectionStyle,
@@ -60,33 +63,34 @@ export function ListItemViewComponent(
6063
children,
6164
isContainer = false,
6265
component: Component = 'div',
66+
componentProps,
6367
collapsible: _collapsible,
6468
description,
6569
draggable: _draggable,
6670
startContent: _startContent,
6771
endContent: _endContent,
6872
nestedLevel: _nestedLevel,
69-
...restProps
7073
} = props;
7174
const containerRef = React.useRef(null);
7275
const componentRef = useForkRef(containerRef, ref);
7376
return (
7477
<Component
7578
ref={componentRef}
76-
{...restProps}
79+
{...componentProps}
7780
{...filterDOMProps(props)}
7881
className={b(
7982
{
8083
size,
8184
selected: selected && selectionStyle === 'highlight',
8285
disabled,
8386
active,
87+
hovered: typeof hovered === 'boolean' && (hovered ? 'yes' : 'no'),
8488
'is-container': isContainer,
8589
'has-description': Boolean(description),
8690
},
87-
className,
91+
componentProps?.className ?? className,
8892
)}
89-
style={style}
93+
style={componentProps?.style ?? style}
9094
onClick={(e) => {
9195
if (disabled) {
9296
e.preventDefault();
@@ -101,15 +105,19 @@ export function ListItemViewComponent(
101105
return;
102106
}
103107

104-
if (typeof onClick === 'function') {
105-
onClick(e);
108+
if (
109+
typeof onClick === 'function' ||
110+
typeof componentProps?.onClick === 'function'
111+
) {
112+
onClick?.(e);
113+
componentProps?.onClick?.(e);
106114
} else if (typeof onCollapseChange === 'function') {
107115
onCollapseChange(!collapsed);
108116
}
109117
}}
110118
>
111119
{isContainer ? (
112-
<Slot name="container">{children}</Slot>
120+
children
113121
) : (
114122
<ListItemViewContent {...props}>{children}</ListItemViewContent>
115123
)}

src/components/lab/Menu/Menu.scss

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@use '../../variables';
2+
3+
$b: '.#{variables.$ns}lab-menu';
4+
5+
#{$b} {
6+
padding: 4px;
7+
}

0 commit comments

Comments
 (0)