@activecollab/components
Version:
ActiveCollab Components
118 lines (114 loc) • 5.37 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
const _excluded = ["content", "components", "space"];
import React, { Fragment, forwardRef, useMemo } from "react";
import { portableTextDefaultBlockRenderers, portableTextDefaultMarkRenderers } from "./renderers";
import { isPortableTextBlock, isPortableTextContent, isPortableTextObjectNode, isPortableTextSpan } from "./types";
import { Stack } from "../Stack";
const defaultUnknownTypeHandler = (kind, type) => {
console.warn("PortableText: no renderer for " + kind + " type \"" + type + "\".");
};
/**
* A span's marks wrap its text inside out. An unknown mark is reported and
* skipped — the text stays, because dropping a word would break the sentence.
*/
const renderSpan = (span, ctx) => {
var _span$marks;
return ((_span$marks = span.marks) != null ? _span$marks : []).reduce((children, mark) => {
var _ctx$components$marks, _ctx$components;
const type = typeof mark === "string" ? mark : mark.type;
const Renderer = (_ctx$components$marks = (_ctx$components = ctx.components) == null || (_ctx$components = _ctx$components.marks) == null ? void 0 : _ctx$components[type]) != null ? _ctx$components$marks : portableTextDefaultMarkRenderers[type];
if (!Renderer) {
ctx.report("mark", type);
return children;
}
return /*#__PURE__*/React.createElement(Renderer, {
mark: typeof mark === "string" ? undefined : mark
}, children);
}, span.text);
};
/**
* Traversal is structural, so the walker keeps no list of block types of its
* own: a `children` array holds spans or nested blocks, a nested `content`
* holds a node list, and anything else has no children to render.
*/
const renderBlockChildren = (block, ctx) => {
const _ref = block,
children = _ref.children,
content = _ref.content;
if (Array.isArray(children)) {
return children.map((child, index) => /*#__PURE__*/React.createElement(Fragment, {
key: index
}, isPortableTextSpan(child) ? renderSpan(child, ctx) : isPortableTextBlock(child) ? renderBlock(child, ctx) : null));
}
return isPortableTextContent(content) ? renderNodes(content, ctx) : undefined;
};
const renderBlock = (block, ctx) => {
var _ctx$components$block, _ctx$components2;
const type = block.type;
const Renderer = (_ctx$components$block = (_ctx$components2 = ctx.components) == null || (_ctx$components2 = _ctx$components2.blocks) == null ? void 0 : _ctx$components2[type]) != null ? _ctx$components$block : portableTextDefaultBlockRenderers[type];
if (!Renderer) {
ctx.report("block", block.type);
return null;
}
// Blocks that carry spans outside `children` (table cells) render them
// through this engine-bound helper, so mark injection works there too.
const renderSpans = spans => spans.map((span, index) => /*#__PURE__*/React.createElement(Fragment, {
key: index
}, renderSpan(span, ctx)));
return /*#__PURE__*/React.createElement(Renderer, {
block: block,
renderSpans: renderSpans
}, renderBlockChildren(block, ctx));
};
const renderObject = (node, ctx) => {
var _ctx$components3;
const Renderer = (_ctx$components3 = ctx.components) == null || (_ctx$components3 = _ctx$components3.objects) == null ? void 0 : _ctx$components3[node._type];
if (!Renderer) {
ctx.report("object", node._type);
return null;
}
return /*#__PURE__*/React.createElement(Renderer, {
node: node
}, isPortableTextContent(node.content) ? renderNodes(node.content, ctx) : undefined);
};
const renderNodes = (nodes, ctx) => nodes.map((node, index) => /*#__PURE__*/React.createElement(Fragment, {
key: index
}, isPortableTextObjectNode(node) ? renderObject(node, ctx) : renderBlock(node, ctx)));
/**
* The Portable Text engine: walks a {@link PortableTextContent} envelope and
* renders it with the default renderers for the universal set, plus whatever
* the application injects through `components`. The engine itself is hollow —
* it knows no product element; an unknown block or object renders nothing and
* is reported once per type through `onUnknownType` (console.warn when the
* hook is absent).
*/
export const PortableText = /*#__PURE__*/forwardRef((_ref2, ref) => {
let content = _ref2.content,
components = _ref2.components,
_ref2$space = _ref2.space,
space = _ref2$space === void 0 ? "md" : _ref2$space,
rest = _objectWithoutPropertiesLoose(_ref2, _excluded);
// Memoized so unknown types are reported once per type for a given
// content/components pair, not again on every parent re-render.
const nodes = useMemo(() => {
const reported = new Set();
const report = (kind, type) => {
var _components$onUnknown;
const key = kind + ":" + type;
if (reported.has(key)) return;
reported.add(key);
((_components$onUnknown = components == null ? void 0 : components.onUnknownType) != null ? _components$onUnknown : defaultUnknownTypeHandler)(kind, type);
};
return renderNodes(content, {
components,
report
});
}, [content, components]);
return /*#__PURE__*/React.createElement(Stack, _extends({
ref: ref,
space: space
}, rest), nodes);
});
PortableText.displayName = "PortableText";
//# sourceMappingURL=PortableText.js.map