@schukai/monster 4.48.0 → 4.48.2

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 CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
 
4
4
 
5
+ ## [4.48.2] - 2025-12-02
6
+
7
+ ### Bug Fixes
8
+
9
+ - **select:** own lookup cache
10
+
11
+
12
+
13
+ ## [4.48.1] - 2025-11-28
14
+
15
+ ### Bug Fixes
16
+
17
+ - **components:** Prevent duplicate entries in select component [#345](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/345)
18
+
19
+
20
+
5
21
  ## [4.48.0] - 2025-11-28
6
22
 
7
23
  ### Add Features
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.48.0"}
1
+ {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.48.2"}
@@ -272,6 +272,18 @@ const currentPageSymbol = Symbol("currentPage");
272
272
  */
273
273
  const remoteFilterFirstOpendSymbol = Symbol("remoteFilterFirstOpend");
274
274
 
275
+ /**
276
+ * @private
277
+ * @type {symbol}
278
+ */
279
+ const lookupCacheSymbol = Symbol("lookupCache");
280
+
281
+ /**
282
+ * @private
283
+ * @type {symbol}
284
+ */
285
+ const lookupInProgressSymbol = Symbol("lookupInProgress");
286
+
275
287
  /**
276
288
  * @private
277
289
  * @type {number}
@@ -346,6 +358,8 @@ class Select extends CustomControl {
346
358
  constructor() {
347
359
  super();
348
360
  this[currentPageSymbol] = 1;
361
+ this[lookupCacheSymbol] = new Map();
362
+ this[lookupInProgressSymbol] = new Map();
349
363
  initOptionObserver.call(this);
350
364
  }
351
365
 
@@ -613,6 +627,10 @@ class Select extends CustomControl {
613
627
  try {
614
628
  hide.call(this);
615
629
 
630
+ // Clear the lookup cache
631
+ this[lookupCacheSymbol].clear();
632
+ this[lookupInProgressSymbol].clear();
633
+
616
634
  setSelection
617
635
  .call(this, null)
618
636
  .then(() => {
@@ -1047,21 +1065,25 @@ function importOptionsIntern(data) {
1047
1065
  }
1048
1066
 
1049
1067
  for (const [value, label] of entries) {
1068
+ let found = false;
1050
1069
  for (const option of options) {
1051
1070
  if (option.value === value) {
1052
1071
  option.label = label;
1053
1072
  option.visibility = visibility;
1054
1073
  option.data = map.get(value);
1055
- continue;
1074
+ found = true;
1075
+ break;
1056
1076
  }
1057
1077
  }
1058
1078
 
1059
- options.push({
1060
- value,
1061
- label,
1062
- visibility,
1063
- data: map.get(value),
1064
- });
1079
+ if (!found) {
1080
+ options.push({
1081
+ value,
1082
+ label,
1083
+ visibility,
1084
+ data: map.get(value),
1085
+ });
1086
+ }
1065
1087
  }
1066
1088
 
1067
1089
  this.setOption("options", options);
@@ -1841,6 +1863,11 @@ function parseSlotsToOptions() {
1841
1863
  * @return {*}
1842
1864
  */
1843
1865
  function buildSelectionLabel(value) {
1866
+ // First, check the lookup cache.
1867
+ if (this[lookupCacheSymbol].has(value)) {
1868
+ return this[lookupCacheSymbol].get(value);
1869
+ }
1870
+
1844
1871
  const options = this.getOption("options");
1845
1872
 
1846
1873
  for (let i = 0; i < options.length; i++) {
@@ -1875,6 +1902,62 @@ function buildSelectionLabel(value) {
1875
1902
  return undefined;
1876
1903
  }
1877
1904
 
1905
+ /**
1906
+ * @private
1907
+ * @param {string} value The value to look up.
1908
+ * @returns {Promise<void>}
1909
+ */
1910
+ async function lookupValueAndCache(value) {
1911
+ const lookupUrl = this.getOption("lookup.url");
1912
+ if (!lookupUrl || !value) {
1913
+ return;
1914
+ }
1915
+
1916
+ // Avoid duplicate lookups for the same value.
1917
+ if (this[lookupInProgressSymbol].has(value)) {
1918
+ return;
1919
+ }
1920
+
1921
+ try {
1922
+ this[lookupInProgressSymbol].set(value, true);
1923
+
1924
+ const markerOpen = this.getOption("filter.marker.open", "{");
1925
+ const markerClose = this.getOption("filter.marker.close", "}");
1926
+ const url = lookupUrl.replace(`${markerOpen}filter${markerClose}`, value);
1927
+
1928
+ const data = await fetchData.call(this, url);
1929
+
1930
+ const mappingOptions = this.getOption("mapping", {});
1931
+ const map = buildMap(
1932
+ data,
1933
+ mappingOptions.selector,
1934
+ mappingOptions.labelTemplate,
1935
+ mappingOptions.valueTemplate,
1936
+ mappingOptions.filter,
1937
+ );
1938
+
1939
+ let found = false;
1940
+ for (const [itemValue, itemLabel] of map.entries()) {
1941
+ // The lookup might return more than the requested value, so we cache all of them.
1942
+ if (!this[lookupCacheSymbol].has(itemValue)) {
1943
+ this[lookupCacheSymbol].set(itemValue, itemLabel);
1944
+ if (`${itemValue}` === `${value}`) {
1945
+ found = true;
1946
+ }
1947
+ }
1948
+ }
1949
+
1950
+ // If the specific value was found, trigger a re-render of the selection.
1951
+ if (found) {
1952
+ await setSelection.call(this, this.getOption("selection"));
1953
+ }
1954
+ } catch (e) {
1955
+ addErrorAttribute(this, e);
1956
+ } finally {
1957
+ this[lookupInProgressSymbol].delete(value);
1958
+ }
1959
+ }
1960
+
1878
1961
  /**
1879
1962
  * @private
1880
1963
  * @param {*} value
@@ -1888,6 +1971,13 @@ function getSelectionLabel(value) {
1888
1971
  if (isString(label)) return label;
1889
1972
  }
1890
1973
 
1974
+ // If the label was not found, and we have a lookup URL, trigger a lookup.
1975
+ if (this.getOption("lookup.url")) {
1976
+ lookupValueAndCache.call(this, value).catch((e) => {
1977
+ addErrorAttribute(this, e);
1978
+ });
1979
+ }
1980
+
1891
1981
  if (isString(value) || isInteger(value)) {
1892
1982
  return `${value}`;
1893
1983
  }