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: `![${text}](https://chuntaoliu.com/markdown-plus/icon.svg)`, }, }); }} > ); 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);