diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 0f36fce..183b59a 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -49,9 +49,7 @@ "devDependencies": { "@phase2/outline-adopted-stylesheets-controller": "^1.0.4", "@phase2/outline-config": "^0.0.14", - "@phase2/outline-core": "^0.2.7", "@phase2/outline-core-alert": "^0.0.4", - "@phase2/outline-core-link": "^0.0.16", "@phase2/outline-docs": "^0.0.21", "@storybook/addon-essentials": "^7.6.17", "@storybook/addon-links": "^7.6.17", diff --git a/packages/storybook/src/components/shared/outline-link/README.md b/packages/storybook/src/components/shared/outline-example/README.md similarity index 100% rename from packages/storybook/src/components/shared/outline-link/README.md rename to packages/storybook/src/components/shared/outline-example/README.md diff --git a/packages/storybook/src/components/shared/outline-example/docs/outline-example.mdx b/packages/storybook/src/components/shared/outline-example/docs/outline-example.mdx new file mode 100644 index 0000000..4d3eef6 --- /dev/null +++ b/packages/storybook/src/components/shared/outline-example/docs/outline-example.mdx @@ -0,0 +1,28 @@ +import { Meta } from '@storybook/addon-docs'; +import { OutlineExample } from '../outline-example'; + + + +# `OutlineExample`: `outline-example` + +> This is an `outline-example` component. +> This component is a wrapper around an anchor tag that provides **encapsulated** and **global** styles for links. +> This component extends `LitElement` and adds custom styling. + +Outline + +```html + + Outline + +``` diff --git a/packages/storybook/src/components/shared/outline-example/outline-example.ts b/packages/storybook/src/components/shared/outline-example/outline-example.ts new file mode 100644 index 0000000..6a4ba0e --- /dev/null +++ b/packages/storybook/src/components/shared/outline-example/outline-example.ts @@ -0,0 +1,49 @@ +import { customElement } from 'lit/decorators.js'; +import { LitElement, html } from 'lit'; + +import globalStyles from './styles/outline-example.global.css?inline'; +import encapsulatedStyles from './styles/outline-example.encapsulated.css?inline'; +import { AdoptedStyleSheets } from '../../../controllers/adopted-stylesheets'; + +/** + * Example component. + * + * @see {@link https://lit.dev/docs/components/defining/ | Defining components} + * @see {@link https://www.npmjs.com/package/@phase2/outline-example-alert | @phase2/outline-example-alert } + * @element outline-example + * @extends OutlineExample + */ +@customElement('outline-example') +export class OutlineExample extends LitElement { + adoptedStyleSheets = new AdoptedStyleSheets(this, { + globalCSS: globalStyles, + encapsulatedCSS: encapsulatedStyles, + }); + + /** + * Wrapping the rendered content in a div with the class "encapsulated-container". + * This demonstrates the difference between encapsulated and light DOM styling. + * + * @category rendering + * @returns {TemplateResult} The template to render. + */ + render() { + return html` +
+ +
+ `; + } +} + +/** + * TypeScript declaration extends the HTMLElementTagNameMap interface, adding the web component. + * This enhances type checking and autocompletion in IDEs. + * + * @see {@link https://lit.dev/docs/components/defining/#typescript-typings | Providing good TypeScript typings} + */ +declare global { + interface HTMLElementTagNameMap { + 'outline-example': OutlineExample; + } +} diff --git a/packages/storybook/src/components/shared/outline-link/styles/outline-link.encapsulated.css b/packages/storybook/src/components/shared/outline-example/styles/outline-example.encapsulated.css similarity index 100% rename from packages/storybook/src/components/shared/outline-link/styles/outline-link.encapsulated.css rename to packages/storybook/src/components/shared/outline-example/styles/outline-example.encapsulated.css diff --git a/packages/storybook/src/components/shared/outline-link/styles/outline-link.global.css b/packages/storybook/src/components/shared/outline-example/styles/outline-example.global.css similarity index 53% rename from packages/storybook/src/components/shared/outline-link/styles/outline-link.global.css rename to packages/storybook/src/components/shared/outline-example/styles/outline-example.global.css index 5edb5c1..d68b37f 100644 --- a/packages/storybook/src/components/shared/outline-link/styles/outline-link.global.css +++ b/packages/storybook/src/components/shared/outline-example/styles/outline-example.global.css @@ -1,4 +1,4 @@ -outline-link { +outline-example { /* This is just here to demonstrate using a nested import in a CSS file. */ - @nested-import './outline-link.imported.css'; + @nested-import './outline-example.imported.css'; } diff --git a/packages/storybook/src/components/shared/outline-link/styles/outline-link.imported.css b/packages/storybook/src/components/shared/outline-example/styles/outline-example.imported.css similarity index 100% rename from packages/storybook/src/components/shared/outline-link/styles/outline-link.imported.css rename to packages/storybook/src/components/shared/outline-example/styles/outline-example.imported.css diff --git a/packages/storybook/src/components/shared/outline-link/docs/outline-link.mdx b/packages/storybook/src/components/shared/outline-link/docs/outline-link.mdx deleted file mode 100644 index d464cd0..0000000 --- a/packages/storybook/src/components/shared/outline-link/docs/outline-link.mdx +++ /dev/null @@ -1,28 +0,0 @@ -import { Meta } from '@storybook/addon-docs'; -import { OutlineLink } from '../outline-link'; - - - -# `OutlineLink`: `outline-link` - -> This is an `outline-link` component. -> This component is a wrapper around an anchor tag that provides a consistent style for links. -> This component extends `OutlineCoreLink` and adds custom styling. - -Outline - -```html - - Outline - -``` diff --git a/packages/storybook/src/components/shared/outline-link/outline-link.ts b/packages/storybook/src/components/shared/outline-link/outline-link.ts deleted file mode 100644 index 2dbb9f2..0000000 --- a/packages/storybook/src/components/shared/outline-link/outline-link.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { customElement } from 'lit/decorators.js'; -import { html } from 'lit'; - -import { OutlineCoreLink } from '@phase2/outline-core-link'; -import globalStyles from './styles/outline-link.global.css?inline'; -import encapsulatedStyles from './styles/outline-link.encapsulated.css?inline'; -import { AdoptedStylesheets } from '@phase2/outline-adopted-stylesheets-controller'; - -/** - * Link component. - * - * @see {@link https://lit.dev/docs/components/defining/ | Defining components} - * @see {@link https://www.npmjs.com/package/@phase2/outline-core-alert | @phase2/outline-core-alert } - * @element outline-link - * @extends OutlineCoreLink - */ -@customElement('outline-link') -export class OutlineLink extends OutlineCoreLink { - GlobalStylesheets: AdoptedStylesheets | undefined = new AdoptedStylesheets( - this, - globalStyles, - document, - ); - - EncapsulatedStylesheets: AdoptedStylesheets | undefined; - - createRenderRoot() { - const root = super.createRenderRoot(); - this.EncapsulatedStylesheets = this.shadowRoot - ? new AdoptedStylesheets(this, encapsulatedStyles, this.shadowRoot) - : undefined; - return root; - } - - /** - * Overrides the render method from the parent class to wrap the rendered content - * in a div with the class "encapsulated-container". This demonstrates the difference - * between encapsulated and light DOM styling. - * - * @category rendering - * @returns {TemplateResult} The template to render. - */ - render() { - return html`
${super.render()}
`; - } -} - -/** - * TypeScript declaration extends the HTMLElementTagNameMap interface, adding the web component. - * This enhances type checking and autocompletion in IDEs. - * - * @see {@link https://lit.dev/docs/components/defining/#typescript-typings | Providing good TypeScript typings} - */ -declare global { - interface HTMLElementTagNameMap { - 'outline-link': OutlineLink; - } -} diff --git a/packages/storybook/src/controllers/adopted-stylesheets.ts b/packages/storybook/src/controllers/adopted-stylesheets.ts new file mode 100644 index 0000000..c87dbb6 --- /dev/null +++ b/packages/storybook/src/controllers/adopted-stylesheets.ts @@ -0,0 +1,82 @@ +import { ReactiveController, ReactiveControllerHost } from 'lit'; + +/** + * A controller for managing adopted stylesheets in a Lit element. + * This allows for styles to be dynamically applied to the component's + * light DOM or shadow DOM. + */ +export class AdoptedStyleSheets implements ReactiveController { + // The host element that the controller is associated with. + private host: ReactiveControllerHost & HTMLElement; + // An object containing the CSS to be applied globally or encapsulated within the shadow DOM. + private css: { globalCSS?: string; encapsulatedCSS?: string }; + + // A set to track the stylesheets applied to the light DOM. + private static appliedLightDomStylesheets: Set = new Set(); + + /** + * Constructs an instance of the AdoptedStyleSheets controller. + * @param host The host element that the controller will be associated with. + * @param css An object containing optional global and encapsulated CSS strings. + */ + constructor( + host: ReactiveControllerHost & HTMLElement, + css: { + globalCSS?: string; + encapsulatedCSS?: string; + } = {} + ) { + this.host = host; + this.css = css; + this.host.addController(this); // Register this instance as a controller for the host element. + } + + /** + * Applies the given CSS text to the specified target (Document or ShadowRoot). + * @param cssText The CSS text to apply. + * @param target The target where the CSS should be applied. + */ + private applyCssToDom(cssText: string, target: Document | ShadowRoot) { + if (target instanceof Document) { + const store = AdoptedStyleSheets.appliedLightDomStylesheets; + + if (store.has(cssText)) { + // If the stylesheet has already been applied, no further action is required. + return; + } + store.add(cssText); // Store the stylesheet with the provided key. + } + + // Create a new stylesheet and replace its contents with the provided CSS text. + const sheet = new CSSStyleSheet(); + sheet.replaceSync(cssText); + + // Apply the stylesheet to the target's adoptedStyleSheets. + target.adoptedStyleSheets = [...target.adoptedStyleSheets, sheet]; + } + + /** + * Lifecycle callback called when the host element is connected to the document's DOM. + * Applies global and encapsulated CSS to the respective DOM targets. + */ + hostConnected() { + if (this.css.globalCSS) { + this.applyCssToDom(this.css.globalCSS, document); // Apply global CSS to the document if it exists. + } + + // Apply encapsulated CSS to the host's shadow root if it exists. + if (this.css.encapsulatedCSS && this.host.shadowRoot) { + this.applyCssToDom(this.css.encapsulatedCSS, this.host.shadowRoot); // Apply encapsulated CSS to the host's shadow root if it exists. + } + } + + /** + * Lifecycle callback called when the host element is disconnected from the document's DOM. + * Note: When a component with a Shadow DOM is disconnected from the document's DOM, the Shadow DOM is also removed along with the component. + * However, for Light DOM styles, they are not removed here because other instances of the component + * might still be present on the page and require these styles. + */ + hostDisconnected() { + // No action is taken when the host is disconnected. + } +} diff --git a/packages/storybook/src/controllers/resize-controller.ts b/packages/storybook/src/controllers/resize-controller.ts new file mode 100644 index 0000000..0dee9bd --- /dev/null +++ b/packages/storybook/src/controllers/resize-controller.ts @@ -0,0 +1,167 @@ +import { ReactiveControllerHost, ReactiveController } from 'lit'; +import { debounce } from '../utilities/debounce'; + +export type breakpointsRangeType = { + min: number; + max: number; +}; + +interface HostWithResizeController extends ReactiveControllerHost, HTMLElement { + resizeController?: ResizeController; +} + +/** + * ResizeController class + * @implements {ReactiveController} + */ +export class ResizeController implements ReactiveController { + host: HostWithResizeController; + resizeObserver: ResizeObserver; + elementToObserve: Element; + options: { + debounce: number; + breakpoints: number[]; + triggerElement: ReactiveControllerHost & HTMLElement; + }; + currentComponentWidth: number; + currentBreakpointRange: number; + breakpointsRangeArray: breakpointsRangeType[] = []; + + /** + * Create a constructor that takes a host and options + * @param {ReactiveControllerHost & Element} host - The host element + * @param {{debounce?: number; breakpoints?: number[]}} [options={}] - The options object + */ + constructor( + host: HostWithResizeController, + options: { + debounce?: number; + breakpoints?: number[]; + triggerElement?: ReactiveControllerHost & HTMLElement; + } = {} + ) { + // If component (host) already has resizeController attached - return early + // e.g. outline-layout use its ancestor outline-container triggerElement, + // but outline-container already added that controller for itself + if (host.resizeController) { + return; + } + + const defaultOptions = { + debounce: 0, + breakpoints: [768], + triggerElement: host, + }; + + /** + * Remove any undefined variables from options object + */ + const filteredOptionsObject = Object.fromEntries( + Object.entries(options).filter(([_, value]) => value !== undefined) + ); + this.options = { ...defaultOptions, ...filteredOptionsObject }; + + // Store a reference to the host + this.host = host; + // Register for lifecycle updates + host.addController(this); + + this.initializeBreakpointsRangeType(); + } + + /** + * Initialize the breakpoints range array + * + * The default breakpoints array ([768]) will create this breakpoints range array: + * [{min: 0, max: 767}, {min: 768, max: 100000}] + * + * If custom breakpoints array is provided, (for example [768, 1200, 2000]) this breakpoints range array will be created: + * [{min: 0, max: 767}, {min: 768, max: 1199}, {min: 1200, max: 1999}, {min: 2000, max: 100000}] + * + */ + initializeBreakpointsRangeType() { + // This will allow create an additional breakpoint from the last custom breakpoint to 100000 + this.options.breakpoints?.push(100000); + + let minBreakpoint = 0; + this.options.breakpoints?.forEach(breakpoint => { + const newBreakpointRange = { + min: minBreakpoint, + max: breakpoint - 1, + }; + minBreakpoint = breakpoint; + this.breakpointsRangeArray.push(newBreakpointRange); + }); + } + + /** + * Called when the host element is connected to the DOM + */ + hostConnected() { + if (!this.options.triggerElement.style.display) { + // adding `display: block` to :host of component + this.options.triggerElement.style.setProperty( + 'display', + 'var(--style-added-by-resize-controller, block)' + ); + } + // Initialize currentBreakpointRange value + this.currentComponentWidth = this.options.triggerElement.clientWidth; + this.calculateNewBreakpointRange(); + + // Create a new ResizeObserver and pass in the function to be called when the element is resized + this.resizeObserver = new ResizeObserver( + (entries: ResizeObserverEntry[]) => { + // Create a debounced version of the onElementResize function + debounce( + this.onElementResize.bind(this), + this.options.debounce + )(entries); + } + ); + + // Observe the element for size changes + this.resizeObserver.observe(this.options.triggerElement); + } + + /** + * Called when the host element is disconnected from the DOM + */ + hostDisconnected() { + this.resizeObserver.disconnect(); + } + + /** + * Called when the element is resized + * @param {ResizeObserverEntry[]} _entries - The ResizeObserverEntry array + */ + onElementResize(_entries: ResizeObserverEntry[]) { + this.currentComponentWidth = _entries[0].contentRect.width; + + // skip if width is not yet set + if (this.currentComponentWidth) { + this.calculateNewBreakpointRange(); + } + } + + /** + * Calculate the new breakpoint based on the current width + */ + calculateNewBreakpointRange() { + let newBreakpointRange = this.currentBreakpointRange; + + this.breakpointsRangeArray.forEach((breakpoint, index) => { + if ( + this.currentComponentWidth >= breakpoint.min && + this.currentComponentWidth <= breakpoint.max + ) { + newBreakpointRange = index; + } + }); + + if (newBreakpointRange !== this.currentBreakpointRange) { + this.currentBreakpointRange = newBreakpointRange; + this.host.requestUpdate(); + } + } +} diff --git a/packages/storybook/src/controllers/reveal-trigger.ts b/packages/storybook/src/controllers/reveal-trigger.ts new file mode 100644 index 0000000..f2303c8 --- /dev/null +++ b/packages/storybook/src/controllers/reveal-trigger.ts @@ -0,0 +1,81 @@ +import type { ReactiveController, ReactiveControllerHost } from 'lit'; + +/** + * Options for the RevealTrigger controller. + */ +export type Options = { + /** + * The selector for the elements that trigger the reveal. + */ + triggerSelector: string; + /** + * The class to add to the elements when they are revealed. + */ + revealClass: string; + /** + * Whether to retrigger the reveal (by removing the class when the element is no longer in the viewport.) + */ + retrigger?: boolean; +}; + +/** + * The revealTrigger ReactiveController. + * + * This controller finds a class on the host element and adds a class when the element is in the viewport. + * Very useful for animations - just add the class to the element and the animation will play when the element is in the viewport. + * + * @param host The host element + */ +export class RevealTrigger implements ReactiveController { + host: ReactiveControllerHost & HTMLElement; + options: Options; + + constructor(host: ReactiveControllerHost & HTMLElement, options: Options) { + this.options = options; + this.options = { + triggerSelector: options.triggerSelector, + revealClass: options.revealClass, + retrigger: options.retrigger || false, + }; + + // Store a reference to the host + this.host = host; + // Register for lifecycle updates + host.addController(this); + } + + /** + * Called when the host element is connected to the DOM. + */ + hostConnected() { + this.setRevealClasses(this.host, this.options); + } + + /** + * Sets the reveal classes on the host element. + * + * @param host The host element + * @param options The options for the controller + */ + setRevealClasses(host: HTMLElement, options: Options) { + setTimeout(() => { + const elementsToReveal = host.shadowRoot?.querySelectorAll( + options.triggerSelector + ); + + const revealObserver = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (entry.intersectionRatio > 0) { + entry.target.classList.add(options.revealClass); + } else if (options.retrigger) { + entry.target.classList.remove(options.revealClass); + } + }); + }); + + elementsToReveal?.forEach(element => { + revealObserver.observe(element); + }); + }, 0); + } +} diff --git a/packages/storybook/src/index.ts b/packages/storybook/src/index.ts index 45ffa29..6214df1 100644 --- a/packages/storybook/src/index.ts +++ b/packages/storybook/src/index.ts @@ -11,7 +11,7 @@ // import { Component1 } from './components/component1'; // import { Component2 } from './components/component2'; // import { OutlineAlert } from './components/shared/outline-alert/outline-alert'; -import { OutlineLink } from './components/shared/outline-link/outline-link'; +import { OutlineExample } from './components/shared/outline-example/outline-example'; // import { OutlineCoreLink } from '@phase2/outline-core-link'; // Add more component imports as needed... @@ -24,7 +24,7 @@ import { OutlineLink } from './components/shared/outline-link/outline-link'; // Exporting all imported components and controllers for external use export { // OutlineAlert, - OutlineLink, + OutlineExample, // OutlineCoreLink, // Component1, // Component2, diff --git a/packages/storybook/src/utilities/base-path.ts b/packages/storybook/src/utilities/base-path.ts new file mode 100644 index 0000000..22d747b --- /dev/null +++ b/packages/storybook/src/utilities/base-path.ts @@ -0,0 +1,62 @@ +// This file was copied from: https://github.com/shoelace-style/shoelace/blob/next/src/utilities/base-path.ts +// Every 'shoelace' occurrence was replaced with 'outline' + +let basePath = ''; + +/** Sets the library's base path to the specified directory. */ +export function setBasePath(path: string) { + basePath = path; +} + +/** + * Gets the library's base path. + * + * The base path is used to load assets such as icons and images, so it needs to be set for components to work properly. + * By default, this script will look for a script ending in outline.js or outline-eager-loader.js or + * outline-lazy-loader.js and set the base path to the directory that contains that file. + * To override this behavior, you can add the data-outline attribute to any script on the page + * (it probably makes the most sense to attach it to the outline script, but it could also be on a bundle). + * The value can be a local folder or it can point to a CORS-enabled endpoint such as a CDN. + * + * + * + * Alternatively, you can set the base path manually using the exported setBasePath() function. + * + * @param subpath - An optional path to append to the base path. + */ +export function getBasePath(subpath = '') { + if (!basePath) { + const scripts = [ + ...document.getElementsByTagName('script'), + ] as HTMLScriptElement[]; + const configScript = scripts.find(script => + script.hasAttribute('data-outline') + ); + + if (configScript) { + // Use the data-outline attribute + setBasePath(configScript.getAttribute('data-outline')!); + } else { + const fallbackScript = scripts.find(s => { + return ( + /outline(\.min)?\.js($|\?)/.test(s.src) || + /outline-eager-loader(\.min)?\.js($|\?)/.test(s.src) || + /outline-lazy-loader(\.min)?\.js($|\?)/.test(s.src) + ); + }); + let path = ''; + + if (fallbackScript) { + path = fallbackScript.getAttribute('src')!; + } + + setBasePath(path.split('/').slice(0, -1).join('/')); + } + } + + // Return the base path without a trailing slash. If one exists, append the subpath separated by a slash. + return ( + basePath.replace(/\/$/, '') + + (subpath ? `/${subpath.replace(/^\//, '')}` : ``) + ); +} diff --git a/packages/storybook/src/utilities/debounce.ts b/packages/storybook/src/utilities/debounce.ts new file mode 100644 index 0000000..96a45ff --- /dev/null +++ b/packages/storybook/src/utilities/debounce.ts @@ -0,0 +1,28 @@ +/** + * Debounces a function + * @template T + * @param {T} func - The function to debounce + * @param {number} delay - The delay in milliseconds + * @param {boolean} [immediate=false] - Whether to execute the function immediately + * @returns {(...args: Parameters) => void} - The debounced function + */ + +export const debounce = ) => void>( + func: T, + delay: number, + immediate = false +): ((...args: Parameters) => void) => { + let timeoutId: ReturnType | undefined = undefined; + + return function debounced(...args: Parameters) { + const executeFunc = () => func(...args); + + clearTimeout(timeoutId); + + if (immediate && timeoutId === undefined) { + executeFunc(); + } + + timeoutId = setTimeout(executeFunc, delay); + }; +}; diff --git a/packages/storybook/src/utilities/dialog.ts b/packages/storybook/src/utilities/dialog.ts new file mode 100644 index 0000000..060cb70 --- /dev/null +++ b/packages/storybook/src/utilities/dialog.ts @@ -0,0 +1,102 @@ +// code adopted from https://web.dev/building-a-dialog-component/#all-together + +// custom events to be added to +const dialogClosingEvent = new Event('closing'); +const dialogClosedEvent = new Event('closed'); +const dialogOpeningEvent = new Event('opening'); +const dialogOpenedEvent = new Event('opened'); +const dialogRemovedEvent = new Event('removed'); + +// track opening +const dialogAttrObserver = new MutationObserver(mutations => { + mutations.forEach(async mutation => { + if (mutation.attributeName === 'open') { + const dialog = mutation.target as HTMLDialogElement; + + const isOpen = dialog.hasAttribute('open'); + if (!isOpen) return; + + // Limited browser support for inert yet, alternatively we add overflow: hidden to body element + document.querySelector('body')!.style.overflow = 'hidden'; + + // set focus + const focusTarget = dialog.querySelector( + '[autofocus]' + ) as HTMLDialogElement; + focusTarget + ? focusTarget.focus() + : dialog.querySelector('button')?.focus(); + + dialog.dispatchEvent(dialogOpeningEvent); + await animationsComplete(dialog); + dialog.dispatchEvent(dialogOpenedEvent); + } + }); +}); + +// track deletion +const dialogDeleteObserver = new MutationObserver(mutations => { + mutations.forEach(mutation => { + mutation.removedNodes.forEach(removedNode => { + if (removedNode.nodeName === 'DIALOG') { + removedNode.removeEventListener('click', lightDismiss); + removedNode.removeEventListener('close', dialogClose); + removedNode.dispatchEvent(dialogRemovedEvent); + } + }); + }); +}); + +// wait for all dialog animations to complete their promises +const animationsComplete = (element: HTMLDialogElement) => + Promise.allSettled( + element.getAnimations().map(animation => animation.finished) + ); + +// click outside the dialog handler + +const lightDismiss = ({ target: dialog }: Event) => { + const element = dialog as HTMLDialogElement; + if (element.nodeName === 'DIALOG') { + element.close('dismiss'); + } +}; + +const dialogClose = async ({ target: dialog }: Event) => { + document.querySelector('body')!.style.overflow = 'revert'; + const element = dialog as HTMLDialogElement; + + element.dispatchEvent(dialogClosingEvent); + + await animationsComplete(element); + + element.dispatchEvent(dialogClosedEvent); +}; + +// page load dialogs setup +export default async function (dialog: HTMLDialogElement) { + dialog.addEventListener('click', lightDismiss); + dialog.addEventListener('close', dialogClose); + + // Chrome doesn't close dialog with ESCAPE key, adding that functionality manually + dialog.addEventListener('keydown', function (event) { + if (event.key === 'Escape') { + dialog.close('cancel'); + } + }); + + dialogAttrObserver.observe(dialog, { + attributes: true, + }); + + dialogDeleteObserver.observe(document.body, { + attributes: false, + subtree: false, + childList: true, + }); + + // remove loading attribute + // prevent page load @keyframes playing + await animationsComplete(dialog); + dialog.removeAttribute('loading'); +} diff --git a/packages/storybook/yarn.lock b/packages/storybook/yarn.lock index 8ca363d..1e0a75d 100644 --- a/packages/storybook/yarn.lock +++ b/packages/storybook/yarn.lock @@ -2559,16 +2559,6 @@ __metadata: languageName: node linkType: hard -"@phase2/outline-core-link@npm:^0.0.16": - version: 0.0.16 - resolution: "@phase2/outline-core-link@npm:0.0.16" - dependencies: - "@phase2/outline-adopted-stylesheets-controller": "npm:^1.0.4" - lit: "npm:^3.1.2" - checksum: 10c0/ec614c2e53b83caba268789b66797183638a3ee687b0a42386fbdbceff1b0fe4ec6ca6be77afc3ae3b5053c246dedcea97568f152c5c68884c371688ff22e391 - languageName: node - linkType: hard - "@phase2/outline-core@npm:^0.2.7": version: 0.2.7 resolution: "@phase2/outline-core@npm:0.2.7" @@ -2594,9 +2584,7 @@ __metadata: dependencies: "@phase2/outline-adopted-stylesheets-controller": "npm:^1.0.4" "@phase2/outline-config": "npm:^0.0.14" - "@phase2/outline-core": "npm:^0.2.7" "@phase2/outline-core-alert": "npm:^0.0.4" - "@phase2/outline-core-link": "npm:^0.0.16" "@phase2/outline-docs": "npm:^0.0.21" "@storybook/addon-essentials": "npm:^7.6.17" "@storybook/addon-links": "npm:^7.6.17"