Selaa lähdekoodia

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

David Luzar 1 vuosi sitten
vanhempi
commit
12420592ef

+ 2 - 0
dev-docs/docs/@excalidraw/excalidraw/api/children-components/main-menu.mdx

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

+ 4 - 0
src/components/Actions.tsx

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

+ 5 - 0
src/components/dropdownMenu/DropdownMenu.scss

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

+ 3 - 1
src/components/dropdownMenu/DropdownMenuItem.tsx

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

+ 5 - 1
src/components/dropdownMenu/DropdownMenuItemCustom.tsx

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

+ 3 - 1
src/components/dropdownMenu/DropdownMenuItemLink.tsx

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

+ 7 - 2
src/components/dropdownMenu/common.ts

@@ -6,8 +6,13 @@ export const DropdownMenuContentPropsContext = React.createContext<{
   onSelect?: (event: Event) => void;
   onSelect?: (event: Event) => void;
 }>({});
 }>({});
 
 
-export const getDropdownMenuItemClassName = (className = "") => {
-  return `dropdown-menu-item dropdown-menu-item-base ${className}`.trim();
+export const getDropdownMenuItemClassName = (
+  className = "",
+  selected = false,
+) => {
+  return `dropdown-menu-item dropdown-menu-item-base ${className} ${
+    selected ? "dropdown-menu-item--selected" : ""
+  }`.trim();
 };
 };
 
 
 export const useHandleDropdownMenuItemClick = (
 export const useHandleDropdownMenuItemClick = (

+ 6 - 0
src/packages/excalidraw/CHANGELOG.md

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