import { html } from "htm"; import * as React from "preact"; /** @typedef {({children, classes}:{children:preact.VNode, classes:string|null})=>preact.VNode} BasicElement */ /** @typedef {{Open:boolean, Done:boolean}} Stage*/ /** @typedef {[set:Stage, get:React.StateUpdater]} Binding */ const BranchContext = React.createContext( /** @type Binding */ ([{ Open: false, Done: true }, (_n) => {}]), ); /** @type BasicElement */ export const Branch = (props) => { /** @type Binding */ const stage = React.useState( /** @type Stage */ ({ Open: false, Done: true }), ); /** @type Binding */ const stageParent = React.useContext(BranchContext); React.useEffect(() => { const [{ Open, Done }, Shut] = stageParent; if (!Open && Done) { Shut({ Open: false, Done: true }); } }, [stageParent]); return React.createElement(BranchContext.Provider, { value: stage, children: props.children, }); }; /** @type BasicElement */ export const Button = (props) => { /** @type Binding */ const [stageGet, stageSet] = React.useContext(BranchContext); const handler = () => { const value = { Open: !stageGet.Open, Done: false }; stageSet(value); console.log(value); }; return html``; }; /** @type BasicElement */ export const Menu = (props) => { const [stageGet, stageSet] = React.useContext(BranchContext); const refElement = React.useRef(null); React.useEffect(() => { console.log(`menu should change`, stageGet); Collapser(refElement.current, () => { stageSet({ ...stageGet, Done: true }); })(stageGet.Open, stageGet.Open == false && stageGet.Done ? 0 : 600); }, [stageGet.Open]); return html`
${props.children}
`; }; /** @type BasicElement */ export const Collapse = (props) => { const ref = React.useRef(); const style = { overflow: "visible", height: "auto" }; return html`
${props.children}
`; }; /** @typedef {(inOpen:boolean, inMilliseconds:number)=>void} Toggler */ /** @type {(inElement:HTMLElement, inUserDone:(Open:boolean)=>void)=>Toggler} */ const Collapser = (inElement, inUserDone) => { const collapse = inElement; /** @type {(inEvent:TransitionEvent)=>void} */ const done = (inEvent) => { if (inEvent.propertyName == "height" && inEvent.target == collapse) { inEvent.stopPropagation(); if (collapse.clientHeight > 0) { collapse.style.height = "auto"; collapse.style.overflow = "visible"; inUserDone(true); } else { inUserDone(false); } } }; /** @type Toggler */ const show = (inOpen, inMs) => { collapse.removeEventListener("transitionend", done); collapse.addEventListener("transitionend", done); collapse.style.height = collapse.clientHeight + "px"; collapse.style.overflow = "hidden"; collapse.style.transition = "none"; requestAnimationFrame(() => { collapse.style.height = `${inOpen ? collapse.scrollHeight : 0}px`; collapse.style.transition = `height ${inMs / 1000}s`; }); }; return show; };