mirror of
https://github.com/linsa-io/linsa.git
synced 2026-01-12 12:20:23 +01:00
Setup (#112)
* wip * wip * wip3 * chore: utils * feat: add command * wip * fix: key duplicate * fix: move and check * fix: use react-use instead * fix: sidebar * chore: make dynamic * chore: tablet mode * chore: fix padding * chore: link instead of inbox * fix: use dnd kit * feat: add select component * chore: use atom * refactor: remove dnd provider * feat: disabled drag when sort is not manual * search route * . * feat: accessibility * fix: search * . * . * . * fix: sidebar collapsed * ai search layout * . * . * . * . * ai responsible content * . * . * . * . * . * global topic route * global topic correct route * topic buttons * sidebar search navigation * ai * Update jazz * . * . * . * . * . * learning status * . * . * chore: content header * fix: pointer none when dragging. prevent auto click after drag end * fix: confirm * fix: prevent drag when editing * chore: remove unused fn * fix: check propagation * chore: list * chore: tweak sonner * chore: update stuff * feat: add badge * chore: close edit when create * chore: escape on manage form * refactor: remove learn path * css: responsive item * chore: separate pages and topic * reafactor: remove new-schema * feat(types): extend jazz type so it can be nullable * chore: use new types * fix: missing deps * fix: link * fix: sidebar in layout * fix: quotes * css: use medium instead semi * Actual streaming and rendering markdown response * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * . * chore: metadata * feat: la-editor * . * fix: editor and page * . * . * . * . * . * . * fix: remove link * chore: page sidebar * fix: remove 'replace with learning status' * fix: link * fix: link * chore: update schema * chore: use new schema * fix: instead of showing error, just do unique slug * feat: create slug * refactor apply * update package json * fix: schema personal page * chore: editor * feat: pages * fix: metadata * fix: jazz provider * feat: handling data * feat: page detail * chore: server page to id * chore: use id instead of slug * chore: update content header * chore: update link header implementation * refactor: global.css * fix: la editor use animation frame * fix: editor export ref * refactor: page detail * chore: tidy up schema * chore: adapt to new schema * fix: wrap using settimeout * fix: wrap using settimeout * . * . --------- Co-authored-by: marshennikovaolga <marshennikova@gmail.com> Co-authored-by: Nikita <github@nikiv.dev> Co-authored-by: Anselm <anselm.eickhoff@gmail.com> Co-authored-by: Damian Tarnawski <gthetarnav@gmail.com>
This commit is contained in:
34
web/components/routes/page/detail/header.tsx
Normal file
34
web/components/routes/page/detail/header.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { ContentHeader, SidebarToggleButton } from "@/components/custom/content-header"
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator
|
||||
} from "@/components/ui/breadcrumb"
|
||||
import { useCoState } from "@/lib/providers/jazz-provider"
|
||||
import { PersonalPage } from "@/lib/schema/personal-page"
|
||||
import { ID } from "jazz-tools"
|
||||
|
||||
export const DetailPageHeader = ({ pageId }: { pageId: ID<PersonalPage> }) => {
|
||||
const page = useCoState(PersonalPage, pageId)
|
||||
|
||||
return (
|
||||
<ContentHeader>
|
||||
<div className="flex min-w-0 gap-2">
|
||||
<SidebarToggleButton />
|
||||
|
||||
<Breadcrumb className="flex flex-row items-center">
|
||||
<BreadcrumbList className="sm:gap-2">
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage className="text-foreground font-medium">Pages</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
</ContentHeader>
|
||||
)
|
||||
}
|
||||
139
web/components/routes/page/detail/wrapper.tsx
Normal file
139
web/components/routes/page/detail/wrapper.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
"use client"
|
||||
|
||||
import React, { useEffect, useRef } from "react"
|
||||
import { LAEditor, LAEditorRef } from "@/components/la-editor"
|
||||
import { DetailPageHeader } from "./header"
|
||||
import { ID } from "jazz-tools"
|
||||
import { PersonalPage } from "@/lib/schema/personal-page"
|
||||
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 { useCoState } from "@/lib/providers/jazz-provider"
|
||||
import { toast } from "sonner"
|
||||
import { EditorView } from "prosemirror-view"
|
||||
|
||||
const configureStarterKit = () =>
|
||||
StarterKit.configure({
|
||||
bold: false,
|
||||
italic: false,
|
||||
typography: false,
|
||||
hardBreak: false,
|
||||
listItem: false,
|
||||
strike: false,
|
||||
focus: false,
|
||||
gapcursor: false,
|
||||
history: false,
|
||||
placeholder: {
|
||||
placeholder: "Page title"
|
||||
}
|
||||
})
|
||||
|
||||
const editorProps = {
|
||||
attributes: {
|
||||
spellcheck: "true",
|
||||
role: "textbox",
|
||||
"aria-readonly": "false",
|
||||
"aria-multiline": "false",
|
||||
"aria-label": "Page title",
|
||||
translate: "no"
|
||||
}
|
||||
}
|
||||
|
||||
export function DetailPageWrapper({ pageId }: { pageId: string }) {
|
||||
const page = useCoState(PersonalPage, pageId as ID<PersonalPage>)
|
||||
const contentEditorRef = useRef<LAEditorRef>(null)
|
||||
|
||||
const handleKeyDown = (view: EditorView, event: KeyboardEvent) => {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault()
|
||||
contentEditorRef.current?.focus()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const handleTitleBlur = (title: string) => {
|
||||
if (page && editor) {
|
||||
if (!title) {
|
||||
toast.error("Update failed", {
|
||||
description: "Title must be longer than or equal to 1 character"
|
||||
})
|
||||
|
||||
// https://github.com/ueberdosis/tiptap/issues/3764
|
||||
setTimeout(() => {
|
||||
editor.commands.setContent(`<p>${page.title}</p>`)
|
||||
})
|
||||
} else {
|
||||
page.title = title
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const editor = useEditor({
|
||||
extensions: [configureStarterKit(), Paragraph],
|
||||
editorProps: {
|
||||
...editorProps,
|
||||
handleKeyDown: handleKeyDown as unknown as (view: EditorView, event: KeyboardEvent) => boolean | void
|
||||
},
|
||||
onBlur: ({ editor }) => handleTitleBlur(editor.getText())
|
||||
})
|
||||
|
||||
const handleContentUpdate = (content: Content) => {
|
||||
console.log("content", content)
|
||||
}
|
||||
|
||||
const updatePageContent = (content: Content) => {
|
||||
if (page) {
|
||||
page.content = content
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (page && editor) {
|
||||
setTimeout(() => {
|
||||
editor.commands.setContent(`<p>${page.title}</p>`)
|
||||
})
|
||||
}
|
||||
}, [page, editor])
|
||||
|
||||
if (!editor) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<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>} />
|
||||
<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">
|
||||
<EditorContent
|
||||
editor={editor}
|
||||
className="la-editor cursor-text select-text text-2xl font-semibold leading-[calc(1.33333)] tracking-[-0.00625rem]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-auto flex-col">
|
||||
<div className="relative flex h-full max-w-full grow flex-col items-stretch p-0">
|
||||
<LAEditor
|
||||
ref={contentEditorRef}
|
||||
editorClassName="-mx-3.5 px-3.5 py-2.5 flex-auto"
|
||||
initialContent={page?.content}
|
||||
placeholder="Add content..."
|
||||
output="json"
|
||||
throttleDelay={3000}
|
||||
onUpdate={handleContentUpdate}
|
||||
onBlur={updatePageContent}
|
||||
onNewBlock={updatePageContent}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user