import React from "react"; type StateArgs = {done?:boolean, open?:boolean}; type StateObj = {done:boolean, open:boolean}; type StateBinding = [state:StateObj, update:(args:StateArgs)=>void]; const CTX = React.createContext([{done:true, open:false}, (args)=>{}] as StateBinding); export const Group =(props:{children:React.JSX.Element|React.JSX.Element[]})=> { const [stateGet, stateSet] = React.useState({done:true, open:false} as StateObj); return stateSet({...stateGet, ...args})]}>{props.children}; }; export const Menu =(props:{children:React.JSX.Element|React.JSX.Element[]})=> { const [stateGet, stateSet] = React.useContext(CTX); const refElement:React.MutableRefObject = React.useRef( null ); const refControl:React.MutableRefObject = React.useRef( null ); type MenuClassStates = {Keep:string, Open:string, Shut:string, Move:string, Exit:string}; const base = `relative transition-all border(8 black) overflow-hidden`; const Classes:MenuClassStates = { Open: `${base} h-auto top-8 duration-700`, Shut: `${base} h-0 top-0 duration-300`, }; const Window = window as {TwindInst?:(c:string)=>string}; if(Window.TwindInst) { for(let stateName in Classes) { Classes[stateName as keyof MenuClassStates] = Window.TwindInst(Classes[stateName as keyof MenuClassStates]); } } React.useEffect(()=>refControl.current = Collapser(refElement.current as HTMLElement, "Shut", Classes), []); React.useEffect(()=> {refControl.current && refControl.current(stateGet.open ? "Open" : "Shut", ()=>stateSet({done:true}))}, [stateGet.open]) return
}> { (!stateGet.open && stateGet.done) ? null : props.children}
; }; export const Button =()=> { const [stateGet, stateSet] = React.useContext(CTX); return <>

{JSON.stringify(stateGet)}

; }; type StyleSize = [classes:string, width:number, height:number]; type StylePack = Record; type StyleCalc = Record; const StyleCalc =(inElement:HTMLElement, inClasses:StylePack)=> { const initialStyle = inElement.getAttribute("style")||""; const initialClass = inElement.getAttribute("class")||""; const output = {} as StyleCalc; inElement.setAttribute("style", `transition: none; overflow: hidden;`); inElement.clientHeight; Object.entries(inClasses).forEach(([key, value])=> { inElement.setAttribute("class", value); inElement.clientHeight; output[key] = [value, inElement.offsetWidth, inElement.offsetHeight]; }); inElement.setAttribute("style", initialStyle); inElement.setAttribute("class", initialClass); inElement.clientHeight; return output; }; type DoneCallback =(openState:boolean)=>void; export type CollapseControls =(inOpen?:string, inDone?:DoneCallback)=>void; export function Collapser(inElement:HTMLElement, initialState:string, library:Record) { let userDone:DoneCallback = (openState) => {}; let userMode = initialState; let frameRequest = 0; let inTransition = false; let measurements:StyleCalc; const transitions:Set = new Set(); const run = (inEvent:TransitionEvent)=> { console.log("+", inEvent.propertyName); (inEvent.target == inElement) && transitions.add(inEvent.propertyName); }; const end = (inEvent:TransitionEvent)=> { console.log("-", inEvent.propertyName); if (inEvent.target == inElement) { transitions.delete(inEvent.propertyName); if(transitions.size === 0) { console.log("--done--", userMode); inElement.setAttribute("style", "transition:none;"); inElement.clientHeight; frameRequest = requestAnimationFrame(()=> { inElement.setAttribute("style", ""); inTransition = false; userDone(userMode); }); } } }; inElement.addEventListener("transitionend", end); inElement.addEventListener("transitionrun", run); return function(inState:string, inDone) { cancelAnimationFrame(frameRequest); if(arguments.length) { if(!library[inState]){ return; } userDone = inDone|| ((m)=>{}) as DoneCallback; userMode = inState; if(!inTransition) { measurements = StyleCalc(inElement, library); console.log("measurements taken", measurements) } if(measurements) { const [classes, width, height] = measurements[inState] as StyleSize; inElement.style.overflow = "hidden"; inElement.style.width = inElement.offsetWidth + "px"; inElement.style.height = inElement.offsetHeight + "px"; inTransition = true; console.log(`from: {${inElement.offsetWidth} ${inElement.offsetHeight}}`) frameRequest = requestAnimationFrame(()=> { inElement.style.width = width + "px"; inElement.style.height = height + "px"; inElement.className = classes; console.log(` to: {${width} ${height}}`) }); } } else { inElement.removeEventListener("transitionrun", run); inElement.removeEventListener("transitionend", end); } } as CollapseControls; }