Skip to content

Commit

Permalink
feat: add horizontal menu variation
Browse files Browse the repository at this point in the history
  • Loading branch information
abelflopes committed Jul 2, 2024
1 parent 2e93a65 commit 3fa7807
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 48 deletions.
33 changes: 24 additions & 9 deletions packages/components/_provisional/src/menu/Menu.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
import styles from "./styles/menu.module.scss";
import classNames from "classnames";
import React from "react";
import React, { useMemo } from "react";
import { MenuItem } from "./MenuItem";
import { MenuDivider } from "./MenuDivider";
import { MenuContext, menuContextDefaults, type MenuContextProps } from "./context";

interface MenuProps extends React.HTMLAttributes<HTMLElement> {
expanded?: boolean;
}
type MenuProps = React.HTMLAttributes<HTMLElement> & MenuContextProps;

// TODO: add a11y https://react.dev/reference/react-dom/createPortal#rendering-a-dock-dialog-with-a-portal
// TODO: keyboard nav / focus

const Menu = ({ children, className, ...otherProps }: Readonly<MenuProps>): React.ReactNode => (
<ul className={classNames(styles.root, className)} {...otherProps}>
{children}
</ul>
);
const Menu = ({
variation = menuContextDefaults.variation,
children,
className,
...otherProps
}: Readonly<MenuProps>): React.ReactNode => {
const menuContextValue = useMemo<MenuContextProps>(
() => ({
variation,
}),
[variation],
);

return (
<MenuContext.Provider value={menuContextValue}>
<ul className={classNames(styles.root, styles[variation], className)} {...otherProps}>
{children}
</ul>
</MenuContext.Provider>
);
};

Menu.Item = MenuItem;

Expand Down
24 changes: 18 additions & 6 deletions packages/components/_provisional/src/menu/MenuDivider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import styles from "./styles/menu-divider.module.scss";
import React from "react";
import React, { useContext } from "react";
import classNames from "classnames";
import { MenuContext } from "./context";

export interface MenuDividerProps extends React.HTMLAttributes<HTMLSpanElement> {
/** Passing a children will render a labeled separator, while omitting children will render a simple line separator */
Expand All @@ -11,8 +12,19 @@ export const MenuDivider = ({
className,
children,
...otherProps
}: Readonly<MenuDividerProps>): React.ReactElement => (
<span className={classNames(styles.root, children && styles.text, className)} {...otherProps}>
{children}
</span>
);
}: Readonly<MenuDividerProps>): React.ReactElement => {
const menuContext = useContext(MenuContext);

return (
<span
className={classNames(
styles.root,
children && styles.text,
styles[menuContext.variation],
className,
)}
{...otherProps}>
{children}
</span>
);
};
44 changes: 21 additions & 23 deletions packages/components/_provisional/src/menu/MenuItem.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
import styles from "./styles/menu-item.module.scss";
import React from "react";
import React, { useContext } from "react";
import classNames from "classnames";
import { MenuContext } from "./context";

export interface MenuItemProps extends React.HTMLAttributes<HTMLElement> {
selected?: boolean;
highlighted?: boolean;
skin?: "default" | "primary" | "secondary" | "disabled";
icon?: NonNullable<React.ReactNode>;
disabled?: boolean;
}

export const MenuItem = ({
skin = "default",
icon,
selected,
highlighted,
disabled,
className,
children,
...otherProps
}: Readonly<MenuItemProps>): React.ReactElement => (
<li
className={classNames(
styles.root,
{
[`${styles.disabled}`]: disabled,
[`${styles.selected}`]: selected,
[`${styles.highlighted}`]: highlighted,
},
className,
)}
{...otherProps}>
{icon}
{children}
</li>
);
}: Readonly<MenuItemProps>): React.ReactElement => {
const menuContext = useContext(MenuContext);

return (
<li
className={classNames(
styles.root,
skin !== "default" && styles[skin],
styles[menuContext.variation],
className,
)}
{...otherProps}>
{icon}
{children}
</li>
);
};
11 changes: 11 additions & 0 deletions packages/components/_provisional/src/menu/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react";

export interface MenuContextProps {
variation: "vertical" | "horizontal";
}

export const menuContextDefaults: MenuContextProps = {
variation: "vertical",
};

export const MenuContext = React.createContext<MenuContextProps>(menuContextDefaults);
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,30 @@

.root {
display: block;
min-height: get-css-var(spacing, border);
background-color: get-color(neutral-light-2);
box-sizing: border-box;

&.text {
@include text-base;
@include text-variation(extra-small);
}

&.vertical {
min-height: get-css-var(spacing, border);

&.text {
padding: #{get-spacing(0.25)} #{get-spacing()};
}
}

&.horizontal {
writing-mode: vertical-lr;
text-orientation: mixed;
height: 100%;
min-width: get-css-var(spacing, border);

padding: #{get-spacing(0.25)} #{get-spacing()};
&.text {
padding: #{get-spacing()} #{get-spacing(0.25)};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
cursor: pointer;
gap: get-spacing();
transition: all 0.2s;
box-sizing: border-box;

&:hover:not(.disabled),
&.highlighted {
&.secondary {
background-color: get-color(neutral-light-2);
}

&.selected {
&.primary {
background-color: get-color(highlight-primary);
color: get-color(neutral-light-1);
}
Expand All @@ -27,4 +28,11 @@
filter: grayscale(1);
opacity: 0.4;
}

&.horizontal {
height: 100%;
flex-grow: 1;
flex-shrink: 1;
justify-content: center;
}
}
10 changes: 10 additions & 0 deletions packages/components/_provisional/src/menu/styles/menu.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,14 @@

.root {
list-style: none;
display: flex;
}

.vertical {
flex-direction: column;
}

.horizontal {
height: get-spacing(10);
flex-direction: row;
}
29 changes: 23 additions & 6 deletions packages/docs/stories/src/menu.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const children = (
<IconUserCircle />
</Icon>
}
selected>
skin="primary">
{faker.person.firstName()}
</Menu.Item>
<Menu.Item
Expand All @@ -53,7 +53,7 @@ const children = (
<IconUserCircle />
</Icon>
}
highlighted>
skin="secondary">
{faker.person.firstName()}
</Menu.Item>
<Menu.Item
Expand All @@ -66,19 +66,36 @@ const children = (
</Menu.Item>
<Menu.Divider />
<Menu.Item>{faker.animal.type()}</Menu.Item>
<Menu.Item disabled>{faker.animal.type()}</Menu.Item>
<Menu.Item skin="disabled">{faker.animal.type()}</Menu.Item>
<Menu.Item>{faker.animal.type()}</Menu.Item>
<Menu.Divider>Companies</Menu.Divider>
<Menu.Item>{faker.company.name()}</Menu.Item>
<Menu.Item>{faker.company.name()}</Menu.Item>
<Menu.Item
icon={
<Icon>
<IconUserCircle />
</Icon>
}>
{faker.company.name()}
<br />
{faker.company.buzzNoun()}
<br />
{faker.company.buzzVerb()}
</Menu.Item>
</>
);

export default meta;

export const Component: Story = {
export const Default: Story = {
args: {
children,
},
};

export const horizontal: Story = {
args: {
expanded: false,
variation: "horizontal",
children,
},
};

0 comments on commit 3fa7807

Please sign in to comment.