@things-factory/figure-library 10.1.223 → 10.1.224
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/dist-client/bootstrap.d.ts +8 -5
- package/dist-client/bootstrap.js +12 -6
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/editors/ox-property-editor-figure-legends.d.ts +34 -0
- package/dist-client/editors/ox-property-editor-figure-legends.js +236 -0
- package/dist-client/editors/ox-property-editor-figure-legends.js.map +1 -0
- package/dist-client/figure-data-report.d.ts +10 -5
- package/dist-client/figure-data-report.js +15 -5
- package/dist-client/figure-data-report.js.map +1 -1
- package/dist-client/legend-looks/legend-looks-editor.d.ts +42 -0
- package/dist-client/legend-looks/legend-looks-editor.js +145 -0
- package/dist-client/legend-looks/legend-looks-editor.js.map +1 -0
- package/dist-client/legend-looks/legend-looks.d.ts +55 -0
- package/dist-client/legend-looks/legend-looks.js +110 -0
- package/dist-client/legend-looks/legend-looks.js.map +1 -0
- package/dist-client/legend-looks/ox-legend-looks.d.ts +57 -0
- package/dist-client/{status-looks/ox-figure-status-looks.js → legend-looks/ox-legend-looks.js} +128 -109
- package/dist-client/legend-looks/ox-legend-looks.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/translations/en.json +43 -43
- package/translations/es.json +43 -43
- package/translations/ja.json +43 -43
- package/translations/ko.json +43 -43
- package/translations/ms.json +43 -43
- package/translations/zh.json +43 -43
- package/dist-client/editors/ox-property-editor-figure-status.d.ts +0 -41
- package/dist-client/editors/ox-property-editor-figure-status.js +0 -290
- package/dist-client/editors/ox-property-editor-figure-status.js.map +0 -1
- package/dist-client/status-looks/ox-figure-status-looks.d.ts +0 -58
- package/dist-client/status-looks/ox-figure-status-looks.js.map +0 -1
- package/dist-client/status-looks/status-looks.d.ts +0 -99
- package/dist-client/status-looks/status-looks.js +0 -134
- package/dist-client/status-looks/status-looks.js.map +0 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@material/web/icon/icon.js';
|
|
3
|
+
import './ox-legend-looks.js';
|
|
4
|
+
import { css, html, LitElement } from 'lit';
|
|
5
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
6
|
+
import { OxGristEditor } from '@operato/data-grist';
|
|
7
|
+
import { i18next } from '@operato/i18n';
|
|
8
|
+
import { closePopup, openPopup } from '@operato/popup';
|
|
9
|
+
import { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles';
|
|
10
|
+
import { legendLooksOf, legendOf } from './legend-looks.js';
|
|
11
|
+
/**
|
|
12
|
+
* A legend theme's value, edited look by look (ADR-0166 11): the board's 「테마」 list opens this for its legend kinds --
|
|
13
|
+
* a code map (`color-map`, `animation-map`) or a range map (`color-ranges`) -- when this module is loaded. Each code's
|
|
14
|
+
* colour, effect, values for named state inputs, and a motion group's clip, speed and pause; what else an entry holds
|
|
15
|
+
* is kept. A look that is a colour alone is written as the colour, as a colour map keeps it.
|
|
16
|
+
*/
|
|
17
|
+
let LegendLooksEditor = class LegendLooksEditor extends OxGristEditor {
|
|
18
|
+
get editorTemplate() {
|
|
19
|
+
return html `<div tabindex="0">${i18next.t('figure.label.legend-count', { count: Object.keys(legendLooksOf(this.value)).length })}</div>`;
|
|
20
|
+
}
|
|
21
|
+
_onclick(e) {
|
|
22
|
+
e.stopPropagation();
|
|
23
|
+
this.openLooks();
|
|
24
|
+
}
|
|
25
|
+
_onkeydown(e) {
|
|
26
|
+
if (e.key == 'Enter') {
|
|
27
|
+
e.stopPropagation();
|
|
28
|
+
this.openLooks();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** The value written back as it came -- an object, or text for a store that keeps text. */
|
|
32
|
+
openLooks() {
|
|
33
|
+
delete this.popup;
|
|
34
|
+
const asText = typeof this.value === 'string';
|
|
35
|
+
/* The popup is drawn again from its first binding when the layout redraws: the looks being edited live in `draft`. */
|
|
36
|
+
const draft = { looks: legendLooksOf(this.value) };
|
|
37
|
+
const confirmCallback = (looks) => {
|
|
38
|
+
const after = legendOf(looks);
|
|
39
|
+
this.dispatchEvent(new CustomEvent('field-change', {
|
|
40
|
+
bubbles: true,
|
|
41
|
+
composed: true,
|
|
42
|
+
detail: { before: this.value, after: asText ? JSON.stringify(after) : after, record: this.record, column: this.column, row: this.row }
|
|
43
|
+
}));
|
|
44
|
+
};
|
|
45
|
+
this.popup = openPopup(html `<legend-looks-popup .draft=${draft} .confirmCallback=${confirmCallback}></legend-looks-popup>`, {
|
|
46
|
+
backdrop: true,
|
|
47
|
+
size: 'large',
|
|
48
|
+
title: `${i18next.t('figure.label.legend-edit')} : ${this.record?.name ?? ''}`
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
LegendLooksEditor = __decorate([
|
|
53
|
+
customElement('legend-looks-editor')
|
|
54
|
+
], LegendLooksEditor);
|
|
55
|
+
export { LegendLooksEditor };
|
|
56
|
+
let LegendLooksPopup = class LegendLooksPopup extends LitElement {
|
|
57
|
+
constructor() {
|
|
58
|
+
super(...arguments);
|
|
59
|
+
/** Inputs offered for a value, with name, unit and range (fmsim: those its carrier figures' tables set). */
|
|
60
|
+
this.addable = [];
|
|
61
|
+
/**
|
|
62
|
+
* Colour only: no effect, no values, no motion -- fmsim's carrier legend, whose figures take the thickness from their own
|
|
63
|
+
* fixed table (ADR-0167 12). What an entry already holds is kept.
|
|
64
|
+
*/
|
|
65
|
+
this.colourOnly = false;
|
|
66
|
+
}
|
|
67
|
+
static { this.styles = [
|
|
68
|
+
CommonHeaderStyles,
|
|
69
|
+
ScrollbarStyles,
|
|
70
|
+
css `
|
|
71
|
+
:host {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
background-color: var(--md-sys-color-surface);
|
|
75
|
+
width: var(--overlay-center-normal-width, 50%);
|
|
76
|
+
height: var(--overlay-center-normal-height, 50%);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
ox-legend-looks {
|
|
80
|
+
flex: 1;
|
|
81
|
+
overflow-y: auto;
|
|
82
|
+
padding: var(--spacing-medium, 8px);
|
|
83
|
+
align-content: start;
|
|
84
|
+
}
|
|
85
|
+
`
|
|
86
|
+
]; }
|
|
87
|
+
render() {
|
|
88
|
+
return html `
|
|
89
|
+
<ox-legend-looks
|
|
90
|
+
.looks=${this.draft.looks}
|
|
91
|
+
.addable=${this.addable}
|
|
92
|
+
default-color=${this.defaultColor ?? ''}
|
|
93
|
+
show-given
|
|
94
|
+
?name-inputs=${!this.addable.length}
|
|
95
|
+
?no-values=${this.colourOnly}
|
|
96
|
+
?effects=${!this.colourOnly}
|
|
97
|
+
?motion=${!this.colourOnly}
|
|
98
|
+
?color-optional=${!this.colourOnly}
|
|
99
|
+
@looks-change=${(e) => {
|
|
100
|
+
e.stopPropagation();
|
|
101
|
+
this.draft.looks = e.detail.looks;
|
|
102
|
+
}}
|
|
103
|
+
></ox-legend-looks>
|
|
104
|
+
|
|
105
|
+
<div class="footer">
|
|
106
|
+
<div filler></div>
|
|
107
|
+
<button @click=${() => closePopup(this)} danger><md-icon>cancel</md-icon>${i18next.t('button.cancel')}</button>
|
|
108
|
+
<button
|
|
109
|
+
@click=${() => {
|
|
110
|
+
this.confirmCallback?.(this.draft.looks);
|
|
111
|
+
closePopup(this);
|
|
112
|
+
}}
|
|
113
|
+
done
|
|
114
|
+
>
|
|
115
|
+
<md-icon>done</md-icon>${i18next.t('button.confirm')}
|
|
116
|
+
</button>
|
|
117
|
+
</div>
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
__decorate([
|
|
122
|
+
property({ type: Object }),
|
|
123
|
+
__metadata("design:type", Object)
|
|
124
|
+
], LegendLooksPopup.prototype, "draft", void 0);
|
|
125
|
+
__decorate([
|
|
126
|
+
property({ type: Object }),
|
|
127
|
+
__metadata("design:type", Function)
|
|
128
|
+
], LegendLooksPopup.prototype, "confirmCallback", void 0);
|
|
129
|
+
__decorate([
|
|
130
|
+
property({ type: Array }),
|
|
131
|
+
__metadata("design:type", Array)
|
|
132
|
+
], LegendLooksPopup.prototype, "addable", void 0);
|
|
133
|
+
__decorate([
|
|
134
|
+
property({ type: Boolean, attribute: 'colour-only' }),
|
|
135
|
+
__metadata("design:type", Object)
|
|
136
|
+
], LegendLooksPopup.prototype, "colourOnly", void 0);
|
|
137
|
+
__decorate([
|
|
138
|
+
property({ type: String, attribute: 'default-color' }),
|
|
139
|
+
__metadata("design:type", String)
|
|
140
|
+
], LegendLooksPopup.prototype, "defaultColor", void 0);
|
|
141
|
+
LegendLooksPopup = __decorate([
|
|
142
|
+
customElement('legend-looks-popup')
|
|
143
|
+
], LegendLooksPopup);
|
|
144
|
+
export { LegendLooksPopup };
|
|
145
|
+
//# sourceMappingURL=legend-looks-editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legend-looks-editor.js","sourceRoot":"","sources":["../../client/legend-looks/legend-looks-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport './ox-legend-looks.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { OxGristEditor } from '@operato/data-grist'\nimport { i18next } from '@operato/i18n'\nimport { closePopup, openPopup, type PopupHandle } from '@operato/popup'\nimport { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'\n\nimport { legendLooksOf, legendOf, type LegendLooks, type LookInput } from './legend-looks.js'\n\n/**\n * A legend theme's value, edited look by look (ADR-0166 11): the board's 「테마」 list opens this for its legend kinds --\n * a code map (`color-map`, `animation-map`) or a range map (`color-ranges`) -- when this module is loaded. Each code's\n * colour, effect, values for named state inputs, and a motion group's clip, speed and pause; what else an entry holds\n * is kept. A look that is a colour alone is written as the colour, as a colour map keeps it.\n */\n@customElement('legend-looks-editor')\nexport class LegendLooksEditor extends OxGristEditor {\n private popup?: PopupHandle\n\n get editorTemplate() {\n return html`<div tabindex=\"0\">${i18next.t('figure.label.legend-count', { count: Object.keys(legendLooksOf(this.value)).length })}</div>`\n }\n\n _onclick(e: Event): void {\n e.stopPropagation()\n this.openLooks()\n }\n\n _onkeydown(e: KeyboardEvent): void {\n if (e.key == 'Enter') {\n e.stopPropagation()\n this.openLooks()\n }\n }\n\n /** The value written back as it came -- an object, or text for a store that keeps text. */\n openLooks() {\n delete this.popup\n const asText = typeof this.value === 'string'\n /* The popup is drawn again from its first binding when the layout redraws: the looks being edited live in `draft`. */\n const draft = { looks: legendLooksOf(this.value) }\n const confirmCallback = (looks: LegendLooks) => {\n const after = legendOf(looks)\n this.dispatchEvent(\n new CustomEvent('field-change', {\n bubbles: true,\n composed: true,\n detail: { before: this.value, after: asText ? JSON.stringify(after) : after, record: this.record, column: this.column, row: this.row }\n })\n )\n }\n this.popup = openPopup(html`<legend-looks-popup .draft=${draft} .confirmCallback=${confirmCallback}></legend-looks-popup>`, {\n backdrop: true,\n size: 'large',\n title: `${i18next.t('figure.label.legend-edit')} : ${this.record?.name ?? ''}`\n })\n }\n}\n\n@customElement('legend-looks-popup')\nexport class LegendLooksPopup extends LitElement {\n static styles = [\n CommonHeaderStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n background-color: var(--md-sys-color-surface);\n width: var(--overlay-center-normal-width, 50%);\n height: var(--overlay-center-normal-height, 50%);\n }\n\n ox-legend-looks {\n flex: 1;\n overflow-y: auto;\n padding: var(--spacing-medium, 8px);\n align-content: start;\n }\n `\n ]\n\n @property({ type: Object }) draft!: { looks: LegendLooks }\n @property({ type: Object }) confirmCallback!: (looks: LegendLooks) => void\n /** Inputs offered for a value, with name, unit and range (fmsim: those its carrier figures' tables set). */\n @property({ type: Array }) addable: LookInput[] = []\n /**\n * Colour only: no effect, no values, no motion -- fmsim's carrier legend, whose figures take the thickness from their own\n * fixed table (ADR-0167 12). What an entry already holds is kept.\n */\n @property({ type: Boolean, attribute: 'colour-only' }) colourOnly = false\n /** The colour a new code starts with: a colour map's entries have one. */\n @property({ type: String, attribute: 'default-color' }) defaultColor?: string\n\n render() {\n return html`\n <ox-legend-looks\n .looks=${this.draft.looks}\n .addable=${this.addable}\n default-color=${this.defaultColor ?? ''}\n show-given\n ?name-inputs=${!this.addable.length}\n ?no-values=${this.colourOnly}\n ?effects=${!this.colourOnly}\n ?motion=${!this.colourOnly}\n ?color-optional=${!this.colourOnly}\n @looks-change=${(e: CustomEvent<{ looks: LegendLooks }>) => {\n e.stopPropagation()\n this.draft.looks = e.detail.looks\n }}\n ></ox-legend-looks>\n\n <div class=\"footer\">\n <div filler></div>\n <button @click=${() => closePopup(this)} danger><md-icon>cancel</md-icon>${i18next.t('button.cancel')}</button>\n <button\n @click=${() => {\n this.confirmCallback?.(this.draft.looks)\n closePopup(this)\n }}\n done\n >\n <md-icon>done</md-icon>${i18next.t('button.confirm')}\n </button>\n </div>\n `\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'legend-looks-editor': LegendLooksEditor\n 'legend-looks-popup': LegendLooksPopup\n }\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { FigureAsset, FigureDataTableBinding } from '@hatiolab/figure-model';
|
|
2
|
+
export interface LegendLookEffect {
|
|
3
|
+
kind: 'blink' | 'glow';
|
|
4
|
+
hz?: number;
|
|
5
|
+
}
|
|
6
|
+
/** One code's look. Any part may be missing; a value is in its input's own unit. Other fields of an entry ride along. */
|
|
7
|
+
export interface LegendLook {
|
|
8
|
+
color?: string;
|
|
9
|
+
effect?: LegendLookEffect | null;
|
|
10
|
+
values?: Record<string, number>;
|
|
11
|
+
clip?: string;
|
|
12
|
+
speed?: number;
|
|
13
|
+
paused?: boolean;
|
|
14
|
+
[other: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
/** A legend's looks: code → look, `default` for a code it does not name. */
|
|
17
|
+
export type LegendLooks = Record<string, LegendLook>;
|
|
18
|
+
/** A state input a legend may give a value to: the input, its name, unit and range, and the figures' tables that take it. */
|
|
19
|
+
export interface LookInput {
|
|
20
|
+
input: string;
|
|
21
|
+
label: string;
|
|
22
|
+
unit?: string;
|
|
23
|
+
min?: number;
|
|
24
|
+
max?: number;
|
|
25
|
+
/** What gives it, as the choice says it: the figures and the tables. */
|
|
26
|
+
givenBy?: string;
|
|
27
|
+
}
|
|
28
|
+
/** A legend's value as looks: an entry that is a colour is that colour; one that is a record is itself; anything else reads as none. */
|
|
29
|
+
export declare function legendLooksOf(value: unknown): LegendLooks;
|
|
30
|
+
/** Looks as a legend keeps them: a look that is a colour alone is the colour, as a colour map has it; the rest whole. */
|
|
31
|
+
export declare function legendOf(looks: LegendLooks): Record<string, unknown>;
|
|
32
|
+
/**
|
|
33
|
+
* One code's look with some parts changed: a part given as undefined is taken away, so the legend no longer gives it.
|
|
34
|
+
* A look left with nothing stays, as a code the legend names.
|
|
35
|
+
*/
|
|
36
|
+
export declare function withLook(looks: LegendLooks, code: string, patch: Partial<LegendLook>): LegendLooks;
|
|
37
|
+
/** One value of one code's look: a number sets it, undefined takes it away. Other inputs' values stay as they were. */
|
|
38
|
+
export declare function withLookValue(looks: LegendLooks, code: string, input: string, value: number | undefined): LegendLooks;
|
|
39
|
+
export declare function withCode(looks: LegendLooks, code: string): LegendLooks;
|
|
40
|
+
export declare function withoutCode(looks: LegendLooks, code: string): LegendLooks;
|
|
41
|
+
/** The inputs some look gives a value to, in the order first met. */
|
|
42
|
+
export declare function inputsGivenIn(looks: LegendLooks): string[];
|
|
43
|
+
/** The tables of a figure a legend may take the place of: those naming a legend role, each with its role. */
|
|
44
|
+
export declare const legendTablesOf: (asset: FigureAsset) => FigureDataTableBinding[];
|
|
45
|
+
/**
|
|
46
|
+
* The state inputs a legend of a role may give values to on this figure: each input a table of that role sets, with its
|
|
47
|
+
* name, unit and range. `role` left out: every table that names a role.
|
|
48
|
+
*/
|
|
49
|
+
export declare function legendInputsOf(asset: FigureAsset, role?: string): LookInput[];
|
|
50
|
+
/**
|
|
51
|
+
* Which kind of legend fits a table (ADR-0166 7): a code table takes a code map (`color-map`, `animation-map`), a range
|
|
52
|
+
* table a range map (`color-ranges`). A legend whose kind the scene does not say is told by its keys: every key but
|
|
53
|
+
* `default` an interval `from~to` is a range map.
|
|
54
|
+
*/
|
|
55
|
+
export declare function legendFits(lookup: 'code' | 'ranges', legend: unknown): boolean;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const isRecord = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
|
|
2
|
+
const isColour = (v) => typeof v === 'string' && /^#[0-9a-f]{6}$/i.test(v);
|
|
3
|
+
/** A legend's value as looks: an entry that is a colour is that colour; one that is a record is itself; anything else reads as none. */
|
|
4
|
+
export function legendLooksOf(value) {
|
|
5
|
+
let v = value;
|
|
6
|
+
if (typeof v === 'string') {
|
|
7
|
+
try {
|
|
8
|
+
v = JSON.parse(v);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
if (!isRecord(v))
|
|
15
|
+
return {};
|
|
16
|
+
const out = {};
|
|
17
|
+
for (const [code, entry] of Object.entries(v)) {
|
|
18
|
+
if (isColour(entry))
|
|
19
|
+
out[code] = { color: entry };
|
|
20
|
+
else if (isRecord(entry))
|
|
21
|
+
out[code] = { ...entry };
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
/** Looks as a legend keeps them: a look that is a colour alone is the colour, as a colour map has it; the rest whole. */
|
|
26
|
+
export function legendOf(looks) {
|
|
27
|
+
return Object.fromEntries(Object.entries(looks).map(([code, look]) => [code, Object.keys(look).length === 1 && isColour(look.color) ? look.color : look]));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* One code's look with some parts changed: a part given as undefined is taken away, so the legend no longer gives it.
|
|
31
|
+
* A look left with nothing stays, as a code the legend names.
|
|
32
|
+
*/
|
|
33
|
+
export function withLook(looks, code, patch) {
|
|
34
|
+
const look = { ...(looks[code] ?? {}) };
|
|
35
|
+
for (const [part, value] of Object.entries(patch)) {
|
|
36
|
+
if (value === undefined)
|
|
37
|
+
delete look[part];
|
|
38
|
+
else
|
|
39
|
+
look[part] = value;
|
|
40
|
+
}
|
|
41
|
+
return { ...looks, [code]: look };
|
|
42
|
+
}
|
|
43
|
+
/** One value of one code's look: a number sets it, undefined takes it away. Other inputs' values stay as they were. */
|
|
44
|
+
export function withLookValue(looks, code, input, value) {
|
|
45
|
+
const held = looks[code]?.values;
|
|
46
|
+
const values = isRecord(held) ? { ...held } : {};
|
|
47
|
+
if (value === undefined)
|
|
48
|
+
delete values[input];
|
|
49
|
+
else
|
|
50
|
+
values[input] = value;
|
|
51
|
+
return withLook(looks, code, { values: Object.keys(values).length ? values : undefined });
|
|
52
|
+
}
|
|
53
|
+
export function withCode(looks, code) {
|
|
54
|
+
return code in looks ? looks : { ...looks, [code]: {} };
|
|
55
|
+
}
|
|
56
|
+
export function withoutCode(looks, code) {
|
|
57
|
+
const { [code]: _gone, ...rest } = looks;
|
|
58
|
+
return rest;
|
|
59
|
+
}
|
|
60
|
+
/** The inputs some look gives a value to, in the order first met. */
|
|
61
|
+
export function inputsGivenIn(looks) {
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
for (const look of Object.values(looks))
|
|
64
|
+
if (isRecord(look.values))
|
|
65
|
+
for (const input of Object.keys(look.values))
|
|
66
|
+
seen.add(input);
|
|
67
|
+
return [...seen];
|
|
68
|
+
}
|
|
69
|
+
/** The tables of a figure a legend may take the place of: those naming a legend role, each with its role. */
|
|
70
|
+
export const legendTablesOf = (asset) => (asset.dataInterface?.bindings ?? []).filter((b) => b.rule === 'table' && !!b.legend);
|
|
71
|
+
/**
|
|
72
|
+
* The state inputs a legend of a role may give values to on this figure: each input a table of that role sets, with its
|
|
73
|
+
* name, unit and range. `role` left out: every table that names a role.
|
|
74
|
+
*/
|
|
75
|
+
export function legendInputsOf(asset, role) {
|
|
76
|
+
const declared = new Map(asset.document.model.inputs.map(i => [i.id, i]));
|
|
77
|
+
const out = [];
|
|
78
|
+
for (const b of legendTablesOf(asset)) {
|
|
79
|
+
if (role !== undefined && b.legend.role !== role)
|
|
80
|
+
continue;
|
|
81
|
+
for (const input of b.targets.inputs ?? []) {
|
|
82
|
+
if (out.some(o => o.input === input))
|
|
83
|
+
continue;
|
|
84
|
+
const d = declared.get(input);
|
|
85
|
+
out.push({
|
|
86
|
+
input,
|
|
87
|
+
label: asset.stateInputs?.[input]?.label ?? input,
|
|
88
|
+
...(d?.unit !== undefined ? { unit: d.unit } : {}),
|
|
89
|
+
...(d?.min !== undefined ? { min: d.min } : {}),
|
|
90
|
+
...(d?.max !== undefined ? { max: d.max } : {}),
|
|
91
|
+
givenBy: b.label ?? b.accessor
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Which kind of legend fits a table (ADR-0166 7): a code table takes a code map (`color-map`, `animation-map`), a range
|
|
99
|
+
* table a range map (`color-ranges`). A legend whose kind the scene does not say is told by its keys: every key but
|
|
100
|
+
* `default` an interval `from~to` is a range map.
|
|
101
|
+
*/
|
|
102
|
+
export function legendFits(lookup, legend) {
|
|
103
|
+
const looks = legendLooksOf(legend);
|
|
104
|
+
const keys = Object.keys(looks).filter(k => k !== 'default');
|
|
105
|
+
if (!Object.keys(looks).length)
|
|
106
|
+
return false;
|
|
107
|
+
const ranged = keys.length > 0 && keys.every(k => /^-?[\d.]*~-?[\d.]*$/.test(k));
|
|
108
|
+
return lookup === 'ranges' ? ranged || keys.length === 0 : !ranged;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=legend-looks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legend-looks.js","sourceRoot":"","sources":["../../client/legend-looks/legend-looks.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * A legend as a person reads and writes it (ADR-0166 7, 11): a board-service theme record -- code (or interval) → look --\n * that a consumer may give a figure's table of the same role in its place. A look holds the columns of a table's\n * entries: a colour, an effect, a value per state input, a clip, a speed, a pause. A colour map keeps a look that is a\n * colour alone as the colour itself, so a 2D reader of the map reads it as before; what else an entry holds (fmsim's 2D\n * animation) is kept as it is.\n *\n * Nothing here decides how a look is taken: things-scene looks a code up (`resolveLegend`, `resolveLegendEntries`) and\n * says what it did not take on the figure's inputs. Kept apart from the elements so it is tested without a board.\n */\nimport type { FigureAsset, FigureDataTableBinding } from '@hatiolab/figure-model'\n\nexport interface LegendLookEffect {\n kind: 'blink' | 'glow'\n hz?: number\n}\n\n/** One code's look. Any part may be missing; a value is in its input's own unit. Other fields of an entry ride along. */\nexport interface LegendLook {\n color?: string\n effect?: LegendLookEffect | null\n values?: Record<string, number>\n clip?: string\n speed?: number\n paused?: boolean\n [other: string]: unknown\n}\n\n/** A legend's looks: code → look, `default` for a code it does not name. */\nexport type LegendLooks = Record<string, LegendLook>\n\n/** A state input a legend may give a value to: the input, its name, unit and range, and the figures' tables that take it. */\nexport interface LookInput {\n input: string\n label: string\n unit?: string\n min?: number\n max?: number\n /** What gives it, as the choice says it: the figures and the tables. */\n givenBy?: string\n}\n\nconst isRecord = (v: unknown): v is Record<string, unknown> => !!v && typeof v === 'object' && !Array.isArray(v)\nconst isColour = (v: unknown): v is string => typeof v === 'string' && /^#[0-9a-f]{6}$/i.test(v)\n\n/** A legend's value as looks: an entry that is a colour is that colour; one that is a record is itself; anything else reads as none. */\nexport function legendLooksOf(value: unknown): LegendLooks {\n let v = value\n if (typeof v === 'string') {\n try {\n v = JSON.parse(v)\n } catch {\n return {}\n }\n }\n if (!isRecord(v)) return {}\n const out: LegendLooks = {}\n for (const [code, entry] of Object.entries(v)) {\n if (isColour(entry)) out[code] = { color: entry }\n else if (isRecord(entry)) out[code] = { ...entry }\n }\n return out\n}\n\n/** Looks as a legend keeps them: a look that is a colour alone is the colour, as a colour map has it; the rest whole. */\nexport function legendOf(looks: LegendLooks): Record<string, unknown> {\n return Object.fromEntries(Object.entries(looks).map(([code, look]) => [code, Object.keys(look).length === 1 && isColour(look.color) ? look.color : look]))\n}\n\n/**\n * One code's look with some parts changed: a part given as undefined is taken away, so the legend no longer gives it.\n * A look left with nothing stays, as a code the legend names.\n */\nexport function withLook(looks: LegendLooks, code: string, patch: Partial<LegendLook>): LegendLooks {\n const look: LegendLook = { ...(looks[code] ?? {}) }\n for (const [part, value] of Object.entries(patch)) {\n if (value === undefined) delete look[part]\n else look[part] = value\n }\n return { ...looks, [code]: look }\n}\n\n/** One value of one code's look: a number sets it, undefined takes it away. Other inputs' values stay as they were. */\nexport function withLookValue(looks: LegendLooks, code: string, input: string, value: number | undefined): LegendLooks {\n const held = looks[code]?.values\n const values: Record<string, unknown> = isRecord(held) ? { ...held } : {}\n if (value === undefined) delete values[input]\n else values[input] = value\n return withLook(looks, code, { values: Object.keys(values).length ? (values as Record<string, number>) : undefined })\n}\n\nexport function withCode(looks: LegendLooks, code: string): LegendLooks {\n return code in looks ? looks : { ...looks, [code]: {} }\n}\n\nexport function withoutCode(looks: LegendLooks, code: string): LegendLooks {\n const { [code]: _gone, ...rest } = looks\n return rest\n}\n\n/** The inputs some look gives a value to, in the order first met. */\nexport function inputsGivenIn(looks: LegendLooks): string[] {\n const seen = new Set<string>()\n for (const look of Object.values(looks)) if (isRecord(look.values)) for (const input of Object.keys(look.values)) seen.add(input)\n return [...seen]\n}\n\n/** The tables of a figure a legend may take the place of: those naming a legend role, each with its role. */\nexport const legendTablesOf = (asset: FigureAsset): FigureDataTableBinding[] =>\n (asset.dataInterface?.bindings ?? []).filter((b): b is FigureDataTableBinding => b.rule === 'table' && !!b.legend)\n\n/**\n * The state inputs a legend of a role may give values to on this figure: each input a table of that role sets, with its\n * name, unit and range. `role` left out: every table that names a role.\n */\nexport function legendInputsOf(asset: FigureAsset, role?: string): LookInput[] {\n const declared = new Map(asset.document.model.inputs.map(i => [i.id, i] as const))\n const out: LookInput[] = []\n for (const b of legendTablesOf(asset)) {\n if (role !== undefined && b.legend!.role !== role) continue\n for (const input of b.targets.inputs ?? []) {\n if (out.some(o => o.input === input)) continue\n const d = declared.get(input)\n out.push({\n input,\n label: asset.stateInputs?.[input]?.label ?? input,\n ...(d?.unit !== undefined ? { unit: d.unit } : {}),\n ...(d?.min !== undefined ? { min: d.min } : {}),\n ...(d?.max !== undefined ? { max: d.max } : {}),\n givenBy: b.label ?? b.accessor\n })\n }\n }\n return out\n}\n\n/**\n * Which kind of legend fits a table (ADR-0166 7): a code table takes a code map (`color-map`, `animation-map`), a range\n * table a range map (`color-ranges`). A legend whose kind the scene does not say is told by its keys: every key but\n * `default` an interval `from~to` is a range map.\n */\nexport function legendFits(lookup: 'code' | 'ranges', legend: unknown): boolean {\n const looks = legendLooksOf(legend)\n const keys = Object.keys(looks).filter(k => k !== 'default')\n if (!Object.keys(looks).length) return false\n const ranged = keys.length > 0 && keys.every(k => /^-?[\\d.]*~-?[\\d.]*$/.test(k))\n return lookup === 'ranges' ? ranged || keys.length === 0 : !ranged\n}\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import { type LegendLooks, type LookInput } from './legend-looks.js';
|
|
3
|
+
/**
|
|
4
|
+
* A legend's looks, one card per code (ADR-0166 7, 11): its colour, its effect, a value for each state input the
|
|
5
|
+
* figures' tables of its role set -- with the input's name, unit and range -- and a clip, speed and pause for a motion
|
|
6
|
+
* group. Every change sends `looks-change` with the whole looks; the host writes them where the legend lives.
|
|
7
|
+
*
|
|
8
|
+
* A value outside its input's range is not taken from the field (as the property panel's number maps). Values for
|
|
9
|
+
* inputs not shown -- another figure's, in a legend shared by many -- are kept as they are, and so is whatever else an
|
|
10
|
+
* entry holds (fmsim's 2D animation).
|
|
11
|
+
*
|
|
12
|
+
* The one legend editor: the board's legend themes (「테마」) and fmsim's legend editor both write looks through it.
|
|
13
|
+
*/
|
|
14
|
+
export declare class OxLegendLooks extends LitElement {
|
|
15
|
+
static styles: import("lit").CSSResult;
|
|
16
|
+
looks: LegendLooks;
|
|
17
|
+
/** The state inputs shown, one field each. */
|
|
18
|
+
inputs: LookInput[];
|
|
19
|
+
/** Inputs that can be added as a field: those the figures' tables of the legend's role set. */
|
|
20
|
+
addable: LookInput[];
|
|
21
|
+
/** Whether every input a look gives a value to has a field, offered or not: a legend, which no one figure reads. */
|
|
22
|
+
showGiven: boolean;
|
|
23
|
+
/** Whether an input can be added by its name typed, when no figure says which there are. */
|
|
24
|
+
nameInputs: boolean;
|
|
25
|
+
/** The clips a look may choose; none, no clip field. */
|
|
26
|
+
clips: string[];
|
|
27
|
+
effects: boolean;
|
|
28
|
+
/** Whether a look may give a motion group's speed and pause. */
|
|
29
|
+
motion: boolean;
|
|
30
|
+
/** Whether a look may give no colour; a colour map's entry has one. */
|
|
31
|
+
colorOptional: boolean;
|
|
32
|
+
/** The colour a new code starts with. */
|
|
33
|
+
defaultColor?: string;
|
|
34
|
+
/** Whether looks give values at all: a legend whose consumer takes colour and effect only (fmsim's carrier legends) gives none. */
|
|
35
|
+
noValues: boolean;
|
|
36
|
+
disabled: boolean;
|
|
37
|
+
private added;
|
|
38
|
+
private newCode;
|
|
39
|
+
private newInput;
|
|
40
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
41
|
+
/** The inputs shown: those given, those added here, and, with `showGiven`, any a look gives a value to. */
|
|
42
|
+
private shownInputs;
|
|
43
|
+
private renderCard;
|
|
44
|
+
private addCode;
|
|
45
|
+
/** A number in range is taken; an empty field takes the value away (the next source gives it); anything else is not taken. */
|
|
46
|
+
private changeValue;
|
|
47
|
+
/** An input named by hand: a field for it on every card, its values the looks' own. */
|
|
48
|
+
private addInput;
|
|
49
|
+
private change;
|
|
50
|
+
}
|
|
51
|
+
/** An input as the choice and the tooltip say it: its name, unit and range, and the tables that set it. */
|
|
52
|
+
export declare function describeInput(input: LookInput): string;
|
|
53
|
+
declare global {
|
|
54
|
+
interface HTMLElementTagNameMap {
|
|
55
|
+
'ox-legend-looks': OxLegendLooks;
|
|
56
|
+
}
|
|
57
|
+
}
|