@liveblocks/react-ui
Version:
A set of React pre-built components for the Liveblocks products. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.
422 lines (419 loc) • 11.1 kB
JavaScript
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { assertNever, sanitizeUrl } from '@liveblocks/core';
import { Slot } from '@radix-ui/react-slot';
import { Lexer } from 'marked';
import { forwardRef, useMemo, memo } from 'react';
const defaultComponents = {
Paragraph: ({ children }) => {
return /* @__PURE__ */ jsx("p", {
children
});
},
Inline: ({ type, children }) => {
switch (type) {
case "strong":
return /* @__PURE__ */ jsx("strong", {
children
});
case "em":
return /* @__PURE__ */ jsx("em", {
children
});
case "code":
return /* @__PURE__ */ jsx("code", {
children
});
case "del":
return /* @__PURE__ */ jsx("del", {
children
});
default:
assertNever(type, "Unknown inline type");
}
},
CodeBlock: ({ language, code }) => {
return /* @__PURE__ */ jsx("pre", {
"data-language": language ?? void 0,
children: /* @__PURE__ */ jsx("code", {
children: code
})
});
},
Link: ({ href, title, children }) => {
return /* @__PURE__ */ jsx("a", {
href,
title,
target: "_blank",
rel: "noopener noreferrer",
children
});
},
Heading: ({ level, children }) => {
const Heading = `h${level}`;
return /* @__PURE__ */ jsx(Heading, {
children
});
},
Image: ({ src, alt, title }) => {
return /* @__PURE__ */ jsx("img", {
src,
alt,
title
});
},
Blockquote: ({ children }) => {
return /* @__PURE__ */ jsx("blockquote", {
children
});
},
Table: ({ headings, rows }) => {
return /* @__PURE__ */ jsxs("table", {
children: [
/* @__PURE__ */ jsx("thead", {
children: /* @__PURE__ */ jsx("tr", {
children: headings.map((heading, index) => {
return /* @__PURE__ */ jsx("th", {
align: heading.align,
children: heading.children
}, index);
})
})
}),
/* @__PURE__ */ jsx("tbody", {
children: rows.map((row, index) => {
return /* @__PURE__ */ jsx("tr", {
children: row.map((cell, index2) => {
return /* @__PURE__ */ jsx("td", {
align: cell.align,
children: cell.children
}, index2);
})
}, index);
})
})
]
});
},
List: ({ type, items, start }) => {
const List = type === "ordered" ? "ol" : "ul";
return /* @__PURE__ */ jsx(List, {
start: start === 1 ? void 0 : start,
children: items.map((item, index) => /* @__PURE__ */ jsxs("li", {
children: [
item.checked !== void 0 && /* @__PURE__ */ jsxs(Fragment, {
children: [
/* @__PURE__ */ jsx("input", {
type: "checkbox",
disabled: true,
checked: item.checked
}),
" "
]
}),
item.children
]
}, index))
});
},
Separator: () => {
return /* @__PURE__ */ jsx("hr", {});
}
};
const Markdown = forwardRef(
({ content, components, asChild, ...props }, forwardedRef) => {
const Component = asChild ? Slot : "div";
const tokens = useMemo(() => {
return new Lexer().lex(content);
}, [content]);
return /* @__PURE__ */ jsx(Component, {
...props,
ref: forwardedRef,
children: tokens.map((token, index) => {
return /* @__PURE__ */ jsx(MemoizedMarkdownToken, {
token,
components
}, index);
})
});
}
);
const MemoizedMarkdownToken = memo(
({
token,
components
}) => {
return /* @__PURE__ */ jsx(MarkdownToken, {
token,
components
});
},
(previousProps, nextProps) => {
const previousToken = previousProps.token;
const nextToken = nextProps.token;
if (previousToken.raw.length !== nextToken.raw.length) {
return false;
}
if (previousToken.type !== nextToken.type) {
return false;
}
return previousToken.raw === nextToken.raw;
}
);
function MarkdownToken({
token,
components
}) {
if (!isMarkedToken(token)) {
return null;
}
switch (token.type) {
case "escape": {
return token.text;
}
case "space": {
return null;
}
case "text": {
if (token.tokens !== void 0) {
return /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: token.tokens,
components
});
} else {
return parseHtmlEntities(token.text);
}
}
case "br": {
return /* @__PURE__ */ jsx("br", {});
}
case "paragraph": {
const Paragraph = components?.Paragraph ?? defaultComponents.Paragraph;
return /* @__PURE__ */ jsx(Paragraph, {
children: /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: token.tokens,
components
})
});
}
case "heading": {
const Heading = components?.Heading ?? defaultComponents.Heading;
return /* @__PURE__ */ jsx(Heading, {
level: clampHeadingLevel(token.depth),
children: /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: token.tokens,
components
})
});
}
case "strong": {
const Inline = components?.Inline ?? defaultComponents.Inline;
return /* @__PURE__ */ jsx(Inline, {
type: "strong",
children: /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: token.tokens,
components
})
});
}
case "em": {
const Inline = components?.Inline ?? defaultComponents.Inline;
return /* @__PURE__ */ jsx(Inline, {
type: "em",
children: /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: token.tokens,
components
})
});
}
case "codespan": {
const Inline = components?.Inline ?? defaultComponents.Inline;
return /* @__PURE__ */ jsx(Inline, {
type: "code",
children: parseHtmlEntities(token.text)
});
}
case "del": {
const Inline = components?.Inline ?? defaultComponents.Inline;
return /* @__PURE__ */ jsx(Inline, {
type: "del",
children: /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: token.tokens,
components
})
});
}
case "link": {
const href = sanitizeUrl(token.href);
if (href === null) {
return /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: token.tokens,
components
});
}
const Link = components?.Link ?? defaultComponents.Link;
return /* @__PURE__ */ jsx(Link, {
href,
title: token.title ?? void 0,
children: /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: token.tokens,
components
})
});
}
case "code": {
let language = void 0;
if (token.lang !== void 0) {
language = token.lang.match(/^\S*/)?.[0] ?? void 0;
}
const CodeBlock = components?.CodeBlock ?? defaultComponents.CodeBlock;
return /* @__PURE__ */ jsx(CodeBlock, {
language,
code: token.text
});
}
case "blockquote": {
const Blockquote = components?.Blockquote ?? defaultComponents.Blockquote;
return /* @__PURE__ */ jsx(Blockquote, {
children: /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: token.tokens,
components,
normalizeToBlockTokens: true
})
});
}
case "list": {
const List = components?.List ?? defaultComponents.List;
const items = token.items.map((item) => {
return {
checked: item.task ? item.checked : void 0,
children: /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: item.tokens,
components,
normalizeToBlockTokens: item.loose
})
};
});
const props = token.ordered ? { type: "ordered", items, start: token.start || 1 } : { type: "unordered", items };
return /* @__PURE__ */ jsx(List, {
...props
});
}
case "table": {
const Table = components?.Table ?? defaultComponents.Table;
const headings = token.header.map(
(cell) => ({
align: cell.align ?? void 0,
children: /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: cell.tokens,
components
})
})
);
const rows = token.rows.map(
(row) => row.map((cell) => ({
align: cell.align ?? void 0,
children: /* @__PURE__ */ jsx(MarkdownTokens, {
tokens: cell.tokens,
components
})
}))
);
return /* @__PURE__ */ jsx(Table, {
headings,
rows
});
}
case "image": {
const href = sanitizeUrl(token.href);
if (href === null) {
return token.text;
}
const Image = components?.Image ?? defaultComponents.Image;
return /* @__PURE__ */ jsx(Image, {
src: href,
alt: token.text,
title: token.title ?? void 0
});
}
case "hr": {
const Separator = components?.Separator ?? defaultComponents.Separator;
return /* @__PURE__ */ jsx(Separator, {});
}
case "html":
default: {
return null;
}
}
}
function MarkdownTokens({
tokens,
components,
normalizeToBlockTokens = false
}) {
const normalizedTokens = [];
if (normalizeToBlockTokens) {
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
switch (token.type) {
case "text": {
const texts = [token];
while (i + 1 < tokens.length && tokens[i + 1].type === "text") {
i++;
texts.push(tokens[i]);
}
normalizedTokens.push({
type: "paragraph",
tokens: texts,
raw: texts.map((text) => text.raw).join(""),
text: texts.map((text) => text.text).join("")
});
break;
}
default: {
normalizedTokens.push(token);
}
}
}
}
return tokens.map((token, index) => /* @__PURE__ */ jsx(MarkdownToken, {
token,
components
}, index));
}
const markedTokenTypes = [
"blockquote",
"br",
"code",
"codespan",
"def",
"del",
"em",
"escape",
"heading",
"hr",
"html",
"image",
"link",
"list",
"list_item",
"paragraph",
"space",
"strong",
"table",
"text"
];
function isMarkedToken(token) {
return typeof token === "object" && token !== null && "type" in token && markedTokenTypes.includes(token.type);
}
function parseHtmlEntities(input) {
const document = new DOMParser().parseFromString(
`<!doctype html><body>${input}`,
"text/html"
);
return document.body.textContent;
}
function clampHeadingLevel(level) {
return Math.max(1, Math.min(6, level));
}
export { Markdown, MarkdownToken };
//# sourceMappingURL=Markdown.js.map