From 1610f490e23c401b544393db209f406fbc3012d8 Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Wed, 12 Feb 2025 16:09:59 -0500 Subject: [PATCH] drilling --- app.js | 16 ++++-- deno.json | 3 +- drill.d.ts | 12 +++++ styles.dev.js | 143 +++++++++++++++++++++++++++++++------------------- types.d.ts | 87 ++++++++++++++++++++++++++++++ 5 files changed, 202 insertions(+), 59 deletions(-) create mode 100644 drill.d.ts create mode 100644 types.d.ts diff --git a/app.js b/app.js index fa7542e..e2d3cff 100644 --- a/app.js +++ b/app.js @@ -1,5 +1,15 @@ import Styles from "./styles.dev.js"; + + const sheet = Styles({ - Button:{padding:"20px", background:"orange"}, - Test:{fontSize:"3rem"} -}); \ No newline at end of file + Button:{ + padding:"20px", + background:"orange", + ".Inner":{ + fontSize:"10rem" + } + }, + Outline:{border:"2px solid orange"} +}); + +const el = sheet.dom.div.Button.Outline(); \ No newline at end of file diff --git a/deno.json b/deno.json index b7a8787..64c4916 100644 --- a/deno.json +++ b/deno.json @@ -4,6 +4,7 @@ "lib": [ "deno.window", "DOM" - ] + ], + "types": ["./drill.d.ts"] } } \ No newline at end of file diff --git a/drill.d.ts b/drill.d.ts new file mode 100644 index 0000000..d42b16a --- /dev/null +++ b/drill.d.ts @@ -0,0 +1,12 @@ + +export {} +declare global { + namespace Gale + { + type Invoker =()=>HTMLElement + type Elemental = {[K in keyof HTMLElementTagNameMap]: Circular} + type Circular = { + [K in Keys]: Circular&Invoker; + }; + } +} \ No newline at end of file diff --git a/styles.dev.js b/styles.dev.js index e179198..f934bf7 100644 --- a/styles.dev.js +++ b/styles.dev.js @@ -1,67 +1,100 @@ -// @ts-check const KeyQuery = "@"; const KeyPseudo = ":"; const KeyChild = "."; const KeyGroup = "^"; -/** - * @typedef { Partial & { - * [key: `${KeyQuery|KeyPseudo|KeyChild|KeyGroup}${string}`]: UserStyles; - * } & { - * [key in keyof CSSStyleDeclaration]?: CSSStyleDeclaration[key] | CSSStyleDeclaration[key][] - * }} UserStyles - */ +/** @typedef { Partial & {[key: `${KeyQuery|KeyPseudo|KeyChild|KeyGroup}${string}`]: UserStyles } } UserStyles */ +/** @typedef {Record} UserSheet */ -/** @typedef {Record & { global?: UserStyles }} UserSheet */ -/** - * @template {UserSheet} T - * @typedef {keyof T | (string & {})} ValidSelectors +/** + * @template Obj + * @typedef { { [Key in keyof Obj]: Obj[Key] extends object ? Key | CollectKeys : Key }[keyof Obj] } CollectKeys */ /** - * @template {UserSheet} T - * @param {T} sheet - * @returns {((...selectors: (keyof T)[]) => string) & { css: string }} + * @template Keys + * @typedef { Keys extends `${KeyChild|KeyGroup}${infer Rest}` ? Keys : never } FilterKeys + */ +/** + * @template A + * @template B + * @typedef {A extends string ? B extends string ? `${A}${B}` : never : never } CrossMultiply */ -const createCss = (sheet) => { - let i = 0; - const id = i ? "_" + i : ""; - i++; - - const Tier = (selector, obj) => { - const styles = Object.keys(obj).map((key) => { - const value = obj[key]; - switch (key[0]) { - case KeyQuery: - return Tier(`@media(max-width:${key.substring(KeyQuery.length)})`, value); - case KeyPseudo: - return Tier(`&${key}`, value); - case KeyChild: - return Tier(`${key}`, value); - case KeyGroup: - return Tier(`&:hover .${key.substring(KeyGroup.length)}`, value); - } - return `${key.replace(/([a-z])([A-Z])/g, '$1-$2')}: ${value};`; - }); - return `${selector}{${styles.join("\n")}}`; - }; - - const css = Object.keys(sheet).map(key => Tier("." + key, sheet[key])).join("\n"); - globalThis.document?.head.insertAdjacentHTML("beforeend", ``); - - /** @type {(...args: (keyof T)[]) => string} */ - const classes = (...args) => { - /** @type {(needle: string, str: string) => string} */ - const extractLast = (needle, str) => { - const ind = str.lastIndexOf(needle) + needle.length; - return ind ? str.substring(ind) : str; - }; - return args.map(arg => extractLast(KeyGroup, extractLast(KeyChild, arg))).join(id + " ") + id; - }; - - classes.css = css; - return classes; -}; -export default createCss; +/** + * @template Rec + * @typedef { keyof Rec | { [K in keyof Rec]: K extends string ? CrossMultiply>> : never }[keyof Rec] } CrossMultiplyRecord + */ + + +/** @type {(selector:string, obj:UserStyles)=>string} */ +const Tier=(selector, obj)=> +{ + const styles = Object.keys(obj).map((key)=> + { + const value = obj[key]; + switch(key[0]) + { + case KeyQuery : + return Tier(`@media(max-width:${key.substring(KeyQuery.length)})`, value); + case KeyPseudo : + return Tier(`&${key}`, value); + case KeyChild : + return Tier(`${key}`, value); + case KeyGroup : + return Tier(`&:hover .${key.substring(KeyGroup.length)}`, value); + } + return `${ key.replace(/([a-z])([A-Z])/g, '$1-$2') }: ${value};` + }); + return `${selector}{${styles.join("\n")}}`; +} + + +/** @type {(ref:T)=>Gale.Elemental>} */ +function MakeElemental(ref) +{ + let pending = false; + let classes = []; + + const collector = new Proxy(()=>{console.log("original func invoked!", pending, classes)}, + { + get(target, prop, rec) + { + classes.push(prop); + return collector; + } + } + ); + + const obj = new Proxy({}, {get(target, prop, rec) + { + pending = prop; + classes = []; + return collector; + } + }); + + return obj; +} + +let i = 0; +/** @type {(sheet:UserSheet&T)=> ((...args:CrossMultiplyRecord[])=>string)&{css:string, dom:Gale.Elemental>} } */ +export default (sheet)=> +{ + const id = i ? "_"+i : ""; + i++; + const css = Object.keys(sheet).map(key=>Tier("."+key, sheet[key])).join(`\n`); + globalThis.document?.head.insertAdjacentHTML("beforeend", ``); + const classes =(...args)=>{ + /** @type {(needle:string, str:string)=>string} */ + const extractLast =(needle, str)=>{ + const ind = str.lastIndexOf(needle)+needle.length; + return ind ? str.substring(ind) : str; + } + return args.map((arg)=>extractLast(KeyGroup, extractLast(KeyChild, arg))).join(id+" ")+id; + } + classes.css = css; + classes.dom = MakeElemental(sheet); + return classes; +} \ No newline at end of file diff --git a/types.d.ts b/types.d.ts new file mode 100644 index 0000000..f29e88b --- /dev/null +++ b/types.d.ts @@ -0,0 +1,87 @@ +export {} +declare global +{ + namespace Van { + + interface State { + val: T + readonly oldVal: T + readonly rawVal: T + } + + // Defining readonly view of State for covariance. + // Basically we want StateView to implement StateView + type StateView = Readonly> + + type Val = State | T + + type Primitive = string | number | boolean | bigint + + // deno-lint-ignore no-explicit-any + type PropValue = Primitive | ((e: any) => void) | null + + type PropValueOrDerived = PropValue | StateView | (() => PropValue) + + type Props = Record & { class?: PropValueOrDerived; is?: string } + + type PropsWithKnownKeys = Partial<{[K in keyof ElementType]: PropValueOrDerived}> + + type ValidChildDomValue = Primitive | Node | null | undefined + + type BindingFunc = ((dom?: Node) => ValidChildDomValue) | ((dom?: Element) => Element) + + type ChildDom = ValidChildDomValue | StateView | BindingFunc | readonly ChildDom[] + + type TagFunc = (first?: Props & PropsWithKnownKeys | ChildDom, ...rest: readonly ChildDom[]) => Result + + type Tags = Readonly>> & { + [K in keyof HTMLElementTagNameMap]: TagFunc + } + } + const van:{ + readonly state: (initVal: T, HMRKey?:string)=> Van.State + readonly derive: (f: () => T) => Van.State + readonly add: (dom: Element, ...children: readonly Van.ChildDom[]) => Element + readonly tags: Van.Tags & ((namespaceURI: string) => Readonly>>) + readonly hydrate: (dom: T, f: (dom: T) => T | null | undefined) => T + }; + + + namespace VanX + { + type StateOf = { readonly [K in keyof T]: Van.State } + type ValueType = T extends (infer V)[] ? V : T[keyof T] + type KeyType = T extends unknown[] ? number : string + type ReplacementFunc = + T extends (infer V)[] ? (items: V[]) => readonly V[] : + (items: [string, T[keyof T]][]) => readonly [string, T[keyof T]][] + } + const vanX:{ + readonly calc: (f: () => R) => R + readonly reactive: (obj: T, HMRKey?:string) => T + readonly noreactive: (obj: T) => T + readonly stateFields: (obj: T) => VanX.StateOf + readonly raw: (obj: T) => T + readonly list: (container: (() => ElementType) | ElementType, items: T,itemFunc: (v: Van.State>, deleter: () => void, k: VanX.KeyType) => Node) => ElementType + readonly replace: (obj: T, replacement: VanX.ReplacementFunc | T) => T + readonly compact: (obj: T) => T + }; + + namespace Gale { + type KeyQuery = "@"; + type KeyPseudo = ":"; + type KeyChild = "."; + type KeyGroup = "^"; + type UserStyles = Partial & {[key: `${KeyQuery|KeyPseudo|KeyChild|KeyGroup}${string}`]: UserStyles } + type UserSheet = Record + type CollectKeys = {[Key in keyof Obj]: Obj[Key] extends object ? Key | CollectKeys : Key }[keyof Obj] + type FilterKeys = Keys extends `${KeyChild|KeyGroup}${infer Rest}` ? Keys : never + type CrossMultiply = A extends string ? B extends string ? `${A}${B}` : never : never + type CrossMultiplyRecord = keyof Rec | { [K in keyof Rec]: K extends string ? CrossMultiply>> : never }[keyof Rec] + type Tier = (selector:string, obj:UserStyles)=>string; + type CreateSheet = (sheet:UserSheet&T)=> ((...args:CrossMultiplyRecord[])=>string)&{css:string} + } + + const Gale:Gale.CreateSheet + +} \ No newline at end of file