@things-factory/figure-ui 10.1.69 → 10.1.70
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/graphql/index.ts +73 -0
- package/client/modeller/figure-v3-parts.ts +3 -3
- package/client/modeller/v3-recipes.ts +18 -0
- package/client/pages/figure-recipe-page.ts +338 -0
- package/client/route.ts +4 -0
- package/dist-client/graphql/index.d.ts +16 -0
- package/dist-client/graphql/index.js +60 -0
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-v3-parts.js +3 -3
- package/dist-client/modeller/figure-v3-parts.js.map +1 -1
- package/dist-client/modeller/v3-recipes.d.ts +10 -0
- package/dist-client/modeller/v3-recipes.js +18 -0
- package/dist-client/modeller/v3-recipes.js.map +1 -1
- package/dist-client/pages/figure-recipe-page.d.ts +41 -0
- package/dist-client/pages/figure-recipe-page.js +352 -0
- package/dist-client/pages/figure-recipe-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +3 -0
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.fui.tsbuildinfo +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/test/v3-recipes.test.ts +16 -0
- package/things-factory.config.js +1 -0
- package/translations/en.json +22 -0
- package/translations/ja.json +22 -0
- package/translations/ko.json +22 -0
- package/translations/ms.json +22 -0
- package/translations/zh.json +22 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figure-v3-parts.js","sourceRoot":"","sources":["../../client/modeller/figure-v3-parts.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { PART_LIMIT } from '@hatiolab/figure-model'\nimport type { V3PrimitiveCreation, V3Recipe, V3RecipeValue } from '@hatiolab/figure-model'\nimport { recipeFieldOf, recipeReady, recipeValueOf, recipeWord } from './v3-recipes.js'\nimport type { V3BudgetLine } from './v3-maturity.js'\nimport { partLabelOf } from './v3-fields.js'\n\n/**\n * 도형 팔레트와 부품 목록 — V3 자산의 것.\n *\n * 팔레트는 목록 위에, 눈 단추는 늘 자리를 차지하고, 조작은 고른 줄에서만 보인다.\n *\n * 부품의 도형은 V3 평가가 준 `provider` 로 읽는다. 「숨김」은 여기서도 **작업대의 것**이다 — 저장되지 않고, 자산의 `visibility`(상태에 따라\n * 보이고 안 보이는 것)와는 다른 것이다. 그것은 속성 패널의 몫이다.\n *\n * 이 컴포넌트는 정본을 만지지 않는다. 무엇을 하겠다는 것만 알린다.\n */\n\n/** 한 줄 — V3 평가에서 이 패널이 읽는 것만. */\nexport interface V3PartRow {\n id: string\n provider: string\n /** 작업대에서 잠깐 가린 것. 저장되지 않는다. */\n hidden?: boolean\n /** 자산이 지금 상태에서 보이지 않는다고 한 것. 저장되는 것이고, 여기서는 표시만 한다. */\n invisible?: boolean\n /** 되풀이한 부품이면 지금 몇 개로 그려지나. 되풀이는 한 부품이다 — 한 줄로 보인다. */\n copies?: number\n}\n\n/** 제공자마다의 아이콘. Material Symbols 이름이다. */\nconst PROVIDER_ICON: Record<string, string> = {\n box: 'deployed_code',\n cylinder: 'database',\n ellipticCylinder: 'database',\n frustum: 'change_history',\n sphere: 'circle',\n roundedBox: 'crop_square',\n roundedBoxXZ: 'crop_square',\n hollowBox: 'check_box_outline_blank',\n hollowBoxXZ: 'check_box_outline_blank',\n polygon: 'pentagon',\n portal: 'door_front'\n}\n\n/** 팔레트 — 저작 API 가 만들 수 있는 것만 보인다. */\n/*\n The palette. A plate and a wall are boxes of a shape a person reaches for, so they are starting sizes, not kinds of their own.\n*/\nexport type V3PaletteKind = V3PrimitiveCreation['kind'] | 'plate' | 'wall'\nconst RECIPES: Array<{ kind: V3PaletteKind; icon: string; label: string }> = [\n { kind: 'box', icon: 'deployed_code', label: 'figure.label.primitive-cube' },\n { kind: 'cylinder', icon: 'database', label: 'figure.label.primitive-cylinder' },\n { kind: 'rounded-box', icon: 'crop_square', label: 'figure.label.primitive-rounded-box' },\n { kind: 'sphere', icon: 'circle', label: 'figure.label.primitive-sphere' },\n { kind: 'frustum', icon: 'change_history', label: 'figure.label.primitive-frustum' },\n { kind: 'polygon', icon: 'hexagon', label: 'figure.label.primitive-polygon' },\n { kind: 'plate', icon: 'check_box_outline_blank', label: 'figure.label.primitive-plate' },\n { kind: 'wall', icon: 'crop_portrait', label: 'figure.label.primitive-wall' },\n { kind: 'portal', icon: 'door_front', label: 'figure.label.primitive-portal' }\n]\n\n@customElement('figure-v3-parts')\nexport class FigureV3Parts 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: hidden;\n background-color: var(--md-sys-color-surface-container-lowest);\n /* The divider beside it is the editor's frame's (ox-three-pane). */\n color: var(--md-sys-color-on-surface);\n font: var(--label-font, inherit);\n font-size: 0.76rem;\n /* Folds by its own width: the editor gives this column 140 px when it has two columns (UX, 2026-09-24). */\n container-type: inline-size;\n }\n section {\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n section[list] {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n }\n h3 {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 6px;\n margin: 0 0 6px 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 /* The cost line: the grade's letter in its colour, then what the figure costs. */\n button[budget] {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n width: 100%;\n margin: 0 0 6px;\n padding: 4px 6px;\n font: inherit;\n font-size: 0.68rem;\n line-height: 1.5;\n text-align: left;\n color: var(--md-sys-color-on-surface-variant);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[budget]:hover {\n background: var(--md-sys-color-surface-container);\n }\n button[budget] b {\n flex: none;\n min-width: 16px;\n text-align: center;\n border-radius: 4px;\n color: var(--md-sys-color-on-primary-container);\n background: var(--md-sys-color-primary-container);\n }\n button[budget][grade='C'] b {\n color: var(--md-sys-color-on-tertiary-container);\n background: var(--md-sys-color-tertiary-container);\n }\n button[budget][grade='D'] {\n border-color: var(--md-sys-color-error);\n }\n button[budget][grade='D'] b {\n color: var(--md-sys-color-on-error);\n background: var(--md-sys-color-error);\n }\n button[budget] em {\n display: block;\n font-style: normal;\n color: var(--md-sys-color-error);\n }\n span[room][full] {\n color: var(--md-sys-color-error);\n font-weight: 600;\n }\n p[full] {\n margin: 4px 0 0;\n font-size: 0.68rem;\n color: var(--md-sys-color-error);\n }\n div[level] {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr);\n align-items: center;\n gap: 6px;\n margin: 0 0 6px;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[level] select {\n min-width: 0;\n padding: 2px 4px;\n font: inherit;\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n small[copies] {\n color: var(--md-sys-color-on-surface-variant);\n }\n span[room] {\n font-size: 0.7rem;\n font-weight: 400;\n letter-spacing: 0;\n text-transform: none;\n }\n /* Three to a row, small (user, 2026-09-24): the palette is reached for, not read -- the list below is what grows. */\n div[palette] {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 3px;\n }\n button[shape] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1px;\n min-width: 0;\n padding: 5px 1px 4px;\n font: var(--label-font, inherit);\n font-size: 0.6rem;\n line-height: 1.2;\n text-align: center;\n overflow-wrap: anywhere;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n div[recipe-list] {\n display: flex;\n flex-wrap: wrap;\n gap: 3px;\n }\n button[recipe] {\n padding: 4px 8px;\n font: inherit;\n font-size: 0.75rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 999em;\n cursor: pointer;\n }\n button[recipe][on] {\n color: var(--md-sys-color-on-secondary-container);\n background: var(--md-sys-color-secondary-container);\n border-color: transparent;\n }\n button[recipe]:disabled {\n opacity: 0.38;\n cursor: default;\n }\n div[recipe-form] {\n display: grid;\n gap: 6px;\n margin-top: 8px;\n padding: 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n font-size: 0.75rem;\n }\n div[recipe-form] label[field] {\n display: grid;\n grid-template-columns: 1fr 5.5em;\n align-items: center;\n gap: 6px;\n }\n div[recipe-form] input[type='number'],\n div[recipe-form] select {\n min-width: 0;\n font: inherit;\n }\n div[recipe-form] p[hint] {\n margin: 0;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[recipe-form] p[error] {\n margin: 0;\n padding: 6px;\n white-space: pre-wrap;\n color: var(--md-sys-color-on-error-container);\n background: var(--md-sys-color-error-container);\n border-radius: 6px;\n }\n div[recipe-actions] {\n display: flex;\n gap: 6px;\n }\n div[recipe-actions] button {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n font: inherit;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 999em;\n background: none;\n color: var(--md-sys-color-on-surface);\n cursor: pointer;\n }\n div[recipe-actions] button[insert] {\n border-color: transparent;\n color: var(--md-sys-color-on-primary);\n background: var(--md-sys-color-primary);\n }\n button[shape]:hover:not([disabled]) {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n button[shape][disabled] {\n opacity: 0.35;\n cursor: default;\n }\n button[shape] md-icon {\n --md-icon-size: 18px;\n }\n ol {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n li {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 6px;\n border-radius: 6px;\n cursor: pointer;\n }\n li:hover {\n background-color: var(--md-sys-color-surface-container);\n }\n li[selected] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n }\n li md-icon {\n --md-icon-size: 16px;\n flex: none;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n button[eye][all] {\n margin-left: auto;\n opacity: 0.75;\n }\n button[eye][all]:hover {\n opacity: 1;\n }\n button[eye] {\n display: flex;\n flex: none;\n padding: 1px;\n color: inherit;\n background: none;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n opacity: 0;\n }\n li:hover button[eye],\n li[selected] button[eye],\n button[eye][off] {\n opacity: 1;\n }\n button[eye][off] {\n color: var(--md-sys-color-tertiary);\n }\n button[eye] md-icon {\n --md-icon-size: 15px;\n color: inherit;\n }\n li[off] span[label],\n li[off] > md-icon,\n li[invisible] span[label],\n li[invisible] > md-icon {\n opacity: 0.45;\n }\n li[selected] md-icon {\n color: inherit;\n }\n span[label] {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 0.78rem;\n }\n span[label] small {\n margin-left: 4px;\n font-size: 0.66rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n div[ops] {\n display: none;\n gap: 1px;\n }\n li[selected] div[ops] {\n display: flex;\n }\n div[ops] button {\n display: flex;\n padding: 2px;\n color: inherit;\n background: none;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n }\n div[ops] button:hover:not([disabled]) {\n background-color: var(--md-sys-color-surface-container-lowest);\n }\n div[ops] button[disabled] {\n opacity: 0.3;\n cursor: default;\n }\n div[ops] md-icon {\n --md-icon-size: 15px;\n color: inherit;\n }\n /*\n Under 200 px the selected row's four buttons pushed its name out and the last one past the edge (seen at 140,\n 2026-09-24). They go to a line of their own under the name.\n */\n @container (max-width: 199px) {\n li[selected] {\n flex-wrap: wrap;\n }\n li[selected] div[ops] {\n flex-basis: 100%;\n justify-content: flex-end;\n }\n }\n p[quiet] {\n margin: 0;\n font-size: 0.76rem;\n line-height: 1.6;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n `\n ]\n\n @property({ type: Array }) parts: V3PartRow[] = []\n /** 고른 부품의 id. 자리(index)가 아니다 — V3 부품은 id 로 안정하게 가리킨다. */\n @property({ type: String }) selected = ''\n /** 편집할 수 없을 때(불러오는 중 · 저장 중) 팔레트와 조작을 잠근다. */\n @property({ type: Boolean }) locked = false\n /**\n * 이 도형이 치르는 값 — 삼각형 · 묶음 · 한 화면에 몇 대 · 등급. 부품을 더하는 바로 그 자리에서 보여야 로우폴리의\n * 기준이 저작을 이끈다. 등급이 D 면 무엇이 넘쳤는지를 적는다. 누르면 성숙도 탭이 열린다.\n */\n @property({ attribute: false }) budget?: V3BudgetLine\n /**\n * 디테일 등급(S/M/L)의 부품 한도(사용자 2026-09-24). 부품을 넣기 **전에** 몇 개 남았는지 보이고, 다 차면 팔레트가\n * 잠긴다. 되풀이는 한 부품으로 센다. 등급을 안 정했으면 한도는 없다.\n */\n @property({ attribute: false }) partBudget?: { level: 'S' | 'M' | 'L' | null; used: number; limit: number | null }\n /** The recipes this domain sees (ADR-0095). */\n @property({ attribute: false }) recipes: V3Recipe[] = []\n /** Why the last recipe put in was refused -- the kernel's sentence, as it is (ADR-0095 condition 3). */\n @property({ type: String }) recipeError = ''\n /** The recipe whose form is open, and the values in it. */\n @state() private openRecipe = ''\n @state() private recipeValues: Record<string, V3RecipeValue> = {}\n\n render() {\n const b = this.partBudget\n const full = !!b && b.limit !== null && b.used >= b.limit\n return html`\n <section>\n <h3>${i18next.t('figure.label.shapes')}</h3>\n <div palette>\n ${RECIPES.map(\n recipe => html`\n <button\n shape\n ?disabled=${this.locked || full}\n title=${full ? i18next.t('figure.text.part-limit-reached') : i18next.t(recipe.label)}\n @click=${() => this.ask('add-part', { kind: recipe.kind })}\n >\n <md-icon>${recipe.icon}</md-icon>\n <span>${i18next.t(recipe.label)}</span>\n </button>\n `\n )}\n </div>\n ${full ? html`<p full>${i18next.t('figure.text.part-limit-reached-short', { level: b!.level, limit: b!.limit })}</p>` : nothing}\n </section>\n ${this.renderRecipes(full)}\n\n <section list>\n <h3>\n <span>${i18next.t('figure.label.parts')}</span>\n <span room ?full=${full} title=${i18next.t('figure.text.limit-comes-from-level')}\n >${b && b.limit !== null ? i18next.t('figure.text.n-of-limit', { used: b.used, limit: b.limit }) : (b?.used ?? this.parts.length)}</span\n >\n ${this.renderShowAll()}\n </h3>\n ${this.renderLevel()}\n ${this.renderBudget()}\n ${this.parts.length === 0\n ? html`<p quiet>${i18next.t('figure.text.no-parts-yet')}</p>`\n : html`<ol>\n ${this.parts.map((part, index) => this.renderRow(part, index))}\n </ol>`}\n </section>\n `\n }\n\n /** 디테일 등급 — 고르면 한도가 바뀐다. 목록 머리 아래. */\n private renderLevel() {\n const b = this.partBudget\n if (!b) return nothing\n return html`<div level>\n <label for=\"detail-level\">${i18next.t('figure.label.detail-level')}</label>\n <select\n id=\"detail-level\"\n title=${i18next.t('figure.text.detail-level-explained')}\n .value=${b.level ?? ''}\n ?disabled=${this.locked}\n @change=${(e: Event) => {\n const value = (e.target as HTMLSelectElement).value\n this.dispatchEvent(new CustomEvent('set-detail-level', { detail: { level: value || null }, bubbles: true, composed: true }))\n }}\n >\n <option value=\"\" ?selected=${!b.level}>${i18next.t('figure.label.detail-none')}</option>\n ${(['S', 'M', 'L'] as const).map(\n one => html`<option value=${one} ?selected=${one === b.level}>\n ${one} ${i18next.t('figure.label.detail-' + one)} · ${i18next.t('figure.text.n-parts-at-most', { limit: PART_LIMIT[one] })}\n </option>`\n )}\n </select>\n </div>`\n }\n\n private renderBudget() {\n const b = this.budget\n if (!b || !this.parts.length) return nothing\n const over = b.grade === 'D'\n return html`<button\n budget\n grade=${b.grade}\n title=${i18next.t('figure.text.parts-budget-open')}\n @click=${() => this.dispatchEvent(new CustomEvent('show-maturity', { bubbles: true, composed: true }))}\n >\n <b>${b.grade}</b>\n <span>\n ${i18next.t('figure.text.parts-budget', {\n triangles: b.triangles.toLocaleString(),\n groups: b.groups.toLocaleString(),\n instances: b.instances.toLocaleString()\n })}\n ${over\n ? html`<em\n >${i18next.t('figure.text.parts-budget-over', {\n budget: i18next.t('figure.label.budget-' + b.tightest.key),\n used: b.tightest.used.toLocaleString(),\n limit: b.tightest.limit.toLocaleString()\n })}</em\n >`\n : nothing}\n </span>\n </button>`\n }\n\n /** 한꺼번에 감추고 한꺼번에 되돌린다 — 하나라도 감춰져 있으면 다 되돌리고, 다 보이면 다 감춘다. */\n private renderShowAll() {\n if (this.parts.length === 0) return nothing\n const anyHidden = this.parts.some(part => part.hidden)\n return html`\n <button\n eye\n all\n ?off=${!anyHidden}\n title=${i18next.t(anyHidden ? 'figure.text.show-all-parts' : 'figure.text.hide-all-parts')}\n @click=${(e: Event) => {\n e.stopPropagation()\n this.ask('set-all-parts-hidden', { hidden: !anyHidden })\n }}\n >\n <md-icon>${anyHidden ? 'visibility' : 'visibility_off'}</md-icon>\n </button>\n `\n }\n\n private renderRow(part: V3PartRow, index: number) {\n const selected = part.id === this.selected\n const off = !!part.hidden\n return html`\n <li\n ?selected=${selected}\n ?off=${off}\n ?invisible=${!!part.invisible}\n @click=${() => this.ask('select-part', { id: part.id })}\n >\n <button\n eye\n ?off=${off}\n title=${i18next.t(off ? 'figure.text.show-this-part' : 'figure.text.hide-this-part')}\n @click=${(e: Event) => {\n e.stopPropagation()\n this.ask('toggle-part-hidden', { id: part.id })\n }}\n >\n <md-icon>${off ? 'visibility_off' : 'visibility'}</md-icon>\n </button>\n <md-icon>${PROVIDER_ICON[part.provider] ?? 'deployed_code'}</md-icon>\n <span label title=${part.id}\n >${partLabelOf(part.id)}${part.copies ? html` <small copies>× ${part.copies}</small>` : nothing}${part.invisible ? html`<small>${i18next.t('figure.label.state-hidden')}</small>` : nothing}</span\n >\n ${selected ? this.renderOps(part.id, index) : nothing}\n </li>\n `\n }\n\n /** 고른 줄의 조작. 못 하는 것은 빼지 않고 흐리게 둔다 — 자리가 밀리면 연달아 지우다 엉뚱한 것을 누른다. */\n private renderOps(id: string, index: number) {\n const count = this.parts.length\n const op = (name: string, icon: string, detail: object, enabled = true) => html`\n <button\n op=${name}\n ?disabled=${!enabled || this.locked}\n title=${i18next.t('figure.button.' + name)}\n @click=${(e: Event) => {\n e.stopPropagation()\n if (enabled && !this.locked) this.ask(name, detail)\n }}\n >\n <md-icon>${icon}</md-icon>\n </button>\n `\n return html`\n <div ops>\n ${op('move-part-up', 'arrow_upward', { id, to: index - 1 }, index > 0)}\n ${op('move-part-down', 'arrow_downward', { id, to: index + 1 }, index < count - 1)}\n ${op('duplicate-part', 'content_copy', { id })} ${op('remove-part', 'delete', { id })}\n </div>\n `\n }\n\n /**\n * Recipes (ADR-0095): a door, a roller row -- parts made in one step, with a few numbers and choices. A recipe for a\n * part needs one chosen; its form opens under it, and putting it in is one step of the history.\n */\n private renderRecipes(full: boolean) {\n if (!this.recipes.length) return nothing\n const language = i18next.language || 'en'\n const open = this.recipes.find(r => r.name === this.openRecipe)\n return html`<section recipes>\n <h3>${i18next.t('figure.label.recipes')}</h3>\n <div recipe-list>\n ${this.recipes.map(recipe => {\n const ready = recipeReady(recipe, this.selected)\n return html`<button\n recipe=${recipe.name}\n ?on=${recipe.name === this.openRecipe}\n ?disabled=${this.locked || full || !ready}\n title=${ready ? recipeWord(recipe.description, language) : i18next.t('figure.text.recipe-needs-a-part')}\n @click=${() => this.openFor(recipe)}\n >\n ${recipeWord(recipe.title, language)}\n </button>`\n })}\n </div>\n ${this.selected || !this.recipes.some(r => r.target === 'part') ? nothing : html`<p hint>${i18next.t('figure.text.recipe-needs-a-part')}</p>`}\n ${open && recipeReady(open, this.selected) ? this.renderRecipeForm(open, language) : nothing}\n </section>`\n }\n\n private openFor(recipe: V3Recipe) {\n if (this.openRecipe === recipe.name) {\n this.openRecipe = ''\n return\n }\n this.openRecipe = recipe.name\n this.recipeValues = Object.fromEntries(recipe.params.map(p => [p.name, p.default]))\n }\n\n private renderRecipeForm(recipe: V3Recipe, language: string) {\n const set = (name: string, value: V3RecipeValue) => (this.recipeValues = { ...this.recipeValues, [name]: value })\n return html`<div recipe-form>\n ${recipe.target === 'part' ? html`<p hint>${i18next.t('figure.text.recipe-onto', { part: this.selected })}</p>` : nothing}\n ${recipe.params.map(param => {\n const label = recipeWord(param.label, language) || param.name\n const value = this.recipeValues[param.name] ?? param.default\n if (param.kind === 'flag')\n return html`<label toggle><input type=\"checkbox\" .checked=${value === true} @change=${(e: Event) => set(param.name, (e.target as HTMLInputElement).checked)} />${label}</label>`\n if (param.kind === 'choice' || param.kind === 'face')\n return html`<label field><span>${label}</span>\n <select @change=${(e: Event) => set(param.name, recipeValueOf(param, (e.target as HTMLSelectElement).value))}>\n ${(param.options ?? []).map(o => html`<option value=${String(o)} ?selected=${o === value}>${String(o)}</option>`)}\n </select></label>`\n return html`<label field><span>${label}${param.kind === 'ratio' ? ' (%)' : ''}</span>\n <input type=\"number\" step=\"any\" param=${param.name} .value=${recipeFieldOf(param, value)} @change=${(e: Event) => set(param.name, recipeValueOf(param, (e.target as HTMLInputElement).value))} /></label>`\n })}\n ${this.recipeError ? html`<p role=\"alert\" error>${this.recipeError}</p>` : nothing}\n <div recipe-actions>\n <button act insert ?disabled=${this.locked} @click=${() => this.ask('insert-recipe', { recipe, values: this.recipeValues })}>\n <md-icon>add</md-icon>${i18next.t('figure.button.insert-recipe')}\n </button>\n <button act @click=${() => (this.openRecipe = '')}>${i18next.t('figure.button.close')}</button>\n </div>\n </div>`\n }\n\n /** 정본을 고치지 않는다 — 무엇을 하겠다는 것만 알린다. */\n private ask(what: string, detail: object) {\n this.dispatchEvent(new CustomEvent(what, { detail, bubbles: true, composed: true }))\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"figure-v3-parts.js","sourceRoot":"","sources":["../../client/modeller/figure-v3-parts.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { PART_LIMIT } from '@hatiolab/figure-model'\nimport type { V3PrimitiveCreation, V3Recipe, V3RecipeValue } from '@hatiolab/figure-model'\nimport { recipeFieldOf, recipeReady, recipeValueOf, recipeWord } from './v3-recipes.js'\nimport type { V3BudgetLine } from './v3-maturity.js'\nimport { partLabelOf } from './v3-fields.js'\n\n/**\n * 도형 팔레트와 부품 목록 — V3 자산의 것.\n *\n * 팔레트는 목록 위에, 눈 단추는 늘 자리를 차지하고, 조작은 고른 줄에서만 보인다.\n *\n * 부품의 도형은 V3 평가가 준 `provider` 로 읽는다. 「숨김」은 여기서도 **작업대의 것**이다 — 저장되지 않고, 자산의 `visibility`(상태에 따라\n * 보이고 안 보이는 것)와는 다른 것이다. 그것은 속성 패널의 몫이다.\n *\n * 이 컴포넌트는 정본을 만지지 않는다. 무엇을 하겠다는 것만 알린다.\n */\n\n/** 한 줄 — V3 평가에서 이 패널이 읽는 것만. */\nexport interface V3PartRow {\n id: string\n provider: string\n /** 작업대에서 잠깐 가린 것. 저장되지 않는다. */\n hidden?: boolean\n /** 자산이 지금 상태에서 보이지 않는다고 한 것. 저장되는 것이고, 여기서는 표시만 한다. */\n invisible?: boolean\n /** 되풀이한 부품이면 지금 몇 개로 그려지나. 되풀이는 한 부품이다 — 한 줄로 보인다. */\n copies?: number\n}\n\n/** 제공자마다의 아이콘. Material Symbols 이름이다. */\nconst PROVIDER_ICON: Record<string, string> = {\n box: 'deployed_code',\n cylinder: 'database',\n ellipticCylinder: 'database',\n frustum: 'change_history',\n sphere: 'circle',\n roundedBox: 'crop_square',\n roundedBoxXZ: 'crop_square',\n hollowBox: 'check_box_outline_blank',\n hollowBoxXZ: 'check_box_outline_blank',\n polygon: 'pentagon',\n portal: 'door_front'\n}\n\n/** 팔레트 — 저작 API 가 만들 수 있는 것만 보인다. */\n/*\n The palette. A plate and a wall are boxes of a shape a person reaches for, so they are starting sizes, not kinds of their own.\n*/\nexport type V3PaletteKind = V3PrimitiveCreation['kind'] | 'plate' | 'wall'\nconst RECIPES: Array<{ kind: V3PaletteKind; icon: string; label: string }> = [\n { kind: 'box', icon: 'deployed_code', label: 'figure.label.primitive-cube' },\n { kind: 'cylinder', icon: 'database', label: 'figure.label.primitive-cylinder' },\n { kind: 'rounded-box', icon: 'crop_square', label: 'figure.label.primitive-rounded-box' },\n { kind: 'sphere', icon: 'circle', label: 'figure.label.primitive-sphere' },\n { kind: 'frustum', icon: 'change_history', label: 'figure.label.primitive-frustum' },\n { kind: 'polygon', icon: 'hexagon', label: 'figure.label.primitive-polygon' },\n { kind: 'plate', icon: 'check_box_outline_blank', label: 'figure.label.primitive-plate' },\n { kind: 'wall', icon: 'crop_portrait', label: 'figure.label.primitive-wall' },\n { kind: 'portal', icon: 'door_front', label: 'figure.label.primitive-portal' }\n]\n\n@customElement('figure-v3-parts')\nexport class FigureV3Parts 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: hidden;\n background-color: var(--md-sys-color-surface-container-lowest);\n /* The divider beside it is the editor's frame's (ox-three-pane). */\n color: var(--md-sys-color-on-surface);\n font: var(--label-font, inherit);\n font-size: 0.76rem;\n /* Folds by its own width: the editor gives this column 140 px when it has two columns (UX, 2026-09-24). */\n container-type: inline-size;\n }\n section {\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n section[list] {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n }\n h3 {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 6px;\n margin: 0 0 6px 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 /* The cost line: the grade's letter in its colour, then what the figure costs. */\n button[budget] {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n width: 100%;\n margin: 0 0 6px;\n padding: 4px 6px;\n font: inherit;\n font-size: 0.68rem;\n line-height: 1.5;\n text-align: left;\n color: var(--md-sys-color-on-surface-variant);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n button[budget]:hover {\n background: var(--md-sys-color-surface-container);\n }\n button[budget] b {\n flex: none;\n min-width: 16px;\n text-align: center;\n border-radius: 4px;\n color: var(--md-sys-color-on-primary-container);\n background: var(--md-sys-color-primary-container);\n }\n button[budget][grade='C'] b {\n color: var(--md-sys-color-on-tertiary-container);\n background: var(--md-sys-color-tertiary-container);\n }\n button[budget][grade='D'] {\n border-color: var(--md-sys-color-error);\n }\n button[budget][grade='D'] b {\n color: var(--md-sys-color-on-error);\n background: var(--md-sys-color-error);\n }\n button[budget] em {\n display: block;\n font-style: normal;\n color: var(--md-sys-color-error);\n }\n span[room][full] {\n color: var(--md-sys-color-error);\n font-weight: 600;\n }\n p[full] {\n margin: 4px 0 0;\n font-size: 0.68rem;\n color: var(--md-sys-color-error);\n }\n div[level] {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr);\n align-items: center;\n gap: 6px;\n margin: 0 0 6px;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[level] select {\n min-width: 0;\n padding: 2px 4px;\n font: inherit;\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n small[copies] {\n color: var(--md-sys-color-on-surface-variant);\n }\n span[room] {\n font-size: 0.7rem;\n font-weight: 400;\n letter-spacing: 0;\n text-transform: none;\n }\n /* Three to a row, small (user, 2026-09-24): the palette is reached for, not read -- the list below is what grows. */\n div[palette] {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 3px;\n }\n button[shape] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1px;\n min-width: 0;\n padding: 5px 1px 4px;\n font: var(--label-font, inherit);\n font-size: 0.6rem;\n line-height: 1.2;\n text-align: center;\n overflow-wrap: anywhere;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n div[recipe-list] {\n display: flex;\n flex-wrap: wrap;\n gap: 3px;\n }\n button[recipe] {\n padding: 4px 8px;\n font: inherit;\n font-size: 0.75rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 999em;\n cursor: pointer;\n }\n button[recipe][on] {\n color: var(--md-sys-color-on-secondary-container);\n background: var(--md-sys-color-secondary-container);\n border-color: transparent;\n }\n button[recipe]:disabled {\n opacity: 0.38;\n cursor: default;\n }\n div[recipe-form] {\n display: grid;\n gap: 6px;\n margin-top: 8px;\n padding: 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n font-size: 0.75rem;\n }\n /* The side pane is narrow (200 px, 160 under 860): the name above its field, not beside it. */\n div[recipe-form] label[field] {\n display: grid;\n gap: 2px;\n }\n div[recipe-form] input[type='number'],\n div[recipe-form] select {\n min-width: 0;\n font: inherit;\n }\n div[recipe-form] p[hint] {\n margin: 0;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[recipe-form] p[error] {\n margin: 0;\n padding: 6px;\n white-space: pre-wrap;\n color: var(--md-sys-color-on-error-container);\n background: var(--md-sys-color-error-container);\n border-radius: 6px;\n }\n div[recipe-actions] {\n display: flex;\n gap: 6px;\n }\n div[recipe-actions] button {\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n font: inherit;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 999em;\n background: none;\n color: var(--md-sys-color-on-surface);\n cursor: pointer;\n }\n div[recipe-actions] button[insert] {\n border-color: transparent;\n color: var(--md-sys-color-on-primary);\n background: var(--md-sys-color-primary);\n }\n button[shape]:hover:not([disabled]) {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n button[shape][disabled] {\n opacity: 0.35;\n cursor: default;\n }\n button[shape] md-icon {\n --md-icon-size: 18px;\n }\n ol {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n li {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 6px;\n border-radius: 6px;\n cursor: pointer;\n }\n li:hover {\n background-color: var(--md-sys-color-surface-container);\n }\n li[selected] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n }\n li md-icon {\n --md-icon-size: 16px;\n flex: none;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n button[eye][all] {\n margin-left: auto;\n opacity: 0.75;\n }\n button[eye][all]:hover {\n opacity: 1;\n }\n button[eye] {\n display: flex;\n flex: none;\n padding: 1px;\n color: inherit;\n background: none;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n opacity: 0;\n }\n li:hover button[eye],\n li[selected] button[eye],\n button[eye][off] {\n opacity: 1;\n }\n button[eye][off] {\n color: var(--md-sys-color-tertiary);\n }\n button[eye] md-icon {\n --md-icon-size: 15px;\n color: inherit;\n }\n li[off] span[label],\n li[off] > md-icon,\n li[invisible] span[label],\n li[invisible] > md-icon {\n opacity: 0.45;\n }\n li[selected] md-icon {\n color: inherit;\n }\n span[label] {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 0.78rem;\n }\n span[label] small {\n margin-left: 4px;\n font-size: 0.66rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n div[ops] {\n display: none;\n gap: 1px;\n }\n li[selected] div[ops] {\n display: flex;\n }\n div[ops] button {\n display: flex;\n padding: 2px;\n color: inherit;\n background: none;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n }\n div[ops] button:hover:not([disabled]) {\n background-color: var(--md-sys-color-surface-container-lowest);\n }\n div[ops] button[disabled] {\n opacity: 0.3;\n cursor: default;\n }\n div[ops] md-icon {\n --md-icon-size: 15px;\n color: inherit;\n }\n /*\n Under 200 px the selected row's four buttons pushed its name out and the last one past the edge (seen at 140,\n 2026-09-24). They go to a line of their own under the name.\n */\n @container (max-width: 199px) {\n li[selected] {\n flex-wrap: wrap;\n }\n li[selected] div[ops] {\n flex-basis: 100%;\n justify-content: flex-end;\n }\n }\n p[quiet] {\n margin: 0;\n font-size: 0.76rem;\n line-height: 1.6;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n `\n ]\n\n @property({ type: Array }) parts: V3PartRow[] = []\n /** 고른 부품의 id. 자리(index)가 아니다 — V3 부품은 id 로 안정하게 가리킨다. */\n @property({ type: String }) selected = ''\n /** 편집할 수 없을 때(불러오는 중 · 저장 중) 팔레트와 조작을 잠근다. */\n @property({ type: Boolean }) locked = false\n /**\n * 이 도형이 치르는 값 — 삼각형 · 묶음 · 한 화면에 몇 대 · 등급. 부품을 더하는 바로 그 자리에서 보여야 로우폴리의\n * 기준이 저작을 이끈다. 등급이 D 면 무엇이 넘쳤는지를 적는다. 누르면 성숙도 탭이 열린다.\n */\n @property({ attribute: false }) budget?: V3BudgetLine\n /**\n * 디테일 등급(S/M/L)의 부품 한도(사용자 2026-09-24). 부품을 넣기 **전에** 몇 개 남았는지 보이고, 다 차면 팔레트가\n * 잠긴다. 되풀이는 한 부품으로 센다. 등급을 안 정했으면 한도는 없다.\n */\n @property({ attribute: false }) partBudget?: { level: 'S' | 'M' | 'L' | null; used: number; limit: number | null }\n /** The recipes this domain sees (ADR-0095). */\n @property({ attribute: false }) recipes: V3Recipe[] = []\n /** Why the last recipe put in was refused -- the kernel's sentence, as it is (ADR-0095 condition 3). */\n @property({ type: String }) recipeError = ''\n /** The recipe whose form is open, and the values in it. */\n @state() private openRecipe = ''\n @state() private recipeValues: Record<string, V3RecipeValue> = {}\n\n render() {\n const b = this.partBudget\n const full = !!b && b.limit !== null && b.used >= b.limit\n return html`\n <section>\n <h3>${i18next.t('figure.label.shapes')}</h3>\n <div palette>\n ${RECIPES.map(\n recipe => html`\n <button\n shape\n ?disabled=${this.locked || full}\n title=${full ? i18next.t('figure.text.part-limit-reached') : i18next.t(recipe.label)}\n @click=${() => this.ask('add-part', { kind: recipe.kind })}\n >\n <md-icon>${recipe.icon}</md-icon>\n <span>${i18next.t(recipe.label)}</span>\n </button>\n `\n )}\n </div>\n ${full ? html`<p full>${i18next.t('figure.text.part-limit-reached-short', { level: b!.level, limit: b!.limit })}</p>` : nothing}\n </section>\n ${this.renderRecipes(full)}\n\n <section list>\n <h3>\n <span>${i18next.t('figure.label.parts')}</span>\n <span room ?full=${full} title=${i18next.t('figure.text.limit-comes-from-level')}\n >${b && b.limit !== null ? i18next.t('figure.text.n-of-limit', { used: b.used, limit: b.limit }) : (b?.used ?? this.parts.length)}</span\n >\n ${this.renderShowAll()}\n </h3>\n ${this.renderLevel()}\n ${this.renderBudget()}\n ${this.parts.length === 0\n ? html`<p quiet>${i18next.t('figure.text.no-parts-yet')}</p>`\n : html`<ol>\n ${this.parts.map((part, index) => this.renderRow(part, index))}\n </ol>`}\n </section>\n `\n }\n\n /** 디테일 등급 — 고르면 한도가 바뀐다. 목록 머리 아래. */\n private renderLevel() {\n const b = this.partBudget\n if (!b) return nothing\n return html`<div level>\n <label for=\"detail-level\">${i18next.t('figure.label.detail-level')}</label>\n <select\n id=\"detail-level\"\n title=${i18next.t('figure.text.detail-level-explained')}\n .value=${b.level ?? ''}\n ?disabled=${this.locked}\n @change=${(e: Event) => {\n const value = (e.target as HTMLSelectElement).value\n this.dispatchEvent(new CustomEvent('set-detail-level', { detail: { level: value || null }, bubbles: true, composed: true }))\n }}\n >\n <option value=\"\" ?selected=${!b.level}>${i18next.t('figure.label.detail-none')}</option>\n ${(['S', 'M', 'L'] as const).map(\n one => html`<option value=${one} ?selected=${one === b.level}>\n ${one} ${i18next.t('figure.label.detail-' + one)} · ${i18next.t('figure.text.n-parts-at-most', { limit: PART_LIMIT[one] })}\n </option>`\n )}\n </select>\n </div>`\n }\n\n private renderBudget() {\n const b = this.budget\n if (!b || !this.parts.length) return nothing\n const over = b.grade === 'D'\n return html`<button\n budget\n grade=${b.grade}\n title=${i18next.t('figure.text.parts-budget-open')}\n @click=${() => this.dispatchEvent(new CustomEvent('show-maturity', { bubbles: true, composed: true }))}\n >\n <b>${b.grade}</b>\n <span>\n ${i18next.t('figure.text.parts-budget', {\n triangles: b.triangles.toLocaleString(),\n groups: b.groups.toLocaleString(),\n instances: b.instances.toLocaleString()\n })}\n ${over\n ? html`<em\n >${i18next.t('figure.text.parts-budget-over', {\n budget: i18next.t('figure.label.budget-' + b.tightest.key),\n used: b.tightest.used.toLocaleString(),\n limit: b.tightest.limit.toLocaleString()\n })}</em\n >`\n : nothing}\n </span>\n </button>`\n }\n\n /** 한꺼번에 감추고 한꺼번에 되돌린다 — 하나라도 감춰져 있으면 다 되돌리고, 다 보이면 다 감춘다. */\n private renderShowAll() {\n if (this.parts.length === 0) return nothing\n const anyHidden = this.parts.some(part => part.hidden)\n return html`\n <button\n eye\n all\n ?off=${!anyHidden}\n title=${i18next.t(anyHidden ? 'figure.text.show-all-parts' : 'figure.text.hide-all-parts')}\n @click=${(e: Event) => {\n e.stopPropagation()\n this.ask('set-all-parts-hidden', { hidden: !anyHidden })\n }}\n >\n <md-icon>${anyHidden ? 'visibility' : 'visibility_off'}</md-icon>\n </button>\n `\n }\n\n private renderRow(part: V3PartRow, index: number) {\n const selected = part.id === this.selected\n const off = !!part.hidden\n return html`\n <li\n ?selected=${selected}\n ?off=${off}\n ?invisible=${!!part.invisible}\n @click=${() => this.ask('select-part', { id: part.id })}\n >\n <button\n eye\n ?off=${off}\n title=${i18next.t(off ? 'figure.text.show-this-part' : 'figure.text.hide-this-part')}\n @click=${(e: Event) => {\n e.stopPropagation()\n this.ask('toggle-part-hidden', { id: part.id })\n }}\n >\n <md-icon>${off ? 'visibility_off' : 'visibility'}</md-icon>\n </button>\n <md-icon>${PROVIDER_ICON[part.provider] ?? 'deployed_code'}</md-icon>\n <span label title=${part.id}\n >${partLabelOf(part.id)}${part.copies ? html` <small copies>× ${part.copies}</small>` : nothing}${part.invisible ? html`<small>${i18next.t('figure.label.state-hidden')}</small>` : nothing}</span\n >\n ${selected ? this.renderOps(part.id, index) : nothing}\n </li>\n `\n }\n\n /** 고른 줄의 조작. 못 하는 것은 빼지 않고 흐리게 둔다 — 자리가 밀리면 연달아 지우다 엉뚱한 것을 누른다. */\n private renderOps(id: string, index: number) {\n const count = this.parts.length\n const op = (name: string, icon: string, detail: object, enabled = true) => html`\n <button\n op=${name}\n ?disabled=${!enabled || this.locked}\n title=${i18next.t('figure.button.' + name)}\n @click=${(e: Event) => {\n e.stopPropagation()\n if (enabled && !this.locked) this.ask(name, detail)\n }}\n >\n <md-icon>${icon}</md-icon>\n </button>\n `\n return html`\n <div ops>\n ${op('move-part-up', 'arrow_upward', { id, to: index - 1 }, index > 0)}\n ${op('move-part-down', 'arrow_downward', { id, to: index + 1 }, index < count - 1)}\n ${op('duplicate-part', 'content_copy', { id })} ${op('remove-part', 'delete', { id })}\n </div>\n `\n }\n\n /**\n * Recipes (ADR-0095): a door, a roller row -- parts made in one step, with a few numbers and choices. A recipe for a\n * part needs one chosen; its form opens under it, and putting it in is one step of the history.\n */\n private renderRecipes(full: boolean) {\n if (!this.recipes.length) return nothing\n const language = i18next.language || 'en'\n const open = this.recipes.find(r => r.name === this.openRecipe)\n return html`<section recipes>\n <h3>${i18next.t('figure.label.recipes')}</h3>\n <div recipe-list>\n ${this.recipes.map(recipe => {\n const ready = recipeReady(recipe, this.selected)\n return html`<button\n recipe=${recipe.name}\n ?on=${recipe.name === this.openRecipe}\n ?disabled=${this.locked || full || !ready}\n title=${ready ? recipeWord(recipe.description, language) : i18next.t('figure.text.recipe-needs-a-part')}\n @click=${() => this.openFor(recipe)}\n >\n ${recipeWord(recipe.title, language)}\n </button>`\n })}\n </div>\n ${this.selected || !this.recipes.some(r => r.target === 'part') ? nothing : html`<p hint>${i18next.t('figure.text.recipe-needs-a-part')}</p>`}\n ${open && recipeReady(open, this.selected) ? this.renderRecipeForm(open, language) : nothing}\n </section>`\n }\n\n private openFor(recipe: V3Recipe) {\n if (this.openRecipe === recipe.name) {\n this.openRecipe = ''\n return\n }\n this.openRecipe = recipe.name\n this.recipeValues = Object.fromEntries(recipe.params.map(p => [p.name, p.default]))\n }\n\n private renderRecipeForm(recipe: V3Recipe, language: string) {\n const set = (name: string, value: V3RecipeValue) => (this.recipeValues = { ...this.recipeValues, [name]: value })\n return html`<div recipe-form>\n ${recipe.target === 'part' ? html`<p hint>${i18next.t('figure.text.recipe-onto', { part: this.selected })}</p>` : nothing}\n ${recipe.params.map(param => {\n const label = recipeWord(param.label, language) || param.name\n const value = this.recipeValues[param.name] ?? param.default\n if (param.kind === 'flag')\n return html`<label toggle><input type=\"checkbox\" .checked=${value === true} @change=${(e: Event) => set(param.name, (e.target as HTMLInputElement).checked)} />${label}</label>`\n if (param.kind === 'choice' || param.kind === 'face')\n return html`<label field><span>${label}</span>\n <select @change=${(e: Event) => set(param.name, recipeValueOf(param, (e.target as HTMLSelectElement).value))}>\n ${(param.options ?? []).map(o => html`<option value=${String(o)} ?selected=${o === value}>${String(o)}</option>`)}\n </select></label>`\n return html`<label field><span>${label}${param.kind === 'ratio' ? ' (%)' : ''}</span>\n <input type=\"number\" step=\"any\" param=${param.name} .value=${recipeFieldOf(param, value)} @change=${(e: Event) => set(param.name, recipeValueOf(param, (e.target as HTMLInputElement).value))} /></label>`\n })}\n ${this.recipeError ? html`<p role=\"alert\" error>${this.recipeError}</p>` : nothing}\n <div recipe-actions>\n <button act insert ?disabled=${this.locked} @click=${() => this.ask('insert-recipe', { recipe, values: this.recipeValues })}>\n <md-icon>add</md-icon>${i18next.t('figure.button.insert-recipe')}\n </button>\n <button act @click=${() => (this.openRecipe = '')}>${i18next.t('figure.button.close')}</button>\n </div>\n </div>`\n }\n\n /** 정본을 고치지 않는다 — 무엇을 하겠다는 것만 알린다. */\n private ask(what: string, detail: object) {\n this.dispatchEvent(new CustomEvent(what, { detail, bubbles: true, composed: true }))\n }\n}\n"]}
|
|
@@ -14,3 +14,13 @@ export declare function recipeReady(recipe: Pick<V3Recipe, 'target'>, selected:
|
|
|
14
14
|
export declare function recipeValueOf(param: V3RecipeParam, typed: string | boolean): V3RecipeValue;
|
|
15
15
|
/** What a field shows for a value: a ratio as a percentage. */
|
|
16
16
|
export declare function recipeFieldOf(param: V3RecipeParam, value: V3RecipeValue): string;
|
|
17
|
+
/** The domain on screen, from the address (`/domain/<subdomain>/…`); '' when the address names none. */
|
|
18
|
+
export declare function subdomainOf(pathname: string): string;
|
|
19
|
+
/** Whose a recipe row is: this domain's own (it may change it) or a parent's (copied in to be changed). */
|
|
20
|
+
export declare function recipeIsOwn(row: {
|
|
21
|
+
domain?: {
|
|
22
|
+
subdomain?: string;
|
|
23
|
+
};
|
|
24
|
+
}, subdomain: string): boolean;
|
|
25
|
+
/** What a parameter takes, in a few words: a range, a percentage range, or its options. */
|
|
26
|
+
export declare function paramRangeOf(param: V3RecipeParam): string;
|
|
@@ -48,4 +48,22 @@ export function recipeFieldOf(param, value) {
|
|
|
48
48
|
return String(Math.round(value * 1e6) / 1e4);
|
|
49
49
|
return String(value);
|
|
50
50
|
}
|
|
51
|
+
/** The domain on screen, from the address (`/domain/<subdomain>/…`); '' when the address names none. */
|
|
52
|
+
export function subdomainOf(pathname) {
|
|
53
|
+
return /^\/domain\/([^/]+)/.exec(pathname)?.[1] ?? '';
|
|
54
|
+
}
|
|
55
|
+
/** Whose a recipe row is: this domain's own (it may change it) or a parent's (copied in to be changed). */
|
|
56
|
+
export function recipeIsOwn(row, subdomain) {
|
|
57
|
+
return !!subdomain && row.domain?.subdomain === subdomain;
|
|
58
|
+
}
|
|
59
|
+
/** What a parameter takes, in a few words: a range, a percentage range, or its options. */
|
|
60
|
+
export function paramRangeOf(param) {
|
|
61
|
+
if (param.kind === 'ratio')
|
|
62
|
+
return `${recipeFieldOf(param, param.min ?? 0)} – ${recipeFieldOf(param, param.max ?? 1)} %`;
|
|
63
|
+
if (param.kind === 'mm')
|
|
64
|
+
return `${param.min ?? ''} – ${param.max ?? ''} mm`;
|
|
65
|
+
if (param.kind === 'flag')
|
|
66
|
+
return '';
|
|
67
|
+
return (param.options ?? []).join(' / ');
|
|
68
|
+
}
|
|
51
69
|
//# sourceMappingURL=v3-recipes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v3-recipes.js","sourceRoot":"","sources":["../../client/modeller/v3-recipes.ts"],"names":[],"mappings":"AAEA;;GAEG;AAEH,sGAAsG;AACtG,MAAM,UAAU,UAAU,CAAC,KAAyC,EAAE,QAAgB;IACpF,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,KAAK,CAAC,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;AACvG,CAAC;AAED,yHAAyH;AACzH,MAAM,UAAU,SAAS,CAAC,IAAuC;IAC/D,MAAM,GAAG,GAAe,EAAE,CAAA;IAC1B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YACrC,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAChG,CAAC;QAAC,MAAM,CAAC;YACP,kBAAkB;QACpB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,MAAgC,EAAE,QAAgB;IAC5E,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAA;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAoB,EAAE,KAAuB;IACzE,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,CAAA;IACpE,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAClD,MAAM,CAAC,GAAG,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;QAChF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC,OAAO,CAAA;QAC7C,OAAO,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IAC/D,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IAC3E,OAAO,MAAM,IAAI,KAAK,CAAC,OAAO,CAAA;AAChC,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,aAAa,CAAC,KAAoB,EAAE,KAAoB;IACtE,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAA;IACrG,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;AACtB,CAAC","sourcesContent":["import type { V3Recipe, V3RecipeParam, V3RecipeValue } from '@hatiolab/figure-model'\n\n/*\n * The recipe section's rules (ADR-0095), kept apart from the element so they can be tested without a browser.\n */\n\n/** A recipe's words in the language on screen: that language, else English, else the first it has. */\nexport function recipeWord(words: Record<string, string> | undefined, language: string): string {\n if (!words) return ''\n return words[language] ?? words[language.split('-')[0]!] ?? words.en ?? Object.values(words)[0] ?? ''\n}\n\n/** The recipes as the server gave them, parsed; a row that is not a recipe is left out rather than breaking the list. */\nexport function recipesOf(rows: ReadonlyArray<{ source: string }>): V3Recipe[] {\n const out: V3Recipe[] = []\n for (const row of rows) {\n try {\n const recipe = JSON.parse(row.source)\n if (recipe && typeof recipe.name === 'string' && Array.isArray(recipe.steps)) out.push(recipe)\n } catch {\n /* not a recipe */\n }\n }\n return out\n}\n\n/** Whether a recipe can be put in now: one for a part needs a part chosen. */\nexport function recipeReady(recipe: Pick<V3Recipe, 'target'>, selected: string): boolean {\n return recipe.target === 'figure' || !!selected\n}\n\n/**\n * A field's value as the recipe takes it. A ratio is shown and typed as a percentage (35 for 0.35), since that is how a\n * person says a share; the rest are taken as they are. A number field left empty or not a number keeps the default.\n */\nexport function recipeValueOf(param: V3RecipeParam, typed: string | boolean): V3RecipeValue {\n if (param.kind === 'flag') return typed === true || typed === 'true'\n if (param.kind === 'ratio' || param.kind === 'mm') {\n const n = typeof typed === 'string' && typed.trim() !== '' ? Number(typed) : NaN\n if (!Number.isFinite(n)) return param.default\n return param.kind === 'ratio' ? Math.round(n * 1e4) / 1e6 : n\n }\n const option = (param.options ?? []).find(o => String(o) === String(typed))\n return option ?? param.default\n}\n\n/** What a field shows for a value: a ratio as a percentage. */\nexport function recipeFieldOf(param: V3RecipeParam, value: V3RecipeValue): string {\n if (param.kind === 'ratio' && typeof value === 'number') return String(Math.round(value * 1e6) / 1e4)\n return String(value)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"v3-recipes.js","sourceRoot":"","sources":["../../client/modeller/v3-recipes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { V3Recipe, V3RecipeParam, V3RecipeValue } from '@hatiolab/figure-model'\n\n/*\n * The recipe section's rules (ADR-0095), kept apart from the element so they can be tested without a browser.\n */\n\n/** A recipe's words in the language on screen: that language, else English, else the first it has. */\nexport function recipeWord(words: Record<string, string> | undefined, language: string): string {\n if (!words) return ''\n return words[language] ?? words[language.split('-')[0]!] ?? words.en ?? Object.values(words)[0] ?? ''\n}\n\n/** The recipes as the server gave them, parsed; a row that is not a recipe is left out rather than breaking the list. */\nexport function recipesOf(rows: ReadonlyArray<{ source: string }>): V3Recipe[] {\n const out: V3Recipe[] = []\n for (const row of rows) {\n try {\n const recipe = JSON.parse(row.source)\n if (recipe && typeof recipe.name === 'string' && Array.isArray(recipe.steps)) out.push(recipe)\n } catch {\n /* not a recipe */\n }\n }\n return out\n}\n\n/** Whether a recipe can be put in now: one for a part needs a part chosen. */\nexport function recipeReady(recipe: Pick<V3Recipe, 'target'>, selected: string): boolean {\n return recipe.target === 'figure' || !!selected\n}\n\n/**\n * A field's value as the recipe takes it. A ratio is shown and typed as a percentage (35 for 0.35), since that is how a\n * person says a share; the rest are taken as they are. A number field left empty or not a number keeps the default.\n */\nexport function recipeValueOf(param: V3RecipeParam, typed: string | boolean): V3RecipeValue {\n if (param.kind === 'flag') return typed === true || typed === 'true'\n if (param.kind === 'ratio' || param.kind === 'mm') {\n const n = typeof typed === 'string' && typed.trim() !== '' ? Number(typed) : NaN\n if (!Number.isFinite(n)) return param.default\n return param.kind === 'ratio' ? Math.round(n * 1e4) / 1e6 : n\n }\n const option = (param.options ?? []).find(o => String(o) === String(typed))\n return option ?? param.default\n}\n\n/** What a field shows for a value: a ratio as a percentage. */\nexport function recipeFieldOf(param: V3RecipeParam, value: V3RecipeValue): string {\n if (param.kind === 'ratio' && typeof value === 'number') return String(Math.round(value * 1e6) / 1e4)\n return String(value)\n}\n\n/** The domain on screen, from the address (`/domain/<subdomain>/…`); '' when the address names none. */\nexport function subdomainOf(pathname: string): string {\n return /^\\/domain\\/([^/]+)/.exec(pathname)?.[1] ?? ''\n}\n\n/** Whose a recipe row is: this domain's own (it may change it) or a parent's (copied in to be changed). */\nexport function recipeIsOwn(row: { domain?: { subdomain?: string } }, subdomain: string): boolean {\n return !!subdomain && row.domain?.subdomain === subdomain\n}\n\n/** What a parameter takes, in a few words: a range, a percentage range, or its options. */\nexport function paramRangeOf(param: V3RecipeParam): string {\n if (param.kind === 'ratio') return `${recipeFieldOf(param, param.min ?? 0)} – ${recipeFieldOf(param, param.max ?? 1)} %`\n if (param.kind === 'mm') return `${param.min ?? ''} – ${param.max ?? ''} mm`\n if (param.kind === 'flag') return ''\n return (param.options ?? []).join(' / ')\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import '@material/web/icon/icon.js';
|
|
2
|
+
import '../modeller/figure-v3-preview.js';
|
|
3
|
+
import { PageView } from '@operato/shell';
|
|
4
|
+
declare const FigureRecipePage_base: (new (...args: any[]) => import("lit").LitElement) & typeof PageView;
|
|
5
|
+
/**
|
|
6
|
+
* The recipes (ADR-0095): what each one puts in, shown put into a room at several sizes, and whose it is.
|
|
7
|
+
*
|
|
8
|
+
* A recipe of this domain's own can be released, taken back to draft, changed or deleted. A parent's -- the ones the
|
|
9
|
+
* system domain ships -- is copied in under its name to be changed, and the copy stands in for it. Figures a recipe was
|
|
10
|
+
* put into keep their parts whatever happens here.
|
|
11
|
+
*/
|
|
12
|
+
export declare class FigureRecipePage extends FigureRecipePage_base {
|
|
13
|
+
static styles: import("lit").CSSResult[];
|
|
14
|
+
private rows;
|
|
15
|
+
private chosen;
|
|
16
|
+
private error;
|
|
17
|
+
private editing;
|
|
18
|
+
private text;
|
|
19
|
+
get context(): {
|
|
20
|
+
title: string;
|
|
21
|
+
};
|
|
22
|
+
pageUpdated(_changes: Record<string, unknown>, _lifecycle: unknown, before: {
|
|
23
|
+
active?: boolean;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
private read;
|
|
26
|
+
private get subdomain();
|
|
27
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
28
|
+
private renderRecipe;
|
|
29
|
+
/** The recipe put into the room it is checked in, with its defaults: what it makes, at several sizes. */
|
|
30
|
+
private renderPreview;
|
|
31
|
+
private choose;
|
|
32
|
+
private edit;
|
|
33
|
+
private run;
|
|
34
|
+
private setState;
|
|
35
|
+
private deleteRecipe;
|
|
36
|
+
/** A parent's recipe copied into this domain as a draft, to be changed here. */
|
|
37
|
+
private copyIn;
|
|
38
|
+
/** The source as typed: checked by the server in the test room; a new name is a new recipe. */
|
|
39
|
+
private saveText;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@material/web/icon/icon.js';
|
|
3
|
+
import '../modeller/figure-v3-preview.js';
|
|
4
|
+
import { css, html, nothing } from 'lit';
|
|
5
|
+
import { customElement, state } from 'lit/decorators.js';
|
|
6
|
+
import { i18next, localize } from '@operato/i18n';
|
|
7
|
+
import { PageView } from '@operato/shell';
|
|
8
|
+
import { OxPrompt } from '@operato/popup';
|
|
9
|
+
import { ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles';
|
|
10
|
+
import { applyV3Recipe, parseV3Recipe, v3RecipeTestRoom } from '@hatiolab/figure-model';
|
|
11
|
+
import { deleteFigureRecipe, fetchFigureRecipeRows, saveFigureRecipe, setFigureRecipeState } from '../graphql/index.js';
|
|
12
|
+
import { paramRangeOf, recipeFieldOf, recipeIsOwn, recipesOf, recipeWord, subdomainOf } from '../modeller/v3-recipes.js';
|
|
13
|
+
/**
|
|
14
|
+
* The recipes (ADR-0095): what each one puts in, shown put into a room at several sizes, and whose it is.
|
|
15
|
+
*
|
|
16
|
+
* A recipe of this domain's own can be released, taken back to draft, changed or deleted. A parent's -- the ones the
|
|
17
|
+
* system domain ships -- is copied in under its name to be changed, and the copy stands in for it. Figures a recipe was
|
|
18
|
+
* put into keep their parts whatever happens here.
|
|
19
|
+
*/
|
|
20
|
+
let FigureRecipePage = class FigureRecipePage extends localize(i18next)(PageView) {
|
|
21
|
+
constructor() {
|
|
22
|
+
super(...arguments);
|
|
23
|
+
this.rows = [];
|
|
24
|
+
this.chosen = '';
|
|
25
|
+
this.error = '';
|
|
26
|
+
this.editing = false;
|
|
27
|
+
this.text = '';
|
|
28
|
+
}
|
|
29
|
+
static { this.styles = [
|
|
30
|
+
WorkSurfacePageStyles,
|
|
31
|
+
ScrollbarStyles,
|
|
32
|
+
css `
|
|
33
|
+
:host {
|
|
34
|
+
display: grid;
|
|
35
|
+
grid-template-columns: minmax(220px, 280px) 1fr;
|
|
36
|
+
background-color: var(--md-sys-color-surface);
|
|
37
|
+
color: var(--md-sys-color-on-surface);
|
|
38
|
+
font-size: 0.85rem;
|
|
39
|
+
}
|
|
40
|
+
@container (max-width: 640px) {
|
|
41
|
+
:host {
|
|
42
|
+
grid-template-columns: 1fr;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
nav {
|
|
46
|
+
overflow: auto;
|
|
47
|
+
border-right: 1px solid var(--md-sys-color-outline-variant);
|
|
48
|
+
padding: 8px;
|
|
49
|
+
}
|
|
50
|
+
nav p[intro] {
|
|
51
|
+
margin: 4px 4px 10px;
|
|
52
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
53
|
+
line-height: 1.5;
|
|
54
|
+
}
|
|
55
|
+
button[row] {
|
|
56
|
+
display: grid;
|
|
57
|
+
gap: 2px;
|
|
58
|
+
width: 100%;
|
|
59
|
+
padding: 8px 10px;
|
|
60
|
+
text-align: left;
|
|
61
|
+
font: inherit;
|
|
62
|
+
color: inherit;
|
|
63
|
+
background: none;
|
|
64
|
+
border: none;
|
|
65
|
+
border-radius: 8px;
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
}
|
|
68
|
+
button[row][on] {
|
|
69
|
+
background: var(--md-sys-color-secondary-container);
|
|
70
|
+
color: var(--md-sys-color-on-secondary-container);
|
|
71
|
+
}
|
|
72
|
+
button[row] small {
|
|
73
|
+
display: flex;
|
|
74
|
+
gap: 6px;
|
|
75
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
76
|
+
}
|
|
77
|
+
span[chip] {
|
|
78
|
+
padding: 0 6px;
|
|
79
|
+
border-radius: 999em;
|
|
80
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
81
|
+
font-size: 0.7rem;
|
|
82
|
+
}
|
|
83
|
+
span[chip][draft] {
|
|
84
|
+
color: var(--md-sys-color-tertiary);
|
|
85
|
+
border-color: var(--md-sys-color-tertiary);
|
|
86
|
+
}
|
|
87
|
+
main {
|
|
88
|
+
overflow: auto;
|
|
89
|
+
padding: 12px 16px;
|
|
90
|
+
display: grid;
|
|
91
|
+
gap: 12px;
|
|
92
|
+
align-content: start;
|
|
93
|
+
}
|
|
94
|
+
main h2 {
|
|
95
|
+
margin: 0;
|
|
96
|
+
font-size: var(--ui-t-title, 16px);
|
|
97
|
+
}
|
|
98
|
+
main p {
|
|
99
|
+
margin: 0;
|
|
100
|
+
line-height: 1.5;
|
|
101
|
+
}
|
|
102
|
+
p[muted] {
|
|
103
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
104
|
+
}
|
|
105
|
+
table {
|
|
106
|
+
border-collapse: collapse;
|
|
107
|
+
width: 100%;
|
|
108
|
+
max-width: 720px;
|
|
109
|
+
}
|
|
110
|
+
th,
|
|
111
|
+
td {
|
|
112
|
+
text-align: left;
|
|
113
|
+
padding: 4px 8px;
|
|
114
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
115
|
+
}
|
|
116
|
+
figure-v3-preview {
|
|
117
|
+
height: 420px;
|
|
118
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
119
|
+
border-radius: 8px;
|
|
120
|
+
overflow: hidden;
|
|
121
|
+
}
|
|
122
|
+
div[actions] {
|
|
123
|
+
display: flex;
|
|
124
|
+
flex-wrap: wrap;
|
|
125
|
+
gap: 8px;
|
|
126
|
+
}
|
|
127
|
+
div[actions] button {
|
|
128
|
+
display: inline-flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
gap: 4px;
|
|
131
|
+
padding: 6px 12px;
|
|
132
|
+
font: inherit;
|
|
133
|
+
color: var(--md-sys-color-on-surface);
|
|
134
|
+
background: none;
|
|
135
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
136
|
+
border-radius: 999em;
|
|
137
|
+
cursor: pointer;
|
|
138
|
+
}
|
|
139
|
+
div[actions] button[primary] {
|
|
140
|
+
color: var(--md-sys-color-on-primary);
|
|
141
|
+
background: var(--md-sys-color-primary);
|
|
142
|
+
border-color: transparent;
|
|
143
|
+
}
|
|
144
|
+
div[actions] button[danger] {
|
|
145
|
+
color: var(--md-sys-color-error);
|
|
146
|
+
border-color: var(--md-sys-color-error);
|
|
147
|
+
}
|
|
148
|
+
p[role='alert'] {
|
|
149
|
+
padding: 8px;
|
|
150
|
+
white-space: pre-wrap;
|
|
151
|
+
color: var(--md-sys-color-on-error-container);
|
|
152
|
+
background: var(--md-sys-color-error-container);
|
|
153
|
+
border-radius: 8px;
|
|
154
|
+
}
|
|
155
|
+
textarea {
|
|
156
|
+
width: 100%;
|
|
157
|
+
min-height: 320px;
|
|
158
|
+
font: 0.78rem/1.4 monospace;
|
|
159
|
+
box-sizing: border-box;
|
|
160
|
+
}
|
|
161
|
+
`
|
|
162
|
+
]; }
|
|
163
|
+
get context() {
|
|
164
|
+
return { title: i18next.t('figure.title.recipes') };
|
|
165
|
+
}
|
|
166
|
+
async pageUpdated(_changes, _lifecycle, before) {
|
|
167
|
+
if (this.active && !before?.active)
|
|
168
|
+
await this.read();
|
|
169
|
+
}
|
|
170
|
+
async read() {
|
|
171
|
+
try {
|
|
172
|
+
this.rows = await fetchFigureRecipeRows();
|
|
173
|
+
if (!this.rows.some(r => r.name === this.chosen))
|
|
174
|
+
this.chosen = this.rows[0]?.name ?? '';
|
|
175
|
+
this.error = '';
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
this.error = error instanceof Error ? error.message : String(error);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
get subdomain() {
|
|
182
|
+
return subdomainOf(location.pathname);
|
|
183
|
+
}
|
|
184
|
+
render() {
|
|
185
|
+
const language = i18next.language || 'en';
|
|
186
|
+
const recipes = recipesOf(this.rows);
|
|
187
|
+
const row = this.rows.find(r => r.name === this.chosen);
|
|
188
|
+
const recipe = recipes.find(r => r.name === this.chosen);
|
|
189
|
+
return html `
|
|
190
|
+
<nav>
|
|
191
|
+
<p intro>${i18next.t('figure.text.recipes-intro')}</p>
|
|
192
|
+
${this.rows.map(one => {
|
|
193
|
+
const r = recipes.find(x => x.name === one.name);
|
|
194
|
+
return html `<button row ?on=${one.name === this.chosen} @click=${() => this.choose(one.name)}>
|
|
195
|
+
<span>${r ? recipeWord(r.title, language) : one.name}</span>
|
|
196
|
+
<small>
|
|
197
|
+
<span>${one.name}</span>
|
|
198
|
+
${one.state === 'draft' ? html `<span chip draft>${i18next.t('figure.label.recipe-draft')}</span>` : nothing}
|
|
199
|
+
${recipeIsOwn(one, this.subdomain) ? nothing : html `<span chip>${i18next.t('figure.label.recipe-inherited')}</span>`}
|
|
200
|
+
</small>
|
|
201
|
+
</button>`;
|
|
202
|
+
})}
|
|
203
|
+
</nav>
|
|
204
|
+
<main>
|
|
205
|
+
${this.error ? html `<p role="alert">${this.error}</p>` : nothing}
|
|
206
|
+
${row && recipe ? this.renderRecipe(row, recipe, language) : html `<p muted>${i18next.t('figure.text.recipes-empty')}</p>`}
|
|
207
|
+
</main>
|
|
208
|
+
`;
|
|
209
|
+
}
|
|
210
|
+
renderRecipe(row, recipe, language) {
|
|
211
|
+
const own = recipeIsOwn(row, this.subdomain);
|
|
212
|
+
return html `
|
|
213
|
+
<h2>${recipeWord(recipe.title, language)}</h2>
|
|
214
|
+
<p>${recipeWord(recipe.description, language)}</p>
|
|
215
|
+
<p muted>
|
|
216
|
+
${i18next.t(recipe.target === 'part' ? 'figure.text.recipe-target-part' : 'figure.text.recipe-target-figure')} ·
|
|
217
|
+
${i18next.t(own ? 'figure.text.recipe-own' : 'figure.text.recipe-inherited', { domain: row.domain?.subdomain ?? '' })}
|
|
218
|
+
</p>
|
|
219
|
+
<table>
|
|
220
|
+
<thead>
|
|
221
|
+
<tr>
|
|
222
|
+
<th>${i18next.t('figure.label.recipe-param')}</th>
|
|
223
|
+
<th>${i18next.t('figure.label.recipe-default')}</th>
|
|
224
|
+
<th>${i18next.t('figure.label.recipe-range')}</th>
|
|
225
|
+
</tr>
|
|
226
|
+
</thead>
|
|
227
|
+
<tbody>
|
|
228
|
+
${recipe.params.map(p => html `<tr>
|
|
229
|
+
<td>${recipeWord(p.label, language) || p.name}</td>
|
|
230
|
+
<td>${p.kind === 'flag' ? i18next.t(p.default ? 'figure.label.yes' : 'figure.label.no') : recipeFieldOf(p, p.default)}${p.kind === 'ratio' ? ' %' : ''}</td>
|
|
231
|
+
<td>${paramRangeOf(p)}</td>
|
|
232
|
+
</tr>`)}
|
|
233
|
+
</tbody>
|
|
234
|
+
</table>
|
|
235
|
+
${this.renderPreview(recipe)}
|
|
236
|
+
<div actions>
|
|
237
|
+
${own
|
|
238
|
+
? html `${row.state === 'draft'
|
|
239
|
+
? html `<button primary @click=${() => this.setState(row.name, 'released')}>${i18next.t('figure.button.recipe-release')}</button>`
|
|
240
|
+
: html `<button @click=${() => this.setState(row.name, 'draft')}>${i18next.t('figure.button.recipe-to-draft')}</button>`}
|
|
241
|
+
<button danger @click=${() => this.deleteRecipe(row.name)}><md-icon>delete</md-icon>${i18next.t('figure.button.recipe-delete')}</button>`
|
|
242
|
+
: html `<button primary @click=${() => this.copyIn(row)}>${i18next.t('figure.button.recipe-copy-in')}</button>`}
|
|
243
|
+
<button @click=${() => this.edit(row)}><md-icon>data_object</md-icon>${i18next.t('figure.button.recipe-edit-source')}</button>
|
|
244
|
+
</div>
|
|
245
|
+
${this.editing
|
|
246
|
+
? html `<p muted>${i18next.t('figure.text.recipe-edit-source')}</p>
|
|
247
|
+
<textarea .value=${this.text} @input=${(e) => (this.text = e.target.value)}></textarea>
|
|
248
|
+
<div actions>
|
|
249
|
+
<button primary @click=${() => this.saveText(own ? row.state : 'draft')}>${i18next.t('figure.button.save')}</button>
|
|
250
|
+
<button @click=${() => (this.editing = false)}>${i18next.t('figure.button.close')}</button>
|
|
251
|
+
</div>`
|
|
252
|
+
: nothing}
|
|
253
|
+
`;
|
|
254
|
+
}
|
|
255
|
+
/** The recipe put into the room it is checked in, with its defaults: what it makes, at several sizes. */
|
|
256
|
+
renderPreview(recipe) {
|
|
257
|
+
let asset;
|
|
258
|
+
try {
|
|
259
|
+
const room = v3RecipeTestRoom();
|
|
260
|
+
asset = applyV3Recipe(room.asset, parseV3Recipe(recipe), recipe.target === 'part' ? { target: room.target } : {}).asset;
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
return html `<p role="alert">${error instanceof Error ? error.message : String(error)}</p>`;
|
|
264
|
+
}
|
|
265
|
+
return html `<p muted>${i18next.t('figure.text.recipe-preview')}</p>
|
|
266
|
+
<figure-v3-preview .asset=${asset} .context=${`recipe:${recipe.name}`}></figure-v3-preview>`;
|
|
267
|
+
}
|
|
268
|
+
choose(name) {
|
|
269
|
+
this.chosen = name;
|
|
270
|
+
this.editing = false;
|
|
271
|
+
this.error = '';
|
|
272
|
+
}
|
|
273
|
+
edit(row) {
|
|
274
|
+
this.editing = !this.editing;
|
|
275
|
+
try {
|
|
276
|
+
this.text = JSON.stringify(JSON.parse(row.source), null, 2);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
this.text = row.source;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async run(action) {
|
|
283
|
+
try {
|
|
284
|
+
await action();
|
|
285
|
+
this.error = '';
|
|
286
|
+
await this.read();
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
this.error = error instanceof Error ? error.message : String(error);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
setState(name, state) {
|
|
293
|
+
return this.run(() => setFigureRecipeState(name, state));
|
|
294
|
+
}
|
|
295
|
+
/* The app's own prompt, as every operato screen asks -- not the browser's confirm box (user, 2026-09-24). */
|
|
296
|
+
async deleteRecipe(name) {
|
|
297
|
+
const sure = await OxPrompt.open({
|
|
298
|
+
type: 'warning',
|
|
299
|
+
title: i18next.t('figure.button.recipe-delete'),
|
|
300
|
+
text: i18next.t('figure.text.recipe-delete-confirm', { name }),
|
|
301
|
+
confirmButton: { text: i18next.t('figure.button.recipe-delete') },
|
|
302
|
+
cancelButton: { text: i18next.t('figure.button.close') }
|
|
303
|
+
});
|
|
304
|
+
if (sure)
|
|
305
|
+
await this.run(() => deleteFigureRecipe(name));
|
|
306
|
+
}
|
|
307
|
+
/** A parent's recipe copied into this domain as a draft, to be changed here. */
|
|
308
|
+
copyIn(row) {
|
|
309
|
+
return this.run(() => saveFigureRecipe(row.source, 'draft'));
|
|
310
|
+
}
|
|
311
|
+
/** The source as typed: checked by the server in the test room; a new name is a new recipe. */
|
|
312
|
+
async saveText(state) {
|
|
313
|
+
let name = '';
|
|
314
|
+
try {
|
|
315
|
+
name = JSON.parse(this.text).name ?? '';
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
this.error = error instanceof Error ? error.message : String(error);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
await this.run(async () => {
|
|
322
|
+
await saveFigureRecipe(this.text, state);
|
|
323
|
+
this.chosen = name;
|
|
324
|
+
this.editing = false;
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
__decorate([
|
|
329
|
+
state(),
|
|
330
|
+
__metadata("design:type", Array)
|
|
331
|
+
], FigureRecipePage.prototype, "rows", void 0);
|
|
332
|
+
__decorate([
|
|
333
|
+
state(),
|
|
334
|
+
__metadata("design:type", Object)
|
|
335
|
+
], FigureRecipePage.prototype, "chosen", void 0);
|
|
336
|
+
__decorate([
|
|
337
|
+
state(),
|
|
338
|
+
__metadata("design:type", Object)
|
|
339
|
+
], FigureRecipePage.prototype, "error", void 0);
|
|
340
|
+
__decorate([
|
|
341
|
+
state(),
|
|
342
|
+
__metadata("design:type", Object)
|
|
343
|
+
], FigureRecipePage.prototype, "editing", void 0);
|
|
344
|
+
__decorate([
|
|
345
|
+
state(),
|
|
346
|
+
__metadata("design:type", Object)
|
|
347
|
+
], FigureRecipePage.prototype, "text", void 0);
|
|
348
|
+
FigureRecipePage = __decorate([
|
|
349
|
+
customElement('figure-recipe-page')
|
|
350
|
+
], FigureRecipePage);
|
|
351
|
+
export { FigureRecipePage };
|
|
352
|
+
//# sourceMappingURL=figure-recipe-page.js.map
|