From 17cfa362c4394aa8e72c67f1e9abea92c5102fe8 Mon Sep 17 00:00:00 2001 From: Angelina Gadzhieva Date: Fri, 9 Jun 2023 17:55:41 +0300 Subject: [PATCH] feat(Title): add component (#58) * feat(Title): add component * feat(Settings): use Title * fix: move uikit/icons pkg to devDeps * chore: reexport Title --- package-lock.json | 6 ++ package.json | 2 + src/components/Settings/README.md | 1 + src/components/Settings/Settings.scss | 8 +- src/components/Settings/Settings.tsx | 87 ++++++++++++------- .../Settings/__stories__/SettingsDemo.tsx | 23 ++++- .../__stories__/SettingsMobileDemo.tsx | 11 +-- src/components/Settings/collect-settings.ts | 2 +- src/components/Title/Title.scss | 22 +++++ src/components/Title/Title.tsx | 51 +++++++++++ src/components/Title/index.ts | 1 + src/components/index.ts | 1 + 12 files changed, 174 insertions(+), 41 deletions(-) create mode 100644 src/components/Title/Title.scss create mode 100644 src/components/Title/Title.tsx create mode 100644 src/components/Title/index.ts diff --git a/package-lock.json b/package-lock.json index 751efd4..00114ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2013,6 +2013,12 @@ "integrity": "sha512-KNYNhQjA9XqLo0RVEwNRqdA7/Lx5LLrNDtqWCvOGzXTwKU0GFNlWJaoSvk7u97apag23nTxgmpk551FlRCfehA==", "dev": true }, + "@gravity-ui/icons": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@gravity-ui/icons/-/icons-2.2.0.tgz", + "integrity": "sha512-CsYg+Dj08Yp1eGKux7c+sm7HSPJRDvDBjyOHnL6UR19QM+HCpdGiyrR2VVBvYadwm2eOeX4JNSn+vMPrqsmJdg==", + "dev": true + }, "@gravity-ui/prettier-config": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@gravity-ui/prettier-config/-/prettier-config-1.0.1.tgz", diff --git a/package.json b/package.json index 6220a94..927899e 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@commitlint/cli": "^17.0.0", "@commitlint/config-conventional": "^17.0.0", "@gravity-ui/eslint-config": "^1.0.0", + "@gravity-ui/icons": "^2.2.0", "@gravity-ui/prettier-config": "^1.0.0", "@gravity-ui/stylelint-config": "^1.0.0", "@gravity-ui/tsconfig": "^1.0.0", @@ -84,6 +85,7 @@ }, "peerDependencies": { "@gravity-ui/uikit": "^4.1.0", + "@gravity-ui/icons": "^2.2.0", "react": "^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" }, diff --git a/src/components/Settings/README.md b/src/components/Settings/README.md index 7298b91..0c9261a 100644 --- a/src/components/Settings/README.md +++ b/src/components/Settings/README.md @@ -18,6 +18,7 @@ The components provides layouting functionality of settings panel with the follo | initialPage | string | | | Inititial page in `/groupId/pageId` format | | view | 'normal', 'mobile' | | 'normal' | Change view for Mobile | | onPageChange | Function | | | Page change handler | +| onClose | Function | | | Settings close handler | #### Settings.Group diff --git a/src/components/Settings/Settings.scss b/src/components/Settings/Settings.scss index eed5541..3228855 100644 --- a/src/components/Settings/Settings.scss +++ b/src/components/Settings/Settings.scss @@ -37,7 +37,6 @@ $block: '.#{variables.$ns}settings'; } #{$block}__page { - padding: 20px #{$content-padding}; overflow-y: visible; } @@ -132,14 +131,17 @@ $block: '.#{variables.$ns}settings'; } &__search { - margin: 12px 20px 16px; + margin: 0 20px 16px; } &__page { - padding: 20px; overflow-y: auto; } + &__content { + padding: 20px; + } + &__section { &-heading { @include text-subheader-2; diff --git a/src/components/Settings/Settings.tsx b/src/components/Settings/Settings.tsx index c1a8c3a..5fc0f37 100644 --- a/src/components/Settings/Settings.tsx +++ b/src/components/Settings/Settings.tsx @@ -6,7 +6,9 @@ import {IconProps, Loader} from '@gravity-ui/uikit'; import {SettingsSearch} from './SettingsSearch/SettingsSearch'; import {SettingsMenu, SettingsMenuInstance} from './SettingsMenu/SettingsMenu'; import {SettingsMenuMobile} from './SettingsMenuMobile/SettingsMenuMobile'; +import {Title} from '../Title'; +import type {SettingsMenu as SettingsMenuType} from './collect-settings'; import {getSettingsFromChildren} from './collect-settings'; import {escapeStringForRegExp} from './helpers'; @@ -23,6 +25,7 @@ interface SettingsProps { loading?: boolean; dict?: SettingsDict; view?: 'normal' | 'mobile'; + onClose?: () => void; } type SettingsDict = Record; type SettingsDictKeys = 'heading_settings' | 'placeholder_search' | 'not_found'; @@ -90,6 +93,17 @@ export function Settings({ ); } +const getPageTitleById = (menu: SettingsMenuType, activePage: string) => { + for (const firstLevel of menu) { + if ('groupTitle' in firstLevel) { + for (const secondLevel of firstLevel.items) + if (secondLevel.id === activePage) return secondLevel.title; + } else if (firstLevel.id === activePage) return firstLevel.title; + } + + return ''; +}; + Settings.defaultProps = { dict: defaultDict, }; @@ -97,11 +111,12 @@ Settings.defaultProps = { type SettingsContentProps = Omit; function SettingsContent({ initialPage, - onPageChange, children, renderNotFound, dict, view, + onPageChange, + onClose, }: SettingsContentProps) { const [search, setSearch] = React.useState(''); const {menu, pages} = getSettingsFromChildren(children, search); @@ -156,34 +171,48 @@ function SettingsContent({ ); } - return pages[activePage].sections - .filter((section) => !section.hidden) - .map((section) => ( -
- {section.showTitle ? ( -

{section.title}

- ) : null} - - {section.header ? ( - isMobile ? ( -
{section.header}
- ) : ( - section.header - ) - ) : null} - - {section.items.map(({hidden, title, element}) => - hidden ? null : ( -
- {React.cloneElement(element, { - ...element.props, - title: search && title ? prepareTitle(title, search) : title, - })} -
- ), - )} + const filteredSections = pages[activePage].sections.filter((section) => !section.hidden); + + return ( + <> + {!isMobile && ( + + {getPageTitleById(menu, activePage)} + + )} + +
+ {filteredSections.map((section) => ( +
+ {section.showTitle && ( +

{section.title}

+ )} + + {section.header && + (isMobile ? ( +
{section.header}
+ ) : ( + section.header + ))} + + {section.items.map(({hidden, title, element}) => + hidden ? null : ( +
+ {React.cloneElement(element, { + ...element.props, + title: + search && title + ? prepareTitle(title, search) + : title, + })} +
+ ), + )} +
+ ))}
- )); + + ); }; return ( @@ -220,7 +249,7 @@ function SettingsContent({ } }} > -

{dict?.heading_settings}

+ {dict?.heading_settings} { + ({ + initialPage, + withBadge, + onClose, + }: { + initialPage?: string; + withBadge?: boolean; + onClose: () => void; + }) => { const [settings, dispatch] = useReducer(reducer, defaultSettings); const handleChange = (name: string, value: any) => { dispatch(setSetting(name, value)); @@ -57,6 +65,7 @@ export const SettingsComponent = React.memo( onPageChange={(page) => { console.log({page}); }} + onClose={onClose} > @@ -145,6 +154,14 @@ export const SettingsComponent = React.memo( }} /> + {onClose && ( + + + + )} @@ -162,7 +179,7 @@ export function SettingsDemo() {

Settings

- + alert('Close settings')} />
); } diff --git a/src/components/Settings/__stories__/SettingsMobileDemo.tsx b/src/components/Settings/__stories__/SettingsMobileDemo.tsx index d6dbd25..9ca9bbf 100644 --- a/src/components/Settings/__stories__/SettingsMobileDemo.tsx +++ b/src/components/Settings/__stories__/SettingsMobileDemo.tsx @@ -63,11 +63,11 @@ export const SettingsMobileComponent = React.memo( ({ initialPage, withBadge, - closeSettings, + onClose, }: { initialPage?: string; withBadge?: boolean; - closeSettings?: () => void; + onClose?: () => void; }) => { const [settings, dispatch] = useReducer(reducer, defaultSettings); const handleChange = (name: string, value: any) => { @@ -80,6 +80,7 @@ export const SettingsMobileComponent = React.memo( onPageChange={(page) => { console.log({page}); }} + onClose={onClose} > @@ -132,9 +133,9 @@ export const SettingsMobileComponent = React.memo( size="l" /> - {closeSettings && ( + {onClose && ( - @@ -192,7 +193,7 @@ SettingsMobileComponent.displayName = 'SettingsMobileComponent'; export function SettingsMobileDemo() { return (
- + alert('Close settings')} />
); } diff --git a/src/components/Settings/collect-settings.ts b/src/components/Settings/collect-settings.ts index 1ec6a08..b49ce2e 100644 --- a/src/components/Settings/collect-settings.ts +++ b/src/components/Settings/collect-settings.ts @@ -2,7 +2,7 @@ import React from 'react'; import {IconProps} from '@gravity-ui/uikit'; import {escapeStringForRegExp, invariant} from './helpers'; -type SettingsMenu = (SettingsMenuGroup | SettingsMenuItem)[]; +export type SettingsMenu = (SettingsMenuGroup | SettingsMenuItem)[]; interface SettingsMenuGroup { groupTitle: string; diff --git a/src/components/Title/Title.scss b/src/components/Title/Title.scss new file mode 100644 index 0000000..f8c6add --- /dev/null +++ b/src/components/Title/Title.scss @@ -0,0 +1,22 @@ +@use '../variables'; +@import '../../../styles/mixins'; + +$block: '.#{variables.$ns}title'; + +#{$block} { + box-sizing: border-box; + padding: 14px 10px 14px 20px; + min-height: 64px; + display: flex; + justify-content: space-between; + align-items: center; + + &_separator { + border-bottom: 1px solid var(--yc-color-line-generic); + } + + &__text { + margin: 0; + margin-right: 20px; + } +} diff --git a/src/components/Title/Title.tsx b/src/components/Title/Title.tsx new file mode 100644 index 0000000..54fe3cc --- /dev/null +++ b/src/components/Title/Title.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import {Button, Icon, Text} from '@gravity-ui/uikit'; +import {Xmark} from '@gravity-ui/icons'; +import {block} from '../utils/cn'; + +import './Title.scss'; + +const b = block('title'); + +type TitleDictKeys = 'close'; +type TitleDict = Record; + +const defaultDict: TitleDict = { + close: 'Close', +}; + +interface TitleProps { + hasSeparator?: boolean; + dict?: TitleDict; + closeIconSize?: number; + onClose?: () => void; +} +export const Title: React.FC> = ({ + children, + closeIconSize = 23, + hasSeparator, + dict = defaultDict, + onClose, +}) => { + return ( +
+ + {children} + + {onClose && ( + + )} +
+ ); +}; + +Title.displayName = 'Title'; diff --git a/src/components/Title/index.ts b/src/components/Title/index.ts new file mode 100644 index 0000000..304b1af --- /dev/null +++ b/src/components/Title/index.ts @@ -0,0 +1 @@ +export * from './Title'; diff --git a/src/components/index.ts b/src/components/index.ts index 02dfd92..8eda775 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -2,6 +2,7 @@ export {ActionBar} from './ActionBar'; export {AsideHeader, AsideHeaderProps} from './AsideHeader/AsideHeader'; export {Drawer, DrawerProps, DrawerItemProps, DrawerItem} from './Drawer/Drawer'; export {FooterItem, FooterItemProps} from './FooterItem/FooterItem'; +export * from './Title'; export * from './HotkeysPanel'; export * from './Settings'; export * from './types';