@things-factory/figure-ui 10.1.6 → 10.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/graphql/index.ts +25 -0
- package/client/modeller/figure-animations.ts +769 -0
- package/client/modeller/figure-ask.ts +6 -1
- package/client/modeller/figure-history-panel.ts +52 -1
- package/client/modeller/figure-inspector.ts +520 -120
- package/client/modeller/figure-side.ts +65 -17
- package/client/modeller/proposal-session.ts +92 -0
- package/client/pages/figure-list-page.ts +56 -4
- package/client/pages/figure-modeller-page.ts +204 -179
- package/client/types.ts +39 -0
- package/client/viewparts/figure-thumb.ts +78 -0
- package/dist-client/graphql/index.d.ts +3 -1
- package/dist-client/graphql/index.js +22 -0
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-animations.d.ts +96 -0
- package/dist-client/modeller/figure-animations.js +708 -0
- package/dist-client/modeller/figure-animations.js.map +1 -0
- package/dist-client/modeller/figure-ask.d.ts +3 -0
- package/dist-client/modeller/figure-ask.js +9 -0
- package/dist-client/modeller/figure-ask.js.map +1 -1
- package/dist-client/modeller/figure-history-panel.d.ts +7 -0
- package/dist-client/modeller/figure-history-panel.js +50 -0
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-inspector.d.ts +17 -0
- package/dist-client/modeller/figure-inspector.js +506 -107
- package/dist-client/modeller/figure-inspector.js.map +1 -1
- package/dist-client/modeller/figure-side.d.ts +23 -0
- package/dist-client/modeller/figure-side.js +68 -16
- package/dist-client/modeller/figure-side.js.map +1 -1
- package/dist-client/modeller/proposal-session.d.ts +23 -0
- package/dist-client/modeller/proposal-session.js +65 -0
- package/dist-client/modeller/proposal-session.js.map +1 -0
- package/dist-client/pages/figure-list-page.d.ts +10 -0
- package/dist-client/pages/figure-list-page.js +55 -4
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +50 -28
- package/dist-client/pages/figure-modeller-page.js +197 -176
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +44 -0
- package/dist-client/types.js.map +1 -1
- package/dist-client/viewparts/figure-thumb.js +74 -0
- package/dist-client/viewparts/figure-thumb.js.map +1 -1
- package/package.json +4 -5
- package/test/ai-proposal-contract.test.ts +169 -0
- package/test/i18n-prefix-guard.test.ts +42 -0
- package/translations/en.json +82 -6
- package/translations/ja.json +34 -1
- package/translations/ko.json +92 -6
- package/translations/ms.json +34 -1
- package/translations/zh.json +34 -1
|
@@ -69,6 +69,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
69
69
|
display: flex;
|
|
70
70
|
flex-direction: column;
|
|
71
71
|
min-height: 0;
|
|
72
|
+
--figure-control-height: 30px;
|
|
72
73
|
overflow-y: auto;
|
|
73
74
|
background-color: var(--md-sys-color-surface-container-lowest);
|
|
74
75
|
color: var(--md-sys-color-on-surface);
|
|
@@ -93,9 +94,39 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
93
94
|
칸 높이는 건드리지 않는다(작게 만드는 것이 아니라 **빈 곳을 줄이는 것**이다).
|
|
94
95
|
*/
|
|
95
96
|
section {
|
|
96
|
-
padding:
|
|
97
|
+
padding: 8px 10px;
|
|
97
98
|
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
98
99
|
}
|
|
100
|
+
/* 입력 행은 촘촘하게, 서로 다른 편집 의도 사이에는 호흡을 둔다. */
|
|
101
|
+
section + section {
|
|
102
|
+
margin-top: 6px;
|
|
103
|
+
}
|
|
104
|
+
section[collapsible] {
|
|
105
|
+
padding: 0;
|
|
106
|
+
}
|
|
107
|
+
details[group] > summary {
|
|
108
|
+
display: flex;
|
|
109
|
+
align-items: center;
|
|
110
|
+
justify-content: space-between;
|
|
111
|
+
min-height: 34px;
|
|
112
|
+
padding: 0 10px;
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
list-style: none;
|
|
115
|
+
}
|
|
116
|
+
details[group] > summary::-webkit-details-marker {
|
|
117
|
+
display: none;
|
|
118
|
+
}
|
|
119
|
+
details[group] > summary h3 {
|
|
120
|
+
margin: 0;
|
|
121
|
+
}
|
|
122
|
+
details[group] > summary md-icon {
|
|
123
|
+
--md-icon-size: 16px;
|
|
124
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
125
|
+
}
|
|
126
|
+
details[group] > div[group-content] {
|
|
127
|
+
padding: 4px 10px 8px;
|
|
128
|
+
border-top: 1px solid var(--md-sys-color-outline-variant);
|
|
129
|
+
}
|
|
99
130
|
h3 {
|
|
100
131
|
display: flex;
|
|
101
132
|
align-items: center;
|
|
@@ -108,6 +139,42 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
108
139
|
color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));
|
|
109
140
|
}
|
|
110
141
|
|
|
142
|
+
/* 자산 메타데이터는 부품을 고친다는 현재 문맥에서는 요약으로만 둔다. */
|
|
143
|
+
details[asset] {
|
|
144
|
+
margin: -1px 0;
|
|
145
|
+
}
|
|
146
|
+
details[asset] summary {
|
|
147
|
+
display: flex;
|
|
148
|
+
align-items: center;
|
|
149
|
+
gap: 6px;
|
|
150
|
+
min-height: 26px;
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
list-style: none;
|
|
153
|
+
color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));
|
|
154
|
+
}
|
|
155
|
+
details[asset] summary::-webkit-details-marker {
|
|
156
|
+
display: none;
|
|
157
|
+
}
|
|
158
|
+
details[asset] summary::before {
|
|
159
|
+
content: '›';
|
|
160
|
+
font-size: 1.1rem;
|
|
161
|
+
line-height: 1;
|
|
162
|
+
transition: transform 120ms ease;
|
|
163
|
+
}
|
|
164
|
+
details[asset][open] summary::before {
|
|
165
|
+
transform: rotate(90deg);
|
|
166
|
+
}
|
|
167
|
+
details[asset] summary b {
|
|
168
|
+
font-size: var(--ui-t-micro, 11px);
|
|
169
|
+
letter-spacing: 0.04em;
|
|
170
|
+
}
|
|
171
|
+
details[asset] summary small {
|
|
172
|
+
overflow: hidden;
|
|
173
|
+
text-overflow: ellipsis;
|
|
174
|
+
white-space: nowrap;
|
|
175
|
+
font-size: 0.7rem;
|
|
176
|
+
}
|
|
177
|
+
|
|
111
178
|
label {
|
|
112
179
|
display: flex;
|
|
113
180
|
align-items: center;
|
|
@@ -153,6 +220,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
153
220
|
input,
|
|
154
221
|
select {
|
|
155
222
|
width: 100%;
|
|
223
|
+
min-height: var(--figure-control-height);
|
|
156
224
|
box-sizing: border-box;
|
|
157
225
|
padding: 4px 7px;
|
|
158
226
|
font: var(--input-field-font, inherit);
|
|
@@ -177,6 +245,9 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
177
245
|
*/
|
|
178
246
|
div[cell] {
|
|
179
247
|
display: flex;
|
|
248
|
+
box-sizing: border-box;
|
|
249
|
+
height: var(--figure-control-height);
|
|
250
|
+
min-height: var(--figure-control-height);
|
|
180
251
|
align-items: center;
|
|
181
252
|
gap: 2px;
|
|
182
253
|
padding-left: 6px;
|
|
@@ -197,6 +268,8 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
197
268
|
}
|
|
198
269
|
div[cell] input {
|
|
199
270
|
min-width: 0;
|
|
271
|
+
min-height: 0;
|
|
272
|
+
height: calc(var(--figure-control-height) - 2px);
|
|
200
273
|
padding: 4px 6px 4px 2px;
|
|
201
274
|
font-family: var(--mono-font, monospace);
|
|
202
275
|
text-align: right;
|
|
@@ -216,11 +289,163 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
216
289
|
}
|
|
217
290
|
div[field] + div[field],
|
|
218
291
|
div[triple] + div[field],
|
|
219
|
-
div[field] + div[triple]
|
|
292
|
+
div[field] + div[triple],
|
|
293
|
+
div[inline-field] + div[inline-field],
|
|
294
|
+
div[inline-field] + div[field],
|
|
295
|
+
div[field] + div[inline-field],
|
|
296
|
+
div[vector-row] + div[vector-row],
|
|
297
|
+
div[vector-row] + div[field],
|
|
298
|
+
div[field] + div[vector-row] {
|
|
220
299
|
/* 칸 사이 7 → 5. 칸 자체는 그대로 두고 사이만 좁힌다. */
|
|
221
300
|
margin-top: 5px;
|
|
222
301
|
}
|
|
223
302
|
|
|
303
|
+
/* 짧은 라벨과 단일 값은 같은 줄에서 읽는다. 복잡한 선택기에는 쓰지 않는다. */
|
|
304
|
+
div[inline-field] {
|
|
305
|
+
display: grid;
|
|
306
|
+
/* 같은 편집 밀도 안에서는 입력 시작선을 맞춘다. 라벨 길이가 칸 폭을 흔들면 안 된다. */
|
|
307
|
+
grid-template-columns: 64px minmax(0, 1fr);
|
|
308
|
+
align-items: center;
|
|
309
|
+
gap: 7px;
|
|
310
|
+
}
|
|
311
|
+
div[inline-field] > label {
|
|
312
|
+
margin: 0;
|
|
313
|
+
white-space: nowrap;
|
|
314
|
+
}
|
|
315
|
+
/* 한 줄 입력의 설명은 입력칸을 밀어내지 않고 바로 아래 전폭에서 읽는다. */
|
|
316
|
+
div[inline-field] > p[hint] {
|
|
317
|
+
grid-column: 1 / -1;
|
|
318
|
+
margin: -2px 0 1px;
|
|
319
|
+
}
|
|
320
|
+
div[inline-field] > code[fixed] {
|
|
321
|
+
min-width: 0;
|
|
322
|
+
overflow: hidden;
|
|
323
|
+
text-overflow: ellipsis;
|
|
324
|
+
white-space: nowrap;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/* X·Y·Z는 함께 비교하는 값이므로, 묶음 제목만 왼쪽에 두고 입력은 한 행에 둔다. */
|
|
328
|
+
div[vector-row] {
|
|
329
|
+
display: grid;
|
|
330
|
+
grid-template-columns: 64px minmax(0, 1fr);
|
|
331
|
+
align-items: center;
|
|
332
|
+
gap: 7px;
|
|
333
|
+
}
|
|
334
|
+
div[vector-row] > label {
|
|
335
|
+
margin: 0;
|
|
336
|
+
white-space: nowrap;
|
|
337
|
+
}
|
|
338
|
+
div[vector-row] > p[hint] {
|
|
339
|
+
grid-column: 1 / -1;
|
|
340
|
+
margin: -2px 0 1px;
|
|
341
|
+
}
|
|
342
|
+
div[sizing-choices] {
|
|
343
|
+
display: grid;
|
|
344
|
+
grid-template-columns: 1fr 1fr;
|
|
345
|
+
gap: 5px;
|
|
346
|
+
}
|
|
347
|
+
button[sizing-choice] {
|
|
348
|
+
display: flex;
|
|
349
|
+
flex-direction: column;
|
|
350
|
+
align-items: flex-start;
|
|
351
|
+
gap: 2px;
|
|
352
|
+
min-height: 52px;
|
|
353
|
+
padding: 7px 8px;
|
|
354
|
+
color: var(--md-sys-color-on-surface);
|
|
355
|
+
background: var(--md-sys-color-surface-container-lowest);
|
|
356
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
357
|
+
border-radius: 7px;
|
|
358
|
+
text-align: left;
|
|
359
|
+
cursor: pointer;
|
|
360
|
+
}
|
|
361
|
+
button[sizing-choice][on] {
|
|
362
|
+
color: var(--md-sys-color-on-primary-container);
|
|
363
|
+
background: var(--md-sys-color-primary-container);
|
|
364
|
+
border-color: transparent;
|
|
365
|
+
}
|
|
366
|
+
button[sizing-choice] b {
|
|
367
|
+
font-size: 0.74rem;
|
|
368
|
+
}
|
|
369
|
+
button[sizing-choice] small {
|
|
370
|
+
font-size: 0.64rem;
|
|
371
|
+
line-height: 1.35;
|
|
372
|
+
opacity: 0.8;
|
|
373
|
+
}
|
|
374
|
+
div[anchor-rows] {
|
|
375
|
+
display: grid;
|
|
376
|
+
gap: 6px;
|
|
377
|
+
}
|
|
378
|
+
div[anchor-row] {
|
|
379
|
+
display: grid;
|
|
380
|
+
grid-template-columns: 46px minmax(0, 1fr);
|
|
381
|
+
align-items: start;
|
|
382
|
+
gap: 8px;
|
|
383
|
+
padding: 6px 7px;
|
|
384
|
+
background: var(--md-sys-color-surface-container);
|
|
385
|
+
border-radius: 7px;
|
|
386
|
+
}
|
|
387
|
+
div[anchor-row] label {
|
|
388
|
+
margin: 0;
|
|
389
|
+
font-size: 0.7rem;
|
|
390
|
+
font-weight: 700;
|
|
391
|
+
}
|
|
392
|
+
div[anchor-options] {
|
|
393
|
+
display: grid;
|
|
394
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
395
|
+
gap: 4px;
|
|
396
|
+
}
|
|
397
|
+
button[anchor-choice] {
|
|
398
|
+
min-width: 0;
|
|
399
|
+
height: var(--figure-control-height);
|
|
400
|
+
box-sizing: border-box;
|
|
401
|
+
padding: 4px 3px;
|
|
402
|
+
overflow: hidden;
|
|
403
|
+
color: var(--md-sys-color-on-surface);
|
|
404
|
+
font: var(--label-font, inherit);
|
|
405
|
+
font-size: 0.63rem;
|
|
406
|
+
line-height: 1.2;
|
|
407
|
+
text-overflow: ellipsis;
|
|
408
|
+
white-space: nowrap;
|
|
409
|
+
background: var(--md-sys-color-surface-container-lowest);
|
|
410
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
411
|
+
border-radius: 5px;
|
|
412
|
+
cursor: pointer;
|
|
413
|
+
}
|
|
414
|
+
button[anchor-choice][on] {
|
|
415
|
+
color: var(--md-sys-color-on-primary-container);
|
|
416
|
+
background: var(--md-sys-color-primary-container);
|
|
417
|
+
border-color: transparent;
|
|
418
|
+
}
|
|
419
|
+
details[anchor-advanced] {
|
|
420
|
+
grid-column: 2;
|
|
421
|
+
margin-top: 4px;
|
|
422
|
+
}
|
|
423
|
+
details[anchor-advanced] summary {
|
|
424
|
+
cursor: pointer;
|
|
425
|
+
font-size: 0.62rem;
|
|
426
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
427
|
+
}
|
|
428
|
+
details[anchor-advanced] div[anchor-options] {
|
|
429
|
+
margin-top: 4px;
|
|
430
|
+
}
|
|
431
|
+
div[palette-meaning] {
|
|
432
|
+
display: grid;
|
|
433
|
+
gap: 2px;
|
|
434
|
+
margin-top: 7px;
|
|
435
|
+
padding: 6px 8px;
|
|
436
|
+
color: var(--md-sys-color-on-surface);
|
|
437
|
+
background: var(--md-sys-color-surface-container);
|
|
438
|
+
border-radius: 7px;
|
|
439
|
+
}
|
|
440
|
+
div[palette-meaning] b {
|
|
441
|
+
font-size: 0.72rem;
|
|
442
|
+
}
|
|
443
|
+
div[palette-meaning] small {
|
|
444
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
445
|
+
font-size: 0.64rem;
|
|
446
|
+
line-height: 1.35;
|
|
447
|
+
}
|
|
448
|
+
|
|
224
449
|
/*
|
|
225
450
|
정밀 제작 도구.
|
|
226
451
|
|
|
@@ -229,13 +454,17 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
229
454
|
늘 조금씩 어긋나고, 기준 치수를 바꾸면 전부 다시 해야 한다.
|
|
230
455
|
*/
|
|
231
456
|
div[tools] {
|
|
232
|
-
display:
|
|
233
|
-
|
|
234
|
-
gap:
|
|
235
|
-
margin-top: 6px;
|
|
457
|
+
display: grid;
|
|
458
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
459
|
+
gap: 4px;
|
|
236
460
|
}
|
|
237
461
|
div[tools] button {
|
|
238
|
-
|
|
462
|
+
display: grid;
|
|
463
|
+
place-items: center;
|
|
464
|
+
min-width: 0;
|
|
465
|
+
height: var(--figure-control-height);
|
|
466
|
+
box-sizing: border-box;
|
|
467
|
+
padding: 0;
|
|
239
468
|
font: var(--label-font, inherit);
|
|
240
469
|
font-size: 0.7rem;
|
|
241
470
|
color: var(--md-sys-color-on-surface);
|
|
@@ -244,6 +473,9 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
244
473
|
border-radius: 6px;
|
|
245
474
|
cursor: pointer;
|
|
246
475
|
}
|
|
476
|
+
div[tools] button md-icon {
|
|
477
|
+
font-size: 17px;
|
|
478
|
+
}
|
|
247
479
|
div[tools] button[disabled] {
|
|
248
480
|
opacity: 0.35;
|
|
249
481
|
cursor: default;
|
|
@@ -253,11 +485,6 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
253
485
|
background-color: var(--md-sys-color-primary-container);
|
|
254
486
|
border-color: transparent;
|
|
255
487
|
}
|
|
256
|
-
div[tools] span[sep] {
|
|
257
|
-
width: 1px;
|
|
258
|
-
margin: 2px 3px;
|
|
259
|
-
background-color: var(--md-sys-color-outline-variant);
|
|
260
|
-
}
|
|
261
488
|
|
|
262
489
|
/* 지금 어느 축이 늘어나나 — 크기를 고치면 바뀔 수 있어서 값으로 적는다 */
|
|
263
490
|
p[axis-now] {
|
|
@@ -316,7 +543,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
316
543
|
*/
|
|
317
544
|
div[segments] {
|
|
318
545
|
display: grid;
|
|
319
|
-
grid-template-columns: repeat(
|
|
546
|
+
grid-template-columns: repeat(4, 1fr);
|
|
320
547
|
gap: 4px;
|
|
321
548
|
}
|
|
322
549
|
/* 원 유지 · 끄기 — 고르는 값이라 분할 수와 같은 모양으로 둔다 */
|
|
@@ -374,8 +601,8 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
374
601
|
button[seg] svg {
|
|
375
602
|
display: block;
|
|
376
603
|
/* 작으면 12 와 16 이 둘 다 동그라미로 보인다 — 각이 읽힐 만큼은 키운다 */
|
|
377
|
-
width:
|
|
378
|
-
height:
|
|
604
|
+
width: 32px;
|
|
605
|
+
height: 32px;
|
|
379
606
|
}
|
|
380
607
|
button[seg] svg polygon {
|
|
381
608
|
fill: var(--md-sys-color-surface-container-highest);
|
|
@@ -398,7 +625,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
398
625
|
button[seg][on] small {
|
|
399
626
|
color: inherit;
|
|
400
627
|
}
|
|
401
|
-
/*
|
|
628
|
+
/* 성능·실루엣 검증을 마친 프리셋만 고른다. */
|
|
402
629
|
details[free] {
|
|
403
630
|
margin-top: 6px;
|
|
404
631
|
}
|
|
@@ -491,6 +718,26 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
491
718
|
label[toggle] input {
|
|
492
719
|
width: auto;
|
|
493
720
|
}
|
|
721
|
+
div[role-choices] {
|
|
722
|
+
display: grid;
|
|
723
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
724
|
+
gap: 4px;
|
|
725
|
+
}
|
|
726
|
+
div[role-choices] label[toggle] {
|
|
727
|
+
justify-content: center;
|
|
728
|
+
min-height: 30px;
|
|
729
|
+
margin: 0;
|
|
730
|
+
padding: 2px 4px;
|
|
731
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
732
|
+
border-radius: 6px;
|
|
733
|
+
font-size: 0.68rem;
|
|
734
|
+
text-align: center;
|
|
735
|
+
}
|
|
736
|
+
div[role-choices] label[toggle]:has(input:checked) {
|
|
737
|
+
color: var(--md-sys-color-on-primary-container);
|
|
738
|
+
background: var(--md-sys-color-primary-container);
|
|
739
|
+
border-color: transparent;
|
|
740
|
+
}
|
|
494
741
|
|
|
495
742
|
p[quiet] {
|
|
496
743
|
margin: 0;
|
|
@@ -531,7 +778,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
531
778
|
const part = this.chosen;
|
|
532
779
|
if (!part || !this.board) {
|
|
533
780
|
return html `
|
|
534
|
-
${this.renderFigure()}
|
|
781
|
+
${this.renderFigure(false)}
|
|
535
782
|
<section>
|
|
536
783
|
<h3>${i18next.t('figure.label.properties')}</h3>
|
|
537
784
|
<p quiet>${i18next.t('figure.text.select-a-part-to-edit')}</p>
|
|
@@ -548,7 +795,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
548
795
|
묶음은 아예 나오지 않는다 — 해 둬도 아무 일도 안 일어나는 칸을 보여 주지 않는다.
|
|
549
796
|
*/
|
|
550
797
|
return html `
|
|
551
|
-
${this.renderFigure()} ${this.renderIdentity(shown)} ${this.renderRole(shown)}
|
|
798
|
+
${this.renderFigure(true)} ${this.renderIdentity(shown)} ${this.renderRole(shown)}
|
|
552
799
|
${this.renderPlacement(shown)} ${this.renderShape(shown)} ${this.renderMaterial(shown)}
|
|
553
800
|
${this.renderBehaviour(shown)}
|
|
554
801
|
`;
|
|
@@ -572,16 +819,18 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
572
819
|
<section>
|
|
573
820
|
<h3>${i18next.t('figure.label.role-in-capability')}${this.help('figure.text.anchors-are-not-drawn')}</h3>
|
|
574
821
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
<
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
822
|
+
<div role-choices>
|
|
823
|
+
${PART_ROLES.map(one => html `
|
|
824
|
+
<label toggle>
|
|
825
|
+
<input
|
|
826
|
+
type="checkbox"
|
|
827
|
+
.checked=${live(roles.includes(one))}
|
|
828
|
+
@change=${(e) => this.toggleRole(one, e.target.checked)}
|
|
829
|
+
/>
|
|
830
|
+
${i18next.t('figure.label.role-' + one)}
|
|
831
|
+
</label>
|
|
832
|
+
`)}
|
|
833
|
+
</div>
|
|
585
834
|
${roles.length === 0 ? html `<p quiet>${i18next.t('figure.text.role-none-means-drawn')}</p>` : nothing}
|
|
586
835
|
|
|
587
836
|
${roles.includes('slot')
|
|
@@ -666,11 +915,9 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
666
915
|
*
|
|
667
916
|
* 저장되는 식별자다. 배치된 보드가 이 이름으로 타입을 찾으므로 만든 뒤에는 못 바꾼다.
|
|
668
917
|
*/
|
|
669
|
-
renderFigure() {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
<h3>${i18next.t('figure.label.figure')}</h3>
|
|
673
|
-
<div field>
|
|
918
|
+
renderFigure(compact = false) {
|
|
919
|
+
const fields = html `
|
|
920
|
+
<div inline-field>
|
|
674
921
|
<label>${i18next.t('figure.label.name')}</label>
|
|
675
922
|
<input
|
|
676
923
|
.value=${this.figureName}
|
|
@@ -682,31 +929,91 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
682
929
|
}))}
|
|
683
930
|
/>
|
|
684
931
|
</div>
|
|
685
|
-
<div field>
|
|
686
|
-
<label>${i18next.t('figure.label.figure-type')}${this.
|
|
932
|
+
<div inline-field>
|
|
933
|
+
<label>${i18next.t('figure.label.figure-type')}${this.helpButton('figure.text.type-name-cannot-change')}</label>
|
|
687
934
|
<code fixed title=${i18next.t('figure.text.type-name-cannot-change')}>
|
|
688
935
|
<md-icon>lock</md-icon>${this.board?.figureType ?? ''}
|
|
689
936
|
</code>
|
|
937
|
+
${this.helpHint('figure.text.type-name-cannot-change')}
|
|
690
938
|
</div>
|
|
691
939
|
<!--
|
|
692
940
|
왜 못 고치는지 **적어 둔다.** 전에는 tooltip 에만 있었고, 그러면 눌러 보고 안 되는 것을
|
|
693
941
|
알게 된다 — 화면이 안 되는 이유를 말하지 않으면 사용자가 자기 실수라고 생각한다.
|
|
694
942
|
-->
|
|
943
|
+
|
|
944
|
+
<div inline-field>
|
|
945
|
+
<label>${i18next.t('figure.label.category')}${this.helpButton('figure.text.category-explained')}</label>
|
|
946
|
+
<input
|
|
947
|
+
.value=${this.figureCategory ?? ''}
|
|
948
|
+
placeholder=${i18next.t('figure.text.category-placeholder')}
|
|
949
|
+
@change=${(e) => this.tellCatalog({ category: e.target.value.trim() || undefined })}
|
|
950
|
+
/>
|
|
951
|
+
${this.helpHint('figure.text.category-explained')}
|
|
952
|
+
</div>
|
|
953
|
+
|
|
954
|
+
<div inline-field>
|
|
955
|
+
<label>${i18next.t('figure.label.tags')}${this.helpButton('figure.text.tags-explained')}</label>
|
|
956
|
+
<input
|
|
957
|
+
.value=${(this.figureTags ?? []).join(', ')}
|
|
958
|
+
placeholder=${i18next.t('figure.text.tags-placeholder')}
|
|
959
|
+
@change=${(e) => this.tellTags(e.target.value)}
|
|
960
|
+
/>
|
|
961
|
+
${this.helpHint('figure.text.tags-explained')}
|
|
962
|
+
</div>
|
|
963
|
+
`;
|
|
964
|
+
if (compact) {
|
|
965
|
+
return html `
|
|
966
|
+
<section>
|
|
967
|
+
<details asset>
|
|
968
|
+
<summary>
|
|
969
|
+
<b>${i18next.t('figure.label.figure')}</b>
|
|
970
|
+
<small>${this.figureName || this.board?.figureType || ''}</small>
|
|
971
|
+
</summary>
|
|
972
|
+
${fields}
|
|
973
|
+
</details>
|
|
974
|
+
</section>
|
|
975
|
+
`;
|
|
976
|
+
}
|
|
977
|
+
return html `
|
|
978
|
+
<section>
|
|
979
|
+
<h3>${i18next.t('figure.label.figure')}</h3>
|
|
980
|
+
${fields}
|
|
695
981
|
</section>
|
|
696
982
|
`;
|
|
697
983
|
}
|
|
984
|
+
/**
|
|
985
|
+
* 태그 줄을 목록으로.
|
|
986
|
+
*
|
|
987
|
+
* 쉼표로 가르고 `#` 은 떼어 낸다 — 사람은 `#반출` 이라고 적고, 저장되는 것은 `반출` 이다.
|
|
988
|
+
* 겹친 것은 한 번만 남긴다. 빈 줄은 **빈 배열**이지 `undefined` 가 아니다 — 전부 지우려는
|
|
989
|
+
* 저작자가 지울 방법을 잃지 않게.
|
|
990
|
+
*/
|
|
991
|
+
tellTags(raw) {
|
|
992
|
+
const tags = [
|
|
993
|
+
...new Set(raw
|
|
994
|
+
.split(',')
|
|
995
|
+
.map(one => one.trim().replace(/^#+/, '').trim())
|
|
996
|
+
.filter(one => one.length > 0))
|
|
997
|
+
];
|
|
998
|
+
this.tellCatalog({ tags });
|
|
999
|
+
}
|
|
1000
|
+
/** 카탈로그 값이 바뀌었다고 알린다. 정본은 저작면이 든다 — 여기서는 알리기만 한다. */
|
|
1001
|
+
tellCatalog(patch) {
|
|
1002
|
+
this.dispatchEvent(new CustomEvent('figure-catalog', { detail: patch, bubbles: true, composed: true }));
|
|
1003
|
+
}
|
|
698
1004
|
renderIdentity(part) {
|
|
699
1005
|
return html `
|
|
700
1006
|
<section>
|
|
701
1007
|
<h3>${i18next.t('figure.label.part')}</h3>
|
|
702
|
-
<div field>
|
|
703
|
-
<label>${i18next.t('figure.label.name')}${this.
|
|
1008
|
+
<div inline-field>
|
|
1009
|
+
<label>${i18next.t('figure.label.name')}${this.helpButton('figure.text.renaming-a-part-breaks-bindings')}</label>
|
|
704
1010
|
<input
|
|
705
1011
|
.value=${part.name}
|
|
706
1012
|
@change=${(e) => this.put({ name: e.target.value })}
|
|
707
1013
|
/>
|
|
1014
|
+
${this.helpHint('figure.text.renaming-a-part-breaks-bindings')}
|
|
708
1015
|
</div>
|
|
709
|
-
<div field>
|
|
1016
|
+
<div inline-field>
|
|
710
1017
|
<label>${i18next.t('figure.label.shape')}</label>
|
|
711
1018
|
<select @change=${(e) => this.changePrimitive(e.target.value)}>
|
|
712
1019
|
${PRIMITIVE_KINDS.map(kind => html `
|
|
@@ -726,56 +1033,74 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
726
1033
|
<section>
|
|
727
1034
|
<h3>${i18next.t('figure.label.placement')}</h3>
|
|
728
1035
|
|
|
729
|
-
<
|
|
730
|
-
|
|
731
|
-
|
|
1036
|
+
<div vector-row>
|
|
1037
|
+
<label>${i18next.t('figure.label.part-position')}</label>
|
|
1038
|
+
<div triple>
|
|
1039
|
+
${['x', 'y', 'z'].map(axis => this.number(axis.toUpperCase(), t.position[axis], value => this.putTransform({ position: { ...t.position, [axis]: value } })))}
|
|
1040
|
+
</div>
|
|
732
1041
|
</div>
|
|
733
1042
|
|
|
734
|
-
<div
|
|
1043
|
+
<div vector-row>
|
|
735
1044
|
<label>${i18next.t('figure.label.part-size')}</label>
|
|
736
1045
|
<div triple>
|
|
737
1046
|
${['x', 'y', 'z'].map(axis => this.number(axis.toUpperCase(), t.size[axis], value => this.putTransform({ size: { ...t.size, [axis]: value } }), 1))}
|
|
738
1047
|
</div>
|
|
739
1048
|
</div>
|
|
740
1049
|
|
|
741
|
-
<div
|
|
742
|
-
<label>${i18next.t('figure.label.
|
|
1050
|
+
<div vector-row>
|
|
1051
|
+
<label>${i18next.t('figure.label.rotation-degrees')}</label>
|
|
1052
|
+
<div triple>
|
|
1053
|
+
${['x', 'y', 'z'].map(axis => this.number(axis.toUpperCase(), rotation[axis] ?? 0, value => this.putTransform({ rotation: { ...rotation, [axis]: value } })))}
|
|
1054
|
+
</div>
|
|
1055
|
+
</div>
|
|
1056
|
+
|
|
1057
|
+
<div vector-row>
|
|
1058
|
+
<label>${i18next.t('figure.label.tools')}${this.helpButton('figure.text.tools-explained')}</label>
|
|
743
1059
|
<div tools>
|
|
744
|
-
<button
|
|
745
|
-
|
|
1060
|
+
<button
|
|
1061
|
+
aria-label=${i18next.t('figure.button.center-x')}
|
|
1062
|
+
title=${i18next.t('figure.text.center-on-x')}
|
|
1063
|
+
@click=${() => this.ask('center', 'x')}
|
|
1064
|
+
>
|
|
1065
|
+
<md-icon>horizontal_distribute</md-icon>
|
|
746
1066
|
</button>
|
|
747
|
-
<button
|
|
748
|
-
|
|
1067
|
+
<button
|
|
1068
|
+
aria-label=${i18next.t('figure.button.center-z')}
|
|
1069
|
+
title=${i18next.t('figure.text.center-on-z')}
|
|
1070
|
+
@click=${() => this.ask('center', 'z')}
|
|
1071
|
+
>
|
|
1072
|
+
<md-icon>vertical_align_center</md-icon>
|
|
749
1073
|
</button>
|
|
750
|
-
<button
|
|
751
|
-
|
|
1074
|
+
<button
|
|
1075
|
+
aria-label=${i18next.t('figure.button.to-floor')}
|
|
1076
|
+
title=${i18next.t('figure.text.put-on-floor')}
|
|
1077
|
+
@click=${() => this.ask('floor', 'y')}
|
|
1078
|
+
>
|
|
1079
|
+
<md-icon>vertical_align_bottom</md-icon>
|
|
752
1080
|
</button>
|
|
753
|
-
<span sep></span>
|
|
754
1081
|
${['x', 'z'].map(axis => {
|
|
755
1082
|
// 이미 가운데면 대칭이 제자리다 — 겹친 부품이 조용히 생기지 않게 막는다
|
|
756
1083
|
const able = this.board ? canMirror(this.board, this.parts, this.selected, axis) : false;
|
|
757
1084
|
return html `
|
|
758
1085
|
<button
|
|
759
1086
|
?disabled=${!able}
|
|
1087
|
+
aria-label=${i18next.t('figure.button.mirror-' + axis)}
|
|
760
1088
|
title=${i18next.t(able ? 'figure.text.mirror-explained' : 'figure.text.mirror-would-overlap')}
|
|
761
1089
|
@click=${() => this.ask('mirror', axis)}
|
|
762
1090
|
>
|
|
763
|
-
|
|
1091
|
+
<md-icon>${axis === 'x' ? 'flip' : 'flip_camera_android'}</md-icon>
|
|
764
1092
|
</button>
|
|
765
1093
|
`;
|
|
766
1094
|
})}
|
|
767
|
-
<
|
|
768
|
-
|
|
769
|
-
|
|
1095
|
+
<button
|
|
1096
|
+
aria-label=${i18next.t('figure.button.snap')}
|
|
1097
|
+
title=${i18next.t('figure.text.snap-explained')}
|
|
1098
|
+
@click=${() => this.ask('snap', 'x')}
|
|
1099
|
+
>
|
|
1100
|
+
<md-icon>grid_on</md-icon>
|
|
770
1101
|
</button>
|
|
771
1102
|
</div>
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
<div field>
|
|
775
|
-
<label>${i18next.t('figure.label.rotation-degrees')}</label>
|
|
776
|
-
<div triple>
|
|
777
|
-
${['x', 'y', 'z'].map(axis => this.number(axis.toUpperCase(), rotation[axis] ?? 0, value => this.putTransform({ rotation: { ...rotation, [axis]: value } })))}
|
|
778
|
-
</div>
|
|
1103
|
+
${this.helpHint('figure.text.tools-explained')}
|
|
779
1104
|
</div>
|
|
780
1105
|
</section>
|
|
781
1106
|
`;
|
|
@@ -807,17 +1132,17 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
807
1132
|
const wall = hollow?.wall ?? 0;
|
|
808
1133
|
const reshape = (over) => this.put({ shape: { ...(part.shape ?? {}), ...over } });
|
|
809
1134
|
return html `
|
|
810
|
-
<div field>
|
|
1135
|
+
<div inline-field>
|
|
811
1136
|
<label>${i18next.t('figure.label.corner-round')}</label>
|
|
812
1137
|
${this.number('', part.shape?.round ?? 0, value => reshape({ round: value }))}
|
|
813
1138
|
</div>
|
|
814
|
-
<div field>
|
|
1139
|
+
<div inline-field>
|
|
815
1140
|
<label>${i18next.t('figure.label.hollow-wall')}</label>
|
|
816
1141
|
${this.number('', wall, value => reshape({ hollow: value > 0 ? { ...(hollow ?? {}), wall: value } : undefined }))}
|
|
817
1142
|
</div>
|
|
818
1143
|
${wall > 0
|
|
819
1144
|
? html `
|
|
820
|
-
<div field>
|
|
1145
|
+
<div inline-field>
|
|
821
1146
|
<label>${i18next.t('figure.label.hollow-floor')}</label>
|
|
822
1147
|
${this.number('', hollow?.floor ?? wall, value => reshape({ hollow: { ...hollow, wall, floor: value } }))}
|
|
823
1148
|
</div>
|
|
@@ -859,11 +1184,13 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
859
1184
|
}
|
|
860
1185
|
renderSegments(part) {
|
|
861
1186
|
const current = part.segments ?? SEGMENT_PRESETS[1];
|
|
1187
|
+
const common = SEGMENT_PRESETS.slice(0, 4);
|
|
1188
|
+
const advanced = SEGMENT_PRESETS.slice(4);
|
|
862
1189
|
return html `
|
|
863
1190
|
<div field>
|
|
864
1191
|
<label>${i18next.t('figure.label.segments')}</label>
|
|
865
1192
|
<div segments>
|
|
866
|
-
${
|
|
1193
|
+
${common.map(preset => {
|
|
867
1194
|
const triangles = trianglesOf({ ...part, segments: preset });
|
|
868
1195
|
return html `
|
|
869
1196
|
<button seg ?on=${current === preset} @click=${() => this.put({ segments: preset })}>
|
|
@@ -875,9 +1202,13 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
875
1202
|
})}
|
|
876
1203
|
</div>
|
|
877
1204
|
|
|
878
|
-
<details free>
|
|
879
|
-
<summary>${i18next.t('figure.text.
|
|
880
|
-
|
|
1205
|
+
<details free ?open=${advanced.includes(current)}>
|
|
1206
|
+
<summary>${i18next.t('figure.text.more-segment-options')}</summary>
|
|
1207
|
+
<div presets>
|
|
1208
|
+
${advanced.map(preset => html `
|
|
1209
|
+
<button ?on=${current === preset} @click=${() => this.put({ segments: preset })}>${preset}</button>
|
|
1210
|
+
`)}
|
|
1211
|
+
</div>
|
|
881
1212
|
</details>
|
|
882
1213
|
</div>
|
|
883
1214
|
`;
|
|
@@ -902,16 +1233,35 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
902
1233
|
`;
|
|
903
1234
|
}
|
|
904
1235
|
/** 견본 한 줄. 무리마다 부른다. */
|
|
1236
|
+
palettePurpose(token) {
|
|
1237
|
+
const purposes = {
|
|
1238
|
+
'palette.primary': 'primary',
|
|
1239
|
+
'palette.accent': 'accent',
|
|
1240
|
+
'palette.neutral': 'neutral',
|
|
1241
|
+
'palette.dark': 'dark',
|
|
1242
|
+
'palette.light': 'light',
|
|
1243
|
+
'palette.surface': 'surface',
|
|
1244
|
+
'palette.status-danger': 'danger',
|
|
1245
|
+
'palette.status-warning': 'warning',
|
|
1246
|
+
'palette.status-info': 'info',
|
|
1247
|
+
'palette.status-success': 'success'
|
|
1248
|
+
};
|
|
1249
|
+
return i18next.t(`figure.label.palette-purpose-${purposes[token ?? ''] ?? 'registered'}`);
|
|
1250
|
+
}
|
|
905
1251
|
swatches(entries, chosen) {
|
|
906
|
-
return entries.map(([token, color]) =>
|
|
1252
|
+
return entries.map(([token, color]) => {
|
|
1253
|
+
const purpose = this.palettePurpose(token);
|
|
1254
|
+
return html `
|
|
907
1255
|
<button
|
|
908
1256
|
swatch
|
|
909
1257
|
?on=${token === chosen}
|
|
910
|
-
|
|
1258
|
+
aria-label=${purpose}
|
|
1259
|
+
title=${purpose}
|
|
911
1260
|
style="--swatch:${color}"
|
|
912
1261
|
@click=${() => this.putMaterial({ token })}
|
|
913
1262
|
></button>
|
|
914
|
-
|
|
1263
|
+
`;
|
|
1264
|
+
});
|
|
915
1265
|
}
|
|
916
1266
|
renderMaterial(part) {
|
|
917
1267
|
const material = part.material ?? {};
|
|
@@ -933,7 +1283,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
933
1283
|
<h3>${i18next.t('figure.label.part-material')}</h3>
|
|
934
1284
|
|
|
935
1285
|
<div field>
|
|
936
|
-
<label>${i18next.t('figure.label.palette-
|
|
1286
|
+
<label>${i18next.t('figure.label.palette-role')}</label>
|
|
937
1287
|
<div swatches>
|
|
938
1288
|
${this.swatches(painted, material.token)}
|
|
939
1289
|
</div>
|
|
@@ -945,7 +1295,10 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
945
1295
|
</div>
|
|
946
1296
|
`
|
|
947
1297
|
: ''}
|
|
948
|
-
<
|
|
1298
|
+
<div palette-meaning>
|
|
1299
|
+
<b>${material.token ? this.palettePurpose(material.token) : i18next.t('figure.label.no-color-chosen')}</b>
|
|
1300
|
+
<small>${i18next.t('figure.text.palette-purpose-explained')}</small>
|
|
1301
|
+
</div>
|
|
949
1302
|
</div>
|
|
950
1303
|
|
|
951
1304
|
<div field>
|
|
@@ -1063,19 +1416,23 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1063
1416
|
return html `
|
|
1064
1417
|
<section>
|
|
1065
1418
|
<h3>${i18next.t('figure.label.sizing-rule')}${this.help('figure.text.sizing-' + sizing + '-explained')}</h3>
|
|
1066
|
-
<
|
|
1419
|
+
<div sizing-choices>
|
|
1067
1420
|
${SIZING_RULES.map(rule => html `
|
|
1068
|
-
<
|
|
1421
|
+
<button sizing-choice ?on=${rule === sizing} @click=${() => this.put({ sizing: rule })}>
|
|
1422
|
+
<b>${i18next.t('figure.label.sizing-' + rule)}</b>
|
|
1423
|
+
<small>${i18next.t('figure.text.sizing-' + rule + '-explained')}</small>
|
|
1424
|
+
</button>
|
|
1069
1425
|
`)}
|
|
1070
|
-
</
|
|
1071
|
-
${sizing === 'scale'
|
|
1072
|
-
|
|
1426
|
+
</div>
|
|
1427
|
+
${sizing === 'scale'
|
|
1428
|
+
? nothing
|
|
1429
|
+
: html `<p cost>${i18next.t('figure.text.sizing-costs-one-draw-call')}</p>`}
|
|
1073
1430
|
|
|
1074
1431
|
${this.renderAnchors(part)}
|
|
1075
1432
|
|
|
1076
1433
|
${sizing === 'repeat'
|
|
1077
1434
|
? html `
|
|
1078
|
-
<div field>
|
|
1435
|
+
<div inline-field>
|
|
1079
1436
|
<label>${i18next.t('figure.label.repeat-axis')}</label>
|
|
1080
1437
|
<select
|
|
1081
1438
|
@change=${(e) => this.put({ repeat: { ...repeat, axis: e.target.value } })}
|
|
@@ -1083,9 +1440,10 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1083
1440
|
${['x', 'y', 'z'].map(axis => html `<option value=${axis} ?selected=${axis === repeat.axis}>${axis}</option>`)}
|
|
1084
1441
|
</select>
|
|
1085
1442
|
</div>
|
|
1086
|
-
<div field>
|
|
1087
|
-
<label>${i18next.t('figure.label.repeat-pitch')}${this.
|
|
1443
|
+
<div inline-field>
|
|
1444
|
+
<label>${i18next.t('figure.label.repeat-pitch')}${this.helpButton('figure.text.repeat-pitch-explained')}</label>
|
|
1088
1445
|
${this.number('', repeat.pitch, value => this.put({ repeat: { ...repeat, pitch: value } }), 1)}
|
|
1446
|
+
${this.helpHint('figure.text.repeat-pitch-explained')}
|
|
1089
1447
|
</div>
|
|
1090
1448
|
${this.renderRepeatCount(part)}
|
|
1091
1449
|
`
|
|
@@ -1172,6 +1530,9 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1172
1530
|
* 계속 고치는 사람이 매번 다시 펴야 하면 접은 것이 되레 방해가 된다.
|
|
1173
1531
|
*/
|
|
1174
1532
|
help(key) {
|
|
1533
|
+
return html `${this.helpButton(key)}${this.helpHint(key)}`;
|
|
1534
|
+
}
|
|
1535
|
+
helpButton(key) {
|
|
1175
1536
|
const shown = this.opened.has(key);
|
|
1176
1537
|
return html `
|
|
1177
1538
|
<button
|
|
@@ -1185,18 +1546,38 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1185
1546
|
>
|
|
1186
1547
|
<md-icon>${shown ? 'help' : 'help_outline'}</md-icon>
|
|
1187
1548
|
</button>
|
|
1188
|
-
${shown ? html `<p hint>${i18next.t(key)}</p>` : nothing}
|
|
1189
1549
|
`;
|
|
1190
1550
|
}
|
|
1551
|
+
helpHint(key) {
|
|
1552
|
+
return this.opened.has(key) ? html `<p hint>${i18next.t(key)}</p>` : nothing;
|
|
1553
|
+
}
|
|
1191
1554
|
renderAnchors(part) {
|
|
1192
1555
|
const base = this.board
|
|
1193
1556
|
? { x: this.board.width, y: this.board.height, z: this.board.depth }
|
|
1194
1557
|
: { x: 0, y: 0, z: 0 };
|
|
1558
|
+
const nameOf = (rule, axis) => `${i18next.t(rule === 'min' || rule === 'max' ? `figure.label.anchor-${rule}-${axis}` : `figure.label.anchor-${rule}`)} (${rule})`;
|
|
1559
|
+
const setAnchor = (axis, rule) => {
|
|
1560
|
+
const anchor = { ...(part.anchor ?? {}) };
|
|
1561
|
+
/* 「자동」은 칸을 지우는 것이다 — 빈 값을 넣어 두면 형식이 그것을 값으로 읽는다. */
|
|
1562
|
+
if (rule)
|
|
1563
|
+
anchor[axis] = rule;
|
|
1564
|
+
else
|
|
1565
|
+
delete anchor[axis];
|
|
1566
|
+
this.put({ anchor: Object.keys(anchor).length ? anchor : undefined });
|
|
1567
|
+
};
|
|
1568
|
+
const shortNameOf = (rule, axis) => {
|
|
1569
|
+
if (rule === 'auto')
|
|
1570
|
+
return i18next.t('figure.label.anchor-choice-auto');
|
|
1571
|
+
if (rule === 'min' || rule === 'max')
|
|
1572
|
+
return i18next.t(`figure.label.anchor-choice-${rule}-${axis}`);
|
|
1573
|
+
return i18next.t(`figure.label.anchor-choice-${rule}`);
|
|
1574
|
+
};
|
|
1195
1575
|
return html `
|
|
1196
1576
|
<div field>
|
|
1197
|
-
<!--
|
|
1577
|
+
<!-- 저장 키(anchor)는 남기되, 사용자는 축마다 어느 면을 기준으로 삼는지 읽는다. -->
|
|
1198
1578
|
<label>${i18next.t('figure.label.anchor')} (anchor)${this.help('figure.text.anchor-explained')}</label>
|
|
1199
|
-
|
|
1579
|
+
<div anchor-rows>
|
|
1580
|
+
${['x', 'y', 'z'].map(axis => {
|
|
1200
1581
|
const said = part.anchor?.[axis];
|
|
1201
1582
|
const auto = anchorOfPart(part, base, axis);
|
|
1202
1583
|
/*
|
|
@@ -1214,32 +1595,42 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1214
1595
|
`max` 는 정본에 저장되는 이름이고 서버 오류·형식 문서·게이트 사유가 다 그 이름을 쓴다.
|
|
1215
1596
|
숨기면 안 되는 종류다. 동작이 먼저고 이름이 괄호로 따라온다.
|
|
1216
1597
|
*/
|
|
1217
|
-
const nameOf = (rule) => `${i18next.t(rule === 'min' || rule === 'max' ? `figure.label.anchor-${rule}-${axis}` : `figure.label.anchor-${rule}`)} (${rule})`;
|
|
1218
1598
|
return html `
|
|
1219
|
-
<div
|
|
1220
|
-
<
|
|
1221
|
-
<
|
|
1222
|
-
|
|
1223
|
-
const
|
|
1224
|
-
const
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
</
|
|
1238
|
-
|
|
1239
|
-
|
|
1599
|
+
<div anchor-row>
|
|
1600
|
+
<label>${axis.toUpperCase()}축</label>
|
|
1601
|
+
<div anchor-options>
|
|
1602
|
+
${['auto', 'scale', 'min', 'center', 'max', 'span'].map(rule => {
|
|
1603
|
+
const selected = rule === 'auto' ? !said : rule === said;
|
|
1604
|
+
const fullName = rule === 'auto' ? i18next.t('figure.label.anchor-auto', { rule: nameOf(auto, axis) }) : nameOf(rule, axis);
|
|
1605
|
+
return html `
|
|
1606
|
+
<button
|
|
1607
|
+
anchor-choice
|
|
1608
|
+
?on=${selected}
|
|
1609
|
+
aria-pressed=${selected ? 'true' : 'false'}
|
|
1610
|
+
title=${fullName}
|
|
1611
|
+
@click=${() => setAnchor(axis, rule === 'auto' ? undefined : rule)}
|
|
1612
|
+
>${shortNameOf(rule, axis)}</button>
|
|
1613
|
+
`;
|
|
1614
|
+
})}
|
|
1615
|
+
</div>
|
|
1616
|
+
<details anchor-advanced>
|
|
1617
|
+
<summary>${i18next.t('figure.label.anchor-advanced')}</summary>
|
|
1618
|
+
<div anchor-options>
|
|
1619
|
+
${ANCHOR_RULES.filter(rule => rule.startsWith('aspect-')).map(rule => html `
|
|
1620
|
+
<button
|
|
1621
|
+
anchor-choice
|
|
1622
|
+
?on=${rule === said}
|
|
1623
|
+
aria-pressed=${rule === said ? 'true' : 'false'}
|
|
1624
|
+
title=${nameOf(rule, axis)}
|
|
1625
|
+
@click=${() => setAnchor(axis, rule)}
|
|
1626
|
+
>${i18next.t(`figure.label.anchor-choice-${rule}`)}</button>
|
|
1627
|
+
`)}
|
|
1628
|
+
</div>
|
|
1629
|
+
</details>
|
|
1240
1630
|
</div>
|
|
1241
1631
|
`;
|
|
1242
1632
|
})}
|
|
1633
|
+
</div>
|
|
1243
1634
|
</div>
|
|
1244
1635
|
`;
|
|
1245
1636
|
}
|
|
@@ -1347,6 +1738,14 @@ __decorate([
|
|
|
1347
1738
|
property({ type: String }),
|
|
1348
1739
|
__metadata("design:type", Object)
|
|
1349
1740
|
], FigureInspector.prototype, "figureName", void 0);
|
|
1741
|
+
__decorate([
|
|
1742
|
+
property({ type: String }),
|
|
1743
|
+
__metadata("design:type", String)
|
|
1744
|
+
], FigureInspector.prototype, "figureCategory", void 0);
|
|
1745
|
+
__decorate([
|
|
1746
|
+
property({ type: Array }),
|
|
1747
|
+
__metadata("design:type", Array)
|
|
1748
|
+
], FigureInspector.prototype, "figureTags", void 0);
|
|
1350
1749
|
__decorate([
|
|
1351
1750
|
state(),
|
|
1352
1751
|
__metadata("design:type", Object)
|