remove
This commit is contained in:
parent
7f01c93d7c
commit
b3138b6e91
|
|
@ -1,157 +0,0 @@
|
||||||
// Forked from https://github.com/lume/custom-attributes
|
|
||||||
|
|
||||||
type Constructor<T = object, A extends any[] = any[], Static = {}> = (new (...a: A) => T) & Static;
|
|
||||||
|
|
||||||
const forEach = Array.prototype.forEach;
|
|
||||||
|
|
||||||
export class CustomAttributeRegistry {
|
|
||||||
private _attrMap = new Map<string, Constructor>();
|
|
||||||
private _elementMap = new WeakMap<Element, Map<string, CustomAttribute>>();
|
|
||||||
|
|
||||||
private _observer: MutationObserver = new MutationObserver((mutations) => {
|
|
||||||
forEach.call(mutations, (m: MutationRecord) => {
|
|
||||||
if (m.type === 'attributes') {
|
|
||||||
const attr = this._getConstructor(m.attributeName!);
|
|
||||||
if (attr) this._handleChange(m.attributeName!, m.target as Element, m.oldValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// childList
|
|
||||||
else {
|
|
||||||
forEach.call(m.removedNodes, this._elementDisconnected);
|
|
||||||
forEach.call(m.addedNodes, this._elementConnected);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
constructor(public ownerDocument: Document | ShadowRoot) {
|
|
||||||
if (!ownerDocument) throw new Error('Must be given a document');
|
|
||||||
}
|
|
||||||
|
|
||||||
define(attrName: string, Class: Constructor) {
|
|
||||||
this._attrMap.set(attrName, Class);
|
|
||||||
this._upgradeAttr(attrName);
|
|
||||||
this._reobserve();
|
|
||||||
}
|
|
||||||
|
|
||||||
get(element: Element, attrName: string) {
|
|
||||||
const map = this._elementMap.get(element);
|
|
||||||
if (!map) return;
|
|
||||||
return map.get(attrName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getConstructor(attrName: string) {
|
|
||||||
return this._attrMap.get(attrName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _observe() {
|
|
||||||
this._observer.observe(this.ownerDocument, {
|
|
||||||
childList: true,
|
|
||||||
subtree: true,
|
|
||||||
attributes: true,
|
|
||||||
attributeOldValue: true,
|
|
||||||
attributeFilter: Array.from(this._attrMap.keys()),
|
|
||||||
// attributeFilter: [...this._attrMap.keys()], // Broken in Oculus
|
|
||||||
// attributeFilter: this._attrMap.keys(), // This works in Chrome, but TS complains, and not clear if it should work in all browsers yet: https://github.com/whatwg/dom/issues/1092
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _unobserve() {
|
|
||||||
this._observer.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _reobserve() {
|
|
||||||
this._unobserve();
|
|
||||||
this._observe();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _upgradeAttr(
|
|
||||||
attrName: string,
|
|
||||||
node: Element | Document | ShadowRoot = this.ownerDocument
|
|
||||||
) {
|
|
||||||
const matches = node.querySelectorAll('[' + attrName + ']');
|
|
||||||
|
|
||||||
// Possibly create custom attributes that may be in the given 'node' tree.
|
|
||||||
// Use a forEach as Edge doesn't support for...of on a NodeList
|
|
||||||
forEach.call(matches, (element: Element) => this._handleChange(attrName, element, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
private _elementConnected = (element: Element) => {
|
|
||||||
if (element.nodeType !== 1) return;
|
|
||||||
|
|
||||||
// For each of the connected element's attribute, possibly instantiate the custom attributes.
|
|
||||||
// Use a forEach as Safari 10 doesn't support for...of on NamedNodeMap (attributes)
|
|
||||||
forEach.call(element.attributes, (attr: Attr) => {
|
|
||||||
if (this._getConstructor(attr.name)) this._handleChange(attr.name, element, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Possibly instantiate custom attributes that may be in the subtree of the connected element.
|
|
||||||
this._attrMap.forEach((_constructor, attr) => this._upgradeAttr(attr, element));
|
|
||||||
};
|
|
||||||
|
|
||||||
private _elementDisconnected = (element: Element) => {
|
|
||||||
const map = this._elementMap.get(element);
|
|
||||||
if (!map) return;
|
|
||||||
|
|
||||||
map.forEach((inst) => inst.disconnectedCallback?.(), this);
|
|
||||||
|
|
||||||
this._elementMap.delete(element);
|
|
||||||
};
|
|
||||||
|
|
||||||
private _handleChange(attrName: string, el: Element, oldVal: string | null) {
|
|
||||||
let map = this._elementMap.get(el);
|
|
||||||
if (!map) this._elementMap.set(el, (map = new Map()));
|
|
||||||
|
|
||||||
let inst = map.get(attrName);
|
|
||||||
const newVal = el.getAttribute(attrName);
|
|
||||||
|
|
||||||
// Attribute is being created
|
|
||||||
if (!inst) {
|
|
||||||
const Constructor = this._getConstructor(attrName)!;
|
|
||||||
inst = new Constructor() as CustomAttribute;
|
|
||||||
map.set(attrName, inst);
|
|
||||||
inst.ownerElement = el;
|
|
||||||
inst.name = attrName;
|
|
||||||
if (newVal == null) throw new Error('Not possible!');
|
|
||||||
inst.value = newVal;
|
|
||||||
inst.connectedCallback?.();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute was removed
|
|
||||||
if (newVal == null) {
|
|
||||||
inst.disconnectedCallback?.();
|
|
||||||
map.delete(attrName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute changed
|
|
||||||
else if (newVal !== inst.value) {
|
|
||||||
inst.value = newVal;
|
|
||||||
if (oldVal == null) throw new Error('Not possible!');
|
|
||||||
inst.changedCallback?.(oldVal, newVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CustomAttribute {
|
|
||||||
ownerElement: Element;
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
connectedCallback?(): void;
|
|
||||||
disconnectedCallback?(): void;
|
|
||||||
changedCallback?(oldValue: string, newValue: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid errors trying to use DOM APIs in non-DOM environments (f.e. server-side rendering).
|
|
||||||
if (globalThis.window?.document) {
|
|
||||||
const _attachShadow = Element.prototype.attachShadow;
|
|
||||||
|
|
||||||
Element.prototype.attachShadow = function attachShadow(options) {
|
|
||||||
const root = _attachShadow.call(this, options);
|
|
||||||
|
|
||||||
if (!root.customAttributes) root.customAttributes = new CustomAttributeRegistry(root);
|
|
||||||
|
|
||||||
return root;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const customAttributes = new CustomAttributeRegistry(document);
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
import { CustomAttribute } from './custom-attributes';
|
|
||||||
|
|
||||||
export class Moveable extends CustomAttribute {
|
|
||||||
connectedCallback() {
|
|
||||||
this.ownerElement.addEventListener('pointerdown', this);
|
|
||||||
this.ownerElement.addEventListener('lostpointercapture', this);
|
|
||||||
this.ownerElement.addEventListener('touchstart', this);
|
|
||||||
this.ownerElement.addEventListener('dragstart', this);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
this.ownerElement.removeEventListener('pointerdown', this);
|
|
||||||
this.ownerElement.removeEventListener('lostpointercapture', this);
|
|
||||||
this.ownerElement.removeEventListener('touchstart', this);
|
|
||||||
this.ownerElement.removeEventListener('dragstart', this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEvent(event: PointerEvent) {
|
|
||||||
switch (event.type) {
|
|
||||||
case 'pointerdown': {
|
|
||||||
if (event.button !== 0 || event.ctrlKey) return;
|
|
||||||
|
|
||||||
this.ownerElement.addEventListener('pointermove', this);
|
|
||||||
this.ownerElement.setPointerCapture(event.pointerId);
|
|
||||||
(this.ownerElement as HTMLElement).style.userSelect = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 'pointermove': {
|
|
||||||
const { left, top } = window.getComputedStyle(this.ownerElement);
|
|
||||||
let leftValue = parseInt(left);
|
|
||||||
let topValue = parseInt(top);
|
|
||||||
(this.ownerElement as HTMLElement).style.left = `${leftValue + event.movementX}px`;
|
|
||||||
(this.ownerElement as HTMLElement).style.top = `${topValue + event.movementY}px`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 'lostpointercapture': {
|
|
||||||
(this.ownerElement as HTMLElement).style.userSelect = '';
|
|
||||||
this.ownerElement.removeEventListener('pointermove', this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 'touchstart':
|
|
||||||
case 'dragstart': {
|
|
||||||
event.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
import { CustomAttribute } from './custom-attributes';
|
|
||||||
|
|
||||||
export class Resizable extends CustomAttribute {
|
|
||||||
// Add resize handlers and CSS variables
|
|
||||||
connectedCallback() {
|
|
||||||
this.ownerElement.addEventListener('pointerdown', this);
|
|
||||||
this.ownerElement.addEventListener('lostpointercapture', this);
|
|
||||||
this.ownerElement.addEventListener('touchstart', this);
|
|
||||||
this.ownerElement.addEventListener('dragstart', this);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
this.ownerElement.removeEventListener('pointerdown', this);
|
|
||||||
this.ownerElement.removeEventListener('lostpointercapture', this);
|
|
||||||
this.ownerElement.removeEventListener('touchstart', this);
|
|
||||||
this.ownerElement.removeEventListener('dragstart', this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEvent(event: PointerEvent) {
|
|
||||||
switch (event.type) {
|
|
||||||
case 'pointerdown': {
|
|
||||||
if (event.button !== 0 || event.ctrlKey) return;
|
|
||||||
|
|
||||||
this.ownerElement.addEventListener('pointermove', this);
|
|
||||||
this.ownerElement.setPointerCapture(event.pointerId);
|
|
||||||
(this.ownerElement as HTMLElement).style.userSelect = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO
|
|
||||||
case 'pointermove': {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 'lostpointercapture': {
|
|
||||||
(this.ownerElement as HTMLElement).style.userSelect = '';
|
|
||||||
this.ownerElement.removeEventListener('pointermove', this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 'touchstart':
|
|
||||||
case 'dragstart': {
|
|
||||||
event.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
import { CustomAttribute } from './custom-attributes';
|
|
||||||
|
|
||||||
export class Rotatable extends CustomAttribute {
|
|
||||||
// Add rotate handlers and CSS variables
|
|
||||||
connectedCallback() {
|
|
||||||
this.ownerElement.addEventListener('pointerdown', this);
|
|
||||||
this.ownerElement.addEventListener('lostpointercapture', this);
|
|
||||||
this.ownerElement.addEventListener('touchstart', this);
|
|
||||||
this.ownerElement.addEventListener('dragstart', this);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
this.ownerElement.removeEventListener('pointerdown', this);
|
|
||||||
this.ownerElement.removeEventListener('lostpointercapture', this);
|
|
||||||
this.ownerElement.removeEventListener('touchstart', this);
|
|
||||||
this.ownerElement.removeEventListener('dragstart', this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEvent(event: PointerEvent) {
|
|
||||||
switch (event.type) {
|
|
||||||
case 'pointerdown': {
|
|
||||||
if (event.button !== 0 || event.ctrlKey) return;
|
|
||||||
|
|
||||||
this.ownerElement.addEventListener('pointermove', this);
|
|
||||||
this.ownerElement.setPointerCapture(event.pointerId);
|
|
||||||
(this.ownerElement as HTMLElement).style.userSelect = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO
|
|
||||||
case 'pointermove': {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 'lostpointercapture': {
|
|
||||||
(this.ownerElement as HTMLElement).style.userSelect = '';
|
|
||||||
this.ownerElement.removeEventListener('pointermove', this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 'touchstart':
|
|
||||||
case 'dragstart': {
|
|
||||||
event.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue