feat: support menu / dropdown items to have selected state (#7078)

This commit is contained in:
David Luzar 2023-10-03 23:35:47 +02:00 committed by GitHub
parent bfd318e765
commit 12420592ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 35 additions and 5 deletions

View File

@ -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 |

View File

@ -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>

View File

@ -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;

View File

@ -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}>

View File

@ -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>

View File

@ -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}
> >

View File

@ -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 = (

View File

@ -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