UNPKG

@enact/sandstone

Version:

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

596 lines (583 loc) 25.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = exports.HeaderBase = exports.Header = void 0; var _handle = require("@enact/core/handle"); var _propTypes = _interopRequireDefault(require("@enact/core/internal/prop-types")); var _kind = _interopRequireDefault(require("@enact/core/kind")); var _util = require("@enact/i18n/util"); var _SpotlightContainerDecorator = _interopRequireDefault(require("@enact/spotlight/SpotlightContainerDecorator")); var _Layout = require("@enact/ui/Layout"); var _Measurable = require("@enact/ui/Measurable"); var _resolution = require("@enact/ui/resolution"); var _Slottable = _interopRequireDefault(require("@enact/ui/Slottable")); var _ViewManager = _interopRequireWildcard(require("@enact/ui/ViewManager")); var _propTypes2 = _interopRequireDefault(require("prop-types")); var _compose = _interopRequireDefault(require("ramda/src/compose")); var _react = require("react"); var _$L = _interopRequireDefault(require("../internal/$L")); var _Button = _interopRequireDefault(require("../Button")); var _Heading = _interopRequireDefault(require("../Heading")); var _Skinnable = _interopRequireDefault(require("../Skinnable")); var _Panels = require("../internal/Panels"); var _util2 = require("../internal/Panels/util"); var _HeaderModule = _interopRequireDefault(require("./Header.module.css")); var _jsxRuntime = require("react/jsx-runtime"); var _excluded = ["backButtonAriaLabel", "backButtonAvailable", "backButtonBackgroundOpacity", "centered", "children", "closeButtonAriaLabel", "closeButtonBackgroundOpacity", "css", "noBackButton", "noCloseButton", "onBack", "onClose", "shadowed", "slotAbove", "slotAfter", "slotAfterRef", "slotBefore", "slotBeforeRef", "slotSize", "titleCell"]; function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _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(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } 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(arr) { if (Array.isArray(arr)) return arr; } 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; } 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); } var hasChildren = function hasChildren(children) { return _react.Children.toArray(children).filter(Boolean).length > 0; }; /** * A header component for a Panel with a `title` and `subtitle`, supporting several configurable * {@link ui/Slottable.Slottable|slots} for components. * * @class Header * @memberof sandstone/Panels * @ui * @public */ var HeaderBase = exports.HeaderBase = (0, _kind["default"])({ name: 'Header', propTypes: /** @lends sandstone/Panels.Header.prototype */{ /** * The animation arranger used to transition title and subtitle changes. * * Only supported when `type="wizard"`. * * @type {ui/ViewManager.Arranger} * @private */ arranger: _ViewManager.shape, /** * Sets the hint string read when focusing the back button. * * @type {String} * @default 'go to previous' * @public */ backButtonAriaLabel: _propTypes2["default"].string, /** * Informs Header that the back button as allowed to be shown. * * This does not represent whether it is showing, just whether it can or not. * * @type {Boolean} * @default false * @private */ backButtonAvailable: _propTypes2["default"].bool, /** * Background opacity of the application back button. * * @type {('opaque'|'transparent')} * @default 'transparent' * @public */ backButtonBackgroundOpacity: _propTypes2["default"].oneOf(['opaque', 'transparent']), /** * Centers the contents of the Header. * * This setting does not affect `slotBefore` or `slotAfter`. * * @type {Boolean} * @default false * @public */ centered: _propTypes2["default"].bool, /** * Children provided are added to the header-components area. * * A space for controls which live in the header, apart from the body of the panel view. * * @type {Element|Element[]} */ children: _propTypes2["default"].oneOfType([_propTypes2["default"].element, _propTypes2["default"].arrayOf(_propTypes2["default"].element)]), /** * Hint string read when focusing the application close button. * * @type {String} * @default 'Exit app' * @public */ closeButtonAriaLabel: _propTypes2["default"].string, /** * Background opacity of the application close button. * * @type {('opaque'|'transparent')} * @default 'transparent' * @public */ closeButtonBackgroundOpacity: _propTypes2["default"].oneOf(['opaque', 'transparent']), /** * Customizes the component by mapping the supplied collection of CSS class names to the * corresponding internal elements and states of this component. * * The following classes are supported: * * * `header` - The root class name * * @type {Object} * @public */ css: _propTypes2["default"].object, /** * Determines what triggers the header content to start its animation. * * @type {('focus'|'hover'|'render')} * @default 'render' * @public */ marqueeOn: _propTypes2["default"].oneOf(['focus', 'hover', 'render']), /** * Omits the back button. * * @type {Boolean} * @default false * @public */ noBackButton: _propTypes2["default"].bool, /** * Omits the close button. * * @type {Boolean} * @default false * @public */ noCloseButton: _propTypes2["default"].bool, /** * Omits the subtitle area. * * @type {Boolean} * @default false * @public */ noSubtitle: _propTypes2["default"].bool, /** * Called with cancel/back key events. * * @type {Function} * @public */ onBack: _propTypes2["default"].func, /** * Called when the app close button is clicked. * * @type {Function} * @public */ onClose: _propTypes2["default"].func, /** * Adds shadow to the text contents. * * @type {Boolean} * @public */ shadowed: _propTypes2["default"].bool, /** * A location for arbitrary elements to be placed above the title * * This is a {@link ui/Slottable.Slottable|slot}, so it can be used as a tag-name inside * this component. * * ``` * <Header> * <slotAbove><Button /></slotAbove> * <title>My Title</title> * </Header> * ``` * * @type {Node} * @public */ slotAbove: _propTypes2["default"].node, /** * A location for arbitrary elements to be placed to the right the title in LTR locales and * to the left in RTL locales * * This is a {@link ui/Slottable.Slottable|slot}, so it can be used as a tag-name inside * this component. * * ``` * <Header> * <title>My Title</title> * <slotAfter><Button /></slotAfter> * </Header> * ``` * * @type {Node} * @public */ slotAfter: _propTypes2["default"].node, /** * The method which receives the reference node to the slotAfter element, used to determine * the `slotSize`. * * @type {Function|Object} * @private */ slotAfterRef: _propTypes["default"].ref, /** * A location for arbitrary elements to be placed to the left the title in LTR locales and * to the right in RTL locales * * This is a {@link ui/Slottable.Slottable|slot}, so it can be used as a tag-name inside * this component. * * ``` * <Header> * <slotBefore><Button /></slotBefore> * <title>My Title</title> * </Header> * ``` * * @type {Node} * @public */ slotBefore: _propTypes2["default"].node, /** * The method which receives the reference node to the slotBefore element, used to determine * the `slotSize`. * * @type {Function|Object} * @private */ slotBeforeRef: _propTypes["default"].ref, /** * The size for slotBefore and slotAfter. * This size is set by HeaderMeasurementDecorator for consistent title centering. * * @type {String} * @private */ slotSize: _propTypes2["default"].string, /** * Text displayed below the title. * * This is a {@link ui/Slottable.Slottable|slot}, so it can be used as a tag-name inside * this component. * If {@link sandstone/Panels.Header.noSubtitle|noSubtitle} is `true`, this prop is ignored. * * @type {String|String[]} */ subtitle: _propTypes2["default"].oneOfType([_propTypes2["default"].string, _propTypes2["default"].arrayOf(_propTypes2["default"].string)]), /** * Subtitle id of the header. * * @type {String} * @private */ subtitleId: _propTypes2["default"].string, /** * Title of the header. * * This is a {@link ui/Slottable.Slottable|slot}, so it can be used as a tag-name inside * this component. * * Example: * ``` * <Header> * <title>Example Header Title</title> * <subtitle>The Adventure Continues</subtitle> * </Header> * ``` * * @type {String|String[]} */ title: _propTypes2["default"].oneOfType([_propTypes2["default"].string, _propTypes2["default"].arrayOf(_propTypes2["default"].string)]), /** * Title id of the header. * * @type {String} * @private */ titleId: _propTypes2["default"].string, /** * Set the type of header to be used. * * @type {('compact'|'mini'|'standard'|'wizard')} * @default 'standard' */ type: _propTypes2["default"].oneOf(['compact', 'mini', 'standard', 'wizard']) }, defaultProps: { marqueeOn: 'render', noSubtitle: false, type: 'standard' }, styles: { css: _HeaderModule["default"], className: 'header', publicClassNames: ['header'] }, computed: { className: function className(_ref) { var centered = _ref.centered, children = _ref.children, noSubtitle = _ref.noSubtitle, type = _ref.type, shadowed = _ref.shadowed, styler = _ref.styler, subtitle = _ref.subtitle; return styler.append({ centered: centered, noSubtitle: noSubtitle, shadowed: shadowed, // This likely doesn't need to be as verbose as it is, with the first 2 conditionals withChildren: hasChildren(children), withSubtitle: subtitle }, type); }, titleCell: function titleCell(_ref2) { var _slotBefore$props, _slotAfter$props, _slotBefore$props2, _slotAfter$props2; var arranger = _ref2.arranger, centered = _ref2.centered, css = _ref2.css, marqueeOn = _ref2.marqueeOn, noSubtitle = _ref2.noSubtitle, slotAfter = _ref2.slotAfter, slotBefore = _ref2.slotBefore, slotSize = _ref2.slotSize, subtitle = _ref2.subtitle, subtitleId = _ref2.subtitleId, title = _ref2.title, titleId = _ref2.titleId, type = _ref2.type; var direction = (0, _util.isRtlText)(title) || (0, _util.isRtlText)(subtitle) ? 'rtl' : 'ltr'; var titleHeading = /*#__PURE__*/(0, _jsxRuntime.jsx)(_Heading["default"], _objectSpread(_objectSpread({}, centered ? { slotSize: slotSize } : {}), {}, { id: titleId, size: "title", spacing: "auto", marqueeOn: marqueeOn, forceDirection: direction, alignment: centered ? 'center' : null, className: css.title, children: type === 'wizard' && (slotBefore !== null && slotBefore !== void 0 && (_slotBefore$props = slotBefore.props) !== null && _slotBefore$props !== void 0 && _slotBefore$props.visible || slotAfter !== null && slotAfter !== void 0 && (_slotAfter$props = slotAfter.props) !== null && _slotAfter$props !== void 0 && _slotAfter$props.visible) && slotSize === '0rem' ? ' ' : title })); var subtitleHeading = /*#__PURE__*/(0, _jsxRuntime.jsx)(_Heading["default"], _objectSpread(_objectSpread({}, centered ? { slotSize: slotSize } : {}), {}, { id: subtitleId, size: "subtitle", spacing: "auto", marqueeDisabled: type === 'wizard', marqueeOn: marqueeOn, forceDirection: direction, alignment: centered ? 'center' : null, className: css.subtitle, children: type === 'wizard' && (slotBefore !== null && slotBefore !== void 0 && (_slotBefore$props2 = slotBefore.props) !== null && _slotBefore$props2 !== void 0 && _slotBefore$props2.visible || slotAfter !== null && slotAfter !== void 0 && (_slotAfter$props2 = slotAfter.props) !== null && _slotAfter$props2 !== void 0 && _slotAfter$props2.visible) && slotSize === '0rem' ? ' ' : subtitle })); // WizardPanels uses an animated title but that isn't supported for other types if (arranger && type === 'wizard') { return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Layout.Cell, { className: css.titleCell, component: _ViewManager["default"], arranger: arranger, duration: 500, index: 0, children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { className: css.titleContainer, children: [titleHeading, noSubtitle ? null : subtitleHeading] }, title + subtitle) }); } return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Layout.Cell, { className: css.titleCell, children: [titleHeading, noSubtitle ? null : subtitleHeading] }); } }, handlers: { onBack: (0, _handle.forwardCustom)('onBack'), onClose: (0, _handle.forwardCustom)('onClose') }, render: function render(_ref3) { var backButtonAriaLabel = _ref3.backButtonAriaLabel, backButtonAvailable = _ref3.backButtonAvailable, backButtonBackgroundOpacity = _ref3.backButtonBackgroundOpacity, centered = _ref3.centered, children = _ref3.children, closeButtonAriaLabel = _ref3.closeButtonAriaLabel, closeButtonBackgroundOpacity = _ref3.closeButtonBackgroundOpacity, css = _ref3.css, noBackButton = _ref3.noBackButton, noCloseButton = _ref3.noCloseButton, onBack = _ref3.onBack, onClose = _ref3.onClose, shadowed = _ref3.shadowed, slotAbove = _ref3.slotAbove, slotAfter = _ref3.slotAfter, slotAfterRef = _ref3.slotAfterRef, slotBefore = _ref3.slotBefore, slotBeforeRef = _ref3.slotBeforeRef, slotSize = _ref3.slotSize, titleCell = _ref3.titleCell, rest = _objectWithoutProperties(_ref3, _excluded); delete rest.arranger; delete rest.marqueeOn; delete rest.noSubtitle; delete rest.subtitle; delete rest.subtitleId; delete rest.title; delete rest.titleId; delete rest.type; // Set up the back button var backButton = backButtonAvailable && !noBackButton ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button["default"], { "aria-label": backButtonAriaLabel == null ? (0, _$L["default"])('go to previous') : backButtonAriaLabel, backgroundOpacity: backButtonBackgroundOpacity, className: css.back, icon: "arrowhookleft", iconFlip: "auto", onClick: onBack, shadowed: shadowed, size: "small" }) : null; // Set up the close button var closeButton = !noCloseButton ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button["default"], { "aria-label": closeButtonAriaLabel == null ? (0, _$L["default"])('Exit app') : closeButtonAriaLabel, backgroundOpacity: closeButtonBackgroundOpacity, className: css.close, icon: "closex", onClick: onClose, shadowed: shadowed, size: "small" }) : null; // Only provide the synced cell size if the title should be centered, beyond that case, // the cell sizes don't need to be synced. var syncCellSize = centered ? slotSize : null; // Hide slots for the first render to avoid unexpected positioning when 'centered' is given. // After the first render, HeaderMeasurementDecorator measures widths of slots and set right 'slotSize'. var hideSlots = { opacity: centered && slotSize === '0rem' ? '0' : null }; // The side Cells are always present, even if empty, to support the measurement ref. return /*#__PURE__*/(0, _jsxRuntime.jsxs)("header", _objectSpread(_objectSpread({}, rest), {}, { children: [slotAbove ? /*#__PURE__*/(0, _jsxRuntime.jsx)("nav", { className: css.slotAbove, children: slotAbove }) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Layout.Row, { className: css.titlesRow, align: "center", children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Layout.Cell, { className: css.slotBefore, shrink: !syncCellSize, size: syncCellSize, style: hideSlots, children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { ref: slotBeforeRef, className: css.slotSizer, children: [backButton, slotBefore] }) }), titleCell, /*#__PURE__*/(0, _jsxRuntime.jsx)(_Layout.Cell, { className: css.slotAfter, shrink: !syncCellSize, size: syncCellSize, style: hideSlots, children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", { ref: slotAfterRef, className: css.slotSizer, children: [slotAfter, closeButton] }) })] }), hasChildren(children) ? /*#__PURE__*/(0, _jsxRuntime.jsx)("nav", { className: css.slotBelow, children: children }) : null] })); } }); // Customized ContextAsDefaults HOC to incorporate the backButtonAvailable prop feature var ContextAsDefaultsHeader = function ContextAsDefaultsHeader(Wrapped) { // eslint-disable-next-line no-shadow function ContextAsDefaultsHeader(props) { var _useContextAsDefaults = (0, _util2.useContextAsDefaults)(props), contextProps = _useContextAsDefaults.contextProps, provideContextAsDefaults = _useContextAsDefaults.provideContextAsDefaults; var _useContext = (0, _react.useContext)(_Panels.PanelsStateContext), panelsType = _useContext.type; var index = props['data-index']; var backButtonAvailable = index > 0 && panelsType !== 'wizard' || panelsType === 'flexiblePopup'; return provideContextAsDefaults( /*#__PURE__*/(0, _jsxRuntime.jsx)(Wrapped, _objectSpread(_objectSpread(_objectSpread({}, contextProps), props), {}, { backButtonAvailable: backButtonAvailable }))); } ContextAsDefaultsHeader.propTypes = { /** * Used internally to render back button. * * @type {Number} * @private */ 'data-index': _propTypes2["default"].number }; return ContextAsDefaultsHeader; }; var HeaderMeasurementDecorator = function HeaderMeasurementDecorator(Wrapped) { return function HeaderMeasurementDecorator(props) { // eslint-disable-line no-shadow var _ref4 = (0, _Measurable.useMeasurable)() || {}, slotBeforeRef = _ref4.ref, _ref4$measurement = _ref4.measurement, _ref4$measurement2 = _ref4$measurement === void 0 ? {} : _ref4$measurement, _ref4$measurement2$wi = _ref4$measurement2.width, slotBeforeWidth = _ref4$measurement2$wi === void 0 ? 0 : _ref4$measurement2$wi; var _ref5 = (0, _Measurable.useMeasurable)() || {}, slotAfterRef = _ref5.ref, _ref5$measurement = _ref5.measurement, _ref5$measurement2 = _ref5$measurement === void 0 ? {} : _ref5$measurement, _ref5$measurement2$wi = _ref5$measurement2.width, slotAfterWidth = _ref5$measurement2$wi === void 0 ? 0 : _ref5$measurement2$wi; var _useState = (0, _react.useState)({}), _useState2 = _slicedToArray(_useState, 2), _useState2$ = _useState2[0], slotSize = _useState2$.slotSize, prevSlotBeforeWidth = _useState2$.prevSlotBeforeWidth, prevSlotAfterWidth = _useState2$.prevSlotAfterWidth, setSlotSize = _useState2[1]; // If the slot width has changed, re-run this. if (slotBeforeWidth !== prevSlotBeforeWidth || slotAfterWidth !== prevSlotAfterWidth) { var largestSlotSize = Math.max(slotBeforeWidth, slotAfterWidth); // And only do this the largest slot is a different value this time around. if (slotSize !== largestSlotSize) { setSlotSize({ slotSize: largestSlotSize, prevSlotBeforeWidth: slotBeforeWidth, prevSlotAfterWidth: slotAfterWidth }); } } var measurableProps = { slotBeforeRef: slotBeforeRef, slotAfterRef: slotAfterRef, slotSize: (0, _resolution.unit)(slotSize, 'rem') }; return /*#__PURE__*/(0, _jsxRuntime.jsx)(Wrapped, _objectSpread(_objectSpread({}, props), measurableProps)); }; }; var HeaderDecorator = (0, _compose["default"])(_SpotlightContainerDecorator["default"], (0, _Slottable["default"])({ slots: ['title', 'subtitle', 'slotAbove', 'slotAfter', 'slotBefore'] }), ContextAsDefaultsHeader, HeaderMeasurementDecorator, _Skinnable["default"]); // Note that we only export this (even as HeaderBase). HeaderBase is not useful on its own. var Header = exports.Header = HeaderDecorator(HeaderBase); // Set up Header so when it's used in a slottable layout (like Panel), it is automatically // recognized as this specific slot. Header.defaultSlot = 'header'; var _default = exports["default"] = Header;