Skip to content

Commit

Permalink
fix(core): add embedded normalization utils for UIPModel to resolve m…
Browse files Browse the repository at this point in the history
…ultiple extra refreshes
  • Loading branch information
ala-n committed Sep 22, 2023
1 parent dc769c2 commit 0fc8891
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 19 deletions.
23 changes: 9 additions & 14 deletions src/core/base/model.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {Observable} from '@exadel/esl/modules/esl-utils/abstract/observable';
import {decorate} from '@exadel/esl/modules/esl-utils/decorators';
import {microtask} from '@exadel/esl/modules/esl-utils/async';

import {UIPPlugin} from './plugin';
import {UIPRoot} from './root';
import {UIPHtmlNormalizationService} from '../utils/normalization';

/** Type for function to change attribute's current value */
export type TransformSignature = (current: string | null) => string | boolean | null;
Expand Down Expand Up @@ -38,8 +42,6 @@ export class UIPStateModel extends Observable {
private _html = new DOMParser().parseFromString('', 'text/html').body;
/** Last {@link UIPPlugin} element which changed markup */
private _lastModifier: UIPPlugin | UIPRoot;
/** Marker whether state changes were dispatched */
private _isFired = false;

/** Snippets {@link SnippetTemplate template-holders} */
private _snippets: SnippetTemplate[];
Expand All @@ -52,11 +54,9 @@ export class UIPStateModel extends Observable {
* @param modifier - plugin, that initiates the change
*/
public setHtml(markup: string, modifier: UIPPlugin | UIPRoot) {
const root = new DOMParser().parseFromString(markup, 'text/html').body;
const indent = markup.match(/^\s*/)?.[0];
indent && root.prepend(document.createTextNode(indent));

if (!root || root.innerHTML !== this.html) {
const html = UIPHtmlNormalizationService.normalize(markup);
const root = new DOMParser().parseFromString(html, 'text/html').body;
if (!root || root.innerHTML.trim() !== this.html.trim()) {
this._html = root;
this._lastModifier = modifier;
this.dispatchChange();
Expand Down Expand Up @@ -127,14 +127,9 @@ export class UIPStateModel extends Observable {
}

/** Plans microtask to dispatch model change event */
@decorate(microtask)
protected dispatchChange() {
if (!this._isFired) {
this._isFired = true;
Promise.resolve().then(() => {
this.fire();
this._isFired = false;
});
}
this.fire();
}

/**
Expand Down
9 changes: 4 additions & 5 deletions src/core/base/plugin.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {memoize} from '@exadel/esl/modules/esl-utils/decorators/memoize';
import {attr, ESLBaseElement} from '@exadel/esl/modules/esl-base-element/core';
import React from 'jsx-dom';

import {ESLBaseElement} from '@exadel/esl/modules/esl-base-element/core';
import {attr, memoize} from '@exadel/esl/modules/esl-utils/decorators';

import {UIPRoot} from './root';
import {UIPStateModel} from './model';

import * as React from 'jsx-dom';

/**
* Base class for UI Playground plugins
* Implements basic relation and styles
Expand Down Expand Up @@ -38,7 +38,6 @@ export abstract class UIPPlugin extends ESLBaseElement {
super.connectedCallback();
this.classList.add('uip-plugin');
this.root?.addStateListener(this._onRootStateChange);
this.root && this._onRootStateChange();
}

protected disconnectedCallback() {
Expand Down
1 change: 1 addition & 0 deletions src/core/registration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {UIPPlugin} from './base/plugin';

import {UIPPreview} from './preview/preview';

export * from './utils/normalization';
export {UIPRoot, UIPPlugin, UIPStateModel, UIPPreview, ChangeAttrConfig};

export const registerCore = () => {
Expand Down
41 changes: 41 additions & 0 deletions src/core/utils/normalization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/** Function to process html string for normalization*/
export type UIPNormalizationProcessor = (input: string) => string;

export class UIPHtmlNormalizationService {
/** Processor storage */
protected static processors: Record<string, UIPNormalizationProcessor> = {};

/** Add processor function to normalization process */
public static addProcessor(name: string, processor: UIPNormalizationProcessor) {
UIPHtmlNormalizationService.processors[name] = processor;
}

/** Normalizes passes html string by running all registered processors in chain */
public static normalize(html: string): string {
return Object.keys(UIPHtmlNormalizationService.processors)
.map((name) => UIPHtmlNormalizationService.processors[name])
.filter((processor) => typeof processor === 'function')
.reduce((input, processor) => processor(input), html);
}
}

/** Removes extra indents */
UIPHtmlNormalizationService.addProcessor('remove-indent', (input: string): string => {
// Get all indents from text
const indents = input.match(/^[^\S\n\r]*(?=\S)/gm);
// No processing if no indent on the first line or input is empty
if (!indents || !indents[0].length) return input;
// Sort indents by length
indents.sort((a, b) => a.length - b.length);
// No processing if minimal indent is 0
if (!indents[0].length) return input;
// Remove indents from text
return input.replace(RegExp('^' + indents[0], 'gm'), '');
});
/** Removes extra spaces */
// TODO: handle case with inline script with literal double spaces
UIPHtmlNormalizationService.addProcessor('remove-trailing', (input: string) => input.replace(/\s*?$/gm, ''));
/** Remove beginning spaces */
UIPHtmlNormalizationService.addProcessor('left-trim', (input: string) => input.replace(/^\s+/, ''));
/** Remove ending spaces */
UIPHtmlNormalizationService.addProcessor('right-trim', (input: string) => input.replace(/\s+$/, ''));

0 comments on commit 0fc8891

Please sign in to comment.