@xcelsior/ui-spreadsheets 1.1.1 → 1.1.3

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.js CHANGED
@@ -370,7 +370,7 @@ var SpreadsheetCell = ({
370
370
  onKeyDown: handleCellKeyDown,
371
371
  "data-cell-id": `${rowId}-${column.id}`,
372
372
  className: cn(
373
- "border border-gray-200 group cursor-pointer select-none",
373
+ "border border-gray-200 group cursor-pointer transition-colors select-none",
374
374
  compactMode ? "text-[10px]" : "text-xs",
375
375
  cellPadding,
376
376
  column.align === "right" && "text-right",
@@ -1121,71 +1121,56 @@ function ColumnHeaderActions({
1121
1121
  unpinnedTitle = "Pin column",
1122
1122
  className
1123
1123
  }) {
1124
- const handleClick = (e) => {
1125
- e.stopPropagation();
1126
- };
1127
- const handleKeyDown = (e) => {
1128
- e.stopPropagation();
1129
- };
1130
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1131
- "button",
1132
- {
1133
- type: "button",
1134
- className: cn("flex items-center gap-0.5", className),
1135
- onClick: handleClick,
1136
- onKeyDown: handleKeyDown,
1137
- children: [
1138
- enableFiltering && onFilterClick && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1139
- "button",
1140
- {
1141
- type: "button",
1142
- onClick: (e) => {
1143
- e.stopPropagation();
1144
- onFilterClick();
1145
- },
1146
- className: cn(
1147
- "p-0.5 hover:bg-gray-200 rounded transition-opacity",
1148
- hasActiveFilter ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
1149
- ),
1150
- title: filterTitle,
1151
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HiFilter, { className: "h-3 w-3" })
1152
- }
1124
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: cn("flex items-center gap-0.5", className), children: [
1125
+ enableFiltering && onFilterClick && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1126
+ "button",
1127
+ {
1128
+ type: "button",
1129
+ onClick: (e) => {
1130
+ e.stopPropagation();
1131
+ onFilterClick();
1132
+ },
1133
+ className: cn(
1134
+ "p-0.5 hover:bg-gray-200 rounded transition-opacity",
1135
+ hasActiveFilter ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
1153
1136
  ),
1154
- enableHighlighting && onHighlightClick && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1155
- "button",
1156
- {
1157
- type: "button",
1158
- onClick: (e) => {
1159
- e.stopPropagation();
1160
- onHighlightClick();
1161
- },
1162
- className: cn(
1163
- "p-0.5 hover:bg-gray-200 rounded transition-opacity",
1164
- hasActiveHighlight ? "text-amber-500 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
1165
- ),
1166
- title: highlightTitle,
1167
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AiFillHighlight, { className: "h-3 w-3" })
1168
- }
1137
+ title: filterTitle,
1138
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HiFilter, { className: "h-3 w-3" })
1139
+ }
1140
+ ),
1141
+ enableHighlighting && onHighlightClick && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1142
+ "button",
1143
+ {
1144
+ type: "button",
1145
+ onClick: (e) => {
1146
+ e.stopPropagation();
1147
+ onHighlightClick();
1148
+ },
1149
+ className: cn(
1150
+ "p-0.5 hover:bg-gray-200 rounded transition-opacity",
1151
+ hasActiveHighlight ? "text-amber-500 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
1169
1152
  ),
1170
- enablePinning && onPinClick && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1171
- "button",
1172
- {
1173
- type: "button",
1174
- onClick: (e) => {
1175
- e.stopPropagation();
1176
- onPinClick();
1177
- },
1178
- className: cn(
1179
- "p-0.5 hover:bg-gray-200 rounded transition-opacity",
1180
- isPinned ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
1181
- ),
1182
- title: isPinned ? pinnedTitle : unpinnedTitle,
1183
- children: isPinned ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MdPushPin, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MdOutlinePushPin, { className: "h-3 w-3" })
1184
- }
1185
- )
1186
- ]
1187
- }
1188
- );
1153
+ title: highlightTitle,
1154
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AiFillHighlight, { className: "h-3 w-3" })
1155
+ }
1156
+ ),
1157
+ enablePinning && onPinClick && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1158
+ "button",
1159
+ {
1160
+ type: "button",
1161
+ onClick: (e) => {
1162
+ e.stopPropagation();
1163
+ onPinClick();
1164
+ },
1165
+ className: cn(
1166
+ "p-0.5 hover:bg-gray-200 rounded transition-opacity",
1167
+ isPinned ? "text-blue-600 opacity-100" : "text-gray-400 opacity-0 group-hover:opacity-100"
1168
+ ),
1169
+ title: isPinned ? pinnedTitle : unpinnedTitle,
1170
+ children: isPinned ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MdPushPin, { className: "h-3 w-3" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MdOutlinePushPin, { className: "h-3 w-3" })
1171
+ }
1172
+ )
1173
+ ] });
1189
1174
  }
1190
1175
  ColumnHeaderActions.displayName = "ColumnHeaderActions";
1191
1176
 
@@ -1280,22 +1265,17 @@ function useSpreadsheetPinning({
1280
1265
  columns,
1281
1266
  columnGroups,
1282
1267
  showRowIndex = true,
1283
- defaultPinnedColumns = [],
1284
- defaultPinRowIndex = false
1268
+ defaultPinnedColumns = []
1285
1269
  }) {
1286
1270
  const [pinnedColumns, setPinnedColumns] = (0, import_react6.useState)(() => {
1287
1271
  const map = /* @__PURE__ */ new Map();
1288
1272
  defaultPinnedColumns.forEach((col) => {
1289
- if (col !== ROW_INDEX_COLUMN_ID) {
1290
- map.set(col, "left");
1291
- }
1273
+ map.set(col, "left");
1292
1274
  });
1293
1275
  return map;
1294
1276
  });
1295
- const [isRowIndexPinned, setIsRowIndexPinned] = (0, import_react6.useState)(
1296
- defaultPinRowIndex || defaultPinnedColumns.includes(ROW_INDEX_COLUMN_ID)
1297
- );
1298
1277
  const [collapsedGroups, setCollapsedGroups] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
1278
+ const isRowIndexPinned = pinnedColumns.has(ROW_INDEX_COLUMN_ID);
1299
1279
  const handleTogglePin = (0, import_react6.useCallback)((columnId) => {
1300
1280
  setPinnedColumns((prev) => {
1301
1281
  const newMap = new Map(prev);
@@ -1307,21 +1287,12 @@ function useSpreadsheetPinning({
1307
1287
  return newMap;
1308
1288
  });
1309
1289
  }, []);
1310
- const handleToggleRowIndexPin = (0, import_react6.useCallback)(() => {
1311
- setIsRowIndexPinned((prev) => !prev);
1312
- }, []);
1313
1290
  const setPinnedColumnsFromIds = (0, import_react6.useCallback)((columnIds) => {
1314
1291
  const map = /* @__PURE__ */ new Map();
1315
1292
  columnIds.forEach((col) => {
1316
- if (col !== ROW_INDEX_COLUMN_ID) {
1317
- map.set(col, "left");
1318
- }
1293
+ map.set(col, "left");
1319
1294
  });
1320
1295
  setPinnedColumns(map);
1321
- setIsRowIndexPinned(columnIds.includes(ROW_INDEX_COLUMN_ID));
1322
- }, []);
1323
- const setRowIndexPinned = (0, import_react6.useCallback)((pinned) => {
1324
- setIsRowIndexPinned(pinned);
1325
1296
  }, []);
1326
1297
  const handleToggleGroupCollapse = (0, import_react6.useCallback)((groupId) => {
1327
1298
  setCollapsedGroups((prev) => {
@@ -1347,14 +1318,17 @@ function useSpreadsheetPinning({
1347
1318
  return pinnedColumns.has(column.id);
1348
1319
  });
1349
1320
  }
1350
- if (pinnedColumns.size === 0) {
1321
+ const nonRowIndexPinned = Array.from(pinnedColumns.keys()).filter(
1322
+ (id) => id !== ROW_INDEX_COLUMN_ID
1323
+ );
1324
+ if (nonRowIndexPinned.length === 0) {
1351
1325
  return result;
1352
1326
  }
1353
1327
  const leftPinned = [];
1354
1328
  const unpinned = [];
1355
1329
  const rightPinned = [];
1356
- const pinnedLeftIds = Array.from(pinnedColumns.entries()).filter(([, side]) => side === "left").map(([id]) => id);
1357
- const pinnedRightIds = Array.from(pinnedColumns.entries()).filter(([, side]) => side === "right").map(([id]) => id);
1330
+ const pinnedLeftIds = Array.from(pinnedColumns.entries()).filter(([id, side]) => side === "left" && id !== ROW_INDEX_COLUMN_ID).map(([id]) => id);
1331
+ const pinnedRightIds = Array.from(pinnedColumns.entries()).filter(([id, side]) => side === "right" && id !== ROW_INDEX_COLUMN_ID).map(([id]) => id);
1358
1332
  for (const column of result) {
1359
1333
  const pinSide = pinnedColumns.get(column.id);
1360
1334
  if (pinSide === "left") {
@@ -1369,23 +1343,24 @@ function useSpreadsheetPinning({
1369
1343
  rightPinned.sort((a, b) => pinnedRightIds.indexOf(a.id) - pinnedRightIds.indexOf(b.id));
1370
1344
  return [...leftPinned, ...unpinned, ...rightPinned];
1371
1345
  }, [columns, columnGroups, collapsedGroups, pinnedColumns]);
1372
- const getRowIndexLeftOffset = (0, import_react6.useCallback)(() => {
1373
- return 0;
1374
- }, []);
1375
1346
  const getColumnLeftOffset = (0, import_react6.useCallback)(
1376
1347
  (columnId) => {
1377
- const pinnedLeft = Array.from(pinnedColumns.entries()).filter(([, side]) => side === "left").map(([id]) => id);
1348
+ if (columnId === ROW_INDEX_COLUMN_ID) {
1349
+ return 0;
1350
+ }
1351
+ const pinnedLeft = Array.from(pinnedColumns.entries()).filter(([id, side]) => side === "left" && id !== ROW_INDEX_COLUMN_ID).map(([id]) => id);
1378
1352
  const index = pinnedLeft.indexOf(columnId);
1379
- const baseOffset = showRowIndex && isRowIndexPinned ? ROW_INDEX_COLUMN_WIDTH : 0;
1353
+ const isRowIndexPinnedNow = pinnedColumns.has(ROW_INDEX_COLUMN_ID);
1354
+ const baseOffset = showRowIndex && isRowIndexPinnedNow ? ROW_INDEX_COLUMN_WIDTH : 0;
1380
1355
  if (index === -1) return baseOffset;
1381
1356
  let offset = baseOffset;
1382
1357
  for (let i = 0; i < index; i++) {
1383
1358
  const col = columns.find((c) => c.id === pinnedLeft[i]);
1384
- offset += col?.width || col?.minWidth || 100;
1359
+ offset += col?.minWidth || col?.width || 100;
1385
1360
  }
1386
1361
  return offset;
1387
1362
  },
1388
- [pinnedColumns, columns, showRowIndex, isRowIndexPinned]
1363
+ [pinnedColumns, columns, showRowIndex]
1389
1364
  );
1390
1365
  const isColumnPinned = (0, import_react6.useCallback)(
1391
1366
  (columnId) => {
@@ -1405,12 +1380,9 @@ function useSpreadsheetPinning({
1405
1380
  collapsedGroups,
1406
1381
  visibleColumns,
1407
1382
  handleTogglePin,
1408
- handleToggleRowIndexPin,
1409
1383
  handleToggleGroupCollapse,
1410
1384
  setPinnedColumnsFromIds,
1411
- setRowIndexPinned,
1412
1385
  getColumnLeftOffset,
1413
- getRowIndexLeftOffset,
1414
1386
  isColumnPinned,
1415
1387
  getColumnPinSide
1416
1388
  };
@@ -2243,13 +2215,14 @@ function useSpreadsheetFiltering({
2243
2215
  onSortChange,
2244
2216
  serverSide = false,
2245
2217
  controlledFilters,
2246
- controlledSortConfig
2218
+ controlledSortConfig,
2219
+ defaultSortConfig
2247
2220
  }) {
2248
2221
  const [internalFilters, setInternalFilters] = (0, import_react11.useState)(
2249
2222
  {}
2250
2223
  );
2251
2224
  const [internalSortConfig, setInternalSortConfig] = (0, import_react11.useState)(
2252
- null
2225
+ defaultSortConfig ?? null
2253
2226
  );
2254
2227
  const [activeFilterColumn, setActiveFilterColumn] = (0, import_react11.useState)(null);
2255
2228
  const filters = controlledFilters ?? internalFilters;
@@ -3377,7 +3350,8 @@ function Spreadsheet({
3377
3350
  onSortChange,
3378
3351
  serverSide,
3379
3352
  controlledFilters,
3380
- controlledSortConfig: controlledSortConfig ?? spreadsheetSettings?.defaultSort
3353
+ controlledSortConfig,
3354
+ defaultSortConfig: spreadsheetSettings.defaultSort
3381
3355
  });
3382
3356
  const {
3383
3357
  getCellHighlight,
@@ -3409,7 +3383,8 @@ function Spreadsheet({
3409
3383
  getColumnPinSide
3410
3384
  } = useSpreadsheetPinning({
3411
3385
  columns,
3412
- columnGroups
3386
+ columnGroups,
3387
+ defaultPinnedColumns: initialSettings?.defaultPinnedColumns
3413
3388
  });
3414
3389
  const {
3415
3390
  getCellComments,
@@ -3478,6 +3453,27 @@ function Spreadsheet({
3478
3453
  defaultSort: sortConfig
3479
3454
  }));
3480
3455
  }, [sortConfig]);
3456
+ (0, import_react16.useEffect)(() => {
3457
+ const pinnedColumnIds = Array.from(pinnedColumns.keys());
3458
+ setSpreadsheetSettings((prev) => {
3459
+ const prevIds = prev.defaultPinnedColumns;
3460
+ if (prevIds.length === pinnedColumnIds.length && prevIds.every((id, idx) => id === pinnedColumnIds[idx])) {
3461
+ return prev;
3462
+ }
3463
+ return {
3464
+ ...prev,
3465
+ defaultPinnedColumns: pinnedColumnIds
3466
+ };
3467
+ });
3468
+ }, [pinnedColumns]);
3469
+ const isInitialMount = (0, import_react16.useRef)(true);
3470
+ (0, import_react16.useEffect)(() => {
3471
+ if (isInitialMount.current) {
3472
+ isInitialMount.current = false;
3473
+ return;
3474
+ }
3475
+ onSettingsChange?.(spreadsheetSettings);
3476
+ }, [spreadsheetSettings, onSettingsChange]);
3481
3477
  const applyUndo = (0, import_react16.useCallback)(() => {
3482
3478
  const entry = popUndoEntry();
3483
3479
  if (!entry || !onCellsEdit) return;