|
1 | 1 | {
|
2 | 2 | // This file consistently uses `let` keyword instead of `const` for reducing the bundle size.
|
3 | 3 | // Global variables - aliasing some builtin symbols to reduce the bundle size.
|
4 |
| - let {fromEntries, entries, keys} = Object |
| 4 | + let {fromEntries, entries, keys, hasOwn, getPrototypeOf} = Object |
5 | 5 | let {get: refGet, set: refSet, deleteProperty: refDelete, ownKeys: refOwnKeys} = Reflect
|
6 | 6 | let {state, derive, add} = van
|
7 |
| - let itemsToGc, gcCycleInMs = 1000, _undefined, replacing |
8 |
| - let objSym = Symbol(), statesSym = Symbol(), isCalcFunc = Symbol(), bindingsSym = Symbol() |
9 |
| - let keysGenSym = Symbol(), keyToChildSym = Symbol() |
| 7 | + let statesToGc, gcCycleInMs = 1000, _undefined, replacing |
| 8 | + let statesSym = Symbol(), isCalcFunc = Symbol(), bindingsSym = Symbol(), keysGenSym = Symbol(), keyToChildSym = Symbol() |
10 | 9 | let calc = f => (f[isCalcFunc] = 1, f)
|
11 | 10 | let toState = v => {
|
12 | 11 | if (v?.[isCalcFunc]) {
|
|
19 | 18 | return s
|
20 | 19 | } else return state(reactive(v))
|
21 | 20 | }
|
| 21 | + let buildStates = srcObj => { |
| 22 | + let states = Array.isArray(srcObj) ? [] : {__proto__: getPrototypeOf(srcObj)} |
| 23 | + for (let [k, v] of entries(srcObj)) states[k] = toState(v) |
| 24 | + states[bindingsSym] = [] |
| 25 | + states[keysGenSym] = state(1) |
| 26 | + return states |
| 27 | + } |
22 | 28 | let reactive = srcObj => !(srcObj instanceof Object) || srcObj[statesSym] ? srcObj :
|
23 |
| - new Proxy( |
24 |
| - (srcObj[objSym] = srcObj, |
25 |
| - srcObj[statesSym] = fromEntries(entries(srcObj).map(([k, v]) => [k, toState(v)])), |
26 |
| - srcObj[bindingsSym] = [], |
27 |
| - srcObj[keysGenSym] = state(1), |
28 |
| - srcObj), |
29 |
| - { |
30 |
| - get: (obj, name, proxy) => obj[statesSym].hasOwnProperty(name) ? obj[statesSym][name].val : ( |
31 |
| - name === "length" && obj[keysGenSym].val, |
32 |
| - refGet(obj, name, proxy) |
33 |
| - ), |
34 |
| - set(obj, name, v, proxy) { |
35 |
| - let states = obj[statesSym] |
36 |
| - if (states.hasOwnProperty(name)) return states[name].val = reactive(v), 1 |
37 |
| - let existingKey = name in obj |
38 |
| - let setNewLength = existingKey && name === "length" && v !== obj.length |
39 |
| - if (!refSet(obj, name, v)) return |
40 |
| - existingKey ? |
41 |
| - setNewLength && ++obj[keysGenSym].val : |
42 |
| - refSet(states, name, toState(v)) && (++obj[keysGenSym].val, onAdd(proxy, name, states[name])) |
43 |
| - return 1 |
44 |
| - }, |
45 |
| - deleteProperty: (obj, name) => ( |
46 |
| - refDelete(obj[statesSym], name) && onDelete(obj, name), |
47 |
| - refDelete(obj, name) && ++obj[keysGenSym].val |
48 |
| - ), |
49 |
| - ownKeys: obj => (obj[keysGenSym].val, refOwnKeys(obj)), |
50 |
| - } |
51 |
| - ) |
| 29 | + new Proxy(buildStates(srcObj), { |
| 30 | + get: (states, name, proxy) => |
| 31 | + name === statesSym ? states : |
| 32 | + hasOwn(states, name) ? |
| 33 | + Array.isArray(states) && name === "length" ? |
| 34 | + (states[keysGenSym].val, states.length) : |
| 35 | + states[name].val : |
| 36 | + refGet(states, name, proxy), |
| 37 | + set: (states, name, v, proxy) => |
| 38 | + hasOwn(states, name) ? |
| 39 | + Array.isArray(states) && name === "length" && v !== states.length ? |
| 40 | + (states.length = v, ++states[keysGenSym].val) : |
| 41 | + (states[name].val = reactive(v), 1) : |
| 42 | + name in states ? refSet(states, name, v, proxy) : |
| 43 | + refSet(states, name, toState(v)) && ( |
| 44 | + ++states[keysGenSym].val, |
| 45 | + filterBindings(states).forEach( |
| 46 | + addToContainer.bind(_undefined, proxy, name, states[name], replacing)), |
| 47 | + 1 |
| 48 | + ), |
| 49 | + deleteProperty: (states, name) => |
| 50 | + (refDelete(states, name) && onDelete(states, name), ++states[keysGenSym].val), |
| 51 | + ownKeys: states => (states[keysGenSym].val, refOwnKeys(states)), |
| 52 | + }) |
52 | 53 | let stateFields = obj => obj[statesSym]
|
53 | 54 | let raw = obj => obj[statesSym] ?
|
54 | 55 | new Proxy(obj[statesSym], {get: (obj, name) => raw(obj[name].rawVal)}) : obj
|
55 |
| - let filterBindings = items => |
56 |
| - items[bindingsSym] = items[bindingsSym].filter(b => b._containerDom.isConnected) |
| 56 | + let filterBindings = states => |
| 57 | + states[bindingsSym] = states[bindingsSym].filter(b => b._containerDom.isConnected) |
57 | 58 | let addToContainer = (items, k, v, skipReorder, {_containerDom, f}) => {
|
58 |
| - let isArray = Array.isArray(items) |
| 59 | + let isArray = Array.isArray(items), typedK = isArray ? Number(k) : k |
59 | 60 | add(_containerDom, () =>
|
60 |
| - _containerDom[keyToChildSym][k] = f(v, () => delete items[k], isArray ? Number(k) : k)) |
61 |
| - if (isArray && !skipReorder && k != items.length - 1) { |
62 |
| - let kNumber = Number(k) |
| 61 | + _containerDom[keyToChildSym][k] = f(v, () => delete items[k], typedK)) |
| 62 | + isArray && !skipReorder && typedK !== items.length - 1 && |
63 | 63 | _containerDom.insertBefore(_containerDom.lastChild,
|
64 |
| - _containerDom[keyToChildSym][keys(items).find(key => Number(key) > kNumber)]) |
65 |
| - } |
| 64 | + _containerDom[keyToChildSym][keys(items).find(key => Number(key) > typedK)]) |
66 | 65 | }
|
67 |
| - let onAdd = (items, k, v) => filterBindings(items).forEach( |
68 |
| - addToContainer.bind(_undefined, items, k, v, replacing)) |
69 |
| - let onDelete = (items, k) => { |
70 |
| - for (let b of filterBindings(items)) { |
| 66 | + let onDelete = (states, k) => { |
| 67 | + for (let b of filterBindings(states)) { |
71 | 68 | let keyToChild = b._containerDom[keyToChildSym]
|
72 | 69 | keyToChild[k]?.remove()
|
73 | 70 | delete keyToChild[k]
|
74 | 71 | }
|
75 | 72 | }
|
76 |
| - let addItemsToGc = items => (itemsToGc ?? (itemsToGc = ( |
| 73 | + let addStatesToGc = states => (statesToGc ?? (statesToGc = ( |
77 | 74 | setTimeout(
|
78 |
| - () => (itemsToGc.forEach(filterBindings), itemsToGc = _undefined), gcCycleInMs), |
79 |
| - new Set))).add(items) |
| 75 | + () => (statesToGc.forEach(filterBindings), statesToGc = _undefined), gcCycleInMs), |
| 76 | + new Set))).add(states) |
80 | 77 | let list = (container, items, itemFunc) => {
|
81 | 78 | let binding = {_containerDom: container instanceof Function ? container() : container, f: itemFunc}
|
| 79 | + let states = items[statesSym] |
82 | 80 | binding._containerDom[keyToChildSym] = {}
|
83 |
| - items[bindingsSym].push(binding) |
84 |
| - addItemsToGc(items) |
85 |
| - for (let [k, v] of entries(items[statesSym])) addToContainer(items, k, v, 1, binding) |
| 81 | + states[bindingsSym].push(binding) |
| 82 | + addStatesToGc(states) |
| 83 | + for (let [k, v] of entries(states)) addToContainer(items, k, v, 1, binding) |
86 | 84 | return binding._containerDom
|
87 | 85 | }
|
88 | 86 | let replaceInternal = (obj, replacement) => {
|
89 | 87 | for (let [k, v] of entries(replacement)) {
|
90 | 88 | let existingV = obj[k]
|
91 | 89 | existingV instanceof Object && v instanceof Object ? replaceInternal(existingV, v) : obj[k] = v
|
92 | 90 | }
|
93 |
| - for (let k in obj) replacement.hasOwnProperty(k) || delete obj[k] |
| 91 | + for (let k in obj) hasOwn(replacement, k) || delete obj[k] |
94 | 92 | let newKeys = keys(replacement), isArray = Array.isArray(obj)
|
95 | 93 | if (isArray || keys(obj).some((k, i) => k !== newKeys[i])) {
|
| 94 | + let states = obj[statesSym] |
96 | 95 | if (isArray) obj.length = replacement.length; else {
|
97 |
| - ++obj[keysGenSym].val |
98 |
| - let rawObj = obj[objSym], objCopy = {...rawObj} |
99 |
| - for (let k of newKeys) delete rawObj[k] |
100 |
| - for (let k of newKeys) rawObj[k] = objCopy[k] |
| 96 | + ++states[keysGenSym].val |
| 97 | + let statesCopy = {...states} |
| 98 | + for (let k of newKeys) delete states[k] |
| 99 | + for (let k of newKeys) states[k] = statesCopy[k] |
101 | 100 | }
|
102 |
| - for (let {_containerDom} of filterBindings(obj)) { |
| 101 | + for (let {_containerDom} of filterBindings(states)) { |
103 | 102 | let {firstChild: dom, [keyToChildSym]: keyToChild} = _containerDom
|
104 | 103 | for (let k of newKeys) dom === keyToChild[k] ?
|
105 | 104 | dom = dom.nextSibling : _containerDom.insertBefore(keyToChild[k], dom)
|
|
0 commit comments