Skip to content

Commit

Permalink
chore: maintain style sheet sort order for all shadow roots (microsof…
Browse files Browse the repository at this point in the history
  • Loading branch information
spmonahan committed Feb 26, 2024
1 parent 13d37b6 commit 5556da7
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 39 deletions.
22 changes: 14 additions & 8 deletions packages/merge-styles/etc/merge-styles.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ export type DeepPartial<T> = {
// @public (undocumented)
export type EventHandler<T> = (args: EventArgs<T>) => void;

// @public (undocumented)
export type ExtendedCSSStyleSheet = CSSStyleSheet & {
bucketName: string;
metadata: Record<string, unknown>;
};

// @public
export function fontFace(font: IFontFace): void;

Expand Down Expand Up @@ -557,14 +563,14 @@ export type ShadowConfig = {
export class Stylesheet {
constructor(config?: IStyleSheetConfig, serializedStylesheet?: ISerializedStylesheet);
// (undocumented)
addAdoptableStyleSheet(key: string, sheet: CSSStyleSheet): void;
addAdoptableStyleSheet(key: string, sheet: ExtendedCSSStyleSheet): void;
argsFromClassName(className: string): IStyle[] | undefined;
cacheClassName(className: string, key: string, args: IStyle[], rules: string[]): void;
classNameFromKey(key: string): string | undefined;
// (undocumented)
forEachAdoptedStyleSheet(callback: (value: CSSStyleSheet, key: string, map: Map<string, CSSStyleSheet>) => void): void;
forEachAdoptedStyleSheet(callback: (value: ExtendedCSSStyleSheet, key: string, map: Map<string, ExtendedCSSStyleSheet>) => void): void;
// (undocumented)
getAdoptableStyleSheet(key: string): CSSStyleSheet;
getAdoptableStyleSheet(key: string): ExtendedCSSStyleSheet;
getClassName(displayName?: string): string;
getClassNameCache(): {
[key: string]: string;
Expand All @@ -574,16 +580,16 @@ export class Stylesheet {
insertedRulesFromClassName(className: string): string[] | undefined;
insertRule(rule: string, preserve?: boolean): void;
// (undocumented)
makeCSSStyleSheet(win: Window): CSSStyleSheet;
makeCSSStyleSheet(win: Window): ExtendedCSSStyleSheet;
// (undocumented)
offAddConstructableStyleSheet(callback: EventHandler<CSSStyleSheet>): void;
offAddConstructableStyleSheet(callback: EventHandler<ExtendedCSSStyleSheet>): void;
// (undocumented)
offInsertRuleIntoConstructableStyleSheet(callback: EventHandler<CSSStyleSheet>): void;
offInsertRuleIntoConstructableStyleSheet(callback: EventHandler<ExtendedCSSStyleSheet>): void;
// (undocumented)
onAddConstructableStyleSheet(callback: EventHandler<CSSStyleSheet>): void;
onAddConstructableStyleSheet(callback: EventHandler<ExtendedCSSStyleSheet>): void;
onInsertRule(callback: Function): Function;
// (undocumented)
onInsertRuleIntoConstructableStyleSheet(callback: EventHandler<CSSStyleSheet>): void;
onInsertRuleIntoConstructableStyleSheet(callback: EventHandler<ExtendedCSSStyleSheet>): void;
onReset(callback: Function): Function;
// (undocumented)
projectStylesToWindow(targetWindow: Window): void;
Expand Down
47 changes: 29 additions & 18 deletions packages/merge-styles/src/Stylesheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { IStyle } from './IStyle';
import { DEFAULT_SHADOW_CONFIG, GLOBAL_STYLESHEET_KEY, ShadowConfig } from './shadowConfig';
import { EventHandler, EventMap } from './EventMap';
import { cloneCSSStyleSheet } from './cloneCSSStyleSheet';
import { cloneExtendedCSSStyleSheet } from './cloneCSSStyleSheet';

export const InjectionMode = {
/**
Expand Down Expand Up @@ -128,9 +128,9 @@ if (SUPPORTS_CONSTRUCTABLE_STYLESHEETS) {
}
}

export type AdoptableStylesheet = {
fluentSheet: Stylesheet;
adoptedSheet: CSSStyleSheet;
export type ExtendedCSSStyleSheet = CSSStyleSheet & {
bucketName: string;
metadata: Record<string, unknown>;
};

let _global: (Window | {}) & {
Expand Down Expand Up @@ -184,7 +184,8 @@ export class Stylesheet {
private _onInsertRuleCallbacks: Function[] = [];
private _onResetCallbacks: Function[] = [];
private _classNameToArgs: { [key: string]: { args: any; rules: string[] } } = {};
private _adoptableSheets?: EventMap<string, CSSStyleSheet>;
private _adoptableSheets?: EventMap<string, ExtendedCSSStyleSheet>;
private _sheetCounter = 0;

/**
* Gets the singleton instance.
Expand Down Expand Up @@ -226,7 +227,7 @@ export class Stylesheet {
if (inShadow || stylesheetKey === GLOBAL_STYLESHEET_KEY) {
const sheetWindow = win ?? getWindow();
if (sheetWindow) {
_stylesheet.addAdoptableStyleSheet(stylesheetKey, _stylesheet.makeCSSStyleSheet(sheetWindow));
_stylesheet.addAdoptableStyleSheet(stylesheetKey, _stylesheet.getAdoptableStyleSheet(stylesheetKey));
}
}

Expand Down Expand Up @@ -260,7 +261,7 @@ export class Stylesheet {
this._rules = serializedStylesheet?.rules ?? this._rules;
}

public addAdoptableStyleSheet(key: string, sheet: CSSStyleSheet): void {
public addAdoptableStyleSheet(key: string, sheet: ExtendedCSSStyleSheet): void {
if (!this._adoptableSheets) {
this._adoptableSheets = new EventMap();
// window.__DEBUG_SHEETS__ = this._adoptableSheets;
Expand All @@ -274,7 +275,7 @@ export class Stylesheet {
}
}

public getAdoptableStyleSheet(key: string): CSSStyleSheet {
public getAdoptableStyleSheet(key: string): ExtendedCSSStyleSheet {
if (!this._adoptableSheets) {
this._adoptableSheets = new EventMap();
// window.__DEBUG_SHEETS__ = this._adoptableSheets;
Expand All @@ -290,12 +291,12 @@ export class Stylesheet {
}

public forEachAdoptedStyleSheet(
callback: (value: CSSStyleSheet, key: string, map: Map<string, CSSStyleSheet>) => void,
callback: (value: ExtendedCSSStyleSheet, key: string, map: Map<string, ExtendedCSSStyleSheet>) => void,
): void {
this._adoptableSheets?.forEach(callback);
}

public onAddConstructableStyleSheet(callback: EventHandler<CSSStyleSheet>): void {
public onAddConstructableStyleSheet(callback: EventHandler<ExtendedCSSStyleSheet>): void {
if (!this._adoptableSheets) {
this._adoptableSheets = new EventMap();
// window.__DEBUG_SHEETS__ = this._adoptableSheets;
Expand All @@ -304,7 +305,7 @@ export class Stylesheet {
this._adoptableSheets.on('add-sheet', callback);
}

public offAddConstructableStyleSheet(callback: EventHandler<CSSStyleSheet>): void {
public offAddConstructableStyleSheet(callback: EventHandler<ExtendedCSSStyleSheet>): void {
if (!this._adoptableSheets) {
this._adoptableSheets = new EventMap();
// window.__DEBUG_SHEETS__ = this._adoptableSheets;
Expand All @@ -313,7 +314,7 @@ export class Stylesheet {
this._adoptableSheets.off('add-sheet', callback);
}

public onInsertRuleIntoConstructableStyleSheet(callback: EventHandler<CSSStyleSheet>): void {
public onInsertRuleIntoConstructableStyleSheet(callback: EventHandler<ExtendedCSSStyleSheet>): void {
if (!this._adoptableSheets) {
this._adoptableSheets = new EventMap();
// window.__DEBUG_SHEETS__ = this._adoptableSheets;
Expand All @@ -322,7 +323,7 @@ export class Stylesheet {
this._adoptableSheets.on('insert-rule', callback);
}

public offInsertRuleIntoConstructableStyleSheet(callback: EventHandler<CSSStyleSheet>): void {
public offInsertRuleIntoConstructableStyleSheet(callback: EventHandler<ExtendedCSSStyleSheet>): void {
if (!this._adoptableSheets) {
this._adoptableSheets = new EventMap();
// window.__DEBUG_SHEETS__ = this._adoptableSheets;
Expand All @@ -346,7 +347,7 @@ export class Stylesheet {
(targetWindow as typeof _global)[STYLESHEET_SETTING] = targetStylesheet;

this.forEachAdoptedStyleSheet((srcSheet, key) => {
const clonedSheet = cloneCSSStyleSheet(srcSheet, this.makeCSSStyleSheet(targetWindow));
const clonedSheet = cloneExtendedCSSStyleSheet(srcSheet, this.makeCSSStyleSheet(targetWindow));
targetStylesheet.addAdoptableStyleSheet(key, clonedSheet);
});

Expand Down Expand Up @@ -546,7 +547,7 @@ export class Stylesheet {
if (inserted && sheet) {
this._adoptableSheets?.raise('insert-rule', {
key: stylesheetKey,
sheet: sheet as CSSStyleSheet,
sheet: sheet as ExtendedCSSStyleSheet,
rule,
});
}
Expand Down Expand Up @@ -589,13 +590,23 @@ export class Stylesheet {
this._keyToClassName = {};
}

public makeCSSStyleSheet(win: Window): CSSStyleSheet {
public makeCSSStyleSheet(win: Window): ExtendedCSSStyleSheet {
let sheet: ExtendedCSSStyleSheet | undefined = undefined;
if (!SUPPORTS_CONSTRUCTABLE_STYLESHEETS) {
const style = this._createStyleElement(win);
return style.sheet as CSSStyleSheet;
sheet = style.sheet as ExtendedCSSStyleSheet;
} else {
sheet = new (win as Window & typeof globalThis).CSSStyleSheet() as ExtendedCSSStyleSheet;
}

if (sheet) {
sheet.bucketName = 'merge-styles';
sheet.metadata = {
sortOrder: this._sheetCounter++,
};
}

return new (win as Window & typeof globalThis).CSSStyleSheet();
return sheet;
}

private _insertNode(element: HTMLStyleElement | undefined, rule: string): boolean {
Expand Down
16 changes: 16 additions & 0 deletions packages/merge-styles/src/cloneCSSStyleSheet.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import type { ExtendedCSSStyleSheet } from './Stylesheet';

export const cloneCSSStyleSheet = (srcSheet: CSSStyleSheet, targetSheet: CSSStyleSheet): CSSStyleSheet => {
for (let i = 0; i < srcSheet.cssRules.length; i++) {
targetSheet.insertRule(srcSheet.cssRules[i].cssText);
}

return targetSheet;
};

export const cloneExtendedCSSStyleSheet = (
srcSheet: ExtendedCSSStyleSheet,
targetSheet: ExtendedCSSStyleSheet,
): ExtendedCSSStyleSheet => {
const clone = cloneCSSStyleSheet(srcSheet, targetSheet) as ExtendedCSSStyleSheet;

clone.bucketName = srcSheet.bucketName;
for (const key of Object.keys(srcSheet.metadata)) {
clone.metadata[key] = srcSheet.metadata[key];
}

return clone;
};
2 changes: 1 addition & 1 deletion packages/merge-styles/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export { fontFace } from './fontFace';
export { keyframes } from './keyframes';

export { InjectionMode, Stylesheet } from './Stylesheet';
export type { ICSPSettings, ISerializedStylesheet, IStyleSheetConfig } from './Stylesheet';
export type { ICSPSettings, ISerializedStylesheet, IStyleSheetConfig, ExtendedCSSStyleSheet } from './Stylesheet';

export { setRTL } from './StyleOptionsState';

Expand Down
3 changes: 2 additions & 1 deletion packages/utilities/etc/utilities.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { DATA_PORTAL_ATTRIBUTE } from '@fluentui/dom-utilities';
import { elementContains } from '@fluentui/dom-utilities';
import { elementContainsAttribute } from '@fluentui/dom-utilities';
import type { ExtendedCSSStyleSheet } from '@fluentui/merge-styles';
import { findElementRecursive } from '@fluentui/dom-utilities';
import { getChildren } from '@fluentui/dom-utilities';
import { getParent } from '@fluentui/dom-utilities';
Expand Down Expand Up @@ -1291,7 +1292,7 @@ export const useHasMergeStylesShadowRootContext: () => boolean;
export const useIsomorphicLayoutEffect: typeof React_2.useEffect;

// @public
export const useMergeStylesRootStylesheets: () => Map<string, CSSStyleSheet>;
export const useMergeStylesRootStylesheets: () => Map<string, ExtendedCSSStyleSheet>;

// Warning: (ae-forgotten-export) The symbol "MergeStylesShadowRootContextValue" needs to be exported by the entry point index.d.ts
//
Expand Down
13 changes: 8 additions & 5 deletions packages/utilities/src/shadowDom/MergeStylesRootContext.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { GLOBAL_STYLESHEET_KEY, Stylesheet, makeShadowConfig } from '@fluentui/merge-styles';
import { getWindow } from '../dom';
import type { ExtendedCSSStyleSheet } from '@fluentui/merge-styles';

declare global {
// eslint-disable-next-line @typescript-eslint/naming-convention
Expand All @@ -14,7 +15,7 @@ export type MergeStylesRootContextValue = {
/**
* Map of stylesheets available in the context.
*/
stylesheets: Map<string, CSSStyleSheet>;
stylesheets: Map<string, ExtendedCSSStyleSheet>;
};

const MergeStylesRootContext = React.createContext<MergeStylesRootContextValue>({
Expand All @@ -25,7 +26,7 @@ export type MergeStylesRootProviderProps = {
/**
* Map of stylesheets available in the context.
*/
stylesheets?: Map<string, CSSStyleSheet>;
stylesheets?: Map<string, ExtendedCSSStyleSheet>;

/**
* Optional `window` object to use for reading adopted stylesheets.
Expand All @@ -44,11 +45,13 @@ export const MergeStylesRootProvider: React.FC<MergeStylesRootProviderProps> = (
...props
}) => {
const win = userWindow ?? getWindow();
const [stylesheets, setStylesheets] = React.useState<Map<string, CSSStyleSheet>>(() => userSheets ?? new Map());
const [stylesheets, setStylesheets] = React.useState<Map<string, ExtendedCSSStyleSheet>>(
() => userSheets ?? new Map(),
);

const sheetHandler = React.useCallback(({ key, sheet }) => {
setStylesheets(prev => {
const next = new Map<string, CSSStyleSheet>(prev);
const next = new Map<string, ExtendedCSSStyleSheet>(prev);
next.set(key, sheet);
return next;
});
Expand Down Expand Up @@ -81,7 +84,7 @@ export const MergeStylesRootProvider: React.FC<MergeStylesRootProviderProps> = (
}

let changed = false;
const next = new Map<string, CSSStyleSheet>(stylesheets);
const next = new Map<string, ExtendedCSSStyleSheet>(stylesheets);
const sheet = Stylesheet.getInstance(makeShadowConfig(GLOBAL_STYLESHEET_KEY, false, win));

sheet.forEachAdoptedStyleSheet((adoptedSheet, key) => {
Expand Down
32 changes: 26 additions & 6 deletions packages/utilities/src/shadowDom/MergeStylesShadowRootContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@fluentui/merge-styles';
import { useMergeStylesRootStylesheets } from './MergeStylesRootContext';
import { useDocument, useWindow } from '@fluentui/react-window-provider';
import type { ExtendedCSSStyleSheet } from '@fluentui/merge-styles';

type PolyfileInsertListeners = Record<string, EventHandler<CSSStyleSheet>>;

Expand Down Expand Up @@ -126,21 +127,40 @@ const adoptSheet = (
shadowCtx: MergeStylesShadowRootContextValue,
doc: Document,
stylesheetKey: string,
stylesheet: CSSStyleSheet,
stylesheet: ExtendedCSSStyleSheet,
listenerRef: PolyfileInsertListeners,
) => {
const shadowRoot = shadowCtx.shadowRoot!;

shadowCtx.stylesheets.set(stylesheetKey, stylesheet);
const sheet = Stylesheet.getInstance();
if (sheet.supportsConstructableStylesheets()) {
// The current spec allows the `adoptedStyleSheets` array to be modified.
// Previous versions of the spec required a new array to be created.
// For more details see: https://github.com/microsoft/fast/pull/6703
// Maintain the sort order of Fluent style sheets
const prevSheets = shadowRoot.adoptedStyleSheets;
let i = prevSheets.length;
let found = i === 0;
while (i >= 0 && !found) {
i--;

const prevSheet = prevSheets[i] as ExtendedCSSStyleSheet;
const prevSortOrder = (prevSheet.metadata?.sortOrder as number) ?? 0;
const sheetSortOrder = (stylesheet.metadata?.sortOrder as number) ?? 0;
if (prevSheet.bucketName === 'merge-styles' && prevSortOrder < sheetSortOrder) {
found = true;
}
}

if (sheet.supportsModifyingAdoptedStyleSheets()) {
shadowRoot.adoptedStyleSheets.push(stylesheet);
// The current spec allows the `adoptedStyleSheets` array to be modified.
// Previous versions of the spec required a new array to be created.
// For more details see: https://github.com/microsoft/fast/pull/6703
shadowRoot.adoptedStyleSheets.splice(i + 1, 0, stylesheet);
} else {
shadowRoot.adoptedStyleSheets = [...shadowRoot.adoptedStyleSheets, stylesheet];
shadowRoot.adoptedStyleSheets = [
...shadowRoot.adoptedStyleSheets.slice(0, i + 1),
stylesheet,
...shadowRoot.adoptedStyleSheets.slice(i + 1),
];
}
} else {
const style = doc.createElement('style');
Expand Down

0 comments on commit 5556da7

Please sign in to comment.