@xsolla/xui-date-picker 0.219.0 → 0.221.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.
package/native/index.mjs CHANGED
@@ -267,11 +267,16 @@ var isWeb = false;
267
267
  var isNative = true;
268
268
 
269
269
  // src/DatePicker.tsx
270
+ import { useOverlayLayer, LAYER_OFFSET } from "@xsolla/xui-core";
270
271
  import { Input } from "@xsolla/xui-input";
271
272
  import { parse, isValid } from "date-fns";
272
273
  import { Calendar, DualCalendar } from "@xsolla/xui-calendar";
273
274
  import { Calendar as CalendarIcon } from "@xsolla/xui-icons-base";
274
275
 
276
+ // src/Portal.native.tsx
277
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
278
+ var Portal = ({ children }) => /* @__PURE__ */ jsx2(Fragment, { children });
279
+
275
280
  // src/utils.ts
276
281
  var SEGMENT_WIDTH = {
277
282
  day: 2,
@@ -521,11 +526,11 @@ function stepDateSegment(date, type, delta) {
521
526
  }
522
527
 
523
528
  // src/DatePicker.tsx
524
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
529
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
525
530
  var PLACEMENT_OFFSET = 4;
526
531
  var RANGE_SEPARATOR = /\s*[-–—]\s*/;
527
532
  var RANGE_DISPLAY_SEP = " - ";
528
- var CalendarFieldIcon = () => /* @__PURE__ */ jsx2(CalendarIcon, { size: "100%" });
533
+ var CalendarFieldIcon = () => /* @__PURE__ */ jsx3(CalendarIcon, { size: "100%" });
529
534
  var FALLBACK_PANEL_SIZE = { width: 320, height: 360 };
530
535
  var resolveAutoPlacement = (container, panel, viewport, offset) => {
531
536
  const spaceBelow = viewport.height - container.bottom;
@@ -573,15 +578,15 @@ var resolveAlignForSide = (side, container, panel, viewport, preferred) => {
573
578
  const opposite = preferred === "start" ? "end" : "start";
574
579
  return fits(opposite) ? opposite : preferred;
575
580
  };
576
- var getPlacementStyle = (side, align, offset) => {
581
+ var getFixedPlacementStyle = (side, align, container, panel, viewport, offset) => {
577
582
  if (side === "top" || side === "bottom") {
578
- const vertical2 = side === "bottom" ? { top: "100%", bottom: "auto", marginTop: offset, marginBottom: 0 } : { top: "auto", bottom: "100%", marginTop: 0, marginBottom: offset };
579
- const horizontal2 = align === "center" ? { left: "50%", right: "auto", transform: "translateX(-50%)" } : align === "end" ? { left: "auto", right: 0 } : { left: 0, right: "auto" };
580
- return { marginLeft: 0, marginRight: 0, ...vertical2, ...horizontal2 };
583
+ const vertical2 = side === "bottom" ? { top: container.bottom + offset } : { bottom: viewport.height - container.top + offset };
584
+ const horizontal2 = align === "center" ? { left: (container.left + container.right) / 2 - panel.width / 2 } : align === "end" ? { right: viewport.width - container.right } : { left: container.left };
585
+ return { position: "fixed", ...vertical2, ...horizontal2 };
581
586
  }
582
- const horizontal = side === "right" ? { left: "100%", right: "auto", marginLeft: offset, marginRight: 0 } : { left: "auto", right: "100%", marginLeft: 0, marginRight: offset };
583
- const vertical = align === "center" ? { top: "50%", bottom: "auto", transform: "translateY(-50%)" } : align === "end" ? { top: "auto", bottom: 0 } : { top: 0, bottom: "auto" };
584
- return { marginTop: 0, marginBottom: 0, ...horizontal, ...vertical };
587
+ const horizontal = side === "right" ? { left: container.right + offset } : { right: viewport.width - container.left + offset };
588
+ const vertical = align === "center" ? { top: (container.top + container.bottom) / 2 - panel.height / 2 } : align === "end" ? { bottom: viewport.height - container.bottom } : { top: container.top };
589
+ return { position: "fixed", ...horizontal, ...vertical };
585
590
  };
586
591
  var DatePicker = forwardRef(
587
592
  ({
@@ -612,7 +617,7 @@ var DatePicker = forwardRef(
612
617
  themeProductContext,
613
618
  overlayThemeMode,
614
619
  overlayThemeProductContext,
615
- iconRight = /* @__PURE__ */ jsx2(CalendarFieldIcon, {}),
620
+ iconRight = /* @__PURE__ */ jsx3(CalendarFieldIcon, {}),
616
621
  iconLeft,
617
622
  label,
618
623
  clearable = false,
@@ -646,9 +651,10 @@ var DatePicker = forwardRef(
646
651
  const [activeSegment, setActiveSegment] = useState(null);
647
652
  const [internalChip, setInternalChip] = useState(null);
648
653
  const [parseError, setParseError] = useState(null);
649
- const [layout, setLayout] = useState(
650
- () => placement ? resolveForcedLayout(placement, dropdownPosition) : { side: "bottom", align: alignFromDropdownPosition(dropdownPosition) }
654
+ const [fixedPosition, setFixedPosition] = useState(
655
+ null
651
656
  );
657
+ const overlayLayer = useOverlayLayer();
652
658
  const containerRef = useRef(null);
653
659
  const calendarRef = useRef(null);
654
660
  const inputRef = useRef(null);
@@ -1058,8 +1064,12 @@ var DatePicker = forwardRef(
1058
1064
  const handleClickOutside = (event) => {
1059
1065
  const target = event.target;
1060
1066
  const container = containerRef.current;
1067
+ const calendar = calendarRef.current;
1061
1068
  if (!target || !container) return;
1062
1069
  const path = typeof event.composedPath === "function" ? event.composedPath() : [];
1070
+ if (calendar && (path.length ? path.includes(calendar) : calendar.contains(target))) {
1071
+ return;
1072
+ }
1063
1073
  const listbox = path.length ? path.find(
1064
1074
  (el) => el instanceof Element && el.getAttribute("role") === "listbox"
1065
1075
  ) ?? null : target instanceof Element ? target.closest('[role="listbox"]') : null;
@@ -1067,7 +1077,7 @@ var DatePicker = forwardRef(
1067
1077
  const owner = document.querySelector(
1068
1078
  `[aria-controls="${listbox.id}"]`
1069
1079
  );
1070
- if (owner && container.contains(owner)) return;
1080
+ if (owner && calendar?.contains(owner)) return;
1071
1081
  }
1072
1082
  if (!container.contains(target)) {
1073
1083
  setOpen(false);
@@ -1090,15 +1100,11 @@ var DatePicker = forwardRef(
1090
1100
  document.addEventListener("keydown", handleEscape);
1091
1101
  return () => document.removeEventListener("keydown", handleEscape);
1092
1102
  }, [open]);
1103
+ useEffect(() => {
1104
+ if (!open) setFixedPosition(null);
1105
+ }, [open]);
1093
1106
  useEffect(() => {
1094
1107
  if (!open || !isWeb) return;
1095
- if (placement) {
1096
- const next = resolveForcedLayout(placement, dropdownPosition);
1097
- setLayout(
1098
- (prev) => prev.side === next.side && prev.align === next.align ? prev : next
1099
- );
1100
- return;
1101
- }
1102
1108
  const compute = () => {
1103
1109
  const container = containerRef.current;
1104
1110
  if (!container) return;
@@ -1109,30 +1115,44 @@ var DatePicker = forwardRef(
1109
1115
  width: window.innerWidth,
1110
1116
  height: window.innerHeight
1111
1117
  };
1112
- const side = resolveAutoPlacement(
1113
- containerRect,
1114
- panel,
1115
- viewport,
1116
- PLACEMENT_OFFSET
1117
- );
1118
- const preferred = side === "top" || side === "bottom" ? alignFromDropdownPosition(dropdownPosition) : "start";
1119
- const align = resolveAlignForSide(
1120
- side,
1121
- containerRect,
1122
- panel,
1123
- viewport,
1124
- preferred
1125
- );
1126
- setLayout(
1127
- (prev) => prev.side === side && prev.align === align ? prev : { side, align }
1118
+ let side;
1119
+ let align;
1120
+ if (placement) {
1121
+ ({ side, align } = resolveForcedLayout(placement, dropdownPosition));
1122
+ } else {
1123
+ side = resolveAutoPlacement(
1124
+ containerRect,
1125
+ panel,
1126
+ viewport,
1127
+ PLACEMENT_OFFSET
1128
+ );
1129
+ const preferred = side === "top" || side === "bottom" ? alignFromDropdownPosition(dropdownPosition) : "start";
1130
+ align = resolveAlignForSide(
1131
+ side,
1132
+ containerRect,
1133
+ panel,
1134
+ viewport,
1135
+ preferred
1136
+ );
1137
+ }
1138
+ setFixedPosition(
1139
+ getFixedPlacementStyle(
1140
+ side,
1141
+ align,
1142
+ containerRect,
1143
+ panel,
1144
+ viewport,
1145
+ PLACEMENT_OFFSET
1146
+ )
1128
1147
  );
1129
1148
  };
1130
- const rafId = window.requestAnimationFrame(compute);
1131
- const handleResize = () => compute();
1132
- window.addEventListener("resize", handleResize);
1149
+ compute();
1150
+ const handleReposition = () => compute();
1151
+ window.addEventListener("resize", handleReposition);
1152
+ window.addEventListener("scroll", handleReposition, true);
1133
1153
  return () => {
1134
- window.cancelAnimationFrame(rafId);
1135
- window.removeEventListener("resize", handleResize);
1154
+ window.removeEventListener("resize", handleReposition);
1155
+ window.removeEventListener("scroll", handleReposition, true);
1136
1156
  };
1137
1157
  }, [open, placement, dropdownPosition]);
1138
1158
  const overlayThemeProps = {
@@ -1147,7 +1167,7 @@ var DatePicker = forwardRef(
1147
1167
  // eslint-disable-next-line react-hooks/exhaustive-deps
1148
1168
  [viewAnchor?.getFullYear(), viewAnchor?.getMonth()]
1149
1169
  );
1150
- const renderCalendar = () => isDual ? /* @__PURE__ */ jsx2(
1170
+ const renderCalendar = () => isDual ? /* @__PURE__ */ jsx3(
1151
1171
  DualCalendar,
1152
1172
  {
1153
1173
  locale,
@@ -1165,7 +1185,7 @@ var DatePicker = forwardRef(
1165
1185
  ...rest,
1166
1186
  ...overlayThemeProps
1167
1187
  }
1168
- ) : /* @__PURE__ */ jsx2(
1188
+ ) : /* @__PURE__ */ jsx3(
1169
1189
  Calendar,
1170
1190
  {
1171
1191
  locale,
@@ -1198,7 +1218,7 @@ var DatePicker = forwardRef(
1198
1218
  width: "100%",
1199
1219
  ...isWeb ? { onClick: () => setOpen(true) } : {},
1200
1220
  children: [
1201
- /* @__PURE__ */ jsx2(
1221
+ /* @__PURE__ */ jsx3(
1202
1222
  Input,
1203
1223
  {
1204
1224
  ...rest,
@@ -1225,7 +1245,7 @@ var DatePicker = forwardRef(
1225
1245
  themeProductContext
1226
1246
  }
1227
1247
  ),
1228
- open && (isWeb ? /* @__PURE__ */ jsx2(
1248
+ open && (isWeb ? /* @__PURE__ */ jsx3(Portal, { children: /* @__PURE__ */ jsx3(
1229
1249
  Box,
1230
1250
  {
1231
1251
  ref: calendarRef,
@@ -1233,16 +1253,15 @@ var DatePicker = forwardRef(
1233
1253
  "aria-modal": true,
1234
1254
  "aria-label": isRange ? "Choose a date range" : "Choose a date",
1235
1255
  onClick: (e) => e.stopPropagation(),
1236
- position: "absolute",
1237
- zIndex: 1e3,
1238
- style: getPlacementStyle(
1239
- layout.side,
1240
- layout.align,
1241
- PLACEMENT_OFFSET
1242
- ),
1256
+ zIndex: overlayLayer + LAYER_OFFSET.popover,
1257
+ style: {
1258
+ position: "fixed",
1259
+ visibility: fixedPosition ? "visible" : "hidden",
1260
+ ...fixedPosition
1261
+ },
1243
1262
  children: renderCalendar()
1244
1263
  }
1245
- ) : /* @__PURE__ */ jsx2(Box, { marginTop: 4, children: renderCalendar() }))
1264
+ ) }) : /* @__PURE__ */ jsx3(Box, { marginTop: 4, children: renderCalendar() }))
1246
1265
  ]
1247
1266
  }
1248
1267
  );