Code Monkey home page Code Monkey logo

ember-mobiledoc-editor's Introduction

ember-mobiledoc-editor

npm version Build Status Ember Observer Score

A Mobiledoc editor written using Ember.js UI components and Mobiledoc Kit.

Additionally, ember-mobiledoc-editor supports the creation of Mobiledoc cards as Ember components. This is a significant improvement for developer ergonomics over using Mobiledoc cards directly.

Installation

ember install ember-mobiledoc-editor

ember-mobiledoc-editor will install the mobiledoc-kit package as a dependency and load its assets.

Usage

This addon is primarily composed of components used for building an editor UI.

{{mobiledoc-editor}}

This component is the main entrance point for a mobiledoc editor instance in your Ember app. Used in the most basic form it will render a dummy editor with no toolbar. For example this usage:

{{mobiledoc-editor}}

Will render a blank Mobiledoc into the following DOM:

<article class="mobiledoc-editor">
  <div class="mobiledoc-editor__editor-wrapper">
    <div class="mobiledoc-editor__editor"></div>
  </div>
</article>

The component accepts these arguments:

  • mobiledoc, a Mobiledoc to be edited
  • cards, an array of available cards for use by the editor. Jump to the section on Component-based cards for more detail on how to use cards with Ember components.
  • atoms, an array of available atoms for use by the editor. Jump to the section on Component-based atoms for more detail on how to use atoms with Ember components.
  • spellcheck boolean
  • autofocus boolean
  • showLinkTooltips boolean
  • placeholder string -- the placeholder text to display when the mobiledoc is blank
  • options hash -- any properties in the options hash will be passed to the MobiledocKitEditor constructor
  • serializeVersion string -- The mobiledoc version to serialize to when firing the on-change action. Default: 0.3.2
  • sectionAttributesConfig hash -- information about supported section attributes. defaults to { 'text-align': { values: ['left', 'center', 'right'], defaultValue: 'left' } }
  • on-change -- Accepts an action that the component will send every time the mobiledoc is updated
  • will-create-editor -- Accepts an action that will be sent when the instance of the MobiledocKitEditor is about to be created This action may be fired more than once if the component's mobiledoc property is set to a new value.
  • did-create-editor -- Accepts an action that will be sent after the instance of the MobiledocKitEditor is created. The action is passed the created editor instance. This action may be fired more than once if the component's mobiledoc property is set to a new value.

For example, the following index route and template would log before and after creating the MobiledocKitEditor, and every time the user modified the mobiledoc (by typing some text, e.g.).

// routes/index.js

export default Ember.Route.extend({
 ...,
 actions: {
   mobiledocWasUpdated(updatedMobiledoc) {
     console.log('New mobiledoc:',updatedMobiledoc);
     // note that this action will be fired for every changed character,
     // so you may want to debounce API calls if you are using it for
     // an "autosave" feature.
   },
   willCreateEditor() {
     console.log('about to create the editor');
   },
   didCreateEditor(editor) {
     console.log('created the editor:', editor);
   }
 }
});
{{! index.hbs }}

{{mobiledoc-editor
    on-change=(action 'mobiledocWasUpdated')
    will-create-editor=(action 'willCreateEditor')
    did-create-editor=(action 'didCreateEditor')
}}

Of course often you want to provide a user interface to bold text, create headlines, or otherwise reflect the state of the editor.

Called with a block, the editor param is yielded.

{{#mobiledoc-editor mobiledoc=someDoc as |editor|}}
{{/mobiledoc-editor}}

editor has the following properties, useful to inspect the current state of the editor:

  • editor, the Mobiledoc kit editor instance itself
  • activeSectionTagNames, an object with true values for section tag names in the current selection. For example activeSectionTagNames.isH1.
  • activeMarkupTagNames, an object with true values for markup tag names in the current selection. For example activeMarkupTagNames.isStrong

Additionally editor provides the following actions:

  • toggleMarkup, toggling the passed markup tag name in the current selection.
  • toggleSection, toggling the passed section tag name in the current selection. Pass a string tagName as an argument. Possible valid values: "h1", "h2", "p", "blockquote". To toggle to-from a list section pass "ul" or "ol".
  • toggleLink, toggling the linking of a selection. The user will be prompted for a URL if required.
  • addCard, passed a card name and payload will add that card at the end of the post.
  • addCardInEditMode, passed a card name and payload will add that card at the end of a post and render it in "edit" mode initially.
  • addAtom, passed an atomName, text, and payload, will add that atom at the cursor position.
  • setAttribute, passed an attribute name and attribute value, will add that attribute to the current section, or remove the attribute if the value is the default value.
  • removeAttribute, passed an attribute name, will remove that attribute from the current section.

The editor object is often used indirectly by passing it to other components. For example:

{{#mobiledoc-editor as |editor|}}
  <div class="toolbar">
    {{mobiledoc-markup-button editor=editor for="strong"}}
    {{mobiledoc-link-button editor=editor}}
  </div>
{{/mobiledoc-editor}}

{{mobiledoc-section-button}}

Requires two properties:

  • for, the name of the tag
  • editor, the editor instance from mobiledoc-editor

And accepts one optional property:

  • title, added as the title attribute on the button element

Creates a <button> element that has a class of active when the provided section tag is used in the current selection. For example:

{{mobiledoc-section-button editor=editor for="h2"}}

Alternatively, custom text for the HTML of the button can be yielded:

{{#mobiledoc-section-button editor=editor for="h2"}}
  Headline 2
{{/mobiledoc-section-button}}

When clicked, the section tag name will be toggled.

{{mobiledoc-markup-button}}

Requires two properties:

  • for, the name of the tag
  • editor, the editor instance from mobiledoc-editor

And accepts one optional property:

  • title, added as the title attribute on the button element

Creates a <button> element that has a class of active when the provided markup tag is used in the current selection. For example:

{{mobiledoc-markup-button editor=editor for="em"}}

Alternatively, custom text for the HTML of the button can be yielded:

{{#mobiledoc-markup-button editor=editor for="em"}}
  Italicize
{{/mobiledoc-markup-button}}

When clicked, the markup tag name will be toggled.

{{mobiledoc-link-button}}

Requires one property:

  • editor, the editor instance from mobiledoc-editor

And accepts one optional property:

  • title, added as the title attribute on the button element

Creates a <button> element that has a class of active when the an a tag is used in the current selection. For example:

{{mobiledoc-link-button editor=editor}}

Custom text for the HTML of the button can be yielded:

{{#mobiledoc-link-button editor=editor}}
  Toggle Link
{{/mobiledoc-link-button}}

When clicked, the presence of a link will be toggled. The user will be prompted for a URL if required.

{{mobiledoc-section-attribute-button}}

Requires two properties:

  • attributeName, the name of the attribute
  • attributeValue, the value of the attribute
  • editor, the editor instance from mobiledoc-editor

And accepts one optional property:

  • title, added as the title attribute on the button element

Creates a <button> element that has a class of active when the provided attributeValue is used in the current section. For example:

{{mobiledoc-section-attribute-button editor=editor attributeName="text-align" attributeValue="center"}}

Alternatively, custom text for the HTML of the button can be yielded:

{{#mobiledoc-section-attribute-button editor=editor attributeName="text-align" attributeValue="center"}}
  Center
{{/mobiledoc-section-attribute-button}}

{{mobiledoc-toolbar}}

Requires one property:

  • editor, the editor instance from mobiledoc-editor

This component creates a full-featured toolbar for the Mobiledoc editor. For example:

{{#mobiledoc-editor as |editor|}}
  {{mobiledoc-toolbar editor=editor}}
{{/mobiledoc-editor}}

Component-based Cards

Mobiledoc supports "cards", blocks of rich content that are embedded into a post. For more details on the API for authoring cards in vanilla JavaScript, see CARDS.md.

ember-mobiledoc-editor comes with a handle helper for using Ember components as the display and edit modes of a card. Create a list of cards using the createComponentCard helper:

import Ember from 'ember';
import createComponentCard from 'ember-mobiledoc-editor/utils/create-component-card';

export default Ember.Component.extend({
  cards: Ember.computed(function() {
    return [
      createComponentCard('demo-card')
    ];
  })
});

And pass that list into the {{mobiledoc-editor}} component:

{{mobiledoc-editor cards=cards}}

When added to the post (or loaded from a passed-in Mobiledoc), these components will be used:

  • For display, the demo-card component will be called
  • For edit, the demo-card-editor component will be called

The component will be provided with the following attrs:

  • payload, the payload for this card. Note the payload object is disconnected from the card's payload in the serialized mobiledoc. To update the mobiledoc payload, use the saveCard action.
  • editCard, an action for toggling this card into edit mode (this action is a no-op if the card is already in edit mode)
  • removeCard, an action for removing this card (see the "remove" Mobiledoc card action)
  • saveCard, an action accepting new payload for the card, then saving that payload and toggling this card into display mode can optionally be passed an extra false argument to avoid toggling to display mode (see the "save Mobiledoc card action)
  • cancelCard, an action toggling this card to display mode without saving (this action is a no-op if the card is already in display mode) (see the "cancel Mobiledoc card action)
  • cardName the name of this card
  • editor A reference to the mobiledoc-kit
  • postModel A reference to this card's model in the editor's abstract tree. This may be necessary to do programmatic editing (such as moving the card via the postEditor#moveSection API that Mobiledoc editor provides)

Component-based Atoms

Mobiledoc supports "atoms", inline sections of rich content that are embedded into a line of text in your post. For more details on the API for authoring atoms in vanilla JavaScript, see ATOMS.md.

ember-mobiledoc-editor comes with a handle helper for using Ember components as an atom. Create a list of atoms using the createComponentAtom helper:

import Ember from 'ember';
import createComponentAtom from 'ember-mobiledoc-editor/utils/create-component-atom';

export default Ember.Component.extend({
  atoms: Ember.computed(function() {
    return [
      createComponentAtom('demo-atom')
    ];
  })
});

And pass that list into the {{mobiledoc-editor}} component:

{{mobiledoc-editor atoms=atoms}}

Editor Lifecycle Hooks

Currently editor lifecycle hooks are available by available by extending the mobiledoc-editor component.

import Component from 'ember-mobiledoc-editor/components/mobiledoc-editor/component';

export default Component.extend({
  cursorDidChange(editor) {
    this._super(...arguments);
    // custom event handling goes here
  }
});

The following lifecycle hooks are available:

  • willRender
  • didRender
  • postDidChange
  • inputModeDidChange
  • cursorDidChange

Test Helpers

ember-mobiledoc-editor exposes two test helpers for use in your acceptance tests:

  • insertText(editorElement, text) -- inserts text into the editor (at the end)
  • run(editorElement, callback) -- equivalent to editor.run, it calls the callback with the postEditor

Example usage:

// acceptance test
import { insertText, run } from '../../helpers/ember-mobiledoc-editor';
import { find } from '@enber/test-helpers';
test('visit /', function(assert) {
  visit('/');
  andThen(() => {
    let editorEl = find('.mobiledoc-editor__editor');
    return insertText(editorEl, 'here is some text');
    /* Or:
      return run(editorEl, (postEditor) => ...);
    */
  });
  andThen(() => {
    // assert text inserted, etc.
  });
});

Developing ember-mobiledoc-editor

Releasing a new version:

This README outlines the details of collaborating on this Ember addon.

Installation

  • git clone <repository-url> this repository
  • cd ember-mobiledoc-editor
  • yarn install

Run the development server:

Running Tests

  • yarn test (Runs ember try:each to test your addon against multiple Ember versions)
  • ember test
  • ember test --server

Building

  • ember build

For more information on using ember-cli, visit https://ember-cli.com/.

ember-mobiledoc-editor's People

Contributors

3h15 avatar backspace avatar bantic avatar chrisdpeters avatar chrisgame avatar cyril-sf avatar dependabot[bot] avatar dtetto avatar ef4 avatar eguitarz avatar ember-tomster avatar habdelra avatar jacojoubert avatar jasonbekolay avatar jordpo avatar kategengler avatar knownasilya avatar kylenathan avatar lukemelia avatar mattmcmanus avatar mixonic avatar orisomething avatar rlivsey avatar srsgores avatar stevenwu avatar t4t5 avatar tholman avatar zeejab avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ember-mobiledoc-editor's Issues

env not passed to atom component

env and env.save are not passed to atom components as they are for card components.
Is it by design ?
Or can I add it ?

{{component card.cardName
    editor=editor
    postModel=card.postModel
    cardName=card.cardName
    payload=card.payload
    data=card.payload
    env=card.env
    editCard=(action card.env.edit)
    saveCard=(action card.env.save)
    cancelCard=(action card.env.cancel)
    removeCard=(action card.env.remove)}}


{{component atom.atomName
    editor=editor
    postModel=atom.postModel
    atomName=atom.atomName
    payload=atom.payload
    value=atom.value}}`

Scroll does not follow cursor when typing

This is essentially a duplicate of this issue bustle/mobiledoc-kit#558.

In that issue the recommendation is for the editor to handle the scrolling. Are there plans to do this for the ember mobliedoc editor? Or is it already implemented but interactions with my code is breaking it?

Mobiledoc renderer included in vendor.js twice

After building, try:

grep "define('mobiledoc-dom-renderer\/utils\/tag-names'" dist/assets/vendor.js

You will see 2 matching lines.

If you include ember-mobiledoc-dom-renderer in your app as well, try:

grep "define('ember-mobiledoc-dom-renderer/mobiledoc-dom-renderer\/utils\/tag-names'" dist/assets/vendor.js

You will see 1 matching line.

That means that if you want to edit & render mobiledoc with an ember app, you will include mobiledoc-dom-renderer in vendor.js 3 times. Aside from being just plain crazy, it's a problem for those of us concerned about page weight.

IMO this addon should not require the dom renderer at all (let alone twice). It's fine to have it in dev dependencies (for tests), but afaict, the addon code itself does not use the dom renderer anywhere. So it shouldn't be in dependencies and it shouldn't be imported in index.js either.

Also mobiledoc-kit adds mobiledoc-dom-renderer to dependencies as well, but afaict it's just to support editor.serialize('html'), even though it has a fallback way to generate that html. IMO, none of the editor packages ought to require the renderer packages (since the renderers are not used to generate the html for the editors).

Best strategy for migrating existing content?

Mobiledoc Editor looks awesome (especially cards), and I want to use it on my Ember app. Even though it's a green field app, there is existing content in my database in the field (project.description) in which I want to use the Mobiledoc editor.

So right now when I edit a Project, the Mobiledoc editor crashes because the "existing" value is not valid Mobiledoc.

I'm thinking my best option is to convert the existing text/html to Mobiledoc format. Is there already a converter or do I have to roll my own?

Or is there another option I can use that will transparently do this the first time the doc is edited?

Best way to setup mobiledoc structure

Mobiledoc format seems awesome, but I'm not sure how to bind it to model attributes in ember.

Is the recommended approach to:

  1. Bootstrap mobiledoc attributes on the server with the base mobiledoc structure
  2. Somehow bind and update the content within that attribute as it's updated in the UI
  3. Persist the entire blob of mobiledoc with the update content and tags

If so, will you post an example of binding an ember model attribute's content within a mobiledoc blob?

Again, I realize this library is WIP, so I'd understand if your answer is to just say 'hold off until a future version'

Improve API for current state

Currently, buttons for components toggle active/inactive state based on matching the contents of an array. For example this code manages if text is strong and this code if you are in an active section.

The current implementation has two failures:

  • The in-array helper is poorly namespaced, and additionally is not actually bound to the contents of the array. In our use case this is fine, however it is also a footgun as a generic helper.
  • The API seems less than ideal for someone who wants to consume this state directly in their own app.

on-change event not fired after running editor.run

Im attempting to do a search and replace. Replacing the text in a range with a link.

I have it creating the link properly and that all seems to be working. However the on-change callback is not being fired. I added postEditor.scheduleDidUpdate(); but that didn't change it.

I attempted to walk through the code and it seemed to be calling the isDirty call backs, but my ember call back (passed as on-change) was not called.

    if (range) {
      editor.run(postEditor => {
        let position = postEditor.deleteRange(range);
        let link = postEditor.builder.createMarkup('a', {href: linkURL});
        postEditor.insertTextWithMarkup(position, linkName, [link]);
        postEditor.scheduleDidUpdate();
      });
    }

Inserting text into the editor

First of all, thanks for the great work on this plugin! ๐Ÿ˜€

I was wondering if there's a way to insert text into the editor from the outside. I'm working on a feature that allows users to insert some arbitrary text by clicking on something that's outside the editor.

I tried working with the innerHTML property (i.e. appending to the editor by modifying its innerHTML) but that doesn't quite get it right. It will insert it goes out of sync; deleting that text is impossible.

Any ideas?

Fastboot compatibility

It looks like some work is needed to make this addon fastboot-compatible. It currently fails at boot time when it hits require("mobiledoc-kit")["registerGlobal"](window, document); in mobiledoc-kit's globals build.

Passing context or metadata into component cards/atoms

I'd like to be able to pass some arbitrary metadata about a mobiledoc down into the Ember components that render cards and/or atoms. Is there any good way to do this at the moment?

Here's a use case: I have some metadata about a mobiledoc's author. I'd like to refer to this in building the payload for certain atoms.

At the moment, it seems to me that the only way to do this is to subclass the mobiledoc-editor component and write a custom template that passes in the additional properties here. That doesn't seem optimal to me, because there's no way to inherit a parent template and override only a subsection thereof. Among other things, it's hard for maintainability; every time the component template changed upstream, consumers would have to manually review changes to the code upstream and update...

I understand if you don't want unnecessary clutter in the code, but I think it would be quite helpful to provide some way for consumers to pass context into their atom/card components. Any suggestions? (I'd be happy to provide a PR if appropriate.)

Adding a card to an empty mobiledoc fails

Adding a new card to an empty mobiledoc fails in 0.4.2; works fine in 0.4.1. (Adding a single character to the document, then adding the card is sufficient to workaround the issue.)

Multiple mobiledoc components won't render.

I have a comment box composer component which appears underneath multiple posts. Unfortunately, it only renders the first mobiledoc editor and won't render any subsequent posts. I'm not sure if this is intended.

I have a mobile doc for a main post composer on the top of the page which renders fine even with the multiple posts underneath with the top one having the mobiledoc editor.

Is there a solution to this or is it just out of scope and not intended. (I'm using the markup etc. for @ style mentions.

Uncaught TypeError: Cannot read property 'commit' of null
    at Environment.commit (291a4de8b38b36f4040b.js?r=vendor-frontend-js:22571)
    at Environment.commit (291a4de8b38b36f4040b.js?r=vendor-frontend-js:32633)
    at InteractiveRenderer._renderRootsTransaction (291a4de8b38b36f4040b.js?r=vendor-frontend-js:34758)
    at InteractiveRenderer._renderRoot (291a4de8b38b36f4040b.js?r=vendor-frontend-js:34688)
    at InteractiveRenderer._appendDefinition (291a4de8b38b36f4040b.js?r=vendor-frontend-js:34613)
    at InteractiveRenderer.appendOutletView (291a4de8b38b36f4040b.js?r=vendor-frontend-js:34601)
    at invoke (291a4de8b38b36f4040b.js?r=vendor-frontend-js:24506)
    at Queue.flush (291a4de8b38b36f4040b.js?r=vendor-frontend-js:24424)
    at DeferredActionQueues.flush (291a4de8b38b36f4040b.js?r=vendor-frontend-js:24577)
    at Backburner.end (291a4de8b38b36f4040b.js?r=vendor-frontend-js:24709)

Here's the mention code in the post component.

{{#mobiledoc-editor placeholder=(t 'editable.placeholder')
                    on-change=(action 'onEdit')
                    did-create-editor=(action 'didCreateEditor')
                    }}
{{/mobiledoc-editor}}

When I navigate to another page with the same style of post/comments I get an infinite rendering error.

Uncaught Error: infinite rendering invalidation detected
    at Array.loopEnd (291a4de8b38b36f4040b.js?r=vendor-frontend-js:34566)
    at Backburner._trigger (291a4de8b38b36f4040b.js?r=vendor-frontend-js:25118)
    at Backburner.end (291a4de8b38b36f4040b.js?r=vendor-frontend-js:24722)
    at Backburner._run (291a4de8b38b36f4040b.js?r=vendor-frontend-js:25059)
    at Backburner._join (291a4de8b38b36f4040b.js?r=vendor-frontend-js:25035)
    at Backburner.join (291a4de8b38b36f4040b.js?r=vendor-frontend-js:24776)
    at Array.loopEnd (291a4de8b38b36f4040b.js?r=vendor-frontend-js:34569)
    at Backburner._trigger (291a4de8b38b36f4040b.js?r=vendor-frontend-js:25118)
    at Backburner.end (291a4de8b38b36f4040b.js?r=vendor-frontend-js:24722)
    at Backburner._run (291a4de8b38b36f4040b.js?r=vendor-frontend-js:25059)

fix failing tests on ember-beta

There are a number of failing tests when using ember's beta release:

  • env property is nullified in cards (see emberjs/ember.js#13907)
  • Use willDestroy instead of willDestroyElement in mobiledoc-editor component
  • this.$('input').change() does not result in ember updating the href property of the link-prompt component, causing some tests to fail

Problems with text disappearing or failing to link, when linking

Several bug reports reference jumping cursors or deleting text when using the ember-mobiledoc-editor UI, especially surrounding the link button. I have not been able to duplicate nor identify, but have collected some mentions of it here:

[Images removed]

Add button component

For changing markup or section, we should provide a general button component (or perhaps two).

addAtom only works when editor is focused

I have some buttons in my editor toolbar that add an atom to the editor. Here's the code:

<button class="btn tag tag-info" {{action editor.addAtom 'message-tag' 'example'}}>example</button>

However, the button only works when the editor is actively focused. If the user presses a button while the editor is not focused, nothing happens. The ideal behaviour would be appending the atom to the end of the document.

Auto-expanding the editing area to a max height

Thanks for this great add-on. This approach seems more robust than using TinyMCE, etc.

Is there a plan to add autosizing functionality to the component so it expands to a max of X lines/pixels, and becomes scrollable after that? I'm happy to work on this and submit a PR but didn't want to create duplicate work if it's already part of your near-term roadmap.

Currently when I add more text, it just flows off the component surface (example attached).
screen shot 2016-05-04 at 3 39 20 pm

Nesting within a card returns an error due to undefined section

screen shot 2017-09-26 at 12 12 11 am

TypeError: Cannot read property 'isNested' of null
    at editor.js:1188
    at Editor.run (editor.js:755)
    at Editor.insertCard (editor.js:1176)
    at Class._addCard (component.js:351)
    at Class.addCard (component.js:113)
    at Backburner.join (backburner.js:567)
    at Function.run$1.join (ember-metal.js:4370)
    at action.js:388
    at exports.flaggedInstrument (ember-metal.js:3797)
    at EmptyObject.addCard (action.js:387)

My code looks something like this:

<section>
  <button onclick={{action editCard}} type='button'>Edit</button>
</section>
{{mobiledoc-editor}}

Full code is here https://github.com/knownasilya/ember-mobiledoc-grid/blob/master/addon/components/grid-cards-row/template.hbs

This is within a display card. The card is added to the editor, when I click inside the card and try to addCard inside this nested editor, it errors in this way.

Could you supply an example?

Sorry for my ignorance but I do not understand how to use the most basic function of the {{content-kit-editor}}. How do I get and save the text I input in the editor? Could someone provide a quick example?

Warning: ignoring input sourcemap...

I get the following warning when using the addon:

Warning: ignoring input sourcemap for vendor/content-kit-editor/global/content-kit-editor.js because ENOENT: no such file or directory, open '[path-to-app]/tmp/concat_with_maps-input_base_path-Cd3L4EPZ.tmp/0/vendor/content-kit-editor/global/content-kit-editor.map'

Could this be to do with the way the files are copied over from content-kit-editor?
Does content-kit-editor build a source map for this to use?

Cannot render component cards in FastBoot

This is kinda related to issue #50, but a very specific scenario. For FastBoot we use the mobiledoc DOM renderer with SimpleDOM. This means that we are not using this particular component to render our mobiledoc component cards https://github.com/bustlelabs/ember-mobiledoc-dom-renderer/blob/master/addon/components/render-mobiledoc.js (we do use this component to render component when running from the browser though). As such, when constructing the renderer, we need to provide cardOptions.addComponent to instruct the renderer on how to assemble the DOM for the component card, as cards are rendered thusly: https://github.com/bustlelabs/ember-mobiledoc-editor/blob/master/addon/utils/create-component-card.js#L22.

Sadly, it's not possible to generate the DOM for an Ember component in this manner using public API's.

For reference, our renderer for fastboot looks something like this:

import Ember from 'ember';
import Renderer from 'ember-mobiledoc-dom-renderer';
import Component from 'ember-component';
import computed from 'ember-computed';
import inject from 'ember-service/inject';
import createComponentCard from 'ember-mobiledoc-editor/utils/create-component-card';
import { optimizedUrl } from '../services/cloudinary';

export const cards = ['image-card', 'images-card', 'video-card'];

const { uuid } = Ember;
const { camelize } = Ember.String;

const CARD_ELEMENT_CLASS = '__rendered-mobiledoc-card';
const CARD_TAG_NAME = 'div';
const UUID_PREFIX = '__rendered-mobiledoc-entity-';

export default Component.extend({
  tagName: '',
  cardNames: cards,
  fastboot: inject(),
  isFastBoot: computed.readOnly('fastboot.isFastBoot'),

  // When in FastBoot mode, where rendering the Mobiledoc is a one-shot
  // operation, we can build the DOM directly and return the serialized value
  // as this computed property. This lets us insert the raw HTML into the
  // rendered output.
  renderedMobiledoc: computed(function() {
    let _this = this;
    let SimpleDOM = FastBoot.require('simple-dom');
    let doc = new SimpleDOM.Document();

    let cards = this.get('cardNames').map(cardName => createComponentCard(cardName, doc));
    let mobiledoc = this.get('mobiledoc');

    let renderer = new Renderer({
      cards,
      dom: doc,
      cardOptions: {
        addComponent({env, options, payload}) {
          let { name: cardName, dom } = env;
          let domForCard = camelize('domFor-' + cardName);
          // it is not possible to generate DOM for an Ember component without using
          // a template so we need to hand-craft the mobiledoc card components
          // when in the fastboot runtime
          let element = _this[domForCard](dom, payload);
          let card = {
            cardName,
            destinationElementId: element.getAttribute('id'),
            payload
          };

          return { card, element };
        },

        removeComponent: Ember.K
      }
    });
    let { result, teardown } = renderer.render(mobiledoc);

    let HTMLSerializer = new SimpleDOM.HTMLSerializer(SimpleDOM.voidMap);
    let serialized = HTMLSerializer.serialize(result);

    // Immediately teardown once we've serialized
    teardown();

    return serialized;
  }),

  domForImagesCard(doc, payload) {
    let classNames = [CARD_ELEMENT_CLASS, `${CARD_ELEMENT_CLASS}-images-card`];
    let parent = createElement(doc, CARD_TAG_NAME, classNames);

    let el1 = createElement(doc, 'div', ['images-card']);
    let el2 = createElement(doc, 'div');
    el1.appendChild(el2);

    payload.images.forEach(image => {
      let div = createElement(doc, 'div');
      let img = createElement(doc, 'img');
      img.setAttribute('src', optimizedUrl(image.url));
      div.appendChild(img);
      el2.appendChild(div);
    });
    parent.appendChild(el1);

    if (payload.caption) {
      let p = createElement(doc, 'p', ['caption']);
      let caption = doc.createTextNode(payload.caption);
      p.appendChild(caption);
      parent.appendChild(p);
    }

    return parent;
  },

  domForVideoCard(doc, payload) {
    let classNames = [CARD_ELEMENT_CLASS, `${CARD_ELEMENT_CLASS}-video-car`];
    let parent = createElement(doc, CARD_TAG_NAME, classNames);

    let posterUrl = payload.posterUrl;
    let el1 = createElement(doc, 'div', ['video-card']);
    let el2 = createElement(doc, 'div', ['lazyLoad-container']);

    if (posterUrl) {
      el2.setAttribute('style', `background-image:url("${optimizedUrl(posterUrl)}")`);
    }
    el1.appendChild(el2);
    parent.appendChild(el1);

    return parent;
  }

});

function generateUuid() {
  return `${UUID_PREFIX}${uuid()}`;
}

function createElement(dom, tagName, classNames=[]) {
  let el = dom.createElement(tagName);
  el.setAttribute('id', generateUuid());
  el.setAttribute('class', classNames.join(' '));
  return el;
}

export function buildMobiledoc(initialString="") {
  return {
    "version": "0.3.0",
    "atoms": [],
    "cards": [],
    "markups": [],
    "sections": [
      [
        1,
        "p",
        [
          [
            0,
            [],
            0,
            initialString
          ]
        ]
      ]
    ]
  };
}

placeholder does not show

When I inspect the page, the placeholder is appearing as a tag for the element. But it is not appearing on the page when the mobiledoc is empty.

Based on the documentation, so far I have this:

// template.hbs
{{#mobiledoc-editor
    mobiledoc=mobiledoc
    placeholder=placeholder
    as |editor|}}
    <div class="toolbar">
        {{mobiledoc-markup-button editor=editor for="strong"}}
        {{mobiledoc-section-button editor=editor for="h2"}}
    </div>
{{/mobiledoc-editor}}
// component.js
...
export default Ember.Component.extend({
  mobiledoc: {
    version: '0.3.0',
    atoms: [],
    markups: [],
    cards: [],
    sections: []
  },

  placeholder: 'Write here'

});

Is there something else I need to do to get the placeholder to work?

buggy behavior with events

Right now I have my mobiledoc saving changes when the user focuses out of the element.

I'm trying to add a separate input field for the title. But it seems that having another text area along with the editor causes buggy behavior. When I try to click on the title area, the mouse moves to the mobiledoc.

I've tried placing the input field outside the element but that didn't change anything. And neither do the preventDefault=false or bubbles=false options for events.

Questions:

  • Is there a way to add separate action handlers (besides on-change) to the mobiledoc using this addon?
  • Is there a better way to save the mobiledoc? (Using the on-change handler caused the mouse to move to the beginning on every change, which is why I resorted to 'focusOut'. But now it seems that I can't add another input field.)

Error when binding ember-mobiledoc-editor to a model

I have an example app hosted on S3 that demonstrates the error that I've been experiencing:
http://mobiledoc-error.s3-website-us-east-1.amazonaws.com/

And here is the accompanying Git repo:
https://github.com/chrisdpeters/mobiledoc-error

I was following @mixonic's advice on how to bind the {{mobiledoc-editor}} mobiledoc attribute to an Ember model attribute. We were discussing it in this gist.

He had suggested that I do something like this:

{{mobiledoc-editor mobiledoc=(readonly article.body) on-change=(action (mut article.body))}}

With the component call setup like this, I get the following error on the 2nd keystroke within the editor:

Uncaught TypeError: Cannot read property 'isCardSection' of null
    handleKeydown   @ editor.js:719
    handleEvent @ editor.js:616
    (anonymous function)    @ editor.js:582

If you run the S3-hosted app above, you'll see the state of the article record in Ember Inspector. It looks like it does get changed in some way but perhaps not how mobiledoc-editor expects:

{ version: 0.2.0, sections: [Array : 2] }

Is there a different type that I should be setting the model attribute to? Right now, it's setup as a string:

body: DS.attr('string')

Bonus issue: When I upgrade Ember and Ember Data to v2.2, I get the following console error on the very first keystroke:

Uncaught TypeError: Failed to execute 'setStart' on 'Range': parameter 1 is not of type 'Node'.
    _moveToNode @ cursor.js:142
    selectRange @ cursor.js:110
    renderRange @ editor.js:295
    PostEditor._renderRange @ post.js:34
    (anonymous function)    @ lifecycle-callbacks.js:21
    runCallbacks    @ lifecycle-callbacks.js:20
    complete    @ post.js:1459
    run @ editor.js:520
    _insertEmptyMarkupSectionAtCursor   @ editor.js:653
    handleKeydown   @ editor.js:667
    handleEvent @ editor.js:616
    (anonymous function)    @ editor.js:582

Then I also get the error that the Ember 1.13 is tripping.

{{mobiledoc-editor}} includes non-functioning pull-quote toggle

Was playing with the addon today and had what I thought was a pretty vanilla template:

  {{#mobiledoc-editor mobiledoc=properties.mobiledoc on-change=(action 'updateDoc') as |editor|}}
    {{mobiledoc-toolbar editor=editor}}
  {{/mobiledoc-editor}}

Everything worked great except the pull-quote button, which throws a MobileDocError with a message "Cannot set section tagName to pull-quote". Seems to be something about isValidTagName not being defined?

Not a big deal but should probably make vanilla use-case 100% functional. If you point me in the right direction I can take a crack at it

How to prevent form submit with link prompt

I realize this project is WIP, so feel free to close this if the question will be resolved as the project progresses

I'm using ember-content-kit 0.1.0 as follows:

    {{#content-kit-editor mobiledoc=mobileDoc on-change=(action 'onChange') as |contentKit|}}
        <button {{action contentKit.toggleMarkup 'strong'}}>B</button>
        <button {{action contentKit.toggleMarkup 'em'}}>I</button>
        <button {{action contentKit.toggleLink}}>Link</button>
      {{/content-kit-editor}}

One issue I'm running into is the content-kit-link-prompt does a full form submit which causes the page to refresh. How do I prevent that?

Infinite loop

this.get("editor") is called inside of the editor CP, causing an infinite loop here

It appears cursorDidChange is being triggered immediately after creating the editor.

Combining Component Cards with the default cardsList

I've been getting to know the mobiledoc-kit for the past couple days and am really loving it! Thanks for building this, it's so much better than attempting to roll my own contenteditable text areas.

I've been able to create my own Component Cards, but I'm now trying to add the component card to the default cardsList:

    import Ember from 'ember';
    import createComponentCard from 'ember-mobiledoc-editor/utils/create-component-card';
    import { cardsList } from '../mobiledoc-cards/index';

    // Combining Component Card with the default cardsList
    export default Ember.Component.extend({
        cards: Ember.computed(function() {
            return [
                createComponentCard('demo-card'),
                cardsList
            ];
        })
    });

Here I get an error Uncaught Error: Unknown card encountered: image-card when trying to add an image card.

What would be the correct way of doing this?

only pass appropriate actions to cards, depending on mode

component cards currently get the full set of component actions (editCard, saveCard, etc), but some of the actions are meaningless depending on the card's current mode. If the card is in edit mode, the editCard action is meaningless, for example.

An improvement would be to only pass the appropriate actions and/or give a meaningful error message if the action does not make sense (calling editCard when card is in edit mode, e.g.).

Creating a sticky toolbar

From looking at the source it doesn't appear that creating a sticky toolbar is currently possible within the confines of what ember-content-kit provides. There are two issues that I identified:

Selection state
Ember-content-kit doesn't expose if there is currently a selection inside an editable card. This prevents us from showing/hiding the toolbar based on the selection state. I looked at content-kit and it doesn't appear to expose such data either.

Selection change
Content-kit does have a cursorDidChange hook, but ember-content-kit doesn't expose it so we don't know when to update the positioning of the toolbar. There is a component tether-to-selection, but it only does an initial positioning, not a true tether.

Any guidance on how to approach these issues? Not sure if I missed something.

It seems like it would make sense to have ember-content-kit yield a selection property that can be bound to for showing/hiding content and calculating positioning. Updating the tether-to-selection component to use this selection to actually tether and only show itself if there is a selection.

Use a stable key to guard against data loop

The data-loop defenses in the mobiledoc-editor component (_localMobiledoc and _upstreamMobileDoc) aren't sufficient to guard against many possible data loops, so it's easy to lose cursor position.

We could use an explicit document key/id instead. In practice, there is usually a good key laying around, like:

{{mobiledoc-editor mobiledoc=post.body key=post.id on-change=(action (mut post.body))}}

The above example would only rebuild the editor if key changes. It would safely ignore any of its own changes that come back down.

I implemented this pattern in a component that wraps mobiledoc-editor and it seems to work well.

Can't Clear Content of the editor

Hi there -

I think the error here is mostly between the chair and the keyboard, but I can't figure out what (i think should be an easy task: clear all the contents out of the editor.

I noticed that setting up the editor happens in the init method: https://github.com/bustle/ember-mobiledoc-editor/blob/master/addon/components/mobiledoc-editor/component.js#L65

So if I want to clear the editor instance, I'm not really sure how supposed to accomplish that:

Passing a null value into the mobiledoc param of {{mobiledoc-editor}} doesn't seem to work, and I've tried hiding it with something like this:

{{#if showEditor}}
  {{#mobiledoc-editor}}....

But that doesn't seem to unset the data either.

Any thoughts?

Could I submit a PR that moves some of that logic to didRecieveAttrs?

Extensible markup attributes

I use a custom widget for adding links that accepts both a URL and a checkbox for "Open in New Window". Then I do postEditor.builder.createMarkup('a', { href, target }). And this is all working great, except I needed to do this:

import { VALID_ATTRIBUTES } from 'mobiledoc-kit/models/markup';
VALID_ATTRIBUTES.push('target');

Mutating a list in another module seems bad. Should we offer a different way to extend this?

Whitelisting additional attributes seems like a progressive-enhancement-friendly way of extending mobiledoc. Implementations that don't trust the same set of attributes as you do can ignore them.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.