@things-factory/figure-ui 10.1.156 → 10.1.158

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.
Files changed (37) hide show
  1. package/client/modeller/clips.ts +43 -4
  2. package/client/modeller/data-interface.ts +110 -0
  3. package/client/modeller/editor.ts +16 -2
  4. package/client/modeller/figure-clips.ts +125 -12
  5. package/client/modeller/figure-data-panel.ts +308 -0
  6. package/client/modeller/figure-preview.ts +41 -4
  7. package/client/modeller/refusals.ts +46 -0
  8. package/dist-client/modeller/clips.d.ts +32 -2
  9. package/dist-client/modeller/clips.js +32 -4
  10. package/dist-client/modeller/clips.js.map +1 -1
  11. package/dist-client/modeller/data-interface.d.ts +42 -0
  12. package/dist-client/modeller/data-interface.js +95 -0
  13. package/dist-client/modeller/data-interface.js.map +1 -0
  14. package/dist-client/modeller/editor.d.ts +3 -0
  15. package/dist-client/modeller/editor.js +18 -0
  16. package/dist-client/modeller/editor.js.map +1 -1
  17. package/dist-client/modeller/figure-clips.d.ts +10 -0
  18. package/dist-client/modeller/figure-clips.js +125 -10
  19. package/dist-client/modeller/figure-clips.js.map +1 -1
  20. package/dist-client/modeller/figure-data-panel.d.ts +39 -0
  21. package/dist-client/modeller/figure-data-panel.js +317 -0
  22. package/dist-client/modeller/figure-data-panel.js.map +1 -0
  23. package/dist-client/modeller/figure-preview.d.ts +7 -0
  24. package/dist-client/modeller/figure-preview.js +49 -4
  25. package/dist-client/modeller/figure-preview.js.map +1 -1
  26. package/dist-client/modeller/refusals.d.ts +5 -0
  27. package/dist-client/modeller/refusals.js +58 -0
  28. package/dist-client/modeller/refusals.js.map +1 -1
  29. package/dist-client/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +6 -6
  31. package/test/data-motion.test.ts +137 -0
  32. package/translations/en.json +86 -2
  33. package/translations/es.json +86 -2
  34. package/translations/ja.json +86 -2
  35. package/translations/ko.json +86 -2
  36. package/translations/ms.json +86 -2
  37. package/translations/zh.json +86 -2
@@ -0,0 +1,317 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/icon/icon.js';
3
+ import { css, html, LitElement, nothing } from 'lit';
4
+ import { customElement, property, state } from 'lit/decorators.js';
5
+ import { i18next, localize } from '@operato/i18n';
6
+ import { FIGURE_UNITS } from '@hatiolab/figure-model';
7
+ import { connectionDraftOf, dataSampleOf, dataTargetsOf, domainOf, propertyDraftOf, sampleValueOf, targetKeyOf } from './data-interface.js';
8
+ import { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js';
9
+ import { HelpToggles } from './help.js';
10
+ /**
11
+ * 데이터 -- the figure's public data properties and what each moves inside it (ADR-0051 §2.2). A property is a value a
12
+ * consumer maps its own data onto, by its id: a number with a unit and a range, a boolean, or an enum. A connection
13
+ * makes a property move a control, a colour, or which clip of a motion group plays and how fast. Every change is one
14
+ * part edit, and whether it holds is the kernel's check.
15
+ *
16
+ * The sample is not saved: values tried here, what they would move (figure-model's evaluation), and -- in the preview --
17
+ * the figure running on them (`data-sample`).
18
+ */
19
+ let FigureDataPanel = class FigureDataPanel extends localize(i18next)(LitElement) {
20
+ constructor() {
21
+ super(...arguments);
22
+ this.help = new HelpToggles(this);
23
+ this.locked = false;
24
+ this.adding = { label: '', kind: 'number' };
25
+ this.connecting = { property: '', target: '' };
26
+ /** The sample's values as typed, by property id; a property left out reads its default. */
27
+ this.sample = {};
28
+ this.sampling = false;
29
+ }
30
+ static { this.styles = [SidePanelStyles, css `
31
+ :host {
32
+ display: block;
33
+ }
34
+ section[item] {
35
+ display: grid;
36
+ gap: 8px;
37
+ padding: 8px;
38
+ border: 1px solid var(--md-sys-color-outline-variant);
39
+ border-radius: 8px;
40
+ }
41
+ div[head] {
42
+ display: grid;
43
+ grid-template-columns: minmax(0, 1fr) 30px;
44
+ gap: 6px;
45
+ align-items: center;
46
+ }
47
+ div[head] strong {
48
+ min-width: 0;
49
+ overflow-wrap: anywhere;
50
+ }
51
+ div[head] button[act] {
52
+ width: 30px;
53
+ min-height: var(--figure-control-height);
54
+ margin: 0;
55
+ padding: 0;
56
+ }
57
+ div[cases] {
58
+ display: grid;
59
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
60
+ gap: 4px 6px;
61
+ align-items: center;
62
+ }
63
+ ul[result] {
64
+ margin: 0;
65
+ padding-left: 16px;
66
+ }
67
+ md-icon {
68
+ font-size: 16px;
69
+ width: 16px;
70
+ height: 16px;
71
+ }
72
+ `]; }
73
+ render() {
74
+ const asset = this.asset;
75
+ if (!asset)
76
+ return nothing;
77
+ const properties = asset.dataInterface?.properties ?? [];
78
+ const connections = asset.dataInterface?.connections ?? [];
79
+ return html `
80
+ <section>
81
+ <h3>${i18next.t('figure.label.data-properties')}${this.help.button('figure.text.data-properties-explained')}</h3>
82
+ ${this.help.hint('figure.text.data-properties-explained')}
83
+ ${properties.map(p => this.renderProperty(p))}
84
+ ${this.renderAddProperty(asset)}
85
+ </section>
86
+ <section>
87
+ <h3>${i18next.t('figure.label.data-connections')}${this.help.button('figure.text.data-connections-explained')}</h3>
88
+ ${this.help.hint('figure.text.data-connections-explained')}
89
+ ${connections.map(c => this.renderConnection(asset, c, properties.find(p => p.id === c.property)))}
90
+ ${properties.length ? this.renderAddConnection(asset, properties) : html `<p hint>${i18next.t('figure.text.data-no-properties')}</p>`}
91
+ </section>
92
+ ${properties.length ? this.renderSample(asset, properties) : nothing}
93
+ `;
94
+ }
95
+ renderProperty(p) {
96
+ const set = (patch) => this.edit({ kind: 'set-data-property', property: { ...p, ...patch } });
97
+ const value = (e) => e.target.value;
98
+ const number = (e) => e.target.valueAsNumber;
99
+ return html `<section item>
100
+ <div head>
101
+ <strong>${p.label} <small>(${i18next.t('figure.label.data-kind-' + p.kind)})</small></strong>
102
+ <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-data-property')} @click=${() => this.edit({ kind: 'remove-data-property', id: p.id })}>
103
+ <md-icon>close</md-icon>
104
+ </button>
105
+ </div>
106
+ <div form>
107
+ <span>${i18next.t('figure.label.data-id')}</span>
108
+ <input .value=${p.id} ?disabled=${this.locked} @change=${(e) => value(e).trim() !== p.id && this.edit({ kind: 'rename-data-property', id: p.id, to: value(e).trim() })} />
109
+ <span>${i18next.t('figure.label.name')}</span>
110
+ <input .value=${p.label} ?disabled=${this.locked} @change=${(e) => value(e).trim() && set({ label: value(e).trim() })} />
111
+ ${p.kind === 'number'
112
+ ? html `<span>${i18next.t('figure.label.data-unit')}</span>
113
+ <select ?disabled=${this.locked} @change=${(e) => set({ unit: value(e) })}>
114
+ ${FIGURE_UNITS.map(u => html `<option value=${u} ?selected=${u === p.unit}>${u}</option>`)}
115
+ </select>
116
+ <span>${i18next.t('figure.label.data-min')}</span>
117
+ <input type="number" step="any" .value=${String(p.min)} ?disabled=${this.locked} @change=${(e) => set({ min: number(e) })} />
118
+ <span>${i18next.t('figure.label.data-max')}</span>
119
+ <input type="number" step="any" .value=${String(p.max)} ?disabled=${this.locked} @change=${(e) => set({ max: number(e) })} />
120
+ <span>${i18next.t('figure.label.data-default')}</span>
121
+ <input type="number" step="any" .value=${String(p.default)} ?disabled=${this.locked} @change=${(e) => set({ default: number(e) })} />`
122
+ : p.kind === 'boolean'
123
+ ? html `<span>${i18next.t('figure.label.data-default')}</span>
124
+ <select ?disabled=${this.locked} @change=${(e) => set({ default: value(e) === 'true' })}>
125
+ ${[false, true].map(b => html `<option value=${String(b)} ?selected=${b === p.default}>${i18next.t(b ? 'figure.label.data-true' : 'figure.label.data-false')}</option>`)}
126
+ </select>`
127
+ : html `<span>${i18next.t('figure.label.data-values')}</span>
128
+ <input
129
+ .value=${p.values.join(', ')}
130
+ ?disabled=${this.locked}
131
+ @change=${(e) => {
132
+ const values = value(e).split(',').map(v => v.trim()).filter(Boolean);
133
+ set({ values, default: values.includes(p.default) ? p.default : values[0] });
134
+ }}
135
+ />
136
+ <span>${i18next.t('figure.label.data-default')}</span>
137
+ <select ?disabled=${this.locked} @change=${(e) => set({ default: value(e) })}>
138
+ ${p.values.map(v => html `<option value=${v} ?selected=${v === p.default}>${v}</option>`)}
139
+ </select>`}
140
+ </div>
141
+ </section>`;
142
+ }
143
+ renderAddProperty(asset) {
144
+ const a = this.adding;
145
+ return html `<div form>
146
+ <span>${i18next.t('figure.label.name')}</span>
147
+ <input .value=${a.label} @input=${(e) => (this.adding = { ...a, label: e.target.value })} />
148
+ <span>${i18next.t('figure.label.data-kind')}</span>
149
+ <select @change=${(e) => (this.adding = { ...a, kind: e.target.value })}>
150
+ ${['number', 'boolean', 'enum'].map(k => html `<option value=${k} ?selected=${k === a.kind}>${i18next.t('figure.label.data-kind-' + k)}</option>`)}
151
+ </select>
152
+ </div>
153
+ <button
154
+ act
155
+ ?disabled=${this.locked || !a.label.trim()}
156
+ @click=${() => {
157
+ this.edit({ kind: 'set-data-property', property: propertyDraftOf(asset, a.label.trim(), a.kind) });
158
+ this.adding = { ...a, label: '' };
159
+ }}
160
+ >
161
+ <md-icon>add</md-icon>${i18next.t('figure.button.add-data-property')}
162
+ </button>`;
163
+ }
164
+ targetName(asset, c, property) {
165
+ const choice = property ? dataTargetsOf(asset, property.kind).find(t => t.key === targetKeyOf(c.target)) : undefined;
166
+ const name = choice?.name ?? targetKeyOf(c.target);
167
+ return i18next.t('figure.label.data-target-' + c.target.kind, { name });
168
+ }
169
+ renderConnection(asset, c, property) {
170
+ const set = (patch) => this.edit({ kind: 'set-data-connection', connection: { ...c, ...patch } });
171
+ const number = (e) => e.target.valueAsNumber;
172
+ const t = c.transform;
173
+ return html `<section item>
174
+ <div head>
175
+ <strong>${property?.label ?? c.property} → ${this.targetName(asset, c, property)}</strong>
176
+ <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-data-connection')} @click=${() => this.edit({ kind: 'remove-data-connection', id: c.id })}>
177
+ <md-icon>close</md-icon>
178
+ </button>
179
+ </div>
180
+ <div form>
181
+ ${c.target.kind === 'state'
182
+ ? html `<span>${i18next.t('figure.label.data-source-mode')}</span>
183
+ <select ?disabled=${this.locked} @change=${(e) => set({ target: { ...c.target, sourceMode: e.target.value } })}>
184
+ ${['observed', 'commanded'].map(m => html `<option value=${m} ?selected=${c.target.kind === 'state' && c.target.sourceMode === m}>${i18next.t('figure.label.data-' + m)}</option>`)}
185
+ </select>`
186
+ : nothing}
187
+ ${t.kind === 'lookup'
188
+ ? nothing
189
+ : html `<span>${i18next.t('figure.label.data-transform')}</span>
190
+ <select ?disabled=${this.locked} @change=${(e) => set({ transform: e.target.value === 'affine' ? { kind: 'affine', scale: 1, offset: 0 } : { kind: 'identity' } })}>
191
+ <option value="identity" ?selected=${t.kind === 'identity'}>${i18next.t('figure.label.data-identity')}</option>
192
+ <option value="affine" ?selected=${t.kind === 'affine'}>${i18next.t('figure.label.data-affine')}</option>
193
+ </select>`}
194
+ ${t.kind === 'affine'
195
+ ? html `<span>${i18next.t('figure.label.data-scale')}</span>
196
+ <input type="number" step="any" .value=${String(t.scale)} ?disabled=${this.locked} @change=${(e) => set({ transform: { ...t, scale: number(e) } })} />
197
+ <span>${i18next.t('figure.label.data-offset')}</span>
198
+ <input type="number" step="any" .value=${String(t.offset)} ?disabled=${this.locked} @change=${(e) => set({ transform: { ...t, offset: number(e) } })} />`
199
+ : nothing}
200
+ </div>
201
+ ${t.kind === 'lookup' ? this.renderCases(asset, c, set) : nothing}
202
+ </section>`;
203
+ }
204
+ /** A case per value of the property: what the target takes for it -- a number, a colour, or a clip of the group. */
205
+ renderCases(asset, c, set) {
206
+ if (c.transform.kind !== 'lookup')
207
+ return nothing;
208
+ const cases = c.transform.cases;
209
+ const write = (i, value) => set({ transform: { kind: 'lookup', cases: cases.map((k, j) => (j === i ? { ...k, value } : k)) } });
210
+ const clips = c.target.kind === 'motion-clip' ? ((asset.motionGroups ?? []).find(g => c.target.kind === 'motion-clip' && g.id === c.target.group)?.clips ?? []) : [];
211
+ return html `<div cases>
212
+ ${cases.map((k, i) => html `<span>${typeof k.when === 'boolean' ? i18next.t(k.when ? 'figure.label.data-true' : 'figure.label.data-false') : String(k.when)}</span>
213
+ ${c.target.kind === 'palette'
214
+ ? html `<input type="color" .value=${String(k.value)} ?disabled=${this.locked} @change=${(e) => write(i, e.target.value)} />`
215
+ : c.target.kind === 'motion-clip'
216
+ ? html `<select ?disabled=${this.locked} @change=${(e) => write(i, e.target.value)}>
217
+ ${clips.map(clip => html `<option value=${clip} ?selected=${clip === k.value}>${clip}</option>`)}
218
+ </select>`
219
+ : html `<input type="number" step="any" .value=${String(k.value)} ?disabled=${this.locked} @change=${(e) => write(i, e.target.valueAsNumber)} />`}`)}
220
+ </div>`;
221
+ }
222
+ renderAddConnection(asset, properties) {
223
+ const a = this.connecting;
224
+ const property = properties.find(p => p.id === a.property) ?? properties[0];
225
+ const targets = dataTargetsOf(asset, property.kind);
226
+ const choice = targets.find(t => t.key === a.target) ?? targets[0];
227
+ return html `<div form>
228
+ <span>${i18next.t('figure.label.data-property')}</span>
229
+ <select @change=${(e) => (this.connecting = { ...a, property: e.target.value })}>
230
+ ${properties.map(p => html `<option value=${p.id} ?selected=${p === property}>${p.label}</option>`)}
231
+ </select>
232
+ <span>${i18next.t('figure.label.data-target')}</span>
233
+ <select ?disabled=${!targets.length} @change=${(e) => (this.connecting = { ...a, target: e.target.value })}>
234
+ ${targets.map((t) => html `<option value=${t.key} ?selected=${t === choice}>${i18next.t('figure.label.data-target-' + t.target.kind, { name: t.name })}</option>`)}
235
+ </select>
236
+ </div>
237
+ <button act ?disabled=${this.locked || !choice} @click=${() => this.edit({ kind: 'set-data-connection', connection: connectionDraftOf(asset, property, choice) })}>
238
+ <md-icon>add</md-icon>${i18next.t('figure.button.add-data-connection')}
239
+ </button>`;
240
+ }
241
+ /** Values to try: what they move, by figure-model's evaluation; in the preview, the figure running on them. */
242
+ renderSample(asset, properties) {
243
+ const values = Object.fromEntries(properties.map(p => [p.id, this.sample[p.id] ?? p.default]));
244
+ const outcome = dataSampleOf(asset, values);
245
+ return html `<section>
246
+ <h3>${i18next.t('figure.label.data-sample')}${this.help.button('figure.text.data-sample-explained')}</h3>
247
+ ${this.help.hint('figure.text.data-sample-explained')}
248
+ <div form>
249
+ ${properties.map(p => html `<span>${p.label}</span>
250
+ ${p.kind === 'number'
251
+ ? html `<input type="number" step="any" .value=${String(values[p.id])} @change=${(e) => this.try(p, e.target.value)} />`
252
+ : html `<select @change=${(e) => this.try(p, e.target.value)}>
253
+ ${domainOf(p).map(v => html `<option value=${String(v)} ?selected=${v === values[p.id]}>${typeof v === 'boolean' ? i18next.t(v ? 'figure.label.data-true' : 'figure.label.data-false') : v}</option>`)}
254
+ </select>`}`)}
255
+ </div>
256
+ ${'issues' in outcome
257
+ ? html `<p hint>${outcome.issues.map(i => i18next.t('figure.text.data-value-' + i.reason, { property: i.property ?? '' })).join(' · ')}</p>`
258
+ : html `<ul result>
259
+ ${Object.entries(outcome.result.states).map(([input, s]) => html `<li>${asset.stateInputs?.[input]?.label ?? input} = ${s.value}</li>`)}
260
+ ${Object.entries(outcome.result.palette).map(([token, colour]) => html `<li>${token} = ${colour}</li>`)}
261
+ ${Object.entries(outcome.result.motion).map(([group, m]) => html `<li>${(asset.motionGroups ?? []).find(g => g.id === group)?.label ?? group}: ${[m.clip, m.speed === undefined ? '' : `× ${m.speed}`].filter(Boolean).join(' ')}</li>`)}
262
+ </ul>`}
263
+ <label toggle>
264
+ <input
265
+ type="checkbox"
266
+ .checked=${this.sampling}
267
+ @change=${(e) => {
268
+ this.sampling = e.target.checked;
269
+ this.tell(values);
270
+ }}
271
+ />
272
+ ${i18next.t('figure.label.data-sample-in-preview')}
273
+ </label>
274
+ </section>`;
275
+ }
276
+ try(property, typed) {
277
+ this.sample = { ...this.sample, [property.id]: sampleValueOf(property, typed) };
278
+ const properties = this.asset?.dataInterface?.properties ?? [];
279
+ this.tell(Object.fromEntries(properties.map(p => [p.id, this.sample[p.id] ?? p.default])));
280
+ }
281
+ /** The sample to the preview while it is asked for; null lets the preview's figure go back to its own values. */
282
+ tell(values) {
283
+ this.dispatchEvent(new CustomEvent('data-sample', { detail: { values: this.sampling ? values : null }, bubbles: true, composed: true }));
284
+ }
285
+ edit(edit) {
286
+ this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit }, bubbles: true, composed: true }));
287
+ }
288
+ };
289
+ __decorate([
290
+ property({ attribute: false }),
291
+ __metadata("design:type", Object)
292
+ ], FigureDataPanel.prototype, "asset", void 0);
293
+ __decorate([
294
+ property({ type: Boolean }),
295
+ __metadata("design:type", Object)
296
+ ], FigureDataPanel.prototype, "locked", void 0);
297
+ __decorate([
298
+ state(),
299
+ __metadata("design:type", Object)
300
+ ], FigureDataPanel.prototype, "adding", void 0);
301
+ __decorate([
302
+ state(),
303
+ __metadata("design:type", Object)
304
+ ], FigureDataPanel.prototype, "connecting", void 0);
305
+ __decorate([
306
+ state(),
307
+ __metadata("design:type", Object)
308
+ ], FigureDataPanel.prototype, "sample", void 0);
309
+ __decorate([
310
+ state(),
311
+ __metadata("design:type", Object)
312
+ ], FigureDataPanel.prototype, "sampling", void 0);
313
+ FigureDataPanel = __decorate([
314
+ customElement('figure-data-panel')
315
+ ], FigureDataPanel);
316
+ export { FigureDataPanel };
317
+ //# sourceMappingURL=figure-data-panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-data-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-data-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { FIGURE_UNITS } from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureDataConnection, FigureDataProperty, FigureDataValue } from '@hatiolab/figure-model'\n\nimport { connectionDraftOf, dataSampleOf, dataTargetsOf, domainOf, propertyDraftOf, sampleValueOf, targetKeyOf, type DataTargetChoice } from './data-interface.js'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { HelpToggles } from './help.js'\n\n/**\n * 데이터 -- the figure's public data properties and what each moves inside it (ADR-0051 §2.2). A property is a value a\n * consumer maps its own data onto, by its id: a number with a unit and a range, a boolean, or an enum. A connection\n * makes a property move a control, a colour, or which clip of a motion group plays and how fast. Every change is one\n * part edit, and whether it holds is the kernel's check.\n *\n * The sample is not saved: values tried here, what they would move (figure-model's evaluation), and -- in the preview --\n * the figure running on them (`data-sample`).\n */\n@customElement('figure-data-panel')\nexport class FigureDataPanel extends localize(i18next)(LitElement) {\n private readonly help = new HelpToggles(this)\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n section[item] {\n display: grid;\n gap: 8px;\n padding: 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n }\n div[head] {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 30px;\n gap: 6px;\n align-items: center;\n }\n div[head] strong {\n min-width: 0;\n overflow-wrap: anywhere;\n }\n div[head] button[act] {\n width: 30px;\n min-height: var(--figure-control-height);\n margin: 0;\n padding: 0;\n }\n div[cases] {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);\n gap: 4px 6px;\n align-items: center;\n }\n ul[result] {\n margin: 0;\n padding-left: 16px;\n }\n md-icon {\n font-size: 16px;\n width: 16px;\n height: 16px;\n }\n `]\n\n @property({ attribute: false }) asset?: FigureAsset\n @property({ type: Boolean }) locked = false\n\n @state() private adding = { label: '', kind: 'number' as FigureDataProperty['kind'] }\n @state() private connecting = { property: '', target: '' }\n /** The sample's values as typed, by property id; a property left out reads its default. */\n @state() private sample: Record<string, FigureDataValue> = {}\n @state() private sampling = false\n\n render() {\n const asset = this.asset\n if (!asset) return nothing\n const properties = asset.dataInterface?.properties ?? []\n const connections = asset.dataInterface?.connections ?? []\n return html`\n <section>\n <h3>${i18next.t('figure.label.data-properties')}${this.help.button('figure.text.data-properties-explained')}</h3>\n ${this.help.hint('figure.text.data-properties-explained')}\n ${properties.map(p => this.renderProperty(p))}\n ${this.renderAddProperty(asset)}\n </section>\n <section>\n <h3>${i18next.t('figure.label.data-connections')}${this.help.button('figure.text.data-connections-explained')}</h3>\n ${this.help.hint('figure.text.data-connections-explained')}\n ${connections.map(c => this.renderConnection(asset, c, properties.find(p => p.id === c.property)))}\n ${properties.length ? this.renderAddConnection(asset, properties) : html`<p hint>${i18next.t('figure.text.data-no-properties')}</p>`}\n </section>\n ${properties.length ? this.renderSample(asset, properties) : nothing}\n `\n }\n\n private renderProperty(p: FigureDataProperty) {\n const set = (patch: Partial<FigureDataProperty>) => this.edit({ kind: 'set-data-property', property: { ...p, ...patch } as FigureDataProperty })\n const value = (e: Event) => (e.target as HTMLInputElement | HTMLSelectElement).value\n const number = (e: Event) => (e.target as HTMLInputElement).valueAsNumber\n return html`<section item>\n <div head>\n <strong>${p.label} <small>(${i18next.t('figure.label.data-kind-' + p.kind)})</small></strong>\n <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-data-property')} @click=${() => this.edit({ kind: 'remove-data-property', id: p.id })}>\n <md-icon>close</md-icon>\n </button>\n </div>\n <div form>\n <span>${i18next.t('figure.label.data-id')}</span>\n <input .value=${p.id} ?disabled=${this.locked} @change=${(e: Event) => value(e).trim() !== p.id && this.edit({ kind: 'rename-data-property', id: p.id, to: value(e).trim() })} />\n <span>${i18next.t('figure.label.name')}</span>\n <input .value=${p.label} ?disabled=${this.locked} @change=${(e: Event) => value(e).trim() && set({ label: value(e).trim() })} />\n ${p.kind === 'number'\n ? html`<span>${i18next.t('figure.label.data-unit')}</span>\n <select ?disabled=${this.locked} @change=${(e: Event) => set({ unit: value(e) as never })}>\n ${FIGURE_UNITS.map(u => html`<option value=${u} ?selected=${u === p.unit}>${u}</option>`)}\n </select>\n <span>${i18next.t('figure.label.data-min')}</span>\n <input type=\"number\" step=\"any\" .value=${String(p.min)} ?disabled=${this.locked} @change=${(e: Event) => set({ min: number(e) })} />\n <span>${i18next.t('figure.label.data-max')}</span>\n <input type=\"number\" step=\"any\" .value=${String(p.max)} ?disabled=${this.locked} @change=${(e: Event) => set({ max: number(e) })} />\n <span>${i18next.t('figure.label.data-default')}</span>\n <input type=\"number\" step=\"any\" .value=${String(p.default)} ?disabled=${this.locked} @change=${(e: Event) => set({ default: number(e) })} />`\n : p.kind === 'boolean'\n ? html`<span>${i18next.t('figure.label.data-default')}</span>\n <select ?disabled=${this.locked} @change=${(e: Event) => set({ default: value(e) === 'true' })}>\n ${[false, true].map(b => html`<option value=${String(b)} ?selected=${b === p.default}>${i18next.t(b ? 'figure.label.data-true' : 'figure.label.data-false')}</option>`)}\n </select>`\n : html`<span>${i18next.t('figure.label.data-values')}</span>\n <input\n .value=${p.values.join(', ')}\n ?disabled=${this.locked}\n @change=${(e: Event) => {\n const values = value(e).split(',').map(v => v.trim()).filter(Boolean)\n set({ values, default: values.includes(p.default) ? p.default : values[0]! } as never)\n }}\n />\n <span>${i18next.t('figure.label.data-default')}</span>\n <select ?disabled=${this.locked} @change=${(e: Event) => set({ default: value(e) })}>\n ${p.values.map(v => html`<option value=${v} ?selected=${v === p.default}>${v}</option>`)}\n </select>`}\n </div>\n </section>`\n }\n\n private renderAddProperty(asset: FigureAsset) {\n const a = this.adding\n return html`<div form>\n <span>${i18next.t('figure.label.name')}</span>\n <input .value=${a.label} @input=${(e: Event) => (this.adding = { ...a, label: (e.target as HTMLInputElement).value })} />\n <span>${i18next.t('figure.label.data-kind')}</span>\n <select @change=${(e: Event) => (this.adding = { ...a, kind: (e.target as HTMLSelectElement).value as FigureDataProperty['kind'] })}>\n ${(['number', 'boolean', 'enum'] as const).map(k => html`<option value=${k} ?selected=${k === a.kind}>${i18next.t('figure.label.data-kind-' + k)}</option>`)}\n </select>\n </div>\n <button\n act\n ?disabled=${this.locked || !a.label.trim()}\n @click=${() => {\n this.edit({ kind: 'set-data-property', property: propertyDraftOf(asset, a.label.trim(), a.kind) })\n this.adding = { ...a, label: '' }\n }}\n >\n <md-icon>add</md-icon>${i18next.t('figure.button.add-data-property')}\n </button>`\n }\n\n private targetName(asset: FigureAsset, c: FigureDataConnection, property?: FigureDataProperty): string {\n const choice = property ? dataTargetsOf(asset, property.kind).find(t => t.key === targetKeyOf(c.target)) : undefined\n const name = choice?.name ?? targetKeyOf(c.target)\n return i18next.t('figure.label.data-target-' + c.target.kind, { name })\n }\n\n private renderConnection(asset: FigureAsset, c: FigureDataConnection, property?: FigureDataProperty) {\n const set = (patch: Partial<FigureDataConnection>) => this.edit({ kind: 'set-data-connection', connection: { ...c, ...patch } })\n const number = (e: Event) => (e.target as HTMLInputElement).valueAsNumber\n const t = c.transform\n return html`<section item>\n <div head>\n <strong>${property?.label ?? c.property} → ${this.targetName(asset, c, property)}</strong>\n <button act ?disabled=${this.locked} title=${i18next.t('figure.button.remove-data-connection')} @click=${() => this.edit({ kind: 'remove-data-connection', id: c.id })}>\n <md-icon>close</md-icon>\n </button>\n </div>\n <div form>\n ${c.target.kind === 'state'\n ? html`<span>${i18next.t('figure.label.data-source-mode')}</span>\n <select ?disabled=${this.locked} @change=${(e: Event) => set({ target: { ...(c.target as { kind: 'state'; input: string }), sourceMode: (e.target as HTMLSelectElement).value as 'observed' | 'commanded' } })}>\n ${(['observed', 'commanded'] as const).map(m => html`<option value=${m} ?selected=${c.target.kind === 'state' && c.target.sourceMode === m}>${i18next.t('figure.label.data-' + m)}</option>`)}\n </select>`\n : nothing}\n ${t.kind === 'lookup'\n ? nothing\n : html`<span>${i18next.t('figure.label.data-transform')}</span>\n <select ?disabled=${this.locked} @change=${(e: Event) => set({ transform: (e.target as HTMLSelectElement).value === 'affine' ? { kind: 'affine', scale: 1, offset: 0 } : { kind: 'identity' } })}>\n <option value=\"identity\" ?selected=${t.kind === 'identity'}>${i18next.t('figure.label.data-identity')}</option>\n <option value=\"affine\" ?selected=${t.kind === 'affine'}>${i18next.t('figure.label.data-affine')}</option>\n </select>`}\n ${t.kind === 'affine'\n ? html`<span>${i18next.t('figure.label.data-scale')}</span>\n <input type=\"number\" step=\"any\" .value=${String(t.scale)} ?disabled=${this.locked} @change=${(e: Event) => set({ transform: { ...t, scale: number(e) } })} />\n <span>${i18next.t('figure.label.data-offset')}</span>\n <input type=\"number\" step=\"any\" .value=${String(t.offset)} ?disabled=${this.locked} @change=${(e: Event) => set({ transform: { ...t, offset: number(e) } })} />`\n : nothing}\n </div>\n ${t.kind === 'lookup' ? this.renderCases(asset, c, set) : nothing}\n </section>`\n }\n\n /** A case per value of the property: what the target takes for it -- a number, a colour, or a clip of the group. */\n private renderCases(asset: FigureAsset, c: FigureDataConnection, set: (patch: Partial<FigureDataConnection>) => void) {\n if (c.transform.kind !== 'lookup') return nothing\n const cases = c.transform.cases\n const write = (i: number, value: FigureDataValue) => set({ transform: { kind: 'lookup', cases: cases.map((k, j) => (j === i ? { ...k, value } : k)) } })\n const clips = c.target.kind === 'motion-clip' ? ((asset.motionGroups ?? []).find(g => c.target.kind === 'motion-clip' && g.id === c.target.group)?.clips ?? []) : []\n return html`<div cases>\n ${cases.map(\n (k, i) => html`<span>${typeof k.when === 'boolean' ? i18next.t(k.when ? 'figure.label.data-true' : 'figure.label.data-false') : String(k.when)}</span>\n ${c.target.kind === 'palette'\n ? html`<input type=\"color\" .value=${String(k.value)} ?disabled=${this.locked} @change=${(e: Event) => write(i, (e.target as HTMLInputElement).value)} />`\n : c.target.kind === 'motion-clip'\n ? html`<select ?disabled=${this.locked} @change=${(e: Event) => write(i, (e.target as HTMLSelectElement).value)}>\n ${clips.map(clip => html`<option value=${clip} ?selected=${clip === k.value}>${clip}</option>`)}\n </select>`\n : html`<input type=\"number\" step=\"any\" .value=${String(k.value)} ?disabled=${this.locked} @change=${(e: Event) => write(i, (e.target as HTMLInputElement).valueAsNumber)} />`}`\n )}\n </div>`\n }\n\n private renderAddConnection(asset: FigureAsset, properties: FigureDataProperty[]) {\n const a = this.connecting\n const property = properties.find(p => p.id === a.property) ?? properties[0]!\n const targets = dataTargetsOf(asset, property.kind)\n const choice = targets.find(t => t.key === a.target) ?? targets[0]\n return html`<div form>\n <span>${i18next.t('figure.label.data-property')}</span>\n <select @change=${(e: Event) => (this.connecting = { ...a, property: (e.target as HTMLSelectElement).value })}>\n ${properties.map(p => html`<option value=${p.id} ?selected=${p === property}>${p.label}</option>`)}\n </select>\n <span>${i18next.t('figure.label.data-target')}</span>\n <select ?disabled=${!targets.length} @change=${(e: Event) => (this.connecting = { ...a, target: (e.target as HTMLSelectElement).value })}>\n ${targets.map((t: DataTargetChoice) => html`<option value=${t.key} ?selected=${t === choice}>${i18next.t('figure.label.data-target-' + t.target.kind, { name: t.name })}</option>`)}\n </select>\n </div>\n <button act ?disabled=${this.locked || !choice} @click=${() => this.edit({ kind: 'set-data-connection', connection: connectionDraftOf(asset, property, choice!) })}>\n <md-icon>add</md-icon>${i18next.t('figure.button.add-data-connection')}\n </button>`\n }\n\n /** Values to try: what they move, by figure-model's evaluation; in the preview, the figure running on them. */\n private renderSample(asset: FigureAsset, properties: FigureDataProperty[]) {\n const values = Object.fromEntries(properties.map(p => [p.id, this.sample[p.id] ?? p.default]))\n const outcome = dataSampleOf(asset, values)\n return html`<section>\n <h3>${i18next.t('figure.label.data-sample')}${this.help.button('figure.text.data-sample-explained')}</h3>\n ${this.help.hint('figure.text.data-sample-explained')}\n <div form>\n ${properties.map(\n p => html`<span>${p.label}</span>\n ${p.kind === 'number'\n ? html`<input type=\"number\" step=\"any\" .value=${String(values[p.id])} @change=${(e: Event) => this.try(p, (e.target as HTMLInputElement).value)} />`\n : html`<select @change=${(e: Event) => this.try(p, (e.target as HTMLSelectElement).value)}>\n ${domainOf(p).map(v => html`<option value=${String(v)} ?selected=${v === values[p.id]}>${typeof v === 'boolean' ? i18next.t(v ? 'figure.label.data-true' : 'figure.label.data-false') : v}</option>`)}\n </select>`}`\n )}\n </div>\n ${'issues' in outcome\n ? html`<p hint>${outcome.issues.map(i => i18next.t('figure.text.data-value-' + i.reason, { property: i.property ?? '' })).join(' · ')}</p>`\n : html`<ul result>\n ${Object.entries(outcome.result.states).map(([input, s]) => html`<li>${asset.stateInputs?.[input]?.label ?? input} = ${s.value}</li>`)}\n ${Object.entries(outcome.result.palette).map(([token, colour]) => html`<li>${token} = ${colour}</li>`)}\n ${Object.entries(outcome.result.motion).map(\n ([group, m]) => html`<li>${(asset.motionGroups ?? []).find(g => g.id === group)?.label ?? group}: ${[m.clip, m.speed === undefined ? '' : `× ${m.speed}`].filter(Boolean).join(' ')}</li>`\n )}\n </ul>`}\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${this.sampling}\n @change=${(e: Event) => {\n this.sampling = (e.target as HTMLInputElement).checked\n this.tell(values)\n }}\n />\n ${i18next.t('figure.label.data-sample-in-preview')}\n </label>\n </section>`\n }\n\n private try(property: FigureDataProperty, typed: string) {\n this.sample = { ...this.sample, [property.id]: sampleValueOf(property, typed) }\n const properties = this.asset?.dataInterface?.properties ?? []\n this.tell(Object.fromEntries(properties.map(p => [p.id, this.sample[p.id] ?? p.default])))\n }\n\n /** The sample to the preview while it is asked for; null lets the preview's figure go back to its own values. */\n private tell(values: Record<string, FigureDataValue>) {\n this.dispatchEvent(new CustomEvent('data-sample', { detail: { values: this.sampling ? values : null }, bubbles: true, composed: true }))\n }\n\n private edit(edit: unknown) {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit }, bubbles: true, composed: true }))\n }\n}\n"]}
@@ -24,6 +24,10 @@ export declare class FigurePreview extends FigurePreview_base {
24
24
  /** Clip speeds and control values given here -- the preview's, not the figure's. */
25
25
  private driving;
26
26
  private giving;
27
+ /** Per motion group, the clip played here and how fast (ADR-0051 §3.2) -- the preview's, as a board's \`motion\`. */
28
+ private moving;
29
+ /** Data values tried in the data tab: the preview's figures run on them as a live snapshot; null when none are. */
30
+ sample: Record<string, unknown> | null;
27
31
  /** The part chosen in the editor. The size help starts on it, so the help speaks of the part the author was working on. */
28
32
  selected: string;
29
33
  private setupPart;
@@ -38,11 +42,14 @@ export declare class FigurePreview extends FigurePreview_base {
38
42
  private watcher?;
39
43
  render(): import("lit-html").TemplateResult<1>;
40
44
  private renderDrivers;
45
+ private move;
41
46
  private renderDial;
42
47
  private drive;
43
48
  private give;
44
49
  private poseAsDefault;
45
50
  private tellInstances;
51
+ /** The data sample to the figures standing now: a live snapshot, transient on each, never saved with the board. */
52
+ private tellSample;
46
53
  willUpdate(changed: Map<string, unknown>): void;
47
54
  firstUpdated(): void;
48
55
  disconnectedCallback(): void;
@@ -25,6 +25,10 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
25
25
  /** Clip speeds and control values given here -- the preview's, not the figure's. */
26
26
  this.driving = {};
27
27
  this.giving = {};
28
+ /** Per motion group, the clip played here and how fast (ADR-0051 §3.2) -- the preview's, as a board's \`motion\`. */
29
+ this.moving = {};
30
+ /** Data values tried in the data tab: the preview's figures run on them as a live snapshot; null when none are. */
31
+ this.sample = null;
28
32
  /** The part chosen in the editor. The size help starts on it, so the help speaks of the part the author was working on. */
29
33
  this.selected = '';
30
34
  this.setupPart = '';
@@ -176,7 +180,9 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
176
180
  render() {
177
181
  const asset = this.asset;
178
182
  const controls = asset ? asset.document.model.inputs.filter(i => i.role === 'state') : [];
179
- const clips = asset ? [...new Set((asset.drivers ?? []).map(d => d.clip))] : [];
183
+ /* Clips in a motion group are played by the group's choice of clip, not each by its own speed. */
184
+ const grouped = new Set((asset?.motionGroups ?? []).flatMap(g => g.clips));
185
+ const clips = asset ? [...new Set((asset.drivers ?? []).map(d => d.clip))].filter(c => !grouped.has(c)) : [];
180
186
  let clampsByWay = {};
181
187
  try {
182
188
  clampsByWay = asset ? previewClampsOf(asset, this.authoredSize()) : {};
@@ -207,7 +213,7 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
207
213
  </select>
208
214
  <button @click=${() => this.dispatchEvent(new CustomEvent('configure-size', { detail: { part: setupPart }, bubbles: true, composed: true }))}>${i18next.t('figure.button.configure-size')}</button>
209
215
  </div>` : nothing}
210
- ${controls.length + clips.length ? this.renderDrivers(asset, controls, clips) : nothing}</div>`}`;
216
+ ${controls.length + clips.length + (asset?.motionGroups?.length ?? 0) ? this.renderDrivers(asset, controls, clips) : nothing}</div>`}`;
211
217
  }
212
218
  renderDrivers(asset, controls, clips) {
213
219
  const changed = controls.filter(c => this.giving[c.id] !== undefined && this.giving[c.id] !== asset.stateDefaults[c.id]);
@@ -248,8 +254,23 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
248
254
  <output>${value.toFixed(2)}</output>
249
255
  </label>`;
250
256
  })}
257
+ ${(asset.motionGroups ?? []).map(group => {
258
+ const playing = this.moving[group.id] ?? { clip: group.defaultClip, speed: 0 };
259
+ return html `<label>
260
+ <span>${group.label} <span drive>${i18next.t('figure.label.playback-speed')}</span></span>
261
+ <select aria-label=${group.label} @change=${(e) => this.move(group.id, { ...playing, clip: e.target.value })}>
262
+ ${group.clips.map(c => html `<option value=${c} ?selected=${c === playing.clip}>${c}</option>`)}
263
+ </select>
264
+ <input type="range" min="0" max="3" step="0.05" .value=${String(playing.speed)} @input=${(e) => this.move(group.id, { ...playing, speed: Number(e.target.value) })} />
265
+ <output>${playing.speed.toFixed(2)}</output>
266
+ </label>`;
267
+ })}
251
268
  </div>`;
252
269
  }
270
+ move(group, playing) {
271
+ this.moving = { ...this.moving, [group]: playing };
272
+ this.tellInstances('motion', Object.fromEntries(Object.entries(this.moving).map(([id, m]) => [id, { ...m, paused: false }])));
273
+ }
253
274
  renderDial(control, value) {
254
275
  if (control.unit !== 'deg')
255
276
  return html `<span></span>`;
@@ -274,10 +295,23 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
274
295
  this.giving = {};
275
296
  }
276
297
  tellInstances(key, values) {
277
- for (const component of this.scene?.root?.components ?? [])
278
- component.set?.(key, { ...values });
298
+ for (const component of this.scene?.root?.components ?? []) {
299
+ const value = values === null ? null : { ...values };
300
+ /* A live snapshot is transient on the figure (its \`figureData\` accessor), never written into the saved model. */
301
+ if (key === 'figureData')
302
+ component.figureData = value;
303
+ else
304
+ component.set?.(key, value);
305
+ }
306
+ }
307
+ /** The data sample to the figures standing now: a live snapshot, transient on each, never saved with the board. */
308
+ tellSample() {
309
+ if (this.sample)
310
+ this.tellInstances('figureData', { ...this.sample });
279
311
  }
280
312
  willUpdate(changed) {
313
+ if (changed.has('sample') && this.scene)
314
+ this.tellInstances('figureData', this.sample ? { ...this.sample } : null);
281
315
  if (!this.hasUpdated)
282
316
  return;
283
317
  if (!this.active) {
@@ -349,6 +383,7 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
349
383
  if (!again)
350
384
  this.teardown();
351
385
  this.driving = {};
386
+ this.moving = {};
352
387
  registerFigure(type, asset);
353
388
  this.registered = type;
354
389
  const { components, board } = previewInstances(type, size);
@@ -370,6 +405,7 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
370
405
  this.scene.rootContainer.rebuildContentModel(model, { preserveThreeD: true });
371
406
  this.standing = signature;
372
407
  this.cannot = '';
408
+ this.tellSample();
373
409
  return;
374
410
  }
375
411
  catch {
@@ -388,6 +424,7 @@ let FigurePreview = class FigurePreview extends localize(i18next)(LitElement) {
388
424
  this.scene.fit?.('ratio');
389
425
  this.scene.root?.fitCameraToModel?.();
390
426
  this.cannot = '';
427
+ this.tellSample();
391
428
  }
392
429
  catch (error) {
393
430
  this.cannot = error.message;
@@ -434,6 +471,14 @@ __decorate([
434
471
  state(),
435
472
  __metadata("design:type", Object)
436
473
  ], FigurePreview.prototype, "giving", void 0);
474
+ __decorate([
475
+ state(),
476
+ __metadata("design:type", Object)
477
+ ], FigurePreview.prototype, "moving", void 0);
478
+ __decorate([
479
+ property({ attribute: false }),
480
+ __metadata("design:type", Object)
481
+ ], FigurePreview.prototype, "sample", void 0);
437
482
  __decorate([
438
483
  property(),
439
484
  __metadata("design:type", Object)