@edynamix/mf-isolation 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -2
- package/dist/angular.d.ts.map +1 -1
- package/dist/angular.js +11 -9
- package/dist/angular.js.map +1 -1
- package/dist/document-queries.d.ts +11 -0
- package/dist/document-queries.d.ts.map +1 -0
- package/dist/document-queries.js +125 -0
- package/dist/document-queries.js.map +1 -0
- package/dist/document-root-selectors.d.ts +14 -0
- package/dist/document-root-selectors.d.ts.map +1 -0
- package/dist/document-root-selectors.js +124 -0
- package/dist/document-root-selectors.js.map +1 -0
- package/dist/hosted-module-styles.d.ts +17 -0
- package/dist/hosted-module-styles.d.ts.map +1 -0
- package/dist/hosted-module-styles.js +226 -0
- package/dist/hosted-module-styles.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/postcss.d.ts +1 -1
- package/dist/postcss.d.ts.map +1 -1
- package/dist/postcss.js +35 -74
- package/dist/postcss.js.map +1 -1
- package/dist/root-font-size.d.ts +3 -7
- package/dist/root-font-size.d.ts.map +1 -1
- package/dist/root-font-size.js +5 -121
- package/dist/root-font-size.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -43,7 +43,24 @@ The host and remote must use the same `scopeId`. Both PostCSS plugins keep ordin
|
|
|
43
43
|
|
|
44
44
|
`rem` always resolves against the page's `<html>`, even inside a shadow root. A module page that sized its own root (`html, body { font-size: 12px }`) drew every rem length from that size; hosted, the same lengths would use the host page's root. Both PostCSS plugins therefore record a resolvable `font-size` or `font` size on `html` or `:root` (px, %, em, rem, absolute units and size keywords, against the initial 16px) as `--edyn-mf-rem` on the module boundary, in a rule right after the one that sets it, so `@media` conditions and `!important` carry over. Sizing only `body` (or `html body`) leaves rem alone, as it did on the module's own page. A size that cannot be resolved at build time (`var()`, `calc()` with `var()`, viewport units, system fonts) is reported as a PostCSS warning; rem then keeps the host root size. CSS that never sizes the root is emitted exactly as before.
|
|
45
45
|
|
|
46
|
-
At runtime, `provideMfOverlayRoot()` and `provideMfOverlayRootFromToken()` start `followModuleRootFontSize(shadowRoot)` for the shadow root the overlay root sits in, from an environment initializer (before the first component renders) until the injector is destroyed. Once a stylesheet in that root declares `--edyn-mf-rem`, every rem length in its stylesheets, including the component styles Angular adds later,
|
|
46
|
+
At runtime, `provideMfOverlayRoot()` and `provideMfOverlayRootFromToken()` start `followModuleRootFontSize(shadowRoot)` for the shadow root the overlay root sits in, from an environment initializer (before the first component renders) until the injector is destroyed. Once a stylesheet in that root declares `--edyn-mf-rem`, every rem length becomes `calc(<n> * var(--edyn-mf-rem, 1rem))`: in its stylesheets, including the component styles Angular adds later, and in inline `style` attributes, including ones inserted or changed later by templates, bindings or code. Both go through the CSSOM (`rule.style` and `element.style`), so `@media`, `@container` and `@supports` conditions, strings and `url()` keep their text, `!important` stays, and the rewrite is idempotent. Inline styles without an unfollowed rem length (Material's ripples and overlay positions) are skipped on a single attribute test, and a module whose CSS never sizes its root pays nothing: no inline observer is started. Rules added with `insertRule()` to an already rewritten sheet, and styles inside nested shadow roots, are not rewritten.
|
|
47
|
+
|
|
48
|
+
### Hosted module styles at runtime
|
|
49
|
+
|
|
50
|
+
A host calls `adaptHostedModuleStyles(shadowRoot)` from `@edynamix/mf-isolation` on each module shadow root it creates, and the function it returns on unmount. It adapts what a module brings into that root at runtime, with no module rebuild:
|
|
51
|
+
|
|
52
|
+
- rem follows the module's own root font size, as `followModuleRootFontSize()` does above;
|
|
53
|
+
- rules a library injects at runtime for the document root never match inside a shadow root (Angular Material's `html { --mat-focus-indicator-display: block; }` left keyboard focus rings off), so they move onto the shadow host, as the PostCSS plugins do at build time: `html`, `body`, `:root` and `html body` become `:host`, and `html .x`, `body > .x` or `:root .x` become `:host .x` and `:host > .x`. A root with conditions of its own (`html.dark`, `body#app`, `:root[dir=rtl]`) is left alone, as is any rule that already names `:host` or `data-edyn-mf`. `rescopeDocumentRootSelectors(selectorText)` is the pure selector step and also reports how many qualified roots it left.
|
|
54
|
+
|
|
55
|
+
It is safe beside a module's own `followModuleRootFontSize()` on the same root (0.0.31 or later): stylesheet changes go through the CSSOM, which no mutation observer sees, every step is idempotent, and an inline style already followed never looks like work again, so neither side acts on the other's writes.
|
|
56
|
+
|
|
57
|
+
### Document queries from hosted modules
|
|
58
|
+
|
|
59
|
+
A module that found its own elements through the global document (`document.querySelector('.week-wrapper')`, `this.document.getElementById('notes-tab-3')` through Angular's `DOCUMENT`) found them on its own page; hosted, they live in its shadow root, which document queries never enter, so the same calls return `null` or nothing. A host calls `scopeDocumentQueries(shadowRoot)` from `@edynamix/mf-isolation` next to `adaptHostedModuleStyles()` on each module shadow root it creates, and the function it returns on unmount.
|
|
60
|
+
|
|
61
|
+
While a root is registered, `querySelector`, `querySelectorAll`, `getElementById` and `getElementsByClassName` on that root's document first run as normal and keep their result whenever it finds anything. Only an empty result falls back to the registered roots, newest first (during a module switch the incoming module answers before the outgoing one), and the first root with a match answers. Calls on any other document, such as an iframe's or one from `createHTMLDocument()`, are untouched. The document prototype is patched once for any number of roots, shared with other copies of this package on the page, and restored when the last root stops; a method someone else wrapped after us keeps their wrapper, and ours inside it passes calls through.
|
|
62
|
+
|
|
63
|
+
The trade-offs: fallback results of `querySelectorAll` and `getElementsByClassName` are static `NodeList`s, not live collections, so a module that holds one and expects it to update must query again. A document match always wins, so a module asking for a class or id the host page also uses gets the host's element, as it would have before. Code anywhere on the page that relies on `null` for a selector one of the modules matches now gets the module's element. `getElementsByTagName` is not patched: tag queries nearly always match the host's own elements, so a fallback would rarely help and could hand a module's `<link>` or `<style>` to library code checking the page.
|
|
47
64
|
|
|
48
65
|
### Angular with Vite
|
|
49
66
|
|
|
@@ -62,7 +79,7 @@ export default defineConfig({
|
|
|
62
79
|
});
|
|
63
80
|
```
|
|
64
81
|
|
|
65
|
-
The Angular-aware plugin
|
|
82
|
+
The Angular-aware plugin leaves Emulated `.component.css`, `.component.scss`, `.component.sass` and `.component.less` files as written (only `@keyframes` names get the module prefix), so Angular compiles exactly what it compiled on the module's own page: every component rule keeps its weight (`mat-form-field[_ngcontent-x]` stays (0,1,1) and still loses to a global (0,2,0) rule), template content rendered in an overlay outside the component's host keeps its component styles, and a top-level `::ng-deep` rule stays global to the module. Angular's `_ngcontent` and `_nghost` attributes keep component rules to their component, and a hosted module's component styles live in its own shadow root, so no boundary is needed. Components declaring `ViewEncapsulation.None` use the global selector behavior described above, since Angular leaves their CSS global.
|
|
66
83
|
|
|
67
84
|
Run this plugin through Vite/PostCSS before Angular AOT compilation. Do not combine it with `scopeRemoteCss` in the same Vite configuration.
|
|
68
85
|
|
package/dist/angular.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"angular.d.ts","sourceRoot":"","sources":["../src/angular.ts"],"names":[],"mappings":"AAUA,OAAO,EAAuC,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AA6T7G,eAAO,MAAM,oBAAoB,GAAI,cAAc,WAAW,KAAG,cAAc,EAwB9E,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,kBAAkB,aAAa,CAAC,WAAW,CAAC,KAAG,cAAc,EAE1G,CAAC;AAEF,OAAO,EAAE,uCAAuC,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["import {\n FlexibleConnectedPositionStrategy,\n type FlexibleConnectedPositionStrategyOrigin,\n GlobalPositionStrategy,\n OverlayContainer,\n OverlayPositionBuilder,\n} from '@angular/cdk/overlay';\nimport { Platform } from '@angular/cdk/platform';\nimport { ViewportRuler } from '@angular/cdk/scrolling';\nimport { DOCUMENT } from '@angular/common';\nimport { DestroyRef, ENVIRONMENT_INITIALIZER, type ProviderToken, type StaticProvider } from '@angular/core';\nimport { followModuleRootFontSize } from './root-font-size.js';\n\n// Mirrors the CDK 19 overlay-prebuilt.css structural rules while intentionally excluding document-level scroll blocking.\nconst CDK_OVERLAY_STRUCTURAL_CSS = `\n.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}\n.cdk-overlay-container{position:fixed;z-index:1000}\n.cdk-overlay-container:empty{display:none}\n.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}\n.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%;z-index:1000}\n.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation;z-index:1000;transition:opacity 400ms cubic-bezier(0.25,0.8,0.25,1)}\n@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}\n.cdk-overlay-backdrop-showing{opacity:1}\n@media(forced-colors:active){.cdk-overlay-backdrop-showing{opacity:.6}}\n.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}\n.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}\n.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}\n.cdk-overlay-backdrop-noop-animation{transition:none}\n.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px;z-index:1000}\n`;\n\ninterface IClientRect {\n bottom: number;\n height: number;\n left: number;\n right: number;\n top: number;\n width: number;\n x?: number;\n y?: number;\n}\n\ninterface IFlexibleConnectedPositionStrategyInternals {\n _getOriginPoint: (originRect: IClientRect, containerRect: IClientRect, position: unknown) => { x: number; y: number };\n _getOriginRect: () => IClientRect;\n}\n\nconst translateRectToBoundary = (rect: IClientRect, boundary: IClientRect): IClientRect => {\n const left = rect.left - boundary.left;\n const top = rect.top - boundary.top;\n\n return {\n bottom: top + rect.height,\n height: rect.height,\n left,\n right: left + rect.width,\n top,\n width: rect.width,\n x: left,\n y: top,\n };\n};\n\nconst getBoundaryRect = (overlayRoot: HTMLElement): IClientRect => {\n const { height, width } = overlayRoot.getBoundingClientRect();\n return {\n bottom: height,\n height,\n left: 0,\n right: width,\n top: 0,\n width,\n x: 0,\n y: 0,\n };\n};\n\nconst createBoundaryDocument = (document: Document, overlayRoot: HTMLElement): Document => {\n const boundaryDocumentElement = new Proxy(document.documentElement, {\n get: (target, property, receiver) => {\n if (property === 'clientHeight') {\n return overlayRoot.getBoundingClientRect().height;\n }\n if (property === 'clientWidth') {\n return overlayRoot.getBoundingClientRect().width;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n\n return new Proxy(document, {\n get: (target, property, receiver) => {\n if (property === 'documentElement') {\n return boundaryDocumentElement;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n};\n\nconst createBoundaryViewportRuler = (viewportRuler: ViewportRuler, overlayRoot: HTMLElement): ViewportRuler => {\n const boundaryViewportRuler = Object.create(viewportRuler) as ViewportRuler;\n boundaryViewportRuler.getViewportRect = () => getBoundaryRect(overlayRoot);\n boundaryViewportRuler.getViewportScrollPosition = () => ({ left: 0, top: 0 });\n boundaryViewportRuler.getViewportSize = () => {\n const { height, width } = getBoundaryRect(overlayRoot);\n return { height, width };\n };\n return boundaryViewportRuler;\n};\n\nconst adaptConnectedPositionStrategy = (\n strategy: FlexibleConnectedPositionStrategy,\n overlayRoot: HTMLElement,\n observeBoundary: (callback: () => void) => () => void,\n): FlexibleConnectedPositionStrategy => {\n const internals = strategy as unknown as IFlexibleConnectedPositionStrategyInternals;\n const getOriginRect = internals._getOriginRect.bind(strategy);\n const getOriginPoint = internals._getOriginPoint.bind(strategy);\n const attach = strategy.attach.bind(strategy);\n const detach = strategy.detach.bind(strategy);\n const dispose = strategy.dispose.bind(strategy);\n let stopObserving = () => {};\n internals._getOriginRect = () => {\n return translateRectToBoundary(getOriginRect(), overlayRoot.getBoundingClientRect());\n };\n internals._getOriginPoint = (originRect, _containerRect, position) => {\n return getOriginPoint(originRect, getBoundaryRect(overlayRoot), position);\n };\n strategy.attach = (overlayRef) => {\n attach(overlayRef);\n stopObserving();\n stopObserving = observeBoundary(() => strategy.apply());\n };\n strategy.detach = () => {\n stopObserving();\n stopObserving = () => {};\n detach();\n };\n strategy.dispose = () => {\n stopObserving();\n stopObserving = () => {};\n dispose();\n };\n\n return strategy;\n};\n\nclass MfOverlayPositionBuilder extends OverlayPositionBuilder {\n readonly #document: Document;\n readonly #overlayContainer: OverlayContainer;\n readonly #overlayRoot: HTMLElement;\n readonly #platform: Platform;\n readonly #viewportRuler: ViewportRuler;\n\n constructor(viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer, overlayRoot: HTMLElement) {\n super();\n this.#viewportRuler = viewportRuler;\n this.#document = document;\n this.#platform = platform;\n this.#overlayContainer = overlayContainer;\n this.#overlayRoot = overlayRoot;\n }\n\n override global(): GlobalPositionStrategy {\n return new GlobalPositionStrategy();\n }\n\n override flexibleConnectedTo(origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy {\n const boundaryDocument = createBoundaryDocument(this.#document, this.#overlayRoot);\n const boundaryViewportRuler = createBoundaryViewportRuler(this.#viewportRuler, this.#overlayRoot);\n const strategy = new FlexibleConnectedPositionStrategy(origin, boundaryViewportRuler, boundaryDocument, this.#platform, this.#overlayContainer);\n\n const overlayContainer = this.#overlayContainer as MfOverlayContainer;\n return adaptConnectedPositionStrategy(strategy, this.#overlayRoot, (callback) => overlayContainer.observeBoundary(callback));\n }\n}\n\nclass MfOverlayContainer extends OverlayContainer {\n readonly #boundaryObservers = new Set<() => void>();\n readonly #overlayRoot: HTMLElement;\n #animationFrame: number | null = null;\n #boundaryRect: IClientRect | null = null;\n #restoreRootPosition: (() => void) | null = null;\n #shadowStyle: HTMLStyleElement | null = null;\n\n constructor(overlayRoot: HTMLElement) {\n super();\n this.#overlayRoot = overlayRoot;\n }\n\n override ngOnDestroy(): void {\n if (this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n }\n this.#animationFrame = null;\n this.#boundaryObservers.clear();\n super.ngOnDestroy();\n this.#shadowStyle?.remove();\n this.#shadowStyle = null;\n this.#restoreRootPosition?.();\n this.#restoreRootPosition = null;\n }\n\n observeBoundary(callback: () => void): () => void {\n this.#boundaryObservers.add(callback);\n if (this.#boundaryObservers.size === 1) {\n this.#boundaryRect = this.#overlayRoot.getBoundingClientRect();\n this.#scheduleBoundaryCheck();\n }\n\n return () => {\n this.#boundaryObservers.delete(callback);\n if (this.#boundaryObservers.size === 0 && this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n this.#animationFrame = null;\n this.#boundaryRect = null;\n }\n };\n }\n\n protected override _createContainer(): void {\n if (this.#overlayRoot.ownerDocument.defaultView?.getComputedStyle(this.#overlayRoot).position === 'static') {\n const previousPosition = this.#overlayRoot.style.position;\n this.#overlayRoot.style.position = 'relative';\n this.#restoreRootPosition = () => {\n this.#overlayRoot.style.position = previousPosition;\n };\n }\n\n const rootNode = this.#overlayRoot.getRootNode();\n const ShadowRootConstructor = this.#overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n const style = this._document.createElement('style');\n style.setAttribute('data-edyn-mf-cdk-overlay-styles', '');\n style.textContent = CDK_OVERLAY_STRUCTURAL_CSS;\n rootNode.appendChild(style);\n this.#shadowStyle = style;\n }\n\n const container = this._document.createElement('div');\n container.classList.add('cdk-overlay-container');\n container.style.inset = '0';\n container.style.position = 'absolute';\n this.#overlayRoot.appendChild(container);\n this._containerElement = container;\n }\n\n #scheduleBoundaryCheck(): void {\n const window = this.#overlayRoot.ownerDocument.defaultView;\n if (!window || this.#animationFrame !== null || this.#boundaryObservers.size === 0) {\n return;\n }\n\n this.#animationFrame = window.requestAnimationFrame(() => {\n this.#animationFrame = null;\n const previous = this.#boundaryRect;\n const current = this.#overlayRoot.getBoundingClientRect();\n this.#boundaryRect = current;\n\n if (\n previous &&\n (previous.height !== current.height || previous.left !== current.left || previous.top !== current.top || previous.width !== current.width)\n ) {\n for (const observer of this.#boundaryObservers) {\n observer();\n }\n }\n\n this.#scheduleBoundaryCheck();\n });\n }\n}\n\nconst isDocumentViewportRoot = (overlayRoot: HTMLElement): boolean => {\n return overlayRoot === overlayRoot.ownerDocument.body || overlayRoot === overlayRoot.ownerDocument.documentElement;\n};\n\nconst createOverlayContainer = (overlayRoot: HTMLElement): OverlayContainer => {\n return isDocumentViewportRoot(overlayRoot) ? new OverlayContainer() : new MfOverlayContainer(overlayRoot);\n};\n\nconst createOverlayPositionBuilder = (\n viewportRuler: ViewportRuler,\n document: Document,\n platform: Platform,\n overlayContainer: OverlayContainer,\n overlayRoot: HTMLElement,\n): OverlayPositionBuilder => {\n if (isDocumentViewportRoot(overlayRoot)) {\n return new OverlayPositionBuilder();\n }\n return new MfOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n};\n\n// Hosted, the overlay root sits in the module's shadow root, so the providers every module already uses for overlays also\n// make rem lengths there follow the module's own root font size. They start with the module's injector, before its\n// first component renders and Angular adds component styles, and stop with it.\nconst followRootFontSizeOf = (overlayRoot: HTMLElement, destroyRef: DestroyRef): void => {\n const rootNode = overlayRoot.getRootNode();\n const ShadowRootConstructor = overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n destroyRef.onDestroy(followModuleRootFontSize(rootNode));\n }\n};\n\nconst createTokenProviders = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return [\n {\n provide: ENVIRONMENT_INITIALIZER,\n multi: true,\n useFactory: (overlayRoot: HTMLElement, destroyRef: DestroyRef) => () => followRootFontSizeOf(overlayRoot, destroyRef),\n deps: [overlayRootToken, DestroyRef],\n },\n {\n provide: OverlayContainer,\n useFactory: createOverlayContainer,\n deps: [overlayRootToken],\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: createOverlayPositionBuilder,\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer, overlayRootToken],\n },\n ];\n};\n\nexport const provideMfOverlayRoot = (overlayRoot?: HTMLElement): StaticProvider[] => {\n if (!overlayRoot) {\n return [];\n }\n\n return [\n {\n provide: ENVIRONMENT_INITIALIZER,\n multi: true,\n useFactory: (destroyRef: DestroyRef) => () => followRootFontSizeOf(overlayRoot, destroyRef),\n deps: [DestroyRef],\n },\n {\n provide: OverlayContainer,\n useFactory: () => createOverlayContainer(overlayRoot),\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: (viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer) => {\n return createOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n },\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer],\n },\n ];\n};\n\nexport const provideMfOverlayRootFromToken = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return createTokenProviders(overlayRootToken);\n};\n\nexport { CaseInsensitiveBasePathLocationStrategy, provideCaseInsensitiveBasePath } from './angular-location.js';\n"]}
|
|
1
|
+
{"version":3,"file":"angular.d.ts","sourceRoot":"","sources":["../src/angular.ts"],"names":[],"mappings":"AAUA,OAAO,EAA+C,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AA+TrH,eAAO,MAAM,oBAAoB,GAAI,cAAc,WAAW,KAAG,cAAc,EAwB9E,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,kBAAkB,aAAa,CAAC,WAAW,CAAC,KAAG,cAAc,EAE1G,CAAC;AAEF,OAAO,EAAE,uCAAuC,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["import {\n FlexibleConnectedPositionStrategy,\n type FlexibleConnectedPositionStrategyOrigin,\n GlobalPositionStrategy,\n OverlayContainer,\n OverlayPositionBuilder,\n} from '@angular/cdk/overlay';\nimport { Platform } from '@angular/cdk/platform';\nimport { ViewportRuler } from '@angular/cdk/scrolling';\nimport { DOCUMENT } from '@angular/common';\nimport { DestroyRef, ENVIRONMENT_INITIALIZER, NgZone, type ProviderToken, type StaticProvider } from '@angular/core';\nimport { followModuleRootFontSize } from './hosted-module-styles.js';\n\n// Mirrors the CDK 19 overlay-prebuilt.css structural rules while intentionally excluding document-level scroll blocking.\nconst CDK_OVERLAY_STRUCTURAL_CSS = `\n.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}\n.cdk-overlay-container{position:fixed;z-index:1000}\n.cdk-overlay-container:empty{display:none}\n.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}\n.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%;z-index:1000}\n.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation;z-index:1000;transition:opacity 400ms cubic-bezier(0.25,0.8,0.25,1)}\n@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}\n.cdk-overlay-backdrop-showing{opacity:1}\n@media(forced-colors:active){.cdk-overlay-backdrop-showing{opacity:.6}}\n.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}\n.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}\n.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}\n.cdk-overlay-backdrop-noop-animation{transition:none}\n.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px;z-index:1000}\n`;\n\ninterface IClientRect {\n bottom: number;\n height: number;\n left: number;\n right: number;\n top: number;\n width: number;\n x?: number;\n y?: number;\n}\n\ninterface IFlexibleConnectedPositionStrategyInternals {\n _getOriginPoint: (originRect: IClientRect, containerRect: IClientRect, position: unknown) => { x: number; y: number };\n _getOriginRect: () => IClientRect;\n}\n\nconst translateRectToBoundary = (rect: IClientRect, boundary: IClientRect): IClientRect => {\n const left = rect.left - boundary.left;\n const top = rect.top - boundary.top;\n\n return {\n bottom: top + rect.height,\n height: rect.height,\n left,\n right: left + rect.width,\n top,\n width: rect.width,\n x: left,\n y: top,\n };\n};\n\nconst getBoundaryRect = (overlayRoot: HTMLElement): IClientRect => {\n const { height, width } = overlayRoot.getBoundingClientRect();\n return {\n bottom: height,\n height,\n left: 0,\n right: width,\n top: 0,\n width,\n x: 0,\n y: 0,\n };\n};\n\nconst createBoundaryDocument = (document: Document, overlayRoot: HTMLElement): Document => {\n const boundaryDocumentElement = new Proxy(document.documentElement, {\n get: (target, property, receiver) => {\n if (property === 'clientHeight') {\n return overlayRoot.getBoundingClientRect().height;\n }\n if (property === 'clientWidth') {\n return overlayRoot.getBoundingClientRect().width;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n\n return new Proxy(document, {\n get: (target, property, receiver) => {\n if (property === 'documentElement') {\n return boundaryDocumentElement;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n};\n\nconst createBoundaryViewportRuler = (viewportRuler: ViewportRuler, overlayRoot: HTMLElement): ViewportRuler => {\n const boundaryViewportRuler = Object.create(viewportRuler) as ViewportRuler;\n boundaryViewportRuler.getViewportRect = () => getBoundaryRect(overlayRoot);\n boundaryViewportRuler.getViewportScrollPosition = () => ({ left: 0, top: 0 });\n boundaryViewportRuler.getViewportSize = () => {\n const { height, width } = getBoundaryRect(overlayRoot);\n return { height, width };\n };\n return boundaryViewportRuler;\n};\n\nconst adaptConnectedPositionStrategy = (\n strategy: FlexibleConnectedPositionStrategy,\n overlayRoot: HTMLElement,\n observeBoundary: (callback: () => void) => () => void,\n): FlexibleConnectedPositionStrategy => {\n const internals = strategy as unknown as IFlexibleConnectedPositionStrategyInternals;\n const getOriginRect = internals._getOriginRect.bind(strategy);\n const getOriginPoint = internals._getOriginPoint.bind(strategy);\n const attach = strategy.attach.bind(strategy);\n const detach = strategy.detach.bind(strategy);\n const dispose = strategy.dispose.bind(strategy);\n let stopObserving = () => {};\n internals._getOriginRect = () => {\n return translateRectToBoundary(getOriginRect(), overlayRoot.getBoundingClientRect());\n };\n internals._getOriginPoint = (originRect, _containerRect, position) => {\n return getOriginPoint(originRect, getBoundaryRect(overlayRoot), position);\n };\n strategy.attach = (overlayRef) => {\n attach(overlayRef);\n stopObserving();\n stopObserving = observeBoundary(() => strategy.apply());\n };\n strategy.detach = () => {\n stopObserving();\n stopObserving = () => {};\n detach();\n };\n strategy.dispose = () => {\n stopObserving();\n stopObserving = () => {};\n dispose();\n };\n\n return strategy;\n};\n\nclass MfOverlayPositionBuilder extends OverlayPositionBuilder {\n readonly #document: Document;\n readonly #overlayContainer: OverlayContainer;\n readonly #overlayRoot: HTMLElement;\n readonly #platform: Platform;\n readonly #viewportRuler: ViewportRuler;\n\n constructor(viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer, overlayRoot: HTMLElement) {\n super();\n this.#viewportRuler = viewportRuler;\n this.#document = document;\n this.#platform = platform;\n this.#overlayContainer = overlayContainer;\n this.#overlayRoot = overlayRoot;\n }\n\n override global(): GlobalPositionStrategy {\n return new GlobalPositionStrategy();\n }\n\n override flexibleConnectedTo(origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy {\n const boundaryDocument = createBoundaryDocument(this.#document, this.#overlayRoot);\n const boundaryViewportRuler = createBoundaryViewportRuler(this.#viewportRuler, this.#overlayRoot);\n const strategy = new FlexibleConnectedPositionStrategy(origin, boundaryViewportRuler, boundaryDocument, this.#platform, this.#overlayContainer);\n\n const overlayContainer = this.#overlayContainer as MfOverlayContainer;\n return adaptConnectedPositionStrategy(strategy, this.#overlayRoot, (callback) => overlayContainer.observeBoundary(callback));\n }\n}\n\nclass MfOverlayContainer extends OverlayContainer {\n readonly #boundaryObservers = new Set<() => void>();\n readonly #overlayRoot: HTMLElement;\n #animationFrame: number | null = null;\n #boundaryRect: IClientRect | null = null;\n #restoreRootPosition: (() => void) | null = null;\n #shadowStyle: HTMLStyleElement | null = null;\n\n constructor(overlayRoot: HTMLElement) {\n super();\n this.#overlayRoot = overlayRoot;\n }\n\n override ngOnDestroy(): void {\n if (this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n }\n this.#animationFrame = null;\n this.#boundaryObservers.clear();\n super.ngOnDestroy();\n this.#shadowStyle?.remove();\n this.#shadowStyle = null;\n this.#restoreRootPosition?.();\n this.#restoreRootPosition = null;\n }\n\n observeBoundary(callback: () => void): () => void {\n this.#boundaryObservers.add(callback);\n if (this.#boundaryObservers.size === 1) {\n this.#boundaryRect = this.#overlayRoot.getBoundingClientRect();\n this.#scheduleBoundaryCheck();\n }\n\n return () => {\n this.#boundaryObservers.delete(callback);\n if (this.#boundaryObservers.size === 0 && this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n this.#animationFrame = null;\n this.#boundaryRect = null;\n }\n };\n }\n\n protected override _createContainer(): void {\n if (this.#overlayRoot.ownerDocument.defaultView?.getComputedStyle(this.#overlayRoot).position === 'static') {\n const previousPosition = this.#overlayRoot.style.position;\n this.#overlayRoot.style.position = 'relative';\n this.#restoreRootPosition = () => {\n this.#overlayRoot.style.position = previousPosition;\n };\n }\n\n const rootNode = this.#overlayRoot.getRootNode();\n const ShadowRootConstructor = this.#overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n const style = this._document.createElement('style');\n style.setAttribute('data-edyn-mf-cdk-overlay-styles', '');\n style.textContent = CDK_OVERLAY_STRUCTURAL_CSS;\n rootNode.appendChild(style);\n this.#shadowStyle = style;\n }\n\n const container = this._document.createElement('div');\n container.classList.add('cdk-overlay-container');\n container.style.inset = '0';\n container.style.position = 'absolute';\n this.#overlayRoot.appendChild(container);\n this._containerElement = container;\n }\n\n #scheduleBoundaryCheck(): void {\n const window = this.#overlayRoot.ownerDocument.defaultView;\n if (!window || this.#animationFrame !== null || this.#boundaryObservers.size === 0) {\n return;\n }\n\n this.#animationFrame = window.requestAnimationFrame(() => {\n this.#animationFrame = null;\n const previous = this.#boundaryRect;\n const current = this.#overlayRoot.getBoundingClientRect();\n this.#boundaryRect = current;\n\n if (\n previous &&\n (previous.height !== current.height || previous.left !== current.left || previous.top !== current.top || previous.width !== current.width)\n ) {\n for (const observer of this.#boundaryObservers) {\n observer();\n }\n }\n\n this.#scheduleBoundaryCheck();\n });\n }\n}\n\nconst isDocumentViewportRoot = (overlayRoot: HTMLElement): boolean => {\n return overlayRoot === overlayRoot.ownerDocument.body || overlayRoot === overlayRoot.ownerDocument.documentElement;\n};\n\nconst createOverlayContainer = (overlayRoot: HTMLElement): OverlayContainer => {\n return isDocumentViewportRoot(overlayRoot) ? new OverlayContainer() : new MfOverlayContainer(overlayRoot);\n};\n\nconst createOverlayPositionBuilder = (\n viewportRuler: ViewportRuler,\n document: Document,\n platform: Platform,\n overlayContainer: OverlayContainer,\n overlayRoot: HTMLElement,\n): OverlayPositionBuilder => {\n if (isDocumentViewportRoot(overlayRoot)) {\n return new OverlayPositionBuilder();\n }\n return new MfOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n};\n\n// Hosted, the overlay root sits in the module's shadow root, so the providers every module already uses for overlays also\n// make rem lengths there follow the module's own root font size. They start with the module's injector, before its\n// first component renders and Angular adds component styles, and stop with it. The observers only rewrite CSS, so they\n// start outside Angular's zone: inside it, every DOM change in the module would run change detection, and a template\n// that renders new nodes on each check (an *ngFor over a getter, without trackBy) would never settle.\nconst followRootFontSizeOf = (overlayRoot: HTMLElement, destroyRef: DestroyRef, ngZone: NgZone): void => {\n const rootNode = overlayRoot.getRootNode();\n const ShadowRootConstructor = overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n destroyRef.onDestroy(ngZone.runOutsideAngular(() => followModuleRootFontSize(rootNode)));\n }\n};\n\nconst createTokenProviders = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return [\n {\n provide: ENVIRONMENT_INITIALIZER,\n multi: true,\n useFactory: (overlayRoot: HTMLElement, destroyRef: DestroyRef, ngZone: NgZone) => () => followRootFontSizeOf(overlayRoot, destroyRef, ngZone),\n deps: [overlayRootToken, DestroyRef, NgZone],\n },\n {\n provide: OverlayContainer,\n useFactory: createOverlayContainer,\n deps: [overlayRootToken],\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: createOverlayPositionBuilder,\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer, overlayRootToken],\n },\n ];\n};\n\nexport const provideMfOverlayRoot = (overlayRoot?: HTMLElement): StaticProvider[] => {\n if (!overlayRoot) {\n return [];\n }\n\n return [\n {\n provide: ENVIRONMENT_INITIALIZER,\n multi: true,\n useFactory: (destroyRef: DestroyRef, ngZone: NgZone) => () => followRootFontSizeOf(overlayRoot, destroyRef, ngZone),\n deps: [DestroyRef, NgZone],\n },\n {\n provide: OverlayContainer,\n useFactory: () => createOverlayContainer(overlayRoot),\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: (viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer) => {\n return createOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n },\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer],\n },\n ];\n};\n\nexport const provideMfOverlayRootFromToken = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return createTokenProviders(overlayRootToken);\n};\n\nexport { CaseInsensitiveBasePathLocationStrategy, provideCaseInsensitiveBasePath } from './angular-location.js';\n"]}
|
package/dist/angular.js
CHANGED
|
@@ -2,8 +2,8 @@ import { FlexibleConnectedPositionStrategy, GlobalPositionStrategy, OverlayConta
|
|
|
2
2
|
import { Platform } from '@angular/cdk/platform';
|
|
3
3
|
import { ViewportRuler } from '@angular/cdk/scrolling';
|
|
4
4
|
import { DOCUMENT } from '@angular/common';
|
|
5
|
-
import { DestroyRef, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
6
|
-
import { followModuleRootFontSize } from './
|
|
5
|
+
import { DestroyRef, ENVIRONMENT_INITIALIZER, NgZone } from '@angular/core';
|
|
6
|
+
import { followModuleRootFontSize } from './hosted-module-styles.js';
|
|
7
7
|
// Mirrors the CDK 19 overlay-prebuilt.css structural rules while intentionally excluding document-level scroll blocking.
|
|
8
8
|
const CDK_OVERLAY_STRUCTURAL_CSS = `
|
|
9
9
|
.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}
|
|
@@ -231,12 +231,14 @@ const createOverlayPositionBuilder = (viewportRuler, document, platform, overlay
|
|
|
231
231
|
};
|
|
232
232
|
// Hosted, the overlay root sits in the module's shadow root, so the providers every module already uses for overlays also
|
|
233
233
|
// make rem lengths there follow the module's own root font size. They start with the module's injector, before its
|
|
234
|
-
// first component renders and Angular adds component styles, and stop with it.
|
|
235
|
-
|
|
234
|
+
// first component renders and Angular adds component styles, and stop with it. The observers only rewrite CSS, so they
|
|
235
|
+
// start outside Angular's zone: inside it, every DOM change in the module would run change detection, and a template
|
|
236
|
+
// that renders new nodes on each check (an *ngFor over a getter, without trackBy) would never settle.
|
|
237
|
+
const followRootFontSizeOf = (overlayRoot, destroyRef, ngZone) => {
|
|
236
238
|
const rootNode = overlayRoot.getRootNode();
|
|
237
239
|
const ShadowRootConstructor = overlayRoot.ownerDocument.defaultView?.ShadowRoot;
|
|
238
240
|
if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {
|
|
239
|
-
destroyRef.onDestroy(followModuleRootFontSize(rootNode));
|
|
241
|
+
destroyRef.onDestroy(ngZone.runOutsideAngular(() => followModuleRootFontSize(rootNode)));
|
|
240
242
|
}
|
|
241
243
|
};
|
|
242
244
|
const createTokenProviders = (overlayRootToken) => {
|
|
@@ -244,8 +246,8 @@ const createTokenProviders = (overlayRootToken) => {
|
|
|
244
246
|
{
|
|
245
247
|
provide: ENVIRONMENT_INITIALIZER,
|
|
246
248
|
multi: true,
|
|
247
|
-
useFactory: (overlayRoot, destroyRef) => () => followRootFontSizeOf(overlayRoot, destroyRef),
|
|
248
|
-
deps: [overlayRootToken, DestroyRef],
|
|
249
|
+
useFactory: (overlayRoot, destroyRef, ngZone) => () => followRootFontSizeOf(overlayRoot, destroyRef, ngZone),
|
|
250
|
+
deps: [overlayRootToken, DestroyRef, NgZone],
|
|
249
251
|
},
|
|
250
252
|
{
|
|
251
253
|
provide: OverlayContainer,
|
|
@@ -267,8 +269,8 @@ export const provideMfOverlayRoot = (overlayRoot) => {
|
|
|
267
269
|
{
|
|
268
270
|
provide: ENVIRONMENT_INITIALIZER,
|
|
269
271
|
multi: true,
|
|
270
|
-
useFactory: (destroyRef) => () => followRootFontSizeOf(overlayRoot, destroyRef),
|
|
271
|
-
deps: [DestroyRef],
|
|
272
|
+
useFactory: (destroyRef, ngZone) => () => followRootFontSizeOf(overlayRoot, destroyRef, ngZone),
|
|
273
|
+
deps: [DestroyRef, NgZone],
|
|
272
274
|
},
|
|
273
275
|
{
|
|
274
276
|
provide: OverlayContainer,
|
package/dist/angular.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"angular.js","sourceRoot":"","sources":["../src/angular.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FlexibleConnectedPositionStrategy,\n type FlexibleConnectedPositionStrategyOrigin,\n GlobalPositionStrategy,\n OverlayContainer,\n OverlayPositionBuilder,\n} from '@angular/cdk/overlay';\nimport { Platform } from '@angular/cdk/platform';\nimport { ViewportRuler } from '@angular/cdk/scrolling';\nimport { DOCUMENT } from '@angular/common';\nimport { DestroyRef, ENVIRONMENT_INITIALIZER, type ProviderToken, type StaticProvider } from '@angular/core';\nimport { followModuleRootFontSize } from './root-font-size.js';\n\n// Mirrors the CDK 19 overlay-prebuilt.css structural rules while intentionally excluding document-level scroll blocking.\nconst CDK_OVERLAY_STRUCTURAL_CSS = `\n.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}\n.cdk-overlay-container{position:fixed;z-index:1000}\n.cdk-overlay-container:empty{display:none}\n.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}\n.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%;z-index:1000}\n.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation;z-index:1000;transition:opacity 400ms cubic-bezier(0.25,0.8,0.25,1)}\n@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}\n.cdk-overlay-backdrop-showing{opacity:1}\n@media(forced-colors:active){.cdk-overlay-backdrop-showing{opacity:.6}}\n.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}\n.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}\n.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}\n.cdk-overlay-backdrop-noop-animation{transition:none}\n.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px;z-index:1000}\n`;\n\ninterface IClientRect {\n bottom: number;\n height: number;\n left: number;\n right: number;\n top: number;\n width: number;\n x?: number;\n y?: number;\n}\n\ninterface IFlexibleConnectedPositionStrategyInternals {\n _getOriginPoint: (originRect: IClientRect, containerRect: IClientRect, position: unknown) => { x: number; y: number };\n _getOriginRect: () => IClientRect;\n}\n\nconst translateRectToBoundary = (rect: IClientRect, boundary: IClientRect): IClientRect => {\n const left = rect.left - boundary.left;\n const top = rect.top - boundary.top;\n\n return {\n bottom: top + rect.height,\n height: rect.height,\n left,\n right: left + rect.width,\n top,\n width: rect.width,\n x: left,\n y: top,\n };\n};\n\nconst getBoundaryRect = (overlayRoot: HTMLElement): IClientRect => {\n const { height, width } = overlayRoot.getBoundingClientRect();\n return {\n bottom: height,\n height,\n left: 0,\n right: width,\n top: 0,\n width,\n x: 0,\n y: 0,\n };\n};\n\nconst createBoundaryDocument = (document: Document, overlayRoot: HTMLElement): Document => {\n const boundaryDocumentElement = new Proxy(document.documentElement, {\n get: (target, property, receiver) => {\n if (property === 'clientHeight') {\n return overlayRoot.getBoundingClientRect().height;\n }\n if (property === 'clientWidth') {\n return overlayRoot.getBoundingClientRect().width;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n\n return new Proxy(document, {\n get: (target, property, receiver) => {\n if (property === 'documentElement') {\n return boundaryDocumentElement;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n};\n\nconst createBoundaryViewportRuler = (viewportRuler: ViewportRuler, overlayRoot: HTMLElement): ViewportRuler => {\n const boundaryViewportRuler = Object.create(viewportRuler) as ViewportRuler;\n boundaryViewportRuler.getViewportRect = () => getBoundaryRect(overlayRoot);\n boundaryViewportRuler.getViewportScrollPosition = () => ({ left: 0, top: 0 });\n boundaryViewportRuler.getViewportSize = () => {\n const { height, width } = getBoundaryRect(overlayRoot);\n return { height, width };\n };\n return boundaryViewportRuler;\n};\n\nconst adaptConnectedPositionStrategy = (\n strategy: FlexibleConnectedPositionStrategy,\n overlayRoot: HTMLElement,\n observeBoundary: (callback: () => void) => () => void,\n): FlexibleConnectedPositionStrategy => {\n const internals = strategy as unknown as IFlexibleConnectedPositionStrategyInternals;\n const getOriginRect = internals._getOriginRect.bind(strategy);\n const getOriginPoint = internals._getOriginPoint.bind(strategy);\n const attach = strategy.attach.bind(strategy);\n const detach = strategy.detach.bind(strategy);\n const dispose = strategy.dispose.bind(strategy);\n let stopObserving = () => {};\n internals._getOriginRect = () => {\n return translateRectToBoundary(getOriginRect(), overlayRoot.getBoundingClientRect());\n };\n internals._getOriginPoint = (originRect, _containerRect, position) => {\n return getOriginPoint(originRect, getBoundaryRect(overlayRoot), position);\n };\n strategy.attach = (overlayRef) => {\n attach(overlayRef);\n stopObserving();\n stopObserving = observeBoundary(() => strategy.apply());\n };\n strategy.detach = () => {\n stopObserving();\n stopObserving = () => {};\n detach();\n };\n strategy.dispose = () => {\n stopObserving();\n stopObserving = () => {};\n dispose();\n };\n\n return strategy;\n};\n\nclass MfOverlayPositionBuilder extends OverlayPositionBuilder {\n readonly #document: Document;\n readonly #overlayContainer: OverlayContainer;\n readonly #overlayRoot: HTMLElement;\n readonly #platform: Platform;\n readonly #viewportRuler: ViewportRuler;\n\n constructor(viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer, overlayRoot: HTMLElement) {\n super();\n this.#viewportRuler = viewportRuler;\n this.#document = document;\n this.#platform = platform;\n this.#overlayContainer = overlayContainer;\n this.#overlayRoot = overlayRoot;\n }\n\n override global(): GlobalPositionStrategy {\n return new GlobalPositionStrategy();\n }\n\n override flexibleConnectedTo(origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy {\n const boundaryDocument = createBoundaryDocument(this.#document, this.#overlayRoot);\n const boundaryViewportRuler = createBoundaryViewportRuler(this.#viewportRuler, this.#overlayRoot);\n const strategy = new FlexibleConnectedPositionStrategy(origin, boundaryViewportRuler, boundaryDocument, this.#platform, this.#overlayContainer);\n\n const overlayContainer = this.#overlayContainer as MfOverlayContainer;\n return adaptConnectedPositionStrategy(strategy, this.#overlayRoot, (callback) => overlayContainer.observeBoundary(callback));\n }\n}\n\nclass MfOverlayContainer extends OverlayContainer {\n readonly #boundaryObservers = new Set<() => void>();\n readonly #overlayRoot: HTMLElement;\n #animationFrame: number | null = null;\n #boundaryRect: IClientRect | null = null;\n #restoreRootPosition: (() => void) | null = null;\n #shadowStyle: HTMLStyleElement | null = null;\n\n constructor(overlayRoot: HTMLElement) {\n super();\n this.#overlayRoot = overlayRoot;\n }\n\n override ngOnDestroy(): void {\n if (this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n }\n this.#animationFrame = null;\n this.#boundaryObservers.clear();\n super.ngOnDestroy();\n this.#shadowStyle?.remove();\n this.#shadowStyle = null;\n this.#restoreRootPosition?.();\n this.#restoreRootPosition = null;\n }\n\n observeBoundary(callback: () => void): () => void {\n this.#boundaryObservers.add(callback);\n if (this.#boundaryObservers.size === 1) {\n this.#boundaryRect = this.#overlayRoot.getBoundingClientRect();\n this.#scheduleBoundaryCheck();\n }\n\n return () => {\n this.#boundaryObservers.delete(callback);\n if (this.#boundaryObservers.size === 0 && this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n this.#animationFrame = null;\n this.#boundaryRect = null;\n }\n };\n }\n\n protected override _createContainer(): void {\n if (this.#overlayRoot.ownerDocument.defaultView?.getComputedStyle(this.#overlayRoot).position === 'static') {\n const previousPosition = this.#overlayRoot.style.position;\n this.#overlayRoot.style.position = 'relative';\n this.#restoreRootPosition = () => {\n this.#overlayRoot.style.position = previousPosition;\n };\n }\n\n const rootNode = this.#overlayRoot.getRootNode();\n const ShadowRootConstructor = this.#overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n const style = this._document.createElement('style');\n style.setAttribute('data-edyn-mf-cdk-overlay-styles', '');\n style.textContent = CDK_OVERLAY_STRUCTURAL_CSS;\n rootNode.appendChild(style);\n this.#shadowStyle = style;\n }\n\n const container = this._document.createElement('div');\n container.classList.add('cdk-overlay-container');\n container.style.inset = '0';\n container.style.position = 'absolute';\n this.#overlayRoot.appendChild(container);\n this._containerElement = container;\n }\n\n #scheduleBoundaryCheck(): void {\n const window = this.#overlayRoot.ownerDocument.defaultView;\n if (!window || this.#animationFrame !== null || this.#boundaryObservers.size === 0) {\n return;\n }\n\n this.#animationFrame = window.requestAnimationFrame(() => {\n this.#animationFrame = null;\n const previous = this.#boundaryRect;\n const current = this.#overlayRoot.getBoundingClientRect();\n this.#boundaryRect = current;\n\n if (\n previous &&\n (previous.height !== current.height || previous.left !== current.left || previous.top !== current.top || previous.width !== current.width)\n ) {\n for (const observer of this.#boundaryObservers) {\n observer();\n }\n }\n\n this.#scheduleBoundaryCheck();\n });\n }\n}\n\nconst isDocumentViewportRoot = (overlayRoot: HTMLElement): boolean => {\n return overlayRoot === overlayRoot.ownerDocument.body || overlayRoot === overlayRoot.ownerDocument.documentElement;\n};\n\nconst createOverlayContainer = (overlayRoot: HTMLElement): OverlayContainer => {\n return isDocumentViewportRoot(overlayRoot) ? new OverlayContainer() : new MfOverlayContainer(overlayRoot);\n};\n\nconst createOverlayPositionBuilder = (\n viewportRuler: ViewportRuler,\n document: Document,\n platform: Platform,\n overlayContainer: OverlayContainer,\n overlayRoot: HTMLElement,\n): OverlayPositionBuilder => {\n if (isDocumentViewportRoot(overlayRoot)) {\n return new OverlayPositionBuilder();\n }\n return new MfOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n};\n\n// Hosted, the overlay root sits in the module's shadow root, so the providers every module already uses for overlays also\n// make rem lengths there follow the module's own root font size. They start with the module's injector, before its\n// first component renders and Angular adds component styles, and stop with it.\nconst followRootFontSizeOf = (overlayRoot: HTMLElement, destroyRef: DestroyRef): void => {\n const rootNode = overlayRoot.getRootNode();\n const ShadowRootConstructor = overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n destroyRef.onDestroy(followModuleRootFontSize(rootNode));\n }\n};\n\nconst createTokenProviders = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return [\n {\n provide: ENVIRONMENT_INITIALIZER,\n multi: true,\n useFactory: (overlayRoot: HTMLElement, destroyRef: DestroyRef) => () => followRootFontSizeOf(overlayRoot, destroyRef),\n deps: [overlayRootToken, DestroyRef],\n },\n {\n provide: OverlayContainer,\n useFactory: createOverlayContainer,\n deps: [overlayRootToken],\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: createOverlayPositionBuilder,\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer, overlayRootToken],\n },\n ];\n};\n\nexport const provideMfOverlayRoot = (overlayRoot?: HTMLElement): StaticProvider[] => {\n if (!overlayRoot) {\n return [];\n }\n\n return [\n {\n provide: ENVIRONMENT_INITIALIZER,\n multi: true,\n useFactory: (destroyRef: DestroyRef) => () => followRootFontSizeOf(overlayRoot, destroyRef),\n deps: [DestroyRef],\n },\n {\n provide: OverlayContainer,\n useFactory: () => createOverlayContainer(overlayRoot),\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: (viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer) => {\n return createOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n },\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer],\n },\n ];\n};\n\nexport const provideMfOverlayRootFromToken = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return createTokenProviders(overlayRootToken);\n};\n\nexport { CaseInsensitiveBasePathLocationStrategy, provideCaseInsensitiveBasePath } from './angular-location.js';\n"]}
|
|
1
|
+
{"version":3,"file":"angular.js","sourceRoot":"","sources":["../src/angular.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FlexibleConnectedPositionStrategy,\n type FlexibleConnectedPositionStrategyOrigin,\n GlobalPositionStrategy,\n OverlayContainer,\n OverlayPositionBuilder,\n} from '@angular/cdk/overlay';\nimport { Platform } from '@angular/cdk/platform';\nimport { ViewportRuler } from '@angular/cdk/scrolling';\nimport { DOCUMENT } from '@angular/common';\nimport { DestroyRef, ENVIRONMENT_INITIALIZER, NgZone, type ProviderToken, type StaticProvider } from '@angular/core';\nimport { followModuleRootFontSize } from './hosted-module-styles.js';\n\n// Mirrors the CDK 19 overlay-prebuilt.css structural rules while intentionally excluding document-level scroll blocking.\nconst CDK_OVERLAY_STRUCTURAL_CSS = `\n.cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}\n.cdk-overlay-container{position:fixed;z-index:1000}\n.cdk-overlay-container:empty{display:none}\n.cdk-global-overlay-wrapper{display:flex;position:absolute;z-index:1000}\n.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%;z-index:1000}\n.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation;z-index:1000;transition:opacity 400ms cubic-bezier(0.25,0.8,0.25,1)}\n@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}\n.cdk-overlay-backdrop-showing{opacity:1}\n@media(forced-colors:active){.cdk-overlay-backdrop-showing{opacity:.6}}\n.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}\n.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}\n.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}\n.cdk-overlay-backdrop-noop-animation{transition:none}\n.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px;z-index:1000}\n`;\n\ninterface IClientRect {\n bottom: number;\n height: number;\n left: number;\n right: number;\n top: number;\n width: number;\n x?: number;\n y?: number;\n}\n\ninterface IFlexibleConnectedPositionStrategyInternals {\n _getOriginPoint: (originRect: IClientRect, containerRect: IClientRect, position: unknown) => { x: number; y: number };\n _getOriginRect: () => IClientRect;\n}\n\nconst translateRectToBoundary = (rect: IClientRect, boundary: IClientRect): IClientRect => {\n const left = rect.left - boundary.left;\n const top = rect.top - boundary.top;\n\n return {\n bottom: top + rect.height,\n height: rect.height,\n left,\n right: left + rect.width,\n top,\n width: rect.width,\n x: left,\n y: top,\n };\n};\n\nconst getBoundaryRect = (overlayRoot: HTMLElement): IClientRect => {\n const { height, width } = overlayRoot.getBoundingClientRect();\n return {\n bottom: height,\n height,\n left: 0,\n right: width,\n top: 0,\n width,\n x: 0,\n y: 0,\n };\n};\n\nconst createBoundaryDocument = (document: Document, overlayRoot: HTMLElement): Document => {\n const boundaryDocumentElement = new Proxy(document.documentElement, {\n get: (target, property, receiver) => {\n if (property === 'clientHeight') {\n return overlayRoot.getBoundingClientRect().height;\n }\n if (property === 'clientWidth') {\n return overlayRoot.getBoundingClientRect().width;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n\n return new Proxy(document, {\n get: (target, property, receiver) => {\n if (property === 'documentElement') {\n return boundaryDocumentElement;\n }\n return Reflect.get(target, property, receiver);\n },\n });\n};\n\nconst createBoundaryViewportRuler = (viewportRuler: ViewportRuler, overlayRoot: HTMLElement): ViewportRuler => {\n const boundaryViewportRuler = Object.create(viewportRuler) as ViewportRuler;\n boundaryViewportRuler.getViewportRect = () => getBoundaryRect(overlayRoot);\n boundaryViewportRuler.getViewportScrollPosition = () => ({ left: 0, top: 0 });\n boundaryViewportRuler.getViewportSize = () => {\n const { height, width } = getBoundaryRect(overlayRoot);\n return { height, width };\n };\n return boundaryViewportRuler;\n};\n\nconst adaptConnectedPositionStrategy = (\n strategy: FlexibleConnectedPositionStrategy,\n overlayRoot: HTMLElement,\n observeBoundary: (callback: () => void) => () => void,\n): FlexibleConnectedPositionStrategy => {\n const internals = strategy as unknown as IFlexibleConnectedPositionStrategyInternals;\n const getOriginRect = internals._getOriginRect.bind(strategy);\n const getOriginPoint = internals._getOriginPoint.bind(strategy);\n const attach = strategy.attach.bind(strategy);\n const detach = strategy.detach.bind(strategy);\n const dispose = strategy.dispose.bind(strategy);\n let stopObserving = () => {};\n internals._getOriginRect = () => {\n return translateRectToBoundary(getOriginRect(), overlayRoot.getBoundingClientRect());\n };\n internals._getOriginPoint = (originRect, _containerRect, position) => {\n return getOriginPoint(originRect, getBoundaryRect(overlayRoot), position);\n };\n strategy.attach = (overlayRef) => {\n attach(overlayRef);\n stopObserving();\n stopObserving = observeBoundary(() => strategy.apply());\n };\n strategy.detach = () => {\n stopObserving();\n stopObserving = () => {};\n detach();\n };\n strategy.dispose = () => {\n stopObserving();\n stopObserving = () => {};\n dispose();\n };\n\n return strategy;\n};\n\nclass MfOverlayPositionBuilder extends OverlayPositionBuilder {\n readonly #document: Document;\n readonly #overlayContainer: OverlayContainer;\n readonly #overlayRoot: HTMLElement;\n readonly #platform: Platform;\n readonly #viewportRuler: ViewportRuler;\n\n constructor(viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer, overlayRoot: HTMLElement) {\n super();\n this.#viewportRuler = viewportRuler;\n this.#document = document;\n this.#platform = platform;\n this.#overlayContainer = overlayContainer;\n this.#overlayRoot = overlayRoot;\n }\n\n override global(): GlobalPositionStrategy {\n return new GlobalPositionStrategy();\n }\n\n override flexibleConnectedTo(origin: FlexibleConnectedPositionStrategyOrigin): FlexibleConnectedPositionStrategy {\n const boundaryDocument = createBoundaryDocument(this.#document, this.#overlayRoot);\n const boundaryViewportRuler = createBoundaryViewportRuler(this.#viewportRuler, this.#overlayRoot);\n const strategy = new FlexibleConnectedPositionStrategy(origin, boundaryViewportRuler, boundaryDocument, this.#platform, this.#overlayContainer);\n\n const overlayContainer = this.#overlayContainer as MfOverlayContainer;\n return adaptConnectedPositionStrategy(strategy, this.#overlayRoot, (callback) => overlayContainer.observeBoundary(callback));\n }\n}\n\nclass MfOverlayContainer extends OverlayContainer {\n readonly #boundaryObservers = new Set<() => void>();\n readonly #overlayRoot: HTMLElement;\n #animationFrame: number | null = null;\n #boundaryRect: IClientRect | null = null;\n #restoreRootPosition: (() => void) | null = null;\n #shadowStyle: HTMLStyleElement | null = null;\n\n constructor(overlayRoot: HTMLElement) {\n super();\n this.#overlayRoot = overlayRoot;\n }\n\n override ngOnDestroy(): void {\n if (this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n }\n this.#animationFrame = null;\n this.#boundaryObservers.clear();\n super.ngOnDestroy();\n this.#shadowStyle?.remove();\n this.#shadowStyle = null;\n this.#restoreRootPosition?.();\n this.#restoreRootPosition = null;\n }\n\n observeBoundary(callback: () => void): () => void {\n this.#boundaryObservers.add(callback);\n if (this.#boundaryObservers.size === 1) {\n this.#boundaryRect = this.#overlayRoot.getBoundingClientRect();\n this.#scheduleBoundaryCheck();\n }\n\n return () => {\n this.#boundaryObservers.delete(callback);\n if (this.#boundaryObservers.size === 0 && this.#animationFrame !== null) {\n this.#overlayRoot.ownerDocument.defaultView?.cancelAnimationFrame(this.#animationFrame);\n this.#animationFrame = null;\n this.#boundaryRect = null;\n }\n };\n }\n\n protected override _createContainer(): void {\n if (this.#overlayRoot.ownerDocument.defaultView?.getComputedStyle(this.#overlayRoot).position === 'static') {\n const previousPosition = this.#overlayRoot.style.position;\n this.#overlayRoot.style.position = 'relative';\n this.#restoreRootPosition = () => {\n this.#overlayRoot.style.position = previousPosition;\n };\n }\n\n const rootNode = this.#overlayRoot.getRootNode();\n const ShadowRootConstructor = this.#overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n const style = this._document.createElement('style');\n style.setAttribute('data-edyn-mf-cdk-overlay-styles', '');\n style.textContent = CDK_OVERLAY_STRUCTURAL_CSS;\n rootNode.appendChild(style);\n this.#shadowStyle = style;\n }\n\n const container = this._document.createElement('div');\n container.classList.add('cdk-overlay-container');\n container.style.inset = '0';\n container.style.position = 'absolute';\n this.#overlayRoot.appendChild(container);\n this._containerElement = container;\n }\n\n #scheduleBoundaryCheck(): void {\n const window = this.#overlayRoot.ownerDocument.defaultView;\n if (!window || this.#animationFrame !== null || this.#boundaryObservers.size === 0) {\n return;\n }\n\n this.#animationFrame = window.requestAnimationFrame(() => {\n this.#animationFrame = null;\n const previous = this.#boundaryRect;\n const current = this.#overlayRoot.getBoundingClientRect();\n this.#boundaryRect = current;\n\n if (\n previous &&\n (previous.height !== current.height || previous.left !== current.left || previous.top !== current.top || previous.width !== current.width)\n ) {\n for (const observer of this.#boundaryObservers) {\n observer();\n }\n }\n\n this.#scheduleBoundaryCheck();\n });\n }\n}\n\nconst isDocumentViewportRoot = (overlayRoot: HTMLElement): boolean => {\n return overlayRoot === overlayRoot.ownerDocument.body || overlayRoot === overlayRoot.ownerDocument.documentElement;\n};\n\nconst createOverlayContainer = (overlayRoot: HTMLElement): OverlayContainer => {\n return isDocumentViewportRoot(overlayRoot) ? new OverlayContainer() : new MfOverlayContainer(overlayRoot);\n};\n\nconst createOverlayPositionBuilder = (\n viewportRuler: ViewportRuler,\n document: Document,\n platform: Platform,\n overlayContainer: OverlayContainer,\n overlayRoot: HTMLElement,\n): OverlayPositionBuilder => {\n if (isDocumentViewportRoot(overlayRoot)) {\n return new OverlayPositionBuilder();\n }\n return new MfOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n};\n\n// Hosted, the overlay root sits in the module's shadow root, so the providers every module already uses for overlays also\n// make rem lengths there follow the module's own root font size. They start with the module's injector, before its\n// first component renders and Angular adds component styles, and stop with it. The observers only rewrite CSS, so they\n// start outside Angular's zone: inside it, every DOM change in the module would run change detection, and a template\n// that renders new nodes on each check (an *ngFor over a getter, without trackBy) would never settle.\nconst followRootFontSizeOf = (overlayRoot: HTMLElement, destroyRef: DestroyRef, ngZone: NgZone): void => {\n const rootNode = overlayRoot.getRootNode();\n const ShadowRootConstructor = overlayRoot.ownerDocument.defaultView?.ShadowRoot;\n if (ShadowRootConstructor && rootNode instanceof ShadowRootConstructor) {\n destroyRef.onDestroy(ngZone.runOutsideAngular(() => followModuleRootFontSize(rootNode)));\n }\n};\n\nconst createTokenProviders = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return [\n {\n provide: ENVIRONMENT_INITIALIZER,\n multi: true,\n useFactory: (overlayRoot: HTMLElement, destroyRef: DestroyRef, ngZone: NgZone) => () => followRootFontSizeOf(overlayRoot, destroyRef, ngZone),\n deps: [overlayRootToken, DestroyRef, NgZone],\n },\n {\n provide: OverlayContainer,\n useFactory: createOverlayContainer,\n deps: [overlayRootToken],\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: createOverlayPositionBuilder,\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer, overlayRootToken],\n },\n ];\n};\n\nexport const provideMfOverlayRoot = (overlayRoot?: HTMLElement): StaticProvider[] => {\n if (!overlayRoot) {\n return [];\n }\n\n return [\n {\n provide: ENVIRONMENT_INITIALIZER,\n multi: true,\n useFactory: (destroyRef: DestroyRef, ngZone: NgZone) => () => followRootFontSizeOf(overlayRoot, destroyRef, ngZone),\n deps: [DestroyRef, NgZone],\n },\n {\n provide: OverlayContainer,\n useFactory: () => createOverlayContainer(overlayRoot),\n },\n {\n provide: OverlayPositionBuilder,\n useFactory: (viewportRuler: ViewportRuler, document: Document, platform: Platform, overlayContainer: OverlayContainer) => {\n return createOverlayPositionBuilder(viewportRuler, document, platform, overlayContainer, overlayRoot);\n },\n deps: [ViewportRuler, DOCUMENT, Platform, OverlayContainer],\n },\n ];\n};\n\nexport const provideMfOverlayRootFromToken = (overlayRootToken: ProviderToken<HTMLElement>): StaticProvider[] => {\n return createTokenProviders(overlayRootToken);\n};\n\nexport { CaseInsensitiveBasePathLocationStrategy, provideCaseInsensitiveBasePath } from './angular-location.js';\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lets the global document's querySelector, querySelectorAll, getElementById and getElementsByClassName also find a
|
|
3
|
+
* hosted module's elements, for a host to call on each module shadow root it creates and stop on unmount. Each call
|
|
4
|
+
* first runs as normal and keeps its result whenever it finds anything; only an empty result on the shadow root's own
|
|
5
|
+
* document falls back to the registered roots, newest first, and the first root with a match answers. Fallback results
|
|
6
|
+
* of querySelectorAll and getElementsByClassName are static NodeLists, not live collections. getElementsByTagName is
|
|
7
|
+
* left alone: tag queries on a page nearly always match the host's own elements. The prototype is patched once for
|
|
8
|
+
* any number of roots and restored when the last one stops.
|
|
9
|
+
*/
|
|
10
|
+
export declare const scopeDocumentQueries: (root: ShadowRoot) => (() => void);
|
|
11
|
+
//# sourceMappingURL=document-queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-queries.d.ts","sourceRoot":"","sources":["../src/document-queries.ts"],"names":[],"mappings":"AA+GA;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,UAAU,KAAG,CAAC,MAAM,IAAI,CA0BlE,CAAC","sourcesContent":["// A module that queried its own elements through the global document (`document.querySelector('.week-wrapper')`, or\n// Angular's injected DOCUMENT) found them on its own page. Hosted, they live in the module's shadow root, which document\n// queries never enter, so the same calls return null or nothing. scopeDocumentQueries() lets those calls fall back to\n// the module's shadow root, and only when the document itself has no match.\n\ntype TQueryMethod = 'getElementById' | 'getElementsByClassName' | 'querySelector' | 'querySelectorAll';\n\ntype TQuery = (this: Document, ...args: unknown[]) => unknown;\n\ninterface IDocumentQueryScope {\n originals: Record<TQueryMethod, TQuery>;\n roots: Map<Document, ShadowRoot[]>;\n wrappers: Record<TQueryMethod, TQuery>;\n}\n\nconst QUERY_METHODS: readonly TQueryMethod[] = ['getElementById', 'getElementsByClassName', 'querySelector', 'querySelectorAll'];\n// Shared through the prototype under a registry symbol, so two copies of this package on one page wrap it only once.\nconst SCOPE_KEY = Symbol.for('edynamix.mf-isolation.document-query-scope');\n\ntype TQueryPrototype = Record<TQueryMethod, TQuery> & { [SCOPE_KEY]?: IDocumentQueryScope };\n\nconst escapeClassName = (name: string): string => {\n return name\n .replace(/[^\\w-]/g, (character) => `\\\\${character}`)\n .replace(/^(-?)(\\d)/, (_match, dash: string, digit: string) => `${dash}\\\\3${digit} `);\n};\n\nconst isEmpty = (result: unknown): boolean => {\n return result === null || (typeof result === 'object' && result !== null && 'length' in result && result.length === 0);\n};\n\nconst fallbackIn = (root: ShadowRoot, method: TQueryMethod, args: unknown[]): unknown => {\n const [query] = args;\n if (typeof query !== 'string') {\n return undefined;\n }\n switch (method) {\n case 'getElementById':\n return root.getElementById(query);\n case 'querySelector':\n return root.querySelector(query);\n case 'querySelectorAll':\n return root.querySelectorAll(query);\n case 'getElementsByClassName': {\n const classSelector = query.split(/\\s+/).filter(Boolean).map(escapeClassName).join('.');\n return classSelector ? root.querySelectorAll(`.${classSelector}`) : undefined;\n }\n }\n};\n\n// The method's own home on the prototype chain (Document.prototype in browsers), found from the document so the right\n// realm is patched: another window's or frame's Document is a different prototype and is never touched.\nconst queryPrototypeOf = (document: Document): TQueryPrototype => {\n let prototype: object | null = Object.getPrototypeOf(document);\n while (prototype && !Object.hasOwn(prototype, 'querySelector')) {\n prototype = Object.getPrototypeOf(prototype);\n }\n if (!prototype) {\n throw new Error('Cannot scope document queries: the document has no querySelector on its prototype chain.');\n }\n return prototype as TQueryPrototype;\n};\n\nconst installScope = (prototype: TQueryPrototype): IDocumentQueryScope => {\n const existing = prototype[SCOPE_KEY];\n if (existing) {\n return existing;\n }\n\n const roots = new Map<Document, ShadowRoot[]>();\n const originals = {} as Record<TQueryMethod, TQuery>;\n const wrappers = {} as Record<TQueryMethod, TQuery>;\n for (const method of QUERY_METHODS) {\n const original = prototype[method];\n originals[method] = original;\n // A function expression keeps `this`: calls on any other document, or that find something, return the original result.\n wrappers[method] = function (this: Document, ...args: unknown[]): unknown {\n const result = original.apply(this, args);\n const scopedRoots = roots.get(this);\n if (!scopedRoots || !isEmpty(result)) {\n return result;\n }\n for (let index = scopedRoots.length - 1; index >= 0; index -= 1) {\n const root = scopedRoots[index];\n const fallback = root ? fallbackIn(root, method, args) : undefined;\n if (fallback !== undefined && !isEmpty(fallback)) {\n return fallback;\n }\n }\n return result;\n };\n prototype[method] = wrappers[method];\n }\n\n const scope: IDocumentQueryScope = { originals, roots, wrappers };\n Object.defineProperty(prototype, SCOPE_KEY, { configurable: true, value: scope });\n return scope;\n};\n\nconst uninstallScope = (prototype: TQueryPrototype, scope: IDocumentQueryScope): void => {\n // A method someone wrapped again after us keeps their wrapper, and ours inside it passes calls straight through.\n for (const method of QUERY_METHODS) {\n if (prototype[method] === scope.wrappers[method]) {\n prototype[method] = scope.originals[method];\n }\n }\n if (QUERY_METHODS.every((method) => prototype[method] === scope.originals[method])) {\n delete prototype[SCOPE_KEY];\n }\n};\n\n/**\n * Lets the global document's querySelector, querySelectorAll, getElementById and getElementsByClassName also find a\n * hosted module's elements, for a host to call on each module shadow root it creates and stop on unmount. Each call\n * first runs as normal and keeps its result whenever it finds anything; only an empty result on the shadow root's own\n * document falls back to the registered roots, newest first, and the first root with a match answers. Fallback results\n * of querySelectorAll and getElementsByClassName are static NodeLists, not live collections. getElementsByTagName is\n * left alone: tag queries on a page nearly always match the host's own elements. The prototype is patched once for\n * any number of roots and restored when the last one stops.\n */\nexport const scopeDocumentQueries = (root: ShadowRoot): (() => void) => {\n const document = root.ownerDocument;\n const prototype = queryPrototypeOf(document);\n const scope = installScope(prototype);\n const roots = scope.roots.get(document) ?? [];\n roots.push(root);\n scope.roots.set(document, roots);\n\n let stopped = false;\n return () => {\n if (stopped) {\n return;\n }\n stopped = true;\n const current = scope.roots.get(document) ?? [];\n const index = current.lastIndexOf(root);\n if (index !== -1) {\n current.splice(index, 1);\n }\n if (current.length === 0) {\n scope.roots.delete(document);\n }\n if (scope.roots.size === 0) {\n uninstallScope(prototype, scope);\n }\n };\n};\n"]}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// A module that queried its own elements through the global document (`document.querySelector('.week-wrapper')`, or
|
|
2
|
+
// Angular's injected DOCUMENT) found them on its own page. Hosted, they live in the module's shadow root, which document
|
|
3
|
+
// queries never enter, so the same calls return null or nothing. scopeDocumentQueries() lets those calls fall back to
|
|
4
|
+
// the module's shadow root, and only when the document itself has no match.
|
|
5
|
+
const QUERY_METHODS = ['getElementById', 'getElementsByClassName', 'querySelector', 'querySelectorAll'];
|
|
6
|
+
// Shared through the prototype under a registry symbol, so two copies of this package on one page wrap it only once.
|
|
7
|
+
const SCOPE_KEY = Symbol.for('edynamix.mf-isolation.document-query-scope');
|
|
8
|
+
const escapeClassName = (name) => {
|
|
9
|
+
return name
|
|
10
|
+
.replace(/[^\w-]/g, (character) => `\\${character}`)
|
|
11
|
+
.replace(/^(-?)(\d)/, (_match, dash, digit) => `${dash}\\3${digit} `);
|
|
12
|
+
};
|
|
13
|
+
const isEmpty = (result) => {
|
|
14
|
+
return result === null || (typeof result === 'object' && result !== null && 'length' in result && result.length === 0);
|
|
15
|
+
};
|
|
16
|
+
const fallbackIn = (root, method, args) => {
|
|
17
|
+
const [query] = args;
|
|
18
|
+
if (typeof query !== 'string') {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
switch (method) {
|
|
22
|
+
case 'getElementById':
|
|
23
|
+
return root.getElementById(query);
|
|
24
|
+
case 'querySelector':
|
|
25
|
+
return root.querySelector(query);
|
|
26
|
+
case 'querySelectorAll':
|
|
27
|
+
return root.querySelectorAll(query);
|
|
28
|
+
case 'getElementsByClassName': {
|
|
29
|
+
const classSelector = query.split(/\s+/).filter(Boolean).map(escapeClassName).join('.');
|
|
30
|
+
return classSelector ? root.querySelectorAll(`.${classSelector}`) : undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
// The method's own home on the prototype chain (Document.prototype in browsers), found from the document so the right
|
|
35
|
+
// realm is patched: another window's or frame's Document is a different prototype and is never touched.
|
|
36
|
+
const queryPrototypeOf = (document) => {
|
|
37
|
+
let prototype = Object.getPrototypeOf(document);
|
|
38
|
+
while (prototype && !Object.hasOwn(prototype, 'querySelector')) {
|
|
39
|
+
prototype = Object.getPrototypeOf(prototype);
|
|
40
|
+
}
|
|
41
|
+
if (!prototype) {
|
|
42
|
+
throw new Error('Cannot scope document queries: the document has no querySelector on its prototype chain.');
|
|
43
|
+
}
|
|
44
|
+
return prototype;
|
|
45
|
+
};
|
|
46
|
+
const installScope = (prototype) => {
|
|
47
|
+
const existing = prototype[SCOPE_KEY];
|
|
48
|
+
if (existing) {
|
|
49
|
+
return existing;
|
|
50
|
+
}
|
|
51
|
+
const roots = new Map();
|
|
52
|
+
const originals = {};
|
|
53
|
+
const wrappers = {};
|
|
54
|
+
for (const method of QUERY_METHODS) {
|
|
55
|
+
const original = prototype[method];
|
|
56
|
+
originals[method] = original;
|
|
57
|
+
// A function expression keeps `this`: calls on any other document, or that find something, return the original result.
|
|
58
|
+
wrappers[method] = function (...args) {
|
|
59
|
+
const result = original.apply(this, args);
|
|
60
|
+
const scopedRoots = roots.get(this);
|
|
61
|
+
if (!scopedRoots || !isEmpty(result)) {
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
for (let index = scopedRoots.length - 1; index >= 0; index -= 1) {
|
|
65
|
+
const root = scopedRoots[index];
|
|
66
|
+
const fallback = root ? fallbackIn(root, method, args) : undefined;
|
|
67
|
+
if (fallback !== undefined && !isEmpty(fallback)) {
|
|
68
|
+
return fallback;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
73
|
+
prototype[method] = wrappers[method];
|
|
74
|
+
}
|
|
75
|
+
const scope = { originals, roots, wrappers };
|
|
76
|
+
Object.defineProperty(prototype, SCOPE_KEY, { configurable: true, value: scope });
|
|
77
|
+
return scope;
|
|
78
|
+
};
|
|
79
|
+
const uninstallScope = (prototype, scope) => {
|
|
80
|
+
// A method someone wrapped again after us keeps their wrapper, and ours inside it passes calls straight through.
|
|
81
|
+
for (const method of QUERY_METHODS) {
|
|
82
|
+
if (prototype[method] === scope.wrappers[method]) {
|
|
83
|
+
prototype[method] = scope.originals[method];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (QUERY_METHODS.every((method) => prototype[method] === scope.originals[method])) {
|
|
87
|
+
delete prototype[SCOPE_KEY];
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Lets the global document's querySelector, querySelectorAll, getElementById and getElementsByClassName also find a
|
|
92
|
+
* hosted module's elements, for a host to call on each module shadow root it creates and stop on unmount. Each call
|
|
93
|
+
* first runs as normal and keeps its result whenever it finds anything; only an empty result on the shadow root's own
|
|
94
|
+
* document falls back to the registered roots, newest first, and the first root with a match answers. Fallback results
|
|
95
|
+
* of querySelectorAll and getElementsByClassName are static NodeLists, not live collections. getElementsByTagName is
|
|
96
|
+
* left alone: tag queries on a page nearly always match the host's own elements. The prototype is patched once for
|
|
97
|
+
* any number of roots and restored when the last one stops.
|
|
98
|
+
*/
|
|
99
|
+
export const scopeDocumentQueries = (root) => {
|
|
100
|
+
const document = root.ownerDocument;
|
|
101
|
+
const prototype = queryPrototypeOf(document);
|
|
102
|
+
const scope = installScope(prototype);
|
|
103
|
+
const roots = scope.roots.get(document) ?? [];
|
|
104
|
+
roots.push(root);
|
|
105
|
+
scope.roots.set(document, roots);
|
|
106
|
+
let stopped = false;
|
|
107
|
+
return () => {
|
|
108
|
+
if (stopped) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
stopped = true;
|
|
112
|
+
const current = scope.roots.get(document) ?? [];
|
|
113
|
+
const index = current.lastIndexOf(root);
|
|
114
|
+
if (index !== -1) {
|
|
115
|
+
current.splice(index, 1);
|
|
116
|
+
}
|
|
117
|
+
if (current.length === 0) {
|
|
118
|
+
scope.roots.delete(document);
|
|
119
|
+
}
|
|
120
|
+
if (scope.roots.size === 0) {
|
|
121
|
+
uninstallScope(prototype, scope);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=document-queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-queries.js","sourceRoot":"","sources":["../src/document-queries.ts"],"names":[],"mappings":"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","sourcesContent":["// A module that queried its own elements through the global document (`document.querySelector('.week-wrapper')`, or\n// Angular's injected DOCUMENT) found them on its own page. Hosted, they live in the module's shadow root, which document\n// queries never enter, so the same calls return null or nothing. scopeDocumentQueries() lets those calls fall back to\n// the module's shadow root, and only when the document itself has no match.\n\ntype TQueryMethod = 'getElementById' | 'getElementsByClassName' | 'querySelector' | 'querySelectorAll';\n\ntype TQuery = (this: Document, ...args: unknown[]) => unknown;\n\ninterface IDocumentQueryScope {\n originals: Record<TQueryMethod, TQuery>;\n roots: Map<Document, ShadowRoot[]>;\n wrappers: Record<TQueryMethod, TQuery>;\n}\n\nconst QUERY_METHODS: readonly TQueryMethod[] = ['getElementById', 'getElementsByClassName', 'querySelector', 'querySelectorAll'];\n// Shared through the prototype under a registry symbol, so two copies of this package on one page wrap it only once.\nconst SCOPE_KEY = Symbol.for('edynamix.mf-isolation.document-query-scope');\n\ntype TQueryPrototype = Record<TQueryMethod, TQuery> & { [SCOPE_KEY]?: IDocumentQueryScope };\n\nconst escapeClassName = (name: string): string => {\n return name\n .replace(/[^\\w-]/g, (character) => `\\\\${character}`)\n .replace(/^(-?)(\\d)/, (_match, dash: string, digit: string) => `${dash}\\\\3${digit} `);\n};\n\nconst isEmpty = (result: unknown): boolean => {\n return result === null || (typeof result === 'object' && result !== null && 'length' in result && result.length === 0);\n};\n\nconst fallbackIn = (root: ShadowRoot, method: TQueryMethod, args: unknown[]): unknown => {\n const [query] = args;\n if (typeof query !== 'string') {\n return undefined;\n }\n switch (method) {\n case 'getElementById':\n return root.getElementById(query);\n case 'querySelector':\n return root.querySelector(query);\n case 'querySelectorAll':\n return root.querySelectorAll(query);\n case 'getElementsByClassName': {\n const classSelector = query.split(/\\s+/).filter(Boolean).map(escapeClassName).join('.');\n return classSelector ? root.querySelectorAll(`.${classSelector}`) : undefined;\n }\n }\n};\n\n// The method's own home on the prototype chain (Document.prototype in browsers), found from the document so the right\n// realm is patched: another window's or frame's Document is a different prototype and is never touched.\nconst queryPrototypeOf = (document: Document): TQueryPrototype => {\n let prototype: object | null = Object.getPrototypeOf(document);\n while (prototype && !Object.hasOwn(prototype, 'querySelector')) {\n prototype = Object.getPrototypeOf(prototype);\n }\n if (!prototype) {\n throw new Error('Cannot scope document queries: the document has no querySelector on its prototype chain.');\n }\n return prototype as TQueryPrototype;\n};\n\nconst installScope = (prototype: TQueryPrototype): IDocumentQueryScope => {\n const existing = prototype[SCOPE_KEY];\n if (existing) {\n return existing;\n }\n\n const roots = new Map<Document, ShadowRoot[]>();\n const originals = {} as Record<TQueryMethod, TQuery>;\n const wrappers = {} as Record<TQueryMethod, TQuery>;\n for (const method of QUERY_METHODS) {\n const original = prototype[method];\n originals[method] = original;\n // A function expression keeps `this`: calls on any other document, or that find something, return the original result.\n wrappers[method] = function (this: Document, ...args: unknown[]): unknown {\n const result = original.apply(this, args);\n const scopedRoots = roots.get(this);\n if (!scopedRoots || !isEmpty(result)) {\n return result;\n }\n for (let index = scopedRoots.length - 1; index >= 0; index -= 1) {\n const root = scopedRoots[index];\n const fallback = root ? fallbackIn(root, method, args) : undefined;\n if (fallback !== undefined && !isEmpty(fallback)) {\n return fallback;\n }\n }\n return result;\n };\n prototype[method] = wrappers[method];\n }\n\n const scope: IDocumentQueryScope = { originals, roots, wrappers };\n Object.defineProperty(prototype, SCOPE_KEY, { configurable: true, value: scope });\n return scope;\n};\n\nconst uninstallScope = (prototype: TQueryPrototype, scope: IDocumentQueryScope): void => {\n // A method someone wrapped again after us keeps their wrapper, and ours inside it passes calls straight through.\n for (const method of QUERY_METHODS) {\n if (prototype[method] === scope.wrappers[method]) {\n prototype[method] = scope.originals[method];\n }\n }\n if (QUERY_METHODS.every((method) => prototype[method] === scope.originals[method])) {\n delete prototype[SCOPE_KEY];\n }\n};\n\n/**\n * Lets the global document's querySelector, querySelectorAll, getElementById and getElementsByClassName also find a\n * hosted module's elements, for a host to call on each module shadow root it creates and stop on unmount. Each call\n * first runs as normal and keeps its result whenever it finds anything; only an empty result on the shadow root's own\n * document falls back to the registered roots, newest first, and the first root with a match answers. Fallback results\n * of querySelectorAll and getElementsByClassName are static NodeLists, not live collections. getElementsByTagName is\n * left alone: tag queries on a page nearly always match the host's own elements. The prototype is patched once for\n * any number of roots and restored when the last one stops.\n */\nexport const scopeDocumentQueries = (root: ShadowRoot): (() => void) => {\n const document = root.ownerDocument;\n const prototype = queryPrototypeOf(document);\n const scope = installScope(prototype);\n const roots = scope.roots.get(document) ?? [];\n roots.push(root);\n scope.roots.set(document, roots);\n\n let stopped = false;\n return () => {\n if (stopped) {\n return;\n }\n stopped = true;\n const current = scope.roots.get(document) ?? [];\n const index = current.lastIndexOf(root);\n if (index !== -1) {\n current.splice(index, 1);\n }\n if (current.length === 0) {\n scope.roots.delete(document);\n }\n if (scope.roots.size === 0) {\n uninstallScope(prototype, scope);\n }\n };\n};\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface IDocumentRootRescope {
|
|
2
|
+
/** The selector list with exact document-root compounds moved onto `:host`; unchanged when nothing could move. */
|
|
3
|
+
selectorText: string;
|
|
4
|
+
/** Selectors led by a document root with conditions of its own (`html.dark`, `body#app`, `:root[dir]`), left alone. */
|
|
5
|
+
qualifiedRoots: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Moves the selectors of a runtime rule that start at the document root onto the shadow host, as the PostCSS plugins
|
|
9
|
+
* do at build time for root-only selectors: `html`, `body`, `:root` and `html body` become `:host`, and `html .x`,
|
|
10
|
+
* `body > .x` or `:root .x` become `:host .x` and `:host > .x`. A root with conditions of its own is left alone and
|
|
11
|
+
* counted, and a list that already names `:host` or the module boundary is returned as it is.
|
|
12
|
+
*/
|
|
13
|
+
export declare const rescopeDocumentRootSelectors: (selectorText: string) => IDocumentRootRescope;
|
|
14
|
+
//# sourceMappingURL=document-root-selectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document-root-selectors.d.ts","sourceRoot":"","sources":["../src/document-root-selectors.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,oBAAoB;IACnC,kHAAkH;IAClH,YAAY,EAAE,MAAM,CAAC;IACrB,uHAAuH;IACvH,cAAc,EAAE,MAAM,CAAC;CACxB;AA2GD;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,GAAI,cAAc,MAAM,KAAG,oBAgBnE,CAAC","sourcesContent":["// Inside a shadow root no element is the document's <html> or <body>, so a rule aimed at the document root never\n// matches. The build-time PostCSS plugins move such rules onto the module boundary; CSS a library injects at runtime\n// skips that step. Angular Material's `html { --mat-focus-indicator-display: block; }` is one: hosted, the variable never\n// reached Material's components and keyboard focus rings stayed off. This is the runtime counterpart for those rules.\n\nexport interface IDocumentRootRescope {\n /** The selector list with exact document-root compounds moved onto `:host`; unchanged when nothing could move. */\n selectorText: string;\n /** Selectors led by a document root with conditions of its own (`html.dark`, `body#app`, `:root[dir]`), left alone. */\n qualifiedRoots: number;\n}\n\nconst ROOT_COMPOUND_PATTERN = /^(?:html|body|:root)$/i;\nconst ROOT_LED_COMPOUND_PATTERN = /^(?:html|body)(?![\\w-])|^:root(?![\\w-])/i;\n// A cheap first look: a document root at the start of a selector or after a combinator or comma.\nconst ROOT_SELECTOR_HINT_PATTERN = /(?:^|[\\s,>+~])(?:html|body|:root)(?![\\w-])/i;\n// Rules that already name the boundary were scoped at build time or by the host, and stay as they are.\nconst SCOPED_SELECTOR_PATTERN = /data-edyn-mf|:host/i;\nconst COMBINATOR_PATTERN = /^[\\s>+~]$/;\n\ninterface IComplexSelector {\n combinators: string[];\n compounds: string[];\n}\n\n// Splits at top level only: commas, combinators and spaces inside (), [] and strings belong to their compound.\nconst splitTopLevel = (text: string, visit: (character: string, depth: number) => boolean): string[] => {\n const parts: string[] = [];\n let current = '';\n let depth = 0;\n let quote: string | undefined;\n for (let index = 0; index < text.length; index += 1) {\n const character = text[index] ?? '';\n if (character === '\\\\') {\n current += character + (text[index + 1] ?? '');\n index += 1;\n continue;\n }\n if (quote) {\n quote = character === quote ? undefined : quote;\n current += character;\n continue;\n }\n if (character === '\"' || character === \"'\") {\n quote = character;\n } else if (character === '(' || character === '[') {\n depth += 1;\n } else if (character === ')' || character === ']') {\n depth -= 1;\n }\n if (visit(character, depth)) {\n parts.push(current);\n current = character;\n continue;\n }\n current += character;\n }\n parts.push(current);\n return parts;\n};\n\nconst splitSelectorList = (selectorText: string): string[] => {\n return splitTopLevel(selectorText, (character, depth) => depth === 0 && character === ',').map((part) => part.replace(/^,/, '').trim());\n};\n\nconst parseComplexSelector = (selector: string): IComplexSelector | undefined => {\n const tokens = splitTopLevel(selector, (character, depth) => depth === 0 && COMBINATOR_PATTERN.test(character));\n const compounds: string[] = [];\n const combinators: string[] = [];\n let combinator: string | undefined;\n for (const token of tokens) {\n const separator = token[0] ?? '';\n const compound = COMBINATOR_PATTERN.test(separator) ? token.slice(1) : token;\n if (COMBINATOR_PATTERN.test(separator) && separator.trim()) {\n combinator = separator;\n } else if (COMBINATOR_PATTERN.test(separator)) {\n combinator ??= ' ';\n }\n if (!compound) {\n continue;\n }\n if (compounds.length > 0) {\n combinators.push(combinator ?? ' ');\n } else if (combinator?.trim()) {\n // A relative selector (`> .x` in nested CSS) has no document root to move.\n return undefined;\n }\n compounds.push(compound);\n combinator = undefined;\n }\n return compounds.length > 0 ? { combinators, compounds } : undefined;\n};\n\nconst rescopeComplexSelector = (selector: string): { qualified: boolean; selector: string } => {\n const parsed = parseComplexSelector(selector);\n const first = parsed?.compounds[0];\n if (!parsed || !first || !ROOT_LED_COMPOUND_PATTERN.test(first)) {\n return { qualified: false, selector };\n }\n\n let rootCompounds = 0;\n while (ROOT_COMPOUND_PATTERN.test(parsed.compounds[rootCompounds] ?? '')) {\n rootCompounds += 1;\n }\n const next = parsed.compounds[rootCompounds];\n if (rootCompounds === 0 || (next !== undefined && ROOT_LED_COMPOUND_PATTERN.test(next))) {\n return { qualified: true, selector };\n }\n\n let rescoped = ':host';\n for (let index = rootCompounds; index < parsed.compounds.length; index += 1) {\n const combinator = parsed.combinators[index - 1] ?? ' ';\n rescoped += `${combinator === ' ' ? ' ' : ` ${combinator} `}${parsed.compounds[index]}`;\n }\n return { qualified: false, selector: rescoped };\n};\n\n/**\n * Moves the selectors of a runtime rule that start at the document root onto the shadow host, as the PostCSS plugins\n * do at build time for root-only selectors: `html`, `body`, `:root` and `html body` become `:host`, and `html .x`,\n * `body > .x` or `:root .x` become `:host .x` and `:host > .x`. A root with conditions of its own is left alone and\n * counted, and a list that already names `:host` or the module boundary is returned as it is.\n */\nexport const rescopeDocumentRootSelectors = (selectorText: string): IDocumentRootRescope => {\n if (!ROOT_SELECTOR_HINT_PATTERN.test(selectorText) || SCOPED_SELECTOR_PATTERN.test(selectorText) || selectorText.includes('&')) {\n return { qualifiedRoots: 0, selectorText };\n }\n\n let qualifiedRoots = 0;\n const selectors: string[] = [];\n for (const part of splitSelectorList(selectorText)) {\n const { qualified, selector } = rescopeComplexSelector(part);\n qualifiedRoots += qualified ? 1 : 0;\n if (!selectors.includes(selector)) {\n selectors.push(selector);\n }\n }\n const rescoped = selectors.join(', ');\n return { qualifiedRoots, selectorText: rescoped === splitSelectorList(selectorText).join(', ') ? selectorText : rescoped };\n};\n"]}
|