init
This commit is contained in:
commit
f959621812
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"liveServer.settings.port": 5501
|
||||||
|
}
|
57
app.js
Normal file
57
app.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//@ts-check
|
||||||
|
import Gale from "./lib/gale/gale.js";
|
||||||
|
import Van from "./lib/van/van.js";
|
||||||
|
import HMR from "./lib/van/hmr.js";
|
||||||
|
|
||||||
|
const css = Gale({
|
||||||
|
Board:{
|
||||||
|
padding: "1rem",
|
||||||
|
background: "blue",
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const Components = {
|
||||||
|
Title()
|
||||||
|
{
|
||||||
|
const titleString = HMR("title", "default!")
|
||||||
|
return ()=> div({class:css("Board"), onclick(){
|
||||||
|
titleString.val = Math.random();
|
||||||
|
}}, titleString.val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {div} = Van.tags;
|
||||||
|
Van.add(document.body, Components.Title());
|
||||||
|
|
||||||
|
|
||||||
|
const World = {
|
||||||
|
Round: Van.state(0),
|
||||||
|
Turn: Van.state(0),
|
||||||
|
Energy: Van.state([4, 4, 4])
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @typedef {[From:number, To:number]} Transfer */
|
||||||
|
|
||||||
|
/** @typedef {{Transfers:Transfer[], Power:number, Vector:number[], Charge:number, Effect:string}} Ability */
|
||||||
|
|
||||||
|
/** @type Ability */
|
||||||
|
const Ability = {
|
||||||
|
Transfers:[],
|
||||||
|
Power:1,
|
||||||
|
Vector:[],
|
||||||
|
Charge:0,
|
||||||
|
Effect:"damage"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** @typedef {{}} Character */
|
||||||
|
|
||||||
|
function shuffleArray(array) {
|
||||||
|
for (let i = array.length - 1; i > 0; i--) {
|
||||||
|
let j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[array[i], array[j]] = [array[j], array[i]];
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
7
apptest.js
Normal file
7
apptest.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import Test from "./lib/test.js";
|
||||||
|
|
||||||
|
|
||||||
|
/** @type {Test.de} */
|
||||||
|
const thing =()=>{}
|
||||||
|
|
||||||
|
console.log(Test.default("a", "b"));
|
26
appx.js
Normal file
26
appx.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import vanX from "./lib/vanx/vanx.js";
|
||||||
|
import Van from "./lib/van/van.js";
|
||||||
|
|
||||||
|
|
||||||
|
const {span, input, button} = Van.tags;
|
||||||
|
|
||||||
|
|
||||||
|
const data = vanX.reactive({name: {first: "Tao", last: "Xin"}});
|
||||||
|
const flatDerived = vanX.calc(() => `${data.name.first} ${data.name.last}`);
|
||||||
|
|
||||||
|
const Name = () => {
|
||||||
|
|
||||||
|
return span(
|
||||||
|
"First name: ",
|
||||||
|
input({type: "text", value: () => data.name.first,
|
||||||
|
oninput: e => data.name.first = e.target.value}), " ",
|
||||||
|
"Last name: ",
|
||||||
|
input({type: "text", value: () => data.name.last,
|
||||||
|
oninput: e => data.name.last = e.target.value}), " ",
|
||||||
|
//"Full name: ", () => derived.fullName, " ",
|
||||||
|
"CALC:", flatDerived,
|
||||||
|
button({onclick: () => data.name = {first: "Tao", last: "Xin"}}, "Reset"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Van.add(document.body, Name());
|
9
index.html
Normal file
9
index.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head></head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="app.js" type="module"></script>
|
||||||
|
<!--<script src="appx.js" type="module"></script>-->
|
||||||
|
</body>
|
||||||
|
</html>
|
72
lib/gale/gale.js
Normal file
72
lib/gale/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;
|
||||||
|
}
|
6
lib/test.d.ts
vendored
Normal file
6
lib/test.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
export type defaultType = (a:string, b:string)=>string;
|
||||||
|
|
||||||
|
declare const members:defaultType
|
||||||
|
|
||||||
|
export default members
|
4
lib/test.js
Normal file
4
lib/test.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export default function(a, b)
|
||||||
|
{
|
||||||
|
return a + "---" + b;
|
||||||
|
}
|
62
lib/van/hmr.js
Normal file
62
lib/van/hmr.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
//@ts-check
|
||||||
|
import * as Van from "./van.members.js";
|
||||||
|
|
||||||
|
const Gateway = {
|
||||||
|
Time: 0,
|
||||||
|
Temp:{},
|
||||||
|
Tick()
|
||||||
|
{
|
||||||
|
for(let k in Gateway.Temp)
|
||||||
|
{
|
||||||
|
localStorage.setItem(k, Gateway.Temp[k]);
|
||||||
|
}
|
||||||
|
Gateway.Temp = {};
|
||||||
|
Gateway.Time = 0;
|
||||||
|
},
|
||||||
|
Save(key, value)
|
||||||
|
{
|
||||||
|
Gateway.Temp[key] = value;
|
||||||
|
if(!Gateway.Time)
|
||||||
|
{
|
||||||
|
Gateway.Time = setTimeout(Gateway.Tick, 500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Load(key)
|
||||||
|
{
|
||||||
|
return localStorage.getItem(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HMR Wrapper for Van.state
|
||||||
|
* @template T
|
||||||
|
* @param {T} value - initial value
|
||||||
|
* @param {string} key - Storage ID
|
||||||
|
* @returns {Van.State<T>}
|
||||||
|
*/
|
||||||
|
export default function(value, key)
|
||||||
|
{
|
||||||
|
const type = typeof value;
|
||||||
|
let reader =(data)=>data;
|
||||||
|
let writer =(data)=> data.toString();
|
||||||
|
|
||||||
|
if(type === "object")
|
||||||
|
{
|
||||||
|
reader = JSON.parse;
|
||||||
|
writer = JSON.stringify;
|
||||||
|
}
|
||||||
|
else if(type === "number")
|
||||||
|
{
|
||||||
|
reader = parseFloat;
|
||||||
|
}
|
||||||
|
else if(type === "boolean")
|
||||||
|
{
|
||||||
|
reader =(data)=> data === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
const stringValue = Gateway.Load(key);
|
||||||
|
const signal = Van.state(/**@type{T}*/(stringValue ? reader(stringValue) : value));
|
||||||
|
Van.derive(()=>Gateway.Save(key, writer(signal.val)));
|
||||||
|
|
||||||
|
return signal;
|
||||||
|
}
|
48
lib/van/van.d.ts
vendored
Normal file
48
lib/van/van.d.ts
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
export 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>
|
||||||
|
export type StateView<T> = Readonly<State<T>>
|
||||||
|
|
||||||
|
export type Val<T> = State<T> | T
|
||||||
|
|
||||||
|
export type Primitive = string | number | boolean | bigint
|
||||||
|
|
||||||
|
export type PropValue = Primitive | ((e: any) => void) | null
|
||||||
|
|
||||||
|
export type PropValueOrDerived = PropValue | StateView<PropValue> | (() => PropValue)
|
||||||
|
|
||||||
|
export type Props = Record<string, PropValueOrDerived> & { class?: PropValueOrDerived; is?: string }
|
||||||
|
|
||||||
|
export type PropsWithKnownKeys<ElementType> = Partial<{[K in keyof ElementType]: PropValueOrDerived}>
|
||||||
|
|
||||||
|
export type ValidChildDomValue = Primitive | Node | null | undefined
|
||||||
|
|
||||||
|
export type BindingFunc = ((dom?: Node) => ValidChildDomValue) | ((dom?: Element) => Element)
|
||||||
|
|
||||||
|
export type ChildDom = ValidChildDomValue | StateView<Primitive | null | undefined> | BindingFunc | readonly ChildDom[]
|
||||||
|
|
||||||
|
export 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]>
|
||||||
|
}
|
||||||
|
|
||||||
|
declare function state<T>(): State<T>
|
||||||
|
declare function state<T>(initVal: T): State<T>
|
||||||
|
|
||||||
|
export interface Van {
|
||||||
|
readonly state: typeof state
|
||||||
|
readonly derive: <T>(f: () => T) => State<T>
|
||||||
|
readonly add: (dom: Element, ...children: readonly ChildDom[]) => Element
|
||||||
|
readonly tags: Tags & ((namespaceURI: string) => Readonly<Record<string, TagFunc<Element>>>)
|
||||||
|
readonly hydrate: <T extends Node>(dom: T, f: (dom: T) => T | null | undefined) => T
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const van: Van
|
||||||
|
|
||||||
|
export default van
|
140
lib/van/van.js
Normal file
140
lib/van/van.js
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// This file consistently uses `let` keyword instead of `const` for reducing the bundle size.
|
||||||
|
|
||||||
|
// Global variables - aliasing some builtin symbols to reduce the bundle size.
|
||||||
|
let protoOf = Object.getPrototypeOf
|
||||||
|
let changedStates, derivedStates, curDeps, curNewDerives, alwaysConnectedDom = {isConnected: 1}
|
||||||
|
let gcCycleInMs = 1000, statesToGc, propSetterCache = {}
|
||||||
|
let objProto = protoOf(alwaysConnectedDom), funcProto = protoOf(protoOf), _undefined
|
||||||
|
|
||||||
|
let addAndScheduleOnFirst = (set, s, f, waitMs) =>
|
||||||
|
(set ?? (setTimeout(f, waitMs), new Set)).add(s)
|
||||||
|
|
||||||
|
let runAndCaptureDeps = (f, deps, arg) => {
|
||||||
|
let prevDeps = curDeps
|
||||||
|
curDeps = deps
|
||||||
|
try {
|
||||||
|
return f(arg)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
return arg
|
||||||
|
} finally {
|
||||||
|
curDeps = prevDeps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let keepConnected = l => l.filter(b => b._dom?.isConnected)
|
||||||
|
|
||||||
|
let addStatesToGc = d => statesToGc = addAndScheduleOnFirst(statesToGc, d, () => {
|
||||||
|
for (let s of statesToGc)
|
||||||
|
s._bindings = keepConnected(s._bindings),
|
||||||
|
s._listeners = keepConnected(s._listeners)
|
||||||
|
statesToGc = _undefined
|
||||||
|
}, gcCycleInMs)
|
||||||
|
|
||||||
|
let stateProto = {
|
||||||
|
get val() {
|
||||||
|
curDeps?._getters?.add(this)
|
||||||
|
return this.rawVal
|
||||||
|
},
|
||||||
|
|
||||||
|
get oldVal() {
|
||||||
|
curDeps?._getters?.add(this)
|
||||||
|
return this._oldVal
|
||||||
|
},
|
||||||
|
|
||||||
|
set val(v) {
|
||||||
|
curDeps?._setters?.add(this)
|
||||||
|
if (v !== this.rawVal) {
|
||||||
|
this.rawVal = v
|
||||||
|
this._bindings.length + this._listeners.length ?
|
||||||
|
(derivedStates?.add(this), changedStates = addAndScheduleOnFirst(changedStates, this, updateDoms)) :
|
||||||
|
this._oldVal = v
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = initVal => ({
|
||||||
|
__proto__: stateProto,
|
||||||
|
rawVal: initVal,
|
||||||
|
_oldVal: initVal,
|
||||||
|
_bindings: [],
|
||||||
|
_listeners: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
let bind = (f, dom) => {
|
||||||
|
let deps = {_getters: new Set, _setters: new Set}, binding = {f}, prevNewDerives = curNewDerives
|
||||||
|
curNewDerives = []
|
||||||
|
let newDom = runAndCaptureDeps(f, deps, dom)
|
||||||
|
newDom = (newDom ?? document).nodeType ? newDom : new Text(newDom)
|
||||||
|
for (let d of deps._getters)
|
||||||
|
deps._setters.has(d) || (addStatesToGc(d), d._bindings.push(binding))
|
||||||
|
for (let l of curNewDerives) l._dom = newDom
|
||||||
|
curNewDerives = prevNewDerives
|
||||||
|
return binding._dom = newDom
|
||||||
|
}
|
||||||
|
|
||||||
|
let derive = (f, s = state(), dom) => {
|
||||||
|
let deps = {_getters: new Set, _setters: new Set}, listener = {f, s}
|
||||||
|
listener._dom = dom ?? curNewDerives?.push(listener) ?? alwaysConnectedDom
|
||||||
|
s.val = runAndCaptureDeps(f, deps, s.rawVal)
|
||||||
|
for (let d of deps._getters)
|
||||||
|
deps._setters.has(d) || (addStatesToGc(d), d._listeners.push(listener))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
let add = (dom, ...children) => {
|
||||||
|
for (let c of children.flat(Infinity)) {
|
||||||
|
let protoOfC = protoOf(c ?? 0)
|
||||||
|
let child = protoOfC === stateProto ? bind(() => c.val) :
|
||||||
|
protoOfC === funcProto ? bind(c) : c
|
||||||
|
child != _undefined && dom.append(child)
|
||||||
|
}
|
||||||
|
return dom
|
||||||
|
}
|
||||||
|
|
||||||
|
let tag = (ns, name, ...args) => {
|
||||||
|
let [{is, ...props}, ...children] = protoOf(args[0] ?? 0) === objProto ? args : [{}, ...args]
|
||||||
|
let dom = ns ? document.createElementNS(ns, name, {is}) : document.createElement(name, {is})
|
||||||
|
for (let [k, v] of Object.entries(props)) {
|
||||||
|
let getPropDescriptor = proto => proto ?
|
||||||
|
Object.getOwnPropertyDescriptor(proto, k) ?? getPropDescriptor(protoOf(proto)) :
|
||||||
|
_undefined
|
||||||
|
let cacheKey = name + "," + k
|
||||||
|
let propSetter = propSetterCache[cacheKey] ??= getPropDescriptor(protoOf(dom))?.set ?? 0
|
||||||
|
let setter = k.startsWith("on") ?
|
||||||
|
(v, oldV) => {
|
||||||
|
let event = k.slice(2)
|
||||||
|
dom.removeEventListener(event, oldV)
|
||||||
|
dom.addEventListener(event, v)
|
||||||
|
} :
|
||||||
|
propSetter ? propSetter.bind(dom) : dom.setAttribute.bind(dom, k)
|
||||||
|
let protoOfV = protoOf(v ?? 0)
|
||||||
|
k.startsWith("on") || protoOfV === funcProto && (v = derive(v), protoOfV = stateProto)
|
||||||
|
protoOfV === stateProto ? bind(() => (setter(v.val, v._oldVal), dom)) : setter(v)
|
||||||
|
}
|
||||||
|
return add(dom, children)
|
||||||
|
}
|
||||||
|
|
||||||
|
let handler = ns => ({get: (_, name) => tag.bind(_undefined, ns, name)})
|
||||||
|
|
||||||
|
let update = (dom, newDom) => newDom ? newDom !== dom && dom.replaceWith(newDom) : dom.remove()
|
||||||
|
|
||||||
|
let updateDoms = () => {
|
||||||
|
let iter = 0, derivedStatesArray = [...changedStates].filter(s => s.rawVal !== s._oldVal)
|
||||||
|
do {
|
||||||
|
derivedStates = new Set
|
||||||
|
for (let l of new Set(derivedStatesArray.flatMap(s => s._listeners = keepConnected(s._listeners))))
|
||||||
|
derive(l.f, l.s, l._dom), l._dom = _undefined
|
||||||
|
} while (++iter < 100 && (derivedStatesArray = [...derivedStates]).length)
|
||||||
|
let changedStatesArray = [...changedStates].filter(s => s.rawVal !== s._oldVal)
|
||||||
|
changedStates = _undefined
|
||||||
|
for (let b of new Set(changedStatesArray.flatMap(s => s._bindings = keepConnected(s._bindings))))
|
||||||
|
update(b._dom, bind(b.f, b._dom)), b._dom = _undefined
|
||||||
|
for (let s of changedStatesArray) s._oldVal = s.rawVal
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
tags: new Proxy(ns => new Proxy(tag, handler(ns)), handler()),
|
||||||
|
hydrate: (dom, f) => update(dom, bind(f, dom)),
|
||||||
|
add, state, derive,
|
||||||
|
}
|
8
lib/van/van.members.js
Normal file
8
lib/van/van.members.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export * from "./van.js";
|
||||||
|
import Van from "./van.js";
|
||||||
|
|
||||||
|
export const add = Van.add;
|
||||||
|
export const derive = Van.derive;
|
||||||
|
export const state = Van.state;
|
||||||
|
export const hydrate = Van.hydrate;
|
||||||
|
export const tags = Van.tags;
|
8
lib/vanx/hmr.js
Normal file
8
lib/vanx/hmr.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import * as VanX from "./vanx.js";
|
||||||
|
|
||||||
|
const HMR =()=>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
VanX.default.reactive
|
24
lib/vanx/vanx.d.ts
vendored
Normal file
24
lib/vanx/vanx.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import * as Van from "../van/van.members.js";
|
||||||
|
|
||||||
|
export type StateOf<T> = { readonly [K in keyof T]: Van.State<T[K]> }
|
||||||
|
export type ValueType<T> = T extends (infer V)[] ? V : T[keyof T]
|
||||||
|
export type KeyType<T> = T extends unknown[] ? number : string
|
||||||
|
export type ReplacementFunc<T> =
|
||||||
|
T extends (infer V)[] ? (items: V[]) => readonly V[] :
|
||||||
|
(items: [string, T[keyof T]][]) => readonly [string, T[keyof T]][]
|
||||||
|
|
||||||
|
declare const vanX:{
|
||||||
|
calc: <R>(f: () => R) => R
|
||||||
|
reactive: <T extends object>(obj: T) => T
|
||||||
|
noreactive: <T extends object>(obj: T) => T,
|
||||||
|
stateFields: <T extends object>(obj: T) => StateOf<T>
|
||||||
|
raw: <T extends object>(obj: T) => T,
|
||||||
|
list: <T extends object, ElementType extends Element>(
|
||||||
|
container: (() => ElementType) | ElementType, items: T,
|
||||||
|
itemFunc: (v: Van.State<ValueType<T>>,
|
||||||
|
deleter: () => void, k: KeyType<T>) => Node
|
||||||
|
) => ElementType,
|
||||||
|
replace: <T extends object>(obj: T, replacement: ReplacementFunc<T> | T) => T,
|
||||||
|
compact: <T extends object>(obj: T) => T,
|
||||||
|
}
|
||||||
|
export default vanX;
|
5
lib/vanx/vanx.js
Normal file
5
lib/vanx/vanx.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import van from "../van/van.js";
|
||||||
|
let e,t,r,{fromEntries:o,entries:l,keys:n,hasOwn:f,getPrototypeOf:a}=Object,{get:i,set:y,deleteProperty:c,ownKeys:s}=Reflect,
|
||||||
|
{state:m,derive:d,add:u}=van,
|
||||||
|
b=1e3,w=Symbol(),A=Symbol(),S=Symbol(),_=Symbol(),g=Symbol(),p=Symbol(),P=e=>(e[A]=1,e),v=e=>e instanceof Object&&!(e instanceof Function)&&!e[p],h=e=>{if(e?.[A]){let t=m();return d(()=>{let r=e();v(t.rawVal)&&v(r)?B(t.rawVal,r):t.val=x(r)}),t}return m(x(e))},F=e=>{let t=Array.isArray(e)?[]:{__proto__:a(e)};for(let[r,o]of l(e))t[r]=h(o);return t[S]=[],t[_]=m(1),t},O={get:(e,t,r)=>t===w?e:f(e,t)?Array.isArray(e)&&"length"===t?(e[_].val,e.length):e[t].val:i(e,t,r),set:(e,o,l,n)=>f(e,o)?Array.isArray(e)&&"length"===o?(l!==e.length&&++e[_].val,e.length=l,1):(e[o].val=x(l),1):o in e?y(e,o,l,n):y(e,o,h(l))&&(++e[_].val,C(e).forEach(E.bind(t,n,o,e[o],r)),1),deleteProperty:(e,t)=>(c(e,t)&&R(e,t),++e[_].val),ownKeys:e=>(e[_].val,s(e))},x=e=>!v(e)||e[w]?e:new Proxy(F(e),O),D=e=>(e[p]=1,e),j=e=>e[w],K=a(m()),N=e=>new Proxy(e,{get:(e,t,r)=>a(e[t]??0)===K?{val:k(e[t].rawVal)}:i(e,t,r)}),k=e=>e?.[w]?new Proxy(N(e[w]),O):e,C=e=>e[S]=e[S].filter(e=>e.t.isConnected),E=(e,t,r,o,{t:l,f:f})=>{let a=Array.isArray(e),i=a?Number(t):t;u(l,()=>l[g][t]=f(r,()=>delete e[t],i)),a&&!o&&i!==e.length-1&&l.insertBefore(l.lastChild,l[g][n(e).find(e=>Number(e)>i)])},R=(e,t)=>{for(let r of C(e)){let e=r.t[g];e[t]?.remove(),delete e[t]}},T=r=>(e??(setTimeout(()=>(e.forEach(C),e=t),b),e=new Set)).add(r),q=(e,t,r)=>{let o={t:e instanceof Function?e():e,f:r},n=t[w];o.t[g]={},n[S].push(o),T(n);for(let[e,r]of l(n))E(t,e,r,1,o);return o.t},z=(e,t)=>{for(let[r,o]of l(t)){let t=e[r];v(t)&&v(o)?z(t,o):e[r]=o}for(let r in e)f(t,r)||delete e[r];let r=n(t),o=Array.isArray(e);if(o||n(e).some((e,t)=>e!==r[t])){let l=e[w];if(o)e.length=t.length;else{++l[_].val;let e={...l};for(let e of r)delete l[e];for(let t of r)l[t]=e[t]}for(let{t:e}of C(l)){let{firstChild:t,[g]:o}=e;for(let l of r)t===o[l]?t=t.nextSibling:e.insertBefore(o[l],t)}}return e},B=(e,n)=>{r=1;try{return z(e,n instanceof Function?Array.isArray(e)?n(e.filter(e=>1)):o(n(l(e))):n)}finally{r=t}},G=e=>Array.isArray(e)?e.filter(e=>1).map(G):v(e)?o(l(e).map(([e,t])=>[e,G(t)])):e;
|
||||||
|
export default {calc:P,reactive:x,noreactive:D,stateFields:j,raw:k,list:q,replace:B,compact:G}
|
6
tsconfig.json
Normal file
6
tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"checkJs": true,
|
||||||
|
"lib": ["ES2024", "DOM"]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user