editor-js / paragraph

Paragraph Tool for Editor.js 2.0

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Paragraph text alignment (Feature Request)

gopumon opened this issue · comments

It would be nice to have an option to choose text alignment (left, center or right) for the paragraph.

Very much so! I have struggled one full week trying to figure this out. Any ideas??

We will include this feature to the plan of next updates

Thank you! Looking forward to it.

Is there any hack I can use for now in the meantime?

Hi, you can try the hack i did. Take a look.
It will add classes (text-right, text-left or text-center)

Screen Shot 2020-06-25 at 12 12 50 AM

`/**

  • Build styles
    */
    require('./index.css').toString()

/**

/**

  • @typedef {object} ParagraphConfig
  • @Property {string} placeholder - placeholder for the empty paragraph
  • @Property {boolean} preserveBlank - Whether or not to keep blank paragraphs when saving editor data
    */

/**

  • @typedef {Object} ParagraphData
  • @description Tool's input and output data format
  • @Property {String} text — Paragraph's content. Can include HTML tags:
    /
    class Paragraph {
    /
    *
    • Default placeholder for Paragraph Tool
    • @return {string}
    • @constructor
      */
      static get DEFAULT_PLACEHOLDER() {
      return ''
      }

/**

  • Render plugin`s main Element and fill it with saved data
  • @param {object} params - constructor params
  • @param {ParagraphData} params.data - previously saved data
  • @param {ParagraphConfig} params.config - user config for Tool
  • @param {object} params.api - editor.js api
    */
    constructor({ data, config, api }) {
    this.api = api
this._CSS = {
  block: this.api.styles.block,
  wrapper: 'ce-paragraph',
}
this.CSS = {
  baseClass: this.api.styles.block,
  loading: this.api.styles.loader,
  input: this.api.styles.input,
  settingsButton: this.api.styles.settingsButton,
  settingsButtonActive: this.api.styles.settingsButtonActive,

  /**
   * Tool's classes
   */
  imageHolder: 'utd-image__picture',
  caption: 'utd-image__caption',
  link: 'utd-image__link',
}

this.onKeyUp = this.onKeyUp.bind(this)

/**
 * Placeholder for paragraph if it is first Block
 * @type {string}
 */
this._placeholder = config.placeholder ? config.placeholder : Paragraph.DEFAULT_PLACEHOLDER
this._data = {}
this._element = this.drawView(data)
this._preserveBlank = config.preserveBlank !== undefined ? config.preserveBlank : false

this.data = data

this.settings = [
  {
    name: 'text-left',
    icon: `<svg class="svg-icon" viewBox="0 0 20 20">
    <path fill="none" d="M1.683,3.39h16.676C18.713,3.39,19,3.103,19,2.749s-0.287-0.642-0.642-0.642H1.683
    c-0.354,0-0.641,0.287-0.641,0.642S1.328,3.39,1.683,3.39z M1.683,7.879h11.545c0.354,0,0.642-0.287,0.642-0.641
    s-0.287-0.642-0.642-0.642H1.683c-0.354,0-0.641,0.287-0.641,0.642S1.328,7.879,1.683,7.879z M18.358,11.087H1.683
    c-0.354,0-0.641,0.286-0.641,0.641s0.287,0.642,0.641,0.642h16.676c0.354,0,0.642-0.287,0.642-0.642S18.713,11.087,18.358,11.087z
     M11.304,15.576H1.683c-0.354,0-0.641,0.287-0.641,0.642s0.287,0.641,0.641,0.641h9.621c0.354,0,0.642-0.286,0.642-0.641
    S11.657,15.576,11.304,15.576z"></path>
      </svg>`,
  },
  {
    name: 'text-center',
    icon: `<svg class="svg-icon" viewBox="0 0 20 20">
    <path fill="none" d="M1.686,3.327h16.754c0.356,0,0.645-0.288,0.645-0.644c0-0.356-0.288-0.645-0.645-0.645H1.686
        c-0.356,0-0.644,0.288-0.644,0.645C1.042,3.039,1.33,3.327,1.686,3.327z M4.263,6.549c-0.356,0-0.644,0.288-0.644,0.645
        c0,0.356,0.288,0.644,0.644,0.644h11.599c0.356,0,0.645-0.288,0.645-0.644c0-0.356-0.288-0.645-0.645-0.645H4.263z M18.439,11.06
        H1.686c-0.356,0-0.644,0.288-0.644,0.644c0,0.356,0.288,0.645,0.644,0.645h16.754c0.356,0,0.645-0.288,0.645-0.645
        C19.084,11.348,18.796,11.06,18.439,11.06z M15.218,15.57H5.552c-0.356,0-0.645,0.288-0.645,0.645c0,0.355,0.289,0.644,0.645,0.644
        h9.666c0.355,0,0.645-0.288,0.645-0.644C15.862,15.858,15.573,15.57,15.218,15.57z"></path>
</svg>`,
  },
  {
    name: 'text-right',
    icon: `<svg class="svg-icon" viewBox="0 0 20 20">
    <path fill="none" d="M1.321,3.417h17.024C18.707,3.417,19,3.124,19,2.762c0-0.362-0.293-0.655-0.654-0.655H1.321
        c-0.362,0-0.655,0.293-0.655,0.655C0.667,3.124,0.959,3.417,1.321,3.417z M18.346,15.857H8.523c-0.361,0-0.655,0.293-0.655,0.654
        c0,0.362,0.293,0.655,0.655,0.655h9.822c0.361,0,0.654-0.293,0.654-0.655C19,16.15,18.707,15.857,18.346,15.857z M18.346,11.274
        H1.321c-0.362,0-0.655,0.292-0.655,0.654s0.292,0.654,0.655,0.654h17.024c0.361,0,0.654-0.292,0.654-0.654
        S18.707,11.274,18.346,11.274z M18.346,6.69H6.56c-0.362,0-0.655,0.293-0.655,0.655C5.904,7.708,6.198,8,6.56,8h11.786
        C18.707,8,19,7.708,19,7.345C19,6.983,18.707,6.69,18.346,6.69z"></path>
</svg>`,
  },
]

}

renderSettings() {
let wrapper = document.createElement('div')

this.settings.forEach(tune => {
  let el = document.createElement('div')

  el.classList.add(this.CSS.settingsButton)
  el.innerHTML = tune.icon

  el.addEventListener('click', () => {
    this._toggleTune(tune.name)
  })

  el.classList.toggle(!this.CSS.settingsButtonActive, this.data[tune.name])
  wrapper.appendChild(el)
})
return wrapper

}

/**

  • Check if text content is empty and set empty string to inner html.
  • We need this because some browsers (e.g. Safari) insert
    into empty contenteditanle elements
  • @param {KeyboardEvent} e - key up event
    */
    onKeyUp(e) {
    if (e.code !== 'Backspace' && e.code !== 'Delete') {
    return
    }
const { textContent } = this._element

if (textContent === '') {
  this._element.innerHTML = ''
}

}

/**

  • Create Tool's view
  • @return {HTMLElement}
  • @Private
    */
    drawView(data) {
    let c = data.class ? data.class : ''
    let div = document.createElement('DIV')
    div.classList.add(this._CSS.wrapper, this._CSS.block)
    if (c) {
    div.classList.add(c)
    }
div.contentEditable = true
div.dataset.placeholder = this.api.i18n.t(this._placeholder)

div.addEventListener('keyup', this.onKeyUp)

return div

}

/**

  • Return Tool's view
  • @returns {HTMLDivElement}
  • @public
    */
    render() {
    return this._element
    }

/**

  • Method that specified how to merge two Text blocks.
  • Called by Editor.js by backspace at the beginning of the Block
  • @param {ParagraphData} data
  • @public
    */
    merge(data) {
    let newData = {
    text: this.data.text + data.text,
    class: this.data.class,
    }
this.data = newData

}

/**

  • Validate Paragraph block data:
    • check for emptiness
  • @param {ParagraphData} savedData — data received after saving
  • @returns {boolean} false if saved data is not correct, otherwise true
  • @public
    */
    validate(savedData) {
    if (savedData.text.trim() === '' && !this._preserveBlank) {
    return false
    }
return true

}

/**

  • Extract Tool's data from the view
  • @param {HTMLDivElement} toolsContent - Paragraph tools rendered view
  • @returns {ParagraphData} - saved data
  • @public
    */
    save(toolsContent) {
    return {
    text: toolsContent.innerHTML,
    class: toolsContent.classList[2],
    }
    }

/**

  • On paste callback fired from Editor.
  • @param {PasteEvent} event - event with pasted data
    */
    onPaste(event) {
    const data = {
    text: event.detail.data.innerHTML,
    }
this.data = data

}

/**

  • Enable Conversion Toolbar. Paragraph can be converted to/from other tools
    */
    static get conversionConfig() {
    return {
    export: 'text', // to convert Paragraph to other block, use 'text' property of saved data
    import: 'text', // to covert other block's exported string to Paragraph, fill 'text' property of tool data
    }
    }

/**

  • Sanitizer rules
    */
    static get sanitize() {
    return {
    text: {
    br: true,
    },
    }
    }

/**

  • Get current Tools`s data
  • @returns {ParagraphData} Current data
  • @Private
    */
    get data() {
    let text = this._element.innerHTML
    let c = this._element.classList
this._data.text = text
this._data.class = c[2]

return this._data

}

/**

  • Store data in plugin:
    • at the this._data property
    • at the HTML
  • @param {ParagraphData} data — data to set
  • @Private
    */
    set data(data) {
    this._data = data || {}
this._element.innerHTML = this._data.text || ''
// this._element.classList = this._data.class

}

/**

  • Used by Editor paste handling API.
  • Provides configuration to handle P tags.
  • @returns {{tags: string[]}}
    */
    static get pasteConfig() {
    return {
    tags: ['P'],
    }
    }

/**

  • Click on the Settings Button
  • @Private
    /
    _toggleTune(tune) {
    this.data[tune] = !this.data[tune]
    this._element.classList = ${this._CSS.wrapper} ${this._CSS.block} ${tune}
    }
    /
    *
  • Icon and title for displaying at the Toolbox
  • @return {{icon: string, title: string}}
    */
    static get toolbox() {
    return {
    icon: require('./toolbox-icon.svg').default,
    title: 'Text',
    }
    }
    }

export default Paragraph

`

Screen Shot 2020-06-25 at 12 11 25 AM

You also need to add this simple css to work with the svg
.svg-icon {
width: 1em;
height: 1em;
}

.svg-icon path,
.svg-icon polygon,
.svg-icon rect {
fill: #4691f6;
}

.svg-icon circle {
stroke: #4691f6;
stroke-width: 1;
}

Has anyone seen it yet?

Could be a better solution a plugin called @editor-js/alignment, that checks if the block that has been created can be aligned, if so, just align it?

It could work for: images, headings, paragraphs, tables...

Probably i'll work on it

i made it once
https://github.com/kaaaaaaaaaaai/paragraph-with-alignment

codex-team/editor.js#1272
Implemented it on core editor, we're discussing what could be the best solution

@semoal how can I use it? as you said it's been implemented already. Kinda noob :')

commented

I wish #35 would be merged.

@neSpecc Is this feature still included in the next updates?

is editorjs still maintened? I see pull request in table repository with feature to delete cells from December and there is a pull request from September that adds alignment. If these are not stable, why don't merge it on not-stable branch then?

@kaaaaaaaaaaai please add some function such as line height and font size.

I spent 2 days making it. LOL

don't forget to add line-height and font-size dropdown in settings.