@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
512 lines (464 loc) • 12.2 kB
JavaScript
/**
* WordPress dependencies
*/
import { combineReducers } from '@wordpress/data';
/**
* Internal dependencies
*/
import { EDITOR_SETTINGS_DEFAULTS } from './defaults';
import dataviewsReducer from '../dataviews/store/reducer';
/**
* Returns a post attribute value, flattening nested rendered content using its
* raw value in place of its original object form.
*
* @param {*} value Original value.
*
* @return {*} Raw value.
*/
export function getPostRawValue( value ) {
if ( value && 'object' === typeof value && 'raw' in value ) {
return value.raw;
}
return value;
}
/**
* Returns true if the two object arguments have the same keys, or false
* otherwise.
*
* @param {Object} a First object.
* @param {Object} b Second object.
*
* @return {boolean} Whether the two objects have the same keys.
*/
export function hasSameKeys( a, b ) {
const keysA = Object.keys( a ).sort();
const keysB = Object.keys( b ).sort();
return (
keysA.length === keysB.length &&
keysA.every( ( key, index ) => keysB[ index ] === key )
);
}
/**
* Returns true if, given the currently dispatching action and the previously
* dispatched action, the two actions are editing the same post property, or
* false otherwise.
*
* @param {Object} action Currently dispatching action.
* @param {Object} previousAction Previously dispatched action.
*
* @return {boolean} Whether actions are updating the same post property.
*/
export function isUpdatingSamePostProperty( action, previousAction ) {
return (
action.type === 'EDIT_POST' &&
hasSameKeys( action.edits, previousAction.edits )
);
}
/**
* Returns true if, given the currently dispatching action and the previously
* dispatched action, the two actions are modifying the same property such that
* undo history should be batched.
*
* @param {Object} action Currently dispatching action.
* @param {Object} previousAction Previously dispatched action.
*
* @return {boolean} Whether to overwrite present state.
*/
export function shouldOverwriteState( action, previousAction ) {
if ( action.type === 'RESET_EDITOR_BLOCKS' ) {
return ! action.shouldCreateUndoLevel;
}
if ( ! previousAction || action.type !== previousAction.type ) {
return false;
}
return isUpdatingSamePostProperty( action, previousAction );
}
export function postId( state = null, action ) {
switch ( action.type ) {
case 'SET_EDITED_POST':
return action.postId;
}
return state;
}
export function templateId( state = null, action ) {
switch ( action.type ) {
case 'SET_CURRENT_TEMPLATE_ID':
return action.id;
}
return state;
}
export function postType( state = null, action ) {
switch ( action.type ) {
case 'SET_EDITED_POST':
return action.postType;
}
return state;
}
/**
* Reducer returning whether the post blocks match the defined template or not.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {boolean} Updated state.
*/
export function template( state = { isValid: true }, action ) {
switch ( action.type ) {
case 'SET_TEMPLATE_VALIDITY':
return {
...state,
isValid: action.isValid,
};
}
return state;
}
/**
* Reducer returning current network request state (whether a request to
* the WP REST API is in progress, successful, or failed).
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {Object} Updated state.
*/
export function saving( state = {}, action ) {
switch ( action.type ) {
case 'REQUEST_POST_UPDATE_START':
case 'REQUEST_POST_UPDATE_FINISH':
return {
pending: action.type === 'REQUEST_POST_UPDATE_START',
options: action.options || {},
};
}
return state;
}
/**
* Reducer returning deleting post request state.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {Object} Updated state.
*/
export function deleting( state = {}, action ) {
switch ( action.type ) {
case 'REQUEST_POST_DELETE_START':
case 'REQUEST_POST_DELETE_FINISH':
return {
pending: action.type === 'REQUEST_POST_DELETE_START',
};
}
return state;
}
/**
* Post Lock State.
*
* @typedef {Object} PostLockState
*
* @property {boolean} isLocked Whether the post is locked.
* @property {?boolean} isTakeover Whether the post editing has been taken over.
* @property {?boolean} activePostLock Active post lock value.
* @property {?Object} user User that took over the post.
*/
/**
* Reducer returning the post lock status.
*
* @param {PostLockState} state Current state.
* @param {Object} action Dispatched action.
*
* @return {PostLockState} Updated state.
*/
export function postLock( state = { isLocked: false }, action ) {
switch ( action.type ) {
case 'UPDATE_POST_LOCK':
return action.lock;
}
return state;
}
/**
* Post saving lock.
*
* When post saving is locked, the post cannot be published or updated.
*
* @param {PostLockState} state Current state.
* @param {Object} action Dispatched action.
*
* @return {PostLockState} Updated state.
*/
export function postSavingLock( state = {}, action ) {
switch ( action.type ) {
case 'LOCK_POST_SAVING':
return { ...state, [ action.lockName ]: true };
case 'UNLOCK_POST_SAVING': {
const { [ action.lockName ]: removedLockName, ...restState } =
state;
return restState;
}
}
return state;
}
/**
* Post autosaving lock.
*
* When post autosaving is locked, the post will not autosave.
*
* @param {PostLockState} state Current state.
* @param {Object} action Dispatched action.
*
* @return {PostLockState} Updated state.
*/
export function postAutosavingLock( state = {}, action ) {
switch ( action.type ) {
case 'LOCK_POST_AUTOSAVING':
return { ...state, [ action.lockName ]: true };
case 'UNLOCK_POST_AUTOSAVING': {
const { [ action.lockName ]: removedLockName, ...restState } =
state;
return restState;
}
}
return state;
}
/**
* Reducer returning the post editor setting.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {Object} Updated state.
*/
export function editorSettings( state = EDITOR_SETTINGS_DEFAULTS, action ) {
switch ( action.type ) {
case 'UPDATE_EDITOR_SETTINGS':
return {
...state,
...action.settings,
};
}
return state;
}
export function renderingMode( state = 'post-only', action ) {
switch ( action.type ) {
case 'SET_RENDERING_MODE':
return action.mode;
}
return state;
}
/**
* Reducer returning the editing canvas device type.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*
* @return {Object} Updated state.
*/
export function deviceType( state = 'Desktop', action ) {
switch ( action.type ) {
case 'SET_DEVICE_TYPE':
return action.deviceType;
}
return state;
}
/**
* Reducer storing the list of all programmatically removed panels.
*
* @param {Array} state Current state.
* @param {Object} action Action object.
*
* @return {Array} Updated state.
*/
export function removedPanels( state = [], action ) {
switch ( action.type ) {
case 'REMOVE_PANEL':
if ( ! state.includes( action.panelName ) ) {
return [ ...state, action.panelName ];
}
}
return state;
}
/**
* Reducer to set the block inserter panel open or closed.
*
* Note: this reducer interacts with the list view panel reducer
* to make sure that only one of the two panels is open at the same time.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*/
export function blockInserterPanel( state = false, action ) {
switch ( action.type ) {
case 'SET_IS_LIST_VIEW_OPENED':
return action.isOpen ? false : state;
case 'SET_IS_INSERTER_OPENED':
return action.value;
}
return state;
}
/**
* Reducer to set the list view panel open or closed.
*
* Note: this reducer interacts with the inserter panel reducer
* to make sure that only one of the two panels is open at the same time.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
*/
export function listViewPanel( state = false, action ) {
switch ( action.type ) {
case 'SET_IS_INSERTER_OPENED':
return action.value ? false : state;
case 'SET_IS_LIST_VIEW_OPENED':
return action.isOpen;
}
return state;
}
/**
* This reducer does nothing aside initializing a ref to the list view toggle.
* We will have a unique ref per "editor" instance.
*
* @param {Object} state
* @return {Object} Reference to the list view toggle button.
*/
export function listViewToggleRef( state = { current: null } ) {
return state;
}
/**
* This reducer does nothing aside initializing a ref to the inserter sidebar toggle.
* We will have a unique ref per "editor" instance.
*
* @param {Object} state
* @return {Object} Reference to the inserter sidebar toggle button.
*/
export function inserterSidebarToggleRef( state = { current: null } ) {
return state;
}
export function publishSidebarActive( state = false, action ) {
switch ( action.type ) {
case 'OPEN_PUBLISH_SIDEBAR':
return true;
case 'CLOSE_PUBLISH_SIDEBAR':
return false;
case 'TOGGLE_PUBLISH_SIDEBAR':
return ! state;
}
return state;
}
/**
* Reducer for the current global styles navigation path.
*
* @param {string} state Current state.
* @param {Object} action Dispatched action.
* @return {string} Updated state.
*/
export function stylesPath( state = '/', action ) {
switch ( action.type ) {
case 'SET_STYLES_PATH':
return action.path;
case 'RESET_STYLES_NAVIGATION':
return '/';
}
return state;
}
/**
* Reducer for whether the stylebook is visible.
*
* @param {boolean} state Current state.
* @param {Object} action Dispatched action.
* @return {boolean} Updated state.
*/
export function showStylebook( state = false, action ) {
switch ( action.type ) {
case 'SET_SHOW_STYLEBOOK':
return action.show;
case 'RESET_STYLES_NAVIGATION':
return false;
}
return state;
}
/**
* Reducer for the canvas minimum height.
*
* @param {number} state Current state.
* @param {Object} action Dispatched action.
* @return {number} Updated state.
*/
export function canvasMinHeight( state = 0, action ) {
switch ( action.type ) {
case 'SET_CANVAS_MIN_HEIGHT':
return action.minHeight;
}
return state;
}
/**
* Reducer for the revisions preview mode.
* Stores the current revision ID, or null if not in revisions mode.
*
* @param {number|null} state Current revision ID.
* @param {Object} action Dispatched action.
* @return {number|null} Updated state.
*/
export function revisionId( state = null, action ) {
switch ( action.type ) {
case 'SET_CURRENT_REVISION_ID':
return action.revisionId;
}
return state;
}
/**
* Reducer returning the currently selected note and its options.
*
* @param {Object} state Current state.
* @param {Object} action Dispatched action.
* @return {Object} Updated state.
*/
/**
* Reducer for whether the revision diff is shown.
* Resets to true when entering/exiting revisions mode.
*
* @param {boolean} state Current state.
* @param {Object} action Dispatched action.
* @return {boolean} Updated state.
*/
export function showRevisionDiff( state = true, action ) {
switch ( action.type ) {
case 'SET_SHOW_REVISION_DIFF':
return action.showDiff;
case 'SET_CURRENT_REVISION_ID':
return true; // reset on enter/exit revisions
}
return state;
}
export function selectedNote( state = {}, action ) {
switch ( action.type ) {
case 'SELECT_NOTE':
return { noteId: action.noteId, options: action.options };
}
return state;
}
export default combineReducers( {
postId,
postType,
templateId,
saving,
deleting,
postLock,
template,
postSavingLock,
editorSettings,
postAutosavingLock,
renderingMode,
deviceType,
removedPanels,
blockInserterPanel,
inserterSidebarToggleRef,
listViewPanel,
listViewToggleRef,
publishSidebarActive,
stylesPath,
showStylebook,
canvasMinHeight,
revisionId,
showRevisionDiff,
selectedNote,
dataviews: dataviewsReducer,
} );