UNPKG

@enact/sandstone

Version:

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

945 lines (939 loc) 41.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Picker = void 0; Object.defineProperty(exports, "PickerItem", { enumerable: true, get: function get() { return _PickerItem["default"]; } }); exports["default"] = void 0; var _classnames3 = _interopRequireDefault(require("classnames")); var _handle = require("@enact/core/handle"); var _propTypes = _interopRequireDefault(require("@enact/core/internal/prop-types")); var _keymap = require("@enact/core/keymap"); var _platform = _interopRequireDefault(require("@enact/core/platform")); var _usePublicClassNames = require("@enact/core/usePublicClassNames"); var _util = require("@enact/core/util"); var _ForwardRef = _interopRequireDefault(require("@enact/ui/ForwardRef")); var _IdProvider = _interopRequireDefault(require("@enact/ui/internal/IdProvider")); var _Layout = _interopRequireWildcard(require("@enact/ui/Layout")); var _Touchable = _interopRequireDefault(require("@enact/ui/Touchable")); var _ViewManager = require("@enact/ui/ViewManager"); var _spotlight = _interopRequireWildcard(require("@enact/spotlight")); var _propTypes2 = _interopRequireDefault(require("prop-types")); var _react = require("react"); var _Skinnable = _interopRequireDefault(require("../../Skinnable")); var _$L = _interopRequireDefault(require("../$L")); var _validators = require("../validators"); var _util2 = require("../util"); var _PickerButton = _interopRequireDefault(require("./PickerButton")); var _SpottablePicker = _interopRequireDefault(require("./SpottablePicker")); var _PickerModule = _interopRequireDefault(require("./Picker.module.css")); var _jsxRuntime = require("react/jsx-runtime"); var _PickerItem = _interopRequireDefault(require("./PickerItem")); var _excluded = ["containerRef"], _excluded2 = ["aria-valuetext", "changedBy", "children", "css", "disabled", "id", "index", "joined", "max", "min", "onChange", "onSpotlightDisappear", "orientation", "reverse", "spotlightDisabled", "step", "value", "width", "wrap"]; 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 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 holdConfig = { events: [{ name: 'hold', time: 800 }] }; var isDown = (0, _keymap.is)('down'); var isEnter = (0, _keymap.is)('enter'); var isLeft = (0, _keymap.is)('left'); var isRight = (0, _keymap.is)('right'); var isUp = (0, _keymap.is)('up'); var DivComponent = function DivComponent(_ref) { var containerRef = _ref.containerRef, rest = _objectWithoutProperties(_ref, _excluded); return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", _objectSpread({ ref: containerRef }, rest)); }; DivComponent.propTypes = { /** * Called with the reference to the current node. * * @type {Object|Function} * @public */ containerRef: _propTypes["default"].ref }; var Div = (0, _Touchable["default"])((0, _ForwardRef["default"])({ prop: 'containerRef' }, DivComponent)); var SpottableDiv = (0, _Touchable["default"])((0, _ForwardRef["default"])({ prop: 'containerRef' }, _SpottablePicker["default"])); var PickerViewManager = (0, _util2.onlyUpdateForProps)(_ViewManager.ViewManager, ['index', 'children']); var wrapRange = function wrapRange(min, max, value) { if (value > max) { return min; } else if (value < min) { return max; } else { return value; } }; var selectIcon = function selectIcon(icon, v, h) { return function (props) { return props[icon] || (props.orientation === 'vertical' ? v : h); }; }; var selectIncIcon = selectIcon('incrementIcon', 'triangleup', 'triangleright'); var selectDecIcon = selectIcon('decrementIcon', 'triangledown', 'triangleleft'); // Set-up event forwarding var forwardBlur = (0, _handle.forward)('onBlur'), forwardFocus = (0, _handle.forward)('onFocus'), forwardKeyDown = (0, _handle.forward)('onKeyDown'), forwardKeyUp = (0, _handle.forward)('onKeyUp'), forwardWheel = (0, _handle.forward)('onWheel'); var allowedClassNames = ['picker', 'valueWrapper', 'joined', 'horizontal', 'vertical']; /** * The base component for {@link sandstone/internal/Picker.Picker}. * * @class Picker * @memberof sandstone/internal/Picker * @ui * @private */ var PickerBase = function PickerBase(props) { // Set to `true` onFocus and `false` onBlur to prevent setting aria-valuetext (which // will notify the user) when the component does not have focus var _useState = (0, _react.useState)(false), _useState2 = _slicedToArray(_useState, 2), active = _useState2[0], setActive = _useState2[1]; var _useState3 = (0, _react.useState)(0), _useState4 = _slicedToArray(_useState3, 2), pressed = _useState4[0], setPressed = _useState4[1]; var containerRef = (0, _react.useRef)(); var pickerButtonPressed = (0, _react.useRef)(0); var reverseTransition = (0, _react.useRef)(false); var ariaValueText = props['aria-valuetext'], _props$changedBy = props.changedBy, changedBy = _props$changedBy === void 0 ? 'enter' : _props$changedBy, children = props.children, customCss = props.css, disabled = props.disabled, id = props.id, index = props.index, joined = props.joined, max = props.max, min = props.min, onChange = props.onChange, onSpotlightDisappear = props.onSpotlightDisappear, _props$orientation = props.orientation, orientation = _props$orientation === void 0 ? 'horizontal' : _props$orientation, reverse = props.reverse, _props$spotlightDisab = props.spotlightDisabled, spotlightDisabled = _props$spotlightDisab === void 0 ? false : _props$spotlightDisab, _props$step = props.step, step = _props$step === void 0 ? 1 : _props$step, _props$value = props.value, value = _props$value === void 0 ? 0 : _props$value, width = props.width, wrap = props.wrap, rest = _objectWithoutProperties(props, _excluded2); var css = (0, _usePublicClassNames.usePublicClassNames)({ componentCss: _PickerModule["default"], customCss: customCss, publicClassNames: allowedClassNames }); var incrementIcon = selectIncIcon(props); var decrementIcon = selectDecIcon(props); var horizontal = orientation === 'horizontal'; var isHorizontalJoinedEnter = horizontal && joined && changedBy === 'enter'; var showIndicators = isHorizontalJoinedEnter && Array.isArray(children) && children.length > 1; var voiceProps = (0, _util2.extractVoiceProps)(rest); var voiceLabelsExt = voiceProps['data-webos-voice-labels-ext']; var Component; var arranger = horizontal ? _ViewManager.SlideLeftArranger : _ViewManager.SlideTopArranger; var noAnimation = props.noAnimation || disabled; var sizingPlaceholder = null; var clearPressedState = (0, _react.useCallback)(function () { pickerButtonPressed.current = 0; setPressed(0); }, []); var emulateMouseUp = (0, _react.useMemo)(function () { return new _util.Job(clearPressedState, 175); }, [clearPressedState]); var handleVoice = (0, _react.useCallback)(function (ev) { var voiceIndex = ev && ev.detail && typeof ev.detail.matchedIndex !== 'undefined' && Number(ev.detail.matchedIndex); if (Number.isInteger(voiceIndex)) { var voiceValue = min + voiceIndex; if (onChange && voiceValue >= min && voiceValue <= max && voiceValue !== value) { (0, _handle.forwardCustom)('onChange', function () { return { value: voiceValue }; })(ev, props); ev.preventDefault(); } } }, [max, min, onChange, props, value]); var computeNextValue = (0, _react.useCallback)(function (delta) { var horizontalJoined = orientation === 'horizontal' && joined && changedBy === 'enter'; var shouldWrap = horizontalJoined || wrap; return shouldWrap ? wrapRange(min, max, value + delta) : (0, _util.clamp)(min, max, value + delta); }, [changedBy, joined, orientation, min, max, value, wrap]); var adjustDirection = (0, _react.useCallback)(function (dir) { return reverse ? -dir : dir; }, [reverse]); var hasReachedBound = (0, _react.useCallback)(function (delta) { return computeNextValue(adjustDirection(delta)) === value; }, [adjustDirection, computeNextValue, value]); var setTransitionDirection = (0, _react.useCallback)(function (dir) { // change the transition direction based on the button press reverseTransition.current = !(dir > 0); }, []); var updateValue = (0, _react.useCallback)(function (dir) { dir = adjustDirection(dir); setTransitionDirection(dir); if (!disabled && onChange) { var updatedValue = computeNextValue(dir * step); (0, _handle.forwardCustom)('onChange', function () { return { value: updatedValue }; })(null, props); } }, [adjustDirection, setTransitionDirection, disabled, onChange, computeNextValue, step, props]); var setPressedState = (0, _react.useCallback)(function (pressedValue) { if (joined) { setPressed(pressedValue); } }, [joined]); var handleDecrement = (0, _react.useCallback)(function () { if (!hasReachedBound(-step)) { updateValue(-1); setPressedState(-1); } }, [hasReachedBound, setPressedState, step, updateValue]); var handleIncrement = (0, _react.useCallback)(function () { if (!hasReachedBound(step)) { updateValue(1); setPressedState(1); } }, [hasReachedBound, setPressedState, step, updateValue]); var throttleWheelDec = (0, _react.useMemo)(function () { return new _util.Job(handleDecrement, 100); }, [handleDecrement]); var throttleWheelInc = (0, _react.useMemo)(function () { return new _util.Job(handleIncrement, 100); }, [handleIncrement]); var handleWheel = (0, _react.useCallback)(function (ev) { forwardWheel(ev, props); var isContainerSpotted = containerRef.current === _spotlight["default"].getCurrent(); if (isContainerSpotted) { var dir = -Math.sign(ev.deltaY); // We'll sometimes get a 0/-0 wheel event we need to ignore or the wheel event has reached // the bounds of the picker if (dir && !hasReachedBound(step * dir)) { // fire the onChange event if (dir > 0) { throttleWheelInc.throttle(); } else if (dir < 0) { throttleWheelDec.throttle(); } // simulate mouse down setPressedState(dir); // set a timer to simulate the mouse up emulateMouseUp.start(); // prevent the default scroll behavior to avoid bounce back ev.preventDefault(); ev.stopPropagation(); } } }, [emulateMouseUp, hasReachedBound, props, setPressedState, step, throttleWheelDec, throttleWheelInc]); (0, _react.useEffect)(function () { var currentPicker = containerRef.current; if (_platform["default"].type === 'webos') currentPicker.addEventListener('webOSVoice', handleVoice); if (joined) { currentPicker.addEventListener('wheel', handleWheel); } else { currentPicker.removeEventListener('wheel', handleWheel); } return function () { if (joined) { currentPicker.removeEventListener('wheel', handleWheel); } if (_platform["default"].type === 'webos') { currentPicker.removeEventListener('webOSVoice', handleVoice); } }; }, [joined, handleVoice, handleWheel]); (0, _react.useEffect)(function () { return function () { emulateMouseUp.stop(); throttleWheelInc.stop(); throttleWheelDec.stop(); }; }, []); // eslint-disable-line react-hooks/exhaustive-deps var handleBlur = (0, _react.useCallback)(function (ev) { forwardBlur(ev, props); setActive(false); }, [props]); var handleFocus = (0, _react.useCallback)(function (ev) { forwardFocus(ev, props); setActive(true); }, [props]); var handleUp = (0, _react.useCallback)(function () { if (joined && (pickerButtonPressed.current !== 0 || pressed !== 0)) { emulateMouseUp.start(); } }, [emulateMouseUp, joined, pressed]); var setIncPickerButtonPressed = (0, _react.useCallback)(function () { pickerButtonPressed.current = 1; }, []); var handleDown = (0, _react.useCallback)(function () { if (joined && orientation === 'horizontal' && changedBy === 'enter') { setIncPickerButtonPressed(); } if (joined && pickerButtonPressed.current === 1) { handleIncrement(); if (orientation === 'vertical' || changedBy === 'arrow') { emulateMouseUp.start(); } } else if (joined && pickerButtonPressed.current === -1) { handleDecrement(); emulateMouseUp.start(); } }, [changedBy, emulateMouseUp, handleDecrement, handleIncrement, joined, orientation, setIncPickerButtonPressed]); var setDecPickerButtonPressed = (0, _react.useCallback)(function () { pickerButtonPressed.current = -1; }, []); var handleHold = (0, _react.useCallback)(function () { if (joined && pickerButtonPressed.current === 1) { handleIncrement(); } else if (joined && pickerButtonPressed.current === -1) { handleDecrement(); } }, [handleDecrement, handleIncrement, joined]); var handleKeyDown = (0, _react.useCallback)(function (ev) { var onSpotlightDown = props.onSpotlightDown, onSpotlightLeft = props.onSpotlightLeft, onSpotlightRight = props.onSpotlightRight, onSpotlightUp = props.onSpotlightUp; var keyCode = ev.keyCode; forwardKeyDown(ev, props); if (joined && !disabled) { var direction = (0, _spotlight.getDirection)(keyCode); var directions = { up: setIncPickerButtonPressed, down: setDecPickerButtonPressed, right: setIncPickerButtonPressed, left: setDecPickerButtonPressed }; var isVertical = orientation === 'vertical' && (isUp(keyCode) || isDown(keyCode)); var isHorizontal = orientation === 'horizontal' && changedBy === 'enter' && isEnter(keyCode); var isHorizontalArrow = orientation === 'horizontal' && changedBy === 'arrow' && (isRight(keyCode) || isLeft(keyCode)); if (isVertical || isHorizontalArrow) { directions[direction](); } else if (isHorizontal) { setIncPickerButtonPressed(); } else if (orientation === 'horizontal' && isDown(keyCode) && onSpotlightDown) { (0, _handle.forwardCustom)('onSpotlightDown')(ev, props); } else if (orientation === 'horizontal' && isUp(keyCode) && onSpotlightUp) { (0, _handle.forwardCustom)('onSpotlightUp')(ev, props); } else if (orientation === 'vertical' && isLeft(keyCode) && onSpotlightLeft) { (0, _handle.forwardCustom)('onSpotlightLeft')(ev, props); } else if (orientation === 'vertical' && isRight(keyCode) && onSpotlightRight) { (0, _handle.forwardCustom)('onSpotlightRight')(ev, props); } } }, [changedBy, disabled, joined, orientation, props, setDecPickerButtonPressed, setIncPickerButtonPressed]); var handleKeyUp = (0, _react.useCallback)(function (ev) { var keyCode = ev.keyCode; forwardKeyUp(ev, props); if (joined && !disabled) { var isVertical = orientation === 'vertical' && (isUp(keyCode) || isDown(keyCode)); var isHorizontal = orientation === 'horizontal' && isEnter(keyCode); var isHorizontalArrow = orientation === 'horizontal' && changedBy === 'arrow' && (isRight(keyCode) || isLeft(keyCode)); if (isVertical || isHorizontal || isHorizontalArrow) { pickerButtonPressed.current = 0; } } }, [changedBy, disabled, joined, orientation, props]); var handleDecKeyDown = (0, _react.useCallback)(function (ev) { var keyCode = ev.keyCode; var direction = (0, _spotlight.getDirection)(keyCode); if (direction) { if (!hasReachedBound(step) && (isRight(keyCode) && orientation === 'horizontal' || isUp(keyCode) && orientation === 'vertical')) { ev.preventDefault(); // prevent parent handler behavior (0, _handle.stop)(ev); // prevent default spotlight behavior (0, _handle.stopImmediate)(ev); // set the pointer mode to false on keydown _spotlight["default"].setPointerMode(false); _spotlight["default"].focus(containerRef.current.querySelector(".".concat(_PickerModule["default"].incrementer))); } else { (0, _handle.forwardCustom)("onSpotlight".concat((0, _util.cap)(direction)))(ev, props); } } }, [hasReachedBound, orientation, props, step]); var handleIncKeyDown = (0, _react.useCallback)(function (ev) { var keyCode = ev.keyCode; var direction = (0, _spotlight.getDirection)(keyCode); if (direction) { if (!hasReachedBound(step * -1) && (isLeft(keyCode) && orientation === 'horizontal' || isDown(keyCode) && orientation === 'vertical')) { ev.preventDefault(); // prevent parent handler behavior (0, _handle.stop)(ev); // prevent default spotlight behavior (0, _handle.stopImmediate)(ev); // set the pointer mode to false on keydown _spotlight["default"].setPointerMode(false); _spotlight["default"].focus(containerRef.current.querySelector(".".concat(_PickerModule["default"].decrementer))); } else { (0, _handle.forwardCustom)("onSpotlight".concat((0, _util.cap)(direction)))(ev, props); } } }, [hasReachedBound, orientation, props, step]); var determineClasses = (0, _react.useCallback)(function (cssProps, decrementerDisabledProp, incrementerDisabledProp) { var classNameVariable = props.className; return (0, _classnames3["default"])(cssProps.picker, cssProps[changedBy], cssProps[orientation], cssProps[width], _defineProperty(_defineProperty(_defineProperty({}, cssProps.joined, joined), cssProps.decrementing, !decrementerDisabledProp && pressed === -1), cssProps.incrementing, !incrementerDisabledProp && pressed === 1), classNameVariable); }, [changedBy, joined, orientation, pressed, props, width]); var calcValueText = (0, _react.useCallback)(function () { var _props$accessibilityH = props.accessibilityHint, accessibilityHint = _props$accessibilityH === void 0 ? '' : _props$accessibilityH; var valueTextVariable = value; // Sometimes this.props.value is not equal to node text content. For example, when `PM` // is shown in AM/PM picker, its value is `1` and its node.textContent is `PM`. In this // case, Screen readers should read `PM` instead of `1`. if (children && Array.isArray(children)) { valueTextVariable = children[index] ? children[index].props.children : value; } else if (children && children.props && !children.props.children) { valueTextVariable = children.props.children; } if (accessibilityHint) { valueTextVariable = "".concat(valueTextVariable, " ").concat(accessibilityHint); } return valueTextVariable; }, [children, index, props, value]); var calcButtonLabel = (0, _react.useCallback)(function (next, valueTextProp) { var decrementAriaLabel = props.decrementAriaLabel, incrementAriaLabel = props.incrementAriaLabel, _props$type = props.type, type = _props$type === void 0 ? 'string' : _props$type; var titleText = props.title ? props.title + ' ' : ''; var label; if (orientation === 'vertical') { label = next ? decrementAriaLabel : incrementAriaLabel; } else { label = next ? incrementAriaLabel : decrementAriaLabel; } if (label != null) { return titleText + label; } if (type === 'number') { return titleText + "".concat(valueTextProp, " ").concat(next ? (0, _$L["default"])('press ok button to increase the value') : (0, _$L["default"])('press ok button to decrease the value')); } else { return titleText + "".concat(valueTextProp, " ").concat(next ? (0, _$L["default"])('next item') : (0, _$L["default"])('previous item')); } }, [orientation, props]); var calcDecrementLabel = (0, _react.useCallback)(function (valueTextProp) { return !joined ? calcButtonLabel(reverse, valueTextProp) : null; }, [calcButtonLabel, joined, reverse]); var calcIncrementLabel = (0, _react.useCallback)(function (valueTextProp) { return !joined ? calcButtonLabel(!reverse, valueTextProp) : null; }, [calcButtonLabel, joined, reverse]); var calcAriaLabel = (0, _react.useCallback)(function (valueTextProp) { var ariaLabel = props['aria-label']; var hint; if (orientation === 'horizontal') { hint = changedBy === 'arrow' ? (0, _$L["default"])('change a value with left right button') : (0, _$L["default"])('press ok button to change the value'); } else { hint = (0, _$L["default"])('change a value with up down button'); } if (!joined || ariaLabel != null) { return ariaLabel; } return "".concat(valueTextProp, " ").concat(hint); }, [changedBy, joined, orientation, props]); if (process.env.NODE_ENV !== "production") { (0, _validators.validateRange)(value, min, max, PickerBase.displayName); (0, _validators.validateStepped)(value, min, step, PickerBase.displayName); (0, _validators.validateStepped)(max, min, step, PickerBase.displayName, 'max'); } var reachedStart = hasReachedBound(step * -1); var decrementerDisabled = disabled || reachedStart; var reachedEnd = hasReachedBound(step); var incrementerDisabled = disabled || reachedEnd; var className = determineClasses(css, decrementerDisabled, incrementerDisabled); var valueText = ariaValueText != null ? ariaValueText : calcValueText(); var decrementerAriaControls = !incrementerDisabled ? id : null; var incrementerAriaControls = !decrementerDisabled ? id : null; var spottablePickerProps = {}; if (typeof width === 'number' && width > 0) { sizingPlaceholder = /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { "aria-hidden": true, className: css.sizingPlaceholder, children: '0'.repeat(width) }); } if (joined) { Component = SpottableDiv; spottablePickerProps.changedBy = changedBy; spottablePickerProps.onSpotlightDisappear = onSpotlightDisappear; spottablePickerProps.pickerOrientation = orientation; spottablePickerProps.spotlightDisabled = spotlightDisabled; } else { Component = Div; } delete rest['aria-label']; delete rest.accessibilityHint; delete rest.decrementAriaLabel; delete rest.decrementIcon; delete rest.incrementAriaLabel; delete rest.incrementIcon; delete rest.noAnimation; delete rest.onSpotlightDown; delete rest.onSpotlightLeft; delete rest.onSpotlightRight; delete rest.onSpotlightUp; delete rest.title; delete voiceProps['data-webos-voice-label']; delete voiceProps['data-webos-voice-labels']; delete voiceProps['data-webos-voice-labels-ext']; return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Layout["default"], _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, voiceProps), rest), {}, { align: "center space-around", "aria-controls": joined ? id : null, "aria-disabled": disabled, "aria-label": calcAriaLabel(valueText), className: className, component: Component, "data-webos-voice-intent": "Select", "data-webos-voice-labels-ext": voiceLabelsExt, disabled: disabled, holdConfig: holdConfig, inline: true, onBlur: handleBlur, onDown: handleDown, onFocus: handleFocus, onHold: handleHold, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, onUp: handleUp, onMouseLeave: clearPressedState, orientation: orientation, ref: containerRef }, spottablePickerProps), {}, { children: [isHorizontalJoinedEnter ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Layout.Cell, _objectSpread(_objectSpread({}, voiceProps), {}, { align: joined ? 'stretch' : null, "aria-controls": !joined ? incrementerAriaControls : null, "aria-label": calcIncrementLabel(valueText), className: css.incrementer, component: _PickerButton["default"], "data-webos-voice-label": joined ? calcButtonLabel(!reverse, valueText) : null, disabled: incrementerDisabled, holdConfig: holdConfig, icon: incrementIcon, joined: joined, onDown: handleIncrement, onHold: handleIncrement, onKeyDown: handleIncKeyDown, onSpotlightDisappear: onSpotlightDisappear, shrink: true, spotlightDisabled: spotlightDisabled })), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Layout.Cell, { "aria-disabled": disabled, "aria-hidden": !active, "aria-valuetext": valueText, className: css.valueWrapper, id: id, role: "spinbutton", shrink: true, children: [sizingPlaceholder, /*#__PURE__*/(0, _jsxRuntime.jsx)(PickerViewManager, { "aria-hidden": true, arranger: arranger, className: css.viewManager, duration: 100, index: index, noAnimation: noAnimation, reverseTransition: reverseTransition.current, children: children }), showIndicators && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", _objectSpread(_objectSpread({ className: css.indicatorContainer }, voiceProps), {}, { children: children.map(function (c, indicator) { return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { className: (0, _classnames3["default"])(css.indicator, _defineProperty({}, css.active, index === indicator)) }, "indicator".concat(indicator)); }) }))] }), isHorizontalJoinedEnter ? null : /*#__PURE__*/(0, _jsxRuntime.jsx)(_Layout.Cell, _objectSpread(_objectSpread({}, voiceProps), {}, { align: joined ? 'stretch' : null, "aria-controls": !joined ? decrementerAriaControls : null, "aria-label": calcDecrementLabel(valueText), className: css.decrementer, component: _PickerButton["default"], "data-webos-voice-label": joined ? calcButtonLabel(reverse, valueText) : null, disabled: decrementerDisabled, holdConfig: holdConfig, icon: decrementIcon, joined: joined, onDown: handleDecrement, onHold: handleDecrement, onKeyDown: handleDecKeyDown, onSpotlightDisappear: onSpotlightDisappear, shrink: true, spotlightDisabled: spotlightDisabled }))] })); }; PickerBase.displayName = 'Picker'; PickerBase.propTypes = /** @lends sandstone/internal/Picker.Picker.prototype */{ /** * Index for internal ViewManager * * @type {Number} * @required * @public */ index: _propTypes2["default"].number.isRequired, /** * The maximum value selectable by the picker (inclusive). * * The range between `min` and `max` should be evenly divisible by * {@link sandstone/internal/Picker.PickerBase.step|step}. * * @type {Number} * @required * @public */ max: _propTypes2["default"].number.isRequired, /** * The minimum value selectable by the picker (inclusive). * * The range between `min` and `max` should be evenly divisible by * {@link sandstone/internal/Picker.PickerBase.step|step}. * * @type {Number} * @required * @public */ min: _propTypes2["default"].number.isRequired, /** * Accessibility hint * * For example, `hour`, `year`, and `meridiem` * * @type {String} * @default '' * @public */ accessibilityHint: _propTypes2["default"].string, /** * The "aria-label" for the picker. * * While the `aria-label` will always be set on the root node, that node is only focusable * when the picker is `joined`. * * @type {String} * @memberof sandstone/internal/Picker.PickerBase.prototype * @public */ 'aria-label': _propTypes2["default"].string, /** * Overrides the `aria-valuetext` for the picker. * * By default, `aria-valuetext` is set to the current selected child and `accessibilityHint` * text. * * @type {String} * @memberof sandstone/internal/Picker.PickerBase.prototype * @public */ 'aria-valuetext': _propTypes2["default"].string, /** * Determines which key to adjust the picker's value for the joined horizontal one. * * * `'enter'` allows the user to use the enter key to adjust the picker's value * * `'arrow'` allows the user to use the left or right keys to adjust the picker's value. * * The default value for joined horizontal picker is `'enter'`. * If {@link sandstone/internal/Picker.PickerBase.orientation|orientation} is `'vertical'` or * {@link sandstone/internal/Picker.PickerBase.joined|joined} is undefined or is `false`, this prop is ignored. * * @type {('enter'|'arrow')} * @default 'enter' * @public */ changedBy: _propTypes2["default"].oneOf(['enter', 'arrow']), /** * Children from which to pick * * @type {Node} * @public */ children: _propTypes2["default"].node, /** * Class name for component * * @type {String} * @public */ className: _propTypes2["default"].string, /** * Customize component style * * @type {Object} * @private */ css: _propTypes2["default"].object, /** * Disables voice control. * * @type {Boolean} * @memberof sandstone/internal/Picker.PickerBase.prototype * @public */ 'data-webos-voice-disabled': _propTypes2["default"].bool, /** * The `data-webos-voice-group-label` for the Button of Picker. * * @type {String} * @memberof sandstone/internal/Picker.PickerBase.prototype * @public */ 'data-webos-voice-group-label': _propTypes2["default"].string, /** * The "aria-label" for the decrement button. * * @type {String} * @default 'previous item' * @public */ decrementAriaLabel: _propTypes2["default"].string, /** * Assign a custom icon for the decrementer. All strings supported by {@link sandstone/Icon.Icon|Icon} are * supported. Without a custom icon, the default is used, and is automatically changed when * the {@link sandstone/internal/Picker.PickerBase.orientation|orientation} is changed. * * @type {String} * @public */ decrementIcon: _propTypes2["default"].string, /** * When `true`, the Picker is shown as disabled and does not generate `onChange` * {@link /docs/developer-guide/glossary/#event|events}. * * @type {Boolean} * @public */ disabled: _propTypes2["default"].bool, /** * The picker id reference for setting aria-controls. * * @type {String} * @private */ id: _propTypes2["default"].string, /** * The "aria-label" for the increment button. * * @type {String} * @default 'next item' * @public */ incrementAriaLabel: _propTypes2["default"].string, /** * Assign a custom icon for the incrementer. All strings supported by {@link sandstone/Icon.Icon|Icon} are * supported. Without a custom icon, the default is used, and is automatically changed when * the {@link sandstone/internal/Picker.PickerBase.orientation|orientation} is changed. * * @type {String} * @public */ incrementIcon: _propTypes2["default"].string, /** * Determines the user interaction of the control. A joined picker allows the user to use * the arrow keys or the enter key to adjust the picker's value. * It depends on {@link sandstone/internal/Picker.PickerBase.changedBy|changedBy} * whether to use the arrow keys or the enter key. * A split control allows full navigation, * but requires individual ENTER presses on the incrementer and decrementer buttons. * Pointer interaction is the same for both formats. * * @type {Boolean} * @public */ joined: _propTypes2["default"].bool, /** * By default, the picker will animate transitions between items if it has a defined * `width`. Specifying `noAnimation` will prevent any transition animation for the * component. * * @type {Boolean} * @public */ noAnimation: _propTypes2["default"].bool, /** * A function to run when the control should increment or decrement. * * @type {Function} * @public */ onChange: _propTypes2["default"].func, /** * A function to run when the picker is removed while retaining focus. * * @type {Function} * @private */ onSpotlightDisappear: _propTypes2["default"].func, /** * The handler to run prior to focus leaving the picker when the 5-way down key is pressed. * * @type {Function} * @param {Object} event * @public */ onSpotlightDown: _propTypes2["default"].func, /** * The handler to run prior to focus leaving the picker when the 5-way left key is pressed. * * @type {Function} * @param {Object} event * @public */ onSpotlightLeft: _propTypes2["default"].func, /** * The handler to run prior to focus leaving the picker when the 5-way right key is pressed. * * @type {Function} * @param {Object} event * @public */ onSpotlightRight: _propTypes2["default"].func, /** * The handler to run prior to focus leaving the picker when the 5-way up key is pressed. * * @type {Function} * @param {Object} event * @public */ onSpotlightUp: _propTypes2["default"].func, /** * Sets the orientation of the picker, whether the buttons are above and below or on the * sides of the value. Must be either `'horizontal'` or `'vertical'`. * * @type {('horizontal'|'vertical')} * @default 'horizontal' * @public */ orientation: _propTypes2["default"].oneOf(['horizontal', 'vertical']), /** * When `true`, the picker buttons operate in the reverse direction such that pressing * up/left decrements the value and down/right increments the value. This is more natural * for vertical lists of text options where "up" implies a spatial change rather than * incrementing the value. * * @type {Boolean} * @public */ reverse: _propTypes2["default"].bool, /** * When `true`, the component cannot be navigated using spotlight. * * @type {Boolean} * @default false * @public */ spotlightDisabled: _propTypes2["default"].bool, /** * Allow the picker to only increment or decrement by a given value. * * A step of `2` would cause a picker to increment from 10 to 12 to 14, etc. It must evenly * divide into the range designated by `min` and `max`. * * @type {Number} * @default 1 * @public */ step: _propTypes2["default"].number, /** * The primary text of the `Picker`. * * The screen readers read out the title text when the `joined` prop is false * * @type {String} * @public */ title: _propTypes2["default"].string, /** * The type of picker. It determines the aria-label for the next and previous buttons. * * Depending on the `type`, `joined`, `decrementAriaLabel`, and `incrementAriaLabel`, * the screen readers read out differently when Spotlight is on the next button, the previous button, * or the picker itself. * * For example, if Spotlight is on the next button, the `joined` prop is false, * and aria label props(`decrementAriaLabel` and `incrementAriaLabel`) are not defined, * then the screen readers read out as follows. * `'string'` type: `'next item'` * `'number'` type: `'press ok button to increase the value'` * * @type {('number'|'string')} * @default 'string' * @public */ type: _propTypes2["default"].oneOf(['number', 'string']), /** * Index of the selected child * * @type {Number} * @default 0 * @public */ value: _propTypes2["default"].number, /** * Choose a specific size for your picker. `'small'`, `'medium'`, `'large'`, or set to `null` to * assume auto-sizing. `'small'` is good for numeric pickers, `'medium'` for single or short * word pickers, `'large'` for maximum-sized pickers. * * You may also supply a number. This number will determine the minimum size of the Picker. * Setting a number to less than the number of characters in your longest value may produce * unexpected results. * * @type {('small'|'medium'|'large'|Number)} * @public */ width: _propTypes2["default"].oneOfType([_propTypes2["default"].oneOf([null, 'small', 'medium', 'large']), _propTypes2["default"].number]), /** * Should the picker stop incrementing when the picker reaches the last element? Set `wrap` * to `true` to allow the picker to continue from the opposite end of the list of options. * * @type {Boolean} * @public */ wrap: _propTypes2["default"].bool }; var Picker = exports.Picker = (0, _IdProvider["default"])({ generateProp: null, prefix: 'p_' }, (0, _Skinnable["default"])(PickerBase)); var _default = exports["default"] = Picker;