Skip to content

Instantly share code, notes, and snippets.

@berhalak
Last active June 27, 2022 12:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save berhalak/318c7a109f32950b2a00439a83351e4a to your computer and use it in GitHub Desktop.
Save berhalak/318c7a109f32950b2a00439a83351e4a to your computer and use it in GitHub Desktop.
MarkDown widget with HTML
<!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