gp-grid-react 0.1.2 → 0.1.4

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/dist/index.d.ts CHANGED
@@ -1,15 +1,20 @@
1
1
  import React from "react";
2
- import * as react_jsx_runtime0 from "react/jsx-runtime";
3
2
 
4
3
  //#region ../core/src/types.d.ts
4
+ /** Cell data type primitive types */
5
5
  type CellDataType = "text" | "number" | "boolean" | "date" | "dateString" | "dateTime" | "dateTimeString" | "object";
6
+ /** Cell value type */
6
7
  type CellValue = string | number | boolean | Date | object | null;
8
+ /** Row type */
7
9
  type Row = unknown;
10
+ /** Sort direction type */
8
11
  type SortDirection = "asc" | "desc";
12
+ /** Sort model type */
9
13
  type SortModel = {
10
14
  colId: string;
11
15
  direction: SortDirection;
12
16
  };
17
+ /** Filter model type */
13
18
  type FilterModel = Record<string, string>;
14
19
  interface ColumnDefinition {
15
20
  field: string;
@@ -33,68 +38,88 @@ interface CellRange {
33
38
  endRow: number;
34
39
  endCol: number;
35
40
  }
41
+ /** Data source request */
36
42
  interface DataSourceRequest {
43
+ /** Pagination */
37
44
  pagination: {
45
+ /** Page index */
38
46
  pageIndex: number;
47
+ /** Page size */
39
48
  pageSize: number;
40
49
  };
50
+ /** Sort */
41
51
  sort?: SortModel[];
52
+ /** Filter */
42
53
  filter?: FilterModel;
43
54
  }
55
+ /** Data source response */
44
56
  interface DataSourceResponse<TData = Row> {
57
+ /** Rows */
45
58
  rows: TData[];
59
+ /** Total rows */
46
60
  totalRows: number;
47
61
  }
48
62
  interface DataSource<TData = Row> {
49
63
  fetch(request: DataSourceRequest): Promise<DataSourceResponse<TData>>;
50
64
  }
65
+ /** Slot lifecycle instructions */
51
66
  interface CreateSlotInstruction {
52
67
  type: "CREATE_SLOT";
53
68
  slotId: string;
54
69
  }
70
+ /** Destroy slot instruction */
55
71
  interface DestroySlotInstruction {
56
72
  type: "DESTROY_SLOT";
57
73
  slotId: string;
58
74
  }
75
+ /** Assign slot instruction */
59
76
  interface AssignSlotInstruction {
60
77
  type: "ASSIGN_SLOT";
61
78
  slotId: string;
62
79
  rowIndex: number;
63
80
  rowData: Row;
64
81
  }
82
+ /** Move slot instruction */
65
83
  interface MoveSlotInstruction {
66
84
  type: "MOVE_SLOT";
67
85
  slotId: string;
68
86
  translateY: number;
69
87
  }
88
+ /** Selection instructions */
70
89
  interface SetActiveCellInstruction {
71
90
  type: "SET_ACTIVE_CELL";
72
91
  position: CellPosition | null;
73
92
  }
93
+ /** Set selection range instruction */
74
94
  interface SetSelectionRangeInstruction {
75
95
  type: "SET_SELECTION_RANGE";
76
96
  range: CellRange | null;
77
97
  }
98
+ /** Edit instructions */
78
99
  interface StartEditInstruction {
79
100
  type: "START_EDIT";
80
101
  row: number;
81
102
  col: number;
82
103
  initialValue: CellValue;
83
104
  }
105
+ /** Stop edit instruction */
84
106
  interface StopEditInstruction {
85
107
  type: "STOP_EDIT";
86
108
  }
109
+ /** Commit edit instruction */
87
110
  interface CommitEditInstruction {
88
111
  type: "COMMIT_EDIT";
89
112
  row: number;
90
113
  col: number;
91
114
  value: CellValue;
92
115
  }
116
+ /** Layout instructions */
93
117
  interface SetContentSizeInstruction {
94
118
  type: "SET_CONTENT_SIZE";
95
119
  width: number;
96
120
  height: number;
97
121
  }
122
+ /** Update header instruction */
98
123
  interface UpdateHeaderInstruction {
99
124
  type: "UPDATE_HEADER";
100
125
  colIndex: number;
@@ -102,15 +127,18 @@ interface UpdateHeaderInstruction {
102
127
  sortDirection?: SortDirection;
103
128
  sortIndex?: number;
104
129
  }
130
+ /** Fill handle instructions */
105
131
  interface StartFillInstruction {
106
132
  type: "START_FILL";
107
133
  sourceRange: CellRange;
108
134
  }
135
+ /** Update fill instruction */
109
136
  interface UpdateFillInstruction {
110
137
  type: "UPDATE_FILL";
111
138
  targetRow: number;
112
139
  targetCol: number;
113
140
  }
141
+ /** Commit fill instruction */
114
142
  interface CommitFillInstruction {
115
143
  type: "COMMIT_FILL";
116
144
  filledCells: Array<{
@@ -119,42 +147,73 @@ interface CommitFillInstruction {
119
147
  value: CellValue;
120
148
  }>;
121
149
  }
150
+ /** Cancel fill instruction */
122
151
  interface CancelFillInstruction {
123
152
  type: "CANCEL_FILL";
124
153
  }
154
+ /** Data loading instruction */
125
155
  interface DataLoadingInstruction {
126
156
  type: "DATA_LOADING";
127
157
  }
158
+ /** Data loaded instruction */
128
159
  interface DataLoadedInstruction {
129
160
  type: "DATA_LOADED";
130
161
  totalRows: number;
131
162
  }
163
+ /** Data error instruction */
132
164
  interface DataErrorInstruction {
133
165
  type: "DATA_ERROR";
134
166
  error: string;
135
167
  }
136
- type GridInstruction = CreateSlotInstruction | DestroySlotInstruction | AssignSlotInstruction | MoveSlotInstruction | SetActiveCellInstruction | SetSelectionRangeInstruction | StartEditInstruction | StopEditInstruction | CommitEditInstruction | SetContentSizeInstruction | UpdateHeaderInstruction | StartFillInstruction | UpdateFillInstruction | CommitFillInstruction | CancelFillInstruction | DataLoadingInstruction | DataLoadedInstruction | DataErrorInstruction;
168
+ /** Union type of all instructions */
169
+ type GridInstruction = /** Slot lifecycle */
170
+ CreateSlotInstruction | DestroySlotInstruction | AssignSlotInstruction | MoveSlotInstruction
171
+ /** Selection */ | SetActiveCellInstruction | SetSelectionRangeInstruction
172
+ /** Editing */ | StartEditInstruction | StopEditInstruction | CommitEditInstruction
173
+ /** Layout */ | SetContentSizeInstruction | UpdateHeaderInstruction
174
+ /** Fill handle */ | StartFillInstruction | UpdateFillInstruction | CommitFillInstruction | CancelFillInstruction
175
+ /** Data */ | DataLoadingInstruction | DataLoadedInstruction | DataErrorInstruction;
176
+ /** Cell renderer params */
137
177
  interface CellRendererParams {
178
+ /** Cell value */
138
179
  value: CellValue;
180
+ /** Row data */
139
181
  rowData: Row;
182
+ /** Column definition */
140
183
  column: ColumnDefinition;
184
+ /** Row index */
141
185
  rowIndex: number;
186
+ /** Column index */
142
187
  colIndex: number;
188
+ /** Is active cell */
143
189
  isActive: boolean;
190
+ /** Is selected cell */
144
191
  isSelected: boolean;
192
+ /** Is editing cell */
145
193
  isEditing: boolean;
146
194
  }
195
+ /** Edit renderer params */
147
196
  interface EditRendererParams extends CellRendererParams {
197
+ /** Initial value */
148
198
  initialValue: CellValue;
199
+ /** On value change */
149
200
  onValueChange: (newValue: CellValue) => void;
201
+ /** On commit */
150
202
  onCommit: () => void;
203
+ /** On cancel */
151
204
  onCancel: () => void;
152
205
  }
206
+ /** Header renderer params */
153
207
  interface HeaderRendererParams {
208
+ /** Column definition */
154
209
  column: ColumnDefinition;
210
+ /** Column index */
155
211
  colIndex: number;
212
+ /** Sort direction */
156
213
  sortDirection?: SortDirection;
214
+ /** Sort index */
157
215
  sortIndex?: number;
216
+ /** On sort */
158
217
  onSort: (direction: SortDirection | null, addToExisting: boolean) => void;
159
218
  }
160
219
  //#endregion
@@ -162,10 +221,13 @@ interface HeaderRendererParams {
162
221
  /**
163
222
  * Creates a client-side data source that holds all data in memory.
164
223
  * Sorting and filtering are performed client-side.
224
+ * For large datasets (10k+ rows), sorting is automatically offloaded to a Web Worker.
165
225
  */
166
226
  declare function createClientDataSource<TData extends Row = Row>(data: TData[], options?: {
167
227
  /** Custom field accessor for nested properties */
168
228
  getFieldValue?: (row: TData, field: string) => CellValue;
229
+ /** Use Web Worker for sorting large datasets (default: true) */
230
+ useWorker?: boolean;
169
231
  }): DataSource<TData>;
170
232
  type ServerFetchFunction<TData> = (request: DataSourceRequest) => Promise<DataSourceResponse<TData>>;
171
233
  /**
@@ -180,32 +242,53 @@ declare function createServerDataSource<TData extends Row = Row>(fetchFn: Server
180
242
  declare function createDataSourceFromArray<TData extends Row = Row>(data: TData[]): DataSource<TData>;
181
243
  //#endregion
182
244
  //#region src/Grid.d.ts
245
+ /** React cell renderer: A function that renders a cell */
183
246
  type ReactCellRenderer = (params: CellRendererParams) => React.ReactNode;
247
+ /** React edit renderer: A function that renders the cell while in edit mode */
184
248
  type ReactEditRenderer = (params: EditRendererParams) => React.ReactNode;
249
+ /** React header renderer: A function that renders a header cell */
185
250
  type ReactHeaderRenderer = (params: HeaderRendererParams) => React.ReactNode;
251
+ /** Grid component props */
186
252
  interface GridProps<TData extends Row = Row> {
253
+ /** Column definitions */
187
254
  columns: ColumnDefinition[];
188
255
  /** Data source for the grid */
189
256
  dataSource?: DataSource<TData>;
190
257
  /** Legacy: Raw row data (will be wrapped in a client data source) */
191
258
  rowData?: TData[];
259
+ /** Row height in pixels */
192
260
  rowHeight: number;
261
+ /** Header height in pixels: Default to row height */
193
262
  headerHeight?: number;
263
+ /** Overscan: How many rows to render outside the viewport */
194
264
  overscan?: number;
195
- /** Show filter row below headers */
265
+ /** Show filter row below headers: Default to false */
196
266
  showFilters?: boolean;
197
- /** Debounce time for filter input (ms) */
267
+ /** Debounce time for filter input (ms): Default to 300 */
198
268
  filterDebounce?: number;
199
- /** Enable dark mode styling */
269
+ /** Enable dark mode styling: Default to false */
200
270
  darkMode?: boolean;
271
+ /** Wheel scroll dampening factor when virtual scrolling is active (0-1): Default 0.1 */
272
+ wheelDampening?: number;
273
+ /** Renderer registries */
201
274
  cellRenderers?: Record<string, ReactCellRenderer>;
275
+ /** Edit renderer registries */
202
276
  editRenderers?: Record<string, ReactEditRenderer>;
277
+ /** Header renderer registries */
203
278
  headerRenderers?: Record<string, ReactHeaderRenderer>;
279
+ /** Global cell renderer */
204
280
  cellRenderer?: ReactCellRenderer;
281
+ /** Global edit renderer */
205
282
  editRenderer?: ReactEditRenderer;
283
+ /** Global header renderer */
206
284
  headerRenderer?: ReactHeaderRenderer;
207
285
  }
208
- declare function Grid<TData extends Row = Row>(props: GridProps<TData>): react_jsx_runtime0.JSX.Element;
286
+ /**
287
+ * Grid component
288
+ * @param props - Grid component props
289
+ * @returns Grid React component
290
+ */
291
+ declare function Grid<TData extends Row = Row>(props: GridProps<TData>): React.ReactNode;
209
292
  //#endregion
210
293
  export { type CellDataType, type CellPosition, type CellRange, type CellRendererParams, type CellValue, type ColumnDefinition, type DataSource, type DataSourceRequest, type DataSourceResponse, type EditRendererParams, type FilterModel, Grid, type GridInstruction, type GridProps, type HeaderRendererParams, type ReactCellRenderer, type ReactEditRenderer, type ReactHeaderRenderer, type Row, type SortDirection, type SortModel, createClientDataSource, createDataSourceFromArray, createServerDataSource };
211
294
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -540,9 +540,14 @@ function createInitialState() {
540
540
  totalRows: 0
541
541
  };
542
542
  }
543
+ /**
544
+ * Grid component
545
+ * @param props - Grid component props
546
+ * @returns Grid React component
547
+ */
543
548
  function Grid(props) {
544
549
  injectStyles();
545
- const { columns, dataSource: providedDataSource, rowData, rowHeight, headerHeight = rowHeight, overscan = 3, showFilters = false, filterDebounce = 300, darkMode = false, cellRenderers = {}, editRenderers = {}, headerRenderers = {}, cellRenderer, editRenderer, headerRenderer } = props;
550
+ const { columns, dataSource: providedDataSource, rowData, rowHeight, headerHeight = rowHeight, overscan = 3, showFilters = false, filterDebounce = 300, darkMode = false, wheelDampening = .1, cellRenderers = {}, editRenderers = {}, headerRenderers = {}, cellRenderer, editRenderer, headerRenderer } = props;
546
551
  const containerRef = useRef(null);
547
552
  const coreRef = useRef(null);
548
553
  const [state, dispatch] = useReducer(gridReducer, null, createInitialState);
@@ -603,6 +608,15 @@ function Grid(props) {
603
608
  if (!container || !core) return;
604
609
  core.setViewport(container.scrollTop, container.scrollLeft, container.clientWidth, container.clientHeight);
605
610
  }, []);
611
+ const handleWheel = useCallback((e) => {
612
+ const container = containerRef.current;
613
+ const core = coreRef.current;
614
+ if (!container || !core) return;
615
+ if (!core.isScalingActive()) return;
616
+ e.preventDefault();
617
+ container.scrollTop += e.deltaY * wheelDampening;
618
+ container.scrollLeft += e.deltaX * wheelDampening;
619
+ }, [wheelDampening]);
606
620
  useEffect(() => {
607
621
  const container = containerRef.current;
608
622
  const core = coreRef.current;
@@ -696,23 +710,24 @@ function Grid(props) {
696
710
  if (!state.activeCell || !containerRef.current || state.editingCell) return;
697
711
  const { row, col } = state.activeCell;
698
712
  const container = containerRef.current;
699
- const cellTop = row * rowHeight + totalHeaderHeight;
700
- const cellBottom = cellTop + rowHeight;
713
+ const core = coreRef.current;
714
+ if (!core) return;
715
+ const { start: visibleStart, end: visibleEnd } = core.getVisibleRowRange();
716
+ if (row < visibleStart) container.scrollTop = core.getScrollTopForRow(row);
717
+ else if (row > visibleEnd) {
718
+ const visibleRows = Math.max(1, visibleEnd - visibleStart);
719
+ const targetFirstRow = Math.max(0, row - visibleRows);
720
+ container.scrollTop = core.getScrollTopForRow(targetFirstRow);
721
+ }
701
722
  const cellLeft = columnPositions[col] ?? 0;
702
723
  const cellRight = cellLeft + (columns[col]?.width ?? 0);
703
- const visibleTop = container.scrollTop + totalHeaderHeight;
704
- const visibleBottom = container.scrollTop + container.clientHeight;
705
724
  const visibleLeft = container.scrollLeft;
706
725
  const visibleRight = container.scrollLeft + container.clientWidth;
707
- if (cellTop < visibleTop) container.scrollTop = cellTop - totalHeaderHeight;
708
- else if (cellBottom > visibleBottom) container.scrollTop = cellBottom - container.clientHeight;
709
726
  if (cellLeft < visibleLeft) container.scrollLeft = cellLeft;
710
727
  else if (cellRight > visibleRight) container.scrollLeft = cellRight - container.clientWidth;
711
728
  }, [
712
729
  state.activeCell,
713
730
  state.editingCell,
714
- rowHeight,
715
- totalHeaderHeight,
716
731
  columnPositions,
717
732
  columns
718
733
  ]);
@@ -782,7 +797,7 @@ function Grid(props) {
782
797
  const scrollTop = container.scrollTop;
783
798
  const mouseX = e.clientX - rect.left + scrollLeft;
784
799
  const mouseY = e.clientY - rect.top + scrollTop - totalHeaderHeight;
785
- const targetRow = Math.max(0, Math.floor(mouseY / rowHeight));
800
+ const targetRow = Math.max(0, core.getRowIndexAtDisplayY(mouseY, scrollTop));
786
801
  let targetCol = 0;
787
802
  for (let i = 0; i < columnPositions.length - 1; i++) {
788
803
  if (mouseX >= columnPositions[i] && mouseX < columnPositions[i + 1]) {
@@ -858,7 +873,7 @@ function Grid(props) {
858
873
  const scrollTop = container.scrollTop;
859
874
  const mouseX = e.clientX - rect.left + scrollLeft;
860
875
  const mouseY = e.clientY - rect.top + scrollTop - totalHeaderHeight;
861
- const targetRow = Math.max(0, Math.min(Math.floor(mouseY / rowHeight), core.getRowCount() - 1));
876
+ const targetRow = Math.max(0, Math.min(core.getRowIndexAtDisplayY(mouseY, scrollTop), core.getRowCount() - 1));
862
877
  let targetCol = 0;
863
878
  for (let i = 0; i < columnPositions.length - 1; i++) {
864
879
  if (mouseX >= columnPositions[i] && mouseX < columnPositions[i + 1]) {
@@ -911,7 +926,6 @@ function Grid(props) {
911
926
  }, [
912
927
  isDraggingSelection,
913
928
  totalHeaderHeight,
914
- rowHeight,
915
929
  columnPositions,
916
930
  columns.length
917
931
  ]);
@@ -1060,7 +1074,7 @@ function Grid(props) {
1060
1074
  }, [headerRenderers, headerRenderer]);
1061
1075
  const slotsArray = useMemo(() => Array.from(state.slots.values()), [state.slots]);
1062
1076
  const fillHandlePosition = useMemo(() => {
1063
- const { activeCell, selectionRange } = state;
1077
+ const { activeCell, selectionRange, slots } = state;
1064
1078
  if (!activeCell && !selectionRange) return null;
1065
1079
  let row, col;
1066
1080
  let minCol, maxCol;
@@ -1079,7 +1093,12 @@ function Grid(props) {
1079
1093
  const column = columns[c];
1080
1094
  if (!column || column.editable !== true) return null;
1081
1095
  }
1082
- const cellTop = row * rowHeight + totalHeaderHeight;
1096
+ let cellTop = null;
1097
+ for (const slot of slots.values()) if (slot.rowIndex === row) {
1098
+ cellTop = slot.translateY;
1099
+ break;
1100
+ }
1101
+ if (cellTop === null) return null;
1083
1102
  const cellLeft = columnPositions[col] ?? 0;
1084
1103
  const cellWidth = columns[col]?.width ?? 0;
1085
1104
  return {
@@ -1089,8 +1108,8 @@ function Grid(props) {
1089
1108
  }, [
1090
1109
  state.activeCell,
1091
1110
  state.selectionRange,
1111
+ state.slots,
1092
1112
  rowHeight,
1093
- totalHeaderHeight,
1094
1113
  columnPositions,
1095
1114
  columns
1096
1115
  ]);
@@ -1104,6 +1123,7 @@ function Grid(props) {
1104
1123
  position: "relative"
1105
1124
  },
1106
1125
  onScroll: handleScroll,
1126
+ onWheel: handleWheel,
1107
1127
  onKeyDown: handleKeyDown,
1108
1128
  tabIndex: 0,
1109
1129
  children: /* @__PURE__ */ jsxs("div", {