This repository has been archived by the owner on May 5, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[IMP] Renderer object, layout to generate the minimum of mutations
This renderer returns a structure similar to the dom without creating an element. The layout create elements or use the available elements already in the document. This has the advantage: - optimal diff to carry out the minimum mutation in the dom; - localization in when rendering; - add and remove handlers.
- Loading branch information
Showing
57 changed files
with
6,104 additions
and
1,311 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,72 @@ | ||
import { AbstractRenderer } from '../../plugin-renderer/src/AbstractRenderer'; | ||
import { CharNode } from './CharNode'; | ||
import { InlineNode } from '../../plugin-inline/src/InlineNode'; | ||
import { HtmlDomRenderingEngine } from '../../plugin-html/src/HtmlDomRenderingEngine'; | ||
import { VNode } from '../../core/src/VNodes/VNode'; | ||
import { Attributes } from '../../plugin-xml/src/Attributes'; | ||
import { | ||
DomObjectRenderingEngine, | ||
DomObject, | ||
DomObjectText, | ||
} from '../../plugin-html/src/DomObjectRenderingEngine'; | ||
import { InlineFormatDomObjectRenderer } from '../../plugin-inline/src/InlineFormatDomObjectRenderer'; | ||
import { Format } from '../../plugin-inline/src/Format'; | ||
|
||
export class CharHtmlDomRenderer extends AbstractRenderer<Node[]> { | ||
static id = HtmlDomRenderingEngine.id; | ||
export class CharDomObjectRenderer extends InlineFormatDomObjectRenderer { | ||
static id = DomObjectRenderingEngine.id; | ||
engine: DomObjectRenderingEngine; | ||
predicate = CharNode; | ||
|
||
async render(node: CharNode): Promise<Node[]> { | ||
async render(node: CharNode): Promise<DomObject> { | ||
// Consecutive compatible char nodes are rendered as a single text node. | ||
let text = '' + node.char; | ||
const charNodes = [node]; | ||
const siblings = node.parent.children(); | ||
let index = siblings.indexOf(node) + 1; | ||
let next: VNode; | ||
while ((next = siblings[index]) && node.isSameTextNode(next)) { | ||
charNodes.push(next); | ||
if (next.char === ' ' && text[text.length - 1] === ' ') { | ||
let sibling: VNode; | ||
let index = siblings.indexOf(node) - 1; | ||
while ((sibling = siblings[index]) && node.isSameTextNode(sibling)) { | ||
charNodes.unshift(sibling); | ||
index--; | ||
} | ||
index = siblings.indexOf(node) + 1; | ||
while ((sibling = siblings[index]) && node.isSameTextNode(sibling)) { | ||
charNodes.push(sibling); | ||
index++; | ||
} | ||
let text = ''; | ||
for (const charNode of charNodes) { | ||
if (charNode.char === ' ' && text[text.length - 1] === ' ') { | ||
// Browsers don't render consecutive space chars otherwise. | ||
text += '\u00A0'; | ||
} else { | ||
text += next.char; | ||
text += charNode.char; | ||
} | ||
index++; | ||
} | ||
// Render block edge spaces as non-breakable space (otherwise browsers | ||
// won't render them). | ||
const previous = node.previousSibling(); | ||
if (!previous || !previous.is(InlineNode)) { | ||
text = text.replace(/^ /g, '\u00A0'); | ||
} | ||
if (!next || !next.is(InlineNode)) { | ||
if (!sibling || !sibling.is(InlineNode)) { | ||
text = text.replace(/ $/g, '\u00A0'); | ||
} | ||
const rendering = Promise.resolve([document.createTextNode(text)]); | ||
return this.engine.rendered(charNodes, [this, rendering]); | ||
|
||
const textObject: DomObjectText = { text: text }; | ||
this.engine.locate(charNodes, textObject); | ||
|
||
// If the node has attributes, wrap it inside a span with those | ||
// attributes. | ||
let domObject: DomObject; | ||
const attributes = node.modifiers.find(Attributes); | ||
if (attributes?.length) { | ||
domObject = { | ||
tag: 'SPAN', | ||
children: [textObject], | ||
}; | ||
this.engine.renderAttributes(Attributes, node, domObject); | ||
} else { | ||
domObject = textObject; | ||
} | ||
|
||
const rendering = this.renderFormats(node.modifiers.filter(Format), domObject); | ||
return this.engine.rendered(charNodes, this, rendering); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 17 additions & 15 deletions
32
packages/plugin-dialog/src/ui/DialogZoneDomObjectRenderer.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,55 @@ | ||
import { HtmlDomRenderingEngine } from '../../../plugin-html/src/HtmlDomRenderingEngine'; | ||
import { | ||
DomObjectRenderingEngine, | ||
DomObject, | ||
} from '../../../plugin-html/src/DomObjectRenderingEngine'; | ||
import { AbstractRenderer } from '../../../plugin-renderer/src/AbstractRenderer'; | ||
import { DialogZoneNode } from './DialogZoneNode'; | ||
import { VNode } from '../../../core/src/VNodes/VNode'; | ||
import { ComponentId } from '../../../plugin-layout/src/LayoutEngine'; | ||
import { Layout } from '../../../plugin-layout/src/Layout'; | ||
|
||
import template from './Dialog.xml'; | ||
import './Dialog.css'; | ||
import { Layout } from '../../../plugin-layout/src/Layout'; | ||
import { ComponentId } from '../../../plugin-layout/src/LayoutEngine'; | ||
import { MetadataNode } from '../../../plugin-metadata/src/MetadataNode'; | ||
|
||
const container = document.createElement('jw-container'); | ||
container.innerHTML = template; | ||
const dialog = container.firstElementChild; | ||
|
||
export class DialogZoneHtmlDomRenderer extends AbstractRenderer<Node[]> { | ||
static id = HtmlDomRenderingEngine.id; | ||
engine: HtmlDomRenderingEngine; | ||
export class DialogZoneDomObjectRenderer extends AbstractRenderer<DomObject> { | ||
static id = DomObjectRenderingEngine.id; | ||
engine: DomObjectRenderingEngine; | ||
predicate = DialogZoneNode; | ||
|
||
async render(node: DialogZoneNode): Promise<Node[]> { | ||
async render(node: DialogZoneNode): Promise<DomObject> { | ||
const float = document.createElement('jw-dialog-container'); | ||
for (const child of node.childVNodes) { | ||
if (!node.hidden.get(child)) { | ||
if (!node.hidden.get(child) && (child.tangible || child.is(MetadataNode))) { | ||
float.appendChild(await this._renderDialog(child)); | ||
} | ||
} | ||
return float.childNodes.length ? [float] : [document.createDocumentFragment()]; | ||
return { | ||
dom: float.childNodes.length ? [float] : [], | ||
}; | ||
} | ||
|
||
private async _renderDialog(node: VNode): Promise<Element> { | ||
const clone = dialog.cloneNode(true) as Element; | ||
const content = clone.querySelector('jw-content'); | ||
for (const domNode of await this.engine.render(node)) { | ||
content.appendChild(domNode); | ||
} | ||
|
||
content.appendChild(this.engine.renderPlaceholder(node)); | ||
let componentId: ComponentId; | ||
const components = this.engine.editor.plugins.get(Layout).engines.dom.components; | ||
for (const [id, nodes] of components) { | ||
if (nodes.includes(node)) { | ||
componentId = id; | ||
} | ||
} | ||
|
||
clone.addEventListener('click', (ev: MouseEvent): void => { | ||
const target = ev.target as Element; | ||
if (target.classList.contains('jw-close')) { | ||
this.engine.editor.execCommand('hide', { componentID: componentId }); | ||
} | ||
}); | ||
|
||
return clone; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.