@things-factory/figure-ui 10.1.21 → 10.1.23
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/client/modeller/figure-canvas.ts +5 -5
- package/client/modeller/figure-capabilities.ts +12 -5
- package/client/modeller/figure-inspector.ts +2 -2
- package/client/modeller/figure-preview.ts +7 -7
- package/client/modeller/figure-settings.ts +4 -4
- package/client/modeller/figure-thumbnail.ts +1 -1
- package/client/modeller/figure-view.ts +6 -6
- package/client/modeller/part-edits.ts +1 -1
- package/client/modeller/scene-budget.ts +1 -1
- package/client/pages/figure-list-page.ts +1 -1
- package/client/pages/figure-modeller-page.ts +4 -4
- package/client/types.ts +2 -2
- package/dist-client/modeller/figure-canvas.d.ts +1 -1
- package/dist-client/modeller/figure-canvas.js +5 -5
- package/dist-client/modeller/figure-canvas.js.map +1 -1
- package/dist-client/modeller/figure-capabilities.d.ts +1 -1
- package/dist-client/modeller/figure-capabilities.js +7 -2
- package/dist-client/modeller/figure-capabilities.js.map +1 -1
- package/dist-client/modeller/figure-inspector.d.ts +1 -1
- package/dist-client/modeller/figure-inspector.js +2 -2
- package/dist-client/modeller/figure-inspector.js.map +1 -1
- package/dist-client/modeller/figure-preview.d.ts +3 -3
- package/dist-client/modeller/figure-preview.js +5 -5
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-settings.js +4 -4
- package/dist-client/modeller/figure-settings.js.map +1 -1
- package/dist-client/modeller/figure-thumbnail.js +1 -1
- package/dist-client/modeller/figure-thumbnail.js.map +1 -1
- package/dist-client/modeller/figure-view.d.ts +6 -6
- package/dist-client/modeller/figure-view.js.map +1 -1
- package/dist-client/modeller/part-edits.js +1 -1
- package/dist-client/modeller/part-edits.js.map +1 -1
- package/dist-client/modeller/scene-budget.d.ts +1 -1
- package/dist-client/modeller/scene-budget.js +1 -1
- package/dist-client/modeller/scene-budget.js.map +1 -1
- package/dist-client/pages/figure-list-page.js +1 -1
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +3 -3
- package/dist-client/pages/figure-modeller-page.js +4 -4
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +2 -2
- package/dist-client/types.js.map +1 -1
- package/package.json +4 -4
- package/test/figure-source.test.ts +3 -3
- package/test/i18n-prefix-guard.test.ts +19 -1
- package/translations/en.json +25 -14
- package/translations/ja.json +8 -8
- package/translations/ko.json +13 -13
- package/translations/ms.json +8 -8
- package/translations/zh.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figure-capabilities.js","sourceRoot":"","sources":["../../client/modeller/figure-capabilities.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport type { FigureDraft, PartModel } from './figure-source.js'\n\n/**\n * 이 도형이 **무엇을 하는가.**\n *\n * ## 왜 탭 하나가 필요한가\n *\n * 지금까지 저작한 것은 **생김새**뿐이었다. 컨베이어를 아무리 잘 그려도 보드에 놓으면\n * 상자 하나를 못 받는다 — 물건을 받고 담고 흘려보내는 것은 씬의 capability 가 하는 일이고,\n * 손으로 쓴 컴포넌트는 클래스 선언 한 줄로 그것을 졌다. 저작한 도형에는 그 줄을 쓸 자리가\n * 없어서, 여기서 고른 것이 그 줄이 된다.\n *\n * ## 사다리로 묻는다\n *\n * 능력의 이름(`Capacity` · `Holdable` · `Transferable` · `FlowNode`)을 그대로 늘어놓고\n * 고르라고 하면, 저작자는 넷의 관계를 먼저 배워야 한다. 실제로 물어야 하는 것은 하나다 —\n * **이 도형이 물건을 다루나, 다룬다면 지나가나.** 그래서 세 칸의 사다리로 묻고, 이름은\n * 고른 뒤에 보여 준다.\n *\n * 윗 칸이 아랫 칸을 포함한다(`FlowNode` 가 나머지 셋을 스스로 흡수한다). 그래서 사다리이지\n * 체크상자가 아니다.\n */\n\n/** 사다리 한 칸 — 화면이 묻는 말과, 그것이 되는 능력 이름. */\ntype Rung = 'none' | 'hold' | 'flow'\n\nconst RUNGS: Readonly<Record<Rung, readonly string[]>> = Object.freeze({\n none: [],\n hold: ['Capacity', 'Holdable'],\n flow: ['FlowNode']\n})\n\n/** 선언된 이름 집합이 사다리 어느 칸인가. 어느 칸도 아니면 `null` — 지어내지 않는다. */\nfunction rungOf(names: readonly string[] | undefined): Rung | null {\n const has = new Set(names ?? [])\n if (has.size === 0) return 'none'\n\n for (const [rung, wanted] of Object.entries(RUNGS) as [Rung, readonly string[]][]) {\n if (wanted.length === has.size && wanted.every(name => has.has(name))) return rung\n }\n return null\n}\n\n@customElement('figure-capabilities')\nexport class FigureCapabilities extends localize(i18next)(LitElement) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n background-color: var(--md-sys-color-surface-container-lowest);\n color: var(--md-sys-color-on-surface);\n font: var(--label-font, inherit);\n font-size: 0.76rem;\n }\n\n section {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-large, 12px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n\n /* 다른 탭들과 같은 눈금이다 — 탭마다 다른 제목 규격을 쓰면 따로 논다 */\n h3 {\n margin: 0 0 2px 0;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n p[quiet] {\n margin: 0;\n font-size: 0.72rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n /* 사다리 — 고르는 것이 하나라 라디오다 */\n label[rung] {\n display: flex;\n align-items: flex-start;\n gap: var(--spacing-medium, 8px);\n padding: 6px 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 4px;\n cursor: pointer;\n }\n label[rung][on] {\n border-color: var(--md-sys-color-primary);\n background-color: var(--md-sys-color-surface-container);\n }\n label[rung]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container-low);\n }\n label[rung] span {\n display: flex;\n flex-direction: column;\n gap: 1px;\n }\n label[rung] strong {\n font-weight: 600;\n }\n label[rung] em {\n font-style: normal;\n font-size: 0.7rem;\n line-height: 1.4;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n input[type='radio'] {\n flex: none;\n margin: 2px 0 0 0;\n accent-color: var(--md-sys-color-primary);\n cursor: pointer;\n }\n\n /* 실제로 씬에 붙는 이름. 고른 뒤에 보여 준다 */\n code[names] {\n font: var(--mono-font, ui-monospace, monospace);\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n /* 지정된 부품 한 줄 — 읽는 것이지 고치는 것이 아니다. 눌러 그 부품으로 간다 */\n button[marked] {\n display: flex;\n align-items: baseline;\n gap: var(--spacing-medium, 8px);\n width: 100%;\n padding: 5px 8px;\n text-align: left;\n font: inherit;\n color: inherit;\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 4px;\n cursor: pointer;\n }\n button[marked]:hover {\n background-color: var(--md-sys-color-surface-container);\n }\n button[marked] b {\n flex: none;\n font-weight: 600;\n }\n button[marked] span {\n flex: none;\n font-size: 0.72rem;\n color: var(--md-sys-color-primary);\n }\n button[marked] em {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: right;\n font-style: normal;\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n /* 부품마다 한 줄. 자리를 고른 것만 아래로 펼친다 */\n div[part] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 6px 0;\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n div[part]:first-of-type {\n border-top: none;\n }\n div[head] {\n display: flex;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n }\n div[head] b {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 600;\n }\n\n select,\n input[type='text'],\n input[type='number'] {\n padding: 4px 6px;\n font: var(--input-field-font, inherit);\n font-size: 0.74rem;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 4px;\n }\n /* 이름이 길어질 수 있는 쪽(문)까지 안 잘리는 폭이다 */\n div[head] select {\n flex: none;\n width: 8.5em;\n }\n\n div[detail] {\n display: grid;\n grid-template-columns: 4.5em 1fr;\n align-items: center;\n gap: 4px var(--spacing-medium, 8px);\n padding-left: var(--spacing-medium, 8px);\n }\n div[detail] > span {\n font-size: 0.72rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[detail] input {\n width: 100%;\n box-sizing: border-box;\n }\n div[detail] em {\n grid-column: 2;\n font-style: normal;\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n `\n ]\n\n @property({ type: Object }) draft?: FigureDraft\n @property({ type: Array }) parts: PartModel[] = []\n\n render() {\n const rung = rungOf(this.draft?.capabilities)\n const names = this.draft?.capabilities ?? []\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.what-this-does')}</h3>\n\n ${this.rung('none', rung)} ${this.rung('hold', rung)} ${this.rung('flow', rung)}\n ${rung === null\n ? html`<p quiet>${i18next.t('figure.text.capability-hand-written')}</p>`\n : ''}\n ${names.length > 0 ? html`<code names>${names.join(' · ')}</code>` : ''}\n </section>\n\n ${this.renderAnchors(rung)}\n `\n }\n\n /**\n * 자리와 문 — **여기서는 읽기만 한다.**\n *\n * 지정은 속성 탭에서 한다. 부품을 고치는 곳이 한 군데여야 하기 때문이다 — 목록을 여기 또\n * 두면 같은 부품을 두 화면에서 고치게 되고, 저작자는 어느 쪽이 이기는지 매번 생각해야 한다.\n *\n * 그래도 요약은 남긴다. 「어느 부품이 자리인가」는 부품을 하나씩 눌러 보며 알아낼 일이\n * 아니다 — 자리는 그려지지 않으므로 화면을 봐서는 영영 못 찾는다.\n */\n private renderAnchors(rung: Rung | null) {\n if (rung === 'none') {\n return html`\n <section>\n <h3>${i18next.t('figure.label.slots-and-ports')}</h3>\n <p quiet>${i18next.t('figure.text.slots-need-capability')}</p>\n </section>\n `\n }\n\n const marked = this.parts.filter(part => part.capability)\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.slots-and-ports')}</h3>\n <p quiet>${i18next.t('figure.text.anchors-are-not-drawn')}</p>\n\n ${marked.length === 0\n ? html`<p quiet>${i18next.t('figure.text.designate-in-properties')}</p>`\n : marked.map(part => this.renderMarked(part))}\n </section>\n `\n }\n\n /** 지정된 부품 한 줄. 누르면 그 부품이 골라진다 — 고치는 자리는 속성 탭이다. */\n private renderMarked(part: PartModel) {\n const spec = part.capability!\n\n return html`\n <button marked @click=${() => this.pick(part)}>\n <b>${part.name}</b>\n <span>${spec.roles.map(role => i18next.t('figure.label.role-' + role)).join(' · ')}</span>\n ${spec.accepts?.length ? html`<em>${spec.accepts.join(' · ')}</em>` : ''}\n </button>\n `\n }\n\n /** 이 부품을 고르게 한다. 부품 목록에서 누른 것과 같은 알림이다. */\n private pick(part: PartModel) {\n const index = this.parts.indexOf(part)\n if (index < 0) return\n\n this.dispatchEvent(new CustomEvent('select-part', { detail: { index }, bubbles: true, composed: true }))\n }\n\n /**\n * 사다리 한 칸.\n *\n * `checked` 를 `live()` 로 묶는다. 그냥 두면 Lit 이 **마지막으로 그린 값**과 견주므로,\n * 브라우저가 DOM 을 바꿨는데 모델은 안 바뀐 경우 되돌리지 않는다. 실제로 그랬다 — 고르는\n * 도중 도형이 다시 실렸더니 점은 「흐른다」에 있고 강조 상자는 「그리기만 한다」에 있었다.\n * 화면이 모델과 다른 말을 하는 상태다. `live()` 는 **지금 DOM 의 값**과 견준다.\n */\n private rung(which: Rung, chosen: Rung | null) {\n return html`\n <label rung ?on=${chosen === which}>\n <input\n type=\"radio\"\n name=\"rung\"\n .checked=${live(chosen === which)}\n @change=${() => this.setRung(which)}\n />\n <span>\n <strong>${i18next.t(`figure.label.rung-${which}`)}</strong>\n <em>${i18next.t(`figure.text.rung-${which}-note`)}</em>\n </span>\n </label>\n `\n }\n\n /**\n * 사다리를 내리면 자리도 함께 거둔다.\n *\n * 능력을 「그리기만 한다」로 되돌려 놓고 부품에 자리 표시가 남아 있으면, 형식은 자리를\n * 그대로 실어 보내는데 씬은 그것을 쓸 클래스가 없다. **선언은 있고 길이 없는** 상태가\n * 되고, 그 도형은 멀쩡해 보이면서 아무것도 못 받는다.\n */\n private setRung(which: Rung) {\n const draft = this.draft\n if (!draft) return\n\n this.tellBoard({ ...draft, capabilities: RUNGS[which].length > 0 ? [...RUNGS[which]] : undefined })\n\n if (which === 'none') {\n this.parts.forEach((part, index) => {\n if (!part.capability) return\n const { capability, ...rest } = part\n this.dispatchEvent(\n new CustomEvent('update-part', { detail: { index, part: rest }, bubbles: true, composed: true })\n )\n })\n }\n }\n\n private tellBoard(draft: FigureDraft) {\n this.draft = draft\n this.dispatchEvent(new CustomEvent('update-draft', { detail: { draft }, bubbles: true, composed: true }))\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"figure-capabilities.js","sourceRoot":"","sources":["../../client/modeller/figure-capabilities.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport type { FigureCapability } from '@hatiolab/figure-model'\n\nimport type { FigureDraft, PartModel } from './figure-source.js'\n\n/**\n * 이 도형이 **무엇을 하는가.**\n *\n * ## 왜 탭 하나가 필요한가\n *\n * 지금까지 저작한 것은 **생김새**뿐이었다. 컨베이어를 아무리 잘 그려도 도면에 놓으면\n * 상자 하나를 못 받는다 — 물건을 받고 담고 흘려보내는 것은 씬의 capability 가 하는 일이고,\n * 손으로 쓴 컴포넌트는 클래스 선언 한 줄로 그것을 졌다. 저작한 도형에는 그 줄을 쓸 자리가\n * 없어서, 여기서 고른 것이 그 줄이 된다.\n *\n * ## 사다리로 묻는다\n *\n * 능력의 이름(`Capacity` · `Holdable` · `Transferable` · `FlowNode`)을 그대로 늘어놓고\n * 고르라고 하면, 저작자는 넷의 관계를 먼저 배워야 한다. 실제로 물어야 하는 것은 하나다 —\n * **이 도형이 물건을 다루나, 다룬다면 지나가나.** 그래서 세 칸의 사다리로 묻고, 이름은\n * 고른 뒤에 보여 준다.\n *\n * 윗 칸이 아랫 칸을 포함한다(`FlowNode` 가 나머지 셋을 스스로 흡수한다). 그래서 사다리이지\n * 체크상자가 아니다.\n */\n\n/** 사다리 한 칸 — 화면이 묻는 말과, 그것이 되는 능력 이름. */\ntype Rung = 'none' | 'hold' | 'flow'\n\n/*\n 칸마다의 이름은 **형식의 능력 이름**이다. `string[]` 으로 두면 사다리에 오타를 적어도\n 타입이 통과하고, 그 도형은 발행까지 지난 뒤 씬에서 죽는다 — `hoist` 가 그렇게 나갔다\n (ADR-0051 ④). `as const` 로 덮지 않는 이유도 같다: 그건 좁히는 것이 아니라 가리는 것이다.\n*/\nconst RUNGS: Readonly<Record<Rung, readonly FigureCapability[]>> = Object.freeze({\n none: [],\n hold: ['Capacity', 'Holdable'],\n flow: ['FlowNode']\n})\n\n/** 선언된 이름 집합이 사다리 어느 칸인가. 어느 칸도 아니면 `null` — 지어내지 않는다. */\nfunction rungOf(names: readonly string[] | undefined): Rung | null {\n const has = new Set(names ?? [])\n if (has.size === 0) return 'none'\n\n for (const [rung, wanted] of Object.entries(RUNGS) as [Rung, readonly FigureCapability[]][]) {\n if (wanted.length === has.size && wanted.every(name => has.has(name))) return rung\n }\n return null\n}\n\n@customElement('figure-capabilities')\nexport class FigureCapabilities extends localize(i18next)(LitElement) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n background-color: var(--md-sys-color-surface-container-lowest);\n color: var(--md-sys-color-on-surface);\n font: var(--label-font, inherit);\n font-size: 0.76rem;\n }\n\n section {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-large, 12px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n\n /* 다른 탭들과 같은 눈금이다 — 탭마다 다른 제목 규격을 쓰면 따로 논다 */\n h3 {\n margin: 0 0 2px 0;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n p[quiet] {\n margin: 0;\n font-size: 0.72rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n /* 사다리 — 고르는 것이 하나라 라디오다 */\n label[rung] {\n display: flex;\n align-items: flex-start;\n gap: var(--spacing-medium, 8px);\n padding: 6px 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 4px;\n cursor: pointer;\n }\n label[rung][on] {\n border-color: var(--md-sys-color-primary);\n background-color: var(--md-sys-color-surface-container);\n }\n label[rung]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container-low);\n }\n label[rung] span {\n display: flex;\n flex-direction: column;\n gap: 1px;\n }\n label[rung] strong {\n font-weight: 600;\n }\n label[rung] em {\n font-style: normal;\n font-size: 0.7rem;\n line-height: 1.4;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n input[type='radio'] {\n flex: none;\n margin: 2px 0 0 0;\n accent-color: var(--md-sys-color-primary);\n cursor: pointer;\n }\n\n /* 실제로 씬에 붙는 이름. 고른 뒤에 보여 준다 */\n code[names] {\n font: var(--mono-font, ui-monospace, monospace);\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n /* 지정된 부품 한 줄 — 읽는 것이지 고치는 것이 아니다. 눌러 그 부품으로 간다 */\n button[marked] {\n display: flex;\n align-items: baseline;\n gap: var(--spacing-medium, 8px);\n width: 100%;\n padding: 5px 8px;\n text-align: left;\n font: inherit;\n color: inherit;\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 4px;\n cursor: pointer;\n }\n button[marked]:hover {\n background-color: var(--md-sys-color-surface-container);\n }\n button[marked] b {\n flex: none;\n font-weight: 600;\n }\n button[marked] span {\n flex: none;\n font-size: 0.72rem;\n color: var(--md-sys-color-primary);\n }\n button[marked] em {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: right;\n font-style: normal;\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n /* 부품마다 한 줄. 자리를 고른 것만 아래로 펼친다 */\n div[part] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 6px 0;\n border-top: 1px solid var(--md-sys-color-outline-variant);\n }\n div[part]:first-of-type {\n border-top: none;\n }\n div[head] {\n display: flex;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n }\n div[head] b {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 600;\n }\n\n select,\n input[type='text'],\n input[type='number'] {\n padding: 4px 6px;\n font: var(--input-field-font, inherit);\n font-size: 0.74rem;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 4px;\n }\n /* 이름이 길어질 수 있는 쪽(문)까지 안 잘리는 폭이다 */\n div[head] select {\n flex: none;\n width: 8.5em;\n }\n\n div[detail] {\n display: grid;\n grid-template-columns: 4.5em 1fr;\n align-items: center;\n gap: 4px var(--spacing-medium, 8px);\n padding-left: var(--spacing-medium, 8px);\n }\n div[detail] > span {\n font-size: 0.72rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[detail] input {\n width: 100%;\n box-sizing: border-box;\n }\n div[detail] em {\n grid-column: 2;\n font-style: normal;\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n `\n ]\n\n @property({ type: Object }) draft?: FigureDraft\n @property({ type: Array }) parts: PartModel[] = []\n\n render() {\n const rung = rungOf(this.draft?.capabilities)\n const names = this.draft?.capabilities ?? []\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.what-this-does')}</h3>\n\n ${this.rung('none', rung)} ${this.rung('hold', rung)} ${this.rung('flow', rung)}\n ${rung === null\n ? html`<p quiet>${i18next.t('figure.text.capability-hand-written')}</p>`\n : ''}\n ${names.length > 0 ? html`<code names>${names.join(' · ')}</code>` : ''}\n </section>\n\n ${this.renderAnchors(rung)}\n `\n }\n\n /**\n * 자리와 문 — **여기서는 읽기만 한다.**\n *\n * 지정은 속성 탭에서 한다. 부품을 고치는 곳이 한 군데여야 하기 때문이다 — 목록을 여기 또\n * 두면 같은 부품을 두 화면에서 고치게 되고, 저작자는 어느 쪽이 이기는지 매번 생각해야 한다.\n *\n * 그래도 요약은 남긴다. 「어느 부품이 자리인가」는 부품을 하나씩 눌러 보며 알아낼 일이\n * 아니다 — 자리는 그려지지 않으므로 화면을 봐서는 영영 못 찾는다.\n */\n private renderAnchors(rung: Rung | null) {\n if (rung === 'none') {\n return html`\n <section>\n <h3>${i18next.t('figure.label.slots-and-ports')}</h3>\n <p quiet>${i18next.t('figure.text.slots-need-capability')}</p>\n </section>\n `\n }\n\n const marked = this.parts.filter(part => part.capability)\n\n return html`\n <section>\n <h3>${i18next.t('figure.label.slots-and-ports')}</h3>\n <p quiet>${i18next.t('figure.text.anchors-are-not-drawn')}</p>\n\n ${marked.length === 0\n ? html`<p quiet>${i18next.t('figure.text.designate-in-properties')}</p>`\n : marked.map(part => this.renderMarked(part))}\n </section>\n `\n }\n\n /** 지정된 부품 한 줄. 누르면 그 부품이 골라진다 — 고치는 자리는 속성 탭이다. */\n private renderMarked(part: PartModel) {\n const spec = part.capability!\n\n return html`\n <button marked @click=${() => this.pick(part)}>\n <b>${part.name}</b>\n <span>${spec.roles.map(role => i18next.t('figure.label.role-' + role)).join(' · ')}</span>\n ${spec.accepts?.length ? html`<em>${spec.accepts.join(' · ')}</em>` : ''}\n </button>\n `\n }\n\n /** 이 부품을 고르게 한다. 부품 목록에서 누른 것과 같은 알림이다. */\n private pick(part: PartModel) {\n const index = this.parts.indexOf(part)\n if (index < 0) return\n\n this.dispatchEvent(new CustomEvent('select-part', { detail: { index }, bubbles: true, composed: true }))\n }\n\n /**\n * 사다리 한 칸.\n *\n * `checked` 를 `live()` 로 묶는다. 그냥 두면 Lit 이 **마지막으로 그린 값**과 견주므로,\n * 브라우저가 DOM 을 바꿨는데 모델은 안 바뀐 경우 되돌리지 않는다. 실제로 그랬다 — 고르는\n * 도중 도형이 다시 실렸더니 점은 「흐른다」에 있고 강조 상자는 「그리기만 한다」에 있었다.\n * 화면이 모델과 다른 말을 하는 상태다. `live()` 는 **지금 DOM 의 값**과 견준다.\n */\n private rung(which: Rung, chosen: Rung | null) {\n return html`\n <label rung ?on=${chosen === which}>\n <input\n type=\"radio\"\n name=\"rung\"\n .checked=${live(chosen === which)}\n @change=${() => this.setRung(which)}\n />\n <span>\n <strong>${i18next.t(`figure.label.rung-${which}`)}</strong>\n <em>${i18next.t(`figure.text.rung-${which}-note`)}</em>\n </span>\n </label>\n `\n }\n\n /**\n * 사다리를 내리면 자리도 함께 거둔다.\n *\n * 능력을 「그리기만 한다」로 되돌려 놓고 부품에 자리 표시가 남아 있으면, 형식은 자리를\n * 그대로 실어 보내는데 씬은 그것을 쓸 클래스가 없다. **선언은 있고 길이 없는** 상태가\n * 되고, 그 도형은 멀쩡해 보이면서 아무것도 못 받는다.\n */\n private setRung(which: Rung) {\n const draft = this.draft\n if (!draft) return\n\n this.tellDraft({ ...draft, capabilities: RUNGS[which].length > 0 ? [...RUNGS[which]] : undefined })\n\n if (which === 'none') {\n this.parts.forEach((part, index) => {\n if (!part.capability) return\n const { capability, ...rest } = part\n this.dispatchEvent(\n new CustomEvent('update-part', { detail: { index, part: rest }, bubbles: true, composed: true })\n )\n })\n }\n }\n\n private tellDraft(draft: FigureDraft) {\n this.draft = draft\n this.dispatchEvent(new CustomEvent('update-draft', { detail: { draft }, bubbles: true, composed: true }))\n }\n}\n"]}
|
|
@@ -450,7 +450,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
450
450
|
/*
|
|
451
451
|
정밀 제작 도구.
|
|
452
452
|
|
|
453
|
-
|
|
453
|
+
도면 편집기는 자산을 배치하는 곳이라 대충 놓아도 되지만, 여기는 자산을 만드는
|
|
454
454
|
곳이다. 「몸통 한가운데」·「바닥에 붙이기」·「반대쪽에도 하나」를 손으로 계산하면
|
|
455
455
|
늘 조금씩 어긋나고, 기준 치수를 바꾸면 전부 다시 해야 한다.
|
|
456
456
|
*/
|
|
@@ -914,7 +914,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
914
914
|
*
|
|
915
915
|
* ## 타입은 읽기만 한다
|
|
916
916
|
*
|
|
917
|
-
* 저장되는 식별자다. 배치된
|
|
917
|
+
* 저장되는 식별자다. 배치된 도면이 이 이름으로 타입을 찾으므로 만든 뒤에는 못 바꾼다.
|
|
918
918
|
*/
|
|
919
919
|
renderFigure(compact = false) {
|
|
920
920
|
const fields = html `
|