Skip to content

Commit

Permalink
Update custom elements with adoptedCallback
Browse files Browse the repository at this point in the history
For custom elements that use constructed CSS, we have to implement
the adoptedCallback method and manually reconstruct and reattach
a new CSS object to the shadow DOM. This is because when a custom
element is moved to a new document, any constructed CSS objects
that it has adopted already are silently stripped.

We also need to update how we construct our CSS objects. Specifically
we need to ensure we are accessing the current owner document of our
custom element, and not bind the root document from which ever window
we defined our custom element in.

We also can no longer use a static variable to store our CSS obj,
since in Chrome based browsers the static element is shared across
windows (though not the case in firefox). So we need to keep a map
from document to CSS obj singleton and do the lookup manually.
  • Loading branch information
Posnet committed Feb 14, 2024
1 parent fadc6b7 commit f9a038b
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 26 deletions.
30 changes: 23 additions & 7 deletions module/applications/components/filigree-box.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,36 @@ export default class FiligreeBoxElement extends HTMLElement {
/* -------------------------------------------- */

/**
* The stylesheet to attach to the element's shadow root.
* @type {CSSStyleSheet}
* The stylesheets map to store the CSS singleton
* associated with a particular shadowroot.
* @type {WeakMap<Document, CSSStyleSheet>}
* @protected
*/
static _stylesheet;
static _stylesheets;

/* -------------------------------------------- */

/** @override */
adoptedCallback() {
this.#buildStyle();
}

/* -------------------------------------------- */

/**
* Build the shadow DOM's styles.
*/
#buildStyle() {
if ( !this.constructor._stylesheet ) {
this.constructor._stylesheet = new CSSStyleSheet();
this.constructor._stylesheet.replaceSync(`
if (!this.constructor._stylesheets) {
this.constructor._stylesheets = new WeakMap();
}

const _window = this.ownerDocument.defaultView;
let cssObj = this.constructor._stylesheets.get(_window.document);

if (!cssObj) {
cssObj = new _window.CSSStyleSheet();
cssObj.replaceSync(`
:host {
position: relative;
isolation: isolate;
Expand Down Expand Up @@ -92,8 +107,9 @@ export default class FiligreeBoxElement extends HTMLElement {
inset-block: 30px;
}
`);
this.constructor._stylesheets.set(_window.document, cssObj);
}
this.#shadowRoot.adoptedStyleSheets = [this.constructor._stylesheet];
this.#shadowRoot.adoptedStyleSheets = [this.constructor._stylesheets.get(_window.document)];
}

/* -------------------------------------------- */
Expand Down
41 changes: 31 additions & 10 deletions module/applications/components/icon.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ export default class IconElement extends HTMLElement {
/* -------------------------------------------- */

/**
* Stylesheet that is shared among all icons.
* @type {CSSStyleSheet}
* The stylesheets map to store the CSS singleton
* associated with a particular shadowroot.
* @type {WeakMap<Document, CSSStyleSheet>}
*/
static #stylesheet;
static #stylesheets;

/* -------------------------------------------- */

Expand All @@ -51,14 +52,20 @@ export default class IconElement extends HTMLElement {
*/
static #svgCache = new Map();


/* -------------------------------------------- */

/** @inheritDoc */
connectedCallback() {
// Create icon styles
if ( !this.constructor.#stylesheet ) {
this.constructor.#stylesheet = new CSSStyleSheet();
this.constructor.#stylesheet.replaceSync(`
#buildCSS() {
if (!this.constructor.#stylesheets) {
this.constructor.#stylesheets = new WeakMap();
}

const _window = this.ownerDocument.defaultView;
let cssObj = this.constructor.#stylesheets.get(_window.document);

if (!cssObj) {
cssObj = new _window.CSSStyleSheet();
cssObj.replaceSync(`
:host {
display: contents;
}
Expand All @@ -68,9 +75,23 @@ export default class IconElement extends HTMLElement {
height: var(--icon-height, var(--icon-size, 1em));
}
`);
this.constructor.#stylesheets.set(_window.document, cssObj);
}
this.#shadowRoot.adoptedStyleSheets = [this.constructor.#stylesheet];
this.#shadowRoot.adoptedStyleSheets = [this.constructor.#stylesheets.get(_window.document)];
}

/* -------------------------------------------- */

/** @override */
adoptedCallback() {
this.#buildCSS();
}

/* -------------------------------------------- */

/** @inheritDoc */
connectedCallback() {
this.#buildCSS();
const insertElement = element => {
if ( !element ) return;
const clone = element.cloneNode(true);
Expand Down
33 changes: 24 additions & 9 deletions module/applications/components/proficiency-cycle.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ export default class ProficiencyCycleElement extends HTMLElement {
#shadowRoot;

/**
* The stylesheet to attach to the element's shadow root.
* @type {CSSStyleSheet}
* The stylesheets map to store the CSS singleton
* associated with a particular shadowroot.
* @type {WeakMap<Document, CSSStyleSheet>}
* @protected
*/
static _stylesheet;
static _stylesheets;

/* -------------------------------------------- */

Expand Down Expand Up @@ -120,9 +121,15 @@ export default class ProficiencyCycleElement extends HTMLElement {
/* Methods */
/* -------------------------------------------- */

/** @override */
adoptedCallback() {
this.#buildCSS();
}

/* -------------------------------------------- */

/** @override */
connectedCallback() {
this.replaceChildren();
this.#buildHTML();
this.#refreshValue();

Expand All @@ -139,9 +146,16 @@ export default class ProficiencyCycleElement extends HTMLElement {
* Build the CSS internals.
*/
#buildCSS() {
if ( !this.constructor._stylesheet ) {
this.constructor._stylesheet = new CSSStyleSheet();
this.constructor._stylesheet.replaceSync(`
if (!this.constructor._stylesheets) {
this.constructor._stylesheets = new WeakMap();
}

const _window = this.ownerDocument.defaultView;
let cssObj = this.constructor._stylesheets.get(_window.document);

if (!cssObj) {
cssObj = new _window.CSSStyleSheet();
cssObj.replaceSync(`
:host { display: inline-block; }
div { --_fill: var(--proficiency-cycle-enabled-color, var(--dnd5e-color-blue)); }
div:has(:disabled, :focus-visible) { --_fill: var(--proficiency-cycle-disabled-color, var(--dnd5e-color-gold)); }
Expand Down Expand Up @@ -198,8 +212,9 @@ export default class ProficiencyCycleElement extends HTMLElement {
opacity: 0;
}
`);
this.constructor._stylesheets.set(_window.document, cssObj);
}
this.#shadowRoot.adoptedStyleSheets = [this.constructor._stylesheet];
this.#shadowRoot.adoptedStyleSheets = [this.constructor._stylesheets.get(_window.document)];
}

/* -------------------------------------------- */
Expand All @@ -209,7 +224,7 @@ export default class ProficiencyCycleElement extends HTMLElement {
*/
#buildHTML() {
const div = document.createElement("div");
this.#shadowRoot.appendChild(div);
this.#shadowRoot.replaceChildren(div);

const input = document.createElement("input");
input.setAttribute("type", "number");
Expand Down

0 comments on commit f9a038b

Please sign in to comment.