Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Popout! compatibilty (Fixes #3044) #3048

Merged
merged 3 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions module/applications/components/_module.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import InventoryElement from "./inventory.mjs";
import ItemListControlsElement from "./item-list-controls.mjs";
import ProficiencyCycleElement from "./proficiency-cycle.mjs";
import SlideToggleElement from "./slide-toggle.mjs";
import AdoptedStyleSheetMixin from "./adopted-stylesheet-mixin.mjs";

window.customElements.define("dnd5e-effects", EffectsElement);
window.customElements.define("dnd5e-icon", IconElement);
Expand All @@ -15,6 +16,6 @@ window.customElements.define("proficiency-cycle", ProficiencyCycleElement);
window.customElements.define("slide-toggle", SlideToggleElement);

export {
EffectsElement, IconElement, InventoryElement, ItemListControlsElement, FiligreeBoxElement, ProficiencyCycleElement,
SlideToggleElement
AdoptedStyleSheetMixin, EffectsElement, IconElement, InventoryElement, ItemListControlsElement, FiligreeBoxElement,
ProficiencyCycleElement, SlideToggleElement
};
54 changes: 54 additions & 0 deletions module/applications/components/adopted-stylesheet-mixin.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Adds functionality to a custom HTML element for caching its stylesheet and adopting it into its Shadow DOM, rather
* than having each stylesheet duplicated per element.
* @param {typeof HTMLElement} Base The base class being mixed.
* @returns {typeof AdoptedStyleSheetElement}
*/
export default function AdoptedStyleSheetMixin(Base) {
return class AdoptedStyleSheetElement extends Base {
/**
* A map of cached stylesheets per Document root.
* @type {WeakMap<WeakKey<Document>, CSSStyleSheet>}
* @protected
*/
static _stylesheets = new WeakMap();

/**
* The CSS content for this element.
* @type {string}
*/
static CSS = "";

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

/** @inheritDoc */
adoptedCallback() {
this._adoptStyleSheet(this._getStyleSheet());
}

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

/**
* Retrieves the cached stylesheet, or generates a new one.
* @protected
*/
_getStyleSheet() {
let sheet = this.constructor._stylesheets.get(this.ownerDocument);
if ( !sheet ) {
sheet = new this.ownerDocument.defaultView.CSSStyleSheet();
sheet.replaceSync(this.constructor.CSS);
this.constructor._stylesheets.set(this.ownerDocument, sheet);
}
return sheet;
}

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

/**
* Adopt the stylesheet into the Shadow DOM.
* @param {CSSStyleSheet} sheet The sheet to adopt.
* @abstract
*/
_adoptStyleSheet(sheet) {}
}
}
138 changes: 64 additions & 74 deletions module/applications/components/filigree-box.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import AdoptedStyleSheetMixin from "./adopted-stylesheet-mixin.mjs";

/**
* Custom element that adds a filigree border that can be colored.
*/
export default class FiligreeBoxElement extends HTMLElement {
export default class FiligreeBoxElement extends AdoptedStyleSheetMixin(HTMLElement) {
constructor() {
super();
this.#shadowRoot = this.attachShadow({ mode: "closed" });
this.#buildStyle();
this._adoptStyleSheet(this._getStyleSheet());
const backdrop = document.createElement("div");
backdrop.classList.add("backdrop");
this.#shadowRoot.appendChild(backdrop);
Expand All @@ -21,82 +23,55 @@ export default class FiligreeBoxElement extends HTMLElement {
this.#shadowRoot.appendChild(slot);
}

/**
* Shadow root that contains the box shapes.
* @type {ShadowRoot}
*/
#shadowRoot;

/* -------------------------------------------- */
/** @inheritDoc */
static CSS = `
:host {
position: relative;
isolation: isolate;
min-height: 56px;
filter: var(--filigree-drop-shadow, drop-shadow(0 0 12px var(--dnd5e-shadow-15)));
}
.backdrop {
--chamfer: 12px;
position: absolute;
inset: 0;
background: var(--filigree-background-color, var(--dnd5e-color-card));
z-index: -2;
clip-path: polygon(
var(--chamfer) 0,
calc(100% - var(--chamfer)) 0,
100% var(--chamfer),
100% calc(100% - var(--chamfer)),
calc(100% - var(--chamfer)) 100%,
var(--chamfer) 100%,
0 calc(100% - var(--chamfer)),
0 var(--chamfer)
);
}
.filigree {
position: absolute;
fill: var(--filigree-border-color, var(--dnd5e-color-gold));
z-index: -1;

/**
* The stylesheet to attach to the element's shadow root.
* @type {CSSStyleSheet}
* @protected
*/
static _stylesheet;
&.top, &.bottom { height: 30px; }
&.top { top: 0; }
&.bottom { bottom: 0; scale: 1 -1; }

/* -------------------------------------------- */
&.left, &.right { width: 25px; }
&.left { left: 0; }
&.right { right: 0; scale: -1 1; }

/**
* Build the shadow DOM's styles.
*/
#buildStyle() {
if ( !this.constructor._stylesheet ) {
this.constructor._stylesheet = new CSSStyleSheet();
this.constructor._stylesheet.replaceSync(`
:host {
position: relative;
isolation: isolate;
min-height: 56px;
filter: var(--filigree-drop-shadow, drop-shadow(0 0 12px var(--dnd5e-shadow-15)));
}
.backdrop {
--chamfer: 12px;
position: absolute;
inset: 0;
background: var(--filigree-background-color, var(--dnd5e-color-card));
z-index: -2;
clip-path: polygon(
var(--chamfer) 0,
calc(100% - var(--chamfer)) 0,
100% var(--chamfer),
100% calc(100% - var(--chamfer)),
calc(100% - var(--chamfer)) 100%,
var(--chamfer) 100%,
0 calc(100% - var(--chamfer)),
0 var(--chamfer)
);
}
.filigree {
position: absolute;
fill: var(--filigree-border-color, var(--dnd5e-color-gold));
z-index: -1;

&.top, &.bottom { height: 30px; }
&.top { top: 0; }
&.bottom { bottom: 0; scale: 1 -1; }

&.left, &.right { width: 25px; }
&.left { left: 0; }
&.right { right: 0; scale: -1 1; }

&.bottom.right { scale: -1 -1; }
}
.filigree.block {
inline-size: calc(100% - 50px);
inset-inline: 25px;
}
.filigree.inline {
block-size: calc(100% - 60px);
inset-block: 30px;
}
`);
&.bottom.right { scale: -1 -1; }
}
this.#shadowRoot.adoptedStyleSheets = [this.constructor._stylesheet];
}

/* -------------------------------------------- */
.filigree.block {
inline-size: calc(100% - 50px);
inset-inline: 25px;
}
.filigree.inline {
block-size: calc(100% - 60px);
inset-block: 30px;
}
`;

/**
* Path definitions for the various box corners and edges.
Expand All @@ -108,6 +83,21 @@ export default class FiligreeBoxElement extends HTMLElement {
inline: "M 0 10 L 0 0 L 2.99 0 L 2.989 10 L 0 10 Z M 6.9 10 L 6.9 0 L 8.6 0 L 8.6 10 L 6.9 10 Z"
});

/**
* Shadow root that contains the box shapes.
* @type {ShadowRoot}
*/
#shadowRoot;

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

/** @inheritDoc */
_adoptStyleSheet(sheet) {
this.#shadowRoot.adoptedStyleSheets = [sheet];
}

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

/**
* Build an SVG element.
* @param {string} path SVG path to use.
Expand Down
56 changes: 26 additions & 30 deletions module/applications/components/icon.mjs
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
import AdoptedStyleSheetMixin from "./adopted-stylesheet-mixin.mjs";

/**
* Custom element for displaying SVG icons that are cached and can be styled.
*/
export default class IconElement extends HTMLElement {
export default class IconElement extends AdoptedStyleSheetMixin(HTMLElement) {
constructor() {
super();
this.#internals = this.attachInternals();
this.#internals.role = "img";
this.#shadowRoot = this.attachShadow({ mode: "closed" });
}

/** @inheritDoc */
static CSS = `
:host {
display: contents;
}
svg {
fill: var(--icon-fill, #000);
width: var(--icon-width, var(--icon-size, 1em));
height: var(--icon-height, var(--icon-size, 1em));
}
`;

/**
* Cached SVG files by SRC.
* @type {Map<string, SVGElement|Promise<SVGElement>>}
*/
static #svgCache = new Map();

/**
* The custom element's form and accessibility internals.
* @type {ElementInternals}
Expand Down Expand Up @@ -37,40 +57,16 @@ export default class IconElement extends HTMLElement {

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

/**
* Stylesheet that is shared among all icons.
* @type {CSSStyleSheet}
*/
static #stylesheet;

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

/**
* Cached SVG files by SRC.
* @type {Map<string, SVGElement|Promise<SVGElement>>}
*/
static #svgCache = new Map();
/** @inheritDoc */
_adoptStyleSheet(sheet) {
this.#shadowRoot.adoptedStyleSheets = [sheet];
}

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

/** @inheritDoc */
connectedCallback() {
// Create icon styles
if ( !this.constructor.#stylesheet ) {
this.constructor.#stylesheet = new CSSStyleSheet();
this.constructor.#stylesheet.replaceSync(`
:host {
display: contents;
}
svg {
fill: var(--icon-fill, #000);
width: var(--icon-width, var(--icon-size, 1em));
height: var(--icon-height, var(--icon-size, 1em));
}
`);
}
this.#shadowRoot.adoptedStyleSheets = [this.constructor.#stylesheet];

this._adoptStyleSheet(this._getStyleSheet());
const insertElement = element => {
if ( !element ) return;
const clone = element.cloneNode(true);
Expand Down
Loading