From 262cef2487c7494bd8d23b4ab27bfcdf1870a111 Mon Sep 17 00:00:00 2001 From: Erika <3019731+Princesseuh@users.noreply.github.com> Date: Tue, 31 Oct 2023 23:35:32 +0100 Subject: [PATCH] refactor: dev overlay to make it easier to work with VT (#8966) --- .changeset/healthy-hornets-kiss.md | 5 + packages/astro/src/@types/astro.ts | 14 + .../runtime/client/dev-overlay/entrypoint.ts | 84 +++ .../src/runtime/client/dev-overlay/overlay.ts | 478 ++++++++---------- .../client/dev-overlay/plugins/astro.ts | 3 +- .../client/dev-overlay/plugins/audit.ts | 55 +- .../dev-overlay/plugins/utils/highlight.ts | 9 +- .../client/dev-overlay/plugins/xray.ts | 27 +- .../client/dev-overlay/ui-library/tooltip.ts | 2 +- .../src/vite-plugin-astro-server/route.ts | 2 +- 10 files changed, 386 insertions(+), 293 deletions(-) create mode 100644 .changeset/healthy-hornets-kiss.md create mode 100644 packages/astro/src/runtime/client/dev-overlay/entrypoint.ts diff --git a/.changeset/healthy-hornets-kiss.md b/.changeset/healthy-hornets-kiss.md new file mode 100644 index 000000000000..df69c840cebf --- /dev/null +++ b/.changeset/healthy-hornets-kiss.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix Dev Overlay not working properly when view transitions are enabled diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 7b08808a5012..4ae5e7138656 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -21,7 +21,11 @@ import type { TSConfig } from '../core/config/tsconfig.js'; import type { AstroCookies } from '../core/cookies/index.js'; import type { ResponseWithEncoding } from '../core/endpoint/index.js'; import type { AstroIntegrationLogger, Logger, LoggerLevel } from '../core/logger/core.js'; +import type { AstroDevOverlay, DevOverlayCanvas } from '../runtime/client/dev-overlay/overlay.js'; +import type { DevOverlayHighlight } from '../runtime/client/dev-overlay/ui-library/highlight.js'; import type { Icon } from '../runtime/client/dev-overlay/ui-library/icons.js'; +import type { DevOverlayTooltip } from '../runtime/client/dev-overlay/ui-library/tooltip.js'; +import type { DevOverlayWindow } from '../runtime/client/dev-overlay/ui-library/window.js'; import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server/index.js'; import type { OmitIndexSignature, Simplify } from '../type-utils.js'; import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js'; @@ -2322,3 +2326,13 @@ export type DevOverlayMetadata = Window & root: string; }; }; + +declare global { + interface HTMLElementTagNameMap { + 'astro-dev-overlay': AstroDevOverlay; + 'astro-dev-overlay-window': DevOverlayWindow; + 'astro-dev-overlay-plugin-canvas': DevOverlayCanvas; + 'astro-dev-overlay-tooltip': DevOverlayTooltip; + 'astro-dev-overlay-highlight': DevOverlayHighlight; + } +} diff --git a/packages/astro/src/runtime/client/dev-overlay/entrypoint.ts b/packages/astro/src/runtime/client/dev-overlay/entrypoint.ts new file mode 100644 index 000000000000..fe7efcccc232 --- /dev/null +++ b/packages/astro/src/runtime/client/dev-overlay/entrypoint.ts @@ -0,0 +1,84 @@ +import type { DevOverlayPlugin as DevOverlayPluginDefinition } from '../../../@types/astro.js'; +import { type AstroDevOverlay, type DevOverlayPlugin } from './overlay.js'; + +let overlay: AstroDevOverlay; + +document.addEventListener('DOMContentLoaded', async () => { + const [ + { loadDevOverlayPlugins }, + { default: astroDevToolPlugin }, + { default: astroAuditPlugin }, + { default: astroXrayPlugin }, + { AstroDevOverlay, DevOverlayCanvas }, + { DevOverlayCard }, + { DevOverlayHighlight }, + { DevOverlayTooltip }, + { DevOverlayWindow }, + ] = await Promise.all([ + // @ts-expect-error + import('astro:dev-overlay'), + import('./plugins/astro.js'), + import('./plugins/audit.js'), + import('./plugins/xray.js'), + import('./overlay.js'), + import('./ui-library/card.js'), + import('./ui-library/highlight.js'), + import('./ui-library/tooltip.js'), + import('./ui-library/window.js'), + ]); + + // Register custom elements + customElements.define('astro-dev-overlay', AstroDevOverlay); + customElements.define('astro-dev-overlay-window', DevOverlayWindow); + customElements.define('astro-dev-overlay-plugin-canvas', DevOverlayCanvas); + customElements.define('astro-dev-overlay-tooltip', DevOverlayTooltip); + customElements.define('astro-dev-overlay-highlight', DevOverlayHighlight); + customElements.define('astro-dev-overlay-card', DevOverlayCard); + + overlay = document.createElement('astro-dev-overlay'); + + const preparePlugin = ( + pluginDefinition: DevOverlayPluginDefinition, + builtIn: boolean + ): DevOverlayPlugin => { + const eventTarget = new EventTarget(); + const plugin = { + ...pluginDefinition, + builtIn: builtIn, + active: false, + status: 'loading' as const, + eventTarget: eventTarget, + }; + + // Events plugins can send to the overlay to update their status + eventTarget.addEventListener('plugin-notification', (evt) => { + const target = overlay.shadowRoot?.querySelector(`[data-plugin-id="${plugin.id}"]`); + if (!target) return; + + let newState = true; + if (evt instanceof CustomEvent) { + newState = evt.detail.state ?? true; + } + + target.querySelector('.notification')?.toggleAttribute('data-active', newState); + }); + + return plugin; + }; + + const customPluginsDefinitions = (await loadDevOverlayPlugins()) as DevOverlayPluginDefinition[]; + const plugins: DevOverlayPlugin[] = [ + ...[astroDevToolPlugin, astroXrayPlugin, astroAuditPlugin].map((pluginDef) => + preparePlugin(pluginDef, true) + ), + ...customPluginsDefinitions.map((pluginDef) => preparePlugin(pluginDef, false)), + ]; + + overlay.plugins = plugins; + + document.body.append(overlay); + + document.addEventListener('astro:after-swap', () => { + document.body.append(overlay); + }); +}); diff --git a/packages/astro/src/runtime/client/dev-overlay/overlay.ts b/packages/astro/src/runtime/client/dev-overlay/overlay.ts index e93f3bcac79f..00ca35569be0 100644 --- a/packages/astro/src/runtime/client/dev-overlay/overlay.ts +++ b/packages/astro/src/runtime/client/dev-overlay/overlay.ts @@ -1,79 +1,47 @@ /* eslint-disable no-console */ -// @ts-expect-error -import { loadDevOverlayPlugins } from 'astro:dev-overlay'; import type { DevOverlayPlugin as DevOverlayPluginDefinition } from '../../../@types/astro.js'; -import astroDevToolPlugin from './plugins/astro.js'; -import astroAuditPlugin from './plugins/audit.js'; -import astroXrayPlugin from './plugins/xray.js'; -import { DevOverlayCard } from './ui-library/card.js'; -import { DevOverlayHighlight } from './ui-library/highlight.js'; import { getIconElement, isDefinedIcon, type Icon } from './ui-library/icons.js'; -import { DevOverlayTooltip } from './ui-library/tooltip.js'; -import { DevOverlayWindow } from './ui-library/window.js'; -type DevOverlayPlugin = DevOverlayPluginDefinition & { +export type DevOverlayPlugin = DevOverlayPluginDefinition & { + builtIn: boolean; active: boolean; status: 'ready' | 'loading' | 'error'; eventTarget: EventTarget; }; -document.addEventListener('DOMContentLoaded', async () => { - const WS_EVENT_NAME = 'astro-dev-overlay'; - const HOVER_DELAY = 750; - - const builtinPlugins: DevOverlayPlugin[] = [ - astroDevToolPlugin, - astroXrayPlugin, - astroAuditPlugin, - ].map((plugin) => ({ - ...plugin, - active: false, - status: 'loading', - eventTarget: new EventTarget(), - })); - - const customPluginsImports = (await loadDevOverlayPlugins()) as DevOverlayPluginDefinition[]; - const customPlugins: DevOverlayPlugin[] = []; - customPlugins.push( - ...customPluginsImports.map((plugin) => ({ - ...plugin, - active: false, - status: 'loading' as const, - eventTarget: new EventTarget(), - })) - ); - - const plugins: DevOverlayPlugin[] = [...builtinPlugins, ...customPlugins]; - - for (const plugin of plugins) { - plugin.eventTarget.addEventListener('plugin-notification', (evt) => { - const target = overlay.shadowRoot?.querySelector(`[data-plugin-id="${plugin.id}"]`); - if (!target) return; - - let newState = true; - if (evt instanceof CustomEvent) { - newState = evt.detail.state ?? true; - } +const WS_EVENT_NAME = 'astro-dev-overlay'; - target.querySelector('.notification')?.toggleAttribute('data-active', newState); - }); - } +export class AstroDevOverlay extends HTMLElement { + shadowRoot: ShadowRoot; + hoverTimeout: number | undefined; + isHidden: () => boolean = () => this.devOverlay?.hasAttribute('data-hidden') ?? true; + devOverlay: HTMLDivElement | undefined; + plugins: DevOverlayPlugin[] = []; + HOVER_DELAY = 750; + hasBeenInitialized = false; - class AstroDevOverlay extends HTMLElement { - shadowRoot: ShadowRoot; - hoverTimeout: number | undefined; - isHidden: () => boolean = () => this.devOverlay?.hasAttribute('data-hidden') ?? true; - devOverlay: HTMLDivElement | undefined; - - constructor() { - super(); - this.shadowRoot = this.attachShadow({ mode: 'open' }); - } + constructor() { + super(); + this.shadowRoot = this.attachShadow({ mode: 'open' }); + } - // connect component - async connectedCallback() { + // Happens whenever the component is connected to the DOM + // When view transitions are enabled, this happens every time the view changes + async connectedCallback() { + if (!this.hasBeenInitialized) { this.shadowRoot.innerHTML = `