UNPKG

multi-list-selection

Version:

Library for managing selections across multiple lists

201 lines (185 loc) 8.46 kB
'use strict'; 'use babel'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function valueEqualityIndexOf(list, targetItem, equalityPredicate) { for (var index = 0; index < list.length; index++) { var item = list[index]; if (equalityPredicate(item, targetItem)) { return index; } } return -1; } var MultiList = function () { function MultiList(lists, equalityPredicate) { _classCallCheck(this, MultiList); this.lists = lists; this.equalityPredicate = equalityPredicate || function (a, b) { return a === b; }; this.selectedListIndex = 0; this.selectedItemIndexesForList = Array(lists.length).fill(0); } _createClass(MultiList, [{ key: 'getSelectedListIndex', value: function getSelectedListIndex() { return this.selectedListIndex; } }, { key: 'getSelectedItemIndexForList', value: function getSelectedItemIndexForList(listIndex) { if (listIndex === undefined) throw new RangeError(); return this.selectedItemIndexesForList[listIndex]; } }, { key: 'getSelectedItemForList', value: function getSelectedItemForList(listIndex) { if (listIndex === undefined) throw new RangeError(); return this.lists[listIndex][this.getSelectedItemIndexForList(listIndex)]; } }, { key: 'getSelectedItem', value: function getSelectedItem() { var itemIndex = this.getSelectedItemIndexForList(this.selectedListIndex); return this.lists[this.selectedListIndex][itemIndex]; } }, { key: 'getListAtIndex', value: function getListAtIndex(listIndex) { return this.lists[listIndex]; } }, { key: 'selectListAtIndex', value: function selectListAtIndex(listIndex) { this.selectedListIndex = listIndex; } }, { key: 'selectItemAtLocation', value: function selectItemAtLocation(_ref) { var _ref2 = _slicedToArray(_ref, 2); var listIndex = _ref2[0]; var itemIndex = _ref2[1]; this.selectListAtIndex(listIndex); this.selectedItemIndexesForList[listIndex] = itemIndex; } }, { key: 'getGlobalItemIndex', value: function getGlobalItemIndex(_ref3) { var _ref4 = _slicedToArray(_ref3, 2); var localListIndex = _ref4[0]; var localItemIndex = _ref4[1]; var globalIndex = 0; for (var i = 0; i < localListIndex; i++) { globalIndex += this.lists[i].length; } return globalIndex + localItemIndex; } }, { key: 'getLocalItemLocation', value: function getLocalItemLocation(globalIndex) { if (globalIndex < 0) throw new RangeError('Index ' + globalIndex + ' out of range'); var totalLength = this.lists.reduce(function (totalLength, list) { return totalLength + list.length; }, 0); if (totalLength < globalIndex) throw new RangeError('Index ' + globalIndex + ' out of range'); var listLengths = this.lists.map(function (list) { return list.length; }); var count = 0; var currentListIndex = 0; while (count + listLengths[currentListIndex] <= globalIndex) { count += listLengths[currentListIndex]; currentListIndex++; } var localItemIndex = globalIndex - count; return [currentListIndex, localItemIndex]; } }, { key: 'selectItem', value: function selectItem(item) { var flattenedList = this.lists.reduce(function (acc, list) { return acc.concat(list); }, []); var globalIndex = valueEqualityIndexOf(flattenedList, item, this.equalityPredicate); this.selectItemAtLocation(this.getLocalItemLocation(globalIndex)); } }, { key: 'moveListSelection', value: function moveListSelection(steps) { var newListIndex = this.selectedListIndex + steps; var listCount = this.lists.length; newListIndex = newListIndex % listCount; if (newListIndex < 0) { this.selectedListIndex = listCount - 1; this.moveListSelection(newListIndex + 1); } else { this.selectListAtIndex(newListIndex); } } }, { key: 'moveItemSelection', value: function moveItemSelection(steps) { var listIndex = this.selectedListIndex; var localItemIndex = this.getSelectedItemIndexForList(listIndex); var globalIndex = this.getGlobalItemIndex([listIndex, localItemIndex]); var totalLength = this.lists.reduce(function (totalLength, list) { return totalLength + list.length; }, 0); var newGlobalIndex = (globalIndex + steps) % totalLength; if (newGlobalIndex < 0) { newGlobalIndex = totalLength + newGlobalIndex; } this.selectItemAtLocation(this.getLocalItemLocation(newGlobalIndex)); } }, { key: 'updateLists', value: function updateLists(newLists) { if (newLists.length !== this.lists.length) { throw new Error('Number or lists not equal'); } for (var listIndex = 0; listIndex < newLists.length; listIndex++) { var itemIndex = this.selectedItemIndexesForList[listIndex]; var item = this.lists[listIndex][itemIndex]; var newIndex = valueEqualityIndexOf(newLists[listIndex], item, this.equalityPredicate); if (newIndex > -1) { // if item is still present in list, return its new index this.selectedItemIndexesForList[listIndex] = newIndex; } else if (newLists[listIndex].length > itemIndex) { // else item not present, but if there is another item in the same location, return current index this.selectedItemIndexesForList[listIndex] = itemIndex; } else if (newLists[listIndex].length) { // else nothing at location, if there are still items in the current list, return last item this.selectedItemIndexesForList[listIndex] = newLists[listIndex].length - 1; } else { if (this.getSelectedListIndex() === listIndex) { // else list is empty, and if current list is the selected list... if (newLists[listIndex + 1] && newLists[listIndex + 1].length) { // ... and there is an element in the next list, set the first item as selected this.selectItemAtLocation([listIndex + 1, 0]); } else if (newLists[listIndex - 1] && newLists[listIndex - 1].length) { // if the next list is empty but the previous list is non-empty, set the last item as selected this.selectItemAtLocation([listIndex - 1, newLists[listIndex - 1].length - 1]); } this.selectedItemIndexesForList[listIndex] = 0; } } } this.lists = newLists; } }, { key: 'toObject', value: function toObject() { var lists = this.lists; var selectedListIndex = this.selectedListIndex; var selectedItemIndexesForList = this.selectedItemIndexesForList; return { lists: lists, selectedListIndex: selectedListIndex, selectedItemIndexesForList: selectedItemIndexesForList }; } }]); return MultiList; }(); exports.default = MultiList;