@xsolla/xui-date-picker 0.220.0 → 0.221.1

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.js CHANGED
@@ -294,11 +294,16 @@ var isWeb = false;
294
294
  var isNative = true;
295
295
 
296
296
  // src/DatePicker.tsx
297
+ var import_xui_core = require("@xsolla/xui-core");
297
298
  var import_xui_input = require("@xsolla/xui-input");
298
299
  var import_date_fns = require("date-fns");
299
300
  var import_xui_calendar = require("@xsolla/xui-calendar");
300
301
  var import_xui_icons_base = require("@xsolla/xui-icons-base");
301
302
 
303
+ // src/Portal.native.tsx
304
+ var import_jsx_runtime2 = require("react/jsx-runtime");
305
+ var Portal = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
306
+
302
307
  // src/utils.ts
303
308
  var SEGMENT_WIDTH = {
304
309
  day: 2,
@@ -548,11 +553,11 @@ function stepDateSegment(date, type, delta) {
548
553
  }
549
554
 
550
555
  // src/DatePicker.tsx
551
- var import_jsx_runtime2 = require("react/jsx-runtime");
556
+ var import_jsx_runtime3 = require("react/jsx-runtime");
552
557
  var PLACEMENT_OFFSET = 4;
553
558
  var RANGE_SEPARATOR = /\s*[-–—]\s*/;
554
559
  var RANGE_DISPLAY_SEP = " - ";
555
- var CalendarFieldIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_xui_icons_base.Calendar, { size: "100%" });
560
+ var CalendarFieldIcon = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_xui_icons_base.Calendar, { size: "100%" });
556
561
  var FALLBACK_PANEL_SIZE = { width: 320, height: 360 };
557
562
  var resolveAutoPlacement = (container, panel, viewport, offset) => {
558
563
  const spaceBelow = viewport.height - container.bottom;
@@ -600,15 +605,15 @@ var resolveAlignForSide = (side, container, panel, viewport, preferred) => {
600
605
  const opposite = preferred === "start" ? "end" : "start";
601
606
  return fits(opposite) ? opposite : preferred;
602
607
  };
603
- var getPlacementStyle = (side, align, offset) => {
608
+ var getFixedPlacementStyle = (side, align, container, panel, viewport, offset) => {
604
609
  if (side === "top" || side === "bottom") {
605
- const vertical2 = side === "bottom" ? { top: "100%", bottom: "auto", marginTop: offset, marginBottom: 0 } : { top: "auto", bottom: "100%", marginTop: 0, marginBottom: offset };
606
- const horizontal2 = align === "center" ? { left: "50%", right: "auto", transform: "translateX(-50%)" } : align === "end" ? { left: "auto", right: 0 } : { left: 0, right: "auto" };
607
- return { marginLeft: 0, marginRight: 0, ...vertical2, ...horizontal2 };
610
+ const vertical2 = side === "bottom" ? { top: container.bottom + offset } : { bottom: viewport.height - container.top + offset };
611
+ const horizontal2 = align === "center" ? { left: (container.left + container.right) / 2 - panel.width / 2 } : align === "end" ? { right: viewport.width - container.right } : { left: container.left };
612
+ return { position: "fixed", ...vertical2, ...horizontal2 };
608
613
  }
609
- const horizontal = side === "right" ? { left: "100%", right: "auto", marginLeft: offset, marginRight: 0 } : { left: "auto", right: "100%", marginLeft: 0, marginRight: offset };
610
- const vertical = align === "center" ? { top: "50%", bottom: "auto", transform: "translateY(-50%)" } : align === "end" ? { top: "auto", bottom: 0 } : { top: 0, bottom: "auto" };
611
- return { marginTop: 0, marginBottom: 0, ...horizontal, ...vertical };
614
+ const horizontal = side === "right" ? { left: container.right + offset } : { right: viewport.width - container.left + offset };
615
+ const vertical = align === "center" ? { top: (container.top + container.bottom) / 2 - panel.height / 2 } : align === "end" ? { bottom: viewport.height - container.bottom } : { top: container.top };
616
+ return { position: "fixed", ...horizontal, ...vertical };
612
617
  };
613
618
  var DatePicker = (0, import_react.forwardRef)(
614
619
  ({
@@ -639,7 +644,7 @@ var DatePicker = (0, import_react.forwardRef)(
639
644
  themeProductContext,
640
645
  overlayThemeMode,
641
646
  overlayThemeProductContext,
642
- iconRight = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CalendarFieldIcon, {}),
647
+ iconRight = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CalendarFieldIcon, {}),
643
648
  iconLeft,
644
649
  label,
645
650
  clearable = false,
@@ -673,9 +678,10 @@ var DatePicker = (0, import_react.forwardRef)(
673
678
  const [activeSegment, setActiveSegment] = (0, import_react.useState)(null);
674
679
  const [internalChip, setInternalChip] = (0, import_react.useState)(null);
675
680
  const [parseError, setParseError] = (0, import_react.useState)(null);
676
- const [layout, setLayout] = (0, import_react.useState)(
677
- () => placement ? resolveForcedLayout(placement, dropdownPosition) : { side: "bottom", align: alignFromDropdownPosition(dropdownPosition) }
681
+ const [fixedPosition, setFixedPosition] = (0, import_react.useState)(
682
+ null
678
683
  );
684
+ const overlayLayer = (0, import_xui_core.useOverlayLayer)();
679
685
  const containerRef = (0, import_react.useRef)(null);
680
686
  const calendarRef = (0, import_react.useRef)(null);
681
687
  const inputRef = (0, import_react.useRef)(null);
@@ -1085,8 +1091,12 @@ var DatePicker = (0, import_react.forwardRef)(
1085
1091
  const handleClickOutside = (event) => {
1086
1092
  const target = event.target;
1087
1093
  const container = containerRef.current;
1094
+ const calendar = calendarRef.current;
1088
1095
  if (!target || !container) return;
1089
1096
  const path = typeof event.composedPath === "function" ? event.composedPath() : [];
1097
+ if (calendar && (path.length ? path.includes(calendar) : calendar.contains(target))) {
1098
+ return;
1099
+ }
1090
1100
  const listbox = path.length ? path.find(
1091
1101
  (el) => el instanceof Element && el.getAttribute("role") === "listbox"
1092
1102
  ) ?? null : target instanceof Element ? target.closest('[role="listbox"]') : null;
@@ -1094,7 +1104,7 @@ var DatePicker = (0, import_react.forwardRef)(
1094
1104
  const owner = document.querySelector(
1095
1105
  `[aria-controls="${listbox.id}"]`
1096
1106
  );
1097
- if (owner && container.contains(owner)) return;
1107
+ if (owner && calendar?.contains(owner)) return;
1098
1108
  }
1099
1109
  if (!container.contains(target)) {
1100
1110
  setOpen(false);
@@ -1117,15 +1127,11 @@ var DatePicker = (0, import_react.forwardRef)(
1117
1127
  document.addEventListener("keydown", handleEscape);
1118
1128
  return () => document.removeEventListener("keydown", handleEscape);
1119
1129
  }, [open]);
1130
+ (0, import_react.useEffect)(() => {
1131
+ if (!open) setFixedPosition(null);
1132
+ }, [open]);
1120
1133
  (0, import_react.useEffect)(() => {
1121
1134
  if (!open || !isWeb) return;
1122
- if (placement) {
1123
- const next = resolveForcedLayout(placement, dropdownPosition);
1124
- setLayout(
1125
- (prev) => prev.side === next.side && prev.align === next.align ? prev : next
1126
- );
1127
- return;
1128
- }
1129
1135
  const compute = () => {
1130
1136
  const container = containerRef.current;
1131
1137
  if (!container) return;
@@ -1136,30 +1142,44 @@ var DatePicker = (0, import_react.forwardRef)(
1136
1142
  width: window.innerWidth,
1137
1143
  height: window.innerHeight
1138
1144
  };
1139
- const side = resolveAutoPlacement(
1140
- containerRect,
1141
- panel,
1142
- viewport,
1143
- PLACEMENT_OFFSET
1144
- );
1145
- const preferred = side === "top" || side === "bottom" ? alignFromDropdownPosition(dropdownPosition) : "start";
1146
- const align = resolveAlignForSide(
1147
- side,
1148
- containerRect,
1149
- panel,
1150
- viewport,
1151
- preferred
1152
- );
1153
- setLayout(
1154
- (prev) => prev.side === side && prev.align === align ? prev : { side, align }
1145
+ let side;
1146
+ let align;
1147
+ if (placement) {
1148
+ ({ side, align } = resolveForcedLayout(placement, dropdownPosition));
1149
+ } else {
1150
+ side = resolveAutoPlacement(
1151
+ containerRect,
1152
+ panel,
1153
+ viewport,
1154
+ PLACEMENT_OFFSET
1155
+ );
1156
+ const preferred = side === "top" || side === "bottom" ? alignFromDropdownPosition(dropdownPosition) : "start";
1157
+ align = resolveAlignForSide(
1158
+ side,
1159
+ containerRect,
1160
+ panel,
1161
+ viewport,
1162
+ preferred
1163
+ );
1164
+ }
1165
+ setFixedPosition(
1166
+ getFixedPlacementStyle(
1167
+ side,
1168
+ align,
1169
+ containerRect,
1170
+ panel,
1171
+ viewport,
1172
+ PLACEMENT_OFFSET
1173
+ )
1155
1174
  );
1156
1175
  };
1157
- const rafId = window.requestAnimationFrame(compute);
1158
- const handleResize = () => compute();
1159
- window.addEventListener("resize", handleResize);
1176
+ compute();
1177
+ const handleReposition = () => compute();
1178
+ window.addEventListener("resize", handleReposition);
1179
+ window.addEventListener("scroll", handleReposition, true);
1160
1180
  return () => {
1161
- window.cancelAnimationFrame(rafId);
1162
- window.removeEventListener("resize", handleResize);
1181
+ window.removeEventListener("resize", handleReposition);
1182
+ window.removeEventListener("scroll", handleReposition, true);
1163
1183
  };
1164
1184
  }, [open, placement, dropdownPosition]);
1165
1185
  const overlayThemeProps = {
@@ -1174,7 +1194,7 @@ var DatePicker = (0, import_react.forwardRef)(
1174
1194
  // eslint-disable-next-line react-hooks/exhaustive-deps
1175
1195
  [viewAnchor?.getFullYear(), viewAnchor?.getMonth()]
1176
1196
  );
1177
- const renderCalendar = () => isDual ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1197
+ const renderCalendar = () => isDual ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1178
1198
  import_xui_calendar.DualCalendar,
1179
1199
  {
1180
1200
  locale,
@@ -1192,7 +1212,7 @@ var DatePicker = (0, import_react.forwardRef)(
1192
1212
  ...rest,
1193
1213
  ...overlayThemeProps
1194
1214
  }
1195
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1215
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1196
1216
  import_xui_calendar.Calendar,
1197
1217
  {
1198
1218
  locale,
@@ -1213,7 +1233,7 @@ var DatePicker = (0, import_react.forwardRef)(
1213
1233
  ...overlayThemeProps
1214
1234
  }
1215
1235
  );
1216
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1236
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1217
1237
  Box,
1218
1238
  {
1219
1239
  ref: (node) => {
@@ -1225,7 +1245,7 @@ var DatePicker = (0, import_react.forwardRef)(
1225
1245
  width: "100%",
1226
1246
  ...isWeb ? { onClick: () => setOpen(true) } : {},
1227
1247
  children: [
1228
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1248
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1229
1249
  import_xui_input.Input,
1230
1250
  {
1231
1251
  ...rest,
@@ -1252,7 +1272,7 @@ var DatePicker = (0, import_react.forwardRef)(
1252
1272
  themeProductContext
1253
1273
  }
1254
1274
  ),
1255
- open && (isWeb ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1275
+ open && (isWeb ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1256
1276
  Box,
1257
1277
  {
1258
1278
  ref: calendarRef,
@@ -1260,16 +1280,15 @@ var DatePicker = (0, import_react.forwardRef)(
1260
1280
  "aria-modal": true,
1261
1281
  "aria-label": isRange ? "Choose a date range" : "Choose a date",
1262
1282
  onClick: (e) => e.stopPropagation(),
1263
- position: "absolute",
1264
- zIndex: 1e3,
1265
- style: getPlacementStyle(
1266
- layout.side,
1267
- layout.align,
1268
- PLACEMENT_OFFSET
1269
- ),
1283
+ zIndex: overlayLayer + import_xui_core.LAYER_OFFSET.popover,
1284
+ style: {
1285
+ position: "fixed",
1286
+ visibility: fixedPosition ? "visible" : "hidden",
1287
+ ...fixedPosition
1288
+ },
1270
1289
  children: renderCalendar()
1271
1290
  }
1272
- ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box, { marginTop: 4, children: renderCalendar() }))
1291
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box, { marginTop: 4, children: renderCalendar() }))
1273
1292
  ]
1274
1293
  }
1275
1294
  );