Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tiptap): blocks editor #1656

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6f69f07
fix(tablebubblemenu): prevent overlap
seaerchin Oct 26, 2023
5dcb97c
refactor(misc): updated editable and usedrag
seaerchin Oct 27, 2023
2a87cc1
feat(blocks): add menu bars + bubble menus
seaerchin Oct 27, 2023
03be205
refactor(editpage): add new variant
seaerchin Oct 27, 2023
9a357f3
feat(blocks): add constants and types
seaerchin Oct 27, 2023
d96b688
feat(blocks): add components
seaerchin Oct 27, 2023
368ecf0
fix(blockseditpage): style
seaerchin Oct 27, 2023
d167857
feat(blocks): added blocks in tiptap
seaerchin Oct 27, 2023
5bfa1cc
feat(tiptap-blocks): add blocks in tiptap
seaerchin Oct 28, 2023
1d68253
refactor(tiptap-blocks): move to diff folder
seaerchin Oct 28, 2023
5cfcc7a
refactor(extensions): move to own folder
seaerchin Oct 28, 2023
c6d6c7d
refactor(draggableview): update to dynamically toggle
seaerchin Oct 28, 2023
2f5e1e9
feat(editable): editpage
seaerchin Oct 28, 2023
e05ab8a
refactor(constants): update how it works
seaerchin Oct 29, 2023
afa938d
feat(views): add view and trim
seaerchin Oct 29, 2023
17d3102
fix(constants): trim url
seaerchin Oct 29, 2023
6a9ba14
refactor(editpage): add to end
seaerchin Oct 29, 2023
d097d4f
refactor(editpagelayotu): disable save
seaerchin Oct 30, 2023
0f4e5cc
fix(menu): dont lose focus
seaerchin Oct 31, 2023
29c48be
refactor(mvp): remove other stuff
seaerchin Nov 7, 2023
f7fc6c7
fix(editpagelayout): add back missing check
seaerchin Nov 7, 2023
f8d87fb
refactor(blockscontext): remove extra props
seaerchin Nov 7, 2023
135c23b
refactor(tablebubblemenu): delete unused as dup
seaerchin Nov 7, 2023
23c3d90
refactor(editpage): remove extra variants
seaerchin Nov 7, 2023
bdce5a7
refactor(preview): change 100% -> 100vh
seaerchin Nov 7, 2023
26eb6a2
refactor(editor): shift to blocks editor
seaerchin Nov 8, 2023
b476917
refactor(menuitem): remove editable toggling
seaerchin Nov 9, 2023
fc4e0c9
refactor(menubar): removed unused items
seaerchin Nov 9, 2023
fad62cb
refactor(menuitem): direct return
seaerchin Nov 9, 2023
ef30330
refactor(extensions): remove iseditable
seaerchin Nov 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions src/components/Editable/Editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const getDraggableAccordionItemStyle = ({
interface SidebarHeaderProps {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe can add ss in pr desc + test plan including common steps to test if all working

title: string
}
const SidebarHeader = ({ title }: SidebarHeaderProps) => {
export const SidebarHeader = ({ title }: SidebarHeaderProps) => {
return (
<Flex
w="100%"
Expand All @@ -97,7 +97,7 @@ export const CustomiseSectionsHeader = () => (
</>
)

interface EmptySectionProps {
interface EmptySectionProps extends FlexProps {
image?: JSX.Element
title: string
subtitle: string
Expand All @@ -109,13 +109,15 @@ export const EmptySection = ({
title,
subtitle,
isEmpty,
...rest
}: PropsWithChildren<EmptySectionProps>) => {
return isEmpty ? (
<Flex
alignItems="center"
flexDir="column"
p="3.75rem 1.5rem"
justifyContent="center"
{...rest}
>
{image}
<Text
Expand Down Expand Up @@ -181,6 +183,7 @@ type DroppableZone =
| HomepageDroppableZone
| ContactUsDroppableZone
| NavDroppableZone
| "Blocks"

type DropInfo = {
droppableId: DroppableZone
Expand Down Expand Up @@ -293,10 +296,11 @@ interface DraggableAccordionItemProps {
// TODO: Should get these props automatically
// rather than having us pass in manually
index: number
draggableId: string
draggableId?: string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this means not everything is draggable now?

isInvalid?: boolean
isNested?: boolean
}

// NOTE: Separating editable/draggable
// due to semantics on `Draggables`
/**
Expand Down Expand Up @@ -465,7 +469,11 @@ const DraggableAccordionItem = ({
)
}

const EditableAccordion = (props: AccordionProps) => {
const EditableAccordion = (
props: Omit<AccordionProps, "allowMultiple" | "onChange"> & {
onChange?: (idx: number) => void
}
) => {
return <Accordion allowToggle bg="base.canvas.default" {...props} />
}

Expand Down
3 changes: 2 additions & 1 deletion src/components/pages/PagePreview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ const PagePreview = ({ title, chunk, ...rest }) => {

return (
<Box
w="50%"
w="100%"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this 100% now?

h="100%"
maxH="100vh"
bg="white"
overflowY="auto"
className={editorStyles.pageEditorMain}
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useDrag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {

const RADIX_PARSE_INT = 10

const updatePositions = <T,>(
export const updatePositions = <T,>(
section: T[],
source: number,
destination: number,
Expand Down
39 changes: 39 additions & 0 deletions src/layouts/EditPage/BlocksEditPage/BlocksContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { PropsWithChildren, createContext, useContext, useState } from "react"

type BlocksTab = "content" | "add" | "edit"

interface UseBlocksReturn {
curTab: BlocksTab
showAddView: () => void
showContentView: () => void
}

const BlocksContext = createContext<null | UseBlocksReturn>(null)

export const useBlocks = (): UseBlocksReturn => {
const blocksContext = useContext(BlocksContext)
if (!blocksContext)
throw new Error(
"useBlocksContext must be used within an BlocksContextProvider"
)

return blocksContext
}

export const BlocksContextProvider = ({
children,
}: PropsWithChildren<unknown>): JSX.Element => {
const [curTab, setCurTab] = useState<BlocksTab>("content")

return (
<BlocksContext.Provider
value={{
showAddView: () => setCurTab("add"),
showContentView: () => setCurTab("content"),
curTab,
}}
>
{children}
</BlocksContext.Provider>
)
}
196 changes: 196 additions & 0 deletions src/layouts/EditPage/BlocksEditPage/BlocksEditPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import {
Card,
Flex,
SimpleGrid,
Text,
VStack,
CardHeader,
CardBody,
CardFooter,
Icon,
Grid,
GridItem,
ButtonGroup,
} from "@chakra-ui/react"
import {
IconButton,
Button,
Infobox,
Textarea,
} from "@opengovsg/design-system-react"
import _ from "lodash"
import { useState } from "react"
import { useForm } from "react-hook-form"
import { BiGridAlt, BiPlus } from "react-icons/bi"
import { useParams } from "react-router-dom"

import { Editable } from "components/Editable"
import PagePreview from "components/pages/PagePreview"

import { EditorContextProvider, useEditorContext } from "contexts/EditorContext"

import { useGetPageHook } from "hooks/pageHooks"

import { Editor } from "../../components/Editor/Editor"
import { EditPageLayout } from "../EditPageLayout"

import { useBlocks } from "./BlocksContext"
import { BLOCKS_CONTENT } from "./constants"
import { Preview } from "./Preview"
import { BlockAddView } from "./types"
import { usePreviewEditor } from "./useBlockEditor"

export const AddBlockView = () => {
const { curTab } = useBlocks()

if (curTab !== "add") return null

return (
<VStack spacing="1.25rem" p="1.25rem" h="100%" align="flex-start">
<Text textStyle="subhead-3">Content Blocks</Text>
<SimpleGrid columns={2} spacing="1.25rem">
{_.map(BLOCKS_CONTENT, (block) => (
<BlockContentCard {...block} />
))}
</SimpleGrid>
<Infobox>
We are slowly introducing new content types. Looking to add a specific
kind of content to your pages? Let us know here.
</Infobox>
</VStack>
)
}

export const BlockContentCard = ({
title,
description,
icon,
variant,
getContent,
}: BlockAddView & { getContent: (val: string) => string }) => {
const { showContentView } = useBlocks()
const { editor } = useEditorContext()
const [isEditable, setIsEditable] = useState(false)
const { register, handleSubmit } = useForm()
const onSave = handleSubmit((data) => {
showContentView()
const { size } = editor.view.state.doc.content
editor?.commands.insertContentAt(
size,
`<div data-type="draggable-item">${getContent(data[variant])}</div>`
)
})

return (
// TODO: Fix the styling for cards - have too much padding
<Card shadow="none" border="1px solid" borderColor="base.divider.medium">
<CardHeader>
<Flex flexDir="row" align="center">
<Icon fontSize="1.25rem" mr="0.5rem">
{icon({})}
</Icon>
<Text>{title}</Text>
</Flex>
</CardHeader>
<CardBody py={0}>
{isEditable ? (
<Textarea
{...register(variant)}
placeholder="Put your desired content here"
/>
) : (
<Text>{description}</Text>
)}
</CardBody>
<CardFooter justify="flex-end">
{isEditable ? (
<ButtonGroup>
<Button
variant="clear"
colorScheme="critical"
onClick={() => setIsEditable(false)}
>
Cancel
</Button>
<Button variant="clear" onClick={onSave}>
Add to page
</Button>
</ButtonGroup>
) : (
<Button variant="clear" onClick={() => setIsEditable(true)}>
Add to page
</Button>
)}
</CardFooter>
</Card>
)
}

export const BlocksEditPage = () => {
const params = useParams<{ siteName: string }>()
const { data: initialPageData, isLoading: isLoadingPage } = useGetPageHook(
params
)
const { curTab, showAddView, showContentView } = useBlocks()

const editor = usePreviewEditor()

if (!editor) return null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would this happen? is it on the first paint?


return (
<EditorContextProvider editor={editor}>
<EditPageLayout
setEditorContent={(content) => {
editor.commands.setContent(content)
}}
getEditorContent={() => editor.getHTML()}
variant="blocks"
>
{/* TODO: Add in icons at bottom for guide + settings etc */}
<VStack
px="0.5rem"
py="1rem"
bg="base.canvas.default"
h="100vh"
borderRight="1px solid"
borderColor="base.divider.medium"
>
<IconButton
variant="clear"
isActive={curTab === "content"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there is a list of tab and ids, can we namespace it like CurTabs.content?

aria-label="Show blocks"
onClick={showContentView}
>
<BiGridAlt />
</IconButton>
<IconButton
variant="clear"
aria-label="Show blocks"
isActive={curTab === "add"}
onClick={showAddView}
>
<BiPlus />
</IconButton>
</VStack>
<Grid templateColumns="36rem 1fr" w="100%" h="100%">
<GridItem>
<VStack h="100%">
<Editable.Sidebar title="Edit Content" w="36rem" h="100%" pb={0}>
{curTab !== "add" && (
<Editor editor={editor} maxW="100%" maxH="100vh" />
)}
<AddBlockView />
</Editable.Sidebar>
</VStack>
</GridItem>
{/* Preview */}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove?

<GridItem>
<Preview
title={initialPageData?.content?.frontMatter?.title || ""}
/>
</GridItem>
</Grid>
</EditPageLayout>
</EditorContextProvider>
)
}
60 changes: 60 additions & 0 deletions src/layouts/EditPage/BlocksEditPage/Preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Box } from "@chakra-ui/react"
import { PropsWithChildren } from "react"
import { useParams } from "react-router-dom"

import editorStyles from "styles/isomer-cms/pages/Editor.module.scss"

import { PageHeader, LeftNav } from "templates/pageComponentsV2"

import { getDecodedParams } from "utils"

interface PreviewProps {
title: string
}

interface CollectionPageRouteParams {
fileName: string
collectionName: string
}

export const Preview = ({
title,
children,
}: PropsWithChildren<PreviewProps>) => {
const params = useParams<CollectionPageRouteParams>()
const pageParams = getDecodedParams(
(params as unknown) as Record<string, string>
)
const { collectionName, fileName } = pageParams

return (
<Box
w="100%"
h="100vh"
bg="white"
overflowY="auto"
className={editorStyles.pageEditorMain}
>
<Box>
<section
id="display-header"
className="bp-section is-small bp-section-pagetitle"
>
<PageHeader pageParams={{ fileName, collectionName }} title={title} />
</section>
<section className="bp-section page-content-body">
<Box className="bp-container padding--top--lg padding--bottom--xl">
<Box className="row">
<LeftNav collectionName={collectionName} fileName={fileName} />
<Box
className={`${"col is-8 is-offset-1-desktop is-12-touch print-content page-content-body"}`}
>
{children}
</Box>
</Box>
</Box>
</section>
</Box>
</Box>
)
}
Loading
Loading