import { auto } from 'manate/react';
import React, { cloneElement, ReactElement } from 'react';
import { Store } from '../store';
const Toolbar = (props: { store: Store }) => {
const { store } = props;
const { modals } = store;
const stylingClicked = (modifier: string): void => {
const editor = store.editor;
let mainSelection = editor.state.selection.main;
if (mainSelection.empty) {
const word = editor.state.wordAt(mainSelection.head);
if (word) {
editor.dispatch({
selection: { anchor: word.from, head: word.to },
});
}
}
mainSelection = editor.state.selection.main; // don't forget to update this variable
editor.dispatch({
changes: {
from: mainSelection.from,
to: mainSelection.to,
insert: `${modifier}${editor.state.sliceDoc(
mainSelection.from,
mainSelection.to,
)}${modifier}`,
},
});
};
const listClicked = (prefix: string): void => {
const editor = store.editor;
const startLine = editor.state.doc.lineAt(editor.state.selection.main.from);
const endLine = editor.state.doc.lineAt(editor.state.selection.main.to);
for (let i = startLine.number; i <= endLine.number; i++) {
const line = editor.state.doc.line(i);
editor.dispatch({
changes: {
from: line.from,
to: line.from,
insert: prefix,
},
});
}
};
const insertFence = (type: string, sample: string): void => {
const editor = store.editor;
const mainSelection = editor.state.selection.main;
const text =
editor.state.sliceDoc(mainSelection.from, mainSelection.to) || sample;
editor.dispatch({
changes: {
from: mainSelection.from,
to: mainSelection.to,
insert: `\n\`\`\`${type}\n${text}\n\`\`\`\n`,
},
});
};
return (
{store.preferences.toolBarItems.map((item, index) => {
let reactElement: ReactElement;
if (typeof item === 'string') {
switch (item) {
case 'about': {
reactElement = (

modals.about.open()}
title="About"
/>
);
break;
}
case '|': {
reactElement =
|;
break;
}
case 'bold': {
reactElement = (
stylingClicked('**')}
>
);
break;
}
case 'italic': {
reactElement = (
stylingClicked('*')}
>
);
break;
}
case 'strikethrough': {
reactElement = (
stylingClicked('~~')}
>
);
break;
}
case 'underline': {
reactElement = (
stylingClicked('++')}
>
);
break;
}
case 'mark': {
reactElement = (
stylingClicked('==')}
>
);
break;
}
case 'emoji': {
reactElement = (
modals.emoji.open()}
>
);
break;
}
case 'fontawesome': {
reactElement = (
modals.fontAwesome.open()}
>
);
break;
}
case 'quote': {
reactElement = (
listClicked('> ')}
>
);
break;
}
case 'unordered-list': {
reactElement = (
listClicked('- ')}
>
);
break;
}
case 'ordered-list': {
reactElement = (
listClicked('1. ')}
>
);
break;
}
case 'unchecked-list': {
reactElement = (
listClicked('- [ ] ')}
>
);
break;
}
case 'checked-list': {
reactElement = (
listClicked('- [x] ')}
>
);
break;
}
case 'link': {
reactElement = (
{
const editor = store.editor;
const mainSelection = editor.state.selection.main;
const text =
editor.state.sliceDoc(
mainSelection.from,
mainSelection.to,
) || 'link';
editor.dispatch({
changes: {
from: mainSelection.from,
to: mainSelection.to,
insert: `[${text}](https://github.com/tylerlong/markdown-plus/)`,
},
});
}}
>
);
break;
}
case 'image': {
reactElement = (
{
const editor = store.editor;
const mainSelection = editor.state.selection.main;
const text =
editor.state.sliceDoc(
mainSelection.from,
mainSelection.to,
) || 'image';
editor.dispatch({
changes: {
from: mainSelection.from,
to: mainSelection.to,
insert: ``,
},
});
}}
>
);
break;
}
case 'code': {
reactElement = (
{
const editor = store.editor;
const mainSelection = editor.state.selection.main;
const text =
editor.state.sliceDoc(
mainSelection.from,
mainSelection.to,
) || "console.log('Hello, world!');";
editor.dispatch({
changes: {
from: mainSelection.from,
to: mainSelection.to,
insert: `\n\`\`\`\n${text}\n\`\`\`\n`,
},
});
}}
>
);
break;
}
case 'table': {
reactElement = (
{
const sample = `
header 1 | header 2
---|---
row 1 col 1 | row 1 col 2
row 2 col 1 | row 2 col 2`.trim();
const editor = store.editor;
const cursorPos = editor.state.selection.main.head;
const currentLine = editor.state.doc.lineAt(cursorPos);
const isAtLineStart = cursorPos === currentLine.from;
if (isAtLineStart) {
editor.dispatch({
changes: {
from: currentLine.from,
to: currentLine.from,
insert: `\n${sample}\n\n`,
},
});
} else {
editor.dispatch({
changes: {
from: currentLine.to,
to: currentLine.to,
insert: `\n\n${sample}\n`,
},
});
}
}}
>
);
break;
}
case 'math': {
reactElement = (
{
const editor = store.editor;
const mainSelection = editor.state.selection.main;
const text =
editor.state.sliceDoc(
mainSelection.from,
mainSelection.to,
) || 'E = mc^2';
editor.dispatch({
changes: {
from: mainSelection.from,
to: mainSelection.to,
insert: `\n\`\`\`math\n${text}\n\`\`\`\n`,
},
});
}}
>
);
break;
}
case 'mermaid': {
reactElement = (
insertFence('mermaid', 'graph LR\nA-->B')}
>
);
break;
}
case 'chartjs': {
reactElement = (
insertFence(
'chartjs',
`{
"type": "bar",
"data": {
"labels": [
"last year",
"this year"
],
"datasets": [
{
"label": "# of Rainy Days",
"data": [
60,
30
]
}
]
}
}`,
)
}
>
);
break;
}
case 'preferences': {
reactElement = (
modals.preferences.open()}
>
);
break;
}
case 'print': {
reactElement = (
window.print()}
>
);
break;
}
default: {
throw new Error(`Unknown toolbar item: ${item}`);
}
}
} else {
reactElement = item as ReactElement;
}
return cloneElement(reactElement, { key: `item-${index}` });
})}
);
};
export default auto(Toolbar);