@xcelsior/ui-spreadsheets 1.0.18 → 1.1.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.
@@ -15,11 +15,12 @@ import { AddCommentModal, ViewCommentsModal } from './CommentModals';
15
15
  import { KeyboardShortcutsModal } from './KeyboardShortcutsModal';
16
16
  import { Pagination } from '@xcelsior/design-system';
17
17
  import { useSpreadsheetFiltering } from '../hooks/useSpreadsheetFiltering';
18
+ import { useSpreadsheetHighlighting } from '../hooks/useSpreadsheetHighlighting';
18
19
  import {
19
- useSpreadsheetHighlighting,
20
+ useSpreadsheetPinning,
21
+ ROW_INDEX_COLUMN_WIDTH,
20
22
  ROW_INDEX_COLUMN_ID,
21
- } from '../hooks/useSpreadsheetHighlighting';
22
- import { useSpreadsheetPinning, ROW_INDEX_COLUMN_WIDTH } from '../hooks/useSpreadsheetPinning';
23
+ } from '../hooks/useSpreadsheetPinning';
23
24
  import { useSpreadsheetComments } from '../hooks/useSpreadsheetComments';
24
25
  import { useSpreadsheetUndoRedo } from '../hooks/useSpreadsheetUndoRedo';
25
26
  import { useSpreadsheetKeyboardShortcuts } from '../hooks/useSpreadsheetKeyboardShortcuts';
@@ -89,18 +90,15 @@ export function Spreadsheet<T extends Record<string, any>>({
89
90
  onRowHighlight,
90
91
  showToolbar = true,
91
92
  showPagination = true,
92
- showRowIndex = true,
93
93
  enableRowSelection = true,
94
94
  enableCellEditing = true,
95
95
  enableComments = true,
96
96
  enableHighlighting = true,
97
97
  enableUndoRedo = true,
98
- defaultPageSize = 25,
99
98
  pageSizeOptions = [25, 50, 100, 200],
100
- defaultZoom = 100,
101
- autoSave,
102
99
  onSave,
103
- compactMode,
100
+ settings: initialSettings,
101
+ onSettingsChange,
104
102
  isLoading = false,
105
103
  className,
106
104
  emptyMessage = 'No data available',
@@ -113,12 +111,22 @@ export function Spreadsheet<T extends Record<string, any>>({
113
111
  totalItems,
114
112
  currentPage: controlledCurrentPage,
115
113
  pageSize: controlledPageSize,
116
- onPageChange,
117
114
  sortConfig: controlledSortConfig,
115
+ onPageChange,
118
116
  filters: controlledFilters,
119
117
  }: SpreadsheetProps<T>) {
120
118
  // ==================== HOOKS ====================
121
119
 
120
+ // Settings state - declare early so it can be used in hooks
121
+ const [spreadsheetSettings, setSpreadsheetSettings] = useState<SpreadsheetSettings>({
122
+ defaultPinnedColumns: initialSettings?.defaultPinnedColumns ?? [],
123
+ defaultSort: initialSettings?.defaultSort ?? null,
124
+ defaultPageSize: initialSettings?.defaultPageSize ?? 25,
125
+ defaultZoom: initialSettings?.defaultZoom ?? 100,
126
+ autoSave: initialSettings?.autoSave ?? true,
127
+ compactView: initialSettings?.compactView ?? false,
128
+ });
129
+
122
130
  // Filtering and sorting hook
123
131
  const {
124
132
  filters,
@@ -138,7 +146,7 @@ export function Spreadsheet<T extends Record<string, any>>({
138
146
  onSortChange,
139
147
  serverSide,
140
148
  controlledFilters,
141
- controlledSortConfig,
149
+ controlledSortConfig: controlledSortConfig ?? spreadsheetSettings?.defaultSort,
142
150
  });
143
151
 
144
152
  // Highlighting hook
@@ -167,15 +175,14 @@ export function Spreadsheet<T extends Record<string, any>>({
167
175
  collapsedGroups,
168
176
  visibleColumns,
169
177
  handleTogglePin,
170
- handleToggleRowIndexPin,
171
178
  handleToggleGroupCollapse,
179
+ setPinnedColumnsFromIds,
172
180
  getColumnLeftOffset,
173
181
  isColumnPinned,
174
182
  getColumnPinSide,
175
183
  } = useSpreadsheetPinning({
176
184
  columns,
177
185
  columnGroups,
178
- showRowIndex,
179
186
  });
180
187
 
181
188
  // Comments hook
@@ -199,22 +206,6 @@ export function Spreadsheet<T extends Record<string, any>>({
199
206
  // Modal state
200
207
  const [showSettingsModal, setShowSettingsModal] = useState(false);
201
208
 
202
- // Settings state - declare early so it can be used in hooks
203
- const [spreadsheetSettings, setSpreadsheetSettings] = useState<SpreadsheetSettings>({
204
- defaultPinnedColumns: [],
205
- defaultSort: null,
206
- defaultPageSize,
207
- defaultZoom,
208
- autoSave: autoSave ?? true,
209
- compactView: compactMode ?? false,
210
- showRowIndex: showRowIndex,
211
- pinRowIndex: false,
212
- rowIndexHighlightColor: undefined,
213
- });
214
-
215
- // Effective values from settings
216
- const effectiveAutoSave = autoSave ?? spreadsheetSettings.autoSave;
217
-
218
209
  // Undo/Redo hook
219
210
  const {
220
211
  canUndo,
@@ -230,7 +221,7 @@ export function Spreadsheet<T extends Record<string, any>>({
230
221
  markAsChanged,
231
222
  } = useSpreadsheetUndoRedo<SpreadsheetUndoEntry>({
232
223
  enabled: enableUndoRedo,
233
- autoSave: effectiveAutoSave,
224
+ autoSave: spreadsheetSettings.autoSave,
234
225
  onSave,
235
226
  });
236
227
 
@@ -241,8 +232,8 @@ export function Spreadsheet<T extends Record<string, any>>({
241
232
 
242
233
  // Pagination state (supports both controlled and uncontrolled modes)
243
234
  const [internalCurrentPage, setInternalCurrentPage] = useState(1);
244
- const [internalPageSize, setInternalPageSize] = useState(defaultPageSize);
245
- const [zoom, setZoom] = useState(defaultZoom);
235
+ const [internalPageSize, setInternalPageSize] = useState(spreadsheetSettings.defaultPageSize);
236
+ const [zoom, setZoom] = useState(spreadsheetSettings.defaultZoom);
246
237
 
247
238
  // Use controlled state if provided, otherwise use internal state
248
239
  const currentPage = controlledCurrentPage ?? internalCurrentPage;
@@ -463,8 +454,7 @@ export function Spreadsheet<T extends Record<string, any>>({
463
454
 
464
455
  // ==================== COMPUTED VALUES ====================
465
456
 
466
- const effectiveShowRowIndex = spreadsheetSettings.showRowIndex !== false;
467
- const effectiveCompactMode = compactMode ?? spreadsheetSettings.compactView;
457
+ const effectiveCompactMode = spreadsheetSettings.compactView ?? false;
468
458
  const rowIndexHighlightColor = getColumnHighlight(ROW_INDEX_COLUMN_ID);
469
459
 
470
460
  // Refs
@@ -632,7 +622,7 @@ export function Spreadsheet<T extends Record<string, any>>({
632
622
  selectedRowCount={selectedRows.size}
633
623
  hasUnsavedChanges={hasUnsavedChanges}
634
624
  saveStatus={saveStatus}
635
- autoSave={effectiveAutoSave}
625
+ autoSave={spreadsheetSettings.autoSave}
636
626
  hasActiveFilters={hasActiveFilters}
637
627
  onClearFilters={clearAllFilters}
638
628
  onZoomIn={() => setZoom((z) => Math.min(z + 10, 200))}
@@ -667,17 +657,15 @@ export function Spreadsheet<T extends Record<string, any>>({
667
657
  {columnGroups && (
668
658
  <tr>
669
659
  {/* Row index column header (rowSpan=2 for groups) */}
670
- {effectiveShowRowIndex && (
671
- <RowIndexColumnHeader
672
- enableHighlighting={enableHighlighting}
673
- highlightColor={rowIndexHighlightColor}
674
- isPinned={isRowIndexPinned}
675
- onHighlightClick={handleRowIndexHighlightClick}
676
- onPinClick={handleToggleRowIndexPin}
677
- hasColumnGroups={true}
678
- compactMode={effectiveCompactMode}
679
- />
680
- )}
660
+ <RowIndexColumnHeader
661
+ enableHighlighting={enableHighlighting}
662
+ highlightColor={rowIndexHighlightColor}
663
+ isPinned={isRowIndexPinned}
664
+ onHighlightClick={handleRowIndexHighlightClick}
665
+ onPinClick={() => handleTogglePin(ROW_INDEX_COLUMN_ID)}
666
+ hasColumnGroups={true}
667
+ compactMode={effectiveCompactMode}
668
+ />
681
669
  {columnGroups.map((group) => {
682
670
  const groupColumns = (columns || []).filter((c) =>
683
671
  group.columns.includes(c.id)
@@ -727,13 +715,13 @@ export function Spreadsheet<T extends Record<string, any>>({
727
715
  {/* Column Headers */}
728
716
  <tr>
729
717
  {/* Row index column header (when no groups) */}
730
- {effectiveShowRowIndex && !columnGroups && (
718
+ {!columnGroups && (
731
719
  <RowIndexColumnHeader
732
720
  enableHighlighting={enableHighlighting}
733
721
  highlightColor={rowIndexHighlightColor}
734
722
  isPinned={isRowIndexPinned}
735
723
  onHighlightClick={handleRowIndexHighlightClick}
736
- onPinClick={handleToggleRowIndexPin}
724
+ onPinClick={() => handleTogglePin(ROW_INDEX_COLUMN_ID)}
737
725
  hasColumnGroups={false}
738
726
  compactMode={effectiveCompactMode}
739
727
  />
@@ -791,9 +779,7 @@ export function Spreadsheet<T extends Record<string, any>>({
791
779
  {isLoading ? (
792
780
  <tr>
793
781
  <td
794
- colSpan={
795
- visibleColumns.length + (effectiveShowRowIndex ? 1 : 0)
796
- }
782
+ colSpan={visibleColumns.length + 1}
797
783
  className="text-center py-8 text-gray-500"
798
784
  >
799
785
  <div className="flex items-center justify-center gap-2">
@@ -805,9 +791,7 @@ export function Spreadsheet<T extends Record<string, any>>({
805
791
  ) : paginatedData.length === 0 ? (
806
792
  <tr>
807
793
  <td
808
- colSpan={
809
- visibleColumns.length + (effectiveShowRowIndex ? 1 : 0)
810
- }
794
+ colSpan={visibleColumns.length + 1}
811
795
  className="text-center py-8 text-gray-500"
812
796
  >
813
797
  {emptyMessage}
@@ -831,187 +815,180 @@ export function Spreadsheet<T extends Record<string, any>>({
831
815
  onRowClick?.(row, rowIndex);
832
816
  }}
833
817
  onDoubleClick={() => onRowDoubleClick?.(row, rowIndex)}
834
- className="transition-colors"
835
818
  >
836
819
  {/* Row Index Column */}
837
- {effectiveShowRowIndex && (
838
- <td
839
- onClick={(e) => handleRowSelect(rowId, e)}
840
- className={cn(
841
- 'border border-gray-200 text-center font-semibold cursor-pointer group',
842
- effectiveCompactMode ? 'text-[10px] px-1 py-px' : 'text-xs px-2 py-1',
843
- isRowIndexPinned ? 'z-20' : 'z-0',
844
- isRowSelected && 'bg-blue-100',
845
- !isRowSelected && rowHighlight && '',
846
- isRowHovered &&
847
- !isRowSelected &&
848
- !rowHighlight &&
849
- 'bg-gray-50'
850
- )}
851
- style={{
852
- backgroundColor:
853
- rowHighlight?.color ||
854
- (isRowSelected
855
- ? '#dbeafe'
856
- : isRowHovered
857
- ? '#f9fafb'
858
- : rowIndexHighlightColor ||
859
- 'white'),
860
- minWidth: `${ROW_INDEX_COLUMN_WIDTH}px`,
861
- width: `${ROW_INDEX_COLUMN_WIDTH}px`,
862
- ...(isRowIndexPinned && {
863
- position: 'sticky' as const,
864
- left: 0,
865
- }),
866
- }}
867
- >
868
- <div className="relative flex items-center justify-center">
869
- {/* Row number - always centered */}
870
- <span>{displayIndex}</span>
871
-
872
- {/* Action buttons - absolute positioned to not affect centering */}
873
- <div className="absolute right-0 flex items-center gap-0.5">
874
- {/* Clone/Duplicate Row - hover only */}
875
- {onRowClone && (
820
+ <td
821
+ onClick={(e) => handleRowSelect(rowId, e)}
822
+ className={cn(
823
+ 'border border-gray-200 text-center font-semibold cursor-pointer group',
824
+ effectiveCompactMode
825
+ ? 'text-[10px] px-1 py-px'
826
+ : 'text-xs px-2 py-1',
827
+ isRowIndexPinned ? 'z-20' : 'z-0',
828
+ isRowSelected && 'bg-blue-100',
829
+ !isRowSelected && rowHighlight && '',
830
+ isRowHovered &&
831
+ !isRowSelected &&
832
+ !rowHighlight &&
833
+ 'bg-gray-50'
834
+ )}
835
+ style={{
836
+ backgroundColor:
837
+ rowHighlight?.color ||
838
+ (isRowSelected
839
+ ? '#dbeafe'
840
+ : isRowHovered
841
+ ? '#f9fafb'
842
+ : rowIndexHighlightColor || 'white'),
843
+ minWidth: `${ROW_INDEX_COLUMN_WIDTH}px`,
844
+ width: `${ROW_INDEX_COLUMN_WIDTH}px`,
845
+ ...(isRowIndexPinned && {
846
+ position: 'sticky' as const,
847
+ left: 0,
848
+ }),
849
+ }}
850
+ >
851
+ <div className="relative flex items-center justify-center">
852
+ {/* Row number - always centered */}
853
+ <span>{displayIndex}</span>
854
+
855
+ {/* Action buttons - absolute positioned to the right */}
856
+ <div className="absolute right-0 flex items-center gap-0.5">
857
+ {/* Clone/Duplicate Row */}
858
+ {onRowClone && (
859
+ <button
860
+ type="button"
861
+ onClick={(e) => {
862
+ e.stopPropagation();
863
+ handleRowClone(row, rowId);
864
+ }}
865
+ className="opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded"
866
+ title="Duplicate row"
867
+ >
868
+ <HiDuplicate className="h-2.5 w-2.5 text-gray-500" />
869
+ </button>
870
+ )}
871
+
872
+ {/* Delete Row */}
873
+ {onRowDelete && (
874
+ <button
875
+ type="button"
876
+ onClick={(e) => {
877
+ e.stopPropagation();
878
+ handleRowDelete(row, rowId);
879
+ }}
880
+ className="opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-red-100 rounded"
881
+ title="Delete row"
882
+ >
883
+ <HiTrash className="h-2.5 w-2.5 text-gray-500 hover:text-red-500" />
884
+ </button>
885
+ )}
886
+
887
+ {/* Highlight Row */}
888
+ {enableHighlighting && (
889
+ <button
890
+ type="button"
891
+ onClick={(e) => {
892
+ e.stopPropagation();
893
+ setHighlightPickerRow(rowId);
894
+ }}
895
+ className="opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded"
896
+ title="Highlight row"
897
+ >
898
+ <AiFillHighlight
899
+ className={cn(
900
+ 'h-2.5 w-2.5',
901
+ rowHighlight
902
+ ? 'text-amber-500'
903
+ : 'text-gray-500'
904
+ )}
905
+ />
906
+ </button>
907
+ )}
908
+
909
+ {/* Comment button */}
910
+ {enableComments &&
911
+ (cellHasComments(
912
+ rowId,
913
+ ROW_INDEX_COLUMN_ID
914
+ ) ? (
876
915
  <button
877
916
  type="button"
878
917
  onClick={(e) => {
879
918
  e.stopPropagation();
880
- handleRowClone(row, rowId);
919
+ setViewCommentsCell({
920
+ rowId,
921
+ columnId:
922
+ ROW_INDEX_COLUMN_ID,
923
+ });
881
924
  }}
882
- className="opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded"
883
- title="Duplicate row"
925
+ className="p-0.5 bg-amber-100 hover:bg-amber-200 rounded transition-colors flex items-center gap-0.5"
926
+ title={`${getCellUnresolvedCommentCount(rowId, ROW_INDEX_COLUMN_ID)} comment(s) - click to view`}
884
927
  >
885
- <HiDuplicate className="h-2.5 w-2.5 text-gray-500" />
928
+ <FaComment className="h-2.5 w-2.5 text-amber-500" />
929
+ {getCellUnresolvedCommentCount(
930
+ rowId,
931
+ ROW_INDEX_COLUMN_ID
932
+ ) > 0 && (
933
+ <span className="text-[9px] font-medium text-amber-600">
934
+ {getCellUnresolvedCommentCount(
935
+ rowId,
936
+ ROW_INDEX_COLUMN_ID
937
+ ) > 99
938
+ ? '99+'
939
+ : getCellUnresolvedCommentCount(
940
+ rowId,
941
+ ROW_INDEX_COLUMN_ID
942
+ )}
943
+ </span>
944
+ )}
886
945
  </button>
887
- )}
888
-
889
- {/* Delete Row - hover only */}
890
- {onRowDelete && (
946
+ ) : (
891
947
  <button
892
948
  type="button"
893
949
  onClick={(e) => {
894
950
  e.stopPropagation();
895
- handleRowDelete(row, rowId);
951
+ setCommentModalCell({
952
+ rowId,
953
+ columnId:
954
+ ROW_INDEX_COLUMN_ID,
955
+ });
896
956
  }}
897
- className="opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-red-100 rounded"
898
- title="Delete row"
957
+ className="opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded"
958
+ title="Add comment"
899
959
  >
900
- <HiTrash className="h-2.5 w-2.5 text-gray-500 hover:text-red-500" />
960
+ <FaRegComment className="h-2.5 w-2.5 text-gray-500" />
901
961
  </button>
902
- )}
903
-
904
- {/* Highlight Row - hover only */}
905
- {enableHighlighting && (
962
+ ))}
963
+
964
+ {/* Custom Row Actions */}
965
+ {rowActions?.map((action) => {
966
+ if (
967
+ action.visible &&
968
+ !action.visible(row)
969
+ )
970
+ return null;
971
+ return (
906
972
  <button
973
+ key={action.id}
907
974
  type="button"
908
975
  onClick={(e) => {
909
976
  e.stopPropagation();
910
- setHighlightPickerRow(
911
- rowId
912
- );
977
+ action.onClick(row, rowId);
913
978
  }}
914
- className="opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded"
915
- title="Highlight row"
979
+ className={cn(
980
+ 'opacity-0 group-hover:opacity-100 transition-opacity p-0.5 hover:bg-gray-200 rounded',
981
+ action.className
982
+ )}
983
+ title={action.tooltip}
916
984
  >
917
- <AiFillHighlight
918
- className={cn(
919
- 'h-2.5 w-2.5',
920
- rowHighlight
921
- ? 'text-amber-500'
922
- : 'text-gray-500'
923
- )}
924
- />
985
+ {action.icon}
925
986
  </button>
926
- )}
927
-
928
- {/* Comment button - always visible when has comments, hover only when adding */}
929
- {enableComments &&
930
- (cellHasComments(
931
- rowId,
932
- ROW_INDEX_COLUMN_ID
933
- ) ? (
934
- <button
935
- type="button"
936
- onClick={(e) => {
937
- e.stopPropagation();
938
- setViewCommentsCell({
939
- rowId,
940
- columnId:
941
- ROW_INDEX_COLUMN_ID,
942
- });
943
- }}
944
- className="p-0.5 bg-amber-100 hover:bg-amber-200 rounded transition-colors flex items-center gap-0.5"
945
- title={`${getCellUnresolvedCommentCount(rowId, ROW_INDEX_COLUMN_ID)} comment(s) - click to view`}
946
- >
947
- <FaComment className="h-2.5 w-2.5 text-amber-500" />
948
- {getCellUnresolvedCommentCount(
949
- rowId,
950
- ROW_INDEX_COLUMN_ID
951
- ) > 0 && (
952
- <span className="text-[9px] font-medium text-amber-600">
953
- {getCellUnresolvedCommentCount(
954
- rowId,
955
- ROW_INDEX_COLUMN_ID
956
- ) > 99
957
- ? '99+'
958
- : getCellUnresolvedCommentCount(
959
- rowId,
960
- ROW_INDEX_COLUMN_ID
961
- )}
962
- </span>
963
- )}
964
- </button>
965
- ) : (
966
- <button
967
- type="button"
968
- onClick={(e) => {
969
- e.stopPropagation();
970
- setCommentModalCell({
971
- rowId,
972
- columnId:
973
- ROW_INDEX_COLUMN_ID,
974
- });
975
- }}
976
- className="opacity-0 group-hover:opacity-100 transition-opacity p-0.5 bg-gray-100 hover:bg-gray-200 rounded"
977
- title="Add comment"
978
- >
979
- <FaRegComment className="h-2.5 w-2.5 text-gray-500" />
980
- </button>
981
- ))}
982
-
983
- {/* Custom Row Actions - hover only */}
984
- {rowActions?.map((action) => {
985
- if (
986
- action.visible &&
987
- !action.visible(row)
988
- )
989
- return null;
990
- return (
991
- <button
992
- key={action.id}
993
- type="button"
994
- onClick={(e) => {
995
- e.stopPropagation();
996
- action.onClick(
997
- row,
998
- rowId
999
- );
1000
- }}
1001
- className={cn(
1002
- 'opacity-0 group-hover:opacity-100 transition-opacity p-0.5 hover:bg-gray-200 rounded',
1003
- action.className
1004
- )}
1005
- title={action.tooltip}
1006
- >
1007
- {action.icon}
1008
- </button>
1009
- );
1010
- })}
1011
- </div>
987
+ );
988
+ })}
1012
989
  </div>
1013
- </td>
1014
- )}
990
+ </div>
991
+ </td>
1015
992
 
1016
993
  {/* Data Cells */}
1017
994
  {visibleColumns.map((column) => {
@@ -1248,6 +1225,10 @@ export function Spreadsheet<T extends Record<string, any>>({
1248
1225
  ) {
1249
1226
  setSortConfig(newSettings.defaultSort);
1250
1227
  }
1228
+ // Apply pinned columns changes from settings
1229
+ setPinnedColumnsFromIds(newSettings.defaultPinnedColumns);
1230
+ // Notify consumer of settings changes
1231
+ onSettingsChange?.(newSettings);
1251
1232
  }}
1252
1233
  columns={columns || []}
1253
1234
  title="Spreadsheet Settings"
@@ -136,8 +136,8 @@ const SpreadsheetCell: React.FC<SpreadsheetCellProps> = ({
136
136
  onKeyDown={handleKeyDown}
137
137
  onBlur={() => onConfirm?.(localValue)}
138
138
  className={cn(
139
- 'w-full border border-gray-300 rounded text-xs focus:outline-none focus:ring-1 focus:ring-blue-500',
140
- compactMode ? 'px-1 py-0.5' : 'px-2 py-1'
139
+ 'w-full border-0 bg-blue-50 focus:outline-none focus:ring-1 focus:ring-blue-500 rounded-sm',
140
+ compactMode ? 'text-[10px]' : 'text-xs'
141
141
  )}
142
142
  >
143
143
  {column.options.map((option) => (
@@ -171,8 +171,8 @@ const SpreadsheetCell: React.FC<SpreadsheetCellProps> = ({
171
171
  autoCapitalize="off"
172
172
  spellCheck={false}
173
173
  className={cn(
174
- 'w-full border border-gray-300 rounded text-xs focus:outline-none focus:ring-1 focus:ring-blue-500 bg-blue-50',
175
- compactMode ? 'px-1 py-0.5' : 'px-2 py-1'
174
+ 'w-full border-0 bg-blue-50 focus:outline-none focus:ring-1 focus:ring-blue-500 rounded-sm',
175
+ compactMode ? 'text-[10px]' : 'text-xs'
176
176
  )}
177
177
  />
178
178
  );
@@ -231,7 +231,7 @@ const SpreadsheetCell: React.FC<SpreadsheetCellProps> = ({
231
231
  onKeyDown={handleCellKeyDown}
232
232
  data-cell-id={`${rowId}-${column.id}`}
233
233
  className={cn(
234
- 'border border-gray-200 group cursor-pointer transition-colors select-none',
234
+ 'border border-gray-200 group cursor-pointer select-none',
235
235
  compactMode ? 'text-[10px]' : 'text-xs',
236
236
  cellPadding,
237
237
  column.align === 'right' && 'text-right',
@@ -257,8 +257,7 @@ const SpreadsheetCell: React.FC<SpreadsheetCellProps> = ({
257
257
  <div
258
258
  className={cn(
259
259
  'flex-1 truncate',
260
- isEditable &&
261
- 'cursor-text hover:bg-gray-50 px-0.5 rounded min-h-[18px] flex items-center bg-blue-50/50'
260
+ isEditable && 'cursor-text bg-blue-50/50 rounded'
262
261
  )}
263
262
  title={String(value ?? '')}
264
263
  >
@@ -351,6 +350,7 @@ export const MemoizedSpreadsheetCell = memo(SpreadsheetCell, (prevProps, nextPro
351
350
  if (prevProps.isPinned !== nextProps.isPinned) return false;
352
351
  if (prevProps.leftOffset !== nextProps.leftOffset) return false;
353
352
  if (prevProps.rightOffset !== nextProps.rightOffset) return false;
353
+ if (prevProps.compactMode !== nextProps.compactMode) return false;
354
354
 
355
355
  // Check selection edge changes
356
356
  const prevEdge = prevProps.selectionEdge;