@operato/data-grist 10.29.6 → 10.29.7

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 CHANGED
@@ -3,6 +3,15 @@
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.29.7](https://github.com/hatiolab/operato/compare/v10.29.6...v10.29.7) (2026-10-05)
7
+
8
+
9
+ ### :bug: Bug Fix
10
+
11
+ * **data-grist:** scroll grid header and body in one scroller ([b004080](https://github.com/hatiolab/operato/commit/b0040800ce32634bcb2f3ecf0ebbac174199570f))
12
+
13
+
14
+
6
15
  ### [10.29.6](https://github.com/hatiolab/operato/compare/v10.29.5...v10.29.6) (2026-10-04)
7
16
 
8
17
 
@@ -5,18 +5,10 @@ export const dataGridBodyStyle = css `
5
5
  grid-template-columns: var(--grid-template-columns);
6
6
  grid-auto-rows: var(--grid-record-height, min-content);
7
7
 
8
- overflow: auto;
9
8
  /*
10
- * 세로 스크롤바 자리를 늘 비워 둔다 — 머리와 가로 스크롤 범위를 같게 만들기 위해서다.
11
- *
12
- * 머리는 overflow: hidden 이라 스크롤바에 자리를 내주지 않고, 몸은 긴 목록에서 스크롤바 폭만큼
13
- * 좁아진다. 그래서 갈 수 있는 거리가 몸이 그만큼 더 길었고, 오른쪽 끝에서 몸만 더 갔다.
14
- * 확인해 보니 정확히 --scrollbar-width 만큼(5px)이었다.
15
- *
16
- * 자리를 늘 비워 두면 목록 길이와 무관하게 두 폭이 같다. 목록이 길어질 때 열이 움찔하는 것도
17
- * 함께 없어진다 — 스크롤바가 생기는 순간 폭이 바뀌지 않는다.
9
+ * 몸은 스크롤하지 않는다 — 머리와 함께 data-grid 의 div[grid] 안에서 움직인다(data-grid.ts 주석).
10
+ * 몸이 스크롤러였을 때는 머리를 이벤트로 따라오게 해야 했고, 그것이 이격과 흔들림을 만들었다.
18
11
  */
19
- scrollbar-gutter: stable;
20
12
  outline: none;
21
13
  color: var(--grid-record-color);
22
14
  position: relative;
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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 * Row wrapper — generates no box.\n *\n * It exists only to carry role=row, so it must stay out of layout. With display: contents the\n * element produces no box and its children remain items of the outer grid — column widths, fixed\n * columns and the grid-column placement of group headers are untouched.\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"]}
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8InC,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 /*\n * 몸은 스크롤하지 않는다 — 머리와 함께 data-grid 의 div[grid] 안에서 움직인다(data-grid.ts 주석).\n * 몸이 스크롤러였을 때는 머리를 이벤트로 따라오게 해야 했고, 그것이 이격과 흔들림을 만들었다.\n */\n outline: none;\n color: var(--grid-record-color);\n position: relative;\n border-bottom: var(--grid-body-bottom-border);\n }\n\n /*\n * Row wrapper — generates no box.\n *\n * It exists only to carry role=row, so it must stay out of layout. With display: contents the\n * element produces no box and its children remain items of the outer grid — column widths, fixed\n * columns and the grid-column placement of group headers are untouched.\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"]}
@@ -48,6 +48,7 @@ export declare class DataGridBody extends LitElement {
48
48
  handleOnScroll(e: WheelEvent): void;
49
49
  render(): import("lit-html").TemplateResult<1>;
50
50
  firstUpdated(): void;
51
+ get scroller(): HTMLElement;
51
52
  connectedCallback(): void;
52
53
  disconnectedCallback(): void;
53
54
  _onFetchParamsChange: EventListener;
@@ -23,10 +23,15 @@ const THRESHOLD = 300;
23
23
  const DATA_PADDING = 3;
24
24
  const ROW_HEIGHT = 40;
25
25
  const GAP_SIZE = 1;
26
- function calcScrollPos(parent, child) {
26
+ /*
27
+ * parent 는 스크롤러다. stickyTop 은 그 위쪽에 붙어 칸을 가리는 머리의 높이다 — 머리 밑으로 들어간
28
+ * 칸은 화면 안에 있어도 보이지 않으므로 그만큼 내려서 판단한다.
29
+ */
30
+ function calcScrollPos(parent, child, stickyTop = 0) {
27
31
  /* getBoundingClientRect는 safari에서 스크롤 상태에서 다른 브라우저와는 다른 값을 리턴함 - 사파리는 약간 이상 작동함. */
28
32
  var { top: ct, left: cl, right: cr, bottom: cb } = child.getBoundingClientRect();
29
33
  var { top: pt, left: pl, right: pr, bottom: pb } = parent.getBoundingClientRect();
34
+ pt += stickyTop;
30
35
  var { scrollLeft, scrollTop } = parent;
31
36
  var scrollbarWidth = parent.clientWidth - parent.offsetWidth;
32
37
  var scrollbarHeight = parent.clientHeight - parent.offsetHeight;
@@ -104,7 +109,7 @@ let DataGridBody = class DataGridBody extends LitElement {
104
109
  this._lastPointerTouch = false;
105
110
  // 검색시 스크롤 맨 위로
106
111
  this._onFetchParamsChange = () => {
107
- this.scrollTop = 0;
112
+ this.scroller.scrollTop = 0;
108
113
  };
109
114
  this._hoveredRow = -1;
110
115
  }
@@ -444,9 +449,13 @@ let DataGridBody = class DataGridBody extends LitElement {
444
449
  `url("data:image/svg+xml,${encodedSvg}")`);
445
450
  });
446
451
  this.addEventListener('show-record-view', (e) => this.popupRecordView(e.detail));
447
- this.addEventListener('scroll', function () {
448
- this.scrollLeft == 0 ? this.removeAttribute('raised') : this.setAttribute('raised', '');
449
- });
452
+ }
453
+ /*
454
+ * 몸을 옮기는 스크롤러. 표 안에서는 머리와 몸을 함께 담은 div[grid] 이고(data-grid.ts), 몸만 홀로
455
+ * 세웠을 때는 몸 자신이다.
456
+ */
457
+ get scroller() {
458
+ return this.closest('div[grid]') || this;
450
459
  }
451
460
  connectedCallback() {
452
461
  var _a;
@@ -559,7 +568,9 @@ let DataGridBody = class DataGridBody extends LitElement {
559
568
  if (!element) {
560
569
  return;
561
570
  }
562
- let { top, left } = calcScrollPos(this, element);
571
+ const scroller = this.scroller;
572
+ const header = scroller === this ? null : scroller.querySelector('ox-grid-header');
573
+ let { top, left } = calcScrollPos(scroller, element, (header === null || header === void 0 ? void 0 : header.offsetHeight) || 0);
563
574
  // TODO this.scroll()을 사용하면, 효과가 좋으나 left 계산에 문제가 있는 것 같음.
564
575
  // this.scroll({
565
576
  // top,
@@ -567,10 +578,10 @@ let DataGridBody = class DataGridBody extends LitElement {
567
578
  // behavior: 'smooth'
568
579
  // })
569
580
  if (top !== undefined) {
570
- this.scrollTop = top;
581
+ scroller.scrollTop = top;
571
582
  }
572
583
  if (left !== undefined) {
573
- this.scrollLeft = left;
584
+ scroller.scrollLeft = left;
574
585
  }
575
586
  /* IME buffer 위치 업데이트 */
576
587
  this._positionIMEBuffer();
@@ -584,7 +595,7 @@ let DataGridBody = class DataGridBody extends LitElement {
584
595
  const prevPage = prevData && prevData.page;
585
596
  const currPage = this.data.page;
586
597
  if (prevPage !== undefined && prevPage !== currPage) {
587
- this.scrollTop = 0;
598
+ this.scroller.scrollTop = 0;
588
599
  }
589
600
  }
590
601
  if (this._recordView) {