import React, { memo } from "react"; import { useTranslation } from "react-i18next"; import { nanoid } from "nanoid"; import styled from "styled-components"; import { useRecoilCallback } from "recoil"; import debounce from "lodash.debounce"; import { useItemBaseActions } from "react-sync-board"; import { search } from "../views/utils"; import Chevron from "./ui/Chevron"; const StyledItemList = styled.ul` display: flex; flex-flow: row wrap; list-style: none; margin: 0; padding: 0; & li.group { background-color: rgba(0, 0, 0, 0.1); padding: 0 0.5em; flex-basis: 100%; } overflow: visible; `; const StyledItem = styled.li` display: block; padding: 0.5em; margin: 0.2em; cursor: pointer; opacity: 0.7; &:hover { opacity: 1; } & > div { display: flex; flex-direction: column; align-items: center; pointer-events: none; max-width: 80px; & > span { margin-top: 0.2em; text-align: center; max-width: 80px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 0.2em 0.5em; } } &:hover > div > span { z-index: 2; max-width: none; overflow: visible; background-color: #222; box-shadow: 0px 3px 6px #00000029; } `; const size = 60; const NewItem = memo(({ type, template, component: Component, name }) => { const { pushItem } = useItemBaseActions(); const addItem = React.useCallback(async () => { pushItem({ ...template, id: nanoid(), type, }); }, [pushItem, template, type]); return (
{name}
); }); NewItem.displayName = "NewItem"; const SubItemList = ({ name, items }) => { const { t } = useTranslation(); const [open, setOpen] = React.useState(false); const { pushItems } = useItemBaseActions(); const addItems = useRecoilCallback( async (itemsToAdd) => { pushItems( itemsToAdd.map(({ template }) => ({ ...template, id: nanoid() })) ); }, [pushItems] ); return ( <>

setOpen((prev) => !prev)} style={{ cursor: "pointer" }} > {open ? ( ) : ( )}{" "} {name}{" "} { e.preventDefault(); e.stopPropagation(); addItems(items); }} > [{t("Add all")}]

{open && } ); }; const ItemList = ({ items }) => ( {items.map((node) => { if (node.type) { return ; } // it's a group return (
  • ); })}
    ); const MemoizedItemList = memo(ItemList); const filterItems = (filter, nodes) => nodes.reduce((acc, node) => { if (node.type) { if (search(filter, node.name)) { acc.push(node); } return acc; } const filteredItems = filterItems(filter, node.items); if (filteredItems.length) { acc.push({ ...node, items: filteredItems }); } return acc; }, []); const ItemLibrary = ({ items }) => { const { t } = useTranslation(); const [filter, setFilter] = React.useState(""); const [filteredItems, setFilteredItems] = React.useState(items); // eslint-disable-next-line react-hooks/exhaustive-deps const debouncedFilterItems = React.useCallback( debounce((filterToApply, itemsToFilter) => { setFilteredItems(filterItems(filterToApply, itemsToFilter)); }, 500), [] ); React.useEffect(() => { debouncedFilterItems(filter, items); }, [debouncedFilterItems, filter, items]); return ( <> setFilter(e.target.value)} style={{ marginBottom: "1em" }} placeholder={t("Search...")} /> ); }; export default memo(ItemLibrary);