Skip to content

Commit e3eaeba

Browse files
nikolaykosttenphi
andauthored
fix(menu): prop to display menu checkbox (#151)
* fix: use param to display checkboxes * fix: after merge * chore: add changeset * fix(Menu): change icon and selectionType -> selectionIcon * fix(Menu): change checkbox story Co-authored-by: Andrey Yamanov <[email protected]>
1 parent f45b927 commit e3eaeba

File tree

5 files changed

+91
-8
lines changed

5 files changed

+91
-8
lines changed

.changeset/stupid-rockets-clap.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@cube-dev/ui-kit": patch
3+
---
4+
5+
Adds prop `selectionType` for `Menu` component. That stands for values `checkbox` or `radio`.
6+
7+
```jsx
8+
<Menu selectionType="checkbox" selectionMode="single">
9+
<Item key="1">Item 1</Item>
10+
<Item key="2">Item 2</Item>
11+
</Menu>
12+
```
13+

src/components/pickers/Menu/Menu.stories.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,36 @@ export const MenuSelectableMultiple = (props) => {
199199
});
200200
};
201201

202+
export const MenuSelectableCheckboxes = (props) => {
203+
const [selectedKeys, setSelectedKeys] = useState(['1', '2']);
204+
const onSelectionChange = (key) => {
205+
setSelectedKeys(key);
206+
};
207+
208+
return MenuTemplate({
209+
...props,
210+
selectionIcon: 'checkbox',
211+
selectionMode: 'multiple',
212+
selectedKeys,
213+
onSelectionChange,
214+
});
215+
};
216+
217+
export const MenuSelectableRadio = (props) => {
218+
const [selectedKeys, setSelectedKeys] = useState(['1']);
219+
const onSelectionChange = (key) => {
220+
setSelectedKeys(key);
221+
};
222+
223+
return MenuTemplate({
224+
...props,
225+
selectionIcon: 'radio',
226+
selectionMode: 'single',
227+
selectedKeys,
228+
onSelectionChange,
229+
});
230+
};
231+
202232
export const PaymentDetails = (props) => {
203233
return (
204234
<Root>

src/components/pickers/Menu/Menu.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ import {
1919
import { StyledMenu, StyledMenuHeader } from './styled';
2020
import { MenuItem } from './MenuItem';
2121
import { MenuSection } from './MenuSection';
22-
import { MenuButtonProps } from './MenuButton';
22+
import { MenuButtonProps, MenuSelectionType } from './MenuButton';
2323
import { useMenuContext } from './context';
2424

2525
export interface CubeMenuProps<T>
2626
extends ContainerStyleProps,
2727
AriaMenuProps<T> {
28+
selectionIcon?: MenuSelectionType;
2829
header?: ReactNode;
2930
footer?: ReactNode;
3031
styles?: Styles;
@@ -34,7 +35,7 @@ function Menu<T extends object>(
3435
props: CubeMenuProps<T>,
3536
ref: DOMRef<HTMLUListElement>,
3637
) {
37-
const { header, footer } = props;
38+
const { header, footer, selectionIcon } = props;
3839
const domRef = useDOMRef(ref);
3940
const contextProps = useMenuContext();
4041
const completeProps = mergeProps(contextProps, props);
@@ -70,6 +71,7 @@ function Menu<T extends object>(
7071
key={item.key}
7172
item={item}
7273
state={state}
74+
selectionIcon={selectionIcon}
7375
onAction={completeProps.onAction}
7476
/>
7577
);
@@ -80,6 +82,7 @@ function Menu<T extends object>(
8082
key={item.key}
8183
item={item}
8284
state={state}
85+
selectionIcon={selectionIcon}
8386
onAction={completeProps.onAction}
8487
/>
8588
);

src/components/pickers/Menu/MenuButton.tsx

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { ReactNode } from 'react';
22
import { Button, CubeButtonProps } from '../../actions';
33
import { Text } from '../../content/Text';
4-
import { Styles } from '../../../tasty';
4+
import { Styles, tasty } from '../../../tasty';
55
import { Space } from '../../layout/Space';
6-
import { CheckOutlined } from '@ant-design/icons';
6+
import { CheckOutlined, CheckCircleOutlined } from '@ant-design/icons';
77

88
const ACTION_BUTTON: Styles = {
99
border: {
@@ -30,6 +30,8 @@ const ACTION_BUTTON: Styles = {
3030
padding: {
3131
'': '(0.75x - 1px) (1.5x - 1px)',
3232
'selectable & !selected':
33+
'(0.75x - 1px) (1.5x - 1px) (0.75x - 1px) (1.5x - 1px)',
34+
'selectionIcon & selectable & !selected':
3335
'(0.75x - 1px) (1.5x - 1px) (0.75x - 1px) (1.5x - 1px + 22px)',
3436
},
3537
display: 'flex',
@@ -45,6 +47,23 @@ const ACTION_BUTTON: Styles = {
4547
},
4648
};
4749

50+
const RadioIcon = tasty({
51+
styles: {
52+
display: 'flex',
53+
width: '1.875x',
54+
placeContent: 'center',
55+
56+
'&::before': {
57+
display: 'block',
58+
content: '""',
59+
width: '1x',
60+
height: '1x',
61+
radius: 'round',
62+
fill: '#current',
63+
},
64+
},
65+
});
66+
4867
const getPostfix = (postfix) =>
4968
typeof postfix === 'string' ? (
5069
<Text nowrap color="inherit" data-element="Postfix">
@@ -54,22 +73,38 @@ const getPostfix = (postfix) =>
5473
postfix
5574
);
5675

76+
export type MenuSelectionType = 'checkbox' | 'radio';
77+
5778
export type MenuButtonProps = {
5879
postfix: ReactNode;
80+
selectionIcon?: MenuSelectionType;
5981
isSelectable?: boolean;
6082
disabled?: boolean;
6183
} & CubeButtonProps;
6284

85+
const getSelectionTypeIcon = (selectionIcon?: MenuSelectionType) => {
86+
switch (selectionIcon) {
87+
case 'checkbox':
88+
return <CheckOutlined />;
89+
case 'radio':
90+
return <RadioIcon />;
91+
default:
92+
return null;
93+
}
94+
};
95+
6396
export function MenuButton({
6497
children,
6598
icon,
6699
postfix,
67100
...props
68101
}: MenuButtonProps) {
69-
const { isSelected, isSelectable } = props;
70-
const checkIcon = isSelectable && isSelected ? <CheckOutlined /> : null;
102+
const { selectionIcon, isSelected, isSelectable } = props;
103+
const checkIcon =
104+
isSelectable && isSelected ? getSelectionTypeIcon(selectionIcon) : null;
71105
const mods = {
72106
...props.mods,
107+
selectionIcon: !!selectionIcon,
73108
selectable: isSelectable,
74109
selected: isSelected,
75110
};

src/components/pickers/Menu/MenuItem.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,19 @@ import { useMenuItem } from '@react-aria/menu';
77
import { mergeProps, ClearSlots, SlotProvider } from '../../../utils/react';
88
import { useMenuContext } from './context';
99
import { StyledMenuItem } from './styled';
10-
import { MenuButton } from './MenuButton';
10+
import { MenuButton, MenuSelectionType } from './MenuButton';
1111

1212
export interface MenuItemProps<T> {
1313
item: Node<T>;
1414
state: TreeState<T>;
15+
selectionIcon?: MenuSelectionType;
1516
isVirtualized?: boolean;
1617
onAction?: (key: Key) => void;
1718
}
1819

1920
/** @private */
2021
export function MenuItem<T>(props: MenuItemProps<T>) {
21-
const { item, state, isVirtualized, onAction } = props;
22+
const { item, state, selectionIcon, isVirtualized, onAction } = props;
2223
const { onClose, closeOnSelect } = useMenuContext();
2324
const { rendered, key, props: itemProps } = item;
2425

@@ -48,6 +49,7 @@ export function MenuItem<T>(props: MenuItemProps<T>) {
4849
typeof rendered === 'string' ? (
4950
<MenuButton
5051
{...itemProps}
52+
selectionIcon={selectionIcon}
5153
isSelectable={isSelectable}
5254
isSelected={isSelected}
5355
isDisabled={isDisabled}

0 commit comments

Comments
 (0)