buttonTitle?: string;
maxLength?: number;
/**
- * Whether this form is for a reply to a Private Message
+ * 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;
<div className="mb-3 row">
<div className="col-12">
<div className="rounded bg-light border">
- <div className="d-flex flex-wrap border-bottom">
+ <div
+ className={classNames("d-flex flex-wrap border-bottom", {
+ "no-click": this.isDisabled,
+ })}
+ >
{this.getFormatButton("bold", this.handleInsertBold)}
{this.getFormatButton("italic", this.handleInsertItalic)}
{this.getFormatButton("link", this.handleInsertLink)}
<EmojiPicker
onEmojiClick={e => this.handleEmoji(this, e)}
- disabled={this.isDisabled}
></EmojiPicker>
- <form className="btn btn-sm text-muted font-weight-bold">
+ <form className="btn btn-sm text-muted fw-bold">
<label
htmlFor={`file-upload-${this.id}`}
+ // TODO: Fix this linting violation
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
+ tabIndex={0}
className={`mb-0 ${
UserService.Instance.myUserInfo && "pointer"
}`}
name="file"
className="d-none"
multiple
- disabled={
- !UserService.Instance.myUserInfo || this.isDisabled
- }
+ disabled={!UserService.Instance.myUserInfo}
onChange={linkEvent(this, this.handleImageUpload)}
/>
</form>
{this.getFormatButton("spoiler", this.handleInsertSpoiler)}
<a
href={markdownHelpUrl}
- className="btn btn-sm text-muted font-weight-bold"
+ className="btn btn-sm text-muted fw-bold"
title={I18NextService.i18n.t("formatting_help")}
rel={relTags}
>
)}
<button
type="button"
- disabled={!this.state.content ? true : false}
- className={`btn btn-sm btn-secondary ms-2 ${
- this.state.previewMode && "active"
- }`}
+ disabled={!this.state.content}
+ className={classNames("btn btn-sm btn-secondary ms-2", {
+ active: this.state.previewMode,
+ })}
onClick={linkEvent(this, this.handlePreviewToggle)}
>
{this.state.previewMode
<button
type="submit"
className="btn btn-sm btn-secondary ms-2"
- disabled={this.isDisabled}
+ disabled={this.isDisabled || !this.state.content}
>
{this.state.loading && <Spinner className="me-1" />}
{this.props.buttonTitle}
data-tippy-content={I18NextService.i18n.t(type)}
aria-label={I18NextService.i18n.t(type)}
onClick={linkEvent(this, handleClick)}
- disabled={this.isDisabled}
>
<Icon icon={iconType} classes="icon-inline" />
</button>
const textarea: any = document.getElementById(i.id);
autosize.update(textarea);
pictrsDeleteToast(image.name, res.data.delete_url as string);
+ } else if (res.data.msg === "too_large") {
+ toast(I18NextService.i18n.t("upload_too_large"), "danger");
+ i.setState({ imageUploadStatus: undefined });
+ throw JSON.stringify(res.data);
} else {
throw JSON.stringify(res.data);
}
// Keybind handler
// Keybinds inspired by github comment area
handleKeyBinds(i: MarkdownTextArea, event: KeyboardEvent) {
- if (event.ctrlKey) {
+ if (event.ctrlKey || event.metaKey) {
switch (event.key) {
case "k": {
i.handleInsertLink(i, event);
quoteInsert() {
const textarea: any = document.getElementById(this.id);
const selectedText = window.getSelection()?.toString();
- const { content } = this.state;
+ let { content } = this.state;
if (selectedText) {
const quotedText =
selectedText
.split("\n")
.map(t => `> ${t}`)
.join("\n") + "\n\n";
+
if (!content) {
- this.setState({ content: "" });
+ content = "";
} else {
- this.setState({ content: `${content}\n` });
+ content = `${content}\n\n`;
}
+
this.setState({
content: `${content}${quotedText}`,
});