mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 07:30:16 +03:00
feat: add example
This commit is contained in:
113
.templates/README.md
Normal file
113
.templates/README.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Шаблоны монорепозитория
|
||||
|
||||
Шаблоны создают повторяемый boilerplate после принятого SLM-решения. Шаблон не выбирает owner, layer, scope, public API или product data path.
|
||||
|
||||
Запускай генератор из корня монорепозитория, где находится `.templates`:
|
||||
|
||||
```bash
|
||||
npx @gromlab/create <template> <name> [path]
|
||||
```
|
||||
|
||||
`[path]` — папка вывода относительно корня монорепозитория, например `apps/admin/src/compositions/screens`.
|
||||
|
||||
## Как выбирать шаблон
|
||||
|
||||
Сначала примени `slm-design`. Затем выбирай специализированный шаблон: `route`, `layout`, `screen`, `widget`, `business`, `business-composition` или `rest-api`.
|
||||
|
||||
Если сущность не попадает под один из специализированных шаблонов, используй `module`.
|
||||
|
||||
`module` — основной универсальный шаблон для обычных SLM-модулей и компонентов внутри существующих модулей.
|
||||
|
||||
## Доступные шаблоны
|
||||
|
||||
| Шаблон | Для чего | Куда генерировать |
|
||||
|---|---|---|
|
||||
| `route` | Leaf route module Next.js с обязательным business provider, одноимённым screen и public API. | `apps/web/src/compositions/routes` |
|
||||
| `layout` | Layout composition с `*.layout.tsx`, стилями, props и public API. | `apps/{app}/src/compositions/layouts` |
|
||||
| `screen` | Screen composition с `*.screen.tsx`, стилями, props и public API. | `apps/{app}/src/compositions/screens` |
|
||||
| `widget` | Переиспользуемый composition widget, не привязанный к одной странице. | `apps/{app}/src/compositions/widgets` |
|
||||
| `business` | Business-домен слоя `src/business`: factory, deps, domain error и public API. | `apps/{app}/src/business` |
|
||||
| `business-composition` | Runtime-сборка business-домена: `create*Business` и public API. | `apps/{app}/src/compositions/business` |
|
||||
| `rest-api` | Vite runtime REST-модуль поверх generated SDK: transport, errors и полный bound-клиент. Только для `apps/admin`. | `apps/admin/src/infra` |
|
||||
| `module` | Универсальный SLM-модуль или компонент, если сущность не подходит под шаблоны выше. | `apps/{app}/src/**/parts`, `apps/{app}/src/**/ui`, локальные сегменты модулей |
|
||||
|
||||
## Route
|
||||
|
||||
`route` создаёт leaf route module Next.js в `apps/web/src/compositions/routes`.
|
||||
|
||||
Передавай имя без суффикса `-route`: генератор создаст папку `{name}`, файл `{name}.route.tsx`, компонент `{Name}Route` и обязательный `{Name}BusinessProvider`.
|
||||
|
||||
Route и одноимённый screen должны представлять одну leaf-страницу: `home` route подключает `HomeScreen`, `product` route подключает `ProductScreen`.
|
||||
|
||||
Route module не собирает общий layout. Общий каркас route group остаётся в соответствующем `app/**/layout.tsx` и подключает layout composition напрямую.
|
||||
|
||||
Business provider создаётся всегда, даже если route пока не владеет business API. До появления реального graph он передаёт точное пустое значение `value={{}}`.
|
||||
|
||||
После генерации добавляй в provider только реально используемые route-level business API и определяй их lifecycle внутри route scope.
|
||||
|
||||
Экспортируй route component через public API модуля и подключай его из соответствующего `app/**/page.tsx`.
|
||||
|
||||
## Module
|
||||
|
||||
`module` — fallback-шаблон проекта.
|
||||
|
||||
Используй его для:
|
||||
|
||||
- обычных вложенных модулей в `parts`;
|
||||
- компонентов внутри `ui` родительского модуля;
|
||||
- UI-модулей слоя `src/ui`;
|
||||
- небольших composition-модулей, для которых нет отдельного шаблона;
|
||||
- любой повторяемой сущности, которая не является `route`, `layout`, `screen`, `widget`, `business` или `business-composition`.
|
||||
|
||||
Шаблон создаёт корневой `.tsx`, `styles`, `types` и `index.ts`. После генерации убирай лишнее вручную, если конкретному модулю не нужны стили или props.
|
||||
|
||||
## Business
|
||||
|
||||
`business` создаёт каркас доменного модуля слоя `src/business`.
|
||||
|
||||
После генерации:
|
||||
|
||||
- не оставляй пустые `Api` и `Deps`: замени их реальными сценариями и минимальными runtime capabilities;
|
||||
- добавь `services`, `hooks`, `mappers`, `lib` и доменные типы по мере появления логики;
|
||||
- расширь `ERROR_CODES` конкретными кодами;
|
||||
- добавь factory-level tests до завершения задачи.
|
||||
|
||||
## Business Composition
|
||||
|
||||
`business-composition` создаёт runtime-сборку business-домена для application scope.
|
||||
|
||||
После генерации:
|
||||
|
||||
- замени пустой вызов `factory({})` на явные private adapters и config;
|
||||
- добавь отдельный `adapters/*` для каждой runtime capability из `Deps`;
|
||||
- добавь `types/create-*-business-deps.type.ts`, если сборка зависит от другого business API;
|
||||
- не экспортируй adapters через `index.ts`;
|
||||
- добавь assembly tests до завершения задачи.
|
||||
|
||||
## REST API
|
||||
|
||||
`rest-api` создаёт Vite runtime infra-модуль для generated SDK `{name}-rest-api-sdk`. Шаблон не выбирает auth strategy и не предназначен для Next.js.
|
||||
|
||||
Передавай имя сервиса без суффикса `-rest-api`: `adp-client` создаст `src/infra/adp-client-rest-api`.
|
||||
|
||||
После генерации:
|
||||
|
||||
- настрой фактическую переменную окружения для base URL;
|
||||
- добавь auth, cookies, CSRF и lifecycle только по фактическому контракту сервиса;
|
||||
- экспортируй только transport API и точечные types, имеющие реальных consumers;
|
||||
- подключай product operations через private adapters в `compositions/business/{domain}`;
|
||||
- не используй GET-хуки напрямую из page, route, screen, widget или UI.
|
||||
|
||||
## Примеры
|
||||
|
||||
```bash
|
||||
npx @gromlab/create route home apps/web/src/compositions/routes
|
||||
npx @gromlab/create layout main apps/admin/src/compositions/layouts
|
||||
npx @gromlab/create screen main apps/admin/src/compositions/screens
|
||||
npx @gromlab/create widget error-state apps/admin/src/compositions/widgets
|
||||
npx @gromlab/create business user apps/admin/src/business
|
||||
npx @gromlab/create business-composition user apps/admin/src/compositions/business
|
||||
npx @gromlab/create rest-api backend apps/admin/src/infra
|
||||
npx @gromlab/create module hero-section apps/admin/src/compositions/screens/main/parts
|
||||
npx @gromlab/create module submit-button apps/admin/src/compositions/screens/main/ui
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
import { {{name.camelCase}}Factory } from '../../../business/{{name.kebabCase}}'
|
||||
import type { {{name.pascalCase}}Api } from '../../../business/{{name.kebabCase}}'
|
||||
|
||||
/**
|
||||
* Создаёт {{name.pascalCase}} business API с runtime-зависимостями приложения.
|
||||
*/
|
||||
export const create{{name.pascalCase}}Business = (): {{name.pascalCase}}Api => {
|
||||
return {{name.camelCase}}Factory({})
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { create{{name.pascalCase}}Business } from './create-{{name.kebabCase}}-business'
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { {{name.pascalCase}}ErrorCode } from '../types/{{name.kebabCase}}-error-code.type'
|
||||
|
||||
/**
|
||||
* Доменная ошибка бизнес-модуля {{name.pascalCase}}.
|
||||
*
|
||||
* UI/i18n-слой должен использовать `code`, а не `message`, для выбора пользовательского текста.
|
||||
*/
|
||||
export class {{name.pascalCase}}BusinessError extends Error {
|
||||
/** Код доменной ошибки для UI/i18n-слоя. */
|
||||
readonly code: {{name.pascalCase}}ErrorCode
|
||||
/** Исходная ошибка нижележащей runtime-зависимости. */
|
||||
readonly cause: unknown
|
||||
|
||||
constructor(code: {{name.pascalCase}}ErrorCode, cause: unknown) {
|
||||
super(code)
|
||||
|
||||
this.name = '{{name.pascalCase}}BusinessError'
|
||||
this.code = code
|
||||
this.cause = cause
|
||||
}
|
||||
}
|
||||
6
.templates/business/{{name.kebabCase}}/index.ts
Normal file
6
.templates/business/{{name.kebabCase}}/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export { {{name.camelCase}}Factory } from './{{name.kebabCase}}.factory'
|
||||
export type { {{name.pascalCase}}Api } from './types/{{name.kebabCase}}-api.type'
|
||||
export type { {{name.pascalCase}}Deps } from './types/{{name.kebabCase}}-deps.type'
|
||||
export type { {{name.pascalCase}}Error } from './types/{{name.kebabCase}}-error.type'
|
||||
export type { {{name.pascalCase}}ErrorCode } from './types/{{name.kebabCase}}-error-code.type'
|
||||
export type { {{name.pascalCase}}Factory } from './types/{{name.kebabCase}}-factory.type'
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Публичный API бизнес-модуля {{name.pascalCase}}.
|
||||
*/
|
||||
export type {{name.pascalCase}}Api = object
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Runtime-зависимости бизнес-модуля {{name.pascalCase}}.
|
||||
*/
|
||||
export type {{name.pascalCase}}Deps = object
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Коды доменных ошибок бизнес-модуля {{name.pascalCase}}.
|
||||
*/
|
||||
export const {{name.screamingSnakeCase}}_ERROR_CODES = {
|
||||
/** Базовая доменная ошибка бизнес-модуля {{name.pascalCase}}. */
|
||||
UNKNOWN: '{{name.screamingSnakeCase}}_UNKNOWN'
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Код доменной ошибки бизнес-модуля {{name.pascalCase}}.
|
||||
*/
|
||||
export type {{name.pascalCase}}ErrorCode = (typeof {{name.screamingSnakeCase}}_ERROR_CODES)[keyof typeof {{name.screamingSnakeCase}}_ERROR_CODES]
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { {{name.pascalCase}}ErrorCode } from './{{name.kebabCase}}-error-code.type'
|
||||
|
||||
/**
|
||||
* Публичный структурный контракт доменной ошибки {{name.pascalCase}}.
|
||||
*/
|
||||
export type {{name.pascalCase}}Error = {
|
||||
/** Исходная ошибка нижележащей runtime-зависимости. */
|
||||
cause: unknown
|
||||
/** Код доменной ошибки для UI/i18n-слоя. */
|
||||
code: {{name.pascalCase}}ErrorCode
|
||||
/** Сообщение ошибки, не используемое как UI-контракт. */
|
||||
message: string
|
||||
/** Имя доменной ошибки для диагностики. */
|
||||
name: string
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { {{name.pascalCase}}Api } from './{{name.kebabCase}}-api.type'
|
||||
import type { {{name.pascalCase}}Deps } from './{{name.kebabCase}}-deps.type'
|
||||
|
||||
/**
|
||||
* Фабрика публичного API бизнес-модуля {{name.pascalCase}}.
|
||||
*/
|
||||
export type {{name.pascalCase}}Factory = (deps: {{name.pascalCase}}Deps) => {{name.pascalCase}}Api
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { {{name.pascalCase}}Factory } from './types/{{name.kebabCase}}-factory.type'
|
||||
|
||||
/**
|
||||
* Создаёт публичный API бизнес-модуля {{name.pascalCase}}.
|
||||
*/
|
||||
export const {{name.camelCase}}Factory: {{name.pascalCase}}Factory = (_deps) => {
|
||||
return {}
|
||||
}
|
||||
2
.templates/layout/{{name.kebabCase}}/index.ts
Normal file
2
.templates/layout/{{name.kebabCase}}/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { {{name.pascalCase}}Layout } from './{{name.kebabCase}}.layout'
|
||||
export type { {{name.pascalCase}}LayoutProps } from './types/{{name.kebabCase}}-layout-props.type'
|
||||
@@ -0,0 +1,2 @@
|
||||
.root {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { ComponentPropsWithoutRef } from 'react'
|
||||
|
||||
/**
|
||||
* Параметры layout {{name.pascalCase}}.
|
||||
*/
|
||||
export type {{name.pascalCase}}LayoutParams = object
|
||||
|
||||
/** Атрибуты корневого элемента. */
|
||||
type RootAttrs = ComponentPropsWithoutRef<'div'>
|
||||
|
||||
export type {{name.pascalCase}}LayoutProps = RootAttrs & {{name.pascalCase}}LayoutParams
|
||||
@@ -0,0 +1,20 @@
|
||||
import cl from 'clsx'
|
||||
import type { {{name.pascalCase}}LayoutProps } from './types/{{name.kebabCase}}-layout-props.type'
|
||||
import styles from './styles/{{name.kebabCase}}.module.css'
|
||||
|
||||
/**
|
||||
* <Назначение layout {{name.pascalCase}} в 1 строке>.
|
||||
*
|
||||
* Используется для:
|
||||
* - <сценарий 1>
|
||||
* - <сценарий 2>
|
||||
*/
|
||||
export const {{name.pascalCase}}Layout = (props: {{name.pascalCase}}LayoutProps) => {
|
||||
const { children, className, ...rootAttrs } = props
|
||||
|
||||
return (
|
||||
<div {...rootAttrs} className={cl(styles.root, className)}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
2
.templates/module/{{name.kebabCase}}/index.ts
Normal file
2
.templates/module/{{name.kebabCase}}/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { {{name.pascalCase}} } from './{{name.kebabCase}}'
|
||||
export type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}-props.type'
|
||||
@@ -0,0 +1,2 @@
|
||||
.root {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { ComponentPropsWithoutRef } from 'react'
|
||||
|
||||
/**
|
||||
* Параметры модуля {{name.pascalCase}}.
|
||||
*/
|
||||
export type {{name.pascalCase}}Params = object
|
||||
|
||||
/** Атрибуты корневого элемента. */
|
||||
type RootAttrs = ComponentPropsWithoutRef<'div'>
|
||||
|
||||
export type {{name.pascalCase}}Props = RootAttrs & {{name.pascalCase}}Params
|
||||
20
.templates/module/{{name.kebabCase}}/{{name.kebabCase}}.tsx
Normal file
20
.templates/module/{{name.kebabCase}}/{{name.kebabCase}}.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import cl from 'clsx'
|
||||
import type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}-props.type'
|
||||
import styles from './styles/{{name.kebabCase}}.module.css'
|
||||
|
||||
/**
|
||||
* <Назначение {{name.pascalCase}} в 1 строке>.
|
||||
*
|
||||
* Используется для:
|
||||
* - <сценарий 1>
|
||||
* - <сценарий 2>
|
||||
*/
|
||||
export const {{name.pascalCase}} = (props: {{name.pascalCase}}Props) => {
|
||||
const { children, className, ...rootAttrs } = props
|
||||
|
||||
return (
|
||||
<div {...rootAttrs} className={cl(styles.root, className)}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
14
.templates/rest-api/{{name.kebabCase}}-rest-api/client.ts
Normal file
14
.templates/rest-api/{{name.kebabCase}}-rest-api/client.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { HttpClient } from '@biocad/{{name.kebabCase}}-rest-api-sdk'
|
||||
|
||||
import {
|
||||
{{name.screamingSnakeCase}}_REST_API_BASE_URL,
|
||||
{{name.screamingSnakeCase}}_REST_API_TIMEOUT_MS
|
||||
} from './config/{{name.kebabCase}}-rest-api.config'
|
||||
import { {{name.camelCase}}RestApiFetch } from './lib/{{name.kebabCase}}-rest-api-fetch'
|
||||
|
||||
/** Транспортный HTTP-клиент {{name.pascalCase}} REST API. */
|
||||
export const {{name.camelCase}}HttpClient = new HttpClient({
|
||||
baseUrl: {{name.screamingSnakeCase}}_REST_API_BASE_URL,
|
||||
customFetch: {{name.camelCase}}RestApiFetch,
|
||||
timeout: {{name.screamingSnakeCase}}_REST_API_TIMEOUT_MS
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
/** Базовый URL {{name.pascalCase}} REST API. */
|
||||
export const {{name.screamingSnakeCase}}_REST_API_BASE_URL = import.meta.env.VITE_{{name.screamingSnakeCase}}_REST_API_BASE_URL ?? ''
|
||||
|
||||
/** Максимальная длительность REST-запроса до автоматической отмены. */
|
||||
export const {{name.screamingSnakeCase}}_REST_API_TIMEOUT_MS = 30_000
|
||||
@@ -0,0 +1,3 @@
|
||||
export { ApiError as {{name.pascalCase}}RestApiError } from '@biocad/{{name.kebabCase}}-rest-api-sdk'
|
||||
export { {{name.pascalCase}}RestApiTransportError } from './{{name.kebabCase}}-rest-api-transport.error'
|
||||
export type { {{name.pascalCase}}RestApiTransportErrorCode } from './{{name.kebabCase}}-rest-api-transport.error'
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Причина сбоя непосредственно на transport-границе {{name.pascalCase}} REST API.
|
||||
*/
|
||||
export type {{name.pascalCase}}RestApiTransportErrorCode = 'NETWORK_UNAVAILABLE' | 'REQUEST_TIMEOUT'
|
||||
|
||||
/**
|
||||
* Маркирует fetch failure, не смешивая его с TypeError из прикладного кода.
|
||||
*/
|
||||
export class {{name.pascalCase}}RestApiTransportError extends Error {
|
||||
readonly code: {{name.pascalCase}}RestApiTransportErrorCode
|
||||
readonly cause: unknown
|
||||
|
||||
constructor(code: {{name.pascalCase}}RestApiTransportErrorCode, cause: unknown) {
|
||||
super(code)
|
||||
|
||||
this.name = '{{name.pascalCase}}RestApiTransportError'
|
||||
this.code = code
|
||||
this.cause = cause
|
||||
}
|
||||
}
|
||||
3
.templates/rest-api/{{name.kebabCase}}-rest-api/index.ts
Normal file
3
.templates/rest-api/{{name.kebabCase}}-rest-api/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { {{name.camelCase}}HttpClient } from './client'
|
||||
export { {{name.pascalCase}}RestApiError, {{name.pascalCase}}RestApiTransportError } from './errors'
|
||||
export { {{name.camelCase}}RestApi } from './rest-api'
|
||||
@@ -0,0 +1,14 @@
|
||||
import { {{name.pascalCase}}RestApiTransportError } from '../errors'
|
||||
|
||||
/**
|
||||
* Выполняет fetch с явной классификацией transport failure.
|
||||
*/
|
||||
export const {{name.camelCase}}RestApiFetch: typeof fetch = async (...params) => {
|
||||
try {
|
||||
return await fetch(...params)
|
||||
} catch (error) {
|
||||
const code = error instanceof DOMException && error.name === 'AbortError' ? 'REQUEST_TIMEOUT' : 'NETWORK_UNAVAILABLE'
|
||||
|
||||
throw new {{name.pascalCase}}RestApiTransportError(code, error)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { createApiClient } from '@biocad/{{name.kebabCase}}-rest-api-sdk/create-api-client'
|
||||
import { operationsTree } from '@biocad/{{name.kebabCase}}-rest-api-sdk/operations-tree'
|
||||
|
||||
import { {{name.camelCase}}HttpClient } from './client'
|
||||
|
||||
/** Полный bound-клиент {{name.pascalCase}} REST API. */
|
||||
export const {{name.camelCase}}RestApi = createApiClient({{name.camelCase}}HttpClient, operationsTree)
|
||||
1
.templates/route/{{name.kebabCase}}/index.ts
Normal file
1
.templates/route/{{name.kebabCase}}/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { {{name.pascalCase}}Route } from './{{name.kebabCase}}.route'
|
||||
@@ -0,0 +1,18 @@
|
||||
import { BusinessProvider } from '@/infra/business'
|
||||
import type { {{name.pascalCase}}BusinessProviderProps } from '../types/{{name.kebabCase}}-business-provider-props.type'
|
||||
|
||||
/**
|
||||
* Route-level provider бизнес-модулей для {{name.pascalCase}} route.
|
||||
*
|
||||
* Используется для:
|
||||
* - владения business API в lifecycle route-ветки
|
||||
*/
|
||||
export const {{name.pascalCase}}BusinessProvider = (props: {{name.pascalCase}}BusinessProviderProps) => {
|
||||
const { children } = props
|
||||
|
||||
return (
|
||||
<BusinessProvider value={{}}>
|
||||
{children}
|
||||
</BusinessProvider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
/**
|
||||
* Параметры {{name.pascalCase}}BusinessProvider.
|
||||
*/
|
||||
export type {{name.pascalCase}}BusinessProviderProps = {
|
||||
/** Дочернее дерево {{name.pascalCase}} route. */
|
||||
children: ReactNode
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { {{name.pascalCase}}Screen } from '@/compositions/screens/{{name.kebabCase}}'
|
||||
import { {{name.pascalCase}}BusinessProvider } from './providers/{{name.kebabCase}}-business.provider'
|
||||
|
||||
/**
|
||||
* Route module страницы {{name.pascalCase}}.
|
||||
*
|
||||
* Используется для:
|
||||
* - сборки route-level business graph и screen страницы
|
||||
*/
|
||||
export const {{name.pascalCase}}Route = () => {
|
||||
return (
|
||||
<{{name.pascalCase}}BusinessProvider>
|
||||
<{{name.pascalCase}}Screen />
|
||||
</{{name.pascalCase}}BusinessProvider>
|
||||
)
|
||||
}
|
||||
2
.templates/screen/{{name.kebabCase}}/index.ts
Normal file
2
.templates/screen/{{name.kebabCase}}/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { {{name.pascalCase}}Screen } from './{{name.kebabCase}}.screen'
|
||||
export type { {{name.pascalCase}}ScreenProps } from './types/{{name.kebabCase}}-screen-props.type'
|
||||
@@ -0,0 +1,2 @@
|
||||
.root {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { ComponentPropsWithoutRef } from 'react'
|
||||
|
||||
/**
|
||||
* Параметры экрана {{name.pascalCase}}.
|
||||
*/
|
||||
export type {{name.pascalCase}}ScreenParams = object
|
||||
|
||||
/** Атрибуты корневого элемента. */
|
||||
type RootAttrs = ComponentPropsWithoutRef<'main'>
|
||||
|
||||
export type {{name.pascalCase}}ScreenProps = RootAttrs & {{name.pascalCase}}ScreenParams
|
||||
@@ -0,0 +1,20 @@
|
||||
import cl from 'clsx'
|
||||
import type { {{name.pascalCase}}ScreenProps } from './types/{{name.kebabCase}}-screen-props.type'
|
||||
import styles from './styles/{{name.kebabCase}}.module.css'
|
||||
|
||||
/**
|
||||
* <Назначение экрана {{name.pascalCase}} в 1 строке>.
|
||||
*
|
||||
* Используется для:
|
||||
* - <сценарий 1>
|
||||
* - <сценарий 2>
|
||||
*/
|
||||
export const {{name.pascalCase}}Screen = (props: {{name.pascalCase}}ScreenProps) => {
|
||||
const { children, className, ...rootAttrs } = props
|
||||
|
||||
return (
|
||||
<main {...rootAttrs} className={cl(styles.root, className)}>
|
||||
{children}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
2
.templates/widget/{{name.kebabCase}}/index.ts
Normal file
2
.templates/widget/{{name.kebabCase}}/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { {{name.pascalCase}}Widget } from './{{name.kebabCase}}.widget'
|
||||
export type { {{name.pascalCase}}WidgetProps } from './types/{{name.kebabCase}}-widget-props.type'
|
||||
@@ -0,0 +1,2 @@
|
||||
.root {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { ComponentPropsWithoutRef } from 'react'
|
||||
|
||||
/**
|
||||
* Параметры виджета {{name.pascalCase}}.
|
||||
*/
|
||||
export type {{name.pascalCase}}WidgetParams = object
|
||||
|
||||
/** Атрибуты корневого элемента. */
|
||||
type RootAttrs = ComponentPropsWithoutRef<'div'>
|
||||
|
||||
export type {{name.pascalCase}}WidgetProps = RootAttrs & {{name.pascalCase}}WidgetParams
|
||||
@@ -0,0 +1,20 @@
|
||||
import cl from 'clsx'
|
||||
import type { {{name.pascalCase}}WidgetProps } from './types/{{name.kebabCase}}-widget-props.type'
|
||||
import styles from './styles/{{name.kebabCase}}.module.css'
|
||||
|
||||
/**
|
||||
* <Назначение виджета {{name.pascalCase}} в 1 строке>.
|
||||
*
|
||||
* Используется для:
|
||||
* - <сценарий 1>
|
||||
* - <сценарий 2>
|
||||
*/
|
||||
export const {{name.pascalCase}}Widget = (props: {{name.pascalCase}}WidgetProps) => {
|
||||
const { children, className, ...rootAttrs } = props
|
||||
|
||||
return (
|
||||
<div {...rootAttrs} className={cl(styles.root, className)}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user