Skip to content

Commit

Permalink
Add Editor#activeSectionAttributes to support toolbar state for new s…
Browse files Browse the repository at this point in the history
…ection attributes in mobiledoc 0.3.2
  • Loading branch information
lukemelia committed Aug 21, 2019
1 parent 35ef6de commit 0f65798
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 4 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ The available lifecycle hooks are:
* `editor.cursorDidChange()` - When the cursor (or selection) changes as a result of arrow-key
movement or clicking in the document.
* `editor.onTextInput()` - When the user adds text to the document (see [example](https://github.com/bustlelabs/mobiledoc-kit#responding-to-text-input))
* `editor.inputModeDidChange()` - The active section(s) or markup(s) at the current cursor position or selection have changed. This hook can be used with `Editor#activeMarkups` and `Editor#activeSections` to implement a custom toolbar.
* `editor.inputModeDidChange()` - The active section(s) or markup(s) at the current cursor position or selection have changed. This hook can be used with `Editor#activeMarkups`, `Editor#activeSections`, and `Editor#activeSectionAttributes` to implement a custom toolbar.
* `editor.beforeToggleMarkup(({markup, range, willAdd} => {...})` - Register a
callback that will be called before `editor#toggleMarkup` is applied. If any
callback returns literal `false`, the toggling of markup will be canceled.
Expand Down
36 changes: 33 additions & 3 deletions src/js/editor/edit-state.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { contains, isArrayEqual } from 'mobiledoc-kit/utils/array-utils';
import {
contains,
isArrayEqual,
objectToSortedKVArray
} from 'mobiledoc-kit/utils/array-utils';
import Range from 'mobiledoc-kit/utils/cursor/range';

/**
Expand All @@ -14,7 +18,8 @@ class EditState {
range: Range.blankRange(),
activeMarkups: [],
activeSections: [],
activeSectionTagNames: []
activeSectionTagNames: [],
activeSectionAttributes: {}
};

this.prevState = this.state = defaultState;
Expand Down Expand Up @@ -46,7 +51,8 @@ class EditState {
inputModeDidChange() {
let { state, prevState } = this;
return (!isArrayEqual(state.activeMarkups, prevState.activeMarkups) ||
!isArrayEqual(state.activeSectionTagNames, prevState.activeSectionTagNames));
!isArrayEqual(state.activeSectionTagNames, prevState.activeSectionTagNames) ||
!isArrayEqual(objectToSortedKVArray(state.activeSectionAttributes), objectToSortedKVArray(prevState.activeSectionAttributes)));
}

/**
Expand All @@ -63,6 +69,14 @@ class EditState {
return this.state.activeSections;
}


/**
* @return {Object}
*/
get activeSectionAttributes() {
return this.state.activeSectionAttributes;
}

/**
* @return {Markup[]}
*/
Expand Down Expand Up @@ -97,6 +111,7 @@ class EditState {
state.activeSectionTagNames = state.activeSections.map(s => {
return s.isNested ? s.parent.tagName : s.tagName;
});
state.activeSectionAttributes = this._readSectionAttributes(state.activeSections);
return state;
}

Expand All @@ -115,6 +130,21 @@ class EditState {
return post.markupsInRange(range);
}

_readSectionAttributes(sections) {
return sections.reduce((sectionAttributes, s) => {
let attributes = s.isNested ? s.parent.attributes : s.attributes;
Object.keys(attributes || {}).forEach(attrName => {
let camelizedAttrName = attrName.replace(/^data-md-/, '');
let attrValue = attributes[attrName];
sectionAttributes[camelizedAttrName] = sectionAttributes[camelizedAttrName] || [];
if (!contains(sectionAttributes[camelizedAttrName], attrValue)) {
sectionAttributes[camelizedAttrName].push(attrValue);
}
});
return sectionAttributes;
}, {});
}

_removeActiveMarkup(markup) {
let index = this.state.activeMarkups.indexOf(markup);
this.state.activeMarkups.splice(index, 1);
Expand Down
4 changes: 4 additions & 0 deletions src/js/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,10 @@ class Editor {
return activeSections[activeSections.length - 1];
}

get activeSectionAttributes() {
return this._editState.activeSectionAttributes;
}

detectMarkupInRange(range, markupTagName) {
let markups = this.post.markupsInRange(range);
return detect(markups, markup => {
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/editor/editor-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,28 @@ test('activeSections is empty when the editor has no cursor', (assert) => {
assert.equal(editor.activeSections.length, 0, 'empty activeSections');
});

test('activeSectionAttributes of a rendered blank mobiledoc is an empty array', (assert) => {
editor = Helpers.mobiledoc.renderInto(editorElement, ({post}) => {
return post();
});

assert.ok(editor.hasRendered, 'editor has rendered');
assert.deepEqual(editor.activeSectionAttributes, {}, 'empty activeSectionAttributes');
});

test('activeSectionAttributes is updated based on the selection', (assert) => {
editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker}) => {
return post([markupSection('p', [marker('abc')], false, { 'data-md-text-align': 'center' })]);
}, {autofocus: false});

assert.ok(!editor.hasCursor(), 'precond - no cursor');
assert.deepEqual(editor.activeSectionAttributes, {}, 'empty activeSectionAttributes');

let head = editor.post.sections.head;
editor.selectRange(Range.create(head, 'abc'.length));
assert.deepEqual(editor.activeSectionAttributes['text-align'], ['center'], 'active section attributes captured');
});

test('editor.cursor.hasCursor() is false before rendering', (assert) => {
let mobiledoc = Helpers.mobiledoc.build(({post}) => post());
editor = new Editor({mobiledoc});
Expand Down

0 comments on commit 0f65798

Please sign in to comment.