UNPKG

polaris-glints

Version:

Glints forked polaris react

337 lines (284 loc) 9.95 kB
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 };