Files
element-web/src/components/views/rooms/wysiwyg_composer/utils/selection.ts
Florian Duros 2b66cfc25f Open message in editing mode when keyboard up is pressed (RTE) (#10079)
Move to previous message when arrow up is pressed in the main composer (RTE)
2023-02-03 17:43:02 +01:00

95 lines
3.4 KiB
TypeScript

/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { SubSelection } from "../types";
export function setSelection(selection: SubSelection): Promise<void> {
if (selection.anchorNode && selection.focusNode) {
const range = new Range();
if (selection.isForward) {
range.setStart(selection.anchorNode, selection.anchorOffset);
range.setEnd(selection.focusNode, selection.focusOffset);
} else {
range.setStart(selection.focusNode, selection.focusOffset);
range.setEnd(selection.anchorNode, selection.anchorOffset);
}
document.getSelection()?.removeAllRanges();
document.getSelection()?.addRange(range);
}
// Waiting for the next loop to ensure that the selection is effective
return new Promise((resolve) => setTimeout(resolve, 0));
}
export function isSelectionEmpty(): boolean {
const selection = document.getSelection();
return Boolean(selection?.isCollapsed);
}
export function isCaretAtStart(editor: HTMLElement): boolean {
const selection = document.getSelection();
// No selection or the caret is not at the beginning of the selected element
if (!selection) {
return false;
}
// When we are pressing keyboard up in an empty main composer, the selection is on the editor with an anchorOffset at O or 1 (yes, this is strange)
const isOnFirstElement = selection.anchorNode === editor && selection.anchorOffset <= 1;
if (isOnFirstElement) {
return true;
}
// In case of nested html elements (list, code blocks), we are going through all the first child
let child = editor.firstChild;
do {
if (child === selection.anchorNode) {
return selection.anchorOffset === 0;
}
} while ((child = child?.firstChild || null));
return false;
}
export function isCaretAtEnd(editor: HTMLElement): boolean {
const selection = document.getSelection();
if (!selection) {
return false;
}
// When we are cycling across all the timeline message with the keyboard
// The caret is on the last text element but focusNode and anchorNode refers to the editor div
// In this case, the focusOffset & anchorOffset match the index + 1 of the selected text
const isOnLastElement = selection.focusNode === editor && selection.focusOffset === editor.childNodes?.length;
if (isOnLastElement) {
return true;
}
// In case of nested html elements (list, code blocks), we are going through all the last child
// The last child of the editor is always a <br> tag, we skip it
let child: ChildNode | null = editor.childNodes.item(editor.childNodes.length - 2);
do {
if (child === selection.focusNode) {
// Checking that the cursor is at end of the selected text
return selection.focusOffset === child.textContent?.length;
}
} while ((child = child.lastChild));
return false;
}