Last active
June 27, 2022 12:45
-
-
Save berhalak/318c7a109f32950b2a00439a83351e4a to your computer and use it in GitHub Desktop.
MarkDown widget with HTML
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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>A cheap and cheerful Markdown viewer/editor</title> | |
<script src="https://docs.getgrist.com/grist-plugin-api.js"></script> | |
<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css"> | |
<script src="https://unpkg.com/easymde/dist/easymde.min.js"></script> | |
<style> | |
html, body, textarea { | |
width: 100%; | |
height: 100vh; | |
padding: 0; | |
margin: 0; | |
} | |
#error { | |
display: none; | |
background: red; | |
color: white; | |
padding: 20px; | |
text-align: center; | |
} | |
.editor-toolbar { | |
border: none; | |
} | |
.EasyMDEContainer .CodeMirror { | |
border: none; | |
border-radius: 0px; | |
border-top: 1px solid #ddd; | |
border-bottom: 1px solid #ddd; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="error"></div> | |
<textarea id="txt"></textarea> | |
<script> | |
var tableId = null; | |
var rowId = null; | |
var colId = null; | |
var htmlId = null; // Stores column id for raw HTML | |
var cachedData = null; | |
window.addEventListener('keypress', (ev) => { | |
// If user pressed Enter or Space | |
if (ev.keyCode === 13 || ev.keyCode === 32) { | |
// and we are in the read mode | |
if (txt.isPreviewActive()) { | |
// switch to edit mode. | |
editMode(); | |
ev.preventDefault(); | |
return; | |
} | |
} | |
}); | |
window.addEventListener('keydown', (ev) => { | |
// If user pressed Ctrl + S | |
if (ev.key === 's' && (ev.ctrlKey || ev.metaKey)) { | |
// and in edit mode | |
if (!txt.isPreviewActive()) { | |
// save and go to read mode | |
save(); | |
readMode(); | |
ev.preventDefault(); | |
return; | |
} | |
} else if (ev.keyCode === 27) { | |
if (!txt.isPreviewActive()) { | |
// If user pressed Escape, cancel edit | |
txt.value("" + cachedData); | |
readMode(); | |
ev.preventDefault(); | |
} | |
} | |
}) | |
function editMode() { | |
document.querySelector(".edit-action").style.display = 'none'; | |
document.querySelector(".save-action").style.display = 'inline-block'; | |
if (txt.isPreviewActive()) { | |
txt.togglePreview(); | |
// We are using internals here to focus inner code editor. | |
if (txt.codemirror && typeof txt.codemirror.focus === 'function') { | |
txt.codemirror.focus(); | |
} | |
} | |
} | |
function readMode() { | |
document.querySelector(".edit-action").style.display = 'inline-block'; | |
document.querySelector(".save-action").style.display = 'none'; | |
if (!txt.isPreviewActive()) { | |
// We are using internals to release the focus. | |
if (txt.codemirror && | |
txt.codemirror.display && | |
txt.codemirror.display.input && | |
typeof txt.codemirror.display.input.blur === 'function') { | |
txt.codemirror.display.input.blur(); | |
} | |
txt.togglePreview(); | |
} | |
} | |
var isMac = /Mac/.test(navigator.platform); | |
var txt = new EasyMDE({ | |
spellChecker: false, | |
toolbar: [{ | |
name: 'save', | |
action: function(editor) { | |
save(); | |
readMode(); | |
}, | |
className: 'fa fa-save save-action', | |
title: `Save (${isMac ? 'Cmd' : 'Ctrl'} + S)` | |
}, | |
{ | |
name: 'edit', | |
action: function(editor) { | |
editMode(); | |
}, | |
className: 'fa fa-pencil edit-action', | |
title: 'Edit (Enter or Space)' | |
}, "|", "bold", "italic", "heading", "quote", "|", "link", "guide"] | |
}); | |
function showError(msg) { | |
var el = document.getElementById('error') | |
if (!msg) { | |
el.style.display = 'none'; | |
} else { | |
el.innerHTML = msg; | |
el.style.display = 'block'; | |
} | |
} | |
function save() { | |
if (!rowId || !tableId) { return; } | |
var data = txt.value() || ''; | |
if (data === cachedData) { return; } | |
console.log("SAVE", data); | |
grist.docApi.applyUserActions([ ['UpdateRecord', tableId, rowId, { | |
[colId]: data, | |
[htmlId] : txt.markdown(data) // Convert markdown to HTML | |
}]]).then(function(e) { | |
showError(null); | |
}).catch(function(e) { | |
showError("Please grant full access for writing."); | |
}); | |
} | |
grist.ready({ | |
columns: [{ name: "Content", type: 'Text'}, 'Html'], | |
requiredAccess: 'full' | |
}); | |
editMode(); | |
grist.on('message', (e) => { | |
if (e.tableId) { tableId = e.tableId; } | |
}); | |
grist.onRecord(function(record, mappings) { | |
save(); | |
var nextRowId = record.id; | |
delete record.id; | |
var keys = Object.keys(record); | |
rowId = null; | |
colId = null; | |
if (!mappings) { | |
// We will fallback to reading a value from a single column to | |
// support old way of mapping (exposing only a single column). | |
// New widgets should only check if mappings object is truthy, | |
// or use grist.mapColumnNames helper method. | |
if (keys.length !== 1) { | |
showError("Please pick a column to store content on Creator Panel"); | |
return; | |
} | |
colId = keys[0]; | |
} else if (mappings) { | |
if (!mappings.Content) { | |
showError("Please pick a column to store content on Creator Panel"); | |
return; | |
} | |
colId = mappings.Content; | |
htmlId = mappings.Html; // Remember HTML column id | |
} | |
showError(null); | |
data = record[colId] || ''; | |
if (nextRowId !== rowId || cachedData !== data) { | |
txt.value("" + data); | |
if (data) { | |
readMode(); | |
} else { | |
editMode(); | |
} | |
} | |
cachedData = data; | |
rowId = nextRowId; | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment