fix: topic selector virtual (#142)

This commit is contained in:
Aslam
2024-09-06 07:31:22 +07:00
committed by GitHub
parent 1c01a18c89
commit 2bebcbc20a
3 changed files with 72 additions and 97 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -1,9 +1,8 @@
"use client"
import * as React from "react"
import React, { useCallback, useRef, useEffect } from "react"
import { ID } from "jazz-tools"
import { PersonalPage } from "@/lib/schema"
import { useCallback, useRef, useEffect } from "react"
import { LAEditor, LAEditorRef } from "@/components/la-editor"
import { Content, EditorContent, useEditor } from "@tiptap/react"
import { StarterKit } from "@/components/la-editor/extensions/starter-kit"
@@ -29,7 +28,6 @@ export function PageDetailRoute({ pageId }: { pageId: string }) {
const isMobile = useMedia("(max-width: 770px)")
const page = useCoState(PersonalPage, pageId as ID<PersonalPage>)
const router = useRouter()
const confirm = useConfirm()
const handleDelete = async () => {
@@ -38,17 +36,11 @@ export function PageDetailRoute({ pageId }: { pageId: string }) {
description: "Are you sure you want to delete this page?",
confirmText: "Delete",
cancelText: "Cancel",
cancelButton: {
variant: "outline"
},
confirmButton: {
variant: "destructive"
}
cancelButton: { variant: "outline" },
confirmButton: { variant: "destructive" }
})
if (result) {
if (!me?.root.personalPages) return
if (result && me?.root.personalPages) {
try {
const index = me.root.personalPages.findIndex(item => item?.id === pageId)
if (index === -1) {
@@ -56,22 +48,11 @@ export function PageDetailRoute({ pageId }: { pageId: string }) {
return
}
toast.success("Page deleted.", {
position: "bottom-right",
description: (
<span>
<strong>{page?.title}</strong> has been deleted.
</span>
)
})
me.root.personalPages.splice(index, 1)
// push without history
toast.success("Page deleted.", { position: "bottom-right" })
router.replace("/")
} catch (error) {
console.error("Delete operation fail", { error })
return
}
}
}
@@ -82,48 +63,49 @@ export function PageDetailRoute({ pageId }: { pageId: string }) {
<div className="absolute inset-0 flex flex-row overflow-hidden">
<div className="flex h-full w-full">
<div className="relative flex min-w-0 grow basis-[760px] flex-col">
<DetailPageHeader page={page} handleDelete={handleDelete} />
<DetailPageHeader page={page} handleDelete={handleDelete} isMobile={isMobile} />
<DetailPageForm page={page} />
</div>
{!isMobile && (
<div className="relative min-w-56 max-w-72 border-l">
<div className="flex">
<div className="flex h-10 flex-auto flex-row items-center justify-between px-5">
<span className="text-left text-[13px] font-medium">Page actions</span>
</div>
<div className="absolute bottom-0 left-0 right-0 top-10 space-y-3 overflow-y-auto px-4 py-1.5">
<TopicSelector
value={page.topic?.name}
onTopicChange={topic => {
page.topic = topic
page.updatedAt = new Date()
}}
variant="ghost"
className="-ml-1.5"
renderSelectedText={() => (
<span className="truncate">{page.topic?.prettyName || "Select a topic"}</span>
)}
/>
<Button size="sm" variant="ghost" onClick={handleDelete} className="-ml-1.5">
<LaIcon name="Trash" className="mr-2 size-3.5" />
<span className="text-sm">Delete</span>
</Button>
</div>
</div>
</div>
)}
{!isMobile && <SidebarActions page={page} handleDelete={handleDelete} />}
</div>
</div>
)
}
export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
const SidebarActions = ({ page, handleDelete }: { page: PersonalPage; handleDelete: () => void }) => (
<div className="relative min-w-56 max-w-72 border-l">
<div className="flex">
<div className="flex h-10 flex-auto flex-row items-center justify-between px-5">
<span className="text-left text-[13px] font-medium">Page actions</span>
</div>
<div className="absolute bottom-0 left-0 right-0 top-10 space-y-3 overflow-y-auto px-4 py-1.5">
<div className="flex flex-row">
<TopicSelector
value={page.topic?.name}
onTopicChange={topic => {
page.topic = topic
page.updatedAt = new Date()
}}
variant="ghost"
className="-ml-1.5"
renderSelectedText={() => <span className="truncate">{page.topic?.prettyName || "Select a topic"}</span>}
/>
</div>
<div className="flex flex-row">
<Button size="sm" variant="ghost" onClick={handleDelete} className="-ml-1.5">
<LaIcon name="Trash" className="mr-2 size-3.5" />
<span className="text-sm">Delete</span>
</Button>
</div>
</div>
</div>
</div>
)
const DetailPageForm = ({ page }: { page: PersonalPage }) => {
const { me } = useAccount()
const titleEditorRef = useRef<Editor | null>(null)
const contentEditorRef = useRef<LAEditorRef>(null)
const isTitleInitialMount = useRef(true)
const isContentInitialMount = useRef(true)
@@ -132,7 +114,6 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
isContentInitialMount.current = false
return
}
model.content = content
model.updatedAt = new Date()
}
@@ -143,13 +124,10 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
return
}
const personalPages = me?.root?.personalPages?.toJSON() || []
const newTitle = editor.getText()
// Only update if the title has actually changed
if (newTitle !== page.title) {
const personalPages = me?.root?.personalPages?.toJSON() || []
const slug = generateUniqueSlug(personalPages, page.slug || "")
page.title = newTitle
page.slug = slug
page.updatedAt = new Date()
@@ -181,7 +159,6 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
}
break
}
return false
}, [])
@@ -198,7 +175,6 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
titleEditorRef.current?.commands.focus("end")
return true
}
return false
}, [])
@@ -217,9 +193,7 @@ export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
strike: false,
focus: false,
gapcursor: false,
placeholder: {
placeholder: TITLE_PLACEHOLDER
}
placeholder: { placeholder: TITLE_PLACEHOLDER }
})
],
editorProps: {

View File

@@ -1,42 +1,43 @@
"use client"
import * as React from "react"
import React from "react"
import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header"
import { PersonalPage } from "@/lib/schema/personal-page"
import { useMedia } from "react-use"
import { TopicSelector } from "@/components/custom/topic-selector"
import { Button } from "@/components/ui/button"
import { LaIcon } from "@/components/custom/la-icon"
export const DetailPageHeader = ({ page, handleDelete }: { page: PersonalPage; handleDelete: () => void }) => {
const isMobile = useMedia("(max-width: 770px)")
interface DetailPageHeaderProps {
page: PersonalPage
handleDelete: () => void
isMobile: boolean
}
export const DetailPageHeader: React.FC<DetailPageHeaderProps> = ({ page, handleDelete, isMobile }) => {
if (!isMobile) return null
return (
isMobile && (
<>
<ContentHeader className="lg:min-h-0">
<div className="flex min-w-0 gap-2">
<SidebarToggleButton />
</div>
</ContentHeader>
<div className="flex flex-row items-start gap-1.5 border-b px-6 py-2 max-lg:pl-4">
<TopicSelector
value={page.topic?.name}
onTopicChange={topic => {
page.topic = topic
page.updatedAt = new Date()
}}
align="start"
variant="outline"
renderSelectedText={() => <span className="truncate">{page.topic?.prettyName || "Select a topic"}</span>}
/>
<Button size="sm" variant="outline" onClick={handleDelete}>
<LaIcon name="Trash" className="mr-2 size-3.5" />
Delete
</Button>
<>
<ContentHeader className="lg:min-h-0">
<div className="flex min-w-0 gap-2">
<SidebarToggleButton />
</div>
</>
)
</ContentHeader>
<div className="flex flex-row items-start gap-1.5 border-b px-6 py-2 max-lg:pl-4">
<TopicSelector
value={page.topic?.name}
onTopicChange={topic => {
page.topic = topic
page.updatedAt = new Date()
}}
align="start"
variant="outline"
renderSelectedText={() => <span className="truncate">{page.topic?.prettyName || "Select a topic"}</span>}
/>
<Button size="sm" variant="outline" onClick={handleDelete}>
<LaIcon name="Trash" className="mr-2 size-3.5" />
Delete
</Button>
</div>
</>
)
}