Skip to content

Commit

Permalink
Merge pull request #675 from exadel-inc/feature/snippet-isolated-marker
Browse files Browse the repository at this point in the history
feat: snippet isolated marker
  • Loading branch information
AlexanderBazukevich authored Mar 11, 2024
2 parents f0a7373 + 68a42d6 commit a641a49
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 23 deletions.
4 changes: 2 additions & 2 deletions site/views/examples/example/form.njk
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: Example with form
<uip-root>
<uip-snippets class="uip-toolbar"></uip-snippets>

<script type="text/html" uip-snippet label="Example with form">
<script type="text/html" uip-snippet uip-isolated label="Example with form">
<form class="demo-form">
<div class="form-section">
<label for="name">Name: </label>
Expand All @@ -32,7 +32,7 @@ title: Example with form
};
</script>

<uip-preview class="centered-content" resizable isolation></uip-preview>
<uip-preview class="centered-content" resizable></uip-preview>

<uip-settings resizable vertical="@+sm" target=".demo-form">
<uip-bool-setting label="Email validation" target="#email" mode="append" attribute="class" value="validation-input"></uip-bool-setting>
Expand Down
2 changes: 1 addition & 1 deletion site/views/examples/example/image.njk
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: Example with image
<uip-snippets dropdown-view="all"></uip-snippets>
<uip-theme-toggle></uip-theme-toggle>
</div>
<script type="text/html" uip-snippet label="Example with image">
<script type="text/html" uip-snippet uip-isolated label="Example with image">
<img class="demo-img" alt="patrick" src="{{ '/assets/patrick.png' | url }}" height="500" width="500">
</script>
<script type="text/html" uip-snippet label="Example with image link">
Expand Down
7 changes: 6 additions & 1 deletion src/core/base/model.change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import type {UIPStateModel} from './model';
export type UIPChangeInfo = {
modifier: UIPPlugin | UIPRoot;
type: 'html' | 'js';
force?: boolean;
};

export class UIPChangeEvent extends Event {
public readonly target: UIPRoot;
public override readonly target: UIPRoot;

public constructor(
type: string,
Expand All @@ -25,6 +26,10 @@ export class UIPChangeEvent extends Event {
return this.target.model;
}

public get force(): boolean {
return this.changes.some((change) => change.force);
}

public get jsChanges(): UIPChangeInfo[] {
return this.changes.filter((change) => change.type === 'js');
}
Expand Down
9 changes: 5 additions & 4 deletions src/core/base/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,22 @@ export class UIPStateModel extends SyntheticEventTarget {
const script = UIPJSNormalizationPreprocessors.preprocess(js);
if (this._js === script) return;
this._js = script;
this._changes.push({modifier, type: 'js'});
this._changes.push({modifier, type: 'js', force: true});
this.dispatchChange();
}

/**
* Sets current markup state to the passed one
* @param markup - new state
* @param modifier - plugin, that initiates the change
* @param force - marker, that indicates if html changes require iframe rerender
*/
public setHtml(markup: string, modifier: UIPPlugin | UIPRoot): void {
public setHtml(markup: string, modifier: UIPPlugin | UIPRoot, force: boolean = false): void {
const html = UIPHTMLNormalizationPreprocessors.preprocess(markup);
const root = new DOMParser().parseFromString(html, 'text/html').body;
if (!root || root.innerHTML.trim() !== this.html.trim()) {
this._html = root;
this._changes.push({modifier, type: 'html'});
this._changes.push({modifier, type: 'html', force});
this.dispatchChange();
}
}
Expand Down Expand Up @@ -120,7 +121,7 @@ export class UIPStateModel extends SyntheticEventTarget {
): void {
if (!snippet) return;
this._snippets.forEach((s) => (s.active = s === snippet));
this.setHtml(snippet.html, modifier);
this.setHtml(snippet.html, modifier, true);
this.setJS(snippet.js, modifier);
this.dispatchEvent(
new CustomEvent('uip:model:snippet:change', {detail: this})
Expand Down
11 changes: 11 additions & 0 deletions src/core/base/snippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ export class UIPSnippetItem {
return this.$elementJS ? this.$elementJS.innerHTML : '';
}

/** @returns template to use for isolated rendering */
@memoize()
public get isolatedTemplate(): string {
return this.$element.getAttribute('isolated-template') || '';
}

/** @returns if snippet should preview in isolated iframe */
public get isolated(): boolean {
return this.$element.hasAttribute('uip-isolated');
}

/** @returns snippet's anchor id */
public get anchor(): string | null {
return this.$element.getAttribute('anchor');
Expand Down
24 changes: 9 additions & 15 deletions src/core/preview/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,13 @@ import type {ESLIntersectionEvent} from '@exadel/esl/modules/esl-event-listener/
*/
export class UIPPreview extends UIPPlugin {
static is = 'uip-preview';
static observedAttributes: string[] = ['dir', 'resizable', 'isolation', 'isolation-template'];
static observedAttributes: string[] = ['dir', 'resizable'];

/** 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;
/** Marker to use iframe isolated rendering */
/** Marker to force iframe rerendering */
@attr({parser: parseBoolean, serializer: toBooleanAttribute}) public forceUpdate: boolean;
/** Template to use for isolated rendering */
@attr({defaultValue: 'default'}) public isolationTemplate: string;

protected _iframeResizeRAF: number = 0;

Expand Down Expand Up @@ -70,15 +66,13 @@ export class UIPPreview extends UIPPlugin {
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();
}
}

protected update(e?: UIPChangeEvent): void {
if (!this.isolation) return this.writeContent();
if (!e || e.jsChanges.length || this.forceUpdate) this.writeContentIsolated();
const isolated = this.model!.activeSnippet?.isolated || false;

if (!isolated) return this.writeContent();
if (!e || this.forceUpdate || e.force) this.writeContentIsolated();
this._onIframeLoad();
}

Expand Down Expand Up @@ -134,16 +128,16 @@ export class UIPPreview extends UIPPlugin {

@listen({
event: 'load',
target: ($this: UIPPreview) => $this.$iframe,
condition: ($this: UIPPreview) => $this.isolation
target: ($this: UIPPreview) => $this.$iframe
})
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});
const isolatedTemplate = this.model!.activeSnippet?.isolatedTemplate || 'default';
const html = UIPRenderingTemplatesService.render(isolatedTemplate, {title, content, script});

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

0 comments on commit a641a49

Please sign in to comment.