@vesture/react 0.1.0 → 0.1.1

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
@@ -1082,22 +1082,31 @@ function Alert({
1082
1082
  import { useCallback as useCallback2, 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__7xivx7i";
1091
+ var emptyState = "DataGrid_emptyState__7xivx7b";
1092
+ var headerButton = "DataGrid_headerButton__7xivx74";
1093
+ var headerCell = "DataGrid_headerCell__7xivx73";
1094
+ var headerRow = "DataGrid_headerRow__7xivx72";
1095
+ var iconButton = "DataGrid_iconButton__7xivx7h";
1096
+ var pinnedCell = "DataGrid_pinnedCell__7xivx7c";
1097
+ var pinnedHeaderCell = "DataGrid_pinnedHeaderCell__7xivx7d";
1098
+ var pinnedLeftEdge = "DataGrid_pinnedLeftEdge__7xivx7e";
1099
+ var pinnedRightEdge = "DataGrid_pinnedRightEdge__7xivx7f";
1100
+ var resizeHandle = "DataGrid_resizeHandle__7xivx76";
1101
+ var row = "DataGrid_row__7xivx78";
1102
+ var sortIcon = "DataGrid_sortIcon__7xivx75";
1103
+ var srOnly2 = "DataGrid_srOnly__7xivx70";
1096
1104
 
1097
1105
  // src/components/DataGrid/DataGrid.tsx
1098
- import { jsx as jsx34, jsxs as jsxs17 } from "react/jsx-runtime";
1106
+ import { Fragment as Fragment5, jsx as jsx34, jsxs as jsxs17 } from "react/jsx-runtime";
1099
1107
  var DEFAULT_COLUMN_WIDTH = 160;
1100
1108
  var CHECKBOX_COLUMN_WIDTH = 44;
1109
+ var ACTIONS_COLUMN_WIDTH = 72;
1101
1110
  function compareValues(a, b) {
1102
1111
  if (typeof a === "number" && typeof b === "number") {
1103
1112
  return a - b;
@@ -1122,7 +1131,8 @@ function DataGrid({
1122
1131
  onSelectionChange,
1123
1132
  sort: controlledSort,
1124
1133
  onSortChange,
1125
- emptyMessage = "No data"
1134
+ emptyMessage = "No data",
1135
+ onRowEdit
1126
1136
  }) {
1127
1137
  const [uncontrolledSort, setUncontrolledSort] = useState8(null);
1128
1138
  const [uncontrolledSelectedIds, setUncontrolledSelectedIds] = useState8(() => /* @__PURE__ */ new Set());
@@ -1130,6 +1140,9 @@ function DataGrid({
1130
1140
  () => Object.fromEntries(columns.map((c) => [c.key, c.width ?? DEFAULT_COLUMN_WIDTH]))
1131
1141
  );
1132
1142
  const [scrollTop, setScrollTop] = useState8(0);
1143
+ const [editingRowId, setEditingRowId] = useState8(null);
1144
+ const [editDraft, setEditDraft] = useState8({});
1145
+ const editingEnabled = Boolean(onRowEdit);
1133
1146
  const sort = controlledSort !== void 0 ? controlledSort : uncontrolledSort;
1134
1147
  const selectedIds = controlledSelectedIds ?? uncontrolledSelectedIds;
1135
1148
  const setSort = (next) => {
@@ -1211,12 +1224,91 @@ function DataGrid({
1211
1224
  resizeState.current = null;
1212
1225
  event.currentTarget.releasePointerCapture(event.pointerId);
1213
1226
  }, []);
1227
+ const widthOf = useCallback2(
1228
+ (column) => columnWidths[column.key] ?? column.width ?? DEFAULT_COLUMN_WIDTH,
1229
+ [columnWidths]
1230
+ );
1231
+ const layout = useMemo2(() => {
1232
+ const leftPinned = columns.filter((c) => c.pinned === "left");
1233
+ const rightPinned = columns.filter((c) => c.pinned === "right");
1234
+ const middle = columns.filter((c) => !c.pinned);
1235
+ const ordered = [...leftPinned, ...middle, ...rightPinned];
1236
+ const leftOffsets = /* @__PURE__ */ new Map();
1237
+ let leftAcc = selectable ? CHECKBOX_COLUMN_WIDTH : 0;
1238
+ for (const column of leftPinned) {
1239
+ leftOffsets.set(column.key, leftAcc);
1240
+ leftAcc += widthOf(column);
1241
+ }
1242
+ const rightOffsets = /* @__PURE__ */ new Map();
1243
+ let rightAcc = editingEnabled ? ACTIONS_COLUMN_WIDTH : 0;
1244
+ for (let i = rightPinned.length - 1; i >= 0; i--) {
1245
+ const column = rightPinned[i];
1246
+ rightOffsets.set(column.key, rightAcc);
1247
+ rightAcc += widthOf(column);
1248
+ }
1249
+ return {
1250
+ ordered,
1251
+ leftOffsets,
1252
+ rightOffsets,
1253
+ lastLeftKey: leftPinned[leftPinned.length - 1]?.key,
1254
+ firstRightKey: rightPinned[0]?.key
1255
+ };
1256
+ }, [columns, selectable, editingEnabled, widthOf]);
1257
+ const pinnedStyle = (column) => {
1258
+ if (column.pinned === "left" && layout.leftOffsets.has(column.key)) {
1259
+ return { left: layout.leftOffsets.get(column.key) };
1260
+ }
1261
+ if (column.pinned === "right" && layout.rightOffsets.has(column.key)) {
1262
+ return { right: layout.rightOffsets.get(column.key) };
1263
+ }
1264
+ return void 0;
1265
+ };
1266
+ const pinnedClass = (column) => {
1267
+ const classes = [];
1268
+ if (column.pinned) {
1269
+ classes.push(pinnedCell);
1270
+ if (column.key === layout.lastLeftKey) classes.push(pinnedLeftEdge);
1271
+ if (column.key === layout.firstRightKey) classes.push(pinnedRightEdge);
1272
+ }
1273
+ return classes.join(" ");
1274
+ };
1275
+ const headerPinnedClass = (column) => {
1276
+ const classes = [];
1277
+ if (column.pinned) {
1278
+ classes.push(pinnedHeaderCell);
1279
+ if (column.key === layout.lastLeftKey) classes.push(pinnedLeftEdge);
1280
+ if (column.key === layout.firstRightKey) classes.push(pinnedRightEdge);
1281
+ }
1282
+ return classes.join(" ");
1283
+ };
1284
+ const startEditing = (rowData) => {
1285
+ const id = getRowId(rowData);
1286
+ const draft = {};
1287
+ for (const column of columns) {
1288
+ if (column.editable) {
1289
+ draft[column.key] = String(getCellValue(column, rowData) ?? "");
1290
+ }
1291
+ }
1292
+ setEditDraft(draft);
1293
+ setEditingRowId(id);
1294
+ };
1295
+ const cancelEditing = () => {
1296
+ setEditingRowId(null);
1297
+ setEditDraft({});
1298
+ };
1299
+ const saveEditing = () => {
1300
+ if (editingRowId) {
1301
+ onRowEdit?.(editingRowId, editDraft);
1302
+ }
1303
+ setEditingRowId(null);
1304
+ setEditDraft({});
1305
+ };
1214
1306
  const totalHeight = sortedData.length * rowHeight;
1215
1307
  const visibleCount = Math.ceil(height / rowHeight) + overscan * 2;
1216
1308
  const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
1217
1309
  const endIndex = Math.min(sortedData.length, startIndex + visibleCount);
1218
1310
  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);
1311
+ const totalWidth = (selectable ? CHECKBOX_COLUMN_WIDTH : 0) + (editingEnabled ? ACTIONS_COLUMN_WIDTH : 0) + columns.reduce((sum, c) => sum + widthOf(c), 0);
1220
1312
  return /* @__PURE__ */ jsxs17(
1221
1313
  "div",
1222
1314
  {
@@ -1226,7 +1318,7 @@ function DataGrid({
1226
1318
  role: "table",
1227
1319
  children: [
1228
1320
  /* @__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(
1321
+ selectable ? /* @__PURE__ */ jsx34("div", { className: [checkboxCell, pinnedHeaderCell].join(" "), style: { left: 0 }, role: "columnheader", children: /* @__PURE__ */ jsx34(
1230
1322
  "input",
1231
1323
  {
1232
1324
  type: "checkbox",
@@ -1238,15 +1330,15 @@ function DataGrid({
1238
1330
  "aria-label": "Select all rows"
1239
1331
  }
1240
1332
  ) }) : null,
1241
- columns.map((column) => {
1242
- const width = columnWidths[column.key] ?? DEFAULT_COLUMN_WIDTH;
1333
+ layout.ordered.map((column) => {
1334
+ const width = widthOf(column);
1243
1335
  const isSorted = sort?.key === column.key;
1244
1336
  const direction2 = isSorted ? sort.direction : null;
1245
1337
  return /* @__PURE__ */ jsxs17(
1246
1338
  "div",
1247
1339
  {
1248
- className: headerCell,
1249
- style: { width },
1340
+ className: [headerCell, headerPinnedClass(column)].filter(Boolean).join(" "),
1341
+ style: { width, ...pinnedStyle(column) },
1250
1342
  role: "columnheader",
1251
1343
  "aria-sort": direction2 === "asc" ? "ascending" : direction2 === "desc" ? "descending" : "none",
1252
1344
  children: [
@@ -1270,34 +1362,108 @@ function DataGrid({
1270
1362
  },
1271
1363
  column.key
1272
1364
  );
1273
- })
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
1274
1375
  ] }),
1275
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) => {
1276
1377
  const index = startIndex + i;
1277
1378
  const id = getRowId(rowData);
1278
1379
  const isSelected = selectedIds.has(id);
1380
+ const isEditing = editingRowId === id;
1279
1381
  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",
1283
- {
1284
- type: "checkbox",
1285
- checked: isSelected,
1286
- onChange: () => toggleRow(id),
1287
- "aria-label": `Select row ${index + 1}`
1288
- }
1289
- ) }) : null,
1290
- columns.map((column) => /* @__PURE__ */ jsx34(
1291
- "div",
1292
- {
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);
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
+ {
1444
+ type: "button",
1445
+ className: iconButton,
1446
+ onClick: cancelEditing,
1447
+ "aria-label": `Cancel editing row ${index + 1}`,
1448
+ children: "\u2715"
1449
+ }
1450
+ )
1451
+ ] }) : /* @__PURE__ */ jsx34(
1452
+ "button",
1453
+ {
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
+ );
1301
1467
  }) })
1302
1468
  ]
1303
1469
  }