feat: support menu / dropdown items to have selected
state (#7078)
This commit is contained in:
parent
bfd318e765
commit
12420592ef
@ -38,6 +38,7 @@ To render an item, its recommended to use `MainMenu.Item`.
|
|||||||
| Prop | Type | Required | Default | Description |
|
| Prop | Type | Required | Default | Description |
|
||||||
| --- | --- | :-: | :-: | --- |
|
| --- | --- | :-: | :-: | --- |
|
||||||
| `onSelect` | `function` | Yes | - | Triggered when selected (via mouse). Calling `event.preventDefault()` will stop menu from closing. |
|
| `onSelect` | `function` | Yes | - | Triggered when selected (via mouse). Calling `event.preventDefault()` will stop menu from closing. |
|
||||||
|
| `selected` | `boolean` | No | `false` | Whether item is active |
|
||||||
| `children` | `React.ReactNode` | Yes | - | The content of the menu item |
|
| `children` | `React.ReactNode` | Yes | - | The content of the menu item |
|
||||||
| `icon` | `JSX.Element` | No | - | The icon used in the menu item |
|
| `icon` | `JSX.Element` | No | - | The icon used in the menu item |
|
||||||
| `shortcut` | `string` | No | - | The shortcut to be shown for the menu item |
|
| `shortcut` | `string` | No | - | The shortcut to be shown for the menu item |
|
||||||
@ -70,6 +71,7 @@ function App() {
|
|||||||
| Prop | Type | Required | Default | Description |
|
| Prop | Type | Required | Default | Description |
|
||||||
| --- | --- | :-: | :-: | --- |
|
| --- | --- | :-: | :-: | --- |
|
||||||
| `onSelect` | `function` | No | - | Triggered when selected (via mouse). Calling `event.preventDefault()` will stop menu from closing. |
|
| `onSelect` | `function` | No | - | Triggered when selected (via mouse). Calling `event.preventDefault()` will stop menu from closing. |
|
||||||
|
| `selected` | `boolean` | No | `false` | Whether item is active |
|
||||||
| `href` | `string` | Yes | - | The `href` attribute to be added to the `anchor` element. |
|
| `href` | `string` | Yes | - | The `href` attribute to be added to the `anchor` element. |
|
||||||
| `children` | `React.ReactNode` | Yes | - | The content of the menu item |
|
| `children` | `React.ReactNode` | Yes | - | The content of the menu item |
|
||||||
| `icon` | `JSX.Element` | No | - | The icon used in the menu item |
|
| `icon` | `JSX.Element` | No | - | The icon used in the menu item |
|
||||||
|
@ -318,6 +318,7 @@ export const ShapesSwitcher = ({
|
|||||||
activeEmbeddable: null,
|
activeEmbeddable: null,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
selected={activeTool.type === "frame"}
|
||||||
/>
|
/>
|
||||||
<ToolButton
|
<ToolButton
|
||||||
className={clsx("Shape", { fillable: false })}
|
className={clsx("Shape", { fillable: false })}
|
||||||
@ -348,6 +349,7 @@ export const ShapesSwitcher = ({
|
|||||||
activeEmbeddable: null,
|
activeEmbeddable: null,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
selected={activeTool.type === "embeddable"}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@ -378,6 +380,7 @@ export const ShapesSwitcher = ({
|
|||||||
icon={frameToolIcon}
|
icon={frameToolIcon}
|
||||||
shortcut={KEYS.F.toLocaleUpperCase()}
|
shortcut={KEYS.F.toLocaleUpperCase()}
|
||||||
data-testid="toolbar-frame"
|
data-testid="toolbar-frame"
|
||||||
|
selected={activeTool.type === "frame"}
|
||||||
>
|
>
|
||||||
{t("toolBar.frame")}
|
{t("toolBar.frame")}
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
@ -394,6 +397,7 @@ export const ShapesSwitcher = ({
|
|||||||
}}
|
}}
|
||||||
icon={EmbedIcon}
|
icon={EmbedIcon}
|
||||||
data-testid="toolbar-embeddable"
|
data-testid="toolbar-embeddable"
|
||||||
|
selected={activeTool.type === "embeddable"}
|
||||||
>
|
>
|
||||||
{t("toolBar.embeddable")}
|
{t("toolBar.embeddable")}
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
|
@ -59,6 +59,11 @@
|
|||||||
height: 2.25rem;
|
height: 2.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--selected {
|
||||||
|
background: var(--color-primary-light);
|
||||||
|
--icon-fill-color: var(--color-primary-darker);
|
||||||
|
}
|
||||||
|
|
||||||
&__text {
|
&__text {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -11,12 +11,14 @@ const DropdownMenuItem = ({
|
|||||||
children,
|
children,
|
||||||
shortcut,
|
shortcut,
|
||||||
className,
|
className,
|
||||||
|
selected,
|
||||||
...rest
|
...rest
|
||||||
}: {
|
}: {
|
||||||
icon?: JSX.Element;
|
icon?: JSX.Element;
|
||||||
onSelect: (event: Event) => void;
|
onSelect: (event: Event) => void;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
shortcut?: string;
|
shortcut?: string;
|
||||||
|
selected?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => {
|
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => {
|
||||||
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
|
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
|
||||||
@ -26,7 +28,7 @@ const DropdownMenuItem = ({
|
|||||||
{...rest}
|
{...rest}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
type="button"
|
type="button"
|
||||||
className={getDropdownMenuItemClassName(className)}
|
className={getDropdownMenuItemClassName(className, selected)}
|
||||||
title={rest.title ?? rest["aria-label"]}
|
title={rest.title ?? rest["aria-label"]}
|
||||||
>
|
>
|
||||||
<MenuItemContent icon={icon} shortcut={shortcut}>
|
<MenuItemContent icon={icon} shortcut={shortcut}>
|
||||||
|
@ -3,15 +3,19 @@ import React from "react";
|
|||||||
const DropdownMenuItemCustom = ({
|
const DropdownMenuItemCustom = ({
|
||||||
children,
|
children,
|
||||||
className = "",
|
className = "",
|
||||||
|
selected,
|
||||||
...rest
|
...rest
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
selected?: boolean;
|
||||||
} & React.HTMLAttributes<HTMLDivElement>) => {
|
} & React.HTMLAttributes<HTMLDivElement>) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...rest}
|
{...rest}
|
||||||
className={`dropdown-menu-item-base dropdown-menu-item-custom ${className}`.trim()}
|
className={`dropdown-menu-item-base dropdown-menu-item-custom ${className} ${
|
||||||
|
selected ? `dropdown-menu-item--selected` : ``
|
||||||
|
}`.trim()}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,6 +12,7 @@ const DropdownMenuItemLink = ({
|
|||||||
children,
|
children,
|
||||||
onSelect,
|
onSelect,
|
||||||
className = "",
|
className = "",
|
||||||
|
selected,
|
||||||
...rest
|
...rest
|
||||||
}: {
|
}: {
|
||||||
href: string;
|
href: string;
|
||||||
@ -19,6 +20,7 @@ const DropdownMenuItemLink = ({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
shortcut?: string;
|
shortcut?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
selected?: boolean;
|
||||||
onSelect?: (event: Event) => void;
|
onSelect?: (event: Event) => void;
|
||||||
} & React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
|
} & React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
|
||||||
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
|
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
|
||||||
@ -29,7 +31,7 @@ const DropdownMenuItemLink = ({
|
|||||||
href={href}
|
href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className={getDropdownMenuItemClassName(className)}
|
className={getDropdownMenuItemClassName(className, selected)}
|
||||||
title={rest.title ?? rest["aria-label"]}
|
title={rest.title ?? rest["aria-label"]}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
|
@ -6,8 +6,13 @@ export const DropdownMenuContentPropsContext = React.createContext<{
|
|||||||
onSelect?: (event: Event) => void;
|
onSelect?: (event: Event) => void;
|
||||||
}>({});
|
}>({});
|
||||||
|
|
||||||
export const getDropdownMenuItemClassName = (className = "") => {
|
export const getDropdownMenuItemClassName = (
|
||||||
return `dropdown-menu-item dropdown-menu-item-base ${className}`.trim();
|
className = "",
|
||||||
|
selected = false,
|
||||||
|
) => {
|
||||||
|
return `dropdown-menu-item dropdown-menu-item-base ${className} ${
|
||||||
|
selected ? "dropdown-menu-item--selected" : ""
|
||||||
|
}`.trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useHandleDropdownMenuItemClick = (
|
export const useHandleDropdownMenuItemClick = (
|
||||||
|
@ -11,6 +11,12 @@ The change should be grouped under one of the below section and must contain PR
|
|||||||
Please add the latest change on the top under the correct section.
|
Please add the latest change on the top under the correct section.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add `selected` prop for `MainMenu.Item` and `MainMenu.ItemCustom` components to indicate active state. [7078](https://github.com/excalidraw/excalidraw/pull/7078)
|
||||||
|
|
||||||
## 0.16.1 (2023-09-21)
|
## 0.16.1 (2023-09-21)
|
||||||
|
|
||||||
## Excalidraw Library
|
## Excalidraw Library
|
||||||
|
Loading…
x
Reference in New Issue
Block a user