@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.
- package/CHANGELOG.md +21 -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 +270 -128
- 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 (
|
1524
|
+
if (
|
1525
|
+
selection.length === 0 ||
|
1526
|
+
self[isLoadingSymbol] ||
|
1527
|
+
self[lazyLoadDoneSymbol]
|
1528
|
+
) {
|
1437
1529
|
return;
|
1438
1530
|
}
|
1439
1531
|
|
1440
|
-
|
1441
|
-
return;
|
1442
|
-
}
|
1443
|
-
|
1444
|
-
if (self[lazyLoadDoneSymbol] === true) {
|
1445
|
-
return;
|
1446
|
-
}
|
1447
|
-
|
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);
|
@@ -1863,21 +1938,19 @@ function setTotalText() {
|
|
1863
1938
|
|
1864
1939
|
const count = this.getOption("options").length;
|
1865
1940
|
const total = Number.parseInt(this.getOption("total"));
|
1866
|
-
|
1867
1941
|
if (Number.isNaN(total)) {
|
1868
1942
|
this.setOption("messages.total", "");
|
1869
1943
|
return;
|
1870
1944
|
}
|
1871
1945
|
|
1872
1946
|
const translations = getDefaultTranslation.call(this);
|
1873
|
-
const text = translations.getPluralRuleText("total", total, "");
|
1874
1947
|
|
1875
1948
|
const diff = total - count;
|
1876
1949
|
if (diff < 0) {
|
1877
1950
|
this.setOption("messages.total", "");
|
1878
1951
|
return;
|
1879
1952
|
}
|
1880
|
-
|
1953
|
+
const text = translations.getPluralRuleText("total", diff, "");
|
1881
1954
|
const selectedText = new Formatter({
|
1882
1955
|
count: String(diff),
|
1883
1956
|
}).format(text);
|
@@ -2252,6 +2325,7 @@ function handleFilterKeyEvents() {
|
|
2252
2325
|
if (getFilterMode.call(this) !== FILTER_MODE_REMOTE) {
|
2253
2326
|
filterOptions.call(this);
|
2254
2327
|
} else {
|
2328
|
+
this[currentPageSymbol] = 1;
|
2255
2329
|
this[cleanupOptionsListSymbol] = true;
|
2256
2330
|
|
2257
2331
|
filterFromRemote.call(this).catch((e) => {
|
@@ -2291,7 +2365,6 @@ function filterFromRemote() {
|
|
2291
2365
|
filterValue = this[inlineFilterElementSymbol].value.toLowerCase();
|
2292
2366
|
}
|
2293
2367
|
showFlag = true;
|
2294
|
-
|
2295
2368
|
break;
|
2296
2369
|
case FILTER_POSITION_POPPER:
|
2297
2370
|
default:
|
@@ -2300,24 +2373,44 @@ function filterFromRemote() {
|
|
2300
2373
|
}
|
2301
2374
|
}
|
2302
2375
|
|
2303
|
-
|
2376
|
+
const params = {
|
2377
|
+
filter: filterValue,
|
2378
|
+
page: this[currentPageSymbol] || 1,
|
2379
|
+
};
|
2380
|
+
|
2381
|
+
return filterFromRemoteByValue.call(this, url, params, showFlag);
|
2304
2382
|
}
|
2305
2383
|
|
2306
2384
|
/**
|
2307
2385
|
* @private
|
2308
2386
|
* @param url
|
2309
|
-
* @param
|
2387
|
+
* @param {object} params
|
2310
2388
|
* @returns {string}
|
2311
2389
|
*/
|
2312
|
-
function formatURL(url,
|
2313
|
-
|
2314
|
-
|
2315
|
-
|
2316
|
-
|
2390
|
+
function formatURL(url, params = {}) {
|
2391
|
+
// Die Logik für den Default-Filterwert bleibt erhalten
|
2392
|
+
if (
|
2393
|
+
params.filter === undefined ||
|
2394
|
+
params.filter === null ||
|
2395
|
+
params.filter === ""
|
2396
|
+
) {
|
2397
|
+
const defaultValue = this.getOption("filter.defaultValue");
|
2398
|
+
if (defaultValue === undefined || defaultValue === null) {
|
2399
|
+
params.filter = disabledRequestMarker.toString();
|
2400
|
+
} else {
|
2401
|
+
params.filter = defaultValue;
|
2402
|
+
}
|
2403
|
+
}
|
2404
|
+
|
2405
|
+
const encodedParams = {};
|
2406
|
+
for (const key in params) {
|
2407
|
+
if (Object.hasOwn(params, key)) {
|
2408
|
+
const value = params[key];
|
2409
|
+
encodedParams[key] = encodeURI(String(value));
|
2317
2410
|
}
|
2318
2411
|
}
|
2319
2412
|
|
2320
|
-
const formatter = new Formatter(
|
2413
|
+
const formatter = new Formatter(encodedParams);
|
2321
2414
|
const openMarker = this.getOption("filter.marker.open");
|
2322
2415
|
let closeMarker = this.getOption("filter.marker.close");
|
2323
2416
|
if (!closeMarker) {
|
@@ -2339,14 +2432,15 @@ function formatURL(url, value) {
|
|
2339
2432
|
* @param {boolean} [openPopper] Flag indicating whether to open the popper.
|
2340
2433
|
* @return {string} The formatted URL with the applied filters and markers.
|
2341
2434
|
*/
|
2342
|
-
function filterFromRemoteByValue(optionUrl,
|
2435
|
+
function filterFromRemoteByValue(optionUrl, params, openPopper) {
|
2343
2436
|
return new Processing(() => {
|
2344
|
-
let url = formatURL.call(this, optionUrl,
|
2437
|
+
let url = formatURL.call(this, optionUrl, params);
|
2438
|
+
|
2345
2439
|
if (url.indexOf(disabledRequestMarker.toString()) !== -1) {
|
2346
|
-
return;
|
2440
|
+
return Promise.resolve();
|
2347
2441
|
}
|
2348
2442
|
|
2349
|
-
fetchIt
|
2443
|
+
return fetchIt
|
2350
2444
|
.call(this, url, {
|
2351
2445
|
disableHiding: true,
|
2352
2446
|
})
|
@@ -2359,12 +2453,9 @@ function filterFromRemoteByValue(optionUrl, value, openPopper) {
|
|
2359
2453
|
.catch((e) => {
|
2360
2454
|
addErrorAttribute(this, e);
|
2361
2455
|
setStatusOrRemoveBadges.call(this, "error");
|
2456
|
+
throw e;
|
2362
2457
|
});
|
2363
|
-
})
|
2364
|
-
.run()
|
2365
|
-
.catch((e) => {
|
2366
|
-
throw e;
|
2367
|
-
});
|
2458
|
+
}).run();
|
2368
2459
|
}
|
2369
2460
|
|
2370
2461
|
/**
|
@@ -2646,7 +2737,21 @@ function areOptionsAvailableAndInitInternal() {
|
|
2646
2737
|
|
2647
2738
|
this[areOptionsAvailableAndInitSymbol]++;
|
2648
2739
|
|
2649
|
-
|
2740
|
+
let options = this.getOption("options");
|
2741
|
+
|
2742
|
+
if (isArray(options) && Array.length === 1 && isString(options?.[0])) {
|
2743
|
+
try {
|
2744
|
+
const obj = JSON.parse(options[0]);
|
2745
|
+
if (isArray(obj)) {
|
2746
|
+
this.setOption("options", obj);
|
2747
|
+
options = obj;
|
2748
|
+
}
|
2749
|
+
} catch (e) {
|
2750
|
+
addErrorAttribute(this, e);
|
2751
|
+
setStatusOrRemoveBadges.call(this, "error");
|
2752
|
+
return false;
|
2753
|
+
}
|
2754
|
+
}
|
2650
2755
|
|
2651
2756
|
if (
|
2652
2757
|
options === undefined ||
|
@@ -2688,7 +2793,6 @@ function areOptionsAvailableAndInitInternal() {
|
|
2688
2793
|
}
|
2689
2794
|
}, 1000);
|
2690
2795
|
|
2691
|
-
//this.setOption("messages.control", msg);
|
2692
2796
|
this.setOption("messages.summary", "");
|
2693
2797
|
|
2694
2798
|
if (this.getOption("features.emptyValueIfNoOptions") === true) {
|
@@ -2717,6 +2821,11 @@ function areOptionsAvailableAndInitInternal() {
|
|
2717
2821
|
let updated = false;
|
2718
2822
|
let valueCounter = 1;
|
2719
2823
|
for (const option of options) {
|
2824
|
+
if (isObject(option) === false) {
|
2825
|
+
console.error("option is not an object", option);
|
2826
|
+
continue;
|
2827
|
+
}
|
2828
|
+
|
2720
2829
|
if (option?.visibility === undefined) {
|
2721
2830
|
option.visibility = "visible";
|
2722
2831
|
updated = true;
|
@@ -3058,6 +3167,8 @@ function hide() {
|
|
3058
3167
|
* @private
|
3059
3168
|
*/
|
3060
3169
|
function show() {
|
3170
|
+
const self = this;
|
3171
|
+
|
3061
3172
|
if (this.getOption("disabled", undefined) === true) {
|
3062
3173
|
return;
|
3063
3174
|
}
|
@@ -3117,6 +3228,16 @@ function show() {
|
|
3117
3228
|
addAttributeToken(this[controlElementSymbol], "class", "open");
|
3118
3229
|
|
3119
3230
|
new Processing(() => {
|
3231
|
+
if (!self?.[remoteFilterFirstOpendSymbol]) {
|
3232
|
+
self[remoteFilterFirstOpendSymbol] = true;
|
3233
|
+
setTimeout(
|
3234
|
+
() =>
|
3235
|
+
filterFromRemote.call(self).catch((e) => {
|
3236
|
+
addErrorAttribute(self, e);
|
3237
|
+
}),
|
3238
|
+
0,
|
3239
|
+
);
|
3240
|
+
}
|
3120
3241
|
calcAndSetOptionsDimension.call(this);
|
3121
3242
|
focusFilter.call(this);
|
3122
3243
|
this[popperElementSymbol].style.removeProperty("visibility");
|
@@ -3150,6 +3271,9 @@ function initDefaultOptionsFromUrl() {
|
|
3150
3271
|
});
|
3151
3272
|
}
|
3152
3273
|
|
3274
|
+
/**
|
3275
|
+
* @private
|
3276
|
+
*/
|
3153
3277
|
/**
|
3154
3278
|
* @private
|
3155
3279
|
*/
|
@@ -3159,9 +3283,9 @@ function initTotal() {
|
|
3159
3283
|
}
|
3160
3284
|
|
3161
3285
|
const url = this.getOption("remoteInfo.url");
|
3162
|
-
const
|
3286
|
+
const mappingTotal = this.getOption("mapping.total");
|
3163
3287
|
|
3164
|
-
if (!isString(
|
3288
|
+
if (!isString(mappingTotal) || !isString(url)) {
|
3165
3289
|
return;
|
3166
3290
|
}
|
3167
3291
|
|
@@ -3171,28 +3295,20 @@ function initTotal() {
|
|
3171
3295
|
.fetch(url, fetchOptions)
|
3172
3296
|
.then((response) => {
|
3173
3297
|
if (!response.ok) {
|
3174
|
-
// Improved status checking using `response.ok`
|
3175
3298
|
addErrorAttribute(
|
3176
3299
|
this,
|
3177
3300
|
`HTTP error status: ${response.status} - ${response.statusText}`,
|
3178
3301
|
);
|
3179
3302
|
return;
|
3180
3303
|
}
|
3181
|
-
|
3182
3304
|
return response.text();
|
3183
3305
|
})
|
3184
3306
|
.then((text) => {
|
3307
|
+
if (!text) return;
|
3185
3308
|
try {
|
3186
3309
|
const data = JSON.parse(String(text));
|
3187
|
-
const pathfinder = new Pathfinder(data);
|
3188
|
-
const total = pathfinder.getVia(mapping);
|
3189
3310
|
|
3190
|
-
|
3191
|
-
addErrorAttribute(this, "total is not an integer");
|
3192
|
-
return;
|
3193
|
-
}
|
3194
|
-
|
3195
|
-
this.setOption("total", total);
|
3311
|
+
processAndApplyPaginationData.call(this, data);
|
3196
3312
|
} catch (e) {
|
3197
3313
|
addErrorAttribute(this, e);
|
3198
3314
|
}
|
@@ -3202,6 +3318,16 @@ function initTotal() {
|
|
3202
3318
|
});
|
3203
3319
|
}
|
3204
3320
|
|
3321
|
+
function updatePagination(total, currentPage, objectsPerPage) {
|
3322
|
+
const totalPages = Math.ceil(total / objectsPerPage);
|
3323
|
+
this[paginationElementSymbol].style.display = "block";
|
3324
|
+
this[paginationElementSymbol].setOption("objectsPerPage", objectsPerPage);
|
3325
|
+
this[paginationElementSymbol].setPaginationState({
|
3326
|
+
totalPages: totalPages,
|
3327
|
+
currentPage: currentPage,
|
3328
|
+
});
|
3329
|
+
}
|
3330
|
+
|
3205
3331
|
/**
|
3206
3332
|
* @private
|
3207
3333
|
*/
|
@@ -3441,6 +3567,16 @@ function initEventHandler() {
|
|
3441
3567
|
subtree: true,
|
3442
3568
|
});
|
3443
3569
|
|
3570
|
+
if (this[paginationElementSymbol]) {
|
3571
|
+
this[paginationElementSymbol].style.display = "none";
|
3572
|
+
this[paginationElementSymbol].setOption("callbacks.click", (page) => {
|
3573
|
+
self[currentPageSymbol] = page;
|
3574
|
+
self[cleanupOptionsListSymbol] = true;
|
3575
|
+
filterFromRemote.call(self).catch((e) => {
|
3576
|
+
addErrorAttribute(self, e);
|
3577
|
+
});
|
3578
|
+
});
|
3579
|
+
}
|
3444
3580
|
return self;
|
3445
3581
|
}
|
3446
3582
|
|
@@ -3524,6 +3660,10 @@ function initControlReferences() {
|
|
3524
3660
|
throw new Error("no shadow-root is defined");
|
3525
3661
|
}
|
3526
3662
|
|
3663
|
+
this[paginationElementSymbol] = this.shadowRoot.querySelector(
|
3664
|
+
`[${ATTRIBUTE_ROLE}=pagination]`,
|
3665
|
+
);
|
3666
|
+
|
3527
3667
|
this[controlElementSymbol] = this.shadowRoot.querySelector(
|
3528
3668
|
`[${ATTRIBUTE_ROLE}=control]`,
|
3529
3669
|
);
|
@@ -3648,10 +3788,12 @@ function getTemplate() {
|
|
3648
3788
|
autocomplete="off"
|
3649
3789
|
tabindex="0">
|
3650
3790
|
</div>
|
3791
|
+
|
3651
3792
|
<div part="content" class="flex" data-monster-replace="path:content">
|
3652
3793
|
<div part="options" data-monster-role="options" data-monster-insert="options path:options"
|
3653
3794
|
tabindex="-1"></div>
|
3654
3795
|
</div>
|
3796
|
+
<monster-pagination data-monster-role="pagination"></monster-pagination>
|
3655
3797
|
<div part="remote-info"
|
3656
3798
|
data-monster-role="remote-info"
|
3657
3799
|
data-monster-attributes="class path:classes.remoteInfo"
|