feat: Добавлен переиспользуемый компонент кнопки с задаваемыми размерами, вариантами и цветовыми схемами. Добавлен декоратор стилей для сторибука, установлена библиотека для работы с классами

This commit is contained in:
Ilia Mashkov
2025-11-20 09:26:01 +03:00
parent e440005e60
commit 5c869eb215
12 changed files with 281 additions and 2 deletions

View File

@@ -0,0 +1,8 @@
import type { Decorator } from '@storybook/react'
import '@/app/styles/index.scss'
export const StyleDecorator: Decorator = (Story) => (
<>
<Story />
</>
)

View File

@@ -49,7 +49,13 @@ const config: StorybookConfig = {
if (typeof rule === 'object' && rule !== null && 'test' in rule) { if (typeof rule === 'object' && rule !== null && 'test' in rule) {
const test = rule.test const test = rule.test
if (test instanceof RegExp) { if (test instanceof RegExp) {
return !(test.test('.css') || test.test('.scss') || test.test('.sass')) // Удаляем правила для CSS/SCSS и SVG
return !(
test.test('.css') ||
test.test('.scss') ||
test.test('.sass') ||
test.test('.svg')
)
} }
} }
return true return true
@@ -58,6 +64,12 @@ const config: StorybookConfig = {
// Использование конфигурации CSS loader из проекта // Использование конфигурации CSS loader из проекта
config.module.rules.push(buildCssLoader(true)) config.module.rules.push(buildCssLoader(true))
// Добавление поддержки SVGR для SVG иконок
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
})
return config return config
}, },

View File

@@ -1,6 +1,8 @@
import type { Preview } from '@storybook/react' import type { Preview } from '@storybook/react'
import { StyleDecorator } from './StyleDecorator.tsx'
const preview: Preview = { const preview: Preview = {
decorators: [StyleDecorator],
parameters: { parameters: {
controls: { controls: {
matchers: { matchers: {
@@ -13,7 +15,7 @@ const preview: Preview = {
values: [ values: [
{ {
name: 'light', name: 'light',
value: '#ffffff', value: '#F4F5F9',
}, },
{ {
name: 'dark', name: 'dark',

View File

@@ -19,6 +19,7 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"classnames": "^2.5.1",
"gsap": "^3.13.0", "gsap": "^3.13.0",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",

8
pnpm-lock.yaml generated
View File

@@ -8,6 +8,9 @@ importers:
.: .:
dependencies: dependencies:
classnames:
specifier: ^2.5.1
version: 2.5.1
gsap: gsap:
specifier: ^3.13.0 specifier: ^3.13.0
version: 3.13.0 version: 3.13.0
@@ -2211,6 +2214,9 @@ packages:
cjs-module-lexer@1.4.3: cjs-module-lexer@1.4.3:
resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==}
classnames@2.5.1:
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
clean-css@5.3.3: clean-css@5.3.3:
resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==}
engines: {node: '>= 10.0'} engines: {node: '>= 10.0'}
@@ -7278,6 +7284,8 @@ snapshots:
cjs-module-lexer@1.4.3: {} cjs-module-lexer@1.4.3: {}
classnames@2.5.1: {}
clean-css@5.3.3: clean-css@5.3.3:
dependencies: dependencies:
source-map: 0.6.1 source-map: 0.6.1

View File

@@ -6,6 +6,7 @@
--color-bg: #F4F5F9; --color-bg: #F4F5F9;
--color-border: rgb(66 86 122 / 10%); --color-border: rgb(66 86 122 / 10%);
--color-blue: #3877EE; --color-blue: #3877EE;
--color-white: #FFFFFF;
// Градиенты // Градиенты
--gradient-primary: linear-gradient(to right, #3877EE, #EF5DA8); --gradient-primary: linear-gradient(to right, #3877EE, #EF5DA8);

View File

@@ -0,0 +1,3 @@
<svg width="9" height="14" viewBox="0 0 9 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.66418 0.707108L1.41419 6.95711L7.66418 13.2071" stroke="#42567A" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 197 B

View File

@@ -0,0 +1,78 @@
.button {
display: inline-flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
cursor: pointer;
transition: all 0.3s ease;
padding: 0;
outline: none;
font-family: var(--font-family-main);
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
// Variants
&.round {
border-radius: 50%;
aspect-ratio: 1;
}
&.regular {
border-radius: 1em;
padding: 0.5em 1em;
}
// Sizes
&.small {
height: 40px;
font-size: 14px;
}
&.medium {
height: 50px;
font-size: 18px;
}
&.large {
height: 60px;
font-size: 24px;
}
// Color Schemes
&.primary {
$color-primary: var(--color-primary);
background-color: transparent;
color: $color-primary;
border: 1px solid $color-primary;
&:hover:not(:disabled) {
background-color: var(--color-white);
}
}
&.secondary {
$color-blue: var(--color-blue);
background-color: var(--color-white);
color: $color-blue;
box-shadow: 0px 0px 15px rgb($color-blue / 10%);
}
// Icon handling
.icon {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
svg {
width: 40%;
height: 40%;
object-fit: contain;
}
}
}

View File

@@ -0,0 +1,102 @@
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './Button'
import ChevronLeftIcon from '@/shared/assets/chevron--left.svg'
const meta = {
title: 'Shared/Button',
component: Button,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['round', 'regular'],
description: 'Вариант внешнего вида',
},
size: {
control: 'select',
options: ['small', 'medium', 'large'],
description: 'Размер кнопки',
},
colorScheme: {
control: 'select',
options: ['primary', 'secondary'],
description: 'Цветовая схема',
},
disabled: {
control: 'boolean',
description: 'Активность кнопки',
},
onClick: { action: 'clicked' },
},
} satisfies Meta<typeof Button>
export default meta
type Story = StoryObj<typeof meta>
/**
* Базовая кнопка
*/
export const Default: Story = {
args: {
children: 'Submit',
variant: 'regular',
size: 'medium',
colorScheme: 'primary',
},
}
/**
* Альтернативная цветовая схема
*/
export const SecondaryColorScheme: Story = {
args: {
children: 'Submit',
variant: 'regular',
size: 'medium',
colorScheme: 'secondary',
},
}
/**
* Маленькая кнопка
*/
export const Small: Story = {
args: {
children: 'Submit',
size: 'small',
},
}
/**
* Большая кнопка
*/
export const Large: Story = {
args: {
children: 'Submit',
size: 'large',
},
}
/**
* Кнопка с SVG иконкой (шеврон)
*/
export const WithIcon: Story = {
args: {
children: <ChevronLeftIcon />,
variant: 'round',
size: 'medium',
},
}
/**
* Отключенная кнопка
*/
export const Disabled: Story = {
args: {
children: 'Submit',
disabled: true,
},
}

View File

@@ -0,0 +1,60 @@
import { ButtonHTMLAttributes, memo, PropsWithChildren } from 'react'
import classNames from 'classnames'
import styles from './Button.module.scss'
export type ButtonVariant = 'round' | 'regular'
export type ButtonSize = 'small' | 'medium' | 'large'
export type ButtonColorScheme = 'primary' | 'secondary'
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>, PropsWithChildren {
/**
* Вариант внешнего вида кнопки
* @default 'round'
*/
variant?: ButtonVariant
/**
* Размер кнопки
* @default 'medium'
*/
size?: ButtonSize
/**
* Цветовая схема
* @default 'timeframe'
*/
colorScheme?: ButtonColorScheme
}
/**
* Универсальный компонент кнопки для использования в слайдерах и других элементах интерфейса.
* Поддерживает различные варианты отображения, размеры и цветовые схемы.
*/
export const Button = memo((props: ButtonProps) => {
const {
className,
children,
variant = 'round',
size = 'medium',
colorScheme = 'primary',
disabled,
...otherProps
} = props
const mods: Record<string, boolean | undefined> = {
[styles[variant]]: true,
[styles[size]]: true,
[styles[colorScheme]]: true,
}
return (
<button
type="button"
className={classNames(styles.button, mods, className)}
disabled={disabled}
{...otherProps}
>
{children}
</button>
)
})
Button.displayName = 'Button'

View File

@@ -0,0 +1,2 @@
export { Button } from './Button'
export type { ButtonProps, ButtonVariant, ButtonSize, ButtonColorScheme } from './Button'

2
src/shared/ui/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export { Button } from './Button'
export type { ButtonProps } from './Button'