@operato/board 10.29.9 → 10.29.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/coverage/lcov-report/index.html +55 -10
- package/coverage/lcov.info +4284 -335
- package/dist/src/modeller/component-toolbar/component-menu.js +62 -11
- package/dist/src/modeller/component-toolbar/component-menu.js.map +1 -1
- package/dist/src/types.d.ts +8 -0
- package/dist/src/types.js.map +1 -1
- package/dist/test/component-menu.test.d.ts +1 -0
- package/dist/test/component-menu.test.js +61 -0
- package/dist/test/component-menu.test.js.map +1 -0
- package/dist/test/figure-provider.test.js +8 -2
- package/dist/test/figure-provider.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/test/component-menu.test.ts +69 -0
- package/test/figure-provider.test.ts +8 -2
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @license Copyright © HatioLab Inc. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
import { __decorate } from "tslib";
|
|
5
|
-
import { css, html, LitElement } from 'lit';
|
|
5
|
+
import { css, html, LitElement, nothing } from 'lit';
|
|
6
6
|
import { property, query, state } from 'lit/decorators.js';
|
|
7
7
|
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
|
|
8
8
|
import { ScrollbarStyles } from '@operato/styles';
|
|
@@ -37,11 +37,22 @@ export class ComponentMenu extends ScopedElementsMixin(LitElement) {
|
|
|
37
37
|
<div templates @mouseover=${(e) => this.onHoverComponent(e)}>
|
|
38
38
|
${(this.templates || []).map(template => {
|
|
39
39
|
const { type } = template;
|
|
40
|
+
const reason = template.unavailableReason;
|
|
41
|
+
const label = i18next.exists(`component.${type}`) ? i18next.t(`component.${type}`) : template.description || type;
|
|
40
42
|
return html `
|
|
41
|
-
<div
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
<div
|
|
44
|
+
@click=${this.onClickTemplate}
|
|
45
|
+
data-type=${type}
|
|
46
|
+
template
|
|
47
|
+
?unavailable=${!!reason}
|
|
48
|
+
aria-disabled=${reason ? 'true' : 'false'}
|
|
49
|
+
title=${reason || ''}
|
|
50
|
+
>
|
|
51
|
+
<img src=${String(this.templateIcon(template))} draggable="false" />
|
|
52
|
+
<span name>${label}${reason ? html `<span reason>${reason}</span>` : nothing}</span>
|
|
53
|
+
${reason
|
|
54
|
+
? html `<span mark>${template.unavailableLabel || i18next.t('text.unavailable', { defaultValue: 'unavailable' })}</span>`
|
|
55
|
+
: nothing}
|
|
45
56
|
</div>
|
|
46
57
|
`;
|
|
47
58
|
})}
|
|
@@ -60,7 +71,7 @@ export class ComponentMenu extends ScopedElementsMixin(LitElement) {
|
|
|
60
71
|
: html ``;
|
|
61
72
|
}
|
|
62
73
|
updated(changes) {
|
|
63
|
-
var _a;
|
|
74
|
+
var _a, _b, _c;
|
|
64
75
|
if (changes.has('group')) {
|
|
65
76
|
if (!this.group) {
|
|
66
77
|
this.templates = [];
|
|
@@ -72,9 +83,8 @@ export class ComponentMenu extends ScopedElementsMixin(LitElement) {
|
|
|
72
83
|
}
|
|
73
84
|
}
|
|
74
85
|
if (changes.has('template')) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
: this.detail.setAttribute('hidden', '');
|
|
86
|
+
/* no detail panel is rendered while no group is open */
|
|
87
|
+
(_b = this.detail) === null || _b === void 0 ? void 0 : _b.toggleAttribute('hidden', !((_c = this.template) === null || _c === void 0 ? void 0 : _c.about));
|
|
78
88
|
}
|
|
79
89
|
}
|
|
80
90
|
findTemplate(type) {
|
|
@@ -86,14 +96,18 @@ export class ComponentMenu extends ScopedElementsMixin(LitElement) {
|
|
|
86
96
|
this.findTemplate((_a = button.closest('[data-type]')) === null || _a === void 0 ? void 0 : _a.getAttribute('data-type'));
|
|
87
97
|
}
|
|
88
98
|
onClickTemplate(e) {
|
|
89
|
-
var _a, _b;
|
|
99
|
+
var _a, _b, _c;
|
|
90
100
|
var item = e.target;
|
|
91
101
|
var type = (_a = item.closest('[data-type]')) === null || _a === void 0 ? void 0 : _a.getAttribute('data-type');
|
|
92
102
|
if (!type) {
|
|
93
103
|
return;
|
|
94
104
|
}
|
|
105
|
+
/* A template that cannot be placed here does nothing when picked; its reason is on the item. */
|
|
106
|
+
if ((_b = this.templates.find(template => template.type == type)) === null || _b === void 0 ? void 0 : _b.unavailableReason) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
95
109
|
if (this.scene) {
|
|
96
|
-
(
|
|
110
|
+
(_c = this.scene.rootContainer) === null || _c === void 0 ? void 0 : _c.trigger('addstop'); // 새로운 컴포넌트 클릭시 이전 추가 모드 정지
|
|
97
111
|
this.template = this.templates.find(template => template.type == type);
|
|
98
112
|
this.template && this.scene.startAddMode(JSON.parse(JSON.stringify(this.template.model)));
|
|
99
113
|
}
|
|
@@ -169,6 +183,43 @@ ComponentMenu.styles = [
|
|
|
169
183
|
cursor: pointer;
|
|
170
184
|
}
|
|
171
185
|
|
|
186
|
+
/* Shown but not placeable: dimmed, marked, and the reason under the name on hover (ADR-0114 P7). */
|
|
187
|
+
[template][unavailable] {
|
|
188
|
+
opacity: 0.5;
|
|
189
|
+
cursor: not-allowed;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
[template][unavailable]:hover,
|
|
193
|
+
[template][unavailable]:focus {
|
|
194
|
+
font-weight: normal;
|
|
195
|
+
cursor: not-allowed;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
[template] [name] {
|
|
199
|
+
display: flex;
|
|
200
|
+
flex-direction: column;
|
|
201
|
+
min-width: 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
[template] [mark] {
|
|
205
|
+
margin-left: auto;
|
|
206
|
+
padding: 0 4px;
|
|
207
|
+
border-radius: 4px;
|
|
208
|
+
background: var(--md-sys-color-surface-container-high);
|
|
209
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
210
|
+
white-space: nowrap;
|
|
211
|
+
text-transform: none;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
[template] [reason] {
|
|
215
|
+
display: none;
|
|
216
|
+
text-transform: none;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
[template][unavailable]:hover [reason] {
|
|
220
|
+
display: block;
|
|
221
|
+
}
|
|
222
|
+
|
|
172
223
|
[template] img {
|
|
173
224
|
margin: 5px;
|
|
174
225
|
width: var(--component-menu-item-icon-size);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-menu.js","sourceRoot":"","sources":["../../../../src/modeller/component-toolbar/component-menu.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { property, query, state } from 'lit/decorators.js'\n\nimport { Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { i18next } from '@operato/i18n'\n\nimport { Pallet, PalletItem } from '../../types.js'\nimport { ComponentDetail } from './component-detail.js'\n\nconst noImage = new URL('../../../../icons/components/no-image.png', import.meta.url).href\n\nexport class ComponentMenu extends ScopedElementsMixin(LitElement) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n align-content: stretch;\n\n background-color: var(--component-menu-background-color, var(--md-sys-color-secondary-container));\n color: var(--component-menu-color, var(--md-sys-color-on-secondary-container));\n margin: 0px;\n padding: 0px;\n\n width: 180px;\n height: 100%;\n\n overflow: visible;\n\n border: 1px solid var(--component-menu-border-color, var(--md-sys-color-outline-variant));\n box-sizing: border-box;\n\n position: absolute;\n top: 0px;\n\n z-index: 1;\n }\n\n h2 {\n background-color: var(--component-menu-title-background, var(--md-sys-color-surface-container-highest));\n padding: 4px 8px;\n margin: 0;\n font: var(--component-menu-title);\n color: var(--component-menu-title-color, var(--md-sys-color-on-surface));\n text-transform: capitalize;\n }\n\n [templates] {\n flex: 1;\n\n display: block;\n margin: 0;\n padding: 0;\n overflow-y: auto;\n\n background-color: var(--component-menu-background-color, var(--md-sys-color-secondary-container));\n color: var(--component-menu-color, var(--md-sys-color-on-secondary-container));\n }\n\n [template] {\n display: flex;\n align-items: center;\n min-height: var(--component-menu-item-icon-size);\n padding: 0 5px 0 0;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n font-size: 11px;\n color: var(--component-menu-item-color, var(--md-sys-color-on-secondary-container));\n text-transform: capitalize;\n }\n\n [template]:hover,\n [template]:focus {\n color: var(--component-menu-item-hover-color, var(--md-sys-color-secondary));\n font-weight: bold;\n cursor: pointer;\n }\n\n [template] img {\n margin: 5px;\n width: var(--component-menu-item-icon-size);\n height: var(--component-menu-item-icon-size);\n }\n\n component-detail {\n position: absolute;\n top: 0;\n left: 180px;\n height: 100%;\n outline: none;\n }\n\n component-detail[hidden] {\n display: none;\n }\n `\n ]\n\n @property({ type: Object }) groups: Pallet[] = []\n @property({ type: Object }) scene: Scene | null = null\n @property({ type: String }) group: string | null = ''\n\n @state() templates: PalletItem[] = []\n @state() template: PalletItem | any\n\n @query('component-detail') detail!: HTMLElement\n\n static get scopedElements() {\n return {\n 'component-detail': ComponentDetail\n }\n }\n\n render() {\n return this.group\n ? html`\n <h2 onclick=${(e: MouseEvent) => e.stopPropagation()}>\n ${/* group label — translate via group.<name> key, fall back to the\n raw group name so untranslated groups keep working.\n \"list\" is also translatable; defaults to the literal \"list\"\n which is what users have been seeing all along. */ ''}\n ${i18next.t(`group.${this.group}`, { defaultValue: this.group })}\n ${i18next.t('label.list', { defaultValue: 'list' })}\n </h2>\n\n <div templates @mouseover=${(e: MouseEvent) => this.onHoverComponent(e)}>\n ${(this.templates || []).map(template => {\n const { type } = template\n\n return html`\n <div @click=${this.onClickTemplate} data-type=${type} template>\n <img src=${String(this.templateIcon(template))} />${i18next.exists(`component.${type}`)\n ? i18next.t(`component.${type}`)\n : template.description || type}\n </div>\n `\n })}\n </div>\n\n <component-detail\n tabindex=\"-1\"\n @focusout=${() => {\n this.template = null\n }}\n .template=${this.template}\n hidden\n >\n </component-detail>\n `\n : html``\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('group')) {\n if (!this.group) {\n this.templates = []\n this.setAttribute('hidden', '')\n } else {\n this.templates = this.groups.find((g: Pallet) => g.name === this.group)?.templates || []\n this.removeAttribute('active')\n }\n }\n\n if (changes.has('template')) {\n this.template && this.template.about\n ? this.detail.removeAttribute('hidden')\n : this.detail.setAttribute('hidden', '')\n }\n }\n\n findTemplate(type: string | null | undefined) {\n this.template = type && this.templates.find(template => template.type == type)\n }\n\n onHoverComponent(e: MouseEvent) {\n const button = e.target as HTMLElement\n this.findTemplate(button!.closest('[data-type]')?.getAttribute('data-type'))\n }\n\n onClickTemplate(e: MouseEvent) {\n var item = e.target as HTMLElement\n var type = item.closest('[data-type]')?.getAttribute('data-type')\n\n if (!type) {\n return\n }\n\n if (this.scene) {\n this.scene.rootContainer?.trigger('addstop') // 새로운 컴포넌트 클릭시 이전 추가 모드 정지\n this.template = this.templates.find(template => template.type == type)\n this.template && this.scene.startAddMode(JSON.parse(JSON.stringify(this.template.model)))\n }\n\n this.group = null\n }\n\n templateIcon(template: PalletItem) {\n return template.icon || noImage\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"component-menu.js","sourceRoot":"","sources":["../../../../src/modeller/component-toolbar/component-menu.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport { css, html, LitElement, nothing, PropertyValues } from 'lit'\nimport { property, query, state } from 'lit/decorators.js'\n\nimport { Scene } from '@hatiolab/things-scene'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { i18next } from '@operato/i18n'\n\nimport { Pallet, PalletItem } from '../../types.js'\nimport { ComponentDetail } from './component-detail.js'\n\nconst noImage = new URL('../../../../icons/components/no-image.png', import.meta.url).href\n\nexport class ComponentMenu extends ScopedElementsMixin(LitElement) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n align-content: stretch;\n\n background-color: var(--component-menu-background-color, var(--md-sys-color-secondary-container));\n color: var(--component-menu-color, var(--md-sys-color-on-secondary-container));\n margin: 0px;\n padding: 0px;\n\n width: 180px;\n height: 100%;\n\n overflow: visible;\n\n border: 1px solid var(--component-menu-border-color, var(--md-sys-color-outline-variant));\n box-sizing: border-box;\n\n position: absolute;\n top: 0px;\n\n z-index: 1;\n }\n\n h2 {\n background-color: var(--component-menu-title-background, var(--md-sys-color-surface-container-highest));\n padding: 4px 8px;\n margin: 0;\n font: var(--component-menu-title);\n color: var(--component-menu-title-color, var(--md-sys-color-on-surface));\n text-transform: capitalize;\n }\n\n [templates] {\n flex: 1;\n\n display: block;\n margin: 0;\n padding: 0;\n overflow-y: auto;\n\n background-color: var(--component-menu-background-color, var(--md-sys-color-secondary-container));\n color: var(--component-menu-color, var(--md-sys-color-on-secondary-container));\n }\n\n [template] {\n display: flex;\n align-items: center;\n min-height: var(--component-menu-item-icon-size);\n padding: 0 5px 0 0;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n font-size: 11px;\n color: var(--component-menu-item-color, var(--md-sys-color-on-secondary-container));\n text-transform: capitalize;\n }\n\n [template]:hover,\n [template]:focus {\n color: var(--component-menu-item-hover-color, var(--md-sys-color-secondary));\n font-weight: bold;\n cursor: pointer;\n }\n\n /* Shown but not placeable: dimmed, marked, and the reason under the name on hover (ADR-0114 P7). */\n [template][unavailable] {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n [template][unavailable]:hover,\n [template][unavailable]:focus {\n font-weight: normal;\n cursor: not-allowed;\n }\n\n [template] [name] {\n display: flex;\n flex-direction: column;\n min-width: 0;\n }\n\n [template] [mark] {\n margin-left: auto;\n padding: 0 4px;\n border-radius: 4px;\n background: var(--md-sys-color-surface-container-high);\n color: var(--md-sys-color-on-surface-variant);\n white-space: nowrap;\n text-transform: none;\n }\n\n [template] [reason] {\n display: none;\n text-transform: none;\n }\n\n [template][unavailable]:hover [reason] {\n display: block;\n }\n\n [template] img {\n margin: 5px;\n width: var(--component-menu-item-icon-size);\n height: var(--component-menu-item-icon-size);\n }\n\n component-detail {\n position: absolute;\n top: 0;\n left: 180px;\n height: 100%;\n outline: none;\n }\n\n component-detail[hidden] {\n display: none;\n }\n `\n ]\n\n @property({ type: Object }) groups: Pallet[] = []\n @property({ type: Object }) scene: Scene | null = null\n @property({ type: String }) group: string | null = ''\n\n @state() templates: PalletItem[] = []\n @state() template: PalletItem | any\n\n @query('component-detail') detail!: HTMLElement\n\n static get scopedElements() {\n return {\n 'component-detail': ComponentDetail\n }\n }\n\n render() {\n return this.group\n ? html`\n <h2 onclick=${(e: MouseEvent) => e.stopPropagation()}>\n ${/* group label — translate via group.<name> key, fall back to the\n raw group name so untranslated groups keep working.\n \"list\" is also translatable; defaults to the literal \"list\"\n which is what users have been seeing all along. */ ''}\n ${i18next.t(`group.${this.group}`, { defaultValue: this.group })}\n ${i18next.t('label.list', { defaultValue: 'list' })}\n </h2>\n\n <div templates @mouseover=${(e: MouseEvent) => this.onHoverComponent(e)}>\n ${(this.templates || []).map(template => {\n const { type } = template\n\n const reason = template.unavailableReason\n const label = i18next.exists(`component.${type}`) ? i18next.t(`component.${type}`) : template.description || type\n\n return html`\n <div\n @click=${this.onClickTemplate}\n data-type=${type}\n template\n ?unavailable=${!!reason}\n aria-disabled=${reason ? 'true' : 'false'}\n title=${reason || ''}\n >\n <img src=${String(this.templateIcon(template))} draggable=\"false\" />\n <span name>${label}${reason ? html`<span reason>${reason}</span>` : nothing}</span>\n ${reason\n ? html`<span mark>${template.unavailableLabel || i18next.t('text.unavailable', { defaultValue: 'unavailable' })}</span>`\n : nothing}\n </div>\n `\n })}\n </div>\n\n <component-detail\n tabindex=\"-1\"\n @focusout=${() => {\n this.template = null\n }}\n .template=${this.template}\n hidden\n >\n </component-detail>\n `\n : html``\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('group')) {\n if (!this.group) {\n this.templates = []\n this.setAttribute('hidden', '')\n } else {\n this.templates = this.groups.find((g: Pallet) => g.name === this.group)?.templates || []\n this.removeAttribute('active')\n }\n }\n\n if (changes.has('template')) {\n /* no detail panel is rendered while no group is open */\n this.detail?.toggleAttribute('hidden', !this.template?.about)\n }\n }\n\n findTemplate(type: string | null | undefined) {\n this.template = type && this.templates.find(template => template.type == type)\n }\n\n onHoverComponent(e: MouseEvent) {\n const button = e.target as HTMLElement\n this.findTemplate(button!.closest('[data-type]')?.getAttribute('data-type'))\n }\n\n onClickTemplate(e: MouseEvent) {\n var item = e.target as HTMLElement\n var type = item.closest('[data-type]')?.getAttribute('data-type')\n\n if (!type) {\n return\n }\n\n /* A template that cannot be placed here does nothing when picked; its reason is on the item. */\n if (this.templates.find(template => template.type == type)?.unavailableReason) {\n return\n }\n\n if (this.scene) {\n this.scene.rootContainer?.trigger('addstop') // 새로운 컴포넌트 클릭시 이전 추가 모드 정지\n this.template = this.templates.find(template => template.type == type)\n this.template && this.scene.startAddMode(JSON.parse(JSON.stringify(this.template.model)))\n }\n\n this.group = null\n }\n\n templateIcon(template: PalletItem) {\n return template.icon || noImage\n }\n}\n"]}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ export interface PalletItem {
|
|
|
5
5
|
icon: HTMLImageElement;
|
|
6
6
|
group: string | string[];
|
|
7
7
|
model: Model;
|
|
8
|
+
/** Why it cannot be placed here. A template with a reason is shown dimmed and does nothing when picked. */
|
|
9
|
+
unavailableReason?: string;
|
|
10
|
+
/** The short mark shown on an unavailable template, such as "format unsupported". */
|
|
11
|
+
unavailableLabel?: string;
|
|
8
12
|
}
|
|
9
13
|
export interface Pallet {
|
|
10
14
|
name: string;
|
|
@@ -16,6 +20,10 @@ export type ComponentTemplate = {
|
|
|
16
20
|
group: 'line' | 'shape' | 'textAndMedia' | 'chartAndGauge' | 'table' | 'container' | 'dataSource' | 'IoT' | '3D' | 'warehouse' | 'form' | 'etc' | string;
|
|
17
21
|
icon: any;
|
|
18
22
|
model: Model;
|
|
23
|
+
/** Why it cannot be placed here. A template with a reason is shown dimmed and does nothing when picked. */
|
|
24
|
+
unavailableReason?: string;
|
|
25
|
+
/** The short mark shown on an unavailable template, such as "format unsupported". */
|
|
26
|
+
unavailableLabel?: string;
|
|
19
27
|
};
|
|
20
28
|
export type ComponentGroup = {
|
|
21
29
|
name: string;
|
package/dist/src/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Model } from '@hatiolab/things-scene'\n\nexport interface PalletItem {\n type: string\n description: string\n icon: HTMLImageElement\n group: string | string[]\n model: Model\n}\n\nexport interface Pallet {\n name: string\n templates: PalletItem[]\n}\n\nexport type ComponentTemplate = {\n type: string\n description: string\n group:\n | 'line'\n | 'shape'\n | 'textAndMedia'\n | 'chartAndGauge'\n | 'table'\n | 'container'\n | 'dataSource'\n | 'IoT'\n | '3D'\n | 'warehouse'\n | 'form'\n | 'etc'\n | string\n icon: any\n model: Model\n}\n\nexport type ComponentGroup = {\n name: string\n description: string\n icon: string\n /* A Material Symbols glyph name for the palette rail; the rail has a default per known group. */\n symbol?: string\n templates: ComponentTemplate[]\n}\n\nexport type Board = {\n id?: string\n name?: string\n description?: string\n model?: any\n groupId?: string\n thumbnail?: string\n}\n\nexport type BoardGroup = {\n id?: string\n name?: string\n description?: string\n}\n\nexport type PlayGroup = {\n id?: string\n name?: string\n description?: string\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Model } from '@hatiolab/things-scene'\n\nexport interface PalletItem {\n type: string\n description: string\n icon: HTMLImageElement\n group: string | string[]\n model: Model\n /** Why it cannot be placed here. A template with a reason is shown dimmed and does nothing when picked. */\n unavailableReason?: string\n /** The short mark shown on an unavailable template, such as \"format unsupported\". */\n unavailableLabel?: string\n}\n\nexport interface Pallet {\n name: string\n templates: PalletItem[]\n}\n\nexport type ComponentTemplate = {\n type: string\n description: string\n group:\n | 'line'\n | 'shape'\n | 'textAndMedia'\n | 'chartAndGauge'\n | 'table'\n | 'container'\n | 'dataSource'\n | 'IoT'\n | '3D'\n | 'warehouse'\n | 'form'\n | 'etc'\n | string\n icon: any\n model: Model\n /** Why it cannot be placed here. A template with a reason is shown dimmed and does nothing when picked. */\n unavailableReason?: string\n /** The short mark shown on an unavailable template, such as \"format unsupported\". */\n unavailableLabel?: string\n}\n\nexport type ComponentGroup = {\n name: string\n description: string\n icon: string\n /* A Material Symbols glyph name for the palette rail; the rail has a default per known group. */\n symbol?: string\n templates: ComponentTemplate[]\n}\n\nexport type Board = {\n id?: string\n name?: string\n description?: string\n model?: any\n groupId?: string\n thumbnail?: string\n}\n\nexport type BoardGroup = {\n id?: string\n name?: string\n description?: string\n}\n\nexport type PlayGroup = {\n id?: string\n name?: string\n description?: string\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { expect, fixture, html } from '@open-wc/testing';
|
|
2
|
+
import { ComponentMenu } from '../src/modeller/component-toolbar/component-menu.js';
|
|
3
|
+
/**
|
|
4
|
+
* A tray template that cannot be placed here (ADR-0114 P7): shown dimmed with its mark, its reason on hover, and
|
|
5
|
+
* nothing happens when it is picked -- no add mode, no image drag.
|
|
6
|
+
*/
|
|
7
|
+
customElements.get('test-component-menu') || customElements.define('test-component-menu', class extends ComponentMenu {
|
|
8
|
+
});
|
|
9
|
+
const group = {
|
|
10
|
+
name: 'figure',
|
|
11
|
+
templates: [
|
|
12
|
+
{ type: 'hatiolab.com/ok', description: 'OK figure', icon: '', group: 'figure', model: { type: 'hatiolab.com/ok' } },
|
|
13
|
+
{
|
|
14
|
+
type: 'hatiolab.com/future',
|
|
15
|
+
description: 'Future figure',
|
|
16
|
+
icon: '',
|
|
17
|
+
group: 'figure',
|
|
18
|
+
model: { type: 'hatiolab.com/future' },
|
|
19
|
+
unavailableReason: 'this server cannot read figure-asset-9',
|
|
20
|
+
unavailableLabel: 'format unsupported'
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
};
|
|
24
|
+
async function menuWith(added) {
|
|
25
|
+
const scene = { rootContainer: { trigger: () => { } }, startAddMode: (model) => added.push(model) };
|
|
26
|
+
const menu = (await fixture(html `<test-component-menu .groups=${[group]} .scene=${scene}></test-component-menu>`));
|
|
27
|
+
menu.group = 'figure';
|
|
28
|
+
await menu.updateComplete;
|
|
29
|
+
return menu;
|
|
30
|
+
}
|
|
31
|
+
const itemOf = (menu, type) => menu.renderRoot.querySelector(`[data-type="${type}"]`);
|
|
32
|
+
describe('트레이 — 놓을 수 없는 템플릿', () => {
|
|
33
|
+
it('흐리게 · 표 · 사유를 가진다, 아이콘은 끌리지 않는다', async () => {
|
|
34
|
+
const menu = await menuWith([]);
|
|
35
|
+
const item = itemOf(menu, 'hatiolab.com/future');
|
|
36
|
+
expect(item.hasAttribute('unavailable')).to.equal(true);
|
|
37
|
+
expect(item.getAttribute('aria-disabled')).to.equal('true');
|
|
38
|
+
expect(item.getAttribute('title')).to.equal('this server cannot read figure-asset-9');
|
|
39
|
+
expect(item.querySelector('[mark]').textContent).to.equal('format unsupported');
|
|
40
|
+
expect(item.querySelector('[reason]').textContent).to.equal('this server cannot read figure-asset-9');
|
|
41
|
+
expect(item.querySelector('img').getAttribute('draggable')).to.equal('false');
|
|
42
|
+
expect(getComputedStyle(item).opacity).to.equal('0.5');
|
|
43
|
+
});
|
|
44
|
+
it('★ 고르면 아무 일도 없다 — 놓을 수 있는 것은 추가 모드에 들어간다', async () => {
|
|
45
|
+
const added = [];
|
|
46
|
+
const menu = await menuWith(added);
|
|
47
|
+
itemOf(menu, 'hatiolab.com/future').click();
|
|
48
|
+
expect(added).to.deep.equal([]);
|
|
49
|
+
menu.group = 'figure';
|
|
50
|
+
await menu.updateComplete;
|
|
51
|
+
itemOf(menu, 'hatiolab.com/ok').click();
|
|
52
|
+
expect(added).to.deep.equal([{ type: 'hatiolab.com/ok' }]);
|
|
53
|
+
});
|
|
54
|
+
it('놓을 수 있는 템플릿에는 표 · 사유가 없다', async () => {
|
|
55
|
+
const menu = await menuWith([]);
|
|
56
|
+
const item = itemOf(menu, 'hatiolab.com/ok');
|
|
57
|
+
expect(item.hasAttribute('unavailable')).to.equal(false);
|
|
58
|
+
expect(item.querySelector('[mark]')).to.equal(null);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=component-menu.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-menu.test.js","sourceRoot":"","sources":["../../test/component-menu.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture, html } from '@open-wc/testing'\n\nimport { ComponentMenu } from '../src/modeller/component-toolbar/component-menu.js'\n\n/**\n * A tray template that cannot be placed here (ADR-0114 P7): shown dimmed with its mark, its reason on hover, and\n * nothing happens when it is picked -- no add mode, no image drag.\n */\ncustomElements.get('test-component-menu') || customElements.define('test-component-menu', class extends ComponentMenu {})\n\nconst group = {\n name: 'figure',\n templates: [\n { type: 'hatiolab.com/ok', description: 'OK figure', icon: '', group: 'figure', model: { type: 'hatiolab.com/ok' } },\n {\n type: 'hatiolab.com/future',\n description: 'Future figure',\n icon: '',\n group: 'figure',\n model: { type: 'hatiolab.com/future' },\n unavailableReason: 'this server cannot read figure-asset-9',\n unavailableLabel: 'format unsupported'\n }\n ]\n}\n\nasync function menuWith(added: unknown[]) {\n const scene = { rootContainer: { trigger: () => {} }, startAddMode: (model: unknown) => added.push(model) }\n const menu = (await fixture(html`<test-component-menu .groups=${[group]} .scene=${scene as never}></test-component-menu>`)) as ComponentMenu\n menu.group = 'figure'\n await menu.updateComplete\n return menu\n}\n\nconst itemOf = (menu: ComponentMenu, type: string) => menu.renderRoot.querySelector(`[data-type=\"${type}\"]`) as HTMLElement\n\ndescribe('트레이 — 놓을 수 없는 템플릿', () => {\n it('흐리게 · 표 · 사유를 가진다, 아이콘은 끌리지 않는다', async () => {\n const menu = await menuWith([])\n const item = itemOf(menu, 'hatiolab.com/future')\n expect(item.hasAttribute('unavailable')).to.equal(true)\n expect(item.getAttribute('aria-disabled')).to.equal('true')\n expect(item.getAttribute('title')).to.equal('this server cannot read figure-asset-9')\n expect(item.querySelector('[mark]')!.textContent).to.equal('format unsupported')\n expect(item.querySelector('[reason]')!.textContent).to.equal('this server cannot read figure-asset-9')\n expect(item.querySelector('img')!.getAttribute('draggable')).to.equal('false')\n expect(getComputedStyle(item).opacity).to.equal('0.5')\n })\n\n it('★ 고르면 아무 일도 없다 — 놓을 수 있는 것은 추가 모드에 들어간다', async () => {\n const added: unknown[] = []\n const menu = await menuWith(added)\n\n itemOf(menu, 'hatiolab.com/future').click()\n expect(added).to.deep.equal([])\n\n menu.group = 'figure'\n await menu.updateComplete\n itemOf(menu, 'hatiolab.com/ok').click()\n expect(added).to.deep.equal([{ type: 'hatiolab.com/ok' }])\n })\n\n it('놓을 수 있는 템플릿에는 표 · 사유가 없다', async () => {\n const menu = await menuWith([])\n const item = itemOf(menu, 'hatiolab.com/ok')\n expect(item.hasAttribute('unavailable')).to.equal(false)\n expect(item.querySelector('[mark]')).to.equal(null)\n })\n})\n"]}
|
|
@@ -29,11 +29,17 @@ const figureAssetOf = (type) => ({
|
|
|
29
29
|
{ id: 'a0', unit: 'deg', value: 0 }
|
|
30
30
|
],
|
|
31
31
|
nodes: [
|
|
32
|
-
{
|
|
32
|
+
{
|
|
33
|
+
id: 'body.shape',
|
|
34
|
+
op: 'extrude@1',
|
|
35
|
+
args: ['zero', 'w', 'w', 'w'],
|
|
36
|
+
outputs: { shape: 'body.s' },
|
|
37
|
+
params: { frame: 'body.local', axis: 'y', section: 'rectangle' }
|
|
38
|
+
},
|
|
33
39
|
{
|
|
34
40
|
id: 'body.pose',
|
|
35
41
|
op: 'rigid@1',
|
|
36
|
-
args: ['zero', '
|
|
42
|
+
args: ['zero', 'zero', 'zero', 'a0', 'a0', 'a0'],
|
|
37
43
|
outputs: { pose: 'body.p' },
|
|
38
44
|
params: { from: 'body.local', to: 'asset' }
|
|
39
45
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figure-provider.test.js","sourceRoot":"","sources":["../../test/figure-provider.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing'\n\nimport { Component, hasFigure, registerTypeResolver, resetFigures, resolveUnknownTypes } from '@hatiolab/things-scene'\n\nimport {\n createFigureResolver,\n FIGURE_TYPE_MAP_CHANGED,\n figureMapReleaseOfPage,\n resetFigureMapPage,\n setFigureMapChoice,\n type FigureBlueprintsAnswer\n} from '../src/figure-provider.js'\n\n/**\n * The path a figure takes to a board (ADR-0063 결정 2, ADR-0114): one batch question by full name.\n *\n * things-scene holds the seat and knows nothing about figures; this package fills it. What is\n * checked here is the part that belongs to the board: which names are asked about, what happens to\n * the answer, and how \"nobody published this\" stays apart from \"could not ask\".\n *\n * The payload below is written out rather than built with figure-model: this package does not read it, it passes it\n * to the scene's own registration, and a fixture that drifts from the format should fail loudly here rather than\n * draw nothing at runtime.\n */\n\n/** What the door hands over: a figure asset. One box standing on the floor, its room declared. */\nconst figureAssetOf = (type: string) => ({\n version: 'figure-asset-1',\n name: type,\n document: {\n version: 'figure-prototype-1',\n model: {\n version: 'r3-spatial-1',\n inputs: [],\n constants: [\n { id: 'w', unit: 'mm', value: 100 },\n { id: 'half', unit: 'mm', value: 50 },\n { id: 'minusHalf', unit: 'mm', value: -50 },\n { id: 'zero', unit: 'mm', value: 0 },\n { id: 'a0', unit: 'deg', value: 0 }\n ],\n nodes: [\n { id: 'body.shape', op: 'box-shape@1', args: ['w', 'w', 'w'], outputs: { shape: 'body.s' }, params: { frame: 'body.local' } },\n {\n id: 'body.pose',\n op: 'rigid@1',\n args: ['zero', 'half', 'zero', 'a0', 'a0', 'a0'],\n outputs: { pose: 'body.p' },\n params: { from: 'body.local', to: 'asset' }\n },\n { id: 'body', op: 'place@1', args: ['body.s', 'body.p'], outputs: { placed: 'body.placed' } }\n ]\n },\n capabilities: { version: 'r3-capability-3', assetFrame: 'asset', bindings: [] }\n },\n designInputs: {},\n stateDefaults: {},\n palette: { 'palette.primary': '#477b9e' },\n appearance: [{ target: 'body', material: { token: 'palette.primary' } }],\n occupancy: {\n bounds: { x: { min: 'minusHalf', max: 'half' }, y: { min: 'zero', max: 'w' }, z: { min: 'minusHalf', max: 'half' } },\n placement: 'floor'\n }\n})\n\n/** A server that answers from a table, from one map release, and records each question. */\nfunction serverOf(\n published: Record<string, unknown>,\n release: { version: number; revision: number } | null = { version: 1, revision: 1 },\n refusals: Record<string, string> = {}\n) {\n const asked: { types: string[]; map: string }[] = []\n const state = { release }\n return {\n asked,\n state,\n fetch: async (types: readonly string[], map: string): Promise<FigureBlueprintsAnswer> => {\n asked.push({ types: [...types], map })\n const items = types.filter(type => published[type]).map(type => ({ type, ref: `${type}@1`, asset: published[type] }))\n const refused = types\n .filter(type => !published[type])\n .map(type => ({ type, code: 'figure-not-in-type-map', message: refusals[type] ?? `${type} is not in the type map` }))\n return { release: state.release, items, refused }\n }\n }\n}\n\nconst name = (label: string) => `hatiolab.com/${label}-${Math.random().toString(36).slice(2, 8)}`\n\ndescribe('figure 공급자 — 보드가 Figure 를 정식 이름으로 한 번에 묻는다', () => {\n afterEach(() => {\n resetFigures()\n resetFigureMapPage()\n })\n\n it('등록되지 않은 type 을 대소문자와 관계없이 한 번에 묻는다 — 이미 등록된 것은 묻지 않는다', async () => {\n const mixer = name('eq-mixer')\n const { asked, fetch } = serverOf({ [mixer]: figureAssetOf(mixer) })\n\n await createFigureResolver(fetch)([mixer, 'rect', 'Conveyor', 'text', 'VEH_AGV_01'])\n\n expect(asked).to.deep.equal([{ types: [mixer, 'Conveyor', 'VEH_AGV_01'], map: 'ACTIVE' }])\n })\n\n it('이미 등록된 이름은 묻지 않고, figure 가 그 컴포넌트를 덮지 않는다', async () => {\n const taken = name('taken')\n const { asked, fetch } = serverOf({ [taken]: figureAssetOf(taken) })\n Component.register(taken, class Taken extends Component {} as never)\n\n await createFigureResolver(fetch)([taken])\n\n expect(asked).to.deep.equal([])\n expect(hasFigure(taken)).to.equal(false)\n })\n\n it('받은 자산을 물은 이름으로 등록한다', async () => {\n const conveyor = name('conveyor')\n const { fetch } = serverOf({ [conveyor]: figureAssetOf(conveyor) })\n\n await createFigureResolver(fetch)([conveyor])\n\n expect(hasFigure(conveyor)).to.equal(true)\n expect(Component.register(conveyor)).to.not.equal(undefined)\n })\n\n it('★ 그리지 못한 이름은 서버가 준 이유를 그대로 돌려준다 — 읽을 수 없는 자산도 그 이름만 이유를 받는다', async () => {\n const good = name('good')\n const broken = name('broken')\n const missing = name('missing')\n const brokenAsset = figureAssetOf(broken)\n brokenAsset.appearance = []\n const { fetch } = serverOf({ [good]: figureAssetOf(good), [broken]: brokenAsset }, { version: 1, revision: 1 }, { [missing]: '타입 맵에 없습니다' })\n\n const answer = await createFigureResolver(fetch)([good, broken, missing])\n\n expect(hasFigure(good)).to.equal(true)\n expect(hasFigure(broken)).to.equal(false)\n expect(answer!.refused.map(r => r.type)).to.deep.equal([missing, broken])\n expect(answer!.refused[0].message).to.equal('타입 맵에 없습니다')\n })\n\n it('물을 수 없으면 거절한다 — 씬은 실패로 모으고 보드는 열린다', async () => {\n const fetch = async () => {\n throw new Error('no route to the door')\n }\n let message = ''\n try {\n await createFigureResolver(fetch as never)([name('x')])\n } catch (error) {\n message = error instanceof Error ? error.message : String(error)\n }\n expect(message).to.contain('no route to the door')\n })\n\n it('★ 페이지는 처음 받은 맵 릴리스를 기억하고, 후보의 revision 이 바뀐 답은 등록하지 않는다', async () => {\n const first = name('first')\n const later = name('later')\n const server = serverOf({ [first]: figureAssetOf(first), [later]: figureAssetOf(later) }, { version: 2, revision: 3 })\n const resolver = createFigureResolver(server.fetch)\n setFigureMapChoice('CANDIDATE')\n\n await resolver([first])\n expect(figureMapReleaseOfPage()).to.deep.equal({ map: 'CANDIDATE', version: 2, revision: 3 })\n\n let changed: any = null\n const listen = (e: Event) => (changed = (e as CustomEvent).detail)\n window.addEventListener(FIGURE_TYPE_MAP_CHANGED, listen)\n try {\n server.state.release = { version: 2, revision: 4 }\n const answer = await resolver([later])\n expect(hasFigure(later)).to.equal(false)\n expect(answer!.refused.map(r => r.type)).to.deep.equal([later])\n expect(changed).to.deep.equal({ was: { map: 'CANDIDATE', version: 2, revision: 3 }, now: { map: 'CANDIDATE', version: 2, revision: 4 } })\n expect(figureMapReleaseOfPage()).to.deep.equal({ map: 'CANDIDATE', version: 2, revision: 3 })\n } finally {\n window.removeEventListener(FIGURE_TYPE_MAP_CHANGED, listen)\n }\n })\n\n it('★ 활성 맵과 후보 맵의 등록을 한 페이지에서 섞지 않는다', async () => {\n const active = name('active')\n const preview = name('preview')\n const server = serverOf({ [active]: figureAssetOf(active), [preview]: figureAssetOf(preview) }, { version: 1, revision: 1 })\n const resolver = createFigureResolver(server.fetch)\n\n await resolver([active])\n expect(figureMapReleaseOfPage()).to.deep.equal({ map: 'ACTIVE', version: 1, revision: 1 })\n\n setFigureMapChoice('CANDIDATE')\n const answer = await resolver([preview])\n\n expect(server.asked.map(a => a.map)).to.deep.equal(['ACTIVE', 'CANDIDATE'])\n expect(hasFigure(preview)).to.equal(false)\n expect(answer!.refused.map(r => r.type)).to.deep.equal([preview])\n })\n\n it('맵 릴리스가 없는 서버(저작 서버의 최신 정책)는 기억하지 않는다', async () => {\n const a = name('a')\n const b = name('b')\n const server = serverOf({ [a]: figureAssetOf(a), [b]: figureAssetOf(b) }, null)\n const resolver = createFigureResolver(server.fetch)\n\n await resolver([a])\n await resolver([b])\n\n expect(figureMapReleaseOfPage()).to.equal(null)\n expect(hasFigure(a)).to.equal(true)\n expect(hasFigure(b)).to.equal(true)\n })\n\n it('씬의 자리에 앉아 동작한다 — 받은 것은 그려지고 나머지는 자리표시로 남는다', async () => {\n const onBoard = name('on-board')\n const never = name('never')\n const { fetch } = serverOf({ [onBoard]: figureAssetOf(onBoard) })\n const takeBack = registerTypeResolver(createFigureResolver(fetch) as never)\n\n try {\n const model = {\n type: 'container',\n components: [\n { type: onBoard, left: 0, top: 0, width: 100, height: 100 },\n { type: never, left: 0, top: 0, width: 100, height: 100 }\n ]\n }\n const resolution = await resolveUnknownTypes(model)\n expect(resolution.unresolved).to.deep.equal([never])\n expect(resolution.failed).to.deep.equal([])\n expect(hasFigure(onBoard)).to.equal(true)\n } finally {\n takeBack()\n }\n })\n})\n"]}
|
|
1
|
+
{"version":3,"file":"figure-provider.test.js","sourceRoot":"","sources":["../../test/figure-provider.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect } from '@open-wc/testing'\n\nimport { Component, hasFigure, registerTypeResolver, resetFigures, resolveUnknownTypes } from '@hatiolab/things-scene'\n\nimport {\n createFigureResolver,\n FIGURE_TYPE_MAP_CHANGED,\n figureMapReleaseOfPage,\n resetFigureMapPage,\n setFigureMapChoice,\n type FigureBlueprintsAnswer\n} from '../src/figure-provider.js'\n\n/**\n * The path a figure takes to a board (ADR-0063 결정 2, ADR-0114): one batch question by full name.\n *\n * things-scene holds the seat and knows nothing about figures; this package fills it. What is\n * checked here is the part that belongs to the board: which names are asked about, what happens to\n * the answer, and how \"nobody published this\" stays apart from \"could not ask\".\n *\n * The payload below is written out rather than built with figure-model: this package does not read it, it passes it\n * to the scene's own registration, and a fixture that drifts from the format should fail loudly here rather than\n * draw nothing at runtime.\n */\n\n/** What the door hands over: a figure asset. One box standing on the floor, its room declared. */\nconst figureAssetOf = (type: string) => ({\n version: 'figure-asset-1',\n name: type,\n document: {\n version: 'figure-prototype-1',\n model: {\n version: 'r3-spatial-1',\n inputs: [],\n constants: [\n { id: 'w', unit: 'mm', value: 100 },\n { id: 'half', unit: 'mm', value: 50 },\n { id: 'minusHalf', unit: 'mm', value: -50 },\n { id: 'zero', unit: 'mm', value: 0 },\n { id: 'a0', unit: 'deg', value: 0 }\n ],\n nodes: [\n {\n id: 'body.shape',\n op: 'extrude@1',\n args: ['zero', 'w', 'w', 'w'],\n outputs: { shape: 'body.s' },\n params: { frame: 'body.local', axis: 'y', section: 'rectangle' }\n },\n {\n id: 'body.pose',\n op: 'rigid@1',\n args: ['zero', 'zero', 'zero', 'a0', 'a0', 'a0'],\n outputs: { pose: 'body.p' },\n params: { from: 'body.local', to: 'asset' }\n },\n { id: 'body', op: 'place@1', args: ['body.s', 'body.p'], outputs: { placed: 'body.placed' } }\n ]\n },\n capabilities: { version: 'r3-capability-3', assetFrame: 'asset', bindings: [] }\n },\n designInputs: {},\n stateDefaults: {},\n palette: { 'palette.primary': '#477b9e' },\n appearance: [{ target: 'body', material: { token: 'palette.primary' } }],\n occupancy: {\n bounds: { x: { min: 'minusHalf', max: 'half' }, y: { min: 'zero', max: 'w' }, z: { min: 'minusHalf', max: 'half' } },\n placement: 'floor'\n }\n})\n\n/** A server that answers from a table, from one map release, and records each question. */\nfunction serverOf(\n published: Record<string, unknown>,\n release: { version: number; revision: number } | null = { version: 1, revision: 1 },\n refusals: Record<string, string> = {}\n) {\n const asked: { types: string[]; map: string }[] = []\n const state = { release }\n return {\n asked,\n state,\n fetch: async (types: readonly string[], map: string): Promise<FigureBlueprintsAnswer> => {\n asked.push({ types: [...types], map })\n const items = types.filter(type => published[type]).map(type => ({ type, ref: `${type}@1`, asset: published[type] }))\n const refused = types\n .filter(type => !published[type])\n .map(type => ({ type, code: 'figure-not-in-type-map', message: refusals[type] ?? `${type} is not in the type map` }))\n return { release: state.release, items, refused }\n }\n }\n}\n\nconst name = (label: string) => `hatiolab.com/${label}-${Math.random().toString(36).slice(2, 8)}`\n\ndescribe('figure 공급자 — 보드가 Figure 를 정식 이름으로 한 번에 묻는다', () => {\n afterEach(() => {\n resetFigures()\n resetFigureMapPage()\n })\n\n it('등록되지 않은 type 을 대소문자와 관계없이 한 번에 묻는다 — 이미 등록된 것은 묻지 않는다', async () => {\n const mixer = name('eq-mixer')\n const { asked, fetch } = serverOf({ [mixer]: figureAssetOf(mixer) })\n\n await createFigureResolver(fetch)([mixer, 'rect', 'Conveyor', 'text', 'VEH_AGV_01'])\n\n expect(asked).to.deep.equal([{ types: [mixer, 'Conveyor', 'VEH_AGV_01'], map: 'ACTIVE' }])\n })\n\n it('이미 등록된 이름은 묻지 않고, figure 가 그 컴포넌트를 덮지 않는다', async () => {\n const taken = name('taken')\n const { asked, fetch } = serverOf({ [taken]: figureAssetOf(taken) })\n Component.register(taken, class Taken extends Component {} as never)\n\n await createFigureResolver(fetch)([taken])\n\n expect(asked).to.deep.equal([])\n expect(hasFigure(taken)).to.equal(false)\n })\n\n it('받은 자산을 물은 이름으로 등록한다', async () => {\n const conveyor = name('conveyor')\n const { fetch } = serverOf({ [conveyor]: figureAssetOf(conveyor) })\n\n await createFigureResolver(fetch)([conveyor])\n\n expect(hasFigure(conveyor)).to.equal(true)\n expect(Component.register(conveyor)).to.not.equal(undefined)\n })\n\n it('★ 그리지 못한 이름은 서버가 준 이유를 그대로 돌려준다 — 읽을 수 없는 자산도 그 이름만 이유를 받는다', async () => {\n const good = name('good')\n const broken = name('broken')\n const missing = name('missing')\n const brokenAsset = figureAssetOf(broken)\n brokenAsset.appearance = []\n const { fetch } = serverOf({ [good]: figureAssetOf(good), [broken]: brokenAsset }, { version: 1, revision: 1 }, { [missing]: '타입 맵에 없습니다' })\n\n const answer = await createFigureResolver(fetch)([good, broken, missing])\n\n expect(hasFigure(good)).to.equal(true)\n expect(hasFigure(broken)).to.equal(false)\n expect(answer!.refused.map(r => r.type)).to.deep.equal([missing, broken])\n expect(answer!.refused[0].message).to.equal('타입 맵에 없습니다')\n })\n\n it('물을 수 없으면 거절한다 — 씬은 실패로 모으고 보드는 열린다', async () => {\n const fetch = async () => {\n throw new Error('no route to the door')\n }\n let message = ''\n try {\n await createFigureResolver(fetch as never)([name('x')])\n } catch (error) {\n message = error instanceof Error ? error.message : String(error)\n }\n expect(message).to.contain('no route to the door')\n })\n\n it('★ 페이지는 처음 받은 맵 릴리스를 기억하고, 후보의 revision 이 바뀐 답은 등록하지 않는다', async () => {\n const first = name('first')\n const later = name('later')\n const server = serverOf({ [first]: figureAssetOf(first), [later]: figureAssetOf(later) }, { version: 2, revision: 3 })\n const resolver = createFigureResolver(server.fetch)\n setFigureMapChoice('CANDIDATE')\n\n await resolver([first])\n expect(figureMapReleaseOfPage()).to.deep.equal({ map: 'CANDIDATE', version: 2, revision: 3 })\n\n let changed: any = null\n const listen = (e: Event) => (changed = (e as CustomEvent).detail)\n window.addEventListener(FIGURE_TYPE_MAP_CHANGED, listen)\n try {\n server.state.release = { version: 2, revision: 4 }\n const answer = await resolver([later])\n expect(hasFigure(later)).to.equal(false)\n expect(answer!.refused.map(r => r.type)).to.deep.equal([later])\n expect(changed).to.deep.equal({ was: { map: 'CANDIDATE', version: 2, revision: 3 }, now: { map: 'CANDIDATE', version: 2, revision: 4 } })\n expect(figureMapReleaseOfPage()).to.deep.equal({ map: 'CANDIDATE', version: 2, revision: 3 })\n } finally {\n window.removeEventListener(FIGURE_TYPE_MAP_CHANGED, listen)\n }\n })\n\n it('★ 활성 맵과 후보 맵의 등록을 한 페이지에서 섞지 않는다', async () => {\n const active = name('active')\n const preview = name('preview')\n const server = serverOf({ [active]: figureAssetOf(active), [preview]: figureAssetOf(preview) }, { version: 1, revision: 1 })\n const resolver = createFigureResolver(server.fetch)\n\n await resolver([active])\n expect(figureMapReleaseOfPage()).to.deep.equal({ map: 'ACTIVE', version: 1, revision: 1 })\n\n setFigureMapChoice('CANDIDATE')\n const answer = await resolver([preview])\n\n expect(server.asked.map(a => a.map)).to.deep.equal(['ACTIVE', 'CANDIDATE'])\n expect(hasFigure(preview)).to.equal(false)\n expect(answer!.refused.map(r => r.type)).to.deep.equal([preview])\n })\n\n it('맵 릴리스가 없는 서버(저작 서버의 최신 정책)는 기억하지 않는다', async () => {\n const a = name('a')\n const b = name('b')\n const server = serverOf({ [a]: figureAssetOf(a), [b]: figureAssetOf(b) }, null)\n const resolver = createFigureResolver(server.fetch)\n\n await resolver([a])\n await resolver([b])\n\n expect(figureMapReleaseOfPage()).to.equal(null)\n expect(hasFigure(a)).to.equal(true)\n expect(hasFigure(b)).to.equal(true)\n })\n\n it('씬의 자리에 앉아 동작한다 — 받은 것은 그려지고 나머지는 자리표시로 남는다', async () => {\n const onBoard = name('on-board')\n const never = name('never')\n const { fetch } = serverOf({ [onBoard]: figureAssetOf(onBoard) })\n const takeBack = registerTypeResolver(createFigureResolver(fetch) as never)\n\n try {\n const model = {\n type: 'container',\n components: [\n { type: onBoard, left: 0, top: 0, width: 100, height: 100 },\n { type: never, left: 0, top: 0, width: 100, height: 100 }\n ]\n }\n const resolution = await resolveUnknownTypes(model)\n expect(resolution.unresolved).to.deep.equal([never])\n expect(resolution.failed).to.deep.equal([])\n expect(hasFigure(onBoard)).to.equal(true)\n } finally {\n takeBack()\n }\n })\n})\n"]}
|