@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.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,44 @@ 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
+ // Note: onPageChange is intentionally not called here to avoid loops
4130
+ // Parent should handle page changes via controlled component pattern
4020
4131
  }
4021
- }, [pageNumber]);
4132
+ }, [pageNumber, currentPage]);
4022
4133
  useEffect(function () {
4023
4134
  setLimit(pageSize);
4024
4135
  }, [pageSize]);
4025
4136
 
4026
- // Check if mobile view
4137
+ // Check if mobile view with debouncing
4027
4138
  useEffect(function () {
4139
+ var timeoutId;
4028
4140
  var checkMobile = function checkMobile() {
4029
4141
  setIsMobileView(window.innerWidth < responsiveBreakpoint);
4030
4142
  };
4143
+ var debouncedCheck = function debouncedCheck() {
4144
+ clearTimeout(timeoutId);
4145
+ timeoutId = setTimeout(checkMobile, RESIZE_DEBOUNCE_MS);
4146
+ };
4031
4147
 
4032
4148
  // Initial check
4033
4149
  checkMobile();
4034
4150
 
4035
- // Add event listener
4036
- window.addEventListener("resize", checkMobile);
4151
+ // Add event listener with debouncing
4152
+ window.addEventListener("resize", debouncedCheck);
4037
4153
  return function () {
4038
- window.removeEventListener("resize", checkMobile);
4154
+ clearTimeout(timeoutId);
4155
+ window.removeEventListener("resize", debouncedCheck);
4039
4156
  };
4040
4157
  }, [responsiveBreakpoint]);
4041
- var isValidList = function isValidList(d) {
4042
- return Array.isArray(d);
4043
- };
4044
4158
 
4045
4159
  // Close column menu when clicking outside
4046
4160
  useEffect(function () {
4161
+ if (!showColumnMenu) return;
4047
4162
  var onDocClick = function onDocClick(e) {
4048
- if (showColumnMenu && columnMenuRef.current && !columnMenuRef.current.contains(e.target) && columnToggleBtnRef.current && !columnToggleBtnRef.current.contains(e.target)) {
4163
+ if (columnMenuRef.current && !columnMenuRef.current.contains(e.target) && columnToggleBtnRef.current && !columnToggleBtnRef.current.contains(e.target)) {
4049
4164
  setShowColumnMenu(false);
4050
4165
  }
4051
4166
  };
@@ -4055,26 +4170,38 @@ var Table = function Table(_ref) {
4055
4170
  };
4056
4171
  }, [showColumnMenu]);
4057
4172
 
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
4173
  // Visible columns derived from columnsState
4072
4174
  var visibleColumns = useMemo(function () {
4073
4175
  return columnsState.filter(function (col) {
4074
4176
  return col.isVisible !== false;
4075
4177
  });
4076
4178
  }, [columnsState]);
4077
- var toggleColumnVisibility = function toggleColumnVisibility(key) {
4179
+
4180
+ // Memoize actions
4181
+ var actionsToUse = useMemo(function () {
4182
+ return actions !== null && actions !== void 0 ? actions : DEFAULT_ACTIONS;
4183
+ }, [actions]);
4184
+
4185
+ // Memoize visible count
4186
+ var visibleCount = useMemo(function () {
4187
+ return (showSerial ? 1 : 0) + visibleColumns.length + (selectable ? 1 : 0) + (hasDetails ? 1 : 0) + (withAction ? 1 : 0);
4188
+ }, [showSerial, visibleColumns.length, selectable, hasDetails, withAction]);
4189
+
4190
+ // Memoize key-to-row mapping for selection
4191
+ var keyToRowMap = useMemo(function () {
4192
+ var map = new Map();
4193
+ filteredData.forEach(function (row, i) {
4194
+ map.set(getRowKey(row, i), row);
4195
+ });
4196
+ return map;
4197
+ }, [filteredData]);
4198
+
4199
+ // Wrapper for setCurrentPage that also calls onPageChange
4200
+ var handlePageChange = useCallback(function (newPage) {
4201
+ setCurrentPage(newPage);
4202
+ onPageChange === null || onPageChange === void 0 || onPageChange(newPage);
4203
+ }, [onPageChange]);
4204
+ var toggleColumnVisibility = useCallback(function (key) {
4078
4205
  setColumnsState(function (prev) {
4079
4206
  return prev.map(function (c) {
4080
4207
  return c.key === key ? _objectSpread$9(_objectSpread$9({}, c), {}, {
@@ -4082,58 +4209,23 @@ var Table = function Table(_ref) {
4082
4209
  }) : c;
4083
4210
  });
4084
4211
  });
4085
- setCurrentPage(1);
4086
- };
4212
+ handlePageChange(1);
4213
+ }, [handlePageChange]);
4087
4214
 
4088
4215
  // Handle actions
4089
- var handleOnAction = function handleOnAction(action, row) {
4216
+ var handleOnAction = useCallback(function (action, row) {
4090
4217
  var _action$onClick;
4091
4218
  (_action$onClick = action.onClick) === null || _action$onClick === void 0 || _action$onClick.call(action, {
4092
4219
  action: action,
4093
4220
  row: row
4094
4221
  });
4095
- onAction && onAction({
4222
+ onAction === null || onAction === void 0 || onAction({
4096
4223
  action: action,
4097
4224
  row: row
4098
4225
  });
4099
4226
  setOpenActionKey(null);
4100
4227
  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
- };
4228
+ }, [onAction]);
4137
4229
 
4138
4230
  // Sorting
4139
4231
  var sortedData = useMemo(function () {
@@ -4152,16 +4244,38 @@ var Table = function Table(_ref) {
4152
4244
  // Filtering
4153
4245
  var filteredData = useMemo(function () {
4154
4246
  if (serverSide || !filterable || !Object.keys(filters).length) return sortedData;
4155
- var q = (filters.global || "").toLowerCase();
4156
4247
  return sortedData.filter(function (row) {
4157
- return Object.values(row || {}).some(function (v) {
4158
- return String(v).toLowerCase().includes(q);
4248
+ // Apply global search filter if present
4249
+ if (filters.global) {
4250
+ var q = filters.global.toLowerCase();
4251
+ var matchesGlobal = Object.values(row || {}).some(function (v) {
4252
+ return String(v).toLowerCase().includes(q);
4253
+ });
4254
+ if (!matchesGlobal) return false;
4255
+ }
4256
+
4257
+ // Apply column-specific filters
4258
+ return visibleColumns.every(function (column) {
4259
+ var columnFilter = filters[column.key];
4260
+ if (!columnFilter) return true; // No filter for this column
4261
+
4262
+ var cellValue = row === null || row === void 0 ? void 0 : row[column.key];
4263
+ return String(cellValue || "").toLowerCase().includes(columnFilter.toLowerCase());
4159
4264
  });
4160
4265
  });
4161
- }, [sortedData, filters, filterable, serverSide]);
4162
- // Pagination indices
4163
- var startIndex = pagination ? (currentPage - 1) * limit : 0;
4164
- var endIndex = pagination ? startIndex + limit : filteredData.length;
4266
+ }, [sortedData, filters, filterable, serverSide, visibleColumns]);
4267
+
4268
+ // Pagination indices - memoized
4269
+ var _useMemo = useMemo(function () {
4270
+ var start = pagination ? (currentPage - 1) * limit : 0;
4271
+ var end = pagination ? start + limit : filteredData.length;
4272
+ return {
4273
+ startIndex: start,
4274
+ endIndex: end
4275
+ };
4276
+ }, [pagination, currentPage, limit, filteredData.length]),
4277
+ startIndex = _useMemo.startIndex,
4278
+ endIndex = _useMemo.endIndex;
4165
4279
 
4166
4280
  // Paginated view
4167
4281
  var paginatedData = useMemo(function () {
@@ -4175,7 +4289,7 @@ var Table = function Table(_ref) {
4175
4289
  }, [pagination, serverSide, totalRecords, filteredData.length, limit]);
4176
4290
 
4177
4291
  // Sorting handler
4178
- var handleSort = function handleSort(key) {
4292
+ var handleSort = useCallback(function (key) {
4179
4293
  if (!sortable) return;
4180
4294
  var direction = sortConfig.key === key && sortConfig.direction === "asc" ? "desc" : "asc";
4181
4295
  setSortConfig({
@@ -4183,10 +4297,10 @@ var Table = function Table(_ref) {
4183
4297
  direction: direction
4184
4298
  });
4185
4299
  onSort === null || onSort === void 0 || onSort(key, direction);
4186
- };
4300
+ }, [sortable, sortConfig, onSort]);
4187
4301
 
4188
- // Selection handlers
4189
- var handleSelectAll = function handleSelectAll() {
4302
+ // Selection handlers - optimized with memoized keyToRowMap
4303
+ var handleSelectAll = useCallback(function () {
4190
4304
  var newSelection = new Set(selectedRows);
4191
4305
  var pageRowKeys = paginatedData.map(function (r, i) {
4192
4306
  return getRowKey(r, startIndex + i);
@@ -4204,50 +4318,45 @@ var Table = function Table(_ref) {
4204
4318
  });
4205
4319
  }
4206
4320
  setSelectedRows(newSelection);
4207
- var keyToRow = new Map(filteredData.map(function (r, i) {
4208
- return [getRowKey(r, i), r];
4209
- }));
4210
4321
  var selectedData = Array.from(newSelection).map(function (k) {
4211
- return keyToRow.get(k);
4322
+ return keyToRowMap.get(k);
4212
4323
  }).filter(Boolean);
4213
4324
  onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange(selectedData);
4214
- };
4215
- var handleSelectRow = function handleSelectRow(row, rowIndexInPage) {
4325
+ }, [selectedRows, paginatedData, startIndex, keyToRowMap, onSelectionChange]);
4326
+ var handleSelectRow = useCallback(function (row, rowIndexInPage) {
4216
4327
  var globalIndex = startIndex + rowIndexInPage;
4217
4328
  var key = getRowKey(row, globalIndex);
4218
4329
  var newSelection = new Set(selectedRows);
4219
4330
  if (newSelection.has(key)) newSelection["delete"](key);else newSelection.add(key);
4220
4331
  setSelectedRows(newSelection);
4221
- var keyToRow = new Map(filteredData.map(function (r, i) {
4222
- return [getRowKey(r, i), r];
4223
- }));
4224
4332
  var selectedData = Array.from(newSelection).map(function (k) {
4225
- return keyToRow.get(k);
4333
+ return keyToRowMap.get(k);
4226
4334
  }).filter(Boolean);
4227
4335
  onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange(selectedData);
4228
- };
4229
- var toggleExpandRow = function toggleExpandRow(row, rowIndexInPage) {
4336
+ }, [startIndex, selectedRows, keyToRowMap, onSelectionChange]);
4337
+ var toggleExpandRow = useCallback(function (row, rowIndexInPage) {
4230
4338
  var globalIndex = startIndex + rowIndexInPage;
4231
4339
  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);
4241
- setCurrentPage(1);
4242
- };
4243
- var renderCell = function renderCell(column, row, globalIndex) {
4340
+ setExpandedRows(function (prev) {
4341
+ var newExpanded = new Set(prev);
4342
+ if (newExpanded.has(key)) newExpanded["delete"](key);else newExpanded.add(key);
4343
+ return newExpanded;
4344
+ });
4345
+ }, [startIndex]);
4346
+ var handleFilter = useCallback(function (key, value) {
4347
+ setFilters(function (prev) {
4348
+ var newFilters = _objectSpread$9(_objectSpread$9({}, prev), {}, _defineProperty$4({}, key, value));
4349
+ onFilter === null || onFilter === void 0 || onFilter(newFilters);
4350
+ onFilterChange === null || onFilterChange === void 0 || onFilterChange(newFilters);
4351
+ return newFilters;
4352
+ });
4353
+ handlePageChange(1);
4354
+ }, [onFilter, onFilterChange, handlePageChange]);
4355
+ var renderCell = useCallback(function (column, row, globalIndex) {
4244
4356
  if (column.render) return column.render(row, globalIndex);
4245
4357
  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) {
4358
+ }, []);
4359
+ var toggleActions = useCallback(function (e, actionCellKey, row) {
4251
4360
  e.stopPropagation();
4252
4361
  if (actionAnchor && actionAnchor.key === actionCellKey) {
4253
4362
  setOpenActionKey(null);
@@ -4260,7 +4369,7 @@ var Table = function Table(_ref) {
4260
4369
  row: row
4261
4370
  });
4262
4371
  }
4263
- };
4372
+ }, [actionAnchor]);
4264
4373
  useEffect(function () {
4265
4374
  if (!actionAnchor) return;
4266
4375
  var onDocClick = function onDocClick(ev) {
@@ -4271,9 +4380,9 @@ var Table = function Table(_ref) {
4271
4380
  setActionAnchor(null);
4272
4381
  };
4273
4382
  var onScrollResize = function onScrollResize() {
4274
- setOpenActionKey(function (k) {
4275
- return k ? k : null;
4276
- });
4383
+ // Close menu on scroll/resize to prevent positioning issues
4384
+ setOpenActionKey(null);
4385
+ setActionAnchor(null);
4277
4386
  };
4278
4387
  window.addEventListener("click", onDocClick);
4279
4388
  window.addEventListener("scroll", onScrollResize, true);
@@ -4284,58 +4393,163 @@ var Table = function Table(_ref) {
4284
4393
  window.removeEventListener("resize", onScrollResize);
4285
4394
  };
4286
4395
  }, [actionAnchor]);
4396
+
4397
+ // Track previous fetch params to prevent unnecessary calls
4398
+ var prevFetchParamsRef = useRef(null);
4399
+ var isFetchingRef = useRef(false);
4400
+ var isMountedRef = useRef(true);
4401
+ var onFetchRef = useRef(onFetch);
4402
+ var activeFetchIdRef = useRef(0);
4403
+
4404
+ // Keep ref in sync with onFetch
4287
4405
  useEffect(function () {
4288
- if (!onFetch) return;
4289
- onFetch({
4290
- setData: function setData(rows) {
4291
- return setTableData(Array.isArray(rows) ? rows : []);
4292
- },
4293
- setLoading: setLoading,
4406
+ onFetchRef.current = onFetch;
4407
+ }, [onFetch]);
4408
+
4409
+ // Track mount status
4410
+ useEffect(function () {
4411
+ isMountedRef.current = true;
4412
+ return function () {
4413
+ isMountedRef.current = false;
4414
+ isFetchingRef.current = false;
4415
+ };
4416
+ }, []);
4417
+
4418
+ // Serialize fetch params for comparison - serialize filters to avoid object reference issues
4419
+ var fetchParams = useMemo(function () {
4420
+ return {
4294
4421
  filters: filters,
4295
4422
  page: currentPage,
4296
4423
  limit: limit,
4297
- sort: sortConfig
4298
- });
4299
- }, [onFetch, filters, currentPage, limit, sortConfig]);
4300
- useEffect(function () {
4301
- if (!serverSide) setTableData(data);
4302
- }, [data, serverSide]);
4424
+ sortKey: sortConfig.key,
4425
+ sortDirection: sortConfig.direction
4426
+ };
4427
+ }, [filters, currentPage, limit, sortConfig.key, sortConfig.direction]);
4428
+ var fetchParamsString = useMemo(function () {
4429
+ return JSON.stringify(fetchParams);
4430
+ }, [fetchParams]);
4431
+
4432
+ // Consolidated data sync effect
4303
4433
  useEffect(function () {
4304
- if (!onFetch && isValidList(data)) setTableData(data);
4305
- }, [data, onFetch]);
4434
+ var currentOnFetch = onFetchRef.current;
4435
+ if (currentOnFetch) {
4436
+ // Only fetch if params actually changed and not already fetching
4437
+ if (!isFetchingRef.current && prevFetchParamsRef.current !== fetchParamsString) {
4438
+ prevFetchParamsRef.current = fetchParamsString;
4439
+ isFetchingRef.current = true;
4440
+
4441
+ // Track fetch ID to prevent race conditions
4442
+ var fetchId = Date.now();
4443
+ activeFetchIdRef.current = fetchId;
4444
+ try {
4445
+ var result = currentOnFetch({
4446
+ setData: function setData(rows) {
4447
+ // Only update if component is still mounted and this is the active fetch
4448
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4449
+ setTableData(Array.isArray(rows) ? rows : []);
4450
+ // Don't set isFetchingRef to false here - let setLoading handle it
4451
+ }
4452
+ },
4453
+ setLoading: function setLoading(loading) {
4454
+ // Only update if component is still mounted and this is the active fetch
4455
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4456
+ _setLoading(loading);
4457
+ if (!loading) {
4458
+ isFetchingRef.current = false;
4459
+ }
4460
+ }
4461
+ },
4462
+ filters: fetchParams.filters,
4463
+ page: fetchParams.page,
4464
+ limit: fetchParams.limit,
4465
+ sort: {
4466
+ key: fetchParams.sortKey,
4467
+ direction: fetchParams.sortDirection
4468
+ }
4469
+ });
4470
+
4471
+ // Handle promise if onFetch returns one
4472
+ if (result && typeof result.then === "function") {
4473
+ result["catch"](function (error) {
4474
+ console.error("Table onFetch error:", error);
4475
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4476
+ _setLoading(false);
4477
+ isFetchingRef.current = false;
4478
+ }
4479
+ });
4480
+ }
4481
+ } catch (error) {
4482
+ console.error("Table onFetch error:", error);
4483
+ if (isMountedRef.current && activeFetchIdRef.current === fetchId) {
4484
+ _setLoading(false);
4485
+ isFetchingRef.current = false;
4486
+ }
4487
+ }
4488
+ }
4489
+ } else if (!serverSide && Array.isArray(data)) {
4490
+ setTableData(data);
4491
+ }
4492
+ }, [serverSide, data, fetchParamsString]);
4306
4493
 
4307
4494
  // Global search
4308
4495
  useEffect(function () {
4309
4496
  setSearchInput(filters.global || "");
4310
4497
  }, [filters.global]);
4311
- var applyGlobalSearch = function applyGlobalSearch() {
4498
+ var applyGlobalSearch = useCallback(function () {
4312
4499
  handleFilter("global", searchInput);
4313
- };
4314
- var onGlobalKeyDown = function onGlobalKeyDown(e) {
4500
+ }, [handleFilter, searchInput]);
4501
+ var onGlobalKeyDown = useCallback(function (e) {
4315
4502
  if (e.key === "Enter") {
4316
4503
  e.preventDefault();
4317
4504
  applyGlobalSearch();
4318
4505
  }
4319
- };
4320
- var handleLimitChange = function handleLimitChange(nextLimit) {
4506
+ }, [applyGlobalSearch]);
4507
+ var handleLimitChange = useCallback(function (nextLimit) {
4321
4508
  var parsed = Number(nextLimit);
4322
4509
  if (!Number.isFinite(parsed) || parsed <= 0) return;
4323
4510
  setLimit(parsed);
4324
- setCurrentPage(1);
4511
+ handlePageChange(1);
4325
4512
  onLimitChange === null || onLimitChange === void 0 || onLimitChange(parsed);
4326
- };
4513
+ }, [onLimitChange, handlePageChange]);
4327
4514
 
4328
- // Render mobile card
4329
- var renderMobileCard = function renderMobileCard(row, rowIndexInPage) {
4515
+ // Memoize limit options
4516
+ var limitOptionsMemo = useMemo(function () {
4517
+ var opts = Array.isArray(limitOptions) && limitOptions.length > 0 ? limitOptions : [25, 50, 100];
4518
+ return opts.map(function (opt) {
4519
+ return {
4520
+ label: String(opt),
4521
+ value: opt
4522
+ };
4523
+ });
4524
+ }, [limitOptions]);
4525
+
4526
+ // Extract row metadata computation
4527
+ var getRowMetadata = useCallback(function (row, rowIndexInPage) {
4330
4528
  var globalIndex = startIndex + rowIndexInPage;
4331
4529
  var key = getRowKey(row, globalIndex);
4332
4530
  var actionCellKey = "actions-".concat(key);
4333
- var extraRowClass = typeof rowClass === "function" ? rowClass({
4531
+ var extraRowClass = typeof rowClass === "function" ? (rowClass({
4334
4532
  row: row,
4335
4533
  rowIndex: globalIndex
4336
- }) : "";
4337
- var safeExtraRowClass = typeof extraRowClass === "string" ? extraRowClass.trim() : "";
4534
+ }) || "").trim() : "";
4338
4535
  var stripeBg = stripedRows && !selectedRows.has(key) ? stripedColors[globalIndex % stripedColors.length] : undefined;
4536
+ return {
4537
+ globalIndex: globalIndex,
4538
+ key: key,
4539
+ actionCellKey: actionCellKey,
4540
+ extraRowClass: extraRowClass,
4541
+ stripeBg: stripeBg
4542
+ };
4543
+ }, [startIndex, rowClass, stripedRows, selectedRows, stripedColors]);
4544
+
4545
+ // Render mobile card
4546
+ var renderMobileCard = useCallback(function (row, rowIndexInPage) {
4547
+ var _getRowMetadata = getRowMetadata(row, rowIndexInPage),
4548
+ globalIndex = _getRowMetadata.globalIndex,
4549
+ key = _getRowMetadata.key,
4550
+ actionCellKey = _getRowMetadata.actionCellKey,
4551
+ extraRowClass = _getRowMetadata.extraRowClass,
4552
+ stripeBg = _getRowMetadata.stripeBg;
4339
4553
  return /*#__PURE__*/React__default.createElement("div", {
4340
4554
  key: key,
4341
4555
  style: stripeBg ? {
@@ -4345,7 +4559,7 @@ var Table = function Table(_ref) {
4345
4559
  "cursor-pointer": !!onRowClick,
4346
4560
  "bg-primary-50 border-primary-200": selectedRows.has(key),
4347
4561
  "hover:shadow-md": !selectedRows.has(key)
4348
- }, safeExtraRowClass),
4562
+ }, extraRowClass),
4349
4563
  onClick: function onClick() {
4350
4564
  return onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row, globalIndex);
4351
4565
  }
@@ -4402,16 +4616,15 @@ var Table = function Table(_ref) {
4402
4616
  }), document.body))), /*#__PURE__*/React__default.createElement("div", {
4403
4617
  className: "p-4"
4404
4618
  }, visibleColumns.map(function (column, colIndex) {
4405
- var extraCellClass = typeof cellClass === "function" ? cellClass({
4619
+ var extraCellClass = typeof cellClass === "function" ? (cellClass({
4406
4620
  row: row,
4407
4621
  rowIndex: globalIndex,
4408
4622
  column: column,
4409
4623
  columnIndex: colIndex
4410
- }) : "";
4411
- var safeExtraCellClass = typeof extraCellClass === "string" ? extraCellClass.trim() : "";
4624
+ }) || "").trim() : "";
4412
4625
  return /*#__PURE__*/React__default.createElement("div", {
4413
4626
  key: column.key,
4414
- className: cn$1("flex justify-between items-center flex-row py-2 border-b last:border-b-0", safeExtraCellClass)
4627
+ className: cn$1("flex justify-between items-center flex-row py-2 border-b last:border-b-0", extraCellClass)
4415
4628
  }, /*#__PURE__*/React__default.createElement("div", {
4416
4629
  className: "text-xs font-medium text-gray-500 uppercase tracking-wide mb-1 sm:mb-0 sm:w-1/3 sm:pr-2"
4417
4630
  }, column.label), /*#__PURE__*/React__default.createElement("div", {
@@ -4423,10 +4636,10 @@ var Table = function Table(_ref) {
4423
4636
  row: row,
4424
4637
  index: globalIndex
4425
4638
  })));
4426
- };
4639
+ }, [getRowMetadata, hasDetails, selectable, showSerial, withAction, expandedRows, selectedRows, onRowClick, visibleColumns, renderCell, cellClass, DetailsComponent, actionsToUse, actionAnchor, openActionKey, handleOnAction, actionMenuRef, toggleActions, toggleExpandRow, handleSelectRow]);
4427
4640
 
4428
4641
  // Render mobile filters
4429
- var renderMobileFilters = function renderMobileFilters() {
4642
+ var renderMobileFilters = useCallback(function () {
4430
4643
  return /*#__PURE__*/React__default.createElement("div", {
4431
4644
  className: "mb-4 p-3 bg-gray-50 rounded-lg"
4432
4645
  }, /*#__PURE__*/React__default.createElement("div", {
@@ -4468,7 +4681,7 @@ var Table = function Table(_ref) {
4468
4681
  }
4469
4682
  }));
4470
4683
  })));
4471
- };
4684
+ }, [showMobileFilters, globalSearch, searchInput, onGlobalKeyDown, filterable, visibleColumns, filters, handleFilter, setShowMobileFilters, setSearchInput]);
4472
4685
 
4473
4686
  // Render
4474
4687
  return /*#__PURE__*/React__default.createElement("div", {
@@ -4491,23 +4704,11 @@ var Table = function Table(_ref) {
4491
4704
  htmlFor: "pagination-limit",
4492
4705
  className: "text-sm text-gray-600 whitespace-nowrap"
4493
4706
  }, "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
- }),
4707
+ options: limitOptionsMemo,
4505
4708
  value: limit,
4506
4709
  allowClear: false,
4507
4710
  placeholder: "",
4508
- onChange: function onChange(e) {
4509
- return handleLimitChange(e);
4510
- },
4711
+ onChange: handleLimitChange,
4511
4712
  className: "w-20 md:w-30"
4512
4713
  }), /*#__PURE__*/React__default.createElement("p", {
4513
4714
  className: "text-sm text-gray-600 whitespace-nowrap"
@@ -4523,9 +4724,7 @@ var Table = function Table(_ref) {
4523
4724
  className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4524
4725
  disabled: currentPage === 1,
4525
4726
  onClick: function onClick() {
4526
- return setCurrentPage(function (p) {
4527
- return Math.max(1, p - 1);
4528
- });
4727
+ return handlePageChange(Math.max(1, currentPage - 1));
4529
4728
  },
4530
4729
  "aria-label": "Previous page"
4531
4730
  }, /*#__PURE__*/React__default.createElement(ChevronLeft, {
@@ -4534,9 +4733,7 @@ var Table = function Table(_ref) {
4534
4733
  className: "rounded-lg md:rounded-md border border-gray-300 p-2 disabled:opacity-50 hover:bg-gray-50",
4535
4734
  disabled: currentPage === totalPages,
4536
4735
  onClick: function onClick() {
4537
- return setCurrentPage(function (p) {
4538
- return Math.min(totalPages, p + 1);
4539
- });
4736
+ return handlePageChange(Math.min(totalPages, currentPage + 1));
4540
4737
  },
4541
4738
  "aria-label": "Next page"
4542
4739
  }, /*#__PURE__*/React__default.createElement(ChevronRight, {
@@ -4679,33 +4876,16 @@ var Table = function Table(_ref) {
4679
4876
  }, loading ? /*#__PURE__*/React__default.createElement("tr", null, /*#__PURE__*/React__default.createElement("td", {
4680
4877
  colSpan: visibleCount,
4681
4878
  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", {
4879
+ }, /*#__PURE__*/React__default.createElement(LoadingState, null))) : paginatedData.length === 0 ? /*#__PURE__*/React__default.createElement("tr", null, /*#__PURE__*/React__default.createElement("td", {
4689
4880
  colSpan: visibleCount,
4690
4881
  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;
4882
+ }, /*#__PURE__*/React__default.createElement(EmptyState, null))) : paginatedData.map(function (row, rowIndexInPage) {
4883
+ var _getRowMetadata2 = getRowMetadata(row, rowIndexInPage),
4884
+ globalIndex = _getRowMetadata2.globalIndex,
4885
+ key = _getRowMetadata2.key,
4886
+ actionCellKey = _getRowMetadata2.actionCellKey,
4887
+ extraRowClass = _getRowMetadata2.extraRowClass,
4888
+ stripeBg = _getRowMetadata2.stripeBg;
4709
4889
  return /*#__PURE__*/React__default.createElement(React__default.Fragment, {
4710
4890
  key: key
4711
4891
  }, /*#__PURE__*/React__default.createElement("tr", {
@@ -4715,7 +4895,7 @@ var Table = function Table(_ref) {
4715
4895
  className: cn$1("hover:bg-gray-50", {
4716
4896
  "cursor-pointer": !!onRowClick,
4717
4897
  "bg-primary-50": selectedRows.has(key)
4718
- }, safeExtraRowClass),
4898
+ }, extraRowClass),
4719
4899
  onClick: function onClick() {
4720
4900
  return onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row, globalIndex);
4721
4901
  }
@@ -4743,16 +4923,15 @@ var Table = function Table(_ref) {
4743
4923
  return handleSelectRow(row, rowIndexInPage);
4744
4924
  }
4745
4925
  })), visibleColumns.map(function (column, columnIndex) {
4746
- var extraCellClass = typeof cellClass === "function" ? cellClass({
4926
+ var extraCellClass = typeof cellClass === "function" ? (cellClass({
4747
4927
  row: row,
4748
4928
  rowIndex: globalIndex,
4749
4929
  column: column,
4750
4930
  columnIndex: columnIndex
4751
- }) : "";
4752
- var safeExtraCellClass = typeof extraCellClass === "string" ? extraCellClass.trim() : "";
4931
+ }) || "").trim() : "";
4753
4932
  return /*#__PURE__*/React__default.createElement("td", {
4754
4933
  key: column.key,
4755
- className: cn$1("px-6 py-4 whitespace-nowrap text-sm text-gray-900", safeExtraCellClass)
4934
+ className: cn$1("px-6 py-4 whitespace-nowrap text-sm text-gray-900", extraCellClass)
4756
4935
  }, renderCell(column, row, globalIndex));
4757
4936
  }), withAction && /*#__PURE__*/React__default.createElement("td", {
4758
4937
  className: "px-4 py-4 text-sm text-right",
@@ -4795,23 +4974,9 @@ var Table = function Table(_ref) {
4795
4974
  className: "p-4"
4796
4975
  }, loading ? /*#__PURE__*/React__default.createElement("div", {
4797
4976
  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", {
4977
+ }, /*#__PURE__*/React__default.createElement(LoadingState, null)) : paginatedData.length === 0 ? /*#__PURE__*/React__default.createElement("div", {
4805
4978
  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) {
4979
+ }, /*#__PURE__*/React__default.createElement(EmptyState, null)) : paginatedData.map(function (row, rowIndexInPage) {
4815
4980
  return renderMobileCard(row, rowIndexInPage);
4816
4981
  })), pagination && /*#__PURE__*/React__default.createElement("div", {
4817
4982
  className: "p-4 border-t"
@@ -4825,25 +4990,11 @@ var Table = function Table(_ref) {
4825
4990
  className: "rounded-lg md:rounded-md border border-gray-300 px-4 py-2 text-sm disabled:opacity-50 hover:bg-gray-50",
4826
4991
  disabled: currentPage === 1,
4827
4992
  onClick: function onClick() {
4828
- return setCurrentPage(function (p) {
4829
- return Math.max(1, p - 1);
4830
- });
4993
+ return handlePageChange(Math.max(1, currentPage - 1));
4831
4994
  }
4832
4995
  }, "Previous"), /*#__PURE__*/React__default.createElement("div", {
4833
4996
  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
- }
4997
+ }, getPaginationNumbers(currentPage, totalPages).map(function (pageNum) {
4847
4998
  return /*#__PURE__*/React__default.createElement("button", {
4848
4999
  key: pageNum,
4849
5000
  className: cn$1("h-8 w-8 rounded-lg md:rounded-md text-sm", {
@@ -4851,19 +5002,23 @@ var Table = function Table(_ref) {
4851
5002
  "border border-gray-300 hover:bg-gray-50": pageNum !== currentPage
4852
5003
  }),
4853
5004
  onClick: function onClick() {
4854
- return setCurrentPage(pageNum);
5005
+ return handlePageChange(pageNum);
4855
5006
  }
4856
5007
  }, pageNum);
4857
5008
  })), /*#__PURE__*/React__default.createElement("button", {
4858
5009
  className: "rounded-lg md:rounded-md border border-gray-300 px-4 py-2 text-sm disabled:opacity-50 hover:bg-gray-50",
4859
5010
  disabled: currentPage === totalPages,
4860
5011
  onClick: function onClick() {
4861
- return setCurrentPage(function (p) {
4862
- return Math.min(totalPages, p + 1);
4863
- });
5012
+ return handlePageChange(Math.min(totalPages, currentPage + 1));
4864
5013
  }
4865
5014
  }, "Next")))));
4866
5015
  };
5016
+
5017
+ // ActionMenuPortal constants
5018
+ var MENU_WIDTH = 180;
5019
+ var MAX_MENU_HEIGHT = 320;
5020
+ var MENU_MARGIN = 8;
5021
+ var MIN_MENU_HEIGHT = 80;
4867
5022
  function ActionMenuPortal(_ref2) {
4868
5023
  var anchorElem = _ref2.anchorElem,
4869
5024
  anchorRow = _ref2.anchorRow,
@@ -4877,16 +5032,12 @@ function ActionMenuPortal(_ref2) {
4877
5032
  top: 0,
4878
5033
  transformOrigin: "top right",
4879
5034
  maxHeight: 300,
4880
- width: 180,
5035
+ width: MENU_WIDTH,
4881
5036
  opacity: 0
4882
5037
  }),
4883
5038
  _useState30 = _slicedToArray(_useState29, 2),
4884
5039
  style = _useState30[0],
4885
5040
  setStyle = _useState30[1];
4886
- var menuWidth = 180;
4887
- var maxMenuHeight = 320;
4888
- var margin = 8;
4889
- var minMenuHeight = 80;
4890
5041
 
4891
5042
  // compute position using actual menu height (if available) and layout measurements
4892
5043
  var computePosition = function computePosition() {
@@ -4898,17 +5049,17 @@ function ActionMenuPortal(_ref2) {
4898
5049
  var spaceAbove = rect.top;
4899
5050
 
4900
5051
  // allowed max height based on available space
4901
- var allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, Math.max(spaceBelow - margin, spaceAbove - margin)));
5052
+ var allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, Math.max(spaceBelow - MENU_MARGIN, spaceAbove - MENU_MARGIN)));
4902
5053
 
4903
5054
  // choose placement by comparing actual available spaces
4904
5055
  var placement = "bottom";
4905
- if (spaceBelow < 160 && spaceAbove > spaceBelow) {
5056
+ if (spaceBelow < MENU_PLACEMENT_THRESHOLD && spaceAbove > spaceBelow) {
4906
5057
  placement = "top";
4907
5058
  // when placing on top we should cap allowedMaxHeight by spaceAbove
4908
- allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceAbove - margin));
5059
+ allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, spaceAbove - MENU_MARGIN));
4909
5060
  } else {
4910
5061
  // placing bottom: cap by spaceBelow
4911
- allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceBelow - margin));
5062
+ allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, spaceBelow - MENU_MARGIN));
4912
5063
  }
4913
5064
 
4914
5065
  // measure the menu's content height if we can, and compute the actual menu height we'll use.
@@ -4925,23 +5076,23 @@ function ActionMenuPortal(_ref2) {
4925
5076
  var top;
4926
5077
  if (placement === "top") {
4927
5078
  // place menu so its bottom sits just above the anchor (rect.top - margin)
4928
- top = rect.top + scrollY - measuredMenuHeight - margin;
5079
+ top = rect.top + scrollY - measuredMenuHeight - MENU_MARGIN;
4929
5080
  } else {
4930
5081
  // place menu just below the anchor (rect.bottom + margin)
4931
- top = rect.bottom + scrollY + margin;
5082
+ top = rect.bottom + scrollY + MENU_MARGIN;
4932
5083
  }
4933
5084
 
4934
5085
  // clamp top to viewport (so it never goes off-screen)
4935
- var minTop = margin + scrollY;
4936
- var maxTop = window.innerHeight + scrollY - measuredMenuHeight - margin;
5086
+ var minTop = MENU_MARGIN + scrollY;
5087
+ var maxTop = window.innerHeight + scrollY - measuredMenuHeight - MENU_MARGIN;
4937
5088
  if (top < minTop) top = minTop;
4938
5089
  if (top > maxTop) top = maxTop;
4939
5090
 
4940
5091
  // 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);
5092
+ var left = rect.right + scrollX - MENU_WIDTH;
5093
+ if (left < MENU_MARGIN) left = MENU_MARGIN;
5094
+ if (left + MENU_WIDTH > window.innerWidth - MENU_MARGIN) {
5095
+ left = Math.max(MENU_MARGIN, window.innerWidth - MENU_WIDTH - MENU_MARGIN);
4945
5096
  }
4946
5097
  var transformOrigin = placement === "bottom" ? "top right" : "bottom right";
4947
5098
 
@@ -4951,7 +5102,7 @@ function ActionMenuPortal(_ref2) {
4951
5102
  top: top,
4952
5103
  transformOrigin: transformOrigin,
4953
5104
  maxHeight: allowedMaxHeight,
4954
- width: menuWidth,
5105
+ width: MENU_WIDTH,
4955
5106
  opacity: 1
4956
5107
  });
4957
5108
  };
@@ -4974,7 +5125,8 @@ function ActionMenuPortal(_ref2) {
4974
5125
  window.removeEventListener("scroll", onScrollOrResize, true);
4975
5126
  };
4976
5127
  // Recompute when anchor element changes or actions change (content height may change).
4977
- }, [anchorElem, actions, menuRef]);
5128
+ // Note: menuRef is a ref object and never changes, so it's safe to include
5129
+ }, [anchorElem, actions]);
4978
5130
  return /*#__PURE__*/createPortal(/*#__PURE__*/React__default.createElement("div", {
4979
5131
  ref: menuRef,
4980
5132
  style: {