@things-factory/figure-ui 10.1.113 → 10.1.114
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/editor.ts +39 -5
- package/client/modeller/face-mapping.ts +39 -0
- package/client/modeller/figure-face-mapping.ts +78 -0
- package/client/modeller/figure-section-editor.ts +12 -4
- package/client/modeller/figure-section-panel.ts +5 -1
- package/dist-client/modeller/clips.d.ts +1 -1
- package/dist-client/modeller/editor.d.ts +4 -0
- package/dist-client/modeller/editor.js +40 -5
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/face-mapping.d.ts +15 -0
- package/dist-client/modeller/face-mapping.js +20 -0
- package/dist-client/modeller/face-mapping.js.map +1 -0
- package/dist-client/modeller/figure-face-mapping.d.ts +16 -0
- package/dist-client/modeller/figure-face-mapping.js +85 -0
- package/dist-client/modeller/figure-face-mapping.js.map +1 -0
- package/dist-client/modeller/figure-section-editor.d.ts +4 -1
- package/dist-client/modeller/figure-section-editor.js +14 -3
- package/dist-client/modeller/figure-section-editor.js.map +1 -1
- package/dist-client/modeller/figure-section-panel.d.ts +4 -1
- package/dist-client/modeller/figure-section-panel.js +6 -0
- package/dist-client/modeller/figure-section-panel.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/test/browser/acceptance-page.js +11 -0
- package/test/clip-consent.test.ts +1 -1
- package/test/face-mapping.test.ts +80 -0
- package/test/section-panel.test.ts +1 -1
- package/translations/en.json +7 -1
- package/translations/ja.json +7 -1
- package/translations/ko.json +7 -1
- package/translations/ms.json +7 -1
- package/translations/zh.json +7 -1
|
@@ -9,7 +9,7 @@ import { live } from 'lit/directives/live.js'
|
|
|
9
9
|
import { PART_LIMIT, parseFigureRecipe, resolveFigureRecipe, figurePartCountOf, figureRecipeIdFor, parseFigureAsset, figureSizeEditChanges, figureScaleAnchorOf, figureSectionMeasuresOf, figurePlaneAlignChanges, type FigurePlaneContact, type FigureClipLoss, figurePartFaceOf, type FigureFaceArea, figureRowOf, type FigureRowReading, figureFaceOverhangOf, type FigureFaceOverhang, figureRoomOnlyInputsOf, figureRoomRefitOf } from '@hatiolab/figure-model'
|
|
10
10
|
import type { RecipeTemplate } from './recipes.js'
|
|
11
11
|
import './figure-recipe-catalog.js'
|
|
12
|
-
import type { FigureAsset, FigureAuthoringAction, FigureEvaluatedShape, FigureInputChange, FigurePartEdit, FigurePrimitiveCreation, FigureProposalStep, FigureRecipe, FigureRecipeValue, FigurePlacement } from '@hatiolab/figure-model'
|
|
12
|
+
import type { FigureAsset, FigureAuthoringAction, FigureEvaluatedShape, FigureInputChange, FigurePartEdit, FigurePrimitiveCreation, FigureProposalStep, FigureRecipe, FigureRecipeValue, FigurePlacement, FigureFaceMappingNeed, FigureFaceRemap } from '@hatiolab/figure-model'
|
|
13
13
|
import type { FigureProductSession } from './product-session.js'
|
|
14
14
|
import { captureFigureThumbnail } from './thumbnail.js'
|
|
15
15
|
import { drawableFigureParts } from './renderer.js'
|
|
@@ -396,6 +396,12 @@ export class FigureEditor extends LitElement {
|
|
|
396
396
|
@property({ attribute: false }) firstSave?: (name: string) => Promise<FirstSave | undefined>
|
|
397
397
|
@state() private selected = ''
|
|
398
398
|
@state() private error = ''
|
|
399
|
+
/*
|
|
400
|
+
* An edit that takes a face away which something stands on (ADR-0100 A8): held, with what the kernel says stands
|
|
401
|
+
* there, until the author chooses where each goes -- then the same edit goes again with the remap. Shown where the
|
|
402
|
+
* edit was made (`from`).
|
|
403
|
+
*/
|
|
404
|
+
@state() private faceMapping?: { action: FigureAuthoringAction; need: FigureFaceMappingNeed; from: 'section-editor' | 'panel' }
|
|
399
405
|
/** The same link offered again with the size range narrowed, after a link the declared range could not hold. */
|
|
400
406
|
@state() private retry?: { label: string; action?: FigureAuthoringAction; run?: () => void }
|
|
401
407
|
@state() private tab: SideTab = 'properties'
|
|
@@ -1168,7 +1174,15 @@ export class FigureEditor extends LitElement {
|
|
|
1168
1174
|
></figure-clips>`
|
|
1169
1175
|
}
|
|
1170
1176
|
/** One command, one step of the history. A refusal a person can meet here is said in their words. */
|
|
1171
|
-
|
|
1177
|
+
/** Where a held edit goes once the author has chosen where what stands on its lost faces goes. */
|
|
1178
|
+
private mapFaces(remap: FigureFaceRemap[]) {
|
|
1179
|
+
const held = this.faceMapping
|
|
1180
|
+
if (!held) return
|
|
1181
|
+
const done = this.linkCommand({ ...held.action, remap } as FigureAuthoringAction, held.from)
|
|
1182
|
+
if (done && held.from === 'section-editor') this.closeSectionEditor()
|
|
1183
|
+
}
|
|
1184
|
+
/** Runs a command; true when it was taken. */
|
|
1185
|
+
private linkCommand(action: FigureAuthoringAction, from: 'section-editor' | 'panel' = 'panel'): boolean {
|
|
1172
1186
|
/* Committed or refused, a command in hand is let go of: the figure as it stands is drawn again. */
|
|
1173
1187
|
this.holdAction(null)
|
|
1174
1188
|
this.retry = undefined
|
|
@@ -1177,6 +1191,7 @@ export class FigureEditor extends LitElement {
|
|
|
1177
1191
|
try {
|
|
1178
1192
|
this.session!.command(action)
|
|
1179
1193
|
this.error = ''
|
|
1194
|
+
this.faceMapping = undefined
|
|
1180
1195
|
/* What the command did besides what was asked, in one line: the figure's size declared to follow (ADR ruling Q1). */
|
|
1181
1196
|
if (action.kind === 'follow-size' && !hadSize) {
|
|
1182
1197
|
const size = this.designValues()
|
|
@@ -1187,14 +1202,22 @@ export class FigureEditor extends LitElement {
|
|
|
1187
1202
|
})
|
|
1188
1203
|
}
|
|
1189
1204
|
this.changed()
|
|
1205
|
+
return true
|
|
1190
1206
|
} catch (error) {
|
|
1191
|
-
if (this.askClipLoss(error, () => this.linkCommand(action))) return
|
|
1207
|
+
if (this.askClipLoss(error, () => this.linkCommand(action, from))) return false
|
|
1192
1208
|
const code = (error as { code?: string })?.code
|
|
1209
|
+
/* Something stands on a face this takes away: ask where it goes, there, rather than only saying no. */
|
|
1210
|
+
const need = (error as { details?: FigureFaceMappingNeed }).details
|
|
1211
|
+
if (code === 'FEATURE_MAPPING_REQUIRED' && need?.lost?.length) {
|
|
1212
|
+
this.faceMapping = { action, need, from }
|
|
1213
|
+
this.error = ''
|
|
1214
|
+
return false
|
|
1215
|
+
}
|
|
1193
1216
|
const narrowing = narrowingRetryOf(action, error, (key, params) => i18next.t(key, params))
|
|
1194
1217
|
if (narrowing) {
|
|
1195
1218
|
this.error = narrowing.message
|
|
1196
1219
|
this.retry = { label: narrowing.label, action: narrowing.retry as FigureAuthoringAction }
|
|
1197
|
-
return
|
|
1220
|
+
return false
|
|
1198
1221
|
}
|
|
1199
1222
|
this.error =
|
|
1200
1223
|
code === 'DIMENSION_CYCLE'
|
|
@@ -1204,6 +1227,7 @@ export class FigureEditor extends LitElement {
|
|
|
1204
1227
|
: sizeRangeRefusalOf(error, (key, params) => i18next.t(key, params)) ??
|
|
1205
1228
|
placementRefusalOf(error, (key, params) => i18next.t(key, params)) ??
|
|
1206
1229
|
this.said(error)
|
|
1230
|
+
return false
|
|
1207
1231
|
}
|
|
1208
1232
|
}
|
|
1209
1233
|
private changeField(field: { id: string; unit: FigureInputChange['unit']; role?: string }, event: Event) {
|
|
@@ -1743,6 +1767,7 @@ export class FigureEditor extends LitElement {
|
|
|
1743
1767
|
private choose(id: string) {
|
|
1744
1768
|
/* A copy on the canvas selects its repeat: the part the author made. */
|
|
1745
1769
|
this.selected = id ? ownerOf(id) : id
|
|
1770
|
+
this.faceMapping = undefined
|
|
1746
1771
|
if (id && (this.tab === 'verdict' || this.tab === 'view' || this.tab === 'history')) this.tab = 'properties'
|
|
1747
1772
|
}
|
|
1748
1773
|
private asset(): unknown {
|
|
@@ -1801,8 +1826,14 @@ export class FigureEditor extends LitElement {
|
|
|
1801
1826
|
.kept=${kept}
|
|
1802
1827
|
.pointBlocked=${this.pointBlockedOf(drawn)}
|
|
1803
1828
|
.revision=${this.session?.read().revision ?? 0}
|
|
1804
|
-
|
|
1829
|
+
.faceMapping=${this.faceMapping?.from === 'section-editor' ? this.faceMapping.need : undefined}
|
|
1830
|
+
@command=${(e: CustomEvent) => {
|
|
1831
|
+
/* Held for a choice of where things go: the editor stays open, the choice shown in it. */
|
|
1832
|
+
if (!this.linkCommand(e.detail.action, 'section-editor')) e.detail.held = true
|
|
1833
|
+
}}
|
|
1805
1834
|
@command-live=${(e: CustomEvent) => this.holdAction(e.detail.action)}
|
|
1835
|
+
@face-mapped=${(e: CustomEvent) => this.mapFaces(e.detail.remap)}
|
|
1836
|
+
@face-mapping-cancel=${() => (this.faceMapping = undefined)}
|
|
1806
1837
|
@section-editor-close=${() => this.closeSectionEditor()}
|
|
1807
1838
|
></figure-section-editor>`
|
|
1808
1839
|
}
|
|
@@ -2078,6 +2109,9 @@ export class FigureEditor extends LitElement {
|
|
|
2078
2109
|
<figure-section-panel
|
|
2079
2110
|
.body=${{ ...part, id: this.selected }}
|
|
2080
2111
|
?locked=${unavailable}
|
|
2112
|
+
.faceMapping=${this.faceMapping?.from === 'panel' ? this.faceMapping.need : undefined}
|
|
2113
|
+
@face-mapped=${(e: CustomEvent) => this.mapFaces(e.detail.remap)}
|
|
2114
|
+
@face-mapping-cancel=${() => (this.faceMapping = undefined)}
|
|
2081
2115
|
@command=${(e: CustomEvent) => this.linkCommand(e.detail.action)}
|
|
2082
2116
|
@command-live=${(e: CustomEvent) => this.holdAction(e.detail.action)}
|
|
2083
2117
|
@section-editor-open=${() => (this.sectionEditing = this.selected)}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { FigureFaceMappingNeed, FigureFaceReference, FigureFaceRemap } from '@hatiolab/figure-model'
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Where what stands on a face an edit takes away goes instead (ADR-0100 A8, user 2026-10-02): one row per thing that
|
|
5
|
+
* refers to a lost face, its choices the faces that remain looking the same way, and -- for a joint, never a named
|
|
6
|
+
* face -- let go where it stands. Nothing is chosen for the author: where it goes only they know.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface FaceMappingRow {
|
|
10
|
+
ref: FigureFaceReference
|
|
11
|
+
/** The face it is on now, which the edit takes away. */
|
|
12
|
+
lost: string
|
|
13
|
+
/** The faces it may go to: those that remain, looking the way the lost one did. */
|
|
14
|
+
faces: string[]
|
|
15
|
+
/** Whether it may be let go where it stands: a joint may, a named face may not. */
|
|
16
|
+
detach: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const DETACH = '__detach'
|
|
20
|
+
|
|
21
|
+
export function faceMappingRowsOf(need: FigureFaceMappingNeed): FaceMappingRow[] {
|
|
22
|
+
return need.lost.flatMap(lost => {
|
|
23
|
+
const faces = need.remain.filter(r => lost.axis === undefined || (r.axis === lost.axis && r.normal === lost.normal)).map(r => r.face)
|
|
24
|
+
return lost.refs.map(ref => ({ ref, lost: lost.face, faces, detach: ref.kind === 'joint' }))
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The remap a command takes, once every row is chosen; undefined while one is not. */
|
|
29
|
+
export function remapOf(rows: readonly FaceMappingRow[], picks: Readonly<Record<string, string>>): FigureFaceRemap[] | undefined {
|
|
30
|
+
const out: FigureFaceRemap[] = []
|
|
31
|
+
for (const row of rows) {
|
|
32
|
+
const pick = picks[rowKey(row)]
|
|
33
|
+
if (!pick) return undefined
|
|
34
|
+
out.push({ ref: { kind: row.ref.kind, id: row.ref.id }, to: pick === DETACH ? 'detach' : { face: pick } })
|
|
35
|
+
}
|
|
36
|
+
return out
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const rowKey = (row: FaceMappingRow) => `${row.ref.kind}:${row.ref.id}:${row.lost}`
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { css, html, LitElement, nothing, type PropertyValues } from 'lit'
|
|
2
|
+
import { customElement, property, state } from 'lit/decorators.js'
|
|
3
|
+
|
|
4
|
+
import { i18next, localize } from '@operato/i18n'
|
|
5
|
+
import type { FigureFaceMappingNeed } from '@hatiolab/figure-model'
|
|
6
|
+
|
|
7
|
+
import { SidePanelStyles } from './side-panel-styles.js'
|
|
8
|
+
import { DETACH, faceMappingRowsOf, remapOf, rowKey } from './face-mapping.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 없어지는 면에 붙은 것을 옮길 곳 (ADR-0100 A8): shown where the edit was made, under it. Each thing on a lost face
|
|
12
|
+
* is a row; apply is held until every row is chosen, and then the same edit goes again with the remap, one step.
|
|
13
|
+
*/
|
|
14
|
+
@customElement('figure-face-mapping')
|
|
15
|
+
export class FigureFaceMapping extends localize(i18next)(LitElement) {
|
|
16
|
+
static styles = [
|
|
17
|
+
SidePanelStyles,
|
|
18
|
+
css`
|
|
19
|
+
:host {
|
|
20
|
+
display: grid;
|
|
21
|
+
gap: 8px;
|
|
22
|
+
padding: 10px 12px;
|
|
23
|
+
border: 1px solid var(--md-sys-color-error);
|
|
24
|
+
border-radius: 8px;
|
|
25
|
+
}
|
|
26
|
+
div[row] {
|
|
27
|
+
display: grid;
|
|
28
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
29
|
+
gap: 8px;
|
|
30
|
+
align-items: center;
|
|
31
|
+
}
|
|
32
|
+
div[buttons] {
|
|
33
|
+
display: flex;
|
|
34
|
+
gap: 8px;
|
|
35
|
+
justify-content: flex-end;
|
|
36
|
+
}
|
|
37
|
+
`
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
@property({ attribute: false }) need?: FigureFaceMappingNeed
|
|
41
|
+
@state() private picks: Record<string, string> = {}
|
|
42
|
+
|
|
43
|
+
protected willUpdate(changed: PropertyValues<this>) {
|
|
44
|
+
/* Another refusal is another question: what was chosen for the last one does not carry over. */
|
|
45
|
+
if (changed.has('need')) this.picks = {}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private face = (face: string) => i18next.t('figure.label.face-' + face, { defaultValue: face })
|
|
49
|
+
|
|
50
|
+
render() {
|
|
51
|
+
if (!this.need) return nothing
|
|
52
|
+
const rows = faceMappingRowsOf(this.need)
|
|
53
|
+
const remap = remapOf(rows, this.picks)
|
|
54
|
+
const lost = [...new Set(this.need.lost.map(l => this.face(l.face)))].join(', ')
|
|
55
|
+
return html`<p>${i18next.t('figure.text.face-mapping-lost', { faces: lost })}</p>
|
|
56
|
+
${rows.map(
|
|
57
|
+
row => html`<div row>
|
|
58
|
+
<span>${row.ref.part ?? row.ref.id} · ${this.face(row.lost)}</span>
|
|
59
|
+
<select
|
|
60
|
+
aria-label=${i18next.t('figure.label.face-mapping-to', { part: row.ref.part ?? row.ref.id })}
|
|
61
|
+
@change=${(e: Event) => (this.picks = { ...this.picks, [rowKey(row)]: (e.target as HTMLSelectElement).value })}
|
|
62
|
+
>
|
|
63
|
+
<option value="" ?selected=${!this.picks[rowKey(row)]}>${i18next.t('figure.label.face-mapping-choose')}</option>
|
|
64
|
+
${row.faces.map(f => html`<option value=${f}>${this.face(f)}</option>`)}
|
|
65
|
+
${row.detach ? html`<option value=${DETACH}>${i18next.t('figure.label.face-mapping-detach')}</option>` : nothing}
|
|
66
|
+
</select>
|
|
67
|
+
</div>`
|
|
68
|
+
)}
|
|
69
|
+
<div buttons>
|
|
70
|
+
<button @click=${() => this.dispatchEvent(new CustomEvent('face-mapping-cancel', { bubbles: true, composed: true }))}>
|
|
71
|
+
${i18next.t('figure.button.face-mapping-cancel')}
|
|
72
|
+
</button>
|
|
73
|
+
<button act ?disabled=${!remap} @click=${() => remap && this.dispatchEvent(new CustomEvent('face-mapped', { detail: { remap }, bubbles: true, composed: true }))}>
|
|
74
|
+
${i18next.t('figure.button.face-mapping-apply')}
|
|
75
|
+
</button>
|
|
76
|
+
</div>`
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -4,11 +4,12 @@ import { live } from 'lit/directives/live.js'
|
|
|
4
4
|
|
|
5
5
|
import { i18next, localize } from '@operato/i18n'
|
|
6
6
|
import { figureCavitySectionOf, figureSectionSidesOf } from '@hatiolab/figure-model'
|
|
7
|
-
import type { FigureAuthoringAction, FigureEvaluatedShape, FigureSection, FigureSectionMeasure, FigureSectionPoint } from '@hatiolab/figure-model'
|
|
7
|
+
import type { FigureAuthoringAction, FigureEvaluatedShape, FigureFaceMappingNeed, FigureSection, FigureSectionMeasure, FigureSectionPoint } from '@hatiolab/figure-model'
|
|
8
8
|
import { SidePanelStyles } from './side-panel-styles.js'
|
|
9
9
|
import { apexOver, drawnOf, insertAfter, markedBy, movePoint, pointLabel, removePoint, sectionOf, workingIssuesOf, workingOf, workingShapeOf, type SectionWorking } from './section-drawing.js'
|
|
10
10
|
import { cavityDepthOf, cavityRequestOf, cavityResultOf, END_FACE, sameOf, sidesOf } from './section-controls.js'
|
|
11
11
|
import './figure-cavity-drawing.js'
|
|
12
|
+
import './figure-face-mapping.js'
|
|
12
13
|
import { openPopup } from '@operato/layout'
|
|
13
14
|
import './figure-section-close-choice.js'
|
|
14
15
|
import type { SectionCloseChoice } from './figure-section-close-choice.js'
|
|
@@ -276,6 +277,8 @@ export class FigureSectionEditor extends localize(i18next)(LitElement) {
|
|
|
276
277
|
@property({ attribute: false }) pointBlocked = ''
|
|
277
278
|
/** The figure's revision now; the editor remembers the one it opened at. */
|
|
278
279
|
@property({ type: Number }) revision = 0
|
|
280
|
+
/** What stands on the faces the last apply takes away (ADR-0100 A8): asked here, where the edit was made. */
|
|
281
|
+
@property({ attribute: false }) faceMapping?: FigureFaceMappingNeed
|
|
279
282
|
/** Narrow: the outline or the solid behind. */
|
|
280
283
|
@property({ reflect: true }) show: 'outline' | 'solid' = 'outline'
|
|
281
284
|
/** Set when the space the editor stands in leaves too little beside it to see the solid. */
|
|
@@ -430,8 +433,10 @@ export class FigureSectionEditor extends localize(i18next)(LitElement) {
|
|
|
430
433
|
|
|
431
434
|
private apply() {
|
|
432
435
|
if (this.stale || !this.valid(this.working)) return
|
|
433
|
-
|
|
434
|
-
this.
|
|
436
|
+
const detail: { action: FigureAuthoringAction; held?: boolean } = { action: this.action(this.working) }
|
|
437
|
+
this.dispatchEvent(new CustomEvent('command', { detail, bubbles: true, composed: true }))
|
|
438
|
+
/* Held for a choice of where what stands on a lost face goes: stays open, the choice shown below. */
|
|
439
|
+
if (!detail.held) this.close()
|
|
435
440
|
}
|
|
436
441
|
|
|
437
442
|
/** Whether anything was changed since the editor opened. */
|
|
@@ -526,7 +531,10 @@ export class FigureSectionEditor extends localize(i18next)(LitElement) {
|
|
|
526
531
|
</nav>
|
|
527
532
|
<div body>
|
|
528
533
|
<div outline>${this.drawing(issues, words.length > 0)}</div>
|
|
529
|
-
<aside
|
|
534
|
+
<aside>
|
|
535
|
+
${this.faceMapping ? html`<figure-face-mapping .need=${this.faceMapping}></figure-face-mapping>` : nothing}
|
|
536
|
+
${this.list(issues, words)}
|
|
537
|
+
</aside>
|
|
530
538
|
</div>
|
|
531
539
|
<footer>
|
|
532
540
|
<button ?disabled=${!this.history.length} @click=${() => this.back()}>${i18next.t('figure.button.section-editor-back')}</button>
|
|
@@ -4,10 +4,11 @@ import { customElement, property, state } from 'lit/decorators.js'
|
|
|
4
4
|
import { live } from 'lit/directives/live.js'
|
|
5
5
|
|
|
6
6
|
import { i18next, localize } from '@operato/i18n'
|
|
7
|
-
import type { FigureAuthoringAction, FigureEvaluatedShape } from '@hatiolab/figure-model'
|
|
7
|
+
import type { FigureAuthoringAction, FigureEvaluatedShape, FigureFaceMappingNeed } from '@hatiolab/figure-model'
|
|
8
8
|
import { SidePanelStyles } from './side-panel-styles.js'
|
|
9
9
|
import { drawnOf } from './section-drawing.js'
|
|
10
10
|
import './figure-cavity-drawing.js'
|
|
11
|
+
import './figure-face-mapping.js'
|
|
11
12
|
import { cavityDepthOf, cavityRequestOf, cavityResultOf, cornerLimitOf, cornerRefusalOf, mm, sameOf } from './section-controls.js'
|
|
12
13
|
|
|
13
14
|
type Body = { id: string; shape: FigureEvaluatedShape }
|
|
@@ -59,6 +60,8 @@ export class FigureSectionPanel extends localize(i18next)(LitElement) {
|
|
|
59
60
|
|
|
60
61
|
@property({ attribute: false }) body?: Body
|
|
61
62
|
@property({ type: Boolean }) locked = false
|
|
63
|
+
/** What stands on the faces the last edit here takes away (ADR-0100 A8): asked here, where the edit was made. */
|
|
64
|
+
@property({ attribute: false }) faceMapping?: FigureFaceMappingNeed
|
|
62
65
|
/** What each field would not take, by field, said under it. */
|
|
63
66
|
@state() private refusals: Record<string, string> = {}
|
|
64
67
|
/** What is typed in a field and not yet sent, by field: the figure drawn with it must not type over it. */
|
|
@@ -97,6 +100,7 @@ export class FigureSectionPanel extends localize(i18next)(LitElement) {
|
|
|
97
100
|
: nothing}
|
|
98
101
|
${kind === 'rectangle' ? this.renderCorner(body) : nothing}
|
|
99
102
|
${/* No inside for a drawn section: the control is not there, rather than a line saying so. */ kind === 'rectangle' || kind === 'ellipse' ? this.renderCavity(body) : nothing}
|
|
103
|
+
${this.faceMapping ? html`<figure-face-mapping .need=${this.faceMapping}></figure-face-mapping>` : nothing}
|
|
100
104
|
</section>`
|
|
101
105
|
}
|
|
102
106
|
|
|
@@ -47,6 +47,7 @@ export declare class FigureEditor extends LitElement {
|
|
|
47
47
|
firstSave?: (name: string) => Promise<FirstSave | undefined>;
|
|
48
48
|
private selected;
|
|
49
49
|
private error;
|
|
50
|
+
private faceMapping?;
|
|
50
51
|
/** The same link offered again with the size range narrowed, after a link the declared range could not hold. */
|
|
51
52
|
private retry?;
|
|
52
53
|
private tab;
|
|
@@ -210,6 +211,9 @@ export declare class FigureEditor extends LitElement {
|
|
|
210
211
|
/** The figure's controls: try a value, name it, set its range and travel time, keep the pose. */
|
|
211
212
|
private renderControls;
|
|
212
213
|
/** One command, one step of the history. A refusal a person can meet here is said in their words. */
|
|
214
|
+
/** Where a held edit goes once the author has chosen where what stands on its lost faces goes. */
|
|
215
|
+
private mapFaces;
|
|
216
|
+
/** Runs a command; true when it was taken. */
|
|
213
217
|
private linkCommand;
|
|
214
218
|
private changeField;
|
|
215
219
|
/** Typing or a held spinner: each number on the way is drawn; the field's change records it. */
|
|
@@ -1194,7 +1194,17 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1194
1194
|
></figure-clips>`;
|
|
1195
1195
|
}
|
|
1196
1196
|
/** One command, one step of the history. A refusal a person can meet here is said in their words. */
|
|
1197
|
-
|
|
1197
|
+
/** Where a held edit goes once the author has chosen where what stands on its lost faces goes. */
|
|
1198
|
+
mapFaces(remap) {
|
|
1199
|
+
const held = this.faceMapping;
|
|
1200
|
+
if (!held)
|
|
1201
|
+
return;
|
|
1202
|
+
const done = this.linkCommand({ ...held.action, remap }, held.from);
|
|
1203
|
+
if (done && held.from === 'section-editor')
|
|
1204
|
+
this.closeSectionEditor();
|
|
1205
|
+
}
|
|
1206
|
+
/** Runs a command; true when it was taken. */
|
|
1207
|
+
linkCommand(action, from = 'panel') {
|
|
1198
1208
|
/* Committed or refused, a command in hand is let go of: the figure as it stands is drawn again. */
|
|
1199
1209
|
this.holdAction(null);
|
|
1200
1210
|
this.retry = undefined;
|
|
@@ -1203,6 +1213,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1203
1213
|
try {
|
|
1204
1214
|
this.session.command(action);
|
|
1205
1215
|
this.error = '';
|
|
1216
|
+
this.faceMapping = undefined;
|
|
1206
1217
|
/* What the command did besides what was asked, in one line: the figure's size declared to follow (ADR ruling Q1). */
|
|
1207
1218
|
if (action.kind === 'follow-size' && !hadSize) {
|
|
1208
1219
|
const size = this.designValues();
|
|
@@ -1213,16 +1224,24 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1213
1224
|
});
|
|
1214
1225
|
}
|
|
1215
1226
|
this.changed();
|
|
1227
|
+
return true;
|
|
1216
1228
|
}
|
|
1217
1229
|
catch (error) {
|
|
1218
|
-
if (this.askClipLoss(error, () => this.linkCommand(action)))
|
|
1219
|
-
return;
|
|
1230
|
+
if (this.askClipLoss(error, () => this.linkCommand(action, from)))
|
|
1231
|
+
return false;
|
|
1220
1232
|
const code = error?.code;
|
|
1233
|
+
/* Something stands on a face this takes away: ask where it goes, there, rather than only saying no. */
|
|
1234
|
+
const need = error.details;
|
|
1235
|
+
if (code === 'FEATURE_MAPPING_REQUIRED' && need?.lost?.length) {
|
|
1236
|
+
this.faceMapping = { action, need, from };
|
|
1237
|
+
this.error = '';
|
|
1238
|
+
return false;
|
|
1239
|
+
}
|
|
1221
1240
|
const narrowing = narrowingRetryOf(action, error, (key, params) => i18next.t(key, params));
|
|
1222
1241
|
if (narrowing) {
|
|
1223
1242
|
this.error = narrowing.message;
|
|
1224
1243
|
this.retry = { label: narrowing.label, action: narrowing.retry };
|
|
1225
|
-
return;
|
|
1244
|
+
return false;
|
|
1226
1245
|
}
|
|
1227
1246
|
this.error =
|
|
1228
1247
|
code === 'DIMENSION_CYCLE'
|
|
@@ -1232,6 +1251,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1232
1251
|
: sizeRangeRefusalOf(error, (key, params) => i18next.t(key, params)) ??
|
|
1233
1252
|
placementRefusalOf(error, (key, params) => i18next.t(key, params)) ??
|
|
1234
1253
|
this.said(error);
|
|
1254
|
+
return false;
|
|
1235
1255
|
}
|
|
1236
1256
|
}
|
|
1237
1257
|
changeField(field, event) {
|
|
@@ -1779,6 +1799,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1779
1799
|
choose(id) {
|
|
1780
1800
|
/* A copy on the canvas selects its repeat: the part the author made. */
|
|
1781
1801
|
this.selected = id ? ownerOf(id) : id;
|
|
1802
|
+
this.faceMapping = undefined;
|
|
1782
1803
|
if (id && (this.tab === 'verdict' || this.tab === 'view' || this.tab === 'history'))
|
|
1783
1804
|
this.tab = 'properties';
|
|
1784
1805
|
}
|
|
@@ -1842,8 +1863,15 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1842
1863
|
.kept=${kept}
|
|
1843
1864
|
.pointBlocked=${this.pointBlockedOf(drawn)}
|
|
1844
1865
|
.revision=${this.session?.read().revision ?? 0}
|
|
1845
|
-
|
|
1866
|
+
.faceMapping=${this.faceMapping?.from === 'section-editor' ? this.faceMapping.need : undefined}
|
|
1867
|
+
@command=${(e) => {
|
|
1868
|
+
/* Held for a choice of where things go: the editor stays open, the choice shown in it. */
|
|
1869
|
+
if (!this.linkCommand(e.detail.action, 'section-editor'))
|
|
1870
|
+
e.detail.held = true;
|
|
1871
|
+
}}
|
|
1846
1872
|
@command-live=${(e) => this.holdAction(e.detail.action)}
|
|
1873
|
+
@face-mapped=${(e) => this.mapFaces(e.detail.remap)}
|
|
1874
|
+
@face-mapping-cancel=${() => (this.faceMapping = undefined)}
|
|
1847
1875
|
@section-editor-close=${() => this.closeSectionEditor()}
|
|
1848
1876
|
></figure-section-editor>`;
|
|
1849
1877
|
}
|
|
@@ -2115,6 +2143,9 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
2115
2143
|
<figure-section-panel
|
|
2116
2144
|
.body=${{ ...part, id: this.selected }}
|
|
2117
2145
|
?locked=${unavailable}
|
|
2146
|
+
.faceMapping=${this.faceMapping?.from === 'panel' ? this.faceMapping.need : undefined}
|
|
2147
|
+
@face-mapped=${(e) => this.mapFaces(e.detail.remap)}
|
|
2148
|
+
@face-mapping-cancel=${() => (this.faceMapping = undefined)}
|
|
2118
2149
|
@command=${(e) => this.linkCommand(e.detail.action)}
|
|
2119
2150
|
@command-live=${(e) => this.holdAction(e.detail.action)}
|
|
2120
2151
|
@section-editor-open=${() => (this.sectionEditing = this.selected)}
|
|
@@ -2363,6 +2394,10 @@ __decorate([
|
|
|
2363
2394
|
state(),
|
|
2364
2395
|
__metadata("design:type", Object)
|
|
2365
2396
|
], FigureEditor.prototype, "error", void 0);
|
|
2397
|
+
__decorate([
|
|
2398
|
+
state(),
|
|
2399
|
+
__metadata("design:type", Object)
|
|
2400
|
+
], FigureEditor.prototype, "faceMapping", void 0);
|
|
2366
2401
|
__decorate([
|
|
2367
2402
|
state(),
|
|
2368
2403
|
__metadata("design:type", Object)
|