@vesture/react 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1079,25 +1079,79 @@ function Alert({
1079
1079
  }
1080
1080
 
1081
1081
  // src/components/DataGrid/DataGrid.tsx
1082
- import { useCallback as useCallback2, useMemo as useMemo2, useRef as useRef3, useState as useState8 } from "react";
1082
+ import { useCallback as useCallback2, useEffect, useMemo as useMemo2, useRef as useRef3, useState as useState8 } from "react";
1083
1083
 
1084
1084
  // src/components/DataGrid/DataGrid.css.ts
1085
- var body3 = "DataGrid_body__7xivx76";
1086
- var cell = "DataGrid_cell__7xivx78";
1087
- var checkboxCell = "DataGrid_checkboxCell__7xivx79";
1088
- var container = "DataGrid_container__7xivx70";
1089
- var emptyState = "DataGrid_emptyState__7xivx7a";
1090
- var headerButton = "DataGrid_headerButton__7xivx73";
1091
- var headerCell = "DataGrid_headerCell__7xivx72";
1092
- var headerRow = "DataGrid_headerRow__7xivx71";
1093
- var resizeHandle = "DataGrid_resizeHandle__7xivx75";
1094
- var row = "DataGrid_row__7xivx77";
1095
- var sortIcon = "DataGrid_sortIcon__7xivx74";
1085
+ var actionsCell = "DataGrid_actionsCell__7xivx7g";
1086
+ var body3 = "DataGrid_body__7xivx77";
1087
+ var cell = "DataGrid_cell__7xivx79";
1088
+ var checkboxCell = "DataGrid_checkboxCell__7xivx7a";
1089
+ var container = "DataGrid_container__7xivx71";
1090
+ var editInput = "DataGrid_editInput__7xivx7o";
1091
+ var emptyState = "DataGrid_emptyState__7xivx7b";
1092
+ var filterCell = "DataGrid_filterCell__7xivx7j";
1093
+ var filterInput = "DataGrid_filterInput__7xivx7k";
1094
+ var filterRow = "DataGrid_filterRow__7xivx7i";
1095
+ var gridWrapper = "DataGrid_gridWrapper__7xivx7l";
1096
+ var headerButton = "DataGrid_headerButton__7xivx74";
1097
+ var headerCell = "DataGrid_headerCell__7xivx73";
1098
+ var headerRow = "DataGrid_headerRow__7xivx72";
1099
+ var iconButton = "DataGrid_iconButton__7xivx7h";
1100
+ var loadingOverlay = "DataGrid_loadingOverlay__7xivx7m";
1101
+ var paginationFooter = "DataGrid_paginationFooter__7xivx7n";
1102
+ var pinnedCell = "DataGrid_pinnedCell__7xivx7c";
1103
+ var pinnedHeaderCell = "DataGrid_pinnedHeaderCell__7xivx7d";
1104
+ var pinnedLeftEdge = "DataGrid_pinnedLeftEdge__7xivx7e";
1105
+ var pinnedRightEdge = "DataGrid_pinnedRightEdge__7xivx7f";
1106
+ var resizeHandle = "DataGrid_resizeHandle__7xivx76";
1107
+ var row = "DataGrid_row__7xivx78";
1108
+ var sortIcon = "DataGrid_sortIcon__7xivx75";
1109
+ var srOnly2 = "DataGrid_srOnly__7xivx70";
1096
1110
 
1097
1111
  // src/components/DataGrid/DataGrid.tsx
1098
- import { jsx as jsx34, jsxs as jsxs17 } from "react/jsx-runtime";
1112
+ import { Fragment as Fragment5, jsx as jsx34, jsxs as jsxs17 } from "react/jsx-runtime";
1099
1113
  var DEFAULT_COLUMN_WIDTH = 160;
1100
1114
  var CHECKBOX_COLUMN_WIDTH = 44;
1115
+ var ACTIONS_COLUMN_WIDTH = 72;
1116
+ var FILTER_DEBOUNCE_MS = 200;
1117
+ function useDebouncedValue(value, delay) {
1118
+ const [debounced, setDebounced] = useState8(value);
1119
+ useEffect(() => {
1120
+ const timer = setTimeout(() => setDebounced(value), delay);
1121
+ return () => clearTimeout(timer);
1122
+ }, [value, delay]);
1123
+ return debounced;
1124
+ }
1125
+ function FilterTextInput({
1126
+ value,
1127
+ onChange,
1128
+ ...rest
1129
+ }) {
1130
+ const [draft, setDraft] = useState8(value);
1131
+ const debounced = useDebouncedValue(draft, FILTER_DEBOUNCE_MS);
1132
+ const onChangeRef = useRef3(onChange);
1133
+ onChangeRef.current = onChange;
1134
+ const mounted = useRef3(false);
1135
+ useEffect(() => {
1136
+ if (!mounted.current) {
1137
+ mounted.current = true;
1138
+ return;
1139
+ }
1140
+ onChangeRef.current(debounced);
1141
+ }, [debounced]);
1142
+ useEffect(() => {
1143
+ setDraft(value);
1144
+ }, [value]);
1145
+ return /* @__PURE__ */ jsx34(
1146
+ "input",
1147
+ {
1148
+ type: "text",
1149
+ value: draft,
1150
+ onChange: (e) => setDraft(e.target.value),
1151
+ ...rest
1152
+ }
1153
+ );
1154
+ }
1101
1155
  function compareValues(a, b) {
1102
1156
  if (typeof a === "number" && typeof b === "number") {
1103
1157
  return a - b;
@@ -1122,15 +1176,35 @@ function DataGrid({
1122
1176
  onSelectionChange,
1123
1177
  sort: controlledSort,
1124
1178
  onSortChange,
1125
- emptyMessage = "No data"
1179
+ filters: controlledFilters,
1180
+ onFilterChange,
1181
+ emptyMessage = "No data",
1182
+ onRowEdit,
1183
+ serverSide = false,
1184
+ loading = false,
1185
+ totalRowCount,
1186
+ page,
1187
+ pageSize,
1188
+ onPageChange
1126
1189
  }) {
1127
- const [uncontrolledSort, setUncontrolledSort] = useState8(null);
1190
+ const [uncontrolledSort, setUncontrolledSort] = useState8(
1191
+ null
1192
+ );
1193
+ const [uncontrolledFilters, setUncontrolledFilters] = useState8(
1194
+ []
1195
+ );
1128
1196
  const [uncontrolledSelectedIds, setUncontrolledSelectedIds] = useState8(() => /* @__PURE__ */ new Set());
1129
1197
  const [columnWidths, setColumnWidths] = useState8(
1130
- () => Object.fromEntries(columns.map((c) => [c.key, c.width ?? DEFAULT_COLUMN_WIDTH]))
1198
+ () => Object.fromEntries(
1199
+ columns.map((c) => [c.key, c.width ?? DEFAULT_COLUMN_WIDTH])
1200
+ )
1131
1201
  );
1132
1202
  const [scrollTop, setScrollTop] = useState8(0);
1203
+ const [editingRowId, setEditingRowId] = useState8(null);
1204
+ const [editDraft, setEditDraft] = useState8({});
1205
+ const editingEnabled = Boolean(onRowEdit);
1133
1206
  const sort = controlledSort !== void 0 ? controlledSort : uncontrolledSort;
1207
+ const filters = controlledFilters !== void 0 ? controlledFilters : uncontrolledFilters;
1134
1208
  const selectedIds = controlledSelectedIds ?? uncontrolledSelectedIds;
1135
1209
  const setSort = (next) => {
1136
1210
  if (controlledSort === void 0) {
@@ -1138,6 +1212,17 @@ function DataGrid({
1138
1212
  }
1139
1213
  onSortChange?.(next);
1140
1214
  };
1215
+ const setFilters = (next) => {
1216
+ if (controlledFilters === void 0) {
1217
+ setUncontrolledFilters(next);
1218
+ }
1219
+ onFilterChange?.(next);
1220
+ };
1221
+ const handleFilterChange = (key, value) => {
1222
+ const next = value ? [...filters.filter((f) => f.key !== key), { key, value }] : filters.filter((f) => f.key !== key);
1223
+ setFilters(next);
1224
+ };
1225
+ const getFilterValue = (key) => filters.find((f) => f.key === key)?.value ?? "";
1141
1226
  const setSelectedIds = (next) => {
1142
1227
  if (controlledSelectedIds === void 0) {
1143
1228
  setUncontrolledSelectedIds(next);
@@ -1154,17 +1239,50 @@ function DataGrid({
1154
1239
  setSort(null);
1155
1240
  }
1156
1241
  };
1242
+ const filteredData = useMemo2(() => {
1243
+ if (serverSide || filters.length === 0) {
1244
+ return data;
1245
+ }
1246
+ return data.filter(
1247
+ (row2) => filters.every((f) => {
1248
+ if (!f.value) return true;
1249
+ const column = columns.find((c) => c.key === f.key);
1250
+ if (!column) return true;
1251
+ const cellValue = String(getCellValue(column, row2) ?? "").toLowerCase();
1252
+ return cellValue.includes(f.value.toLowerCase());
1253
+ })
1254
+ );
1255
+ }, [data, filters, columns, serverSide]);
1157
1256
  const sortedData = useMemo2(() => {
1158
- if (!sort || !sort.direction) {
1257
+ if (serverSide) {
1159
1258
  return data;
1160
1259
  }
1260
+ if (!sort || !sort.direction) {
1261
+ return filteredData;
1262
+ }
1161
1263
  const column = columns.find((c) => c.key === sort.key);
1162
1264
  if (!column) {
1163
- return data;
1265
+ return filteredData;
1164
1266
  }
1165
- const sorted = [...data].sort((a, b) => compareValues(getCellValue(column, a), getCellValue(column, b)));
1267
+ const sorted = [...filteredData].sort(
1268
+ (a, b) => compareValues(getCellValue(column, a), getCellValue(column, b))
1269
+ );
1166
1270
  return sort.direction === "desc" ? sorted.reverse() : sorted;
1167
- }, [data, sort, columns]);
1271
+ }, [filteredData, sort, columns, serverSide, data]);
1272
+ const selectFilterOptions = useMemo2(() => {
1273
+ const map = {};
1274
+ for (const column of columns) {
1275
+ if (!column.filterable || column.filterType !== "select") continue;
1276
+ const values = /* @__PURE__ */ new Set();
1277
+ for (const row2 of data) {
1278
+ values.add(String(getCellValue(column, row2) ?? ""));
1279
+ }
1280
+ map[column.key] = Array.from(values).sort();
1281
+ }
1282
+ return map;
1283
+ }, [columns, data]);
1284
+ const hasFilterableColumns = columns.some((c) => c.filterable);
1285
+ const showPagination = page !== void 0 && pageSize !== void 0 && onPageChange !== void 0;
1168
1286
  const allSelected = sortedData.length > 0 && sortedData.every((r) => selectedIds.has(getRowId(r)));
1169
1287
  const someSelected = !allSelected && sortedData.some((r) => selectedIds.has(getRowId(r)));
1170
1288
  const toggleAll = () => {
@@ -1199,109 +1317,399 @@ function DataGrid({
1199
1317
  },
1200
1318
  [columnWidths]
1201
1319
  );
1202
- const handleResizeMove = useCallback2((event) => {
1203
- const state = resizeState.current;
1204
- if (!state) return;
1205
- const delta = event.clientX - state.startX;
1206
- const minWidth = columns.find((c) => c.key === state.key)?.minWidth ?? 60;
1207
- const nextWidth = Math.max(minWidth, state.startWidth + delta);
1208
- setColumnWidths((prev) => ({ ...prev, [state.key]: nextWidth }));
1209
- }, [columns]);
1210
- const handleResizeEnd = useCallback2((event) => {
1211
- resizeState.current = null;
1212
- event.currentTarget.releasePointerCapture(event.pointerId);
1213
- }, []);
1320
+ const handleResizeMove = useCallback2(
1321
+ (event) => {
1322
+ const state = resizeState.current;
1323
+ if (!state) return;
1324
+ const delta = event.clientX - state.startX;
1325
+ const minWidth = columns.find((c) => c.key === state.key)?.minWidth ?? 60;
1326
+ const nextWidth = Math.max(minWidth, state.startWidth + delta);
1327
+ setColumnWidths((prev) => ({ ...prev, [state.key]: nextWidth }));
1328
+ },
1329
+ [columns]
1330
+ );
1331
+ const handleResizeEnd = useCallback2(
1332
+ (event) => {
1333
+ resizeState.current = null;
1334
+ event.currentTarget.releasePointerCapture(event.pointerId);
1335
+ },
1336
+ []
1337
+ );
1338
+ const widthOf = useCallback2(
1339
+ (column) => columnWidths[column.key] ?? column.width ?? DEFAULT_COLUMN_WIDTH,
1340
+ [columnWidths]
1341
+ );
1342
+ const layout = useMemo2(() => {
1343
+ const leftPinned = columns.filter((c) => c.pinned === "left");
1344
+ const rightPinned = columns.filter((c) => c.pinned === "right");
1345
+ const middle = columns.filter((c) => !c.pinned);
1346
+ const ordered = [...leftPinned, ...middle, ...rightPinned];
1347
+ const leftOffsets = /* @__PURE__ */ new Map();
1348
+ let leftAcc = selectable ? CHECKBOX_COLUMN_WIDTH : 0;
1349
+ for (const column of leftPinned) {
1350
+ leftOffsets.set(column.key, leftAcc);
1351
+ leftAcc += widthOf(column);
1352
+ }
1353
+ const rightOffsets = /* @__PURE__ */ new Map();
1354
+ let rightAcc = editingEnabled ? ACTIONS_COLUMN_WIDTH : 0;
1355
+ for (let i = rightPinned.length - 1; i >= 0; i--) {
1356
+ const column = rightPinned[i];
1357
+ rightOffsets.set(column.key, rightAcc);
1358
+ rightAcc += widthOf(column);
1359
+ }
1360
+ return {
1361
+ ordered,
1362
+ leftOffsets,
1363
+ rightOffsets,
1364
+ lastLeftKey: leftPinned[leftPinned.length - 1]?.key,
1365
+ firstRightKey: rightPinned[0]?.key
1366
+ };
1367
+ }, [columns, selectable, editingEnabled, widthOf]);
1368
+ const pinnedStyle = (column) => {
1369
+ if (column.pinned === "left" && layout.leftOffsets.has(column.key)) {
1370
+ return { left: layout.leftOffsets.get(column.key) };
1371
+ }
1372
+ if (column.pinned === "right" && layout.rightOffsets.has(column.key)) {
1373
+ return { right: layout.rightOffsets.get(column.key) };
1374
+ }
1375
+ return void 0;
1376
+ };
1377
+ const pinnedClass = (column) => {
1378
+ const classes = [];
1379
+ if (column.pinned) {
1380
+ classes.push(pinnedCell);
1381
+ if (column.key === layout.lastLeftKey) classes.push(pinnedLeftEdge);
1382
+ if (column.key === layout.firstRightKey) classes.push(pinnedRightEdge);
1383
+ }
1384
+ return classes.join(" ");
1385
+ };
1386
+ const headerPinnedClass = (column) => {
1387
+ const classes = [];
1388
+ if (column.pinned) {
1389
+ classes.push(pinnedHeaderCell);
1390
+ if (column.key === layout.lastLeftKey) classes.push(pinnedLeftEdge);
1391
+ if (column.key === layout.firstRightKey) classes.push(pinnedRightEdge);
1392
+ }
1393
+ return classes.join(" ");
1394
+ };
1395
+ const startEditing = (rowData) => {
1396
+ const id = getRowId(rowData);
1397
+ const draft = {};
1398
+ for (const column of columns) {
1399
+ if (column.editable) {
1400
+ draft[column.key] = String(getCellValue(column, rowData) ?? "");
1401
+ }
1402
+ }
1403
+ setEditDraft(draft);
1404
+ setEditingRowId(id);
1405
+ };
1406
+ const cancelEditing = () => {
1407
+ setEditingRowId(null);
1408
+ setEditDraft({});
1409
+ };
1410
+ const saveEditing = () => {
1411
+ if (editingRowId) {
1412
+ onRowEdit?.(editingRowId, editDraft);
1413
+ }
1414
+ setEditingRowId(null);
1415
+ setEditDraft({});
1416
+ };
1214
1417
  const totalHeight = sortedData.length * rowHeight;
1215
1418
  const visibleCount = Math.ceil(height / rowHeight) + overscan * 2;
1216
1419
  const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
1217
1420
  const endIndex = Math.min(sortedData.length, startIndex + visibleCount);
1218
1421
  const visibleRows = sortedData.slice(startIndex, endIndex);
1219
- const totalWidth = (selectable ? CHECKBOX_COLUMN_WIDTH : 0) + columns.reduce((sum, c) => sum + (columnWidths[c.key] ?? DEFAULT_COLUMN_WIDTH), 0);
1220
- return /* @__PURE__ */ jsxs17(
1221
- "div",
1222
- {
1223
- className: container,
1224
- style: { height },
1225
- onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
1226
- role: "table",
1227
- children: [
1228
- /* @__PURE__ */ jsxs17("div", { className: headerRow, style: { width: totalWidth, minWidth: "100%" }, role: "row", children: [
1229
- selectable ? /* @__PURE__ */ jsx34("div", { className: checkboxCell, role: "columnheader", children: /* @__PURE__ */ jsx34(
1230
- "input",
1231
- {
1232
- type: "checkbox",
1233
- checked: allSelected,
1234
- ref: (el) => {
1235
- if (el) el.indeterminate = someSelected;
1236
- },
1237
- onChange: toggleAll,
1238
- "aria-label": "Select all rows"
1239
- }
1240
- ) }) : null,
1241
- columns.map((column) => {
1242
- const width = columnWidths[column.key] ?? DEFAULT_COLUMN_WIDTH;
1243
- const isSorted = sort?.key === column.key;
1244
- const direction2 = isSorted ? sort.direction : null;
1245
- return /* @__PURE__ */ jsxs17(
1422
+ const totalWidth = (selectable ? CHECKBOX_COLUMN_WIDTH : 0) + (editingEnabled ? ACTIONS_COLUMN_WIDTH : 0) + columns.reduce((sum, c) => sum + widthOf(c), 0);
1423
+ return /* @__PURE__ */ jsxs17(Fragment5, { children: [
1424
+ /* @__PURE__ */ jsxs17("div", { className: gridWrapper, children: [
1425
+ /* @__PURE__ */ jsxs17(
1426
+ "div",
1427
+ {
1428
+ className: container,
1429
+ style: { height },
1430
+ onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
1431
+ role: "table",
1432
+ children: [
1433
+ /* @__PURE__ */ jsxs17(
1246
1434
  "div",
1247
1435
  {
1248
- className: headerCell,
1249
- style: { width },
1250
- role: "columnheader",
1251
- "aria-sort": direction2 === "asc" ? "ascending" : direction2 === "desc" ? "descending" : "none",
1436
+ className: headerRow,
1437
+ style: { width: totalWidth, minWidth: "100%" },
1438
+ role: "row",
1252
1439
  children: [
1253
- column.sortable ? /* @__PURE__ */ jsxs17("button", { type: "button", className: headerButton, onClick: () => handleSortClick(column), children: [
1254
- column.header,
1255
- /* @__PURE__ */ jsx34("span", { className: sortIcon, "aria-hidden": "true", children: direction2 === "asc" ? "\u25B2" : direction2 === "desc" ? "\u25BC" : "" })
1256
- ] }) : column.header,
1257
- column.resizable !== false ? /* @__PURE__ */ jsx34(
1440
+ selectable ? /* @__PURE__ */ jsx34(
1441
+ "div",
1442
+ {
1443
+ className: [checkboxCell, pinnedHeaderCell].join(" "),
1444
+ style: { left: 0 },
1445
+ role: "columnheader",
1446
+ children: /* @__PURE__ */ jsx34(
1447
+ "input",
1448
+ {
1449
+ type: "checkbox",
1450
+ checked: allSelected,
1451
+ ref: (el) => {
1452
+ if (el) el.indeterminate = someSelected;
1453
+ },
1454
+ onChange: toggleAll,
1455
+ "aria-label": "Select all rows"
1456
+ }
1457
+ )
1458
+ }
1459
+ ) : null,
1460
+ layout.ordered.map((column) => {
1461
+ const width = widthOf(column);
1462
+ const isSorted = sort?.key === column.key;
1463
+ const direction2 = isSorted ? sort.direction : null;
1464
+ return /* @__PURE__ */ jsxs17(
1465
+ "div",
1466
+ {
1467
+ className: [headerCell, headerPinnedClass(column)].filter(Boolean).join(" "),
1468
+ style: { width, ...pinnedStyle(column) },
1469
+ role: "columnheader",
1470
+ "aria-sort": direction2 === "asc" ? "ascending" : direction2 === "desc" ? "descending" : "none",
1471
+ children: [
1472
+ column.sortable ? /* @__PURE__ */ jsxs17(
1473
+ "button",
1474
+ {
1475
+ type: "button",
1476
+ className: headerButton,
1477
+ onClick: () => handleSortClick(column),
1478
+ children: [
1479
+ column.header,
1480
+ /* @__PURE__ */ jsx34("span", { className: sortIcon, "aria-hidden": "true", children: direction2 === "asc" ? "\u25B2" : direction2 === "desc" ? "\u25BC" : "" })
1481
+ ]
1482
+ }
1483
+ ) : column.header,
1484
+ column.resizable !== false ? /* @__PURE__ */ jsx34(
1485
+ "div",
1486
+ {
1487
+ className: resizeHandle,
1488
+ onPointerDown: (e) => handleResizeStart(e, column),
1489
+ onPointerMove: handleResizeMove,
1490
+ onPointerUp: handleResizeEnd,
1491
+ role: "separator",
1492
+ "aria-orientation": "vertical",
1493
+ "aria-label": `Resize ${String(column.header)} column`
1494
+ }
1495
+ ) : null
1496
+ ]
1497
+ },
1498
+ column.key
1499
+ );
1500
+ }),
1501
+ editingEnabled ? /* @__PURE__ */ jsx34(
1258
1502
  "div",
1259
1503
  {
1260
- className: resizeHandle,
1261
- onPointerDown: (e) => handleResizeStart(e, column),
1262
- onPointerMove: handleResizeMove,
1263
- onPointerUp: handleResizeEnd,
1264
- role: "separator",
1265
- "aria-orientation": "vertical",
1266
- "aria-label": `Resize ${String(column.header)} column`
1504
+ className: [actionsCell, pinnedHeaderCell].join(" "),
1505
+ style: { right: 0 },
1506
+ role: "columnheader",
1507
+ children: /* @__PURE__ */ jsx34("span", { className: srOnly2, children: "Actions" })
1267
1508
  }
1268
1509
  ) : null
1269
1510
  ]
1270
- },
1271
- column.key
1272
- );
1273
- })
1274
- ] }),
1275
- sortedData.length === 0 ? /* @__PURE__ */ jsx34("div", { className: emptyState, children: emptyMessage }) : /* @__PURE__ */ jsx34("div", { className: body3, style: { height: totalHeight, width: totalWidth, minWidth: "100%" }, children: visibleRows.map((rowData, i) => {
1276
- const index = startIndex + i;
1277
- const id = getRowId(rowData);
1278
- const isSelected = selectedIds.has(id);
1279
- const style = { top: index * rowHeight, height: rowHeight, width: totalWidth };
1280
- return /* @__PURE__ */ jsxs17("div", { className: row, style, role: "row", "data-selected": isSelected || void 0, children: [
1281
- selectable ? /* @__PURE__ */ jsx34("div", { className: checkboxCell, role: "cell", children: /* @__PURE__ */ jsx34(
1282
- "input",
1511
+ }
1512
+ ),
1513
+ hasFilterableColumns ? /* @__PURE__ */ jsxs17(
1514
+ "div",
1283
1515
  {
1284
- type: "checkbox",
1285
- checked: isSelected,
1286
- onChange: () => toggleRow(id),
1287
- "aria-label": `Select row ${index + 1}`
1516
+ className: filterRow,
1517
+ style: { width: totalWidth, minWidth: "100%" },
1518
+ role: "row",
1519
+ children: [
1520
+ selectable ? /* @__PURE__ */ jsx34(
1521
+ "div",
1522
+ {
1523
+ className: [checkboxCell, pinnedHeaderCell].join(" "),
1524
+ style: { left: 0 },
1525
+ role: "cell"
1526
+ }
1527
+ ) : null,
1528
+ layout.ordered.map((column) => {
1529
+ const width = widthOf(column);
1530
+ return /* @__PURE__ */ jsx34(
1531
+ "div",
1532
+ {
1533
+ className: [filterCell, headerPinnedClass(column)].filter(Boolean).join(" "),
1534
+ style: { width, ...pinnedStyle(column) },
1535
+ role: "cell",
1536
+ children: column.filterable ? column.filterType === "select" ? /* @__PURE__ */ jsxs17(
1537
+ "select",
1538
+ {
1539
+ className: filterInput,
1540
+ value: getFilterValue(column.key),
1541
+ onChange: (e) => handleFilterChange(column.key, e.target.value),
1542
+ "aria-label": `Filter ${String(column.header)}`,
1543
+ children: [
1544
+ /* @__PURE__ */ jsx34("option", { value: "", children: "All" }),
1545
+ (selectFilterOptions[column.key] ?? []).map(
1546
+ (value) => /* @__PURE__ */ jsx34("option", { value, children: value }, value)
1547
+ )
1548
+ ]
1549
+ }
1550
+ ) : /* @__PURE__ */ jsx34(
1551
+ FilterTextInput,
1552
+ {
1553
+ className: filterInput,
1554
+ value: getFilterValue(column.key),
1555
+ onChange: (value) => handleFilterChange(column.key, value),
1556
+ placeholder: `Filter ${String(column.header)}`,
1557
+ "aria-label": `Filter ${String(column.header)}`
1558
+ }
1559
+ ) : null
1560
+ },
1561
+ column.key
1562
+ );
1563
+ }),
1564
+ editingEnabled ? /* @__PURE__ */ jsx34(
1565
+ "div",
1566
+ {
1567
+ className: [actionsCell, pinnedHeaderCell].join(" "),
1568
+ style: { right: 0 },
1569
+ role: "cell"
1570
+ }
1571
+ ) : null
1572
+ ]
1288
1573
  }
1289
- ) }) : null,
1290
- columns.map((column) => /* @__PURE__ */ jsx34(
1574
+ ) : null,
1575
+ sortedData.length === 0 ? /* @__PURE__ */ jsx34("div", { className: emptyState, children: emptyMessage }) : /* @__PURE__ */ jsx34(
1291
1576
  "div",
1292
1577
  {
1293
- className: cell,
1294
- style: { width: columnWidths[column.key] ?? DEFAULT_COLUMN_WIDTH },
1295
- role: "cell",
1296
- children: column.render ? column.render(rowData) : String(getCellValue(column, rowData) ?? "")
1297
- },
1298
- column.key
1299
- ))
1300
- ] }, id);
1301
- }) })
1302
- ]
1303
- }
1304
- );
1578
+ className: body3,
1579
+ style: {
1580
+ height: totalHeight,
1581
+ width: totalWidth,
1582
+ minWidth: "100%"
1583
+ },
1584
+ children: visibleRows.map((rowData, i) => {
1585
+ const index = startIndex + i;
1586
+ const id = getRowId(rowData);
1587
+ const isSelected = selectedIds.has(id);
1588
+ const isEditing = editingRowId === id;
1589
+ const style = {
1590
+ top: index * rowHeight,
1591
+ height: rowHeight,
1592
+ width: totalWidth
1593
+ };
1594
+ return /* @__PURE__ */ jsxs17(
1595
+ "div",
1596
+ {
1597
+ className: row,
1598
+ style,
1599
+ role: "row",
1600
+ "data-selected": isSelected || void 0,
1601
+ "data-editing": isEditing || void 0,
1602
+ children: [
1603
+ selectable ? /* @__PURE__ */ jsx34(
1604
+ "div",
1605
+ {
1606
+ className: [checkboxCell, pinnedCell].join(" "),
1607
+ style: { left: 0 },
1608
+ role: "cell",
1609
+ children: /* @__PURE__ */ jsx34(
1610
+ "input",
1611
+ {
1612
+ type: "checkbox",
1613
+ checked: isSelected,
1614
+ onChange: () => toggleRow(id),
1615
+ "aria-label": `Select row ${index + 1}`
1616
+ }
1617
+ )
1618
+ }
1619
+ ) : null,
1620
+ layout.ordered.map((column) => /* @__PURE__ */ jsx34(
1621
+ "div",
1622
+ {
1623
+ className: [cell, pinnedClass(column)].filter(Boolean).join(" "),
1624
+ style: {
1625
+ width: widthOf(column),
1626
+ ...pinnedStyle(column)
1627
+ },
1628
+ role: "cell",
1629
+ children: isEditing && column.editable ? /* @__PURE__ */ jsx34(
1630
+ "input",
1631
+ {
1632
+ type: "text",
1633
+ className: editInput,
1634
+ value: editDraft[column.key] ?? "",
1635
+ onChange: (e) => setEditDraft((prev) => ({
1636
+ ...prev,
1637
+ [column.key]: e.target.value
1638
+ })),
1639
+ onKeyDown: (e) => {
1640
+ if (e.key === "Enter") saveEditing();
1641
+ if (e.key === "Escape") cancelEditing();
1642
+ },
1643
+ "aria-label": `Edit ${String(column.header)}`,
1644
+ autoFocus: column.key === columns.find((c) => c.editable)?.key
1645
+ }
1646
+ ) : column.render ? column.render(rowData) : String(getCellValue(column, rowData) ?? "")
1647
+ },
1648
+ column.key
1649
+ )),
1650
+ editingEnabled ? /* @__PURE__ */ jsx34(
1651
+ "div",
1652
+ {
1653
+ className: [actionsCell, pinnedCell].join(" "),
1654
+ style: { right: 0 },
1655
+ role: "cell",
1656
+ children: isEditing ? /* @__PURE__ */ jsxs17(Fragment5, { children: [
1657
+ /* @__PURE__ */ jsx34(
1658
+ "button",
1659
+ {
1660
+ type: "button",
1661
+ className: iconButton,
1662
+ onClick: saveEditing,
1663
+ "aria-label": `Save row ${index + 1}`,
1664
+ children: "\u2713"
1665
+ }
1666
+ ),
1667
+ /* @__PURE__ */ jsx34(
1668
+ "button",
1669
+ {
1670
+ type: "button",
1671
+ className: iconButton,
1672
+ onClick: cancelEditing,
1673
+ "aria-label": `Cancel editing row ${index + 1}`,
1674
+ children: "\u2715"
1675
+ }
1676
+ )
1677
+ ] }) : /* @__PURE__ */ jsx34(
1678
+ "button",
1679
+ {
1680
+ type: "button",
1681
+ className: iconButton,
1682
+ onClick: () => startEditing(rowData),
1683
+ "aria-label": `Edit row ${index + 1}`,
1684
+ children: "\u270E"
1685
+ }
1686
+ )
1687
+ }
1688
+ ) : null
1689
+ ]
1690
+ },
1691
+ id
1692
+ );
1693
+ })
1694
+ }
1695
+ )
1696
+ ]
1697
+ }
1698
+ ),
1699
+ loading ? /* @__PURE__ */ jsx34("div", { className: loadingOverlay, children: /* @__PURE__ */ jsx34(Spinner, { size: "md" }) }) : null
1700
+ ] }),
1701
+ showPagination ? /* @__PURE__ */ jsx34("div", { className: paginationFooter, children: /* @__PURE__ */ jsx34(
1702
+ Pagination,
1703
+ {
1704
+ page,
1705
+ totalPages: Math.max(
1706
+ 1,
1707
+ Math.ceil((totalRowCount ?? data.length) / pageSize)
1708
+ ),
1709
+ onPageChange
1710
+ }
1711
+ ) }) : null
1712
+ ] });
1305
1713
  }
1306
1714
 
1307
1715
  // src/index.ts