@ricsam/react-spreadsheets 0.0.10 → 0.0.12

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/react-spreadsheets",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/lib.mjs CHANGED
@@ -8,6 +8,8 @@ import React2, {
8
8
  useCallback as useCallback3,
9
9
  useEffect as useEffect3,
10
10
  useImperativeHandle,
11
+ useId,
12
+ useLayoutEffect,
11
13
  useMemo,
12
14
  useRef as useRef3,
13
15
  useState as useState4
@@ -1326,6 +1328,7 @@ var resizeCellSnapAnchorToNearestEdges = (anchor, rect, handle, columnWidths, ro
1326
1328
 
1327
1329
  // src/spreadsheet/spreadsheet.tsx
1328
1330
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1331
+ var SPREADSHEET_OVERLAY_SELECTOR = "[data-rsp-overlay]";
1329
1332
  var resolveGridLineColor = (probe, colorScheme) => {
1330
1333
  const fallback = colorScheme === "dark" ? "#232c3d" : "#e8ebf1";
1331
1334
  if (!probe || typeof getComputedStyle !== "function") {
@@ -1471,6 +1474,7 @@ var CellComponent = React2.memo(({
1471
1474
  value,
1472
1475
  row,
1473
1476
  col,
1477
+ domId,
1474
1478
  selectionManager,
1475
1479
  cellToPixelBase,
1476
1480
  getColumnWidth,
@@ -1484,11 +1488,11 @@ var CellComponent = React2.memo(({
1484
1488
  }) => {
1485
1489
  const cellRef = useCallback3((el) => {
1486
1490
  if (el) {
1487
- const rowIndex2 = row - 1;
1488
- const colIndex2 = columnToIndex(col);
1491
+ const rowIndex = row - 1;
1492
+ const colIndex = columnToIndex(col);
1489
1493
  return selectionManager.setupCellElement(el, {
1490
- row: rowIndex2,
1491
- col: colIndex2
1494
+ row: rowIndex,
1495
+ col: colIndex
1492
1496
  });
1493
1497
  }
1494
1498
  }, [row, col, selectionManager]);
@@ -1516,16 +1520,22 @@ var CellComponent = React2.memo(({
1516
1520
  });
1517
1521
  const inputRef = useCallback3((el) => {
1518
1522
  if (el) {
1519
- const rowIndex2 = row - 1;
1520
- const colIndex2 = columnToIndex(col);
1523
+ const rowIndex = row - 1;
1524
+ const colIndex = columnToIndex(col);
1521
1525
  return selectionManager.setupInputElement(el, {
1522
- rowIndex: rowIndex2,
1523
- colIndex: colIndex2
1526
+ rowIndex,
1527
+ colIndex
1524
1528
  });
1525
1529
  }
1526
1530
  }, [row, col, selectionManager]);
1527
1531
  return /* @__PURE__ */ jsxs2("div", {
1528
1532
  ref: cellRef,
1533
+ id: domId,
1534
+ role: "gridcell",
1535
+ "aria-rowindex": row + 1,
1536
+ "aria-colindex": colIndex + 2,
1537
+ "aria-selected": isSelected,
1538
+ "data-cell-address": `${col}${row}`,
1529
1539
  className: cn("rsp-cell", {
1530
1540
  "rsp-cell-multiline": typeof value === "string" && value.includes(`
1531
1541
  `),
@@ -1545,6 +1555,7 @@ var CellComponent = React2.memo(({
1545
1555
  children: [
1546
1556
  isBeingEdited ? /* @__PURE__ */ jsx3("textarea", {
1547
1557
  className: "rsp-cell-editor",
1558
+ "aria-label": `Edit ${col}${row}`,
1548
1559
  "data-testid": `spreadsheet-cell-input-${col}${row}`,
1549
1560
  ref: inputRef,
1550
1561
  defaultValue: inputValue?.toString() ?? "",
@@ -1623,6 +1634,10 @@ var HeaderComponent = React2.memo(({
1623
1634
  const content = type === "col" ? reverseHeaders ? index + 1 : indexToColumn(index) : reverseHeaders ? rowToLetter(index + 1) : index + 1;
1624
1635
  return /* @__PURE__ */ jsxs2("div", {
1625
1636
  ref: headerRef,
1637
+ role: type === "col" ? "columnheader" : "rowheader",
1638
+ "aria-colindex": type === "col" ? index + 2 : 1,
1639
+ "aria-rowindex": type === "row" ? index + 2 : 1,
1640
+ "aria-selected": isSelected,
1626
1641
  className: cn("rsp-header", `rsp-header-${type}`, {
1627
1642
  "rsp-header-selected": isSelected
1628
1643
  }),
@@ -1767,6 +1782,9 @@ var Spreadsheet = forwardRef(({
1767
1782
  style,
1768
1783
  className,
1769
1784
  containerProps,
1785
+ "aria-label": ariaLabel = "Spreadsheet",
1786
+ "aria-labelledby": ariaLabelledBy,
1787
+ "aria-describedby": ariaDescribedBy,
1770
1788
  cellData: controlledCellData,
1771
1789
  onCellDataChange,
1772
1790
  onCellDataChangeError,
@@ -1789,6 +1807,7 @@ var Spreadsheet = forwardRef(({
1789
1807
  parentSelected = true,
1790
1808
  disableClipboard = false
1791
1809
  }, ref) => {
1810
+ const gridId = useId();
1792
1811
  const [{ width, height }, containerRef] = useResizeObserver();
1793
1812
  const [containerDivRef, setContainerDivRef] = useState4(null);
1794
1813
  const colorScheme = useColorScheme(containerDivRef);
@@ -1982,15 +2001,15 @@ var Spreadsheet = forwardRef(({
1982
2001
  };
1983
2002
  const effectsRef = useRef3(effectsRefOb);
1984
2003
  effectsRef.current = effectsRefOb;
1985
- const setupCanvas = useCallback3((canvas, width2, height2) => {
2004
+ const setupCanvas = useCallback3((canvas, width, height) => {
1986
2005
  const ctx = canvas.getContext("2d");
1987
2006
  if (!ctx)
1988
2007
  return null;
1989
2008
  const devicePixelRatio = window.devicePixelRatio || 1;
1990
- canvas.width = width2 * devicePixelRatio;
1991
- canvas.height = height2 * devicePixelRatio;
1992
- canvas.style.width = width2 + "px";
1993
- canvas.style.height = height2 + "px";
2009
+ canvas.width = width * devicePixelRatio;
2010
+ canvas.height = height * devicePixelRatio;
2011
+ canvas.style.width = width + "px";
2012
+ canvas.style.height = height + "px";
1994
2013
  ctx.scale(devicePixelRatio, devicePixelRatio);
1995
2014
  return ctx;
1996
2015
  }, []);
@@ -2218,36 +2237,36 @@ var Spreadsheet = forwardRef(({
2218
2237
  return selectionManager.listenToUpdateData((updates) => {
2219
2238
  const editingState = selectionManager.isEditing;
2220
2239
  try {
2221
- const { cellData: cellData2, onCellDataChange: onCellDataChange2 } = effectsRef.current;
2222
- const newData = new Map(cellData2);
2240
+ const { cellData, onCellDataChange } = effectsRef.current;
2241
+ const newData = new Map(cellData);
2223
2242
  updates.forEach(({ value, rowIndex, colIndex }) => {
2224
2243
  const key = `${indexToColumn(colIndex)}${rowIndex + 1}`;
2225
- const parseValue2 = effectsRef.current.parseValue;
2244
+ const parseValue = effectsRef.current.parseValue;
2226
2245
  if (value === "") {
2227
2246
  newData.delete(key);
2228
2247
  } else {
2229
2248
  let parsedValue = value;
2230
- if (parseValue2 && typeof value === "string") {
2249
+ if (parseValue && typeof value === "string") {
2231
2250
  const match = key.match(/^([A-Z]+)(\d+)$/);
2232
2251
  const col = match?.[1];
2233
2252
  const rowText = match?.[2];
2234
2253
  if (col && rowText) {
2235
2254
  const row = parseInt(rowText, 10);
2236
- const colIndex2 = columnToIndex(col);
2237
- const rowIndex2 = row - 1;
2238
- parsedValue = parseValue2(value, {
2255
+ const colIndex = columnToIndex(col);
2256
+ const rowIndex = row - 1;
2257
+ parsedValue = parseValue(value, {
2239
2258
  id: key,
2240
2259
  row,
2241
2260
  col,
2242
- rowIndex: rowIndex2,
2243
- colIndex: colIndex2
2261
+ rowIndex,
2262
+ colIndex
2244
2263
  });
2245
2264
  }
2246
2265
  }
2247
2266
  newData.set(key, parsedValue);
2248
2267
  }
2249
2268
  });
2250
- onCellDataChange2?.(newData);
2269
+ onCellDataChange?.(newData);
2251
2270
  setInternalCellData(newData);
2252
2271
  } catch (error) {
2253
2272
  console.error("Failed to update spreadsheet cell data:", error);
@@ -2286,6 +2305,12 @@ var Spreadsheet = forwardRef(({
2286
2305
  });
2287
2306
  }, [selectionManager, clipboardManager, disableClipboard]);
2288
2307
  const handleWheel = useCallback3((e) => {
2308
+ const target = e.target;
2309
+ const currentTarget = e.currentTarget;
2310
+ const overlay = target instanceof Element ? target.closest(SPREADSHEET_OVERLAY_SELECTOR) : null;
2311
+ if (overlay && currentTarget instanceof Node && currentTarget.contains(overlay)) {
2312
+ return;
2313
+ }
2289
2314
  e.preventDefault();
2290
2315
  e.stopPropagation();
2291
2316
  setViewport((previous) => ({
@@ -2424,13 +2449,13 @@ var Spreadsheet = forwardRef(({
2424
2449
  const handleColumnResizeDoubleClick = useCallback3((e, col) => {
2425
2450
  e.preventDefault();
2426
2451
  e.stopPropagation();
2427
- const { viewport: viewport2, gridDims: gridDims2, height: height2, cellData: cellData2 } = effectsRef.current;
2452
+ const { viewport, gridDims, height, cellData } = effectsRef.current;
2428
2453
  let maxWidth = 50;
2429
- const startRow = Math.max(0, Math.floor(viewport2.scrollY / gridDims2.cellHeight));
2430
- const endRow = Math.ceil((viewport2.scrollY + height2) / gridDims2.cellHeight) + 1;
2454
+ const startRow = Math.max(0, Math.floor(viewport.scrollY / gridDims.cellHeight));
2455
+ const endRow = Math.ceil((viewport.scrollY + height) / gridDims.cellHeight) + 1;
2431
2456
  for (let row = startRow;row <= endRow; row++) {
2432
2457
  const cellKey = `${col}${getRowNumber(row)}`;
2433
- const content = cellData2.get(cellKey) ?? "";
2458
+ const content = cellData.get(cellKey) ?? "";
2434
2459
  if (content) {
2435
2460
  const estimatedWidth = String(content).length * 8 + 20;
2436
2461
  maxWidth = Math.max(maxWidth, estimatedWidth);
@@ -2699,8 +2724,8 @@ var Spreadsheet = forwardRef(({
2699
2724
  return editingCleanup;
2700
2725
  }, [selectionManager]);
2701
2726
  React2.useEffect(() => {
2702
- return selectionManager.observeStateChange((state) => state.hasFocus, (hasFocus2) => {
2703
- if (hasFocus2 && selectedOverlayId) {
2727
+ return selectionManager.observeStateChange((state) => state.hasFocus, (hasFocus) => {
2728
+ if (hasFocus && selectedOverlayId) {
2704
2729
  setSelectedOverlay(null);
2705
2730
  }
2706
2731
  });
@@ -2803,6 +2828,21 @@ var Spreadsheet = forwardRef(({
2803
2828
  const visibleCells = React2.useMemo(() => getVisibleCells(), [getVisibleCells]);
2804
2829
  const visibleColumnHeaders = React2.useMemo(() => getVisibleColumnHeaders(), [getVisibleColumnHeaders]);
2805
2830
  const visibleRowHeaders = React2.useMemo(() => getVisibleRowHeaders(), [getVisibleRowHeaders]);
2831
+ const activeAddress = useSelectionManager(selectionManager, () => {
2832
+ const cell = selectionManager.getTopLeftCellInSelection();
2833
+ return cell ? `${indexToColumn(cell.col)}${cell.row + 1}` : undefined;
2834
+ });
2835
+ const activeCellId = visibleCells.some((cell) => `${cell.col}${cell.row}` === activeAddress) ? `${gridId}-cell-${activeAddress}` : undefined;
2836
+ const accessibilityManager = selectionManager;
2837
+ useLayoutEffect(() => {
2838
+ accessibilityManager.setInputCaptureAccessibility?.({
2839
+ label: `${ariaLabel} keyboard input`,
2840
+ controls: gridId,
2841
+ activeDescendant: activeCellId,
2842
+ describedBy: ariaDescribedBy
2843
+ });
2844
+ return () => accessibilityManager.setInputCaptureAccessibility?.({});
2845
+ }, [accessibilityManager, ariaLabel, ariaDescribedBy, gridId, activeCellId]);
2806
2846
  const hasFocus = useSelectionManager(selectionManager, () => selectionManager.hasFocus);
2807
2847
  const referenceSelection = useSelectionManager(selectionManager, () => selectionManager.referenceSelection);
2808
2848
  const referenceSelectionRect = useMemo(() => {
@@ -2878,7 +2918,21 @@ var Spreadsheet = forwardRef(({
2878
2918
  width,
2879
2919
  height
2880
2920
  },
2921
+ id: gridId,
2922
+ role: "grid",
2923
+ "aria-activedescendant": activeCellId,
2924
+ "aria-label": ariaLabel,
2925
+ "aria-labelledby": ariaLabelledBy,
2926
+ "aria-describedby": ariaDescribedBy,
2927
+ "aria-rowcount": finiteRowCount === undefined ? -1 : finiteRowCount + 1,
2928
+ "aria-colcount": finiteColumnCount === undefined ? -1 : finiteColumnCount + 1,
2929
+ "aria-multiselectable": "true",
2881
2930
  tabIndex: 0,
2931
+ onFocus: (event) => {
2932
+ if (event.target === event.currentTarget && !selectionManager.hasFocus) {
2933
+ selectionManager.focus();
2934
+ }
2935
+ },
2882
2936
  children: [
2883
2937
  /* @__PURE__ */ jsx3("span", {
2884
2938
  ref: gridLineProbeRef,
@@ -2894,12 +2948,15 @@ var Spreadsheet = forwardRef(({
2894
2948
  }),
2895
2949
  /* @__PURE__ */ jsx3("canvas", {
2896
2950
  ref: canvasRef,
2951
+ "aria-hidden": "true",
2897
2952
  className: "rsp-canvas",
2898
2953
  "data-testid": "spreadsheet-canvas",
2899
2954
  style: { width, height }
2900
2955
  }),
2901
2956
  /* @__PURE__ */ jsx3("div", {
2902
2957
  className: "rsp-column-headers",
2958
+ role: "row",
2959
+ "aria-rowindex": 1,
2903
2960
  "data-testid": "spreadsheet-column-headers",
2904
2961
  style: {
2905
2962
  left: gridDims.headerWidth,
@@ -2942,16 +2999,21 @@ var Spreadsheet = forwardRef(({
2942
2999
  transformOrigin: "0 0"
2943
3000
  },
2944
3001
  children: visibleRowHeaders.map(({ row, rowIndex }) => {
2945
- return /* @__PURE__ */ jsx3(HeaderComponent, {
2946
- index: rowIndex,
2947
- type: "row",
2948
- selectionManager,
2949
- getColumnWidth,
2950
- getRowHeight,
2951
- handleColumnResizeStart,
2952
- handleColumnResizeDoubleClick,
2953
- handleRowResizeStart,
2954
- handleRowResizeDoubleClick
3002
+ return /* @__PURE__ */ jsx3("div", {
3003
+ role: "row",
3004
+ "aria-rowindex": row + 1,
3005
+ "aria-owns": visibleCells.filter((cell) => cell.row === row).map((cell) => `${gridId}-cell-${cell.col}${cell.row}`).join(" ") || undefined,
3006
+ children: /* @__PURE__ */ jsx3(HeaderComponent, {
3007
+ index: rowIndex,
3008
+ type: "row",
3009
+ selectionManager,
3010
+ getColumnWidth,
3011
+ getRowHeight,
3012
+ handleColumnResizeStart,
3013
+ handleColumnResizeDoubleClick,
3014
+ handleRowResizeStart,
3015
+ handleRowResizeDoubleClick
3016
+ })
2955
3017
  }, row);
2956
3018
  })
2957
3019
  })
@@ -2974,6 +3036,7 @@ var Spreadsheet = forwardRef(({
2974
3036
  },
2975
3037
  children: [
2976
3038
  visibleCells.map((cell) => /* @__PURE__ */ jsx3(CellComponent, {
3039
+ domId: `${gridId}-cell-${cell.col}${cell.row}`,
2977
3040
  value: cellData.get(`${cell.col}${cell.row}`) ?? "",
2978
3041
  row: cell.row,
2979
3042
  col: cell.col,
@@ -3011,6 +3074,7 @@ var Spreadsheet = forwardRef(({
3011
3074
  const isTextBox = child.component === "RichText";
3012
3075
  const isMinimized = Boolean(child.minimized && !isTextBox);
3013
3076
  return /* @__PURE__ */ jsxs2("div", {
3077
+ "data-rsp-overlay": child.id,
3014
3078
  style: {
3015
3079
  position: "absolute",
3016
3080
  width: child.width,
@@ -3807,11 +3871,11 @@ function FormulaSheet({
3807
3871
  });
3808
3872
  }
3809
3873
  }),
3810
- spreadsheetSelectionManager.observeStateChange((state2) => {
3811
- const currentSelection = state2.selections.length === 1 ? state2.selections[0] : undefined;
3874
+ spreadsheetSelectionManager.observeStateChange((state) => {
3875
+ const currentSelection = state.selections.length === 1 ? state.selections[0] : undefined;
3812
3876
  return currentSelection;
3813
- }, (selection2) => {
3814
- setSelectedArea(selection2 ?? null);
3877
+ }, (selection) => {
3878
+ setSelectedArea(selection ?? null);
3815
3879
  })
3816
3880
  ];
3817
3881
  const handleUndoRedoKeyDown = (event) => {
@@ -3927,7 +3991,7 @@ function FormulaSheet({
3927
3991
  workbookName
3928
3992
  });
3929
3993
  if (!tableInfo) {
3930
- const style2 = conditionalStyle ? {
3994
+ const style = conditionalStyle ? {
3931
3995
  ...conditionalStyle.backgroundColor && {
3932
3996
  backgroundColor: conditionalStyle.backgroundColor
3933
3997
  },
@@ -3949,7 +4013,7 @@ function FormulaSheet({
3949
4013
  ...getBorderCellStyle(conditionalStyle),
3950
4014
  ...getWrapCellStyle(conditionalStyle)
3951
4015
  } : {};
3952
- return customCellStyle ? customCellStyle(cell, style2) : style2;
4016
+ return customCellStyle ? customCellStyle(cell, style) : style;
3953
4017
  }
3954
4018
  const isHeaderRow = cell.rowIndex === tableInfo.start.rowIndex;
3955
4019
  const isFirstColumn = cell.colIndex === tableInfo.start.colIndex;
@@ -4499,4 +4563,4 @@ export {
4499
4563
  useResizeObserver
4500
4564
  };
4501
4565
 
4502
- //# debugId=27095BB366C4630864756E2164756E21
4566
+ //# debugId=FA27216279C90A7964756E2164756E21