From 7caa55e9215113c1c2a86a0c5703819a57db0958 Mon Sep 17 00:00:00 2001 From: Jiuzhen Pan <2216991777@qq.com> Date: Sun, 21 Aug 2022 23:40:58 -0700 Subject: [PATCH] feat(menu): added new menu options to make it more customizable Added sr (screen reader) prop, new classNames for: - menu - icon. New iconStart and iconEnd options along with their relative positions to the button element. New menuPositionX & menuPositionY prop for customizing menu popup location. Added descriptions for all props. --- src/lib/components/Form/AppMenu.tsx | 117 ++++++++++++++++++---------- src/lib/components/Form/index.tsx | 88 ++++++++++++++++++++- 2 files changed, 162 insertions(+), 43 deletions(-) diff --git a/src/lib/components/Form/AppMenu.tsx b/src/lib/components/Form/AppMenu.tsx index ceb677f..b8f1665 100644 --- a/src/lib/components/Form/AppMenu.tsx +++ b/src/lib/components/Form/AppMenu.tsx @@ -5,47 +5,62 @@ import { MenuProps } from '.'; import clsxm from '../../helpers/clsxm'; function AppMenu({ + sr, label, header, - icon, + iconStart: IconStart, + iconEnd: IconEnd, badge, menus, className, buttonClassName, labelClassName, badgeClassName, + menuClassName, + iconClassName, + iconStartPosition = 'flex', + iconEndPosition = 'flex', + menuPositionX = 'center', + menuPositionY = 'bottom', ...props -}: MenuProps & { - className?: string; - buttonClassName?: string; - labelClassName?: string; - badgeClassName?: string; - [x: string]: any; -} & React.HTMLAttributes) { +}: MenuProps & React.HTMLAttributes) { + const iconDefaultClassName = + 'h-5 w-5 flex-shrink-0 text-primary-400 group-hover:text-primary-500 dark:text-primary-400 dark:group-hover:text-primary-50'; return ( - {label && ( - - {label} - - )} - {icon && icon} - {badge && ( - - {badge} - - )} + {sr && {sr}} +
+ {IconStart && } + {iconStartPosition === 'between' && } + {label && ( + + {label} + + )} + {iconEndPosition === 'between' && } + {IconEnd && } + {badge && ( + + {badge} + + )} +
- + {header && header}
- {menus.map((item) => ( - + {menus.map(({ icon: Icon, label, onClick, role, ...props }) => ( + {({ active }) => - item.role === 'separator' ? ( -
+ role === 'separator' ? ( +
) : ( ) } diff --git a/src/lib/components/Form/index.tsx b/src/lib/components/Form/index.tsx index 77d071e..7bbf0c4 100644 --- a/src/lib/components/Form/index.tsx +++ b/src/lib/components/Form/index.tsx @@ -38,14 +38,100 @@ export interface DashboardNavProps { export interface MenuProps { label?: string; + /** + * A React component to be used as the icon for the menu item. + */ icon?: JSX.Element; + /** + * displayed after the label + * `string + */ badge?: string; + /** + * Header of the menu + */ header?: JSX.Element; + /** + * sr: Screen reader text + */ + sr?: string; + /** + * iconStart: Icon to display on the left of the label + */ + iconStart?: (props: SVGProps) => JSX.Element; + /** + * iconEnd: Icon to display on the right of the label + */ + iconEnd?: (props: SVGProps) => JSX.Element; + /** + * iconStartPosition: Position of the iconStart + * `flex` (default) or `between` + * `between` will create a gap between the icon and the label + */ + iconStartPosition?: 'flex' | 'between'; + /** + * iconEndPosition: Position of the iconEnd + * `flex` (default) or `between` + * `between` will create a gap between the icon and the label + */ + iconEndPosition?: 'flex' | 'between'; + className?: string; + /** + * buttonClassName: Additional class name to apply to the button + * This will override the default class name if the custom class name conflicts with an existing class name + */ + buttonClassName?: string; + /** + * labelClassName: Additinal class name to apply to the label + * This will override the default class name if the custom class name conflicts with an existing class name + */ + labelClassName?: string; + /** + * badgeClassName: Additinal class name to apply to the badge + * This will override the default class name if the custom class name conflicts with an existing class name + */ + badgeClassName?: string; + /** + * menuClassName: Additinal class name to apply to the menu + * This will override the default class name if the custom class name conflicts with an existing class name + */ + menuClassName?: string; + /** + * iconClassName: Additinal class name to apply to the icon + * This will override the default class name if the custom class name conflicts with an existing class name + */ + iconClassName?: string; + /** + * menuPositionX: Horizontal position of the menu + * `center` (default) or `left` or `right` + */ + menuPositionX?: 'start' | 'center' | 'end'; + /** + * menuPositionY: Vertical position of the menu + * `top` (default) or `bottom` + */ + menuPositionY?: 'top' | 'center' | 'bottom'; + [x: string]: any; + /** + * menus: Array of menu items + * Each menu item should be an object with the following properties: + * `label`: The name of the menu item + * `href`: `optional` - The URL of the menu item + * `icon`: `optional` - A React component or a function that returns a React component to be used as the icon for the menu item + * `role`: `optional` - The role of the menu item + * `onClick`: `optional` - A function to be called when the menu item is clicked + * `props`: `optional` - Any additional props to be passed to the menu item + */ menus: { + /** + * sr: Screen reader text + */ + sr?: string; label?: string; onClick?: () => void | Promise; - icon?: (props: SVGProps) => JSX.Element; + icon?: (props: SVGProps) => JSX.Element | JSX.Element; role?: 'separator' | 'destructive' | 'default' | 'info' | 'success' | 'warning'; + [x: string]: any; }[]; } export interface PopoverProps {