Add insert link button to the format bar (#5879)
This commit is contained in:
parent
75c7daa2c9
commit
ceb4c7e368
5 changed files with 24 additions and 3 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>)",
|
||||||
|
|
Loading…
Reference in a new issue