@mantine/core
Version:
React components library focused on usability, accessibility and developer experience
222 lines (219 loc) • 7.4 kB
JavaScript
import React, { useState, useRef } from 'react';
import { rem } from '@mantine/styles';
import { useScrollIntoView } from '@mantine/hooks';
import { groupOptions } from '@mantine/utils';
import { SelectScrollArea } from '../../Select/SelectScrollArea/SelectScrollArea.js';
import { PaginationPreviousIcon, PaginationNextIcon, PaginationFirstIcon, PaginationLastIcon } from '../../Pagination/Pagination.icons.js';
import useStyles from './RenderList.styles.js';
import { UnstyledButton } from '../../UnstyledButton/UnstyledButton.js';
import { Divider } from '../../Divider/Divider.js';
import { Text } from '../../Text/Text.js';
import { TextInput } from '../../TextInput/TextInput.js';
import { ActionIcon } from '../../ActionIcon/ActionIcon.js';
const icons = {
Prev: PaginationPreviousIcon,
Next: PaginationNextIcon,
First: PaginationFirstIcon,
Last: PaginationLastIcon
};
const rtlIons = {
Next: PaginationPreviousIcon,
Prev: PaginationNextIcon,
Last: PaginationFirstIcon,
First: PaginationLastIcon
};
function RenderList({
className,
data,
onSelect,
selection,
itemComponent: ItemComponent,
listComponent,
transferIcon: TransferIcon,
transferAllIcon: TransferAllIcon,
transferAllMatchingFilter,
searchPlaceholder,
query,
onSearch,
filter,
nothingFound,
placeholder,
title,
showTransferAll,
reversed,
onMoveAll,
onMove,
height,
radius,
classNames,
styles,
limit,
unstyled,
variant
}) {
const { classes, cx, theme } = useStyles({ reversed, native: listComponent !== SelectScrollArea, radius }, { name: "TransferList", classNames, styles, unstyled, variant });
const unGroupedItems = [];
const groupedItems = [];
const [hovered, setHovered] = useState(-1);
const filteredData = data.filter((item) => filter(query, item)).slice(0, limit);
const ListComponent = listComponent || "div";
const Icons = theme.dir === "rtl" ? rtlIons : icons;
const itemsRefs = useRef({});
const sortedData = groupOptions({ data: filteredData });
const { scrollIntoView, targetRef, scrollableRef } = useScrollIntoView({
duration: 0,
offset: 5,
cancelable: false,
isList: true
});
let groupName = null;
sortedData.forEach((item, index) => {
const itemComponent = /* @__PURE__ */ React.createElement(UnstyledButton, {
unstyled,
tabIndex: -1,
onClick: () => onSelect(item.value),
key: item.value,
onMouseEnter: () => setHovered(index),
className: cx(classes.transferListItem, {
[classes.transferListItemHovered]: index === hovered
}),
ref: (node) => {
if (itemsRefs && itemsRefs.current) {
itemsRefs.current[item.value] = node;
}
}
}, /* @__PURE__ */ React.createElement(ItemComponent, {
data: item,
selected: selection.includes(item.value),
radius
}));
if (!item.group) {
unGroupedItems.push(itemComponent);
} else {
if (groupName !== item.group) {
groupName = item.group;
groupedItems.push(/* @__PURE__ */ React.createElement("div", {
className: classes.separator,
key: groupName
}, /* @__PURE__ */ React.createElement(Divider, {
classNames: { label: classes.separatorLabel },
label: groupName
})));
}
groupedItems.push(itemComponent);
}
});
if (groupedItems.length > 0 && unGroupedItems.length > 0) {
unGroupedItems.unshift(/* @__PURE__ */ React.createElement("div", {
className: classes.separator
}, /* @__PURE__ */ React.createElement(Divider, {
unstyled,
classNames: { label: classes.separatorLabel }
})));
}
const handleSearchKeydown = (event) => {
switch (event.key) {
case "Enter": {
event.preventDefault();
if (filteredData[hovered]) {
onSelect(filteredData[hovered].value);
}
break;
}
case "ArrowDown": {
event.preventDefault();
setHovered((current) => {
var _a;
const nextIndex = current < filteredData.length - 1 ? current + 1 : current;
targetRef.current = itemsRefs.current[(_a = filteredData[nextIndex]) == null ? void 0 : _a.value];
scrollIntoView({
alignment: "end"
});
return nextIndex;
});
break;
}
case "ArrowUp": {
event.preventDefault();
setHovered((current) => {
var _a;
const nextIndex = current > 0 ? current - 1 : current;
targetRef.current = itemsRefs.current[(_a = filteredData[nextIndex]) == null ? void 0 : _a.value];
scrollIntoView({
alignment: "start"
});
return nextIndex;
});
}
}
};
const transferIcon = reversed ? /* @__PURE__ */ React.createElement(Icons.Prev, {
size: "1rem"
}) : /* @__PURE__ */ React.createElement(Icons.Next, {
size: "1rem"
});
const transferAllIcon = reversed ? /* @__PURE__ */ React.createElement(Icons.First, {
size: "1rem"
}) : /* @__PURE__ */ React.createElement(Icons.Last, {
size: "1rem"
});
return /* @__PURE__ */ React.createElement("div", {
className: cx(classes.transferList, className)
}, title && /* @__PURE__ */ React.createElement(Text, {
weight: 500,
unstyled,
className: classes.transferListTitle
}, title), /* @__PURE__ */ React.createElement("div", {
className: classes.transferListBody
}, /* @__PURE__ */ React.createElement("div", {
className: classes.transferListHeader
}, /* @__PURE__ */ React.createElement(TextInput, {
unstyled,
value: query,
onChange: (event) => {
onSearch(event.currentTarget.value);
setHovered(0);
},
onFocus: () => setHovered(0),
onBlur: () => setHovered(-1),
placeholder: searchPlaceholder,
radius: 0,
onKeyDown: handleSearchKeydown,
sx: { flex: 1 },
classNames: { input: classes.transferListSearch }
}), /* @__PURE__ */ React.createElement(ActionIcon, {
variant: "default",
size: 36,
radius: 0,
className: classes.transferListControl,
disabled: selection.length === 0,
onClick: onMove,
unstyled
}, TransferIcon ? /* @__PURE__ */ React.createElement(TransferIcon, {
reversed
}) : transferIcon), showTransferAll && /* @__PURE__ */ React.createElement(ActionIcon, {
variant: "default",
size: 36,
radius: 0,
className: classes.transferListControl,
disabled: transferAllMatchingFilter ? filteredData.length === 0 : data.length === 0,
onClick: onMoveAll,
unstyled
}, TransferAllIcon ? /* @__PURE__ */ React.createElement(TransferAllIcon, {
reversed
}) : transferAllIcon)), /* @__PURE__ */ React.createElement(ListComponent, {
ref: scrollableRef,
onMouseLeave: () => setHovered(-1),
className: classes.transferListItems,
style: { height: rem(height), position: "relative", overflowX: "hidden" }
}, groupedItems.length > 0 || unGroupedItems.length > 0 ? /* @__PURE__ */ React.createElement(React.Fragment, null, groupedItems, unGroupedItems) : /* @__PURE__ */ React.createElement(Text, {
color: "dimmed",
unstyled,
size: "sm",
align: "center",
mt: "sm"
}, !query && placeholder ? placeholder : nothingFound))));
}
RenderList.displayName = "@mantine/core/RenderList";
export { RenderList };
//# sourceMappingURL=RenderList.js.map