mirror of https://github.com/vector-im/riot-web
use caret nodes in caret positioning code, to move caret out of pills
parent
607fc328ed
commit
a229641985
|
@ -15,50 +15,104 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {needsCaretNodeBefore, needsCaretNodeAfter} from "./render";
|
||||
|
||||
export function setCaretPosition(editor, model, caretPosition) {
|
||||
const sel = document.getSelection();
|
||||
sel.removeAllRanges();
|
||||
const range = document.createRange();
|
||||
const {offset, lineIndex, nodeIndex} = getLineAndNodePosition(model, caretPosition);
|
||||
const lineNode = editor.childNodes[lineIndex];
|
||||
|
||||
let focusNode;
|
||||
// empty line with just a <br>
|
||||
if (nodeIndex === -1) {
|
||||
focusNode = lineNode;
|
||||
} else {
|
||||
focusNode = lineNode.childNodes[nodeIndex];
|
||||
// make sure we have a text node
|
||||
if (focusNode.nodeType === Node.ELEMENT_NODE && focusNode.firstChild) {
|
||||
focusNode = focusNode.firstChild;
|
||||
}
|
||||
}
|
||||
range.setStart(focusNode, offset);
|
||||
range.collapse(true);
|
||||
sel.addRange(range);
|
||||
}
|
||||
|
||||
function getLineAndNodePosition(model, caretPosition) {
|
||||
const {parts} = model;
|
||||
const {index} = caretPosition;
|
||||
const partIndex = caretPosition.index;
|
||||
const lineResult = findNodeInLineForPart(parts, partIndex);
|
||||
const {lineIndex} = lineResult;
|
||||
let {nodeIndex} = lineResult;
|
||||
let {offset} = caretPosition;
|
||||
// we're at an empty line between a newline part
|
||||
// and another newline part or end/start of parts.
|
||||
// set offset to 0 so it gets set to the <br> inside the line container
|
||||
if (nodeIndex === -1) {
|
||||
offset = 0;
|
||||
} else {
|
||||
// move caret out of uneditable part (into caret node, or empty line br) if needed
|
||||
({nodeIndex, offset} = moveOutOfUneditablePart(parts, partIndex, nodeIndex, offset));
|
||||
}
|
||||
return {lineIndex, nodeIndex, offset};
|
||||
}
|
||||
|
||||
function findNodeInLineForPart(parts, partIndex) {
|
||||
let lineIndex = 0;
|
||||
let nodeIndex = -1;
|
||||
for (let i = 0; i <= index; ++i) {
|
||||
|
||||
let prevPart = null;
|
||||
// go through to parts up till (and including) the index
|
||||
// to find newline parts
|
||||
for (let i = 0; i <= partIndex; ++i) {
|
||||
const part = parts[i];
|
||||
if (part && part.type === "newline") {
|
||||
if (i < index) {
|
||||
if (part.type === "newline") {
|
||||
lineIndex += 1;
|
||||
nodeIndex = -1;
|
||||
prevPart = null;
|
||||
} else {
|
||||
// if index points at a newline part,
|
||||
// put the caret at the end of the previous part
|
||||
// so it stays on the same line
|
||||
const prevPart = parts[i - 1];
|
||||
offset = prevPart ? prevPart.text.length : 0;
|
||||
nodeIndex += 1;
|
||||
if (needsCaretNodeBefore(part, prevPart)) {
|
||||
nodeIndex += 1;
|
||||
}
|
||||
} else {
|
||||
// only jump over caret node if we're not at our destination node already,
|
||||
// as we'll assume in moveOutOfUneditablePart that nodeIndex
|
||||
// refers to the node corresponding to the part,
|
||||
// and not an adjacent caret node
|
||||
if (i < partIndex) {
|
||||
const nextPart = parts[i + 1];
|
||||
const isLastOfLine = !nextPart || nextPart.type === "newline";
|
||||
if (needsCaretNodeAfter(part, isLastOfLine)) {
|
||||
nodeIndex += 1;
|
||||
}
|
||||
}
|
||||
let focusNode;
|
||||
const lineNode = editor.childNodes[lineIndex];
|
||||
if (lineNode) {
|
||||
focusNode = lineNode.childNodes[nodeIndex];
|
||||
if (!focusNode) {
|
||||
focusNode = lineNode;
|
||||
} else if (focusNode.nodeType === Node.ELEMENT_NODE) {
|
||||
focusNode = focusNode.childNodes[0];
|
||||
prevPart = part;
|
||||
}
|
||||
}
|
||||
// node not found, set caret at end
|
||||
if (!focusNode) {
|
||||
range.selectNodeContents(editor);
|
||||
range.collapse(false);
|
||||
|
||||
return {lineIndex, nodeIndex};
|
||||
}
|
||||
|
||||
function moveOutOfUneditablePart(parts, partIndex, nodeIndex, offset) {
|
||||
// move caret before or after uneditable part
|
||||
const part = parts[partIndex];
|
||||
if (part && !part.canEdit) {
|
||||
if (offset === 0) {
|
||||
nodeIndex -= 1;
|
||||
const prevPart = parts[partIndex - 1];
|
||||
// if the previous node is a caret node, it's empty
|
||||
// so the offset can stay at 0
|
||||
// only when it's not, we need to set the offset
|
||||
// at the end of the node
|
||||
if (!needsCaretNodeBefore(part, prevPart)) {
|
||||
offset = prevPart.text.length;
|
||||
}
|
||||
} else {
|
||||
// make sure we have a text node
|
||||
range.setStart(focusNode, offset);
|
||||
range.collapse(true);
|
||||
nodeIndex += 1;
|
||||
offset = 0;
|
||||
}
|
||||
sel.addRange(range);
|
||||
}
|
||||
return {nodeIndex, offset};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue