@schukai/monster 3.96.1 → 3.96.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +20 -5
  2. package/package.json +1 -1
  3. package/source/components/datatable/change-button.mjs +39 -41
  4. package/source/components/datatable/dataset.mjs +335 -325
  5. package/source/components/datatable/datasource/rest.mjs +33 -29
  6. package/source/components/datatable/embedded-pagination.mjs +3 -1
  7. package/source/components/datatable/filter.mjs +164 -63
  8. package/source/components/datatable/pagination.mjs +13 -6
  9. package/source/components/datatable/save-button.mjs +25 -3
  10. package/source/components/datatable/status.mjs +21 -26
  11. package/source/components/datatable/style/status.pcss +12 -2
  12. package/source/components/datatable/stylesheet/status.mjs +1 -1
  13. package/source/components/datatable/util.mjs +1 -2
  14. package/source/components/form/form.mjs +5 -4
  15. package/source/components/form/select.mjs +2008 -2013
  16. package/source/components/form/style/field-set.pcss +28 -7
  17. package/source/components/form/style/toggle-switch.pcss +13 -2
  18. package/source/components/form/stylesheet/field-set.mjs +14 -7
  19. package/source/components/form/stylesheet/toggle-switch.mjs +14 -7
  20. package/source/components/form/toggle-switch.mjs +372 -380
  21. package/source/components/layout/tabs.mjs +1 -2
  22. package/source/constants.mjs +14 -1
  23. package/source/data/extend.mjs +2 -1
  24. package/source/data/transformer.mjs +2 -0
  25. package/source/dom/constants.mjs +0 -1
  26. package/source/dom/customelement.mjs +7 -3
  27. package/source/dom/updater.mjs +5 -1
  28. package/source/monster.mjs +1 -1
  29. package/source/text/formatter.mjs +5 -3
  30. package/source/types/is.mjs +13 -0
  31. package/source/types/proxyobserver.mjs +7 -2
  32. package/source/types/version.mjs +1 -1
  33. package/source/util/clone.mjs +9 -14
  34. package/test/cases/data/pathfinder.mjs +18 -0
  35. package/test/cases/monster.mjs +1 -1
  36. package/test/cases/text/formatter.mjs +21 -1
  37. package/test/web/test.html +2 -2
  38. package/test/web/tests.js +266 -176
@@ -104,7 +104,7 @@ class Rest extends Datasource {
104
104
  * @property {string} templates.main Main template
105
105
  * @property {Object} features Feature definitions
106
106
  * @property {boolean} features.autoInit If true, the component is initialized automatically
107
- * @property {boolean} features.filter If true, the component is initialized automatically
107
+ * @property {boolean} features.filter If true, the filter.id is used to attach a filter control
108
108
  * @property {Object} autoInit Auto init definitions
109
109
  * @property {boolean} autoInit.intersectionObserver If true, the intersection observer is initialized automatically
110
110
  * @property {boolean} autoInit.oneTime If true, the intersection observer is initialized only once
@@ -131,7 +131,7 @@ class Rest extends Datasource {
131
131
 
132
132
  restOptions.read.parameters = {
133
133
  filter: null,
134
- oderBy: null,
134
+ orderBy: null,
135
135
  page: "1",
136
136
  };
137
137
 
@@ -199,7 +199,6 @@ class Rest extends Datasource {
199
199
  [assembleMethodSymbol]() {
200
200
  super[assembleMethodSymbol]();
201
201
  initEventHandler.call(this);
202
- initAutoInit.call(this);
203
202
  }
204
203
 
205
204
  /**
@@ -260,6 +259,12 @@ class Rest extends Datasource {
260
259
  queueMicrotask(() => {
261
260
  if (this.getOption("features.filter", false) === true) {
262
261
  initFilter.call(this);
262
+
263
+ if (!this[filterObserverSymbol]) {
264
+ initAutoInit.call(this);
265
+ }
266
+ } else {
267
+ initAutoInit.call(this);
263
268
  }
264
269
  });
265
270
  }
@@ -286,12 +291,26 @@ class Rest extends Datasource {
286
291
  this[dataSourceSymbol].setOption("read", opt);
287
292
 
288
293
  let url = this.getOption("read.url");
289
- const formatter = new Formatter(this.getOption("read.parameters"));
290
-
291
294
  if (!url) {
292
295
  return Promise.reject(new Error("No url defined"));
293
296
  }
294
297
 
298
+ const param = this.getOption("read.parameters", {});
299
+
300
+ if (param.query === null || param.query === undefined) {
301
+ param.query = "";
302
+ }
303
+
304
+ if (param.page === null || param.page === undefined) {
305
+ param.page = "1";
306
+ }
307
+
308
+ if (param.orderBy === null || param.orderBy === undefined) {
309
+ param.orderBy = "";
310
+ }
311
+
312
+ const formatter = new Formatter(param);
313
+
295
314
  url = formatter.format(url);
296
315
 
297
316
  this[dataSourceSymbol].setOption("read.url", url);
@@ -368,25 +387,6 @@ class Rest extends Datasource {
368
387
  });
369
388
  });
370
389
  }
371
-
372
- // /**
373
- // * @return {int}
374
- // */
375
- // currentPage() {
376
- //
377
- // const key = this.getOption("read.mapping.currentPage")
378
- // if (key === undefined) {
379
- // return 1;
380
- // }
381
- //
382
- // const pf = new Pathfinder(this.data);
383
- // if (pf.exists(key)) {
384
- // return parseInt(pf.getVia(key), 10);
385
- // }
386
- //
387
- // return 1;
388
- //
389
- // }
390
390
  }
391
391
 
392
392
  /**
@@ -413,23 +413,28 @@ function initFilter() {
413
413
  throw new Error("filter feature is enabled but no filter id is defined");
414
414
 
415
415
  const filterControl = findElementWithIdUpwards(this, filterID);
416
- if (!filterControl)
417
- throw new Error(
416
+ if (!filterControl) {
417
+ addAttributeToken(
418
+ this,
419
+ ATTRIBUTE_ERRORMESSAGE,
418
420
  "filter feature is enabled but no filter control with id " +
419
421
  filterID +
420
422
  " is found",
421
423
  );
424
+ return;
425
+ }
422
426
 
423
427
  this[filterObserverSymbol] = new Observer(() => {
424
428
  const query = filterControl.getOption("query");
425
429
  if (query === undefined) {
426
430
  return;
427
431
  }
432
+
428
433
  this.setParameters({ query: query });
429
434
  this.fetch()
430
435
  .then((response) => {
431
436
  if (!(response instanceof Response)) {
432
- throw new Error("Response is not an instance of Response");
437
+ return Promise.reject(new Error("response is not a Response object"));
433
438
  }
434
439
 
435
440
  if (response?.ok === true) {
@@ -584,8 +589,7 @@ function initIntersectionObserver() {
584
589
  */
585
590
  function getTemplate() {
586
591
  // language=HTML
587
- return `
588
- <slot></slot>`;
592
+ return `<slot></slot>`;
589
593
  }
590
594
 
591
595
  registerCustomElement(Rest);
@@ -39,7 +39,9 @@ class EmbeddedPagination extends Pagination {
39
39
  * @return {symbol}
40
40
  */
41
41
  static get [instanceSymbol]() {
42
- return Symbol.for("@schukai/monster/components/embedded-pagination");
42
+ return Symbol.for(
43
+ "@schukai/monster/components/datatable/embedded-pagination",
44
+ );
43
45
  }
44
46
 
45
47
  /**
@@ -13,7 +13,10 @@
13
13
  */
14
14
 
15
15
  import { instanceSymbol } from "../../constants.mjs";
16
- import { findTargetElementFromEvent } from "../../dom/events.mjs";
16
+ import {
17
+ findTargetElementFromEvent,
18
+ fireCustomEvent,
19
+ } from "../../dom/events.mjs";
17
20
  import {
18
21
  findElementWithIdUpwards,
19
22
  findElementWithSelectorUpwards,
@@ -25,7 +28,6 @@ import {
25
28
  registerCustomElement,
26
29
  } from "../../dom/customelement.mjs";
27
30
  import { ID } from "../../types/id.mjs";
28
- import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
29
31
  import { Settings } from "./filter/settings.mjs";
30
32
  import { FilterStyleSheet } from "./stylesheet/filter.mjs";
31
33
  import { getDocument, getWindow } from "../../dom/util.mjs";
@@ -54,6 +56,14 @@ import {
54
56
 
55
57
  import "./filter/select.mjs";
56
58
 
59
+ import "../form/button.mjs";
60
+ import "../form/select.mjs";
61
+ import "../form/popper-button.mjs";
62
+ import "../form/toggle-switch.mjs";
63
+ import "../form/context-help.mjs";
64
+ import "../form/context-error.mjs";
65
+ import "../form/message-state-button.mjs";
66
+
57
67
  export { Filter };
58
68
 
59
69
  /**
@@ -130,9 +140,23 @@ const sizeDataSymbol = Symbol("sizeData");
130
140
  */
131
141
  const debounceSizeSymbol = Symbol("debounceSize");
132
142
 
143
+ /**
144
+ * @private
145
+ * @type {symbol}
146
+ */
147
+ const hashChangeSymbol = Symbol("hashChange");
148
+
133
149
  /**
134
150
  * The Filter component is used to show and handle the filter values.
135
151
  *
152
+ * @fragments /fragments/components/datatable/filter
153
+ *
154
+ * @example /examples/components/datatable/filter-simple First filter
155
+ * @example /examples/components/datatable/filter-advanced Advanced filter
156
+ * @example /examples/components/datatable/filter-store Store filter
157
+ *
158
+ * @issue https://localhost.alvine.dev:8443/development/issues/closed/272.html
159
+ *
136
160
  * @copyright schukai GmbH
137
161
  * @summary The Filter component is used to show and handle the filter values.
138
162
  */
@@ -143,6 +167,9 @@ class Filter extends CustomElement {
143
167
  constructor() {
144
168
  super();
145
169
  this[settingsSymbol] = new Settings();
170
+
171
+ // debounce the hash change event if doSearch is called by click the search button
172
+ this[hashChangeSymbol] = 0;
146
173
  }
147
174
 
148
175
  /**
@@ -171,7 +198,7 @@ class Filter extends CustomElement {
171
198
 
172
199
  /**
173
200
  *
174
- * @return {{Filter}}
201
+ * @return {Filter}
175
202
  */
176
203
  resetFailureMessage() {
177
204
  this[searchButtonElementSymbol].hideMessage();
@@ -181,7 +208,7 @@ class Filter extends CustomElement {
181
208
 
182
209
  /**
183
210
  *
184
- * @return {{Filter}}
211
+ * @return {Filter}
185
212
  */
186
213
  showSuccess() {
187
214
  this[searchButtonElementSymbol].setState(
@@ -210,8 +237,10 @@ class Filter extends CustomElement {
210
237
  * @property {Object} formatter.marker Marker definitions
211
238
  * @property {Object} formatter.marker.open Marker open
212
239
  * @property {Object} formatter.marker.close Marker close
240
+ * @property {Object} features Feature definitions
241
+ * @property {boolean} features.storedConfig Stored configuration, this replaces the setting `storedConfig.enabled` @since 3.97.0
213
242
  * @property {Object} storedConfig Stored configuration
214
- * @property {boolean} storedConfig.enabled Enabled
243
+ * @property {boolean} storedConfig.enabled The store has been enabled, this option will no longer have any effect. @deprecated 20250101
215
244
  * @property {string} storedConfig.selector Selector
216
245
  * @property {Object} timeouts Timeout definitions
217
246
  * @property {number} timeouts.message Message timeout
@@ -227,19 +256,23 @@ class Filter extends CustomElement {
227
256
  templates: {
228
257
  main: getTemplate(),
229
258
  },
259
+
230
260
  formatter: {
231
261
  marker: {
232
262
  open: null,
233
263
  close: null,
234
264
  },
235
265
  },
266
+
236
267
  labels: {
237
268
  search: "Search",
238
269
  reset: "Reset",
239
270
  save: "Save",
240
271
  "filter-name": "Filter name",
241
- "empty-query-and-no-default": "Please select a filter",
242
- "query-not-changed": "The query has not changed",
272
+ "empty-query-and-no-default":
273
+ "The query is empty and there is no default query.",
274
+ "query-not-changed":
275
+ "The search request has not changed, so no search is required.",
243
276
  },
244
277
 
245
278
  templateMapping: {
@@ -247,6 +280,10 @@ class Filter extends CustomElement {
247
280
  "filter-name-label": name,
248
281
  },
249
282
 
283
+ features: {
284
+ storedConfig: false,
285
+ },
286
+
250
287
  storedConfig: {
251
288
  enabled: true,
252
289
  selector: "",
@@ -268,8 +305,8 @@ class Filter extends CustomElement {
268
305
  },
269
306
  },
270
307
 
271
- query: undefined,
272
- defaultQuery: "",
308
+ query: null,
309
+ defaultQuery: null,
273
310
  eventProcessing: true,
274
311
  });
275
312
  }
@@ -284,12 +321,14 @@ class Filter extends CustomElement {
284
321
 
285
322
  /**
286
323
  * @return {FilterButton}
324
+ * @fires monster-filter-initialized
287
325
  */
288
326
  [assembleMethodSymbol]() {
289
327
  this.setOption(
290
328
  "templateMapping.filter-save-label",
291
329
  this.getOption("labels.save"),
292
330
  );
331
+
293
332
  this.setOption(
294
333
  "templateMapping.filter-name-label",
295
334
  this.getOption("labels.filter-name"),
@@ -302,12 +341,19 @@ class Filter extends CustomElement {
302
341
 
303
342
  initFromConfig
304
343
  .call(this)
305
- .then(() => {
306
- initFilter.call(this);
307
- updateFilterTabs.call(this);
308
- })
344
+ .then(() => {})
309
345
  .catch((error) => {
310
346
  addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message);
347
+ })
348
+ .finally(() => {
349
+ initFilter.call(this);
350
+ updateFilterTabs.call(this);
351
+ doSearch
352
+ .call(this, { showEffect: false })
353
+ .then(() => {
354
+ fireCustomEvent(this, "monster-filter-initialized");
355
+ })
356
+ .catch(() => {});
311
357
  });
312
358
  }
313
359
 
@@ -316,7 +362,6 @@ class Filter extends CustomElement {
316
362
  */
317
363
  connectedCallback() {
318
364
  super.connectedCallback();
319
-
320
365
  getWindow().addEventListener(
321
366
  "hashchange",
322
367
  this[locationChangeHandlerSymbol],
@@ -385,10 +430,13 @@ function updateFilterSelections() {
385
430
  queueMicrotask(() => {
386
431
  const options = this[settingsSymbol].getOptions();
387
432
  this[filterSelectElementSymbol].setOption("options", options);
388
- queueMicrotask(() => {
389
- this[filterSelectElementSymbol].value =
390
- this[settingsSymbol].getSelected();
391
- });
433
+
434
+ setTimeout(() => {
435
+ window.requestAnimationFrame(() => {
436
+ this[filterSelectElementSymbol].value =
437
+ this[settingsSymbol].getSelected();
438
+ });
439
+ }, 0);
392
440
  });
393
441
  }
394
442
 
@@ -411,7 +459,12 @@ function initFilter() {
411
459
  .forEach((element) => {
412
460
  const label = element.getAttribute("data-monster-label");
413
461
  if (!label) {
414
- throw new Error("no filter label is defined");
462
+ addAttributeToken(
463
+ this,
464
+ ATTRIBUTE_ERRORMESSAGE,
465
+ "no filter label is defined",
466
+ );
467
+ return;
415
468
  }
416
469
 
417
470
  let value = element.id;
@@ -448,6 +501,7 @@ function initFilter() {
448
501
 
449
502
  this[settingsSymbol].set({ value, label, visible });
450
503
  });
504
+
451
505
  updateFilterSelections.call(this);
452
506
  }
453
507
 
@@ -499,6 +553,33 @@ function setSlotAttribute(element, visible) {
499
553
  */
500
554
  function initEventHandler() {
501
555
  const self = this;
556
+
557
+ let lastHash = getGlobal().location.hash;
558
+ self[locationChangeHandlerSymbol] = () => {
559
+ if (lastHash === getGlobal().location.hash) {
560
+ return;
561
+ }
562
+
563
+ /**
564
+ * debounce the hash change event if doSearch
565
+ * is called by click the search button
566
+ */
567
+ if (self[hashChangeSymbol] > 0) {
568
+ self[hashChangeSymbol]--;
569
+ return;
570
+ }
571
+
572
+ initFilter.call(this);
573
+
574
+ doSearch
575
+ .call(self)
576
+ .then(() => {})
577
+ .catch((error) => {})
578
+ .finally(() => {
579
+ lastHash = getGlobal().location.hash;
580
+ });
581
+ };
582
+
502
583
  /**
503
584
  * Monster.Components.Form.event:monster-selection-cleared
504
585
  */
@@ -542,6 +623,7 @@ function initEventHandler() {
542
623
  );
543
624
  }
544
625
 
626
+ /** save the current filter */
545
627
  if (self[filterSaveActionButtonElementSymbol]) {
546
628
  self[filterSaveActionButtonElementSymbol].setOption(
547
629
  "actions.click",
@@ -641,6 +723,8 @@ function initEventHandler() {
641
723
  }
642
724
 
643
725
  self[searchButtonElementSymbol].setOption("actions.click", () => {
726
+ self[hashChangeSymbol] = 1;
727
+
644
728
  doSearch
645
729
  .call(self)
646
730
  .then(() => {})
@@ -684,7 +768,10 @@ function initEventHandler() {
684
768
  }
685
769
 
686
770
  if (event.keyCode === 13) {
687
- doSearch.call(self, { showEffect: false });
771
+ doSearch
772
+ .call(self, { showEffect: false })
773
+ .then(() => {})
774
+ .catch((error) => {});
688
775
  }
689
776
  });
690
777
 
@@ -696,6 +783,7 @@ function initEventHandler() {
696
783
  }
697
784
 
698
785
  function initTabEvents() {
786
+ const self = this;
699
787
  this[filterTabElementSymbol].addEventListener(
700
788
  "monster-tab-changed",
701
789
  (event) => {
@@ -836,7 +924,18 @@ function doSearch({ showEffect } = { showEffect: true }) {
836
924
  .call(this)
837
925
  .then((query) => {
838
926
  const buildQuery = buildSearchQuery.call(this, query);
839
- if (buildQuery === "" && !this.getOption("defaultQuery")) {
927
+ if (buildQuery === null) {
928
+ const msg = this.getOption("labels.empty-query-and-no-default");
929
+ if (showEffect) {
930
+ this[searchButtonElementSymbol].removeState();
931
+ this[searchButtonElementSymbol]
932
+ .setMessage(msg)
933
+ .showMessage(this.getOption("timeouts.message", 4000));
934
+ }
935
+ return Promise.reject(new Error(msg));
936
+ }
937
+
938
+ if (buildQuery === "" && this.getOption("defaultQuery") === null) {
840
939
  const msg = this.getOption("labels.empty-query-and-no-default");
841
940
 
842
941
  if (showEffect) {
@@ -846,7 +945,7 @@ function doSearch({ showEffect } = { showEffect: true }) {
846
945
  .showMessage(this.getOption("timeouts.message", 4000));
847
946
  }
848
947
 
849
- throw new Error(msg);
948
+ return Promise.reject(new Error(msg));
850
949
  }
851
950
 
852
951
  if (buildQuery === this.getOption("query")) {
@@ -859,7 +958,7 @@ function doSearch({ showEffect } = { showEffect: true }) {
859
958
  .showMessage(this.getOption("timeouts.message", 4000));
860
959
  }
861
960
 
862
- throw new Error(msg);
961
+ return Promise.reject(new Error(msg));
863
962
  }
864
963
 
865
964
  if (showEffect) {
@@ -870,6 +969,8 @@ function doSearch({ showEffect } = { showEffect: true }) {
870
969
  }
871
970
 
872
971
  this.setOption("query", buildSearchQuery.call(this, query));
972
+
973
+ return Promise.resolve();
873
974
  })
874
975
  .catch((error) => {
875
976
  if (error instanceof Error) {
@@ -945,7 +1046,6 @@ function collectSearchQueries() {
945
1046
  return;
946
1047
  }
947
1048
 
948
- //const visible = window.getComputedStyle(element).display !== "none";
949
1049
  const visible = getVisibilityFromSlotAttribute(element);
950
1050
  if (!visible) {
951
1051
  return;
@@ -1136,49 +1236,50 @@ function updateConfig() {
1136
1236
  */
1137
1237
  function getTemplate() {
1138
1238
  // language=HTML
1139
- return `<div data-monster-role="control" part="control">
1140
- <div data-monster-role="container">
1141
- <div data-monster-role="layout">
1142
- <div data-monster-role="filter">
1143
- <slot></slot>
1144
- <slot name="hidden"></slot>
1145
- </div>
1146
- <div data-monster-role="select-and-search">
1147
- <monster-message-state-button data-monster-role="search-button" class="stretched-control"
1148
- data-monster-replace="path:labels.search">
1149
- </monster-message-state-button>
1150
- <monster-select class="stretched-control"
1151
- data-monster-selected-template="summary"
1152
- data-monster-option-type="checkbox"
1153
- data-monster-option-filter-mode="options"
1154
- data-monster-option-filter-position="popper"
1155
- data-monster-role="filter-select"></monster-select>
1156
- <monster-popper-button data-monster-role="save-button" class="stretched-control"
1157
- data-monster-attributes="data-monster-visible path:storedConfig.enabled">
1158
- <div slot="button">\${filter-save-label}</div>
1159
- <div class="monster-form" data-monster-role="form">
1160
-
1161
- <label for="filter-name">\${filter-name-label}
1162
- <input name="filter-name"
1163
- type="search"
1164
- class="monster-margin-bottom-5"></label>
1165
- <monster-message-state-button
1166
- data-monster-role="save-action-button"
1167
- data-monster-option-labels-button="\${filter-save-label}">
1239
+ return `
1240
+ <div data-monster-role="control" part="control">
1241
+ <div data-monster-role="container">
1242
+ <div data-monster-role="layout">
1243
+ <div data-monster-role="filter">
1244
+ <slot></slot>
1245
+ <slot name="hidden"></slot>
1246
+ </div>
1247
+ <div data-monster-role="select-and-search">
1248
+ <monster-message-state-button data-monster-role="search-button" class="stretched-control"
1249
+ data-monster-replace="path:labels.search">
1168
1250
  </monster-message-state-button>
1169
-
1251
+ <monster-select class="stretched-control"
1252
+ data-monster-selected-template="summary"
1253
+ data-monster-option-type="checkbox"
1254
+ data-monster-option-filter-mode="options"
1255
+ data-monster-option-filter-position="popper"
1256
+ data-monster-role="filter-select"></monster-select>
1257
+ <monster-popper-button data-monster-role="save-button" class="stretched-control"
1258
+ data-monster-attributes="data-monster-visible path:features.storedConfig">
1259
+ <div slot="button">\${filter-save-label}</div>
1260
+ <div class="monster-form" data-monster-role="form">
1261
+
1262
+ <label for="filter-name">\${filter-name-label}
1263
+ <input name="filter-name"
1264
+ type="search"
1265
+ class="monster-margin-bottom-5"></label>
1266
+ <monster-message-state-button
1267
+ data-monster-role="save-action-button"
1268
+ data-monster-option-labels-button="\${filter-save-label}">
1269
+ </monster-message-state-button>
1270
+
1271
+ </div>
1272
+ </monster-popper-button>
1273
+ <monster-button data-monster-role="reset-button" class="stretched-control"
1274
+ data-monster-replace="path:labels.reset">
1275
+ </monster-button>
1170
1276
  </div>
1171
- </monster-popper-button>
1172
- <monster-button data-monster-role="reset-button" class="stretched-control"
1173
- data-monster-replace="path:labels.reset">
1174
- </monster-button>
1175
- </div>
1176
1277
 
1278
+ </div>
1279
+ <input class="hidden" name="query" data-monster-role="query"
1280
+ data-monster-attributes="value path:query">
1281
+ </div>
1177
1282
  </div>
1178
- <input class="hidden" name="query" data-monster-role="query"
1179
- data-monster-attributes="value path:query">
1180
- </div>
1181
- </div>
1182
1283
  `;
1183
1284
  }
1184
1285
 
@@ -211,7 +211,7 @@ class Pagination extends CustomElement {
211
211
  showNumbers: ownWidth < parentWidth,
212
212
  };
213
213
 
214
- this[resizeObserverSymbol] = new ResizeObserver((entries) => {
214
+ this[resizeObserverSymbol] = new ResizeObserver(() => {
215
215
  if (this[debounceSizeSymbol] instanceof DeadMansSwitch) {
216
216
  try {
217
217
  this[debounceSizeSymbol].touch();
@@ -359,6 +359,7 @@ function initEventHandler() {
359
359
  if (
360
360
  !page ||
361
361
  page === "" ||
362
+ page === "…" ||
362
363
  page === null ||
363
364
  page === undefined ||
364
365
  page === "undefined" ||
@@ -460,7 +461,7 @@ function buildPagination(current, max) {
460
461
  let next = current === max ? null : current + 1;
461
462
  const itemList = [1];
462
463
 
463
- if (current > 4) itemList.push("…");
464
+ if (current > 4) itemList.push(-1);
464
465
 
465
466
  const r = 2;
466
467
  const r1 = current - r;
@@ -468,7 +469,7 @@ function buildPagination(current, max) {
468
469
 
469
470
  for (let i = r1 > 2 ? r1 : 2; i <= Math.min(max, r2); i++) itemList.push(i);
470
471
 
471
- if (r2 + 1 < max) itemList.push("…");
472
+ if (r2 + 1 < max) itemList.push(-1);
472
473
  if (r2 < max) itemList.push(max);
473
474
 
474
475
  let prevClass = "";
@@ -483,7 +484,13 @@ function buildPagination(current, max) {
483
484
  }
484
485
 
485
486
  const items = itemList.map((item) => {
486
- const p = `${item}`;
487
+ let p = `${item}`;
488
+
489
+ if (item === -1) {
490
+ item = null;
491
+ p = "…";
492
+ }
493
+
487
494
  const c = `${current}`;
488
495
 
489
496
  const obj = {
@@ -493,7 +500,7 @@ function buildPagination(current, max) {
493
500
  class: (p === c ? "current" : "").trim(),
494
501
  };
495
502
 
496
- if (p === "…") {
503
+ if (item === null) {
497
504
  obj.class += " disabled".trim();
498
505
  }
499
506
 
@@ -502,7 +509,7 @@ function buildPagination(current, max) {
502
509
  obj.description = formatter.format(this.getOption("labels.description"));
503
510
  obj.label = formatter.format(this.getOption("labels.page"));
504
511
  obj.href =
505
- p === "…"
512
+ item === null
506
513
  ? "#"
507
514
  : p === c
508
515
  ? "#"