diff --git a/package.json b/package.json index d527303..3ec5917 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tnsu/ui-kit", - "version": "0.6.0", + "version": "0.7.0", "description": "TNSU shared UI components for SPFx web parts", "author": "Timur Gorodetskiy", "license": "ISC", diff --git a/src/Tabs/Tabs.module.scss b/src/Tabs/Tabs.module.scss new file mode 100644 index 0000000..3eb3f7a --- /dev/null +++ b/src/Tabs/Tabs.module.scss @@ -0,0 +1,44 @@ +@import '../tokens/tokens'; + +// Светлая полоса вкладок; активная — navy с золотым подчёркиванием. +// На узких экранах — горизонтальная прокрутка (не ломается, не переносится «лесенкой»). +.tabs { + display: flex; + align-items: stretch; + gap: $sp-2; + border-bottom: 1px solid $gray-200; + overflow-x: auto; +} + +.tab { + display: inline-flex; + align-items: center; + gap: $sp-2; + padding: 12px 16px; + border: 0; + border-bottom: 2px solid transparent; + background: transparent; + color: $gray-500; + font-family: $font-sans; + font-size: 13px; + font-weight: 500; + letter-spacing: .2px; + white-space: nowrap; + cursor: pointer; + transition: color .18s, border-color .18s; + + &:hover { + color: $navy-800; + } +} + +.active { + color: $navy-900; + font-weight: 600; + border-bottom-color: $gold-500; +} + +.count { + color: $gray-400; + font-weight: 500; +} diff --git a/src/Tabs/Tabs.stories.tsx b/src/Tabs/Tabs.stories.tsx new file mode 100644 index 0000000..a64ea44 --- /dev/null +++ b/src/Tabs/Tabs.stories.tsx @@ -0,0 +1,44 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { useState } from 'react'; +import { Tabs, ITab } from './Tabs'; + +const QUEUE: ITab[] = [ + { id: 'pending', label: 'На согласовании у меня', count: 1 }, + { id: 'mine', label: 'Инициализированные мной', count: 71 }, + { id: 'approved', label: 'Согласованные мной', count: 4 } +]; + +const meta: Meta = { + title: 'Навигация/Tabs', + component: Tabs, + tags: ['autodocs'] +}; + +export default meta; +type Story = StoryObj; + +// Вкладки-очереди реестра (как в дашборде myDocuments). +export const Queues: Story = { + render: () => { + const [active, setActive] = useState('pending'); + return ; + } +}; + +// Вкладки категорий с иконками. +export const WithIcons: Story = { + render: () => { + const [active, setActive] = useState('rec'); + return ( + + ); + } +}; diff --git a/src/Tabs/Tabs.tsx b/src/Tabs/Tabs.tsx new file mode 100644 index 0000000..c4a9002 --- /dev/null +++ b/src/Tabs/Tabs.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import styles from './Tabs.module.scss'; +import { Icon } from '../Icon/Icon'; +import { IconName } from '../Icon/iconNames'; + +export interface ITab { + id: string | number; + label: React.ReactNode; + /** Счётчик после подписи, напр. «(71)». undefined/0 — не показывать. */ + count?: number; + /** Иконка слева (необязательно). */ + icon?: IconName; +} + +export interface ITabsProps { + tabs: ITab[]; + activeId: string | number; + onChange: (id: string | number) => void; +} + +/** + * Горизонтальные вкладки: активная — navy с золотым подчёркиванием. Управляемый. + * На узких экранах прокручивается вбок (не ломается). Подходит и для очередей со + * счётчиками (реестр), и для категорий с иконками. + */ +export const Tabs: React.FC = ({ tabs, activeId, onChange }) => ( + +); diff --git a/src/index.ts b/src/index.ts index 325e897..3006261 100644 --- a/src/index.ts +++ b/src/index.ts @@ -50,6 +50,8 @@ export { confirmModal } from './ConfirmDialog/confirmModal'; export type { IConfirmOptions } from './ConfirmDialog/confirmModal'; // --- Навигация --- +export { Tabs } from './Tabs/Tabs'; +export type { ITab, ITabsProps } from './Tabs/Tabs'; export { Stepper, highestReachableIndex } from './Stepper/Stepper'; export type { IStepperStep, IStepperProps } from './Stepper/Stepper'; export { Sidebar } from './Sidebar/Sidebar';