gp-grid-react 0.6.0 → 0.7.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.
package/dist/index.d.ts CHANGED
@@ -60,6 +60,81 @@ interface FillHandleState {
60
60
  targetCol: number;
61
61
  }
62
62
  //#endregion
63
+ //#region ../core/src/types/highlighting.d.ts
64
+ /**
65
+ * Minimal column info for highlighting context.
66
+ * Uses structural typing to avoid circular dependency with columns.ts.
67
+ */
68
+ interface HighlightColumnInfo {
69
+ field: string;
70
+ colId?: string;
71
+ }
72
+ /**
73
+ * Unified context for row, column, and cell highlighting.
74
+ *
75
+ * - Row context: `rowIndex` is set, `colIndex` is null
76
+ * - Column context: `colIndex` is set, `rowIndex` is null
77
+ * - Cell context: both `rowIndex` and `colIndex` are set
78
+ */
79
+ interface HighlightContext<TData = Record<string, unknown>> {
80
+ /** Row index. Null for column-only context. */
81
+ rowIndex: number | null;
82
+ /** Column index. Null for row-only context. */
83
+ colIndex: number | null;
84
+ /** Column definition. Present for column and cell contexts. */
85
+ column?: HighlightColumnInfo;
86
+ /** Row data. Present for row and cell contexts. */
87
+ rowData?: TData;
88
+ /** Currently hovered cell position, null if not hovering */
89
+ hoverPosition: CellPosition | null;
90
+ /** Currently active (focused) cell position */
91
+ activeCell: CellPosition | null;
92
+ /** Current selection range */
93
+ selectionRange: CellRange | null;
94
+ /** Whether this row/column/cell is hovered (respects hoverScope) */
95
+ isHovered: boolean;
96
+ /** Whether this row/column contains or is the active cell */
97
+ isActive: boolean;
98
+ /** Whether this row/column/cell overlaps or is in the selection range */
99
+ isSelected: boolean;
100
+ }
101
+ /**
102
+ * Grid-level highlighting options.
103
+ * Hover tracking is automatically enabled when any highlighting callback is defined.
104
+ * Each callback type has its own natural interpretation of `isHovered`:
105
+ * - computeRowClasses: isHovered = mouse is on any cell in this row
106
+ * - computeColumnClasses: isHovered = mouse is on any cell in this column
107
+ * - computeCellClasses: isHovered = mouse is on this exact cell
108
+ *
109
+ * For a crosshair effect, implement both computeRowClasses and computeColumnClasses.
110
+ */
111
+ interface HighlightingOptions<TData = Record<string, unknown>> {
112
+ /**
113
+ * Row-level class callback.
114
+ * Classes returned are applied to the row container element.
115
+ * Context has `rowIndex` set, `colIndex` is null.
116
+ * `isHovered` is true when the mouse is on any cell in this row.
117
+ * @returns Array of CSS class names
118
+ */
119
+ computeRowClasses?: (context: HighlightContext<TData>) => string[];
120
+ /**
121
+ * Column-level class callback.
122
+ * Classes returned are applied to all cells in that column (not header).
123
+ * Context has `colIndex` set, `rowIndex` is null.
124
+ * `isHovered` is true when the mouse is on any cell in this column.
125
+ * @returns Array of CSS class names
126
+ */
127
+ computeColumnClasses?: (context: HighlightContext<TData>) => string[];
128
+ /**
129
+ * Cell-level class callback.
130
+ * Classes returned are applied to individual cells for fine-grained control.
131
+ * Context has both `rowIndex` and `colIndex` set.
132
+ * `isHovered` is true only when the mouse is on this exact cell.
133
+ * @returns Array of CSS class names
134
+ */
135
+ computeCellClasses?: (context: HighlightContext<TData>) => string[];
136
+ }
137
+ //#endregion
63
138
  //#region ../core/src/types/columns.d.ts
64
139
  /** Column definition */
65
140
  interface ColumnDefinition {
@@ -73,10 +148,26 @@ interface ColumnDefinition {
73
148
  sortable?: boolean;
74
149
  /** Whether column is filterable. Default: true */
75
150
  filterable?: boolean;
151
+ /** Whether column is hidden. Hidden columns are not rendered but still exist in the definition. Default: false */
152
+ hidden?: boolean;
76
153
  /** Renderer key for adapter lookup, or inline renderer function */
77
154
  cellRenderer?: string;
78
155
  editRenderer?: string;
79
156
  headerRenderer?: string;
157
+ /**
158
+ * Per-column override for column-level highlighting.
159
+ * If defined, overrides grid-level computeColumnClasses for this column.
160
+ * Context has `colIndex` set, `rowIndex` is null.
161
+ * @returns Array of CSS class names to apply to all cells in this column
162
+ */
163
+ computeColumnClasses?: (context: HighlightContext) => string[];
164
+ /**
165
+ * Per-column override for cell-level highlighting.
166
+ * If defined, overrides grid-level computeCellClasses for cells in this column.
167
+ * Context has both `rowIndex` and `colIndex` set.
168
+ * @returns Array of CSS class names to apply to individual cells
169
+ */
170
+ computeCellClasses?: (context: HighlightContext) => string[];
80
171
  }
81
172
  //#endregion
82
173
  //#region ../core/src/types/filters.d.ts
@@ -188,6 +279,11 @@ interface SetActiveCellInstruction {
188
279
  type: "SET_ACTIVE_CELL";
189
280
  position: CellPosition | null;
190
281
  }
282
+ /** Set hover position instruction (for highlighting) */
283
+ interface SetHoverPositionInstruction {
284
+ type: "SET_HOVER_POSITION";
285
+ position: CellPosition | null;
286
+ }
191
287
  /** Set selection range instruction */
192
288
  interface SetSelectionRangeInstruction {
193
289
  type: "SET_SELECTION_RANGE";
@@ -297,19 +393,20 @@ interface DataErrorInstruction {
297
393
  /** Rows added instruction */
298
394
  interface RowsAddedInstruction {
299
395
  type: "ROWS_ADDED";
396
+ indices: number[];
300
397
  count: number;
301
398
  totalRows: number;
302
399
  }
303
400
  /** Rows removed instruction */
304
401
  interface RowsRemovedInstruction {
305
402
  type: "ROWS_REMOVED";
306
- count: number;
403
+ indices: number[];
307
404
  totalRows: number;
308
405
  }
309
406
  /** Rows updated instruction */
310
407
  interface RowsUpdatedInstruction {
311
408
  type: "ROWS_UPDATED";
312
- count: number;
409
+ indices: number[];
313
410
  }
314
411
  /** Transaction processed instruction */
315
412
  interface TransactionProcessedInstruction {
@@ -322,6 +419,7 @@ interface TransactionProcessedInstruction {
322
419
  type GridInstruction = /** Slot lifecycle */
323
420
  CreateSlotInstruction | DestroySlotInstruction | AssignSlotInstruction | MoveSlotInstruction
324
421
  /** Selection */ | SetActiveCellInstruction | SetSelectionRangeInstruction | UpdateVisibleRangeInstruction
422
+ /** Highlighting */ | SetHoverPositionInstruction
325
423
  /** Editing */ | StartEditInstruction | StopEditInstruction | CommitEditInstruction
326
424
  /** Layout */ | SetContentSizeInstruction | UpdateHeaderInstruction
327
425
  /** Filter popup */ | OpenFilterPopupInstruction | CloseFilterPopupInstruction
@@ -330,8 +428,6 @@ CreateSlotInstruction | DestroySlotInstruction | AssignSlotInstruction | MoveSlo
330
428
  /** Transactions */ | RowsAddedInstruction | RowsRemovedInstruction | RowsUpdatedInstruction | TransactionProcessedInstruction;
331
429
  /** Instruction listener: Single instruction Listener that receives a single instruction, used by frameworks to update their state */
332
430
  type InstructionListener = (instruction: GridInstruction) => void;
333
- /** Batch instruction listener: Batch instruction Listener that receives an array of instructions, used by frameworks to update their state */
334
- type BatchInstructionListener = (instructions: GridInstruction[]) => void;
335
431
  //#endregion
336
432
  //#region ../core/src/types/renderers.d.ts
337
433
  /** Cell renderer params */
@@ -405,6 +501,8 @@ interface GridCoreOptions<TData = Row> {
405
501
  transactionDebounceMs?: number;
406
502
  /** Function to extract unique ID from row. Required for mutations. */
407
503
  getRowId?: (row: TData) => RowId;
504
+ /** Row/column/cell highlighting configuration */
505
+ highlighting?: HighlightingOptions<TData>;
408
506
  }
409
507
  //#endregion
410
508
  //#region ../core/src/types/input.d.ts
@@ -485,10 +583,16 @@ interface InputHandlerDeps {
485
583
  getHeaderHeight: () => number;
486
584
  /** Get row height */
487
585
  getRowHeight: () => number;
488
- /** Get column positions array */
586
+ /** Get column positions array (indexed by visible column) */
489
587
  getColumnPositions: () => number[];
490
- /** Get column count */
588
+ /** Get visible column count */
491
589
  getColumnCount: () => number;
590
+ /**
591
+ * Convert visible column index to original column index.
592
+ * Used when columns can be hidden. Returns the original index for selection tracking.
593
+ * If not provided, visible index is used directly (no hidden columns).
594
+ */
595
+ getOriginalColumnIndex?: (visibleIndex: number) => number;
492
596
  }
493
597
  /** Current drag state for UI rendering */
494
598
  interface DragState {
@@ -505,6 +609,12 @@ interface DragState {
505
609
  } | null;
506
610
  }
507
611
  //#endregion
612
+ //#region ../core/src/utils/event-emitter.d.ts
613
+ /**
614
+ * Batch instruction listener for efficient state updates
615
+ */
616
+ type BatchInstructionListener = (instructions: GridInstruction[]) => void;
617
+ //#endregion
508
618
  //#region ../core/src/selection.d.ts
509
619
  type Direction = "up" | "down" | "left" | "right";
510
620
  interface SelectionManagerOptions {
@@ -520,10 +630,10 @@ interface SelectionManagerOptions {
520
630
  declare class SelectionManager {
521
631
  private state;
522
632
  private options;
523
- private listeners;
524
- constructor(options: SelectionManagerOptions);
525
- onInstruction(listener: InstructionListener): () => void;
633
+ private emitter;
634
+ onInstruction: (listener: InstructionListener) => () => void;
526
635
  private emit;
636
+ constructor(options: SelectionManagerOptions);
527
637
  getState(): SelectionState;
528
638
  getActiveCell(): CellPosition | null;
529
639
  getSelectionRange(): CellRange | null;
@@ -588,10 +698,10 @@ interface FillManagerOptions {
588
698
  declare class FillManager {
589
699
  private state;
590
700
  private options;
591
- private listeners;
592
- constructor(options: FillManagerOptions);
593
- onInstruction(listener: InstructionListener): () => void;
701
+ private emitter;
702
+ onInstruction: (listener: InstructionListener) => () => void;
594
703
  private emit;
704
+ constructor(options: FillManagerOptions);
595
705
  getState(): FillHandleState | null;
596
706
  isActive(): boolean;
597
707
  /**
@@ -648,6 +758,14 @@ declare class InputHandler<TData extends Row = Row> {
648
758
  * Handle cell double click event (start editing)
649
759
  */
650
760
  handleCellDoubleClick(rowIndex: number, colIndex: number): void;
761
+ /**
762
+ * Handle cell mouse enter event (for hover highlighting)
763
+ */
764
+ handleCellMouseEnter(rowIndex: number, colIndex: number): void;
765
+ /**
766
+ * Handle cell mouse leave event (for hover highlighting)
767
+ */
768
+ handleCellMouseLeave(): void;
651
769
  /**
652
770
  * Handle fill handle mouse down event
653
771
  */
@@ -683,16 +801,222 @@ declare class InputHandler<TData extends Row = Row> {
683
801
  row: number;
684
802
  col: number;
685
803
  } | null, filterPopupOpen: boolean): KeyboardResult;
686
- /**
687
- * Find column index at a given X coordinate
688
- */
689
- private findColumnAtX;
690
804
  /**
691
805
  * Calculate auto-scroll deltas based on mouse position
692
806
  */
693
807
  private calculateAutoScroll;
694
808
  }
695
809
  //#endregion
810
+ //#region ../core/src/highlight-manager.d.ts
811
+ interface HighlightManagerOptions {
812
+ getActiveCell: () => CellPosition | null;
813
+ getSelectionRange: () => CellRange | null;
814
+ getColumn: (colIndex: number) => ColumnDefinition | undefined;
815
+ }
816
+ /**
817
+ * Manages row/column/cell highlighting state and class computation.
818
+ * Emits SET_HOVER_POSITION instructions when hover position changes.
819
+ */
820
+ declare class HighlightManager<TData = Record<string, unknown>> {
821
+ private options;
822
+ private highlightingOptions;
823
+ private hoverPosition;
824
+ private emitter;
825
+ onInstruction: (listener: InstructionListener) => () => void;
826
+ private emit;
827
+ private rowClassCache;
828
+ private columnClassCache;
829
+ private cellClassCache;
830
+ constructor(options: HighlightManagerOptions, highlightingOptions?: HighlightingOptions<TData>);
831
+ /**
832
+ * Check if highlighting is enabled (any callback defined).
833
+ * Hover tracking is automatically enabled when highlighting is enabled.
834
+ */
835
+ isEnabled(): boolean;
836
+ /**
837
+ * Set the current hover position. Clears caches and emits instruction.
838
+ * Hover tracking is automatically enabled when any highlighting callback is defined.
839
+ */
840
+ setHoverPosition(position: CellPosition | null): void;
841
+ /**
842
+ * Get the current hover position
843
+ */
844
+ getHoverPosition(): CellPosition | null;
845
+ /**
846
+ * Called when selection changes. Clears all caches.
847
+ */
848
+ onSelectionChange(): void;
849
+ /**
850
+ * Build context for row highlighting callback.
851
+ * Returns context with `rowIndex` set, `colIndex` is null.
852
+ * `isHovered` is true when the mouse is on any cell in this row.
853
+ */
854
+ buildRowContext(rowIndex: number, rowData?: TData): HighlightContext<TData>;
855
+ /**
856
+ * Build context for column highlighting callback.
857
+ * Returns context with `colIndex` set, `rowIndex` is null.
858
+ * `isHovered` is true when the mouse is on any cell in this column.
859
+ */
860
+ buildColumnContext(colIndex: number, column: ColumnDefinition): HighlightContext<TData>;
861
+ /**
862
+ * Build context for cell highlighting callback.
863
+ * Returns context with both `rowIndex` and `colIndex` set.
864
+ * `isHovered` is true only when the mouse is on this exact cell.
865
+ */
866
+ buildCellContext(rowIndex: number, colIndex: number, column: ColumnDefinition, rowData?: TData): HighlightContext<TData>;
867
+ /**
868
+ * Compute row classes using cache and user callback
869
+ */
870
+ computeRowClasses(rowIndex: number, rowData?: TData): string[];
871
+ /**
872
+ * Compute column classes using cache and user callback (or per-column override)
873
+ */
874
+ computeColumnClasses(colIndex: number, column: ColumnDefinition): string[];
875
+ /**
876
+ * Compute cell classes using cache and user callback (or per-column override)
877
+ */
878
+ computeCellClasses(rowIndex: number, colIndex: number, column: ColumnDefinition, rowData?: TData): string[];
879
+ /**
880
+ * Compute combined cell classes (column + cell classes flattened)
881
+ */
882
+ computeCombinedCellClasses(rowIndex: number, colIndex: number, column: ColumnDefinition, rowData?: TData): string[];
883
+ /**
884
+ * Clear all caches
885
+ */
886
+ clearAllCaches(): void;
887
+ /**
888
+ * Destroy the manager and release resources
889
+ */
890
+ destroy(): void;
891
+ }
892
+ //#endregion
893
+ //#region ../core/src/sort-filter-manager.d.ts
894
+ interface SortFilterManagerOptions<TData> {
895
+ /** Get all columns */
896
+ getColumns: () => ColumnDefinition[];
897
+ /** Check if sorting is enabled globally */
898
+ isSortingEnabled: () => boolean;
899
+ /** Get cached rows for distinct value computation */
900
+ getCachedRows: () => Map<number, TData>;
901
+ /** Called when sort/filter changes to trigger data refresh */
902
+ onSortFilterChange: () => Promise<void>;
903
+ /** Called after data refresh to update UI */
904
+ onDataRefreshed: () => void;
905
+ }
906
+ /**
907
+ * Manages sorting and filtering state and operations.
908
+ */
909
+ declare class SortFilterManager<TData = Record<string, unknown>> {
910
+ private options;
911
+ private emitter;
912
+ private sortModel;
913
+ private filterModel;
914
+ private openFilterColIndex;
915
+ onInstruction: (listener: InstructionListener) => () => void;
916
+ private emit;
917
+ constructor(options: SortFilterManagerOptions<TData>);
918
+ setSort(colId: string, direction: SortDirection | null, addToExisting?: boolean): Promise<void>;
919
+ getSortModel(): SortModel[];
920
+ setFilter(colId: string, filter: ColumnFilterModel | string | null): Promise<void>;
921
+ getFilterModel(): FilterModel;
922
+ /**
923
+ * Check if a column has an active filter
924
+ */
925
+ hasActiveFilter(colId: string): boolean;
926
+ /**
927
+ * Check if a column is sortable
928
+ */
929
+ isColumnSortable(colIndex: number): boolean;
930
+ /**
931
+ * Check if a column is filterable
932
+ */
933
+ isColumnFilterable(colIndex: number): boolean;
934
+ /**
935
+ * Get distinct values for a column (for filter dropdowns)
936
+ * For array-type columns (like tags), each unique array combination is returned.
937
+ * Arrays are sorted internally for consistent comparison.
938
+ * Limited to maxValues to avoid performance issues with large datasets.
939
+ */
940
+ getDistinctValuesForColumn(colId: string, maxValues?: number): CellValue[];
941
+ /**
942
+ * Open filter popup for a column (toggles if already open for same column)
943
+ */
944
+ openFilterPopup(colIndex: number, anchorRect: {
945
+ top: number;
946
+ left: number;
947
+ width: number;
948
+ height: number;
949
+ }): void;
950
+ /**
951
+ * Close filter popup
952
+ */
953
+ closeFilterPopup(): void;
954
+ /**
955
+ * Get sort info map for header rendering
956
+ */
957
+ getSortInfoMap(): Map<string, {
958
+ direction: SortDirection;
959
+ index: number;
960
+ }>;
961
+ destroy(): void;
962
+ }
963
+ //#endregion
964
+ //#region ../core/src/row-mutation-manager.d.ts
965
+ interface RowMutationManagerOptions<TData> {
966
+ /** Get the cached rows map */
967
+ getCachedRows: () => Map<number, TData>;
968
+ /** Set the cached rows map (for bulk operations) */
969
+ setCachedRows: (rows: Map<number, TData>) => void;
970
+ /** Get total row count */
971
+ getTotalRows: () => number;
972
+ /** Set total row count */
973
+ setTotalRows: (count: number) => void;
974
+ /** Update a single slot after row change */
975
+ updateSlot: (rowIndex: number) => void;
976
+ /** Refresh all slots after bulk changes */
977
+ refreshAllSlots: () => void;
978
+ /** Emit content size change */
979
+ emitContentSize: () => void;
980
+ /** Clear selection if it references invalid rows */
981
+ clearSelectionIfInvalid: (maxValidRow: number) => void;
982
+ }
983
+ /**
984
+ * Manages row CRUD operations and cache management.
985
+ */
986
+ declare class RowMutationManager<TData extends Row = Row> {
987
+ private options;
988
+ private emitter;
989
+ onInstruction: (listener: InstructionListener) => () => void;
990
+ private emit;
991
+ constructor(options: RowMutationManagerOptions<TData>);
992
+ /**
993
+ * Get a row by index.
994
+ */
995
+ getRow(index: number): TData | undefined;
996
+ /**
997
+ * Add rows to the grid at the specified index.
998
+ * If no index is provided, rows are added at the end.
999
+ */
1000
+ addRows(rows: TData[], index?: number): void;
1001
+ /**
1002
+ * Update existing rows with partial data.
1003
+ */
1004
+ updateRows(updates: Array<{
1005
+ index: number;
1006
+ data: Partial<TData>;
1007
+ }>): void;
1008
+ /**
1009
+ * Delete rows at the specified indices.
1010
+ */
1011
+ deleteRows(indices: number[]): void;
1012
+ /**
1013
+ * Set a complete row at the specified index.
1014
+ * Use this for complete row replacement. For partial updates, use updateRows.
1015
+ */
1016
+ setRow(index: number, data: TData): void;
1017
+ destroy(): void;
1018
+ }
1019
+ //#endregion
696
1020
  //#region ../core/src/grid-core.d.ts
697
1021
  declare class GridCore<TData extends Row = Row> {
698
1022
  private columns;
@@ -709,30 +1033,25 @@ declare class GridCore<TData extends Row = Row> {
709
1033
  private totalRows;
710
1034
  private currentPageIndex;
711
1035
  private pageSize;
712
- private sortModel;
713
- private filterModel;
714
- private openFilterColIndex;
715
1036
  readonly selection: SelectionManager;
716
1037
  readonly fill: FillManager;
717
1038
  readonly input: InputHandler<TData>;
1039
+ readonly highlight: HighlightManager<TData> | null;
1040
+ readonly sortFilter: SortFilterManager<TData>;
1041
+ readonly rowMutation: RowMutationManager<TData>;
718
1042
  private readonly slotPool;
719
1043
  private readonly editManager;
720
1044
  private columnPositions;
721
- private listeners;
722
- private batchListeners;
1045
+ private emitter;
1046
+ onInstruction: (listener: InstructionListener) => () => void;
1047
+ onBatchInstruction: (listener: BatchInstructionListener) => () => void;
1048
+ private emit;
1049
+ private emitBatch;
723
1050
  private naturalContentHeight;
724
1051
  private virtualContentHeight;
725
1052
  private scrollRatio;
726
1053
  private isDestroyed;
727
1054
  constructor(options: GridCoreOptions<TData>);
728
- onInstruction(listener: InstructionListener): () => void;
729
- /**
730
- * Subscribe to batched instructions for efficient React state updates.
731
- * Batch listeners receive arrays of instructions instead of individual ones.
732
- */
733
- onBatchInstruction(listener: BatchInstructionListener): () => void;
734
- private emit;
735
- private emitBatch;
736
1055
  /**
737
1056
  * Initialize the grid and load initial data.
738
1057
  */
@@ -746,37 +1065,16 @@ declare class GridCore<TData extends Row = Row> {
746
1065
  private fetchAllData;
747
1066
  setSort(colId: string, direction: SortDirection | null, addToExisting?: boolean): Promise<void>;
748
1067
  setFilter(colId: string, filter: ColumnFilterModel | string | null): Promise<void>;
749
- /**
750
- * Check if a column has an active filter
751
- */
752
1068
  hasActiveFilter(colId: string): boolean;
753
- /**
754
- * Check if a column is sortable
755
- */
756
1069
  isColumnSortable(colIndex: number): boolean;
757
- /**
758
- * Check if a column is filterable
759
- */
760
1070
  isColumnFilterable(colIndex: number): boolean;
761
- /**
762
- * Get distinct values for a column (for filter dropdowns)
763
- * For array-type columns (like tags), each unique array combination is returned.
764
- * Arrays are sorted internally for consistent comparison.
765
- * Limited to MAX_DISTINCT_VALUES to avoid performance issues with large datasets.
766
- */
767
1071
  getDistinctValuesForColumn(colId: string, maxValues?: number): CellValue[];
768
- /**
769
- * Open filter popup for a column (toggles if already open for same column)
770
- */
771
1072
  openFilterPopup(colIndex: number, anchorRect: {
772
1073
  top: number;
773
1074
  left: number;
774
1075
  width: number;
775
1076
  height: number;
776
1077
  }): void;
777
- /**
778
- * Close filter popup
779
- */
780
1078
  closeFilterPopup(): void;
781
1079
  getSortModel(): SortModel[];
782
1080
  getFilterModel(): FilterModel;
@@ -787,8 +1085,6 @@ declare class GridCore<TData extends Row = Row> {
787
1085
  getEditState(): EditState | null;
788
1086
  getCellValue(row: number, col: number): CellValue;
789
1087
  setCellValue(row: number, col: number, value: CellValue): void;
790
- private getFieldValue;
791
- private setFieldValue;
792
1088
  private computeColumnPositions;
793
1089
  private emitContentSize;
794
1090
  private emitHeaders;
@@ -845,6 +1141,31 @@ declare class GridCore<TData extends Row = Row> {
845
1141
  * Useful after in-place data modifications like fill operations.
846
1142
  */
847
1143
  refreshSlotData(): void;
1144
+ /**
1145
+ * Add rows to the grid at the specified index.
1146
+ * If no index is provided, rows are added at the end.
1147
+ */
1148
+ addRows(rows: TData[], index?: number): void;
1149
+ /**
1150
+ * Update existing rows with partial data.
1151
+ */
1152
+ updateRows(updates: Array<{
1153
+ index: number;
1154
+ data: Partial<TData>;
1155
+ }>): void;
1156
+ /**
1157
+ * Delete rows at the specified indices.
1158
+ */
1159
+ deleteRows(indices: number[]): void;
1160
+ /**
1161
+ * Get a row by index.
1162
+ */
1163
+ getRow(index: number): TData | undefined;
1164
+ /**
1165
+ * Set a complete row at the specified index.
1166
+ * Use this for complete row replacement. For partial updates, use updateRows.
1167
+ */
1168
+ setRow(index: number, data: TData): void;
848
1169
  /**
849
1170
  * Update the data source and refresh.
850
1171
  */
@@ -1004,6 +1325,8 @@ interface GridProps<TData extends Row = Row> {
1004
1325
  initialHeight?: number;
1005
1326
  /** Optional ref to access GridCore API */
1006
1327
  gridRef?: React.MutableRefObject<GridRef<TData> | null>;
1328
+ /** Row/column/cell highlighting configuration */
1329
+ highlighting?: HighlightingOptions<TData>;
1007
1330
  }
1008
1331
  //#endregion
1009
1332
  //#region src/Grid.d.ts
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import e,{useCallback as t,useEffect as n,useMemo as r,useReducer as i,useRef as a,useState as o}from"react";import{GridCore as s,GridCore as c,buildCellClasses as l,calculateScaledColumnPositions as u,createClientDataSource as d,createClientDataSource as f,createDataSourceFromArray as p,createDataSourceFromArray as m,createMutableClientDataSource as h,createServerDataSource as g,getTotalWidth as _,injectStyles as v,isCellActive as y,isCellEditing as b,isCellInFillPreview as x,isCellSelected as S}from"gp-grid-core";import{Fragment as C,jsx as w,jsxs as T}from"react/jsx-runtime";const E=[{value:`contains`,label:`Contains`},{value:`notContains`,label:`Does not contain`},{value:`equals`,label:`Equals`},{value:`notEquals`,label:`Does not equal`},{value:`startsWith`,label:`Starts with`},{value:`endsWith`,label:`Ends with`},{value:`blank`,label:`Is blank`},{value:`notBlank`,label:`Is not blank`}];function D({distinctValues:e,currentFilter:n,onApply:i,onClose:a}){let s=t(e=>Array.isArray(e)?e.join(`, `):String(e??``),[]),c=r(()=>{let t=e.filter(e=>e!=null&&e!==``&&!(Array.isArray(e)&&e.length===0)).map(e=>s(e));return Array.from(new Set(t)).sort((e,t)=>{let n=parseFloat(e),r=parseFloat(t);return!isNaN(n)&&!isNaN(r)?n-r:e.localeCompare(t,void 0,{numeric:!0,sensitivity:`base`})})},[e,s]),l=c.length>100,[u,d]=o(r(()=>{if(!n?.conditions[0])return l?`condition`:`values`;let e=n.conditions[0];return e.selectedValues&&e.selectedValues.size>0?`values`:`condition`},[n,l])),f=r(()=>n?.conditions[0]?n.conditions[0].selectedValues??new Set:new Set,[n]),p=r(()=>n?.conditions[0]?n.conditions[0].includeBlank??!0:!0,[n]),[m,h]=o(``),[g,_]=o(f),[v,y]=o(p),[b,x]=o(r(()=>{if(!n?.conditions.length)return[{operator:`contains`,value:``,nextOperator:`and`}];let e=n.conditions[0];if(e.selectedValues&&e.selectedValues.size>0)return[{operator:`contains`,value:``,nextOperator:`and`}];let t=n.combination??`and`;return n.conditions.map(e=>{let n=e;return{operator:n.operator,value:n.value??``,nextOperator:n.nextOperator??t}})},[n])),S=r(()=>{if(!m)return c;let e=m.toLowerCase();return c.filter(t=>t.toLowerCase().includes(e))},[c,m]),D=r(()=>e.some(e=>e==null||e===``),[e]),O=r(()=>S.every(e=>g.has(e))&&(!D||v),[S,g,D,v]),k=t(()=>{_(new Set(S)),D&&y(!0)},[S,D]),A=t(()=>{_(new Set),y(!1)},[]),j=t(e=>{_(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),M=t((e,t)=>{x(n=>{let r=[...n];return r[e]={...r[e],...t},r})},[]),N=t(()=>{x(e=>[...e,{operator:`contains`,value:``,nextOperator:`and`}])},[]),P=t(e=>{x(t=>t.filter((t,n)=>n!==e))},[]),F=t(()=>{if(u===`values`){if(c.every(e=>g.has(e))&&(!D||v)){i(null);return}i({conditions:[{type:`text`,operator:`equals`,selectedValues:g,includeBlank:v}],combination:`and`})}else{let e=b.filter(e=>e.operator===`blank`||e.operator===`notBlank`?!0:e.value.trim()!==``);if(e.length===0){i(null);return}i({conditions:e.map(e=>({type:`text`,operator:e.operator,value:e.value,nextOperator:e.nextOperator})),combination:`and`})}},[u,c,g,v,D,b,i]),I=t(()=>{i(null)},[i]);return T(`div`,{className:`gp-grid-filter-content gp-grid-filter-text`,children:[!l&&T(`div`,{className:`gp-grid-filter-mode-toggle`,children:[w(`button`,{type:`button`,className:u===`values`?`active`:``,onClick:()=>d(`values`),children:`Values`}),w(`button`,{type:`button`,className:u===`condition`?`active`:``,onClick:()=>d(`condition`),children:`Condition`})]}),l&&u===`condition`&&T(`div`,{className:`gp-grid-filter-info`,children:[`Too many unique values (`,c.length,`). Use conditions to filter.`]}),u===`values`&&T(C,{children:[w(`input`,{className:`gp-grid-filter-search`,type:`text`,placeholder:`Search...`,value:m,onChange:e=>h(e.target.value),autoFocus:!0}),T(`div`,{className:`gp-grid-filter-actions`,children:[w(`button`,{type:`button`,onClick:k,disabled:O,children:`Select All`}),w(`button`,{type:`button`,onClick:A,children:`Deselect All`})]}),T(`div`,{className:`gp-grid-filter-list`,children:[D&&T(`label`,{className:`gp-grid-filter-option`,children:[w(`input`,{type:`checkbox`,checked:v,onChange:()=>y(!v)}),w(`span`,{className:`gp-grid-filter-blank`,children:`(Blanks)`})]}),S.map(e=>T(`label`,{className:`gp-grid-filter-option`,children:[w(`input`,{type:`checkbox`,checked:g.has(e),onChange:()=>j(e)}),w(`span`,{children:e})]},e))]})]}),u===`condition`&&T(C,{children:[b.map((e,t)=>T(`div`,{className:`gp-grid-filter-condition`,children:[t>0&&T(`div`,{className:`gp-grid-filter-combination`,children:[w(`button`,{type:`button`,className:b[t-1]?.nextOperator===`and`?`active`:``,onClick:()=>M(t-1,{nextOperator:`and`}),children:`AND`}),w(`button`,{type:`button`,className:b[t-1]?.nextOperator===`or`?`active`:``,onClick:()=>M(t-1,{nextOperator:`or`}),children:`OR`})]}),T(`div`,{className:`gp-grid-filter-row`,children:[w(`select`,{value:e.operator,onChange:e=>M(t,{operator:e.target.value}),autoFocus:t===0,children:E.map(e=>w(`option`,{value:e.value,children:e.label},e.value))}),e.operator!==`blank`&&e.operator!==`notBlank`&&w(`input`,{type:`text`,value:e.value,onChange:e=>M(t,{value:e.target.value}),placeholder:`Value`,className:`gp-grid-filter-text-input`}),b.length>1&&w(`button`,{type:`button`,className:`gp-grid-filter-remove`,onClick:()=>P(t),children:`×`})]})]},t)),w(`button`,{type:`button`,className:`gp-grid-filter-add`,onClick:N,children:`+ Add condition`})]}),T(`div`,{className:`gp-grid-filter-buttons`,children:[w(`button`,{type:`button`,className:`gp-grid-filter-btn-clear`,onClick:I,children:`Clear`}),w(`button`,{type:`button`,className:`gp-grid-filter-btn-apply`,onClick:F,children:`Apply`})]})]})}const O=[{value:`=`,label:`=`},{value:`!=`,label:`≠`},{value:`>`,label:`>`},{value:`<`,label:`<`},{value:`>=`,label:`≥`},{value:`<=`,label:`≤`},{value:`between`,label:`↔`},{value:`blank`,label:`Is blank`},{value:`notBlank`,label:`Not blank`}];function k({currentFilter:e,onApply:n,onClose:i}){let[a,s]=o(r(()=>{if(!e?.conditions.length)return[{operator:`=`,value:``,valueTo:``,nextOperator:`and`}];let t=e.combination??`and`;return e.conditions.map(e=>{let n=e;return{operator:n.operator,value:n.value==null?``:String(n.value),valueTo:n.valueTo==null?``:String(n.valueTo),nextOperator:n.nextOperator??t}})},[e])),c=t((e,t)=>{s(n=>{let r=[...n];return r[e]={...r[e],...t},r})},[]),l=t(()=>{s(e=>[...e,{operator:`=`,value:``,valueTo:``,nextOperator:`and`}])},[]),u=t(e=>{s(t=>t.filter((t,n)=>n!==e))},[]),d=t(()=>{let e=a.filter(e=>e.operator===`blank`||e.operator===`notBlank`?!0:e.operator===`between`?e.value!==``&&e.valueTo!==``:e.value!==``);if(e.length===0){n(null);return}n({conditions:e.map(e=>({type:`number`,operator:e.operator,value:e.value?parseFloat(e.value):void 0,valueTo:e.valueTo?parseFloat(e.valueTo):void 0,nextOperator:e.nextOperator})),combination:`and`})},[a,n]),f=t(()=>{n(null)},[n]);return T(`div`,{className:`gp-grid-filter-content gp-grid-filter-number`,children:[a.map((e,t)=>T(`div`,{className:`gp-grid-filter-condition`,children:[t>0&&T(`div`,{className:`gp-grid-filter-combination`,children:[w(`button`,{type:`button`,className:a[t-1]?.nextOperator===`and`?`active`:``,onClick:()=>c(t-1,{nextOperator:`and`}),children:`AND`}),w(`button`,{type:`button`,className:a[t-1]?.nextOperator===`or`?`active`:``,onClick:()=>c(t-1,{nextOperator:`or`}),children:`OR`})]}),T(`div`,{className:`gp-grid-filter-row`,children:[w(`select`,{value:e.operator,onChange:e=>c(t,{operator:e.target.value}),children:O.map(e=>w(`option`,{value:e.value,children:e.label},e.value))}),e.operator!==`blank`&&e.operator!==`notBlank`&&w(`input`,{type:`number`,value:e.value,onChange:e=>c(t,{value:e.target.value}),placeholder:`Value`}),e.operator===`between`&&T(C,{children:[w(`span`,{className:`gp-grid-filter-to`,children:`to`}),w(`input`,{type:`number`,value:e.valueTo,onChange:e=>c(t,{valueTo:e.target.value}),placeholder:`Value`})]}),a.length>1&&w(`button`,{type:`button`,className:`gp-grid-filter-remove`,onClick:()=>u(t),children:`×`})]})]},t)),w(`button`,{type:`button`,className:`gp-grid-filter-add`,onClick:l,children:`+ Add condition`}),T(`div`,{className:`gp-grid-filter-buttons`,children:[w(`button`,{type:`button`,className:`gp-grid-filter-btn-clear`,onClick:f,children:`Clear`}),w(`button`,{type:`button`,className:`gp-grid-filter-btn-apply`,onClick:d,children:`Apply`})]})]})}const A=[{value:`=`,label:`=`},{value:`!=`,label:`≠`},{value:`>`,label:`>`},{value:`<`,label:`<`},{value:`between`,label:`↔`},{value:`blank`,label:`Is blank`},{value:`notBlank`,label:`Not blank`}];function j(e){if(!e)return``;let t=typeof e==`string`?new Date(e):e;return isNaN(t.getTime())?``:t.toISOString().split(`T`)[0]}function M({currentFilter:e,onApply:n,onClose:i}){let[a,s]=o(r(()=>{if(!e?.conditions.length)return[{operator:`=`,value:``,valueTo:``,nextOperator:`and`}];let t=e.combination??`and`;return e.conditions.map(e=>{let n=e;return{operator:n.operator,value:j(n.value),valueTo:j(n.valueTo),nextOperator:n.nextOperator??t}})},[e])),c=t((e,t)=>{s(n=>{let r=[...n];return r[e]={...r[e],...t},r})},[]),l=t(()=>{s(e=>[...e,{operator:`=`,value:``,valueTo:``,nextOperator:`and`}])},[]),u=t(e=>{s(t=>t.filter((t,n)=>n!==e))},[]),d=t(()=>{let e=a.filter(e=>e.operator===`blank`||e.operator===`notBlank`?!0:e.operator===`between`?e.value!==``&&e.valueTo!==``:e.value!==``);if(e.length===0){n(null);return}n({conditions:e.map(e=>({type:`date`,operator:e.operator,value:e.value||void 0,valueTo:e.valueTo||void 0,nextOperator:e.nextOperator})),combination:`and`})},[a,n]),f=t(()=>{n(null)},[n]);return T(`div`,{className:`gp-grid-filter-content gp-grid-filter-date`,children:[a.map((e,t)=>T(`div`,{className:`gp-grid-filter-condition`,children:[t>0&&T(`div`,{className:`gp-grid-filter-combination`,children:[w(`button`,{type:`button`,className:a[t-1]?.nextOperator===`and`?`active`:``,onClick:()=>c(t-1,{nextOperator:`and`}),children:`AND`}),w(`button`,{type:`button`,className:a[t-1]?.nextOperator===`or`?`active`:``,onClick:()=>c(t-1,{nextOperator:`or`}),children:`OR`})]}),T(`div`,{className:`gp-grid-filter-row`,children:[w(`select`,{value:e.operator,onChange:e=>c(t,{operator:e.target.value}),children:A.map(e=>w(`option`,{value:e.value,children:e.label},e.value))}),e.operator!==`blank`&&e.operator!==`notBlank`&&w(`input`,{type:`date`,value:e.value,onChange:e=>c(t,{value:e.target.value})}),e.operator===`between`&&T(C,{children:[w(`span`,{className:`gp-grid-filter-to`,children:`to`}),w(`input`,{type:`date`,value:e.valueTo,onChange:e=>c(t,{valueTo:e.target.value})})]}),a.length>1&&w(`button`,{type:`button`,className:`gp-grid-filter-remove`,onClick:()=>u(t),children:`×`})]})]},t)),w(`button`,{type:`button`,className:`gp-grid-filter-add`,onClick:l,children:`+ Add condition`}),T(`div`,{className:`gp-grid-filter-buttons`,children:[w(`button`,{type:`button`,className:`gp-grid-filter-btn-clear`,onClick:f,children:`Clear`}),w(`button`,{type:`button`,className:`gp-grid-filter-btn-apply`,onClick:d,children:`Apply`})]})]})}function N({column:e,colIndex:r,anchorRect:i,distinctValues:o,currentFilter:s,onApply:c,onClose:l}){let u=a(null);n(()=>{let e=e=>{let t=e.target;t.closest(`.gp-grid-filter-icon`)||u.current&&!u.current.contains(t)&&l()},t=e=>{e.key===`Escape`&&l()};return requestAnimationFrame(()=>{document.addEventListener(`mousedown`,e),document.addEventListener(`keydown`,t)}),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`keydown`,t)}},[l]);let d=t(t=>{c(e.colId??e.field,t),l()},[e,c,l]),f={position:`fixed`,top:i.top+i.height+4,left:i.left,minWidth:Math.max(200,i.width),zIndex:1e4},p=e.cellDataType,m=p===`text`||p===`object`,h=p===`number`,g=p===`date`||p===`dateString`||p===`dateTime`||p===`dateTimeString`;return T(`div`,{ref:u,className:`gp-grid-filter-popup`,style:f,children:[T(`div`,{className:`gp-grid-filter-header`,children:[`Filter: `,e.headerName??e.field]}),m&&w(D,{distinctValues:o,currentFilter:s,onApply:d,onClose:l}),h&&w(k,{currentFilter:s,onApply:d,onClose:l}),g&&w(M,{currentFilter:s,onApply:d,onClose:l}),!m&&!h&&!g&&w(D,{distinctValues:o,currentFilter:s,onApply:d,onClose:l})]})}function P(e){return{slots:new Map,activeCell:null,selectionRange:null,editingCell:null,contentWidth:0,contentHeight:e?.initialHeight??0,viewportWidth:e?.initialWidth??0,headers:new Map,filterPopup:null,isLoading:!1,error:null,totalRows:0,visibleRowRange:null}}function F(e,t,n){switch(e.type){case`CREATE_SLOT`:return t.set(e.slotId,{slotId:e.slotId,rowIndex:-1,rowData:{},translateY:0}),null;case`DESTROY_SLOT`:return t.delete(e.slotId),null;case`ASSIGN_SLOT`:{let n=t.get(e.slotId);return n&&t.set(e.slotId,{...n,rowIndex:e.rowIndex,rowData:e.rowData}),null}case`MOVE_SLOT`:{let n=t.get(e.slotId);return n&&t.set(e.slotId,{...n,translateY:e.translateY}),null}case`SET_ACTIVE_CELL`:return{activeCell:e.position};case`SET_SELECTION_RANGE`:return{selectionRange:e.range};case`UPDATE_VISIBLE_RANGE`:return{visibleRowRange:{start:e.start,end:e.end}};case`START_EDIT`:return{editingCell:{row:e.row,col:e.col,initialValue:e.initialValue}};case`STOP_EDIT`:return{editingCell:null};case`SET_CONTENT_SIZE`:return{contentWidth:e.width,contentHeight:e.height,viewportWidth:e.viewportWidth};case`UPDATE_HEADER`:return n.set(e.colIndex,{column:e.column,sortDirection:e.sortDirection,sortIndex:e.sortIndex,sortable:e.sortable,filterable:e.filterable,hasFilter:e.hasFilter}),null;case`OPEN_FILTER_POPUP`:return{filterPopup:{isOpen:!0,colIndex:e.colIndex,column:e.column,anchorRect:e.anchorRect,distinctValues:e.distinctValues,currentFilter:e.currentFilter}};case`CLOSE_FILTER_POPUP`:return{filterPopup:null};case`DATA_LOADING`:return{isLoading:!0,error:null};case`DATA_LOADED`:return{isLoading:!1,totalRows:e.totalRows};case`DATA_ERROR`:return{isLoading:!1,error:e.error};case`ROWS_ADDED`:case`ROWS_REMOVED`:return{totalRows:e.totalRows};case`ROWS_UPDATED`:case`TRANSACTION_PROCESSED`:return null;default:return null}}function I(e,t){if(t.type===`RESET`)return P();let{instructions:n}=t;if(n.length===0)return e;let r=new Map(e.slots),i=new Map(e.headers),a={};for(let e of n){let t=F(e,r,i);t&&(a={...a,...t})}return{...e,...a,slots:r,headers:i}}function L(e,t){for(let n of e.values())if(n.rowIndex===t)return n;return null}function R(e,t,n,r,i,a){let o=L(a,n),s=(o?o.translateY:i+n*r)-t.scrollTop,c=s+r,l=i,u=t.clientHeight;if(s<l)t.scrollTop=e.getScrollTopForRow(n);else if(c>u){let a=t.clientHeight-i,o=Math.floor(a/r),s=Math.max(0,n-o+1);t.scrollTop=e.getScrollTopForRow(s)}}function ee(e,r,i,s){let{activeCell:c,selectionRange:l,editingCell:u,filterPopupOpen:d,rowHeight:f,headerHeight:p,columnPositions:m,slots:h}=s,g=a(null),[_,v]=o({isDragging:!1,dragType:null,fillSourceRange:null,fillTarget:null});n(()=>{let t=e.current;t?.input&&t.input.updateDeps({getHeaderHeight:()=>p,getRowHeight:()=>f,getColumnPositions:()=>m,getColumnCount:()=>i.length})},[e,p,f,m,i.length]);let y=t((e,t)=>{g.current&&clearInterval(g.current),g.current=setInterval(()=>{let n=r.current;n&&(n.scrollTop+=t,n.scrollLeft+=e)},16)},[r]),b=t(()=>{g.current&&=(clearInterval(g.current),null)},[]),x=t(()=>{let e=r.current;if(!e)return null;let t=e.getBoundingClientRect();return{top:t.top,left:t.left,width:t.width,height:t.height,scrollTop:e.scrollTop,scrollLeft:e.scrollLeft}},[r]),S=e=>({clientX:e.clientX,clientY:e.clientY,button:e.button,shiftKey:e.shiftKey,ctrlKey:e.ctrlKey,metaKey:e.metaKey}),C=t(()=>{let t=t=>{let n=e.current,r=x();if(!n?.input||!r)return;let i=n.input.handleDragMove(S(t),r);i&&(i.autoScroll?y(i.autoScroll.dx,i.autoScroll.dy):b(),v(n.input.getDragState()))},n=()=>{let r=e.current;r?.input&&(r.input.handleDragEnd(),v(r.input.getDragState())),b(),document.removeEventListener(`mousemove`,t),document.removeEventListener(`mouseup`,n)};document.addEventListener(`mousemove`,t),document.addEventListener(`mouseup`,n)},[e,x,y,b]),w=t((t,n,i)=>{let a=e.current;if(!a?.input)return;let o=a.input.handleCellMouseDown(t,n,S(i));o.focusContainer&&r.current?.focus(),o.startDrag===`selection`&&(a.input.startSelectionDrag(),v(a.input.getDragState()),C())},[e,r,C]),T=t((t,n)=>{let r=e.current;r?.input&&r.input.handleCellDoubleClick(t,n)},[e]),E=t(t=>{let n=e.current;if(!n?.input)return;let r=n.input.handleFillHandleMouseDown(c,l,S(t));r.preventDefault&&t.preventDefault(),r.stopPropagation&&t.stopPropagation(),r.startDrag===`fill`&&(v(n.input.getDragState()),C())},[e,c,l,C]),D=t((t,n)=>{let r=e.current;if(!r?.input)return;let a=i[t];if(!a)return;let o=a.colId??a.field;r.input.handleHeaderClick(o,n.shiftKey)},[e,i]),O=t(t=>{let n=e.current,i=r.current;if(!n?.input)return;let a=n.input.handleKeyDown({key:t.key,shiftKey:t.shiftKey,ctrlKey:t.ctrlKey,metaKey:t.metaKey},c,u,d);a.preventDefault&&t.preventDefault(),a.scrollToCell&&i&&R(n,i,a.scrollToCell.row,f,p,h)},[e,r,c,u,d,f,p,h]),k=t((t,n)=>{let i=e.current,a=r.current;if(!i?.input||!a)return;let o=i.input.handleWheel(t.deltaY,t.deltaX,n);o&&(t.preventDefault(),a.scrollTop+=o.dy,a.scrollLeft+=o.dx)},[e,r]);return n(()=>()=>{b()},[b]),{handleCellMouseDown:w,handleCellDoubleClick:T,handleFillHandleMouseDown:E,handleHeaderClick:D,handleKeyDown:O,handleWheel:k,dragState:_}}function z(e,t){let n=t.split(`.`),r=e;for(let e of n){if(typeof r!=`object`||!r)return null;r=r[e]}return r??null}function te(e){let{column:t,rowData:n,rowIndex:r,colIndex:i,isActive:a,isSelected:o,isEditing:s,cellRenderers:c,globalCellRenderer:l}=e,u=z(n,t.field),d={value:u,rowData:n,column:t,rowIndex:r,colIndex:i,isActive:a,isSelected:o,isEditing:s};if(t.cellRenderer&&typeof t.cellRenderer==`string`){let e=c[t.cellRenderer];if(e)return e(d)}return l?l(d):u==null?``:String(u)}function ne(e){let{column:t,rowData:n,rowIndex:r,colIndex:i,initialValue:a,coreRef:o,editRenderers:s,globalEditRenderer:c}=e,l=o.current;if(!l)return null;let u={value:z(n,t.field),rowData:n,column:t,rowIndex:r,colIndex:i,isActive:!0,isSelected:!0,isEditing:!0,initialValue:a,onValueChange:e=>l.updateEditValue(e),onCommit:()=>l.commitEdit(),onCancel:()=>l.cancelEdit()};if(t.editRenderer&&typeof t.editRenderer==`string`){let e=s[t.editRenderer];if(e)return e(u)}return c?c(u):w(`input`,{className:`gp-grid-edit-input`,type:`text`,defaultValue:a==null?``:String(a),autoFocus:!0,onFocus:e=>e.target.select(),onChange:e=>l.updateEditValue(e.target.value),onKeyDown:e=>{e.stopPropagation(),e.key===`Enter`?l.commitEdit():e.key===`Escape`?l.cancelEdit():e.key===`Tab`&&(e.preventDefault(),l.commitEdit(),l.selection.moveFocus(e.shiftKey?`left`:`right`,!1))},onBlur:()=>l.commitEdit()})}function re(e){let{column:t,colIndex:n,sortDirection:r,sortIndex:i,sortable:a,filterable:o,hasFilter:s,coreRef:c,containerRef:l,headerRenderers:u,globalHeaderRenderer:d}=e,f=c.current,p={column:t,colIndex:n,sortDirection:r,sortIndex:i,sortable:a,filterable:o,hasFilter:s,onSort:(e,n)=>{f&&a&&f.setSort(t.colId??t.field,e,n)},onFilterClick:()=>{if(f&&o){let e=l.current?.querySelector(`[data-col-index="${n}"]`);if(e){let t=e.getBoundingClientRect();f.openFilterPopup(n,{top:t.top,left:t.left,width:t.width,height:t.height})}}}};if(t.headerRenderer&&typeof t.headerRenderer==`string`){let e=u[t.headerRenderer];if(e)return e(p)}return d?d(p):T(C,{children:[w(`span`,{className:`gp-grid-header-text`,children:t.headerName??t.field}),T(`span`,{className:`gp-grid-header-icons`,children:[a&&T(`span`,{className:`gp-grid-sort-arrows`,children:[T(`span`,{className:`gp-grid-sort-arrows-stack`,children:[w(`svg`,{className:`gp-grid-sort-arrow-up${r===`asc`?` active`:``}`,width:`8`,height:`6`,viewBox:`0 0 8 6`,children:w(`path`,{d:`M4 0L8 6H0L4 0Z`,fill:`currentColor`})}),w(`svg`,{className:`gp-grid-sort-arrow-down${r===`desc`?` active`:``}`,width:`8`,height:`6`,viewBox:`0 0 8 6`,children:w(`path`,{d:`M4 6L0 0H8L4 6Z`,fill:`currentColor`})})]}),i!==void 0&&i>0&&w(`span`,{className:`gp-grid-sort-index`,children:i})]}),o&&w(`span`,{className:`gp-grid-filter-icon${s?` active`:``}`,onMouseDown:e=>{e.stopPropagation(),e.preventDefault(),p.onFilterClick()},onClick:e=>{e.stopPropagation()},children:w(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`currentColor`,children:w(`path`,{d:`M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z`})})})]})]})}function B(e){v();let{columns:o,dataSource:s,rowData:d,rowHeight:p,headerHeight:h=p,overscan:g=3,sortingEnabled:C=!0,darkMode:E=!1,wheelDampening:D=.1,cellRenderers:O={},editRenderers:k={},headerRenderers:A={},cellRenderer:j,editRenderer:M,headerRenderer:F,initialWidth:L,initialHeight:R,gridRef:z}=e,B=a(null),V=a(null),H=a(null),[U,W]=i(I,{initialWidth:L,initialHeight:R},P),G=h,{dataSource:K,ownsDataSource:q}=r(()=>s?{dataSource:s,ownsDataSource:!1}:d?{dataSource:m(d),ownsDataSource:!0}:{dataSource:f([]),ownsDataSource:!0},[s,d]);n(()=>{let e=H.current;e&&e!==K&&W({type:`RESET`}),H.current=K},[K]);let{positions:J,widths:Y}=r(()=>u(o,U.viewportWidth),[o,U.viewportWidth]),X=_(J),{handleCellMouseDown:ie,handleCellDoubleClick:ae,handleFillHandleMouseDown:oe,handleHeaderClick:se,handleKeyDown:ce,handleWheel:le,dragState:Z}=ee(V,B,o,{activeCell:U.activeCell,selectionRange:U.selectionRange,editingCell:U.editingCell,filterPopupOpen:U.filterPopup?.isOpen??!1,rowHeight:p,headerHeight:G,columnPositions:J,slots:U.slots});n(()=>{let e=new c({columns:o,dataSource:K,rowHeight:p,headerHeight:G,overscan:g,sortingEnabled:C});V.current=e,z&&(z.current={core:e});let t=e.onBatchInstruction(e=>{W({type:`BATCH_INSTRUCTIONS`,instructions:e})});return e.initialize(),()=>{t(),e.destroy(),q&&K.destroy?.(),V.current=null,z&&(z.current=null)}},[o,K,q,p,G,g,C,z]),n(()=>{let e=K;if(e.subscribe)return e.subscribe(()=>{V.current?.refresh()})},[K]);let Q=t(()=>{let e=B.current,t=V.current;!e||!t||t.setViewport(e.scrollTop,e.scrollLeft,e.clientWidth,e.clientHeight)},[]);n(()=>{let e=B.current,t=V.current;if(!e||!t)return;if(typeof ResizeObserver>`u`){Q();return}let n=new ResizeObserver(()=>{t.setViewport(e.scrollTop,e.scrollLeft,e.clientWidth,e.clientHeight)});return n.observe(e),Q(),()=>n.disconnect()},[Q]);let ue=t((e,t)=>{let n=V.current;n&&n.setFilter(e,t)},[]),de=t(()=>{let e=V.current;e&&e.closeFilterPopup()},[]),fe=r(()=>Array.from(U.slots.values()),[U.slots]),$=r(()=>{let{activeCell:e,selectionRange:t,slots:n}=U;if(!e&&!t)return null;let r,i,a,s;if(t)r=Math.max(t.startRow,t.endRow),i=Math.max(t.startCol,t.endCol),a=Math.min(t.startCol,t.endCol),s=Math.max(t.startCol,t.endCol);else if(e)r=e.row,i=e.col,a=i,s=i;else return null;for(let e=a;e<=s;e++){let t=o[e];if(!t||t.editable!==!0)return null}let c=null;for(let e of n.values())if(e.rowIndex===r){c=e.translateY;break}if(c===null)return null;let l=J[i]??0,u=Y[i]??0;return{top:c+p-5,left:l+u-20}},[U.activeCell,U.selectionRange,U.slots,p,J,Y,o]);return T(`div`,{ref:B,className:`gp-grid-container${E?` gp-grid-container--dark`:``}`,style:{width:`100%`,height:`100%`,overflow:`auto`,position:`relative`},onScroll:Q,onWheel:e=>le(e,D),onKeyDown:ce,tabIndex:0,children:[T(`div`,{style:{width:Math.max(U.contentWidth,X),height:Math.max(U.contentHeight,G),position:`relative`,minWidth:`100%`},children:[w(`div`,{className:`gp-grid-header`,style:{position:`sticky`,top:0,left:0,height:h,width:Math.max(U.contentWidth,X),minWidth:`100%`},children:o.map((e,t)=>{let n=U.headers.get(t);return w(`div`,{className:`gp-grid-header-cell`,"data-col-index":t,style:{position:`absolute`,left:`${J[t]}px`,top:0,width:`${Y[t]}px`,height:`${h}px`,background:`transparent`},onClick:e=>se(t,e),children:re({column:e,colIndex:t,sortDirection:n?.sortDirection,sortIndex:n?.sortIndex,sortable:n?.sortable??!0,filterable:n?.filterable??!0,hasFilter:n?.hasFilter??!1,coreRef:V,containerRef:B,headerRenderers:A,globalHeaderRenderer:F})},e.colId??e.field)})}),fe.map(e=>e.rowIndex<0?null:w(`div`,{className:`gp-grid-row ${e.rowIndex%2==0?`gp-grid-row--even`:``}`,style:{position:`absolute`,top:0,left:0,transform:`translateY(${e.translateY}px)`,width:`${Math.max(U.contentWidth,X)}px`,height:`${p}px`},children:o.map((t,n)=>{let r=b(e.rowIndex,n,U.editingCell),i=y(e.rowIndex,n,U.activeCell),a=S(e.rowIndex,n,U.selectionRange);return w(`div`,{className:l(i,a,r,x(e.rowIndex,n,Z.dragType===`fill`,Z.fillSourceRange,Z.fillTarget)),style:{position:`absolute`,left:`${J[n]}px`,top:0,width:`${Y[n]}px`,height:`${p}px`},onMouseDown:t=>ie(e.rowIndex,n,t),onDoubleClick:()=>ae(e.rowIndex,n),children:r&&U.editingCell?ne({column:t,rowData:e.rowData,rowIndex:e.rowIndex,colIndex:n,initialValue:U.editingCell.initialValue,coreRef:V,editRenderers:k,globalEditRenderer:M}):te({column:t,rowData:e.rowData,rowIndex:e.rowIndex,colIndex:n,isActive:i,isSelected:a,isEditing:r,cellRenderers:O,globalCellRenderer:j})},`${e.slotId}-${n}`)})},e.slotId)),$&&!U.editingCell&&w(`div`,{className:`gp-grid-fill-handle`,style:{position:`absolute`,top:$.top,left:$.left,zIndex:200},onMouseDown:oe}),U.isLoading&&T(`div`,{className:`gp-grid-loading`,children:[w(`div`,{className:`gp-grid-loading-spinner`}),`Loading...`]}),U.error&&T(`div`,{className:`gp-grid-error`,children:[`Error: `,U.error]}),!U.isLoading&&!U.error&&U.totalRows===0&&w(`div`,{className:`gp-grid-empty`,children:`No data to display`})]}),U.filterPopup?.isOpen&&U.filterPopup.column&&U.filterPopup.anchorRect&&w(N,{column:U.filterPopup.column,colIndex:U.filterPopup.colIndex,anchorRect:U.filterPopup.anchorRect,distinctValues:U.filterPopup.distinctValues,currentFilter:U.filterPopup.currentFilter,onApply:ue,onClose:de})]})}export{B as Grid,s as GridCore,d as createClientDataSource,p as createDataSourceFromArray,h as createMutableClientDataSource,g as createServerDataSource};
1
+ import e,{useCallback as t,useEffect as n,useMemo as r,useReducer as i,useRef as a,useState as o}from"react";import{GridCore as s,GridCore as c,buildCellClasses as l,calculateScaledColumnPositions as u,createClientDataSource as d,createClientDataSource as f,createDataSourceFromArray as p,createDataSourceFromArray as m,createMutableClientDataSource as h,createServerDataSource as g,getTotalWidth as _,injectStyles as v,isCellActive as y,isCellEditing as b,isCellInFillPreview as x,isCellSelected as S}from"gp-grid-core";import{Fragment as C,jsx as w,jsxs as T}from"react/jsx-runtime";const E=[{value:`contains`,label:`Contains`},{value:`notContains`,label:`Does not contain`},{value:`equals`,label:`Equals`},{value:`notEquals`,label:`Does not equal`},{value:`startsWith`,label:`Starts with`},{value:`endsWith`,label:`Ends with`},{value:`blank`,label:`Is blank`},{value:`notBlank`,label:`Is not blank`}];function D({distinctValues:e,currentFilter:n,onApply:i,onClose:a}){let s=t(e=>Array.isArray(e)?e.join(`, `):String(e??``),[]),c=r(()=>{let t=e.filter(e=>e!=null&&e!==``&&!(Array.isArray(e)&&e.length===0)).map(e=>s(e));return Array.from(new Set(t)).sort((e,t)=>{let n=parseFloat(e),r=parseFloat(t);return!isNaN(n)&&!isNaN(r)?n-r:e.localeCompare(t,void 0,{numeric:!0,sensitivity:`base`})})},[e,s]),l=c.length>100,[u,d]=o(r(()=>{if(!n?.conditions[0])return l?`condition`:`values`;let e=n.conditions[0];return e.selectedValues&&e.selectedValues.size>0?`values`:`condition`},[n,l])),f=r(()=>n?.conditions[0]?n.conditions[0].selectedValues??new Set:new Set,[n]),p=r(()=>n?.conditions[0]?n.conditions[0].includeBlank??!0:!0,[n]),[m,h]=o(``),[g,_]=o(f),[v,y]=o(p),[b,x]=o(r(()=>{if(!n?.conditions.length)return[{operator:`contains`,value:``,nextOperator:`and`}];let e=n.conditions[0];if(e.selectedValues&&e.selectedValues.size>0)return[{operator:`contains`,value:``,nextOperator:`and`}];let t=n.combination??`and`;return n.conditions.map(e=>{let n=e;return{operator:n.operator,value:n.value??``,nextOperator:n.nextOperator??t}})},[n])),S=r(()=>{if(!m)return c;let e=m.toLowerCase();return c.filter(t=>t.toLowerCase().includes(e))},[c,m]),D=r(()=>e.some(e=>e==null||e===``),[e]),O=r(()=>S.every(e=>g.has(e))&&(!D||v),[S,g,D,v]),k=t(()=>{_(new Set(S)),D&&y(!0)},[S,D]),A=t(()=>{_(new Set),y(!1)},[]),j=t(e=>{_(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),M=t((e,t)=>{x(n=>{let r=[...n];return r[e]={...r[e],...t},r})},[]),ee=t(()=>{x(e=>[...e,{operator:`contains`,value:``,nextOperator:`and`}])},[]),N=t(e=>{x(t=>t.filter((t,n)=>n!==e))},[]),P=t(()=>{if(u===`values`){if(c.every(e=>g.has(e))&&(!D||v)){i(null);return}i({conditions:[{type:`text`,operator:`equals`,selectedValues:g,includeBlank:v}],combination:`and`})}else{let e=b.filter(e=>e.operator===`blank`||e.operator===`notBlank`?!0:e.value.trim()!==``);if(e.length===0){i(null);return}i({conditions:e.map(e=>({type:`text`,operator:e.operator,value:e.value,nextOperator:e.nextOperator})),combination:`and`})}},[u,c,g,v,D,b,i]),F=t(()=>{i(null)},[i]);return T(`div`,{className:`gp-grid-filter-content gp-grid-filter-text`,children:[!l&&T(`div`,{className:`gp-grid-filter-mode-toggle`,children:[w(`button`,{type:`button`,className:u===`values`?`active`:``,onClick:()=>d(`values`),children:`Values`}),w(`button`,{type:`button`,className:u===`condition`?`active`:``,onClick:()=>d(`condition`),children:`Condition`})]}),l&&u===`condition`&&T(`div`,{className:`gp-grid-filter-info`,children:[`Too many unique values (`,c.length,`). Use conditions to filter.`]}),u===`values`&&T(C,{children:[w(`input`,{className:`gp-grid-filter-search`,type:`text`,placeholder:`Search...`,value:m,onChange:e=>h(e.target.value),autoFocus:!0}),T(`div`,{className:`gp-grid-filter-actions`,children:[w(`button`,{type:`button`,onClick:k,disabled:O,children:`Select All`}),w(`button`,{type:`button`,onClick:A,children:`Deselect All`})]}),T(`div`,{className:`gp-grid-filter-list`,children:[D&&T(`label`,{className:`gp-grid-filter-option`,children:[w(`input`,{type:`checkbox`,checked:v,onChange:()=>y(!v)}),w(`span`,{className:`gp-grid-filter-blank`,children:`(Blanks)`})]}),S.map(e=>T(`label`,{className:`gp-grid-filter-option`,children:[w(`input`,{type:`checkbox`,checked:g.has(e),onChange:()=>j(e)}),w(`span`,{children:e})]},e))]})]}),u===`condition`&&T(C,{children:[b.map((e,t)=>T(`div`,{className:`gp-grid-filter-condition`,children:[t>0&&T(`div`,{className:`gp-grid-filter-combination`,children:[w(`button`,{type:`button`,className:b[t-1]?.nextOperator===`and`?`active`:``,onClick:()=>M(t-1,{nextOperator:`and`}),children:`AND`}),w(`button`,{type:`button`,className:b[t-1]?.nextOperator===`or`?`active`:``,onClick:()=>M(t-1,{nextOperator:`or`}),children:`OR`})]}),T(`div`,{className:`gp-grid-filter-row`,children:[w(`select`,{value:e.operator,onChange:e=>M(t,{operator:e.target.value}),autoFocus:t===0,children:E.map(e=>w(`option`,{value:e.value,children:e.label},e.value))}),e.operator!==`blank`&&e.operator!==`notBlank`&&w(`input`,{type:`text`,value:e.value,onChange:e=>M(t,{value:e.target.value}),placeholder:`Value`,className:`gp-grid-filter-text-input`}),b.length>1&&w(`button`,{type:`button`,className:`gp-grid-filter-remove`,onClick:()=>N(t),children:`×`})]})]},t)),w(`button`,{type:`button`,className:`gp-grid-filter-add`,onClick:ee,children:`+ Add condition`})]}),T(`div`,{className:`gp-grid-filter-buttons`,children:[w(`button`,{type:`button`,className:`gp-grid-filter-btn-clear`,onClick:F,children:`Clear`}),w(`button`,{type:`button`,className:`gp-grid-filter-btn-apply`,onClick:P,children:`Apply`})]})]})}const O=[{value:`=`,label:`=`},{value:`!=`,label:`≠`},{value:`>`,label:`>`},{value:`<`,label:`<`},{value:`>=`,label:`≥`},{value:`<=`,label:`≤`},{value:`between`,label:`↔`},{value:`blank`,label:`Is blank`},{value:`notBlank`,label:`Not blank`}];function k({currentFilter:e,onApply:n,onClose:i}){let[a,s]=o(r(()=>{if(!e?.conditions.length)return[{operator:`=`,value:``,valueTo:``,nextOperator:`and`}];let t=e.combination??`and`;return e.conditions.map(e=>{let n=e;return{operator:n.operator,value:n.value==null?``:String(n.value),valueTo:n.valueTo==null?``:String(n.valueTo),nextOperator:n.nextOperator??t}})},[e])),c=t((e,t)=>{s(n=>{let r=[...n];return r[e]={...r[e],...t},r})},[]),l=t(()=>{s(e=>[...e,{operator:`=`,value:``,valueTo:``,nextOperator:`and`}])},[]),u=t(e=>{s(t=>t.filter((t,n)=>n!==e))},[]),d=t(()=>{let e=a.filter(e=>e.operator===`blank`||e.operator===`notBlank`?!0:e.operator===`between`?e.value!==``&&e.valueTo!==``:e.value!==``);if(e.length===0){n(null);return}n({conditions:e.map(e=>({type:`number`,operator:e.operator,value:e.value?parseFloat(e.value):void 0,valueTo:e.valueTo?parseFloat(e.valueTo):void 0,nextOperator:e.nextOperator})),combination:`and`})},[a,n]),f=t(()=>{n(null)},[n]);return T(`div`,{className:`gp-grid-filter-content gp-grid-filter-number`,children:[a.map((e,t)=>T(`div`,{className:`gp-grid-filter-condition`,children:[t>0&&T(`div`,{className:`gp-grid-filter-combination`,children:[w(`button`,{type:`button`,className:a[t-1]?.nextOperator===`and`?`active`:``,onClick:()=>c(t-1,{nextOperator:`and`}),children:`AND`}),w(`button`,{type:`button`,className:a[t-1]?.nextOperator===`or`?`active`:``,onClick:()=>c(t-1,{nextOperator:`or`}),children:`OR`})]}),T(`div`,{className:`gp-grid-filter-row`,children:[w(`select`,{value:e.operator,onChange:e=>c(t,{operator:e.target.value}),children:O.map(e=>w(`option`,{value:e.value,children:e.label},e.value))}),e.operator!==`blank`&&e.operator!==`notBlank`&&w(`input`,{type:`number`,value:e.value,onChange:e=>c(t,{value:e.target.value}),placeholder:`Value`}),e.operator===`between`&&T(C,{children:[w(`span`,{className:`gp-grid-filter-to`,children:`to`}),w(`input`,{type:`number`,value:e.valueTo,onChange:e=>c(t,{valueTo:e.target.value}),placeholder:`Value`})]}),a.length>1&&w(`button`,{type:`button`,className:`gp-grid-filter-remove`,onClick:()=>u(t),children:`×`})]})]},t)),w(`button`,{type:`button`,className:`gp-grid-filter-add`,onClick:l,children:`+ Add condition`}),T(`div`,{className:`gp-grid-filter-buttons`,children:[w(`button`,{type:`button`,className:`gp-grid-filter-btn-clear`,onClick:f,children:`Clear`}),w(`button`,{type:`button`,className:`gp-grid-filter-btn-apply`,onClick:d,children:`Apply`})]})]})}const A=[{value:`=`,label:`=`},{value:`!=`,label:`≠`},{value:`>`,label:`>`},{value:`<`,label:`<`},{value:`between`,label:`↔`},{value:`blank`,label:`Is blank`},{value:`notBlank`,label:`Not blank`}];function j(e){if(!e)return``;let t=typeof e==`string`?new Date(e):e;return isNaN(t.getTime())?``:t.toISOString().split(`T`)[0]}function M({currentFilter:e,onApply:n,onClose:i}){let[a,s]=o(r(()=>{if(!e?.conditions.length)return[{operator:`=`,value:``,valueTo:``,nextOperator:`and`}];let t=e.combination??`and`;return e.conditions.map(e=>{let n=e;return{operator:n.operator,value:j(n.value),valueTo:j(n.valueTo),nextOperator:n.nextOperator??t}})},[e])),c=t((e,t)=>{s(n=>{let r=[...n];return r[e]={...r[e],...t},r})},[]),l=t(()=>{s(e=>[...e,{operator:`=`,value:``,valueTo:``,nextOperator:`and`}])},[]),u=t(e=>{s(t=>t.filter((t,n)=>n!==e))},[]),d=t(()=>{let e=a.filter(e=>e.operator===`blank`||e.operator===`notBlank`?!0:e.operator===`between`?e.value!==``&&e.valueTo!==``:e.value!==``);if(e.length===0){n(null);return}n({conditions:e.map(e=>({type:`date`,operator:e.operator,value:e.value||void 0,valueTo:e.valueTo||void 0,nextOperator:e.nextOperator})),combination:`and`})},[a,n]),f=t(()=>{n(null)},[n]);return T(`div`,{className:`gp-grid-filter-content gp-grid-filter-date`,children:[a.map((e,t)=>T(`div`,{className:`gp-grid-filter-condition`,children:[t>0&&T(`div`,{className:`gp-grid-filter-combination`,children:[w(`button`,{type:`button`,className:a[t-1]?.nextOperator===`and`?`active`:``,onClick:()=>c(t-1,{nextOperator:`and`}),children:`AND`}),w(`button`,{type:`button`,className:a[t-1]?.nextOperator===`or`?`active`:``,onClick:()=>c(t-1,{nextOperator:`or`}),children:`OR`})]}),T(`div`,{className:`gp-grid-filter-row`,children:[w(`select`,{value:e.operator,onChange:e=>c(t,{operator:e.target.value}),children:A.map(e=>w(`option`,{value:e.value,children:e.label},e.value))}),e.operator!==`blank`&&e.operator!==`notBlank`&&w(`input`,{type:`date`,value:e.value,onChange:e=>c(t,{value:e.target.value})}),e.operator===`between`&&T(C,{children:[w(`span`,{className:`gp-grid-filter-to`,children:`to`}),w(`input`,{type:`date`,value:e.valueTo,onChange:e=>c(t,{valueTo:e.target.value})})]}),a.length>1&&w(`button`,{type:`button`,className:`gp-grid-filter-remove`,onClick:()=>u(t),children:`×`})]})]},t)),w(`button`,{type:`button`,className:`gp-grid-filter-add`,onClick:l,children:`+ Add condition`}),T(`div`,{className:`gp-grid-filter-buttons`,children:[w(`button`,{type:`button`,className:`gp-grid-filter-btn-clear`,onClick:f,children:`Clear`}),w(`button`,{type:`button`,className:`gp-grid-filter-btn-apply`,onClick:d,children:`Apply`})]})]})}function ee({column:e,colIndex:r,anchorRect:i,distinctValues:o,currentFilter:s,onApply:c,onClose:l}){let u=a(null);n(()=>{let e=e=>{let t=e.target;t.closest(`.gp-grid-filter-icon`)||u.current&&!u.current.contains(t)&&l()},t=e=>{e.key===`Escape`&&l()};return requestAnimationFrame(()=>{document.addEventListener(`mousedown`,e),document.addEventListener(`keydown`,t)}),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`keydown`,t)}},[l]);let d=t(t=>{c(e.colId??e.field,t),l()},[e,c,l]),f={position:`fixed`,top:i.top+i.height+4,left:i.left,minWidth:Math.max(200,i.width),zIndex:1e4},p=e.cellDataType,m=p===`text`||p===`object`,h=p===`number`,g=p===`date`||p===`dateString`||p===`dateTime`||p===`dateTimeString`;return T(`div`,{ref:u,className:`gp-grid-filter-popup`,style:f,children:[T(`div`,{className:`gp-grid-filter-header`,children:[`Filter: `,e.headerName??e.field]}),m&&w(D,{distinctValues:o,currentFilter:s,onApply:d,onClose:l}),h&&w(k,{currentFilter:s,onApply:d,onClose:l}),g&&w(M,{currentFilter:s,onApply:d,onClose:l}),!m&&!h&&!g&&w(D,{distinctValues:o,currentFilter:s,onApply:d,onClose:l})]})}function N(e){return{slots:new Map,activeCell:null,selectionRange:null,editingCell:null,contentWidth:0,contentHeight:e?.initialHeight??0,viewportWidth:e?.initialWidth??0,headers:new Map,filterPopup:null,isLoading:!1,error:null,totalRows:0,visibleRowRange:null,hoverPosition:null}}function P(e,t,n){switch(e.type){case`CREATE_SLOT`:return t.set(e.slotId,{slotId:e.slotId,rowIndex:-1,rowData:{},translateY:0}),null;case`DESTROY_SLOT`:return t.delete(e.slotId),null;case`ASSIGN_SLOT`:{let n=t.get(e.slotId);return n&&t.set(e.slotId,{...n,rowIndex:e.rowIndex,rowData:e.rowData}),null}case`MOVE_SLOT`:{let n=t.get(e.slotId);return n&&t.set(e.slotId,{...n,translateY:e.translateY}),null}case`SET_ACTIVE_CELL`:return{activeCell:e.position};case`SET_SELECTION_RANGE`:return{selectionRange:e.range};case`UPDATE_VISIBLE_RANGE`:return{visibleRowRange:{start:e.start,end:e.end}};case`SET_HOVER_POSITION`:return{hoverPosition:e.position};case`START_EDIT`:return{editingCell:{row:e.row,col:e.col,initialValue:e.initialValue}};case`STOP_EDIT`:return{editingCell:null};case`SET_CONTENT_SIZE`:return{contentWidth:e.width,contentHeight:e.height,viewportWidth:e.viewportWidth};case`UPDATE_HEADER`:return n.set(e.colIndex,{column:e.column,sortDirection:e.sortDirection,sortIndex:e.sortIndex,sortable:e.sortable,filterable:e.filterable,hasFilter:e.hasFilter}),null;case`OPEN_FILTER_POPUP`:return{filterPopup:{isOpen:!0,colIndex:e.colIndex,column:e.column,anchorRect:e.anchorRect,distinctValues:e.distinctValues,currentFilter:e.currentFilter}};case`CLOSE_FILTER_POPUP`:return{filterPopup:null};case`DATA_LOADING`:return{isLoading:!0,error:null};case`DATA_LOADED`:return{isLoading:!1,totalRows:e.totalRows};case`DATA_ERROR`:return{isLoading:!1,error:e.error};case`ROWS_ADDED`:case`ROWS_REMOVED`:return{totalRows:e.totalRows};case`ROWS_UPDATED`:case`TRANSACTION_PROCESSED`:return null;default:return null}}function F(e,t){if(t.type===`RESET`)return N();let{instructions:n}=t;if(n.length===0)return e;let r=new Map(e.slots),i=new Map(e.headers),a={};for(let e of n){let t=P(e,r,i);t&&(a={...a,...t})}return{...e,...a,slots:r,headers:i}}function I(e,t){for(let n of e.values())if(n.rowIndex===t)return n;return null}function L(e,t,n,r,i,a){let o=I(a,n),s=(o?o.translateY:i+n*r)-t.scrollTop,c=s+r,l=i,u=t.clientHeight;if(s<l)t.scrollTop=e.getScrollTopForRow(n);else if(c>u){let a=t.clientHeight-i,o=Math.floor(a/r),s=Math.max(0,n-o+1);t.scrollTop=e.getScrollTopForRow(s)}}function te(e,r,i,s){let{activeCell:c,selectionRange:l,editingCell:u,filterPopupOpen:d,rowHeight:f,headerHeight:p,columnPositions:m,visibleColumnsWithIndices:h,slots:g}=s,_=a(null),[v,y]=o({isDragging:!1,dragType:null,fillSourceRange:null,fillTarget:null});n(()=>{let t=e.current;t?.input&&t.input.updateDeps({getHeaderHeight:()=>p,getRowHeight:()=>f,getColumnPositions:()=>m,getColumnCount:()=>h.length,getOriginalColumnIndex:e=>{let t=h[e];return t?t.originalIndex:e}})},[e,p,f,m,h]);let b=t((e,t)=>{_.current&&clearInterval(_.current),_.current=setInterval(()=>{let n=r.current;n&&(n.scrollTop+=t,n.scrollLeft+=e)},16)},[r]),x=t(()=>{_.current&&=(clearInterval(_.current),null)},[]),S=t(()=>{let e=r.current;if(!e)return null;let t=e.getBoundingClientRect();return{top:t.top,left:t.left,width:t.width,height:t.height,scrollTop:e.scrollTop,scrollLeft:e.scrollLeft}},[r]),C=e=>({clientX:e.clientX,clientY:e.clientY,button:e.button,shiftKey:e.shiftKey,ctrlKey:e.ctrlKey,metaKey:e.metaKey}),w=t(()=>{let t=t=>{let n=e.current,r=S();if(!n?.input||!r)return;let i=n.input.handleDragMove(C(t),r);i&&(i.autoScroll?b(i.autoScroll.dx,i.autoScroll.dy):x(),y(n.input.getDragState()))},n=()=>{let r=e.current;r?.input&&(r.input.handleDragEnd(),y(r.input.getDragState())),x(),document.removeEventListener(`mousemove`,t),document.removeEventListener(`mouseup`,n)};document.addEventListener(`mousemove`,t),document.addEventListener(`mouseup`,n)},[e,S,b,x]),T=t((t,n,i)=>{let a=e.current;if(!a?.input)return;let o=a.input.handleCellMouseDown(t,n,C(i));o.focusContainer&&r.current?.focus(),o.startDrag===`selection`&&(a.input.startSelectionDrag(),y(a.input.getDragState()),w())},[e,r,w]),E=t((t,n)=>{let r=e.current;r?.input&&r.input.handleCellDoubleClick(t,n)},[e]),D=t(t=>{let n=e.current;if(!n?.input)return;let r=n.input.handleFillHandleMouseDown(c,l,C(t));r.preventDefault&&t.preventDefault(),r.stopPropagation&&t.stopPropagation(),r.startDrag===`fill`&&(y(n.input.getDragState()),w())},[e,c,l,w]),O=t((t,n)=>{let r=e.current;if(!r?.input)return;let a=i[t];if(!a)return;let o=a.colId??a.field;r.input.handleHeaderClick(o,n.shiftKey)},[e,i]),k=t(t=>{let n=e.current,i=r.current;if(!n?.input)return;let a=n.input.handleKeyDown({key:t.key,shiftKey:t.shiftKey,ctrlKey:t.ctrlKey,metaKey:t.metaKey},c,u,d);a.preventDefault&&t.preventDefault(),a.scrollToCell&&i&&L(n,i,a.scrollToCell.row,f,p,g)},[e,r,c,u,d,f,p,g]),A=t((t,n)=>{let i=e.current,a=r.current;if(!i?.input||!a)return;let o=i.input.handleWheel(t.deltaY,t.deltaX,n);o&&(t.preventDefault(),a.scrollTop+=o.dy,a.scrollLeft+=o.dx)},[e,r]);return n(()=>()=>{x()},[x]),{handleCellMouseDown:T,handleCellDoubleClick:E,handleFillHandleMouseDown:D,handleHeaderClick:O,handleKeyDown:k,handleWheel:A,dragState:v}}function R(e,t){let n=t.split(`.`),r=e;for(let e of n){if(typeof r!=`object`||!r)return null;r=r[e]}return r??null}function ne(e){let{column:t,rowData:n,rowIndex:r,colIndex:i,isActive:a,isSelected:o,isEditing:s,cellRenderers:c,globalCellRenderer:l}=e,u=R(n,t.field),d={value:u,rowData:n,column:t,rowIndex:r,colIndex:i,isActive:a,isSelected:o,isEditing:s};if(t.cellRenderer&&typeof t.cellRenderer==`string`){let e=c[t.cellRenderer];if(e)return e(d)}return l?l(d):u==null?``:String(u)}function re(e){let{column:t,rowData:n,rowIndex:r,colIndex:i,initialValue:a,coreRef:o,editRenderers:s,globalEditRenderer:c}=e,l=o.current;if(!l)return null;let u={value:R(n,t.field),rowData:n,column:t,rowIndex:r,colIndex:i,isActive:!0,isSelected:!0,isEditing:!0,initialValue:a,onValueChange:e=>l.updateEditValue(e),onCommit:()=>l.commitEdit(),onCancel:()=>l.cancelEdit()};if(t.editRenderer&&typeof t.editRenderer==`string`){let e=s[t.editRenderer];if(e)return e(u)}return c?c(u):w(`input`,{className:`gp-grid-edit-input`,type:`text`,defaultValue:a==null?``:String(a),autoFocus:!0,onFocus:e=>e.target.select(),onChange:e=>l.updateEditValue(e.target.value),onKeyDown:e=>{e.stopPropagation(),e.key===`Enter`?l.commitEdit():e.key===`Escape`?l.cancelEdit():e.key===`Tab`&&(e.preventDefault(),l.commitEdit(),l.selection.moveFocus(e.shiftKey?`left`:`right`,!1))},onBlur:()=>l.commitEdit()})}function ie(e){let{column:t,colIndex:n,sortDirection:r,sortIndex:i,sortable:a,filterable:o,hasFilter:s,coreRef:c,containerRef:l,headerRenderers:u,globalHeaderRenderer:d}=e,f=c.current,p={column:t,colIndex:n,sortDirection:r,sortIndex:i,sortable:a,filterable:o,hasFilter:s,onSort:(e,n)=>{f&&a&&f.setSort(t.colId??t.field,e,n)},onFilterClick:()=>{if(f&&o){let e=l.current?.querySelector(`[data-col-index="${n}"]`);if(e){let t=e.getBoundingClientRect();f.openFilterPopup(n,{top:t.top,left:t.left,width:t.width,height:t.height})}}}};if(t.headerRenderer&&typeof t.headerRenderer==`string`){let e=u[t.headerRenderer];if(e)return e(p)}return d?d(p):T(C,{children:[w(`span`,{className:`gp-grid-header-text`,children:t.headerName??t.field}),T(`span`,{className:`gp-grid-header-icons`,children:[a&&T(`span`,{className:`gp-grid-sort-arrows`,children:[T(`span`,{className:`gp-grid-sort-arrows-stack`,children:[w(`svg`,{className:`gp-grid-sort-arrow-up${r===`asc`?` active`:``}`,width:`8`,height:`6`,viewBox:`0 0 8 6`,children:w(`path`,{d:`M4 0L8 6H0L4 0Z`,fill:`currentColor`})}),w(`svg`,{className:`gp-grid-sort-arrow-down${r===`desc`?` active`:``}`,width:`8`,height:`6`,viewBox:`0 0 8 6`,children:w(`path`,{d:`M4 6L0 0H8L4 6Z`,fill:`currentColor`})})]}),i!==void 0&&i>0&&w(`span`,{className:`gp-grid-sort-index`,children:i})]}),o&&w(`span`,{className:`gp-grid-filter-icon${s?` active`:``}`,onMouseDown:e=>{e.stopPropagation(),e.preventDefault(),p.onFilterClick()},onClick:e=>{e.stopPropagation()},children:w(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`currentColor`,children:w(`path`,{d:`M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z`})})})]})]})}function z(e){v();let{columns:o,dataSource:s,rowData:d,rowHeight:p,headerHeight:h=p,overscan:g=3,sortingEnabled:C=!0,darkMode:E=!1,wheelDampening:D=.1,cellRenderers:O={},editRenderers:k={},headerRenderers:A={},cellRenderer:j,editRenderer:M,headerRenderer:P,initialWidth:I,initialHeight:L,gridRef:R,highlighting:z}=e,B=a(null),V=a(null),ae=a(null),[H,oe]=i(F,{initialWidth:I,initialHeight:L},N),U=h,W=a(null),G=W.current;(!G||G.providedDataSource!==s||G.rowData!==d)&&(G?.ownsDataSource&&G.dataSource.destroy?.(),s?W.current={dataSource:s,ownsDataSource:!1,providedDataSource:s,rowData:d}:d?W.current={dataSource:m(d),ownsDataSource:!0,providedDataSource:s,rowData:d}:W.current={dataSource:f([]),ownsDataSource:!0,providedDataSource:s,rowData:d});let{dataSource:K,ownsDataSource:se}=W.current;n(()=>()=>{W.current?.ownsDataSource&&(W.current.dataSource.destroy?.(),W.current=null)},[]),n(()=>{let e=ae.current;e&&e!==K&&oe({type:`RESET`}),ae.current=K},[K]);let q=r(()=>o.map((e,t)=>({column:e,originalIndex:t})).filter(({column:e})=>!e.hidden),[o]),{positions:J,widths:Y}=r(()=>u(q.map(e=>e.column),H.viewportWidth),[q,H.viewportWidth]),X=_(J),{handleCellMouseDown:ce,handleCellDoubleClick:le,handleFillHandleMouseDown:ue,handleHeaderClick:de,handleKeyDown:fe,handleWheel:pe,dragState:Z}=te(V,B,o,{activeCell:H.activeCell,selectionRange:H.selectionRange,editingCell:H.editingCell,filterPopupOpen:H.filterPopup?.isOpen??!1,rowHeight:p,headerHeight:U,columnPositions:J,visibleColumnsWithIndices:q,slots:H.slots});n(()=>{let e=new c({columns:o,dataSource:K,rowHeight:p,headerHeight:U,overscan:g,sortingEnabled:C,highlighting:z});V.current=e,R&&(R.current={core:e});let t=e.onBatchInstruction(e=>{oe({type:`BATCH_INSTRUCTIONS`,instructions:e})});return e.initialize(),()=>{t(),e.destroy(),V.current=null,R&&(R.current=null)}},[o,K,p,U,g,C,R,z]),n(()=>{let e=K;if(e.subscribe)return e.subscribe(()=>{V.current?.refresh()})},[K]);let Q=t(()=>{let e=B.current,t=V.current;!e||!t||t.setViewport(e.scrollTop,e.scrollLeft,e.clientWidth,e.clientHeight)},[]);n(()=>{let e=B.current,t=V.current;if(!e||!t)return;if(typeof ResizeObserver>`u`){Q();return}let n=new ResizeObserver(()=>{t.setViewport(e.scrollTop,e.scrollLeft,e.clientWidth,e.clientHeight)});return n.observe(e),Q(),()=>n.disconnect()},[Q]),n(()=>{let e=B.current;if(!e)return;let t=e=>{pe(e,D)};return e.addEventListener(`wheel`,t,{passive:!1}),()=>e.removeEventListener(`wheel`,t)},[pe,D]);let me=t((e,t)=>{let n=V.current;n&&n.setFilter(e,t)},[]),he=t(()=>{let e=V.current;e&&e.closeFilterPopup()},[]),ge=t((e,t)=>{V.current?.input.handleCellMouseEnter(e,t)},[]),_e=t(()=>{V.current?.input.handleCellMouseLeave()},[]),ve=r(()=>Array.from(H.slots.values()),[H.slots]),$=r(()=>{let{activeCell:e,selectionRange:t,slots:n}=H;if(!e&&!t)return null;let r,i,a,s;if(t)r=Math.max(t.startRow,t.endRow),i=Math.max(t.startCol,t.endCol),a=Math.min(t.startCol,t.endCol),s=Math.max(t.startCol,t.endCol);else if(e)r=e.row,i=e.col,a=i,s=i;else return null;for(let e=a;e<=s;e++){let t=o[e];if(!(!t||t.hidden)&&t.editable!==!0)return null}let c=q.findIndex(e=>e.originalIndex===i);if(c===-1)return null;let l=null;for(let e of n.values())if(e.rowIndex===r){l=e.translateY;break}if(l===null)return null;let u=J[c]??0,d=Y[c]??0;return{top:l+p-5,left:u+d-20}},[H.activeCell,H.selectionRange,H.slots,p,J,Y,o,q]);return T(`div`,{ref:B,className:`gp-grid-container${E?` gp-grid-container--dark`:``}`,style:{width:`100%`,height:`100%`,overflow:`auto`,position:`relative`},onScroll:Q,onKeyDown:fe,tabIndex:0,children:[T(`div`,{style:{width:Math.max(H.contentWidth,X),height:Math.max(H.contentHeight,U),position:`relative`,minWidth:`100%`},children:[w(`div`,{className:`gp-grid-header`,style:{position:`sticky`,top:0,left:0,height:h,width:Math.max(H.contentWidth,X),minWidth:`100%`},children:q.map(({column:e,originalIndex:t},n)=>{let r=H.headers.get(t);return w(`div`,{className:`gp-grid-header-cell`,"data-col-index":t,style:{position:`absolute`,left:`${J[n]}px`,top:0,width:`${Y[n]}px`,height:`${h}px`,background:`transparent`},onClick:e=>de(t,e),children:ie({column:e,colIndex:t,sortDirection:r?.sortDirection,sortIndex:r?.sortIndex,sortable:r?.sortable??!0,filterable:r?.filterable??!0,hasFilter:r?.hasFilter??!1,coreRef:V,containerRef:B,headerRenderers:A,globalHeaderRenderer:P})},e.colId??e.field)})}),ve.map(e=>e.rowIndex<0?null:w(`div`,{className:[`gp-grid-row`,...V.current?.highlight?.computeRowClasses(e.rowIndex,e.rowData)??[]].filter(Boolean).join(` `),style:{position:`absolute`,top:0,left:0,transform:`translateY(${e.translateY}px)`,width:`${Math.max(H.contentWidth,X)}px`,height:`${p}px`},children:q.map(({column:t,originalIndex:n},r)=>{let i=b(e.rowIndex,n,H.editingCell),a=y(e.rowIndex,n,H.activeCell),o=S(e.rowIndex,n,H.selectionRange);return w(`div`,{className:[l(a,o,i,x(e.rowIndex,n,Z.dragType===`fill`,Z.fillSourceRange,Z.fillTarget)),...V.current?.highlight?.computeCombinedCellClasses(e.rowIndex,n,t,e.rowData)??[]].filter(Boolean).join(` `),style:{position:`absolute`,left:`${J[r]}px`,top:0,width:`${Y[r]}px`,height:`${p}px`},onMouseDown:t=>ce(e.rowIndex,n,t),onDoubleClick:()=>le(e.rowIndex,n),onMouseEnter:()=>ge(e.rowIndex,n),onMouseLeave:_e,children:i&&H.editingCell?re({column:t,rowData:e.rowData,rowIndex:e.rowIndex,colIndex:n,initialValue:H.editingCell.initialValue,coreRef:V,editRenderers:k,globalEditRenderer:M}):ne({column:t,rowData:e.rowData,rowIndex:e.rowIndex,colIndex:n,isActive:a,isSelected:o,isEditing:i,cellRenderers:O,globalCellRenderer:j})},`${e.slotId}-${n}`)})},e.slotId)),$&&!H.editingCell&&w(`div`,{className:`gp-grid-fill-handle`,style:{position:`absolute`,top:$.top,left:$.left,zIndex:200},onMouseDown:ue}),H.isLoading&&T(`div`,{className:`gp-grid-loading`,children:[w(`div`,{className:`gp-grid-loading-spinner`}),`Loading...`]}),H.error&&T(`div`,{className:`gp-grid-error`,children:[`Error: `,H.error]}),!H.isLoading&&!H.error&&H.totalRows===0&&w(`div`,{className:`gp-grid-empty`,children:`No data to display`})]}),H.filterPopup?.isOpen&&H.filterPopup.column&&H.filterPopup.anchorRect&&w(ee,{column:H.filterPopup.column,colIndex:H.filterPopup.colIndex,anchorRect:H.filterPopup.anchorRect,distinctValues:H.filterPopup.distinctValues,currentFilter:H.filterPopup.currentFilter,onApply:me,onClose:he})]})}export{z as Grid,s as GridCore,d as createClientDataSource,p as createDataSourceFromArray,h as createMutableClientDataSource,g as createServerDataSource};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gp-grid-react",
3
3
  "description": "A high-performance React data grid component with virtual scrolling, cell selection, sorting, filtering, and Excel-like editing",
4
- "version": "0.6.0",
4
+ "version": "0.7.1",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -45,7 +45,7 @@
45
45
  "link-workspace-packages": false
46
46
  },
47
47
  "dependencies": {
48
- "gp-grid-core": "0.6.0"
48
+ "gp-grid-core": "0.7.1"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "react": "^19.0.0",