@xcelsior/ui-spreadsheets 1.1.0 → 1.1.2
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.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +44 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +44 -34
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/Spreadsheet.tsx +32 -1
- package/src/components/SpreadsheetCell.tsx +1 -1
- package/src/hooks/useSpreadsheetPinning.ts +26 -45
package/package.json
CHANGED
|
@@ -111,6 +111,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
111
111
|
totalItems,
|
|
112
112
|
currentPage: controlledCurrentPage,
|
|
113
113
|
pageSize: controlledPageSize,
|
|
114
|
+
sortConfig: controlledSortConfig,
|
|
114
115
|
onPageChange,
|
|
115
116
|
filters: controlledFilters,
|
|
116
117
|
}: SpreadsheetProps<T>) {
|
|
@@ -145,7 +146,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
145
146
|
onSortChange,
|
|
146
147
|
serverSide,
|
|
147
148
|
controlledFilters,
|
|
148
|
-
controlledSortConfig: spreadsheetSettings?.defaultSort,
|
|
149
|
+
controlledSortConfig: controlledSortConfig ?? spreadsheetSettings?.defaultSort,
|
|
149
150
|
});
|
|
150
151
|
|
|
151
152
|
// Highlighting hook
|
|
@@ -182,6 +183,7 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
182
183
|
} = useSpreadsheetPinning({
|
|
183
184
|
columns,
|
|
184
185
|
columnGroups,
|
|
186
|
+
defaultPinnedColumns: initialSettings?.defaultPinnedColumns,
|
|
185
187
|
});
|
|
186
188
|
|
|
187
189
|
// Comments hook
|
|
@@ -270,6 +272,35 @@ export function Spreadsheet<T extends Record<string, any>>({
|
|
|
270
272
|
}));
|
|
271
273
|
}, [sortConfig]);
|
|
272
274
|
|
|
275
|
+
// Sync pinned columns to spreadsheetSettings when pinning changes
|
|
276
|
+
useEffect(() => {
|
|
277
|
+
const pinnedColumnIds = Array.from(pinnedColumns.keys());
|
|
278
|
+
setSpreadsheetSettings((prev) => {
|
|
279
|
+
// Only update if the arrays are different to avoid unnecessary re-renders
|
|
280
|
+
const prevIds = prev.defaultPinnedColumns;
|
|
281
|
+
if (
|
|
282
|
+
prevIds.length === pinnedColumnIds.length &&
|
|
283
|
+
prevIds.every((id, idx) => id === pinnedColumnIds[idx])
|
|
284
|
+
) {
|
|
285
|
+
return prev;
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
...prev,
|
|
289
|
+
defaultPinnedColumns: pinnedColumnIds,
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
}, [pinnedColumns]);
|
|
293
|
+
|
|
294
|
+
// Notify parent when settings change (skip initial render)
|
|
295
|
+
const isInitialMount = useRef(true);
|
|
296
|
+
useEffect(() => {
|
|
297
|
+
if (isInitialMount.current) {
|
|
298
|
+
isInitialMount.current = false;
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
onSettingsChange?.(spreadsheetSettings);
|
|
302
|
+
}, [spreadsheetSettings, onSettingsChange]);
|
|
303
|
+
|
|
273
304
|
const applyUndo = useCallback(() => {
|
|
274
305
|
const entry = popUndoEntry();
|
|
275
306
|
if (!entry || !onCellsEdit) return;
|
|
@@ -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 select-none',
|
|
234
|
+
'border border-gray-200 group cursor-pointer transition-colors select-none',
|
|
235
235
|
compactMode ? 'text-[10px]' : 'text-xs',
|
|
236
236
|
cellPadding,
|
|
237
237
|
column.align === 'right' && 'text-right',
|
|
@@ -10,7 +10,6 @@ export interface UseSpreadsheetPinningOptions<T> {
|
|
|
10
10
|
columnGroups?: SpreadsheetColumnGroup[];
|
|
11
11
|
showRowIndex?: boolean;
|
|
12
12
|
defaultPinnedColumns?: string[];
|
|
13
|
-
defaultPinRowIndex?: boolean;
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
export interface UseSpreadsheetPinningReturn<T> {
|
|
@@ -26,14 +25,11 @@ export interface UseSpreadsheetPinningReturn<T> {
|
|
|
26
25
|
|
|
27
26
|
// Actions
|
|
28
27
|
handleTogglePin: (columnId: string) => void;
|
|
29
|
-
handleToggleRowIndexPin: () => void;
|
|
30
28
|
handleToggleGroupCollapse: (groupId: string) => void;
|
|
31
29
|
setPinnedColumnsFromIds: (columnIds: string[]) => void;
|
|
32
|
-
setRowIndexPinned: (pinned: boolean) => void;
|
|
33
30
|
|
|
34
31
|
// Offset calculations
|
|
35
32
|
getColumnLeftOffset: (columnId: string) => number;
|
|
36
|
-
getRowIndexLeftOffset: () => number;
|
|
37
33
|
|
|
38
34
|
// Utility
|
|
39
35
|
isColumnPinned: (columnId: string) => boolean;
|
|
@@ -45,26 +41,22 @@ export function useSpreadsheetPinning<T>({
|
|
|
45
41
|
columnGroups,
|
|
46
42
|
showRowIndex = true,
|
|
47
43
|
defaultPinnedColumns = [],
|
|
48
|
-
defaultPinRowIndex = false,
|
|
49
44
|
}: UseSpreadsheetPinningOptions<T>): UseSpreadsheetPinningReturn<T> {
|
|
50
|
-
// Initialize pinned columns from defaults (
|
|
45
|
+
// Initialize pinned columns from defaults (including row index)
|
|
51
46
|
const [pinnedColumns, setPinnedColumns] = useState<Map<string, 'left' | 'right'>>(() => {
|
|
52
47
|
const map = new Map<string, 'left' | 'right'>();
|
|
53
48
|
defaultPinnedColumns.forEach((col) => {
|
|
54
|
-
|
|
55
|
-
map.set(col, 'left');
|
|
56
|
-
}
|
|
49
|
+
map.set(col, 'left');
|
|
57
50
|
});
|
|
58
51
|
return map;
|
|
59
52
|
});
|
|
60
53
|
|
|
61
|
-
// Check if row index should be pinned from either defaultPinRowIndex or defaultPinnedColumns
|
|
62
|
-
const [isRowIndexPinned, setIsRowIndexPinned] = useState(
|
|
63
|
-
defaultPinRowIndex || defaultPinnedColumns.includes(ROW_INDEX_COLUMN_ID)
|
|
64
|
-
);
|
|
65
54
|
const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(new Set());
|
|
66
55
|
|
|
67
|
-
//
|
|
56
|
+
// Derive isRowIndexPinned from pinnedColumns for convenience
|
|
57
|
+
const isRowIndexPinned = pinnedColumns.has(ROW_INDEX_COLUMN_ID);
|
|
58
|
+
|
|
59
|
+
// Toggle column pin (works for any column including row index)
|
|
68
60
|
const handleTogglePin = useCallback((columnId: string) => {
|
|
69
61
|
setPinnedColumns((prev) => {
|
|
70
62
|
const newMap = new Map(prev);
|
|
@@ -77,27 +69,13 @@ export function useSpreadsheetPinning<T>({
|
|
|
77
69
|
});
|
|
78
70
|
}, []);
|
|
79
71
|
|
|
80
|
-
// Toggle row index pin
|
|
81
|
-
const handleToggleRowIndexPin = useCallback(() => {
|
|
82
|
-
setIsRowIndexPinned((prev) => !prev);
|
|
83
|
-
}, []);
|
|
84
|
-
|
|
85
72
|
// Set pinned columns from an array of column IDs
|
|
86
73
|
const setPinnedColumnsFromIds = useCallback((columnIds: string[]) => {
|
|
87
74
|
const map = new Map<string, 'left' | 'right'>();
|
|
88
75
|
columnIds.forEach((col) => {
|
|
89
|
-
|
|
90
|
-
map.set(col, 'left');
|
|
91
|
-
}
|
|
76
|
+
map.set(col, 'left');
|
|
92
77
|
});
|
|
93
78
|
setPinnedColumns(map);
|
|
94
|
-
// Also update row index pinned state
|
|
95
|
-
setIsRowIndexPinned(columnIds.includes(ROW_INDEX_COLUMN_ID));
|
|
96
|
-
}, []);
|
|
97
|
-
|
|
98
|
-
// Set row index pinned state directly
|
|
99
|
-
const setRowIndexPinned = useCallback((pinned: boolean) => {
|
|
100
|
-
setIsRowIndexPinned(pinned);
|
|
101
79
|
}, []);
|
|
102
80
|
|
|
103
81
|
// Toggle group collapse
|
|
@@ -132,8 +110,11 @@ export function useSpreadsheetPinning<T>({
|
|
|
132
110
|
});
|
|
133
111
|
}
|
|
134
112
|
|
|
135
|
-
// If no columns are pinned, return result as-is to preserve original order
|
|
136
|
-
|
|
113
|
+
// If no columns are pinned (excluding row index), return result as-is to preserve original order
|
|
114
|
+
const nonRowIndexPinned = Array.from(pinnedColumns.keys()).filter(
|
|
115
|
+
(id) => id !== ROW_INDEX_COLUMN_ID
|
|
116
|
+
);
|
|
117
|
+
if (nonRowIndexPinned.length === 0) {
|
|
137
118
|
return result;
|
|
138
119
|
}
|
|
139
120
|
|
|
@@ -144,10 +125,10 @@ export function useSpreadsheetPinning<T>({
|
|
|
144
125
|
|
|
145
126
|
// Maintain the order of pinned columns as they were added
|
|
146
127
|
const pinnedLeftIds = Array.from(pinnedColumns.entries())
|
|
147
|
-
.filter(([, side]) => side === 'left')
|
|
128
|
+
.filter(([id, side]) => side === 'left' && id !== ROW_INDEX_COLUMN_ID)
|
|
148
129
|
.map(([id]) => id);
|
|
149
130
|
const pinnedRightIds = Array.from(pinnedColumns.entries())
|
|
150
|
-
.filter(([, side]) => side === 'right')
|
|
131
|
+
.filter(([id, side]) => side === 'right' && id !== ROW_INDEX_COLUMN_ID)
|
|
151
132
|
.map(([id]) => id);
|
|
152
133
|
|
|
153
134
|
for (const column of result) {
|
|
@@ -168,32 +149,35 @@ export function useSpreadsheetPinning<T>({
|
|
|
168
149
|
return [...leftPinned, ...unpinned, ...rightPinned];
|
|
169
150
|
}, [columns, columnGroups, collapsedGroups, pinnedColumns]);
|
|
170
151
|
|
|
171
|
-
// Get left offset for row index column
|
|
172
|
-
const getRowIndexLeftOffset = useCallback((): number => {
|
|
173
|
-
return 0;
|
|
174
|
-
}, []);
|
|
175
|
-
|
|
176
152
|
// Calculate column offset for sticky positioning
|
|
177
153
|
const getColumnLeftOffset = useCallback(
|
|
178
154
|
(columnId: string): number => {
|
|
155
|
+
// Row index column is always at left: 0 when pinned
|
|
156
|
+
if (columnId === ROW_INDEX_COLUMN_ID) {
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Get left-pinned columns (excluding row index)
|
|
179
161
|
const pinnedLeft = Array.from(pinnedColumns.entries())
|
|
180
|
-
.filter(([, side]) => side === 'left')
|
|
162
|
+
.filter(([id, side]) => side === 'left' && id !== ROW_INDEX_COLUMN_ID)
|
|
181
163
|
.map(([id]) => id);
|
|
182
164
|
const index = pinnedLeft.indexOf(columnId);
|
|
183
165
|
|
|
184
166
|
// Base offset includes the row index column if shown and pinned
|
|
185
|
-
const
|
|
167
|
+
const isRowIndexPinnedNow = pinnedColumns.has(ROW_INDEX_COLUMN_ID);
|
|
168
|
+
const baseOffset = showRowIndex && isRowIndexPinnedNow ? ROW_INDEX_COLUMN_WIDTH : 0;
|
|
186
169
|
|
|
187
170
|
if (index === -1) return baseOffset;
|
|
188
171
|
|
|
189
172
|
let offset = baseOffset;
|
|
190
173
|
for (let i = 0; i < index; i++) {
|
|
191
174
|
const col = columns.find((c) => c.id === pinnedLeft[i]);
|
|
192
|
-
|
|
175
|
+
// Use minWidth || width to match the rendered cell width
|
|
176
|
+
offset += col?.minWidth || col?.width || 100;
|
|
193
177
|
}
|
|
194
178
|
return offset;
|
|
195
179
|
},
|
|
196
|
-
[pinnedColumns, columns, showRowIndex
|
|
180
|
+
[pinnedColumns, columns, showRowIndex]
|
|
197
181
|
);
|
|
198
182
|
|
|
199
183
|
// Check if column is pinned
|
|
@@ -218,12 +202,9 @@ export function useSpreadsheetPinning<T>({
|
|
|
218
202
|
collapsedGroups,
|
|
219
203
|
visibleColumns,
|
|
220
204
|
handleTogglePin,
|
|
221
|
-
handleToggleRowIndexPin,
|
|
222
205
|
handleToggleGroupCollapse,
|
|
223
206
|
setPinnedColumnsFromIds,
|
|
224
|
-
setRowIndexPinned,
|
|
225
207
|
getColumnLeftOffset,
|
|
226
|
-
getRowIndexLeftOffset,
|
|
227
208
|
isColumnPinned,
|
|
228
209
|
getColumnPinSide,
|
|
229
210
|
};
|