From 052dd13bb9958b5f96965a20665f1a52e5d9fbfd Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Tue, 27 Jun 2023 21:54:39 -0400 Subject: [PATCH] import conversion --- example/dyn-test.tsx | 12 +++ iso-menu.tsx | 224 +++++++++++++++++++++++++++++++++++++++++++ run-serve.tsx | 33 ++++++- 3 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 example/dyn-test.tsx create mode 100644 iso-menu.tsx diff --git a/example/dyn-test.tsx b/example/dyn-test.tsx new file mode 100644 index 0000000..6bb8bf1 --- /dev/null +++ b/example/dyn-test.tsx @@ -0,0 +1,12 @@ + + +import('https://esm.sh/react').then((module) => { + console.log(module); +}); + + +function unimport(n:number) +{ + return n; +} +unimport(123) \ No newline at end of file diff --git a/iso-menu.tsx b/iso-menu.tsx new file mode 100644 index 0000000..bf2ebe3 --- /dev/null +++ b/iso-menu.tsx @@ -0,0 +1,224 @@ +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 ); + const refInitial:React.MutableRefObject = React.useRef(true); + + 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-700`, + 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]); + } + } + + React.useEffect(()=> + { + refControl.current = refElement.current && Collapser(refElement.current, stateGet.open ? "Open" : "Shut", Classes); + } + , []); + React.useEffect(()=> + { + (!refInitial.current && refControl.current) && refControl.current(stateGet.open ? "Open" : "Shut", ()=>stateSet({done:true})); + refInitial.current = false; + } + , [stateGet.open]); + + useAway(refElement, (e)=>stateSet({open:false, done:false}) ); + + return
} class={Classes.Shut}> + { (!stateGet.open && stateGet.done) ? null : props.children} +
; +}; + +export const Button =()=> +{ + const [stateGet, stateSet] = React.useContext(CTX); + return <> +

{JSON.stringify(stateGet)}

+ + + ; +}; + +type Handler = (e:MouseEvent)=>void +const Refs:Map> = new Map(); +function isHighest(inElement:HTMLElement, inSelection:HTMLElement[]) +{ + let currentNode = inElement; + while (currentNode != document.body) + { + currentNode = currentNode.parentNode as HTMLElement; + if(currentNode.hasAttribute("data-use-away") && inSelection.includes(currentNode)) + { + return false; + } + } + return true; +} +window.innerWidth && document.addEventListener("click", e=> +{ + const path = e.composedPath(); + const away:HTMLElement[] = []; + + Refs.forEach( (handlerRef, element)=> + { + if(!path.includes(element) && handlerRef.current) + { + away.push(element); + } + }); + + away.forEach((element)=> + { + if(isHighest(element, away)) + { + const handler = Refs.get(element); + handler?.current && handler.current(e); + } + }); + +} +, true); +const useAway =(inRef:React.Ref, handleAway:Handler)=> +{ + const refHandler:React.MutableRefObject = React.useRef(handleAway); + refHandler.current = handleAway; + + React.useEffect(()=> + { + if(inRef.current) + { + inRef.current.setAttribute("data-use-away", "0"); + Refs.set(inRef.current, refHandler); + } + return ()=> inRef.current && Refs.delete(inRef.current); + } + , []); +}; + +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;`); + Object.entries(inClasses).forEach(([key, value])=> + { + inElement.setAttribute("class", value); + output[key] = [value, inElement.offsetWidth, inElement.offsetHeight]; + }); + inElement.setAttribute("class", initialClass); + inElement.offsetHeight; // this has be be exactly here + inElement.setAttribute("style", initialStyle); + + return output; +}; + +type DoneCallback =(inState:string)=>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)=> (inEvent.target == inElement) && transitions.add(inEvent.propertyName); + const end = (inEvent:TransitionEvent)=> + { + if (inEvent.target == inElement) + { + transitions.delete(inEvent.propertyName); + if(transitions.size === 0) + { + measurements = StyleCalc(inElement, library); + const [, w, h] = measurements[userMode]; + if(inElement.offsetHeight != h || inElement.offsetWidth != w) + { + anim(userMode, userDone); + } + else + { + inElement.setAttribute("style", ""); + inTransition = false; + userDone(userMode); + } + } + } + }; + const anim = 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); + } + + if(measurements) + { + const [classes, width, height] = measurements[inState] as StyleSize; + const oldWidth = inElement.offsetWidth; + const oldHeight = inElement.offsetHeight; + inElement.style.width = oldWidth + "px"; + inElement.style.height = oldHeight + "px"; + inTransition = true; + + frameRequest = requestAnimationFrame(()=> + { + inElement.style.height = height + "px"; + inElement.style.width = width + "px"; + inElement.className = classes; + }); + } + } + else + { + inElement.removeEventListener("transitionrun", run); + inElement.removeEventListener("transitionend", end); + } + } as CollapseControls; + + inElement.addEventListener("transitionend", end); + inElement.addEventListener("transitionrun", run); + + return anim; +} diff --git a/run-serve.tsx b/run-serve.tsx index f67320a..b0dc224 100644 --- a/run-serve.tsx +++ b/run-serve.tsx @@ -55,7 +55,16 @@ let Configuration:Configuration = Allow: "*", Reset: "/clear-cache", Spoof: "/@able", - Serve(inReq, inURL, inExt, inMap, inConfig){}, + async Serve(inReq, inURL, inExt, inMap, inConfig) + { + if(inReq.headers.get("user-agent")?.startsWith("Deno")) + { + const file = await fetch(inConfig.Proxy + inURL.pathname); + const text = await file.text(); + + return new Response(Transpile.Patch(text), {headers:{"content-type":"application/javascript"}} ); + } + }, Remap: (inImports, inConfig)=> { const reactURL = inImports["react"]; @@ -89,6 +98,9 @@ let Configuration:Configuration = }, SWCOp: { + env:{ + dynamicImport:false + }, sourceMaps: false, minify: true, jsc: @@ -127,6 +139,25 @@ export const Transpile = ImportMapReload(); return size; }, + Patch(inText) + { + const regex = /(?