Fix scrolling sync, tweak styles

This commit is contained in:
Daniel Lautzenheiser 2022-09-20 09:14:34 -04:00
parent e33f3dec02
commit e2b11ba3ff
12 changed files with 165 additions and 77 deletions

View File

@ -0,0 +1,30 @@
import type { AnyAction } from 'redux';
import type { ThunkDispatch } from 'redux-thunk';
import type { State } from '../types/redux';
export const SCROLL_SYNC_ENABLED = 'cms.scroll-sync-enabled';
export const TOGGLE_SCROLL = 'TOGGLE_SCROLL';
export const SET_SCROLL = 'SET_SCROLL';
export function togglingScroll() {
return {
type: TOGGLE_SCROLL,
} as const;
}
export function loadScroll() {
return {
type: SET_SCROLL,
payload: localStorage.getItem(SCROLL_SYNC_ENABLED) !== 'false',
} as const;
}
export function toggleScroll() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return async (dispatch: ThunkDispatch<State, undefined, AnyAction>, _getState: () => State) => {
return dispatch(togglingScroll());
};
}
export type ScrollAction = ReturnType<typeof togglingScroll | typeof loadScroll>;

View File

@ -140,7 +140,7 @@ export function searchEntries(searchTerm: string, searchCollections: string[], p
try {
const response: SearchResponse = await searchPromise;
return dispatch(searchSuccess(response.entries, response.pagination));
} catch (error) {
} catch (error: any) {
return dispatch(searchFailure(error));
}
};
@ -177,7 +177,7 @@ export function query(
try {
const response: QueryResponse = await queryPromise;
return dispatch(querySuccess(namespace, response.hits));
} catch (error) {
} catch (error: any) {
return dispatch(queryFailure(error));
}
};

View File

@ -87,7 +87,7 @@ export function checkBackendStatus() {
}
dispatch(statusSuccess(status));
} catch (error) {
} catch (error: any) {
dispatch(statusFailure(error));
}
};

View File

@ -36,12 +36,12 @@ TopBarProgress.config({
const AppRoot = styled.div`
width: 100%;
min-height: 100%;
min-height: 100vh;
`;
const AppWrapper = styled.div`
width: 100%;
min-height: 100%;
min-height: 100vh;
`;
const AppMainContainer = styled.div`
@ -96,6 +96,7 @@ class App extends React.Component {
useMediaLibrary: PropTypes.bool,
openMediaLibrary: PropTypes.func.isRequired,
showMediaButton: PropTypes.bool,
scrollSyncEnabled: PropTypes.bool.isRequired,
t: PropTypes.func.isRequired,
};
@ -164,6 +165,7 @@ class App extends React.Component {
openMediaLibrary,
t,
showMediaButton,
scrollSyncEnabled,
} = this.props;
if (config === null) {
@ -186,7 +188,7 @@ class App extends React.Component {
const hasWorkflow = publishMode === EDITORIAL_WORKFLOW;
return (
<ScrollSync>
<ScrollSync enabled={scrollSyncEnabled}>
<AppRoot id="cms-root">
<AppWrapper className="cms-wrapper">
<Notifs CustomComponent={Toast} />
@ -269,12 +271,13 @@ class App extends React.Component {
}
function mapStateToProps(state) {
const { auth, config, collections, globalUI, mediaLibrary } = state;
const { auth, config, collections, globalUI, mediaLibrary, scroll } = state;
const user = auth.user;
const isFetching = globalUI.isFetching;
const publishMode = config.publish_mode;
const useMediaLibrary = !mediaLibrary.get('externalLibrary');
const showMediaButton = mediaLibrary.get('showMediaButton');
const scrollSyncEnabled = scroll.isScrolling;
return {
auth,
config,
@ -284,6 +287,7 @@ function mapStateToProps(state) {
publishMode,
showMediaButton,
useMediaLibrary,
scrollSyncEnabled,
};
}

View File

@ -1,38 +1,39 @@
import { debounce } from 'lodash';
import { Loader } from 'netlify-cms-ui-default';
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { Loader } from 'netlify-cms-ui-default';
import { translate } from 'react-polyglot';
import { debounce } from 'lodash';
import { connect } from 'react-redux';
import { history, navigateToCollection, navigateToNewEntry } from '../../routing/history';
import { logoutUser } from '../../actions/auth';
import { loadDeployPreview } from '../../actions/deploys';
import {
loadEntry,
loadEntries,
createDraftDuplicateFromEntry,
createEmptyDraft,
discardDraft,
changeDraftField,
changeDraftFieldValidation,
persistEntry,
deleteEntry,
persistLocalBackup,
loadLocalBackup,
retrieveLocalBackup,
deleteLocalBackup,
} from '../../actions/entries';
import {
updateUnpublishedEntryStatus,
deleteUnpublishedEntry,
publishUnpublishedEntry,
unpublishPublishedEntry,
deleteUnpublishedEntry,
updateUnpublishedEntryStatus,
} from '../../actions/editorialWorkflow';
import { loadDeployPreview } from '../../actions/deploys';
import { selectEntry, selectUnpublishedEntry, selectDeployPreview } from '../../reducers';
import {
changeDraftField,
changeDraftFieldValidation,
createDraftDuplicateFromEntry,
createEmptyDraft,
deleteEntry,
deleteLocalBackup,
discardDraft,
loadEntries,
loadEntry,
loadLocalBackup,
persistEntry,
persistLocalBackup,
retrieveLocalBackup,
} from '../../actions/entries';
import { loadScroll, toggleScroll } from '../../actions/scroll';
import { EDITORIAL_WORKFLOW, status } from '../../constants/publishModes';
import { selectDeployPreview, selectEntry, selectUnpublishedEntry } from '../../reducers';
import { selectFields } from '../../reducers/collections';
import { status, EDITORIAL_WORKFLOW } from '../../constants/publishModes';
import { history, navigateToCollection, navigateToNewEntry } from '../../routing/history';
import EditorInterface from './EditorInterface';
import withWorkflow from './withWorkflow';
@ -79,6 +80,9 @@ export class Editor extends React.Component {
loadLocalBackup: PropTypes.func,
persistLocalBackup: PropTypes.func.isRequired,
deleteLocalBackup: PropTypes.func,
toggleScroll: PropTypes.func.isRequired,
scrollSyncEnabled: PropTypes.bool.isRequired,
loadScroll: PropTypes.func.isRequired,
};
componentDidMount() {
@ -356,6 +360,9 @@ export class Editor extends React.Component {
slug,
t,
editorBackLink,
toggleScroll,
scrollSyncEnabled,
loadScroll,
} = this.props;
const isPublished = !newEntry && !unpublishedEntry;
@ -405,6 +412,9 @@ export class Editor extends React.Component {
deployPreview={deployPreview}
loadDeployPreview={opts => loadDeployPreview(collection, slug, entry, isPublished, opts)}
editorBackLink={editorBackLink}
toggleScroll={toggleScroll}
scrollSyncEnabled={scrollSyncEnabled}
loadScroll={loadScroll}
t={t}
/>
);
@ -412,7 +422,7 @@ export class Editor extends React.Component {
}
function mapStateToProps(state, ownProps) {
const { collections, entryDraft, auth, config, entries, globalUI } = state;
const { collections, entryDraft, auth, config, entries, globalUI, scroll } = state;
const slug = ownProps.match.params[0];
const collection = collections.get(ownProps.match.params.name);
const collectionName = collection.get('name');
@ -444,6 +454,8 @@ function mapStateToProps(state, ownProps) {
}
}
const scrollSyncEnabled = scroll.isScrolling;
return {
collection,
collections,
@ -466,6 +478,7 @@ function mapStateToProps(state, ownProps) {
publishedEntry,
unPublishedEntry,
editorBackLink,
scrollSyncEnabled,
};
}
@ -489,6 +502,8 @@ const mapDispatchToProps = {
unpublishPublishedEntry,
deleteUnpublishedEntry,
logoutUser,
toggleScroll,
loadScroll,
};
export default connect(mapStateToProps, mapDispatchToProps)(withWorkflow(translate()(Editor)));

View File

@ -21,7 +21,6 @@ import { FILES } from '../../constants/collectionTypes';
import { getFileFromSlug } from '../../reducers/collections';
const PREVIEW_VISIBLE = 'cms.preview-visible';
const SCROLL_SYNC_ENABLED = 'cms.scroll-sync-enabled';
const I18N_VISIBLE = 'cms.i18n-visible';
const styles = {
@ -103,6 +102,7 @@ const Editor = styled.div`
height: 100%;
margin: 0 auto;
position: relative;
background-color: ${colorsRaw.white};
`;
const PreviewPaneContainer = styled.div`
@ -152,10 +152,15 @@ function isPreviewEnabled(collection, entry) {
class EditorInterface extends Component {
state = {
previewVisible: localStorage.getItem(PREVIEW_VISIBLE) !== 'false',
scrollSyncEnabled: localStorage.getItem(SCROLL_SYNC_ENABLED) !== 'false',
i18nVisible: localStorage.getItem(I18N_VISIBLE) !== 'false',
};
constructor(props) {
super(props);
this.props.loadScroll();
}
handleOnPersist = async (opts = {}) => {
const { createNew = false, duplicate = false } = opts;
await this.controlPaneRef.switchToDefaultLocale();
@ -177,9 +182,8 @@ class EditorInterface extends Component {
};
handleToggleScrollSync = () => {
const newScrollSyncEnabled = !this.state.scrollSyncEnabled;
this.setState({ scrollSyncEnabled: newScrollSyncEnabled });
localStorage.setItem(SCROLL_SYNC_ENABLED, newScrollSyncEnabled);
const { toggleScroll } = this.props;
toggleScroll();
};
handleToggleI18n = () => {
@ -222,11 +226,10 @@ class EditorInterface extends Component {
deployPreview,
draftKey,
editorBackLink,
scrollSyncEnabled,
t,
} = this.props;
const { scrollSyncEnabled } = this.state;
const previewEnabled = isPreviewEnabled(collection, entry);
const collectionI18nEnabled = hasI18n(collection);
@ -255,7 +258,7 @@ class EditorInterface extends Component {
);
const editor2 = (
<ControlPaneContainer overFlow={!this.state.scrollSyncEnabled}>
<ControlPaneContainer overFlow={!this.props.scrollSyncEnabled}>
<EditorControlPane {...editorProps} locale={locales?.[1]} t={t} />
</ControlPaneContainer>
);
@ -265,27 +268,25 @@ class EditorInterface extends Component {
: entry;
const editorWithPreview = (
<ScrollSync enabled={this.state.scrollSyncEnabled}>
<>
<ReactSplitPaneGlobalStyles />
<StyledSplitPane>
<ScrollSyncPane>{editor}</ScrollSyncPane>
<PreviewPaneContainer>
<EditorPreviewPane
collection={collection}
entry={previewEntry}
fields={fields}
fieldsMetaData={fieldsMetaData}
locale={leftPanelLocale}
/>
</PreviewPaneContainer>
</StyledSplitPane>
</>
</ScrollSync>
<>
<ReactSplitPaneGlobalStyles />
<StyledSplitPane>
<ScrollSyncPane>{editor}</ScrollSyncPane>
<PreviewPaneContainer>
<EditorPreviewPane
collection={collection}
entry={previewEntry}
fields={fields}
fieldsMetaData={fieldsMetaData}
locale={leftPanelLocale}
/>
</PreviewPaneContainer>
</StyledSplitPane>
</>
);
const editorWithEditor = (
<ScrollSync enabled={this.state.scrollSyncEnabled}>
<ScrollSync enabled={this.props.scrollSyncEnabled}>
<div>
<StyledSplitPane>
<ScrollSyncPane>{editor}</ScrollSyncPane>
@ -406,6 +407,9 @@ EditorInterface.propTypes = {
deployPreview: PropTypes.object,
loadDeployPreview: PropTypes.func.isRequired,
draftKey: PropTypes.string.isRequired,
toggleScroll: PropTypes.func.isRequired,
scrollSyncEnabled: PropTypes.bool.isRequired,
loadScroll: PropTypes.func.isRequired,
t: PropTypes.func.isRequired,
};

View File

@ -174,7 +174,7 @@ export class ErrorBoundary extends React.Component {
return this.props.children;
}
return (
<ErrorBoundaryContainer>
<ErrorBoundaryContainer key="error-boundary-container">
<h1>{t('ui.errorBoundary.title')}</h1>
<p>
<span>{t('ui.errorBoundary.details')}</span>
@ -190,17 +190,15 @@ export class ErrorBoundary extends React.Component {
<p>
{t('ui.errorBoundary.privacyWarning')
.split('\n')
.map((item, index) => (
<>
<PrivacyWarning key={index}>{item}</PrivacyWarning>
<br />
</>
))}
.map((item, index) => [
<PrivacyWarning key={`private-warning-${index}`}>{item}</PrivacyWarning>,
<br key={`break-${index}`} />,
])}
</p>
<hr />
<h2>{t('ui.errorBoundary.detailsHeading')}</h2>
<p>{errorMessage}</p>
{backup && showBackup && <RecoveredEntry entry={backup} t={t} />}
{backup && showBackup && <RecoveredEntry key="backup" entry={backup} t={t} />}
</ErrorBoundaryContainer>
);
}

View File

@ -14,6 +14,7 @@ import mediaLibrary from './mediaLibrary';
import deploys, * as fromDeploys from './deploys';
import globalUI from './globalUI';
import status from './status';
import scroll from './scroll';
import type { Status } from '../constants/publishModes';
import type { State, Collection } from '../types/redux';
@ -33,6 +34,7 @@ const reducers = {
deploys,
globalUI,
status,
scroll,
};
export default reducers;

View File

@ -0,0 +1,28 @@
import { produce } from 'immer';
import { SCROLL_SYNC_ENABLED, SET_SCROLL, TOGGLE_SCROLL } from '../actions/scroll';
import type { ScrollAction } from '../actions/scroll';
export type ScrollState = {
isScrolling: boolean;
};
const defaultState: ScrollState = {
isScrolling: true,
};
const status = produce((state: ScrollState, action: ScrollAction) => {
switch (action.type) {
case TOGGLE_SCROLL:
state.isScrolling = !state.isScrolling;
localStorage.setItem(SCROLL_SYNC_ENABLED, `${state.isScrolling}`);
break;
case SET_SCROLL:
state.isScrolling = action.payload;
localStorage.setItem(SCROLL_SYNC_ENABLED, `${state.isScrolling}`);
break;
}
}, defaultState);
export default status;

View File

@ -1,15 +1,16 @@
import type { List, Map, OrderedMap, Set } from 'immutable';
import type { Action } from 'redux';
import type { StaticallyTypedRecord } from './immutable';
import type { Map, List, OrderedMap, Set } from 'immutable';
import type { FILES, FOLDER } from '../constants/collectionTypes';
import type { MediaFile as BackendMediaFile } from '../backend';
import type { Auth } from '../reducers/auth';
import type { Status } from '../reducers/status';
import type { Medias } from '../reducers/medias';
import type { Deploys } from '../reducers/deploys';
import type { Search } from '../reducers/search';
import type { GlobalUI } from '../reducers/globalUI';
import type { FILES, FOLDER } from '../constants/collectionTypes';
import type { formatExtensions } from '../formats/formats';
import type { Auth } from '../reducers/auth';
import type { Deploys } from '../reducers/deploys';
import type { GlobalUI } from '../reducers/globalUI';
import type { Medias } from '../reducers/medias';
import type { ScrollState } from '../reducers/scroll';
import type { Search } from '../reducers/search';
import type { Status } from '../reducers/status';
import type { StaticallyTypedRecord } from './immutable';
export type CmsBackendType =
| 'azure'
@ -699,6 +700,7 @@ export interface State {
search: Search;
notifs: { message: { key: string }; kind: string; id: number }[];
status: Status;
scroll: ScrollState;
}
export interface Integration {

View File

@ -17,8 +17,8 @@ const StyledDropdownButton = styled(DropdownButton)`
${buttons.button};
${buttons.default};
display: block;
padding-left: 20px;
padding-right: 40px;
padding-left: 20px!important;
padding-right: 40px!important;
position: relative;
&:after {

View File

@ -562,6 +562,11 @@ function GlobalStyles() {
textarea {
resize: none;
}
.ol-viewport {
position: absolute!important;
top: 0;
}
}
`}
/>