use Context for sticky, support multiple stickies
This commit is contained in:
parent
ec29a04089
commit
fc5d935e53
@ -6,6 +6,7 @@ import { ScrollSync, ScrollSyncPane } from '../ScrollSync';
|
||||
import ControlPane from '../ControlPanel/ControlPane';
|
||||
import PreviewPane from '../PreviewPane/PreviewPane';
|
||||
import Toolbar from './EntryEditorToolbar';
|
||||
import { StickyContext } from '../UI/Sticky/Sticky';
|
||||
import styles from './EntryEditor.css';
|
||||
import stickyStyles from '../UI/Sticky/Sticky.css';
|
||||
|
||||
@ -36,35 +37,6 @@ class EntryEditor extends Component {
|
||||
localStorage.setItem(PREVIEW_VISIBLE, newPreviewVisible);
|
||||
};
|
||||
|
||||
setSticky = (contextTop, containerRect, sticky) => {
|
||||
if (contextTop >= containerRect.top) {
|
||||
if (contextTop < containerRect.bottom - 60) {
|
||||
sticky.classList.add(stickyStyles.top);
|
||||
sticky.classList.remove(stickyStyles.bottom);
|
||||
} else if (contextTop >= containerRect.bottom - 60) {
|
||||
sticky.classList.remove(stickyStyles.top);
|
||||
sticky.classList.add(stickyStyles.bottom);
|
||||
}
|
||||
} else {
|
||||
sticky.classList.remove(stickyStyles.top);
|
||||
sticky.classList.remove(stickyStyles.bottom);
|
||||
}
|
||||
};
|
||||
|
||||
handleControlPaneRef = (ref) => {
|
||||
const sticky = ref.querySelector('.cms__index__editorControlBar');
|
||||
const stickyContainer = ref.querySelector('.stickyContainer');
|
||||
stickyContainer.style.paddingTop = `${ sticky.offsetHeight }px`;
|
||||
sticky.style.position = 'absolute';
|
||||
sticky.style.top = `${ -sticky.offsetHeight }px`;
|
||||
sticky.style.width = `${ stickyContainer.getBoundingClientRect().width }px`;
|
||||
|
||||
ref && ref.addEventListener('scroll', (e) => {
|
||||
const contextTop = e.target.getBoundingClientRect().top;
|
||||
this.setSticky(contextTop, stickyContainer.getBoundingClientRect(), sticky);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
collection,
|
||||
@ -90,7 +62,7 @@ class EntryEditor extends Component {
|
||||
);
|
||||
|
||||
const editor = (
|
||||
<div className={controlClassName} ref={this.handleControlPaneRef}>
|
||||
<StickyContext className={controlClassName}>
|
||||
{ collectionPreviewEnabled ? togglePreviewButton : null }
|
||||
<ControlPane
|
||||
collection={collection}
|
||||
@ -105,7 +77,7 @@ class EntryEditor extends Component {
|
||||
onRemoveAsset={onRemoveAsset}
|
||||
ref={c => this.controlPaneRef = c} // eslint-disable-line
|
||||
/>
|
||||
</div>
|
||||
</StickyContext>
|
||||
);
|
||||
|
||||
const editorWithPreview = (
|
||||
|
@ -1,9 +1,18 @@
|
||||
.top {
|
||||
.stickyContainer {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.sticky {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.stickyActive:not(.stickyAtBottom) {
|
||||
position: fixed !important;
|
||||
top: 64px !important;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
.stickyAtBottom {
|
||||
position: absolute !important;
|
||||
top: auto !important;
|
||||
bottom: 30px !important;
|
||||
}
|
||||
|
116
src/components/UI/Sticky/Sticky.js
Normal file
116
src/components/UI/Sticky/Sticky.js
Normal file
@ -0,0 +1,116 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import styles from './Sticky.css';
|
||||
|
||||
export class StickyContext extends Component {
|
||||
static childContextTypes = { subscribeToStickyContext: PropTypes.func };
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.subscriptions = [];
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
return { subscribeToStickyContext: (fn) => { this.subscriptions.push(fn); } };
|
||||
}
|
||||
|
||||
updateStickies = (ref) => {
|
||||
const stickyContextTop = ref.getBoundingClientRect().top;
|
||||
this.subscriptions.forEach((fn) => { fn(stickyContextTop); });
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.updateStickies(this.ref);
|
||||
}
|
||||
|
||||
handleScroll = (event) => {
|
||||
this.updateStickies(event.target);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={this.props.className} onScroll={this.handleScroll} ref={(ref) => { this.ref = ref; }}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class StickyContainer extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.subscriptions = [];
|
||||
}
|
||||
|
||||
static contextTypes = { subscribeToStickyContext: PropTypes.func };
|
||||
static childContextTypes = { subscribeToStickyContainer: PropTypes.func };
|
||||
|
||||
getChildContext() {
|
||||
return { subscribeToStickyContainer: (fn) => { this.subscriptions.push(fn); } };
|
||||
}
|
||||
|
||||
getPosition = (contextTop) => {
|
||||
const rect = this.ref.getBoundingClientRect();
|
||||
const shouldStick = rect.top < contextTop;
|
||||
const shouldStickAtBottom = rect.bottom - 60 < contextTop;
|
||||
this.subscriptions.forEach((fn) => { fn(shouldStick, shouldStickAtBottom, rect.width) });
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.context.subscribeToStickyContext(this.getPosition);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
id={this.context.string}
|
||||
className={classnames(this.props.className, styles.stickyContainer)}
|
||||
ref={(ref) => { this.ref = ref }}
|
||||
>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class Sticky extends Component {
|
||||
static contextTypes = { subscribeToStickyContainer: PropTypes.func };
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
updateSticky = (shouldStick, shouldStickAtBottom, containerWidth) => {
|
||||
this.setState({ shouldStick, shouldStickAtBottom, containerWidth });
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.context.subscribeToStickyContainer(this.updateSticky);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { props, state } = this;
|
||||
const stickyPlaceholderHeight = state.shouldStick ? this.ref.getBoundingClientRect().height : 0;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{paddingBottom: stickyPlaceholderHeight}}></div>
|
||||
<div
|
||||
className={classnames(
|
||||
props.className,
|
||||
styles.sticky,
|
||||
{
|
||||
[styles.stickyActive]: state.shouldStick,
|
||||
[styles.stickyAtBottom]: state.shouldStickAtBottom,
|
||||
},
|
||||
)}
|
||||
style={props.fillContainerWidth && state.containerWidth ? { width: state.containerWidth } : null}
|
||||
ref={(ref) => {this.ref = ref}}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ import registry from '../../lib/registry';
|
||||
import RawEditor from './MarkdownControlElements/RawEditor';
|
||||
import VisualEditor from './MarkdownControlElements/VisualEditor';
|
||||
import { processEditorPlugins } from './richText';
|
||||
import { StickyContainer } from '../UI/Sticky/Sticky';
|
||||
|
||||
const MODE_STORAGE_KEY = 'cms.md-mode';
|
||||
|
||||
@ -32,23 +33,20 @@ export default class MarkdownControl extends React.Component {
|
||||
render() {
|
||||
const { onChange, onAddAsset, onRemoveAsset, getAsset, value } = this.props;
|
||||
const { mode } = this.state;
|
||||
if (mode === 'visual') {
|
||||
return (
|
||||
<div className="cms-editor-visual stickyContainer">
|
||||
<VisualEditor
|
||||
onChange={onChange}
|
||||
onAddAsset={onAddAsset}
|
||||
onRemoveAsset={onRemoveAsset}
|
||||
onMode={this.handleMode}
|
||||
getAsset={getAsset}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="cms-editor-raw stickyContainer">
|
||||
const visualEditor = (
|
||||
<div className="cms-editor-visual">
|
||||
<VisualEditor
|
||||
onChange={onChange}
|
||||
onAddAsset={onAddAsset}
|
||||
onRemoveAsset={onRemoveAsset}
|
||||
onMode={this.handleMode}
|
||||
getAsset={getAsset}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
const rawEditor = (
|
||||
<div className="cms-editor-raw">
|
||||
<RawEditor
|
||||
onChange={onChange}
|
||||
onAddAsset={onAddAsset}
|
||||
@ -60,5 +58,6 @@ export default class MarkdownControl extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
return <StickyContainer>{ mode === 'visual' ? visualEditor : rawEditor }</StickyContainer>;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import registry from '../../../../lib/registry';
|
||||
import { createAssetProxy } from '../../../../valueObjects/AssetProxy';
|
||||
import Toolbar from '../Toolbar';
|
||||
import ToolbarPlugins from '../ToolbarPlugins';
|
||||
import { Sticky } from '../../../UI/Sticky/Sticky';
|
||||
import styles from './index.css';
|
||||
|
||||
const HAS_LINE_BREAK = /\n/m;
|
||||
@ -319,7 +320,7 @@ export default class RawEditor extends React.Component {
|
||||
onDragOver={this.handleDragOver}
|
||||
onDrop={this.handleDrop}
|
||||
>
|
||||
<div className={styles.editorControlBar}>
|
||||
<Sticky className={styles.editorControlBar} fillContainerWidth={true}>
|
||||
<Toolbar
|
||||
selectionPosition={selectionPosition}
|
||||
onH1={this.handleHeader('#')}
|
||||
@ -339,7 +340,7 @@ export default class RawEditor extends React.Component {
|
||||
getAsset={getAsset}
|
||||
rawMode
|
||||
/>
|
||||
</div>
|
||||
</Sticky>
|
||||
<textarea
|
||||
ref={this.handleRef}
|
||||
value={this.props.value || ''}
|
||||
|
@ -4,6 +4,7 @@
|
||||
background-color: var(--controlBGColor);
|
||||
border-bottom: 1px solid var(--backgroundTertiaryColorDark);
|
||||
border-radius: var(--borderRadius) var(--borderRadius) 0 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.editor {
|
||||
|
@ -17,6 +17,7 @@ import { buildKeymap } from './keymap';
|
||||
import createMarkdownParser from './parser';
|
||||
import Toolbar from '../Toolbar';
|
||||
import ToolbarPlugins from '../ToolbarPlugins';
|
||||
import { Sticky } from '../../../UI/Sticky/Sticky';
|
||||
import styles from './index.css';
|
||||
|
||||
function processUrl(url) {
|
||||
@ -275,7 +276,7 @@ export default class Editor extends Component {
|
||||
onDragOver={this.handleDragOver}
|
||||
onDrop={this.handleDrop}
|
||||
>
|
||||
<div className={styles.editorControlBar}>
|
||||
<Sticky className={styles.editorControlBar} fillContainerWidth={true}>
|
||||
<Toolbar
|
||||
selectionPosition={selectionPosition}
|
||||
onH1={this.handleHeader(1)}
|
||||
@ -293,7 +294,7 @@ export default class Editor extends Component {
|
||||
onRemoveAsset={onRemoveAsset}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
</div>
|
||||
</Sticky>
|
||||
<div ref={this.handleRef} />
|
||||
<div className={styles.shim} />
|
||||
</div>);
|
||||
|
Loading…
x
Reference in New Issue
Block a user