forked from templates/nextjs-template
chore: обновить инфраструктуру Mantine и шаблоны
- добавлен алиас infra и обновлены импорты Mantine - настроены тема, цветовая схема и CSS-переменные Mantine - добавлен переключатель цветовой схемы на главный экран - обновлены шаблоны бизнес- и infra-модулей под фабрики - добавлена команда генерации Petstore API и исключён generated-код из Biome - обновлены JSDoc-комментарии React-компонентов
This commit is contained in:
@@ -1,2 +1,5 @@
|
|||||||
# Базовый URL приложения
|
# Базовый URL приложения
|
||||||
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||||
|
|
||||||
|
# Базовый URL локального Swagger Petstore API
|
||||||
|
NEXT_PUBLIC_PET_STORE_API_URL=http://localhost:8080/api/v3
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
export { {{name.pascalCase}}Business } from './{{name.kebabCase}}.business'
|
export { {{name.camelCase}}Factory } from './{{name.kebabCase}}.factory';
|
||||||
|
export type { {{name.pascalCase}} } from './types/{{name.kebabCase}}.type';
|
||||||
|
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}}Factory } from './types/{{name.kebabCase}}-factory.type';
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Публичный API бизнес-модуля {{name.pascalCase}}.
|
||||||
|
*/
|
||||||
|
export type {{name.pascalCase}}Api = Record<string, never>;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Зависимости бизнес-модуля {{name.pascalCase}}.
|
||||||
|
*/
|
||||||
|
export type {{name.pascalCase}}Deps = Record<string, never>;
|
||||||
@@ -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;
|
||||||
@@ -1,11 +1,4 @@
|
|||||||
import type { HTMLAttributes } from 'react'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Параметры бизнес-модуля {{name.pascalCase}}.
|
* Доменная сущность {{name.pascalCase}}.
|
||||||
*/
|
*/
|
||||||
export type {{name.pascalCase}}BusinessParams = {}
|
export type {{name.pascalCase}} = Record<string, never>;
|
||||||
|
|
||||||
/** HTML-атрибуты корневого элемента. */
|
|
||||||
type RootAttrs = HTMLAttributes<HTMLDivElement>
|
|
||||||
|
|
||||||
export type {{name.pascalCase}}BusinessProps = RootAttrs & {{name.pascalCase}}BusinessParams
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import cl from 'clsx'
|
|
||||||
import type { {{name.pascalCase}}BusinessProps } from './types/{{name.kebabCase}}.type'
|
|
||||||
import styles from './styles/{{name.kebabCase}}.module.css'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <Назначение бизнес-модуля {{name.pascalCase}} в 1 строке>.
|
|
||||||
*
|
|
||||||
* Используется для:
|
|
||||||
* - <сценарий 1>
|
|
||||||
* - <сценарий 2>
|
|
||||||
*/
|
|
||||||
export const {{name.pascalCase}}Business = (props: {{name.pascalCase}}BusinessProps) => {
|
|
||||||
const { children, className, ...htmlAttr } = props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div {...htmlAttr} className={cl(styles.root, className)}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -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 = () => {
|
||||||
|
return {};
|
||||||
|
};
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
.root {
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,8 @@
|
|||||||
"!.next",
|
"!.next",
|
||||||
"!dist",
|
"!dist",
|
||||||
"!build",
|
"!build",
|
||||||
"!.templates"
|
"!.templates",
|
||||||
|
"!src/infrastructure/**/generated"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"formatter": {
|
"formatter": {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"lint": "biome check",
|
"lint": "biome check",
|
||||||
"format": "biome format --write",
|
"format": "biome format --write",
|
||||||
"sprite": "svg-sprites",
|
"sprite": "svg-sprites",
|
||||||
|
"codegen:pet-store-api": "npx @gromlab/api-codegen@latest -i http://localhost:8080/api/v3/openapi.json -o src/infrastructure/pet-store-api/generated -n pet-store-api.generated",
|
||||||
"predev": "svg-sprites",
|
"predev": "svg-sprites",
|
||||||
"prebuild": "svg-sprites"
|
"prebuild": "svg-sprites"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import '@mantine/core/styles.css';
|
import '@mantine/core/styles.css';
|
||||||
import 'shared/styles/global.css';
|
import 'shared/styles/global.css';
|
||||||
|
|
||||||
import { ColorSchemeScript } from '@mantine/core';
|
import {
|
||||||
import { MantineProvider } from 'infrastructure/mantine';
|
MantineColorSchemeScript,
|
||||||
|
MantineProvider,
|
||||||
|
mantineHtmlProps,
|
||||||
|
} from 'infra/mantine';
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
|
|
||||||
@@ -31,11 +34,18 @@ export const metadata: Metadata = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Корневой layout приложения Next.js.
|
||||||
|
*
|
||||||
|
* Используется для:
|
||||||
|
* - подключения провайдеров приложения
|
||||||
|
* - установки базовой HTML-структуры всех страниц
|
||||||
|
*/
|
||||||
export default function RootLayout({ children }: PropsWithChildren) {
|
export default function RootLayout({ children }: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<html lang="ru" suppressHydrationWarning>
|
<html lang="ru" {...mantineHtmlProps}>
|
||||||
<head>
|
<head>
|
||||||
<ColorSchemeScript />
|
<MantineColorSchemeScript />
|
||||||
<link rel="preload" href="/sprites/icons.sprite.svg" as="image" />
|
<link rel="preload" href="/sprites/icons.sprite.svg" as="image" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ export const metadata: Metadata = {
|
|||||||
description: 'Главная страница приложения',
|
description: 'Главная страница приложения',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Страница главного маршрута приложения.
|
||||||
|
*
|
||||||
|
* Используется для:
|
||||||
|
* - отображения главного экрана в корневом маршруте
|
||||||
|
* - композиции контента главной страницы
|
||||||
|
*/
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
return <HomeScreen />;
|
return <HomeScreen />;
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/infra/mantine/config/color-scheme.config.ts
Normal file
4
src/infra/mantine/config/color-scheme.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import type { MantineColorScheme } from '@mantine/core';
|
||||||
|
|
||||||
|
export const MANTINE_COLOR_SCHEME_STORAGE_KEY = 'app-color-scheme';
|
||||||
|
export const MANTINE_DEFAULT_COLOR_SCHEME: MantineColorScheme = 'auto';
|
||||||
12
src/infra/mantine/config/dark-theme.config.ts
Normal file
12
src/infra/mantine/config/dark-theme.config.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export const darkTheme = {
|
||||||
|
primary: '#60a5fa',
|
||||||
|
background: '#0f172a',
|
||||||
|
backgroundHover: '#1e293b',
|
||||||
|
surface: '#111827',
|
||||||
|
text: '#f8fafc',
|
||||||
|
textSecondary: '#cbd5e1',
|
||||||
|
border: '#334155',
|
||||||
|
error: '#ff6b6b',
|
||||||
|
success: '#69db7c',
|
||||||
|
warning: '#ffd43b',
|
||||||
|
} as const;
|
||||||
12
src/infra/mantine/config/light-theme.config.ts
Normal file
12
src/infra/mantine/config/light-theme.config.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export const lightTheme = {
|
||||||
|
primary: '#2563eb',
|
||||||
|
background: '#ffffff',
|
||||||
|
backgroundHover: '#f8fafc',
|
||||||
|
surface: '#ffffff',
|
||||||
|
text: '#111827',
|
||||||
|
textSecondary: '#64748b',
|
||||||
|
border: '#e2e8f0',
|
||||||
|
error: '#e03131',
|
||||||
|
success: '#2f9e44',
|
||||||
|
warning: '#e67700',
|
||||||
|
} as const;
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import type { CSSVariablesResolver } from '@mantine/core';
|
||||||
|
import { darkTheme } from './dark-theme.config';
|
||||||
|
import { lightTheme } from './light-theme.config';
|
||||||
|
|
||||||
|
type ThemePalette = {
|
||||||
|
primary: string;
|
||||||
|
background: string;
|
||||||
|
backgroundHover: string;
|
||||||
|
surface: string;
|
||||||
|
text: string;
|
||||||
|
textSecondary: string;
|
||||||
|
border: string;
|
||||||
|
error: string;
|
||||||
|
success: string;
|
||||||
|
warning: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ThemeCssVariables = {
|
||||||
|
'--color-primary': string;
|
||||||
|
'--color-bg': string;
|
||||||
|
'--color-bg-hover': string;
|
||||||
|
'--color-surface': string;
|
||||||
|
'--color-text': string;
|
||||||
|
'--color-text-secondary': string;
|
||||||
|
'--color-border': string;
|
||||||
|
'--color-error': string;
|
||||||
|
'--color-success': string;
|
||||||
|
'--color-warning': string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getThemeCssVariables = (theme: ThemePalette): ThemeCssVariables => {
|
||||||
|
return {
|
||||||
|
'--color-primary': theme.primary,
|
||||||
|
'--color-bg': theme.background,
|
||||||
|
'--color-bg-hover': theme.backgroundHover,
|
||||||
|
'--color-surface': theme.surface,
|
||||||
|
'--color-text': theme.text,
|
||||||
|
'--color-text-secondary': theme.textSecondary,
|
||||||
|
'--color-border': theme.border,
|
||||||
|
'--color-error': theme.error,
|
||||||
|
'--color-success': theme.success,
|
||||||
|
'--color-warning': theme.warning,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mantineCssVariablesResolver: CSSVariablesResolver = () => {
|
||||||
|
return {
|
||||||
|
variables: {},
|
||||||
|
light: getThemeCssVariables(lightTheme),
|
||||||
|
dark: getThemeCssVariables(darkTheme),
|
||||||
|
};
|
||||||
|
};
|
||||||
56
src/infra/mantine/config/mantine-theme.config.ts
Normal file
56
src/infra/mantine/config/mantine-theme.config.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import type { MantineColorsTuple, MantineThemeOverride } from '@mantine/core';
|
||||||
|
import { createTheme, virtualColor } from '@mantine/core';
|
||||||
|
import { darkTheme } from './dark-theme.config';
|
||||||
|
import { lightTheme } from './light-theme.config';
|
||||||
|
|
||||||
|
const brandLightColors = [
|
||||||
|
'#eff6ff',
|
||||||
|
'#dbeafe',
|
||||||
|
'#bfdbfe',
|
||||||
|
'#93c5fd',
|
||||||
|
'#60a5fa',
|
||||||
|
'#3b82f6',
|
||||||
|
'#2563eb',
|
||||||
|
'#1d4ed8',
|
||||||
|
'#1e40af',
|
||||||
|
'#1e3a8a',
|
||||||
|
] satisfies MantineColorsTuple;
|
||||||
|
|
||||||
|
const brandDarkColors = [
|
||||||
|
'#dbeafe',
|
||||||
|
'#bfdbfe',
|
||||||
|
'#93c5fd',
|
||||||
|
'#60a5fa',
|
||||||
|
'#3b82f6',
|
||||||
|
'#2563eb',
|
||||||
|
'#1d4ed8',
|
||||||
|
'#1e40af',
|
||||||
|
'#1e3a8a',
|
||||||
|
'#172554',
|
||||||
|
] satisfies MantineColorsTuple;
|
||||||
|
|
||||||
|
export const mantineTheme: MantineThemeOverride = createTheme({
|
||||||
|
black: darkTheme.background,
|
||||||
|
white: lightTheme.background,
|
||||||
|
primaryColor: 'brand',
|
||||||
|
primaryShade: { light: 6, dark: 4 },
|
||||||
|
defaultRadius: 'md',
|
||||||
|
autoContrast: true,
|
||||||
|
cursorType: 'pointer',
|
||||||
|
colors: {
|
||||||
|
brand: virtualColor({
|
||||||
|
name: 'brand',
|
||||||
|
light: 'brandLight',
|
||||||
|
dark: 'brandDark',
|
||||||
|
}),
|
||||||
|
brandLight: brandLightColors,
|
||||||
|
brandDark: brandDarkColors,
|
||||||
|
},
|
||||||
|
fontFamily:
|
||||||
|
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
||||||
|
headings: {
|
||||||
|
fontFamily:
|
||||||
|
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
||||||
|
fontWeight: '700',
|
||||||
|
},
|
||||||
|
});
|
||||||
5
src/infra/mantine/index.ts
Normal file
5
src/infra/mantine/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export { mantineHtmlProps } from '@mantine/core';
|
||||||
|
|
||||||
|
export { MantineProvider } from './providers/mantine-provider';
|
||||||
|
export { MantineColorSchemeScript } from './ui/mantine-color-scheme-script';
|
||||||
|
export { MantineColorSchemeSwitch } from './ui/mantine-color-scheme-switch';
|
||||||
39
src/infra/mantine/providers/mantine-provider.tsx
Normal file
39
src/infra/mantine/providers/mantine-provider.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MantineProvider as BaseMantineProvider,
|
||||||
|
localStorageColorSchemeManager,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import {
|
||||||
|
MANTINE_COLOR_SCHEME_STORAGE_KEY,
|
||||||
|
MANTINE_DEFAULT_COLOR_SCHEME,
|
||||||
|
} from '../config/color-scheme.config';
|
||||||
|
import { mantineCssVariablesResolver } from '../config/mantine-css-variables-resolver.config';
|
||||||
|
import { mantineTheme } from '../config/mantine-theme.config';
|
||||||
|
import type { MantineProviderProps } from '../types/mantine-provider.type';
|
||||||
|
|
||||||
|
const colorSchemeManager = localStorageColorSchemeManager({
|
||||||
|
key: MANTINE_COLOR_SCHEME_STORAGE_KEY,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Провайдер темы и цветовой схемы Mantine.
|
||||||
|
*
|
||||||
|
* Используется для:
|
||||||
|
* - подключения глобальной темы Mantine
|
||||||
|
* - синхронизации цветовой схемы приложения
|
||||||
|
*/
|
||||||
|
export const MantineProvider = (props: MantineProviderProps) => {
|
||||||
|
const { children } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseMantineProvider
|
||||||
|
colorSchemeManager={colorSchemeManager}
|
||||||
|
cssVariablesResolver={mantineCssVariablesResolver}
|
||||||
|
defaultColorScheme={MANTINE_DEFAULT_COLOR_SCHEME}
|
||||||
|
theme={mantineTheme}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</BaseMantineProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
8
src/infra/mantine/types/mantine-provider.type.ts
Normal file
8
src/infra/mantine/types/mantine-provider.type.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Пропсы провайдера Mantine UI.
|
||||||
|
*/
|
||||||
|
export type MantineProviderProps = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
22
src/infra/mantine/ui/mantine-color-scheme-script.tsx
Normal file
22
src/infra/mantine/ui/mantine-color-scheme-script.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { ColorSchemeScript } from '@mantine/core';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import {
|
||||||
|
MANTINE_COLOR_SCHEME_STORAGE_KEY,
|
||||||
|
MANTINE_DEFAULT_COLOR_SCHEME,
|
||||||
|
} from '../config/color-scheme.config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Скрипт инициализации цветовой схемы Mantine до гидрации.
|
||||||
|
*
|
||||||
|
* Используется для:
|
||||||
|
* - применения сохраненной цветовой схемы до загрузки React
|
||||||
|
* - предотвращения мигания темы при первом рендере
|
||||||
|
*/
|
||||||
|
export const MantineColorSchemeScript = (): ReactNode => {
|
||||||
|
return (
|
||||||
|
<ColorSchemeScript
|
||||||
|
defaultColorScheme={MANTINE_DEFAULT_COLOR_SCHEME}
|
||||||
|
localStorageKey={MANTINE_COLOR_SCHEME_STORAGE_KEY}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
127
src/infra/mantine/ui/mantine-color-scheme-switch.tsx
Normal file
127
src/infra/mantine/ui/mantine-color-scheme-switch.tsx
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import type { MantineColorScheme } from '@mantine/core';
|
||||||
|
import {
|
||||||
|
Center,
|
||||||
|
SegmentedControl,
|
||||||
|
useMantineColorScheme,
|
||||||
|
VisuallyHidden,
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useMounted } from '@mantine/hooks';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
const colorSchemeValues = [
|
||||||
|
'auto',
|
||||||
|
'light',
|
||||||
|
'dark',
|
||||||
|
] as const satisfies readonly MantineColorScheme[];
|
||||||
|
|
||||||
|
type ColorSchemeValue = (typeof colorSchemeValues)[number];
|
||||||
|
|
||||||
|
const systemThemeLabel = (
|
||||||
|
<Center component="span" h={24} w={28}>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
fill="none"
|
||||||
|
height="18"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.8"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="18"
|
||||||
|
>
|
||||||
|
<path d="M4 5.5A2.5 2.5 0 0 1 6.5 3h11A2.5 2.5 0 0 1 20 5.5v8A2.5 2.5 0 0 1 17.5 16h-11A2.5 2.5 0 0 1 4 13.5z" />
|
||||||
|
<path d="M9 21h6" />
|
||||||
|
<path d="M12 16v5" />
|
||||||
|
</svg>
|
||||||
|
<VisuallyHidden>Системная тема</VisuallyHidden>
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
|
||||||
|
const lightThemeLabel = (
|
||||||
|
<Center component="span" h={24} w={28}>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
fill="none"
|
||||||
|
height="18"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="1.8"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="18"
|
||||||
|
>
|
||||||
|
<circle cx="12" cy="12" r="4" />
|
||||||
|
<path d="M12 2v2" />
|
||||||
|
<path d="M12 20v2" />
|
||||||
|
<path d="m4.93 4.93 1.41 1.41" />
|
||||||
|
<path d="m17.66 17.66 1.41 1.41" />
|
||||||
|
<path d="M2 12h2" />
|
||||||
|
<path d="M20 12h2" />
|
||||||
|
<path d="m6.34 17.66-1.41 1.41" />
|
||||||
|
<path d="m19.07 4.93-1.41 1.41" />
|
||||||
|
</svg>
|
||||||
|
<VisuallyHidden>Светлая тема</VisuallyHidden>
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
|
||||||
|
const darkThemeLabel = (
|
||||||
|
<Center component="span" h={24} w={28}>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
fill="currentColor"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="18"
|
||||||
|
>
|
||||||
|
<path d="M21 14.31A8.5 8.5 0 0 1 9.69 3a7 7 0 1 0 11.3 11.3Z" />
|
||||||
|
</svg>
|
||||||
|
<VisuallyHidden>Темная тема</VisuallyHidden>
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
|
||||||
|
const colorSchemeItems: { label: ReactNode; value: ColorSchemeValue }[] = [
|
||||||
|
{ label: systemThemeLabel, value: 'auto' },
|
||||||
|
{ label: lightThemeLabel, value: 'light' },
|
||||||
|
{ label: darkThemeLabel, value: 'dark' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const isColorSchemeValue = (value: string): value is ColorSchemeValue => {
|
||||||
|
return colorSchemeValues.some((item) => item === value);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Переключатель цветовой схемы Mantine.
|
||||||
|
*
|
||||||
|
* Используется для:
|
||||||
|
* - выбора системной, светлой или темной темы
|
||||||
|
* - управления пользовательской цветовой схемой приложения
|
||||||
|
*/
|
||||||
|
export const MantineColorSchemeSwitch = (): ReactNode => {
|
||||||
|
const mounted = useMounted();
|
||||||
|
const { colorScheme, setColorScheme } = useMantineColorScheme();
|
||||||
|
const value =
|
||||||
|
mounted && isColorSchemeValue(colorScheme) ? colorScheme : 'auto';
|
||||||
|
|
||||||
|
const handleChange = (nextValue: string): void => {
|
||||||
|
if (!isColorSchemeValue(nextValue)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setColorScheme(nextValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SegmentedControl
|
||||||
|
aria-label="Цветовая схема"
|
||||||
|
color="brand"
|
||||||
|
data={colorSchemeItems}
|
||||||
|
onChange={handleChange}
|
||||||
|
radius="xl"
|
||||||
|
size="sm"
|
||||||
|
value={value}
|
||||||
|
withItemsBorders={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { MantineProvider } from './mantine-provider';
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { MantineProvider as BaseMantineProvider } from '@mantine/core';
|
|
||||||
import type { PropsWithChildren } from 'react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Провайдер Mantine UI.
|
|
||||||
*/
|
|
||||||
export const MantineProvider = ({ children }: PropsWithChildren) => {
|
|
||||||
return <BaseMantineProvider>{children}</BaseMantineProvider>;
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
import { Container, Image, Stack, Text, Title } from '@mantine/core';
|
import { Container, Image, Stack, Text, Title } from '@mantine/core';
|
||||||
|
import { MantineColorSchemeSwitch } from 'infra/mantine';
|
||||||
import styles from './styles/home.module.css';
|
import styles from './styles/home.module.css';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Главный экран приложения.
|
* Главный экран стартовой страницы приложения.
|
||||||
|
*
|
||||||
|
* Используется для:
|
||||||
|
* - отображения приветственного содержимого шаблона
|
||||||
|
* - предоставления быстрого переключения цветовой схемы
|
||||||
*/
|
*/
|
||||||
export const HomeScreen = () => {
|
export const HomeScreen = () => {
|
||||||
return (
|
return (
|
||||||
@@ -18,6 +23,7 @@ export const HomeScreen = () => {
|
|||||||
/>
|
/>
|
||||||
<Title order={1}>Добро пожаловать</Title>
|
<Title order={1}>Добро пожаловать</Title>
|
||||||
<Text c="dimmed">Шаблон приложения на Next.js и TypeScript.</Text>
|
<Text c="dimmed">Шаблон приложения на Next.js и TypeScript.</Text>
|
||||||
|
<MantineColorSchemeSwitch />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"app/*": ["./src/app/*"],
|
"app/*": ["./src/app/*"],
|
||||||
"business/*": ["./src/business/*"],
|
"business/*": ["./src/business/*"],
|
||||||
"infrastructure/*": ["./src/infrastructure/*"],
|
"infra/*": ["./src/infra/*"],
|
||||||
"layouts/*": ["./src/layouts/*"],
|
"layouts/*": ["./src/layouts/*"],
|
||||||
"screens/*": ["./src/screens/*"],
|
"screens/*": ["./src/screens/*"],
|
||||||
"ui/*": ["./src/ui/*"],
|
"ui/*": ["./src/ui/*"],
|
||||||
|
|||||||
Reference in New Issue
Block a user