react-glide-table 2.3.0 → 2.3.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.cjs CHANGED
@@ -538,6 +538,90 @@ function withCellUpdate(context, commitValue) {
538
538
  };
539
539
  }
540
540
 
541
+ // src/components/ui/table/features/cell-selection/pasteData.ts
542
+ function countLeadingEmptyCells(cells) {
543
+ let depth = 0;
544
+ while (depth < cells.length && cells[depth] === "") {
545
+ depth += 1;
546
+ }
547
+ return depth;
548
+ }
549
+ function looksLikeSubtreeIndentation(leadingEmptyCounts) {
550
+ if (leadingEmptyCounts.length === 0) return false;
551
+ const firstDepth = leadingEmptyCounts[0] ?? 0;
552
+ if (firstDepth !== 0) return false;
553
+ return leadingEmptyCounts.some((depth) => depth > 0);
554
+ }
555
+ function parseClipboardTSV(text) {
556
+ return parseClipboardTSVWithDepths(text).values;
557
+ }
558
+ function parseClipboardTSVWithDepths(text) {
559
+ if (!text) return { values: [], depths: [] };
560
+ const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
561
+ const withoutTrailing = normalized.replace(/\n+$/, "");
562
+ if (!withoutTrailing) return { values: [], depths: [] };
563
+ const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
564
+ const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
565
+ const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
566
+ const values = [];
567
+ const depths = [];
568
+ for (let index = 0; index < rows.length; index += 1) {
569
+ const cells = rows[index] ?? [];
570
+ const depth = leadingEmptyCounts[index] ?? 0;
571
+ if (treatAsDepth) {
572
+ values.push(cells.slice(depth));
573
+ depths.push(depth);
574
+ } else {
575
+ values.push(cells);
576
+ depths.push(0);
577
+ }
578
+ }
579
+ return { values, depths };
580
+ }
581
+ function resolvePasteColumnIds(rows, startCol, width) {
582
+ if (width <= 0) return [];
583
+ const cells = rows[0]?.getVisibleCells() ?? [];
584
+ const columnIds = [];
585
+ for (let offset = 0; offset < width; offset += 1) {
586
+ const cell = cells[startCol + offset];
587
+ if (!cell) break;
588
+ columnIds.push(cell.column.id);
589
+ }
590
+ return columnIds;
591
+ }
592
+ function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
593
+ const { values, depths } = parseClipboardTSVWithDepths(text);
594
+ if (values.length === 0) return null;
595
+ const width = Math.max(...values.map((row) => row.length), 0);
596
+ if (width === 0) return null;
597
+ const columnIds = resolvePasteColumnIds(rows, startCol, width);
598
+ if (columnIds.length === 0) return null;
599
+ const rowIds = [];
600
+ for (let offset = 0; offset < values.length; offset += 1) {
601
+ const row = rows[startRow + offset];
602
+ if (!row) break;
603
+ rowIds.push(row.id);
604
+ }
605
+ const anchorRow = rows[endRow] ?? rows[startRow];
606
+ return {
607
+ mode,
608
+ startRow,
609
+ startCol,
610
+ endRow,
611
+ rowIds,
612
+ anchorRowId: anchorRow?.id ?? "",
613
+ columnIds,
614
+ values,
615
+ depths
616
+ };
617
+ }
618
+ function isEditablePasteTarget(target) {
619
+ if (!(target instanceof HTMLElement)) return false;
620
+ const tag = target.tagName;
621
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
622
+ return Boolean(target.isContentEditable);
623
+ }
624
+
541
625
  // src/components/ui/table/features/cell-selection/useCellSelection.ts
542
626
  var import_react3 = require("react");
543
627
 
@@ -1271,90 +1355,6 @@ function hasFillExtension(sourceBounds, fillBounds) {
1271
1355
  return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
1272
1356
  }
1273
1357
 
1274
- // src/components/ui/table/features/cell-selection/pasteData.ts
1275
- function countLeadingEmptyCells(cells) {
1276
- let depth = 0;
1277
- while (depth < cells.length && cells[depth] === "") {
1278
- depth += 1;
1279
- }
1280
- return depth;
1281
- }
1282
- function looksLikeSubtreeIndentation(leadingEmptyCounts) {
1283
- if (leadingEmptyCounts.length === 0) return false;
1284
- const firstDepth = leadingEmptyCounts[0] ?? 0;
1285
- if (firstDepth !== 0) return false;
1286
- return leadingEmptyCounts.some((depth) => depth > 0);
1287
- }
1288
- function parseClipboardTSV(text) {
1289
- return parseClipboardTSVWithDepths(text).values;
1290
- }
1291
- function parseClipboardTSVWithDepths(text) {
1292
- if (!text) return { values: [], depths: [] };
1293
- const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1294
- const withoutTrailing = normalized.replace(/\n+$/, "");
1295
- if (!withoutTrailing) return { values: [], depths: [] };
1296
- const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
1297
- const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
1298
- const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
1299
- const values = [];
1300
- const depths = [];
1301
- for (let index = 0; index < rows.length; index += 1) {
1302
- const cells = rows[index] ?? [];
1303
- const depth = leadingEmptyCounts[index] ?? 0;
1304
- if (treatAsDepth) {
1305
- values.push(cells.slice(depth));
1306
- depths.push(depth);
1307
- } else {
1308
- values.push(cells);
1309
- depths.push(0);
1310
- }
1311
- }
1312
- return { values, depths };
1313
- }
1314
- function resolvePasteColumnIds(rows, startCol, width) {
1315
- if (width <= 0) return [];
1316
- const cells = rows[0]?.getVisibleCells() ?? [];
1317
- const columnIds = [];
1318
- for (let offset = 0; offset < width; offset += 1) {
1319
- const cell = cells[startCol + offset];
1320
- if (!cell) break;
1321
- columnIds.push(cell.column.id);
1322
- }
1323
- return columnIds;
1324
- }
1325
- function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
1326
- const { values, depths } = parseClipboardTSVWithDepths(text);
1327
- if (values.length === 0) return null;
1328
- const width = Math.max(...values.map((row) => row.length), 0);
1329
- if (width === 0) return null;
1330
- const columnIds = resolvePasteColumnIds(rows, startCol, width);
1331
- if (columnIds.length === 0) return null;
1332
- const rowIds = [];
1333
- for (let offset = 0; offset < values.length; offset += 1) {
1334
- const row = rows[startRow + offset];
1335
- if (!row) break;
1336
- rowIds.push(row.id);
1337
- }
1338
- const anchorRow = rows[endRow] ?? rows[startRow];
1339
- return {
1340
- mode,
1341
- startRow,
1342
- startCol,
1343
- endRow,
1344
- rowIds,
1345
- anchorRowId: anchorRow?.id ?? "",
1346
- columnIds,
1347
- values,
1348
- depths
1349
- };
1350
- }
1351
- function isEditablePasteTarget(target) {
1352
- if (!(target instanceof HTMLElement)) return false;
1353
- const tag = target.tagName;
1354
- if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
1355
- return Boolean(target.isContentEditable);
1356
- }
1357
-
1358
1358
  // src/components/ui/table/features/cell-selection/useCellSelection.ts
1359
1359
  function useCellSelection({
1360
1360
  data,
@@ -1436,11 +1436,19 @@ function useCellSelection({
1436
1436
  },
1437
1437
  [enabled]
1438
1438
  );
1439
+ const clearSelection = (0, import_react3.useCallback)(() => {
1440
+ const prev = dragStateRef.current;
1441
+ if (prev.start === null && prev.end === null && !prev.isSelecting && !prev.isFillDragging) {
1442
+ return;
1443
+ }
1444
+ dragStateRef.current = INITIAL_DRAG_STATE;
1445
+ setDragState(INITIAL_DRAG_STATE);
1446
+ }, []);
1439
1447
  (0, import_react3.useEffect)(() => {
1440
1448
  if (!enabled) {
1441
- setDragState(INITIAL_DRAG_STATE);
1449
+ clearSelection();
1442
1450
  }
1443
- }, [enabled]);
1451
+ }, [clearSelection, enabled]);
1444
1452
  (0, import_react3.useEffect)(() => {
1445
1453
  if (!enabled) return;
1446
1454
  const handleKeyDown = (e) => {
@@ -1654,6 +1662,7 @@ function useCellSelection({
1654
1662
  handleCellMouseDown,
1655
1663
  handleCellMouseEnter,
1656
1664
  handleFillHandleMouseDown,
1665
+ clearSelection,
1657
1666
  copySelection
1658
1667
  };
1659
1668
  }
@@ -2937,6 +2946,7 @@ function useGlideTable(options) {
2937
2946
  handleCellMouseDown,
2938
2947
  handleCellMouseEnter,
2939
2948
  handleFillHandleMouseDown,
2949
+ clearSelection: clearCellSelection,
2940
2950
  copySelection
2941
2951
  } = useCellSelection({
2942
2952
  data: tableData,
@@ -2952,6 +2962,42 @@ function useGlideTable(options) {
2952
2962
  cellRendererRegistry,
2953
2963
  rootRef
2954
2964
  });
2965
+ const clearRowSelection = (0, import_react6.useCallback)(() => {
2966
+ if (rowSelectionMode === "none") return;
2967
+ const hasSelection = Object.values(rowSelection).some(Boolean);
2968
+ if (!hasSelection) return;
2969
+ if (onRowSelectionChange) {
2970
+ onRowSelectionChange(() => ({}));
2971
+ return;
2972
+ }
2973
+ setInternalRowSelection({});
2974
+ }, [onRowSelectionChange, rowSelection, rowSelectionMode]);
2975
+ (0, import_react6.useEffect)(() => {
2976
+ const clearAllSelections = () => {
2977
+ clearCellSelection();
2978
+ clearRowSelection();
2979
+ };
2980
+ const handleKeyDown = (event) => {
2981
+ if (event.key !== "Escape") return;
2982
+ if (event.defaultPrevented) return;
2983
+ if (isEditablePasteTarget(event.target) || isEditablePasteTarget(document.activeElement)) {
2984
+ return;
2985
+ }
2986
+ clearAllSelections();
2987
+ };
2988
+ const handleMouseDown = (event) => {
2989
+ const root = rootRef.current;
2990
+ if (!root) return;
2991
+ if (event.target instanceof Node && root.contains(event.target)) return;
2992
+ clearAllSelections();
2993
+ };
2994
+ window.addEventListener("keydown", handleKeyDown);
2995
+ document.addEventListener("mousedown", handleMouseDown);
2996
+ return () => {
2997
+ window.removeEventListener("keydown", handleKeyDown);
2998
+ document.removeEventListener("mousedown", handleMouseDown);
2999
+ };
3000
+ }, [clearCellSelection, clearRowSelection]);
2955
3001
  const {
2956
3002
  editingCell,
2957
3003
  draftValue,
package/dist/index.js CHANGED
@@ -441,6 +441,90 @@ function withCellUpdate(context, commitValue) {
441
441
  };
442
442
  }
443
443
 
444
+ // src/components/ui/table/features/cell-selection/pasteData.ts
445
+ function countLeadingEmptyCells(cells) {
446
+ let depth = 0;
447
+ while (depth < cells.length && cells[depth] === "") {
448
+ depth += 1;
449
+ }
450
+ return depth;
451
+ }
452
+ function looksLikeSubtreeIndentation(leadingEmptyCounts) {
453
+ if (leadingEmptyCounts.length === 0) return false;
454
+ const firstDepth = leadingEmptyCounts[0] ?? 0;
455
+ if (firstDepth !== 0) return false;
456
+ return leadingEmptyCounts.some((depth) => depth > 0);
457
+ }
458
+ function parseClipboardTSV(text) {
459
+ return parseClipboardTSVWithDepths(text).values;
460
+ }
461
+ function parseClipboardTSVWithDepths(text) {
462
+ if (!text) return { values: [], depths: [] };
463
+ const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
464
+ const withoutTrailing = normalized.replace(/\n+$/, "");
465
+ if (!withoutTrailing) return { values: [], depths: [] };
466
+ const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
467
+ const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
468
+ const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
469
+ const values = [];
470
+ const depths = [];
471
+ for (let index = 0; index < rows.length; index += 1) {
472
+ const cells = rows[index] ?? [];
473
+ const depth = leadingEmptyCounts[index] ?? 0;
474
+ if (treatAsDepth) {
475
+ values.push(cells.slice(depth));
476
+ depths.push(depth);
477
+ } else {
478
+ values.push(cells);
479
+ depths.push(0);
480
+ }
481
+ }
482
+ return { values, depths };
483
+ }
484
+ function resolvePasteColumnIds(rows, startCol, width) {
485
+ if (width <= 0) return [];
486
+ const cells = rows[0]?.getVisibleCells() ?? [];
487
+ const columnIds = [];
488
+ for (let offset = 0; offset < width; offset += 1) {
489
+ const cell = cells[startCol + offset];
490
+ if (!cell) break;
491
+ columnIds.push(cell.column.id);
492
+ }
493
+ return columnIds;
494
+ }
495
+ function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
496
+ const { values, depths } = parseClipboardTSVWithDepths(text);
497
+ if (values.length === 0) return null;
498
+ const width = Math.max(...values.map((row) => row.length), 0);
499
+ if (width === 0) return null;
500
+ const columnIds = resolvePasteColumnIds(rows, startCol, width);
501
+ if (columnIds.length === 0) return null;
502
+ const rowIds = [];
503
+ for (let offset = 0; offset < values.length; offset += 1) {
504
+ const row = rows[startRow + offset];
505
+ if (!row) break;
506
+ rowIds.push(row.id);
507
+ }
508
+ const anchorRow = rows[endRow] ?? rows[startRow];
509
+ return {
510
+ mode,
511
+ startRow,
512
+ startCol,
513
+ endRow,
514
+ rowIds,
515
+ anchorRowId: anchorRow?.id ?? "",
516
+ columnIds,
517
+ values,
518
+ depths
519
+ };
520
+ }
521
+ function isEditablePasteTarget(target) {
522
+ if (!(target instanceof HTMLElement)) return false;
523
+ const tag = target.tagName;
524
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
525
+ return Boolean(target.isContentEditable);
526
+ }
527
+
444
528
  // src/components/ui/table/features/cell-selection/useCellSelection.ts
445
529
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
446
530
 
@@ -1174,90 +1258,6 @@ function hasFillExtension(sourceBounds, fillBounds) {
1174
1258
  return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
1175
1259
  }
1176
1260
 
1177
- // src/components/ui/table/features/cell-selection/pasteData.ts
1178
- function countLeadingEmptyCells(cells) {
1179
- let depth = 0;
1180
- while (depth < cells.length && cells[depth] === "") {
1181
- depth += 1;
1182
- }
1183
- return depth;
1184
- }
1185
- function looksLikeSubtreeIndentation(leadingEmptyCounts) {
1186
- if (leadingEmptyCounts.length === 0) return false;
1187
- const firstDepth = leadingEmptyCounts[0] ?? 0;
1188
- if (firstDepth !== 0) return false;
1189
- return leadingEmptyCounts.some((depth) => depth > 0);
1190
- }
1191
- function parseClipboardTSV(text) {
1192
- return parseClipboardTSVWithDepths(text).values;
1193
- }
1194
- function parseClipboardTSVWithDepths(text) {
1195
- if (!text) return { values: [], depths: [] };
1196
- const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1197
- const withoutTrailing = normalized.replace(/\n+$/, "");
1198
- if (!withoutTrailing) return { values: [], depths: [] };
1199
- const rows = withoutTrailing.split("\n").map((line) => line.split(" "));
1200
- const leadingEmptyCounts = rows.map(countLeadingEmptyCells);
1201
- const treatAsDepth = looksLikeSubtreeIndentation(leadingEmptyCounts);
1202
- const values = [];
1203
- const depths = [];
1204
- for (let index = 0; index < rows.length; index += 1) {
1205
- const cells = rows[index] ?? [];
1206
- const depth = leadingEmptyCounts[index] ?? 0;
1207
- if (treatAsDepth) {
1208
- values.push(cells.slice(depth));
1209
- depths.push(depth);
1210
- } else {
1211
- values.push(cells);
1212
- depths.push(0);
1213
- }
1214
- }
1215
- return { values, depths };
1216
- }
1217
- function resolvePasteColumnIds(rows, startCol, width) {
1218
- if (width <= 0) return [];
1219
- const cells = rows[0]?.getVisibleCells() ?? [];
1220
- const columnIds = [];
1221
- for (let offset = 0; offset < width; offset += 1) {
1222
- const cell = cells[startCol + offset];
1223
- if (!cell) break;
1224
- columnIds.push(cell.column.id);
1225
- }
1226
- return columnIds;
1227
- }
1228
- function buildRowsPastePayload(rows, startRow, startCol, text, mode, endRow = startRow) {
1229
- const { values, depths } = parseClipboardTSVWithDepths(text);
1230
- if (values.length === 0) return null;
1231
- const width = Math.max(...values.map((row) => row.length), 0);
1232
- if (width === 0) return null;
1233
- const columnIds = resolvePasteColumnIds(rows, startCol, width);
1234
- if (columnIds.length === 0) return null;
1235
- const rowIds = [];
1236
- for (let offset = 0; offset < values.length; offset += 1) {
1237
- const row = rows[startRow + offset];
1238
- if (!row) break;
1239
- rowIds.push(row.id);
1240
- }
1241
- const anchorRow = rows[endRow] ?? rows[startRow];
1242
- return {
1243
- mode,
1244
- startRow,
1245
- startCol,
1246
- endRow,
1247
- rowIds,
1248
- anchorRowId: anchorRow?.id ?? "",
1249
- columnIds,
1250
- values,
1251
- depths
1252
- };
1253
- }
1254
- function isEditablePasteTarget(target) {
1255
- if (!(target instanceof HTMLElement)) return false;
1256
- const tag = target.tagName;
1257
- if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
1258
- return Boolean(target.isContentEditable);
1259
- }
1260
-
1261
1261
  // src/components/ui/table/features/cell-selection/useCellSelection.ts
1262
1262
  function useCellSelection({
1263
1263
  data,
@@ -1339,11 +1339,19 @@ function useCellSelection({
1339
1339
  },
1340
1340
  [enabled]
1341
1341
  );
1342
+ const clearSelection = useCallback2(() => {
1343
+ const prev = dragStateRef.current;
1344
+ if (prev.start === null && prev.end === null && !prev.isSelecting && !prev.isFillDragging) {
1345
+ return;
1346
+ }
1347
+ dragStateRef.current = INITIAL_DRAG_STATE;
1348
+ setDragState(INITIAL_DRAG_STATE);
1349
+ }, []);
1342
1350
  useEffect2(() => {
1343
1351
  if (!enabled) {
1344
- setDragState(INITIAL_DRAG_STATE);
1352
+ clearSelection();
1345
1353
  }
1346
- }, [enabled]);
1354
+ }, [clearSelection, enabled]);
1347
1355
  useEffect2(() => {
1348
1356
  if (!enabled) return;
1349
1357
  const handleKeyDown = (e) => {
@@ -1557,6 +1565,7 @@ function useCellSelection({
1557
1565
  handleCellMouseDown,
1558
1566
  handleCellMouseEnter,
1559
1567
  handleFillHandleMouseDown,
1568
+ clearSelection,
1560
1569
  copySelection
1561
1570
  };
1562
1571
  }
@@ -2847,6 +2856,7 @@ function useGlideTable(options) {
2847
2856
  handleCellMouseDown,
2848
2857
  handleCellMouseEnter,
2849
2858
  handleFillHandleMouseDown,
2859
+ clearSelection: clearCellSelection,
2850
2860
  copySelection
2851
2861
  } = useCellSelection({
2852
2862
  data: tableData,
@@ -2862,6 +2872,42 @@ function useGlideTable(options) {
2862
2872
  cellRendererRegistry,
2863
2873
  rootRef
2864
2874
  });
2875
+ const clearRowSelection = useCallback4(() => {
2876
+ if (rowSelectionMode === "none") return;
2877
+ const hasSelection = Object.values(rowSelection).some(Boolean);
2878
+ if (!hasSelection) return;
2879
+ if (onRowSelectionChange) {
2880
+ onRowSelectionChange(() => ({}));
2881
+ return;
2882
+ }
2883
+ setInternalRowSelection({});
2884
+ }, [onRowSelectionChange, rowSelection, rowSelectionMode]);
2885
+ useEffect5(() => {
2886
+ const clearAllSelections = () => {
2887
+ clearCellSelection();
2888
+ clearRowSelection();
2889
+ };
2890
+ const handleKeyDown = (event) => {
2891
+ if (event.key !== "Escape") return;
2892
+ if (event.defaultPrevented) return;
2893
+ if (isEditablePasteTarget(event.target) || isEditablePasteTarget(document.activeElement)) {
2894
+ return;
2895
+ }
2896
+ clearAllSelections();
2897
+ };
2898
+ const handleMouseDown = (event) => {
2899
+ const root = rootRef.current;
2900
+ if (!root) return;
2901
+ if (event.target instanceof Node && root.contains(event.target)) return;
2902
+ clearAllSelections();
2903
+ };
2904
+ window.addEventListener("keydown", handleKeyDown);
2905
+ document.addEventListener("mousedown", handleMouseDown);
2906
+ return () => {
2907
+ window.removeEventListener("keydown", handleKeyDown);
2908
+ document.removeEventListener("mousedown", handleMouseDown);
2909
+ };
2910
+ }, [clearCellSelection, clearRowSelection]);
2865
2911
  const {
2866
2912
  editingCell,
2867
2913
  draftValue,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-glide-table",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/zpxlffjrm/react-glide-table.git"