@dreamtree-org/twreact-ui 1.0.71 → 1.0.73

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
@@ -3886,6 +3886,98 @@ var Select = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
3886
3886
  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"];
3887
3887
  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; }
3888
3888
  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; }
3889
+
3890
+ // Constants moved outside component
3891
+ var DEFAULT_ACTIONS = [{
3892
+ name: "edit",
3893
+ label: "Edit",
3894
+ onClick: function onClick() {
3895
+ console.log("Edit action clicked");
3896
+ },
3897
+ icon: /*#__PURE__*/React.createElement(PenSquare, {
3898
+ size: 16
3899
+ })
3900
+ }, {
3901
+ name: "delete",
3902
+ label: "Delete",
3903
+ onClick: function onClick() {
3904
+ console.log("Delete action clicked");
3905
+ },
3906
+ icon: /*#__PURE__*/React.createElement(Trash, {
3907
+ size: 16
3908
+ })
3909
+ }, {
3910
+ name: "view",
3911
+ label: "View",
3912
+ onClick: function onClick() {
3913
+ console.log("View action clicked");
3914
+ },
3915
+ icon: /*#__PURE__*/React.createElement(Eye, {
3916
+ size: 16
3917
+ })
3918
+ }];
3919
+ var RESIZE_DEBOUNCE_MS = 150;
3920
+ var MENU_PLACEMENT_THRESHOLD = 160;
3921
+
3922
+ // Helper functions
3923
+ var getRowKey = function getRowKey(row, globalIndex) {
3924
+ if ((row === null || row === void 0 ? void 0 : row.id) != null) return String(row.id);
3925
+ if ((row === null || row === void 0 ? void 0 : row._id) != null) return String(row._id);
3926
+ return String(globalIndex);
3927
+ };
3928
+
3929
+ // Calculate pagination numbers
3930
+ var getPaginationNumbers = function getPaginationNumbers(currentPage, totalPages) {
3931
+ var maxVisible = 5;
3932
+ if (totalPages <= maxVisible) {
3933
+ return Array.from({
3934
+ length: totalPages
3935
+ }, function (_, i) {
3936
+ return i + 1;
3937
+ });
3938
+ }
3939
+ if (currentPage <= 3) {
3940
+ return Array.from({
3941
+ length: maxVisible
3942
+ }, function (_, i) {
3943
+ return i + 1;
3944
+ });
3945
+ }
3946
+ if (currentPage >= totalPages - 2) {
3947
+ return Array.from({
3948
+ length: maxVisible
3949
+ }, function (_, i) {
3950
+ return totalPages - 4 + i;
3951
+ });
3952
+ }
3953
+ return Array.from({
3954
+ length: maxVisible
3955
+ }, function (_, i) {
3956
+ return currentPage - 2 + i;
3957
+ });
3958
+ };
3959
+
3960
+ // Empty and Loading state components
3961
+ var EmptyState = function EmptyState() {
3962
+ return /*#__PURE__*/React.createElement("div", {
3963
+ className: "flex flex-col items-center gap-2"
3964
+ }, /*#__PURE__*/React.createElement("div", {
3965
+ className: "text-gray-400"
3966
+ }, /*#__PURE__*/React.createElement(List, {
3967
+ className: "h-8 w-8"
3968
+ })), /*#__PURE__*/React.createElement("span", {
3969
+ className: "text-sm text-gray-500"
3970
+ }, "No results found."));
3971
+ };
3972
+ var LoadingState = function LoadingState() {
3973
+ return /*#__PURE__*/React.createElement("div", {
3974
+ className: "flex flex-col items-center gap-2"
3975
+ }, /*#__PURE__*/React.createElement("div", {
3976
+ className: "h-6 w-6 animate-spin rounded-full border-2 border-primary-600 border-t-transparent"
3977
+ }), /*#__PURE__*/React.createElement("span", {
3978
+ className: "text-sm text-gray-500"
3979
+ }, "Loading..."));
3980
+ };
3889
3981
  var Table = function Table(_ref) {
3890
3982
  var _ref$data = _ref.data,
3891
3983
  data = _ref$data === void 0 ? [] : _ref$data,
@@ -3941,9 +4033,9 @@ var Table = function Table(_ref) {
3941
4033
  serverSide = _ref$serverSide === void 0 ? false : _ref$serverSide,
3942
4034
  _ref$totalRecords = _ref.totalRecords,
3943
4035
  totalRecords = _ref$totalRecords === void 0 ? 0 : _ref$totalRecords,
3944
- pageNumber = _ref.pageNumber;
3945
- _ref.onPageChange;
3946
- var props = _objectWithoutProperties$1(_ref, _excluded$k);
4036
+ pageNumber = _ref.pageNumber,
4037
+ onPageChange = _ref.onPageChange,
4038
+ props = _objectWithoutProperties$1(_ref, _excluded$k);
3947
4039
  // State for responsive view
3948
4040
  var _useState = React.useState(false),
3949
4041
  _useState2 = _slicedToArray(_useState, 2),
@@ -3951,14 +4043,16 @@ var Table = function Table(_ref) {
3951
4043
  setIsMobileView = _useState2[1];
3952
4044
 
3953
4045
  // Keep original table data / loading
3954
- var _useState3 = React.useState(Array.isArray(data) ? data : []),
4046
+ var _useState3 = React.useState(function () {
4047
+ return Array.isArray(data) ? data : [];
4048
+ }),
3955
4049
  _useState4 = _slicedToArray(_useState3, 2),
3956
4050
  tableData = _useState4[0],
3957
4051
  setTableData = _useState4[1];
3958
4052
  var _useState5 = React.useState(false),
3959
4053
  _useState6 = _slicedToArray(_useState5, 2),
3960
4054
  loading = _useState6[0],
3961
- setLoading = _useState6[1];
4055
+ _setLoading = _useState6[1];
3962
4056
 
3963
4057
  // Column visibility state
3964
4058
  var _useState7 = React.useState(function () {
@@ -3970,10 +4064,26 @@ var Table = function Table(_ref) {
3970
4064
  columnsState = _useState8[0],
3971
4065
  setColumnsState = _useState8[1];
3972
4066
  React.useEffect(function () {
3973
- // Sync when prop changes
3974
- setColumnsState(Array.isArray(columns) ? columns.map(function (c) {
3975
- return _objectSpread$9({}, c);
3976
- }) : []);
4067
+ // Sync when prop changes - only update if columns actually changed
4068
+ if (Array.isArray(columns)) {
4069
+ setColumnsState(function (prev) {
4070
+ var prevKeys = new Set(prev.map(function (c) {
4071
+ return c.key;
4072
+ }));
4073
+ var newKeys = new Set(columns.map(function (c) {
4074
+ return c.key;
4075
+ }));
4076
+ var keysChanged = prevKeys.size !== newKeys.size || _toConsumableArray$1(prevKeys).some(function (k) {
4077
+ return !newKeys.has(k);
4078
+ });
4079
+ if (keysChanged) {
4080
+ return columns.map(function (c) {
4081
+ return _objectSpread$9({}, c);
4082
+ });
4083
+ }
4084
+ return prev;
4085
+ });
4086
+ }
3977
4087
  }, [columns]);
3978
4088
 
3979
4089
  // Popover state for column toggler
@@ -4033,39 +4143,44 @@ var Table = function Table(_ref) {
4033
4143
  _useState28 = _slicedToArray(_useState27, 2),
4034
4144
  openActionKey = _useState28[0],
4035
4145
  setOpenActionKey = _useState28[1];
4036
- var openRef = React.useRef(null);
4037
4146
  React.useEffect(function () {
4038
4147
  if (typeof pageNumber === "number" && pageNumber !== currentPage) {
4039
4148
  setCurrentPage(pageNumber);
4149
+ // Note: onPageChange is intentionally not called here to avoid loops
4150
+ // Parent should handle page changes via controlled component pattern
4040
4151
  }
4041
- }, [pageNumber]);
4152
+ }, [pageNumber, currentPage]);
4042
4153
  React.useEffect(function () {
4043
4154
  setLimit(pageSize);
4044
4155
  }, [pageSize]);
4045
4156
 
4046
- // Check if mobile view
4157
+ // Check if mobile view with debouncing
4047
4158
  React.useEffect(function () {
4159
+ var timeoutId;
4048
4160
  var checkMobile = function checkMobile() {
4049
4161
  setIsMobileView(window.innerWidth < responsiveBreakpoint);
4050
4162
  };
4163
+ var debouncedCheck = function debouncedCheck() {
4164
+ clearTimeout(timeoutId);
4165
+ timeoutId = setTimeout(checkMobile, RESIZE_DEBOUNCE_MS);
4166
+ };
4051
4167
 
4052
4168
  // Initial check
4053
4169
  checkMobile();
4054
4170
 
4055
- // Add event listener
4056
- window.addEventListener("resize", checkMobile);
4171
+ // Add event listener with debouncing
4172
+ window.addEventListener("resize", debouncedCheck);
4057
4173
  return function () {
4058
- window.removeEventListener("resize", checkMobile);
4174
+ clearTimeout(timeoutId);
4175
+ window.removeEventListener("resize", debouncedCheck);
4059
4176
  };
4060
4177
  }, [responsiveBreakpoint]);
4061
- var isValidList = function isValidList(d) {
4062
- return Array.isArray(d);
4063
- };
4064
4178
 
4065
4179
  // Close column menu when clicking outside
4066
4180
  React.useEffect(function () {
4181
+ if (!showColumnMenu) return;
4067
4182
  var onDocClick = function onDocClick(e) {
4068
- if (showColumnMenu && columnMenuRef.current && !columnMenuRef.current.contains(e.target) && columnToggleBtnRef.current && !columnToggleBtnRef.current.contains(e.target)) {
4183
+ if (columnMenuRef.current && !columnMenuRef.current.contains(e.target) && columnToggleBtnRef.current && !columnToggleBtnRef.current.contains(e.target)) {
4069
4184
  setShowColumnMenu(false);
4070
4185
  }
4071
4186
  };
@@ -4075,26 +4190,38 @@ var Table = function Table(_ref) {
4075
4190
  };
4076
4191
  }, [showColumnMenu]);
4077
4192
 
4078
- // Close per-row actionmenu when clicking outside
4079
- React.useEffect(function () {
4080
- var handler = function handler(e) {
4081
- if (openActionKey && openRef.current && !openRef.current.contains(e.target)) {
4082
- setOpenActionKey(null);
4083
- }
4084
- };
4085
- document.addEventListener("click", handler);
4086
- return function () {
4087
- return document.removeEventListener("click", handler);
4088
- };
4089
- }, [openActionKey]);
4090
-
4091
4193
  // Visible columns derived from columnsState
4092
4194
  var visibleColumns = React.useMemo(function () {
4093
4195
  return columnsState.filter(function (col) {
4094
4196
  return col.isVisible !== false;
4095
4197
  });
4096
4198
  }, [columnsState]);
4097
- var toggleColumnVisibility = function toggleColumnVisibility(key) {
4199
+
4200
+ // Memoize actions
4201
+ var actionsToUse = React.useMemo(function () {
4202
+ return actions !== null && actions !== void 0 ? actions : DEFAULT_ACTIONS;
4203
+ }, [actions]);
4204
+
4205
+ // Memoize visible count
4206
+ var visibleCount = React.useMemo(function () {
4207
+ return (showSerial ? 1 : 0) + visibleColumns.length + (selectable ? 1 : 0) + (hasDetails ? 1 : 0) + (withAction ? 1 : 0);
4208
+ }, [showSerial, visibleColumns.length, selectable, hasDetails, withAction]);
4209
+
4210
+ // Memoize key-to-row mapping for selection
4211
+ var keyToRowMap = React.useMemo(function () {
4212
+ var map = new Map();
4213
+ filteredData.forEach(function (row, i) {
4214
+ map.set(getRowKey(row, i), row);
4215
+ });
4216
+ return map;
4217
+ }, [filteredData]);
4218
+
4219
+ // Wrapper for setCurrentPage that also calls onPageChange
4220
+ var handlePageChange = React.useCallback(function (newPage) {
4221
+ setCurrentPage(newPage);
4222
+ onPageChange === null || onPageChange === void 0 || onPageChange(newPage);
4223
+ }, [onPageChange]);
4224
+ var toggleColumnVisibility = React.useCallback(function (key) {
4098
4225
  setColumnsState(function (prev) {
4099
4226
  return prev.map(function (c) {
4100
4227
  return c.key === key ? _objectSpread$9(_objectSpread$9({}, c), {}, {
@@ -4102,58 +4229,23 @@ var Table = function Table(_ref) {
4102
4229
  }) : c;
4103
4230
  });
4104
4231
  });
4105
- setCurrentPage(1);
4106
- };
4232
+ handlePageChange(1);
4233
+ }, [handlePageChange]);
4107
4234
 
4108
4235
  // Handle actions
4109
- var handleOnAction = function handleOnAction(action, row) {
4236
+ var handleOnAction = React.useCallback(function (action, row) {
4110
4237
  var _action$onClick;
4111
4238
  (_action$onClick = action.onClick) === null || _action$onClick === void 0 || _action$onClick.call(action, {
4112
4239
  action: action,
4113
4240
  row: row
4114
4241
  });
4115
- onAction && onAction({
4242
+ onAction === null || onAction === void 0 || onAction({
4116
4243
  action: action,
4117
4244
  row: row
4118
4245
  });
4119
4246
  setOpenActionKey(null);
4120
4247
  setActionAnchor(null);
4121
- };
4122
- var defaultActions = [{
4123
- name: "edit",
4124
- label: "Edit",
4125
- onClick: function onClick() {
4126
- console.log("Edit action clicked");
4127
- },
4128
- icon: /*#__PURE__*/React.createElement(PenSquare, {
4129
- size: 16
4130
- })
4131
- }, {
4132
- name: "delete",
4133
- label: "Delete",
4134
- onClick: function onClick() {
4135
- console.log("Delete action clicked");
4136
- },
4137
- icon: /*#__PURE__*/React.createElement(Trash, {
4138
- size: 16
4139
- })
4140
- }, {
4141
- name: "view",
4142
- label: "View",
4143
- onClick: function onClick() {
4144
- console.log("View action clicked");
4145
- },
4146
- icon: /*#__PURE__*/React.createElement(Eye, {
4147
- size: 16
4148
- })
4149
- }];
4150
- var actionsToUse = actions !== undefined ? actions : defaultActions;
4151
- var getRowKey = function getRowKey(row, globalIndex) {
4152
- if (row == null) return String(globalIndex);
4153
- if (row.id !== undefined && row.id !== null) return String(row.id);
4154
- if (row._id !== undefined && row._id !== null) return String(row._id);
4155
- return String(globalIndex);
4156
- };
4248
+ }, [onAction]);
4157
4249
 
4158
4250
  // Sorting
4159
4251
  var sortedData = React.useMemo(function () {
@@ -4172,16 +4264,38 @@ var Table = function Table(_ref) {
4172
4264
  // Filtering
4173
4265
  var filteredData = React.useMemo(function () {
4174
4266
  if (serverSide || !filterable || !Object.keys(filters).length) return sortedData;
4175
- var q = (filters.global || "").toLowerCase();
4176
4267
  return sortedData.filter(function (row) {
4177
- return Object.values(row || {}).some(function (v) {
4178
- 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());
4179
4284
  });
4180
4285
  });
4181
- }, [sortedData, filters, filterable, serverSide]);
4182
- // Pagination indices
4183
- var startIndex = pagination ? (currentPage - 1) * limit : 0;
4184
- var endIndex = pagination ? startIndex + limit : filteredData.length;
4286
+ }, [sortedData, filters, filterable, serverSide, visibleColumns]);
4287
+
4288
+ // Pagination indices - memoized
4289
+ var _useMemo = React.useMemo(function () {
4290
+ var start = pagination ? (currentPage - 1) * limit : 0;
4291
+ var end = pagination ? start + limit : filteredData.length;
4292
+ return {
4293
+ startIndex: start,
4294
+ endIndex: end
4295
+ };
4296
+ }, [pagination, currentPage, limit, filteredData.length]),
4297
+ startIndex = _useMemo.startIndex,
4298
+ endIndex = _useMemo.endIndex;
4185
4299
 
4186
4300
  // Paginated view
4187
4301
  var paginatedData = React.useMemo(function () {
@@ -4195,7 +4309,7 @@ var Table = function Table(_ref) {
4195
4309
  }, [pagination, serverSide, totalRecords, filteredData.length, limit]);
4196
4310
 
4197
4311
  // Sorting handler
4198
- var handleSort = function handleSort(key) {
4312
+ var handleSort = React.useCallback(function (key) {
4199
4313
  if (!sortable) return;
4200
4314
  var direction = sortConfig.key === key && sortConfig.direction === "asc" ? "desc" : "asc";
4201
4315
  setSortConfig({
@@ -4203,10 +4317,10 @@ var Table = function Table(_ref) {
4203
4317
  direction: direction
4204
4318
  });
4205
4319
  onSort === null || onSort === void 0 || onSort(key, direction);
4206
- };
4320
+ }, [sortable, sortConfig, onSort]);
4207
4321
 
4208
- // Selection handlers
4209
- var handleSelectAll = function handleSelectAll() {
4322
+ // Selection handlers - optimized with memoized keyToRowMap
4323
+ var handleSelectAll = React.useCallback(function () {
4210
4324
  var newSelection = new Set(selectedRows);
4211
4325
  var pageRowKeys = paginatedData.map(function (r, i) {
4212
4326
  return getRowKey(r, startIndex + i);
@@ -4224,50 +4338,45 @@ var Table = function Table(_ref) {
4224
4338
  });
4225
4339
  }
4226
4340
  setSelectedRows(newSelection);
4227
- var keyToRow = new Map(filteredData.map(function (r, i) {
4228
- return [getRowKey(r, i), r];
4229
- }));
4230
4341
  var selectedData = Array.from(newSelection).map(function (k) {
4231
- return keyToRow.get(k);
4342
+ return keyToRowMap.get(k);
4232
4343
  }).filter(Boolean);
4233
4344
  onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange(selectedData);
4234
- };
4235
- var handleSelectRow = function handleSelectRow(row, rowIndexInPage) {
4345
+ }, [selectedRows, paginatedData, startIndex, keyToRowMap, onSelectionChange]);
4346
+ var handleSelectRow = React.useCallback(function (row, rowIndexInPage) {
4236
4347
  var globalIndex = startIndex + rowIndexInPage;
4237
4348
  var key = getRowKey(row, globalIndex);
4238
4349
  var newSelection = new Set(selectedRows);
4239
4350
  if (newSelection.has(key)) newSelection["delete"](key);else newSelection.add(key);
4240
4351
  setSelectedRows(newSelection);
4241
- var keyToRow = new Map(filteredData.map(function (r, i) {
4242
- return [getRowKey(r, i), r];
4243
- }));
4244
4352
  var selectedData = Array.from(newSelection).map(function (k) {
4245
- return keyToRow.get(k);
4353
+ return keyToRowMap.get(k);
4246
4354
  }).filter(Boolean);
4247
4355
  onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange(selectedData);
4248
- };
4249
- var toggleExpandRow = function toggleExpandRow(row, rowIndexInPage) {
4356
+ }, [startIndex, selectedRows, keyToRowMap, onSelectionChange]);
4357
+ var toggleExpandRow = React.useCallback(function (row, rowIndexInPage) {
4250
4358
  var globalIndex = startIndex + rowIndexInPage;
4251
4359
  var key = getRowKey(row, globalIndex);
4252
- var newExpanded = new Set(expandedRows);
4253
- if (newExpanded.has(key)) newExpanded["delete"](key);else newExpanded.add(key);
4254
- setExpandedRows(newExpanded);
4255
- };
4256
- var handleFilter = function handleFilter(key, value) {
4257
- var newFilters = _objectSpread$9(_objectSpread$9({}, filters), {}, _defineProperty$4({}, key, value));
4258
- setFilters(newFilters);
4259
- onFilter === null || onFilter === void 0 || onFilter(newFilters);
4260
- onFilterChange === null || onFilterChange === void 0 || onFilterChange(newFilters);
4261
- setCurrentPage(1);
4262
- };
4263
- var renderCell = function renderCell(column, row, globalIndex) {
4360
+ setExpandedRows(function (prev) {
4361
+ var newExpanded = new Set(prev);
4362
+ if (newExpanded.has(key)) newExpanded["delete"](key);else newExpanded.add(key);
4363
+ return newExpanded;
4364
+ });
4365
+ }, [startIndex]);
4366
+ var handleFilter = React.useCallback(function (key, value) {
4367
+ setFilters(function (prev) {
4368
+ var newFilters = _objectSpread$9(_objectSpread$9({}, prev), {}, _defineProperty$4({}, key, value));
4369
+ onFilter === null || onFilter === void 0 || onFilter(newFilters);
4370
+ onFilterChange === null || onFilterChange === void 0 || onFilterChange(newFilters);
4371
+ return newFilters;
4372
+ });
4373
+ handlePageChange(1);
4374
+ }, [onFilter, onFilterChange, handlePageChange]);
4375
+ var renderCell = React.useCallback(function (column, row, globalIndex) {
4264
4376
  if (column.render) return column.render(row, globalIndex);
4265
4377
  return row === null || row === void 0 ? void 0 : row[column.key];
4266
- };
4267
-
4268
- // UI counts
4269
- var visibleCount = (showSerial ? 1 : 0) + visibleColumns.length + (selectable ? 1 : 0) + (hasDetails ? 1 : 0) + (withAction ? 1 : 0);
4270
- var toggleActions = function toggleActions(e, actionCellKey, row) {
4378
+ }, []);
4379
+ var toggleActions = React.useCallback(function (e, actionCellKey, row) {
4271
4380
  e.stopPropagation();
4272
4381
  if (actionAnchor && actionAnchor.key === actionCellKey) {
4273
4382
  setOpenActionKey(null);
@@ -4280,7 +4389,7 @@ var Table = function Table(_ref) {
4280
4389
  row: row
4281
4390
  });
4282
4391
  }
4283
- };
4392
+ }, [actionAnchor]);
4284
4393
  React.useEffect(function () {
4285
4394
  if (!actionAnchor) return;
4286
4395
  var onDocClick = function onDocClick(ev) {
@@ -4291,9 +4400,9 @@ var Table = function Table(_ref) {
4291
4400
  setActionAnchor(null);
4292
4401
  };
4293
4402
  var onScrollResize = function onScrollResize() {
4294
- setOpenActionKey(function (k) {
4295
- return k ? k : null;
4296
- });
4403
+ // Close menu on scroll/resize to prevent positioning issues
4404
+ setOpenActionKey(null);
4405
+ setActionAnchor(null);
4297
4406
  };
4298
4407
  window.addEventListener("click", onDocClick);
4299
4408
  window.addEventListener("scroll", onScrollResize, true);
@@ -4304,58 +4413,163 @@ var Table = function Table(_ref) {
4304
4413
  window.removeEventListener("resize", onScrollResize);
4305
4414
  };
4306
4415
  }, [actionAnchor]);
4416
+
4417
+ // Track previous fetch params to prevent unnecessary calls
4418
+ var prevFetchParamsRef = React.useRef(null);
4419
+ var isFetchingRef = React.useRef(false);
4420
+ var isMountedRef = React.useRef(true);
4421
+ var onFetchRef = React.useRef(onFetch);
4422
+ var activeFetchIdRef = React.useRef(0);
4423
+
4424
+ // Keep ref in sync with onFetch
4307
4425
  React.useEffect(function () {
4308
- if (!onFetch) return;
4309
- onFetch({
4310
- setData: function setData(rows) {
4311
- return setTableData(Array.isArray(rows) ? rows : []);
4312
- },
4313
- setLoading: setLoading,
4426
+ onFetchRef.current = onFetch;
4427
+ }, [onFetch]);
4428
+
4429
+ // Track mount status
4430
+ React.useEffect(function () {
4431
+ isMountedRef.current = true;
4432
+ return function () {
4433
+ isMountedRef.current = false;
4434
+ isFetchingRef.current = false;
4435
+ };
4436
+ }, []);
4437
+
4438
+ // Serialize fetch params for comparison - serialize filters to avoid object reference issues
4439
+ var fetchParams = React.useMemo(function () {
4440
+ return {
4314
4441
  filters: filters,
4315
4442
  page: currentPage,
4316
4443
  limit: limit,
4317
- sort: sortConfig
4318
- });
4319
- }, [onFetch, filters, currentPage, limit, sortConfig]);
4320
- React.useEffect(function () {
4321
- if (!serverSide) setTableData(data);
4322
- }, [data, serverSide]);
4444
+ sortKey: sortConfig.key,
4445
+ sortDirection: sortConfig.direction
4446
+ };
4447
+ }, [filters, currentPage, limit, sortConfig.key, sortConfig.direction]);
4448
+ var fetchParamsString = React.useMemo(function () {
4449
+ return JSON.stringify(fetchParams);
4450
+ }, [fetchParams]);
4451
+
4452
+ // Consolidated data sync effect
4323
4453
  React.useEffect(function () {
4324
- if (!onFetch && isValidList(data)) setTableData(data);
4325
- }, [data, onFetch]);
4454
+ var currentOnFetch = onFetchRef.current;
4455
+ if (currentOnFetch) {
4456
+ // Only fetch if params actually changed and not already fetching
4457
+ if (!isFetchingRef.current && prevFetchParamsRef.current !== fetchParamsString) {
4458
+ prevFetchParamsRef.current = fetchParamsString;
4459
+ isFetchingRef.current = true;
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
4488
+ }
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);
4497
+ isFetchingRef.current = false;
4498
+ }
4499
+ });
4500
+ }
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
+ }
4508
+ }
4509
+ } else if (!serverSide && Array.isArray(data)) {
4510
+ setTableData(data);
4511
+ }
4512
+ }, [serverSide, data, fetchParamsString]);
4326
4513
 
4327
4514
  // Global search
4328
4515
  React.useEffect(function () {
4329
4516
  setSearchInput(filters.global || "");
4330
4517
  }, [filters.global]);
4331
- var applyGlobalSearch = function applyGlobalSearch() {
4518
+ var applyGlobalSearch = React.useCallback(function () {
4332
4519
  handleFilter("global", searchInput);
4333
- };
4334
- var onGlobalKeyDown = function onGlobalKeyDown(e) {
4520
+ }, [handleFilter, searchInput]);
4521
+ var onGlobalKeyDown = React.useCallback(function (e) {
4335
4522
  if (e.key === "Enter") {
4336
4523
  e.preventDefault();
4337
4524
  applyGlobalSearch();
4338
4525
  }
4339
- };
4340
- var handleLimitChange = function handleLimitChange(nextLimit) {
4526
+ }, [applyGlobalSearch]);
4527
+ var handleLimitChange = React.useCallback(function (nextLimit) {
4341
4528
  var parsed = Number(nextLimit);
4342
4529
  if (!Number.isFinite(parsed) || parsed <= 0) return;
4343
4530
  setLimit(parsed);
4344
- setCurrentPage(1);
4531
+ handlePageChange(1);
4345
4532
  onLimitChange === null || onLimitChange === void 0 || onLimitChange(parsed);
4346
- };
4533
+ }, [onLimitChange, handlePageChange]);
4347
4534
 
4348
- // Render mobile card
4349
- var renderMobileCard = function renderMobileCard(row, rowIndexInPage) {
4535
+ // Memoize limit options
4536
+ var limitOptionsMemo = React.useMemo(function () {
4537
+ var opts = Array.isArray(limitOptions) && limitOptions.length > 0 ? limitOptions : [25, 50, 100];
4538
+ return opts.map(function (opt) {
4539
+ return {
4540
+ label: String(opt),
4541
+ value: opt
4542
+ };
4543
+ });
4544
+ }, [limitOptions]);
4545
+
4546
+ // Extract row metadata computation
4547
+ var getRowMetadata = React.useCallback(function (row, rowIndexInPage) {
4350
4548
  var globalIndex = startIndex + rowIndexInPage;
4351
4549
  var key = getRowKey(row, globalIndex);
4352
4550
  var actionCellKey = "actions-".concat(key);
4353
- var extraRowClass = typeof rowClass === "function" ? rowClass({
4551
+ var extraRowClass = typeof rowClass === "function" ? (rowClass({
4354
4552
  row: row,
4355
4553
  rowIndex: globalIndex
4356
- }) : "";
4357
- var safeExtraRowClass = typeof extraRowClass === "string" ? extraRowClass.trim() : "";
4554
+ }) || "").trim() : "";
4358
4555
  var stripeBg = stripedRows && !selectedRows.has(key) ? stripedColors[globalIndex % stripedColors.length] : undefined;
4556
+ return {
4557
+ globalIndex: globalIndex,
4558
+ key: key,
4559
+ actionCellKey: actionCellKey,
4560
+ extraRowClass: extraRowClass,
4561
+ stripeBg: stripeBg
4562
+ };
4563
+ }, [startIndex, rowClass, stripedRows, selectedRows, stripedColors]);
4564
+
4565
+ // Render mobile card
4566
+ var renderMobileCard = React.useCallback(function (row, rowIndexInPage) {
4567
+ var _getRowMetadata = getRowMetadata(row, rowIndexInPage),
4568
+ globalIndex = _getRowMetadata.globalIndex,
4569
+ key = _getRowMetadata.key,
4570
+ actionCellKey = _getRowMetadata.actionCellKey,
4571
+ extraRowClass = _getRowMetadata.extraRowClass,
4572
+ stripeBg = _getRowMetadata.stripeBg;
4359
4573
  return /*#__PURE__*/React.createElement("div", {
4360
4574
  key: key,
4361
4575
  style: stripeBg ? {
@@ -4365,7 +4579,7 @@ var Table = function Table(_ref) {
4365
4579
  "cursor-pointer": !!onRowClick,
4366
4580
  "bg-primary-50 border-primary-200": selectedRows.has(key),
4367
4581
  "hover:shadow-md": !selectedRows.has(key)
4368
- }, safeExtraRowClass),
4582
+ }, extraRowClass),
4369
4583
  onClick: function onClick() {
4370
4584
  return onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row, globalIndex);
4371
4585
  }
@@ -4422,16 +4636,15 @@ var Table = function Table(_ref) {
4422
4636
  }), document.body))), /*#__PURE__*/React.createElement("div", {
4423
4637
  className: "p-4"
4424
4638
  }, visibleColumns.map(function (column, colIndex) {
4425
- var extraCellClass = typeof cellClass === "function" ? cellClass({
4639
+ var extraCellClass = typeof cellClass === "function" ? (cellClass({
4426
4640
  row: row,
4427
4641
  rowIndex: globalIndex,
4428
4642
  column: column,
4429
4643
  columnIndex: colIndex
4430
- }) : "";
4431
- var safeExtraCellClass = typeof extraCellClass === "string" ? extraCellClass.trim() : "";
4644
+ }) || "").trim() : "";
4432
4645
  return /*#__PURE__*/React.createElement("div", {
4433
4646
  key: column.key,
4434
- className: cn$1("flex justify-between items-center flex-row py-2 border-b last:border-b-0", safeExtraCellClass)
4647
+ className: cn$1("flex justify-between items-center flex-row py-2 border-b last:border-b-0", extraCellClass)
4435
4648
  }, /*#__PURE__*/React.createElement("div", {
4436
4649
  className: "text-xs font-medium text-gray-500 uppercase tracking-wide mb-1 sm:mb-0 sm:w-1/3 sm:pr-2"
4437
4650
  }, column.label), /*#__PURE__*/React.createElement("div", {
@@ -4443,10 +4656,10 @@ var Table = function Table(_ref) {
4443
4656
  row: row,
4444
4657
  index: globalIndex
4445
4658
  })));
4446
- };
4659
+ }, [getRowMetadata, hasDetails, selectable, showSerial, withAction, expandedRows, selectedRows, onRowClick, visibleColumns, renderCell, cellClass, DetailsComponent, actionsToUse, actionAnchor, openActionKey, handleOnAction, actionMenuRef, toggleActions, toggleExpandRow, handleSelectRow]);
4447
4660
 
4448
4661
  // Render mobile filters
4449
- var renderMobileFilters = function renderMobileFilters() {
4662
+ var renderMobileFilters = React.useCallback(function () {
4450
4663
  return /*#__PURE__*/React.createElement("div", {
4451
4664
  className: "mb-4 p-3 bg-gray-50 rounded-lg"
4452
4665
  }, /*#__PURE__*/React.createElement("div", {
@@ -4488,7 +4701,7 @@ var Table = function Table(_ref) {
4488
4701
  }
4489
4702
  }));
4490
4703
  })));
4491
- };
4704
+ }, [showMobileFilters, globalSearch, searchInput, onGlobalKeyDown, filterable, visibleColumns, filters, handleFilter, setShowMobileFilters, setSearchInput]);
4492
4705
 
4493
4706
  // Render
4494
4707
  return /*#__PURE__*/React.createElement("div", {
@@ -4511,23 +4724,11 @@ var Table = function Table(_ref) {
4511
4724
  htmlFor: "pagination-limit",
4512
4725
  className: "text-sm text-gray-600 whitespace-nowrap"
4513
4726
  }, "Show"), /*#__PURE__*/React.createElement(Select, {
4514
- options: Array.isArray(limitOptions) && limitOptions.length > 0 ? limitOptions.map(function (opt) {
4515
- return {
4516
- label: String(opt),
4517
- value: opt
4518
- };
4519
- }) : [25, 50, 100].map(function (opt) {
4520
- return {
4521
- label: String(opt),
4522
- value: opt
4523
- };
4524
- }),
4727
+ options: limitOptionsMemo,
4525
4728
  value: limit,
4526
4729
  allowClear: false,
4527
4730
  placeholder: "",
4528
- onChange: function onChange(e) {
4529
- return handleLimitChange(e);
4530
- },
4731
+ onChange: handleLimitChange,
4531
4732
  className: "w-20 md:w-30"
4532
4733
  }), /*#__PURE__*/React.createElement("p", {
4533
4734
  className: "text-sm text-gray-600 whitespace-nowrap"
@@ -4543,9 +4744,7 @@ var Table = function Table(_ref) {
4543
4744
  className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4544
4745
  disabled: currentPage === 1,
4545
4746
  onClick: function onClick() {
4546
- return setCurrentPage(function (p) {
4547
- return Math.max(1, p - 1);
4548
- });
4747
+ return handlePageChange(Math.max(1, currentPage - 1));
4549
4748
  },
4550
4749
  "aria-label": "Previous page"
4551
4750
  }, /*#__PURE__*/React.createElement(ChevronLeft, {
@@ -4554,9 +4753,7 @@ var Table = function Table(_ref) {
4554
4753
  className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4555
4754
  disabled: currentPage === totalPages,
4556
4755
  onClick: function onClick() {
4557
- return setCurrentPage(function (p) {
4558
- return Math.min(totalPages, p + 1);
4559
- });
4756
+ return handlePageChange(Math.min(totalPages, currentPage + 1));
4560
4757
  },
4561
4758
  "aria-label": "Next page"
4562
4759
  }, /*#__PURE__*/React.createElement(ChevronRight, {
@@ -4699,33 +4896,16 @@ var Table = function Table(_ref) {
4699
4896
  }, loading ? /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", {
4700
4897
  colSpan: visibleCount,
4701
4898
  className: "px-6 py-8 text-center"
4702
- }, /*#__PURE__*/React.createElement("div", {
4703
- className: "flex flex-col items-center gap-2"
4704
- }, /*#__PURE__*/React.createElement("div", {
4705
- className: "h-6 w-6 animate-spin rounded-full border-2 border-primary-600 border-t-transparent"
4706
- }), /*#__PURE__*/React.createElement("span", {
4707
- className: "text-sm text-gray-500"
4708
- }, "Loading...")))) : paginatedData.length === 0 ? /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", {
4899
+ }, /*#__PURE__*/React.createElement(LoadingState, null))) : paginatedData.length === 0 ? /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", {
4709
4900
  colSpan: visibleCount,
4710
4901
  className: "px-6 py-8 text-center"
4711
- }, /*#__PURE__*/React.createElement("div", {
4712
- className: "flex flex-col items-center gap-2"
4713
- }, /*#__PURE__*/React.createElement("div", {
4714
- className: "text-gray-400"
4715
- }, /*#__PURE__*/React.createElement(List, {
4716
- className: "h-8 w-8"
4717
- })), /*#__PURE__*/React.createElement("span", {
4718
- className: "text-sm text-gray-500"
4719
- }, "No results found.")))) : paginatedData.map(function (row, rowIndexInPage) {
4720
- var globalIndex = startIndex + rowIndexInPage;
4721
- var key = getRowKey(row, globalIndex);
4722
- var actionCellKey = "actions-".concat(key);
4723
- var extraRowClass = typeof rowClass === "function" ? rowClass({
4724
- row: row,
4725
- rowIndex: globalIndex
4726
- }) : "";
4727
- var safeExtraRowClass = typeof extraRowClass === "string" ? extraRowClass.trim() : "";
4728
- var stripeBg = stripedRows && !selectedRows.has(key) ? stripedColors[globalIndex % stripedColors.length] : undefined;
4902
+ }, /*#__PURE__*/React.createElement(EmptyState, null))) : paginatedData.map(function (row, rowIndexInPage) {
4903
+ var _getRowMetadata2 = getRowMetadata(row, rowIndexInPage),
4904
+ globalIndex = _getRowMetadata2.globalIndex,
4905
+ key = _getRowMetadata2.key,
4906
+ actionCellKey = _getRowMetadata2.actionCellKey,
4907
+ extraRowClass = _getRowMetadata2.extraRowClass,
4908
+ stripeBg = _getRowMetadata2.stripeBg;
4729
4909
  return /*#__PURE__*/React.createElement(React.Fragment, {
4730
4910
  key: key
4731
4911
  }, /*#__PURE__*/React.createElement("tr", {
@@ -4735,7 +4915,7 @@ var Table = function Table(_ref) {
4735
4915
  className: cn$1("hover:bg-gray-50", {
4736
4916
  "cursor-pointer": !!onRowClick,
4737
4917
  "bg-primary-50": selectedRows.has(key)
4738
- }, safeExtraRowClass),
4918
+ }, extraRowClass),
4739
4919
  onClick: function onClick() {
4740
4920
  return onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row, globalIndex);
4741
4921
  }
@@ -4763,16 +4943,15 @@ var Table = function Table(_ref) {
4763
4943
  return handleSelectRow(row, rowIndexInPage);
4764
4944
  }
4765
4945
  })), visibleColumns.map(function (column, columnIndex) {
4766
- var extraCellClass = typeof cellClass === "function" ? cellClass({
4946
+ var extraCellClass = typeof cellClass === "function" ? (cellClass({
4767
4947
  row: row,
4768
4948
  rowIndex: globalIndex,
4769
4949
  column: column,
4770
4950
  columnIndex: columnIndex
4771
- }) : "";
4772
- var safeExtraCellClass = typeof extraCellClass === "string" ? extraCellClass.trim() : "";
4951
+ }) || "").trim() : "";
4773
4952
  return /*#__PURE__*/React.createElement("td", {
4774
4953
  key: column.key,
4775
- className: cn$1("px-6 py-4 whitespace-nowrap text-sm text-gray-900", safeExtraCellClass)
4954
+ className: cn$1("px-6 py-4 whitespace-nowrap text-sm text-gray-900", extraCellClass)
4776
4955
  }, renderCell(column, row, globalIndex));
4777
4956
  }), withAction && /*#__PURE__*/React.createElement("td", {
4778
4957
  className: "px-4 py-4 text-sm text-right",
@@ -4815,23 +4994,9 @@ var Table = function Table(_ref) {
4815
4994
  className: "p-4"
4816
4995
  }, loading ? /*#__PURE__*/React.createElement("div", {
4817
4996
  className: "py-8 text-center"
4818
- }, /*#__PURE__*/React.createElement("div", {
4819
- className: "flex flex-col items-center gap-2"
4820
- }, /*#__PURE__*/React.createElement("div", {
4821
- className: "h-6 w-6 animate-spin rounded-full border-2 border-primary-600 border-t-transparent"
4822
- }), /*#__PURE__*/React.createElement("span", {
4823
- className: "text-sm text-gray-500"
4824
- }, "Loading..."))) : paginatedData.length === 0 ? /*#__PURE__*/React.createElement("div", {
4997
+ }, /*#__PURE__*/React.createElement(LoadingState, null)) : paginatedData.length === 0 ? /*#__PURE__*/React.createElement("div", {
4825
4998
  className: "py-8 text-center"
4826
- }, /*#__PURE__*/React.createElement("div", {
4827
- className: "flex flex-col items-center gap-2"
4828
- }, /*#__PURE__*/React.createElement("div", {
4829
- className: "text-gray-400"
4830
- }, /*#__PURE__*/React.createElement(List, {
4831
- className: "h-8 w-8"
4832
- })), /*#__PURE__*/React.createElement("span", {
4833
- className: "text-sm text-gray-500"
4834
- }, "No results found."))) : paginatedData.map(function (row, rowIndexInPage) {
4999
+ }, /*#__PURE__*/React.createElement(EmptyState, null)) : paginatedData.map(function (row, rowIndexInPage) {
4835
5000
  return renderMobileCard(row, rowIndexInPage);
4836
5001
  })), pagination && /*#__PURE__*/React.createElement("div", {
4837
5002
  className: "p-4 border-t"
@@ -4845,25 +5010,11 @@ var Table = function Table(_ref) {
4845
5010
  className: "rounded-lg md:rounded-md border border-gray-300 px-4 py-2 text-sm disabled:opacity-50 hover:bg-gray-50",
4846
5011
  disabled: currentPage === 1,
4847
5012
  onClick: function onClick() {
4848
- return setCurrentPage(function (p) {
4849
- return Math.max(1, p - 1);
4850
- });
5013
+ return handlePageChange(Math.max(1, currentPage - 1));
4851
5014
  }
4852
5015
  }, "Previous"), /*#__PURE__*/React.createElement("div", {
4853
5016
  className: "flex items-center gap-1"
4854
- }, Array.from({
4855
- length: Math.min(5, totalPages)
4856
- }, function (_, i) {
4857
- var pageNum;
4858
- if (totalPages <= 5) {
4859
- pageNum = i + 1;
4860
- } else if (currentPage <= 3) {
4861
- pageNum = i + 1;
4862
- } else if (currentPage >= totalPages - 2) {
4863
- pageNum = totalPages - 4 + i;
4864
- } else {
4865
- pageNum = currentPage - 2 + i;
4866
- }
5017
+ }, getPaginationNumbers(currentPage, totalPages).map(function (pageNum) {
4867
5018
  return /*#__PURE__*/React.createElement("button", {
4868
5019
  key: pageNum,
4869
5020
  className: cn$1("h-8 w-8 rounded-lg md:rounded-md text-sm", {
@@ -4871,19 +5022,23 @@ var Table = function Table(_ref) {
4871
5022
  "border border-gray-300 hover:bg-gray-50": pageNum !== currentPage
4872
5023
  }),
4873
5024
  onClick: function onClick() {
4874
- return setCurrentPage(pageNum);
5025
+ return handlePageChange(pageNum);
4875
5026
  }
4876
5027
  }, pageNum);
4877
5028
  })), /*#__PURE__*/React.createElement("button", {
4878
5029
  className: "rounded-lg md:rounded-md border border-gray-300 px-4 py-2 text-sm disabled:opacity-50 hover:bg-gray-50",
4879
5030
  disabled: currentPage === totalPages,
4880
5031
  onClick: function onClick() {
4881
- return setCurrentPage(function (p) {
4882
- return Math.min(totalPages, p + 1);
4883
- });
5032
+ return handlePageChange(Math.min(totalPages, currentPage + 1));
4884
5033
  }
4885
5034
  }, "Next")))));
4886
5035
  };
5036
+
5037
+ // ActionMenuPortal constants
5038
+ var MENU_WIDTH = 180;
5039
+ var MAX_MENU_HEIGHT = 320;
5040
+ var MENU_MARGIN = 8;
5041
+ var MIN_MENU_HEIGHT = 80;
4887
5042
  function ActionMenuPortal(_ref2) {
4888
5043
  var anchorElem = _ref2.anchorElem,
4889
5044
  anchorRow = _ref2.anchorRow,
@@ -4897,16 +5052,12 @@ function ActionMenuPortal(_ref2) {
4897
5052
  top: 0,
4898
5053
  transformOrigin: "top right",
4899
5054
  maxHeight: 300,
4900
- width: 180,
5055
+ width: MENU_WIDTH,
4901
5056
  opacity: 0
4902
5057
  }),
4903
5058
  _useState30 = _slicedToArray(_useState29, 2),
4904
5059
  style = _useState30[0],
4905
5060
  setStyle = _useState30[1];
4906
- var menuWidth = 180;
4907
- var maxMenuHeight = 320;
4908
- var margin = 8;
4909
- var minMenuHeight = 80;
4910
5061
 
4911
5062
  // compute position using actual menu height (if available) and layout measurements
4912
5063
  var computePosition = function computePosition() {
@@ -4918,17 +5069,17 @@ function ActionMenuPortal(_ref2) {
4918
5069
  var spaceAbove = rect.top;
4919
5070
 
4920
5071
  // allowed max height based on available space
4921
- var allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, Math.max(spaceBelow - margin, spaceAbove - margin)));
5072
+ var allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, Math.max(spaceBelow - MENU_MARGIN, spaceAbove - MENU_MARGIN)));
4922
5073
 
4923
5074
  // choose placement by comparing actual available spaces
4924
5075
  var placement = "bottom";
4925
- if (spaceBelow < 160 && spaceAbove > spaceBelow) {
5076
+ if (spaceBelow < MENU_PLACEMENT_THRESHOLD && spaceAbove > spaceBelow) {
4926
5077
  placement = "top";
4927
5078
  // when placing on top we should cap allowedMaxHeight by spaceAbove
4928
- allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceAbove - margin));
5079
+ allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, spaceAbove - MENU_MARGIN));
4929
5080
  } else {
4930
5081
  // placing bottom: cap by spaceBelow
4931
- allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceBelow - margin));
5082
+ allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, spaceBelow - MENU_MARGIN));
4932
5083
  }
4933
5084
 
4934
5085
  // measure the menu's content height if we can, and compute the actual menu height we'll use.
@@ -4945,23 +5096,23 @@ function ActionMenuPortal(_ref2) {
4945
5096
  var top;
4946
5097
  if (placement === "top") {
4947
5098
  // place menu so its bottom sits just above the anchor (rect.top - margin)
4948
- top = rect.top + scrollY - measuredMenuHeight - margin;
5099
+ top = rect.top + scrollY - measuredMenuHeight - MENU_MARGIN;
4949
5100
  } else {
4950
5101
  // place menu just below the anchor (rect.bottom + margin)
4951
- top = rect.bottom + scrollY + margin;
5102
+ top = rect.bottom + scrollY + MENU_MARGIN;
4952
5103
  }
4953
5104
 
4954
5105
  // clamp top to viewport (so it never goes off-screen)
4955
- var minTop = margin + scrollY;
4956
- var maxTop = window.innerHeight + scrollY - measuredMenuHeight - margin;
5106
+ var minTop = MENU_MARGIN + scrollY;
5107
+ var maxTop = window.innerHeight + scrollY - measuredMenuHeight - MENU_MARGIN;
4957
5108
  if (top < minTop) top = minTop;
4958
5109
  if (top > maxTop) top = maxTop;
4959
5110
 
4960
5111
  // compute left and clamp horizontally
4961
- var left = rect.right + scrollX - menuWidth;
4962
- if (left < margin) left = margin;
4963
- if (left + menuWidth > window.innerWidth - margin) {
4964
- left = Math.max(margin, window.innerWidth - menuWidth - margin);
5112
+ var left = rect.right + scrollX - MENU_WIDTH;
5113
+ if (left < MENU_MARGIN) left = MENU_MARGIN;
5114
+ if (left + MENU_WIDTH > window.innerWidth - MENU_MARGIN) {
5115
+ left = Math.max(MENU_MARGIN, window.innerWidth - MENU_WIDTH - MENU_MARGIN);
4965
5116
  }
4966
5117
  var transformOrigin = placement === "bottom" ? "top right" : "bottom right";
4967
5118
 
@@ -4971,7 +5122,7 @@ function ActionMenuPortal(_ref2) {
4971
5122
  top: top,
4972
5123
  transformOrigin: transformOrigin,
4973
5124
  maxHeight: allowedMaxHeight,
4974
- width: menuWidth,
5125
+ width: MENU_WIDTH,
4975
5126
  opacity: 1
4976
5127
  });
4977
5128
  };
@@ -4994,7 +5145,8 @@ function ActionMenuPortal(_ref2) {
4994
5145
  window.removeEventListener("scroll", onScrollOrResize, true);
4995
5146
  };
4996
5147
  // Recompute when anchor element changes or actions change (content height may change).
4997
- }, [anchorElem, actions, menuRef]);
5148
+ // Note: menuRef is a ref object and never changes, so it's safe to include
5149
+ }, [anchorElem, actions]);
4998
5150
  return /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/React.createElement("div", {
4999
5151
  ref: menuRef,
5000
5152
  style: {