@schukai/monster 4.1.2 → 4.3.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,26 @@
|
|
2
2
|
|
3
3
|
|
4
4
|
|
5
|
+
## [4.3.0] - 2025-05-12
|
6
|
+
|
7
|
+
### Add Features
|
8
|
+
|
9
|
+
- Add support for array-list filter serialization in Datatable
|
10
|
+
### Changes
|
11
|
+
|
12
|
+
- Enhance documentation for login component properties
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
## [4.2.0] - 2025-05-12
|
17
|
+
|
18
|
+
### Add Features
|
19
|
+
|
20
|
+
- Update Node binary paths and introduce username callback customization
|
21
|
+
- Update event naming and clean up spacing for clarity
|
22
|
+
|
23
|
+
|
24
|
+
|
5
25
|
## [4.1.2] - 2025-05-11
|
6
26
|
|
7
27
|
### Bug Fixes
|
package/package.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.0","@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.0","@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.3.0"}
|
@@ -75,7 +75,7 @@ const emptyHistoryStateElementSymbol = Symbol("emptyHistoryStateElement");
|
|
75
75
|
* @since 3.111.0
|
76
76
|
* @copyright schukai GmbH
|
77
77
|
* @summary A simple but powerful camera capture component. It can be used to capture images from the camera.
|
78
|
-
* @fires monster-camera-capture-
|
78
|
+
* @fires monster-camera-capture-captured
|
79
79
|
*/
|
80
80
|
class CameraCapture extends CustomElement {
|
81
81
|
/**
|
@@ -373,17 +373,17 @@ function getTranslations() {
|
|
373
373
|
/**
|
374
374
|
* @private
|
375
375
|
* @return {initEventHandler}
|
376
|
-
* @fires monster-camera-capture-
|
376
|
+
* @fires monster-camera-capture-captured
|
377
377
|
*/
|
378
378
|
function initEventHandler() {
|
379
379
|
const self = this;
|
380
380
|
|
381
381
|
this[takePictureButtonElementSymbol].setOption("actions.click", function () {
|
382
|
-
fireCustomEvent(self, "monster-camera-capture-take-picture", {
|
383
|
-
element: self,
|
384
|
-
});
|
385
|
-
|
386
382
|
self.capture();
|
383
|
+
|
384
|
+
fireCustomEvent(self, "monster-camera-capture-captured", {
|
385
|
+
element: self,
|
386
|
+
})
|
387
387
|
});
|
388
388
|
|
389
389
|
return this;
|
@@ -13,18 +13,18 @@
|
|
13
13
|
*/
|
14
14
|
|
15
15
|
import {
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
assembleMethodSymbol,
|
17
|
+
CustomElement,
|
18
|
+
registerCustomElement,
|
19
19
|
} from "../../dom/customelement.mjs";
|
20
20
|
import "../notify/notify.mjs";
|
21
|
-
import {
|
22
|
-
import {
|
23
|
-
import {
|
24
|
-
import {
|
25
|
-
import {
|
21
|
+
import {ViewerStyleSheet} from "./stylesheet/viewer.mjs";
|
22
|
+
import {instanceSymbol} from "../../constants.mjs";
|
23
|
+
import {isString} from "../../types/is.mjs";
|
24
|
+
import {getGlobal} from "../../types/global.mjs";
|
25
|
+
import {MediaType, parseMediaType} from "../../types/mediatype.mjs";
|
26
26
|
|
27
|
-
export {
|
27
|
+
export {Viewer};
|
28
28
|
|
29
29
|
/**
|
30
30
|
* @private
|
@@ -45,278 +45,286 @@ const viewerElementSymbol = Symbol("viewerElement");
|
|
45
45
|
* @summary A simple viewer component for PDF, HTML and images.
|
46
46
|
*/
|
47
47
|
class Viewer extends CustomElement {
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
48
|
+
/**
|
49
|
+
* This method is called by the `instanceof` operator.
|
50
|
+
* @return {symbol}
|
51
|
+
*/
|
52
|
+
static get [instanceSymbol]() {
|
53
|
+
return Symbol.for("@schukai/monster/components/content/viewer@@instance");
|
54
|
+
}
|
55
|
+
|
56
|
+
/**
|
57
|
+
* To set the options via the HTML tag, the attribute `data-monster-options` must be used.
|
58
|
+
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
|
59
|
+
*
|
60
|
+
* The individual configuration values can be found in the table.
|
61
|
+
*
|
62
|
+
* @property {Object} templates Template definitions
|
63
|
+
* @property {string} templates.main Main template
|
64
|
+
* @property {string} content Content to be displayed in the viewer
|
65
|
+
* @property {Object} classes Css classes
|
66
|
+
* @property {string} classes.viewer Css class for the viewer
|
67
|
+
*/
|
68
|
+
get defaults() {
|
69
|
+
return Object.assign({}, super.defaults, {
|
70
|
+
templates: {
|
71
|
+
main: getTemplate(),
|
72
|
+
},
|
73
|
+
content: "<slot></slot>",
|
74
|
+
classes: {
|
75
|
+
viewer: "",
|
76
|
+
},
|
77
|
+
});
|
78
|
+
}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Sets the content of an element based on the provided content and media type.
|
82
|
+
*
|
83
|
+
* @param {string} content - The content to be set.
|
84
|
+
* @param {string} [mediaType="text/plain"] - The media type of the content. Defaults to "text/plain" if not specified.
|
85
|
+
* @return {void} This method does not return a value.
|
86
|
+
* @throws {Error} Throws an error if shadowRoot is not defined.
|
87
|
+
*/
|
88
|
+
setContent(content, mediaType = "text/plain") {
|
89
|
+
if (!this.shadowRoot) {
|
90
|
+
throw new Error("no shadow-root is defined");
|
91
|
+
}
|
92
|
+
|
93
|
+
let type;
|
94
|
+
try {
|
95
|
+
const m = new parseMediaType(mediaType);
|
96
|
+
switch (m.type) {
|
97
|
+
case "image":
|
98
|
+
return this.setImage(content);
|
99
|
+
}
|
100
|
+
|
101
|
+
mediaType = m.toString();
|
102
|
+
} catch (error) {
|
103
|
+
type = null;
|
104
|
+
}
|
105
|
+
|
106
|
+
if (mediaType === undefined || mediaType === null || mediaType === "") {
|
107
|
+
mediaType = "text/plain";
|
108
|
+
}
|
109
|
+
|
110
|
+
switch (mediaType) {
|
111
|
+
case "text/html":
|
112
|
+
this.setHTML(content);
|
113
|
+
break;
|
114
|
+
case "text/plain":
|
115
|
+
this.setPlainText(content);
|
116
|
+
break;
|
117
|
+
case "application/pdf":
|
118
|
+
this.setPDF(content);
|
119
|
+
break;
|
120
|
+
case "image/png":
|
121
|
+
case "image/jpeg":
|
122
|
+
case "image/gif":
|
123
|
+
this.setImage(content);
|
124
|
+
break;
|
125
|
+
default:
|
126
|
+
this.setOption("content", content);
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Configures and embeds a PDF document into the application with customizable display settings.
|
132
|
+
*
|
133
|
+
* @param {Blob|URL|string} data The PDF data to be embedded. Can be provided as a Blob, URL or base64 string.
|
134
|
+
* @param {boolean} [navigation=true] Determines whether the navigation pane is displayed in the PDF viewer.
|
135
|
+
* @param {boolean} [toolbar=true] Controls the visibility of the toolbar in the PDF viewer.
|
136
|
+
* @param {boolean} [scrollbar=false] Configures the display of the scrollbar in the PDF viewer.
|
137
|
+
* @return {void} This method returns nothing but sets the embedded PDF as the content.
|
138
|
+
*/
|
139
|
+
setPDF(data, navigation = true, toolbar = true, scrollbar = false) {
|
140
|
+
const hashes =
|
141
|
+
"#toolbar=" +
|
142
|
+
(toolbar ? "1" : "0") +
|
143
|
+
"&navpanes=" +
|
144
|
+
(navigation ? "1" : "0") +
|
145
|
+
"&scrollbar=" +
|
146
|
+
(scrollbar ? "1" : "0");
|
147
|
+
|
148
|
+
let pdfURL = "";
|
149
|
+
if (isBlob(data)) {
|
150
|
+
pdfURL = URL.createObjectURL(data);
|
151
|
+
pdfURL += hashes;
|
152
|
+
} else if (isURL(data)) {
|
153
|
+
// check if the url already contains the hashes
|
154
|
+
if (data?.hash?.indexOf("#") === -1) {
|
155
|
+
pdfURL = data.toString() + hashes;
|
156
|
+
} else {
|
157
|
+
pdfURL = data.toString();
|
158
|
+
}
|
159
|
+
} else if (isString(data)) {
|
160
|
+
//URL.createObjectURL(data);
|
161
|
+
const blobObj = new Blob([atob(data)], {type: "application/pdf"});
|
162
|
+
const url = window.URL.createObjectURL(blobObj);
|
163
|
+
|
164
|
+
pdfURL = data;
|
165
|
+
} else {
|
166
|
+
throw new Error("Blob or URL expected");
|
167
|
+
}
|
168
|
+
|
169
|
+
const html =
|
170
|
+
'<object part="pdf" data="' +
|
171
|
+
pdfURL +
|
172
|
+
'" width="100%" height="100%" type="application/pdf"></object>';
|
173
|
+
|
174
|
+
this.setOption("content", html);
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* Sets an image for the target by accepting a blob, URL, or string representation of the image.
|
179
|
+
*
|
180
|
+
* @param {(Blob|string)} data - The image data, which can be a Blob, a valid URL, or a string representation of the image.
|
181
|
+
* @return {void} Does not return a value.
|
182
|
+
*/
|
183
|
+
setImage(data) {
|
184
|
+
if (isBlob(data)) {
|
185
|
+
data = URL.createObjectURL(data);
|
186
|
+
} else if (isURL(data)) {
|
187
|
+
// nothing to do
|
188
|
+
} else if (isString(data)) {
|
189
|
+
// nothing to do
|
190
|
+
} else {
|
191
|
+
throw new Error("Blob or URL expected");
|
192
|
+
}
|
193
|
+
|
194
|
+
this.setOption("content", '<img src="' + data + '" alt="image" />');
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
*
|
199
|
+
* if the data is a string, it is interpreted as HTML.
|
200
|
+
* if the data is an url, the HTML is loaded from the url and set as content.
|
201
|
+
* if the data is an HTMLElement, the outerHTML is used as content.
|
202
|
+
*
|
203
|
+
* @param {HTMLElement|URL|string|Blob} data
|
204
|
+
*/
|
205
|
+
setHTML(data) {
|
206
|
+
if (data instanceof Blob) {
|
207
|
+
blobToText(data)
|
208
|
+
.then((html) => {
|
209
|
+
this.setOption("content", html);
|
210
|
+
})
|
211
|
+
.catch((error) => {
|
212
|
+
throw new Error(error);
|
213
|
+
});
|
214
|
+
|
215
|
+
return;
|
216
|
+
} else if (data instanceof HTMLElement) {
|
217
|
+
data = data.outerHTML;
|
218
|
+
} else if (isString(data)) {
|
219
|
+
// nothing to do
|
220
|
+
} else if (isURL(data)) {
|
221
|
+
// fetch element
|
222
|
+
getGlobal()
|
223
|
+
.fetch(data)
|
224
|
+
.then((response) => {
|
225
|
+
return response.text();
|
226
|
+
})
|
227
|
+
.then((html) => {
|
228
|
+
this.setOption("content", html);
|
229
|
+
})
|
230
|
+
.catch((error) => {
|
231
|
+
throw new Error(error);
|
232
|
+
});
|
233
|
+
} else {
|
234
|
+
throw new Error("HTMLElement or string expected");
|
235
|
+
}
|
236
|
+
|
237
|
+
this.setOption("content", data);
|
238
|
+
}
|
239
|
+
|
240
|
+
/**
|
241
|
+
* Sets the plain text content by processing the input data, which can be of various types, including Blob,
|
242
|
+
* HTMLElement, string, or a valid URL. The method extracts and sets the raw text content into a predefined option.
|
243
|
+
*
|
244
|
+
* @param {Blob|HTMLElement|string} data - The input data to be processed. It can be a Blob object, an HTMLElement,
|
245
|
+
* a plain string, or a string formatted as a valid URL. The method determines
|
246
|
+
* the data type and processes it accordingly.
|
247
|
+
* @return {void} - This method does not return any value. It processes the content and updates the relevant option
|
248
|
+
* property.
|
249
|
+
*/
|
250
|
+
setPlainText(data) {
|
251
|
+
|
252
|
+
const mkPreSpan = (text) => {
|
253
|
+
const pre = document.createElement("pre");
|
254
|
+
pre.innerText = text;
|
255
|
+
pre.setAttribute("part", "text");
|
256
|
+
return pre.outerHTML;
|
257
|
+
}
|
258
|
+
|
259
|
+
if (data instanceof Blob) {
|
260
|
+
blobToText(data)
|
261
|
+
.then((text) => {
|
262
|
+
const div = document.createElement("div");
|
263
|
+
div.innerHTML = test;
|
264
|
+
text = div.innerText;
|
265
|
+
|
266
|
+
this.setOption("content", mkPreSpan(text));
|
267
|
+
})
|
268
|
+
.catch((error) => {
|
269
|
+
throw new Error(error);
|
270
|
+
});
|
271
|
+
|
272
|
+
return;
|
273
|
+
} else if (data instanceof HTMLElement) {
|
274
|
+
data = data.outerText;
|
275
|
+
} else if (isString(data)) {
|
276
|
+
const div = document.createElement("div");
|
277
|
+
div.innerHTML = data;
|
278
|
+
data = div.innerText;
|
279
|
+
} else if (isURL(data)) {
|
280
|
+
|
281
|
+
getGlobal()
|
282
|
+
.fetch(data)
|
283
|
+
.then((response) => {
|
284
|
+
return response.text();
|
285
|
+
})
|
286
|
+
.then((text) => {
|
287
|
+
const div = document.createElement("div");
|
288
|
+
div.innerHTML = text;
|
289
|
+
text = div.innerText;
|
290
|
+
|
291
|
+
this.setOption("content", mkPreSpan(text));
|
292
|
+
})
|
293
|
+
.catch((error) => {
|
294
|
+
throw new Error(error);
|
295
|
+
});
|
296
|
+
} else {
|
297
|
+
throw new Error("HTMLElement or string expected");
|
298
|
+
}
|
299
|
+
|
300
|
+
this.setOption("content", mkPreSpan(data));
|
301
|
+
}
|
302
|
+
|
303
|
+
/**
|
304
|
+
*
|
305
|
+
* @return {Viewer}
|
306
|
+
*/
|
307
|
+
[assembleMethodSymbol]() {
|
308
|
+
super[assembleMethodSymbol]();
|
309
|
+
|
310
|
+
initControlReferences.call(this);
|
311
|
+
initEventHandler.call(this);
|
312
|
+
}
|
313
|
+
|
314
|
+
/**
|
315
|
+
*
|
316
|
+
* @return {string}
|
317
|
+
*/
|
318
|
+
static getTag() {
|
319
|
+
return "monster-viewer";
|
320
|
+
}
|
321
|
+
|
322
|
+
/**
|
323
|
+
* @return {CSSStyleSheet[]}
|
324
|
+
*/
|
325
|
+
static getCSSStyleSheet() {
|
326
|
+
return [ViewerStyleSheet];
|
327
|
+
}
|
320
328
|
}
|
321
329
|
|
322
330
|
/**
|
@@ -325,12 +333,12 @@ class Viewer extends CustomElement {
|
|
325
333
|
* @return {boolean}
|
326
334
|
*/
|
327
335
|
function isURL(variable) {
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
336
|
+
try {
|
337
|
+
new URL(variable);
|
338
|
+
return true;
|
339
|
+
} catch (error) {
|
340
|
+
return false;
|
341
|
+
}
|
334
342
|
}
|
335
343
|
|
336
344
|
/**
|
@@ -339,7 +347,7 @@ function isURL(variable) {
|
|
339
347
|
* @return {boolean}
|
340
348
|
*/
|
341
349
|
function isBlob(variable) {
|
342
|
-
|
350
|
+
return variable instanceof Blob;
|
343
351
|
}
|
344
352
|
|
345
353
|
/**
|
@@ -348,12 +356,12 @@ function isBlob(variable) {
|
|
348
356
|
* @return {Promise<unknown>}
|
349
357
|
*/
|
350
358
|
function blobToText(blob) {
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
359
|
+
return new Promise((resolve, reject) => {
|
360
|
+
const reader = new FileReader();
|
361
|
+
reader.onloadend = () => resolve(reader.result);
|
362
|
+
reader.onerror = reject;
|
363
|
+
reader.readAsText(blob);
|
364
|
+
});
|
357
365
|
}
|
358
366
|
|
359
367
|
/**
|
@@ -362,18 +370,18 @@ function blobToText(blob) {
|
|
362
370
|
* @throws {Error} no shadow-root is defined
|
363
371
|
*/
|
364
372
|
function initControlReferences() {
|
365
|
-
|
366
|
-
|
367
|
-
|
373
|
+
if (!this.shadowRoot) {
|
374
|
+
throw new Error("no shadow-root is defined");
|
375
|
+
}
|
368
376
|
|
369
|
-
|
377
|
+
this[viewerElementSymbol] = this.shadowRoot.getElementById("viewer");
|
370
378
|
}
|
371
379
|
|
372
380
|
/**
|
373
381
|
* @private
|
374
382
|
*/
|
375
383
|
function initEventHandler() {
|
376
|
-
|
384
|
+
return this;
|
377
385
|
}
|
378
386
|
|
379
387
|
/**
|
@@ -381,8 +389,8 @@ function initEventHandler() {
|
|
381
389
|
* @return {string}
|
382
390
|
*/
|
383
391
|
function getTemplate() {
|
384
|
-
|
385
|
-
|
392
|
+
// language=HTML
|
393
|
+
return `
|
386
394
|
<div id="viewer" data-monster-role="viewer" part="viewer"
|
387
395
|
data-monster-replace="path:content"
|
388
396
|
data-monster-attributes="class path:classes.viewer">
|
@@ -1231,6 +1231,25 @@ function collectSearchQueries() {
|
|
1231
1231
|
.join(",")
|
1232
1232
|
);
|
1233
1233
|
},
|
1234
|
+
"array-list": (value, key) => {
|
1235
|
+
if (isString(value)) {
|
1236
|
+
value = value.split(",");
|
1237
|
+
}
|
1238
|
+
|
1239
|
+
if (!isArray(value)) {
|
1240
|
+
return "";
|
1241
|
+
}
|
1242
|
+
|
1243
|
+
return (
|
1244
|
+
key +
|
1245
|
+
"=" +
|
1246
|
+
value
|
1247
|
+
.map((v) => {
|
1248
|
+
return `"${encodeURIComponent(v)}"`;
|
1249
|
+
})
|
1250
|
+
.join(",")
|
1251
|
+
);
|
1252
|
+
},
|
1234
1253
|
"date-range": (value, key) => {
|
1235
1254
|
const query = parseDateInput(value, key);
|
1236
1255
|
if (!query || query === "false") {
|
@@ -166,56 +166,88 @@ class Login extends CustomElement {
|
|
166
166
|
* The individual configuration values can be found in the table.
|
167
167
|
*
|
168
168
|
* @property {Object} templates Template definitions
|
169
|
-
* @property {string} templates.main
|
170
|
-
* @property {Object} labels Label definitions
|
171
|
-
* @property {
|
172
|
-
* @property {string}
|
173
|
-
* @property {string}
|
174
|
-
* @property {string}
|
175
|
-
* @property {
|
176
|
-
* @property {
|
177
|
-
* @property {
|
178
|
-
* @property {
|
179
|
-
* @property {
|
180
|
-
* @property {
|
181
|
-
* @property {
|
182
|
-
* @property {string}
|
183
|
-
* @property {string}
|
184
|
-
* @property {
|
185
|
-
* @property {
|
186
|
-
* @property {
|
187
|
-
* @property {string}
|
188
|
-
* @property {string}
|
189
|
-
* @property {string}
|
190
|
-
* @property {
|
191
|
-
* @property {
|
192
|
-
* @property {string}
|
193
|
-
* @property {string}
|
194
|
-
* @property {string}
|
195
|
-
* @property {Object}
|
196
|
-
* @property {string}
|
197
|
-
* @property {string}
|
198
|
-
* @property {
|
199
|
-
* @property {string}
|
200
|
-
* @property {
|
201
|
-
* @property {
|
169
|
+
* @property {string} templates.main The main HTML template used for rendering the login form
|
170
|
+
* @property {Object} labels Label definitions used for localization and form messages
|
171
|
+
* @property {string} labels.username Label for the username or email field
|
172
|
+
* @property {string} labels.password Label for the password field
|
173
|
+
* @property {string} labels.login Label for the login button
|
174
|
+
* @property {string} labels.forgotPasswordLink Text for the "forgot password" link
|
175
|
+
* @property {string} labels.mailAddress Label for the email input in password reset flow
|
176
|
+
* @property {string} labels.requestLink Label for the button that sends a password reset code
|
177
|
+
* @property {string} labels.digits Label for the digits input field
|
178
|
+
* @property {string} labels.loginLink Label for the back-to-login link
|
179
|
+
* @property {string} labels.secondFactor Label for the second factor authentication input
|
180
|
+
* @property {string} labels.sendDigits Label for the button that submits the digits input
|
181
|
+
* @property {string} labels.sendSecondFactorDigits Label for the button that submits the second factor code
|
182
|
+
* @property {string} labels.resetLoginProcess Label for the link to return to the login form
|
183
|
+
* @property {string} labels.messageEmptyUserName Message shown when username is empty
|
184
|
+
* @property {string} labels.messageEmptyPassword Message shown when password is empty
|
185
|
+
* @property {string} labels.messageEmptyBoth Message shown when both username and password are empty
|
186
|
+
* @property {string} labels.messageEmptyEmail Message shown when email field is empty
|
187
|
+
* @property {string} labels.messageInvalidEmail Message shown when an invalid email address is entered
|
188
|
+
* @property {string} labels.digitsEmpty Message shown when digits field is empty
|
189
|
+
* @property {string} labels.digitsInvalid Message shown when digits input is invalid
|
190
|
+
* @property {string} labels.messageLoginFailed Message shown on failed login attempt
|
191
|
+
* @property {string} labels.messageForbidden Message shown on successful login with insufficient permissions
|
192
|
+
* @property {string} labels.messageSomethingWentWrong Fallback error message
|
193
|
+
* @property {string} labels.messageThisFormIsNotConfigured Message shown if no backend URL is configured
|
194
|
+
* @property {string} labels.messagePasswordResetDisabled Message shown if password reset is disabled due to 2FA
|
195
|
+
* @property {Object} classes Class definitions for visual styling
|
196
|
+
* @property {string} classes.usernameInvalid CSS class applied when username is invalid
|
197
|
+
* @property {string} classes.passwordInvalid CSS class applied when password is invalid
|
198
|
+
* @property {string} classes.emailInvalid CSS class applied when email input is invalid
|
199
|
+
* @property {string} classes.button CSS class applied to all form buttons
|
200
|
+
* @property {boolean} disabled If true, disables interaction with the component
|
201
|
+
* @property {Object} features Feature flags to toggle optional behavior
|
202
|
+
* @property {boolean} features.forgotPassword Enables the forgot password flow
|
203
|
+
* @property {boolean} features.redirectToFirstSuccessUrl If true, redirects to the first success URL after login
|
204
|
+
* @property {Object} actions Action definitions for custom event handling
|
205
|
+
* @property {Function} actions.click Callback function for generic click actions within the login component
|
206
|
+
* @property {Object} callbacks Optional callback hooks for modifying internal behavior
|
207
|
+
* @property {Function} callbacks.username A function that receives and can transform the entered username before submission
|
208
|
+
* @property {number} digits Number of digits required for second factor or password reset code input
|
209
|
+
* @property {Object[]} successUrls List of URLs shown after successful login (e.g., home or logout)
|
210
|
+
* @property {string} successUrls.label Label for the success URL (displayed)
|
211
|
+
* @property {string} successUrls.url Actual target URL
|
212
|
+
* @property {number} timeoutForMessage Duration in milliseconds to show error messages
|
213
|
+
* @property {number} timeoutForSuccess Duration in milliseconds before triggering the post-login success state
|
214
|
+
* @property {Object} accessKeys Keyboard access keys for accessibility and shortcuts
|
215
|
+
* @property {string} accessKeys.loginLink Access key for switching to login form
|
216
|
+
* @property {string} accessKeys.username Access key for focusing the username field
|
217
|
+
* @property {string} accessKeys.password Access key for focusing the password field
|
218
|
+
* @property {Object} fetch Definitions for backend requests to support login workflows
|
219
|
+
* @property {Object} fetch.login Fetch config for login request
|
220
|
+
* @property {string} fetch.login.url Endpoint to post login credentials to
|
221
|
+
* @property {string} fetch.login.method HTTP method for login (e.g., "POST")
|
222
|
+
* @property {string} fetch.login.mode Fetch mode (e.g., "same-origin")
|
223
|
+
* @property {Object} fetch.login.headers HTTP headers to be sent with the login request
|
224
|
+
* @property {string} fetch.login.headers.accept Accept header value
|
225
|
+
* @property {string} fetch.login.headers.Content-Type Content-Type header value
|
226
|
+
* @property {string} fetch.login.credentials Credential mode (e.g., "same-origin")
|
227
|
+
* @property {Object} fetch.forgotPassword Fetch config for password reset code request
|
228
|
+
* @property {string} fetch.forgotPassword.url Endpoint to request a reset link/code
|
229
|
+
* @property {string} fetch.forgotPassword.method HTTP method
|
230
|
+
* @property {string} fetch.forgotPassword.mode Fetch mode
|
202
231
|
* @property {Object} fetch.forgotPassword.headers Headers
|
203
|
-
* @property {string} fetch.forgotPassword.headers.accept Accept
|
204
|
-
* @property {string} fetch.forgotPassword.
|
205
|
-
* @property {
|
206
|
-
* @property {
|
207
|
-
* @property {string} fetch.digits.
|
208
|
-
* @property {string} fetch.digits.
|
232
|
+
* @property {string} fetch.forgotPassword.headers.accept Accept header
|
233
|
+
* @property {string} fetch.forgotPassword.headers.Content-Type Content-Type header
|
234
|
+
* @property {string} fetch.forgotPassword.credentials Credential mode
|
235
|
+
* @property {Object} fetch.digits Fetch config for submitting password reset code
|
236
|
+
* @property {string} fetch.digits.url Endpoint for validating the code
|
237
|
+
* @property {string} fetch.digits.method HTTP method
|
238
|
+
* @property {string} fetch.digits.mode Fetch mode
|
209
239
|
* @property {Object} fetch.digits.headers Headers
|
210
|
-
* @property {string} fetch.digits.headers.accept Accept
|
211
|
-
* @property {string} fetch.digits.
|
212
|
-
* @property {
|
213
|
-
* @property {
|
214
|
-
* @property {string} fetch.secondFactor.
|
215
|
-
* @property {string} fetch.secondFactor.
|
240
|
+
* @property {string} fetch.digits.headers.accept Accept header
|
241
|
+
* @property {string} fetch.digits.headers.Content-Type Content-Type header
|
242
|
+
* @property {string} fetch.digits.credentials Credential mode
|
243
|
+
* @property {Object} fetch.secondFactor Fetch config for submitting second factor code
|
244
|
+
* @property {string} fetch.secondFactor.url Endpoint to validate second factor code
|
245
|
+
* @property {string} fetch.secondFactor.method HTTP method
|
246
|
+
* @property {string} fetch.secondFactor.mode Fetch mode
|
216
247
|
* @property {Object} fetch.secondFactor.headers Headers
|
217
|
-
* @property {string} fetch.secondFactor.headers.accept Accept
|
218
|
-
* @property {string} fetch.secondFactor.
|
248
|
+
* @property {string} fetch.secondFactor.headers.accept Accept header
|
249
|
+
* @property {string} fetch.secondFactor.headers.Content-Type Content-Type header
|
250
|
+
* @property {string} fetch.secondFactor.credentials Credential mode
|
219
251
|
*/
|
220
252
|
get defaults() {
|
221
253
|
return Object.assign({}, super.defaults, {
|
@@ -236,6 +268,10 @@ class Login extends CustomElement {
|
|
236
268
|
},
|
237
269
|
actions: {},
|
238
270
|
|
271
|
+
callbacks : {
|
272
|
+
username : null,
|
273
|
+
},
|
274
|
+
|
239
275
|
digits: 6,
|
240
276
|
|
241
277
|
successUrls: [
|
@@ -1076,11 +1112,19 @@ function initEventHandler() {
|
|
1076
1112
|
});
|
1077
1113
|
|
1078
1114
|
this[loginButtonSymbol].setOption("actions.click", (event) => {
|
1079
|
-
|
1080
|
-
|
1115
|
+
|
1116
|
+
let username = this.shadowRoot.querySelector(
|
1081
1117
|
"input[name='username']",
|
1082
1118
|
).value;
|
1083
1119
|
|
1120
|
+
const userCallback = this.getOption("callbacks.username");
|
1121
|
+
if (isFunction(userCallback)) {
|
1122
|
+
const userCallbackResult = userCallback.call(this, username);
|
1123
|
+
if (userCallbackResult !== undefined) {
|
1124
|
+
username = userCallbackResult;
|
1125
|
+
}
|
1126
|
+
}
|
1127
|
+
|
1084
1128
|
const password = this.shadowRoot.querySelector("monster-password").value;
|
1085
1129
|
|
1086
1130
|
let missingBits = 0;
|