import {classNames, range} from "@/utils/helpers"; import BoxGraphic from "@/components/BoxGraphic"; import Xarrow from "react-xarrows"; import Chance from "chance"; import {useMemo, useState} from "react"; const chance = new Chance(); const colors = [ "#EF4444", "#F97316", "#F59E0B", "#EAB308", "#84CC16", "#22C55E", "#10B981", "#14B8A6", "#06B6D4", "#0EA5E9", "#3B82F6", "#6366F1", "#8B5CF6", "#A855F7", "#D946EF", "#EC4899", "#F43F5E", ] const smartShuffle = (n: number[]): number[] => { let shuffled: number[] | null = null; while (shuffled == null || shuffled.some((v, i) => v == i + 1)) { shuffled = chance.shuffle(n) } return shuffled; } const BoxTable = () => { const [count, setCount] = useState(16); const {sources, boxes, boxesBySource} = useMemo(() => { const sources: number[] = range(1, count); const destinations: number[] = smartShuffle(sources); const boxes: [number, number][] = sources.map((e, i) => [e, destinations[i] as number]); const boxesBySource: Record = Object.fromEntries(boxes); return {sources, destinations, boxes, boxesBySource}; }, [count]) const extractLoop = (start: number): number[] => { const results: number[] = []; results.push(start); while (true) { const last = results[results.length - 1] as number; const next = boxesBySource[last] as number; if (next == start) break; results.push(next) } return results; } const [filteredBoxes, setFilteredBoxes] = useState>(Object.fromEntries( sources.map(n => [n, false]) )); const [enabledLines, setEnabledLines] = useState>(Object.fromEntries( sources.map(n => [n, false]) )); const [isFiltered, setFiltered] = useState(false); const toggleLines = (from: number) => { hideLine(from); const newState = !isFiltered; setFiltered(newState); showLine(from, newState); const loop = Object.fromEntries(extractLoop(from).map(n => [n, false])); setFilteredBoxes((prev) => { const outsideLoop = Object.fromEntries(Object.entries(prev).map(([n]) => [n, newState])) return {...outsideLoop, ...loop}; }) } const showLine = (from: number, showLess: boolean | null = null) => { const loop = Object.fromEntries(extractLoop(from).slice(0, (showLess ?? isFiltered) ? 15 : 3).map(n => [n, true])); setEnabledLines((prev) => ({...prev, ...loop})) } const hideLine = (from: number) => { const loop = Object.fromEntries(extractLoop(from).slice(0, isFiltered ? 15 : 3).map(n => [n, false])); setEnabledLines((prev) => ({...prev, ...loop})) } const getColor = (seed: any) => { const chance = Chance(seed); return chance.pickone(colors); } const columns = Math.ceil(Math.sqrt(boxes.length)); return (
{boxes.map(([source, destination]) =>
toggleLines(source)} onMouseEnter={() => showLine(source)} onMouseLeave={() => hideLine(source)}>
{destination}
{source} {enabledLines[source] ? : null}
)}
); } export default BoxTable;