mirror of
https://github.com/linsa-io/linsa.git
synced 2026-04-25 09:48:44 +02:00
fix: link (#115)
* start * . * seeding connections * . * wip * wip: learning state * wip: notes section * wip: many * topics * chore: update schema * update package * update sidebar * update page section * feat: profile * fix: remove z index * fix: wrong type * add avatar * add avatar * wip * . * store page section key * remove atom page section * fix rerender * fix rerender * fix rerender * fix rerender * fix link * search light/dark mode * bubble menu ui * . * fix: remove unecessary code * chore: mark as old for old schema * chore: adapt new schema * fix: add topic schema but null for now * fix: add icon on personal link * fix: list item * fix: set url fetched when editing * fix: remove image * feat: add icon to link * feat: custom url zod validation * fix: metadata test * chore: update utils * fix: link * fix: url fetcher * . * . * fix: add link, section * chore: seeder * . * . * . * . * fix: change checkbox to learning state * fix: click outside editing form * feat: constant * chore: move to master folder * chore: adapt new schema * chore: cli for new schema * fix: new schema for dev seed * fix: seeding * update package * chore: forcegraph seed * bottombar * if isEdit delete icon * showCreate X button * . * options * chore: implement topic from public global group * chore: update learning state * fix: change implementation for outside click * chore: implement new form param * chore: update env example * feat: link form refs exception * new page button layout, link topic search fixed * chore: enable topic * chore: update seed * profile * chore: move framer motion package from root to web and add nuqs * chore: add LearningStateValue * chore: implement active state * profile * chore: use fancy switch and update const * feat: filter implementation * dropdown menu * . * sidebar topics * topic selected color * feat: topic detail * fix: collapsible page * pages - sorted by, layout, visible mode * . * . * . * topic status sidebar * topic button and count * fix: topic * page delete/topic buttons * search ui * selected topic for page * selected topic status sidebar * removed footer * update package * . --------- Co-authored-by: Nikita <github@nikiv.dev> Co-authored-by: marshennikovaolga <marshennikova@gmail.com> Co-authored-by: Kisuyo <ig.intr3st@gmail.com>
This commit is contained in:
@@ -1,18 +1,26 @@
|
||||
"use client"
|
||||
|
||||
import React, { useCallback, useRef, useEffect } from "react"
|
||||
import { LAEditor, LAEditorRef } from "@/components/la-editor"
|
||||
// import { DetailPageHeader } from "./header" //dont need. check figma
|
||||
import * as React from "react"
|
||||
import { useAtom } from "jotai"
|
||||
import { ID } from "jazz-tools"
|
||||
import { PersonalPage } from "@/lib/schema/personal-page"
|
||||
import { PersonalPage, Topic } from "@/lib/schema"
|
||||
import { useCallback, useRef, useEffect, useState } 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"
|
||||
import { Paragraph } from "@/components/la-editor/extensions/paragraph"
|
||||
import { useAccount, useCoState } from "@/lib/providers/jazz-provider"
|
||||
import { toast } from "sonner"
|
||||
import { EditorView } from "prosemirror-view"
|
||||
import { EditorView } from "@tiptap/pm/view"
|
||||
import { Editor } from "@tiptap/core"
|
||||
import { generateUniqueSlug } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { LaIcon } from "@/components/custom/la-icon"
|
||||
import { pageTopicSelectorAtom } from "@/store/page"
|
||||
import { TopicSelector } from "@/components/routes/link/form/partial/topic-selector"
|
||||
import DeletePageModal from "@/components/custom/delete-modal"
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
||||
import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover"
|
||||
|
||||
const TITLE_PLACEHOLDER = "Page title"
|
||||
|
||||
@@ -25,7 +33,6 @@ export function DetailPageWrapper({ pageId }: { pageId: string }) {
|
||||
<div className="flex flex-row">
|
||||
<div className="flex h-full w-full">
|
||||
<div className="relative flex min-w-0 grow basis-[760px] flex-col">
|
||||
{/* <DetailPageHeader pageId={pageId as ID<PersonalPage>} /> */}
|
||||
<DetailPageForm page={page} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -33,11 +40,13 @@ export function DetailPageWrapper({ pageId }: { pageId: string }) {
|
||||
)
|
||||
}
|
||||
|
||||
const DetailPageForm = ({ page }: { page: PersonalPage }) => {
|
||||
export const DetailPageForm = ({ page }: { page: PersonalPage }) => {
|
||||
const { me } = useAccount()
|
||||
|
||||
const titleEditorRef = useRef<Editor | null>(null)
|
||||
const contentEditorRef = useRef<LAEditorRef>(null)
|
||||
const [, setTopicSelectorOpen] = useAtom(pageTopicSelectorAtom)
|
||||
const [selectedPageTopic, setSelectedPageTopic] = useState<Topic | null>(page.topic || null)
|
||||
const [deleteModalOpen, setDeleteModalOpen] = useState(false)
|
||||
|
||||
const updatePageContent = (content: Content, model: PersonalPage) => {
|
||||
model.content = content
|
||||
@@ -59,11 +68,11 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => {
|
||||
const personalPages = me.root?.personalPages?.toJSON() || []
|
||||
const slug = generateUniqueSlug(personalPages, page.slug)
|
||||
|
||||
const capitalizedTitle = newTitle.charAt(0).toUpperCase() + newTitle.slice(1)
|
||||
page.title = capitalizedTitle
|
||||
const trimmedTitle = editor.getText().trim()
|
||||
page.title = trimmedTitle
|
||||
page.slug = slug
|
||||
|
||||
editor.commands.setContent(capitalizedTitle)
|
||||
editor.commands.setContent(trimmedTitle)
|
||||
}
|
||||
|
||||
const handleTitleKeyDown = useCallback((view: EditorView, event: KeyboardEvent) => {
|
||||
@@ -74,44 +83,29 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => {
|
||||
const { selection } = state
|
||||
const { $anchor } = selection
|
||||
|
||||
switch (event.key) {
|
||||
case "ArrowRight":
|
||||
case "ArrowDown":
|
||||
if ($anchor.pos === state.doc.content.size - 1) {
|
||||
event.preventDefault()
|
||||
contentEditorRef.current?.editor?.commands.focus("start")
|
||||
return true
|
||||
}
|
||||
break
|
||||
case "Enter":
|
||||
if (!event.shiftKey) {
|
||||
event.preventDefault()
|
||||
contentEditorRef.current?.editor?.commands.focus("start")
|
||||
return true
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return false
|
||||
}, [])
|
||||
|
||||
const handleContentKeyDown = useCallback((view: EditorView, event: KeyboardEvent) => {
|
||||
const editor = contentEditorRef.current?.editor
|
||||
if (!editor) return false
|
||||
|
||||
const { state } = editor
|
||||
const { selection } = state
|
||||
const { $anchor } = selection
|
||||
|
||||
if ((event.key === "ArrowLeft" || event.key === "ArrowUp") && $anchor.pos - 1 === 0) {
|
||||
event.preventDefault()
|
||||
titleEditorRef.current?.commands.focus("end")
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}, [])
|
||||
|
||||
const handleContentKeyDown = useCallback((view: EditorView, event: KeyboardEvent) => {
|
||||
const editor = contentEditorRef.current?.editor
|
||||
if (!editor) return false
|
||||
const { state } = editor
|
||||
const { selection } = state
|
||||
const { $anchor } = selection
|
||||
return false
|
||||
}, [])
|
||||
|
||||
const confirmDelete = (page: PersonalPage) => {
|
||||
console.log("Deleting page:", page.id)
|
||||
setDeleteModalOpen(false)
|
||||
//TODO: add delete logic
|
||||
}
|
||||
|
||||
const titleEditor = useEditor({
|
||||
immediatelyRender: false,
|
||||
extensions: [
|
||||
@@ -159,11 +153,28 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => {
|
||||
<div tabIndex={0} className="relative flex grow flex-col overflow-y-auto">
|
||||
<div className="relative mx-auto flex h-full w-[calc(100%-40px)] shrink-0 grow flex-col sm:w-[calc(100%-80px)]">
|
||||
<form className="flex shrink-0 flex-col">
|
||||
<div className="mb-2 mt-8 py-1.5">
|
||||
<div className="mb-2 mt-8 flex flex-row justify-between py-1.5">
|
||||
<EditorContent
|
||||
editor={titleEditor}
|
||||
className="la-editor cursor-text select-text text-2xl font-semibold leading-[calc(1.33333)] tracking-[-0.00625rem]"
|
||||
/>
|
||||
<div className="items-center space-x-4">
|
||||
<TopicSelector
|
||||
onSelect={topic => {
|
||||
page.topic = topic
|
||||
setSelectedPageTopic(topic)
|
||||
setTopicSelectorOpen(false)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
className="text-foreground bg-truncat"
|
||||
onClick={() => setDeleteModalOpen(true)}
|
||||
>
|
||||
<LaIcon name="Trash" className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-auto flex-col">
|
||||
<div className="relative flex h-full max-w-full grow flex-col items-stretch p-0">
|
||||
@@ -183,6 +194,15 @@ const DetailPageForm = ({ page }: { page: PersonalPage }) => {
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<DeletePageModal
|
||||
isOpen={deleteModalOpen}
|
||||
onClose={() => setDeleteModalOpen(false)}
|
||||
onConfirm={() => {
|
||||
confirmDelete(page)
|
||||
}}
|
||||
title={page.title.charAt(0).toUpperCase() + page.title.slice(1)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user