eno-example/components/collapse.tsx

87 lines
2.5 KiB
TypeScript

import React from "react";
export default (props:{open:boolean, children:React.JSX.Element|React.JSX.Element[]})=>
{
const [openGet, openSet] = React.useState(props.open);
const [doneGet, doneSet] = React.useState(true);
const refEl:React.RefObject<HTMLElement> = React.useRef(null);
const refCollapse:React.RefObject<CollapseControls> = React.useRef(null);
React.useEffect(()=>
{
if(refEl.current)
{
refCollapse.current = Collapser(refEl.current, openGet);
}
return ()=> refCollapse.current && refCollapse.current();
}
, []);
const Handler =()=>
{
openSet((old)=>
{
if(refCollapse.current)
{
doneSet(false);
refCollapse.current(!old, 1000, ()=>doneSet(true));
}
return !old;
});
};
return <div>
<button onClick={Handler}>{openGet ? "Close this" : "Open it"}{doneGet ? "." : "..."}</button>
<div ref={refEl as React.RefObject<HTMLDivElement>}>
{!(!openGet && doneGet) && props.children}
</div>
</div>
};
type DoneCallback =(openState:boolean)=>void;
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);
/** @type Toggler */
return function(inOpen?:boolean, inMs?:number, inDone?:DoneCallback)
{
cancelAnimationFrame(frameRequest);
if(arguments.length)
{
userDone = inDone|| ((m)=>{}) as DoneCallback;
userMode = inOpen||!userMode;
inElement.style.height = inElement.clientHeight + "px";
inElement.style.overflow = "hidden";
inElement.style.transition = "none";
frameRequest = requestAnimationFrame(()=>
{
inElement.style.height = `${inOpen ? inElement.scrollHeight : 0}px`;
inElement.style.transition = `height ${(inMs||1000) / 1000}s`;
});
}
else
{
inElement.removeEventListener("transitionend", done);
}
} as CollapseControls;
}