Skip to content

Commit

Permalink
refactor: select and dropdown, ref #4798
Browse files Browse the repository at this point in the history
  • Loading branch information
fbwoolf committed Jan 12, 2024
1 parent 3b0488e commit a82457d
Show file tree
Hide file tree
Showing 19 changed files with 364 additions and 358 deletions.
16 changes: 16 additions & 0 deletions src/app/ui/components/dowpdown-menu/dropdown-menu-item.layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ReactNode } from 'react';

import { Flex } from 'leather-styles/jsx';

interface DropdownMenuItemLayoutProps {
contentLeft: ReactNode;
contentRight?: ReactNode;
}
export function DropdownMenuItemLayout({ contentLeft, contentRight }: DropdownMenuItemLayoutProps) {
return (
<Flex alignItems="center" justifyContent="space-between" width="100%">
{contentLeft}
{contentRight}
</Flex>
);
}
14 changes: 0 additions & 14 deletions src/app/ui/components/dowpdown-menu/dropdown-menu-item.tsx

This file was deleted.

This file was deleted.

22 changes: 0 additions & 22 deletions src/app/ui/components/dowpdown-menu/dropdown-menu-trigger.tsx

This file was deleted.

22 changes: 0 additions & 22 deletions src/app/ui/components/dowpdown-menu/dropdown-menu.layout.tsx

This file was deleted.

63 changes: 39 additions & 24 deletions src/app/ui/components/dowpdown-menu/dropdown-menu.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,53 @@
import * as RadixDropdownMenu from '@radix-ui/react-dropdown-menu';
import type { Meta, StoryObj } from '@storybook/react';
import { HStack, styled } from 'leather-styles/jsx';

import { ListItemType } from '../list/list-item';
import { DropdownMenu as Component } from './dropdown-menu';
import { DropdownMenuItem } from './dropdown-menu-item';
import { DropdownMenuSectionLabel } from './dropdown-menu-section-label';
import { ChevronDownIcon } from '../icons/chevron-down-icon';
import { PlaceholderIcon } from '../icons/placeholder-icon';
import { DropdownMenu as Component, DropdownMenuItem } from './dropdown-menu';
import { DropdownMenuItemLayout } from './dropdown-menu-item.layout';

const items: ListItemType[] = [{ label: 'Label 1' }, { label: 'Label 2' }];
const items: DropdownMenuItem[] = [{ label: 'Label 1' }, { label: 'Label 2' }];

const meta: Meta<typeof Component> = {
component: Component,
const meta: Meta<typeof Component.Root> = {
component: Component.Root,
tags: ['autodocs'],
title: 'Dropdown Menu',
};

export default meta;
type Story = StoryObj<typeof Component>;
type Story = StoryObj<typeof Component.Root>;

export const DropdownMenu: Story = {
render: () => (
<Component>
<RadixDropdownMenu.Group>
<DropdownMenuSectionLabel label="Items" />
{items.map(item => {
return (
<DropdownMenuItem
key={item.label}
iconLeft={item.iconLeft}
iconRight={item.iconRight}
label={item.label}
/>
);
})}
</RadixDropdownMenu.Group>
</Component>
<Component.Root>
<Component.Trigger>
<styled.button>
<HStack gap="space.02" width="100%">
<styled.span textStyle="label.02">Options</styled.span>
<ChevronDownIcon />
</HStack>
</styled.button>
</Component.Trigger>
<Component.Portal>
<Component.Content align="start" sideOffset={8}>
<Component.Group>
<Component.Label>Label</Component.Label>
{items.map(item => (
<Component.Item key={item.label}>
<DropdownMenuItemLayout
contentLeft={
<HStack gap="space.02" minHeight="24px">
<PlaceholderIcon />
<styled.span textStyle="label.02">{item.label}</styled.span>
</HStack>
}
contentRight={<PlaceholderIcon />}
/>
</Component.Item>
))}
</Component.Group>
</Component.Content>
</Component.Portal>
</Component.Root>
),
};
113 changes: 102 additions & 11 deletions src/app/ui/components/dowpdown-menu/dropdown-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,105 @@
import { ReactNode } from 'react';
import { ReactNode, forwardRef } from 'react';

import { DropdownMenuTrigger } from './dropdown-menu-trigger';
import { DropdownMenuLayout } from './dropdown-menu.layout';
import * as RadixDropdownMenu from '@radix-ui/react-dropdown-menu';
import { css } from 'leather-styles/css';

interface DropdownMenuProps {
children: ReactNode;
trigger?: ReactNode;
}
export function DropdownMenu({ children, trigger }: DropdownMenuProps) {
return (
<DropdownMenuLayout trigger={trigger ?? <DropdownMenuTrigger />}>{children}</DropdownMenuLayout>
);
export interface DropdownMenuItem {
iconLeft?: ReactNode;
iconRight?: ReactNode;
label: string;
}

const Root = RadixDropdownMenu.Root;

const dropdownTriggerStyles = css({
bg: 'accent.background-primary',
borderRadius: '2px',
fontWeight: 500,
maxWidth: 'fit-content',
maxHeight: 'fit-content',
px: 'space.04',
py: 'space.03',
textStyle: 'label.02',

'&[data-state=open]': {
bg: 'accent.component-background-pressed',
},
});
const Trigger: typeof RadixDropdownMenu.Trigger = forwardRef((props, ref) => (
<RadixDropdownMenu.Trigger className={dropdownTriggerStyles} ref={ref} {...props} />
));

const Portal = RadixDropdownMenu.Portal;

const dropdownContentStyles = css({
alignItems: 'center',
animationDuration: '400ms',
animationTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)',
'--base-menu-padding': '0px',
bg: 'accent.background-primary',
borderRadius: '2px',
boxShadow:
'0px 12px 24px 0px rgba(18, 16, 15, 0.08), 0px 4px 8px 0px rgba(18, 16, 15, 0.08), 0px 0px 2px 0px rgba(18, 16, 15, 0.08)',
minWidth: '256px',
p: 'space.02',
willChange: 'transform, opacity',
zIndex: 999,

'&[data-side=bottom]': {
animationName: 'slideUpAndFade',
},
});
const Content: typeof RadixDropdownMenu.Content = forwardRef((props, ref) => (
<RadixDropdownMenu.Content className={dropdownContentStyles} ref={ref} {...props} />
));

const Group = RadixDropdownMenu.Group;

const dropdownMenuLabelStyles = css({
color: 'accent.text-subdued',
height: 'auto',
px: 'space.03',
py: 'space.02',
textStyle: 'body.02',
width: '100%',
});
const Label: typeof RadixDropdownMenu.Label = forwardRef((props, ref) => (
<RadixDropdownMenu.Label className={dropdownMenuLabelStyles} ref={ref} {...props} />
));

const dropdownMenuItemStyles = css({
bg: 'accent.background-primary',
color: 'accent.text-primary',
height: 'auto',
outline: 'none',
userSelect: 'none',
p: 'space.03',

'&[data-highlighted]': {
bg: 'accent.component-background-hover',
},
});
const Item: typeof RadixDropdownMenu.Item = forwardRef((props, ref) => (
<RadixDropdownMenu.Item className={dropdownMenuItemStyles} ref={ref} {...props} />
));

const dropdownMenuSeparatorStyles = css({
bg: 'accent.background-primary',
color: 'accent.border-default',
mx: '0px',
my: 'space.03',
});
const Separator: typeof RadixDropdownMenu.Separator = forwardRef((props, ref) => (
<RadixDropdownMenu.Separator className={dropdownMenuSeparatorStyles} ref={ref} {...props} />
));

export const DropdownMenu = {
Root,
Trigger,
Portal,
Content,
Group,
Label,
Item,
Separator,
};
24 changes: 24 additions & 0 deletions src/app/ui/components/icons/placeholder-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { styled } from 'leather-styles/jsx';

import { SvgProps } from '@app/ui/ui-types';

export function PlaceholderIcon({ size = 'md', ...props }: SvgProps) {
return (
<styled.svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
{...props}
>
<path
d="M4 6V5C4 4.44772 4.44772 4 5 4H6M18 4H19C19.5523 4 20 4.44772 20 5V6M20 18V19C20 19.5523 19.5523 20 19 20H18M6 20H5C4.44772 20 4 19.5523 4 19V18M4 13.5V10.5M10.5 4H13.5M20 10.5V13.5M13.5 20H10.5"
stroke="black"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</styled.svg>
);
}
16 changes: 0 additions & 16 deletions src/app/ui/components/list/list-item.layout.tsx

This file was deleted.

31 changes: 0 additions & 31 deletions src/app/ui/components/list/list-item.tsx

This file was deleted.

16 changes: 0 additions & 16 deletions src/app/ui/components/list/list-section-label.tsx

This file was deleted.

Loading

0 comments on commit a82457d

Please sign in to comment.