import React, { useRef, useState, useEffect, useCallback } from "react"; import PropTypes from "prop-types"; import Column from "./Column"; import Footer from "./Footer"; import Header from "./Header"; import Body from "./Body"; import { FixedSizeList as List } from "react-window"; import InfiniteLoader from "react-window-infinite-loader"; import AutoSizer from "react-virtualized-auto-sizer"; import ReactTooltip from "react-tooltip"; import Checkbox from "@appserver/components/checkbox"; import Link from "@appserver/components/link"; import ComboBox from "@appserver/components/combobox"; import SearchInput from "@appserver/components/search-input"; import Loader from "@appserver/components/loader"; import Text from "@appserver/components/text"; import Tooltip from "@appserver/components/tooltip"; import CustomScrollbarsVirtualList from "@appserver/components/scrollbar/custom-scrollbars-virtual-list"; import HelpButton from "@appserver/components/help-button"; import StyledSelector from "./StyledSelector"; const convertGroups = (items) => { if (!items) return []; const wrappedGroups = items.map(convertGroup); return wrappedGroups; }; const convertGroup = (group) => { return { key: group.key, label: `${group.label} (${group.total})`, total: group.total, selected: 0, }; }; const getCurrentGroup = (items) => { const currentGroup = items.length > 0 ? items[0] : {}; return currentGroup; }; const Selector = (props) => { const { displayType, groups, selectButtonLabel, isDisabled, isMultiSelect, hasNextPage, options, isNextPageLoading, loadNextPage, selectedOptions, selectedGroups, groupsHeaderLabel, searchPlaceHolderLabel, emptySearchOptionsLabel, emptyOptionsLabel, loadingLabel, selectAllLabel, onSelect, getOptionTooltipContent, onSearchChanged, onGroupChanged, size, allowGroupSelection, embeddedComponent, showCounter, } = props; //console.log("options", options); //console.log("hasNextPage", hasNextPage); //console.log("isNextPageLoading", isNextPageLoading); const listOptionsRef = useRef(null); const listGroupsRef = useRef(null); useEffect(() => { Object.keys(currentGroup).length === 0 && setCurrentGroup(getCurrentGroup(convertGroups(groups))); resetCache(); }, [searchValue, currentGroup, hasNextPage]); const [selectedOptionList, setSelectedOptionList] = useState( selectedOptions || [] ); const [selectedGroupList, setSelectedGroupList] = useState( selectedGroups || [] ); const [searchValue, setSearchValue] = useState(""); const [selectedAll, setSelectedAll] = useState(false); const [currentGroup, setCurrentGroup] = useState( getCurrentGroup(convertGroups(groups)) ); // Every row is loaded except for our loading indicator row. const isItemLoaded = useCallback( (index) => { return !hasNextPage || index < options.length; }, [hasNextPage, options] ); const onOptionChange = useCallback( (e) => { const option = options[+e.target.value]; const newSelected = e.target.checked ? [option, ...selectedOptionList] : selectedOptionList.filter((el) => el.key !== option.key); setSelectedOptionList(newSelected); if (!option.groups) return; const newSelectedGroups = []; const removedSelectedGroups = []; if (e.target.checked) { option.groups.forEach((g) => { let index = selectedGroupList.findIndex((sg) => sg.key === g); if (index > -1) { // exists const selectedGroup = selectedGroupList[index]; const newSelected = selectedGroup.selected + 1; newSelectedGroups.push( Object.assign({}, selectedGroup, { selected: newSelected, }) ); } else { index = groups.findIndex((sg) => sg.key === g); if (index < 0) return; const notSelectedGroup = convertGroup(groups[index]); newSelectedGroups.push( Object.assign({}, notSelectedGroup, { selected: 1, }) ); } }); } else { option.groups.forEach((g) => { let index = selectedGroupList.findIndex((sg) => sg.key === g); if (index > -1) { // exists const selectedGroup = selectedGroupList[index]; const newSelected = selectedGroup.selected - 1; if (newSelected > 0) { newSelectedGroups.push( Object.assign({}, selectedGroup, { selected: newSelected, }) ); } else { removedSelectedGroups.push( Object.assign({}, selectedGroup, { selected: newSelected, }) ); } } }); } selectedGroupList.forEach((g) => { const indexNew = newSelectedGroups.findIndex((sg) => sg.key === g.key); if (indexNew === -1) { const indexRemoved = removedSelectedGroups.findIndex( (sg) => sg.key === g.key ); if (indexRemoved === -1) { newSelectedGroups.push(g); } } }); setSelectedGroupList(newSelectedGroups); }, [options, selectedOptionList, groups, selectedGroupList] ); const onGroupChange = useCallback( (e) => { const group = convertGroup(groups[+e.target.value]); group.selected = e.target.checked ? group.total : 0; const newSelectedGroups = e.target.checked ? [group, ...selectedGroupList] : selectedGroupList.filter((el) => el.key !== group.key); //console.log("onGroupChange", item); setSelectedGroupList(newSelectedGroups); onGroupSelect(group); if (e.target.checked) { //const newSelectedOptions = []; //options.forEach(o => o.groups.forEach(gKey => group.)) //setSelectedOptionList() //TODO: Implement setSelectedOptionList changes } }, [groups, selectedGroupList, currentGroup] ); const resetCache = useCallback(() => { if (listOptionsRef && listOptionsRef.current) { listOptionsRef.current.resetloadMoreItemsCache(true); } }, [listOptionsRef]); const onGroupSelect = useCallback( (group) => { if (!currentGroup || !group || currentGroup.key === group.key) { return; } setCurrentGroup(group); onGroupChanged && onGroupChanged(group); if (displayType === "aside" && isMultiSelect) { setSelectedAll(isGroupChecked(group)); } }, [displayType, isMultiSelect, currentGroup] ); const onSelectAllChange = useCallback(() => { const checked = !selectedAll; //console.log("onSelectAllChange", checked); setSelectedAll(checked); if (!currentGroup) return; const group = convertGroup(currentGroup); if (!group) return; group.selected = checked ? group.total : 0; const newSelectedGroups = checked ? [group, ...selectedGroupList] : selectedGroupList.filter((el) => el.key !== group.key); setSelectedGroupList(newSelectedGroups); }, [selectedAll, currentGroup, selectedGroupList]); const onSearchChange = useCallback((value) => { setSearchValue(value); onSearchChanged && onSearchChanged(value); }); const onSearchReset = useCallback(() => { onSearchChanged && onSearchChange(""); }); const onSelectOptions = (items) => { onSelect && onSelect(items); }; const isOptionChecked = useCallback( (option) => { const checked = selectedOptionList.findIndex((el) => el.key === option.key) > -1 || (option.groups && option.groups.filter((gKey) => { const selectedGroup = selectedGroupList.find( (sg) => sg.key === gKey ); if (!selectedGroup) return false; return selectedGroup.total === selectedGroup.selected; }).length > 0); return checked; }, [selectedOptionList, selectedGroupList] ); const onLinkClick = useCallback( (e) => { const index = e.target.dataset.index; if (!index) return; const option = options[index]; if (!option) return; onSelectOptions([option]); }, [options] ); const onAddClick = useCallback(() => { onSelectOptions(selectedOptionList); }, [selectedOptionList]); const renderOptionItem = useCallback( (index, style, option, isChecked, tooltipProps) => { return isMultiSelect ? (