@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.mjs
CHANGED
|
@@ -448,7 +448,8 @@ var useMultiSelectControl = ({
|
|
|
448
448
|
stateList: stateList.map((item) => item.value),
|
|
449
449
|
variant,
|
|
450
450
|
size,
|
|
451
|
-
removeTagsButtons
|
|
451
|
+
removeTagsButtons,
|
|
452
|
+
lockedList: stateList.filter((item) => item.removable === false).map((item) => item.value)
|
|
452
453
|
}),
|
|
453
454
|
[stateList, variant, size, removeTagsButtons]
|
|
454
455
|
);
|
|
@@ -461,11 +462,18 @@ var useMultiSelectControl = ({
|
|
|
461
462
|
return true;
|
|
462
463
|
if (prev.stateList.length !== widthsDependencies.stateList.length)
|
|
463
464
|
return true;
|
|
465
|
+
if (prev.lockedList.length !== widthsDependencies.lockedList.length)
|
|
466
|
+
return true;
|
|
464
467
|
let i = 0;
|
|
465
468
|
for (const v of prev.stateList) {
|
|
466
469
|
if (v !== widthsDependencies.stateList[i]) return true;
|
|
467
470
|
i++;
|
|
468
471
|
}
|
|
472
|
+
let j = 0;
|
|
473
|
+
for (const v of prev.lockedList) {
|
|
474
|
+
if (v !== widthsDependencies.lockedList[j]) return true;
|
|
475
|
+
j++;
|
|
476
|
+
}
|
|
469
477
|
return false;
|
|
470
478
|
};
|
|
471
479
|
const setPlaceholder = () => {
|
|
@@ -519,26 +527,28 @@ var useMultiSelectControl = ({
|
|
|
519
527
|
}
|
|
520
528
|
const content = displayedItems.map((item, index2) => {
|
|
521
529
|
const isCount = item.value === COUNT_ITEM_VALUE;
|
|
530
|
+
const isLocked = !isCount && item.removable === false;
|
|
531
|
+
const canRemove = !isCount && !isLocked;
|
|
522
532
|
const isReducable = index2 === 0 && displayStateRef.current === 3 /* Render */ && displayedItemsCount === 0 || flexible;
|
|
523
533
|
if (variant === "tag") {
|
|
524
534
|
return /* @__PURE__ */ jsx4(
|
|
525
535
|
Box,
|
|
526
536
|
{
|
|
527
|
-
onPress:
|
|
537
|
+
onPress: canRemove ? (e) => {
|
|
528
538
|
e.stopPropagation();
|
|
529
539
|
if (!disabled) removeValue(item.value, e);
|
|
530
540
|
} : void 0,
|
|
531
541
|
style: {
|
|
532
542
|
minWidth: isReducable ? 0 : "fit-content",
|
|
533
543
|
opacity: disabled ? 0.5 : 1,
|
|
534
|
-
cursor: disabled || isCount ? "not-allowed" : "pointer"
|
|
544
|
+
cursor: isLocked ? "default" : disabled || isCount ? "not-allowed" : "pointer"
|
|
535
545
|
},
|
|
536
546
|
children: /* @__PURE__ */ jsx4(
|
|
537
547
|
Tag,
|
|
538
548
|
{
|
|
539
549
|
size,
|
|
540
550
|
tone: "primary",
|
|
541
|
-
onRemove: removeTagsButtons &&
|
|
551
|
+
onRemove: removeTagsButtons && canRemove ? (e) => {
|
|
542
552
|
e?.stopPropagation();
|
|
543
553
|
if (!disabled) removeValue(item.value, e);
|
|
544
554
|
} : void 0,
|
|
@@ -797,6 +807,7 @@ var initialState = {
|
|
|
797
807
|
selectedItems: [],
|
|
798
808
|
isOpen: false
|
|
799
809
|
};
|
|
810
|
+
var isLockedOption = (option) => option.removable === false;
|
|
800
811
|
var useMultiSelect = ({
|
|
801
812
|
options,
|
|
802
813
|
value = [],
|
|
@@ -804,9 +815,13 @@ var useMultiSelect = ({
|
|
|
804
815
|
}) => {
|
|
805
816
|
const [state, setState] = useState2(initialState);
|
|
806
817
|
const optionsRef = useRef3(options);
|
|
818
|
+
const valuesRef = useRef3(value);
|
|
807
819
|
useEffect2(() => {
|
|
808
820
|
optionsRef.current = options;
|
|
809
821
|
}, [options]);
|
|
822
|
+
useEffect2(() => {
|
|
823
|
+
valuesRef.current = state.values;
|
|
824
|
+
}, [state.values]);
|
|
810
825
|
useEffect2(() => {
|
|
811
826
|
const selectedItems = options.filter((opt) => value.includes(opt.value));
|
|
812
827
|
setState((prevState) => ({
|
|
@@ -815,9 +830,15 @@ var useMultiSelect = ({
|
|
|
815
830
|
selectedItems
|
|
816
831
|
}));
|
|
817
832
|
}, [options, value]);
|
|
833
|
+
const getLockedSelectedValues = () => optionsRef.current.filter(
|
|
834
|
+
(opt) => isLockedOption(opt) && valuesRef.current.includes(opt.value)
|
|
835
|
+
).map((opt) => opt.value);
|
|
818
836
|
const onChoose = useCallback(
|
|
819
837
|
(selectedIds) => {
|
|
820
|
-
const
|
|
838
|
+
const lockedValues = getLockedSelectedValues();
|
|
839
|
+
const newValues = optionsRef.current.filter(
|
|
840
|
+
(opt) => selectedIds.includes(String(opt.value)) || lockedValues.includes(opt.value)
|
|
841
|
+
).map((opt) => opt.value);
|
|
821
842
|
const newSelectedItems = optionsRef.current.filter(
|
|
822
843
|
(opt) => newValues.includes(opt.value)
|
|
823
844
|
);
|
|
@@ -833,6 +854,8 @@ var useMultiSelect = ({
|
|
|
833
854
|
const onRemove = useCallback(
|
|
834
855
|
(value2, event) => {
|
|
835
856
|
event?.stopPropagation();
|
|
857
|
+
const option = optionsRef.current.find((opt) => opt.value === value2);
|
|
858
|
+
if (option && isLockedOption(option)) return;
|
|
836
859
|
setState((prev) => {
|
|
837
860
|
const newValues = prev.values.filter((v) => v !== value2);
|
|
838
861
|
const newSelectedItems = optionsRef.current.filter(
|
|
@@ -849,12 +872,16 @@ var useMultiSelect = ({
|
|
|
849
872
|
[onChange]
|
|
850
873
|
);
|
|
851
874
|
const onRemoveAll = useCallback(() => {
|
|
875
|
+
const keptValues = getLockedSelectedValues();
|
|
876
|
+
const keptItems = optionsRef.current.filter(
|
|
877
|
+
(opt) => keptValues.includes(opt.value)
|
|
878
|
+
);
|
|
852
879
|
setState((prevState) => ({
|
|
853
880
|
...prevState,
|
|
854
|
-
values:
|
|
855
|
-
selectedItems:
|
|
881
|
+
values: keptValues,
|
|
882
|
+
selectedItems: keptItems
|
|
856
883
|
}));
|
|
857
|
-
onChange?.(
|
|
884
|
+
onChange?.(keptValues);
|
|
858
885
|
}, [onChange]);
|
|
859
886
|
const onSelectClick = useCallback(() => {
|
|
860
887
|
setState((prevState) => ({
|
|
@@ -894,6 +921,8 @@ var Portal = ({ children }) => {
|
|
|
894
921
|
// src/MultiSelect.tsx
|
|
895
922
|
import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
896
923
|
var EXTERNAL_MENU_MIN_WIDTH_DEFAULT = 540;
|
|
924
|
+
var SEARCH_ROW_HEIGHT = 60;
|
|
925
|
+
var SELECT_ALL_ROW_HEIGHT = 37;
|
|
897
926
|
var MultiSelect = forwardRef2(
|
|
898
927
|
({
|
|
899
928
|
options,
|
|
@@ -920,6 +949,9 @@ var MultiSelect = forwardRef2(
|
|
|
920
949
|
searchable = false,
|
|
921
950
|
searchPlaceholder = "Search",
|
|
922
951
|
noOptionsMessage = "No results",
|
|
952
|
+
selectAll = false,
|
|
953
|
+
selectAllLabel = "Select all",
|
|
954
|
+
deselectAllLabel = "Deselect all",
|
|
923
955
|
testID,
|
|
924
956
|
themeMode,
|
|
925
957
|
themeProductContext
|
|
@@ -971,22 +1003,33 @@ var MultiSelect = forwardRef2(
|
|
|
971
1003
|
const filteredOptions = searchable && searchValue ? options.filter(
|
|
972
1004
|
(opt) => getOptionText(opt).toLowerCase().includes(searchValue.toLowerCase())
|
|
973
1005
|
) : options;
|
|
1006
|
+
const selectedIds = values.map(String);
|
|
974
1007
|
const menuItems = filteredOptions.map((opt) => {
|
|
975
1008
|
const id = String(opt.value);
|
|
976
|
-
const checked =
|
|
1009
|
+
const checked = selectedIds.includes(id);
|
|
977
1010
|
return {
|
|
978
1011
|
id,
|
|
979
1012
|
children: opt.label,
|
|
980
1013
|
checked,
|
|
981
|
-
disabled: opt.disabled
|
|
1014
|
+
disabled: opt.disabled,
|
|
1015
|
+
locked: opt.removable === false
|
|
982
1016
|
};
|
|
983
1017
|
});
|
|
984
1018
|
const handleItemToggle = (id, checked) => {
|
|
985
|
-
const
|
|
986
|
-
if (
|
|
987
|
-
|
|
1019
|
+
const option = options.find((opt) => String(opt.value) === id);
|
|
1020
|
+
if (option === void 0) return;
|
|
1021
|
+
if (!checked && option.removable === false) return;
|
|
1022
|
+
const newValues = checked ? [...values, option.value] : values.filter((v) => v !== option.value);
|
|
988
1023
|
onChoose(newValues.map(String));
|
|
989
1024
|
};
|
|
1025
|
+
const selectAllTargets = filteredOptions.filter((opt) => !opt.disabled);
|
|
1026
|
+
const showSelectAll = selectAll && dropdownMenu && selectAllTargets.length > 0;
|
|
1027
|
+
const areAllTargetsSelected = showSelectAll && selectAllTargets.every((opt) => selectedIds.includes(String(opt.value)));
|
|
1028
|
+
const handleSelectAllToggle = () => {
|
|
1029
|
+
const targetIds = selectAllTargets.map((opt) => String(opt.value));
|
|
1030
|
+
const nextIds = areAllTargetsSelected ? selectedIds.filter((id) => !targetIds.includes(id)) : Array.from(/* @__PURE__ */ new Set([...selectedIds, ...targetIds]));
|
|
1031
|
+
onChoose(nextIds);
|
|
1032
|
+
};
|
|
990
1033
|
const controlMenuOpen = dropdownMenu ? isOpen : Boolean(menuOpen);
|
|
991
1034
|
const controlOnClick = dropdownMenu ? onSelectClick : onTriggerPress;
|
|
992
1035
|
const isScrollOverflow = optionOverflow === "scroll";
|
|
@@ -1005,6 +1048,7 @@ var MultiSelect = forwardRef2(
|
|
|
1005
1048
|
overflow: "hidden",
|
|
1006
1049
|
textOverflow: "ellipsis"
|
|
1007
1050
|
};
|
|
1051
|
+
const optionsListMaxHeight = maxHeight - (searchable ? SEARCH_ROW_HEIGHT : 0) - (showSelectAll ? SELECT_ALL_ROW_HEIGHT : 0);
|
|
1008
1052
|
useEffect4(() => {
|
|
1009
1053
|
if (!isWeb || !controlMenuOpen || isDisable || !dropdownMenu) return;
|
|
1010
1054
|
let rafId = null;
|
|
@@ -1138,12 +1182,41 @@ var MultiSelect = forwardRef2(
|
|
|
1138
1182
|
)
|
|
1139
1183
|
}
|
|
1140
1184
|
),
|
|
1185
|
+
showSelectAll && /* @__PURE__ */ jsx6(
|
|
1186
|
+
Box,
|
|
1187
|
+
{
|
|
1188
|
+
role: "button",
|
|
1189
|
+
"aria-label": areAllTargetsSelected ? deselectAllLabel : selectAllLabel,
|
|
1190
|
+
testID: testID ? `${testID}-select-all` : void 0,
|
|
1191
|
+
paddingHorizontal: sizeStyles.paddingHorizontal,
|
|
1192
|
+
paddingVertical: 8,
|
|
1193
|
+
borderBottomWidth: 1,
|
|
1194
|
+
borderColor: theme.colors.border.secondary,
|
|
1195
|
+
flexDirection: "row",
|
|
1196
|
+
alignItems: "center",
|
|
1197
|
+
onPress: handleSelectAllToggle,
|
|
1198
|
+
hoverStyle: {
|
|
1199
|
+
backgroundColor: theme.colors.control.input.bgHover
|
|
1200
|
+
},
|
|
1201
|
+
style: { cursor: "pointer" },
|
|
1202
|
+
children: /* @__PURE__ */ jsx6(
|
|
1203
|
+
Text,
|
|
1204
|
+
{
|
|
1205
|
+
color: theme.colors.content.brand.primary,
|
|
1206
|
+
fontSize: sizeStyles.fontSize,
|
|
1207
|
+
fontWeight: "500",
|
|
1208
|
+
numberOfLines: 1,
|
|
1209
|
+
children: areAllTargetsSelected ? deselectAllLabel : selectAllLabel
|
|
1210
|
+
}
|
|
1211
|
+
)
|
|
1212
|
+
}
|
|
1213
|
+
),
|
|
1141
1214
|
/* @__PURE__ */ jsx6(
|
|
1142
1215
|
Box,
|
|
1143
1216
|
{
|
|
1144
1217
|
paddingVertical: 4,
|
|
1145
1218
|
style: {
|
|
1146
|
-
maxHeight:
|
|
1219
|
+
maxHeight: optionsListMaxHeight,
|
|
1147
1220
|
overflowY: "auto",
|
|
1148
1221
|
overflowX: optionListOverflowX,
|
|
1149
1222
|
scrollbarWidth: "none"
|
|
@@ -1166,6 +1239,7 @@ var MultiSelect = forwardRef2(
|
|
|
1166
1239
|
) : menuItems.map((item, _index) => {
|
|
1167
1240
|
const brandColors = theme.colors.control.brand.primary;
|
|
1168
1241
|
const contentColors = theme.colors.content;
|
|
1242
|
+
const isInert = item.locked && item.checked;
|
|
1169
1243
|
return /* @__PURE__ */ jsx6(
|
|
1170
1244
|
Box,
|
|
1171
1245
|
{
|
|
@@ -1173,7 +1247,7 @@ var MultiSelect = forwardRef2(
|
|
|
1173
1247
|
paddingHorizontal: sizeStyles.paddingHorizontal,
|
|
1174
1248
|
paddingVertical: 8,
|
|
1175
1249
|
onPress: () => {
|
|
1176
|
-
if (!item.disabled) {
|
|
1250
|
+
if (!item.disabled && !isInert) {
|
|
1177
1251
|
handleItemToggle(item.id, !item.checked);
|
|
1178
1252
|
}
|
|
1179
1253
|
},
|
|
@@ -1185,7 +1259,7 @@ var MultiSelect = forwardRef2(
|
|
|
1185
1259
|
backgroundColor: theme.colors.control.input.bgHover
|
|
1186
1260
|
} : void 0,
|
|
1187
1261
|
style: {
|
|
1188
|
-
cursor: item.disabled ? "not-allowed" : "pointer",
|
|
1262
|
+
cursor: item.disabled ? "not-allowed" : isInert ? "default" : "pointer",
|
|
1189
1263
|
opacity: item.disabled ? 0.5 : 1,
|
|
1190
1264
|
minWidth: optionRowMinWidth
|
|
1191
1265
|
},
|