import React from 'react' interface LinkEntityProps { getOwnEntityData: any mergeOwnEntityData: any setReadOnly: any } interface LinkEntityState extends ComponentState { hoverOffsetLeft: number, hoverOffsetTop: number, isHovered: boolean, url: string } export default class LinkEntity extends React.Component { mouseLeaveTimer: any constructor(props) { super(props); this.bindFns(); this.state = { isHovered: false, hoverOffsetLeft: 0, hoverOffsetTop: 0, url: '', } this.mouseLeaveTimer = null; } bindFns() { this.handleMouseDown = this.handleMouseDown.bind(this); this.handleMouseEnter = this.handleMouseEnter.bind(this); this.handleMouseLeave = this.handleMouseLeave.bind(this); this.handleLinkChange = this.handleLinkChange.bind(this); } // LIFECYCLE ----- componentDidMount() { this.setState({ url: this.props.getOwnEntityData(this.props).url }); $(':root').on('mousedown', this.handleMouseDown); } componentWillUnmount() { $(':root').off('mousedown', this.handleMouseDown); } // HANDLERS ----- handleMouseEnter(e) { if (this.mouseLeaveTimer) { clearTimeout(this.mouseLeaveTimer); this.mouseLeaveTimer = null; } const { screenX } = e; const link = this.refs.link as HTMLElement; const { hoverPreview } = this.refs; this.setState({ isHovered: true, hoverOffsetLeft: screenX - (hoverPreview["offsetWidth"] / 2), hoverOffsetTop: link.getBoundingClientRect().top - hoverPreview["offsetHeight"] - 10, }); } handleMouseLeave(e) { this.mouseLeaveTimer = setTimeout(() => this.setState({ isHovered: false }), 400); } handleMouseDown(e) { if (!this.state.isHovered) return; const wrapper = this.refs.wrapper as HTMLElement; const clickIsInside = wrapper.contains(e.target); this.props.setReadOnly(clickIsInside); } handleLinkChange(e) { e.preventDefault(); const url = e.target.value; this.setState({ url }, () => { this.props.mergeOwnEntityData(this.props, { url }); }) } // RENDERING ----- render() { const { children } = this.props; const props = { ref: 'link', className: 'link', onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave, }; return ( { this.renderHoverBox() } { children } ); } renderLinkEditInput() { return (
); } renderHoverBox() { const { isHovered, hoverOffsetLeft, hoverOffsetTop } = this.state; const props = { ref: 'hoverPreview', className: 'link-hover-preview' + (isHovered ? ' visible' : '' ), style: { position: 'fixed' as 'fixed', left: hoverOffsetLeft, top: hoverOffsetTop, } } return (
{ this.renderLinkEditInput() }
); } }