1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
export function scope(node) {
return mergeProxies(closestDataStack(node))
}
export function addScopeToNode(node, data, referenceNode) {
node._x_dataStack = [data, ...closestDataStack(referenceNode || node)]
return () => {
node._x_dataStack = node._x_dataStack.filter(i => i !== data)
}
}
export function hasScope(node) {
return !! node._x_dataStack
}
export function refreshScope(element, scope) {
let existingScope = element._x_dataStack[0]
Object.entries(scope).forEach(([key, value]) => {
existingScope[key] = value
})
}
export function closestDataStack(node) {
if (node._x_dataStack) return node._x_dataStack
if (typeof ShadowRoot === 'function' && node instanceof ShadowRoot) {
return closestDataStack(node.host)
}
if (! node.parentNode) {
return []
}
return closestDataStack(node.parentNode)
}
export function closestDataProxy(el) {
return mergeProxies(closestDataStack(el))
}
export function mergeProxies(objects) {
let thisProxy = new Proxy({}, {
ownKeys: () => {
return Array.from(new Set(objects.flatMap(i => Object.keys(i))))
},
has: (target, name) => {
return objects.some(obj => obj.hasOwnProperty(name))
},
get: (target, name) => {
return (objects.find(obj => {
if (obj.hasOwnProperty(name)) {
let descriptor = Object.getOwnPropertyDescriptor(obj, name)
// If we already bound this getter, don't rebind.
if ((descriptor.get && descriptor.get._x_alreadyBound) || (descriptor.set && descriptor.set._x_alreadyBound)) {
return true
}
// Properly bind getters and setters to this wrapper Proxy.
if ((descriptor.get || descriptor.set) && descriptor.enumerable) {
// Only bind user-defined getters, not our magic properties.
let getter = descriptor.get
let setter = descriptor.set
let property = descriptor
getter = getter && getter.bind(thisProxy)
setter = setter && setter.bind(thisProxy)
if (getter) getter._x_alreadyBound = true
if (setter) setter._x_alreadyBound = true
Object.defineProperty(obj, name, {
...property,
get: getter,
set: setter,
})
}
return true
}
return false
}) || {})[name]
},
set: (target, name, value) => {
let closestObjectWithKey = objects.find(obj => obj.hasOwnProperty(name))
if (closestObjectWithKey) {
closestObjectWithKey[name] = value
} else {
objects[objects.length - 1][name] = value
}
return true
},
})
return thisProxy
}
|