diff --git a/package.json b/package.json index 6aa27f2..62000db 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/src/assets/icons/edit-2-outline.tsx b/src/assets/icons/edit-2-outline.tsx new file mode 100644 index 0000000..8b8ca64 --- /dev/null +++ b/src/assets/icons/edit-2-outline.tsx @@ -0,0 +1,33 @@ +import { SVGProps } from 'react' +const SvgComponent = (props: SVGProps) => ( + + + + + + + + + + + +) + +export default SvgComponent diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index adf3ef0..efc4a51 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -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' diff --git a/src/assets/icons/keyboard-arrow-left.tsx b/src/assets/icons/keyboard-arrow-left.tsx new file mode 100644 index 0000000..7a56039 --- /dev/null +++ b/src/assets/icons/keyboard-arrow-left.tsx @@ -0,0 +1,27 @@ +import { Ref, SVGProps, forwardRef, memo } from 'react' +const SvgComponent = (props: SVGProps, ref: Ref) => ( + + + + + + + + + + +) +const ForwardRef = forwardRef(SvgComponent) + +export default memo(ForwardRef) diff --git a/src/assets/icons/keyboard-arrow-right.tsx b/src/assets/icons/keyboard-arrow-right.tsx new file mode 100644 index 0000000..4d1da91 --- /dev/null +++ b/src/assets/icons/keyboard-arrow-right.tsx @@ -0,0 +1,27 @@ +import { Ref, SVGProps, forwardRef, memo } from 'react' +const SvgComponent = (props: SVGProps, ref: Ref) => ( + + + + + + + + + + +) +const ForwardRef = forwardRef(SvgComponent) + +export default memo(ForwardRef) diff --git a/src/assets/icons/play-circle-outline.tsx b/src/assets/icons/play-circle-outline.tsx new file mode 100644 index 0000000..d626954 --- /dev/null +++ b/src/assets/icons/play-circle-outline.tsx @@ -0,0 +1,33 @@ +import { SVGProps } from 'react' +const SvgComponent = (props: SVGProps) => ( + + + + + + + + + + + +) + +export default SvgComponent diff --git a/src/assets/icons/search.tsx b/src/assets/icons/search.tsx new file mode 100644 index 0000000..ec77f00 --- /dev/null +++ b/src/assets/icons/search.tsx @@ -0,0 +1,28 @@ +import { Ref, SVGProps, forwardRef, memo } from 'react' +const SvgComponent = (props: SVGProps, ref: Ref) => ( + + + + + + + + + + +) + +export default memo(forwardRef(SvgComponent)) diff --git a/src/assets/icons/trash-outline.tsx b/src/assets/icons/trash-outline.tsx new file mode 100644 index 0000000..e711c21 --- /dev/null +++ b/src/assets/icons/trash-outline.tsx @@ -0,0 +1,27 @@ +import { SVGProps } from 'react' +const SvgComponent = (props: SVGProps) => ( + + + + + + + + + + +) + +export default SvgComponent diff --git a/src/components/decks/decks-table/decks-table.module.scss b/src/components/decks/decks-table/decks-table.module.scss new file mode 100644 index 0000000..64afbbd --- /dev/null +++ b/src/components/decks/decks-table/decks-table.module.scss @@ -0,0 +1,5 @@ +.iconsContainer { + display: flex; + gap: 10px; + align-items: center; +} diff --git a/src/components/decks/decks-table/decks-table.stories.ts b/src/components/decks/decks-table/decks-table.stories.ts new file mode 100644 index 0000000..4e1f124 --- /dev/null +++ b/src/components/decks/decks-table/decks-table.stories.ts @@ -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 + +export default meta +type Story = StoryObj + +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, + }, +} diff --git a/src/components/decks/decks-table/decks-table.tsx b/src/components/decks/decks-table/decks-table.tsx new file mode 100644 index 0000000..af1642d --- /dev/null +++ b/src/components/decks/decks-table/decks-table.tsx @@ -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 ( + + + + Name + Cards + Last updated + Author + Actions + + + + {decks?.map(deck => ( + + + + {deck.name} + + + {deck.cards} + {new Date(deck.lastUpdated).toLocaleString('ru-ru')} + {deck.createdBy} + +
+ + + +
+
+
+ ))} +
+
+ ) +} diff --git a/src/components/decks/decks-table/index.ts b/src/components/decks/decks-table/index.ts new file mode 100644 index 0000000..d6606d0 --- /dev/null +++ b/src/components/decks/decks-table/index.ts @@ -0,0 +1 @@ +export * from './decks-table' diff --git a/src/components/ui/button/button.module.scss b/src/components/ui/button/button.module.scss index 99309a5..560fca9 100644 --- a/src/components/ui/button/button.module.scss +++ b/src/components/ui/button/button.module.scss @@ -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; +} diff --git a/src/components/ui/button/button.stories.tsx b/src/components/ui/button/button.stories.tsx index 45907c6..e4d0c40 100644 --- a/src/components/ui/button/button.stories.tsx +++ b/src/components/ui/button/button.stories.tsx @@ -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 export default meta @@ -19,47 +19,47 @@ type Story = StoryObj 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', }, } diff --git a/src/components/ui/button/button.tsx b/src/components/ui/button/button.tsx index 3e703e0..dbcad6e 100644 --- a/src/components/ui/button/button.tsx +++ b/src/components/ui/button/button.tsx @@ -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 = { as?: T children: ReactNode - variant?: 'primary' | 'secondary' | 'tertiary' | 'link' - fullWidth?: boolean className?: string + fullWidth?: boolean + variant?: 'icon' | 'link' | 'primary' | 'secondary' | 'tertiary' } & ComponentPropsWithoutRef -export const Button = (props: ButtonProps) => { - const { variant = 'primary', fullWidth, className, as: Component = 'button', ...rest } = props +const ButtonPolymorph = (props: ButtonProps, ref: any) => { + const { + as: Component = 'button', + className, + fullWidth, + rounded, + variant = 'primary', + ...rest + } = props return ( - + ) } + +export const Button = forwardRef(ButtonPolymorph) as ( + props: ButtonProps & + Omit, keyof ButtonProps> & { + ref?: ForwardedRef> + } +) => ReturnType diff --git a/tsconfig.json b/tsconfig.json index 1c8358f..b151dc6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ /* Bundler mode */ "moduleResolution": "bundler", - "allowImportingTsExtensions": true, + "allowImportingTsExtensions": false, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true,