@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.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, useCallback, createContext, useContext, cloneElement, PureComponent } from 'react';
2
+ import React__default, { forwardRef, createElement, useId, useRef, useState, useEffect, useImperativeHandle, useMemo, useLayoutEffect, useCallback, createContext, useContext, cloneElement, PureComponent } from 'react';
3
3
  import ReactDOM, { createPortal } from 'react-dom';
4
4
 
5
5
  function _extends() {
@@ -3863,7 +3863,7 @@ 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
3869
  var Table = function Table(_ref) {
@@ -3917,7 +3917,13 @@ var Table = function Table(_ref) {
3917
3917
  stripedColors = _ref$stripedColors === void 0 ? ["#ffffff", "#f7fafc"] : _ref$stripedColors,
3918
3918
  _ref$responsiveBreakp = _ref.responsiveBreakpoint,
3919
3919
  responsiveBreakpoint = _ref$responsiveBreakp === void 0 ? 768 : _ref$responsiveBreakp,
3920
- props = _objectWithoutProperties$1(_ref, _excluded$k);
3920
+ _ref$serverSide = _ref.serverSide,
3921
+ serverSide = _ref$serverSide === void 0 ? false : _ref$serverSide,
3922
+ _ref$totalRecords = _ref.totalRecords,
3923
+ totalRecords = _ref$totalRecords === void 0 ? 0 : _ref$totalRecords,
3924
+ pageNumber = _ref.pageNumber;
3925
+ _ref.onPageChange;
3926
+ var props = _objectWithoutProperties$1(_ref, _excluded$k);
3921
3927
  // State for responsive view
3922
3928
  var _useState = useState(false),
3923
3929
  _useState2 = _slicedToArray(_useState, 2),
@@ -3978,7 +3984,7 @@ var Table = function Table(_ref) {
3978
3984
  _useState16 = _slicedToArray(_useState15, 2),
3979
3985
  expandedRows = _useState16[0],
3980
3986
  setExpandedRows = _useState16[1];
3981
- var _useState17 = useState(1),
3987
+ var _useState17 = useState(typeof pageNumber === "number" ? pageNumber : 1),
3982
3988
  _useState18 = _slicedToArray(_useState17, 2),
3983
3989
  currentPage = _useState18[0],
3984
3990
  setCurrentPage = _useState18[1];
@@ -4008,6 +4014,14 @@ var Table = function Table(_ref) {
4008
4014
  openActionKey = _useState28[0],
4009
4015
  setOpenActionKey = _useState28[1];
4010
4016
  var openRef = useRef(null);
4017
+ useEffect(function () {
4018
+ if (typeof pageNumber === "number" && pageNumber !== currentPage) {
4019
+ setCurrentPage(pageNumber);
4020
+ }
4021
+ }, [pageNumber]);
4022
+ useEffect(function () {
4023
+ setLimit(pageSize);
4024
+ }, [pageSize]);
4011
4025
 
4012
4026
  // Check if mobile view
4013
4027
  useEffect(function () {
@@ -4027,9 +4041,6 @@ var Table = function Table(_ref) {
4027
4041
  var isValidList = function isValidList(d) {
4028
4042
  return Array.isArray(d);
4029
4043
  };
4030
- var setDataForExternal = function setDataForExternal(newData) {
4031
- if (isValidList(newData)) setTableData(newData);else setTableData([]);
4032
- };
4033
4044
 
4034
4045
  // Close column menu when clicking outside
4035
4046
  useEffect(function () {
@@ -4126,70 +4137,42 @@ var Table = function Table(_ref) {
4126
4137
 
4127
4138
  // Sorting
4128
4139
  var sortedData = useMemo(function () {
4129
- if (!sortConfig.key) return tableData;
4140
+ if (serverSide || !sortConfig.key) return tableData;
4130
4141
  var key = sortConfig.key,
4131
4142
  direction = sortConfig.direction;
4132
4143
  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;
4144
+ var av = a === null || a === void 0 ? void 0 : a[key];
4145
+ var bv = b === null || b === void 0 ? void 0 : b[key];
4146
+ if (av == null || bv == null) return 0;
4147
+ if (typeof av === "number") return direction === "asc" ? av - bv : bv - av;
4148
+ return direction === "asc" ? String(av).localeCompare(String(bv)) : String(bv).localeCompare(String(av));
4146
4149
  });
4147
- }, [tableData, sortConfig]);
4150
+ }, [tableData, sortConfig, serverSide]);
4148
4151
 
4149
4152
  // Filtering
4150
4153
  var filteredData = useMemo(function () {
4151
- if (!filterable || Object.keys(filters).length === 0) return sortedData;
4152
- var globalQuery = String(filters.global || "").trim().toLowerCase();
4154
+ if (serverSide || !filterable || !Object.keys(filters).length) return sortedData;
4155
+ var q = (filters.global || "").toLowerCase();
4153
4156
  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());
4157
+ return Object.values(row || {}).some(function (v) {
4158
+ return String(v).toLowerCase().includes(q);
4164
4159
  });
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
4160
  });
4181
- }, [sortedData, filters, filterable, visibleColumns]);
4182
-
4161
+ }, [sortedData, filters, filterable, serverSide]);
4183
4162
  // Pagination indices
4184
4163
  var startIndex = pagination ? (currentPage - 1) * limit : 0;
4185
4164
  var endIndex = pagination ? startIndex + limit : filteredData.length;
4186
4165
 
4187
4166
  // Paginated view
4188
4167
  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));
4168
+ if (!pagination || serverSide) return filteredData;
4169
+ var start = (currentPage - 1) * limit;
4170
+ return filteredData.slice(start, start + limit);
4171
+ }, [filteredData, pagination, serverSide, currentPage, limit]);
4172
+ var totalPages = useMemo(function () {
4173
+ if (!pagination) return 1;
4174
+ return serverSide ? Math.max(1, Math.ceil((totalRecords || 0) / limit)) : Math.max(1, Math.ceil(filteredData.length / limit));
4175
+ }, [pagination, serverSide, totalRecords, filteredData.length, limit]);
4193
4176
 
4194
4177
  // Sorting handler
4195
4178
  var handleSort = function handleSort(key) {
@@ -4304,17 +4287,19 @@ var Table = function Table(_ref) {
4304
4287
  useEffect(function () {
4305
4288
  if (!onFetch) return;
4306
4289
  onFetch({
4307
- setData: setDataForExternal,
4308
- filters: filters,
4290
+ setData: function setData(rows) {
4291
+ return setTableData(Array.isArray(rows) ? rows : []);
4292
+ },
4309
4293
  setLoading: setLoading,
4310
- pagination: {
4311
- currentPage: currentPage,
4312
- limit: limit,
4313
- totalPages: totalPages,
4314
- nextRecords: paginatedData
4315
- }
4294
+ filters: filters,
4295
+ page: currentPage,
4296
+ limit: limit,
4297
+ sort: sortConfig
4316
4298
  });
4317
- }, [filters, currentPage, limit]);
4299
+ }, [onFetch, filters, currentPage, limit, sortConfig]);
4300
+ useEffect(function () {
4301
+ if (!serverSide) setTableData(data);
4302
+ }, [data, serverSide]);
4318
4303
  useEffect(function () {
4319
4304
  if (!onFetch && isValidList(data)) setTableData(data);
4320
4305
  }, [data, onFetch]);
@@ -4332,13 +4317,11 @@ var Table = function Table(_ref) {
4332
4317
  applyGlobalSearch();
4333
4318
  }
4334
4319
  };
4335
- useEffect(function () {
4336
- setLimit(pageSize);
4337
- }, [pageSize]);
4338
4320
  var handleLimitChange = function handleLimitChange(nextLimit) {
4339
4321
  var parsed = Number(nextLimit);
4340
4322
  if (!Number.isFinite(parsed) || parsed <= 0) return;
4341
4323
  setLimit(parsed);
4324
+ setCurrentPage(1);
4342
4325
  onLimitChange === null || onLimitChange === void 0 || onLimitChange(parsed);
4343
4326
  };
4344
4327
 
@@ -4681,7 +4664,7 @@ var Table = function Table(_ref) {
4681
4664
  }, /*#__PURE__*/React__default.createElement("input", {
4682
4665
  type: "text",
4683
4666
  placeholder: "Filter ".concat(column.label),
4684
- className: "w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm",
4667
+ className: "w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm font-normal",
4685
4668
  value: filters[column.key] || "",
4686
4669
  onChange: function onChange(e) {
4687
4670
  return handleFilter(column.key, e.target.value);
@@ -4881,20 +4864,15 @@ var Table = function Table(_ref) {
4881
4864
  }
4882
4865
  }, "Next")))));
4883
4866
  };
4884
-
4885
- /* ---------------- ActionMenuPortal (updated for mobile) ---------------- */
4886
- function ActionMenuPortal(_ref4) {
4887
- var anchorElem = _ref4.anchorElem,
4888
- anchorRow = _ref4.anchorRow,
4889
- _ref4$actions = _ref4.actions,
4890
- actions = _ref4$actions === void 0 ? [] : _ref4$actions;
4891
- _ref4.onClose;
4892
- var onAction = _ref4.onAction,
4893
- menuRef = _ref4.menuRef;
4894
- var _useState29 = useState(0),
4895
- _useState30 = _slicedToArray(_useState29, 2),
4896
- setTick = _useState30[1];
4897
- var _useState31 = useState({
4867
+ function ActionMenuPortal(_ref2) {
4868
+ var anchorElem = _ref2.anchorElem,
4869
+ anchorRow = _ref2.anchorRow,
4870
+ _ref2$actions = _ref2.actions,
4871
+ actions = _ref2$actions === void 0 ? [] : _ref2$actions;
4872
+ _ref2.onClose;
4873
+ var onAction = _ref2.onAction,
4874
+ menuRef = _ref2.menuRef;
4875
+ var _useState29 = useState({
4898
4876
  left: 0,
4899
4877
  top: 0,
4900
4878
  transformOrigin: "top right",
@@ -4902,11 +4880,15 @@ function ActionMenuPortal(_ref4) {
4902
4880
  width: 180,
4903
4881
  opacity: 0
4904
4882
  }),
4905
- _useState32 = _slicedToArray(_useState31, 2),
4906
- style = _useState32[0],
4907
- setStyle = _useState32[1];
4883
+ _useState30 = _slicedToArray(_useState29, 2),
4884
+ style = _useState30[0],
4885
+ setStyle = _useState30[1];
4908
4886
  var menuWidth = 180;
4909
4887
  var maxMenuHeight = 320;
4888
+ var margin = 8;
4889
+ var minMenuHeight = 80;
4890
+
4891
+ // compute position using actual menu height (if available) and layout measurements
4910
4892
  var computePosition = function computePosition() {
4911
4893
  if (!anchorElem) return;
4912
4894
  var rect = anchorElem.getBoundingClientRect();
@@ -4914,52 +4896,95 @@ function ActionMenuPortal(_ref4) {
4914
4896
  var scrollX = window.scrollX || window.pageXOffset;
4915
4897
  var spaceBelow = window.innerHeight - rect.bottom;
4916
4898
  var spaceAbove = rect.top;
4917
- var top;
4899
+
4900
+ // allowed max height based on available space
4901
+ var allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, Math.max(spaceBelow - margin, spaceAbove - margin)));
4902
+
4903
+ // choose placement by comparing actual available spaces
4918
4904
  var placement = "bottom";
4919
- var maxHeight = Math.min(maxMenuHeight, Math.max(80, spaceBelow - 16));
4920
4905
  if (spaceBelow < 160 && spaceAbove > spaceBelow) {
4921
4906
  placement = "top";
4922
- maxHeight = Math.min(maxMenuHeight, Math.max(80, spaceAbove - 16));
4923
- top = rect.top + scrollY - maxHeight - 8;
4907
+ // when placing on top we should cap allowedMaxHeight by spaceAbove
4908
+ allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceAbove - margin));
4924
4909
  } else {
4925
- top = rect.bottom + scrollY + 8;
4910
+ // placing bottom: cap by spaceBelow
4911
+ allowedMaxHeight = Math.min(maxMenuHeight, Math.max(minMenuHeight, spaceBelow - margin));
4926
4912
  }
4913
+
4914
+ // measure the menu's content height if we can, and compute the actual menu height we'll use.
4915
+ var measuredMenuHeight = allowedMaxHeight;
4916
+ var menuEl = menuRef === null || menuRef === void 0 ? void 0 : menuRef.current;
4917
+ if (menuEl) {
4918
+ // scrollHeight is the content height; offsetHeight may reflect current rendered height.
4919
+ var contentHeight = menuEl.scrollHeight || menuEl.offsetHeight || allowedMaxHeight;
4920
+ // actual height will be min(contentHeight, allowedMaxHeight)
4921
+ measuredMenuHeight = Math.min(contentHeight, allowedMaxHeight);
4922
+ }
4923
+
4924
+ // compute top according to placement and measuredMenuHeight
4925
+ var top;
4926
+ if (placement === "top") {
4927
+ // place menu so its bottom sits just above the anchor (rect.top - margin)
4928
+ top = rect.top + scrollY - measuredMenuHeight - margin;
4929
+ } else {
4930
+ // place menu just below the anchor (rect.bottom + margin)
4931
+ top = rect.bottom + scrollY + margin;
4932
+ }
4933
+
4934
+ // clamp top to viewport (so it never goes off-screen)
4935
+ var minTop = margin + scrollY;
4936
+ var maxTop = window.innerHeight + scrollY - measuredMenuHeight - margin;
4937
+ if (top < minTop) top = minTop;
4938
+ if (top > maxTop) top = maxTop;
4939
+
4940
+ // compute left and clamp horizontally
4927
4941
  var left = rect.right + scrollX - menuWidth;
4928
- if (left < 8) left = 8;
4929
- if (left + menuWidth > window.innerWidth - 8) left = window.innerWidth - menuWidth - 8;
4942
+ if (left < margin) left = margin;
4943
+ if (left + menuWidth > window.innerWidth - margin) {
4944
+ left = Math.max(margin, window.innerWidth - menuWidth - margin);
4945
+ }
4930
4946
  var transformOrigin = placement === "bottom" ? "top right" : "bottom right";
4947
+
4948
+ // set style (opacity 1 to fade-in)
4931
4949
  setStyle({
4932
4950
  left: left,
4933
4951
  top: top,
4934
4952
  transformOrigin: transformOrigin,
4935
- maxHeight: maxHeight,
4953
+ maxHeight: allowedMaxHeight,
4936
4954
  width: menuWidth,
4937
4955
  opacity: 1
4938
4956
  });
4939
4957
  };
4940
- useEffect(function () {
4958
+
4959
+ // useLayoutEffect so the position is measured & applied before paint
4960
+ useLayoutEffect(function () {
4961
+ // compute once after mount
4941
4962
  computePosition();
4942
4963
  var onScrollOrResize = function onScrollOrResize() {
4943
- computePosition();
4944
- setTick(function (t) {
4945
- return t + 1;
4964
+ // using requestAnimationFrame to avoid layout thrash on fast scroll/resize
4965
+ window.requestAnimationFrame(function () {
4966
+ return computePosition();
4946
4967
  });
4947
4968
  };
4948
4969
  window.addEventListener("resize", onScrollOrResize);
4970
+ // capture scrolls (true) so position updates even when scrolling ancestor elements
4949
4971
  window.addEventListener("scroll", onScrollOrResize, true);
4950
4972
  return function () {
4951
4973
  window.removeEventListener("resize", onScrollOrResize);
4952
4974
  window.removeEventListener("scroll", onScrollOrResize, true);
4953
4975
  };
4954
- }, [anchorElem]);
4976
+ // Recompute when anchor element changes or actions change (content height may change).
4977
+ }, [anchorElem, actions, menuRef]);
4955
4978
  return /*#__PURE__*/createPortal(/*#__PURE__*/React__default.createElement("div", {
4956
4979
  ref: menuRef,
4957
4980
  style: {
4981
+ position: "absolute",
4958
4982
  top: style.top,
4959
4983
  left: style.left,
4960
4984
  width: style.width,
4961
4985
  maxHeight: style.maxHeight,
4962
- transformOrigin: style.transformOrigin
4986
+ transformOrigin: style.transformOrigin,
4987
+ opacity: style.opacity
4963
4988
  },
4964
4989
  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",
4965
4990
  onClick: function onClick(e) {
@@ -10350,7 +10375,12 @@ var DatePicker = /*#__PURE__*/React__default.forwardRef(function (_ref, forwarde
10350
10375
  currentMonth = _useState4[0],
10351
10376
  setCurrentMonth = _useState4[1];
10352
10377
  var datePickerRef = useRef(null);
10378
+ var popupRef = useRef(null);
10353
10379
  var gridButtonRefs = useRef([]);
10380
+ var _useState5 = useState({}),
10381
+ _useState6 = _slicedToArray(_useState5, 2),
10382
+ popupStyle = _useState6[0],
10383
+ setPopupStyle = _useState6[1];
10354
10384
  useImperativeHandle(forwardedRef, function () {
10355
10385
  return datePickerRef.current;
10356
10386
  }, []);
@@ -10363,7 +10393,7 @@ var DatePicker = /*#__PURE__*/React__default.forwardRef(function (_ref, forwarde
10363
10393
  // Close on outside click
10364
10394
  useEffect(function () {
10365
10395
  var handleClickOutside = function handleClickOutside(e) {
10366
- if (datePickerRef.current && !datePickerRef.current.contains(e.target)) {
10396
+ if (datePickerRef.current && !datePickerRef.current.contains(e.target) && popupRef.current && !popupRef.current.contains(e.target)) {
10367
10397
  setIsOpen(false);
10368
10398
  }
10369
10399
  };
@@ -10479,6 +10509,75 @@ var DatePicker = /*#__PURE__*/React__default.forwardRef(function (_ref, forwarde
10479
10509
  if (idx >= 0) focusDayAtIndex(idx);
10480
10510
  }, 0);
10481
10511
  };
10512
+
10513
+ // compute popup position so it's aware of viewport edges (top/bottom/left/right)
10514
+ var computePopupPosition = function computePopupPosition() {
10515
+ var margin = 8;
10516
+ var trigger = datePickerRef.current;
10517
+ var popup = popupRef.current;
10518
+ if (!trigger || !popup) return;
10519
+ var triggerRect = trigger.getBoundingClientRect();
10520
+ // measure popup by temporarily making it visible if hidden
10521
+ var popupRect = popup.getBoundingClientRect();
10522
+
10523
+ // If the popup is not yet fully measured (width/height 0), try to read offsetWidth/Height
10524
+ if (popupRect.width === 0 || popupRect.height === 0) {
10525
+ popupRect = {
10526
+ width: popup.offsetWidth || 240,
10527
+ height: popup.offsetHeight || 300
10528
+ };
10529
+ }
10530
+
10531
+ // Decide vertical placement
10532
+ var spaceBelow = window.innerHeight - triggerRect.bottom;
10533
+ var spaceAbove = triggerRect.top;
10534
+ var placeBelow = spaceBelow >= popupRect.height + margin;
10535
+ if (!placeBelow && spaceAbove >= popupRect.height + margin) placeBelow = false;
10536
+ // If neither side has full space, pick the side with more space
10537
+ if (!placeBelow && spaceBelow < popupRect.height + margin && spaceAbove < popupRect.height + margin) {
10538
+ placeBelow = spaceBelow >= spaceAbove;
10539
+ }
10540
+
10541
+ // compute top coordinate (fixed positioning)
10542
+ var top;
10543
+ if (placeBelow) {
10544
+ top = Math.min(window.innerHeight - margin - popupRect.height, triggerRect.bottom + margin);
10545
+ } else {
10546
+ top = Math.max(margin, triggerRect.top - popupRect.height - margin);
10547
+ }
10548
+
10549
+ // Decide horizontal placement (try align left to trigger)
10550
+ var left = triggerRect.left;
10551
+ // If overflowing right, shift left
10552
+ if (left + popupRect.width > window.innerWidth - margin) {
10553
+ left = Math.max(margin, window.innerWidth - margin - popupRect.width);
10554
+ }
10555
+ // If overflowing left, clamp
10556
+ if (left < margin) left = margin;
10557
+ var transformOrigin = placeBelow ? "top left" : "bottom left";
10558
+ setPopupStyle({
10559
+ position: "fixed",
10560
+ top: "".concat(Math.round(top), "px"),
10561
+ left: "".concat(Math.round(left), "px"),
10562
+ zIndex: 9999,
10563
+ transformOrigin: transformOrigin
10564
+ });
10565
+ };
10566
+
10567
+ // recompute on open, on resize/scroll, and when popup content changes (currentMonth)
10568
+ useLayoutEffect(function () {
10569
+ if (!isOpen) return;
10570
+ computePopupPosition();
10571
+ var handleResize = function handleResize() {
10572
+ return computePopupPosition();
10573
+ };
10574
+ window.addEventListener("resize", handleResize);
10575
+ window.addEventListener("scroll", handleResize, true); // capture scrolling in ancestors
10576
+ return function () {
10577
+ window.removeEventListener("resize", handleResize);
10578
+ window.removeEventListener("scroll", handleResize, true);
10579
+ };
10580
+ }, [isOpen, currentMonth]);
10482
10581
  var header = /*#__PURE__*/React__default.createElement("div", {
10483
10582
  className: "mb-4 flex items-center justify-between"
10484
10583
  }, /*#__PURE__*/React__default.createElement("button", {
@@ -10503,9 +10602,11 @@ var DatePicker = /*#__PURE__*/React__default.forwardRef(function (_ref, forwarde
10503
10602
  className: "h-4 w-4"
10504
10603
  })));
10505
10604
  var calendarPopup = /*#__PURE__*/React__default.createElement("div", {
10506
- className: "absolute z-50 mt-1 w-80 rounded-md border border-gray-300 bg-white p-4 shadow-lg",
10605
+ ref: popupRef,
10606
+ className: "w-80 rounded-md border border-gray-300 bg-white p-4 shadow-lg",
10507
10607
  role: "dialog",
10508
- "aria-modal": "false"
10608
+ "aria-modal": "false",
10609
+ style: popupStyle
10509
10610
  }, header, /*#__PURE__*/React__default.createElement("div", {
10510
10611
  className: "mb-2 grid grid-cols-7 gap-1"
10511
10612
  }, Array.from({
@@ -11854,11 +11955,10 @@ function Slider(_ref6) {
11854
11955
  var _excluded$g = ["value", "onChange", "placeholder", "label", "error", "disabled", "required", "minDate", "maxDate", "className", "weekStartsOn", "portal", "displayFormat", "locale", "showClear", "closeOnSelect"];
11855
11956
 
11856
11957
  /*
11857
- Single-calendar DateRangePicker with hover-preview
11858
- - Enforces mandatory range selection: the component will NOT commit or show a selected range
11859
- until both start and end dates are chosen.
11860
- - Popup cannot be dismissed (via outside click, trigger toggle, or Escape) while only
11861
- a start date is selected. Use Clear to cancel the in-progress selection.
11958
+ Single-calendar DateRangePicker with hover-preview and space-aware popup positioning
11959
+ - Enforces mandatory range selection until both start and end are chosen.
11960
+ - Popup flips above/below and clamps horizontally to avoid clipping.
11961
+ - Popup won't close while only a start date is selected (Clear cancels in-progress selection).
11862
11962
  */
11863
11963
 
11864
11964
  var normalizeDate = function normalizeDate(d) {
@@ -11932,11 +12032,16 @@ var DateRangePicker = function DateRangePicker(_ref) {
11932
12032
  if (start) setCurrentMonth(start);
11933
12033
  }, [start, end]);
11934
12034
  var datePickerRef = useRef(null);
12035
+ var popupRef = useRef(null);
12036
+ var _useState1 = useState({}),
12037
+ _useState10 = _slicedToArray(_useState1, 2),
12038
+ popupStyle = _useState10[0],
12039
+ setPopupStyle = _useState10[1];
11935
12040
 
11936
12041
  // Outside click: only close if there is NOT an in-progress single-date selection
11937
12042
  useEffect(function () {
11938
12043
  var handleClickOutside = function handleClickOutside(e) {
11939
- if (datePickerRef.current && !datePickerRef.current.contains(e.target)) {
12044
+ if (datePickerRef.current && !datePickerRef.current.contains(e.target) && (!popupRef.current || !popupRef.current.contains(e.target))) {
11940
12045
  // If user has selected a start but no end, prevent closing to force range selection
11941
12046
  if (localStart && !localEnd) {
11942
12047
  return;
@@ -12092,10 +12197,82 @@ var DateRangePicker = function DateRangePicker(_ref) {
12092
12197
  }, /*#__PURE__*/React__default.createElement(ChevronRight, {
12093
12198
  className: "h-4 w-4"
12094
12199
  })));
12200
+
12201
+ // compute popup position so it's aware of viewport edges (top/bottom/left/right)
12202
+ var computePopupPosition = function computePopupPosition() {
12203
+ var margin = 8;
12204
+ var trigger = datePickerRef.current;
12205
+ var popup = popupRef.current;
12206
+ if (!trigger || !popup) return;
12207
+ var triggerRect = trigger.getBoundingClientRect();
12208
+ // measure popup by temporarily making it visible if hidden
12209
+ var popupRect = popup.getBoundingClientRect();
12210
+
12211
+ // If the popup is not yet fully measured (width/height 0), try to read offsetWidth/Height
12212
+ if (popupRect.width === 0 || popupRect.height === 0) {
12213
+ popupRect = {
12214
+ width: popup.offsetWidth || 320,
12215
+ height: popup.offsetHeight || 300
12216
+ };
12217
+ }
12218
+
12219
+ // Decide vertical placement
12220
+ var spaceBelow = window.innerHeight - triggerRect.bottom;
12221
+ var spaceAbove = triggerRect.top;
12222
+ var placeBelow = spaceBelow >= popupRect.height + margin;
12223
+ if (!placeBelow && spaceAbove >= popupRect.height + margin) placeBelow = false;
12224
+ // If neither side has full space, pick the side with more space
12225
+ if (!placeBelow && spaceBelow < popupRect.height + margin && spaceAbove < popupRect.height + margin) {
12226
+ placeBelow = spaceBelow >= spaceAbove;
12227
+ }
12228
+
12229
+ // compute top coordinate (fixed positioning)
12230
+ var top;
12231
+ if (placeBelow) {
12232
+ top = Math.min(window.innerHeight - margin - popupRect.height, triggerRect.bottom + margin);
12233
+ } else {
12234
+ top = Math.max(margin, triggerRect.top - popupRect.height - margin);
12235
+ }
12236
+
12237
+ // Decide horizontal placement (try align left to trigger)
12238
+ var left = triggerRect.left;
12239
+ // If overflowing right, shift left
12240
+ if (left + popupRect.width > window.innerWidth - margin) {
12241
+ left = Math.max(margin, window.innerWidth - margin - popupRect.width);
12242
+ }
12243
+ // If overflowing left, clamp
12244
+ if (left < margin) left = margin;
12245
+ var transformOrigin = placeBelow ? "top left" : "bottom left";
12246
+ setPopupStyle({
12247
+ position: "fixed",
12248
+ top: "".concat(Math.round(top), "px"),
12249
+ left: "".concat(Math.round(left), "px"),
12250
+ zIndex: 9999,
12251
+ transformOrigin: transformOrigin
12252
+ });
12253
+ };
12254
+
12255
+ // recompute on open, on resize/scroll, and when popup content changes (currentMonth)
12256
+ useLayoutEffect(function () {
12257
+ if (!isOpen) return;
12258
+ // compute on next frame to ensure popup ref exists in DOM (especially with portal)
12259
+ computePopupPosition();
12260
+ var handleResize = function handleResize() {
12261
+ return computePopupPosition();
12262
+ };
12263
+ window.addEventListener("resize", handleResize);
12264
+ window.addEventListener("scroll", handleResize, true); // capture scrolling in ancestors
12265
+ return function () {
12266
+ window.removeEventListener("resize", handleResize);
12267
+ window.removeEventListener("scroll", handleResize, true);
12268
+ };
12269
+ }, [isOpen, currentMonth]);
12095
12270
  var calendarPopup = /*#__PURE__*/React__default.createElement("div", {
12096
- className: "absolute z-50 mt-1 w-80 rounded-md border border-gray-300 bg-white p-4 shadow-lg",
12271
+ ref: popupRef,
12272
+ className: "w-80 rounded-md border border-gray-300 bg-white p-4 shadow-lg",
12097
12273
  role: "dialog",
12098
- "aria-modal": "false"
12274
+ "aria-modal": "false",
12275
+ style: popupStyle
12099
12276
  }, header, /*#__PURE__*/React__default.createElement("div", {
12100
12277
  className: "mb-2 grid grid-cols-7 gap-1"
12101
12278
  }, Array.from({