@enact/sandstone
Version:
Large-screen/TV support library for Enact, containing a variety of UI components.
980 lines (960 loc) • 51.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = exports.EditableWrapper = exports.EditableShape = void 0;
var _handle = require("@enact/core/handle");
var _propTypes = _interopRequireDefault(require("@enact/core/internal/prop-types"));
var _keymap = require("@enact/core/keymap");
var _usePublicClassNames = require("@enact/core/usePublicClassNames");
var _spotlight = _interopRequireWildcard(require("@enact/spotlight"));
var _container = require("@enact/spotlight/src/container");
var _target = require("@enact/spotlight/src/target");
var _Accelerator = _interopRequireDefault(require("@enact/spotlight/Accelerator"));
var _pointer = require("@enact/spotlight/src/pointer");
var _AnnounceDecorator = require("@enact/ui/AnnounceDecorator");
var _Touchable = _interopRequireDefault(require("@enact/ui/Touchable"));
var _classnames = _interopRequireDefault(require("classnames"));
var _IString = _interopRequireDefault(require("ilib/lib/IString"));
var _propTypes2 = _interopRequireDefault(require("prop-types"));
var _react = require("react");
var _$L = _interopRequireDefault(require("../internal/$L"));
var _EditableWrapperModule = _interopRequireDefault(require("./EditableWrapper.module.css"));
var _jsxRuntime = require("react/jsx-runtime");
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 _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
var completeAnnounceDelay = 300; // An arbitrary delay at a level that is not ignored by the new focus element
var TouchableDiv = (0, _Touchable["default"])('div');
/**
* The shape for editable of {@link sandstone/Scroller|Scroller}.
*
* @typedef {Object} EditableShape
* @memberof sandstone/Scroller
* @property {Function} onComplete The callback function called when editing is finished.
* It has an event object contains `orders` array which app can use for repopulate items.
* @property {Function|Object} [blurItemFuncRef] Obtains a reference to `blurItem` function.
* If you would like to remove `focused` CSS class to an item, you can get the reference to `blurItem` function via `useRef`.
* `blurItem` function needs to be called with an item node when an item is blurred.
* @property {Boolean} [centered] Centers the contents of the scroller.
* @property {Object} [css] Customizes the component by mapping the supplied collection of CSS class names to the
* corresponding internal elements and states of this component.
* The following classes are supported:
*
* * `wrapper` - The content wrapper component class
* * `selected` - The selected item class
* * `focused` - The focused item class
* @property {Function|Object} [focusItemFuncRef] Obtains a reference to `focusItem` function.
* If you would like to use `focused` CSS class to an item, you can get the reference to `focusItem` function via `useRef`.
* `focusItem` function needs to be called with an item node when an item is focused.
* @property {Function|Object} [hideItemFuncRef] Obtains a reference to `hideItem` function.
* If you would like to hide an item, you can get the reference to `hideItem` function via `useRef`.
* @property {Function|Object} [removeItemFuncRef] Obtains a reference to `removeItem` function.
* If you would like to remove an item, you can get the reference to `removeItem` function via `useRef`.
* @property {string} [selectItemBy] Decides how to start editing items.
* It can be either `'press'` or `'longPress'`. If unset, it defaults to `'longPress'`.
* @property {Function|Object} [showItemFuncRef] Obtains a reference to `showItem` function.
* If you would like to show an item, you can get the reference to `showItem` function via `useRef`.
* @public
*/
var EditableShape = exports.EditableShape = _propTypes2["default"].shape({
onComplete: _propTypes2["default"].func.isRequired,
blurItemFuncRef: _propTypes["default"].ref,
centered: _propTypes2["default"].bool,
css: _propTypes2["default"].object,
focusItemFuncRef: _propTypes["default"].ref,
hideItemFuncRef: _propTypes["default"].ref,
removeItemFuncRef: _propTypes["default"].ref,
selectItemBy: _propTypes2["default"].string,
showItemFuncRef: _propTypes["default"].ref
});
var SpotlightAccelerator = new _Accelerator["default"]([5, 4]);
var holdDuration = 500;
var holdConfig = {
events: [{
name: 'hold',
time: holdDuration
}]
};
/**
* A Sandstone-styled EditableWrapper.
*
* @class EditableWrapper
* @memberof sandstone/Scroller
* @ui
* @public
*/
var EditableWrapper = exports.EditableWrapper = function EditableWrapper(props) {
var _editable$hideIndex;
var children = props.children,
editable = props.editable,
scrollContainerHandle = props.scrollContainerHandle,
scrollContainerRef = props.scrollContainerRef,
scrollContentRef = props.scrollContentRef;
var centered = (editable === null || editable === void 0 ? void 0 : editable.centered) != null ? editable.centered : true;
var selectItemBy = (editable === null || editable === void 0 ? void 0 : editable.selectItemBy) || 'longPress';
var customCss = (editable === null || editable === void 0 ? void 0 : editable.css) || {};
var removeItemFuncRef = editable === null || editable === void 0 ? void 0 : editable.removeItemFuncRef;
var hideItemFuncRef = editable === null || editable === void 0 ? void 0 : editable.hideItemFuncRef;
var showItemFuncRef = editable === null || editable === void 0 ? void 0 : editable.showItemFuncRef;
var focusItemFuncRef = editable === null || editable === void 0 ? void 0 : editable.focusItemFuncRef;
var blurItemFuncRef = editable === null || editable === void 0 ? void 0 : editable.blurItemFuncRef;
var mergedCss = (0, _usePublicClassNames.usePublicClassNames)({
componentCss: _EditableWrapperModule["default"],
customCss: customCss,
publicClassNames: true
});
var dataSize = children === null || children === void 0 ? void 0 : children.length;
// Mutable value
var wrapperRef = (0, _react.useRef)();
var mutableRef = (0, _react.useRef)({
// Constants
itemWidth: null,
centeredOffset: 0,
spotlightId: null,
// DOM elements
focusedItem: null,
selectedItem: null,
selectedItemLabel: '',
rearrangedItems: [],
// Indices
fromIndex: null,
prevToIndex: null,
hideIndex: null,
// Position for restoring focus after removing item
nextSpotlightRect: null,
// Move direction from the starting position to the current position
moveDirection: null,
// Last mouse position
lastMouseClientX: null,
// Last InputType
lastInputType: null,
// Timer for holding key input
keyHoldTimerId: null,
// Flag for prevent event propagation
needToPreventEvent: null,
lastInputDirection: null,
isDraggingItem: false,
isDragging: false,
// initialSelected
initialSelected: editable === null || editable === void 0 ? void 0 : editable.initialSelected
});
var announceRef = (0, _react.useRef)({});
mutableRef.current.hideIndex = (_editable$hideIndex = editable === null || editable === void 0 ? void 0 : editable.hideIndex) !== null && _editable$hideIndex !== void 0 ? _editable$hideIndex : dataSize;
// Functions
// Reset values
var reset = (0, _react.useCallback)(function () {
var _mutableRef$current = mutableRef.current,
focusedItem = _mutableRef$current.focusedItem,
selectedItem = _mutableRef$current.selectedItem,
spotlightId = _mutableRef$current.spotlightId;
focusedItem === null || focusedItem === void 0 || focusedItem.classList.remove(customCss.focused);
selectedItem === null || selectedItem === void 0 || selectedItem.classList.remove(_EditableWrapperModule["default"].selected, customCss.selected, _EditableWrapperModule["default"].rearranged);
mutableRef.current.focusedItem = null;
mutableRef.current.selectedItem = null;
mutableRef.current.selectedItemLabel = '';
mutableRef.current.moveDirection = null;
mutableRef.current.prevToIndex = null;
mutableRef.current.initialSelected = null;
wrapperRef.current.style.setProperty('--selected-item-offset', '0px');
_spotlight["default"].set(spotlightId, {
restrict: 'self-first'
});
}, [customCss.focused, customCss.selected]);
// Finalize the order
var finalizeOrders = (0, _react.useCallback)(function () {
var _mutableRef$current2 = mutableRef.current,
fromIndex = _mutableRef$current2.fromIndex,
moveDirection = _mutableRef$current2.moveDirection,
prevToIndex = _mutableRef$current2.prevToIndex,
rearrangedItems = _mutableRef$current2.rearrangedItems,
selectedItem = _mutableRef$current2.selectedItem;
var orders = Array.from({
length: dataSize
}, function (_, i) {
return i + 1;
});
if (rearrangedItems.length > 0) {
var selectedOrder = selectedItem.style.order;
var changedOrder = [];
rearrangedItems.forEach(function (item) {
var order = Number(item.style.order);
selectedOrder = order;
item.style.order = order - moveDirection;
item.classList.remove(_EditableWrapperModule["default"].rearrangedTransform, _EditableWrapperModule["default"].rearranged);
if (moveDirection > 0) {
changedOrder.push(order);
} else {
changedOrder.unshift(order);
}
});
if (moveDirection > 0) {
changedOrder.push(Number(selectedItem.style.order));
} else {
changedOrder.unshift(Number(selectedItem.style.order));
}
mutableRef.current.rearrangedItems = [];
selectedItem.style.order = selectedOrder;
// Create reordered array
orders.splice.apply(orders, [Math.min(fromIndex, prevToIndex), changedOrder.length].concat(changedOrder));
}
return orders;
}, [dataSize]);
var updateArrowIcon = (0, _react.useCallback)(function (index) {
var _mutableRef$current$s, _mutableRef$current$s2;
(_mutableRef$current$s = mutableRef.current.selectedItem) === null || _mutableRef$current$s === void 0 || _mutableRef$current$s.classList.toggle(customCss.noBefore, index === 0);
(_mutableRef$current$s2 = mutableRef.current.selectedItem) === null || _mutableRef$current$s2 === void 0 || _mutableRef$current$s2.classList.toggle(customCss.noAfter, index === mutableRef.current.hideIndex - 1);
}, [customCss.noBefore, customCss.noAfter]);
var startEditing = (0, _react.useCallback)(function (item) {
var _item$dataset;
if (item !== null && item !== void 0 && (_item$dataset = item.dataset) !== null && _item$dataset !== void 0 && _item$dataset.index && (!item.hasAttribute('disabled') || item.className.includes('hidden'))) {
var _mutableRef$current$f;
item.classList.add(_EditableWrapperModule["default"].selected, customCss.selected);
mutableRef.current.selectedItem = item;
(_mutableRef$current$f = mutableRef.current.focusedItem) === null || _mutableRef$current$f === void 0 || _mutableRef$current$f.classList.remove(customCss.focused);
mutableRef.current.focusedItem = null;
mutableRef.current.selectedItemLabel = (item.ariaLabel || item.textContent) + ' ';
mutableRef.current.fromIndex = Number(item.style.order) - 1;
mutableRef.current.prevToIndex = mutableRef.current.fromIndex;
updateArrowIcon(mutableRef.current.fromIndex);
setTimeout(function () {
if (item !== null && item !== void 0 && item.children[1]) {
item.children[1].ariaLabel = '';
}
if (!mutableRef.current.initialSelected) {
announceRef.current.announce(mutableRef.current.selectedItemLabel + (0, _$L["default"])('Press the left/right button to move or press the up button to select other options.'));
}
}, completeAnnounceDelay);
}
}, [customCss.focused, customCss.selected, updateArrowIcon]);
var finalizeEditing = (0, _react.useCallback)(function (orders) {
if (mutableRef.current.initialSelected) {
mutableRef.current.selectedItem.children[1].ariaLabel = "".concat(mutableRef.current.selectedItem.ariaLabel, " ").concat((0, _$L["default"])('Press the OK button to move or press the up button to select other options.'));
}
(0, _handle.forwardCustom)('onComplete', function () {
return {
orders: orders,
hideIndex: mutableRef.current.hideIndex
};
})(null, editable);
reset();
}, [editable, reset]);
var findItemNode = (0, _react.useCallback)(function (node) {
for (var current = node; current !== scrollContentRef.current && current !== document; current = current.parentNode) {
if (current.dataset.index) {
return current;
}
}
return null;
}, [scrollContentRef]);
var focusItem = (0, _react.useCallback)(function (target) {
var itemNode = findItemNode(target);
if (itemNode && !mutableRef.current.selectedItem) {
var _mutableRef$current$f2, _mutableRef$current$f3;
(_mutableRef$current$f2 = mutableRef.current.focusedItem) === null || _mutableRef$current$f2 === void 0 || _mutableRef$current$f2.classList.remove(customCss.focused);
mutableRef.current.focusedItem = itemNode;
(_mutableRef$current$f3 = mutableRef.current.focusedItem) === null || _mutableRef$current$f3 === void 0 || _mutableRef$current$f3.classList.add(customCss.focused);
mutableRef.current.prevToIndex = Number(itemNode.style.order) - 1;
}
}, [customCss.focused, findItemNode]);
var blurItem = (0, _react.useCallback)(function (target) {
var itemNode = findItemNode(target);
if (itemNode && !mutableRef.current.selectedItem) {
var _mutableRef$current$f4;
(_mutableRef$current$f4 = mutableRef.current.focusedItem) === null || _mutableRef$current$f4 === void 0 || _mutableRef$current$f4.classList.remove(customCss.focused);
mutableRef.current.focusedItem = null;
mutableRef.current.prevToIndex = null;
}
}, [customCss.focused, findItemNode]);
var handleClickCapture = (0, _react.useCallback)(function (ev) {
var _ev$target;
if (((_ev$target = ev.target) === null || _ev$target === void 0 || (_ev$target = _ev$target.parentNode) === null || _ev$target === void 0 ? void 0 : _ev$target.parentNode.getAttribute('role')) === 'button') {
return;
}
// Consume the event to prevent Item behavior
if (mutableRef.current.selectedItem || mutableRef.current.needToPreventEvent) {
ev.preventDefault();
ev.stopPropagation();
mutableRef.current.needToPreventEvent = false;
}
}, []);
var handleMouseDown = (0, _react.useCallback)(function (ev) {
var _ev$target2;
if (((_ev$target2 = ev.target) === null || _ev$target2 === void 0 || (_ev$target2 = _ev$target2.parentNode) === null || _ev$target2 === void 0 ? void 0 : _ev$target2.parentNode.getAttribute('role')) === 'button') {
return;
}
if (mutableRef.current.selectedItem) {
// Finalize orders and forward `onComplete` event
var orders = finalizeOrders();
finalizeEditing(orders);
if (selectItemBy === 'press') {
focusItem(ev.target);
}
mutableRef.current.needToPreventEvent = true;
} else {
var targetItemNode = findItemNode(ev.target);
if (selectItemBy === 'press') {
if (targetItemNode && targetItemNode.dataset.index) {
// Start editing by adding selected transition to selected item
mutableRef.current.targetItemNode = targetItemNode;
startEditing(targetItemNode);
}
mutableRef.current.needToPreventEvent = true;
} else {
mutableRef.current.targetItemNode = targetItemNode;
mutableRef.current.needToPreventEvent = false;
}
}
}, [finalizeEditing, finalizeOrders, findItemNode, focusItem, selectItemBy, startEditing]);
var handleHoldStart = (0, _react.useCallback)(function () {
var targetItemNode = mutableRef.current.targetItemNode;
if (targetItemNode && targetItemNode.dataset.index && selectItemBy === 'longPress') {
// Start editing by adding selected transition to selected item
startEditing(targetItemNode);
}
}, [selectItemBy, startEditing]);
var readOutCurrentPosition = (0, _react.useCallback)(function (neighborItem) {
var _mutableRef$current3 = mutableRef.current,
lastInputDirection = _mutableRef$current3.lastInputDirection,
lastInputType = _mutableRef$current3.lastInputType,
selectedItemLabel = _mutableRef$current3.selectedItemLabel;
if (lastInputType === 'key') {
if (lastInputDirection === 'left') {
announceRef.current.announce(new _IString["default"]((0, _$L["default"])('{selectedItem} moved to the left of {neighborItem}')).format({
selectedItem: selectedItemLabel,
neighborItem: neighborItem
}), true);
} else {
announceRef.current.announce(new _IString["default"]((0, _$L["default"])('{selectedItem} moved to the right of {neighborItem}')).format({
selectedItem: selectedItemLabel,
neighborItem: neighborItem
}), true);
}
}
}, []);
// Add rearranged items
var addRearrangedItems = (0, _react.useCallback)(function (_ref) {
var currentMoveDirection = _ref.currentMoveDirection,
toIndex = _ref.toIndex;
// Set the currentMoveDirection to css variable
var rtl = scrollContainerHandle.current.rtl;
wrapperRef.current.style.setProperty('--move-direction', currentMoveDirection * (rtl ? -1 : 1));
var _mutableRef$current4 = mutableRef.current,
fromIndex = _mutableRef$current4.fromIndex,
rearrangedItems = _mutableRef$current4.rearrangedItems,
selectedItem = _mutableRef$current4.selectedItem;
var getNextElement = function getNextElement(item) {
return currentMoveDirection > 0 ? item.nextElementSibling : item.previousElementSibling;
};
var sibling = getNextElement(selectedItem);
var lastRearrangedItem;
var start = currentMoveDirection > 0 ? toIndex : fromIndex;
var end = currentMoveDirection > 0 ? fromIndex : toIndex;
while (start > end && sibling) {
var _sibling;
(_sibling = sibling) === null || _sibling === void 0 || _sibling.classList.add(_EditableWrapperModule["default"].rearranged, _EditableWrapperModule["default"].rearrangedTransform);
if (!rearrangedItems.includes(sibling)) {
rearrangedItems.push(sibling);
}
lastRearrangedItem = sibling;
sibling = getNextElement(sibling);
start--;
}
if (lastRearrangedItem) {
readOutCurrentPosition(lastRearrangedItem.ariaLabel || lastRearrangedItem.textContent);
}
mutableRef.current.moveDirection = currentMoveDirection;
}, [readOutCurrentPosition, scrollContainerHandle]);
var removeRearrangedItems = (0, _react.useCallback)(function (numToRemove) {
var rearrangedItems = mutableRef.current.rearrangedItems;
var toItem = null;
if (rearrangedItems.length > 0) {
for (var i = 0; i < numToRemove; i++) {
var _toItem;
toItem = rearrangedItems.pop();
(_toItem = toItem) === null || _toItem === void 0 || _toItem.classList.remove(_EditableWrapperModule["default"].rearrangedTransform);
}
}
if (toItem) {
readOutCurrentPosition(toItem.ariaLabel || toItem.textContent);
}
}, [readOutCurrentPosition]);
// Move items
var moveItems = (0, _react.useCallback)(function (toIndex) {
var selectedItem = mutableRef.current.selectedItem;
var rtl = scrollContainerHandle.current.rtl;
if (selectedItem && !selectedItem.className.includes('hidden')) {
// Bail out when index is out of scope
if (toIndex < mutableRef.current.hideIndex && toIndex >= 0) {
var _mutableRef$current5 = mutableRef.current,
fromIndex = _mutableRef$current5.fromIndex,
itemWidth = _mutableRef$current5.itemWidth,
moveDirection = _mutableRef$current5.moveDirection,
prevToIndex = _mutableRef$current5.prevToIndex,
rearrangedItems = _mutableRef$current5.rearrangedItems;
// Set the selected item's offset to css variable
var offset = (toIndex - fromIndex) * itemWidth;
wrapperRef.current.style.setProperty('--selected-item-offset', offset * (rtl ? -1 : 1) + 'px');
// If the current toIndex is new,
if (toIndex !== prevToIndex) {
// Determine the direction of move from the latest from index
var currentMoveDirection = Math.sign(toIndex - prevToIndex);
// If the direction is changed and there are rearranged items, we remove them first.
if (moveDirection && currentMoveDirection !== moveDirection && rearrangedItems.length > 0) {
var numToRemove = currentMoveDirection > 0 ? toIndex - prevToIndex : prevToIndex - toIndex;
var needToAddItems = numToRemove > rearrangedItems.length;
removeRearrangedItems(numToRemove);
// When there's jump, meaning, numToRemove is bigger than 0, we need to add an item
if (needToAddItems) {
addRearrangedItems({
currentMoveDirection: currentMoveDirection,
toIndex: toIndex
});
}
} else {
addRearrangedItems({
currentMoveDirection: currentMoveDirection,
toIndex: toIndex
});
}
mutableRef.current.prevToIndex = toIndex;
}
updateArrowIcon(toIndex);
}
}
}, [addRearrangedItems, removeRearrangedItems, scrollContainerHandle, updateArrowIcon]);
var moveItemsByKeyDown = (0, _react.useCallback)(function (ev) {
var keyCode = ev.keyCode;
var container = scrollContentRef.current;
var _mutableRef$current6 = mutableRef.current,
itemWidth = _mutableRef$current6.itemWidth,
prevToIndex = _mutableRef$current6.prevToIndex;
var rtl = scrollContainerHandle.current.rtl;
var toIndex = !rtl ^ !(0, _keymap.is)('left', keyCode) ? prevToIndex - 1 : prevToIndex + 1;
var scrollLeft = container.scrollLeft * (rtl ? -1 : 1);
var itemLeft = toIndex * itemWidth - scrollLeft;
var left;
if (itemLeft > container.offsetLeft + container.clientWidth - itemWidth) {
left = itemLeft - (container.clientWidth - itemWidth) + scrollLeft;
} else if (itemLeft < 0) {
left = scrollLeft + itemLeft;
}
if (left != null) {
/* avoid null or undefined */
scrollContainerHandle.current.start({
targetX: left,
targetY: 0
});
}
mutableRef.current.lastInputDirection = (0, _keymap.is)('left', keyCode) ? 'left' : 'right';
moveItems(toIndex);
if (toIndex <= 0) {
announceRef.current.announce((0, _keymap.is)('left', keyCode) && !rtl && (0, _$L["default"])('LEFTMOST') || (0, _keymap.is)('right', keyCode) && rtl && (0, _$L["default"])('RIGHTMOST'));
} else if (toIndex >= dataSize - 1) {
announceRef.current.announce((0, _keymap.is)('right', keyCode) && !rtl && (0, _$L["default"])('RIGHTMOST') || (0, _keymap.is)('left', keyCode) && rtl && (0, _$L["default"])('LEFTMOST'));
}
}, [dataSize, moveItems, scrollContainerHandle, scrollContentRef]);
// Remove an item
var removeItem = (0, _react.useCallback)(function () {
var _mutableRef$current7 = mutableRef.current,
focusedItem = _mutableRef$current7.focusedItem,
prevToIndex = _mutableRef$current7.prevToIndex,
selectedItem = _mutableRef$current7.selectedItem;
var targetItem = selectedItem || focusedItem;
if (targetItem) {
// rearrangedItems need for the case when removing item while moving selected item
var rearrangedItems = mutableRef.current.rearrangedItems;
var targetItemRect = targetItem && targetItem.getBoundingClientRect();
mutableRef.current.nextSpotlightRect = {
x: targetItemRect.right,
y: targetItemRect.top
};
mutableRef.current.hideIndex -= 1;
var orders = finalizeOrders();
orders.splice(prevToIndex, 1);
rearrangedItems.forEach(function (item) {
item.style.order -= 1;
});
finalizeEditing(orders);
}
}, [finalizeEditing, finalizeOrders]);
var hideItem = (0, _react.useCallback)(function () {
var _mutableRef$current8 = mutableRef.current,
focusedItem = _mutableRef$current8.focusedItem,
selectedItem = _mutableRef$current8.selectedItem;
var targetItem = selectedItem || focusedItem;
if (targetItem) {
// rearrangedItems need for the case when hiding item while moving selected item
var rearrangedItems = mutableRef.current.rearrangedItems;
var targetItemOrder = Number(targetItem.style.order);
var targetItemRect = targetItem && targetItem.getBoundingClientRect();
mutableRef.current.nextSpotlightRect = {
x: targetItemRect.right,
y: targetItemRect.top
};
mutableRef.current.hideIndex -= 1;
var orders = finalizeOrders();
orders.splice(orders.indexOf(targetItemOrder), 1);
orders.push(targetItemOrder);
rearrangedItems.forEach(function (item) {
item.style.order -= 1;
});
targetItem.style.order = orders.length;
finalizeEditing(orders);
}
}, [finalizeEditing, finalizeOrders]);
var showItem = (0, _react.useCallback)(function () {
var _mutableRef$current9 = mutableRef.current,
focusedItem = _mutableRef$current9.focusedItem,
selectedItem = _mutableRef$current9.selectedItem;
var targetItem = selectedItem || focusedItem;
if (targetItem) {
var targetItemOrder = Number(targetItem.style.order);
var targetItemRect = targetItem && targetItem.getBoundingClientRect();
mutableRef.current.nextSpotlightRect = {
x: targetItemRect.right,
y: targetItemRect.top
};
var orders = Array.from({
length: dataSize
}, function (_, i) {
return i + 1;
});
orders.splice(targetItemOrder - 1, 1);
orders.splice(mutableRef.current.hideIndex, 0, targetItemOrder);
mutableRef.current.hideIndex += 1;
finalizeEditing(orders);
}
}, [dataSize, finalizeEditing]);
var getNextIndexFromPosition = (0, _react.useCallback)(function (x, tolerance) {
var _mutableRef$current10 = mutableRef.current,
centeredOffset = _mutableRef$current10.centeredOffset,
itemWidth = _mutableRef$current10.itemWidth,
prevToIndex = _mutableRef$current10.prevToIndex;
var rtl = scrollContainerHandle.current.rtl;
var bodyWidth = document.body.getBoundingClientRect().width;
// Determine toIndex with mouse client x position
// Coordinate calculation in RTL locales is not supported in chrome below 85
var scrollContentOffset = scrollContentRef.current.scrollLeft * (rtl ? -1 : 1) - centeredOffset;
var clientXFromContent = (rtl ? bodyWidth - x : x) + scrollContentOffset;
var direction = itemWidth * (prevToIndex + 0.5) < clientXFromContent ? 1 : -1; // 1: To next index , -1: To prev index
var moveTolerance = itemWidth * tolerance * direction;
return Math.floor((clientXFromContent - moveTolerance) / itemWidth);
}, [scrollContainerHandle, scrollContentRef]);
var handlePointerDown = (0, _react.useCallback)(function (ev) {
var selectedItem = mutableRef.current.selectedItem;
if (selectedItem) {
if (ev.target.hasPointerCapture(ev.pointerId)) {
ev.target.releasePointerCapture(ev.pointerId);
}
}
}, []);
var handleMouseMove = (0, _react.useCallback)(function (ev) {
var clientX = ev.clientX;
mutableRef.current.lastMouseClientX = clientX;
mutableRef.current.lastInputType = 'mouse';
if (mutableRef.current.selectedItem && Number(mutableRef.current.selectedItem.style.order) - 1 < mutableRef.current.hideIndex) {
var toIndex = getNextIndexFromPosition(clientX, 0.33);
moveItems(toIndex);
}
}, [getNextIndexFromPosition, moveItems]);
var handleMouseLeave = (0, _react.useCallback)(function () {
var _mutableRef$current11 = mutableRef.current,
focusedItem = _mutableRef$current11.focusedItem,
itemWidth = _mutableRef$current11.itemWidth,
lastInputType = _mutableRef$current11.lastInputType,
lastMouseClientX = _mutableRef$current11.lastMouseClientX,
selectedItem = _mutableRef$current11.selectedItem;
var rtl = scrollContainerHandle.current.rtl;
var scrollContentNode = scrollContentRef.current;
var scrollContentCenter = scrollContentNode.getBoundingClientRect().width / 2;
if (selectedItem || focusedItem) {
var orders = finalizeOrders();
finalizeEditing(orders);
if (lastInputType === 'scroll') {
var offset = itemWidth * (!rtl ^ !(lastMouseClientX > scrollContentCenter) ? 1 : -1);
scrollContainerHandle.current.start({
targetX: scrollContentNode.scrollLeft + offset,
targetY: 0
});
}
}
}, [finalizeEditing, finalizeOrders, scrollContainerHandle, scrollContentRef]);
var completeEditingByKeyDown = (0, _react.useCallback)(function () {
var _mutableRef$current12 = mutableRef.current,
selectedItem = _mutableRef$current12.selectedItem,
selectedItemLabel = _mutableRef$current12.selectedItemLabel;
var focusTarget = selectedItem.children[1];
var orders = finalizeOrders();
finalizeEditing(orders);
if (selectItemBy === 'press') {
if ((0, _pointer.getPointerMode)()) {
_spotlight["default"].setPointerMode(false);
_spotlight["default"].focus(focusTarget);
}
focusItem(focusTarget);
}
setTimeout(function () {
announceRef.current.announce(selectedItemLabel + (0, _$L["default"])('Movement completed'), true);
setTimeout(function () {
selectedItem.children[1].ariaLabel = "".concat(selectedItem.ariaLabel, " ").concat((0, _$L["default"])('Press the OK button to move or press the up button to select other options.'));
}, completeAnnounceDelay);
}, completeAnnounceDelay);
}, [finalizeEditing, finalizeOrders, focusItem, selectItemBy]);
var handleMoveItemsByKeyDown = (0, _react.useCallback)(function (ev, repeat) {
var selectedItem = mutableRef.current.selectedItem;
if (Number(selectedItem.style.order) - 1 < mutableRef.current.hideIndex) {
if (repeat) {
SpotlightAccelerator.processKey(ev, moveItemsByKeyDown);
} else {
SpotlightAccelerator.reset();
moveItemsByKeyDown(ev);
}
}
ev.preventDefault();
ev.stopPropagation();
}, [moveItemsByKeyDown]);
var handleFocusLeaveScrollContainer = (0, _react.useCallback)(function (ev, nextTarget) {
if (nextTarget && !(0, _container.getContainersForNode)(nextTarget).includes(mutableRef.current.spotlightId)) {
(0, _pointer.setPointerMode)(false);
_spotlight["default"].focus(nextTarget);
var orders = finalizeOrders();
finalizeEditing(orders);
ev.preventDefault();
ev.stopPropagation();
}
}, [finalizeEditing, finalizeOrders]);
var handleKeyDownCapture = (0, _react.useCallback)(function (ev) {
var keyCode = ev.keyCode,
repeat = ev.repeat,
target = ev.target;
var _mutableRef$current13 = mutableRef.current,
focusedItem = _mutableRef$current13.focusedItem,
selectedItem = _mutableRef$current13.selectedItem;
var targetItemNode = findItemNode(target);
if ((0, _keymap.is)('enter', keyCode) && target.getAttribute('role') !== 'button') {
if (!repeat) {
if (selectedItem) {
completeEditingByKeyDown();
mutableRef.current.needToPreventEvent = true;
} else if (selectItemBy === 'press') {
startEditing(targetItemNode);
mutableRef.current.needToPreventEvent = true;
}
} else if (repeat && targetItemNode && !mutableRef.current.timer && selectItemBy === 'longPress') {
mutableRef.current.timer = setTimeout(function () {
startEditing(targetItemNode);
}, holdDuration - 300);
}
} else if ((0, _keymap.is)('down', keyCode) && target.getAttribute('role') !== 'button' && !repeat && selectedItem) {
completeEditingByKeyDown();
mutableRef.current.needToPreventEvent = true;
} else if ((0, _keymap.is)('left', keyCode) || (0, _keymap.is)('right', keyCode)) {
var nextTarget = (0, _target.getTargetByDirectionFromElement)((0, _spotlight.getDirection)(keyCode), target);
if (selectedItem) {
// If keyDown event target is item(=not button), move item.
if (target.getAttribute('role') !== 'button') {
handleMoveItemsByKeyDown(ev, repeat);
// If keyDown event target is button and next spot target is item, move item and then spot selected item.
} else if ((nextTarget === null || nextTarget === void 0 ? void 0 : nextTarget.getAttribute('role')) !== 'button') {
handleMoveItemsByKeyDown(ev, repeat);
(0, _pointer.setPointerMode)(false);
_spotlight["default"].focus(selectedItem.children[1]);
// If keyDown event target is button and next spot target is button, check whether focus leaves the scroll container.
} else {
// Check if focus leaves scroll container.
handleFocusLeaveScrollContainer(ev, nextTarget);
}
} else if (nextTarget && !(0, _container.getContainersForNode)(nextTarget).includes(mutableRef.current.spotlightId) && !repeat) {
// Check if focus leaves scroll container.
handleFocusLeaveScrollContainer(ev, nextTarget);
}
} else if ((0, _keymap.is)('up', keyCode) || (0, _keymap.is)('down', keyCode)) {
if (selectedItem || focusedItem) {
var _nextTarget = (0, _target.getTargetByDirectionFromElement)((0, _spotlight.getDirection)(keyCode), target);
// Check if focus leaves scroll container.
handleFocusLeaveScrollContainer(ev, _nextTarget);
}
}
}, [findItemNode, handleFocusLeaveScrollContainer, handleMoveItemsByKeyDown, selectItemBy, startEditing, completeEditingByKeyDown]);
var handleKeyUpCapture = (0, _react.useCallback)(function (ev) {
var keyCode = ev.keyCode,
target = ev.target;
var selectedItem = mutableRef.current.selectedItem;
if ((0, _keymap.is)('cancel', keyCode)) {
if (selectedItem) {
completeEditingByKeyDown();
ev.stopPropagation(); // To prevent onCancel by CancelDecorator
}
} else if (target.getAttribute('role') === 'button') {
return;
}
clearTimeout(mutableRef.current.timer);
mutableRef.current.timer = null;
if (mutableRef.current.needToPreventEvent || mutableRef.current.selectedItem) {
ev.preventDefault();
mutableRef.current.needToPreventEvent = false;
}
}, [completeEditingByKeyDown]);
var handleGlobalKeyDownCapture = (0, _react.useCallback)(function (ev) {
var _mutableRef$current14 = mutableRef.current,
focusedItem = _mutableRef$current14.focusedItem,
selectedItem = _mutableRef$current14.selectedItem;
mutableRef.current.lastInputType = 'key';
// If the pointer mode is `true` and the focused component is not contained in scrollContainerRef,
// only `handleGlobalKeyDownCapture` is called instead of `handleKeyDownCapture`
// Below is mainly for handling key pressed while pointer mode is `true`.
if ((0, _pointer.getPointerMode)() && !scrollContainerRef.current.contains(_spotlight["default"].getCurrent()) && (selectedItem || focusedItem)) {
var keyCode = ev.keyCode;
var position = (0, _pointer.getLastPointerPosition)();
var direction = (0, _spotlight.getDirection)(keyCode);
if (direction) {
var nextTarget = (0, _target.getTargetByDirectionFromPosition)(direction, position, mutableRef.current.spotlightId);
if (!scrollContainerRef.current.contains(nextTarget)) {
// If the nextTarget is not contained in scrollContainerRef, complete editing
var orders = finalizeOrders();
finalizeEditing(orders);
} else if (((0, _keymap.is)('left', keyCode) || (0, _keymap.is)('right', keyCode)) && selectedItem) {
// When an item is selected and press the `left` or `right` key, move the selectedItem in that direction
moveItemsByKeyDown(ev);
ev.preventDefault();
ev.stopPropagation();
} else if ((0, _keymap.is)('down', keyCode) && selectedItem) {
// When an item is selected and press the `down` key, complete editing and focus the selectedItem
completeEditingByKeyDown();
} else if ((0, _keymap.is)('up', keyCode) && nextTarget.getAttribute('role') !== 'button') {
// When the nextTarget is the item and press the `up` key, focus the nextTarget to move focus successfully to the button above the item
(0, _pointer.setPointerMode)(false);
_spotlight["default"].focus(nextTarget);
}
} else if ((0, _keymap.is)('enter', keyCode)) {
if (selectedItem) {
// When an item is selected and press the `enter` key, complete editing and focus the selectedItem
completeEditingByKeyDown();
} else {
// When an item is focused and press the `enter` key, start editing
startEditing(focusedItem);
}
}
}
}, [completeEditingByKeyDown, finalizeEditing, finalizeOrders, moveItemsByKeyDown, scrollContainerRef, startEditing]);
var handleTouchMove = (0, _react.useCallback)(function (ev) {
var _ev$target3;
mutableRef.current.lastInputType = 'touch';
if (mutableRef.current.selectedItem) {
// Prevent scrolling by dragging when item is selected
ev.preventDefault();
}
if (mutableRef.current.isDraggingItem && ((_ev$target3 = ev.target) === null || _ev$target3 === void 0 || (_ev$target3 = _ev$target3.parentNode) === null || _ev$target3 === void 0 ? void 0 : _ev$target3.parentNode.getAttribute('role')) !== 'button') {
var clientX = ev.targetTouches[0].clientX;
mutableRef.current.lastMouseClientX = clientX;
scrollContainerRef.current.style.setProperty('--scroller-hover-to-scroll-by-touch', 'auto');
var toIndex = getNextIndexFromPosition(clientX, 0.33);
if (toIndex !== mutableRef.current.prevToIndex) {
moveItems(toIndex);
}
}
}, [getNextIndexFromPosition, moveItems, scrollContainerRef]);
var handleTouchEnd = (0, _react.useCallback)(function (ev) {
var _ev$target4;
var _mutableRef$current15 = mutableRef.current,
itemWidth = _mutableRef$current15.itemWidth,
lastInputType = _mutableRef$current15.lastInputType,
lastMouseClientX = _mutableRef$current15.lastMouseClientX,
selectedItem = _mutableRef$current15.selectedItem;
var rtl = scrollContainerHandle.current.rtl;
var scrollContentNode = scrollContentRef.current;
var scrollContentCenter = scrollContentNode.getBoundingClientRect().width / 2;
var clientX = ev.changedTouches[0].clientX;
var targetItemIndex = getNextIndexFromPosition(clientX, 0);
if (((_ev$target4 = ev.target) === null || _ev$target4 === void 0 || (_ev$target4 = _ev$target4.parentNode) === null || _ev$target4 === void 0 ? void 0 : _ev$target4.parentNode.getAttribute('role')) === 'button' && Number(selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.style.order) - 1 === targetItemIndex) {
return;
}
if (selectedItem) {
// Cancel mouse event to deselect a selected item when it is tapped
ev.preventDefault();
// Finalize orders and forward `onComplete` event
var orders = finalizeOrders();
finalizeEditing(orders);
if (lastInputType === 'scroll' && mutableRef.current.isDragging) {
var offset = itemWidth * (!rtl ^ !(lastMouseClientX > scrollContentCenter) ? 1 : -1);
scrollContainerHandle.current.start({
targetX: scrollContentNode.scrollLeft + offset,
targetY: 0
});
}
} else if (!mutableRef.current.isDragging) {
// Cancel mouse event to select a item when it is tapped
ev.preventDefault();
var targetItemNode = findItemNode(ev.target);
if (selectItemBy === 'press') {
if (targetItemNode && targetItemNode.dataset.index) {
mutableRef.current.targetItemNode = targetItemNode;
startEditing(targetItemNode);
}
}
}
mutableRef.current.isDraggingItem = false;
mutableRef.current.isDragging = false;
scrollContainerRef.current.style.setProperty('--scroller-hover-to-scroll-by-touch', 'none');
}, [getNextIndexFromPosition, finalizeEditing, finalizeOrders, findItemNode, scrollContainerHandle, scrollContainerRef, scrollContentRef, selectItemBy, startEditing]);
var handleDragStart = (0, _react.useCallback)(function (ev) {
var selectedItem = mutableRef.current.selectedItem;
// Index of dragged item
var dragTagetItemIndex = getNextIndexFromPosition(ev.x, 0);
mutableRef.current.isDragging = true;
if (selectedItem && Number(selectedItem.style.order) - 1 === dragTagetItemIndex) {
mutableRef.current.isDraggingItem = true;
}
}, [getNextIndexFromPosition]);
(0, _react.useLayoutEffect)(function () {
var available = typeof document === 'object';
if (available) {
document.addEventListener('keydown', handleGlobalKeyDownCapture, {
capture: true
});
}
return function () {
if (available) {
document.removeEventListener('keydown', handleGlobalKeyDownCapture, {
capture: true
});
}
};
}, [handleGlobalKeyDownCapture]);
(0, _react.useEffect)(function () {
if (mutableRef.current.nextSpotlightRect !== null) {
_spotlight["default"].focusNextFromPoint('down', mutableRef.current.nextSpotlightRect);
mutableRef.current.nextSpotlightRect = null;
}
});
(0, _react.useEffect)(function () {
var _wrapperRef$current;
// Calculate the item width once
var rtl = scrollContainerHandle.current.rtl;
var container = scrollContentRef.current;
var item = (_wrapperRef$current = wrapperRef.current) === null || _wrapperRef$current === void 0 ? void 0 : _wrapperRef$current.children[0];
if (item && typeof window !== 'undefined') {
var _wrapperRef$current2;
var bodyWidth = document.body.getBoundingClientRect().width;
var neighbor = item.nextElementSibling || item.previousElementSibling;
mutableRef.current.itemWidth = Math.abs(item.offsetLeft - (neighbor === null || neighbor === void 0 ? void 0 : neighbor.offsetLeft));
mutableRef.current.centeredOffset = rtl ? bodyWidth - (item.getBoundingClientRect().right + container.scrollLeft) : item.getBoundingClientRect().left + container.scrollLeft;
(_wrapperRef$current2 = wrapperRef.current) === null || _wrapperRef$current2 === void 0 || _wrapperRef$current2.style.setProperty('--item-width', mutableRef.current.itemWidth + 'px');
}
}, [centered, dataSize, scrollContainerHandle, scrollContentRef]);
(0, _react.useEffect)(function () {
mutableRef.current.spotlightId = scrollContainerRef.current && scrollContainerRef.current.dataset.spotlightId;
}, [scrollContainerRef]);
(0, _react.useEffect)(function () {
var scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
scrollContainer.addEventListener('mouseleave', handleMouseLeave);
scrollContainer.addEventListener('touchmove', handleTouchMove);
}
return function () {
if (scrollContainer) {
scrollContainer.removeEventListener('mouseleave', handleMouseLeave);
scrollContainer.removeEventListener('touchmove', handleTouchMove);
}
};
}, [handleMouseLeave, handleTouchMove, scrollContainerRef]);
(0, _react.useLayoutEffect)(function () {
if (removeItemFuncRef) {
removeItemFuncRef.current = removeItem;
}
}, [removeItem, removeItemFuncRef]);
(0, _react.useLayoutEffect)(function () {
if (hideItemFuncRef) {
hideItemFuncRef.current = hideItem;
}
}, [hideItem, hideItemFuncRef]);
(0, _react.useLayoutEffect)(function () {
if (showItemFuncRef) {
showItemFuncRef.current = showItem;
}
}, [showItem, showItemFuncRef]);
(0, _react.useLayoutEffect)(function () {
if (focusItemFuncRef) {
focusItemFuncRef.current = focusItem;
}
}, [focusItem, focusItemFuncRef]);
(0, _react.useLayoutEffect)(function () {
if (blurItemFuncRef) {
blurItemFuncRef.current = blurItem;
}
}, [blurItem, blurItemFuncRef]);
(0, _react.useEffect)(function () {
// addEventListener to moveItems while scrolled
var scrollContentNode = scrollContentRef.current;
var handleMoveItemsByScroll = function handleMoveItemsByScroll() {
var bodyWidth = document.body.getBoundingClientRect().width;
var _mutableRef$current16 = mutableRef.current,
lastMouseClientX = _mutableRef$current16.lastMouseClientX,
selectedItem = _mutableRef$current16.selectedItem;
var _scrollContainerHandl = scrollContainerHandle.current,
isHoveringToScroll = _scrollContainerHandl.isHoveringToScroll,
rtl = _scrollContainerHandl.rtl;
if (selectedItem && mutableRef.current.lastInputType !== 'key' && Number(selectedItem.style.order) - 1 < mutableRef.current.hideIndex) {
mutableRef.current.lastInputType = 'scroll';
if (isHoveringToScroll) {
var toIndex = getNextIndexFromPosition(lastMouseClientX, 0);
moveItems(!rtl ^ !(lastMouseClientX > bodyWidth / 2) ? toIndex + 1 : toIndex - 1);
} else {
moveItems(getNextIndexFromPosition(lastMouseClientX, 0.33));
}
}
};
setTimeout(function () {
scrollContentNode.addEventListener('scroll', handleMoveItemsByScroll);
}, 400); // Wait for finishing scroll animation when initial selected item is given.
return function () {
scrollContentNode.removeEventListener('scroll', handleMoveItemsByScroll);
};
}, [getNextIndexFromPosition, moveItems, scrollContainerHandle, scrollContentRef]);
(0, _react.useEffect)(function () {
if (mutableRef.current.initialSelected) {
var _scrollContainerHandl2;
(_scrollContainerHandl2 = scrollContainerHandle.current) === null || _scrollContainerHandl2 === void 0 || _scrollContainerHandl2.scrollTo({
animate: false,
position: {
x: mutableRef.current.initialSelected.scrollLeft
}
});
}
}, [scrollContainerHandle]);
(0, _react.useLayoutEffect)(function () {
var _initialSelected, _initialSelected2;
var iconItemList = Array.from(wrapperRef.current.children);
var initialSelected = mutableRef.current.initialSelected;
if (initialSelected && !(((_initialSelected = initialSelected) === null || _initialSelected === void 0 ? void 0 : _initialSelected.itemIndex) > 0)) {
// filter nullish values
initialSelected = mutableRef.current.initialSelected = null;
}
if ((_initialSelected2 = initialSelected) !== null && _initialSelected2 !== void 0 && _initialSelected2.itemIndex) {
var _initialSelected3;
var initialSelectedItem = wrapperRef.current.children[((_initialSelected3 = initialSelected) === null || _initialSelected3 === void 0 ? void 0 : _initialSelected3.itemIndex) - 1];
if (initialSelectedItem !== null && initialSelectedItem !== void 0 && initialSelectedItem.dataset.index) {
mutableRef.current.focusedItem = initialSelectedItem;
mutableRef.current.lastMouseClientX = (0, _pointer.getLastPointerPosition)().x;
startEditing(initialSelectedItem);
(0, _pointer.setPointerMode)(false);
_spotlight["default"].focus(initialSelectedItem.children[1]);
}
}
iconItemList.forEach(function (iconItemWrapper, index) {
if (iconItemWrapper !== null && iconItemWrapper !== void 0 && iconItemWrapper.children[1]) {
if (initialSelected && initialSelected.itemIndex - 1 === index) {
iconItemWrapper.children[1].ariaLabel += " ".concat((0, _$L["default"])('Press the left/right button to move or press the up button to select other options.'));
} else {
iconItemWrapper.children[1].ariaLabel += " ".concat((0, _$L["default"])('Press the OK button to move or press the up button to select other options.'));
}
}
});
}, []); // eslint-disable-line react-hooks/exhaustive-deps
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(TouchableDiv, {
holdConfig: holdConfig,
className: (0, _classnames["default"])(mergedCss.wrapper, _def