Skip to content

Commit c117b68

Browse files
committed
First stab at #103
1 parent c26cc5a commit c117b68

File tree

1 file changed

+79
-5
lines changed

1 file changed

+79
-5
lines changed

src/element-style-observer.js

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ export default class ElementStyleObserver {
213213
*/
214214
updateTransitionProperties () {
215215
// Clear our own transition
216-
this.target.style.setProperty("--style-observer-transition", "");
216+
this.setProperty("--style-observer-transition", "");
217217

218218
let transitionProperties = new Set(
219219
getComputedStyle(this.target).transitionProperty.split(", "),
@@ -232,7 +232,7 @@ export default class ElementStyleObserver {
232232
.map(property => `${property} 1ms step-start${allowDiscrete}`)
233233
.join(", ");
234234

235-
this.target.style.setProperty("--style-observer-transition", transition);
235+
this.setProperty("--style-observer-transition", transition);
236236
}
237237

238238
/**
@@ -247,7 +247,7 @@ export default class ElementStyleObserver {
247247
*/
248248
updateTransition ({ firstTime } = {}) {
249249
const sot = "var(--style-observer-transition, --style-observer-noop)";
250-
const inlineTransition = this.target.style.transition;
250+
const inlineTransition = this.getProperty("transition");
251251
let transition;
252252

253253
// NOTE This code assumes that if there is an inline style, it takes precedence over other styles
@@ -260,7 +260,7 @@ export default class ElementStyleObserver {
260260
if (transition === undefined && (firstTime || !this.#inlineTransition)) {
261261
// Just update based on most current computed style
262262
if (inlineTransition.includes(sot)) {
263-
this.target.style.transition = "";
263+
this.setProperty("transition", "");
264264
}
265265

266266
transition = getComputedStyle(this.target).transition;
@@ -274,11 +274,85 @@ export default class ElementStyleObserver {
274274
// transition: all, var(--style-observer-transition, all);
275275
// so we can't just concatenate with whatever the existing value is
276276
const prefix = transition ? transition + ", " : "";
277-
this.target.style.setProperty("transition", prefix + sot);
277+
this.setProperty("transition", prefix + sot);
278278

279279
this.updateTransitionProperties();
280280
}
281281

282+
/**
283+
* Whether the target has an open shadow root (and the modern adoptedStyleSheets API is supported).
284+
* @type { boolean }
285+
* @private
286+
*/
287+
get _isHost () {
288+
return (
289+
this.target.shadowRoot && !Object.isFrozen(this.target.shadowRoot.adoptedStyleSheets)
290+
);
291+
}
292+
293+
/**
294+
* Shadow style sheet. Only used if _isHost is true.
295+
* @type { CSSStyleSheet | undefined }
296+
* @private
297+
*/
298+
_shadowSheet;
299+
300+
/**
301+
* Any styles we've set on the target, for any reason.
302+
* @type { Record<string, string> }
303+
* @private
304+
*/
305+
_styles = {};
306+
307+
/**
308+
* Set a CSS property on the target.
309+
* @param {string} property
310+
* @param {string} value
311+
* @return {void}
312+
*/
313+
setProperty (property, value) {
314+
let inlineStyle = this.target.style;
315+
let style = inlineStyle;
316+
if (this._isHost) {
317+
// This has an open shadow root.
318+
// We can use an adopted shadow style to avoid manipulating its style attribute
319+
if (!this._shadowSheet) {
320+
this._shadowSheet = new CSSStyleSheet();
321+
this._shadowSheet.insertRule(`:host { }`);
322+
this.target.shadowRoot.adoptedStyleSheets.push(this._shadowSheet);
323+
324+
if (Object.keys(this._styles).length > 0) {
325+
// It was previously not a host, so we need to port the properties over
326+
for (let property in this._styles) {
327+
let value = this._styles[property];
328+
this.setProperty(property, value);
329+
330+
// Remove from inline style if it hasn't changed externally
331+
if (inlineStyle.getPropertyValue(property) === value) {
332+
inlineStyle.removeProperty(property);
333+
}
334+
}
335+
}
336+
}
337+
338+
style = this._shadowSheet.cssRules[0].style;
339+
}
340+
341+
style.setProperty(property, value);
342+
// Store reserialized value for later comparison
343+
this._styles[property] = this.getProperty(property);
344+
}
345+
346+
/**
347+
* Get a CSS property from the target.
348+
* @param {string} property
349+
* @return {string}
350+
*/
351+
getProperty (property) {
352+
let style = this._shadowSheet?.cssRules[0]?.style ?? this.target.style;
353+
return style.getPropertyValue(property);
354+
}
355+
282356
/**
283357
* Stop observing a target for changes to one or more CSS properties.
284358
* @param { string | string[] } [properties] Properties to stop observing. Defaults to all observed properties.

0 commit comments

Comments
 (0)