Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Mobiledoc 0.3.2 (text alignment attribute) (continued) #688

Merged
merged 3 commits into from
Jul 16, 2019
Merged
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 assets/demo/demo.css
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,26 @@ table tr {

.toolbar {
text-align: center;
display: flex;
}

.toolbar-section {
display: flex;
margin: 0 12px;
}
.toolbar-section:first-child { margin-left: 0; }
.toolbar-section:last-child { margin-right: 0; }

.toolbar-section button {
display: flex;
align-items: center;
margin: 0 4px;
}
.toolbar-section button:first-child { margin-left: 0; }
.toolbar-section button:last-child { margin-right: 0; }

.toolbar-section button svg {
height: 24px;
}

#editor-wrapper {
Expand Down
4 changes: 2 additions & 2 deletions assets/demo/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ let activateButtons = (parentSelector, editor) => {
$(`${parentSelector} button`).click(function() {
let button = $(this);
let action = button.data('action');
let arg = button.data('arg');
let args = button.data('args').split(',');

editor[action](arg);
editor[action](...args);
});
};

Expand Down
47 changes: 39 additions & 8 deletions assets/demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,41 @@
</div>
<div id="editor-wrapper">
<div class="toolbar">
<button data-action='toggleSection' data-arg='h1'>Headline</button>
<button data-action='toggleSection' data-arg='h2'>Subheadline</button>
<button data-action='toggleMarkup' data-arg='strong'>Bold</button>
<button data-action='toggleMarkup' data-arg='em'>Italic</button>
<button data-action='toggleSection' data-arg='ul'>Bullet List</button>
<button data-action='toggleSection' data-arg='ol'>Number List</button>
<!-- Icons are from https://material.io/tools/icons/ -->
<div class="toolbar-section">
<button data-action='toggleSection' data-args='h1'>Headline</button>
<button data-action='toggleSection' data-args='h2'>Subheadline</button>
</div>
<div class="toolbar-section">
<button data-action='toggleMarkup' data-args='strong'>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.6 10.79c.97-.67 1.65-1.77 1.65-2.79 0-2.26-1.75-4-4-4H7v14h7.04c2.09 0 3.71-1.7 3.71-3.79 0-1.52-.86-2.82-2.15-3.42zM10 6.5h3c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-3v-3zm3.5 9H10v-3h3.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
</button>
<button data-action='toggleMarkup' data-args='em'>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M10 4v3h2.21l-3.42 8H6v3h8v-3h-2.21l3.42-8H18V4z"/></svg>
</button>
</div>
<div class="toolbar-section">
<button data-action='toggleSection' data-args='ul'>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.68-1.5 1.5s.68 1.5 1.5 1.5 1.5-.68 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z"/><path fill="none" d="M0 0h24v24H0V0z"/></svg>
</button>
<button data-action='toggleSection' data-args='ol'>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2 17h2v.5H3v1h1v.5H2v1h3v-4H2v1zm1-9h1V4H2v1h1v3zm-1 3h1.8L2 13.1v.9h3v-1H3.2L5 10.9V10H2v1zm5-6v2h14V5H7zm0 14h14v-2H7v2zm0-6h14v-2H7v2z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
</button>
</div>
<div class="toolbar-section">
<button data-action='setAttribute' data-args='text-align,left'>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15 15H3v2h12v-2zm0-8H3v2h12V7zM3 13h18v-2H3v2zm0 8h18v-2H3v2zM3 3v2h18V3H3z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
</button>
<button data-action='setAttribute' data-args='text-align,center'>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 15v2h10v-2H7zm-4 6h18v-2H3v2zm0-8h18v-2H3v2zm4-6v2h10V7H7zM3 3v2h18V3H3z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
</button>
<button data-action='setAttribute' data-args='text-align,right'>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 21h18v-2H3v2zm6-4h12v-2H9v2zm-6-4h18v-2H3v2zm6-4h12V7H9v2zM3 3v2h18V3H3z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
</button>
<button data-action='setAttribute' data-args='text-align,justify'>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 21h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18v-2H3v2zm0-4h18V7H3v2zm0-6v2h18V3H3z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
</button>
</div>
</div>
<div id='editor'></div>
</div>
Expand Down Expand Up @@ -173,8 +202,7 @@ <h3>Basic Editor</h3>
<h3>Toolbar Buttons</h3>

<p>
Use the <a href='http://bustle.github.io/mobiledoc-kit/demo/docs/Editor.html#toggleMarkup'><code>editor.toggleMarkup</code></a> and <a href='http://bustle.github.io/mobiledoc-kit/demo/docs/Editor.html#toggleSection'><code>editor.toggleSelection</code></a> methods to modify
the text.
Use the <a href='http://bustle.github.io/mobiledoc-kit/demo/docs/Editor.html#toggleMarkup'><code>editor.toggleMarkup</code></a>, <a href='http://bustle.github.io/mobiledoc-kit/demo/docs/Editor.html#toggleSection'><code>editor.toggleSection</code></a> and <a href='http://bustle.github.io/mobiledoc-kit/demo/docs/Editor.html#setAttribute'><code>editor.setAttribute</code></a> methods to modify the text.
</p>

<div class="two-column">
Expand All @@ -186,6 +214,9 @@ <h3>Toolbar Buttons</h3>
$('button.strong').click(() =&gt; {
editor.toggleMarkup('strong');
});
$('button.center').click(() =&gt; {
editor.setAttribute('text-align', 'center');
});
</code></pre>
</div>
<div class="column right">
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
],
"license": "MIT",
"dependencies": {
"mobiledoc-dom-renderer": "0.6.5",
"mobiledoc-text-renderer": "0.3.2"
"mobiledoc-dom-renderer": "0.7.0",
"mobiledoc-text-renderer": "0.4.0"
},
"devDependencies": {
"broccoli": "^1.1.3",
Expand Down
21 changes: 21 additions & 0 deletions src/css/mobiledoc-kit.css
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,27 @@
max-width: 100%;
}

.__mobiledoc-editor [data-md-text-align='left'] {
text-align: left;
}

.__mobiledoc-editor [data-md-text-align='center'] {
text-align: center;
}

.__mobiledoc-editor [data-md-text-align='right'] {
text-align: right;
}

.__mobiledoc-editor [data-md-text-align='justify'] {
text-align: justify;
}

.__mobiledoc-editor ol,
.__mobiledoc-editor ul {
list-style-position: inside;
}

/**
* Cards
*/
Expand Down
12 changes: 12 additions & 0 deletions src/js/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,18 @@ class Editor {
this.run(postEditor => postEditor.toggleSection(tagName, this.range));
}

/**
* Sets an attribute for the current active section(s).
*
* @param {String} key The attribute. The only valid attribute is 'text-align'.
* @param {String} value The value of the attribute.
* @public
* @see PostEditor#setAttribute
*/
setAttribute(key, value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure this is needed, we could just document the postEditor version. But I'm not sure it is harmful. Should get jsdoc.

this.run(postEditor => postEditor.setAttribute(key, value, this.range));
}

/**
* Finds and runs the first matching key command for the event
*
Expand Down
19 changes: 19 additions & 0 deletions src/js/editor/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,25 @@ class PostEditor {
this.setRange(nextRange);
}

setAttribute(key, value, range) {
range = toRange(range);
let { post } = this.editor;
let attribute = `data-md-${key}`;

post.walkMarkerableSections(range, section => {
if (section.isListItem) {
section = section.parent;
}

if (section.getAttribute(attribute) !== value) {
section.setAttribute(attribute, value);
this._markDirty(section);
}
});

this.setRange(range);
}

_isSameSectionType(section, sectionTagName) {
return section.isListItem ?
section.parent.tagName === sectionTagName :
Expand Down
25 changes: 25 additions & 0 deletions src/js/models/_attributable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const VALID_ATTRIBUTES = [
'data-md-text-align'
];

/*
* A "mixin" to add section attribute support
* to markup and list sections.
*/
export function attributable(ctx) {
ctx.attributes = {};

ctx.setAttribute = (key, value) => {
if (!VALID_ATTRIBUTES.includes(key)) {
throw new Error(`Invalid attribute "${key}" was passed. Constrain attributes to the spec-compliant whitelist.`);
}
ctx.attributes[key] = value;
};
ctx.removeAttribute = key => {
delete ctx.attributes[key];
};
ctx.getAttribute = key => ctx.attributes[key];
ctx.eachAttribute = cb => {
Object.entries(ctx.attributes).forEach(([k,v]) => cb(k,v));
};
}
6 changes: 5 additions & 1 deletion src/js/models/list-section.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { LIST_SECTION_TYPE } from './types';
import Section from './_section';
import { normalizeTagName } from '../utils/dom-utils';
import assert from '../utils/assert';
import { attributable } from './_attributable';

export const VALID_LIST_SECTION_TAGNAMES = [
'ul', 'ol'
Expand All @@ -12,12 +13,15 @@ export const VALID_LIST_SECTION_TAGNAMES = [
export const DEFAULT_TAG_NAME = VALID_LIST_SECTION_TAGNAMES[0];

export default class ListSection extends Section {
constructor(tagName=DEFAULT_TAG_NAME, items=[]) {
constructor(tagName=DEFAULT_TAG_NAME, items=[], attributes={}) {
super(LIST_SECTION_TYPE);
this.tagName = tagName;
this.isListSection = true;
this.isLeafSection = false;

attributable(this);
Object.entries(attributes).forEach(([k,v]) => this.setAttribute(k, v));

this.items = new LinkedList({
adoptItem: i => {
assert(`Cannot insert non-list-item to list (is: ${i.type})`,
Expand Down
7 changes: 6 additions & 1 deletion src/js/models/markup-section.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Markerable from './_markerable';
import { normalizeTagName } from '../utils/dom-utils';
import { contains } from '../utils/array-utils';
import { MARKUP_SECTION_TYPE } from './types';
import { attributable } from './_attributable';

// valid values of `tagName` for a MarkupSection
export const VALID_MARKUP_SECTION_TAGNAMES = [
Expand Down Expand Up @@ -33,8 +34,12 @@ export const MARKUP_SECTION_ELEMENT_NAMES = [
export const DEFAULT_TAG_NAME = VALID_MARKUP_SECTION_TAGNAMES[8];

const MarkupSection = class MarkupSection extends Markerable {
constructor(tagName=DEFAULT_TAG_NAME, markers=[]) {
constructor(tagName=DEFAULT_TAG_NAME, markers=[], attributes={}) {
super(MARKUP_SECTION_TYPE, tagName, markers);

attributable(this);
Object.entries(attributes).forEach(([k,v]) => this.setAttribute(k, v));

this.isMarkupSection = true;
}

Expand Down
8 changes: 4 additions & 4 deletions src/js/models/post-node-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,19 @@ class PostNodeBuilder {
* @param {Marker[]} [markers=[]]
* @return {MarkupSection}
*/
createMarkupSection(tagName=DEFAULT_MARKUP_SECTION_TAG_NAME, markers=[], isGenerated=false) {
createMarkupSection(tagName=DEFAULT_MARKUP_SECTION_TAG_NAME, markers=[], isGenerated=false, attributes={}) {
tagName = normalizeTagName(tagName);
const section = new MarkupSection(tagName, markers);
const section = new MarkupSection(tagName, markers, attributes);
if (isGenerated) {
section.isGenerated = true;
}
section.builder = this;
return section;
}

createListSection(tagName=DEFAULT_LIST_SECTION_TAG_NAME, items=[]) {
createListSection(tagName=DEFAULT_LIST_SECTION_TAG_NAME, items=[], attributes={}) {
tagName = normalizeTagName(tagName);
const section = new ListSection(tagName, items);
const section = new ListSection(tagName, items, attributes);
section.builder = this;
return section;
}
Expand Down
Loading