Last active
November 11, 2021 10:14
-
-
Save neongreen/7dbdddae3af0c476340e0dc175552fad to your computer and use it in GitHub Desktop.
Integrating Tiptap with IHP. See https://windofchange.me/ShowCard?cardId=1d4e2e31-f319-4d01-9798-f775e321fdb1 for latest updates.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is used in windofchange.me as of November 2021. | |
// skypack pinned URLs can be obtained by visiting links like 'https://cdn.skypack.dev/@tiptap/core'. | |
// | |
// Note that now I regret pulling Tiptap from Skypack and doing customizations by copying-and-pasting | |
// code into my own app. The right way to do it is probably to fork Tiptap and make your own build | |
// (from the TypeScript source) that you would later embed into the IHP app as a single module. | |
// | |
// Also note that Tiptap does not provide any UI. If you want a ProseMirror-based editor with a built-in UI, | |
// consider Remirror: https://remirror.io/. | |
import { Editor, Extension } from 'https://cdn.skypack.dev/pin/@tiptap/[email protected]/mode=imports,min/optimized/@tiptap/core.js'; | |
import StarterKit from 'https://cdn.skypack.dev/pin/@tiptap/[email protected]/mode=imports,min/optimized/@tiptap/starter-kit.js'; | |
import Typography from 'https://cdn.skypack.dev/pin/@tiptap/[email protected]/mode=imports,min/optimized/@tiptap/extension-typography.js'; | |
// These modules were modified locally, but you can just use the official versions. | |
import HardBreak from './tiptap-hard-break.js'; | |
import CodeBlock from './tiptap-code-block.js'; | |
import { TrailingNode } from './tiptap-trailing-node.js'; | |
import Link from './tiptap-link.js'; | |
import { Code } from './tiptap-code.js' | |
import TurndownService from 'https://cdn.skypack.dev/pin/[email protected]/mode=imports,min/optimized/turndown.js'; | |
import * as commonmark from 'https://cdn.skypack.dev/pin/[email protected]/mode=imports,min/optimized/commonmark.js'; | |
var turndownService = new TurndownService(); | |
var cmarkReader = new commonmark.Parser(); | |
var cmarkWriter = new commonmark.HtmlRenderer({safe: true}); | |
// See https://github.com/digitallyinduced/ihp/issues/1177 | |
$(window).on("error", function(evt) { | |
var e = evt.originalEvent; // get the javascript event | |
if (e.message) { | |
alert("JavaScript error:\n " + e.message + "\n" + | |
"Line:\n " + e.lineno + "\n" + | |
"File:\n " + e.filename + "\n\n" + | |
"Please reload the page!"); | |
} else { | |
alert("JavaScript error:\n " + e.type + "\n" + | |
"Element:\n " + (e.srcElement || e.target) + "\n\n" + | |
"Please reload the page!"); | |
} | |
}); | |
// We want forms with Tiptap to be submittable on Ctrl-Enter | |
const SubmitShortcut = Extension.create({ | |
name: 'SubmitShortcut', | |
addKeyboardShortcuts() { | |
return { | |
'Mod-Enter': () => window.submitForm($(this.editor.options.element).closest('form')[0]) | |
} | |
} | |
}) | |
// Things that will only be called once. | |
// | |
// Note that data-turbolinks-permanent doesn't always work (e.g. it doesn't work on the Headway widget) | |
function onReady() { | |
// ... | |
} | |
// Things that will be called on load, or Turbolinks reloads | |
function onReadyOrTurbo() { | |
const newTiptap = function (content) { | |
return new Editor({ | |
extensions: [ | |
StarterKit, | |
HardBreak, | |
Code, | |
CodeBlock, | |
Typography, | |
Link, | |
TrailingNode, | |
SubmitShortcut, | |
], | |
editorProps: { | |
attributes: { | |
class: 'form-control', | |
}, | |
}, | |
content: content, | |
}); | |
} | |
// Revitalize tiptap editors that were killed by a turbolinks forth/back visit | |
$('textarea.use-tiptap.tiptap-processed').each(function(_, textarea) { | |
const editorElement = $(textarea).next().children()[0]; | |
if (!editorElement.editor) { | |
const editor = newTiptap(editorElement.innerHTML); | |
$(editorElement).replaceWith(editor.options.element); | |
} | |
}); | |
// Create tiptap editors for new textareas | |
$('textarea.use-tiptap:not(.tiptap-processed)').each(function(_, textarea) { | |
const editor = newTiptap(cmarkWriter.render(cmarkReader.parse(textarea.value))); | |
$(textarea).after(editor.options.element); | |
$(textarea).addClass('tiptap-processed'); | |
$(textarea).hide(); | |
if ($(textarea).attr('autofocus')) { | |
editor.commands.focus(); | |
} | |
}); | |
// Make all external links open in a new tab | |
$('a').each(function() { | |
var a = new RegExp('/' + window.location.host + '/'); | |
if(!a.test(this.href)) { | |
$(this).click(function(event) { | |
event.preventDefault(); | |
event.stopPropagation(); | |
window.open(this.href, '_blank'); | |
}); | |
} | |
}); | |
}; | |
$(document).ready(function () { | |
onReady(); | |
onReadyOrTurbo(); | |
}); | |
$(document).on('turbolinks:load', function () { | |
onReadyOrTurbo(); | |
}); | |
// Register a cunning form submit handler that will turn HTML from rich editors into Markdown before submitting the | |
// form. Note that IHP doesn't use the submit method and instead uses its own logic for submitting the form. | |
// | |
// Depends on editors being preceded by plain textareas that we mirror the content to. | |
// | |
// https://github.com/digitallyinduced/ihp/blob/90d16d52eb05f2b8086ab3336035669caafb67de/lib/IHP/static/helpers.js#L163 | |
window.__ihp_submitForm = window.submitForm; | |
window.submitForm = function(form, possibleClickedButton) { | |
$(form).find('.ProseMirror').each(function(_, editorElement) { | |
$(editorElement).parent().prev('textarea')[0].value = turndownService.turndown(editorElement.editor.getHTML()); | |
}); | |
if ($(form)[0].onsubmit) { | |
$(form)[0].onsubmit(); | |
} | |
return window.__ihp_submitForm(form, possibleClickedButton); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Example of a form using tiptap | |
renderForm :: CardUpdate -> Html | |
renderForm cardUpdate = formFor cardUpdate [hsx| | |
{(textareaField #content) { | |
disableLabel = True, | |
fieldClass = "use-tiptap" | |
} | |
} | |
{submitButton {label = "Save"}} | |
|] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment