You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

116 lines
4.8 KiB

  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. * @flow strict-local
  9. * @emails oncall+draft_js
  10. */
  11. 'use strict';
  12. import type { BlockNodeRecord } from "./BlockNodeRecord";
  13. import type { DraftInsertionType } from "./DraftInsertionType";
  14. import type SelectionState from "./SelectionState";
  15. const BlockMapBuilder = require("./BlockMapBuilder");
  16. const CharacterMetadata = require("./CharacterMetadata");
  17. const ContentBlock = require("./ContentBlock");
  18. const ContentBlockNode = require("./ContentBlockNode");
  19. const DraftModifier = require("./DraftModifier");
  20. const EditorState = require("./EditorState");
  21. const generateRandomKey = require("./generateRandomKey");
  22. const gkx = require("./gkx");
  23. const Immutable = require("immutable");
  24. const moveBlockInContentState = require("./moveBlockInContentState");
  25. const experimentalTreeDataSupport = gkx('draft_tree_data_support');
  26. const ContentBlockRecord = experimentalTreeDataSupport ? ContentBlockNode : ContentBlock;
  27. const {
  28. List,
  29. Repeat
  30. } = Immutable;
  31. const AtomicBlockUtils = {
  32. insertAtomicBlock: function (editorState: EditorState, entityKey: string, character: string): EditorState {
  33. const contentState = editorState.getCurrentContent();
  34. const selectionState = editorState.getSelection();
  35. const afterRemoval = DraftModifier.removeRange(contentState, selectionState, 'backward');
  36. const targetSelection = afterRemoval.getSelectionAfter();
  37. const afterSplit = DraftModifier.splitBlock(afterRemoval, targetSelection);
  38. const insertionTarget = afterSplit.getSelectionAfter();
  39. const asAtomicBlock = DraftModifier.setBlockType(afterSplit, insertionTarget, 'atomic');
  40. const charData = CharacterMetadata.create({
  41. entity: entityKey
  42. });
  43. let atomicBlockConfig = {
  44. key: generateRandomKey(),
  45. type: 'atomic',
  46. text: character,
  47. characterList: List(Repeat(charData, character.length))
  48. };
  49. let atomicDividerBlockConfig = {
  50. key: generateRandomKey(),
  51. type: 'unstyled'
  52. };
  53. if (experimentalTreeDataSupport) {
  54. atomicBlockConfig = { ...atomicBlockConfig,
  55. nextSibling: atomicDividerBlockConfig.key
  56. };
  57. atomicDividerBlockConfig = { ...atomicDividerBlockConfig,
  58. prevSibling: atomicBlockConfig.key
  59. };
  60. }
  61. const fragmentArray = [new ContentBlockRecord(atomicBlockConfig), new ContentBlockRecord(atomicDividerBlockConfig)];
  62. const fragment = BlockMapBuilder.createFromArray(fragmentArray);
  63. const withAtomicBlock = DraftModifier.replaceWithFragment(asAtomicBlock, insertionTarget, fragment);
  64. const newContent = withAtomicBlock.merge({
  65. selectionBefore: selectionState,
  66. selectionAfter: withAtomicBlock.getSelectionAfter().set('hasFocus', true)
  67. });
  68. return EditorState.push(editorState, newContent, 'insert-fragment');
  69. },
  70. moveAtomicBlock: function (editorState: EditorState, atomicBlock: BlockNodeRecord, targetRange: SelectionState, insertionMode?: DraftInsertionType): EditorState {
  71. const contentState = editorState.getCurrentContent();
  72. const selectionState = editorState.getSelection();
  73. let withMovedAtomicBlock;
  74. if (insertionMode === 'before' || insertionMode === 'after') {
  75. const targetBlock = contentState.getBlockForKey(insertionMode === 'before' ? targetRange.getStartKey() : targetRange.getEndKey());
  76. withMovedAtomicBlock = moveBlockInContentState(contentState, atomicBlock, targetBlock, insertionMode);
  77. } else {
  78. const afterRemoval = DraftModifier.removeRange(contentState, targetRange, 'backward');
  79. const selectionAfterRemoval = afterRemoval.getSelectionAfter();
  80. const targetBlock = afterRemoval.getBlockForKey(selectionAfterRemoval.getFocusKey());
  81. if (selectionAfterRemoval.getStartOffset() === 0) {
  82. withMovedAtomicBlock = moveBlockInContentState(afterRemoval, atomicBlock, targetBlock, 'before');
  83. } else if (selectionAfterRemoval.getEndOffset() === targetBlock.getLength()) {
  84. withMovedAtomicBlock = moveBlockInContentState(afterRemoval, atomicBlock, targetBlock, 'after');
  85. } else {
  86. const afterSplit = DraftModifier.splitBlock(afterRemoval, selectionAfterRemoval);
  87. const selectionAfterSplit = afterSplit.getSelectionAfter();
  88. const targetBlock = afterSplit.getBlockForKey(selectionAfterSplit.getFocusKey());
  89. withMovedAtomicBlock = moveBlockInContentState(afterSplit, atomicBlock, targetBlock, 'before');
  90. }
  91. }
  92. const newContent = withMovedAtomicBlock.merge({
  93. selectionBefore: selectionState,
  94. selectionAfter: withMovedAtomicBlock.getSelectionAfter().set('hasFocus', true)
  95. });
  96. return EditorState.push(editorState, newContent, 'move-block');
  97. }
  98. };
  99. module.exports = AtomicBlockUtils;