Перейти к основному содержанию

Компоненты макетов

Этот документ предоставляет справочник по API и паттерны построения для компонентов макетов в @ui8kit/core. Компоненты макетов организуют множество UI-компонентов в полноценные структуры приложений, таких как панели управления, целевые страницы и административные панели. Для базовых примитивных компонентов макета (Grid, Flex, Stack) см. Базовые компоненты. Для паттернов композиции UI-компонентов см. UI-компоненты. Для общих лучших практик по построению макетов см. Лучшие практики.

Обзор

Библиотека предоставляет три специализированных компонента макета, которые составляют примитивы и UI-компоненты в шаблоны приложений:
КомпонентНазначениеОсновной сценарий использования
DashLayoutМакет панели управления с изменяемым размером боковой панелиАдминистративные панели, дашборды
LayoutBlockГибкие секции контента с режимами grid/flex/stackЦелевые страницы, маркетинговые сайты
SplitBlockДвухколоночные разделенные макетыСекции функций, героические секции
Эти макеты находятся на уровне 3 архитектуры и строятся на основе примитивов уровня 1 (Block, Box, Grid, Container) и UI-компонентов уровня 2 (Card, Button, Text, Title). Источники: src/layouts/DashLayout.tsx src/layouts/LayoutBlock.tsx src/layouts/SplitBlock.tsx README.md147-168

Взаимосвязи компонентов

Внешние зависимости

Уровень 1: Базовые примитивы

Уровень 2: UI-компоненты

Уровень 3: Компоненты макетов

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

использует

DashLayout

LayoutBlock

SplitBlock

Navbar

Sidebar

Card

Button

Text

Title

Icon

Badge

Group

Image

Block

Box

Grid

Stack

Container

PanelGroup
(react-resizable-panels)

Panel
(react-resizable-panels)

PanelResizeHandle
(react-resizable-panels)
Диаграмма: Дерево зависимостей компонентов макета, показывающее, как макеты уровня 3 составляют UI-компоненты уровня 2 и примитивы уровня 1, плюс внешние зависимости для продвинутых функций. Источники: src/layouts/DashLayout.tsx1-99 src/layouts/LayoutBlock.tsx1-389 src/layouts/SplitBlock.tsx1-145

DashLayout

Компонент макета панели управления с изменяемой боковой панелью, навигационной панелью и основной областью контента. Построен на react-resizable-panels для функциональности изменения размера панелей.

Свойства DashLayout

interface DashboardProps {
  page?: React.ComponentType;
  children?: React.ReactNode;
  sidebar?: React.ReactNode;
  navbarProps?: Omit<NavbarProps, 'toggleDarkMode' | 'isDarkMode'> & 
    Partial<Pick<NavbarProps, 'isDarkMode' | 'toggleDarkMode'>>;
}
СвойствоТипПо умолчаниюОписание
pageReact.ComponentType-Компонент для рендеринга в основной области контента
childrenReact.ReactNode-Альтернатива свойству page для основного контента
sidebarReact.ReactNode-Контент для изменяемой боковой панели
navbarPropsPartial<NavbarProps>-Свойства для навигационной панели, включая бренд, переключатель темы

Подкомпонент Navbar

interface NavbarProps {
  isDarkMode: boolean;
  toggleDarkMode: () => void;
  brand?: string;
}
Минималистичная навигационная панель с идентификацией бренда и переключателем темы. Рендерится как семантический элемент <nav> с атрибутом data-role="dash-navbar". Реализация: src/layouts/DashLayout.tsx34-49

Подкомпонент Sidebar

interface SidebarProps {
  children?: React.ReactNode;
  className?: string;
  dataClass?: string;
  title?: string;
}
Контейнер изменяемой боковой панели, рендерится как семантический элемент <aside>. Оборачивает дочерние элементы в Stack макет с опциональным заголовком. Реализация: src/layouts/DashLayout.tsx14-25

Структура панелей

DashLayout

Navbar
(component='nav')

Block
(component='main')

PanelGroup
(direction='horizontal'
autoSaveId='dashlayout-panels')

Panel
(defaultSize=20
minSize=10
maxSize=40)

Panel
(defaultSize=80
minSize=50)

PanelResizeHandle

Sidebar
(component='aside')

Box + Container
Диаграмма: Структура панелей DashLayout, показывающая PanelGroup с изменяемой боковой панелью (20% по умолчанию) и основным контентом (80% по умолчанию), разделенными ручкой изменения размера. Источники: src/layouts/DashLayout.tsx64-91

Паттерн импорта

// Полный импорт
import { DashLayout } from '@ui8kit/core';

// Импорт подкомпонентов
import { Dashboard, Navbar, Sidebar } from '@ui8kit/core';
Источники: src/layouts/DashLayout.tsx93-96

LayoutBlock

Гибкий компонент секции контента, поддерживающий три режима макета: grid, flex и stack. Включает систему хуков контента для настраиваемого рендеринга и презентеры по умолчанию для распространенных паттернов.

Свойства LayoutBlock

interface LayoutBlockProps {
  // Управление макетом
  layout: LayoutType; // "grid" | "flex" | "stack"
  
  // Настройки контейнера
  useContainer?: boolean;
  containerSize?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
  padding?: VariantSpacingProps["px"] | null;
  py?: VariantSpacingProps["py"];

  // Настройки Grid (layout="grid")
  cols?: VariantGridProps["cols"];
  gridCols?: VariantGridProps["cols"];
  gap?: VariantGridProps["gap"];
  align?: VariantGridProps["align"];
  justify?: VariantGridProps["justify"];

  // Настройки Flex (layout="flex")
  wrap?: VariantFlexProps["wrap"];
  flexWrap?: boolean;

  // Настройки Stack (layout="stack")
  stackAlign?: VariantFlexProps["align"];

  // Настройки заголовка
  showHeader?: boolean;
  headerAlign?: "start" | "center" | "end";

  // Данные контента
  content?: {
    badge?: string;
    title?: string;
    description?: string;
    items?: Array<{
      id: string;
      title?: string;
      description: string;
      image?: { src: string; alt: string; };
      lucideIcon?: any;
      [key: string]: any;
    }>;
    [key: string]: any;
  };

  // Хуки контента
  contentHooks?: LayoutContentHooks;
  
  className?: string;
}
Источники: src/layouts/LayoutBlock.tsx32-84

Режимы макета

РежимКомпонентСценарий использованияСвойства
gridGridСетки функций, галереи карточекcols, gridCols, gap, align, justify
flexGroupГибкие переносимые макетыwrap, flexWrap, gap, align, justify
stackStackВертикальные списки, таймлайныstackAlign, gap

Система хуков контента

interface LayoutContentHooks {
  beforeHeader?: (content: any) => ReactNode;
  header?: (content: any) => ReactNode;
  afterHeader?: (content: any) => ReactNode;
  beforeItems?: (content: any) => ReactNode;
  item?: (item: any, index: number) => ReactNode;
  afterItems?: (content: any) => ReactNode;
}
Хуки контента обеспечивают пользовательскую логику рендеринга в определенных точках жизненного цикла макета. Хуки по умолчанию предоставляются для распространенных паттернов:
Набор хуковРежим макетаРендерер элементаСценарий использования
defaultLayoutContentHooks.gridCardsgridDefaultItemRenderers.gridCardСетки на основе карточек с изображениями
defaultLayoutContentHooks.gridSimplegridDefaultItemRenderers.gridSimpleПростые сетки иконка + текст
defaultLayoutContentHooks.flexflexDefaultItemRenderers.flexItemГоризонтальные списки функций
defaultLayoutContentHooks.stackstackDefaultItemRenderers.flexItemВертикальные списки функций
Источники: src/layouts/LayoutBlock.tsx23-30 src/layouts/LayoutBlock.tsx229-254

Рендереры элементов по умолчанию

layout='grid'
есть изображения

layout='grid'
нет изображений

layout='flex'

layout='stack'

LayoutBlock

gridCard
Card + Image + Icon
Макет Stack

gridSimple
Icon + Title + Text
Макет Stack

flexItem
Icon + Title + Text
Макет Group
Диаграмма: Логика выбора рендерера элемента по умолчанию на основе режима макета и структуры контента. Источники: src/layouts/LayoutBlock.tsx123-227

Паттерн импорта

// Базовый импорт
import { LayoutBlock } from '@ui8kit/core';

// С хуками контента
import { 
  LayoutBlock, 
  defaultLayoutContentHooks,
  createLayoutContentHook 
} from '@ui8kit/core';
Источники: src/layouts/LayoutBlock.tsx256-389

SplitBlock

Компонент двухколоночного разделенного макета для героических секций и демонстрации функций. Поддерживает позиционирование медиа слева/справа и опциональную обертку в контейнер.

Свойства SplitBlock

interface SplitBlockProps {
  // Основные секции
  mediaSection?: ReactNode;
  contentSection?: ReactNode;

  // Управление макетом
  leftMedia?: boolean;
  splitSection?: boolean;

  // Данные контента
  content?: {
    [key: string]: any;
  };

  // Хуки контента
  contentHooks?: ContentHooks;

  // Именованные слоты
  slots?: {
    media?: ReactNode;
    header?: ReactNode;
    body?: ReactNode;
    actions?: ReactNode;
  };

  // Настройки контейнера (splitSection=false)
  containerSize?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
  padding?: VariantSpacingProps["px"];
  py?: VariantSpacingProps["py"];

  // Настройки Grid (splitSection=true)
  gap?: VariantGridProps["gap"];
  align?: VariantGridProps["align"];
  
  className?: string;
}
Источники: src/layouts/SplitBlock.tsx17-55

Режимы макета

РежимОписаниеСтруктура
splitSection={false}Макет в контейнереBlock > Container > Grid(cols="1-2")
splitSection={true}Полноширинный макетBlock > Grid(cols="1-2")

Система хуков контента

interface ContentHooks {
  beforeContent?: (content: any) => ReactNode;
  content?: (content: any) => ReactNode;
  afterContent?: (content: any) => ReactNode;
}
Минимальная система хуков для настройки контента. Рендерер контента по умолчанию применяет хуки последовательно: Реализация: src/layouts/SplitBlock.tsx57-65

Паттерн импорта

// Базовый импорт
import { SplitBlock } from '@ui8kit/core';

// С утилитами
import { 
  SplitBlock, 
  DefaultContentSection,
  createContentHook 
} from '@ui8kit/core';
Источники: src/layouts/SplitBlock.tsx67-145

Паттерны построения по сценариям использования

Целевые страницы

Целевые страницы обычно комбинируют множество компонентов LayoutBlock и SplitBlock для героических секций, функций и призывов к действию.
Структура целевой страницы

SplitBlock
Героическая секция
leftMedia=true

LayoutBlock
Сетка функций
layout='grid'
cols='1-2-3'

LayoutBlock
Карусель отзывов
layout='flex'

SplitBlock
Призыв к действию
leftMedia=false
Диаграмма: Типичная структура целевой страницы с использованием компонентов макета. Паттерн импорта:
import { 
  LayoutBlock, 
  SplitBlock,
  Container,
  Card,
  Button,
  Text,
  Title,
  Badge,
  Image
} from '@ui8kit/core';
Пример структуры:
// Героическая секция с медиа слева
<SplitBlock
  leftMedia={true}
  splitSection={false}
  containerSize="xl"
  mediaSection={<Image src="hero.jpg" alt="Hero" />}
  contentSection={
    <Stack gap="lg">
      <Badge>Новая функция</Badge>
      <Title order={1}>Название продукта</Title>
      <Text>Текст описания</Text>
      <Button variant="primary">Начать</Button>
    </Stack>
  }
/>

// Сетка функций с 3 колонками
<LayoutBlock
  layout="grid"
  cols="1-2-3"
  content={{
    title: "Функции",
    items: [
      { id: "1", title: "Быстрый", description: "...", lucideIcon: Zap },
      { id: "2", title: "Безопасный", description: "...", lucideIcon: Lock },
      { id: "3", title: "Простой", description: "...", lucideIcon: Smile }
    ]
  }}
/>
Источники: src/layouts/LayoutBlock.tsx1-389 src/layouts/SplitBlock.tsx1-145

Панели управления

Панели управления используют DashLayout как основной контейнер с изменяемой боковой панелью для навигации.
Приложение панели управления

DashLayout

Navbar
Бренд + переключатель темы

Sidebar
Навигационные ссылки

Основная область контента

LayoutBlock
Карточки статистики
layout='grid'
cols='1-2-4'

Grid
Компоненты графиков

Card
Таблица данных
Диаграмма: Структура панели управления с DashLayout и вложенными макетами контента. Паттерн импорта:
import { 
  DashLayout,
  LayoutBlock,
  Card,
  Grid,
  Stack,
  Button,
  Icon,
  Text,
  Title
} from '@ui8kit/core';
import { useTheme } from '@ui8kit/core';
Пример структуры:
// Обертка панели управления
<DashLayout
  sidebar={
    <Stack gap="md">
      <Button variant="ghost" fullWidth>
        <Icon lucideIcon={Home} /> Панель управления
      </Button>
      <Button variant="ghost" fullWidth>
        <Icon lucideIcon={Users} /> Пользователи
      </Button>
    </Stack>
  }
  navbarProps={{
    brand: "Административная панель",
    isDarkMode: isDark,
    toggleDarkMode: toggleDark
  }}
>
  {/* Сетка карточек статистики */}
  <LayoutBlock
    layout="grid"
    cols="1-2-4"
    gap="md"
    useContainer={false}
    content={{
      items: [
        { id: "1", title: "1,234", description: "Всего пользователей" },
        { id: "2", title: "$12,345", description: "Выручка" }
      ]
    }}
  />
  
  {/* Графики и таблицы */}
  <Grid cols="1-2" gap="lg">
    <Card p="lg">
      <Card.Header>
        <Title order={3}>Аналитика</Title>
      </Card.Header>
      <Card.Content>{/* Компонент графика */}</Card.Content>
    </Card>
  </Grid>
</DashLayout>
Источники: src/layouts/DashLayout.tsx1-99 src/layouts/LayoutBlock.tsx1-389

Веб-сайты

Многостраничные веб-сайты составляют секции LayoutBlock с различными режимами макета для разнообразных типов контента.
Структура веб-сайта

Block
Навигация заголовка

SplitBlock
Героическая секция О нас
splitSection=true

LayoutBlock
Сетка команды
layout='grid'
cols='1-2-3'

LayoutBlock
Таймлайн компании
layout='stack'

Block
Ссылки подвала
Диаграмма: Структура страницы веб-сайта, комбинирующая различные типы макетов. Паттерн импорта:
import { 
  Block,
  LayoutBlock,
  SplitBlock,
  Container,
  Stack,
  Grid,
  Card,
  Image,
  Text,
  Title
} from '@ui8kit/core';
Пример структуры:
// Героическая секция страницы О нас
<SplitBlock
  leftMedia={false}
  splitSection={true}
  mediaSection={<Image src="team.jpg" alt="Команда" />}
  contentSection={
    <Stack gap="lg" align="start">
      <Title order={1}>О нас</Title>
      <Text size="lg">История компании...</Text>
    </Stack>
  }
/>

// Сетка членов команды
<LayoutBlock
  layout="grid"
  cols="1-2-3"
  content={{
    title: "Наша команда",
    items: teamMembers.map(member => ({
      id: member.id,
      title: member.name,
      description: member.role,
      image: { src: member.photo, alt: member.name }
    }))
  }}
  contentHooks={defaultLayoutContentHooks.gridCards}
/>

// Таймлайн компании
<LayoutBlock
  layout="stack"
  content={{
    title: "Наш путь",
    items: milestones.map(milestone => ({
      id: milestone.year,
      title: milestone.year,
      description: milestone.event,
      lucideIcon: Calendar
    }))
  }}
/>
Источники: src/layouts/LayoutBlock.tsx1-389 src/layouts/SplitBlock.tsx1-145

Административные панели

Административные панели расширяют паттерн панели управления с макетами форм и интерфейсами управления данными.
Приложение административной панели

DashLayout

Navbar
Бренд администратора

Sidebar
Навигация администратора
Настройки, Пользователи, Контент

Основная область контента
Container

LayoutBlock
Форма настроек
layout='stack'
useContainer=false

Card
Таблица пользователей
Элементы управления пагинацией

Stack
Кнопки действий
Диаграмма: Структура административной панели с макетами форм и компонентами управления данными. Паттерн импорта:
import { 
  DashLayout,
  LayoutBlock,
  Card,
  Stack,
  Grid,
  Button,
  Text,
  Title,
  Icon,
  Badge
} from '@ui8kit/core';
Пример структуры:
<DashLayout
  sidebar={
    <Stack gap="md">
      <Text c="muted" size="xs">УПРАВЛЕНИЕ</Text>
      <Button variant="ghost" fullWidth>
        <Icon lucideIcon={Users} /> Пользователи
      </Button>
      <Button variant="ghost" fullWidth>
        <Icon lucideIcon={FileText} /> Контент
      </Button>
      <Text c="muted" size="xs">НАСТРОЙКИ</Text>
      <Button variant="ghost" fullWidth>
        <Icon lucideIcon={Settings} /> Конфигурация
      </Button>
    </Stack>
  }
  navbarProps={{
    brand: "Панель администратора"
  }}
>
  {/* Форма настроек */}
  <Card p="xl">
    <Card.Header>
      <Title order={2}>Настройки сайта</Title>
    </Card.Header>
    <Card.Content>
      <LayoutBlock
        layout="stack"
        gap="lg"
        useContainer={false}
        content={{
          items: settingsFields.map(field => ({
            id: field.name,
            title: field.label,
            description: field.description
          }))
        }}
        contentHooks={{
          item: (item) => (
            <Stack gap="xs">
              <Text weight="medium">{item.title}</Text>
              <input type="text" />
              <Text size="sm" c="muted">{item.description}</Text>
            </Stack>
          )
        }}
      />
    </Card.Content>
    <Card.Footer>
      <Grid cols="2" gap="md">
        <Button variant="secondary">Сбросить</Button>
        <Button variant="primary">Сохранить изменения</Button>
      </Grid>
    </Card.Footer>
  </Card>
</DashLayout>
Источники: src/layouts/DashLayout.tsx1-99 src/layouts/LayoutBlock.tsx1-389

Распространенные паттерны композиции

Конфигурации адаптивной сетки

Сценарий использованияЗначение ColsОписание
Сетка функций"1-2-3"1 колонка мобильная, 2 планшет, 3 десктоп
Карточки статистики"1-2-4"1 колонка мобильная, 2 планшет, 4 десктоп
Две колонки"1-2"1 колонка мобильная, 2 десктоп
Простая сетка"2"Всегда 2 колонки
Источники: src/layouts/LayoutBlock.tsx280-324

Композиция хуков контента

// Переопределение рендерера элемента по умолчанию
const customHooks = {
  ...defaultLayoutContentHooks.gridCards,
  item: (item, index) => (
    <Card variant="outlined" p="lg">
      <Stack gap="md">
        <Title order={3}>{item.title}</Title>
        <Text>{item.description}</Text>
        <Button variant="primary">Узнать больше</Button>
      </Stack>
    </Card>
  )
};

<LayoutBlock
  layout="grid"
  cols="1-2-3"
  content={content}
  contentHooks={customHooks}
/>
Источники: src/layouts/LayoutBlock.tsx388-389

Вложенная композиция макетов

// Внешний блок макета с вложенными разделенными блоками
<LayoutBlock
  layout="stack"
  gap="3xl"
  content={{
    title: "Витрина продуктов",
    items: products
  }}
  contentHooks={{
    item: (product) => (
      <SplitBlock
        leftMedia={product.id % 2 === 0}
        mediaSection={<Image src={product.image} />}
        contentSection={
          <Stack gap="md">
            <Title order={3}>{product.name}</Title>
            <Text>{product.description}</Text>
            <Button variant="primary">Подробности</Button>
          </Stack>
        }
      />
    )
  }}
/>
Источники: src/layouts/LayoutBlock.tsx256-383 src/layouts/SplitBlock.tsx67-137

Атрибуты данных для таргетинга

Компоненты макета включают атрибуты data-class и data-role для согласованного таргетинга DOM:
КомпонентАтрибутЗначениеЭлемент
DashLayoutdata-role"dash-main"Блок основного контента
Navbardata-role"dash-navbar"Элемент Nav
Sidebardata-role"dash-sidebar-stack"Контейнер Stack
LayoutBlockdata-class"layout-block"Элемент Section
LayoutBlockdata-class"layout-grid" / "layout-flex" / "layout-stack"Контейнер макета
SplitBlockdata-class"split-grid"Контейнер Grid
Источники: src/layouts/DashLayout.tsx18-48 src/layouts/LayoutBlock.tsx320-345 src/layouts/SplitBlock.tsx129