Skip to content

Commit

Permalink
fix(preview): fix isolated content rendering problems
Browse files Browse the repository at this point in the history
  • Loading branch information
ala-n committed Jan 26, 2024
1 parent 2423687 commit 2f9b628
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 31 deletions.
2 changes: 1 addition & 1 deletion src/core/preview/preview.less
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

&-iframe {
width: 100%;
min-height: 100%;
height: 100%;
border: none;
}

Expand Down
78 changes: 48 additions & 30 deletions src/core/preview/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'jsx-dom';

import {attr, listen, memoize} from '@exadel/esl/modules/esl-utils/decorators';
import {attr, listen, memoize, prop} from '@exadel/esl/modules/esl-utils/decorators';
import {ESLIntersectionTarget} from '@exadel/esl/modules/esl-event-listener/core';
import {parseBoolean, toBooleanAttribute} from '@exadel/esl/modules/esl-utils/misc';
import {afterNextRender, skipOneRender} from '@exadel/esl/modules/esl-utils/async';
Expand All @@ -19,6 +19,9 @@ export class UIPPreview extends UIPPlugin {
static is = 'uip-preview';
static observedAttributes: string[] = ['dir', 'resizable', 'isolation', 'isolation-template'];

/** Sync height with inner iframe content height */
@prop(true) public resizeLoop: boolean;

/** Marker to use iframe isolated rendering */
@attr({parser: parseBoolean, serializer: toBooleanAttribute}) public isolation: boolean;
/** Template to use for isolated rendering */
Expand Down Expand Up @@ -51,19 +54,6 @@ export class UIPPreview extends UIPPlugin {
) as HTMLElement;
}

/** Updates preview content from the model state changes */
@listen({event: 'uip:change', target: ($this: UIPPreview) => $this.$root})
protected _onRootStateChange(): void {
this.$container.style.minHeight = `${this.$inner.offsetHeight}px`;
this.isolation ? this.writeContentIsolated() : this.writeContent();

afterNextRender(() => this.$container.style.minHeight = '0px');
skipOneRender(() => {
if (this.$container.clientHeight !== this.$inner.offsetHeight) return;
this.$container.style.removeProperty('min-height');
});
}

protected override connectedCallback(): void {
super.connectedCallback();
this.appendChild(this.$container);
Expand All @@ -74,6 +64,15 @@ export class UIPPreview extends UIPPlugin {
super.disconnectedCallback();
}

protected override attributeChangedCallback(attrName: string, oldVal: string, newVal: string): void {
if (attrName === 'resizable' && newVal === null) this.clearInlineSize();
if (attrName === 'dir') this.updateDir();
if (attrName === 'isolation' || attrName === 'isolation-template') {
this.$$on(this._onIframeLoad);
this._onRootStateChange();
}
}

/** Writes the content directly to the inner area (non-isolated frame) */
protected writeContent(): void {
this.$inner.innerHTML = UIPHTMLRenderingPreprocessors.preprocess(this.model!.html);
Expand All @@ -85,21 +84,15 @@ export class UIPPreview extends UIPPlugin {
if (this.$iframe.parentElement !== this.$inner) {
this.$inner.innerHTML = '';
this.$inner.appendChild(this.$iframe);
this.startIframeResizeLoop();
}

const title = this.model!.activeSnippet?.label || 'UI Playground';
const script = UIPJSRenderingPreprocessors.preprocess(this.model!.js);
const content = UIPHTMLRenderingPreprocessors.preprocess(this.model!.html);
const html = UIPRenderingTemplatesService.render(this.isolationTemplate, {title, content, script});

this.$iframe.contentWindow?.document.open();
this.$iframe.contentWindow?.document.write(html);
this.$iframe.contentWindow?.document.close();
this.stopIframeResizeLoop();
this.$iframe.src = '';
this.startIframeResizeLoop();
}

/** Start and do a resize sync-loop iteration. Recall itself on the next frame. */
protected startIframeResizeLoop(): void {
if (!this.resizeLoop) return;
// Prevents multiple loops
if (this._iframeResizeRAF) cancelAnimationFrame(this._iframeResizeRAF);
// Addition loop fallback for iframe removal
Expand All @@ -116,12 +109,6 @@ export class UIPPreview extends UIPPlugin {
this._iframeResizeRAF = 0;
}

protected override attributeChangedCallback(attrName: string, oldVal: string, newVal: string): void {
if (attrName === 'resizable' && newVal === null) this.clearInlineSize();
if (attrName === 'dir') this.updateDir();
if (attrName === 'isolation' || attrName === 'isolation-template') this._onRootStateChange();
}

/** Resets element both inline height and width properties */
protected clearInlineSize(): void {
this.$inner.style.removeProperty('height');
Expand All @@ -133,6 +120,37 @@ export class UIPPreview extends UIPPlugin {
isChanged && this.$$fire('uip:dirchange');
}

@listen({
event: 'load',
target: ($this: UIPPreview) => $this.$iframe,
condition: ($this: UIPPreview) => $this.isolation
})
protected _onIframeLoad(): void {
if (!this.$iframe.contentWindow) return;

const title = this.model!.activeSnippet?.label || 'UI Playground';
const script = UIPJSRenderingPreprocessors.preprocess(this.model!.js);
const content = UIPHTMLRenderingPreprocessors.preprocess(this.model!.html);
const html = UIPRenderingTemplatesService.render(this.isolationTemplate, {title, content, script});

this.$iframe.contentWindow?.document.close();
this.$iframe.contentWindow?.document.write(html);
this.$iframe.contentWindow?.document.close();
}

/** Updates preview content from the model state changes */
@listen({event: 'uip:change', target: ($this: UIPPreview) => $this.$root})
protected _onRootStateChange(): void {
this.$container.style.minHeight = `${this.$inner.offsetHeight}px`;
this.isolation ? this.writeContentIsolated() : this.writeContent();

afterNextRender(() => this.$container.style.minHeight = '0px');
skipOneRender(() => {
if (this.$container.clientHeight !== this.$inner.offsetHeight) return;
this.$container.style.removeProperty('min-height');
});
}

/** Handles end of animation playing while the demo content change */
@listen({
event: 'transitionend',
Expand Down
1 change: 1 addition & 0 deletions src/core/processors/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ UIPRenderingTemplatesService.add('default', `
<html>
<head>
<title>{title}</title>
<base href="${location.origin}"/>
<script type="module">{script}</script>
</head>
<body>{content}</body>
Expand Down
1 change: 1 addition & 0 deletions src/core/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export {UIPRoot, UIPPlugin, UIPStateModel, UIPPreview, ChangeAttrConfig};

export * from './processors/normalization';
export * from './processors/rendering';
export * from './processors/templates';

export const registerCore = (): void => {
UIPRoot.register();
Expand Down

0 comments on commit 2f9b628

Please sign in to comment.