@dreamtree-org/twreact-ui 1.0.70 → 1.0.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import React__default, { forwardRef, createElement, useId, useRef, useState, useEffect, useImperativeHandle, useMemo, useLayoutEffect, useCallback, createContext, useContext, cloneElement, PureComponent } from 'react';
2
+ import React__default, { forwardRef, createElement, useId, useRef, useState, useEffect, useImperativeHandle, useMemo, useCallback, useLayoutEffect, createContext, useContext, cloneElement, PureComponent } from 'react';
3
3
  import ReactDOM, { createPortal } from 'react-dom';
4
4
 
5
5
  function _extends() {
@@ -3863,9 +3863,101 @@ var Select = /*#__PURE__*/React__default.forwardRef(function (_ref, forwardedRef
3863
3863
  }, error));
3864
3864
  });
3865
3865
 
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"];
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,
@@ -3917,6 +4009,12 @@ var Table = function Table(_ref) {
3917
4009
  stripedColors = _ref$stripedColors === void 0 ? ["#ffffff", "#f7fafc"] : _ref$stripedColors,
3918
4010
  _ref$responsiveBreakp = _ref.responsiveBreakpoint,
3919
4011
  responsiveBreakpoint = _ref$responsiveBreakp === void 0 ? 768 : _ref$responsiveBreakp,
4012
+ _ref$serverSide = _ref.serverSide,
4013
+ serverSide = _ref$serverSide === void 0 ? false : _ref$serverSide,
4014
+ _ref$totalRecords = _ref.totalRecords,
4015
+ totalRecords = _ref$totalRecords === void 0 ? 0 : _ref$totalRecords,
4016
+ pageNumber = _ref.pageNumber,
4017
+ onPageChange = _ref.onPageChange,
3920
4018
  props = _objectWithoutProperties$1(_ref, _excluded$k);
3921
4019
  // State for responsive view
3922
4020
  var _useState = useState(false),
@@ -3925,14 +4023,16 @@ var Table = function Table(_ref) {
3925
4023
  setIsMobileView = _useState2[1];
3926
4024
 
3927
4025
  // Keep original table data / loading
3928
- var _useState3 = useState(Array.isArray(data) ? data : []),
4026
+ var _useState3 = useState(function () {
4027
+ return Array.isArray(data) ? data : [];
4028
+ }),
3929
4029
  _useState4 = _slicedToArray(_useState3, 2),
3930
4030
  tableData = _useState4[0],
3931
4031
  setTableData = _useState4[1];
3932
4032
  var _useState5 = useState(false),
3933
4033
  _useState6 = _slicedToArray(_useState5, 2),
3934
4034
  loading = _useState6[0],
3935
- setLoading = _useState6[1];
4035
+ _setLoading = _useState6[1];
3936
4036
 
3937
4037
  // Column visibility state
3938
4038
  var _useState7 = useState(function () {
@@ -3944,10 +4044,26 @@ var Table = function Table(_ref) {
3944
4044
  columnsState = _useState8[0],
3945
4045
  setColumnsState = _useState8[1];
3946
4046
  useEffect(function () {
3947
- // Sync when prop changes
3948
- setColumnsState(Array.isArray(columns) ? columns.map(function (c) {
3949
- return _objectSpread$9({}, c);
3950
- }) : []);
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
+ }
3951
4067
  }, [columns]);
3952
4068
 
3953
4069
  // Popover state for column toggler
@@ -3978,7 +4094,7 @@ var Table = function Table(_ref) {
3978
4094
  _useState16 = _slicedToArray(_useState15, 2),
3979
4095
  expandedRows = _useState16[0],
3980
4096
  setExpandedRows = _useState16[1];
3981
- var _useState17 = useState(1),
4097
+ var _useState17 = useState(typeof pageNumber === "number" ? pageNumber : 1),
3982
4098
  _useState18 = _slicedToArray(_useState17, 2),
3983
4099
  currentPage = _useState18[0],
3984
4100
  setCurrentPage = _useState18[1];
@@ -4007,34 +4123,43 @@ var Table = function Table(_ref) {
4007
4123
  _useState28 = _slicedToArray(_useState27, 2),
4008
4124
  openActionKey = _useState28[0],
4009
4125
  setOpenActionKey = _useState28[1];
4010
- var openRef = useRef(null);
4126
+ useEffect(function () {
4127
+ if (typeof pageNumber === "number" && pageNumber !== currentPage) {
4128
+ setCurrentPage(pageNumber);
4129
+ onPageChange === null || onPageChange === void 0 || onPageChange(pageNumber);
4130
+ }
4131
+ }, [pageNumber, currentPage, onPageChange]);
4132
+ useEffect(function () {
4133
+ setLimit(pageSize);
4134
+ }, [pageSize]);
4011
4135
 
4012
- // Check if mobile view
4136
+ // Check if mobile view with debouncing
4013
4137
  useEffect(function () {
4138
+ var timeoutId;
4014
4139
  var checkMobile = function checkMobile() {
4015
4140
  setIsMobileView(window.innerWidth < responsiveBreakpoint);
4016
4141
  };
4142
+ var debouncedCheck = function debouncedCheck() {
4143
+ clearTimeout(timeoutId);
4144
+ timeoutId = setTimeout(checkMobile, RESIZE_DEBOUNCE_MS);
4145
+ };
4017
4146
 
4018
4147
  // Initial check
4019
4148
  checkMobile();
4020
4149
 
4021
- // Add event listener
4022
- window.addEventListener("resize", checkMobile);
4150
+ // Add event listener with debouncing
4151
+ window.addEventListener("resize", debouncedCheck);
4023
4152
  return function () {
4024
- window.removeEventListener("resize", checkMobile);
4153
+ clearTimeout(timeoutId);
4154
+ window.removeEventListener("resize", debouncedCheck);
4025
4155
  };
4026
4156
  }, [responsiveBreakpoint]);
4027
- var isValidList = function isValidList(d) {
4028
- return Array.isArray(d);
4029
- };
4030
- var setDataForExternal = function setDataForExternal(newData) {
4031
- if (isValidList(newData)) setTableData(newData);else setTableData([]);
4032
- };
4033
4157
 
4034
4158
  // Close column menu when clicking outside
4035
4159
  useEffect(function () {
4160
+ if (!showColumnMenu) return;
4036
4161
  var onDocClick = function onDocClick(e) {
4037
- if (showColumnMenu && columnMenuRef.current && !columnMenuRef.current.contains(e.target) && columnToggleBtnRef.current && !columnToggleBtnRef.current.contains(e.target)) {
4162
+ if (columnMenuRef.current && !columnMenuRef.current.contains(e.target) && columnToggleBtnRef.current && !columnToggleBtnRef.current.contains(e.target)) {
4038
4163
  setShowColumnMenu(false);
4039
4164
  }
4040
4165
  };
@@ -4044,26 +4169,32 @@ var Table = function Table(_ref) {
4044
4169
  };
4045
4170
  }, [showColumnMenu]);
4046
4171
 
4047
- // Close per-row actionmenu when clicking outside
4048
- useEffect(function () {
4049
- var handler = function handler(e) {
4050
- if (openActionKey && openRef.current && !openRef.current.contains(e.target)) {
4051
- setOpenActionKey(null);
4052
- }
4053
- };
4054
- document.addEventListener("click", handler);
4055
- return function () {
4056
- return document.removeEventListener("click", handler);
4057
- };
4058
- }, [openActionKey]);
4059
-
4060
4172
  // Visible columns derived from columnsState
4061
4173
  var visibleColumns = useMemo(function () {
4062
4174
  return columnsState.filter(function (col) {
4063
4175
  return col.isVisible !== false;
4064
4176
  });
4065
4177
  }, [columnsState]);
4066
- var toggleColumnVisibility = function toggleColumnVisibility(key) {
4178
+
4179
+ // Memoize actions
4180
+ var actionsToUse = useMemo(function () {
4181
+ return actions !== null && actions !== void 0 ? actions : DEFAULT_ACTIONS;
4182
+ }, [actions]);
4183
+
4184
+ // Memoize visible count
4185
+ var visibleCount = useMemo(function () {
4186
+ return (showSerial ? 1 : 0) + visibleColumns.length + (selectable ? 1 : 0) + (hasDetails ? 1 : 0) + (withAction ? 1 : 0);
4187
+ }, [showSerial, visibleColumns.length, selectable, hasDetails, withAction]);
4188
+
4189
+ // Memoize key-to-row mapping for selection
4190
+ var keyToRowMap = useMemo(function () {
4191
+ var map = new Map();
4192
+ filteredData.forEach(function (row, i) {
4193
+ map.set(getRowKey(row, i), row);
4194
+ });
4195
+ return map;
4196
+ }, [filteredData]);
4197
+ var toggleColumnVisibility = useCallback(function (key) {
4067
4198
  setColumnsState(function (prev) {
4068
4199
  return prev.map(function (c) {
4069
4200
  return c.key === key ? _objectSpread$9(_objectSpread$9({}, c), {}, {
@@ -4072,127 +4203,73 @@ var Table = function Table(_ref) {
4072
4203
  });
4073
4204
  });
4074
4205
  setCurrentPage(1);
4075
- };
4206
+ }, []);
4076
4207
 
4077
4208
  // Handle actions
4078
- var handleOnAction = function handleOnAction(action, row) {
4209
+ var handleOnAction = useCallback(function (action, row) {
4079
4210
  var _action$onClick;
4080
4211
  (_action$onClick = action.onClick) === null || _action$onClick === void 0 || _action$onClick.call(action, {
4081
4212
  action: action,
4082
4213
  row: row
4083
4214
  });
4084
- onAction && onAction({
4215
+ onAction === null || onAction === void 0 || onAction({
4085
4216
  action: action,
4086
4217
  row: row
4087
4218
  });
4088
4219
  setOpenActionKey(null);
4089
4220
  setActionAnchor(null);
4090
- };
4091
- var defaultActions = [{
4092
- name: "edit",
4093
- label: "Edit",
4094
- onClick: function onClick() {
4095
- console.log("Edit action clicked");
4096
- },
4097
- icon: /*#__PURE__*/React__default.createElement(PenSquare, {
4098
- size: 16
4099
- })
4100
- }, {
4101
- name: "delete",
4102
- label: "Delete",
4103
- onClick: function onClick() {
4104
- console.log("Delete action clicked");
4105
- },
4106
- icon: /*#__PURE__*/React__default.createElement(Trash, {
4107
- size: 16
4108
- })
4109
- }, {
4110
- name: "view",
4111
- label: "View",
4112
- onClick: function onClick() {
4113
- console.log("View action clicked");
4114
- },
4115
- icon: /*#__PURE__*/React__default.createElement(Eye, {
4116
- size: 16
4117
- })
4118
- }];
4119
- var actionsToUse = actions !== undefined ? actions : defaultActions;
4120
- var getRowKey = function getRowKey(row, globalIndex) {
4121
- if (row == null) return String(globalIndex);
4122
- if (row.id !== undefined && row.id !== null) return String(row.id);
4123
- if (row._id !== undefined && row._id !== null) return String(row._id);
4124
- return String(globalIndex);
4125
- };
4221
+ }, [onAction]);
4126
4222
 
4127
4223
  // Sorting
4128
4224
  var sortedData = useMemo(function () {
4129
- if (!sortConfig.key) return tableData;
4225
+ if (serverSide || !sortConfig.key) return tableData;
4130
4226
  var key = sortConfig.key,
4131
4227
  direction = sortConfig.direction;
4132
4228
  return _toConsumableArray$1(tableData).sort(function (a, b) {
4133
- var aValue = a === null || a === void 0 ? void 0 : a[key];
4134
- var bValue = b === null || b === void 0 ? void 0 : b[key];
4135
- if (aValue == null && bValue == null) return 0;
4136
- if (aValue == null) return direction === "asc" ? -1 : 1;
4137
- if (bValue == null) return direction === "asc" ? 1 : -1;
4138
- if (typeof aValue === "number" && typeof bValue === "number") {
4139
- return direction === "asc" ? aValue - bValue : bValue - aValue;
4140
- }
4141
- var aStr = String(aValue).toLowerCase();
4142
- var bStr = String(bValue).toLowerCase();
4143
- if (aStr < bStr) return direction === "asc" ? -1 : 1;
4144
- if (aStr > bStr) return direction === "asc" ? 1 : -1;
4145
- return 0;
4229
+ var av = a === null || a === void 0 ? void 0 : a[key];
4230
+ var bv = b === null || b === void 0 ? void 0 : b[key];
4231
+ if (av == null || bv == null) return 0;
4232
+ if (typeof av === "number") return direction === "asc" ? av - bv : bv - av;
4233
+ return direction === "asc" ? String(av).localeCompare(String(bv)) : String(bv).localeCompare(String(av));
4146
4234
  });
4147
- }, [tableData, sortConfig]);
4235
+ }, [tableData, sortConfig, serverSide]);
4148
4236
 
4149
4237
  // Filtering
4150
4238
  var filteredData = useMemo(function () {
4151
- if (!filterable || Object.keys(filters).length === 0) return sortedData;
4152
- var globalQuery = String(filters.global || "").trim().toLowerCase();
4239
+ if (serverSide || !filterable || !Object.keys(filters).length) return sortedData;
4240
+ var q = (filters.global || "").toLowerCase();
4153
4241
  return sortedData.filter(function (row) {
4154
- // Per-column filters
4155
- var perColumnOk = Object.entries(filters).every(function (_ref2) {
4156
- var _ref3 = _slicedToArray(_ref2, 2),
4157
- key = _ref3[0],
4158
- value = _ref3[1];
4159
- if (key === "global") return true;
4160
- if (!value) return true;
4161
- var cell = row === null || row === void 0 ? void 0 : row[key];
4162
- if (cell == null) return false;
4163
- return String(cell).toLowerCase().includes(String(value).toLowerCase());
4242
+ return Object.values(row || {}).some(function (v) {
4243
+ return String(v).toLowerCase().includes(q);
4164
4244
  });
4165
- if (!perColumnOk) return false;
4166
-
4167
- // Global filter
4168
- if (globalQuery) {
4169
- var matchesVisible = visibleColumns.some(function (col) {
4170
- var v = row === null || row === void 0 ? void 0 : row[col.key];
4171
- if (v == null) return false;
4172
- return String(v).toLowerCase().includes(globalQuery);
4173
- });
4174
- if (matchesVisible) return true;
4175
- return Object.values(row || {}).some(function (v) {
4176
- return v != null ? String(v).toLowerCase().includes(globalQuery) : false;
4177
- });
4178
- }
4179
- return true;
4180
4245
  });
4181
- }, [sortedData, filters, filterable, visibleColumns]);
4246
+ }, [sortedData, filters, filterable, serverSide]);
4182
4247
 
4183
- // Pagination indices
4184
- var startIndex = pagination ? (currentPage - 1) * limit : 0;
4185
- var endIndex = pagination ? startIndex + limit : filteredData.length;
4248
+ // Pagination indices - memoized
4249
+ var _useMemo = useMemo(function () {
4250
+ var start = pagination ? (currentPage - 1) * limit : 0;
4251
+ var end = pagination ? start + limit : filteredData.length;
4252
+ return {
4253
+ startIndex: start,
4254
+ endIndex: end
4255
+ };
4256
+ }, [pagination, currentPage, limit, filteredData.length]),
4257
+ startIndex = _useMemo.startIndex,
4258
+ endIndex = _useMemo.endIndex;
4186
4259
 
4187
4260
  // Paginated view
4188
4261
  var paginatedData = useMemo(function () {
4189
- if (!pagination) return filteredData;
4190
- return filteredData.slice(startIndex, endIndex);
4191
- }, [filteredData, startIndex, endIndex, pagination]);
4192
- var totalPages = Math.max(1, Math.ceil(filteredData.length / limit));
4262
+ if (!pagination || serverSide) return filteredData;
4263
+ var start = (currentPage - 1) * limit;
4264
+ return filteredData.slice(start, start + limit);
4265
+ }, [filteredData, pagination, serverSide, currentPage, limit]);
4266
+ var totalPages = useMemo(function () {
4267
+ if (!pagination) return 1;
4268
+ return serverSide ? Math.max(1, Math.ceil((totalRecords || 0) / limit)) : Math.max(1, Math.ceil(filteredData.length / limit));
4269
+ }, [pagination, serverSide, totalRecords, filteredData.length, limit]);
4193
4270
 
4194
4271
  // Sorting handler
4195
- var handleSort = function handleSort(key) {
4272
+ var handleSort = useCallback(function (key) {
4196
4273
  if (!sortable) return;
4197
4274
  var direction = sortConfig.key === key && sortConfig.direction === "asc" ? "desc" : "asc";
4198
4275
  setSortConfig({
@@ -4200,10 +4277,10 @@ var Table = function Table(_ref) {
4200
4277
  direction: direction
4201
4278
  });
4202
4279
  onSort === null || onSort === void 0 || onSort(key, direction);
4203
- };
4280
+ }, [sortable, sortConfig, onSort]);
4204
4281
 
4205
- // Selection handlers
4206
- var handleSelectAll = function handleSelectAll() {
4282
+ // Selection handlers - optimized with memoized keyToRowMap
4283
+ var handleSelectAll = useCallback(function () {
4207
4284
  var newSelection = new Set(selectedRows);
4208
4285
  var pageRowKeys = paginatedData.map(function (r, i) {
4209
4286
  return getRowKey(r, startIndex + i);
@@ -4221,50 +4298,45 @@ var Table = function Table(_ref) {
4221
4298
  });
4222
4299
  }
4223
4300
  setSelectedRows(newSelection);
4224
- var keyToRow = new Map(filteredData.map(function (r, i) {
4225
- return [getRowKey(r, i), r];
4226
- }));
4227
4301
  var selectedData = Array.from(newSelection).map(function (k) {
4228
- return keyToRow.get(k);
4302
+ return keyToRowMap.get(k);
4229
4303
  }).filter(Boolean);
4230
4304
  onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange(selectedData);
4231
- };
4232
- var handleSelectRow = function handleSelectRow(row, rowIndexInPage) {
4305
+ }, [selectedRows, paginatedData, startIndex, keyToRowMap, onSelectionChange]);
4306
+ var handleSelectRow = useCallback(function (row, rowIndexInPage) {
4233
4307
  var globalIndex = startIndex + rowIndexInPage;
4234
4308
  var key = getRowKey(row, globalIndex);
4235
4309
  var newSelection = new Set(selectedRows);
4236
4310
  if (newSelection.has(key)) newSelection["delete"](key);else newSelection.add(key);
4237
4311
  setSelectedRows(newSelection);
4238
- var keyToRow = new Map(filteredData.map(function (r, i) {
4239
- return [getRowKey(r, i), r];
4240
- }));
4241
4312
  var selectedData = Array.from(newSelection).map(function (k) {
4242
- return keyToRow.get(k);
4313
+ return keyToRowMap.get(k);
4243
4314
  }).filter(Boolean);
4244
4315
  onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange(selectedData);
4245
- };
4246
- var toggleExpandRow = function toggleExpandRow(row, rowIndexInPage) {
4316
+ }, [startIndex, selectedRows, keyToRowMap, onSelectionChange]);
4317
+ var toggleExpandRow = useCallback(function (row, rowIndexInPage) {
4247
4318
  var globalIndex = startIndex + rowIndexInPage;
4248
4319
  var key = getRowKey(row, globalIndex);
4249
- var newExpanded = new Set(expandedRows);
4250
- if (newExpanded.has(key)) newExpanded["delete"](key);else newExpanded.add(key);
4251
- setExpandedRows(newExpanded);
4252
- };
4253
- var handleFilter = function handleFilter(key, value) {
4254
- var newFilters = _objectSpread$9(_objectSpread$9({}, filters), {}, _defineProperty$4({}, key, value));
4255
- setFilters(newFilters);
4256
- onFilter === null || onFilter === void 0 || onFilter(newFilters);
4257
- onFilterChange === null || onFilterChange === void 0 || onFilterChange(newFilters);
4320
+ setExpandedRows(function (prev) {
4321
+ var newExpanded = new Set(prev);
4322
+ if (newExpanded.has(key)) newExpanded["delete"](key);else newExpanded.add(key);
4323
+ return newExpanded;
4324
+ });
4325
+ }, [startIndex]);
4326
+ var handleFilter = useCallback(function (key, value) {
4327
+ setFilters(function (prev) {
4328
+ var newFilters = _objectSpread$9(_objectSpread$9({}, prev), {}, _defineProperty$4({}, key, value));
4329
+ onFilter === null || onFilter === void 0 || onFilter(newFilters);
4330
+ onFilterChange === null || onFilterChange === void 0 || onFilterChange(newFilters);
4331
+ return newFilters;
4332
+ });
4258
4333
  setCurrentPage(1);
4259
- };
4260
- var renderCell = function renderCell(column, row, globalIndex) {
4334
+ }, [onFilter, onFilterChange]);
4335
+ var renderCell = useCallback(function (column, row, globalIndex) {
4261
4336
  if (column.render) return column.render(row, globalIndex);
4262
4337
  return row === null || row === void 0 ? void 0 : row[column.key];
4263
- };
4264
-
4265
- // UI counts
4266
- var visibleCount = (showSerial ? 1 : 0) + visibleColumns.length + (selectable ? 1 : 0) + (hasDetails ? 1 : 0) + (withAction ? 1 : 0);
4267
- var toggleActions = function toggleActions(e, actionCellKey, row) {
4338
+ }, []);
4339
+ var toggleActions = useCallback(function (e, actionCellKey, row) {
4268
4340
  e.stopPropagation();
4269
4341
  if (actionAnchor && actionAnchor.key === actionCellKey) {
4270
4342
  setOpenActionKey(null);
@@ -4277,7 +4349,7 @@ var Table = function Table(_ref) {
4277
4349
  row: row
4278
4350
  });
4279
4351
  }
4280
- };
4352
+ }, [actionAnchor]);
4281
4353
  useEffect(function () {
4282
4354
  if (!actionAnchor) return;
4283
4355
  var onDocClick = function onDocClick(ev) {
@@ -4301,58 +4373,139 @@ var Table = function Table(_ref) {
4301
4373
  window.removeEventListener("resize", onScrollResize);
4302
4374
  };
4303
4375
  }, [actionAnchor]);
4376
+
4377
+ // Track previous fetch params to prevent unnecessary calls
4378
+ var prevFetchParamsRef = useRef(null);
4379
+ var isFetchingRef = useRef(false);
4380
+ var isMountedRef = useRef(true);
4381
+ var onFetchRef = useRef(onFetch);
4382
+
4383
+ // Keep ref in sync with onFetch
4304
4384
  useEffect(function () {
4305
- if (!onFetch) return;
4306
- onFetch({
4307
- setData: setDataForExternal,
4385
+ onFetchRef.current = onFetch;
4386
+ }, [onFetch]);
4387
+
4388
+ // Track mount status
4389
+ useEffect(function () {
4390
+ isMountedRef.current = true;
4391
+ return function () {
4392
+ isMountedRef.current = false;
4393
+ isFetchingRef.current = false;
4394
+ };
4395
+ }, []);
4396
+
4397
+ // Serialize fetch params for comparison
4398
+ var fetchParams = useMemo(function () {
4399
+ return {
4308
4400
  filters: filters,
4309
- setLoading: setLoading,
4310
- pagination: {
4311
- currentPage: currentPage,
4312
- limit: limit,
4313
- totalPages: totalPages,
4314
- nextRecords: paginatedData
4315
- }
4316
- });
4317
- }, [filters, currentPage, limit]);
4401
+ page: currentPage,
4402
+ limit: limit,
4403
+ sortKey: sortConfig.key,
4404
+ sortDirection: sortConfig.direction
4405
+ };
4406
+ }, [filters, currentPage, limit, sortConfig.key, sortConfig.direction]);
4407
+ var fetchParamsString = useMemo(function () {
4408
+ return JSON.stringify(fetchParams);
4409
+ }, [fetchParams]);
4410
+
4411
+ // Consolidated data sync effect
4318
4412
  useEffect(function () {
4319
- if (!onFetch && isValidList(data)) setTableData(data);
4320
- }, [data, onFetch]);
4413
+ var currentOnFetch = onFetchRef.current;
4414
+ if (currentOnFetch) {
4415
+ // Only fetch if params actually changed and not already fetching
4416
+ if (!isFetchingRef.current && prevFetchParamsRef.current !== fetchParamsString) {
4417
+ prevFetchParamsRef.current = fetchParamsString;
4418
+ isFetchingRef.current = true;
4419
+ currentOnFetch({
4420
+ setData: function setData(rows) {
4421
+ // Only update if component is still mounted
4422
+ if (isMountedRef.current && isFetchingRef.current) {
4423
+ setTableData(Array.isArray(rows) ? rows : []);
4424
+ isFetchingRef.current = false;
4425
+ }
4426
+ },
4427
+ setLoading: function setLoading(loading) {
4428
+ // Only update if component is still mounted
4429
+ if (isMountedRef.current) {
4430
+ _setLoading(loading);
4431
+ if (!loading && isFetchingRef.current) {
4432
+ isFetchingRef.current = false;
4433
+ }
4434
+ }
4435
+ },
4436
+ filters: fetchParams.filters,
4437
+ page: fetchParams.page,
4438
+ limit: fetchParams.limit,
4439
+ sort: {
4440
+ key: fetchParams.sortKey,
4441
+ direction: fetchParams.sortDirection
4442
+ }
4443
+ });
4444
+ }
4445
+ } else if (!serverSide && Array.isArray(data)) {
4446
+ setTableData(data);
4447
+ }
4448
+ }, [serverSide, data, fetchParamsString, fetchParams]);
4321
4449
 
4322
4450
  // Global search
4323
4451
  useEffect(function () {
4324
4452
  setSearchInput(filters.global || "");
4325
4453
  }, [filters.global]);
4326
- var applyGlobalSearch = function applyGlobalSearch() {
4454
+ var applyGlobalSearch = useCallback(function () {
4327
4455
  handleFilter("global", searchInput);
4328
- };
4329
- var onGlobalKeyDown = function onGlobalKeyDown(e) {
4456
+ }, [handleFilter, searchInput]);
4457
+ var onGlobalKeyDown = useCallback(function (e) {
4330
4458
  if (e.key === "Enter") {
4331
4459
  e.preventDefault();
4332
4460
  applyGlobalSearch();
4333
4461
  }
4334
- };
4335
- useEffect(function () {
4336
- setLimit(pageSize);
4337
- }, [pageSize]);
4338
- var handleLimitChange = function handleLimitChange(nextLimit) {
4462
+ }, [applyGlobalSearch]);
4463
+ var handleLimitChange = useCallback(function (nextLimit) {
4339
4464
  var parsed = Number(nextLimit);
4340
4465
  if (!Number.isFinite(parsed) || parsed <= 0) return;
4341
4466
  setLimit(parsed);
4467
+ setCurrentPage(1);
4342
4468
  onLimitChange === null || onLimitChange === void 0 || onLimitChange(parsed);
4343
- };
4469
+ }, [onLimitChange]);
4344
4470
 
4345
- // Render mobile card
4346
- var renderMobileCard = function renderMobileCard(row, rowIndexInPage) {
4471
+ // Memoize limit options
4472
+ var limitOptionsMemo = useMemo(function () {
4473
+ var opts = Array.isArray(limitOptions) && limitOptions.length > 0 ? limitOptions : [25, 50, 100];
4474
+ return opts.map(function (opt) {
4475
+ return {
4476
+ label: String(opt),
4477
+ value: opt
4478
+ };
4479
+ });
4480
+ }, [limitOptions]);
4481
+
4482
+ // Extract row metadata computation
4483
+ var getRowMetadata = useCallback(function (row, rowIndexInPage) {
4347
4484
  var globalIndex = startIndex + rowIndexInPage;
4348
4485
  var key = getRowKey(row, globalIndex);
4349
4486
  var actionCellKey = "actions-".concat(key);
4350
- var extraRowClass = typeof rowClass === "function" ? rowClass({
4487
+ var extraRowClass = typeof rowClass === "function" ? (rowClass({
4351
4488
  row: row,
4352
4489
  rowIndex: globalIndex
4353
- }) : "";
4354
- var safeExtraRowClass = typeof extraRowClass === "string" ? extraRowClass.trim() : "";
4490
+ }) || "").trim() : "";
4355
4491
  var stripeBg = stripedRows && !selectedRows.has(key) ? stripedColors[globalIndex % stripedColors.length] : undefined;
4492
+ return {
4493
+ globalIndex: globalIndex,
4494
+ key: key,
4495
+ actionCellKey: actionCellKey,
4496
+ extraRowClass: extraRowClass,
4497
+ stripeBg: stripeBg
4498
+ };
4499
+ }, [startIndex, rowClass, stripedRows, selectedRows, stripedColors]);
4500
+
4501
+ // Render mobile card
4502
+ var renderMobileCard = useCallback(function (row, rowIndexInPage) {
4503
+ var _getRowMetadata = getRowMetadata(row, rowIndexInPage),
4504
+ globalIndex = _getRowMetadata.globalIndex,
4505
+ key = _getRowMetadata.key,
4506
+ actionCellKey = _getRowMetadata.actionCellKey;
4507
+ _getRowMetadata.extraRowClass;
4508
+ var stripeBg = _getRowMetadata.stripeBg;
4356
4509
  return /*#__PURE__*/React__default.createElement("div", {
4357
4510
  key: key,
4358
4511
  style: stripeBg ? {
@@ -4419,16 +4572,15 @@ var Table = function Table(_ref) {
4419
4572
  }), document.body))), /*#__PURE__*/React__default.createElement("div", {
4420
4573
  className: "p-4"
4421
4574
  }, visibleColumns.map(function (column, colIndex) {
4422
- var extraCellClass = typeof cellClass === "function" ? cellClass({
4575
+ var extraCellClass = typeof cellClass === "function" ? (cellClass({
4423
4576
  row: row,
4424
4577
  rowIndex: globalIndex,
4425
4578
  column: column,
4426
4579
  columnIndex: colIndex
4427
- }) : "";
4428
- var safeExtraCellClass = typeof extraCellClass === "string" ? extraCellClass.trim() : "";
4580
+ }) || "").trim() : "";
4429
4581
  return /*#__PURE__*/React__default.createElement("div", {
4430
4582
  key: column.key,
4431
- className: cn$1("flex justify-between items-center flex-row py-2 border-b last:border-b-0", safeExtraCellClass)
4583
+ className: cn$1("flex justify-between items-center flex-row py-2 border-b last:border-b-0", extraCellClass)
4432
4584
  }, /*#__PURE__*/React__default.createElement("div", {
4433
4585
  className: "text-xs font-medium text-gray-500 uppercase tracking-wide mb-1 sm:mb-0 sm:w-1/3 sm:pr-2"
4434
4586
  }, column.label), /*#__PURE__*/React__default.createElement("div", {
@@ -4440,7 +4592,7 @@ var Table = function Table(_ref) {
4440
4592
  row: row,
4441
4593
  index: globalIndex
4442
4594
  })));
4443
- };
4595
+ }, [getRowMetadata, hasDetails, selectable, showSerial, withAction, expandedRows, selectedRows, onRowClick, visibleColumns, renderCell, cellClass, DetailsComponent, actionsToUse, actionAnchor, openActionKey, handleOnAction, actionMenuRef]);
4444
4596
 
4445
4597
  // Render mobile filters
4446
4598
  var renderMobileFilters = function renderMobileFilters() {
@@ -4508,23 +4660,11 @@ var Table = function Table(_ref) {
4508
4660
  htmlFor: "pagination-limit",
4509
4661
  className: "text-sm text-gray-600 whitespace-nowrap"
4510
4662
  }, "Show"), /*#__PURE__*/React__default.createElement(Select, {
4511
- options: Array.isArray(limitOptions) && limitOptions.length > 0 ? limitOptions.map(function (opt) {
4512
- return {
4513
- label: String(opt),
4514
- value: opt
4515
- };
4516
- }) : [25, 50, 100].map(function (opt) {
4517
- return {
4518
- label: String(opt),
4519
- value: opt
4520
- };
4521
- }),
4663
+ options: limitOptionsMemo,
4522
4664
  value: limit,
4523
4665
  allowClear: false,
4524
4666
  placeholder: "",
4525
- onChange: function onChange(e) {
4526
- return handleLimitChange(e);
4527
- },
4667
+ onChange: handleLimitChange,
4528
4668
  className: "w-20 md:w-30"
4529
4669
  }), /*#__PURE__*/React__default.createElement("p", {
4530
4670
  className: "text-sm text-gray-600 whitespace-nowrap"
@@ -4681,7 +4821,7 @@ var Table = function Table(_ref) {
4681
4821
  }, /*#__PURE__*/React__default.createElement("input", {
4682
4822
  type: "text",
4683
4823
  placeholder: "Filter ".concat(column.label),
4684
- className: "w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm",
4824
+ className: "w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm font-normal",
4685
4825
  value: filters[column.key] || "",
4686
4826
  onChange: function onChange(e) {
4687
4827
  return handleFilter(column.key, e.target.value);
@@ -4696,33 +4836,16 @@ var Table = function Table(_ref) {
4696
4836
  }, loading ? /*#__PURE__*/React__default.createElement("tr", null, /*#__PURE__*/React__default.createElement("td", {
4697
4837
  colSpan: visibleCount,
4698
4838
  className: "px-6 py-8 text-center"
4699
- }, /*#__PURE__*/React__default.createElement("div", {
4700
- className: "flex flex-col items-center gap-2"
4701
- }, /*#__PURE__*/React__default.createElement("div", {
4702
- className: "h-6 w-6 animate-spin rounded-full border-2 border-primary-600 border-t-transparent"
4703
- }), /*#__PURE__*/React__default.createElement("span", {
4704
- className: "text-sm text-gray-500"
4705
- }, "Loading...")))) : paginatedData.length === 0 ? /*#__PURE__*/React__default.createElement("tr", null, /*#__PURE__*/React__default.createElement("td", {
4839
+ }, /*#__PURE__*/React__default.createElement(LoadingState, null))) : paginatedData.length === 0 ? /*#__PURE__*/React__default.createElement("tr", null, /*#__PURE__*/React__default.createElement("td", {
4706
4840
  colSpan: visibleCount,
4707
4841
  className: "px-6 py-8 text-center"
4708
- }, /*#__PURE__*/React__default.createElement("div", {
4709
- className: "flex flex-col items-center gap-2"
4710
- }, /*#__PURE__*/React__default.createElement("div", {
4711
- className: "text-gray-400"
4712
- }, /*#__PURE__*/React__default.createElement(List, {
4713
- className: "h-8 w-8"
4714
- })), /*#__PURE__*/React__default.createElement("span", {
4715
- className: "text-sm text-gray-500"
4716
- }, "No results found.")))) : paginatedData.map(function (row, rowIndexInPage) {
4717
- var globalIndex = startIndex + rowIndexInPage;
4718
- var key = getRowKey(row, globalIndex);
4719
- var actionCellKey = "actions-".concat(key);
4720
- var extraRowClass = typeof rowClass === "function" ? rowClass({
4721
- row: row,
4722
- rowIndex: globalIndex
4723
- }) : "";
4724
- var safeExtraRowClass = typeof extraRowClass === "string" ? extraRowClass.trim() : "";
4725
- var stripeBg = stripedRows && !selectedRows.has(key) ? stripedColors[globalIndex % stripedColors.length] : undefined;
4842
+ }, /*#__PURE__*/React__default.createElement(EmptyState, null))) : paginatedData.map(function (row, rowIndexInPage) {
4843
+ var _getRowMetadata2 = getRowMetadata(row, rowIndexInPage),
4844
+ globalIndex = _getRowMetadata2.globalIndex,
4845
+ key = _getRowMetadata2.key,
4846
+ actionCellKey = _getRowMetadata2.actionCellKey,
4847
+ extraRowClass = _getRowMetadata2.extraRowClass,
4848
+ stripeBg = _getRowMetadata2.stripeBg;
4726
4849
  return /*#__PURE__*/React__default.createElement(React__default.Fragment, {
4727
4850
  key: key
4728
4851
  }, /*#__PURE__*/React__default.createElement("tr", {
@@ -4732,7 +4855,7 @@ var Table = function Table(_ref) {
4732
4855
  className: cn$1("hover:bg-gray-50", {
4733
4856
  "cursor-pointer": !!onRowClick,
4734
4857
  "bg-primary-50": selectedRows.has(key)
4735
- }, safeExtraRowClass),
4858
+ }, extraRowClass),
4736
4859
  onClick: function onClick() {
4737
4860
  return onRowClick === null || onRowClick === void 0 ? void 0 : onRowClick(row, globalIndex);
4738
4861
  }
@@ -4760,16 +4883,15 @@ var Table = function Table(_ref) {
4760
4883
  return handleSelectRow(row, rowIndexInPage);
4761
4884
  }
4762
4885
  })), visibleColumns.map(function (column, columnIndex) {
4763
- var extraCellClass = typeof cellClass === "function" ? cellClass({
4886
+ var extraCellClass = typeof cellClass === "function" ? (cellClass({
4764
4887
  row: row,
4765
4888
  rowIndex: globalIndex,
4766
4889
  column: column,
4767
4890
  columnIndex: columnIndex
4768
- }) : "";
4769
- var safeExtraCellClass = typeof extraCellClass === "string" ? extraCellClass.trim() : "";
4891
+ }) || "").trim() : "";
4770
4892
  return /*#__PURE__*/React__default.createElement("td", {
4771
4893
  key: column.key,
4772
- className: cn$1("px-6 py-4 whitespace-nowrap text-sm text-gray-900", safeExtraCellClass)
4894
+ className: cn$1("px-6 py-4 whitespace-nowrap text-sm text-gray-900", extraCellClass)
4773
4895
  }, renderCell(column, row, globalIndex));
4774
4896
  }), withAction && /*#__PURE__*/React__default.createElement("td", {
4775
4897
  className: "px-4 py-4 text-sm text-right",
@@ -4812,23 +4934,9 @@ var Table = function Table(_ref) {
4812
4934
  className: "p-4"
4813
4935
  }, loading ? /*#__PURE__*/React__default.createElement("div", {
4814
4936
  className: "py-8 text-center"
4815
- }, /*#__PURE__*/React__default.createElement("div", {
4816
- className: "flex flex-col items-center gap-2"
4817
- }, /*#__PURE__*/React__default.createElement("div", {
4818
- className: "h-6 w-6 animate-spin rounded-full border-2 border-primary-600 border-t-transparent"
4819
- }), /*#__PURE__*/React__default.createElement("span", {
4820
- className: "text-sm text-gray-500"
4821
- }, "Loading..."))) : paginatedData.length === 0 ? /*#__PURE__*/React__default.createElement("div", {
4937
+ }, /*#__PURE__*/React__default.createElement(LoadingState, null)) : paginatedData.length === 0 ? /*#__PURE__*/React__default.createElement("div", {
4822
4938
  className: "py-8 text-center"
4823
- }, /*#__PURE__*/React__default.createElement("div", {
4824
- className: "flex flex-col items-center gap-2"
4825
- }, /*#__PURE__*/React__default.createElement("div", {
4826
- className: "text-gray-400"
4827
- }, /*#__PURE__*/React__default.createElement(List, {
4828
- className: "h-8 w-8"
4829
- })), /*#__PURE__*/React__default.createElement("span", {
4830
- className: "text-sm text-gray-500"
4831
- }, "No results found."))) : paginatedData.map(function (row, rowIndexInPage) {
4939
+ }, /*#__PURE__*/React__default.createElement(EmptyState, null)) : paginatedData.map(function (row, rowIndexInPage) {
4832
4940
  return renderMobileCard(row, rowIndexInPage);
4833
4941
  })), pagination && /*#__PURE__*/React__default.createElement("div", {
4834
4942
  className: "p-4 border-t"
@@ -4848,19 +4956,7 @@ var Table = function Table(_ref) {
4848
4956
  }
4849
4957
  }, "Previous"), /*#__PURE__*/React__default.createElement("div", {
4850
4958
  className: "flex items-center gap-1"
4851
- }, Array.from({
4852
- length: Math.min(5, totalPages)
4853
- }, function (_, i) {
4854
- var pageNum;
4855
- if (totalPages <= 5) {
4856
- pageNum = i + 1;
4857
- } else if (currentPage <= 3) {
4858
- pageNum = i + 1;
4859
- } else if (currentPage >= totalPages - 2) {
4860
- pageNum = totalPages - 4 + i;
4861
- } else {
4862
- pageNum = currentPage - 2 + i;
4863
- }
4959
+ }, getPaginationNumbers(currentPage, totalPages).map(function (pageNum) {
4864
4960
  return /*#__PURE__*/React__default.createElement("button", {
4865
4961
  key: pageNum,
4866
4962
  className: cn$1("h-8 w-8 rounded-lg md:rounded-md text-sm", {
@@ -4881,29 +4977,31 @@ var Table = function Table(_ref) {
4881
4977
  }
4882
4978
  }, "Next")))));
4883
4979
  };
4884
- function ActionMenuPortal(_ref4) {
4885
- var anchorElem = _ref4.anchorElem,
4886
- anchorRow = _ref4.anchorRow,
4887
- _ref4$actions = _ref4.actions,
4888
- actions = _ref4$actions === void 0 ? [] : _ref4$actions;
4889
- _ref4.onClose;
4890
- var onAction = _ref4.onAction,
4891
- menuRef = _ref4.menuRef;
4980
+
4981
+ // ActionMenuPortal constants
4982
+ var MENU_WIDTH = 180;
4983
+ var MAX_MENU_HEIGHT = 320;
4984
+ var MENU_MARGIN = 8;
4985
+ var MIN_MENU_HEIGHT = 80;
4986
+ function ActionMenuPortal(_ref2) {
4987
+ var anchorElem = _ref2.anchorElem,
4988
+ anchorRow = _ref2.anchorRow,
4989
+ _ref2$actions = _ref2.actions,
4990
+ actions = _ref2$actions === void 0 ? [] : _ref2$actions;
4991
+ _ref2.onClose;
4992
+ var onAction = _ref2.onAction,
4993
+ menuRef = _ref2.menuRef;
4892
4994
  var _useState29 = useState({
4893
4995
  left: 0,
4894
4996
  top: 0,
4895
4997
  transformOrigin: "top right",
4896
4998
  maxHeight: 300,
4897
- width: 180,
4999
+ width: MENU_WIDTH,
4898
5000
  opacity: 0
4899
5001
  }),
4900
5002
  _useState30 = _slicedToArray(_useState29, 2),
4901
5003
  style = _useState30[0],
4902
5004
  setStyle = _useState30[1];
4903
- var menuWidth = 180;
4904
- var maxMenuHeight = 320;
4905
- var margin = 8;
4906
- var minMenuHeight = 80;
4907
5005
 
4908
5006
  // compute position using actual menu height (if available) and layout measurements
4909
5007
  var computePosition = function computePosition() {
@@ -4915,17 +5013,17 @@ function ActionMenuPortal(_ref4) {
4915
5013
  var spaceAbove = rect.top;
4916
5014
 
4917
5015
  // allowed max height based on available space
4918
- var allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, Math.max(spaceBelow - margin, spaceAbove - margin)));
5016
+ var allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, Math.max(spaceBelow - MENU_MARGIN, spaceAbove - MENU_MARGIN)));
4919
5017
 
4920
5018
  // choose placement by comparing actual available spaces
4921
5019
  var placement = "bottom";
4922
- if (spaceBelow < 160 && spaceAbove > spaceBelow) {
5020
+ if (spaceBelow < MENU_PLACEMENT_THRESHOLD && spaceAbove > spaceBelow) {
4923
5021
  placement = "top";
4924
5022
  // when placing on top we should cap allowedMaxHeight by spaceAbove
4925
- allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceAbove - margin));
5023
+ allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, spaceAbove - MENU_MARGIN));
4926
5024
  } else {
4927
5025
  // placing bottom: cap by spaceBelow
4928
- allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceBelow - margin));
5026
+ allowedMaxHeight = Math.min(MAX_MENU_HEIGHT, Math.max(MIN_MENU_HEIGHT, spaceBelow - MENU_MARGIN));
4929
5027
  }
4930
5028
 
4931
5029
  // measure the menu's content height if we can, and compute the actual menu height we'll use.
@@ -4942,23 +5040,23 @@ function ActionMenuPortal(_ref4) {
4942
5040
  var top;
4943
5041
  if (placement === "top") {
4944
5042
  // place menu so its bottom sits just above the anchor (rect.top - margin)
4945
- top = rect.top + scrollY - measuredMenuHeight - margin;
5043
+ top = rect.top + scrollY - measuredMenuHeight - MENU_MARGIN;
4946
5044
  } else {
4947
5045
  // place menu just below the anchor (rect.bottom + margin)
4948
- top = rect.bottom + scrollY + margin;
5046
+ top = rect.bottom + scrollY + MENU_MARGIN;
4949
5047
  }
4950
5048
 
4951
5049
  // clamp top to viewport (so it never goes off-screen)
4952
- var minTop = margin + scrollY;
4953
- var maxTop = window.innerHeight + scrollY - measuredMenuHeight - margin;
5050
+ var minTop = MENU_MARGIN + scrollY;
5051
+ var maxTop = window.innerHeight + scrollY - measuredMenuHeight - MENU_MARGIN;
4954
5052
  if (top < minTop) top = minTop;
4955
5053
  if (top > maxTop) top = maxTop;
4956
5054
 
4957
5055
  // compute left and clamp horizontally
4958
- var left = rect.right + scrollX - menuWidth;
4959
- if (left < margin) left = margin;
4960
- if (left + menuWidth > window.innerWidth - margin) {
4961
- left = Math.max(margin, window.innerWidth - menuWidth - margin);
5056
+ var left = rect.right + scrollX - MENU_WIDTH;
5057
+ if (left < MENU_MARGIN) left = MENU_MARGIN;
5058
+ if (left + MENU_WIDTH > window.innerWidth - MENU_MARGIN) {
5059
+ left = Math.max(MENU_MARGIN, window.innerWidth - MENU_WIDTH - MENU_MARGIN);
4962
5060
  }
4963
5061
  var transformOrigin = placement === "bottom" ? "top right" : "bottom right";
4964
5062
 
@@ -4968,7 +5066,7 @@ function ActionMenuPortal(_ref4) {
4968
5066
  top: top,
4969
5067
  transformOrigin: transformOrigin,
4970
5068
  maxHeight: allowedMaxHeight,
4971
- width: menuWidth,
5069
+ width: MENU_WIDTH,
4972
5070
  opacity: 1
4973
5071
  });
4974
5072
  };