Skip to content

Commit

Permalink
Moving atomic blocks (facebookarchive#551)
Browse files Browse the repository at this point in the history
* Implemented moveAtomicBlockBefore and moveAtomicBlockAfter in AtomicBlockUtils

* Renamed EditorChangeType 'change-fragment' to 'move-block' and added to docs

* Changed interface for moving atomic blocks:

- Just use one function called moveAtomicBlock instead of three
- Introduce DraftInsertionType for determining if a fragment shall replace another fragment, or if it shall be inserted before or after another fragment

* Using ES6 variable declaration and getStart/EndKey instead of getAnchor/FocusKey
  • Loading branch information
delijah authored and hellendag committed Dec 30, 2016
1 parent 5120924 commit b7fadff
Show file tree
Hide file tree
Showing 7 changed files with 756 additions and 2 deletions.
11 changes: 11 additions & 0 deletions docs/APIReference-AtomicBlockUtils.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,14 @@ insertAtomicBlock: function(
character: string
): EditorState
```

### moveAtomicBlock

```
moveAtomicBlock: function(
editorState: EditorState,
atomicBlock: ContentBlock,
targetRange: SelectionState,
insertionMode?: DraftInsertionType
): EditorState
```
4 changes: 4 additions & 0 deletions docs/APIReference-EditorChangeType.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ The `type` value of one or more `ContentBlock` objects is being changed.

An inline style is being applied or removed for one or more characters.

#### `move-block`

A block is being moved within the [BlockMap](https://github.com/facebook/draft-js/blob/master/src/model/immutable/BlockMap.js).

#### `delete-character`

A single character is being forward-removed.
Expand Down
19 changes: 19 additions & 0 deletions src/model/constants/DraftInsertionType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule DraftInsertionType
* @flow
*/

'use strict';

/**
* A type that defines if an fragment shall be inserted before or after
* another fragment or if the selected fragment shall be replaced
*/
export type DraftInsertionType = 'replace' | 'before' | 'after';
1 change: 1 addition & 0 deletions src/model/immutable/EditorChangeType.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export type EditorChangeType = (
'change-block-data' |
'change-block-type' |
'change-inline-style' |
'move-block' |
'delete-character' |
'insert-characters' |
'insert-fragment' |
Expand Down
82 changes: 82 additions & 0 deletions src/model/modifier/AtomicBlockUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ const CharacterMetadata = require('CharacterMetadata');
const ContentBlock = require('ContentBlock');
const DraftModifier = require('DraftModifier');
const EditorState = require('EditorState');
const SelectionState = require('SelectionState');
const Immutable = require('immutable');

const generateRandomKey = require('generateRandomKey');
const moveBlockInContentState = require('moveBlockInContentState');

import type {DraftInsertionType} from 'DraftInsertionType';

const {
List,
Expand Down Expand Up @@ -84,6 +88,84 @@ const AtomicBlockUtils = {

return EditorState.push(editorState, newContent, 'insert-fragment');
},

moveAtomicBlock: function(
editorState: EditorState,
atomicBlock: ContentBlock,
targetRange: SelectionState,
insertionMode?: DraftInsertionType
): EditorState {
const contentState = editorState.getCurrentContent();
const selectionState = editorState.getSelection();

let withMovedAtomicBlock;

if (insertionMode === 'before' || insertionMode === 'after') {
const targetBlock = contentState.getBlockForKey(
insertionMode === 'before' ?
targetRange.getStartKey() :
targetRange.getEndKey()
);

withMovedAtomicBlock = moveBlockInContentState(
contentState,
atomicBlock,
targetBlock,
insertionMode
);
} else {
const afterRemoval = DraftModifier.removeRange(
contentState,
targetRange,
'backward'
);

const selectionAfterRemoval = afterRemoval.getSelectionAfter();
const targetBlock = afterRemoval.getBlockForKey(
selectionAfterRemoval.getFocusKey()
);

if (selectionAfterRemoval.getStartOffset() === 0) {
withMovedAtomicBlock = moveBlockInContentState(
afterRemoval,
atomicBlock,
targetBlock,
'before'
);
} else if (selectionAfterRemoval.getEndOffset() === targetBlock.getLength()) {
withMovedAtomicBlock = moveBlockInContentState(
afterRemoval,
atomicBlock,
targetBlock,
'after'
);
} else {
const afterSplit = DraftModifier.splitBlock(
afterRemoval,
selectionAfterRemoval
);

const selectionAfterSplit = afterSplit.getSelectionAfter();
const targetBlock = afterSplit.getBlockForKey(
selectionAfterSplit.getFocusKey()
);

withMovedAtomicBlock = moveBlockInContentState(
afterSplit,
atomicBlock,
targetBlock,
'before'
);
}
}

const newContent = withMovedAtomicBlock.merge({
selectionBefore: selectionState,
selectionAfter: withMovedAtomicBlock.getSelectionAfter().set('hasFocus', true),
});

return EditorState.push(editorState, newContent, 'move-block');
},
};

module.exports = AtomicBlockUtils;
Loading

0 comments on commit b7fadff

Please sign in to comment.