@xsolla/xui-multi-select 0.175.0 → 0.176.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/web/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/MultiSelect.tsx
2
- import { forwardRef as forwardRef4, useRef as useRef4, useEffect as useEffect3 } from "react";
2
+ import { forwardRef as forwardRef4, useRef as useRef4, useEffect as useEffect4, useState as useState4, useCallback as useCallback2 } from "react";
3
3
 
4
4
  // ../../foundation/primitives-web/src/Box.tsx
5
5
  import React2 from "react";
@@ -317,8 +317,11 @@ var Icon = ({
317
317
  return /* @__PURE__ */ jsx3(StyledIcon, { "data-testid": dataTestId || testID, ...props, children });
318
318
  };
319
319
 
320
+ // ../../foundation/primitives-web/src/index.tsx
321
+ var isWeb = true;
322
+
320
323
  // src/MultiSelect.tsx
321
- import { useResolvedTheme as useResolvedTheme4 } from "@xsolla/xui-core";
324
+ import { useModalId, useResolvedTheme as useResolvedTheme4 } from "@xsolla/xui-core";
322
325
 
323
326
  // src/MultiSelectControl.tsx
324
327
  import { forwardRef as forwardRef3, useRef as useRef2 } from "react";
@@ -2174,6 +2177,16 @@ var useMultiSelect = ({
2174
2177
  };
2175
2178
  };
2176
2179
 
2180
+ // src/Portal.web.tsx
2181
+ import { useEffect as useEffect3, useState as useState3 } from "react";
2182
+ import { createPortal } from "react-dom";
2183
+ var Portal = ({ children }) => {
2184
+ const [mountNode, setMountNode] = useState3(null);
2185
+ useEffect3(() => setMountNode(document.body), []);
2186
+ if (!mountNode) return null;
2187
+ return createPortal(children, mountNode);
2188
+ };
2189
+
2177
2190
  // src/MultiSelect.tsx
2178
2191
  import { Fragment, jsx as jsx726, jsxs as jsxs4 } from "react/jsx-runtime";
2179
2192
  var EXTERNAL_MENU_MIN_WIDTH_DEFAULT = 540;
@@ -2204,8 +2217,10 @@ var MultiSelect = forwardRef4(
2204
2217
  themeProductContext
2205
2218
  }, ref) => {
2206
2219
  const { theme } = useResolvedTheme4({ themeMode, themeProductContext });
2220
+ const modalId = useModalId();
2207
2221
  const controlRef = useRef4(null);
2208
2222
  const menuRef = useRef4(null);
2223
+ const [menuPosition, setMenuPosition] = useState4(null);
2209
2224
  const sizeStyles = theme.sizing.input(size);
2210
2225
  const state = externalState || (disabled ? "disable" : "default");
2211
2226
  const isDisable = state === "disable" || disabled;
@@ -2226,7 +2241,7 @@ var MultiSelect = forwardRef4(
2226
2241
  value,
2227
2242
  onChange
2228
2243
  });
2229
- useEffect3(() => {
2244
+ useEffect4(() => {
2230
2245
  if (isDisable || !dropdownMenu) {
2231
2246
  onClose();
2232
2247
  }
@@ -2249,11 +2264,111 @@ var MultiSelect = forwardRef4(
2249
2264
  };
2250
2265
  const controlMenuOpen = dropdownMenu ? isOpen : Boolean(menuOpen);
2251
2266
  const controlOnClick = dropdownMenu ? onSelectClick : onTriggerPress;
2267
+ const updateMenuPosition = useCallback2(() => {
2268
+ if (!isWeb || !controlRef.current) return;
2269
+ const rect = controlRef.current.getBoundingClientRect();
2270
+ setMenuPosition({
2271
+ left: rect.left,
2272
+ top: rect.bottom + 4,
2273
+ width: rect.width
2274
+ });
2275
+ }, []);
2276
+ useEffect4(() => {
2277
+ if (!isWeb || !controlMenuOpen || isDisable || !dropdownMenu) return;
2278
+ updateMenuPosition();
2279
+ window.addEventListener("resize", updateMenuPosition);
2280
+ window.addEventListener("scroll", updateMenuPosition, true);
2281
+ return () => {
2282
+ window.removeEventListener("resize", updateMenuPosition);
2283
+ window.removeEventListener("scroll", updateMenuPosition, true);
2284
+ };
2285
+ }, [controlMenuOpen, dropdownMenu, isDisable, updateMenuPosition]);
2252
2286
  const externalFieldLayout = !dropdownMenu ? {
2253
2287
  width: "100%",
2254
2288
  minWidth: menuMinWidth ?? EXTERNAL_MENU_MIN_WIDTH_DEFAULT,
2255
2289
  boxSizing: "border-box"
2256
2290
  } : void 0;
2291
+ const dropdown = dropdownMenu && isOpen && !isDisable && (!isWeb || menuPosition) && /* @__PURE__ */ jsxs4(Fragment, { children: [
2292
+ /* @__PURE__ */ jsx726(
2293
+ Box,
2294
+ {
2295
+ "data-modal-id": modalId,
2296
+ style: {
2297
+ position: "fixed",
2298
+ top: 0,
2299
+ left: 0,
2300
+ right: 0,
2301
+ bottom: 0,
2302
+ zIndex: 999,
2303
+ cursor: "default"
2304
+ },
2305
+ onPress: onClose
2306
+ }
2307
+ ),
2308
+ /* @__PURE__ */ jsx726(
2309
+ Box,
2310
+ {
2311
+ ref: menuRef,
2312
+ "data-modal-id": modalId,
2313
+ backgroundColor: theme.colors.background.secondary,
2314
+ borderColor: theme.colors.border.secondary,
2315
+ borderWidth: 1,
2316
+ borderRadius: sizeStyles.radius,
2317
+ paddingVertical: 4,
2318
+ style: {
2319
+ position: isWeb ? "fixed" : "absolute",
2320
+ top: isWeb ? menuPosition?.top : "100%",
2321
+ left: isWeb ? menuPosition?.left : 0,
2322
+ right: isWeb ? void 0 : 0,
2323
+ width: isWeb ? menuPosition?.width : void 0,
2324
+ marginTop: isWeb ? void 0 : 4,
2325
+ zIndex: 1001,
2326
+ // Above control (1000) and backdrop (999)
2327
+ boxShadow: theme.shadow.popover,
2328
+ maxHeight,
2329
+ overflowY: "auto"
2330
+ },
2331
+ children: menuItems.map((item, _index) => {
2332
+ const brandColors = theme.colors.control.brand.primary;
2333
+ const contentColors = theme.colors.content;
2334
+ return /* @__PURE__ */ jsx726(
2335
+ Box,
2336
+ {
2337
+ testID,
2338
+ paddingHorizontal: sizeStyles.paddingHorizontal,
2339
+ paddingVertical: 8,
2340
+ onPress: () => {
2341
+ if (!item.disabled) {
2342
+ handleItemToggle(item.id, !item.checked);
2343
+ }
2344
+ },
2345
+ flexDirection: "row",
2346
+ alignItems: "center",
2347
+ justifyContent: "space-between",
2348
+ backgroundColor: item.checked ? brandColors.bg : "transparent",
2349
+ hoverStyle: !item.disabled && !item.checked ? {
2350
+ backgroundColor: theme.colors.control.input.bgHover
2351
+ } : void 0,
2352
+ style: {
2353
+ cursor: item.disabled ? "not-allowed" : "pointer",
2354
+ opacity: item.disabled ? 0.5 : 1
2355
+ },
2356
+ children: /* @__PURE__ */ jsx726(
2357
+ Text,
2358
+ {
2359
+ color: item.checked ? contentColors.on.brand : theme.colors.content.secondary,
2360
+ fontSize: sizeStyles.fontSize,
2361
+ fontWeight: "400",
2362
+ children: item.children
2363
+ }
2364
+ )
2365
+ },
2366
+ item.id
2367
+ );
2368
+ })
2369
+ }
2370
+ )
2371
+ ] });
2257
2372
  return /* @__PURE__ */ jsxs4(
2258
2373
  Box,
2259
2374
  {
@@ -2305,84 +2420,7 @@ var MultiSelect = forwardRef4(
2305
2420
  extraClear
2306
2421
  }
2307
2422
  ),
2308
- dropdownMenu && isOpen && !isDisable && /* @__PURE__ */ jsxs4(Fragment, { children: [
2309
- /* @__PURE__ */ jsx726(
2310
- Box,
2311
- {
2312
- style: {
2313
- position: "fixed",
2314
- top: 0,
2315
- left: 0,
2316
- right: 0,
2317
- bottom: 0,
2318
- zIndex: 999,
2319
- cursor: "default"
2320
- },
2321
- onPress: onClose
2322
- }
2323
- ),
2324
- /* @__PURE__ */ jsx726(
2325
- Box,
2326
- {
2327
- ref: menuRef,
2328
- backgroundColor: theme.colors.background.secondary,
2329
- borderColor: theme.colors.border.secondary,
2330
- borderWidth: 1,
2331
- borderRadius: theme.shape.input[size].borderRadius,
2332
- paddingVertical: 4,
2333
- style: {
2334
- position: "absolute",
2335
- top: "100%",
2336
- left: 0,
2337
- right: 0,
2338
- marginTop: 4,
2339
- zIndex: 1001,
2340
- // Above control (1000) and backdrop (999)
2341
- boxShadow: theme.shadow.popover,
2342
- maxHeight,
2343
- overflowY: "auto"
2344
- },
2345
- children: menuItems.map((item, _index) => {
2346
- const brandColors = theme.colors.control.brand.primary;
2347
- const contentColors = theme.colors.content;
2348
- return /* @__PURE__ */ jsx726(
2349
- Box,
2350
- {
2351
- testID,
2352
- paddingHorizontal: sizeStyles.paddingHorizontal,
2353
- paddingVertical: 8,
2354
- onPress: () => {
2355
- if (!item.disabled) {
2356
- handleItemToggle(item.id, !item.checked);
2357
- }
2358
- },
2359
- flexDirection: "row",
2360
- alignItems: "center",
2361
- justifyContent: "space-between",
2362
- backgroundColor: item.checked ? brandColors.bg : "transparent",
2363
- hoverStyle: !item.disabled && !item.checked ? {
2364
- backgroundColor: theme.colors.control.input.bgHover
2365
- } : void 0,
2366
- style: {
2367
- cursor: item.disabled ? "not-allowed" : "pointer",
2368
- opacity: item.disabled ? 0.5 : 1
2369
- },
2370
- children: /* @__PURE__ */ jsx726(
2371
- Text,
2372
- {
2373
- color: item.checked ? contentColors.on.brand : theme.colors.content.secondary,
2374
- fontSize: sizeStyles.fontSize,
2375
- fontWeight: "400",
2376
- children: item.children
2377
- }
2378
- )
2379
- },
2380
- item.id
2381
- );
2382
- })
2383
- }
2384
- )
2385
- ] })
2423
+ isWeb ? /* @__PURE__ */ jsx726(Portal, { children: dropdown }) : dropdown
2386
2424
  ]
2387
2425
  }
2388
2426
  ),