/** * Inspired by https://github.com/kunall17/MarkdownEditor */ import React from 'react'; import { View, StyleSheet, TextInput, Platform, KeyboardAvoidingView, TouchableOpacity, Image, } from 'react-native'; import { renderFormatButtons } from './renderButtons'; import NoteBodyViewer from 'lib/components/NoteBodyViewer/NoteBodyViewer'; const styles = StyleSheet.create({ buttonContainer: { flex: 0, flexDirection: 'row', }, screen: { // Wrapper around the editor and the preview flex: 1, flexDirection: 'column', alignItems: 'stretch', }, }); const MarkdownPreviewButton = (props) => ; export default class MarkdownEditor extends React.Component { constructor(props) { super(props); this.state = { text: props.value, selection: { start: 0, end: 0 }, // Show preview by default showPreview: props.showPreview ? props.showPreview : true, }; this.textAreaRef = React.createRef(); // For focusing the textarea } textInput: TextInput; changeText = (selection: {start: number, end: number}) => (input: string) => { let result = input; const cursor = selection.start; const isOnNewline = '\n' === input.slice(cursor - 1, cursor); const isDeletion = input.length < this.state.text.length; if (isOnNewline && !isDeletion) { const prevLines = input.slice(0, cursor - 1).split('\n'); const prevLine = prevLines[prevLines.length - 1]; const insertListLine = (bullet) => ([ prevLines.join('\n'), // Previous text `\n${bullet} `, // Current line with new bullet point input.slice(cursor, input.length), // Following text ].join('')); const insertedEndListLine = [ // Previous text (all but last bullet line, which we remove) prevLines.slice(0, prevLines.length - 1).join('\n') , '\n\n', // Two newlines to get out of the list input.slice(cursor, input.length), // Following text ].join(''); // Add new ordered list line item if (prevLine.startsWith('- ') && !prevLine.startsWith('- [ ')) { // If the bullet on the previous line isn't empty, add a new bullet. if (prevLine.trim() !== '-') { result = insertListLine('-'); } else { result = insertedEndListLine; } } // Add new checklist line item if ((prevLine.startsWith('- [ ] ') || prevLine.startsWith('- [x] '))) { // If the bullet on the previous line isn't empty, add a new bullet. if (prevLine.trim() !== '- [ ]' && prevLine.trim() !== '- [x]') { result = insertListLine('- [ ]'); } else { result = insertedEndListLine; } } // Add new ordered list item if (/^\d+\./.test(prevLine)) { // If the bullet on the previous line isn't empty, add a new bullet. const digit = Number(prevLine.match(/^\d+/)[0]); if (prevLine.trim() !== `${digit}.`) { result = insertListLine(`${digit + 1}.`); } else { result = insertedEndListLine; } } } // Hide Markdown preview on text change this.setState({ text: result, showPreview: false }); this.props.saveText(result); if (this.props.onMarkdownChange) this.props.onMarkdownChange(input); }; onSelectionChange = event => { this.setState({ selection: event.nativeEvent.selection }); }; focus = () => this.textAreaRef.current.focus() convertMarkdown = () => this.setState({ showPreview: !this.state.showPreview }) render() { const WrapperView = Platform.OS === 'ios' ? KeyboardAvoidingView : View; const { Formats, markdownButton } = this.props; const { text, selection, showPreview } = this.state; return ( {showPreview && } {renderFormatButtons( { color: this.props.markdownButtonsColor, getState: () => this.state, setState: (state, callback) => { // Hide Markdown preview on text change this.setState({ showPreview: false }); this.setState(state, callback); }, }, Formats, markdownButton )} ); } }