// tss.ts type PseudoKeys = ["hover"|"before"|"after"] | ["hover", "after"|"before"]; type ValueSignature = (...args:any[])=>string type EnumDefinition = [property:string, options:Record, values?:Signature]; type EnumBlock = Record>; type RecursiveObject> = { [F in keyof Obj]: Obj[F][2] extends Sig ? ((...args:Parameters)=> RecursiveObject&((...args:PseudoKeys)=>RecursiveObject)&Array>)&{ [E in keyof Obj[F][1]]:RecursiveObject&((...args:PseudoKeys)=>RecursiveObject)&Array> } : { [E in keyof Obj[F][1]]:RecursiveObject&((...args:PseudoKeys)=>RecursiveObject)&Array> } } function Block>(options:Fields):()=>RecursiveObject { return ()=>{ let pseudo = false; let query = false; const list = []; let fieldLookup = {}; const proxyOuter = new Proxy( function(...args) { // OUTER context called as function: if(args.length) // pseudo elements are being declared, capture and return the OUTER context { list.push(`${(pseudo||query) ? "}" : ""} &:${args.join("::")}{`); pseudo = true; query = false; return proxyOuter; } else // css dump requested: { pseudo && list.push("}"); query && list.push("}"); pseudo = false; query = false; return list; } }, { get(_target, propName) { // OUTER context property access: if(parseInt(propName)) // if a number was used as a property, treat that as a mobile-first media query in px units, and return the OUTER context { list.push(`${(query) ? "}" : ""} @media(min-width:${propName}px){`); query = true; return proxyOuter; } // a css property alias was accessed, look up the mapped css property name from the declaration array, push that, and return the OUTER context fieldLookup = options[propName]; if(fieldLookup) { list.push(fieldLookup[0]); } return proxyInner; } } ); const proxyInner = new Proxy( function(...args) { // INNER context called as function // capture the evalued dynamic values and return the OUTER context list.push(`:${fieldLookup[2](...args)};`); return proxyOuter; }, { get(_target, valName) { // INNER context property access try // capture the mapped css value and return the OUTER context { list.push(`:${fieldLookup[1][valName]};`); return proxyOuter; } catch(_e) { console.warn("someone is trying to stringify a style proxy"); return ()=>"[StyleProxy]"; } } } ); return proxyOuter; } } type Measure = "px" | "em" | "rem" | "vh" | "vw" | "%"; type UnitString = `${number}${Measure}`; const funcUnitSingle = (amount:UnitString)=>amount const funcUnitTRBL = (...args:[t:UnitString, r?:UnitString, b?:UnitString, l?:UnitString])=>args.join(" ") const styles = Block( { Pos:["position", { Abs:"absolute", Rel:"relative", Fix:"fixed", Pin:"sticky", No:"static" }], Dis:["display", { Flex:"flex", Grid:"grid", None:"none", Block:"block", InlineBlock:"inline-block" }], Box:["box-sizing", { Border:"border-box", Content:"content-box" }], L:[ "left", { Auto:"auto"}, funcUnitSingle], R:[ "right", { Auto:"auto"}, funcUnitSingle], T:[ "top", { Auto:"auto"}, funcUnitSingle], B:[ "bottom", { Auto:"auto"}, funcUnitSingle], Pad: ["padding", {}, funcUnitTRBL ], PadT:["padding-top", {}, funcUnitSingle], PadR:["padding-right", {}, funcUnitSingle], PadB:["padding-bottom", {}, funcUnitSingle], PadL:["padding-left", {}, funcUnitSingle], Mar: ["margin", {}, funcUnitTRBL ], MarT:["margin-top", {}, funcUnitSingle], MarR:["margin-right", {}, funcUnitSingle], MarB:["margin-bottom", {}, funcUnitSingle], MarL:["margin-left", {}, funcUnitSingle] } ); let masterSheet = []; export function Sheet)=>void >>(userClasses:UserClasses):{[Class in keyof UserClasses]:string} { const hash = Math.floor(Math.random()*10000); return new Proxy(function(){}, {get(_target, className){ console.log("sheet accessor", className); const lookup = userClasses[className]; if(typeof lookup == "string") { return lookup; } else { const skewer = styles(); lookup(skewer); const name = `.${className}_${hash}`; masterSheet.push( `${name}{ ${skewer().join("")} }` ); userClasses[className] = name; return name; } }}); } export function Divulge() { return masterSheet.join("\n"); }