import { DndContext, PointerSensor, useSensor, useSensors } from "@dnd-kit/core";
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
import { arrayMove, SortableContext, verticalListSortingStrategy, useSortable } from "@dnd-kit/sortable";
import { memo, useEffect, useMemo, useRef, useState } from "@wordpress/element";
import { CSS } from "@dnd-kit/utilities";
import "./dnd-select.scss";
const SortableItem = memo((props) => {
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: props.id });
const style = {
transform: CSS.Transform.toString(transform),
transition: transition,
};
const { item, onChange } = props;
// Delete Icon
const DeleteIcon = () => {
return (
);
};
return (
<>
{/* Display the label of the item */}
{item?.label}
{/* Delete button */}
onChange(item.id)}
>
>
);
});
const MultiSelectDndKit = ({ label = "", items, values, onChange, onInputChange = false, searchable = false }) => {
const [allOptions, setAllOptions] = useState([]);
const [toggleSelectField, setToggleSelectField] = useState(false);
const [optionOpen, setOptionOpen] = useState(true);
const [searchFieldData, setSearchFieldData] = useState("");
const dndRef = useRef(null);
const containerRef = useRef(null);
const allValues = useMemo(() => {
if (!Array.isArray(values) || values.some(item => !item.value) ) {return []}; // prevents the crash
return values.map(item => item.value);
}
, [values]);
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: { distance: 5 },
})
);
// icons
const ArrowIconOne = () => (
);
const ArrowIconTwo = () => (
);
const containerHeight = values?.length * 34 + 2 + "px";
const handleDragEnd = (event) => {
setToggleSelectField(false);
const { active, over } = event;
if (active.id !== over.id) {
const oldIndex = values.findIndex((i) => `${i.id}` === `${active.id}`);
const newIndex = values.findIndex((i) => `${i.id}` === `${over.id}`);
const updateValues = arrayMove(values, oldIndex, newIndex);
onChange(updateValues);
setOptionOpen(true);
}
};
const onDragStart = () => {
setOptionOpen(false);
setToggleSelectField(false);
};
const handleRemoveItems = (id) => {
const updatedValues = values?.filter((val) => val.id !== id);
onChange(updatedValues);
};
const toggleSelectFieldVal = () => {
if (optionOpen) {
setToggleSelectField((prev) => !prev);
}
};
const filterNotSelectedItems = (selectFieldItems) => {
const result = selectFieldItems?.filter((item) => !allValues.includes(item.value));
return result;
};
const handleSelectField = (data) => {
// const { id, value, label, slug, order } = data;
// const newValue = [ ...values, { id, value, label, slug, order } ];
const newValue = [...values, { ...data }];
setSearchFieldData("");
onChange(newValue);
};
const handleSearchField = (event) => {
const value = event.target.value.toLowerCase();
setSearchFieldData(value);
const searchableArray = filterNotSelectedItems(items);
const matchedOption = searchableArray?.filter((i) => i.label.toLowerCase().includes(value));
onInputChange ? onInputChange(value) : setAllOptions(matchedOption);
};
useEffect(() => {
let options = [];
if (toggleSelectField) {
options = filterNotSelectedItems(items);
} else {
options = [items];
}
setAllOptions(options);
}, [items, values, toggleSelectField]);
useEffect(() => {
const handleClickOutside = (event) => {
if (containerRef.current && !containerRef.current.contains(event.target)) {
setToggleSelectField(false);
}
};
if (toggleSelectField) {
document.addEventListener("click", handleClickOutside);
}
return () => {
document.removeEventListener("click", handleClickOutside);
};
}, [toggleSelectField]);
return (
`${item.id}`)}
strategy={verticalListSortingStrategy}
>
{values?.map((item, index) => (
))}
{toggleSelectField ? : }
{toggleSelectField && (
{searchable && (
handleSearchField(e)}
className="sp-select-search-field"
/>
)}
{allOptions?.length > 0 &&
allOptions?.map((option, i) => {
return (
{option?.label && option?.value && (
handleSelectField(option)}
className={`sp-select-option ${
values === option.value ? "selected" : ""
}`}
>
{option.label}
)}
);
})}
)}
);
};
export default memo(MultiSelectDndKit);