/* 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) import tableDragger from "table-dragger" import {EditorState} from "prosemirror-state" import {EditorView} from "prosemirror-view" import {Schema, DOMParser} from "prosemirror-model" import {keymap} from "prosemirror-keymap" // 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" import {toggleMark, setBlockType, wrapIn, baseKeymap} from "prosemirror-commands" import {toggleWrap, toggleBlockType, toggleList, updateMark, removeMark} from "tiptap-commands" import {startImageUpload, placeholderPlugin} from "./imageUpload" 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 { tr.addMark(from, to, type.create({ href: window.prompt('elegir url', '//sutty.nl') })) } return dispatch(tr) } class SuttyEditor { constructor ({element, markdown, toolbar}) { this.state = this.buildState(markdown) this.view = this.buildView(element, this.state) 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)) const imgInput = el.querySelector('.img-input') imgInput.addEventListener('change', event => { if ( this.view.state.selection.$from.parent.inlineContent && event.target.files.length ) { const file = event.target.files[0] event.target.value = '' startImageUpload(this.view, file, schema) } this.view.focus() }) } buildState (markdown) { return EditorState.create({ schema, ...(markdown ? { doc: defaultMarkdownParser.parse(markdown) } : {}), plugins: [ gapCursor(), keymap(baseKeymap), placeholderPlugin, 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}"]` ) // conseguir toolbar const toolbarEl = document.querySelector( `.sutty-editor-toolbar[data-sutty-identifier="${el.dataset.suttyIdentifier}"]` ) // 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, toolbar: toolbarEl, }) // 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) } const table = document.querySelector('.table-draggable') if (table == null) return tableDragger(table, { mode: 'row', onlyBody: true, dragHandler: '.handle', }).on('drop', (from, to, el, mode) => { $('.reorder').val((i, v) => i) $('.submit-reorder').removeClass('d-none') }) })