@xsolla/xui-multi-select 0.183.0 → 0.185.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/README.md +56 -0
- package/native/index.d.mts +31 -1
- package/native/index.d.ts +31 -1
- package/native/index.js +171 -41
- package/native/index.js.map +1 -1
- package/native/index.mjs +178 -42
- package/native/index.mjs.map +1 -1
- package/package.json +5 -5
- package/web/index.d.mts +31 -1
- package/web/index.d.ts +31 -1
- package/web/index.js +171 -41
- package/web/index.js.map +1 -1
- package/web/index.mjs +178 -42
- package/web/index.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -174,6 +174,9 @@ Add backdrop, click-outside, and Escape handling in your layout as needed (see S
|
|
|
174
174
|
| `removeTagsButtons` | `boolean` | `true` | Show a remove button on each tag. |
|
|
175
175
|
| `extraClear` | `boolean` | `false` | Show a clear-all button. |
|
|
176
176
|
| `maxHeight` | `number` | `300` | Maximum dropdown height in pixels. |
|
|
177
|
+
| `searchable` | `boolean` | `false` | Show a search input at the top of the built-in dropdown that filters options in memory by label. Web only. |
|
|
178
|
+
| `searchPlaceholder` | `string` | `'Search'` | Placeholder for the search input (used when `searchable`). |
|
|
179
|
+
| `noOptionsMessage` | `string` | `'No results'` | Message shown when the search filter matches no options. |
|
|
177
180
|
| `iconLeft` | `ReactNode` | — | Icon rendered on the left of the control. |
|
|
178
181
|
| `iconRight` | `ReactNode` | — | Icon on the right (overrides the default caret). |
|
|
179
182
|
| `dropdownMenu` | `boolean` | `true` | When `false`, hides the built-in list and disables click-to-open; use with an external picker (e.g. `GroupSelect`) wired to the same `value` / `onChange`. |
|
|
@@ -268,8 +271,61 @@ const options = [
|
|
|
268
271
|
<MultiSelect options={options} value={["react"]} disabled />;
|
|
269
272
|
```
|
|
270
273
|
|
|
274
|
+
### Searchable
|
|
275
|
+
|
|
276
|
+
Enable `searchable` to show a search field at the top of the dropdown. It filters the options in memory by label as the user types, mirroring the [`Select`](./select.md) component's `searchable` behavior. Customize the input hint with `searchPlaceholder` and the empty-result text with `noOptionsMessage`. Web only — the prop is ignored on native.
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
const options = [
|
|
280
|
+
{ label: "React", value: "react" },
|
|
281
|
+
{ label: "Vue", value: "vue" },
|
|
282
|
+
{ label: "Angular", value: "angular" },
|
|
283
|
+
{ label: "Svelte", value: "svelte" },
|
|
284
|
+
{ label: "Solid", value: "solid" },
|
|
285
|
+
];
|
|
286
|
+
|
|
287
|
+
const [selected, setSelected] = useState<MultiSelectValue>([]);
|
|
288
|
+
|
|
289
|
+
<MultiSelect
|
|
290
|
+
label="Frameworks"
|
|
291
|
+
options={options}
|
|
292
|
+
value={selected}
|
|
293
|
+
onChange={setSelected}
|
|
294
|
+
searchable
|
|
295
|
+
searchPlaceholder="Search frameworks"
|
|
296
|
+
noOptionsMessage="No frameworks found"
|
|
297
|
+
placeholder="Select frameworks"
|
|
298
|
+
/>;
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Long option labels
|
|
302
|
+
|
|
303
|
+
Long, unbreakable option labels (URLs, IDs, tokens) wrap inside the dropdown menu instead of forcing a horizontal scrollbar; the menu width stays pinned to the control.
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
const options = [
|
|
307
|
+
{
|
|
308
|
+
label:
|
|
309
|
+
"https://prototype.xsolla.dev/mini-apps/some-really-long-slug?token=abcdef0123456789",
|
|
310
|
+
value: "url",
|
|
311
|
+
},
|
|
312
|
+
{ label: "urn:xsolla:resource:0123456789abcdef0123456789abcdef", value: "id" },
|
|
313
|
+
{ label: "Short", value: "short" },
|
|
314
|
+
];
|
|
315
|
+
|
|
316
|
+
const [selected, setSelected] = useState<MultiSelectValue>([]);
|
|
317
|
+
|
|
318
|
+
<MultiSelect
|
|
319
|
+
options={options}
|
|
320
|
+
value={selected}
|
|
321
|
+
onChange={setSelected}
|
|
322
|
+
placeholder="Select options"
|
|
323
|
+
/>;
|
|
324
|
+
```
|
|
325
|
+
|
|
271
326
|
## Accessibility
|
|
272
327
|
|
|
273
328
|
- Selection is rendered as a checkbox list inside the dropdown.
|
|
274
329
|
- The dropdown is keyboard navigable; selection state is announced to assistive technology.
|
|
275
330
|
- An `errorMessage` marks the control as invalid for screen readers.
|
|
331
|
+
- When `searchable`, the dropdown's search input exposes an `aria-label` matching `searchPlaceholder`; options filter in memory as the user types (web only).
|
package/native/index.d.mts
CHANGED
|
@@ -5,6 +5,7 @@ import { ThemeOverrideProps } from '@xsolla/xui-core';
|
|
|
5
5
|
type MultiSelectValue = (string | number)[];
|
|
6
6
|
type MultiSelectVariant = "tag" | "text";
|
|
7
7
|
type MultiSelectSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
8
|
+
type MultiSelectOptionOverflow = "truncate" | "wrap" | "scroll";
|
|
8
9
|
type MultiSelectState = "default" | "hover" | "focus" | "disable" | "error";
|
|
9
10
|
interface MultiSelectOption {
|
|
10
11
|
label: ReactNode;
|
|
@@ -83,6 +84,35 @@ interface MultiSelectProps extends ThemeOverrideProps {
|
|
|
83
84
|
* @default 300
|
|
84
85
|
*/
|
|
85
86
|
maxHeight?: number;
|
|
87
|
+
/**
|
|
88
|
+
* How long option labels in the dropdown handle horizontal overflow.
|
|
89
|
+
* - `"truncate"` — clamp each label to a single line with an ellipsis.
|
|
90
|
+
* - `"wrap"` — allow labels to wrap onto multiple lines (rows grow taller).
|
|
91
|
+
* - `"scroll"` — keep labels on one line and scroll the list horizontally
|
|
92
|
+
* (legacy behavior). Web only — falls back to `"wrap"` on native, which
|
|
93
|
+
* cannot horizontally scroll a text row.
|
|
94
|
+
* @default "truncate"
|
|
95
|
+
*/
|
|
96
|
+
optionOverflow?: MultiSelectOptionOverflow;
|
|
97
|
+
/**
|
|
98
|
+
* When true, shows a search input at the top of the built-in dropdown that
|
|
99
|
+
* filters options in memory by their label as the user types. Mirrors the
|
|
100
|
+
* `searchable` behavior of the Select component. Web only — ignored on
|
|
101
|
+
* native and when `dropdownMenu` is false.
|
|
102
|
+
* @default false
|
|
103
|
+
*/
|
|
104
|
+
searchable?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Placeholder text for the search input. Only used when `searchable` is true.
|
|
107
|
+
* @default "Search"
|
|
108
|
+
*/
|
|
109
|
+
searchPlaceholder?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Message shown in the dropdown when the search filter matches no options.
|
|
112
|
+
* Only used when `searchable` is true.
|
|
113
|
+
* @default "No results"
|
|
114
|
+
*/
|
|
115
|
+
noOptionsMessage?: string;
|
|
86
116
|
/**
|
|
87
117
|
* When false, the built-in options list and backdrop are not shown and the control
|
|
88
118
|
* does not open on click. Use with an external picker (e.g. grouped select) wired
|
|
@@ -112,4 +142,4 @@ interface MultiSelectProps extends ThemeOverrideProps {
|
|
|
112
142
|
|
|
113
143
|
declare const MultiSelect: react.ForwardRefExoticComponent<MultiSelectProps & react.RefAttributes<HTMLDivElement>>;
|
|
114
144
|
|
|
115
|
-
export { MultiSelect, type MultiSelectOption, type MultiSelectProps, type MultiSelectSize, type MultiSelectState, type MultiSelectValue, type MultiSelectVariant };
|
|
145
|
+
export { MultiSelect, type MultiSelectOption, type MultiSelectOptionOverflow, type MultiSelectProps, type MultiSelectSize, type MultiSelectState, type MultiSelectValue, type MultiSelectVariant };
|
package/native/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { ThemeOverrideProps } from '@xsolla/xui-core';
|
|
|
5
5
|
type MultiSelectValue = (string | number)[];
|
|
6
6
|
type MultiSelectVariant = "tag" | "text";
|
|
7
7
|
type MultiSelectSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
8
|
+
type MultiSelectOptionOverflow = "truncate" | "wrap" | "scroll";
|
|
8
9
|
type MultiSelectState = "default" | "hover" | "focus" | "disable" | "error";
|
|
9
10
|
interface MultiSelectOption {
|
|
10
11
|
label: ReactNode;
|
|
@@ -83,6 +84,35 @@ interface MultiSelectProps extends ThemeOverrideProps {
|
|
|
83
84
|
* @default 300
|
|
84
85
|
*/
|
|
85
86
|
maxHeight?: number;
|
|
87
|
+
/**
|
|
88
|
+
* How long option labels in the dropdown handle horizontal overflow.
|
|
89
|
+
* - `"truncate"` — clamp each label to a single line with an ellipsis.
|
|
90
|
+
* - `"wrap"` — allow labels to wrap onto multiple lines (rows grow taller).
|
|
91
|
+
* - `"scroll"` — keep labels on one line and scroll the list horizontally
|
|
92
|
+
* (legacy behavior). Web only — falls back to `"wrap"` on native, which
|
|
93
|
+
* cannot horizontally scroll a text row.
|
|
94
|
+
* @default "truncate"
|
|
95
|
+
*/
|
|
96
|
+
optionOverflow?: MultiSelectOptionOverflow;
|
|
97
|
+
/**
|
|
98
|
+
* When true, shows a search input at the top of the built-in dropdown that
|
|
99
|
+
* filters options in memory by their label as the user types. Mirrors the
|
|
100
|
+
* `searchable` behavior of the Select component. Web only — ignored on
|
|
101
|
+
* native and when `dropdownMenu` is false.
|
|
102
|
+
* @default false
|
|
103
|
+
*/
|
|
104
|
+
searchable?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Placeholder text for the search input. Only used when `searchable` is true.
|
|
107
|
+
* @default "Search"
|
|
108
|
+
*/
|
|
109
|
+
searchPlaceholder?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Message shown in the dropdown when the search filter matches no options.
|
|
112
|
+
* Only used when `searchable` is true.
|
|
113
|
+
* @default "No results"
|
|
114
|
+
*/
|
|
115
|
+
noOptionsMessage?: string;
|
|
86
116
|
/**
|
|
87
117
|
* When false, the built-in options list and backdrop are not shown and the control
|
|
88
118
|
* does not open on click. Use with an external picker (e.g. grouped select) wired
|
|
@@ -112,4 +142,4 @@ interface MultiSelectProps extends ThemeOverrideProps {
|
|
|
112
142
|
|
|
113
143
|
declare const MultiSelect: react.ForwardRefExoticComponent<MultiSelectProps & react.RefAttributes<HTMLDivElement>>;
|
|
114
144
|
|
|
115
|
-
export { MultiSelect, type MultiSelectOption, type MultiSelectProps, type MultiSelectSize, type MultiSelectState, type MultiSelectValue, type MultiSelectVariant };
|
|
145
|
+
export { MultiSelect, type MultiSelectOption, type MultiSelectOptionOverflow, type MultiSelectProps, type MultiSelectSize, type MultiSelectState, type MultiSelectValue, type MultiSelectVariant };
|
package/native/index.js
CHANGED
|
@@ -312,10 +312,6 @@ var isWeb = false;
|
|
|
312
312
|
// src/MultiSelect.tsx
|
|
313
313
|
var import_xui_core4 = require("@xsolla/xui-core");
|
|
314
314
|
|
|
315
|
-
// src/MultiSelectControl.tsx
|
|
316
|
-
var import_react8 = require("react");
|
|
317
|
-
var import_xui_core3 = require("@xsolla/xui-core");
|
|
318
|
-
|
|
319
315
|
// ../../foundation/icons-base/dist/web/index.mjs
|
|
320
316
|
var import_styled_components = __toESM(require("styled-components"), 1);
|
|
321
317
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
@@ -1094,6 +1090,13 @@ var ExclamationMarkCr = (props) => /* @__PURE__ */ (0, import_jsx_runtime152.jsx
|
|
|
1094
1090
|
var solidContent172 = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><g id="icon_remove--solid"><path id="Union" d="M20.5 3C20.7761 3 21 3.22386 21 3.5V20.5C21 20.7761 20.7761 21 20.5 21H3.5C3.22386 21 3 20.7761 3 20.5V3.5C3 3.22386 3.22386 3 3.5 3H20.5ZM12 10.5859L7.20703 5.79297L5.79297 7.20703L10.5859 12L5.79297 16.793L7.20703 18.207L12 13.4141L16.793 18.207L18.207 16.793L13.4141 12L18.207 7.20703L16.793 5.79297L12 10.5859Z" style="fill: currentColor"/></g></svg>`;
|
|
1095
1091
|
var lineContent172 = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><g id="icon_remove--line"><path id="Union" d="M20.0303 5.41455L13.4199 12.0249L20.0195 18.6245L18.6055 20.0386L12.0059 13.439L5.41406 20.0308L4 18.6167L10.5918 12.0249L4.00879 5.44189L5.42285 4.02783L12.0059 10.6108L18.6162 4.00049L20.0303 5.41455Z" style="fill: currentColor"/></g></svg>`;
|
|
1096
1092
|
var Remove = (props) => /* @__PURE__ */ (0, import_jsx_runtime176.jsx)(BaseIcon, { ...props, solidContent: solidContent172, lineContent: lineContent172 });
|
|
1093
|
+
var solidContent706 = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><g id="icon_search--solid"><path id="Union" d="M10.0215 2.02148C14.4398 2.02148 18.0215 5.60321 18.0215 10.0215C18.0215 11.8699 17.3926 13.5702 16.3398 14.9248L21.9785 20.5645L20.5645 21.9785L14.9248 16.3389C13.5702 17.3918 11.8701 18.0215 10.0215 18.0215C5.60321 18.0215 2.02148 14.4398 2.02148 10.0215C2.02148 5.60321 5.60321 2.02148 10.0215 2.02148ZM10.0215 4.02148C6.70778 4.02148 4.02148 6.70778 4.02148 10.0215C4.02148 13.3352 6.70778 16.0215 10.0215 16.0215C13.3352 16.0215 16.0215 13.3352 16.0215 10.0215C16.0215 6.70778 13.3352 4.02148 10.0215 4.02148ZM7.19336 7.19336C8.75546 5.63126 11.2875 5.63126 12.8496 7.19336C14.4117 8.75546 14.4117 11.2875 12.8496 12.8496C11.2875 14.4117 8.75546 14.4117 7.19336 12.8496C5.63126 11.2875 5.63127 8.75546 7.19336 7.19336Z" style="fill: currentColor"/></g></svg>`;
|
|
1094
|
+
var lineContent706 = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><g id="icon_search--line"><path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M10 2C14.4183 2 18 5.58172 18 10C18 11.8484 17.3711 13.5488 16.3184 14.9033L21.957 20.543L20.543 21.957L14.9033 16.3174C13.5487 17.3703 11.8486 18 10 18C5.58172 18 2 14.4183 2 10C2 5.58172 5.58172 2 10 2ZM10 4C6.68629 4 4 6.68629 4 10C4 13.3137 6.68629 16 10 16C13.3137 16 16 13.3137 16 10C16 6.68629 13.3137 4 10 4Z" style="fill: currentColor"/></g></svg>`;
|
|
1095
|
+
var Search = (props) => /* @__PURE__ */ (0, import_jsx_runtime710.jsx)(BaseIcon, { ...props, solidContent: solidContent706, lineContent: lineContent706 });
|
|
1096
|
+
|
|
1097
|
+
// src/MultiSelectControl.tsx
|
|
1098
|
+
var import_react8 = require("react");
|
|
1099
|
+
var import_xui_core3 = require("@xsolla/xui-core");
|
|
1097
1100
|
|
|
1098
1101
|
// src/useMultiSelectControl.tsx
|
|
1099
1102
|
var import_react7 = require("react");
|
|
@@ -2186,10 +2189,14 @@ var MultiSelect = (0, import_react10.forwardRef)(
|
|
|
2186
2189
|
iconLeft,
|
|
2187
2190
|
iconRight,
|
|
2188
2191
|
maxHeight = 300,
|
|
2192
|
+
optionOverflow = "truncate",
|
|
2189
2193
|
dropdownMenu = true,
|
|
2190
2194
|
onTriggerPress,
|
|
2191
2195
|
menuOpen = false,
|
|
2192
2196
|
menuMinWidth,
|
|
2197
|
+
searchable = false,
|
|
2198
|
+
searchPlaceholder = "Search",
|
|
2199
|
+
noOptionsMessage = "No results",
|
|
2193
2200
|
testID,
|
|
2194
2201
|
themeMode,
|
|
2195
2202
|
themeProductContext
|
|
@@ -2198,8 +2205,12 @@ var MultiSelect = (0, import_react10.forwardRef)(
|
|
|
2198
2205
|
const modalId = (0, import_xui_core4.useModalId)();
|
|
2199
2206
|
const controlRef = (0, import_react10.useRef)(null);
|
|
2200
2207
|
const menuRef = (0, import_react10.useRef)(null);
|
|
2208
|
+
const containerRef = (0, import_react10.useRef)(null);
|
|
2209
|
+
const searchInputRef = (0, import_react10.useRef)(null);
|
|
2201
2210
|
const [menuPosition, setMenuPosition] = (0, import_react10.useState)(null);
|
|
2211
|
+
const [searchValue, setSearchValue] = (0, import_react10.useState)("");
|
|
2202
2212
|
const sizeStyles = theme.sizing.input(size);
|
|
2213
|
+
const inputColors = theme.colors.control.input;
|
|
2203
2214
|
const state = externalState || (disabled ? "disable" : "default");
|
|
2204
2215
|
const isDisable = state === "disable" || disabled;
|
|
2205
2216
|
const isError = state === "error" || !!errorMessage;
|
|
@@ -2224,7 +2235,19 @@ var MultiSelect = (0, import_react10.forwardRef)(
|
|
|
2224
2235
|
onClose();
|
|
2225
2236
|
}
|
|
2226
2237
|
}, [isDisable, dropdownMenu, onClose]);
|
|
2227
|
-
|
|
2238
|
+
(0, import_react10.useEffect)(() => {
|
|
2239
|
+
if (isOpen && searchable && isWeb) {
|
|
2240
|
+
searchInputRef.current?.focus();
|
|
2241
|
+
}
|
|
2242
|
+
}, [isOpen, searchable]);
|
|
2243
|
+
(0, import_react10.useEffect)(() => {
|
|
2244
|
+
if (!isOpen) setSearchValue("");
|
|
2245
|
+
}, [isOpen]);
|
|
2246
|
+
const getOptionText = (opt) => typeof opt.label === "string" ? opt.label : String(opt.value);
|
|
2247
|
+
const filteredOptions = searchable && searchValue ? options.filter(
|
|
2248
|
+
(opt) => getOptionText(opt).toLowerCase().includes(searchValue.toLowerCase())
|
|
2249
|
+
) : options;
|
|
2250
|
+
const menuItems = filteredOptions.map((opt) => {
|
|
2228
2251
|
const id = String(opt.value);
|
|
2229
2252
|
const checked = values.map(String).includes(id);
|
|
2230
2253
|
return {
|
|
@@ -2242,6 +2265,22 @@ var MultiSelect = (0, import_react10.forwardRef)(
|
|
|
2242
2265
|
};
|
|
2243
2266
|
const controlMenuOpen = dropdownMenu ? isOpen : Boolean(menuOpen);
|
|
2244
2267
|
const controlOnClick = dropdownMenu ? onSelectClick : onTriggerPress;
|
|
2268
|
+
const isScrollOverflow = optionOverflow === "scroll";
|
|
2269
|
+
const isWrapOverflow = optionOverflow === "wrap";
|
|
2270
|
+
const optionListOverflowX = isScrollOverflow ? "auto" : "hidden";
|
|
2271
|
+
const optionRowMinWidth = isScrollOverflow ? void 0 : 0;
|
|
2272
|
+
const optionLabelNumberOfLines = optionOverflow === "truncate" ? 1 : void 0;
|
|
2273
|
+
const optionLabelStyle = isScrollOverflow ? { whiteSpace: "nowrap" } : isWrapOverflow ? {
|
|
2274
|
+
minWidth: 0,
|
|
2275
|
+
whiteSpace: "normal",
|
|
2276
|
+
overflowWrap: "break-word",
|
|
2277
|
+
wordBreak: "break-word"
|
|
2278
|
+
} : {
|
|
2279
|
+
minWidth: 0,
|
|
2280
|
+
whiteSpace: "nowrap",
|
|
2281
|
+
overflow: "hidden",
|
|
2282
|
+
textOverflow: "ellipsis"
|
|
2283
|
+
};
|
|
2245
2284
|
const updateMenuPosition = (0, import_react10.useCallback)(() => {
|
|
2246
2285
|
if (!isWeb || !controlRef.current) return;
|
|
2247
2286
|
const rect = controlRef.current.getBoundingClientRect();
|
|
@@ -2261,13 +2300,25 @@ var MultiSelect = (0, import_react10.forwardRef)(
|
|
|
2261
2300
|
window.removeEventListener("scroll", updateMenuPosition, true);
|
|
2262
2301
|
};
|
|
2263
2302
|
}, [controlMenuOpen, dropdownMenu, isDisable, updateMenuPosition]);
|
|
2303
|
+
(0, import_react10.useEffect)(() => {
|
|
2304
|
+
if (!isWeb || !dropdownMenu || !isOpen || isDisable) return;
|
|
2305
|
+
const handleOutsideMouseDown = (event) => {
|
|
2306
|
+
const path = typeof event.composedPath === "function" ? event.composedPath() : [];
|
|
2307
|
+
const targetNode = event.target;
|
|
2308
|
+
const isInside = (el) => !!el && (path.length ? path.includes(el) : !!targetNode && el.contains(targetNode));
|
|
2309
|
+
if (isInside(containerRef.current) || isInside(menuRef.current)) return;
|
|
2310
|
+
onClose();
|
|
2311
|
+
};
|
|
2312
|
+
document.addEventListener("mousedown", handleOutsideMouseDown);
|
|
2313
|
+
return () => document.removeEventListener("mousedown", handleOutsideMouseDown);
|
|
2314
|
+
}, [dropdownMenu, isOpen, isDisable, onClose]);
|
|
2264
2315
|
const externalFieldLayout = !dropdownMenu ? {
|
|
2265
2316
|
width: "100%",
|
|
2266
2317
|
minWidth: menuMinWidth ?? EXTERNAL_MENU_MIN_WIDTH_DEFAULT,
|
|
2267
2318
|
boxSizing: "border-box"
|
|
2268
2319
|
} : void 0;
|
|
2269
2320
|
const dropdown = dropdownMenu && isOpen && !isDisable && (!isWeb || menuPosition) && /* @__PURE__ */ (0, import_jsx_runtime734.jsxs)(import_jsx_runtime734.Fragment, { children: [
|
|
2270
|
-
/* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2321
|
+
!isWeb && /* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2271
2322
|
Box,
|
|
2272
2323
|
{
|
|
2273
2324
|
"data-modal-id": modalId,
|
|
@@ -2283,7 +2334,7 @@ var MultiSelect = (0, import_react10.forwardRef)(
|
|
|
2283
2334
|
onPress: onClose
|
|
2284
2335
|
}
|
|
2285
2336
|
),
|
|
2286
|
-
/* @__PURE__ */ (0, import_jsx_runtime734.
|
|
2337
|
+
/* @__PURE__ */ (0, import_jsx_runtime734.jsxs)(
|
|
2287
2338
|
Box,
|
|
2288
2339
|
{
|
|
2289
2340
|
ref: menuRef,
|
|
@@ -2292,7 +2343,6 @@ var MultiSelect = (0, import_react10.forwardRef)(
|
|
|
2292
2343
|
borderColor: theme.colors.border.secondary,
|
|
2293
2344
|
borderWidth: 1,
|
|
2294
2345
|
borderRadius: sizeStyles.radius,
|
|
2295
|
-
paddingVertical: 4,
|
|
2296
2346
|
style: {
|
|
2297
2347
|
position: isWeb ? "fixed" : "absolute",
|
|
2298
2348
|
top: isWeb ? menuPosition?.top : "100%",
|
|
@@ -2302,54 +2352,134 @@ var MultiSelect = (0, import_react10.forwardRef)(
|
|
|
2302
2352
|
marginTop: isWeb ? void 0 : 4,
|
|
2303
2353
|
zIndex: 1001,
|
|
2304
2354
|
// Above control (1000) and backdrop (999)
|
|
2305
|
-
boxShadow: theme.shadow.popover
|
|
2306
|
-
maxHeight,
|
|
2307
|
-
overflowY: "auto"
|
|
2355
|
+
boxShadow: theme.shadow.popover
|
|
2308
2356
|
},
|
|
2309
|
-
children:
|
|
2310
|
-
|
|
2311
|
-
const contentColors = theme.colors.content;
|
|
2312
|
-
return /* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2357
|
+
children: [
|
|
2358
|
+
searchable && isWeb && /* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2313
2359
|
Box,
|
|
2314
2360
|
{
|
|
2315
|
-
testID,
|
|
2316
2361
|
paddingHorizontal: sizeStyles.paddingHorizontal,
|
|
2317
|
-
paddingVertical:
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2362
|
+
paddingVertical: sizeStyles.paddingVertical,
|
|
2363
|
+
borderBottomWidth: 1,
|
|
2364
|
+
borderColor: theme.colors.border.secondary,
|
|
2365
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime734.jsxs)(
|
|
2366
|
+
Box,
|
|
2367
|
+
{
|
|
2368
|
+
flexDirection: "row",
|
|
2369
|
+
alignItems: "center",
|
|
2370
|
+
gap: sizeStyles.paddingHorizontal / 2,
|
|
2371
|
+
paddingHorizontal: 4,
|
|
2372
|
+
children: [
|
|
2373
|
+
/* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2374
|
+
Icon,
|
|
2375
|
+
{
|
|
2376
|
+
size: sizeStyles.iconSize - 2,
|
|
2377
|
+
color: inputColors.placeholder,
|
|
2378
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime734.jsx)(Search, {})
|
|
2379
|
+
}
|
|
2380
|
+
),
|
|
2381
|
+
/* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2382
|
+
Box,
|
|
2383
|
+
{
|
|
2384
|
+
as: "input",
|
|
2385
|
+
ref: searchInputRef,
|
|
2386
|
+
testID: testID ? `${testID}-search-input` : void 0,
|
|
2387
|
+
flex: 1,
|
|
2388
|
+
type: "text",
|
|
2389
|
+
"aria-label": searchPlaceholder,
|
|
2390
|
+
value: searchValue,
|
|
2391
|
+
onChange: (e) => setSearchValue(e.target.value),
|
|
2392
|
+
placeholder: searchPlaceholder,
|
|
2393
|
+
style: {
|
|
2394
|
+
border: "none",
|
|
2395
|
+
outline: "none",
|
|
2396
|
+
background: "transparent",
|
|
2397
|
+
color: inputColors.text,
|
|
2398
|
+
fontSize: sizeStyles.fontSize,
|
|
2399
|
+
width: "100%"
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
)
|
|
2403
|
+
]
|
|
2321
2404
|
}
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
} : void 0,
|
|
2405
|
+
)
|
|
2406
|
+
}
|
|
2407
|
+
),
|
|
2408
|
+
/* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2409
|
+
Box,
|
|
2410
|
+
{
|
|
2411
|
+
paddingVertical: 4,
|
|
2330
2412
|
style: {
|
|
2331
|
-
|
|
2332
|
-
|
|
2413
|
+
maxHeight: searchable ? maxHeight - 60 : maxHeight,
|
|
2414
|
+
overflowY: "auto",
|
|
2415
|
+
overflowX: optionListOverflowX
|
|
2333
2416
|
},
|
|
2334
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2335
|
-
|
|
2417
|
+
children: menuItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2418
|
+
Box,
|
|
2336
2419
|
{
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
children:
|
|
2420
|
+
paddingVertical: sizeStyles.paddingVertical * 2,
|
|
2421
|
+
paddingHorizontal: sizeStyles.paddingHorizontal,
|
|
2422
|
+
alignItems: "center",
|
|
2423
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2424
|
+
Text,
|
|
2425
|
+
{
|
|
2426
|
+
color: theme.colors.content.tertiary,
|
|
2427
|
+
fontSize: sizeStyles.fontSize,
|
|
2428
|
+
children: noOptionsMessage
|
|
2429
|
+
}
|
|
2430
|
+
)
|
|
2341
2431
|
}
|
|
2342
|
-
)
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2432
|
+
) : menuItems.map((item, _index) => {
|
|
2433
|
+
const brandColors = theme.colors.control.brand.primary;
|
|
2434
|
+
const contentColors = theme.colors.content;
|
|
2435
|
+
return /* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2436
|
+
Box,
|
|
2437
|
+
{
|
|
2438
|
+
testID,
|
|
2439
|
+
paddingHorizontal: sizeStyles.paddingHorizontal,
|
|
2440
|
+
paddingVertical: 8,
|
|
2441
|
+
onPress: () => {
|
|
2442
|
+
if (!item.disabled) {
|
|
2443
|
+
handleItemToggle(item.id, !item.checked);
|
|
2444
|
+
}
|
|
2445
|
+
},
|
|
2446
|
+
flexDirection: "row",
|
|
2447
|
+
alignItems: "center",
|
|
2448
|
+
justifyContent: "space-between",
|
|
2449
|
+
backgroundColor: item.checked ? brandColors.bg : "transparent",
|
|
2450
|
+
hoverStyle: !item.disabled && !item.checked ? {
|
|
2451
|
+
backgroundColor: theme.colors.control.input.bgHover
|
|
2452
|
+
} : void 0,
|
|
2453
|
+
style: {
|
|
2454
|
+
cursor: item.disabled ? "not-allowed" : "pointer",
|
|
2455
|
+
opacity: item.disabled ? 0.5 : 1,
|
|
2456
|
+
minWidth: optionRowMinWidth
|
|
2457
|
+
},
|
|
2458
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime734.jsx)(
|
|
2459
|
+
Text,
|
|
2460
|
+
{
|
|
2461
|
+
color: item.checked ? contentColors.on.brand : theme.colors.content.secondary,
|
|
2462
|
+
fontSize: sizeStyles.fontSize,
|
|
2463
|
+
fontWeight: "400",
|
|
2464
|
+
numberOfLines: optionLabelNumberOfLines,
|
|
2465
|
+
style: optionLabelStyle,
|
|
2466
|
+
children: item.children
|
|
2467
|
+
}
|
|
2468
|
+
)
|
|
2469
|
+
},
|
|
2470
|
+
item.id
|
|
2471
|
+
);
|
|
2472
|
+
})
|
|
2473
|
+
}
|
|
2474
|
+
)
|
|
2475
|
+
]
|
|
2347
2476
|
}
|
|
2348
2477
|
)
|
|
2349
2478
|
] });
|
|
2350
2479
|
return /* @__PURE__ */ (0, import_jsx_runtime734.jsxs)(
|
|
2351
2480
|
Box,
|
|
2352
2481
|
{
|
|
2482
|
+
ref: containerRef,
|
|
2353
2483
|
testID,
|
|
2354
2484
|
flexDirection: "column",
|
|
2355
2485
|
gap: sizeStyles.fieldGap,
|