Skip to content
This repository was archived by the owner on May 5, 2021. It is now read-only.

Add doc & rename & add type information #191

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions documentation/core/Modifiers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Modifiers
A modifier is: ...
Copy link
Contributor

@Zynton Zynton May 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

You could think of a modifier the way you think of a loadable but for nodes: they are simple holders of information that have the potential of modifying the node.


The main modifiers are `Attribute` and `Format`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. And they are on plugins ^^


## Attributes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On XML Plugin.

When parsing and rendering a document, we need to save the attributes that a
DOM node has and save it in our VNode.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • "save them"
  • you mention the DOM, that's because indeed this is from XML plugin.


Attributes exists to:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"exist"

- retrieve HTML attributes that a DOM node has originally
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • XML ?
  • "retrieve a DOM node's original XML attributes"?

- render the HTML attributes that a VNode has
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok well then why not just in one line: "Attributes serve the purpose of parsing, manipulating and rendering XML attributes of a DOM node". Admittedly, "manipulating" might soon not be true anymore if we start using Attributes only for the attributes we don't have support for, and move the others to their own modifiers.


## Format
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Inline Plugin (though that might have to move to core, I don't know).

The DOM inline nodes (e.g. span, strong, em, b, a, ...) in HTML are difficult to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existence of a dichotomy between blocks and inline nodes is what causes a level of complexity that we managed to eliminate by introducing Format. It's not so much that the inline nodes themselves are hard to manipulate but the fact that there is that dichotomy means that we have to constantly be on our guard and treat different nodes differently, which can end up being complicated.

manipulate. We therefore we transform them into a Format modifiers that we
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • "Therefore we" or "We therefore" but not "We therefore we" ^^
  • "into Format modifiers"

attach to a VNode through the "modifiers" properties.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everywhere in the DOC please don't forget to use `` for the references to classes etc.
In this case: VNode instead of VNode.


Format themselve have modifiers. For instance, a HTML strong tag migh have
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Formats may themselves have modifiers. For instance a bold text may be colored red, which would translate into HTML as a strong tag with a style attribute "color: red", and into Jabberwock as (with the help of Char, Attributes and Inline plugins) a group of Char nodes with as modifiers: (1) an extension of the Format class that renders as a strong tag and (2) Attributes containing the "color" style set to red."
Too much? :'D

attributes (styles, classes, ...).
14 changes: 14 additions & 0 deletions packages/core/src/Modifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ export interface Modifier {
constructor: ModifierConstructor & this;
}
export class Modifier {
/**
* The name of the this Modifier.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The this? :-)
"Return the name of this modifier."

*/
get name(): string {
return '';
}
/**
* Return the string representation of this Modifier.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the capital letter?

*/
toString(): string {
return this.name;
}
Expand All @@ -22,9 +28,17 @@ export class Modifier {
applyTo(node: VNode): void {
node.modifiers.prepend(this);
}
/**
* Compare two modifiers.
*
* Meant to be overridden if necessary.
Comment on lines +32 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Return true if this modifier is the same as the given modifier, false otherwise.

This can be overridden to change the meaning of equality between modifiers."

*/
isSameAs(otherModifier: Modifier): boolean {
return this === otherModifier;
}
/**
* Clone this modifier.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Return a clone of this modifier."

*/
clone(): this {
return new this.constructor();
}
Expand Down
9 changes: 6 additions & 3 deletions packages/core/src/Modifiers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { Modifier } from './Modifier';
import { Constructor, isConstructor } from '../../utils/src/utils';

/**
* When parsing dom elements, modifiers can be DOM inline nodes (a, b, strong).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't write this here.

  1. No mention of the DOM in core
  2. No mention of a plugin in core (Format in this case)
  3. Start your description of the class by talking about the general case, the general purpose, not the edge cases.
  4. Be consistent: you write "dom" and "DOM" in the same sentence.

*
* The order of modifiers are important because it impact the way they will
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The order" -> "is"
"It" -> "impacts"

=> "The order of modifiers is important because it may impact their rendering." ?

* be rendered.
*/
export class Modifiers {
private _contents: Modifier[];
constructor(...modifiers: Array<Modifier | Constructor<Modifier>>) {
Expand Down Expand Up @@ -86,7 +92,6 @@ export class Modifiers {
* for `this._contents`.
*
* @see Array.find
* @param modifier
*/
find<T extends Modifier>(callback: (modifier: T) => boolean): T;
find<T extends Modifier>(modifier: T | Constructor<T>): T;
Expand All @@ -110,8 +115,6 @@ export class Modifiers {
* modifier class or create one, append it and return it.
* If the modifier passed is a modifier instance, return it if it was
* present in the array.
*
* @param modifier
*/
get<T extends Modifier>(modifier: T | Constructor<T>): T {
let found = this.find(modifier);
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-char/src/Char.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Attributes } from '../../plugin-xml/src/Attributes';

export interface InsertTextParams extends CommandParams {
text: string;
formats?: Modifiers;
modifiers?: Modifiers;
}

export class Char<T extends JWPluginConfig = JWPluginConfig> extends JWPlugin<T> {
Expand Down
26 changes: 26 additions & 0 deletions packages/plugin-inline/src/Format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,31 @@ import { Modifier } from '../../core/src/Modifier';
import { Modifiers } from '../../core/src/Modifiers';
import { Attributes } from '../../plugin-xml/src/Attributes';

/**
* A Format might represent DOM inline nodes (a, b, strong) that were parsed or
* the DOM inline node (a, b, strong) that we might render.
Comment on lines +6 to +7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a TODO just below: "remove this reference to DOM."
I would try and rewrite this to not invoke the DOM.
Plus, all the uses of "might" make this very hesitant ^^

Now what this does however is reveal how a Format is not fundamentally different from any other Modifier apart from its DOM rendering... @dmo-odoo Any input?

*/
export class Format extends Modifier {
htmlTag: string; // TODO: remove this reference to DOM.
/**
* The modifiers for a format is `Attribute`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea what that sentence is supposed to mean. If you mean that the only modifier a Format can have is Attributes, that's wrong. Modifiers can potentially hold any kind of information to modify a node or a format.

* Used to be rendered.
*/
modifiers = new Modifiers();

constructor(htmlTag?: string) {
super();
this.htmlTag = htmlTag;
}
/**
* @override
*/
get name(): string {
return this.htmlTag.toLowerCase();
}
/**
* @override
*/
toString(): string {
const nonEmptyAttributes = this.modifiers.filter(
modifier => !(modifier instanceof Attributes) || !!modifier.length,
Expand All @@ -31,6 +46,11 @@ export class Format extends Modifier {
// Public
//--------------------------------------------------------------------------

/**
* Render the current format into a dom Node.
*
* Meant to be used with `FormatDomRenderer`.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// TODO: remove this reference to DOM.
(my bad)

render(): Element {
const domNode = document.createElement(this.htmlTag);
const attributes = this.modifiers.find(Attributes);
Expand All @@ -51,12 +71,18 @@ export class Format extends Modifier {
}
return domNode;
}
/**
* @override
*/
clone(): this {
const clone = new this.constructor();
clone.htmlTag = this.htmlTag;
clone.modifiers = this.modifiers.clone();
return clone;
}
/**
* @override
*/
isSameAs(otherFormat: Format): boolean {
const aModifiers = this.modifiers;
const bModifiers = otherFormat?.modifiers;
Expand Down
5 changes: 4 additions & 1 deletion packages/plugin-inline/src/Inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ export class Inline<T extends JWPluginConfig = JWPluginConfig> extends JWPlugin<
for (const inline of selectedInlines) {
const format = inline.modifiers.find(FormatClass);
// Apply the attributes of the format we're about to remove
// to the inline itself.
// to the inline itself in order to keep information of the
// Format.
// For instance, if the Format has a style attribute we need
// to keep that information to render it later.
const attributes = inline.modifiers.get(Attributes);
const matchingFormatAttributes = format.modifiers.find(Attributes);
if (matchingFormatAttributes) {
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-link/src/Link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ export class Link<T extends JWPluginConfig = JWPluginConfig> extends JWPlugin<T>
await layout.remove('link');
await layout.add('link');

return this.editor.execCommand('show', { componentID: 'link' });
return this.editor.execCommand<Layout>('show', { componentID: 'link' });
}

// Otherwise create a link and insert it.
const link = new LinkFormat(params.url);
return this.editor.execCommand<Char>('insertText', {
text: params.label || link.url,
formats: new Modifiers(link),
modifiers: new Modifiers(link),
});
}
unlink(params: LinkParams): void {
Expand Down
6 changes: 3 additions & 3 deletions packages/plugin-link/src/components/LinkComponent.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { OwlComponent } from '../../../plugin-owl/src/ui/OwlComponent';
import { InlineNode } from '../../../plugin-inline/src/InlineNode';
import { LinkFormat } from '../LinkFormat';
import { LinkParams } from '../Link';
import { Link } from '../Link';
import { Layout } from '../../../plugin-layout/src/Layout';
import { useState } from '@odoo/owl';

Expand All @@ -18,10 +18,10 @@ export class LinkComponent<T = {}> extends OwlComponent<T> {
//--------------------------------------------------------------------------

async saveLink(): Promise<void> {
await this.env.editor.execCommand('link', {
await this.env.editor.execCommand<Link>('link', {
url: this.state.url,
label: this.state.label,
} as LinkParams);
});
this.env.editor.plugins.get(Layout).remove('link');
this.destroy();
}
Expand Down