178 lines
5.6 KiB
TypeScript
178 lines
5.6 KiB
TypeScript
// tss.ts
|
|
type PseudoKeys = ["hover"|"before"|"after"] | ["hover", "after"|"before"];
|
|
type ValueSignature = (...args:any[])=>string
|
|
type EnumDefinition<Signature> = [property:string, options:Record<string, string>, values?:Signature];
|
|
type EnumBlock<Signature> = Record<string, EnumDefinition<Signature>>;
|
|
type RecursiveObject<Sig extends ValueSignature, Obj extends EnumBlock<Sig>> =
|
|
{
|
|
[F in keyof Obj]: Obj[F][2] extends Sig ? ((...args:Parameters<Obj[F][2]>)=> RecursiveObject<Sig, Obj>&((...args:PseudoKeys)=>RecursiveObject<Sig, Obj>)&Array<RecursiveObject<Sig, Obj>>)&{
|
|
[E in keyof Obj[F][1]]:RecursiveObject<Sig, Obj>&((...args:PseudoKeys)=>RecursiveObject<Sig, Obj>)&Array<RecursiveObject<Sig, Obj>>
|
|
} :
|
|
{
|
|
[E in keyof Obj[F][1]]:RecursiveObject<Sig, Obj>&((...args:PseudoKeys)=>RecursiveObject<Sig, Obj>)&Array<RecursiveObject<Sig, Obj>>
|
|
}
|
|
}
|
|
|
|
function Block<Signature extends ValueSignature, Fields extends EnumBlock<Signature>>(options:Fields):()=>RecursiveObject<Signature, Fields>
|
|
{
|
|
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=<T = UnitString>(...args:[t:T, r?:T, b?:T, l?:T])=>args.join(" ")
|
|
const UnitAuto = {Auto:"auto"};
|
|
|
|
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" }],
|
|
|
|
T:["top", UnitAuto, funcUnitSingle],
|
|
R:["right", UnitAuto, funcUnitSingle],
|
|
B:["bottom", UnitAuto, funcUnitSingle],
|
|
L:["left", UnitAuto, funcUnitSingle],
|
|
|
|
Pad: ["padding", {}, funcUnitTRBL ],
|
|
PadT:["padding-top", {}, funcUnitSingle],
|
|
PadR:["padding-right", {}, funcUnitSingle],
|
|
PadB:["padding-bottom", {}, funcUnitSingle],
|
|
PadL:["padding-left", {}, funcUnitSingle],
|
|
|
|
Mar: ["margin", UnitAuto, funcUnitTRBL<UnitString|"auto"> ],
|
|
MarT:["margin-top", UnitAuto, funcUnitSingle],
|
|
MarR:["margin-right", UnitAuto, funcUnitSingle],
|
|
MarB:["margin-bottom", UnitAuto, funcUnitSingle],
|
|
MarL:["margin-left", UnitAuto, funcUnitSingle]
|
|
}
|
|
);
|
|
|
|
const masterSheet = {};
|
|
|
|
const SheetId = {
|
|
index:-1,
|
|
getId()
|
|
{
|
|
this.index++;
|
|
return this.index;
|
|
}
|
|
};
|
|
|
|
export function Sheet<UserClasses extends Record<string, (this:ReturnType<typeof styles>, css:ReturnType<typeof styles>)=>void >>(userClasses:UserClasses):{[Class in keyof UserClasses]:string}
|
|
{
|
|
const hash = SheetId.getId();
|
|
|
|
const writer = globalThis["document"]
|
|
? (selector, declaration)=>
|
|
{
|
|
const styleBlock = document.createElement("style");
|
|
styleBlock.innerText = "."+selector+declaration;
|
|
document.head.insertAdjacentElement("beforeend", styleBlock)
|
|
}
|
|
: (selector, declaration)=>{masterSheet[selector] = declaration};
|
|
|
|
return new Proxy(function(){}, {get(_target, className){
|
|
|
|
const lookup = userClasses[className];
|
|
if(typeof lookup == "function")
|
|
{
|
|
const skewer = styles();
|
|
lookup.call(skewer, skewer);
|
|
const name = `${className}_${hash}`;
|
|
writer(name, `{ ${skewer().join("")} }`)
|
|
userClasses[className] = name;
|
|
return name;
|
|
}
|
|
else
|
|
{
|
|
return lookup;
|
|
}
|
|
}});
|
|
}
|
|
|
|
export function Divulge()
|
|
{
|
|
return masterSheet;
|
|
} |