create EditorRender, fix data init in column component, render on home

This commit is contained in:
Guillaume Dorce 2023-06-04 19:03:10 +02:00
parent 984b1cf428
commit 2aa9f0462f
7 changed files with 184 additions and 110 deletions

View File

@ -0,0 +1,111 @@
import { PagesRecord } from "@/@types/pocketbase-types";
function parseBoldText(text: string) {
const parts = text.split(/(<b>.*?<\/b>)/g);
const result = [];
for (let i = 0; i < parts.length; i++) {
if (parts[i]?.startsWith('<b>')) {
result.push(<b key={i}>{parts[i]?.slice(3, -4)}</b>)
} else {
result.push(parts[i])
}
}
return result;
}
function Paragraph({ text }: { text: string }) {
return <p className='text-xl'>{parseBoldText(text)}</p>
}
function Heading({ text, level }: { text: string, level: number }) {
switch (level) {
case 1:
return (
<>
<h1 className='font-poppins text-5xl'>{text}</h1>
<div className="line h-1 w-full bg-black"></div>
</>
)
case 2:
return <h2 className='text-2xl'>{text}</h2>
case 3:
return <h3>{text}</h3>
default:
return <h4>{text}</h4>
}
}
function Column({ blocksData }: { blocksData: Block[][] }) {
if (blocksData.length === 0) return null;
return (
<div className="mb-4 flex flex-row gap-10">
{blocksData.map((blocks, i) => {
return (<div className="flex flex-col gap-6 w-1/2" key={"col-" + i}>{blocks.map(block => {
switch (block.type) {
case 'paragraph':
return <Paragraph key={block.id} text={block.data.text} />
case 'header':
return <Heading key={block.id} text={block.data.text} level={block.data.level} />
default:
return null;
}
})}</div>)
})}
</div>
)
}
type ParagraphBlock = {
type: 'paragraph',
data: {
text: string,
},
id: string,
}
type HeaderBlock = {
type: 'header',
data: {
text: string,
level: number,
},
id: string,
}
type ColumnBlock = {
type: 'column',
data: {
blocksData: Block[][],
},
id: string,
}
type Block = ParagraphBlock | HeaderBlock | ColumnBlock;
export type Content = {
blocks: Block[],
version: string,
time: number,
}
export default function EditorRender({ data, className }: { data: PagesRecord<Content>, className?: string }) {
return (
<div className={className}>
{data.content && data.content.blocks.map((block, index): (JSX.Element | null) => {
switch (block.type) {
case 'paragraph':
return <Paragraph key={index} text={block.data.text} />
case 'header':
return <Heading key={index} text={block.data.text} level={block.data.level} />
case 'column':
return <Column key={index} blocksData={block.data.blocksData} />
default:
return <p key={index}>Unknown block type: <pre>{JSON.stringify(block, null, 2)}</pre></p>
}
})}
</div>
)
}
export { EditorRender };

View File

@ -1,5 +1,5 @@
import { memo, useEffect, useRef } from "react"; import { memo, useEffect, useRef } from "react";
import EditorJS from "@editorjs/editorjs"; import EditorJS, { ToolConstructable } from "@editorjs/editorjs";
import type { OutputData } from "@editorjs/editorjs"; import type { OutputData } from "@editorjs/editorjs";
import { EDITOR_JS_TOOLS } from "@/utils/tools"; import { EDITOR_JS_TOOLS } from "@/utils/tools";
// @ts-expect-error Undo is not a valid tool // @ts-expect-error Undo is not a valid tool
@ -8,6 +8,8 @@ import Undo from "editorjs-undo";
import DragDrop from "editorjs-drag-drop"; import DragDrop from "editorjs-drag-drop";
import ColumnTool from "@/utils/editor-tools/column"; import ColumnTool from "@/utils/editor-tools/column";
import "@/styles/editor.css";
type Props = { type Props = {
data?: OutputData; data?: OutputData;
onChange(val: OutputData): void; onChange(val: OutputData): void;
@ -25,7 +27,7 @@ const EditorBlock = ({ data, onChange, holder, autofocus }: Props) => {
tools: { tools: {
...EDITOR_JS_TOOLS, ...EDITOR_JS_TOOLS,
column: { column: {
class: ColumnTool, class: ColumnTool as ToolConstructable,
config: { config: {
tools: EDITOR_JS_TOOLS, tools: EDITOR_JS_TOOLS,
}, },

View File

@ -1,7 +1,9 @@
import Image from "next/image"; import Image from "next/image";
import Layout from "@/layouts/Home"; import Layout from "@/layouts/Home";
import { PagesRecord } from "@/@types/pocketbase-types";
import { Content, EditorRender } from "@/components/EditorRender";
function Home() { function Home({ data }: { data: PagesRecord<Content>}) {
return ( return (
<Layout title="Accueil"> <Layout title="Accueil">
<Image <Image
@ -75,8 +77,27 @@ function Home() {
</div> </div>
</div> </div>
</div> </div>
<div className="container mx-auto p-8">
<EditorRender data={data} />
</div>
</Layout> </Layout>
); );
} }
export default Home; export default Home;
export async function getServerSideProps() {
const { getPageBySlug } = await import("@/utils/pb");
const { data, error } = await getPageBySlug("home");
if (error || !data) {
return {
notFound: true,
}
}
return {
props: {
data: data,
},
}
}

View File

@ -1,114 +1,14 @@
import type { PagesRecord } from '@/@types/pocketbase-types'; import type { PagesRecord } from '@/@types/pocketbase-types';
import Layout from '@/layouts/Home' import Layout from '@/layouts/Home'
import EditorRender from '@/components/EditorRender';
import type { Content } from '@/components/EditorRender';
function parseBoldText(text: string) { export default function Page({ data }: { data: PagesRecord<Content> }) {
const parts = text.split(/(<b>.*?<\/b>)/g);
const result = [];
for (let i = 0; i < parts.length; i++) {
if (parts[i]?.startsWith('<b>')) {
result.push(<b key={i}>{parts[i]?.slice(3, -4)}</b>)
} else {
result.push(parts[i])
}
}
return result;
}
function Paragraph({ text }: { text: string }) {
return <p className='text-xl'>{parseBoldText(text)}</p>
}
function Heading({ text, level }: { text: string, level: number }) {
switch (level) {
case 1:
return (
<>
<h1 className='font-poppins text-5xl'>{text}</h1>
<div className="line h-1 w-full bg-black"></div>
</>
)
case 2:
return <h2 className='text-2xl'>{text}</h2>
case 3:
return <h3>{text}</h3>
default:
return <h4>{text}</h4>
}
}
function Column({ blocksData }: { blocksData: Block[][] }) {
if (blocksData.length === 0) return null;
return (
<div className="flex flex-row gap-6">
{blocksData.map((blocks, i) => {
return (<div className="flex flex-col gap-6 w-1/2" key={"col-" + i}>{blocks.map(block => {
switch (block.type) {
case 'paragraph':
return <Paragraph key={block.id} text={block.data.text} />
case 'header':
return <Heading key={block.id} text={block.data.text} level={block.data.level} />
default:
return null;
}
})}</div>)
})}
</div>
)
}
type ParagraphBlock = {
type: 'paragraph',
data: {
text: string,
},
id: string,
}
type HeaderBlock = {
type: 'header',
data: {
text: string,
level: number,
},
id: string,
}
type ColumnBlock = {
type: 'column',
data: {
blocksData: Block[][],
},
id: string,
}
type Block = ParagraphBlock | HeaderBlock | ColumnBlock;
type Content = {
blocks: Block[],
version: string,
time: number,
}
export default function Page({ data }: { slug: string, data: PagesRecord<Content> }) {
return ( return (
<Layout title={data.title}> <Layout title={data.title}>
<div className="container mx-auto p-8"> <div className="container mx-auto p-8">
<p>Here is the content rendered:</p> <p>Here is the content rendered:</p>
<div className="flex flex-col gap-6"> <EditorRender data={data} />
{data.content && data.content.blocks.map((block, index): (JSX.Element | null) => {
switch (block.type) {
case 'paragraph':
return <Paragraph key={index} text={block.data.text} />
case 'header':
return <Heading key={index} text={block.data.text} level={block.data.level} />
case 'column':
return <Column key={index} blocksData={block.data.blocksData} />
default:
return <p key={index}>Unknown block type: <pre>{JSON.stringify(block, null, 2)}</pre></p>
}
})}
</div>
</div> </div>
<pre>{JSON.stringify(data.content, null, 2)}</pre> <pre>{JSON.stringify(data.content, null, 2)}</pre>
</Layout> </Layout>

7
src/styles/editor.css Normal file
View File

@ -0,0 +1,7 @@
.ce-block .ce-block__content {
max-width: unset;
}
.ce-toolbar .ce-toolbar__content {
max-width: unset;
}

View File

@ -54,10 +54,14 @@ class ColumnTool {
const child = document.createElement("div"); const child = document.createElement("div");
child.classList.add("flex", "flex-row", "w-full", "space-x-4"); child.classList.add("flex", "flex-row", "w-full", "space-x-4");
const blocksData: BlockToolData[] = this.data.blocksData; const blocksData: BlockToolData[] = this.data.blocksData?.length > 0 ? this.data.blocksData : [[]];
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
if (this.editors[i]) return;
const column = this.createColumn(); const column = this.createColumn();
child.appendChild(column);
const editor = new EditorJS({ const editor = new EditorJS({
holder: column, holder: column,
tools: this.tools, tools: this.tools,
@ -66,10 +70,9 @@ class ColumnTool {
blocksData[i] = data.blocks; blocksData[i] = data.blocks;
}, },
data: { data: {
blocks: this.data.blocksData[i] || [], blocks: blocksData ? blocksData[i] : [],
}, },
}); });
child.appendChild(column);
this.editors.push(editor); this.editors.push(editor);
} }
this.wrapper.appendChild(child); this.wrapper.appendChild(child);

View File

@ -68,4 +68,34 @@ async function getIdFromSlug(slug: string) {
} }
} }
export async function uploadFile(file: File) {
try {
const res = await pb.collection("files").create(file);
return { data: res };
} catch (error) {
console.log(error);
return { error };
}
}
export async function listFiles() {
try {
const res = await pb.collection("files").getFullList();
return { data: res };
} catch (error) {
console.log(error);
return { error };
}
}
export async function getFile(id: string) {
try {
const res = await pb.collection("files").getOne(id);
return { data: res };
} catch (error) {
console.log(error);
return { error };
}
}
export { pb, getPages, getPageById, getPageBySlug, createPage, updatePage, getIdFromSlug }; export { pb, getPages, getPageById, getPageBySlug, createPage, updatePage, getIdFromSlug };