Web: Refactored GroupButtonsMenu component, new examples have been added, speed of the mechanism for hiding elements has been improved.

This commit is contained in:
Ilya Oleshko 2019-07-08 10:12:43 +03:00
parent 7e1eeb9766
commit 6090fdc112
6 changed files with 243 additions and 137 deletions

View File

@ -0,0 +1,69 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import { withKnobs, boolean, text, select, number } from '@storybook/addon-knobs/react';
import { BooleanValue, createBooleanValue } from 'react-values'
import withReadme from 'storybook-readme/with-readme'
import styled from '@emotion/styled';
import Readme from './README.md'
import { GroupButtonsMenu, Checkbox, DropDownItem, Button } from 'asc-web-components'
const GroupButtonsMenuContainer = styled.div`
height: 2000px;
`;
const createItems = (label, dropDownLabel, menuItemLabel, count) => {
var items =[
{
label: label,
isDropdown: true,
isSeparator: true,
fontWeight: 'bold',
children: [
<DropDownItem label={dropDownLabel}/>,
<DropDownItem label={dropDownLabel}/>,
<DropDownItem label={dropDownLabel}/>
]
}
];
for (var i=0; i<count; i++){
items.push({label:menuItemLabel});
}
return items;
}
storiesOf('Components|GroupButtonsMenu', module)
.addDecorator(withReadme(Readme))
.addDecorator(withKnobs)
.add('base', () => {
const elements = 10;
const selectLabel = 'Select';
const dropLabel = 'Dropdown item';
const menuItemLabel = 'Menu item';
const menuItems = createItems(selectLabel, dropLabel, menuItemLabel, elements);
return (
<GroupButtonsMenuContainer>
<GroupButtonsMenu checkBox={
<BooleanValue>
{({ value, toggle }) => (
<Checkbox isChecked={value}
onChange={e => {
console.log(e.target.value+' is checked');
toggle(e.target.checked);
}}
isDisabled={false}
value='Checkbox'
id='check1' />)}
</BooleanValue>}
menuItems={menuItems}
visible={true}
moreLabel={text('moreLabel', 'More')}
closeTitle={text('closeTitle', 'Close')}
/>
</GroupButtonsMenuContainer>
);
});

View File

@ -1,64 +0,0 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import withReadme from 'storybook-readme/with-readme'
import styled, { css } from '@emotion/styled';
import Readme from './README.md'
import { GroupButtonsMenu, GroupButton, DropDownItem } from 'asc-web-components'
const GroupButtonsMenuContainer = styled.div`
height: 2000px;
`;
const Checkbox = styled.input`
vertical-align: middle;
margin-left: 24px;
`;
storiesOf('Components|GroupButtonsMenu', module)
.addDecorator(withReadme(Readme))
.add('empty', () => (
<GroupButtonsMenuContainer>
<GroupButtonsMenu />
</GroupButtonsMenuContainer>
))
.add('documents', () => (
<GroupButtonsMenuContainer>
<GroupButtonsMenu needCollapse>
<Checkbox name="checkbox" type="checkbox" />
<GroupButton label='Select' isDropdown isSeparator fontWeight='bold'>
<DropDownItem label='All'/>
<DropDownItem label='Files'/>
<DropDownItem label='Folders'/>
<DropDownItem label='Documents'/>
<DropDownItem label='Presentations'/>
<DropDownItem label='Images'/>
<DropDownItem label='Archives'/>
</GroupButton>
<GroupButton label='Share'/>
<GroupButton label='Download' />
<GroupButton label='Download as'/>
<GroupButton label='Move'/>
<GroupButton label='Copy'/>
<GroupButton label='Delete'/>
</GroupButtonsMenu>
</GroupButtonsMenuContainer>
))
.add('people', () => (
<GroupButtonsMenuContainer>
<GroupButtonsMenu needCollapse>
<Checkbox name="checkbox" type="checkbox" />
<GroupButton label='Select' isDropdown isSeparator fontWeight='bold'>
<DropDownItem label='Active'/>
<DropDownItem label='Disabled'/>
<DropDownItem label='Invited'/>
</GroupButton>
<GroupButton label='Make employee' />
<GroupButton label='Make guest' />
<GroupButton label='Set active' />
<GroupButton label='Set disabled' />
<GroupButton label='Invite again' />
<GroupButton label='Send e-mail' />
<GroupButton label='Delete' />
</GroupButtonsMenu>
</GroupButtonsMenuContainer>
));

View File

@ -0,0 +1,76 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import { withKnobs, text} from '@storybook/addon-knobs/react';
import { BooleanValue } from 'react-values'
import styled from '@emotion/styled';
import { GroupButtonsMenu, Checkbox, DropDownItem } from 'asc-web-components'
const GroupButtonsMenuContainer = styled.div`
height: 2000px;
`;
const peopleItems = [
{
label: 'Select',
isDropdown: true,
isSeparator: true,
fontWeight: 'bold',
children: [
<DropDownItem label='Active'/>,
<DropDownItem label='Disabled'/>,
<DropDownItem label='Invited'/>
]
},
{
label: 'Make employee',
action: () => console.log('Make employee action')
},
{
label: 'Make guest',
action: () => console.log('Make guest action')
},
{
label: 'Set active',
action: () => console.log('Set active action')
},
{
label: 'Set disabled',
action: () => console.log('Set disabled action')
},
{
label: 'Invite again',
action: () => console.log('Invite again action')
},
{
label: 'Send e-mail',
action: () => console.log('Send e-mail action')
},
{
label: 'Delete',
action: () => console.log('Delete action')
}
];
storiesOf('EXAMPLES|GroupButtonsMenu', module)
.addDecorator(withKnobs)
.add('people', () => (
<GroupButtonsMenuContainer>
<GroupButtonsMenu checkBox={
<BooleanValue>
{({ value, toggle }) => (
<Checkbox isChecked={value}
onChange={e => {
console.log(e.target.value);
toggle(e.target.checked);
}}
isDisabled={false}
value='Checkbox'
id='check1' />)}
</BooleanValue>}
menuItems={peopleItems}
visible={true}
moreLabel={text('moreLabel', 'More')}
closeTitle={text('closeTitle', 'Close')}
/>
</GroupButtonsMenuContainer>
));

View File

@ -3,6 +3,7 @@ import styled, { css } from 'styled-components'
import PropTypes from 'prop-types'
import { Icons } from '../icons'
import DropDown from '../drop-down'
import Checkbox from '../checkbox'
const textColor = '#333333',
disabledTextColor = '#A3A9AE';
@ -88,7 +89,7 @@ const useOuterClickNotifier = (onOuterClick, ref) => {
}
const GroupButton = (props) => {
const { label, isDropdown, opened, disabled, action, isSeparator } = props;
const { label, isDropdown, opened, disabled, action, isSeparator} = props;
const [isOpen, toggle] = useState(opened);
const ref = useRef(null);

View File

@ -1,7 +1,8 @@
import React, { useState, useEffect, useRef } from 'react'
import React from 'react'
import styled from 'styled-components'
import PropTypes from 'prop-types'
import GroupButton from '../group-button'
import DropDownItem from '../drop-down-item'
const StyledGroupButtonsMenu = styled.div`
position: sticky;
@ -11,10 +12,9 @@ const StyledGroupButtonsMenu = styled.div`
height: 56px;
list-style: none;
padding: 0 18px 19px 0;
.hidden {
display: none;
}
width: 100%;
white-space: nowrap;
display: ${state => state.visible ? 'block' : 'none'};
`;
const CloseButton = styled.div`
@ -46,84 +46,108 @@ const CloseButton = styled.div`
}
`;
const padding = 4;
var lastWidth = 0;
const CheckBox = styled.div`
display: inline-block;
margin-left: 20px;
vertical-align: middle;
const collapseButtons = (menu) => {
if (menu == undefined) return;
& > * {
margin: 0px;
}
`;
class GroupButtonsMenu extends React.Component {
constructor(props) {
super(props);
this.updateMenu = this.updateMenu.bind(this);
this.state = {
priorityItems: [],
moreItems: [],
visible: true
}
this.fullMenuArray = this.props.menuItems;
this.checkBox = this.props.checkBox;
}
var groupMenu = menu,
groupMenuWidth = groupMenu.clientWidth,
groupButtons = groupMenu.querySelectorAll('div[class*="-0"]:not(.more):not(.hidden)'),
groupButtonsMore = groupMenu.querySelector('div.more[class*="-0"]'),
groupButtonsMoreWidth = groupButtonsMore.clientWidth || 0,
groupButtonsWidthArray = getButtonsWidthArray(groupButtons),
groupButtonsWidth = getButtonsWidth(groupButtonsWidthArray),
lastHidden = getLastHidden(groupMenu),
lastHiddenWidth = (lastHidden != undefined) ? lastHidden.clientWidth : 0,
lastGroupButton = groupButtons[groupButtons.length -1],
moreThanMenu = groupButtonsWidth + groupButtonsMoreWidth - lastHiddenWidth,
lessThanMenu = groupButtonsWidth + groupButtonsMoreWidth + lastHiddenWidth + lastGroupButton.clientWidth * 1.3;
if (lastWidth !== 0 && lastWidth > groupMenuWidth){
if (moreThanMenu > groupMenuWidth) {
lastGroupButton.classList.add('hidden');
}
} else {
if (lessThanMenu < groupMenuWidth && lastHidden != undefined) {
lastHidden.classList.remove('hidden');
}
componentWillMount() {
this.setState({
priorityItems: this.props.menuItems
})
}
if (lastHidden == undefined){
groupButtonsMore.classList.add('hidden');
} else {
groupButtonsMore.classList.remove('hidden');
componentDidMount() {
this.widthsArray = Array.from(this.refs.groupMenu.children).map(item => item.getBoundingClientRect().width);
window.addEventListener('resize', _.throttle(this.updateMenu), 100);
this.updateMenu();
}
howManyItemsInMenuArray(array, outerWidth, initialWidth, minimumNumberInNav) {
let total = (initialWidth+150);
for(let i = 0; i < array.length; i++) {
if(total + array[i] > outerWidth) {
return i < minimumNumberInNav ? minimumNumberInNav : i;
} else {
total += array[i];
}
}
}
lastWidth = groupMenuWidth;
}
updateMenu() {
this.outerWidth = this.refs.groupMenuOuter ? this.refs.groupMenuOuter.getBoundingClientRect().width : 0;
this.moreMenu = this.refs.moreMenu ? this.refs.moreMenu.getBoundingClientRect().width : 0;
const getButtonsWidthArray = (buttons) => {
return Array.prototype.slice.call(buttons).map((button => button.clientWidth + padding * 2));
}
const arrayAmount = this.howManyItemsInMenuArray(this.widthsArray, this.outerWidth, this.moreMenu, 1);
const navItemsCopy = this.fullMenuArray;
const priorityItems = navItemsCopy.slice(0, arrayAmount);
this.setState({
priorityItems: priorityItems,
moreItems: priorityItems.length !== navItemsCopy.length ? navItemsCopy.slice(arrayAmount, navItemsCopy.length) : []
});
}
const getButtonsWidth = (buttonsWidthArray) => {
return buttonsWidthArray.reduce((a,b) => a + b);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateMenu());
}
const getLastHidden = (buttons) => {
return buttons.querySelectorAll('div[class*="-0"].hidden:not(.more)')[0];
}
render() {
const { priorityItems, moreItems } = this.state;
const GroupButtonsMenu = props => {
const { children, needCollapse } = props;
const ref = useRef(null);
const toggle = () => this.setState({visible: !this.props.visible});
const collapseEvent = (e) => (React.Children.toArray(props.children).length && needCollapse)
? collapseButtons(ref.current)
: e.preventDefault();
useEffect(() => {
window.addEventListener('load', collapseEvent(event));
window.onresize = (e) => collapseEvent(e);
});
return (
<StyledGroupButtonsMenu ref={ref} {...props}>
{children}
{needCollapse && <GroupButton className="more" isDropdown label='More'>{children}</GroupButton>}
<CloseButton/>
</StyledGroupButtonsMenu>
);
}
GroupButtonsMenu.propTypes = {
needCollapse: PropTypes.bool
}
GroupButtonsMenu.defaultProps = {
needCollapse: false
return (
<StyledGroupButtonsMenu ref="groupMenuOuter" visible={this.state.visible} {...this.state}>
{this.checkBox &&
<CheckBox>{this.checkBox}</CheckBox>
}
<div ref="groupMenu" style={{display: 'inline-block'}}>
{priorityItems.map((item, i) =>
<GroupButton key={`navItem-${i}`}
label={item.label}
isDropdown={item.isDropdown}
isSeparator={item.isSeparator}
fontWeight={item.fontWeight}
action={item.action}>
{item.children}
</GroupButton>
)}
</div>
{moreItems.length > 0 &&
<GroupButton ref="moreMenu" isDropdown label={this.props.moreLabel}>
{moreItems.map((item, i) =>
<DropDownItem
key={`moreNavItem-${i}`}
label={item.label}
onClick={item.onClick} />
)}
</GroupButton>
}
<CloseButton title={this.props.closeTitle} onClick={() => toggle()} />
</StyledGroupButtonsMenu>
);
}
}
export default GroupButtonsMenu;