import { isBrowser } from "@utils/browser"; import { numToSI, randomStr } from "@utils/helpers"; import autosize from "autosize"; import classNames from "classnames"; import { NoOptionI18nKeys } from "i18next"; import { Component, linkEvent } from "inferno"; import { Language } from "lemmy-js-client"; import { concurrentImageUpload, markdownFieldCharacterLimit, markdownHelpUrl, maxUploadImages, relTags, } from "../../config"; import { customEmojisLookup, mdToHtml, setupTribute } from "../../markdown"; import { HttpService, I18NextService, UserService } from "../../services"; import { setupTippy } from "../../tippy"; import { pictrsDeleteToast, toast } from "../../toast"; import { EmojiPicker } from "./emoji-picker"; import { Icon, Spinner } from "./icon"; import { LanguageSelect } from "./language-select"; import NavigationPrompt from "./navigation-prompt"; import ProgressBar from "./progress-bar"; interface MarkdownTextAreaProps { /** * Initial content inside the textarea */ initialContent?: string; /** * Numerical ID of the language to select in dropdown */ initialLanguageId?: number; placeholder?: string; buttonTitle?: string; maxLength?: number; /** * Whether this form is for a reply to a Private Message. * If true, a "Cancel" button is shown that will close the reply. */ replyType?: boolean; focus?: boolean; disabled?: boolean; finished?: boolean; /** * Whether to show the language selector */ showLanguage?: boolean; hideNavigationWarnings?: boolean; onContentChange?(val: string): void; onReplyCancel?(): void; onSubmit?(content: string, formId: string, languageId?: number): void; allLanguages: Language[]; // TODO should probably be nullable siteLanguages: number[]; // TODO same } interface ImageUploadStatus { total: number; uploaded: number; } interface MarkdownTextAreaState { content?: string; languageId?: number; previewMode: boolean; imageUploadStatus?: ImageUploadStatus; loading: boolean; submitted: boolean; } export class MarkdownTextArea extends Component< MarkdownTextAreaProps, MarkdownTextAreaState > { private id = `markdown-textarea-${randomStr()}`; private formId = `markdown-form-${randomStr()}`; private tribute: any; state: MarkdownTextAreaState = { content: this.props.initialContent, languageId: this.props.initialLanguageId, previewMode: false, loading: false, submitted: false, }; constructor(props: any, context: any) { super(props, context); this.handleLanguageChange = this.handleLanguageChange.bind(this); if (isBrowser()) { this.tribute = setupTribute(); } } componentDidMount() { const textarea: any = document.getElementById(this.id); if (textarea) { autosize(textarea); this.tribute.attach(textarea); textarea.addEventListener("tribute-replaced", () => { this.setState({ content: textarea.value }); autosize.update(textarea); }); this.quoteInsert(); if (this.props.focus) { textarea.focus(); } // TODO this is slow for some reason setupTippy(); } } componentWillReceiveProps(nextProps: MarkdownTextAreaProps) { if (nextProps.finished) { this.setState({ previewMode: false, imageUploadStatus: undefined, loading: false, content: undefined, }); if (this.props.replyType) { this.props.onReplyCancel?.(); } const textarea: any = document.getElementById(this.id); const form: any = document.getElementById(this.formId); form.reset(); setTimeout(() => autosize.update(textarea), 10); } } render() { const languageId = this.state.languageId; // TODO add these prompts back in at some point // return (
{this.getFormatButton("bold", this.handleInsertBold)} {this.getFormatButton("italic", this.handleInsertItalic)} {this.getFormatButton("link", this.handleInsertLink)} this.handleEmoji(this, e)} > {this.getFormatButton("header", this.handleInsertHeader)} {this.getFormatButton( "strikethrough", this.handleInsertStrikethrough )} {this.getFormatButton("quote", this.handleInsertQuote)} {this.getFormatButton("list", this.handleInsertList)} {this.getFormatButton("code", this.handleInsertCode)} {this.getFormatButton("subscript", this.handleInsertSubscript)} {this.getFormatButton( "superscript", this.handleInsertSuperscript )} {this.getFormatButton("spoiler", this.handleInsertSpoiler)}