@atlaskit/editor-plugin-show-diff
Version:
ShowDiff plugin for @atlaskit/editor-core
200 lines (188 loc) • 7.55 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.NodeViewSerializer = void 0;
exports.isEditorViewWithNodeViews = isEditorViewWithNodeViews;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _model = require("@atlaskit/editor-prosemirror/model");
/**
* Utilities for working with ProseMirror node views and DOM serialization within the
* Show Diff editor plugin.
*
* This module centralizes:
* - Access to the editor's `nodeViews` registry (when available on `EditorView`)
* - Safe attempts to instantiate a node view for a given node, with a blocklist to
* avoid node types that are known to be problematic in this context (e.g. tables)
* - Schema-driven serialization of nodes and fragments to DOM via `DOMSerializer`
*
* The Show Diff decorations leverage this to either render nodes using their
* corresponding node view implementation, or fall back to DOM serialization.
*/
/**
* Narrowed `EditorView` that exposes the internal `nodeViews` registry.
* Many editor instances provide this, but it's not part of the base type.
*/
/**
* Type guard to detect whether an `EditorView` exposes a `nodeViews` map.
*/
function isEditorViewWithNodeViews(view) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return view.nodeViews !== undefined;
}
/**
* Encapsulates DOM serialization and node view access/creation.
*
* Responsible for:
* - Creating a `DOMSerializer` from the provided schema
* - Reading `nodeViews` from an `EditorView` (if present) or using an explicit mapping
* - Preventing node view creation for blocklisted node types
*/
var NodeViewSerializer = exports.NodeViewSerializer = /*#__PURE__*/function () {
function NodeViewSerializer(params) {
var _params$blocklist;
(0, _classCallCheck2.default)(this, NodeViewSerializer);
if (params !== null && params !== void 0 && params.editorView) {
this.init({
editorView: params.editorView
});
}
this.nodeViewBlocklist = new Set((_params$blocklist = params.blocklist) !== null && _params$blocklist !== void 0 ? _params$blocklist : ['paragraph']);
}
/**
* Initializes or reinitializes the NodeViewSerializer with a new EditorView.
* This allows the same serializer instance to be reused across different editor states.
*/
return (0, _createClass2.default)(NodeViewSerializer, [{
key: "init",
value: function init(params) {
var _params$editorView, _ref, _this$editorView;
this.serializer = _model.DOMSerializer.fromSchema(params.editorView.state.schema);
if (isEditorViewWithNodeViews(params.editorView)) {
this.editorView = params.editorView;
}
var nodeViews =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
((_params$editorView = params.editorView) === null || _params$editorView === void 0 ? void 0 : _params$editorView.nodeViews) || {};
this.nodeViews = (_ref = nodeViews !== null && nodeViews !== void 0 ? nodeViews : (_this$editorView = this.editorView) === null || _this$editorView === void 0 ? void 0 : _this$editorView.nodeViews) !== null && _ref !== void 0 ? _ref : {};
}
/**
* Appends serialized child nodes to the given contentDOM element.
*/
}, {
key: "appendChildNodes",
value: function appendChildNodes(children, contentDOM) {
var _this = this;
children.forEach(function (child) {
var childNode = _this.tryCreateNodeView(child) || _this.serializeNode(child);
if (childNode) {
contentDOM === null || contentDOM === void 0 || contentDOM.append(childNode);
}
});
}
/**
* Attempts to create a node view for the given node.
*
* Returns `null` when there is no `EditorView`, no constructor for the node type,
* or the node type is blocklisted. Otherwise returns the constructed node view instance.
*/
}, {
key: "tryCreateNodeView",
value: function tryCreateNodeView(targetNode) {
var _this$nodeViews;
if (!this.editorView) {
return null;
}
var constructor = (_this$nodeViews = this.nodeViews) === null || _this$nodeViews === void 0 ? void 0 : _this$nodeViews[targetNode.type.name];
if (this.nodeViewBlocklist.has(targetNode.type.name)) {
return null;
}
try {
if (!constructor) {
var _targetNode$type$spec, _targetNode$type$spec2;
if (targetNode.isInline) {
return null;
}
var toDOMResult = (_targetNode$type$spec = (_targetNode$type$spec2 = targetNode.type.spec).toDOM) === null || _targetNode$type$spec === void 0 ? void 0 : _targetNode$type$spec.call(_targetNode$type$spec2, targetNode);
if (!toDOMResult) {
return null;
}
var _DOMSerializer$render = _model.DOMSerializer.renderSpec(document, toDOMResult),
_dom = _DOMSerializer$render.dom,
_contentDOM = _DOMSerializer$render.contentDOM;
if (_dom instanceof HTMLElement) {
if (targetNode.type.name === 'paragraph' && targetNode.children.length === 1) {
return this.serializeFragment(targetNode.content);
}
// Iteratively populate children
this.appendChildNodes(targetNode.children, _contentDOM);
}
return _dom;
}
var _constructor = constructor(targetNode, this.editorView, function () {
return 0;
}, [], {}),
dom = _constructor.dom,
contentDOM = _constructor.contentDOM;
// Iteratively populate children
this.appendChildNodes(targetNode.children, contentDOM);
return dom;
} catch (e) {
return null;
}
}
/**
* Serializes a node to a DOM `Node` using the schema's `DOMSerializer`.
*/
}, {
key: "serializeNode",
value: function serializeNode(node) {
if (!this.serializer) {
throw new Error('NodeViewSerializer must be initialized with init() before use');
}
try {
return this.serializer.serializeNode(node);
} catch (e) {
return null;
}
}
/**
* Serializes a fragment to a `DocumentFragment` using the schema's `DOMSerializer`.
*/
}, {
key: "serializeFragment",
value: function serializeFragment(fragment) {
if (!this.serializer) {
throw new Error('NodeViewSerializer must be initialized with init() before use');
}
try {
return this.serializer.serializeFragment(fragment);
} catch (e) {
return null;
}
}
/**
* Returns a copy of the current node view blocklist.
*/
}, {
key: "getNodeViewBlocklist",
value: function getNodeViewBlocklist() {
return new Set(this.nodeViewBlocklist);
}
/**
* Returns a filtered copy of the node view blocklist, excluding specified node types.
* @param excludeTypes - Array of node type names to exclude from the blocklist
*/
}, {
key: "getFilteredNodeViewBlocklist",
value: function getFilteredNodeViewBlocklist(excludeTypes) {
var filtered = new Set(this.nodeViewBlocklist);
excludeTypes.forEach(function (type) {
return filtered.delete(type);
});
return filtered;
}
}]);
}();