@things-factory/figure-ui 10.1.147 → 10.1.149
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/bootstrap.ts +10 -2
- package/client/graphql/index.ts +20 -0
- package/client/modeller/canvas.ts +6 -2
- package/client/modeller/carrier-declaration.ts +63 -0
- package/client/modeller/editor.ts +62 -27
- package/client/modeller/figure-carrier.ts +117 -0
- package/client/modeller/figure-cavity-drawing.ts +3 -9
- package/client/modeller/figure-first-save.ts +4 -7
- package/client/modeller/figure-history-panel.ts +2 -3
- package/client/modeller/figure-links.ts +46 -60
- package/client/modeller/figure-material-panel.ts +4 -14
- package/client/modeller/figure-mesh-choice.ts +68 -0
- package/client/modeller/figure-parts.ts +23 -1
- package/client/modeller/figure-preview.ts +2 -2
- package/client/modeller/figure-properties.ts +5 -3
- package/client/modeller/figure-section-editor.ts +12 -33
- package/client/modeller/figure-section-panel.ts +6 -7
- package/client/modeller/figure-slot-layout-panel.ts +208 -0
- package/client/modeller/figure-verdict.ts +21 -31
- package/client/modeller/first-save.ts +23 -0
- package/client/modeller/links.ts +52 -1
- package/client/modeller/material-edit.ts +38 -0
- package/client/modeller/maturity.ts +28 -0
- package/client/modeller/mesh-import.ts +77 -0
- package/client/modeller/modeller-route.ts +9 -0
- package/client/modeller/mounting-part.ts +15 -0
- package/client/modeller/newest-answer.ts +21 -0
- package/client/modeller/preview-model.ts +5 -0
- package/client/modeller/refusals.ts +168 -82
- package/client/modeller/section-controls.ts +25 -1
- package/client/modeller/section-drawing.ts +43 -1
- package/client/modeller/slot-layout-host.ts +140 -0
- package/client/pages/figure-modeller-page.ts +6 -5
- package/client/viewparts/detail-route.ts +7 -0
- package/client/viewparts/figure-detail-dock.ts +1 -3
- package/dist-client/bootstrap.d.ts +1 -1
- package/dist-client/bootstrap.js +9 -2
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/graphql/index.d.ts +5 -0
- package/dist-client/graphql/index.js +19 -0
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/canvas.d.ts +2 -0
- package/dist-client/modeller/canvas.js +10 -2
- package/dist-client/modeller/canvas.js.map +1 -1
- package/dist-client/modeller/carrier-declaration.d.ts +31 -0
- package/dist-client/modeller/carrier-declaration.js +53 -0
- package/dist-client/modeller/carrier-declaration.js.map +1 -0
- package/dist-client/modeller/editor.d.ts +13 -0
- package/dist-client/modeller/editor.js +62 -27
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-carrier.d.ts +26 -0
- package/dist-client/modeller/figure-carrier.js +123 -0
- package/dist-client/modeller/figure-carrier.js.map +1 -0
- package/dist-client/modeller/figure-cavity-drawing.js +3 -11
- package/dist-client/modeller/figure-cavity-drawing.js.map +1 -1
- package/dist-client/modeller/figure-first-save.d.ts +4 -0
- package/dist-client/modeller/figure-first-save.js +5 -8
- package/dist-client/modeller/figure-first-save.js.map +1 -1
- package/dist-client/modeller/figure-history-panel.js +2 -4
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-links.d.ts +4 -1
- package/dist-client/modeller/figure-links.js +47 -57
- package/dist-client/modeller/figure-links.js.map +1 -1
- package/dist-client/modeller/figure-material-panel.d.ts +0 -1
- package/dist-client/modeller/figure-material-panel.js +4 -14
- package/dist-client/modeller/figure-material-panel.js.map +1 -1
- package/dist-client/modeller/figure-mesh-choice.d.ts +16 -0
- package/dist-client/modeller/figure-mesh-choice.js +75 -0
- package/dist-client/modeller/figure-mesh-choice.js.map +1 -0
- package/dist-client/modeller/figure-parts.d.ts +1 -1
- package/dist-client/modeller/figure-parts.js +22 -0
- package/dist-client/modeller/figure-parts.js.map +1 -1
- package/dist-client/modeller/figure-preview.js +2 -2
- package/dist-client/modeller/figure-preview.js.map +1 -1
- package/dist-client/modeller/figure-properties.d.ts +2 -1
- package/dist-client/modeller/figure-properties.js +5 -3
- package/dist-client/modeller/figure-properties.js.map +1 -1
- package/dist-client/modeller/figure-section-editor.d.ts +2 -2
- package/dist-client/modeller/figure-section-editor.js +12 -33
- package/dist-client/modeller/figure-section-editor.js.map +1 -1
- package/dist-client/modeller/figure-section-panel.js +6 -7
- package/dist-client/modeller/figure-section-panel.js.map +1 -1
- package/dist-client/modeller/figure-slot-layout-panel.d.ts +36 -0
- package/dist-client/modeller/figure-slot-layout-panel.js +198 -0
- package/dist-client/modeller/figure-slot-layout-panel.js.map +1 -0
- package/dist-client/modeller/figure-verdict.d.ts +1 -1
- package/dist-client/modeller/figure-verdict.js +19 -37
- package/dist-client/modeller/figure-verdict.js.map +1 -1
- package/dist-client/modeller/first-save.d.ts +10 -0
- package/dist-client/modeller/first-save.js +16 -0
- package/dist-client/modeller/first-save.js.map +1 -1
- package/dist-client/modeller/links.d.ts +26 -1
- package/dist-client/modeller/links.js +43 -0
- package/dist-client/modeller/links.js.map +1 -1
- package/dist-client/modeller/material-edit.d.ts +11 -0
- package/dist-client/modeller/material-edit.js +28 -0
- package/dist-client/modeller/material-edit.js.map +1 -0
- package/dist-client/modeller/maturity.d.ts +15 -0
- package/dist-client/modeller/maturity.js +23 -0
- package/dist-client/modeller/maturity.js.map +1 -1
- package/dist-client/modeller/mesh-import.d.ts +47 -0
- package/dist-client/modeller/mesh-import.js +47 -0
- package/dist-client/modeller/mesh-import.js.map +1 -0
- package/dist-client/modeller/modeller-route.d.ts +5 -0
- package/dist-client/modeller/modeller-route.js +9 -0
- package/dist-client/modeller/modeller-route.js.map +1 -1
- package/dist-client/modeller/mounting-part.d.ts +14 -0
- package/dist-client/modeller/mounting-part.js +10 -0
- package/dist-client/modeller/mounting-part.js.map +1 -0
- package/dist-client/modeller/newest-answer.d.ts +12 -0
- package/dist-client/modeller/newest-answer.js +21 -0
- package/dist-client/modeller/newest-answer.js.map +1 -0
- package/dist-client/modeller/preview-model.d.ts +2 -0
- package/dist-client/modeller/preview-model.js +4 -0
- package/dist-client/modeller/preview-model.js.map +1 -1
- package/dist-client/modeller/refusals.d.ts +26 -5
- package/dist-client/modeller/refusals.js +184 -86
- package/dist-client/modeller/refusals.js.map +1 -1
- package/dist-client/modeller/section-controls.d.ts +17 -1
- package/dist-client/modeller/section-controls.js +18 -0
- package/dist-client/modeller/section-controls.js.map +1 -1
- package/dist-client/modeller/section-drawing.d.ts +24 -1
- package/dist-client/modeller/section-drawing.js +42 -0
- package/dist-client/modeller/section-drawing.js.map +1 -1
- package/dist-client/modeller/slot-layout-host.d.ts +81 -0
- package/dist-client/modeller/slot-layout-host.js +105 -0
- package/dist-client/modeller/slot-layout-host.js.map +1 -0
- package/dist-client/pages/figure-modeller-page.js +5 -4
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/viewparts/detail-route.d.ts +5 -0
- package/dist-client/viewparts/detail-route.js +8 -0
- package/dist-client/viewparts/detail-route.js.map +1 -0
- package/dist-client/viewparts/figure-detail-dock.d.ts +0 -1
- package/dist-client/viewparts/figure-detail-dock.js +1 -3
- package/dist-client/viewparts/figure-detail-dock.js.map +1 -1
- package/package.json +9 -8
- package/test/align.test.ts +0 -9
- package/test/browser/assistant-stand-in.js +32 -0
- package/test/browser/build.mjs +6 -1
- package/test/browser/editing-page.js +224 -0
- package/test/browser/editing.check.mjs +550 -0
- package/test/browser/links-page.js +113 -0
- package/test/browser/links.check.mjs +568 -0
- package/test/browser/modeller.check.mjs +21 -0
- package/test/browser/parts-page.js +120 -0
- package/test/browser/parts.check.mjs +328 -0
- package/test/browser/places-mesh-page.js +36 -0
- package/test/browser/places-mesh.check.mjs +239 -0
- package/test/browser/product-page.check.mjs +682 -0
- package/test/browser/product-page.js +276 -0
- package/test/browser/screen-page.js +112 -0
- package/test/browser/screen-product-page.js +62 -0
- package/test/browser/screen.check.mjs +1003 -0
- package/test/browser/section-page.js +256 -0
- package/test/browser/section.check.mjs +719 -0
- package/test/browser/serve.mjs +20 -12
- package/test/carrier-declaration.test.ts +69 -0
- package/test/cavity-drawing.test.ts +2 -18
- package/test/clip-ai-proposal.test.ts +1 -5
- package/test/clip-consent.test.ts +0 -5
- package/test/clips.test.ts +0 -7
- package/test/command-preview.test.ts +0 -68
- package/test/controls.test.ts +0 -9
- package/test/detail-dock.test.ts +6 -25
- package/test/face-mapping.test.ts +1 -20
- package/test/fasten-drawing.test.ts +0 -23
- package/test/fields.test.ts +6 -33
- package/test/figure-assistant-surface.test.ts +32 -38
- package/test/first-minute.test.ts +3 -55
- package/test/first-save-suggest.test.ts +11 -8
- package/test/first-save.test.ts +4 -26
- package/test/gizmo.test.ts +0 -25
- package/test/joint-fan.test.ts +0 -10
- package/test/keys.test.ts +0 -7
- package/test/layout.test.ts +4 -73
- package/test/links.test.ts +48 -67
- package/test/live.test.ts +0 -34
- package/test/material.test.ts +17 -26
- package/test/maturity.test.ts +13 -16
- package/test/mesh-import.test.ts +76 -0
- package/test/modeller-route.test.ts +3 -19
- package/test/package-download.test.ts +6 -7
- package/test/parts-wiring.test.ts +9 -51
- package/test/preview.test.ts +5 -46
- package/test/properties.test.ts +11 -26
- package/test/proposal-record.test.ts +2 -19
- package/test/recipes.test.ts +0 -15
- package/test/refusals.test.ts +257 -151
- package/test/release-entry.test.ts +1 -9
- package/test/release-panel.test.ts +1 -22
- package/test/resize-with-placement.test.ts +0 -32
- package/test/revert-choice.test.ts +1 -22
- package/test/revert-keeping.test.ts +1 -12
- package/test/save-landed.test.ts +16 -14
- package/test/scene-canvas.test.ts +2 -13
- package/test/section-editor.test.ts +30 -83
- package/test/section-panel.test.ts +22 -17
- package/test/slot-layout-host.test.ts +117 -0
- package/test/theme.test.ts +5 -9
- package/test/verdict.test.ts +44 -41
- package/test/view.test.ts +5 -12
- package/translations/en.json +61 -1
- package/translations/es.json +61 -1
- package/translations/ja.json +61 -1
- package/translations/ko.json +61 -1
- package/translations/ms.json +61 -1
- package/translations/zh.json +61 -1
- package/test/thumbnail.test.ts +0 -29
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import '@material/web/icon/icon.js'
|
|
2
2
|
|
|
3
3
|
import { css, html, LitElement, nothing } from 'lit'
|
|
4
|
+
import '@operato/input/ox-input-select-buttons.js'
|
|
5
|
+
import './figure-slot-layout-panel.js'
|
|
4
6
|
import { customElement, property, state } from 'lit/decorators.js'
|
|
5
7
|
|
|
6
8
|
import { i18next, localize } from '@operato/i18n'
|
|
@@ -9,14 +11,12 @@ import type { FigureAsset, FigureAuthoringAction, FigureDimensionSource, FigureF
|
|
|
9
11
|
|
|
10
12
|
import {
|
|
11
13
|
acceptsOf,
|
|
12
|
-
alignAxesOf,
|
|
13
|
-
mountingPlaneOf,
|
|
14
14
|
alignOf,
|
|
15
15
|
AXES,
|
|
16
16
|
canFasten,
|
|
17
17
|
facesOf,
|
|
18
18
|
canFollow,
|
|
19
|
-
|
|
19
|
+
followCommandOf,
|
|
20
20
|
followableAxes,
|
|
21
21
|
positionIsShare,
|
|
22
22
|
choiceOf,
|
|
@@ -46,6 +46,8 @@ import {
|
|
|
46
46
|
positionsWorkedOut,
|
|
47
47
|
fieldWordsOf,
|
|
48
48
|
measureNow,
|
|
49
|
+
ownFacesOf,
|
|
50
|
+
alignRowsOf,
|
|
49
51
|
type Alignment,
|
|
50
52
|
type Axis,
|
|
51
53
|
type Follow,
|
|
@@ -55,7 +57,6 @@ import { SidePanelStyles } from './side-panel-styles.js'
|
|
|
55
57
|
import './figure-fasten-drawing.js'
|
|
56
58
|
import type { FastenChange } from './figure-fasten-drawing.js'
|
|
57
59
|
import { faceViewOf, modeOf, myFaceFor, turnsInPlace, type FastenMode } from './fasten-geometry.js'
|
|
58
|
-
import { boxOf } from './align.js'
|
|
59
60
|
import type { FigureRevision } from './revision.js'
|
|
60
61
|
import { HelpToggles } from './help.js'
|
|
61
62
|
import { jointAxisWord } from './joint-axis.js'
|
|
@@ -121,8 +122,6 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
121
122
|
/* Size behaviour is a choice about dimensions only. It never implies a placement or a fastening. */
|
|
122
123
|
details[resize-choice] { margin: 12px 0; padding: 0; border: 0; }
|
|
123
124
|
details[resize-choice] > div { display: grid; gap: 8px; padding: 8px 0; }
|
|
124
|
-
div[resize-held] { display: grid; gap: 8px; margin: 12px 0; padding: 10px 12px; border-radius: 6px; background: var(--md-sys-color-surface-container, #8881); }
|
|
125
|
-
div[resize-held] p { margin: 0; display: flex; align-items: center; gap: 6px; }
|
|
126
125
|
p[fasten-mode] { margin: 0; font-weight: 600; }
|
|
127
126
|
section[fastening][spotlit] { outline: 2px solid var(--md-sys-color-primary, #477b9e); outline-offset: 4px; border-radius: 6px; transition: outline-color 0.3s; }
|
|
128
127
|
details { margin-top: 8px; }
|
|
@@ -189,6 +188,8 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
189
188
|
@state() private fasten = freshFasten()
|
|
190
189
|
@state() private motion: MotionForm = freshMotion()
|
|
191
190
|
@state() private place = freshPlace()
|
|
191
|
+
/** How places are made here: one at a time, or by the figure's rule. Unset: by the rule when the figure has one. */
|
|
192
|
+
@state() private placesBy?: 'one' | 'rule'
|
|
192
193
|
@state() private row = freshRow()
|
|
193
194
|
/** The motion form is open over the part's own motion, to change it where it is. */
|
|
194
195
|
@state() private editingMotion = false
|
|
@@ -240,9 +241,28 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
240
241
|
if (this.section === 'motion')
|
|
241
242
|
return html`<section><h3>${i18next.t('figure.label.motion')}${this.help.button('figure.text.motion-explained')}</h3>${this.help.hint('figure.text.motion-explained')}
|
|
242
243
|
${this.renderMotion(asset)}${this.renderShown(asset)}</section>`
|
|
243
|
-
if (this.section === 'places')
|
|
244
|
+
if (this.section === 'places') {
|
|
245
|
+
/*
|
|
246
|
+
One idea, places, two ways to make them (ADR-0118): one at a time on this part, or many by a rule -- the figure's
|
|
247
|
+
slot layout. One section with a choice, not two screens for the same thing (chief architect, 2026-10-11).
|
|
248
|
+
*/
|
|
249
|
+
const byRule = (this.placesBy ?? (asset.document.capabilities.slotLayout ? 'rule' : 'one')) === 'rule'
|
|
244
250
|
return html`<section><h3>${i18next.t('figure.label.places-on-part', { part: this.partId })}${this.help.button('figure.text.place-explained')}</h3>${this.help.hint('figure.text.place-explained')}
|
|
245
|
-
|
|
251
|
+
<ox-input-select-buttons
|
|
252
|
+
places-by
|
|
253
|
+
.value=${byRule ? 'rule' : 'one'}
|
|
254
|
+
.options=${[
|
|
255
|
+
{ value: 'one', display: i18next.t('figure.label.places-one') },
|
|
256
|
+
{ value: 'rule', display: i18next.t('figure.label.places-by-rule') }
|
|
257
|
+
]}
|
|
258
|
+
@change=${(e: CustomEvent) => (this.placesBy = e.detail === 'rule' ? 'rule' : 'one')}
|
|
259
|
+
></ox-input-select-buttons>
|
|
260
|
+
${byRule
|
|
261
|
+
? html`<figure-slot-layout-panel .asset=${asset} .partId=${this.partId} ?locked=${this.locked}></figure-slot-layout-panel>`
|
|
262
|
+
: placeFacesOf(asset, this.partId).length
|
|
263
|
+
? this.renderPlaces(asset)
|
|
264
|
+
: html`<p hint>${i18next.t('figure.text.places-need-a-box')}</p>`}</section>`
|
|
265
|
+
}
|
|
246
266
|
const shown = html`
|
|
247
267
|
<section>
|
|
248
268
|
<h3>${i18next.t('figure.label.resize-intent')}</h3>
|
|
@@ -350,7 +370,7 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
350
370
|
${following
|
|
351
371
|
? html`<button link resize-unfollow=${axis} ?disabled=${this.locked}
|
|
352
372
|
@click=${() => {
|
|
353
|
-
this.follow(
|
|
373
|
+
this.follow(axis, 'own', now)
|
|
354
374
|
}}>${i18next.t('figure.button.resize-unfollow')}</button>`
|
|
355
375
|
: nothing}
|
|
356
376
|
</div>`
|
|
@@ -367,7 +387,7 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
367
387
|
const name = round ? i18next.t(axis === 'x' ? 'figure.label.field-radius' : 'figure.label.field-length') : axis.toUpperCase()
|
|
368
388
|
return html`<div row>
|
|
369
389
|
<b>${name}</b>
|
|
370
|
-
<select ?disabled=${this.locked} @change=${(e: Event) => this.follow(
|
|
390
|
+
<select ?disabled=${this.locked} @change=${(e: Event) => this.follow(axis, (e.target as HTMLSelectElement).value, now)}>
|
|
371
391
|
<option value="own" ?selected=${value === 'own'}>${i18next.t('figure.label.follows-nothing')}</option>
|
|
372
392
|
${AXES.filter(one => hasInstanceSize(asset, one)).map(
|
|
373
393
|
one => html`<option value=${`share:${one}`} ?selected=${value === `share:${one}`}>
|
|
@@ -413,7 +433,10 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
413
433
|
private async showWhere() {
|
|
414
434
|
this.working = 'fasten'
|
|
415
435
|
await this.updateComplete
|
|
416
|
-
|
|
436
|
+
/* The drawing draws itself after this panel does: its "where" is there only once it has. */
|
|
437
|
+
const drawing = this.renderRoot.querySelector<HTMLElement & { updateComplete?: Promise<unknown> }>('figure-fasten-drawing')
|
|
438
|
+
await drawing?.updateComplete
|
|
439
|
+
const where = drawing?.shadowRoot?.querySelector<HTMLElement>('div[where]')
|
|
417
440
|
if (!where) return
|
|
418
441
|
where.scrollIntoView({ block: 'nearest', behavior: 'smooth' })
|
|
419
442
|
where.focus({ preventScroll: true })
|
|
@@ -452,22 +475,6 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
452
475
|
return where
|
|
453
476
|
}
|
|
454
477
|
|
|
455
|
-
private renderHeld(asset: FigureAsset) {
|
|
456
|
-
const joint = (asset.joints ?? []).find(j => j.body1.part === this.partId)
|
|
457
|
-
return html`<div resize-held>
|
|
458
|
-
<p><md-icon>link</md-icon> ${this.heldWords(asset)}</p>
|
|
459
|
-
<button act open-fastening @click=${() => this.openFastening(asset)}>
|
|
460
|
-
<md-icon>link</md-icon>${i18next.t('figure.button.open-fastening')}
|
|
461
|
-
</button>
|
|
462
|
-
${joint
|
|
463
|
-
? html`<button act detach-held ?disabled=${this.locked} @click=${() => this.command({ kind: 'detach', part: this.partId })}>
|
|
464
|
-
<md-icon>link_off</md-icon>${i18next.t('figure.button.detach-held')}
|
|
465
|
-
</button>
|
|
466
|
-
${this.renderDetachWords()}`
|
|
467
|
-
: nothing}
|
|
468
|
-
</div>`
|
|
469
|
-
}
|
|
470
|
-
|
|
471
478
|
/** The same source, the same share, a new difference: a leaf the opening less its gaps (link-dimension's `plus`). */
|
|
472
479
|
private followPlus(axis: Axis, now: Follow, plus: number) {
|
|
473
480
|
if (now.of !== 'instance' && now.of !== 'part') return
|
|
@@ -477,27 +484,9 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
477
484
|
this.command({ kind: 'link-dimension', part: this.partId, axis, source, ...(times !== 1 ? { times } : {}), ...(plus ? { plus } : {}), replace: true })
|
|
478
485
|
}
|
|
479
486
|
|
|
480
|
-
private follow(
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
/* The part's own extent along the axis now, as drawn: what a dimension is fixed at when it stops following. */
|
|
484
|
-
const current = dimensionNow(this.revision!, this.partId, axis) ?? boxOf(this.revision!, this.partId)?.dims[AXES.indexOf(axis)] ?? 100
|
|
485
|
-
if (choice === 'own') {
|
|
486
|
-
if (!following) return
|
|
487
|
-
this.command({ kind: 'unlink-dimension', part: this.partId, axis, mm: Math.max(1, Math.round(current)) })
|
|
488
|
-
return
|
|
489
|
-
}
|
|
490
|
-
const [of, a, b] = choice.split(':')
|
|
491
|
-
if (of === 'share') {
|
|
492
|
-
/* A proportional rule: this measure is the share of the instance size it is now, so it grows with the figure. */
|
|
493
|
-
const size = asset.designInputs[`size.${a}`]
|
|
494
|
-
if (!size) return
|
|
495
|
-
const times = Math.round((current / size) * 1e6) / 1e6
|
|
496
|
-
this.command({ kind: 'link-dimension', part: this.partId, axis, source: { of: 'instance', axis: a as Axis }, times, replace: following })
|
|
497
|
-
return
|
|
498
|
-
}
|
|
499
|
-
const source: FigureDimensionSource = of === 'instance' ? { of: 'instance', axis: a as Axis } : { of: 'part', part: a!, axis: b as Axis }
|
|
500
|
-
this.command({ kind: 'link-dimension', part: this.partId, axis, source, replace: following })
|
|
487
|
+
private follow(axis: Axis, choice: string, now: Follow) {
|
|
488
|
+
const action = followCommandOf(this.revision!, this.partId, axis, choice, now)
|
|
489
|
+
if (action) this.command(action)
|
|
501
490
|
}
|
|
502
491
|
|
|
503
492
|
/**
|
|
@@ -549,15 +538,9 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
549
538
|
const targets = asset.appearance.map(look => look.target).filter(one => one !== this.partId && canFasten(asset, one))
|
|
550
539
|
/* Shown, not dropped, and not choosable, with the reason (ADR-0097 §5.5): fastening it would close a loop. */
|
|
551
540
|
const loops = (one: string) => hangsFrom(asset, one, this.partId)
|
|
552
|
-
/* The faces are the ones each shape declares: a part is fastened by one of its outer faces, to any face of the target. */
|
|
553
|
-
/* On the mounting plane only the faces it takes: a bottom or a top on the floor, a back on a wall. */
|
|
554
|
-
const plane = mountingPlaneOf(asset)
|
|
555
541
|
const planeWord = i18next.t('figure.label.resize-place-' + (asset.placement ?? 'floor'))
|
|
556
|
-
/*
|
|
557
|
-
const mineFaces
|
|
558
|
-
...(facesOf(asset, this.partId).filter(face => FACES.includes(face as FigureFace) && (!f.toPlane || plane.faces.includes(face as FigureFace))) as FigureFace[]),
|
|
559
|
-
...(f.toPlane ? [] : (['centre'] as const))
|
|
560
|
-
]
|
|
542
|
+
/* The faces are the ones each shape declares: a part is fastened by one of its outer faces, to any face of the target. */
|
|
543
|
+
const mineFaces = ownFacesOf(asset, this.partId, f.toPlane)
|
|
561
544
|
const theirFaces = f.target ? facesOf(asset, f.target) : FACES
|
|
562
545
|
const targetFace = theirFaces.includes(f.targetFace) ? f.targetFace : theirFaces[0]!
|
|
563
546
|
const aim = f.toPlane ? ('plane' as const) : { part: f.target, face: targetFace }
|
|
@@ -592,9 +575,7 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
592
575
|
]
|
|
593
576
|
/* Across the face, a row only where the drawing cannot set it: no drawing of the face, or a share or a field to follow. */
|
|
594
577
|
const view = f.toPlane || f.target ? faceViewOf(this.revision!, this.partId, aim) : undefined
|
|
595
|
-
const alignRows =
|
|
596
|
-
({ axis }) => !view || !view.axes.includes(axis) || !!f.alignInput?.[axis] || hasInstanceSize(asset, axis)
|
|
597
|
-
)
|
|
578
|
+
const alignRows = f.toPlane || f.target ? alignRowsOf(asset, this.partId, aim, view, !!f.twist, f.alignInput) : []
|
|
598
579
|
/* A fastening the part has is changed as soon as something is chosen, as the drawing does; a new one waits for 붙이기. */
|
|
599
580
|
const settle = () => this.onFastenRelease(!!held)
|
|
600
581
|
/*
|
|
@@ -791,7 +772,12 @@ export class FigureLinks extends localize(i18next)(LitElement) {
|
|
|
791
772
|
: undefined
|
|
792
773
|
this.rowCandidate = action
|
|
793
774
|
const set = (next: typeof r) => ((this.row = next), (this.working = 'row'))
|
|
794
|
-
|
|
775
|
+
/*
|
|
776
|
+
Whether the row is being worked follows the fold's own toggle. A press on the heading focuses it before the click;
|
|
777
|
+
set on focus, the fold opened there and the click then folded it again (2026-10-11: it took two clicks to open).
|
|
778
|
+
*/
|
|
779
|
+
const toggled = (e: Event) => (this.working = (e.target as HTMLDetailsElement).open ? 'row' : 'fasten')
|
|
780
|
+
return html`<section row><details row-fold ?open=${this.working === 'row'} @toggle=${toggled}>
|
|
795
781
|
${head}
|
|
796
782
|
<div form>
|
|
797
783
|
<span>${i18next.t('figure.label.repeat-axis')}</span>
|
|
@@ -7,6 +7,7 @@ import { activePalette } from '@hatiolab/things-scene'
|
|
|
7
7
|
import type { FigureAssetGeometry, FigureMaterial, FigurePartEdit } from '@hatiolab/figure-model'
|
|
8
8
|
import { SidePanelStyles } from './side-panel-styles.js'
|
|
9
9
|
import { isRoundBody } from './starting-shape.js'
|
|
10
|
+
import { glowOf, materialEditOf, offeredPaletteOf } from './material-edit.js'
|
|
10
11
|
|
|
11
12
|
/** The purpose each palette role is for, by its token. */
|
|
12
13
|
const PURPOSE: Record<string, string> = {
|
|
@@ -284,28 +285,17 @@ export class FigureMaterialPanel extends localize(i18next)(LitElement) {
|
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
private offered(): Record<string, string> {
|
|
287
|
-
return
|
|
288
|
+
return offeredPaletteOf(activePalette() as Record<string, string>, this.palette)
|
|
288
289
|
}
|
|
289
290
|
|
|
290
|
-
/** Intensity 0 is no glow at all; a new glow takes the part's own colour until another is picked. */
|
|
291
291
|
private glow(intensity: number, live = false) {
|
|
292
|
-
const material = this.body!.material
|
|
293
292
|
this.glowing = live ? intensity : undefined
|
|
294
|
-
this.put(
|
|
293
|
+
this.put(glowOf(this.body!.material, intensity), live)
|
|
295
294
|
}
|
|
296
295
|
|
|
297
296
|
/** A material change: recorded, or -- while a slider is dragged -- only drawn (`part-live`). */
|
|
298
297
|
private put(patch: Partial<FigureMaterial>, live = false) {
|
|
299
|
-
const
|
|
300
|
-
for (const key of Object.keys(material) as (keyof FigureMaterial)[]) if (material[key] === undefined) delete material[key]
|
|
301
|
-
/* The colours this material names that the asset does not carry yet. */
|
|
302
|
-
const offered = this.offered()
|
|
303
|
-
const palette = Object.fromEntries(
|
|
304
|
-
[material.token, material.emissive?.token]
|
|
305
|
-
.filter((token): token is string => !!token && this.palette[token] === undefined && offered[token] !== undefined)
|
|
306
|
-
.map(token => [token, offered[token]!])
|
|
307
|
-
)
|
|
308
|
-
const edit: FigurePartEdit = { kind: 'set-material', target: this.body!.id, material, ...(Object.keys(palette).length ? { palette } : {}) }
|
|
298
|
+
const edit = materialEditOf(this.body!.id, this.body!.material, patch, this.palette, this.offered())
|
|
309
299
|
if (live) this.dispatchEvent(new CustomEvent('part-live', { detail: { edit }, bubbles: true, composed: true }))
|
|
310
300
|
else this.edit(edit)
|
|
311
301
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit'
|
|
2
|
+
import { customElement, property } from 'lit/decorators.js'
|
|
3
|
+
import { i18next } from '@operato/i18n'
|
|
4
|
+
import { closePopup } from '@operato/popup'
|
|
5
|
+
import '@operato/popup/ox-popup-actions.js'
|
|
6
|
+
import { CommonPopupStyles } from '@operato/styles'
|
|
7
|
+
import '@material/web/button/text-button.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* What of an uploaded GLB becomes a part (ADR-0129): the whole file, or one node of it -- a crane's hoist, its trolley.
|
|
11
|
+
* Closes with the choice, or with nothing; then nothing is added.
|
|
12
|
+
*/
|
|
13
|
+
@customElement('figure-mesh-choice')
|
|
14
|
+
export class FigureMeshChoice extends LitElement {
|
|
15
|
+
static styles = [
|
|
16
|
+
CommonPopupStyles,
|
|
17
|
+
css`
|
|
18
|
+
:host {
|
|
19
|
+
overflow-y: auto;
|
|
20
|
+
}
|
|
21
|
+
ul {
|
|
22
|
+
list-style: none;
|
|
23
|
+
margin: 0;
|
|
24
|
+
padding: 0;
|
|
25
|
+
}
|
|
26
|
+
button {
|
|
27
|
+
display: flex;
|
|
28
|
+
justify-content: space-between;
|
|
29
|
+
gap: 12px;
|
|
30
|
+
width: 100%;
|
|
31
|
+
padding: 8px 12px;
|
|
32
|
+
border: 0;
|
|
33
|
+
background: none;
|
|
34
|
+
color: inherit;
|
|
35
|
+
font: inherit;
|
|
36
|
+
text-align: start;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
}
|
|
39
|
+
button:hover,
|
|
40
|
+
button:focus-visible {
|
|
41
|
+
background: var(--md-sys-color-surface-container-high);
|
|
42
|
+
}
|
|
43
|
+
span[count] {
|
|
44
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
45
|
+
}
|
|
46
|
+
`
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
@property({ attribute: false }) choices: Array<{ node?: number; name: string; triangles: number }> = []
|
|
50
|
+
|
|
51
|
+
render() {
|
|
52
|
+
return html`
|
|
53
|
+
<ul>
|
|
54
|
+
${this.choices.map(
|
|
55
|
+
c => html`<li>
|
|
56
|
+
<button @click=${() => closePopup(this, { node: c.node })}>
|
|
57
|
+
<span>${c.node === undefined ? i18next.t('figure.text.mesh-whole-file') : c.name}</span>
|
|
58
|
+
<span count>${i18next.t('figure.text.mesh-triangles', { n: c.triangles.toLocaleString() })}</span>
|
|
59
|
+
</button>
|
|
60
|
+
</li>`
|
|
61
|
+
)}
|
|
62
|
+
</ul>
|
|
63
|
+
<ox-popup-actions>
|
|
64
|
+
<md-text-button slot="cancel" @click=${() => closePopup(this)}>${i18next.t('figure.button.cancel')}</md-text-button>
|
|
65
|
+
</ox-popup-actions>
|
|
66
|
+
`
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -56,7 +56,8 @@ const KIND_ICON: Record<string, string> = {
|
|
|
56
56
|
starting sizes, not kinds of their own. A rounded box and a box open at the top are not here: they are a box with its
|
|
57
57
|
corners rounded or an inside made, from the part's own 단면 section (§3.3), so the palette does not offer a second way.
|
|
58
58
|
*/
|
|
59
|
-
|
|
59
|
+
/* A mesh part is made from a file (`import-mesh`), not from a starting size. */
|
|
60
|
+
export type FigurePaletteKind = Exclude<FigurePrimitiveCreation['kind'], 'mesh'> | 'plate' | 'wall'
|
|
60
61
|
const RECIPES: Array<{ kind: FigurePaletteKind; icon: string; label: string }> = [
|
|
61
62
|
{ kind: 'box', icon: 'deployed_code', label: 'figure.label.primitive-cube' },
|
|
62
63
|
{ kind: 'cylinder', icon: 'database', label: 'figure.label.primitive-cylinder' },
|
|
@@ -438,6 +439,27 @@ export class FigureParts extends localize(i18next)(LitElement) {
|
|
|
438
439
|
</button>
|
|
439
440
|
`
|
|
440
441
|
)}
|
|
442
|
+
<button
|
|
443
|
+
shape
|
|
444
|
+
?disabled=${this.locked || full}
|
|
445
|
+
title=${full ? i18next.t('figure.text.part-limit-reached') : i18next.t('figure.text.import-mesh-hint')}
|
|
446
|
+
@click=${() => this.renderRoot.querySelector<HTMLInputElement>('input[mesh-file]')?.click()}
|
|
447
|
+
>
|
|
448
|
+
<md-icon>view_in_ar</md-icon>
|
|
449
|
+
<span>${i18next.t('figure.button.import-mesh')}</span>
|
|
450
|
+
</button>
|
|
451
|
+
<input
|
|
452
|
+
mesh-file
|
|
453
|
+
type="file"
|
|
454
|
+
accept=".glb,model/gltf-binary"
|
|
455
|
+
hidden
|
|
456
|
+
@change=${(e: Event) => {
|
|
457
|
+
const input = e.target as HTMLInputElement
|
|
458
|
+
const file = input.files?.[0]
|
|
459
|
+
input.value = ''
|
|
460
|
+
if (file) this.ask('import-mesh', { file })
|
|
461
|
+
}}
|
|
462
|
+
/>
|
|
441
463
|
</div>
|
|
442
464
|
${full ? html`<p full>${i18next.t('figure.text.part-limit-reached-short', { level: b!.level, limit: b!.limit })}</p>` : nothing}
|
|
443
465
|
</section>
|
|
@@ -9,7 +9,7 @@ import { create, registerFigure, unregisterFigure } from '@hatiolab/things-scene
|
|
|
9
9
|
import { DEFAULT_VIEW, previewBoardDepth, sceneLook, WORKBENCH, type ViewSettings } from './figure-view.js'
|
|
10
10
|
import { dialShape } from './angle-dial.js'
|
|
11
11
|
import { derivedOf, type FigureRevision } from './revision.js'
|
|
12
|
-
import { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, setupPartOf } from './preview-model.js'
|
|
12
|
+
import { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances, previewTypeOf, setupPartOf } from './preview-model.js'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* The figure placed on a board.
|
|
@@ -376,7 +376,7 @@ export class FigurePreview extends localize(i18next)(LitElement) {
|
|
|
376
376
|
}
|
|
377
377
|
try {
|
|
378
378
|
/* Registered as a board registers it, under a type of this preview's own: a real type's drawings stay as they are. */
|
|
379
|
-
const type =
|
|
379
|
+
const type = previewTypeOf(this.context)
|
|
380
380
|
const size = this.authoredSize()
|
|
381
381
|
/*
|
|
382
382
|
The figure changed under a scene that is standing: the same type is registered again and the content made again
|
|
@@ -8,8 +8,10 @@ import { i18next, localize } from '@operato/i18n'
|
|
|
8
8
|
import { FIGURE_FACES } from '@hatiolab/figure-model'
|
|
9
9
|
import type { FigureAuthoringAction, FigureFace } from '@hatiolab/figure-model'
|
|
10
10
|
|
|
11
|
+
import './figure-carrier.js'
|
|
11
12
|
import { occupancyFromParts } from './occupancy.js'
|
|
12
13
|
import { tagsOf, figureFigureFacts, type Placement, type FigureFigureFacts } from './figure-facts.js'
|
|
14
|
+
import { offeredMountingOf } from './mounting-part.js'
|
|
13
15
|
import type { FigureRevision } from './revision.js'
|
|
14
16
|
import { SidePanelStyles } from './side-panel-styles.js'
|
|
15
17
|
import { ScrollbarStyles } from '@operato/styles'
|
|
@@ -24,7 +26,7 @@ const FACES = Object.keys(FIGURE_FACES) as FigureFace[]
|
|
|
24
26
|
* catalogue files it.
|
|
25
27
|
*
|
|
26
28
|
* Two kinds of change leave this tab, and it does neither itself:
|
|
27
|
-
* - `command` carries an authoring command (rename, declare the room, the mounting face). The page runs it
|
|
29
|
+
* - `command` carries an authoring command (rename, declare the room, the mounting face, the carrier). The page runs it
|
|
28
30
|
* through the session, so it is one step of the same undo history as every other edit, and is saved with it.
|
|
29
31
|
* - `catalog` carries a change to the row (category, tags). Those are not in the asset; the page writes them
|
|
30
32
|
* to the saved row at once, so they need a saved figure.
|
|
@@ -123,6 +125,7 @@ export class FigureProperties extends localize(i18next)(LitElement) {
|
|
|
123
125
|
<h3>${i18next.t('figure.label.group-standing')}</h3>
|
|
124
126
|
${this.renderSpace(facts)} ${this.renderMounting(facts)}
|
|
125
127
|
</section>
|
|
128
|
+
<figure-carrier .carrier=${this.revision?.asset.carrier}></figure-carrier>
|
|
126
129
|
<section>
|
|
127
130
|
<h3>${i18next.t('figure.label.group-catalog')}</h3>
|
|
128
131
|
${this.renderCatalog()}
|
|
@@ -195,8 +198,7 @@ export class FigureProperties extends localize(i18next)(LitElement) {
|
|
|
195
198
|
private renderMounting(facts: FigureFigureFacts) {
|
|
196
199
|
const mounting = facts.mounting
|
|
197
200
|
/* Chosen, or what is set, or where the figure meets its plane -- not the first part listed (walk 1). */
|
|
198
|
-
const part =
|
|
199
|
-
const face: FigureFace = this.pickedFace ?? mounting?.face ?? facts.suggested?.face ?? 'bottom'
|
|
201
|
+
const { part, face } = offeredMountingOf(facts, { part: this.pickedPart, face: this.pickedFace })
|
|
200
202
|
return html`
|
|
201
203
|
<div field>
|
|
202
204
|
<label>${i18next.t('figure.label.mounting-face')}</label>
|
|
@@ -6,8 +6,8 @@ import { i18next, localize } from '@operato/i18n'
|
|
|
6
6
|
import { figureCavitySectionOf, figureSectionSidesOf } from '@hatiolab/figure-model'
|
|
7
7
|
import type { FigureAuthoringAction, FigureEvaluatedShape, FigureFaceMappingNeed, FigureSection, FigureSectionMeasure, FigureSectionPoint } from '@hatiolab/figure-model'
|
|
8
8
|
import { SidePanelStyles } from './side-panel-styles.js'
|
|
9
|
-
import { apexOver, drawnOf, insertAfter, markedBy, movePoint, pointLabel, removePoint, sectionOf, workingIssuesOf, workingOf, workingShapeOf, type SectionWorking } from './section-drawing.js'
|
|
10
|
-
import { cavityDepthOf, cavityRequestOf, cavityResultOf,
|
|
9
|
+
import { apexOver, cavityKeyOf, drawnOf, insertAfter, markedBy, measureNoteOf, movePoint, pointLabel, removePoint, sectionActionOf, sectionOf, workingIssuesOf, workingOf, workingShapeOf, type SectionWorking } from './section-drawing.js'
|
|
10
|
+
import { cavityDepthOf, cavityRequestOf, cavityResultOf, hollowableOf, sameOf, sidesOf } from './section-controls.js'
|
|
11
11
|
import './figure-cavity-drawing.js'
|
|
12
12
|
import './figure-face-mapping.js'
|
|
13
13
|
import { openPopup } from '@operato/layout'
|
|
@@ -19,10 +19,6 @@ type Axis = 'x' | 'y' | 'z'
|
|
|
19
19
|
/** How the section is laid on the screen, by the axis it is pushed along: across to the right, then down. */
|
|
20
20
|
const SCREEN: Record<Axis, [Axis, Axis]> = { y: ['x', 'z'], x: ['z', 'y'], z: ['x', 'y'] }
|
|
21
21
|
|
|
22
|
-
/** The inside said so two can be compared: its walls by side, its ends. */
|
|
23
|
-
const cavityKey = (w: SectionWorking) =>
|
|
24
|
-
w.cavity ? [Object.entries(w.cavity.wall).sort(), w.cavity.start, w.cavity.end] : null
|
|
25
|
-
|
|
26
22
|
/** The editor's width and room for the solid beside it: below this the two take turns. */
|
|
27
23
|
export const NARROW_BELOW = 900
|
|
28
24
|
|
|
@@ -310,7 +306,7 @@ export class FigureSectionEditor extends localize(i18next)(LitElement) {
|
|
|
310
306
|
}
|
|
311
307
|
/** A pushed rectangle or ellipse can have an inside; a drawn section's is a later stage. */
|
|
312
308
|
private get hollowable() {
|
|
313
|
-
return !!this.body
|
|
309
|
+
return !!this.body && hollowableOf(this.body.shape)
|
|
314
310
|
}
|
|
315
311
|
|
|
316
312
|
connectedCallback() {
|
|
@@ -398,20 +394,7 @@ export class FigureSectionEditor extends localize(i18next)(LitElement) {
|
|
|
398
394
|
|
|
399
395
|
/** One command for everything changed since the editor opened; a measure not changed is not sent (it may follow another). */
|
|
400
396
|
private action(w: SectionWorking): FigureAuthoringAction {
|
|
401
|
-
|
|
402
|
-
const changed = (a: number | undefined, b: number | undefined) => Math.abs((a ?? 0) - (b ?? 0)) > 1e-9
|
|
403
|
-
const across = Object.fromEntries(Object.entries(w.across).filter(([k, v]) => changed(v, o.across[k as Axis])))
|
|
404
|
-
const apexMoved = !w.apex !== !o.apex || (!!w.apex && Object.entries(w.apex).some(([k, v]) => changed(v, o.apex![k as Axis])))
|
|
405
|
-
const cavityMoved = JSON.stringify(cavityKey(w)) !== JSON.stringify(cavityKey(o))
|
|
406
|
-
return {
|
|
407
|
-
kind: 'set-section',
|
|
408
|
-
part: this.body!.id,
|
|
409
|
-
...(this.kind === 'polygon' ? { points: w.points.map(p => ({ ...p })) } : {}),
|
|
410
|
-
...(Object.keys(across).length ? { across } : {}),
|
|
411
|
-
...(changed(w.length, o.length) ? { length: w.length } : {}),
|
|
412
|
-
...(apexMoved ? { apex: w.apex ? { ...w.apex } : null } : {}),
|
|
413
|
-
...(cavityMoved ? { cavity: w.cavity ? { wall: { ...w.cavity.wall }, start: w.cavity.start, end: w.cavity.end } : null } : {})
|
|
414
|
-
}
|
|
397
|
+
return sectionActionOf(this.body!.id, this.kind, this.opened, w)
|
|
415
398
|
}
|
|
416
399
|
|
|
417
400
|
private removePoint(id: string) {
|
|
@@ -447,7 +430,7 @@ export class FigureSectionEditor extends localize(i18next)(LitElement) {
|
|
|
447
430
|
Object.entries(w.across).sort(),
|
|
448
431
|
w.length,
|
|
449
432
|
w.apex ? Object.entries(w.apex).sort() : null,
|
|
450
|
-
|
|
433
|
+
cavityKeyOf(w)
|
|
451
434
|
])
|
|
452
435
|
return !!this.opened && key(this.working) !== key(this.opened)
|
|
453
436
|
}
|
|
@@ -507,13 +490,10 @@ export class FigureSectionEditor extends localize(i18next)(LitElement) {
|
|
|
507
490
|
return { h: Math.round(p.x), v: Math.round(p.y) }
|
|
508
491
|
}
|
|
509
492
|
|
|
510
|
-
/**
|
|
511
|
-
private
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
return i18next.t('figure.text.section-editor-keeps', {
|
|
515
|
-
face: i18next.t('figure.label.face-' + END_FACE(this.axis, this.kept === 1 ? 'start' : 'end'))
|
|
516
|
-
})
|
|
493
|
+
/** What a measure's field says under it: following another value, or for the length the face it keeps. */
|
|
494
|
+
private noteWords(axis: Axis, kept?: -1 | 0 | 1): string {
|
|
495
|
+
const note = measureNoteOf(this.measures?.measures[axis], kept, axis)
|
|
496
|
+
return note ? i18next.t(note.key, note.face ? { face: i18next.t('figure.label.face-' + note.face) } : {}) : ''
|
|
517
497
|
}
|
|
518
498
|
|
|
519
499
|
render() {
|
|
@@ -816,27 +796,26 @@ export class FigureSectionEditor extends localize(i18next)(LitElement) {
|
|
|
816
796
|
const [h, v] = SCREEN[this.axis]
|
|
817
797
|
const marked = markedBy(issues)
|
|
818
798
|
const m = this.measures?.measures
|
|
819
|
-
const linked = i18next.t('figure.text.section-editor-linked')
|
|
820
799
|
return html`<div sizes>
|
|
821
800
|
${this.field(
|
|
822
801
|
i18next.t(this.working.apex ? 'figure.label.section-editor-height' : 'figure.label.section-editor-length'),
|
|
823
802
|
this.working.length,
|
|
824
803
|
value => this.change({ ...this.working, length: value }),
|
|
825
804
|
m?.[this.axis] !== 'editable',
|
|
826
|
-
|
|
805
|
+
this.noteWords(this.axis, this.kept)
|
|
827
806
|
)}
|
|
828
807
|
${this.kind === 'polygon' ? this.apexFields() : nothing}
|
|
829
808
|
${this.kind === 'polygon'
|
|
830
809
|
? nothing
|
|
831
810
|
: this.measures?.round
|
|
832
|
-
? this.field(i18next.t('figure.label.section-editor-diameter'), this.working.across[h] ?? 0, value => this.change(this.across(h, value)), m?.[h] !== 'editable',
|
|
811
|
+
? this.field(i18next.t('figure.label.section-editor-diameter'), this.working.across[h] ?? 0, value => this.change(this.across(h, value)), m?.[h] !== 'editable', this.noteWords(h))
|
|
833
812
|
: ([h, v] as const).map(axis =>
|
|
834
813
|
this.field(
|
|
835
814
|
i18next.t('figure.label.section-editor-across', { axis: axis.toUpperCase() }),
|
|
836
815
|
this.working.across[axis] ?? 0,
|
|
837
816
|
value => this.change(this.across(axis, value)),
|
|
838
817
|
m?.[axis] !== 'editable',
|
|
839
|
-
|
|
818
|
+
this.noteWords(axis)
|
|
840
819
|
)
|
|
841
820
|
)}
|
|
842
821
|
</div>
|
|
@@ -6,10 +6,10 @@ import { live } from 'lit/directives/live.js'
|
|
|
6
6
|
import { i18next, localize } from '@operato/i18n'
|
|
7
7
|
import type { FigureAuthoringAction, FigureEvaluatedShape, FigureFaceMappingNeed } from '@hatiolab/figure-model'
|
|
8
8
|
import { SidePanelStyles } from './side-panel-styles.js'
|
|
9
|
-
import { drawnOf } from './section-drawing.js'
|
|
9
|
+
import { drawnOf, sectionPointsWordsOf } from './section-drawing.js'
|
|
10
10
|
import './figure-cavity-drawing.js'
|
|
11
11
|
import './figure-face-mapping.js'
|
|
12
|
-
import { cavityDepthOf, cavityRequestOf, cavityResultOf, cornerLimitOf, cornerRefusalOf, mm, sameOf } from './section-controls.js'
|
|
12
|
+
import { cavityDepthOf, cavityRequestOf, cavityResultOf, cornerActionOf, cornerLimitOf, cornerRefusalOf, hollowableOf, mm, sameOf } from './section-controls.js'
|
|
13
13
|
|
|
14
14
|
type Body = { id: string; shape: FigureEvaluatedShape }
|
|
15
15
|
|
|
@@ -86,6 +86,7 @@ export class FigureSectionPanel extends localize(i18next)(LitElement) {
|
|
|
86
86
|
const e = body?.shape ? drawnOf(body.shape) : undefined
|
|
87
87
|
if (!body || !e) return nothing
|
|
88
88
|
const kind = e.section.kind
|
|
89
|
+
const points = sectionPointsWordsOf(e)
|
|
89
90
|
return html`<section>
|
|
90
91
|
<h3>${i18next.t('figure.label.group-section')}</h3>
|
|
91
92
|
${body.shape.dimensions.jamb === undefined
|
|
@@ -93,13 +94,11 @@ export class FigureSectionPanel extends localize(i18next)(LitElement) {
|
|
|
93
94
|
<button act ?disabled=${this.locked} @click=${() => this.dispatchEvent(new CustomEvent('section-editor-open', { bubbles: true, composed: true }))}>
|
|
94
95
|
<md-icon>edit</md-icon>${i18next.t('figure.button.section-editor-open')}
|
|
95
96
|
</button>
|
|
96
|
-
${
|
|
97
|
-
? html`<small>${i18next.t(e.apex ? 'figure.text.section-points-apex' : 'figure.text.section-points', { points: String(e.section.points.length) })}</small>`
|
|
98
|
-
: nothing}
|
|
97
|
+
${points ? html`<small>${i18next.t(points.key, points.params)}</small>` : nothing}
|
|
99
98
|
</div>`
|
|
100
99
|
: nothing}
|
|
101
100
|
${kind === 'rectangle' ? this.renderCorner(body) : nothing}
|
|
102
|
-
${/* No inside for a drawn section: the control is not there, rather than a line saying so. */
|
|
101
|
+
${/* No inside for a drawn section: the control is not there, rather than a line saying so. */ hollowableOf(body.shape) ? this.renderCavity(body) : nothing}
|
|
103
102
|
${this.faceMapping ? html`<figure-face-mapping .need=${this.faceMapping}></figure-face-mapping>` : nothing}
|
|
104
103
|
</section>`
|
|
105
104
|
}
|
|
@@ -120,7 +119,7 @@ export class FigureSectionPanel extends localize(i18next)(LitElement) {
|
|
|
120
119
|
const r = cornerRefusalOf(body.shape, value)
|
|
121
120
|
return r ? i18next.t('figure.text.section-corner-over', { max: String(mm(r.max)) }) : ''
|
|
122
121
|
},
|
|
123
|
-
action: value => (
|
|
122
|
+
action: value => cornerActionOf(body.id, value),
|
|
124
123
|
placeholder: now === undefined ? i18next.t('figure.text.section-corner-differs') : ''
|
|
125
124
|
})}
|
|
126
125
|
${this.refusals.corner ? nothing : max !== undefined ? html`<small>${i18next.t('figure.text.section-at-most', { max: String(mm(max)) })}</small>` : nothing}
|