Skip to content

Commit

Permalink
feat: init tiptap
Browse files Browse the repository at this point in the history
  • Loading branch information
ledouxm committed Apr 25, 2024
1 parent 61b93ce commit 9a49a5e
Show file tree
Hide file tree
Showing 5 changed files with 846 additions and 5 deletions.
10 changes: 8 additions & 2 deletions packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@ark-ui/anatomy": "^2.3.1",
"@ark-ui/react": "^2.2.3",
"@codegouvfr/react-dsfr": "^1.9.2",
"@cr-vif/electric-client": "workspace:*",
"@emotion/react": "^11.11.4",
"@emotion/server": "^11.11.0",
"@emotion/styled": "^11.11.0",
Expand All @@ -31,7 +32,10 @@
"@tanstack/react-query": "^4.18.0",
"@tanstack/react-router": "^1.22.2",
"@tanstack/router-devtools": "^1.22.2",
"@cr-vif/electric-client": "workspace:*",
"@tiptap/extension-link": "^2.3.0",
"@tiptap/extension-placeholder": "^2.3.0",
"@tiptap/react": "^2.3.0",
"@tiptap/starter-kit": "^2.3.0",
"date-fns": "^3.6.0",
"electric-sql": "^0.10.1",
"install": "^0.13.0",
Expand All @@ -41,6 +45,8 @@
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.13",
"react-hook-form": "^7.51.1",
"react-icons": "^5.1.0",
"react-pdf-html": "^2.0.4",
"react-use": "^17.5.0",
"uuid": "^9.0.1",
"vite-plugin-wasm": "^3.3.0",
Expand Down Expand Up @@ -68,8 +74,8 @@
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"prisma": "^4.8.1",
"postcss": "^8.4.38",
"prisma": "^4.8.1",
"typescript": "^5.4",
"vite": "^5.1.6",
"vite-node": "^1.4.0"
Expand Down
113 changes: 113 additions & 0 deletions packages/frontend/src/features/text-editor/TextEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import Link from "@tiptap/extension-link";
import { Placeholder } from "@tiptap/extension-placeholder";
import { EditorContent, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import { useEffect } from "react";
import { css } from "#styled-system/css";
import { TextEditorToolbar } from "./TextEditorToolbar";

interface Props {
defaultValue?: string;
onChange?: (value: string) => void;
autoFocus?: boolean;
readOnly?: boolean;
placeholder?: string;
hasSubmitted?: boolean;
}

export const TextEditor = (props: Props) => {
const { defaultValue, onChange, autoFocus, readOnly, placeholder, hasSubmitted } = props;

const editor = useEditor({
autofocus: autoFocus ?? false,
editable: !readOnly,
extensions: [
StarterKit.configure({
codeBlock: false,
code: {
HTMLAttributes: {
class: "inline",
},
},
}),
Placeholder.configure({
placeholder,
}),
Link.configure({
autolink: true,
linkOnPaste: true,
}),
],
content: defaultValue,
onUpdate({ editor }) {
onChange?.(editor.getHTML());
},
});

const isEditorActive = !!editor;

// biome-ignore lint/correctness/useExhaustiveDependencies:
useEffect(() => {
if (hasSubmitted && isEditorActive) {
editor.commands.clearContent(true);
}
}, [isEditorActive, hasSubmitted]);

return (
<div className="text-edito">
<EditorContent
className={css({
minH: "160px",
"& > div": {
outline: "none",
roundedTop: "md",
borderWidth: "1px",
minH: "160px",
maxH: "240px",
py: "2",
px: "2",
fontSize: "13px",
overflowY: "auto",
_focusVisible: {
outlineWidth: "1px",
borderColor: "purple.500",
outlineColor: "purple.500",
outlineStyle: "solid",
},
"& strong": {
fontWeight: "semibold",
},
"& em": {
fontStyle: "italic",
},
"& :where(ul, ol, li)": {
listStyle: "unset",
},
"& :where(ul, ol)": {
paddingStart: "1rem",
},
"& blockquote": {
borderLeftWidth: "3px",
borderColor: "gray.300",
paddingLeft: "4",
},
"& a": {
color: "purple.600",
},
"& code.inline": {
rounded: "2px",
borderWidth: "1px",
mb: "1px",
padding: "2px 3px 1px",
fontFamily: "mono",
fontSize: "0.9em",
bg: "gray.600",
},
},
})}
editor={editor}
/>
{editor && <TextEditorToolbar editor={editor} />}
</div>
);
};
134 changes: 134 additions & 0 deletions packages/frontend/src/features/text-editor/TextEditorToolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import type { Editor } from "@tiptap/react";
import { LuBold, LuCode, LuItalic, LuLink, LuList, LuListOrdered, LuQuote, LuStrikethrough } from "react-icons/lu";
import { cva } from "#styled-system/css";
import { hstack } from "#styled-system/patterns";

const toolbar = hstack({
gap: "0",
roundedBottom: "sm",
borderWidth: "1px",
mt: "-1px",
lineHeight: "0",
transition: "all 0.15s",
});

const toolbarButtonRecipe = cva({
base: {
display: "flex",
justifyContent: "center",
alignItems: "center",
w: "8",
h: "8",
},
variants: {
active: {
true: {
color: "gray.100",
bg: "gray.600",
},
},
},
});

interface Props {
editor: Editor;
}

export const TextEditorToolbar = (props: Props) => {
const { editor } = props;

return (
<div className={toolbar}>
<button
type="button"
onPointerDown={(event) => event.preventDefault()}
onClick={() => editor.chain().focus().toggleBold().run()}
className={toolbarButtonRecipe({
active: editor.isActive("bold"),
})}
>
<LuBold />
</button>
<button
type="button"
onPointerDown={(event) => event.preventDefault()}
onClick={() => editor.chain().focus().toggleItalic().run()}
className={toolbarButtonRecipe({
active: editor.isActive("italic"),
})}
>
<LuItalic />
</button>
<button
type="button"
onPointerDown={(event) => event.preventDefault()}
onClick={() => editor.chain().focus().toggleStrike().run()}
className={toolbarButtonRecipe({
active: editor.isActive("strike"),
})}
>
<LuStrikethrough />
</button>
<button
type="button"
onPointerDown={(event) => event.preventDefault()}
onClick={() => editor.chain().focus().toggleBulletList().run()}
className={toolbarButtonRecipe({
active: editor.isActive("bulletList"),
})}
>
<LuList />
</button>
<button
type="button"
onPointerDown={(event) => event.preventDefault()}
onClick={() => editor.chain().focus().toggleOrderedList().run()}
className={toolbarButtonRecipe({
active: editor.isActive("orderedList"),
})}
>
<LuListOrdered />
</button>
<button
type="button"
onPointerDown={(event) => event.preventDefault()}
onClick={() => editor.chain().focus().toggleBlockquote().run()}
className={toolbarButtonRecipe({
active: editor.isActive("blockquote"),
})}
>
<LuQuote />
</button>
<button
type="button"
onPointerDown={(event) => event.preventDefault()}
onClick={() => editor.chain().focus().toggleCode().run()}
className={toolbarButtonRecipe({
active: editor.isActive("code"),
})}
>
<LuCode />
</button>
<button
type="button"
onPointerDown={(event) => event.preventDefault()}
onClick={() => {
const previousUrl = editor.getAttributes("link").href;
const url = window.prompt("URL", previousUrl);
if (url === null) return; // cancelled
if (url === "") {
// empty
editor.chain().focus().extendMarkRange("link").unsetLink().run();
return;
}
editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
}}
className={toolbarButtonRecipe({
active: editor.isActive("link"),
})}
>
<LuLink />
</button>
</div>
);
};
16 changes: 15 additions & 1 deletion packages/frontend/src/routes/export.$reportId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { Flex, styled } from "#styled-system/jsx";
import type { Report } from "../generated/client";
import { Document, Page, Text, View, StyleSheet, type Styles } from "@react-pdf/renderer";
import { PDFViewer } from "@react-pdf/renderer";
import { TextEditor } from "../features/text-editor/TextEditor";
import { useState } from "react";
import Html from "react-pdf-html";

const ExportPdf = () => {
const { reportId } = Route.useParams();
Expand All @@ -14,12 +17,23 @@ const ExportPdf = () => {
};

const WithReport = ({ report }: { report: Report }) => {
const [value, setValue] = useState("");

console.log(value);

return (
<Flex direction="column">
<TextEditor defaultValue={value} onChange={(e) => setValue(e)} />
<PDFViewer>
{/* <ReportPdf report={report} /> */}
<Document>
<RenderPdfLike components={pdfComponents} styles={styles} />
<Page size="A4" style={styles.page}>
<Html>
{/* <div dangerouslySetInnerHTML={{ __html: value }}></div> */}
{value}
</Html>
{/* <View _ht */}
</Page>
</Document>
</PDFViewer>

Expand Down
Loading

0 comments on commit 9a49a5e

Please sign in to comment.