@xsolla/xui-multi-select 0.216.1 → 0.217.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 +82 -3
- package/native/index.d.mts +43 -2
- package/native/index.d.ts +43 -2
- package/native/index.js +90 -16
- package/native/index.js.map +1 -1
- package/native/index.mjs +90 -16
- package/native/index.mjs.map +1 -1
- package/package.json +7 -7
- package/web/index.d.mts +43 -2
- package/web/index.d.ts +43 -2
- package/web/index.js +90 -16
- package/web/index.js.map +1 -1
- package/web/index.mjs +90 -16
- package/web/index.mjs.map +1 -1
package/web/index.js
CHANGED
|
@@ -464,7 +464,8 @@ var useMultiSelectControl = ({
|
|
|
464
464
|
stateList: stateList.map((item) => item.value),
|
|
465
465
|
variant,
|
|
466
466
|
size,
|
|
467
|
-
removeTagsButtons
|
|
467
|
+
removeTagsButtons,
|
|
468
|
+
lockedList: stateList.filter((item) => item.removable === false).map((item) => item.value)
|
|
468
469
|
}),
|
|
469
470
|
[stateList, variant, size, removeTagsButtons]
|
|
470
471
|
);
|
|
@@ -477,11 +478,18 @@ var useMultiSelectControl = ({
|
|
|
477
478
|
return true;
|
|
478
479
|
if (prev.stateList.length !== widthsDependencies.stateList.length)
|
|
479
480
|
return true;
|
|
481
|
+
if (prev.lockedList.length !== widthsDependencies.lockedList.length)
|
|
482
|
+
return true;
|
|
480
483
|
let i = 0;
|
|
481
484
|
for (const v of prev.stateList) {
|
|
482
485
|
if (v !== widthsDependencies.stateList[i]) return true;
|
|
483
486
|
i++;
|
|
484
487
|
}
|
|
488
|
+
let j = 0;
|
|
489
|
+
for (const v of prev.lockedList) {
|
|
490
|
+
if (v !== widthsDependencies.lockedList[j]) return true;
|
|
491
|
+
j++;
|
|
492
|
+
}
|
|
485
493
|
return false;
|
|
486
494
|
};
|
|
487
495
|
const setPlaceholder = () => {
|
|
@@ -535,26 +543,28 @@ var useMultiSelectControl = ({
|
|
|
535
543
|
}
|
|
536
544
|
const content = displayedItems.map((item, index2) => {
|
|
537
545
|
const isCount = item.value === COUNT_ITEM_VALUE;
|
|
546
|
+
const isLocked = !isCount && item.removable === false;
|
|
547
|
+
const canRemove = !isCount && !isLocked;
|
|
538
548
|
const isReducable = index2 === 0 && displayStateRef.current === 3 /* Render */ && displayedItemsCount === 0 || flexible;
|
|
539
549
|
if (variant === "tag") {
|
|
540
550
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
541
551
|
Box,
|
|
542
552
|
{
|
|
543
|
-
onPress:
|
|
553
|
+
onPress: canRemove ? (e) => {
|
|
544
554
|
e.stopPropagation();
|
|
545
555
|
if (!disabled) removeValue(item.value, e);
|
|
546
556
|
} : void 0,
|
|
547
557
|
style: {
|
|
548
558
|
minWidth: isReducable ? 0 : "fit-content",
|
|
549
559
|
opacity: disabled ? 0.5 : 1,
|
|
550
|
-
cursor: disabled || isCount ? "not-allowed" : "pointer"
|
|
560
|
+
cursor: isLocked ? "default" : disabled || isCount ? "not-allowed" : "pointer"
|
|
551
561
|
},
|
|
552
562
|
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
553
563
|
import_xui_tag.Tag,
|
|
554
564
|
{
|
|
555
565
|
size,
|
|
556
566
|
tone: "primary",
|
|
557
|
-
onRemove: removeTagsButtons &&
|
|
567
|
+
onRemove: removeTagsButtons && canRemove ? (e) => {
|
|
558
568
|
e?.stopPropagation();
|
|
559
569
|
if (!disabled) removeValue(item.value, e);
|
|
560
570
|
} : void 0,
|
|
@@ -813,6 +823,7 @@ var initialState = {
|
|
|
813
823
|
selectedItems: [],
|
|
814
824
|
isOpen: false
|
|
815
825
|
};
|
|
826
|
+
var isLockedOption = (option) => option.removable === false;
|
|
816
827
|
var useMultiSelect = ({
|
|
817
828
|
options,
|
|
818
829
|
value = [],
|
|
@@ -820,9 +831,13 @@ var useMultiSelect = ({
|
|
|
820
831
|
}) => {
|
|
821
832
|
const [state, setState] = (0, import_react5.useState)(initialState);
|
|
822
833
|
const optionsRef = (0, import_react5.useRef)(options);
|
|
834
|
+
const valuesRef = (0, import_react5.useRef)(value);
|
|
823
835
|
(0, import_react5.useEffect)(() => {
|
|
824
836
|
optionsRef.current = options;
|
|
825
837
|
}, [options]);
|
|
838
|
+
(0, import_react5.useEffect)(() => {
|
|
839
|
+
valuesRef.current = state.values;
|
|
840
|
+
}, [state.values]);
|
|
826
841
|
(0, import_react5.useEffect)(() => {
|
|
827
842
|
const selectedItems = options.filter((opt) => value.includes(opt.value));
|
|
828
843
|
setState((prevState) => ({
|
|
@@ -831,9 +846,15 @@ var useMultiSelect = ({
|
|
|
831
846
|
selectedItems
|
|
832
847
|
}));
|
|
833
848
|
}, [options, value]);
|
|
849
|
+
const getLockedSelectedValues = () => optionsRef.current.filter(
|
|
850
|
+
(opt) => isLockedOption(opt) && valuesRef.current.includes(opt.value)
|
|
851
|
+
).map((opt) => opt.value);
|
|
834
852
|
const onChoose = (0, import_react5.useCallback)(
|
|
835
853
|
(selectedIds) => {
|
|
836
|
-
const
|
|
854
|
+
const lockedValues = getLockedSelectedValues();
|
|
855
|
+
const newValues = optionsRef.current.filter(
|
|
856
|
+
(opt) => selectedIds.includes(String(opt.value)) || lockedValues.includes(opt.value)
|
|
857
|
+
).map((opt) => opt.value);
|
|
837
858
|
const newSelectedItems = optionsRef.current.filter(
|
|
838
859
|
(opt) => newValues.includes(opt.value)
|
|
839
860
|
);
|
|
@@ -849,6 +870,8 @@ var useMultiSelect = ({
|
|
|
849
870
|
const onRemove = (0, import_react5.useCallback)(
|
|
850
871
|
(value2, event) => {
|
|
851
872
|
event?.stopPropagation();
|
|
873
|
+
const option = optionsRef.current.find((opt) => opt.value === value2);
|
|
874
|
+
if (option && isLockedOption(option)) return;
|
|
852
875
|
setState((prev) => {
|
|
853
876
|
const newValues = prev.values.filter((v) => v !== value2);
|
|
854
877
|
const newSelectedItems = optionsRef.current.filter(
|
|
@@ -865,12 +888,16 @@ var useMultiSelect = ({
|
|
|
865
888
|
[onChange]
|
|
866
889
|
);
|
|
867
890
|
const onRemoveAll = (0, import_react5.useCallback)(() => {
|
|
891
|
+
const keptValues = getLockedSelectedValues();
|
|
892
|
+
const keptItems = optionsRef.current.filter(
|
|
893
|
+
(opt) => keptValues.includes(opt.value)
|
|
894
|
+
);
|
|
868
895
|
setState((prevState) => ({
|
|
869
896
|
...prevState,
|
|
870
|
-
values:
|
|
871
|
-
selectedItems:
|
|
897
|
+
values: keptValues,
|
|
898
|
+
selectedItems: keptItems
|
|
872
899
|
}));
|
|
873
|
-
onChange?.(
|
|
900
|
+
onChange?.(keptValues);
|
|
874
901
|
}, [onChange]);
|
|
875
902
|
const onSelectClick = (0, import_react5.useCallback)(() => {
|
|
876
903
|
setState((prevState) => ({
|
|
@@ -910,6 +937,8 @@ var Portal = ({ children }) => {
|
|
|
910
937
|
// src/MultiSelect.tsx
|
|
911
938
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
912
939
|
var EXTERNAL_MENU_MIN_WIDTH_DEFAULT = 540;
|
|
940
|
+
var SEARCH_ROW_HEIGHT = 60;
|
|
941
|
+
var SELECT_ALL_ROW_HEIGHT = 37;
|
|
913
942
|
var MultiSelect = (0, import_react7.forwardRef)(
|
|
914
943
|
({
|
|
915
944
|
options,
|
|
@@ -936,6 +965,9 @@ var MultiSelect = (0, import_react7.forwardRef)(
|
|
|
936
965
|
searchable = false,
|
|
937
966
|
searchPlaceholder = "Search",
|
|
938
967
|
noOptionsMessage = "No results",
|
|
968
|
+
selectAll = false,
|
|
969
|
+
selectAllLabel = "Select all",
|
|
970
|
+
deselectAllLabel = "Deselect all",
|
|
939
971
|
testID,
|
|
940
972
|
themeMode,
|
|
941
973
|
themeProductContext
|
|
@@ -987,22 +1019,33 @@ var MultiSelect = (0, import_react7.forwardRef)(
|
|
|
987
1019
|
const filteredOptions = searchable && searchValue ? options.filter(
|
|
988
1020
|
(opt) => getOptionText(opt).toLowerCase().includes(searchValue.toLowerCase())
|
|
989
1021
|
) : options;
|
|
1022
|
+
const selectedIds = values.map(String);
|
|
990
1023
|
const menuItems = filteredOptions.map((opt) => {
|
|
991
1024
|
const id = String(opt.value);
|
|
992
|
-
const checked =
|
|
1025
|
+
const checked = selectedIds.includes(id);
|
|
993
1026
|
return {
|
|
994
1027
|
id,
|
|
995
1028
|
children: opt.label,
|
|
996
1029
|
checked,
|
|
997
|
-
disabled: opt.disabled
|
|
1030
|
+
disabled: opt.disabled,
|
|
1031
|
+
locked: opt.removable === false
|
|
998
1032
|
};
|
|
999
1033
|
});
|
|
1000
1034
|
const handleItemToggle = (id, checked) => {
|
|
1001
|
-
const
|
|
1002
|
-
if (
|
|
1003
|
-
|
|
1035
|
+
const option = options.find((opt) => String(opt.value) === id);
|
|
1036
|
+
if (option === void 0) return;
|
|
1037
|
+
if (!checked && option.removable === false) return;
|
|
1038
|
+
const newValues = checked ? [...values, option.value] : values.filter((v) => v !== option.value);
|
|
1004
1039
|
onChoose(newValues.map(String));
|
|
1005
1040
|
};
|
|
1041
|
+
const selectAllTargets = filteredOptions.filter((opt) => !opt.disabled);
|
|
1042
|
+
const showSelectAll = selectAll && dropdownMenu && selectAllTargets.length > 0;
|
|
1043
|
+
const areAllTargetsSelected = showSelectAll && selectAllTargets.every((opt) => selectedIds.includes(String(opt.value)));
|
|
1044
|
+
const handleSelectAllToggle = () => {
|
|
1045
|
+
const targetIds = selectAllTargets.map((opt) => String(opt.value));
|
|
1046
|
+
const nextIds = areAllTargetsSelected ? selectedIds.filter((id) => !targetIds.includes(id)) : Array.from(/* @__PURE__ */ new Set([...selectedIds, ...targetIds]));
|
|
1047
|
+
onChoose(nextIds);
|
|
1048
|
+
};
|
|
1006
1049
|
const controlMenuOpen = dropdownMenu ? isOpen : Boolean(menuOpen);
|
|
1007
1050
|
const controlOnClick = dropdownMenu ? onSelectClick : onTriggerPress;
|
|
1008
1051
|
const isScrollOverflow = optionOverflow === "scroll";
|
|
@@ -1021,6 +1064,7 @@ var MultiSelect = (0, import_react7.forwardRef)(
|
|
|
1021
1064
|
overflow: "hidden",
|
|
1022
1065
|
textOverflow: "ellipsis"
|
|
1023
1066
|
};
|
|
1067
|
+
const optionsListMaxHeight = maxHeight - (searchable ? SEARCH_ROW_HEIGHT : 0) - (showSelectAll ? SELECT_ALL_ROW_HEIGHT : 0);
|
|
1024
1068
|
(0, import_react7.useEffect)(() => {
|
|
1025
1069
|
if (!isWeb || !controlMenuOpen || isDisable || !dropdownMenu) return;
|
|
1026
1070
|
let rafId = null;
|
|
@@ -1154,12 +1198,41 @@ var MultiSelect = (0, import_react7.forwardRef)(
|
|
|
1154
1198
|
)
|
|
1155
1199
|
}
|
|
1156
1200
|
),
|
|
1201
|
+
showSelectAll && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1202
|
+
Box,
|
|
1203
|
+
{
|
|
1204
|
+
role: "button",
|
|
1205
|
+
"aria-label": areAllTargetsSelected ? deselectAllLabel : selectAllLabel,
|
|
1206
|
+
testID: testID ? `${testID}-select-all` : void 0,
|
|
1207
|
+
paddingHorizontal: sizeStyles.paddingHorizontal,
|
|
1208
|
+
paddingVertical: 8,
|
|
1209
|
+
borderBottomWidth: 1,
|
|
1210
|
+
borderColor: theme.colors.border.secondary,
|
|
1211
|
+
flexDirection: "row",
|
|
1212
|
+
alignItems: "center",
|
|
1213
|
+
onPress: handleSelectAllToggle,
|
|
1214
|
+
hoverStyle: {
|
|
1215
|
+
backgroundColor: theme.colors.control.input.bgHover
|
|
1216
|
+
},
|
|
1217
|
+
style: { cursor: "pointer" },
|
|
1218
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1219
|
+
Text,
|
|
1220
|
+
{
|
|
1221
|
+
color: theme.colors.content.brand.primary,
|
|
1222
|
+
fontSize: sizeStyles.fontSize,
|
|
1223
|
+
fontWeight: "500",
|
|
1224
|
+
numberOfLines: 1,
|
|
1225
|
+
children: areAllTargetsSelected ? deselectAllLabel : selectAllLabel
|
|
1226
|
+
}
|
|
1227
|
+
)
|
|
1228
|
+
}
|
|
1229
|
+
),
|
|
1157
1230
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1158
1231
|
Box,
|
|
1159
1232
|
{
|
|
1160
1233
|
paddingVertical: 4,
|
|
1161
1234
|
style: {
|
|
1162
|
-
maxHeight:
|
|
1235
|
+
maxHeight: optionsListMaxHeight,
|
|
1163
1236
|
overflowY: "auto",
|
|
1164
1237
|
overflowX: optionListOverflowX,
|
|
1165
1238
|
scrollbarWidth: "none"
|
|
@@ -1182,6 +1255,7 @@ var MultiSelect = (0, import_react7.forwardRef)(
|
|
|
1182
1255
|
) : menuItems.map((item, _index) => {
|
|
1183
1256
|
const brandColors = theme.colors.control.brand.primary;
|
|
1184
1257
|
const contentColors = theme.colors.content;
|
|
1258
|
+
const isInert = item.locked && item.checked;
|
|
1185
1259
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1186
1260
|
Box,
|
|
1187
1261
|
{
|
|
@@ -1189,7 +1263,7 @@ var MultiSelect = (0, import_react7.forwardRef)(
|
|
|
1189
1263
|
paddingHorizontal: sizeStyles.paddingHorizontal,
|
|
1190
1264
|
paddingVertical: 8,
|
|
1191
1265
|
onPress: () => {
|
|
1192
|
-
if (!item.disabled) {
|
|
1266
|
+
if (!item.disabled && !isInert) {
|
|
1193
1267
|
handleItemToggle(item.id, !item.checked);
|
|
1194
1268
|
}
|
|
1195
1269
|
},
|
|
@@ -1201,7 +1275,7 @@ var MultiSelect = (0, import_react7.forwardRef)(
|
|
|
1201
1275
|
backgroundColor: theme.colors.control.input.bgHover
|
|
1202
1276
|
} : void 0,
|
|
1203
1277
|
style: {
|
|
1204
|
-
cursor: item.disabled ? "not-allowed" : "pointer",
|
|
1278
|
+
cursor: item.disabled ? "not-allowed" : isInert ? "default" : "pointer",
|
|
1205
1279
|
opacity: item.disabled ? 0.5 : 1,
|
|
1206
1280
|
minWidth: optionRowMinWidth
|
|
1207
1281
|
},
|