@xsolla/xui-multi-select 0.149.1 → 0.150.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xsolla/xui-multi-select",
3
- "version": "0.149.1",
3
+ "version": "0.150.0",
4
4
  "main": "./web/index.js",
5
5
  "module": "./web/index.mjs",
6
6
  "types": "./web/index.d.ts",
@@ -10,10 +10,10 @@
10
10
  "build:native": "PLATFORM=native tsup"
11
11
  },
12
12
  "dependencies": {
13
- "@xsolla/xui-checkbox": "0.149.1",
14
- "@xsolla/xui-core": "0.149.1",
15
- "@xsolla/xui-dropdown": "0.149.1",
16
- "@xsolla/xui-primitives-core": "0.149.1"
13
+ "@xsolla/xui-checkbox": "0.150.0",
14
+ "@xsolla/xui-core": "0.150.0",
15
+ "@xsolla/xui-dropdown": "0.150.0",
16
+ "@xsolla/xui-primitives-core": "0.150.0"
17
17
  },
18
18
  "peerDependencies": {
19
19
  "react": ">=16.8.0",
package/web/index.d.mts CHANGED
@@ -83,6 +83,29 @@ interface MultiSelectProps extends ThemeOverrideProps {
83
83
  * @default 300
84
84
  */
85
85
  maxHeight?: number;
86
+ /**
87
+ * When false, the built-in options list and backdrop are not shown and the control
88
+ * does not open on click. Use with an external picker (e.g. grouped select) wired
89
+ * to the same `value` / `onChange`.
90
+ * @default true
91
+ */
92
+ dropdownMenu?: boolean;
93
+ /**
94
+ * When `dropdownMenu` is false: fired when the user activates the field (same gesture
95
+ * that would open the built-in list). Typically toggle an external panel.
96
+ */
97
+ onTriggerPress?: () => void;
98
+ /**
99
+ * When `dropdownMenu` is false: whether an external menu/panel is open — drives
100
+ * chevron direction and control layering like the built-in open state.
101
+ */
102
+ menuOpen?: boolean;
103
+ /**
104
+ * When `dropdownMenu` is false: `min-width` of the field in px so it aligns with
105
+ * a typical grouped panel (default **540**, same as `GroupSelect`). Use `0` for
106
+ * no minimum. Ignored when the built-in dropdown is enabled.
107
+ */
108
+ menuMinWidth?: number;
86
109
  }
87
110
 
88
111
  declare const MultiSelect: react.ForwardRefExoticComponent<MultiSelectProps & react.RefAttributes<HTMLDivElement>>;
package/web/index.d.ts CHANGED
@@ -83,6 +83,29 @@ interface MultiSelectProps extends ThemeOverrideProps {
83
83
  * @default 300
84
84
  */
85
85
  maxHeight?: number;
86
+ /**
87
+ * When false, the built-in options list and backdrop are not shown and the control
88
+ * does not open on click. Use with an external picker (e.g. grouped select) wired
89
+ * to the same `value` / `onChange`.
90
+ * @default true
91
+ */
92
+ dropdownMenu?: boolean;
93
+ /**
94
+ * When `dropdownMenu` is false: fired when the user activates the field (same gesture
95
+ * that would open the built-in list). Typically toggle an external panel.
96
+ */
97
+ onTriggerPress?: () => void;
98
+ /**
99
+ * When `dropdownMenu` is false: whether an external menu/panel is open — drives
100
+ * chevron direction and control layering like the built-in open state.
101
+ */
102
+ menuOpen?: boolean;
103
+ /**
104
+ * When `dropdownMenu` is false: `min-width` of the field in px so it aligns with
105
+ * a typical grouped panel (default **540**, same as `GroupSelect`). Use `0` for
106
+ * no minimum. Ignored when the built-in dropdown is enabled.
107
+ */
108
+ menuMinWidth?: number;
86
109
  }
87
110
 
88
111
  declare const MultiSelect: react.ForwardRefExoticComponent<MultiSelectProps & react.RefAttributes<HTMLDivElement>>;
package/web/index.js CHANGED
@@ -1972,6 +1972,7 @@ var MultiSelectControl = (0, import_react9.forwardRef)(
1972
1972
  iconRight,
1973
1973
  disabled = false,
1974
1974
  extraClear = false,
1975
+ width,
1975
1976
  themeMode,
1976
1977
  themeProductContext
1977
1978
  }, ref) => {
@@ -2020,6 +2021,7 @@ var MultiSelectControl = (0, import_react9.forwardRef)(
2020
2021
  Box,
2021
2022
  {
2022
2023
  ref,
2024
+ width,
2023
2025
  backgroundColor,
2024
2026
  borderColor,
2025
2027
  borderWidth: borderColor !== "transparent" ? 1 : 0,
@@ -2031,7 +2033,7 @@ var MultiSelectControl = (0, import_react9.forwardRef)(
2031
2033
  alignItems: "center",
2032
2034
  gap: 8,
2033
2035
  style: {
2034
- cursor: isDisable ? "not-allowed" : "pointer",
2036
+ cursor: isDisable ? "not-allowed" : onClick ? "pointer" : "default",
2035
2037
  boxSizing: "border-box",
2036
2038
  height: flexible ? "auto" : sizeStyles.height,
2037
2039
  position: "relative",
@@ -2039,7 +2041,7 @@ var MultiSelectControl = (0, import_react9.forwardRef)(
2039
2041
  // Above backdrop when open
2040
2042
  },
2041
2043
  onPress: isDisable ? void 0 : onClick,
2042
- hoverStyle: !isDisable && !isFocus && !isOpen && !isError ? {
2044
+ hoverStyle: !isDisable && onClick && !isFocus && !isOpen && !isError ? {
2043
2045
  backgroundColor: inputColors.bgHover,
2044
2046
  borderColor: inputColors.borderHover
2045
2047
  } : void 0,
@@ -2195,6 +2197,7 @@ var useMultiSelect = ({
2195
2197
 
2196
2198
  // src/MultiSelect.tsx
2197
2199
  var import_jsx_runtime733 = require("react/jsx-runtime");
2200
+ var EXTERNAL_MENU_MIN_WIDTH_DEFAULT = 540;
2198
2201
  var MultiSelect = (0, import_react11.forwardRef)(
2199
2202
  ({
2200
2203
  options,
@@ -2213,6 +2216,10 @@ var MultiSelect = (0, import_react11.forwardRef)(
2213
2216
  iconLeft,
2214
2217
  iconRight,
2215
2218
  maxHeight = 300,
2219
+ dropdownMenu = true,
2220
+ onTriggerPress,
2221
+ menuOpen = false,
2222
+ menuMinWidth,
2216
2223
  themeMode,
2217
2224
  themeProductContext
2218
2225
  }, ref) => {
@@ -2240,10 +2247,10 @@ var MultiSelect = (0, import_react11.forwardRef)(
2240
2247
  onChange
2241
2248
  });
2242
2249
  (0, import_react11.useEffect)(() => {
2243
- if (isDisable) {
2250
+ if (isDisable || !dropdownMenu) {
2244
2251
  onClose();
2245
2252
  }
2246
- }, [isDisable, onClose]);
2253
+ }, [isDisable, dropdownMenu, onClose]);
2247
2254
  const menuItems = options.map((opt) => {
2248
2255
  const id = String(opt.value);
2249
2256
  const checked = values.map(String).includes(id);
@@ -2260,138 +2267,155 @@ var MultiSelect = (0, import_react11.forwardRef)(
2260
2267
  const newValues = checked ? [...values, value2] : values.filter((v) => v !== value2);
2261
2268
  onChoose(newValues.map(String));
2262
2269
  };
2263
- return /* @__PURE__ */ (0, import_jsx_runtime733.jsxs)(Box, { flexDirection: "column", gap: sizeStyles.fieldGap, children: [
2264
- label && /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2265
- Text,
2266
- {
2267
- color: theme.colors.content.secondary,
2268
- fontSize: sizeStyles.fontSize - 2,
2269
- fontWeight: "500",
2270
- children: label
2271
- }
2272
- ),
2273
- /* @__PURE__ */ (0, import_jsx_runtime733.jsxs)(
2274
- Box,
2275
- {
2276
- ref,
2277
- style: {
2278
- position: "relative"
2279
- },
2280
- children: [
2281
- /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2282
- MultiSelectControl,
2283
- {
2284
- ref: controlRef,
2285
- isOpen,
2286
- isFocus,
2287
- isError,
2288
- size,
2289
- state,
2290
- disabled: isDisable,
2291
- onClick: onSelectClick,
2292
- removeValue: onRemove,
2293
- removeAllValues: onRemoveAll,
2294
- stateList,
2295
- selectedItems,
2296
- variant,
2297
- flexible,
2298
- placeholder,
2299
- removeTagsButtons,
2300
- iconLeft,
2301
- iconRight,
2302
- extraClear
2303
- }
2304
- ),
2305
- isOpen && !isDisable && /* @__PURE__ */ (0, import_jsx_runtime733.jsxs)(import_jsx_runtime733.Fragment, { children: [
2306
- /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2307
- Box,
2308
- {
2309
- style: {
2310
- position: "fixed",
2311
- top: 0,
2312
- left: 0,
2313
- right: 0,
2314
- bottom: 0,
2315
- zIndex: 999,
2316
- cursor: "default"
2317
- },
2318
- onPress: onClose
2319
- }
2320
- ),
2321
- /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2322
- Box,
2323
- {
2324
- ref: menuRef,
2325
- backgroundColor: theme.colors.background.secondary,
2326
- borderColor: theme.colors.border.secondary,
2327
- borderWidth: 1,
2328
- borderRadius: theme.radius.button,
2329
- paddingVertical: 4,
2330
- style: {
2331
- position: "absolute",
2332
- top: "100%",
2333
- left: 0,
2334
- right: 0,
2335
- marginTop: 4,
2336
- zIndex: 1001,
2337
- // Above control (1000) and backdrop (999)
2338
- boxShadow: theme.shadow.popover,
2339
- maxHeight,
2340
- overflowY: "auto"
2341
- },
2342
- children: menuItems.map((item, _index) => {
2343
- const brandColors = theme.colors.control.brand.primary;
2344
- const contentColors = theme.colors.content;
2345
- return /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2346
- Box,
2347
- {
2348
- paddingHorizontal: sizeStyles.paddingHorizontal,
2349
- paddingVertical: 8,
2350
- onPress: () => {
2351
- if (!item.disabled) {
2352
- handleItemToggle(item.id, !item.checked);
2353
- }
2354
- },
2355
- flexDirection: "row",
2356
- alignItems: "center",
2357
- justifyContent: "space-between",
2358
- backgroundColor: item.checked ? brandColors.bg : "transparent",
2359
- hoverStyle: !item.disabled && !item.checked ? {
2360
- backgroundColor: theme.colors.control.input.bgHover
2361
- } : void 0,
2362
- style: {
2363
- cursor: item.disabled ? "not-allowed" : "pointer",
2364
- opacity: item.disabled ? 0.5 : 1
2365
- },
2366
- children: /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2367
- Text,
2368
- {
2369
- color: item.checked ? contentColors.on.brand : theme.colors.content.secondary,
2370
- fontSize: sizeStyles.fontSize,
2371
- fontWeight: "400",
2372
- children: item.children
2373
- }
2374
- )
2270
+ const controlMenuOpen = dropdownMenu ? isOpen : Boolean(menuOpen);
2271
+ const controlOnClick = dropdownMenu ? onSelectClick : onTriggerPress;
2272
+ const externalFieldLayout = !dropdownMenu ? {
2273
+ width: "100%",
2274
+ minWidth: menuMinWidth ?? EXTERNAL_MENU_MIN_WIDTH_DEFAULT,
2275
+ boxSizing: "border-box"
2276
+ } : void 0;
2277
+ return /* @__PURE__ */ (0, import_jsx_runtime733.jsxs)(
2278
+ Box,
2279
+ {
2280
+ flexDirection: "column",
2281
+ gap: sizeStyles.fieldGap,
2282
+ style: externalFieldLayout,
2283
+ children: [
2284
+ label && /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2285
+ Text,
2286
+ {
2287
+ color: theme.colors.content.secondary,
2288
+ fontSize: sizeStyles.fontSize - 2,
2289
+ fontWeight: "500",
2290
+ children: label
2291
+ }
2292
+ ),
2293
+ /* @__PURE__ */ (0, import_jsx_runtime733.jsxs)(
2294
+ Box,
2295
+ {
2296
+ ref,
2297
+ style: {
2298
+ position: "relative",
2299
+ ...externalFieldLayout ? { width: "100%" } : {}
2300
+ },
2301
+ children: [
2302
+ /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2303
+ MultiSelectControl,
2304
+ {
2305
+ ref: controlRef,
2306
+ isOpen: controlMenuOpen,
2307
+ isFocus,
2308
+ isError,
2309
+ size,
2310
+ state,
2311
+ disabled: isDisable,
2312
+ onClick: controlOnClick,
2313
+ width: dropdownMenu ? void 0 : "100%",
2314
+ removeValue: onRemove,
2315
+ removeAllValues: onRemoveAll,
2316
+ stateList,
2317
+ selectedItems,
2318
+ variant,
2319
+ flexible,
2320
+ placeholder,
2321
+ removeTagsButtons,
2322
+ iconLeft,
2323
+ iconRight,
2324
+ extraClear
2325
+ }
2326
+ ),
2327
+ dropdownMenu && isOpen && !isDisable && /* @__PURE__ */ (0, import_jsx_runtime733.jsxs)(import_jsx_runtime733.Fragment, { children: [
2328
+ /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2329
+ Box,
2330
+ {
2331
+ style: {
2332
+ position: "fixed",
2333
+ top: 0,
2334
+ left: 0,
2335
+ right: 0,
2336
+ bottom: 0,
2337
+ zIndex: 999,
2338
+ cursor: "default"
2375
2339
  },
2376
- item.id
2377
- );
2378
- })
2379
- }
2380
- )
2381
- ] })
2382
- ]
2383
- }
2384
- ),
2385
- errorMessage && /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2386
- Text,
2387
- {
2388
- color: theme.colors.content.alert.primary,
2389
- fontSize: sizeStyles.fontSize - 2,
2390
- style: { lineHeight: sizeStyles.lineHeight + "px" },
2391
- children: errorMessage
2392
- }
2393
- )
2394
- ] });
2340
+ onPress: onClose
2341
+ }
2342
+ ),
2343
+ /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2344
+ Box,
2345
+ {
2346
+ ref: menuRef,
2347
+ backgroundColor: theme.colors.background.secondary,
2348
+ borderColor: theme.colors.border.secondary,
2349
+ borderWidth: 1,
2350
+ borderRadius: theme.radius.button,
2351
+ paddingVertical: 4,
2352
+ style: {
2353
+ position: "absolute",
2354
+ top: "100%",
2355
+ left: 0,
2356
+ right: 0,
2357
+ marginTop: 4,
2358
+ zIndex: 1001,
2359
+ // Above control (1000) and backdrop (999)
2360
+ boxShadow: theme.shadow.popover,
2361
+ maxHeight,
2362
+ overflowY: "auto"
2363
+ },
2364
+ children: menuItems.map((item, _index) => {
2365
+ const brandColors = theme.colors.control.brand.primary;
2366
+ const contentColors = theme.colors.content;
2367
+ return /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2368
+ Box,
2369
+ {
2370
+ paddingHorizontal: sizeStyles.paddingHorizontal,
2371
+ paddingVertical: 8,
2372
+ onPress: () => {
2373
+ if (!item.disabled) {
2374
+ handleItemToggle(item.id, !item.checked);
2375
+ }
2376
+ },
2377
+ flexDirection: "row",
2378
+ alignItems: "center",
2379
+ justifyContent: "space-between",
2380
+ backgroundColor: item.checked ? brandColors.bg : "transparent",
2381
+ hoverStyle: !item.disabled && !item.checked ? {
2382
+ backgroundColor: theme.colors.control.input.bgHover
2383
+ } : void 0,
2384
+ style: {
2385
+ cursor: item.disabled ? "not-allowed" : "pointer",
2386
+ opacity: item.disabled ? 0.5 : 1
2387
+ },
2388
+ children: /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2389
+ Text,
2390
+ {
2391
+ color: item.checked ? contentColors.on.brand : theme.colors.content.secondary,
2392
+ fontSize: sizeStyles.fontSize,
2393
+ fontWeight: "400",
2394
+ children: item.children
2395
+ }
2396
+ )
2397
+ },
2398
+ item.id
2399
+ );
2400
+ })
2401
+ }
2402
+ )
2403
+ ] })
2404
+ ]
2405
+ }
2406
+ ),
2407
+ errorMessage && /* @__PURE__ */ (0, import_jsx_runtime733.jsx)(
2408
+ Text,
2409
+ {
2410
+ color: theme.colors.content.alert.primary,
2411
+ fontSize: sizeStyles.fontSize - 2,
2412
+ style: { lineHeight: sizeStyles.lineHeight + "px" },
2413
+ children: errorMessage
2414
+ }
2415
+ )
2416
+ ]
2417
+ }
2418
+ );
2395
2419
  }
2396
2420
  );
2397
2421
  MultiSelect.displayName = "MultiSelect";