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

Commit 8d5cbbe

Browse files
Claudio Procidafacebook-github-bot
authored andcommitted
Adds iframed editor example (#1879)
Summary: **Summary** Adds an iframed editor example to Draft.js based on haikyuu's CodeSandbox demo https://codesandbox.io/s/y0q1q281kz **Test Plan** ``` git clone claudiopro/draft-js . cd draft-js git checkout iframe-example python3 -m http.server 8000 open http://localhost:8000/examples/draft-0-10-0/iframe/iframe.html ``` <img width="1072" alt="screen shot 2018-09-24 at 4 23 24 pm" src="https://user-images.githubusercontent.com/860099/45957816-396c1c00-c016-11e8-9808-c4453ff6c4ff.png"> Pull Request resolved: #1879 Differential Revision: D10194094 fbshipit-source-id: 0beabd7b890fbc31acdc84a780d6e91ca1de31a4
1 parent 6ba124c commit 8d5cbbe

File tree

1 file changed

+292
-0
lines changed

1 file changed

+292
-0
lines changed
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
<!--
2+
Copyright (c) 2013-present, Facebook, Inc. All rights reserved.
3+
4+
This file provided by Facebook is for non-commercial testing and evaluation
5+
purposes only. Facebook reserves all rights not expressly granted.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
10+
FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
11+
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
12+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13+
-->
14+
<!DOCTYPE html>
15+
<html>
16+
<head>
17+
<meta charset="utf-8" />
18+
<title>Draft • Iframed Editor</title>
19+
</head>
20+
<body>
21+
<div id="target"></div>
22+
<script src="../../../node_modules/react/umd/react.development.js"></script>
23+
<script src="../../../node_modules/react-dom/umd/react-dom.development.js"></script>
24+
<script src="../../../node_modules/immutable/dist/immutable.js"></script>
25+
<script src="../../../node_modules/es6-shim/es6-shim.js"></script>
26+
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.js"></script>
27+
<script src="../../../dist/Draft.js"></script>
28+
<script type="text/babel">
29+
'use strict';
30+
31+
const {Editor, EditorState, RichUtils, getDefaultKeyBinding} = Draft;
32+
33+
class IframedEditorExample extends React.Component {
34+
constructor(props) {
35+
super(props);
36+
this.state = {editorState: EditorState.createEmpty()};
37+
38+
this.focus = () => this.refs.editor.focus();
39+
this.onChange = (editorState) => this.setState({editorState});
40+
41+
this.handleKeyCommand = this._handleKeyCommand.bind(this);
42+
this.mapKeyToEditorCommand = this._mapKeyToEditorCommand.bind(this);
43+
this.toggleBlockType = this._toggleBlockType.bind(this);
44+
this.toggleInlineStyle = this._toggleInlineStyle.bind(this);
45+
}
46+
47+
_handleKeyCommand(command, editorState) {
48+
const newState = RichUtils.handleKeyCommand(editorState, command);
49+
if (newState) {
50+
this.onChange(newState);
51+
return true;
52+
}
53+
return false;
54+
}
55+
56+
_mapKeyToEditorCommand(e) {
57+
if (e.keyCode === 9 /* TAB */) {
58+
const newEditorState = RichUtils.onTab(
59+
e,
60+
this.state.editorState,
61+
4, /* maxDepth */
62+
);
63+
if (newEditorState !== this.state.editorState) {
64+
this.onChange(newEditorState);
65+
}
66+
return;
67+
}
68+
return getDefaultKeyBinding(e);
69+
}
70+
71+
_toggleBlockType(blockType) {
72+
this.onChange(
73+
RichUtils.toggleBlockType(
74+
this.state.editorState,
75+
blockType
76+
)
77+
);
78+
}
79+
80+
_toggleInlineStyle(inlineStyle) {
81+
this.onChange(
82+
RichUtils.toggleInlineStyle(
83+
this.state.editorState,
84+
inlineStyle
85+
)
86+
);
87+
}
88+
89+
render() {
90+
const {editorState} = this.state;
91+
92+
// If the user changes block type before entering any text, we can
93+
// either style the placeholder or hide it. Let's just hide it now.
94+
let className = 'RichEditor-editor';
95+
var contentState = editorState.getCurrentContent();
96+
if (!contentState.hasText()) {
97+
if (contentState.getBlockMap().first().getType() !== 'unstyled') {
98+
className += ' RichEditor-hidePlaceholder';
99+
}
100+
}
101+
102+
return (
103+
<Frame
104+
head={`
105+
<meta charset="utf-8" />
106+
<link rel="stylesheet" href="../../../dist/Draft.css" />
107+
<link rel="stylesheet" href="../rich/RichEditor.css" />
108+
`}>
109+
<div className="Editor-root">
110+
<BlockStyleControls
111+
editorState={editorState}
112+
onToggle={this.toggleBlockType}
113+
/>
114+
<InlineStyleControls
115+
editorState={editorState}
116+
onToggle={this.toggleInlineStyle}
117+
/>
118+
<div className={className} onClick={this.focus}>
119+
<Editor
120+
blockStyleFn={getBlockStyle}
121+
customStyleMap={styleMap}
122+
editorState={editorState}
123+
handleKeyCommand={this.handleKeyCommand}
124+
keyBindingFn={this.mapKeyToEditorCommand}
125+
onChange={this.onChange}
126+
placeholder="Tell a story..."
127+
ref="editor"
128+
spellCheck={true}
129+
/>
130+
</div>
131+
</div>
132+
</Frame>
133+
);
134+
}
135+
}
136+
137+
class Frame extends React.Component {
138+
constructor(props) {
139+
super(props);
140+
this.iframeRef = null;
141+
this.handleRef = this._handleRef.bind(this);
142+
}
143+
144+
_handleRef(ref) {
145+
if (ref !== this.iframeRef) {
146+
this.iframeRef = ref;
147+
if (ref) {
148+
if (ref.contentDocument && this.props.head) {
149+
ref.contentDocument.head.innerHTML = this.props.head;
150+
}
151+
// Re-render must take place in the next tick (Firefox)
152+
setTimeout(() => {
153+
this.forceUpdate();
154+
});
155+
}
156+
}
157+
}
158+
159+
render() {
160+
const ref = this.iframeRef;
161+
let portal = null;
162+
if (ref && ref.contentDocument) {
163+
portal = ReactDOM.createPortal(
164+
React.cloneElement(this.props.children, { reference: ref }),
165+
ref.contentDocument.body
166+
);
167+
}
168+
return (
169+
<div>
170+
<iframe ref={this.handleRef} style={styles.frame} />
171+
{portal}
172+
</div>
173+
);
174+
}
175+
}
176+
177+
178+
// Custom overrides for "code" style.
179+
const styleMap = {
180+
CODE: {
181+
backgroundColor: 'rgba(0, 0, 0, 0.05)',
182+
fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
183+
fontSize: 16,
184+
padding: 2,
185+
},
186+
};
187+
188+
function getBlockStyle(block) {
189+
switch (block.getType()) {
190+
case 'blockquote': return 'RichEditor-blockquote';
191+
default: return null;
192+
}
193+
}
194+
195+
class StyleButton extends React.Component {
196+
constructor() {
197+
super();
198+
this.onToggle = (e) => {
199+
e.preventDefault();
200+
this.props.onToggle(this.props.style);
201+
};
202+
}
203+
204+
render() {
205+
let className = 'RichEditor-styleButton';
206+
if (this.props.active) {
207+
className += ' RichEditor-activeButton';
208+
}
209+
210+
return (
211+
<span className={className} onMouseDown={this.onToggle}>
212+
{this.props.label}
213+
</span>
214+
);
215+
}
216+
}
217+
218+
const BLOCK_TYPES = [
219+
{label: 'H1', style: 'header-one'},
220+
{label: 'H2', style: 'header-two'},
221+
{label: 'H3', style: 'header-three'},
222+
{label: 'H4', style: 'header-four'},
223+
{label: 'H5', style: 'header-five'},
224+
{label: 'H6', style: 'header-six'},
225+
{label: 'Blockquote', style: 'blockquote'},
226+
{label: 'UL', style: 'unordered-list-item'},
227+
{label: 'OL', style: 'ordered-list-item'},
228+
{label: 'Code Block', style: 'code-block'},
229+
];
230+
231+
const BlockStyleControls = (props) => {
232+
const {editorState} = props;
233+
const selection = editorState.getSelection();
234+
const blockType = editorState
235+
.getCurrentContent()
236+
.getBlockForKey(selection.getStartKey())
237+
.getType();
238+
239+
return (
240+
<div className="RichEditor-controls">
241+
{BLOCK_TYPES.map((type) =>
242+
<StyleButton
243+
key={type.label}
244+
active={type.style === blockType}
245+
label={type.label}
246+
onToggle={props.onToggle}
247+
style={type.style}
248+
/>
249+
)}
250+
</div>
251+
);
252+
};
253+
254+
var INLINE_STYLES = [
255+
{label: 'Bold', style: 'BOLD'},
256+
{label: 'Italic', style: 'ITALIC'},
257+
{label: 'Underline', style: 'UNDERLINE'},
258+
{label: 'Monospace', style: 'CODE'},
259+
];
260+
261+
const InlineStyleControls = (props) => {
262+
const currentStyle = props.editorState.getCurrentInlineStyle();
263+
264+
return (
265+
<div className="RichEditor-controls">
266+
{INLINE_STYLES.map((type) =>
267+
<StyleButton
268+
key={type.label}
269+
active={currentStyle.has(type.style)}
270+
label={type.label}
271+
onToggle={props.onToggle}
272+
style={type.style}
273+
/>
274+
)}
275+
</div>
276+
);
277+
};
278+
279+
const styles = {
280+
frame: {
281+
height: 400,
282+
width: 800,
283+
},
284+
};
285+
286+
ReactDOM.render(
287+
<IframedEditorExample />,
288+
document.getElementById('target')
289+
);
290+
</script>
291+
</body>
292+
</html>

0 commit comments

Comments
 (0)