@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 +16 -0
- package/package.json +1 -1
- package/source/components/form/checklist.mjs +361 -0
- package/source/components/form/message-state-button.mjs +112 -0
- package/source/components/form/style/checklist.pcss +49 -0
- package/source/components/form/stylesheet/checklist.mjs +31 -0
- package/source/components/state/stylesheet/thread-message.mjs +38 -0
- package/source/components/state/thread/message.mjs +80 -0
- package/source/components/state/thread.mjs +3 -3
- package/source/monster.mjs +1 -0
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.
|
|
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
|
-
<
|
|
987
|
-
data-monster-
|
|
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"
|
package/source/monster.mjs
CHANGED
|
@@ -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";
|