mirror of
https://github.com/ershisan99/flashcards-example-project.git
synced 2025-12-16 20:59:27 +00:00
add decks table component for convenience
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
"preview": "vite preview",
|
||||
"format": "prettier --write src",
|
||||
"lint": "eslint --fix src/**/*.{tsx,ts,jsx,js} --no-error-on-unmatched-pattern && stylelint --fix src/{,*/}*.{scss,css} --allow-empty-input",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"sb": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
33
src/assets/icons/edit-2-outline.tsx
Normal file
33
src/assets/icons/edit-2-outline.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { SVGProps } from 'react'
|
||||
const SvgComponent = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
fill={'none'}
|
||||
height={'16'}
|
||||
viewBox={'0 0 16 16'}
|
||||
width={'16'}
|
||||
xmlns={'http://www.w3.org/2000/svg'}
|
||||
{...props}
|
||||
>
|
||||
<g clipPath={'url(#clip0_28366_3239)'}>
|
||||
<path
|
||||
d={
|
||||
'M12.6666 13.3334H3.33329C3.15648 13.3334 2.98691 13.4036 2.86189 13.5286C2.73686 13.6537 2.66663 13.8232 2.66663 14C2.66663 14.1769 2.73686 14.3464 2.86189 14.4714C2.98691 14.5965 3.15648 14.6667 3.33329 14.6667H12.6666C12.8434 14.6667 13.013 14.5965 13.138 14.4714C13.2631 14.3464 13.3333 14.1769 13.3333 14C13.3333 13.8232 13.2631 13.6537 13.138 13.5286C13.013 13.4036 12.8434 13.3334 12.6666 13.3334Z'
|
||||
}
|
||||
fill={'white'}
|
||||
/>
|
||||
<path
|
||||
d={
|
||||
'M3.33329 12H3.39329L6.17329 11.7467C6.47782 11.7163 6.76264 11.5821 6.97995 11.3667L12.98 5.36665C13.2128 5.12063 13.3387 4.79233 13.3299 4.45368C13.3212 4.11503 13.1786 3.79366 12.9333 3.55999L11.1066 1.73332C10.8682 1.50938 10.5558 1.38089 10.2288 1.37229C9.90187 1.36368 9.58314 1.47557 9.33329 1.68665L3.33329 7.68665C3.1178 7.90396 2.98362 8.18879 2.95329 8.49332L2.66662 11.2733C2.65764 11.371 2.67031 11.4694 2.70373 11.5616C2.73715 11.6538 2.79049 11.7374 2.85995 11.8067C2.92225 11.8684 2.99612 11.9173 3.07735 11.9505C3.15857 11.9837 3.24555 12.0005 3.33329 12ZM10.18 2.66665L12 4.48665L10.6666 5.78665L8.87995 3.99999L10.18 2.66665ZM4.24662 8.60665L7.99995 4.87999L9.79995 6.67999L6.06662 10.4133L4.06662 10.6L4.24662 8.60665Z'
|
||||
}
|
||||
fill={'white'}
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id={'clip0_28366_3239'}>
|
||||
<rect fill={'white'} height={'16'} width={'16'} />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default SvgComponent
|
||||
@@ -1,11 +1,17 @@
|
||||
export { default as Eye } from './eye'
|
||||
export { default as VisibilityOff } from './visibility-off'
|
||||
export { default as Check } from './check'
|
||||
export { default as Email } from './email'
|
||||
export { default as Camera } from './camera'
|
||||
export { default as Logout } from './logout'
|
||||
export { default as Edit } from './edit'
|
||||
export { default as Logo } from './logo'
|
||||
export { default as PersonOutline } from './person-outline'
|
||||
export { default as Check } from './check'
|
||||
export { default as ChevronUp } from './chevron-up'
|
||||
export { default as Close } from './close'
|
||||
export { default as Edit } from './edit'
|
||||
export { default as Edit2Outline } from './edit-2-outline'
|
||||
export { default as Email } from './email'
|
||||
export { default as Eye } from './eye'
|
||||
export { default as KeyboardArrowLeft } from './keyboard-arrow-left'
|
||||
export { default as KeyboardArrowRight } from './keyboard-arrow-right'
|
||||
export { default as Logo } from './logo'
|
||||
export { default as Logout } from './logout'
|
||||
export { default as PersonOutline } from './person-outline'
|
||||
export { default as PlayCircleOutline } from './play-circle-outline'
|
||||
export { default as Search } from './search'
|
||||
export { default as TrashOutline } from './trash-outline'
|
||||
export { default as VisibilityOff } from './visibility-off'
|
||||
|
||||
27
src/assets/icons/keyboard-arrow-left.tsx
Normal file
27
src/assets/icons/keyboard-arrow-left.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Ref, SVGProps, forwardRef, memo } from 'react'
|
||||
const SvgComponent = (props: SVGProps<SVGSVGElement>, ref: Ref<SVGSVGElement>) => (
|
||||
<svg
|
||||
fill={'none'}
|
||||
height={'16'}
|
||||
ref={ref}
|
||||
viewBox={'0 0 16 16'}
|
||||
width={'16'}
|
||||
xmlns={'http://www.w3.org/2000/svg'}
|
||||
{...props}
|
||||
>
|
||||
<g clipPath={'url(#clip0_5928_3055)'}>
|
||||
<path
|
||||
d={'M10.2733 11.06L7.21998 8L10.2733 4.94L9.33331 4L5.33331 8L9.33331 12L10.2733 11.06Z'}
|
||||
fill={'white'}
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id={'clip0_5928_3055'}>
|
||||
<rect fill={'white'} height={'16'} width={'16'} />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
const ForwardRef = forwardRef(SvgComponent)
|
||||
|
||||
export default memo(ForwardRef)
|
||||
27
src/assets/icons/keyboard-arrow-right.tsx
Normal file
27
src/assets/icons/keyboard-arrow-right.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Ref, SVGProps, forwardRef, memo } from 'react'
|
||||
const SvgComponent = (props: SVGProps<SVGSVGElement>, ref: Ref<SVGSVGElement>) => (
|
||||
<svg
|
||||
fill={'none'}
|
||||
height={'16'}
|
||||
ref={ref}
|
||||
viewBox={'0 0 16 16'}
|
||||
width={'16'}
|
||||
xmlns={'http://www.w3.org/2000/svg'}
|
||||
{...props}
|
||||
>
|
||||
<g clipPath={'url(#clip0_5928_3027)'}>
|
||||
<path
|
||||
d={'M5.72665 11.06L8.77999 8L5.72665 4.94L6.66665 4L10.6667 8L6.66665 12L5.72665 11.06Z'}
|
||||
fill={'white'}
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id={'clip0_5928_3027'}>
|
||||
<rect fill={'white'} height={'16'} width={'16'} />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
const ForwardRef = forwardRef(SvgComponent)
|
||||
|
||||
export default memo(ForwardRef)
|
||||
33
src/assets/icons/play-circle-outline.tsx
Normal file
33
src/assets/icons/play-circle-outline.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { SVGProps } from 'react'
|
||||
const SvgComponent = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
fill={'none'}
|
||||
height={'16'}
|
||||
viewBox={'0 0 16 16'}
|
||||
width={'16'}
|
||||
xmlns={'http://www.w3.org/2000/svg'}
|
||||
{...props}
|
||||
>
|
||||
<g clipPath={'url(#clip0_28366_3435)'}>
|
||||
<path
|
||||
d={
|
||||
'M8.00004 1.33337C6.6815 1.33337 5.39257 1.72437 4.29624 2.45691C3.19991 3.18945 2.34543 4.23064 1.84085 5.44882C1.33626 6.66699 1.20424 8.00744 1.46148 9.30064C1.71871 10.5938 2.35365 11.7817 3.286 12.7141C4.21835 13.6464 5.40624 14.2814 6.69944 14.5386C7.99265 14.7958 9.33309 14.6638 10.5513 14.1592C11.7694 13.6547 12.8106 12.8002 13.5432 11.7038C14.2757 10.6075 14.6667 9.31858 14.6667 8.00004C14.6667 7.12456 14.4943 6.25766 14.1592 5.44882C13.8242 4.63998 13.3331 3.90505 12.7141 3.286C12.095 2.66694 11.3601 2.17588 10.5513 1.84084C9.74243 1.50581 8.87552 1.33337 8.00004 1.33337ZM8.00004 13.3334C6.94521 13.3334 5.91406 13.0206 5.037 12.4345C4.15994 11.8485 3.47635 11.0156 3.07269 10.041C2.66902 9.06648 2.5634 7.99412 2.76919 6.95956C2.97498 5.92499 3.48293 4.97468 4.22881 4.2288C4.97469 3.48292 5.925 2.97497 6.95956 2.76919C7.99413 2.5634 9.06648 2.66902 10.041 3.07268C11.0156 3.47635 11.8485 4.15994 12.4345 5.037C13.0206 5.91406 13.3334 6.94521 13.3334 8.00004C13.3334 9.41453 12.7715 10.7711 11.7713 11.7713C10.7711 12.7715 9.41453 13.3334 8.00004 13.3334Z'
|
||||
}
|
||||
fill={'white'}
|
||||
/>
|
||||
<path
|
||||
d={
|
||||
'M8.22666 4.96667C8.06331 4.81613 7.85932 4.71692 7.64004 4.68136C7.42077 4.6458 7.19588 4.67547 6.99333 4.76667C6.7967 4.84621 6.62825 4.98257 6.5095 5.15832C6.39075 5.33407 6.32709 5.54123 6.32666 5.75334V10.2467C6.32709 10.4588 6.39075 10.6659 6.5095 10.8417C6.62825 11.0174 6.7967 11.1538 6.99333 11.2333C7.13784 11.2989 7.29464 11.333 7.45333 11.3333C7.73927 11.3321 8.01467 11.2252 8.22666 11.0333L10.6667 8.78667C10.7758 8.68674 10.8629 8.56519 10.9226 8.42976C10.9822 8.29433 11.013 8.14798 11.013 8C11.013 7.85203 10.9822 7.70568 10.9226 7.57025C10.8629 7.43482 10.7758 7.31327 10.6667 7.21334L8.22666 4.96667ZM7.66666 9.73334V6.26667L9.53999 8L7.66666 9.73334Z'
|
||||
}
|
||||
fill={'white'}
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id={'clip0_28366_3435'}>
|
||||
<rect fill={'white'} height={'16'} width={'16'} />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default SvgComponent
|
||||
28
src/assets/icons/search.tsx
Normal file
28
src/assets/icons/search.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Ref, SVGProps, forwardRef, memo } from 'react'
|
||||
const SvgComponent = (props: SVGProps<SVGSVGElement>, ref: Ref<SVGSVGElement>) => (
|
||||
<svg
|
||||
fill={'none'}
|
||||
height={20}
|
||||
ref={ref}
|
||||
viewBox={'0 0 20 20'}
|
||||
width={20}
|
||||
xmlns={'http://www.w3.org/2000/svg'}
|
||||
{...props}
|
||||
>
|
||||
<g clipPath={'url(#clip0_21547_180)'}>
|
||||
<path
|
||||
d={
|
||||
'M17.2583 16.075L14.425 13.25C15.3392 12.0854 15.8352 10.6472 15.8333 9.16667C15.8333 7.84813 15.4423 6.5592 14.7098 5.46287C13.9773 4.36654 12.9361 3.51206 11.7179 3.00747C10.4997 2.50289 9.15927 2.37087 7.86607 2.6281C6.57286 2.88534 5.38497 3.52027 4.45262 4.45262C3.52027 5.38497 2.88534 6.57286 2.6281 7.86607C2.37087 9.15927 2.50289 10.4997 3.00747 11.7179C3.51206 12.9361 4.36654 13.9773 5.46287 14.7098C6.5592 15.4423 7.84813 15.8333 9.16667 15.8333C10.6472 15.8352 12.0854 15.3392 13.25 14.425L16.075 17.2583C16.1525 17.3364 16.2446 17.3984 16.3462 17.4407C16.4477 17.4831 16.5567 17.5048 16.6667 17.5048C16.7767 17.5048 16.8856 17.4831 16.9871 17.4407C17.0887 17.3984 17.1809 17.3364 17.2583 17.2583C17.3364 17.1809 17.3984 17.0887 17.4407 16.9871C17.4831 16.8856 17.5048 16.7767 17.5048 16.6667C17.5048 16.5567 17.4831 16.4477 17.4407 16.3462C17.3984 16.2446 17.3364 16.1525 17.2583 16.075ZM4.16667 9.16667C4.16667 8.17776 4.45991 7.21106 5.00932 6.38882C5.55873 5.56657 6.33962 4.92571 7.25325 4.54727C8.16688 4.16883 9.17222 4.06982 10.1421 4.26274C11.112 4.45567 12.0029 4.93187 12.7022 5.63114C13.4015 6.3304 13.8777 7.22131 14.0706 8.19122C14.2635 9.16112 14.1645 10.1665 13.7861 11.0801C13.4076 11.9937 12.7668 12.7746 11.9445 13.324C11.1223 13.8734 10.1556 14.1667 9.16667 14.1667C7.84059 14.1667 6.56882 13.6399 5.63114 12.7022C4.69345 11.7645 4.16667 10.4928 4.16667 9.16667Z'
|
||||
}
|
||||
fill={'#808080'}
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id={'clip0_21547_180'}>
|
||||
<rect fill={'white'} height={20} width={20} />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default memo(forwardRef(SvgComponent))
|
||||
27
src/assets/icons/trash-outline.tsx
Normal file
27
src/assets/icons/trash-outline.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { SVGProps } from 'react'
|
||||
const SvgComponent = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
fill={'none'}
|
||||
height={'16'}
|
||||
viewBox={'0 0 16 16'}
|
||||
width={'16'}
|
||||
xmlns={'http://www.w3.org/2000/svg'}
|
||||
{...props}
|
||||
>
|
||||
<g clipPath={'url(#clip0_28366_3245)'}>
|
||||
<path
|
||||
d={
|
||||
'M14 3.99998H10.6667V2.88665C10.6511 2.45986 10.4668 2.05669 10.1544 1.76551C9.842 1.47433 9.42687 1.31891 9.00004 1.33332H7.00004C6.57321 1.31891 6.15808 1.47433 5.84566 1.76551C5.53324 2.05669 5.34901 2.45986 5.33337 2.88665V3.99998H2.00004C1.82323 3.99998 1.65366 4.07022 1.52864 4.19525C1.40361 4.32027 1.33337 4.48984 1.33337 4.66665C1.33337 4.84346 1.40361 5.01303 1.52864 5.13805C1.65366 5.26308 1.82323 5.33332 2.00004 5.33332H2.66671V12.6666C2.66671 13.1971 2.87742 13.7058 3.25249 14.0809C3.62757 14.4559 4.13627 14.6666 4.66671 14.6666H11.3334C11.8638 14.6666 12.3725 14.4559 12.7476 14.0809C13.1227 13.7058 13.3334 13.1971 13.3334 12.6666V5.33332H14C14.1769 5.33332 14.3464 5.26308 14.4714 5.13805C14.5965 5.01303 14.6667 4.84346 14.6667 4.66665C14.6667 4.48984 14.5965 4.32027 14.4714 4.19525C14.3464 4.07022 14.1769 3.99998 14 3.99998ZM6.66671 2.88665C6.66671 2.77998 6.80671 2.66665 7.00004 2.66665H9.00004C9.19337 2.66665 9.33337 2.77998 9.33337 2.88665V3.99998H6.66671V2.88665ZM12 12.6666C12 12.8435 11.9298 13.013 11.8048 13.1381C11.6798 13.2631 11.5102 13.3333 11.3334 13.3333H4.66671C4.4899 13.3333 4.32033 13.2631 4.1953 13.1381C4.07028 13.013 4.00004 12.8435 4.00004 12.6666V5.33332H12V12.6666Z'
|
||||
}
|
||||
fill={'white'}
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id={'clip0_28366_3245'}>
|
||||
<rect fill={'white'} height={'16'} width={'16'} />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default SvgComponent
|
||||
5
src/components/decks/decks-table/decks-table.module.scss
Normal file
5
src/components/decks/decks-table/decks-table.module.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
.iconsContainer {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
56
src/components/decks/decks-table/decks-table.stories.ts
Normal file
56
src/components/decks/decks-table/decks-table.stories.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { DecksTable } from './'
|
||||
|
||||
const meta = {
|
||||
component: DecksTable,
|
||||
tags: ['autodocs'],
|
||||
title: 'Components/Decks/Table',
|
||||
} satisfies Meta<typeof DecksTable>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
const mockDecks = [
|
||||
{
|
||||
cards: 50,
|
||||
createdBy: 'Anthony Johnson',
|
||||
id: 'fddb846b-f674-4507-a0c8-a8a9ef68c9b6',
|
||||
lastUpdated: '2023-09-04T22:35:07',
|
||||
name: 'Court',
|
||||
},
|
||||
{
|
||||
cards: 35,
|
||||
createdBy: 'Mark Brown',
|
||||
id: '2d8324af-3516-47d5-8f42-8ad7a68fd9c2',
|
||||
lastUpdated: '2023-08-19T22:45:58',
|
||||
name: 'Personal',
|
||||
},
|
||||
{
|
||||
cards: 43,
|
||||
createdBy: 'Teresa Ward',
|
||||
id: '0110bf6d-305a-453a-90fa-3af94cd910ce',
|
||||
lastUpdated: '2023-09-24T01:49:08',
|
||||
name: 'Begin',
|
||||
},
|
||||
{
|
||||
cards: 7,
|
||||
createdBy: 'Julian Mcbride',
|
||||
id: '2e7153b1-29be-488c-9083-6fa363761b4d',
|
||||
lastUpdated: '2023-07-11T13:10:15',
|
||||
name: 'Sure',
|
||||
},
|
||||
{
|
||||
cards: 2,
|
||||
createdBy: 'Carl Johnson',
|
||||
id: '369236a3-8230-4fa4-b1af-5ffec321e3e6',
|
||||
lastUpdated: '2023-04-02T05:21:35',
|
||||
name: 'Sign',
|
||||
},
|
||||
]
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
decks: mockDecks,
|
||||
},
|
||||
}
|
||||
72
src/components/decks/decks-table/decks-table.tsx
Normal file
72
src/components/decks/decks-table/decks-table.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Edit2Outline, PlayCircleOutline, TrashOutline } from '@/assets'
|
||||
import { Button, Typography } from '@/components'
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeadCell,
|
||||
TableRow,
|
||||
} from '@/components/ui/table'
|
||||
|
||||
import s from './decks-table.module.scss'
|
||||
|
||||
type Deck = {
|
||||
cards: number
|
||||
createdBy: string
|
||||
id: string
|
||||
lastUpdated: string
|
||||
name: string
|
||||
}
|
||||
|
||||
type Props = {
|
||||
decks: Deck[] | undefined
|
||||
onDeleteClick?: (id: string) => void
|
||||
onEditClick?: (id: string) => void
|
||||
}
|
||||
|
||||
export const DecksTable = ({ decks, onDeleteClick, onEditClick }: Props) => {
|
||||
const handleEditClick = (id: string) => () => onEditClick?.(id)
|
||||
const handleDeleteClick = (id: string) => () => onDeleteClick?.(id)
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeadCell>Name</TableHeadCell>
|
||||
<TableHeadCell>Cards</TableHeadCell>
|
||||
<TableHeadCell>Last updated</TableHeadCell>
|
||||
<TableHeadCell>Author</TableHeadCell>
|
||||
<TableHeadCell>Actions</TableHeadCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{decks?.map(deck => (
|
||||
<TableRow key={deck.id}>
|
||||
<TableCell>
|
||||
<Typography as={'a'} href={`/decks/${deck.id}`} variant={'body2'}>
|
||||
{deck.name}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{deck.cards}</TableCell>
|
||||
<TableCell>{new Date(deck.lastUpdated).toLocaleString('ru-ru')}</TableCell>
|
||||
<TableCell>{deck.createdBy}</TableCell>
|
||||
<TableCell>
|
||||
<div className={s.iconsContainer}>
|
||||
<Button as={'a'} href={`/decks/${deck.id}/learn`} variant={'icon'}>
|
||||
<PlayCircleOutline />
|
||||
</Button>
|
||||
<Button onClick={handleEditClick(deck.id)} variant={'icon'}>
|
||||
<Edit2Outline />
|
||||
</Button>
|
||||
<Button onClick={handleDeleteClick(deck.id)} variant={'icon'}>
|
||||
<TrashOutline />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
1
src/components/decks/decks-table/index.ts
Normal file
1
src/components/decks/decks-table/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './decks-table'
|
||||
@@ -1,4 +1,4 @@
|
||||
@mixin button {
|
||||
.button {
|
||||
all: unset;
|
||||
|
||||
cursor: pointer;
|
||||
@@ -28,7 +28,8 @@
|
||||
var(--transition-duration-basic) color;
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--color-info-700);
|
||||
outline: var(--outline-focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@@ -43,8 +44,7 @@
|
||||
}
|
||||
|
||||
.primary {
|
||||
@include button;
|
||||
|
||||
composes: button;
|
||||
color: var(--color-light-100);
|
||||
background-color: var(--color-accent-500);
|
||||
box-shadow: 0 4px 18px rgb(140 97 255 / 35%);
|
||||
@@ -59,8 +59,7 @@
|
||||
}
|
||||
|
||||
.secondary {
|
||||
@include button;
|
||||
|
||||
composes: button;
|
||||
color: var(--color-light-100);
|
||||
background-color: var(--color-dark-300);
|
||||
box-shadow: 0 2px 10px 0 #6d6d6d40;
|
||||
@@ -75,8 +74,7 @@
|
||||
}
|
||||
|
||||
.tertiary {
|
||||
@include button;
|
||||
|
||||
composes: button;
|
||||
color: var(--color-accent-500);
|
||||
background-color: var(--color-dark-900);
|
||||
border: 1px solid var(--color-accent-700);
|
||||
@@ -91,7 +89,7 @@
|
||||
}
|
||||
|
||||
.link {
|
||||
@include button;
|
||||
composes: button;
|
||||
|
||||
padding: 0.375rem 0;
|
||||
|
||||
@@ -100,3 +98,10 @@
|
||||
color: var(--color-accent-500);
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
|
||||
.icon {
|
||||
composes: button;
|
||||
|
||||
padding: 0;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@ import type { Meta, StoryObj } from '@storybook/react'
|
||||
import { Button } from './'
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Button',
|
||||
component: Button,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
variant: {
|
||||
options: ['primary', 'secondary', 'tertiary', 'link'],
|
||||
control: { type: 'radio' },
|
||||
options: ['primary', 'secondary', 'tertiary', 'link'],
|
||||
},
|
||||
},
|
||||
component: Button,
|
||||
tags: ['autodocs'],
|
||||
title: 'Components/Button',
|
||||
} satisfies Meta<typeof Button>
|
||||
|
||||
export default meta
|
||||
@@ -19,47 +19,47 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
children: 'Primary Button',
|
||||
disabled: false,
|
||||
variant: 'primary',
|
||||
},
|
||||
}
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
variant: 'secondary',
|
||||
children: 'Secondary Button',
|
||||
disabled: false,
|
||||
variant: 'secondary',
|
||||
},
|
||||
}
|
||||
export const Tertiary: Story = {
|
||||
args: {
|
||||
variant: 'tertiary',
|
||||
children: 'Tertiary Button',
|
||||
disabled: false,
|
||||
variant: 'tertiary',
|
||||
},
|
||||
}
|
||||
export const Link: Story = {
|
||||
args: {
|
||||
variant: 'link',
|
||||
children: 'Tertiary Button',
|
||||
disabled: false,
|
||||
variant: 'link',
|
||||
},
|
||||
}
|
||||
|
||||
export const FullWidth: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
children: 'Full Width Button',
|
||||
disabled: false,
|
||||
fullWidth: true,
|
||||
variant: 'primary',
|
||||
},
|
||||
}
|
||||
|
||||
export const AsLink: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
children: 'Link that looks like a button',
|
||||
as: 'a',
|
||||
children: 'Link that looks like a button',
|
||||
variant: 'primary',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,19 +1,46 @@
|
||||
import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react'
|
||||
import {
|
||||
ComponentPropsWithoutRef,
|
||||
ElementRef,
|
||||
ElementType,
|
||||
ForwardedRef,
|
||||
ReactNode,
|
||||
forwardRef,
|
||||
} from 'react'
|
||||
|
||||
import { clsx } from 'clsx'
|
||||
|
||||
import s from './button.module.scss'
|
||||
|
||||
export type ButtonProps<T extends ElementType = 'button'> = {
|
||||
as?: T
|
||||
children: ReactNode
|
||||
variant?: 'primary' | 'secondary' | 'tertiary' | 'link'
|
||||
fullWidth?: boolean
|
||||
className?: string
|
||||
fullWidth?: boolean
|
||||
variant?: 'icon' | 'link' | 'primary' | 'secondary' | 'tertiary'
|
||||
} & ComponentPropsWithoutRef<T>
|
||||
|
||||
export const Button = <T extends ElementType = 'button'>(props: ButtonProps<T>) => {
|
||||
const { variant = 'primary', fullWidth, className, as: Component = 'button', ...rest } = props
|
||||
const ButtonPolymorph = <T extends ElementType = 'button'>(props: ButtonProps<T>, ref: any) => {
|
||||
const {
|
||||
as: Component = 'button',
|
||||
className,
|
||||
fullWidth,
|
||||
rounded,
|
||||
variant = 'primary',
|
||||
...rest
|
||||
} = props
|
||||
|
||||
return (
|
||||
<Component className={`${s[variant]} ${fullWidth ? s.fullWidth : ''} ${className}`} {...rest} />
|
||||
<Component
|
||||
className={clsx(s[variant], fullWidth && s.fullWidth, className)}
|
||||
{...rest}
|
||||
ref={ref}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export const Button = forwardRef(ButtonPolymorph) as <T extends ElementType = 'button'>(
|
||||
props: ButtonProps<T> &
|
||||
Omit<ComponentPropsWithoutRef<T>, keyof ButtonProps<T>> & {
|
||||
ref?: ForwardedRef<ElementRef<T>>
|
||||
}
|
||||
) => ReturnType<typeof ButtonPolymorph>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"allowImportingTsExtensions": false,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
|
||||
Reference in New Issue
Block a user