@nuxtjs/sanity
Version:
Sanity integration for Nuxt
211 lines (210 loc) • 5.58 kB
JavaScript
import { defu } from "defu";
import { defineComponent, h } from "vue";
import { isVue2 } from "#imports";
const isSpan = (block) => block._type === "span";
const defaults = {
types: {
span: "span",
image: "img"
},
marks: {
strong: "strong",
em: "em",
link: "a"
},
styles: {
h1: "h1",
h2: "h2",
h3: "h3",
h4: "h4",
h5: "h5",
h6: "h6",
normal: "p",
blockquote: "blockquote"
},
listItem: "li",
container: "div"
};
const validAttrs = [
"abbr",
"accesskey",
"accessKey",
"action",
"alt",
"autocomplete",
"autofocus",
"autoplay",
"charset",
"checked",
"cite",
"class",
"cols",
"colspan",
"command",
"content",
"datetime",
"default",
"disabled",
"download",
"draggable",
"dropzone",
"headers",
"height",
"hidden",
"href",
"hreflang",
"id",
"maxlength",
"minlength",
"muted",
"placeholder",
"preload",
"radiogroup",
"readonly",
"required",
"role",
"selected",
"src",
"srcdoc",
"srcset",
"tabindex",
"title",
"value",
"width",
"wrap"
];
function findSerializer(item, serializers) {
if (item?.listItem && item._type !== "list") {
return serializers.listItem || "li";
}
return item?._type ? serializers.types[item._type] || serializers.marks[item._type] : void 0;
}
function renderStyle(item, serializers, children) {
const serializer = item.style && serializers.styles[item.style];
const isElement = typeof serializer === "string";
const props = extractProps(item, isElement);
if (!item.listItem && item.style && serializer) {
return h(serializer, props, isVue2 ? children?.() : { default: children });
}
return children?.();
}
function renderInSerializer(item, serializers) {
return render(serializers, item, () => (item.children || []).map((child) => {
if (isSpan(child)) {
return renderMarks(child.text, child.marks, serializers, item.markDefs);
}
return render(serializers, child, () => renderMarks(child.text, child.marks, serializers, item.markDefs));
}));
}
function renderMarks(content, [mark, ...marks] = [], serializers, markDefs = []) {
if (!mark) return content;
const definition = mark in serializers.marks ? { _type: mark, _key: "" } : markDefs.find((m) => m._key === mark);
return render(serializers, definition, () => renderMarks(content, marks, serializers, markDefs));
}
function walkList(blocks, block) {
if (!block.listItem) {
blocks.push(block);
return blocks;
}
const lastBlock = blocks[blocks.length - 1] || {};
if (lastBlock._type !== "list" || !lastBlock.children || block.level === 1 && block.listItem !== lastBlock.listItem) {
blocks.push({
_type: "list",
listItem: block.listItem,
level: block.level,
children: [block]
});
return blocks;
}
if (block.level === lastBlock.level && block.listItem === lastBlock.listItem) {
lastBlock.children.push(block);
return blocks;
}
walkList(lastBlock.children, block);
return blocks;
}
function render(serializers, item, children) {
const serializer = findSerializer(item, serializers);
if (!serializer) return children?.();
if (!item) {
return void 0;
}
const isElement = typeof serializer === "string";
const props = extractProps(item, isElement);
if (isElement) {
return h(serializer, props, children?.());
}
return h(serializer, props, isVue2 ? children?.() : { default: () => children?.() });
}
function extractProps(item, isElement) {
return Object.fromEntries(
Object.entries(item).filter(([key]) => key !== "_type" && key !== "markDefs").map(
([key, value]) => {
if (key === "_key") return ["key", value || null];
if (!isElement || validAttrs.includes(key)) return [key, value];
return [];
}
)
);
}
function renderBlocks(blocks, serializers) {
return blocks.map((block) => {
const node = renderStyle(block, serializers, () => renderInSerializer(block, serializers));
if (import.meta.dev && (!node || Array.isArray(node) && !node.length)) {
console.warn(`No serializer found for block type "${block._type}".`, block);
}
return node;
});
}
export default defineComponent({
name: "SanityContent",
inheritAttrs: false,
props: {
blocks: {
type: Array,
default: () => []
},
serializers: {
type: Object,
default: () => ({})
}
},
setup(props) {
const serializers = defu(props.serializers, defaults);
serializers.types.list = serializers.types.list || createListSerializer(serializers);
return () => renderBlocks(props.blocks?.reduce(walkList, []) || [], serializers);
}
});
const createListSerializer = (serializers) => {
return defineComponent({
name: "ListComponent",
inheritAttrs: false,
props: {
children: {
type: Array,
default: () => []
},
level: {
type: Number,
default: 1
}
},
setup(props) {
return () => {
const isOrdered = props.children[0]?.listItem === "number";
if (props.level > 1) {
return h(serializers.listItem || "li", [h(
isOrdered ? "ol" : "ul",
{},
isVue2 ? renderBlocks(props.children, serializers) : { default: () => renderBlocks(props.children, serializers) }
)]);
}
return h(
isOrdered ? "ol" : "ul",
{},
isVue2 ? renderBlocks(props.children, serializers) : { default: () => renderBlocks(props.children, serializers) }
);
};
}
});
};