@pictogrammers/element
Version:
Web Component Element for TypeScript
113 lines • 4.56 kB
JavaScript
export const addObserver = Symbol('addObserver');
export const removeObserver = Symbol('removeObserver');
export const hasObserver = Symbol('getObservers');
export const isProxy = Symbol('isProxy');
export const getTarget = Symbol('getTarget');
const arrayMutate = ['fill', 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'];
const arrayRead = ['forEach', 'slice', 'some', 'map', 'indexOf', 'lastIndexOf', 'width'];
// key = obj, value = Map<ele, callback[]>
const observers = new Map();
//@ts-ignore
window.observers = observers;
export function createProxy(obj) {
return new Proxy(obj, {
get(target, prop) {
if (typeof prop === 'symbol') {
switch (prop) {
case isProxy:
return true;
case getTarget:
return target;
case hasObserver:
return observers.has(obj);
case addObserver:
return (host, callback) => {
if (observers.has(obj)) {
if (observers.get(obj).has(host)) {
observers.get(obj).get(host).push(callback);
}
else {
observers.get(obj).set(host, [callback]);
}
}
else {
observers.set(obj, new Map([[host, [callback]]]));
}
};
case removeObserver:
return (host) => {
if (observers.has(obj)) {
observers.get(obj).delete(host);
if (observers.get(obj).size === 0) {
observers.delete(obj);
}
}
};
case Symbol.toPrimitive:
case Symbol.toStringTag:
return Reflect.get(target, prop);
}
throw new Error('Unsupported symbol');
}
if (prop in target) {
if (!Number.isNaN(Number(prop))) {
if (typeof target[prop] === 'object') {
return createProxy(target[prop]);
}
return target[prop];
}
if (prop === 'copyWithin') {
throw new Error('Unsupported array method copyWithin');
}
if (arrayMutate.includes(prop)) {
if (observers.has(target)) {
return function () {
const result = Array.prototype[prop].apply(target, arguments);
const map = observers.get(target);
map.forEach((callbacks, host) => {
callbacks.forEach((callback) => {
callback(target, prop, arguments);
});
});
return result;
};
}
return Reflect.get(target, prop);
}
if (target[prop] instanceof Array) {
return createProxy(target[prop]);
}
// nested objects do not get proxied for perf
}
return Reflect.get(target, prop);
},
set(target, prop, value) {
if (typeof prop === 'symbol') {
throw new Error('Setting symbols not allowed.');
}
if (Array.isArray(target)) {
return Reflect.set(target, prop, value);
}
if (observers.has(target)) {
const map = observers.get(target);
map.forEach((callbacks, host) => {
callbacks.forEach((callback) => {
callback(prop, value);
});
});
}
return Reflect.set(target, prop, value);
}
});
}
export const Mutation = {
fill: 'fill',
pop: 'pop',
push: 'push',
reverse: 'reverse',
shift: 'shift',
sort: 'sort',
splice: 'splice',
unshift: 'unshift'
};
//# sourceMappingURL=proxy.js.map