feat: layout component

This commit is contained in:
2023-12-30 15:19:19 +01:00
parent 0ccc447e40
commit bb59863441
13 changed files with 132 additions and 54 deletions

View File

@@ -1,4 +1,10 @@
.root {
position: fixed;
z-index: auto;
top: 0;
right: 0;
left: 0;
width: 100%;
height: var(--header-height);
padding: 12px var(--horizontal-padding);

View File

@@ -1,3 +1,13 @@
.email {
color: var(--color-dark-100);
}
.trigger {
gap: 14px;
align-items: center;
}
.name {
text-decoration: underline dashed var(--color-light-100) 1px;
text-underline-offset: 6px;
}

View File

@@ -4,7 +4,6 @@ import { Link } from 'react-router-dom'
import { Logout, PersonOutline } from '@/assets'
import {
Avatar,
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
@@ -27,9 +26,12 @@ export const UserDropdown = ({ avatar, email, onLogout, userName }: UserDropdown
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button rounded variant={'icon'}>
<button className={s.trigger}>
<Typography className={s.name} variant={'subtitle1'}>
{userName}
</Typography>
<Avatar src={avatar} />
</Button>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>

View File

@@ -0,0 +1 @@
export * from './layout.tsx'

View File

@@ -0,0 +1,14 @@
.layout {
width: 100%;
padding: var(--header-height) var(--horizontal-padding) 0;
}
.content {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
max-width: var(--max-width);
margin: 0 auto;
}

View File

@@ -0,0 +1,36 @@
import type { Meta, StoryObj } from '@storybook/react'
import { Layout } from './'
const meta = {
argTypes: {
onLogout: { action: 'logout' },
},
component: Layout,
parameters: {
layout: 'fullscreen',
},
tags: ['autodocs'],
title: 'Components/Layout',
} satisfies Meta<typeof Layout>
export default meta
type Story = StoryObj<typeof meta>
export const LoggedIn: Story = {
// @ts-expect-error onLogout is required but it is provided through argTypes
args: {
avatar: 'https://avatars.githubusercontent.com/u/1196870?v=4',
children: 'Hello World',
email: 'johndoe@gmail.com',
isLoggedIn: true,
userName: 'John Doe',
},
}
export const LoggedOut: Story = {
args: {
children: 'Hello World',
isLoggedIn: false,
},
}

View File

@@ -0,0 +1,16 @@
import { ReactNode } from 'react'
import { Header, HeaderProps } from '@/components'
import s from './layout.module.scss'
export type LayoutProps = { children: ReactNode } & HeaderProps
export const Layout = ({ children, ...headerProps }: LayoutProps) => {
return (
<div className={s.layout}>
<Header {...headerProps} />
<div className={s.content}>{children}</div>
</div>
)
}

View File

@@ -93,23 +93,3 @@
color: var(--color-accent-500);
text-decoration-line: underline;
}
.icon {
all: unset;
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
justify-content: center;
&:focus-visible {
outline: var(--outline-focus);
outline-offset: 2px;
}
&.rounded {
border-radius: 9999px;
}
}

View File

@@ -1,7 +1,8 @@
import type { Meta, StoryObj } from '@storybook/react'
import { Logout } from '@/assets'
import { Button } from './'
import { Camera } from '@/assets'
const meta = {
argTypes: {
@@ -19,10 +20,17 @@ export default meta
type Story = StoryObj<typeof meta>
export const Primary: Story = {
args: {
children: 'Primary Button',
disabled: false,
variant: 'primary',
},
}
export const PrimaryWithIcon: Story = {
args: {
children: (
<>
Turn Camera On <Camera />
Sign out <Logout />
</>
),
disabled: false,
@@ -46,7 +54,7 @@ export const Tertiary: Story = {
}
export const Link: Story = {
args: {
children: 'Tertiary Button',
children: 'Link Button',
disabled: false,
variant: 'link',
},
@@ -63,7 +71,7 @@ export const FullWidth: Story = {
export const AsLink: Story = {
args: {
as: 'button',
as: 'a',
children: 'Link that looks like a button',
href: 'https://google.com',
variant: 'primary',

View File

@@ -16,8 +16,7 @@ export type ButtonProps<T extends ElementType = 'button'> = {
children: ReactNode
className?: string
fullWidth?: boolean
rounded?: boolean
variant?: 'icon' | 'link' | 'primary' | 'secondary' | 'tertiary'
variant?: 'link' | 'primary' | 'secondary' | 'tertiary'
} & ComponentPropsWithoutRef<T>
const ButtonPolymorph = <T extends ElementType = 'button'>(props: ButtonProps<T>, ref: any) => {
@@ -32,13 +31,7 @@ const ButtonPolymorph = <T extends ElementType = 'button'>(props: ButtonProps<T>
return (
<Component
className={clsx(
s.button,
s[variant],
fullWidth && s.fullWidth,
className,
rounded && s.rounded
)}
className={clsx(s.button, s[variant], fullWidth && s.fullWidth, className)}
{...rest}
ref={ref}
/>

View File

@@ -58,7 +58,6 @@
.arrow {
position: absolute;
top: -3.75px;
right: calc(50% - 3px);
transform: rotate(45deg);
width: 9px;

View File

@@ -9,29 +9,22 @@ const DropdownMenu = DropdownMenuPrimitive.Root
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
const DropdownMenuGroup = DropdownMenuPrimitive.Group
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
const DropdownMenuContent = forwardRef<
ElementRef<typeof DropdownMenuPrimitive.Content>,
ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ children, className, sideOffset = 8, ...props }, ref) => (
>(({ align = 'end', children, className, sideOffset = 8, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
align={align}
className={clsx(s.content, className)}
ref={ref}
sideOffset={sideOffset}
{...props}
>
<div>
<DropdownMenuPrimitive.Arrow asChild className={s.arrowBox}>
<DropdownMenuPrimitive.Arrow asChild>
<div className={s.arrow} />
</DropdownMenuPrimitive.Arrow>
<div className={s.itemsBox}>{children}</div>
</div>
</DropdownMenuPrimitive.Content>
</DropdownMenuPrimitive.Portal>
))
@@ -68,11 +61,8 @@ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
export {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuSeparator,
DropdownMenuTrigger,
}

View File

@@ -12,8 +12,31 @@ html {
box-sizing: border-box;
}
button {
all: unset;
cursor: pointer;
display: inline-flex;
align-items: center;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
font-style: inherit;
color: inherit;
user-select: none;
box-sizing: border-box;
&:focus-visible {
outline: var(--outline-focus);
outline-offset: 2px;
}
&:disabled {
cursor: default;
opacity: 0.5;
}
}
input,
button,
select,
textarea,
optgroup,