UNPKG

@carbon/react

Version:

React components for the Carbon Design System

232 lines (218 loc) 8.32 kB
/** * Copyright IBM Corp. 2016, 2023 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var React = require('react'); var PropTypes = require('prop-types'); var cx = require('classnames'); var iconsReact = require('@carbon/icons-react'); var index$1 = require('../IconButton/index.js'); var Button = require('../Button/Button.js'); require('../Button/Button.Skeleton.js'); var Menu = require('../Menu/Menu.js'); require('../Menu/MenuItem.js'); var useAttachedMenu = require('../../internal/useAttachedMenu.js'); var useId = require('../../internal/useId.js'); var usePrefix = require('../../internal/usePrefix.js'); var react = require('@floating-ui/react'); var index = require('../FeatureFlags/index.js'); var mergeRefs = require('../../tools/mergeRefs.js'); var deprecateValuesWithin = require('../../prop-types/deprecateValuesWithin.js'); var mapPopoverAlign = require('../../tools/mapPopoverAlign.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes); var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx); var _ChevronDown; const defaultTranslations = { 'carbon.combo-button.additional-actions': 'Additional actions' }; /** * Message ids that will be passed to translateWithId(). */ function defaultTranslateWithId(messageId) { return defaultTranslations[messageId]; } const ComboButton = /*#__PURE__*/React__default["default"].forwardRef(function ComboButton({ children, className, disabled, label, onClick, size = 'lg', menuAlignment = 'bottom', tooltipAlignment, translateWithId: t = defaultTranslateWithId, ...rest }, forwardRef) { // feature flag utilized to separate out only the dynamic styles from @floating-ui // flag is turned on when collision detection (ie. flip, hide) logic is not desired const enableOnlyFloatingStyles = index.useFeatureFlag('enable-v12-dynamic-floating-styles'); const id = useId.useId('combobutton'); const prefix = usePrefix.usePrefix(); const containerRef = React.useRef(null); let middlewares = []; if (!enableOnlyFloatingStyles) { middlewares = [react.flip({ crossAxis: false }), react.hide()]; } if (menuAlignment === 'bottom' || menuAlignment === 'top') { middlewares.push(react.size({ apply({ rects, elements }) { Object.assign(elements.floating.style, { width: `${rects.reference.width}px` }); } })); } const { refs, floatingStyles, placement, middlewareData } = react.useFloating({ placement: menuAlignment, // The floating element is positioned relative to its nearest // containing block (usually the viewport). It will in many cases also // “break” the floating element out of a clipping ancestor. // https://floating-ui.com/docs/misc#clipping strategy: 'fixed', // Middleware order matters, arrow should be last middleware: middlewares, whileElementsMounted: react.autoUpdate }); const ref = mergeRefs["default"](forwardRef, containerRef, refs.setReference); const { open, handleClick: hookOnClick, handleMousedown: handleTriggerMousedown, handleClose } = useAttachedMenu.useAttachedMenu(containerRef); React.useLayoutEffect(() => { const updatedFloatingStyles = { ...floatingStyles, visibility: middlewareData.hide?.referenceHidden ? 'hidden' : 'visible' }; Object.keys(updatedFloatingStyles).forEach(style => { if (refs.floating.current) { refs.floating.current.style[style] = updatedFloatingStyles[style]; } }); }, [floatingStyles, refs.floating, middlewareData, placement, open]); function handleTriggerClick() { if (containerRef.current) { hookOnClick(); } } function handlePrimaryActionClick(e) { if (onClick) { onClick(e); } } const containerClasses = cx__default["default"](`${prefix}--combo-button__container`, `${prefix}--combo-button__container--${size}`, { [`${prefix}--combo-button__container--open`]: open }, className); const menuClasses = cx__default["default"](`${prefix}--combo-button__${menuAlignment}`); const primaryActionClasses = cx__default["default"](`${prefix}--combo-button__primary-action`); const triggerClasses = cx__default["default"](`${prefix}--combo-button__trigger`); return /*#__PURE__*/React__default["default"].createElement("div", _rollupPluginBabelHelpers["extends"]({}, rest, { className: containerClasses, ref: ref, "aria-owns": open ? id : undefined }), /*#__PURE__*/React__default["default"].createElement("div", { className: primaryActionClasses }, /*#__PURE__*/React__default["default"].createElement(Button["default"], { title: label, size: size, disabled: disabled, onClick: handlePrimaryActionClick }, label)), /*#__PURE__*/React__default["default"].createElement(index$1.IconButton, { ref: refs.setReference, className: triggerClasses, label: t('carbon.combo-button.additional-actions'), size: size, disabled: disabled, align: tooltipAlignment, "aria-haspopup": true, "aria-expanded": open, onClick: handleTriggerClick, onMouseDown: handleTriggerMousedown, "aria-controls": open ? id : undefined }, _ChevronDown || (_ChevronDown = /*#__PURE__*/React__default["default"].createElement(iconsReact.ChevronDown, null))), /*#__PURE__*/React__default["default"].createElement(Menu.Menu, { containerRef: containerRef, menuAlignment: menuAlignment, className: menuClasses, ref: refs.setFloating, id: id, label: t('carbon.combo-button.additional-actions'), size: size, open: open, onClose: handleClose }, children)); }); ComboButton.propTypes = { /** * A collection of MenuItems to be rendered as additional actions for this ComboButton. */ children: PropTypes__default["default"].node.isRequired, /** * Additional CSS class names. */ className: PropTypes__default["default"].string, /** * Specify whether the ComboButton should be disabled, or not. */ disabled: PropTypes__default["default"].bool, /** * Provide the label to be rendered on the primary action button. */ label: PropTypes__default["default"].string.isRequired, /** * Experimental property. Specify how the menu should align with the button element */ menuAlignment: PropTypes__default["default"].oneOf(['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end']), /** * Provide an optional function to be called when the primary action element is clicked. */ onClick: PropTypes__default["default"].func, /** * Specify the size of the buttons and menu. */ size: PropTypes__default["default"].oneOf(['sm', 'md', 'lg']), /** * Specify how the trigger tooltip should be aligned. */ tooltipAlignment: deprecateValuesWithin["default"](PropTypes__default["default"].oneOf(['top', 'top-left', // deprecated use top-start instead 'top-right', // deprecated use top-end instead 'bottom', 'bottom-left', // deprecated use bottom-start instead 'bottom-right', // deprecated use bottom-end instead 'left', 'left-bottom', // deprecated use left-end instead 'left-top', // deprecated use left-start instead 'right', 'right-bottom', // deprecated use right-end instead 'right-top', // deprecated use right-start instead // new values to match floating-ui 'top-start', 'top-end', 'bottom-start', 'bottom-end', 'left-end', 'left-start', 'right-end', 'right-start']), ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end'], mapPopoverAlign.mapPopoverAlign), /** * Optional method that takes in a message id and returns an * internationalized string. */ translateWithId: PropTypes__default["default"].func }; exports.ComboButton = ComboButton;