@lensui/lens-table 0.1.74 → 0.1.76

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
@@ -335,11 +335,15 @@ var import_jsx_runtime3 = require("react/jsx-runtime");
335
335
  function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownHeight, style, labels, onChange, onCommit, onCancel }) {
336
336
  const editorRef = (0, import_react2.useRef)(null);
337
337
  const popupRef = (0, import_react2.useRef)(null);
338
+ const activeOptionRef = (0, import_react2.useRef)(null);
338
339
  const originalValueRef = (0, import_react2.useRef)(value);
339
340
  const listboxId = (0, import_react2.useId)();
340
341
  const originalValue = originalValueRef.current;
341
342
  const [inputValue, setInputValue] = (0, import_react2.useState)(value);
342
- const [activeIndex, setActiveIndex] = (0, import_react2.useState)(0);
343
+ const [activeIndex, setActiveIndex] = (0, import_react2.useState)(() => {
344
+ const selectedIndex = options.findIndex((option) => option.value === value);
345
+ return selectedIndex >= 0 ? selectedIndex : 0;
346
+ });
343
347
  const [popupStyle, setPopupStyle] = (0, import_react2.useState)({});
344
348
  const filteredOptions = (0, import_react2.useMemo)(() => {
345
349
  const query = inputValue.trim().toLocaleLowerCase();
@@ -378,6 +382,9 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
378
382
  window.removeEventListener("scroll", positionPopup, true);
379
383
  };
380
384
  }, [maxDropdownHeight]);
385
+ (0, import_react2.useLayoutEffect)(() => {
386
+ activeOptionRef.current?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
387
+ }, [activeIndex, filteredOptions, popupStyle]);
381
388
  (0, import_react2.useEffect)(() => {
382
389
  const handleOutsidePointer = (event) => {
383
390
  const target = event.target;
@@ -432,6 +439,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
432
439
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_portal.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: popupRef, id: listboxId, role: "listbox", className: "rvg-choice-content rvg-searchable-choice-content", style: popupStyle, children: filteredOptions.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
433
440
  "button",
434
441
  {
442
+ ref: index === activeIndex ? activeOptionRef : void 0,
435
443
  type: "button",
436
444
  role: "option",
437
445
  "aria-selected": option.value === originalValue,
@@ -1106,6 +1114,7 @@ function paintGrid(options) {
1106
1114
  const headerLeafTop = options.headerLeafTop ?? 0;
1107
1115
  const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
1108
1116
  const colors = { ...COLORS, ...options.colors };
1117
+ const resolveColor = options.resolveColor ?? ((color) => color);
1109
1118
  const getAxisSelectionFill = (rowIndex, columnKey) => selectedColumnKeys.has(columnKey) && striped && rowIndex % 2 === 1 ? colors.axisSelectionStripeFill : colors.axisSelectionFill;
1110
1119
  const bodyTop = options.bodyTop ?? headerHeight;
1111
1120
  const getCellSpan = (rowIndex, columnIndex) => options.cellSpans.get(getCellCoordKey(rowIndex, columnIndex));
@@ -1169,8 +1178,9 @@ function paintGrid(options) {
1169
1178
  const trailingRowStyle = options.rowStyle?.(row, rowIndex);
1170
1179
  const y = bodyTop + rowIndex * rowHeight - scrollTop + getRowDragOffset(rowIndex);
1171
1180
  const paintTrailingLayer = (fill) => {
1172
- if (!fill) return;
1173
- ctx.fillStyle = fill;
1181
+ const resolvedFill = resolveColor(fill);
1182
+ if (!resolvedFill) return;
1183
+ ctx.fillStyle = resolvedFill;
1174
1184
  ctx.fillRect(trailingBlankStart, y, width - trailingBlankStart, rowHeight);
1175
1185
  };
1176
1186
  if (striped && rowIndex % 2 === 1) paintTrailingLayer(colors.stripe);
@@ -1213,8 +1223,11 @@ function paintGrid(options) {
1213
1223
  ctx.fillRect(x, y, cellWidth, cellHeight);
1214
1224
  }
1215
1225
  if (rowStyle?.backgroundColor) {
1216
- ctx.fillStyle = rowStyle.backgroundColor;
1217
- ctx.fillRect(x, y, cellWidth, cellHeight);
1226
+ const rowBackground = resolveColor(rowStyle.backgroundColor);
1227
+ if (rowBackground) {
1228
+ ctx.fillStyle = rowBackground;
1229
+ ctx.fillRect(x, y, cellWidth, cellHeight);
1230
+ }
1218
1231
  }
1219
1232
  if (isAxisSelected) {
1220
1233
  ctx.fillStyle = getAxisSelectionFill(rowIndex, column.key);
@@ -1233,8 +1246,11 @@ function paintGrid(options) {
1233
1246
  const rawValue = column.dataIndex === void 0 ? void 0 : row[column.dataIndex];
1234
1247
  const cellStyle = column.cellStyle?.(rawValue, row, rowIndex);
1235
1248
  if (!isSelectedCell && !isRangeCell && cellStyle?.backgroundColor) {
1236
- ctx.fillStyle = cellStyle.backgroundColor;
1237
- ctx.fillRect(x, y, cellWidth, cellHeight);
1249
+ const cellBackground = resolveColor(cellStyle.backgroundColor);
1250
+ if (cellBackground) {
1251
+ ctx.fillStyle = cellBackground;
1252
+ ctx.fillRect(x, y, cellWidth, cellHeight);
1253
+ }
1238
1254
  }
1239
1255
  if (highlightEditedCells && !insertedRowKeys.has(rowKey) && !isSelectedCell && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`)) {
1240
1256
  ctx.fillStyle = colors.editedFill;
@@ -1245,8 +1261,11 @@ function paintGrid(options) {
1245
1261
  ctx.fillRect(x, y, cellWidth, cellHeight);
1246
1262
  }
1247
1263
  if (annotation?.type === "background") {
1248
- ctx.fillStyle = annotation.color;
1249
- ctx.fillRect(x, y, cellWidth, cellHeight);
1264
+ const annotationBackground = resolveColor(annotation.color);
1265
+ if (annotationBackground) {
1266
+ ctx.fillStyle = annotationBackground;
1267
+ ctx.fillRect(x, y, cellWidth, cellHeight);
1268
+ }
1250
1269
  }
1251
1270
  if (isColumnDropTarget) {
1252
1271
  ctx.fillStyle = isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : colors.columnDropTargetFill;
@@ -1262,14 +1281,14 @@ function paintGrid(options) {
1262
1281
  }
1263
1282
  if (rowSpan > 1) {
1264
1283
  const mergedFill = isColumnDropTarget ? isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : colors.columnDropTargetFill : annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : cellStyle?.backgroundColor ?? rowStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1265
- ctx.fillStyle = mergedFill;
1284
+ ctx.fillStyle = resolveColor(mergedFill) ?? colors.background;
1266
1285
  for (let offset = 1; offset < rowSpan; offset += 1) {
1267
1286
  ctx.fillRect(x, y + offset * rowHeight - 1, cellWidth, 2);
1268
1287
  }
1269
1288
  }
1270
1289
  if (colSpan > 1 && !verticalBorderless) {
1271
1290
  const mergedFill = isColumnDropTarget ? isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : colors.columnDropTargetFill : annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : cellStyle?.backgroundColor ?? rowStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1272
- ctx.fillStyle = mergedFill;
1291
+ ctx.fillStyle = resolveColor(mergedFill) ?? colors.background;
1273
1292
  let offsetLeft = 0;
1274
1293
  for (let offset = 1; offset < colSpan; offset += 1) {
1275
1294
  offsetLeft += metrics[columnIndex + offset - 1]?.width ?? 0;
@@ -1365,7 +1384,8 @@ function paintGrid(options) {
1365
1384
  ctx.beginPath();
1366
1385
  ctx.rect(x + 2, y + 1, Math.max(0, cellWidth - 4), cellHeight - 2);
1367
1386
  ctx.clip();
1368
- ctx.fillStyle = isAxisSelected ? colors.axisSelectionText : isColumnDropTarget ? colors.text : cellStyle?.color ?? rowStyle?.color ?? colors.text;
1387
+ const textColor = isAxisSelected ? colors.axisSelectionText : isColumnDropTarget ? colors.text : resolveColor(cellStyle?.color ?? rowStyle?.color) ?? colors.text;
1388
+ ctx.fillStyle = textColor;
1369
1389
  ctx.font = createCellFont(bodyFontSize);
1370
1390
  ctx.textBaseline = "alphabetic";
1371
1391
  ctx.textAlign = column.align === "right" ? "right" : column.align === "center" ? "center" : "left";
@@ -1375,7 +1395,7 @@ function paintGrid(options) {
1375
1395
  }
1376
1396
  if (annotation?.type === "corner") {
1377
1397
  const cornerTop = y - 1;
1378
- ctx.fillStyle = annotation.color;
1398
+ ctx.fillStyle = resolveColor(annotation.color) ?? colors.text;
1379
1399
  ctx.beginPath();
1380
1400
  ctx.moveTo(x + cellWidth - 10, cornerTop);
1381
1401
  ctx.lineTo(x + cellWidth, cornerTop);
@@ -1761,6 +1781,18 @@ function readThemePixel(styles, name, fallback) {
1761
1781
  const value = Number.parseFloat(styles.getPropertyValue(name));
1762
1782
  return Number.isFinite(value) && value > 0 ? value : fallback;
1763
1783
  }
1784
+ function resolveCanvasColor(styles, color) {
1785
+ if (!color) return void 0;
1786
+ let resolved = color.trim();
1787
+ for (let depth = 0; depth < 8; depth += 1) {
1788
+ const variable = resolved.match(/^var\(\s*(--[^,\s)]+)\s*(?:,\s*(.+))?\)$/);
1789
+ if (!variable) return resolved || void 0;
1790
+ const value = styles.getPropertyValue(variable[1]).trim();
1791
+ resolved = value || variable[2]?.trim() || "";
1792
+ if (!resolved) return void 0;
1793
+ }
1794
+ return void 0;
1795
+ }
1764
1796
  function getDefaultRowKey(row, index) {
1765
1797
  const id = row.id;
1766
1798
  return typeof id === "string" || typeof id === "number" ? id : index;
@@ -1997,6 +2029,7 @@ function TableInner({
1997
2029
  const rootRef = (0, import_react8.useRef)(null);
1998
2030
  const frameRef = (0, import_react8.useRef)(null);
1999
2031
  const textFrameRef = (0, import_react8.useRef)(null);
2032
+ const textLayoutScrollTopRef = (0, import_react8.useRef)(0);
2000
2033
  const toastTimerRef = (0, import_react8.useRef)(null);
2001
2034
  const headerTooltipTimerRef = (0, import_react8.useRef)(null);
2002
2035
  const cellTooltipTimerRef = (0, import_react8.useRef)(null);
@@ -2581,7 +2614,7 @@ function TableInner({
2581
2614
  const scroll = scrollRef.current;
2582
2615
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2583
2616
  const range = isVirtualized ? getViewportRange(scroll.left, rowScrollTop, viewport.width, bodyViewportHeight, rows.length, rowHeight, metrics, virtualOverscan) : { rowStart: 0, rowEnd: rows.length, columnStart: 0, columnEnd: columns.length };
2584
- paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, horizontalBorderless, frameBorderless, extendVerticalGridLines: !autoHeight, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, rowStyle, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, rowSelectionMode, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget, colors: themeColors });
2617
+ paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, horizontalBorderless, frameBorderless, extendVerticalGridLines: !autoHeight, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, rowStyle, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, rowSelectionMode, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget, resolveColor: (color) => resolveCanvasColor(themeStyles, color), colors: themeColors });
2585
2618
  }, [autoHeight, bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDragPreview, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, frameBorderless, getRowKey, hasStripedRows, hasVerticalBorders, headerActionSlotWidth, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, horizontalBorderless, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rowStyle, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2586
2619
  const scheduleDraw = (0, import_react8.useCallback)(() => {
2587
2620
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
@@ -3034,6 +3067,13 @@ function TableInner({
3034
3067
  horizontalScrollbarDragRef.current = null;
3035
3068
  showCustomScrollbar("horizontal");
3036
3069
  }, [showCustomScrollbar]);
3070
+ (0, import_react8.useLayoutEffect)(() => {
3071
+ textLayoutScrollTopRef.current = scrollPosition.top;
3072
+ const clips = rootRef.current?.querySelectorAll(".rvg-body-text-clip");
3073
+ clips?.forEach((clip) => {
3074
+ clip.style.transform = "";
3075
+ });
3076
+ }, [scrollPosition.top]);
3037
3077
  const handleScroll = (0, import_react8.useCallback)(() => {
3038
3078
  const element = scrollerRef.current;
3039
3079
  if (!element) return;
@@ -3041,6 +3081,13 @@ function TableInner({
3041
3081
  const scrollDeltaX = element.scrollLeft - previousScroll.left;
3042
3082
  const scrollDeltaY = element.scrollTop - previousScroll.top;
3043
3083
  scrollRef.current = { left: element.scrollLeft, top: element.scrollTop };
3084
+ const textDeltaY = element.scrollTop - textLayoutScrollTopRef.current;
3085
+ if (textDeltaY !== 0) {
3086
+ const clips = rootRef.current?.querySelectorAll(".rvg-body-text-clip");
3087
+ clips?.forEach((clip) => {
3088
+ clip.style.transform = `translate3d(0, ${-textDeltaY}px, 0)`;
3089
+ });
3090
+ }
3044
3091
  syncSelectionEdgeTarget(element.scrollLeft, element.scrollTop);
3045
3092
  const focus = selectionFocusRef.current;
3046
3093
  if (focus && selection && !selectionRange) {
package/dist/index.css CHANGED
@@ -183,6 +183,7 @@
183
183
  bottom: 0;
184
184
  overflow: hidden;
185
185
  pointer-events: none;
186
+ will-change: transform;
186
187
  }
187
188
  .rvg-text-scroll-clip {
188
189
  position: absolute;
package/dist/index.js CHANGED
@@ -305,11 +305,15 @@ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
305
305
  function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownHeight, style, labels, onChange, onCommit, onCancel }) {
306
306
  const editorRef = useRef(null);
307
307
  const popupRef = useRef(null);
308
+ const activeOptionRef = useRef(null);
308
309
  const originalValueRef = useRef(value);
309
310
  const listboxId = useId2();
310
311
  const originalValue = originalValueRef.current;
311
312
  const [inputValue, setInputValue] = useState(value);
312
- const [activeIndex, setActiveIndex] = useState(0);
313
+ const [activeIndex, setActiveIndex] = useState(() => {
314
+ const selectedIndex = options.findIndex((option) => option.value === value);
315
+ return selectedIndex >= 0 ? selectedIndex : 0;
316
+ });
313
317
  const [popupStyle, setPopupStyle] = useState({});
314
318
  const filteredOptions = useMemo(() => {
315
319
  const query = inputValue.trim().toLocaleLowerCase();
@@ -348,6 +352,9 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
348
352
  window.removeEventListener("scroll", positionPopup, true);
349
353
  };
350
354
  }, [maxDropdownHeight]);
355
+ useLayoutEffect(() => {
356
+ activeOptionRef.current?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
357
+ }, [activeIndex, filteredOptions, popupStyle]);
351
358
  useEffect(() => {
352
359
  const handleOutsidePointer = (event) => {
353
360
  const target = event.target;
@@ -402,6 +409,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
402
409
  /* @__PURE__ */ jsx3(Portal, { children: /* @__PURE__ */ jsx3("div", { ref: popupRef, id: listboxId, role: "listbox", className: "rvg-choice-content rvg-searchable-choice-content", style: popupStyle, children: filteredOptions.map((option, index) => /* @__PURE__ */ jsxs3(
403
410
  "button",
404
411
  {
412
+ ref: index === activeIndex ? activeOptionRef : void 0,
405
413
  type: "button",
406
414
  role: "option",
407
415
  "aria-selected": option.value === originalValue,
@@ -1076,6 +1084,7 @@ function paintGrid(options) {
1076
1084
  const headerLeafTop = options.headerLeafTop ?? 0;
1077
1085
  const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
1078
1086
  const colors = { ...COLORS, ...options.colors };
1087
+ const resolveColor = options.resolveColor ?? ((color) => color);
1079
1088
  const getAxisSelectionFill = (rowIndex, columnKey) => selectedColumnKeys.has(columnKey) && striped && rowIndex % 2 === 1 ? colors.axisSelectionStripeFill : colors.axisSelectionFill;
1080
1089
  const bodyTop = options.bodyTop ?? headerHeight;
1081
1090
  const getCellSpan = (rowIndex, columnIndex) => options.cellSpans.get(getCellCoordKey(rowIndex, columnIndex));
@@ -1139,8 +1148,9 @@ function paintGrid(options) {
1139
1148
  const trailingRowStyle = options.rowStyle?.(row, rowIndex);
1140
1149
  const y = bodyTop + rowIndex * rowHeight - scrollTop + getRowDragOffset(rowIndex);
1141
1150
  const paintTrailingLayer = (fill) => {
1142
- if (!fill) return;
1143
- ctx.fillStyle = fill;
1151
+ const resolvedFill = resolveColor(fill);
1152
+ if (!resolvedFill) return;
1153
+ ctx.fillStyle = resolvedFill;
1144
1154
  ctx.fillRect(trailingBlankStart, y, width - trailingBlankStart, rowHeight);
1145
1155
  };
1146
1156
  if (striped && rowIndex % 2 === 1) paintTrailingLayer(colors.stripe);
@@ -1183,8 +1193,11 @@ function paintGrid(options) {
1183
1193
  ctx.fillRect(x, y, cellWidth, cellHeight);
1184
1194
  }
1185
1195
  if (rowStyle?.backgroundColor) {
1186
- ctx.fillStyle = rowStyle.backgroundColor;
1187
- ctx.fillRect(x, y, cellWidth, cellHeight);
1196
+ const rowBackground = resolveColor(rowStyle.backgroundColor);
1197
+ if (rowBackground) {
1198
+ ctx.fillStyle = rowBackground;
1199
+ ctx.fillRect(x, y, cellWidth, cellHeight);
1200
+ }
1188
1201
  }
1189
1202
  if (isAxisSelected) {
1190
1203
  ctx.fillStyle = getAxisSelectionFill(rowIndex, column.key);
@@ -1203,8 +1216,11 @@ function paintGrid(options) {
1203
1216
  const rawValue = column.dataIndex === void 0 ? void 0 : row[column.dataIndex];
1204
1217
  const cellStyle = column.cellStyle?.(rawValue, row, rowIndex);
1205
1218
  if (!isSelectedCell && !isRangeCell && cellStyle?.backgroundColor) {
1206
- ctx.fillStyle = cellStyle.backgroundColor;
1207
- ctx.fillRect(x, y, cellWidth, cellHeight);
1219
+ const cellBackground = resolveColor(cellStyle.backgroundColor);
1220
+ if (cellBackground) {
1221
+ ctx.fillStyle = cellBackground;
1222
+ ctx.fillRect(x, y, cellWidth, cellHeight);
1223
+ }
1208
1224
  }
1209
1225
  if (highlightEditedCells && !insertedRowKeys.has(rowKey) && !isSelectedCell && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`)) {
1210
1226
  ctx.fillStyle = colors.editedFill;
@@ -1215,8 +1231,11 @@ function paintGrid(options) {
1215
1231
  ctx.fillRect(x, y, cellWidth, cellHeight);
1216
1232
  }
1217
1233
  if (annotation?.type === "background") {
1218
- ctx.fillStyle = annotation.color;
1219
- ctx.fillRect(x, y, cellWidth, cellHeight);
1234
+ const annotationBackground = resolveColor(annotation.color);
1235
+ if (annotationBackground) {
1236
+ ctx.fillStyle = annotationBackground;
1237
+ ctx.fillRect(x, y, cellWidth, cellHeight);
1238
+ }
1220
1239
  }
1221
1240
  if (isColumnDropTarget) {
1222
1241
  ctx.fillStyle = isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : colors.columnDropTargetFill;
@@ -1232,14 +1251,14 @@ function paintGrid(options) {
1232
1251
  }
1233
1252
  if (rowSpan > 1) {
1234
1253
  const mergedFill = isColumnDropTarget ? isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : colors.columnDropTargetFill : annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : cellStyle?.backgroundColor ?? rowStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1235
- ctx.fillStyle = mergedFill;
1254
+ ctx.fillStyle = resolveColor(mergedFill) ?? colors.background;
1236
1255
  for (let offset = 1; offset < rowSpan; offset += 1) {
1237
1256
  ctx.fillRect(x, y + offset * rowHeight - 1, cellWidth, 2);
1238
1257
  }
1239
1258
  }
1240
1259
  if (colSpan > 1 && !verticalBorderless) {
1241
1260
  const mergedFill = isColumnDropTarget ? isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : colors.columnDropTargetFill : annotation?.type === "background" ? annotation.color : isSelectedCell || isRangeCell ? colors.selectionFill : highlightEditedCells && !insertedRowKeys.has(rowKey) && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`) ? colors.editedFill : highlightInsertedRows && insertedRowKeys.has(rowKey) ? colors.insertedFill : hoveredRowIndex === rowIndex ? colors.rowHoverFill : isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : cellStyle?.backgroundColor ?? rowStyle?.backgroundColor ?? (striped && rowIndex % 2 === 1 ? colors.stripe : colors.background);
1242
- ctx.fillStyle = mergedFill;
1261
+ ctx.fillStyle = resolveColor(mergedFill) ?? colors.background;
1243
1262
  let offsetLeft = 0;
1244
1263
  for (let offset = 1; offset < colSpan; offset += 1) {
1245
1264
  offsetLeft += metrics[columnIndex + offset - 1]?.width ?? 0;
@@ -1335,7 +1354,8 @@ function paintGrid(options) {
1335
1354
  ctx.beginPath();
1336
1355
  ctx.rect(x + 2, y + 1, Math.max(0, cellWidth - 4), cellHeight - 2);
1337
1356
  ctx.clip();
1338
- ctx.fillStyle = isAxisSelected ? colors.axisSelectionText : isColumnDropTarget ? colors.text : cellStyle?.color ?? rowStyle?.color ?? colors.text;
1357
+ const textColor = isAxisSelected ? colors.axisSelectionText : isColumnDropTarget ? colors.text : resolveColor(cellStyle?.color ?? rowStyle?.color) ?? colors.text;
1358
+ ctx.fillStyle = textColor;
1339
1359
  ctx.font = createCellFont(bodyFontSize);
1340
1360
  ctx.textBaseline = "alphabetic";
1341
1361
  ctx.textAlign = column.align === "right" ? "right" : column.align === "center" ? "center" : "left";
@@ -1345,7 +1365,7 @@ function paintGrid(options) {
1345
1365
  }
1346
1366
  if (annotation?.type === "corner") {
1347
1367
  const cornerTop = y - 1;
1348
- ctx.fillStyle = annotation.color;
1368
+ ctx.fillStyle = resolveColor(annotation.color) ?? colors.text;
1349
1369
  ctx.beginPath();
1350
1370
  ctx.moveTo(x + cellWidth - 10, cornerTop);
1351
1371
  ctx.lineTo(x + cellWidth, cornerTop);
@@ -1731,6 +1751,18 @@ function readThemePixel(styles, name, fallback) {
1731
1751
  const value = Number.parseFloat(styles.getPropertyValue(name));
1732
1752
  return Number.isFinite(value) && value > 0 ? value : fallback;
1733
1753
  }
1754
+ function resolveCanvasColor(styles, color) {
1755
+ if (!color) return void 0;
1756
+ let resolved = color.trim();
1757
+ for (let depth = 0; depth < 8; depth += 1) {
1758
+ const variable = resolved.match(/^var\(\s*(--[^,\s)]+)\s*(?:,\s*(.+))?\)$/);
1759
+ if (!variable) return resolved || void 0;
1760
+ const value = styles.getPropertyValue(variable[1]).trim();
1761
+ resolved = value || variable[2]?.trim() || "";
1762
+ if (!resolved) return void 0;
1763
+ }
1764
+ return void 0;
1765
+ }
1734
1766
  function getDefaultRowKey(row, index) {
1735
1767
  const id = row.id;
1736
1768
  return typeof id === "string" || typeof id === "number" ? id : index;
@@ -1967,6 +1999,7 @@ function TableInner({
1967
1999
  const rootRef = useRef4(null);
1968
2000
  const frameRef = useRef4(null);
1969
2001
  const textFrameRef = useRef4(null);
2002
+ const textLayoutScrollTopRef = useRef4(0);
1970
2003
  const toastTimerRef = useRef4(null);
1971
2004
  const headerTooltipTimerRef = useRef4(null);
1972
2005
  const cellTooltipTimerRef = useRef4(null);
@@ -2551,7 +2584,7 @@ function TableInner({
2551
2584
  const scroll = scrollRef.current;
2552
2585
  const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
2553
2586
  const range = isVirtualized ? getViewportRange(scroll.left, rowScrollTop, viewport.width, bodyViewportHeight, rows.length, rowHeight, metrics, virtualOverscan) : { rowStart: 0, rowEnd: rows.length, columnStart: 0, columnEnd: columns.length };
2554
- paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, horizontalBorderless, frameBorderless, extendVerticalGridLines: !autoHeight, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, rowStyle, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, rowSelectionMode, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget, colors: themeColors });
2587
+ paintGrid({ context, width: viewport.width, height: renderHeight, pixelRatio: ratio, scrollLeft: scroll.left, scrollTop: scroll.top, rowHeight, bodyFontSize, headerActionSlotWidth, headerHeight, headerLeafTop, headerLeafHeight, bodyTop, suppressLastRowBottomBorder: bottomSummaryHeight > 0, suppressFrameBottomBorder: bottomSummaryHeight > 0, fixedHeader, verticalBorderless: !hasVerticalBorders, horizontalBorderless, frameBorderless, extendVerticalGridLines: !autoHeight, striped: hasStripedRows, columnDraggable, sortState, filterValues, hoveredHeaderAction: hoveredHeaderActionRef.current, rows, rowStyle, columns, metrics, range, selection, editing, hoveredRowIndex: hoveredRowIndexRef.current, selectionRange, selectedRowKeys: selectedRowKeySet, rowSelectionMode, selectedColumnKeys: selectedColumnKeySet, cellSpans: cellSpanLookup.covered, maxRowSpan: cellSpanLookup.maxRowSpan, highlightEditedCells: highlightsEditedCells, highlightInsertedRows: highlightsInsertedRows, insertedRowKeys: insertedRowKeySet, editedCellKeys, cellAnnotations, getRowKey, rowDragPreview, columnDropTarget, resolveColor: (color) => resolveCanvasColor(themeStyles, color), colors: themeColors });
2555
2588
  }, [autoHeight, bodyTop, bodyViewportHeight, bottomSummaryHeight, cellAnnotations, cellSpanLookup, columnDragPreview, columnDraggable, columnDropTarget, columns, editedCellHighlightColor, editedCellKeys, editing, renderHeight, filterValues, fixedHeader, frameBorderless, getRowKey, hasStripedRows, hasVerticalBorders, headerActionSlotWidth, headerDepth, headerHeight, headerLeafHeight, headerLeafTop, horizontalBorderless, highlightsEditedCells, highlightsInsertedRows, insertedRowHighlightColor, insertedRowKeySet, isVirtualized, metrics, rowDragPreview, rowHeight, rowSelectionMode, rowStyle, rows, selectedColumnKeySet, selectedRowKeySet, selection, selectionRange, sortState, stripedColor, viewport.width, virtualOverscan]);
2556
2589
  const scheduleDraw = useCallback2(() => {
2557
2590
  if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
@@ -3004,6 +3037,13 @@ function TableInner({
3004
3037
  horizontalScrollbarDragRef.current = null;
3005
3038
  showCustomScrollbar("horizontal");
3006
3039
  }, [showCustomScrollbar]);
3040
+ useLayoutEffect3(() => {
3041
+ textLayoutScrollTopRef.current = scrollPosition.top;
3042
+ const clips = rootRef.current?.querySelectorAll(".rvg-body-text-clip");
3043
+ clips?.forEach((clip) => {
3044
+ clip.style.transform = "";
3045
+ });
3046
+ }, [scrollPosition.top]);
3007
3047
  const handleScroll = useCallback2(() => {
3008
3048
  const element = scrollerRef.current;
3009
3049
  if (!element) return;
@@ -3011,6 +3051,13 @@ function TableInner({
3011
3051
  const scrollDeltaX = element.scrollLeft - previousScroll.left;
3012
3052
  const scrollDeltaY = element.scrollTop - previousScroll.top;
3013
3053
  scrollRef.current = { left: element.scrollLeft, top: element.scrollTop };
3054
+ const textDeltaY = element.scrollTop - textLayoutScrollTopRef.current;
3055
+ if (textDeltaY !== 0) {
3056
+ const clips = rootRef.current?.querySelectorAll(".rvg-body-text-clip");
3057
+ clips?.forEach((clip) => {
3058
+ clip.style.transform = `translate3d(0, ${-textDeltaY}px, 0)`;
3059
+ });
3060
+ }
3014
3061
  syncSelectionEdgeTarget(element.scrollLeft, element.scrollTop);
3015
3062
  const focus = selectionFocusRef.current;
3016
3063
  if (focus && selection && !selectionRange) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensui/lens-table",
3
- "version": "0.1.74",
3
+ "version": "0.1.76",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",