@schukai/monster 3.113.0 → 3.114.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,18 @@
2
2
 
3
3
 
4
4
 
5
+ ## [3.114.0] - 2025-03-23
6
+
7
+ ### Add Features
8
+
9
+ - new login dialog
10
+ - finalize digit control [#300](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/300)
11
+ ### Changes
12
+
13
+ - update project and web tests
14
+
15
+
16
+
5
17
  ## [3.113.0] - 2025-03-20
6
18
 
7
19
  ### Add Features
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.6.13","@popperjs/core":"^2.11.8","buffer":"^6.0.3"},"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":"3.113.0"}
1
+ {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.6.13","@popperjs/core":"^2.11.8","buffer":"^6.0.3"},"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":"3.114.0"}
@@ -0,0 +1,372 @@
1
+ /**
2
+ * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
3
+ * Node module: @schukai/monster
4
+ *
5
+ * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
6
+ * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
7
+ *
8
+ * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
9
+ * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
10
+ * For more information about purchasing a commercial license, please contact schukai GmbH.
11
+ */
12
+
13
+ import { instanceSymbol } from "../../constants.mjs";
14
+ import { ATTRIBUTE_ROLE } from "../../dom/constants.mjs";
15
+ import {
16
+ assembleMethodSymbol,
17
+ registerCustomElement,
18
+ } from "../../dom/customelement.mjs";
19
+ import { DigitsStyleSheet } from "./stylesheet/digits.mjs";
20
+ import { addErrorAttribute } from "../../dom/error.mjs";
21
+ import { Observer } from "../../types/observer.mjs";
22
+ import { CustomControl } from "../../dom/customcontrol.mjs";
23
+ import { InvalidStyleSheet } from "./stylesheet/invalid.mjs";
24
+
25
+ export { Digits };
26
+
27
+ /**
28
+ * @private
29
+ * @type {symbol}
30
+ */
31
+ const digitsElementSymbol = Symbol("digitsElement");
32
+
33
+ /**
34
+ * A Digits
35
+ *
36
+ * @fragments /fragments/components/form/digits/
37
+ *
38
+ * @example /examples/components/form/digits-simple
39
+ *
40
+ * @since 3.113.0
41
+ * @copyright schukai GmbH
42
+ * @summary A beautiful Digits that can make your life easier and also looks good.
43
+ */
44
+ class Digits extends CustomControl {
45
+ /**
46
+ *
47
+ */
48
+ constructor() {
49
+ super();
50
+ initOptionObserver.call(this);
51
+ }
52
+
53
+ /**
54
+ * This method is called by the `instanceof` operator.
55
+ * @returns {symbol}
56
+ */
57
+ static get [instanceSymbol]() {
58
+ return Symbol.for("@schukai/monster/components/form/digits@@instance");
59
+ }
60
+
61
+ /**
62
+ *
63
+ * @return {Components.Form.Digits
64
+ */
65
+ [assembleMethodSymbol]() {
66
+ super[assembleMethodSymbol]();
67
+
68
+ setTimeout(() => {
69
+ initControlReferences.call(this);
70
+ initEventHandler.call(this);
71
+ updateDigitControls.call(this);
72
+ }, 0);
73
+
74
+ return this;
75
+ }
76
+
77
+ /**
78
+ * The current value of the Switch
79
+ *
80
+ * ```
81
+ * e = document.querySelector('monster-toggle-switch');
82
+ * console.log(e.value)
83
+ * // ↦ on
84
+ * ```
85
+ *
86
+ * @return {string}
87
+ */
88
+ get value() {
89
+ return this.getOption("value");
90
+ }
91
+
92
+ /**
93
+ * Set value
94
+ *
95
+ * ```
96
+ * e = document.querySelector('monster-toggle-switch');
97
+ * e.value="on"
98
+ * ```
99
+ *
100
+ * @property {string} value
101
+ */
102
+ set value(value) {
103
+ const chars = String(value).split("");
104
+
105
+ this.setOption("value", value);
106
+ this.setFormValue(value);
107
+
108
+ if (chars.every(checkCharacter.bind(this))) {
109
+ this.setValidity({ badInput: false }, "");
110
+ } else {
111
+ this.setValidity(
112
+ { badInput: true },
113
+ "The value contains invalid characters",
114
+ );
115
+ }
116
+ }
117
+
118
+ /**
119
+ * To set the options via the HTML Tag, the attribute `data-monster-options` must be used.
120
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
121
+ *
122
+ * The individual configuration values can be found in the table.
123
+ *
124
+ * @property {Object} templates Template definitions
125
+ * @property {string} templates.main Main template
126
+ * @property {number} digits Number of digits
127
+ * @property {string} characterSet Character set for the digits, which are allowed
128
+ */
129
+ get defaults() {
130
+ return Object.assign({}, super.defaults, {
131
+ templates: {
132
+ main: getTemplate(),
133
+ },
134
+
135
+ digits: 4,
136
+ characterSet: "0123456789",
137
+
138
+ digitsControls: [],
139
+ value: null,
140
+ });
141
+ }
142
+
143
+ /**
144
+ * @return {string}
145
+ */
146
+ static getTag() {
147
+ return "monster-digits";
148
+ }
149
+
150
+ /**
151
+ * @return {CSSStyleSheet[]}
152
+ */
153
+ static getCSSStyleSheet() {
154
+ return [DigitsStyleSheet, InvalidStyleSheet];
155
+ }
156
+
157
+ /**
158
+ * @return {void}
159
+ */
160
+ focus(options) {
161
+ const element = this[digitsElementSymbol].querySelector("input");
162
+ if (element) {
163
+ element.focus(options);
164
+ }
165
+ }
166
+
167
+ /**
168
+ * @return {void}
169
+ */
170
+ blur() {
171
+ const elements = this[digitsElementSymbol].querySelectorAll("input");
172
+ if (elements) {
173
+ for (const element of elements) {
174
+ element.blur();
175
+ }
176
+ }
177
+ }
178
+ }
179
+
180
+ /**
181
+ * @private
182
+ */
183
+ function initOptionObserver() {
184
+ const self = this;
185
+
186
+ let lastValue = this.getOption("value");
187
+
188
+ self.attachObserver(
189
+ new Observer(function () {
190
+ if (lastValue !== self.getOption("value")) {
191
+ lastValue = self.getOption("value");
192
+ updateDigitControls.call(self);
193
+ }
194
+ }),
195
+ );
196
+ }
197
+
198
+ /**
199
+ * @private
200
+ */
201
+ function updateDigitControls() {
202
+ const digits = this.getOption("digits") || this.setOption("digits", 4);
203
+
204
+ const controls = [];
205
+
206
+ const values = this.getOption("value") || "";
207
+
208
+ if (this[digitsElementSymbol]) {
209
+ this[digitsElementSymbol].style.gridTemplateColumns =
210
+ `repeat(${digits}, 1fr)`;
211
+ }
212
+
213
+ for (let i = 0; i < digits; i++) {
214
+ controls.push({
215
+ value: values[i] ?? " ",
216
+ });
217
+ }
218
+
219
+ this.setOption("digitsControls", controls);
220
+ }
221
+
222
+ /**
223
+ * @private
224
+ * @param value
225
+ * @returns {boolean}
226
+ */
227
+ function checkCharacter(value) {
228
+ const characterSet = this.getOption("characterSet");
229
+ return characterSet.includes(value);
230
+ }
231
+
232
+ /**
233
+ * @private
234
+ * @returns {initEventHandler}
235
+ */
236
+ function initEventHandler() {
237
+ const self = this;
238
+ const element = this[digitsElementSymbol];
239
+
240
+ element.addEventListener("keydown", function (event) {
241
+ if (event.target.tagName !== "INPUT") return;
242
+ const inputControl = event.target;
243
+
244
+ const pressedKey = event.key;
245
+
246
+ if ((event.ctrlKey || event.metaKey) && pressedKey === "v") {
247
+ console.log("paste");
248
+ event.preventDefault();
249
+
250
+ navigator.clipboard
251
+ .readText()
252
+ .then((clipText) => {
253
+ self.value = clipText;
254
+ })
255
+ .catch(() => {
256
+ addErrorAttribute(this, "Error while pasting");
257
+ });
258
+
259
+ return;
260
+ }
261
+
262
+ if (pressedKey === "ArrowRight") {
263
+ const nextControl = inputControl.nextElementSibling;
264
+ if (nextControl && nextControl.tagName === "INPUT") {
265
+ event.preventDefault();
266
+ nextControl.focus();
267
+ }
268
+ return;
269
+ }
270
+
271
+ if (pressedKey === "ArrowLeft") {
272
+ const previousControl = inputControl.previousElementSibling;
273
+ if (previousControl && previousControl.tagName === "INPUT") {
274
+ event.preventDefault();
275
+ previousControl.focus();
276
+ }
277
+ return;
278
+ }
279
+
280
+ if (pressedKey === "Backspace") {
281
+ if (inputControl.value !== "" && inputControl.value !== " ") {
282
+ event.preventDefault();
283
+ inputControl.value = "";
284
+ collectValues.call(self);
285
+ return;
286
+ } else {
287
+ const previousControl = inputControl.previousElementSibling;
288
+ if (previousControl && previousControl.tagName === "INPUT") {
289
+ event.preventDefault();
290
+ previousControl.focus();
291
+ }
292
+ return;
293
+ }
294
+ }
295
+
296
+ if (pressedKey.length === 1) {
297
+ if (!checkCharacter.call(self, pressedKey)) {
298
+ event.preventDefault();
299
+ inputControl.classList.add("invalid");
300
+
301
+ setTimeout(() => {
302
+ inputControl.classList.remove("invalid");
303
+ }, 500);
304
+ return;
305
+ }
306
+
307
+ if (inputControl.value.length === 1) {
308
+ event.preventDefault();
309
+ inputControl.value = pressedKey;
310
+ const nextControl = inputControl.nextElementSibling;
311
+ if (nextControl && nextControl.tagName === "INPUT") {
312
+ nextControl.focus();
313
+ }
314
+ collectValues.call(self);
315
+ return;
316
+ }
317
+ }
318
+ });
319
+
320
+ // Input event as a fallback: On successful input, the focus changes.
321
+ element.addEventListener("input", function (event) {
322
+ if (event.target.tagName !== "INPUT") return;
323
+ if (event.target.value.length === 1) {
324
+ const nextControl = event.target.nextElementSibling;
325
+ if (nextControl && nextControl.tagName === "INPUT") {
326
+ nextControl.focus();
327
+ }
328
+ collectValues.call(self);
329
+ }
330
+ });
331
+
332
+ return this;
333
+ }
334
+
335
+ function collectValues() {
336
+ const controlsValues = Array.from(
337
+ this[digitsElementSymbol].querySelectorAll("input"),
338
+ ).map((input) => input.value || " ");
339
+ this.value = controlsValues.join("");
340
+ }
341
+
342
+ /**
343
+ * @private
344
+ * @return {void}
345
+ */
346
+ function initControlReferences() {
347
+ this[digitsElementSymbol] = this.shadowRoot.querySelector(
348
+ `[${ATTRIBUTE_ROLE}="digits"]`,
349
+ );
350
+ }
351
+
352
+ /**
353
+ * @private
354
+ * @return {string}
355
+ */
356
+ function getTemplate() {
357
+ // language=HTML
358
+ return `
359
+ <template id="digit">
360
+ <input maxlength="1"
361
+ data-monster-attributes="
362
+ value path:digit.value">
363
+ </template>
364
+
365
+ <div data-monster-role="control" part="control" data-monster-attributes="class path:classes.control">
366
+ <div part="digits" data-monster-role="digits"
367
+ data-monster-insert="digit path:digitsControls"
368
+ tabindex="-1"></div>
369
+ </div>`;
370
+ }
371
+
372
+ registerCustomElement(Digits);
@@ -30,6 +30,7 @@ import "../layout/collapse.mjs";
30
30
  import "./toggle-switch.mjs";
31
31
  import { getLocaleOfDocument } from "../../dom/locale.mjs";
32
32
  import { addErrorAttribute } from "../../dom/error.mjs";
33
+ import { InvalidStyleSheet } from "./stylesheet/invalid.mjs";
33
34
 
34
35
  export { FieldSet };
35
36
 
@@ -148,7 +149,7 @@ class FieldSet extends CustomControl {
148
149
  * @return {CSSStyleSheet[]}
149
150
  */
150
151
  static getCSSStyleSheet() {
151
- return [FieldSetStyleSheet];
152
+ return [FieldSetStyleSheet, InvalidStyleSheet];
152
153
  }
153
154
 
154
155
  /**
@@ -25,6 +25,7 @@ import { datasourceLinkedElementSymbol } from "../datatable/util.mjs";
25
25
  import { FormStyleSheet } from "./stylesheet/form.mjs";
26
26
  import { addAttributeToken } from "../../dom/attributes.mjs";
27
27
  import { getDocument } from "../../dom/util.mjs";
28
+ import { InvalidStyleSheet } from "./stylesheet/invalid.mjs";
28
29
 
29
30
  export { Form };
30
31
 
@@ -88,7 +89,8 @@ class Form extends DataSet {
88
89
  },
89
90
 
90
91
  reportValidity: {
91
- selector: "input,select,textarea,monster-select,monster-toggle-switch",
92
+ selector:
93
+ "input,select,textarea,monster-select,monster-toggle-switch,monster-password",
92
94
  },
93
95
 
94
96
  eventProcessing: true,
@@ -112,7 +114,7 @@ class Form extends DataSet {
112
114
  * @return {CSSStyleSheet[]}
113
115
  */
114
116
  static getCSSStyleSheet() {
115
- return [FormStyleSheet];
117
+ return [FormStyleSheet, InvalidStyleSheet];
116
118
  }
117
119
 
118
120
  /**