mirror of
https://github.com/ershisan99/flashcards-example-project.git
synced 2025-12-16 20:59:27 +00:00
Merge master into storybook-deploy
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
1
src/components/layout/index.ts
Normal file
1
src/components/layout/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './layout.tsx'
|
||||
14
src/components/layout/layout.module.scss
Normal file
14
src/components/layout/layout.module.scss
Normal 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;
|
||||
}
|
||||
36
src/components/layout/layout.stories.tsx
Normal file
36
src/components/layout/layout.stories.tsx
Normal 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,
|
||||
},
|
||||
}
|
||||
16
src/components/layout/layout.tsx
Normal file
16
src/components/layout/layout.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
.arrow {
|
||||
position: absolute;
|
||||
top: -3.75px;
|
||||
right: calc(50% - 3px);
|
||||
transform: rotate(45deg);
|
||||
|
||||
width: 9px;
|
||||
|
||||
@@ -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}>
|
||||
<div className={s.arrow} />
|
||||
</DropdownMenuPrimitive.Arrow>
|
||||
<div className={s.itemsBox}>{children}</div>
|
||||
</div>
|
||||
<DropdownMenuPrimitive.Arrow asChild>
|
||||
<div className={s.arrow} />
|
||||
</DropdownMenuPrimitive.Arrow>
|
||||
<div className={s.itemsBox}>{children}</div>
|
||||
</DropdownMenuPrimitive.Content>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
))
|
||||
@@ -68,11 +61,8 @@ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user