From cdc92607d6f77653f6e460d08ab6654ffae60d2d Mon Sep 17 00:00:00 2001 From: soupette Date: Wed, 11 Apr 2018 19:36:08 +0200 Subject: [PATCH] Handle img links!!!!!!!! --- .../lib/src/components/Wysiwyg/link.js | 15 +- .../src/components/Wysiwyg/previewWysiwyg.js | 130 ++++++++++++++++-- .../lib/src/components/Wysiwyg/strategies.js | 11 +- .../lib/src/components/Wysiwyg/utils.js | 2 +- 4 files changed, 139 insertions(+), 19 deletions(-) diff --git a/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/link.js b/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/link.js index 0d7742a606..4d0a162879 100644 --- a/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/link.js +++ b/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/link.js @@ -6,19 +6,26 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { includes } from 'lodash'; const Link = props => { - const { url } = props.contentState.getEntity(props.entityKey).getData(); + const { url, aHref, aInnerHTML } = props.contentState.getEntity(props.entityKey).getData(); + let content = aInnerHTML; + + if (includes(aInnerHTML, '; + } return ( { - window.open(url, '_blank'); + window.open(url || aHref, '_blank'); }} style={{ cursor: 'pointer' }} > - {props.children} + {content || props.children} ); }; diff --git a/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/previewWysiwyg.js b/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/previewWysiwyg.js index 2d3400e117..19cf0ca994 100644 --- a/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/previewWysiwyg.js +++ b/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/previewWysiwyg.js @@ -6,13 +6,28 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { CompositeDecorator, ContentState, convertFromHTML, EditorState } from 'draft-js'; +import { + CompositeDecorator, + ContentState, + convertFromHTML, + EditorState, + ContentBlock, + genKey, + Entity, + CharacterMetadata, +} from 'draft-js'; +import { List, OrderedSet, Repeat, fromJS } from 'immutable'; import cn from 'classnames'; -import { isEmpty } from 'lodash'; +import { isEmpty, toArray } from 'lodash'; import WysiwygEditor from 'components/WysiwygEditor'; import converter from './converter'; -import { findLinkEntities, findImageEntities, findVideoEntities } from './strategies'; +import { + findAtomicEntities, + findLinkEntities, + findImageEntities, + findVideoEntities, +} from './strategies'; import Image from './image'; import Link from './link'; @@ -55,8 +70,72 @@ const decorator = new CompositeDecorator([ strategy: findVideoEntities, component: Video, }, + { + strategy: findAtomicEntities, + component: Link, + }, ]); +const getBlockSpecForElement = aElement => ({ + contentType: 'link', + aHref: aElement.href, + aInnerHTML: aElement.innerHTML, +}); + +const elementToBlockSpecElement = element => wrapBlockSpec(getBlockSpecForElement(element)); + +const wrapBlockSpec = blockSpec => { + if (blockSpec == null) { + return null; + } + const tempEl = document.createElement('blockquote'); + // stringify meta data and insert it as text content of temp HTML element. We will later extract + // and parse it. + tempEl.innerText = JSON.stringify(blockSpec); + return tempEl; +}; + +const replaceElement = (oldEl, newEl) => { + if (!(newEl instanceof HTMLElement)) { + return; + } + const parentNode = oldEl.parentNode; + return parentNode.replaceChild(newEl, oldEl); +}; + +const aReplacer = aElement => replaceElement(aElement, elementToBlockSpecElement(aElement)); + +const createContentBlock = (blockData = {}) => { + const { key, type, text, data, inlineStyles, entityData } = blockData; + + let blockSpec = { + type: type !== null && type !== undefined ? type : 'unstyled', + text: text !== null && text !== undefined ? text : '', + key: key !== null && key !== undefined ? key : genKey(), + }; + + if (data) { + blockSpec.data = fromJS(data); + } + + if (inlineStyles || entityData) { + let entityKey; + if (entityData) { + const { type, mutability, data } = entityData; + entityKey = Entity.create(type, mutability, data); + } else { + entityKey = null; + } + const style = OrderedSet(inlineStyles || ['LINK']); + const charData = CharacterMetadata.applyEntity( + CharacterMetadata.create({ style, entityKey }), + entityKey, + ); + blockSpec.characterList = List(Repeat(charData, text.length)); + } + return new ContentBlock(blockSpec); +}; + class PreviewWysiwyg extends React.PureComponent { state = { editorState: EditorState.createEmpty(), isMounted: false }; @@ -101,26 +180,51 @@ class PreviewWysiwyg extends React.PureComponent { previewHTML = rawContent => { const initHtml = isEmpty(rawContent) ? '

' : rawContent; - const html = converter.makeHtml(initHtml); - // This action takes a long time - const blocksFromHTML = convertFromHTML(html); + const html = new DOMParser().parseFromString(converter.makeHtml(initHtml), 'text/html'); + toArray(html.querySelectorAll('a')).forEach(aReplacer); + + let blocksFromHTML = convertFromHTML(html.body.innerHTML); - // Make sure blocksFromHTML.contentBlocks !== null if (blocksFromHTML.contentBlocks) { - const contentState = ContentState.createFromBlockArray( - blocksFromHTML.contentBlocks, - blocksFromHTML.entityMap, - ); + blocksFromHTML = blocksFromHTML.contentBlocks.reduce((acc, block) => { + if (block.getType() === 'blockquote') { + try { + const { aHref, aInnerHTML } = JSON.parse(block.getText()); + const entityData = { + type: 'LINK', + mutability: 'IMMUTABLE', + data: { + aHref, + aInnerHTML, + }, + }; + + const blockSpec = Object.assign( + { type: 'atomic', text: ' ', key: block.getKey() }, + { entityData }, + ); + const atomicBlock = createContentBlock(blockSpec); + + return acc.concat([atomicBlock]); + } catch (err) { + return acc.concat(block); + } + } + + return acc.concat(block); + }, []); + + const contentState = ContentState.createFromBlockArray(blocksFromHTML); + return this.setState({ editorState: EditorState.createWithContent(contentState, decorator) }); } - // Prevent errors if value is empty return this.setState({ editorState: EditorState.createEmpty() }); }; render() { const { placeholder } = this.context; - + // this.previewHTML2(this.props.data); return (
{ const entityKey = character.getEntity(); @@ -5,6 +7,13 @@ function findLinkEntities(contentBlock, callback, contentState) { }, callback); } +function findAtomicEntities(contentBlock, callback, contentState) { + contentBlock.findEntityRanges(character => { + const entityKey = character.getEntity(); + return entityKey !== null && contentBlock.getType() === 'atomic'; + }, callback); +} + function findImageEntities(contentBlock, callback, contentState) { contentBlock.findEntityRanges(character => { const entityKey = character.getEntity(); @@ -23,4 +32,4 @@ function findVideoEntities(contentBlock, cb, contentState) { const isVideoType = (fileName) => /\.(mp4|mpg|mpeg|mov|avi)$/i.test(fileName); -export { findLinkEntities, findImageEntities, findVideoEntities }; +export { findAtomicEntities, findLinkEntities, findImageEntities, findVideoEntities }; diff --git a/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/utils.js b/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/utils.js index 1ed5a91c0b..d13b4ac746 100644 --- a/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/utils.js +++ b/packages/strapi-helper-plugin/lib/src/components/Wysiwyg/utils.js @@ -9,7 +9,7 @@ import { List } from 'immutable'; import { DEFAULT_INDENTATION } from './constants'; export function createNewBlock(text = '', type = 'unstyled', key = genKey()) { - return new ContentBlock({ key, type, text, charaterList: List([]) }); + return new ContentBlock({ key, type, text, characterList: List([]) }); } export function getNextBlocksList(editorState, startKey) {