148 lines
5.6 KiB
JavaScript
148 lines
5.6 KiB
JavaScript
|
/**
|
||
|
* @license
|
||
|
* Copyright 2019 Google LLC
|
||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
*/
|
||
|
import { ElementInternalsShim } from './lib/element-internals.js';
|
||
|
export { ariaMixinAttributes, ElementInternals, HYDRATE_INTERNALS_ATTR_PREFIX, } from './lib/element-internals.js';
|
||
|
const attributes = new WeakMap();
|
||
|
const attributesForElement = (element) => {
|
||
|
let attrs = attributes.get(element);
|
||
|
if (attrs === undefined) {
|
||
|
attributes.set(element, (attrs = new Map()));
|
||
|
}
|
||
|
return attrs;
|
||
|
};
|
||
|
// The typings around the exports below are a little funky:
|
||
|
//
|
||
|
// 1. We want the `name` of the shim classes to match the real ones at runtime,
|
||
|
// hence e.g. `class Element`.
|
||
|
// 2. We can't shadow the global types with a simple class declaration, because
|
||
|
// then we can't reference the global types for casting, hence e.g.
|
||
|
// `const ElementShim = class Element`.
|
||
|
// 3. We want to export the classes typed as the real ones, hence e.g.
|
||
|
// `const ElementShimWithRealType = ElementShim as object as typeof Element;`.
|
||
|
// 4. We want the exported names to match the real ones, hence e.g.
|
||
|
// `export {ElementShimWithRealType as Element}`.
|
||
|
const ElementShim = class Element {
|
||
|
constructor() {
|
||
|
this.__shadowRootMode = null;
|
||
|
this.__shadowRoot = null;
|
||
|
this.__internals = null;
|
||
|
}
|
||
|
get attributes() {
|
||
|
return Array.from(attributesForElement(this)).map(([name, value]) => ({
|
||
|
name,
|
||
|
value,
|
||
|
}));
|
||
|
}
|
||
|
get shadowRoot() {
|
||
|
if (this.__shadowRootMode === 'closed') {
|
||
|
return null;
|
||
|
}
|
||
|
return this.__shadowRoot;
|
||
|
}
|
||
|
setAttribute(name, value) {
|
||
|
// Emulate browser behavior that silently casts all values to string. E.g.
|
||
|
// `42` becomes `"42"` and `{}` becomes `"[object Object]""`.
|
||
|
attributesForElement(this).set(name, String(value));
|
||
|
}
|
||
|
removeAttribute(name) {
|
||
|
attributesForElement(this).delete(name);
|
||
|
}
|
||
|
toggleAttribute(name, force) {
|
||
|
// Steps reference https://dom.spec.whatwg.org/#dom-element-toggleattribute
|
||
|
if (this.hasAttribute(name)) {
|
||
|
// Step 5
|
||
|
if (force === undefined || !force) {
|
||
|
this.removeAttribute(name);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// Step 4
|
||
|
if (force === undefined || force) {
|
||
|
// Step 4.1
|
||
|
this.setAttribute(name, '');
|
||
|
return true;
|
||
|
}
|
||
|
else {
|
||
|
// Step 4.2
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
// Step 6
|
||
|
return true;
|
||
|
}
|
||
|
hasAttribute(name) {
|
||
|
return attributesForElement(this).has(name);
|
||
|
}
|
||
|
attachShadow(init) {
|
||
|
const shadowRoot = { host: this };
|
||
|
this.__shadowRootMode = init.mode;
|
||
|
if (init && init.mode === 'open') {
|
||
|
this.__shadowRoot = shadowRoot;
|
||
|
}
|
||
|
return shadowRoot;
|
||
|
}
|
||
|
attachInternals() {
|
||
|
if (this.__internals !== null) {
|
||
|
throw new Error(`Failed to execute 'attachInternals' on 'HTMLElement': ` +
|
||
|
`ElementInternals for the specified element was already attached.`);
|
||
|
}
|
||
|
const internals = new ElementInternalsShim(this);
|
||
|
this.__internals = internals;
|
||
|
return internals;
|
||
|
}
|
||
|
getAttribute(name) {
|
||
|
const value = attributesForElement(this).get(name);
|
||
|
return value ?? null;
|
||
|
}
|
||
|
};
|
||
|
const ElementShimWithRealType = ElementShim;
|
||
|
export { ElementShimWithRealType as Element };
|
||
|
const HTMLElementShim = class HTMLElement extends ElementShim {
|
||
|
};
|
||
|
const HTMLElementShimWithRealType = HTMLElementShim;
|
||
|
export { HTMLElementShimWithRealType as HTMLElement };
|
||
|
const CustomElementRegistryShim = class CustomElementRegistry {
|
||
|
constructor() {
|
||
|
this.__definitions = new Map();
|
||
|
}
|
||
|
define(name, ctor) {
|
||
|
if (this.__definitions.has(name)) {
|
||
|
if (process.env.NODE_ENV === 'development') {
|
||
|
console.warn(`'CustomElementRegistry' already has "${name}" defined. ` +
|
||
|
`This may have been caused by live reload or hot module ` +
|
||
|
`replacement in which case it can be safely ignored.\n` +
|
||
|
`Make sure to test your application with a production build as ` +
|
||
|
`repeat registrations will throw in production.`);
|
||
|
}
|
||
|
else {
|
||
|
throw new Error(`Failed to execute 'define' on 'CustomElementRegistry': ` +
|
||
|
`the name "${name}" has already been used with this registry`);
|
||
|
}
|
||
|
}
|
||
|
this.__definitions.set(name, {
|
||
|
ctor,
|
||
|
// Note it's important we read `observedAttributes` in case it is a getter
|
||
|
// with side-effects, as is the case in Lit, where it triggers class
|
||
|
// finalization.
|
||
|
//
|
||
|
// TODO(aomarks) To be spec compliant, we should also capture the
|
||
|
// registration-time lifecycle methods like `connectedCallback`. For them
|
||
|
// to be actually accessible to e.g. the Lit SSR element renderer, though,
|
||
|
// we'd need to introduce a new API for accessing them (since `get` only
|
||
|
// returns the constructor).
|
||
|
observedAttributes: ctor.observedAttributes ?? [],
|
||
|
});
|
||
|
}
|
||
|
get(name) {
|
||
|
const definition = this.__definitions.get(name);
|
||
|
return definition?.ctor;
|
||
|
}
|
||
|
};
|
||
|
const CustomElementRegistryShimWithRealType = CustomElementRegistryShim;
|
||
|
export { CustomElementRegistryShimWithRealType as CustomElementRegistry };
|
||
|
export const customElements = new CustomElementRegistryShimWithRealType();
|
||
|
//# sourceMappingURL=index.js.map
|