@operato/data-grist 10.9.0 → 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.
- package/CHANGELOG.md +15 -0
- package/dist/src/configure/column-builder.js +17 -2
- package/dist/src/configure/column-builder.js.map +1 -1
- package/dist/src/configure/config-builder.js +1 -1
- package/dist/src/configure/config-builder.js.map +1 -1
- package/dist/src/data-grid/data-grid-body-style.js +12 -1
- package/dist/src/data-grid/data-grid-body-style.js.map +1 -1
- package/dist/src/data-grid/data-grid-body.d.ts +24 -0
- package/dist/src/data-grid/data-grid-body.js +126 -39
- package/dist/src/data-grid/data-grid-body.js.map +1 -1
- package/dist/src/data-grid/data-grid-field.d.ts +2 -2
- package/dist/src/data-grid/data-grid-field.js +29 -29
- package/dist/src/data-grid/data-grid-field.js.map +1 -1
- package/dist/src/data-grid/data-grid-footer.js +1 -1
- package/dist/src/data-grid/data-grid-footer.js.map +1 -1
- package/dist/src/data-grid/data-grid-header.d.ts +12 -1
- package/dist/src/data-grid/data-grid-header.js +81 -8
- package/dist/src/data-grid/data-grid-header.js.map +1 -1
- package/dist/src/data-grid/data-grid.d.ts +9 -1
- package/dist/src/data-grid/data-grid.js +79 -27
- package/dist/src/data-grid/data-grid.js.map +1 -1
- package/dist/src/data-grist.d.ts +7 -0
- package/dist/src/data-grist.js +12 -8
- package/dist/src/data-grist.js.map +1 -1
- package/dist/src/editors/ox-grist-editor.d.ts +14 -1
- package/dist/src/editors/ox-grist-editor.js +27 -3
- package/dist/src/editors/ox-grist-editor.js.map +1 -1
- package/dist/src/editors/registry.d.ts +2 -2
- package/dist/src/editors/registry.js +2 -2
- package/dist/src/editors/registry.js.map +1 -1
- package/dist/src/filters/filter-input.js +15 -0
- package/dist/src/filters/filter-input.js.map +1 -1
- package/dist/src/filters/filter-range-date.js +9 -0
- package/dist/src/filters/filter-range-date.js.map +1 -1
- package/dist/src/filters/filter-range-number.js +9 -0
- package/dist/src/filters/filter-range-number.js.map +1 -1
- package/dist/src/gutters/gutter-row-selector.js +16 -0
- package/dist/src/gutters/gutter-row-selector.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/renderers/ox-grist-renderer-boolean.js +8 -0
- package/dist/src/renderers/ox-grist-renderer-boolean.js.map +1 -1
- package/dist/src/types.d.ts +11 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/column-text.d.ts +18 -0
- package/dist/src/utils/column-text.js +37 -0
- package/dist/src/utils/column-text.js.map +1 -0
- package/dist/src/utils/index.d.ts +1 -0
- package/dist/src/utils/index.js +1 -0
- package/dist/src/utils/index.js.map +1 -1
- package/dist/test/accessible-name.test.d.ts +2 -0
- package/dist/test/accessible-name.test.js +324 -0
- package/dist/test/accessible-name.test.js.map +1 -0
- package/dist/test/row-hover-visible.test.js +2 -2
- package/dist/test/row-hover-visible.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/test/accessible-name.test.ts +408 -0
- package/test/row-hover-visible.test.ts +2 -2
- package/translations/en.json +8 -0
- package/translations/ja.json +8 -0
- package/translations/ko.json +7 -0
- package/translations/ms.json +8 -0
- package/translations/zh.json +8 -0
|
@@ -62,17 +62,40 @@ let DataGridHeader = class DataGridHeader extends LitElement {
|
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
+
/*
|
|
66
|
+
* ── 머리줄을 **줄이라고 말한다** ──────────────────────────────────────────
|
|
67
|
+
*
|
|
68
|
+
* 이 머리는 CSS grid 라 칸들이 평평하게 놓여 있었다 — 보기에는 한 줄(묶음이 있으면 두 줄)이지만
|
|
69
|
+
* DOM 에는 줄이 없다. 눈으로는 같은 높이에 있는 것이 한 줄로 읽히는데, 그것은 그림일 뿐이다.
|
|
70
|
+
*
|
|
71
|
+
* role=row 를 담을 자리가 필요해서 감싸개를 둔다. `display: contents` 라 상자를 만들지 않으므로
|
|
72
|
+
* 칸들은 그대로 grid 의 항목으로 남는다 — 열 폭 · 고정 열 · 묶음 머리의 grid-column 배치가 하나도
|
|
73
|
+
* 바뀌지 않는다.
|
|
74
|
+
*/
|
|
65
75
|
render() {
|
|
66
|
-
const
|
|
76
|
+
const grouped = this.row2.length > 0;
|
|
77
|
+
const clazz = grouped ? 'span-both' : undefined;
|
|
67
78
|
return html `
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
<div grid-row role="row" aria-rowindex="1">
|
|
80
|
+
${this.row1.map(({ column, clazz, start, size, align, index, group, colIndex }) => this.renderHeaderColumn({ column, clazz, start, size, align, index, group, colIndex, grouped }))}
|
|
81
|
+
|
|
82
|
+
<!--
|
|
83
|
+
남는 폭을 메우는 칸이다. 읽을 것이 없으므로 표의 칸으로 세지 않는다 — 세면 열 수가 하나
|
|
84
|
+
더 많다고 말하게 된다.
|
|
85
|
+
-->
|
|
86
|
+
<div column role="presentation" class=${ifDefined(clazz)}></div>
|
|
87
|
+
</div>
|
|
71
88
|
|
|
72
|
-
${
|
|
89
|
+
${grouped
|
|
90
|
+
? html `
|
|
91
|
+
<div grid-row role="row" aria-rowindex="2">
|
|
92
|
+
${this.row2.map(({ column, index, colIndex }) => this.renderHeaderColumn({ column, index, colIndex, grouped }))}
|
|
93
|
+
</div>
|
|
94
|
+
`
|
|
95
|
+
: nothing}
|
|
73
96
|
`;
|
|
74
97
|
}
|
|
75
|
-
renderHeaderColumn({ column, clazz, start, size, align, index, group }) {
|
|
98
|
+
renderHeaderColumn({ column, clazz, start, size, align, index, group, colIndex, grouped }) {
|
|
76
99
|
var _a, _b, _c;
|
|
77
100
|
if (column.hidden) {
|
|
78
101
|
return html `${nothing}`;
|
|
@@ -91,7 +114,7 @@ let DataGridHeader = class DataGridHeader extends LitElement {
|
|
|
91
114
|
*
|
|
92
115
|
* 그 span 은 이름 길이만큼만 차지하므로(위 span[for-title] 머리말) 안에 글자를 밀 여백이 없다.
|
|
93
116
|
* 그래서 text-align 이 아무 일도 하지 않는다 — 숫자 칸의 값은 오른쪽인데 머리글만 왼쪽에
|
|
94
|
-
* 남았다. [
|
|
117
|
+
* 남았다. [확인함] 「수량」 머리 x=391 · 값 오른쪽 끝 x=402. 2026-09-02
|
|
95
118
|
*
|
|
96
119
|
* 칸이 밀면 이름과 표식이 **함께** 그 방향으로 간다. 그래서 오른쪽 정렬 칸에서도 표식이 이름
|
|
97
120
|
* 옆에 붙어 있다.
|
|
@@ -105,6 +128,11 @@ let DataGridHeader = class DataGridHeader extends LitElement {
|
|
|
105
128
|
?gutter=${column.type == 'gutter'}
|
|
106
129
|
?fixed=${column.fixed}
|
|
107
130
|
column
|
|
131
|
+
role="columnheader"
|
|
132
|
+
aria-colindex=${ifDefined(group ? start : colIndex)}
|
|
133
|
+
aria-colspan=${ifDefined(group ? size : undefined)}
|
|
134
|
+
aria-rowspan=${ifDefined(clazz === 'span-both' ? 2 : undefined)}
|
|
135
|
+
aria-sort=${ifDefined(this._ariaSort(column, group))}
|
|
108
136
|
class=${ifDefined(clazz)}
|
|
109
137
|
style=${group
|
|
110
138
|
? `--group-start:${start};--group-size:${size};${justify}${((_b = column.header) === null || _b === void 0 ? void 0 : _b.style) || ''}`
|
|
@@ -158,6 +186,18 @@ let DataGridHeader = class DataGridHeader extends LitElement {
|
|
|
158
186
|
updated(changes) {
|
|
159
187
|
var isColumnWidthChangePossible = false;
|
|
160
188
|
if (changes.has('columns')) {
|
|
189
|
+
/*
|
|
190
|
+
* 보이는 열의 번호 — aria-colindex 는 **숨긴 열을 세지 않는다**(1부터).
|
|
191
|
+
*
|
|
192
|
+
* 아래 reduce 의 index 는 this.columns 안의 자리라 숨긴 열까지 센 값이다. 그것을 그대로
|
|
193
|
+
* 내보내면 열을 하나 숨긴 표에서 「3번째 열」이라고 읽히는 칸이 실제로는 두 번째가 된다.
|
|
194
|
+
*/
|
|
195
|
+
const colIndexOf = new Map();
|
|
196
|
+
this.columns.forEach((column, index) => {
|
|
197
|
+
if (!column.hidden) {
|
|
198
|
+
colIndexOf.set(index, colIndexOf.size + 1);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
161
201
|
this.row2 = this.columns.reduce((row2, column, index) => {
|
|
162
202
|
var _a;
|
|
163
203
|
if (column.hidden || !((_a = column.header) === null || _a === void 0 ? void 0 : _a.group)) {
|
|
@@ -165,6 +205,7 @@ let DataGridHeader = class DataGridHeader extends LitElement {
|
|
|
165
205
|
}
|
|
166
206
|
return row2.concat({
|
|
167
207
|
index,
|
|
208
|
+
colIndex: colIndexOf.get(index),
|
|
168
209
|
column
|
|
169
210
|
});
|
|
170
211
|
}, []);
|
|
@@ -179,6 +220,7 @@ let DataGridHeader = class DataGridHeader extends LitElement {
|
|
|
179
220
|
if (!((_a = column.header) === null || _a === void 0 ? void 0 : _a.group)) {
|
|
180
221
|
return row1.concat({
|
|
181
222
|
index,
|
|
223
|
+
colIndex: colIndexOf.get(index),
|
|
182
224
|
column,
|
|
183
225
|
clazz
|
|
184
226
|
});
|
|
@@ -221,6 +263,26 @@ let DataGridHeader = class DataGridHeader extends LitElement {
|
|
|
221
263
|
var title = renderer.call(this, column);
|
|
222
264
|
return html `${((_a = column === null || column === void 0 ? void 0 : column.record) === null || _a === void 0 ? void 0 : _a.mandatory) ? '*' : ''} ${title} `;
|
|
223
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* 이 열이 지금 어떻게 정렬되어 있나 — 화살표가 말하는 것을 글자로도 말한다.
|
|
268
|
+
*
|
|
269
|
+
* 화살표(keyboard_arrow_up/down)는 그림이라 눈으로만 읽힌다. aria-sort 가 있으면 스크린 리더가
|
|
270
|
+
* 열 이름 뒤에 「오름차순」을 붙여 읽고, 정렬을 바꿨을 때 바뀌었다는 것도 전해진다.
|
|
271
|
+
*
|
|
272
|
+
* 정렬할 수 없는 열에는 붙이지 않는다 — none 은 「지금은 정렬 안 됨」이라는 뜻이라
|
|
273
|
+
* 정렬 자체가 없는 열에 쓰면 있지도 않은 기능을 있다고 말하게 된다.
|
|
274
|
+
* 묶음 머리도 마찬가지다(그 자리는 누를 수 없다).
|
|
275
|
+
*/
|
|
276
|
+
_ariaSort(column, group) {
|
|
277
|
+
if (group || !column.sortable) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
const sorter = (this.sorters || []).find(sorter => column.type !== 'gutter' && column.name == sorter.name);
|
|
281
|
+
if (!sorter) {
|
|
282
|
+
return 'none';
|
|
283
|
+
}
|
|
284
|
+
return sorter.desc ? 'descending' : 'ascending';
|
|
285
|
+
}
|
|
224
286
|
_renderSortHeader(column) {
|
|
225
287
|
var sorters = this.sorters || [];
|
|
226
288
|
var sorter = sorters.find(sorter => column.type !== 'gutter' && column.name == sorter.name);
|
|
@@ -508,7 +570,7 @@ DataGridHeader.styles = [
|
|
|
508
570
|
/*
|
|
509
571
|
* 이름이 자기 길이만큼만 차지한다 — 그러면 정렬 표식이 이름 바로 오른쪽에 붙는다.
|
|
510
572
|
*
|
|
511
|
-
* 고치는 표에서는 1 로 되돌린다(data-grid.ts 의 editable-grid
|
|
573
|
+
* 고치는 표에서는 1 로 되돌린다(data-grid.ts 의 editable-grid 와 같은 값). 거기서는 칸 끝에 폭 조절
|
|
512
574
|
* 손잡이가 서 있어서, 표식이 그 옆에 모여 있는 편이 낫다.
|
|
513
575
|
*
|
|
514
576
|
* **조건은 손잡이가 보이는지다.** 표식이 칸 오른쪽 끝에 있는데 그 오른쪽에 선이 없으면 눈이
|
|
@@ -678,6 +740,17 @@ DataGridHeader.styles = [
|
|
|
678
740
|
grid-row: 1 / span 2; /* 1단과 2단에 걸쳐 표시 */
|
|
679
741
|
}
|
|
680
742
|
|
|
743
|
+
/*
|
|
744
|
+
* 줄 감싸개 — 상자를 만들지 않는다.
|
|
745
|
+
*
|
|
746
|
+
* role=row 를 담기 위한 요소일 뿐이라 배치에는 끼어들면 안 된다. display: contents 면 이 요소는
|
|
747
|
+
* 상자를 만들지 않고 자식이 그대로 바깥 grid 의 항목이 된다 — 열 폭 · 고정 열 · 묶음 머리의
|
|
748
|
+
* grid-column 배치가 그대로다.
|
|
749
|
+
*/
|
|
750
|
+
div[grid-row] {
|
|
751
|
+
display: contents;
|
|
752
|
+
}
|
|
753
|
+
|
|
681
754
|
.group-header {
|
|
682
755
|
grid-row: 1; /* 1행에 배치 */
|
|
683
756
|
grid-column: var(--group-start) / span var(--group-size); /* 2열부터 시작하여 2열에 걸쳐 표시 */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid-header.js","sourceRoot":"","sources":["../../../src/data-grid/data-grid-header.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/popup/ox-popup.js'\nimport '@material/web/icon/icon.js'\n\nimport i18next from 'i18next'\nimport { css, html, LitElement, PropertyValues, nothing, TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\nimport throttle from 'lodash-es/throttle.js'\n\nimport { OxPopup } from '@operato/popup'\nimport { TooltipReactiveController, closestElement } from '@operato/utils'\n\nimport { ZERO_COLUMNS, ZERO_DATA } from '../configure/zero-config.js'\nimport { FilterStyles } from '../filters/filter-styles.js'\nimport { getFilterRenderer } from '../filters/registry.js'\nimport { ColumnConfig, FilterConfigObject, FilterValue, GristData, SortersConfig } from '../types.js'\n\n/** 칸의 정렬 선언(record.align)을 flex 의 말로 옮긴다. */\nconst ALIGN_JUSTIFY = {\n left: 'flex-start',\n center: 'center',\n right: 'flex-end'\n} as const\n\n@customElement('ox-grid-header')\nexport class DataGridHeader extends LitElement {\n static styles = [\n FilterStyles,\n css`\n :host {\n position: relative;\n display: grid;\n grid-template-columns: var(--grid-template-columns);\n\n border-top: var(--grid-header-top-border);\n\n /*\n * 가로는 감춘다 — 머리를 사람이 직접 끌지는 않는다. 몸을 따라 프로그램이 옮긴다.\n *\n * 세로는 몸과 같은 만큼 자리를 비운다. 몸만 스크롤바에 자리를 내주면 갈 수 있는 거리가\n * 달라져서, 오른쪽 끝에서 몸이 그 폭만큼 더 간다(몸의 scrollbar-gutter 주석 참조).\n * 머리에는 세로로 넘칠 것이 없으므로 손잡이는 그려지지 않고 자리만 남는다.\n */\n overflow-x: hidden;\n overflow-y: scroll;\n scrollbar-gutter: stable;\n\n font-variation-settings: 'FILL' 1;\n\n user-select: none;\n }\n\n :host([raised]) [fixed] {\n /* 고정 컬럼이 살짝 올라와 있는 느낌을 표현 */\n box-shadow: 3px 0 3px rgba(0, 0, 0, 0.1);\n }\n\n div[column] {\n display: flex;\n\n white-space: nowrap;\n overflow: hidden;\n background-color: var(--grid-header-background-color);\n border-bottom: var(--grid-header-bottom-border);\n padding: var(--grid-header-padding);\n\n text-overflow: ellipsis;\n font: var(--grid-header-font);\n /*\n * 글꼴은 따로 받는다 — 기본은 물려받는다(본문과 같은 글꼴).\n *\n * font 축약형은 글꼴 이름을 반드시 요구해서, 토큰 하나로 쓰면 굵기 · 크기와 글꼴이 묶인다.\n * 그 탓에 머리글이 고정폭으로 그려졌는데, **고정폭에는 한글 글자가 없다.** 대체 글꼴로 떨어지므로\n * 「ISA-95 계층 단」 한 줄 안에서 라틴과 한글이 서로 다른 글꼴이 됐다.\n *\n * 테마가 글꼴까지 정하고 싶으면 --grid-header-font-family 를 준다.\n */\n font-family: var(--grid-header-font-family, inherit);\n color: var(--grid-header-color);\n }\n\n /*\n * 거터 칸(순번 · 선택 · 더러움 표식)은 몸과 **같은 자리**에 있어야 한다. 머리의 체크박스와 각 줄의\n * 체크박스가 한 줄로 서지 않으면 표가 어긋나 보인다.\n *\n * 전에는 두 가지가 어긋나 있었다.\n * · text-align: center 로 가운데를 맞추려 했지만 이 칸은 display: flex 다 —\n * flex 안에서 text-align 은 항목을 옮기지 못한다. 그래서 왼쪽에 붙었다\n * · 여백을 몸(--grid-gutter-padding)과 **다른 토큰**(--spacing-small)에서 읽었다\n *\n * 그래서 정렬은 flex 로 하고, 여백은 몸과 같은 토큰을 본다.\n */\n div[gutter] {\n padding: var(--grid-gutter-padding);\n /*\n * 기본은 가운데. 거터마다 자기 정렬이 있으면 renderHeaderColumn 이 인라인으로 덮는다 —\n * 순번은 오른쪽이다(몸의 숫자가 오른쪽이므로 머리의 # 도 오른쪽이어야 자릿수가 맞아 보인다).\n */\n justify-content: center;\n }\n\n /*\n * 거터 칸의 내용물은 span[for-title] 안에 들어간다 — 열 제목이 쓰는 그 span 이다. 그래서 제목용\n * 규칙 둘을 함께 물려받는데, 거터에는 제목도 정렬 표식도 없으므로 둘 다 체크박스를 밀기만 한다.\n *\n * flex: 1 칸을 다 채워서, 칸의 justify-content 가 나눌 여백이 남지 않는다\n * margin-right 제목과 표식 사이를 벌리려던 값이라, 내용물이 그만큼 왼쪽으로 간다\n *\n * 둘을 거두면 span 이 내용물 크기로 줄고, 칸이 그것을 가운데로 놓는다. 입력의 기본 여백도 없애야\n * 몸의 체크박스와 정확히 같은 자리에 선다 — 몸은 여백 없이 가운데에 있다.\n */\n div[gutter] span[for-title] {\n flex: none;\n margin-right: 0;\n }\n\n div[gutter] input {\n margin: 0;\n }\n\n span {\n display: block;\n white-space: nowrap;\n overflow: hidden;\n }\n\n span[for-title] {\n /*\n * 이름이 자기 길이만큼만 차지한다 — 그러면 정렬 표식이 이름 바로 오른쪽에 붙는다.\n *\n * 고치는 표에서는 1 로 되돌린다(data-grid.ts 의 editable-grid 한 벌). 거기서는 칸 끝에 폭 조절\n * 손잡이가 서 있어서, 표식이 그 옆에 모여 있는 편이 낫다.\n *\n * **조건은 손잡이가 보이는지다.** 표식이 칸 오른쪽 끝에 있는데 그 오른쪽에 선이 없으면 눈이\n * 다음 칸의 것으로 읽는다. 손잡이가 있으면 그 선이 「여기까지가 이 칸」이라고 말해 주므로 끝에\n * 있어도 자기 칸의 것으로 읽힌다.\n *\n * 그래서 손잡이 토큰에서 직접 파생시킨다. 전에는 편집 여부에 걸고 두 토큰을 한 자리에서 함께\n * 정했는데, 그러면 테마가 한쪽만 건드릴 때 어긋난다.\n *\n * 기본은 **붙는 쪽**이다. 스타일 질의를 모르는 브라우저에서는 이 값이 남는데, 읽기 전용 표가\n * 다수라 그쪽이 옳은 기본이다(고치는 표에서는 표식이 이름 옆에 붙을 뿐이고 그건 흠이 아니다).\n *\n * 줄어듦은 항상 1 이다. 긴 이름이 줄어들 수 있어야 말줄임으로 잘린다 — 줄어들지 못하면 칸을\n * 넘어가 그냥 잘린다. 축약형(flex)에 var 을 쓰면 「0 1 auto」 가 「none」 으로 해석되는 일이\n * 있어서(실측 0 0 auto) 낱개 속성으로 적는다.\n */\n flex-grow: 0;\n flex-shrink: 1;\n flex-basis: auto;\n /* 표식 무리와 이름 사이를 벌린다 — 붙어 있으면 이름의 일부처럼 읽힌다. */\n margin-right: 6px;\n /* 작은 대문자 라벨로 쓸 때 자간이 필요하다. 테마가 정하지 않으면 0 이다. */\n letter-spacing: var(--grid-header-letter-spacing, normal);\n text-overflow: ellipsis;\n line-height: 1.6;\n text-transform: var(--grid-header-text-transform, capitalize);\n align-self: center;\n }\n\n /*\n * 손잡이가 보이면 이름이 칸을 채워 표식을 오른쪽 끝으로 민다 — 거기에는 손잡이가 서 있으므로\n * 표식이 그 옆에 모인다.\n *\n * none 이 아닌 값을 묻는다. 이 저장소에서 「보임」은 flex 뿐이지만, 테마가 inline-flex 나\n * block 을 쓸 수도 있다. 감춤은 none 하나뿐이라 그쪽을 부정하는 편이 좁다.\n */\n @container not style(--grid-header-splitter-display: none) {\n span[for-title] {\n flex-grow: 1;\n }\n }\n\n span[for-title] * {\n vertical-align: middle;\n }\n\n span[for-title] md-icon {\n font-size: 1.2em;\n }\n\n span[sorter],\n span[filter] {\n display: flex;\n align-self: center;\n\n padding: 0;\n border: 0;\n }\n\n span[sorter] md-icon {\n font-size: var(--grid-header-sorter-size);\n }\n\n /*\n * ── 머리에는 「고칠 수 있다」 표식을 두지 않는다 (2026-09-05) ────────\n * 칸마다 표식이 생긴 뒤로 머리의 연필은 같은 말을 두 번 하는 자리가 됐다. 게다가 머리의\n * 것은 **틀린 말을 할 수 있다** — editable 이 함수인 열에서 머리는 참·거짓을 가리지 못해\n * 늘 「고칠 수 있다」고 하고, 실제로는 한 줄도 못 고치는 표가 나온다. 칸의 표식은 그 함수를\n * 줄마다 불러 보고 정한다.\n *\n * 머리는 이름·정렬·거르기의 자리로 두고, 고치기는 고치는 자리에서 알린다.\n *\n * ── 정렬 표식 ──────────────────────────────────────────────────────\n * 정렬할 수 있다는 것은 아주 흐리게, 지금 정렬 중인 것만 한 단계 진하게 둔다.\n *\n * 색은 토큰으로 받되 currentColor 를 섞은 폴백을 둔다 — 테마가 값을 정하지 않아도 흐려지고\n * 라이트·다크 어느 쪽에서도 배경 대비가 유지된다.\n */\n md-icon[sortable-mark] {\n color: var(--grid-header-sortable-color, color-mix(in srgb, currentColor 18%, transparent));\n }\n\n /*\n * 지금 정렬 중이라는 표시는 **상태**다 — 흐리게 두면 무엇으로 정렬됐는지 화면이 말하지 않는\n * 것과 같다. 52% 로는 어두운 화면에서 2.86:1 이었다(실측 2026-09-05). 60% 면 밝은 화면\n * 4.7 · 어두운 화면 5.4 로 UI 요소 기준(3:1)을 넘는다.\n *\n * 「정렬할 수 있다」(sortable-mark)는 그 옆에서 계속 흐리게 둔다. 그것은 상태가 아니라\n * 곁들이는 안내이고, 머리글 자체가 눌리는 자리라 길은 따로 있다.\n */\n span[sorter] md-icon:not([sortable-mark]),\n span[sorter] sub {\n color: var(--grid-header-sorted-color, color-mix(in srgb, currentColor 60%, transparent));\n }\n\n span[filter] > md-icon {\n font-size: var(--fontsize-default);\n line-height: 20px;\n }\n\n /*\n * 열 폭 조절 손잡이 — **세로 선을 그리는 것이 이것이다.**\n *\n * 표 안의 세로 선은 이 손잡이의 오른쪽 테두리다(칸 자체는 위아래 테두리만 갖는다). 그래서 이\n * 손잡이를 감추면 열 폭 조절과 세로 격자가 함께 사라진다 — 그 둘은 원래 한 물건이다.\n *\n * 고치지 않는 목록에서는 열 폭을 끌어 옮길 일이 없고, 세로 선이 없으면 표가 격자가 아니라\n * 줄로 읽힌다. 그럴 때 테마가 이렇게 정한다.\n *\n * --grid-header-splitter-display: none\n *\n * 값을 정하지 않으면 예전 그대로다 — 편집용 표는 손잡이가 있어야 한다.\n */\n span[splitter] {\n display: var(--grid-header-splitter-display, flex);\n cursor: col-resize;\n border-right: var(--grid-header-splitter-border);\n margin-right: 0;\n }\n\n /*\n * 손잡이가 있는 칸은 오른쪽 여백을 두지 않는다.\n *\n * 손잡이는 flex 항목이라 칸의 padding 안쪽에 놓인다. 좌우 여백이 2px 이던 동안에는 그 차이가\n * 안 보였지만, 여백을 10px 로 올리자 손잡이가 열 경계에서 10px 당겨졌다 — 세로 선이 경계가\n * 아닌 자리에 그려진 것이다.\n *\n * 그래서 손잡이가 있을 때만 오른쪽 여백을 거둔다. 손잡이가 없는 칸(거터 · 폭을 못 바꾸는 열)은\n * 여백을 그대로 둬야 글자가 경계에 닿지 않는다.\n *\n * 세로 길이는 손대지 않는다 — flex 가 늘려 주는 그대로, 칸의 위아래 여백만큼 들어간 짧은 선이다.\n */\n div[column]:has(span[splitter]) {\n padding-right: 0;\n }\n\n span[splitter]:hover {\n border-right: var(--grid-header-splitter-border-hover);\n }\n\n [filter-title] {\n color: var(--grid-header-filter-title-color);\n font: var(--grid-header-filter-title-font);\n text-transform: capitalize;\n }\n\n [filter-title] * {\n vertical-align: middle;\n }\n\n [filter-title] md-icon {\n opacity: 0.7;\n color: var(--grid-header-filter-title-icon-color);\n }\n\n [filter] input[type='checkbox'] {\n margin-left: var(--spacing-medium);\n margin-bottom: var(--spacing-small);\n }\n\n [fixed] {\n position: sticky;\n z-index: 2;\n }\n\n .span-both {\n grid-row: 1 / span 2; /* 1단과 2단에 걸쳐 표시 */\n }\n\n .group-header {\n grid-row: 1; /* 1행에 배치 */\n grid-column: var(--group-start) / span var(--group-size); /* 2열부터 시작하여 2열에 걸쳐 표시 */\n }\n\n @media print {\n :host {\n grid-template-columns: var(--grid-template-print-columns);\n }\n }\n\n @media (max-width: 768px) {\n /* 모바일에서는 그리드에서 고정된 열 해제 */\n [fixed] {\n position: static;\n z-index: 0;\n box-shadow: none !important;\n }\n }\n `\n ]\n\n @property({ type: Array }) columns: ColumnConfig[] = ZERO_COLUMNS\n @property({ type: Object }) data: GristData = ZERO_DATA\n @property({ type: Array }) sorters: SortersConfig = []\n @property({ type: Array }) filters: FilterValue[] = []\n @property({ type: Boolean, attribute: 'filtering-feature' }) filteringFeature: boolean = false\n\n @state() private row1: {\n index: number\n column: ColumnConfig\n clazz?: string\n start?: number\n size?: number\n align?: string\n group?: string\n }[] = []\n\n @state() private row2: {\n index: number\n column: ColumnConfig\n }[] = []\n\n private _lastAccVal?: number\n private _throttledNotifier?: any\n\n private tooltipController?: TooltipReactiveController = new TooltipReactiveController(this)\n\n connectedCallback() {\n super.connectedCallback()\n\n const grid = closestElement('ox-grist', this)\n\n this.addEventListener('scroll', function () {\n this.scrollLeft == 0 ? this.removeAttribute('raised') : this.setAttribute('raised', '')\n })\n\n if (this.filteringFeature) {\n this?.addEventListener('filter-change', (e: Event) => {\n const { name, operator, value } = (e as CustomEvent).detail\n const filters = this.filters instanceof Array ? [...this.filters] : []\n\n if (value == null) {\n const index = filters.findIndex(filter => filter.name === name)\n if (index === -1) {\n return\n }\n\n filters.splice(index, 1)\n } else {\n const index = filters.findIndex(filter => filter.name === name)\n if (index === -1) {\n filters.push({ name, operator, value })\n } else {\n filters.splice(index, 1, { name, operator, value })\n }\n }\n\n grid?.dispatchEvent(\n new CustomEvent('fetch-params-change', {\n detail: {\n filters,\n from: 'data-grid-header'\n }\n })\n )\n })\n }\n }\n\n render() {\n const clazz = this.row2.length > 0 ? 'span-both' : undefined\n\n return html`\n ${this.row1.map(({ column, clazz, start, size, align, index, group }) =>\n this.renderHeaderColumn({ column, clazz, start, size, align, index, group })\n )}\n\n <div column class=${ifDefined(clazz)}></div>\n\n ${this.row2.map(({ column, index }) => this.renderHeaderColumn({ column, index }))}\n `\n }\n\n renderHeaderColumn({ column, clazz, start, size, align, index, group }: any): TemplateResult {\n if (column.hidden) {\n return html`${nothing}`\n }\n\n /*\n * 거터 머리는 flex 로 정렬한다.\n *\n * 아래 span 의 `text-align` 은 거터에서 듣지 않는다 — 그 span 이 내용물 크기로 줄어 있어서\n * (위 div[gutter] 머리말 참조) 글자를 밀 여백이 span 안에 없다. 그래서 칸이 밀어야 한다.\n *\n * 값은 그 거터가 이미 선언한 것을 본다. 순번은 right, 선택과 더러움 표식은 center 다. 여기에 따로\n * 적으면 거터를 하나 더할 때 두 곳을 맞춰야 한다.\n */\n /*\n * 정렬은 **칸이 flex 로 민다.** 아래 span 의 text-align 에 기대지 않는다.\n *\n * 그 span 은 이름 길이만큼만 차지하므로(위 span[for-title] 머리말) 안에 글자를 밀 여백이 없다.\n * 그래서 text-align 이 아무 일도 하지 않는다 — 숫자 칸의 값은 오른쪽인데 머리글만 왼쪽에\n * 남았다. [재봤음] 「수량」 머리 x=391 · 값 오른쪽 끝 x=402. 2026-09-02\n *\n * 칸이 밀면 이름과 표식이 **함께** 그 방향으로 간다. 그래서 오른쪽 정렬 칸에서도 표식이 이름\n * 옆에 붙어 있다.\n *\n * 거터의 기본은 가운데(체크박스 · 더러움 표식), 보통 칸의 기본은 왼쪽이다.\n */\n const fallback = column.type == 'gutter' ? 'center' : 'flex-start'\n const justify = `justify-content:${ALIGN_JUSTIFY[(align || column.record?.align) as keyof typeof ALIGN_JUSTIFY] || fallback};`\n\n return html`\n <div\n ?gutter=${column.type == 'gutter'}\n ?fixed=${column.fixed}\n column\n class=${ifDefined(clazz)}\n style=${group\n ? `--group-start:${start};--group-size:${size};${justify}${column.header?.style || ''}`\n : `${justify}${column.header?.style || ''}`}\n >\n <span\n for-title\n data-reactive-tooltip\n style=\"text-align:${align || column.record.align || 'left'};\"\n @click=${(e: MouseEvent) => this._changeSort(column)}\n >${this._renderHeader(column)}\n </span>\n\n ${!group && column.sortable\n ? html`\n <span sorter @click=${(e: MouseEvent) => this._changeSort(column)}>\n ${this._renderSortHeader(column)}\n </span>\n `\n : nothing}\n ${!group &&\n this.filteringFeature &&\n column.filter &&\n (column.filter as FilterConfigObject).operator !== 'search'\n ? html` <span filter> ${this._renderFilterHeader(column)} </span> `\n : nothing}\n ${column.resizable !== false\n ? html`\n <span splitter draggable=\"false\" @pointerdown=${(e: PointerEvent) => this._pointerdown(e, index)}\n > </span\n >\n `\n : nothing}\n </div>\n `\n }\n\n notifyFixedLeftChange() {\n const fixedHeaders = Array.from(this.renderRoot.querySelectorAll('div[fixed]')) as HTMLElement[]\n const fixedLefts = [] as number[]\n var left = 0\n fixedHeaders.forEach((header: HTMLElement) => {\n header.style.left = left + 'px'\n fixedLefts.push(left)\n\n const width = header.offsetWidth\n left += width\n })\n\n this.dispatchEvent(\n new CustomEvent('fixed-lefts-change', {\n detail: fixedLefts\n })\n )\n }\n\n updated(changes: PropertyValues) {\n var isColumnWidthChangePossible = false\n\n if (changes.has('columns')) {\n this.row2 = this.columns.reduce((row2, column, index) => {\n if (column.hidden || !column.header?.group) {\n return row2\n }\n return row2.concat({\n index,\n column\n } as any)\n }, [] as any[])\n\n const clazz = this.row2.length > 0 ? 'span-both' : undefined\n\n var columnNo = 0\n\n this.row1 = this.columns.reduce((row1, column, index) => {\n if (column.hidden) {\n return row1\n }\n\n columnNo++\n if (!column.header?.group) {\n return row1.concat({\n index,\n column,\n clazz\n } as any)\n }\n const { group, groupStyle } = column.header\n const last = row1[row1.length - 1] as any\n\n if (!last || group !== last.group) {\n return row1.concat({\n index,\n column: {\n ...column,\n header: {\n renderer: () => group,\n style: groupStyle\n }\n },\n group,\n align: 'center',\n clazz: 'group-header',\n start: columnNo,\n size: 1\n } as any)\n }\n\n last.size++\n\n return row1\n }, [] as any[])\n\n isColumnWidthChangePossible = true\n }\n\n if (changes.has('data')) {\n isColumnWidthChangePossible = true\n }\n\n if (isColumnWidthChangePossible) {\n // requestAnimationFrame(() => this.notifyFixedLeftChange())\n this.notifyFixedLeftChange()\n }\n }\n\n _renderHeader(column: ColumnConfig) {\n var { renderer } = column.header || {}\n var title = renderer.call(this, column)\n\n return html`${column?.record?.mandatory ? '*' : ''} ${title} `\n }\n\n _renderSortHeader(column: ColumnConfig) {\n var sorters = this.sorters || []\n\n var sorter = sorters.find(sorter => column.type !== 'gutter' && column.name == sorter.name)\n if (!sorter) {\n return html`<md-icon sortable-mark>unfold_more</md-icon>`\n }\n\n if (sorters.length > 1) {\n var rank = sorters.indexOf(sorter) + 1\n return sorter.desc\n ? html` <md-icon>keyboard_arrow_down</md-icon><sub>${rank}</sub> `\n : html` <md-icon>keyboard_arrow_up</md-icon><sub>${rank}</sub> `\n } else {\n return sorter.desc ? html` <md-icon>keyboard_arrow_down</md-icon> ` : html` <md-icon>keyboard_arrow_up</md-icon> `\n }\n }\n\n _renderFilterHeader(column: ColumnConfig) {\n const name = column.name\n const filter = column.filter as FilterConfigObject\n const type = filter.type\n const value = this.filters.find(filter => filter.name === name)?.value\n const idx = filter!.operator === 'between' ? 1 : 0\n const renderer = getFilterRenderer(type)[idx]\n\n return html`\n <md-icon\n @click=${(e: Event) => {\n const parent = (e.target as HTMLElement).closest('[column]') as HTMLElement\n const popup = parent.querySelector('ox-popup, ox-popup-list') as OxPopup | null\n // const popup = (e.target as HTMLElement).nextSibling as OxPopupList | null\n\n // absolute position인 popup의 위치 부모는 grist 이므로,\n // data-grid-header 의 포지션 부모(grist)의 위치로부터 계산해야함.\n // this의 position을 relative로 하지 못하는 이유 : ox-popup-list가 grid body에 덮히기 때문.\n // const top = parent.offsetTop + parent.offsetHeight\n // const right = this.clientWidth - (parent.offsetLeft + parent.offsetWidth - this.scrollLeft)\n\n popup?.open({\n right: 0,\n top: parent.offsetHeight,\n fixed: true\n })\n }}\n >filter_alt</md-icon\n >\n\n ${!renderer\n ? html``\n : type !== 'select'\n ? html` <ox-popup\n ><div filter-title><md-icon>filter_alt</md-icon> filter by <strong>${column.name}</strong></div>\n ${renderer(column, value, this)}</ox-popup\n >`\n : filter!.operator === 'in'\n ? html`<ox-popup-list\n multiple\n attr-selected=\"checked\"\n .value=${value}\n with-search\n @select=${(e: CustomEvent) =>\n e.target?.dispatchEvent(\n new CustomEvent('filter-change', {\n bubbles: true,\n composed: true,\n detail: {\n name,\n operator: filter!.operator,\n value: !e.detail\n ? undefined\n : e.detail instanceof Array && e.detail.length === 0\n ? undefined\n : e.detail\n }\n })\n )}\n ><div filter-title slot=\"header\">\n <md-icon>filter_alt</md-icon> filter by <strong>${column.name}</strong>\n </div>\n ${renderer(column, value, this)}</ox-popup-list\n >`\n : html`<ox-popup-list\n .value=${value}\n with-search\n @select=${(e: CustomEvent) =>\n e.target?.dispatchEvent(\n new CustomEvent('filter-change', {\n bubbles: true,\n composed: true,\n detail: {\n name,\n operator: filter!.operator,\n value: e.detail ? e.detail : undefined\n }\n })\n )}\n ><div filter-title slot=\"header\">\n <md-icon>filter_alt</md-icon> filter by <strong>${column.name}</strong>\n </div>\n ${renderer(column, value, this)}</ox-popup-list\n >`}\n `\n }\n\n _changeSort(column: ColumnConfig) {\n if (!column.sortable) {\n return\n }\n\n var sorters = [...this.sorters]\n\n var idx = sorters.findIndex(sorter => sorter.name == column.name)\n if (idx !== -1) {\n let sorter = sorters[idx]\n if (sorter.desc) {\n sorters.splice(idx, 1)\n } else {\n sorters.splice(idx, 1, {\n ...sorter,\n desc: true\n })\n }\n } else {\n var sorter = {\n name: column.name,\n desc: false\n }\n\n sorters.push(sorter)\n }\n\n this.sorters = sorters\n\n this.dispatchEvent(\n new CustomEvent('fetch-params-change', {\n bubbles: true,\n composed: true,\n detail: {\n sorters: this.sorters,\n from: 'data-grid-header'\n }\n })\n )\n }\n\n _accumalate(x: number) {\n this._lastAccVal = (this._lastAccVal ?? 0) + x\n return this._lastAccVal\n }\n\n _notifyWidthChange(idx: number, width: number) {\n if (!this._throttledNotifier) {\n this._throttledNotifier = throttle((idx: number, width: number) => {\n this.dispatchEvent(\n new CustomEvent('column-width-change', {\n bubbles: true,\n composed: true,\n detail: {\n idx,\n width\n }\n })\n )\n\n this.notifyFixedLeftChange()\n\n this._lastAccVal = 0\n }, 100)\n }\n\n this._throttledNotifier(idx, width)\n }\n\n _pointerdown(e: PointerEvent, idx: number) {\n e.stopPropagation()\n e.preventDefault()\n ;(e.target as HTMLElement).setPointerCapture(e.pointerId)\n\n var pointermoveHandler = ((e: PointerEvent) => {\n e.stopPropagation()\n e.preventDefault()\n let column = this.columns[idx]\n\n let width = Math.max(0, Number(column.width || 100) + this._accumalate(e.movementX))\n if (width == 0) {\n /* CLARIFY-ME 왜 마지막 이벤트의 offsetX로 음수 값이 오는가 */\n return\n }\n\n this._notifyWidthChange(idx, width)\n }).bind(this)\n\n var pointerupHandler = ((e: PointerEvent) => {\n ;(e.target as HTMLElement).releasePointerCapture(e.pointerId)\n\n document.removeEventListener('pointermove', pointermoveHandler)\n document.removeEventListener('pointerup', pointerupHandler)\n\n pointermoveHandler(e)\n }).bind(this)\n\n document.addEventListener('pointermove', pointermoveHandler)\n document.addEventListener('pointerup', pointerupHandler)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"data-grid-header.js","sourceRoot":"","sources":["../../../src/data-grid/data-grid-header.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/popup/ox-popup.js'\nimport '@material/web/icon/icon.js'\n\nimport i18next from 'i18next'\nimport { css, html, LitElement, PropertyValues, nothing, TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\nimport throttle from 'lodash-es/throttle.js'\n\nimport { OxPopup } from '@operato/popup'\nimport { TooltipReactiveController, closestElement } from '@operato/utils'\n\nimport { ZERO_COLUMNS, ZERO_DATA } from '../configure/zero-config.js'\nimport { FilterStyles } from '../filters/filter-styles.js'\nimport { getFilterRenderer } from '../filters/registry.js'\nimport { ColumnConfig, FilterConfigObject, FilterValue, GristData, SortersConfig } from '../types.js'\n\n/** 칸의 정렬 선언(record.align)을 flex 의 말로 옮긴다. */\nconst ALIGN_JUSTIFY = {\n left: 'flex-start',\n center: 'center',\n right: 'flex-end'\n} as const\n\n@customElement('ox-grid-header')\nexport class DataGridHeader extends LitElement {\n static styles = [\n FilterStyles,\n css`\n :host {\n position: relative;\n display: grid;\n grid-template-columns: var(--grid-template-columns);\n\n border-top: var(--grid-header-top-border);\n\n /*\n * 가로는 감춘다 — 머리를 사람이 직접 끌지는 않는다. 몸을 따라 프로그램이 옮긴다.\n *\n * 세로는 몸과 같은 만큼 자리를 비운다. 몸만 스크롤바에 자리를 내주면 갈 수 있는 거리가\n * 달라져서, 오른쪽 끝에서 몸이 그 폭만큼 더 간다(몸의 scrollbar-gutter 주석 참조).\n * 머리에는 세로로 넘칠 것이 없으므로 손잡이는 그려지지 않고 자리만 남는다.\n */\n overflow-x: hidden;\n overflow-y: scroll;\n scrollbar-gutter: stable;\n\n font-variation-settings: 'FILL' 1;\n\n user-select: none;\n }\n\n :host([raised]) [fixed] {\n /* 고정 컬럼이 살짝 올라와 있는 느낌을 표현 */\n box-shadow: 3px 0 3px rgba(0, 0, 0, 0.1);\n }\n\n div[column] {\n display: flex;\n\n white-space: nowrap;\n overflow: hidden;\n background-color: var(--grid-header-background-color);\n border-bottom: var(--grid-header-bottom-border);\n padding: var(--grid-header-padding);\n\n text-overflow: ellipsis;\n font: var(--grid-header-font);\n /*\n * 글꼴은 따로 받는다 — 기본은 물려받는다(본문과 같은 글꼴).\n *\n * font 축약형은 글꼴 이름을 반드시 요구해서, 토큰 하나로 쓰면 굵기 · 크기와 글꼴이 묶인다.\n * 그 탓에 머리글이 고정폭으로 그려졌는데, **고정폭에는 한글 글자가 없다.** 대체 글꼴로 떨어지므로\n * 「ISA-95 계층 단」 한 줄 안에서 라틴과 한글이 서로 다른 글꼴이 됐다.\n *\n * 테마가 글꼴까지 정하고 싶으면 --grid-header-font-family 를 준다.\n */\n font-family: var(--grid-header-font-family, inherit);\n color: var(--grid-header-color);\n }\n\n /*\n * 거터 칸(순번 · 선택 · 더러움 표식)은 몸과 **같은 자리**에 있어야 한다. 머리의 체크박스와 각 줄의\n * 체크박스가 한 줄로 서지 않으면 표가 어긋나 보인다.\n *\n * 전에는 두 가지가 어긋나 있었다.\n * · text-align: center 로 가운데를 맞추려 했지만 이 칸은 display: flex 다 —\n * flex 안에서 text-align 은 항목을 옮기지 못한다. 그래서 왼쪽에 붙었다\n * · 여백을 몸(--grid-gutter-padding)과 **다른 토큰**(--spacing-small)에서 읽었다\n *\n * 그래서 정렬은 flex 로 하고, 여백은 몸과 같은 토큰을 본다.\n */\n div[gutter] {\n padding: var(--grid-gutter-padding);\n /*\n * 기본은 가운데. 거터마다 자기 정렬이 있으면 renderHeaderColumn 이 인라인으로 덮는다 —\n * 순번은 오른쪽이다(몸의 숫자가 오른쪽이므로 머리의 # 도 오른쪽이어야 자릿수가 맞아 보인다).\n */\n justify-content: center;\n }\n\n /*\n * 거터 칸의 내용물은 span[for-title] 안에 들어간다 — 열 제목이 쓰는 그 span 이다. 그래서 제목용\n * 규칙 둘을 함께 물려받는데, 거터에는 제목도 정렬 표식도 없으므로 둘 다 체크박스를 밀기만 한다.\n *\n * flex: 1 칸을 다 채워서, 칸의 justify-content 가 나눌 여백이 남지 않는다\n * margin-right 제목과 표식 사이를 벌리려던 값이라, 내용물이 그만큼 왼쪽으로 간다\n *\n * 둘을 거두면 span 이 내용물 크기로 줄고, 칸이 그것을 가운데로 놓는다. 입력의 기본 여백도 없애야\n * 몸의 체크박스와 정확히 같은 자리에 선다 — 몸은 여백 없이 가운데에 있다.\n */\n div[gutter] span[for-title] {\n flex: none;\n margin-right: 0;\n }\n\n div[gutter] input {\n margin: 0;\n }\n\n span {\n display: block;\n white-space: nowrap;\n overflow: hidden;\n }\n\n span[for-title] {\n /*\n * 이름이 자기 길이만큼만 차지한다 — 그러면 정렬 표식이 이름 바로 오른쪽에 붙는다.\n *\n * 고치는 표에서는 1 로 되돌린다(data-grid.ts 의 editable-grid 와 같은 값). 거기서는 칸 끝에 폭 조절\n * 손잡이가 서 있어서, 표식이 그 옆에 모여 있는 편이 낫다.\n *\n * **조건은 손잡이가 보이는지다.** 표식이 칸 오른쪽 끝에 있는데 그 오른쪽에 선이 없으면 눈이\n * 다음 칸의 것으로 읽는다. 손잡이가 있으면 그 선이 「여기까지가 이 칸」이라고 말해 주므로 끝에\n * 있어도 자기 칸의 것으로 읽힌다.\n *\n * 그래서 손잡이 토큰에서 직접 파생시킨다. 전에는 편집 여부에 걸고 두 토큰을 한 자리에서 함께\n * 정했는데, 그러면 테마가 한쪽만 건드릴 때 어긋난다.\n *\n * 기본은 **붙는 쪽**이다. 스타일 질의를 모르는 브라우저에서는 이 값이 남는데, 읽기 전용 표가\n * 다수라 그쪽이 옳은 기본이다(고치는 표에서는 표식이 이름 옆에 붙을 뿐이고 그건 흠이 아니다).\n *\n * 줄어듦은 항상 1 이다. 긴 이름이 줄어들 수 있어야 말줄임으로 잘린다 — 줄어들지 못하면 칸을\n * 넘어가 그냥 잘린다. 축약형(flex)에 var 을 쓰면 「0 1 auto」 가 「none」 으로 해석되는 일이\n * 있어서(실측 0 0 auto) 낱개 속성으로 적는다.\n */\n flex-grow: 0;\n flex-shrink: 1;\n flex-basis: auto;\n /* 표식 무리와 이름 사이를 벌린다 — 붙어 있으면 이름의 일부처럼 읽힌다. */\n margin-right: 6px;\n /* 작은 대문자 라벨로 쓸 때 자간이 필요하다. 테마가 정하지 않으면 0 이다. */\n letter-spacing: var(--grid-header-letter-spacing, normal);\n text-overflow: ellipsis;\n line-height: 1.6;\n text-transform: var(--grid-header-text-transform, capitalize);\n align-self: center;\n }\n\n /*\n * 손잡이가 보이면 이름이 칸을 채워 표식을 오른쪽 끝으로 민다 — 거기에는 손잡이가 서 있으므로\n * 표식이 그 옆에 모인다.\n *\n * none 이 아닌 값을 묻는다. 이 저장소에서 「보임」은 flex 뿐이지만, 테마가 inline-flex 나\n * block 을 쓸 수도 있다. 감춤은 none 하나뿐이라 그쪽을 부정하는 편이 좁다.\n */\n @container not style(--grid-header-splitter-display: none) {\n span[for-title] {\n flex-grow: 1;\n }\n }\n\n span[for-title] * {\n vertical-align: middle;\n }\n\n span[for-title] md-icon {\n font-size: 1.2em;\n }\n\n span[sorter],\n span[filter] {\n display: flex;\n align-self: center;\n\n padding: 0;\n border: 0;\n }\n\n span[sorter] md-icon {\n font-size: var(--grid-header-sorter-size);\n }\n\n /*\n * ── 머리에는 「고칠 수 있다」 표식을 두지 않는다 (2026-09-05) ────────\n * 칸마다 표식이 생긴 뒤로 머리의 연필은 같은 말을 두 번 하는 자리가 됐다. 게다가 머리의\n * 것은 **틀린 말을 할 수 있다** — editable 이 함수인 열에서 머리는 참·거짓을 가리지 못해\n * 늘 「고칠 수 있다」고 하고, 실제로는 한 줄도 못 고치는 표가 나온다. 칸의 표식은 그 함수를\n * 줄마다 불러 보고 정한다.\n *\n * 머리는 이름·정렬·거르기의 자리로 두고, 고치기는 고치는 자리에서 알린다.\n *\n * ── 정렬 표식 ──────────────────────────────────────────────────────\n * 정렬할 수 있다는 것은 아주 흐리게, 지금 정렬 중인 것만 한 단계 진하게 둔다.\n *\n * 색은 토큰으로 받되 currentColor 를 섞은 폴백을 둔다 — 테마가 값을 정하지 않아도 흐려지고\n * 라이트·다크 어느 쪽에서도 배경 대비가 유지된다.\n */\n md-icon[sortable-mark] {\n color: var(--grid-header-sortable-color, color-mix(in srgb, currentColor 18%, transparent));\n }\n\n /*\n * 지금 정렬 중이라는 표시는 **상태**다 — 흐리게 두면 무엇으로 정렬됐는지 화면이 말하지 않는\n * 것과 같다. 52% 로는 어두운 화면에서 2.86:1 이었다(실측 2026-09-05). 60% 면 밝은 화면\n * 4.7 · 어두운 화면 5.4 로 UI 요소 기준(3:1)을 넘는다.\n *\n * 「정렬할 수 있다」(sortable-mark)는 그 옆에서 계속 흐리게 둔다. 그것은 상태가 아니라\n * 곁들이는 안내이고, 머리글 자체가 눌리는 자리라 길은 따로 있다.\n */\n span[sorter] md-icon:not([sortable-mark]),\n span[sorter] sub {\n color: var(--grid-header-sorted-color, color-mix(in srgb, currentColor 60%, transparent));\n }\n\n span[filter] > md-icon {\n font-size: var(--fontsize-default);\n line-height: 20px;\n }\n\n /*\n * 열 폭 조절 손잡이 — **세로 선을 그리는 것이 이것이다.**\n *\n * 표 안의 세로 선은 이 손잡이의 오른쪽 테두리다(칸 자체는 위아래 테두리만 갖는다). 그래서 이\n * 손잡이를 감추면 열 폭 조절과 세로 격자가 함께 사라진다 — 그 둘은 원래 한 물건이다.\n *\n * 고치지 않는 목록에서는 열 폭을 끌어 옮길 일이 없고, 세로 선이 없으면 표가 격자가 아니라\n * 줄로 읽힌다. 그럴 때 테마가 이렇게 정한다.\n *\n * --grid-header-splitter-display: none\n *\n * 값을 정하지 않으면 예전 그대로다 — 편집용 표는 손잡이가 있어야 한다.\n */\n span[splitter] {\n display: var(--grid-header-splitter-display, flex);\n cursor: col-resize;\n border-right: var(--grid-header-splitter-border);\n margin-right: 0;\n }\n\n /*\n * 손잡이가 있는 칸은 오른쪽 여백을 두지 않는다.\n *\n * 손잡이는 flex 항목이라 칸의 padding 안쪽에 놓인다. 좌우 여백이 2px 이던 동안에는 그 차이가\n * 안 보였지만, 여백을 10px 로 올리자 손잡이가 열 경계에서 10px 당겨졌다 — 세로 선이 경계가\n * 아닌 자리에 그려진 것이다.\n *\n * 그래서 손잡이가 있을 때만 오른쪽 여백을 거둔다. 손잡이가 없는 칸(거터 · 폭을 못 바꾸는 열)은\n * 여백을 그대로 둬야 글자가 경계에 닿지 않는다.\n *\n * 세로 길이는 손대지 않는다 — flex 가 늘려 주는 그대로, 칸의 위아래 여백만큼 들어간 짧은 선이다.\n */\n div[column]:has(span[splitter]) {\n padding-right: 0;\n }\n\n span[splitter]:hover {\n border-right: var(--grid-header-splitter-border-hover);\n }\n\n [filter-title] {\n color: var(--grid-header-filter-title-color);\n font: var(--grid-header-filter-title-font);\n text-transform: capitalize;\n }\n\n [filter-title] * {\n vertical-align: middle;\n }\n\n [filter-title] md-icon {\n opacity: 0.7;\n color: var(--grid-header-filter-title-icon-color);\n }\n\n [filter] input[type='checkbox'] {\n margin-left: var(--spacing-medium);\n margin-bottom: var(--spacing-small);\n }\n\n [fixed] {\n position: sticky;\n z-index: 2;\n }\n\n .span-both {\n grid-row: 1 / span 2; /* 1단과 2단에 걸쳐 표시 */\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 .group-header {\n grid-row: 1; /* 1행에 배치 */\n grid-column: var(--group-start) / span var(--group-size); /* 2열부터 시작하여 2열에 걸쳐 표시 */\n }\n\n @media print {\n :host {\n grid-template-columns: var(--grid-template-print-columns);\n }\n }\n\n @media (max-width: 768px) {\n /* 모바일에서는 그리드에서 고정된 열 해제 */\n [fixed] {\n position: static;\n z-index: 0;\n box-shadow: none !important;\n }\n }\n `\n ]\n\n @property({ type: Array }) columns: ColumnConfig[] = ZERO_COLUMNS\n @property({ type: Object }) data: GristData = ZERO_DATA\n @property({ type: Array }) sorters: SortersConfig = []\n @property({ type: Array }) filters: FilterValue[] = []\n @property({ type: Boolean, attribute: 'filtering-feature' }) filteringFeature: boolean = false\n\n @state() private row1: {\n index: number\n /** 보이는 열 중 몇 번째인가(1부터) — aria-colindex 가 이 값을 쓴다. */\n colIndex?: number\n column: ColumnConfig\n clazz?: string\n start?: number\n size?: number\n align?: string\n group?: string\n }[] = []\n\n @state() private row2: {\n index: number\n /** 보이는 열 중 몇 번째인가(1부터) — aria-colindex 가 이 값을 쓴다. */\n colIndex?: number\n column: ColumnConfig\n }[] = []\n\n private _lastAccVal?: number\n private _throttledNotifier?: any\n\n private tooltipController?: TooltipReactiveController = new TooltipReactiveController(this)\n\n connectedCallback() {\n super.connectedCallback()\n\n const grid = closestElement('ox-grist', this)\n\n this.addEventListener('scroll', function () {\n this.scrollLeft == 0 ? this.removeAttribute('raised') : this.setAttribute('raised', '')\n })\n\n if (this.filteringFeature) {\n this?.addEventListener('filter-change', (e: Event) => {\n const { name, operator, value } = (e as CustomEvent).detail\n const filters = this.filters instanceof Array ? [...this.filters] : []\n\n if (value == null) {\n const index = filters.findIndex(filter => filter.name === name)\n if (index === -1) {\n return\n }\n\n filters.splice(index, 1)\n } else {\n const index = filters.findIndex(filter => filter.name === name)\n if (index === -1) {\n filters.push({ name, operator, value })\n } else {\n filters.splice(index, 1, { name, operator, value })\n }\n }\n\n grid?.dispatchEvent(\n new CustomEvent('fetch-params-change', {\n detail: {\n filters,\n from: 'data-grid-header'\n }\n })\n )\n })\n }\n }\n\n /*\n * ── 머리줄을 **줄이라고 말한다** ──────────────────────────────────────────\n *\n * 이 머리는 CSS grid 라 칸들이 평평하게 놓여 있었다 — 보기에는 한 줄(묶음이 있으면 두 줄)이지만\n * DOM 에는 줄이 없다. 눈으로는 같은 높이에 있는 것이 한 줄로 읽히는데, 그것은 그림일 뿐이다.\n *\n * role=row 를 담을 자리가 필요해서 감싸개를 둔다. `display: contents` 라 상자를 만들지 않으므로\n * 칸들은 그대로 grid 의 항목으로 남는다 — 열 폭 · 고정 열 · 묶음 머리의 grid-column 배치가 하나도\n * 바뀌지 않는다.\n */\n render() {\n const grouped = this.row2.length > 0\n const clazz = grouped ? 'span-both' : undefined\n\n return html`\n <div grid-row role=\"row\" aria-rowindex=\"1\">\n ${this.row1.map(({ column, clazz, start, size, align, index, group, colIndex }) =>\n this.renderHeaderColumn({ column, clazz, start, size, align, index, group, colIndex, grouped })\n )}\n\n <!--\n 남는 폭을 메우는 칸이다. 읽을 것이 없으므로 표의 칸으로 세지 않는다 — 세면 열 수가 하나\n 더 많다고 말하게 된다.\n -->\n <div column role=\"presentation\" class=${ifDefined(clazz)}></div>\n </div>\n\n ${grouped\n ? html`\n <div grid-row role=\"row\" aria-rowindex=\"2\">\n ${this.row2.map(({ column, index, colIndex }) =>\n this.renderHeaderColumn({ column, index, colIndex, grouped })\n )}\n </div>\n `\n : nothing}\n `\n }\n\n renderHeaderColumn({ column, clazz, start, size, align, index, group, colIndex, grouped }: any): TemplateResult {\n if (column.hidden) {\n return html`${nothing}`\n }\n\n /*\n * 거터 머리는 flex 로 정렬한다.\n *\n * 아래 span 의 `text-align` 은 거터에서 듣지 않는다 — 그 span 이 내용물 크기로 줄어 있어서\n * (위 div[gutter] 머리말 참조) 글자를 밀 여백이 span 안에 없다. 그래서 칸이 밀어야 한다.\n *\n * 값은 그 거터가 이미 선언한 것을 본다. 순번은 right, 선택과 더러움 표식은 center 다. 여기에 따로\n * 적으면 거터를 하나 더할 때 두 곳을 맞춰야 한다.\n */\n /*\n * 정렬은 **칸이 flex 로 민다.** 아래 span 의 text-align 에 기대지 않는다.\n *\n * 그 span 은 이름 길이만큼만 차지하므로(위 span[for-title] 머리말) 안에 글자를 밀 여백이 없다.\n * 그래서 text-align 이 아무 일도 하지 않는다 — 숫자 칸의 값은 오른쪽인데 머리글만 왼쪽에\n * 남았다. [확인함] 「수량」 머리 x=391 · 값 오른쪽 끝 x=402. 2026-09-02\n *\n * 칸이 밀면 이름과 표식이 **함께** 그 방향으로 간다. 그래서 오른쪽 정렬 칸에서도 표식이 이름\n * 옆에 붙어 있다.\n *\n * 거터의 기본은 가운데(체크박스 · 더러움 표식), 보통 칸의 기본은 왼쪽이다.\n */\n const fallback = column.type == 'gutter' ? 'center' : 'flex-start'\n const justify = `justify-content:${ALIGN_JUSTIFY[(align || column.record?.align) as keyof typeof ALIGN_JUSTIFY] || fallback};`\n\n return html`\n <div\n ?gutter=${column.type == 'gutter'}\n ?fixed=${column.fixed}\n column\n role=\"columnheader\"\n aria-colindex=${ifDefined(group ? start : colIndex)}\n aria-colspan=${ifDefined(group ? size : undefined)}\n aria-rowspan=${ifDefined(clazz === 'span-both' ? 2 : undefined)}\n aria-sort=${ifDefined(this._ariaSort(column, group))}\n class=${ifDefined(clazz)}\n style=${group\n ? `--group-start:${start};--group-size:${size};${justify}${column.header?.style || ''}`\n : `${justify}${column.header?.style || ''}`}\n >\n <span\n for-title\n data-reactive-tooltip\n style=\"text-align:${align || column.record.align || 'left'};\"\n @click=${(e: MouseEvent) => this._changeSort(column)}\n >${this._renderHeader(column)}\n </span>\n\n ${!group && column.sortable\n ? html`\n <span sorter @click=${(e: MouseEvent) => this._changeSort(column)}>\n ${this._renderSortHeader(column)}\n </span>\n `\n : nothing}\n ${!group &&\n this.filteringFeature &&\n column.filter &&\n (column.filter as FilterConfigObject).operator !== 'search'\n ? html` <span filter> ${this._renderFilterHeader(column)} </span> `\n : nothing}\n ${column.resizable !== false\n ? html`\n <span splitter draggable=\"false\" @pointerdown=${(e: PointerEvent) => this._pointerdown(e, index)}\n > </span\n >\n `\n : nothing}\n </div>\n `\n }\n\n notifyFixedLeftChange() {\n const fixedHeaders = Array.from(this.renderRoot.querySelectorAll('div[fixed]')) as HTMLElement[]\n const fixedLefts = [] as number[]\n var left = 0\n fixedHeaders.forEach((header: HTMLElement) => {\n header.style.left = left + 'px'\n fixedLefts.push(left)\n\n const width = header.offsetWidth\n left += width\n })\n\n this.dispatchEvent(\n new CustomEvent('fixed-lefts-change', {\n detail: fixedLefts\n })\n )\n }\n\n updated(changes: PropertyValues) {\n var isColumnWidthChangePossible = false\n\n if (changes.has('columns')) {\n /*\n * 보이는 열의 번호 — aria-colindex 는 **숨긴 열을 세지 않는다**(1부터).\n *\n * 아래 reduce 의 index 는 this.columns 안의 자리라 숨긴 열까지 센 값이다. 그것을 그대로\n * 내보내면 열을 하나 숨긴 표에서 「3번째 열」이라고 읽히는 칸이 실제로는 두 번째가 된다.\n */\n const colIndexOf = new Map<number, number>()\n this.columns.forEach((column, index) => {\n if (!column.hidden) {\n colIndexOf.set(index, colIndexOf.size + 1)\n }\n })\n\n this.row2 = this.columns.reduce((row2, column, index) => {\n if (column.hidden || !column.header?.group) {\n return row2\n }\n return row2.concat({\n index,\n colIndex: colIndexOf.get(index),\n column\n } as any)\n }, [] as any[])\n\n const clazz = this.row2.length > 0 ? 'span-both' : undefined\n\n var columnNo = 0\n\n this.row1 = this.columns.reduce((row1, column, index) => {\n if (column.hidden) {\n return row1\n }\n\n columnNo++\n if (!column.header?.group) {\n return row1.concat({\n index,\n colIndex: colIndexOf.get(index),\n column,\n clazz\n } as any)\n }\n const { group, groupStyle } = column.header\n const last = row1[row1.length - 1] as any\n\n if (!last || group !== last.group) {\n return row1.concat({\n index,\n column: {\n ...column,\n header: {\n renderer: () => group,\n style: groupStyle\n }\n },\n group,\n align: 'center',\n clazz: 'group-header',\n start: columnNo,\n size: 1\n } as any)\n }\n\n last.size++\n\n return row1\n }, [] as any[])\n\n isColumnWidthChangePossible = true\n }\n\n if (changes.has('data')) {\n isColumnWidthChangePossible = true\n }\n\n if (isColumnWidthChangePossible) {\n // requestAnimationFrame(() => this.notifyFixedLeftChange())\n this.notifyFixedLeftChange()\n }\n }\n\n _renderHeader(column: ColumnConfig) {\n var { renderer } = column.header || {}\n var title = renderer.call(this, column)\n\n return html`${column?.record?.mandatory ? '*' : ''} ${title} `\n }\n\n /**\n * 이 열이 지금 어떻게 정렬되어 있나 — 화살표가 말하는 것을 글자로도 말한다.\n *\n * 화살표(keyboard_arrow_up/down)는 그림이라 눈으로만 읽힌다. aria-sort 가 있으면 스크린 리더가\n * 열 이름 뒤에 「오름차순」을 붙여 읽고, 정렬을 바꿨을 때 바뀌었다는 것도 전해진다.\n *\n * 정렬할 수 없는 열에는 붙이지 않는다 — none 은 「지금은 정렬 안 됨」이라는 뜻이라\n * 정렬 자체가 없는 열에 쓰면 있지도 않은 기능을 있다고 말하게 된다.\n * 묶음 머리도 마찬가지다(그 자리는 누를 수 없다).\n */\n private _ariaSort(column: ColumnConfig, group?: string): 'ascending' | 'descending' | 'none' | undefined {\n if (group || !column.sortable) {\n return undefined\n }\n\n const sorter = (this.sorters || []).find(sorter => column.type !== 'gutter' && column.name == sorter.name)\n\n if (!sorter) {\n return 'none'\n }\n\n return sorter.desc ? 'descending' : 'ascending'\n }\n\n _renderSortHeader(column: ColumnConfig) {\n var sorters = this.sorters || []\n\n var sorter = sorters.find(sorter => column.type !== 'gutter' && column.name == sorter.name)\n if (!sorter) {\n return html`<md-icon sortable-mark>unfold_more</md-icon>`\n }\n\n if (sorters.length > 1) {\n var rank = sorters.indexOf(sorter) + 1\n return sorter.desc\n ? html` <md-icon>keyboard_arrow_down</md-icon><sub>${rank}</sub> `\n : html` <md-icon>keyboard_arrow_up</md-icon><sub>${rank}</sub> `\n } else {\n return sorter.desc ? html` <md-icon>keyboard_arrow_down</md-icon> ` : html` <md-icon>keyboard_arrow_up</md-icon> `\n }\n }\n\n _renderFilterHeader(column: ColumnConfig) {\n const name = column.name\n const filter = column.filter as FilterConfigObject\n const type = filter.type\n const value = this.filters.find(filter => filter.name === name)?.value\n const idx = filter!.operator === 'between' ? 1 : 0\n const renderer = getFilterRenderer(type)[idx]\n\n return html`\n <md-icon\n @click=${(e: Event) => {\n const parent = (e.target as HTMLElement).closest('[column]') as HTMLElement\n const popup = parent.querySelector('ox-popup, ox-popup-list') as OxPopup | null\n // const popup = (e.target as HTMLElement).nextSibling as OxPopupList | null\n\n // absolute position인 popup의 위치 부모는 grist 이므로,\n // data-grid-header 의 포지션 부모(grist)의 위치로부터 계산해야함.\n // this의 position을 relative로 하지 못하는 이유 : ox-popup-list가 grid body에 덮히기 때문.\n // const top = parent.offsetTop + parent.offsetHeight\n // const right = this.clientWidth - (parent.offsetLeft + parent.offsetWidth - this.scrollLeft)\n\n popup?.open({\n right: 0,\n top: parent.offsetHeight,\n fixed: true\n })\n }}\n >filter_alt</md-icon\n >\n\n ${!renderer\n ? html``\n : type !== 'select'\n ? html` <ox-popup\n ><div filter-title><md-icon>filter_alt</md-icon> filter by <strong>${column.name}</strong></div>\n ${renderer(column, value, this)}</ox-popup\n >`\n : filter!.operator === 'in'\n ? html`<ox-popup-list\n multiple\n attr-selected=\"checked\"\n .value=${value}\n with-search\n @select=${(e: CustomEvent) =>\n e.target?.dispatchEvent(\n new CustomEvent('filter-change', {\n bubbles: true,\n composed: true,\n detail: {\n name,\n operator: filter!.operator,\n value: !e.detail\n ? undefined\n : e.detail instanceof Array && e.detail.length === 0\n ? undefined\n : e.detail\n }\n })\n )}\n ><div filter-title slot=\"header\">\n <md-icon>filter_alt</md-icon> filter by <strong>${column.name}</strong>\n </div>\n ${renderer(column, value, this)}</ox-popup-list\n >`\n : html`<ox-popup-list\n .value=${value}\n with-search\n @select=${(e: CustomEvent) =>\n e.target?.dispatchEvent(\n new CustomEvent('filter-change', {\n bubbles: true,\n composed: true,\n detail: {\n name,\n operator: filter!.operator,\n value: e.detail ? e.detail : undefined\n }\n })\n )}\n ><div filter-title slot=\"header\">\n <md-icon>filter_alt</md-icon> filter by <strong>${column.name}</strong>\n </div>\n ${renderer(column, value, this)}</ox-popup-list\n >`}\n `\n }\n\n _changeSort(column: ColumnConfig) {\n if (!column.sortable) {\n return\n }\n\n var sorters = [...this.sorters]\n\n var idx = sorters.findIndex(sorter => sorter.name == column.name)\n if (idx !== -1) {\n let sorter = sorters[idx]\n if (sorter.desc) {\n sorters.splice(idx, 1)\n } else {\n sorters.splice(idx, 1, {\n ...sorter,\n desc: true\n })\n }\n } else {\n var sorter = {\n name: column.name,\n desc: false\n }\n\n sorters.push(sorter)\n }\n\n this.sorters = sorters\n\n this.dispatchEvent(\n new CustomEvent('fetch-params-change', {\n bubbles: true,\n composed: true,\n detail: {\n sorters: this.sorters,\n from: 'data-grid-header'\n }\n })\n )\n }\n\n _accumalate(x: number) {\n this._lastAccVal = (this._lastAccVal ?? 0) + x\n return this._lastAccVal\n }\n\n _notifyWidthChange(idx: number, width: number) {\n if (!this._throttledNotifier) {\n this._throttledNotifier = throttle((idx: number, width: number) => {\n this.dispatchEvent(\n new CustomEvent('column-width-change', {\n bubbles: true,\n composed: true,\n detail: {\n idx,\n width\n }\n })\n )\n\n this.notifyFixedLeftChange()\n\n this._lastAccVal = 0\n }, 100)\n }\n\n this._throttledNotifier(idx, width)\n }\n\n _pointerdown(e: PointerEvent, idx: number) {\n e.stopPropagation()\n e.preventDefault()\n ;(e.target as HTMLElement).setPointerCapture(e.pointerId)\n\n var pointermoveHandler = ((e: PointerEvent) => {\n e.stopPropagation()\n e.preventDefault()\n let column = this.columns[idx]\n\n let width = Math.max(0, Number(column.width || 100) + this._accumalate(e.movementX))\n if (width == 0) {\n /* CLARIFY-ME 왜 마지막 이벤트의 offsetX로 음수 값이 오는가 */\n return\n }\n\n this._notifyWidthChange(idx, width)\n }).bind(this)\n\n var pointerupHandler = ((e: PointerEvent) => {\n ;(e.target as HTMLElement).releasePointerCapture(e.pointerId)\n\n document.removeEventListener('pointermove', pointermoveHandler)\n document.removeEventListener('pointerup', pointerupHandler)\n\n pointermoveHandler(e)\n }).bind(this)\n\n document.addEventListener('pointermove', pointermoveHandler)\n document.addEventListener('pointerup', pointerupHandler)\n }\n}\n"]}
|
|
@@ -23,7 +23,7 @@ export declare class DataGrid extends DataManipulator {
|
|
|
23
23
|
description?: string;
|
|
24
24
|
};
|
|
25
25
|
/**
|
|
26
|
-
* 이 표에 고칠 수 있는 열이 하나라도
|
|
26
|
+
* 이 표에 고칠 수 있는 열이 하나라도 있는지. **겉모습이 이것으로 달라진다.**
|
|
27
27
|
*
|
|
28
28
|
* 셀 하나를 가리키는 점선과 열 폭 조절 손잡이는 **고칠 셀을 겨누는 장치**다. 고치지 않는 목록에서는
|
|
29
29
|
* 셀 하나가 뜻을 갖지 않아, 줄을 고르려고 누른 자리에 점선이 남고 세로 선이 줄 읽기를 방해한다.
|
|
@@ -46,6 +46,14 @@ export declare class DataGrid extends DataManipulator {
|
|
|
46
46
|
onRecordChanged(recordData: GristRecord, row: number, column: ColumnConfig | null): void;
|
|
47
47
|
updated(changes: PropertyValues<this>): void;
|
|
48
48
|
calculateWidths(columns: ColumnConfig[]): void;
|
|
49
|
+
/**
|
|
50
|
+
* 이 표의 이름 — `<ox-grid aria-label="입고 대기 목록">` 처럼 쓰는 쪽이 정한다.
|
|
51
|
+
*
|
|
52
|
+
* role=grid 는 안쪽 감싸개에 걸려 있어서 호스트에 적은 aria-label 이 거기까지 닿지 않는다.
|
|
53
|
+
* 그래서 값을 받아 넘긴다. 이름을 여기서 지어내지는 않는다 — 한 화면에 표가 여럿일 때
|
|
54
|
+
* 무엇을 보는 표인지는 화면이 아는 것이고, 부품이 지어낼 수 있는 것이 아니다.
|
|
55
|
+
*/
|
|
56
|
+
gridLabel?: string;
|
|
49
57
|
render(): import("lit-html").TemplateResult<1>;
|
|
50
58
|
focus(): void;
|
|
51
59
|
get pullToRefreshTarget(): LitElement;
|
|
@@ -5,6 +5,7 @@ import './data-grid-body.js';
|
|
|
5
5
|
import './data-grid-footer.js';
|
|
6
6
|
import { css, html, nothing } from 'lit';
|
|
7
7
|
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
8
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
8
9
|
import { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles.js';
|
|
9
10
|
import { ScrollbarStyles } from '@operato/styles';
|
|
10
11
|
import { DataManipulator } from '../data-manipulator.js';
|
|
@@ -15,7 +16,7 @@ let DataGrid = class DataGrid extends DataManipulator {
|
|
|
15
16
|
constructor() {
|
|
16
17
|
super(...arguments);
|
|
17
18
|
/**
|
|
18
|
-
* 이 표에 고칠 수 있는 열이 하나라도
|
|
19
|
+
* 이 표에 고칠 수 있는 열이 하나라도 있는지. **겉모습이 이것으로 달라진다.**
|
|
19
20
|
*
|
|
20
21
|
* 셀 하나를 가리키는 점선과 열 폭 조절 손잡이는 **고칠 셀을 겨누는 장치**다. 고치지 않는 목록에서는
|
|
21
22
|
* 셀 하나가 뜻을 갖지 않아, 줄을 고르려고 누른 자리에 점선이 남고 세로 선이 줄 읽기를 방해한다.
|
|
@@ -149,19 +150,51 @@ let DataGrid = class DataGrid extends DataManipulator {
|
|
|
149
150
|
})
|
|
150
151
|
.concat(['auto'])
|
|
151
152
|
.join(' ');
|
|
152
|
-
/* 겉모습이 이 사실로
|
|
153
|
+
/* 겉모습이 이 사실로 달라진다(위 editableGrid 주석 참조). 열이 정해지는 이 시점이 그 자리다. */
|
|
153
154
|
this.editableGrid = columns.some(column => { var _a; return !column.hidden && !column.gutterName && !!((_a = column.record) === null || _a === void 0 ? void 0 : _a.editable); });
|
|
154
155
|
this.style.setProperty('--grid-template-columns', widths);
|
|
155
156
|
/* 인쇄는 종이 폭에 맞춰 비율로 나눈다. `220px` → `220fr` 로 바뀐다. */
|
|
156
157
|
this.style.setProperty('--grid-template-print-columns', widths.replace(/px/gi, 'fr'));
|
|
157
158
|
}
|
|
158
159
|
render() {
|
|
159
|
-
var _a, _b, _c, _d, _e, _f;
|
|
160
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
160
161
|
var { pagination = {}, columns = [], filters } = this.config;
|
|
161
162
|
var paginatable = !pagination.infinite;
|
|
162
163
|
var data = this.data;
|
|
164
|
+
/*
|
|
165
|
+
* ── 표를 표라고 선언한다 (2026-09-06) ─────────────────────────────────
|
|
166
|
+
*
|
|
167
|
+
* 이 표에는 role 이 하나도 없었다(전체 소스에 aria- 가 페이지 번호 하나뿐이었다). 그래서
|
|
168
|
+
* 스크린 리더에게 이 화면은 **입력칸이 잔뜩 늘어선 더미**였다. 칸마다 이름을 손으로 붙여도
|
|
169
|
+
* 「여기가 표다」 「지금 3행 3열이다」 「전체는 40행이다」는 여전히 말하지 못한다.
|
|
170
|
+
* 표에서 하는 일은 훑고 견주는 것인데, 그 조작 자체가 없는 것이다.
|
|
171
|
+
*
|
|
172
|
+
* role=grid 를 세우면 칸의 이름을 아무 데도 안 적어도 **열 머리가 그 칸의 이름이 된다** —
|
|
173
|
+
* HTML table 이 원래 그렇게 동작하고, 표준이 그렇게 설계된 이유가 이것이다.
|
|
174
|
+
*
|
|
175
|
+
* ── 왜 감싸개가 하나 더 필요한가 ──────────────────────────────────────
|
|
176
|
+
* grid 안에는 줄(또는 줄 묶음)만 들어갈 수 있다. 그런데 이 요소 밑에는 설정 단추 자리와
|
|
177
|
+
* 페이지 넘김도 있어서, 호스트에 role 을 걸면 표 안에 표가 아닌 것이 섞인다. 보조기술은 그때
|
|
178
|
+
* 표 읽기 모드를 포기한다.
|
|
179
|
+
*
|
|
180
|
+
* 그래서 머리와 몸만 감싼다. display: contents 라 상자를 만들지 않으므로 세로 flex 배치는
|
|
181
|
+
* 그대로다.
|
|
182
|
+
*/
|
|
183
|
+
const headerRows = columns.some(column => { var _a; return !column.hidden && ((_a = column.header) === null || _a === void 0 ? void 0 : _a.group); }) ? 2 : 1;
|
|
184
|
+
const visibleColumns = columns.filter(column => !column.hidden).length;
|
|
185
|
+
const total = typeof data.total == 'number' && data.total > 0 ? data.total : ((_a = data.records) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
186
|
+
const page = Number(data.page) > 0 ? Number(data.page) : 1;
|
|
187
|
+
const limit = Number(data.limit) > 0 ? Number(data.limit) : ((_b = data.records) === null || _b === void 0 ? void 0 : _b.length) || 0;
|
|
163
188
|
return html `
|
|
189
|
+
<div
|
|
190
|
+
grid
|
|
191
|
+
role="grid"
|
|
192
|
+
aria-label=${ifDefined(this.gridLabel)}
|
|
193
|
+
aria-rowcount=${headerRows + total}
|
|
194
|
+
aria-colcount=${visibleColumns}
|
|
195
|
+
>
|
|
164
196
|
<ox-grid-header
|
|
197
|
+
role="rowgroup"
|
|
165
198
|
class="md-typescale-label-medium-prominent"
|
|
166
199
|
.columns=${columns}
|
|
167
200
|
.sorters=${this.sorters}
|
|
@@ -177,35 +210,43 @@ let DataGrid = class DataGrid extends DataManipulator {
|
|
|
177
210
|
>
|
|
178
211
|
</ox-grid-header>
|
|
179
212
|
|
|
180
|
-
<!--
|
|
181
|
-
헤더 **다음**에 둔다. CSS 로 「헤더에 올렸을 때만」을 표현하려면 형제 선택자가 필요하고, 형제
|
|
182
|
-
선택자는 뒤쪽 형제만 고를 수 있다.
|
|
183
|
-
|
|
184
|
-
:host(:has(ox-grid-header:hover)) 로는 안 된다 — :host() 의 인수는 바깥 트리에서 평가되므로
|
|
185
|
-
섀도 트리 안의 ox-grid-header 를 보지 못한다. 실제로 그렇게 써 봤고 hover 에 반응하지 않았다.
|
|
186
|
-
|
|
187
|
-
순서를 바꿔도 보이는 자리는 같다 — 아래 스타일에서 호스트의 오른쪽 위에 절대 좌표로 붙인다.
|
|
188
|
-
-->
|
|
189
|
-
<div setting>
|
|
190
|
-
<slot name="setting"></slot>
|
|
191
|
-
</div>
|
|
192
|
-
|
|
193
213
|
<ox-grid-body
|
|
214
|
+
role="rowgroup"
|
|
194
215
|
class="md-typescale-body-medium"
|
|
195
216
|
.config=${this.config}
|
|
196
217
|
.columns=${columns}
|
|
197
218
|
.data=${data}
|
|
198
219
|
.focused=${this.focused}
|
|
199
220
|
.fixedLefts=${this.fixedLefts}
|
|
221
|
+
.ariaRowIndexBase=${headerRows + (page - 1) * limit + 1}
|
|
200
222
|
>
|
|
201
223
|
${this.empty
|
|
202
224
|
? html `<ox-empty-note
|
|
203
|
-
icon=${(
|
|
204
|
-
title=${(
|
|
205
|
-
description=${(
|
|
225
|
+
icon=${(_d = (_c = this.emptyNote) === null || _c === void 0 ? void 0 : _c.icon) !== null && _d !== void 0 ? _d : nothing}
|
|
226
|
+
title=${(_f = (_e = this.emptyNote) === null || _e === void 0 ? void 0 : _e.title) !== null && _f !== void 0 ? _f : ''}
|
|
227
|
+
description=${(_h = (_g = this.emptyNote) === null || _g === void 0 ? void 0 : _g.description) !== null && _h !== void 0 ? _h : nothing}
|
|
206
228
|
></ox-empty-note>`
|
|
207
229
|
: html ``}
|
|
208
230
|
</ox-grid-body>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<!--
|
|
234
|
+
표 **다음**에 둔다. CSS 로 「머리에 올렸을 때만」을 표현하려면 형제 선택자가 필요하고, 형제
|
|
235
|
+
선택자는 뒤쪽 형제만 고를 수 있다.
|
|
236
|
+
|
|
237
|
+
:host(:has(ox-grid-header:hover)) 로는 안 된다 — :host() 의 인수는 바깥 트리에서 평가되므로
|
|
238
|
+
섀도 트리 안의 ox-grid-header 를 보지 못한다. 실제로 그렇게 써 봤고 hover 에 반응하지 않았다.
|
|
239
|
+
표 감싸개에 건 :has() 는 섀도 트리 안에서 평가되므로 문제가 없다.
|
|
240
|
+
|
|
241
|
+
설정 단추는 표의 일부가 아니라 표를 **다루는** 것이라 role=grid 안에 두지 않는다 — grid 안에는
|
|
242
|
+
줄만 들어갈 수 있고, 아닌 것이 섞이면 보조기술이 표 읽기 모드를 포기한다.
|
|
243
|
+
|
|
244
|
+
순서를 바꿔도 보이는 자리는 같다 — 아래 스타일에서 호스트의 오른쪽 위에 절대 좌표로 붙인다.
|
|
245
|
+
-->
|
|
246
|
+
<div setting>
|
|
247
|
+
<slot name="setting"></slot>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
209
250
|
${paginatable
|
|
210
251
|
? html `
|
|
211
252
|
<!--
|
|
@@ -214,7 +255,7 @@ let DataGrid = class DataGrid extends DataManipulator {
|
|
|
214
255
|
그 클래스는 font 축약형으로 0.875rem 를 넣는데, **바깥 트리의 규칙이라 푸터의
|
|
215
256
|
:host { font-size } 를 이긴다**(그림자 캐스케이드에서 호스트 요소는 바깥 트리 규칙이
|
|
216
257
|
우선한다). 그래서 푸터가 자기 크기를 못 정하고 본문과 같은 14px 로 남아 있었다.
|
|
217
|
-
[
|
|
258
|
+
[확인함] 리터럴 :host { font-size: 7px } 도 안 먹었다. 2026-09-02
|
|
218
259
|
|
|
219
260
|
푸터는 본문이 아니다. 크기는 --grid-footer-font-size 가 정한다.
|
|
220
261
|
-->
|
|
@@ -242,7 +283,7 @@ DataGrid.styles = [
|
|
|
242
283
|
* ── 고치는 표만 셀을 겨눈다 ─────────────────────────────────────────
|
|
243
284
|
*
|
|
244
285
|
* 셀 하나를 가리키는 점선과 열 폭 조절 손잡이는 **고칠 셀을 겨누는 장치**다. 기본 겉모습에서는
|
|
245
|
-
* 둘을 두지 않는다(data-grist.ts
|
|
286
|
+
* 둘을 두지 않는다(data-grist.ts 와 같은 값). 고칠 수 있는 열이 하나라도 있으면 여기서 되살린다.
|
|
246
287
|
*
|
|
247
288
|
* 화면이 고르는 모드가 아니다 — 표가 스스로 아는 사실이다(위 editableGrid 주석 참조).
|
|
248
289
|
*/
|
|
@@ -286,6 +327,14 @@ DataGrid.styles = [
|
|
|
286
327
|
border-width: var(--grid-container-border-width);
|
|
287
328
|
}
|
|
288
329
|
|
|
330
|
+
/*
|
|
331
|
+
* 표 감싸개(role=grid) — 상자를 만들지 않는다. 머리와 몸은 그대로 호스트의 flex 항목으로
|
|
332
|
+
* 남는다.
|
|
333
|
+
*/
|
|
334
|
+
div[grid] {
|
|
335
|
+
display: contents;
|
|
336
|
+
}
|
|
337
|
+
|
|
289
338
|
ox-grid-body {
|
|
290
339
|
flex: 1;
|
|
291
340
|
position: relative;
|
|
@@ -310,7 +359,7 @@ DataGrid.styles = [
|
|
|
310
359
|
}
|
|
311
360
|
|
|
312
361
|
/*
|
|
313
|
-
* 컬럼 설정
|
|
362
|
+
* 컬럼 설정 버튼은 **헤더에 마우스를 올릴 때만** 보인다.
|
|
314
363
|
*
|
|
315
364
|
* 늘 보이면 목록 화면마다 오른쪽 위에 눈에 걸리는 것이 하나 생긴다. 자주 쓰는 것도 아니다.
|
|
316
365
|
*
|
|
@@ -319,13 +368,13 @@ DataGrid.styles = [
|
|
|
319
368
|
*
|
|
320
369
|
* 세 갈래로 켠다.
|
|
321
370
|
* 헤더에 올렸을 때 :has(ox-grid-header:hover)
|
|
322
|
-
*
|
|
371
|
+
* 버튼 자체에 올렸을 때 버튼이 헤더 위에 떠 있으므로, 커서가 버튼으로 옮겨오면 헤더는 더 이상
|
|
323
372
|
* hover 가 아니다. 이 갈래가 없으면 커서 아래에서 사라진다
|
|
324
373
|
* 키보드 초점 focus-within
|
|
325
374
|
*/
|
|
326
375
|
div[setting] {
|
|
327
376
|
/*
|
|
328
|
-
* 크기 없이 호스트의 오른쪽 위에 붙는다. 안에 들어오는
|
|
377
|
+
* 크기 없이 호스트의 오른쪽 위에 붙는다. 안에 들어오는 버튼이 [slot='setting'] 규칙으로
|
|
329
378
|
* top: -3px · right: 0 을 스스로 잡으므로, 이 div 는 기준점 노릇만 한다.
|
|
330
379
|
*/
|
|
331
380
|
position: absolute;
|
|
@@ -340,10 +389,10 @@ DataGrid.styles = [
|
|
|
340
389
|
}
|
|
341
390
|
|
|
342
391
|
/*
|
|
343
|
-
*
|
|
392
|
+
* 버튼 자체에 올렸을 때도 켜 둔다. 버튼이 헤더 위에 떠 있어서, 커서가 버튼으로 옮겨오면 헤더는
|
|
344
393
|
* 더 이상 hover 가 아니다. 이 갈래가 없으면 커서 아래에서 사라진다.
|
|
345
394
|
*/
|
|
346
|
-
ox-grid-header:hover + div[setting],
|
|
395
|
+
div[grid]:has(ox-grid-header:hover) + div[setting],
|
|
347
396
|
div[setting]:hover,
|
|
348
397
|
div[setting]:focus-within {
|
|
349
398
|
opacity: 1;
|
|
@@ -354,7 +403,7 @@ DataGrid.styles = [
|
|
|
354
403
|
* 손가락으로 쓰는 장치 — hover 가 없으므로 감추면 닿을 방법이 없다. 늘 두고 평소에는 흐리게,
|
|
355
404
|
* 누르는 동안 선명하게 한다.
|
|
356
405
|
*
|
|
357
|
-
* 화면 폭이 아니라 입력 장치로
|
|
406
|
+
* 화면 폭이 아니라 입력 장치로 구분한다. 창을 좁힌 데스크톱은 여기에 걸리지 않고, 큰 태블릿은
|
|
358
407
|
* 폭이 넓어도 걸린다.
|
|
359
408
|
*/
|
|
360
409
|
@media (pointer: coarse) {
|
|
@@ -400,6 +449,9 @@ __decorate([
|
|
|
400
449
|
__decorate([
|
|
401
450
|
state()
|
|
402
451
|
], DataGrid.prototype, "fixedLefts", void 0);
|
|
452
|
+
__decorate([
|
|
453
|
+
property({ type: String, attribute: 'aria-label' })
|
|
454
|
+
], DataGrid.prototype, "gridLabel", void 0);
|
|
403
455
|
DataGrid = __decorate([
|
|
404
456
|
customElement('ox-grid')
|
|
405
457
|
], DataGrid);
|