@dreamtree-org/twreact-ui 1.0.69 → 1.0.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3883,7 +3883,7 @@ var Select = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
3883
3883
  }, error));
3884
3884
  });
3885
3885
 
3886
- var _excluded$k = ["data", "columns", "sortable", "filterable", "selectable", "pagination", "pageSize", "onSort", "onFilter", "onFetch", "onFilterChange", "onSelectionChange", "onRowClick", "hasDetails", "DetailsComponent", "className", "withAction", "onAction", "actions", "showSerial", "cellClass", "rowClass", "globalSearch", "limitOptions", "showLimitSelector", "onLimitChange", "showReloadButton", "renderReloadButton", "onReload", "stripedRows", "stripedColors", "responsiveBreakpoint"];
3886
+ var _excluded$k = ["data", "columns", "sortable", "filterable", "selectable", "pagination", "pageSize", "onSort", "onFilter", "onFetch", "onFilterChange", "onSelectionChange", "onRowClick", "hasDetails", "DetailsComponent", "className", "withAction", "onAction", "actions", "showSerial", "cellClass", "rowClass", "globalSearch", "limitOptions", "showLimitSelector", "onLimitChange", "showReloadButton", "renderReloadButton", "onReload", "stripedRows", "stripedColors", "responsiveBreakpoint", "serverSide", "totalRecords", "pageNumber", "onPageChange"];
3887
3887
  function ownKeys$9(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3888
3888
  function _objectSpread$9(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$9(Object(t), true).forEach(function (r) { _defineProperty$4(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$9(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3889
3889
  var Table = function Table(_ref) {
@@ -3937,7 +3937,13 @@ var Table = function Table(_ref) {
3937
3937
  stripedColors = _ref$stripedColors === void 0 ? ["#ffffff", "#f7fafc"] : _ref$stripedColors,
3938
3938
  _ref$responsiveBreakp = _ref.responsiveBreakpoint,
3939
3939
  responsiveBreakpoint = _ref$responsiveBreakp === void 0 ? 768 : _ref$responsiveBreakp,
3940
- props = _objectWithoutProperties$1(_ref, _excluded$k);
3940
+ _ref$serverSide = _ref.serverSide,
3941
+ serverSide = _ref$serverSide === void 0 ? false : _ref$serverSide,
3942
+ _ref$totalRecords = _ref.totalRecords,
3943
+ totalRecords = _ref$totalRecords === void 0 ? 0 : _ref$totalRecords,
3944
+ pageNumber = _ref.pageNumber;
3945
+ _ref.onPageChange;
3946
+ var props = _objectWithoutProperties$1(_ref, _excluded$k);
3941
3947
  // State for responsive view
3942
3948
  var _useState = React.useState(false),
3943
3949
  _useState2 = _slicedToArray(_useState, 2),
@@ -3998,7 +4004,7 @@ var Table = function Table(_ref) {
3998
4004
  _useState16 = _slicedToArray(_useState15, 2),
3999
4005
  expandedRows = _useState16[0],
4000
4006
  setExpandedRows = _useState16[1];
4001
- var _useState17 = React.useState(1),
4007
+ var _useState17 = React.useState(typeof pageNumber === "number" ? pageNumber : 1),
4002
4008
  _useState18 = _slicedToArray(_useState17, 2),
4003
4009
  currentPage = _useState18[0],
4004
4010
  setCurrentPage = _useState18[1];
@@ -4028,6 +4034,14 @@ var Table = function Table(_ref) {
4028
4034
  openActionKey = _useState28[0],
4029
4035
  setOpenActionKey = _useState28[1];
4030
4036
  var openRef = React.useRef(null);
4037
+ React.useEffect(function () {
4038
+ if (typeof pageNumber === "number" && pageNumber !== currentPage) {
4039
+ setCurrentPage(pageNumber);
4040
+ }
4041
+ }, [pageNumber]);
4042
+ React.useEffect(function () {
4043
+ setLimit(pageSize);
4044
+ }, [pageSize]);
4031
4045
 
4032
4046
  // Check if mobile view
4033
4047
  React.useEffect(function () {
@@ -4047,9 +4061,6 @@ var Table = function Table(_ref) {
4047
4061
  var isValidList = function isValidList(d) {
4048
4062
  return Array.isArray(d);
4049
4063
  };
4050
- var setDataForExternal = function setDataForExternal(newData) {
4051
- if (isValidList(newData)) setTableData(newData);else setTableData([]);
4052
- };
4053
4064
 
4054
4065
  // Close column menu when clicking outside
4055
4066
  React.useEffect(function () {
@@ -4146,70 +4157,42 @@ var Table = function Table(_ref) {
4146
4157
 
4147
4158
  // Sorting
4148
4159
  var sortedData = React.useMemo(function () {
4149
- if (!sortConfig.key) return tableData;
4160
+ if (serverSide || !sortConfig.key) return tableData;
4150
4161
  var key = sortConfig.key,
4151
4162
  direction = sortConfig.direction;
4152
4163
  return _toConsumableArray$1(tableData).sort(function (a, b) {
4153
- var aValue = a === null || a === void 0 ? void 0 : a[key];
4154
- var bValue = b === null || b === void 0 ? void 0 : b[key];
4155
- if (aValue == null && bValue == null) return 0;
4156
- if (aValue == null) return direction === "asc" ? -1 : 1;
4157
- if (bValue == null) return direction === "asc" ? 1 : -1;
4158
- if (typeof aValue === "number" && typeof bValue === "number") {
4159
- return direction === "asc" ? aValue - bValue : bValue - aValue;
4160
- }
4161
- var aStr = String(aValue).toLowerCase();
4162
- var bStr = String(bValue).toLowerCase();
4163
- if (aStr < bStr) return direction === "asc" ? -1 : 1;
4164
- if (aStr > bStr) return direction === "asc" ? 1 : -1;
4165
- return 0;
4164
+ var av = a === null || a === void 0 ? void 0 : a[key];
4165
+ var bv = b === null || b === void 0 ? void 0 : b[key];
4166
+ if (av == null || bv == null) return 0;
4167
+ if (typeof av === "number") return direction === "asc" ? av - bv : bv - av;
4168
+ return direction === "asc" ? String(av).localeCompare(String(bv)) : String(bv).localeCompare(String(av));
4166
4169
  });
4167
- }, [tableData, sortConfig]);
4170
+ }, [tableData, sortConfig, serverSide]);
4168
4171
 
4169
4172
  // Filtering
4170
4173
  var filteredData = React.useMemo(function () {
4171
- if (!filterable || Object.keys(filters).length === 0) return sortedData;
4172
- var globalQuery = String(filters.global || "").trim().toLowerCase();
4174
+ if (serverSide || !filterable || !Object.keys(filters).length) return sortedData;
4175
+ var q = (filters.global || "").toLowerCase();
4173
4176
  return sortedData.filter(function (row) {
4174
- // Per-column filters
4175
- var perColumnOk = Object.entries(filters).every(function (_ref2) {
4176
- var _ref3 = _slicedToArray(_ref2, 2),
4177
- key = _ref3[0],
4178
- value = _ref3[1];
4179
- if (key === "global") return true;
4180
- if (!value) return true;
4181
- var cell = row === null || row === void 0 ? void 0 : row[key];
4182
- if (cell == null) return false;
4183
- return String(cell).toLowerCase().includes(String(value).toLowerCase());
4177
+ return Object.values(row || {}).some(function (v) {
4178
+ return String(v).toLowerCase().includes(q);
4184
4179
  });
4185
- if (!perColumnOk) return false;
4186
-
4187
- // Global filter
4188
- if (globalQuery) {
4189
- var matchesVisible = visibleColumns.some(function (col) {
4190
- var v = row === null || row === void 0 ? void 0 : row[col.key];
4191
- if (v == null) return false;
4192
- return String(v).toLowerCase().includes(globalQuery);
4193
- });
4194
- if (matchesVisible) return true;
4195
- return Object.values(row || {}).some(function (v) {
4196
- return v != null ? String(v).toLowerCase().includes(globalQuery) : false;
4197
- });
4198
- }
4199
- return true;
4200
4180
  });
4201
- }, [sortedData, filters, filterable, visibleColumns]);
4202
-
4181
+ }, [sortedData, filters, filterable, serverSide]);
4203
4182
  // Pagination indices
4204
4183
  var startIndex = pagination ? (currentPage - 1) * limit : 0;
4205
4184
  var endIndex = pagination ? startIndex + limit : filteredData.length;
4206
4185
 
4207
4186
  // Paginated view
4208
4187
  var paginatedData = React.useMemo(function () {
4209
- if (!pagination) return filteredData;
4210
- return filteredData.slice(startIndex, endIndex);
4211
- }, [filteredData, startIndex, endIndex, pagination]);
4212
- var totalPages = Math.max(1, Math.ceil(filteredData.length / limit));
4188
+ if (!pagination || serverSide) return filteredData;
4189
+ var start = (currentPage - 1) * limit;
4190
+ return filteredData.slice(start, start + limit);
4191
+ }, [filteredData, pagination, serverSide, currentPage, limit]);
4192
+ var totalPages = React.useMemo(function () {
4193
+ if (!pagination) return 1;
4194
+ return serverSide ? Math.max(1, Math.ceil((totalRecords || 0) / limit)) : Math.max(1, Math.ceil(filteredData.length / limit));
4195
+ }, [pagination, serverSide, totalRecords, filteredData.length, limit]);
4213
4196
 
4214
4197
  // Sorting handler
4215
4198
  var handleSort = function handleSort(key) {
@@ -4324,17 +4307,19 @@ var Table = function Table(_ref) {
4324
4307
  React.useEffect(function () {
4325
4308
  if (!onFetch) return;
4326
4309
  onFetch({
4327
- setData: setDataForExternal,
4328
- filters: filters,
4310
+ setData: function setData(rows) {
4311
+ return setTableData(Array.isArray(rows) ? rows : []);
4312
+ },
4329
4313
  setLoading: setLoading,
4330
- pagination: {
4331
- currentPage: currentPage,
4332
- limit: limit,
4333
- totalPages: totalPages,
4334
- nextRecords: paginatedData
4335
- }
4314
+ filters: filters,
4315
+ page: currentPage,
4316
+ limit: limit,
4317
+ sort: sortConfig
4336
4318
  });
4337
- }, [filters, currentPage, limit]);
4319
+ }, [onFetch, filters, currentPage, limit, sortConfig]);
4320
+ React.useEffect(function () {
4321
+ if (!serverSide) setTableData(data);
4322
+ }, [data, serverSide]);
4338
4323
  React.useEffect(function () {
4339
4324
  if (!onFetch && isValidList(data)) setTableData(data);
4340
4325
  }, [data, onFetch]);
@@ -4352,13 +4337,11 @@ var Table = function Table(_ref) {
4352
4337
  applyGlobalSearch();
4353
4338
  }
4354
4339
  };
4355
- React.useEffect(function () {
4356
- setLimit(pageSize);
4357
- }, [pageSize]);
4358
4340
  var handleLimitChange = function handleLimitChange(nextLimit) {
4359
4341
  var parsed = Number(nextLimit);
4360
4342
  if (!Number.isFinite(parsed) || parsed <= 0) return;
4361
4343
  setLimit(parsed);
4344
+ setCurrentPage(1);
4362
4345
  onLimitChange === null || onLimitChange === void 0 || onLimitChange(parsed);
4363
4346
  };
4364
4347
 
@@ -4701,7 +4684,7 @@ var Table = function Table(_ref) {
4701
4684
  }, /*#__PURE__*/React.createElement("input", {
4702
4685
  type: "text",
4703
4686
  placeholder: "Filter ".concat(column.label),
4704
- className: "w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm",
4687
+ className: "w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm font-normal",
4705
4688
  value: filters[column.key] || "",
4706
4689
  onChange: function onChange(e) {
4707
4690
  return handleFilter(column.key, e.target.value);
@@ -4901,20 +4884,15 @@ var Table = function Table(_ref) {
4901
4884
  }
4902
4885
  }, "Next")))));
4903
4886
  };
4904
-
4905
- /* ---------------- ActionMenuPortal (updated for mobile) ---------------- */
4906
- function ActionMenuPortal(_ref4) {
4907
- var anchorElem = _ref4.anchorElem,
4908
- anchorRow = _ref4.anchorRow,
4909
- _ref4$actions = _ref4.actions,
4910
- actions = _ref4$actions === void 0 ? [] : _ref4$actions;
4911
- _ref4.onClose;
4912
- var onAction = _ref4.onAction,
4913
- menuRef = _ref4.menuRef;
4914
- var _useState29 = React.useState(0),
4915
- _useState30 = _slicedToArray(_useState29, 2),
4916
- setTick = _useState30[1];
4917
- var _useState31 = React.useState({
4887
+ function ActionMenuPortal(_ref2) {
4888
+ var anchorElem = _ref2.anchorElem,
4889
+ anchorRow = _ref2.anchorRow,
4890
+ _ref2$actions = _ref2.actions,
4891
+ actions = _ref2$actions === void 0 ? [] : _ref2$actions;
4892
+ _ref2.onClose;
4893
+ var onAction = _ref2.onAction,
4894
+ menuRef = _ref2.menuRef;
4895
+ var _useState29 = React.useState({
4918
4896
  left: 0,
4919
4897
  top: 0,
4920
4898
  transformOrigin: "top right",
@@ -4922,11 +4900,15 @@ function ActionMenuPortal(_ref4) {
4922
4900
  width: 180,
4923
4901
  opacity: 0
4924
4902
  }),
4925
- _useState32 = _slicedToArray(_useState31, 2),
4926
- style = _useState32[0],
4927
- setStyle = _useState32[1];
4903
+ _useState30 = _slicedToArray(_useState29, 2),
4904
+ style = _useState30[0],
4905
+ setStyle = _useState30[1];
4928
4906
  var menuWidth = 180;
4929
4907
  var maxMenuHeight = 320;
4908
+ var margin = 8;
4909
+ var minMenuHeight = 80;
4910
+
4911
+ // compute position using actual menu height (if available) and layout measurements
4930
4912
  var computePosition = function computePosition() {
4931
4913
  if (!anchorElem) return;
4932
4914
  var rect = anchorElem.getBoundingClientRect();
@@ -4934,52 +4916,95 @@ function ActionMenuPortal(_ref4) {
4934
4916
  var scrollX = window.scrollX || window.pageXOffset;
4935
4917
  var spaceBelow = window.innerHeight - rect.bottom;
4936
4918
  var spaceAbove = rect.top;
4937
- var top;
4919
+
4920
+ // allowed max height based on available space
4921
+ var allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, Math.max(spaceBelow - margin, spaceAbove - margin)));
4922
+
4923
+ // choose placement by comparing actual available spaces
4938
4924
  var placement = "bottom";
4939
- var maxHeight = Math.min(maxMenuHeight, Math.max(80, spaceBelow - 16));
4940
4925
  if (spaceBelow < 160 && spaceAbove > spaceBelow) {
4941
4926
  placement = "top";
4942
- maxHeight = Math.min(maxMenuHeight, Math.max(80, spaceAbove - 16));
4943
- top = rect.top + scrollY - maxHeight - 8;
4927
+ // when placing on top we should cap allowedMaxHeight by spaceAbove
4928
+ allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceAbove - margin));
4944
4929
  } else {
4945
- top = rect.bottom + scrollY + 8;
4930
+ // placing bottom: cap by spaceBelow
4931
+ allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceBelow - margin));
4946
4932
  }
4933
+
4934
+ // measure the menu's content height if we can, and compute the actual menu height we'll use.
4935
+ var measuredMenuHeight = allowedMaxHeight;
4936
+ var menuEl = menuRef === null || menuRef === void 0 ? void 0 : menuRef.current;
4937
+ if (menuEl) {
4938
+ // scrollHeight is the content height; offsetHeight may reflect current rendered height.
4939
+ var contentHeight = menuEl.scrollHeight || menuEl.offsetHeight || allowedMaxHeight;
4940
+ // actual height will be min(contentHeight, allowedMaxHeight)
4941
+ measuredMenuHeight = Math.min(contentHeight, allowedMaxHeight);
4942
+ }
4943
+
4944
+ // compute top according to placement and measuredMenuHeight
4945
+ var top;
4946
+ if (placement === "top") {
4947
+ // place menu so its bottom sits just above the anchor (rect.top - margin)
4948
+ top = rect.top + scrollY - measuredMenuHeight - margin;
4949
+ } else {
4950
+ // place menu just below the anchor (rect.bottom + margin)
4951
+ top = rect.bottom + scrollY + margin;
4952
+ }
4953
+
4954
+ // clamp top to viewport (so it never goes off-screen)
4955
+ var minTop = margin + scrollY;
4956
+ var maxTop = window.innerHeight + scrollY - measuredMenuHeight - margin;
4957
+ if (top < minTop) top = minTop;
4958
+ if (top > maxTop) top = maxTop;
4959
+
4960
+ // compute left and clamp horizontally
4947
4961
  var left = rect.right + scrollX - menuWidth;
4948
- if (left < 8) left = 8;
4949
- if (left + menuWidth > window.innerWidth - 8) left = window.innerWidth - menuWidth - 8;
4962
+ if (left < margin) left = margin;
4963
+ if (left + menuWidth > window.innerWidth - margin) {
4964
+ left = Math.max(margin, window.innerWidth - menuWidth - margin);
4965
+ }
4950
4966
  var transformOrigin = placement === "bottom" ? "top right" : "bottom right";
4967
+
4968
+ // set style (opacity 1 to fade-in)
4951
4969
  setStyle({
4952
4970
  left: left,
4953
4971
  top: top,
4954
4972
  transformOrigin: transformOrigin,
4955
- maxHeight: maxHeight,
4973
+ maxHeight: allowedMaxHeight,
4956
4974
  width: menuWidth,
4957
4975
  opacity: 1
4958
4976
  });
4959
4977
  };
4960
- React.useEffect(function () {
4978
+
4979
+ // useLayoutEffect so the position is measured & applied before paint
4980
+ React.useLayoutEffect(function () {
4981
+ // compute once after mount
4961
4982
  computePosition();
4962
4983
  var onScrollOrResize = function onScrollOrResize() {
4963
- computePosition();
4964
- setTick(function (t) {
4965
- return t + 1;
4984
+ // using requestAnimationFrame to avoid layout thrash on fast scroll/resize
4985
+ window.requestAnimationFrame(function () {
4986
+ return computePosition();
4966
4987
  });
4967
4988
  };
4968
4989
  window.addEventListener("resize", onScrollOrResize);
4990
+ // capture scrolls (true) so position updates even when scrolling ancestor elements
4969
4991
  window.addEventListener("scroll", onScrollOrResize, true);
4970
4992
  return function () {
4971
4993
  window.removeEventListener("resize", onScrollOrResize);
4972
4994
  window.removeEventListener("scroll", onScrollOrResize, true);
4973
4995
  };
4974
- }, [anchorElem]);
4996
+ // Recompute when anchor element changes or actions change (content height may change).
4997
+ }, [anchorElem, actions, menuRef]);
4975
4998
  return /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/React.createElement("div", {
4976
4999
  ref: menuRef,
4977
5000
  style: {
5001
+ position: "absolute",
4978
5002
  top: style.top,
4979
5003
  left: style.left,
4980
5004
  width: style.width,
4981
5005
  maxHeight: style.maxHeight,
4982
- transformOrigin: style.transformOrigin
5006
+ transformOrigin: style.transformOrigin,
5007
+ opacity: style.opacity
4983
5008
  },
4984
5009
  className: "absolute z-50 bg-white rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 overflow-y-auto transition-opacity duration-150 ease-out",
4985
5010
  onClick: function onClick(e) {
@@ -10370,7 +10395,12 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
10370
10395
  currentMonth = _useState4[0],
10371
10396
  setCurrentMonth = _useState4[1];
10372
10397
  var datePickerRef = React.useRef(null);
10398
+ var popupRef = React.useRef(null);
10373
10399
  var gridButtonRefs = React.useRef([]);
10400
+ var _useState5 = React.useState({}),
10401
+ _useState6 = _slicedToArray(_useState5, 2),
10402
+ popupStyle = _useState6[0],
10403
+ setPopupStyle = _useState6[1];
10374
10404
  React.useImperativeHandle(forwardedRef, function () {
10375
10405
  return datePickerRef.current;
10376
10406
  }, []);
@@ -10383,7 +10413,7 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
10383
10413
  // Close on outside click
10384
10414
  React.useEffect(function () {
10385
10415
  var handleClickOutside = function handleClickOutside(e) {
10386
- if (datePickerRef.current && !datePickerRef.current.contains(e.target)) {
10416
+ if (datePickerRef.current && !datePickerRef.current.contains(e.target) && popupRef.current && !popupRef.current.contains(e.target)) {
10387
10417
  setIsOpen(false);
10388
10418
  }
10389
10419
  };
@@ -10499,6 +10529,75 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
10499
10529
  if (idx >= 0) focusDayAtIndex(idx);
10500
10530
  }, 0);
10501
10531
  };
10532
+
10533
+ // compute popup position so it's aware of viewport edges (top/bottom/left/right)
10534
+ var computePopupPosition = function computePopupPosition() {
10535
+ var margin = 8;
10536
+ var trigger = datePickerRef.current;
10537
+ var popup = popupRef.current;
10538
+ if (!trigger || !popup) return;
10539
+ var triggerRect = trigger.getBoundingClientRect();
10540
+ // measure popup by temporarily making it visible if hidden
10541
+ var popupRect = popup.getBoundingClientRect();
10542
+
10543
+ // If the popup is not yet fully measured (width/height 0), try to read offsetWidth/Height
10544
+ if (popupRect.width === 0 || popupRect.height === 0) {
10545
+ popupRect = {
10546
+ width: popup.offsetWidth || 240,
10547
+ height: popup.offsetHeight || 300
10548
+ };
10549
+ }
10550
+
10551
+ // Decide vertical placement
10552
+ var spaceBelow = window.innerHeight - triggerRect.bottom;
10553
+ var spaceAbove = triggerRect.top;
10554
+ var placeBelow = spaceBelow >= popupRect.height + margin;
10555
+ if (!placeBelow && spaceAbove >= popupRect.height + margin) placeBelow = false;
10556
+ // If neither side has full space, pick the side with more space
10557
+ if (!placeBelow && spaceBelow < popupRect.height + margin && spaceAbove < popupRect.height + margin) {
10558
+ placeBelow = spaceBelow >= spaceAbove;
10559
+ }
10560
+
10561
+ // compute top coordinate (fixed positioning)
10562
+ var top;
10563
+ if (placeBelow) {
10564
+ top = Math.min(window.innerHeight - margin - popupRect.height, triggerRect.bottom + margin);
10565
+ } else {
10566
+ top = Math.max(margin, triggerRect.top - popupRect.height - margin);
10567
+ }
10568
+
10569
+ // Decide horizontal placement (try align left to trigger)
10570
+ var left = triggerRect.left;
10571
+ // If overflowing right, shift left
10572
+ if (left + popupRect.width > window.innerWidth - margin) {
10573
+ left = Math.max(margin, window.innerWidth - margin - popupRect.width);
10574
+ }
10575
+ // If overflowing left, clamp
10576
+ if (left < margin) left = margin;
10577
+ var transformOrigin = placeBelow ? "top left" : "bottom left";
10578
+ setPopupStyle({
10579
+ position: "fixed",
10580
+ top: "".concat(Math.round(top), "px"),
10581
+ left: "".concat(Math.round(left), "px"),
10582
+ zIndex: 9999,
10583
+ transformOrigin: transformOrigin
10584
+ });
10585
+ };
10586
+
10587
+ // recompute on open, on resize/scroll, and when popup content changes (currentMonth)
10588
+ React.useLayoutEffect(function () {
10589
+ if (!isOpen) return;
10590
+ computePopupPosition();
10591
+ var handleResize = function handleResize() {
10592
+ return computePopupPosition();
10593
+ };
10594
+ window.addEventListener("resize", handleResize);
10595
+ window.addEventListener("scroll", handleResize, true); // capture scrolling in ancestors
10596
+ return function () {
10597
+ window.removeEventListener("resize", handleResize);
10598
+ window.removeEventListener("scroll", handleResize, true);
10599
+ };
10600
+ }, [isOpen, currentMonth]);
10502
10601
  var header = /*#__PURE__*/React.createElement("div", {
10503
10602
  className: "mb-4 flex items-center justify-between"
10504
10603
  }, /*#__PURE__*/React.createElement("button", {
@@ -10523,9 +10622,11 @@ var DatePicker = /*#__PURE__*/React.forwardRef(function (_ref, forwardedRef) {
10523
10622
  className: "h-4 w-4"
10524
10623
  })));
10525
10624
  var calendarPopup = /*#__PURE__*/React.createElement("div", {
10526
- className: "absolute z-50 mt-1 w-80 rounded-md border border-gray-300 bg-white p-4 shadow-lg",
10625
+ ref: popupRef,
10626
+ className: "w-80 rounded-md border border-gray-300 bg-white p-4 shadow-lg",
10527
10627
  role: "dialog",
10528
- "aria-modal": "false"
10628
+ "aria-modal": "false",
10629
+ style: popupStyle
10529
10630
  }, header, /*#__PURE__*/React.createElement("div", {
10530
10631
  className: "mb-2 grid grid-cols-7 gap-1"
10531
10632
  }, Array.from({
@@ -11874,11 +11975,10 @@ function Slider(_ref6) {
11874
11975
  var _excluded$g = ["value", "onChange", "placeholder", "label", "error", "disabled", "required", "minDate", "maxDate", "className", "weekStartsOn", "portal", "displayFormat", "locale", "showClear", "closeOnSelect"];
11875
11976
 
11876
11977
  /*
11877
- Single-calendar DateRangePicker with hover-preview
11878
- - Enforces mandatory range selection: the component will NOT commit or show a selected range
11879
- until both start and end dates are chosen.
11880
- - Popup cannot be dismissed (via outside click, trigger toggle, or Escape) while only
11881
- a start date is selected. Use Clear to cancel the in-progress selection.
11978
+ Single-calendar DateRangePicker with hover-preview and space-aware popup positioning
11979
+ - Enforces mandatory range selection until both start and end are chosen.
11980
+ - Popup flips above/below and clamps horizontally to avoid clipping.
11981
+ - Popup won't close while only a start date is selected (Clear cancels in-progress selection).
11882
11982
  */
11883
11983
 
11884
11984
  var normalizeDate = function normalizeDate(d) {
@@ -11952,11 +12052,16 @@ var DateRangePicker = function DateRangePicker(_ref) {
11952
12052
  if (start) setCurrentMonth(start);
11953
12053
  }, [start, end]);
11954
12054
  var datePickerRef = React.useRef(null);
12055
+ var popupRef = React.useRef(null);
12056
+ var _useState1 = React.useState({}),
12057
+ _useState10 = _slicedToArray(_useState1, 2),
12058
+ popupStyle = _useState10[0],
12059
+ setPopupStyle = _useState10[1];
11955
12060
 
11956
12061
  // Outside click: only close if there is NOT an in-progress single-date selection
11957
12062
  React.useEffect(function () {
11958
12063
  var handleClickOutside = function handleClickOutside(e) {
11959
- if (datePickerRef.current && !datePickerRef.current.contains(e.target)) {
12064
+ if (datePickerRef.current && !datePickerRef.current.contains(e.target) && (!popupRef.current || !popupRef.current.contains(e.target))) {
11960
12065
  // If user has selected a start but no end, prevent closing to force range selection
11961
12066
  if (localStart && !localEnd) {
11962
12067
  return;
@@ -12112,10 +12217,82 @@ var DateRangePicker = function DateRangePicker(_ref) {
12112
12217
  }, /*#__PURE__*/React.createElement(ChevronRight, {
12113
12218
  className: "h-4 w-4"
12114
12219
  })));
12220
+
12221
+ // compute popup position so it's aware of viewport edges (top/bottom/left/right)
12222
+ var computePopupPosition = function computePopupPosition() {
12223
+ var margin = 8;
12224
+ var trigger = datePickerRef.current;
12225
+ var popup = popupRef.current;
12226
+ if (!trigger || !popup) return;
12227
+ var triggerRect = trigger.getBoundingClientRect();
12228
+ // measure popup by temporarily making it visible if hidden
12229
+ var popupRect = popup.getBoundingClientRect();
12230
+
12231
+ // If the popup is not yet fully measured (width/height 0), try to read offsetWidth/Height
12232
+ if (popupRect.width === 0 || popupRect.height === 0) {
12233
+ popupRect = {
12234
+ width: popup.offsetWidth || 320,
12235
+ height: popup.offsetHeight || 300
12236
+ };
12237
+ }
12238
+
12239
+ // Decide vertical placement
12240
+ var spaceBelow = window.innerHeight - triggerRect.bottom;
12241
+ var spaceAbove = triggerRect.top;
12242
+ var placeBelow = spaceBelow >= popupRect.height + margin;
12243
+ if (!placeBelow && spaceAbove >= popupRect.height + margin) placeBelow = false;
12244
+ // If neither side has full space, pick the side with more space
12245
+ if (!placeBelow && spaceBelow < popupRect.height + margin && spaceAbove < popupRect.height + margin) {
12246
+ placeBelow = spaceBelow >= spaceAbove;
12247
+ }
12248
+
12249
+ // compute top coordinate (fixed positioning)
12250
+ var top;
12251
+ if (placeBelow) {
12252
+ top = Math.min(window.innerHeight - margin - popupRect.height, triggerRect.bottom + margin);
12253
+ } else {
12254
+ top = Math.max(margin, triggerRect.top - popupRect.height - margin);
12255
+ }
12256
+
12257
+ // Decide horizontal placement (try align left to trigger)
12258
+ var left = triggerRect.left;
12259
+ // If overflowing right, shift left
12260
+ if (left + popupRect.width > window.innerWidth - margin) {
12261
+ left = Math.max(margin, window.innerWidth - margin - popupRect.width);
12262
+ }
12263
+ // If overflowing left, clamp
12264
+ if (left < margin) left = margin;
12265
+ var transformOrigin = placeBelow ? "top left" : "bottom left";
12266
+ setPopupStyle({
12267
+ position: "fixed",
12268
+ top: "".concat(Math.round(top), "px"),
12269
+ left: "".concat(Math.round(left), "px"),
12270
+ zIndex: 9999,
12271
+ transformOrigin: transformOrigin
12272
+ });
12273
+ };
12274
+
12275
+ // recompute on open, on resize/scroll, and when popup content changes (currentMonth)
12276
+ React.useLayoutEffect(function () {
12277
+ if (!isOpen) return;
12278
+ // compute on next frame to ensure popup ref exists in DOM (especially with portal)
12279
+ computePopupPosition();
12280
+ var handleResize = function handleResize() {
12281
+ return computePopupPosition();
12282
+ };
12283
+ window.addEventListener("resize", handleResize);
12284
+ window.addEventListener("scroll", handleResize, true); // capture scrolling in ancestors
12285
+ return function () {
12286
+ window.removeEventListener("resize", handleResize);
12287
+ window.removeEventListener("scroll", handleResize, true);
12288
+ };
12289
+ }, [isOpen, currentMonth]);
12115
12290
  var calendarPopup = /*#__PURE__*/React.createElement("div", {
12116
- className: "absolute z-50 mt-1 w-80 rounded-md border border-gray-300 bg-white p-4 shadow-lg",
12291
+ ref: popupRef,
12292
+ className: "w-80 rounded-md border border-gray-300 bg-white p-4 shadow-lg",
12117
12293
  role: "dialog",
12118
- "aria-modal": "false"
12294
+ "aria-modal": "false",
12295
+ style: popupStyle
12119
12296
  }, header, /*#__PURE__*/React.createElement("div", {
12120
12297
  className: "mb-2 grid grid-cols-7 gap-1"
12121
12298
  }, Array.from({