@schukai/monster 3.91.0 → 3.92.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,17 @@
2
2
 
3
3
 
4
4
 
5
+ ## [3.92.0] - 2024-12-18
6
+
7
+ ### Add Features
8
+
9
+ - **tabs:** new feature flag removeBehavior [#268](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/268) , update form/template and tree-select
10
+ ### Changes
11
+
12
+ - update project
13
+
14
+
15
+
5
16
  ## [3.91.0] - 2024-12-15
6
17
 
7
18
  ### 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.0"}
@@ -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
 
@@ -146,6 +146,10 @@ const resizeObserverSymbol = Symbol("resizeObserver");
146
146
  * @fragments /fragments/components/layout/tabs/
147
147
  *
148
148
  * @example /examples/components/layout/tabs-simple
149
+ * @example /examples/components/layout/tabs-active
150
+ * @example /examples/components/layout/tabs-removable
151
+ *
152
+ * @issue https://localhost.alvine.dev:8443/development/issues/closed/268.html
149
153
  *
150
154
  * @since 3.74.0
151
155
  * @copyright schukai GmbH
@@ -172,6 +176,7 @@ class Tabs extends CustomElement {
172
176
  * @property {string} labels.new-tab-label="New Tab"
173
177
  * @property {Object} features
174
178
  * @property {number} features.openDelay=500 Open delay in milliseconds
179
+ * @property {string} features.removeBehavior="auto" Remove behavior, auto (default), next, previous and none
175
180
  * @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
176
181
  * @property {String} fetch.redirect=error
177
182
  * @property {String} fetch.method=GET
@@ -206,6 +211,7 @@ class Tabs extends CustomElement {
206
211
 
207
212
  features: {
208
213
  openDelay: null,
214
+ removeBehavior: "auto",
209
215
  },
210
216
 
211
217
  classes: {
@@ -510,7 +516,6 @@ function attachTabChangeObserver() {
510
516
  * @private
511
517
  * @return {Select}
512
518
  * @external "external:createPopper"
513
- * @see {@link Plugins}
514
519
  */
515
520
  function initPopper() {
516
521
  const self = this;
@@ -685,6 +690,10 @@ function initEventHandler() {
685
690
  const reference = button.getAttribute(
686
691
  `${ATTRIBUTE_PREFIX}tab-reference`,
687
692
  );
693
+
694
+ const previous = button.previousElementSibling;
695
+ const next = button.nextElementSibling;
696
+
688
697
  if (reference) {
689
698
  const container = this.querySelector(`[id=${reference}]`);
690
699
  if (container instanceof HTMLElement) {
@@ -695,6 +704,32 @@ function initEventHandler() {
695
704
  });
696
705
  }
697
706
  }
707
+
708
+ switch (this.getOption("features.removeBehavior")) {
709
+ case "auto":
710
+ if (next instanceof HTMLButtonElement) {
711
+ next.click();
712
+ } else {
713
+ // get previous button
714
+ if (previous instanceof HTMLButtonElement) {
715
+ previous.click();
716
+ }
717
+ }
718
+ break;
719
+ case "next":
720
+ if (next instanceof HTMLButtonElement) {
721
+ next.click();
722
+ }
723
+ break;
724
+ case "previous":
725
+ if (previous instanceof HTMLButtonElement) {
726
+ previous.click();
727
+ }
728
+ break;
729
+
730
+ default: // and "none"
731
+ break;
732
+ }
698
733
  }
699
734
  }
700
735
  };
@@ -826,7 +861,7 @@ function initTabButtons() {
826
861
  }
827
862
 
828
863
  if (node.hasAttribute(`${ATTRIBUTE_PREFIX}button-icon`)) {
829
- label = `<span part="label">${label}</span><img part="icon" src="${node.getAttribute(
864
+ label = `<span part="label">${label}</span><img part="icon" alt="this is an icon" src="${node.getAttribute(
830
865
  `${ATTRIBUTE_PREFIX}button-icon`,
831
866
  )}">`;
832
867
  }
@@ -1107,7 +1142,7 @@ function getTemplate() {
1107
1142
  data-monster-insert="buttons path:buttons.standard">
1108
1143
  <slot name="start" class="invisible"></slot>
1109
1144
  <div data-monster-role="popper" part="popper" tabindex="-1"
1110
- data-monster-attributes="class path:classes.popper">
1145
+ data-monster-attributes="class path:classes.popper">
1111
1146
  <div data-popper-arrow></div>
1112
1147
 
1113
1148
 
@@ -156,7 +156,7 @@ function getMonsterVersion() {
156
156
  }
157
157
 
158
158
  /** don't touch, replaced by make with package.json version */
159
- monsterVersion = new Version("3.90.0");
159
+ monsterVersion = new Version("3.91.0");
160
160
 
161
161
  return monsterVersion;
162
162
  }
@@ -7,7 +7,7 @@ describe('Monster', function () {
7
7
  let monsterVersion
8
8
 
9
9
  /** don´t touch, replaced by make with package.json version */
10
- monsterVersion = new Version("3.90.0")
10
+ monsterVersion = new Version("3.91.0")
11
11
 
12
12
  let m = getMonsterVersion();
13
13
 
@@ -9,8 +9,8 @@
9
9
  </head>
10
10
  <body>
11
11
  <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
12
- <h1 style='margin-bottom: 0.1em;'>Monster 3.90.0</h1>
13
- <div id="lastupdate" style='font-size:0.7em'>last update Mo 16. Dez 00:30:57 CET 2024</div>
12
+ <h1 style='margin-bottom: 0.1em;'>Monster 3.91.0</h1>
13
+ <div id="lastupdate" style='font-size:0.7em'>last update Mi 18. Dez 01:06:46 CET 2024</div>
14
14
  </div>
15
15
  <div id="mocha-errors"
16
16
  style="color: red;font-weight: bold;display: flex;align-items: center;justify-content: center;flex-direction: column;margin:20px;"></div>