create EditorRender, fix data init in column component, render on home
This commit is contained in:
parent
984b1cf428
commit
2aa9f0462f
|
|
@ -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 };
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { memo, useEffect, useRef } from "react";
|
||||
import EditorJS from "@editorjs/editorjs";
|
||||
import EditorJS, { ToolConstructable } from "@editorjs/editorjs";
|
||||
import type { OutputData } from "@editorjs/editorjs";
|
||||
import { EDITOR_JS_TOOLS } from "@/utils/tools";
|
||||
// @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 ColumnTool from "@/utils/editor-tools/column";
|
||||
|
||||
import "@/styles/editor.css";
|
||||
|
||||
type Props = {
|
||||
data?: OutputData;
|
||||
onChange(val: OutputData): void;
|
||||
|
|
@ -25,7 +27,7 @@ const EditorBlock = ({ data, onChange, holder, autofocus }: Props) => {
|
|||
tools: {
|
||||
...EDITOR_JS_TOOLS,
|
||||
column: {
|
||||
class: ColumnTool,
|
||||
class: ColumnTool as ToolConstructable,
|
||||
config: {
|
||||
tools: EDITOR_JS_TOOLS,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import Image from "next/image";
|
||||
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 (
|
||||
<Layout title="Accueil">
|
||||
<Image
|
||||
|
|
@ -75,8 +77,27 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container mx-auto p-8">
|
||||
<EditorRender data={data} />
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,114 +1,14 @@
|
|||
import type { PagesRecord } from '@/@types/pocketbase-types';
|
||||
import Layout from '@/layouts/Home'
|
||||
import EditorRender from '@/components/EditorRender';
|
||||
import type { Content } from '@/components/EditorRender';
|
||||
|
||||
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="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> }) {
|
||||
export default function Page({ data }: { data: PagesRecord<Content> }) {
|
||||
return (
|
||||
<Layout title={data.title}>
|
||||
<div className="container mx-auto p-8">
|
||||
<p>Here is the content rendered:</p>
|
||||
<div className="flex flex-col gap-6">
|
||||
{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>
|
||||
<EditorRender data={data} />
|
||||
</div>
|
||||
<pre>{JSON.stringify(data.content, null, 2)}</pre>
|
||||
</Layout>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
.ce-block .ce-block__content {
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.ce-toolbar .ce-toolbar__content {
|
||||
max-width: unset;
|
||||
}
|
||||
|
|
@ -54,10 +54,14 @@ class ColumnTool {
|
|||
const child = document.createElement("div");
|
||||
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++) {
|
||||
if (this.editors[i]) return;
|
||||
|
||||
const column = this.createColumn();
|
||||
child.appendChild(column);
|
||||
|
||||
const editor = new EditorJS({
|
||||
holder: column,
|
||||
tools: this.tools,
|
||||
|
|
@ -66,10 +70,9 @@ class ColumnTool {
|
|||
blocksData[i] = data.blocks;
|
||||
},
|
||||
data: {
|
||||
blocks: this.data.blocksData[i] || [],
|
||||
blocks: blocksData ? blocksData[i] : [],
|
||||
},
|
||||
});
|
||||
child.appendChild(column);
|
||||
this.editors.push(editor);
|
||||
}
|
||||
this.wrapper.appendChild(child);
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
Loading…
Reference in New Issue