2019-08-30 20:47:31 +00:00
|
|
|
/* eslint no-console:0 */
|
|
|
|
// This file is automatically compiled by Webpack, along with any other files
|
|
|
|
// present in this directory. You're encouraged to place your actual application logic in
|
|
|
|
// a relevant structure within app/javascript and only use these pack files to reference
|
|
|
|
// that code so it'll be compiled.
|
|
|
|
//
|
|
|
|
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
|
|
|
|
// layout file, like app/views/layouts/application.html.erb
|
|
|
|
|
|
|
|
|
|
|
|
// Uncomment to copy all static images under ../images to the output folder and reference
|
|
|
|
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
|
|
|
|
// or the `imagePath` JavaScript helper below.
|
|
|
|
//
|
|
|
|
// const images = require.context('../images', true)
|
|
|
|
// const imagePath = (name) => images(name, true)
|
|
|
|
|
2019-11-20 03:43:07 +00:00
|
|
|
import tableDragger from "table-dragger"
|
2019-11-15 15:31:40 +00:00
|
|
|
|
2019-10-18 23:05:59 +00:00
|
|
|
import {EditorState} from "prosemirror-state"
|
|
|
|
import {EditorView} from "prosemirror-view"
|
|
|
|
import {Schema, DOMParser} from "prosemirror-model"
|
2019-11-20 03:35:58 +00:00
|
|
|
import {keymap} from "prosemirror-keymap"
|
2019-10-18 23:05:59 +00:00
|
|
|
|
|
|
|
// seleccionar cosas que no son texto
|
|
|
|
import {gapCursor} from "prosemirror-gapcursor"
|
|
|
|
import "prosemirror-gapcursor/style/gapcursor.css"
|
|
|
|
|
|
|
|
// convertir cosas como ">" a una quote: https://prosemirror.net/docs/ref/#inputrules
|
|
|
|
import {inputRules, wrappingInputRule, textblockTypeInputRule,
|
|
|
|
emDash, ellipsis} from "prosemirror-inputrules"
|
|
|
|
|
|
|
|
// markdown (commonmark de markdown-it)
|
|
|
|
import {schema, defaultMarkdownParser,
|
|
|
|
defaultMarkdownSerializer} from "prosemirror-markdown"
|
|
|
|
|
2019-11-20 03:35:58 +00:00
|
|
|
import {toggleMark, setBlockType, wrapIn, baseKeymap} from "prosemirror-commands"
|
|
|
|
import {toggleWrap, toggleBlockType, toggleList,
|
|
|
|
updateMark, removeMark} from "tiptap-commands"
|
|
|
|
|
2019-10-18 23:05:59 +00:00
|
|
|
import { DirectUpload } from "@rails/activestorage"
|
|
|
|
|
2019-11-20 03:35:58 +00:00
|
|
|
const toggleBold = toggleMark(schema.marks.strong)
|
|
|
|
const toggleItalic = toggleMark(schema.marks.em)
|
|
|
|
const wrapInQuote = toggleWrap(schema.nodes.blockquote)
|
|
|
|
|
|
|
|
const toggleUl = toggleList(schema.nodes.bullet_list, schema.nodes.list_item)
|
|
|
|
const toggleOl = toggleList(schema.nodes.ordered_list, schema.nodes.list_item)
|
|
|
|
|
|
|
|
const generateHeadingToggle = level =>
|
|
|
|
toggleBlockType(
|
|
|
|
schema.nodes.heading,
|
|
|
|
schema.nodes.paragraph,
|
|
|
|
{ level: level },
|
|
|
|
)
|
|
|
|
const toggleH1 = generateHeadingToggle(1)
|
|
|
|
const toggleH2 = generateHeadingToggle(2)
|
|
|
|
|
|
|
|
const makeLink = (state, dispatch) => {
|
|
|
|
const {tr, selection, doc} = state
|
|
|
|
const type = schema.marks.link
|
|
|
|
|
|
|
|
const {from, to} = selection
|
|
|
|
const has = doc.rangeHasMark(from, to, type)
|
|
|
|
|
|
|
|
if (has) {
|
|
|
|
tr.removeMark(from, to, type)
|
|
|
|
} else {
|
|
|
|
console.log(tr.addMark(from, to, type.create({
|
|
|
|
href: window.prompt('elegir url', '//sutty.nl')
|
|
|
|
})))
|
|
|
|
}
|
|
|
|
|
|
|
|
return dispatch(tr)
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(schema)
|
|
|
|
|
2019-10-18 23:05:59 +00:00
|
|
|
class SuttyEditor {
|
2019-11-20 03:35:58 +00:00
|
|
|
constructor ({element, markdown, toolbar}) {
|
2019-10-18 23:05:59 +00:00
|
|
|
this.state = this.buildState(markdown)
|
|
|
|
this.view = this.buildView(element, this.state)
|
2019-11-20 03:35:58 +00:00
|
|
|
this.hooks(toolbar)
|
|
|
|
}
|
|
|
|
|
|
|
|
runCommand (command) {
|
|
|
|
return command(this.view.state, this.view.dispatch, this.view)
|
|
|
|
}
|
|
|
|
|
|
|
|
hooks (el) {
|
|
|
|
const setupBtn = (class_, action) => {
|
|
|
|
const btnEl = el.querySelector('.' + class_ + '-btn')
|
|
|
|
btnEl.type = 'button'
|
|
|
|
btnEl.addEventListener('click', e => {
|
|
|
|
action()
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
setupBtn('bold', () => this.runCommand(toggleBold))
|
|
|
|
setupBtn('italic', () => this.runCommand(toggleItalic))
|
|
|
|
setupBtn('quote', () => this.runCommand(wrapInQuote))
|
|
|
|
setupBtn('link', () => this.runCommand(makeLink))
|
|
|
|
setupBtn('ul', () => this.runCommand(toggleUl))
|
|
|
|
setupBtn('ol', () => this.runCommand(toggleOl))
|
|
|
|
setupBtn('h1', () => this.runCommand(toggleH1))
|
|
|
|
setupBtn('h2', () => this.runCommand(toggleH2))
|
2019-10-18 23:05:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
buildState (markdown) {
|
|
|
|
return EditorState.create({
|
|
|
|
schema,
|
|
|
|
...(markdown ? {
|
|
|
|
doc: defaultMarkdownParser.parse(markdown)
|
|
|
|
} : {}),
|
|
|
|
plugins: [
|
|
|
|
gapCursor(),
|
2019-11-20 03:35:58 +00:00
|
|
|
keymap(baseKeymap),
|
2019-10-18 23:05:59 +00:00
|
|
|
inputRules({
|
|
|
|
rules: [
|
|
|
|
//emDash, // -- => —
|
|
|
|
//ellipsis, // ... => …
|
|
|
|
|
|
|
|
],
|
|
|
|
}),
|
|
|
|
],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
buildView (element, state) {
|
|
|
|
return new EditorView(element, {state})
|
|
|
|
}
|
|
|
|
|
|
|
|
get markdown () {
|
|
|
|
return defaultMarkdownSerializer.serialize(this.view.state.doc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
document.addEventListener('turbolinks:load', e => {
|
|
|
|
const editorEls = document.querySelectorAll('.sutty-editor-prosemirror')
|
|
|
|
for (const el of editorEls) {
|
|
|
|
// conseguir textarea con el markdown
|
|
|
|
const textareaEl = document.querySelector(
|
|
|
|
`.sutty-editor-content[data-sutty-identifier="${el.dataset.suttyIdentifier}"]`
|
|
|
|
)
|
|
|
|
|
2019-11-20 03:35:58 +00:00
|
|
|
// conseguir toolbar
|
|
|
|
const toolbarEl = document.querySelector(
|
|
|
|
`.sutty-editor-toolbar[data-sutty-identifier="${el.dataset.suttyIdentifier}"]`
|
|
|
|
)
|
|
|
|
|
2019-10-18 23:05:59 +00:00
|
|
|
// ocultarlo (no se oculta originalmente para usuaries noscript
|
|
|
|
textareaEl.style.display = 'none'
|
|
|
|
|
|
|
|
// crear editor con el markdown del textarea
|
|
|
|
const editor = new SuttyEditor({
|
|
|
|
element: el,
|
|
|
|
markdown: textareaEl.value,
|
2019-11-20 03:35:58 +00:00
|
|
|
toolbar: toolbarEl,
|
2019-10-18 23:05:59 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
// hookear a la form para inyectar el contenido del editor al textarea oculto para que se mande
|
|
|
|
textareaEl.form.addEventListener('submit', e => {
|
|
|
|
console.log('enviando formulario: inyectando markdown en textarea...')
|
|
|
|
textareaEl.value = editor.markdown
|
|
|
|
}, true)
|
|
|
|
}
|
2019-11-20 03:43:07 +00:00
|
|
|
|
|
|
|
const table = document.querySelector('.table-draggable')
|
|
|
|
|
|
|
|
if (table == null) return
|
2019-11-15 15:31:40 +00:00
|
|
|
|
|
|
|
tableDragger(table, {
|
|
|
|
mode: 'row',
|
|
|
|
onlyBody: true,
|
2019-11-20 03:43:07 +00:00
|
|
|
dragHandler: '.handle',
|
2019-11-15 15:31:40 +00:00
|
|
|
}).on('drop', (from, to, el, mode) => {
|
2019-11-20 03:43:07 +00:00
|
|
|
$('.reorder').val((i, v) => i)
|
|
|
|
$('.submit-reorder').removeClass('d-none')
|
|
|
|
})
|
2019-10-18 23:05:59 +00:00
|
|
|
})
|