@x-plat/design-system 0.14.18 → 0.15.0

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.
@@ -36,13 +36,13 @@ __export(Select_exports, {
36
36
  module.exports = __toCommonJS(Select_exports);
37
37
 
38
38
  // src/components/Select/Select.tsx
39
- var import_react6 = __toESM(require("react"), 1);
39
+ var import_react7 = __toESM(require("react"), 1);
40
40
 
41
41
  // src/tokens/hooks/Portal.tsx
42
42
  var import_react_dom = __toESM(require("react-dom"), 1);
43
- var Portal = ({ children }) => {
43
+ var Portal = ({ children, container }) => {
44
44
  if (typeof document === "undefined") return null;
45
- return import_react_dom.default.createPortal(children, document.body);
45
+ return import_react_dom.default.createPortal(children, container ?? document.body);
46
46
  };
47
47
  Portal.displayName = "Portal";
48
48
  var Portal_default = Portal;
@@ -105,10 +105,16 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
105
105
  const scrollParentRef = import_react.default.useRef(null);
106
106
  const placedRef = import_react.default.useRef(false);
107
107
  const frozenMaxRef = import_react.default.useRef(void 0);
108
+ const cssCapRef = import_react.default.useRef(void 0);
108
109
  const calculatePosition = import_react.default.useCallback((remeasure = false) => {
109
110
  const pop = popRef.current;
110
111
  if (!triggerRef.current || !pop) return;
111
112
  if (remeasure) frozenMaxRef.current = void 0;
113
+ if (cssCapRef.current === void 0) {
114
+ pop.style.maxHeight = "";
115
+ const raw = getComputedStyle(pop).maxHeight;
116
+ cssCapRef.current = raw === "none" ? null : Number.parseFloat(raw) || null;
117
+ }
112
118
  if (remeasure || !sizeRef.current) {
113
119
  sizeRef.current = {
114
120
  w: pop.offsetWidth,
@@ -130,8 +136,10 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
130
136
  };
131
137
  const first = !placedRef.current;
132
138
  if (first) pop.style.transition = "none";
139
+ const cap = cssCapRef.current;
140
+ const desiredH = cap == null ? popH : Math.min(popH, cap);
133
141
  const frozen = frozenMaxRef.current;
134
- const effectiveH = frozen == null ? popH : Math.min(popH, frozen);
142
+ const effectiveH = frozen == null ? desiredH : Math.min(desiredH, frozen);
135
143
  const { direction: next, top, left, over, maxHeight: suggested } = computePlacement(
136
144
  triggerRect,
137
145
  popW,
@@ -140,8 +148,9 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
140
148
  );
141
149
  if (frozenMaxRef.current === void 0) frozenMaxRef.current = suggested;
142
150
  const maxHeight = frozenMaxRef.current;
143
- pop.style.top = `${top + window.scrollY}px`;
144
- pop.style.left = `${left + window.scrollX}px`;
151
+ const hostRect = (pop.offsetParent ?? pop.parentElement)?.getBoundingClientRect();
152
+ pop.style.top = `${top - (hostRect?.top ?? 0)}px`;
153
+ pop.style.left = `${left - (hostRect?.left ?? 0)}px`;
145
154
  pop.style.maxHeight = maxHeight === null ? "" : `${maxHeight}px`;
146
155
  pop.style.clipPath = over.top || over.right || over.bottom || over.left ? `inset(${over.top}px ${over.right}px ${over.bottom}px ${over.left}px)` : "none";
147
156
  directionRef.current = next;
@@ -159,6 +168,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
159
168
  scrollParentRef.current = null;
160
169
  placedRef.current = false;
161
170
  frozenMaxRef.current = void 0;
171
+ cssCapRef.current = void 0;
162
172
  return;
163
173
  }
164
174
  if (triggerRef.current) {
@@ -215,8 +225,34 @@ var useClickOutside = (refs, handler, enabled = true) => {
215
225
  };
216
226
  var useClickOutside_default = useClickOutside;
217
227
 
218
- // src/tokens/hooks/useOverlayKeyboard.ts
228
+ // src/tokens/hooks/useOverlayHost.ts
219
229
  var import_react3 = __toESM(require("react"), 1);
230
+ var useOverlayHost = (triggerRef, enabled) => {
231
+ const [host, setHost] = import_react3.default.useState(null);
232
+ import_react3.default.useLayoutEffect(() => {
233
+ if (!enabled || !triggerRef.current) {
234
+ setHost(null);
235
+ return;
236
+ }
237
+ const el = document.createElement("div");
238
+ el.dataset.xplatOverlayHost = "";
239
+ el.style.position = "relative";
240
+ el.style.width = "0";
241
+ el.style.height = "0";
242
+ const parent = findScrollParent(triggerRef.current) ?? document.body;
243
+ parent.appendChild(el);
244
+ setHost(el);
245
+ return () => {
246
+ el.remove();
247
+ setHost(null);
248
+ };
249
+ }, [enabled, triggerRef]);
250
+ return host;
251
+ };
252
+ var useOverlayHost_default = useOverlayHost;
253
+
254
+ // src/tokens/hooks/useOverlayKeyboard.ts
255
+ var import_react4 = __toESM(require("react"), 1);
220
256
  var FOCUSABLE = [
221
257
  "a[href]",
222
258
  "button:not([disabled])",
@@ -226,7 +262,7 @@ var FOCUSABLE = [
226
262
  "[tabindex]:not([tabindex='-1'])"
227
263
  ].join(",");
228
264
  var useEscapeKey = (enabled, onEscape) => {
229
- import_react3.default.useEffect(() => {
265
+ import_react4.default.useEffect(() => {
230
266
  if (!enabled) return;
231
267
  const onKey = (e) => {
232
268
  if (e.key !== "Escape") return;
@@ -239,9 +275,9 @@ var useEscapeKey = (enabled, onEscape) => {
239
275
  }, [enabled, onEscape]);
240
276
  };
241
277
  var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
242
- const onCloseRef = import_react3.default.useRef(onClose);
278
+ const onCloseRef = import_react4.default.useRef(onClose);
243
279
  onCloseRef.current = onClose;
244
- import_react3.default.useEffect(() => {
280
+ import_react4.default.useEffect(() => {
245
281
  const trigger = triggerRef.current;
246
282
  if (!enabled || !trigger) return;
247
283
  const observer = new IntersectionObserver(
@@ -1305,16 +1341,16 @@ Field.displayName = "Field";
1305
1341
  var hasError = (validations) => !!validations?.some((v) => v.status === "error");
1306
1342
 
1307
1343
  // src/components/Select/context.ts
1308
- var import_react4 = __toESM(require("react"), 1);
1309
- var SelectContext = import_react4.default.createContext(null);
1344
+ var import_react5 = __toESM(require("react"), 1);
1345
+ var SelectContext = import_react5.default.createContext(null);
1310
1346
  var context_default = SelectContext;
1311
1347
 
1312
1348
  // src/components/Select/SelectItem.tsx
1313
- var import_react5 = __toESM(require("react"), 1);
1349
+ var import_react6 = __toESM(require("react"), 1);
1314
1350
  var import_jsx_runtime296 = require("react/jsx-runtime");
1315
1351
  var SelectItem = (props) => {
1316
1352
  const { children, value, onClick, disabled = false } = props;
1317
- const ctx = import_react5.default.useContext(context_default);
1353
+ const ctx = import_react6.default.useContext(context_default);
1318
1354
  const handleClick = (e) => {
1319
1355
  e.preventDefault();
1320
1356
  e.stopPropagation();
@@ -1364,29 +1400,29 @@ var SelectRoot = (props) => {
1364
1400
  required,
1365
1401
  size = "md"
1366
1402
  } = props;
1367
- const fieldId = import_react6.default.useId();
1368
- const itemChildren = import_react6.default.Children.toArray(children).filter(
1369
- (child) => import_react6.default.isValidElement(child) && child.type === SelectItem_default
1403
+ const fieldId = import_react7.default.useId();
1404
+ const itemChildren = import_react7.default.Children.toArray(children).filter(
1405
+ (child) => import_react7.default.isValidElement(child) && child.type === SelectItem_default
1370
1406
  );
1371
1407
  const isControlled = valueProp !== void 0;
1372
- const [isOpen, setOpen] = import_react6.default.useState(false);
1373
- const [uncontrolledLabel, setUncontrolledLabel] = import_react6.default.useState(null);
1374
- const [uncontrolledValue, setUncontrolledValue] = import_react6.default.useState(void 0);
1375
- const controlledLabel = import_react6.default.useMemo(() => {
1408
+ const [isOpen, setOpen] = import_react7.default.useState(false);
1409
+ const [uncontrolledLabel, setUncontrolledLabel] = import_react7.default.useState(null);
1410
+ const [uncontrolledValue, setUncontrolledValue] = import_react7.default.useState(void 0);
1411
+ const controlledLabel = import_react7.default.useMemo(() => {
1376
1412
  if (!isControlled) return null;
1377
1413
  const match = itemChildren.find((child) => child.props.value === valueProp);
1378
1414
  return match ? match.props.children : null;
1379
1415
  }, [isControlled, valueProp, itemChildren]);
1380
1416
  const selectedLabel = isControlled ? controlledLabel : uncontrolledLabel;
1381
1417
  const selectedValue = isControlled ? valueProp : uncontrolledValue;
1382
- const triggerRef = import_react6.default.useRef(null);
1383
- const contentRef = import_react6.default.useRef(null);
1384
- const [mounted, setMounted] = import_react6.default.useState(false);
1385
- const [visible, setVisible] = import_react6.default.useState(false);
1386
- import_react6.default.useEffect(() => {
1418
+ const triggerRef = import_react7.default.useRef(null);
1419
+ const contentRef = import_react7.default.useRef(null);
1420
+ const [mounted, setMounted] = import_react7.default.useState(false);
1421
+ const [visible, setVisible] = import_react7.default.useState(false);
1422
+ import_react7.default.useEffect(() => {
1387
1423
  if (disabled && isOpen) setOpen(false);
1388
1424
  }, [disabled, isOpen]);
1389
- import_react6.default.useEffect(() => {
1425
+ import_react7.default.useEffect(() => {
1390
1426
  if (isOpen) {
1391
1427
  setMounted(true);
1392
1428
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -1396,33 +1432,34 @@ var SelectRoot = (props) => {
1396
1432
  const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS);
1397
1433
  return () => clearTimeout(t);
1398
1434
  }, [isOpen]);
1399
- const [query, setQuery] = import_react6.default.useState("");
1435
+ const [query, setQuery] = import_react7.default.useState("");
1400
1436
  const textOf = (node) => {
1401
1437
  if (node === null || node === void 0 || typeof node === "boolean") return "";
1402
1438
  if (typeof node === "string" || typeof node === "number") return String(node);
1403
1439
  if (Array.isArray(node)) return node.map(textOf).join("");
1404
- if (import_react6.default.isValidElement(node)) return textOf(node.props.children);
1440
+ if (import_react7.default.isValidElement(node)) return textOf(node.props.children);
1405
1441
  return "";
1406
1442
  };
1407
- const visibleItems = import_react6.default.useMemo(() => {
1443
+ const visibleItems = import_react7.default.useMemo(() => {
1408
1444
  if (!searchable || !query.trim()) return itemChildren;
1409
1445
  const q = query.trim().toLowerCase();
1410
1446
  return itemChildren.filter((c) => textOf(c.props.children).toLowerCase().includes(q));
1411
1447
  }, [searchable, query, itemChildren]);
1412
- const open = import_react6.default.useCallback(() => setOpen(true), []);
1413
- const close = import_react6.default.useCallback(() => {
1448
+ const open = import_react7.default.useCallback(() => setOpen(true), []);
1449
+ const close = import_react7.default.useCallback(() => {
1414
1450
  setOpen(false);
1415
1451
  setQuery("");
1416
1452
  }, []);
1417
- const toggle = import_react6.default.useCallback(() => setOpen((prev) => !prev), []);
1453
+ const toggle = import_react7.default.useCallback(() => setOpen((prev) => !prev), []);
1418
1454
  useClickOutside_default([contentRef, triggerRef], close, isOpen);
1419
1455
  useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
1420
1456
  useEscapeKey(isOpen, () => {
1421
1457
  close();
1422
1458
  triggerRef.current?.focus({ preventScroll: true });
1423
1459
  });
1460
+ const host = useOverlayHost_default(triggerRef, mounted);
1424
1461
  useAutoPosition_default(triggerRef, contentRef, mounted);
1425
- const setSelected = import_react6.default.useCallback(
1462
+ const setSelected = import_react7.default.useCallback(
1426
1463
  (label2, itemValue) => {
1427
1464
  if (!isControlled) {
1428
1465
  setUncontrolledLabel(label2);
@@ -1432,7 +1469,7 @@ var SelectRoot = (props) => {
1432
1469
  },
1433
1470
  [isControlled, onChange]
1434
1471
  );
1435
- const ctxValue = import_react6.default.useMemo(
1472
+ const ctxValue = import_react7.default.useMemo(
1436
1473
  () => ({
1437
1474
  isOpen,
1438
1475
  mounted,
@@ -1511,7 +1548,7 @@ var SelectRoot = (props) => {
1511
1548
  ]
1512
1549
  }
1513
1550
  ),
1514
- mounted && /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime297.jsxs)(
1551
+ mounted && /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(Portal_default, { container: host, children: /* @__PURE__ */ (0, import_jsx_runtime297.jsxs)(
1515
1552
  "div",
1516
1553
  {
1517
1554
  className: clsx_default("lib-xplat-select-content", size, stateClass),
@@ -1,11 +1,11 @@
1
1
  // src/components/Select/Select.tsx
2
- import React6 from "react";
2
+ import React7 from "react";
3
3
 
4
4
  // src/tokens/hooks/Portal.tsx
5
5
  import ReactDOM from "react-dom";
6
- var Portal = ({ children }) => {
6
+ var Portal = ({ children, container }) => {
7
7
  if (typeof document === "undefined") return null;
8
- return ReactDOM.createPortal(children, document.body);
8
+ return ReactDOM.createPortal(children, container ?? document.body);
9
9
  };
10
10
  Portal.displayName = "Portal";
11
11
  var Portal_default = Portal;
@@ -68,10 +68,16 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
68
68
  const scrollParentRef = React.useRef(null);
69
69
  const placedRef = React.useRef(false);
70
70
  const frozenMaxRef = React.useRef(void 0);
71
+ const cssCapRef = React.useRef(void 0);
71
72
  const calculatePosition = React.useCallback((remeasure = false) => {
72
73
  const pop = popRef.current;
73
74
  if (!triggerRef.current || !pop) return;
74
75
  if (remeasure) frozenMaxRef.current = void 0;
76
+ if (cssCapRef.current === void 0) {
77
+ pop.style.maxHeight = "";
78
+ const raw = getComputedStyle(pop).maxHeight;
79
+ cssCapRef.current = raw === "none" ? null : Number.parseFloat(raw) || null;
80
+ }
75
81
  if (remeasure || !sizeRef.current) {
76
82
  sizeRef.current = {
77
83
  w: pop.offsetWidth,
@@ -93,8 +99,10 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
93
99
  };
94
100
  const first = !placedRef.current;
95
101
  if (first) pop.style.transition = "none";
102
+ const cap = cssCapRef.current;
103
+ const desiredH = cap == null ? popH : Math.min(popH, cap);
96
104
  const frozen = frozenMaxRef.current;
97
- const effectiveH = frozen == null ? popH : Math.min(popH, frozen);
105
+ const effectiveH = frozen == null ? desiredH : Math.min(desiredH, frozen);
98
106
  const { direction: next, top, left, over, maxHeight: suggested } = computePlacement(
99
107
  triggerRect,
100
108
  popW,
@@ -103,8 +111,9 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
103
111
  );
104
112
  if (frozenMaxRef.current === void 0) frozenMaxRef.current = suggested;
105
113
  const maxHeight = frozenMaxRef.current;
106
- pop.style.top = `${top + window.scrollY}px`;
107
- pop.style.left = `${left + window.scrollX}px`;
114
+ const hostRect = (pop.offsetParent ?? pop.parentElement)?.getBoundingClientRect();
115
+ pop.style.top = `${top - (hostRect?.top ?? 0)}px`;
116
+ pop.style.left = `${left - (hostRect?.left ?? 0)}px`;
108
117
  pop.style.maxHeight = maxHeight === null ? "" : `${maxHeight}px`;
109
118
  pop.style.clipPath = over.top || over.right || over.bottom || over.left ? `inset(${over.top}px ${over.right}px ${over.bottom}px ${over.left}px)` : "none";
110
119
  directionRef.current = next;
@@ -122,6 +131,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
122
131
  scrollParentRef.current = null;
123
132
  placedRef.current = false;
124
133
  frozenMaxRef.current = void 0;
134
+ cssCapRef.current = void 0;
125
135
  return;
126
136
  }
127
137
  if (triggerRef.current) {
@@ -178,8 +188,34 @@ var useClickOutside = (refs, handler, enabled = true) => {
178
188
  };
179
189
  var useClickOutside_default = useClickOutside;
180
190
 
181
- // src/tokens/hooks/useOverlayKeyboard.ts
191
+ // src/tokens/hooks/useOverlayHost.ts
182
192
  import React3 from "react";
193
+ var useOverlayHost = (triggerRef, enabled) => {
194
+ const [host, setHost] = React3.useState(null);
195
+ React3.useLayoutEffect(() => {
196
+ if (!enabled || !triggerRef.current) {
197
+ setHost(null);
198
+ return;
199
+ }
200
+ const el = document.createElement("div");
201
+ el.dataset.xplatOverlayHost = "";
202
+ el.style.position = "relative";
203
+ el.style.width = "0";
204
+ el.style.height = "0";
205
+ const parent = findScrollParent(triggerRef.current) ?? document.body;
206
+ parent.appendChild(el);
207
+ setHost(el);
208
+ return () => {
209
+ el.remove();
210
+ setHost(null);
211
+ };
212
+ }, [enabled, triggerRef]);
213
+ return host;
214
+ };
215
+ var useOverlayHost_default = useOverlayHost;
216
+
217
+ // src/tokens/hooks/useOverlayKeyboard.ts
218
+ import React4 from "react";
183
219
  var FOCUSABLE = [
184
220
  "a[href]",
185
221
  "button:not([disabled])",
@@ -189,7 +225,7 @@ var FOCUSABLE = [
189
225
  "[tabindex]:not([tabindex='-1'])"
190
226
  ].join(",");
191
227
  var useEscapeKey = (enabled, onEscape) => {
192
- React3.useEffect(() => {
228
+ React4.useEffect(() => {
193
229
  if (!enabled) return;
194
230
  const onKey = (e) => {
195
231
  if (e.key !== "Escape") return;
@@ -202,9 +238,9 @@ var useEscapeKey = (enabled, onEscape) => {
202
238
  }, [enabled, onEscape]);
203
239
  };
204
240
  var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
205
- const onCloseRef = React3.useRef(onClose);
241
+ const onCloseRef = React4.useRef(onClose);
206
242
  onCloseRef.current = onClose;
207
- React3.useEffect(() => {
243
+ React4.useEffect(() => {
208
244
  const trigger = triggerRef.current;
209
245
  if (!enabled || !trigger) return;
210
246
  const observer = new IntersectionObserver(
@@ -1268,16 +1304,16 @@ Field.displayName = "Field";
1268
1304
  var hasError = (validations) => !!validations?.some((v) => v.status === "error");
1269
1305
 
1270
1306
  // src/components/Select/context.ts
1271
- import React4 from "react";
1272
- var SelectContext = React4.createContext(null);
1307
+ import React5 from "react";
1308
+ var SelectContext = React5.createContext(null);
1273
1309
  var context_default = SelectContext;
1274
1310
 
1275
1311
  // src/components/Select/SelectItem.tsx
1276
- import React5 from "react";
1312
+ import React6 from "react";
1277
1313
  import { jsx as jsx296 } from "react/jsx-runtime";
1278
1314
  var SelectItem = (props) => {
1279
1315
  const { children, value, onClick, disabled = false } = props;
1280
- const ctx = React5.useContext(context_default);
1316
+ const ctx = React6.useContext(context_default);
1281
1317
  const handleClick = (e) => {
1282
1318
  e.preventDefault();
1283
1319
  e.stopPropagation();
@@ -1327,29 +1363,29 @@ var SelectRoot = (props) => {
1327
1363
  required,
1328
1364
  size = "md"
1329
1365
  } = props;
1330
- const fieldId = React6.useId();
1331
- const itemChildren = React6.Children.toArray(children).filter(
1332
- (child) => React6.isValidElement(child) && child.type === SelectItem_default
1366
+ const fieldId = React7.useId();
1367
+ const itemChildren = React7.Children.toArray(children).filter(
1368
+ (child) => React7.isValidElement(child) && child.type === SelectItem_default
1333
1369
  );
1334
1370
  const isControlled = valueProp !== void 0;
1335
- const [isOpen, setOpen] = React6.useState(false);
1336
- const [uncontrolledLabel, setUncontrolledLabel] = React6.useState(null);
1337
- const [uncontrolledValue, setUncontrolledValue] = React6.useState(void 0);
1338
- const controlledLabel = React6.useMemo(() => {
1371
+ const [isOpen, setOpen] = React7.useState(false);
1372
+ const [uncontrolledLabel, setUncontrolledLabel] = React7.useState(null);
1373
+ const [uncontrolledValue, setUncontrolledValue] = React7.useState(void 0);
1374
+ const controlledLabel = React7.useMemo(() => {
1339
1375
  if (!isControlled) return null;
1340
1376
  const match = itemChildren.find((child) => child.props.value === valueProp);
1341
1377
  return match ? match.props.children : null;
1342
1378
  }, [isControlled, valueProp, itemChildren]);
1343
1379
  const selectedLabel = isControlled ? controlledLabel : uncontrolledLabel;
1344
1380
  const selectedValue = isControlled ? valueProp : uncontrolledValue;
1345
- const triggerRef = React6.useRef(null);
1346
- const contentRef = React6.useRef(null);
1347
- const [mounted, setMounted] = React6.useState(false);
1348
- const [visible, setVisible] = React6.useState(false);
1349
- React6.useEffect(() => {
1381
+ const triggerRef = React7.useRef(null);
1382
+ const contentRef = React7.useRef(null);
1383
+ const [mounted, setMounted] = React7.useState(false);
1384
+ const [visible, setVisible] = React7.useState(false);
1385
+ React7.useEffect(() => {
1350
1386
  if (disabled && isOpen) setOpen(false);
1351
1387
  }, [disabled, isOpen]);
1352
- React6.useEffect(() => {
1388
+ React7.useEffect(() => {
1353
1389
  if (isOpen) {
1354
1390
  setMounted(true);
1355
1391
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -1359,33 +1395,34 @@ var SelectRoot = (props) => {
1359
1395
  const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS);
1360
1396
  return () => clearTimeout(t);
1361
1397
  }, [isOpen]);
1362
- const [query, setQuery] = React6.useState("");
1398
+ const [query, setQuery] = React7.useState("");
1363
1399
  const textOf = (node) => {
1364
1400
  if (node === null || node === void 0 || typeof node === "boolean") return "";
1365
1401
  if (typeof node === "string" || typeof node === "number") return String(node);
1366
1402
  if (Array.isArray(node)) return node.map(textOf).join("");
1367
- if (React6.isValidElement(node)) return textOf(node.props.children);
1403
+ if (React7.isValidElement(node)) return textOf(node.props.children);
1368
1404
  return "";
1369
1405
  };
1370
- const visibleItems = React6.useMemo(() => {
1406
+ const visibleItems = React7.useMemo(() => {
1371
1407
  if (!searchable || !query.trim()) return itemChildren;
1372
1408
  const q = query.trim().toLowerCase();
1373
1409
  return itemChildren.filter((c) => textOf(c.props.children).toLowerCase().includes(q));
1374
1410
  }, [searchable, query, itemChildren]);
1375
- const open = React6.useCallback(() => setOpen(true), []);
1376
- const close = React6.useCallback(() => {
1411
+ const open = React7.useCallback(() => setOpen(true), []);
1412
+ const close = React7.useCallback(() => {
1377
1413
  setOpen(false);
1378
1414
  setQuery("");
1379
1415
  }, []);
1380
- const toggle = React6.useCallback(() => setOpen((prev) => !prev), []);
1416
+ const toggle = React7.useCallback(() => setOpen((prev) => !prev), []);
1381
1417
  useClickOutside_default([contentRef, triggerRef], close, isOpen);
1382
1418
  useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
1383
1419
  useEscapeKey(isOpen, () => {
1384
1420
  close();
1385
1421
  triggerRef.current?.focus({ preventScroll: true });
1386
1422
  });
1423
+ const host = useOverlayHost_default(triggerRef, mounted);
1387
1424
  useAutoPosition_default(triggerRef, contentRef, mounted);
1388
- const setSelected = React6.useCallback(
1425
+ const setSelected = React7.useCallback(
1389
1426
  (label2, itemValue) => {
1390
1427
  if (!isControlled) {
1391
1428
  setUncontrolledLabel(label2);
@@ -1395,7 +1432,7 @@ var SelectRoot = (props) => {
1395
1432
  },
1396
1433
  [isControlled, onChange]
1397
1434
  );
1398
- const ctxValue = React6.useMemo(
1435
+ const ctxValue = React7.useMemo(
1399
1436
  () => ({
1400
1437
  isOpen,
1401
1438
  mounted,
@@ -1474,7 +1511,7 @@ var SelectRoot = (props) => {
1474
1511
  ]
1475
1512
  }
1476
1513
  ),
1477
- mounted && /* @__PURE__ */ jsx297(Portal_default, { children: /* @__PURE__ */ jsxs190(
1514
+ mounted && /* @__PURE__ */ jsx297(Portal_default, { container: host, children: /* @__PURE__ */ jsxs190(
1478
1515
  "div",
1479
1516
  {
1480
1517
  className: clsx_default("lib-xplat-select-content", size, stateClass),
@@ -35,13 +35,13 @@ __export(TimePicker_exports, {
35
35
  module.exports = __toCommonJS(TimePicker_exports);
36
36
 
37
37
  // src/components/TimePicker/TimePicker.tsx
38
- var import_react4 = __toESM(require("react"), 1);
38
+ var import_react5 = __toESM(require("react"), 1);
39
39
 
40
40
  // src/tokens/hooks/Portal.tsx
41
41
  var import_react_dom = __toESM(require("react-dom"), 1);
42
- var Portal = ({ children }) => {
42
+ var Portal = ({ children, container }) => {
43
43
  if (typeof document === "undefined") return null;
44
- return import_react_dom.default.createPortal(children, document.body);
44
+ return import_react_dom.default.createPortal(children, container ?? document.body);
45
45
  };
46
46
  Portal.displayName = "Portal";
47
47
  var Portal_default = Portal;
@@ -104,10 +104,16 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
104
104
  const scrollParentRef = import_react.default.useRef(null);
105
105
  const placedRef = import_react.default.useRef(false);
106
106
  const frozenMaxRef = import_react.default.useRef(void 0);
107
+ const cssCapRef = import_react.default.useRef(void 0);
107
108
  const calculatePosition = import_react.default.useCallback((remeasure = false) => {
108
109
  const pop = popRef.current;
109
110
  if (!triggerRef.current || !pop) return;
110
111
  if (remeasure) frozenMaxRef.current = void 0;
112
+ if (cssCapRef.current === void 0) {
113
+ pop.style.maxHeight = "";
114
+ const raw = getComputedStyle(pop).maxHeight;
115
+ cssCapRef.current = raw === "none" ? null : Number.parseFloat(raw) || null;
116
+ }
111
117
  if (remeasure || !sizeRef.current) {
112
118
  sizeRef.current = {
113
119
  w: pop.offsetWidth,
@@ -129,8 +135,10 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
129
135
  };
130
136
  const first = !placedRef.current;
131
137
  if (first) pop.style.transition = "none";
138
+ const cap = cssCapRef.current;
139
+ const desiredH = cap == null ? popH : Math.min(popH, cap);
132
140
  const frozen = frozenMaxRef.current;
133
- const effectiveH = frozen == null ? popH : Math.min(popH, frozen);
141
+ const effectiveH = frozen == null ? desiredH : Math.min(desiredH, frozen);
134
142
  const { direction: next, top, left, over, maxHeight: suggested } = computePlacement(
135
143
  triggerRect,
136
144
  popW,
@@ -139,8 +147,9 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
139
147
  );
140
148
  if (frozenMaxRef.current === void 0) frozenMaxRef.current = suggested;
141
149
  const maxHeight = frozenMaxRef.current;
142
- pop.style.top = `${top + window.scrollY}px`;
143
- pop.style.left = `${left + window.scrollX}px`;
150
+ const hostRect = (pop.offsetParent ?? pop.parentElement)?.getBoundingClientRect();
151
+ pop.style.top = `${top - (hostRect?.top ?? 0)}px`;
152
+ pop.style.left = `${left - (hostRect?.left ?? 0)}px`;
144
153
  pop.style.maxHeight = maxHeight === null ? "" : `${maxHeight}px`;
145
154
  pop.style.clipPath = over.top || over.right || over.bottom || over.left ? `inset(${over.top}px ${over.right}px ${over.bottom}px ${over.left}px)` : "none";
146
155
  directionRef.current = next;
@@ -158,6 +167,7 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
158
167
  scrollParentRef.current = null;
159
168
  placedRef.current = false;
160
169
  frozenMaxRef.current = void 0;
170
+ cssCapRef.current = void 0;
161
171
  return;
162
172
  }
163
173
  if (triggerRef.current) {
@@ -214,8 +224,34 @@ var useClickOutside = (refs, handler, enabled = true) => {
214
224
  };
215
225
  var useClickOutside_default = useClickOutside;
216
226
 
217
- // src/tokens/hooks/useOverlayKeyboard.ts
227
+ // src/tokens/hooks/useOverlayHost.ts
218
228
  var import_react3 = __toESM(require("react"), 1);
229
+ var useOverlayHost = (triggerRef, enabled) => {
230
+ const [host, setHost] = import_react3.default.useState(null);
231
+ import_react3.default.useLayoutEffect(() => {
232
+ if (!enabled || !triggerRef.current) {
233
+ setHost(null);
234
+ return;
235
+ }
236
+ const el = document.createElement("div");
237
+ el.dataset.xplatOverlayHost = "";
238
+ el.style.position = "relative";
239
+ el.style.width = "0";
240
+ el.style.height = "0";
241
+ const parent = findScrollParent(triggerRef.current) ?? document.body;
242
+ parent.appendChild(el);
243
+ setHost(el);
244
+ return () => {
245
+ el.remove();
246
+ setHost(null);
247
+ };
248
+ }, [enabled, triggerRef]);
249
+ return host;
250
+ };
251
+ var useOverlayHost_default = useOverlayHost;
252
+
253
+ // src/tokens/hooks/useOverlayKeyboard.ts
254
+ var import_react4 = __toESM(require("react"), 1);
219
255
  var FOCUSABLE = [
220
256
  "a[href]",
221
257
  "button:not([disabled])",
@@ -225,7 +261,7 @@ var FOCUSABLE = [
225
261
  "[tabindex]:not([tabindex='-1'])"
226
262
  ].join(",");
227
263
  var useEscapeKey = (enabled, onEscape) => {
228
- import_react3.default.useEffect(() => {
264
+ import_react4.default.useEffect(() => {
229
265
  if (!enabled) return;
230
266
  const onKey = (e) => {
231
267
  if (e.key !== "Escape") return;
@@ -238,9 +274,9 @@ var useEscapeKey = (enabled, onEscape) => {
238
274
  }, [enabled, onEscape]);
239
275
  };
240
276
  var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
241
- const onCloseRef = import_react3.default.useRef(onClose);
277
+ const onCloseRef = import_react4.default.useRef(onClose);
242
278
  onCloseRef.current = onClose;
243
- import_react3.default.useEffect(() => {
279
+ import_react4.default.useEffect(() => {
244
280
  const trigger = triggerRef.current;
245
281
  if (!enabled || !trigger) return;
246
282
  const observer = new IntersectionObserver(
@@ -450,13 +486,13 @@ var TimePicker = (props) => {
450
486
  required,
451
487
  validations
452
488
  } = props;
453
- const fieldId = import_react4.default.useId();
454
- const triggerRef = import_react4.default.useRef(null);
455
- const contentRef = import_react4.default.useRef(null);
456
- const [isOpen, setOpen] = import_react4.default.useState(false);
457
- const [mounted, setMounted] = import_react4.default.useState(false);
458
- const [visible, setVisible] = import_react4.default.useState(false);
459
- const options = import_react4.default.useMemo(() => {
489
+ const fieldId = import_react5.default.useId();
490
+ const triggerRef = import_react5.default.useRef(null);
491
+ const contentRef = import_react5.default.useRef(null);
492
+ const [isOpen, setOpen] = import_react5.default.useState(false);
493
+ const [mounted, setMounted] = import_react5.default.useState(false);
494
+ const [visible, setVisible] = import_react5.default.useState(false);
495
+ const options = import_react5.default.useMemo(() => {
460
496
  const safeStep = step > 0 && step <= 720 ? step : 30;
461
497
  const lo = toMinutes(min) ?? 0;
462
498
  const hi = toMinutes(max) ?? 24 * 60 - 1;
@@ -464,18 +500,19 @@ var TimePicker = (props) => {
464
500
  for (let t = lo; t <= hi; t += safeStep) out.push(toHHmm(t));
465
501
  return out;
466
502
  }, [step, min, max]);
467
- const close = import_react4.default.useCallback(() => setOpen(false), []);
503
+ const close = import_react5.default.useCallback(() => setOpen(false), []);
468
504
  useClickOutside_default([triggerRef, contentRef], close, isOpen);
469
505
  useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
470
506
  useEscapeKey(isOpen, () => {
471
507
  close();
472
508
  triggerRef.current?.focus({ preventScroll: true });
473
509
  });
510
+ const host = useOverlayHost_default(triggerRef, mounted);
474
511
  useAutoPosition_default(triggerRef, contentRef, mounted);
475
- import_react4.default.useEffect(() => {
512
+ import_react5.default.useEffect(() => {
476
513
  if (disabled && isOpen) setOpen(false);
477
514
  }, [disabled, isOpen]);
478
- import_react4.default.useEffect(() => {
515
+ import_react5.default.useEffect(() => {
479
516
  if (isOpen) {
480
517
  setMounted(true);
481
518
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -513,7 +550,7 @@ var TimePicker = (props) => {
513
550
  children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: clsx_default("time-picker-value", !value && "placeholder"), children: value ? display(value, use12Hours) : placeholder })
514
551
  }
515
552
  ),
516
- mounted && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
553
+ mounted && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Portal_default, { container: host, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
517
554
  "div",
518
555
  {
519
556
  ref: contentRef,