handsontable 0.0.0-next-9379dd1-20231020 → 0.0.0-next-b0a4ea2-20231024

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of handsontable might be problematic. Click here for more details.

Files changed (57) hide show
  1. package/base.js +2 -2
  2. package/base.mjs +2 -2
  3. package/dist/handsontable.css +13 -12
  4. package/dist/handsontable.full.css +13 -12
  5. package/dist/handsontable.full.js +1864 -1143
  6. package/dist/handsontable.full.min.css +5 -5
  7. package/dist/handsontable.full.min.js +70 -70
  8. package/dist/handsontable.js +1864 -1143
  9. package/dist/handsontable.min.css +5 -5
  10. package/dist/handsontable.min.js +24 -24
  11. package/helpers/mixed.js +1 -1
  12. package/helpers/mixed.mjs +1 -1
  13. package/package.json +1 -1
  14. package/plugins/contextMenu/menu/defaultShortcutsList.js +88 -0
  15. package/plugins/contextMenu/menu/defaultShortcutsList.mjs +84 -0
  16. package/plugins/contextMenu/menu/menu.js +87 -151
  17. package/plugins/contextMenu/menu/menu.mjs +90 -154
  18. package/plugins/contextMenu/menu/menuItemRenderer.js +58 -0
  19. package/plugins/contextMenu/menu/menuItemRenderer.mjs +54 -0
  20. package/plugins/contextMenu/menu/navigator.js +19 -144
  21. package/plugins/contextMenu/menu/navigator.mjs +18 -143
  22. package/plugins/contextMenu/menu/shortcuts.js +114 -0
  23. package/plugins/contextMenu/menu/shortcuts.mjs +110 -0
  24. package/plugins/dropdownMenu/dropdownMenu.js +32 -4
  25. package/plugins/dropdownMenu/dropdownMenu.mjs +33 -5
  26. package/plugins/filters/component/_base.js +23 -8
  27. package/plugins/filters/component/_base.mjs +23 -8
  28. package/plugins/filters/component/actionBar.js +29 -27
  29. package/plugins/filters/component/actionBar.mjs +26 -23
  30. package/plugins/filters/component/condition.js +46 -59
  31. package/plugins/filters/component/condition.mjs +40 -52
  32. package/plugins/filters/component/operators.js +21 -22
  33. package/plugins/filters/component/operators.mjs +18 -18
  34. package/plugins/filters/component/value.js +35 -26
  35. package/plugins/filters/component/value.mjs +32 -22
  36. package/plugins/filters/filters.js +75 -48
  37. package/plugins/filters/filters.mjs +68 -41
  38. package/plugins/filters/menu/focusController.js +123 -0
  39. package/plugins/filters/menu/focusController.mjs +119 -0
  40. package/plugins/filters/menu/focusNavigator.js +30 -0
  41. package/plugins/filters/menu/focusNavigator.mjs +26 -0
  42. package/plugins/filters/ui/_base.js +35 -13
  43. package/plugins/filters/ui/_base.mjs +35 -13
  44. package/plugins/filters/ui/input.js +43 -32
  45. package/plugins/filters/ui/input.mjs +42 -30
  46. package/plugins/filters/ui/link.js +44 -12
  47. package/plugins/filters/ui/link.mjs +44 -11
  48. package/plugins/filters/ui/multipleSelect.js +234 -129
  49. package/plugins/filters/ui/multipleSelect.mjs +232 -127
  50. package/plugins/filters/ui/radioInput.js +42 -18
  51. package/plugins/filters/ui/radioInput.mjs +42 -17
  52. package/plugins/filters/ui/select.js +144 -75
  53. package/plugins/filters/ui/select.mjs +140 -70
  54. package/shortcuts/context.js +3 -2
  55. package/shortcuts/context.mjs +3 -2
  56. package/utils/paginator.js +151 -0
  57. package/utils/paginator.mjs +147 -0
@@ -1,45 +1,121 @@
1
+ import "core-js/modules/es.error.cause.js";
2
+ function _classPrivateMethodInitSpec(obj, privateSet) { _checkPrivateRedeclaration(obj, privateSet); privateSet.add(obj); }
3
+ function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
4
+ function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
5
+ function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
6
+ function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
7
+ function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
8
+ function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
9
+ function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
10
+ function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
1
11
  import { Menu } from "../../../plugins/contextMenu/menu/index.mjs";
2
12
  import { clone, extend } from "../../../helpers/object.mjs";
3
13
  import { arrayEach } from "../../../helpers/array.mjs";
4
14
  import * as C from "../../../i18n/constants.mjs";
5
15
  import { SEPARATOR } from "../../../plugins/contextMenu/predefinedItems/index.mjs";
6
- import BaseUI from "./_base.mjs";
7
- const privatePool = new WeakMap();
8
-
16
+ import { BaseUI } from "./_base.mjs";
9
17
  /**
10
18
  * @private
11
19
  * @class SelectUI
12
20
  */
13
- class SelectUI extends BaseUI {
21
+ var _menu = /*#__PURE__*/new WeakMap();
22
+ var _items = /*#__PURE__*/new WeakMap();
23
+ var _caption = /*#__PURE__*/new WeakMap();
24
+ var _captionElement = /*#__PURE__*/new WeakMap();
25
+ var _dropdown = /*#__PURE__*/new WeakMap();
26
+ var _onMenuSelect = /*#__PURE__*/new WeakSet();
27
+ var _onMenuClosed = /*#__PURE__*/new WeakSet();
28
+ var _onClick = /*#__PURE__*/new WeakSet();
29
+ export class SelectUI extends BaseUI {
14
30
  static get DEFAULTS() {
15
31
  return clone({
16
32
  className: 'htUISelect',
17
- wrapIt: false
33
+ wrapIt: false,
34
+ tabIndex: -1
18
35
  });
19
36
  }
37
+
38
+ /**
39
+ * Instance of {@link Menu}.
40
+ *
41
+ * @type {Menu}
42
+ */
43
+
20
44
  constructor(hotInstance, options) {
21
45
  super(hotInstance, extend(SelectUI.DEFAULTS, options));
22
- privatePool.set(this, {});
23
46
  /**
24
- * Instance of {@link Menu}.
47
+ * On element click listener.
25
48
  *
26
- * @type {Menu}
49
+ * @private
27
50
  */
28
- this.menu = null;
51
+ _classPrivateMethodInitSpec(this, _onClick);
52
+ /**
53
+ * On menu closed listener.
54
+ */
55
+ _classPrivateMethodInitSpec(this, _onMenuClosed);
56
+ /**
57
+ * On menu selected listener.
58
+ *
59
+ * @param {object} command Selected item.
60
+ */
61
+ _classPrivateMethodInitSpec(this, _onMenuSelect);
62
+ _classPrivateFieldInitSpec(this, _menu, {
63
+ writable: true,
64
+ value: null
65
+ });
29
66
  /**
30
67
  * List of available select options.
31
68
  *
32
69
  * @type {Array}
33
70
  */
34
- this.items = [];
71
+ _classPrivateFieldInitSpec(this, _items, {
72
+ writable: true,
73
+ value: []
74
+ });
75
+ /**
76
+ * The reference to the BaseUI instance of the caption.
77
+ *
78
+ * @type {BaseUI}
79
+ */
80
+ _classPrivateFieldInitSpec(this, _caption, {
81
+ writable: true,
82
+ value: void 0
83
+ });
84
+ /**
85
+ * The reference to the table caption element.
86
+ *
87
+ * @type {HTMLTableCaptionElement}
88
+ */
89
+ _classPrivateFieldInitSpec(this, _captionElement, {
90
+ writable: true,
91
+ value: void 0
92
+ });
93
+ /**
94
+ * The reference to the BaseUI instance of the dropdown.
95
+ *
96
+ * @type {BaseUI}
97
+ */
98
+ _classPrivateFieldInitSpec(this, _dropdown, {
99
+ writable: true,
100
+ value: void 0
101
+ });
35
102
  this.registerHooks();
36
103
  }
37
104
 
105
+ /**
106
+ * Gets the instance of the Menu.
107
+ *
108
+ * @returns {Menu}
109
+ */
110
+ getMenu() {
111
+ return _classPrivateFieldGet(this, _menu);
112
+ }
113
+
38
114
  /**
39
115
  * Register all necessary hooks.
40
116
  */
41
117
  registerHooks() {
42
- this.addLocalHook('click', () => this.onClick());
118
+ this.addLocalHook('click', () => _classPrivateMethodGet(this, _onClick, _onClick2).call(this));
43
119
  }
44
120
 
45
121
  /**
@@ -48,9 +124,9 @@ class SelectUI extends BaseUI {
48
124
  * @param {Array} items Array of objects with required keys `key` and `name`.
49
125
  */
50
126
  setItems(items) {
51
- this.items = this.translateNames(items);
52
- if (this.menu) {
53
- this.menu.setMenuItems(this.items);
127
+ _classPrivateFieldSet(this, _items, this.translateNames(items));
128
+ if (_classPrivateFieldGet(this, _menu)) {
129
+ _classPrivateFieldGet(this, _menu).setMenuItems(_classPrivateFieldGet(this, _items));
54
130
  }
55
131
  }
56
132
 
@@ -72,26 +148,25 @@ class SelectUI extends BaseUI {
72
148
  */
73
149
  build() {
74
150
  super.build();
75
- this.menu = new Menu(this.hot, {
151
+ _classPrivateFieldSet(this, _menu, new Menu(this.hot, {
76
152
  className: 'htSelectUI htFiltersConditionsMenu',
77
153
  keepInViewport: false,
78
154
  standalone: true,
79
155
  container: this.options.menuContainer
80
- });
81
- this.menu.setMenuItems(this.items);
156
+ }));
157
+ _classPrivateFieldGet(this, _menu).setMenuItems(_classPrivateFieldGet(this, _items));
82
158
  const caption = new BaseUI(this.hot, {
83
159
  className: 'htUISelectCaption'
84
160
  });
85
161
  const dropdown = new BaseUI(this.hot, {
86
162
  className: 'htUISelectDropdown'
87
163
  });
88
- const priv = privatePool.get(this);
89
- priv.caption = caption;
90
- priv.captionElement = caption.element;
91
- priv.dropdown = dropdown;
164
+ _classPrivateFieldSet(this, _caption, caption);
165
+ _classPrivateFieldSet(this, _captionElement, caption.element);
166
+ _classPrivateFieldSet(this, _dropdown, dropdown);
92
167
  arrayEach([caption, dropdown], element => this._element.appendChild(element.element));
93
- this.menu.addLocalHook('select', command => this.onMenuSelect(command));
94
- this.menu.addLocalHook('afterClose', () => this.onMenuClosed());
168
+ _classPrivateFieldGet(this, _menu).addLocalHook('select', command => _classPrivateMethodGet(this, _onMenuSelect, _onMenuSelect2).call(this, command));
169
+ _classPrivateFieldGet(this, _menu).addLocalHook('afterClose', () => _classPrivateMethodGet(this, _onMenuClosed, _onMenuClosed2).call(this));
95
170
  this.update();
96
171
  }
97
172
 
@@ -106,9 +181,9 @@ class SelectUI extends BaseUI {
106
181
  if (this.options.value) {
107
182
  conditionName = this.options.value.name;
108
183
  } else {
109
- conditionName = this.menu.hot.getTranslatedPhrase(C.FILTERS_CONDITIONS_NONE);
184
+ conditionName = _classPrivateFieldGet(this, _menu).hot.getTranslatedPhrase(C.FILTERS_CONDITIONS_NONE);
110
185
  }
111
- privatePool.get(this).captionElement.textContent = conditionName;
186
+ _classPrivateFieldGet(this, _captionElement).textContent = conditionName;
112
187
  super.update();
113
188
  }
114
189
 
@@ -117,14 +192,25 @@ class SelectUI extends BaseUI {
117
192
  */
118
193
  openOptions() {
119
194
  const rect = this.element.getBoundingClientRect();
120
- if (this.menu) {
121
- this.menu.open();
122
- this.menu.setPosition({
195
+ if (_classPrivateFieldGet(this, _menu)) {
196
+ _classPrivateFieldGet(this, _menu).open();
197
+ _classPrivateFieldGet(this, _menu).setPosition({
123
198
  left: this.hot.isLtr() ? rect.left - 5 : rect.left - 31,
124
199
  top: rect.top - 1,
125
200
  width: rect.width,
126
201
  height: rect.height
127
202
  });
203
+ _classPrivateFieldGet(this, _menu).getNavigator().toFirstItem();
204
+ _classPrivateFieldGet(this, _menu).getKeyboardShortcutsCtrl().addCustomShortcuts([{
205
+ keys: [['Tab'], ['Shift', 'Tab']],
206
+ callback: event => {
207
+ this.closeOptions();
208
+ this.runLocalHooks('tabKeydown', event);
209
+ }
210
+ }, {
211
+ keys: [['Control/Meta', 'A']],
212
+ callback: () => false
213
+ }]);
128
214
  }
129
215
  }
130
216
 
@@ -132,62 +218,46 @@ class SelectUI extends BaseUI {
132
218
  * Close select dropdown menu.
133
219
  */
134
220
  closeOptions() {
135
- if (this.menu) {
136
- this.menu.close();
221
+ if (_classPrivateFieldGet(this, _menu)) {
222
+ _classPrivateFieldGet(this, _menu).close();
137
223
  }
138
224
  }
139
225
 
140
226
  /**
141
- * On menu selected listener.
142
- *
143
- * @private
144
- * @param {object} command Selected item.
227
+ * Focus element.
145
228
  */
146
- onMenuSelect(command) {
147
- if (command.name !== SEPARATOR) {
148
- this.options.value = command;
149
- this.update();
150
- this.runLocalHooks('select', this.options.value);
229
+ focus() {
230
+ if (this.isBuilt()) {
231
+ this.element.focus();
151
232
  }
152
233
  }
153
-
154
- /**
155
- * On menu closed listener.
156
- *
157
- * @private
158
- */
159
- onMenuClosed() {
160
- this.runLocalHooks('afterClose');
161
- }
162
-
163
- /**
164
- * On element click listener.
165
- *
166
- * @private
167
- */
168
- onClick() {
169
- this.openOptions();
170
- }
171
-
172
234
  /**
173
235
  * Destroy instance.
174
236
  */
175
237
  destroy() {
176
- if (this.menu) {
177
- this.menu.destroy();
178
- this.menu = null;
238
+ if (_classPrivateFieldGet(this, _menu)) {
239
+ _classPrivateFieldGet(this, _menu).destroy();
240
+ _classPrivateFieldSet(this, _menu, null);
179
241
  }
180
- const {
181
- caption,
182
- dropdown
183
- } = privatePool.get(this);
184
- if (caption) {
185
- caption.destroy();
242
+ if (_classPrivateFieldGet(this, _caption)) {
243
+ _classPrivateFieldGet(this, _caption).destroy();
186
244
  }
187
- if (dropdown) {
188
- dropdown.destroy();
245
+ if (_classPrivateFieldGet(this, _dropdown)) {
246
+ _classPrivateFieldGet(this, _dropdown).destroy();
189
247
  }
190
248
  super.destroy();
191
249
  }
192
250
  }
193
- export default SelectUI;
251
+ function _onMenuSelect2(command) {
252
+ if (command.name !== SEPARATOR) {
253
+ this.options.value = command;
254
+ this.update();
255
+ this.runLocalHooks('select', this.options.value);
256
+ }
257
+ }
258
+ function _onMenuClosed2() {
259
+ this.runLocalHooks('afterClose');
260
+ }
261
+ function _onClick2() {
262
+ this.openOptions();
263
+ }
@@ -49,7 +49,7 @@ const createContext = name => {
49
49
  * @param {Function} options.callback The shortcut's action
50
50
  * @param {object} options.group A group of shortcuts to which the shortcut belongs
51
51
  * @param {object} [options.runOnlyIf] A condition on which the shortcut's action runs
52
- * @param {object} [options.stopPropagation=true] If set to `true`: stops the event's propagation
52
+ * @param {object} [options.stopPropagation=false] If set to `true`: stops the event's propagation
53
53
  * @param {object} [options.captureCtrl=false] If set to `true`: captures the state of the Control/Meta modifier key
54
54
  * @param {object} [options.preventDefault=true] If set to `true`: prevents the default behavior
55
55
  * @param {object} [options.position='after'] The order in which the shortcut's action runs:
@@ -126,11 +126,12 @@ const createContext = name => {
126
126
  * @param {Function} [options.callback] A shortcut's action
127
127
  * @param {object} [options.group] A group of shortcuts to which a shortcut belongs
128
128
  * @param {object} [options.runOnlyIf] A condition on which a shortcut's action runs
129
- * @param {object} [options.stopPropagation=true] If set to `true`: stops the event's propagation
129
+ * @param {object} [options.stopPropagation=false] If set to `true`: stops the event's propagation
130
130
  * @param {object} [options.preventDefault=true] If set to `true`: prevents the default behavior
131
131
  * @param {object} [options.position='after'] The order in which a shortcut's action runs:
132
132
  * `'before'` or `'after'` a `relativeToGroup` group of actions
133
133
  * @param {object} [options.relativeToGroup] The name of a group of actions, used to determine an action's `position`
134
+ * @param {object} [options.forwardToContext] The context object where the event will be forwarded to.
134
135
  */
135
136
  const addShortcuts = function (shortcuts) {
136
137
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -45,7 +45,7 @@ export const createContext = name => {
45
45
  * @param {Function} options.callback The shortcut's action
46
46
  * @param {object} options.group A group of shortcuts to which the shortcut belongs
47
47
  * @param {object} [options.runOnlyIf] A condition on which the shortcut's action runs
48
- * @param {object} [options.stopPropagation=true] If set to `true`: stops the event's propagation
48
+ * @param {object} [options.stopPropagation=false] If set to `true`: stops the event's propagation
49
49
  * @param {object} [options.captureCtrl=false] If set to `true`: captures the state of the Control/Meta modifier key
50
50
  * @param {object} [options.preventDefault=true] If set to `true`: prevents the default behavior
51
51
  * @param {object} [options.position='after'] The order in which the shortcut's action runs:
@@ -122,11 +122,12 @@ export const createContext = name => {
122
122
  * @param {Function} [options.callback] A shortcut's action
123
123
  * @param {object} [options.group] A group of shortcuts to which a shortcut belongs
124
124
  * @param {object} [options.runOnlyIf] A condition on which a shortcut's action runs
125
- * @param {object} [options.stopPropagation=true] If set to `true`: stops the event's propagation
125
+ * @param {object} [options.stopPropagation=false] If set to `true`: stops the event's propagation
126
126
  * @param {object} [options.preventDefault=true] If set to `true`: prevents the default behavior
127
127
  * @param {object} [options.position='after'] The order in which a shortcut's action runs:
128
128
  * `'before'` or `'after'` a `relativeToGroup` group of actions
129
129
  * @param {object} [options.relativeToGroup] The name of a group of actions, used to determine an action's `position`
130
+ * @param {object} [options.forwardToContext] The context object where the event will be forwarded to.
130
131
  */
131
132
  const addShortcuts = function (shortcuts) {
132
133
  let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.createPaginator = createPaginator;
5
+ var _number = require("../helpers/number");
6
+ /**
7
+ * @typedef Paginator
8
+ * @property {function(number): void} setCurrentPage Sets the current index to the specific page.
9
+ * @property {function(): number} getCurrentPage Gets the current page.
10
+ * @property {function(): number} getSize Gets the total number of pages.
11
+ * @property {function(): void} toFirstItem Move the index to the first page.
12
+ * @property {function(): void} toLastItem Move the index to the last page.
13
+ * @property {function(): void} toNextItem Move the index to the next page.
14
+ * @property {function(): void} toPreviousItem Move the index to the previous page.
15
+ * @property {function(): void} clear Clear the internal state of the paginator.
16
+ */
17
+ /**
18
+ * @param {object} options Paginator options.
19
+ * @param {number} [options.initialPage] Initial index from which paging starts. Also, after clearing the paginator
20
+ * the page is cleared to the initial page.
21
+ * @param {function(): number} [options.size] Sets the max size of the pages.
22
+ * @param {function(number): boolean | void} [options.onItemSelect] Fires the function on each page change.
23
+ * @param {function(): void} [options.onClear] Fires the function after clearing the state.
24
+ * @returns {Paginator}
25
+ */
26
+ function createPaginator(_ref) {
27
+ let {
28
+ initialPage = -1,
29
+ size = () => 0,
30
+ onItemSelect = () => {},
31
+ onClear = () => {}
32
+ } = _ref;
33
+ const visitedPages = new Set();
34
+ let currentIndex = (0, _number.clamp)(initialPage, -1, getSize() - 1);
35
+
36
+ /**
37
+ * Updates the internal state of the paginator.
38
+ *
39
+ * @param {number} newIndex The page index to switch.
40
+ * @param {-1|1} direction The direction of traversing the pages in case when they are disabled.
41
+ * @returns {number} Returns the final index of the page.
42
+ */
43
+ function _updateState(newIndex, direction) {
44
+ const lastIndex = getSize() - 1;
45
+ if (newIndex < 0) {
46
+ newIndex = lastIndex;
47
+ }
48
+ if (newIndex > lastIndex) {
49
+ newIndex = 0;
50
+ }
51
+ if (visitedPages.has(newIndex)) {
52
+ return -1;
53
+ }
54
+ visitedPages.add(newIndex);
55
+ const changeProceed = onItemSelect(newIndex, false);
56
+ if (changeProceed === false) {
57
+ newIndex = _updateState(direction === 1 ? ++newIndex : --newIndex,
58
+ // eslint-disable-line no-plusplus
59
+ direction);
60
+ }
61
+ return newIndex;
62
+ }
63
+
64
+ /**
65
+ * Sets the page index as current one.
66
+ *
67
+ * @param {number} index The index to set.
68
+ */
69
+ function setCurrentPage(index) {
70
+ if (index > -1 && index < getSize() && onItemSelect(index, true) !== false) {
71
+ currentIndex = index;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Gets the current page.
77
+ *
78
+ * @returns {number}
79
+ */
80
+ function getCurrentPage() {
81
+ return currentIndex;
82
+ }
83
+
84
+ /**
85
+ * Moves the index to the first page.
86
+ */
87
+ function toFirstItem() {
88
+ if (getSize() > 0) {
89
+ visitedPages.clear();
90
+ currentIndex = _updateState(0, 1);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Moves the index to the last page.
96
+ */
97
+ function toLastItem() {
98
+ if (getSize() > 0) {
99
+ visitedPages.clear();
100
+ currentIndex = _updateState(getSize() - 1, -1);
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Moves the index to the next page.
106
+ */
107
+ function toNextItem() {
108
+ if (getSize() > 0) {
109
+ visitedPages.clear();
110
+ currentIndex = _updateState(++currentIndex, 1); // eslint-disable-line no-plusplus
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Moves the index to the previous page.
116
+ */
117
+ function toPreviousItem() {
118
+ if (getSize() > 0) {
119
+ visitedPages.clear();
120
+ currentIndex = _updateState(--currentIndex, -1); // eslint-disable-line no-plusplus
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Gets the total number of pages.
126
+ *
127
+ * @returns {number}
128
+ */
129
+ function getSize() {
130
+ return Math.max(size(), 0);
131
+ }
132
+
133
+ /**
134
+ * Clears the internal state of the paginator.
135
+ */
136
+ function clear() {
137
+ visitedPages.clear();
138
+ currentIndex = initialPage;
139
+ onClear();
140
+ }
141
+ return {
142
+ setCurrentPage,
143
+ getCurrentPage,
144
+ toFirstItem,
145
+ toLastItem,
146
+ toNextItem,
147
+ toPreviousItem,
148
+ getSize,
149
+ clear
150
+ };
151
+ }
@@ -0,0 +1,147 @@
1
+ import { clamp } from "../helpers/number.mjs";
2
+ /**
3
+ * @typedef Paginator
4
+ * @property {function(number): void} setCurrentPage Sets the current index to the specific page.
5
+ * @property {function(): number} getCurrentPage Gets the current page.
6
+ * @property {function(): number} getSize Gets the total number of pages.
7
+ * @property {function(): void} toFirstItem Move the index to the first page.
8
+ * @property {function(): void} toLastItem Move the index to the last page.
9
+ * @property {function(): void} toNextItem Move the index to the next page.
10
+ * @property {function(): void} toPreviousItem Move the index to the previous page.
11
+ * @property {function(): void} clear Clear the internal state of the paginator.
12
+ */
13
+ /**
14
+ * @param {object} options Paginator options.
15
+ * @param {number} [options.initialPage] Initial index from which paging starts. Also, after clearing the paginator
16
+ * the page is cleared to the initial page.
17
+ * @param {function(): number} [options.size] Sets the max size of the pages.
18
+ * @param {function(number): boolean | void} [options.onItemSelect] Fires the function on each page change.
19
+ * @param {function(): void} [options.onClear] Fires the function after clearing the state.
20
+ * @returns {Paginator}
21
+ */
22
+ export function createPaginator(_ref) {
23
+ let {
24
+ initialPage = -1,
25
+ size = () => 0,
26
+ onItemSelect = () => {},
27
+ onClear = () => {}
28
+ } = _ref;
29
+ const visitedPages = new Set();
30
+ let currentIndex = clamp(initialPage, -1, getSize() - 1);
31
+
32
+ /**
33
+ * Updates the internal state of the paginator.
34
+ *
35
+ * @param {number} newIndex The page index to switch.
36
+ * @param {-1|1} direction The direction of traversing the pages in case when they are disabled.
37
+ * @returns {number} Returns the final index of the page.
38
+ */
39
+ function _updateState(newIndex, direction) {
40
+ const lastIndex = getSize() - 1;
41
+ if (newIndex < 0) {
42
+ newIndex = lastIndex;
43
+ }
44
+ if (newIndex > lastIndex) {
45
+ newIndex = 0;
46
+ }
47
+ if (visitedPages.has(newIndex)) {
48
+ return -1;
49
+ }
50
+ visitedPages.add(newIndex);
51
+ const changeProceed = onItemSelect(newIndex, false);
52
+ if (changeProceed === false) {
53
+ newIndex = _updateState(direction === 1 ? ++newIndex : --newIndex,
54
+ // eslint-disable-line no-plusplus
55
+ direction);
56
+ }
57
+ return newIndex;
58
+ }
59
+
60
+ /**
61
+ * Sets the page index as current one.
62
+ *
63
+ * @param {number} index The index to set.
64
+ */
65
+ function setCurrentPage(index) {
66
+ if (index > -1 && index < getSize() && onItemSelect(index, true) !== false) {
67
+ currentIndex = index;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Gets the current page.
73
+ *
74
+ * @returns {number}
75
+ */
76
+ function getCurrentPage() {
77
+ return currentIndex;
78
+ }
79
+
80
+ /**
81
+ * Moves the index to the first page.
82
+ */
83
+ function toFirstItem() {
84
+ if (getSize() > 0) {
85
+ visitedPages.clear();
86
+ currentIndex = _updateState(0, 1);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Moves the index to the last page.
92
+ */
93
+ function toLastItem() {
94
+ if (getSize() > 0) {
95
+ visitedPages.clear();
96
+ currentIndex = _updateState(getSize() - 1, -1);
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Moves the index to the next page.
102
+ */
103
+ function toNextItem() {
104
+ if (getSize() > 0) {
105
+ visitedPages.clear();
106
+ currentIndex = _updateState(++currentIndex, 1); // eslint-disable-line no-plusplus
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Moves the index to the previous page.
112
+ */
113
+ function toPreviousItem() {
114
+ if (getSize() > 0) {
115
+ visitedPages.clear();
116
+ currentIndex = _updateState(--currentIndex, -1); // eslint-disable-line no-plusplus
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Gets the total number of pages.
122
+ *
123
+ * @returns {number}
124
+ */
125
+ function getSize() {
126
+ return Math.max(size(), 0);
127
+ }
128
+
129
+ /**
130
+ * Clears the internal state of the paginator.
131
+ */
132
+ function clear() {
133
+ visitedPages.clear();
134
+ currentIndex = initialPage;
135
+ onClear();
136
+ }
137
+ return {
138
+ setCurrentPage,
139
+ getCurrentPage,
140
+ toFirstItem,
141
+ toLastItem,
142
+ toNextItem,
143
+ toPreviousItem,
144
+ getSize,
145
+ clear
146
+ };
147
+ }