UNPKG

@enact/sandstone

Version:

Large-screen/TV support library for Enact, containing a variety of UI components.

262 lines (259 loc) 12.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = exports.ContextualMenuDecorator = void 0; var _handle = require("@enact/core/handle"); var _hoc = _interopRequireDefault(require("@enact/core/hoc")); var _kind = _interopRequireDefault(require("@enact/core/kind")); var _Repeater = _interopRequireDefault(require("@enact/ui/Repeater")); var _Toggleable = _interopRequireDefault(require("@enact/ui/Toggleable")); var _compose = _interopRequireDefault(require("ramda/src/compose")); var _propTypes = _interopRequireDefault(require("prop-types")); var _ContextualPopupDecorator = _interopRequireDefault(require("../ContextualPopupDecorator")); var _Item = _interopRequireDefault(require("../Item")); var _Scroller = _interopRequireDefault(require("../Scroller")); var _Skinnable = _interopRequireDefault(require("../Skinnable")); var _ContextualMenuDecoratorModule = _interopRequireDefault(require("./ContextualMenuDecorator.module.css")); var _jsxRuntime = require("react/jsx-runtime"); var _excluded = ["className"], _excluded2 = ["onOpen", "popupProps"]; /** * A decorator for adding contextual menus to components. * * @module sandstone/ContextualMenuDecorator * @exports ContextualMenuDecorator */ // The maximum number of visible items. More than this number invokes a scroller. // When updating this value, you must also set the max-items LESS variable. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var MAX_VISIBLE_MENU_ITEMS = 5; /** * Default config for {@link sandstone/ContextualMenuDecorator.ContextualMenuDecorator} * * @type {Object} * @hocconfig * @memberof sandstone/ContextualMenuDecorator.ContextualMenuDecorator */ var defaultConfig = { /** * Disables passing the `skin` prop to the wrapped component. * * @see {@link sandstone/Skinnable.Skinnable.skin} * @type {Boolean} * @default false * @memberof sandstone/ContextualMenuDecorator.ContextualMenuDecorator.defaultConfig * @public */ noSkin: false, /** * The prop in which to pass the value of `open` state of ContextualMenuDecorator to the * wrapped component. * * @type {String} * @default 'selected' * @memberof sandstone/ContextualMenuDecorator.ContextualMenuDecorator.defaultConfig * @public */ openProp: 'selected' }; var ScrollingRepeater = function ScrollingRepeater(_ref) { var className = _ref.className, rest = _objectWithoutProperties(_ref, _excluded); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Scroller["default"], { className: className, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Repeater["default"], _objectSpread({}, rest)) }); }; var ContextualMenuDecoratorBase = (0, _hoc["default"])(defaultConfig, function (config, Wrapped) { // we might not need Skinnable at all here. If we want to skin the popup and it's defined as a // private component in this module, we can wrap it with skinnable and style it as needed there. var Component = (0, _Skinnable["default"])((0, _ContextualPopupDecorator["default"])(_objectSpread(_objectSpread({}, config), {}, { noArrow: true }), Wrapped)); return (0, _kind["default"])({ name: 'ContextualMenuDecorator', propTypes: /** @lends sandstone/ContextualMenuDecorator.ContextualMenuDecorator.prototype */{ /** * Direction of popup with respect to the wrapped component. * * @type {('above'|'above center'|'above left'|'above right'|'below'|'below center'|'below left'|'below right'|'left middle'|'left top'|'left bottom'|'right middle'|'right top'|'right bottom')} * @default 'below right' * @public */ direction: _propTypes["default"].oneOf(['above', 'above center', 'above left', 'above right', 'below', 'below center', 'below left', 'below right', 'left middle', 'left top', 'left bottom', 'right middle', 'right top', 'right bottom']), /** * The items to be displayed in the `ContextualMenuDecorator` when `open`. * * Takes either an array of strings or an array of objects. When strings, the values will be * used in the generated components as the readable text. When objects, the properties will * be passed onto an `Item` component and `children` as well as a unique `key` property are * required. * * @type {String[]|Array.<{key: (Number|String), children: (String|Component)}>} * @public */ menuItems: _propTypes["default"].oneOfType([_propTypes["default"].arrayOf(_propTypes["default"].string), _propTypes["default"].arrayOf(_propTypes["default"].shape({ children: _propTypes["default"].string.isRequired, key: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]).isRequired }))]), /** * Offset from the activator to apply to the position of the popup. * * @type {('none'|'overlap'|'small')} * @default 'overlap' * @public */ offset: _propTypes["default"].oneOf(['none', 'overlap', 'small']), /** * Called when the user has attempted to close the popup. * * This may occur either when the close button is clicked or when spotlight focus * moves outside the boundary of the popup. Setting `spotlightRestrict` to `'self-only'` * will prevent Spotlight focus from leaving the popup. * * @type {Function} * @public */ onClose: _propTypes["default"].func, /** * Called when the popup is opened. * * @type {Function} * @public */ onOpen: _propTypes["default"].func, /** * CSS class name to pass to the * {@link sandstone/ContextualPopupDecorator.ContextualPopup|ContextualPopup}. * * This is commonly used to set width and height of the popup. * * @type {String} * @public */ popupClassName: _propTypes["default"].string, /** * An object containing properties to be passed to popup component. * * @type {Object} * @public */ popupProps: _propTypes["default"].object, /** * Width of the Popup component. * * When `'auto'` the popup will match the activator's width when `direction` is * `'below'` or `'above'` or a width specified in `popupClassName`. * * @type {('auto'|'large'|'small')} * @default 'auto' * @private */ popupWidth: _propTypes["default"].oneOf(['auto', 'large', 'small']), /** * Set the type of scrim to use * * @type {('holepunch'|'translucent'|'transparent'|'none')} * @default 'holepunch' * @private */ scrimType: _propTypes["default"].oneOf(['holepunch', 'translucent', 'transparent', 'none']), /** * Restricts or prioritizes spotlight navigation. * * Allowed values are: * * `'none'` - Spotlight can move freely within and beyond the popup * * `'self-first'` - Spotlight should prefer components within the popup over * components beyond the popup, or * * `'self-only'` - Spotlight can only be set within the popup * * @type {('none'|'self-first'|'self-only')} * @default 'self-only' * @public */ spotlightRestrict: _propTypes["default"].oneOf(['none', 'self-first', 'self-only']) }, defaultProps: { direction: 'below right', offset: 'overlap', popupWidth: 'auto', scrimType: 'holepunch', spotlightRestrict: 'self-only' }, handlers: { onOpen: (0, _handle.handle)((0, _handle.forward)('onClick'), (0, _handle.forProp)('open', false), (0, _handle.forward)('onOpen')) }, styles: { css: _ContextualMenuDecoratorModule["default"] }, computed: { // expect we'll be able to drop this when we add the private popupComponent // implementation with the Repeater for the items since the popup class could be set // on the component by itself popupClassName: function popupClassName(_ref2) { var popupWidth = _ref2.popupWidth, _popupClassName = _ref2.popupClassName, styler = _ref2.styler; var sizeClass = popupWidth !== 'auto' && popupWidth; return styler.join('popup', 'container', _popupClassName, sizeClass); }, popupComponent: function popupComponent(_ref3) { var menuItems = _ref3.menuItems; return menuItems && menuItems.length > MAX_VISIBLE_MENU_ITEMS ? ScrollingRepeater : _Repeater["default"]; }, popupProps: function popupProps(_ref4) { var menuItems = _ref4.menuItems, _popupProps = _ref4.popupProps; return _objectSpread({ 'aria-live': null, children: menuItems, childComponent: _Item["default"], className: _ContextualMenuDecoratorModule["default"].innerContainer, itemProps: { className: _ContextualMenuDecoratorModule["default"].item, size: 'small' }, component: 'div', role: null }, _popupProps); } }, render: function render(_ref5) { var onOpen = _ref5.onOpen, popupProps = _ref5.popupProps, rest = _objectWithoutProperties(_ref5, _excluded2); delete rest.menuItems; delete rest.onOpen; delete rest.popupWidth; return /*#__PURE__*/(0, _jsxRuntime.jsx)(Component, _objectSpread(_objectSpread({}, rest), {}, { onClick: onOpen, popupProps: popupProps })); } }); }); /** * Wraps a component to display a contextual popup menu. * * @hoc * @memberof sandstone/ContextualMenuDecorator * @mixes ui/Toggleable.Toggleable * @mixes sandstone/Skinnable.Skinnable * @mixes sandstone/ContextualPopupDecorator.ContextualPopupDecorator * @public */ var ContextualMenuDecorator = exports.ContextualMenuDecorator = (0, _compose["default"])((0, _Toggleable["default"])({ activate: 'onOpen', deactivate: 'onClose', prop: 'open', toggle: null }), ContextualMenuDecoratorBase); var _default = exports["default"] = ContextualMenuDecorator;