UNPKG

@enact/sandstone

Version:

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

701 lines (694 loc) 26.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = exports.InputPopupBase = exports.InputPopup = exports.InputDecorator = exports.InputBase = exports.Input = void 0; var _handle = require("@enact/core/handle"); var _deprecate = _interopRequireDefault(require("@enact/core/internal/deprecate")); var _kind = _interopRequireDefault(require("@enact/core/kind")); var _util = require("@enact/core/util"); var _spotlight = _interopRequireDefault(require("@enact/spotlight")); var _SpotlightContainerDecorator = require("@enact/spotlight/SpotlightContainerDecorator"); var _AnnounceDecorator = require("@enact/ui/AnnounceDecorator"); var _Changeable = _interopRequireDefault(require("@enact/ui/Changeable")); var _Pure = _interopRequireDefault(require("@enact/ui/internal/Pure")); var _Toggleable = _interopRequireDefault(require("@enact/ui/Toggleable")); var _Layout = _interopRequireWildcard(require("@enact/ui/Layout")); var _classnames = _interopRequireDefault(require("classnames")); var _propTypes = _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 _Popup = _interopRequireDefault(require("../Popup")); var _Skinnable = _interopRequireDefault(require("../Skinnable")); var _Heading = _interopRequireDefault(require("../Heading")); var _NumberField = _interopRequireDefault(require("./NumberField")); var _InputField = _interopRequireDefault(require("./InputField")); var _util2 = require("./util"); var _InputModule = _interopRequireDefault(require("./Input.module.css")); var _jsxRuntime = require("react/jsx-runtime"); var _excluded = ["announce", "backButtonAriaLabel", "children", "css", "defaultValue", "disabled", "inputFieldSpotlightId", "noBackButton", "noSubmitButton", "numberInputField", "onBeforeChange", "onClose", "onNumberComplete", "onInputKeyDown", "onShow", "open", "placeholder", "popupAriaLabel", "popupClassName", "popupType", "size", "subtitle", "title", "type", "value", "maxLength", "minLength"], _excluded2 = ["announce", "buttonAriaLabel", "buttonLabel", "type", "size", "disabled", "value", "placeholder", "onClick", "className", "style"]; 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 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 prepareInputEventPayload = function prepareInputEventPayload(ev) { return { value: ev.target.value }; }; var isPasswordType = function isPasswordType(type) { return type && type.includes('password'); }; /** * Base component for providing text input in the form of a popup without button. * * @class InputPopupBase * @memberof sandstone/Input * @ui * @public */ var InputPopupBase = exports.InputPopupBase = (0, _kind["default"])({ name: 'InputPopup', propTypes: /** @lends sandstone/Input.InputPopupBase.prototype */{ /** * Passed by AnnounceDecorator for accessibility. * * @type {Object} * @public */ announce: _propTypes["default"].func, /** * Sets the hint string read when focusing the back button. * * @type {String} * @default 'go to previous' * @public */ backButtonAriaLabel: _propTypes["default"].string, /** * Customize component style * * @type {Object} * @public */ css: _propTypes["default"].object, /** * Initial value of the input. * * This value is used for setting the `defaultValue` of the `InputField`. * @see {@link sandstone/Input.InputField} * @type {String|Number} * @public */ defaultValue: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]), /** * Disables the input popup. * * @type {Boolean} * @public */ disabled: _propTypes["default"].bool, /** * Sets spotlightId to InputField. * * @type {String} * @public */ inputFieldSpotlightId: _propTypes["default"].string, /** * Indicates the value is invalid and shows * {@link sandstone/Input.InputPopupBase.invalidMessage|invalidMessage}, if set. * * @type {Boolean} * @public */ invalid: _propTypes["default"].bool, /** * The tooltip text to be displayed when the input is * {@link sandstone/Input.InputPopupBase.invalid|invalid}. * * If this value is *falsy*, the tooltip will be shown with the default message. * * @type {String} * @default 'Please enter a valid value.' * @public */ invalidMessage: _propTypes["default"].string, /** * Set the length of number input field. * * Sets the amount of numbers this field will collect. Any number between 1 and 6 * (inclusive) will render individual number cells, greater than 6 will render a single box * with numbers in it. This only has an effect on `'number'` and `'passwordnumber'` `type` * and when `numberInputField` is `'auto'`. * * This value will override `minLength` and `maxLength`. * * @type {Number} * @public */ length: _propTypes["default"].number, /** * The maximum length of number input fields. * * Overridden by `length` value. * * @type {Number} * @default 4 * @public */ maxLength: _propTypes["default"].number, /** * The minimum length of number input fields. * * Overridden by `length` value. * * When smaller than `maxLength`, number type inputs will show a submit button and will not * auto-submit when the length reaches `maxLength`. Defaults to the `maxLength` value. * * @type {Number} * @public */ minLength: _propTypes["default"].number, /** * Omits the back button. * * @type {Boolean} * @public */ noBackButton: _propTypes["default"].bool, /** * Omits the submit button. * * @type {Boolean} * @public */ noSubmitButton: _propTypes["default"].bool, /** * The type of numeric input to use. * * The default is to display separated digits when `length` is less than `7`. If `field` is * set, a standard `InputField` will be used instead of the normal number input. * * This has no effect on other {@link sandstone/Input.InputPopupBase.type|types}. * * @type {('auto'|'separated'|'joined'|'field')} * @default 'auto' * @public */ numberInputField: _propTypes["default"].oneOf(['auto', 'separated', 'joined', 'field']), /** * Called before the input value is changed. * * The change can be prevented by calling `preventDefault` on the event. * * @type {Function} * @public */ onBeforeChange: _propTypes["default"].func, /** * Called when the input value is changed. * * @type {Function} * @public */ onChange: _propTypes["default"].func, /** * Called when the popup is closed. * * @type {Function} * @public */ onClose: _propTypes["default"].func, /** * Called when input is complete. * * @type {Function} * @public */ onComplete: _propTypes["default"].func, /** * Called when the popup is opened. * * @type {Function} * @public */ onOpenPopup: _propTypes["default"].func, /** * Opens the popup. * * @type {Boolean} * @public */ open: _propTypes["default"].bool, /** * Text displayed when value is not set. * * @type {String} * @public */ placeholder: _propTypes["default"].string, /** * The "aria-label" for the popup when opened. * * @type {String} * @public */ popupAriaLabel: _propTypes["default"].string, /** * Type of popup. * * @type {('fullscreen'|'overlay')} * @default 'fullscreen' * @public */ popupType: _propTypes["default"].oneOf(['fullscreen', 'overlay']), /** * Size of the input field. * * @type {('large'|'small')} * @default 'small' * @public */ size: _propTypes["default"].oneOf(['small', 'large']), /** * Subtitle below the title of popup. * * @type {String} * @default '' * @public */ subtitle: _propTypes["default"].string, /** * Title text of popup. * * @type {String} * @default '' * @public */ title: _propTypes["default"].string, /** * Type of the input. * * @type {('text'|'password'|'number'|'passwordnumber'|'url'|'tel'|'passwordtel')} * @default 'text' * @public */ type: _propTypes["default"].oneOf(['text', 'password', 'number', 'passwordnumber', 'url', 'tel', 'passwordtel']), /** * Initial value of the input. * * This value is used for setting the `defaultValue` of the `InputField`. * @see {@link sandstone/Input.InputField} * @type {String|Number} * @public * @deprecated Will be removed in 3.0.0. Use {@link sandstone/Input.InputPopupBase.defaultValue|defaultValue} instead. */ value: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]) }, defaultProps: { defaultValue: '', popupType: 'fullscreen', numberInputField: 'auto', size: 'small', subtitle: '', title: '', type: 'text', value: '' // value is provided by Changeable, but will be null if defaultValue wasn't specified by the consumer }, styles: { css: _InputModule["default"], className: 'input', publicClassNames: ['textField'] }, handlers: { onShow: (0, _handle.handle)((0, _handle.forwardCustom)('onShow'), function (ev, _ref) { var type = _ref.type; return !type.includes('number'); }, function () { return _spotlight["default"].setPointerMode(false); }), onNumberComplete: (0, _handle.handle)(function (ev, props) { setTimeout(function () { (0, _handle.forward)('onComplete', ev, props); (0, _handle.forward)('onClose', ev, props); }, 250); return true; }), onInputKeyDown: (0, _handle.handle)((0, _handle.forKey)('enter'), // Ensure that the source of the enter is the <input> function (_ref2) { var target = _ref2.target; return target.nodeName === 'INPUT'; }, (0, _handle.forwardCustom)('onComplete', prepareInputEventPayload), (0, _handle.forwardCustom)('onClose')) }, computed: { maxLength: function maxLength(_ref3) { var length = _ref3.length, _maxLength = _ref3.maxLength; return length || _maxLength; }, minLength: function minLength(_ref4) { var length = _ref4.length, maxLength = _ref4.maxLength, _minLength = _ref4.minLength; if (length) return length; if (_minLength != null) return _minLength; if (maxLength != null) return maxLength; return _util2.DEFAULT_LENGTH; }, popupClassName: function popupClassName(_ref5) { var popupType = _ref5.popupType, type = _ref5.type, styler = _ref5.styler; return styler.join('popup', popupType, type); } }, render: function render(_ref6) { var announce = _ref6.announce, backButtonAriaLabel = _ref6.backButtonAriaLabel, children = _ref6.children, css = _ref6.css, defaultValue = _ref6.defaultValue, disabled = _ref6.disabled, inputFieldSpotlightId = _ref6.inputFieldSpotlightId, noBackButton = _ref6.noBackButton, noSubmitButton = _ref6.noSubmitButton, numberInputField = _ref6.numberInputField, onBeforeChange = _ref6.onBeforeChange, onClose = _ref6.onClose, onNumberComplete = _ref6.onNumberComplete, onInputKeyDown = _ref6.onInputKeyDown, onShow = _ref6.onShow, open = _ref6.open, placeholder = _ref6.placeholder, popupAriaLabel = _ref6.popupAriaLabel, popupClassName = _ref6.popupClassName, popupType = _ref6.popupType, size = _ref6.size, subtitle = _ref6.subtitle, title = _ref6.title, type = _ref6.type, value = _ref6.value, maxLength = _ref6.maxLength, minLength = _ref6.minLength, rest = _objectWithoutProperties(_ref6, _excluded); /* istanbul ignore next */ if (value) { (0, _deprecate["default"])({ name: 'sandstone/Input.InputPopupBase.value', replacedBy: 'sandstone/Input.InputPopupBase.defaultValue', until: '3.0.0' }); } var id = "inputPopup"; var ariaLabelledBy = popupAriaLabel ? null : "".concat(id, "_title ").concat(id, "_subtitle"); var inputProps = (0, _util2.extractInputFieldProps)(_objectSpread({ disabled: disabled }, rest)); var numberMode = numberInputField !== 'field' && (type === 'number' || type === 'passwordnumber'); // Set up the back button var backButton = !noBackButton ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button["default"], { "aria-label": backButtonAriaLabel == null ? (0, _$L["default"])('go to previous') : backButtonAriaLabel, className: css.back, disabled: disabled, icon: "arrowhookleft", iconFlip: "auto", onClick: onClose, size: "small" }) : null; var heading = /*#__PURE__*/(0, _jsxRuntime.jsx)(_Heading["default"], { id: "".concat(id, "_title"), size: "title", marqueeOn: "render", alignment: "center", className: css.title, children: title }); delete rest.length; delete rest.onComplete; delete rest.onOpenPopup; return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { "aria-owns": id, className: css.inputPopupWrapper, children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Popup["default"], { id: id, "aria-label": popupAriaLabel, "aria-labelledby": ariaLabelledBy, onClose: onClose, onShow: onShow, position: popupType === 'fullscreen' ? 'fullscreen' : 'center', className: popupClassName, noAlertRole: true, noAnimation: true, open: open, role: "region", children: [popupType === 'fullscreen' ? backButton : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Layout["default"], { orientation: "vertical", align: "center ".concat(numberMode ? 'space-between' : ''), className: css.body, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_Layout.Cell, { shrink: true, className: css.titles, children: [popupType === 'fullscreen' ? heading : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [backButton, heading] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Heading["default"], { id: "".concat(id, "_subtitle"), size: "subtitle", marqueeOn: "render", alignment: "center", className: css.subtitle, children: subtitle })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Layout.Cell, { shrink: true, className: css.inputArea, children: numberMode ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_NumberField["default"], _objectSpread(_objectSpread({}, inputProps), {}, { announce: announce, maxLength: (0, _util2.limitNumberLength)(popupType, maxLength), minLength: (0, _util2.limitNumberLength)(popupType, minLength), defaultValue: defaultValue || value, onBeforeChange: onBeforeChange, onComplete: onNumberComplete, showKeypad: true, type: type === 'passwordnumber' ? 'password' : 'number', numberInputField: numberInputField, noSubmitButton: noSubmitButton })) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_InputField["default"], _objectSpread(_objectSpread({}, inputProps), {}, { className: (0, _classnames["default"])(css.textField, _SpotlightContainerDecorator.spotlightDefaultClass), css: css, maxLength: maxLength, minLength: minLength, size: size, autoFocus: true, type: type, defaultValue: defaultValue || value, placeholder: placeholder, onBeforeChange: onBeforeChange, onKeyDown: onInputKeyDown, spotlightId: inputFieldSpotlightId })) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Layout.Cell, { shrink: true, className: css.buttonArea, children: children })] })] }) }); } }); /** * Base component for providing text input in the form of a popup. * * @class InputBase * @memberof sandstone/Input * @ui * @public */ var InputBase = exports.InputBase = (0, _kind["default"])({ name: 'Input', propTypes: /** @lends sandstone/Input.InputBase.prototype */{ /** * Disables the button that activates the input popup. * * @type {Boolean} * @private */ announce: _propTypes["default"].func, /** * Disables the button that activates the input popup. * * @type {Boolean} * @public */ disabled: _propTypes["default"].bool, /** * Text displayed when value is not set. * * @type {String} * @default '-' * @public */ placeholder: _propTypes["default"].string, /** * The size of the input field. * * @type {('large'|'small')} * @default 'small' * @public */ size: _propTypes["default"].oneOf(['small', 'large']), /** * Type of the input. * * @type {('text'|'password'|'number'|'passwordnumber'|'url'|'tel'|'passwordtel')} * @default 'text' * @public */ type: _propTypes["default"].oneOf(['text', 'password', 'number', 'passwordnumber', 'url', 'tel', 'passwordtel']), /** * The value of the input. * * @see {@link sandstone/Input.InputField} * @type {String|Number} * @public */ value: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]) }, defaultProps: { placeholder: '-', type: 'text' }, handlers: { onClick: (0, _handle.handle)((0, _handle.forward)('onClick'), (0, _handle.forward)('onOpenPopup')) }, computed: { buttonAriaLabel: function buttonAriaLabel(_ref7) { var placeholder = _ref7.placeholder, type = _ref7.type, value = _ref7.value; if (value || value === 0) { type = isPasswordType(type) ? 'password' : type; return (0, _util2.calcAriaLabel)('', type, type === 'number' ? value.toString().split('') : value.toString()); } return (0, _util2.calcAriaLabel)('', null, placeholder); }, buttonLabel: function buttonLabel(_ref8) { var placeholder = _ref8.placeholder, type = _ref8.type, value = _ref8.value; if (value || value === 0) { return isPasswordType(type) ? (0, _util2.convertToPasswordFormat)(value.toString()) : value.toString(); } else { return placeholder; } } }, render: function render(_ref9) { var announce = _ref9.announce, buttonAriaLabel = _ref9.buttonAriaLabel, buttonLabel = _ref9.buttonLabel, type = _ref9.type, size = _ref9.size, disabled = _ref9.disabled, value = _ref9.value, placeholder = _ref9.placeholder, onClick = _ref9.onClick, className = _ref9.className, style = _ref9.style, rest = _objectWithoutProperties(_ref9, _excluded2); var ariaProps = (0, _util.extractAriaProps)(rest); return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.Fragment, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(InputPopupBase, _objectSpread({ announce: announce, type: type, size: size, disabled: disabled, defaultValue: value, placeholder: placeholder }, rest)), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button["default"], _objectSpread(_objectSpread({ size: size, disabled: disabled, className: className, style: style, onClick: onClick, "aria-label": buttonAriaLabel }, ariaProps), {}, { children: buttonLabel }))] }); } }); // eslint-disable-next-line no-shadow var AnnounceDecorator = function AnnounceDecorator(Wrapped) { return function AnnounceDecorator(props) { var _useAnnounce = (0, _AnnounceDecorator.useAnnounce)(), announce = _useAnnounce.announce, children = _useAnnounce.children; return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.Fragment, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(Wrapped, _objectSpread(_objectSpread({}, props), {}, { announce: announce })), children] }); }; }; /** * Sandstone specific item behaviors to apply to {@link sandstone/Input.InputBase|Input}. * * @class InputDecorator * @hoc * @memberof sandstone/Input * @mixes ui/Toggleable.Toggleable * @mixes ui/Changeable.Changeable * @mixes sandstone/Skinnable.Skinnable * @public */ var InputDecorator = exports.InputDecorator = (0, _compose["default"])(_Pure["default"], (0, _Toggleable["default"])({ activate: 'onOpenPopup', deactivate: 'onClose', prop: 'open' }), (0, _Changeable["default"])({ change: 'onComplete' }), AnnounceDecorator, _Skinnable["default"]); /** * Provides an input in the form of a popup. * * Usage: * ``` * <Input * onComplete={this.handleInputComplete} * placeholder="Placeholder" * subtitle="TitleBelow" * title="Title" * /> * ``` * * By default, `Input` maintains the state of its `value` property. Supply the `defaultValue` * property to control its initial value. If you wish to directly control updates to the * component, supply a value to `value` at creation time and update it in response to `onChange` * events. * * @class Input * @memberof sandstone/Input * @extends sandstone/Input.InputBase * @ui * @public */ var Input = exports.Input = InputDecorator(InputBase); /** * Provides an input popup without button. * * Usage: * ``` * <InputPopup * open={this.state.open} * onComplete={this.handleInputPopupComplete} * placeholder="Placeholder" * subtitle="Subtitle" * title="Title" * /> * ``` * * By default, `InputPopup` maintains the state of its `value` property. Supply the `defaultValue` * property to control its initial value. If you wish to directly control updates to the * component, supply a value to `value` at creation time and update it in response to `onChange` * events. * * @class InputPopup * @memberof sandstone/Input * @extends sandstone/Input.InputPopupBase * @ui * @public */ var InputPopup = exports.InputPopup = InputDecorator(InputPopupBase); var _default = exports["default"] = Input;