@dreamtree-org/twreact-ui 1.0.72 → 1.0.74

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
@@ -276,6 +276,26 @@ const ChevronUp = createLucideIcon$1("ChevronUp", [
276
276
  */
277
277
 
278
278
 
279
+ const ChevronsLeft = createLucideIcon$1("ChevronsLeft", [
280
+ ["path", { d: "m11 17-5-5 5-5", key: "13zhaf" }],
281
+ ["path", { d: "m18 17-5-5 5-5", key: "h8a8et" }]
282
+ ]);
283
+
284
+ /**
285
+ * lucide-react v0.0.1 - ISC
286
+ */
287
+
288
+
289
+ const ChevronsRight = createLucideIcon$1("ChevronsRight", [
290
+ ["path", { d: "m6 17 5-5-5-5", key: "xnjwq" }],
291
+ ["path", { d: "m13 17 5-5-5-5", key: "17xmmf" }]
292
+ ]);
293
+
294
+ /**
295
+ * lucide-react v0.0.1 - ISC
296
+ */
297
+
298
+
279
299
  const EyeOff = createLucideIcon$1("EyeOff", [
280
300
  ["path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24", key: "1jxqfv" }],
281
301
  [
@@ -4146,9 +4166,10 @@ var Table = function Table(_ref) {
4146
4166
  React.useEffect(function () {
4147
4167
  if (typeof pageNumber === "number" && pageNumber !== currentPage) {
4148
4168
  setCurrentPage(pageNumber);
4149
- onPageChange === null || onPageChange === void 0 || onPageChange(pageNumber);
4169
+ // Note: onPageChange is intentionally not called here to avoid loops
4170
+ // Parent should handle page changes via controlled component pattern
4150
4171
  }
4151
- }, [pageNumber, currentPage, onPageChange]);
4172
+ }, [pageNumber, currentPage]);
4152
4173
  React.useEffect(function () {
4153
4174
  setLimit(pageSize);
4154
4175
  }, [pageSize]);
@@ -4214,6 +4235,12 @@ var Table = function Table(_ref) {
4214
4235
  });
4215
4236
  return map;
4216
4237
  }, [filteredData]);
4238
+
4239
+ // Wrapper for setCurrentPage that also calls onPageChange
4240
+ var handlePageChange = React.useCallback(function (newPage) {
4241
+ setCurrentPage(newPage);
4242
+ onPageChange === null || onPageChange === void 0 || onPageChange(newPage);
4243
+ }, [onPageChange]);
4217
4244
  var toggleColumnVisibility = React.useCallback(function (key) {
4218
4245
  setColumnsState(function (prev) {
4219
4246
  return prev.map(function (c) {
@@ -4222,8 +4249,8 @@ var Table = function Table(_ref) {
4222
4249
  }) : c;
4223
4250
  });
4224
4251
  });
4225
- setCurrentPage(1);
4226
- }, []);
4252
+ handlePageChange(1);
4253
+ }, [handlePageChange]);
4227
4254
 
4228
4255
  // Handle actions
4229
4256
  var handleOnAction = React.useCallback(function (action, row) {
@@ -4257,13 +4284,26 @@ var Table = function Table(_ref) {
4257
4284
  // Filtering
4258
4285
  var filteredData = React.useMemo(function () {
4259
4286
  if (serverSide || !filterable || !Object.keys(filters).length) return sortedData;
4260
- var q = (filters.global || "").toLowerCase();
4261
4287
  return sortedData.filter(function (row) {
4262
- return Object.values(row || {}).some(function (v) {
4263
- return String(v).toLowerCase().includes(q);
4288
+ // Apply global search filter if present
4289
+ if (filters.global) {
4290
+ var q = filters.global.toLowerCase();
4291
+ var matchesGlobal = Object.values(row || {}).some(function (v) {
4292
+ return String(v).toLowerCase().includes(q);
4293
+ });
4294
+ if (!matchesGlobal) return false;
4295
+ }
4296
+
4297
+ // Apply column-specific filters
4298
+ return visibleColumns.every(function (column) {
4299
+ var columnFilter = filters[column.key];
4300
+ if (!columnFilter) return true; // No filter for this column
4301
+
4302
+ var cellValue = row === null || row === void 0 ? void 0 : row[column.key];
4303
+ return String(cellValue || "").toLowerCase().includes(columnFilter.toLowerCase());
4264
4304
  });
4265
4305
  });
4266
- }, [sortedData, filters, filterable, serverSide]);
4306
+ }, [sortedData, filters, filterable, serverSide, visibleColumns]);
4267
4307
 
4268
4308
  // Pagination indices - memoized
4269
4309
  var _useMemo = React.useMemo(function () {
@@ -4350,8 +4390,8 @@ var Table = function Table(_ref) {
4350
4390
  onFilterChange === null || onFilterChange === void 0 || onFilterChange(newFilters);
4351
4391
  return newFilters;
4352
4392
  });
4353
- setCurrentPage(1);
4354
- }, [onFilter, onFilterChange]);
4393
+ handlePageChange(1);
4394
+ }, [onFilter, onFilterChange, handlePageChange]);
4355
4395
  var renderCell = React.useCallback(function (column, row, globalIndex) {
4356
4396
  if (column.render) return column.render(row, globalIndex);
4357
4397
  return row === null || row === void 0 ? void 0 : row[column.key];
@@ -4380,9 +4420,9 @@ var Table = function Table(_ref) {
4380
4420
  setActionAnchor(null);
4381
4421
  };
4382
4422
  var onScrollResize = function onScrollResize() {
4383
- setOpenActionKey(function (k) {
4384
- return k ? k : null;
4385
- });
4423
+ // Close menu on scroll/resize to prevent positioning issues
4424
+ setOpenActionKey(null);
4425
+ setActionAnchor(null);
4386
4426
  };
4387
4427
  window.addEventListener("click", onDocClick);
4388
4428
  window.addEventListener("scroll", onScrollResize, true);
@@ -4399,6 +4439,7 @@ var Table = function Table(_ref) {
4399
4439
  var isFetchingRef = React.useRef(false);
4400
4440
  var isMountedRef = React.useRef(true);
4401
4441
  var onFetchRef = React.useRef(onFetch);
4442
+ var activeFetchIdRef = React.useRef(0);
4402
4443
 
4403
4444
  // Keep ref in sync with onFetch
4404
4445
  React.useEffect(function () {
@@ -4414,7 +4455,7 @@ var Table = function Table(_ref) {
4414
4455
  };
4415
4456
  }, []);
4416
4457
 
4417
- // Serialize fetch params for comparison
4458
+ // Serialize fetch params for comparison - serialize filters to avoid object reference issues
4418
4459
  var fetchParams = React.useMemo(function () {
4419
4460
  return {
4420
4461
  filters: filters,
@@ -4436,36 +4477,59 @@ var Table = function Table(_ref) {
4436
4477
  if (!isFetchingRef.current && prevFetchParamsRef.current !== fetchParamsString) {
4437
4478
  prevFetchParamsRef.current = fetchParamsString;
4438
4479
  isFetchingRef.current = true;
4439
- currentOnFetch({
4440
- setData: function setData(rows) {
4441
- // Only update if component is still mounted
4442
- if (isMountedRef.current && isFetchingRef.current) {
4443
- setTableData(Array.isArray(rows) ? rows : []);
4444
- isFetchingRef.current = false;
4480
+
4481
+ // Track fetch ID to prevent race conditions
4482
+ var fetchId = Date.now();
4483
+ activeFetchIdRef.current = fetchId;
4484
+ try {
4485
+ var result = currentOnFetch({
4486
+ setData: function setData(rows) {
4487
+ // Only update if component is still mounted and this is the active fetch
4488
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4489
+ setTableData(Array.isArray(rows) ? rows : []);
4490
+ // Don't set isFetchingRef to false here - let setLoading handle it
4491
+ }
4492
+ },
4493
+ setLoading: function setLoading(loading) {
4494
+ // Only update if component is still mounted and this is the active fetch
4495
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4496
+ _setLoading(loading);
4497
+ if (!loading) {
4498
+ isFetchingRef.current = false;
4499
+ }
4500
+ }
4501
+ },
4502
+ filters: fetchParams.filters,
4503
+ page: fetchParams.page,
4504
+ limit: fetchParams.limit,
4505
+ sort: {
4506
+ key: fetchParams.sortKey,
4507
+ direction: fetchParams.sortDirection
4445
4508
  }
4446
- },
4447
- setLoading: function setLoading(loading) {
4448
- // Only update if component is still mounted
4449
- if (isMountedRef.current) {
4450
- _setLoading(loading);
4451
- if (!loading && isFetchingRef.current) {
4509
+ });
4510
+
4511
+ // Handle promise if onFetch returns one
4512
+ if (result && typeof result.then === "function") {
4513
+ result["catch"](function (error) {
4514
+ console.error("Table onFetch error:", error);
4515
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4516
+ _setLoading(false);
4452
4517
  isFetchingRef.current = false;
4453
4518
  }
4454
- }
4455
- },
4456
- filters: fetchParams.filters,
4457
- page: fetchParams.page,
4458
- limit: fetchParams.limit,
4459
- sort: {
4460
- key: fetchParams.sortKey,
4461
- direction: fetchParams.sortDirection
4519
+ });
4462
4520
  }
4463
- });
4521
+ } catch (error) {
4522
+ console.error("Table onFetch error:", error);
4523
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4524
+ _setLoading(false);
4525
+ isFetchingRef.current = false;
4526
+ }
4527
+ }
4464
4528
  }
4465
4529
  } else if (!serverSide && Array.isArray(data)) {
4466
4530
  setTableData(data);
4467
4531
  }
4468
- }, [serverSide, data, fetchParamsString, fetchParams]);
4532
+ }, [serverSide, data, fetchParamsString]);
4469
4533
 
4470
4534
  // Global search
4471
4535
  React.useEffect(function () {
@@ -4484,9 +4548,9 @@ var Table = function Table(_ref) {
4484
4548
  var parsed = Number(nextLimit);
4485
4549
  if (!Number.isFinite(parsed) || parsed <= 0) return;
4486
4550
  setLimit(parsed);
4487
- setCurrentPage(1);
4551
+ handlePageChange(1);
4488
4552
  onLimitChange === null || onLimitChange === void 0 || onLimitChange(parsed);
4489
- }, [onLimitChange]);
4553
+ }, [onLimitChange, handlePageChange]);
4490
4554
 
4491
4555
  // Memoize limit options
4492
4556
  var limitOptionsMemo = React.useMemo(function () {
@@ -4523,9 +4587,9 @@ var Table = function Table(_ref) {
4523
4587
  var _getRowMetadata = getRowMetadata(row, rowIndexInPage),
4524
4588
  globalIndex = _getRowMetadata.globalIndex,
4525
4589
  key = _getRowMetadata.key,
4526
- actionCellKey = _getRowMetadata.actionCellKey;
4527
- _getRowMetadata.extraRowClass;
4528
- var stripeBg = _getRowMetadata.stripeBg;
4590
+ actionCellKey = _getRowMetadata.actionCellKey,
4591
+ extraRowClass = _getRowMetadata.extraRowClass,
4592
+ stripeBg = _getRowMetadata.stripeBg;
4529
4593
  return /*#__PURE__*/React.createElement("div", {
4530
4594
  key: key,
4531
4595
  style: stripeBg ? {
@@ -4535,7 +4599,7 @@ var Table = function Table(_ref) {
4535
4599
  "cursor-pointer": !!onRowClick,
4536
4600
  "bg-primary-50 border-primary-200": selectedRows.has(key),
4537
4601
  "hover:shadow-md": !selectedRows.has(key)
4538
- }, safeExtraRowClass),
4602
+ }, extraRowClass),
4539
4603
  onClick: function onClick() {
4540
4604
  return onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row, globalIndex);
4541
4605
  }
@@ -4612,10 +4676,10 @@ var Table = function Table(_ref) {
4612
4676
  row: row,
4613
4677
  index: globalIndex
4614
4678
  })));
4615
- }, [getRowMetadata, hasDetails, selectable, showSerial, withAction, expandedRows, selectedRows, onRowClick, visibleColumns, renderCell, cellClass, DetailsComponent, actionsToUse, actionAnchor, openActionKey, handleOnAction, actionMenuRef]);
4679
+ }, [getRowMetadata, hasDetails, selectable, showSerial, withAction, expandedRows, selectedRows, onRowClick, visibleColumns, renderCell, cellClass, DetailsComponent, actionsToUse, actionAnchor, openActionKey, handleOnAction, actionMenuRef, toggleActions, toggleExpandRow, handleSelectRow]);
4616
4680
 
4617
4681
  // Render mobile filters
4618
- var renderMobileFilters = function renderMobileFilters() {
4682
+ var renderMobileFilters = React.useCallback(function () {
4619
4683
  return /*#__PURE__*/React.createElement("div", {
4620
4684
  className: "mb-4 p-3 bg-gray-50 rounded-lg"
4621
4685
  }, /*#__PURE__*/React.createElement("div", {
@@ -4657,7 +4721,7 @@ var Table = function Table(_ref) {
4657
4721
  }
4658
4722
  }));
4659
4723
  })));
4660
- };
4724
+ }, [showMobileFilters, globalSearch, searchInput, onGlobalKeyDown, filterable, visibleColumns, filters, handleFilter, setShowMobileFilters, setSearchInput]);
4661
4725
 
4662
4726
  // Render
4663
4727
  return /*#__PURE__*/React.createElement("div", {
@@ -4700,24 +4764,42 @@ var Table = function Table(_ref) {
4700
4764
  className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4701
4765
  disabled: currentPage === 1,
4702
4766
  onClick: function onClick() {
4703
- return setCurrentPage(function (p) {
4704
- return Math.max(1, p - 1);
4705
- });
4767
+ return handlePageChange(1);
4768
+ },
4769
+ "aria-label": "First page",
4770
+ title: "First page"
4771
+ }, /*#__PURE__*/React.createElement(ChevronsLeft, {
4772
+ className: "size-4 md:size-5 text-gray-800"
4773
+ })), /*#__PURE__*/React.createElement("button", {
4774
+ className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4775
+ disabled: currentPage === 1,
4776
+ onClick: function onClick() {
4777
+ return handlePageChange(Math.max(1, currentPage - 1));
4706
4778
  },
4707
- "aria-label": "Previous page"
4779
+ "aria-label": "Previous page",
4780
+ title: "Previous page"
4708
4781
  }, /*#__PURE__*/React.createElement(ChevronLeft, {
4709
4782
  className: "size-4 md:size-5 text-gray-800"
4710
4783
  })), /*#__PURE__*/React.createElement("button", {
4711
4784
  className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4712
4785
  disabled: currentPage === totalPages,
4713
4786
  onClick: function onClick() {
4714
- return setCurrentPage(function (p) {
4715
- return Math.min(totalPages, p + 1);
4716
- });
4787
+ return handlePageChange(Math.min(totalPages, currentPage + 1));
4717
4788
  },
4718
- "aria-label": "Next page"
4789
+ "aria-label": "Next page",
4790
+ title: "Next page"
4719
4791
  }, /*#__PURE__*/React.createElement(ChevronRight, {
4720
4792
  className: "size-4 md:size-5 text-gray-800"
4793
+ })), /*#__PURE__*/React.createElement("button", {
4794
+ className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4795
+ disabled: currentPage === totalPages,
4796
+ onClick: function onClick() {
4797
+ return handlePageChange(totalPages);
4798
+ },
4799
+ "aria-label": "Last page",
4800
+ title: "Last page"
4801
+ }, /*#__PURE__*/React.createElement(ChevronsRight, {
4802
+ className: "size-4 md:size-5 text-gray-800"
4721
4803
  })))))), globalSearch && /*#__PURE__*/React.createElement("div", {
4722
4804
  className: "p-4 border-b"
4723
4805
  }, /*#__PURE__*/React.createElement("div", {
@@ -4967,12 +5049,21 @@ var Table = function Table(_ref) {
4967
5049
  }, "Page ", currentPage, " of ", totalPages), /*#__PURE__*/React.createElement("div", {
4968
5050
  className: "flex items-center gap-2"
4969
5051
  }, /*#__PURE__*/React.createElement("button", {
5052
+ className: "rounded-lg md:rounded-md border border-gray-300 px-3 py-2 text-sm disabled:opacity-50 hover:bg-gray-50 flex items-center gap-1",
5053
+ disabled: currentPage === 1,
5054
+ onClick: function onClick() {
5055
+ return handlePageChange(1);
5056
+ },
5057
+ title: "First page"
5058
+ }, /*#__PURE__*/React.createElement(ChevronsLeft, {
5059
+ className: "size-4"
5060
+ }), /*#__PURE__*/React.createElement("span", {
5061
+ className: "hidden sm:inline"
5062
+ }, "First")), /*#__PURE__*/React.createElement("button", {
4970
5063
  className: "rounded-lg md:rounded-md border border-gray-300 px-4 py-2 text-sm disabled:opacity-50 hover:bg-gray-50",
4971
5064
  disabled: currentPage === 1,
4972
5065
  onClick: function onClick() {
4973
- return setCurrentPage(function (p) {
4974
- return Math.max(1, p - 1);
4975
- });
5066
+ return handlePageChange(Math.max(1, currentPage - 1));
4976
5067
  }
4977
5068
  }, "Previous"), /*#__PURE__*/React.createElement("div", {
4978
5069
  className: "flex items-center gap-1"
@@ -4984,18 +5075,27 @@ var Table = function Table(_ref) {
4984
5075
  "border border-gray-300 hover:bg-gray-50": pageNum !== currentPage
4985
5076
  }),
4986
5077
  onClick: function onClick() {
4987
- return setCurrentPage(pageNum);
5078
+ return handlePageChange(pageNum);
4988
5079
  }
4989
5080
  }, pageNum);
4990
5081
  })), /*#__PURE__*/React.createElement("button", {
4991
5082
  className: "rounded-lg md:rounded-md border border-gray-300 px-4 py-2 text-sm disabled:opacity-50 hover:bg-gray-50",
4992
5083
  disabled: currentPage === totalPages,
4993
5084
  onClick: function onClick() {
4994
- return setCurrentPage(function (p) {
4995
- return Math.min(totalPages, p + 1);
4996
- });
5085
+ return handlePageChange(Math.min(totalPages, currentPage + 1));
4997
5086
  }
4998
- }, "Next")))));
5087
+ }, "Next"), /*#__PURE__*/React.createElement("button", {
5088
+ className: "rounded-lg md:rounded-md border border-gray-300 px-3 py-2 text-sm disabled:opacity-50 hover:bg-gray-50 flex items-center gap-1",
5089
+ disabled: currentPage === totalPages,
5090
+ onClick: function onClick() {
5091
+ return handlePageChange(totalPages);
5092
+ },
5093
+ title: "Last page"
5094
+ }, /*#__PURE__*/React.createElement("span", {
5095
+ className: "hidden sm:inline"
5096
+ }, "Last"), /*#__PURE__*/React.createElement(ChevronsRight, {
5097
+ className: "size-4"
5098
+ }))))));
4999
5099
  };
5000
5100
 
5001
5101
  // ActionMenuPortal constants
@@ -5109,7 +5209,8 @@ function ActionMenuPortal(_ref2) {
5109
5209
  window.removeEventListener("scroll", onScrollOrResize, true);
5110
5210
  };
5111
5211
  // Recompute when anchor element changes or actions change (content height may change).
5112
- }, [anchorElem, actions, menuRef]);
5212
+ // Note: menuRef is a ref object and never changes, so it's safe to include
5213
+ }, [anchorElem, actions]);
5113
5214
  return /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/React.createElement("div", {
5114
5215
  ref: menuRef,
5115
5216
  style: {