diff --git a/.changeset/clever-crabs-act.md b/.changeset/clever-crabs-act.md new file mode 100644 index 00000000..51245ed7 --- /dev/null +++ b/.changeset/clever-crabs-act.md @@ -0,0 +1,9 @@ +--- +'@real-system/ariakit-library': patch +'@real-system/menu-primitive': patch +'@real-system/styled-library': patch +'@real-system/select': patch +'@real-system/menu': patch +--- + +add SubMenu functionality to Menu diff --git a/packages/components/menu/src/Menu.tsx b/packages/components/menu/src/Menu.tsx index a7e8a35c..b08e6fc7 100644 --- a/packages/components/menu/src/Menu.tsx +++ b/packages/components/menu/src/Menu.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { MenuProviderPrimitive, type MenuProviderPrimitiveProps, + useMenuStorePrimitive, } from '@real-system/menu-primitive'; import { MenuGroup } from './MenuGroup/index'; @@ -35,16 +36,16 @@ function Menu({ onSelect, setOpen, }: MenuProps) { + const store = useMenuStorePrimitive({ + placement, + open, + values, + defaultValues, + setValues: onSelect, + setOpen, + }); return ( - - {children} - + {children} ); } diff --git a/packages/components/menu/src/MenuButton.tsx b/packages/components/menu/src/MenuButton.tsx index cc478d46..189ae398 100644 --- a/packages/components/menu/src/MenuButton.tsx +++ b/packages/components/menu/src/MenuButton.tsx @@ -3,9 +3,14 @@ import React, { forwardRef } from 'react'; import type { ButtonProps } from '@real-system/button'; import { Button } from '@real-system/button'; import { Icon } from '@real-system/icon'; -import { MenuButtonPrimitive } from '@real-system/menu-primitive'; +import { + MenuButtonPrimitive, + useMenuContextPrimitive, +} from '@real-system/menu-primitive'; import { makeTestId } from '@real-system/utils-library'; +import { MenuItem } from './MenuItem'; + type MenuButtonProps = ( | { trailingArrow?: boolean; @@ -23,6 +28,17 @@ const MenuButton = forwardRef( { children, trailingArrow, leadingArrow, ...restProps }, ref ) { + const menu = useMenuContextPrimitive(); + + if (menu?.parent) { + return ( + + {children} + + + ); + } + return ( { diff --git a/packages/components/menu/src/MenuItem/MenuItem.style.tsx b/packages/components/menu/src/MenuItem/MenuItem.style.tsx index 028c9c17..c96c3fe5 100644 --- a/packages/components/menu/src/MenuItem/MenuItem.style.tsx +++ b/packages/components/menu/src/MenuItem/MenuItem.style.tsx @@ -5,10 +5,11 @@ import type { StylishProps } from '@real-system/styled-library'; import type { CommonMenuItemProps } from './MenuItem.model'; -const menuItemStyles: StylishProps = { +const menuItemStyles = { transition: 'background-color 150ms ease-out, color 150ms ease-out', - paddingX: 7, + paddingX: 8, paddingY: 5, + borderRadius: 4, display: 'inline-flex', alignItems: 'center', width: '100%', @@ -19,25 +20,27 @@ const menuItemStyles: StylishProps = { textDecoration: 'none', cursor: 'pointer', _hover: { - backgroundColor: 'gray-50', - color: 'gray-600', + backgroundColor: `gray-50`, + color: `gray-500`, }, _focus: { outline: 'none', - backgroundColor: 'gray-50', - color: 'gray-600', + backgroundColor: `gray-50`, + color: `gray-500`, }, _active: { - backgroundColor: 'gray-100', - color: 'gray-700', + bgColor: `gray-50`, + color: `gray-500`, }, _checked: { - color: 'gray-700', + color: `gray-500`, }, _disabled: { backgroundColor: 'none', color: 'gray-300' }, -}; +} satisfies StylishProps; -const MenuItemWrapper = forwardRef( +type MenuItemWrapperProps = CommonMenuItemProps; + +const MenuItemWrapper = forwardRef( function MenuItemWrapper({ children, ...restProps }, ref) { return ( diff --git a/packages/components/menu/src/MenuItem/MenuItem.tsx b/packages/components/menu/src/MenuItem/MenuItem.tsx index 81450bb8..c09e2a1c 100644 --- a/packages/components/menu/src/MenuItem/MenuItem.tsx +++ b/packages/components/menu/src/MenuItem/MenuItem.tsx @@ -1,6 +1,8 @@ import React, { forwardRef } from 'react'; +import { MenuButtonPrimitive } from '@real-system/menu-primitive'; import { MenuItemPrimitive } from '@real-system/menu-primitive'; +import { type ColorSchemes } from '@real-system/styled-library'; import { makeTestId } from '@real-system/utils-library'; import type { CommonMenuItemProps } from './MenuItem.model'; @@ -11,7 +13,10 @@ import { MenuItemIcon } from './MenuItemIcon'; import { MenuItemLink } from './MenuItemLink'; import { MenuItemRadio } from './MenuItemRadio'; -type MenuItemProps = CommonMenuItemProps; +type MenuItemProps = CommonMenuItemProps & { + colorScheme?: ColorSchemes; + isSubmenu?: boolean; +}; export interface MenuItemComponent extends React.ForwardRefExoticComponent { @@ -24,12 +29,16 @@ export interface MenuItemComponent // @ts-ignore MenuItem properties are defined below const MenuItem: MenuItemComponent = forwardRef( - function MenuItem({ children, ...restProps }, ref) { + function MenuItem({ children, isSubmenu, ...restProps }, ref) { return ( } + render={ + + } data-testid={makeTestId('menu-item')} - {...restProps} ref={ref}> {children} diff --git a/packages/components/menu/src/MenuItem/MenuItemIcon.tsx b/packages/components/menu/src/MenuItem/MenuItemIcon.tsx index 1e510d07..dd77a8ea 100644 --- a/packages/components/menu/src/MenuItem/MenuItemIcon.tsx +++ b/packages/components/menu/src/MenuItem/MenuItemIcon.tsx @@ -4,31 +4,21 @@ import type { IconProps } from '@real-system/icon'; import { Icon } from '@real-system/icon'; import { makeTestId } from '@real-system/utils-library'; -type MenuItemIconProps = ( - | { - alignLeft?: boolean; - alignRight?: never; - } - | { - alignLeft?: never; - alignRight?: boolean; - } -) & - Omit; +type MenuItemIconProps = { + alignRight?: boolean; +} & Omit; const MenuItemIcon = forwardRef( - function MenuItemIcon({ alignRight, alignLeft = true, ...restProps }, ref) { - const spaceProps = alignLeft - ? { marginRight: 5 } - : alignRight + function MenuItemIcon({ alignRight, ...restProps }, ref) { + const spaceProps = alignRight ? { marginLeft: 'auto', marginRight: 0 } - : {}; + : { marginRight: 5 }; return ( ); diff --git a/packages/components/menu/src/MenuList.tsx b/packages/components/menu/src/MenuList.tsx index 972f8cf2..a6c4207e 100644 --- a/packages/components/menu/src/MenuList.tsx +++ b/packages/components/menu/src/MenuList.tsx @@ -1,21 +1,29 @@ import React, { forwardRef } from 'react'; -import { MenuPrimitive } from '@real-system/menu-primitive'; +import { + MenuPrimitive, + useMenuContextPrimitive, +} from '@real-system/menu-primitive'; import styled from '@real-system/styled-library'; import { makeTestId } from '@real-system/utils-library'; import type { CommonMenuProps } from './types'; const StyledMenuList = styled(MenuPrimitive)({ + pos: 'relative', py: 4, + px: 4, zIndex: 'dropdown', backgroundColor: 'white', boxShadow: 'menu', borderRadius: 4, width: 'auto', - minWidth: '15rem', + minWidth: '18rem', maxWidth: '22rem', + maxHeight: '65rem', outline: 'none', + overflow: 'visible', + overscrollBehavior: 'contain', }); type MenuListProps = { @@ -26,10 +34,14 @@ const MenuList = forwardRef(function MenuList( { children, ...restProps }, ref ) { + const menu = useMenuContextPrimitive(); + return ( {children} diff --git a/packages/components/menu/stories/Menu.stories.tsx b/packages/components/menu/stories/Menu.stories.tsx index 02000905..ba97cada 100644 --- a/packages/components/menu/stories/Menu.stories.tsx +++ b/packages/components/menu/stories/Menu.stories.tsx @@ -20,6 +20,7 @@ export const Default = () => ( Edit E + Share @@ -47,6 +48,63 @@ export const Default = () => ( ); +export const Submenu = () => ( + + + Actions + + + + Edit + E + + + + + Share + S + + + + Archive + A + + + + Delete + D + + + + Get Support + + + Report issue + + + Chat Support + + + + Other Options + + + + Delete + E + + + + + + + + +); + export const MenuGroups = () => ( diff --git a/packages/components/select/src/Select.tsx b/packages/components/select/src/Select.tsx index 4965f517..c199104b 100644 --- a/packages/components/select/src/Select.tsx +++ b/packages/components/select/src/Select.tsx @@ -61,6 +61,7 @@ const Select: SelectComponent = forwardRef( placement={placement} setValue={onChange} setOpen={setOpen} + animated value={value} defaultValue={defaultValue} open={open}> diff --git a/packages/components/select/src/SelectGroup/SelectGroupLabel.tsx b/packages/components/select/src/SelectGroup/SelectGroupLabel.tsx index c73c5d7c..c47ceb4f 100644 --- a/packages/components/select/src/SelectGroup/SelectGroupLabel.tsx +++ b/packages/components/select/src/SelectGroup/SelectGroupLabel.tsx @@ -20,9 +20,9 @@ const SelectGroupLabel = ({ return ( ( scrollMargin={4} alignItems="center" gap={4} - paddingX={7} + paddingX={8} paddingY={5} + borderRadius={4} color="gray-500" fontScale="select-item" fontWeight="select-item" @@ -28,7 +29,7 @@ const SelectItem = forwardRef( _hover={{ bgColor: 'gray-50' }} _focus={{ bgColor: 'gray-50', color: 'gray-600' }} _focusVisible={{ bgColor: 'gray-50', color: 'gray-600' }} - _active={{ bgColor: 'gray-100', color: 'gray-700' }} + _selected={{ bgColor: 'gray-100', color: 'gray-700' }} _disabled={{ backgroundColor: 'none', color: 'gray-300' }} {...restProps} ref={ref}> diff --git a/packages/components/select/src/SelectPopover.tsx b/packages/components/select/src/SelectPopover.tsx index feeb5137..d03b834f 100644 --- a/packages/components/select/src/SelectPopover.tsx +++ b/packages/components/select/src/SelectPopover.tsx @@ -8,9 +8,6 @@ import type { OmitSelectPrivateProps } from './types'; type SelectPopoverProps = OmitSelectPrivateProps; -/** - * @todo animate popover - */ const SelectPopover = ({ children, sameWidth = true, @@ -30,6 +27,10 @@ const SelectPopover = ({ border="weak" bgColor="white" py={4} + px={4} + opacity="0" + transition="opacity .2s ease-in-out" + _enter={{ opacity: 1 }} color="white" filter="popover" outline="none" diff --git a/packages/libraries/ariakit-library/src/menu.ts b/packages/libraries/ariakit-library/src/menu.ts index 7e766e7b..60092336 100644 --- a/packages/libraries/ariakit-library/src/menu.ts +++ b/packages/libraries/ariakit-library/src/menu.ts @@ -16,4 +16,6 @@ export { MenuItemRadio as AriakitMenuItemRadio, MenuProvider as AriakitMenuProvider, MenuSeparator as AriakitMenuSeparator, + useMenuContext as useAriakitMenuContext, + useMenuStore as useAriakitMenuStore, } from '@ariakit/react/menu'; diff --git a/packages/libraries/styled-library/src/theme/palettes/config.ts b/packages/libraries/styled-library/src/theme/palettes/config.ts index 9f9d1bf2..8002bbbb 100644 --- a/packages/libraries/styled-library/src/theme/palettes/config.ts +++ b/packages/libraries/styled-library/src/theme/palettes/config.ts @@ -4,7 +4,7 @@ export type DefaultColorSchemes = | '#ffffff' | '#101840' | '#596375' - | '#3366FF' + | '#006DFA' | '#da1e28' | '#ffb020' | '#ffca58' @@ -38,7 +38,7 @@ const palettes: PaletteConfig = { white: '#ffffff', black: '#101840', gray: '#596375', - blue: '#3366FF', + blue: '#006DFA', // previous: #3366FF red: '#da1e28', orange: '#ffb020', yellow: '#ffca58', diff --git a/packages/libraries/styled-library/src/theme/sizeUtils.ts b/packages/libraries/styled-library/src/theme/sizeUtils.ts index 76d02b11..fd2a277c 100644 --- a/packages/libraries/styled-library/src/theme/sizeUtils.ts +++ b/packages/libraries/styled-library/src/theme/sizeUtils.ts @@ -1,20 +1,22 @@ import { _logger } from '@real-system/utils-library'; type Format = 'rem' | 'px'; -type FormatScaleReturnValue = `-${string}${Format}` | `${string}${Format}`; +type FormatScaleReturnValue = + | `-${string}${T}` + | `${string}${T}`; -const formatScale = ( - size: number, - format: Format = 'rem', +const formatScale = ( + size: S, + format?: F, destructive?: boolean -): FormatScaleReturnValue => { +): FormatScaleReturnValue => { let prefix = ''; if (destructive) prefix = '-'; - if (format === 'rem') { - // divide by 10 because real system's html size is sized down to 0.625rem - return `${prefix}${size / 10}rem`; + if (format === 'px') { + return `${prefix}${size}px` as FormatScaleReturnValue; } - return `${prefix}${size}px`; + // divide by 10 because real system's html size is sized down to 0.625rem + return `${prefix}${size / 10}rem` as FormatScaleReturnValue; }; type MakeScaleUtilOptions = { @@ -43,33 +45,33 @@ type ScalerOptions = { destructive?: boolean; }; -const defaultScalerOptions: ScalerOptions = { +const DEFAULT_SCALER_OPTS: ScalerOptions = { format: 'rem', destructive: false, }; /** - * Creates a multiple-of-8 measurement in rem (by default) or pixels. Can return non-integers via `destructive` + * Creates a measurement multipled by 8 in rem (by default) or pixels. Can return non-integers via `destructive` */ const majorScale = ( size: number, - { format = 'rem', destructive = false }: ScalerOptions = defaultScalerOptions + { format = 'rem', destructive = false }: ScalerOptions = DEFAULT_SCALER_OPTS ) => makeScaleUtil(size, 8, { origin: 'majorScale', format, destructive }); /** - * Creates a multiple-of-4 measurement in rem (by default) or pixels. Can return non-integers via `destructive` + * Creates a measurement multipled by 4 in rem (by default) or pixels. Can return non-integers via `destructive` */ const minorScale = ( size: number, - { format = 'rem', destructive = false }: ScalerOptions = defaultScalerOptions + { format = 'rem', destructive = false }: ScalerOptions = DEFAULT_SCALER_OPTS ) => makeScaleUtil(size, 4, { origin: 'minorScale', format, destructive }); /** - * Creates a multiple-of-2 measurement in rem (by default) or pixels. Can return non-integers via `destructive` + * Creates a measurement multipled by 2 in rem (by default) or pixels. Can return non-integers via `destructive` */ const patchScale = ( size: number, - { format = 'rem', destructive = false }: ScalerOptions = defaultScalerOptions + { format = 'rem', destructive = false }: ScalerOptions = DEFAULT_SCALER_OPTS ) => makeScaleUtil(size, 2, { origin: 'patchScale', format, destructive }); export type { ScalerOptions }; diff --git a/packages/libraries/styled-library/src/theme/tokens/tokens.static.ts b/packages/libraries/styled-library/src/theme/tokens/tokens.static.ts index 4781bf8a..c9ab19aa 100644 --- a/packages/libraries/styled-library/src/theme/tokens/tokens.static.ts +++ b/packages/libraries/styled-library/src/theme/tokens/tokens.static.ts @@ -184,6 +184,7 @@ const fontSizes = { 'badge-lg': patchScale(6.5), 'badge-xl': patchScale(7), label: patchScale(7), + 'group-label': patchScale(6.5), link: patchScale(7), help: patchScale(7), h1: patchScale(17), @@ -226,6 +227,7 @@ const lineHeights = { 'badge-lg': patchScale(6.5), 'badge-xl': patchScale(7), label: patchScale(7), + 'group-label': patchScale(6.5), help: patchScale(7), link: patchScale(7), h1: patchScale(17), @@ -263,6 +265,7 @@ const letterSpacings = { 'badge-lg': 0, 'badge-xl': 0, label: '0.25px', + 'group-label': '0.25px', help: '0.25px', link: '0.25px', h1: '0.3px', @@ -301,6 +304,7 @@ const fontWeights = { 'badge-lg': 500, 'badge-xl': 500, label: 600, + 'group-label': 600, help: 400, link: 400, h1: 700, diff --git a/packages/libraries/styled-library/src/theme/tokens/tokens.utils.ts b/packages/libraries/styled-library/src/theme/tokens/tokens.utils.ts index ca4063d7..db101284 100644 --- a/packages/libraries/styled-library/src/theme/tokens/tokens.utils.ts +++ b/packages/libraries/styled-library/src/theme/tokens/tokens.utils.ts @@ -35,6 +35,7 @@ const makeColorRangeFromPalette = ( const paletteColor = plt[paletteKey]; const colorRange = { + [`${paletteKey}-25`]: tint(0.975, paletteColor), [`${paletteKey}-50`]: tint(0.95, paletteColor), [`${paletteKey}-100`]: tint(0.85, paletteColor), [`${paletteKey}-200`]: tint(0.65, paletteColor), @@ -46,6 +47,7 @@ const makeColorRangeFromPalette = ( [`${paletteKey}-800`]: shade(0.65, paletteColor), [`${paletteKey}-900`]: shade(0.8, paletteColor), [`${paletteKey}-950`]: shade(0.95, paletteColor), + [`${paletteKey}-975`]: tint(0.975, paletteColor), }; const readableTextRange = Object.keys(colorRange).reduce((a, b) => { diff --git a/packages/primitives/menu-primitive/README.md b/packages/primitives/menu-primitive/README.md index 6bf7aa3f..e0ad7df1 100644 --- a/packages/primitives/menu-primitive/README.md +++ b/packages/primitives/menu-primitive/README.md @@ -36,7 +36,6 @@ import type { } from '@real-system/menu-primitive'; import { MenuArrowPrimitive, - MenuButtonArrowPrimitive, MenuButtonPrimitive, MenuGroupLabelPrimitive, MenuGroupPrimitive, diff --git a/packages/primitives/menu-primitive/src/index.ts b/packages/primitives/menu-primitive/src/index.ts index 2431b629..1a803807 100644 --- a/packages/primitives/menu-primitive/src/index.ts +++ b/packages/primitives/menu-primitive/src/index.ts @@ -16,4 +16,6 @@ export { AriakitMenu as MenuPrimitive, AriakitMenuProvider as MenuProviderPrimitive, AriakitMenuSeparator as MenuSeparatorPrimitive, + useAriakitMenuContext as useMenuContextPrimitive, + useAriakitMenuStore as useMenuStorePrimitive, } from '@real-system/ariakit-library';