feat: AppLayout (оболочка приложения) — 0.8.0

This commit is contained in:
Тимур Городецкий
2026-08-17 10:30:00 +05:00
parent 6bf198f8f2
commit 4bdaca7b8f
5 changed files with 128 additions and 1 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@tnsu/ui-kit",
"version": "0.7.0",
"version": "0.8.0",
"description": "TNSU shared UI components for SPFx web parts",
"author": "Timur Gorodetskiy",
"license": "ISC",
+44
View File
@@ -0,0 +1,44 @@
@import '../tokens/tokens';
.layout {
display: flex;
align-items: stretch; // боковая панель тянется на высоту контента
width: 100%;
min-height: 78vh; // «присутствие» рейла даже когда контента мало
font-family: $font-sans;
}
.side {
flex: 0 0 auto;
display: flex;
}
.main {
flex: 1 1 auto;
min-width: 0; // чтобы контент не распирал колонку
display: flex;
flex-direction: column;
padding: $sp-5 $sp-6 $sp-8;
}
// Бургер: виден только на мобилке (над контентом).
.mobileBar {
display: none;
margin-bottom: $sp-4;
}
// < 768px: боковая панель превращается в drawer (position:fixed внутри Sidebar) —
// колонка .side схлопывается сама, контент на всю ширину. Показываем бургер.
@include below($bp-md) {
.layout {
min-height: 0;
}
.mobileBar {
display: block;
}
.main {
padding: $sp-4;
}
}
+50
View File
@@ -0,0 +1,50 @@
import * as React from 'react';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { useState } from 'react';
import { AppLayout } from './AppLayout';
import { Sidebar, ISidebarItem } from '../Sidebar/Sidebar';
import { SidebarToggle } from '../Sidebar/SidebarToggle';
const ITEMS: ISidebarItem[] = [
{ id: 'ct', title: 'СТ ОМТС', icon: 'clipboard', count: 1 },
{ id: 'oid', title: 'СТ ОИД', icon: 'chart' },
{ id: 'kps', title: 'СТ КПС', icon: 'building' }
];
const meta: Meta<typeof AppLayout> = {
title: 'Раскладка/AppLayout',
component: AppLayout,
tags: ['autodocs']
};
export default meta;
// Оболочка: сайдбар слева + контент справа. Сузь окно < 768px — сайдбар уйдёт в
// drawer, появится бургер, контент займёт всю ширину.
export const SidebarWithContent: StoryObj<typeof AppLayout> = {
render: () => {
const [active, setActive] = useState<string | number>('ct');
const [collapsed, setCollapsed] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
return (
<AppLayout
sidebar={
<Sidebar
items={ITEMS}
activeId={active}
onChange={setActive}
brand="Договора"
collapsed={collapsed}
onToggleCollapsed={() => setCollapsed((c) => !c)}
mobileOpen={mobileOpen}
onMobileClose={() => setMobileOpen(false)}
/>
}
mobileToggle={<SidebarToggle onClick={() => setMobileOpen(true)} />}
>
<h2 style={{ margin: 0, fontFamily: "'Lora', Georgia, serif", color: '#0F2440' }}>Контентная область</h2>
<p style={{ color: '#6B7280' }}>Здесь — содержимое активного раздела (реестр, форма и т.д.).</p>
</AppLayout>
);
}
};
+29
View File
@@ -0,0 +1,29 @@
import * as React from 'react';
import styles from './AppLayout.module.scss';
export interface IAppLayoutProps {
/** Боковая панель — обычно <Sidebar/>. */
sidebar: React.ReactNode;
/**
* Кнопка-бургер для мобильного drawer — обычно <SidebarToggle/>. Видна только
* на мобилке (< 768px), над контентом. Необязательна.
*/
mobileToggle?: React.ReactNode;
children: React.ReactNode;
}
/**
* Оболочка приложения — один из видов раскладки: боковая панель слева + контентная
* область справа. Переиспользуемый app-shell.
* Адаптивно: на < 768px боковая панель уходит в выезжающий drawer (за это отвечает
* сам Sidebar), контент занимает всю ширину, сверху показывается бургер (mobileToggle).
*/
export const AppLayout: React.FC<IAppLayoutProps> = ({ sidebar, mobileToggle, children }) => (
<div className={styles.layout}>
<div className={styles.side}>{sidebar}</div>
<main className={styles.main}>
{mobileToggle && <div className={styles.mobileBar}>{mobileToggle}</div>}
{children}
</main>
</div>
);
+4
View File
@@ -49,6 +49,10 @@ export { ConfirmHost } from './ConfirmDialog/ConfirmDialog';
export { confirmModal } from './ConfirmDialog/confirmModal';
export type { IConfirmOptions } from './ConfirmDialog/confirmModal';
// --- Раскладка приложения ---
export { AppLayout } from './AppLayout/AppLayout';
export type { IAppLayoutProps } from './AppLayout/AppLayout';
// --- Навигация ---
export { Tabs } from './Tabs/Tabs';
export type { ITab, ITabsProps } from './Tabs/Tabs';