@schukai/monster 4.13.1 → 4.15.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
CHANGED
@@ -2,6 +2,22 @@
|
|
2
2
|
|
3
3
|
|
4
4
|
|
5
|
+
## [4.15.0] - 2025-06-05
|
6
|
+
|
7
|
+
### Add Features
|
8
|
+
|
9
|
+
- Update node and pnpx binaries; enhance datatable column bar and select components
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
## [4.14.0] - 2025-06-04
|
14
|
+
|
15
|
+
### Add Features
|
16
|
+
|
17
|
+
- Add i18n support in CustomElement template formatting
|
18
|
+
|
19
|
+
|
20
|
+
|
5
21
|
## [4.13.1] - 2025-06-03
|
6
22
|
|
7
23
|
### Bug Fixes
|
package/package.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.1","@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.
|
1
|
+
{"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.1","@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.15.0"}
|
@@ -23,6 +23,9 @@ import { clone } from "../../util/clone.mjs";
|
|
23
23
|
import { ColumnBarStyleSheet } from "./stylesheet/column-bar.mjs";
|
24
24
|
import { createPopper } from "@popperjs/core";
|
25
25
|
import { getLocaleOfDocument } from "../../dom/locale.mjs";
|
26
|
+
import {hasObjectLink} from "../../dom/attributes.mjs";
|
27
|
+
import {customElementUpdaterLinkSymbol} from "../../dom/constants.mjs";
|
28
|
+
import {getGlobalObject} from "../../types/global.mjs";
|
26
29
|
|
27
30
|
export { ColumnBar };
|
28
31
|
|
@@ -56,6 +59,12 @@ const dotsContainerElementSymbol = Symbol("dotsContainerElement");
|
|
56
59
|
*/
|
57
60
|
const popperInstanceSymbol = Symbol("popperInstance");
|
58
61
|
|
62
|
+
/**
|
63
|
+
* @private
|
64
|
+
* @type {symbol}
|
65
|
+
*/
|
66
|
+
const closeEventHandlerSymbol = Symbol("closeEventHandler");
|
67
|
+
|
59
68
|
/**
|
60
69
|
* A column bar for a datatable
|
61
70
|
*
|
@@ -72,7 +81,18 @@ class ColumnBar extends CustomElement {
|
|
72
81
|
* @return {symbol}
|
73
82
|
*/
|
74
83
|
static get [instanceSymbol]() {
|
75
|
-
return Symbol.for("@schukai/monster/components/column-bar");
|
84
|
+
return Symbol.for("@schukai/monster/components/column-bar@@instance");
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* This method is called to customize the component.
|
89
|
+
* @returns {Map<unknown, unknown>}
|
90
|
+
*/
|
91
|
+
get customization() {
|
92
|
+
return new Map([
|
93
|
+
...super.customization,
|
94
|
+
["templateFormatter.i18n", true],
|
95
|
+
]);
|
76
96
|
}
|
77
97
|
|
78
98
|
/**
|
@@ -83,24 +103,61 @@ class ColumnBar extends CustomElement {
|
|
83
103
|
*
|
84
104
|
* @property {Object} templates Template definitions
|
85
105
|
* @property {string} templates.main Main template
|
86
|
-
* @property {object}
|
87
|
-
* @property {
|
106
|
+
* @property {object} labels Locale definitions
|
107
|
+
* @property {string} locale.settings The text for the settings button
|
88
108
|
*/
|
89
109
|
get defaults() {
|
90
|
-
|
110
|
+
return Object.assign({}, super.defaults, {
|
91
111
|
templates: {
|
92
112
|
main: getTemplate(),
|
93
113
|
},
|
94
|
-
|
114
|
+
labels: getTranslations(),
|
95
115
|
|
96
116
|
columns: [],
|
97
117
|
});
|
118
|
+
}
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Called every time the element is added to the DOM. Useful for running initialization code.
|
122
|
+
* @return {void}
|
123
|
+
* @since 4.14.0
|
124
|
+
*/
|
125
|
+
connectedCallback() {
|
126
|
+
super.connectedCallback();
|
127
|
+
|
128
|
+
this[closeEventHandlerSymbol] = (event) => {
|
129
|
+
const path = event.composedPath();
|
130
|
+
const isOutsideElement = !path.includes(this);
|
131
|
+
const isOutsideShadow = !path.includes(this.shadowRoot);
|
132
|
+
|
133
|
+
if (isOutsideElement && isOutsideShadow && this[settingsLayerElementSymbol]) {
|
134
|
+
this[settingsLayerElementSymbol].classList.remove("visible");
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
getGlobalObject("document").addEventListener("click" , this[closeEventHandlerSymbol]);
|
139
|
+
getGlobalObject("document").addEventListener("touch" , this[closeEventHandlerSymbol]);
|
98
140
|
|
99
|
-
return obj;
|
100
141
|
}
|
101
142
|
|
102
143
|
/**
|
144
|
+
* Called every time the element is removed from the DOM. Useful for running clean up code.
|
103
145
|
*
|
146
|
+
* @return {void}
|
147
|
+
* @since 4.14.0
|
148
|
+
*/
|
149
|
+
disconnectedCallback() {
|
150
|
+
super.disconnectedCallback();
|
151
|
+
|
152
|
+
if(this[closeEventHandlerSymbol]) {
|
153
|
+
getGlobalObject("document").removeEventListener("click", this[closeEventHandlerSymbol]);
|
154
|
+
getGlobalObject("document").removeEventListener("touch", this[closeEventHandlerSymbol]);
|
155
|
+
this[closeEventHandlerSymbol] = null;
|
156
|
+
}
|
157
|
+
|
158
|
+
}
|
159
|
+
|
160
|
+
/**
|
104
161
|
* @return {string}
|
105
162
|
*/
|
106
163
|
static getTag() {
|
@@ -133,43 +190,47 @@ class ColumnBar extends CustomElement {
|
|
133
190
|
function getTranslations() {
|
134
191
|
const locale = getLocaleOfDocument();
|
135
192
|
switch (locale.language) {
|
136
|
-
case "de":
|
137
|
-
return {
|
138
|
-
|
139
|
-
};
|
140
|
-
case "
|
141
|
-
return {
|
142
|
-
|
143
|
-
};
|
144
|
-
case "
|
145
|
-
return {
|
146
|
-
|
147
|
-
};
|
148
|
-
case "
|
149
|
-
return {
|
150
|
-
|
151
|
-
};
|
152
|
-
case "
|
153
|
-
return {
|
154
|
-
|
155
|
-
};
|
156
|
-
case "
|
157
|
-
return {
|
158
|
-
|
159
|
-
};
|
160
|
-
case "
|
161
|
-
return {
|
162
|
-
|
163
|
-
};
|
164
|
-
case "
|
165
|
-
return {
|
166
|
-
|
167
|
-
};
|
168
|
-
|
193
|
+
case "de": // German
|
194
|
+
return { settings: "Einstellungen" };
|
195
|
+
case "fr": // French
|
196
|
+
return { settings: "Paramètres" };
|
197
|
+
case "es": // Spanish
|
198
|
+
return { settings: "Configuración" };
|
199
|
+
case "zh": // Mandarin (Chinese)
|
200
|
+
return { settings: "设置" };
|
201
|
+
case "hi": // Hindi
|
202
|
+
return { settings: "सेटिंग्स" };
|
203
|
+
case "bn": // Bengali
|
204
|
+
return { settings: "সেটিংস" };
|
205
|
+
case "pt": // Portuguese
|
206
|
+
return { settings: "Configurações" };
|
207
|
+
case "ru": // Russian
|
208
|
+
return { settings: "Настройки" };
|
209
|
+
case "ja": // Japanese
|
210
|
+
return { settings: "設定" };
|
211
|
+
case "pa": // Western Punjabi
|
212
|
+
return { settings: "ਸੈਟਿੰਗਾਂ" };
|
213
|
+
case "mr": // Marathi
|
214
|
+
return { settings: "सेटिंग्ज" };
|
215
|
+
case "it": // Italian
|
216
|
+
return { settings: "Impostazioni" };
|
217
|
+
case "nl": // Dutch
|
218
|
+
return { settings: "Instellingen" };
|
219
|
+
case "sv": // Swedish
|
220
|
+
return { settings: "Inställningar" };
|
221
|
+
case "pl": // Polish
|
222
|
+
return { settings: "Ustawienia" };
|
223
|
+
case "da": // Danish
|
224
|
+
return { settings: "Indstillinger" };
|
225
|
+
case "fi": // Finnish
|
226
|
+
return { settings: "Asetukset" };
|
227
|
+
case "no": // Norwegian
|
228
|
+
return { settings: "Innstillinger" };
|
229
|
+
case "cs": // Czech
|
230
|
+
return { settings: "Nastavení" };
|
231
|
+
default: // English fallback
|
169
232
|
case "en":
|
170
|
-
return {
|
171
|
-
settings: "Settings",
|
172
|
-
};
|
233
|
+
return { settings: "Settings" };
|
173
234
|
}
|
174
235
|
}
|
175
236
|
|
@@ -325,7 +386,7 @@ function getTemplate() {
|
|
325
386
|
<div data-monster-role="control" part="control" data-monster-select-this="true" data-monster-attributes="class path:columns | has-entries | ?::hidden">
|
326
387
|
<ul data-monster-insert="dots path:columns"
|
327
388
|
data-monster-role="dots"></ul>
|
328
|
-
<a href="#" data-monster-role="settings-button"
|
389
|
+
<a href="#" data-monster-role="settings-button">i18n{settings}</a>
|
329
390
|
<div data-monster-role="settings-layer">
|
330
391
|
<div data-monster-insert="column path:columns" data-monster-role="settings-popup-list">
|
331
392
|
</div>
|
@@ -390,6 +390,7 @@ class Select extends CustomControl {
|
|
390
390
|
* @property {"radio"|"checkbox"} type Selection mode: "radio" for single, "checkbox" for multiple.
|
391
391
|
* @property {string} name Name of the hidden form field for form submission.
|
392
392
|
* @property {string|null} url URL to dynamically fetch options via HTTP when opening or filtering.
|
393
|
+
* @property {Object} lookup Configuration for lookup requests.
|
393
394
|
* @property {string} lookup.url URL template with ${filter} placeholder to fetch only selected entries on init when `url` is set and either `features.lazyLoad` or `filter.mode==="remote"`.
|
394
395
|
* @property {boolean} lookup.grouping Group lookup requests: true to fetch all selected values in one request, false to fetch each individually.
|
395
396
|
* @property {string} fetch.redirect Fetch redirect mode (e.g. "error").
|
@@ -1354,51 +1355,64 @@ function getTranslations() {
|
|
1354
1355
|
function lookupSelection() {
|
1355
1356
|
const self = this;
|
1356
1357
|
|
1357
|
-
|
1358
|
-
const
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1358
|
+
const observer = new IntersectionObserver((entries, obs) => {
|
1359
|
+
for (const entry of entries) {
|
1360
|
+
if (entry.isIntersecting) {
|
1361
|
+
console.log("IntersectionObserver: entry.target is visible");
|
1362
|
+
obs.disconnect(); // Only observe once
|
1362
1363
|
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1364
|
+
setTimeout(() => {
|
1365
|
+
const selection = self.getOption("selection");
|
1366
|
+
if (selection.length === 0) {
|
1367
|
+
return;
|
1368
|
+
}
|
1366
1369
|
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
+
if (self[isLoadingSymbol] === true) {
|
1371
|
+
return;
|
1372
|
+
}
|
1370
1373
|
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
url = lookupUrl;
|
1375
|
-
}
|
1374
|
+
if (self[lazyLoadDoneSymbol] === true) {
|
1375
|
+
return;
|
1376
|
+
}
|
1376
1377
|
|
1377
|
-
|
1378
|
+
let url = self.getOption("url");
|
1379
|
+
const lookupUrl = self.getOption("lookup.url");
|
1380
|
+
if (lookupUrl !== null) {
|
1381
|
+
url = lookupUrl;
|
1382
|
+
}
|
1378
1383
|
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1384
|
+
self[cleanupOptionsListSymbol] = false;
|
1385
|
+
|
1386
|
+
if (self.getOption("lookup.grouping") === true) {
|
1387
|
+
filterFromRemoteByValue
|
1388
|
+
.call(
|
1389
|
+
self,
|
1390
|
+
url,
|
1391
|
+
selection.map((s) => s?.["value"]),
|
1392
|
+
)
|
1393
|
+
.catch((e) => {
|
1394
|
+
addErrorAttribute(self, e);
|
1395
|
+
});
|
1396
|
+
return;
|
1397
|
+
}
|
1391
1398
|
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1399
|
+
for (const s of selection) {
|
1400
|
+
if (s?.["value"]) {
|
1401
|
+
filterFromRemoteByValue.call(self, url, s["value"]).catch((e) => {
|
1402
|
+
addErrorAttribute(self, e);
|
1403
|
+
});
|
1404
|
+
}
|
1405
|
+
}
|
1406
|
+
}, 100);
|
1397
1407
|
}
|
1398
1408
|
}
|
1399
|
-
},
|
1409
|
+
}, { threshold: 0.1 });
|
1410
|
+
|
1411
|
+
// Beobachte das Element selbst (dieses Element muss im DOM sein)
|
1412
|
+
observer.observe(self);
|
1400
1413
|
}
|
1401
1414
|
|
1415
|
+
|
1402
1416
|
/**
|
1403
1417
|
*
|
1404
1418
|
* @param url
|
@@ -297,6 +297,7 @@ class CustomElement extends HTMLElement {
|
|
297
297
|
* @property {Object} templateFormatter.marker Specifies the marker for the templates.
|
298
298
|
* @property {Function} templateFormatter.marker.open=null Specifies the opening marker for the templates.
|
299
299
|
* @property {Function} templateFormatter.marker.close=null Specifies the closing marker for the templates.
|
300
|
+
* @property {Boolean} templateFormatter.i18n=false Specifies whether the templates should be formatted with i18n.
|
300
301
|
* @property {Boolean} eventProcessing=false Specifies whether the control processes events.
|
301
302
|
* @since 1.8.0
|
302
303
|
*/
|
@@ -314,6 +315,7 @@ class CustomElement extends HTMLElement {
|
|
314
315
|
open: null,
|
315
316
|
close: null,
|
316
317
|
},
|
318
|
+
i18n : false,
|
317
319
|
},
|
318
320
|
|
319
321
|
eventProcessing: false,
|
@@ -1075,6 +1077,27 @@ function parseOptionsJSON(data) {
|
|
1075
1077
|
return validateObject(obj);
|
1076
1078
|
}
|
1077
1079
|
|
1080
|
+
/**
|
1081
|
+
* @private
|
1082
|
+
* @param html
|
1083
|
+
* @returns {*|string}
|
1084
|
+
*/
|
1085
|
+
function substituteI18n(html) {
|
1086
|
+
|
1087
|
+
if(!this.getOption("templateFormatter.i18n", false)) {
|
1088
|
+
return html;
|
1089
|
+
}
|
1090
|
+
|
1091
|
+
const labels = this.getOption("labels", {});
|
1092
|
+
if (!(isObject(labels) || isIterable(labels))) {
|
1093
|
+
return html;
|
1094
|
+
}
|
1095
|
+
|
1096
|
+
const formatter = new Formatter(labels, {});
|
1097
|
+
formatter.setMarker("i18n{", '}')
|
1098
|
+
return formatter.format(html);
|
1099
|
+
}
|
1100
|
+
|
1078
1101
|
/**
|
1079
1102
|
* @private
|
1080
1103
|
* @return {initHtmlContent}
|
@@ -1090,7 +1113,7 @@ function initHtmlContent() {
|
|
1090
1113
|
if (isObject(mapping)) {
|
1091
1114
|
html = new Formatter(mapping, {}).format(html);
|
1092
1115
|
}
|
1093
|
-
this.innerHTML = html;
|
1116
|
+
this.innerHTML = substituteI18n.call(this, html);
|
1094
1117
|
}
|
1095
1118
|
}
|
1096
1119
|
|
@@ -1194,7 +1217,7 @@ function initShadowRoot() {
|
|
1194
1217
|
html = formatter.format(html);
|
1195
1218
|
}
|
1196
1219
|
|
1197
|
-
this.shadowRoot.innerHTML = html;
|
1220
|
+
this.shadowRoot.innerHTML = substituteI18n.call(this, html);
|
1198
1221
|
return this;
|
1199
1222
|
}
|
1200
1223
|
|
@@ -38,13 +38,12 @@ describe('Button', function () {
|
|
38
38
|
before(function (done) {
|
39
39
|
initJSDOM().then(() => {
|
40
40
|
|
41
|
-
import("element-internals-polyfill").
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
done()
|
46
|
-
}).catch(e => done(e))
|
47
|
-
|
41
|
+
import("element-internals-polyfill").then(()=>{
|
42
|
+
import("../../../../source/components/form/button.mjs").then((m) => {
|
43
|
+
Button = m['Button'];
|
44
|
+
done()
|
45
|
+
}).catch(e => done(e))
|
46
|
+
}).catch(e => done(e));
|
48
47
|
|
49
48
|
});
|
50
49
|
})
|
package/test/util/jsdom.mjs
CHANGED
@@ -67,7 +67,7 @@ function initJSDOM(options) {
|
|
67
67
|
'InputEvent',
|
68
68
|
'KeyboardEvent',
|
69
69
|
'MutationObserver',
|
70
|
-
|
70
|
+
// 'navigator',
|
71
71
|
'Node',
|
72
72
|
'NodeFilter',
|
73
73
|
'NodeList',
|
@@ -76,7 +76,9 @@ function initJSDOM(options) {
|
|
76
76
|
'XMLSerializer',
|
77
77
|
].forEach(key => {
|
78
78
|
try {
|
79
|
-
|
79
|
+
console.log("setting key", key);
|
80
|
+
|
81
|
+
g[key] = window[key]
|
80
82
|
} catch(e) {
|
81
83
|
console.error("Error setting key", key, e);
|
82
84
|
}
|