@schukai/monster 4.98.1 → 4.100.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.100.0] - 2026-01-16
6
+
7
+ ### Add Features
8
+
9
+ - Add confirmation button and API feedback for issue [#375](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/375)
10
+
11
+
12
+
13
+ ## [4.99.0] - 2026-01-15
14
+
15
+ ### Add Features
16
+
17
+ - Add a checklist component to enhance task tracking
18
+
19
+
20
+
5
21
  ## [4.98.1] - 2026-01-14
6
22
 
7
23
  ### Bug Fixes
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.4","@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.98.1"}
1
+ {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.4","@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.100.0"}
@@ -0,0 +1,361 @@
1
+ /**
2
+ * Copyright © Volker Schukai 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 Volker Schukai.
11
+ *
12
+ * SPDX-License-Identifier: AGPL-3.0
13
+ */
14
+
15
+ import { instanceSymbol } from "../../constants.mjs";
16
+ import { CustomControl } from "../../dom/customcontrol.mjs";
17
+ import { Observer } from "../../types/observer.mjs";
18
+ import { clone } from "../../util/clone.mjs";
19
+ import { isArray, isObject, isString, isNumber } from "../../types/is.mjs";
20
+ import {
21
+ assembleMethodSymbol,
22
+ registerCustomElement,
23
+ attributeObserverSymbol,
24
+ } from "../../dom/customelement.mjs";
25
+ import { fireCustomEvent, fireEvent, findTargetElementFromEvent } from "../../dom/events.mjs";
26
+ import { ChecklistStyleSheet } from "./stylesheet/checklist.mjs";
27
+ import { addAttributeToken } from "../../dom/attributes.mjs";
28
+ import { ATTRIBUTE_ERRORMESSAGE } from "../../dom/constants.mjs";
29
+
30
+ export { Checklist };
31
+
32
+ /**
33
+ * @private
34
+ * @type {symbol}
35
+ */
36
+ const controlElementSymbol = Symbol("controlElement");
37
+
38
+ /**
39
+ * Checklist control for displaying and tracking a list of items.
40
+ *
41
+ * @summary A checklist control with change events and form integration.
42
+ */
43
+ class Checklist extends CustomControl {
44
+ /**
45
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
46
+ *
47
+ * @property {Array} items The checklist items.
48
+ * @property {boolean} disabled Disabled state.
49
+ * @property {Object} templates Template definitions.
50
+ * @property {string} templates.main Main template.
51
+ */
52
+ get defaults() {
53
+ return Object.assign({}, super.defaults, {
54
+ items: [],
55
+ disabled: false,
56
+ templates: {
57
+ main: getTemplate(),
58
+ },
59
+ });
60
+ }
61
+
62
+ /**
63
+ * @return {void}
64
+ */
65
+ [assembleMethodSymbol]() {
66
+ super[assembleMethodSymbol]();
67
+ initControlReferences.call(this);
68
+ initEventHandler.call(this);
69
+ initAttributeObservers.call(this);
70
+ normalizeItemsOption.call(this);
71
+ syncFormValue.call(this);
72
+
73
+ this.attachObserver(
74
+ new Observer(() => {
75
+ syncFormValue.call(this);
76
+ }),
77
+ );
78
+ }
79
+
80
+ /**
81
+ * @return {CSSStyleSheet[]}
82
+ */
83
+ static getCSSStyleSheet() {
84
+ return [ChecklistStyleSheet];
85
+ }
86
+
87
+ /**
88
+ * @return {string}
89
+ */
90
+ static getTag() {
91
+ return "monster-checklist";
92
+ }
93
+
94
+ /**
95
+ * @returns {Array}
96
+ */
97
+ get value() {
98
+ return getCheckedIds.call(this);
99
+ }
100
+
101
+ /**
102
+ * @param {Array} value
103
+ */
104
+ set value(value) {
105
+ if (!isArray(value)) {
106
+ value = [];
107
+ }
108
+ setCheckedIds.call(this, value);
109
+ }
110
+
111
+ /**
112
+ * @return {Array}
113
+ */
114
+ getItems() {
115
+ return clone(this.getOption("items", []));
116
+ }
117
+
118
+ /**
119
+ * @return {Array}
120
+ */
121
+ getCheckedItems() {
122
+ return this.getItems().filter((item) => Boolean(item?.checked));
123
+ }
124
+
125
+ /**
126
+ * @param {Array} items
127
+ * @return {Checklist}
128
+ */
129
+ setItems(items) {
130
+ const normalized = normalizeItems(items);
131
+ this.setOption("items", normalized.items);
132
+ syncFormValue.call(this);
133
+ return this;
134
+ }
135
+ }
136
+
137
+ /**
138
+ * @private
139
+ */
140
+ function initControlReferences() {
141
+ this[controlElementSymbol] = this.shadowRoot?.querySelector(
142
+ "[data-monster-role=control]",
143
+ );
144
+ }
145
+
146
+ /**
147
+ * @private
148
+ */
149
+ function initEventHandler() {
150
+ this.addEventListener("change", (event) => {
151
+ const checkbox = findTargetElementFromEvent(
152
+ event,
153
+ "data-monster-role",
154
+ "checkbox",
155
+ );
156
+ if (!(checkbox instanceof HTMLInputElement)) {
157
+ return;
158
+ }
159
+
160
+ if (this.getOption("disabled") === true) {
161
+ event.preventDefault();
162
+ return;
163
+ }
164
+
165
+ const itemId = checkbox.getAttribute("data-item-id");
166
+ if (!itemId) {
167
+ return;
168
+ }
169
+
170
+ const items = this.getOption("items", []);
171
+ const updated = clone(items).map((item) => {
172
+ if (item?.id === itemId) {
173
+ return Object.assign({}, item, { checked: checkbox.checked });
174
+ }
175
+ return item;
176
+ });
177
+
178
+ this.setOption("items", updated);
179
+ syncFormValue.call(this);
180
+
181
+ fireEvent(this, "change");
182
+ fireCustomEvent(this, "monster-checklist-change", {
183
+ id: itemId,
184
+ checked: checkbox.checked,
185
+ item: updated.find((item) => item?.id === itemId),
186
+ items: clone(updated),
187
+ value: getCheckedIds.call(this),
188
+ });
189
+ });
190
+ }
191
+
192
+ /**
193
+ * @private
194
+ */
195
+ function normalizeItemsOption() {
196
+ if (applyItemsAttribute.call(this)) {
197
+ return;
198
+ }
199
+ const normalized = normalizeItems(this.getOption("items", []));
200
+ if (normalized.changed) {
201
+ this.setOption("items", normalized.items);
202
+ }
203
+ }
204
+
205
+ /**
206
+ * @private
207
+ * @param {Array} items
208
+ * @return {{items: Array, changed: boolean}}
209
+ */
210
+ function normalizeItems(items) {
211
+ const normalized = [];
212
+ let changed = false;
213
+
214
+ if (!isArray(items)) {
215
+ return { items: [], changed: true };
216
+ }
217
+
218
+ for (const [index, entry] of Object.entries(items)) {
219
+ const idx = Number(index);
220
+ let item = entry;
221
+
222
+ if (isString(item) || isNumber(item)) {
223
+ item = {
224
+ id: `item-${idx}`,
225
+ label: String(item),
226
+ checked: false,
227
+ disabled: false,
228
+ };
229
+ changed = true;
230
+ } else if (isObject(item)) {
231
+ item = Object.assign({}, item, {
232
+ id: item.id ?? `item-${idx}`,
233
+ label: item.label ?? "",
234
+ checked: Boolean(item.checked),
235
+ disabled: Boolean(item.disabled),
236
+ });
237
+ if (item.id !== entry.id || item.label !== entry.label) {
238
+ changed = true;
239
+ }
240
+ } else {
241
+ item = {
242
+ id: `item-${idx}`,
243
+ label: "",
244
+ checked: false,
245
+ disabled: false,
246
+ };
247
+ changed = true;
248
+ }
249
+
250
+ normalized.push(item);
251
+ }
252
+
253
+ return { items: normalized, changed };
254
+ }
255
+
256
+ /**
257
+ * @private
258
+ * @return {boolean}
259
+ */
260
+ function applyItemsAttribute() {
261
+ if (!this.hasAttribute("data-monster-option-items")) {
262
+ return false;
263
+ }
264
+
265
+ const raw = this.getAttribute("data-monster-option-items");
266
+ if (!raw) {
267
+ this.setOption("items", []);
268
+ return true;
269
+ }
270
+
271
+ try {
272
+ const parsed = JSON.parse(raw);
273
+ if (!isArray(parsed)) {
274
+ addAttributeToken(
275
+ this,
276
+ ATTRIBUTE_ERRORMESSAGE,
277
+ "data-monster-option-items must be a JSON array",
278
+ );
279
+ return true;
280
+ }
281
+ const normalized = normalizeItems(parsed);
282
+ this.setOption("items", normalized.items);
283
+ return true;
284
+ } catch (error) {
285
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message || String(error));
286
+ return true;
287
+ }
288
+ }
289
+
290
+ /**
291
+ * @private
292
+ */
293
+ function initAttributeObservers() {
294
+ this[attributeObserverSymbol]["data-monster-option-items"] = () => {
295
+ applyItemsAttribute.call(this);
296
+ syncFormValue.call(this);
297
+ };
298
+ }
299
+
300
+ /**
301
+ * @private
302
+ * @return {Array}
303
+ */
304
+ function getCheckedIds() {
305
+ const items = this.getOption("items", []);
306
+ return items.filter((item) => item?.checked).map((item) => item.id);
307
+ }
308
+
309
+ /**
310
+ * @private
311
+ * @param {Array} ids
312
+ */
313
+ function setCheckedIds(ids) {
314
+ const items = this.getOption("items", []);
315
+ const updated = clone(items).map((item) =>
316
+ Object.assign({}, item, { checked: ids.includes(item?.id) }),
317
+ );
318
+ this.setOption("items", updated);
319
+ syncFormValue.call(this);
320
+ }
321
+
322
+ /**
323
+ * @private
324
+ */
325
+ function syncFormValue() {
326
+ const value = JSON.stringify(getCheckedIds.call(this));
327
+ if (typeof this.setFormValue === "function") {
328
+ this.setFormValue(value);
329
+ }
330
+ }
331
+
332
+ /**
333
+ * @private
334
+ * @return {string}
335
+ */
336
+ function getTemplate() {
337
+ // language=HTML
338
+ return `
339
+ <template id="item">
340
+ <label data-monster-role="item"
341
+ part="item"
342
+ data-monster-attributes="data-item-id path:item.id, class path:item.disabled | ?:disabled">
343
+ <input type="checkbox"
344
+ data-monster-role="checkbox"
345
+ part="checkbox"
346
+ data-monster-attributes="data-item-id path:item.id, checked path:item.checked | ?:checked, disabled path:item.disabled | ?:disabled">
347
+ <span data-monster-role="label"
348
+ part="label"
349
+ data-monster-replace="path:item.label | default: "></span>
350
+ </label>
351
+ </template>
352
+
353
+ <div data-monster-role="control" part="control">
354
+ <div data-monster-role="items"
355
+ part="items"
356
+ data-monster-insert="item path:items"></div>
357
+ </div>
358
+ `;
359
+ }
360
+
361
+ registerCustomElement(Checklist);
@@ -36,6 +36,9 @@ export { MessageStateButton };
36
36
  */
37
37
  const buttonElementSymbol = Symbol("buttonElement");
38
38
  const innerDisabledObserverSymbol = Symbol("innerDisabledObserver");
39
+ const popperElementSymbol = Symbol("popperElement");
40
+ const messageElementSymbol = Symbol("messageElement");
41
+ const measurementPopperSymbol = Symbol("measurementPopper");
39
42
 
40
43
  /**
41
44
  * A specialized button component that combines state management with message display capabilities.
@@ -239,6 +242,7 @@ class MessageStateButton extends Popper {
239
242
  * @return {MessageStateButton} Returns the button instance for chaining
240
243
  */
241
244
  showMessage(timeout) {
245
+ applyMeasuredMessageWidth.call(this);
242
246
  this.showDialog.call(this);
243
247
 
244
248
  if (timeout !== undefined) {
@@ -398,6 +402,12 @@ function initControlReferences() {
398
402
  this[buttonElementSymbol] = this.shadowRoot.querySelector(
399
403
  `[${ATTRIBUTE_ROLE}=button]`,
400
404
  );
405
+ this[popperElementSymbol] = this.shadowRoot.querySelector(
406
+ `[${ATTRIBUTE_ROLE}=popper]`,
407
+ );
408
+ this[messageElementSymbol] = this.shadowRoot.querySelector(
409
+ `[${ATTRIBUTE_ROLE}=message]`,
410
+ );
401
411
  }
402
412
 
403
413
  /**
@@ -499,4 +509,106 @@ function getTemplate() {
499
509
  `;
500
510
  }
501
511
 
512
+ /**
513
+ * @private
514
+ * @param {string|HTMLElement} content
515
+ * @return {HTMLElement|string|null}
516
+ */
517
+ function getMeasurementContent(content) {
518
+ if (isString(content)) {
519
+ return content;
520
+ }
521
+
522
+ if (content instanceof HTMLElement) {
523
+ return content.cloneNode(true);
524
+ }
525
+
526
+ return null;
527
+ }
528
+
529
+ /**
530
+ * @private
531
+ * @return {{popper: HTMLElement, message: HTMLElement}|null}
532
+ */
533
+ function ensureMeasurementPopper() {
534
+ if (this[measurementPopperSymbol]) {
535
+ return this[measurementPopperSymbol];
536
+ }
537
+
538
+ if (!this.shadowRoot) {
539
+ return null;
540
+ }
541
+
542
+ const popper = document.createElement("div");
543
+ popper.setAttribute(ATTRIBUTE_ROLE, "popper");
544
+ popper.setAttribute("data-measurement", "true");
545
+ popper.setAttribute("aria-hidden", "true");
546
+ popper.style.position = "absolute";
547
+ popper.style.left = "-10000px";
548
+ popper.style.top = "-10000px";
549
+ popper.style.visibility = "hidden";
550
+ popper.style.display = "block";
551
+ popper.style.pointerEvents = "none";
552
+ popper.style.maxWidth = "none";
553
+ popper.style.width = "max-content";
554
+
555
+ const message = document.createElement("div");
556
+ message.setAttribute(ATTRIBUTE_ROLE, "message");
557
+ message.className = "flex";
558
+
559
+ popper.appendChild(message);
560
+ this.shadowRoot.appendChild(popper);
561
+
562
+ this[measurementPopperSymbol] = { popper, message };
563
+ return this[measurementPopperSymbol];
564
+ }
565
+
566
+ /**
567
+ * @private
568
+ */
569
+ function applyMeasuredMessageWidth() {
570
+ const popper = this[popperElementSymbol];
571
+ if (!popper) {
572
+ return;
573
+ }
574
+
575
+ const content = this.getOption("message.content");
576
+ const measureContent = getMeasurementContent(content);
577
+ if (!measureContent) {
578
+ return;
579
+ }
580
+
581
+ const measurement = ensureMeasurementPopper.call(this);
582
+ if (!measurement?.message) {
583
+ return;
584
+ }
585
+
586
+ measurement.message.innerHTML = "";
587
+ if (isString(measureContent)) {
588
+ measurement.message.innerHTML = measureContent;
589
+ } else {
590
+ measurement.message.appendChild(measureContent);
591
+ }
592
+
593
+ const measuredWidth = Math.ceil(
594
+ measurement.popper.getBoundingClientRect().width,
595
+ );
596
+ const fontSize = parseFloat(getComputedStyle(popper).fontSize) || 16;
597
+ const minWidth = Math.round(fontSize * 12);
598
+ const maxWidth = Math.max(
599
+ minWidth,
600
+ Math.min(window.innerWidth * 0.7, fontSize * 32),
601
+ );
602
+ const targetWidth = Math.max(
603
+ minWidth,
604
+ Math.min(measuredWidth || minWidth, maxWidth),
605
+ );
606
+
607
+ popper.style.width = `${targetWidth}px`;
608
+ popper.style.minWidth = `${minWidth}px`;
609
+ popper.style.maxWidth = `${maxWidth}px`;
610
+ popper.style.whiteSpace = "normal";
611
+ popper.style.overflowWrap = "anywhere";
612
+ }
613
+
502
614
  registerCustomElement(MessageStateButton);
@@ -0,0 +1,49 @@
1
+
2
+ :host {
3
+ box-sizing: border-box;
4
+ color: var(--monster-color-primary-1);
5
+ display: block;
6
+ font-family: var(--monster-font-family);
7
+ font-size: 1rem;
8
+ line-height: 1.4;
9
+ }
10
+
11
+ *,
12
+ *::before,
13
+ *::after {
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ [data-monster-role="control"] {
18
+ display: block;
19
+ }
20
+
21
+ [data-monster-role="items"] {
22
+ display: flex;
23
+ flex-direction: column;
24
+ gap: 0.4rem;
25
+ }
26
+
27
+ [data-monster-role="item"] {
28
+ align-items: flex-start;
29
+ cursor: pointer;
30
+ display: flex;
31
+ gap: 0.5rem;
32
+ line-height: 1.4;
33
+ }
34
+
35
+ [data-monster-role="item"].disabled {
36
+ cursor: not-allowed;
37
+ opacity: 0.6;
38
+ }
39
+
40
+ [data-monster-role="checkbox"] {
41
+ accent-color: var(--monster-color-secondary-1);
42
+ margin-top: 0.2rem;
43
+ }
44
+
45
+ [data-monster-role="label"] {
46
+ display: inline-block;
47
+ min-width: 0;
48
+ word-break: break-word;
49
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Copyright © Volker Schukai and all contributing authors, 2026. 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 Volker Schukai.
11
+ */
12
+
13
+ import {addAttributeToken} from "../../../dom/attributes.mjs";
14
+ import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs";
15
+
16
+ export {ChecklistStyleSheet}
17
+
18
+ /**
19
+ * @private
20
+ * @type {CSSStyleSheet}
21
+ */
22
+ const ChecklistStyleSheet = new CSSStyleSheet();
23
+
24
+ try {
25
+ ChecklistStyleSheet.insertRule(`
26
+ @layer checklist {
27
+ :host{box-sizing:border-box;color:var(--monster-color-primary-1);display:block;font-family:var(--monster-font-family);font-size:1rem;line-height:1.4}*,:after,:before{box-sizing:border-box}[data-monster-role=control]{display:block}[data-monster-role=items]{display:flex;flex-direction:column;gap:.4rem}[data-monster-role=item]{align-items:flex-start;cursor:pointer;display:flex;gap:.5rem;line-height:1.4}[data-monster-role=item].disabled{cursor:not-allowed;opacity:.6}[data-monster-role=checkbox]{accent-color:var(--monster-color-secondary-1);margin-top:.2rem}[data-monster-role=label]{display:inline-block;min-width:0;word-break:break-word}
28
+ }`, 0);
29
+ } catch (e) {
30
+ addAttributeToken(document.getRootNode().querySelector('html'), ATTRIBUTE_ERRORMESSAGE, e + "");
31
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Copyright © Volker Schukai and all contributing authors, 2026. 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 Volker Schukai.
11
+ */
12
+
13
+ import { addAttributeToken } from "../../../dom/attributes.mjs";
14
+ import { ATTRIBUTE_ERRORMESSAGE } from "../../../dom/constants.mjs";
15
+
16
+ export { ThreadMessageStyleSheet };
17
+
18
+ /**
19
+ * @private
20
+ * @type {CSSStyleSheet}
21
+ */
22
+ const ThreadMessageStyleSheet = new CSSStyleSheet();
23
+
24
+ try {
25
+ ThreadMessageStyleSheet.insertRule(
26
+ `
27
+ @layer thread-message {
28
+ :host{box-sizing:border-box;color:var(--monster-color-primary-1);display:block;font-family:var(--monster-font-family);font-size:1rem;font-weight:400;line-height:1.4}*,*::before,*::after{box-sizing:border-box}[data-monster-role=content]{display:block}a,a:active,a:focus,a:hover,a:link,a:visited{color:var(--monster-color-secondary-1);text-decoration:none;transition:color .3s ease-in-out,text-decoration-color .3s ease-in-out}a:active,a:focus,a:hover{color:var(--monster-color-primary-2);text-decoration:underline;text-decoration-color:var(--monster-color-secondary-1);text-decoration-thickness:1px}a:focus{outline:1px dashed var(--monster-color-selection-1);outline-offset:2px}@media (prefers-color-scheme: dark){a,a:active,a:focus,a:hover,a:link,a:visited{color:var(--monster-color-amber-2)}a:focus{outline:1px dashed var(--monster-color-selection-4)}}p{font-size:1rem;font-weight:400;line-height:1.6;margin:0 0 .65rem;text-align:justify}p:last-child{margin-bottom:0}.monster-paragraph{font-size:1rem;font-weight:400;line-height:1.6;text-align:justify}code{font-family:var(--monster-font-family-monospace)}h1{font-size:3rem;font-weight:400;line-height:1.15;margin:4rem 0 1.5rem}h2{font-size:2.5rem;font-weight:400;line-height:1.2;margin:4rem 0 1.5rem}h3{font-size:2rem;font-weight:400;line-height:1.25;margin:4rem 0 1.25rem}h4{font-size:1.5rem;font-weight:400;line-height:1.3;margin:4rem 0 1.25rem}h5{font-size:1.4rem;font-weight:bolder;line-height:1.3;margin:4rem 0 1.25rem}h6{font-size:1.3rem;font-weight:700;line-height:1.3;margin:4rem 0 1.25rem}p+h1{margin-top:3.75rem}p+h2{margin-top:3rem}p+h3{margin-top:2.25rem}p+h4{margin-top:1.5rem}p+h5{margin-top:.75rem}p+h6{margin-top:0}blockquote+h1,ol+h1,p+h1,pre+h1,table+h1,ul+h1{margin-top:2rem}article+h1,div+h1,section+h1{margin-top:0}blockquote+h2,ol+h2,p+h2,pre+h2,table+h2,ul+h2{margin-top:2rem}article+h2,div+h2,section+h2{margin-top:0}blockquote+h3,ol+h3,p+h3,pre+h3,table+h3,ul+h3{margin-top:2rem}article+h3,div+h3,section+h3{margin-top:0}blockquote+h4,ol+h4,p+h4,pre+h4,table+h4,ul+h4{margin-top:2rem}article+h4,div+h4,section+h4{margin-top:0}blockquote+h5,ol+h5,p+h5,pre+h5,table+h5,ul+h5{margin-top:2rem}article+h5,div+h5,section+h5{margin-top:0}blockquote+h6,ol+h6,p+h6,pre+h6,table+h6,ul+h6{margin-top:2rem}article+h6,div+h6,section+h6{margin-top:0}ul,ol{margin:0 0 .65rem;padding-left:1.25rem}li{margin:0 0 .25rem}blockquote{margin:0 0 .65rem;padding:0 0 0 .75rem;border-left:2px solid var(--monster-bg-color-primary-3)}img{height:auto;max-width:100%}
29
+ }`,
30
+ 0,
31
+ );
32
+ } catch (e) {
33
+ addAttributeToken(
34
+ document.getRootNode().querySelector("html"),
35
+ ATTRIBUTE_ERRORMESSAGE,
36
+ e + "",
37
+ );
38
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Copyright © Volker Schukai 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 Volker Schukai.
11
+ *
12
+ * SPDX-License-Identifier: AGPL-3.0
13
+ */
14
+
15
+ import { instanceSymbol } from "../../../constants.mjs";
16
+ import {
17
+ CustomElement,
18
+ registerCustomElement,
19
+ } from "../../../dom/customelement.mjs";
20
+ import { ThreadMessageStyleSheet } from "../stylesheet/thread-message.mjs";
21
+
22
+ export { ThreadMessage };
23
+
24
+ /**
25
+ * The ThreadMessage component encapsulates a message in a shadow root
26
+ * to prevent list styling from the thread control from leaking in.
27
+ *
28
+ * @summary Isolated message content for thread entries.
29
+ */
30
+ class ThreadMessage extends CustomElement {
31
+ /**
32
+ * This method is called by the `instanceof` operator.
33
+ * @return {symbol}
34
+ */
35
+ static get [instanceSymbol]() {
36
+ return Symbol.for(
37
+ "@schukai/monster/components/state/thread-message@@instance",
38
+ );
39
+ }
40
+
41
+ /**
42
+ * @return {object}
43
+ */
44
+ get defaults() {
45
+ return Object.assign({}, super.defaults, {
46
+ content: "",
47
+ templates: {
48
+ main: getTemplate(),
49
+ },
50
+ });
51
+ }
52
+
53
+ /**
54
+ * @return {string}
55
+ */
56
+ static getTag() {
57
+ return "monster-thread-message";
58
+ }
59
+
60
+ /**
61
+ * @return {CSSStyleSheet[]}
62
+ */
63
+ static getCSSStyleSheet() {
64
+ return [ThreadMessageStyleSheet];
65
+ }
66
+ }
67
+
68
+ /**
69
+ * @private
70
+ * @return {string}
71
+ */
72
+ function getTemplate() {
73
+ // language=HTML
74
+ return `
75
+ <div data-monster-role="content" part="content"
76
+ data-monster-replace="path:content"></div>
77
+ `;
78
+ }
79
+
80
+ registerCustomElement(ThreadMessage);
@@ -20,6 +20,7 @@ import {
20
20
  } from "../../dom/customelement.mjs";
21
21
  import { ThreadStyleSheet } from "./stylesheet/thread.mjs";
22
22
  import { Entry } from "./thread/entry.mjs";
23
+ import "./thread/message.mjs";
23
24
  import { validateInstance, validateString } from "../../types/validate.mjs";
24
25
  import "./state.mjs";
25
26
  import { isArray } from "../../types/is.mjs";
@@ -983,9 +984,8 @@ function getTemplate() {
983
984
  data-monster-replace="path:entry.date | time-ago"
984
985
  data-monster-attributes="title path:entry.date | datetime"></span>
985
986
  </div>
986
- <div data-monster-role="message"
987
- data-monster-replace="path:entry.message"
988
- data-monster-attributes="class path:entry.message | ?:message:hidden"></div>
987
+ <monster-thread-message data-monster-role="message"
988
+ data-monster-attributes="data-monster-option-content path:entry.message | default: , class path:entry.message | ?:message:hidden"></monster-thread-message>
989
989
  <div data-monster-role="thread-controls">
990
990
  <button type="button"
991
991
  class="monster-button-outline-secondary"
@@ -76,6 +76,7 @@ export * from "./components/form/shadow-reload.mjs";
76
76
  export * from "./components/form/button.mjs";
77
77
  export * from "./components/form/field-set.mjs";
78
78
  export * from "./components/form/toggle-switch.mjs";
79
+ export * from "./components/form/checklist.mjs";
79
80
  export * from "./components/form/types/state.mjs";
80
81
  export * from "./components/form/template.mjs";
81
82
  export * from "./components/form/constants.mjs";