@schukai/monster 4.154.3 → 4.154.5
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/content/image-editor.mjs +19 -0
- package/source/components/data/metric-graph.mjs +10 -6
- package/source/components/data/metric.mjs +9 -7
- package/source/dom/updater.mjs +2 -2
- package/test/cases/components/content/image-editor.mjs +27 -0
- package/test/cases/components/data/metric-graph.mjs +40 -0
- package/test/cases/components/data/metric.mjs +41 -0
- package/test/cases/dom/updater.mjs +26 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
## [4.154.5] - 2026-10-04
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- **image-editor:** load raw SVG markup ([#589](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/589))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## [4.154.4] - 2026-10-04
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
- **data:** render and document metric values ([#587](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/587), [#588](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/588))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
5
21
|
## [4.154.3] - 2026-10-02
|
|
6
22
|
|
|
7
23
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","exports":{".":"./source/monster.mjs","./CHANGELOG.md":"./CHANGELOG.md","./LICENSE":"./LICENSE","./README.md":"./README.md","./package.json":"./package.json","./source/*":"./source/*","./test/*":"./test/*"},"files":["source","test/cases","test/util","CHANGELOG.md"],"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"},"sideEffects":["./source/monster.mjs","./source/components/**","./source/i18n/**","./test/**"],"type":"module","version":"4.154.
|
|
1
|
+
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","exports":{".":"./source/monster.mjs","./CHANGELOG.md":"./CHANGELOG.md","./LICENSE":"./LICENSE","./README.md":"./README.md","./package.json":"./package.json","./source/*":"./source/*","./test/*":"./test/*"},"files":["source","test/cases","test/util","CHANGELOG.md"],"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"},"sideEffects":["./source/monster.mjs","./source/components/**","./source/i18n/**","./test/**"],"type":"module","version":"4.154.5"}
|
|
@@ -1181,6 +1181,25 @@ function setImageFromData(data, options) {
|
|
|
1181
1181
|
|
|
1182
1182
|
if (isString(data)) {
|
|
1183
1183
|
const trimmed = data.trim();
|
|
1184
|
+
const normalizedContentType = contentType
|
|
1185
|
+
.split(";", 1)[0]
|
|
1186
|
+
.trim()
|
|
1187
|
+
.toLowerCase();
|
|
1188
|
+
const svgMarkup = trimmed.replace(/^<\?xml[\s\S]*?\?>\s*/iu, "");
|
|
1189
|
+
if (
|
|
1190
|
+
normalizedContentType === "image/svg+xml" &&
|
|
1191
|
+
/^<svg(?:\s|>)/iu.test(svgMarkup)
|
|
1192
|
+
) {
|
|
1193
|
+
return loadImageFromSource.call(
|
|
1194
|
+
this,
|
|
1195
|
+
`data:image/svg+xml;charset=utf-8,${encodeURIComponent(trimmed)}`,
|
|
1196
|
+
{
|
|
1197
|
+
storeOriginal,
|
|
1198
|
+
resetAspect: storeOriginal,
|
|
1199
|
+
resetView: storeOriginal,
|
|
1200
|
+
},
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1184
1203
|
if (trimmed.startsWith("data:") || trimmed.startsWith("blob:")) {
|
|
1185
1204
|
return loadImageFromSource.call(this, trimmed, {
|
|
1186
1205
|
storeOriginal,
|
|
@@ -77,17 +77,20 @@ class MetricGraph extends CustomElement {
|
|
|
77
77
|
*
|
|
78
78
|
* @property {Object} templates Template definitions
|
|
79
79
|
* @property {string} templates.main Main template
|
|
80
|
-
* @property {string}
|
|
80
|
+
* @property {string} graphType The graph renderer (step, smooth, bubble, bar, area, dot, lollipop, line)
|
|
81
81
|
* @property {Object} values Value definitions
|
|
82
|
-
* @property {number} values.
|
|
82
|
+
* @property {string|number|null} values.main The primary value of the metric
|
|
83
83
|
* @property {number} values.change The change of the metric
|
|
84
|
-
* @property {number} values.secondary The secondary value of the metric
|
|
85
|
-
* @property {Array} values.points The points of the metric
|
|
84
|
+
* @property {string|number|null} values.secondary The secondary value of the metric
|
|
85
|
+
* @property {Array<number>|string} values.points The points of the metric
|
|
86
86
|
* @property {Object} labels Label definitions
|
|
87
|
-
* @property {string} labels.title Title of the metric
|
|
88
|
-
* @property {string} labels.subtext Subtext of the metric
|
|
87
|
+
* @property {string|null} labels.title Title of the metric
|
|
88
|
+
* @property {string|null} labels.subtext Subtext of the metric
|
|
89
89
|
* @property {Object} classes CSS classes
|
|
90
90
|
* @property {string} classes.dot CSS class for the dot
|
|
91
|
+
* @property {Object} aria Accessible descriptions
|
|
92
|
+
* @property {string|null} aria.description Description of the complete metric card
|
|
93
|
+
* @property {string|null} aria.graph Text alternative for the compact graph
|
|
91
94
|
*/
|
|
92
95
|
get defaults() {
|
|
93
96
|
return Object.assign({}, super.defaults, {
|
|
@@ -113,6 +116,7 @@ class MetricGraph extends CustomElement {
|
|
|
113
116
|
|
|
114
117
|
aria: {
|
|
115
118
|
description: null,
|
|
119
|
+
graph: null,
|
|
116
120
|
},
|
|
117
121
|
});
|
|
118
122
|
}
|
|
@@ -74,16 +74,18 @@ class Metric extends CustomElement {
|
|
|
74
74
|
* @property {Object} templates Template definitions
|
|
75
75
|
* @property {string} templates.main Main template
|
|
76
76
|
* @property {Object} values Value definitions
|
|
77
|
-
* @property {number} values.
|
|
78
|
-
* @property {number} values.change The change of the metric
|
|
79
|
-
* @property {number} values.direction
|
|
80
|
-
* @property {number} values.secondary The secondary value of the metric
|
|
77
|
+
* @property {string|number|null} values.main The primary value of the metric
|
|
78
|
+
* @property {string|number|null} values.change The displayed change of the metric
|
|
79
|
+
* @property {number} values.direction Rotation of the direction indicator in degrees
|
|
80
|
+
* @property {string|number|null} values.secondary The secondary value of the metric
|
|
81
81
|
* @property {Object} labels Label definitions
|
|
82
|
-
* @property {string} labels.title Title of the metric
|
|
83
|
-
* @property {string} labels.subtext Subtext of the metric
|
|
82
|
+
* @property {string|null} labels.title Title of the metric
|
|
83
|
+
* @property {string|null} labels.subtext Subtext of the metric
|
|
84
84
|
* @property {Object} classes CSS classes
|
|
85
85
|
* @property {string} classes.dot CSS class for the dot
|
|
86
|
-
* @property {string} classes.
|
|
86
|
+
* @property {string} classes.change CSS class for the metric change (positive/negative)
|
|
87
|
+
* @property {Object} aria Accessible descriptions
|
|
88
|
+
* @property {string|null} aria.description Description of the complete metric card
|
|
87
89
|
*/
|
|
88
90
|
get defaults() {
|
|
89
91
|
return Object.assign({}, super.defaults, {
|
package/source/dom/updater.mjs
CHANGED
|
@@ -1372,11 +1372,11 @@ function runUpdateContent(
|
|
|
1372
1372
|
}
|
|
1373
1373
|
|
|
1374
1374
|
function applyReplacementValue(element, value) {
|
|
1375
|
-
if (!(element instanceof
|
|
1375
|
+
if (!(element instanceof Element)) {
|
|
1376
1376
|
return false;
|
|
1377
1377
|
}
|
|
1378
1378
|
|
|
1379
|
-
if (value instanceof
|
|
1379
|
+
if (value instanceof Element) {
|
|
1380
1380
|
if (element.childNodes.length === 1 && element.firstChild === value) {
|
|
1381
1381
|
return false;
|
|
1382
1382
|
}
|
|
@@ -109,4 +109,31 @@ describe('ImageEditor', function () {
|
|
|
109
109
|
'<monster-button-bar class="grid-span-full monster-margin-top-2">',
|
|
110
110
|
);
|
|
111
111
|
});
|
|
112
|
+
|
|
113
|
+
it('loads raw SVG markup with its documented content type issue #589', async function () {
|
|
114
|
+
const OriginalImage = globalThis.Image;
|
|
115
|
+
const sources = [];
|
|
116
|
+
|
|
117
|
+
class ImageMock {
|
|
118
|
+
set src(value) {
|
|
119
|
+
sources.push(value);
|
|
120
|
+
queueMicrotask(() => this.onerror?.(new Error('test stop')));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
globalThis.Image = ImageMock;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const element = document.createElement('monster-image-editor');
|
|
128
|
+
const markup = '<svg xmlns="http://www.w3.org/2000/svg"><rect width="4" height="3"/></svg>';
|
|
129
|
+
|
|
130
|
+
await element.setImage(markup, {contentType: 'image/svg+xml'}).catch(() => {});
|
|
131
|
+
|
|
132
|
+
expect(sources).to.have.length(1);
|
|
133
|
+
expect(sources[0]).to.match(/^data:image\/svg\+xml;charset=utf-8,/);
|
|
134
|
+
expect(decodeURIComponent(sources[0].split(',', 2)[1])).to.equal(markup);
|
|
135
|
+
} finally {
|
|
136
|
+
globalThis.Image = OriginalImage;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
112
139
|
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {expect} from "chai";
|
|
2
|
+
import {initJSDOM} from "../../../util/jsdom.mjs";
|
|
3
|
+
|
|
4
|
+
describe("MetricGraph", () => {
|
|
5
|
+
let MetricGraph;
|
|
6
|
+
|
|
7
|
+
before(async () => {
|
|
8
|
+
await initJSDOM();
|
|
9
|
+
({MetricGraph} = await import(
|
|
10
|
+
"../../../../source/components/data/metric-graph.mjs"
|
|
11
|
+
));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => document.getElementById("mocks").replaceChildren());
|
|
15
|
+
|
|
16
|
+
it("initializes its documented graph options from attributes", async () => {
|
|
17
|
+
const mocks = document.getElementById("mocks");
|
|
18
|
+
mocks.innerHTML = `
|
|
19
|
+
<monster-metric-graph
|
|
20
|
+
data-monster-option-labels-title="Weekly requests"
|
|
21
|
+
data-monster-option-values-main="840 requests"
|
|
22
|
+
data-monster-option-values-points="62::75::68"
|
|
23
|
+
data-monster-option-graph-type="bar"
|
|
24
|
+
data-monster-option-aria-graph="Three daily request totals">
|
|
25
|
+
</monster-metric-graph>
|
|
26
|
+
`;
|
|
27
|
+
const graph = mocks.firstElementChild;
|
|
28
|
+
|
|
29
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
30
|
+
|
|
31
|
+
expect(graph).to.be.instanceOf(MetricGraph);
|
|
32
|
+
expect(graph.getOption("graphType")).to.equal("bar");
|
|
33
|
+
expect(graph.getOption("values.main")).to.equal("840 requests");
|
|
34
|
+
expect(graph.getOption("values.points")).to.deep.equal(["62", "75", "68"]);
|
|
35
|
+
expect(graph.getOption("aria.graph")).to.equal("Three daily request totals");
|
|
36
|
+
expect(graph.shadowRoot.querySelectorAll("svg rect")).to.have.length(3);
|
|
37
|
+
expect(graph.shadowRoot.getElementById("metric-graph-desc").textContent.trim())
|
|
38
|
+
.to.equal("Three daily request totals");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {expect} from "chai";
|
|
2
|
+
import {initJSDOM} from "../../../util/jsdom.mjs";
|
|
3
|
+
|
|
4
|
+
describe("Metric", () => {
|
|
5
|
+
let Metric;
|
|
6
|
+
|
|
7
|
+
before(async () => {
|
|
8
|
+
await initJSDOM();
|
|
9
|
+
({Metric} = await import("../../../../source/components/data/metric.mjs"));
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(() => document.getElementById("mocks").replaceChildren());
|
|
13
|
+
|
|
14
|
+
it("initializes its documented metric options from attributes", async () => {
|
|
15
|
+
const mocks = document.getElementById("mocks");
|
|
16
|
+
mocks.innerHTML = `
|
|
17
|
+
<monster-metric
|
|
18
|
+
data-monster-option-labels-title="Active subscriptions"
|
|
19
|
+
data-monster-option-labels-subtext="Previous hour"
|
|
20
|
+
data-monster-option-values-main="1,248"
|
|
21
|
+
data-monster-option-values-secondary="1,204"
|
|
22
|
+
data-monster-option-values-change="+3.7%"
|
|
23
|
+
data-monster-option-values-direction="225"
|
|
24
|
+
data-monster-option-classes-change="positive"
|
|
25
|
+
data-monster-option-aria-description="Subscriptions increased">
|
|
26
|
+
</monster-metric>
|
|
27
|
+
`;
|
|
28
|
+
const metric = mocks.firstElementChild;
|
|
29
|
+
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
31
|
+
|
|
32
|
+
expect(metric).to.be.instanceOf(Metric);
|
|
33
|
+
expect(metric.getOption("values.main")).to.equal("1,248");
|
|
34
|
+
expect(metric.getOption("values.change")).to.equal("+3.7%");
|
|
35
|
+
expect(metric.getOption("classes.change")).to.equal("positive");
|
|
36
|
+
expect(metric.shadowRoot.getElementById("metric-value").textContent).to.equal("1,248");
|
|
37
|
+
const change = metric.shadowRoot.getElementById("metric-change-text");
|
|
38
|
+
expect(change.classList.contains("positive")).to.equal(true);
|
|
39
|
+
expect(change.style.getPropertyValue("--arrow-direction")).to.equal("225deg");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -1388,6 +1388,32 @@ describe("DOM", function () {
|
|
|
1388
1388
|
});
|
|
1389
1389
|
});
|
|
1390
1390
|
|
|
1391
|
+
it("should replace SVG child markup issue #587", async function () {
|
|
1392
|
+
const mocks = document.getElementById("mocks");
|
|
1393
|
+
mocks.innerHTML = `
|
|
1394
|
+
<div id="svg-replace-root">
|
|
1395
|
+
<svg viewBox="0 0 100 30">
|
|
1396
|
+
<g id="svg-replace-target" data-monster-replace="path:markup"></g>
|
|
1397
|
+
</svg>
|
|
1398
|
+
</div>
|
|
1399
|
+
`;
|
|
1400
|
+
const updater = new Updater(
|
|
1401
|
+
document.getElementById("svg-replace-root"),
|
|
1402
|
+
{ markup: '<rect x="1" y="2" width="3" height="4"></rect>' },
|
|
1403
|
+
);
|
|
1404
|
+
|
|
1405
|
+
await updater.run();
|
|
1406
|
+
|
|
1407
|
+
const target = document.getElementById("svg-replace-target");
|
|
1408
|
+
expect(target.querySelector("rect")?.getAttribute("width")).to.equal("3");
|
|
1409
|
+
|
|
1410
|
+
updater.getSubject().markup = '<circle cx="5" cy="6" r="2"></circle>';
|
|
1411
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1412
|
+
|
|
1413
|
+
expect(target.querySelector("rect")).to.equal(null);
|
|
1414
|
+
expect(target.querySelector("circle")?.getAttribute("r")).to.equal("2");
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1391
1417
|
it("should dispose linked updaters when replacing a mounted stateful subtree", function (done) {
|
|
1392
1418
|
let mocks = document.getElementById("mocks");
|
|
1393
1419
|
mocks.innerHTML = htmlStatefulReplace;
|