Compare commits
4 Commits
f4823a6576
...
a009f334eb
Author | SHA1 | Date | |
---|---|---|---|
a009f334eb | |||
1610f490e2 | |||
6479974994 | |||
a54a067695 |
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "deno launch",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"runtimeExecutable": "deno",
|
|
||||||
"runtimeArgs": ["styler.tsx"],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
19
app.js
Normal file
19
app.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import Styles from "./styles.dev.js";
|
||||||
|
|
||||||
|
|
||||||
|
const sheet = Styles({
|
||||||
|
Button:{
|
||||||
|
padding:"20px",
|
||||||
|
background:"orange",
|
||||||
|
".Inner":{
|
||||||
|
fontSize:"10rem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Outline:{border:"2px solid orange"}
|
||||||
|
});
|
||||||
|
|
||||||
|
const el = sheet.dom.a.Button.Outline({onclick(){console.log("clicked!")}}, "Click!");
|
||||||
|
console.log(el);
|
||||||
|
|
||||||
|
|
||||||
|
van.add(document.body, el, sheet.dom.p.Outline("paragraph!"));
|
15
deno.json
15
deno.json
@ -1,13 +1,10 @@
|
|||||||
{
|
{
|
||||||
"imports": {
|
|
||||||
"react": "https://esm.sh/preact@10.18.1/compat",
|
|
||||||
"react/": "https://esm.sh/preact@10.18.1/compat/",
|
|
||||||
"preact-render-to-string": "https://esm.sh/preact-render-to-string@6.5.7?deps=preact@10.18.1"
|
|
||||||
},
|
|
||||||
"tasks": {"go": "deno run -A styler.tsx"},
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"jsx": "react-jsx",
|
"checkJs": true,
|
||||||
"jsxImportSource": "react",
|
"lib": [
|
||||||
"lib": ["deno.window","dom","dom.asynciterable"]
|
"deno.window",
|
||||||
|
"DOM"
|
||||||
|
],
|
||||||
|
"types": ["./drill.d.ts", "./types.d.ts"]
|
||||||
}
|
}
|
||||||
}
|
}
|
21
deno.lock
21
deno.lock
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "3",
|
|
||||||
"redirects": {
|
|
||||||
"https://esm.sh/v128/@types/react@~18.3/index.d.ts": "https://esm.sh/v128/@types/react@18.2.38/index.d.ts",
|
|
||||||
"https://esm.sh/v135/@types/react-dom@~18.3/server~.d.ts": "https://esm.sh/v135/@types/react-dom@18.3.0/server~.d.ts"
|
|
||||||
},
|
|
||||||
"remote": {
|
|
||||||
"https://esm.sh/preact-render-to-string@6.5.7?deps=preact@10.18.1": "1d9e844b000fd2bf592142f98e6a6c9c3c10953ac90bef90a67ded5e28667279",
|
|
||||||
"https://esm.sh/preact@10.18.1/compat/jsx-runtime": "d9addc942dc9b5eb507c622e7ad93d5594093f5d31e461d4125f7a1ab475f932",
|
|
||||||
"https://esm.sh/react-dom@18.3.1/server": "90e374c5323303d5315c780b19e5d6297ec5a569f9cee1df28ee1030dd23af9c",
|
|
||||||
"https://esm.sh/react@18.3.1": "52480d4c00855e4f76843caa4f2818eb001671523740cbf064cbd108721c29c8",
|
|
||||||
"https://esm.sh/stable/preact@10.18.1/denonext/compat.js": "cb1028add6b66ecea904a750ad9887f21c5a42c17303c4c317710866c290efa7",
|
|
||||||
"https://esm.sh/stable/preact@10.18.1/denonext/compat/jsx-runtime.js": "16e3c12a4942f4f3027f6f3c5a0686ef00a80d70a4fb08097b702f4ddd5d488f",
|
|
||||||
"https://esm.sh/stable/preact@10.18.1/denonext/hooks.js": "cb7e8c9973e6a224348eaa51fba21e13f239839e403f751b29894a258a6d16d0",
|
|
||||||
"https://esm.sh/stable/preact@10.18.1/denonext/jsx-runtime.js": "be3f1ff4c3c03b08ed19d69428e35bf3d90360a8e081a2e60075ddfd38fd86df",
|
|
||||||
"https://esm.sh/stable/preact@10.18.1/denonext/preact.mjs": "b2ad171554b90f2be0f30b1318f63d0df90420b2bdb727fddd97193daa177f84",
|
|
||||||
"https://esm.sh/stable/react@18.3.1/denonext/react.mjs": "fc048ffc55366baf7669519127d186761db72046e2bebf35fe8d0de3964defa3",
|
|
||||||
"https://esm.sh/v135/preact-render-to-string@6.5.7/X-ZC9wcmVhY3RAMTAuMTguMQ/denonext/preact-render-to-string.mjs": "80d81cb9eeee0b973a136113bd8ff22288b5d40d3dae15f91838dbc72356e095",
|
|
||||||
"https://esm.sh/v135/react-dom@18.3.1/denonext/server.js": "b4db56f16b45002b5e9b127a9938ab45b12f39825161dffe875920510c524878"
|
|
||||||
}
|
|
||||||
}
|
|
11
drill.d.ts
vendored
Normal file
11
drill.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
namespace Gale
|
||||||
|
{
|
||||||
|
type Elemental<T extends string> = {[K in keyof HTMLElementTagNameMap]: Circular<T, K>}
|
||||||
|
type Circular<Keys extends string, ElementName extends keyof HTMLElementTagNameMap> = {
|
||||||
|
[K in Keys]: Circular<Keys, ElementName>&Van.TagFunc<HTMLElementTagNameMap[ElementName]>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
11
dump.html
11
dump.html
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-s" />
|
|
||||||
<style>.Face-sans_lg{ @media(min-width:1024px){ font-family:sans-serif; }}
|
|
||||||
.Pad-large_md{ @media(min-width:767px){ padding:3rem; }}
|
|
||||||
.Pad-small_lg{ @media(min-width:1024px){ padding:1rem; }}</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="Face-sans_lg Pad-large_md Pad-small_lg">hello!</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,3 +0,0 @@
|
|||||||
import {config} from "./gale.tsx";
|
|
||||||
|
|
||||||
export default config({other:"lol"});
|
|
72
gale.js
Normal file
72
gale.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// @ts-check
|
||||||
|
const KeyQuery = "@";
|
||||||
|
const KeyPseudo = ":";
|
||||||
|
const KeyChild = ".";
|
||||||
|
const KeyGroup = "^";
|
||||||
|
|
||||||
|
/** @typedef { Partial<CSSStyleDeclaration> & {[key: `${KeyQuery|KeyPseudo|KeyChild|KeyGroup}${string}`]: UserStyles } } UserStyles */
|
||||||
|
/** @typedef {Record<string, UserStyles>} UserSheet */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template Obj
|
||||||
|
* @typedef { { [Key in keyof Obj]: Obj[Key] extends object ? Key | CollectKeys<Obj[Key]> : Key }[keyof Obj] } CollectKeys
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template Rec
|
||||||
|
* @typedef { keyof Rec | { [K in keyof Rec]: K extends string ? CrossMultiply<K, FilterKeys<CollectKeys<Rec[K]>>> : 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")}}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
/** @type {<T extends UserSheet>(sheet:UserSheet&T)=> ((...args:CrossMultiplyRecord<T>[])=>string)&{css:string}} */
|
||||||
|
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", `<style data-sheet="${i}">${css}</style>`);
|
||||||
|
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;
|
||||||
|
}
|
102
gale.tsx
102
gale.tsx
@ -1,102 +0,0 @@
|
|||||||
|
|
||||||
const typeface = {
|
|
||||||
sans: "sans-serif",
|
|
||||||
serif: "Times New Roman"
|
|
||||||
};
|
|
||||||
const sizes = {
|
|
||||||
small: "1rem",
|
|
||||||
large: "3rem"
|
|
||||||
};
|
|
||||||
const colors = {
|
|
||||||
red: "#ff2200",
|
|
||||||
blue: "#0022ff"
|
|
||||||
};
|
|
||||||
const responsive = {
|
|
||||||
md: "min-width:767px",
|
|
||||||
lg: "min-width:1024px",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let sheet = {insertRule(_:string){}} as CSSStyleSheet;
|
|
||||||
if(globalThis?.document)
|
|
||||||
{
|
|
||||||
const sheetElement = document.createElement("style");
|
|
||||||
document.head.setAttribute("data-gale", "true");
|
|
||||||
document.head.appendChild(sheetElement);
|
|
||||||
sheet = sheetElement.sheet as CSSStyleSheet;
|
|
||||||
}
|
|
||||||
const styles = new Map<string, string>();
|
|
||||||
function Mapper<T extends Record<string, string>>(className:string, propertyName:string, lut:T)
|
|
||||||
{
|
|
||||||
const build =(propertyValue:string, customPropertyName?:string):string=>
|
|
||||||
{
|
|
||||||
const selector = `${className}-${(customPropertyName||propertyValue as string).replace(/[^a-zA-Z0-9]/g, "")}${activeQuery.val?"_"+activeQuery.key:""}`;
|
|
||||||
const check = styles.get(selector);
|
|
||||||
if(!check)
|
|
||||||
{
|
|
||||||
const body = `{ ${propertyName}:${propertyValue}; }`;
|
|
||||||
const rules = activeQuery.val ? `{ @media(${activeQuery.val})${body}}` : body
|
|
||||||
styles.set(selector, rules);
|
|
||||||
sheet.insertRule(selector + " " + rules);
|
|
||||||
}
|
|
||||||
return selector;
|
|
||||||
}
|
|
||||||
type UseCustom = (custom:string)=>string;
|
|
||||||
return new Proxy(((override:string)=>build(override)) as UseCustom,
|
|
||||||
{
|
|
||||||
get(_, prop){
|
|
||||||
|
|
||||||
return build(lut[prop as string], prop as string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) as T & UseCustom;
|
|
||||||
}
|
|
||||||
function MapperQuery<T extends Record<string, string>>(lut:T)
|
|
||||||
{
|
|
||||||
type UseCustom = (custom:string)=>QueryInvoker;
|
|
||||||
return new Proxy(((override:string)=>Query(override, override)) as UseCustom,
|
|
||||||
{
|
|
||||||
get(_, prop:string){
|
|
||||||
return Query(lut[prop], prop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) as Record<keyof T, QueryInvoker> & UseCustom;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let activeQuery:ActiveQuery;
|
|
||||||
type ActiveQuery = {key:string|false, val:string|false};
|
|
||||||
const activeQueryStack:ActiveQuery[] = [];
|
|
||||||
type QueryInvoker = (...args:string[])=>string
|
|
||||||
function Query(amount:string, key?:string):QueryInvoker
|
|
||||||
{
|
|
||||||
activeQuery = {key:key||amount, val:amount};
|
|
||||||
activeQueryStack.push(activeQuery);
|
|
||||||
return (...args)=> {
|
|
||||||
|
|
||||||
activeQueryStack.pop()
|
|
||||||
activeQuery = activeQueryStack.at(-1) || {key:false, val:false};
|
|
||||||
return args.join(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function config<T>(obj:T)
|
|
||||||
{
|
|
||||||
const CSS = {
|
|
||||||
Sheet()
|
|
||||||
{
|
|
||||||
const rules = [];
|
|
||||||
for(const [key, value] of styles.entries())
|
|
||||||
{
|
|
||||||
rules.push("."+key+value);
|
|
||||||
}
|
|
||||||
return rules.join("\n");
|
|
||||||
},
|
|
||||||
Face: Mapper("Face", "font-family", {...typeface, ...obj}),
|
|
||||||
Pad: Mapper("Pad", "padding", sizes),
|
|
||||||
Q: MapperQuery(responsive)
|
|
||||||
};
|
|
||||||
return CSS;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default config({});
|
|
21
index.html
21
index.html
@ -1,16 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<html lang="en">
|
||||||
<html>
|
<head>
|
||||||
<head></head>
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"> </div>
|
<script src="https://vanjs.org/code/van-1.5.3.nomodule.min.js"></script>
|
||||||
<script type="module">
|
<script type="module" src="app.js"></script>
|
||||||
import Gale from "./gale-custom.tsx";
|
|
||||||
|
|
||||||
document.querySelector("#app").innerHTML = `
|
|
||||||
<div class="${Gale.Q.lg( Gale.Face.sans, Gale.Q.md(Gale.Pad.large), Gale.Pad.small )}">
|
|
||||||
hey
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
30
styler.tsx
30
styler.tsx
@ -1,30 +0,0 @@
|
|||||||
import Gale from "./gale-custom.tsx";
|
|
||||||
import Render from "preact-render-to-string";
|
|
||||||
|
|
||||||
|
|
||||||
const html = Render(<div className={Gale.Q.lg( Gale.Face.sans, Gale.Q.md(Gale.Pad.large), Gale.Pad.small )}>
|
|
||||||
hello!
|
|
||||||
</div>)
|
|
||||||
|
|
||||||
const css = Gale.Sheet();
|
|
||||||
|
|
||||||
Deno.writeTextFile(
|
|
||||||
"dump.html",
|
|
||||||
`<!DOCTYPE html>
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-s" />
|
|
||||||
<style>${css}</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
${html}
|
|
||||||
</body>
|
|
||||||
</html>`
|
|
||||||
)
|
|
||||||
|
|
||||||
/* idea:
|
|
||||||
Gale.Table(
|
|
||||||
[, "md" ], {
|
|
||||||
Face:["serif", "sans" ],
|
|
||||||
Pad:["small", "large"]
|
|
||||||
})
|
|
||||||
*/
|
|
105
styles.dev.js
Normal file
105
styles.dev.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
const KeyQuery = "@";
|
||||||
|
const KeyPseudo = ":";
|
||||||
|
const KeyChild = ".";
|
||||||
|
const KeyGroup = "^";
|
||||||
|
|
||||||
|
/** @typedef { Partial<CSSStyleDeclaration> & {[key: `${KeyQuery|KeyPseudo|KeyChild|KeyGroup}${string}`]: UserStyles } } UserStyles */
|
||||||
|
/** @typedef {Record<string, UserStyles>} UserSheet */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template Obj
|
||||||
|
* @typedef { { [Key in keyof Obj]: Obj[Key] extends object ? Key | CollectKeys<Obj[Key]> : Key }[keyof Obj] } CollectKeys
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template Rec
|
||||||
|
* @typedef { keyof Rec | { [K in keyof Rec]: K extends string ? CrossMultiply<K, FilterKeys<CollectKeys<Rec[K]>>> : 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 {<T extends UserSheet>(ref:T)=>Gale.Elemental<CrossMultiplyRecord<T>>} */
|
||||||
|
function MakeElemental(ref)
|
||||||
|
{
|
||||||
|
let pending = false;
|
||||||
|
let classes = [];
|
||||||
|
|
||||||
|
const collector = new Proxy((...args)=>{
|
||||||
|
console.log("original func invoked!", pending, classes);
|
||||||
|
const element = van.tags[pending](...args);
|
||||||
|
element.className = classes.join(" ");
|
||||||
|
return element
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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 {<T extends UserSheet>(sheet:UserSheet&T)=> ((...args:CrossMultiplyRecord<T>[])=>string)&{css:string, dom:Gale.Elemental<CrossMultiplyRecord<T>>} } */
|
||||||
|
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", `<style data-sheet="${i}">${css}</style>`);
|
||||||
|
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;
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
//////////////////////
|
|
||||||
type OriginalType = {
|
|
||||||
key1: { sub1: string; sub2: string };
|
|
||||||
key2: { subA: string; subB: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
type ValidKeys = keyof OriginalType;
|
|
||||||
type ValidSubKeys<K extends ValidKeys> = keyof OriginalType[K];
|
|
||||||
|
|
||||||
type EnforcedRecord = {
|
|
||||||
[K in ValidKeys]: ValidSubKeys<K>[];
|
|
||||||
};
|
|
||||||
|
|
||||||
// Example usage:
|
|
||||||
const example: EnforcedRecord = {
|
|
||||||
key1: ['sub1', 'sub2'],
|
|
||||||
key2: ['subA', 'subB', "sub1"], // last is invalid
|
|
||||||
};
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
type OriginalType = {
|
|
||||||
key1: { sub1: string; sub2: string };
|
|
||||||
key2: { subA: string; subB: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
type ValidKeys = keyof OriginalType;
|
|
||||||
type ValidSubKeys<K extends ValidKeys> = keyof OriginalType[K];
|
|
||||||
|
|
||||||
type FixedLengthArray<T, L extends number> = [T, ...T[]] & { length: L };
|
|
||||||
|
|
||||||
type EnforcedRecord<L extends number> = {
|
|
||||||
[K in ValidKeys]: FixedLengthArray<ValidSubKeys<K>, L>;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Example usage:
|
|
||||||
const example: EnforcedRecord<2> = {
|
|
||||||
key1: ['sub1', 'sub2'], // Valid
|
|
||||||
key2: ['subA', 'subB'], // Valid
|
|
||||||
// key1: ['sub1'], // Invalid, TypeScript will give an error
|
|
||||||
// key2: ['subA', 'subB', 'subC'], // Invalid, TypeScript will give an error
|
|
||||||
};
|
|
@ -1,27 +0,0 @@
|
|||||||
type OriginalType = {
|
|
||||||
key1: { sub1: string; sub2: string };
|
|
||||||
key2: { subA: string; subB: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
type ValidKeys = keyof OriginalType;
|
|
||||||
type ValidSubKeys<K extends ValidKeys> = keyof OriginalType[K];
|
|
||||||
|
|
||||||
type FixedLengthArray<T, L extends number> = [T, ...T[]] & { length: L };
|
|
||||||
|
|
||||||
type EnforcedRecord<L extends number> = {
|
|
||||||
[K in ValidKeys]: FixedLengthArray<ValidSubKeys<K>, L>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const createTable = <T extends { length: number }>(
|
|
||||||
cols: T,
|
|
||||||
rows: EnforcedRecord<T['length']>
|
|
||||||
) => {
|
|
||||||
return { cols, rows };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Example usage:
|
|
||||||
const cols = ["a", "b", "c"] as const;
|
|
||||||
const table = createTable(cols, {
|
|
||||||
key1: ['sub1', 'sub2', 'sub1', 'sub1'], // invalid because too long
|
|
||||||
key2: ['subA', 'subB', 'sub1'], // invalid because bad key
|
|
||||||
});
|
|
@ -1,30 +0,0 @@
|
|||||||
type OriginalType = {
|
|
||||||
key1: { sub1: string; sub2: string };
|
|
||||||
key2: { subA: string; subB: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
type ValidKeys = keyof OriginalType;
|
|
||||||
type ValidSubKeys<K extends ValidKeys> = keyof OriginalType[K];
|
|
||||||
|
|
||||||
type FixedLengthArray<T, L extends number> = T[] & { length: L };
|
|
||||||
|
|
||||||
type EnforcedRecord<L extends number> = {
|
|
||||||
[K in ValidKeys]: FixedLengthArray<ValidSubKeys<K>, L>;
|
|
||||||
};
|
|
||||||
|
|
||||||
type FixedArr<Arr, Length extends number> = Arr & { length: Length };
|
|
||||||
function Func<L extends number>(arr1:FixedArr<string[], L>)
|
|
||||||
{
|
|
||||||
return arr1.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const test = Func<3>(["a", "b", "c"])
|
|
||||||
|
|
||||||
// Example usage:
|
|
||||||
const example: EnforcedRecord<2> = {
|
|
||||||
key1: ['sub1', 'sub2', 'sub1'], // Valid
|
|
||||||
key2: ['subA', 'subB'], // Valid
|
|
||||||
// key1: ['sub1'], // Invalid, TypeScript will give an error
|
|
||||||
// key2: ['subA', 'subB', 'subC'], // Invalid, TypeScript will give an error
|
|
||||||
};
|
|
87
types.d.ts
vendored
Normal file
87
types.d.ts
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
export {}
|
||||||
|
declare global
|
||||||
|
{
|
||||||
|
namespace Van {
|
||||||
|
|
||||||
|
interface State<T> {
|
||||||
|
val: T
|
||||||
|
readonly oldVal: T
|
||||||
|
readonly rawVal: T
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defining readonly view of State<T> for covariance.
|
||||||
|
// Basically we want StateView<string> to implement StateView<string | number>
|
||||||
|
type StateView<T> = Readonly<State<T>>
|
||||||
|
|
||||||
|
type Val<T> = State<T> | 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> | (() => PropValue)
|
||||||
|
|
||||||
|
type Props = Record<string, PropValueOrDerived> & { class?: PropValueOrDerived; is?: string }
|
||||||
|
|
||||||
|
type PropsWithKnownKeys<ElementType> = Partial<{[K in keyof ElementType]: PropValueOrDerived}>
|
||||||
|
|
||||||
|
type ValidChildDomValue = Primitive | Node | null | undefined
|
||||||
|
|
||||||
|
type BindingFunc = ((dom?: Node) => ValidChildDomValue) | ((dom?: Element) => Element)
|
||||||
|
|
||||||
|
type ChildDom = ValidChildDomValue | StateView<Primitive | null | undefined> | BindingFunc | readonly ChildDom[]
|
||||||
|
|
||||||
|
type TagFunc<Result> = (first?: Props & PropsWithKnownKeys<Result> | ChildDom, ...rest: readonly ChildDom[]) => Result
|
||||||
|
|
||||||
|
type Tags = Readonly<Record<string, TagFunc<Element>>> & {
|
||||||
|
[K in keyof HTMLElementTagNameMap]: TagFunc<HTMLElementTagNameMap[K]>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const van:{
|
||||||
|
readonly state: <T>(initVal: T, HMRKey?:string)=> Van.State<T>
|
||||||
|
readonly derive: <T>(f: () => T) => Van.State<T>
|
||||||
|
readonly add: (dom: Element, ...children: readonly Van.ChildDom[]) => Element
|
||||||
|
readonly tags: Van.Tags & ((namespaceURI: string) => Readonly<Record<string, Van.TagFunc<Element>>>)
|
||||||
|
readonly hydrate: <T extends Node>(dom: T, f: (dom: T) => T | null | undefined) => T
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace VanX
|
||||||
|
{
|
||||||
|
type StateOf<T> = { readonly [K in keyof T]: Van.State<T[K]> }
|
||||||
|
type ValueType<T> = T extends (infer V)[] ? V : T[keyof T]
|
||||||
|
type KeyType<T> = T extends unknown[] ? number : string
|
||||||
|
type ReplacementFunc<T> =
|
||||||
|
T extends (infer V)[] ? (items: V[]) => readonly V[] :
|
||||||
|
(items: [string, T[keyof T]][]) => readonly [string, T[keyof T]][]
|
||||||
|
}
|
||||||
|
const vanX:{
|
||||||
|
readonly calc: <R>(f: () => R) => R
|
||||||
|
readonly reactive: <T extends object>(obj: T, HMRKey?:string) => T
|
||||||
|
readonly noreactive: <T extends object>(obj: T) => T
|
||||||
|
readonly stateFields: <T extends object>(obj: T) => VanX.StateOf<T>
|
||||||
|
readonly raw: <T extends object>(obj: T) => T
|
||||||
|
readonly list: <T extends object, ElementType extends Element>(container: (() => ElementType) | ElementType, items: T,itemFunc: (v: Van.State<VanX.ValueType<T>>, deleter: () => void, k: VanX.KeyType<T>) => Node) => ElementType
|
||||||
|
readonly replace: <T extends object>(obj: T, replacement: VanX.ReplacementFunc<T> | T) => T
|
||||||
|
readonly compact: <T extends object>(obj: T) => T
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Gale {
|
||||||
|
type KeyQuery = "@";
|
||||||
|
type KeyPseudo = ":";
|
||||||
|
type KeyChild = ".";
|
||||||
|
type KeyGroup = "^";
|
||||||
|
type UserStyles = Partial<CSSStyleDeclaration> & {[key: `${KeyQuery|KeyPseudo|KeyChild|KeyGroup}${string}`]: UserStyles }
|
||||||
|
type UserSheet = Record<string, UserStyles>
|
||||||
|
type CollectKeys<Obj> = {[Key in keyof Obj]: Obj[Key] extends object ? Key | CollectKeys<Obj[Key]> : Key }[keyof Obj]
|
||||||
|
type FilterKeys<Keys> = Keys extends `${KeyChild|KeyGroup}${infer Rest}` ? Keys : never
|
||||||
|
type CrossMultiply<A, B> = A extends string ? B extends string ? `${A}${B}` : never : never
|
||||||
|
type CrossMultiplyRecord<Rec> = keyof Rec | { [K in keyof Rec]: K extends string ? CrossMultiply<K, FilterKeys<CollectKeys<Rec[K]>>> : never }[keyof Rec]
|
||||||
|
type Tier = (selector:string, obj:UserStyles)=>string;
|
||||||
|
type CreateSheet = <T extends UserSheet>(sheet:UserSheet&T)=> ((...args:CrossMultiplyRecord<T>[])=>string)&{css:string}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Gale:Gale.CreateSheet
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user