diff --git a/src/attributes/custom-attributes.ts b/src/attributes/custom-attributes.ts deleted file mode 100644 index c702b57..0000000 --- a/src/attributes/custom-attributes.ts +++ /dev/null @@ -1,157 +0,0 @@ -// Forked from https://github.com/lume/custom-attributes - -type Constructor = (new (...a: A) => T) & Static; - -const forEach = Array.prototype.forEach; - -export class CustomAttributeRegistry { - private _attrMap = new Map(); - private _elementMap = new WeakMap>(); - - 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); diff --git a/src/attributes/move.ts b/src/attributes/move.ts deleted file mode 100644 index 7e408f7..0000000 --- a/src/attributes/move.ts +++ /dev/null @@ -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; - } - } - } -} diff --git a/src/attributes/resize.ts b/src/attributes/resize.ts deleted file mode 100644 index b8cd7b0..0000000 --- a/src/attributes/resize.ts +++ /dev/null @@ -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; - } - } - } -} diff --git a/src/attributes/rotate.ts b/src/attributes/rotate.ts deleted file mode 100644 index c236a47..0000000 --- a/src/attributes/rotate.ts +++ /dev/null @@ -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; - } - } - } -}