From edb1633a592e9282e5ea5fd45359df28eefe74ad Mon Sep 17 00:00:00 2001 From: devneko Date: Wed, 3 Jul 2019 13:39:55 +0900 Subject: [PATCH] feat: support separating header click --- __tests__/Container.test.js | 9 ++++- example/Header.js | 5 ++- example/app.js | 11 +++++- src/components/Decorators/Container.js | 21 ++++++---- src/components/Decorators/Header.js | 7 ++-- src/components/Decorators/Toggle.js | 14 +++++-- src/components/NodeHeader.js | 8 ++-- src/components/TreeNode/index.js | 55 +++++++++++++++++++++----- src/components/common/index.js | 2 +- src/components/index.js | 8 ++-- 10 files changed, 104 insertions(+), 36 deletions(-) diff --git a/__tests__/Container.test.js b/__tests__/Container.test.js index 0a0231a..157c441 100644 --- a/__tests__/Container.test.js +++ b/__tests__/Container.test.js @@ -14,6 +14,9 @@ const renderComponent = (props = {}) => { {...{decorators, onClick, animations}} node={data} style={style.tree.node} + onClick={onClick} + onClickToggle={onClick} + onClickHeader={onClick} {...props} />); wrapper.Toggle = () => wrapper.find('Toggle'); @@ -28,7 +31,11 @@ describe('', () => { expect( wrapper .children() - .contains() + .contains() ).toBe(true); }); }); diff --git a/example/Header.js b/example/Header.js index 2331449..c9de364 100644 --- a/example/Header.js +++ b/example/Header.js @@ -4,13 +4,13 @@ import PropTypes from 'prop-types'; import {Div} from '../src/components/common'; // Example: Customising The Header Decorator To Include Icons -const Header = ({style, node}) => { +const Header = ({style, node, onClick}) => { const iconType = node.children ? 'folder' : 'file-text'; const iconClass = `fa fa-${iconType}`; const iconStyle = {marginRight: '5px'}; return ( -
+
onClick()}>
@@ -23,6 +23,7 @@ const Header = ({style, node}) => { Header.propTypes = { node: PropTypes.object, style: PropTypes.object, + onClick: PropTypes.func, }; export default Header; diff --git a/example/app.js b/example/app.js index 725a382..79f3745 100644 --- a/example/app.js +++ b/example/app.js @@ -14,10 +14,11 @@ class DemoTree extends PureComponent { super(props); this.state = {data}; this.onToggle = this.onToggle.bind(this); + this.onClickHeader = this.onClickHeader.bind(this); } onToggle(node, toggled) { - const {cursor, data} = this.state; + const {data, cursor} = this.state; if (cursor) { this.setState(() => ({cursor, active: false})); @@ -28,7 +29,11 @@ class DemoTree extends PureComponent { node.toggled = toggled; } - this.setState(() => ({cursor: node, data: Object.assign({}, data)})); + this.setState(() => ({data: Object.assign({}, data)})); + } + + onClickHeader(node) { + this.setState(() => ({cursor: node})); } onFilterMouseUp({target: {value}}) { @@ -62,7 +67,9 @@ class DemoTree extends PureComponent {
diff --git a/src/components/Decorators/Container.js b/src/components/Decorators/Container.js index 88d21ae..6c054c7 100644 --- a/src/components/Decorators/Container.js +++ b/src/components/Decorators/Container.js @@ -21,19 +21,22 @@ class Container extends PureComponent { } renderToggleDecorator() { - const {style, decorators} = this.props; - return ; + const {style, decorators, onClickToggle} = this.props; + return onClickToggle() : null}/>; } render() { - const {style, decorators, terminal, onClick, node} = this.props; + const {style, decorators, terminal, onClick, onClickHeader, node} = this.props; return (
onClick() : null} style={node.active ? {...style.container} : {...style.link}} > {!terminal ? this.renderToggle() : null} - +
); } @@ -43,12 +46,14 @@ Container.propTypes = { style: PropTypes.object.isRequired, decorators: PropTypes.object.isRequired, terminal: PropTypes.bool.isRequired, - onClick: PropTypes.func.isRequired, + onClick: PropTypes.func, + onClickToggle: PropTypes.func, + onClickHeader: PropTypes.func, animations: PropTypes.oneOfType([ PropTypes.object, - PropTypes.bool + PropTypes.bool, ]).isRequired, - node: PropTypes.object.isRequired + node: PropTypes.object.isRequired, }; export default Container; diff --git a/src/components/Decorators/Header.js b/src/components/Decorators/Header.js index 5f9c1fd..b3668ef 100644 --- a/src/components/Decorators/Header.js +++ b/src/components/Decorators/Header.js @@ -3,8 +3,8 @@ import PropTypes from 'prop-types'; import {Div} from '../common'; -const Header = ({node, style}) => ( -
+const Header = ({node, style, onClick}) => ( +
onClick() : null}>
{node.name}
@@ -13,7 +13,8 @@ const Header = ({node, style}) => ( Header.propTypes = { style: PropTypes.object, - node: PropTypes.object.isRequired + node: PropTypes.object.isRequired, + onClick: PropTypes.func, }; export default Header; diff --git a/src/components/Decorators/Toggle.js b/src/components/Decorators/Toggle.js index 2873dd6..fbce403 100644 --- a/src/components/Decorators/Toggle.js +++ b/src/components/Decorators/Toggle.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useCallback} from 'react'; import PropTypes from 'prop-types'; import styled from '@emotion/styled'; @@ -8,13 +8,18 @@ const Polygon = styled('polygon', { shouldForwardProp: prop => ['className', 'children', 'points'].indexOf(prop) !== -1 })((({style}) => style)); -const Toggle = ({style}) => { +const Toggle = ({onClick, style}) => { const {height, width} = style; const midHeight = height * 0.5; const points = `0,0 0,${height} ${width},${midHeight}`; + const click = useCallback(() => { + if (onClick) { + onClick(); + } + }, [onClick]); return ( -
+
@@ -25,7 +30,8 @@ const Toggle = ({style}) => { }; Toggle.propTypes = { - style: PropTypes.object + style: PropTypes.object, + onClick: PropTypes.func, }; export default Toggle; diff --git a/src/components/NodeHeader.js b/src/components/NodeHeader.js index f90d8a2..c0aba78 100644 --- a/src/components/NodeHeader.js +++ b/src/components/NodeHeader.js @@ -24,7 +24,7 @@ class NodeHeader extends Component { } render() { - const {animations, decorators, node, onClick, style} = this.props; + const {animations, decorators, node, onClick, onClickToggle, onClickHeader, style} = this.props; const {active, children} = node; const terminal = !children; let styles; @@ -35,7 +35,7 @@ class NodeHeader extends Component { } return ( ); @@ -50,7 +50,9 @@ NodeHeader.propTypes = { PropTypes.bool ]).isRequired, node: PropTypes.object.isRequired, - onClick: PropTypes.func + onClick: PropTypes.func, + onClickToggle: PropTypes.func, + onClickHeader: PropTypes.func, }; export default NodeHeader; diff --git a/src/components/TreeNode/index.js b/src/components/TreeNode/index.js index 32712af..2c37aa1 100644 --- a/src/components/TreeNode/index.js +++ b/src/components/TreeNode/index.js @@ -11,10 +11,18 @@ import Drawer from './Drawer'; import Loading from './Loading'; const Li = styled('li', { - shouldForwardProp: prop => ['className', 'children', 'ref'].indexOf(prop) !== -1 + shouldForwardProp: prop => ['className', 'children', 'ref'].indexOf(prop) !== -1, })(({style}) => style); class TreeNode extends PureComponent { + + constructor() { + super(); + this.onClick = this.onClick.bind(this); + this.onClickToggle = this.onClickToggle.bind(this); + this.onClickHeader = this.onClickHeader.bind(this); + } + onClick() { const {node, onToggle} = this.props; const {toggled} = node; @@ -23,17 +31,32 @@ class TreeNode extends PureComponent { } } + onClickToggle() { + const {node, onToggle} = this.props; + if (onToggle) { + const {toggled} = node; + onToggle(node, !toggled); + } + } + + onClickHeader() { + const {node, onClickHeader} = this.props; + if (onClickHeader) { + onClickHeader(node); + } + } + animations() { const {animations, node} = this.props; if (!animations) { return { - toggle: defaultAnimations.toggle(this.props, 0) + toggle: defaultAnimations.toggle(this.props, 0), }; } const animation = Object.assign({}, animations, node.animations); return { toggle: animation.toggle(this.props), - drawer: animation.drawer(this.props) + drawer: animation.drawer(this.props), }; } @@ -45,7 +68,14 @@ class TreeNode extends PureComponent { } renderChildren(decorators) { - const {animations, decorators: propDecorators, node, style, onToggle} = this.props; + const { + animations, decorators: propDecorators, + node, + style, + separateToggleEvent, + onToggle, + onClickHeader, + } = this.props; if (node.loading) { return ( @@ -62,7 +92,7 @@ class TreeNode extends PureComponent {
    {children.map(child => ( - this.onClick()}/> + this.onClick()} + onClickHeader={separateToggleEvent ? () => this.onClickHeader() : null} + onClickToggle={separateToggleEvent ? () => this.onClickToggle() : null} + /> {node.toggled ? this.renderChildren(decorators, animations) : null} @@ -89,14 +124,16 @@ class TreeNode extends PureComponent { } TreeNode.propTypes = { + separateToggleEvent: PropTypes.bool, onToggle: PropTypes.func, + onClickHeader: PropTypes.func, style: PropTypes.object.isRequired, node: PropTypes.object.isRequired, decorators: PropTypes.object.isRequired, animations: PropTypes.oneOfType([ PropTypes.object, - PropTypes.bool - ]).isRequired + PropTypes.bool, + ]).isRequired, }; export default TreeNode; diff --git a/src/components/common/index.js b/src/components/common/index.js index 268fe02..cd418d5 100644 --- a/src/components/common/index.js +++ b/src/components/common/index.js @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; export const Div = styled('div', { - shouldForwardProp: prop => ['className', 'children'].indexOf(prop) !== -1 + shouldForwardProp: prop => ['className', 'children', 'onClick'].indexOf(prop) !== -1 })((({style}) => style)); export const Ul = styled('ul', { diff --git a/src/components/index.js b/src/components/index.js index b837780..2767948 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -9,11 +9,11 @@ import {Ul} from './common'; import defaultDecorators from './Decorators'; import TreeNode from './TreeNode'; -const TreeBeard = ({animations, decorators, data, onToggle, style}) => ( +const TreeBeard = ({animations, decorators, data, separateToggleEvent, onToggle, onClickHeader, style}) => (
      {castArray(data).map(node => ( @@ -31,14 +31,16 @@ TreeBeard.propTypes = { PropTypes.object, PropTypes.bool ]), + separateToggleEvent: PropTypes.bool, onToggle: PropTypes.func, + onClickHeader: PropTypes.func, decorators: PropTypes.object }; TreeBeard.defaultProps = { style: defaultTheme, animations: defaultAnimations, - decorators: defaultDecorators + decorators: defaultDecorators, }; export default TreeBeard;