@vesture/react 0.1.1 → 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,7 +1079,7 @@ 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
1085
  var actionsCell = "DataGrid_actionsCell__7xivx7g";
@@ -1087,12 +1087,18 @@ var body3 = "DataGrid_body__7xivx77";
1087
1087
  var cell = "DataGrid_cell__7xivx79";
1088
1088
  var checkboxCell = "DataGrid_checkboxCell__7xivx7a";
1089
1089
  var container = "DataGrid_container__7xivx71";
1090
- var editInput = "DataGrid_editInput__7xivx7i";
1090
+ var editInput = "DataGrid_editInput__7xivx7o";
1091
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";
1092
1096
  var headerButton = "DataGrid_headerButton__7xivx74";
1093
1097
  var headerCell = "DataGrid_headerCell__7xivx73";
1094
1098
  var headerRow = "DataGrid_headerRow__7xivx72";
1095
1099
  var iconButton = "DataGrid_iconButton__7xivx7h";
1100
+ var loadingOverlay = "DataGrid_loadingOverlay__7xivx7m";
1101
+ var paginationFooter = "DataGrid_paginationFooter__7xivx7n";
1096
1102
  var pinnedCell = "DataGrid_pinnedCell__7xivx7c";
1097
1103
  var pinnedHeaderCell = "DataGrid_pinnedHeaderCell__7xivx7d";
1098
1104
  var pinnedLeftEdge = "DataGrid_pinnedLeftEdge__7xivx7e";
@@ -1107,6 +1113,45 @@ import { Fragment as Fragment5, jsx as jsx34, jsxs as jsxs17 } from "react/jsx-r
1107
1113
  var DEFAULT_COLUMN_WIDTH = 160;
1108
1114
  var CHECKBOX_COLUMN_WIDTH = 44;
1109
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
+ }
1110
1155
  function compareValues(a, b) {
1111
1156
  if (typeof a === "number" && typeof b === "number") {
1112
1157
  return a - b;
@@ -1131,19 +1176,35 @@ function DataGrid({
1131
1176
  onSelectionChange,
1132
1177
  sort: controlledSort,
1133
1178
  onSortChange,
1179
+ filters: controlledFilters,
1180
+ onFilterChange,
1134
1181
  emptyMessage = "No data",
1135
- onRowEdit
1182
+ onRowEdit,
1183
+ serverSide = false,
1184
+ loading = false,
1185
+ totalRowCount,
1186
+ page,
1187
+ pageSize,
1188
+ onPageChange
1136
1189
  }) {
1137
- const [uncontrolledSort, setUncontrolledSort] = useState8(null);
1190
+ const [uncontrolledSort, setUncontrolledSort] = useState8(
1191
+ null
1192
+ );
1193
+ const [uncontrolledFilters, setUncontrolledFilters] = useState8(
1194
+ []
1195
+ );
1138
1196
  const [uncontrolledSelectedIds, setUncontrolledSelectedIds] = useState8(() => /* @__PURE__ */ new Set());
1139
1197
  const [columnWidths, setColumnWidths] = useState8(
1140
- () => 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
+ )
1141
1201
  );
1142
1202
  const [scrollTop, setScrollTop] = useState8(0);
1143
1203
  const [editingRowId, setEditingRowId] = useState8(null);
1144
1204
  const [editDraft, setEditDraft] = useState8({});
1145
1205
  const editingEnabled = Boolean(onRowEdit);
1146
1206
  const sort = controlledSort !== void 0 ? controlledSort : uncontrolledSort;
1207
+ const filters = controlledFilters !== void 0 ? controlledFilters : uncontrolledFilters;
1147
1208
  const selectedIds = controlledSelectedIds ?? uncontrolledSelectedIds;
1148
1209
  const setSort = (next) => {
1149
1210
  if (controlledSort === void 0) {
@@ -1151,6 +1212,17 @@ function DataGrid({
1151
1212
  }
1152
1213
  onSortChange?.(next);
1153
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 ?? "";
1154
1226
  const setSelectedIds = (next) => {
1155
1227
  if (controlledSelectedIds === void 0) {
1156
1228
  setUncontrolledSelectedIds(next);
@@ -1167,17 +1239,50 @@ function DataGrid({
1167
1239
  setSort(null);
1168
1240
  }
1169
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]);
1170
1256
  const sortedData = useMemo2(() => {
1171
- if (!sort || !sort.direction) {
1257
+ if (serverSide) {
1172
1258
  return data;
1173
1259
  }
1260
+ if (!sort || !sort.direction) {
1261
+ return filteredData;
1262
+ }
1174
1263
  const column = columns.find((c) => c.key === sort.key);
1175
1264
  if (!column) {
1176
- return data;
1265
+ return filteredData;
1177
1266
  }
1178
- 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
+ );
1179
1270
  return sort.direction === "desc" ? sorted.reverse() : sorted;
1180
- }, [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;
1181
1286
  const allSelected = sortedData.length > 0 && sortedData.every((r) => selectedIds.has(getRowId(r)));
1182
1287
  const someSelected = !allSelected && sortedData.some((r) => selectedIds.has(getRowId(r)));
1183
1288
  const toggleAll = () => {
@@ -1212,18 +1317,24 @@ function DataGrid({
1212
1317
  },
1213
1318
  [columnWidths]
1214
1319
  );
1215
- const handleResizeMove = useCallback2((event) => {
1216
- const state = resizeState.current;
1217
- if (!state) return;
1218
- const delta = event.clientX - state.startX;
1219
- const minWidth = columns.find((c) => c.key === state.key)?.minWidth ?? 60;
1220
- const nextWidth = Math.max(minWidth, state.startWidth + delta);
1221
- setColumnWidths((prev) => ({ ...prev, [state.key]: nextWidth }));
1222
- }, [columns]);
1223
- const handleResizeEnd = useCallback2((event) => {
1224
- resizeState.current = null;
1225
- event.currentTarget.releasePointerCapture(event.pointerId);
1226
- }, []);
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
+ );
1227
1338
  const widthOf = useCallback2(
1228
1339
  (column) => columnWidths[column.key] ?? column.width ?? DEFAULT_COLUMN_WIDTH,
1229
1340
  [columnWidths]
@@ -1309,165 +1420,296 @@ function DataGrid({
1309
1420
  const endIndex = Math.min(sortedData.length, startIndex + visibleCount);
1310
1421
  const visibleRows = sortedData.slice(startIndex, endIndex);
1311
1422
  const totalWidth = (selectable ? CHECKBOX_COLUMN_WIDTH : 0) + (editingEnabled ? ACTIONS_COLUMN_WIDTH : 0) + columns.reduce((sum, c) => sum + widthOf(c), 0);
1312
- return /* @__PURE__ */ jsxs17(
1313
- "div",
1314
- {
1315
- className: container,
1316
- style: { height },
1317
- onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
1318
- role: "table",
1319
- children: [
1320
- /* @__PURE__ */ jsxs17("div", { className: headerRow, style: { width: totalWidth, minWidth: "100%" }, role: "row", children: [
1321
- selectable ? /* @__PURE__ */ jsx34("div", { className: [checkboxCell, pinnedHeaderCell].join(" "), style: { left: 0 }, role: "columnheader", children: /* @__PURE__ */ jsx34(
1322
- "input",
1323
- {
1324
- type: "checkbox",
1325
- checked: allSelected,
1326
- ref: (el) => {
1327
- if (el) el.indeterminate = someSelected;
1328
- },
1329
- onChange: toggleAll,
1330
- "aria-label": "Select all rows"
1331
- }
1332
- ) }) : null,
1333
- layout.ordered.map((column) => {
1334
- const width = widthOf(column);
1335
- const isSorted = sort?.key === column.key;
1336
- const direction2 = isSorted ? sort.direction : null;
1337
- return /* @__PURE__ */ jsxs17(
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(
1338
1434
  "div",
1339
1435
  {
1340
- className: [headerCell, headerPinnedClass(column)].filter(Boolean).join(" "),
1341
- style: { width, ...pinnedStyle(column) },
1342
- role: "columnheader",
1343
- "aria-sort": direction2 === "asc" ? "ascending" : direction2 === "desc" ? "descending" : "none",
1436
+ className: headerRow,
1437
+ style: { width: totalWidth, minWidth: "100%" },
1438
+ role: "row",
1344
1439
  children: [
1345
- column.sortable ? /* @__PURE__ */ jsxs17("button", { type: "button", className: headerButton, onClick: () => handleSortClick(column), children: [
1346
- column.header,
1347
- /* @__PURE__ */ jsx34("span", { className: sortIcon, "aria-hidden": "true", children: direction2 === "asc" ? "\u25B2" : direction2 === "desc" ? "\u25BC" : "" })
1348
- ] }) : column.header,
1349
- column.resizable !== false ? /* @__PURE__ */ jsx34(
1440
+ selectable ? /* @__PURE__ */ jsx34(
1350
1441
  "div",
1351
1442
  {
1352
- className: resizeHandle,
1353
- onPointerDown: (e) => handleResizeStart(e, column),
1354
- onPointerMove: handleResizeMove,
1355
- onPointerUp: handleResizeEnd,
1356
- role: "separator",
1357
- "aria-orientation": "vertical",
1358
- "aria-label": `Resize ${String(column.header)} column`
1359
- }
1360
- ) : null
1361
- ]
1362
- },
1363
- column.key
1364
- );
1365
- }),
1366
- editingEnabled ? /* @__PURE__ */ jsx34(
1367
- "div",
1368
- {
1369
- className: [actionsCell, pinnedHeaderCell].join(" "),
1370
- style: { right: 0 },
1371
- role: "columnheader",
1372
- children: /* @__PURE__ */ jsx34("span", { className: srOnly2, children: "Actions" })
1373
- }
1374
- ) : null
1375
- ] }),
1376
- 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) => {
1377
- const index = startIndex + i;
1378
- const id = getRowId(rowData);
1379
- const isSelected = selectedIds.has(id);
1380
- const isEditing = editingRowId === id;
1381
- const style = { top: index * rowHeight, height: rowHeight, width: totalWidth };
1382
- return /* @__PURE__ */ jsxs17(
1383
- "div",
1384
- {
1385
- className: row,
1386
- style,
1387
- role: "row",
1388
- "data-selected": isSelected || void 0,
1389
- "data-editing": isEditing || void 0,
1390
- children: [
1391
- selectable ? /* @__PURE__ */ jsx34("div", { className: [checkboxCell, pinnedCell].join(" "), style: { left: 0 }, role: "cell", children: /* @__PURE__ */ jsx34(
1392
- "input",
1393
- {
1394
- type: "checkbox",
1395
- checked: isSelected,
1396
- onChange: () => toggleRow(id),
1397
- "aria-label": `Select row ${index + 1}`
1398
- }
1399
- ) }) : null,
1400
- layout.ordered.map((column) => /* @__PURE__ */ jsx34(
1401
- "div",
1402
- {
1403
- className: [cell, pinnedClass(column)].filter(Boolean).join(" "),
1404
- style: { width: widthOf(column), ...pinnedStyle(column) },
1405
- role: "cell",
1406
- children: isEditing && column.editable ? /* @__PURE__ */ jsx34(
1407
- "input",
1408
- {
1409
- type: "text",
1410
- className: editInput,
1411
- value: editDraft[column.key] ?? "",
1412
- onChange: (e) => setEditDraft((prev) => ({ ...prev, [column.key]: e.target.value })),
1413
- onKeyDown: (e) => {
1414
- if (e.key === "Enter") saveEditing();
1415
- if (e.key === "Escape") cancelEditing();
1416
- },
1417
- "aria-label": `Edit ${String(column.header)}`,
1418
- autoFocus: column.key === columns.find((c) => c.editable)?.key
1419
- }
1420
- ) : column.render ? column.render(rowData) : String(getCellValue(column, rowData) ?? "")
1421
- },
1422
- column.key
1423
- )),
1424
- editingEnabled ? /* @__PURE__ */ jsx34(
1425
- "div",
1426
- {
1427
- className: [actionsCell, pinnedCell].join(" "),
1428
- style: { right: 0 },
1429
- role: "cell",
1430
- children: isEditing ? /* @__PURE__ */ jsxs17(Fragment5, { children: [
1431
- /* @__PURE__ */ jsx34(
1432
- "button",
1433
- {
1434
- type: "button",
1435
- className: iconButton,
1436
- onClick: saveEditing,
1437
- "aria-label": `Save row ${index + 1}`,
1438
- children: "\u2713"
1439
- }
1440
- ),
1441
- /* @__PURE__ */ jsx34(
1442
- "button",
1443
+ className: [checkboxCell, pinnedHeaderCell].join(" "),
1444
+ style: { left: 0 },
1445
+ role: "columnheader",
1446
+ children: /* @__PURE__ */ jsx34(
1447
+ "input",
1443
1448
  {
1444
- type: "button",
1445
- className: iconButton,
1446
- onClick: cancelEditing,
1447
- "aria-label": `Cancel editing row ${index + 1}`,
1448
- children: "\u2715"
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"
1449
1456
  }
1450
1457
  )
1451
- ] }) : /* @__PURE__ */ jsx34(
1452
- "button",
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",
1453
1466
  {
1454
- type: "button",
1455
- className: iconButton,
1456
- onClick: () => startEditing(rowData),
1457
- "aria-label": `Edit row ${index + 1}`,
1458
- children: "\u270E"
1459
- }
1460
- )
1461
- }
1462
- ) : null
1463
- ]
1464
- },
1465
- id
1466
- );
1467
- }) })
1468
- ]
1469
- }
1470
- );
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(
1502
+ "div",
1503
+ {
1504
+ className: [actionsCell, pinnedHeaderCell].join(" "),
1505
+ style: { right: 0 },
1506
+ role: "columnheader",
1507
+ children: /* @__PURE__ */ jsx34("span", { className: srOnly2, children: "Actions" })
1508
+ }
1509
+ ) : null
1510
+ ]
1511
+ }
1512
+ ),
1513
+ hasFilterableColumns ? /* @__PURE__ */ jsxs17(
1514
+ "div",
1515
+ {
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
+ ]
1573
+ }
1574
+ ) : null,
1575
+ sortedData.length === 0 ? /* @__PURE__ */ jsx34("div", { className: emptyState, children: emptyMessage }) : /* @__PURE__ */ jsx34(
1576
+ "div",
1577
+ {
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
+ ] });
1471
1713
  }
1472
1714
 
1473
1715
  // src/index.ts