UNPKG

@procore/core-react

Version:
285 lines (279 loc) • 13.6 kB
var _excluded = ["option", "highlighted", "expanded", "optionRenderer"], _excluded2 = ["children", "icon"], _excluded3 = ["disabled", "icon", "label", "loading", "options", "onClick", "optionRenderer", "children", "placement", "onKeyDown", "onFocus", "onBlur", "onMouseDown", "onMouseEnter", "onMouseLeave", "size", "variant"]; function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; } import { EllipsisVertical } from '@procore/core-icons/dist'; import React, { useCallback } from 'react'; import { Card } from '../Card'; import { DropdownButton } from '../Dropdown/Dropdown'; import { Portal } from '../Portal'; import { useClickOutside } from '../_hooks/ClickOutside'; import { useI18nContext } from '../_hooks/I18n'; import { mergeRefs } from '../_utils/mergeRefs'; import { defaultOptionRenderer, DropdownFlyoutContext, hasChildren, noop, transformOption, useDropdownFlyout, useDropdownFlyoutContext } from './DropdownFlyout.helpers'; import { StyledDropdownFlyout, StyledDropdownFlyoutItem } from './DropdownFlyout.styles'; import { rootId } from './DropdownFlyout.types'; import { useDropdownFlyoutOverlay } from './useDropdownFlyoutOverlay'; export var FlyoutCaption = /*#__PURE__*/React.forwardRef(function FlyoutCaption(_ref, ref) { var option = _ref.option, highlighted = _ref.highlighted, expanded = _ref.expanded, optionRenderer = _ref.optionRenderer, props = _objectWithoutProperties(_ref, _excluded); var renderedOption = optionRenderer(option); return /*#__PURE__*/React.createElement(StyledDropdownFlyoutItem, _extends({}, props, { ref: ref, "aria-expanded": expanded, "data-highlighted": highlighted, "data-flyout": true }), renderedOption); }); FlyoutCaption.displayName = 'FlyoutCaption'; export function FlyoutItem(_ref2) { var option = _ref2.option; var overlayElRef = React.useRef(null); var _useDropdownFlyoutCon = useDropdownFlyoutContext(), isHighlighted = _useDropdownFlyoutCon.isHighlighted, isExpanded = _useDropdownFlyoutCon.isExpanded, _onClick = _useDropdownFlyoutCon.onClick, expand = _useDropdownFlyoutCon.expand, collapse = _useDropdownFlyoutCon.collapse, optionRenderer = _useDropdownFlyoutCon.optionRenderer; var onMouseEnter = React.useCallback(function () { return expand(option); }, [option, expand]); var onMouseLeave = React.useCallback(function (e) { if (e.relatedTarget instanceof Node && overlayElRef.current && !overlayElRef.current.contains(e.relatedTarget)) { collapse(option); } }, [collapse, option]); var onClick = React.useCallback(function () { return _onClick(option); }, [_onClick, option]); var _React$useState = React.useState(null), _React$useState2 = _slicedToArray(_React$useState, 2), referenceEl = _React$useState2[0], setReferenceEl = _React$useState2[1]; return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FlyoutCaption, { ref: setReferenceEl, role: "listitem", option: option.origin, onClick: onClick, optionRenderer: optionRenderer, highlighted: isHighlighted(option), expanded: hasChildren(option.origin) ? isExpanded(option) : undefined, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave }), Array.isArray(option.children) && isExpanded(option) ? /*#__PURE__*/React.createElement(Portal, null, /*#__PURE__*/React.createElement(FlyoutList, { referenceEl: referenceEl, options: option.children, overlayRef: overlayElRef, placement: "right-top" })) : null); } export function FlyoutList(_ref3) { var options = _ref3.options, placement = _ref3.placement, referenceEl = _ref3.referenceEl, overlayElRef = _ref3.overlayRef, _ref3$offset = _ref3.offset, offset = _ref3$offset === void 0 ? 0 : _ref3$offset; var _useDropdownFlyoutOve = useDropdownFlyoutOverlay({ placement: placement, offset: { mainAxis: offset } }), overlayStyle = _useDropdownFlyoutOve.overlayStyle, referenceRef = _useDropdownFlyoutOve.referenceRef, overlayRef = _useDropdownFlyoutOve.overlayRef; React.useEffect(function () { referenceRef(referenceEl); }, [referenceEl, referenceRef]); return /*#__PURE__*/React.createElement(Card, { ref: mergeRefs(overlayRef, overlayElRef), shadowStrength: 2, style: overlayStyle }, /*#__PURE__*/React.createElement(StyledDropdownFlyout, { role: "list" }, options.map(function (option) { return /*#__PURE__*/React.createElement(FlyoutItem, { key: option.id, option: option }); }))); } var DefaultButton = /*#__PURE__*/React.forwardRef(function DefaultButton(_ref4, ref) { var children = _ref4.children, icon = _ref4.icon, props = _objectWithoutProperties(_ref4, _excluded2); return /*#__PURE__*/React.createElement(DropdownButton, _extends({}, props, { icon: props.loading ? undefined : !icon && !children ? /*#__PURE__*/React.createElement(EllipsisVertical, null) : icon, arrow: Boolean(React.Children.count(children)), ref: ref }), children); }); /** The dropdown flyout allows for additional menu items to be nested within parent items, which allows more menu options for users. @a11y WARN: DropdownFlyotus hold no value and take an action. Focus is expected to be moved from a Dropdown to something else, to continue the user flow. If the action is inert and does not move focus, this is left to be added by the consumer. @since 10.19.0 @see [Storybook](https://stories.core.procore.com/?path=/story/core-react_demos-dropdownflyout--demo) @see [Design Guidelines](https://design.procore.com/dropdown-flyout) */ export var DropdownFlyout = /*#__PURE__*/React.forwardRef(function DropdownFlyout(_ref5, ref) { var disabled = _ref5.disabled, icon = _ref5.icon, label = _ref5.label, loading = _ref5.loading, _options = _ref5.options, _ref5$onClick = _ref5.onClick, _onClick = _ref5$onClick === void 0 ? noop : _ref5$onClick, _ref5$optionRenderer = _ref5.optionRenderer, optionRenderer = _ref5$optionRenderer === void 0 ? defaultOptionRenderer : _ref5$optionRenderer, children = _ref5.children, _ref5$placement = _ref5.placement, placement = _ref5$placement === void 0 ? 'right-bottom' : _ref5$placement, _onKeyDown = _ref5.onKeyDown, onFocus_ = _ref5.onFocus, onBlur_ = _ref5.onBlur, onMouseDown_ = _ref5.onMouseDown, onMouseEnter_ = _ref5.onMouseEnter, onMouseLeave_ = _ref5.onMouseLeave, size = _ref5.size, _ref5$variant = _ref5.variant, variant = _ref5$variant === void 0 ? 'secondary' : _ref5$variant, props = _objectWithoutProperties(_ref5, _excluded3); var innerRef = React.useRef(null); var containerRef = ref || innerRef; var overlayElRef = React.useRef(null); var targetRef = React.useRef(null); var flyoutOptions = React.useMemo(function () { return transformOption(_options, rootId); }, [_options]); var _useDropdownFlyout = useDropdownFlyout({ flyoutOptions: flyoutOptions, onClick: _onClick, onKeyDown: _onKeyDown }), options = _useDropdownFlyout.options, collapse = _useDropdownFlyout.collapse, expand = _useDropdownFlyout.expand, closeSelectedDropdown = _useDropdownFlyout.closeSelectedDropdown, openDropdown = _useDropdownFlyout.openDropdown, isOpen = _useDropdownFlyout.isOpen, onClick = _useDropdownFlyout.onClick, onKeyDown = _useDropdownFlyout.onKeyDown, setMouseOver = _useDropdownFlyout.setMouseOver, setFocused = _useDropdownFlyout.setFocused, isExpanded = _useDropdownFlyout.isExpanded, isHighlighted = _useDropdownFlyout.isHighlighted, closeDropdown = _useDropdownFlyout.closeDropdown; useClickOutside({ onClickOutside: isOpen ? closeSelectedDropdown : noop, refs: [containerRef] }); var element = typeof children === 'function' ? children({ isOpen: isOpen }) : children; var I18n = useI18nContext(); var ariaLabel = props['aria-label'] || label || I18n.t("core.dropdown.".concat(loading ? 'loading' : 'moreOptions')); var trigger = /*#__PURE__*/React.isValidElement(element) ? element : /*#__PURE__*/React.createElement(DefaultButton, { "aria-label": ariaLabel, "aria-expanded": isOpen, children: label, disabled: disabled, loading: loading, icon: icon, size: size, variant: variant }); function onBlur(e) { setFocused(false); closeSelectedDropdown(e); onBlur_ === null || onBlur_ === void 0 ? void 0 : onBlur_(e); } function onFocus(e) { setFocused(true); onFocus_ === null || onFocus_ === void 0 ? void 0 : onFocus_(e); } function onMouseDown(e) { var _containerRef$current; // Discard firing of blur event // when click was inside the dropdown if (e.target.dataset.flyout) { e.preventDefault(); return; } if (e.target instanceof Node && (_containerRef$current = containerRef.current) !== null && _containerRef$current !== void 0 && _containerRef$current.contains(e.target)) { var _targetRef$current; e.preventDefault(); (_targetRef$current = targetRef.current) === null || _targetRef$current === void 0 ? void 0 : _targetRef$current.focus(); } onMouseDown_ === null || onMouseDown_ === void 0 ? void 0 : onMouseDown_(e); } function onMouseLeave(e) { setMouseOver(false); onMouseLeave_ === null || onMouseLeave_ === void 0 ? void 0 : onMouseLeave_(e); } function onMouseEnter(e) { setMouseOver(true); onMouseEnter_ === null || onMouseEnter_ === void 0 ? void 0 : onMouseEnter_(e); } var _React$useState3 = React.useState(null), _React$useState4 = _slicedToArray(_React$useState3, 2), referenceEl = _React$useState4[0], setReferenceEl = _React$useState4[1]; // mergeRefs function is from shared utils. // eslint-disable-next-line react-hooks/exhaustive-deps var memoRef = useCallback(trigger.ref ? mergeRefs(trigger.ref, targetRef, setReferenceEl) : mergeRefs(targetRef, setReferenceEl), [targetRef, setReferenceEl, trigger.ref]); return /*#__PURE__*/React.createElement("div", _extends({}, props, { "aria-label": undefined, ref: containerRef, onKeyDown: onKeyDown, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, onFocus: onFocus, onMouseDown: onMouseDown, onBlur: onBlur }), /*#__PURE__*/React.cloneElement(trigger, { ref: memoRef, onClick: function onClick(e) { var _trigger$props$onClic, _trigger$props; (_trigger$props$onClic = (_trigger$props = trigger.props).onClick) === null || _trigger$props$onClic === void 0 ? void 0 : _trigger$props$onClic.call(_trigger$props, e); if (isOpen) { closeDropdown(); } else { openDropdown(); } }, tabIndex: 0 }), /*#__PURE__*/React.createElement(DropdownFlyoutContext.Provider, { value: { expand: expand, collapse: collapse, onClick: onClick, isExpanded: isExpanded, isHighlighted: isHighlighted, optionRenderer: optionRenderer } }, isOpen && /*#__PURE__*/React.createElement(Portal, null, /*#__PURE__*/React.createElement(FlyoutList, { options: options, overlayRef: overlayElRef, placement: placement, referenceEl: referenceEl, offset: 4 })))); }); //# sourceMappingURL=DropdownFlyout.js.map