Skip to content

Commit

Permalink
feat(frontend): add prose-renderer (#317)
Browse files Browse the repository at this point in the history
* chore: convert

add script

* refactor: resource

use 2 queries cos inner join + update in same query doesn't work

* refactor: component selector

update typings

* feat: editor drawer context

add curr active idx

* feat: editpagedrawer

update so data is dynamic

* chore: componentselector

only paragraph uses component selector

* chore: update convert function

1. add zod validation
2. simplify the function

* chore: component selector

* chore: components selector

update to correct names

* chore: constants

update default prose block

* feat: convert

add convert functions

* chore: typings

* chore: next state

compute next state from block

* feat: update

update UI on save click

* feat: update

update pages automatically

* chore: rename editorstate

* chore: utils

delete obsolete function

* chore: menubar

remove title cos we don't want them to set h1

* chore: remove unused imports

* chore: remove json schema setting

* fix: tiptap

update typings + validation

* chore: componentselector

update to remove unsafe cast

* fix: add snapshot

* chore: add added block state to drawer context

* wip commit

* chore: export

update export from components package

* chore: update imports

* chore: update import

* fix: editing experience

update schema for type checking

* chore: remove unused comment

* chore: menubar

remove extra itesm

* chore: prose

add format

* chore: menubar

ui fixes

* feat: add tiptap renderer

* chore: delete old imports

* chore: format

* fix: resource

update service select for blob

* chore: style

lint and format fixes

* chore: fixup

* chore: style/lint

* fix: root state drawer

add title for prose

* chore: formbuilder

* fix: prose

shift into top level

* fix: component selector

* fix: preview issues

* docs: component selector

update docs

* fix: import

* chore: style

* chore: fix imports

* chore: utils

removed unused function

* chore: route params

use query parse
  • Loading branch information
seaerchin authored Jul 24, 2024
1 parent 1e1d460 commit 7c11803
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 177 deletions.
23 changes: 21 additions & 2 deletions apps/studio/src/components/PageEditor/ComponentSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { IsomerComponent } from "@opengovsg/isomer-components"
import {
Flex,
Popover,
Expand All @@ -9,7 +10,6 @@ import {
Wrap,
} from "@chakra-ui/react"
import { Button, IconButton } from "@opengovsg/design-system-react"
import { type IsomerComponent } from "@opengovsg/isomer-components"
import { type IconType } from "react-icons"
import {
BiCard,
Expand All @@ -28,7 +28,10 @@ import {
} from "react-icons/bi"

import { useEditorDrawerContext } from "~/contexts/EditorDrawerContext"
import { useQueryParse } from "~/hooks/useQueryParse"
import { editPageSchema } from "~/pages/sites/[siteId]/pages/[pageId]"
import { type DrawerState } from "~/types/editorDrawer"
import { trpc } from "~/utils/trpc"
import { DEFAULT_BLOCKS } from "./constants"
import { type SectionType } from "./types"

Expand Down Expand Up @@ -103,7 +106,15 @@ function ComponentSelector() {
setDrawerState,
setSavedPageState,
setPreviewPageState,
setAddedBlock,
} = useEditorDrawerContext()

const { pageId, siteId } = useQueryParse(editPageSchema)
const [page] = trpc.page.readPageAndBlob.useSuspenseQuery({
pageId,
siteId,
})

const onProceed = (sectionType: SectionType) => {
// TODO: add new section to page/editor state
// NOTE: Only paragraph should go to tiptap editor
Expand All @@ -117,10 +128,18 @@ function ComponentSelector() {
const nextPageState = !!newComponent
? [...savedPageState, newComponent]
: savedPageState
setSavedPageState(nextPageState)

setDrawerState({ state: nextState })
setCurrActiveIdx(nextPageState.length - 1)
setPreviewPageState(nextPageState)

// TODO: Decide if setting addedBlocks
// to only be for complex blocks is a good idea
// or if we should combine prose to addedBlocks as well
// and handle prose -> complex components in the renderer itself
if (sectionType !== "prose") {
setAddedBlock(sectionType)
}
}

return (
Expand Down
51 changes: 1 addition & 50 deletions apps/studio/src/components/PageEditor/MenuBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,11 @@ import {
BiBold,
BiChevronDown,
BiChevronUp,
BiCodeAlt,
BiFile,
BiImageAdd,
BiItalic,
BiLink,
BiListOl,
BiListUl,
BiPlus,
BiRedo,
BiStrikethrough,
BiTable,
BiUnderline,
BiUndo,
} from "react-icons/bi"
import { MdSubscript, MdSuperscript } from "react-icons/md"

Expand Down Expand Up @@ -224,48 +216,6 @@ export const MenuBar = ({ editor }: { editor: Editor }) => {
},
],
},
{
type: "divider",
},
// {
// type: 'item',
// icon: BiLink,
// title: 'Add link',
// action: () => showModal('hyperlink'),
// },
{
type: "item",
icon: BiTable,
title: "Add table",
action: () =>
editor
.chain()
.focus()
// NOTE: Default to smallest multi table
.insertTable({ rows: 3, cols: 3, withHeaderRow: true })
.run(),
},
// {
// type: 'item',
// icon: BiFile,
// title: 'Add file',
// action: () => showModal('files'),
// },
{
type: "divider",
},
{
type: "item",
icon: BiUndo,
title: "Undo",
action: () => editor.chain().focus().undo().run(),
},
{
type: "item",
icon: BiRedo,
title: "Redo",
action: () => editor.chain().focus().redo().run(),
},
]

return (
Expand All @@ -276,6 +226,7 @@ export const MenuBar = ({ editor }: { editor: Editor }) => {
pl="0.75rem"
pr="0.25rem"
py="0.25rem"
w="100%"
borderBottom="1px solid"
borderColor="base.divider.strong"
borderTopRadius="0.25rem"
Expand Down
2 changes: 1 addition & 1 deletion apps/studio/src/components/PageEditor/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type IsomerComponent } from "@opengovsg/isomer-components"
import type { IsomerComponent } from "@opengovsg/isomer-components"

// TODO: add in default blocks for remaining
export const DEFAULT_BLOCKS: Record<
Expand Down
9 changes: 8 additions & 1 deletion apps/studio/src/contexts/EditorDrawerContext.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import type { IsomerComponent } from "@opengovsg/isomer-components"
import type { Dispatch, PropsWithChildren, SetStateAction } from "react"
import { createContext, useContext, useState } from "react"
import { type IsomerComponent } from "@opengovsg/isomer-components"

import type { SectionType } from "~/components/PageEditor/types"
import { type DrawerState } from "~/types/editorDrawer"

export interface DrawerContextType {
currActiveIdx: number
setCurrActiveIdx: (currActiveIdx: number) => void
drawerState: DrawerState
setDrawerState: (state: DrawerState) => void
addedBlock: Exclude<SectionType, "prose">
setAddedBlock: (addedBlock: Exclude<SectionType, "prose">) => void
savedPageState: IsomerComponent[]
setSavedPageState: Dispatch<SetStateAction<IsomerComponent[]>>
previewPageState: IsomerComponent[]
Expand All @@ -28,6 +31,8 @@ export function EditorDrawerProvider({ children }: PropsWithChildren) {
const [previewPageState, setPreviewPageState] = useState<IsomerComponent[]>(
[],
)
const [addedBlock, setAddedBlock] =
useState<Exclude<SectionType, "prose">>("button")

return (
<EditorDrawerContext.Provider
Expand All @@ -40,6 +45,8 @@ export function EditorDrawerProvider({ children }: PropsWithChildren) {
setSavedPageState,
previewPageState,
setPreviewPageState,
addedBlock,
setAddedBlock,
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function ComplexEditorStateDrawer(): JSX.Element {
return <></>
}

const component = savedPageState[currActiveIdx]
const component = previewPageState[currActiveIdx]

if (!component) {
return <></>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ export default function RootStateDrawer() {
ml="0.75rem"
/>
<Text px="3" fontWeight={500}>
{getComponentSchema(block.type).title}
{/* NOTE: Because we use `Type.Ref` for prose, */}
{/* this gets a `$Ref` only and not the concrete values */}
{block.type === "prose"
? "Prose component"
: getComponentSchema(block.type).title}
</Text>
</HStack>
<Divider />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,11 @@ import type { ProseProps } from "@opengovsg/isomer-components/dist/cjs/interface
import type { JSONContent } from "@tiptap/react"
import { Box, Text as ChakraText, Flex, Icon, VStack } from "@chakra-ui/react"
import { Button, IconButton } from "@opengovsg/design-system-react"
import { Blockquote } from "@tiptap/extension-blockquote"
import { Bold } from "@tiptap/extension-bold"
import { BulletList } from "@tiptap/extension-bullet-list"
import { Document } from "@tiptap/extension-document"
import { Dropcursor } from "@tiptap/extension-dropcursor"
import { Gapcursor } from "@tiptap/extension-gapcursor"
import { HardBreak } from "@tiptap/extension-hard-break"
import { Heading } from "@tiptap/extension-heading"
import { History } from "@tiptap/extension-history"
import { HorizontalRule } from "@tiptap/extension-horizontal-rule"
import { Italic } from "@tiptap/extension-italic"
import { ListItem } from "@tiptap/extension-list-item"
import { OrderedList } from "@tiptap/extension-ordered-list"
import { Paragraph } from "@tiptap/extension-paragraph"
import { Strike } from "@tiptap/extension-strike"
import { Subscript } from "@tiptap/extension-subscript"
import { Superscript } from "@tiptap/extension-superscript"
import TableCell from "@tiptap/extension-table-cell"
import TableHeader from "@tiptap/extension-table-header"
import TableRow from "@tiptap/extension-table-row"
import { Text } from "@tiptap/extension-text"
import Underline from "@tiptap/extension-underline"
import { EditorContent, useEditor } from "@tiptap/react"
import { cloneDeep } from "lodash"
import { BiText, BiX } from "react-icons/bi"

import { MenuBar } from "~/components/PageEditor/MenuBar"
import { useEditorDrawerContext } from "~/contexts/EditorDrawerContext"
import { Table } from "./extensions/Table"
import { TiptapEditor } from "./form-builder/renderers/TipTapEditor"

interface TipTapComponentProps {
content: ProseProps
Expand Down Expand Up @@ -58,61 +34,7 @@ function TipTapComponent({ content }: TipTapComponentProps) {
})
}

const editor = useEditor({
extensions: [
Blockquote,
Bold,
BulletList.extend({
name: "unorderedList",
}).configure({
HTMLAttributes: {
class: "list-disc",
},
}),
Document.extend({
name: "prose",
}),
Dropcursor,
Gapcursor,
HardBreak,
Heading.configure({
levels: [2, 3, 4, 6],
}),
History,
HorizontalRule.extend({
name: "divider",
}),
Italic,
ListItem,
OrderedList.extend({
name: "orderedList",
}).configure({
HTMLAttributes: {
class: "list-decimal",
},
}),
Paragraph,
Strike,
Superscript,
Subscript,
Table.configure({
resizable: false,
}),
TableRow,
TableHeader,
TableCell,
Text,
Underline,
],
content,
onUpdate: (e) => {
const jsonContent = e.editor.getJSON()
updatePageState(jsonContent)
},
})

// TODO: Add a loading state or use suspsense
if (!editor) return null
return (
<VStack bg="white" h="100%" gap="0">
<Flex
Expand Down Expand Up @@ -140,37 +62,8 @@ function TipTapComponent({ content }: TipTapComponentProps) {
}}
/>
</Flex>
<Box
p="2rem"
h="100%"
backgroundColor="gray.50"
flex="1"
maxH="calc(100vh - 12.875rem)"
>
<VStack
border="1px solid"
borderColor="base.divider.strong"
h="100%"
w="100%"
gap="0"
>
<MenuBar editor={editor} />
<Box
as={EditorContent}
editor={editor}
w="100%"
maxW="30vw"
flex="1 1 auto"
overflowX="hidden"
overflowY="auto"
px="2rem"
py="1rem"
h="100%"
backgroundColor="white"
onClick={() => editor.chain().focus().run()}
cursor="text"
/>
</VStack>
<Box w="100%" p="2rem" h="100%">
<TiptapEditor data={content} handleChange={updatePageState} />
</Box>
<Flex
px="2rem"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function FormBuilder(): JSX.Element {
return <></>
}

const component = savedPageState[currActiveIdx]
const component = previewPageState[currActiveIdx]

if (!component) {
return <></>
Expand Down
Loading

0 comments on commit 7c11803

Please sign in to comment.