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: New Pages with Enhanced Document Editor Packages made over Editor Core 📝 #2784

Merged
merged 89 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 77 commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
5216f18
fix: page transaction model
NarayanBavisetti Oct 6, 2023
1039337
Merge branch 'develop' of github.com:makeplane/plane into fix/page_st…
NarayanBavisetti Oct 9, 2023
0302644
fix: page transaction model
NarayanBavisetti Oct 11, 2023
975464d
feat: updated ui for page route
henit-chobisa Oct 18, 2023
b83ae69
chore: initailized `document-editor` package for plane
henit-chobisa Oct 21, 2023
2935934
fix: format persistence while pasting markdown in editor
henit-chobisa Oct 26, 2023
340d871
feat: Inititalized Document-Editor and Editor with Ref
henit-chobisa Oct 26, 2023
39d8b2c
feat: added tooltip component and slash command for editor
henit-chobisa Oct 26, 2023
165b9b0
feat: added `document-editor` extensions
henit-chobisa Oct 26, 2023
0e26b4c
feat: added custom search component for embedding labels
henit-chobisa Oct 26, 2023
3fe8e88
feat: added top bar menu component
henit-chobisa Oct 26, 2023
f7b7583
feat: created document-editor exposed components
henit-chobisa Oct 26, 2023
731b83c
feat: integrated `document-editor` in `pages` route
henit-chobisa Oct 26, 2023
fbb5c24
chore: updated dependencies
henit-chobisa Oct 26, 2023
cc86d2c
Merge branch 'develop' into feat/document-editor
henit-chobisa Nov 10, 2023
40c5ecb
feat: merge conflict resolution
henit-chobisa Nov 10, 2023
e3396eb
chore: modified configuration for document editor
henit-chobisa Nov 10, 2023
8fb96a9
feat: added content browser menu for document editor summary
henit-chobisa Nov 10, 2023
e372c29
feat: added fixed menu and editor instances
henit-chobisa Nov 10, 2023
0fbd893
feat: added document edittor instances and summary table
henit-chobisa Nov 10, 2023
57f8ab2
feat: implemented document-editor in PageDetail
henit-chobisa Nov 10, 2023
513c6e1
chore: css and export fixes
henit-chobisa Nov 10, 2023
598c22f
Merge branch 'develop' of github.com:makeplane/plane into fix/page_st…
NarayanBavisetti Nov 13, 2023
73c2416
fix: migration and optimisation
NarayanBavisetti Nov 13, 2023
0ad7dcf
fix: added `on_create` hook in the core editor
henit-chobisa Nov 14, 2023
9d0d4d8
feat: added conditional menu bar action in document-editor
henit-chobisa Nov 14, 2023
95df971
feat: added menu actions from single page view
henit-chobisa Nov 14, 2023
f9a2453
Merge branch 'fix/page_structuring' into feat/document-editor
henit-chobisa Nov 14, 2023
77a98fe
feat: added services for archiving, unarchiving and retriving archive…
henit-chobisa Nov 14, 2023
61b13c1
feat: added services for page archives
henit-chobisa Nov 14, 2023
794d7cf
feat: implemented page archives in page list view
henit-chobisa Nov 14, 2023
cb6f76c
feat: implemented page archives in document-editor
henit-chobisa Nov 14, 2023
5f4b3e1
feat: added editor marking hook
henit-chobisa Nov 14, 2023
42258a9
chore: seperated editor header from the main content
henit-chobisa Nov 14, 2023
22699a3
chore: seperated editor summary utilities from the main editor
henit-chobisa Nov 14, 2023
ed51b73
chore: refactored necessary components from the document editor
henit-chobisa Nov 14, 2023
d1d6518
chore: removed summary sidebar component from the main content editor
henit-chobisa Nov 14, 2023
3efde4b
chore: removed scrollSummaryDependency from Header and Sidebar
henit-chobisa Nov 14, 2023
93598db
feat: seperated page renderer as a seperate component
henit-chobisa Nov 14, 2023
9cb018a
chore: seperated page_renderer and sidebar as component from index
henit-chobisa Nov 14, 2023
e1eb712
feat: added locked property to IPage type
henit-chobisa Nov 14, 2023
8ed18a5
feat: added lock/unlock services in page service
henit-chobisa Nov 14, 2023
3d27cee
chore: seperated DocumentDetails as exported interface from index
henit-chobisa Nov 14, 2023
a25d80f
feat: seperated document editor configs as seperate interfaces
henit-chobisa Nov 14, 2023
48744b1
chore: seperated menu options from the editor header component
henit-chobisa Nov 14, 2023
443f131
fix: fixed page_lock performing lock/unlock operation on queryset ins…
henit-chobisa Nov 15, 2023
c707fa7
fix: css positioning changes
henit-chobisa Nov 16, 2023
8184bf2
feat: added archive/lock alert labels
henit-chobisa Nov 16, 2023
f3fb945
feat: added boolean props in menu-actions/options
henit-chobisa Nov 16, 2023
f82a7b5
feat: added lock/unlock & archive/unarchive services
henit-chobisa Nov 16, 2023
1281ff9
feat: added on update mutations for archived pages in page-view
henit-chobisa Nov 16, 2023
c973b92
feat: added archive/lock on_update mutations in single page vieq
henit-chobisa Nov 16, 2023
230b79b
feat: exported readonly editor for locked pages
henit-chobisa Nov 16, 2023
06f4095
chore: seperated kanban menu props and saved over passing redundant data
henit-chobisa Nov 16, 2023
4968014
fix: readonly editor not generating markings on first render
henit-chobisa Nov 16, 2023
e38f3cb
fix: cheveron overflowing from editor-header
henit-chobisa Nov 16, 2023
29a5ae7
chore: removed unused utility actions
henit-chobisa Nov 16, 2023
a68dcd7
fix: enabled sidebar view by default
henit-chobisa Nov 16, 2023
140dd2c
feat: removed locking on pages in archived state
henit-chobisa Nov 16, 2023
a7e8af4
feat: added indentation in heading component
henit-chobisa Nov 16, 2023
eeb7173
fix: button classnames in vertical dropdowns
henit-chobisa Nov 16, 2023
4d6315e
feat: added `last_archived_at` and `last_edited_at` details in editor…
henit-chobisa Nov 16, 2023
16a764d
feat: changed types for archived updates and document last updates
henit-chobisa Nov 16, 2023
ae81656
feat: updated editor and header props
henit-chobisa Nov 16, 2023
8c7491d
Merge branch 'develop' into feat/document-editor
henit-chobisa Nov 16, 2023
2286c67
feat: updated queryset according to new page query format
henit-chobisa Nov 16, 2023
6a1795d
feat: added parameters in page view for shared / private pages
henit-chobisa Nov 17, 2023
5e706ee
feat: updated other-page-view to shared page view && same with privat…
henit-chobisa Nov 17, 2023
f605489
feat: added page-view as shared / private
henit-chobisa Nov 17, 2023
18ed2d5
fix: replaced deleting to archiving for pages
henit-chobisa Nov 17, 2023
ffbcd96
feat: handle restoring of page from archived section from list view
henit-chobisa Nov 17, 2023
742f5b0
feat: made previledge based option render for pages
henit-chobisa Nov 17, 2023
436e655
feat: removed layout view for page list view
henit-chobisa Nov 17, 2023
5b5fdfe
feat: linting changes
henit-chobisa Nov 17, 2023
b9ebdc2
fix: merge conflicts resolved
sriramveeraghanta Nov 18, 2023
91da7dd
fix: adding mobx changes to pages
sriramveeraghanta Nov 19, 2023
6606479
fix: removed uneccessary migrations
sriramveeraghanta Nov 19, 2023
56a5ebc
fix: mobx store changes
sriramveeraghanta Nov 20, 2023
1ceb727
fix: adding date-fns pacakge
sriramveeraghanta Nov 20, 2023
ae3e8b2
fix: merge conflicts resolved
sriramveeraghanta Nov 20, 2023
d65363b
fix: updating yarn lock
sriramveeraghanta Nov 20, 2023
3491933
fix: removing unneccessary method params
sriramveeraghanta Nov 20, 2023
e8cae94
chore: added access specifier to the create/update page modal
aaryan610 Nov 20, 2023
0ae89d4
fix: tab view layout changes
sriramveeraghanta Nov 20, 2023
545434e
chore: delete endpoint for page
NarayanBavisetti Nov 20, 2023
eaa4991
fix: page actions, including- archive, favorite, access control, delete
aaryan610 Nov 20, 2023
94851e5
chore: remove archive page modal
aaryan610 Nov 20, 2023
deafbc8
Merge branch 'develop' into feat/document-editor
sriramveeraghanta Nov 20, 2023
e932c4a
fix: build errors
aaryan610 Nov 20, 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
27 changes: 15 additions & 12 deletions apiserver/plane/api/views/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,12 @@ def partial_update(self, request, slug, project_id, pk):
status=status.HTTP_400_BAD_REQUEST,
)

def lock(self, request, slug, project_id, pk):

def lock(self, request, slug, project_id, page_id):

page = Page.objects.filter(
pk=pk, workspace__slug=slug, project_id=project_id
)
pk=page_id, workspace__slug=slug, project_id=project_id
).first()

# only the owner can lock the page
if request.user.id != page.owned_by_id:
Expand All @@ -163,8 +165,12 @@ def lock(self, request, slug, project_id, pk):
page.save()
return Response(status=status.HTTP_204_NO_CONTENT)

def unlock(self, request, slug, project_id, pk):
page = Page.objects.get(pk=pk, workspace__slug=slug, project_id=project_id)
def unlock(self, request, slug, project_id, page_id):

page = Page.objects.filter(
pk=page_id, workspace__slug=slug, project_id=project_id
).first()


# only the owner can unlock the page
if request.user.id != page.owned_by_id:
Expand Down Expand Up @@ -223,16 +229,14 @@ def list(self, request, slug, project_id):
PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK
)

# My pages
if page_view == "created_by_me":
queryset = queryset.filter(owned_by=request.user)
if page_view == "shared":
queryset = queryset.filter(owned_by=request.user, access=0)
return Response(
PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK
)

# Created by other Pages
if page_view == "created_by_other":
queryset = queryset.filter(~Q(owned_by=request.user), access=0)
if page_view == "private":
queryset = queryset.filter(owned_by=request.user, access=1)
return Response(
PageSerializer(queryset, many=True).data, status=status.HTTP_200_OK
)
Expand Down Expand Up @@ -417,4 +421,3 @@ def get(self, request, slug, project_id, page_id):
return Response(
SubPageSerializer(pages, many=True).data, status=status.HTTP_200_OK
)

1 change: 1 addition & 0 deletions apiserver/plane/db/models/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ def save(self, *args, **kwargs):
except ImportError:
pass


if self._state.adding:
# Get the maximum display_id value from the database
last_id = IssueSequence.objects.filter(project=self.project).aggregate(
Expand Down
5 changes: 5 additions & 0 deletions packages/editor/core/src/ui/hooks/useEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface CustomEditorProps {
value: string;
deleteFile: DeleteImage;
debouncedUpdatesEnabled?: boolean;
onStart?: (json: any, html: string) => void;
onChange?: (json: any, html: string) => void;
extensions?: any;
editorProps?: EditorProps;
Expand All @@ -34,6 +35,7 @@ export const useEditor = ({
editorProps = {},
value,
extensions = [],
onStart,
onChange,
setIsSubmitting,
forwardedRef,
Expand All @@ -60,6 +62,9 @@ export const useEditor = ({
],
content:
typeof value === "string" && value.trim() !== "" ? value : "<p></p>",
onCreate: async ({ editor }) => {
onStart?.(editor.getJSON(), getTrimmedHTML(editor.getHTML()))
},
onUpdate: async ({ editor }) => {
// for instant feedback loop
setIsSubmitting?.("submitting");
Expand Down
1 change: 1 addition & 0 deletions packages/editor/document-editor/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Document Editor
73 changes: 73 additions & 0 deletions packages/editor/document-editor/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"name": "@plane/document-editor",
"version": "0.0.1",
"description": "Package that powers Plane's Pages Editor",
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"files": [
"dist/**/*"
],
"exports": {
".": {
"types": "./dist/index.d.mts",
"import": "./dist/index.mjs",
"module": "./dist/index.mjs"
}
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"check-types": "tsc --noEmit"
},
"peerDependencies": {
"next": "12.3.2",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-dom": "18.2.0"
},
"dependencies": {
"@headlessui/react": "^1.7.17",
"@plane/ui": "*",
"@plane/editor-core": "*",
"@popperjs/core": "^2.11.8",
"@tiptap/core": "^2.1.7",
"@tiptap/extension-code-block-lowlight": "^2.1.11",
"@tiptap/extension-horizontal-rule": "^2.1.11",
"@tiptap/extension-list-item": "^2.1.11",
"@tiptap/extension-placeholder": "^2.1.11",
"@tiptap/suggestion": "^2.1.7",
"@types/node": "18.15.3",
"@types/react": "^18.2.5",
"@types/react-dom": "18.0.11",
"class-variance-authority": "^0.7.0",
"clsx": "^1.2.1",
"eslint": "8.36.0",
"eslint-config-next": "13.2.4",
"eventsource-parser": "^0.1.0",
"highlight.js": "^11.8.0",
"lowlight": "^3.0.0",
"lucide-react": "^0.244.0",
"react-markdown": "^8.0.7",
"react-popper": "^2.3.0",
"tailwind-merge": "^1.14.0",
"tippy.js": "^6.3.7",
"tiptap-markdown": "^0.8.2",
"use-debounce": "^9.0.4"
},
"devDependencies": {
"eslint": "^7.32.0",
"postcss": "^8.4.29",
"tailwind-config-custom": "*",
"tsconfig": "*",
"tsup": "^7.2.0",
"typescript": "4.9.5"
},
"keywords": [
"editor",
"rich-text",
"markdown",
"nextjs",
"react"
]
}
9 changes: 9 additions & 0 deletions packages/editor/document-editor/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// If you want to use other PostCSS plugins, see the following:
// https://tailwindcss.com/docs/using-with-preprocessors

module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
3 changes: 3 additions & 0 deletions packages/editor/document-editor/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { DocumentEditor, DocumentEditorWithRef } from "./ui"
export { DocumentReadOnlyEditor, DocumentReadOnlyEditorWithRef } from "./ui/readonly"
export { FixedMenu } from "./ui/menu/fixed-menu"
19 changes: 19 additions & 0 deletions packages/editor/document-editor/src/ui/components/alert-label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Icon } from "lucide-react"

interface IAlertLabelProps {
Icon: Icon,
backgroundColor: string,
textColor?: string,
label: string,
}

export const AlertLabel = ({ Icon, backgroundColor,textColor, label }: IAlertLabelProps) => {

return (
<div className={`text-xs flex items-center gap-1 ${backgroundColor} p-0.5 pl-3 pr-3 mr-1 rounded`}>
<Icon size={12} />
<span className={`normal-case ${textColor}`}>{label}</span>
</div>
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { HeadingComp, SubheadingComp } from "./heading-component";
import { IMarking } from "..";
import { Editor } from "@tiptap/react";
import { scrollSummary } from "../utils/editor-summary-utils";

interface ContentBrowserProps {
editor: Editor;
markings: IMarking[];
}

export const ContentBrowser = ({
editor,
markings,
}: ContentBrowserProps) => (
<div className="mt-4 flex w-[250px] flex-col h-full">
<h2 className="ml-4 border-b border-solid border-custom-border py-5 font-medium leading-[85.714%] tracking-tight max-md:ml-2.5">
Table of Contents
</h2>
<div className="mt-3 h-0.5 w-full self-stretch border-custom-border" />
{markings.length !== 0 ? (
markings.map((marking) =>
marking.level === 1 ? (
<HeadingComp
onClick={() => scrollSummary(editor, marking)}
heading={marking.text}
/>
) : (
<SubheadingComp
onClick={() => scrollSummary(editor, marking)}
subHeading={marking.text}
/>
)
)
) : (
<p className="ml-3 mr-3 flex h-full items-center px-5 text-center text-xs text-gray-500">
{"Headings will be displayed here for Navigation"}
</p>
)}
</div>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Editor } from "@tiptap/react"
import { Lock, ArchiveIcon, MenuSquare } from "lucide-react"
import { useRef, useState } from "react"
import { usePopper } from "react-popper"
import { IMarking, UploadImage } from ".."
import { FixedMenu } from "../menu"
import { DocumentDetails } from "../types/editor-types"
import { AlertLabel } from "./alert-label"
import { ContentBrowser } from "./content-browser"
import { IVerticalDropdownItemProps, VerticalDropdownMenu } from "./vertical-dropdown-menu"

interface IEditorHeader {
editor: Editor,
KanbanMenuOptions: IVerticalDropdownItemProps[],
sidePeakVisible: boolean,
setSidePeakVisible: (currentState: boolean) => void,
markings: IMarking[],
isLocked: boolean,
isArchived: boolean,
archivedAt?: Date,
readonly: boolean,
uploadFile?: UploadImage,
setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void,
documentDetails: DocumentDetails
}

export const EditorHeader = ({ documentDetails, archivedAt, editor, sidePeakVisible, readonly, setSidePeakVisible, markings, uploadFile, setIsSubmitting, KanbanMenuOptions, isArchived, isLocked }: IEditorHeader) => {

const summaryMenuRef = useRef(null);
const summaryButtonRef = useRef(null);
const [summaryPopoverVisible, setSummaryPopoverVisible] = useState(false);

const { styles: summaryPopoverStyles, attributes: summaryPopoverAttributes } = usePopper(summaryButtonRef.current, summaryMenuRef.current, {
placement: "bottom-start"
})

return (

<div className="border-custom-border self-stretch flex flex-col border-b border-solid max-md:max-w-full">
<div
className="self-center flex ml-0 w-full items-start justify-between gap-5 max-md:max-w-full max-md:flex-wrap max-md:justify-center">
<div className={"flex flex-row items-center"}>
<div
onMouseEnter={() => setSummaryPopoverVisible(true)}
onMouseLeave={() => setSummaryPopoverVisible(false)}
>
<button
ref={summaryButtonRef}
className={"p-2 text-custom-text-300 hover:bg-custom-primary-100/5 active:bg-custom-primary-100/5 transition-colors"}
onClick={() => {
setSidePeakVisible(!sidePeakVisible)
setSummaryPopoverVisible(false)
}}
>
<MenuSquare
size={20}
/>
</button>
{summaryPopoverVisible &&
<div style={summaryPopoverStyles.popper} {...summaryPopoverAttributes.popper} className="z-10 h-[300px] w-[300px] ml-[40px] mt-[40px] shadow-xl rounded border-custom-border border-solid border-2 bg-custom-background-100 border-b pl-3 pr-3 pb-3 overflow-scroll">
<ContentBrowser editor={editor} markings={markings} />
</div>
}
</div>
{isLocked && <AlertLabel Icon={Lock} backgroundColor={"bg-red-200"} label={"Locked"} />}
{(isArchived && archivedAt) && <AlertLabel Icon={ArchiveIcon} backgroundColor={"bg-blue-200"} label={`Archived at ${new Date(archivedAt).toLocaleString()}`} />}
</div>

{(!readonly && uploadFile) && <FixedMenu editor={editor} uploadFile={uploadFile} setIsSubmitting={setIsSubmitting} />}
<div className="self-center flex items-start gap-3 my-auto max-md:justify-center"
>
{!isArchived && <p className="text-sm text-custom-text-300">{`Last updated at ${new Date(documentDetails.last_updated_at).toLocaleString()}`}</p>}
<VerticalDropdownMenu items={KanbanMenuOptions} />
</div>
</div>
</div>
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const HeadingComp = ({
heading,
onClick,
}: {
heading: string;
onClick: (event: React.MouseEvent<HTMLParagraphElement, MouseEvent>) => void;
}) => (
<h3
onClick={onClick}
className="ml-4 mt-3 cursor-pointer text-sm font-bold font-medium leading-[125%] tracking-tight hover:text-custom-primary max-md:ml-2.5"
>
{heading}
</h3>
);

export const SubheadingComp = ({
subHeading,
onClick,
}: {
subHeading: string;
onClick: (event: React.MouseEvent<HTMLParagraphElement, MouseEvent>) => void;
}) => (
<p
onClick={onClick}
className="ml-6 mt-2 text-xs cursor-pointer font-medium tracking-tight text-gray-400 hover:text-custom-primary"
>
{subHeading}
</p>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { EditorContainer, EditorContentWrapper } from "@plane/editor-core"
import { Editor } from "@tiptap/react"
import { DocumentDetails } from "../types/editor-types"

interface IPageRenderer {
sidePeakVisible: boolean,
documentDetails: DocumentDetails ,
editor: Editor,
editorClassNames: string,
editorContentCustomClassNames?: string
}

export const PageRenderer = ({ sidePeakVisible, documentDetails, editor, editorClassNames, editorContentCustomClassNames }: IPageRenderer) => {
return (
<div className={`flex h-[88vh] flex-col w-full max-md:w-full max-md:ml-0 transition-all duration-200 ease-in-out ${sidePeakVisible ? 'ml-[3%] ' : 'ml-0'}`}>
<div className="items-start mt-4 h-full flex flex-col w-fit max-md:max-w-full overflow-auto">
<div className="flex flex-col py-2 max-md:max-w-full">
<h1
className="border-none outline-none bg-transparent text-4xl font-bold leading-8 tracking-tight self-center w-[700px] max-w-full"
>{documentDetails.title}</h1>
</div>
<div className="border-custom-border border-b border-solid self-stretch w-full h-0.5 mt-3" />
<div className="flex flex-col max-md:max-w-full">
<EditorContainer editor={editor} editorClassNames={editorClassNames}>
<div className="flex flex-col">
<EditorContentWrapper editor={editor} editorContentCustomClassNames={editorContentCustomClassNames} />
</div>
</EditorContainer >
</div>
</div>
</div>
)
}
Loading
Loading