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 { 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,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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");
|
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);
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue