@things-factory/figure-ui 10.1.85 → 10.1.87
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 +44 -3
- package/client/modeller/figure-section-editor.ts +643 -0
- package/client/modeller/figure-section-panel.ts +7 -0
- package/client/modeller/live.ts +5 -0
- package/client/modeller/section-drawing.ts +150 -0
- package/dist-client/modeller/clips.d.ts +1 -1
- package/dist-client/modeller/editor.d.ts +6 -0
- package/dist-client/modeller/editor.js +50 -2
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-section-editor.d.ts +87 -0
- package/dist-client/modeller/figure-section-editor.js +635 -0
- package/dist-client/modeller/figure-section-editor.js.map +1 -0
- package/dist-client/modeller/figure-section-panel.js +7 -0
- package/dist-client/modeller/figure-section-panel.js.map +1 -1
- package/dist-client/modeller/live.js +7 -0
- package/dist-client/modeller/live.js.map +1 -1
- package/dist-client/modeller/section-drawing.d.ts +52 -0
- package/dist-client/modeller/section-drawing.js +123 -0
- package/dist-client/modeller/section-drawing.js.map +1 -0
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/test/browser/acceptance-page.js +7 -0
- package/test/section-editor.test.ts +232 -0
- package/test/section-panel.test.ts +12 -9
- package/translations/en.json +34 -0
- package/translations/ja.json +34 -0
- package/translations/ko.json +34 -0
- package/translations/ms.json +34 -0
- package/translations/zh.json +34 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { FIGURE_SECTION_PLANE, FIGURE_SECTION_POINT_LIMIT, checkFigureSection, figureExtrusionFeaturesOf, figureSectionSidesOf } from '@hatiolab/figure-model'
|
|
2
|
+
import type { FigureEvaluatedShape, FigureSection, FigureSectionIssue, FigureSectionPoint } from '@hatiolab/figure-model'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* What the section editor does to a drawn section's points, and how it says what is wrong with them (ADR-0100 §3.2).
|
|
6
|
+
* The points are the kernel's own (`{ id, x, z }` for a push along Y), their validity is the kernel's own check, and a
|
|
7
|
+
* point's number on screen is its own name's number (Q9): p3 is 「3」 before and after any edit, and deleting it leaves
|
|
8
|
+
* 1, 2 and 4.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
type Axis = 'x' | 'y' | 'z'
|
|
12
|
+
export type Points = readonly FigureSectionPoint[]
|
|
13
|
+
|
|
14
|
+
/** The number a point is shown by: its name's number. */
|
|
15
|
+
export const pointLabel = (id: string) => /^p(\d+)$/.exec(id)?.[1] ?? id
|
|
16
|
+
|
|
17
|
+
/** The next point's name: one past the highest number the loop has, so a new point never takes a gap's number. */
|
|
18
|
+
export function nextPointId(points: Points): string {
|
|
19
|
+
return `p${Math.max(0, ...points.map(p => Number(/^p(\d+)$/.exec(p.id)?.[1] ?? 0))) + 1}`
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A point in the middle of the side from `from` to the point after it, whole millimetres; the new point's id with it. */
|
|
23
|
+
export function insertAfter(points: Points, from: string, axis: Axis): { points: FigureSectionPoint[]; id: string } {
|
|
24
|
+
const [u, v] = FIGURE_SECTION_PLANE[axis]
|
|
25
|
+
const i = points.findIndex(p => p.id === from)
|
|
26
|
+
const a = points[i]!,
|
|
27
|
+
b = points[(i + 1) % points.length]!
|
|
28
|
+
const id = nextPointId(points)
|
|
29
|
+
const mid = { id, [u]: Math.round((a[u]! + b[u]!) / 2), [v]: Math.round((a[v]! + b[v]!) / 2) } as FigureSectionPoint
|
|
30
|
+
return { points: [...points.slice(0, i + 1), mid, ...points.slice(i + 1)], id }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The points less one, or why not: a section is three points or more. */
|
|
34
|
+
export function removePoint(points: Points, id: string): FigureSectionPoint[] | { refused: 'too-few' } {
|
|
35
|
+
if (points.length <= 3) return { refused: 'too-few' }
|
|
36
|
+
return points.filter(p => p.id !== id)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function movePoint(points: Points, id: string, at: Partial<Record<Axis, number>>): FigureSectionPoint[] {
|
|
40
|
+
return points.map(p => (p.id === id ? { ...p, ...at } : p))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** What is wrong with the points, by the kernel's own check. */
|
|
44
|
+
export const issuesOf = (points: Points, axis: Axis) => checkFigureSection({ kind: 'polygon', points: [...points] }, axis)
|
|
45
|
+
|
|
46
|
+
/** A side said in the loop's order, 3–4 not 4–3: its id is the pair in name order, which a person never sees. */
|
|
47
|
+
function sideLabel(points: Points, axis: Axis, side: string): string {
|
|
48
|
+
const s = figureSectionSidesOf({ kind: 'polygon', points: [...points] }, axis).find(x => x.id === side)
|
|
49
|
+
const [a, b] = s?.from ? [s.from, s.to!] : side.split('~')
|
|
50
|
+
return `${pointLabel(a!)}–${pointLabel(b!)}`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** An issue as the words that say it: an i18n key and its values. */
|
|
54
|
+
export function issueWords(issue: FigureSectionIssue, points: Points, axis: Axis): { key: string; params: Record<string, string> } {
|
|
55
|
+
const line = (s: string) => sideLabel(points, axis, s)
|
|
56
|
+
switch (issue.code) {
|
|
57
|
+
case 'SIDES_CROSS':
|
|
58
|
+
return { key: 'figure.text.section-issue-cross', params: { a: line(issue.sides[0]), b: line(issue.sides[1]) } }
|
|
59
|
+
case 'SIDES_TOUCH':
|
|
60
|
+
return { key: 'figure.text.section-issue-touch', params: { a: line(issue.sides[0]), b: line(issue.sides[1]) } }
|
|
61
|
+
case 'SIDES_FOLD':
|
|
62
|
+
return { key: 'figure.text.section-issue-fold', params: { a: line(issue.sides[0]), b: line(issue.sides[1]) } }
|
|
63
|
+
case 'ZERO_LENGTH_SIDE':
|
|
64
|
+
return { key: 'figure.text.section-issue-zero-length', params: { a: line(issue.side) } }
|
|
65
|
+
case 'ZERO_AREA':
|
|
66
|
+
return { key: 'figure.text.section-issue-zero-area', params: {} }
|
|
67
|
+
case 'TOO_FEW_POINTS':
|
|
68
|
+
return { key: 'figure.text.section-issue-too-few', params: {} }
|
|
69
|
+
case 'TOO_MANY_POINTS':
|
|
70
|
+
return { key: 'figure.text.section-issue-too-many', params: { limit: String(issue.limit) } }
|
|
71
|
+
case 'CORNER_TOO_LARGE':
|
|
72
|
+
return { key: 'figure.text.section-issue-corner', params: { max: String(Math.round(issue.max * 1000) / 1000) } }
|
|
73
|
+
default:
|
|
74
|
+
return { key: 'figure.text.section-issue-number', params: {} }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Every side and point an issue names, to mark on the drawing -- by more than colour: the list says them too. */
|
|
79
|
+
export function markedBy(issues: readonly FigureSectionIssue[]): { sides: Set<string>; points: Set<string> } {
|
|
80
|
+
const sides = new Set<string>(),
|
|
81
|
+
points = new Set<string>()
|
|
82
|
+
for (const i of issues) {
|
|
83
|
+
if ('sides' in i) for (const s of i.sides) sides.add(s)
|
|
84
|
+
if ('side' in i) sides.add(i.side)
|
|
85
|
+
if ('point' in i) points.add(i.point)
|
|
86
|
+
if ('at' in i) points.add(String(i.at).split('.')[0]!)
|
|
87
|
+
}
|
|
88
|
+
for (const s of sides) for (const p of s.split('~')) points.add(p)
|
|
89
|
+
return { sides, points }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** How far the drawing reaches from the origin: the points' farthest coordinate with room around, in 50 mm steps. */
|
|
93
|
+
export function extentOf(points: Points, axis: Axis): number {
|
|
94
|
+
const [u, v] = FIGURE_SECTION_PLANE[axis]
|
|
95
|
+
const far = Math.max(1, ...points.flatMap(p => [Math.abs(p[u] ?? 0), Math.abs(p[v] ?? 0)]))
|
|
96
|
+
return Math.ceil((far * 1.4 + 40) / 50) * 50
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const POINT_LIMIT = FIGURE_SECTION_POINT_LIMIT
|
|
100
|
+
|
|
101
|
+
/** What the section editor holds while it is open: the points of a drawn section, the widths across, the length. */
|
|
102
|
+
export interface SectionWorking {
|
|
103
|
+
points: FigureSectionPoint[]
|
|
104
|
+
/** Along each plane axis: a rectangle's size, an ellipse's diameter. Empty for a drawn section. */
|
|
105
|
+
across: Partial<Record<Axis, number>>
|
|
106
|
+
length: number
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** The working state of a pushed part as it stands. */
|
|
110
|
+
export function workingOf(shape: FigureEvaluatedShape): SectionWorking {
|
|
111
|
+
const e = shape.extrusion!
|
|
112
|
+
const s = e.section
|
|
113
|
+
const [u, v] = FIGURE_SECTION_PLANE[e.axis]
|
|
114
|
+
const across =
|
|
115
|
+
s.kind === 'rectangle'
|
|
116
|
+
? { [u]: s.size[u]!, [v]: s.size[v]! }
|
|
117
|
+
: s.kind === 'ellipse'
|
|
118
|
+
? { [u]: 2 * s.radius[u]!, [v]: 2 * s.radius[v]! }
|
|
119
|
+
: {}
|
|
120
|
+
return { points: s.kind === 'polygon' ? s.points.map(p => ({ ...p })) : [], across, length: e.end - e.start }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The section the working state makes, of the part's own kind: its corners kept, its points or widths the working ones. */
|
|
124
|
+
export function sectionOf(shape: FigureEvaluatedShape, w: SectionWorking): FigureSection {
|
|
125
|
+
const s = shape.extrusion!.section
|
|
126
|
+
const [u, v] = FIGURE_SECTION_PLANE[shape.extrusion!.axis]
|
|
127
|
+
if (s.kind === 'polygon') return { kind: 'polygon', points: w.points }
|
|
128
|
+
if (s.kind === 'ellipse') return { kind: 'ellipse', radius: { [u]: w.across[u]! / 2, [v]: w.across[v]! / 2 } }
|
|
129
|
+
return { kind: 'rectangle', size: { [u]: w.across[u]!, [v]: w.across[v]! }, ...(s.corner ? { corner: s.corner } : {}) }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** What would be refused, as words, with the section's own issues for marking points and sides. By the kernel's checks. */
|
|
133
|
+
export function workingIssuesOf(
|
|
134
|
+
shape: FigureEvaluatedShape,
|
|
135
|
+
w: SectionWorking
|
|
136
|
+
): { issues: FigureSectionIssue[]; words: Array<{ key: string; params: Record<string, string> }> } {
|
|
137
|
+
const e = shape.extrusion!
|
|
138
|
+
const section = sectionOf(shape, w)
|
|
139
|
+
const issues = checkFigureSection(section, e.axis)
|
|
140
|
+
const words = issues.map(i => issueWords(i, w.points, e.axis))
|
|
141
|
+
if (!(Number.isFinite(w.length) && w.length > 0)) words.push({ key: 'figure.text.section-issue-length', params: {} })
|
|
142
|
+
if (!issues.length && words.length === 0 && shape.cavity) {
|
|
143
|
+
const made = figureExtrusionFeaturesOf({ axis: e.axis, section, start: e.start, end: e.start + w.length }, [shape.cavity])
|
|
144
|
+
if ('refused' in made) {
|
|
145
|
+
const r = made.refused
|
|
146
|
+
words.push({ key: r.code === 'CAVITY_NO_LENGTH' ? 'figure.text.section-issue-cavity-length' : 'figure.text.section-issue-cavity-walls', params: {} })
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { issues, words }
|
|
150
|
+
}
|
|
@@ -7,6 +7,7 @@ import './canvas.js';
|
|
|
7
7
|
import './figure-parts.js';
|
|
8
8
|
import './figure-material-panel.js';
|
|
9
9
|
import './figure-section-panel.js';
|
|
10
|
+
import './figure-section-editor.js';
|
|
10
11
|
import './figure-links.js';
|
|
11
12
|
import './figure-controls.js';
|
|
12
13
|
import './figure-clips.js';
|
|
@@ -73,6 +74,8 @@ export declare class FigureEditor extends LitElement {
|
|
|
73
74
|
private hiddenParts;
|
|
74
75
|
/** A value in hand -- typed, held on a spinner, scrubbed, dragged -- drawn at once and recorded on release. */
|
|
75
76
|
private live;
|
|
77
|
+
/** The part whose drawn section is open in the section editor (ADR-0100 §3.2), or null. */
|
|
78
|
+
private sectionEditing;
|
|
76
79
|
/**
|
|
77
80
|
* A command seen before it is done (ADR-0097 §5.5): worked out on a copy for the figure, version, part and command it
|
|
78
81
|
* names, and shown only while all four are still the ones on screen. Nothing of it is saved or undone.
|
|
@@ -134,6 +137,7 @@ export declare class FigureEditor extends LitElement {
|
|
|
134
137
|
private currentInputs;
|
|
135
138
|
/** A value in hand: an input's value (design or pose) or a part edit. Nothing is recorded. */
|
|
136
139
|
private hold;
|
|
140
|
+
private closeSectionEditor;
|
|
137
141
|
/** A command in hand, drawn and not run; `null` lets go of it and the figure as it stands is drawn again. */
|
|
138
142
|
private holdAction;
|
|
139
143
|
private changed;
|
|
@@ -289,6 +293,8 @@ export declare class FigureEditor extends LitElement {
|
|
|
289
293
|
* recipe, or an AI draft when the page offers one. Nothing is added until one is chosen; the type, the space, the
|
|
290
294
|
* mounting face and the tags are not asked for first.
|
|
291
295
|
*/
|
|
296
|
+
/** The section editor over the canvas, the figure behind it drawing what the working points make. */
|
|
297
|
+
private renderSectionEditor;
|
|
292
298
|
private renderStart;
|
|
293
299
|
/**
|
|
294
300
|
* Where the panel is, held at its top: the model by its name -- the way back to its settings -- and the part chosen,
|
|
@@ -7,7 +7,7 @@ import { notify } from '@operato/layout';
|
|
|
7
7
|
import { OxPrompt } from '@operato/popup';
|
|
8
8
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
9
9
|
import { live } from 'lit/directives/live.js';
|
|
10
|
-
import { PART_LIMIT, parseFigureRecipe, resolveFigureRecipe, figurePartCountOf, figureRecipeIdFor, parseFigureAsset, figureSizeEditChanges, figureScaleAnchorOf, figurePlaneAlignChanges, figurePartFaceOf, figureRowOf, figureFaceOverhangOf, figureRoomOnlyInputsOf, figureRoomRefitOf } from '@hatiolab/figure-model';
|
|
10
|
+
import { PART_LIMIT, parseFigureRecipe, resolveFigureRecipe, figurePartCountOf, figureRecipeIdFor, parseFigureAsset, figureSizeEditChanges, figureScaleAnchorOf, figureSectionMeasuresOf, figurePlaneAlignChanges, figurePartFaceOf, figureRowOf, figureFaceOverhangOf, figureRoomOnlyInputsOf, figureRoomRefitOf } from '@hatiolab/figure-model';
|
|
11
11
|
import './figure-recipe-catalog.js';
|
|
12
12
|
import { captureFigureThumbnail } from './thumbnail.js';
|
|
13
13
|
import { drawableFigureParts } from './renderer.js';
|
|
@@ -15,6 +15,7 @@ import './canvas.js';
|
|
|
15
15
|
import './figure-parts.js';
|
|
16
16
|
import './figure-material-panel.js';
|
|
17
17
|
import './figure-section-panel.js';
|
|
18
|
+
import './figure-section-editor.js';
|
|
18
19
|
import { faceHighlightOf, ghostPartsOf, previewWordsOf, rowWordsOf } from './command-preview.js';
|
|
19
20
|
import { motionEndsOf } from './motion-ends.js';
|
|
20
21
|
import './figure-links.js';
|
|
@@ -70,12 +71,20 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
70
71
|
this.hiddenParts = new Set();
|
|
71
72
|
/** A value in hand -- typed, held on a spinner, scrubbed, dragged -- drawn at once and recorded on release. */
|
|
72
73
|
this.live = NOTHING_LIVE;
|
|
74
|
+
/** The part whose drawn section is open in the section editor (ADR-0100 §3.2), or null. */
|
|
75
|
+
this.sectionEditing = null;
|
|
73
76
|
this.lastContext = null;
|
|
74
77
|
/** The modeller's keys (`keys.ts`): undo, redo, duplicate, remove, back to the whole figure, the gizmo's modes. */
|
|
75
78
|
this.onKey = (e) => {
|
|
76
79
|
const view = this.session?.read();
|
|
77
80
|
if (!view || view.loading || view.saving || !view.context || !this.isConnected)
|
|
78
81
|
return;
|
|
82
|
+
/*
|
|
83
|
+
* The section editor open takes the keys for its own: Esc cancels it, Delete deletes a point, Ctrl+Z undoes a point.
|
|
84
|
+
* Heard here first, they deselected the part, deleted the part and undid the figure's last step (2026-09-30).
|
|
85
|
+
*/
|
|
86
|
+
if (this.sectionEditing)
|
|
87
|
+
return;
|
|
79
88
|
const canvas = this.renderRoot.querySelector('figure-canvas');
|
|
80
89
|
const path = e.composedPath();
|
|
81
90
|
const action = keyActionOf({ key: e.key, ctrlKey: e.ctrlKey, metaKey: e.metaKey, shiftKey: e.shiftKey, altKey: e.altKey, target: path[0], inScene: !!canvas && path.includes(canvas) }, this.selected);
|
|
@@ -622,6 +631,12 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
622
631
|
this.selected = '';
|
|
623
632
|
if (_changed.has('selected'))
|
|
624
633
|
this.gizmoMode = undefined;
|
|
634
|
+
/*
|
|
635
|
+
* The section editor is of one part: when the selection moves off it by any way -- another part picked, the scene's
|
|
636
|
+
* own Esc, the part removed, another figure opened -- it closes and lets go of the preview it held.
|
|
637
|
+
*/
|
|
638
|
+
if (this.sectionEditing && this.sectionEditing !== this.selected)
|
|
639
|
+
this.closeSectionEditor();
|
|
625
640
|
}
|
|
626
641
|
/** The scene toggles: the mode already shown takes the gizmo away. */
|
|
627
642
|
useGizmo(mode) {
|
|
@@ -693,6 +708,10 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
693
708
|
hold(values, edit) {
|
|
694
709
|
this.live = { values: { ...this.live.values, ...values }, ...(edit ? { edit } : this.live.edit ? { edit: this.live.edit } : {}) };
|
|
695
710
|
}
|
|
711
|
+
closeSectionEditor() {
|
|
712
|
+
this.sectionEditing = null;
|
|
713
|
+
this.holdAction(null);
|
|
714
|
+
}
|
|
696
715
|
/** A command in hand, drawn and not run; `null` lets go of it and the figure as it stands is drawn again. */
|
|
697
716
|
holdAction(action) {
|
|
698
717
|
const { action: _, ...rest } = this.live;
|
|
@@ -1801,6 +1820,25 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1801
1820
|
* recipe, or an AI draft when the page offers one. Nothing is added until one is chosen; the type, the space, the
|
|
1802
1821
|
* mounting face and the tags are not asked for first.
|
|
1803
1822
|
*/
|
|
1823
|
+
/** The section editor over the canvas, the figure behind it drawing what the working points make. */
|
|
1824
|
+
renderSectionEditor(geometry) {
|
|
1825
|
+
const part = geometry.find(g => ownerOf(g.id) === this.selected);
|
|
1826
|
+
/* A door frame's section is its own width, height, jamb and head: its fields edit it, not an outline. */
|
|
1827
|
+
if (!part?.shape?.extrusion || part.shape.dimensions.jamb !== undefined)
|
|
1828
|
+
return nothing;
|
|
1829
|
+
const asset = this.asset();
|
|
1830
|
+
const measures = figureSectionMeasuresOf(asset, this.selected);
|
|
1831
|
+
const kept = figureScaleAnchorOf(asset, this.selected)?.[part.shape.extrusion.axis];
|
|
1832
|
+
return html `<figure-section-editor
|
|
1833
|
+
.body=${{ ...part, id: this.selected }}
|
|
1834
|
+
.measures=${measures}
|
|
1835
|
+
.kept=${kept}
|
|
1836
|
+
.revision=${this.session?.read().revision ?? 0}
|
|
1837
|
+
@command=${(e) => this.linkCommand(e.detail.action)}
|
|
1838
|
+
@command-live=${(e) => this.holdAction(e.detail.action)}
|
|
1839
|
+
@section-editor-close=${() => this.closeSectionEditor()}
|
|
1840
|
+
></figure-section-editor>`;
|
|
1841
|
+
}
|
|
1804
1842
|
renderStart() {
|
|
1805
1843
|
const shapes = ['box', 'cylinder', 'plate'];
|
|
1806
1844
|
/* Where it is authored from, chosen before any part (ADR-0097): the figure's own setting, no room made up for it. */
|
|
@@ -2007,6 +2045,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
2007
2045
|
></figure-preview>`
|
|
2008
2046
|
: html `<div middle>
|
|
2009
2047
|
${view.geometry.length === 0 && !unavailable ? this.renderStart() : nothing}
|
|
2048
|
+
${this.sectionEditing && this.sectionEditing === this.selected && !unavailable ? this.renderSectionEditor(view.geometry) : nothing}
|
|
2010
2049
|
<figure-canvas
|
|
2011
2050
|
?startShown=${view.geometry.length === 0 && !unavailable}
|
|
2012
2051
|
.view=${this.view}
|
|
@@ -2061,6 +2100,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
2061
2100
|
?locked=${unavailable}
|
|
2062
2101
|
@command=${(e) => this.linkCommand(e.detail.action)}
|
|
2063
2102
|
@command-live=${(e) => this.holdAction(e.detail.action)}
|
|
2103
|
+
@section-editor-open=${() => (this.sectionEditing = this.selected)}
|
|
2064
2104
|
></figure-section-panel>
|
|
2065
2105
|
<figure-material-panel
|
|
2066
2106
|
.body=${{ ...part, id: this.selected }}
|
|
@@ -2221,7 +2261,11 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
2221
2261
|
this.linkCommand(refit.step);
|
|
2222
2262
|
}
|
|
2223
2263
|
renderSingle(name, field, unavailable) {
|
|
2224
|
-
|
|
2264
|
+
/* A drawn section's point is called by its number and axis, as the section editor calls it (ADR-0100 §2.1, Q9). */
|
|
2265
|
+
const point = /^point\.p(\d+)\.([xyz])$/.exec(name);
|
|
2266
|
+
const words = point
|
|
2267
|
+
? [i18next.t('figure.label.section-editor-coordinate', { point: point[1], axis: point[2].toUpperCase() })]
|
|
2268
|
+
: name.split('.').map(part => i18next.t('figure.label.field-' + part, { defaultValue: part }));
|
|
2225
2269
|
return html `<span ?scrub=${!unavailable} @pointerdown=${(e) => this.scrub(field, e, unavailable)}>${words.join(' · ')} (${field.unit})</span
|
|
2226
2270
|
><input
|
|
2227
2271
|
aria-label=${field.id}
|
|
@@ -2330,6 +2374,10 @@ __decorate([
|
|
|
2330
2374
|
state(),
|
|
2331
2375
|
__metadata("design:type", Object)
|
|
2332
2376
|
], FigureEditor.prototype, "live", void 0);
|
|
2377
|
+
__decorate([
|
|
2378
|
+
state(),
|
|
2379
|
+
__metadata("design:type", Object)
|
|
2380
|
+
], FigureEditor.prototype, "sectionEditing", void 0);
|
|
2333
2381
|
__decorate([
|
|
2334
2382
|
state(),
|
|
2335
2383
|
__metadata("design:type", Object)
|