@atlaskit/editor-plugin-history
Version:
History plugin for @atlaskit/editor-core
158 lines • 5.21 kB
JavaScript
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
import { pluginFactory } from '@atlaskit/editor-common/utils';
import { undo } from '@atlaskit/prosemirror-history/undo';
import { HistoryActionTypes } from './editor-actions/actions';
import reducer from './editor-actions/reducer';
import { getPmHistoryPluginState } from './editor-actions/utils';
import { historyPluginKey } from './pm-plugins/plugin-key';
const getInitialState = () => ({
canUndo: false,
canRedo: false
});
const {
createPluginState,
getPluginState
} = pluginFactory(historyPluginKey, reducer);
/**
* Expose prosemirror-history's `undo` as an `EditorCommand`. `undo(state, dispatch)`
* builds its own transaction, so capture that transaction and return it in place of the
* one passed in. Nothing is re-implemented: the returned transaction is exactly what
* pressing Cmd+Z would dispatch, and callers may add metadata to it before it is
* dispatched by `api.core.actions.execute`.
*/
const undoCommand = editorViewRef => ({
tr
}) => {
var _editorViewRef$curren;
const state = (_editorViewRef$curren = editorViewRef.current) === null || _editorViewRef$curren === void 0 ? void 0 : _editorViewRef$curren.state;
// The caller's `tr` is discarded, so refuse if it already carries changes or is stale.
if (!state || tr.steps.length > 0 || tr.before !== state.doc) {
return null;
}
let undoTr = null;
undo(state, built => {
undoTr = built;
});
return undoTr;
};
const createPlugin = (dispatch, editorViewRef) => new SafePlugin({
state: createPluginState(dispatch, getInitialState),
key: historyPluginKey,
view: editorView => {
editorViewRef.current = editorView;
return {
destroy: () => {
if (editorViewRef.current === editorView) {
editorViewRef.current = undefined;
}
}
};
},
appendTransaction: (transactions, oldState, newState) => {
if (transactions.find(tr => tr.docChanged && tr.getMeta('addToHistory') !== false || tr.getMeta('endHistorySlice'))) {
const pmHistoryPluginState = getPmHistoryPluginState(newState);
if (!pmHistoryPluginState) {
return;
}
const canUndo = pmHistoryPluginState.done.eventCount > 0;
const canRedo = pmHistoryPluginState.undone.eventCount > 0;
const {
canUndo: prevCanUndo,
canRedo: prevCanRedo
} = getPluginState(newState);
if (canUndo !== prevCanUndo || canRedo !== prevCanRedo) {
const action = {
type: HistoryActionTypes.UPDATE,
canUndo,
canRedo
};
return newState.tr.setMeta(historyPluginKey, action);
}
}
}
});
const historyPlugin = ({
api
}) => {
let currentId = null;
const editorViewRef = {
current: undefined
};
return {
name: 'history',
pmPlugins() {
return [{
name: 'history',
plugin: ({
dispatch
}) => createPlugin(dispatch, editorViewRef)
}];
},
getSharedState: editorState => {
var _getPmHistoryPluginSt, _done$eventCount, _undone$eventCount;
if (!editorState) {
return undefined;
}
const historyPluginState = historyPluginKey.getState(editorState);
if (!historyPluginState) {
return undefined;
}
const {
done,
undone
} = (_getPmHistoryPluginSt = getPmHistoryPluginState(editorState)) !== null && _getPmHistoryPluginSt !== void 0 ? _getPmHistoryPluginSt : {};
return {
canUndo: historyPluginState.canUndo,
canRedo: historyPluginState.canRedo,
done: {
eventCount: (_done$eventCount = done === null || done === void 0 ? void 0 : done.eventCount) !== null && _done$eventCount !== void 0 ? _done$eventCount : 0
},
undone: {
eventCount: (_undone$eventCount = undone === null || undone === void 0 ? void 0 : undone.eventCount) !== null && _undone$eventCount !== void 0 ? _undone$eventCount : 0
}
};
},
commands: {
undo: undoCommand(editorViewRef),
updatePluginState: ({
tr
}) => {
var _api$history$sharedSt;
const {
done,
undone
} = (_api$history$sharedSt = api === null || api === void 0 ? void 0 : api.history.sharedState.currentState()) !== null && _api$history$sharedSt !== void 0 ? _api$history$sharedSt : {};
if (done === undefined || undone === undefined) {
return tr;
}
const canUndo = done.eventCount > 0;
const canRedo = undone.eventCount > 0;
const action = {
type: HistoryActionTypes.UPDATE,
canUndo,
canRedo
};
return tr.setMeta(historyPluginKey, action);
},
startHistorySlice: id => ({
tr
}) => {
if (currentId) {
return null;
}
currentId = id;
return tr.setMeta('startHistorySlice', true);
},
endHistorySlice: id => ({
tr
}) => {
if (currentId !== id) {
return null;
}
currentId = null;
return tr.setMeta('endHistorySlice', true);
}
}
};
};
export default historyPlugin;