From edf8abbc429d7d59473394281a2212cd0460ed9a Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Thu, 29 Sep 2016 22:17:29 +0200 Subject: [PATCH] Implemented scroll sync from control pane to the preview pane. --- src/components/ControlPanel/ControlPane.js | 23 ++++--- src/components/EntryEditor/EntryEditor.js | 77 ++++++++++++++-------- src/components/PreviewPane.js | 62 +++++++++++------ 3 files changed, 106 insertions(+), 56 deletions(-) diff --git a/src/components/ControlPanel/ControlPane.js b/src/components/ControlPanel/ControlPane.js index fed6425f..939dbb24 100644 --- a/src/components/ControlPanel/ControlPane.js +++ b/src/components/ControlPanel/ControlPane.js @@ -1,23 +1,26 @@ -import React, { PropTypes } from 'react'; +import React, { Component, PropTypes } from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { resolveWidget } from '../Widgets'; import styles from './ControlPane.css'; -export default class ControlPane extends React.Component { +export default class ControlPane extends Component { + controlFor(field) { const { entry, getMedia, onChange, onAddMedia, onRemoveMedia } = this.props; const widget = resolveWidget(field.get('widget')); return (
- {React.createElement(widget.control, { - field: field, - value: entry.getIn(['data', field.get('name')]), - onChange: (value) => onChange(entry.setIn(['data', field.get('name')], value)), - onAddMedia: onAddMedia, - onRemoveMedia: onRemoveMedia, - getMedia: getMedia - })} + { + React.createElement(widget.control, { + field: field, + value: entry.getIn(['data', field.get('name')]), + onChange: (value) => onChange(entry.setIn(['data', field.get('name')], value)), + onAddMedia: onAddMedia, + onRemoveMedia: onRemoveMedia, + getMedia: getMedia + }) + }
); } diff --git a/src/components/EntryEditor/EntryEditor.js b/src/components/EntryEditor/EntryEditor.js index 9095584c..ff62080d 100644 --- a/src/components/EntryEditor/EntryEditor.js +++ b/src/components/EntryEditor/EntryEditor.js @@ -1,38 +1,63 @@ -import React, { PropTypes } from 'react'; +import React, { Component, PropTypes } from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ControlPane from '../ControlPanel/ControlPane'; import PreviewPane from '../PreviewPane'; import styles from './EntryEditor.css'; -export default function EntryEditor(props) { - const { collection, entry, getMedia, onChange, onAddMedia, onRemoveMedia, onPersist } = props; +export default class EntryEditor extends Component { - return ( -
-
-
- + state = { + scrollTop: 0, + scrollHeight: 0, + offsetHeight: 0, + } + + handleControlPaneScroll = evt => { + const { scrollTop, scrollHeight, offsetHeight } = evt.target; + this.setState({ + scrollTop, + scrollHeight, + offsetHeight, + }); + } + + render() { + const { collection, entry, getMedia, onChange, onAddMedia, onRemoveMedia, onPersist } = this.props; + const { scrollTop, scrollHeight, offsetHeight } = this.state; + + return ( +
+
+
+ +
+
+ +
-
- +
+
-
- -
-
- ); + ); + } } EntryEditor.propTypes = { diff --git a/src/components/PreviewPane.js b/src/components/PreviewPane.js index d41185db..36daa6ca 100644 --- a/src/components/PreviewPane.js +++ b/src/components/PreviewPane.js @@ -6,6 +6,7 @@ import { resolveWidget } from './Widgets'; import styles from './PreviewPane.css'; class Preview extends React.Component { + previewFor(field) { const { entry, getMedia } = this.props; const widget = resolveWidget(field.get('widget')); @@ -18,10 +19,20 @@ class Preview extends React.Component { render() { const { collection } = this.props; - if (!collection) { return null; } + if (!collection) { + return null; + } return
- {collection.get('fields').map((field) =>
{this.previewFor(field)}
)} + { + collection.get('fields').map(field => ( +
+ {this.previewFor(field)} +
+ )) + }
; } } @@ -33,19 +44,22 @@ Preview.propTypes = { }; export default class PreviewPane extends React.Component { - constructor(props) { - super(props); - this.handleIframeRef = this.handleIframeRef.bind(this); - this.widgetFor = this.widgetFor.bind(this); + + componentDidUpdate(prevProps) { + // Update scroll position of the iframe + const { scrollTop, scrollHeight, offsetHeight, ...rest } = this.props; + const frameHeight = this.iframeBody.scrollHeight - offsetHeight; + this.iframeBody.scrollTop = frameHeight * scrollTop / (scrollHeight - offsetHeight); + + // We don't want to re-render on scroll + if (prevProps.collection !== this.props.collection || prevProps.entry !== this.props.entry) { + this.renderPreview(rest); + } } - componentDidUpdate() { - this.renderPreview(); - } - - widgetFor(name) { + widgetFor = name => { const { collection, entry, getMedia } = this.props; - const field = collection.get('fields').find((field) => field.get('name') === name); + const field = collection.get('fields').find((field) => field.get('name') === name); const widget = resolveWidget(field.get('widget')); return React.createElement(widget.preview, { field: field, @@ -54,14 +68,16 @@ export default class PreviewPane extends React.Component { }); } - renderPreview() { - const props = Object.assign({}, this.props, { widgetFor: this.widgetFor }); + renderPreview(props) { const component = registry.getPreviewTemplate(props.collection.get('name')) || Preview; - - render(React.createElement(component, props), this.previewEl); + const previewProps = { + ...props, + widgetFor: this.widgetFor + }; + render(React.createElement(component, previewProps), this.previewEl); } - handleIframeRef(ref) { + handleIframeRef = ref => { if (ref) { registry.getPreviewStyles().forEach((style) => { const linkEl = document.createElement('link'); @@ -70,14 +86,17 @@ export default class PreviewPane extends React.Component { ref.contentDocument.head.appendChild(linkEl); }); this.previewEl = document.createElement('div'); - ref.contentDocument.body.appendChild(this.previewEl); - this.renderPreview(); + this.iframeBody = ref.contentDocument.body; + this.iframeBody.appendChild(this.previewEl); + this.renderPreview(this.props); } } render() { const { collection } = this.props; - if (!collection) { return null; } + if (!collection) { + return null; + } return ; } @@ -87,4 +106,7 @@ PreviewPane.propTypes = { collection: ImmutablePropTypes.map.isRequired, entry: ImmutablePropTypes.map.isRequired, getMedia: PropTypes.func.isRequired, + scrollTop: PropTypes.number, + scrollHeight: PropTypes.number, + offsetHeight: PropTypes.number, };