reblend-ui
Version:
Utilities for creating robust overlay components
146 lines (143 loc) • 5.33 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.default = void 0;
exports.useDropdownMenu = useDropdownMenu;
var _reblendjs = require("reblendjs");
var _reblendHooks = require("reblend-hooks");
var _DropdownContext = require("./DropdownContext");
var _usePopper = require("./usePopper");
var _useClickOutside = require("./useClickOutside");
var _mergeOptionsWithPopperConfig = require("./mergeOptionsWithPopperConfig");
const noop = () => {};
/**
* @memberOf Dropdown
* @param {object} options
* @param {boolean} options.flip Automatically adjust the menu `drop` position based on viewport edge detection
* @param {[number, number]} options.offset Define an offset distance between the Menu and the Toggle
* @param {boolean} options.show Display the menu manually, ignored in the context of a `Dropdown`
* @param {boolean} options.usePopper opt in/out of using PopperJS to position menus. When disabled you must position it yourself.
* @param {string} options.rootCloseEvent The pointer event to listen for when determining "clicks outside" the menu for triggering a close.
* @param {object} options.popperConfig Options passed to the [`usePopper`](/api/usePopper) hook.
*/
function useDropdownMenu(options = {}) {
const [context] = _reblendjs.useContext.bind(this)(_DropdownContext.default, "context");
this.state.context = context;
const elementRef = _reblendHooks.useCallbackRef.bind(this)();
this.state.elementRef = elementRef;
const hasShownRef = _reblendjs.useRef.bind(this)(false);
this.state.hasShownRef = hasShownRef;
const {
flip,
offset,
rootCloseEvent,
fixed = false,
placement: placementOverride,
popperConfig = {},
enableEventListeners = true,
usePopper: shouldUsePopper = !!this.state.context
} = options;
this.state.flip = flip;
this.state.offset = offset;
this.state.rootCloseEvent = rootCloseEvent;
this.state.fixed = fixed;
this.state.placementOverride = placementOverride;
this.state.popperConfig = popperConfig;
this.state.enableEventListeners = enableEventListeners;
this.state.shouldUsePopper = shouldUsePopper;
const show = this.state.context?.show == null ? !!options.show : this.state.context.show;
this.state.show = show;
if (this.state.show && !this.state.hasShownRef.current) {
this.state.hasShownRef.current = true;
}
const handleClose = e => {
this.state.context?.toggle(false, e);
};
this.state.handleClose = handleClose;
const {
placement,
setMenu,
menuElement,
toggleElement
} = this.state.context || {};
this.state.placement = placement;
this.state.setMenu = setMenu;
this.state.menuElement = menuElement;
this.state.toggleElement = toggleElement;
const popper = _usePopper.default.bind(this)(this.state.toggleElement, this.state.menuElement, (0, _mergeOptionsWithPopperConfig.default)({
placement: this.state.placementOverride || this.state.placement || 'bottom-start',
enabled: this.state.shouldUsePopper,
enableEvents: this.state.enableEventListeners == null ? this.state.show : this.state.enableEventListeners,
offset: this.state.offset,
flip: this.state.flip,
fixed: this.state.fixed,
arrowElement: this.state.elementRef.item,
popperConfig: this.state.popperConfig
}));
this.state.popper = popper;
const menuProps = {
ref: this.state.setMenu || noop,
'aria-labelledby': this.state.toggleElement?.id,
...this.state.popper.attributes.popper,
style: this.state.popper.styles.popper
};
this.state.menuProps = menuProps;
const metadata = {
show: this.state.show,
placement: this.state.placement,
hasShown: this.state.hasShownRef.current,
toggle: this.state.context?.toggle,
popper: this.state.shouldUsePopper ? this.state.popper : null,
arrowProps: this.state.shouldUsePopper ? {
ref: this.state.elementRef.ref,
...this.state.popper.attributes.arrow,
style: this.state.popper.styles.arrow
} : {}
};
this.state.metadata = metadata;
_useClickOutside.default.bind(this)(this.state.menuElement, this.state.handleClose, {
clickTrigger: this.state.rootCloseEvent,
disabled: !this.state.show
});
return [this.state.menuProps, this.state.metadata];
}
/* @Reblend: Transformed from function to class */
/**
* Also exported as `<Dropdown.Menu>` from `Dropdown`.
*
* @displayName DropdownMenu
* @memberOf Dropdown
*/
class DropdownMenu extends _reblendjs.default {
static ELEMENT_NAME = "DropdownMenu";
constructor() {
super();
}
async initState() {
const [props, meta] = useDropdownMenu.bind(this)({
...this.props,
usePopper: this.props.usePopperProp
});
this.state.props = props;
this.state.meta = meta;
}
async initProps({
children,
usePopper: usePopperProp = true,
...options
}) {
this.props = {};
this.props.children = children;
this.props.usePopperProp = usePopperProp;
this.props = {
...this.props,
...options
};
}
async html() {
return _reblendjs.default.construct.bind(this)(_reblendjs.default, null, this.props.children.map(child => child(this.state.props, this.state.meta)));
}
}
/* @Reblend: Transformed from function to class */
DropdownMenu.displayName = 'DropdownMenu';
/** @component */
var _default = exports.default = DropdownMenu;