mirror of
https://github.com/ershisan99/flashcards-example-project.git
synced 2025-12-25 12:33:49 +00:00
chore: update deps, apply updated eslint ocnfig
This commit is contained in:
@@ -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',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import s from './button.module.scss'
|
||||
export type ButtonProps<T extends ElementType = 'button'> = {
|
||||
as?: T
|
||||
children: ReactNode
|
||||
variant?: 'primary' | 'secondary' | 'tertiary' | 'link' | 'icon'
|
||||
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 { as: Component = 'button', className, fullWidth, variant = 'primary', ...rest } = props
|
||||
|
||||
return (
|
||||
<Component className={`${s[variant]} ${fullWidth ? s.fullWidth : ''} ${className}`} {...rest} />
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { Card } from './'
|
||||
|
||||
import { Typography } from '@/components'
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Card',
|
||||
component: Card,
|
||||
tags: ['autodocs'],
|
||||
title: 'Components/Card',
|
||||
} satisfies Meta<typeof Card>
|
||||
|
||||
export default meta
|
||||
@@ -17,9 +16,9 @@ export const Default: Story = {
|
||||
args: {
|
||||
children: <Typography variant={'large'}>Card</Typography>,
|
||||
style: {
|
||||
width: '300px',
|
||||
height: '300px',
|
||||
padding: '24px',
|
||||
width: '300px',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,5 +11,5 @@ export const Card = forwardRef<HTMLDivElement, CardProps>(({ className, ...restP
|
||||
root: clsx(s.root, className),
|
||||
}
|
||||
|
||||
return <div ref={ref} className={classNames.root} {...restProps}></div>
|
||||
return <div className={classNames.root} ref={ref} {...restProps}></div>
|
||||
})
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { Checkbox } from './checkbox'
|
||||
import { Meta, StoryObj } from '@storybook/react'
|
||||
const meta = {
|
||||
title: 'Components/Checkbox',
|
||||
component: Checkbox,
|
||||
tags: ['autodocs'],
|
||||
title: 'Components/Checkbox',
|
||||
} satisfies Meta<typeof Checkbox>
|
||||
|
||||
export default meta
|
||||
@@ -14,8 +13,8 @@ export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
export const Uncontrolled: Story = {
|
||||
args: {
|
||||
label: 'Click here',
|
||||
disabled: false,
|
||||
label: 'Click here',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -26,8 +25,8 @@ export const Controlled: Story = {
|
||||
return (
|
||||
<Checkbox
|
||||
{...args}
|
||||
label="Click here"
|
||||
checked={checked}
|
||||
label={'Click here'}
|
||||
onChange={() => setChecked(!checked)}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,55 +1,54 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
import { Check } from '@/assets/icons'
|
||||
import { Typography } from '@/components'
|
||||
import * as CheckboxRadix from '@radix-ui/react-checkbox'
|
||||
import * as LabelRadix from '@radix-ui/react-label'
|
||||
import { clsx } from 'clsx'
|
||||
|
||||
import s from './checkbox.module.scss'
|
||||
|
||||
import { Check } from '@/assets/icons'
|
||||
import { Typography } from '@/components'
|
||||
|
||||
export type CheckboxProps = {
|
||||
className?: string
|
||||
checked?: boolean
|
||||
onChange?: (checked: boolean) => void
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
required?: boolean
|
||||
label?: string
|
||||
id?: string
|
||||
label?: string
|
||||
onChange?: (checked: boolean) => void
|
||||
position?: 'left'
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
export const Checkbox: FC<CheckboxProps> = ({
|
||||
checked,
|
||||
className,
|
||||
disabled,
|
||||
id,
|
||||
label,
|
||||
onChange,
|
||||
position,
|
||||
disabled,
|
||||
required,
|
||||
label,
|
||||
id,
|
||||
className,
|
||||
}) => {
|
||||
const classNames = {
|
||||
container: clsx(s.container, className),
|
||||
buttonWrapper: clsx(s.buttonWrapper, disabled && s.disabled, position === 'left' && s.left),
|
||||
root: s.root,
|
||||
container: clsx(s.container, className),
|
||||
indicator: s.indicator,
|
||||
label: clsx(s.label, disabled && s.disabled),
|
||||
root: s.root,
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames.container}>
|
||||
<LabelRadix.Root asChild>
|
||||
<Typography variant="body2" className={classNames.label} as={'label'}>
|
||||
<Typography as={'label'} className={classNames.label} variant={'body2'}>
|
||||
<div className={classNames.buttonWrapper}>
|
||||
<CheckboxRadix.Root
|
||||
className={classNames.root}
|
||||
checked={checked}
|
||||
onCheckedChange={onChange}
|
||||
className={classNames.root}
|
||||
disabled={disabled}
|
||||
required={required}
|
||||
id={id}
|
||||
onCheckedChange={onChange}
|
||||
required={required}
|
||||
>
|
||||
{checked && (
|
||||
<CheckboxRadix.Indicator className={classNames.indicator} forceMount>
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'
|
||||
import { FieldValues, UseControllerProps, useController } from 'react-hook-form'
|
||||
|
||||
import { Checkbox, CheckboxProps } from '../../'
|
||||
|
||||
export type ControlledCheckboxProps<TFieldValues extends FieldValues> =
|
||||
UseControllerProps<TFieldValues> & Omit<CheckboxProps, 'onChange' | 'value' | 'id'>
|
||||
UseControllerProps<TFieldValues> & Omit<CheckboxProps, 'id' | 'onChange' | 'value'>
|
||||
|
||||
export const ControlledCheckbox = <TFieldValues extends FieldValues>({
|
||||
control,
|
||||
defaultValue,
|
||||
name,
|
||||
rules,
|
||||
shouldUnregister,
|
||||
control,
|
||||
defaultValue,
|
||||
...checkboxProps
|
||||
}: ControlledCheckboxProps<TFieldValues>) => {
|
||||
const {
|
||||
field: { onChange, value },
|
||||
} = useController({
|
||||
control,
|
||||
defaultValue,
|
||||
name,
|
||||
rules,
|
||||
shouldUnregister,
|
||||
control,
|
||||
defaultValue,
|
||||
})
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
{...{
|
||||
onChange,
|
||||
checked: value,
|
||||
id: name,
|
||||
onChange,
|
||||
...checkboxProps,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -3,9 +3,9 @@ import { Control, FieldPath, FieldValues, useController } from 'react-hook-form'
|
||||
import { RadioGroup, RadioGroupProps } from '@/components/ui'
|
||||
|
||||
export type ControlledRadioGroupProps<TFieldValues extends FieldValues> = {
|
||||
name: FieldPath<TFieldValues>
|
||||
control: Control<TFieldValues>
|
||||
} & Omit<RadioGroupProps, 'onChange' | 'value' | 'id'>
|
||||
name: FieldPath<TFieldValues>
|
||||
} & Omit<RadioGroupProps, 'id' | 'onChange' | 'value'>
|
||||
|
||||
export const ControlledRadioGroup = <TFieldValues extends FieldValues>(
|
||||
props: ControlledRadioGroupProps<TFieldValues>
|
||||
@@ -14,17 +14,17 @@ export const ControlledRadioGroup = <TFieldValues extends FieldValues>(
|
||||
field: { onChange, ...field },
|
||||
fieldState: { error },
|
||||
} = useController({
|
||||
name: props.name,
|
||||
control: props.control,
|
||||
name: props.name,
|
||||
})
|
||||
|
||||
return (
|
||||
<RadioGroup
|
||||
{...props}
|
||||
{...field}
|
||||
onValueChange={onChange}
|
||||
errorMessage={error?.message}
|
||||
id={props.name}
|
||||
onValueChange={onChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { Control, FieldPath, FieldValues, useController } from 'react-hook-form'
|
||||
import { TextField, TextFieldProps } from '@/components'
|
||||
|
||||
export type ControlledTextFieldProps<TFieldValues extends FieldValues> = {
|
||||
name: FieldPath<TFieldValues>
|
||||
control: Control<TFieldValues>
|
||||
} & Omit<TextFieldProps, 'onChange' | 'value' | 'id'>
|
||||
name: FieldPath<TFieldValues>
|
||||
} & Omit<TextFieldProps, 'id' | 'onChange' | 'value'>
|
||||
|
||||
export const ControlledTextField = <TFieldValues extends FieldValues>(
|
||||
props: ControlledTextFieldProps<TFieldValues>
|
||||
@@ -14,8 +14,8 @@ export const ControlledTextField = <TFieldValues extends FieldValues>(
|
||||
field,
|
||||
fieldState: { error },
|
||||
} = useController({
|
||||
name: props.name,
|
||||
control: props.control,
|
||||
name: props.name,
|
||||
})
|
||||
|
||||
return <TextField {...props} {...field} errorMessage={error?.message} id={props.name} />
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Dialog } from './'
|
||||
import { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { Dialog } from './'
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Dialog',
|
||||
component: Dialog,
|
||||
tags: ['autodocs'],
|
||||
title: 'Components/Dialog',
|
||||
} satisfies Meta<typeof Dialog>
|
||||
|
||||
export default meta
|
||||
@@ -15,10 +14,10 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
open: true,
|
||||
onOpenChange: () => {},
|
||||
title: 'Modal',
|
||||
children: 'Modal',
|
||||
onOpenChange: () => {},
|
||||
open: true,
|
||||
title: 'Modal',
|
||||
},
|
||||
render: args => {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import s from './dialog.module.scss'
|
||||
|
||||
import { Button, Modal, ModalProps } from '@/components'
|
||||
|
||||
import s from './dialog.module.scss'
|
||||
|
||||
export type DialogProps = ModalProps & {
|
||||
confirmText?: string
|
||||
cancelText?: string
|
||||
onConfirm?: () => void
|
||||
confirmText?: string
|
||||
onCancel?: () => void
|
||||
onConfirm?: () => void
|
||||
}
|
||||
export const Dialog = ({
|
||||
cancelText = 'Cancel',
|
||||
children,
|
||||
confirmText = 'OK',
|
||||
onCancel,
|
||||
onConfirm,
|
||||
confirmText = 'OK',
|
||||
cancelText = 'Cancel',
|
||||
...modalProps
|
||||
}: DialogProps) => {
|
||||
return (
|
||||
<Modal {...modalProps}>
|
||||
{children}
|
||||
<div className={s.buttons}>
|
||||
<Button variant={'secondary'} onClick={onCancel}>
|
||||
<Button onClick={onCancel} variant={'secondary'}>
|
||||
{cancelText}
|
||||
</Button>
|
||||
<Button onClick={onConfirm}>{confirmText}</Button>
|
||||
|
||||
@@ -9,7 +9,7 @@ export type LabelProps = {
|
||||
label?: ReactNode
|
||||
} & ComponentPropsWithoutRef<'label'>
|
||||
|
||||
export const Label: FC<LabelProps> = ({ label, children, className, ...rest }) => {
|
||||
export const Label: FC<LabelProps> = ({ children, className, label, ...rest }) => {
|
||||
const classNames = {
|
||||
label: clsx(s.label, className),
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Modal } from '@/components'
|
||||
import { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { Modal } from '@/components'
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Modal',
|
||||
component: Modal,
|
||||
tags: ['autodocs'],
|
||||
title: 'Components/Modal',
|
||||
} satisfies Meta<typeof Modal>
|
||||
|
||||
export default meta
|
||||
@@ -15,10 +14,10 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
open: true,
|
||||
onOpenChange: () => {},
|
||||
title: 'Modal',
|
||||
children: 'Modal',
|
||||
onOpenChange: () => {},
|
||||
open: true,
|
||||
title: 'Modal',
|
||||
},
|
||||
render: args => {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { ComponentPropsWithoutRef, ReactNode } from 'react'
|
||||
|
||||
import { Close } from '@/assets'
|
||||
import { Typography } from '@/components'
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
||||
|
||||
import s from './modal.module.scss'
|
||||
|
||||
import { Close } from '@/assets'
|
||||
import { Typography } from '@/components'
|
||||
|
||||
export type ModalProps = {
|
||||
title?: string
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
children: ReactNode
|
||||
} & Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Dialog>, 'open' | 'onOpenChange'>
|
||||
onOpenChange: (open: boolean) => void
|
||||
open: boolean
|
||||
title?: string
|
||||
} & Omit<ComponentPropsWithoutRef<typeof DialogPrimitive.Dialog>, 'onOpenChange' | 'open'>
|
||||
export const Modal = ({ children, title, ...props }: ModalProps) => {
|
||||
return (
|
||||
<DialogPrimitive.Root {...props}>
|
||||
@@ -21,7 +20,7 @@ export const Modal = ({ children, title, ...props }: ModalProps) => {
|
||||
<DialogPrimitive.Content className={s.content}>
|
||||
<div className={s.header}>
|
||||
<DialogPrimitive.Title asChild>
|
||||
<Typography variant={'h2'} as={'h2'}>
|
||||
<Typography as={'h2'} variant={'h2'}>
|
||||
{title}
|
||||
</Typography>
|
||||
</DialogPrimitive.Title>
|
||||
|
||||
@@ -11,5 +11,5 @@ export const Page = forwardRef<HTMLDivElement, PageProps>(({ className, ...props
|
||||
root: clsx(s.root, className),
|
||||
}
|
||||
|
||||
return <div {...props} ref={ref} className={classNames.root} />
|
||||
return <div {...props} className={classNames.root} ref={ref} />
|
||||
})
|
||||
|
||||
@@ -1,67 +1,66 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
import { usePagination } from './usePagination'
|
||||
import { KeyboardArrowLeft, KeyboardArrowRight } from '@/assets'
|
||||
import { clsx } from 'clsx'
|
||||
|
||||
import s from './pagination.module.scss'
|
||||
import { usePagination } from './usePagination'
|
||||
|
||||
import { KeyboardArrowLeft, KeyboardArrowRight } from '@/assets'
|
||||
|
||||
type PaginationConditionals =
|
||||
| {
|
||||
perPage?: null
|
||||
perPageOptions?: never
|
||||
onPerPageChange?: never
|
||||
}
|
||||
| {
|
||||
onPerPageChange: (itemPerPage: number) => void
|
||||
perPage: number
|
||||
perPageOptions: number[]
|
||||
onPerPageChange: (itemPerPage: number) => void
|
||||
}
|
||||
| {
|
||||
onPerPageChange?: never
|
||||
perPage?: null
|
||||
perPageOptions?: never
|
||||
}
|
||||
|
||||
export type PaginationProps = {
|
||||
count: number
|
||||
page: number
|
||||
onChange: (page: number) => void
|
||||
siblings?: number
|
||||
onPerPageChange?: (itemPerPage: number) => void
|
||||
page: number
|
||||
perPage?: number
|
||||
perPageOptions?: number[]
|
||||
onPerPageChange?: (itemPerPage: number) => void
|
||||
siblings?: number
|
||||
} & PaginationConditionals
|
||||
|
||||
const classNames = {
|
||||
root: s.root,
|
||||
container: s.container,
|
||||
selectBox: s.selectBox,
|
||||
select: s.select,
|
||||
item: s.item,
|
||||
dots: s.dots,
|
||||
icon: s.icon,
|
||||
item: s.item,
|
||||
pageButton(selected?: boolean) {
|
||||
return clsx(this.item, selected && s.selected)
|
||||
},
|
||||
root: s.root,
|
||||
select: s.select,
|
||||
selectBox: s.selectBox,
|
||||
}
|
||||
|
||||
export const Pagination: FC<PaginationProps> = ({
|
||||
onChange,
|
||||
count,
|
||||
onChange,
|
||||
onPerPageChange,
|
||||
page,
|
||||
perPage = null,
|
||||
perPageOptions,
|
||||
onPerPageChange,
|
||||
siblings,
|
||||
}) => {
|
||||
const {
|
||||
paginationRange,
|
||||
isLastPage,
|
||||
isFirstPage,
|
||||
handlePreviousPageClicked,
|
||||
handleNextPageClicked,
|
||||
handleMainPageClicked,
|
||||
handleNextPageClicked,
|
||||
handlePreviousPageClicked,
|
||||
isFirstPage,
|
||||
isLastPage,
|
||||
paginationRange,
|
||||
} = usePagination({
|
||||
page,
|
||||
count,
|
||||
onChange,
|
||||
page,
|
||||
siblings,
|
||||
})
|
||||
|
||||
@@ -70,7 +69,7 @@ export const Pagination: FC<PaginationProps> = ({
|
||||
return (
|
||||
<div className={classNames.root}>
|
||||
<div className={classNames.container}>
|
||||
<PrevButton onClick={handlePreviousPageClicked} disabled={isFirstPage} />
|
||||
<PrevButton disabled={isFirstPage} onClick={handlePreviousPageClicked} />
|
||||
|
||||
<MainPaginationButtons
|
||||
currentPage={page}
|
||||
@@ -78,15 +77,15 @@ export const Pagination: FC<PaginationProps> = ({
|
||||
paginationRange={paginationRange}
|
||||
/>
|
||||
|
||||
<NextButton onClick={handleNextPageClicked} disabled={isLastPage} />
|
||||
<NextButton disabled={isLastPage} onClick={handleNextPageClicked} />
|
||||
</div>
|
||||
|
||||
{showPerPageSelect && (
|
||||
<PerPageSelect
|
||||
{...{
|
||||
onPerPageChange,
|
||||
perPage,
|
||||
perPageOptions,
|
||||
onPerPageChange,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -95,8 +94,8 @@ export const Pagination: FC<PaginationProps> = ({
|
||||
}
|
||||
|
||||
type NavigationButtonProps = {
|
||||
onClick: () => void
|
||||
disabled?: boolean
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
type PageButtonProps = NavigationButtonProps & {
|
||||
@@ -107,43 +106,43 @@ type PageButtonProps = NavigationButtonProps & {
|
||||
const Dots: FC = () => {
|
||||
return <span className={classNames.dots}>…</span>
|
||||
}
|
||||
const PageButton: FC<PageButtonProps> = ({ onClick, disabled, selected, page }) => {
|
||||
const PageButton: FC<PageButtonProps> = ({ disabled, onClick, page, selected }) => {
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
disabled={selected || disabled}
|
||||
className={classNames.pageButton(selected)}
|
||||
disabled={selected || disabled}
|
||||
onClick={onClick}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
const PrevButton: FC<NavigationButtonProps> = ({ onClick, disabled }) => {
|
||||
const PrevButton: FC<NavigationButtonProps> = ({ disabled, onClick }) => {
|
||||
return (
|
||||
<button className={classNames.item} onClick={onClick} disabled={disabled}>
|
||||
<button className={classNames.item} disabled={disabled} onClick={onClick}>
|
||||
<KeyboardArrowLeft className={classNames.icon} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
const NextButton: FC<NavigationButtonProps> = ({ onClick, disabled }) => {
|
||||
const NextButton: FC<NavigationButtonProps> = ({ disabled, onClick }) => {
|
||||
return (
|
||||
<button className={classNames.item} onClick={onClick} disabled={disabled}>
|
||||
<button className={classNames.item} disabled={disabled} onClick={onClick}>
|
||||
<KeyboardArrowRight className={classNames.icon} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
type MainPaginationButtonsProps = {
|
||||
paginationRange: (number | string)[]
|
||||
currentPage: number
|
||||
onClick: (pageNumber: number) => () => void
|
||||
paginationRange: (number | string)[]
|
||||
}
|
||||
|
||||
const MainPaginationButtons: FC<MainPaginationButtonsProps> = ({
|
||||
paginationRange,
|
||||
currentPage,
|
||||
onClick,
|
||||
paginationRange,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
@@ -154,16 +153,16 @@ const MainPaginationButtons: FC<MainPaginationButtonsProps> = ({
|
||||
return <Dots key={index} />
|
||||
}
|
||||
|
||||
return <PageButton key={index} page={page} selected={isSelected} onClick={onClick(page)} />
|
||||
return <PageButton key={index} onClick={onClick(page)} page={page} selected={isSelected} />
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export type PerPageSelectProps = {
|
||||
onPerPageChange: (itemPerPage: number) => void
|
||||
perPage: number
|
||||
perPageOptions: number[]
|
||||
onPerPageChange: (itemPerPage: number) => void
|
||||
}
|
||||
|
||||
export const PerPageSelect: FC<PerPageSelectProps> = (
|
||||
|
||||
@@ -3,9 +3,9 @@ import type { Meta, StoryObj } from '@storybook/react'
|
||||
import { RadioGroup } from './'
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Radio Group',
|
||||
component: RadioGroup,
|
||||
tags: ['autodocs'],
|
||||
title: 'Components/Radio Group',
|
||||
} satisfies Meta<typeof RadioGroup>
|
||||
|
||||
export default meta
|
||||
@@ -22,10 +22,10 @@ export const Default: Story = {
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
options: [
|
||||
{ label: 'Option One', value: 'option-one' },
|
||||
{ label: 'Option Two', value: 'option-two' },
|
||||
],
|
||||
disabled: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import { Typography } from '@/components'
|
||||
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group'
|
||||
import { clsx } from 'clsx'
|
||||
|
||||
import s from './radio-group.module.scss'
|
||||
|
||||
import { Typography } from '@/components'
|
||||
|
||||
const RadioGroupRoot = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
|
||||
@@ -19,9 +18,9 @@ RadioGroupRoot.displayName = RadioGroupPrimitive.Root.displayName
|
||||
const RadioGroupItem = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
||||
>(({ className, children, ...props }, ref) => {
|
||||
>(({ children, className, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroupPrimitive.Item ref={ref} className={clsx(s.option, className)} {...props}>
|
||||
<RadioGroupPrimitive.Item className={clsx(s.option, className)} ref={ref} {...props}>
|
||||
<div className={s.icon}></div>
|
||||
</RadioGroupPrimitive.Item>
|
||||
)
|
||||
@@ -37,21 +36,21 @@ export type RadioGroupProps = Omit<
|
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>,
|
||||
'children'
|
||||
> & {
|
||||
options: Option[]
|
||||
errorMessage?: string
|
||||
options: Option[]
|
||||
}
|
||||
const RadioGroup = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
||||
RadioGroupProps
|
||||
>((props, ref) => {
|
||||
const { options, errorMessage, ...restProps } = props
|
||||
const { errorMessage, options, ...restProps } = props
|
||||
|
||||
return (
|
||||
<RadioGroupRoot {...restProps} ref={ref}>
|
||||
{options.map(option => (
|
||||
<div className={s.label} key={option.value}>
|
||||
<RadioGroupItem value={option.value} id={option.value} />
|
||||
<Typography variant={'body2'} as={'label'} htmlFor={option.value}>
|
||||
<RadioGroupItem id={option.value} value={option.value} />
|
||||
<Typography as={'label'} htmlFor={option.value} variant={'body2'}>
|
||||
{option.label}
|
||||
</Typography>
|
||||
</div>
|
||||
@@ -60,4 +59,4 @@ const RadioGroup = React.forwardRef<
|
||||
)
|
||||
})
|
||||
|
||||
export { RadioGroupRoot, RadioGroupItem, RadioGroup }
|
||||
export { RadioGroup, RadioGroupItem, RadioGroupRoot }
|
||||
|
||||
@@ -9,24 +9,26 @@ const Slider = forwardRef<
|
||||
Omit<ComponentPropsWithoutRef<typeof SliderPrimitive.Root>, 'value'> & {
|
||||
value?: (number | undefined)[]
|
||||
}
|
||||
>(({ className, value, ...props }, ref) => {
|
||||
>(({ className, max, onValueChange, value, ...props }, ref) => {
|
||||
useEffect(() => {
|
||||
if (value?.[1] === undefined || value?.[1] === null) {
|
||||
props.onValueChange?.([value?.[0] ?? 0, props.max ?? 0])
|
||||
onValueChange?.([value?.[0] ?? 0, max ?? 0])
|
||||
}
|
||||
}, [props.max, value])
|
||||
}, [max, value, onValueChange])
|
||||
|
||||
return (
|
||||
<div className={s.container}>
|
||||
<span>{value?.[0]}</span>
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={clsx(s.root, className)}
|
||||
max={max}
|
||||
onValueChange={onValueChange}
|
||||
ref={ref}
|
||||
{...props}
|
||||
value={[value?.[0] ?? 0, value?.[1] ?? props.max ?? 0]}
|
||||
value={[value?.[0] ?? 0, value?.[1] ?? max ?? 0]}
|
||||
>
|
||||
<SliderPrimitive.Track className={s.track}>
|
||||
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||
<SliderPrimitive.Range className={'absolute h-full bg-primary'} />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className={s.thumb} />
|
||||
<SliderPrimitive.Thumb className={s.thumb} />
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { Table, TableBody, TableCell, TableEmpty, TableHead, TableHeadCell, TableRow } from './'
|
||||
import { Typography } from '@/components'
|
||||
import { Meta } from '@storybook/react'
|
||||
|
||||
import { Table, TableBody, TableCell, TableEmpty, TableHead, TableHeadCell, TableRow } from './'
|
||||
|
||||
import { Typography } from '@/components'
|
||||
|
||||
export default {
|
||||
title: 'Components/Table',
|
||||
component: Table,
|
||||
title: 'Components/Table',
|
||||
} as Meta<typeof Table>
|
||||
|
||||
export const Default = {
|
||||
@@ -16,7 +14,7 @@ export const Default = {
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeadCell>Название</TableHeadCell>
|
||||
<TableHeadCell align="center">Описание</TableHeadCell>
|
||||
<TableHeadCell align={'center'}>Описание</TableHeadCell>
|
||||
<TableHeadCell>Ссылка</TableHeadCell>
|
||||
<TableHeadCell>Тип</TableHeadCell>
|
||||
<TableHeadCell>Вид</TableHeadCell>
|
||||
@@ -34,9 +32,9 @@ export const Default = {
|
||||
<TableCell>
|
||||
<Typography
|
||||
as={'a'}
|
||||
href={'https://it-incubator.io/'}
|
||||
target={'_blank'}
|
||||
variant={'link1'}
|
||||
href="https://it-incubator.io/"
|
||||
target="_blank"
|
||||
>
|
||||
Какая-то ссылка кудато на какой-то источник с информациейо ссылка кудато на какой-то
|
||||
источник
|
||||
@@ -69,27 +67,27 @@ export const Default = {
|
||||
|
||||
const data = [
|
||||
{
|
||||
category: 'Основной',
|
||||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor',
|
||||
id: '01',
|
||||
title: 'Web Basic',
|
||||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor',
|
||||
link: 'Какая-то ссылка кудато на какой-то источник с информациейо ссылка кудато на какой-то',
|
||||
category: 'Основной',
|
||||
title: 'Web Basic',
|
||||
type: 'Читать',
|
||||
},
|
||||
{
|
||||
category: 'Основной',
|
||||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor',
|
||||
id: '02',
|
||||
title: 'Web Basic',
|
||||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor',
|
||||
link: 'Какая-то ссылка куда-то',
|
||||
category: 'Основной',
|
||||
title: 'Web Basic',
|
||||
type: 'Читать',
|
||||
},
|
||||
{
|
||||
id: '03',
|
||||
title: 'Web Basic',
|
||||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor',
|
||||
link: 'Какая-то ссылка кудато на какой-то источник с информациейо ссылка кудато на какой-то. Какая-то ссылка кудато на какой-то источник с информациейо ссылка куда-то на какой-то',
|
||||
category: 'Основной',
|
||||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor',
|
||||
id: '03',
|
||||
link: 'Какая-то ссылка кудато на какой-то источник с информациейо ссылка кудато на какой-то. Какая-то ссылка кудато на какой-то источник с информациейо ссылка куда-то на какой-то',
|
||||
title: 'Web Basic',
|
||||
type: 'Читать',
|
||||
},
|
||||
]
|
||||
@@ -101,7 +99,7 @@ export const WithMapMethod = {
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeadCell>Название</TableHeadCell>
|
||||
<TableHeadCell align="center">Описание</TableHeadCell>
|
||||
<TableHeadCell align={'center'}>Описание</TableHeadCell>
|
||||
<TableHeadCell>Ссылка</TableHeadCell>
|
||||
<TableHeadCell>Тип</TableHeadCell>
|
||||
<TableHeadCell>Вид</TableHeadCell>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { ComponentProps, ComponentPropsWithoutRef, ElementRef, FC, forwardRef } from 'react'
|
||||
|
||||
import { Typography } from '@/components'
|
||||
import { clsx } from 'clsx'
|
||||
|
||||
import s from './table.module.scss'
|
||||
|
||||
import { Typography } from '@/components'
|
||||
|
||||
export const Table = forwardRef<HTMLTableElement, ComponentPropsWithoutRef<'table'>>(
|
||||
({ className, ...rest }, ref) => {
|
||||
const classNames = {
|
||||
@@ -34,7 +33,7 @@ export const TableRow = forwardRef<ElementRef<'tr'>, ComponentPropsWithoutRef<'t
|
||||
)
|
||||
|
||||
export const TableHeadCell = forwardRef<ElementRef<'th'>, ComponentPropsWithoutRef<'th'>>(
|
||||
({ className, children, ...rest }, ref) => {
|
||||
({ children, className, ...rest }, ref) => {
|
||||
const classNames = {
|
||||
headCell: clsx(className, s.headCell),
|
||||
}
|
||||
@@ -56,10 +55,10 @@ export const TableCell = forwardRef<ElementRef<'td'>, ComponentPropsWithoutRef<'
|
||||
}
|
||||
)
|
||||
|
||||
export const TableEmpty: FC<ComponentProps<'div'> & { mt?: string; mb?: string }> = ({
|
||||
export const TableEmpty: FC<ComponentProps<'div'> & { mb?: string; mt?: string }> = ({
|
||||
className,
|
||||
mt = '89px',
|
||||
mb,
|
||||
mt = '89px',
|
||||
}) => {
|
||||
const classNames = {
|
||||
empty: clsx(className, s.empty),
|
||||
@@ -67,9 +66,9 @@ export const TableEmpty: FC<ComponentProps<'div'> & { mt?: string; mb?: string }
|
||||
|
||||
return (
|
||||
<Typography
|
||||
variant={'h2'}
|
||||
className={classNames.empty}
|
||||
style={{ marginTop: mt, marginBottom: mb }}
|
||||
style={{ marginBottom: mb, marginTop: mt }}
|
||||
variant={'h2'}
|
||||
>
|
||||
Пока тут еще нет данных! :(
|
||||
</Typography>
|
||||
@@ -77,41 +76,47 @@ export const TableEmpty: FC<ComponentProps<'div'> & { mt?: string; mb?: string }
|
||||
}
|
||||
export type Column = {
|
||||
key: string
|
||||
title: string
|
||||
sortable?: boolean
|
||||
title: string
|
||||
}
|
||||
export type Sort = {
|
||||
key: string
|
||||
direction: 'asc' | 'desc'
|
||||
key: string
|
||||
} | null
|
||||
|
||||
export const TableHeader: FC<
|
||||
Omit<
|
||||
ComponentPropsWithoutRef<'thead'> & {
|
||||
columns: Column[]
|
||||
sort?: Sort
|
||||
onSort?: (sort: Sort) => void
|
||||
sort?: Sort
|
||||
},
|
||||
'children'
|
||||
>
|
||||
> = ({ columns, sort, onSort, ...restProps }) => {
|
||||
> = ({ columns, onSort, sort, ...restProps }) => {
|
||||
const handleSort = (key: string, sortable?: boolean) => () => {
|
||||
if (!onSort || !sortable) return
|
||||
if (!onSort || !sortable) {
|
||||
return
|
||||
}
|
||||
|
||||
if (sort?.key !== key) return onSort({ key, direction: 'asc' })
|
||||
if (sort?.key !== key) {
|
||||
return onSort({ direction: 'asc', key })
|
||||
}
|
||||
|
||||
if (sort.direction === 'desc') return onSort(null)
|
||||
if (sort.direction === 'desc') {
|
||||
return onSort(null)
|
||||
}
|
||||
|
||||
return onSort({
|
||||
key,
|
||||
direction: sort?.direction === 'asc' ? 'desc' : 'asc',
|
||||
key,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<TableHead {...restProps}>
|
||||
<TableRow>
|
||||
{columns.map(({ title, key, sortable = true }) => (
|
||||
{columns.map(({ key, sortable = true, title }) => (
|
||||
<TableHeadCell key={key} onClick={handleSort(key, sortable)}>
|
||||
{title}
|
||||
{sort && sort.key === key && <span>{sort.direction === 'asc' ? '▲' : '▼'}</span>}
|
||||
|
||||
@@ -11,7 +11,7 @@ const TabsList = forwardRef<
|
||||
ElementRef<typeof TabsPrimitive.List>,
|
||||
ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.List ref={ref} className={clsx(s.list, className)} {...props} />
|
||||
<TabsPrimitive.List className={clsx(s.list, className)} ref={ref} {...props} />
|
||||
))
|
||||
|
||||
TabsList.displayName = TabsPrimitive.List.displayName
|
||||
@@ -20,7 +20,7 @@ const TabsTrigger = forwardRef<
|
||||
ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger ref={ref} className={clsx(s.trigger, className)} {...props} />
|
||||
<TabsPrimitive.Trigger className={clsx(s.trigger, className)} ref={ref} {...props} />
|
||||
))
|
||||
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
||||
@@ -29,9 +29,9 @@ const TabsContent = forwardRef<
|
||||
ElementRef<typeof TabsPrimitive.Content>,
|
||||
ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Content ref={ref} className={clsx(s.content, className)} {...props} />
|
||||
<TabsPrimitive.Content className={clsx(s.content, className)} ref={ref} {...props} />
|
||||
))
|
||||
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
||||
export { Tabs, TabsContent, TabsList, TabsTrigger }
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { ChangeEvent, ComponentProps, ComponentPropsWithoutRef, forwardRef, useState } from 'react'
|
||||
|
||||
import { Eye, Search, VisibilityOff } from '@/assets'
|
||||
import { Typography } from '@/components'
|
||||
import { clsx } from 'clsx'
|
||||
|
||||
import s from './text-field.module.scss'
|
||||
|
||||
import { VisibilityOff, Eye, Search } from '@/assets'
|
||||
import { Typography } from '@/components'
|
||||
|
||||
export type TextFieldProps = {
|
||||
onValueChange?: (value: string) => void
|
||||
containerProps?: ComponentProps<'div'>
|
||||
labelProps?: ComponentProps<'label'>
|
||||
errorMessage?: string
|
||||
label?: string
|
||||
labelProps?: ComponentProps<'label'>
|
||||
onValueChange?: (value: string) => void
|
||||
search?: boolean
|
||||
} & ComponentPropsWithoutRef<'input'>
|
||||
|
||||
@@ -20,15 +19,15 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
errorMessage,
|
||||
placeholder,
|
||||
type,
|
||||
containerProps,
|
||||
labelProps,
|
||||
errorMessage,
|
||||
label,
|
||||
labelProps,
|
||||
onChange,
|
||||
onValueChange,
|
||||
placeholder,
|
||||
search,
|
||||
type,
|
||||
...restProps
|
||||
},
|
||||
ref
|
||||
@@ -45,18 +44,18 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
||||
}
|
||||
|
||||
const classNames = {
|
||||
root: clsx(s.root, containerProps?.className),
|
||||
fieldContainer: clsx(s.fieldContainer),
|
||||
field: clsx(s.field, !!errorMessage && s.error, search && s.hasLeadingIcon, className),
|
||||
label: clsx(s.label, labelProps?.className),
|
||||
error: clsx(s.error),
|
||||
field: clsx(s.field, !!errorMessage && s.error, search && s.hasLeadingIcon, className),
|
||||
fieldContainer: clsx(s.fieldContainer),
|
||||
label: clsx(s.label, labelProps?.className),
|
||||
leadingIcon: s.leadingIcon,
|
||||
root: clsx(s.root, containerProps?.className),
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames.root}>
|
||||
{label && (
|
||||
<Typography variant="body2" as="label" className={classNames.label}>
|
||||
<Typography as={'label'} className={classNames.label} variant={'body2'}>
|
||||
{label}
|
||||
</Typography>
|
||||
)}
|
||||
@@ -64,24 +63,24 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
||||
{search && <Search className={classNames.leadingIcon} />}
|
||||
<input
|
||||
className={classNames.field}
|
||||
onChange={handleChange}
|
||||
placeholder={placeholder}
|
||||
ref={ref}
|
||||
type={finalType}
|
||||
onChange={handleChange}
|
||||
{...restProps}
|
||||
/>
|
||||
{isShowPasswordButtonShown && (
|
||||
<button
|
||||
className={s.showPassword}
|
||||
type={'button'}
|
||||
onClick={() => setShowPassword(prev => !prev)}
|
||||
type={'button'}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Eye />}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Typography variant="error" className={classNames.error}>
|
||||
<Typography className={classNames.error} variant={'error'}>
|
||||
{errorMessage}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
@@ -3,11 +3,9 @@ import type { Meta, StoryObj } from '@storybook/react'
|
||||
import { Typography } from './'
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Typography',
|
||||
component: Typography,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
variant: {
|
||||
control: { type: 'radio' },
|
||||
options: [
|
||||
'large',
|
||||
'h1',
|
||||
@@ -23,9 +21,11 @@ const meta = {
|
||||
'link2',
|
||||
'error',
|
||||
],
|
||||
control: { type: 'radio' },
|
||||
},
|
||||
},
|
||||
component: Typography,
|
||||
tags: ['autodocs'],
|
||||
title: 'Components/Typography',
|
||||
} satisfies Meta<typeof Typography>
|
||||
|
||||
export default meta
|
||||
|
||||
@@ -6,22 +6,22 @@ import s from './typography.module.scss'
|
||||
|
||||
export interface TextProps<T extends ElementType> {
|
||||
as?: T
|
||||
children?: ReactNode
|
||||
className?: string
|
||||
variant?:
|
||||
| 'large'
|
||||
| 'body1'
|
||||
| 'body2'
|
||||
| 'caption'
|
||||
| 'error'
|
||||
| 'h1'
|
||||
| 'h2'
|
||||
| 'h3'
|
||||
| 'body1'
|
||||
| 'body2'
|
||||
| 'subtitle1'
|
||||
| 'subtitle2'
|
||||
| 'caption'
|
||||
| 'overline'
|
||||
| 'large'
|
||||
| 'link1'
|
||||
| 'link2'
|
||||
| 'error'
|
||||
children?: ReactNode
|
||||
className?: string
|
||||
| 'overline'
|
||||
| 'subtitle1'
|
||||
| 'subtitle2'
|
||||
}
|
||||
|
||||
export function Typography<T extends ElementType = 'p'>({
|
||||
|
||||
Reference in New Issue
Block a user