@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.esm.js CHANGED
@@ -256,6 +256,26 @@ const ChevronUp = createLucideIcon$1("ChevronUp", [
256
256
  */
257
257
 
258
258
 
259
+ const ChevronsLeft = createLucideIcon$1("ChevronsLeft", [
260
+ ["path", { d: "m11 17-5-5 5-5", key: "13zhaf" }],
261
+ ["path", { d: "m18 17-5-5 5-5", key: "h8a8et" }]
262
+ ]);
263
+
264
+ /**
265
+ * lucide-react v0.0.1 - ISC
266
+ */
267
+
268
+
269
+ const ChevronsRight = createLucideIcon$1("ChevronsRight", [
270
+ ["path", { d: "m6 17 5-5-5-5", key: "xnjwq" }],
271
+ ["path", { d: "m13 17 5-5-5-5", key: "17xmmf" }]
272
+ ]);
273
+
274
+ /**
275
+ * lucide-react v0.0.1 - ISC
276
+ */
277
+
278
+
259
279
  const EyeOff = createLucideIcon$1("EyeOff", [
260
280
  ["path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24", key: "1jxqfv" }],
261
281
  [
@@ -4126,9 +4146,10 @@ var Table = function Table(_ref) {
4126
4146
  useEffect(function () {
4127
4147
  if (typeof pageNumber === "number" && pageNumber !== currentPage) {
4128
4148
  setCurrentPage(pageNumber);
4129
- onPageChange === null || onPageChange === void 0 || onPageChange(pageNumber);
4149
+ // Note: onPageChange is intentionally not called here to avoid loops
4150
+ // Parent should handle page changes via controlled component pattern
4130
4151
  }
4131
- }, [pageNumber, currentPage, onPageChange]);
4152
+ }, [pageNumber, currentPage]);
4132
4153
  useEffect(function () {
4133
4154
  setLimit(pageSize);
4134
4155
  }, [pageSize]);
@@ -4194,6 +4215,12 @@ var Table = function Table(_ref) {
4194
4215
  });
4195
4216
  return map;
4196
4217
  }, [filteredData]);
4218
+
4219
+ // Wrapper for setCurrentPage that also calls onPageChange
4220
+ var handlePageChange = useCallback(function (newPage) {
4221
+ setCurrentPage(newPage);
4222
+ onPageChange === null || onPageChange === void 0 || onPageChange(newPage);
4223
+ }, [onPageChange]);
4197
4224
  var toggleColumnVisibility = useCallback(function (key) {
4198
4225
  setColumnsState(function (prev) {
4199
4226
  return prev.map(function (c) {
@@ -4202,8 +4229,8 @@ var Table = function Table(_ref) {
4202
4229
  }) : c;
4203
4230
  });
4204
4231
  });
4205
- setCurrentPage(1);
4206
- }, []);
4232
+ handlePageChange(1);
4233
+ }, [handlePageChange]);
4207
4234
 
4208
4235
  // Handle actions
4209
4236
  var handleOnAction = useCallback(function (action, row) {
@@ -4237,13 +4264,26 @@ var Table = function Table(_ref) {
4237
4264
  // Filtering
4238
4265
  var filteredData = useMemo(function () {
4239
4266
  if (serverSide || !filterable || !Object.keys(filters).length) return sortedData;
4240
- var q = (filters.global || "").toLowerCase();
4241
4267
  return sortedData.filter(function (row) {
4242
- return Object.values(row || {}).some(function (v) {
4243
- return String(v).toLowerCase().includes(q);
4268
+ // Apply global search filter if present
4269
+ if (filters.global) {
4270
+ var q = filters.global.toLowerCase();
4271
+ var matchesGlobal = Object.values(row || {}).some(function (v) {
4272
+ return String(v).toLowerCase().includes(q);
4273
+ });
4274
+ if (!matchesGlobal) return false;
4275
+ }
4276
+
4277
+ // Apply column-specific filters
4278
+ return visibleColumns.every(function (column) {
4279
+ var columnFilter = filters[column.key];
4280
+ if (!columnFilter) return true; // No filter for this column
4281
+
4282
+ var cellValue = row === null || row === void 0 ? void 0 : row[column.key];
4283
+ return String(cellValue || "").toLowerCase().includes(columnFilter.toLowerCase());
4244
4284
  });
4245
4285
  });
4246
- }, [sortedData, filters, filterable, serverSide]);
4286
+ }, [sortedData, filters, filterable, serverSide, visibleColumns]);
4247
4287
 
4248
4288
  // Pagination indices - memoized
4249
4289
  var _useMemo = useMemo(function () {
@@ -4330,8 +4370,8 @@ var Table = function Table(_ref) {
4330
4370
  onFilterChange === null || onFilterChange === void 0 || onFilterChange(newFilters);
4331
4371
  return newFilters;
4332
4372
  });
4333
- setCurrentPage(1);
4334
- }, [onFilter, onFilterChange]);
4373
+ handlePageChange(1);
4374
+ }, [onFilter, onFilterChange, handlePageChange]);
4335
4375
  var renderCell = useCallback(function (column, row, globalIndex) {
4336
4376
  if (column.render) return column.render(row, globalIndex);
4337
4377
  return row === null || row === void 0 ? void 0 : row[column.key];
@@ -4360,9 +4400,9 @@ var Table = function Table(_ref) {
4360
4400
  setActionAnchor(null);
4361
4401
  };
4362
4402
  var onScrollResize = function onScrollResize() {
4363
- setOpenActionKey(function (k) {
4364
- return k ? k : null;
4365
- });
4403
+ // Close menu on scroll/resize to prevent positioning issues
4404
+ setOpenActionKey(null);
4405
+ setActionAnchor(null);
4366
4406
  };
4367
4407
  window.addEventListener("click", onDocClick);
4368
4408
  window.addEventListener("scroll", onScrollResize, true);
@@ -4379,6 +4419,7 @@ var Table = function Table(_ref) {
4379
4419
  var isFetchingRef = useRef(false);
4380
4420
  var isMountedRef = useRef(true);
4381
4421
  var onFetchRef = useRef(onFetch);
4422
+ var activeFetchIdRef = useRef(0);
4382
4423
 
4383
4424
  // Keep ref in sync with onFetch
4384
4425
  useEffect(function () {
@@ -4394,7 +4435,7 @@ var Table = function Table(_ref) {
4394
4435
  };
4395
4436
  }, []);
4396
4437
 
4397
- // Serialize fetch params for comparison
4438
+ // Serialize fetch params for comparison - serialize filters to avoid object reference issues
4398
4439
  var fetchParams = useMemo(function () {
4399
4440
  return {
4400
4441
  filters: filters,
@@ -4416,36 +4457,59 @@ var Table = function Table(_ref) {
4416
4457
  if (!isFetchingRef.current && prevFetchParamsRef.current !== fetchParamsString) {
4417
4458
  prevFetchParamsRef.current = fetchParamsString;
4418
4459
  isFetchingRef.current = true;
4419
- currentOnFetch({
4420
- setData: function setData(rows) {
4421
- // Only update if component is still mounted
4422
- if (isMountedRef.current && isFetchingRef.current) {
4423
- setTableData(Array.isArray(rows) ? rows : []);
4424
- isFetchingRef.current = false;
4460
+
4461
+ // Track fetch ID to prevent race conditions
4462
+ var fetchId = Date.now();
4463
+ activeFetchIdRef.current = fetchId;
4464
+ try {
4465
+ var result = currentOnFetch({
4466
+ setData: function setData(rows) {
4467
+ // Only update if component is still mounted and this is the active fetch
4468
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4469
+ setTableData(Array.isArray(rows) ? rows : []);
4470
+ // Don't set isFetchingRef to false here - let setLoading handle it
4471
+ }
4472
+ },
4473
+ setLoading: function setLoading(loading) {
4474
+ // Only update if component is still mounted and this is the active fetch
4475
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4476
+ _setLoading(loading);
4477
+ if (!loading) {
4478
+ isFetchingRef.current = false;
4479
+ }
4480
+ }
4481
+ },
4482
+ filters: fetchParams.filters,
4483
+ page: fetchParams.page,
4484
+ limit: fetchParams.limit,
4485
+ sort: {
4486
+ key: fetchParams.sortKey,
4487
+ direction: fetchParams.sortDirection
4425
4488
  }
4426
- },
4427
- setLoading: function setLoading(loading) {
4428
- // Only update if component is still mounted
4429
- if (isMountedRef.current) {
4430
- _setLoading(loading);
4431
- if (!loading && isFetchingRef.current) {
4489
+ });
4490
+
4491
+ // Handle promise if onFetch returns one
4492
+ if (result && typeof result.then === "function") {
4493
+ result["catch"](function (error) {
4494
+ console.error("Table onFetch error:", error);
4495
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4496
+ _setLoading(false);
4432
4497
  isFetchingRef.current = false;
4433
4498
  }
4434
- }
4435
- },
4436
- filters: fetchParams.filters,
4437
- page: fetchParams.page,
4438
- limit: fetchParams.limit,
4439
- sort: {
4440
- key: fetchParams.sortKey,
4441
- direction: fetchParams.sortDirection
4499
+ });
4442
4500
  }
4443
- });
4501
+ } catch (error) {
4502
+ console.error("Table onFetch error:", error);
4503
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4504
+ _setLoading(false);
4505
+ isFetchingRef.current = false;
4506
+ }
4507
+ }
4444
4508
  }
4445
4509
  } else if (!serverSide && Array.isArray(data)) {
4446
4510
  setTableData(data);
4447
4511
  }
4448
- }, [serverSide, data, fetchParamsString, fetchParams]);
4512
+ }, [serverSide, data, fetchParamsString]);
4449
4513
 
4450
4514
  // Global search
4451
4515
  useEffect(function () {
@@ -4464,9 +4528,9 @@ var Table = function Table(_ref) {
4464
4528
  var parsed = Number(nextLimit);
4465
4529
  if (!Number.isFinite(parsed) || parsed <= 0) return;
4466
4530
  setLimit(parsed);
4467
- setCurrentPage(1);
4531
+ handlePageChange(1);
4468
4532
  onLimitChange === null || onLimitChange === void 0 || onLimitChange(parsed);
4469
- }, [onLimitChange]);
4533
+ }, [onLimitChange, handlePageChange]);
4470
4534
 
4471
4535
  // Memoize limit options
4472
4536
  var limitOptionsMemo = useMemo(function () {
@@ -4503,9 +4567,9 @@ var Table = function Table(_ref) {
4503
4567
  var _getRowMetadata = getRowMetadata(row, rowIndexInPage),
4504
4568
  globalIndex = _getRowMetadata.globalIndex,
4505
4569
  key = _getRowMetadata.key,
4506
- actionCellKey = _getRowMetadata.actionCellKey;
4507
- _getRowMetadata.extraRowClass;
4508
- var stripeBg = _getRowMetadata.stripeBg;
4570
+ actionCellKey = _getRowMetadata.actionCellKey,
4571
+ extraRowClass = _getRowMetadata.extraRowClass,
4572
+ stripeBg = _getRowMetadata.stripeBg;
4509
4573
  return /*#__PURE__*/React__default.createElement("div", {
4510
4574
  key: key,
4511
4575
  style: stripeBg ? {
@@ -4515,7 +4579,7 @@ var Table = function Table(_ref) {
4515
4579
  "cursor-pointer": !!onRowClick,
4516
4580
  "bg-primary-50 border-primary-200": selectedRows.has(key),
4517
4581
  "hover:shadow-md": !selectedRows.has(key)
4518
- }, safeExtraRowClass),
4582
+ }, extraRowClass),
4519
4583
  onClick: function onClick() {
4520
4584
  return onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row, globalIndex);
4521
4585
  }
@@ -4592,10 +4656,10 @@ var Table = function Table(_ref) {
4592
4656
  row: row,
4593
4657
  index: globalIndex
4594
4658
  })));
4595
- }, [getRowMetadata, hasDetails, selectable, showSerial, withAction, expandedRows, selectedRows, onRowClick, visibleColumns, renderCell, cellClass, DetailsComponent, actionsToUse, actionAnchor, openActionKey, handleOnAction, actionMenuRef]);
4659
+ }, [getRowMetadata, hasDetails, selectable, showSerial, withAction, expandedRows, selectedRows, onRowClick, visibleColumns, renderCell, cellClass, DetailsComponent, actionsToUse, actionAnchor, openActionKey, handleOnAction, actionMenuRef, toggleActions, toggleExpandRow, handleSelectRow]);
4596
4660
 
4597
4661
  // Render mobile filters
4598
- var renderMobileFilters = function renderMobileFilters() {
4662
+ var renderMobileFilters = useCallback(function () {
4599
4663
  return /*#__PURE__*/React__default.createElement("div", {
4600
4664
  className: "mb-4 p-3 bg-gray-50 rounded-lg"
4601
4665
  }, /*#__PURE__*/React__default.createElement("div", {
@@ -4637,7 +4701,7 @@ var Table = function Table(_ref) {
4637
4701
  }
4638
4702
  }));
4639
4703
  })));
4640
- };
4704
+ }, [showMobileFilters, globalSearch, searchInput, onGlobalKeyDown, filterable, visibleColumns, filters, handleFilter, setShowMobileFilters, setSearchInput]);
4641
4705
 
4642
4706
  // Render
4643
4707
  return /*#__PURE__*/React__default.createElement("div", {
@@ -4680,24 +4744,42 @@ var Table = function Table(_ref) {
4680
4744
  className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4681
4745
  disabled: currentPage === 1,
4682
4746
  onClick: function onClick() {
4683
- return setCurrentPage(function (p) {
4684
- return Math.max(1, p - 1);
4685
- });
4747
+ return handlePageChange(1);
4748
+ },
4749
+ "aria-label": "First page",
4750
+ title: "First page"
4751
+ }, /*#__PURE__*/React__default.createElement(ChevronsLeft, {
4752
+ className: "size-4 md:size-5 text-gray-800"
4753
+ })), /*#__PURE__*/React__default.createElement("button", {
4754
+ className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4755
+ disabled: currentPage === 1,
4756
+ onClick: function onClick() {
4757
+ return handlePageChange(Math.max(1, currentPage - 1));
4686
4758
  },
4687
- "aria-label": "Previous page"
4759
+ "aria-label": "Previous page",
4760
+ title: "Previous page"
4688
4761
  }, /*#__PURE__*/React__default.createElement(ChevronLeft, {
4689
4762
  className: "size-4 md:size-5 text-gray-800"
4690
4763
  })), /*#__PURE__*/React__default.createElement("button", {
4691
4764
  className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4692
4765
  disabled: currentPage === totalPages,
4693
4766
  onClick: function onClick() {
4694
- return setCurrentPage(function (p) {
4695
- return Math.min(totalPages, p + 1);
4696
- });
4767
+ return handlePageChange(Math.min(totalPages, currentPage + 1));
4697
4768
  },
4698
- "aria-label": "Next page"
4769
+ "aria-label": "Next page",
4770
+ title: "Next page"
4699
4771
  }, /*#__PURE__*/React__default.createElement(ChevronRight, {
4700
4772
  className: "size-4 md:size-5 text-gray-800"
4773
+ })), /*#__PURE__*/React__default.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 === totalPages,
4776
+ onClick: function onClick() {
4777
+ return handlePageChange(totalPages);
4778
+ },
4779
+ "aria-label": "Last page",
4780
+ title: "Last page"
4781
+ }, /*#__PURE__*/React__default.createElement(ChevronsRight, {
4782
+ className: "size-4 md:size-5 text-gray-800"
4701
4783
  })))))), globalSearch && /*#__PURE__*/React__default.createElement("div", {
4702
4784
  className: "p-4 border-b"
4703
4785
  }, /*#__PURE__*/React__default.createElement("div", {
@@ -4947,12 +5029,21 @@ var Table = function Table(_ref) {
4947
5029
  }, "Page ", currentPage, " of ", totalPages), /*#__PURE__*/React__default.createElement("div", {
4948
5030
  className: "flex items-center gap-2"
4949
5031
  }, /*#__PURE__*/React__default.createElement("button", {
5032
+ 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",
5033
+ disabled: currentPage === 1,
5034
+ onClick: function onClick() {
5035
+ return handlePageChange(1);
5036
+ },
5037
+ title: "First page"
5038
+ }, /*#__PURE__*/React__default.createElement(ChevronsLeft, {
5039
+ className: "size-4"
5040
+ }), /*#__PURE__*/React__default.createElement("span", {
5041
+ className: "hidden sm:inline"
5042
+ }, "First")), /*#__PURE__*/React__default.createElement("button", {
4950
5043
  className: "rounded-lg md:rounded-md border border-gray-300 px-4 py-2 text-sm disabled:opacity-50 hover:bg-gray-50",
4951
5044
  disabled: currentPage === 1,
4952
5045
  onClick: function onClick() {
4953
- return setCurrentPage(function (p) {
4954
- return Math.max(1, p - 1);
4955
- });
5046
+ return handlePageChange(Math.max(1, currentPage - 1));
4956
5047
  }
4957
5048
  }, "Previous"), /*#__PURE__*/React__default.createElement("div", {
4958
5049
  className: "flex items-center gap-1"
@@ -4964,18 +5055,27 @@ var Table = function Table(_ref) {
4964
5055
  "border border-gray-300 hover:bg-gray-50": pageNum !== currentPage
4965
5056
  }),
4966
5057
  onClick: function onClick() {
4967
- return setCurrentPage(pageNum);
5058
+ return handlePageChange(pageNum);
4968
5059
  }
4969
5060
  }, pageNum);
4970
5061
  })), /*#__PURE__*/React__default.createElement("button", {
4971
5062
  className: "rounded-lg md:rounded-md border border-gray-300 px-4 py-2 text-sm disabled:opacity-50 hover:bg-gray-50",
4972
5063
  disabled: currentPage === totalPages,
4973
5064
  onClick: function onClick() {
4974
- return setCurrentPage(function (p) {
4975
- return Math.min(totalPages, p + 1);
4976
- });
5065
+ return handlePageChange(Math.min(totalPages, currentPage + 1));
4977
5066
  }
4978
- }, "Next")))));
5067
+ }, "Next"), /*#__PURE__*/React__default.createElement("button", {
5068
+ 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",
5069
+ disabled: currentPage === totalPages,
5070
+ onClick: function onClick() {
5071
+ return handlePageChange(totalPages);
5072
+ },
5073
+ title: "Last page"
5074
+ }, /*#__PURE__*/React__default.createElement("span", {
5075
+ className: "hidden sm:inline"
5076
+ }, "Last"), /*#__PURE__*/React__default.createElement(ChevronsRight, {
5077
+ className: "size-4"
5078
+ }))))));
4979
5079
  };
4980
5080
 
4981
5081
  // ActionMenuPortal constants
@@ -5089,7 +5189,8 @@ function ActionMenuPortal(_ref2) {
5089
5189
  window.removeEventListener("scroll", onScrollOrResize, true);
5090
5190
  };
5091
5191
  // Recompute when anchor element changes or actions change (content height may change).
5092
- }, [anchorElem, actions, menuRef]);
5192
+ // Note: menuRef is a ref object and never changes, so it's safe to include
5193
+ }, [anchorElem, actions]);
5093
5194
  return /*#__PURE__*/createPortal(/*#__PURE__*/React__default.createElement("div", {
5094
5195
  ref: menuRef,
5095
5196
  style: {