wix-style-react
Version:
wix-style-react
205 lines • 8.82 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import DropdownBase from '../DropdownBase';
import { placements } from '../Popover';
import { st, classes } from './PopoverMenu.st.css';
import { listItemSectionBuilder } from '../ListItemSection';
/** PopoverMenu */
class PopoverMenu extends React.PureComponent {
constructor() {
super(...arguments);
this.ref = React.createRef();
this._open = () => {
if (this.ref.current) {
this.ref.current._open();
}
};
this._filterChildren = children => {
return React.Children.map(children, child => child).filter(child => typeof child !== 'string');
};
this._buildOptions = children => {
return children.map((child, id) => {
const displayName = child.type && child.type.displayName;
if (displayName && displayName === 'PopoverMenu.Divider') {
return {
id,
divider: true,
dataHook: child.props.dataHook,
};
}
if (displayName && displayName === 'PopoverMenu.MenuItem') {
return {
id,
title: child.props.text,
onClick: child.props.onClick,
skin: child.props.skin,
dataHook: child.props.dataHook,
prefixIcon: child.props.prefixIcon,
disabled: child.props.disabled,
subtitle: child.props.subtitle,
suffixIcon: child.props.suffixIcon,
};
}
return { id, value: child, custom: true, overrideStyle: true };
});
};
this._renderOptions = () => {
const { textSize, ellipsis } = this.props;
const children = this._filterChildren(this.props.children);
const options = this._buildOptions(children);
return options.map(option => {
// Custom
if (option.custom) {
return option;
}
// Divider
if (option.divider) {
return listItemSectionBuilder({
type: 'divider',
...option,
});
}
const { id, disabled, dataHook, skin, subtitle, title, ...rest } = option;
return {
...rest,
id,
disabled,
as: 'button',
dataHook: dataHook || `popover-menu-${id}`,
skin: skin || 'dark',
size: textSize,
className: classes.listItem,
ellipsis,
subtitle,
shouldFocusWithoutScroll: true,
value: 'action',
title: false,
optionTitle: title,
};
});
};
this._renderTriggerElement = ({ toggle, open, close, isOpen, ref }) => {
const { triggerElement } = this.props;
if (!triggerElement) {
return null;
}
const commonProps = {
onClick: toggle,
ref,
'aria-haspopup': 'menu',
'aria-expanded': isOpen,
};
return React.isValidElement(triggerElement)
? React.cloneElement(triggerElement, {
...commonProps,
})
: triggerElement({
toggle,
open,
close,
isOpen,
...commonProps,
});
};
}
render() {
const { appendTo, placement, minWidth, maxWidth, flip, fixed, showArrow, dataHook, moveBy, maxHeight, zIndex, className, fluid, onHide, onShow, } = this.props;
return (React.createElement(DropdownBase, { ref: this.ref, className: st(classes.root, className), dataHook: dataHook, animate: true, options: this._renderOptions(), appendTo: appendTo, placement: placement, minWidth: minWidth, maxWidth: maxWidth, flip: flip, fixed: fixed, showArrow: showArrow, tabIndex: -1, moveBy: moveBy, maxHeight: maxHeight, zIndex: zIndex, fluid: fluid, onHide: onHide, onShow: onShow, listType: "action", autoFocus: true }, ({ toggle, open, close, isOpen, ref }) => this._renderTriggerElement({ toggle, open, close, isOpen, ref })));
}
}
PopoverMenu.displayName = 'PopoverMenu';
PopoverMenu.MenuItem = () => ({});
PopoverMenu.Divider = () => ({});
PopoverMenu.propTypes = {
/** Sets a maximum width for a Popover menu */
maxWidth: PropTypes.number,
/** Sets a minimum width for a Popover menu */
minWidth: PropTypes.number,
/** Sets a maximum height for a Popover menu */
maxHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Sets the Popover z-index */
zIndex: PropTypes.number,
/** Moves Popover menu overlay relative to its trigger element by a defined number of px:
* - horizontally (on X-axis)
* - or vertically (on Y-axis)
*/
moveBy: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }),
/** Defines a component that calls out a Popover menu (`<IconButton />`, `<Button />` or `<Text Button />`) */
triggerElement: PropTypes.oneOfType([PropTypes.element, PropTypes.func])
.isRequired,
/** Defines the Popover menu’s overlay placement in relation to its trigger element:
* * auto-start
* * auto
* * auto-end
* * top-start
* * top
* * top-end
* * right-start
* * right
* * right-end
* * bottom-start
* * bottom
* * bottom-end
* * left-start
* * left
* * left-end
*/
placement: PropTypes.oneOf(placements),
/** Sets the size of text in Popover menu items */
textSize: PropTypes.oneOf(['small', 'medium']),
/** Truncates menu item text that overflows component’s max Width */
ellipsis: PropTypes.bool,
/**
* Stores Popover menu compound components:
* - <PopoverMenu.MenuItem />
* - <PopoverMenu.Divider /> (optional)
*
* `<PopoverMenu.MenuItem>` component has these fields:
* * `text` - Sets a label for a Popover menu item
* * `onClick` - Defines a callback function which is called when a Popover menu item is clicked on
* * `skin` - Controls the appearance of a Popover menu item (one of `standard`, `dark`, `destructive`)
* * `prefixIcon` - Contains an icon at the start of a Popover menu item
* * `dataHook` - Applies a data-hook HTML attribute to be used in the tests
* * `suffixIcon` - suffix icon property
* * `disabled` - Disables a Popover menu item
* * `subtitle` - Sets a text for a Popover menu item subtitle\
*
* `<PopoverMenu.Divider>` component has these fields:
* * `dataHook` - Applies a data-hook HTML attribute to be used in the tests
*/
children: PropTypes.node,
/** Enables calculations in relation to a DOM element */
appendTo: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
/** Enables the flip behaviour. This behaviour is used to flip the overlay placement when it starts to overlap the trigger element. */
flip: PropTypes.bool,
/** Enables the fixed behaviour. This behaviour is used to keep the overlay at its original placement even when it is being positioned outside the boundary. */
fixed: PropTypes.bool,
/* Allows truncating the trigger element to a specified parent container width. */
fluid: PropTypes.bool,
/** Controls visibility of the pointer arrow */
showArrow: PropTypes.bool,
/** Applies a data-hook HTML attribute to be used in the tests */
dataHook: PropTypes.string,
/** Applies a CSS class to the component’s root element */
className: PropTypes.string,
/** Defines a callback function which is called when a Popover menu is opened */
onShow: PropTypes.func,
/** Defines a callback function which is called when a Popover menu is closed */
onHide: PropTypes.func,
};
PopoverMenu.defaultProps = {
maxWidth: 204,
minWidth: 144,
placement: 'bottom',
appendTo: 'window',
textSize: 'medium',
fixed: true,
flip: true,
showArrow: true,
ellipsis: false,
maxHeight: 'auto',
};
PopoverMenu.MenuItem.displayName = 'PopoverMenu.MenuItem';
PopoverMenu.Divider.displayName = 'PopoverMenu.Divider';
export default PopoverMenu;
//# sourceMappingURL=PopoverMenu.js.map