@lensui/lens-table 0.1.75 → 0.1.77
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 +77 -18
- package/dist/index.js +77 -18
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -335,12 +335,21 @@ 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);
|
|
339
|
+
const popupReadyRef = (0, import_react2.useRef)(false);
|
|
340
|
+
const navigationSourceRef = (0, import_react2.useRef)(null);
|
|
338
341
|
const originalValueRef = (0, import_react2.useRef)(value);
|
|
339
342
|
const listboxId = (0, import_react2.useId)();
|
|
340
343
|
const originalValue = originalValueRef.current;
|
|
341
344
|
const [inputValue, setInputValue] = (0, import_react2.useState)(value);
|
|
342
|
-
const [activeIndex, setActiveIndex] = (0, import_react2.useState)(
|
|
343
|
-
|
|
345
|
+
const [activeIndex, setActiveIndex] = (0, import_react2.useState)(() => {
|
|
346
|
+
const selectedIndex = options.findIndex((option) => option.value === value);
|
|
347
|
+
return selectedIndex >= 0 ? selectedIndex : 0;
|
|
348
|
+
});
|
|
349
|
+
const [popupStyle, setPopupStyle] = (0, import_react2.useState)({
|
|
350
|
+
position: "fixed",
|
|
351
|
+
visibility: "hidden"
|
|
352
|
+
});
|
|
344
353
|
const filteredOptions = (0, import_react2.useMemo)(() => {
|
|
345
354
|
const query = inputValue.trim().toLocaleLowerCase();
|
|
346
355
|
if (!query || query === originalValue.toLocaleLowerCase()) return options;
|
|
@@ -358,7 +367,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
358
367
|
bottom: window.innerHeight - rect.top + 4,
|
|
359
368
|
left: rect.left,
|
|
360
369
|
width: rect.width,
|
|
361
|
-
maxHeight: Math.min(maxDropdownHeight, availableAbove)
|
|
370
|
+
maxHeight: Math.min(maxDropdownHeight, availableAbove),
|
|
371
|
+
visibility: popupReadyRef.current ? "visible" : "hidden"
|
|
362
372
|
});
|
|
363
373
|
return;
|
|
364
374
|
}
|
|
@@ -367,7 +377,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
367
377
|
top: rect.bottom + 4,
|
|
368
378
|
left: rect.left,
|
|
369
379
|
width: rect.width,
|
|
370
|
-
maxHeight: Math.min(maxDropdownHeight, Math.max(80, availableBelow))
|
|
380
|
+
maxHeight: Math.min(maxDropdownHeight, Math.max(80, availableBelow)),
|
|
381
|
+
visibility: popupReadyRef.current ? "visible" : "hidden"
|
|
371
382
|
});
|
|
372
383
|
};
|
|
373
384
|
positionPopup();
|
|
@@ -378,6 +389,23 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
378
389
|
window.removeEventListener("scroll", positionPopup, true);
|
|
379
390
|
};
|
|
380
391
|
}, [maxDropdownHeight]);
|
|
392
|
+
(0, import_react2.useLayoutEffect)(() => {
|
|
393
|
+
if (popupReadyRef.current) return;
|
|
394
|
+
if (popupStyle.top === void 0 && popupStyle.bottom === void 0) return;
|
|
395
|
+
const selectedOption = popupRef.current?.querySelector('[aria-selected="true"]') ?? activeOptionRef.current;
|
|
396
|
+
selectedOption?.scrollIntoView?.({ block: "center", inline: "nearest" });
|
|
397
|
+
popupReadyRef.current = true;
|
|
398
|
+
setPopupStyle((current) => ({ ...current, visibility: "visible" }));
|
|
399
|
+
}, [popupStyle.bottom, popupStyle.top]);
|
|
400
|
+
(0, import_react2.useLayoutEffect)(() => {
|
|
401
|
+
if (!popupReadyRef.current) return;
|
|
402
|
+
if (navigationSourceRef.current === "keyboard") {
|
|
403
|
+
activeOptionRef.current?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
|
|
404
|
+
} else if (navigationSourceRef.current === "filter" && popupRef.current) {
|
|
405
|
+
popupRef.current.scrollTop = 0;
|
|
406
|
+
}
|
|
407
|
+
navigationSourceRef.current = null;
|
|
408
|
+
}, [activeIndex, filteredOptions]);
|
|
381
409
|
(0, import_react2.useEffect)(() => {
|
|
382
410
|
const handleOutsidePointer = (event) => {
|
|
383
411
|
const target = event.target;
|
|
@@ -397,10 +425,12 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
397
425
|
if (event.key === "Escape") onCancel();
|
|
398
426
|
if (event.key === "ArrowDown") {
|
|
399
427
|
event.preventDefault();
|
|
428
|
+
navigationSourceRef.current = "keyboard";
|
|
400
429
|
setActiveIndex((index) => Math.min(filteredOptions.length - 1, index + 1));
|
|
401
430
|
}
|
|
402
431
|
if (event.key === "ArrowUp") {
|
|
403
432
|
event.preventDefault();
|
|
433
|
+
navigationSourceRef.current = "keyboard";
|
|
404
434
|
setActiveIndex((index) => Math.max(0, index - 1));
|
|
405
435
|
}
|
|
406
436
|
if (event.key === "Enter") {
|
|
@@ -423,6 +453,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
423
453
|
onChange: (event) => {
|
|
424
454
|
setInputValue(event.target.value);
|
|
425
455
|
onChange(event.target.value);
|
|
456
|
+
navigationSourceRef.current = "filter";
|
|
426
457
|
setActiveIndex(0);
|
|
427
458
|
},
|
|
428
459
|
onKeyDown: handleKeyDown
|
|
@@ -432,12 +463,16 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
432
463
|
/* @__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
464
|
"button",
|
|
434
465
|
{
|
|
466
|
+
ref: index === activeIndex ? activeOptionRef : void 0,
|
|
435
467
|
type: "button",
|
|
436
468
|
role: "option",
|
|
437
469
|
"aria-selected": option.value === originalValue,
|
|
438
470
|
className: `rvg-choice-item${index === activeIndex ? " is-active" : ""}${option.value === originalValue ? " is-selected" : ""}`,
|
|
439
471
|
onPointerDown: (event) => event.preventDefault(),
|
|
440
|
-
onMouseEnter: () =>
|
|
472
|
+
onMouseEnter: () => {
|
|
473
|
+
navigationSourceRef.current = "pointer";
|
|
474
|
+
setActiveIndex(index);
|
|
475
|
+
},
|
|
441
476
|
onClick: () => selectOption(option),
|
|
442
477
|
children: [
|
|
443
478
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: option.label }),
|
|
@@ -1106,6 +1141,7 @@ function paintGrid(options) {
|
|
|
1106
1141
|
const headerLeafTop = options.headerLeafTop ?? 0;
|
|
1107
1142
|
const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
|
|
1108
1143
|
const colors = { ...COLORS, ...options.colors };
|
|
1144
|
+
const resolveColor = options.resolveColor ?? ((color) => color);
|
|
1109
1145
|
const getAxisSelectionFill = (rowIndex, columnKey) => selectedColumnKeys.has(columnKey) && striped && rowIndex % 2 === 1 ? colors.axisSelectionStripeFill : colors.axisSelectionFill;
|
|
1110
1146
|
const bodyTop = options.bodyTop ?? headerHeight;
|
|
1111
1147
|
const getCellSpan = (rowIndex, columnIndex) => options.cellSpans.get(getCellCoordKey(rowIndex, columnIndex));
|
|
@@ -1169,8 +1205,9 @@ function paintGrid(options) {
|
|
|
1169
1205
|
const trailingRowStyle = options.rowStyle?.(row, rowIndex);
|
|
1170
1206
|
const y = bodyTop + rowIndex * rowHeight - scrollTop + getRowDragOffset(rowIndex);
|
|
1171
1207
|
const paintTrailingLayer = (fill) => {
|
|
1172
|
-
|
|
1173
|
-
|
|
1208
|
+
const resolvedFill = resolveColor(fill);
|
|
1209
|
+
if (!resolvedFill) return;
|
|
1210
|
+
ctx.fillStyle = resolvedFill;
|
|
1174
1211
|
ctx.fillRect(trailingBlankStart, y, width - trailingBlankStart, rowHeight);
|
|
1175
1212
|
};
|
|
1176
1213
|
if (striped && rowIndex % 2 === 1) paintTrailingLayer(colors.stripe);
|
|
@@ -1213,8 +1250,11 @@ function paintGrid(options) {
|
|
|
1213
1250
|
ctx.fillRect(x, y, cellWidth, cellHeight);
|
|
1214
1251
|
}
|
|
1215
1252
|
if (rowStyle?.backgroundColor) {
|
|
1216
|
-
|
|
1217
|
-
|
|
1253
|
+
const rowBackground = resolveColor(rowStyle.backgroundColor);
|
|
1254
|
+
if (rowBackground) {
|
|
1255
|
+
ctx.fillStyle = rowBackground;
|
|
1256
|
+
ctx.fillRect(x, y, cellWidth, cellHeight);
|
|
1257
|
+
}
|
|
1218
1258
|
}
|
|
1219
1259
|
if (isAxisSelected) {
|
|
1220
1260
|
ctx.fillStyle = getAxisSelectionFill(rowIndex, column.key);
|
|
@@ -1233,8 +1273,11 @@ function paintGrid(options) {
|
|
|
1233
1273
|
const rawValue = column.dataIndex === void 0 ? void 0 : row[column.dataIndex];
|
|
1234
1274
|
const cellStyle = column.cellStyle?.(rawValue, row, rowIndex);
|
|
1235
1275
|
if (!isSelectedCell && !isRangeCell && cellStyle?.backgroundColor) {
|
|
1236
|
-
|
|
1237
|
-
|
|
1276
|
+
const cellBackground = resolveColor(cellStyle.backgroundColor);
|
|
1277
|
+
if (cellBackground) {
|
|
1278
|
+
ctx.fillStyle = cellBackground;
|
|
1279
|
+
ctx.fillRect(x, y, cellWidth, cellHeight);
|
|
1280
|
+
}
|
|
1238
1281
|
}
|
|
1239
1282
|
if (highlightEditedCells && !insertedRowKeys.has(rowKey) && !isSelectedCell && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`)) {
|
|
1240
1283
|
ctx.fillStyle = colors.editedFill;
|
|
@@ -1245,8 +1288,11 @@ function paintGrid(options) {
|
|
|
1245
1288
|
ctx.fillRect(x, y, cellWidth, cellHeight);
|
|
1246
1289
|
}
|
|
1247
1290
|
if (annotation?.type === "background") {
|
|
1248
|
-
|
|
1249
|
-
|
|
1291
|
+
const annotationBackground = resolveColor(annotation.color);
|
|
1292
|
+
if (annotationBackground) {
|
|
1293
|
+
ctx.fillStyle = annotationBackground;
|
|
1294
|
+
ctx.fillRect(x, y, cellWidth, cellHeight);
|
|
1295
|
+
}
|
|
1250
1296
|
}
|
|
1251
1297
|
if (isColumnDropTarget) {
|
|
1252
1298
|
ctx.fillStyle = isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : colors.columnDropTargetFill;
|
|
@@ -1262,14 +1308,14 @@ function paintGrid(options) {
|
|
|
1262
1308
|
}
|
|
1263
1309
|
if (rowSpan > 1) {
|
|
1264
1310
|
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;
|
|
1311
|
+
ctx.fillStyle = resolveColor(mergedFill) ?? colors.background;
|
|
1266
1312
|
for (let offset = 1; offset < rowSpan; offset += 1) {
|
|
1267
1313
|
ctx.fillRect(x, y + offset * rowHeight - 1, cellWidth, 2);
|
|
1268
1314
|
}
|
|
1269
1315
|
}
|
|
1270
1316
|
if (colSpan > 1 && !verticalBorderless) {
|
|
1271
1317
|
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;
|
|
1318
|
+
ctx.fillStyle = resolveColor(mergedFill) ?? colors.background;
|
|
1273
1319
|
let offsetLeft = 0;
|
|
1274
1320
|
for (let offset = 1; offset < colSpan; offset += 1) {
|
|
1275
1321
|
offsetLeft += metrics[columnIndex + offset - 1]?.width ?? 0;
|
|
@@ -1365,7 +1411,8 @@ function paintGrid(options) {
|
|
|
1365
1411
|
ctx.beginPath();
|
|
1366
1412
|
ctx.rect(x + 2, y + 1, Math.max(0, cellWidth - 4), cellHeight - 2);
|
|
1367
1413
|
ctx.clip();
|
|
1368
|
-
|
|
1414
|
+
const textColor = isAxisSelected ? colors.axisSelectionText : isColumnDropTarget ? colors.text : resolveColor(cellStyle?.color ?? rowStyle?.color) ?? colors.text;
|
|
1415
|
+
ctx.fillStyle = textColor;
|
|
1369
1416
|
ctx.font = createCellFont(bodyFontSize);
|
|
1370
1417
|
ctx.textBaseline = "alphabetic";
|
|
1371
1418
|
ctx.textAlign = column.align === "right" ? "right" : column.align === "center" ? "center" : "left";
|
|
@@ -1375,7 +1422,7 @@ function paintGrid(options) {
|
|
|
1375
1422
|
}
|
|
1376
1423
|
if (annotation?.type === "corner") {
|
|
1377
1424
|
const cornerTop = y - 1;
|
|
1378
|
-
ctx.fillStyle = annotation.color;
|
|
1425
|
+
ctx.fillStyle = resolveColor(annotation.color) ?? colors.text;
|
|
1379
1426
|
ctx.beginPath();
|
|
1380
1427
|
ctx.moveTo(x + cellWidth - 10, cornerTop);
|
|
1381
1428
|
ctx.lineTo(x + cellWidth, cornerTop);
|
|
@@ -1761,6 +1808,18 @@ function readThemePixel(styles, name, fallback) {
|
|
|
1761
1808
|
const value = Number.parseFloat(styles.getPropertyValue(name));
|
|
1762
1809
|
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
1763
1810
|
}
|
|
1811
|
+
function resolveCanvasColor(styles, color) {
|
|
1812
|
+
if (!color) return void 0;
|
|
1813
|
+
let resolved = color.trim();
|
|
1814
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
1815
|
+
const variable = resolved.match(/^var\(\s*(--[^,\s)]+)\s*(?:,\s*(.+))?\)$/);
|
|
1816
|
+
if (!variable) return resolved || void 0;
|
|
1817
|
+
const value = styles.getPropertyValue(variable[1]).trim();
|
|
1818
|
+
resolved = value || variable[2]?.trim() || "";
|
|
1819
|
+
if (!resolved) return void 0;
|
|
1820
|
+
}
|
|
1821
|
+
return void 0;
|
|
1822
|
+
}
|
|
1764
1823
|
function getDefaultRowKey(row, index) {
|
|
1765
1824
|
const id = row.id;
|
|
1766
1825
|
return typeof id === "string" || typeof id === "number" ? id : index;
|
|
@@ -2582,7 +2641,7 @@ function TableInner({
|
|
|
2582
2641
|
const scroll = scrollRef.current;
|
|
2583
2642
|
const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
|
|
2584
2643
|
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 };
|
|
2585
|
-
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 });
|
|
2644
|
+
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 });
|
|
2586
2645
|
}, [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]);
|
|
2587
2646
|
const scheduleDraw = (0, import_react8.useCallback)(() => {
|
|
2588
2647
|
if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
|
package/dist/index.js
CHANGED
|
@@ -305,12 +305,21 @@ 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);
|
|
309
|
+
const popupReadyRef = useRef(false);
|
|
310
|
+
const navigationSourceRef = useRef(null);
|
|
308
311
|
const originalValueRef = useRef(value);
|
|
309
312
|
const listboxId = useId2();
|
|
310
313
|
const originalValue = originalValueRef.current;
|
|
311
314
|
const [inputValue, setInputValue] = useState(value);
|
|
312
|
-
const [activeIndex, setActiveIndex] = useState(
|
|
313
|
-
|
|
315
|
+
const [activeIndex, setActiveIndex] = useState(() => {
|
|
316
|
+
const selectedIndex = options.findIndex((option) => option.value === value);
|
|
317
|
+
return selectedIndex >= 0 ? selectedIndex : 0;
|
|
318
|
+
});
|
|
319
|
+
const [popupStyle, setPopupStyle] = useState({
|
|
320
|
+
position: "fixed",
|
|
321
|
+
visibility: "hidden"
|
|
322
|
+
});
|
|
314
323
|
const filteredOptions = useMemo(() => {
|
|
315
324
|
const query = inputValue.trim().toLocaleLowerCase();
|
|
316
325
|
if (!query || query === originalValue.toLocaleLowerCase()) return options;
|
|
@@ -328,7 +337,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
328
337
|
bottom: window.innerHeight - rect.top + 4,
|
|
329
338
|
left: rect.left,
|
|
330
339
|
width: rect.width,
|
|
331
|
-
maxHeight: Math.min(maxDropdownHeight, availableAbove)
|
|
340
|
+
maxHeight: Math.min(maxDropdownHeight, availableAbove),
|
|
341
|
+
visibility: popupReadyRef.current ? "visible" : "hidden"
|
|
332
342
|
});
|
|
333
343
|
return;
|
|
334
344
|
}
|
|
@@ -337,7 +347,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
337
347
|
top: rect.bottom + 4,
|
|
338
348
|
left: rect.left,
|
|
339
349
|
width: rect.width,
|
|
340
|
-
maxHeight: Math.min(maxDropdownHeight, Math.max(80, availableBelow))
|
|
350
|
+
maxHeight: Math.min(maxDropdownHeight, Math.max(80, availableBelow)),
|
|
351
|
+
visibility: popupReadyRef.current ? "visible" : "hidden"
|
|
341
352
|
});
|
|
342
353
|
};
|
|
343
354
|
positionPopup();
|
|
@@ -348,6 +359,23 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
348
359
|
window.removeEventListener("scroll", positionPopup, true);
|
|
349
360
|
};
|
|
350
361
|
}, [maxDropdownHeight]);
|
|
362
|
+
useLayoutEffect(() => {
|
|
363
|
+
if (popupReadyRef.current) return;
|
|
364
|
+
if (popupStyle.top === void 0 && popupStyle.bottom === void 0) return;
|
|
365
|
+
const selectedOption = popupRef.current?.querySelector('[aria-selected="true"]') ?? activeOptionRef.current;
|
|
366
|
+
selectedOption?.scrollIntoView?.({ block: "center", inline: "nearest" });
|
|
367
|
+
popupReadyRef.current = true;
|
|
368
|
+
setPopupStyle((current) => ({ ...current, visibility: "visible" }));
|
|
369
|
+
}, [popupStyle.bottom, popupStyle.top]);
|
|
370
|
+
useLayoutEffect(() => {
|
|
371
|
+
if (!popupReadyRef.current) return;
|
|
372
|
+
if (navigationSourceRef.current === "keyboard") {
|
|
373
|
+
activeOptionRef.current?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
|
|
374
|
+
} else if (navigationSourceRef.current === "filter" && popupRef.current) {
|
|
375
|
+
popupRef.current.scrollTop = 0;
|
|
376
|
+
}
|
|
377
|
+
navigationSourceRef.current = null;
|
|
378
|
+
}, [activeIndex, filteredOptions]);
|
|
351
379
|
useEffect(() => {
|
|
352
380
|
const handleOutsidePointer = (event) => {
|
|
353
381
|
const target = event.target;
|
|
@@ -367,10 +395,12 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
367
395
|
if (event.key === "Escape") onCancel();
|
|
368
396
|
if (event.key === "ArrowDown") {
|
|
369
397
|
event.preventDefault();
|
|
398
|
+
navigationSourceRef.current = "keyboard";
|
|
370
399
|
setActiveIndex((index) => Math.min(filteredOptions.length - 1, index + 1));
|
|
371
400
|
}
|
|
372
401
|
if (event.key === "ArrowUp") {
|
|
373
402
|
event.preventDefault();
|
|
403
|
+
navigationSourceRef.current = "keyboard";
|
|
374
404
|
setActiveIndex((index) => Math.max(0, index - 1));
|
|
375
405
|
}
|
|
376
406
|
if (event.key === "Enter") {
|
|
@@ -393,6 +423,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
393
423
|
onChange: (event) => {
|
|
394
424
|
setInputValue(event.target.value);
|
|
395
425
|
onChange(event.target.value);
|
|
426
|
+
navigationSourceRef.current = "filter";
|
|
396
427
|
setActiveIndex(0);
|
|
397
428
|
},
|
|
398
429
|
onKeyDown: handleKeyDown
|
|
@@ -402,12 +433,16 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
|
|
|
402
433
|
/* @__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
434
|
"button",
|
|
404
435
|
{
|
|
436
|
+
ref: index === activeIndex ? activeOptionRef : void 0,
|
|
405
437
|
type: "button",
|
|
406
438
|
role: "option",
|
|
407
439
|
"aria-selected": option.value === originalValue,
|
|
408
440
|
className: `rvg-choice-item${index === activeIndex ? " is-active" : ""}${option.value === originalValue ? " is-selected" : ""}`,
|
|
409
441
|
onPointerDown: (event) => event.preventDefault(),
|
|
410
|
-
onMouseEnter: () =>
|
|
442
|
+
onMouseEnter: () => {
|
|
443
|
+
navigationSourceRef.current = "pointer";
|
|
444
|
+
setActiveIndex(index);
|
|
445
|
+
},
|
|
411
446
|
onClick: () => selectOption(option),
|
|
412
447
|
children: [
|
|
413
448
|
/* @__PURE__ */ jsx3("span", { children: option.label }),
|
|
@@ -1076,6 +1111,7 @@ function paintGrid(options) {
|
|
|
1076
1111
|
const headerLeafTop = options.headerLeafTop ?? 0;
|
|
1077
1112
|
const headerLeafHeight = options.headerLeafHeight ?? headerHeight;
|
|
1078
1113
|
const colors = { ...COLORS, ...options.colors };
|
|
1114
|
+
const resolveColor = options.resolveColor ?? ((color) => color);
|
|
1079
1115
|
const getAxisSelectionFill = (rowIndex, columnKey) => selectedColumnKeys.has(columnKey) && striped && rowIndex % 2 === 1 ? colors.axisSelectionStripeFill : colors.axisSelectionFill;
|
|
1080
1116
|
const bodyTop = options.bodyTop ?? headerHeight;
|
|
1081
1117
|
const getCellSpan = (rowIndex, columnIndex) => options.cellSpans.get(getCellCoordKey(rowIndex, columnIndex));
|
|
@@ -1139,8 +1175,9 @@ function paintGrid(options) {
|
|
|
1139
1175
|
const trailingRowStyle = options.rowStyle?.(row, rowIndex);
|
|
1140
1176
|
const y = bodyTop + rowIndex * rowHeight - scrollTop + getRowDragOffset(rowIndex);
|
|
1141
1177
|
const paintTrailingLayer = (fill) => {
|
|
1142
|
-
|
|
1143
|
-
|
|
1178
|
+
const resolvedFill = resolveColor(fill);
|
|
1179
|
+
if (!resolvedFill) return;
|
|
1180
|
+
ctx.fillStyle = resolvedFill;
|
|
1144
1181
|
ctx.fillRect(trailingBlankStart, y, width - trailingBlankStart, rowHeight);
|
|
1145
1182
|
};
|
|
1146
1183
|
if (striped && rowIndex % 2 === 1) paintTrailingLayer(colors.stripe);
|
|
@@ -1183,8 +1220,11 @@ function paintGrid(options) {
|
|
|
1183
1220
|
ctx.fillRect(x, y, cellWidth, cellHeight);
|
|
1184
1221
|
}
|
|
1185
1222
|
if (rowStyle?.backgroundColor) {
|
|
1186
|
-
|
|
1187
|
-
|
|
1223
|
+
const rowBackground = resolveColor(rowStyle.backgroundColor);
|
|
1224
|
+
if (rowBackground) {
|
|
1225
|
+
ctx.fillStyle = rowBackground;
|
|
1226
|
+
ctx.fillRect(x, y, cellWidth, cellHeight);
|
|
1227
|
+
}
|
|
1188
1228
|
}
|
|
1189
1229
|
if (isAxisSelected) {
|
|
1190
1230
|
ctx.fillStyle = getAxisSelectionFill(rowIndex, column.key);
|
|
@@ -1203,8 +1243,11 @@ function paintGrid(options) {
|
|
|
1203
1243
|
const rawValue = column.dataIndex === void 0 ? void 0 : row[column.dataIndex];
|
|
1204
1244
|
const cellStyle = column.cellStyle?.(rawValue, row, rowIndex);
|
|
1205
1245
|
if (!isSelectedCell && !isRangeCell && cellStyle?.backgroundColor) {
|
|
1206
|
-
|
|
1207
|
-
|
|
1246
|
+
const cellBackground = resolveColor(cellStyle.backgroundColor);
|
|
1247
|
+
if (cellBackground) {
|
|
1248
|
+
ctx.fillStyle = cellBackground;
|
|
1249
|
+
ctx.fillRect(x, y, cellWidth, cellHeight);
|
|
1250
|
+
}
|
|
1208
1251
|
}
|
|
1209
1252
|
if (highlightEditedCells && !insertedRowKeys.has(rowKey) && !isSelectedCell && editedCellKeys.has(`${typeof rowKey}:${String(rowKey)}\0${column.key}`)) {
|
|
1210
1253
|
ctx.fillStyle = colors.editedFill;
|
|
@@ -1215,8 +1258,11 @@ function paintGrid(options) {
|
|
|
1215
1258
|
ctx.fillRect(x, y, cellWidth, cellHeight);
|
|
1216
1259
|
}
|
|
1217
1260
|
if (annotation?.type === "background") {
|
|
1218
|
-
|
|
1219
|
-
|
|
1261
|
+
const annotationBackground = resolveColor(annotation.color);
|
|
1262
|
+
if (annotationBackground) {
|
|
1263
|
+
ctx.fillStyle = annotationBackground;
|
|
1264
|
+
ctx.fillRect(x, y, cellWidth, cellHeight);
|
|
1265
|
+
}
|
|
1220
1266
|
}
|
|
1221
1267
|
if (isColumnDropTarget) {
|
|
1222
1268
|
ctx.fillStyle = isAxisSelected ? getAxisSelectionFill(rowIndex, column.key) : colors.columnDropTargetFill;
|
|
@@ -1232,14 +1278,14 @@ function paintGrid(options) {
|
|
|
1232
1278
|
}
|
|
1233
1279
|
if (rowSpan > 1) {
|
|
1234
1280
|
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;
|
|
1281
|
+
ctx.fillStyle = resolveColor(mergedFill) ?? colors.background;
|
|
1236
1282
|
for (let offset = 1; offset < rowSpan; offset += 1) {
|
|
1237
1283
|
ctx.fillRect(x, y + offset * rowHeight - 1, cellWidth, 2);
|
|
1238
1284
|
}
|
|
1239
1285
|
}
|
|
1240
1286
|
if (colSpan > 1 && !verticalBorderless) {
|
|
1241
1287
|
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;
|
|
1288
|
+
ctx.fillStyle = resolveColor(mergedFill) ?? colors.background;
|
|
1243
1289
|
let offsetLeft = 0;
|
|
1244
1290
|
for (let offset = 1; offset < colSpan; offset += 1) {
|
|
1245
1291
|
offsetLeft += metrics[columnIndex + offset - 1]?.width ?? 0;
|
|
@@ -1335,7 +1381,8 @@ function paintGrid(options) {
|
|
|
1335
1381
|
ctx.beginPath();
|
|
1336
1382
|
ctx.rect(x + 2, y + 1, Math.max(0, cellWidth - 4), cellHeight - 2);
|
|
1337
1383
|
ctx.clip();
|
|
1338
|
-
|
|
1384
|
+
const textColor = isAxisSelected ? colors.axisSelectionText : isColumnDropTarget ? colors.text : resolveColor(cellStyle?.color ?? rowStyle?.color) ?? colors.text;
|
|
1385
|
+
ctx.fillStyle = textColor;
|
|
1339
1386
|
ctx.font = createCellFont(bodyFontSize);
|
|
1340
1387
|
ctx.textBaseline = "alphabetic";
|
|
1341
1388
|
ctx.textAlign = column.align === "right" ? "right" : column.align === "center" ? "center" : "left";
|
|
@@ -1345,7 +1392,7 @@ function paintGrid(options) {
|
|
|
1345
1392
|
}
|
|
1346
1393
|
if (annotation?.type === "corner") {
|
|
1347
1394
|
const cornerTop = y - 1;
|
|
1348
|
-
ctx.fillStyle = annotation.color;
|
|
1395
|
+
ctx.fillStyle = resolveColor(annotation.color) ?? colors.text;
|
|
1349
1396
|
ctx.beginPath();
|
|
1350
1397
|
ctx.moveTo(x + cellWidth - 10, cornerTop);
|
|
1351
1398
|
ctx.lineTo(x + cellWidth, cornerTop);
|
|
@@ -1731,6 +1778,18 @@ function readThemePixel(styles, name, fallback) {
|
|
|
1731
1778
|
const value = Number.parseFloat(styles.getPropertyValue(name));
|
|
1732
1779
|
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
1733
1780
|
}
|
|
1781
|
+
function resolveCanvasColor(styles, color) {
|
|
1782
|
+
if (!color) return void 0;
|
|
1783
|
+
let resolved = color.trim();
|
|
1784
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
1785
|
+
const variable = resolved.match(/^var\(\s*(--[^,\s)]+)\s*(?:,\s*(.+))?\)$/);
|
|
1786
|
+
if (!variable) return resolved || void 0;
|
|
1787
|
+
const value = styles.getPropertyValue(variable[1]).trim();
|
|
1788
|
+
resolved = value || variable[2]?.trim() || "";
|
|
1789
|
+
if (!resolved) return void 0;
|
|
1790
|
+
}
|
|
1791
|
+
return void 0;
|
|
1792
|
+
}
|
|
1734
1793
|
function getDefaultRowKey(row, index) {
|
|
1735
1794
|
const id = row.id;
|
|
1736
1795
|
return typeof id === "string" || typeof id === "number" ? id : index;
|
|
@@ -2552,7 +2611,7 @@ function TableInner({
|
|
|
2552
2611
|
const scroll = scrollRef.current;
|
|
2553
2612
|
const rowScrollTop = fixedHeader ? scroll.top : Math.max(0, scroll.top - bodyTop);
|
|
2554
2613
|
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 };
|
|
2555
|
-
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 });
|
|
2614
|
+
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 });
|
|
2556
2615
|
}, [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]);
|
|
2557
2616
|
const scheduleDraw = useCallback2(() => {
|
|
2558
2617
|
if (frameRef.current === null) frameRef.current = requestAnimationFrame(draw);
|