@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.
@@ -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
- /** One number of the map: its key, the name shown, and what it is held to. */
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
- default?: number;
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 map = value && typeof value === 'object' ? value : {};
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, _b;
25
- const shown = (_a = map[entry.key]) !== null && _a !== void 0 ? _a : entry.default;
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>${(_b = entry.unit) !== null && _b !== void 0 ? _b : ''}</span>
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 map = this.value && typeof this.value === 'object' ? { ...this.value } : {};
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((_d = (_c = map[key]) !== null && _c !== void 0 ? _c : entry === null || entry === void 0 ? void 0 : entry.default) !== null && _d !== void 0 ? _d : '');
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
- map[key] = input.type === 'range' ? Math.round(number * 100) / 100 : number;
98
- this.value = map;
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(map[key]);
149
+ one.value = String(taken);
101
150
  if (one.type === 'range')
102
- one.style.setProperty('--fill', `${this.fillOf(map[key], { min: Number(one.min), max: Number(one.max) })}%`);
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"]}