diff --git a/src/harmony/NavigationSidebar/Navigation.tsx b/src/harmony/NavigationSidebar/Navigation.tsx new file mode 100644 index 00000000..445504ed --- /dev/null +++ b/src/harmony/NavigationSidebar/Navigation.tsx @@ -0,0 +1,10 @@ +import React, { FC, HTMLAttributes } from 'react'; +import cn from 'classnames'; + +import s from './NavigationSidebar.module.css'; + +export const Navigation: FC> = ({ children, className, ...rest }) => ( +
+ {children} +
+); diff --git a/src/harmony/NavigationSidebar/NavigationItem.tsx b/src/harmony/NavigationSidebar/NavigationItem.tsx new file mode 100644 index 00000000..7f11349a --- /dev/null +++ b/src/harmony/NavigationSidebar/NavigationItem.tsx @@ -0,0 +1,33 @@ +import React, { FC, HTMLAttributes } from 'react'; +import cn from 'classnames'; + +import { ListViewItem } from '../ListView/ListView'; +import { Link } from '../Link/Link'; +import { MenuItem } from '../MenuItem/MenuItem'; + +import s from './NavigationSidebar.module.css'; + +interface NavigationItemProps extends HTMLAttributes { + href: string; + selected?: boolean; +} + +export const NavigationItem: FC = ({ selected, href, children, ...rest }) => ( + ( + + + {children} + + + )} + /> +); diff --git a/src/harmony/NavigationSidebar/NavigationSection.tsx b/src/harmony/NavigationSidebar/NavigationSection.tsx new file mode 100644 index 00000000..c7613bd2 --- /dev/null +++ b/src/harmony/NavigationSidebar/NavigationSection.tsx @@ -0,0 +1,41 @@ +import React, { FC, HTMLAttributes, ReactNode, useState } from 'react'; +import cn from 'classnames'; +import { IconDownSmallOutline, IconUpSmallOutline } from '@taskany/icons'; + +import { nullable } from '../../utils'; +import { Text } from '../Text/Text'; +import { ListView } from '../ListView/ListView'; + +import s from './NavigationSidebar.module.css'; + +interface NavigationSectionProps extends Omit, 'title'> { + title: ReactNode; + collapsed?: boolean; +} + +export const NavigationSection: FC = ({ + collapsed: defaultState = false, + title, + children, + className, + ...rest +}) => { + const [collapsed, setCollapsed] = useState(defaultState); + return ( +
+
setCollapsed((val) => !val)}> + {title} + {nullable( + collapsed, + () => ( + + ), + , + )} +
+ {nullable(!collapsed, () => ( + {children} + ))} +
+ ); +}; diff --git a/src/harmony/NavigationSidebar/NavigationSidebar.module.css b/src/harmony/NavigationSidebar/NavigationSidebar.module.css new file mode 100644 index 00000000..d80cb4f2 --- /dev/null +++ b/src/harmony/NavigationSidebar/NavigationSidebar.module.css @@ -0,0 +1,68 @@ +.NavigationSidebarContainer { + display: flex; + flex-direction: column; + height: 100%; + background: var(--layer); + border-right: 1px solid var(--layer-border); +} + +.NavigationSidebarHeader { + display: flex; + padding: var(--gap-sm); + border-bottom: 1px solid var(--gray-900); + align-items: center; + gap: var(--gap-s); +} + +.NavigationSidebarLogo{ + display: flex; +} + +.NavigationSidebarTitle { + flex: 1; +} + +.NavigationSidebarContent { + display: flex; + flex-direction: column; + padding: var(--gap-sm); + gap: var(--gap-sm); + flex: 1; + overflow: auto; +} + +.Navigation { + display: flex; + flex-direction: column; + gap: var(--gap-s); + flex: 1; + overflow: auto; + padding-right: var(--gap-sm); + margin-right: calc(var(--gap-sm)* -1); +} + +.NavigationSection { + padding-bottom: var(--gap-s); + border-bottom: 1px solid var(--gray-900); +} + +.NavigationSection:last-child { + border: 0; +} + +.NavigationSectionTitle { + display: flex; + align-items: center; + padding: var(--gap-s) 0; + color: var(--text-secondary); + cursor: pointer; + user-select: none; +} + +.NavigationItemLink:hover { + color: inherit; +} + +.MenuItem_active { + background-color: var(--layer-above); +} \ No newline at end of file diff --git a/src/harmony/NavigationSidebar/NavigationSidebar.stories.tsx b/src/harmony/NavigationSidebar/NavigationSidebar.stories.tsx new file mode 100644 index 00000000..7c148aa9 --- /dev/null +++ b/src/harmony/NavigationSidebar/NavigationSidebar.stories.tsx @@ -0,0 +1,65 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { IconBellOutline } from '@taskany/icons'; + +import { Link } from '../Link/Link'; +import { TaskanyLogo } from '../TaskanyLogo/TaskanyLogo'; + +import { NavigationSidebar } from './NavigationSidebar'; +import { Navigation } from './Navigation'; +import { NavigationItem } from './NavigationItem'; +import { NavigationSection } from './NavigationSection'; +import { NavigationSidebarContent } from './NavigationSidebarContent'; +import { NavigationSidebarHeader } from './NavigationSidebarHeader'; +import { NavigationSidebarTitle } from './NavigationSidebarTitle'; + +const meta: Meta = { + title: '@harmony/NavigationSidebar', + component: NavigationSidebar, +}; + +export default meta; + +const routes = new Array(3).fill(0).map((item, index) => ({ title: `item ${index + 1}`, href: '#' })); + +export const Default: StoryObj = { + render: () => { + return ( + + + + + + Title + + + + + + {routes.map(({ title, href }) => ( + + {title} + + ))} + + + + {routes.map(({ title, href }) => ( + + {title} + + ))} + + + + {routes.map(({ title, href }) => ( + + {title} + + ))} + + + + + ); + }, +}; diff --git a/src/harmony/NavigationSidebar/NavigationSidebar.tsx b/src/harmony/NavigationSidebar/NavigationSidebar.tsx new file mode 100644 index 00000000..5cd362ad --- /dev/null +++ b/src/harmony/NavigationSidebar/NavigationSidebar.tsx @@ -0,0 +1,12 @@ +import React, { FC, HTMLAttributes } from 'react'; +import cn from 'classnames'; + +import s from './NavigationSidebar.module.css'; + +export const NavigationSidebar: FC> = ({ children, className, ...rest }) => { + return ( +
+ {children} +
+ ); +}; diff --git a/src/harmony/NavigationSidebar/NavigationSidebarContent.tsx b/src/harmony/NavigationSidebar/NavigationSidebarContent.tsx new file mode 100644 index 00000000..f6395897 --- /dev/null +++ b/src/harmony/NavigationSidebar/NavigationSidebarContent.tsx @@ -0,0 +1,10 @@ +import React, { FC, HTMLAttributes } from 'react'; +import cn from 'classnames'; + +import s from './NavigationSidebar.module.css'; + +export const NavigationSidebarContent: FC> = ({ children, className, ...rest }) => ( +
+ {children} +
+); diff --git a/src/harmony/NavigationSidebar/NavigationSidebarHeader.tsx b/src/harmony/NavigationSidebar/NavigationSidebarHeader.tsx new file mode 100644 index 00000000..15dcfd0c --- /dev/null +++ b/src/harmony/NavigationSidebar/NavigationSidebarHeader.tsx @@ -0,0 +1,10 @@ +import React, { FC, HTMLAttributes } from 'react'; +import cn from 'classnames'; + +import s from './NavigationSidebar.module.css'; + +export const NavigationSidebarHeader: FC> = ({ children, className, ...rest }) => ( +
+ {children} +
+); diff --git a/src/harmony/NavigationSidebar/NavigationSidebarTitle.tsx b/src/harmony/NavigationSidebar/NavigationSidebarTitle.tsx new file mode 100644 index 00000000..10fd38e8 --- /dev/null +++ b/src/harmony/NavigationSidebar/NavigationSidebarTitle.tsx @@ -0,0 +1,12 @@ +import React, { ComponentProps, FC } from 'react'; +import cn from 'classnames'; + +import { Text } from '../Text/Text'; + +import s from './NavigationSidebar.module.css'; + +export const NavigationSidebarTitle: FC> = ({ children, className, ...rest }) => ( + + {children} + +); diff --git a/src/harmony/index.ts b/src/harmony/index.ts index 66d3ebd7..48450aa2 100644 --- a/src/harmony/index.ts +++ b/src/harmony/index.ts @@ -28,6 +28,13 @@ export * from './ListView/ListView'; export * from './MenuItem/MenuItem'; export * from './Modal/Modal'; export * from './ModalPreview/ModalPreview'; +export * from './NavigationSidebar/Navigation'; +export * from './NavigationSidebar/NavigationItem'; +export * from './NavigationSidebar/NavigationSection'; +export * from './NavigationSidebar/NavigationSidebar'; +export * from './NavigationSidebar/NavigationSidebarContent'; +export * from './NavigationSidebar/NavigationSidebarHeader'; +export * from './NavigationSidebar/NavigationSidebarTitle'; export * from './OfflineBanner/OfflineBanner'; export * from './PageLoadProgress/PageLoadProgress'; export * from './Popup/Popup';