@schukai/monster 4.56.0 → 4.58.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 +23 -0
- package/package.json +1 -1
- package/source/components/data/stylesheet/metric-graph.mjs +1 -1
- package/source/components/data/stylesheet/metric.mjs +1 -1
- package/source/components/datatable/dataset.mjs +10 -0
- package/source/components/datatable/datasource/rest.mjs +141 -14
- package/source/components/datatable/datasource.mjs +8 -1
- package/source/components/datatable/datatable.mjs +3 -7
- package/source/components/datatable/save-button.mjs +348 -334
- package/source/components/datatable/status.mjs +7 -0
- package/source/components/datatable/util.mjs +7 -0
- package/source/components/form/button-bar.mjs +193 -95
- package/source/components/form/field-set.mjs +283 -283
- package/source/components/form/form.mjs +407 -162
- package/source/components/form/login.mjs +1571 -1571
- package/source/components/form/quantity.mjs +233 -233
- package/source/components/form/select.mjs +3106 -3101
- package/source/components/form/style/field-set.pcss +6 -2
- package/source/components/form/style/form.pcss +8 -0
- package/source/components/form/stylesheet/field-set.mjs +1 -1
- package/source/components/form/stylesheet/form.mjs +1 -1
- package/source/components/form/stylesheet/select.mjs +13 -6
- package/source/components/style/typography.css +2 -2
- package/source/components/tree-menu/stylesheet/tree-menu.mjs +1 -1
- package/source/constraints/abstract.mjs +17 -17
- package/source/dom/customelement.mjs +962 -963
- package/source/dom/updater.mjs +874 -863
- package/source/dom/util/init-options-from-attributes.mjs +56 -56
- package/source/monster.mjs +0 -1
- package/source/net/webconnect.mjs +325 -325
- package/source/types/is.mjs +66 -66
|
@@ -14,8 +14,8 @@ import { instanceSymbol } from "../../constants.mjs";
|
|
|
14
14
|
import { ATTRIBUTE_ROLE } from "../../dom/constants.mjs";
|
|
15
15
|
import { CustomControl } from "../../dom/customcontrol.mjs";
|
|
16
16
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
assembleMethodSymbol,
|
|
18
|
+
registerCustomElement,
|
|
19
19
|
} from "../../dom/customelement.mjs";
|
|
20
20
|
import { fireCustomEvent } from "../../dom/events.mjs";
|
|
21
21
|
|
|
@@ -61,105 +61,105 @@ const holdIntervalSymbol = Symbol("holdInterval");
|
|
|
61
61
|
* @fires monster-quantity-change
|
|
62
62
|
*/
|
|
63
63
|
class Quantity extends CustomControl {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
64
|
+
static get [instanceSymbol]() {
|
|
65
|
+
return Symbol.for("@schukai/monster/components/form/quantity@@instance");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
[assembleMethodSymbol]() {
|
|
69
|
+
super[assembleMethodSymbol]();
|
|
70
|
+
|
|
71
|
+
initControlReferences.call(this);
|
|
72
|
+
initEventHandler.call(this);
|
|
73
|
+
applyEditableState.call(this);
|
|
74
|
+
clampAndRender.call(this, this.getOption("value"));
|
|
75
|
+
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Current numeric value
|
|
81
|
+
* @return {number|null}
|
|
82
|
+
*/
|
|
83
|
+
get value() {
|
|
84
|
+
return this.getOption("value");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Sets the value programmatically (including clamping & FormValue)
|
|
89
|
+
* @param {number|string|null} v
|
|
90
|
+
*/
|
|
91
|
+
set value(v) {
|
|
92
|
+
const n = normalizeNumber(v, this.getOption("precision"));
|
|
93
|
+
clampAndRender.call(this, n);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Options
|
|
98
|
+
*
|
|
99
|
+
* @property {Object} templates
|
|
100
|
+
* @property {string} templates.main Main template
|
|
101
|
+
* @property {Object} templateMapping
|
|
102
|
+
* @property {string} templateMapping.plus Icon (SVG-Path) Plus
|
|
103
|
+
* @property {string} templateMapping.minus Icon (SVG-Path) Minus
|
|
104
|
+
* @property {Object} classes CSS classes
|
|
105
|
+
* @property {string} classes.button Button class (e.g. monster-button-outline-primary)
|
|
106
|
+
* @property {string} classes.input Additional class for input
|
|
107
|
+
* @property {Object} features Feature toggles
|
|
108
|
+
* @property {boolean} features.editable Allow manual input
|
|
109
|
+
* @property {boolean} features.hold Press-and-hold accelerates
|
|
110
|
+
* @property {boolean} features.enforceBounds Clamp value when manual input is out of bounds
|
|
111
|
+
* @property {number} value Current value
|
|
112
|
+
* @property {number} min Use Number.NEGATIVE_INFINITY and Number.POSITIVE_INFINITY for no bounds
|
|
113
|
+
* @property {number} max Use Number.NEGATIVE_INFINITY and Number.POSITIVE_INFINITY for no bounds
|
|
114
|
+
* @property {number} step Increment/decrement step
|
|
115
|
+
* @property {number} precision Round to N decimal places (null = no explicit rounding)
|
|
116
|
+
* @property {boolean} disabled Disable the input field (also disables manual input)
|
|
117
|
+
* @property {string} placeholder Placeholder text
|
|
118
|
+
* @property {string} inputmode For mobile keyboards
|
|
119
|
+
*/
|
|
120
|
+
get defaults() {
|
|
121
|
+
return Object.assign({}, super.defaults, {
|
|
122
|
+
templates: { main: getTemplate() },
|
|
123
|
+
templateMapping: {
|
|
124
|
+
plus: `
|
|
125
125
|
<path d="M8 1a1 1 0 0 1 1 1v5h5a1 1 0 1 1 0 2H9v5a1 1 0 1 1-2 0V9H2a1 1 0 1 1 0-2h5V2a1 1 0 0 1 1-1z"/>`,
|
|
126
|
-
|
|
126
|
+
minus: `
|
|
127
127
|
<path d="M2 7.5a1 1 0 0 0 0 1H14a1 1 0 1 0 0-2H2a1 1 0 0 0 0 1z"/>`,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
128
|
+
},
|
|
129
|
+
classes: {
|
|
130
|
+
button: "monster-button-outline-primary",
|
|
131
|
+
input: "",
|
|
132
|
+
},
|
|
133
|
+
features: {
|
|
134
|
+
editable: true,
|
|
135
|
+
hold: true,
|
|
136
|
+
enforceBounds: true,
|
|
137
|
+
},
|
|
138
|
+
value: 0,
|
|
139
|
+
min: 0,
|
|
140
|
+
max: Number.POSITIVE_INFINITY,
|
|
141
|
+
step: 1,
|
|
142
|
+
precision: null,
|
|
143
|
+
|
|
144
|
+
disabled: false,
|
|
145
|
+
placeholder: "",
|
|
146
|
+
inputmode: "decimal",
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static getTag() {
|
|
151
|
+
return "monster-quantity";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// If you want a stylesheet, return it here.
|
|
155
|
+
static getCSSStyleSheet() {
|
|
156
|
+
return [QuantityStyleSheet];
|
|
157
|
+
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
function getFiniteNumberOr(optionValue, fallback) {
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
const n = Number(optionValue);
|
|
162
|
+
return Number.isFinite(n) ? n : fallback;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
/**
|
|
@@ -169,18 +169,18 @@ function getFiniteNumberOr(optionValue, fallback) {
|
|
|
169
169
|
* @return {void}
|
|
170
170
|
*/
|
|
171
171
|
function initControlReferences() {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
172
|
+
this[controlElementSymbol] = this.shadowRoot.querySelector(
|
|
173
|
+
`[${ATTRIBUTE_ROLE}="control"]`,
|
|
174
|
+
);
|
|
175
|
+
this[decrementButtonSymbol] = this.shadowRoot.querySelector(
|
|
176
|
+
`[${ATTRIBUTE_ROLE}="decrement"]`,
|
|
177
|
+
);
|
|
178
|
+
this[incrementButtonSymbol] = this.shadowRoot.querySelector(
|
|
179
|
+
`[${ATTRIBUTE_ROLE}="increment"]`,
|
|
180
|
+
);
|
|
181
|
+
this[inputElementSymbol] = this.shadowRoot.querySelector(
|
|
182
|
+
`[${ATTRIBUTE_ROLE}="input"]`,
|
|
183
|
+
);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
/**
|
|
@@ -190,152 +190,152 @@ function initControlReferences() {
|
|
|
190
190
|
* @return {void}
|
|
191
191
|
*/
|
|
192
192
|
function initEventHandler() {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
193
|
+
const stepOnce = (dir) => {
|
|
194
|
+
const step = Number(this.getOption("step")) || 1;
|
|
195
|
+
const cur = toNumberOr(this.value, 0);
|
|
196
|
+
const next = cur + (dir > 0 ? step : -step);
|
|
197
|
+
clampAndRender.call(this, next, {
|
|
198
|
+
fire: true,
|
|
199
|
+
kind: dir > 0 ? "increment" : "decrement",
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const startHold = (dir) => {
|
|
204
|
+
if (!this.getOption("features.hold")) return;
|
|
205
|
+
clearTimeout(this[holdTimerSymbol]);
|
|
206
|
+
clearInterval(this[holdIntervalSymbol]);
|
|
207
|
+
|
|
208
|
+
// After a short delay, repeat faster
|
|
209
|
+
this[holdTimerSymbol] = setTimeout(() => {
|
|
210
|
+
this[holdIntervalSymbol] = setInterval(() => stepOnce(dir), 60);
|
|
211
|
+
}, 300);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const stopHold = () => {
|
|
215
|
+
clearTimeout(this[holdTimerSymbol]);
|
|
216
|
+
clearInterval(this[holdIntervalSymbol]);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Buttons
|
|
220
|
+
this[decrementButtonSymbol].addEventListener("click", (e) => stepOnce(-1));
|
|
221
|
+
this[incrementButtonSymbol].addEventListener("click", (e) => stepOnce(1));
|
|
222
|
+
|
|
223
|
+
// Press & hold (Mouse/Touch)
|
|
224
|
+
["mousedown", "pointerdown", "touchstart"].forEach((ev) => {
|
|
225
|
+
this[decrementButtonSymbol].addEventListener(ev, () => startHold(-1));
|
|
226
|
+
this[incrementButtonSymbol].addEventListener(ev, () => startHold(1));
|
|
227
|
+
});
|
|
228
|
+
["mouseup", "mouseleave", "pointerup", "touchend", "touchcancel"].forEach(
|
|
229
|
+
(ev) => {
|
|
230
|
+
this[decrementButtonSymbol].addEventListener(ev, stopHold);
|
|
231
|
+
this[incrementButtonSymbol].addEventListener(ev, stopHold);
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
// Keyboard on input
|
|
236
|
+
this[inputElementSymbol].addEventListener("keydown", (e) => {
|
|
237
|
+
if (e.key === "ArrowUp") {
|
|
238
|
+
e.preventDefault();
|
|
239
|
+
stepOnce(1);
|
|
240
|
+
} else if (e.key === "ArrowDown") {
|
|
241
|
+
e.preventDefault();
|
|
242
|
+
stepOnce(-1);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Manual input
|
|
247
|
+
this[inputElementSymbol].addEventListener("input", () => {
|
|
248
|
+
if (!this.getOption("features.editable")) return;
|
|
249
|
+
// Only store temporarily, clamp on blur/enter – but update FormValue immediately
|
|
250
|
+
const raw = this[inputElementSymbol].value;
|
|
251
|
+
const n = normalizeNumber(raw, this.getOption("precision"));
|
|
252
|
+
this.setOption("value", n);
|
|
253
|
+
this.setFormValue(n);
|
|
254
|
+
fireChanged.call(this, "input");
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
this[inputElementSymbol].addEventListener("blur", () => {
|
|
258
|
+
if (!this.getOption("features.editable")) return;
|
|
259
|
+
const n = normalizeNumber(
|
|
260
|
+
this[inputElementSymbol].value,
|
|
261
|
+
this.getOption("precision"),
|
|
262
|
+
);
|
|
263
|
+
clampAndRender.call(this, n, { fire: true, kind: "blur" });
|
|
264
|
+
});
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
function applyEditableState() {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
268
|
+
const editable = !!this.getOption("features.editable");
|
|
269
|
+
this[inputElementSymbol].toggleAttribute("readonly", !editable);
|
|
270
|
+
this[inputElementSymbol].toggleAttribute(
|
|
271
|
+
"disabled",
|
|
272
|
+
!!this.getOption("disabled"),
|
|
273
|
+
);
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
function clampAndRender(n, opts = {}) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
277
|
+
const min = getFiniteNumberOr(
|
|
278
|
+
this.getOption("min"),
|
|
279
|
+
Number.NEGATIVE_INFINITY,
|
|
280
|
+
);
|
|
281
|
+
const max = getFiniteNumberOr(
|
|
282
|
+
this.getOption("max"),
|
|
283
|
+
Number.POSITIVE_INFINITY,
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
let value = n;
|
|
287
|
+
if (this.getOption("features.enforceBounds")) {
|
|
288
|
+
value = Math.min(max, Math.max(min, toNumberOr(n, 0)));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Precision
|
|
292
|
+
const p = this.getOption("precision");
|
|
293
|
+
if (Number.isInteger(p) && p >= 0) {
|
|
294
|
+
value = Number(toFixedSafe(value, p));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Render into input
|
|
298
|
+
this[inputElementSymbol].value =
|
|
299
|
+
value === null || Number.isNaN(value) ? "" : String(value);
|
|
300
|
+
|
|
301
|
+
// Options + FormValue
|
|
302
|
+
this.setOption("value", value);
|
|
303
|
+
this.setFormValue(value);
|
|
304
|
+
|
|
305
|
+
if (opts.fire) fireChanged.call(this, opts.kind || "programmatic");
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
function fireChanged(kind) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
309
|
+
fireCustomEvent(this, "monster-quantity-change", {
|
|
310
|
+
element: this,
|
|
311
|
+
value: this.value,
|
|
312
|
+
kind, // 'increment' | 'decrement' | 'input' | 'blur' | 'programmatic'
|
|
313
|
+
});
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
function normalizeNumber(v, precision) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
317
|
+
if (v === null || v === undefined || v === "") return null;
|
|
318
|
+
let n = Number(v);
|
|
319
|
+
if (!Number.isFinite(n)) return null;
|
|
320
|
+
if (Number.isInteger(precision) && precision >= 0) {
|
|
321
|
+
n = Number(toFixedSafe(n, precision));
|
|
322
|
+
}
|
|
323
|
+
return n;
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
function toNumberOr(v, dflt) {
|
|
327
|
-
|
|
328
|
-
|
|
327
|
+
const n = Number(v);
|
|
328
|
+
return Number.isFinite(n) ? n : dflt;
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
function toFixedSafe(n, p) {
|
|
332
|
-
|
|
333
|
-
|
|
332
|
+
// Prevents 1.00000000000002 effects
|
|
333
|
+
return (Math.round(n * Math.pow(10, p)) / Math.pow(10, p)).toFixed(p);
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
function getTemplate() {
|
|
337
|
-
|
|
338
|
-
|
|
337
|
+
// language=HTML
|
|
338
|
+
return `
|
|
339
339
|
<div data-monster-role="control" part="control">
|
|
340
340
|
<monster-input-group part="input-group">
|
|
341
341
|
<button type="button"
|