From 9f938f33265ac57217f20d7fe8b8f0fe9218b4e1 Mon Sep 17 00:00:00 2001 From: Sergey Yuferev Date: Thu, 21 Mar 2019 22:58:22 +0300 Subject: [PATCH] fix: review fixes, without menu item control integration --- packages/core/src/menu/MenuControl.tsx | 23 +++ packages/core/src/menu/MenuLevelBuilder.ts | 29 ++-- packages/core/src/menu/index.ts | 1 + .../src/horizontal-menu/HorizontalMenu.md | 6 +- .../src/horizontal-menu/HorizontalMenu.tsx | 71 +++------ packages/mobile/src/level-menu/LevelMenu.md | 6 +- packages/mobile/src/level-menu/LevelMenu.tsx | 52 ++++--- packages/mobile/src/select-menu/SelectMenu.md | 6 +- .../mobile/src/select-menu/SelectMenu.tsx | 138 ++++++------------ 9 files changed, 135 insertions(+), 197 deletions(-) create mode 100644 packages/core/src/menu/MenuControl.tsx diff --git a/packages/core/src/menu/MenuControl.tsx b/packages/core/src/menu/MenuControl.tsx new file mode 100644 index 000000000..91e57e037 --- /dev/null +++ b/packages/core/src/menu/MenuControl.tsx @@ -0,0 +1,23 @@ +import {PureComponent} from 'react' + +import RenderChild from '../RenderChild' +import {MenuLevel, BaseMenuItem, MenuLevelBuilder} from './MenuLevelBuilder' + +export interface MenuControlProps { + items: Item[] + selected: string + children: RenderChild<{ + levels: MenuLevel[] + }> +} + +export class MenuControl extends PureComponent< + MenuControlProps +> { + render() { + const {props} = this + return props.children({ + levels: new MenuLevelBuilder(props.items).levels(props.selected), + }) + } +} diff --git a/packages/core/src/menu/MenuLevelBuilder.ts b/packages/core/src/menu/MenuLevelBuilder.ts index 01da8ab0f..c81a4a96f 100644 --- a/packages/core/src/menu/MenuLevelBuilder.ts +++ b/packages/core/src/menu/MenuLevelBuilder.ts @@ -9,29 +9,30 @@ export type MenuTree = Item & { sub?: MenuTree[] } -export class MenuLevel { - constructor(public items: Item[], public activeId: string) {} -} +export type MenuLevel = { + items: Item[] + selected: string +} | undefined export class MenuLevelBuilder { constructor(protected items: MenuTree[]) {} protected buildLevels( - activeId: string, + selected: string, items: MenuTree[], - levels: (MenuLevel | undefined)[], + levels: MenuLevel[], depth = 0, parentActive = false ): boolean { if (levels.length <= depth) levels.push(undefined) let activeItem: MenuTree | undefined for (let item of items) { - const currentActive = item.id === activeId + const currentActive = item.id === selected const sub = item.sub const childActive = sub && sub.length > 0 ? this.buildLevels( - activeId, + selected, sub, levels, depth + 1, @@ -43,10 +44,10 @@ export class MenuLevelBuilder { } if (activeItem || parentActive) { - levels[depth] = new MenuLevel( + levels[depth] = { items, - activeItem ? activeItem.id : items[0].id - ) + selected: activeItem ? activeItem.id : items[0].id + } } return !!activeItem @@ -56,11 +57,11 @@ export class MenuLevelBuilder { * Split MenuTree structure to levels. * Returns array, where index is a menu level. * - * @param activeId current menu item id + * @param selected current menu item id */ - levels(activeId: string): (MenuLevel | undefined)[] { - const result: (MenuLevel[] | undefined) = [] - this.buildLevels(activeId, this.items, result) + levels(selected: string): MenuLevel[] { + const result: MenuLevel[] = [] + this.buildLevels(selected, this.items, result) return result } } diff --git a/packages/core/src/menu/index.ts b/packages/core/src/menu/index.ts index a8db89a5b..c05dea70d 100644 --- a/packages/core/src/menu/index.ts +++ b/packages/core/src/menu/index.ts @@ -1 +1,2 @@ +export * from './MenuControl' export * from './MenuLevelBuilder' diff --git a/packages/mobile/src/horizontal-menu/HorizontalMenu.md b/packages/mobile/src/horizontal-menu/HorizontalMenu.md index 532cf6887..2a0be62db 100644 --- a/packages/mobile/src/horizontal-menu/HorizontalMenu.md +++ b/packages/mobile/src/horizontal-menu/HorizontalMenu.md @@ -7,7 +7,7 @@ class HorizontalMenuDemo extends React.Component { constructor() { super() this.state = { - activeId: 'requests', + selected: 'requests', } } @@ -15,8 +15,8 @@ class HorizontalMenuDemo extends React.Component { return ( this.setState({activeId: item.id})} + selected={this.state.selected} + onSelect={item => this.setState({selected: item.id})} items={[ { title: 'Услуги', diff --git a/packages/mobile/src/horizontal-menu/HorizontalMenu.tsx b/packages/mobile/src/horizontal-menu/HorizontalMenu.tsx index ab1fb9bd4..387dc5fb4 100644 --- a/packages/mobile/src/horizontal-menu/HorizontalMenu.tsx +++ b/packages/mobile/src/horizontal-menu/HorizontalMenu.tsx @@ -1,38 +1,8 @@ import {BaseMenuItem, ButtonControl, styled} from '@qiwi/pijma-core' -import React, {ReactNode} from 'react' +import React from 'react' import {HorizontalMenuItem} from './HorizontalMenuItem' -export interface HorizontalMenuRenderItemProps { - /** - * Html item id - */ - id: string - item: Item - active: boolean - onClick: () => void -} - -const horizontalMenuRenderItemDefault = ({ - item, - active, - id, - onClick, -}: HorizontalMenuRenderItemProps): ReactNode => ( - ( - - )} - /> -) - // @see https://iamsteve.me/blog/entry/using-flexbox-for-horizontal-scrolling-navigation export const HorizontalMenuContainer = styled('nav')(({theme}) => ({ display: 'flex', @@ -59,33 +29,32 @@ export interface HorizontalMenuProps { /** * Active menu item id */ - activeId: string + selected: string items: Item[] - onClick: (item: Item) => void - renderItem: (props: HorizontalMenuRenderItemProps) => ReactNode + onSelect: (item: Item) => void } -// Do not use React.FC -// See https://github.com/sw-yx/react-typescript-cheatsheet#typing-defaultprops -export const HorizontalMenu = ({ +export const HorizontalMenu = ({ items, - renderItem, - onClick, - activeId, + onSelect, + selected, id, }: HorizontalMenuProps) => ( - renderItem({ - item, - id: `${id}-${item.id}`, - active: item.id === activeId, - onClick: onClick.bind(null, item), - }) - )} + children={items.map(item => ( + ( + + )} + /> + ))} /> ) -HorizontalMenu.defaultProps = { - renderItem: horizontalMenuRenderItemDefault, -} diff --git a/packages/mobile/src/level-menu/LevelMenu.md b/packages/mobile/src/level-menu/LevelMenu.md index 92d144213..f88556f0c 100644 --- a/packages/mobile/src/level-menu/LevelMenu.md +++ b/packages/mobile/src/level-menu/LevelMenu.md @@ -7,7 +7,7 @@ class LevelMenuDemo extends React.Component { constructor() { super() this.state = { - activeId: 't2', + selected: 't2', } } @@ -15,8 +15,8 @@ class LevelMenuDemo extends React.Component { return ( this.setState({activeId: item.id})} + selected={this.state.selected} + onSelect={item => this.setState({selected: item.id})} items={[ { title: 'Услуги', diff --git a/packages/mobile/src/level-menu/LevelMenu.tsx b/packages/mobile/src/level-menu/LevelMenu.tsx index 972470817..410b15de3 100644 --- a/packages/mobile/src/level-menu/LevelMenu.tsx +++ b/packages/mobile/src/level-menu/LevelMenu.tsx @@ -1,35 +1,33 @@ -import {BaseMenuItem, Box, MenuLevelBuilder} from '@qiwi/pijma-core' -import React, {Component} from 'react' +import {BaseMenuItem, Box, MenuControl} from '@qiwi/pijma-core' +import React, {Fragment} from 'react' import {HorizontalMenu} from '../horizontal-menu' import {SelectMenu} from '../select-menu' export interface LevelMenuProps { id: string - activeId: string + selected: string items: Item[] - onClick: (item: Item) => void + onSelect: (item: Item) => void } -export class LevelMenu extends Component< - LevelMenuProps -> { - render() { - const { - props: {id, onClick, activeId, items}, - } = this - const levels = new MenuLevelBuilder(items).levels(activeId) - const level0 = levels[0] - const level1 = levels[1] - const level2 = levels[2] - return ( -
+export const LevelMenu = ({ + id, + onSelect, + selected, + items, +}: LevelMenuProps) => ( + ( + {level0 && ( )} {level1 && ( @@ -37,8 +35,8 @@ export class LevelMenu extends Component< )} @@ -47,12 +45,12 @@ export class LevelMenu extends Component< )} -
- ) - } -} + + )} + /> +) diff --git a/packages/mobile/src/select-menu/SelectMenu.md b/packages/mobile/src/select-menu/SelectMenu.md index d3bb0ee23..de658b363 100644 --- a/packages/mobile/src/select-menu/SelectMenu.md +++ b/packages/mobile/src/select-menu/SelectMenu.md @@ -7,7 +7,7 @@ class SelectMenuDemo extends React.Component { constructor() { super() this.state = { - activeId: 'transactions', + selected: 'transactions', } } @@ -15,8 +15,8 @@ class SelectMenuDemo extends React.Component { return ( this.setState({activeId: item.id})} + selected={this.state.selected} + onSelect={item => this.setState({selected: item.id})} items={[ { title: 'Транзакции и отчеты', diff --git a/packages/mobile/src/select-menu/SelectMenu.tsx b/packages/mobile/src/select-menu/SelectMenu.tsx index 2e4f48975..cead2914f 100644 --- a/packages/mobile/src/select-menu/SelectMenu.tsx +++ b/packages/mobile/src/select-menu/SelectMenu.tsx @@ -5,76 +5,11 @@ import { Flex, FlexItem, Icon, - styled, } from '@qiwi/pijma-core' -import React, {Component, ReactNode} from 'react' +import React, {Component} from 'react' import {SelectMenuItem} from './SelectMenuItem' - -export interface SelectMenuRenderItemProps { - /** - * Html item id - */ - id: string - item: Item - onClick: () => void -} - -const selectMenuRenderItemDefault = ({ - item, - id, - onClick, -}: SelectMenuRenderItemProps): ReactNode => ( - ( - - )} - /> -) - -export const SelectMenuActiveItemContainer = styled(Flex)(({theme}) => ({ - fontWeight: theme.font.weight.normal, - fontFamily: theme.font.family, - fontSize: '16px', - cursor: 'pointer', - alignItems: 'center', - justifyContent: 'space-between', - padding: '8px 16px', - whiteSpace: 'nowrap', -})) - -export interface SelectMenuRenderActiveItemProps - extends SelectMenuRenderItemProps { - expanded: boolean -} - -const selectMenuRenderActiveItemDefault = ({ - item, - id, - onClick, - expanded, -}: SelectMenuRenderActiveItemProps) => ( - - - {item.title} - - - - - -) - -// Flex.withComponent('nav') not working -export const SelectMenuActiveItemsContainer = styled('nav')({ - flexDirection: 'column', - display: 'flex', -}) +import {Text} from '../typography' export interface SelectMenuProps { /** @@ -84,11 +19,9 @@ export interface SelectMenuProps { /** * Active menu item id */ - activeId: string + selected: string items: Item[] - onClick: (item: Item) => void - renderItem: (props: SelectMenuRenderItemProps) => ReactNode - renderActiveItem: (props: SelectMenuRenderActiveItemProps) => ReactNode + onSelect: (item: Item) => void } export interface SelectMenuState { @@ -98,54 +31,67 @@ export interface SelectMenuState { export class SelectMenu< Item extends BaseMenuItem = BaseMenuItem > extends Component, SelectMenuState> { - static defaultProps = { - renderItem: selectMenuRenderItemDefault, - renderActiveItem: selectMenuRenderActiveItemDefault, - } - state: SelectMenuState = { expanded: false, } protected toggle(item: Item) { this.setState({expanded: !this.state.expanded}) - this.props.onClick(item) + this.props.onSelect(item) } render() { const { toggle, state: {expanded}, - props: {items, renderItem, renderActiveItem, activeId, id}, + props: {items, selected, id}, } = this - const activeItem = items.find(item => item.id === activeId) || items[0] + const activeItem = items.find(item => item.id === selected) || items[0] return ( - {renderActiveItem({ - id: `${id}-active`, - item: activeItem, - expanded, - onClick: toggle.bind(this, activeItem), - })} + + + + + + + + {expanded && ( - - {items.map(item => - item.id === activeId - ? null - : renderItem({ - item, - id: `${id}-item-${item.id}`, - onClick: toggle.bind(this, item), - }) + + item.id === selected ? null : ( + ( + + )} + /> + ) )} - + /> )} )