@atlaskit/editor-plugin-block-controls
Version:
Block controls plugin for @atlaskit/editor-core
121 lines • 7.46 kB
JavaScript
import { BLOCK_CONTROLS_LEFT_SURFACE, BLOCK_CONTROLS_RIGHT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
import { getNodeIdProvider } from '@atlaskit/editor-common/node-anchor';
import { resolveSurface } from '@atlaskit/editor-ui-control-model/surface-renderer';
import { createSurfaceAnchorDecorations } from '../decorations-surface-anchor';
import { key as blockControlsKey } from '../main';
import { reconcileSparseSurfaceCandidates } from './sparse-surface-candidates';
import { observeSurfaceViewport } from './surface-viewport';
const haveAnchorPositionsChanged = (positions, anchors) => positions.length !== anchors.size || positions.some(position => !anchors.has(position));
export const createSparseSurfacesView = ({
api,
pluginKey,
view
}) => {
var _api$uiControlRegistr3, _api$editorDisabled, _api$editorViewMode, _api$limitedMode2, _api$showDiff, _api$userIntent;
const win = view.dom.ownerDocument.defaultView;
let frame;
let visiblePositions = [];
let registryChanged = true;
const reconcile = () => {
var _api$limitedMode, _api$limitedMode$shar;
frame = undefined;
const previous = pluginKey.getState(view.state);
if (!previous || api !== null && api !== void 0 && (_api$limitedMode = api.limitedMode) !== null && _api$limitedMode !== void 0 && (_api$limitedMode$shar = _api$limitedMode.sharedState.currentState()) !== null && _api$limitedMode$shar !== void 0 && _api$limitedMode$shar.enabled) {
return;
}
const controls = blockControlsKey.getState(view.state);
const activeNode = controls === null || controls === void 0 ? void 0 : controls.activeNode;
const activeNodesByPosition = new Map();
const menu = controls !== null && controls !== void 0 && controls.isMenuOpen ? controls.menuTriggerByNode : undefined;
if (menu && controls !== null && controls !== void 0 && controls.menuTriggerBy && view.state.doc.nodeAt(menu.pos)) {
var _menu$rootPos;
const menuNode = {
...menu,
anchorName: controls.menuTriggerBy
};
activeNodesByPosition.set(menu.pos, menuNode);
activeNodesByPosition.set((_menu$rootPos = menu.rootPos) !== null && _menu$rootPos !== void 0 ? _menu$rootPos : menu.pos, menuNode);
}
const resolvedSurfaces = [BLOCK_CONTROLS_LEFT_SURFACE, BLOCK_CONTROLS_RIGHT_SURFACE].map(surface => {
var _api$uiControlRegistr, _api$uiControlRegistr2;
return resolveSurface((_api$uiControlRegistr = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(surface)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [], surface);
});
const candidates = reconcileSparseSurfaceCandidates({
activeNode,
activeNodesByPosition,
newState: view.state,
state: previous.candidates,
visiblePositions,
protectedPositions: [...activeNodesByPosition.keys()],
resolvedSurfaces,
documentChanged: previous.documentChanged,
invalidation: {
...previous.invalidation,
all: registryChanged
}
});
const visibilityChanged = registryChanged;
registryChanged = false;
const anchorPositions = candidates.positions;
if (!visibilityChanged && !haveAnchorPositionsChanged(anchorPositions, previous.anchors) && candidates === previous.candidates && !previous.documentChanged && !previous.invalidation) {
return;
}
const nodeIdProvider = getNodeIdProvider(view);
const {
anchors,
decorations
} = createSurfaceAnchorDecorations(view.state, anchorPositions, (node, position, previousAnchorName) => {
if (previousAnchorName) {
nodeIdProvider.setIdForNode(node, previousAnchorName);
return previousAnchorName;
}
return nodeIdProvider.getOrGenerateId(node, position);
}, previous.anchors);
view.dispatch(view.state.tr.setMeta(pluginKey, {
reconciliation: {
activeNodesByPosition,
anchors,
candidates,
decorations
},
type: 'commitReconciliation'
}));
};
const schedule = () => {
if (frame === undefined && win) {
frame = win.requestAnimationFrame(reconcile);
}
};
const observer = observeSurfaceViewport(view, positions => {
visiblePositions = positions;
schedule();
});
const invalidate = () => {
registryChanged = true;
schedule();
};
const unsubscribe = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr3 = api.uiControlRegistry) === null || _api$uiControlRegistr3 === void 0 ? void 0 : _api$uiControlRegistr3.actions.subscribe(invalidate);
const unsubscribers = [api === null || api === void 0 ? void 0 : (_api$editorDisabled = api.editorDisabled) === null || _api$editorDisabled === void 0 ? void 0 : _api$editorDisabled.sharedState.onChange(invalidate), api === null || api === void 0 ? void 0 : (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.sharedState.onChange(invalidate), api === null || api === void 0 ? void 0 : (_api$limitedMode2 = api.limitedMode) === null || _api$limitedMode2 === void 0 ? void 0 : _api$limitedMode2.sharedState.onChange(invalidate), api === null || api === void 0 ? void 0 : (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 ? void 0 : _api$showDiff.sharedState.onChange(invalidate), api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.sharedState.onChange(invalidate)];
return {
update(currentView, previousState) {
observer.update();
const sparse = pluginKey.getState(currentView.state);
const controls = blockControlsKey.getState(currentView.state);
const previousControls = blockControlsKey.getState(previousState);
if ((controls === null || controls === void 0 ? void 0 : controls.isDragging) !== (previousControls === null || previousControls === void 0 ? void 0 : previousControls.isDragging) || (controls === null || controls === void 0 ? void 0 : controls.isResizerResizing) !== (previousControls === null || previousControls === void 0 ? void 0 : previousControls.isResizerResizing)) {
invalidate();
}
if (sparse !== null && sparse !== void 0 && sparse.documentChanged || sparse !== null && sparse !== void 0 && sparse.invalidation || (controls === null || controls === void 0 ? void 0 : controls.activeNode) !== (previousControls === null || previousControls === void 0 ? void 0 : previousControls.activeNode) || (controls === null || controls === void 0 ? void 0 : controls.isMenuOpen) !== (previousControls === null || previousControls === void 0 ? void 0 : previousControls.isMenuOpen) || (controls === null || controls === void 0 ? void 0 : controls.menuTriggerByNode) !== (previousControls === null || previousControls === void 0 ? void 0 : previousControls.menuTriggerByNode)) {
schedule();
}
},
destroy() {
if (frame !== undefined) {
win === null || win === void 0 ? void 0 : win.cancelAnimationFrame(frame);
}
observer.destroy();
unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
unsubscribers.forEach(unsubscribe => unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe());
}
};
};