diff --git a/azure-pipelines.bundlesize.yml b/azure-pipelines.bundlesize.yml index e6b55ca6b629d6..b8d802dcdbd64f 100644 --- a/azure-pipelines.bundlesize.yml +++ b/azure-pipelines.bundlesize.yml @@ -1,6 +1,6 @@ pr: - master - - shadow-dom + - shadow-dom # temporary to get preliminary results trigger: - master diff --git a/packages/merge-styles/etc/merge-styles.api.md b/packages/merge-styles/etc/merge-styles.api.md index 11f81c564ae898..f4f7e5d80114a5 100644 --- a/packages/merge-styles/etc/merge-styles.api.md +++ b/packages/merge-styles/etc/merge-styles.api.md @@ -4,6 +4,12 @@ ```ts +// @public (undocumented) +export type AddSheetCallback = ({ key, sheet }: { + key: string; + sheet: ExtendedCSSStyleSheet; +}) => void; + // @public (undocumented) export const cloneCSSStyleSheet: (srcSheet: CSSStyleSheet, targetSheet: CSSStyleSheet) => CSSStyleSheet; @@ -36,10 +42,8 @@ export type DeepPartial = { [P in keyof T]?: T[P] extends (infer U)[] ? DeepPartial[] : T[P] extends object ? DeepPartial : T[P]; }; -// Warning: (ae-forgotten-export) The symbol "EventArgs" needs to be exported by the entry point index.d.ts -// // @public (undocumented) -export type EventHandler = (args: EventArgs) => void; +export const DEFAULT_SHADOW_CONFIG: ShadowConfig; // @public (undocumented) export type ExtendedCSSStyleSheet = CSSStyleSheet & { @@ -99,6 +103,11 @@ export const InjectionMode: { // @public (undocumented) export type InjectionMode = (typeof InjectionMode)[keyof typeof InjectionMode]; +// Warning: (ae-forgotten-export) The symbol "InsertRuleArgs" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export type InsertRuleCallback = ({ key, sheet, rule }: InsertRuleArgs) => void; + // @public export type IProcessedStyleSet> = { [P in keyof Omit_2]: string; @@ -424,7 +433,7 @@ export interface ISerializedStylesheet { // (undocumented) classNameToArgs: Stylesheet['_classNameToArgs']; // (undocumented) - counter: Stylesheet['_styleCounter']; + counter: Stylesheet['_counter']; // (undocumented) keyToClassName: Stylesheet['_keyToClassName']; // (undocumented) @@ -559,18 +568,35 @@ export type ShadowConfig = { __isShadowConfig__: true; }; +// @public (undocumented) +export class ShadowDomStylesheet extends Stylesheet { + constructor(config?: IStyleSheetConfig, serializedStylesheet?: ISerializedStylesheet); + // (undocumented) + protected _createStyleElement(): HTMLStyleElement; + // (undocumented) + getAdoptedSheets(): Map; + // (undocumented) + protected _getCacheKey(key: string): string; + // (undocumented) + static getInstance(shadowConfig?: ShadowConfig): ShadowDomStylesheet; + // (undocumented) + insertRule(rule: string, preserve?: boolean): void; + // (undocumented) + onAddSheet(callback: AddSheetCallback): Function; +} + // @public export class Stylesheet { constructor(config?: IStyleSheetConfig, serializedStylesheet?: ISerializedStylesheet); - // (undocumented) - 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: ExtendedCSSStyleSheet, key: string, map: Map) => void): void; + protected _config: IStyleSheetConfig; // (undocumented) - getAdoptableStyleSheet(key: string): ExtendedCSSStyleSheet; + protected _createStyleElement(): HTMLStyleElement; + // (undocumented) + protected _getCacheKey(key: string): string; getClassName(displayName?: string): string; getClassNameCache(): { [key: string]: string; @@ -578,21 +604,13 @@ export class Stylesheet { static getInstance(shadowConfig?: ShadowConfig): Stylesheet; getRules(includePreservedRules?: boolean): string; insertedRulesFromClassName(className: string): string[] | undefined; - insertRule(rule: string, preserve?: boolean): void; - // (undocumented) - makeCSSStyleSheet(win: Window): ExtendedCSSStyleSheet; + insertRule(rule: string, preserve?: boolean, stylesheetKey?: string): void; // (undocumented) - offAddConstructableStyleSheet(callback: EventHandler): void; + protected _insertRuleIntoSheet(sheet: CSSStyleSheet | undefined | null, rule: string): boolean; // (undocumented) - offInsertRuleIntoConstructableStyleSheet(callback: EventHandler): void; - // (undocumented) - onAddConstructableStyleSheet(callback: EventHandler): void; - onInsertRule(callback: Function): Function; - // (undocumented) - onInsertRuleIntoConstructableStyleSheet(callback: EventHandler): void; + protected _lastStyleElement?: HTMLStyleElement; + onInsertRule(callback: Function | InsertRuleCallback): Function; onReset(callback: Function): Function; - // (undocumented) - projectStylesToWindow(targetWindow: Window): void; reset(): void; // (undocumented) resetKeys(): void; diff --git a/packages/merge-styles/src/EventMap.ts b/packages/merge-styles/src/EventMap.ts deleted file mode 100644 index 4414187a1050e8..00000000000000 --- a/packages/merge-styles/src/EventMap.ts +++ /dev/null @@ -1,60 +0,0 @@ -export type EventArgs = { key: string; sheet: T; rule?: string }; -export type EventHandler = (args: EventArgs) => void; - -export class EventMap { - private _events: Map[]>; - private _self: Map; - - constructor() { - this._self = new Map(); - this._events = new Map[]>(); - } - - public get(key: K) { - return this._self.get(key); - } - - public set(key: K, value: V) { - this._self.set(key, value); - } - - public has(key: K) { - return this._self.has(key); - } - - public forEach(callback: (value: V, key: K, map: Map) => void) { - this._self.forEach(callback); - } - - public raise(type: string, data: EventArgs) { - const handlers = this._events.get(type); - if (!handlers) { - return; - } - - for (const handler of handlers) { - if (handler) { - handler(data); - } - } - } - - public on(type: string, callback: EventHandler) { - const handlers = this._events.get(type); - if (!handlers) { - this._events.set(type, [callback]); - } else { - handlers.push(callback); - } - } - - public off(type: string, callback: EventHandler) { - const handlers = this._events.get(type); - if (handlers) { - const index = handlers.indexOf(callback); - if (index >= 0) { - handlers.splice(index, 1); - } - } - } -} diff --git a/packages/merge-styles/src/ShadowDomStylesheet.ts b/packages/merge-styles/src/ShadowDomStylesheet.ts new file mode 100644 index 00000000000000..5d04932afe573c --- /dev/null +++ b/packages/merge-styles/src/ShadowDomStylesheet.ts @@ -0,0 +1,238 @@ +/* eslint no-restricted-globals: 0 */ +import { InjectionMode, STYLESHEET_SETTING, Stylesheet } from './Stylesheet'; +import { DEFAULT_SHADOW_CONFIG, GLOBAL_STYLESHEET_KEY, SHADOW_DOM_STYLESHEET_SETTING } from './shadowConfig'; + +import type { + ExtendedCSSStyleSheet, + ISerializedStylesheet, + IStyleSheetConfig, + WindowWithMergeStyles, +} from './Stylesheet'; +import type { ShadowConfig } from './shadowConfig'; + +export const SUPPORTS_CONSTRUCTABLE_STYLESHEETS = + typeof document !== 'undefined' && Array.isArray(document.adoptedStyleSheets) && 'replace' in CSSStyleSheet.prototype; + +let supportsModifyingAdoptedStyleSheets = false; + +if (SUPPORTS_CONSTRUCTABLE_STYLESHEETS) { + try { + document.adoptedStyleSheets.push(); + supportsModifyingAdoptedStyleSheets = true; + } catch (e) { + supportsModifyingAdoptedStyleSheets = false; + } +} + +export const SUPPORTS_MODIFYING_ADOPTED_STYLESHEETS = supportsModifyingAdoptedStyleSheets; + +let _stylesheet: ShadowDomStylesheet | undefined; + +let _global: WindowWithMergeStyles = {}; + +// Grab window. +try { + // Why the cast? + // if compiled/type checked in same program with `@fluentui/font-icons-mdl2` which extends `Window` on global + // ( check packages/font-icons-mdl2/src/index.ts ) the definitions don't match! Thus the need of this extra assertion + _global = (window || {}) as WindowWithMergeStyles; +} catch { + /* leave as blank object */ +} + +export type AddSheetCallback = ({ key, sheet }: { key: string; sheet: ExtendedCSSStyleSheet }) => void; + +const copyOldGlobalRules = ( + stylesheet: ShadowDomStylesheet, + inShadow: boolean = false, + win: Window | undefined, + doc: Document | undefined, +) => { + if (!doc) { + // SSR + return; + } + + const oldGlobalRules = doc.querySelectorAll('[data-merge-styles]'); + if (oldGlobalRules) { + stylesheet.setConfig({ + window: win, + inShadow, + stylesheetKey: GLOBAL_STYLESHEET_KEY, + }); + + for (let i = 0; i < oldGlobalRules.length; i++) { + const styleElem = oldGlobalRules[i] as HTMLStyleElement; + styleElem.setAttribute('data-merge-styles-global', 'true'); + const cssRules = styleElem.sheet?.cssRules || []; + + for (let j = 0; j < cssRules.length; j++) { + const rule = cssRules[j]; + stylesheet.insertRule(rule.cssText); + } + } + } +}; + +export class ShadowDomStylesheet extends Stylesheet { + private _onAddSheetCallbacks: AddSheetCallback[] = []; + private _adoptableSheets: Map; + private _sheetCounter = 0; + + public static getInstance(shadowConfig?: ShadowConfig): ShadowDomStylesheet { + const sConfig = shadowConfig || DEFAULT_SHADOW_CONFIG; + const stylesheetKey = sConfig.stylesheetKey || GLOBAL_STYLESHEET_KEY; + const inShadow = sConfig.inShadow; + const win = sConfig.window || (typeof window !== 'undefined' ? window : undefined); + const global = (win || _global) as WindowWithMergeStyles; + const doc = win ? win.document : typeof document !== 'undefined' ? document : undefined; + + _stylesheet = global[STYLESHEET_SETTING] as ShadowDomStylesheet; + + // When an app has multiple versions of Fluent v8 it is possible + // that an older version of Stylesheet is initialized before + // the version that supports shadow DOM. We check for this case + // and re-initialize the stylesheet in that case. + const oldStylesheetInitializedFirst = _stylesheet && !_stylesheet.getAdoptedSheets; + + if ( + !_stylesheet || + oldStylesheetInitializedFirst || + (_stylesheet._lastStyleElement && _stylesheet._lastStyleElement.ownerDocument !== doc) + ) { + const fabricConfig = global?.FabricConfig || {}; + const defaultMergeStyles = { + window: win, + inShadow, + stylesheetKey, + }; + fabricConfig.mergeStyles = fabricConfig.mergeStyles || {}; + fabricConfig.mergeStyles = { ...defaultMergeStyles, ...fabricConfig.mergeStyles }; + + let stylesheet: ShadowDomStylesheet; + if (oldStylesheetInitializedFirst) { + stylesheet = new ShadowDomStylesheet(fabricConfig.mergeStyles, JSON.parse(_stylesheet.serialize())); + copyOldGlobalRules(stylesheet, inShadow, win, doc); + } else { + stylesheet = new ShadowDomStylesheet(fabricConfig.mergeStyles, fabricConfig.serializedStylesheet); + } + + _stylesheet = stylesheet; + global[STYLESHEET_SETTING] = _stylesheet; + } else { + _stylesheet.setConfig({ + window: win, + inShadow, + stylesheetKey, + }); + } + if (win) { + _stylesheet._getAdoptableStyleSheet(stylesheetKey); + } + + return _stylesheet; + } + + constructor(config?: IStyleSheetConfig, serializedStylesheet?: ISerializedStylesheet) { + super(config, serializedStylesheet); + + this._adoptableSheets = new Map(); + + _global[SHADOW_DOM_STYLESHEET_SETTING] = ShadowDomStylesheet; + } + + public getAdoptedSheets(): Map { + return this._adoptableSheets; + } + + public onAddSheet(callback: AddSheetCallback): Function { + this._onAddSheetCallbacks.push(callback); + + return () => { + this._onAddSheetCallbacks = this._onAddSheetCallbacks.filter(cb => cb !== callback); + }; + } + + public insertRule(rule: string, preserve?: boolean): void { + const { injectionMode, stylesheetKey = GLOBAL_STYLESHEET_KEY } = this._config; + + const injectStyles = injectionMode !== InjectionMode.none; + const addToConstructableStylesheet = + stylesheetKey === GLOBAL_STYLESHEET_KEY || !!this._adoptableSheets.has(stylesheetKey); + + let constructableSheet: CSSStyleSheet | undefined = undefined; + + if (injectStyles && addToConstructableStylesheet) { + constructableSheet = this._getAdoptableStyleSheet(stylesheetKey); + } + + if (constructableSheet) { + this._insertRuleIntoSheet(constructableSheet, rule); + } + + super.insertRule(rule, preserve, stylesheetKey); + } + + protected _getCacheKey(key: string): string { + const { inShadow = false, stylesheetKey: currentStylesheetKey = GLOBAL_STYLESHEET_KEY } = this._config; + + if (inShadow) { + return `__${currentStylesheetKey}__${key}`; + } + + return super._getCacheKey(key); + } + + protected _createStyleElement(): HTMLStyleElement { + const styleElement = super._createStyleElement(); + + if (this._config.stylesheetKey === GLOBAL_STYLESHEET_KEY) { + styleElement.setAttribute('data-merge-styles-global', 'true'); + } + + return styleElement; + } + + private _makeCSSStyleSheet(): ExtendedCSSStyleSheet { + const win = this._config.window || window; + let sheet: ExtendedCSSStyleSheet | undefined = undefined; + if (!SUPPORTS_CONSTRUCTABLE_STYLESHEETS) { + const style = this._createStyleElement(); + sheet = style.sheet as ExtendedCSSStyleSheet; + } else { + sheet = new (win as Window & typeof globalThis).CSSStyleSheet() as ExtendedCSSStyleSheet; + } + + if (sheet) { + sheet.bucketName = 'merge-styles'; + sheet.metadata = { + stylesheetKey: this._config.stylesheetKey || GLOBAL_STYLESHEET_KEY, + sortOrder: this._sheetCounter++, + }; + } + + return sheet; + } + + private _addAdoptableStyleSheet(key: string, sheet: ExtendedCSSStyleSheet, queue: boolean = true): void { + if (!this._adoptableSheets.has(key)) { + this._adoptableSheets.set(key, sheet); + const win = this._config.window; + if (queue && win) { + win.queueMicrotask(() => { + this._onAddSheetCallbacks.forEach(callback => callback({ key, sheet })); + }); + } + } + } + + private _getAdoptableStyleSheet(key: string): ExtendedCSSStyleSheet { + let sheet = this._adoptableSheets.get(key); + if (!sheet) { + sheet = this._makeCSSStyleSheet(); + this._addAdoptableStyleSheet(key, sheet); + } + + return sheet; + } +} diff --git a/packages/merge-styles/src/Stylesheet.ts b/packages/merge-styles/src/Stylesheet.ts index 6293e9062c61f5..4ffe12028d9a4f 100644 --- a/packages/merge-styles/src/Stylesheet.ts +++ b/packages/merge-styles/src/Stylesheet.ts @@ -2,9 +2,8 @@ // globals in stylesheets will be addressed as part of shadow DOM work. // See: https://github.com/microsoft/fluentui/issues/28058 import { IStyle } from './IStyle'; -import { DEFAULT_SHADOW_CONFIG, GLOBAL_STYLESHEET_KEY, ShadowConfig } from './shadowConfig'; -import { EventHandler, EventMap } from './EventMap'; -import { cloneExtendedCSSStyleSheet } from './cloneCSSStyleSheet'; +import { GLOBAL_STYLESHEET_KEY, SHADOW_DOM_STYLESHEET_SETTING } from './shadowConfig'; +import type { ShadowConfig } from './shadowConfig'; export const InjectionMode = { /** @@ -93,13 +92,13 @@ export interface IStyleSheetConfig { */ export interface ISerializedStylesheet { classNameToArgs: Stylesheet['_classNameToArgs']; - counter: Stylesheet['_styleCounter']; + counter: Stylesheet['_counter']; keyToClassName: Stylesheet['_keyToClassName']; preservedRules: Stylesheet['_preservedRules']; rules: Stylesheet['_rules']; } -const STYLESHEET_SETTING = '__stylesheet__'; +export const STYLESHEET_SETTING = '__stylesheet__'; /** * MSIE 11 doesn't cascade styles based on DOM ordering, but rather on the order that each style node @@ -114,50 +113,42 @@ declare global { } } -export const SUPPORTS_CONSTRUCTABLE_STYLESHEETS = - typeof document !== 'undefined' && Array.isArray(document.adoptedStyleSheets) && 'replace' in CSSStyleSheet.prototype; - -let supportsModifyingAdoptedStyleSheets = false; - -if (SUPPORTS_CONSTRUCTABLE_STYLESHEETS) { - try { - document.adoptedStyleSheets.push(); - supportsModifyingAdoptedStyleSheets = true; - } catch (e) { - supportsModifyingAdoptedStyleSheets = false; - } -} - -export const SUPPORTS_MODIFYING_ADOPTED_STYLESHEETS = supportsModifyingAdoptedStyleSheets; - -export type ExtendedCSSStyleSheet = CSSStyleSheet & { - bucketName: string; - metadata: Record; -}; - -let _global: (Window | {}) & { +export type WindowWithMergeStyles = (Window | {}) & { [STYLESHEET_SETTING]?: Stylesheet; + [SHADOW_DOM_STYLESHEET_SETTING]?: typeof Stylesheet; FabricConfig?: { mergeStyles?: IStyleSheetConfig; serializedStylesheet?: ISerializedStylesheet; }; -} = {}; +}; + +let _global: WindowWithMergeStyles = {}; // Grab window. try { // Why the cast? // if compiled/type checked in same program with `@fluentui/font-icons-mdl2` which extends `Window` on global // ( check packages/font-icons-mdl2/src/index.ts ) the definitions don't match! Thus the need of this extra assertion - _global = (window || {}) as typeof _global; + _global = (window || {}) as WindowWithMergeStyles; } catch { /* leave as blank object */ } let _stylesheet: Stylesheet | undefined; -const _getGlobal = (win?: Window): typeof _global => { - return (win ?? _global) as typeof _global; +export type ExtendedCSSStyleSheet = CSSStyleSheet & { + bucketName: string; + metadata: Record; +}; + +type InsertRuleArgs = { + key?: string; + sheet?: ExtendedCSSStyleSheet | null; + rule?: string; }; + +export type InsertRuleCallback = ({ key, sheet, rule }: InsertRuleArgs) => void; + /** * Represents the state of styles registered in the page. Abstracts * the surface for adding styles to the stylesheet, exposes helpers @@ -166,58 +157,35 @@ const _getGlobal = (win?: Window): typeof _global => { * @public */ export class Stylesheet { - private _lastStyleElement?: HTMLStyleElement; + protected _lastStyleElement?: HTMLStyleElement; + protected _config: IStyleSheetConfig; + private _styleElement?: HTMLStyleElement; private _rules: string[] = []; private _preservedRules: string[] = []; - private _config: IStyleSheetConfig; - private _styleCounter = 0; + private _counter = 0; private _keyToClassName: { [key: string]: string } = {}; - private _onInsertRuleCallbacks: Function[] = []; + private _onInsertRuleCallbacks: (Function | InsertRuleCallback)[] = []; private _onResetCallbacks: Function[] = []; private _classNameToArgs: { [key: string]: { args: any; rules: string[] } } = {}; - private _adoptableSheets?: EventMap; - private _sheetCounter = 0; /** * Gets the singleton instance. */ public static getInstance(shadowConfig?: ShadowConfig): Stylesheet { - const { stylesheetKey = GLOBAL_STYLESHEET_KEY, inShadow, window: win } = shadowConfig ?? DEFAULT_SHADOW_CONFIG; - const global = (win ?? _global ?? {}) as typeof _global; - - _stylesheet = global[STYLESHEET_SETTING] as Stylesheet; - - const doc = win ? win.document : typeof document !== 'undefined' ? document : undefined; - - // When an app has multiple versions of Fluent v8 it is possible - // that an older version of Stylesheet is initialized before - // the version that supports shadow DOM. We check for this case - // and re-initialize the stylesheet in that case. - const oldStylesheetInitializedFirst = _stylesheet && !_stylesheet.addAdoptableStyleSheet; - - if ( - !_stylesheet || - oldStylesheetInitializedFirst || - (_stylesheet._lastStyleElement && _stylesheet._lastStyleElement.ownerDocument !== doc) - ) { - const fabricConfig = global?.FabricConfig || {}; - fabricConfig.mergeStyles = fabricConfig.mergeStyles || {}; - fabricConfig.mergeStyles.window = - fabricConfig.mergeStyles.window || win || (typeof window !== 'undefined' ? window : undefined); - fabricConfig.mergeStyles.inShadow = fabricConfig.mergeStyles.inShadow ?? inShadow; - fabricConfig.mergeStyles.stylesheetKey = fabricConfig.mergeStyles.stylesheetKey ?? stylesheetKey; - - let stylesheet: Stylesheet; - if (oldStylesheetInitializedFirst) { - stylesheet = new Stylesheet(fabricConfig.mergeStyles, JSON.parse(_stylesheet.serialize())); - } else { - stylesheet = new Stylesheet(fabricConfig.mergeStyles, fabricConfig.serializedStylesheet); - } + _stylesheet = _global[STYLESHEET_SETTING] as Stylesheet; + + if (_global[SHADOW_DOM_STYLESHEET_SETTING]) { + return _global[SHADOW_DOM_STYLESHEET_SETTING].getInstance(shadowConfig); + } + + if (!_stylesheet || (_stylesheet._lastStyleElement && _stylesheet._lastStyleElement.ownerDocument !== document)) { + const fabricConfig = _global?.FabricConfig || {}; + const stylesheet = new Stylesheet(fabricConfig.mergeStyles, fabricConfig.serializedStylesheet); _stylesheet = stylesheet; - global[STYLESHEET_SETTING] = _stylesheet; + _global[STYLESHEET_SETTING] = stylesheet; } return _stylesheet; @@ -234,118 +202,12 @@ export class Stylesheet { }; this._classNameToArgs = serializedStylesheet?.classNameToArgs ?? this._classNameToArgs; - this._styleCounter = serializedStylesheet?.counter ?? this._styleCounter; + this._counter = serializedStylesheet?.counter ?? this._counter; this._keyToClassName = this._config.classNameCache ?? serializedStylesheet?.keyToClassName ?? this._keyToClassName; this._preservedRules = serializedStylesheet?.preservedRules ?? this._preservedRules; this._rules = serializedStylesheet?.rules ?? this._rules; } - public addAdoptableStyleSheet(key: string, sheet: ExtendedCSSStyleSheet): void { - if (!this._adoptableSheets) { - this._adoptableSheets = new EventMap(); - // window.__DEBUG_SHEETS__ = this._adoptableSheets; - } - - if (!this._adoptableSheets.has(key)) { - this._adoptableSheets.set(key, sheet); - this._config.window?.queueMicrotask?.(() => { - this._adoptableSheets!.raise('add-sheet', { key, sheet }); - }); - } - } - - public getAdoptableStyleSheet(key: string): ExtendedCSSStyleSheet { - if (!this._adoptableSheets) { - this._adoptableSheets = new EventMap(); - // window.__DEBUG_SHEETS__ = this._adoptableSheets; - } - - let sheet = this._adoptableSheets.get(key); - if (!sheet) { - sheet = this.makeCSSStyleSheet(this._config.window ?? window); - this.addAdoptableStyleSheet(key, sheet); - } - - return sheet; - } - - public forEachAdoptedStyleSheet( - callback: (value: ExtendedCSSStyleSheet, key: string, map: Map) => void, - ): void { - this._adoptableSheets?.forEach(callback); - } - - public onAddConstructableStyleSheet(callback: EventHandler): void { - if (!this._adoptableSheets) { - this._adoptableSheets = new EventMap(); - // window.__DEBUG_SHEETS__ = this._adoptableSheets; - } - - this._adoptableSheets.on('add-sheet', callback); - } - - public offAddConstructableStyleSheet(callback: EventHandler): void { - if (!this._adoptableSheets) { - this._adoptableSheets = new EventMap(); - // window.__DEBUG_SHEETS__ = this._adoptableSheets; - } - - this._adoptableSheets.off('add-sheet', callback); - } - - public onInsertRuleIntoConstructableStyleSheet(callback: EventHandler): void { - if (!this._adoptableSheets) { - this._adoptableSheets = new EventMap(); - // window.__DEBUG_SHEETS__ = this._adoptableSheets; - } - - this._adoptableSheets.on('insert-rule', callback); - } - - public offInsertRuleIntoConstructableStyleSheet(callback: EventHandler): void { - if (!this._adoptableSheets) { - this._adoptableSheets = new EventMap(); - // window.__DEBUG_SHEETS__ = this._adoptableSheets; - } - - this._adoptableSheets.off('insert-rule', callback); - } - - public projectStylesToWindow(targetWindow: Window): void { - const global = _getGlobal(this._config.window); - - const serialized = JSON.parse(this.serialize()); - const targetStylesheet = new Stylesheet( - { - injectionMode: this._config.injectionMode, - window: targetWindow, - }, - serialized, - ); - - (targetWindow as typeof _global)[STYLESHEET_SETTING] = targetStylesheet; - - this.forEachAdoptedStyleSheet((srcSheet, key) => { - const clonedSheet = cloneExtendedCSSStyleSheet(srcSheet, this.makeCSSStyleSheet(targetWindow)); - targetStylesheet.addAdoptableStyleSheet(key, clonedSheet); - }); - - if ((global as Window)?.document) { - const globalStyles = (global as Window).document.querySelectorAll('[data-merge-styles-global]') || []; - for (let i = 0; i < globalStyles.length; i++) { - const styleTag = targetWindow.document.createElement('style'); - // TODO: insert this in the right place - targetWindow.document.head.appendChild(styleTag); - const srcSheet = (globalStyles[i] as HTMLStyleElement).sheet; - if (srcSheet) { - for (let j = 0; j < srcSheet.cssRules.length; j++) { - styleTag.sheet?.insertRule(srcSheet.cssRules[j].cssText); - } - } - } - } - } - /** * Serializes the Stylesheet instance into a format which allows rehydration on creation. * @returns string representation of `ISerializedStylesheet` interface. @@ -353,7 +215,7 @@ export class Stylesheet { public serialize(): string { return JSON.stringify({ classNameToArgs: this._classNameToArgs, - counter: this._styleCounter, + counter: this._counter, keyToClassName: this._keyToClassName, preservedRules: this._preservedRules, rules: this._rules, @@ -390,7 +252,7 @@ export class Stylesheet { * @param callback - A callback which will be called when a rule is inserted. * @returns function which when called un-registers provided callback. */ - public onInsertRule(callback: Function): Function { + public onInsertRule(callback: Function | InsertRuleCallback): Function { this._onInsertRuleCallbacks.push(callback); return () => { @@ -407,7 +269,7 @@ export class Stylesheet { const { namespace } = this._config; const prefix = displayName || this._config.defaultPrefix; - return `${namespace ? namespace + '-' : ''}${prefix}-${this._styleCounter++}`; + return `${namespace ? namespace + '-' : ''}${prefix}-${this._counter++}`; } /** @@ -415,8 +277,7 @@ export class Stylesheet { * registered with the stylesheet. */ public cacheClassName(className: string, key: string, args: IStyle[], rules: string[]): void { - const cacheKey = this._getCacheKey(key); - this._keyToClassName[cacheKey] = className; + this._keyToClassName[this._getCacheKey(key)] = className; this._classNameToArgs[className] = { args, rules, @@ -428,8 +289,7 @@ export class Stylesheet { * registered using cacheClassName. */ public classNameFromKey(key: string): string | undefined { - const cacheKey = this._getCacheKey(key); - return this._keyToClassName[cacheKey]; + return this._keyToClassName[this._getCacheKey(key)]; } /** @@ -463,55 +323,26 @@ export class Stylesheet { * Inserts a css rule into the stylesheet. * @param preserve - Preserves the rule beyond a reset boundary. */ - public insertRule(rule: string, preserve?: boolean): void { - const { injectionMode, stylesheetKey = GLOBAL_STYLESHEET_KEY } = this._config; - - const injectStyles = injectionMode !== InjectionMode.none; - const addToConstructableStylesheet = - stylesheetKey === GLOBAL_STYLESHEET_KEY || !!this._adoptableSheets?.has(stylesheetKey); + public insertRule(rule: string, preserve?: boolean, stylesheetKey: string = GLOBAL_STYLESHEET_KEY): void { + const { injectionMode } = this._config; - let element: HTMLStyleElement | undefined = undefined; - let constructableSheet: CSSStyleSheet | undefined = undefined; - - // TODO: maybe change this? seems like we're inserting everything into head always still? - if (injectStyles) { - element = this._getStyleElement(); - } - - if (injectStyles && addToConstructableStylesheet) { - constructableSheet = this.getAdoptableStyleSheet(stylesheetKey); - } + const element: HTMLStyleElement | undefined = + injectionMode !== InjectionMode.none ? this._getStyleElement() : undefined; if (preserve) { this._preservedRules.push(rule); } - if (element || constructableSheet) { - let inserted = false; - const sheet: CSSStyleSheet | null = constructableSheet ?? element?.sheet ?? null; + if (element) { switch (injectionMode) { case InjectionMode.insertNode: - inserted = this._insertNode(element, rule); + this._insertRuleIntoSheet(element.sheet, rule); break; case InjectionMode.appendChild: - if (element) { - (element as HTMLStyleElement).appendChild(document.createTextNode(rule)); - } + (element as HTMLStyleElement).appendChild(document.createTextNode(rule)); break; } - - if (constructableSheet) { - inserted = this._insertRuleIntoSheet(constructableSheet, rule); - } - - if (inserted && sheet) { - this._adoptableSheets?.raise('insert-rule', { - key: stylesheetKey, - sheet: sheet as ExtendedCSSStyleSheet, - rule, - }); - } } else { this._rules.push(rule); } @@ -522,7 +353,9 @@ export class Stylesheet { this._config.onInsertRule(rule); } - this._onInsertRuleCallbacks.forEach(callback => callback()); + this._onInsertRuleCallbacks.forEach(callback => + callback({ key: stylesheetKey, sheet: (element ? element.sheet : undefined) as ExtendedCSSStyleSheet, rule }), + ); } /** @@ -539,7 +372,7 @@ export class Stylesheet { */ public reset(): void { this._rules = []; - this._styleCounter = 0; + this._counter = 0; this._classNameToArgs = {}; this._keyToClassName = {}; @@ -551,89 +384,14 @@ export class Stylesheet { this._keyToClassName = {}; } - public makeCSSStyleSheet(win: Window): ExtendedCSSStyleSheet { - let sheet: ExtendedCSSStyleSheet | undefined = undefined; - if (!SUPPORTS_CONSTRUCTABLE_STYLESHEETS) { - const style = this._createStyleElement(win); - 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 sheet; - } - - private _insertNode(element: HTMLStyleElement | undefined, rule: string): boolean { - if (!element) { - return false; - } - - const { sheet } = element! as HTMLStyleElement; - - try { - (sheet as CSSStyleSheet).insertRule(rule, (sheet as CSSStyleSheet).cssRules.length); - return true; - } catch (e) { - // The browser will throw exceptions on unsupported rules (such as a moz prefix in webkit.) - // We need to swallow the exceptions for this scenario, otherwise we'd need to filter - // which could be slower and bulkier. - } - - return false; - } - - private _insertRuleIntoSheet(sheet: CSSStyleSheet | undefined, rule: string): boolean { - if (!sheet) { - return false; - } - - try { - sheet!.insertRule(rule, sheet!.cssRules.length); - return true; - } catch (e) { - // The browser will throw exceptions on unsupported rules (such as a moz prefix in webkit.) - // We need to swallow the exceptions for this scenario, otherwise we'd need to filter - // which could be slower and bulkier. - } - - return false; - } - - private _getStyleElement(winArg?: Window): HTMLStyleElement | undefined { - const win = winArg ?? this._config.window ?? window; - const doc = win.document; - if (!this._styleElement && typeof doc !== 'undefined') { - this._styleElement = this._createStyleElement(winArg); - - if (!REUSE_STYLE_NODE) { - // Reset the style element on the next frame. - win.requestAnimationFrame(() => { - this._styleElement = undefined; - }); - } - } - return this._styleElement; - } - - private _createStyleElement(winArg?: Window): HTMLStyleElement { - const doc = winArg?.document ?? this._config.window?.document ?? document; + protected _createStyleElement(): HTMLStyleElement { + const doc = this._config.window?.document || document; const head: HTMLHeadElement = doc.head; const styleElement = doc.createElement('style'); let nodeToInsertBefore: Node | null = null; styleElement.setAttribute('data-merge-styles', 'true'); - if (this._config.stylesheetKey === GLOBAL_STYLESHEET_KEY) { - styleElement.setAttribute('data-merge-styles-global', 'true'); - } - const { cspSettings } = this._config; if (cspSettings) { if (cspSettings.nonce) { @@ -660,16 +418,42 @@ export class Stylesheet { return styleElement; } - private _getCacheKey(key: string): string { - const { inShadow = false, stylesheetKey: currentStylesheetKey = GLOBAL_STYLESHEET_KEY } = this._config; + protected _insertRuleIntoSheet(sheet: CSSStyleSheet | undefined | null, rule: string): boolean { + if (!sheet) { + return false; + } - if (inShadow) { - return `__${currentStylesheetKey}__${key}`; + try { + sheet!.insertRule(rule, sheet!.cssRules.length); + return true; + } catch (e) { + // The browser will throw exceptions on unsupported rules (such as a moz prefix in webkit.) + // We need to swallow the exceptions for this scenario, otherwise we'd need to filter + // which could be slower and bulkier. } + return false; + } + + protected _getCacheKey(key: string): string { return key; } + private _getStyleElement(): HTMLStyleElement | undefined { + if (!this._styleElement) { + this._styleElement = this._createStyleElement(); + + if (!REUSE_STYLE_NODE) { + // Reset the style element on the next frame. + const win = this._config.window || window; + win.requestAnimationFrame(() => { + this._styleElement = undefined; + }); + } + } + return this._styleElement; + } + private _findPlaceholderStyleTag(): Element | null { const head: HTMLHeadElement = document.head; if (head) { diff --git a/packages/merge-styles/src/cloneCSSStyleSheet.ts b/packages/merge-styles/src/cloneCSSStyleSheet.ts index 6128c064e72f19..8199c2e5611006 100644 --- a/packages/merge-styles/src/cloneCSSStyleSheet.ts +++ b/packages/merge-styles/src/cloneCSSStyleSheet.ts @@ -2,7 +2,7 @@ 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); + targetSheet.insertRule(srcSheet.cssRules[i].cssText, i); } return targetSheet; diff --git a/packages/merge-styles/src/index.ts b/packages/merge-styles/src/index.ts index e41d520082c17f..34e2bec9a69a77 100644 --- a/packages/merge-styles/src/index.ts +++ b/packages/merge-styles/src/index.ts @@ -32,23 +32,30 @@ export { fontFace } from './fontFace'; export { keyframes } from './keyframes'; +export { InjectionMode, Stylesheet } from './Stylesheet'; +export type { + ICSPSettings, + ISerializedStylesheet, + IStyleSheetConfig, + ExtendedCSSStyleSheet, + InsertRuleCallback, +} from './Stylesheet'; + export { - InjectionMode, - Stylesheet, + ShadowDomStylesheet, SUPPORTS_CONSTRUCTABLE_STYLESHEETS, SUPPORTS_MODIFYING_ADOPTED_STYLESHEETS, -} from './Stylesheet'; -export type { ICSPSettings, ISerializedStylesheet, IStyleSheetConfig, ExtendedCSSStyleSheet } from './Stylesheet'; +} from './ShadowDomStylesheet'; + +export type { AddSheetCallback } from './ShadowDomStylesheet'; export { setRTL } from './StyleOptionsState'; export type { ObjectOnly } from './ObjectOnly'; -export { GLOBAL_STYLESHEET_KEY, makeShadowConfig } from './shadowConfig'; +export { DEFAULT_SHADOW_CONFIG, GLOBAL_STYLESHEET_KEY, makeShadowConfig } from './shadowConfig'; export type { ShadowConfig } from './shadowConfig'; export { cloneCSSStyleSheet } from './cloneCSSStyleSheet'; -export type { EventHandler } from './EventMap'; - import './version'; diff --git a/packages/merge-styles/src/mergeStyleSets.ts b/packages/merge-styles/src/mergeStyleSets.ts index 8ff78b856c84a4..4665c6095f0be5 100644 --- a/packages/merge-styles/src/mergeStyleSets.ts +++ b/packages/merge-styles/src/mergeStyleSets.ts @@ -99,14 +99,6 @@ export function mergeStyleSets( export function mergeStyleSets( ...styleSets: Array ): IProcessedStyleSet { - // let shadowConfig: ShadowConfig | undefined = undefined; - // let sets = styleSets; - // if (isShadowConfig(styleSets[0])) { - // shadowConfig = styleSets[0] as ShadowConfig; - // sets = styleSets.slice(1); - // } - - // return mergeCssSets(sets as any, { ...getStyleOptions(), shadowConfig }); return mergeCssSets(styleSets as any, getStyleOptions()); } diff --git a/packages/merge-styles/src/shadowConfig.ts b/packages/merge-styles/src/shadowConfig.ts index 222c5841902074..0cb6d1f8d647dc 100644 --- a/packages/merge-styles/src/shadowConfig.ts +++ b/packages/merge-styles/src/shadowConfig.ts @@ -6,6 +6,7 @@ export type ShadowConfig = { }; export const GLOBAL_STYLESHEET_KEY = '__global__'; +export const SHADOW_DOM_STYLESHEET_SETTING = '__shadow_dom_stylesheet__'; export const DEFAULT_SHADOW_CONFIG: ShadowConfig = { stylesheetKey: GLOBAL_STYLESHEET_KEY, diff --git a/packages/react-charting/src/components/AreaChart/__snapshots__/AreaChartRTL.test.tsx.snap b/packages/react-charting/src/components/AreaChart/__snapshots__/AreaChartRTL.test.tsx.snap index fc39e1324d2409..b8fb9ccc243790 100644 --- a/packages/react-charting/src/components/AreaChart/__snapshots__/AreaChartRTL.test.tsx.snap +++ b/packages/react-charting/src/components/AreaChart/__snapshots__/AreaChartRTL.test.tsx.snap @@ -1481,6 +1481,20 @@ exports[`Area chart rendering Should render the area chart with date x-axis data @media screen and (-ms-high-contrast: active), screen and (forced-colors: active){.ms-Fabric--isFocusVisible &:focus:after { outline-color: #605e5c; } + :host(.ms-Fabric--isFocusVisible) &:focus:after { + border: 1px solid transparent; + bottom: 1px; + content: ""; + left: 1px; + outline: 1px solid #605e5c; + position: absolute; + right: 1px; + top: 1px; + z-index: 1; + } + @media screen and (-ms-high-contrast: active), screen and (forced-colors: active){:host(.ms-Fabric--isFocusVisible) &:focus:after { + outline-color: #605e5c; + } data-is-focusable="true" role="option" tabindex="0" @@ -1567,6 +1581,20 @@ exports[`Area chart rendering Should render the area chart with date x-axis data @media screen and (-ms-high-contrast: active), screen and (forced-colors: active){.ms-Fabric--isFocusVisible &:focus:after { outline-color: #605e5c; } + :host(.ms-Fabric--isFocusVisible) &:focus:after { + border: 1px solid transparent; + bottom: 1px; + content: ""; + left: 1px; + outline: 1px solid #605e5c; + position: absolute; + right: 1px; + top: 1px; + z-index: 1; + } + @media screen and (-ms-high-contrast: active), screen and (forced-colors: active){:host(.ms-Fabric--isFocusVisible) &:focus:after { + outline-color: #605e5c; + } data-is-focusable="true" role="option" tabindex="-1" diff --git a/packages/react-examples/package.json b/packages/react-examples/package.json index 95310ee824d98c..207368f3cac8db 100644 --- a/packages/react-examples/package.json +++ b/packages/react-examples/package.json @@ -43,6 +43,7 @@ "@fluentui/react-focus": "^8.8.40", "@fluentui/react-hooks": "^8.6.36", "@fluentui/react-icons-mdl2": "^1.3.57", + "@fluentui/react-window-provider": "^2.2.18", "@fluentui/scheme-utilities": "^8.3.42", "@fluentui/style-utilities": "^8.10.3", "@fluentui/theme": "^2.6.41", diff --git a/packages/react-examples/src/react/ShadowDOM/ShadowDOM.ChildWindow.Example.tsx b/packages/react-examples/src/react/ShadowDOM/ShadowDOM.ChildWindow.Example.tsx deleted file mode 100644 index 8dbd57bc89b2f5..00000000000000 --- a/packages/react-examples/src/react/ShadowDOM/ShadowDOM.ChildWindow.Example.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { - Callout, - DefaultButton, - PrimaryButton, - Checkbox, - TextField, - Stack, - IStackTokens, - SpinButton, - Text, - FontIcon, - Icon, - ComboBox, - IComboBoxOption, - SelectableOptionMenuItemType, -} from '@fluentui/react'; -// eslint-disable-next-line -import { WindowProvider } from '@fluentui/react-window-provider'; -import { CompassNWIcon, DictionaryIcon, TrainSolidIcon } from '@fluentui/react-icons-mdl2'; -import { Stylesheet } from '@fluentui/merge-styles'; - -import { Shadow } from './ShadowHelper'; - -export interface IButtonExampleProps { - // These are set based on the toggles shown above the examples (not needed in real code) - disabled?: boolean; - checked?: boolean; -} - -// Example formatting -const stackTokens: IStackTokens = { childrenGap: 10 }; - -const options: IComboBoxOption[] = [ - { key: 'Header1', text: 'First heading', itemType: SelectableOptionMenuItemType.Header }, - { key: 'A', text: 'Option A' }, - { key: 'B', text: 'Option B' }, - { key: 'C', text: 'Option C' }, - { key: 'D', text: 'Option D' }, - { key: 'divider', text: '-', itemType: SelectableOptionMenuItemType.Divider }, - { key: 'Header2', text: 'Second heading', itemType: SelectableOptionMenuItemType.Header }, - { key: 'E', text: 'Option E' }, - { key: 'F', text: 'Option F', disabled: true }, - { key: 'G', text: 'Option G' }, - { key: 'H', text: 'Option H' }, - { key: 'I', text: 'Option I' }, - { key: 'J', text: 'Option J' }, -]; - -const TestLayer: React.FC = () => { - const [showCallout, setShowCallout] = React.useState(false); - - return ( - <> - {/* eslint-disable-next-line react/jsx-no-bind */} - setShowCallout(!showCallout)} /> - {showCallout && ( - setShowCallout(false)} - setInitialFocus - styles={{ root: { padding: '1rem' } }} - > - - Callout shows up next to target within shadow DOM as expected. Default layer host is also created within the - corresponding shadow DOM if no layer host is provided. Note that if providing a custom layer host, it must - be in the same shadow DOM as the target. - - - )} - - ); -}; - -type TestCompProps = { - inShadow: boolean; -}; - -const TestComp: React.FC = ({ inShadow }) => { - const label = inShadow ? 'Shadow DOM' : 'Light DOM'; - - const [disabled, setDisabled] = React.useState(false); - const onClick = () => { - setDisabled(!disabled); - }; - - return ( - - {label} - - - - - - - - {/* eslint-disable-next-line */} - - - - FontIcons - - - - - - - - Icons - - - - - - - - {/* SVG icons use SCSS and load-themed-styles so we'll need to - address that to get them rendering correctly in shadow */} - SVG Icons - - - - - - - - ); -}; - -const TestWindow: React.FC = () => { - const openWindow = () => { - const childWindow = window.open(); - if (!childWindow) { - return; - } - - // childWindow.__SENTINAL__ = 'child'; - - Stylesheet.getInstance().projectStylesToWindow(childWindow); - - const childRoot = childWindow.document.body.appendChild(childWindow.document.createElement('div')); - ReactDOM.render( - - - - - , - childRoot, - ); - }; - - // eslint-disable-next-line - return ; -}; - -export const ShadowDOMChildWindowExample: React.FunctionComponent = () => { - return ( - <> - - - - - - ); -}; diff --git a/packages/react-examples/src/react/ShadowDOM/ShadowDOM.doc.tsx b/packages/react-examples/src/react/ShadowDOM/ShadowDOM.doc.tsx index a61025548b2042..172ed13b327094 100644 --- a/packages/react-examples/src/react/ShadowDOM/ShadowDOM.doc.tsx +++ b/packages/react-examples/src/react/ShadowDOM/ShadowDOM.doc.tsx @@ -82,7 +82,6 @@ import { ShadowDOMTimePickerExample } from './ShadowDOM.TimePicker.Example'; import { ShadowDOMToggleExample } from './ShadowDOM.Toggle.Example'; import { ShadowDOMTooltipExample } from './ShadowDOM.Tooltip.Example'; import { ShadowDOMWeeklyDayPickerExample } from './ShadowDOM.WeeklyDayPicker.Example'; -import { ShadowDOMChildWindowExample } from './ShadowDOM.ChildWindow.Example'; import { IDocPageProps } from '@fluentui/react/lib/common/DocPage.types'; const ShadowDOMActivityItemExampleCode = @@ -334,9 +333,6 @@ const ShadowDOMTooltipExampleCode = const ShadowDOMWeeklyDayPickerExampleCode = require('!raw-loader?esModule=false!@fluentui/react-examples/src/react/ShadowDOM/ShadowDOM.WeeklyPicker.Example.tsx') as string; -const ShadowDOMChildWindowExampleCode = - require('!raw-loader?esModule=false!@fluentui/react-examples/src/react/ShadowDOM/ShadowDOM.ChildWindow.Example.tsx') as string; - /** * Exports a function because the documentation of this page requires some interactivity that is passed in here * as a prop. @@ -761,11 +757,6 @@ export const ShadowDOMPageProps = (): IDocPageProps => ({ code: ShadowDOMWeeklyDayPickerExampleCode, view: , }, - { - title: 'Child Window', - code: ShadowDOMChildWindowExampleCode, - view: , - }, ], allowNativeProps: false, diff --git a/packages/react-examples/src/react/ShadowDOM/ShadowHelper.tsx b/packages/react-examples/src/react/ShadowDOM/ShadowHelper.tsx index e47cf461569d56..d0a437095dd8d8 100644 --- a/packages/react-examples/src/react/ShadowDOM/ShadowHelper.tsx +++ b/packages/react-examples/src/react/ShadowDOM/ShadowHelper.tsx @@ -1,5 +1,17 @@ import * as React from 'react'; -import { FocusRectsProvider, MergeStylesRootProvider, MergeStylesShadowRootProvider } from '@fluentui/react'; +import { + FocusRectsProvider, + MergeStylesRootProvider, + MergeStylesShadowRootProvider, + useAdoptedStylesheet, + useAdoptedStylesheetEx, + useShadowConfig, + useMergeStylesShadowRootContext, + useHasMergeStylesShadowRootContext, + useMergeStylesRootStylesheets, + useStyled, +} from '@fluentui/react'; +import { useWindow } from '@fluentui/react-window-provider'; import root from 'react-shadow'; export type ShadowProps = { @@ -20,7 +32,17 @@ export const Shadow: React.FC = ({ window, children }) => { }, []); return ( - + diff --git a/packages/react-focus/etc/react-focus.api.md b/packages/react-focus/etc/react-focus.api.md index 420c1dc237d940..16d37c28fc2ce6 100644 --- a/packages/react-focus/etc/react-focus.api.md +++ b/packages/react-focus/etc/react-focus.api.md @@ -5,7 +5,7 @@ ```ts import type { IRefObject } from '@fluentui/utilities'; -import { MergeStylesShadowRootContextValue } from '@fluentui/utilities/lib/shadowDom/MergeStylesShadowRootContext'; +import { MergeStylesShadowRootContextValue } from '@fluentui/utilities'; import type { Point } from '@fluentui/utilities'; import * as React_2 from 'react'; diff --git a/packages/react/etc/react.api.md b/packages/react/etc/react.api.md index b40918c5aa8286..34cd202d9bfed3 100644 --- a/packages/react/etc/react.api.md +++ b/packages/react/etc/react.api.md @@ -356,7 +356,7 @@ import { Settings } from '@fluentui/utilities'; import { SettingsFunction } from '@fluentui/utilities'; import { setVirtualParent } from '@fluentui/utilities'; import { setWarningCallback } from '@fluentui/utilities'; -import { ShadowConfig } from '@fluentui/merge-styles'; +import type { ShadowConfig } from '@fluentui/style-utilities'; import { shallowCompare } from '@fluentui/utilities'; import { SharedColors } from '@fluentui/theme'; import { shouldWrapFocus } from '@fluentui/utilities'; @@ -376,12 +376,16 @@ import { trProperties } from '@fluentui/utilities'; import { unhoistMethods } from '@fluentui/utilities'; import { unregisterIcons } from '@fluentui/style-utilities'; import { useAdoptedStylesheet } from '@fluentui/utilities'; +import { useAdoptedStylesheetEx } from '@fluentui/utilities'; import { useCustomizationSettings } from '@fluentui/utilities'; import { useDocument } from '@fluentui/react-window-provider'; import { useFocusRects } from '@fluentui/utilities'; import { useHasMergeStylesShadowRootContext } from '@fluentui/utilities'; +import { useMergeStylesHooks } from '@fluentui/utilities'; import { useMergeStylesRootStylesheets } from '@fluentui/utilities'; import { useMergeStylesShadowRootContext } from '@fluentui/utilities'; +import { useShadowConfig } from '@fluentui/utilities'; +import { useStyled } from '@fluentui/utilities'; import { useWindow } from '@fluentui/react-window-provider'; import { values } from '@fluentui/utilities'; import { videoProperties } from '@fluentui/utilities'; @@ -11440,6 +11444,8 @@ export function updateT(color: IColor, t: number): IColor; export { useAdoptedStylesheet } +export { useAdoptedStylesheetEx } + export { useCustomizationSettings } export { useDocument } @@ -11454,6 +11460,8 @@ export function useHeightOffset({ finalHeight }: IPositioningContainerProps, con // @public export function useKeytipRef(options: KeytipDataOptions): React_2.Ref; +export { useMergeStylesHooks } + export { useMergeStylesRootStylesheets } export { useMergeStylesShadowRootContext } @@ -11461,6 +11469,10 @@ export { useMergeStylesShadowRootContext } // @public export const useResponsiveMode: (elementRef: React_2.RefObject, overrideResponsiveMode?: ResponsiveMode) => ResponsiveMode; +export { useShadowConfig } + +export { useStyled } + // @public @deprecated export type UseStylesOptions = { theme?: Theme; diff --git a/packages/react/src/Styling.ts b/packages/react/src/Styling.ts index 71908bf3268ae3..4dd00c1d37dee2 100644 --- a/packages/react/src/Styling.ts +++ b/packages/react/src/Styling.ts @@ -96,9 +96,10 @@ export type { IStyleSet, IStyleSheetConfig, ITheme, + ShadowConfig, } from '@fluentui/style-utilities'; -import { ShadowConfig } from '@fluentui/merge-styles'; +import type { ShadowConfig } from '@fluentui/style-utilities'; export interface IShadowDomStyle { /** * Optional configuration object when using shadow DOM. diff --git a/packages/react/src/Utilities.ts b/packages/react/src/Utilities.ts index b6b50f744c7dda..b01024409bbe23 100644 --- a/packages/react/src/Utilities.ts +++ b/packages/react/src/Utilities.ts @@ -176,11 +176,15 @@ export { trProperties, unhoistMethods, useAdoptedStylesheet, + useAdoptedStylesheetEx, useCustomizationSettings, useFocusRects, useHasMergeStylesShadowRootContext, + useMergeStylesHooks, useMergeStylesRootStylesheets, useMergeStylesShadowRootContext, + useShadowConfig, + useStyled, values, videoProperties, warn, @@ -190,7 +194,10 @@ export { warnMutuallyExclusive, } from '@fluentui/utilities'; export type { + AdoptedStylesheetHook, + AdoptedStylesheetExHook, FitMode, + HasMergeStylesShadowRootContextHook, IAsAsyncOptions, IBaseProps, ICancelable, @@ -248,5 +255,7 @@ export type { Settings, // eslint-disable-next-line deprecation/deprecation SettingsFunction, + ShadowConfigHook, StyleFunction, + UseStyledHook, } from '@fluentui/utilities'; diff --git a/packages/react/src/components/ComboBox/ComboBox.classNames.ts b/packages/react/src/components/ComboBox/ComboBox.classNames.ts index a0ba2aa77fcc85..489fe48aa9a008 100644 --- a/packages/react/src/components/ComboBox/ComboBox.classNames.ts +++ b/packages/react/src/components/ComboBox/ComboBox.classNames.ts @@ -73,10 +73,9 @@ export const getClassNames = memoizeFunction( export const getComboBoxOptionClassNames = memoizeFunction( (styles: Partial): IComboBoxOptionClassNames => { - // const mergeStyles = mergeStylesShadow(styles.__shadowConfig__); return { - optionText: mergeStyles('ms-ComboBox-optionText', styles.optionText), - root: mergeStyles('ms-ComboBox-option', styles.root, { + optionText: mergeStyles(styles.__shadowConfig__, 'ms-ComboBox-optionText', styles.optionText), + root: mergeStyles(styles.__shadowConfig__, 'ms-ComboBox-option', styles.root, { selectors: { ':hover': styles.rootHovered, ':focus': styles.rootFocused, diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 55b8a322452f87..0c2d7d4e92b0a9 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1391,11 +1391,15 @@ export { trProperties, unhoistMethods, useAdoptedStylesheet, + useAdoptedStylesheetEx, useCustomizationSettings, useFocusRects, useHasMergeStylesShadowRootContext, + useMergeStylesHooks, useMergeStylesRootStylesheets, useMergeStylesShadowRootContext, + useShadowConfig, + useStyled, values, videoProperties, warn, diff --git a/packages/style-utilities/etc/style-utilities.api.md b/packages/style-utilities/etc/style-utilities.api.md index b5085f611915ec..0dbfe7e3742915 100644 --- a/packages/style-utilities/etc/style-utilities.api.md +++ b/packages/style-utilities/etc/style-utilities.api.md @@ -43,6 +43,7 @@ import { keyframes } from '@fluentui/merge-styles'; import { mergeStyles } from '@fluentui/merge-styles'; import { mergeStyleSets } from '@fluentui/merge-styles'; import { registerDefaultFontFaces } from '@fluentui/theme'; +import { ShadowConfig } from '@fluentui/merge-styles'; import { Stylesheet } from '@fluentui/merge-styles'; // @public (undocumented) @@ -327,6 +328,8 @@ export const ScreenWidthMinXXXLarge = 1920; // @public export function setIconOptions(options: Partial): void; +export { ShadowConfig } + export { Stylesheet } // @public (undocumented) diff --git a/packages/style-utilities/src/MergeStyles.ts b/packages/style-utilities/src/MergeStyles.ts index dd9313fd4617cc..056b57eb7a2ff3 100644 --- a/packages/style-utilities/src/MergeStyles.ts +++ b/packages/style-utilities/src/MergeStyles.ts @@ -17,4 +17,5 @@ export type { IProcessedStyleSet, IStyleSheetConfig, ICSPSettings, + ShadowConfig, } from '@fluentui/merge-styles'; diff --git a/packages/style-utilities/src/index.ts b/packages/style-utilities/src/index.ts index beb2135679498c..0681adae1764d6 100644 --- a/packages/style-utilities/src/index.ts +++ b/packages/style-utilities/src/index.ts @@ -1,9 +1,110 @@ -export * from './classNames/index'; -export * from './styles/index'; -export * from './utilities/index'; -export * from './interfaces/index'; -export * from './MergeStyles'; -export * from './cdn'; +export { AnimationClassNames, FontClassNames, ColorClassNames } from './classNames/index'; + +export { + AnimationStyles, + AnimationVariables, + DefaultPalette, + DefaultEffects, + DefaultFontStyles, + registerDefaultFontFaces, + FontSizes, + FontWeights, + IconFontSizes, + createFontStyles, + hiddenContentStyle, + PulsingBeaconAnimationStyles, + getGlobalClassNames, + // eslint-disable-next-line deprecation/deprecation + getFocusStyle, + getFocusOutlineStyle, + getInputFocusStyle, + getThemedContext, + focusClear, + ThemeSettingName, + getTheme, + loadTheme, + createTheme, + registerOnThemeChangeCallback, + removeOnThemeChangeCallback, + HighContrastSelector, + HighContrastSelectorWhite, + HighContrastSelectorBlack, + // eslint-disable-next-line deprecation/deprecation + EdgeChromiumHighContrastSelector, + ScreenWidthMinSmall, + ScreenWidthMinMedium, + ScreenWidthMinLarge, + ScreenWidthMinXLarge, + ScreenWidthMinXXLarge, + ScreenWidthMinXXXLarge, + ScreenWidthMaxSmall, + ScreenWidthMaxMedium, + ScreenWidthMaxLarge, + ScreenWidthMaxXLarge, + ScreenWidthMaxXXLarge, + ScreenWidthMinUhfMobile, + getScreenSelector, + getHighContrastNoAdjustStyle, + // eslint-disable-next-line deprecation/deprecation + getEdgeChromiumNoHighContrastAdjustSelector, + normalize, + noWrap, + getFadedOverflowStyle, + getPlaceholderStyles, + ZIndexes, +} from './styles/index'; +export type { GlobalClassNames } from './styles/index'; + +export { + buildClassMap, + getIcon, + registerIcons, + registerIconAlias, + unregisterIcons, + setIconOptions, + getIconClassName, +} from './utilities/index'; +export type { IIconRecord, IIconSubset, IIconSubsetRecord, IIconOptions } from './utilities/index'; + +export type { + IAnimationStyles, + IAnimationVariables, + IGetFocusStylesOptions, + IEffects, + IFontStyles, + IPalette, + ISemanticColors, + ISemanticTextColors, + ISpacing, + ITheme, + IPartialTheme, + IScheme, + ISchemeNames, +} from './interfaces/index'; + +export { + InjectionMode, + Stylesheet, + concatStyleSets, + concatStyleSetsWithProps, + fontFace, + keyframes, + mergeStyleSets, + mergeStyles, +} from './MergeStyles'; +export type { + IFontFace, + IFontWeight, + IRawStyle, + IStyle, + IStyleSet, + IProcessedStyleSet, + IStyleSheetConfig, + ICSPSettings, + ShadowConfig, +} from './MergeStyles'; + +export { FLUENT_CDN_BASE_URL } from './cdn'; import './version'; diff --git a/packages/utilities/etc/utilities.api.md b/packages/utilities/etc/utilities.api.md index e0eef857a2f779..0e50a8f1d88ded 100644 --- a/packages/utilities/etc/utilities.api.md +++ b/packages/utilities/etc/utilities.api.md @@ -33,6 +33,12 @@ export function addDirectionalKeyCode(which: number): void; // @public export function addElementAtIndex(array: T[], index: number, itemToAdd: T): T[]; +// @public (undocumented) +export type AdoptedStylesheetExHook = (stylesheetKey: string, shadowCtx: MergeStylesShadowRootContextValue | undefined, rootMergeStyles: Map, win: Window | undefined) => boolean; + +// @public (undocumented) +export type AdoptedStylesheetHook = (stylesheetKey: string) => boolean; + // @public export const allowOverscrollOnElement: (element: HTMLElement | null, events: EventGroup) => void; @@ -421,6 +427,9 @@ export class GlobalSettings { // @public export function hasHorizontalOverflow(element: HTMLElement): boolean; +// @public (undocumented) +export type HasMergeStylesShadowRootContextHook = () => boolean; + // @public export function hasOverflow(element: HTMLElement): boolean; @@ -1022,21 +1031,59 @@ export function mergeScopedSettings(oldSettings?: ISettings, newSettings?: ISett // @public export function mergeSettings(oldSettings?: ISettings, newSettings?: ISettings | ISettingsFunction): ISettings; -// Warning: (ae-forgotten-export) The symbol "MergeStylesRootProviderProps" needs to be exported by the entry point index.d.ts -// +// @public (undocumented) +export type MergeStylesContextConsumerProps = { + stylesheetKey: string; + children: (inShadow: boolean) => React_2.ReactElement; +}; + +// @public (undocumented) +export type MergeStylesRootContextValue = { + stylesheets: Map; + useAdoptedStylesheetEx: AdoptedStylesheetExHook; + useAdoptedStylesheet: AdoptedStylesheetHook; + useShadowConfig: ShadowConfigHook; + useMergeStylesShadowRootContext: MergeStylesShadowRootContetHook; + useHasMergeStylesShadowRootContext: HasMergeStylesShadowRootContextHook; + useMergeStylesRootStylesheets: MergeStylesRootStylesheetsHook; + useWindow: UseWindowHook; + useStyled: UseStyledHook; +}; + // @public export const MergeStylesRootProvider: React_2.FC; -// Warning: (ae-forgotten-export) The symbol "MergeStylesShadowRootContextValue" needs to be exported by the entry point index.d.ts -// +// @public (undocumented) +export type MergeStylesRootProviderProps = { + stylesheets?: Map; + window?: Window; + useAdoptedStylesheetEx?: AdoptedStylesheetExHook; + useAdoptedStylesheet?: AdoptedStylesheetHook; + useShadowConfig?: ShadowConfigHook; + useMergeStylesShadowRootContext?: MergeStylesShadowRootContetHook; + useHasMergeStylesShadowRootContext?: HasMergeStylesShadowRootContextHook; + useMergeStylesRootStylesheets?: MergeStylesRootStylesheetsHook; + useWindow?: UseWindowHook; + useStyled?: UseStyledHook; +}; + // @public (undocumented) export const MergeStylesShadowRootContext: React_2.Context; -// Warning: (ae-forgotten-export) The symbol "MergeStylesShadowRootProviderProps" needs to be exported by the entry point index.d.ts -// +// @public (undocumented) +export type MergeStylesShadowRootContextValue = { + stylesheets: Map; + shadowRoot?: ShadowRoot | null; +}; + // @public export const MergeStylesShadowRootProvider: React_2.FC; +// @public (undocumented) +export type MergeStylesShadowRootProviderProps = { + shadowRoot?: ShadowRoot | null; +}; + // @public export function modalize(target: HTMLElement): () => void; @@ -1247,6 +1294,9 @@ export { setVirtualParent } // @public export function setWarningCallback(warningCallback?: (message: string) => void): void; +// @public (undocumented) +export type ShadowConfigHook = (stylesheetKey: string, inShadow: boolean, win?: Window) => ShadowConfig; + // @public export function shallowCompare(a: TA, b: TB): boolean; @@ -1288,7 +1338,10 @@ export const trProperties: Record; export function unhoistMethods(source: any, methodNames: string[]): void; // @public -export const useAdoptedStylesheet: (stylesheetKey: string) => boolean; +export const useAdoptedStylesheet: AdoptedStylesheetHook; + +// @public +export const useAdoptedStylesheetEx: AdoptedStylesheetExHook; // @public export function useCustomizationSettings(properties: string[], scopeName?: string): ISettings; @@ -1297,19 +1350,37 @@ export function useCustomizationSettings(properties: string[], scopeName?: strin export function useFocusRects(rootRef?: React_2.RefObject): void; // @public -export const useHasMergeStylesShadowRootContext: () => boolean; +export const useHasMergeStylesShadowRootContext: HasMergeStylesShadowRootContextHook; // @public export const useIsomorphicLayoutEffect: typeof React_2.useEffect; +// @public (undocumented) +export const useMergeStylesHooks: () => { + useAdoptedStylesheet: AdoptedStylesheetHook; + useAdoptedStylesheetEx: AdoptedStylesheetExHook; + useShadowConfig: ShadowConfigHook; + useMergeStylesShadowRootContext: MergeStylesShadowRootContetHook; + useHasMergeStylesShadowRootContext: HasMergeStylesShadowRootContextHook; + useMergeStylesRootStylesheets: MergeStylesRootStylesheetsHook; + useWindow: () => Window | undefined; + useStyled: UseStyledHook; +}; + // @public -export const useMergeStylesRootStylesheets: () => Map; +export const useMergeStylesRootStylesheets: MergeStylesRootStylesheetsHook; // @public -export const useMergeStylesShadowRootContext: () => MergeStylesShadowRootContextValue | undefined; +export const useMergeStylesShadowRootContext: MergeStylesShadowRootContetHook; // @public -export const useShadowConfig: (stylesheetKey: string, win?: Window) => ShadowConfig; +export const useShadowConfig: ShadowConfigHook; + +// @public (undocumented) +export const useStyled: UseStyledHook; + +// @public (undocumented) +export type UseStyledHook = (scope: string) => ShadowConfig | undefined; // @public export function values(obj: any): T[]; @@ -1332,6 +1403,12 @@ export function warnDeprecations

(componentName: string, props: P, deprecation // @public export function warnMutuallyExclusive

(componentName: string, props: P, exclusiveMap: ISettingsMap

): void; +// Warnings were encountered during analysis: +// +// lib/shadowDom/contexts/MergeStylesRootContext.d.ts:23:5 - (ae-forgotten-export) The symbol "MergeStylesShadowRootContetHook" needs to be exported by the entry point index.d.ts +// lib/shadowDom/contexts/MergeStylesRootContext.d.ts:25:5 - (ae-forgotten-export) The symbol "MergeStylesRootStylesheetsHook" needs to be exported by the entry point index.d.ts +// lib/shadowDom/contexts/MergeStylesRootContext.d.ts:26:5 - (ae-forgotten-export) The symbol "UseWindowHook" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/utilities/src/classNamesFunction.ts b/packages/utilities/src/classNamesFunction.ts index 34921c6c79dc70..37c9bd8e5740ba 100644 --- a/packages/utilities/src/classNamesFunction.ts +++ b/packages/utilities/src/classNamesFunction.ts @@ -92,7 +92,7 @@ export function classNamesFunction).__shadowConfig__ : undefined; - const key = shadowConfig?.window ?? '__default__'; + const key = shadowConfig && shadowConfig.window ? shadowConfig.window : '__default__'; if (!windowMap.has(key)) { windowMap.set(key, new Map()); diff --git a/packages/utilities/src/customizations/customizable.tsx b/packages/utilities/src/customizations/customizable.tsx index 1cda1face3ac45..c68c2a8abf37c3 100644 --- a/packages/utilities/src/customizations/customizable.tsx +++ b/packages/utilities/src/customizations/customizable.tsx @@ -3,7 +3,7 @@ import { Customizations } from './Customizations'; import { hoistStatics } from '../hoistStatics'; import { CustomizerContext } from './CustomizerContext'; import { concatStyleSets, makeShadowConfig } from '@fluentui/merge-styles'; -import { MergeStylesShadowRootConsumer } from '../shadowDom/MergeStylesShadowRootContext'; +import { MergeStylesShadowRootConsumer } from '../shadowDom/contexts/MergeStylesShadowRootConsumer'; import { getWindow } from '../dom/getWindow'; import { WindowContext } from '@fluentui/react-window-provider'; import type { ICustomizerContext } from './CustomizerContext'; diff --git a/packages/utilities/src/index.ts b/packages/utilities/src/index.ts index cdc08863809e29..3c61f5082b218f 100644 --- a/packages/utilities/src/index.ts +++ b/packages/utilities/src/index.ts @@ -239,12 +239,28 @@ import './version'; export type { IStyleFunctionOrObject, Omit } from '@fluentui/merge-styles'; export { + MergeStylesRootProvider, MergeStylesShadowRootContext, MergeStylesShadowRootProvider, useAdoptedStylesheet, + useAdoptedStylesheetEx, useHasMergeStylesShadowRootContext, + useMergeStylesHooks, + useMergeStylesRootStylesheets, useMergeStylesShadowRootContext, useShadowConfig, -} from './shadowDom/MergeStylesShadowRootContext'; + useStyled, +} from './shadowDom'; -export { MergeStylesRootProvider, useMergeStylesRootStylesheets } from './shadowDom/MergeStylesRootContext'; +export type { + AdoptedStylesheetHook, + AdoptedStylesheetExHook, + HasMergeStylesShadowRootContextHook, + MergeStylesRootContextValue, + MergeStylesRootProviderProps, + MergeStylesShadowRootContextValue, + MergeStylesShadowRootProviderProps, + MergeStylesContextConsumerProps, + ShadowConfigHook, + UseStyledHook, +} from './shadowDom'; diff --git a/packages/utilities/src/shadowDom/MergeStylesRootContext.tsx b/packages/utilities/src/shadowDom/MergeStylesRootContext.tsx deleted file mode 100644 index 7a8800963b67d7..00000000000000 --- a/packages/utilities/src/shadowDom/MergeStylesRootContext.tsx +++ /dev/null @@ -1,116 +0,0 @@ -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 - interface DocumentOrShadowRoot { - /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/adoptedStyleSheets) */ - adoptedStyleSheets: CSSStyleSheet[]; - } -} - -export type MergeStylesRootContextValue = { - /** - * Map of stylesheets available in the context. - */ - stylesheets: Map; -}; - -const MergeStylesRootContext = React.createContext({ - stylesheets: new Map(), -}); - -export type MergeStylesRootProviderProps = { - /** - * Map of stylesheets available in the context. - */ - stylesheets?: Map; - - /** - * Optional `window` object to use for reading adopted stylesheets. - * Useful for multi-window scenarios. - */ - window?: Window; -}; - -/** - * Root context provider for mergeStyles shadow DOM. - * Typically this is placed at the render root of your React application. - */ -export const MergeStylesRootProvider: React.FC = ({ - stylesheets: userSheets, - window: userWindow, - ...props -}) => { - const win = userWindow ?? getWindow(); - const [stylesheets, setStylesheets] = React.useState>( - () => userSheets ?? new Map(), - ); - - const sheetHandler = React.useCallback(({ key, sheet }) => { - setStylesheets(prev => { - const next = new Map(prev); - next.set(key, sheet); - return next; - }); - }, []); - - // Udapte stylesheets based on user style sheet changes - React.useEffect(() => { - setStylesheets(userSheets ?? new Map()); - }, [userSheets]); - - // Wire up listener for adopted stylesheets - React.useEffect(() => { - if (!win) { - return; - } - - const sheet = Stylesheet.getInstance(makeShadowConfig(GLOBAL_STYLESHEET_KEY, false, win)); - - sheet.onAddConstructableStyleSheet(sheetHandler); - - return () => { - sheet.offAddConstructableStyleSheet(sheetHandler); - }; - }, [win, sheetHandler]); - - // Read stylesheets from window on mount - React.useEffect(() => { - if (!win) { - return; - } - - let changed = false; - const next = new Map(stylesheets); - const sheet = Stylesheet.getInstance(makeShadowConfig(GLOBAL_STYLESHEET_KEY, false, win)); - - sheet.forEachAdoptedStyleSheet((adoptedSheet, key) => { - next.set(key, adoptedSheet); - changed = true; - }); - - if (changed) { - setStylesheets(next); - } - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const value = React.useMemo(() => { - return { - stylesheets, - }; - }, [stylesheets]); - - return ; -}; - -/** - * Get the map of stylesheets available in the context. - */ -export const useMergeStylesRootStylesheets = () => { - return React.useContext(MergeStylesRootContext).stylesheets; -}; diff --git a/packages/utilities/src/shadowDom/contexts/MergeStylesRootContext.tsx b/packages/utilities/src/shadowDom/contexts/MergeStylesRootContext.tsx new file mode 100644 index 00000000000000..98f007e4675c5c --- /dev/null +++ b/packages/utilities/src/shadowDom/contexts/MergeStylesRootContext.tsx @@ -0,0 +1,181 @@ +import * as React from 'react'; +import { + GLOBAL_STYLESHEET_KEY, + ShadowDomStylesheet, + makeShadowConfig, + DEFAULT_SHADOW_CONFIG, +} from '@fluentui/merge-styles'; +import { getWindow } from '../../dom'; +import type { ExtendedCSSStyleSheet } from '@fluentui/merge-styles'; +import type { AdoptedStylesheetExHook, AdoptedStylesheetHook } from '../hooks/useAdoptedStylesheet'; +import type { ShadowConfigHook } from '../hooks/useShadowConfig'; +import type { + HasMergeStylesShadowRootContextHook, + MergeStylesShadowRootContetHook, +} from '../hooks/useMergeStylesShadowRoot'; +import type { MergeStylesRootStylesheetsHook } from '../hooks/useMergeStylesRootStylesheets'; +import type { UseStyledHook } from '../hooks/useStyled'; + +declare global { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface DocumentOrShadowRoot { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/adoptedStyleSheets) */ + adoptedStyleSheets: CSSStyleSheet[]; + } +} + +type UseWindowHook = () => Window | undefined; + +const noop = () => false; +const noopShadow = () => DEFAULT_SHADOW_CONFIG; +const noopRootStylesheets = () => new Map(); +const noopUndefined = () => undefined; + +export type MergeStylesRootContextValue = { + /** + * Map of stylesheets available in the context. + */ + stylesheets: Map; + useAdoptedStylesheetEx: AdoptedStylesheetExHook; + useAdoptedStylesheet: AdoptedStylesheetHook; + useShadowConfig: ShadowConfigHook; + useMergeStylesShadowRootContext: MergeStylesShadowRootContetHook; + useHasMergeStylesShadowRootContext: HasMergeStylesShadowRootContextHook; + useMergeStylesRootStylesheets: MergeStylesRootStylesheetsHook; + useWindow: UseWindowHook; + useStyled: UseStyledHook; +}; + +export const MergeStylesRootContext = React.createContext({ + stylesheets: new Map(), + useAdoptedStylesheetEx: noop, + useAdoptedStylesheet: noop, + useShadowConfig: noopShadow, + useMergeStylesShadowRootContext: noopUndefined, + useHasMergeStylesShadowRootContext: noop, + useMergeStylesRootStylesheets: noopRootStylesheets, + useWindow: noopUndefined, + useStyled: noopUndefined, +}); + +export type MergeStylesRootProviderProps = { + /** + * Map of stylesheets available in the context. + */ + stylesheets?: Map; + + /** + * Optional `window` object to use for reading adopted stylesheets. + * Useful for multi-window scenarios. + */ + window?: Window; + + useAdoptedStylesheetEx?: AdoptedStylesheetExHook; + useAdoptedStylesheet?: AdoptedStylesheetHook; + useShadowConfig?: ShadowConfigHook; + useMergeStylesShadowRootContext?: MergeStylesShadowRootContetHook; + useHasMergeStylesShadowRootContext?: HasMergeStylesShadowRootContextHook; + useMergeStylesRootStylesheets?: MergeStylesRootStylesheetsHook; + useWindow?: UseWindowHook; + useStyled?: UseStyledHook; +}; + +/** + * Root context provider for mergeStyles shadow DOM. + * Typically this is placed at the render root of your React application. + */ +export const MergeStylesRootProvider: React.FC = ({ + stylesheets: userSheets, + window: userWindow, + useAdoptedStylesheet, + useAdoptedStylesheetEx, + useShadowConfig, + useMergeStylesShadowRootContext, + useHasMergeStylesShadowRootContext, + useMergeStylesRootStylesheets, + useWindow, + useStyled, + ...props +}) => { + const win = userWindow ?? getWindow(); + const [stylesheets, setStylesheets] = React.useState>( + () => userSheets || new Map(), + ); + + const sheetHandler = React.useCallback(({ key, sheet }) => { + setStylesheets(prev => { + const next = new Map(prev); + next.set(key, sheet); + return next; + }); + }, []); + + // Udapte stylesheets based on user style sheet changes + React.useEffect(() => { + setStylesheets(userSheets || new Map()); + }, [userSheets]); + + // Wire up listener for adopted stylesheets + React.useEffect(() => { + if (!win) { + return; + } + + const sheet = ShadowDomStylesheet.getInstance(makeShadowConfig(GLOBAL_STYLESHEET_KEY, false, win)); + const off = sheet.onAddSheet(sheetHandler); + + return () => { + off(); + }; + }, [win, sheetHandler]); + + // Read stylesheets from window on mount + React.useEffect(() => { + if (!win) { + return; + } + + let changed = false; + const next = new Map(stylesheets); + const sheet = ShadowDomStylesheet.getInstance(makeShadowConfig(GLOBAL_STYLESHEET_KEY, false, win)); + + const adoptedSheets = sheet.getAdoptedSheets(); + + adoptedSheets.forEach((adoptedSheet, key) => { + next.set(key, adoptedSheet); + changed = true; + }); + + if (changed) { + setStylesheets(next); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const value = React.useMemo(() => { + return { + stylesheets, + useAdoptedStylesheet: useAdoptedStylesheet || noop, + useAdoptedStylesheetEx: useAdoptedStylesheetEx || noop, + useShadowConfig: useShadowConfig || noopShadow, + useMergeStylesShadowRootContext: useMergeStylesShadowRootContext || noopUndefined, + useHasMergeStylesShadowRootContext: useHasMergeStylesShadowRootContext || noop, + useMergeStylesRootStylesheets: useMergeStylesRootStylesheets || noopRootStylesheets, + useWindow: useWindow || noopUndefined, + useStyled: useStyled || noopUndefined, + }; + }, [ + stylesheets, + useAdoptedStylesheet, + useAdoptedStylesheetEx, + useShadowConfig, + useMergeStylesShadowRootContext, + useHasMergeStylesShadowRootContext, + useMergeStylesRootStylesheets, + useWindow, + useStyled, + ]); + + return ; +}; diff --git a/packages/utilities/src/shadowDom/contexts/MergeStylesShadowRootConsumer.ts b/packages/utilities/src/shadowDom/contexts/MergeStylesShadowRootConsumer.ts new file mode 100644 index 00000000000000..e0aadcf9fdb811 --- /dev/null +++ b/packages/utilities/src/shadowDom/contexts/MergeStylesShadowRootConsumer.ts @@ -0,0 +1,25 @@ +import type * as React from 'react'; +import { GLOBAL_STYLESHEET_KEY } from '@fluentui/merge-styles'; +import { useMergeStylesHooks } from '../hooks/useMergeStylesHooks'; +import { useMergeStylesShadowRootContext } from '../hooks/useMergeStylesShadowRoot'; + +export type MergeStylesContextConsumerProps = { + stylesheetKey: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + children: (inShadow: boolean) => React.ReactElement; +}; + +export const MergeStylesShadowRootConsumer: React.FC = ({ + stylesheetKey, + children, +}) => { + const { useAdoptedStylesheetEx, useMergeStylesRootStylesheets, useWindow } = useMergeStylesHooks(); + const shadowCtx = useMergeStylesShadowRootContext(); + const rootMergeStyles = useMergeStylesRootStylesheets(); + const win = useWindow(); + + useAdoptedStylesheetEx(GLOBAL_STYLESHEET_KEY, shadowCtx, rootMergeStyles, win); + useAdoptedStylesheetEx(stylesheetKey, shadowCtx, rootMergeStyles, win); + + return children(!!shadowCtx); +}; diff --git a/packages/utilities/src/shadowDom/contexts/MergeStylesShadowRootContext.tsx b/packages/utilities/src/shadowDom/contexts/MergeStylesShadowRootContext.tsx new file mode 100644 index 00000000000000..67057bc539f33a --- /dev/null +++ b/packages/utilities/src/shadowDom/contexts/MergeStylesShadowRootContext.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import { GLOBAL_STYLESHEET_KEY } from '@fluentui/merge-styles'; +import { useMergeStylesHooks } from '../hooks/useMergeStylesHooks'; + +export type MergeStylesShadowRootContextValue = { + /** + * Map of stylesheets available in the context. + */ + stylesheets: Map; + + /** + * Shadow root for this context. + */ + shadowRoot?: ShadowRoot | null; +}; + +export const MergeStylesShadowRootContext = React.createContext( + undefined, +); + +export type MergeStylesShadowRootProviderProps = { + /** + * Shadow root for this context. + */ + shadowRoot?: ShadowRoot | null; +}; + +/** + * Context for a shadow root. + */ +export const MergeStylesShadowRootProvider: React.FC = ({ + shadowRoot, + ...props +}) => { + const value = React.useMemo(() => { + return { + stylesheets: new Map(), + shadowRoot, + }; + }, [shadowRoot]); + + return ( + + + {props.children} + + ); +}; + +const GlobalStyles: React.FC = props => { + const { useAdoptedStylesheet } = useMergeStylesHooks(); + useAdoptedStylesheet(GLOBAL_STYLESHEET_KEY); + return null; +}; diff --git a/packages/utilities/src/shadowDom/MergeStylesShadowRootContext.tsx b/packages/utilities/src/shadowDom/hooks/useAdoptedStylesheet.ts similarity index 54% rename from packages/utilities/src/shadowDom/MergeStylesShadowRootContext.tsx rename to packages/utilities/src/shadowDom/hooks/useAdoptedStylesheet.ts index c4941a5dc13cae..dcb5f180b3ebf4 100644 --- a/packages/utilities/src/shadowDom/MergeStylesShadowRootContext.tsx +++ b/packages/utilities/src/shadowDom/hooks/useAdoptedStylesheet.ts @@ -1,93 +1,31 @@ import * as React from 'react'; import { - GLOBAL_STYLESHEET_KEY, SUPPORTS_CONSTRUCTABLE_STYLESHEETS, SUPPORTS_MODIFYING_ADOPTED_STYLESHEETS, Stylesheet, makeShadowConfig, cloneCSSStyleSheet, - EventHandler, } from '@fluentui/merge-styles'; -import { useMergeStylesRootStylesheets } from './MergeStylesRootContext'; import { useWindow } from '@fluentui/react-window-provider'; -import type { ExtendedCSSStyleSheet } from '@fluentui/merge-styles'; +import { useMergeStylesRootStylesheets } from './useMergeStylesRootStylesheets'; +import { useMergeStylesShadowRootContext } from './useMergeStylesShadowRoot'; +import type { ExtendedCSSStyleSheet, InsertRuleCallback } from '@fluentui/merge-styles'; +import type { MergeStylesShadowRootContextValue } from '../contexts/MergeStylesShadowRootContext'; -type PolyfileInsertListeners = Record>; +type PolyfillInsertListeners = Record; -export type MergeStylesShadowRootContextValue = { - /** - * Map of stylesheets available in the context. - */ - stylesheets: Map; - - /** - * Shadow root for this context. - */ - shadowRoot?: ShadowRoot | null; -}; - -export const MergeStylesShadowRootContext = React.createContext( - undefined, -); - -export type MergeStylesShadowRootProviderProps = { - /** - * Shadow root for this context. - */ - shadowRoot?: ShadowRoot | null; -}; - -/** - * Context for a shadow root. - */ -export const MergeStylesShadowRootProvider: React.FC = ({ - shadowRoot, - ...props -}) => { - const value = React.useMemo(() => { - return { - stylesheets: new Map(), - shadowRoot, - }; - }, [shadowRoot]); - - return ( - - - {props.children} - - ); -}; - -export type MergeStylesContextConsumerProps = { - stylesheetKey: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - children: (inShadow: boolean) => React.ReactElement; -}; - -export const MergeStylesShadowRootConsumer: React.FC = ({ - stylesheetKey, - children, -}) => { - const shadowCtx = useMergeStylesShadowRootContext(); - const rootMergeStyles = useMergeStylesRootStylesheets(); - const win = useWindow(); - - useAdoptedStylesheetEx(GLOBAL_STYLESHEET_KEY, shadowCtx, rootMergeStyles, win); - useAdoptedStylesheetEx(stylesheetKey, shadowCtx, rootMergeStyles, win); - - return children(!!shadowCtx); -}; - -const GlobalStyles: React.FC = props => { - useAdoptedStylesheet(GLOBAL_STYLESHEET_KEY); - return null; -}; +export type AdoptedStylesheetHook = (stylesheetKey: string) => boolean; +export type AdoptedStylesheetExHook = ( + stylesheetKey: string, + shadowCtx: MergeStylesShadowRootContextValue | undefined, + rootMergeStyles: Map, + win: Window | undefined, +) => boolean; /** * Use adopted stylesheets in the parent shadow root. */ -export const useAdoptedStylesheet = (stylesheetKey: string): boolean => { +export const useAdoptedStylesheet: AdoptedStylesheetHook = stylesheetKey => { const shadowCtx = useMergeStylesShadowRootContext(); const rootMergeStyles = useMergeStylesRootStylesheets(); const win = useWindow(); @@ -98,13 +36,8 @@ export const useAdoptedStylesheet = (stylesheetKey: string): boolean => { /** * Optimization for specific cases like nested customizables. */ -const useAdoptedStylesheetEx = ( - stylesheetKey: string, - shadowCtx: MergeStylesShadowRootContextValue | undefined, - rootMergeStyles: Map, - win: Window | undefined, -): boolean => { - const polyfillInsertListners = React.useRef({}); +export const useAdoptedStylesheetEx: AdoptedStylesheetExHook = (stylesheetKey, shadowCtx, rootMergeStyles, win) => { + const polyfillInsertListners = React.useRef({}); React.useEffect(() => { if (!shadowCtx) { @@ -114,9 +47,8 @@ const useAdoptedStylesheetEx = ( polyfillInsertListners.current = {}; return () => { - const sheet = Stylesheet.getInstance(makeShadowConfig(stylesheetKey, true, win)); Object.keys(polyfillListeners).forEach(key => { - sheet.offInsertRuleIntoConstructableStyleSheet(polyfillListeners[key]); + polyfillListeners[key](); }); }; }, [win, stylesheetKey, shadowCtx]); @@ -148,7 +80,7 @@ const adoptSheet = ( doc: Document, stylesheetKey: string, stylesheet: ExtendedCSSStyleSheet, - listenerRef: PolyfileInsertListeners, + listenerRef: PolyfillInsertListeners, ) => { const shadowRoot = shadowCtx.shadowRoot!; @@ -195,7 +127,7 @@ const adoptSheet = ( if (style.sheet) { cloneCSSStyleSheet(stylesheet, style.sheet); if (!listenerRef[stylesheetKey]) { - const onInsert: EventHandler = ({ key, rule }) => { + const onInsert: InsertRuleCallback = ({ key, rule }) => { if (key === stylesheetKey) { if (shadowCtx && rule) { updatePolyfillSheet(shadowCtx, key, rule); @@ -205,38 +137,8 @@ const adoptSheet = ( const polyfillSheet = Stylesheet.getInstance( makeShadowConfig(stylesheetKey, true, doc.defaultView ?? undefined), ); - polyfillSheet.onInsertRuleIntoConstructableStyleSheet(onInsert); - listenerRef[stylesheetKey] = onInsert; + listenerRef[stylesheetKey] = polyfillSheet.onInsertRule(onInsert); } } } }; - -/** - * Test if a context is available. - * @returns true if there is a context. - */ -export const useHasMergeStylesShadowRootContext = () => { - return !!useMergeStylesShadowRootContext(); -}; - -/** - * Get a reference to the shadow root context. - * @returns The context for the shadow root. - */ -export const useMergeStylesShadowRootContext = () => { - return React.useContext(MergeStylesShadowRootContext); -}; - -/** - * Get a shadow config. - * @param stylesheetKey - Globally unique key - * @param win - Reference to the `window` global. - * @returns ShadowConfig - */ -export const useShadowConfig = (stylesheetKey: string, win?: Window) => { - const inShadow = useHasMergeStylesShadowRootContext(); - return React.useMemo(() => { - return makeShadowConfig(stylesheetKey, inShadow, win); - }, [stylesheetKey, inShadow, win]); -}; diff --git a/packages/utilities/src/shadowDom/hooks/useMergeStylesHooks.ts b/packages/utilities/src/shadowDom/hooks/useMergeStylesHooks.ts new file mode 100644 index 00000000000000..4198d32185c724 --- /dev/null +++ b/packages/utilities/src/shadowDom/hooks/useMergeStylesHooks.ts @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { MergeStylesRootContext } from '../contexts/MergeStylesRootContext'; + +export const useMergeStylesHooks = () => { + const ctx = React.useContext(MergeStylesRootContext); + return { + useAdoptedStylesheet: ctx.useAdoptedStylesheet, + useAdoptedStylesheetEx: ctx.useAdoptedStylesheetEx, + useShadowConfig: ctx.useShadowConfig, + useMergeStylesShadowRootContext: ctx.useMergeStylesShadowRootContext, + useHasMergeStylesShadowRootContext: ctx.useHasMergeStylesShadowRootContext, + useMergeStylesRootStylesheets: ctx.useMergeStylesRootStylesheets, + useWindow: ctx.useWindow, + useStyled: ctx.useStyled, + }; +}; diff --git a/packages/utilities/src/shadowDom/hooks/useMergeStylesRootStylesheets.ts b/packages/utilities/src/shadowDom/hooks/useMergeStylesRootStylesheets.ts new file mode 100644 index 00000000000000..fa76c6e0db5826 --- /dev/null +++ b/packages/utilities/src/shadowDom/hooks/useMergeStylesRootStylesheets.ts @@ -0,0 +1,12 @@ +import * as React from 'react'; +import { MergeStylesRootContext } from '../contexts/MergeStylesRootContext'; +import type { ExtendedCSSStyleSheet } from '@fluentui/merge-styles'; + +export type MergeStylesRootStylesheetsHook = () => Map; + +/** + * Get the map of stylesheets available in the context. + */ +export const useMergeStylesRootStylesheets: MergeStylesRootStylesheetsHook = () => { + return React.useContext(MergeStylesRootContext).stylesheets; +}; diff --git a/packages/utilities/src/shadowDom/hooks/useMergeStylesShadowRoot.ts b/packages/utilities/src/shadowDom/hooks/useMergeStylesShadowRoot.ts new file mode 100644 index 00000000000000..7a2889e7143a72 --- /dev/null +++ b/packages/utilities/src/shadowDom/hooks/useMergeStylesShadowRoot.ts @@ -0,0 +1,24 @@ +import * as React from 'react'; +import { + MergeStylesShadowRootContext, + MergeStylesShadowRootContextValue, +} from '../contexts/MergeStylesShadowRootContext'; + +export type HasMergeStylesShadowRootContextHook = () => boolean; +export type MergeStylesShadowRootContetHook = () => MergeStylesShadowRootContextValue | undefined; + +/** + * Test if a context is available. + * @returns true if there is a context. + */ +export const useHasMergeStylesShadowRootContext: HasMergeStylesShadowRootContextHook = () => { + return !!useMergeStylesShadowRootContext(); +}; + +/** + * Get a reference to the shadow root context. + * @returns The context for the shadow root. + */ +export const useMergeStylesShadowRootContext: MergeStylesShadowRootContetHook = () => { + return React.useContext(MergeStylesShadowRootContext); +}; diff --git a/packages/utilities/src/shadowDom/hooks/useShadowConfig.ts b/packages/utilities/src/shadowDom/hooks/useShadowConfig.ts new file mode 100644 index 00000000000000..bf6fd5531c2d7b --- /dev/null +++ b/packages/utilities/src/shadowDom/hooks/useShadowConfig.ts @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { makeShadowConfig } from '@fluentui/merge-styles'; +import type { ShadowConfig } from '@fluentui/merge-styles'; + +export type ShadowConfigHook = (stylesheetKey: string, inShadow: boolean, win?: Window) => ShadowConfig; + +/** + * Get a shadow config. + * @param stylesheetKey - Globally unique key + * @param win - Reference to the `window` global. + * @returns ShadowConfig + */ +export const useShadowConfig: ShadowConfigHook = (stylesheetKey, inShadow = false, win?) => { + return React.useMemo(() => { + return makeShadowConfig(stylesheetKey, inShadow, win); + }, [stylesheetKey, inShadow, win]); +}; diff --git a/packages/utilities/src/shadowDom/hooks/useStyled.ts b/packages/utilities/src/shadowDom/hooks/useStyled.ts new file mode 100644 index 00000000000000..56639a983000ee --- /dev/null +++ b/packages/utilities/src/shadowDom/hooks/useStyled.ts @@ -0,0 +1,25 @@ +import { ShadowConfig } from '@fluentui/merge-styles'; +import { getWindow } from '../../dom'; +import { useMergeStylesHooks } from './useMergeStylesHooks'; + +export type UseStyledHook = (scope: string) => ShadowConfig | undefined; + +export const useStyled: UseStyledHook = (scope = '__global__') => { + const { + useAdoptedStylesheetEx, + useShadowConfig, + useMergeStylesShadowRootContext, + useMergeStylesRootStylesheets, + useWindow, + } = useMergeStylesHooks(); + + const win = useWindow() || getWindow(); + const shadowCtx = useMergeStylesShadowRootContext(); + const inShadow = !!shadowCtx; + const rootMergeStyles = useMergeStylesRootStylesheets(); + const shadowConfig = useShadowConfig(scope, inShadow, win); + + useAdoptedStylesheetEx(scope, shadowCtx, rootMergeStyles, win); + + return shadowConfig; +}; diff --git a/packages/utilities/src/shadowDom/index.ts b/packages/utilities/src/shadowDom/index.ts new file mode 100644 index 00000000000000..28fbae4ebfa4df --- /dev/null +++ b/packages/utilities/src/shadowDom/index.ts @@ -0,0 +1,26 @@ +export { MergeStylesRootProvider } from './contexts/MergeStylesRootContext'; +export type { MergeStylesRootContextValue, MergeStylesRootProviderProps } from './contexts/MergeStylesRootContext'; + +export { MergeStylesShadowRootConsumer } from './contexts/MergeStylesShadowRootConsumer'; +export type { MergeStylesContextConsumerProps } from './contexts/MergeStylesShadowRootConsumer'; + +export { MergeStylesShadowRootContext, MergeStylesShadowRootProvider } from './contexts/MergeStylesShadowRootContext'; +export type { + MergeStylesShadowRootContextValue, + MergeStylesShadowRootProviderProps, +} from './contexts/MergeStylesShadowRootContext'; + +export { useAdoptedStylesheet, useAdoptedStylesheetEx } from './hooks/useAdoptedStylesheet'; +export type { AdoptedStylesheetHook, AdoptedStylesheetExHook } from './hooks/useAdoptedStylesheet'; + +export { useMergeStylesHooks } from './hooks/useMergeStylesHooks'; +export { useMergeStylesRootStylesheets } from './hooks/useMergeStylesRootStylesheets'; + +export { useHasMergeStylesShadowRootContext, useMergeStylesShadowRootContext } from './hooks/useMergeStylesShadowRoot'; +export type { HasMergeStylesShadowRootContextHook } from './hooks/useMergeStylesShadowRoot'; + +export { useShadowConfig } from './hooks/useShadowConfig'; +export type { ShadowConfigHook } from './hooks/useShadowConfig'; + +export { useStyled } from './hooks/useStyled'; +export type { UseStyledHook } from './hooks/useStyled'; diff --git a/packages/utilities/src/styled.tsx b/packages/utilities/src/styled.tsx index 435644d110926b..abdce3783c2ee3 100644 --- a/packages/utilities/src/styled.tsx +++ b/packages/utilities/src/styled.tsx @@ -1,11 +1,8 @@ import * as React from 'react'; import { concatStyleSetsWithProps } from '@fluentui/merge-styles'; -import { useAdoptedStylesheet, useShadowConfig } from './shadowDom/MergeStylesShadowRootContext'; +import { useMergeStylesHooks } from './shadowDom/index'; import { useCustomizationSettings } from './customizations/useCustomizationSettings'; import type { IStyleSet, IStyleFunctionOrObject, ShadowConfig } from '@fluentui/merge-styles'; -import { getWindow } from './dom/getWindow'; -// eslint-disable-next-line -import { useWindow } from '@fluentui/react-window-provider'; export interface IPropsWithStyles> { styles?: IStyleFunctionOrObject; @@ -102,8 +99,7 @@ export function styled< const { styles: customizedStyles, dir, ...rest } = settings; const additionalProps = getProps ? getProps(props) : undefined; - const win = useWindow() ?? getWindow(); - const shadowConfig = useShadowConfig(scope, win); + const { useStyled } = useMergeStylesHooks(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const cache = (styles.current && (styles.current as any).__cachedInputs__) || []; @@ -129,9 +125,7 @@ export function styled< styles.current = concatenatedStyles as StyleFunction; } - styles.current.__shadowConfig__ = shadowConfig; - - useAdoptedStylesheet(scope); + styles.current.__shadowConfig__ = useStyled(scope); return ; });