@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
78 lines (77 loc) • 3.29 kB
JavaScript
import { GapCursorSelection, Side } from '@atlaskit/editor-common/selection/gap-cursor/selection';
/**
* A preview is rendered by the live view's own node views, which read `getPos()` against
* `view.state.doc` for both layout (`isTableNested`) and writes (`scaleTable` builds attribute
* steps at `getPos() + 1`). Handing them live positions makes the layout correct but points the
* writes at unrelated live nodes, so give them a view whose document *is* the preview instead.
*
* With `$sourcePos` (the node's position in the doc it came from) the node is swapped into a copy
* of its real top-level ancestor, so a table inside an expand still resolves as nested.
*
* Returns `null` when the node cannot be a top-level child (e.g. a bare `tableCell`), in which
* case the caller falls back to the live view and the previous stubbed positions.
*/
export function createEditorProxy(liveView, rootNode, $sourcePos) {
var liveState = liveView.state;
var createDoc = function createDoc(topLevel) {
return liveState.schema.topNodeType.createChecked(null, topLevel);
};
var previewDoc;
var rootPos = 0;
try {
var _$sourcePos$nodeAfter;
if ($sourcePos && $sourcePos.depth > 0 && ((_$sourcePos$nodeAfter = $sourcePos.nodeAfter) === null || _$sourcePos$nodeAfter === void 0 ? void 0 : _$sourcePos$nodeAfter.type) === rootNode.type) {
var topLevel = rootNode;
for (var depth = $sourcePos.depth; depth > 0; depth--) {
var ancestor = $sourcePos.node(depth);
topLevel = ancestor.copy(ancestor.content.replaceChild($sourcePos.index(depth), topLevel));
}
previewDoc = createDoc(topLevel);
rootPos = $sourcePos.pos - $sourcePos.before(1);
} else {
previewDoc = createDoc(rootNode);
}
} catch (_unused) {
return null;
}
// A gap cursor just before the node selects nothing, so the table's node view doesn't render
// as selected or show its controls.
var previewSelection = new GapCursorSelection(previewDoc.resolve(rootPos), Side.LEFT);
// Proxy rather than `EditorState.create` so plugin state is shared as-is: re-initialising
// every plugin per preview would be costly and re-runs their side effects.
var stateProxy = new Proxy(liveState, {
get: function get(target, prop, receiver) {
if (prop === 'doc') {
return previewDoc;
}
if (prop === 'selection') {
return previewSelection;
}
return Reflect.get(target, prop, receiver);
}
});
var editorProxy = new Proxy(liveView, {
get: function get(target, prop, receiver) {
if (prop === 'state') {
return stateProxy;
}
if (prop === 'dispatch') {
// Keep the transaction inside the preview. It was built against `previewDoc`, so
// applying it to the live state would corrupt the real document.
return function (tr) {
try {
stateProxy = stateProxy.apply(tr);
} catch (_unused2) {
// A preview is throwaway; a failed self-update is not worth surfacing.
}
};
}
var value = Reflect.get(target, prop, receiver);
return typeof value === 'function' ? value.bind(target) : value;
}
});
return {
editorProxy: editorProxy,
rootPos: rootPos
};
}