@schukai/monster 3.91.0 → 3.92.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 CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
 
4
4
 
5
+ ## [3.92.1] - 2024-12-18
6
+
7
+ ### Bug Fixes
8
+
9
+ - **tabs:** Only activate the closing behaviour if the closed tab was previously active. [#264](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/264)
10
+
11
+
12
+
13
+ ## [3.92.0] - 2024-12-18
14
+
15
+ ### Add Features
16
+
17
+ - **tabs:** new feature flag removeBehavior [#268](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/268) , update form/template and tree-select
18
+ ### Changes
19
+
20
+ - update project
21
+
22
+
23
+
5
24
  ## [3.91.0] - 2024-12-15
6
25
 
7
26
  ### Add Features
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.6.12","@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":"3.91.0"}
1
+ {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.6.12","@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":"3.92.1"}
@@ -58,6 +58,9 @@ const originValuesSymbol = Symbol("originValues");
58
58
  */
59
59
  const badgeElementSymbol = Symbol("badgeElement");
60
60
 
61
+ /**
62
+ *
63
+ */
61
64
  class SaveButton extends CustomElement {
62
65
  /**
63
66
  * This method is called by the `instanceof` operator.
@@ -53,23 +53,12 @@ const popperElementSymbol = Symbol("popperElement");
53
53
  */
54
54
  const iconElementSymbol = Symbol("iconElement");
55
55
 
56
- /**
57
- * The ContextError control shows an error message in a popper.
58
- *
59
- * @fragments /fragments/components/form/context-error/
60
- *
61
- * @example /examples/components/form/context-error-simple
62
- *
63
- * @copyright schukai GmbH
64
- * @summary A control that can be used to display a tooltip or a popover with an error message.
65
- **/
66
-
67
56
  /**
68
57
  * A context error control.
69
58
  *
70
- * @fragments /fragments/components/form/select/
59
+ * @fragments /fragments/components/form/context-error
71
60
  *
72
- * @example /examples/components/form/select-simple
61
+ * @example /examples/components/form/context-error-simple
73
62
  *
74
63
  * @since 3.55.0
75
64
  * @copyright schukai GmbH
@@ -23,7 +23,7 @@ export { ContextHelp };
23
23
  /**
24
24
  * A context help control.
25
25
  *
26
- * @fragments /fragments/components/form/context-help/
26
+ * @fragments /fragments/components/form/context-help
27
27
  *
28
28
  * @example /examples/components/form/context-help-simple
29
29
  *
@@ -50,9 +50,9 @@ export const inputElementSymbol = Symbol("inputIconElement");
50
50
  /**
51
51
  * A password field
52
52
  *
53
- * @fragments /fragments/components/components/form/password/
53
+ * @fragments /fragments/components/form/password
54
54
  *
55
- * @example /examples/components/components/form/password-simple
55
+ * @example /examples/components/form/password-simple
56
56
  *
57
57
  * @since 3.89.0
58
58
  * @copyright schukai GmbH
@@ -121,7 +121,7 @@ class Reload extends CustomElement {
121
121
  * @property {string} url=undefined
122
122
  * @property {string} reload=undefined currently the values defined are `onshow` and `always`. The default `onshow` removes the IntersectionObserver. This means that the content is only loaded once. reloading of the content does not occur.
123
123
  * @property {string} filter=undefined dom selectors to search for elements, if undefined then everything is taken
124
- * @property {Monster.Components.Form.Processor[]} processors
124
+ * @property {Object[]} processors
125
125
  * @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
126
126
  * @property {String} fetch.redirect=error
127
127
  * @property {String} fetch.method=GET
@@ -50,7 +50,7 @@ class ShadowReload extends Reload {
50
50
  * @property {string} url=undefined
51
51
  * @property {string} reload=undefined currently the values defined are `onshow` and `always`. The default `onshow` removes the IntersectionObserver. This means that the content is only loaded once. reloading of the content does not occur.
52
52
  * @property {string} filter=undefined dom selectors to search for elements, if undefined then everything is taken
53
- * @property {Monster.Components.Form.Processor[]} processors
53
+ * @property {Object[]} processors
54
54
  * @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
55
55
  * @property {String} fetch.redirect=error
56
56
  * @property {String} fetch.method=GET
@@ -40,9 +40,12 @@ const intersectionObserverWasInitialized = Symbol("wasInitialized");
40
40
  /**
41
41
  * A Template control is a control that can be used to load content from a URL and display it in the ShadowRoot.
42
42
  *
43
- * @fragments /fragments/components/form/template/
43
+ * @fragments /fragments/components/form/template
44
44
  *
45
45
  * @example /examples/components/form/template-simple
46
+ * @example /examples/components/form/template-with-default
47
+ * @example /examples/components/form/template-with-processor
48
+ * @example /examples/components/form/template-onshow
46
49
  *
47
50
  * @since 1.11.0
48
51
  * @copyright schukai GmbH
@@ -69,7 +72,7 @@ class Template extends CustomElement {
69
72
  * @property {string} templates.main Main template
70
73
  * @property {string} url=undefined
71
74
  * @property {string} reload=undefined currently the only value defined is `onshow`. Currently the only value defined is onshow. this removes the IntersectionObserver. this means that the content is only loaded once. reloading of the content does not occur.
72
- * @property {Monster.Components.Form.Processor[]} processors
75
+ * @property {Object[]} processors
73
76
  * @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
74
77
  * @property {String} fetch.redirect=error
75
78
  * @property {String} fetch.method=GET
@@ -85,8 +88,8 @@ class Template extends CustomElement {
85
88
  templates: {
86
89
  main: getTemplate(),
87
90
  },
88
- url: undefined,
89
- reload: undefined,
91
+ url: null,
92
+ reload: null,
90
93
  processors: [],
91
94
  fetch: {
92
95
  redirect: "error",
@@ -123,8 +126,6 @@ class Template extends CustomElement {
123
126
  this[attributeObserverSymbol][ATTRIBUTE_FORM_URL] = (url) => {
124
127
  if (this.hasAttribute(ATTRIBUTE_FORM_URL)) {
125
128
  this.setOption("url", new URL(url, document.location).toString());
126
- } else {
127
- this.setOption("url", undefined);
128
129
  }
129
130
  };
130
131
  }
@@ -139,7 +140,7 @@ class Template extends CustomElement {
139
140
  * @throws {Error} not found
140
141
  * @throws {Error} undefined status or type
141
142
  * @fires monster-fetched
142
- * @return {Monster.Components.Form.Form}
143
+ * @return {void}
143
144
  */
144
145
  [assembleMethodSymbol]() {
145
146
  super[assembleMethodSymbol]();
@@ -181,13 +182,6 @@ class Template extends CustomElement {
181
182
  }
182
183
  }
183
184
 
184
- /**
185
- * @typedef {Object} Processor
186
- * @property {String} destination
187
- * @property {String} source
188
- * @since 1.11.8
189
- */
190
-
191
185
  /**
192
186
  * This attribute can be used to pass a URL to this select.
193
187
  *
@@ -276,7 +270,12 @@ function loadContent() {
276
270
  throw new Error("no shadow-root is defined");
277
271
  }
278
272
 
279
- const url = this.getOption("url", undefined);
273
+ let url = this.getOption("url", undefined);
274
+
275
+ if (url instanceof URL) {
276
+ url = url.toString();
277
+ }
278
+
280
279
  if (!isString(url) || url === "") {
281
280
  throw new Error("missing url");
282
281
  }
@@ -303,6 +302,7 @@ function loadContent() {
303
302
  loadAndAssignContent(container, url, options)
304
303
  .then(() => {
305
304
  defaultSlot.style.display = "none";
305
+ container.style.display = "block";
306
306
  runProcessors.call(this);
307
307
  })
308
308
  .catch((e) => {
@@ -316,15 +316,40 @@ function loadContent() {
316
316
  */
317
317
  function runProcessors() {
318
318
  const processors = this.getOption("processors");
319
- if (!isArray(processors)) return;
319
+ if (!isArray(processors)) return this;
320
320
 
321
321
  for (const [, processor] of processors.entries()) {
322
322
  const source = processor?.source;
323
- const destination = processor?.destination;
323
+ let destination = processor?.destination;
324
+
325
+ if (source === null) {
326
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, "missing source");
327
+ continue;
328
+ }
329
+
330
+ if (
331
+ destination === null ||
332
+ destination === undefined ||
333
+ destination === ""
334
+ ) {
335
+ destination = "[" + ATTRIBUTE_ROLE + "=container]";
336
+ }
324
337
 
325
338
  if (isString(source) && isString(destination)) {
326
339
  const sourceNode = this.shadowRoot.querySelector(source);
327
- const destinationNode = document.querySelector(destination);
340
+ let destinationNode = document.querySelector(destination);
341
+
342
+ if (destinationNode === null) {
343
+ destinationNode = this.shadowRoot.querySelector(destination);
344
+ if (destinationNode === null) {
345
+ addAttributeToken(
346
+ this,
347
+ ATTRIBUTE_ERRORMESSAGE,
348
+ "destination not found",
349
+ );
350
+ continue;
351
+ }
352
+ }
328
353
 
329
354
  if (
330
355
  sourceNode instanceof HTMLElement &&
@@ -332,6 +357,12 @@ function runProcessors() {
332
357
  ) {
333
358
  destinationNode.innerHTML = sourceNode.cloneNode(true).innerHTML;
334
359
  }
360
+ } else {
361
+ addAttributeToken(
362
+ this,
363
+ ATTRIBUTE_ERRORMESSAGE,
364
+ "invalid source or destination",
365
+ );
335
366
  }
336
367
  }
337
368
 
@@ -13,8 +13,12 @@
13
13
  */
14
14
 
15
15
  import { buildTree } from "../../data/buildtree.mjs";
16
- import { findClosestByAttribute } from "../../dom/attributes.mjs";
17
16
  import {
17
+ addAttributeToken,
18
+ findClosestByAttribute,
19
+ } from "../../dom/attributes.mjs";
20
+ import {
21
+ ATTRIBUTE_ERRORMESSAGE,
18
22
  ATTRIBUTE_ROLE,
19
23
  ATTRIBUTE_UPDATER_INSERT_REFERENCE,
20
24
  } from "../../dom/constants.mjs";
@@ -57,7 +61,7 @@ const keyEventHandler = Symbol("keyEventHandler");
57
61
  *
58
62
  * @fragments /fragments/components/form/tree-select
59
63
  *
60
- * @example /examples/components/form/tree-select
64
+ * @example /examples/components/form/tree-select-simple
61
65
  *
62
66
  * @since 1.9.0
63
67
  * @copyright schukai GmbH
@@ -98,8 +102,12 @@ class TreeSelect extends Select {
98
102
  {
99
103
  mapping: {
100
104
  rootReferences: ["0", undefined, null],
101
- idTemplate: "id",
102
- parentTemplate: "parent",
105
+ id: "id",
106
+ parent: "parent",
107
+
108
+ selector: "*",
109
+ labelTemplate: "",
110
+ valueTemplate: "",
103
111
  },
104
112
  formatter: {
105
113
  selection: formatHierarchicalSelection,
@@ -145,54 +153,58 @@ class TreeSelect extends Select {
145
153
  const filter = mappingOptions?.["filter"];
146
154
  const rootReferences = mappingOptions?.["rootReferences"];
147
155
 
148
- const id = this.getOption("mapping.idTemplate", "id");
149
- const parentID = this.getOption("mapping.parentTemplate", "parent");
156
+ const id = this.getOption("mapping.id", "id");
157
+ const parentID = this.getOption("mapping.parent", "parent");
150
158
 
151
159
  const selector = mappingOptions?.["selector"];
152
-
153
- const nodes = buildTree(data, selector, id, parentID, {
154
- filter,
155
- rootReferences,
156
- });
157
-
158
160
  const options = [];
159
- for (const node of nodes) {
160
- const iterator = new NodeRecursiveIterator(node);
161
- for (const n of iterator) {
162
- const formattedValues = formatKeyLabel.call(this, n);
163
-
164
- const label = formattedValues.label;
165
- const value = formattedValues.value;
166
- const intend = n.level;
167
-
168
- const visibility = intend > 0 ? "hidden" : "visible";
169
- const state = "close";
170
-
171
- this[internalNodesSymbol].set(value, n);
172
-
173
- options.push({
174
- value,
175
- label,
176
- intend,
177
- state,
178
- visibility,
179
- ["has-children"]: n.hasChildNodes(),
180
- });
161
+
162
+ try {
163
+ const nodes = buildTree(data, selector, id, parentID, {
164
+ filter,
165
+ rootReferences,
166
+ });
167
+
168
+ for (const node of nodes) {
169
+ const iterator = new NodeRecursiveIterator(node);
170
+ for (const n of iterator) {
171
+ const formattedValues = formatKeyLabel.call(this, n);
172
+
173
+ const label = formattedValues.label;
174
+ const value = formattedValues.value;
175
+ const intend = n.level;
176
+
177
+ const visibility = intend > 0 ? "hidden" : "visible";
178
+ const state = "close";
179
+
180
+ this[internalNodesSymbol].set(value, n);
181
+
182
+ options.push({
183
+ value,
184
+ label,
185
+ intend,
186
+ state,
187
+ visibility,
188
+ ["has-children"]: n.hasChildNodes(),
189
+ });
190
+ }
181
191
  }
182
- }
183
192
 
184
- this.setOption("options", options);
193
+ this.setOption("options", options);
185
194
 
186
- fireCustomEvent(this, "monster-options-set", {
187
- options,
188
- });
195
+ fireCustomEvent(this, "monster-options-set", {
196
+ options,
197
+ });
198
+ } catch (e) {
199
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e?.message || e);
200
+ }
189
201
 
190
202
  return this;
191
203
  }
192
204
 
193
205
  /**
194
206
  *
195
- * @return {Monster.Components.Form.Select}
207
+ * @return {TreeSelect}
196
208
  */
197
209
  [assembleMethodSymbol]() {
198
210
  super[assembleMethodSymbol]();
@@ -251,10 +263,16 @@ function closeOrOpenCurrentOption(event, mode) {
251
263
  function formatKeyLabel(node) {
252
264
  validateInstance(node, Node);
253
265
 
254
- const label = new Formatter(node.value).format(
266
+ const v = node.value;
267
+ if (v === undefined) {
268
+ throw new Error("the object has no value for the specified id");
269
+ }
270
+
271
+ const label = new Formatter(v).format(
255
272
  this.getOption("mapping.labelTemplate", ""),
256
273
  );
257
- const value = new Formatter(node.value).format(
274
+
275
+ const value = new Formatter(v).format(
258
276
  this.getOption("mapping.valueTemplate", ""),
259
277
  );
260
278