@operato/data-grist 10.8.1 → 10.10.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 (74) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/src/configure/column-builder.js +17 -2
  3. package/dist/src/configure/column-builder.js.map +1 -1
  4. package/dist/src/configure/config-builder.js +1 -1
  5. package/dist/src/configure/config-builder.js.map +1 -1
  6. package/dist/src/data-card/data-card.d.ts +6 -0
  7. package/dist/src/data-card/data-card.js +12 -3
  8. package/dist/src/data-card/data-card.js.map +1 -1
  9. package/dist/src/data-grid/data-grid-body-style.js +12 -1
  10. package/dist/src/data-grid/data-grid-body-style.js.map +1 -1
  11. package/dist/src/data-grid/data-grid-body.d.ts +31 -0
  12. package/dist/src/data-grid/data-grid-body.js +141 -39
  13. package/dist/src/data-grid/data-grid-body.js.map +1 -1
  14. package/dist/src/data-grid/data-grid-field.d.ts +21 -0
  15. package/dist/src/data-grid/data-grid-field.js +326 -19
  16. package/dist/src/data-grid/data-grid-field.js.map +1 -1
  17. package/dist/src/data-grid/data-grid-footer.js +1 -1
  18. package/dist/src/data-grid/data-grid-footer.js.map +1 -1
  19. package/dist/src/data-grid/data-grid-header.d.ts +12 -1
  20. package/dist/src/data-grid/data-grid-header.js +97 -36
  21. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  22. package/dist/src/data-grid/data-grid.d.ts +15 -1
  23. package/dist/src/data-grid/data-grid.js +83 -40
  24. package/dist/src/data-grid/data-grid.js.map +1 -1
  25. package/dist/src/data-grist.d.ts +32 -0
  26. package/dist/src/data-grist.js +86 -10
  27. package/dist/src/data-grist.js.map +1 -1
  28. package/dist/src/data-list/data-list.d.ts +6 -0
  29. package/dist/src/data-list/data-list.js +12 -3
  30. package/dist/src/data-list/data-list.js.map +1 -1
  31. package/dist/src/editors/ox-grist-editor-json5.d.ts +1 -0
  32. package/dist/src/editors/ox-grist-editor-json5.js +2 -0
  33. package/dist/src/editors/ox-grist-editor-json5.js.map +1 -1
  34. package/dist/src/editors/ox-grist-editor.d.ts +27 -0
  35. package/dist/src/editors/ox-grist-editor.js +61 -1
  36. package/dist/src/editors/ox-grist-editor.js.map +1 -1
  37. package/dist/src/editors/registry.d.ts +10 -0
  38. package/dist/src/editors/registry.js +16 -0
  39. package/dist/src/editors/registry.js.map +1 -1
  40. package/dist/src/filters/filter-input.js +15 -0
  41. package/dist/src/filters/filter-input.js.map +1 -1
  42. package/dist/src/filters/filter-range-date.js +9 -0
  43. package/dist/src/filters/filter-range-date.js.map +1 -1
  44. package/dist/src/filters/filter-range-number.js +9 -0
  45. package/dist/src/filters/filter-range-number.js.map +1 -1
  46. package/dist/src/gutters/gutter-row-selector.js +16 -0
  47. package/dist/src/gutters/gutter-row-selector.js.map +1 -1
  48. package/dist/src/index.d.ts +1 -0
  49. package/dist/src/index.js +1 -0
  50. package/dist/src/index.js.map +1 -1
  51. package/dist/src/renderers/ox-grist-renderer-boolean.js +8 -0
  52. package/dist/src/renderers/ox-grist-renderer-boolean.js.map +1 -1
  53. package/dist/src/types.d.ts +11 -1
  54. package/dist/src/types.js.map +1 -1
  55. package/dist/src/utils/column-text.d.ts +18 -0
  56. package/dist/src/utils/column-text.js +37 -0
  57. package/dist/src/utils/column-text.js.map +1 -0
  58. package/dist/src/utils/index.d.ts +1 -0
  59. package/dist/src/utils/index.js +1 -0
  60. package/dist/src/utils/index.js.map +1 -1
  61. package/dist/test/accessible-name.test.d.ts +2 -0
  62. package/dist/test/accessible-name.test.js +324 -0
  63. package/dist/test/accessible-name.test.js.map +1 -0
  64. package/dist/test/row-hover-visible.test.js +28 -16
  65. package/dist/test/row-hover-visible.test.js.map +1 -1
  66. package/dist/tsconfig.tsbuildinfo +1 -1
  67. package/package.json +7 -6
  68. package/test/accessible-name.test.ts +408 -0
  69. package/test/row-hover-visible.test.ts +32 -16
  70. package/translations/en.json +20 -10
  71. package/translations/ja.json +19 -9
  72. package/translations/ko.json +19 -10
  73. package/translations/ms.json +19 -9
  74. package/translations/zh.json +19 -9
package/CHANGELOG.md CHANGED
@@ -3,6 +3,50 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [10.10.0](https://github.com/hatiolab/operato/compare/v10.9.0...v10.10.0) (2026-09-06)
7
+
8
+
9
+ ### :memo: Documentation
10
+
11
+ * **webcomponents:** 주석의 지어낸 한국어를 고친다 ([2553211](https://github.com/hatiolab/operato/commit/255321196bcfc6dc2fce0a8b9806d1341a6a7ce3))
12
+ * 지어낸 말 「재보니·재봤음」을 걷는다 ([0b2a04d](https://github.com/hatiolab/operato/commit/0b2a04d874f2e6f3bc44be4d04f4b3eafde998ef))
13
+
14
+
15
+ ### :rocket: New Features
16
+
17
+ * **data-grist:** 표를 표라고 선언하고, 입력칸에 이름을 붙인다 ([e45922b](https://github.com/hatiolab/operato/commit/e45922b51c0bd38f220a618ddc387459a92bef20))
18
+
19
+
20
+
21
+ ## [10.9.0](https://github.com/hatiolab/operato/compare/v10.8.1...v10.9.0) (2026-09-05)
22
+
23
+
24
+ ### :sparkles: Styling
25
+
26
+ * **data-grist:** 빈 표의 말을 사람 말로 바꾼다 ([180b6b2](https://github.com/hatiolab/operato/commit/180b6b20aa8fdc5e5ea504f408bba2432c8cc23d))
27
+ * **data-grist:** 팝업 표식을 마우스 올렸을 때만 보인다 ([cf166b3](https://github.com/hatiolab/operato/commit/cf166b3e0115d29798960f6142e7c749f12cb692))
28
+
29
+
30
+ ### :rocket: New Features
31
+
32
+ * **data-grist, popup:** 표식은 값의 빈자리에, 팝업은 닫히면 제자리로 ([cdcd84c](https://github.com/hatiolab/operato/commit/cdcd84c53cdb12ba0664cc9d2bb4c8238e4ea9f4))
33
+ * **data-grist:** 고칠 수 있는 칸에 표식을 두고, 모양이 어디서 고치는지 말한다 ([561fd61](https://github.com/hatiolab/operato/commit/561fd610a842242cb4ceef940ea47b3c41a125c6))
34
+ * **data-grist:** 팝업으로 고치는 칸에 표식을 둔다 ([69fae8f](https://github.com/hatiolab/operato/commit/69fae8f92024bd59ac21e90b9b2837be95f222cf))
35
+
36
+
37
+ ### :bug: Bug Fix
38
+
39
+ * **context, data-grist:** 야간 모드에서 안 보이던 두 자리 ([a87a76f](https://github.com/hatiolab/operato/commit/a87a76f7499938de603397d7f7cceb58927ccffa))
40
+ * **data-grist:** classifier 가 없는 열에서 던지던 것을 막는다 ([37f689d](https://github.com/hatiolab/operato/commit/37f689dc54955cb796b5b64aeff83942c753b715))
41
+ * **data-grist:** 빈 목록 안내를 세 모드가 함께 쓰게 한다 ([8cd4975](https://github.com/hatiolab/operato/commit/8cd49751f14a2db1d02be56e1e311e8fa004691b))
42
+ * **data-grist:** 빈 목록 안내를 조회 후에만 낸다 ([70101fe](https://github.com/hatiolab/operato/commit/70101fe828dec91258ee62a153ac6915404d0ee2))
43
+ * **data-grist:** 빈 표가 「아직 없다」와 「걸린 것이 없다」를 갈라 말한다 ([2ccd3f5](https://github.com/hatiolab/operato/commit/2ccd3f5a97a20a7776a8569153a545c656f19c8b))
44
+ * **data-grist:** 숫자 칸의 편집기가 왼쪽으로 튀었다 — 정렬이 입력 요소에 안 닿았다 ([68a49f3](https://github.com/hatiolab/operato/commit/68a49f327ebafe38c557ac0cee594eeab5ee5a59))
45
+ * **data-grist:** 편집 중에도 팝업 표식을 남긴다 ([9c6fefa](https://github.com/hatiolab/operato/commit/9c6fefa03f49df912720d4420469b7b4711bda05)), closes [#262e38](https://github.com/hatiolab/operato/issues/262e38)
46
+ * **data-grist:** 표식을 고칠 수 있는 칸에만, 그리고 방해되지 않게 둔다 ([05ae962](https://github.com/hatiolab/operato/commit/05ae962e3035a202e75ab395090c8230166cb179))
47
+
48
+
49
+
6
50
  ### [10.8.1](https://github.com/hatiolab/operato/compare/v10.8.0...v10.8.1) (2026-09-04)
7
51
 
8
52
  **Note:** Version bump only for package @operato/data-grist
@@ -13,15 +13,27 @@ import { getRenderer } from '../renderers/index.js';
13
13
  */
14
14
  const RIGHT_ALIGNED_TYPES = ['integer', 'float', 'number'];
15
15
  export const buildColumn = (column) => {
16
- var _a, _b;
16
+ var _a, _b, _c;
17
17
  var compiled = { ...column };
18
18
  if (column.type == 'gutter') {
19
19
  compiled = generateGutterColumn(column);
20
20
  }
21
21
  var { name, header, record, handlers, label, filter } = compiled;
22
22
  /* header */
23
+ /*
24
+ * 이름을 글자로도 남긴다 — renderer 하나만 두면 이름이 여기서 사라진다.
25
+ *
26
+ * 설정이 header: '상태' 처럼 문자열로 들어와도 여기를 지나면 () => '상태' 한 개만 남는다.
27
+ * 그 함수가 돌려주는 것은 화면에 그릴 조각(대개 lit template)이라 되읽을 수 없다. 그래서 화면 밖에서
28
+ * 이름이 필요한 곳 — 스크린 리더가 읽을 이름, filter 입력칸의 이름 — 이 이름을 구할 데가 없었고,
29
+ * 결국 각자 다시 적어 두는 수밖에 없었다. 한 사실에 출처가 둘이 되면 header 를 고쳐도 나머지가
30
+ * 그대로 남는다.
31
+ *
32
+ * 그리는 것과 이름을 함께 남긴다. columnText() 가 이것을 읽는다.
33
+ */
23
34
  if (typeof header == 'string') {
24
35
  compiled.header = {
36
+ text: header,
25
37
  renderer: () => header
26
38
  };
27
39
  }
@@ -29,6 +41,7 @@ export const buildColumn = (column) => {
29
41
  const text = compiled.header.renderer;
30
42
  compiled.header = {
31
43
  ...compiled.header,
44
+ text,
32
45
  renderer: () => text
33
46
  };
34
47
  }
@@ -38,11 +51,13 @@ export const buildColumn = (column) => {
38
51
  switch (type) {
39
52
  case 'boolean':
40
53
  compiled.label = {
41
- renderer: ((_b = compiled.header) === null || _b === void 0 ? void 0 : _b.renderer) || (() => name)
54
+ text: ((_b = compiled.header) === null || _b === void 0 ? void 0 : _b.text) || name,
55
+ renderer: ((_c = compiled.header) === null || _c === void 0 ? void 0 : _c.renderer) || (() => name)
42
56
  };
43
57
  break;
44
58
  case 'string':
45
59
  compiled.label = {
60
+ text: label,
46
61
  renderer: () => label
47
62
  };
48
63
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"column-builder.js","sourceRoot":"","sources":["../../../src/configure/column-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import { FilterConfigObject, FilterOperator } from '../index.js'\nimport { NOOP } from '../const.js'\nimport { getEditor } from '../editors/index.js'\nimport { generateGutterColumn } from '../gutters/index.js'\nimport { getGristEventHandler } from '../handlers/index.js'\nimport { getRenderer } from '../renderers/index.js'\nimport { ColumnConfig } from '../types.js'\n\n/**\n * 값을 오른쪽에 붙이는 칸.\n *\n * 자릿수가 세로로 맞아야 값을 견줄 수 있다. 왼쪽으로 두면 24 와 1,200 이 시작점만 같고 끝이 어긋나서\n * 어느 쪽이 큰지 눈으로 읽히지 않는다.\n *\n * progress 는 뺀다 — 막대로 그리므로 정렬이 뜻을 갖지 않는다.\n */\nconst RIGHT_ALIGNED_TYPES = ['integer', 'float', 'number']\n\nexport const buildColumn = (column: any): ColumnConfig => {\n var compiled: ColumnConfig = { ...column }\n\n if (column.type == 'gutter') {\n compiled = generateGutterColumn(column)\n }\n\n var { name, header, record, handlers, label, filter } = compiled\n\n /* header */\n\n if (typeof header == 'string') {\n compiled.header = {\n renderer: () => header\n }\n }\n\n if (typeof compiled.header?.renderer == 'string') {\n const text = compiled.header.renderer\n compiled.header = {\n ...compiled.header,\n renderer: () => text\n }\n }\n\n /* label */\n if (label) {\n let type = typeof label\n switch (type) {\n case 'boolean':\n compiled.label = {\n renderer: compiled.header?.renderer || (() => name)\n }\n break\n case 'string':\n compiled.label = {\n renderer: () => label\n }\n break\n default:\n compiled.label = label\n }\n } else {\n compiled.label = false\n }\n\n /*\n * record\n */\n var { renderer: recordRenderer, editor, editable, classifier } = record || {}\n\n recordRenderer = getRenderer(recordRenderer || column.type)\n\n if (editable !== false && typeof editor !== 'function') {\n editor = getEditor(column.type)\n }\n\n if (typeof classifier !== 'function') {\n classifier = NOOP\n }\n\n compiled.record = {\n /*\n * 숫자 칸의 기본 정렬. 화면이 align 을 주면 그것이 이긴다 — 자릿수를 견줄 값이 아닌 숫자\n * (전화번호 · 우편번호 같은 것)는 화면이 따로 정한다.\n *\n * 머리글도 따라온다. data-grid-header 가 열 제목을 그릴 때 record.align 을 본다.\n */\n ...(RIGHT_ALIGNED_TYPES.includes(column.type) ? { align: 'right' } : {}),\n ...record,\n classifier,\n renderer: recordRenderer,\n editor\n }\n\n /* filter */\n if (filter) {\n let type = typeof filter\n switch (type) {\n case 'boolean':\n compiled.filter = {\n type: column.type,\n operator: 'eq'\n }\n break\n case 'string':\n compiled.filter = {\n type: column.type,\n operator: filter as FilterOperator\n }\n break\n default:\n compiled.filter = {\n //@ts-ignore\n type: column.type,\n operator: 'eq',\n ...(filter as FilterConfigObject)\n }\n }\n }\n\n /* handler */\n var { click, dblclick, focus, contextmenu } = handlers || {}\n\n compiled.handlers = {\n click: click && getGristEventHandler(click),\n dblclick: dblclick && getGristEventHandler(dblclick),\n focus: focus && getGristEventHandler(focus),\n contextmenu: contextmenu && getGristEventHandler(contextmenu)\n }\n\n return compiled\n}\n"]}
1
+ {"version":3,"file":"column-builder.js","sourceRoot":"","sources":["../../../src/configure/column-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import { FilterConfigObject, FilterOperator } from '../index.js'\nimport { NOOP } from '../const.js'\nimport { getEditor } from '../editors/index.js'\nimport { generateGutterColumn } from '../gutters/index.js'\nimport { getGristEventHandler } from '../handlers/index.js'\nimport { getRenderer } from '../renderers/index.js'\nimport { ColumnConfig } from '../types.js'\n\n/**\n * 값을 오른쪽에 붙이는 칸.\n *\n * 자릿수가 세로로 맞아야 값을 견줄 수 있다. 왼쪽으로 두면 24 와 1,200 이 시작점만 같고 끝이 어긋나서\n * 어느 쪽이 큰지 눈으로 읽히지 않는다.\n *\n * progress 는 뺀다 — 막대로 그리므로 정렬이 뜻을 갖지 않는다.\n */\nconst RIGHT_ALIGNED_TYPES = ['integer', 'float', 'number']\n\nexport const buildColumn = (column: any): ColumnConfig => {\n var compiled: ColumnConfig = { ...column }\n\n if (column.type == 'gutter') {\n compiled = generateGutterColumn(column)\n }\n\n var { name, header, record, handlers, label, filter } = compiled\n\n /* header */\n\n /*\n * 이름을 글자로도 남긴다 — renderer 하나만 두면 이름이 여기서 사라진다.\n *\n * 설정이 header: '상태' 처럼 문자열로 들어와도 여기를 지나면 () => '상태' 한 개만 남는다.\n * 그 함수가 돌려주는 것은 화면에 그릴 조각(대개 lit template)이라 되읽을 수 없다. 그래서 화면 밖에서\n * 이름이 필요한 곳 — 스크린 리더가 읽을 이름, filter 입력칸의 이름 — 이 이름을 구할 데가 없었고,\n * 결국 각자 다시 적어 두는 수밖에 없었다. 한 사실에 출처가 둘이 되면 header 를 고쳐도 나머지가\n * 그대로 남는다.\n *\n * 그리는 것과 이름을 함께 남긴다. columnText() 가 이것을 읽는다.\n */\n if (typeof header == 'string') {\n compiled.header = {\n text: header,\n renderer: () => header\n }\n }\n\n if (typeof compiled.header?.renderer == 'string') {\n const text = compiled.header.renderer\n compiled.header = {\n ...compiled.header,\n text,\n renderer: () => text\n }\n }\n\n /* label */\n if (label) {\n let type = typeof label\n switch (type) {\n case 'boolean':\n compiled.label = {\n text: compiled.header?.text || name,\n renderer: compiled.header?.renderer || (() => name)\n }\n break\n case 'string':\n compiled.label = {\n text: label as string,\n renderer: () => label\n }\n break\n default:\n compiled.label = label\n }\n } else {\n compiled.label = false\n }\n\n /*\n * record\n */\n var { renderer: recordRenderer, editor, editable, classifier } = record || {}\n\n recordRenderer = getRenderer(recordRenderer || column.type)\n\n if (editable !== false && typeof editor !== 'function') {\n editor = getEditor(column.type)\n }\n\n if (typeof classifier !== 'function') {\n classifier = NOOP\n }\n\n compiled.record = {\n /*\n * 숫자 칸의 기본 정렬. 화면이 align 을 주면 그것이 이긴다 — 자릿수를 견줄 값이 아닌 숫자\n * (전화번호 · 우편번호 같은 것)는 화면이 따로 정한다.\n *\n * 머리글도 따라온다. data-grid-header 가 열 제목을 그릴 때 record.align 을 본다.\n */\n ...(RIGHT_ALIGNED_TYPES.includes(column.type) ? { align: 'right' } : {}),\n ...record,\n classifier,\n renderer: recordRenderer,\n editor\n }\n\n /* filter */\n if (filter) {\n let type = typeof filter\n switch (type) {\n case 'boolean':\n compiled.filter = {\n type: column.type,\n operator: 'eq'\n }\n break\n case 'string':\n compiled.filter = {\n type: column.type,\n operator: filter as FilterOperator\n }\n break\n default:\n compiled.filter = {\n //@ts-ignore\n type: column.type,\n operator: 'eq',\n ...(filter as FilterConfigObject)\n }\n }\n }\n\n /* handler */\n var { click, dblclick, focus, contextmenu } = handlers || {}\n\n compiled.handlers = {\n click: click && getGristEventHandler(click),\n dblclick: dblclick && getGristEventHandler(dblclick),\n focus: focus && getGristEventHandler(focus),\n contextmenu: contextmenu && getGristEventHandler(contextmenu)\n }\n\n return compiled\n}\n"]}
@@ -24,7 +24,7 @@ export const buildConfig = (config) => {
24
24
  }
25
25
  /*
26
26
  * ── 선택 표식의 모양은 `rows.selectable` 이 정한다 (2026-08-31) ────────────
27
- * `row-selector` 거터는 **자기 열의 `multiple`** 을 보고 체크박스와 라디오를 가른다. 그런데
27
+ * `row-selector` 거터는 **자기 열의 `multiple`** 을 보고 체크박스와 라디오를 구분한다. 그런데
28
28
  * 「여러 건 고를 수 있나」는 원래 `rows.selectable.multiple` 이 정하는 것이다.
29
29
  *
30
30
  * 두 곳이 같은 것을 따로 정하고 있어서, 화면이 `rows.selectable.multiple: true` 만 주면
@@ -1 +1 @@
1
- {"version":3,"file":"config-builder.js","sourceRoot":"","sources":["../../../src/configure/config-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAEjE,SAAS,0BAA0B,CAAC,GAAmB,EAAE,SAAuC;IAC9F,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,CAAA;QACV,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC,CAAA;AACX,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAW,EAAe,EAAE;;IACtD,IAAI,EAAE,OAAO,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,CAAA;IAExF,+CAA+C;IAC/C,MAAM,cAAc,GAAG,0BAA0B,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IAC1E,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;QACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAA;QACzB,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACH,MAAM,gBAAgB,GAAG,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,UAAU,0CAAE,QAAQ,CAAA;IAEnD,OAAO;QACL,GAAG,MAAM;QACT,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAW,EAAE,EAAE,CACnC,WAAW,CACT,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,KAAK,cAAc,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;YAC/F,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,gBAAgB,EAAE;YAC7C,CAAC,CAAC,MAAM,CACX,CACF;QACD,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC;QAC5B,UAAU;QACV,OAAO;QACP,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC;QACrC,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC;QAC5B,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC;QAC5B,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC;KAC7B,CAAA;AACH,CAAC,CAAA","sourcesContent":["import { GristConfig, ColumnConfig } from '../types.js'\nimport { buildColumn } from './column-builder.js'\nimport { buildImexOptions } from './imex-option-builder.js'\nimport { buildListOptions } from './list-option-builder.js'\nimport { buildTreeOptions } from './tree-option-builder.js'\nimport { buildRowsOptions } from './rows-option-builder.js'\nimport { buildFiltersOptions } from './filters-option-builder.js'\n\nfunction findLastIndexWithCondition(arr: ColumnConfig[], condition: (x: ColumnConfig) => boolean) {\n for (let i = arr.length - 1; i >= 0; i--) {\n if (condition(arr[i])) {\n return i\n }\n }\n return -1\n}\n\nexport const buildConfig = (config: any): GristConfig => {\n var { columns = [], rows = {}, pagination, sorters, filters, list, tree, imex } = config\n\n /* 마지막 fixed 컬럼 앞의 모든 컬럼의 fixed를 true로 설정한다. */\n const lastFixedIndex = findLastIndexWithCondition(columns, x => !!x.fixed)\n if (lastFixedIndex >= 0) {\n for (let i = 0; i < lastFixedIndex; i++) {\n columns[i].fixed = true\n }\n }\n\n /*\n * ── 선택 표식의 모양은 `rows.selectable` 이 정한다 (2026-08-31) ────────────\n * `row-selector` 거터는 **자기 열의 `multiple`** 을 보고 체크박스와 라디오를 가른다. 그런데\n * 「여러 건 고를 수 있나」는 원래 `rows.selectable.multiple` 이 정하는 것이다.\n *\n * 두 곳이 같은 것을 따로 정하고 있어서, 화면이 `rows.selectable.multiple: true` 만 주면\n * **여러 건이 골라지는데 모양은 라디오**가 됐다 — 화면이 「하나만 된다」고 말하면서 여럿을 받는다.\n * 실제로 작업지시 목록이 그 상태였다.\n *\n * 거터 열이 스스로 `multiple` 을 적지 않았으면 `rows.selectable` 에서 이어받는다. 적었으면\n * 그 화면의 뜻이므로 그대로 둔다.\n */\n const selectorMultiple = rows?.selectable?.multiple\n\n return {\n ...config,\n columns: columns.map((column: any) =>\n buildColumn(\n column.type === 'gutter' && column.gutterName === 'row-selector' && column.multiple === undefined\n ? { ...column, multiple: !!selectorMultiple }\n : column\n )\n ),\n rows: buildRowsOptions(rows),\n pagination,\n sorters,\n filters: buildFiltersOptions(filters),\n list: buildListOptions(list),\n tree: buildTreeOptions(tree),\n imex: buildImexOptions(imex)\n }\n}\n"]}
1
+ {"version":3,"file":"config-builder.js","sourceRoot":"","sources":["../../../src/configure/config-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAEjE,SAAS,0BAA0B,CAAC,GAAmB,EAAE,SAAuC;IAC9F,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,CAAA;QACV,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC,CAAA;AACX,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAW,EAAe,EAAE;;IACtD,IAAI,EAAE,OAAO,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,CAAA;IAExF,+CAA+C;IAC/C,MAAM,cAAc,GAAG,0BAA0B,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IAC1E,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;QACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAA;QACzB,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACH,MAAM,gBAAgB,GAAG,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,UAAU,0CAAE,QAAQ,CAAA;IAEnD,OAAO;QACL,GAAG,MAAM;QACT,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAW,EAAE,EAAE,CACnC,WAAW,CACT,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,KAAK,cAAc,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;YAC/F,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,gBAAgB,EAAE;YAC7C,CAAC,CAAC,MAAM,CACX,CACF;QACD,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC;QAC5B,UAAU;QACV,OAAO;QACP,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC;QACrC,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC;QAC5B,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC;QAC5B,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC;KAC7B,CAAA;AACH,CAAC,CAAA","sourcesContent":["import { GristConfig, ColumnConfig } from '../types.js'\nimport { buildColumn } from './column-builder.js'\nimport { buildImexOptions } from './imex-option-builder.js'\nimport { buildListOptions } from './list-option-builder.js'\nimport { buildTreeOptions } from './tree-option-builder.js'\nimport { buildRowsOptions } from './rows-option-builder.js'\nimport { buildFiltersOptions } from './filters-option-builder.js'\n\nfunction findLastIndexWithCondition(arr: ColumnConfig[], condition: (x: ColumnConfig) => boolean) {\n for (let i = arr.length - 1; i >= 0; i--) {\n if (condition(arr[i])) {\n return i\n }\n }\n return -1\n}\n\nexport const buildConfig = (config: any): GristConfig => {\n var { columns = [], rows = {}, pagination, sorters, filters, list, tree, imex } = config\n\n /* 마지막 fixed 컬럼 앞의 모든 컬럼의 fixed를 true로 설정한다. */\n const lastFixedIndex = findLastIndexWithCondition(columns, x => !!x.fixed)\n if (lastFixedIndex >= 0) {\n for (let i = 0; i < lastFixedIndex; i++) {\n columns[i].fixed = true\n }\n }\n\n /*\n * ── 선택 표식의 모양은 `rows.selectable` 이 정한다 (2026-08-31) ────────────\n * `row-selector` 거터는 **자기 열의 `multiple`** 을 보고 체크박스와 라디오를 구분한다. 그런데\n * 「여러 건 고를 수 있나」는 원래 `rows.selectable.multiple` 이 정하는 것이다.\n *\n * 두 곳이 같은 것을 따로 정하고 있어서, 화면이 `rows.selectable.multiple: true` 만 주면\n * **여러 건이 골라지는데 모양은 라디오**가 됐다 — 화면이 「하나만 된다」고 말하면서 여럿을 받는다.\n * 실제로 작업지시 목록이 그 상태였다.\n *\n * 거터 열이 스스로 `multiple` 을 적지 않았으면 `rows.selectable` 에서 이어받는다. 적었으면\n * 그 화면의 뜻이므로 그대로 둔다.\n */\n const selectorMultiple = rows?.selectable?.multiple\n\n return {\n ...config,\n columns: columns.map((column: any) =>\n buildColumn(\n column.type === 'gutter' && column.gutterName === 'row-selector' && column.multiple === undefined\n ? { ...column, multiple: !!selectorMultiple }\n : column\n )\n ),\n rows: buildRowsOptions(rows),\n pagination,\n sorters,\n filters: buildFiltersOptions(filters),\n list: buildListOptions(list),\n tree: buildTreeOptions(tree),\n imex: buildImexOptions(imex)\n }\n}\n"]}
@@ -6,6 +6,12 @@ import { DataManipulator } from '../data-manipulator.js';
6
6
  export declare class DataCard extends DataManipulator {
7
7
  static styles: import("lit").CSSResult[];
8
8
  empty?: boolean;
9
+ /** 비었을 때 무슨 말을 하나 — 판단은 data-grist 가 하고 여기서는 그리기만 한다. */
10
+ emptyNote?: {
11
+ icon?: string;
12
+ title: string;
13
+ description?: string;
14
+ };
9
15
  private _records;
10
16
  private isTop;
11
17
  private _total;
@@ -1,9 +1,8 @@
1
1
  import { __decorate } from "tslib";
2
- import i18next from 'i18next';
3
2
  import '@material/web/icon/icon.js';
4
3
  import '../empty-note.js';
5
4
  import './record-card.js';
6
- import { css, html } from 'lit';
5
+ import { css, html, nothing } from 'lit';
7
6
  import { customElement, property, state } from 'lit/decorators.js';
8
7
  import { DataManipulator } from '../data-manipulator.js';
9
8
  let DataCard = class DataCard extends DataManipulator {
@@ -45,6 +44,7 @@ let DataCard = class DataCard extends DataManipulator {
45
44
  }
46
45
  }
47
46
  render() {
47
+ var _a, _b, _c, _d, _e, _f;
48
48
  var { classifier } = this.config.rows;
49
49
  var records = this._records || [];
50
50
  if (this.config && this.config.rows.appendable) {
@@ -66,7 +66,13 @@ let DataCard = class DataCard extends DataManipulator {
66
66
  ></ox-record-card>
67
67
  `;
68
68
  })}
69
- ${this.empty ? html ` <ox-empty-note title=${i18next.t('text.grid-no-records', { defaultValue: 'NO RECORDS' })}></ox-empty-note> ` : html ``}
69
+ ${this.empty
70
+ ? html `<ox-empty-note
71
+ icon=${(_b = (_a = this.emptyNote) === null || _a === void 0 ? void 0 : _a.icon) !== null && _b !== void 0 ? _b : nothing}
72
+ title=${(_d = (_c = this.emptyNote) === null || _c === void 0 ? void 0 : _c.title) !== null && _d !== void 0 ? _d : ''}
73
+ description=${(_f = (_e = this.emptyNote) === null || _e === void 0 ? void 0 : _e.description) !== null && _f !== void 0 ? _f : nothing}
74
+ ></ox-empty-note>`
75
+ : html ``}
70
76
  ${this.isTop
71
77
  ? html ``
72
78
  : html ` <md-icon id="upward" @click=${(e) => this.gotoTop(e)}>arrow_upward</md-icon> `}
@@ -137,6 +143,9 @@ DataCard.styles = [
137
143
  __decorate([
138
144
  property({ type: Boolean })
139
145
  ], DataCard.prototype, "empty", void 0);
146
+ __decorate([
147
+ property({ type: Object })
148
+ ], DataCard.prototype, "emptyNote", void 0);
140
149
  __decorate([
141
150
  state()
142
151
  ], DataCard.prototype, "_records", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"data-card.js","sourceRoot":"","sources":["../../../src/data-card/data-card.ts"],"names":[],"mappings":"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","sourcesContent":["import i18next from 'i18next'\nimport '@material/web/icon/icon.js'\nimport '../empty-note.js'\nimport './record-card.js'\n\nimport { PropertyValues, css, html } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { DataManipulator } from '../data-manipulator.js'\nimport { GristRecord } from '../types.js'\n\n@customElement('ox-card')\nexport class DataCard extends DataManipulator {\n static styles = [\n css`\n :host {\n overflow-y: auto;\n padding: var(--spacing-medium);\n\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n grid-template-rows: min-content;\n grid-gap: var(--spacing-large);\n }\n\n [selected-row] {\n background-color: var(--grid-record-selected-background-color);\n }\n\n #upward {\n --md-icon-size: var(--icon-size-large);\n position: absolute;\n top: var(--data-card-fab-position-vertical);\n right: var(--data-card-fab-position-horizontal);\n background-color: rgba(255, 255, 255, 0.7);\n border-radius: var(--md-sys-shape-corner-large);\n color: var(--data-card-fab-color);\n box-shadow: var(--data-card-fab-shadow);\n padding: var(--spacing-small);\n }\n\n slot {\n width: 100%;\n }\n\n ox-empty-note {\n display: block;\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n }\n\n [card][emphasized-row],\n [card][emphasized-row][focused] {\n background-color: var(--grid-record-emphasized-background-color);\n color: var(--grid-record-emphasized-color);\n }\n\n @media screen and (max-width: 460px) {\n #setting {\n grid-template-columns: 1fr;\n grid-template-rows: 1fr 1fr;\n }\n }\n `\n ]\n\n @property({ type: Boolean }) empty?: boolean\n\n @state() private _records: GristRecord[] = []\n @state() private isTop: boolean = false\n\n private _total: number = 0\n private _limit: number = 20\n private _page: number = 1\n\n firstUpdated(changes: PropertyValues<this>) {\n this.isTop = true\n\n /* infinite scrolling */\n this.addEventListener('scroll', e => {\n const totalScrollHeight = this.scrollHeight\n const screenHeight = this.offsetHeight\n const currentScrollTop = this.scrollTop\n\n if (totalScrollHeight <= screenHeight + currentScrollTop + 1) {\n /* 마지막 페이지까지 계속 페이지를 증가시킨다. */\n var lastPage = Math.ceil(this._total / this._limit)\n\n if (this._page < lastPage) {\n this.dispatchEvent(new CustomEvent('attach-page', { bubbles: true, composed: true }))\n }\n }\n\n this.isTop = this.scrollTop == 0\n })\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('config')) {\n this._records = []\n this._page = 1\n }\n\n if (changes.has('data')) {\n this._records = this.data.records\n this._total = this.data.total || 0\n this._limit = this.data.limit || 20\n this._page = this.data.page || 0\n }\n }\n\n render() {\n var { classifier } = this.config.rows\n var records = this._records || []\n\n if (this.config && this.config.rows.appendable) {\n records = [...records, { __dirty__: '+' }]\n }\n\n return html`\n ${records.map((record, rowIndex) => {\n var { emphasized } = classifier.call(null, record, rowIndex) || {}\n\n return html`\n <ox-record-card\n .config=${this.config}\n .data=${this.data}\n .record=${record}\n .rowIndex=${rowIndex}\n .emphasized=${emphasized}\n ?selected-row=${record['__selected__']}\n ?dirty=${record['__dirty__']}\n card\n ></ox-record-card>\n `\n })}\n ${this.empty ? html` <ox-empty-note title=${i18next.t('text.grid-no-records', { defaultValue: 'NO RECORDS' })}></ox-empty-note> ` : html``}\n ${this.isTop\n ? html``\n : html` <md-icon id=\"upward\" @click=${(e: Event) => this.gotoTop(e)}>arrow_upward</md-icon> `}\n `\n }\n\n gotoTop(e: Event) {\n this.scrollTop = 0\n\n e.stopPropagation()\n }\n\n get pullToRefreshTarget() {\n return this\n }\n}\n"]}
1
+ {"version":3,"file":"data-card.js","sourceRoot":"","sources":["../../../src/data-card/data-card.ts"],"names":[],"mappings":"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","sourcesContent":["import i18next from 'i18next'\nimport '@material/web/icon/icon.js'\nimport '../empty-note.js'\nimport './record-card.js'\n\nimport { PropertyValues, css, html, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { DataManipulator } from '../data-manipulator.js'\nimport { GristRecord } from '../types.js'\n\n@customElement('ox-card')\nexport class DataCard extends DataManipulator {\n static styles = [\n css`\n :host {\n overflow-y: auto;\n padding: var(--spacing-medium);\n\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n grid-template-rows: min-content;\n grid-gap: var(--spacing-large);\n }\n\n [selected-row] {\n background-color: var(--grid-record-selected-background-color);\n }\n\n #upward {\n --md-icon-size: var(--icon-size-large);\n position: absolute;\n top: var(--data-card-fab-position-vertical);\n right: var(--data-card-fab-position-horizontal);\n background-color: rgba(255, 255, 255, 0.7);\n border-radius: var(--md-sys-shape-corner-large);\n color: var(--data-card-fab-color);\n box-shadow: var(--data-card-fab-shadow);\n padding: var(--spacing-small);\n }\n\n slot {\n width: 100%;\n }\n\n ox-empty-note {\n display: block;\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n }\n\n [card][emphasized-row],\n [card][emphasized-row][focused] {\n background-color: var(--grid-record-emphasized-background-color);\n color: var(--grid-record-emphasized-color);\n }\n\n @media screen and (max-width: 460px) {\n #setting {\n grid-template-columns: 1fr;\n grid-template-rows: 1fr 1fr;\n }\n }\n `\n ]\n\n @property({ type: Boolean }) empty?: boolean\n\n /** 비었을 때 무슨 말을 하나 — 판단은 data-grist 가 하고 여기서는 그리기만 한다. */\n @property({ type: Object }) emptyNote?: { icon?: string; title: string; description?: string }\n\n @state() private _records: GristRecord[] = []\n @state() private isTop: boolean = false\n\n private _total: number = 0\n private _limit: number = 20\n private _page: number = 1\n\n firstUpdated(changes: PropertyValues<this>) {\n this.isTop = true\n\n /* infinite scrolling */\n this.addEventListener('scroll', e => {\n const totalScrollHeight = this.scrollHeight\n const screenHeight = this.offsetHeight\n const currentScrollTop = this.scrollTop\n\n if (totalScrollHeight <= screenHeight + currentScrollTop + 1) {\n /* 마지막 페이지까지 계속 페이지를 증가시킨다. */\n var lastPage = Math.ceil(this._total / this._limit)\n\n if (this._page < lastPage) {\n this.dispatchEvent(new CustomEvent('attach-page', { bubbles: true, composed: true }))\n }\n }\n\n this.isTop = this.scrollTop == 0\n })\n }\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('config')) {\n this._records = []\n this._page = 1\n }\n\n if (changes.has('data')) {\n this._records = this.data.records\n this._total = this.data.total || 0\n this._limit = this.data.limit || 20\n this._page = this.data.page || 0\n }\n }\n\n render() {\n var { classifier } = this.config.rows\n var records = this._records || []\n\n if (this.config && this.config.rows.appendable) {\n records = [...records, { __dirty__: '+' }]\n }\n\n return html`\n ${records.map((record, rowIndex) => {\n var { emphasized } = classifier.call(null, record, rowIndex) || {}\n\n return html`\n <ox-record-card\n .config=${this.config}\n .data=${this.data}\n .record=${record}\n .rowIndex=${rowIndex}\n .emphasized=${emphasized}\n ?selected-row=${record['__selected__']}\n ?dirty=${record['__dirty__']}\n card\n ></ox-record-card>\n `\n })}\n ${this.empty\n ? html`<ox-empty-note\n icon=${this.emptyNote?.icon ?? nothing}\n title=${this.emptyNote?.title ?? ''}\n description=${this.emptyNote?.description ?? nothing}\n ></ox-empty-note>`\n : html``}\n ${this.isTop\n ? html``\n : html` <md-icon id=\"upward\" @click=${(e: Event) => this.gotoTop(e)}>arrow_upward</md-icon> `}\n `\n }\n\n gotoTop(e: Event) {\n this.scrollTop = 0\n\n e.stopPropagation()\n }\n\n get pullToRefreshTarget() {\n return this\n }\n}\n"]}
@@ -11,7 +11,7 @@ export const dataGridBodyStyle = css `
11
11
  *
12
12
  * 머리는 overflow: hidden 이라 스크롤바에 자리를 내주지 않고, 몸은 긴 목록에서 스크롤바 폭만큼
13
13
  * 좁아진다. 그래서 갈 수 있는 거리가 몸이 그만큼 더 길었고, 오른쪽 끝에서 몸만 더 갔다.
14
- * 재보니 정확히 --scrollbar-width 만큼(5px)이었다.
14
+ * 확인해 보니 정확히 --scrollbar-width 만큼(5px)이었다.
15
15
  *
16
16
  * 자리를 늘 비워 두면 목록 길이와 무관하게 두 폭이 같다. 목록이 길어질 때 열이 움찔하는 것도
17
17
  * 함께 없어진다 — 스크롤바가 생기는 순간 폭이 바뀌지 않는다.
@@ -23,6 +23,17 @@ export const dataGridBodyStyle = css `
23
23
  border-bottom: var(--grid-body-bottom-border);
24
24
  }
25
25
 
26
+ /*
27
+ * 줄 감싸개 — 상자를 만들지 않는다.
28
+ *
29
+ * role=row 를 담기 위한 요소일 뿐이라 배치에는 끼어들면 안 된다. display: contents 면 이 요소는
30
+ * 상자를 만들지 않고 자식이 그대로 바깥 grid 의 항목이 된다 — 열 폭 · 고정 열 · 묶음 머리의
31
+ * grid-column 배치가 그대로다.
32
+ */
33
+ div[grid-row] {
34
+ display: contents;
35
+ }
36
+
26
37
  ox-grid-field[odd] {
27
38
  background-color: var(--grid-record-odd-background-color);
28
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"data-grid-body-style.js","sourceRoot":"","sources":["../../../src/data-grid/data-grid-body-style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2InC,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport const dataGridBodyStyle = css`\n :host {\n display: grid;\n grid-template-columns: var(--grid-template-columns);\n grid-auto-rows: var(--grid-record-height, min-content);\n\n overflow: auto;\n /*\n * 세로 스크롤바 자리를 늘 비워 둔다 — 머리와 가로 스크롤 범위를 같게 만들기 위해서다.\n *\n * 머리는 overflow: hidden 이라 스크롤바에 자리를 내주지 않고, 몸은 긴 목록에서 스크롤바 폭만큼\n * 좁아진다. 그래서 갈 수 있는 거리가 몸이 그만큼 더 길었고, 오른쪽 끝에서 몸만 더 갔다.\n * 재보니 정확히 --scrollbar-width 만큼(5px)이었다.\n *\n * 자리를 늘 비워 두면 목록 길이와 무관하게 두 폭이 같다. 목록이 길어질 때 열이 움찔하는 것도\n * 함께 없어진다 — 스크롤바가 생기는 순간 폭이 바뀌지 않는다.\n */\n scrollbar-gutter: stable;\n outline: none;\n color: var(--grid-record-color);\n position: relative;\n border-bottom: var(--grid-body-bottom-border);\n }\n\n ox-grid-field[odd] {\n background-color: var(--grid-record-odd-background-color);\n }\n\n ox-grid-field[disabled] {\n background-color: var(--grid-record-disabled-background-color, var(--grid-record-background-color));\n color: var(--grid-record-disabled-color, var(--grid-record-color));\n opacity: var(--grid-record-disabled-opacity, 1);\n }\n ox-grid-field[odd][disabled] {\n background-color: var(\n --grid-odd-record-disabled-background-color,\n var(--grid-record-disabled-background-color, var(--grid-record-odd-background-color))\n );\n color: var(--grid-odd-record-disabled-color, var(--grid-record-disabled-color, var(--grid-record-color)));\n opacity: var(--grid-odd-record-disabled-opacity, var(--grid-record-disabled-opacity, 1));\n }\n\n ox-grid-field[selected-row] {\n background-color: var(--grid-record-selected-background-color);\n color: var(--grid-record-selected-color);\n }\n\n /*\n * 마우스가 지나는 줄.\n *\n * 고른 줄보다 **훨씬 옅다.** 지나는 것은 사실이 아니고 눈이 어디 있는지 알려 주는 것뿐이라, 고른\n * 줄과 같은 무게로 그리면 무엇이 골라졌는지 헷갈린다. 그래서 고른 줄 규칙 뒤에 둔다 — 둘이 겹칠\n * 때는 고른 쪽이 이겨야 하므로 색을 옅게 두고 순서로 다투지 않는다.\n *\n * 값은 표가 정한다. 고치는 표에서는 비운다(data-grid.ts) — 거기서는 점선 셀 테두리가 이미 자리를\n * 말하고, 둘이 함께 있으면 시끄럽다.\n */\n @media (hover: hover) {\n ox-grid-field[hovered-row]:not([selected-row]) {\n background-color: var(--grid-record-hover-background-color, transparent);\n }\n }\n\n ox-grid-field[focused-row] {\n box-shadow: var(--grid-record-focused-box-shadow);\n font-weight: bold;\n color: var(--grid-record-focused-color);\n background-image: var(--focused-background-image);\n background-blend-mode: darken;\n }\n\n /*\n * 초점 셀 표식 — border 가 아니라 outline 으로 그린다.\n *\n * border 로 그리면 두 가지가 깨진다.\n * · 네 면 테두리가 셀 크기를 바꿔 줄이 어긋난다\n * · 아래 면이 **줄을 나누는 선 자리**를 차지해, 그 셀만 줄 선이 사라진다\n *\n * outline 은 자리를 차지하지 않는다. outline-offset: -1px 로 안쪽에 그려 옆 셀과 겹치지 않게 한다.\n * 표식을 감추고 싶은 화면은 토큰을 none 으로 두면 되고, 그래도 줄 선은 남는다.\n */\n ox-grid-field[focused] {\n outline: var(--grid-record-focused-cell-border);\n outline-offset: -1px;\n }\n\n ox-grid-field[emphasized-row],\n ox-grid-field[emphasized-row][focused] {\n background-color: var(--grid-record-emphasized-background-color);\n color: var(--grid-record-emphasized-color);\n }\n\n ox-grid-field[editing] {\n background-color: var(--grid-record-editing-background-color);\n }\n\n @media print {\n :host {\n grid-template-columns: var(--grid-template-print-columns);\n }\n ox-grid-field[focused] {\n outline: none;\n }\n\n ox-grid-field[selected-row] {\n background-color: transparent;\n }\n\n /* 종이에는 마우스가 없다. */\n ox-grid-field[hovered-row] {\n background-color: transparent;\n }\n\n ox-grid-field[emphasized-row] {\n background-color: transparent;\n color: initial;\n }\n\n ox-grid-field[focused-row] {\n background-color: transparent;\n color: initial;\n }\n\n ox-grid-field[editing] {\n background-color: transparent;\n }\n }\n\n @media (prefers-color-scheme: dark) {\n ox-grid-field[focused-row] {\n background-blend-mode: lighten;\n }\n }\n\n @media (prefers-color-scheme: light) {\n ox-grid-field[focused-row] {\n background-blend-mode: darken;\n }\n }\n`\n"]}
1
+ {"version":3,"file":"data-grid-body-style.js","sourceRoot":"","sources":["../../../src/data-grid/data-grid-body-style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsJnC,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport const dataGridBodyStyle = css`\n :host {\n display: grid;\n grid-template-columns: var(--grid-template-columns);\n grid-auto-rows: var(--grid-record-height, min-content);\n\n overflow: auto;\n /*\n * 세로 스크롤바 자리를 늘 비워 둔다 — 머리와 가로 스크롤 범위를 같게 만들기 위해서다.\n *\n * 머리는 overflow: hidden 이라 스크롤바에 자리를 내주지 않고, 몸은 긴 목록에서 스크롤바 폭만큼\n * 좁아진다. 그래서 갈 수 있는 거리가 몸이 그만큼 더 길었고, 오른쪽 끝에서 몸만 더 갔다.\n * 확인해 보니 정확히 --scrollbar-width 만큼(5px)이었다.\n *\n * 자리를 늘 비워 두면 목록 길이와 무관하게 두 폭이 같다. 목록이 길어질 때 열이 움찔하는 것도\n * 함께 없어진다 — 스크롤바가 생기는 순간 폭이 바뀌지 않는다.\n */\n scrollbar-gutter: stable;\n outline: none;\n color: var(--grid-record-color);\n position: relative;\n border-bottom: var(--grid-body-bottom-border);\n }\n\n /*\n * 줄 감싸개 — 상자를 만들지 않는다.\n *\n * role=row 를 담기 위한 요소일 뿐이라 배치에는 끼어들면 안 된다. display: contents 면 이 요소는\n * 상자를 만들지 않고 자식이 그대로 바깥 grid 의 항목이 된다 — 열 폭 · 고정 열 · 묶음 머리의\n * grid-column 배치가 그대로다.\n */\n div[grid-row] {\n display: contents;\n }\n\n ox-grid-field[odd] {\n background-color: var(--grid-record-odd-background-color);\n }\n\n ox-grid-field[disabled] {\n background-color: var(--grid-record-disabled-background-color, var(--grid-record-background-color));\n color: var(--grid-record-disabled-color, var(--grid-record-color));\n opacity: var(--grid-record-disabled-opacity, 1);\n }\n ox-grid-field[odd][disabled] {\n background-color: var(\n --grid-odd-record-disabled-background-color,\n var(--grid-record-disabled-background-color, var(--grid-record-odd-background-color))\n );\n color: var(--grid-odd-record-disabled-color, var(--grid-record-disabled-color, var(--grid-record-color)));\n opacity: var(--grid-odd-record-disabled-opacity, var(--grid-record-disabled-opacity, 1));\n }\n\n ox-grid-field[selected-row] {\n background-color: var(--grid-record-selected-background-color);\n color: var(--grid-record-selected-color);\n }\n\n /*\n * 마우스가 지나는 줄.\n *\n * 고른 줄보다 **훨씬 옅다.** 지나는 것은 사실이 아니고 눈이 어디 있는지 알려 주는 것뿐이라, 고른\n * 줄과 같은 무게로 그리면 무엇이 골라졌는지 헷갈린다. 그래서 고른 줄 규칙 뒤에 둔다 — 둘이 겹칠\n * 때는 고른 쪽이 이겨야 하므로 색을 옅게 두고 순서로 다투지 않는다.\n *\n * 값은 표가 정한다. 고치는 표에서는 비운다(data-grid.ts) — 거기서는 점선 셀 테두리가 이미 자리를\n * 말하고, 둘이 함께 있으면 시끄럽다.\n */\n @media (hover: hover) {\n ox-grid-field[hovered-row]:not([selected-row]) {\n background-color: var(--grid-record-hover-background-color, transparent);\n }\n }\n\n ox-grid-field[focused-row] {\n box-shadow: var(--grid-record-focused-box-shadow);\n font-weight: bold;\n color: var(--grid-record-focused-color);\n background-image: var(--focused-background-image);\n background-blend-mode: darken;\n }\n\n /*\n * 초점 셀 표식 — border 가 아니라 outline 으로 그린다.\n *\n * border 로 그리면 두 가지가 깨진다.\n * · 네 면 테두리가 셀 크기를 바꿔 줄이 어긋난다\n * · 아래 면이 **줄을 나누는 선 자리**를 차지해, 그 셀만 줄 선이 사라진다\n *\n * outline 은 자리를 차지하지 않는다. outline-offset: -1px 로 안쪽에 그려 옆 셀과 겹치지 않게 한다.\n * 표식을 감추고 싶은 화면은 토큰을 none 으로 두면 되고, 그래도 줄 선은 남는다.\n */\n ox-grid-field[focused] {\n outline: var(--grid-record-focused-cell-border);\n outline-offset: -1px;\n }\n\n ox-grid-field[emphasized-row],\n ox-grid-field[emphasized-row][focused] {\n background-color: var(--grid-record-emphasized-background-color);\n color: var(--grid-record-emphasized-color);\n }\n\n ox-grid-field[editing] {\n background-color: var(--grid-record-editing-background-color);\n }\n\n @media print {\n :host {\n grid-template-columns: var(--grid-template-print-columns);\n }\n ox-grid-field[focused] {\n outline: none;\n }\n\n ox-grid-field[selected-row] {\n background-color: transparent;\n }\n\n /* 종이에는 마우스가 없다. */\n ox-grid-field[hovered-row] {\n background-color: transparent;\n }\n\n ox-grid-field[emphasized-row] {\n background-color: transparent;\n color: initial;\n }\n\n ox-grid-field[focused-row] {\n background-color: transparent;\n color: initial;\n }\n\n ox-grid-field[editing] {\n background-color: transparent;\n }\n }\n\n @media (prefers-color-scheme: dark) {\n ox-grid-field[focused-row] {\n background-blend-mode: lighten;\n }\n }\n\n @media (prefers-color-scheme: light) {\n ox-grid-field[focused-row] {\n background-blend-mode: darken;\n }\n }\n`\n"]}
@@ -13,6 +13,13 @@ export declare class DataGridBody extends LitElement {
13
13
  row: number;
14
14
  column: number;
15
15
  };
16
+ /**
17
+ * 이 판의 첫 줄이 표 전체에서 몇 번째인가(1부터).
18
+ *
19
+ * 머리줄 수와 페이지 자리를 합한 값이라 ox-grid 가 계산해서 내려 준다 — 여기서 다시 세면
20
+ * 머리가 한 줄인지 두 줄인지(묶음 머리)를 몸이 알아야 한다.
21
+ */
22
+ ariaRowIndexBase: number;
16
23
  editTarget: {
17
24
  row: number;
18
25
  column: number;
@@ -47,8 +54,25 @@ export declare class DataGridBody extends LitElement {
47
54
  private _hoveredRow;
48
55
  /** 지나는 줄을 옮긴다. 같은 줄이면 아무것도 하지 않는다. */
49
56
  private setHoveredRow;
57
+ /**
58
+ * 줄 하나 — 그 줄의 칸을 담고 있는 요소.
59
+ *
60
+ * ── 자식을 세지 않는다 (2026-09-06) ────────────────────────────────────
61
+ * 전에는 `renderRoot.children` 을 `줄번호 * (칸수 + 1)` 로 셌다. 「+1」은 남는 폭을 메우는
62
+ * 빈 칸이고, 그 앞의 주석은 그 셈이 왜 필요한지를 설명하고 있었다 — 셈이 맞아야만 도는 코드였다.
63
+ *
64
+ * 지금은 줄이 **실제 요소**다(role=row). 셀 필요가 없다.
65
+ */
66
+ private rowElementAt;
50
67
  private markRow;
51
68
  getFieldByIndex(rowIndex: number, columnIndex: number, residue?: boolean): DataGridField | undefined;
69
+ /**
70
+ * 칸의 **팝업 표식**을 눌렀다 — 그 칸의 편집을 연다.
71
+ *
72
+ * 더블클릭과 같은 자리로 들어간다. 표식은 한 번 누르는 22px 표적이고, 33px 줄에서 더블클릭을
73
+ * 겨누는 것보다 쉽다. 더블클릭도 그대로 둔다 — 이미 아는 사람의 길을 막지 않는다.
74
+ */
75
+ private onStartEdit;
52
76
  startEditTarget(row: number, column: number, valueWith?: string | null): void;
53
77
  shouldUpdate(changes: any): boolean;
54
78
  updated(changes: PropertyValues<this>): void;
@@ -65,6 +89,13 @@ export declare class DataGridBody extends LitElement {
65
89
  paste(): Promise<void>;
66
90
  setSelectBlock(start?: DataGridField, end?: DataGridField): void;
67
91
  buildAccumulatorRecord(): GristRecord;
92
+ /**
93
+ * IME buffer 가 지금 어느 칸을 대신하고 있는지 — 스크린 리더가 읽을 이름.
94
+ *
95
+ * 이름의 출처는 column 정의 하나다(columnText). 포커스가 아직 아무 칸에도 없거나 이름을 구할 수
96
+ * 없으면 「칸 입력」으로 물러난다 — 이름을 지어내지 않고, 그렇다고 이름을 비우지도 않는다.
97
+ */
98
+ private _imeBufferLabel;
68
99
  /** IME buffer를 포커스된 셀 위에 위치시킨다 */
69
100
  private _positionIMEBuffer;
70
101
  /** IME buffer 값을 field-change 이벤트로 커밋한다 */