@atlaskit/editor-plugin-block-controls
Version:
Block controls plugin for @atlaskit/editor-core
413 lines (409 loc) • 21.7 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
import { BLOCK_CONTROL_UI_CONTEXT } from '@atlaskit/editor-common/block-controls/block-control-ui-context';
import { createSurfaceContext } from '@atlaskit/editor-ui-control-model/create-surface-context';
import { willComponentRender } from '@atlaskit/editor-ui-control-model/surface-renderer';
import { createBlockControlsSurfaceContextForPosition } from '../../ui/block-controls-surface-context';
import { hasSurfaceControls } from '../../ui/utils/has-surface-controls';
/**
* Viewport positions are post-transaction positions supplied by the DOM observer. The reducer
* never needs to inspect DOM or search document IDs for viewport discovery.
*/
var NO_ACTIVE = 'none';
var EMPTY_CANDIDATES = new Map();
var EMPTY_POSITIONS = [];
var EMPTY_PROTECTED_POSITIONS = [];
export var emptySparseSurfaceCandidateState = {
activeRevision: NO_ACTIVE,
candidates: EMPTY_CANDIDATES,
positions: EMPTY_POSITIONS,
protectedPositions: EMPTY_PROTECTED_POSITIONS,
visiblePositions: EMPTY_POSITIONS
};
var getActiveRevision = function getActiveRevision(activeNode) {
var _activeNode$rootPos, _activeNode$rootNodeT;
return activeNode ? [activeNode.pos, activeNode.nodeType, (_activeNode$rootPos = activeNode.rootPos) !== null && _activeNode$rootPos !== void 0 ? _activeNode$rootPos : activeNode.pos, (_activeNode$rootNodeT = activeNode.rootNodeType) !== null && _activeNode$rootNodeT !== void 0 ? _activeNode$rootNodeT : ''].join(':') : NO_ACTIVE;
};
var isInRange = function isInRange(position, range) {
return position >= range.from && position <= range.to;
};
var normalizeRange = function normalizeRange(range, contentSize) {
return {
from: Math.max(0, Math.min(contentSize, Math.min(range.from, range.to))),
to: Math.max(0, Math.min(contentSize, Math.max(range.from, range.to)))
};
};
var getSurfaces = function getSurfaces(resolvedSurfaces) {
return resolvedSurfaces.flatMap(function (_ref) {
var childrenMap = _ref.childrenMap,
components = _ref.components,
root = _ref.root;
return root && hasSurfaceControls(components) ? [{
childrenMap: childrenMap,
root: root
}] : [];
});
};
var getNodeId = function getNodeId(node) {
var _node$attrs;
var localId = (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.localId;
return typeof localId === 'string' ? localId : undefined;
};
var sameCandidate = function sameCandidate(first, second) {
return (first === null || first === void 0 ? void 0 : first.id) === (second === null || second === void 0 ? void 0 : second.id) && (first === null || first === void 0 ? void 0 : first.contextRevision) === (second === null || second === void 0 ? void 0 : second.contextRevision) && (first === null || first === void 0 ? void 0 : first.isPersistent) === (second === null || second === void 0 ? void 0 : second.isPersistent) && (first === null || first === void 0 ? void 0 : first.nodeRef) === (second === null || second === void 0 ? void 0 : second.nodeRef) && (first === null || first === void 0 ? void 0 : first.nodeType) === (second === null || second === void 0 ? void 0 : second.nodeType) && (first === null || first === void 0 ? void 0 : first.nodeSize) === (second === null || second === void 0 ? void 0 : second.nodeSize) && (first === null || first === void 0 ? void 0 : first.pos) === (second === null || second === void 0 ? void 0 : second.pos) && (first === null || first === void 0 ? void 0 : first.renders) === (second === null || second === void 0 ? void 0 : second.renders);
};
var sameCandidateMap = function sameCandidateMap(first, second) {
if (first.size !== second.size) {
return false;
}
var _iterator = _createForOfIteratorHelper(first),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _step$value = _slicedToArray(_step.value, 2),
position = _step$value[0],
candidate = _step$value[1];
if (!sameCandidate(candidate, second.get(position))) {
return false;
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return true;
};
var samePositions = function samePositions(first, second) {
return first.length === second.length && first.every(function (position, index) {
return position === second[index];
});
};
var normalizePositions = function normalizePositions(positions) {
return Array.from(new Set(positions)).sort(function (first, second) {
return first - second;
});
};
var mapPositions = function mapPositions(positions, tr) {
return normalizePositions(positions.map(function (position) {
return tr.docChanged ? tr.mapping.mapResult(position, 1) : {
deleted: false,
pos: position
};
}).filter(function (result) {
return !result.deleted;
}).map(function (result) {
return result.pos;
}));
};
var createSurfaceContextAtPosition = function createSurfaceContextAtPosition(state, position, activeNode) {
var blockControlsContext = createBlockControlsSurfaceContextForPosition(state, position, activeNode);
return blockControlsContext ? createSurfaceContext(BLOCK_CONTROL_UI_CONTEXT, blockControlsContext) : undefined;
};
var willAnySurfaceRenderAt = function willAnySurfaceRenderAt(surfaces, state, position, activeNode) {
var surfaceContext = createSurfaceContextAtPosition(state, position, activeNode);
return surfaces.some(function (_ref2) {
var childrenMap = _ref2.childrenMap,
root = _ref2.root;
return willComponentRender(root, childrenMap, surfaceContext);
});
};
var addCandidateAt = function addCandidateAt(_ref3) {
var candidates = _ref3.candidates,
position = _ref3.position,
state = _ref3.state,
surfaces = _ref3.surfaces,
activeNode = _ref3.activeNode;
var node = state.doc.nodeAt(position);
if (!(node !== null && node !== void 0 && node.isBlock)) {
candidates.delete(position);
return;
}
var isPersistent = willAnySurfaceRenderAt(surfaces, state, position, undefined);
var renders = isPersistent || activeNode !== undefined && willAnySurfaceRenderAt(surfaces, state, position, activeNode);
candidates.set(position, {
contextRevision: getActiveRevision(activeNode),
id: getNodeId(node),
isPersistent: isPersistent,
nodeRef: node,
nodeType: node.type.name,
nodeSize: node.nodeSize,
renders: renders,
pos: position
});
};
var isInvalidated = function isInvalidated(candidate, position, invalidation, changedRange) {
var _invalidation$ids, _invalidation$positio, _invalidation$ranges;
if (invalidation !== null && invalidation !== void 0 && invalidation.all) {
return true;
}
if (invalidation !== null && invalidation !== void 0 && (_invalidation$ids = invalidation.ids) !== null && _invalidation$ids !== void 0 && _invalidation$ids.some(function (id) {
return id === candidate.id;
})) {
return true;
}
if (invalidation !== null && invalidation !== void 0 && (_invalidation$positio = invalidation.positions) !== null && _invalidation$positio !== void 0 && _invalidation$positio.includes(position)) {
return true;
}
return Boolean(changedRange && isInRange(position, changedRange) || (invalidation === null || invalidation === void 0 || (_invalidation$ranges = invalidation.ranges) === null || _invalidation$ranges === void 0 ? void 0 : _invalidation$ranges.some(function (range) {
return isInRange(position, range);
})));
};
var getPositions = function getPositions(candidates) {
return Array.from(candidates.values()).filter(function (candidate) {
return candidate.renders;
}).map(function (candidate) {
return candidate.pos;
}).sort(function (first, second) {
return first - second;
});
};
var mapCandidates = function mapCandidates(state, tr, retain) {
var mapped = new Map();
var _iterator2 = _createForOfIteratorHelper(state.candidates.values()),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var candidate = _step2.value;
var result = tr.docChanged ? tr.mapping.mapResult(candidate.pos, 1) : {
deleted: false,
pos: candidate.pos
};
if (!result.deleted && retain(result.pos, candidate)) {
mapped.set(result.pos, result.pos === candidate.pos ? candidate : _objectSpread(_objectSpread({}, candidate), {}, {
pos: result.pos
}));
}
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
return mapped;
};
/** Map cache state during PM apply. This phase never calls isHidden or scans doc. */
export var mapSparseSurfaceCandidates = function mapSparseSurfaceCandidates(_ref4) {
var newState = _ref4.newState,
previousState = _ref4.previousState,
tr = _ref4.tr;
if (!tr.docChanged) {
return previousState;
}
var candidates = new Map();
var _iterator3 = _createForOfIteratorHelper(previousState.candidates.values()),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var candidate = _step3.value;
var result = tr.mapping.mapResult(candidate.pos, 1);
if (!result.deleted) {
candidates.set(result.pos, result.pos === candidate.pos ? candidate : _objectSpread(_objectSpread({}, candidate), {}, {
pos: result.pos
}));
}
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
var protectedPositions = normalizePositions(previousState.protectedPositions.map(function (position) {
return tr.mapping.mapResult(position, 1);
}).filter(function (result) {
return !result.deleted;
}).map(function (result) {
return result.pos;
}));
var visiblePositions = mapPositions(previousState.visiblePositions, tr);
var positions = getPositions(candidates);
var candidatesUnchanged = sameCandidateMap(previousState.candidates, candidates);
var positionsUnchanged = samePositions(previousState.positions, positions);
var protectedUnchanged = samePositions(previousState.protectedPositions, protectedPositions);
if (candidatesUnchanged && positionsUnchanged && protectedUnchanged && samePositions(previousState.visiblePositions, visiblePositions)) {
return previousState;
}
return _objectSpread(_objectSpread({}, previousState), {}, {
candidates: candidatesUnchanged ? previousState.candidates : candidates,
positions: positionsUnchanged ? previousState.positions : positions,
protectedPositions: protectedUnchanged ? previousState.protectedPositions : protectedPositions,
visiblePositions: visiblePositions
});
};
/**
* Reconcile after view state is current. Predicate work stays bounded to cache,
* exact visible positions, explicit ranges, and protected active/menu positions.
*/
export var reconcileSparseSurfaceCandidates = function reconcileSparseSurfaceCandidates(_ref5) {
var activeNodesByPosition = _ref5.activeNodesByPosition,
activeNode = _ref5.activeNode,
changedRange = _ref5.changedRange,
_ref5$documentChanged = _ref5.documentChanged,
documentChanged = _ref5$documentChanged === void 0 ? false : _ref5$documentChanged,
invalidation = _ref5.invalidation,
newState = _ref5.newState,
protectedPositions = _ref5.protectedPositions,
resolvedSurfaces = _ref5.resolvedSurfaces,
state = _ref5.state,
visiblePositions = _ref5.visiblePositions;
return reduceSparseSurfaceCandidates({
activeNode: activeNode,
meta: {
activeNodesByPosition: activeNodesByPosition,
changedRange: changedRange,
documentChanged: documentChanged,
invalidation: invalidation,
protectedPositions: protectedPositions,
visiblePositions: visiblePositions
},
newState: newState,
previousState: state,
resolvedSurfaces: resolvedSurfaces,
tr: newState.tr
});
};
/**
* Update sparse candidates. Viewport and invalidation metadata are the only
* discovery inputs; active changes re-evaluate retained cache, never document.
*/
export var reduceSparseSurfaceCandidates = function reduceSparseSurfaceCandidates(_ref6) {
var _meta$visiblePosition, _meta$protectedPositi, _meta$documentChanged, _invalidation$positio2;
var activeNode = _ref6.activeNode,
meta = _ref6.meta,
newState = _ref6.newState,
previousState = _ref6.previousState,
resolvedSurfaces = _ref6.resolvedSurfaces,
tr = _ref6.tr;
var activeRevision = getActiveRevision(activeNode);
var activeChanged = activeRevision !== previousState.activeRevision;
var hasVisiblePositionsUpdate = Boolean(meta && Object.prototype.hasOwnProperty.call(meta, 'visiblePositions'));
var visiblePositions = hasVisiblePositionsUpdate ? normalizePositions((_meta$visiblePosition = meta === null || meta === void 0 ? void 0 : meta.visiblePositions) !== null && _meta$visiblePosition !== void 0 ? _meta$visiblePosition : EMPTY_POSITIONS) : tr.docChanged ? mapPositions(previousState.visiblePositions, tr) : previousState.visiblePositions;
var visiblePositionsChanged = !samePositions(visiblePositions, previousState.visiblePositions);
var requestedProtectedPositions = normalizePositions((_meta$protectedPositi = meta === null || meta === void 0 ? void 0 : meta.protectedPositions) !== null && _meta$protectedPositi !== void 0 ? _meta$protectedPositi : previousState.protectedPositions);
var protectedPositions = samePositions(previousState.protectedPositions, requestedProtectedPositions) ? previousState.protectedPositions : requestedProtectedPositions;
var protectedSet = new Set(protectedPositions);
if (activeNode) {
var _activeNode$rootPos2;
protectedSet.add(activeNode.pos);
protectedSet.add((_activeNode$rootPos2 = activeNode.rootPos) !== null && _activeNode$rootPos2 !== void 0 ? _activeNode$rootPos2 : activeNode.pos);
}
var visibleSet = new Set(visiblePositions);
var retain = function retain(position, candidate) {
return ((candidate === null || candidate === void 0 ? void 0 : candidate.isPersistent) || protectedSet.has(position) || visibleSet.has(position)) && position >= 0 && position <= newState.doc.content.size;
};
var surfaces = getSurfaces(resolvedSurfaces);
var activeNodeAt = function activeNodeAt(position) {
var _ref7, _meta$activeNodesByPo, _meta$activeNodesByPo2;
return (_ref7 = (_meta$activeNodesByPo = meta === null || meta === void 0 || (_meta$activeNodesByPo2 = meta.activeNodesByPosition) === null || _meta$activeNodesByPo2 === void 0 ? void 0 : _meta$activeNodesByPo2.get(position)) !== null && _meta$activeNodesByPo !== void 0 ? _meta$activeNodesByPo : activeNode) !== null && _ref7 !== void 0 ? _ref7 : undefined;
};
var changedRange = meta !== null && meta !== void 0 && meta.changedRange ? normalizeRange(meta.changedRange, newState.doc.content.size) : undefined;
var invalidation = meta === null || meta === void 0 ? void 0 : meta.invalidation;
var documentChanged = (_meta$documentChanged = meta === null || meta === void 0 ? void 0 : meta.documentChanged) !== null && _meta$documentChanged !== void 0 ? _meta$documentChanged : tr.docChanged;
var previousCandidates = mapCandidates(previousState, tr, retain);
var dirty = function dirty(candidate, position) {
var currentNode = newState.doc.nodeAt(position);
var currentContextRevision = getActiveRevision(activeNodeAt(position));
return activeChanged || candidate.contextRevision !== currentContextRevision || candidate.nodeRef !== currentNode || isInvalidated(candidate, position, invalidation, changedRange);
};
if (surfaces.length === 0) {
var noCandidates = previousCandidates.size === 0;
if (noCandidates && previousState.positions.length === 0 && samePositions(visiblePositions, previousState.visiblePositions) && activeRevision === previousState.activeRevision && protectedPositions === previousState.protectedPositions) {
return previousState;
}
return {
activeRevision: activeRevision,
candidates: noCandidates ? previousState.candidates : EMPTY_CANDIDATES,
positions: EMPTY_POSITIONS,
protectedPositions: protectedPositions,
visiblePositions: visiblePositions
};
}
var _iterator4 = _createForOfIteratorHelper(previousCandidates),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var _step4$value = _slicedToArray(_step4.value, 2),
_position = _step4$value[0],
candidate = _step4$value[1];
if (dirty(candidate, _position)) {
addCandidateAt({
candidates: previousCandidates,
position: _position,
state: newState,
surfaces: surfaces,
activeNode: activeNodeAt(_position)
});
var updatedCandidate = previousCandidates.get(_position);
if (updatedCandidate && !retain(_position, updatedCandidate)) {
previousCandidates.delete(_position);
}
}
}
// IO supplies exact visible positions. Iterate those positions directly so a long visible
// container cannot cause a traversal of its entire nested document.
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
if (visiblePositionsChanged || documentChanged || invalidation !== null && invalidation !== void 0 && invalidation.all || changedRange) {
var _iterator5 = _createForOfIteratorHelper(visiblePositions),
_step5;
try {
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
var position = _step5.value;
if (!previousCandidates.has(position)) {
addCandidateAt({
candidates: previousCandidates,
position: position,
state: newState,
surfaces: surfaces,
activeNode: activeNodeAt(position)
});
}
}
} catch (err) {
_iterator5.e(err);
} finally {
_iterator5.f();
}
}
var directPositions = new Set([].concat(_toConsumableArray(protectedSet), _toConsumableArray((_invalidation$positio2 = invalidation === null || invalidation === void 0 ? void 0 : invalidation.positions) !== null && _invalidation$positio2 !== void 0 ? _invalidation$positio2 : [])));
var _iterator6 = _createForOfIteratorHelper(directPositions),
_step6;
try {
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
var _position2 = _step6.value;
if (retain(_position2) && !previousCandidates.has(_position2)) {
addCandidateAt({
candidates: previousCandidates,
position: _position2,
state: newState,
surfaces: surfaces,
activeNode: activeNodeAt(_position2)
});
}
}
} catch (err) {
_iterator6.e(err);
} finally {
_iterator6.f();
}
var positions = getPositions(previousCandidates);
if (sameCandidateMap(previousState.candidates, previousCandidates) && samePositions(previousState.positions, positions) && samePositions(previousState.visiblePositions, visiblePositions) && activeRevision === previousState.activeRevision && protectedPositions === previousState.protectedPositions) {
return previousState;
}
return {
activeRevision: activeRevision,
candidates: sameCandidateMap(previousState.candidates, previousCandidates) ? previousState.candidates : previousCandidates,
positions: samePositions(previousState.positions, positions) ? previousState.positions : positions,
protectedPositions: protectedPositions,
visiblePositions: visiblePositions
};
};