Added rich text parsing

This commit is contained in:
DrMint 2024-02-17 23:10:18 +01:00
parent d11114528c
commit a588f81803
18 changed files with 432 additions and 0 deletions

View File

@ -0,0 +1,41 @@
---
import RTNode from "./components/RTNode.astro";
interface Props {
content: {
root: {
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
};
};
}
const { content } = Astro.props;
---
{
/* ------------------------------------------- HTML ------------------------------------------- */
}
<div class="rich-text">
{content.root.children.map((node) => <RTNode node={node} />)}
</div>
{
/* ------------------------------------------- CSS -------------------------------------------- */
}
<style>
.rich-text {
& li[checkbox]::marker {
content: "☐";
}
& li[checkbox][checked]::marker {
content: "☒";
}
}
</style>

View File

@ -0,0 +1,19 @@
---
type BasicNode = {
type: string;
version: number;
[k: string]: unknown;
};
interface Props {
node: BasicNode;
}
const { node } = Astro.props;
---
<p>
{
`Unknown node type: ${node.type}. Please contact website technical administrator.`
}
</p>

View File

@ -0,0 +1,45 @@
---
import RTError from "../RTError.astro";
import RTNode from "../RTNode.astro";
import RTCustomLink from "./components/RTCustomLink.astro";
import RTInternalLink from "./components/RTInternalLink.astro";
interface Props {
node: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
version: number;
fields: {
linkType: "internal" | "custom";
doc: any;
url: string;
newTab: boolean;
};
[k: string]: unknown;
};
}
const { node } = Astro.props;
---
{
node.fields.linkType === "custom" ? (
<RTCustomLink href={node.fields.url} newTab={node.fields.newTab}>
{node.children.map((node) => (
<RTNode node={node} />
))}
</RTCustomLink>
) : node.fields.linkType === "internal" ? (
<RTInternalLink doc={node.fields.doc}>
{node.children.map((node) => (
<RTNode node={node} />
))}
</RTInternalLink>
) : (
<RTError node={node} />
)
}

View File

@ -0,0 +1,16 @@
---
interface Props {
href: string;
newTab: boolean;
}
const { href, newTab } = Astro.props;
---
<a
href={href}
target={newTab ? "_blank" : undefined}
rel={newTab ? "noopener noreferrer" : undefined}
>
<slot />
</a>

View File

@ -0,0 +1,23 @@
---
import { getI18n } from "translations/translations";
interface Props {
doc: {
relationTo: string;
value: any;
};
}
const { doc } = Astro.props;
const { getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
---
{
doc.relationTo === "folders" ? (
<a href={getLocalizedUrl(`/folders/${doc.value.slug}`)}>
<slot />
</a>
) : (
<p>{`Unknown internal link: ${doc.relationTo}. Please contact website technical administrator.`}</p>
)
}

View File

@ -0,0 +1,48 @@
---
import RTBasicListItem from "./components/RTBasicListItem.astro";
import RTCheckListItem from "./components/RTCheckListItem.astro";
interface Props {
node: {
type: string;
version: number;
format: number;
text: string;
listType: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
[k: string]: unknown;
};
}
const { node } = Astro.props;
---
{
node.listType === "number" ? (
<ol>
{node.children.map((node) => (
<RTBasicListItem node={node} />
))}
</ol>
) : node.listType === "bullet" ? (
<ul>
{node.children.map((node) => (
<RTBasicListItem node={node} />
))}
</ul>
) : node.listType === "check" ? (
<ul>
{node.children.map((node) => (
<RTCheckListItem node={node} />
))}
</ul>
) : (
<p>
{`Unknown list type: ${node.listType}. Please contact website technical administrator.`}
</p>
)
}

View File

@ -0,0 +1,23 @@
---
import RTNode from "../../RTNode.astro";
interface Props {
node: {
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
type: string;
version: number;
format: number;
text: string;
listType: string;
[k: string]: unknown;
};
}
const { node } = Astro.props;
---
<li>{node.children.map((node) => <RTNode node={node} />)}</li>

View File

@ -0,0 +1,40 @@
---
import { Icon } from "astro-icon/components";
import RTNode from "../../RTNode.astro";
interface Props {
node: {
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
type: string;
version: number;
format: number;
text: string;
listType: string;
[k: string]: unknown;
};
}
const { node } = Astro.props;
---
<li>
<Icon
name={node.checked
? "material-symbols:check-box"
: "material-symbols:check-box-outline-blank"}
/>
{node.children.map((node) => <RTNode node={node} />)}
</li>
<style>
li {
&::marker {
content: ""
}
margin-left: -16px;
}
</style>

View File

@ -0,0 +1,42 @@
---
import RTParagraph from "./RTParagraph.astro";
import RTList from "./RTList/RTList.astro";
import RTError from "./RTError.astro";
import RTText from "./RTText/RTText.astro";
import RTLink from "./RTLink/RTLink.astro";
interface Props {
node: {
type: string;
version: number;
[k: string]: unknown;
};
}
const { node } = Astro.props;
let NodeElement;
switch (node.type) {
case "paragraph":
NodeElement = RTParagraph;
break;
case "list":
NodeElement = RTList;
break;
case "text":
NodeElement = RTText;
break;
case "link":
NodeElement = RTLink;
break;
default:
NodeElement = RTError;
break;
}
---
<NodeElement node={node} />

View File

@ -0,0 +1,34 @@
---
import RTNode from "./RTNode.astro";
interface Props {
node: {
type: string;
version: number;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
[k: string]: unknown;
};
}
const { node } = Astro.props;
---
{
/* ------------------------------------------- HTML ------------------------------------------- */
}
<p>{node.children.map((node) => <RTNode node={node} />)}</p>
{
/* ------------------------------------------- CSS -------------------------------------------- */
}
<style>
p {
margin-bottom: 1em;
}
</style>

View File

@ -0,0 +1,47 @@
---
import ConditionalWrapper from "components/ConditionalWrapper.astro";
import RTBold from "./components/RTBold.astro";
import RTItalic from "./components/RTItalic.astro";
import RTUnderline from "./components/RTUnderline.astro";
import RTLineThrough from "./components/RTLineThrough.astro";
import RTSubscript from "./components/RTSubscript.astro";
import RTSuperscript from "./components/RTSuperscript.astro";
import RTInlineCode from "./components/RTInlineCode.astro";
interface Props {
node: {
type: string;
version: number;
format: number;
text: string;
[k: string]: unknown;
};
}
const { node } = Astro.props;
---
<ConditionalWrapper wrapper={RTBold} condition={Boolean(node.format & 1)}>
<ConditionalWrapper wrapper={RTItalic} condition={Boolean(node.format & 2)}>
<ConditionalWrapper wrapper={RTLineThrough} condition={Boolean(node.format & 4)}>
<ConditionalWrapper wrapper={RTUnderline} condition={Boolean(node.format & 8)}>
<ConditionalWrapper
wrapper={RTInlineCode}
condition={Boolean(node.format & 16)}
>
<ConditionalWrapper
wrapper={RTSubscript}
condition={Boolean(node.format & 32)}
>
<ConditionalWrapper
wrapper={RTSuperscript}
condition={Boolean(node.format & 64)}
>
{node.text}
</ConditionalWrapper>
</ConditionalWrapper>
</ConditionalWrapper>
</ConditionalWrapper>
</ConditionalWrapper>
</ConditionalWrapper>
</ConditionalWrapper>

View File

@ -0,0 +1,5 @@
---
---
<b><slot /></b>

View File

@ -0,0 +1,12 @@
---
---
<span><slot /></span>
<style>
span {
font-family: monospace;
}
</style>

View File

@ -0,0 +1,5 @@
---
---
<i><slot /></i>

View File

@ -0,0 +1,11 @@
---
---
<span><slot /></span>
<style>
span {
text-decoration: line-through;
}
</style>

View File

@ -0,0 +1,5 @@
---
---
<sub><slot /></sub>

View File

@ -0,0 +1,5 @@
---
---
<sup><slot /></sup>

View File

@ -0,0 +1,11 @@
---
---
<span><slot /></span>
<style>
span {
text-decoration: underline;
}
</style>