@things-factory/figure-ui 10.1.7 → 10.1.9
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 +24 -0
- package/client/modeller/figure-animations.ts +61 -4
- package/client/modeller/figure-ask.ts +6 -1
- package/client/modeller/figure-history-panel.ts +52 -1
- package/client/modeller/figure-inspector.ts +477 -124
- package/client/modeller/proposal-session.ts +92 -0
- package/client/pages/figure-modeller-page.ts +179 -53
- package/client/types.ts +33 -0
- package/dist-client/graphql/index.d.ts +3 -1
- package/dist-client/graphql/index.js +21 -0
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-animations.d.ts +4 -1
- package/dist-client/modeller/figure-animations.js +57 -4
- package/dist-client/modeller/figure-animations.js.map +1 -1
- 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 +3 -0
- package/dist-client/modeller/figure-inspector.js +464 -111
- package/dist-client/modeller/figure-inspector.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-modeller-page.d.ts +43 -9
- package/dist-client/pages/figure-modeller-page.js +173 -50
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +38 -0
- package/dist-client/types.js.map +1 -1
- package/package.json +4 -4
- package/test/ai-proposal-contract.test.ts +169 -0
- package/translations/en.json +32 -0
- package/translations/ja.json +32 -0
- package/translations/ko.json +42 -0
- package/translations/ms.json +32 -0
- package/translations/zh.json +32 -0
|
@@ -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,34 +929,55 @@ 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
|
-->
|
|
695
943
|
|
|
696
|
-
<div field>
|
|
697
|
-
<label>${i18next.t('figure.label.category')}${this.
|
|
944
|
+
<div inline-field>
|
|
945
|
+
<label>${i18next.t('figure.label.category')}${this.helpButton('figure.text.category-explained')}</label>
|
|
698
946
|
<input
|
|
699
947
|
.value=${this.figureCategory ?? ''}
|
|
700
948
|
placeholder=${i18next.t('figure.text.category-placeholder')}
|
|
701
949
|
@change=${(e) => this.tellCatalog({ category: e.target.value.trim() || undefined })}
|
|
702
950
|
/>
|
|
951
|
+
${this.helpHint('figure.text.category-explained')}
|
|
703
952
|
</div>
|
|
704
953
|
|
|
705
|
-
<div field>
|
|
706
|
-
<label>${i18next.t('figure.label.tags')}${this.
|
|
954
|
+
<div inline-field>
|
|
955
|
+
<label>${i18next.t('figure.label.tags')}${this.helpButton('figure.text.tags-explained')}</label>
|
|
707
956
|
<input
|
|
708
957
|
.value=${(this.figureTags ?? []).join(', ')}
|
|
709
958
|
placeholder=${i18next.t('figure.text.tags-placeholder')}
|
|
710
959
|
@change=${(e) => this.tellTags(e.target.value)}
|
|
711
960
|
/>
|
|
961
|
+
${this.helpHint('figure.text.tags-explained')}
|
|
712
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}
|
|
713
981
|
</section>
|
|
714
982
|
`;
|
|
715
983
|
}
|
|
@@ -737,14 +1005,15 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
737
1005
|
return html `
|
|
738
1006
|
<section>
|
|
739
1007
|
<h3>${i18next.t('figure.label.part')}</h3>
|
|
740
|
-
<div field>
|
|
741
|
-
<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>
|
|
742
1010
|
<input
|
|
743
1011
|
.value=${part.name}
|
|
744
1012
|
@change=${(e) => this.put({ name: e.target.value })}
|
|
745
1013
|
/>
|
|
1014
|
+
${this.helpHint('figure.text.renaming-a-part-breaks-bindings')}
|
|
746
1015
|
</div>
|
|
747
|
-
<div field>
|
|
1016
|
+
<div inline-field>
|
|
748
1017
|
<label>${i18next.t('figure.label.shape')}</label>
|
|
749
1018
|
<select @change=${(e) => this.changePrimitive(e.target.value)}>
|
|
750
1019
|
${PRIMITIVE_KINDS.map(kind => html `
|
|
@@ -764,56 +1033,74 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
764
1033
|
<section>
|
|
765
1034
|
<h3>${i18next.t('figure.label.placement')}</h3>
|
|
766
1035
|
|
|
767
|
-
<
|
|
768
|
-
|
|
769
|
-
|
|
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>
|
|
770
1041
|
</div>
|
|
771
1042
|
|
|
772
|
-
<div
|
|
1043
|
+
<div vector-row>
|
|
773
1044
|
<label>${i18next.t('figure.label.part-size')}</label>
|
|
774
1045
|
<div triple>
|
|
775
1046
|
${['x', 'y', 'z'].map(axis => this.number(axis.toUpperCase(), t.size[axis], value => this.putTransform({ size: { ...t.size, [axis]: value } }), 1))}
|
|
776
1047
|
</div>
|
|
777
1048
|
</div>
|
|
778
1049
|
|
|
779
|
-
<div
|
|
780
|
-
<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>
|
|
781
1059
|
<div tools>
|
|
782
|
-
<button
|
|
783
|
-
|
|
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>
|
|
784
1066
|
</button>
|
|
785
|
-
<button
|
|
786
|
-
|
|
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>
|
|
787
1073
|
</button>
|
|
788
|
-
<button
|
|
789
|
-
|
|
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>
|
|
790
1080
|
</button>
|
|
791
|
-
<span sep></span>
|
|
792
1081
|
${['x', 'z'].map(axis => {
|
|
793
1082
|
// 이미 가운데면 대칭이 제자리다 — 겹친 부품이 조용히 생기지 않게 막는다
|
|
794
1083
|
const able = this.board ? canMirror(this.board, this.parts, this.selected, axis) : false;
|
|
795
1084
|
return html `
|
|
796
1085
|
<button
|
|
797
1086
|
?disabled=${!able}
|
|
1087
|
+
aria-label=${i18next.t('figure.button.mirror-' + axis)}
|
|
798
1088
|
title=${i18next.t(able ? 'figure.text.mirror-explained' : 'figure.text.mirror-would-overlap')}
|
|
799
1089
|
@click=${() => this.ask('mirror', axis)}
|
|
800
1090
|
>
|
|
801
|
-
|
|
1091
|
+
<md-icon>${axis === 'x' ? 'flip' : 'flip_camera_android'}</md-icon>
|
|
802
1092
|
</button>
|
|
803
1093
|
`;
|
|
804
1094
|
})}
|
|
805
|
-
<
|
|
806
|
-
|
|
807
|
-
|
|
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>
|
|
808
1101
|
</button>
|
|
809
1102
|
</div>
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
<div field>
|
|
813
|
-
<label>${i18next.t('figure.label.rotation-degrees')}</label>
|
|
814
|
-
<div triple>
|
|
815
|
-
${['x', 'y', 'z'].map(axis => this.number(axis.toUpperCase(), rotation[axis] ?? 0, value => this.putTransform({ rotation: { ...rotation, [axis]: value } })))}
|
|
816
|
-
</div>
|
|
1103
|
+
${this.helpHint('figure.text.tools-explained')}
|
|
817
1104
|
</div>
|
|
818
1105
|
</section>
|
|
819
1106
|
`;
|
|
@@ -845,17 +1132,17 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
845
1132
|
const wall = hollow?.wall ?? 0;
|
|
846
1133
|
const reshape = (over) => this.put({ shape: { ...(part.shape ?? {}), ...over } });
|
|
847
1134
|
return html `
|
|
848
|
-
<div field>
|
|
1135
|
+
<div inline-field>
|
|
849
1136
|
<label>${i18next.t('figure.label.corner-round')}</label>
|
|
850
1137
|
${this.number('', part.shape?.round ?? 0, value => reshape({ round: value }))}
|
|
851
1138
|
</div>
|
|
852
|
-
<div field>
|
|
1139
|
+
<div inline-field>
|
|
853
1140
|
<label>${i18next.t('figure.label.hollow-wall')}</label>
|
|
854
1141
|
${this.number('', wall, value => reshape({ hollow: value > 0 ? { ...(hollow ?? {}), wall: value } : undefined }))}
|
|
855
1142
|
</div>
|
|
856
1143
|
${wall > 0
|
|
857
1144
|
? html `
|
|
858
|
-
<div field>
|
|
1145
|
+
<div inline-field>
|
|
859
1146
|
<label>${i18next.t('figure.label.hollow-floor')}</label>
|
|
860
1147
|
${this.number('', hollow?.floor ?? wall, value => reshape({ hollow: { ...hollow, wall, floor: value } }))}
|
|
861
1148
|
</div>
|
|
@@ -897,11 +1184,13 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
897
1184
|
}
|
|
898
1185
|
renderSegments(part) {
|
|
899
1186
|
const current = part.segments ?? SEGMENT_PRESETS[1];
|
|
1187
|
+
const common = SEGMENT_PRESETS.slice(0, 4);
|
|
1188
|
+
const advanced = SEGMENT_PRESETS.slice(4);
|
|
900
1189
|
return html `
|
|
901
1190
|
<div field>
|
|
902
1191
|
<label>${i18next.t('figure.label.segments')}</label>
|
|
903
1192
|
<div segments>
|
|
904
|
-
${
|
|
1193
|
+
${common.map(preset => {
|
|
905
1194
|
const triangles = trianglesOf({ ...part, segments: preset });
|
|
906
1195
|
return html `
|
|
907
1196
|
<button seg ?on=${current === preset} @click=${() => this.put({ segments: preset })}>
|
|
@@ -913,9 +1202,13 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
913
1202
|
})}
|
|
914
1203
|
</div>
|
|
915
1204
|
|
|
916
|
-
<details free>
|
|
917
|
-
<summary>${i18next.t('figure.text.
|
|
918
|
-
|
|
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>
|
|
919
1212
|
</details>
|
|
920
1213
|
</div>
|
|
921
1214
|
`;
|
|
@@ -940,16 +1233,35 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
940
1233
|
`;
|
|
941
1234
|
}
|
|
942
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
|
+
}
|
|
943
1251
|
swatches(entries, chosen) {
|
|
944
|
-
return entries.map(([token, color]) =>
|
|
1252
|
+
return entries.map(([token, color]) => {
|
|
1253
|
+
const purpose = this.palettePurpose(token);
|
|
1254
|
+
return html `
|
|
945
1255
|
<button
|
|
946
1256
|
swatch
|
|
947
1257
|
?on=${token === chosen}
|
|
948
|
-
|
|
1258
|
+
aria-label=${purpose}
|
|
1259
|
+
title=${purpose}
|
|
949
1260
|
style="--swatch:${color}"
|
|
950
1261
|
@click=${() => this.putMaterial({ token })}
|
|
951
1262
|
></button>
|
|
952
|
-
|
|
1263
|
+
`;
|
|
1264
|
+
});
|
|
953
1265
|
}
|
|
954
1266
|
renderMaterial(part) {
|
|
955
1267
|
const material = part.material ?? {};
|
|
@@ -971,7 +1283,7 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
971
1283
|
<h3>${i18next.t('figure.label.part-material')}</h3>
|
|
972
1284
|
|
|
973
1285
|
<div field>
|
|
974
|
-
<label>${i18next.t('figure.label.palette-
|
|
1286
|
+
<label>${i18next.t('figure.label.palette-role')}</label>
|
|
975
1287
|
<div swatches>
|
|
976
1288
|
${this.swatches(painted, material.token)}
|
|
977
1289
|
</div>
|
|
@@ -983,7 +1295,10 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
983
1295
|
</div>
|
|
984
1296
|
`
|
|
985
1297
|
: ''}
|
|
986
|
-
<
|
|
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>
|
|
987
1302
|
</div>
|
|
988
1303
|
|
|
989
1304
|
<div field>
|
|
@@ -1101,19 +1416,23 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1101
1416
|
return html `
|
|
1102
1417
|
<section>
|
|
1103
1418
|
<h3>${i18next.t('figure.label.sizing-rule')}${this.help('figure.text.sizing-' + sizing + '-explained')}</h3>
|
|
1104
|
-
<
|
|
1419
|
+
<div sizing-choices>
|
|
1105
1420
|
${SIZING_RULES.map(rule => html `
|
|
1106
|
-
<
|
|
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>
|
|
1107
1425
|
`)}
|
|
1108
|
-
</
|
|
1109
|
-
${sizing === 'scale'
|
|
1110
|
-
|
|
1426
|
+
</div>
|
|
1427
|
+
${sizing === 'scale'
|
|
1428
|
+
? nothing
|
|
1429
|
+
: html `<p cost>${i18next.t('figure.text.sizing-costs-one-draw-call')}</p>`}
|
|
1111
1430
|
|
|
1112
1431
|
${this.renderAnchors(part)}
|
|
1113
1432
|
|
|
1114
1433
|
${sizing === 'repeat'
|
|
1115
1434
|
? html `
|
|
1116
|
-
<div field>
|
|
1435
|
+
<div inline-field>
|
|
1117
1436
|
<label>${i18next.t('figure.label.repeat-axis')}</label>
|
|
1118
1437
|
<select
|
|
1119
1438
|
@change=${(e) => this.put({ repeat: { ...repeat, axis: e.target.value } })}
|
|
@@ -1121,9 +1440,10 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1121
1440
|
${['x', 'y', 'z'].map(axis => html `<option value=${axis} ?selected=${axis === repeat.axis}>${axis}</option>`)}
|
|
1122
1441
|
</select>
|
|
1123
1442
|
</div>
|
|
1124
|
-
<div field>
|
|
1125
|
-
<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>
|
|
1126
1445
|
${this.number('', repeat.pitch, value => this.put({ repeat: { ...repeat, pitch: value } }), 1)}
|
|
1446
|
+
${this.helpHint('figure.text.repeat-pitch-explained')}
|
|
1127
1447
|
</div>
|
|
1128
1448
|
${this.renderRepeatCount(part)}
|
|
1129
1449
|
`
|
|
@@ -1210,6 +1530,9 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1210
1530
|
* 계속 고치는 사람이 매번 다시 펴야 하면 접은 것이 되레 방해가 된다.
|
|
1211
1531
|
*/
|
|
1212
1532
|
help(key) {
|
|
1533
|
+
return html `${this.helpButton(key)}${this.helpHint(key)}`;
|
|
1534
|
+
}
|
|
1535
|
+
helpButton(key) {
|
|
1213
1536
|
const shown = this.opened.has(key);
|
|
1214
1537
|
return html `
|
|
1215
1538
|
<button
|
|
@@ -1223,18 +1546,38 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1223
1546
|
>
|
|
1224
1547
|
<md-icon>${shown ? 'help' : 'help_outline'}</md-icon>
|
|
1225
1548
|
</button>
|
|
1226
|
-
${shown ? html `<p hint>${i18next.t(key)}</p>` : nothing}
|
|
1227
1549
|
`;
|
|
1228
1550
|
}
|
|
1551
|
+
helpHint(key) {
|
|
1552
|
+
return this.opened.has(key) ? html `<p hint>${i18next.t(key)}</p>` : nothing;
|
|
1553
|
+
}
|
|
1229
1554
|
renderAnchors(part) {
|
|
1230
1555
|
const base = this.board
|
|
1231
1556
|
? { x: this.board.width, y: this.board.height, z: this.board.depth }
|
|
1232
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
|
+
};
|
|
1233
1575
|
return html `
|
|
1234
1576
|
<div field>
|
|
1235
|
-
<!--
|
|
1577
|
+
<!-- 저장 키(anchor)는 남기되, 사용자는 축마다 어느 면을 기준으로 삼는지 읽는다. -->
|
|
1236
1578
|
<label>${i18next.t('figure.label.anchor')} (anchor)${this.help('figure.text.anchor-explained')}</label>
|
|
1237
|
-
|
|
1579
|
+
<div anchor-rows>
|
|
1580
|
+
${['x', 'y', 'z'].map(axis => {
|
|
1238
1581
|
const said = part.anchor?.[axis];
|
|
1239
1582
|
const auto = anchorOfPart(part, base, axis);
|
|
1240
1583
|
/*
|
|
@@ -1252,32 +1595,42 @@ let FigureInspector = class FigureInspector extends localize(i18next)(LitElement
|
|
|
1252
1595
|
`max` 는 정본에 저장되는 이름이고 서버 오류·형식 문서·게이트 사유가 다 그 이름을 쓴다.
|
|
1253
1596
|
숨기면 안 되는 종류다. 동작이 먼저고 이름이 괄호로 따라온다.
|
|
1254
1597
|
*/
|
|
1255
|
-
const nameOf = (rule) => `${i18next.t(rule === 'min' || rule === 'max' ? `figure.label.anchor-${rule}-${axis}` : `figure.label.anchor-${rule}`)} (${rule})`;
|
|
1256
1598
|
return html `
|
|
1257
|
-
<div
|
|
1258
|
-
<
|
|
1259
|
-
<
|
|
1260
|
-
|
|
1261
|
-
const
|
|
1262
|
-
const
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
</
|
|
1276
|
-
|
|
1277
|
-
|
|
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>
|
|
1278
1630
|
</div>
|
|
1279
1631
|
`;
|
|
1280
1632
|
})}
|
|
1633
|
+
</div>
|
|
1281
1634
|
</div>
|
|
1282
1635
|
`;
|
|
1283
1636
|
}
|