Add edit and remove actions to link in RTE (#9864)

Add edit and remove actions to link in RTE
This commit is contained in:
Florian Duros 2023-01-11 11:10:55 +01:00 committed by GitHub
parent 79033eb034
commit a691e634b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 209 additions and 58 deletions

View file

@ -262,7 +262,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
this.inputRef = inputRef || React.createRef();
inputProps.placeholder = inputProps.placeholder || inputProps.label;
inputProps.placeholder = inputProps.placeholder ?? inputProps.label;
inputProps.id = this.id; // this overwrites the id from props
inputProps.onFocus = this.onFocus;

View file

@ -120,7 +120,7 @@ export function FormattingButtons({ composer, actionStates }: FormattingButtonsP
<Button
isActive={actionStates.link === "reversed"}
label={_td("Link")}
onClick={() => openLinkModal(composer, composerContext)}
onClick={() => openLinkModal(composer, composerContext, actionStates.link === "reversed")}
icon={<LinkIcon className="mx_FormattingButtons_Icon" />}
/>
</div>

View file

@ -17,17 +17,28 @@ limitations under the License.
import { FormattingFunctions } from "@matrix-org/matrix-wysiwyg";
import React, { ChangeEvent, useState } from "react";
import { _td } from "../../../../../languageHandler";
import { _t } from "../../../../../languageHandler";
import Modal from "../../../../../Modal";
import QuestionDialog from "../../../dialogs/QuestionDialog";
import Field from "../../../elements/Field";
import { ComposerContextState } from "../ComposerContext";
import { isSelectionEmpty, setSelection } from "../utils/selection";
import BaseDialog from "../../../dialogs/BaseDialog";
import DialogButtons from "../../../elements/DialogButtons";
export function openLinkModal(composer: FormattingFunctions, composerContext: ComposerContextState) {
export function openLinkModal(
composer: FormattingFunctions,
composerContext: ComposerContextState,
isEditing: boolean,
) {
const modal = Modal.createDialog(
LinkModal,
{ composerContext, composer, onClose: () => modal.close(), isTextEnabled: isSelectionEmpty() },
{
composerContext,
composer,
onClose: () => modal.close(),
isTextEnabled: isSelectionEmpty(),
isEditing,
},
"mx_CompoundDialog",
false,
true,
@ -43,48 +54,86 @@ interface LinkModalProps {
isTextEnabled: boolean;
onClose: () => void;
composerContext: ComposerContextState;
isEditing: boolean;
}
export function LinkModal({ composer, isTextEnabled, onClose, composerContext }: LinkModalProps) {
const [fields, setFields] = useState({ text: "", link: "" });
const isSaveDisabled = (isTextEnabled && isEmpty(fields.text)) || isEmpty(fields.link);
export function LinkModal({ composer, isTextEnabled, onClose, composerContext, isEditing }: LinkModalProps) {
const [hasLinkChanged, setHasLinkChanged] = useState(false);
const [fields, setFields] = useState({ text: "", link: isEditing ? composer.getLink() : "" });
const hasText = !isEditing && isTextEnabled;
const isSaveDisabled = !hasLinkChanged || (hasText && isEmpty(fields.text)) || isEmpty(fields.link);
return (
<QuestionDialog
<BaseDialog
className="mx_LinkModal"
title={_td("Create a link")}
button={_td("Save")}
buttonDisabled={isSaveDisabled}
hasCancelButton={true}
onFinished={async (isClickOnSave: boolean) => {
if (isClickOnSave) {
title={isEditing ? _t("Edit link") : _t("Create a link")}
hasCancel={true}
onFinished={onClose}
>
<form
className="mx_LinkModal_content"
onSubmit={async (evt) => {
evt.preventDefault();
evt.stopPropagation();
onClose();
// When submitting is done when pressing enter when the link field has the focus,
// The link field is getting back the focus (due to react-focus-lock)
// So we are waiting that the focus stuff is done to play with the composer selection
await new Promise((resolve) => setTimeout(resolve, 0));
await setSelection(composerContext.selection);
composer.link(fields.link, isTextEnabled ? fields.text : undefined);
}
onClose();
}}
description={
<div className="mx_LinkModal_content">
{isTextEnabled && (
<Field
autoFocus={true}
label={_td("Text")}
value={fields.text}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setFields((fields) => ({ ...fields, text: e.target.value }))
}
/>
)}
}}
>
{hasText && (
<Field
autoFocus={!isTextEnabled}
label={_td("Link")}
value={fields.link}
required={true}
autoFocus={true}
label={_t("Text")}
value={fields.text}
className="mx_LinkModal_Field"
placeholder=""
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setFields((fields) => ({ ...fields, link: e.target.value }))
setFields((fields) => ({ ...fields, text: e.target.value }))
}
/>
)}
<Field
required={true}
autoFocus={!hasText}
label={_t("Link")}
value={fields.link}
className="mx_LinkModal_Field"
placeholder=""
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setFields((fields) => ({ ...fields, link: e.target.value }));
setHasLinkChanged(true);
}}
/>
<div className="mx_LinkModal_buttons">
{isEditing && (
<button
type="button"
className="danger"
onClick={() => {
composer.removeLinks();
onClose();
}}
>
{_t("Remove")}
</button>
)}
<DialogButtons
primaryButton={_t("Save")}
primaryDisabled={isSaveDisabled}
primaryIsSubmit={true}
onCancel={onClose}
/>
</div>
}
/>
</form>
</BaseDialog>
);
}