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 = { Shut: `${base} h-0 top-0 w-1/2 duration-300`, Open: `${base} h-auto top-8 w-full duration-1000`, lol: `${base} h-auto top-36 bg-yellow-500 w-2/3 duration-700`, }; 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]); } } const initialKey = stateGet.open ? "Open" : "Shut"; React.useEffect(()=> { refElement.current?.setAttribute("style", ""); refControl.current = refElement.current && Collapser(refElement.current, initialKey, Classes); }, []); React.useEffect(()=> {refControl.current && refControl.current(initialKey, ()=>stateSet({done:true}))}, [stateGet.open]) return
} class={Classes.Shut} style="transition:none;"> { (!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;`); Object.entries(inClasses).forEach(([key, value])=> { inElement.setAttribute("class", value); output[key] = [value, inElement.offsetWidth, inElement.offsetHeight]; }); inElement.setAttribute("class", initialClass); inElement.offsetHeight; inElement.setAttribute("style", initialStyle); 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); }); } } }; const child =(e:CustomEvent)=> { if(e.target == inElement || !inTransition){ return; } console.log("resize", e.detail); const oldWidth = inElement.offsetWidth; const oldHeight = inElement.offsetHeight; //inElement.style.overflow = "hidden"; inElement.style.width = oldWidth + e.detail[0] + "px"; inElement.style.height = oldHeight + e.detail[1] + "px"; } inElement.addEventListener("transitionend", end); inElement.addEventListener("transitionrun", run); inElement.addEventListener("childresize", child); 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; const oldWidth = inElement.offsetWidth; const oldHeight = inElement.offsetHeight; //inElement.style.overflow = "hidden"; inElement.style.width = oldWidth + "px"; inElement.style.height = oldHeight + "px"; inTransition = true; console.log(`from: {${inElement.offsetWidth} ${inElement.offsetHeight}}`); inElement.dispatchEvent(new CustomEvent("childresize", {bubbles:true, detail:[width-oldWidth, height-oldHeight]})) frameRequest = requestAnimationFrame(()=> { inElement.style.height = height + "px"; inElement.style.width = width + "px"; inElement.className = classes; console.log(` to: {${width} ${height}}`) }); } } else { inElement.removeEventListener("transitionrun", run); inElement.removeEventListener("transitionend", end); } } as CollapseControls; }