UNPKG

@atlaskit/editor-plugin-block-controls

Version:

Block controls plugin for @atlaskit/editor-core

413 lines (409 loc) 21.7 kB
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 }; };