@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
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import '@material/web/icon/icon.js'
|
|
1
2
|
import './figure-inspector.js'
|
|
2
3
|
import './figure-capabilities.js'
|
|
4
|
+
import './figure-animations.js'
|
|
3
5
|
import './figure-report.js'
|
|
4
6
|
import './figure-settings.js'
|
|
5
7
|
import './figure-history-panel.js'
|
|
@@ -14,7 +16,7 @@ import type { BoardModel, PartModel } from './figure-source.js'
|
|
|
14
16
|
import { DEFAULT_VIEW } from './figure-view.js'
|
|
15
17
|
import type { ViewSettings } from './figure-view.js'
|
|
16
18
|
|
|
17
|
-
type Tab = 'properties' | 'capability' | 'verdict' | 'view' | 'history'
|
|
19
|
+
type Tab = 'properties' | 'capability' | 'animation' | 'verdict' | 'view' | 'history'
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* 저작면 오른쪽 칸 — 속성과 판정을 탭으로 겹쳐 둔다.
|
|
@@ -67,21 +69,21 @@ export class FigureSide extends localize(i18next)(LitElement) {
|
|
|
67
69
|
button[tab] {
|
|
68
70
|
flex: 1;
|
|
69
71
|
/*
|
|
70
|
-
|
|
71
|
-
이름이 접혀 「성숙 / 도」가 되었다. 이름이 접히면 그 줄이 두 줄이 되고 네 탭의 높이가
|
|
72
|
-
서로 달라진다.
|
|
72
|
+
**아이콘 하나가 한 칸을 쓴다.**
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
전에는 이름을 적었는데, 넷이 좁은 칸을 나눠 쓰면서 접혀 「성숙 / 도」가 되었다. 접히면
|
|
75
|
+
그 탭만 두 줄이 되어 높이가 서로 달라진다. 다섯이 되면서 더는 줄여 짜 넣을 수 없어
|
|
76
|
+
아이콘으로 바꿨고, 이름은 title 과 aria-label 로 남는다.
|
|
77
|
+
|
|
78
|
+
배지는 여전히 자리를 차지하지 않고 **아이콘 위에 겹친다**(아래 span[badge]). 개수는
|
|
79
|
+
곁들이는 것이다.
|
|
76
80
|
*/
|
|
77
81
|
position: relative;
|
|
78
82
|
display: flex;
|
|
79
83
|
align-items: center;
|
|
80
84
|
justify-content: center;
|
|
81
85
|
white-space: nowrap;
|
|
82
|
-
padding:
|
|
83
|
-
font: var(--label-font, inherit);
|
|
84
|
-
font-size: 0.76rem;
|
|
86
|
+
padding: 7px 6px;
|
|
85
87
|
color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));
|
|
86
88
|
background: none;
|
|
87
89
|
border: none;
|
|
@@ -104,6 +106,11 @@ export class FigureSide extends localize(i18next)(LitElement) {
|
|
|
104
106
|
자리를 먹지 않게 오른쪽 위에 겹쳐 둔다 — 탭 이름을 밀지 않으므로 이름이 접히지 않는다.
|
|
105
107
|
숫자가 커져도(1,200 같은 것) 이름 위로 넘칠 뿐 줄을 바꾸지 않는다.
|
|
106
108
|
*/
|
|
109
|
+
button[tab] md-icon {
|
|
110
|
+
font-size: 20px;
|
|
111
|
+
width: 20px;
|
|
112
|
+
height: 20px;
|
|
113
|
+
}
|
|
107
114
|
span[badge] {
|
|
108
115
|
position: absolute;
|
|
109
116
|
/* 이름 위에 겹치되 위로 붙지 않게 — 탭 가운데 글자와 눈높이를 조금 맞춘다 */
|
|
@@ -120,6 +127,7 @@ export class FigureSide extends localize(i18next)(LitElement) {
|
|
|
120
127
|
}
|
|
121
128
|
|
|
122
129
|
figure-inspector,
|
|
130
|
+
figure-animations,
|
|
123
131
|
figure-report,
|
|
124
132
|
figure-settings {
|
|
125
133
|
flex: 1;
|
|
@@ -139,6 +147,9 @@ export class FigureSide extends localize(i18next)(LitElement) {
|
|
|
139
147
|
곳이라 변환된 FigureSource 를 받는다. 변환은 모델러 페이지가 한 번만 해서 내려보낸다.
|
|
140
148
|
*/
|
|
141
149
|
@property({ type: String }) figureName = ''
|
|
150
|
+
/** 카탈로그 분류·태그. 속성 탭이 보여 주고 바꾼다. */
|
|
151
|
+
@property({ type: String }) figureCategory?: string
|
|
152
|
+
@property({ type: Array }) figureTags?: string[]
|
|
142
153
|
/** 이력 탭이 발행하고 되돌리는 데 필요한 것들. 정본은 저작면이 든다. */
|
|
143
154
|
@property({ type: String }) figureId?: string
|
|
144
155
|
@property({ type: String }) figureState?: 'draft' | 'released'
|
|
@@ -156,19 +167,34 @@ export class FigureSide extends localize(i18next)(LitElement) {
|
|
|
156
167
|
/** 판정이 알려 준 개수. 탭 이름표에 붙는다. */
|
|
157
168
|
@state() private instances?: number
|
|
158
169
|
|
|
170
|
+
/**
|
|
171
|
+
* 이 도형이 든 clip 수. 탭 아이콘에 붙는다.
|
|
172
|
+
*
|
|
173
|
+
* 아이콘만으로는 「여기에 무언가 있다」를 알 수 없다. 대부분의 도형은 움직이지 않으므로
|
|
174
|
+
* 이 탭은 늘 비어 있을 것이고, 그러면 움직이는 도형을 열었을 때 그 사실이 눈에 걸려야 한다.
|
|
175
|
+
* 0 이면 아무것도 안 붙인다 — 「0」을 붙이면 없는 것이 있는 것처럼 자리를 차지한다.
|
|
176
|
+
*/
|
|
177
|
+
private get clipCount(): number | undefined {
|
|
178
|
+
const n = this.board?.animations?.length ?? 0
|
|
179
|
+
return n === 0 ? undefined : n
|
|
180
|
+
}
|
|
181
|
+
|
|
159
182
|
render() {
|
|
160
183
|
return html`
|
|
161
184
|
<div tabs>
|
|
162
|
-
${this.renderTab('properties', i18next.t('figure.label.properties'))}
|
|
163
|
-
${this.renderTab('capability', i18next.t('figure.label.tab-capability'))}
|
|
164
|
-
${this.renderTab('
|
|
165
|
-
${this.renderTab('
|
|
166
|
-
${this.renderTab('
|
|
185
|
+
${this.renderTab('properties', 'tune', i18next.t('figure.label.properties'))}
|
|
186
|
+
${this.renderTab('capability', 'conveyor_belt', i18next.t('figure.label.tab-capability'))}
|
|
187
|
+
${this.renderTab('animation', 'animation', i18next.t('figure.label.animation'), this.clipCount)}
|
|
188
|
+
${this.renderTab('verdict', 'insights', i18next.t('figure.label.maturity'), this.instances)}
|
|
189
|
+
${this.renderTab('view', 'wb_sunny', i18next.t('figure.label.tab-environment'))}
|
|
190
|
+
${this.renderTab('history', 'history', i18next.t('figure.label.history'))}
|
|
167
191
|
</div>
|
|
168
192
|
|
|
169
193
|
<figure-inspector
|
|
170
194
|
?off=${this.tab !== 'properties'}
|
|
171
195
|
.figureName=${this.figureName}
|
|
196
|
+
.figureCategory=${this.figureCategory}
|
|
197
|
+
.figureTags=${this.figureTags}
|
|
172
198
|
.board=${this.board}
|
|
173
199
|
.parts=${this.parts}
|
|
174
200
|
.selected=${this.selected}
|
|
@@ -180,6 +206,12 @@ export class FigureSide extends localize(i18next)(LitElement) {
|
|
|
180
206
|
.parts=${this.parts}
|
|
181
207
|
></figure-capabilities>
|
|
182
208
|
|
|
209
|
+
<figure-animations
|
|
210
|
+
?off=${this.tab !== 'animation'}
|
|
211
|
+
.board=${this.board}
|
|
212
|
+
.parts=${this.parts}
|
|
213
|
+
></figure-animations>
|
|
214
|
+
|
|
183
215
|
<figure-report
|
|
184
216
|
?off=${this.tab !== 'verdict'}
|
|
185
217
|
.source=${this.source}
|
|
@@ -201,10 +233,26 @@ export class FigureSide extends localize(i18next)(LitElement) {
|
|
|
201
233
|
`
|
|
202
234
|
}
|
|
203
235
|
|
|
204
|
-
|
|
236
|
+
/**
|
|
237
|
+
* 탭 하나 — **아이콘이 이름을 대신한다.**
|
|
238
|
+
*
|
|
239
|
+
* 탭이 다섯이 되면서 이름이 좁은 칸을 나눠 쓰다 접혔다. 앞으로 더 늘어날 자리이므로 이름을
|
|
240
|
+
* 줄여 짜 넣는 대신 아이콘으로 바꾼다.
|
|
241
|
+
*
|
|
242
|
+
* **이름을 버리지는 않는다.** 아이콘만으로는 무엇인지 모르는 사람이 있고, 특히 「능력」처럼
|
|
243
|
+
* 이 도구에만 있는 개념이 그렇다. 그래서 이름은 `title` 로 남아 마우스를 올리면 나오고,
|
|
244
|
+
* `aria-label` 로 읽는 기계에도 그대로 간다.
|
|
245
|
+
*/
|
|
246
|
+
private renderTab(tab: Tab, icon: string, label: string, badge?: number) {
|
|
205
247
|
return html`
|
|
206
|
-
<button
|
|
207
|
-
|
|
248
|
+
<button
|
|
249
|
+
tab
|
|
250
|
+
?on=${this.tab === tab}
|
|
251
|
+
title=${label}
|
|
252
|
+
aria-label=${label}
|
|
253
|
+
@click=${() => (this.tab = tab)}
|
|
254
|
+
>
|
|
255
|
+
<md-icon>${icon}</md-icon>${badge == null ? '' : html`<span badge>${badge.toLocaleString()}</span>`}
|
|
208
256
|
</button>
|
|
209
257
|
`
|
|
210
258
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { FigureSource } from '@hatiolab/figure-model'
|
|
2
|
+
|
|
3
|
+
import type { FigureGateFix, FigureProposal, ProposalFeedback } from '../types.js'
|
|
4
|
+
import { diffProposal } from './proposal.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 한 번의 AI 후보 검토에만 존재하는 상태다.
|
|
8
|
+
*
|
|
9
|
+
* 후보는 정본이 아니다. 후보 원본·선택·메모·측정값을 한 값으로 묶어 두면 화면이
|
|
10
|
+
* 수락/거절 중 일부만 이전 후보에서 남기는 일이 없다.
|
|
11
|
+
*/
|
|
12
|
+
export interface ProposalSession {
|
|
13
|
+
source: FigureSource
|
|
14
|
+
title: string
|
|
15
|
+
picked: Set<string>
|
|
16
|
+
note: string
|
|
17
|
+
metrics?: Pick<FigureProposal, 'grade' | 'triangles' | 'groups' | 'attempts' | 'quality'>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function selectedKeys(current: FigureSource | undefined, source: FigureSource): Set<string> {
|
|
21
|
+
return new Set(diffProposal(current, source).map(change => change.key))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 서버 후보를 검토 세션으로 연다. 저장 형식이 아니면 화면에 후보를 열지 않는다. */
|
|
25
|
+
export function openAiProposal(current: FigureSource | undefined, proposal: FigureProposal): ProposalSession {
|
|
26
|
+
const source = JSON.parse(proposal.source) as FigureSource
|
|
27
|
+
if (!source || !Array.isArray(source.parts)) {
|
|
28
|
+
throw new Error('AI 후보의 Figure 형식이 올바르지 않습니다.')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
source,
|
|
33
|
+
title: '',
|
|
34
|
+
picked: selectedKeys(current, source),
|
|
35
|
+
note: '',
|
|
36
|
+
metrics: {
|
|
37
|
+
grade: proposal.grade,
|
|
38
|
+
triangles: proposal.triangles,
|
|
39
|
+
groups: proposal.groups,
|
|
40
|
+
attempts: proposal.attempts,
|
|
41
|
+
quality: proposal.quality
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 발행 게이트가 측정한 한-앵커 수정도 같은 후보 세션으로 연다. */
|
|
47
|
+
export function openSizingFix(current: FigureSource, fix: FigureGateFix): ProposalSession | undefined {
|
|
48
|
+
if (!current.parts.some(part => part.name === fix.part)) return undefined
|
|
49
|
+
|
|
50
|
+
const source: FigureSource = {
|
|
51
|
+
...current,
|
|
52
|
+
parts: current.parts.map(part =>
|
|
53
|
+
part.name === fix.part ? { ...part, anchor: { ...(part.anchor ?? {}), [fix.axis]: fix.to } } : part
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
source,
|
|
59
|
+
title: `발행 검사 수정안 · ${fix.part}.anchor.${fix.axis}: ${fix.from ?? 'auto'} → ${fix.to}`,
|
|
60
|
+
picked: selectedKeys(current, source),
|
|
61
|
+
note: ''
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 후보 검토 중 사람의 선택을 뒤집는다. 기존 Set을 바꾸지 않아 Lit 상태 경계도 보존한다. */
|
|
66
|
+
export function toggleProposalChange(session: ProposalSession, key: string): ProposalSession {
|
|
67
|
+
const picked = new Set(session.picked)
|
|
68
|
+
if (picked.has(key)) picked.delete(key)
|
|
69
|
+
else picked.add(key)
|
|
70
|
+
return { ...session, picked }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 사람의 결정을, 다음 AI 요청에만 쓰는 제한된 피드백으로 만든다. */
|
|
74
|
+
export function feedbackFromSession(
|
|
75
|
+
current: FigureSource | undefined,
|
|
76
|
+
session: ProposalSession,
|
|
77
|
+
outcome: ProposalFeedback['outcome']
|
|
78
|
+
): ProposalFeedback | undefined {
|
|
79
|
+
if (!session.metrics) return undefined
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
outcome,
|
|
83
|
+
selectedChanges: outcome === 'accepted' ? session.picked.size : 0,
|
|
84
|
+
totalChanges: diffProposal(current, session.source).length,
|
|
85
|
+
note: session.note.trim().slice(0, 500) || undefined,
|
|
86
|
+
grade: session.metrics.grade,
|
|
87
|
+
quality: {
|
|
88
|
+
status: session.metrics.quality.status,
|
|
89
|
+
findingCodes: session.metrics.quality.findings.map(finding => finding.code)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -36,10 +36,21 @@ const CAPACITY = 'font-weight: 700; color: var(--md-sys-color-primary);'
|
|
|
36
36
|
const UNMEASURED = 'font-style: italic; color: var(--md-sys-color-outline);'
|
|
37
37
|
const RELEASED = 'font-weight: 700; color: var(--md-sys-color-primary);'
|
|
38
38
|
const DRAFT = 'color: var(--md-sys-color-tertiary);'
|
|
39
|
-
/**
|
|
40
|
-
|
|
39
|
+
/**
|
|
40
|
+
* 조용히 적는 숫자. 낮은 것만 점으로 눈에 걸리게 한다.
|
|
41
|
+
*
|
|
42
|
+
* **`inline-flex` 를 쓰지 않는다.** 썼더니 점이 뜨는 카드에서만 이 값이 옆의 두 값과 밑줄이
|
|
43
|
+
* 어긋났다 — inline-flex 상자의 밑줄은 **첫 항목**에서 오는데, 첫 항목이 글자 없는 점이라
|
|
44
|
+
* 밑줄이 없어 브라우저가 상자 아래를 밑줄로 삼는다. 점이 없는 카드(50 이상)는 멀쩡했으므로
|
|
45
|
+
* 한 줄에 세 값이 나란히 설 때만 눈에 띄었다.
|
|
46
|
+
*
|
|
47
|
+
* 그냥 인라인으로 두면 이 상자의 밑줄이 곧 숫자의 밑줄이라 옆의 둘과 맞는다. 점은 제 몫으로
|
|
48
|
+
* 글자 가운데에 맞춘다.
|
|
49
|
+
*/
|
|
50
|
+
const SCORE = 'font-variant-numeric: tabular-nums;'
|
|
41
51
|
const LOW_DOT =
|
|
42
|
-
'width: 5px; height: 5px;
|
|
52
|
+
'display: inline-block; width: 5px; height: 5px; margin-right: 4px; vertical-align: middle; ' +
|
|
53
|
+
'border-radius: 50%; background-color: var(--md-sys-color-error);'
|
|
43
54
|
|
|
44
55
|
/*
|
|
45
56
|
* `as typeof PageView` 로 조이지 않는다 — 그렇게 하면 p13n 이 얹어 주는
|
|
@@ -179,7 +190,7 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
179
190
|
this.grist.searchText = search
|
|
180
191
|
},
|
|
181
192
|
value: this.grist?.searchText || '',
|
|
182
|
-
placeholder: i18next.t('figure.text.search-figure
|
|
193
|
+
placeholder: i18next.t('figure.text.search-figure')
|
|
183
194
|
},
|
|
184
195
|
filter: {
|
|
185
196
|
handler: () => {
|
|
@@ -200,6 +211,7 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
200
211
|
return html`
|
|
201
212
|
<ox-grist
|
|
202
213
|
mode="CARD"
|
|
214
|
+
@search-tag=${(e: CustomEvent) => this.findByTag(e.detail.tag)}
|
|
203
215
|
.config=${this.gristConfig}
|
|
204
216
|
.fetchHandler=${this.fetchHandler.bind(this)}
|
|
205
217
|
.personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')!}
|
|
@@ -219,6 +231,21 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
219
231
|
`
|
|
220
232
|
}
|
|
221
233
|
|
|
234
|
+
/**
|
|
235
|
+
* 카드의 태그 알약을 눌렀다 — **그 태그로 목록을 거른다.**
|
|
236
|
+
*
|
|
237
|
+
* 검색창에 그 말을 채워 넣는다. 격자에 조건만 걸고 창을 비워 두면 저작자는 **왜 목록이
|
|
238
|
+
* 줄었는지 모른 채** 보게 되고, 되돌리려면 무엇을 지워야 하는지도 알 수 없다.
|
|
239
|
+
*
|
|
240
|
+
* 서버가 `tags` 뿐 아니라 이름·타입·설명·분류를 함께 훑으므로, 그 말이 다른 칸에 든 도형도
|
|
241
|
+
* 같이 온다. 태그만 정확히 거르는 것이 아니라 **찾기**다.
|
|
242
|
+
*/
|
|
243
|
+
private findByTag(tag: string) {
|
|
244
|
+
this.grist.searchText = tag
|
|
245
|
+
/* 창의 값은 `context` 에서 나온다 — 다시 알려야 헤더가 새 값을 그린다. */
|
|
246
|
+
this.updateContext()
|
|
247
|
+
}
|
|
248
|
+
|
|
222
249
|
async pageInitialized() {
|
|
223
250
|
this.gristConfig = {
|
|
224
251
|
/*
|
|
@@ -300,6 +327,31 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
300
327
|
sortable: true,
|
|
301
328
|
width: 160
|
|
302
329
|
},
|
|
330
|
+
{
|
|
331
|
+
/*
|
|
332
|
+
태그 — **카드에서 곁줄로 보이고 검색에 걸린다.**
|
|
333
|
+
|
|
334
|
+
서버가 이미 `type`·`name`·`description`·`category`·`tags` 를 함께 훑는다. 그런데
|
|
335
|
+
화면에는 태그가 어디에도 안 보여서, 저작자가 적은 태그가 **자기 눈에 돌아오지 않았다** —
|
|
336
|
+
적어 둔 것이 쓰이는지 알 수 없으면 아무도 적지 않는다.
|
|
337
|
+
|
|
338
|
+
정렬은 걸지 않는다. 배열이라 무엇을 기준으로 줄을 세우는지 답할 수 없다.
|
|
339
|
+
|
|
340
|
+
**카드에서는 이 컬럼이 그리지 않는다.** 본문에 한 줄로 두었더니 태그가 있는 카드만
|
|
341
|
+
높아져 격자가 어긋났다 — 개수가 도형마다 다르므로 본문에 두는 한 남는 문제다.
|
|
342
|
+
카드에서는 `figure-thumb` 이 그림 위에 얹는다. 여기 남은 것은 표 모드의 칸이다.
|
|
343
|
+
*/
|
|
344
|
+
type: 'string',
|
|
345
|
+
name: 'tags',
|
|
346
|
+
header: i18next.t('figure.label.tags'),
|
|
347
|
+
record: {
|
|
348
|
+
editable: false,
|
|
349
|
+
renderer: (value: string[] | null) => (value || []).join(' · ')
|
|
350
|
+
},
|
|
351
|
+
filter: 'search',
|
|
352
|
+
sortable: false,
|
|
353
|
+
width: 180
|
|
354
|
+
},
|
|
303
355
|
{
|
|
304
356
|
type: 'string',
|
|
305
357
|
name: 'state',
|