Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ export default class EditLinkPopoverWrapper extends React.Component {
autoPopover: false,
popoverOnTop: false
})
} else if (evt.key === 'Backspace') {
// Backspace should close the popover as it may delete the created link entity
} else if (evt.key === 'Backspace' || evt.key === 'Delete') {
// Backspace or Delete should close the popover as it may delete the created link entity
this.setState({
autoPopover: false,
popoverOnTop: false
Expand Down Expand Up @@ -140,14 +140,23 @@ export default class EditLinkPopoverWrapper extends React.Component {
*/
onEditorStateChange(editorState, prevLastCreatedEntityKey) {
const selectionState = editorState.getSelection()
const contentState = editorState.getCurrentContent()

// If cursor moved
if (
this.isSelectionChanged(selectionState) &&
selectionState.getHasFocus()
) {
const contentState = editorState.getCurrentContent()
const lastCreatedEntityKey = contentState.getLastCreatedEntityKey()
const lastCreatedEntity = getEntityForKey(
contentState,
lastCreatedEntityKey
)
const lastCreatedEntityType = lastCreatedEntity && lastCreatedEntity.getType()
const newEntityCreated =
lastCreatedEntityKey && prevLastCreatedEntityKey !== lastCreatedEntityKey
const newLinkCreated = newEntityCreated && lastCreatedEntityType === 'LINK'

const cursorMoved =
this.isSelectionChanged(selectionState) && selectionState.getHasFocus()

// If cursor moved or new link created
if (cursorMoved || newLinkCreated) {
const startBlock = getSelectionBlock(selectionState, contentState)
const endBlock = getSelectionBlock(selectionState, contentState, true)

Expand All @@ -161,36 +170,11 @@ export default class EditLinkPopoverWrapper extends React.Component {
selectionState.isCollapsed() ? selectionEnd : selectionEnd - 1
)

// If the cursor is on a link or whole/part of the link is selected
if (
startEntity &&
endEntity === startEntity &&
startEntity.getType() === 'LINK'
) {
this.editExistingLink(
startBlock.getEntityAt(selectionStart),
contentState
)
} else if (selectionState.isCollapsed()) {
const lastCreatedEntityKey = contentState.getLastCreatedEntityKey()
const lastCreatedEntity = getEntityForKey(
contentState,
lastCreatedEntityKey
)

// If a new link entity created, auto show the popover
if (selectionState.isCollapsed() && newLinkCreated) {
const entityData = lastCreatedEntity && lastCreatedEntity.getData()
const entityType = lastCreatedEntity && lastCreatedEntity.getType()
const newEntityCreated =
lastCreatedEntityKey &&
prevLastCreatedEntityKey !== lastCreatedEntityKey

// If a new link entity created, auto show the popover
if (
newEntityCreated &&
entityType === 'LINK' &&
entityData &&
entityData.url
) {

if (entityData && entityData.url) {
this.openAutoPopover(lastCreatedEntityKey, contentState)

// If enter is pressed, the cursor would be in the start of the line. So, text till cursor will be empty ('')
Expand All @@ -203,6 +187,17 @@ export default class EditLinkPopoverWrapper extends React.Component {
} else {
this.hideEdit()
}
}
// If the cursor is on a link or whole/part of the link is selected
else if (
startEntity &&
endEntity === startEntity &&
startEntity.getType() === 'LINK'
) {
this.editExistingLink(
startBlock.getEntityAt(selectionStart),
contentState
)
} else {
this.hideEdit()
}
Expand Down
15 changes: 14 additions & 1 deletion src/components/RichTextArea/LinkPlugin/Link/Link.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,22 @@ export default class Link extends Component {
this.setElementGetter(contentState, entityKey)
}

componentWillUnmount() {
const { contentState, entityKey } = this.props
this.removeElementGetter(contentState, entityKey)
}

setElementGetter(contentState, entityKey) {
contentState.mergeEntityData(entityKey, {
el: () => this.element
el: () => {
return this.element
}
})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vigneshTheDev Looks like the previous code is equivalent to the new one. Maybe we can revert this change and keep the previous code.

Copy link
Collaborator Author

@vigneshTheDev vigneshTheDev Jun 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Correct. I changed it to enable debugging. I'll make the change and push it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @maxceem, looks like the pull request is closed. Shall I submit a new PR with the change?

}

removeElementGetter(contentState, entityKey) {
contentState.mergeEntityData(entityKey, {
el: null
})
}

Expand Down
6 changes: 4 additions & 2 deletions src/components/RichTextArea/LinkPlugin/LinkPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Link from './Link/Link'
import linkStrategy from './linkStrategy'

import createLinkEntity from './utils/createLink'
import handlePastedText from './utils/handlePastedText'

/**
* Creates link plugin
Expand All @@ -17,9 +18,10 @@ export default function createLinkPlugin() {
decorators: [
{
strategy: linkStrategy,
component: Link,
component: Link
}
],
onChange: createLinkEntity
onChange: createLinkEntity,
handlePastedText
}
}
104 changes: 104 additions & 0 deletions src/components/RichTextArea/LinkPlugin/utils/handlePastedText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { EditorState, Modifier, convertFromHTML } from 'draft-js'

import newLinkifyIt from 'linkify-it'
import tlds from 'tlds'

const linkifyIt = newLinkifyIt()
linkifyIt.tlds(tlds)

/**
* Handle pasted text
* @param {string} text - pasted text
* @param {string} html - pasted html
* @param {Object} lastEditorState - current editor state
* @param {Object} extras - Extra object passed by draft-js-link-editor.
* @param {function} extras.setEditorState - function to set new editor state.
* @param {function} extras.getEditorState - function to get current editor state.
*/
export default function handlePastedText(
text,
html,
lastEditorState,
{ setEditorState, getEditorState }
) {
if (!text) {
return false
}

// parse all the links in the pasted text
const links = linkifyIt.match(text)
if (!links) {
return false
}

// Let the editor render the pasted text before we detect the links
setTimeout(() => {
const lastSelectionState = lastEditorState.getSelection()
const startKey = lastSelectionState.getStartKey()
let startOffset = lastSelectionState.getStartOffset()

const newBlocks = html && convertFromHTML(html).contentBlocks
const pastedLines = text.split('\n').filter(line => line)

// When we paste formatted text, draftjs pastes it automatically in a new line (i.e., new block)
const extraBlockInserted =
newBlocks && newBlocks.length > pastedLines.length

const currentEditorState = getEditorState()
const initialSelectionState = currentEditorState.getSelection()
let currentContentState = currentEditorState.getCurrentContent()
let currentSelectionState = initialSelectionState

let currentBlock = extraBlockInserted
? currentContentState.getBlockAfter(startKey)
: currentContentState.getBlockForKey(startKey)
let currentBlockKey = currentBlock.getKey()
let currentLine

for (let i = 0, numLines = pastedLines.length; i < numLines; i++) {
currentLine = pastedLines[i]

const linksInLine = linkifyIt.match(currentLine)
if (linksInLine) {
linksInLine.forEach(({ index, lastIndex, url }) => {
// Create the link entity
currentContentState = currentContentState.createEntity(
'LINK',
'MUTABLE',
{
url,
text: null
}
)
const entityKey = currentContentState.getLastCreatedEntityKey()

// Apply the link entity
currentSelectionState = currentSelectionState.merge({
anchorOffset: index + startOffset,
focusOffset: lastIndex + startOffset,
anchorKey: currentBlockKey,
focusKey: currentBlockKey,
isBackward: false
})
currentContentState = Modifier.applyEntity(
currentContentState,
currentSelectionState,
entityKey
)
})
}

currentBlock = currentContentState.getBlockAfter(currentBlockKey)
currentBlockKey = currentBlock && currentBlock.getKey()
startOffset = 0
}

// Move cursor to the end of the pasted text
const newEditorState = EditorState.forceSelection(
EditorState.push(currentEditorState, currentContentState, 'apply-entity'),
initialSelectionState
)

setEditorState(newEditorState)
})
}