@things-factory/figure-ui 10.1.94 → 10.1.96
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 +30 -11
- package/client/modeller/figure-fasten-drawing.ts +0 -2
- package/client/modeller/figure-links.ts +22 -25
- package/client/modeller/figure-material-panel.ts +18 -11
- package/client/modeller/figure-properties.ts +2 -2
- package/client/modeller/figure-section-panel.ts +12 -11
- package/client/modeller/links.ts +49 -23
- package/client/modeller/side-panel-styles.ts +67 -32
- package/dist-client/modeller/editor.d.ts +7 -2
- package/dist-client/modeller/editor.js +31 -10
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-fasten-drawing.js +0 -2
- package/dist-client/modeller/figure-fasten-drawing.js.map +1 -1
- package/dist-client/modeller/figure-links.js +24 -25
- package/dist-client/modeller/figure-links.js.map +1 -1
- package/dist-client/modeller/figure-material-panel.js +18 -11
- package/dist-client/modeller/figure-material-panel.js.map +1 -1
- package/dist-client/modeller/figure-properties.js +2 -2
- package/dist-client/modeller/figure-properties.js.map +1 -1
- package/dist-client/modeller/figure-section-panel.d.ts +1 -0
- package/dist-client/modeller/figure-section-panel.js +12 -11
- package/dist-client/modeller/figure-section-panel.js.map +1 -1
- package/dist-client/modeller/links.d.ts +14 -4
- package/dist-client/modeller/links.js +51 -23
- package/dist-client/modeller/links.js.map +1 -1
- package/dist-client/modeller/side-panel-styles.d.ts +5 -1
- package/dist-client/modeller/side-panel-styles.js +67 -32
- package/dist-client/modeller/side-panel-styles.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/test/command-preview.test.ts +3 -3
- package/test/first-minute.test.ts +16 -7
- package/test/section-panel.test.ts +2 -2
- package/translations/en.json +18 -10
- package/translations/ja.json +18 -10
- package/translations/ko.json +18 -10
- package/translations/ms.json +18 -10
- package/translations/zh.json +18 -10
|
@@ -10,7 +10,11 @@ import { css } from 'lit'
|
|
|
10
10
|
*
|
|
11
11
|
* The vocabulary:
|
|
12
12
|
* section one titled group; the side is a stack of them
|
|
13
|
-
* h3 a group's title
|
|
13
|
+
* h3 a group's title
|
|
14
|
+
*
|
|
15
|
+
* Three sizes and two weights (user 2026-10-01, the panel checked for balance: eight sizes, and titles smaller than
|
|
16
|
+
* what they titled): a title at the body token and semibold, everything else at the small token, and a hint at the
|
|
17
|
+
* micro one. Sections are 16 px above their title and 12 px below their last line.
|
|
14
18
|
* div[inline-field] a short label and its one field on one line
|
|
15
19
|
* div[field] a label above a full-width field
|
|
16
20
|
* div[form] short labels beside their fields, the fields starting on one line
|
|
@@ -25,14 +29,14 @@ import { css } from 'lit'
|
|
|
25
29
|
export const SidePanelStyles = css`
|
|
26
30
|
:host {
|
|
27
31
|
--figure-control-height: 30px;
|
|
28
|
-
font-size:
|
|
32
|
+
font-size: var(--ui-t-small, 11.5px);
|
|
29
33
|
color: var(--md-sys-color-on-surface);
|
|
30
34
|
}
|
|
31
35
|
/* The side is a stack of sections, each a titled group. */
|
|
32
36
|
section {
|
|
33
37
|
display: grid;
|
|
34
|
-
gap:
|
|
35
|
-
padding:
|
|
38
|
+
gap: 8px;
|
|
39
|
+
padding: 16px 12px 12px;
|
|
36
40
|
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
37
41
|
}
|
|
38
42
|
section > h3 {
|
|
@@ -48,18 +52,21 @@ export const SidePanelStyles = css`
|
|
|
48
52
|
align-items: center;
|
|
49
53
|
gap: 4px;
|
|
50
54
|
margin: 14px 0 6px;
|
|
51
|
-
font-size: var(--ui-t-
|
|
52
|
-
font-weight:
|
|
53
|
-
|
|
54
|
-
text-transform: uppercase;
|
|
55
|
-
color: var(--md-sys-color-on-surface-variant);
|
|
55
|
+
font-size: var(--ui-t-body, 13px);
|
|
56
|
+
font-weight: 600;
|
|
57
|
+
color: var(--md-sys-color-on-surface);
|
|
56
58
|
}
|
|
57
59
|
h3:first-child {
|
|
58
60
|
margin-top: 0;
|
|
59
61
|
}
|
|
62
|
+
/* Two weights only: a bold word in a line is the title's weight, not the browser's heavier one. */
|
|
63
|
+
b,
|
|
64
|
+
strong {
|
|
65
|
+
font-weight: 600;
|
|
66
|
+
}
|
|
60
67
|
label,
|
|
61
68
|
div[form] > span {
|
|
62
|
-
font-size:
|
|
69
|
+
font-size: inherit;
|
|
63
70
|
color: var(--md-sys-color-on-surface-variant);
|
|
64
71
|
}
|
|
65
72
|
div[field] {
|
|
@@ -90,7 +97,7 @@ export const SidePanelStyles = css`
|
|
|
90
97
|
}
|
|
91
98
|
/* A value read, not edited, beside its label: the field's size and colour, not the page's. */
|
|
92
99
|
div[inline-field] > span[value] {
|
|
93
|
-
font-size:
|
|
100
|
+
font-size: inherit;
|
|
94
101
|
color: var(--md-sys-color-on-surface);
|
|
95
102
|
}
|
|
96
103
|
label span[unit] {
|
|
@@ -108,10 +115,9 @@ export const SidePanelStyles = css`
|
|
|
108
115
|
gap: 4px;
|
|
109
116
|
}
|
|
110
117
|
div[tools] button {
|
|
111
|
-
height:
|
|
118
|
+
height: var(--figure-control-height);
|
|
112
119
|
padding: 0 10px;
|
|
113
120
|
font: inherit;
|
|
114
|
-
font-size: 0.78rem;
|
|
115
121
|
color: var(--md-sys-color-on-surface);
|
|
116
122
|
background: var(--md-sys-color-surface-container-lowest);
|
|
117
123
|
border: 1px solid var(--md-sys-color-outline-variant);
|
|
@@ -146,16 +152,45 @@ export const SidePanelStyles = css`
|
|
|
146
152
|
box-sizing: border-box;
|
|
147
153
|
padding: 4px 7px;
|
|
148
154
|
font: inherit;
|
|
149
|
-
font-size: 0.76rem;
|
|
150
155
|
color: var(--md-sys-color-on-surface);
|
|
151
156
|
background-color: var(--md-sys-color-surface-container-lowest);
|
|
152
157
|
border: 1px solid var(--md-sys-color-outline-variant);
|
|
153
158
|
border-radius: 6px;
|
|
154
159
|
}
|
|
160
|
+
/* The browser's own box and dot, in the panel's colour. */
|
|
155
161
|
input[type='checkbox'],
|
|
156
162
|
input[type='radio'] {
|
|
157
163
|
width: auto;
|
|
158
164
|
min-height: 0;
|
|
165
|
+
margin: 0;
|
|
166
|
+
accent-color: var(--md-sys-color-primary);
|
|
167
|
+
}
|
|
168
|
+
/* A fold: the panel's chevron, not the browser's triangle. */
|
|
169
|
+
summary {
|
|
170
|
+
display: flex;
|
|
171
|
+
align-items: center;
|
|
172
|
+
gap: 6px;
|
|
173
|
+
padding: 6px 0;
|
|
174
|
+
list-style: none;
|
|
175
|
+
cursor: pointer;
|
|
176
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
177
|
+
}
|
|
178
|
+
summary::-webkit-details-marker {
|
|
179
|
+
display: none;
|
|
180
|
+
}
|
|
181
|
+
summary::before {
|
|
182
|
+
content: '';
|
|
183
|
+
flex: none;
|
|
184
|
+
width: 6px;
|
|
185
|
+
height: 6px;
|
|
186
|
+
margin: 0 2px;
|
|
187
|
+
border-right: 1.5px solid currentColor;
|
|
188
|
+
border-bottom: 1.5px solid currentColor;
|
|
189
|
+
transform: rotate(-45deg);
|
|
190
|
+
transition: transform 0.15s;
|
|
191
|
+
}
|
|
192
|
+
details[open] > summary::before {
|
|
193
|
+
transform: rotate(45deg);
|
|
159
194
|
}
|
|
160
195
|
/* A thin track and a small thumb: a slider is a line to drag along, not a block (user, 2026-09-24). */
|
|
161
196
|
input[type='range'] {
|
|
@@ -239,7 +274,7 @@ export const SidePanelStyles = css`
|
|
|
239
274
|
}
|
|
240
275
|
div[cell] span[axis] {
|
|
241
276
|
flex: none;
|
|
242
|
-
font-size:
|
|
277
|
+
font-size: var(--ui-t-micro, 11px);
|
|
243
278
|
letter-spacing: 0.02em;
|
|
244
279
|
color: var(--md-sys-color-on-surface-variant);
|
|
245
280
|
}
|
|
@@ -272,7 +307,6 @@ export const SidePanelStyles = css`
|
|
|
272
307
|
display: flex;
|
|
273
308
|
align-items: center;
|
|
274
309
|
gap: 6px;
|
|
275
|
-
font-size: 0.76rem;
|
|
276
310
|
color: var(--md-sys-color-on-surface);
|
|
277
311
|
}
|
|
278
312
|
/* The 「?」 beside a title: no border, it says there is more rather than asking to be pressed. */
|
|
@@ -299,41 +333,42 @@ export const SidePanelStyles = css`
|
|
|
299
333
|
small {
|
|
300
334
|
display: block;
|
|
301
335
|
margin: 2px 0 4px;
|
|
302
|
-
font-size:
|
|
336
|
+
font-size: var(--ui-t-micro, 11px);
|
|
303
337
|
line-height: 1.5;
|
|
304
338
|
color: var(--md-sys-color-on-surface-variant);
|
|
305
339
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
340
|
+
/*
|
|
341
|
+
* An action under its fields: as wide as its words, not a band across the panel -- four full-width grey bars read as
|
|
342
|
+
* four walls and none of them as the one to press (user 2026-10-01). Every one carries its icon.
|
|
343
|
+
*/
|
|
310
344
|
button[act] {
|
|
311
345
|
display: inline-flex;
|
|
312
346
|
align-items: center;
|
|
313
347
|
justify-content: center;
|
|
314
|
-
|
|
315
|
-
|
|
348
|
+
justify-self: start;
|
|
349
|
+
gap: 6px;
|
|
350
|
+
width: auto;
|
|
351
|
+
max-width: 100%;
|
|
316
352
|
margin-top: 4px;
|
|
317
|
-
padding:
|
|
318
|
-
min-height:
|
|
353
|
+
padding: 0 12px;
|
|
354
|
+
min-height: var(--figure-control-height);
|
|
319
355
|
font: inherit;
|
|
320
|
-
|
|
321
|
-
color: var(--md-sys-color-
|
|
322
|
-
background-color: var(--md-sys-color-surface-container);
|
|
356
|
+
color: var(--md-sys-color-primary);
|
|
357
|
+
background-color: var(--md-sys-color-surface-container-lowest);
|
|
323
358
|
border: 1px solid var(--md-sys-color-outline-variant);
|
|
324
359
|
border-radius: 6px;
|
|
325
360
|
cursor: pointer;
|
|
326
361
|
}
|
|
327
362
|
button[act]:hover:not(:disabled) {
|
|
328
|
-
background-color: var(--md-sys-color-
|
|
363
|
+
background-color: var(--md-sys-color-primary-container);
|
|
329
364
|
}
|
|
330
365
|
button[act]:disabled {
|
|
331
366
|
opacity: 0.5;
|
|
332
367
|
cursor: default;
|
|
333
368
|
}
|
|
334
369
|
button[act] md-icon {
|
|
335
|
-
font-size:
|
|
336
|
-
width:
|
|
337
|
-
height:
|
|
370
|
+
font-size: 16px;
|
|
371
|
+
width: 16px;
|
|
372
|
+
height: 16px;
|
|
338
373
|
}
|
|
339
374
|
`
|
|
@@ -309,8 +309,7 @@ export declare class FigureEditor extends LitElement {
|
|
|
309
309
|
private pointBlockedOf;
|
|
310
310
|
private renderStart;
|
|
311
311
|
/**
|
|
312
|
-
* The part the panel speaks of, held at its top: its kind,
|
|
313
|
-
* B). No path above it (user 2026-10-01): the parts list and the canvas already show which part is chosen, and the
|
|
312
|
+
* The part the panel speaks of, held at its top: its kind, and what its size follows when it follows something. No path above it (user 2026-10-01): the parts list and the canvas already show which part is chosen, and the
|
|
314
313
|
* list's root is the way back to the model's settings.
|
|
315
314
|
*/
|
|
316
315
|
private renderSubject;
|
|
@@ -328,6 +327,12 @@ export declare class FigureEditor extends LitElement {
|
|
|
328
327
|
*/
|
|
329
328
|
private withSizeRow;
|
|
330
329
|
private renderVector;
|
|
330
|
+
/**
|
|
331
|
+
* Why a row's numbers cannot be typed, said once under the rows by what decides them (user 2026-10-01: "set by a
|
|
332
|
+
* rule" named nothing): a fastened part's place and turn are its fastening's; other numbers are worked out from
|
|
333
|
+
* something else. A size that follows something is said at the panel's head, so not again here.
|
|
334
|
+
*/
|
|
335
|
+
private renderHeldAxes;
|
|
331
336
|
/**
|
|
332
337
|
* A part's fields only the declared room still reads (ADR-0097, designer 2026-09-27): shown as what they are and not
|
|
333
338
|
* typed into -- they move nothing. Declaring the room again is offered; nothing is deleted or widened on the way.
|
|
@@ -386,13 +386,18 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
386
386
|
z-index: 1;
|
|
387
387
|
display: grid;
|
|
388
388
|
gap: 4px;
|
|
389
|
-
padding:
|
|
389
|
+
padding: 10px 12px;
|
|
390
390
|
background: var(--md-sys-color-surface-container-lowest);
|
|
391
391
|
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
392
392
|
}
|
|
393
|
+
/* The panel's three sizes (side-panel-styles): the part's kind at the title's, its two lines at the hint's. */
|
|
393
394
|
div[subject-summary] {
|
|
394
395
|
display: grid;
|
|
395
|
-
gap:
|
|
396
|
+
gap: 2px;
|
|
397
|
+
}
|
|
398
|
+
div[subject-summary] b {
|
|
399
|
+
font-size: var(--ui-t-body, 13px);
|
|
400
|
+
font-weight: 600;
|
|
396
401
|
}
|
|
397
402
|
div[subject-summary] b span {
|
|
398
403
|
font-weight: 400;
|
|
@@ -1651,14 +1656,13 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1651
1656
|
if (this.detachSaid?.key === key)
|
|
1652
1657
|
return this.detachSaid.words;
|
|
1653
1658
|
const say = (k, params) => i18next.t(k, params);
|
|
1654
|
-
const from = 'plane' in joint.body0 ? say('figure.label.mounting-plane') : partLabelOf(joint.body0.part);
|
|
1655
1659
|
let words;
|
|
1656
1660
|
try {
|
|
1657
1661
|
const seen = this.session.previewCommand({ kind: 'detach', part: this.selected });
|
|
1658
1662
|
const own = seen.moves.some(m => m.body === this.selected);
|
|
1659
1663
|
words = own
|
|
1660
1664
|
? [say('figure.text.detach-moves'), ...previewWordsOf({ context: seen.context, revision: seen.revision, key, moves: seen.moves }, this.selected, say)]
|
|
1661
|
-
:
|
|
1665
|
+
: /* It stays where it is: as expected, so nothing is said (user 2026-10-01). */ [];
|
|
1662
1666
|
const children = (asset.joints ?? []).filter(j => 'part' in j.body0 && j.body0.part === this.selected).map(j => partLabelOf(j.body1.part));
|
|
1663
1667
|
if (children.length)
|
|
1664
1668
|
words.push(say('figure.text.detach-keeps-children', { parts: children.join(', ') }));
|
|
@@ -1892,14 +1896,15 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1892
1896
|
</div>`;
|
|
1893
1897
|
}
|
|
1894
1898
|
/**
|
|
1895
|
-
* The part the panel speaks of, held at its top: its kind,
|
|
1896
|
-
* B). No path above it (user 2026-10-01): the parts list and the canvas already show which part is chosen, and the
|
|
1899
|
+
* The part the panel speaks of, held at its top: its kind, and what its size follows when it follows something. No path above it (user 2026-10-01): the parts list and the canvas already show which part is chosen, and the
|
|
1897
1900
|
* list's root is the way back to the model's settings.
|
|
1898
1901
|
*/
|
|
1899
1902
|
renderSubject() {
|
|
1900
1903
|
let summary;
|
|
1901
1904
|
try {
|
|
1902
|
-
summary = this.selected
|
|
1905
|
+
summary = this.selected
|
|
1906
|
+
? partSummaryOf(parseFigureAsset(this.session.source()), ownerOf(this.selected), (key, params) => i18next.t(key, params), i18next.language || 'ko')
|
|
1907
|
+
: undefined;
|
|
1903
1908
|
}
|
|
1904
1909
|
catch {
|
|
1905
1910
|
summary = undefined;
|
|
@@ -1909,7 +1914,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
1909
1914
|
return html `<div subject>
|
|
1910
1915
|
<div subject-summary>
|
|
1911
1916
|
${summary.kind ? html `<b>${summary.kind}</b>` : nothing}
|
|
1912
|
-
|
|
1917
|
+
${summary.size ? html `<small>${summary.size}</small>` : nothing}
|
|
1913
1918
|
</div>
|
|
1914
1919
|
</div>`;
|
|
1915
1920
|
}
|
|
@@ -2150,6 +2155,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
2150
2155
|
? html `<section>
|
|
2151
2156
|
<h3>${i18next.t(vectorTitle)}</h3>
|
|
2152
2157
|
${groups.vectors.map(g => this.renderVector(g, unavailable, held))}
|
|
2158
|
+
${this.renderHeldAxes(groups.vectors)}
|
|
2153
2159
|
${tools ?? nothing}
|
|
2154
2160
|
</section>`
|
|
2155
2161
|
: nothing}
|
|
@@ -2205,7 +2211,22 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
2205
2211
|
</div>`)}
|
|
2206
2212
|
</div>
|
|
2207
2213
|
</div>
|
|
2208
|
-
|
|
2214
|
+
`;
|
|
2215
|
+
}
|
|
2216
|
+
/**
|
|
2217
|
+
* Why a row's numbers cannot be typed, said once under the rows by what decides them (user 2026-10-01: "set by a
|
|
2218
|
+
* rule" named nothing): a fastened part's place and turn are its fastening's; other numbers are worked out from
|
|
2219
|
+
* something else. A size that follows something is said at the panel's head, so not again here.
|
|
2220
|
+
*/
|
|
2221
|
+
renderHeldAxes(vectors) {
|
|
2222
|
+
const fastened = !!this.asset()?.joints?.some(j => j.body1.part === ownerOf(this.selected));
|
|
2223
|
+
const held = vectors.filter(g => g.kind === 'vector' && g.name !== 'size' && ['x', 'y', 'z'].some(axis => !g.cells[axis]));
|
|
2224
|
+
if (!held.length)
|
|
2225
|
+
return nothing;
|
|
2226
|
+
const placed = held.every(g => g.name === 'position' || g.name === 'rotation');
|
|
2227
|
+
if (fastened && placed)
|
|
2228
|
+
return html `<p hint held-axes>${i18next.t('figure.text.held-by-fastening')}</p>`;
|
|
2229
|
+
return html `<p hint held-axes>${i18next.t('figure.text.held-worked-out', { group: held.map(g => i18next.t('figure.label.group-' + g.name)).join(' · ') })}</p>`;
|
|
2209
2230
|
}
|
|
2210
2231
|
/**
|
|
2211
2232
|
* A part's fields only the declared room still reads (ADR-0097, designer 2026-09-27): shown as what they are and not
|
|
@@ -2218,7 +2239,7 @@ let FigureEditor = class FigureEditor extends LitElement {
|
|
|
2218
2239
|
<div form>
|
|
2219
2240
|
${fields.map(f => html `<span>${fieldWordsOf(f.id, key => i18next.t(key))} (${f.unit})</span><input aria-label=${f.id} type="number" .value=${String(f.value)} disabled />`)}
|
|
2220
2241
|
</div>
|
|
2221
|
-
<button act room-refit ?disabled=${unavailable} @click=${() => this.askRoomRefit()}>${i18next.t('figure.button.room-refit')}</button>
|
|
2242
|
+
<button act room-refit ?disabled=${unavailable} @click=${() => this.askRoomRefit()}><md-icon>fit_screen</md-icon>${i18next.t('figure.button.room-refit')}</button>
|
|
2222
2243
|
</section>`;
|
|
2223
2244
|
}
|
|
2224
2245
|
/** The room as declared and as proposed, and what lies outside each as measured, before the author confirms. */
|