@operato/property-editor 10.29.27 → 10.29.29
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 +17 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +146 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +1335 -0
- package/dist/src/ox-property-editor-number-map.d.ts +17 -2
- package/dist/src/ox-property-editor-number-map.js +76 -12
- package/dist/src/ox-property-editor-number-map.js.map +1 -1
- package/dist/test/number-map.test.js +42 -0
- package/dist/test/number-map.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/test/number-map.test.ts +47 -0
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { TemplateResult } from 'lit';
|
|
2
2
|
import { OxPropertyEditor } from './ox-property-editor.js';
|
|
3
3
|
import { PropertySpec } from './types.js';
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* One value of the map: its key, the name shown, and what it is held to. A number unless `kind` says otherwise: a
|
|
6
|
+
* boolean is a checkbox, an enum a choice among `values` (a figure's data properties, ADR-0051).
|
|
7
|
+
*/
|
|
5
8
|
export interface NumberMapEntry {
|
|
6
9
|
key: string;
|
|
7
10
|
label: string;
|
|
11
|
+
kind?: 'number' | 'boolean' | 'enum';
|
|
8
12
|
unit?: string;
|
|
9
13
|
min?: number;
|
|
10
14
|
max?: number;
|
|
11
|
-
|
|
15
|
+
/** For an enum: the values it may take. */
|
|
16
|
+
values?: string[];
|
|
17
|
+
default?: number | boolean | string;
|
|
12
18
|
}
|
|
13
19
|
/**
|
|
14
20
|
* Several numbers kept as one map ({ key: number }) -- a figure's design values and state values a board sets
|
|
@@ -19,10 +25,19 @@ export interface NumberMapEntry {
|
|
|
19
25
|
* The slider is taken at every step of a drag, so what it drives moves with the handle.
|
|
20
26
|
*
|
|
21
27
|
* Built by the Figure lane after the existing editors (chief architect, 2026-09-26); open for the UX lane's review.
|
|
28
|
+
*
|
|
29
|
+
* `property.nested`: a key `a.b` is the value `b` inside the map's `a` -- a figure's motion settings, one map per group
|
|
30
|
+
* (`{ locomotion: { clip, speed, paused } }`, ADR-0051 §3.2). The key splits at its first dot only.
|
|
22
31
|
*/
|
|
23
32
|
export declare class OxPropertyEditorNumberMap extends OxPropertyEditor {
|
|
24
33
|
static styles: import("lit").CSSResult[];
|
|
25
34
|
editorTemplate(value: any, spec: PropertySpec): TemplateResult;
|
|
35
|
+
/** A checkbox for a boolean, a choice for an enum: what is shown is what the map holds, or the entry's default. */
|
|
36
|
+
private choiceRow;
|
|
37
|
+
/** The value a key holds in the map: `a.b` inside `a` when the map is nested. */
|
|
38
|
+
private read;
|
|
39
|
+
/** The map with a key written: `a.b` into a copy of `a` when the map is nested. */
|
|
40
|
+
private write;
|
|
26
41
|
/** The slider's range, when the entry has one to slide over. */
|
|
27
42
|
private rangeOf;
|
|
28
43
|
/** How far along its range a value is, in percent, for the filled part of the track. */
|
|
@@ -12,17 +12,23 @@ import { OxPropertyEditor } from './ox-property-editor.js';
|
|
|
12
12
|
* The slider is taken at every step of a drag, so what it drives moves with the handle.
|
|
13
13
|
*
|
|
14
14
|
* Built by the Figure lane after the existing editors (chief architect, 2026-09-26); open for the UX lane's review.
|
|
15
|
+
*
|
|
16
|
+
* `property.nested`: a key `a.b` is the value `b` inside the map's `a` -- a figure's motion settings, one map per group
|
|
17
|
+
* (`{ locomotion: { clip, speed, paused } }`, ADR-0051 §3.2). The key splits at its first dot only.
|
|
15
18
|
*/
|
|
16
19
|
let OxPropertyEditorNumberMap = class OxPropertyEditorNumberMap extends OxPropertyEditor {
|
|
17
20
|
editorTemplate(value, spec) {
|
|
18
|
-
var _a, _b;
|
|
21
|
+
var _a, _b, _c;
|
|
19
22
|
const entries = (_b = (_a = spec.property) === null || _a === void 0 ? void 0 : _a.entries) !== null && _b !== void 0 ? _b : [];
|
|
20
|
-
const
|
|
23
|
+
const nested = !!((_c = spec.property) === null || _c === void 0 ? void 0 : _c.nested);
|
|
21
24
|
return html `
|
|
22
25
|
<div fullwidth rows>
|
|
23
26
|
${entries.map(entry => {
|
|
24
|
-
var _a
|
|
25
|
-
const
|
|
27
|
+
var _a;
|
|
28
|
+
const held = this.read(value, entry.key, nested);
|
|
29
|
+
if (entry.kind === 'boolean' || entry.kind === 'enum')
|
|
30
|
+
return this.choiceRow(entry, held !== null && held !== void 0 ? held : entry.default);
|
|
31
|
+
const shown = (held !== null && held !== void 0 ? held : entry.default);
|
|
26
32
|
const ranged = this.rangeOf(entry);
|
|
27
33
|
return html `
|
|
28
34
|
<span name title=${entry.label}>${entry.label}</span>
|
|
@@ -52,12 +58,48 @@ let OxPropertyEditorNumberMap = class OxPropertyEditorNumberMap extends OxProper
|
|
|
52
58
|
step="any"
|
|
53
59
|
?disabled=${this.disabled}
|
|
54
60
|
/>
|
|
55
|
-
<span unit>${(
|
|
61
|
+
<span unit>${(_a = entry.unit) !== null && _a !== void 0 ? _a : ''}</span>
|
|
56
62
|
`;
|
|
57
63
|
})}
|
|
58
64
|
</div>
|
|
59
65
|
`;
|
|
60
66
|
}
|
|
67
|
+
/** A checkbox for a boolean, a choice for an enum: what is shown is what the map holds, or the entry's default. */
|
|
68
|
+
choiceRow(entry, shown) {
|
|
69
|
+
var _a, _b;
|
|
70
|
+
return html `
|
|
71
|
+
<span name title=${entry.label}>${entry.label}</span>
|
|
72
|
+
${entry.kind === 'boolean'
|
|
73
|
+
? html `<input type="checkbox" data-key=${entry.key} aria-label=${entry.label} .checked=${shown === true} ?disabled=${this.disabled} />`
|
|
74
|
+
: html `<select data-key=${entry.key} aria-label=${entry.label} ?disabled=${this.disabled}>
|
|
75
|
+
${((_a = entry.values) !== null && _a !== void 0 ? _a : []).map(v => html `<option value=${v} ?selected=${v === shown}>${v}</option>`)}
|
|
76
|
+
</select>`}
|
|
77
|
+
<span unit>${(_b = entry.unit) !== null && _b !== void 0 ? _b : ''}</span>
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
/** The value a key holds in the map: `a.b` inside `a` when the map is nested. */
|
|
81
|
+
read(map, key, nested) {
|
|
82
|
+
if (!map || typeof map !== 'object')
|
|
83
|
+
return undefined;
|
|
84
|
+
const at = nested ? key.indexOf('.') : -1;
|
|
85
|
+
if (at < 0)
|
|
86
|
+
return map[key];
|
|
87
|
+
const inner = map[key.slice(0, at)];
|
|
88
|
+
return inner && typeof inner === 'object' ? inner[key.slice(at + 1)] : undefined;
|
|
89
|
+
}
|
|
90
|
+
/** The map with a key written: `a.b` into a copy of `a` when the map is nested. */
|
|
91
|
+
write(map, key, nested, value) {
|
|
92
|
+
const out = map && typeof map === 'object' ? { ...map } : {};
|
|
93
|
+
const at = nested ? key.indexOf('.') : -1;
|
|
94
|
+
if (at < 0)
|
|
95
|
+
out[key] = value;
|
|
96
|
+
else {
|
|
97
|
+
const head = key.slice(0, at);
|
|
98
|
+
const inner = out[head];
|
|
99
|
+
out[head] = { ...(inner && typeof inner === 'object' ? inner : {}), [key.slice(at + 1)]: value };
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
61
103
|
/** The slider's range, when the entry has one to slide over. */
|
|
62
104
|
rangeOf(entry) {
|
|
63
105
|
const { min, max } = entry;
|
|
@@ -79,27 +121,34 @@ let OxPropertyEditorNumberMap = class OxPropertyEditorNumberMap extends OxProper
|
|
|
79
121
|
this._valueChanged(e);
|
|
80
122
|
}
|
|
81
123
|
_valueChanged(e) {
|
|
82
|
-
var _a, _b, _c, _d;
|
|
124
|
+
var _a, _b, _c, _d, _e;
|
|
83
125
|
e.stopPropagation();
|
|
84
126
|
const input = e.target;
|
|
85
127
|
const key = input.getAttribute('data-key');
|
|
86
128
|
if (!key)
|
|
87
129
|
return;
|
|
88
130
|
const entry = ((_b = (_a = this.property) === null || _a === void 0 ? void 0 : _a.entries) !== null && _b !== void 0 ? _b : []).find(one => one.key === key);
|
|
89
|
-
const
|
|
131
|
+
const nested = !!((_c = this.property) === null || _c === void 0 ? void 0 : _c.nested);
|
|
132
|
+
if ((entry === null || entry === void 0 ? void 0 : entry.kind) === 'boolean' || (entry === null || entry === void 0 ? void 0 : entry.kind) === 'enum') {
|
|
133
|
+
const choice = entry.kind === 'boolean' ? input.checked : input.value;
|
|
134
|
+
this.value = this.write(this.value, key, nested, choice);
|
|
135
|
+
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
|
|
136
|
+
this.observe(this.value);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
90
139
|
const number = input.valueAsNumber;
|
|
91
140
|
const outside = !Number.isFinite(number) || ((entry === null || entry === void 0 ? void 0 : entry.min) !== undefined && number < entry.min) || ((entry === null || entry === void 0 ? void 0 : entry.max) !== undefined && number > entry.max);
|
|
92
141
|
if (outside) {
|
|
93
142
|
/* Not taken: the field shows what the map holds. */
|
|
94
|
-
input.value = String((
|
|
143
|
+
input.value = String((_e = (_d = this.read(this.value, key, nested)) !== null && _d !== void 0 ? _d : entry === null || entry === void 0 ? void 0 : entry.default) !== null && _e !== void 0 ? _e : '');
|
|
95
144
|
return;
|
|
96
145
|
}
|
|
97
|
-
|
|
98
|
-
this.value =
|
|
146
|
+
const taken = input.type === 'range' ? Math.round(number * 100) / 100 : number;
|
|
147
|
+
this.value = this.write(this.value, key, nested, taken);
|
|
99
148
|
this.renderRoot.querySelectorAll(`input[data-key="${key}"]`).forEach(one => {
|
|
100
|
-
one.value = String(
|
|
149
|
+
one.value = String(taken);
|
|
101
150
|
if (one.type === 'range')
|
|
102
|
-
one.style.setProperty('--fill', `${this.fillOf(
|
|
151
|
+
one.style.setProperty('--fill', `${this.fillOf(taken, { min: Number(one.min), max: Number(one.max) })}%`);
|
|
103
152
|
});
|
|
104
153
|
this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
|
|
105
154
|
this.observe(this.value);
|
|
@@ -229,6 +278,21 @@ OxPropertyEditorNumberMap.styles = [
|
|
|
229
278
|
grid-column: var(--property-editor-label-end, 9) / 22;
|
|
230
279
|
}
|
|
231
280
|
|
|
281
|
+
:host > div[rows] > select,
|
|
282
|
+
:host > div[rows] > input[type='checkbox'] {
|
|
283
|
+
grid-column: var(--property-editor-label-end, 9) / 22;
|
|
284
|
+
box-sizing: border-box;
|
|
285
|
+
min-width: 0;
|
|
286
|
+
height: 16px;
|
|
287
|
+
margin: 0;
|
|
288
|
+
font: 0.7rem/1 var(--theme-font, Roboto, sans-serif);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
:host > div[rows] > input[type='checkbox'] {
|
|
292
|
+
justify-self: start;
|
|
293
|
+
width: 14px;
|
|
294
|
+
}
|
|
295
|
+
|
|
232
296
|
:host > div[rows] > [unit] {
|
|
233
297
|
grid-column: 22 / -1;
|
|
234
298
|
overflow: hidden;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ox-property-editor-number-map.js","sourceRoot":"","sources":["../../src/ox-property-editor-number-map.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, nothing, TemplateResult } from 'lit'\nimport { customElement } from 'lit/decorators.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport { OxPropertyEditor } from './ox-property-editor.js'\nimport { PropertySpec } from './types.js'\n\n/** One number of the map: its key, the name shown, and what it is held to. */\nexport interface NumberMapEntry {\n key: string\n label: string\n unit?: string\n min?: number\n max?: number\n default?: number\n}\n\n/**\n * Several numbers kept as one map ({ key: number }) -- a figure's design values and state values a board sets\n * (ADR-0096), a shelf's spacings. Each entry is one compact row: its name, a slider when it has a range, the number\n * field, and its unit. A value outside its range is not taken: the field goes back to what it was. A change sends the\n * whole map, so the map is the one value the component holds.\n *\n * The slider is taken at every step of a drag, so what it drives moves with the handle.\n *\n * Built by the Figure lane after the existing editors (chief architect, 2026-09-26); open for the UX lane's review.\n */\n@customElement('ox-property-editor-number-map')\nexport class OxPropertyEditorNumberMap extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n css`\n /*\n * The rows take the editor's own 24 columns (subgrid), so a name sits in the label column, right-aligned like\n * every other label, and the fields start where every other field starts.\n */\n :host > div[rows] {\n display: grid;\n grid-template-columns: subgrid;\n grid-auto-rows: 16px;\n row-gap: 3px;\n align-items: center;\n }\n\n :host > div[rows] > [name] {\n grid-column: 1 / var(--property-editor-label-end, 9);\n text-align: right;\n font: var(--property-editor-label-font, var(--label-font));\n color: var(--property-editor-label-color, inherit);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* A thin track filled up to the value (--fill), and a small round thumb: quieter than the fields around it. */\n :host > div[rows] > input[type='range'] {\n grid-column: var(--property-editor-label-end, 9) / 18;\n -webkit-appearance: none;\n appearance: none;\n width: 100%;\n min-width: 0;\n height: 12px;\n margin: 0;\n padding: 0;\n border: none;\n background: transparent;\n cursor: pointer;\n }\n\n :host > div[rows] > input[type='range']::-webkit-slider-runnable-track {\n height: 2px;\n border-radius: 1px;\n background: linear-gradient(\n to right,\n var(--md-sys-color-primary) var(--fill, 0%),\n var(--md-sys-color-outline-variant) var(--fill, 0%)\n );\n }\n\n :host > div[rows] > input[type='range']::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 8px;\n height: 8px;\n margin-top: -3px;\n border-radius: 50%;\n background: var(--md-sys-color-primary);\n transition: transform 0.12s;\n }\n\n :host > div[rows] > input[type='range']:hover::-webkit-slider-thumb,\n :host > div[rows] > input[type='range']:focus-visible::-webkit-slider-thumb {\n transform: scale(1.4);\n }\n\n :host > div[rows] > input[type='range']::-moz-range-track {\n height: 2px;\n border-radius: 1px;\n background: var(--md-sys-color-outline-variant);\n }\n\n :host > div[rows] > input[type='range']::-moz-range-progress {\n height: 2px;\n border-radius: 1px;\n background: var(--md-sys-color-primary);\n }\n\n :host > div[rows] > input[type='range']::-moz-range-thumb {\n width: 8px;\n height: 8px;\n border: none;\n border-radius: 50%;\n background: var(--md-sys-color-primary);\n }\n\n :host > div[rows] > input[type='range']:disabled {\n cursor: default;\n opacity: 0.5;\n }\n\n :host > div[rows] > input[type='number'] {\n grid-column: 18 / 22;\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n height: 16px;\n min-height: 0;\n padding: 0 2px;\n font: 0.7rem/1 var(--theme-font, Roboto, sans-serif);\n /* The value reads as text; the field shows itself only when pointed at or typed in. */\n background: transparent;\n border-color: transparent;\n border-radius: 3px;\n }\n\n :host > div[rows] > input[type='number']:hover {\n border-color: var(--md-sys-color-outline-variant);\n }\n\n :host > div[rows] > input[type='number']:focus {\n border-color: var(--md-sys-color-primary);\n background: var(--property-editor-background, field);\n }\n\n :host > div[rows] > input[type='number']::-webkit-inner-spin-button {\n display: none;\n min-width: 0;\n }\n\n /* No range, no slider: the field takes the slider's room. */\n :host > div[rows] > input[type='number'][wide] {\n grid-column: var(--property-editor-label-end, 9) / 22;\n }\n\n :host > div[rows] > [unit] {\n grid-column: 22 / -1;\n overflow: hidden;\n font: 0.66rem/1 var(--theme-font, Roboto, sans-serif);\n color: var(--md-sys-color-outline);\n white-space: nowrap;\n }\n `\n ]\n\n editorTemplate(value: any, spec: PropertySpec): TemplateResult {\n const entries: NumberMapEntry[] = spec.property?.entries ?? []\n const map: Record<string, number> = value && typeof value === 'object' ? value : {}\n return html`\n <div fullwidth rows>\n ${entries.map(entry => {\n const shown = map[entry.key] ?? entry.default\n const ranged = this.rangeOf(entry)\n return html`\n <span name title=${entry.label}>${entry.label}</span>\n ${ranged\n ? html`<input\n type=\"range\"\n data-key=${entry.key}\n aria-label=${entry.label}\n min=${ranged.min}\n max=${ranged.max}\n step=${ranged.step}\n .value=${String(shown ?? ranged.min)}\n style=${`--fill: ${this.fillOf(shown ?? ranged.min, ranged)}%`}\n ?disabled=${this.disabled}\n @input=${this.onSlide}\n />`\n : nothing}\n <input\n type=\"number\"\n data-key=${entry.key}\n aria-label=${entry.label}\n ?wide=${!ranged}\n .value=${String(shown ?? '')}\n placeholder=${ifDefined(this.hintOf(entry))}\n min=${ifDefined(entry.min)}\n max=${ifDefined(entry.max)}\n step=\"any\"\n ?disabled=${this.disabled}\n />\n <span unit>${entry.unit ?? ''}</span>\n `\n })}\n </div>\n `\n }\n\n /** The slider's range, when the entry has one to slide over. */\n private rangeOf(entry: NumberMapEntry): { min: number; max: number; step: number } | undefined {\n const { min, max } = entry\n if (!Number.isFinite(min) || !Number.isFinite(max) || max! <= min!) return undefined\n return { min: min!, max: max!, step: (max! - min!) / 100 }\n }\n\n /** How far along its range a value is, in percent, for the filled part of the track. */\n private fillOf(value: number, range: { min: number; max: number }): number {\n return Math.min(Math.max(((value - range.min) / (range.max - range.min)) * 100, 0), 100)\n }\n\n private hintOf(entry: NumberMapEntry): string | undefined {\n const range = entry.min !== undefined && entry.max !== undefined ? `${entry.min}–${entry.max}` : undefined\n const standard = entry.default !== undefined ? `(${entry.default})` : undefined\n return [range, standard].filter(Boolean).join(' ') || undefined\n }\n\n /** While dragging the value is taken at every step, so the figure moves with the handle. */\n private onSlide(e: Event) {\n this._valueChanged(e)\n }\n\n override _valueChanged(e: Event) {\n e.stopPropagation()\n const input = e.target as HTMLInputElement\n const key = input.getAttribute('data-key')\n if (!key) return\n const entry = ((this.property?.entries ?? []) as NumberMapEntry[]).find(one => one.key === key)\n const map: Record<string, number> = this.value && typeof this.value === 'object' ? { ...this.value } : {}\n const number = input.valueAsNumber\n const outside = !Number.isFinite(number) || (entry?.min !== undefined && number < entry.min) || (entry?.max !== undefined && number > entry.max)\n if (outside) {\n /* Not taken: the field shows what the map holds. */\n input.value = String(map[key] ?? entry?.default ?? '')\n return\n }\n map[key] = input.type === 'range' ? Math.round(number * 100) / 100 : number\n this.value = map\n this.renderRoot.querySelectorAll<HTMLInputElement>(`input[data-key=\"${key}\"]`).forEach(one => {\n one.value = String(map[key])\n if (one.type === 'range') one.style.setProperty('--fill', `${this.fillOf(map[key]!, { min: Number(one.min), max: Number(one.max) })}%`)\n })\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n this.observe(this.value)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ox-property-editor-number-map.js","sourceRoot":"","sources":["../../src/ox-property-editor-number-map.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, nothing, TemplateResult } from 'lit'\nimport { customElement } from 'lit/decorators.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport { OxPropertyEditor } from './ox-property-editor.js'\nimport { PropertySpec } from './types.js'\n\n/**\n * One value of the map: its key, the name shown, and what it is held to. A number unless `kind` says otherwise: a\n * boolean is a checkbox, an enum a choice among `values` (a figure's data properties, ADR-0051).\n */\nexport interface NumberMapEntry {\n key: string\n label: string\n kind?: 'number' | 'boolean' | 'enum'\n unit?: string\n min?: number\n max?: number\n /** For an enum: the values it may take. */\n values?: string[]\n default?: number | boolean | string\n}\n\n/**\n * Several numbers kept as one map ({ key: number }) -- a figure's design values and state values a board sets\n * (ADR-0096), a shelf's spacings. Each entry is one compact row: its name, a slider when it has a range, the number\n * field, and its unit. A value outside its range is not taken: the field goes back to what it was. A change sends the\n * whole map, so the map is the one value the component holds.\n *\n * The slider is taken at every step of a drag, so what it drives moves with the handle.\n *\n * Built by the Figure lane after the existing editors (chief architect, 2026-09-26); open for the UX lane's review.\n *\n * `property.nested`: a key `a.b` is the value `b` inside the map's `a` -- a figure's motion settings, one map per group\n * (`{ locomotion: { clip, speed, paused } }`, ADR-0051 §3.2). The key splits at its first dot only.\n */\n@customElement('ox-property-editor-number-map')\nexport class OxPropertyEditorNumberMap extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n css`\n /*\n * The rows take the editor's own 24 columns (subgrid), so a name sits in the label column, right-aligned like\n * every other label, and the fields start where every other field starts.\n */\n :host > div[rows] {\n display: grid;\n grid-template-columns: subgrid;\n grid-auto-rows: 16px;\n row-gap: 3px;\n align-items: center;\n }\n\n :host > div[rows] > [name] {\n grid-column: 1 / var(--property-editor-label-end, 9);\n text-align: right;\n font: var(--property-editor-label-font, var(--label-font));\n color: var(--property-editor-label-color, inherit);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* A thin track filled up to the value (--fill), and a small round thumb: quieter than the fields around it. */\n :host > div[rows] > input[type='range'] {\n grid-column: var(--property-editor-label-end, 9) / 18;\n -webkit-appearance: none;\n appearance: none;\n width: 100%;\n min-width: 0;\n height: 12px;\n margin: 0;\n padding: 0;\n border: none;\n background: transparent;\n cursor: pointer;\n }\n\n :host > div[rows] > input[type='range']::-webkit-slider-runnable-track {\n height: 2px;\n border-radius: 1px;\n background: linear-gradient(\n to right,\n var(--md-sys-color-primary) var(--fill, 0%),\n var(--md-sys-color-outline-variant) var(--fill, 0%)\n );\n }\n\n :host > div[rows] > input[type='range']::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 8px;\n height: 8px;\n margin-top: -3px;\n border-radius: 50%;\n background: var(--md-sys-color-primary);\n transition: transform 0.12s;\n }\n\n :host > div[rows] > input[type='range']:hover::-webkit-slider-thumb,\n :host > div[rows] > input[type='range']:focus-visible::-webkit-slider-thumb {\n transform: scale(1.4);\n }\n\n :host > div[rows] > input[type='range']::-moz-range-track {\n height: 2px;\n border-radius: 1px;\n background: var(--md-sys-color-outline-variant);\n }\n\n :host > div[rows] > input[type='range']::-moz-range-progress {\n height: 2px;\n border-radius: 1px;\n background: var(--md-sys-color-primary);\n }\n\n :host > div[rows] > input[type='range']::-moz-range-thumb {\n width: 8px;\n height: 8px;\n border: none;\n border-radius: 50%;\n background: var(--md-sys-color-primary);\n }\n\n :host > div[rows] > input[type='range']:disabled {\n cursor: default;\n opacity: 0.5;\n }\n\n :host > div[rows] > input[type='number'] {\n grid-column: 18 / 22;\n box-sizing: border-box;\n width: 100%;\n min-width: 0;\n height: 16px;\n min-height: 0;\n padding: 0 2px;\n font: 0.7rem/1 var(--theme-font, Roboto, sans-serif);\n /* The value reads as text; the field shows itself only when pointed at or typed in. */\n background: transparent;\n border-color: transparent;\n border-radius: 3px;\n }\n\n :host > div[rows] > input[type='number']:hover {\n border-color: var(--md-sys-color-outline-variant);\n }\n\n :host > div[rows] > input[type='number']:focus {\n border-color: var(--md-sys-color-primary);\n background: var(--property-editor-background, field);\n }\n\n :host > div[rows] > input[type='number']::-webkit-inner-spin-button {\n display: none;\n min-width: 0;\n }\n\n /* No range, no slider: the field takes the slider's room. */\n :host > div[rows] > input[type='number'][wide] {\n grid-column: var(--property-editor-label-end, 9) / 22;\n }\n\n :host > div[rows] > select,\n :host > div[rows] > input[type='checkbox'] {\n grid-column: var(--property-editor-label-end, 9) / 22;\n box-sizing: border-box;\n min-width: 0;\n height: 16px;\n margin: 0;\n font: 0.7rem/1 var(--theme-font, Roboto, sans-serif);\n }\n\n :host > div[rows] > input[type='checkbox'] {\n justify-self: start;\n width: 14px;\n }\n\n :host > div[rows] > [unit] {\n grid-column: 22 / -1;\n overflow: hidden;\n font: 0.66rem/1 var(--theme-font, Roboto, sans-serif);\n color: var(--md-sys-color-outline);\n white-space: nowrap;\n }\n `\n ]\n\n editorTemplate(value: any, spec: PropertySpec): TemplateResult {\n const entries: NumberMapEntry[] = spec.property?.entries ?? []\n const nested = !!spec.property?.nested\n return html`\n <div fullwidth rows>\n ${entries.map(entry => {\n const held = this.read(value, entry.key, nested)\n if (entry.kind === 'boolean' || entry.kind === 'enum') return this.choiceRow(entry, held ?? entry.default)\n const shown = (held ?? entry.default) as number | undefined\n const ranged = this.rangeOf(entry)\n return html`\n <span name title=${entry.label}>${entry.label}</span>\n ${ranged\n ? html`<input\n type=\"range\"\n data-key=${entry.key}\n aria-label=${entry.label}\n min=${ranged.min}\n max=${ranged.max}\n step=${ranged.step}\n .value=${String(shown ?? ranged.min)}\n style=${`--fill: ${this.fillOf(shown ?? ranged.min, ranged)}%`}\n ?disabled=${this.disabled}\n @input=${this.onSlide}\n />`\n : nothing}\n <input\n type=\"number\"\n data-key=${entry.key}\n aria-label=${entry.label}\n ?wide=${!ranged}\n .value=${String(shown ?? '')}\n placeholder=${ifDefined(this.hintOf(entry))}\n min=${ifDefined(entry.min)}\n max=${ifDefined(entry.max)}\n step=\"any\"\n ?disabled=${this.disabled}\n />\n <span unit>${entry.unit ?? ''}</span>\n `\n })}\n </div>\n `\n }\n\n /** A checkbox for a boolean, a choice for an enum: what is shown is what the map holds, or the entry's default. */\n private choiceRow(entry: NumberMapEntry, shown: unknown): TemplateResult {\n return html`\n <span name title=${entry.label}>${entry.label}</span>\n ${entry.kind === 'boolean'\n ? html`<input type=\"checkbox\" data-key=${entry.key} aria-label=${entry.label} .checked=${shown === true} ?disabled=${this.disabled} />`\n : html`<select data-key=${entry.key} aria-label=${entry.label} ?disabled=${this.disabled}>\n ${(entry.values ?? []).map(v => html`<option value=${v} ?selected=${v === shown}>${v}</option>`)}\n </select>`}\n <span unit>${entry.unit ?? ''}</span>\n `\n }\n\n /** The value a key holds in the map: `a.b` inside `a` when the map is nested. */\n private read(map: unknown, key: string, nested: boolean): unknown {\n if (!map || typeof map !== 'object') return undefined\n const at = nested ? key.indexOf('.') : -1\n if (at < 0) return (map as Record<string, unknown>)[key]\n const inner = (map as Record<string, unknown>)[key.slice(0, at)]\n return inner && typeof inner === 'object' ? (inner as Record<string, unknown>)[key.slice(at + 1)] : undefined\n }\n\n /** The map with a key written: `a.b` into a copy of `a` when the map is nested. */\n private write(map: unknown, key: string, nested: boolean, value: unknown): Record<string, unknown> {\n const out: Record<string, unknown> = map && typeof map === 'object' ? { ...(map as Record<string, unknown>) } : {}\n const at = nested ? key.indexOf('.') : -1\n if (at < 0) out[key] = value\n else {\n const head = key.slice(0, at)\n const inner = out[head]\n out[head] = { ...(inner && typeof inner === 'object' ? (inner as Record<string, unknown>) : {}), [key.slice(at + 1)]: value }\n }\n return out\n }\n\n /** The slider's range, when the entry has one to slide over. */\n private rangeOf(entry: NumberMapEntry): { min: number; max: number; step: number } | undefined {\n const { min, max } = entry\n if (!Number.isFinite(min) || !Number.isFinite(max) || max! <= min!) return undefined\n return { min: min!, max: max!, step: (max! - min!) / 100 }\n }\n\n /** How far along its range a value is, in percent, for the filled part of the track. */\n private fillOf(value: number, range: { min: number; max: number }): number {\n return Math.min(Math.max(((value - range.min) / (range.max - range.min)) * 100, 0), 100)\n }\n\n private hintOf(entry: NumberMapEntry): string | undefined {\n const range = entry.min !== undefined && entry.max !== undefined ? `${entry.min}–${entry.max}` : undefined\n const standard = entry.default !== undefined ? `(${entry.default})` : undefined\n return [range, standard].filter(Boolean).join(' ') || undefined\n }\n\n /** While dragging the value is taken at every step, so the figure moves with the handle. */\n private onSlide(e: Event) {\n this._valueChanged(e)\n }\n\n override _valueChanged(e: Event) {\n e.stopPropagation()\n const input = e.target as HTMLInputElement\n const key = input.getAttribute('data-key')\n if (!key) return\n const entry = ((this.property?.entries ?? []) as NumberMapEntry[]).find(one => one.key === key)\n const nested = !!this.property?.nested\n if (entry?.kind === 'boolean' || entry?.kind === 'enum') {\n const choice = entry.kind === 'boolean' ? input.checked : (input as unknown as HTMLSelectElement).value\n this.value = this.write(this.value, key, nested, choice)\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n this.observe(this.value)\n return\n }\n const number = input.valueAsNumber\n const outside = !Number.isFinite(number) || (entry?.min !== undefined && number < entry.min) || (entry?.max !== undefined && number > entry.max)\n if (outside) {\n /* Not taken: the field shows what the map holds. */\n input.value = String(this.read(this.value, key, nested) ?? entry?.default ?? '')\n return\n }\n const taken = input.type === 'range' ? Math.round(number * 100) / 100 : number\n this.value = this.write(this.value, key, nested, taken)\n this.renderRoot.querySelectorAll<HTMLInputElement>(`input[data-key=\"${key}\"]`).forEach(one => {\n one.value = String(taken)\n if (one.type === 'range') one.style.setProperty('--fill', `${this.fillOf(taken, { min: Number(one.min), max: Number(one.max) })}%`)\n })\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n this.observe(this.value)\n }\n}\n"]}
|
|
@@ -72,4 +72,46 @@ describe('number-map', () => {
|
|
|
72
72
|
expect(field(el, 'run').hasAttribute('wide')).to.equal(true);
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
|
+
/** A figure's data values and motion settings (ADR-0051): a boolean, an enum, and a map of maps for the groups. */
|
|
76
|
+
describe('number-map — other kinds and nested keys', () => {
|
|
77
|
+
const field = (el, key) => el.renderRoot.querySelector(`[data-key="${key}"]`);
|
|
78
|
+
it('a boolean is a checkbox and an enum a choice; each change tells the whole map, false included', async () => {
|
|
79
|
+
const entries = [
|
|
80
|
+
{ key: 'batteryLow', label: '배터리 부족', kind: 'boolean', default: false },
|
|
81
|
+
{ key: 'mode', label: '동작', kind: 'enum', values: ['idle', 'walk', 'run'], default: 'idle' },
|
|
82
|
+
{ key: 'cadence', label: '동작 속도', unit: 'ratio', min: 0, max: 3, default: 1 }
|
|
83
|
+
];
|
|
84
|
+
const el = await fixture(html `<ox-property-editor-number-map name="figureValues" .property=${{ entries }} .value=${{ batteryLow: true }}></ox-property-editor-number-map>`);
|
|
85
|
+
expect(field(el, 'batteryLow').checked).to.equal(true);
|
|
86
|
+
expect(field(el, 'mode').value).to.equal('idle');
|
|
87
|
+
const box = field(el, 'batteryLow');
|
|
88
|
+
box.checked = false;
|
|
89
|
+
let told = oneEvent(el, 'change');
|
|
90
|
+
box.dispatchEvent(new Event('change', { bubbles: true }));
|
|
91
|
+
await told;
|
|
92
|
+
expect(el.value).to.deep.equal({ batteryLow: false });
|
|
93
|
+
const choice = field(el, 'mode');
|
|
94
|
+
choice.value = 'run';
|
|
95
|
+
told = oneEvent(el, 'change');
|
|
96
|
+
choice.dispatchEvent(new Event('change', { bubbles: true }));
|
|
97
|
+
await told;
|
|
98
|
+
expect(el.value).to.deep.equal({ batteryLow: false, mode: 'run' });
|
|
99
|
+
});
|
|
100
|
+
it('nested: a key a.b is the value b inside a, and writing one keeps the others of a', async () => {
|
|
101
|
+
const entries = [
|
|
102
|
+
{ key: 'locomotion.clip', label: '보행', kind: 'enum', values: ['idle', 'walk', 'run'], default: 'idle' },
|
|
103
|
+
{ key: 'locomotion.speed', label: '보행', unit: '×', min: 0, max: 3, default: 0 },
|
|
104
|
+
{ key: 'locomotion.paused', label: '보행', kind: 'boolean', default: false }
|
|
105
|
+
];
|
|
106
|
+
const el = await fixture(html `<ox-property-editor-number-map name="motion" .property=${{ entries, nested: true }} .value=${{ locomotion: { clip: 'walk', speed: 1 } }}></ox-property-editor-number-map>`);
|
|
107
|
+
expect(field(el, 'locomotion.clip').value).to.equal('walk');
|
|
108
|
+
const speed = el.renderRoot.querySelector('input[type=number][data-key="locomotion.speed"]');
|
|
109
|
+
expect(speed.value).to.equal('1');
|
|
110
|
+
speed.value = '2';
|
|
111
|
+
const told = oneEvent(el, 'change');
|
|
112
|
+
speed.dispatchEvent(new Event('change', { bubbles: true }));
|
|
113
|
+
await told;
|
|
114
|
+
expect(el.value).to.deep.equal({ locomotion: { clip: 'walk', speed: 2 } });
|
|
115
|
+
});
|
|
116
|
+
});
|
|
75
117
|
//# sourceMappingURL=number-map.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number-map.test.js","sourceRoot":"","sources":["../../test/number-map.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture, oneEvent } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport '../src/ox-property-editor-number-map.js'\nimport type { OxPropertyEditorNumberMap } from '../src/ox-property-editor-number-map.js'\n\n/**\n * Several numbers as one map (ADR-0096): a board's design values for a figure. A value within range becomes part of the\n * map and the whole map is told; one outside its range is not taken and the field shows what the map holds.\n */\ndescribe('number-map', () => {\n const entries = [\n { key: 'pitch', label: '롤러 간격', unit: 'mm', min: 100, max: 400, default: 200 },\n { key: 'gap', label: '틈', unit: 'mm', min: 0, max: 50, default: 10 }\n ]\n const make = () =>\n fixture<OxPropertyEditorNumberMap>(html`<ox-property-editor-number-map name=\"designInputs\" .property=${{ entries }} .value=${{ pitch: 150 }}></ox-property-editor-number-map>`)\n const field = (el: OxPropertyEditorNumberMap, key: string) => el.renderRoot.querySelector(`input[type=number][data-key=\"${key}\"]`) as HTMLInputElement\n\n it('shows each value, or its default, with its range', async () => {\n const el = await make()\n expect(field(el, 'pitch').value).to.equal('150')\n expect(field(el, 'gap').value).to.equal('10')\n expect(field(el, 'pitch').placeholder).to.equal('100–400 (200)')\n })\n\n it('a value within range: the whole map is told', async () => {\n const el = await make()\n const input = field(el, 'gap')\n input.value = '20'\n const told = oneEvent(el, 'change')\n input.dispatchEvent(new Event('change', { bubbles: true }))\n await told\n expect(el.value).to.deep.equal({ pitch: 150, gap: 20 })\n })\n\n it('a value outside its range is not taken: nothing is told, the field shows the map again', async () => {\n const el = await make()\n let told = false\n el.addEventListener('change', () => (told = true))\n const input = field(el, 'pitch')\n input.value = '1000'\n input.dispatchEvent(new Event('change', { bubbles: true }))\n await el.updateComplete\n expect(told).to.equal(false)\n expect(el.value).to.deep.equal({ pitch: 150 })\n expect(input.value).to.equal('150')\n })\n\n it('an emptied field is not a number: not taken, the field shows the map again', async () => {\n const el = await make()\n let told = false\n el.addEventListener('change', () => (told = true))\n const input = field(el, 'gap')\n input.value = ''\n input.dispatchEvent(new Event('change', { bubbles: true }))\n await el.updateComplete\n expect(told).to.equal(false)\n expect(el.value).to.deep.equal({ pitch: 150 })\n expect(input.value).to.equal('10')\n })\n\n it('★ a ranged entry has a slider: every step of a drag is told, and the number follows', async () => {\n const el = await make()\n const slider = el.renderRoot.querySelector('input[type=range][data-key=\"gap\"]') as HTMLInputElement\n let told = 0\n el.addEventListener('change', () => told++)\n slider.value = '30'\n slider.dispatchEvent(new Event('input', { bubbles: true }))\n expect(field(el, 'gap').value).to.equal('30')\n expect(told).to.equal(1)\n expect(el.value).to.deep.equal({ pitch: 150, gap: 30 })\n slider.value = '40'\n slider.dispatchEvent(new Event('input', { bubbles: true }))\n expect(told).to.equal(2)\n expect(el.value).to.deep.equal({ pitch: 150, gap: 40 })\n })\n\n it('an entry without a range has no slider; its field takes the room', async () => {\n const el = await fixture<OxPropertyEditorNumberMap>(\n html`<ox-property-editor-number-map name=\"clips\" .property=${{ entries: [{ key: 'run', label: 'run', min: 0 }] }}></ox-property-editor-number-map>`\n )\n expect(el.renderRoot.querySelector('input[type=range]')).to.equal(null)\n expect(field(el, 'run').hasAttribute('wide')).to.equal(true)\n })\n})\n"]}
|
|
1
|
+
{"version":3,"file":"number-map.test.js","sourceRoot":"","sources":["../../test/number-map.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture, oneEvent } from '@open-wc/testing'\nimport { html } from 'lit'\n\nimport '../src/ox-property-editor-number-map.js'\nimport type { OxPropertyEditorNumberMap } from '../src/ox-property-editor-number-map.js'\n\n/**\n * Several numbers as one map (ADR-0096): a board's design values for a figure. A value within range becomes part of the\n * map and the whole map is told; one outside its range is not taken and the field shows what the map holds.\n */\ndescribe('number-map', () => {\n const entries = [\n { key: 'pitch', label: '롤러 간격', unit: 'mm', min: 100, max: 400, default: 200 },\n { key: 'gap', label: '틈', unit: 'mm', min: 0, max: 50, default: 10 }\n ]\n const make = () =>\n fixture<OxPropertyEditorNumberMap>(html`<ox-property-editor-number-map name=\"designInputs\" .property=${{ entries }} .value=${{ pitch: 150 }}></ox-property-editor-number-map>`)\n const field = (el: OxPropertyEditorNumberMap, key: string) => el.renderRoot.querySelector(`input[type=number][data-key=\"${key}\"]`) as HTMLInputElement\n\n it('shows each value, or its default, with its range', async () => {\n const el = await make()\n expect(field(el, 'pitch').value).to.equal('150')\n expect(field(el, 'gap').value).to.equal('10')\n expect(field(el, 'pitch').placeholder).to.equal('100–400 (200)')\n })\n\n it('a value within range: the whole map is told', async () => {\n const el = await make()\n const input = field(el, 'gap')\n input.value = '20'\n const told = oneEvent(el, 'change')\n input.dispatchEvent(new Event('change', { bubbles: true }))\n await told\n expect(el.value).to.deep.equal({ pitch: 150, gap: 20 })\n })\n\n it('a value outside its range is not taken: nothing is told, the field shows the map again', async () => {\n const el = await make()\n let told = false\n el.addEventListener('change', () => (told = true))\n const input = field(el, 'pitch')\n input.value = '1000'\n input.dispatchEvent(new Event('change', { bubbles: true }))\n await el.updateComplete\n expect(told).to.equal(false)\n expect(el.value).to.deep.equal({ pitch: 150 })\n expect(input.value).to.equal('150')\n })\n\n it('an emptied field is not a number: not taken, the field shows the map again', async () => {\n const el = await make()\n let told = false\n el.addEventListener('change', () => (told = true))\n const input = field(el, 'gap')\n input.value = ''\n input.dispatchEvent(new Event('change', { bubbles: true }))\n await el.updateComplete\n expect(told).to.equal(false)\n expect(el.value).to.deep.equal({ pitch: 150 })\n expect(input.value).to.equal('10')\n })\n\n it('★ a ranged entry has a slider: every step of a drag is told, and the number follows', async () => {\n const el = await make()\n const slider = el.renderRoot.querySelector('input[type=range][data-key=\"gap\"]') as HTMLInputElement\n let told = 0\n el.addEventListener('change', () => told++)\n slider.value = '30'\n slider.dispatchEvent(new Event('input', { bubbles: true }))\n expect(field(el, 'gap').value).to.equal('30')\n expect(told).to.equal(1)\n expect(el.value).to.deep.equal({ pitch: 150, gap: 30 })\n slider.value = '40'\n slider.dispatchEvent(new Event('input', { bubbles: true }))\n expect(told).to.equal(2)\n expect(el.value).to.deep.equal({ pitch: 150, gap: 40 })\n })\n\n it('an entry without a range has no slider; its field takes the room', async () => {\n const el = await fixture<OxPropertyEditorNumberMap>(\n html`<ox-property-editor-number-map name=\"clips\" .property=${{ entries: [{ key: 'run', label: 'run', min: 0 }] }}></ox-property-editor-number-map>`\n )\n expect(el.renderRoot.querySelector('input[type=range]')).to.equal(null)\n expect(field(el, 'run').hasAttribute('wide')).to.equal(true)\n })\n})\n\n/** A figure's data values and motion settings (ADR-0051): a boolean, an enum, and a map of maps for the groups. */\ndescribe('number-map — other kinds and nested keys', () => {\n const field = (el: OxPropertyEditorNumberMap, key: string) => el.renderRoot.querySelector(`[data-key=\"${key}\"]`) as HTMLInputElement & HTMLSelectElement\n\n it('a boolean is a checkbox and an enum a choice; each change tells the whole map, false included', async () => {\n const entries = [\n { key: 'batteryLow', label: '배터리 부족', kind: 'boolean', default: false },\n { key: 'mode', label: '동작', kind: 'enum', values: ['idle', 'walk', 'run'], default: 'idle' },\n { key: 'cadence', label: '동작 속도', unit: 'ratio', min: 0, max: 3, default: 1 }\n ]\n const el = await fixture<OxPropertyEditorNumberMap>(html`<ox-property-editor-number-map name=\"figureValues\" .property=${{ entries }} .value=${{ batteryLow: true }}></ox-property-editor-number-map>`)\n expect(field(el, 'batteryLow').checked).to.equal(true)\n expect(field(el, 'mode').value).to.equal('idle')\n const box = field(el, 'batteryLow')\n box.checked = false\n let told = oneEvent(el, 'change')\n box.dispatchEvent(new Event('change', { bubbles: true }))\n await told\n expect(el.value).to.deep.equal({ batteryLow: false })\n const choice = field(el, 'mode')\n choice.value = 'run'\n told = oneEvent(el, 'change')\n choice.dispatchEvent(new Event('change', { bubbles: true }))\n await told\n expect(el.value).to.deep.equal({ batteryLow: false, mode: 'run' })\n })\n\n it('nested: a key a.b is the value b inside a, and writing one keeps the others of a', async () => {\n const entries = [\n { key: 'locomotion.clip', label: '보행', kind: 'enum', values: ['idle', 'walk', 'run'], default: 'idle' },\n { key: 'locomotion.speed', label: '보행', unit: '×', min: 0, max: 3, default: 0 },\n { key: 'locomotion.paused', label: '보행', kind: 'boolean', default: false }\n ]\n const el = await fixture<OxPropertyEditorNumberMap>(\n html`<ox-property-editor-number-map name=\"motion\" .property=${{ entries, nested: true }} .value=${{ locomotion: { clip: 'walk', speed: 1 } }}></ox-property-editor-number-map>`\n )\n expect(field(el, 'locomotion.clip').value).to.equal('walk')\n const speed = el.renderRoot.querySelector('input[type=number][data-key=\"locomotion.speed\"]') as HTMLInputElement\n expect(speed.value).to.equal('1')\n speed.value = '2'\n const told = oneEvent(el, 'change')\n speed.dispatchEvent(new Event('change', { bubbles: true }))\n await told\n expect(el.value).to.deep.equal({ locomotion: { clip: 'walk', speed: 2 } })\n })\n})\n"]}
|