Commit e8c7c7fb authored by Giannis Kopanas's avatar Giannis Kopanas

fix(editor): change loading of main editor

parent ef6f9271
...@@ -112,6 +112,14 @@ class Editoria extends Component { ...@@ -112,6 +112,14 @@ class Editoria extends Component {
placeholder="Type Something..." placeholder="Type Something..."
theme="editoria" theme="editoria"
layout="editoria" layout="editoria"
fileUpload={file => {
let reader = new FileReader()
return new Promise((accept, fail) => {
reader.onload = () => accept(reader.result)
reader.onerror = () => fail(reader.error)
// Some extra delay to make the asynchronicity visible
setTimeout(() => reader.readAsDataURL(file), 1500)
})}}
value="<p>hello</p> value="<p>hello</p>
<ul><li>listItem 1</li><li>listItem 2</li><li>listItem 3</li></ul> <ul><li>listItem 1</li><li>listItem 2</li><li>listItem 3</li></ul>
<table> <table>
...@@ -119,19 +127,30 @@ class Editoria extends Component { ...@@ -119,19 +127,30 @@ class Editoria extends Component {
<tr><td>Jill</td><td>Smith</td><td>50</td></tr> <tr><td>Jill</td><td>Smith</td><td>50</td></tr>
<tr><td>Eve</td><td>Jackson</td><td>94</td></tr> <tr><td>Eve</td><td>Jackson</td><td>94</td></tr>
</table>" </table>"
renderLayout={({ editor, ...props }) => ( >
{({ editor, view, ...props }) => (
<React.Fragment> <React.Fragment>
<MainMenuBar {...props} /> <MainMenuBar view={view} {...props} />
<div className="wax-surface-container"> <div className="wax-surface-container">
<SideMenuBar {...props} /> <SideMenuBar view={view} {...props} />
{editor} {editor}
</div> </div>
</React.Fragment> </React.Fragment>
)} )}
/> </StyledWax>
</React.Fragment> </React.Fragment>
); );
} }
} }
/*
<p>hello</p>
<ul><li>listItem 1</li><li>listItem 2</li><li>listItem 3</li></ul>
<table>
<tr> <th>Firstname</th> <th>Lastname</th> <th>Age</th></tr>
<tr><td>Jill</td><td>Smith</td><td>50</td></tr>
<tr><td>Eve</td><td>Jackson</td><td>94</td></tr>
</table>
*/
export default Editoria; export default Editoria;
...@@ -13,7 +13,7 @@ const UploadImage = styled.div` ...@@ -13,7 +13,7 @@ const UploadImage = styled.div`
display: none; display: none;
} }
`; `;
const ImageUpload = ({ item, state, handle, fileUpload }) => ( const ImageUpload = ({ item, view: { state }, handle, fileUpload }) => (
<UploadImage> <UploadImage>
<label <label
className="custom-file-upload" className="custom-file-upload"
......
...@@ -32,7 +32,7 @@ const dropDownOptions = [ ...@@ -32,7 +32,7 @@ const dropDownOptions = [
{ label: "Toggle header cells", value: "toggleHeaderCell" } { label: "Toggle header cells", value: "toggleHeaderCell" }
]; ];
const TableDropDown = ({ dispatch, state, item }) => ( const TableDropDown = ({ view: { dispatch, state }, item }) => (
<DropdownStyled <DropdownStyled
options={dropDownOptions} options={dropDownOptions}
onChange={option => { onChange={option => {
......
...@@ -8,20 +8,20 @@ const ButtonStyled = styled.button` ...@@ -8,20 +8,20 @@ const ButtonStyled = styled.button`
pointer-events: ${props => (props.select ? "default" : "none")}; pointer-events: ${props => (props.select ? "default" : "none")};
`; `;
const Button = ({ dispatch, state, item }) => ( const Button = ({ view = {}, item }) => (
<ButtonStyled <ButtonStyled
type="button" type="button"
className={classnames({ className={classnames({
[classes.button]: true, [classes.button]: true,
[classes.active]: item.active && item.active(state) [classes.active]: item.active && item.active(view.state)
})} })}
title={item.title} title={item.title}
disabled={item.enable && !item.enable(state)} disabled={item.enable && !item.enable(view.state)}
onMouseDown={e => { onMouseDown={e => {
e.preventDefault(); e.preventDefault();
item.run(state, dispatch); item.run(view.state, view.dispatch);
}} }}
select={item.select && item.select(state)} select={item.select && item.select(view.state)}
> >
{item.content} {item.content}
</ButtonStyled> </ButtonStyled>
......
...@@ -24,8 +24,7 @@ const setMenuItems = (menu, menuItems) => { ...@@ -24,8 +24,7 @@ const setMenuItems = (menu, menuItems) => {
const MainMenuBar = ({ const MainMenuBar = ({
menuItems = [], menuItems = [],
children, children,
state, view,
dispatch,
className, className,
fileUpload fileUpload
}) => ( }) => (
...@@ -35,7 +34,7 @@ const MainMenuBar = ({ ...@@ -35,7 +34,7 @@ const MainMenuBar = ({
{ {
<span className={classes.group}> <span className={classes.group}>
{map(setMenuItems(MainMenuBarItems, menuItems), item => {map(setMenuItems(MainMenuBarItems, menuItems), item =>
item.menu({ state, dispatch, item, fileUpload }) item.menu({ view, item, fileUpload })
)} )}
</span> </span>
} }
......
...@@ -71,7 +71,7 @@ export default { ...@@ -71,7 +71,7 @@ export default {
enable: undo, enable: undo,
run: undo, run: undo,
select: state => true, select: state => true,
menu: props => <Button key={uuid()} {...props} /> menu: props => <Button key={uuid()} {...props} />
}, },
redo: { redo: {
title: "Redo last undone change", title: "Redo last undone change",
......
...@@ -23,8 +23,7 @@ const setMenuItems = (menu, menuItems) => { ...@@ -23,8 +23,7 @@ const setMenuItems = (menu, menuItems) => {
const SideMenuBar = ({ const SideMenuBar = ({
menuItems = [], menuItems = [],
children, children,
state, view,
dispatch,
className, className,
fileUpload fileUpload
}) => ( }) => (
...@@ -34,7 +33,7 @@ const SideMenuBar = ({ ...@@ -34,7 +33,7 @@ const SideMenuBar = ({
{ {
<span> <span>
{map(setMenuItems(SideMenuItems, menuItems), item => {map(setMenuItems(SideMenuItems, menuItems), item =>
item.menu({ state, dispatch, item, fileUpload }) item.menu({ view, item, fileUpload })
)} )}
</span> </span>
} }
......
...@@ -3,7 +3,7 @@ import debounce from "lodash/debounce"; ...@@ -3,7 +3,7 @@ import debounce from "lodash/debounce";
import { DOMParser, DOMSerializer } from "prosemirror-model"; import { DOMParser, DOMSerializer } from "prosemirror-model";
import Editor from "./Editor"; import WaxView from "./WaxView";
import defaultPlugins from "./config/defaultPlugins"; import defaultPlugins from "./config/defaultPlugins";
import placeholder from "./config/plugins/placeholder"; import placeholder from "./config/plugins/placeholder";
...@@ -72,6 +72,7 @@ class Wax extends Component { ...@@ -72,6 +72,7 @@ class Wax extends Component {
render() { render() {
const { const {
autoFocus, autoFocus,
children,
placeholder, placeholder,
renderLayout, renderLayout,
fileUpload, fileUpload,
...@@ -83,28 +84,31 @@ class Wax extends Component { ...@@ -83,28 +84,31 @@ class Wax extends Component {
theme, theme,
debug debug
} = this.props; } = this.props;
const defaultRender = ({ editor, state, dispatch, fileUpload }) => ( const defaultRender = ({ editor, state, dispatch, fileUpload }) => (
<React.Fragment>{editor}</React.Fragment> <React.Fragment>{editor}</React.Fragment>
); );
const WaxRender = renderLayout ? renderLayout : defaultRender; const WaxRender = children.length ? children : defaultRender;
const WaxLayout = layout const WaxLayout = layout
? `wax-container wax-l-${layout}` ? `wax-container wax-l-${layout}`
: "wax-container"; : "wax-container";
return ( return (
<div className={`${WaxLayout} ${className}`}> <div className={`${WaxLayout} ${className}`}>
<Editor <WaxView
autoFocus={autoFocus} autoFocus={autoFocus}
readonly={readonly} readonly={readonly}
options={this.WaxOptions} options={this.WaxOptions}
placeholder={placeholder} placeholder={placeholder}
fileUpload={fileUpload} fileUpload={fileUpload}
renderLayout={WaxRender} // renderLayout={WaxRender}
theme={theme} theme={theme}
onBlur={onBlur || (value => true)} onBlur={onBlur || (value => true)}
onChange={this.onChange || (value => true)} onChange={this.onChange || (value => true)}
debug={debug} debug={debug}
/> >
{WaxRender}
</WaxView>
</div> </div>
); );
} }
......
...@@ -8,38 +8,40 @@ import placeholderPlugin from "./config/plugins/placeholderPlugin"; ...@@ -8,38 +8,40 @@ import placeholderPlugin from "./config/plugins/placeholderPlugin";
import "prosemirror-view/style/prosemirror.css"; import "prosemirror-view/style/prosemirror.css";
import "prosemirror-gapcursor/style/gapcursor.css"; import "prosemirror-gapcursor/style/gapcursor.css";
class Editor extends Component { class WaxView extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { const {readonly, onBlur } = this.props;
state: EditorState.create(props.options)
}; this.editorRef = React.createRef()
// Create view of Editor
this.view = new EditorView(null, {
editable: () => !readonly,
state: EditorState.create(props.options),
dispatchTransaction: this.dispatchTransaction,
fileUpload: this.uploadImage,
handleDOMEvents: {
blur: onBlur
? view => {
onBlur(view.state.doc.content);
}
: null
}
});
} }
createEditorView = node => { componentDidMount() {
const { autoFocus, readonly, onBlur, debug } = this.props; const { autoFocus, debug } = this.props;
if (!this.view) { this.editorRef.current.appendChild(this.view.dom)
this.view = new EditorView(node, {
editable: () => !readonly,
state: this.state.state,
dispatchTransaction: this.dispatchTransaction,
fileUpload: this.uploadImage,
handleDOMEvents: {
blur: onBlur
? view => {
onBlur(view.state.doc.content);
}
: null
}
});
if (debug) applyDevTools(this.view); if (debug) applyDevTools(this.view);
if (autoFocus) this.view.focus(); if (autoFocus) this.view.focus();
} }
};
uploadImage = file => { uploadImage = file => {
const { state } = this.state; const { state, dispatch } = this.view;
const { findPlaceholder } = this;
const { fileUpload } = this.props; const { fileUpload } = this.props;
// A fresh object to act as the ID for this upload // A fresh object to act as the ID for this upload
...@@ -52,11 +54,12 @@ class Editor extends Component { ...@@ -52,11 +54,12 @@ class Editor extends Component {
tr.setMeta(placeholderPlugin, { tr.setMeta(placeholderPlugin, {
add: { id, pos: tr.selection.from } add: { id, pos: tr.selection.from }
}); });
this.view.dispatch(tr);
dispatch(tr);
fileUpload(file).then( fileUpload(file).then(
url => { url => {
const pos = this.findPlaceholder(this.view.state, id); const pos = findPlaceholder(this.view.state, id);
// If the content around the placeholder has been deleted, drop // If the content around the placeholder has been deleted, drop
// the image // the image
if (pos == null) { if (pos == null) {
...@@ -69,8 +72,8 @@ class Editor extends Component { ...@@ -69,8 +72,8 @@ class Editor extends Component {
.replaceWith( .replaceWith(
pos, pos,
pos, pos,
state.config.schema.nodes.image.create({ this.view.state.schema.nodes.image.create({
src: url.file src: url
}) })
) )
.setMeta(placeholderPlugin, { remove: { id } }) .setMeta(placeholderPlugin, { remove: { id } })
...@@ -78,13 +81,14 @@ class Editor extends Component { ...@@ -78,13 +81,14 @@ class Editor extends Component {
}, },
() => { () => {
// On failure, just clean up the placeholder // On failure, just clean up the placeholder
this.view.dispatch(tr.setMeta(placeholderPlugin, { remove: { id } })); dispatch(tr.setMeta(placeholderPlugin, { remove: { id } }));
} }
); );
}; };
findPlaceholder = (state, id) => { findPlaceholder = (state, id) => {
const decos = placeholderPlugin.getState(state); const decos = placeholderPlugin.getState(state);
debugger;
const found = decos.find(null, null, spec => spec.id === id); const found = decos.find(null, null, spec => spec.id === id);
return found.length ? found[0].from : null; return found.length ? found[0].from : null;
}; };
...@@ -92,7 +96,6 @@ class Editor extends Component { ...@@ -92,7 +96,6 @@ class Editor extends Component {
dispatchTransaction = transaction => { dispatchTransaction = transaction => {
const state = this.view.state.apply(transaction); const state = this.view.state.apply(transaction);
this.view.updateState(state); this.view.updateState(state);
this.setState({ state });
this.props.onChange(state.doc.content); this.props.onChange(state.doc.content);
}; };
...@@ -102,14 +105,13 @@ class Editor extends Component { ...@@ -102,14 +105,13 @@ class Editor extends Component {
? `wax-surface-scroll wax-t-${theme}` ? `wax-surface-scroll wax-t-${theme}`
: "wax-surface-scroll"; : "wax-surface-scroll";
const editor = <div ref={this.createEditorView} className={WaxTheme} />; const editor = <div ref={this.editorRef} className={WaxTheme} />;
return this.props.renderLayout({ return this.props.children({
state: this.state.state, view: this.view,
dispatch: this.dispatchTransaction,
fileUpload: this.uploadImage, fileUpload: this.uploadImage,
editor editor
}); });
} }
} }
export default Editor; export default WaxView;
import { Plugin } from "prosemirror-state"; import { Plugin, PluginKey } from "prosemirror-state";
import { Decoration, DecorationSet } from "prosemirror-view"; import { Decoration, DecorationSet } from "prosemirror-view";
const placeHolderText = new PluginKey("placeHolderText");
export default props => { export default props => {
return new Plugin({ return new Plugin({
key: placeHolderText,
props: { props: {
decorations: state => { decorations: state => {
const decorations = []; const decorations = [];
const decorate = (node, pos) => { const decorate = (node, pos) => {
if (node.type.isBlock && node.childCount === 0) { if (
node.type.isBlock &&
node.childCount === 0 &&
state.doc.content.childCount === 1
) {
decorations.push( decorations.push(
Decoration.node(pos, pos + node.nodeSize, { Decoration.node(pos, pos + node.nodeSize, {
class: "empty-node", class: "empty-node",
......
...@@ -12,7 +12,8 @@ class LinkToolTip { ...@@ -12,7 +12,8 @@ class LinkToolTip {
constructor(view) { constructor(view) {
this.tooltip = document.createElement("div"); this.tooltip = document.createElement("div");
this.tooltip.className = "tooltip"; this.tooltip.className = "tooltip";
view.dom.parentNode.appendChild(this.tooltip);
view.dom.appendChild(this.tooltip);
this.update(view, null); this.update(view, null);
} }
......
...@@ -26,7 +26,24 @@ const EditoriaSchema = { ...@@ -26,7 +26,24 @@ const EditoriaSchema = {
return brDOM; return brDOM;
} }
}, },
image: {
inline: true,
attrs: {
src: {},
alt: {default: null},
title: {default: null}
},
group: "inline",
draggable: true,
parseDOM: [{tag: "img[src]", getAttrs(dom) {
return {
src: dom.getAttribute("src"),
title: dom.getAttribute("title"),
alt: dom.getAttribute("alt")
}
}}],
toDOM(node) { let {src, alt, title} = node.attrs; return ["img", {src, alt, title}] }
},
paragraph: { paragraph: {
content: "inline*", content: "inline*",
group: "block", group: "block",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment