polaris-glints
Version:
Glints forked polaris react
337 lines (284 loc) • 9.95 kB
JavaScript
import React, { PureComponent, createRef } from 'react';
import { Transition } from 'react-transition-group';
import { debounce } from '../../utilities/debounce.js';
import { classNames } from '../../utilities/css.js';
import { clamp } from '../../utilities/clamp.js';
import styles from './BulkActions.scss.js';
import { BulkActionMenu } from './components/BulkActionMenu/BulkActionMenu.js';
import { Inline } from '../Inline/Inline.js';
import { BulkActionButton } from './components/BulkActionButton/BulkActionButton.js';
import { Popover } from '../Popover/Popover.js';
import { ActionList } from '../ActionList/ActionList.js';
import { EventListener } from '../EventListener/EventListener.js';
const MAX_PROMOTED_ACTIONS = 2;
const BUTTONS_NODE_ADDITIONAL_WIDTH = 64;
class BulkActionsInner extends PureComponent {
constructor(...args) {
super(...args);
this.state = {
popoverVisible: false,
containerWidth: 0,
measuring: true
};
this.containerNode = null;
this.buttonsNode = null;
this.moreActionsNode = null;
this.groupNode = /*#__PURE__*/createRef();
this.promotedActionsWidths = [];
this.bulkActionsWidth = 0;
this.addedMoreActionsWidthForMeasuring = 0;
this.handleResize = debounce(() => {
const {
popoverVisible
} = this.state;
if (this.containerNode) {
const containerWidth = this.containerNode.getBoundingClientRect().width;
if (containerWidth > 0) {
this.setState({
containerWidth
});
}
}
if (popoverVisible) {
this.setState({
popoverVisible: false
});
}
}, 50, {
trailing: true
});
this.setButtonsNode = node => {
this.buttonsNode = node;
};
this.setContainerNode = node => {
this.containerNode = node;
};
this.setMoreActionsNode = node => {
this.moreActionsNode = node;
};
this.togglePopover = () => {
if (this.props.onMoreActionPopoverToggle) {
this.props.onMoreActionPopoverToggle(this.state.popoverVisible);
}
this.setState(({
popoverVisible
}) => ({
popoverVisible: !popoverVisible
}));
};
this.handleMeasurement = width => {
const {
measuring
} = this.state;
if (measuring) {
this.promotedActionsWidths.push(width);
}
};
}
numberOfPromotedActionsToRender() {
const {
promotedActions
} = this.props;
const {
containerWidth,
measuring
} = this.state;
if (!promotedActions) {
return 0;
}
const containerWidthMinusAdditionalWidth = Math.max(0, containerWidth - BUTTONS_NODE_ADDITIONAL_WIDTH);
if (containerWidthMinusAdditionalWidth >= this.bulkActionsWidth || measuring) {
return promotedActions.length;
}
let sufficientSpace = false;
let counter = promotedActions.length - 1;
let totalWidth = 0;
while (!sufficientSpace && counter >= 0) {
totalWidth += this.promotedActionsWidths[counter];
const widthWithRemovedAction = this.bulkActionsWidth - totalWidth + this.addedMoreActionsWidthForMeasuring;
if (containerWidthMinusAdditionalWidth >= widthWithRemovedAction) {
sufficientSpace = true;
} else {
counter--;
}
}
return clamp(counter, 0, promotedActions.length);
}
actionSections() {
const {
actions
} = this.props;
if (!actions || actions.length === 0) {
return;
}
if (instanceOfBulkActionListSectionArray(actions)) {
return actions;
}
if (instanceOfBulkActionArray(actions)) {
return [{
items: actions
}];
}
}
rolledInPromotedActions() {
const {
promotedActions
} = this.props;
const numberOfPromotedActionsToRender = this.numberOfPromotedActionsToRender();
if (!promotedActions || promotedActions.length === 0 || numberOfPromotedActionsToRender >= promotedActions.length) {
return [];
}
const rolledInPromotedActions = promotedActions.map(action => {
if (instanceOfMenuGroupDescriptor(action)) {
return {
items: [...action.actions]
};
}
return {
items: [action]
};
});
return rolledInPromotedActions.slice(numberOfPromotedActionsToRender);
} // eslint-disable-next-line @typescript-eslint/member-ordering
componentDidMount() {
const {
actions,
promotedActions
} = this.props;
if (promotedActions && !actions && this.moreActionsNode) {
this.addedMoreActionsWidthForMeasuring = this.moreActionsNode.getBoundingClientRect().width;
}
this.bulkActionsWidth = this.buttonsNode ? this.buttonsNode.getBoundingClientRect().width - this.addedMoreActionsWidthForMeasuring : 0;
if (this.containerNode) {
this.setState({
containerWidth: this.containerNode.getBoundingClientRect().width,
measuring: false
});
}
} // eslint-disable-next-line @typescript-eslint/member-ordering
render() {
const {
selectMode,
disabled,
promotedActions,
isSticky,
width
} = this.props;
const actionSections = this.actionSections();
if (promotedActions && promotedActions.length > MAX_PROMOTED_ACTIONS && process.env.NODE_ENV === 'development') {
// eslint-disable-next-line no-console
console.warn(`"To provide a better user experience. There should only be a maximum of ${MAX_PROMOTED_ACTIONS} promoted actions."`);
}
const {
popoverVisible,
measuring
} = this.state;
const numberOfPromotedActionsToRender = this.numberOfPromotedActionsToRender();
const promotedActionsMarkup = promotedActions && numberOfPromotedActionsToRender > 0 ? [...promotedActions].slice(0, numberOfPromotedActionsToRender).map((action, index) => {
if (instanceOfMenuGroupDescriptor(action)) {
return /*#__PURE__*/React.createElement(BulkActionMenu, Object.assign({
key: index
}, action, {
isNewBadgeInBadgeActions: this.isNewBadgeInBadgeActions()
}));
}
return /*#__PURE__*/React.createElement(BulkActionButton, Object.assign({
key: index,
disabled: disabled
}, action, {
handleMeasurement: this.handleMeasurement
}));
}) : null;
const rolledInPromotedActions = this.rolledInPromotedActions();
const activatorLabel = !promotedActions || promotedActions && numberOfPromotedActionsToRender === 0 && !measuring ? 'Actions' : 'More actions';
let combinedActions = [];
if (actionSections && rolledInPromotedActions.length > 0) {
combinedActions = [...rolledInPromotedActions, ...actionSections];
} else if (actionSections) {
combinedActions = actionSections;
} else if (rolledInPromotedActions.length > 0) {
combinedActions = [...rolledInPromotedActions];
}
const actionsPopover = actionSections || rolledInPromotedActions.length > 0 || measuring ? /*#__PURE__*/React.createElement("div", {
className: styles.Popover,
ref: this.setMoreActionsNode
}, /*#__PURE__*/React.createElement(Popover, {
active: popoverVisible,
activator: /*#__PURE__*/React.createElement(BulkActionButton, {
disclosure: true,
showContentInButton: !promotedActionsMarkup,
onAction: this.togglePopover,
content: activatorLabel,
disabled: disabled,
indicator: this.isNewBadgeInBadgeActions()
}),
preferredAlignment: "right",
onClose: this.togglePopover
}, /*#__PURE__*/React.createElement(ActionList, {
sections: combinedActions,
onActionAnyItem: this.togglePopover
}))) : null;
const groupContent = promotedActionsMarkup || actionsPopover ? /*#__PURE__*/React.createElement(Inline, {
gap: "3"
}, promotedActionsMarkup, actionsPopover) : null;
if (!groupContent) {
return null;
}
const group = /*#__PURE__*/React.createElement(Transition, {
timeout: 100,
in: selectMode,
key: "group",
nodeRef: this.groupNode
}, status => {
const groupClassName = classNames(styles.Group, !isSticky && styles['Group-not-sticky'], !measuring && isSticky && styles[`Group-${status}`], measuring && styles['Group-measuring']);
return /*#__PURE__*/React.createElement("div", {
className: groupClassName,
ref: this.groupNode,
style: {
width
}
}, /*#__PURE__*/React.createElement(EventListener, {
event: "resize",
handler: this.handleResize
}), /*#__PURE__*/React.createElement("div", {
className: styles.ButtonGroupWrapper,
ref: this.setButtonsNode
}, /*#__PURE__*/React.createElement("div", {
className: styles.ButtonGroupInner
}, groupContent)));
});
return /*#__PURE__*/React.createElement("div", {
ref: this.setContainerNode
}, group);
}
isNewBadgeInBadgeActions() {
const actions = this.actionSections();
if (!actions) return false;
for (const action of actions) {
for (const item of action.items) {
var _item$badge;
if (((_item$badge = item.badge) === null || _item$badge === void 0 ? void 0 : _item$badge.status) === 'new') return true;
}
}
return false;
}
}
function instanceOfBulkActionListSectionArray(actions) {
const validList = actions.filter(action => {
return action.items;
});
return actions.length === validList.length;
}
function instanceOfBulkActionArray(actions) {
const validList = actions.filter(action => {
return !action.items;
});
return actions.length === validList.length;
}
function instanceOfMenuGroupDescriptor(action) {
return 'title' in action;
}
function BulkActions(props) {
return /*#__PURE__*/React.createElement(BulkActionsInner, props);
}
export { BulkActions };