From 4bdaca7b8f9b324ed013b4b57fb838fb38ec4c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=93=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D1=86=D0=BA=D0=B8=D0=B9?= Date: Mon, 17 Aug 2026 10:30:00 +0500 Subject: [PATCH] =?UTF-8?q?feat:=20AppLayout=20(=D0=BE=D0=B1=D0=BE=D0=BB?= =?UTF-8?q?=D0=BE=D1=87=D0=BA=D0=B0=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F)=20=E2=80=94=200.8.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/AppLayout/AppLayout.module.scss | 44 +++++++++++++++++++++++++ src/AppLayout/AppLayout.stories.tsx | 50 +++++++++++++++++++++++++++++ src/AppLayout/AppLayout.tsx | 29 +++++++++++++++++ src/index.ts | 4 +++ 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/AppLayout/AppLayout.module.scss create mode 100644 src/AppLayout/AppLayout.stories.tsx create mode 100644 src/AppLayout/AppLayout.tsx diff --git a/package.json b/package.json index 3ec5917..25a23de 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/AppLayout/AppLayout.module.scss b/src/AppLayout/AppLayout.module.scss new file mode 100644 index 0000000..278e12d --- /dev/null +++ b/src/AppLayout/AppLayout.module.scss @@ -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; + } +} diff --git a/src/AppLayout/AppLayout.stories.tsx b/src/AppLayout/AppLayout.stories.tsx new file mode 100644 index 0000000..fd49c7e --- /dev/null +++ b/src/AppLayout/AppLayout.stories.tsx @@ -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 = { + title: 'Раскладка/AppLayout', + component: AppLayout, + tags: ['autodocs'] +}; + +export default meta; + +// Оболочка: сайдбар слева + контент справа. Сузь окно < 768px — сайдбар уйдёт в +// drawer, появится бургер, контент займёт всю ширину. +export const SidebarWithContent: StoryObj = { + render: () => { + const [active, setActive] = useState('ct'); + const [collapsed, setCollapsed] = useState(false); + const [mobileOpen, setMobileOpen] = useState(false); + return ( + setCollapsed((c) => !c)} + mobileOpen={mobileOpen} + onMobileClose={() => setMobileOpen(false)} + /> + } + mobileToggle={ setMobileOpen(true)} />} + > +

Контентная область

+

Здесь — содержимое активного раздела (реестр, форма и т.д.).

+
+ ); + } +}; diff --git a/src/AppLayout/AppLayout.tsx b/src/AppLayout/AppLayout.tsx new file mode 100644 index 0000000..686b53e --- /dev/null +++ b/src/AppLayout/AppLayout.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import styles from './AppLayout.module.scss'; + +export interface IAppLayoutProps { + /** Боковая панель — обычно . */ + sidebar: React.ReactNode; + /** + * Кнопка-бургер для мобильного drawer — обычно . Видна только + * на мобилке (< 768px), над контентом. Необязательна. + */ + mobileToggle?: React.ReactNode; + children: React.ReactNode; +} + +/** + * Оболочка приложения — один из видов раскладки: боковая панель слева + контентная + * область справа. Переиспользуемый app-shell. + * Адаптивно: на < 768px боковая панель уходит в выезжающий drawer (за это отвечает + * сам Sidebar), контент занимает всю ширину, сверху показывается бургер (mobileToggle). + */ +export const AppLayout: React.FC = ({ sidebar, mobileToggle, children }) => ( +
+
{sidebar}
+
+ {mobileToggle &&
{mobileToggle}
} + {children} +
+
+); diff --git a/src/index.ts b/src/index.ts index 3006261..2d61556 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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';