Add insert link button to the format bar (#5879)

This commit is contained in:
Šimon Brandner 2021-10-25 11:56:55 +02:00 committed by GitHub
parent 75c7daa2c9
commit ceb4c7e368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 24 additions and 3 deletions

View file

@ -16,7 +16,7 @@ limitations under the License.
.mx_MessageComposerFormatBar { .mx_MessageComposerFormatBar {
display: none; display: none;
width: calc(32px * 5); width: calc(32px * 6);
height: 32px; height: 32px;
position: absolute; position: absolute;
cursor: pointer; cursor: pointer;
@ -87,6 +87,11 @@ limitations under the License.
.mx_MessageComposerFormatBar_buttonIconCode::after { .mx_MessageComposerFormatBar_buttonIconCode::after {
mask-image: url('$(res)/img/element-icons/room/format-bar/code.svg'); mask-image: url('$(res)/img/element-icons/room/format-bar/code.svg');
} }
.mx_MessageComposerFormatBar_buttonIconInsertLink::after {
mask-image: url('$(res)/img/element-icons/link.svg');
mask-size: 18px;
}
} }
.mx_MessageComposerFormatBar_buttonTooltip { .mx_MessageComposerFormatBar_buttonTooltip {

View file

@ -28,6 +28,7 @@ import {
formatRangeAsCode, formatRangeAsCode,
toggleInlineFormat, toggleInlineFormat,
replaceRangeAndMoveCaret, replaceRangeAndMoveCaret,
formatRangeAsLink,
} from '../../../editor/operations'; } from '../../../editor/operations';
import { getCaretOffsetAndText, getRangeForSelection } from '../../../editor/dom'; import { getCaretOffsetAndText, getRangeForSelection } from '../../../editor/dom';
import Autocomplete, { generateCompletionDomId } from '../rooms/Autocomplete'; import Autocomplete, { generateCompletionDomId } from '../rooms/Autocomplete';
@ -706,6 +707,9 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
case Formatting.Quote: case Formatting.Quote:
formatRangeAsQuote(range); formatRangeAsQuote(range);
break; break;
case Formatting.InsertLink:
formatRangeAsLink(range);
break;
} }
}; };

View file

@ -27,6 +27,7 @@ export enum Formatting {
Strikethrough = "strikethrough", Strikethrough = "strikethrough",
Code = "code", Code = "code",
Quote = "quote", Quote = "quote",
InsertLink = "insert_link",
} }
interface IProps { interface IProps {
@ -57,6 +58,7 @@ export default class MessageComposerFormatBar extends React.PureComponent<IProps
<FormatButton label={_t("Strikethrough")} onClick={() => this.props.onAction(Formatting.Strikethrough)} icon="Strikethrough" visible={this.state.visible} /> <FormatButton label={_t("Strikethrough")} onClick={() => this.props.onAction(Formatting.Strikethrough)} icon="Strikethrough" visible={this.state.visible} />
<FormatButton label={_t("Code block")} onClick={() => this.props.onAction(Formatting.Code)} icon="Code" visible={this.state.visible} /> <FormatButton label={_t("Code block")} onClick={() => this.props.onAction(Formatting.Code)} icon="Code" visible={this.state.visible} />
<FormatButton label={_t("Quote")} onClick={() => this.props.onAction(Formatting.Quote)} icon="Quote" shortcut={this.props.shortcuts.quote} visible={this.state.visible} /> <FormatButton label={_t("Quote")} onClick={() => this.props.onAction(Formatting.Quote)} icon="Quote" shortcut={this.props.shortcuts.quote} visible={this.state.visible} />
<FormatButton label={_t("Insert link")} onClick={() => this.props.onAction(Formatting.InsertLink)} icon="InsertLink" visible={this.state.visible} />
</div>); </div>);
} }

View file

@ -32,13 +32,13 @@ export function replaceRangeAndExpandSelection(range: Range, newParts: Part[]):
}); });
} }
export function replaceRangeAndMoveCaret(range: Range, newParts: Part[]): void { export function replaceRangeAndMoveCaret(range: Range, newParts: Part[], offset = 0): void {
const { model } = range; const { model } = range;
model.transform(() => { model.transform(() => {
const oldLen = range.length; const oldLen = range.length;
const addedLen = range.replace(newParts); const addedLen = range.replace(newParts);
const firstOffset = range.start.asOffset(model); const firstOffset = range.start.asOffset(model);
const lastOffset = firstOffset.add(oldLen + addedLen); const lastOffset = firstOffset.add(oldLen + addedLen + offset);
return lastOffset.asPosition(model); return lastOffset.asPosition(model);
}); });
} }
@ -103,6 +103,15 @@ export function formatRangeAsCode(range: Range): void {
replaceRangeAndExpandSelection(range, parts); replaceRangeAndExpandSelection(range, parts);
} }
export function formatRangeAsLink(range: Range) {
const { model, parts } = range;
const { partCreator } = model;
parts.unshift(partCreator.plain("["));
parts.push(partCreator.plain("]()"));
// We set offset to -1 here so that the caret lands between the brackets
replaceRangeAndMoveCaret(range, parts, -1);
}
// parts helper methods // parts helper methods
const isBlank = part => !part.text || !/\S/.test(part.text); const isBlank = part => !part.text || !/\S/.test(part.text);
const isNL = part => part.type === Type.Newline; const isNL = part => part.type === Type.Newline;

View file

@ -1606,6 +1606,7 @@
"Strikethrough": "Strikethrough", "Strikethrough": "Strikethrough",
"Code block": "Code block", "Code block": "Code block",
"Quote": "Quote", "Quote": "Quote",
"Insert link": "Insert link",
"Only the two of you are in this conversation, unless either of you invites anyone to join.": "Only the two of you are in this conversation, unless either of you invites anyone to join.", "Only the two of you are in this conversation, unless either of you invites anyone to join.": "Only the two of you are in this conversation, unless either of you invites anyone to join.",
"This is the beginning of your direct message history with <displayName/>.": "This is the beginning of your direct message history with <displayName/>.", "This is the beginning of your direct message history with <displayName/>.": "This is the beginning of your direct message history with <displayName/>.",
"Topic: %(topic)s (<a>edit</a>)": "Topic: %(topic)s (<a>edit</a>)", "Topic: %(topic)s (<a>edit</a>)": "Topic: %(topic)s (<a>edit</a>)",