@operato/scene-table 9.1.1 → 9.2.1

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.
@@ -21,12 +21,36 @@ var DataListLayout = {
21
21
  var paddingTop = container.getState('paddingTop') || 0;
22
22
  var width_unit = inside.width / widths_sum;
23
23
  var height_unit = inside.height / heights_sum;
24
- var x = offset.x;
25
- var y = offset.y;
24
+ // Calculate row height (assuming uniform height for now based on first row config)
25
+ var rowHeight = (heights ? heights[0] : 1) * height_unit;
26
26
  var components = container.components;
27
27
  components.forEach((component, idx) => {
28
- let w = widths ? widths[idx % columns] : 1;
28
+ if (component.hidden) {
29
+ // Position hidden components far outside the viewport
30
+ component.bounds = {
31
+ left: -10000,
32
+ top: -10000,
33
+ width: 0,
34
+ height: 0
35
+ };
36
+ return;
37
+ }
38
+ // @ts-ignore
39
+ var rowIndex = component._rowIndex;
40
+ // Fallback for non-virtualized or initial state
41
+ if (rowIndex === undefined) {
42
+ rowIndex = Math.floor(idx / columns);
43
+ }
44
+ var colIndex = idx % columns;
45
+ let w = widths ? widths[colIndex] : 1;
29
46
  let h = heights ? heights[0] : 1;
47
+ // Calculate x position based on column widths
48
+ let x = 0;
49
+ for (let i = 0; i < colIndex; i++) {
50
+ x += (widths ? widths[i] : 1) * width_unit;
51
+ }
52
+ // Calculate y position based on rowIndex and offset
53
+ let y = rowIndex * rowHeight + offset.y;
30
54
  let left = paddingLeft + x;
31
55
  let top = paddingTop + y;
32
56
  let width = width_unit * w;
@@ -38,13 +62,6 @@ var DataListLayout = {
38
62
  height
39
63
  };
40
64
  component.set('rotation', 0);
41
- if (idx % columns == columns - 1) {
42
- x = 0;
43
- y += h * height_unit;
44
- }
45
- else {
46
- x += w * width_unit;
47
- }
48
65
  });
49
66
  },
50
67
  capturables: function (container) {
@@ -1 +1 @@
1
- {"version":3,"file":"data-list-layout.js","sourceRoot":"","sources":["../../src/data-list/data-list-layout.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport { Component, Container, Layout } from '@hatiolab/things-scene'\n\nimport DataCell from './data-cell.js'\nimport DataList from './data-list.js'\n\nvar DataListLayout = {\n reflow: function (container: Container) {\n var layoutConfig = container.getState('layoutConfig')\n\n var columns = (layoutConfig && layoutConfig.columns) || container.getState('columns')\n var rows = (layoutConfig && layoutConfig.rows) || container.getState('rows')\n var widths = (layoutConfig && layoutConfig.widths) || container.getState('widths')\n var heights = (layoutConfig && layoutConfig.heights) || container.getState('heights')\n var { offset = { x: 0, y: 0 } } = container.state\n\n var widths_sum = widths\n ? widths.filter((width: number, i: number) => i < columns).reduce((sum: number, width: number) => sum + width, 0)\n : columns\n var heights_sum = heights\n ? heights.filter((height: number, i: number) => i < rows).reduce((sum: number, height: number) => sum + height, 0)\n : rows\n\n var inside = container.textBounds\n var paddingLeft = container.getState('paddingLeft') || 0\n var paddingTop = container.getState('paddingTop') || 0\n\n var width_unit = inside.width / widths_sum\n var height_unit = inside.height / heights_sum\n\n var x = offset.x\n var y = offset.y\n var components = container.components\n\n components.forEach((component, idx) => {\n let w = widths ? widths[idx % columns] : 1\n let h = heights ? heights[0] : 1\n\n let left = paddingLeft + x\n let top = paddingTop + y\n let width = width_unit * w\n let height = height_unit * h\n\n component.bounds = {\n left,\n top,\n width,\n height\n }\n component.set('rotation', 0)\n\n if (idx % columns == columns - 1) {\n x = 0\n y += h * height_unit\n } else {\n x += w * width_unit\n }\n })\n },\n\n capturables: function (container: Container) {\n return container.components\n },\n\n drawables: function (container: Container) {\n return container.components\n },\n\n isStuck: function (component: Component) {\n return true\n },\n\n /*\n * 레이아웃별로, 키보드 방향키 등을 사용해서 네비게이션 할 수 있는 기능을 제공할 수 있다.\n * 하나의 컴포넌트만 선택되어있고, 키보드 이벤트가 발생했을 때 호출되게 된다.\n * keyNavigate 메쏘드가 정의되어 있지 않으면, 'Tab' 키에 대한 네비게이션만 작동한다.\n * 'Tab'키에 의한 네비게이션은 모든 레이아웃에 공통으로 적용된다.\n */\n keyNavigate: function (container: Container, component: Component, e: KeyboardEvent) {\n var layoutConfig = container.getState('layoutConfig')\n\n var columns = (layoutConfig && layoutConfig.columns) || container.getState('columns')\n var rows = (layoutConfig && layoutConfig.rows) || container.getState('rows')\n\n var { row, column } = (container as DataList).getRowColumn(component as DataCell)\n\n switch (e.code) {\n case 'ArrowUp':\n if (row > 0) return container.getAt((row - 1) * columns + column)\n break\n case 'ArrowDown':\n if (row < rows - 1) return container.getAt((row + 1) * columns + column)\n break\n case 'ArrowRight':\n if (column < columns - 1) return container.getAt(row * columns + column + 1)\n break\n case 'ArrowLeft':\n if (column > 0) return container.getAt(row * columns + column - 1)\n break\n default:\n return component\n }\n },\n\n /*\n * 하위 컴포넌트를 영역으로 선택하는 경우에, 바운드에 join만 되어도 선택된 것으로 판단하도록 한다.\n * joinType이 false이거나, 정의되어있지 않으면, 바운드에 포함되어야 선택된 것으로 판단한다.\n */\n joinType: true\n}\n\nLayout.register('data-list', DataListLayout)\n\nexport default DataListLayout\n"]}
1
+ {"version":3,"file":"data-list-layout.js","sourceRoot":"","sources":["../../src/data-list/data-list-layout.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport { Component, Container, Layout } from '@hatiolab/things-scene'\n\nimport DataCell from './data-cell.js'\nimport DataList from './data-list.js'\n\nvar DataListLayout = {\n reflow: function (container: Container) {\n var layoutConfig = container.getState('layoutConfig')\n\n var columns = (layoutConfig && layoutConfig.columns) || container.getState('columns')\n var rows = (layoutConfig && layoutConfig.rows) || container.getState('rows')\n var widths = (layoutConfig && layoutConfig.widths) || container.getState('widths')\n var heights = (layoutConfig && layoutConfig.heights) || container.getState('heights')\n var { offset = { x: 0, y: 0 } } = container.state\n\n var widths_sum = widths\n ? widths.filter((width: number, i: number) => i < columns).reduce((sum: number, width: number) => sum + width, 0)\n : columns\n var heights_sum = heights\n ? heights.filter((height: number, i: number) => i < rows).reduce((sum: number, height: number) => sum + height, 0)\n : rows\n\n var inside = container.textBounds\n var paddingLeft = container.getState('paddingLeft') || 0\n var paddingTop = container.getState('paddingTop') || 0\n\n var width_unit = inside.width / widths_sum\n var height_unit = inside.height / heights_sum\n\n // Calculate row height (assuming uniform height for now based on first row config)\n var rowHeight = (heights ? heights[0] : 1) * height_unit\n\n var components = container.components\n\n components.forEach((component, idx) => {\n if (component.hidden) {\n // Position hidden components far outside the viewport\n component.bounds = {\n left: -10000,\n top: -10000,\n width: 0,\n height: 0\n }\n return\n }\n\n // @ts-ignore\n var rowIndex = component._rowIndex\n\n // Fallback for non-virtualized or initial state\n if (rowIndex === undefined) {\n rowIndex = Math.floor(idx / columns)\n }\n\n var colIndex = idx % columns\n\n let w = widths ? widths[colIndex] : 1\n let h = heights ? heights[0] : 1\n\n // Calculate x position based on column widths\n let x = 0\n for (let i = 0; i < colIndex; i++) {\n x += (widths ? widths[i] : 1) * width_unit\n }\n\n // Calculate y position based on rowIndex and offset\n let y = rowIndex * rowHeight + offset.y\n\n let left = paddingLeft + x\n let top = paddingTop + y\n let width = width_unit * w\n let height = height_unit * h\n\n component.bounds = {\n left,\n top,\n width,\n height\n }\n component.set('rotation', 0)\n })\n },\n\n capturables: function (container: Container) {\n return container.components\n },\n\n drawables: function (container: Container) {\n return container.components\n },\n\n isStuck: function (component: Component) {\n return true\n },\n\n /*\n * 레이아웃별로, 키보드 방향키 등을 사용해서 네비게이션 할 수 있는 기능을 제공할 수 있다.\n * 하나의 컴포넌트만 선택되어있고, 키보드 이벤트가 발생했을 때 호출되게 된다.\n * keyNavigate 메쏘드가 정의되어 있지 않으면, 'Tab' 키에 대한 네비게이션만 작동한다.\n * 'Tab'키에 의한 네비게이션은 모든 레이아웃에 공통으로 적용된다.\n */\n keyNavigate: function (container: Container, component: Component, e: KeyboardEvent) {\n var layoutConfig = container.getState('layoutConfig')\n\n var columns = (layoutConfig && layoutConfig.columns) || container.getState('columns')\n var rows = (layoutConfig && layoutConfig.rows) || container.getState('rows')\n\n var { row, column } = (container as DataList).getRowColumn(component as DataCell)\n\n switch (e.code) {\n case 'ArrowUp':\n if (row > 0) return container.getAt((row - 1) * columns + column)\n break\n case 'ArrowDown':\n if (row < rows - 1) return container.getAt((row + 1) * columns + column)\n break\n case 'ArrowRight':\n if (column < columns - 1) return container.getAt(row * columns + column + 1)\n break\n case 'ArrowLeft':\n if (column > 0) return container.getAt(row * columns + column - 1)\n break\n default:\n return component\n }\n },\n\n /*\n * 하위 컴포넌트를 영역으로 선택하는 경우에, 바운드에 join만 되어도 선택된 것으로 판단하도록 한다.\n * joinType이 false이거나, 정의되어있지 않으면, 바운드에 포함되어야 선택된 것으로 판단한다.\n */\n joinType: true\n}\n\nLayout.register('data-list', DataListLayout)\n\nexport default DataListLayout\n"]}
@@ -5,7 +5,20 @@ import { WHERE } from '../helper-functions.js';
5
5
  export default class DataList extends Container {
6
6
  reflowing: boolean;
7
7
  postrender(context: CanvasRenderingContext2D): void;
8
+ private _renderStart;
9
+ private _renderEnd;
8
10
  renderScrollbar(context: CanvasRenderingContext2D): void;
11
+ private _scrollbarBounds?;
12
+ private _scrollbarTopIconBounds?;
13
+ private _scrollbarBottomIconBounds?;
14
+ private _isDraggingScrollbar;
15
+ private _dragStartY;
16
+ private _startOffsetY;
17
+ ondragstart(e: MouseEvent): void;
18
+ onclick(e: MouseEvent): void;
19
+ ondragmove(e: MouseEvent): void;
20
+ ondragend(e: MouseEvent): void;
21
+ onkeydown(e: KeyboardEvent): void;
9
22
  created(): void;
10
23
  _onwheel(e: WheelEvent): void;
11
24
  private __START_OFFSET?;
@@ -37,6 +50,10 @@ export default class DataList extends Container {
37
50
  touchstart: (e: TouchEvent) => void;
38
51
  touchmove: (e: TouchEvent) => void;
39
52
  touchend: (e: TouchEvent) => void;
53
+ dragstart: (e: MouseEvent) => void;
54
+ dragmove: (e: MouseEvent) => void;
55
+ dragend: (e: MouseEvent) => void;
56
+ click: (e: MouseEvent) => void;
40
57
  };
41
58
  };
42
59
  };
@@ -45,6 +62,7 @@ export default class DataList extends Container {
45
62
  }): void;
46
63
  reflow(): void;
47
64
  setCellsData(): void;
65
+ updateViewport(): void;
48
66
  setCellsStyle(cells: Component[], style: Style, where: WHERE, clearBefore?: boolean): void;
49
67
  buildCells(newcolumns: number, oldcolumns: number): void;
50
68
  getRowColumn(cell: Component): {
@@ -25,6 +25,14 @@ let DataList = class DataList extends Container {
25
25
  constructor() {
26
26
  super(...arguments);
27
27
  this.reflowing = false;
28
+ /*
29
+ * Virtualization State
30
+ */
31
+ this._renderStart = 0;
32
+ this._renderEnd = 0;
33
+ this._isDraggingScrollbar = false;
34
+ this._dragStartY = 0;
35
+ this._startOffsetY = 0;
28
36
  }
29
37
  postrender(context) {
30
38
  super.postrender(context);
@@ -42,13 +50,263 @@ let DataList = class DataList extends Container {
42
50
  }
43
51
  var start = (-offset.y / fullHeight) * height;
44
52
  var end = ((-offset.y + height) / fullHeight) * height;
53
+ // Calculate scroll position boundaries
54
+ const rowHeight = (this.heights[0] / this.heights_sum) * height;
55
+ const minOffsetY = Math.min(-fullHeight + height, 0);
56
+ const isAtTop = offset.y >= 0 || offset.y >= -rowHeight / 2;
57
+ const isAtBottom = offset.y <= minOffsetY || offset.y <= minOffsetY + rowHeight / 2;
58
+ // Store scrollbar bounds for hit testing (absolute coordinates)
59
+ this._scrollbarBounds = {
60
+ left: left + width - 10,
61
+ top: top + start,
62
+ width: 10,
63
+ height: end - start
64
+ };
65
+ // Render Scrollbar Track
66
+ context.beginPath();
67
+ context.strokeStyle = '#e0e0e0';
68
+ context.lineWidth = 16; // Wider than thumb
69
+ context.globalAlpha = 0.2;
70
+ context.moveTo(left + width - 10, top);
71
+ context.lineTo(left + width - 10, top + height);
72
+ context.stroke();
73
+ // Render Scrollbar Thumb
74
+ context.beginPath();
45
75
  context.strokeStyle = 'gray';
46
76
  context.lineWidth = 10;
47
- context.globalAlpha = 0.3;
48
- context.beginPath();
77
+ context.globalAlpha = 0.5;
49
78
  context.moveTo(left + width - 10, top + start);
50
79
  context.lineTo(left + width - 10, top + end);
51
80
  context.stroke();
81
+ // Icon button dimensions
82
+ const iconSize = 20;
83
+ const iconPadding = 2;
84
+ // Render TOP icon (upward triangle - HOME) only if not at top
85
+ if (!isAtTop) {
86
+ // Store icon bounds for hit testing (absolute coordinates)
87
+ this._scrollbarTopIconBounds = {
88
+ left: left + width - 10 - iconSize / 2,
89
+ top: top + iconPadding,
90
+ width: iconSize,
91
+ height: iconSize
92
+ };
93
+ context.globalAlpha = 0.8;
94
+ context.fillStyle = 'gray';
95
+ // Draw horizontal line at top (representing the "end/boundary")
96
+ context.lineWidth = 2;
97
+ context.strokeStyle = 'gray';
98
+ context.beginPath();
99
+ const topIconCenterX = left + width - 10;
100
+ const topIconCenterY = top + iconPadding + iconSize / 2;
101
+ const lineY = topIconCenterY - iconSize / 3;
102
+ context.moveTo(topIconCenterX - iconSize / 3, lineY);
103
+ context.lineTo(topIconCenterX + iconSize / 3, lineY);
104
+ context.stroke();
105
+ // Draw upward triangle (touching the line)
106
+ context.beginPath();
107
+ context.moveTo(topIconCenterX, lineY); // top point (touching line)
108
+ context.lineTo(topIconCenterX - iconSize / 3, topIconCenterY + iconSize / 4); // bottom left
109
+ context.lineTo(topIconCenterX + iconSize / 3, topIconCenterY + iconSize / 4); // bottom right
110
+ context.closePath();
111
+ context.fill();
112
+ }
113
+ else {
114
+ // Clear top icon bounds when not visible
115
+ this._scrollbarTopIconBounds = undefined;
116
+ }
117
+ // Render BOTTOM icon (downward triangle - END) only if not at bottom
118
+ if (!isAtBottom) {
119
+ // Store icon bounds for hit testing (absolute coordinates)
120
+ this._scrollbarBottomIconBounds = {
121
+ left: left + width - 10 - iconSize / 2,
122
+ top: top + height - iconSize - iconPadding,
123
+ width: iconSize,
124
+ height: iconSize
125
+ };
126
+ context.globalAlpha = 0.8;
127
+ context.fillStyle = 'gray';
128
+ // Draw downward triangle
129
+ context.beginPath();
130
+ const bottomIconCenterX = left + width - 10;
131
+ const bottomIconCenterY = top + height - iconPadding - iconSize / 2;
132
+ const bottomLineY = bottomIconCenterY + iconSize / 3;
133
+ context.moveTo(bottomIconCenterX, bottomLineY); // bottom point (touching line)
134
+ context.lineTo(bottomIconCenterX - iconSize / 3, bottomIconCenterY - iconSize / 4); // top left
135
+ context.lineTo(bottomIconCenterX + iconSize / 3, bottomIconCenterY - iconSize / 4); // top right
136
+ context.closePath();
137
+ context.fill();
138
+ // Draw horizontal line at bottom (representing the "end/boundary")
139
+ context.lineWidth = 2;
140
+ context.strokeStyle = 'gray';
141
+ context.beginPath();
142
+ context.moveTo(bottomIconCenterX - iconSize / 3, bottomLineY);
143
+ context.lineTo(bottomIconCenterX + iconSize / 3, bottomLineY);
144
+ context.stroke();
145
+ }
146
+ else {
147
+ // Clear bottom icon bounds when not visible
148
+ this._scrollbarBottomIconBounds = undefined;
149
+ }
150
+ // Reset alpha
151
+ context.globalAlpha = 1.0;
152
+ }
153
+ ondragstart(e) {
154
+ var _a;
155
+ if (this._scrollbarBounds) {
156
+ const { x, y } = this.transcoordC2S(e.offsetX, e.offsetY);
157
+ const { left, top, width, height } = this._scrollbarBounds;
158
+ // Check if click is in the scrollbar track area (right side)
159
+ // Using a wider hit area for the track
160
+ if (x >= left - 20 && x <= left + width + 10) {
161
+ // Check if click is on the thumb
162
+ if (y >= top && y <= top + height) {
163
+ this._isDraggingScrollbar = true;
164
+ this._dragStartY = y;
165
+ this._startOffsetY = ((_a = this.state.offset) === null || _a === void 0 ? void 0 : _a.y) || 0;
166
+ e.stopPropagation();
167
+ return;
168
+ }
169
+ }
170
+ }
171
+ }
172
+ onclick(e) {
173
+ const { x, y } = this.transcoordC2S(e.offsetX, e.offsetY);
174
+ // Check for TOP icon click (HOME functionality)
175
+ if (this._scrollbarTopIconBounds) {
176
+ const { left, top, width, height } = this._scrollbarTopIconBounds;
177
+ if (x >= left && x <= left + width && y >= top && y <= top + height) {
178
+ // Scroll to top (HOME)
179
+ this.setState({
180
+ offset: {
181
+ x: 0,
182
+ y: 0
183
+ }
184
+ });
185
+ e.stopPropagation();
186
+ return;
187
+ }
188
+ }
189
+ // Check for BOTTOM icon click (END functionality)
190
+ if (this._scrollbarBottomIconBounds) {
191
+ const { left, top, width, height } = this._scrollbarBottomIconBounds;
192
+ if (x >= left && x <= left + width && y >= top && y <= top + height) {
193
+ // Scroll to bottom (END)
194
+ const { height: containerHeight } = this.bounds;
195
+ const { data = [] } = this.state;
196
+ const rowHeight = (this.heights[0] / this.heights_sum) * containerHeight;
197
+ const fullHeight = data.length * rowHeight;
198
+ const minOffsetY = Math.min(-fullHeight + containerHeight, 0);
199
+ this.setState({
200
+ offset: {
201
+ x: 0,
202
+ y: minOffsetY
203
+ }
204
+ });
205
+ e.stopPropagation();
206
+ return;
207
+ }
208
+ }
209
+ // Check for scrollbar track clicks (Page Up/Down)
210
+ if (this._scrollbarBounds) {
211
+ const { left, top, width, height } = this._scrollbarBounds;
212
+ const { height: containerHeight, top: containerTop } = this.bounds;
213
+ // Check if click is in the scrollbar track area (right side)
214
+ if (x >= left - 20 && x <= left + width + 10) {
215
+ // Check for Page Up (click above thumb within track)
216
+ if (y < top && y >= containerTop) {
217
+ const { offset = { x: 0, y: 0 } } = this.state;
218
+ const newOffsetY = Math.min(0, offset.y + containerHeight);
219
+ this.setState({
220
+ offset: {
221
+ x: 0,
222
+ y: newOffsetY
223
+ }
224
+ });
225
+ e.stopPropagation();
226
+ return;
227
+ }
228
+ // Check for Page Down (click below thumb within track)
229
+ if (y > top + height && y <= containerTop + containerHeight) {
230
+ const { offset = { x: 0, y: 0 }, data = [] } = this.state;
231
+ const rowHeight = (this.heights[0] / this.heights_sum) * containerHeight;
232
+ const fullHeight = data.length * rowHeight;
233
+ const minOffsetY = Math.min(-fullHeight + containerHeight, 0);
234
+ const newOffsetY = Math.max(minOffsetY, offset.y - containerHeight);
235
+ this.setState({
236
+ offset: {
237
+ x: 0,
238
+ y: newOffsetY
239
+ }
240
+ });
241
+ e.stopPropagation();
242
+ return;
243
+ }
244
+ }
245
+ }
246
+ }
247
+ ondragmove(e) {
248
+ if (this._isDraggingScrollbar) {
249
+ const { height } = this.bounds;
250
+ const { data } = this.state;
251
+ const fullHeight = ((data && data.length) || 0) * (this.heights[0] / this.heights_sum) * height;
252
+ if (fullHeight <= height)
253
+ return;
254
+ const { y } = this.transcoordC2S(e.offsetX, e.offsetY);
255
+ const deltaY = y - this._dragStartY;
256
+ const scrollRatio = fullHeight / height;
257
+ // Calculate new offset based on scrollbar movement
258
+ // Moving scrollbar down means content moves up (negative offset)
259
+ let newOffsetY = this._startOffsetY - deltaY * scrollRatio;
260
+ const minOffsetY = Math.min(-fullHeight + height, 0);
261
+ newOffsetY = Math.max(Math.min(0, newOffsetY), minOffsetY);
262
+ this.setState({
263
+ offset: {
264
+ x: 0,
265
+ y: newOffsetY
266
+ }
267
+ });
268
+ e.stopPropagation();
269
+ }
270
+ }
271
+ ondragend(e) {
272
+ if (this._isDraggingScrollbar) {
273
+ this._isDraggingScrollbar = false;
274
+ e.stopPropagation();
275
+ }
276
+ }
277
+ onkeydown(e) {
278
+ const { height } = this.bounds;
279
+ const { offset = { x: 0, y: 0 }, data = [] } = this.state;
280
+ const rowHeight = (this.heights[0] / this.heights_sum) * height;
281
+ const fullHeight = data.length * rowHeight;
282
+ const minOffsetY = Math.min(-fullHeight + height, 0);
283
+ let newOffsetY = offset.y;
284
+ switch (e.code) {
285
+ case 'PageUp':
286
+ newOffsetY = Math.min(0, offset.y + height);
287
+ break;
288
+ case 'PageDown':
289
+ newOffsetY = Math.max(minOffsetY, offset.y - height);
290
+ break;
291
+ case 'Home':
292
+ newOffsetY = 0;
293
+ break;
294
+ case 'End':
295
+ newOffsetY = minOffsetY;
296
+ break;
297
+ default:
298
+ return;
299
+ }
300
+ if (newOffsetY !== offset.y) {
301
+ this.setState({
302
+ offset: {
303
+ x: 0,
304
+ y: newOffsetY
305
+ }
306
+ });
307
+ e.stopPropagation();
308
+ e.preventDefault();
309
+ }
52
310
  }
53
311
  created() {
54
312
  this.set('rows', 2);
@@ -229,6 +487,9 @@ let DataList = class DataList extends Container {
229
487
  let { columns } = this;
230
488
  this.buildCells(columns, Number(before.columns));
231
489
  }
490
+ if ('offset' in after || 'height' in after) {
491
+ this.updateViewport();
492
+ }
232
493
  }
233
494
  get eventMap() {
234
495
  return {
@@ -240,7 +501,11 @@ let DataList = class DataList extends Container {
240
501
  wheel: this._onwheel,
241
502
  touchstart: this._ontouchstart,
242
503
  touchmove: this._ontouchmove,
243
- touchend: this._ontouchend
504
+ touchend: this._ontouchend,
505
+ dragstart: this.ondragstart,
506
+ dragmove: this.ondragmove,
507
+ dragend: this.ondragend,
508
+ click: this.onclick
244
509
  }
245
510
  }
246
511
  };
@@ -277,35 +542,11 @@ let DataList = class DataList extends Container {
277
542
  if (!(data instanceof Array)) {
278
543
  data = [data];
279
544
  }
280
- /* 기존의 레코드를 모두 삭제 (템플릿 레코드만 남긴다.) */
281
- this.remove(this.components.slice(this.columns), true /* silent - do not let root-container refreshMappings */);
282
545
  /* template 레코드의 데이터를 클리어시킨다 */
283
546
  var templates = this.getCellsByRow(0);
284
547
  templates.forEach(field => {
285
548
  field.data = '';
286
549
  });
287
- /* 데이터의 크기만큼 새로운 레코드를 만든다 */
288
- if (data.length > 1) {
289
- let newbies = [];
290
- for (let i = 1; i < data.length; i++) {
291
- newbies = newbies.concat(templates.map(field => {
292
- const { refid, id, data, ...org } = field.model;
293
- return Model.compile(org, this.app);
294
- }));
295
- }
296
- this.add(newbies, true /* silent - do not let root-container refreshMappings */);
297
- }
298
- data.forEach((record, idx) => {
299
- let data = {
300
- _idx: idx,
301
- ...record
302
- };
303
- let row = this.getCellsByRow(idx);
304
- row.forEach(field => {
305
- // @ts-ignore TODO - remove this comment later
306
- field.value = data;
307
- });
308
- });
309
550
  /* 스크롤 위치를 조정 */
310
551
  var { height } = this.bounds;
311
552
  var fullHeight = data.length * (this.heights[0] / this.heights_sum) * height;
@@ -329,6 +570,84 @@ let DataList = class DataList extends Container {
329
570
  }
330
571
  });
331
572
  }
573
+ this.updateViewport();
574
+ }
575
+ updateViewport() {
576
+ if (!this.app.isViewMode)
577
+ return;
578
+ const { height } = this.bounds;
579
+ const { offset = { x: 0, y: 0 }, data = [] } = this.state;
580
+ // Calculate row height based on the first row's relative height
581
+ const rowHeight = (this.heights[0] / this.heights_sum) * height;
582
+ const totalRows = data.length;
583
+ if (totalRows === 0) {
584
+ // Clear all data rows if no data
585
+ this.remove(this.components.slice(this.columns), true);
586
+ return;
587
+ }
588
+ // Calculate visible range
589
+ const scrollTop = -offset.y;
590
+ const startIndex = Math.floor(scrollTop / rowHeight);
591
+ // Calculate how many rows fit in the viewport
592
+ const visibleRows = Math.ceil(height / rowHeight);
593
+ const endIndex = Math.min(totalRows, startIndex + visibleRows);
594
+ // Add buffer
595
+ const buffer = 1;
596
+ const renderStart = Math.max(0, startIndex - buffer);
597
+ const renderEnd = Math.min(totalRows, endIndex + buffer);
598
+ this._renderStart = renderStart;
599
+ this._renderEnd = renderEnd;
600
+ // Hide templates (first row)
601
+ const templates = this.getCellsByRow(0);
602
+ templates.forEach(cell => {
603
+ cell.hidden = true;
604
+ });
605
+ // Sync components
606
+ const neededRows = renderEnd - renderStart;
607
+ const currentRows = Math.floor((this.components.length - this.columns) / this.columns);
608
+ // 1. Ensure we have enough components
609
+ if (currentRows < neededRows) {
610
+ const rowsToAdd = neededRows - currentRows;
611
+ let newbies = [];
612
+ for (let i = 0; i < rowsToAdd; i++) {
613
+ newbies = newbies.concat(templates.map(field => {
614
+ const { refid, id, data, ...org } = field.model;
615
+ return Model.compile({
616
+ ...org,
617
+ hidden: false // Ensure new components are visible
618
+ }, this.app);
619
+ }));
620
+ }
621
+ this.add(newbies, true);
622
+ }
623
+ else if (currentRows > neededRows) {
624
+ // Optional: Remove excess components to save memory, or keep them as pool
625
+ // For now, let's remove them to keep logic simple and avoid rendering invisible items
626
+ const rowsToRemove = currentRows - neededRows;
627
+ const removeStart = this.columns + neededRows * this.columns;
628
+ const componentsToRemove = this.components.slice(removeStart);
629
+ this.remove(componentsToRemove, true);
630
+ }
631
+ // 2. Update data for each visible row
632
+ for (let i = 0; i < neededRows; i++) {
633
+ const dataIndex = renderStart + i;
634
+ const record = data[dataIndex];
635
+ const rowData = {
636
+ _idx: dataIndex,
637
+ ...record
638
+ };
639
+ const rowStartComponentIndex = this.columns + i * this.columns;
640
+ for (let j = 0; j < this.columns; j++) {
641
+ const cell = this.components[rowStartComponentIndex + j];
642
+ // @ts-ignore
643
+ cell.value = rowData;
644
+ // Store the data index on the component for layout
645
+ // @ts-ignore
646
+ cell._rowIndex = dataIndex;
647
+ }
648
+ }
649
+ // Trigger layout update
650
+ this.invalidate();
332
651
  }
333
652
  setCellsStyle(cells, style, where, clearBefore = true) {
334
653
  var components = this.components;