Skip to content

Instantly share code, notes, and snippets.

@mathieudutour
Last active March 3, 2017 19:36
Show Gist options
  • Save mathieudutour/a92e3f61b528d39d30285c745994baeb to your computer and use it in GitHub Desktop.
Save mathieudutour/a92e3f61b528d39d30285c745994baeb to your computer and use it in GitHub Desktop.
import React from 'react'
import Portal from 'react-portal'
class LinkBlock extends React.Component {
constructor (props) {
super(props)
this.state = {
href: this.props.node.data.get('href'),
showingPopup: false,
popup: null,
focus: false
}
}
componentDidMount = () => {
this.updatePortals()
const { node } = this.props
const { data } = node
if (data.get('href') === '') {
this.setState({focus: true}, () => this.refs.input.focus())
}
}
componentWillUpdate = (nextProps) => {
if (this.props.node.data.get('href') !== nextProps.node.data.get('href')) {
this.setState({href: nextProps.node.data.get('href')})
}
}
componentDidUpdate = () => {
this.updatePortals()
}
componentWillUnmount = () => {
clearTimeout(this.timeout)
}
render () {
const { attributes, children } = this.props
return (
<a target='_blank' {...attributes} href={this.state.href} ref='link'
onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
{children}
<Portal isOpened={this.state.showingPopup || this.state.focus} onOpen={this.onOpenPopup}>
<div className='linkContainer' onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<input value={this.state.href} onChange={this.onChange} placeholder='Enter link URL'
onFocus={this.onFocus} onBlur={this.onBlur} ref='input' />
</div>
</Portal>
</a>
)
}
onMouseEnter = (e) => {
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.setState({showingPopup: true})
}, 500)
}
onMouseLeave = (e) => {
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.setState({showingPopup: false})
}, 300)
}
onFocus = (e) => {
this.setState({focus: true})
}
onBlur = (e) => {
this.setState({focus: false}, () => {
if (this.props.node.data.get('href') === '') {
// TODO unwrap link
} else {
const newState = this.props.state.merge({
document: this.props.state.document.updateDescendant(this.props.node.merge({
data: this.props.node.data.set('href', e.target.value)
}))
})
this.props.onChange(newState)
}
})
}
onOpenPopup = (portal) => {
this.setState({ popup: portal.firstChild })
}
onChange = (e) => {
this.setState({href: e.target.value})
}
updatePortals = () => {
const { popup } = this.state
if (popup) {
const { link } = this.refs
const rect = link.getBoundingClientRect()
popup.style.position = 'absolute'
popup.style.opacity = 1
popup.style.zIndex = 1000
popup.style.top = `${rect.top + window.scrollY - popup.offsetHeight}px`
popup.style.left = `${rect.left + window.scrollX - popup.offsetWidth / 2 + rect.width}px`
}
}
}
LinkBlock.propTypes = {
attributes: React.PropTypes.object.isRequired,
children: React.PropTypes.any.isRequired,
state: React.PropTypes.object.isRequired,
node: React.PropTypes.object.isRequired,
onChange: React.PropTypes.func.isRequired
}
export default LinkBlock
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment