resolveCaretPosition.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. import { getUIValue } from '../../document/UI.js';
  2. import '../../utils/dataTransfer/Clipboard.js';
  3. import { hasOwnSelection } from '../../utils/focus/selection.js';
  4. function resolveCaretPosition({ target, node, offset }) {
  5. if (hasOwnSelection(target)) {
  6. return {
  7. node: target,
  8. offset: offset !== null && offset !== undefined ? offset : getUIValue(target).length
  9. };
  10. } else if (node) {
  11. return {
  12. node,
  13. offset: offset !== null && offset !== undefined ? offset : node.nodeType === 3 ? node.nodeValue.length : node.childNodes.length
  14. };
  15. }
  16. return findNodeAtTextOffset(target, offset);
  17. }
  18. function findNodeAtTextOffset(node, offset, isRoot = true) {
  19. // When clicking after the content the browser behavior can be complicated:
  20. // 1. If there is textContent after the last element child,
  21. // the cursor is moved there.
  22. // 2. If there is textContent in the last element child,
  23. // the browser moves the cursor to the last non-empty text node inside this element.
  24. // 3. Otherwise the cursor is moved to the end of the target.
  25. let i = offset === undefined ? node.childNodes.length - 1 : 0;
  26. const step = offset === undefined ? -1 : 1;
  27. while(offset === undefined ? i >= (isRoot ? Math.max(node.childNodes.length - 1, 0) : 0) : i <= node.childNodes.length){
  28. if (offset && i === node.childNodes.length) {
  29. throw new Error('The given offset is out of bounds.');
  30. }
  31. const c = node.childNodes.item(i);
  32. const text = String(c.textContent);
  33. if (text.length) {
  34. if (offset !== undefined && text.length < offset) {
  35. offset -= text.length;
  36. } else if (c.nodeType === 1) {
  37. return findNodeAtTextOffset(c, offset, false);
  38. } else {
  39. // The pre-commit hooks keeps changing this
  40. // See https://github.com/kentcdodds/kcd-scripts/issues/218
  41. /* istanbul ignore else */ // eslint-disable-next-line no-lonely-if
  42. if (c.nodeType === 3) {
  43. return {
  44. node: c,
  45. offset: offset !== null && offset !== undefined ? offset : c.nodeValue.length
  46. };
  47. }
  48. }
  49. }
  50. i += step;
  51. }
  52. return {
  53. node,
  54. offset: node.childNodes.length
  55. };
  56. }
  57. export { resolveCaretPosition };