diff --git a/.changeset/stupid-rockets-clap.md b/.changeset/stupid-rockets-clap.md
new file mode 100644
index 00000000..afafc082
--- /dev/null
+++ b/.changeset/stupid-rockets-clap.md
@@ -0,0 +1,13 @@
+---
+"@cube-dev/ui-kit": patch
+---
+
+Adds prop `selectionType` for `Menu` component. That stands for values `checkbox` or `radio`.
+
+```jsx
+
+```
+
diff --git a/src/components/pickers/Menu/Menu.stories.tsx b/src/components/pickers/Menu/Menu.stories.tsx
index aebbdd2e..f9a360bc 100644
--- a/src/components/pickers/Menu/Menu.stories.tsx
+++ b/src/components/pickers/Menu/Menu.stories.tsx
@@ -199,6 +199,36 @@ export const MenuSelectableMultiple = (props) => {
});
};
+export const MenuSelectableCheckboxes = (props) => {
+ const [selectedKeys, setSelectedKeys] = useState(['1', '2']);
+ const onSelectionChange = (key) => {
+ setSelectedKeys(key);
+ };
+
+ return MenuTemplate({
+ ...props,
+ selectionIcon: 'checkbox',
+ selectionMode: 'multiple',
+ selectedKeys,
+ onSelectionChange,
+ });
+};
+
+export const MenuSelectableRadio = (props) => {
+ const [selectedKeys, setSelectedKeys] = useState(['1']);
+ const onSelectionChange = (key) => {
+ setSelectedKeys(key);
+ };
+
+ return MenuTemplate({
+ ...props,
+ selectionIcon: 'radio',
+ selectionMode: 'single',
+ selectedKeys,
+ onSelectionChange,
+ });
+};
+
export const PaymentDetails = (props) => {
return (
diff --git a/src/components/pickers/Menu/Menu.tsx b/src/components/pickers/Menu/Menu.tsx
index 17eda332..5e54470c 100644
--- a/src/components/pickers/Menu/Menu.tsx
+++ b/src/components/pickers/Menu/Menu.tsx
@@ -19,12 +19,13 @@ import {
import { StyledMenu, StyledMenuHeader } from './styled';
import { MenuItem } from './MenuItem';
import { MenuSection } from './MenuSection';
-import { MenuButtonProps } from './MenuButton';
+import { MenuButtonProps, MenuSelectionType } from './MenuButton';
import { useMenuContext } from './context';
export interface CubeMenuProps
extends ContainerStyleProps,
AriaMenuProps {
+ selectionIcon?: MenuSelectionType;
header?: ReactNode;
footer?: ReactNode;
styles?: Styles;
@@ -34,7 +35,7 @@ function Menu(
props: CubeMenuProps,
ref: DOMRef,
) {
- const { header, footer } = props;
+ const { header, footer, selectionIcon } = props;
const domRef = useDOMRef(ref);
const contextProps = useMenuContext();
const completeProps = mergeProps(contextProps, props);
@@ -70,6 +71,7 @@ function Menu(
key={item.key}
item={item}
state={state}
+ selectionIcon={selectionIcon}
onAction={completeProps.onAction}
/>
);
@@ -80,6 +82,7 @@ function Menu(
key={item.key}
item={item}
state={state}
+ selectionIcon={selectionIcon}
onAction={completeProps.onAction}
/>
);
diff --git a/src/components/pickers/Menu/MenuButton.tsx b/src/components/pickers/Menu/MenuButton.tsx
index 9a2c0340..099a76ba 100644
--- a/src/components/pickers/Menu/MenuButton.tsx
+++ b/src/components/pickers/Menu/MenuButton.tsx
@@ -1,9 +1,9 @@
import { ReactNode } from 'react';
import { Button, CubeButtonProps } from '../../actions';
import { Text } from '../../content/Text';
-import { Styles } from '../../../tasty';
+import { Styles, tasty } from '../../../tasty';
import { Space } from '../../layout/Space';
-import { CheckOutlined } from '@ant-design/icons';
+import { CheckOutlined, CheckCircleOutlined } from '@ant-design/icons';
const ACTION_BUTTON: Styles = {
border: {
@@ -30,6 +30,8 @@ const ACTION_BUTTON: Styles = {
padding: {
'': '(0.75x - 1px) (1.5x - 1px)',
'selectable & !selected':
+ '(0.75x - 1px) (1.5x - 1px) (0.75x - 1px) (1.5x - 1px)',
+ 'selectionIcon & selectable & !selected':
'(0.75x - 1px) (1.5x - 1px) (0.75x - 1px) (1.5x - 1px + 22px)',
},
display: 'flex',
@@ -45,6 +47,23 @@ const ACTION_BUTTON: Styles = {
},
};
+const RadioIcon = tasty({
+ styles: {
+ display: 'flex',
+ width: '1.875x',
+ placeContent: 'center',
+
+ '&::before': {
+ display: 'block',
+ content: '""',
+ width: '1x',
+ height: '1x',
+ radius: 'round',
+ fill: '#current',
+ },
+ },
+});
+
const getPostfix = (postfix) =>
typeof postfix === 'string' ? (
@@ -54,22 +73,38 @@ const getPostfix = (postfix) =>
postfix
);
+export type MenuSelectionType = 'checkbox' | 'radio';
+
export type MenuButtonProps = {
postfix: ReactNode;
+ selectionIcon?: MenuSelectionType;
isSelectable?: boolean;
disabled?: boolean;
} & CubeButtonProps;
+const getSelectionTypeIcon = (selectionIcon?: MenuSelectionType) => {
+ switch (selectionIcon) {
+ case 'checkbox':
+ return ;
+ case 'radio':
+ return ;
+ default:
+ return null;
+ }
+};
+
export function MenuButton({
children,
icon,
postfix,
...props
}: MenuButtonProps) {
- const { isSelected, isSelectable } = props;
- const checkIcon = isSelectable && isSelected ? : null;
+ const { selectionIcon, isSelected, isSelectable } = props;
+ const checkIcon =
+ isSelectable && isSelected ? getSelectionTypeIcon(selectionIcon) : null;
const mods = {
...props.mods,
+ selectionIcon: !!selectionIcon,
selectable: isSelectable,
selected: isSelected,
};
diff --git a/src/components/pickers/Menu/MenuItem.tsx b/src/components/pickers/Menu/MenuItem.tsx
index 4d04f744..1fbbe314 100644
--- a/src/components/pickers/Menu/MenuItem.tsx
+++ b/src/components/pickers/Menu/MenuItem.tsx
@@ -7,18 +7,19 @@ import { useMenuItem } from '@react-aria/menu';
import { mergeProps, ClearSlots, SlotProvider } from '../../../utils/react';
import { useMenuContext } from './context';
import { StyledMenuItem } from './styled';
-import { MenuButton } from './MenuButton';
+import { MenuButton, MenuSelectionType } from './MenuButton';
export interface MenuItemProps {
item: Node;
state: TreeState;
+ selectionIcon?: MenuSelectionType;
isVirtualized?: boolean;
onAction?: (key: Key) => void;
}
/** @private */
export function MenuItem(props: MenuItemProps) {
- const { item, state, isVirtualized, onAction } = props;
+ const { item, state, selectionIcon, isVirtualized, onAction } = props;
const { onClose, closeOnSelect } = useMenuContext();
const { rendered, key, props: itemProps } = item;
@@ -48,6 +49,7 @@ export function MenuItem(props: MenuItemProps) {
typeof rendered === 'string' ? (