paragraph icon indicating copy to clipboard operation
paragraph copied to clipboard

Paragraph text alignment (Feature Request)

Open gopumon opened this issue 6 years ago • 13 comments

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

gopumon avatar Oct 26 '19 16:10 gopumon

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

tsalira avatar Mar 03 '20 09:03 tsalira

We will include this feature to the plan of next updates

neSpecc avatar Mar 03 '20 09:03 neSpecc

Thank you! Looking forward to it.

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

tsalira avatar Mar 03 '20 09:03 tsalira

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()

/**

  • Base Paragraph Block for the Editor.js.
  • Represents simple paragraph
  • @author CodeX ([email protected])
  • @copyright CodeX 2018
  • @license The MIT License (MIT) */

/**

  • @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 */

/**

/**

  • 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; }

jgui1129 avatar Jun 24 '20 22:06 jgui1129

Has anyone seen it yet?

kaaaaaaaaaaai avatar Aug 04 '20 00:08 kaaaaaaaaaaai

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

semoal avatar Aug 10 '20 12:08 semoal

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

kaaaaaaaaaaai avatar Aug 13 '20 14:08 kaaaaaaaaaaai

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

https://github.com/codex-team/editor.js/pull/1272 Implemented it on core editor, we're discussing what could be the best solution

semoal avatar Aug 13 '20 14:08 semoal

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

atiabjobayer avatar Aug 21 '20 18:08 atiabjobayer

I wish #35 would be merged.

yassh avatar Sep 13 '20 07:09 yassh

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

muster-mark avatar Mar 04 '21 11:03 muster-mark

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?

VityaSchel avatar Mar 06 '21 21:03 VityaSchel

@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.

helmiItsavirus avatar Apr 02 '21 09:04 helmiItsavirus