@dreamtree-org/twreact-ui 1.0.71 → 1.0.72

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
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import React__default, { forwardRef, createElement, useId, useRef, useState, useEffect, useImperativeHandle, useMemo, useLayoutEffect, useCallback, createContext, useContext, cloneElement, PureComponent } from 'react';
2
+ import React__default, { forwardRef, createElement, useId, useRef, useState, useEffect, useImperativeHandle, useMemo, useCallback, useLayoutEffect, createContext, useContext, cloneElement, PureComponent } from 'react';
3
3
  import ReactDOM, { createPortal } from 'react-dom';
4
4
 
5
5
  function _extends() {
@@ -3866,6 +3866,98 @@ var Select = /*#__PURE__*/React__default.forwardRef(function (_ref, forwardedRef
3866
3866
  var _excluded$k = ["data", "columns", "sortable", "filterable", "selectable", "pagination", "pageSize", "onSort", "onFilter", "onFetch", "onFilterChange", "onSelectionChange", "onRowClick", "hasDetails", "DetailsComponent", "className", "withAction", "onAction", "actions", "showSerial", "cellClass", "rowClass", "globalSearch", "limitOptions", "showLimitSelector", "onLimitChange", "showReloadButton", "renderReloadButton", "onReload", "stripedRows", "stripedColors", "responsiveBreakpoint", "serverSide", "totalRecords", "pageNumber", "onPageChange"];
3867
3867
  function ownKeys$9(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3868
3868
  function _objectSpread$9(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$9(Object(t), true).forEach(function (r) { _defineProperty$4(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$9(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3869
+
3870
+ // Constants moved outside component
3871
+ var DEFAULT_ACTIONS = [{
3872
+ name: "edit",
3873
+ label: "Edit",
3874
+ onClick: function onClick() {
3875
+ console.log("Edit action clicked");
3876
+ },
3877
+ icon: /*#__PURE__*/React__default.createElement(PenSquare, {
3878
+ size: 16
3879
+ })
3880
+ }, {
3881
+ name: "delete",
3882
+ label: "Delete",
3883
+ onClick: function onClick() {
3884
+ console.log("Delete action clicked");
3885
+ },
3886
+ icon: /*#__PURE__*/React__default.createElement(Trash, {
3887
+ size: 16
3888
+ })
3889
+ }, {
3890
+ name: "view",
3891
+ label: "View",
3892
+ onClick: function onClick() {
3893
+ console.log("View action clicked");
3894
+ },
3895
+ icon: /*#__PURE__*/React__default.createElement(Eye, {
3896
+ size: 16
3897
+ })
3898
+ }];
3899
+ var RESIZE_DEBOUNCE_MS = 150;
3900
+ var MENU_PLACEMENT_THRESHOLD = 160;
3901
+
3902
+ // Helper functions
3903
+ var getRowKey = function getRowKey(row, globalIndex) {
3904
+ if ((row === null || row === void 0 ? void 0 : row.id) != null) return String(row.id);
3905
+ if ((row === null || row === void 0 ? void 0 : row._id) != null) return String(row._id);
3906
+ return String(globalIndex);
3907
+ };
3908
+
3909
+ // Calculate pagination numbers
3910
+ var getPaginationNumbers = function getPaginationNumbers(currentPage, totalPages) {
3911
+ var maxVisible = 5;
3912
+ if (totalPages <= maxVisible) {
3913
+ return Array.from({
3914
+ length: totalPages
3915
+ }, function (_, i) {
3916
+ return i + 1;
3917
+ });
3918
+ }
3919
+ if (currentPage <= 3) {
3920
+ return Array.from({
3921
+ length: maxVisible
3922
+ }, function (_, i) {
3923
+ return i + 1;
3924
+ });
3925
+ }
3926
+ if (currentPage >= totalPages - 2) {
3927
+ return Array.from({
3928
+ length: maxVisible
3929
+ }, function (_, i) {
3930
+ return totalPages - 4 + i;
3931
+ });
3932
+ }
3933
+ return Array.from({
3934
+ length: maxVisible
3935
+ }, function (_, i) {
3936
+ return currentPage - 2 + i;
3937
+ });
3938
+ };
3939
+
3940
+ // Empty and Loading state components
3941
+ var EmptyState = function EmptyState() {
3942
+ return /*#__PURE__*/React__default.createElement("div", {
3943
+ className: "flex flex-col items-center gap-2"
3944
+ }, /*#__PURE__*/React__default.createElement("div", {
3945
+ className: "text-gray-400"
3946
+ }, /*#__PURE__*/React__default.createElement(List, {
3947
+ className: "h-8 w-8"
3948
+ })), /*#__PURE__*/React__default.createElement("span", {
3949
+ className: "text-sm text-gray-500"
3950
+ }, "No results found."));
3951
+ };
3952
+ var LoadingState = function LoadingState() {
3953
+ return /*#__PURE__*/React__default.createElement("div", {
3954
+ className: "flex flex-col items-center gap-2"
3955
+ }, /*#__PURE__*/React__default.createElement("div", {
3956
+ className: "h-6 w-6 animate-spin rounded-full border-2 border-primary-600 border-t-transparent"
3957
+ }), /*#__PURE__*/React__default.createElement("span", {
3958
+ className: "text-sm text-gray-500"
3959
+ }, "Loading..."));
3960
+ };
3869
3961
  var Table = function Table(_ref) {
3870
3962
  var _ref$data = _ref.data,
3871
3963
  data = _ref$data === void 0 ? [] : _ref$data,
@@ -3921,9 +4013,9 @@ var Table = function Table(_ref) {
3921
4013
  serverSide = _ref$serverSide === void 0 ? false : _ref$serverSide,
3922
4014
  _ref$totalRecords = _ref.totalRecords,
3923
4015
  totalRecords = _ref$totalRecords === void 0 ? 0 : _ref$totalRecords,
3924
- pageNumber = _ref.pageNumber;
3925
- _ref.onPageChange;
3926
- var props = _objectWithoutProperties$1(_ref, _excluded$k);
4016
+ pageNumber = _ref.pageNumber,
4017
+ onPageChange = _ref.onPageChange,
4018
+ props = _objectWithoutProperties$1(_ref, _excluded$k);
3927
4019
  // State for responsive view
3928
4020
  var _useState = useState(false),
3929
4021
  _useState2 = _slicedToArray(_useState, 2),
@@ -3931,14 +4023,16 @@ var Table = function Table(_ref) {
3931
4023
  setIsMobileView = _useState2[1];
3932
4024
 
3933
4025
  // Keep original table data / loading
3934
- var _useState3 = useState(Array.isArray(data) ? data : []),
4026
+ var _useState3 = useState(function () {
4027
+ return Array.isArray(data) ? data : [];
4028
+ }),
3935
4029
  _useState4 = _slicedToArray(_useState3, 2),
3936
4030
  tableData = _useState4[0],
3937
4031
  setTableData = _useState4[1];
3938
4032
  var _useState5 = useState(false),
3939
4033
  _useState6 = _slicedToArray(_useState5, 2),
3940
4034
  loading = _useState6[0],
3941
- setLoading = _useState6[1];
4035
+ _setLoading = _useState6[1];
3942
4036
 
3943
4037
  // Column visibility state
3944
4038
  var _useState7 = useState(function () {
@@ -3950,10 +4044,26 @@ var Table = function Table(_ref) {
3950
4044
  columnsState = _useState8[0],
3951
4045
  setColumnsState = _useState8[1];
3952
4046
  useEffect(function () {
3953
- // Sync when prop changes
3954
- setColumnsState(Array.isArray(columns) ? columns.map(function (c) {
3955
- return _objectSpread$9({}, c);
3956
- }) : []);
4047
+ // Sync when prop changes - only update if columns actually changed
4048
+ if (Array.isArray(columns)) {
4049
+ setColumnsState(function (prev) {
4050
+ var prevKeys = new Set(prev.map(function (c) {
4051
+ return c.key;
4052
+ }));
4053
+ var newKeys = new Set(columns.map(function (c) {
4054
+ return c.key;
4055
+ }));
4056
+ var keysChanged = prevKeys.size !== newKeys.size || _toConsumableArray$1(prevKeys).some(function (k) {
4057
+ return !newKeys.has(k);
4058
+ });
4059
+ if (keysChanged) {
4060
+ return columns.map(function (c) {
4061
+ return _objectSpread$9({}, c);
4062
+ });
4063
+ }
4064
+ return prev;
4065
+ });
4066
+ }
3957
4067
  }, [columns]);
3958
4068
 
3959
4069
  // Popover state for column toggler
@@ -4013,39 +4123,43 @@ var Table = function Table(_ref) {
4013
4123
  _useState28 = _slicedToArray(_useState27, 2),
4014
4124
  openActionKey = _useState28[0],
4015
4125
  setOpenActionKey = _useState28[1];
4016
- var openRef = useRef(null);
4017
4126
  useEffect(function () {
4018
4127
  if (typeof pageNumber === "number" && pageNumber !== currentPage) {
4019
4128
  setCurrentPage(pageNumber);
4129
+ onPageChange === null || onPageChange === void 0 || onPageChange(pageNumber);
4020
4130
  }
4021
- }, [pageNumber]);
4131
+ }, [pageNumber, currentPage, onPageChange]);
4022
4132
  useEffect(function () {
4023
4133
  setLimit(pageSize);
4024
4134
  }, [pageSize]);
4025
4135
 
4026
- // Check if mobile view
4136
+ // Check if mobile view with debouncing
4027
4137
  useEffect(function () {
4138
+ var timeoutId;
4028
4139
  var checkMobile = function checkMobile() {
4029
4140
  setIsMobileView(window.innerWidth < responsiveBreakpoint);
4030
4141
  };
4142
+ var debouncedCheck = function debouncedCheck() {
4143
+ clearTimeout(timeoutId);
4144
+ timeoutId = setTimeout(checkMobile, RESIZE_DEBOUNCE_MS);
4145
+ };
4031
4146
 
4032
4147
  // Initial check
4033
4148
  checkMobile();
4034
4149
 
4035
- // Add event listener
4036
- window.addEventListener("resize", checkMobile);
4150
+ // Add event listener with debouncing
4151
+ window.addEventListener("resize", debouncedCheck);
4037
4152
  return function () {
4038
- window.removeEventListener("resize", checkMobile);
4153
+ clearTimeout(timeoutId);
4154
+ window.removeEventListener("resize", debouncedCheck);
4039
4155
  };
4040
4156
  }, [responsiveBreakpoint]);
4041
- var isValidList = function isValidList(d) {
4042
- return Array.isArray(d);
4043
- };
4044
4157
 
4045
4158
  // Close column menu when clicking outside
4046
4159
  useEffect(function () {
4160
+ if (!showColumnMenu) return;
4047
4161
  var onDocClick = function onDocClick(e) {
4048
- if (showColumnMenu && columnMenuRef.current && !columnMenuRef.current.contains(e.target) && columnToggleBtnRef.current && !columnToggleBtnRef.current.contains(e.target)) {
4162
+ if (columnMenuRef.current && !columnMenuRef.current.contains(e.target) && columnToggleBtnRef.current && !columnToggleBtnRef.current.contains(e.target)) {
4049
4163
  setShowColumnMenu(false);
4050
4164
  }
4051
4165
  };
@@ -4055,26 +4169,32 @@ var Table = function Table(_ref) {
4055
4169
  };
4056
4170
  }, [showColumnMenu]);
4057
4171
 
4058
- // Close per-row actionmenu when clicking outside
4059
- useEffect(function () {
4060
- var handler = function handler(e) {
4061
- if (openActionKey && openRef.current && !openRef.current.contains(e.target)) {
4062
- setOpenActionKey(null);
4063
- }
4064
- };
4065
- document.addEventListener("click", handler);
4066
- return function () {
4067
- return document.removeEventListener("click", handler);
4068
- };
4069
- }, [openActionKey]);
4070
-
4071
4172
  // Visible columns derived from columnsState
4072
4173
  var visibleColumns = useMemo(function () {
4073
4174
  return columnsState.filter(function (col) {
4074
4175
  return col.isVisible !== false;
4075
4176
  });
4076
4177
  }, [columnsState]);
4077
- var toggleColumnVisibility = function toggleColumnVisibility(key) {
4178
+
4179
+ // Memoize actions
4180
+ var actionsToUse = useMemo(function () {
4181
+ return actions !== null && actions !== void 0 ? actions : DEFAULT_ACTIONS;
4182
+ }, [actions]);
4183
+
4184
+ // Memoize visible count
4185
+ var visibleCount = useMemo(function () {
4186
+ return (showSerial ? 1 : 0) + visibleColumns.length + (selectable ? 1 : 0) + (hasDetails ? 1 : 0) + (withAction ? 1 : 0);
4187
+ }, [showSerial, visibleColumns.length, selectable, hasDetails, withAction]);
4188
+
4189
+ // Memoize key-to-row mapping for selection
4190
+ var keyToRowMap = useMemo(function () {
4191
+ var map = new Map();
4192
+ filteredData.forEach(function (row, i) {
4193
+ map.set(getRowKey(row, i), row);
4194
+ });
4195
+ return map;
4196
+ }, [filteredData]);
4197
+ var toggleColumnVisibility = useCallback(function (key) {
4078
4198
  setColumnsState(function (prev) {
4079
4199
  return prev.map(function (c) {
4080
4200
  return c.key === key ? _objectSpread$9(_objectSpread$9({}, c), {}, {
@@ -4083,57 +4203,22 @@ var Table = function Table(_ref) {
4083
4203
  });
4084
4204
  });
4085
4205
  setCurrentPage(1);
4086
- };
4206
+ }, []);
4087
4207
 
4088
4208
  // Handle actions
4089
- var handleOnAction = function handleOnAction(action, row) {
4209
+ var handleOnAction = useCallback(function (action, row) {
4090
4210
  var _action$onClick;
4091
4211
  (_action$onClick = action.onClick) === null || _action$onClick === void 0 || _action$onClick.call(action, {
4092
4212
  action: action,
4093
4213
  row: row
4094
4214
  });
4095
- onAction && onAction({
4215
+ onAction === null || onAction === void 0 || onAction({
4096
4216
  action: action,
4097
4217
  row: row
4098
4218
  });
4099
4219
  setOpenActionKey(null);
4100
4220
  setActionAnchor(null);
4101
- };
4102
- var defaultActions = [{
4103
- name: "edit",
4104
- label: "Edit",
4105
- onClick: function onClick() {
4106
- console.log("Edit action clicked");
4107
- },
4108
- icon: /*#__PURE__*/React__default.createElement(PenSquare, {
4109
- size: 16
4110
- })
4111
- }, {
4112
- name: "delete",
4113
- label: "Delete",
4114
- onClick: function onClick() {
4115
- console.log("Delete action clicked");
4116
- },
4117
- icon: /*#__PURE__*/React__default.createElement(Trash, {
4118
- size: 16
4119
- })
4120
- }, {
4121
- name: "view",
4122
- label: "View",
4123
- onClick: function onClick() {
4124
- console.log("View action clicked");
4125
- },
4126
- icon: /*#__PURE__*/React__default.createElement(Eye, {
4127
- size: 16
4128
- })
4129
- }];
4130
- var actionsToUse = actions !== undefined ? actions : defaultActions;
4131
- var getRowKey = function getRowKey(row, globalIndex) {
4132
- if (row == null) return String(globalIndex);
4133
- if (row.id !== undefined && row.id !== null) return String(row.id);
4134
- if (row._id !== undefined && row._id !== null) return String(row._id);
4135
- return String(globalIndex);
4136
- };
4221
+ }, [onAction]);
4137
4222
 
4138
4223
  // Sorting
4139
4224
  var sortedData = useMemo(function () {
@@ -4159,9 +4244,18 @@ var Table = function Table(_ref) {
4159
4244
  });
4160
4245
  });
4161
4246
  }, [sortedData, filters, filterable, serverSide]);
4162
- // Pagination indices
4163
- var startIndex = pagination ? (currentPage - 1) * limit : 0;
4164
- var endIndex = pagination ? startIndex + limit : filteredData.length;
4247
+
4248
+ // Pagination indices - memoized
4249
+ var _useMemo = useMemo(function () {
4250
+ var start = pagination ? (currentPage - 1) * limit : 0;
4251
+ var end = pagination ? start + limit : filteredData.length;
4252
+ return {
4253
+ startIndex: start,
4254
+ endIndex: end
4255
+ };
4256
+ }, [pagination, currentPage, limit, filteredData.length]),
4257
+ startIndex = _useMemo.startIndex,
4258
+ endIndex = _useMemo.endIndex;
4165
4259
 
4166
4260
  // Paginated view
4167
4261
  var paginatedData = useMemo(function () {
@@ -4175,7 +4269,7 @@ var Table = function Table(_ref) {
4175
4269
  }, [pagination, serverSide, totalRecords, filteredData.length, limit]);
4176
4270
 
4177
4271
  // Sorting handler
4178
- var handleSort = function handleSort(key) {
4272
+ var handleSort = useCallback(function (key) {
4179
4273
  if (!sortable) return;
4180
4274
  var direction = sortConfig.key === key && sortConfig.direction === "asc" ? "desc" : "asc";
4181
4275
  setSortConfig({
@@ -4183,10 +4277,10 @@ var Table = function Table(_ref) {
4183
4277
  direction: direction
4184
4278
  });
4185
4279
  onSort === null || onSort === void 0 || onSort(key, direction);
4186
- };
4280
+ }, [sortable, sortConfig, onSort]);
4187
4281
 
4188
- // Selection handlers
4189
- var handleSelectAll = function handleSelectAll() {
4282
+ // Selection handlers - optimized with memoized keyToRowMap
4283
+ var handleSelectAll = useCallback(function () {
4190
4284
  var newSelection = new Set(selectedRows);
4191
4285
  var pageRowKeys = paginatedData.map(function (r, i) {
4192
4286
  return getRowKey(r, startIndex + i);
@@ -4204,50 +4298,45 @@ var Table = function Table(_ref) {
4204
4298
  });
4205
4299
  }
4206
4300
  setSelectedRows(newSelection);
4207
- var keyToRow = new Map(filteredData.map(function (r, i) {
4208
- return [getRowKey(r, i), r];
4209
- }));
4210
4301
  var selectedData = Array.from(newSelection).map(function (k) {
4211
- return keyToRow.get(k);
4302
+ return keyToRowMap.get(k);
4212
4303
  }).filter(Boolean);
4213
4304
  onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange(selectedData);
4214
- };
4215
- var handleSelectRow = function handleSelectRow(row, rowIndexInPage) {
4305
+ }, [selectedRows, paginatedData, startIndex, keyToRowMap, onSelectionChange]);
4306
+ var handleSelectRow = useCallback(function (row, rowIndexInPage) {
4216
4307
  var globalIndex = startIndex + rowIndexInPage;
4217
4308
  var key = getRowKey(row, globalIndex);
4218
4309
  var newSelection = new Set(selectedRows);
4219
4310
  if (newSelection.has(key)) newSelection["delete"](key);else newSelection.add(key);
4220
4311
  setSelectedRows(newSelection);
4221
- var keyToRow = new Map(filteredData.map(function (r, i) {
4222
- return [getRowKey(r, i), r];
4223
- }));
4224
4312
  var selectedData = Array.from(newSelection).map(function (k) {
4225
- return keyToRow.get(k);
4313
+ return keyToRowMap.get(k);
4226
4314
  }).filter(Boolean);
4227
4315
  onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange(selectedData);
4228
- };
4229
- var toggleExpandRow = function toggleExpandRow(row, rowIndexInPage) {
4316
+ }, [startIndex, selectedRows, keyToRowMap, onSelectionChange]);
4317
+ var toggleExpandRow = useCallback(function (row, rowIndexInPage) {
4230
4318
  var globalIndex = startIndex + rowIndexInPage;
4231
4319
  var key = getRowKey(row, globalIndex);
4232
- var newExpanded = new Set(expandedRows);
4233
- if (newExpanded.has(key)) newExpanded["delete"](key);else newExpanded.add(key);
4234
- setExpandedRows(newExpanded);
4235
- };
4236
- var handleFilter = function handleFilter(key, value) {
4237
- var newFilters = _objectSpread$9(_objectSpread$9({}, filters), {}, _defineProperty$4({}, key, value));
4238
- setFilters(newFilters);
4239
- onFilter === null || onFilter === void 0 || onFilter(newFilters);
4240
- onFilterChange === null || onFilterChange === void 0 || onFilterChange(newFilters);
4320
+ setExpandedRows(function (prev) {
4321
+ var newExpanded = new Set(prev);
4322
+ if (newExpanded.has(key)) newExpanded["delete"](key);else newExpanded.add(key);
4323
+ return newExpanded;
4324
+ });
4325
+ }, [startIndex]);
4326
+ var handleFilter = useCallback(function (key, value) {
4327
+ setFilters(function (prev) {
4328
+ var newFilters = _objectSpread$9(_objectSpread$9({}, prev), {}, _defineProperty$4({}, key, value));
4329
+ onFilter === null || onFilter === void 0 || onFilter(newFilters);
4330
+ onFilterChange === null || onFilterChange === void 0 || onFilterChange(newFilters);
4331
+ return newFilters;
4332
+ });
4241
4333
  setCurrentPage(1);
4242
- };
4243
- var renderCell = function renderCell(column, row, globalIndex) {
4334
+ }, [onFilter, onFilterChange]);
4335
+ var renderCell = useCallback(function (column, row, globalIndex) {
4244
4336
  if (column.render) return column.render(row, globalIndex);
4245
4337
  return row === null || row === void 0 ? void 0 : row[column.key];
4246
- };
4247
-
4248
- // UI counts
4249
- var visibleCount = (showSerial ? 1 : 0) + visibleColumns.length + (selectable ? 1 : 0) + (hasDetails ? 1 : 0) + (withAction ? 1 : 0);
4250
- var toggleActions = function toggleActions(e, actionCellKey, row) {
4338
+ }, []);
4339
+ var toggleActions = useCallback(function (e, actionCellKey, row) {
4251
4340
  e.stopPropagation();
4252
4341
  if (actionAnchor && actionAnchor.key === actionCellKey) {
4253
4342
  setOpenActionKey(null);
@@ -4260,7 +4349,7 @@ var Table = function Table(_ref) {
4260
4349
  row: row
4261
4350
  });
4262
4351
  }
4263
- };
4352
+ }, [actionAnchor]);
4264
4353
  useEffect(function () {
4265
4354
  if (!actionAnchor) return;
4266
4355
  var onDocClick = function onDocClick(ev) {
@@ -4284,58 +4373,139 @@ var Table = function Table(_ref) {
4284
4373
  window.removeEventListener("resize", onScrollResize);
4285
4374
  };
4286
4375
  }, [actionAnchor]);
4376
+
4377
+ // Track previous fetch params to prevent unnecessary calls
4378
+ var prevFetchParamsRef = useRef(null);
4379
+ var isFetchingRef = useRef(false);
4380
+ var isMountedRef = useRef(true);
4381
+ var onFetchRef = useRef(onFetch);
4382
+
4383
+ // Keep ref in sync with onFetch
4287
4384
  useEffect(function () {
4288
- if (!onFetch) return;
4289
- onFetch({
4290
- setData: function setData(rows) {
4291
- return setTableData(Array.isArray(rows) ? rows : []);
4292
- },
4293
- setLoading: setLoading,
4385
+ onFetchRef.current = onFetch;
4386
+ }, [onFetch]);
4387
+
4388
+ // Track mount status
4389
+ useEffect(function () {
4390
+ isMountedRef.current = true;
4391
+ return function () {
4392
+ isMountedRef.current = false;
4393
+ isFetchingRef.current = false;
4394
+ };
4395
+ }, []);
4396
+
4397
+ // Serialize fetch params for comparison
4398
+ var fetchParams = useMemo(function () {
4399
+ return {
4294
4400
  filters: filters,
4295
4401
  page: currentPage,
4296
4402
  limit: limit,
4297
- sort: sortConfig
4298
- });
4299
- }, [onFetch, filters, currentPage, limit, sortConfig]);
4300
- useEffect(function () {
4301
- if (!serverSide) setTableData(data);
4302
- }, [data, serverSide]);
4403
+ sortKey: sortConfig.key,
4404
+ sortDirection: sortConfig.direction
4405
+ };
4406
+ }, [filters, currentPage, limit, sortConfig.key, sortConfig.direction]);
4407
+ var fetchParamsString = useMemo(function () {
4408
+ return JSON.stringify(fetchParams);
4409
+ }, [fetchParams]);
4410
+
4411
+ // Consolidated data sync effect
4303
4412
  useEffect(function () {
4304
- if (!onFetch && isValidList(data)) setTableData(data);
4305
- }, [data, onFetch]);
4413
+ var currentOnFetch = onFetchRef.current;
4414
+ if (currentOnFetch) {
4415
+ // Only fetch if params actually changed and not already fetching
4416
+ if (!isFetchingRef.current && prevFetchParamsRef.current !== fetchParamsString) {
4417
+ prevFetchParamsRef.current = fetchParamsString;
4418
+ 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;
4425
+ }
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) {
4432
+ isFetchingRef.current = false;
4433
+ }
4434
+ }
4435
+ },
4436
+ filters: fetchParams.filters,
4437
+ page: fetchParams.page,
4438
+ limit: fetchParams.limit,
4439
+ sort: {
4440
+ key: fetchParams.sortKey,
4441
+ direction: fetchParams.sortDirection
4442
+ }
4443
+ });
4444
+ }
4445
+ } else if (!serverSide && Array.isArray(data)) {
4446
+ setTableData(data);
4447
+ }
4448
+ }, [serverSide, data, fetchParamsString, fetchParams]);
4306
4449
 
4307
4450
  // Global search
4308
4451
  useEffect(function () {
4309
4452
  setSearchInput(filters.global || "");
4310
4453
  }, [filters.global]);
4311
- var applyGlobalSearch = function applyGlobalSearch() {
4454
+ var applyGlobalSearch = useCallback(function () {
4312
4455
  handleFilter("global", searchInput);
4313
- };
4314
- var onGlobalKeyDown = function onGlobalKeyDown(e) {
4456
+ }, [handleFilter, searchInput]);
4457
+ var onGlobalKeyDown = useCallback(function (e) {
4315
4458
  if (e.key === "Enter") {
4316
4459
  e.preventDefault();
4317
4460
  applyGlobalSearch();
4318
4461
  }
4319
- };
4320
- var handleLimitChange = function handleLimitChange(nextLimit) {
4462
+ }, [applyGlobalSearch]);
4463
+ var handleLimitChange = useCallback(function (nextLimit) {
4321
4464
  var parsed = Number(nextLimit);
4322
4465
  if (!Number.isFinite(parsed) || parsed <= 0) return;
4323
4466
  setLimit(parsed);
4324
4467
  setCurrentPage(1);
4325
4468
  onLimitChange === null || onLimitChange === void 0 || onLimitChange(parsed);
4326
- };
4469
+ }, [onLimitChange]);
4327
4470
 
4328
- // Render mobile card
4329
- var renderMobileCard = function renderMobileCard(row, rowIndexInPage) {
4471
+ // Memoize limit options
4472
+ var limitOptionsMemo = useMemo(function () {
4473
+ var opts = Array.isArray(limitOptions) && limitOptions.length > 0 ? limitOptions : [25, 50, 100];
4474
+ return opts.map(function (opt) {
4475
+ return {
4476
+ label: String(opt),
4477
+ value: opt
4478
+ };
4479
+ });
4480
+ }, [limitOptions]);
4481
+
4482
+ // Extract row metadata computation
4483
+ var getRowMetadata = useCallback(function (row, rowIndexInPage) {
4330
4484
  var globalIndex = startIndex + rowIndexInPage;
4331
4485
  var key = getRowKey(row, globalIndex);
4332
4486
  var actionCellKey = "actions-".concat(key);
4333
- var extraRowClass = typeof rowClass === "function" ? rowClass({
4487
+ var extraRowClass = typeof rowClass === "function" ? (rowClass({
4334
4488
  row: row,
4335
4489
  rowIndex: globalIndex
4336
- }) : "";
4337
- var safeExtraRowClass = typeof extraRowClass === "string" ? extraRowClass.trim() : "";
4490
+ }) || "").trim() : "";
4338
4491
  var stripeBg = stripedRows && !selectedRows.has(key) ? stripedColors[globalIndex % stripedColors.length] : undefined;
4492
+ return {
4493
+ globalIndex: globalIndex,
4494
+ key: key,
4495
+ actionCellKey: actionCellKey,
4496
+ extraRowClass: extraRowClass,
4497
+ stripeBg: stripeBg
4498
+ };
4499
+ }, [startIndex, rowClass, stripedRows, selectedRows, stripedColors]);
4500
+
4501
+ // Render mobile card
4502
+ var renderMobileCard = useCallback(function (row, rowIndexInPage) {
4503
+ var _getRowMetadata = getRowMetadata(row, rowIndexInPage),
4504
+ globalIndex = _getRowMetadata.globalIndex,
4505
+ key = _getRowMetadata.key,
4506
+ actionCellKey = _getRowMetadata.actionCellKey;
4507
+ _getRowMetadata.extraRowClass;
4508
+ var stripeBg = _getRowMetadata.stripeBg;
4339
4509
  return /*#__PURE__*/React__default.createElement("div", {
4340
4510
  key: key,
4341
4511
  style: stripeBg ? {
@@ -4402,16 +4572,15 @@ var Table = function Table(_ref) {
4402
4572
  }), document.body))), /*#__PURE__*/React__default.createElement("div", {
4403
4573
  className: "p-4"
4404
4574
  }, visibleColumns.map(function (column, colIndex) {
4405
- var extraCellClass = typeof cellClass === "function" ? cellClass({
4575
+ var extraCellClass = typeof cellClass === "function" ? (cellClass({
4406
4576
  row: row,
4407
4577
  rowIndex: globalIndex,
4408
4578
  column: column,
4409
4579
  columnIndex: colIndex
4410
- }) : "";
4411
- var safeExtraCellClass = typeof extraCellClass === "string" ? extraCellClass.trim() : "";
4580
+ }) || "").trim() : "";
4412
4581
  return /*#__PURE__*/React__default.createElement("div", {
4413
4582
  key: column.key,
4414
- className: cn$1("flex justify-between items-center flex-row py-2 border-b last:border-b-0", safeExtraCellClass)
4583
+ className: cn$1("flex justify-between items-center flex-row py-2 border-b last:border-b-0", extraCellClass)
4415
4584
  }, /*#__PURE__*/React__default.createElement("div", {
4416
4585
  className: "text-xs font-medium text-gray-500 uppercase tracking-wide mb-1 sm:mb-0 sm:w-1/3 sm:pr-2"
4417
4586
  }, column.label), /*#__PURE__*/React__default.createElement("div", {
@@ -4423,7 +4592,7 @@ var Table = function Table(_ref) {
4423
4592
  row: row,
4424
4593
  index: globalIndex
4425
4594
  })));
4426
- };
4595
+ }, [getRowMetadata, hasDetails, selectable, showSerial, withAction, expandedRows, selectedRows, onRowClick, visibleColumns, renderCell, cellClass, DetailsComponent, actionsToUse, actionAnchor, openActionKey, handleOnAction, actionMenuRef]);
4427
4596
 
4428
4597
  // Render mobile filters
4429
4598
  var renderMobileFilters = function renderMobileFilters() {
@@ -4491,23 +4660,11 @@ var Table = function Table(_ref) {
4491
4660
  htmlFor: "pagination-limit",
4492
4661
  className: "text-sm text-gray-600 whitespace-nowrap"
4493
4662
  }, "Show"), /*#__PURE__*/React__default.createElement(Select, {
4494
- options: Array.isArray(limitOptions) && limitOptions.length > 0 ? limitOptions.map(function (opt) {
4495
- return {
4496
- label: String(opt),
4497
- value: opt
4498
- };
4499
- }) : [25, 50, 100].map(function (opt) {
4500
- return {
4501
- label: String(opt),
4502
- value: opt
4503
- };
4504
- }),
4663
+ options: limitOptionsMemo,
4505
4664
  value: limit,
4506
4665
  allowClear: false,
4507
4666
  placeholder: "",
4508
- onChange: function onChange(e) {
4509
- return handleLimitChange(e);
4510
- },
4667
+ onChange: handleLimitChange,
4511
4668
  className: "w-20 md:w-30"
4512
4669
  }), /*#__PURE__*/React__default.createElement("p", {
4513
4670
  className: "text-sm text-gray-600 whitespace-nowrap"
@@ -4679,33 +4836,16 @@ var Table = function Table(_ref) {
4679
4836
  }, loading ? /*#__PURE__*/React__default.createElement("tr", null, /*#__PURE__*/React__default.createElement("td", {
4680
4837
  colSpan: visibleCount,
4681
4838
  className: "px-6 py-8 text-center"
4682
- }, /*#__PURE__*/React__default.createElement("div", {
4683
- className: "flex flex-col items-center gap-2"
4684
- }, /*#__PURE__*/React__default.createElement("div", {
4685
- className: "h-6 w-6 animate-spin rounded-full border-2 border-primary-600 border-t-transparent"
4686
- }), /*#__PURE__*/React__default.createElement("span", {
4687
- className: "text-sm text-gray-500"
4688
- }, "Loading...")))) : paginatedData.length === 0 ? /*#__PURE__*/React__default.createElement("tr", null, /*#__PURE__*/React__default.createElement("td", {
4839
+ }, /*#__PURE__*/React__default.createElement(LoadingState, null))) : paginatedData.length === 0 ? /*#__PURE__*/React__default.createElement("tr", null, /*#__PURE__*/React__default.createElement("td", {
4689
4840
  colSpan: visibleCount,
4690
4841
  className: "px-6 py-8 text-center"
4691
- }, /*#__PURE__*/React__default.createElement("div", {
4692
- className: "flex flex-col items-center gap-2"
4693
- }, /*#__PURE__*/React__default.createElement("div", {
4694
- className: "text-gray-400"
4695
- }, /*#__PURE__*/React__default.createElement(List, {
4696
- className: "h-8 w-8"
4697
- })), /*#__PURE__*/React__default.createElement("span", {
4698
- className: "text-sm text-gray-500"
4699
- }, "No results found.")))) : paginatedData.map(function (row, rowIndexInPage) {
4700
- var globalIndex = startIndex + rowIndexInPage;
4701
- var key = getRowKey(row, globalIndex);
4702
- var actionCellKey = "actions-".concat(key);
4703
- var extraRowClass = typeof rowClass === "function" ? rowClass({
4704
- row: row,
4705
- rowIndex: globalIndex
4706
- }) : "";
4707
- var safeExtraRowClass = typeof extraRowClass === "string" ? extraRowClass.trim() : "";
4708
- var stripeBg = stripedRows && !selectedRows.has(key) ? stripedColors[globalIndex % stripedColors.length] : undefined;
4842
+ }, /*#__PURE__*/React__default.createElement(EmptyState, null))) : paginatedData.map(function (row, rowIndexInPage) {
4843
+ var _getRowMetadata2 = getRowMetadata(row, rowIndexInPage),
4844
+ globalIndex = _getRowMetadata2.globalIndex,
4845
+ key = _getRowMetadata2.key,
4846
+ actionCellKey = _getRowMetadata2.actionCellKey,
4847
+ extraRowClass = _getRowMetadata2.extraRowClass,
4848
+ stripeBg = _getRowMetadata2.stripeBg;
4709
4849
  return /*#__PURE__*/React__default.createElement(React__default.Fragment, {
4710
4850
  key: key
4711
4851
  }, /*#__PURE__*/React__default.createElement("tr", {
@@ -4715,7 +4855,7 @@ var Table = function Table(_ref) {
4715
4855
  className: cn$1("hover:bg-gray-50", {
4716
4856
  "cursor-pointer": !!onRowClick,
4717
4857
  "bg-primary-50": selectedRows.has(key)
4718
- }, safeExtraRowClass),
4858
+ }, extraRowClass),
4719
4859
  onClick: function onClick() {
4720
4860
  return onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row, globalIndex);
4721
4861
  }
@@ -4743,16 +4883,15 @@ var Table = function Table(_ref) {
4743
4883
  return handleSelectRow(row, rowIndexInPage);
4744
4884
  }
4745
4885
  })), visibleColumns.map(function (column, columnIndex) {
4746
- var extraCellClass = typeof cellClass === "function" ? cellClass({
4886
+ var extraCellClass = typeof cellClass === "function" ? (cellClass({
4747
4887
  row: row,
4748
4888
  rowIndex: globalIndex,
4749
4889
  column: column,
4750
4890
  columnIndex: columnIndex
4751
- }) : "";
4752
- var safeExtraCellClass = typeof extraCellClass === "string" ? extraCellClass.trim() : "";
4891
+ }) || "").trim() : "";
4753
4892
  return /*#__PURE__*/React__default.createElement("td", {
4754
4893
  key: column.key,
4755
- className: cn$1("px-6 py-4 whitespace-nowrap text-sm text-gray-900", safeExtraCellClass)
4894
+ className: cn$1("px-6 py-4 whitespace-nowrap text-sm text-gray-900", extraCellClass)
4756
4895
  }, renderCell(column, row, globalIndex));
4757
4896
  }), withAction && /*#__PURE__*/React__default.createElement("td", {
4758
4897
  className: "px-4 py-4 text-sm text-right",
@@ -4795,23 +4934,9 @@ var Table = function Table(_ref) {
4795
4934
  className: "p-4"
4796
4935
  }, loading ? /*#__PURE__*/React__default.createElement("div", {
4797
4936
  className: "py-8 text-center"
4798
- }, /*#__PURE__*/React__default.createElement("div", {
4799
- className: "flex flex-col items-center gap-2"
4800
- }, /*#__PURE__*/React__default.createElement("div", {
4801
- className: "h-6 w-6 animate-spin rounded-full border-2 border-primary-600 border-t-transparent"
4802
- }), /*#__PURE__*/React__default.createElement("span", {
4803
- className: "text-sm text-gray-500"
4804
- }, "Loading..."))) : paginatedData.length === 0 ? /*#__PURE__*/React__default.createElement("div", {
4937
+ }, /*#__PURE__*/React__default.createElement(LoadingState, null)) : paginatedData.length === 0 ? /*#__PURE__*/React__default.createElement("div", {
4805
4938
  className: "py-8 text-center"
4806
- }, /*#__PURE__*/React__default.createElement("div", {
4807
- className: "flex flex-col items-center gap-2"
4808
- }, /*#__PURE__*/React__default.createElement("div", {
4809
- className: "text-gray-400"
4810
- }, /*#__PURE__*/React__default.createElement(List, {
4811
- className: "h-8 w-8"
4812
- })), /*#__PURE__*/React__default.createElement("span", {
4813
- className: "text-sm text-gray-500"
4814
- }, "No results found."))) : paginatedData.map(function (row, rowIndexInPage) {
4939
+ }, /*#__PURE__*/React__default.createElement(EmptyState, null)) : paginatedData.map(function (row, rowIndexInPage) {
4815
4940
  return renderMobileCard(row, rowIndexInPage);
4816
4941
  })), pagination && /*#__PURE__*/React__default.createElement("div", {
4817
4942
  className: "p-4 border-t"
@@ -4831,19 +4956,7 @@ var Table = function Table(_ref) {
4831
4956
  }
4832
4957
  }, "Previous"), /*#__PURE__*/React__default.createElement("div", {
4833
4958
  className: "flex items-center gap-1"
4834
- }, Array.from({
4835
- length: Math.min(5, totalPages)
4836
- }, function (_, i) {
4837
- var pageNum;
4838
- if (totalPages <= 5) {
4839
- pageNum = i + 1;
4840
- } else if (currentPage <= 3) {
4841
- pageNum = i + 1;
4842
- } else if (currentPage >= totalPages - 2) {
4843
- pageNum = totalPages - 4 + i;
4844
- } else {
4845
- pageNum = currentPage - 2 + i;
4846
- }
4959
+ }, getPaginationNumbers(currentPage, totalPages).map(function (pageNum) {
4847
4960
  return /*#__PURE__*/React__default.createElement("button", {
4848
4961
  key: pageNum,
4849
4962
  className: cn$1("h-8 w-8 rounded-lg md:rounded-md text-sm", {
@@ -4864,6 +4977,12 @@ var Table = function Table(_ref) {
4864
4977
  }
4865
4978
  }, "Next")))));
4866
4979
  };
4980
+
4981
+ // ActionMenuPortal constants
4982
+ var MENU_WIDTH = 180;
4983
+ var MAX_MENU_HEIGHT = 320;
4984
+ var MENU_MARGIN = 8;
4985
+ var MIN_MENU_HEIGHT = 80;
4867
4986
  function ActionMenuPortal(_ref2) {
4868
4987
  var anchorElem = _ref2.anchorElem,
4869
4988
  anchorRow = _ref2.anchorRow,
@@ -4877,16 +4996,12 @@ function ActionMenuPortal(_ref2) {
4877
4996
  top: 0,
4878
4997
  transformOrigin: "top right",
4879
4998
  maxHeight: 300,
4880
- width: 180,
4999
+ width: MENU_WIDTH,
4881
5000
  opacity: 0
4882
5001
  }),
4883
5002
  _useState30 = _slicedToArray(_useState29, 2),
4884
5003
  style = _useState30[0],
4885
5004
  setStyle = _useState30[1];
4886
- var menuWidth = 180;
4887
- var maxMenuHeight = 320;
4888
- var margin = 8;
4889
- var minMenuHeight = 80;
4890
5005
 
4891
5006
  // compute position using actual menu height (if available) and layout measurements
4892
5007
  var computePosition = function computePosition() {
@@ -4898,17 +5013,17 @@ function ActionMenuPortal(_ref2) {
4898
5013
  var spaceAbove = rect.top;
4899
5014
 
4900
5015
  // allowed max height based on available space
4901
- var allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, Math.max(spaceBelow - margin, spaceAbove - margin)));
5016
+ var allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, Math.max(spaceBelow - MENU_MARGIN, spaceAbove - MENU_MARGIN)));
4902
5017
 
4903
5018
  // choose placement by comparing actual available spaces
4904
5019
  var placement = "bottom";
4905
- if (spaceBelow < 160 && spaceAbove > spaceBelow) {
5020
+ if (spaceBelow < MENU_PLACEMENT_THRESHOLD && spaceAbove > spaceBelow) {
4906
5021
  placement = "top";
4907
5022
  // when placing on top we should cap allowedMaxHeight by spaceAbove
4908
- allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceAbove - margin));
5023
+ allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, spaceAbove - MENU_MARGIN));
4909
5024
  } else {
4910
5025
  // placing bottom: cap by spaceBelow
4911
- allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceBelow - margin));
5026
+ allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, spaceBelow - MENU_MARGIN));
4912
5027
  }
4913
5028
 
4914
5029
  // measure the menu's content height if we can, and compute the actual menu height we'll use.
@@ -4925,23 +5040,23 @@ function ActionMenuPortal(_ref2) {
4925
5040
  var top;
4926
5041
  if (placement === "top") {
4927
5042
  // place menu so its bottom sits just above the anchor (rect.top - margin)
4928
- top = rect.top + scrollY - measuredMenuHeight - margin;
5043
+ top = rect.top + scrollY - measuredMenuHeight - MENU_MARGIN;
4929
5044
  } else {
4930
5045
  // place menu just below the anchor (rect.bottom + margin)
4931
- top = rect.bottom + scrollY + margin;
5046
+ top = rect.bottom + scrollY + MENU_MARGIN;
4932
5047
  }
4933
5048
 
4934
5049
  // clamp top to viewport (so it never goes off-screen)
4935
- var minTop = margin + scrollY;
4936
- var maxTop = window.innerHeight + scrollY - measuredMenuHeight - margin;
5050
+ var minTop = MENU_MARGIN + scrollY;
5051
+ var maxTop = window.innerHeight + scrollY - measuredMenuHeight - MENU_MARGIN;
4937
5052
  if (top < minTop) top = minTop;
4938
5053
  if (top > maxTop) top = maxTop;
4939
5054
 
4940
5055
  // compute left and clamp horizontally
4941
- var left = rect.right + scrollX - menuWidth;
4942
- if (left < margin) left = margin;
4943
- if (left + menuWidth > window.innerWidth - margin) {
4944
- left = Math.max(margin, window.innerWidth - menuWidth - margin);
5056
+ var left = rect.right + scrollX - MENU_WIDTH;
5057
+ if (left < MENU_MARGIN) left = MENU_MARGIN;
5058
+ if (left + MENU_WIDTH > window.innerWidth - MENU_MARGIN) {
5059
+ left = Math.max(MENU_MARGIN, window.innerWidth - MENU_WIDTH - MENU_MARGIN);
4945
5060
  }
4946
5061
  var transformOrigin = placement === "bottom" ? "top right" : "bottom right";
4947
5062
 
@@ -4951,7 +5066,7 @@ function ActionMenuPortal(_ref2) {
4951
5066
  top: top,
4952
5067
  transformOrigin: transformOrigin,
4953
5068
  maxHeight: allowedMaxHeight,
4954
- width: menuWidth,
5069
+ width: MENU_WIDTH,
4955
5070
  opacity: 1
4956
5071
  });
4957
5072
  };