Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

removeRangeFromContentState.js 12 KiB

3 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @format
  8. *
  9. * @emails oncall+draft_js
  10. */
  11. 'use strict';
  12. var ContentBlockNode = require("./ContentBlockNode");
  13. var getNextDelimiterBlockKey = require("./getNextDelimiterBlockKey");
  14. var Immutable = require("immutable");
  15. var List = Immutable.List,
  16. Map = Immutable.Map;
  17. var transformBlock = function transformBlock(key, blockMap, func) {
  18. if (!key) {
  19. return;
  20. }
  21. var block = blockMap.get(key);
  22. if (!block) {
  23. return;
  24. }
  25. blockMap.set(key, func(block));
  26. };
  27. /**
  28. * Ancestors needs to be preserved when there are non selected
  29. * children to make sure we do not leave any orphans behind
  30. */
  31. var getAncestorsKeys = function getAncestorsKeys(blockKey, blockMap) {
  32. var parents = [];
  33. if (!blockKey) {
  34. return parents;
  35. }
  36. var blockNode = blockMap.get(blockKey);
  37. while (blockNode && blockNode.getParentKey()) {
  38. var parentKey = blockNode.getParentKey();
  39. if (parentKey) {
  40. parents.push(parentKey);
  41. }
  42. blockNode = parentKey ? blockMap.get(parentKey) : null;
  43. }
  44. return parents;
  45. };
  46. /**
  47. * Get all next delimiter keys until we hit a root delimiter and return
  48. * an array of key references
  49. */
  50. var getNextDelimitersBlockKeys = function getNextDelimitersBlockKeys(block, blockMap) {
  51. var nextDelimiters = [];
  52. if (!block) {
  53. return nextDelimiters;
  54. }
  55. var nextDelimiter = getNextDelimiterBlockKey(block, blockMap);
  56. while (nextDelimiter && blockMap.get(nextDelimiter)) {
  57. var _block = blockMap.get(nextDelimiter);
  58. nextDelimiters.push(nextDelimiter); // we do not need to keep checking all root node siblings, just the first occurance
  59. nextDelimiter = _block.getParentKey() ? getNextDelimiterBlockKey(_block, blockMap) : null;
  60. }
  61. return nextDelimiters;
  62. };
  63. var getNextValidSibling = function getNextValidSibling(block, blockMap, originalBlockMap) {
  64. if (!block) {
  65. return null;
  66. } // note that we need to make sure we refer to the original block since this
  67. // function is called within a withMutations
  68. var nextValidSiblingKey = originalBlockMap.get(block.getKey()).getNextSiblingKey();
  69. while (nextValidSiblingKey && !blockMap.get(nextValidSiblingKey)) {
  70. nextValidSiblingKey = originalBlockMap.get(nextValidSiblingKey).getNextSiblingKey() || null;
  71. }
  72. return nextValidSiblingKey;
  73. };
  74. var getPrevValidSibling = function getPrevValidSibling(block, blockMap, originalBlockMap) {
  75. if (!block) {
  76. return null;
  77. } // note that we need to make sure we refer to the original block since this
  78. // function is called within a withMutations
  79. var prevValidSiblingKey = originalBlockMap.get(block.getKey()).getPrevSiblingKey();
  80. while (prevValidSiblingKey && !blockMap.get(prevValidSiblingKey)) {
  81. prevValidSiblingKey = originalBlockMap.get(prevValidSiblingKey).getPrevSiblingKey() || null;
  82. }
  83. return prevValidSiblingKey;
  84. };
  85. var updateBlockMapLinks = function updateBlockMapLinks(blockMap, startBlock, endBlock, originalBlockMap) {
  86. return blockMap.withMutations(function (blocks) {
  87. // update start block if its retained
  88. transformBlock(startBlock.getKey(), blocks, function (block) {
  89. return block.merge({
  90. nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
  91. prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
  92. });
  93. }); // update endblock if its retained
  94. transformBlock(endBlock.getKey(), blocks, function (block) {
  95. return block.merge({
  96. nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
  97. prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
  98. });
  99. }); // update start block parent ancestors
  100. getAncestorsKeys(startBlock.getKey(), originalBlockMap).forEach(function (parentKey) {
  101. return transformBlock(parentKey, blocks, function (block) {
  102. return block.merge({
  103. children: block.getChildKeys().filter(function (key) {
  104. return blocks.get(key);
  105. }),
  106. nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
  107. prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
  108. });
  109. });
  110. }); // update start block next - can only happen if startBlock == endBlock
  111. transformBlock(startBlock.getNextSiblingKey(), blocks, function (block) {
  112. return block.merge({
  113. prevSibling: startBlock.getPrevSiblingKey()
  114. });
  115. }); // update start block prev
  116. transformBlock(startBlock.getPrevSiblingKey(), blocks, function (block) {
  117. return block.merge({
  118. nextSibling: getNextValidSibling(block, blocks, originalBlockMap)
  119. });
  120. }); // update end block next
  121. transformBlock(endBlock.getNextSiblingKey(), blocks, function (block) {
  122. return block.merge({
  123. prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
  124. });
  125. }); // update end block prev
  126. transformBlock(endBlock.getPrevSiblingKey(), blocks, function (block) {
  127. return block.merge({
  128. nextSibling: endBlock.getNextSiblingKey()
  129. });
  130. }); // update end block parent ancestors
  131. getAncestorsKeys(endBlock.getKey(), originalBlockMap).forEach(function (parentKey) {
  132. transformBlock(parentKey, blocks, function (block) {
  133. return block.merge({
  134. children: block.getChildKeys().filter(function (key) {
  135. return blocks.get(key);
  136. }),
  137. nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
  138. prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
  139. });
  140. });
  141. }); // update next delimiters all the way to a root delimiter
  142. getNextDelimitersBlockKeys(endBlock, originalBlockMap).forEach(function (delimiterKey) {
  143. return transformBlock(delimiterKey, blocks, function (block) {
  144. return block.merge({
  145. nextSibling: getNextValidSibling(block, blocks, originalBlockMap),
  146. prevSibling: getPrevValidSibling(block, blocks, originalBlockMap)
  147. });
  148. });
  149. }); // if parent (startBlock) was deleted
  150. if (blockMap.get(startBlock.getKey()) == null && blockMap.get(endBlock.getKey()) != null && endBlock.getParentKey() === startBlock.getKey() && endBlock.getPrevSiblingKey() == null) {
  151. var prevSiblingKey = startBlock.getPrevSiblingKey(); // endBlock becomes next sibling of parent's prevSibling
  152. transformBlock(endBlock.getKey(), blocks, function (block) {
  153. return block.merge({
  154. prevSibling: prevSiblingKey
  155. });
  156. });
  157. transformBlock(prevSiblingKey, blocks, function (block) {
  158. return block.merge({
  159. nextSibling: endBlock.getKey()
  160. });
  161. }); // Update parent for previous parent's children, and children for that parent
  162. var prevSibling = prevSiblingKey ? blockMap.get(prevSiblingKey) : null;
  163. var newParentKey = prevSibling ? prevSibling.getParentKey() : null;
  164. startBlock.getChildKeys().forEach(function (childKey) {
  165. transformBlock(childKey, blocks, function (block) {
  166. return block.merge({
  167. parent: newParentKey // set to null if there is no parent
  168. });
  169. });
  170. });
  171. if (newParentKey != null) {
  172. var newParent = blockMap.get(newParentKey);
  173. transformBlock(newParentKey, blocks, function (block) {
  174. return block.merge({
  175. children: newParent.getChildKeys().concat(startBlock.getChildKeys())
  176. });
  177. });
  178. } // last child of deleted parent should point to next sibling
  179. transformBlock(startBlock.getChildKeys().find(function (key) {
  180. var block = blockMap.get(key);
  181. return block.getNextSiblingKey() === null;
  182. }), blocks, function (block) {
  183. return block.merge({
  184. nextSibling: startBlock.getNextSiblingKey()
  185. });
  186. });
  187. }
  188. });
  189. };
  190. var removeRangeFromContentState = function removeRangeFromContentState(contentState, selectionState) {
  191. if (selectionState.isCollapsed()) {
  192. return contentState;
  193. }
  194. var blockMap = contentState.getBlockMap();
  195. var startKey = selectionState.getStartKey();
  196. var startOffset = selectionState.getStartOffset();
  197. var endKey = selectionState.getEndKey();
  198. var endOffset = selectionState.getEndOffset();
  199. var startBlock = blockMap.get(startKey);
  200. var endBlock = blockMap.get(endKey); // we assume that ContentBlockNode and ContentBlocks are not mixed together
  201. var isExperimentalTreeBlock = startBlock instanceof ContentBlockNode; // used to retain blocks that should not be deleted to avoid orphan children
  202. var parentAncestors = [];
  203. if (isExperimentalTreeBlock) {
  204. var endBlockchildrenKeys = endBlock.getChildKeys();
  205. var endBlockAncestors = getAncestorsKeys(endKey, blockMap); // endBlock has unselected siblings so we can not remove its ancestors parents
  206. if (endBlock.getNextSiblingKey()) {
  207. parentAncestors = parentAncestors.concat(endBlockAncestors);
  208. } // endBlock has children so can not remove this block or any of its ancestors
  209. if (!endBlockchildrenKeys.isEmpty()) {
  210. parentAncestors = parentAncestors.concat(endBlockAncestors.concat([endKey]));
  211. } // we need to retain all ancestors of the next delimiter block
  212. parentAncestors = parentAncestors.concat(getAncestorsKeys(getNextDelimiterBlockKey(endBlock, blockMap), blockMap));
  213. }
  214. var characterList;
  215. if (startBlock === endBlock) {
  216. characterList = removeFromList(startBlock.getCharacterList(), startOffset, endOffset);
  217. } else {
  218. characterList = startBlock.getCharacterList().slice(0, startOffset).concat(endBlock.getCharacterList().slice(endOffset));
  219. }
  220. var modifiedStart = startBlock.merge({
  221. text: startBlock.getText().slice(0, startOffset) + endBlock.getText().slice(endOffset),
  222. characterList: characterList
  223. }); // If cursor (collapsed) is at the start of the first child, delete parent
  224. // instead of child
  225. var shouldDeleteParent = isExperimentalTreeBlock && startOffset === 0 && endOffset === 0 && endBlock.getParentKey() === startKey && endBlock.getPrevSiblingKey() == null;
  226. var newBlocks = shouldDeleteParent ? Map([[startKey, null]]) : blockMap.toSeq().skipUntil(function (_, k) {
  227. return k === startKey;
  228. }).takeUntil(function (_, k) {
  229. return k === endKey;
  230. }).filter(function (_, k) {
  231. return parentAncestors.indexOf(k) === -1;
  232. }).concat(Map([[endKey, null]])).map(function (_, k) {
  233. return k === startKey ? modifiedStart : null;
  234. });
  235. var updatedBlockMap = blockMap.merge(newBlocks).filter(function (block) {
  236. return !!block;
  237. }); // Only update tree block pointers if the range is across blocks
  238. if (isExperimentalTreeBlock && startBlock !== endBlock) {
  239. updatedBlockMap = updateBlockMapLinks(updatedBlockMap, startBlock, endBlock, blockMap);
  240. }
  241. return contentState.merge({
  242. blockMap: updatedBlockMap,
  243. selectionBefore: selectionState,
  244. selectionAfter: selectionState.merge({
  245. anchorKey: startKey,
  246. anchorOffset: startOffset,
  247. focusKey: startKey,
  248. focusOffset: startOffset,
  249. isBackward: false
  250. })
  251. });
  252. };
  253. /**
  254. * Maintain persistence for target list when removing characters on the
  255. * head and tail of the character list.
  256. */
  257. var removeFromList = function removeFromList(targetList, startOffset, endOffset) {
  258. if (startOffset === 0) {
  259. while (startOffset < endOffset) {
  260. targetList = targetList.shift();
  261. startOffset++;
  262. }
  263. } else if (endOffset === targetList.count()) {
  264. while (endOffset > startOffset) {
  265. targetList = targetList.pop();
  266. endOffset--;
  267. }
  268. } else {
  269. var head = targetList.slice(0, startOffset);
  270. var tail = targetList.slice(endOffset);
  271. targetList = head.concat(tail).toList();
  272. }
  273. return targetList;
  274. };
  275. module.exports = removeRangeFromContentState;