@enact/sandstone
Version:
Large-screen/TV support library for Enact, containing a variety of UI components.
945 lines (939 loc) • 41.1 kB
JavaScript
"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;