sync
This commit is contained in:
@@ -22,8 +22,8 @@ export default defineConfig({
|
|||||||
text: 'Прикладные разделы',
|
text: 'Прикладные разделы',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Структура проекта', link: '/parts/7-project-structure' },
|
{ text: 'Структура проекта', link: '/parts/7-project-structure' },
|
||||||
{ text: 'Шаблоны генерации кода', link: '/parts/17-templates-generation' },
|
{ text: 'Компоненты', link: '/parts/8-0-components' },
|
||||||
{ text: 'Компоненты', link: '/parts/8-components' },
|
{ text: 'Шаблоны и генерация кода', link: '/parts/8-1-templates-generation' },
|
||||||
{ text: 'Стили', link: '/parts/9-styles' },
|
{ text: 'Стили', link: '/parts/9-styles' },
|
||||||
{ text: 'Изображения/спрайты', link: '/parts/10-images-sprites' },
|
{ text: 'Изображения/спрайты', link: '/parts/10-images-sprites' },
|
||||||
{ text: 'Видео', link: '/parts/11-video' },
|
{ text: 'Видео', link: '/parts/11-video' },
|
||||||
|
|||||||
154
RULES.md
154
RULES.md
@@ -5,19 +5,39 @@
|
|||||||
|
|
||||||
Базовый стек технологий и библиотек, на который опираются проекты и примеры в документации.
|
Базовый стек технологий и библиотек, на который опираются проекты и примеры в документации.
|
||||||
|
|
||||||
## Что используем обычно
|
## Что используем
|
||||||
|
|
||||||
- **TypeScript** — типизация всей логики и компонентов.
|
### Стек
|
||||||
|
- **React/TypeScript** — основной стек для UI и приложения.
|
||||||
|
- **Next.js** — для продуктовых сайтов.
|
||||||
|
|
||||||
|
### Архитектура
|
||||||
- **FSD (Feature-Sliced Design)** — структура проекта и границы модулей.
|
- **FSD (Feature-Sliced Design)** — структура проекта и границы модулей.
|
||||||
- **React + Next.js** — основной стек для UI и приложения.
|
|
||||||
|
### UI компоненты
|
||||||
- **Mantine UI** — базовые UI-компоненты.
|
- **Mantine UI** — базовые UI-компоненты.
|
||||||
- **SWR** — получение данных по GET (кеширование и revalidate).
|
|
||||||
|
### Fetch (API)
|
||||||
|
- **@gromlab/api-codegen** — генерация API‑клиентов и типов.
|
||||||
|
- **SWR** — получение, кеширование, ревалидация, дедубликация.
|
||||||
|
- **SWR (useSWRSubscription)** - сокеты, реалтайм подписки.
|
||||||
|
|
||||||
|
### Store
|
||||||
- **Zustand** — глобальное состояние.
|
- **Zustand** — глобальное состояние.
|
||||||
|
|
||||||
|
### Локализация
|
||||||
- **i18next (i18n)** — локализация всех пользовательских текстов.
|
- **i18next (i18n)** — локализация всех пользовательских текстов.
|
||||||
|
|
||||||
|
### Тестирование
|
||||||
- **Vitest** — тестирование.
|
- **Vitest** — тестирование.
|
||||||
- **clsx** — конкатенация CSS‑классов.
|
|
||||||
|
### Стили
|
||||||
- **PostCSS Modules** — изоляция стилей.
|
- **PostCSS Modules** — изоляция стилей.
|
||||||
- **Mobile First** — подход к адаптивной верстке.
|
- **Mobile First** — подход к адаптивной верстке.
|
||||||
|
- **clsx** — конкатенация CSS‑классов.
|
||||||
|
|
||||||
|
### Генерация
|
||||||
|
- **@gromlab/create** — шаблонизатор для создания слоёв и других файлов из шаблонов.
|
||||||
|
|
||||||
|
|
||||||
<a name="2-architecturemd"></a>
|
<a name="2-architecturemd"></a>
|
||||||
@@ -595,7 +615,7 @@ src/
|
|||||||
- Не смешивать ответственность разных слоёв в одном модуле.
|
- Не смешивать ответственность разных слоёв в одном модуле.
|
||||||
|
|
||||||
|
|
||||||
<a name="8-componentsmd"></a>
|
<a name="8-0-componentsmd"></a>
|
||||||
|
|
||||||
# Компоненты
|
# Компоненты
|
||||||
|
|
||||||
@@ -671,6 +691,11 @@ export const Container: FC<ContainerProps> = ({ className, ...htmlAttr }) => {
|
|||||||
export { Container } from './container.ui'
|
export { Container } from './container.ui'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Генерация
|
||||||
|
|
||||||
|
Генерация нужна, чтобы быстро создавать компоненты с единым каркасом и не допускать расхождений в структуре. Это даёт одинаковые папки и имена файлов, уменьшает ручные ошибки и ускоряет старт работы.
|
||||||
|
|
||||||
|
Используйте **@gromlab/create** для создания базового шаблона компонента. После генерации проверьте название компонента/файлов и заполните описание назначения.
|
||||||
|
|
||||||
## Вложенные (дочерние) компоненты
|
## Вложенные (дочерние) компоненты
|
||||||
|
|
||||||
@@ -679,6 +704,123 @@ export { Container } from './container.ui'
|
|||||||
Вложенные компоненты подчиняются тем же правилам по структуре, именованию и стилю (включая папку `styles/` для их стилей).
|
Вложенные компоненты подчиняются тем же правилам по структуре, именованию и стилю (включая папку `styles/` для их стилей).
|
||||||
|
|
||||||
|
|
||||||
|
<a name="8-1-templates-generationmd"></a>
|
||||||
|
|
||||||
|
# Шаблоны генерации кода
|
||||||
|
|
||||||
|
Подход к использованию шаблонов и генерации кода для стандартизации структуры и ускорения разработки.
|
||||||
|
|
||||||
|
## Что генерируем
|
||||||
|
|
||||||
|
- Компоненты (`screens`, `layouts`, `widgets`, `features`, `entities`).
|
||||||
|
- Страницы (nextjs `app`, `pages`)
|
||||||
|
- Типовые инфраструктурные модули (например, `store`).
|
||||||
|
|
||||||
|
## Чем генерируем
|
||||||
|
|
||||||
|
### VSCode extension
|
||||||
|
|
||||||
|
[расширение VS Code](https://open-vsx.org/extension/MyTemplateGenerator/mytemplategenerator) — создание файлов и папок из шаблонов через UI‑интерфейс внутри редактора.
|
||||||
|
|
||||||
|
### CLI (для агентов)
|
||||||
|
|
||||||
|
[@gromlab/create](https://gromlab.ru/gromov/create) — CLI для генерации файлов и папок по шаблонам.
|
||||||
|
|
||||||
|
Примеры:
|
||||||
|
```bash
|
||||||
|
# Создать компонент
|
||||||
|
create component button
|
||||||
|
|
||||||
|
# Создать компонент используя NPX
|
||||||
|
npx @gromlab/create component button
|
||||||
|
```
|
||||||
|
|
||||||
|
## Структура папок
|
||||||
|
Все шаблоны лежат в `.templates/` в корне проекта.
|
||||||
|
Каждая папка в `.templates/` — это уникальный шаблон.
|
||||||
|
|
||||||
|
```text
|
||||||
|
.templates/ # корневая папка всех шаблонов
|
||||||
|
├── component/ # шаблон компонента
|
||||||
|
│ └── {{{name.pascalCase}}}/
|
||||||
|
│ ├── index.ts
|
||||||
|
│ ├── {{{name.pascalCase}}}.tsx
|
||||||
|
│ └── {{{name.pascalCase}}}.module.css
|
||||||
|
└── store/ # шаблон Zustand стора
|
||||||
|
└── {{{name.camelCase}}}Store/
|
||||||
|
├── index.ts
|
||||||
|
├── {{{name.camelCase}}}Store.ts
|
||||||
|
└── {{{name.camelCase}}}Store.type.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Синтаксис
|
||||||
|
|
||||||
|
- Переменные в шаблонах работают в именах файлов/папок и внутри файлов.
|
||||||
|
- Базовая переменная — `name`.
|
||||||
|
|
||||||
|
Формат записи переменной:
|
||||||
|
|
||||||
|
```text
|
||||||
|
{{variable}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Модификаторы — это преобразования переменной, которые меняют регистр и формат записи. Они пишутся после имени через точку и применяются в момент генерации.
|
||||||
|
|
||||||
|
```text
|
||||||
|
{{name.pascalCase}} -> MyButton
|
||||||
|
{{name.camelCase}} -> myButton
|
||||||
|
{{name.kebabCase}} -> my-button
|
||||||
|
{{name.snakeCase}} -> my_button
|
||||||
|
{{name.screamingSnakeCase}} -> MY_BUTTON
|
||||||
|
```
|
||||||
|
|
||||||
|
Пример использования в шаблоне:
|
||||||
|
|
||||||
|
```text
|
||||||
|
{{name}}.tsx
|
||||||
|
{{name.pascalCase}}.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
export const {{name.pascalCase}} = () => {
|
||||||
|
return <div>{{name}}</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Шаблон компонента
|
||||||
|
|
||||||
|
Рекомендуемая структура компонента, создаётся генератором по шаблону.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// .templates/component/index.ts
|
||||||
|
export * from './{{name.pascalCase}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// .templates/component/{{name.pascalCase}}.tsx
|
||||||
|
import { FC, HTMLAttributes } from "react";
|
||||||
|
import styles from './{{name.kebabCase}}.module.css'
|
||||||
|
import cl from 'clsx'
|
||||||
|
|
||||||
|
interface IOwnProps extends HTMLAttributes<HTMLDivElement> {}
|
||||||
|
|
||||||
|
export const {{name.pascalCase}}:FC<IOwnProps> = ({className, ...htmlAttr}) => {
|
||||||
|
return (
|
||||||
|
<div {...htmlAttr} className={cl(styles.root, className)}>
|
||||||
|
{{name.kebabCase}}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* .templates/component/{{name.kebabCase}}.module.css */
|
||||||
|
.root {
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
<a name="9-stylesmd"></a>
|
<a name="9-stylesmd"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ title: Архитектура
|
|||||||
|
|
||||||
# Архитектура
|
# Архитектура
|
||||||
|
|
||||||
Архитектура построена на FSD (Feature‑Sliced Design) и строгих границах модулей.
|
Архитектура построена на FSD (`Feature‑Sliced Design`) и строгих границах модулей.
|
||||||
Цель — разделить ответственность, упростить сопровождение и контроль зависимостей.
|
Цель — разделить ответственность, упростить сопровождение и контроль зависимостей.
|
||||||
|
|
||||||
## Принципы
|
## Принципы
|
||||||
@@ -14,7 +14,7 @@ title: Архитектура
|
|||||||
- Открывать наружу только публичный API модулей.
|
- Открывать наружу только публичный API модулей.
|
||||||
- Не допускать циклических зависимостей.
|
- Не допускать циклических зависимостей.
|
||||||
|
|
||||||
## Слои
|
## Слои (FSD)
|
||||||
|
|
||||||
- **app** — инициализация приложения, роутинг, конфигурации, глобальные провайдеры.
|
- **app** — инициализация приложения, роутинг, конфигурации, глобальные провайдеры.
|
||||||
- **screens** — экраны и их композиция.
|
- **screens** — экраны и их композиция.
|
||||||
@@ -24,6 +24,17 @@ title: Архитектура
|
|||||||
- **entities** — бизнес-сущности и их модель.
|
- **entities** — бизнес-сущности и их модель.
|
||||||
- **shared** — переиспользуемая инфраструктура, утилиты и базовые UI‑компоненты.
|
- **shared** — переиспользуемая инфраструктура, утилиты и базовые UI‑компоненты.
|
||||||
|
|
||||||
|
## Модули (FSD)
|
||||||
|
|
||||||
|
- Модуль — это отдельная папка в слоях `screens`, `layouts`, `widgets`, `features`, `entities`, которая реализует один сценарий/блок. В корне модуля лежит главный файл (`*.screen.tsx`, `*.layout.tsx`, `*.widget.tsx`, `*.feature.tsx`, `*.entity.tsx`) и публичный API (`index.ts`).
|
||||||
|
- Внутри модуля используются подпапки (по необходимости):
|
||||||
|
- `ui/` — дочерние UI‑компоненты модуля.
|
||||||
|
- `model/` — состояние и бизнес‑логика модуля.
|
||||||
|
- `styles/` — локальные стили модуля.
|
||||||
|
- `helpers/` — локальные хелперы.
|
||||||
|
- `lib/` — утилиты модуля.
|
||||||
|
- `api/` — API‑вызовы модуля.
|
||||||
|
|
||||||
## Правила зависимостей
|
## Правила зависимостей
|
||||||
|
|
||||||
- Допустимые импорты идут сверху вниз: `app → screens → layouts → widgets → features → entities → shared`.
|
- Допустимые импорты идут сверху вниз: `app → screens → layouts → widgets → features → entities → shared`.
|
||||||
|
|||||||
@@ -78,20 +78,22 @@ const title = 'Привет, ' + name;
|
|||||||
|
|
||||||
- В именованных импортах использовать пробелы внутри фигурных скобок.
|
- В именованных импортах использовать пробелы внутри фигурных скобок.
|
||||||
- Типы импортировать через `import type`.
|
- Типы импортировать через `import type`.
|
||||||
- `default` импорт и экспорт избегать, использовать именованные.
|
- `default` экспорт избегать, использовать именованные. `default` импорт допустим (например, стили CSS Modules, сторонние библиотеки).
|
||||||
- Избегать импорта всего модуля через `*`.
|
- Избегать импорта всего модуля через `*`.
|
||||||
|
|
||||||
**Хорошо**
|
**Хорошо**
|
||||||
```ts
|
```ts
|
||||||
import { MyComponent } from 'MyComponent';
|
import { MyComponent } from 'MyComponent';
|
||||||
import type { User } from '../model/types';
|
import type { User } from '../model/types';
|
||||||
|
import styles from './styles/button.module.css';
|
||||||
```
|
```
|
||||||
|
|
||||||
**Плохо**
|
**Плохо**
|
||||||
```ts
|
```ts
|
||||||
// Плохо: default импорт и отсутствие пробелов в именованном импорте.
|
// Плохо: отсутствие пробелов в именованном импорте.
|
||||||
import MyComponent from 'MyComponent';
|
|
||||||
import type {User} from '../model/types';
|
import type {User} from '../model/types';
|
||||||
|
// Плохо: default экспорт.
|
||||||
|
export default MyComponent;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ранние возвраты (early return)
|
## Ранние возвраты (early return)
|
||||||
|
|||||||
@@ -75,8 +75,12 @@ src/
|
|||||||
└── shared/
|
└── shared/
|
||||||
└── ui/
|
└── ui/
|
||||||
└── icon/
|
└── icon/
|
||||||
|
├── styles/
|
||||||
|
│ └── icon.module.css
|
||||||
|
├── types/
|
||||||
|
│ └── icon.interface.ts
|
||||||
├── icon.ui.tsx
|
├── icon.ui.tsx
|
||||||
└── icon.module.css
|
└── index.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
**Плохо**
|
**Плохо**
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ title: Типизация
|
|||||||
/**
|
/**
|
||||||
* Параметры кнопки.
|
* Параметры кнопки.
|
||||||
*/
|
*/
|
||||||
interface IOwnProps extends HTMLAttributes<HTMLDivElement> {
|
interface ButtonProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
/** Текст кнопки. */
|
/** Текст кнопки. */
|
||||||
label: string;
|
label: string;
|
||||||
/** Обработчик клика по кнопке. */
|
/** Обработчик клика по кнопке. */
|
||||||
@@ -34,7 +34,7 @@ interface IOwnProps extends HTMLAttributes<HTMLDivElement> {
|
|||||||
/**
|
/**
|
||||||
* Кнопка с пользовательскими стилями.
|
* Кнопка с пользовательскими стилями.
|
||||||
*/
|
*/
|
||||||
export const Button:FC<IOwnProps> = ({ className, label, onClick, ...htmlAttr }) => {
|
export const Button: FC<ButtonProps> = ({ className, label, onClick, ...htmlAttr }) => {
|
||||||
return (
|
return (
|
||||||
<div {...htmlAttr} className={cl(styles.root, className)}>
|
<div {...htmlAttr} className={cl(styles.root, className)}>
|
||||||
button
|
button
|
||||||
|
|||||||
@@ -15,28 +15,46 @@ src/
|
|||||||
│ ├── config/ # Конфигурации и константы уровня приложения
|
│ ├── config/ # Конфигурации и константы уровня приложения
|
||||||
│ ├── providers/ # Провайдеры и обёртки приложения
|
│ ├── providers/ # Провайдеры и обёртки приложения
|
||||||
│ ├── routing/ # Конфигурация маршрутов
|
│ ├── routing/ # Конфигурация маршрутов
|
||||||
|
│ ├── styles/ # Глобальные стили, CSS-переменные, custom media
|
||||||
│ └── index.ts # Публичный API слоя
|
│ └── index.ts # Публичный API слоя
|
||||||
├── screens/ # Экраны приложения
|
├── screens/ # Экраны приложения
|
||||||
│ └── Profile/ # Экран профиля
|
│ └── profile/ # Экран профиля
|
||||||
│ └── ... # ui/model/index.ts
|
│ ├── profile.screen.tsx
|
||||||
|
│ └── index.ts
|
||||||
├── layouts/ # Общие шаблоны и каркасы страниц
|
├── layouts/ # Общие шаблоны и каркасы страниц
|
||||||
│ └── MainLayout/ # Основной layout
|
│ └── main-layout/ # Основной layout
|
||||||
│ └── ... # ui/index.ts
|
│ ├── main-layout.layout.tsx
|
||||||
|
│ └── index.ts
|
||||||
├── widgets/ # Крупные блоки интерфейса
|
├── widgets/ # Крупные блоки интерфейса
|
||||||
│ └── Header/ # Виджет шапки
|
│ └── header/ # Виджет шапки
|
||||||
│ └── ... # ui/index.ts
|
│ ├── header.widget.tsx
|
||||||
|
│ └── index.ts
|
||||||
├── features/ # Пользовательские сценарии
|
├── features/ # Пользовательские сценарии
|
||||||
│ └── auth-by-email/ # Авторизация по email
|
│ └── auth-by-email/ # Авторизация по email
|
||||||
│ └── ... # ui/model/api/index.ts
|
│ ├── ui/
|
||||||
|
│ │ └── login-form.ui.tsx
|
||||||
|
│ ├── model/
|
||||||
|
│ │ └── auth-by-email.store.ts
|
||||||
|
│ ├── auth-by-email.feature.tsx
|
||||||
|
│ └── index.ts
|
||||||
├── entities/ # Бизнес-сущности
|
├── entities/ # Бизнес-сущности
|
||||||
│ └── user/ # Сущность пользователя
|
│ └── user/ # Сущность пользователя
|
||||||
│ └── ... # ui/model/api/lib/index.ts
|
│ ├── model/
|
||||||
|
│ │ └── user.store.ts
|
||||||
|
│ ├── user.entity.tsx
|
||||||
|
│ └── index.ts
|
||||||
└── shared/ # Общие ресурсы проекта
|
└── shared/ # Общие ресурсы проекта
|
||||||
├── ui/ # Базовые UI-компоненты
|
├── ui/ # Повторно используемые UI-элементы
|
||||||
|
│ └── icon/
|
||||||
|
│ ├── styles/
|
||||||
|
│ │ └── icon.module.css
|
||||||
|
│ ├── types/
|
||||||
|
│ │ └── icon.interface.ts
|
||||||
|
│ ├── icon.ui.tsx
|
||||||
|
│ └── index.ts
|
||||||
├── lib/ # Утилиты и хелперы
|
├── lib/ # Утилиты и хелперы
|
||||||
├── services/ # Общие сервисы и клиенты
|
├── services/ # Общие сервисы и клиенты
|
||||||
├── config/ # Общие конфигурации
|
├── config/ # Общие конфигурации
|
||||||
├── styles/ # Глобальные стили и токены
|
|
||||||
└── assets/ # Ресурсы
|
└── assets/ # Ресурсы
|
||||||
├── images/ # Изображения
|
├── images/ # Изображения
|
||||||
├── icons/ # Иконки
|
├── icons/ # Иконки
|
||||||
@@ -56,8 +74,12 @@ src/
|
|||||||
|
|
||||||
## Правила организации
|
## Правила организации
|
||||||
|
|
||||||
|
- В слоях FSD (`features`, `entities`, `widgets`, `screens` и т.д.) `ui/` используется только для дочерних элементов, которые относятся к модулю и не экспортируются отдельно. Главные компоненты, которые составляют сам слой, держат собственные `*.feature.tsx`, `*.widget.tsx` и т. п., а `ui/` служит для вспомогательных мелких компонентов.
|
||||||
|
- В `shared/ui/` хранятся базовые UI-элементы/компоненты, которыми пользуются сразу несколько модулей; в этом случае они экспортируются наружу и не считаются «дочерними» для слоя.
|
||||||
|
- Если модуль строится вокруг «главного» компонента (`*.feature.tsx`, `*.screen.tsx`, `*.widget.tsx`), помещайте его в корень модуля и экспортируйте через `index.ts`. Проверяйте, что `ui/` не используется просто как «контейнер» слоя.
|
||||||
|
|
||||||
- Каждый слой и модуль хранится в собственной папке.
|
- Каждый слой и модуль хранится в собственной папке.
|
||||||
- Внутренние реализации разделяются на `ui`, `model`, `lib`, `api`.
|
- Внутренние реализации разделяются на `ui/`, `model/`, `styles/`, `helpers/`, `lib/`, `api/`.
|
||||||
- Публичный API модуля объявляется в `index.ts`.
|
- Публичный API модуля объявляется в `index.ts`.
|
||||||
- Внутренние файлы не импортируются напрямую извне.
|
- Внутренние файлы не импортируются напрямую извне.
|
||||||
- Не смешивать ответственность разных слоёв в одном модуле.
|
- Не смешивать ответственность разных слоёв в одном модуле.
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
---
|
||||||
|
title: Компоненты
|
||||||
|
---
|
||||||
|
|
||||||
# Компоненты
|
# Компоненты
|
||||||
|
|
||||||
Раздел описывает правила создания UI‑компонентов. Эти правила обязательны для всех слоёв FSD: `app`, `screens`, `layouts`, `widgets`, `features`, `entities`, `shared`.
|
Раздел описывает правила создания UI‑компонентов. Эти правила обязательны для всех слоёв FSD: `app`, `screens`, `layouts`, `widgets`, `features`, `entities`, `shared`.
|
||||||
@@ -39,7 +43,7 @@ export interface ContainerProps extends HTMLAttributes<HTMLDivElement> {}
|
|||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { cl } from 'clsx'
|
import cl from 'clsx'
|
||||||
import type { ContainerProps } from './types/container.interface'
|
import type { ContainerProps } from './types/container.interface'
|
||||||
import styles from './styles/container.module.scss'
|
import styles from './styles/container.module.scss'
|
||||||
|
|
||||||
@@ -72,7 +76,7 @@ export const Container: FC<ContainerProps> = ({ className, ...htmlAttr }) => {
|
|||||||
export { Container } from './container.ui'
|
export { Container } from './container.ui'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Генерация
|
## Шаблоны и генерация кода
|
||||||
|
|
||||||
Генерация нужна, чтобы быстро создавать компоненты с единым каркасом и не допускать расхождений в структуре. Это даёт одинаковые папки и имена файлов, уменьшает ручные ошибки и ускоряет старт работы.
|
Генерация нужна, чтобы быстро создавать компоненты с единым каркасом и не допускать расхождений в структуре. Это даёт одинаковые папки и имена файлов, уменьшает ручные ошибки и ускоряет старт работы.
|
||||||
|
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
---
|
||||||
|
title: Шаблоны генерации кода
|
||||||
|
---
|
||||||
|
|
||||||
# Шаблоны генерации кода
|
# Шаблоны генерации кода
|
||||||
|
|
||||||
Подход к использованию шаблонов и генерации кода для стандартизации структуры и ускорения разработки.
|
Подход к использованию шаблонов и генерации кода для стандартизации структуры и ускорения разработки.
|
||||||
@@ -34,15 +38,18 @@ npx @gromlab/create component button
|
|||||||
```text
|
```text
|
||||||
.templates/ # корневая папка всех шаблонов
|
.templates/ # корневая папка всех шаблонов
|
||||||
├── component/ # шаблон компонента
|
├── component/ # шаблон компонента
|
||||||
│ └── {{{name.pascalCase}}}/
|
│ └── {{name.kebabCase}}/
|
||||||
│ ├── index.ts
|
│ ├── styles/
|
||||||
│ ├── {{{name.pascalCase}}}.tsx
|
│ │ └── {{name.kebabCase}}.module.css
|
||||||
│ └── {{{name.pascalCase}}}.module.css
|
│ ├── types/
|
||||||
|
│ │ └── {{name.kebabCase}}.interface.ts
|
||||||
|
│ ├── {{name.kebabCase}}.ui.tsx
|
||||||
|
│ └── index.ts
|
||||||
└── store/ # шаблон Zustand стора
|
└── store/ # шаблон Zustand стора
|
||||||
└── {{{name.camelCase}}}Store/
|
└── {{name.kebabCase}}/
|
||||||
├── index.ts
|
├── {{name.kebabCase}}.store.ts
|
||||||
├── {{{name.camelCase}}}Store.ts
|
├── {{name.kebabCase}}.type.ts
|
||||||
└── {{{name.camelCase}}}Store.type.ts
|
└── index.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
## Синтаксис
|
## Синтаксис
|
||||||
@@ -85,18 +92,30 @@ export const {{name.pascalCase}} = () => {
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
// .templates/component/index.ts
|
// .templates/component/index.ts
|
||||||
export * from './{{name.pascalCase}}'
|
export { {{name.pascalCase}} } from './{{name.kebabCase}}.ui'
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// .templates/component/types/{{name.kebabCase}}.interface.ts
|
||||||
|
import type { HTMLAttributes } from 'react'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Параметры {{name.pascalCase}}.
|
||||||
|
*/
|
||||||
|
export interface {{name.pascalCase}}Props extends HTMLAttributes<HTMLDivElement> {}
|
||||||
```
|
```
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// .templates/component/{{name.pascalCase}}.tsx
|
// .templates/component/{{name.kebabCase}}.ui.tsx
|
||||||
import { FC, HTMLAttributes } from "react";
|
import type { FC } from 'react'
|
||||||
import styles from './{{name.kebabCase}}.module.css'
|
|
||||||
import cl from 'clsx'
|
import cl from 'clsx'
|
||||||
|
import type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}.interface'
|
||||||
|
import styles from './styles/{{name.kebabCase}}.module.css'
|
||||||
|
|
||||||
interface IOwnProps extends HTMLAttributes<HTMLDivElement> {}
|
/**
|
||||||
|
* {{name.pascalCase}}.
|
||||||
export const {{name.pascalCase}}:FC<IOwnProps> = ({className, ...htmlAttr}) => {
|
*/
|
||||||
|
export const {{name.pascalCase}}: FC<{{name.pascalCase}}Props> = ({ className, ...htmlAttr }) => {
|
||||||
return (
|
return (
|
||||||
<div {...htmlAttr} className={cl(styles.root, className)}>
|
<div {...htmlAttr} className={cl(styles.root, className)}>
|
||||||
{{name.kebabCase}}
|
{{name.kebabCase}}
|
||||||
@@ -106,7 +125,7 @@ export const {{name.pascalCase}}:FC<IOwnProps> = ({className, ...htmlAttr}) => {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```css
|
```css
|
||||||
/* .templates/component/{{name.kebabCase}}.module.css */
|
/* .templates/component/styles/{{name.kebabCase}}.module.css */
|
||||||
.root {
|
.root {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
---
|
||||||
|
title: Стили
|
||||||
|
---
|
||||||
|
|
||||||
|
# Стили
|
||||||
|
|
||||||
|
Раздел описывает правила написания CSS: PostCSS Modules, вложенность, медиа-запросы, переменные, форматирование.
|
||||||
|
|
||||||
|
## Общие правила
|
||||||
|
|
||||||
|
- Только **PostCSS** и **CSS Modules** для стилизации.
|
||||||
|
- Подход **Mobile First** — стили пишутся от мобильных к десктопу.
|
||||||
|
- Именование классов — `camelCase` (`.root`, `.buttonNext`, `.itemTitle`).
|
||||||
|
- Модификаторы — отдельный класс с `_`, применяется через `&._modifier`.
|
||||||
|
|
||||||
|
**Хорошо**
|
||||||
|
```css
|
||||||
|
.submitButton {
|
||||||
|
padding: 8px 16px;
|
||||||
|
|
||||||
|
&._disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
```css
|
||||||
|
/* Плохо: kebab-case и вложенный элемент вместо отдельного класса. */
|
||||||
|
.submit-button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Вложенность
|
||||||
|
|
||||||
|
- Вложенность селекторов запрещена.
|
||||||
|
- Исключения:
|
||||||
|
- Псевдоклассы: `&:hover`, `&:active`, `&:focus`, `&:disabled` и т.д.
|
||||||
|
- Псевдоэлементы: `&::before`, `&::after`.
|
||||||
|
- Медиа-запросы: `@media`.
|
||||||
|
- Модификаторы: `&._active`, `&._disabled`.
|
||||||
|
- Каждый вложенный блок отделяется пустой строкой от предыдущих свойств.
|
||||||
|
|
||||||
|
**Хорошо**
|
||||||
|
```css
|
||||||
|
.card {
|
||||||
|
padding: 16px;
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&._highlighted {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (--md) {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardTitle {
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
@media (--md) {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
```css
|
||||||
|
/* Плохо: вложенность селекторов, нет пустых строк между блоками. */
|
||||||
|
.card {
|
||||||
|
padding: 16px;
|
||||||
|
.cardTitle {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-bg-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Медиа-запросы
|
||||||
|
|
||||||
|
- Только **Custom Media Queries**: `@media (--md) {}`.
|
||||||
|
- Запрещены произвольные breakpoints: `@media (min-width: 768px)`.
|
||||||
|
- `@media` пишется только **внутри** селектора.
|
||||||
|
- Запрещено писать `@media` на верхнем уровне с селекторами внутри.
|
||||||
|
|
||||||
|
**Хорошо**
|
||||||
|
```css
|
||||||
|
.sidebar {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media (--md) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarTitle {
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
@media (--md) {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
```css
|
||||||
|
/* Плохо: @media на верхнем уровне с селекторами внутри. */
|
||||||
|
@media (--md) {
|
||||||
|
.sidebar {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarTitle {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Плохо: произвольный breakpoint вместо custom media. */
|
||||||
|
.sidebar {
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## CSS-переменные
|
||||||
|
|
||||||
|
- Цвета (`--color-*`), отступы (`--space-*`), скругления (`--radius-*`) определяются в `app/styles/variables.css` через `:root`.
|
||||||
|
- Файл переменных подключается один раз в корневом layout/entry point — после этого переменные доступны глобально через каскад.
|
||||||
|
- Не дублировать магические значения в компонентах.
|
||||||
|
|
||||||
|
**Хорошо**
|
||||||
|
```css
|
||||||
|
/* app/styles/variables.css */
|
||||||
|
:root {
|
||||||
|
--color-primary: #3b82f6;
|
||||||
|
--color-bg: #ffffff;
|
||||||
|
--color-bg-hover: #f5f5f5;
|
||||||
|
--space-1: 4px;
|
||||||
|
--space-2: 8px;
|
||||||
|
--space-3: 12px;
|
||||||
|
--radius-1: 4px;
|
||||||
|
--radius-2: 8px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* компонент */
|
||||||
|
.card {
|
||||||
|
padding: var(--space-3);
|
||||||
|
border-radius: var(--radius-2);
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
```css
|
||||||
|
/* Плохо: магические значения вместо переменных. */
|
||||||
|
.card {
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom Media
|
||||||
|
|
||||||
|
- Breakpoints определяются через Custom Media Queries в `app/styles/media.css`.
|
||||||
|
- Custom media подключаются глобально через конфиг PostCSS (плагин `postcss-custom-media`) — не импортировать в файлы стилей.
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* app/styles/media.css */
|
||||||
|
@custom-media --sm (min-width: 36em);
|
||||||
|
@custom-media --md (min-width: 62em);
|
||||||
|
@custom-media --lg (min-width: 82em);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Импорт стилей
|
||||||
|
|
||||||
|
- Стили компонента импортируются только внутри своего компонента.
|
||||||
|
- Запрещено импортировать стили одного компонента в другой.
|
||||||
|
- Custom media не импортируются в файлы стилей — они подключаются глобально через конфиг PostCSS.
|
||||||
|
|
||||||
|
## Форматирование
|
||||||
|
|
||||||
|
- Пустая строка между селекторами верхнего уровня.
|
||||||
|
- Пустая строка перед каждым вложенным блоком (медиа, псевдокласс, модификатор).
|
||||||
|
|
||||||
|
**Хорошо**
|
||||||
|
```css
|
||||||
|
.userBar {
|
||||||
|
display: none;
|
||||||
|
color: var(--color-text);
|
||||||
|
|
||||||
|
@media (--md) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.userBarButton {
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&._active {
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Плохо**
|
||||||
|
```css
|
||||||
|
/* Плохо: нет пустых строк между селекторами и вложенными блоками. */
|
||||||
|
.userBar {
|
||||||
|
display: none;
|
||||||
|
color: var(--color-text);
|
||||||
|
@media (--md) {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.userBarButton {
|
||||||
|
background-color: var(--color-bg);
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-bg-hover);
|
||||||
|
}
|
||||||
|
&._active {
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Единицы измерения
|
||||||
|
|
||||||
|
- `px` — основная единица измерения.
|
||||||
|
- Остальные (`em`, `rem`, `%`, `vh`/`vw`) — допускаются по необходимости дизайна.
|
||||||
|
|
||||||
|
## Порядок CSS-свойств
|
||||||
|
|
||||||
|
В стилях рекомендуется придерживаться логического порядка свойств:
|
||||||
|
|
||||||
|
1. Позиционирование (`position`, `top`, `left`, `z-index`).
|
||||||
|
2. Блочная модель (`display`, `width`, `height`, `margin`, `padding`).
|
||||||
|
3. Оформление (`background`, `border`, `box-shadow`, `border-radius`).
|
||||||
|
4. Текст (`font`, `color`, `text-align`, `line-height`).
|
||||||
|
5. Прочее (`transition`, `animation`, `opacity`, `cursor`).
|
||||||
|
|
||||||
|
## Комментарии
|
||||||
|
|
||||||
|
- Желательно не писать комментарии в CSS.
|
||||||
|
- Исключение — нетривиальные хаки и обходные решения, к которым стоит оставить пояснение.
|
||||||
|
|||||||
Reference in New Issue
Block a user