diff --git a/app.tsx b/app.tsx index 12c0a66..f582e7e 100644 --- a/app.tsx +++ b/app.tsx @@ -1,7 +1,7 @@ import React from "react"; import * as Iso from "@eno/iso"; import Collapse, {CollapseButton, CollapseGroup} from "$/collapse.tsx"; -import C from "./context.tsx"; +import * as C from "./context.tsx"; const Comp = React.lazy(()=>import("./deep/component.tsx")); @@ -22,7 +22,30 @@ export default ()=> - + + + +

hello!

+

hello!

+

hello!

+

hello!

+ + + +

hello!

+

hello!

+

hello!

+

hello!

+

hello!

+

hello!

+

hello!

+
+
+

hello!

+

hello!

+

hello!

+
+
diff --git a/context.tsx b/context.tsx index a8e848e..52e8d92 100644 --- a/context.tsx +++ b/context.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { CollapseControls, Collapser } from "./components/collapse.tsx"; type StateArgs = {done?:boolean, open?:boolean}; type StateObj = {done:boolean, open:boolean}; @@ -7,52 +6,82 @@ type StateBinding = [state:StateObj, update:(args:StateArgs)=>void]; const CTX = React.createContext([{done:true, open:false}, (args)=>{}] as StateBinding); -export default ()=> +export const Group =(props:{children:React.JSX.Element|React.JSX.Element[]})=> { const [stateGet, stateSet] = React.useState({done:true, open:false} as StateObj); - const setter =(args:StateArgs)=> stateSet({...stateGet, ...args}); + return stateSet({...stateGet, ...args})]}>{props.children}; +}; - return - - ; -} - -const Inner =()=> +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 ); - React.useEffect(()=> - { - refControl.current = Collapser(refElement.current as HTMLElement, true); - } - , []); + React.useEffect(()=>refControl.current = Collapser(refElement.current as HTMLElement, true), []); + React.useEffect(()=> {refControl.current && refControl.current(stateGet.open, 1000, ()=>stateSet({done:true}))}, [stateGet.open]) - React.useEffect(()=> - { - console.log("open changed to:", stateGet.open); - refControl.current && refControl.current(stateGet.open, 1000, ()=>stateSet({done:true})); + return
}>{props.children}
; +}; - }, [stateGet.open]) - - React.useEffect(()=>{ - console.log("done changed to:", stateGet.done); - }, [stateGet.done]) - - return
+export const Button =()=> +{ + const [stateGet, stateSet] = React.useContext(CTX); + return <>

{JSON.stringify(stateGet)}

-
}> -

hello

-

hello

-

hello

-

hello

-

hello

-

hello

-

hello

-
-
; - + ; }; + +type DoneCallback =(openState:boolean)=>void; +export type CollapseControls =(inOpen?:boolean, inMs?:number, inDone?:DoneCallback)=>void; +export function Collapser(inElement:HTMLElement, initialState = false) +{ + let userDone:DoneCallback = (openState) => {}; + let userMode = initialState; + let frameRequest = 0; + + const done = (inEvent:TransitionEvent)=> + { + if (inEvent.propertyName == "height" && inEvent.target == inElement) + { + inEvent.stopPropagation(); + if (userMode) + { + inElement.style.height = "auto"; + inElement.style.overflow = "visible"; + } + userDone(userMode); + } + }; + inElement.addEventListener("transitionend", done); + + return function(inOpen, inMs, inDone) + { + cancelAnimationFrame(frameRequest); + + if(arguments.length) + { + userDone = inDone|| ((m)=>{}) as DoneCallback; + userMode = inOpen === true; + + inElement.style.height = inElement.clientHeight + "px"; + inElement.style.overflow = "hidden"; + inElement.style.transition = "none"; + frameRequest = requestAnimationFrame(()=> + { + inElement.style.transition = `height ${(inMs||1000) / 1000}s`; + frameRequest = requestAnimationFrame(()=> + { + inElement.style.height = `${inOpen ? inElement.scrollHeight : 0}px`; + }); + + }); + } + else + { + inElement.removeEventListener("transitionend", done); + } + } as CollapseControls; +} \ No newline at end of file