Skip to content

Commit 68f2aa8

Browse files
committed
fix: simplify adopted stylesheets controller
1 parent b6bf0f1 commit 68f2aa8

File tree

1 file changed

+56
-65
lines changed

1 file changed

+56
-65
lines changed
Lines changed: 56 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,82 @@
11
import { ReactiveController, ReactiveControllerHost } from 'lit';
22

33
/**
4-
* `AdoptedStylesheets` is a class that implements the `ReactiveController` interface from the `lit` library.
5-
* This class is used to manage CSS stylesheets that are adopted into the document or a shadow root.
6-
*
7-
* @property {CSSStyleSheet} adoptedSheet - The CSSStyleSheet object that is adopted into the document or a shadow root.
8-
* @property {Document | ShadowRoot} root - The root where the stylesheet will be adopted.
4+
* A controller for managing adopted stylesheets in a Lit element.
5+
* This allows for styles to be dynamically applied to the component's
6+
* light DOM or shadow DOM.
97
*/
10-
export class AdoptedStylesheets implements ReactiveController {
11-
/**
12-
* A static map that stores CSSStyleSheet objects by their CSS text.
13-
* This allows for reuse of CSSStyleSheet objects across multiple instances of the class.
14-
* @type {Map<string, CSSStyleSheet>}
15-
*/
16-
private static styleSheetMap = new Map<string, CSSStyleSheet>();
17-
18-
/**
19-
* The CSSStyleSheet object that is adopted into the document or a shadow root.
20-
* @type {CSSStyleSheet}
21-
*/
22-
private adoptedSheet: CSSStyleSheet;
8+
export class AdoptedStyleSheets implements ReactiveController {
9+
// The host element that the controller is associated with.
10+
private host: ReactiveControllerHost & HTMLElement;
11+
// An object containing the CSS to be applied globally or encapsulated within the shadow DOM.
12+
private css: { globalCSS?: string; encapsulatedCSS?: string };
2313

24-
/**
25-
* The root where the stylesheet will be adopted.
26-
* This can be either the document or a shadow root.
27-
* @type {Document | ShadowRoot}
28-
*/
29-
private root: Document | ShadowRoot;
30-
31-
/**
32-
* The host that this controller is associated with.
33-
* @type {ReactiveControllerHost}
34-
*/
35-
private host: ReactiveControllerHost;
14+
// A set to track the stylesheets applied to the light DOM.
15+
private static appliedLightDomStylesheets: Set<string> = new Set();
3616

3717
/**
38-
* The constructor for the `AdoptedStylesheets` class.
39-
*
40-
* @param {ReactiveControllerHost} host - The host that this controller is associated with.
41-
* @param {string} cssText - A string that contains the CSS styles to be adopted.
42-
* @param {Document | ShadowRoot} root - The root where the stylesheet will be adopted.
18+
* Constructs an instance of the AdoptedStyleSheets controller.
19+
* @param host The host element that the controller will be associated with.
20+
* @param css An object containing optional global and encapsulated CSS strings.
4321
*/
4422
constructor(
45-
host: ReactiveControllerHost,
46-
cssText: string,
47-
root: Document | ShadowRoot = document
23+
host: ReactiveControllerHost & HTMLElement,
24+
css: {
25+
globalCSS?: string;
26+
encapsulatedCSS?: string;
27+
} = {}
4828
) {
4929
this.host = host;
50-
this.host.addController(this);
51-
this.root = root;
30+
this.css = css;
31+
this.host.addController(this); // Register this instance as a controller for the host element.
32+
}
5233

53-
if (!AdoptedStylesheets.styleSheetMap.has(cssText)) {
54-
const newSheet = new CSSStyleSheet();
55-
newSheet.replace(cssText).catch(error => {
56-
console.error('Failed to replace CSS text:', error);
57-
});
58-
AdoptedStylesheets.styleSheetMap.set(cssText, newSheet);
34+
/**
35+
* Applies the given CSS text to the specified target (Document or ShadowRoot).
36+
* @param cssText The CSS text to apply.
37+
* @param target The target where the CSS should be applied.
38+
*/
39+
private applyCssToDom(cssText: string, target: Document | ShadowRoot) {
40+
if (target instanceof Document) {
41+
const store = AdoptedStyleSheets.appliedLightDomStylesheets;
42+
43+
if (store.has(cssText)) {
44+
// If the stylesheet has already been applied, no further action is required.
45+
return;
46+
}
47+
store.add(cssText); // Store the stylesheet with the provided key.
5948
}
60-
this.adoptedSheet =
61-
AdoptedStylesheets.styleSheetMap.get(cssText) || new CSSStyleSheet();
49+
50+
// Create a new stylesheet and replace its contents with the provided CSS text.
51+
const sheet = new CSSStyleSheet();
52+
sheet.replaceSync(cssText);
53+
54+
// Apply the stylesheet to the target's adoptedStyleSheets.
55+
target.adoptedStyleSheets = [...target.adoptedStyleSheets, sheet];
6256
}
6357

6458
/**
65-
* The `hostConnected` method is called when the host element is connected to the DOM.
66-
* This method adopts the CSSStyleSheet object into the root's adopted stylesheets if it's not already included.
59+
* Lifecycle callback called when the host element is connected to the document's DOM.
60+
* Applies global and encapsulated CSS to the respective DOM targets.
6761
*/
6862
hostConnected() {
69-
if (
70-
this.root &&
71-
!this.root.adoptedStyleSheets.includes(this.adoptedSheet)
72-
) {
73-
this.root.adoptedStyleSheets = [
74-
...this.root.adoptedStyleSheets,
75-
this.adoptedSheet,
76-
];
63+
if (this.css.globalCSS) {
64+
this.applyCssToDom(this.css.globalCSS, document); // Apply global CSS to the document if it exists.
65+
}
66+
67+
// Apply encapsulated CSS to the host's shadow root if it exists.
68+
if (this.css.encapsulatedCSS && this.host.shadowRoot) {
69+
this.applyCssToDom(this.css.encapsulatedCSS, this.host.shadowRoot); // Apply encapsulated CSS to the host's shadow root if it exists.
7770
}
7871
}
7972

8073
/**
81-
* The `hostDisconnected` method is called when the host element is disconnected from the DOM.
82-
* This method removes the CSSStyleSheet object from the root's adopted stylesheets if it's included.
74+
* Lifecycle callback called when the host element is disconnected from the document's DOM.
75+
* Note: When a component with a Shadow DOM is disconnected from the document's DOM, the Shadow DOM is also removed along with the component.
76+
* However, for Light DOM styles, they are not removed here because other instances of the component
77+
* might still be present on the page and require these styles.
8378
*/
8479
hostDisconnected() {
85-
if (this.root && this.root.adoptedStyleSheets.includes(this.adoptedSheet)) {
86-
this.root.adoptedStyleSheets = this.root.adoptedStyleSheets.filter(
87-
sheet => sheet !== this.adoptedSheet
88-
);
89-
}
80+
// No action is taken when the host is disconnected.
9081
}
9182
}

0 commit comments

Comments
 (0)