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 (
+
+ );
+ }
+
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 = () => (
+
+
+
+);
+
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';