@schukai/monster 4.39.0 → 4.40.0
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.
- package/CHANGELOG.md +13 -0
- package/package.json +1 -1
- package/source/components/accessibility/locale-select.mjs +1 -1
- package/source/components/datatable/pagination.mjs +515 -496
- package/source/components/form/select.mjs +269 -125
- package/source/components/form/style/select.pcss +11 -1
- package/source/components/form/stylesheet/select.mjs +7 -14
@@ -66,6 +66,8 @@ import { positionPopper } from "./util/floating-ui.mjs";
|
|
66
66
|
import { Pathfinder } from "../../data/pathfinder.mjs";
|
67
67
|
import { TokenList } from "../../types/tokenlist.mjs";
|
68
68
|
|
69
|
+
import "../datatable/pagination.mjs";
|
70
|
+
|
69
71
|
export {
|
70
72
|
getSelectionTemplate,
|
71
73
|
getSummaryTemplate,
|
@@ -152,6 +154,13 @@ const changeEventHandler = Symbol("changeEventHandler");
|
|
152
154
|
*/
|
153
155
|
const controlElementSymbol = Symbol("controlElement");
|
154
156
|
|
157
|
+
/**
|
158
|
+
* local symbol
|
159
|
+
* @private
|
160
|
+
* @type {Symbol}
|
161
|
+
*/
|
162
|
+
const paginationElementSymbol = Symbol("paginationElement");
|
163
|
+
|
155
164
|
/**
|
156
165
|
* local symbol
|
157
166
|
* @private
|
@@ -251,6 +260,17 @@ const cleanupOptionsListSymbol = Symbol("cleanupOptionsList");
|
|
251
260
|
const debounceOptionsMutationObserverSymbol = Symbol(
|
252
261
|
"debounceOptionsMutationObserver",
|
253
262
|
);
|
263
|
+
/**
|
264
|
+
* @private
|
265
|
+
* @type {symbol}
|
266
|
+
*/
|
267
|
+
const currentPageSymbol = Symbol("currentPage");
|
268
|
+
|
269
|
+
/**
|
270
|
+
* @private
|
271
|
+
* @type {symbol}
|
272
|
+
*/
|
273
|
+
const remoteFilterFirstOpendSymbol = Symbol("remoteFilterFirstOpend");
|
254
274
|
|
255
275
|
/**
|
256
276
|
* @private
|
@@ -307,6 +327,9 @@ const FILTER_POSITION_INLINE = "inline";
|
|
307
327
|
* @example /examples/components/form/select-fetch Fetch options
|
308
328
|
* @example /examples/components/form/select-lazy Lazy load
|
309
329
|
* @example /examples/components/form/select-remote-filter Remote filter
|
330
|
+
* @example /examples/components/form/select-remote-filter Server-side filtering with a remote URL
|
331
|
+
* @example /examples/components/form/select-remote-pagination Server-side filtering with pagination
|
332
|
+
* @example /examples/components/form/select-summary-template Using a summary template for selections
|
310
333
|
*
|
311
334
|
* @copyright schukai GmbH
|
312
335
|
* @summary A beautiful select control that can make your life easier and also looks good.
|
@@ -322,6 +345,7 @@ class Select extends CustomControl {
|
|
322
345
|
*/
|
323
346
|
constructor() {
|
324
347
|
super();
|
348
|
+
this[currentPageSymbol] = 1;
|
325
349
|
initOptionObserver.call(this);
|
326
350
|
}
|
327
351
|
|
@@ -379,74 +403,91 @@ class Select extends CustomControl {
|
|
379
403
|
addErrorAttribute(this, e);
|
380
404
|
});
|
381
405
|
}
|
382
|
-
|
383
406
|
/**
|
384
|
-
*
|
407
|
+
* Defines the default configuration options for the monster-control.
|
408
|
+
* These options can be overridden via the HTML attribute `data-monster-options`.
|
385
409
|
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
|
386
410
|
*
|
387
|
-
*
|
388
|
-
*
|
389
|
-
* @property {
|
390
|
-
* @property {
|
391
|
-
* @property {
|
392
|
-
* @property {
|
393
|
-
* @property {
|
394
|
-
* @property {
|
395
|
-
* @property {
|
396
|
-
* @property {
|
397
|
-
* @property {
|
398
|
-
* @property {
|
399
|
-
* @property {
|
400
|
-
* @property {string} fetch.redirect Fetch redirect mode (e.g
|
401
|
-
* @property {string} fetch.method HTTP method for fetching options (e.g
|
402
|
-
* @property {string} fetch.mode Fetch mode (e.g
|
403
|
-
* @property {string} fetch.credentials Credentials policy for fetch (e.g
|
404
|
-
* @property {Object.<string,string>} fetch.headers HTTP headers
|
405
|
-
* @property {
|
406
|
-
* @property {string} labels.
|
407
|
-
* @property {string} labels.
|
408
|
-
* @property {string} labels.
|
409
|
-
* @property {string} labels.
|
410
|
-
* @property {string} labels.no-options-
|
411
|
-
* @property {string} labels.
|
412
|
-
* @property {string} labels.summary-text.
|
413
|
-
* @property {string} labels.summary-text.
|
414
|
-
* @property {
|
415
|
-
* @property {
|
416
|
-
* @property {boolean} features.
|
417
|
-
* @property {boolean} features.
|
418
|
-
* @property {boolean} features.
|
419
|
-
* @property {boolean} features.
|
420
|
-
* @property {boolean} features.
|
421
|
-
* @property {boolean} features.
|
422
|
-
* @property {
|
423
|
-
* @property {
|
424
|
-
* @property {Object}
|
425
|
-
* @property {string}
|
426
|
-
* @property {
|
427
|
-
* @property {
|
428
|
-
* @property {
|
429
|
-
* @property {string} filter.
|
430
|
-
* @property {
|
431
|
-
* @property {
|
432
|
-
* @property {string}
|
433
|
-
* @property {
|
434
|
-
* @property {string}
|
435
|
-
* @property {
|
436
|
-
* @property {
|
437
|
-
* @property {string}
|
438
|
-
* @property {
|
439
|
-
* @property {
|
440
|
-
* @property {
|
441
|
-
* @property {
|
442
|
-
* @property {Array}
|
443
|
-
* @property {
|
444
|
-
* @property {
|
445
|
-
* @property {string}
|
446
|
-
* @property {string}
|
447
|
-
* @property {
|
448
|
-
* @property {string}
|
449
|
-
* @property {
|
411
|
+
* @property {string[]} toggleEventType - Array of DOM event names (e.g., ["click", "touch"]) that toggle the dropdown.
|
412
|
+
* @property {boolean} delegatesFocus - If `true`, the element delegates focus to its internal control (e.g., the filter input).
|
413
|
+
* @property {Array<Object>} options - Array of option objects `{label, value, visibility?, data?}` for a static option list.
|
414
|
+
* @property {string|string[]} selection - Initial selected value(s), as a string, comma-separated string, or array of strings.
|
415
|
+
* @property {number} showMaxOptions - Maximum number of visible options before the list becomes scrollable.
|
416
|
+
* @property {"radio"|"checkbox"} type - Selection mode: "radio" for single selection, "checkbox" for multiple selections.
|
417
|
+
* @property {string} name - Name of the hidden form field for form submission.
|
418
|
+
* @property {string|null} url - URL to dynamically fetch options via HTTP when opening or filtering.
|
419
|
+
* @property {number|null} total - Total number of available options, useful for pagination with remote data.
|
420
|
+
* @property {Object} lookup - Configuration for fetching initially selected values.
|
421
|
+
* @property {string|null} lookup.url - URL template with a `${filter}` placeholder to look up selected entries on initialization. Used when `url` is set and either `features.lazyLoad` is active or `filter.mode` is `"remote"`.
|
422
|
+
* @property {boolean} lookup.grouping - If `true`, all selected values are fetched in a single request; otherwise, a separate request is sent for each value.
|
423
|
+
* @property {Object} fetch - Configuration for HTTP requests via `fetch`.
|
424
|
+
* @property {string} fetch.redirect - Fetch redirect mode (e.g., "error", "follow").
|
425
|
+
* @property {string} fetch.method - HTTP method for fetching options (e.g., "GET", "POST").
|
426
|
+
* @property {string} fetch.mode - Fetch mode (e.g., "cors", "same-origin").
|
427
|
+
* @property {string} fetch.credentials - Credentials policy for fetch (e.g., "include", "same-origin").
|
428
|
+
* @property {Object.<string, string>} fetch.headers - HTTP headers to be sent with every request.
|
429
|
+
* @property {Object} labels - Text labels for various states and UI elements.
|
430
|
+
* @property {string} labels.cannot-be-loaded - Message displayed when options cannot be loaded.
|
431
|
+
* @property {string} labels.no-options-available - Message displayed when no static options are provided.
|
432
|
+
* @property {string} labels.click-to-load-options - Prompt to load options when `features.lazyLoad` is enabled.
|
433
|
+
* @property {string} labels.select-an-option - Placeholder text when no selection has been made.
|
434
|
+
* @property {string} labels.no-options - Message displayed when no options are available from slots or fetch.
|
435
|
+
* @property {string} labels.no-options-found - Message displayed when the filter yields no matching options.
|
436
|
+
* @property {string} labels.summary-text.zero - Pluralization template for zero selected entries (e.g., "No entries selected").
|
437
|
+
* @property {string} labels.summary-text.one - Pluralization template for one selected entry.
|
438
|
+
* @property {string} labels.summary-text.other - Pluralization template for multiple selected entries.
|
439
|
+
* @property {Object} features - Toggles to enable/disable functionalities.
|
440
|
+
* @property {boolean} features.clearAll - Shows a "Clear all" button to reset the selection.
|
441
|
+
* @property {boolean} features.clear - Shows a "remove" icon on individual selection badges.
|
442
|
+
* @property {boolean} features.lazyLoad - If `true`, options are fetched from the `url` only when the dropdown is first opened. Ignored if `filter.mode` is `"remote"`.
|
443
|
+
* @property {boolean} features.closeOnSelect - If `true`, the dropdown closes automatically after a selection is made.
|
444
|
+
* @property {boolean} features.emptyValueIfNoOptions - Sets the hidden field's value to empty if no options are available.
|
445
|
+
* @property {boolean} features.storeFetchedData - If `true`, the raw fetched data is stored and can be retrieved via `getLastFetchedData()`.
|
446
|
+
* @property {boolean} features.useStrictValueComparison - Uses strict comparison (`===`) when matching option values.
|
447
|
+
* @property {boolean} features.showRemoteInfo - When `filter.mode === "remote"`, displays an info badge indicating that more options may exist on the server.
|
448
|
+
* @property {Object} remoteInfo - Configuration for the remote info display.
|
449
|
+
* @property {string|null} remoteInfo.url - URL to fetch the total count of remote options (used when `filter.mode === "remote"`).
|
450
|
+
* @property {Object} placeholder - Placeholder texts for input fields.
|
451
|
+
* @property {string} placeholder.filter - Placeholder text for the filter input field.
|
452
|
+
* @property {Object} filter - Configuration for the filtering functionality.
|
453
|
+
* @property {string|null} filter.defaultValue - Default filter value for remote requests. An empty string will prevent the initial request.
|
454
|
+
* @property {"options"|"remote"|"disabled"} filter.mode - Filter mode: `"options"` (client-side), `"remote"` (server-side, `lazyLoad` is ignored), or `"disabled"`.
|
455
|
+
* @property {"inline"|"popper"} filter.position - Position of the filter input: `"inline"` (inside the control) or `"popper"` (inside the dropdown).
|
456
|
+
* @property {string|null} filter.defaultOptionsUrl - URL to load an initial list of options when `filter.mode` is `"remote"` and no filter value has been entered.
|
457
|
+
* @property {Object} filter.marker - Markers for embedding the filter value into the `url` for server-side filtering.
|
458
|
+
* @property {string} filter.marker.open - Opening marker (e.g., `{`).
|
459
|
+
* @property {string} filter.marker.close - Closing marker (e.g., `}`).
|
460
|
+
* @property {Object} templates - HTML templates for the main components.
|
461
|
+
* @property {string} templates.main - HTML template string for the control's basic structure.
|
462
|
+
* @property {Object} templateMapping - Mapping of templates for specific use cases.
|
463
|
+
* @property {string} templateMapping.selected - Template variant for selected items (e.g., for rendering as a badge).
|
464
|
+
* @property {Object} popper - Configuration for Popper.js to position the dropdown.
|
465
|
+
* @property {string} popper.placement - Popper.js placement strategy (e.g., "bottom-start").
|
466
|
+
* @property {Array<string|Object>} popper.middleware - Array of middleware configurations for Popper.js (e.g., ["flip", "offset:1"]).
|
467
|
+
* @property {Object} mapping - Defines how fetched data is transformed into `options`.
|
468
|
+
* @property {string} mapping.selector - Path/selector to find the array of items within the fetched data (e.g., "results" for `data.results`).
|
469
|
+
* @property {string} mapping.labelTemplate - Template for the option label using placeholders (e.g., `Label: ${name}`).
|
470
|
+
* @property {string} mapping.valueTemplate - Template for the option value using placeholders (e.g., `ID: ${id}`).
|
471
|
+
* @property {Function|null} mapping.filter - Optional callback function `(item) => boolean` to filter fetched items before processing.
|
472
|
+
* @property {string|null} mapping.total - Path/selector to the total number of items for pagination (e.g., "pagination.total").
|
473
|
+
* @property {string|null} mapping.currentPage - Path/selector to the current page number.
|
474
|
+
* @property {string|null} mapping.objectsPerPage - Path/selector to the number of objects per page.
|
475
|
+
* @property {Object} empty - Handling of empty or undefined values.
|
476
|
+
* @property {string} empty.defaultValueRadio - Default value for `type="radio"` when no selection exists.
|
477
|
+
* @property {Array} empty.defaultValueCheckbox - Default value (empty array) for `type="checkbox"`.
|
478
|
+
* @property {Array} empty.equivalents - Values that are considered "empty" (e.g., `undefined`, `null`, `""`) and are normalized to the default value.
|
479
|
+
* @property {Object} formatter - Functions for formatting display values.
|
480
|
+
* @property {Function} formatter.selection - Callback `(value, option) => string` to format the display text of selected values.
|
481
|
+
* @property {Object} classes - CSS classes for various elements.
|
482
|
+
* @property {string} classes.badge - CSS class for selection badges.
|
483
|
+
* @property {string} classes.statusOrRemoveBadge - CSS class for the status or remove badge.
|
484
|
+
* @property {string} classes.remoteInfo - CSS class for the remote info badge.
|
485
|
+
* @property {string} classes.noOptions - CSS class for the "no options" message.
|
486
|
+
* @property {Object} messages - Internal messages for ARIA attributes and screen readers (should not normally be changed).
|
487
|
+
* @property {string|null} messages.control - Message for the main control element.
|
488
|
+
* @property {string|null} messages.selected - Message for the selected items area.
|
489
|
+
* @property {string|null} messages.emptyOptions - Message for an empty options list.
|
490
|
+
* @property {string|null} messages.total - Message that communicates the total number of options.
|
450
491
|
*/
|
451
492
|
get defaults() {
|
452
493
|
return Object.assign(
|
@@ -529,6 +570,8 @@ class Select extends CustomControl {
|
|
529
570
|
valueTemplate: "",
|
530
571
|
filter: null,
|
531
572
|
total: null,
|
573
|
+
currentPage: null,
|
574
|
+
objectsPerPage: null,
|
532
575
|
},
|
533
576
|
|
534
577
|
empty: {
|
@@ -781,10 +824,7 @@ class Select extends CustomControl {
|
|
781
824
|
addErrorAttribute(this, e);
|
782
825
|
});
|
783
826
|
|
784
|
-
if (
|
785
|
-
this.parentElement &&
|
786
|
-
this.parentElement.nodeName === "MONSTER-BUTTON-BAR"
|
787
|
-
) {
|
827
|
+
if (this.parentElement.nodeName === "MONSTER-BUTTON-BAR") {
|
788
828
|
this.shadowRoot
|
789
829
|
.querySelector("[data-monster-role=control]")
|
790
830
|
.classList.add("in-button-bar");
|
@@ -847,6 +887,49 @@ class Select extends CustomControl {
|
|
847
887
|
}
|
848
888
|
}
|
849
889
|
|
890
|
+
/**
|
891
|
+
* @private
|
892
|
+
* @param {object} data Die rohen Daten aus der API-Antwort.
|
893
|
+
*/
|
894
|
+
function processAndApplyPaginationData(data) {
|
895
|
+
if (!this[paginationElementSymbol]) {
|
896
|
+
return;
|
897
|
+
}
|
898
|
+
|
899
|
+
const mappingTotal = this.getOption("mapping.total");
|
900
|
+
const mappingCurrentPage = this.getOption("mapping.currentPage");
|
901
|
+
const mappingObjectsPerPage = this.getOption("mapping.objectsPerPage");
|
902
|
+
|
903
|
+
if (!mappingTotal || !mappingCurrentPage || !mappingObjectsPerPage) {
|
904
|
+
return;
|
905
|
+
}
|
906
|
+
|
907
|
+
try {
|
908
|
+
const pathfinder = new Pathfinder(data);
|
909
|
+
const total = pathfinder.getVia(mappingTotal);
|
910
|
+
const currentPage = pathfinder.getVia(mappingCurrentPage);
|
911
|
+
const objectsPerPage = pathfinder.getVia(mappingObjectsPerPage);
|
912
|
+
|
913
|
+
if (!isInteger(total)) {
|
914
|
+
addErrorAttribute(this, "total is not an integer");
|
915
|
+
return;
|
916
|
+
}
|
917
|
+
|
918
|
+
this.setOption("total", total);
|
919
|
+
|
920
|
+
if (
|
921
|
+
isInteger(currentPage) &&
|
922
|
+
currentPage > 0 &&
|
923
|
+
isInteger(objectsPerPage) &&
|
924
|
+
objectsPerPage > 0
|
925
|
+
) {
|
926
|
+
updatePagination.call(this, total, currentPage, objectsPerPage);
|
927
|
+
}
|
928
|
+
} catch (e) {
|
929
|
+
addErrorAttribute(this, e);
|
930
|
+
}
|
931
|
+
}
|
932
|
+
|
850
933
|
/**
|
851
934
|
* @private
|
852
935
|
* @param data
|
@@ -911,6 +994,8 @@ function importOptionsIntern(data) {
|
|
911
994
|
options = this.getOption("options", []);
|
912
995
|
}
|
913
996
|
|
997
|
+
this[cleanupOptionsListSymbol] = false;
|
998
|
+
|
914
999
|
if (!isIterable(map)) {
|
915
1000
|
throw new Error("map is not iterable");
|
916
1001
|
}
|
@@ -1419,6 +1504,9 @@ function getTranslations() {
|
|
1419
1504
|
}
|
1420
1505
|
}
|
1421
1506
|
|
1507
|
+
/**
|
1508
|
+
* @private
|
1509
|
+
*/
|
1422
1510
|
/**
|
1423
1511
|
* @private
|
1424
1512
|
*/
|
@@ -1429,37 +1517,25 @@ function lookupSelection() {
|
|
1429
1517
|
(entries, obs) => {
|
1430
1518
|
for (const entry of entries) {
|
1431
1519
|
if (entry.isIntersecting) {
|
1432
|
-
obs.disconnect();
|
1520
|
+
obs.disconnect();
|
1433
1521
|
|
1434
1522
|
setTimeout(() => {
|
1435
1523
|
const selection = self.getOption("selection");
|
1436
|
-
if (
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
return;
|
1442
|
-
}
|
1443
|
-
|
1444
|
-
if (self[lazyLoadDoneSymbol] === true) {
|
1524
|
+
if (
|
1525
|
+
selection.length === 0 ||
|
1526
|
+
self[isLoadingSymbol] ||
|
1527
|
+
self[lazyLoadDoneSymbol]
|
1528
|
+
) {
|
1445
1529
|
return;
|
1446
1530
|
}
|
1447
1531
|
|
1448
|
-
let url = self.getOption("url");
|
1449
|
-
const lookupUrl = self.getOption("lookup.url");
|
1450
|
-
if (lookupUrl !== null) {
|
1451
|
-
url = lookupUrl;
|
1452
|
-
}
|
1453
|
-
|
1532
|
+
let url = self.getOption("lookup.url") || self.getOption("url");
|
1454
1533
|
self[cleanupOptionsListSymbol] = false;
|
1455
1534
|
|
1456
1535
|
if (self.getOption("lookup.grouping") === true) {
|
1536
|
+
const values = selection.map((s) => s?.["value"]);
|
1457
1537
|
filterFromRemoteByValue
|
1458
|
-
.call(
|
1459
|
-
self,
|
1460
|
-
url,
|
1461
|
-
selection.map((s) => s?.["value"]),
|
1462
|
-
)
|
1538
|
+
.call(self, url, { filter: values.join(",") })
|
1463
1539
|
.catch((e) => {
|
1464
1540
|
addErrorAttribute(self, e);
|
1465
1541
|
});
|
@@ -1469,7 +1545,7 @@ function lookupSelection() {
|
|
1469
1545
|
for (const s of selection) {
|
1470
1546
|
if (s?.["value"]) {
|
1471
1547
|
filterFromRemoteByValue
|
1472
|
-
.call(self, url, s["value"])
|
1548
|
+
.call(self, url, { filter: s["value"] })
|
1473
1549
|
.catch((e) => {
|
1474
1550
|
addErrorAttribute(self, e);
|
1475
1551
|
});
|
@@ -1482,10 +1558,8 @@ function lookupSelection() {
|
|
1482
1558
|
{ threshold: 0.1 },
|
1483
1559
|
);
|
1484
1560
|
|
1485
|
-
// Beobachte das Element selbst (dieses Element muss im DOM sein)
|
1486
1561
|
observer.observe(self);
|
1487
1562
|
}
|
1488
|
-
|
1489
1563
|
/**
|
1490
1564
|
*
|
1491
1565
|
* @param url
|
@@ -1523,6 +1597,7 @@ function fetchIt(url, controlOptions) {
|
|
1523
1597
|
) {
|
1524
1598
|
try {
|
1525
1599
|
importOptionsIntern.call(self, map);
|
1600
|
+
processAndApplyPaginationData.call(self, map);
|
1526
1601
|
} catch (e) {
|
1527
1602
|
setStatusOrRemoveBadges.call(this, "error");
|
1528
1603
|
reject(e);
|
@@ -2252,6 +2327,7 @@ function handleFilterKeyEvents() {
|
|
2252
2327
|
if (getFilterMode.call(this) !== FILTER_MODE_REMOTE) {
|
2253
2328
|
filterOptions.call(this);
|
2254
2329
|
} else {
|
2330
|
+
this[currentPageSymbol] = 1;
|
2255
2331
|
this[cleanupOptionsListSymbol] = true;
|
2256
2332
|
|
2257
2333
|
filterFromRemote.call(this).catch((e) => {
|
@@ -2291,7 +2367,6 @@ function filterFromRemote() {
|
|
2291
2367
|
filterValue = this[inlineFilterElementSymbol].value.toLowerCase();
|
2292
2368
|
}
|
2293
2369
|
showFlag = true;
|
2294
|
-
|
2295
2370
|
break;
|
2296
2371
|
case FILTER_POSITION_POPPER:
|
2297
2372
|
default:
|
@@ -2300,24 +2375,44 @@ function filterFromRemote() {
|
|
2300
2375
|
}
|
2301
2376
|
}
|
2302
2377
|
|
2303
|
-
|
2378
|
+
const params = {
|
2379
|
+
filter: filterValue,
|
2380
|
+
page: this[currentPageSymbol] || 1,
|
2381
|
+
};
|
2382
|
+
|
2383
|
+
return filterFromRemoteByValue.call(this, url, params, showFlag);
|
2304
2384
|
}
|
2305
2385
|
|
2306
2386
|
/**
|
2307
2387
|
* @private
|
2308
2388
|
* @param url
|
2309
|
-
* @param
|
2389
|
+
* @param {object} params
|
2310
2390
|
* @returns {string}
|
2311
2391
|
*/
|
2312
|
-
function formatURL(url,
|
2313
|
-
|
2314
|
-
|
2315
|
-
|
2316
|
-
|
2392
|
+
function formatURL(url, params = {}) {
|
2393
|
+
// Die Logik für den Default-Filterwert bleibt erhalten
|
2394
|
+
if (
|
2395
|
+
params.filter === undefined ||
|
2396
|
+
params.filter === null ||
|
2397
|
+
params.filter === ""
|
2398
|
+
) {
|
2399
|
+
const defaultValue = this.getOption("filter.defaultValue");
|
2400
|
+
if (defaultValue === undefined || defaultValue === null) {
|
2401
|
+
params.filter = disabledRequestMarker.toString();
|
2402
|
+
} else {
|
2403
|
+
params.filter = defaultValue;
|
2317
2404
|
}
|
2318
2405
|
}
|
2319
2406
|
|
2320
|
-
const
|
2407
|
+
const encodedParams = {};
|
2408
|
+
for (const key in params) {
|
2409
|
+
if (Object.hasOwn(params, key)) {
|
2410
|
+
const value = params[key];
|
2411
|
+
encodedParams[key] = encodeURI(String(value));
|
2412
|
+
}
|
2413
|
+
}
|
2414
|
+
|
2415
|
+
const formatter = new Formatter(encodedParams);
|
2321
2416
|
const openMarker = this.getOption("filter.marker.open");
|
2322
2417
|
let closeMarker = this.getOption("filter.marker.close");
|
2323
2418
|
if (!closeMarker) {
|
@@ -2339,14 +2434,15 @@ function formatURL(url, value) {
|
|
2339
2434
|
* @param {boolean} [openPopper] Flag indicating whether to open the popper.
|
2340
2435
|
* @return {string} The formatted URL with the applied filters and markers.
|
2341
2436
|
*/
|
2342
|
-
function filterFromRemoteByValue(optionUrl,
|
2437
|
+
function filterFromRemoteByValue(optionUrl, params, openPopper) {
|
2343
2438
|
return new Processing(() => {
|
2344
|
-
let url = formatURL.call(this, optionUrl,
|
2439
|
+
let url = formatURL.call(this, optionUrl, params);
|
2440
|
+
|
2345
2441
|
if (url.indexOf(disabledRequestMarker.toString()) !== -1) {
|
2346
|
-
return;
|
2442
|
+
return Promise.resolve();
|
2347
2443
|
}
|
2348
2444
|
|
2349
|
-
fetchIt
|
2445
|
+
return fetchIt
|
2350
2446
|
.call(this, url, {
|
2351
2447
|
disableHiding: true,
|
2352
2448
|
})
|
@@ -2359,12 +2455,9 @@ function filterFromRemoteByValue(optionUrl, value, openPopper) {
|
|
2359
2455
|
.catch((e) => {
|
2360
2456
|
addErrorAttribute(this, e);
|
2361
2457
|
setStatusOrRemoveBadges.call(this, "error");
|
2458
|
+
throw e;
|
2362
2459
|
});
|
2363
|
-
})
|
2364
|
-
.run()
|
2365
|
-
.catch((e) => {
|
2366
|
-
throw e;
|
2367
|
-
});
|
2460
|
+
}).run();
|
2368
2461
|
}
|
2369
2462
|
|
2370
2463
|
/**
|
@@ -2646,7 +2739,21 @@ function areOptionsAvailableAndInitInternal() {
|
|
2646
2739
|
|
2647
2740
|
this[areOptionsAvailableAndInitSymbol]++;
|
2648
2741
|
|
2649
|
-
|
2742
|
+
let options = this.getOption("options");
|
2743
|
+
|
2744
|
+
if (isArray(options) && Array.length === 1 && isString(options?.[0])) {
|
2745
|
+
try {
|
2746
|
+
const obj = JSON.parse(options[0]);
|
2747
|
+
if (isArray(obj)) {
|
2748
|
+
this.setOption("options", obj);
|
2749
|
+
options = obj;
|
2750
|
+
}
|
2751
|
+
} catch (e) {
|
2752
|
+
addErrorAttribute(this, e);
|
2753
|
+
setStatusOrRemoveBadges.call(this, "error");
|
2754
|
+
return false;
|
2755
|
+
}
|
2756
|
+
}
|
2650
2757
|
|
2651
2758
|
if (
|
2652
2759
|
options === undefined ||
|
@@ -2688,7 +2795,6 @@ function areOptionsAvailableAndInitInternal() {
|
|
2688
2795
|
}
|
2689
2796
|
}, 1000);
|
2690
2797
|
|
2691
|
-
//this.setOption("messages.control", msg);
|
2692
2798
|
this.setOption("messages.summary", "");
|
2693
2799
|
|
2694
2800
|
if (this.getOption("features.emptyValueIfNoOptions") === true) {
|
@@ -2717,6 +2823,11 @@ function areOptionsAvailableAndInitInternal() {
|
|
2717
2823
|
let updated = false;
|
2718
2824
|
let valueCounter = 1;
|
2719
2825
|
for (const option of options) {
|
2826
|
+
if (isObject(option) === false) {
|
2827
|
+
console.error("option is not an object", option);
|
2828
|
+
continue;
|
2829
|
+
}
|
2830
|
+
|
2720
2831
|
if (option?.visibility === undefined) {
|
2721
2832
|
option.visibility = "visible";
|
2722
2833
|
updated = true;
|
@@ -3058,6 +3169,8 @@ function hide() {
|
|
3058
3169
|
* @private
|
3059
3170
|
*/
|
3060
3171
|
function show() {
|
3172
|
+
const self = this;
|
3173
|
+
|
3061
3174
|
if (this.getOption("disabled", undefined) === true) {
|
3062
3175
|
return;
|
3063
3176
|
}
|
@@ -3117,6 +3230,16 @@ function show() {
|
|
3117
3230
|
addAttributeToken(this[controlElementSymbol], "class", "open");
|
3118
3231
|
|
3119
3232
|
new Processing(() => {
|
3233
|
+
if (!self?.[remoteFilterFirstOpendSymbol]) {
|
3234
|
+
self[remoteFilterFirstOpendSymbol] = true;
|
3235
|
+
setTimeout(
|
3236
|
+
() =>
|
3237
|
+
filterFromRemote.call(self).catch((e) => {
|
3238
|
+
addErrorAttribute(self, e);
|
3239
|
+
}),
|
3240
|
+
0,
|
3241
|
+
);
|
3242
|
+
}
|
3120
3243
|
calcAndSetOptionsDimension.call(this);
|
3121
3244
|
focusFilter.call(this);
|
3122
3245
|
this[popperElementSymbol].style.removeProperty("visibility");
|
@@ -3150,6 +3273,9 @@ function initDefaultOptionsFromUrl() {
|
|
3150
3273
|
});
|
3151
3274
|
}
|
3152
3275
|
|
3276
|
+
/**
|
3277
|
+
* @private
|
3278
|
+
*/
|
3153
3279
|
/**
|
3154
3280
|
* @private
|
3155
3281
|
*/
|
@@ -3159,9 +3285,9 @@ function initTotal() {
|
|
3159
3285
|
}
|
3160
3286
|
|
3161
3287
|
const url = this.getOption("remoteInfo.url");
|
3162
|
-
const
|
3288
|
+
const mappingTotal = this.getOption("mapping.total");
|
3163
3289
|
|
3164
|
-
if (!isString(
|
3290
|
+
if (!isString(mappingTotal) || !isString(url)) {
|
3165
3291
|
return;
|
3166
3292
|
}
|
3167
3293
|
|
@@ -3171,28 +3297,20 @@ function initTotal() {
|
|
3171
3297
|
.fetch(url, fetchOptions)
|
3172
3298
|
.then((response) => {
|
3173
3299
|
if (!response.ok) {
|
3174
|
-
// Improved status checking using `response.ok`
|
3175
3300
|
addErrorAttribute(
|
3176
3301
|
this,
|
3177
3302
|
`HTTP error status: ${response.status} - ${response.statusText}`,
|
3178
3303
|
);
|
3179
3304
|
return;
|
3180
3305
|
}
|
3181
|
-
|
3182
3306
|
return response.text();
|
3183
3307
|
})
|
3184
3308
|
.then((text) => {
|
3309
|
+
if (!text) return;
|
3185
3310
|
try {
|
3186
3311
|
const data = JSON.parse(String(text));
|
3187
|
-
const pathfinder = new Pathfinder(data);
|
3188
|
-
const total = pathfinder.getVia(mapping);
|
3189
3312
|
|
3190
|
-
|
3191
|
-
addErrorAttribute(this, "total is not an integer");
|
3192
|
-
return;
|
3193
|
-
}
|
3194
|
-
|
3195
|
-
this.setOption("total", total);
|
3313
|
+
processAndApplyPaginationData.call(this, data);
|
3196
3314
|
} catch (e) {
|
3197
3315
|
addErrorAttribute(this, e);
|
3198
3316
|
}
|
@@ -3202,6 +3320,16 @@ function initTotal() {
|
|
3202
3320
|
});
|
3203
3321
|
}
|
3204
3322
|
|
3323
|
+
function updatePagination(total, currentPage, objectsPerPage) {
|
3324
|
+
const totalPages = Math.ceil(total / objectsPerPage);
|
3325
|
+
this[paginationElementSymbol].style.display = "block";
|
3326
|
+
this[paginationElementSymbol].setOption("objectsPerPage", objectsPerPage);
|
3327
|
+
this[paginationElementSymbol].setPaginationState({
|
3328
|
+
totalPages: totalPages,
|
3329
|
+
currentPage: currentPage,
|
3330
|
+
});
|
3331
|
+
}
|
3332
|
+
|
3205
3333
|
/**
|
3206
3334
|
* @private
|
3207
3335
|
*/
|
@@ -3441,6 +3569,16 @@ function initEventHandler() {
|
|
3441
3569
|
subtree: true,
|
3442
3570
|
});
|
3443
3571
|
|
3572
|
+
if (this[paginationElementSymbol]) {
|
3573
|
+
this[paginationElementSymbol].style.display = "none";
|
3574
|
+
this[paginationElementSymbol].setOption("callbacks.click", (page) => {
|
3575
|
+
self[currentPageSymbol] = page;
|
3576
|
+
self[cleanupOptionsListSymbol] = true;
|
3577
|
+
filterFromRemote.call(self).catch((e) => {
|
3578
|
+
addErrorAttribute(self, e);
|
3579
|
+
});
|
3580
|
+
});
|
3581
|
+
}
|
3444
3582
|
return self;
|
3445
3583
|
}
|
3446
3584
|
|
@@ -3524,6 +3662,10 @@ function initControlReferences() {
|
|
3524
3662
|
throw new Error("no shadow-root is defined");
|
3525
3663
|
}
|
3526
3664
|
|
3665
|
+
this[paginationElementSymbol] = this.shadowRoot.querySelector(
|
3666
|
+
`[${ATTRIBUTE_ROLE}=pagination]`,
|
3667
|
+
);
|
3668
|
+
|
3527
3669
|
this[controlElementSymbol] = this.shadowRoot.querySelector(
|
3528
3670
|
`[${ATTRIBUTE_ROLE}=control]`,
|
3529
3671
|
);
|
@@ -3648,10 +3790,12 @@ function getTemplate() {
|
|
3648
3790
|
autocomplete="off"
|
3649
3791
|
tabindex="0">
|
3650
3792
|
</div>
|
3793
|
+
|
3651
3794
|
<div part="content" class="flex" data-monster-replace="path:content">
|
3652
3795
|
<div part="options" data-monster-role="options" data-monster-insert="options path:options"
|
3653
3796
|
tabindex="-1"></div>
|
3654
3797
|
</div>
|
3798
|
+
<monster-pagination data-monster-role="pagination"></monster-pagination>
|
3655
3799
|
<div part="remote-info"
|
3656
3800
|
data-monster-role="remote-info"
|
3657
3801
|
data-monster-attributes="class path:classes.remoteInfo"
|