@eccenca/gui-elements
Version:
GUI elements based on other libraries, usable in React application, written in Typescript.
278 lines • 11.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const state_1 = require("@codemirror/state");
var Commands;
(function (Commands) {
Commands["header1"] = "Heading 1";
Commands["header2"] = "Heading 2";
Commands["header3"] = "Heading 3";
Commands["header4"] = "Heading 4";
Commands["header5"] = "Heading 5";
Commands["header6"] = "Heading 6";
Commands["codeBlock"] = "Code block";
Commands["quote"] = "Block quote";
Commands["bold"] = "Bold";
Commands["italic"] = "Italic";
Commands["strike"] = "StrikeThrough";
Commands["inlineCode"] = "Inline code";
Commands["unorderedList"] = "Unordered list";
Commands["orderedList"] = "Ordered list";
Commands["todoList"] = "Todo list";
Commands["link"] = "Link";
Commands["image"] = "Image";
})(Commands || (Commands = {}));
//contains all utilities for markdown toolbar
class MarkdownCommand {
constructor(view) {
this.view = null;
/**
* Supported list types are ol, ul, todo.
* utility helps to determine which at the start of the line
*/
this.getListTypeOfLine = (text) => {
if (!text)
return;
text = text === null || text === void 0 ? void 0 : text.trimStart();
if (text.startsWith("- ")) {
if (text.startsWith("- [ ] ") || text.startsWith("- [x] "))
return ["todo"];
return ["ul"];
}
const v = text.match(/^(\d+)\. /);
return v ? ["ol", Number.parseInt(v[1], 10)] : undefined;
};
//factory for different list types.
this.createList = (type) => {
if (!this.view)
return;
const view = this.view;
const doc = view.state.doc;
const orderedList = { currentIndex: 1 };
view.dispatch(view.state.changeByRange((range) => {
const text = doc.slice(range.from, range.to);
const changes = [];
let selectionStart = range.from;
let selectionLength = range.to - range.from;
Array.from({ length: text.lines }).forEach((_, index) => {
const line = doc.line(doc.lineAt(range.from).number + index);
const currentListType = this.getListTypeOfLine(line.text);
if (currentListType && currentListType[0] === type) {
if (currentListType[0] === "ol" && currentListType[1]) {
orderedList.currentIndex = currentListType[1];
}
return;
}
const content = this.createListDelimiter(line.text, type, orderedList);
const diffLength = content.length - line.length;
changes.push({
from: line.from,
to: line.to,
insert: content,
});
if (index === 0) {
selectionStart = selectionStart + diffLength;
}
else {
selectionLength = selectionLength + diffLength;
}
});
return {
changes,
range: state_1.EditorSelection.range(selectionStart, selectionStart + selectionLength),
};
}));
view.focus();
};
this.enforceCursorFocus = (cursorPosition) => {
if (!this.view)
return;
const view = this.view;
setTimeout(() => {
view.dispatch({
selection: state_1.EditorSelection.cursor(cursorPosition),
});
view.focus();
}, 50);
};
//supported headers from h1-h6, h6 being the smallest
this.createHeading = (level) => {
if (!this.view)
return;
const view = this.view;
const state = view.state;
const flags = "#".repeat(level) + " ";
let lastCursorPosition = 0;
view.dispatch(state.changeByRange((range) => {
const line = state.doc.lineAt(range.from);
const content = line.text.replace(/^((#+) )?/, flags);
const diffLength = content.length - line.length;
lastCursorPosition = line.to + diffLength;
return {
changes: {
from: line.from,
to: line.to,
insert: content,
},
range: state_1.EditorSelection.range(range.anchor + diffLength, range.head + diffLength),
};
}));
this.enforceCursorFocus(lastCursorPosition);
};
this.applyFormatting = ({ start, startDelimiter, endDelimiter = startDelimiter, stop = start, }) => {
if (!this.view)
return;
const view = this.view;
const { from, to } = view.state.selection.main;
const text = view.state.sliceDoc(from, to);
view.dispatch(view.state.changeByRange((range) => {
return {
changes: [{ from: range.from, to: range.to, insert: `${startDelimiter}${text}${endDelimiter}` }],
range: state_1.EditorSelection.range(range.from + start, range.to + stop),
};
}));
view.focus();
};
this.applyAttachment = (type) => {
if (!this.view)
return;
const view = this.view;
const { state } = view;
const isImageAttachmentType = type === Commands.image;
const { doc } = state;
view.dispatch(state.changeByRange((range) => {
const { from, to } = range;
const text = doc.sliceString(from, to);
const link = `${isImageAttachmentType ? `!` : ""}[${text}]()`;
const cursor = from + (text.length ? 3 + text.length : 1 + Number(isImageAttachmentType));
return {
changes: [
{
from,
to,
insert: link,
},
],
range: state_1.EditorSelection.range(cursor, cursor),
};
}));
view.focus();
};
this.applyQuoteFormatting = () => {
if (!this.view)
return;
const view = this.view;
const { state } = view;
const { doc } = state;
let lastCursorPosition = 0;
view.dispatch(view.state.changeByRange((range) => {
const startLine = doc.lineAt(range.from);
const text = doc.slice(range.from, range.to);
const lineCount = text.lines;
const changes = [];
let selectionStart = range.from;
let selectionLength = range.to - range.from;
new Array(lineCount).fill(0).forEach((_, index) => {
const line = doc.line(startLine.number + index);
if (line.text.startsWith("> ")) {
return;
}
changes.push({
from: line.from,
insert: "> ",
});
if (index === 0) {
selectionStart = selectionStart + 2;
}
else {
selectionLength += 2;
}
});
lastCursorPosition = selectionStart + selectionLength;
return {
changes,
range: state_1.EditorSelection.range(selectionStart, selectionStart + selectionLength),
};
}));
this.enforceCursorFocus(lastCursorPosition);
};
this.executeCommand = (command) => {
var _a;
switch (command) {
case Commands.bold:
return this.applyFormatting({ start: 2, startDelimiter: "**" });
case Commands.italic:
return this.applyFormatting({ start: 1, startDelimiter: "*" });
case Commands.codeBlock:
return this.applyFormatting({ start: 3, startDelimiter: "```\n", endDelimiter: "\n```" });
case Commands.strike:
return this.applyFormatting({ start: 2, startDelimiter: "~~" });
case Commands.inlineCode:
return this.applyFormatting({ start: 1, startDelimiter: "`" });
case Commands.header1:
case Commands.header2:
case Commands.header3:
case Commands.header4:
case Commands.header5:
case Commands.header6:
return this.createHeading(Number(command.slice(-1)));
case Commands.unorderedList:
case Commands.orderedList:
case Commands.todoList:
return this.createList((_a = MarkdownCommand.commands.lists.find((l) => l.title === command)) === null || _a === void 0 ? void 0 : _a.moniker);
case Commands.image:
case Commands.link:
return this.applyAttachment(command);
case Commands.quote:
return this.applyQuoteFormatting();
default:
return; //do nothing;
}
};
this.view = view;
}
//inserts the list delimiters of "-", "- [ ]" and "{number}."
createListDelimiter(text, type, orderedList) {
return text.replace(/^(( *)(-( \[[x ]])?|\d+\.) )?/, (...args) => {
const { space = "" } = args[args.length - 1];
let newFlag = "- ";
if (type === "ol") {
newFlag = `${orderedList.currentIndex}. `;
orderedList.currentIndex++;
}
else if (type === "todo") {
newFlag = "- [ ] ";
}
return space + newFlag;
});
}
}
//list of supported commands as well as the valid icon names.
MarkdownCommand.commands = {
paragraphs: [
Commands.header1,
Commands.header2,
Commands.header3,
Commands.header4,
Commands.header5,
Commands.header6,
Commands.quote,
Commands.codeBlock,
],
basic: [
{ title: Commands.bold, icon: "operation-format-text-bold" },
{ title: Commands.italic, icon: "operation-format-text-italic" },
{ title: Commands.strike, icon: "operation-format-text-strikethrough" },
{ title: Commands.inlineCode, icon: "operation-format-text-code" },
],
lists: [
{ title: Commands.unorderedList, icon: "operation-format-list-bullet", moniker: "ul" },
{ title: Commands.orderedList, icon: "operation-format-list-numbered", moniker: "ol" },
{ title: Commands.todoList, icon: "operation-format-list-checked", moniker: "todo" },
],
attachments: [
{ title: Commands.link, icon: "operation-link" },
{ title: Commands.image, icon: "item-image" },
],
};
exports.default = MarkdownCommand;
//# sourceMappingURL=markdown.command.js.map