[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/FluentRead/FluentRead/main/userscript/shadow-root.ts [Back]  [Original]

type ShadowRootUiOptions = {
    name: string;
    position?: string;
    alignment?: string;
    zIndex?: number;
    mode?: ShadowRootMode;
    inheritStyles?: boolean;
    isolateEvents?: string[];
    css?: string;
    viewport?: boolean;
    scopeRoot?: boolean;
    onMount(container: HTMLElement): T;
    onRemove?(mounted?: T): void;
};

export interface ShadowRootContentScriptUi {
    shadowHost: HTMLElement;
    shadow: ShadowRoot;
    uiContainer: HTMLElement;
    mounted?: T;
    mount(): void;
    remove(): void;
}

function installStyles(shadow: ShadowRoot, localCss = '', scopeRoot = false): void {
    //  CSS  CSS  ShadowRoot
    const css = [globalThis.__fluentReadUserscriptCss || '', localCss].filter(Boolean).join('\n');
    if (!css) return;
    const style = document.createElement('style');
    style.setAttribute('data-fluent-read-userscript-styles', 'true');
    // Options  :root / :root.dark  ShadowRoot 
    //  host document
    style.textContent = scopeRoot
        ? css.replace(/:root((?:(?:\.[\w-]+)|(?:\[[^\]]+\]))*)/gu, (_match, suffix: string) => (
            suffix ? `:host(${suffix})` : ':host'
        ))
        : css;
    shadow.appendChild(style);
}

/**  userscript runtime  WXT  Shadow UI  */
export async function createShadowRootUi(
    _ctx: unknown,
    options: ShadowRootUiOptions,
): Promise {
    const shadowHost = document.createElement('div');
    shadowHost.setAttribute('data-fluent-read-userscript-host', options.name);
    //  ShadowRoot 
    shadowHost.style.cssText = [
        'all: initial !important',
        'display: block !important',
        'position: fixed !important',
        'left: 0 !important',
        'top: 0 !important',
        options.viewport ? 'width: 100vw !important' : 'width: 0 !important',
        options.viewport ? 'height: 100vh !important' : 'height: 0 !important',
        options.viewport ? 'overflow: hidden !important' : 'overflow: visible !important',
        'pointer-events: auto !important',
        `z-index: ${options.zIndex ?? 2_147_483_647} !important`,
    ].join(';');

    const shadow = shadowHost.attachShadow({mode: options.mode || 'open'});
    installStyles(shadow, options.css, options.scopeRoot);
    const container = document.createElement('div');
    container.setAttribute('data-fluent-read-userscript-container', options.name);
    container.style.cssText = options.viewport
        ? 'all: initial; width: 100%; height: 100%; overflow: hidden; pointer-events: auto;'
        : 'all: initial; width: 0; height: 0; overflow: visible; pointer-events: auto;';
    shadow.appendChild(container);

    //  ShadowRoot 
    // Element Plus select Shadow DOM
    // 
    for (const eventName of options.isolateEvents || []) {
        shadow.addEventListener(eventName, (event) => event.stopPropagation());
    }

    let isMounted = false;
    const ui: ShadowRootContentScriptUi = {
        shadowHost,
        shadow,
        uiContainer: container,
        mount() {
            if (isMounted) return;
            isMounted = true;
            (document.documentElement || document.body).appendChild(shadowHost);
            ui.mounted = options.onMount(container);
        },
        remove() {
            if (!isMounted) return;
            isMounted = false;
            options.onRemove?.(ui.mounted);
            ui.mounted = undefined;
            shadowHost.remove();
        },
    };
    return ui;
}

Web Proxy Viewer  |  New URL  |  Original Page