From e2b11ba3ffa327d46c58e3797d4ebe1a3f6ecfed Mon Sep 17 00:00:00 2001 From: Daniel Lautzenheiser Date: Tue, 20 Sep 2022 09:14:34 -0400 Subject: [PATCH] Fix scrolling sync, tweak styles --- .../netlify-cms-core/src/actions/scroll.ts | 30 +++++++++ .../netlify-cms-core/src/actions/search.ts | 4 +- .../netlify-cms-core/src/actions/status.ts | 2 +- .../src/components/App/App.js | 12 ++-- .../src/components/Editor/Editor.js | 65 ++++++++++++------- .../src/components/Editor/EditorInterface.js | 56 ++++++++-------- .../src/components/UI/ErrorBoundary.js | 14 ++-- .../netlify-cms-core/src/reducers/index.ts | 2 + .../netlify-cms-core/src/reducers/scroll.ts | 28 ++++++++ packages/netlify-cms-core/src/types/redux.ts | 20 +++--- .../netlify-cms-ui-default/src/Dropdown.js | 4 +- packages/netlify-cms-ui-default/src/styles.js | 5 ++ 12 files changed, 165 insertions(+), 77 deletions(-) create mode 100644 packages/netlify-cms-core/src/actions/scroll.ts create mode 100644 packages/netlify-cms-core/src/reducers/scroll.ts diff --git a/packages/netlify-cms-core/src/actions/scroll.ts b/packages/netlify-cms-core/src/actions/scroll.ts new file mode 100644 index 00000000..b4c44a25 --- /dev/null +++ b/packages/netlify-cms-core/src/actions/scroll.ts @@ -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, _getState: () => State) => { + return dispatch(togglingScroll()); + }; +} + +export type ScrollAction = ReturnType; diff --git a/packages/netlify-cms-core/src/actions/search.ts b/packages/netlify-cms-core/src/actions/search.ts index f1064e21..180ac15c 100644 --- a/packages/netlify-cms-core/src/actions/search.ts +++ b/packages/netlify-cms-core/src/actions/search.ts @@ -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)); } }; diff --git a/packages/netlify-cms-core/src/actions/status.ts b/packages/netlify-cms-core/src/actions/status.ts index 2d171448..b66a5930 100644 --- a/packages/netlify-cms-core/src/actions/status.ts +++ b/packages/netlify-cms-core/src/actions/status.ts @@ -87,7 +87,7 @@ export function checkBackendStatus() { } dispatch(statusSuccess(status)); - } catch (error) { + } catch (error: any) { dispatch(statusFailure(error)); } }; diff --git a/packages/netlify-cms-core/src/components/App/App.js b/packages/netlify-cms-core/src/components/App/App.js index 56a1154f..81e59372 100644 --- a/packages/netlify-cms-core/src/components/App/App.js +++ b/packages/netlify-cms-core/src/components/App/App.js @@ -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 ( - + @@ -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, }; } diff --git a/packages/netlify-cms-core/src/components/Editor/Editor.js b/packages/netlify-cms-core/src/components/Editor/Editor.js index 47f3adea..284c192d 100644 --- a/packages/netlify-cms-core/src/components/Editor/Editor.js +++ b/packages/netlify-cms-core/src/components/Editor/Editor.js @@ -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))); diff --git a/packages/netlify-cms-core/src/components/Editor/EditorInterface.js b/packages/netlify-cms-core/src/components/Editor/EditorInterface.js index 27510a8e..0e104d53 100644 --- a/packages/netlify-cms-core/src/components/Editor/EditorInterface.js +++ b/packages/netlify-cms-core/src/components/Editor/EditorInterface.js @@ -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 = ( - + ); @@ -265,27 +268,25 @@ class EditorInterface extends Component { : entry; const editorWithPreview = ( - - <> - - - {editor} - - - - - - + <> + + + {editor} + + + + + ); const editorWithEditor = ( - +
{editor} @@ -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, }; diff --git a/packages/netlify-cms-core/src/components/UI/ErrorBoundary.js b/packages/netlify-cms-core/src/components/UI/ErrorBoundary.js index 239182f3..173be80a 100644 --- a/packages/netlify-cms-core/src/components/UI/ErrorBoundary.js +++ b/packages/netlify-cms-core/src/components/UI/ErrorBoundary.js @@ -174,7 +174,7 @@ export class ErrorBoundary extends React.Component { return this.props.children; } return ( - +

{t('ui.errorBoundary.title')}

{t('ui.errorBoundary.details')} @@ -190,17 +190,15 @@ export class ErrorBoundary extends React.Component {

{t('ui.errorBoundary.privacyWarning') .split('\n') - .map((item, index) => ( - <> - {item} -
- - ))} + .map((item, index) => [ + {item}, +
, + ])}


{t('ui.errorBoundary.detailsHeading')}

{errorMessage}

- {backup && showBackup && } + {backup && showBackup && }
); } diff --git a/packages/netlify-cms-core/src/reducers/index.ts b/packages/netlify-cms-core/src/reducers/index.ts index 3955ace2..3cf201e9 100644 --- a/packages/netlify-cms-core/src/reducers/index.ts +++ b/packages/netlify-cms-core/src/reducers/index.ts @@ -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; diff --git a/packages/netlify-cms-core/src/reducers/scroll.ts b/packages/netlify-cms-core/src/reducers/scroll.ts new file mode 100644 index 00000000..5f6a5322 --- /dev/null +++ b/packages/netlify-cms-core/src/reducers/scroll.ts @@ -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; diff --git a/packages/netlify-cms-core/src/types/redux.ts b/packages/netlify-cms-core/src/types/redux.ts index e00ad572..abc53a1b 100644 --- a/packages/netlify-cms-core/src/types/redux.ts +++ b/packages/netlify-cms-core/src/types/redux.ts @@ -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 { diff --git a/packages/netlify-cms-ui-default/src/Dropdown.js b/packages/netlify-cms-ui-default/src/Dropdown.js index a1331ddb..5b2e3965 100644 --- a/packages/netlify-cms-ui-default/src/Dropdown.js +++ b/packages/netlify-cms-ui-default/src/Dropdown.js @@ -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 { diff --git a/packages/netlify-cms-ui-default/src/styles.js b/packages/netlify-cms-ui-default/src/styles.js index b624f123..1a38728d 100644 --- a/packages/netlify-cms-ui-default/src/styles.js +++ b/packages/netlify-cms-ui-default/src/styles.js @@ -562,6 +562,11 @@ function GlobalStyles() { textarea { resize: none; } + + .ol-viewport { + position: absolute!important; + top: 0; + } } `} />