@operato/data-grist 10.8.0 → 10.9.0

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/src/data-card/data-card.d.ts +6 -0
  3. package/dist/src/data-card/data-card.js +12 -3
  4. package/dist/src/data-card/data-card.js.map +1 -1
  5. package/dist/src/data-grid/data-grid-body.d.ts +7 -0
  6. package/dist/src/data-grid/data-grid-body.js +15 -0
  7. package/dist/src/data-grid/data-grid-body.js.map +1 -1
  8. package/dist/src/data-grid/data-grid-field.d.ts +21 -0
  9. package/dist/src/data-grid/data-grid-field.js +311 -4
  10. package/dist/src/data-grid/data-grid-field.js.map +1 -1
  11. package/dist/src/data-grid/data-grid-header.js +16 -28
  12. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  13. package/dist/src/data-grid/data-grid.d.ts +6 -0
  14. package/dist/src/data-grid/data-grid.js +9 -18
  15. package/dist/src/data-grid/data-grid.js.map +1 -1
  16. package/dist/src/data-grist.d.ts +25 -0
  17. package/dist/src/data-grist.js +74 -2
  18. package/dist/src/data-grist.js.map +1 -1
  19. package/dist/src/data-list/data-list.d.ts +6 -0
  20. package/dist/src/data-list/data-list.js +12 -3
  21. package/dist/src/data-list/data-list.js.map +1 -1
  22. package/dist/src/editors/ox-grist-editor-json5.d.ts +1 -0
  23. package/dist/src/editors/ox-grist-editor-json5.js +2 -0
  24. package/dist/src/editors/ox-grist-editor-json5.js.map +1 -1
  25. package/dist/src/editors/ox-grist-editor.d.ts +14 -0
  26. package/dist/src/editors/ox-grist-editor.js +36 -0
  27. package/dist/src/editors/ox-grist-editor.js.map +1 -1
  28. package/dist/src/editors/registry.d.ts +10 -0
  29. package/dist/src/editors/registry.js +16 -0
  30. package/dist/src/editors/registry.js.map +1 -1
  31. package/dist/test/row-hover-visible.test.js +26 -14
  32. package/dist/test/row-hover-visible.test.js.map +1 -1
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/package.json +6 -6
  35. package/test/row-hover-visible.test.ts +30 -14
  36. package/translations/en.json +12 -10
  37. package/translations/ja.json +11 -9
  38. package/translations/ko.json +12 -10
  39. package/translations/ms.json +11 -9
  40. package/translations/zh.json +11 -9
@@ -1,9 +1,12 @@
1
1
  import { __decorate } from "tslib";
2
+ import '@material/web/icon/icon.js';
3
+ import i18next from 'i18next';
2
4
  import { css, html, LitElement } from 'lit';
3
5
  // import { guard } from 'lit/directives/guard.js'
4
6
  import { customElement, property } from 'lit/decorators.js';
5
7
  import { TooltipReactiveController } from '@operato/utils';
6
8
  import { ZERO_COLUMN } from '../configure/zero-config.js';
9
+ import { editorOpensPopup } from '../editors/registry.js';
7
10
  const DEFAULT_TEXT_ALIGN = 'left';
8
11
  let DataGridField = class DataGridField extends LitElement {
9
12
  constructor() {
@@ -33,10 +36,92 @@ let DataGridField = class DataGridField extends LitElement {
33
36
  ${this.editing
34
37
  ? editor === null || editor === void 0 ? void 0 : editor.call(this, value, column, record, rowIndex, this)
35
38
  : renderer === null || renderer === void 0 ? void 0 : renderer.call(this, value, column, record, rowIndex, this)}
39
+ <!--
40
+ **편집 중에도 표식을 남긴다.**
41
+
42
+ 칸을 누르면 편집 상태가 되고, 그때는 렌더러 대신 편집기가 그려진다. 표식을 !editing 으로
43
+ 묶어 두었더니 **칸에 들어간 순간 사라졌다** — 들어간 사람에게 여는 길이 없어지는 셈이고,
44
+ 다시 더블클릭이나 Enter 를 짐작해야 한다. 그 짐작을 없애려고 만든 표식이다.
45
+
46
+ 팝업 편집기에만 붙으므로(opensPopup) 글자를 치는 편집기를 가릴 일은 없다.
47
+ -->
48
+ <!--
49
+ ── 칸 안에서 고치는 중에는 표식을 걷는다 ────────────────────────────────
50
+ 입력칸이 이미 그 자리에 있다. 펜을 겹쳐 두면 글자를 가리고, 고치는 중인 사람에게
51
+ 「고칠 수 있다」를 다시 말할 이유도 없다.
52
+
53
+ 팝업 쪽은 남긴다 — 편집 상태에 들어가도 팝업은 아직 안 열렸고, 그 표식이 여는 길이다.
54
+ -->
55
+ ${this.editMark && !(this.editing && this.editMark === 'inline')
56
+ ? html `<md-icon
57
+ popup-hint
58
+ ?inline=${this.editMark === 'inline'}
59
+ title=${i18next.t('text.grid-open-editor', { defaultValue: 'press to edit' })}
60
+ @pointerdown=${async (e) => {
61
+ /*
62
+ * **click 이 아니라 pointerdown 이다.**
63
+ *
64
+ * 이 표에서는 click 이벤트가 나지 않는다 — 몸통이 pointer 단계에서 기본 동작을
65
+ * 막는다. document 에 capture 로 click 을 걸어 두고 표식을 눌러 봐도 한 번도
66
+ * 울리지 않았다(2026-09-05 계측).
67
+ */
68
+ e.stopPropagation();
69
+ e.preventDefault();
70
+ /*
71
+ * ── 포커스는 여기서 건드리지 않는다 ──────────────────────────────
72
+ *
73
+ * 팝업이 닫히면 오버레이가 **열릴 때 포커스가 있던 자리**로 돌려준다. 그런데 위에서
74
+ * preventDefault 를 하므로 브라우저가 이 칸으로 포커스를 옮겨 주지 않고, 그래서
75
+ * 팝업을 닫으면 엉뚱한 곳으로 간다(페이지 검색창이었다).
76
+ *
77
+ * 두 가지를 해 봤고 **둘 다 팝업이 안 열리게 만들었다.**
78
+ *
79
+ * focus-change 이벤트 몸통이 그 갱신에서 editTarget 을 지운다(shouldUpdate)
80
+ * ime-buffer 에 직접 focus 같은 결과
81
+ *
82
+ * 이 표는 포커스를 ime-buffer 라는 숨은 입력칸에 담아 두고, 편집 상태를 그 갱신
83
+ * 주기에 얹고 있다. 그 둘이 얽혀 있어서 밖에서 포커스를 옮기면 편집이 취소된다.
84
+ *
85
+ * 표의 포커스 모델을 손대야 풀리는 문제라 여기서 우회하지 않는다.
86
+ * [재봤음] 2026-09-05 · 두 방법 모두 팝업이 안 열림
87
+ */
88
+ this.dispatchEvent(new CustomEvent('start-edit', {
89
+ bubbles: true,
90
+ composed: true,
91
+ detail: { row: this.rowIndex, column: this.columnIndex }
92
+ }));
93
+ /*
94
+ * 칸 안에서 고치는 편집기는 여기서 끝난다 — 편집이 열리면 그 자리에 입력칸이 뜬다.
95
+ * 팝업 편집기는 한 걸음 더 필요하다.
96
+ */
97
+ if (this.editMark !== 'popup') {
98
+ return;
99
+ }
100
+ /*
101
+ * ── 편집을 열고, **그다음** 더블클릭을 편집기에 보낸다 ──────────────
102
+ *
103
+ * 사람이 더블클릭할 때의 순서를 계측해 보면 이렇다.
104
+ *
105
+ * startEditTarget 첫 클릭이 편집을 연다 → 편집기가 그려진다
106
+ * dblclick 그다음에 도착해 **이미 있는 편집기**가 받아 팝업을 연다
107
+ *
108
+ * 편집기마다 여는 메서드 이름이 달라서 부품이 직접 부를 수는 없다. 사람이 하던
109
+ * 동작을 같은 순서로 보내면 길이 한 벌로 남는다.
110
+ */
111
+ for (let i = 0; i < 5 && !this.editing; i++) {
112
+ await new Promise(resolve => requestAnimationFrame(resolve));
113
+ }
114
+ await this.updateComplete;
115
+ const editor = this.renderRoot.firstElementChild;
116
+ editor === null || editor === void 0 ? void 0 : editor.dispatchEvent(new MouseEvent('dblclick', { bubbles: true, composed: true }));
117
+ }}
118
+ >${this.editMark === 'popup' ? 'edit_square' : 'edit'}</md-icon
119
+ >`
120
+ : ''}
36
121
  `;
37
122
  }
38
123
  updated(changes) {
39
- var _a;
124
+ var _a, _b;
40
125
  if (changes.has('editing')) {
41
126
  if (this.editing) {
42
127
  this._onKeydownInEditingMode = ((e) => {
@@ -82,6 +167,8 @@ let DataGridField = class DataGridField extends LitElement {
82
167
  this.style.setProperty('--data-grid-field-justify-content', justify);
83
168
  this.style.setProperty('--data-grid-field-text-align', align);
84
169
  }
170
+ /* 표식은 값의 반대쪽 빈자리에 선다 — 위 :host([mark-start]) 주석 참고. */
171
+ this.toggleAttribute('mark-start', align === 'right');
85
172
  const { danger } = this.column;
86
173
  if (danger) {
87
174
  this.setAttribute('danger', '');
@@ -110,7 +197,15 @@ let DataGridField = class DataGridField extends LitElement {
110
197
  // 비황성화 여부 제거
111
198
  this.removeAttribute('disabled');
112
199
  }
113
- const emphasized = ((_a = this.column.record.classifier.call(this, this.record, this.rowIndex)) === null || _a === void 0 ? void 0 : _a.emphasized) || this.emphasized;
200
+ const emphasized =
201
+ /*
202
+ * classifier 는 없을 수 있다. 열 빌더가 채워 주지만 손으로 만든 열에는 없고, 그때 여기서
203
+ * 던진다 — 갱신 중에 나는 예외라 화면이 조용히 멈춘다.
204
+ *
205
+ * [재봤음] 2026-09-05 · wtr 에서 Cannot read properties of undefined (reading 'call')
206
+ * 팝업 표식을 넣어 칸이 전보다 자주 갱신되면서 드러났다. 원래 있던 자리다.
207
+ */
208
+ ((_b = (_a = this.column.record.classifier) === null || _a === void 0 ? void 0 : _a.call(this, this.record, this.rowIndex)) === null || _b === void 0 ? void 0 : _b.emphasized) || this.emphasized;
114
209
  if (!!emphasized) {
115
210
  this.setAttribute('emphasized-row', '');
116
211
  if (Symbol.iterator in Object(emphasized)) {
@@ -123,6 +218,79 @@ let DataGridField = class DataGridField extends LitElement {
123
218
  this.removeAttribute('emphasized-row');
124
219
  }
125
220
  }
221
+ /**
222
+ * 이 칸에 **팝업 표식**을 붙이나 — 팝업으로 고쳐지고, 지금 고칠 수 있을 때만.
223
+ *
224
+ * 고칠 수 있는지는 열이 함수로 정할 수도 있다(줄마다 다른 경우). 그때는 몸통이 편집을 열 때
225
+ * 하는 것과 같은 방식으로 물어본다 — 표식은 있는데 눌러도 안 열리면 그것이 더 나쁘다.
226
+ */
227
+ get opensPopup() {
228
+ return this.editMark === 'popup';
229
+ }
230
+ /**
231
+ * 이 칸을 **고칠 수 있나, 그리고 어디서 고치나.**
232
+ *
233
+ * popup 팝업이 열린다 — 네모와 펜
234
+ * inline 칸 안에서 고친다 — 펜
235
+ * null 못 고친다 — 표식 없음
236
+ *
237
+ * 표식이 있으면 고칠 수 있는 칸이고, 모양이 어디서 고치는지를 말한다.
238
+ *
239
+ * 고칠 수 있는지는 열이 함수로 정할 수도 있다(줄마다 다른 경우). 그때는 몸통이 편집을 열 때
240
+ * 하는 것과 같은 방식으로 물어본다 — 표식은 있는데 눌러도 안 열리면 그것이 더 나쁘다.
241
+ */
242
+ get editMark() {
243
+ var _a;
244
+ const column = this.column;
245
+ if (!column) {
246
+ return null;
247
+ }
248
+ /* 거터에는 붙이지 않는다 — 순번과 고르기 칸은 고치는 자리가 아니다. */
249
+ if (column.type === 'gutter') {
250
+ return null;
251
+ }
252
+ /*
253
+ * 표 오른쪽의 **빈 채움 열**에도 붙이지 않는다. 남는 자리를 메우는 20px 짜리 가짜 열이라
254
+ * 이름도 타입도 없다 — 고칠 것이 없는데 표식만 떴다.
255
+ *
256
+ * [재봤음] 2026-09-05 · 인원 화면 맨 끝 칸 · { type: '', name: '', label: false } · 폭 20
257
+ */
258
+ if (!column.name) {
259
+ return null;
260
+ }
261
+ let { editable } = column.record || {};
262
+ if (typeof editable === 'function') {
263
+ editable = editable.call(this, this.value, column, this.record, this.rowIndex, this);
264
+ }
265
+ /*
266
+ * **밝히지 않은 열에는 붙이지 않는다.**
267
+ *
268
+ * 표 전체가 고칠 수 있는지를 판정하는 자리가 같은 기준을 쓴다.
269
+ *
270
+ * [읽었음] data-grid.ts · editableGrid = columns.some(c => !!c.record?.editable)
271
+ *
272
+ * 참일 때만 고칠 수 있는 열로 본다. 선언하지 않은 열까지 세면 **고르기만 하는 표**에도
273
+ * 표식이 깔린다 — 참조 팝업의 목록이 그랬다(2026-09-05). 그 표의 칸은 고르는 자리이지
274
+ * 고치는 자리가 아니다.
275
+ */
276
+ if (!editable) {
277
+ return null;
278
+ }
279
+ /*
280
+ * 편집기를 **함수로 직접 준 열**은 타입으로 판정할 수 없다. 그리기를 다른 렌더러로 하려고
281
+ * 타입을 바꾼 열이 그렇다 — 관계를 여럿 다는 칸이 `ops-list` 로 그려지면서 편집은 참조
282
+ * 팝업으로 한다(ops-master 의 reference-list).
283
+ *
284
+ * 그런 열은 `record.opensPopup` 으로 스스로 밝힌다. 밝히지 않으면 타입으로 본다.
285
+ *
286
+ * [재봤음] 2026-09-05 · 보유 자격 · 타입 ops-list · 팝업으로 열리는데 표식이 inline 이었다
287
+ */
288
+ const declared = (_a = column.record) === null || _a === void 0 ? void 0 : _a.opensPopup;
289
+ if (typeof declared === 'boolean') {
290
+ return declared ? 'popup' : 'inline';
291
+ }
292
+ return editorOpensPopup(column.type) ? 'popup' : 'inline';
293
+ }
126
294
  get editableOnClick() {
127
295
  const renderer = this.renderRoot.firstElementChild;
128
296
  //@ts-ignore
@@ -150,6 +318,145 @@ DataGridField.styles = [
150
318
  box-sizing: border-box;
151
319
  }
152
320
 
321
+ /*
322
+ * ── 팝업으로 고치는 칸에는 **표식이 있다** ──────────────────────────────
323
+ *
324
+ * 그런 칸은 글자 칸과 똑같이 생겼다. 열어 보기 전에는 고칠 수 있는지도, 어떻게 고치는지도
325
+ * 모른다 — 「실제 더블클릭을 하기 전에는 모르는 막막함」(사용자, 2026-09-05).
326
+ *
327
+ * **마우스를 올렸을 때 보인다.** 늘 보이게 두었더니 한 화면에 54개가 깔려 표가 시끄러웠다
328
+ * (2026-09-05 · 사용자 판단). 값을 읽으러 온 눈에 장치가 줄마다 끼어든다.
329
+ *
330
+ * **손가락 화면에서는 늘 보인다.** 거기엔 올리는 동작이 없어서 감추면 길이 아예 없어진다.
331
+ * 그 화면들은 표가 성기므로 늘 보여도 시끄럽지 않다.
332
+ *
333
+ * ── 네모와 펜을 쓴다 ─────────────────────────────────────────────────
334
+ * 처음에 삼각형(arrow_drop_down)을 썼다. 참조 칸만 생각한 선택이었다 — 「목록에서 고른다」.
335
+ *
336
+ * 그런데 이 표식은 **팝업으로 여는 편집기 전부**에 붙는다. JSON · 코드 · 권한 편집기도
337
+ * 그렇고, 거기서 삼각형은 거짓말이다. 고를 목록이 없다.
338
+ *
339
+ * ── 모양이 **어디서 고치는지**를 말한다 ────────────────────────────────
340
+ *
341
+ * edit 칸 안에서 고친다
342
+ * edit_square 팝업이 열린다
343
+ *
344
+ * 표식이 있으면 고칠 수 있는 칸이고, 없으면 못 고치는 칸이다. **읽기 전용에는 붙지 않는다.**
345
+ *
346
+ * 처음에 삼각형(arrow_drop_down)을 썼다 — 참조 칸만 생각한 선택이었다. 이 표식은 JSON ·
347
+ * 코드 · 권한 편집기에도 붙고, 거기서 삼각형은 고를 목록이 있다는 거짓말이 된다.
348
+ *
349
+ * 누를 자리는 22px 이다. 33px 줄에서 더블클릭을 겨누는 것보다 한 번 누르는 편이 쉽다.
350
+ */
351
+ /* 마우스 화면 — 칸에 올렸을 때만. 자리는 늘 잡아 두므로 글자가 밀리지 않는다. */
352
+ @media (pointer: fine) {
353
+ [popup-hint] {
354
+ opacity: 0;
355
+ transition: opacity 0.12s ease;
356
+ }
357
+
358
+ :host(:hover) [popup-hint] {
359
+ opacity: 1;
360
+ }
361
+ }
362
+
363
+ /*
364
+ * 손가락 화면 — **고른 칸에만 보인다.**
365
+ *
366
+ * 올리는 동작이 없으니 호버로는 아무것도 못 보여 준다. 그렇다고 늘 보이게 두면 좁은
367
+ * 화면이 표식으로 뒤덮인다 — 시끄러우면 정작 필요한 표식도 안 보인다.
368
+ *
369
+ * 고른 칸 하나에만 두면 둘 다 풀린다. 두드려 고르는 것이 그 화면의 첫 동작이므로,
370
+ * 고르는 순간 「이 칸은 이렇게 고친다」가 나타난다.
371
+ */
372
+ @media (pointer: coarse) {
373
+ [popup-hint] {
374
+ display: none;
375
+ }
376
+
377
+ :host([focused]) [popup-hint] {
378
+ display: flex;
379
+ }
380
+ }
381
+
382
+ /*
383
+ * ── 표식은 늘 **칸의 끝**에 있다. 정렬을 따라가지 않는다 (2026-09-05) ──────
384
+ *
385
+ * 찾는 데 드는 값이 커진다 정렬은 열마다 다르다(이름은 왼쪽, 수량은 오른쪽). 표식이
386
+ * 그것을 따라가면 자리가 열마다 달라져 눈이 매번 찾아야 한다.
387
+ * 끝에 고정하면 규칙이 하나다 — 「손잡이는 칸 끝에 있다」
388
+ * 남의 칸처럼 보인다 오른쪽 정렬 칸의 값 **앞**에 표식을 두면 왼쪽 칸의 값 뒤에
389
+ * 붙어 보인다. 어느 칸의 손잡이인지 흐려진다
390
+ *
391
+ * ── **값이 없는 쪽**에 붙는다 ──────────────────────────────────────
392
+ * 왼쪽으로 붙인 칸의 빈자리는 오른쪽이고, 오른쪽으로 붙인 칸(수량 · 금액)의 빈자리는
393
+ * 왼쪽이다. 정렬의 반대쪽에 두면 표식이 값 위에 얹히는 일 자체가 없어진다.
394
+ *
395
+ * 자리를 비워 두는 방법도 해 봤다 — 칸의 오른쪽을 24px 늘 비우는 것. 되돌렸다.
396
+ * **자료가 먼저다**(사용자, 2026-09-05): 마우스를 올렸을 때만 보이는 안내 때문에 고칠 수
397
+ * 있는 열 전부가 영구히 좁아지는 것은 값을 잘못 매긴 거래다.
398
+ *
399
+ * 가운데 정렬 칸은 양쪽이 다 비므로 끝에 둔다.
400
+ */
401
+ [popup-hint] {
402
+ position: absolute;
403
+ inset-inline-end: 2px;
404
+ top: 50%;
405
+ transform: translateY(-50%);
406
+
407
+ --md-icon-size: 16px;
408
+ font-size: 16px;
409
+ width: 16px;
410
+ height: 16px;
411
+ padding: 3px;
412
+ border-radius: 50%;
413
+
414
+ /*
415
+ * ── 채우지 않는다 (2026-09-05) ──────────────────────────────────
416
+ * md-icon 이 물려주는 값은 FILL 1 이라 글리프가 통짜로 나온다. 두 가지가 어긋난다.
417
+ *
418
+ * 뜻 채운 글리프는 「지금 그 상태다」를 말한다(고른 것 · 켠 것). 이 표식이 말하는
419
+ * 것은 상태가 아니라 **누르면 할 수 있는 일**이다. 그건 윤곽선의 몫이다
420
+ * 밀도 표는 글자밭이다. 같은 16px 에서 채운 글리프는 잉크가 몇 곱절이라 값보다
421
+ * 먼저 눈에 든다. 안내가 자료보다 진해서는 안 된다
422
+ *
423
+ * edit_square 는 특히 그렇다 — 채우면 네모가 덩어리가 되어 「종이 위의 연필」이라는
424
+ * 그림이 뭉개진다. 진하기는 색으로 조절한다(아래 :hover).
425
+ */
426
+ /*
427
+ * 굵기는 한 단 올린다. 16px 에서 400 짜리 윤곽선은 획이 배경에 묻는다 — 채움을 뺀 만큼
428
+ * 획으로 되받는다. 잉크가 늘지 않으면서 형태는 또렷해진다.
429
+ */
430
+ font-variation-settings:
431
+ 'FILL' 0,
432
+ 'wght' 500,
433
+ 'opsz' 20;
434
+
435
+ /*
436
+ * ── 값 **위에 그려지되, 값보다 앞서지 않는다** ──────────────────
437
+ * 자리가 겹치면 표식이 위에 얹힌다. 그래도 이것은 거들어 주는 것이지 읽을 것이 아니다 —
438
+ * 본문 색으로 두었더니 값과 같은 무게가 되어 눈이 먼저 표식으로 갔다(사용자, 2026-09-05).
439
+ * 한 단 낮춰 라벨 색으로 둔다. 손이 표식 위에 오면 그때 본문 색이 된다.
440
+ */
441
+ color: var(--ui-muted, #57676f);
442
+ cursor: pointer;
443
+
444
+ transition:
445
+ background-color 0.12s ease,
446
+ color 0.12s ease;
447
+ }
448
+
449
+ /* 오른쪽으로 붙인 칸 — 값이 오른쪽 끝에 있으므로 표식은 왼쪽의 빈자리로 간다. */
450
+ :host([mark-start]) [popup-hint] {
451
+ inset-inline-end: auto;
452
+ inset-inline-start: 2px;
453
+ }
454
+
455
+ [popup-hint]:hover {
456
+ color: var(--ui-ink, #17272f);
457
+ background-color: var(--ui-hover, color-mix(in srgb, currentColor 12%, transparent));
458
+ }
459
+
153
460
  :host([multiline]) {
154
461
  white-space: pre-line;
155
462
  }
@@ -304,12 +611,12 @@ DataGridField.styles = [
304
611
  }
305
612
 
306
613
  :host > button.icon:hover {
307
- background-color: var(--ui-hover, rgba(0, 0, 0, 0.06));
614
+ background-color: var(--ui-hover, color-mix(in srgb, currentColor 12%, transparent));
308
615
  color: var(--ui-ink, #17272f);
309
616
  }
310
617
 
311
618
  :host > button.icon:active {
312
- background-color: var(--ui-active, rgba(0, 0, 0, 0.12));
619
+ background-color: var(--ui-active, color-mix(in srgb, currentColor 22%, transparent));
313
620
  }
314
621
 
315
622
  /* 키보드로 온 초점만 테를 보인다 — 눌러서 온 초점에는 보이지 않는다. */
@@ -1 +1 @@
1
- {"version":3,"file":"data-grid-field.js","sourceRoot":"","sources":["../../../src/data-grid/data-grid-field.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, PropertyValues, TemplateResult } from 'lit'\n// import { guard } from 'lit/directives/guard.js'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { TooltipReactiveController } from '@operato/utils'\n\nimport { ZERO_COLUMN } from '../configure/zero-config.js'\nimport { ColumnConfig, GristRecord } from '../types.js'\n\nconst DEFAULT_TEXT_ALIGN = 'left'\n\n@customElement('ox-grid-field')\nexport class DataGridField extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n\n align-items: center;\n justify-content: var(--data-grid-field-justify-content, flex-start);\n position: relative;\n\n white-space: nowrap;\n background-color: var(--grid-record-background-color);\n padding: var(--grid-record-padding);\n border: 1px solid transparent;\n border-width: 1px 0;\n border-bottom: var(--grid-record-border-bottom);\n\n font-size: var(--grid-record-wide-fontsize);\n min-height: 30px;\n box-sizing: border-box;\n }\n\n :host([multiline]) {\n white-space: pre-line;\n }\n\n /*\n * ── 칸이 쓸 수 있는 낱말 (2026-08-31) ──────────────────────────────────\n * 렌더러가 그린 것은 이 부품의 shadow DOM 안에 들어간다. 그래서 **화면의 CSS 가 닿지 않는다.**\n * 두 줄 칸이나 상태 알약을 그리려던 화면들이 그 벽에 막혀, 값에 인라인 style 을 박거나 칸마다\n * 부품을 새로 만들었다.\n *\n * 목록이면 어디서나 같은 것을 필요로 하므로 여기서 준다. 이름은 뜻으로 짓는다 —\n * 「늦었다」가 아니라 「심각하다(crit)」다. 늦은 것은 이 표의 일이 아니고 화면의 일이다.\n *\n * .sub 둘째 줄. 「1,200 EA」 아래의 「BOLT-8」처럼 딸린 사실\n * .mono 고정폭. 번호·시각·수량처럼 자릿수를 견주는 값\n * .none 값이 없음. 빈 칸으로 두면 「아직 안 읽었다」와 구별되지 않는다\n * .pill 알약. good·warn·crit·info 로 뜻을 준다\n * .crit · .warn · .good 글자 강조\n *\n * 값은 --ui-* 토큰에서 오고 폴백을 둔다 — 테마가 없어도 무너지지 않는다.\n */\n :host([twoline]) {\n display: block;\n white-space: normal;\n line-height: 1.35;\n }\n\n .sub {\n display: block;\n margin-top: 2px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n }\n\n .mono {\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-variant-numeric: tabular-nums;\n }\n\n /*\n * 그 줄이 무엇인지 가리키는 값 — 작업지시 번호, 설비 이름처럼. 두 줄 칸의 첫 줄이 이것이고\n * 둘째 줄(.sub)이 딸린 사실이다. 굵기만 올린다. 고정폭은 쓰지 않는다 — 자릿수를 세로로 견줄\n * 값이 아니고, 같은 글자 수에도 폭이 넓어져 옆 칸을 밀어낸다.\n */\n .id {\n font-weight: 620;\n }\n\n .none {\n color: var(--ui-faint, #8a959c);\n }\n\n .crit {\n color: var(--ui-crit, #c93f30);\n font-weight: 640;\n }\n\n .warn {\n color: var(--ui-warn, #9f5e02);\n font-weight: 640;\n }\n\n .good {\n color: var(--ui-good, #297d39);\n font-weight: 640;\n }\n\n /*\n * 값과 표식을 한 줄에 담는 자리 — **간격과 높이를 여기서 잡는다.**\n *\n * 처음에는 :not(.pill) + .pill { margin-left } 로 띄우려 했는데 안 걸렸다. 렌더러가 값을\n * 텍스트 노드로 내므로 알약의 앞 형제가 **요소가 아니고**, CSS 형제 선택자는 텍스트 노드를 보지\n * 못한다. 알약이 첫 요소이기도 해서 :first-child 로도 가려지지 않는다(실측 확인).\n *\n * 높이도 같은 자리에서 잡는다. baseline 으로 두면 14px 글자와 11.5px 알약의 기준선을 맞추느라\n * 알약이 내려앉는다.\n */\n /* 값이 있지만 눈에 걸릴 것은 아닌 것 — 「보통」 같은 기본값. .none(값 없음)과 구별한다. */\n .quiet {\n color: var(--ui-muted, #57676f);\n }\n\n /*\n * 별 같은 작은 기호를 글자 크기로 맞춘다. md-icon 의 기본은 24px 이라 줄 높이를 밀어낸다.\n * 색은 물려받는다 — 개수가 크기를 말하고 색은 담는 쪽이 정한다.\n */\n .star {\n --md-icon-size: 15px;\n font-size: 15px;\n width: 15px;\n height: 15px;\n color: inherit;\n }\n\n /* 별처럼 여럿이 붙는 기호는 사이를 좁게 — 낱말 사이의 6px 은 여기서 너무 넓다. */\n .line:has(.star) {\n gap: 1px;\n }\n\n /*\n * 셀 안의 아이콘 단추 — 그 줄에 하는 동작(들어가기 · 열기)을 담는다.\n *\n * 셀의 클릭 경로를 타지 않고 스스로 받으려는 렌더러가 쓴다. 그 경로에는 동작이 견딜 수 없는 관문이\n * 둘 있다.\n *\n * data-grid-body-click-handler.ts if (this.editTarget) return\n * data-grid-body-click-handler.ts (e.target).closest(\"ox-grid-field\")\n *\n * 앞은 다른 칸이 편집 중이면 첫 누름을 먹고, 뒤는 셀 경계를 스치면 칸을 못 찾는다. 진짜 button 을\n * 그리면 둘을 다 피하고 키보드로도 닿는다.\n *\n * 이 규칙이 여기 있는 이유: 렌더러의 결과는 이 셀의 그림자 안에 그려지므로 **페이지의 스타일이\n * 닿지 않는다.** 셀 안의 어휘는 셀이 갖는다.\n *\n * host 자식으로 적은 이유: 이 파일 아래의 host 자식 button 규칙이 채운 단추를 그리고, 특이도가\n * 같으면 **뒤에 오는 쪽이 이긴다.** 그것 없이 두면 이 단추가 채운 단추의 모양으로 나왔다\n * (어두운 바탕 · 흰 글자, 실측 rgb(38, 46, 56)).\n *\n * 둘의 뜻이 다르다 — 채운 단추는 **저지르는 동작**, 이 단추는 **옮겨 가기**다. 그래서 평소에는\n * 흐리고 배경이 없다.\n *\n * 배경은 **칸을 채우지 않고 동그라미로** 든다. 칸을 다 채우면 「이 셀이 골라졌다」로 읽힌다 —\n * 표에서 채워진 사각형은 선택의 뜻이다. 동그라미는 단추의 뜻이다.\n */\n :host > button.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n flex: none;\n /*\n * 누를 자리는 26px 로 두되 **줄 높이는 밀지 않는다.**\n *\n * 이 단추가 든 칸 하나 때문에 표의 모든 줄이 46px 이 됐다(글자만 있는 줄은 38px). 스무 줄이면\n * 240px 을 잃는다. [재봤음] grid-template-rows: 46px 34px\n *\n * 그렇다고 26px 아래로 줄이면 손가락으로 못 누른다(가로세로 24px 이 최소다). 그래서 상자는 26px\n * 로 두고 위아래 3px 씩을 여백에서 되돌려, 자리는 20px 만 차지하게 한다 — 글자 한 줄과 같다.\n */\n margin-top: -3px;\n margin-bottom: -3px;\n border: none;\n border-radius: 50%;\n background: transparent;\n padding: 0;\n color: var(--ui-faint, #8a959c);\n cursor: pointer;\n transition:\n background-color 0.12s ease,\n color 0.12s ease;\n }\n\n :host > button.icon:hover {\n background-color: var(--ui-hover, rgba(0, 0, 0, 0.06));\n color: var(--ui-ink, #17272f);\n }\n\n :host > button.icon:active {\n background-color: var(--ui-active, rgba(0, 0, 0, 0.12));\n }\n\n /* 키보드로 온 초점만 테를 보인다 — 눌러서 온 초점에는 보이지 않는다. */\n :host > button.icon:focus-visible {\n outline: 2px solid var(--ui-accent, #00727e);\n outline-offset: -2px;\n }\n\n /*\n * 값을 함께 담는 단추 — 「4 ›」처럼 숫자와 화살이 나란히 선다.\n *\n * 아이콘만으로는 그 줄에 무엇이 있는지 말하지 못한다. 값이 뜻을 나르고 화살이 「누를 수 있다」를\n * 말한다. 그래서 동그라미가 아니라 알약 모양이다.\n */\n :host > button.icon.wide {\n width: auto;\n min-width: 44px;\n gap: 0;\n padding: 0 2px 0 7px;\n border-radius: 999px;\n font: inherit;\n font-size: var(--grid-record-wide-fontsize);\n font-variant-numeric: tabular-nums;\n }\n\n :host > button.icon.wide md-icon {\n --md-icon-size: 16px;\n font-size: 16px;\n width: 16px;\n height: 16px;\n }\n\n :host > button.icon md-icon {\n --md-icon-size: 18px;\n font-size: 18px;\n width: 18px;\n height: 18px;\n }\n\n .line {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n }\n\n /* 값이 길면 값이 줄고 표식은 그대로 남는다 — 표식이 먼저 잘리면 읽을 수 없다. */\n .line > :first-child {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* 표식 — 상태 같은 짧은 값에 두른다. 점이 색을 나르고 글자가 뜻을 나른다. */\n .pill {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 1px 8px;\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: 999px;\n font-size: var(--ui-t-small, 11.5px);\n font-weight: 400;\n color: var(--ui-muted, #57676f);\n white-space: nowrap;\n }\n\n /*\n * 색만으로 뜻을 전하지 않는다 — 알약 안의 글자가 함께 말한다. 점은 거드는 것이다.\n *\n * align-self: center 가 필요하다. 알약은 flex 이고 점은 그 안의 항목인데, 높이를 6px 로\n * 주어도 교차축에서 늘어나 **6×11.5 짜리 세로 타원**이 됐다(원이 아니라 길쭉했다).\n * .pill 의 align-items: center 는 부모 쪽 값이라 이 항목의 stretch 를 막지 못한다.\n */\n .pill .dot {\n align-self: center;\n width: 6px;\n height: 6px;\n /*\n * 이 파일 아래에 span, pre 의 min-height: 1em 이 있다. 값이 빈 칸에서도 줄 높이를\n * 유지하려는 규칙인데, 점도 span 이라 거기 걸려 **높이가 1em(11.5px)까지 늘어났다** —\n * 그래서 6px 을 줘도 6×11.5 세로 타원이 됐다. 여기서 되돌린다.\n */\n min-height: 0;\n border-radius: 50%;\n background: currentColor;\n flex: none;\n }\n\n .pill.good {\n color: var(--ui-good, #297d39);\n border-color: color-mix(in srgb, var(--ui-good, #297d39) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.info {\n color: var(--ui-accent, #00727e);\n border-color: color-mix(in srgb, var(--ui-accent, #00727e) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.warn {\n color: var(--ui-warn, #9f5e02);\n border-color: color-mix(in srgb, var(--ui-warn, #9f5e02) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.crit {\n color: var(--ui-crit, #c93f30);\n border-color: color-mix(in srgb, var(--ui-crit, #c93f30) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.quiet {\n color: var(--ui-faint, #8a959c);\n }\n\n :host([gutter]) {\n padding: var(--grid-gutter-padding);\n text-align: center;\n }\n\n :host([gutter]) * {\n cursor: default;\n overflow: hidden;\n }\n\n :host([editing]) {\n border-top: var(--grid-record-editing-border);\n border-bottom: var(--grid-record-editing-border);\n }\n\n /*\n * 초점이 갔다고 **줄을 나누는 선을 바꾸지 않는다.**\n *\n * 전에는 여기서 --grid-record-border-bottom 을 초점 표식 값으로 덮어썼다. 그래서 표식을\n * 감추려고 그 토큰을 none 이나 투명으로 두면 **그 셀만 줄 선이 사라졌다.** 한 토큰이 「초점\n * 표식」과 「줄을 나누는 선」 두 가지를 겸하고 있었던 것이다.\n *\n * 초점 표식은 outline 이 그린다(data-grid-body-style.ts). 줄 선은 줄 선으로 남는다.\n */\n\n :host([dirty])::after {\n content: '';\n position: absolute;\n right: 0;\n top: 0;\n\n width: 0px;\n height: 0px;\n border-top: 9px solid red;\n border-left: 9px solid transparent;\n }\n\n span,\n pre {\n display: block;\n text-overflow: ellipsis;\n overflow: hidden;\n min-height: 1em;\n }\n\n :host([multiline]) span,\n :host([multiline]) pre {\n text-overflow: initial;\n overflow: visible;\n white-space: pre-line;\n word-break: break-word;\n overflow-wrap: anywhere;\n }\n\n * {\n margin: 0;\n text-align: var(--data-grid-field-text-align);\n }\n\n *[center] {\n flex: none;\n margin: 0 auto;\n }\n\n :host > button {\n display: flex;\n gap: var(--spacing-small);\n border: 0;\n align-items: center;\n padding: var(--spacing-small);\n\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-primary);\n border-radius: var(--data-card-item-btn-border-radius, var(--md-sys-shape-corner-tiny));\n\n font-size: 0.8rem;\n line-height: 0.8rem;\n\n md-icon {\n border: 0;\n padding: 0;\n background-color: unset;\n }\n }\n\n :host([danger]) > button {\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-error);\n border-color: var(--md-sys-color-error);\n }\n\n :host([danger]) > button:hover {\n color: var(--md-sys-color-on-secondary);\n background-color: var(--md-sys-color-secondary);\n border-color: var(--md-sys-color-secondary);\n }\n `\n ]\n\n @property({ type: String, attribute: true }) align = DEFAULT_TEXT_ALIGN\n @property({ type: Object }) record: GristRecord = {}\n @property({ type: Object }) column: ColumnConfig = ZERO_COLUMN\n @property({ type: Number }) rowIndex = 0\n @property({ type: Number }) columnIndex = 0\n @property({ type: Boolean }) editing = false\n @property({ type: Boolean, reflect: true }) multiline = false\n @property({ type: Object }) value = {}\n @property({ type: String }) valueWith: string | null = null\n @property({ attribute: false }) emphasized: any = false\n @property({ type: String, attribute: true }) fixed?: string\n @property({ type: String }) type?: string\n @property({ type: Boolean }) isWorking: boolean = false\n\n /* check in tree의 변화에 대응하기 위함임. 만일, record control 오브젝트로 통일되면 대체될 것임. */\n @property({ type: String }) checked?: string\n\n private _editCancelled?: boolean\n private _onFieldChange: (e: Event) => void = e => {}\n private _onKeydownInEditingMode: (e: KeyboardEvent) => void = e => {}\n\n private tooltipController?: TooltipReactiveController = new TooltipReactiveController(this)\n\n render(): TemplateResult {\n if (!this.column) {\n return html``\n }\n\n var { value, column, record, rowIndex } = this\n var { renderer, editor } = column.record\n\n return html`\n ${this.editing\n ? editor?.call(this, value, column, record, rowIndex, this)\n : renderer?.call(this, value, column, record, rowIndex, this)}\n `\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('editing')) {\n if (this.editing) {\n this._onKeydownInEditingMode = ((e: KeyboardEvent) => {\n if (e.key === 'Esc' || e.key === 'Escape') {\n /* 편집 취소 */\n this._editCancelled = true\n }\n }).bind(this)\n\n this._onFieldChange = ((e: Event) => {\n this._editCancelled && e.stopPropagation()\n }).bind(this)\n\n delete this._editCancelled\n this.addEventListener('field-change', this._onFieldChange)\n this.addEventListener('keydown', this._onKeydownInEditingMode)\n } else {\n this.removeEventListener('field-change', this._onFieldChange)\n this.removeEventListener('keydown', this._onKeydownInEditingMode)\n }\n }\n\n if (changes.has('column')) {\n /*\n * 정렬은 **넣을 때만이 아니라 되돌릴 때도** 손대야 한다.\n *\n * 이 요소는 재사용된다. 전에는 기본값이 아닐 때만 인라인 값을 넣고 지우지 않았다. 그래서 한 번\n * 오른쪽 정렬 칸을 그린 셀이 다음에 왼쪽 정렬 칸을 그려도 오른쪽에 남았다.\n *\n * [재봤음] 품질 화면 「판단」 칸에서 머리글은 x=380(왼쪽)인데 값은 x=452(flex-end)였다. 숫자 칸을\n * 오른쪽으로 바꾼 뒤 드러났다 — 오른쪽 정렬 칸이 드물던 동안은 눈에 안 띄었다. 2026-09-02\n */\n var align = this.column.record.align || DEFAULT_TEXT_ALIGN\n\n if (align == DEFAULT_TEXT_ALIGN) {\n this.style.removeProperty('--data-grid-field-justify-content')\n this.style.removeProperty('--data-grid-field-text-align')\n } else {\n let justify = 'center'\n switch (align) {\n case 'right':\n justify = 'flex-end'\n break\n }\n this.style.setProperty('--data-grid-field-justify-content', justify)\n this.style.setProperty('--data-grid-field-text-align', align)\n }\n\n const { danger } = this.column\n if (danger) {\n this.setAttribute('danger', '')\n } else {\n this.removeAttribute('danger')\n }\n }\n\n if (changes.has('fixed')) {\n if (this.fixed) {\n this.style.left = this.fixed + 'px'\n } else {\n this.style.left = 'unset'\n }\n }\n\n let disabled = this.column.record.disabled || false\n if (typeof disabled === 'function') {\n disabled = disabled.call(this, this.record[this.column.name], this.column, this.record, this.rowIndex, this)\n }\n\n if (!!disabled) {\n // 비황성화 여부 표시\n this.setAttribute('disabled', '')\n } else {\n // 비황성화 여부 제거\n this.removeAttribute('disabled')\n }\n\n const emphasized =\n this.column.record.classifier.call(this, this.record, this.rowIndex)?.emphasized || this.emphasized\n\n if (!!emphasized) {\n this.setAttribute('emphasized-row', '')\n\n if (Symbol.iterator in Object(emphasized)) {\n const [backgroundColor, foregroundColor] = emphasized as string[]\n backgroundColor && this.style.setProperty('--grid-record-emphasized-background-color', backgroundColor)\n foregroundColor && this.style.setProperty('--grid-record-emphasized-color', foregroundColor)\n }\n } else {\n this.removeAttribute('emphasized-row')\n }\n }\n\n get editableOnClick() {\n const renderer = this.renderRoot.firstElementChild as HTMLElement\n //@ts-ignore\n return renderer && 'editableOnClick' in renderer ? renderer.editableOnClick : true\n }\n}\n"]}
1
+ {"version":3,"file":"data-grid-field.js","sourceRoot":"","sources":["../../../src/data-grid/data-grid-field.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport i18next from 'i18next'\nimport { css, html, LitElement, PropertyValues, TemplateResult } from 'lit'\n// import { guard } from 'lit/directives/guard.js'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { TooltipReactiveController } from '@operato/utils'\n\nimport { ZERO_COLUMN } from '../configure/zero-config.js'\nimport { editorOpensPopup } from '../editors/registry.js'\nimport { ColumnConfig, GristRecord } from '../types.js'\n\nconst DEFAULT_TEXT_ALIGN = 'left'\n\n@customElement('ox-grid-field')\nexport class DataGridField extends LitElement {\n static styles = [\n css`\n :host {\n display: flex;\n\n align-items: center;\n justify-content: var(--data-grid-field-justify-content, flex-start);\n position: relative;\n\n white-space: nowrap;\n background-color: var(--grid-record-background-color);\n padding: var(--grid-record-padding);\n border: 1px solid transparent;\n border-width: 1px 0;\n border-bottom: var(--grid-record-border-bottom);\n\n font-size: var(--grid-record-wide-fontsize);\n min-height: 30px;\n box-sizing: border-box;\n }\n\n /*\n * ── 팝업으로 고치는 칸에는 **표식이 있다** ──────────────────────────────\n *\n * 그런 칸은 글자 칸과 똑같이 생겼다. 열어 보기 전에는 고칠 수 있는지도, 어떻게 고치는지도\n * 모른다 — 「실제 더블클릭을 하기 전에는 모르는 막막함」(사용자, 2026-09-05).\n *\n * **마우스를 올렸을 때 보인다.** 늘 보이게 두었더니 한 화면에 54개가 깔려 표가 시끄러웠다\n * (2026-09-05 · 사용자 판단). 값을 읽으러 온 눈에 장치가 줄마다 끼어든다.\n *\n * **손가락 화면에서는 늘 보인다.** 거기엔 올리는 동작이 없어서 감추면 길이 아예 없어진다.\n * 그 화면들은 표가 성기므로 늘 보여도 시끄럽지 않다.\n *\n * ── 네모와 펜을 쓴다 ─────────────────────────────────────────────────\n * 처음에 삼각형(arrow_drop_down)을 썼다. 참조 칸만 생각한 선택이었다 — 「목록에서 고른다」.\n *\n * 그런데 이 표식은 **팝업으로 여는 편집기 전부**에 붙는다. JSON · 코드 · 권한 편집기도\n * 그렇고, 거기서 삼각형은 거짓말이다. 고를 목록이 없다.\n *\n * ── 모양이 **어디서 고치는지**를 말한다 ────────────────────────────────\n *\n * edit 칸 안에서 고친다\n * edit_square 팝업이 열린다\n *\n * 표식이 있으면 고칠 수 있는 칸이고, 없으면 못 고치는 칸이다. **읽기 전용에는 붙지 않는다.**\n *\n * 처음에 삼각형(arrow_drop_down)을 썼다 — 참조 칸만 생각한 선택이었다. 이 표식은 JSON ·\n * 코드 · 권한 편집기에도 붙고, 거기서 삼각형은 고를 목록이 있다는 거짓말이 된다.\n *\n * 누를 자리는 22px 이다. 33px 줄에서 더블클릭을 겨누는 것보다 한 번 누르는 편이 쉽다.\n */\n /* 마우스 화면 — 칸에 올렸을 때만. 자리는 늘 잡아 두므로 글자가 밀리지 않는다. */\n @media (pointer: fine) {\n [popup-hint] {\n opacity: 0;\n transition: opacity 0.12s ease;\n }\n\n :host(:hover) [popup-hint] {\n opacity: 1;\n }\n }\n\n /*\n * 손가락 화면 — **고른 칸에만 보인다.**\n *\n * 올리는 동작이 없으니 호버로는 아무것도 못 보여 준다. 그렇다고 늘 보이게 두면 좁은\n * 화면이 표식으로 뒤덮인다 — 시끄러우면 정작 필요한 표식도 안 보인다.\n *\n * 고른 칸 하나에만 두면 둘 다 풀린다. 두드려 고르는 것이 그 화면의 첫 동작이므로,\n * 고르는 순간 「이 칸은 이렇게 고친다」가 나타난다.\n */\n @media (pointer: coarse) {\n [popup-hint] {\n display: none;\n }\n\n :host([focused]) [popup-hint] {\n display: flex;\n }\n }\n\n /*\n * ── 표식은 늘 **칸의 끝**에 있다. 정렬을 따라가지 않는다 (2026-09-05) ──────\n *\n * 찾는 데 드는 값이 커진다 정렬은 열마다 다르다(이름은 왼쪽, 수량은 오른쪽). 표식이\n * 그것을 따라가면 자리가 열마다 달라져 눈이 매번 찾아야 한다.\n * 끝에 고정하면 규칙이 하나다 — 「손잡이는 칸 끝에 있다」\n * 남의 칸처럼 보인다 오른쪽 정렬 칸의 값 **앞**에 표식을 두면 왼쪽 칸의 값 뒤에\n * 붙어 보인다. 어느 칸의 손잡이인지 흐려진다\n *\n * ── **값이 없는 쪽**에 붙는다 ──────────────────────────────────────\n * 왼쪽으로 붙인 칸의 빈자리는 오른쪽이고, 오른쪽으로 붙인 칸(수량 · 금액)의 빈자리는\n * 왼쪽이다. 정렬의 반대쪽에 두면 표식이 값 위에 얹히는 일 자체가 없어진다.\n *\n * 자리를 비워 두는 방법도 해 봤다 — 칸의 오른쪽을 24px 늘 비우는 것. 되돌렸다.\n * **자료가 먼저다**(사용자, 2026-09-05): 마우스를 올렸을 때만 보이는 안내 때문에 고칠 수\n * 있는 열 전부가 영구히 좁아지는 것은 값을 잘못 매긴 거래다.\n *\n * 가운데 정렬 칸은 양쪽이 다 비므로 끝에 둔다.\n */\n [popup-hint] {\n position: absolute;\n inset-inline-end: 2px;\n top: 50%;\n transform: translateY(-50%);\n\n --md-icon-size: 16px;\n font-size: 16px;\n width: 16px;\n height: 16px;\n padding: 3px;\n border-radius: 50%;\n\n /*\n * ── 채우지 않는다 (2026-09-05) ──────────────────────────────────\n * md-icon 이 물려주는 값은 FILL 1 이라 글리프가 통짜로 나온다. 두 가지가 어긋난다.\n *\n * 뜻 채운 글리프는 「지금 그 상태다」를 말한다(고른 것 · 켠 것). 이 표식이 말하는\n * 것은 상태가 아니라 **누르면 할 수 있는 일**이다. 그건 윤곽선의 몫이다\n * 밀도 표는 글자밭이다. 같은 16px 에서 채운 글리프는 잉크가 몇 곱절이라 값보다\n * 먼저 눈에 든다. 안내가 자료보다 진해서는 안 된다\n *\n * edit_square 는 특히 그렇다 — 채우면 네모가 덩어리가 되어 「종이 위의 연필」이라는\n * 그림이 뭉개진다. 진하기는 색으로 조절한다(아래 :hover).\n */\n /*\n * 굵기는 한 단 올린다. 16px 에서 400 짜리 윤곽선은 획이 배경에 묻는다 — 채움을 뺀 만큼\n * 획으로 되받는다. 잉크가 늘지 않으면서 형태는 또렷해진다.\n */\n font-variation-settings:\n 'FILL' 0,\n 'wght' 500,\n 'opsz' 20;\n\n /*\n * ── 값 **위에 그려지되, 값보다 앞서지 않는다** ──────────────────\n * 자리가 겹치면 표식이 위에 얹힌다. 그래도 이것은 거들어 주는 것이지 읽을 것이 아니다 —\n * 본문 색으로 두었더니 값과 같은 무게가 되어 눈이 먼저 표식으로 갔다(사용자, 2026-09-05).\n * 한 단 낮춰 라벨 색으로 둔다. 손이 표식 위에 오면 그때 본문 색이 된다.\n */\n color: var(--ui-muted, #57676f);\n cursor: pointer;\n\n transition:\n background-color 0.12s ease,\n color 0.12s ease;\n }\n\n /* 오른쪽으로 붙인 칸 — 값이 오른쪽 끝에 있으므로 표식은 왼쪽의 빈자리로 간다. */\n :host([mark-start]) [popup-hint] {\n inset-inline-end: auto;\n inset-inline-start: 2px;\n }\n\n [popup-hint]:hover {\n color: var(--ui-ink, #17272f);\n background-color: var(--ui-hover, color-mix(in srgb, currentColor 12%, transparent));\n }\n\n :host([multiline]) {\n white-space: pre-line;\n }\n\n /*\n * ── 칸이 쓸 수 있는 낱말 (2026-08-31) ──────────────────────────────────\n * 렌더러가 그린 것은 이 부품의 shadow DOM 안에 들어간다. 그래서 **화면의 CSS 가 닿지 않는다.**\n * 두 줄 칸이나 상태 알약을 그리려던 화면들이 그 벽에 막혀, 값에 인라인 style 을 박거나 칸마다\n * 부품을 새로 만들었다.\n *\n * 목록이면 어디서나 같은 것을 필요로 하므로 여기서 준다. 이름은 뜻으로 짓는다 —\n * 「늦었다」가 아니라 「심각하다(crit)」다. 늦은 것은 이 표의 일이 아니고 화면의 일이다.\n *\n * .sub 둘째 줄. 「1,200 EA」 아래의 「BOLT-8」처럼 딸린 사실\n * .mono 고정폭. 번호·시각·수량처럼 자릿수를 견주는 값\n * .none 값이 없음. 빈 칸으로 두면 「아직 안 읽었다」와 구별되지 않는다\n * .pill 알약. good·warn·crit·info 로 뜻을 준다\n * .crit · .warn · .good 글자 강조\n *\n * 값은 --ui-* 토큰에서 오고 폴백을 둔다 — 테마가 없어도 무너지지 않는다.\n */\n :host([twoline]) {\n display: block;\n white-space: normal;\n line-height: 1.35;\n }\n\n .sub {\n display: block;\n margin-top: 2px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n }\n\n .mono {\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-variant-numeric: tabular-nums;\n }\n\n /*\n * 그 줄이 무엇인지 가리키는 값 — 작업지시 번호, 설비 이름처럼. 두 줄 칸의 첫 줄이 이것이고\n * 둘째 줄(.sub)이 딸린 사실이다. 굵기만 올린다. 고정폭은 쓰지 않는다 — 자릿수를 세로로 견줄\n * 값이 아니고, 같은 글자 수에도 폭이 넓어져 옆 칸을 밀어낸다.\n */\n .id {\n font-weight: 620;\n }\n\n .none {\n color: var(--ui-faint, #8a959c);\n }\n\n .crit {\n color: var(--ui-crit, #c93f30);\n font-weight: 640;\n }\n\n .warn {\n color: var(--ui-warn, #9f5e02);\n font-weight: 640;\n }\n\n .good {\n color: var(--ui-good, #297d39);\n font-weight: 640;\n }\n\n /*\n * 값과 표식을 한 줄에 담는 자리 — **간격과 높이를 여기서 잡는다.**\n *\n * 처음에는 :not(.pill) + .pill { margin-left } 로 띄우려 했는데 안 걸렸다. 렌더러가 값을\n * 텍스트 노드로 내므로 알약의 앞 형제가 **요소가 아니고**, CSS 형제 선택자는 텍스트 노드를 보지\n * 못한다. 알약이 첫 요소이기도 해서 :first-child 로도 가려지지 않는다(실측 확인).\n *\n * 높이도 같은 자리에서 잡는다. baseline 으로 두면 14px 글자와 11.5px 알약의 기준선을 맞추느라\n * 알약이 내려앉는다.\n */\n /* 값이 있지만 눈에 걸릴 것은 아닌 것 — 「보통」 같은 기본값. .none(값 없음)과 구별한다. */\n .quiet {\n color: var(--ui-muted, #57676f);\n }\n\n /*\n * 별 같은 작은 기호를 글자 크기로 맞춘다. md-icon 의 기본은 24px 이라 줄 높이를 밀어낸다.\n * 색은 물려받는다 — 개수가 크기를 말하고 색은 담는 쪽이 정한다.\n */\n .star {\n --md-icon-size: 15px;\n font-size: 15px;\n width: 15px;\n height: 15px;\n color: inherit;\n }\n\n /* 별처럼 여럿이 붙는 기호는 사이를 좁게 — 낱말 사이의 6px 은 여기서 너무 넓다. */\n .line:has(.star) {\n gap: 1px;\n }\n\n /*\n * 셀 안의 아이콘 단추 — 그 줄에 하는 동작(들어가기 · 열기)을 담는다.\n *\n * 셀의 클릭 경로를 타지 않고 스스로 받으려는 렌더러가 쓴다. 그 경로에는 동작이 견딜 수 없는 관문이\n * 둘 있다.\n *\n * data-grid-body-click-handler.ts if (this.editTarget) return\n * data-grid-body-click-handler.ts (e.target).closest(\"ox-grid-field\")\n *\n * 앞은 다른 칸이 편집 중이면 첫 누름을 먹고, 뒤는 셀 경계를 스치면 칸을 못 찾는다. 진짜 button 을\n * 그리면 둘을 다 피하고 키보드로도 닿는다.\n *\n * 이 규칙이 여기 있는 이유: 렌더러의 결과는 이 셀의 그림자 안에 그려지므로 **페이지의 스타일이\n * 닿지 않는다.** 셀 안의 어휘는 셀이 갖는다.\n *\n * host 자식으로 적은 이유: 이 파일 아래의 host 자식 button 규칙이 채운 단추를 그리고, 특이도가\n * 같으면 **뒤에 오는 쪽이 이긴다.** 그것 없이 두면 이 단추가 채운 단추의 모양으로 나왔다\n * (어두운 바탕 · 흰 글자, 실측 rgb(38, 46, 56)).\n *\n * 둘의 뜻이 다르다 — 채운 단추는 **저지르는 동작**, 이 단추는 **옮겨 가기**다. 그래서 평소에는\n * 흐리고 배경이 없다.\n *\n * 배경은 **칸을 채우지 않고 동그라미로** 든다. 칸을 다 채우면 「이 셀이 골라졌다」로 읽힌다 —\n * 표에서 채워진 사각형은 선택의 뜻이다. 동그라미는 단추의 뜻이다.\n */\n :host > button.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n flex: none;\n /*\n * 누를 자리는 26px 로 두되 **줄 높이는 밀지 않는다.**\n *\n * 이 단추가 든 칸 하나 때문에 표의 모든 줄이 46px 이 됐다(글자만 있는 줄은 38px). 스무 줄이면\n * 240px 을 잃는다. [재봤음] grid-template-rows: 46px 34px\n *\n * 그렇다고 26px 아래로 줄이면 손가락으로 못 누른다(가로세로 24px 이 최소다). 그래서 상자는 26px\n * 로 두고 위아래 3px 씩을 여백에서 되돌려, 자리는 20px 만 차지하게 한다 — 글자 한 줄과 같다.\n */\n margin-top: -3px;\n margin-bottom: -3px;\n border: none;\n border-radius: 50%;\n background: transparent;\n padding: 0;\n color: var(--ui-faint, #8a959c);\n cursor: pointer;\n transition:\n background-color 0.12s ease,\n color 0.12s ease;\n }\n\n :host > button.icon:hover {\n background-color: var(--ui-hover, color-mix(in srgb, currentColor 12%, transparent));\n color: var(--ui-ink, #17272f);\n }\n\n :host > button.icon:active {\n background-color: var(--ui-active, color-mix(in srgb, currentColor 22%, transparent));\n }\n\n /* 키보드로 온 초점만 테를 보인다 — 눌러서 온 초점에는 보이지 않는다. */\n :host > button.icon:focus-visible {\n outline: 2px solid var(--ui-accent, #00727e);\n outline-offset: -2px;\n }\n\n /*\n * 값을 함께 담는 단추 — 「4 ›」처럼 숫자와 화살이 나란히 선다.\n *\n * 아이콘만으로는 그 줄에 무엇이 있는지 말하지 못한다. 값이 뜻을 나르고 화살이 「누를 수 있다」를\n * 말한다. 그래서 동그라미가 아니라 알약 모양이다.\n */\n :host > button.icon.wide {\n width: auto;\n min-width: 44px;\n gap: 0;\n padding: 0 2px 0 7px;\n border-radius: 999px;\n font: inherit;\n font-size: var(--grid-record-wide-fontsize);\n font-variant-numeric: tabular-nums;\n }\n\n :host > button.icon.wide md-icon {\n --md-icon-size: 16px;\n font-size: 16px;\n width: 16px;\n height: 16px;\n }\n\n :host > button.icon md-icon {\n --md-icon-size: 18px;\n font-size: 18px;\n width: 18px;\n height: 18px;\n }\n\n .line {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n }\n\n /* 값이 길면 값이 줄고 표식은 그대로 남는다 — 표식이 먼저 잘리면 읽을 수 없다. */\n .line > :first-child {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* 표식 — 상태 같은 짧은 값에 두른다. 점이 색을 나르고 글자가 뜻을 나른다. */\n .pill {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 1px 8px;\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: 999px;\n font-size: var(--ui-t-small, 11.5px);\n font-weight: 400;\n color: var(--ui-muted, #57676f);\n white-space: nowrap;\n }\n\n /*\n * 색만으로 뜻을 전하지 않는다 — 알약 안의 글자가 함께 말한다. 점은 거드는 것이다.\n *\n * align-self: center 가 필요하다. 알약은 flex 이고 점은 그 안의 항목인데, 높이를 6px 로\n * 주어도 교차축에서 늘어나 **6×11.5 짜리 세로 타원**이 됐다(원이 아니라 길쭉했다).\n * .pill 의 align-items: center 는 부모 쪽 값이라 이 항목의 stretch 를 막지 못한다.\n */\n .pill .dot {\n align-self: center;\n width: 6px;\n height: 6px;\n /*\n * 이 파일 아래에 span, pre 의 min-height: 1em 이 있다. 값이 빈 칸에서도 줄 높이를\n * 유지하려는 규칙인데, 점도 span 이라 거기 걸려 **높이가 1em(11.5px)까지 늘어났다** —\n * 그래서 6px 을 줘도 6×11.5 세로 타원이 됐다. 여기서 되돌린다.\n */\n min-height: 0;\n border-radius: 50%;\n background: currentColor;\n flex: none;\n }\n\n .pill.good {\n color: var(--ui-good, #297d39);\n border-color: color-mix(in srgb, var(--ui-good, #297d39) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.info {\n color: var(--ui-accent, #00727e);\n border-color: color-mix(in srgb, var(--ui-accent, #00727e) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.warn {\n color: var(--ui-warn, #9f5e02);\n border-color: color-mix(in srgb, var(--ui-warn, #9f5e02) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.crit {\n color: var(--ui-crit, #c93f30);\n border-color: color-mix(in srgb, var(--ui-crit, #c93f30) 40%, transparent);\n font-weight: 400;\n }\n\n .pill.quiet {\n color: var(--ui-faint, #8a959c);\n }\n\n :host([gutter]) {\n padding: var(--grid-gutter-padding);\n text-align: center;\n }\n\n :host([gutter]) * {\n cursor: default;\n overflow: hidden;\n }\n\n :host([editing]) {\n border-top: var(--grid-record-editing-border);\n border-bottom: var(--grid-record-editing-border);\n }\n\n /*\n * 초점이 갔다고 **줄을 나누는 선을 바꾸지 않는다.**\n *\n * 전에는 여기서 --grid-record-border-bottom 을 초점 표식 값으로 덮어썼다. 그래서 표식을\n * 감추려고 그 토큰을 none 이나 투명으로 두면 **그 셀만 줄 선이 사라졌다.** 한 토큰이 「초점\n * 표식」과 「줄을 나누는 선」 두 가지를 겸하고 있었던 것이다.\n *\n * 초점 표식은 outline 이 그린다(data-grid-body-style.ts). 줄 선은 줄 선으로 남는다.\n */\n\n :host([dirty])::after {\n content: '';\n position: absolute;\n right: 0;\n top: 0;\n\n width: 0px;\n height: 0px;\n border-top: 9px solid red;\n border-left: 9px solid transparent;\n }\n\n span,\n pre {\n display: block;\n text-overflow: ellipsis;\n overflow: hidden;\n min-height: 1em;\n }\n\n :host([multiline]) span,\n :host([multiline]) pre {\n text-overflow: initial;\n overflow: visible;\n white-space: pre-line;\n word-break: break-word;\n overflow-wrap: anywhere;\n }\n\n * {\n margin: 0;\n text-align: var(--data-grid-field-text-align);\n }\n\n *[center] {\n flex: none;\n margin: 0 auto;\n }\n\n :host > button {\n display: flex;\n gap: var(--spacing-small);\n border: 0;\n align-items: center;\n padding: var(--spacing-small);\n\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-primary);\n border-radius: var(--data-card-item-btn-border-radius, var(--md-sys-shape-corner-tiny));\n\n font-size: 0.8rem;\n line-height: 0.8rem;\n\n md-icon {\n border: 0;\n padding: 0;\n background-color: unset;\n }\n }\n\n :host([danger]) > button {\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-error);\n border-color: var(--md-sys-color-error);\n }\n\n :host([danger]) > button:hover {\n color: var(--md-sys-color-on-secondary);\n background-color: var(--md-sys-color-secondary);\n border-color: var(--md-sys-color-secondary);\n }\n `\n ]\n\n @property({ type: String, attribute: true }) align = DEFAULT_TEXT_ALIGN\n @property({ type: Object }) record: GristRecord = {}\n @property({ type: Object }) column: ColumnConfig = ZERO_COLUMN\n @property({ type: Number }) rowIndex = 0\n @property({ type: Number }) columnIndex = 0\n @property({ type: Boolean }) editing = false\n @property({ type: Boolean, reflect: true }) multiline = false\n @property({ type: Object }) value = {}\n @property({ type: String }) valueWith: string | null = null\n @property({ attribute: false }) emphasized: any = false\n @property({ type: String, attribute: true }) fixed?: string\n @property({ type: String }) type?: string\n @property({ type: Boolean }) isWorking: boolean = false\n\n /* check in tree의 변화에 대응하기 위함임. 만일, record control 오브젝트로 통일되면 대체될 것임. */\n @property({ type: String }) checked?: string\n\n private _editCancelled?: boolean\n private _onFieldChange: (e: Event) => void = e => {}\n private _onKeydownInEditingMode: (e: KeyboardEvent) => void = e => {}\n\n private tooltipController?: TooltipReactiveController = new TooltipReactiveController(this)\n\n render(): TemplateResult {\n if (!this.column) {\n return html``\n }\n\n var { value, column, record, rowIndex } = this\n var { renderer, editor } = column.record\n\n return html`\n ${this.editing\n ? editor?.call(this, value, column, record, rowIndex, this)\n : renderer?.call(this, value, column, record, rowIndex, this)}\n <!--\n **편집 중에도 표식을 남긴다.**\n\n 칸을 누르면 편집 상태가 되고, 그때는 렌더러 대신 편집기가 그려진다. 표식을 !editing 으로\n 묶어 두었더니 **칸에 들어간 순간 사라졌다** — 들어간 사람에게 여는 길이 없어지는 셈이고,\n 다시 더블클릭이나 Enter 를 짐작해야 한다. 그 짐작을 없애려고 만든 표식이다.\n\n 팝업 편집기에만 붙으므로(opensPopup) 글자를 치는 편집기를 가릴 일은 없다.\n -->\n <!--\n ── 칸 안에서 고치는 중에는 표식을 걷는다 ────────────────────────────────\n 입력칸이 이미 그 자리에 있다. 펜을 겹쳐 두면 글자를 가리고, 고치는 중인 사람에게\n 「고칠 수 있다」를 다시 말할 이유도 없다.\n\n 팝업 쪽은 남긴다 — 편집 상태에 들어가도 팝업은 아직 안 열렸고, 그 표식이 여는 길이다.\n -->\n ${this.editMark && !(this.editing && this.editMark === 'inline')\n ? html`<md-icon\n popup-hint\n ?inline=${this.editMark === 'inline'}\n title=${i18next.t('text.grid-open-editor', { defaultValue: 'press to edit' })}\n @pointerdown=${async (e: Event) => {\n /*\n * **click 이 아니라 pointerdown 이다.**\n *\n * 이 표에서는 click 이벤트가 나지 않는다 — 몸통이 pointer 단계에서 기본 동작을\n * 막는다. document 에 capture 로 click 을 걸어 두고 표식을 눌러 봐도 한 번도\n * 울리지 않았다(2026-09-05 계측).\n */\n e.stopPropagation()\n e.preventDefault()\n\n /*\n * ── 포커스는 여기서 건드리지 않는다 ──────────────────────────────\n *\n * 팝업이 닫히면 오버레이가 **열릴 때 포커스가 있던 자리**로 돌려준다. 그런데 위에서\n * preventDefault 를 하므로 브라우저가 이 칸으로 포커스를 옮겨 주지 않고, 그래서\n * 팝업을 닫으면 엉뚱한 곳으로 간다(페이지 검색창이었다).\n *\n * 두 가지를 해 봤고 **둘 다 팝업이 안 열리게 만들었다.**\n *\n * focus-change 이벤트 몸통이 그 갱신에서 editTarget 을 지운다(shouldUpdate)\n * ime-buffer 에 직접 focus 같은 결과\n *\n * 이 표는 포커스를 ime-buffer 라는 숨은 입력칸에 담아 두고, 편집 상태를 그 갱신\n * 주기에 얹고 있다. 그 둘이 얽혀 있어서 밖에서 포커스를 옮기면 편집이 취소된다.\n *\n * 표의 포커스 모델을 손대야 풀리는 문제라 여기서 우회하지 않는다.\n * [재봤음] 2026-09-05 · 두 방법 모두 팝업이 안 열림\n */\n\n this.dispatchEvent(\n new CustomEvent('start-edit', {\n bubbles: true,\n composed: true,\n detail: { row: this.rowIndex, column: this.columnIndex }\n })\n )\n\n /*\n * 칸 안에서 고치는 편집기는 여기서 끝난다 — 편집이 열리면 그 자리에 입력칸이 뜬다.\n * 팝업 편집기는 한 걸음 더 필요하다.\n */\n if (this.editMark !== 'popup') {\n return\n }\n\n /*\n * ── 편집을 열고, **그다음** 더블클릭을 편집기에 보낸다 ──────────────\n *\n * 사람이 더블클릭할 때의 순서를 계측해 보면 이렇다.\n *\n * startEditTarget 첫 클릭이 편집을 연다 → 편집기가 그려진다\n * dblclick 그다음에 도착해 **이미 있는 편집기**가 받아 팝업을 연다\n *\n * 편집기마다 여는 메서드 이름이 달라서 부품이 직접 부를 수는 없다. 사람이 하던\n * 동작을 같은 순서로 보내면 길이 한 벌로 남는다.\n */\n for (let i = 0; i < 5 && !this.editing; i++) {\n await new Promise(resolve => requestAnimationFrame(resolve))\n }\n\n await this.updateComplete\n\n const editor = this.renderRoot.firstElementChild as HTMLElement | null\n editor?.dispatchEvent(new MouseEvent('dblclick', { bubbles: true, composed: true }))\n }}\n >${this.editMark === 'popup' ? 'edit_square' : 'edit'}</md-icon\n >`\n : ''}\n `\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('editing')) {\n if (this.editing) {\n this._onKeydownInEditingMode = ((e: KeyboardEvent) => {\n if (e.key === 'Esc' || e.key === 'Escape') {\n /* 편집 취소 */\n this._editCancelled = true\n }\n }).bind(this)\n\n this._onFieldChange = ((e: Event) => {\n this._editCancelled && e.stopPropagation()\n }).bind(this)\n\n delete this._editCancelled\n this.addEventListener('field-change', this._onFieldChange)\n this.addEventListener('keydown', this._onKeydownInEditingMode)\n } else {\n this.removeEventListener('field-change', this._onFieldChange)\n this.removeEventListener('keydown', this._onKeydownInEditingMode)\n }\n }\n\n if (changes.has('column')) {\n /*\n * 정렬은 **넣을 때만이 아니라 되돌릴 때도** 손대야 한다.\n *\n * 이 요소는 재사용된다. 전에는 기본값이 아닐 때만 인라인 값을 넣고 지우지 않았다. 그래서 한 번\n * 오른쪽 정렬 칸을 그린 셀이 다음에 왼쪽 정렬 칸을 그려도 오른쪽에 남았다.\n *\n * [재봤음] 품질 화면 「판단」 칸에서 머리글은 x=380(왼쪽)인데 값은 x=452(flex-end)였다. 숫자 칸을\n * 오른쪽으로 바꾼 뒤 드러났다 — 오른쪽 정렬 칸이 드물던 동안은 눈에 안 띄었다. 2026-09-02\n */\n var align = this.column.record.align || DEFAULT_TEXT_ALIGN\n\n if (align == DEFAULT_TEXT_ALIGN) {\n this.style.removeProperty('--data-grid-field-justify-content')\n this.style.removeProperty('--data-grid-field-text-align')\n } else {\n let justify = 'center'\n switch (align) {\n case 'right':\n justify = 'flex-end'\n break\n }\n this.style.setProperty('--data-grid-field-justify-content', justify)\n this.style.setProperty('--data-grid-field-text-align', align)\n }\n\n /* 표식은 값의 반대쪽 빈자리에 선다 — 위 :host([mark-start]) 주석 참고. */\n this.toggleAttribute('mark-start', align === 'right')\n\n const { danger } = this.column\n if (danger) {\n this.setAttribute('danger', '')\n } else {\n this.removeAttribute('danger')\n }\n }\n\n if (changes.has('fixed')) {\n if (this.fixed) {\n this.style.left = this.fixed + 'px'\n } else {\n this.style.left = 'unset'\n }\n }\n\n let disabled = this.column.record.disabled || false\n if (typeof disabled === 'function') {\n disabled = disabled.call(this, this.record[this.column.name], this.column, this.record, this.rowIndex, this)\n }\n\n if (!!disabled) {\n // 비황성화 여부 표시\n this.setAttribute('disabled', '')\n } else {\n // 비황성화 여부 제거\n this.removeAttribute('disabled')\n }\n\n const emphasized =\n /*\n * classifier 는 없을 수 있다. 열 빌더가 채워 주지만 손으로 만든 열에는 없고, 그때 여기서\n * 던진다 — 갱신 중에 나는 예외라 화면이 조용히 멈춘다.\n *\n * [재봤음] 2026-09-05 · wtr 에서 Cannot read properties of undefined (reading 'call')\n * 팝업 표식을 넣어 칸이 전보다 자주 갱신되면서 드러났다. 원래 있던 자리다.\n */\n this.column.record.classifier?.call(this, this.record, this.rowIndex)?.emphasized || this.emphasized\n\n if (!!emphasized) {\n this.setAttribute('emphasized-row', '')\n\n if (Symbol.iterator in Object(emphasized)) {\n const [backgroundColor, foregroundColor] = emphasized as string[]\n backgroundColor && this.style.setProperty('--grid-record-emphasized-background-color', backgroundColor)\n foregroundColor && this.style.setProperty('--grid-record-emphasized-color', foregroundColor)\n }\n } else {\n this.removeAttribute('emphasized-row')\n }\n }\n\n /**\n * 이 칸에 **팝업 표식**을 붙이나 — 팝업으로 고쳐지고, 지금 고칠 수 있을 때만.\n *\n * 고칠 수 있는지는 열이 함수로 정할 수도 있다(줄마다 다른 경우). 그때는 몸통이 편집을 열 때\n * 하는 것과 같은 방식으로 물어본다 — 표식은 있는데 눌러도 안 열리면 그것이 더 나쁘다.\n */\n get opensPopup(): boolean {\n return this.editMark === 'popup'\n }\n\n /**\n * 이 칸을 **고칠 수 있나, 그리고 어디서 고치나.**\n *\n * popup 팝업이 열린다 — 네모와 펜\n * inline 칸 안에서 고친다 — 펜\n * null 못 고친다 — 표식 없음\n *\n * 표식이 있으면 고칠 수 있는 칸이고, 모양이 어디서 고치는지를 말한다.\n *\n * 고칠 수 있는지는 열이 함수로 정할 수도 있다(줄마다 다른 경우). 그때는 몸통이 편집을 열 때\n * 하는 것과 같은 방식으로 물어본다 — 표식은 있는데 눌러도 안 열리면 그것이 더 나쁘다.\n */\n get editMark(): 'popup' | 'inline' | null {\n const column = this.column\n\n if (!column) {\n return null\n }\n\n /* 거터에는 붙이지 않는다 — 순번과 고르기 칸은 고치는 자리가 아니다. */\n if (column.type === 'gutter') {\n return null\n }\n\n /*\n * 표 오른쪽의 **빈 채움 열**에도 붙이지 않는다. 남는 자리를 메우는 20px 짜리 가짜 열이라\n * 이름도 타입도 없다 — 고칠 것이 없는데 표식만 떴다.\n *\n * [재봤음] 2026-09-05 · 인원 화면 맨 끝 칸 · { type: '', name: '', label: false } · 폭 20\n */\n if (!column.name) {\n return null\n }\n\n let { editable } = column.record || ({} as any)\n\n if (typeof editable === 'function') {\n editable = editable.call(this, this.value, column, this.record, this.rowIndex, this)\n }\n\n /*\n * **밝히지 않은 열에는 붙이지 않는다.**\n *\n * 표 전체가 고칠 수 있는지를 판정하는 자리가 같은 기준을 쓴다.\n *\n * [읽었음] data-grid.ts · editableGrid = columns.some(c => !!c.record?.editable)\n *\n * 참일 때만 고칠 수 있는 열로 본다. 선언하지 않은 열까지 세면 **고르기만 하는 표**에도\n * 표식이 깔린다 — 참조 팝업의 목록이 그랬다(2026-09-05). 그 표의 칸은 고르는 자리이지\n * 고치는 자리가 아니다.\n */\n if (!editable) {\n return null\n }\n\n /*\n * 편집기를 **함수로 직접 준 열**은 타입으로 판정할 수 없다. 그리기를 다른 렌더러로 하려고\n * 타입을 바꾼 열이 그렇다 — 관계를 여럿 다는 칸이 `ops-list` 로 그려지면서 편집은 참조\n * 팝업으로 한다(ops-master 의 reference-list).\n *\n * 그런 열은 `record.opensPopup` 으로 스스로 밝힌다. 밝히지 않으면 타입으로 본다.\n *\n * [재봤음] 2026-09-05 · 보유 자격 · 타입 ops-list · 팝업으로 열리는데 표식이 inline 이었다\n */\n const declared = (column.record as any)?.opensPopup\n\n if (typeof declared === 'boolean') {\n return declared ? 'popup' : 'inline'\n }\n\n return editorOpensPopup(column.type) ? 'popup' : 'inline'\n }\n\n get editableOnClick() {\n const renderer = this.renderRoot.firstElementChild as HTMLElement\n //@ts-ignore\n return renderer && 'editableOnClick' in renderer ? renderer.editableOnClick : true\n }\n}\n"]}
@@ -1,7 +1,6 @@
1
1
  import { __decorate } from "tslib";
2
2
  import '@operato/popup/ox-popup.js';
3
3
  import '@material/web/icon/icon.js';
4
- import i18next from 'i18next';
5
4
  import { css, html, LitElement, nothing } from 'lit';
6
5
  import { customElement, property, state } from 'lit/decorators.js';
7
6
  import { ifDefined } from 'lit/directives/if-defined.js';
@@ -74,7 +73,7 @@ let DataGridHeader = class DataGridHeader extends LitElement {
74
73
  `;
75
74
  }
76
75
  renderHeaderColumn({ column, clazz, start, size, align, index, group }) {
77
- var _a, _b, _c, _d;
76
+ var _a, _b, _c;
78
77
  if (column.hidden) {
79
78
  return html `${nothing}`;
80
79
  }
@@ -119,7 +118,6 @@ let DataGridHeader = class DataGridHeader extends LitElement {
119
118
  >${this._renderHeader(column)}
120
119
  </span>
121
120
 
122
- ${((_d = column.record) === null || _d === void 0 ? void 0 : _d.editable) ? html `<md-icon editable-mark title=${i18next.t('text.grid-column-editable', { defaultValue: 'editable' })}>edit</md-icon>` : nothing}
123
121
  ${!group && column.sortable
124
122
  ? html `
125
123
  <span sorter @click=${(e) => this._changeSort(column)}>
@@ -575,23 +573,13 @@ DataGridHeader.styles = [
575
573
  }
576
574
 
577
575
  /*
578
- * 머리의 표식은 **알림이지 버튼이 아니다.** 머리에서 읽어야 것은 열 이름이다.
576
+ * ── 머리에는 「고칠 있다」 표식을 두지 않는다 (2026-09-05) ────────
577
+ * 칸마다 표식이 생긴 뒤로 머리의 연필은 같은 말을 두 번 하는 자리가 됐다. 게다가 머리의
578
+ * 것은 **틀린 말을 할 수 있다** — editable 이 함수인 열에서 머리는 참·거짓을 가리지 못해
579
+ * 늘 「고칠 수 있다」고 하고, 실제로는 한 줄도 못 고치는 표가 나온다. 칸의 표식은 그 함수를
580
+ * 줄마다 불러 보고 정한다.
579
581
  *
580
- * ── 연필이 지저분해 보이던 이유 ──────────────────────────────────
581
- * 크기가 글자만 했고(1.2em), 획이 본문과 같은 굵기였고, 열 이름 바로 옆에 붙어 있었다.
582
- * 편집용 표에서는 거의 모든 열이 고칠 수 있어서 그 셋이 열 수만큼 반복됐다.
583
- *
584
- * 셋을 각각 낮춘다.
585
- *
586
- * 크기 14px — 정렬 표식과 같게 맞춘다. 전에는 연필 12px, 화살표 18px 로 따로 놀았다
587
- * 획 가변 글꼴의 굵기 축을 200 으로. 채운 글리프가 아니라 가는 선이 된다
588
- * 자리 정렬 표식과 한 무리로 오른쪽에 모은다. 이름과 표식 사이가 갈린다
589
- *
590
- * 글리프는 연필(edit)이다. text_fields · edit_square 와 나란히 놓고 견주었고 사용자가 골랐다 —
591
- * 「고칠 수 있다」는 뜻이 가장 바로 오는 것이 연필이다.
592
- *
593
- * 연필이 지저분해 보였던 것은 글리프 탓이 아니라 크기(15px)와 획 굵기 탓이었다. 14px 에
594
- * 굵기 200 으로 두면 가는 선이 되어 이름을 방해하지 않는다.
582
+ * 머리는 이름·정렬·거르기의 자리로 두고, 고치기는 고치는 자리에서 알린다.
595
583
  *
596
584
  * ── 정렬 표식 ──────────────────────────────────────────────────────
597
585
  * 정렬할 수 있다는 것은 아주 흐리게, 지금 정렬 중인 것만 한 단계 진하게 둔다.
@@ -599,21 +587,21 @@ DataGridHeader.styles = [
599
587
  * 색은 토큰으로 받되 currentColor 를 섞은 폴백을 둔다 — 테마가 값을 정하지 않아도 흐려지고
600
588
  * 라이트·다크 어느 쪽에서도 배경 대비가 유지된다.
601
589
  */
602
- md-icon[editable-mark] {
603
- align-self: center;
604
- font-size: var(--grid-header-mark-size, 14px);
605
- /* 가변 글꼴의 굵기·명암 축. 정적 글꼴이면 조용히 무시된다. */
606
- font-variation-settings: 'wght' 200, 'GRAD' -25, 'opsz' 20;
607
- color: var(--grid-header-editable-color, color-mix(in srgb, currentColor 34%, transparent));
608
- }
609
-
610
590
  md-icon[sortable-mark] {
611
591
  color: var(--grid-header-sortable-color, color-mix(in srgb, currentColor 18%, transparent));
612
592
  }
613
593
 
594
+ /*
595
+ * 지금 정렬 중이라는 표시는 **상태**다 — 흐리게 두면 무엇으로 정렬됐는지 화면이 말하지 않는
596
+ * 것과 같다. 52% 로는 어두운 화면에서 2.86:1 이었다(실측 2026-09-05). 60% 면 밝은 화면
597
+ * 4.7 · 어두운 화면 5.4 로 UI 요소 기준(3:1)을 넘는다.
598
+ *
599
+ * 「정렬할 수 있다」(sortable-mark)는 그 옆에서 계속 흐리게 둔다. 그것은 상태가 아니라
600
+ * 곁들이는 안내이고, 머리글 자체가 눌리는 자리라 길은 따로 있다.
601
+ */
614
602
  span[sorter] md-icon:not([sortable-mark]),
615
603
  span[sorter] sub {
616
- color: var(--grid-header-sorted-color, color-mix(in srgb, currentColor 52%, transparent));
604
+ color: var(--grid-header-sorted-color, color-mix(in srgb, currentColor 60%, transparent));
617
605
  }
618
606
 
619
607
  span[filter] > md-icon {