@schukai/monster 4.39.0 → 4.40.1

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.
@@ -13,9 +13,9 @@
13
13
  */
14
14
 
15
15
  import {
16
- assembleMethodSymbol,
17
- CustomElement,
18
- registerCustomElement,
16
+ assembleMethodSymbol,
17
+ CustomElement,
18
+ registerCustomElement,
19
19
  } from "../../dom/customelement.mjs";
20
20
  import { findElementWithSelectorUpwards, getWindow } from "../../dom/util.mjs";
21
21
  import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
@@ -82,288 +82,335 @@ const debounceSizeSymbol = Symbol("debounceSize");
82
82
  * @summary The Pagination component is used to show the current page and the total number of pages.
83
83
  */
84
84
  class Pagination extends CustomElement {
85
- /**
86
- */
87
- constructor() {
88
- super();
89
- this[datasourceLinkedElementSymbol] = null;
90
- }
91
-
92
- /**
93
- * This method is called by the `instanceof` operator.
94
- * @return {symbol}
95
- */
96
- static get [instanceSymbol]() {
97
- return Symbol.for("@schukai/monster/components/pagination");
98
- }
99
-
100
- /**
101
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
102
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
103
- *
104
- * The individual configuration values can be found in the table.
105
- *
106
- * @property {Object} templates Template definitions
107
- * @property {string} templates.main Main template
108
- * @property {Object} datasource Datasource configuration
109
- * @property {string} datasource.selector Datasource selector
110
- * @property {Object} labels Label definitions
111
- * @property {string} labels.page Page label
112
- * @property {string} labels.description Description label
113
- * @property {string} labels.previous Previous label
114
- * @property {string} labels.next Next label
115
- * @property {string} labels.of Of label
116
- * @property {string} href Href
117
- * @property {number} currentPage Current page
118
- * @property {number} pages Pages
119
- * @property {number} objectsPerPage Objects per page
120
- * @property {Object} mapping Mapping
121
- * @property {string} mapping.pages Pages mapping
122
- * @property {string} mapping.objectsPerPage Objects per page mapping
123
- * @property {string} mapping.currentPage Current page mapping
124
- */
125
- get defaults() {
126
- return Object.assign(
127
- {},
128
- super.defaults,
129
- {
130
- templates: {
131
- main: getTemplate(),
132
- },
133
-
134
- datasource: {
135
- selector: null,
136
- },
137
-
138
- labels: getTranslations(),
139
-
140
- href: "page-${page}",
141
-
142
- pages: null,
143
- objectsPerPage: 20,
144
- currentPage: null,
145
-
146
- mapping: {
147
- pages: "sys.pagination.pages",
148
- objectsPerPage: "sys.pagination.objectsPerPage",
149
- currentPage: "sys.pagination.currentPage",
150
- },
151
-
152
- /* @private */
153
- pagination: {
154
- items: [],
155
- },
156
- },
157
- initOptionsFromArguments.call(this),
158
- );
159
- }
160
-
161
- /**
162
- *
163
- * @return {string}
164
- */
165
- static getTag() {
166
- return "monster-pagination";
167
- }
168
-
169
- /**
170
- * @return {void}
171
- */
172
- disconnectedCallback() {
173
- super.disconnectedCallback();
174
- if (this?.[resizeObserverSymbol] instanceof ResizeObserver) {
175
- this[resizeObserverSymbol].disconnect();
176
- }
177
- }
178
-
179
- /**
180
- * @return {void}
181
- */
182
- connectedCallback() {
183
- super.connectedCallback();
184
-
185
- const parentNode = this.parentNode;
186
- if (!parentNode) {
187
- return;
188
- }
189
-
190
- try {
191
- handleDataSourceChanges.call(this);
192
- } catch (e) {
193
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e?.message || `${e}`);
194
- }
195
-
196
- requestAnimationFrame(() => {
197
- const parentParentNode = parentNode?.parentNode || parentNode;
198
-
199
- const parentWidth = parentParentNode.offsetWidth;
200
- const ownWidth = this.offsetWidth;
201
-
202
- this[sizeDataSymbol] = {
203
- last: {
204
- parentWidth: 0,
205
- },
206
- showNumbers: ownWidth < parentWidth,
207
- };
208
-
209
- this[resizeObserverSymbol] = new ResizeObserver(() => {
210
- if (this[debounceSizeSymbol] instanceof DeadMansSwitch) {
211
- try {
212
- this[debounceSizeSymbol].touch();
213
- return;
214
- } catch (e) {
215
- delete this[debounceSizeSymbol];
216
- }
217
- }
218
-
219
- this[debounceSizeSymbol] = new DeadMansSwitch(250, () => {
220
- queueMicrotask(() => {
221
- const parentWidth = parentParentNode.offsetWidth;
222
- const ownWidth = this.clientWidth;
223
-
224
- if (this[sizeDataSymbol]?.last?.parentWidth === parentWidth) {
225
- return;
226
- }
227
-
228
- this[sizeDataSymbol].last = {
229
- parentWidth: parentWidth,
230
- };
231
-
232
- this[sizeDataSymbol].showNumbers = ownWidth <= parentWidth;
233
- handleDataSourceChanges.call(this);
234
- });
235
- });
236
- });
237
-
238
- this[resizeObserverSymbol].observe(this?.parentNode?.parentNode);
239
- });
240
- }
241
-
242
- /**
243
- * @return {void}
244
- */
245
- [assembleMethodSymbol]() {
246
- super[assembleMethodSymbol]();
247
-
248
- initControlReferences.call(this);
249
- initEventHandler.call(this);
250
-
251
- const selector = this.getOption("datasource.selector", "");
252
-
253
- if (isString(selector)) {
254
- const element = findElementWithSelectorUpwards(this, selector);
255
- if (element === null) {
256
- throw new Error("the selector must match exactly one element");
257
- }
258
-
259
- if (!(element instanceof Datasource)) {
260
- throw new TypeError("the element must be a datasource");
261
- }
262
-
263
- this[datasourceLinkedElementSymbol] = element;
264
- element.datasource.attachObserver(
265
- new Observer(handleDataSourceChanges.bind(this)),
266
- );
267
-
268
- element.attachObserver(new Observer(handleDataSourceChanges.bind(this)));
269
-
270
- handleDataSourceChanges.call(this);
271
- }
272
- }
273
-
274
- /**
275
- * @private
276
- * @return {CSSStyleSheet}
277
- */
278
- static getControlCSSStyleSheet() {
279
- return PaginationStyleSheet;
280
- }
281
-
282
- /**
283
- * @return {CSSStyleSheet[]}
284
- */
285
- static getCSSStyleSheet() {
286
- return [this.getControlCSSStyleSheet(), DisplayStyleSheet, ThemeStyleSheet];
287
- }
85
+ /**
86
+ */
87
+ constructor() {
88
+ super();
89
+ this[datasourceLinkedElementSymbol] = null;
90
+ }
91
+
92
+ /**
93
+ * This method is called by the `instanceof` operator.
94
+ * @return {symbol}
95
+ */
96
+ static get [instanceSymbol]() {
97
+ return Symbol.for("@schukai/monster/components/pagination");
98
+ }
99
+
100
+ /**
101
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
102
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
103
+ *
104
+ * The individual configuration values can be found in the table.
105
+ *
106
+ * @property {Object} templates Template definitions
107
+ * @property {string} templates.main Main template
108
+ * @property {Object} datasource Datasource configuration
109
+ * @property {string} datasource.selector Datasource selector
110
+ * @property {Object} labels Label definitions
111
+ * @property {string} labels.page Page label
112
+ * @property {string} labels.description Description label
113
+ * @property {string} labels.previous Previous label
114
+ * @property {string} labels.next Next label
115
+ * @property {string} labels.of Of label
116
+ * @property {string} href Href
117
+ * @property {number} currentPage Current page
118
+ * @property {number} pages Pages
119
+ * @property {number} objectsPerPage Objects per page
120
+ * @property {Object} mapping Mapping
121
+ * @property {string} mapping.pages Pages mapping
122
+ * @property {string} mapping.objectsPerPage Objects per page mapping
123
+ * @property {string} mapping.currentPage Current page mapping
124
+ */
125
+ get defaults() {
126
+ return Object.assign(
127
+ {},
128
+ super.defaults,
129
+ {
130
+ templates: {
131
+ main: getTemplate(),
132
+ },
133
+
134
+ datasource: {
135
+ selector: null,
136
+ },
137
+
138
+ labels: getTranslations(),
139
+
140
+ callbacks: {
141
+ click: null,
142
+ },
143
+
144
+ href: "page-${page}",
145
+
146
+ pages: null,
147
+ objectsPerPage: 20,
148
+ currentPage: null,
149
+
150
+ mapping: {
151
+ pages: "sys.pagination.pages",
152
+ objectsPerPage: "sys.pagination.objectsPerPage",
153
+ currentPage: "sys.pagination.currentPage",
154
+ },
155
+
156
+ /* @private */
157
+ pagination: {
158
+ items: [],
159
+ },
160
+ },
161
+ initOptionsFromArguments.call(this),
162
+ );
163
+ }
164
+ /**
165
+ * Sets the pagination state directly, without requiring a datasource.
166
+ * This is useful for controlling the component programmatically.
167
+ *
168
+ * @param {object} state - The state object for the pagination.
169
+ * @param {number} state.currentPage - The current active page number.
170
+ * @param {number} state.totalPages - The total number of available pages.
171
+ * @return {void}
172
+ */
173
+ setPaginationState({ currentPage, totalPages }) {
174
+ if (typeof currentPage !== "number" || typeof totalPages !== "number") {
175
+ console.error(
176
+ "setPaginationState requires currentPage and totalPages to be numbers.",
177
+ );
178
+ return;
179
+ }
180
+
181
+ // 1. Update the component's internal options with the new values.
182
+ this.setOption("currentPage", currentPage);
183
+ this.setOption("pages", totalPages);
184
+
185
+ // 2. Call the existing buildPagination function to recalculate the links.
186
+ const pagination = buildPagination.call(
187
+ this,
188
+ this.getOption("currentPage"),
189
+ this.getOption("pages"),
190
+ );
191
+
192
+ // 3. Preserve the responsive visibility of page numbers.
193
+ if (this?.[sizeDataSymbol]?.showNumbers !== true) {
194
+ pagination.items = [];
195
+ }
196
+
197
+ // 4. Set the 'pagination' option, which will trigger the component to re-render.
198
+ this.setOption("pagination", pagination);
199
+ }
200
+ /**
201
+ *
202
+ * @return {string}
203
+ */
204
+ static getTag() {
205
+ return "monster-pagination";
206
+ }
207
+
208
+ /**
209
+ * @return {void}
210
+ */
211
+ disconnectedCallback() {
212
+ super.disconnectedCallback();
213
+ if (this?.[resizeObserverSymbol] instanceof ResizeObserver) {
214
+ this[resizeObserverSymbol].disconnect();
215
+ }
216
+ if (this?.[debounceSizeSymbol] instanceof DeadMansSwitch) {
217
+ try {
218
+ this[debounceSizeSymbol].defuse();
219
+ delete this[debounceSizeSymbol];
220
+ } catch (e) {
221
+ // already fired
222
+ }
223
+ }
224
+ }
225
+
226
+ /**
227
+ * @return {void}
228
+ */
229
+ connectedCallback() {
230
+ super.connectedCallback();
231
+
232
+ const parentNode = this.parentNode;
233
+ if (!parentNode) {
234
+ return;
235
+ }
236
+
237
+ try {
238
+ handleDataSourceChanges.call(this);
239
+ } catch (e) {
240
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e?.message || `${e}`);
241
+ }
242
+
243
+ requestAnimationFrame(() => {
244
+ const parentParentNode = parentNode?.parentNode || parentNode;
245
+
246
+ const parentWidth = parentParentNode.offsetWidth;
247
+ const ownWidth = this.offsetWidth;
248
+
249
+ this[sizeDataSymbol] = {
250
+ last: {
251
+ parentWidth: 0,
252
+ },
253
+ showNumbers: ownWidth < parentWidth,
254
+ };
255
+
256
+ this[resizeObserverSymbol] = new ResizeObserver(() => {
257
+ if (this[debounceSizeSymbol] instanceof DeadMansSwitch) {
258
+ try {
259
+ this[debounceSizeSymbol].touch();
260
+ return;
261
+ } catch (e) {
262
+ delete this[debounceSizeSymbol];
263
+ }
264
+ }
265
+
266
+ this[debounceSizeSymbol] = new DeadMansSwitch(250, () => {
267
+ queueMicrotask(() => {
268
+ const parentWidth = parentParentNode.offsetWidth;
269
+ const ownWidth = this.clientWidth;
270
+
271
+ if (this[sizeDataSymbol]?.last?.parentWidth === parentWidth) {
272
+ return;
273
+ }
274
+
275
+ this[sizeDataSymbol].last = {
276
+ parentWidth: parentWidth,
277
+ };
278
+
279
+ this[sizeDataSymbol].showNumbers = ownWidth <= parentWidth;
280
+ handleDataSourceChanges.call(this);
281
+ });
282
+ });
283
+ });
284
+
285
+ this[resizeObserverSymbol].observe(this?.parentNode?.parentNode);
286
+ });
287
+ }
288
+
289
+ /**
290
+ * @return {void}
291
+ */
292
+ [assembleMethodSymbol]() {
293
+ super[assembleMethodSymbol]();
294
+
295
+ initControlReferences.call(this);
296
+ initEventHandler.call(this);
297
+
298
+ const selector = this.getOption("datasource.selector", "");
299
+
300
+ if (isString(selector)) {
301
+ const element = findElementWithSelectorUpwards(this, selector);
302
+ if (element === null) {
303
+ throw new Error("the selector must match exactly one element");
304
+ }
305
+
306
+ if (!(element instanceof Datasource)) {
307
+ throw new TypeError("the element must be a datasource");
308
+ }
309
+
310
+ this[datasourceLinkedElementSymbol] = element;
311
+ element.datasource.attachObserver(
312
+ new Observer(handleDataSourceChanges.bind(this)),
313
+ );
314
+
315
+ element.attachObserver(new Observer(handleDataSourceChanges.bind(this)));
316
+
317
+ handleDataSourceChanges.call(this);
318
+ }
319
+ }
320
+
321
+ /**
322
+ * @private
323
+ * @return {CSSStyleSheet}
324
+ */
325
+ static getControlCSSStyleSheet() {
326
+ return PaginationStyleSheet;
327
+ }
328
+
329
+ /**
330
+ * @return {CSSStyleSheet[]}
331
+ */
332
+ static getCSSStyleSheet() {
333
+ return [this.getControlCSSStyleSheet(), DisplayStyleSheet, ThemeStyleSheet];
334
+ }
288
335
  }
289
336
 
290
337
  function getTranslations() {
291
- const locale = getLocaleOfDocument();
292
- switch (locale.language) {
293
- case "de":
294
- return {
295
- page: "${page}",
296
- description: "Seite ${page}",
297
- previous: "Vorherige",
298
- next: "Nächste",
299
- of: "von",
300
- };
301
- case "fr":
302
- return {
303
- page: "${page}",
304
- description: "Page ${page}",
305
- previous: "Précédent",
306
- next: "Suivant",
307
- of: "de",
308
- };
309
- case "sp":
310
- return {
311
- page: "${page}",
312
- description: "Página ${page}",
313
- previous: "Anterior",
314
- next: "Siguiente",
315
- of: "de",
316
- };
317
- case "it":
318
- return {
319
- page: "${page}",
320
- description: "Pagina ${page}",
321
- previous: "Precedente",
322
- next: "Successivo",
323
- of: "di",
324
- };
325
- case "pl":
326
- return {
327
- page: "${page}",
328
- description: "Strona ${page}",
329
- previous: "Poprzednia",
330
- next: "Następna",
331
- of: "z",
332
- };
333
- case "no":
334
- return {
335
- page: "${page}",
336
- description: "Side ${page}",
337
- previous: "Forrige",
338
- next: "Neste",
339
- of: "av",
340
- };
341
- case "dk":
342
- return {
343
- page: "${page}",
344
- description: "Side ${page}",
345
- previous: "Forrige",
346
- next: "Næste",
347
- of: "af",
348
- };
349
- case "sw":
350
- return {
351
- page: "${page}",
352
- description: "Sida ${page}",
353
- previous: "Föregående",
354
- next: "Nästa",
355
- of: "av",
356
- };
357
- default:
358
- case "en":
359
- return {
360
- page: "${page}",
361
- description: "Page ${page}",
362
- previous: "Previous",
363
- next: "Next",
364
- of: "of",
365
- };
366
- }
338
+ const locale = getLocaleOfDocument();
339
+ switch (locale.language) {
340
+ case "de":
341
+ return {
342
+ page: "${page}",
343
+ description: "Seite ${page}",
344
+ previous: "Vorherige",
345
+ next: "Nächste",
346
+ of: "von",
347
+ };
348
+ case "fr":
349
+ return {
350
+ page: "${page}",
351
+ description: "Page ${page}",
352
+ previous: "Précédent",
353
+ next: "Suivant",
354
+ of: "de",
355
+ };
356
+ case "sp":
357
+ return {
358
+ page: "${page}",
359
+ description: "Página ${page}",
360
+ previous: "Anterior",
361
+ next: "Siguiente",
362
+ of: "de",
363
+ };
364
+ case "it":
365
+ return {
366
+ page: "${page}",
367
+ description: "Pagina ${page}",
368
+ previous: "Precedente",
369
+ next: "Successivo",
370
+ of: "di",
371
+ };
372
+ case "pl":
373
+ return {
374
+ page: "${page}",
375
+ description: "Strona ${page}",
376
+ previous: "Poprzednia",
377
+ next: "Następna",
378
+ of: "z",
379
+ };
380
+ case "no":
381
+ return {
382
+ page: "${page}",
383
+ description: "Side ${page}",
384
+ previous: "Forrige",
385
+ next: "Neste",
386
+ of: "av",
387
+ };
388
+ case "dk":
389
+ return {
390
+ page: "${page}",
391
+ description: "Side ${page}",
392
+ previous: "Forrige",
393
+ next: "Næste",
394
+ of: "af",
395
+ };
396
+ case "sw":
397
+ return {
398
+ page: "${page}",
399
+ description: "Sida ${page}",
400
+ previous: "Föregående",
401
+ next: "Nästa",
402
+ of: "av",
403
+ };
404
+ default:
405
+ case "en":
406
+ return {
407
+ page: "${page}",
408
+ description: "Page ${page}",
409
+ previous: "Previous",
410
+ next: "Next",
411
+ of: "of",
412
+ };
413
+ }
367
414
  }
368
415
 
369
416
  /**
@@ -372,89 +419,57 @@ function getTranslations() {
372
419
  * @throws {Error} no shadow-root is defined
373
420
  */
374
421
  function initControlReferences() {
375
- if (!this.shadowRoot) {
376
- throw new Error("no shadow-root is defined");
377
- }
422
+ if (!this.shadowRoot) {
423
+ throw new Error("no shadow-root is defined");
424
+ }
378
425
 
379
- this[paginationElementSymbol] = this.shadowRoot.querySelector(
380
- "[data-monster-role=pagination]",
381
- );
426
+ this[paginationElementSymbol] = this.shadowRoot.querySelector(
427
+ "[data-monster-role=pagination]",
428
+ );
382
429
  }
383
430
 
431
+ /**
432
+ * @private
433
+ */
384
434
  /**
385
435
  * @private
386
436
  */
387
437
  function initEventHandler() {
388
- const self = this;
389
-
390
- self[paginationElementSymbol].addEventListener("click", function (event) {
391
- let element = null;
392
- const datasource = self[datasourceLinkedElementSymbol];
393
- if (!datasource) {
394
- return;
395
- }
396
-
397
- element = findTargetElementFromEvent(
398
- event,
399
- ATTRIBUTE_ROLE,
400
- "pagination-item",
401
- );
402
-
403
- if (!element) {
404
- element = findTargetElementFromEvent(
405
- event,
406
- ATTRIBUTE_ROLE,
407
- "pagination-next",
408
- );
409
- if (!element) {
410
- element = findTargetElementFromEvent(
411
- event,
412
- ATTRIBUTE_ROLE,
413
- "pagination-prev",
414
- );
415
- if (!element) {
416
- return;
417
- }
418
- }
419
- }
420
-
421
- if (!(element instanceof HTMLElement)) {
422
- return;
423
- }
424
-
425
- let page = null;
426
-
427
- if (!element.hasAttribute("data-page-no")) {
428
- return;
429
- }
430
-
431
- page = element.getAttribute("data-page-no");
432
-
433
- if (
434
- !page ||
435
- page === "" ||
436
- page === "…" ||
437
- page === null ||
438
- page === undefined ||
439
- page === "undefined" ||
440
- page === "null"
441
- ) {
442
- return;
443
- }
444
-
445
- if (typeof datasource.setParameters !== "function") {
446
- return;
447
- }
448
-
449
- event.preventDefault();
450
- datasource.setParameters({ page });
451
-
452
- if (typeof datasource.reload !== "function") {
453
- return;
454
- }
455
-
456
- datasource.reload();
457
- });
438
+ const self = this;
439
+
440
+ self[paginationElementSymbol].addEventListener("click", function (event) {
441
+ let element =
442
+ findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "pagination-item") ||
443
+ findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "pagination-next") ||
444
+ findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "pagination-prev");
445
+
446
+ if (
447
+ !(element instanceof HTMLElement) ||
448
+ !element.hasAttribute("data-page-no")
449
+ ) {
450
+ return;
451
+ }
452
+
453
+ const page = element.getAttribute("data-page-no");
454
+
455
+ if (!page || page === "…" || page === "null" || page === "undefined") {
456
+ return;
457
+ }
458
+
459
+ event.preventDefault();
460
+
461
+ const datasource = self[datasourceLinkedElementSymbol];
462
+ const clickCallback = self.getOption("callbacks.click");
463
+
464
+ if (datasource && typeof datasource.setParameters === "function") {
465
+ datasource.setParameters({ page });
466
+ if (typeof datasource.reload === "function") {
467
+ datasource.reload();
468
+ }
469
+ } else if (typeof clickCallback === "function") {
470
+ clickCallback(parseInt(page, 10), event);
471
+ }
472
+ });
458
473
  }
459
474
 
460
475
  /**
@@ -470,52 +485,52 @@ function initEventHandler() {
470
485
  * @throws {Error} the datasource could not be initialized
471
486
  */
472
487
  function initOptionsFromArguments() {
473
- const options = {};
474
- const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
475
- if (selector) {
476
- options.datasource = { selector: selector };
477
- }
488
+ const options = {};
489
+ const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
490
+ if (selector) {
491
+ options.datasource = { selector: selector };
492
+ }
478
493
 
479
- return options;
494
+ return options;
480
495
  }
481
496
 
482
497
  /**
483
498
  * @private
484
499
  */
485
500
  function handleDataSourceChanges() {
486
- let pagination;
487
-
488
- if (!this[datasourceLinkedElementSymbol]) {
489
- return;
490
- }
491
-
492
- const mapping = this.getOption("mapping");
493
- const pf = new Pathfinder(this[datasourceLinkedElementSymbol].data);
494
-
495
- for (const key in mapping) {
496
- const path = mapping[key];
497
-
498
- if (pf.exists(path)) {
499
- const value = pf.getVia(path);
500
- this.setOption(key, value);
501
- }
502
-
503
- const o = this[datasourceLinkedElementSymbol].getOption(path);
504
- if (o !== undefined && o !== null) {
505
- this.setOption(key, o);
506
- }
507
- }
508
-
509
- pagination = buildPagination.call(
510
- this,
511
- this.getOption("currentPage"),
512
- this.getOption("pages"),
513
- );
514
- if (this?.[sizeDataSymbol]?.showNumbers !== true) {
515
- pagination.items = [];
516
- }
517
-
518
- this.setOption("pagination", pagination);
501
+ let pagination;
502
+
503
+ if (!this[datasourceLinkedElementSymbol]) {
504
+ return;
505
+ }
506
+
507
+ const mapping = this.getOption("mapping");
508
+ const pf = new Pathfinder(this[datasourceLinkedElementSymbol].data);
509
+
510
+ for (const key in mapping) {
511
+ const path = mapping[key];
512
+
513
+ if (pf.exists(path)) {
514
+ const value = pf.getVia(path);
515
+ this.setOption(key, value);
516
+ }
517
+
518
+ const o = this[datasourceLinkedElementSymbol].getOption(path);
519
+ if (o !== undefined && o !== null) {
520
+ this.setOption(key, o);
521
+ }
522
+ }
523
+
524
+ pagination = buildPagination.call(
525
+ this,
526
+ this.getOption("currentPage"),
527
+ this.getOption("pages"),
528
+ );
529
+ if (this?.[sizeDataSymbol]?.showNumbers !== true) {
530
+ pagination.items = [];
531
+ }
532
+
533
+ this.setOption("pagination", pagination);
519
534
  }
520
535
 
521
536
  /**
@@ -525,97 +540,97 @@ function handleDataSourceChanges() {
525
540
  * @return {object}
526
541
  */
527
542
  function buildPagination(current, max) {
528
- current = parseInt(current, 10);
529
- max = parseInt(max, 10);
530
-
531
- let prev = current === 1 ? null : current - 1;
532
- let next = current === max ? null : current + 1;
533
- const itemList = [1];
534
-
535
- if (current > 4) itemList.push(-1);
536
-
537
- const r = 2;
538
- const r1 = current - r;
539
- const r2 = current + r;
540
-
541
- for (let i = r1 > 2 ? r1 : 2; i <= Math.min(max, r2); i++) itemList.push(i);
542
-
543
- if (r2 + 1 < max) itemList.push(-1);
544
- if (r2 < max) itemList.push(max);
545
-
546
- let prevClass = "";
547
-
548
- if (prev === null) {
549
- prevClass = " disabled";
550
- }
551
-
552
- let nextClass = "";
553
- if (next === null) {
554
- nextClass = " disabled";
555
- }
556
-
557
- const items = itemList.map((item) => {
558
- let p = `${item}`;
559
-
560
- if (item === -1) {
561
- item = null;
562
- p = "…";
563
- }
564
-
565
- const c = `${current}`;
566
-
567
- const obj = {
568
- pageNo: item, // as integer
569
- page: p, // as string
570
- current: p === c,
571
- class: (p === c ? "current" : "").trim(),
572
- };
573
-
574
- if (item === null) {
575
- obj.class += " disabled".trim();
576
- }
577
-
578
- const formatter = new Formatter(obj);
579
-
580
- obj.description = formatter.format(this.getOption("labels.description"));
581
- obj.label = formatter.format(this.getOption("labels.page"));
582
- obj.href =
583
- item === null
584
- ? "#"
585
- : p === c
586
- ? "#"
587
- : p === "1"
588
- ? "#"
589
- : `#${formatter.format(this.getOption("href"))}`;
590
- return obj;
591
- });
592
-
593
- const nextNo = next;
594
- next = `${next}`;
595
-
596
- const nextHref =
597
- next === "null"
598
- ? "#"
599
- : `#${new Formatter({ page: next }).format(this.getOption("href"))}`;
600
- const prevNo = prev;
601
- prev = `${prev}`;
602
- const prevHref =
603
- prev === "null"
604
- ? "#"
605
- : `#${new Formatter({ page: prev }).format(this.getOption("href"))}`;
606
-
607
- return {
608
- current,
609
- nextNo,
610
- next,
611
- nextClass,
612
- nextHref,
613
- prevNo,
614
- prev,
615
- prevClass,
616
- prevHref,
617
- items,
618
- };
543
+ current = parseInt(current, 10);
544
+ max = parseInt(max, 10);
545
+
546
+ let prev = current === 1 ? null : current - 1;
547
+ let next = current === max ? null : current + 1;
548
+ const itemList = [1];
549
+
550
+ if (current > 4) itemList.push(-1);
551
+
552
+ const r = 2;
553
+ const r1 = current - r;
554
+ const r2 = current + r;
555
+
556
+ for (let i = r1 > 2 ? r1 : 2; i <= Math.min(max, r2); i++) itemList.push(i);
557
+
558
+ if (r2 + 1 < max) itemList.push(-1);
559
+ if (r2 < max) itemList.push(max);
560
+
561
+ let prevClass = "";
562
+
563
+ if (prev === null) {
564
+ prevClass = " disabled";
565
+ }
566
+
567
+ let nextClass = "";
568
+ if (next === null) {
569
+ nextClass = " disabled";
570
+ }
571
+
572
+ const items = itemList.map((item) => {
573
+ let p = `${item}`;
574
+
575
+ if (item === -1) {
576
+ item = null;
577
+ p = "…";
578
+ }
579
+
580
+ const c = `${current}`;
581
+
582
+ const obj = {
583
+ pageNo: item, // as integer
584
+ page: p, // as string
585
+ current: p === c,
586
+ class: (p === c ? "current" : "").trim(),
587
+ };
588
+
589
+ if (item === null) {
590
+ obj.class += " disabled".trim();
591
+ }
592
+
593
+ const formatter = new Formatter(obj);
594
+
595
+ obj.description = formatter.format(this.getOption("labels.description"));
596
+ obj.label = formatter.format(this.getOption("labels.page"));
597
+ obj.href =
598
+ item === null
599
+ ? "#"
600
+ : p === c
601
+ ? "#"
602
+ : p === "1"
603
+ ? "#"
604
+ : `#${formatter.format(this.getOption("href"))}`;
605
+ return obj;
606
+ });
607
+
608
+ const nextNo = next;
609
+ next = `${next}`;
610
+
611
+ const nextHref =
612
+ next === "null"
613
+ ? "#"
614
+ : `#${new Formatter({ page: next }).format(this.getOption("href"))}`;
615
+ const prevNo = prev;
616
+ prev = `${prev}`;
617
+ const prevHref =
618
+ prev === "null"
619
+ ? "#"
620
+ : `#${new Formatter({ page: prev }).format(this.getOption("href"))}`;
621
+
622
+ return {
623
+ current,
624
+ nextNo,
625
+ next,
626
+ nextClass,
627
+ nextHref,
628
+ prevNo,
629
+ prev,
630
+ prevClass,
631
+ prevHref,
632
+ items,
633
+ };
619
634
  }
620
635
 
621
636
  /**
@@ -623,10 +638,12 @@ function buildPagination(current, max) {
623
638
  * @return {string}
624
639
  */
625
640
  function getTemplate() {
626
- // language=HTML
627
- return `
641
+ // language=HTML
642
+ return `
643
+
628
644
  <template id="items">
629
- <li><a data-monster-attributes="class path:items.class,
645
+ <li part="item"><a part="link"
646
+ data-monster-attributes="class path:items.class,
630
647
  href path:items.href,
631
648
  aria-label path:items.description,
632
649
  disabled path:items.disabled:?disabled:undefined,
@@ -636,19 +653,21 @@ function getTemplate() {
636
653
  data-monster-replace="path:items.label"></a></li>
637
654
  </template>
638
655
 
639
- <div data-monster-role="control">
640
- <nav data-monster-role="pagination" role="navigation" aria-label="pagination">
656
+ <div data-monster-role="control" part="control">
657
+ <nav data-monster-role="pagination" role="navigation" aria-label="pagination" part="nav">
641
658
  <ul class="pagination-list" data-monster-insert="items path:pagination.items"
642
- data-monster-select-this="true">
643
- <li part="pagination-prev" data-monster-role="pagination-prev"><a
659
+ data-monster-select-this="true" part="list">
660
+ <li part="prev" data-monster-role="pagination-prev"><a
644
661
  data-monster-role="pagination-prev"
662
+ part="prev-link"
645
663
  data-monster-attributes="
646
664
  class path:pagination.prevClass | prefix: previous,
647
665
  data-page-no path:pagination.prevNo,
648
666
  href path:pagination.prevHref | prefix: #"
649
667
  data-monster-replace="path:labels.previous">Previous</a></li>
650
- <li part="pagination-next" data-monster-role="pagination-next"><a
668
+ <li part="next" data-monster-role="pagination-next"><a
651
669
  data-monster-role="pagination-next"
670
+ part="next-link"
652
671
  data-monster-attributes="class path:pagination.nextClass | prefix: next,
653
672
  data-page-no path:pagination.nextNo,
654
673
  href path:pagination.nextHref | prefix: #"