feat: AppLayout (оболочка приложения) — 0.8.0
This commit is contained in:
+1
-1
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user