Skip to content
This repository has been archived by the owner on Feb 6, 2023. It is now read-only.

Commit

Permalink
add image support for convertFromHTML, along with new example (#734)
Browse files Browse the repository at this point in the history
* add image support for convertFromHTML, along with new example

* Update example page title

* Chrome fix

* clean up requires
  • Loading branch information
tylercraft committed Oct 26, 2016
1 parent 5a36195 commit 8ac72f7
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 1 deletion.
177 changes: 177 additions & 0 deletions examples/convertFromHTML/convert.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
<!--
Copyright (c) 2013-present, Facebook, Inc. All rights reserved.
This file provided by Facebook is for non-commercial testing and evaluation
purposes only. Facebook reserves all rights not expressly granted.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Draft • Convert from HTML</title>
<link rel="stylesheet" href="../../dist/Draft.css" />
</head>
<body>
<div id="target"></div>
<script src="../../node_modules/react/dist/react.js"></script>
<script src="../../node_modules/react-dom/dist/react-dom.js"></script>
<script src="../../node_modules/immutable/dist/immutable.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.js"></script>
<script src="../../dist/Draft.js"></script>
<script type="text/babel">
'use strict';

const {
CompositeDecorator,
ContentBlock,
ContentState,
Editor,
EditorState,
convertFromHTML,
convertToRaw,
} = Draft;

class HTMLConvertExample extends React.Component {
constructor(props) {
super(props);

const decorator = new CompositeDecorator([
{
strategy: findLinkEntities,
component: Link,
},
{
strategy: findImageEntities,
component: Image,
},
]);

const sampleMarkup =
'<b>Bold text</b>, <i>Italic text</i><br/ ><br />' +
'<a href="http://www.facebook.com">Example link</a><br /><br/ >' +
'<img src="image.png" height="112" width="200" />';

const blocksFromHTML = convertFromHTML(sampleMarkup);
const state = ContentState.createFromBlockArray(
blocksFromHTML.contentBlocks,
blocksFromHTML.entityMap,
);

this.state = {
editorState: EditorState.createWithContent(
state,
decorator,
),
};

this.focus = () => this.refs.editor.focus();
this.onChange = (editorState) => this.setState({editorState});
this.logState = () => {
const content = this.state.editorState.getCurrentContent();
console.log(convertToRaw(content));
};
}

render() {
return (
<div style={styles.root}>
<div style={{marginBottom: 10}}>
Sample HTML converted into Draft content state
</div>
<div style={styles.editor} onClick={this.focus}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
ref="editor"
/>
</div>
<input
onClick={this.logState}
style={styles.button}
type="button"
value="Log State"
/>
</div>
);
}
}

function findLinkEntities(contentState, contentBlock, callback) {
contentBlock.findEntityRanges(
(character) => {
const entityKey = character.getEntity();
return (
entityKey !== null &&
contentState.getEntity(entityKey).getType() === 'LINK'
);
},
callback
);
}

const Link = (props) => {
const {url} = props.contentState.getEntity(props.entityKey).getData();
return (
<a href={url} style={styles.link}>
{props.children}
</a>
);
};

function findImageEntities(contentState, contentBlock, callback) {
contentBlock.findEntityRanges(
(character) => {
const entityKey = character.getEntity();
return (
entityKey !== null &&
contentState.getEntity(entityKey).getType() === 'IMAGE'
);
},
callback
);
}

const Image = (props) => {
const {
height,
src,
width,
} = props.contentState.getEntity(props.entityKey).getData();

return (
<img src={src} height={height} width={width} />
);
};

const styles = {
root: {
fontFamily: '\'Helvetica\', sans-serif',
padding: 20,
width: 600,
},
editor: {
border: '1px solid #ccc',
cursor: 'text',
minHeight: 80,
padding: 10,
},
button: {
marginTop: 10,
textAlign: 'center',
},
};

ReactDOM.render(
<HTMLConvertExample />,
document.getElementById('target')
);
</script>
</body>
</html>
Binary file added examples/convertFromHTML/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 39 additions & 1 deletion src/model/encoding/convertFromHTMLToContentBlocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ var anchorAttr = [
'title',
];

const imgAttr = [
'alt',
'className',
'height',
'src',
'width',
];

var lastBlock;

type Block = {
Expand Down Expand Up @@ -360,7 +368,37 @@ function genFragment(
) {
return {chunk: getBlockDividerChunk('unstyled', depth), entityMap};
}
return {chunk:getSoftNewlineChunk(), entityMap};
return {chunk: getSoftNewlineChunk(), entityMap};
}

// IMG tags
if (
nodeName === 'img' &&
node instanceof HTMLImageElement &&
node.attributes.getNamedItem('src') &&
node.attributes.getNamedItem('src').value
) {
const image: HTMLImageElement = node;
const entityConfig = {};

imgAttr.forEach((attr) => {
const imageAttribute = image.getAttribute(attr);
if (imageAttribute) {
entityConfig[attr] = imageAttribute;
}
});
const imageURI = new URI(entityConfig.src).toString();
node.textContent = imageURI; // Output src if no decorator

newEntityMap = addEntityToEntityMap(
newEntityMap,
new DraftEntityInstance({
type: 'IMAGE',
mutability: 'MUTABLE',
data: entityConfig || {},
})
);
inEntity = newEntityMap.keySeq().last();
}

var chunk = getEmptyChunk();
Expand Down

0 comments on commit 8ac72f7

Please sign in to comment.