mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-08 20:25:19 +02:00
refactor: enhance UI component styles and functionality in Documents filters, table shell, and editor for improved user experience and consistency
This commit is contained in:
parent
0f7878a3a0
commit
5ce3790847
12 changed files with 38 additions and 34 deletions
|
|
@ -79,7 +79,7 @@ export function DocumentsFilters({
|
|||
<PopoverContent className="w-64 !p-0 overflow-hidden" align="end">
|
||||
<div>
|
||||
{/* Search input */}
|
||||
<div className="p-2 border-b border-neutral-700">
|
||||
<div className="p-2 border-b border-border dark:border-neutral-700">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-0.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
|
|
@ -108,7 +108,7 @@ export function DocumentsFilters({
|
|||
<button
|
||||
type="button"
|
||||
key={value}
|
||||
className="flex w-full items-center gap-2.5 py-2 px-3 rounded-md hover:bg-muted/50 transition-colors cursor-pointer text-left"
|
||||
className="flex w-full items-center gap-2.5 py-2 px-3 rounded-md hover:bg-neutral-200 dark:hover:bg-neutral-700 transition-colors cursor-pointer text-left"
|
||||
onClick={() => onToggleType(value, !activeTypes.includes(value))}
|
||||
>
|
||||
{/* Icon */}
|
||||
|
|
@ -137,11 +137,11 @@ export function DocumentsFilters({
|
|||
)}
|
||||
</div>
|
||||
{activeTypes.length > 0 && (
|
||||
<div className="px-3 pt-1.5 pb-1.5 border-t border-neutral-700">
|
||||
<div className="px-3 pt-1.5 pb-1.5 border-t border-border dark:border-neutral-700">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full h-7 text-[11px] text-muted-foreground hover:text-foreground hover:bg-neutral-700"
|
||||
className="w-full h-7 text-[11px] text-muted-foreground hover:text-foreground hover:bg-neutral-200 dark:hover:bg-neutral-700"
|
||||
onClick={() => {
|
||||
activeTypes.forEach((t) => {
|
||||
onToggleType(t, false);
|
||||
|
|
|
|||
|
|
@ -222,12 +222,14 @@ function RowContextMenu({
|
|||
onPreview,
|
||||
onDelete,
|
||||
searchSpaceId,
|
||||
onEditNavigate,
|
||||
}: {
|
||||
doc: Document;
|
||||
children: React.ReactNode;
|
||||
onPreview: (doc: Document) => void;
|
||||
onDelete: (doc: Document) => void;
|
||||
searchSpaceId: string;
|
||||
onEditNavigate?: () => void;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
|
||||
|
|
@ -252,9 +254,12 @@ function RowContextMenu({
|
|||
</ContextMenuItem>
|
||||
{isEditable && (
|
||||
<ContextMenuItem
|
||||
onClick={() =>
|
||||
!isEditDisabled && router.push(`/dashboard/${searchSpaceId}/editor/${doc.id}`)
|
||||
}
|
||||
onClick={() => {
|
||||
if (!isEditDisabled) {
|
||||
onEditNavigate?.();
|
||||
router.push(`/dashboard/${searchSpaceId}/editor/${doc.id}`);
|
||||
}
|
||||
}}
|
||||
disabled={isEditDisabled}
|
||||
>
|
||||
<PenLine className="h-4 w-4" />
|
||||
|
|
@ -318,9 +323,9 @@ export function DocumentsTableShell({
|
|||
hasMore = false,
|
||||
loadingMore = false,
|
||||
onLoadMore,
|
||||
isSearchMode = false,
|
||||
mentionedDocIds,
|
||||
onToggleChatMention,
|
||||
onEditNavigate,
|
||||
}: {
|
||||
documents: Document[];
|
||||
loading: boolean;
|
||||
|
|
@ -333,11 +338,12 @@ export function DocumentsTableShell({
|
|||
hasMore?: boolean;
|
||||
loadingMore?: boolean;
|
||||
onLoadMore?: () => void;
|
||||
isSearchMode?: boolean;
|
||||
/** IDs of documents currently mentioned as chips in the chat composer */
|
||||
mentionedDocIds?: Set<number>;
|
||||
/** Toggle a document's mention in the chat (add if not mentioned, remove if mentioned) */
|
||||
onToggleChatMention?: (doc: Document, mentioned: boolean) => void;
|
||||
/** Called when user navigates to the editor via Edit — use to close containing sidebar/panel */
|
||||
onEditNavigate?: () => void;
|
||||
}) {
|
||||
const t = useTranslations("documents");
|
||||
const { openDialog } = useDocumentUploadDialog();
|
||||
|
|
@ -579,6 +585,7 @@ export function DocumentsTableShell({
|
|||
onPreview={handleViewDocument}
|
||||
onDelete={setDeleteDoc}
|
||||
searchSpaceId={searchSpaceId}
|
||||
onEditNavigate={onEditNavigate}
|
||||
>
|
||||
<tr
|
||||
className={`border-b border-border/50 transition-colors ${
|
||||
|
|
@ -839,6 +846,7 @@ export function DocumentsTableShell({
|
|||
}
|
||||
onClick={() => {
|
||||
if (mobileActionDoc) {
|
||||
onEditNavigate?.();
|
||||
router.push(`/dashboard/${searchSpaceId}/editor/${mobileActionDoc.id}`);
|
||||
setMobileActionDoc(null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import {
|
|||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { notesApiService } from "@/lib/apis/notes-api.service";
|
||||
|
|
@ -83,6 +83,7 @@ export default function EditorPage() {
|
|||
const [error, setError] = useState<string | null>(null);
|
||||
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
||||
const [showUnsavedDialog, setShowUnsavedDialog] = useState(false);
|
||||
const [editorTitle, setEditorTitle] = useState<string>("Untitled");
|
||||
|
||||
// Store the latest markdown from the editor
|
||||
const markdownRef = useRef<string>("");
|
||||
|
|
@ -117,20 +118,18 @@ export default function EditorPage() {
|
|||
}
|
||||
}, [pendingNavigation, hasUnsavedChanges, router, setPendingNavigation]);
|
||||
|
||||
// Reset state when documentId changes
|
||||
// Reset state and fetch document content when documentId changes
|
||||
useEffect(() => {
|
||||
setDocument(null);
|
||||
setError(null);
|
||||
setHasUnsavedChanges(false);
|
||||
setLoading(true);
|
||||
initialLoadDone.current = false;
|
||||
}, [documentId]);
|
||||
|
||||
// Fetch document content
|
||||
useEffect(() => {
|
||||
async function fetchDocument() {
|
||||
if (isNewNote) {
|
||||
markdownRef.current = "";
|
||||
setEditorTitle("Untitled");
|
||||
setDocument({
|
||||
document_id: 0,
|
||||
title: "Untitled",
|
||||
|
|
@ -173,6 +172,7 @@ export default function EditorPage() {
|
|||
}
|
||||
|
||||
markdownRef.current = data.source_markdown;
|
||||
setEditorTitle(extractTitleFromMarkdown(data.source_markdown));
|
||||
setDocument(data);
|
||||
setError(null);
|
||||
initialLoadDone.current = true;
|
||||
|
|
@ -193,20 +193,17 @@ export default function EditorPage() {
|
|||
|
||||
const isNote = isNewNote || document?.document_type === "NOTE";
|
||||
|
||||
// Extract title dynamically from current markdown for notes
|
||||
const displayTitle = useMemo(() => {
|
||||
if (isNote) {
|
||||
return extractTitleFromMarkdown(markdownRef.current || document?.source_markdown);
|
||||
}
|
||||
if (isNote) return editorTitle;
|
||||
return document?.title || "Untitled";
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isNote, document?.title, document?.source_markdown, hasUnsavedChanges]);
|
||||
}, [isNote, document?.title, editorTitle]);
|
||||
|
||||
// Handle markdown changes from the Plate editor
|
||||
const handleMarkdownChange = useCallback((md: string) => {
|
||||
markdownRef.current = md;
|
||||
if (initialLoadDone.current) {
|
||||
setHasUnsavedChanges(true);
|
||||
setEditorTitle(extractTitleFromMarkdown(md));
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
|
@ -493,13 +490,13 @@ export default function EditorPage() {
|
|||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={handleCancelLeave}>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleSaveAndLeave}>Save</AlertDialogAction>
|
||||
<AlertDialogAction
|
||||
onClick={handleConfirmLeave}
|
||||
className="border border-input bg-background text-foreground hover:bg-accent hover:text-accent-foreground"
|
||||
className={buttonVariants({ variant: "secondary" })}
|
||||
>
|
||||
Leave without saving
|
||||
</AlertDialogAction>
|
||||
<AlertDialogAction onClick={handleSaveAndLeave}>Save</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
|
|
|||
|
|
@ -190,9 +190,9 @@ export function DocumentsSidebar({ open, onOpenChange }: DocumentsSidebarProps)
|
|||
hasMore={hasMore}
|
||||
loadingMore={loadingMore}
|
||||
onLoadMore={onLoadMore}
|
||||
isSearchMode={isSearchMode}
|
||||
mentionedDocIds={mentionedDocIds}
|
||||
onToggleChatMention={handleToggleChatMention}
|
||||
onEditNavigate={() => onOpenChange(false)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -243,8 +243,8 @@ export function ImageConfigDialog({
|
|||
aria-modal="true"
|
||||
className={cn(
|
||||
"relative w-full max-w-lg h-[85vh]",
|
||||
"rounded-xl bg-background shadow-2xl ring-1 ring-border/50",
|
||||
"dark:bg-neutral-900 dark:ring-white/5",
|
||||
"rounded-xl bg-background shadow-2xl",
|
||||
"dark:bg-neutral-900",
|
||||
"flex flex-col overflow-hidden"
|
||||
)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
|
|
|
|||
|
|
@ -195,8 +195,8 @@ export function ModelConfigDialog({
|
|||
aria-modal="true"
|
||||
className={cn(
|
||||
"relative w-full max-w-lg h-[85vh]",
|
||||
"rounded-xl bg-background shadow-2xl ring-1 ring-border/50",
|
||||
"dark:bg-neutral-900 dark:ring-white/5",
|
||||
"rounded-xl bg-background shadow-2xl",
|
||||
"dark:bg-neutral-900",
|
||||
"flex flex-col overflow-hidden"
|
||||
)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ function AlertDialogContent({
|
|||
<AlertDialogPrimitive.Content
|
||||
data-slot="alert-dialog-content"
|
||||
className={cn(
|
||||
"bg-background dark:bg-neutral-900 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xl ring-1 ring-border/50 dark:ring-white/5 p-6 shadow-2xl duration-200 sm:max-w-lg",
|
||||
"bg-background dark:bg-neutral-900 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xl p-6 shadow-2xl duration-200 sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export function FixedToolbar({
|
|||
return (
|
||||
<Toolbar
|
||||
className={cn(
|
||||
"scrollbar-hide sticky top-0 left-0 z-50 w-full justify-between overflow-x-auto rounded-t-lg border-b bg-background/95 p-1 backdrop-blur supports-backdrop-filter:bg-background/60",
|
||||
"scrollbar-hide sticky top-0 left-0 z-10 w-full justify-between overflow-x-auto rounded-t-lg border-b bg-background/95 p-1 backdrop-blur supports-backdrop-filter:bg-background/60",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export function FloatingToolbar({
|
|||
{...rootProps}
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"scrollbar-hide absolute z-50 overflow-x-auto whitespace-nowrap rounded-md border bg-popover p-1 opacity-100 shadow-md print:hidden dark:bg-neutral-800 dark:border-neutral-700",
|
||||
"scrollbar-hide absolute z-50 overflow-x-auto whitespace-nowrap rounded-md border bg-popover p-1 opacity-100 shadow-md print:hidden dark:bg-neutral-900 dark:border-white/5",
|
||||
"max-w-[80vw]",
|
||||
className
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ export function InsertToolbarButton(props: DropdownMenuProps) {
|
|||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent
|
||||
className="z-[100] flex max-h-[60vh] min-w-0 flex-col overflow-y-auto dark:bg-neutral-800 dark:border dark:border-neutral-700"
|
||||
className="z-[100] flex max-h-[60vh] min-w-0 flex-col overflow-y-auto dark:bg-neutral-900 dark:border dark:border-white/5"
|
||||
align="start"
|
||||
>
|
||||
{groups.map(({ group, items }) => (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { SlashInputPlugin } from "@platejs/slash-command/react";
|
||||
import {
|
||||
ChevronRightIcon,
|
||||
Code2Icon,
|
||||
|
|
@ -177,7 +176,7 @@ export function SlashInputElement({ children, ...props }: PlateElementProps) {
|
|||
<InlineCombobox element={props.element} trigger="/">
|
||||
<InlineComboboxInput />
|
||||
|
||||
<InlineComboboxContent className="dark:bg-neutral-800 dark:border dark:border-neutral-700">
|
||||
<InlineComboboxContent className="dark:bg-neutral-900 dark:border dark:border-white/5">
|
||||
<InlineComboboxEmpty>No results found.</InlineComboboxEmpty>
|
||||
|
||||
{slashCommandGroups.map(({ heading, items }) => (
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export function TurnIntoToolbarButton({
|
|||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent
|
||||
className="z-[100] ignore-click-outside/toolbar min-w-0 max-h-[60vh] overflow-y-auto dark:bg-neutral-800 dark:border dark:border-neutral-700"
|
||||
className="z-[100] ignore-click-outside/toolbar min-w-0 max-h-[60vh] overflow-y-auto dark:bg-neutral-900 dark:border dark:border-white/5"
|
||||
onCloseAutoFocus={(e) => {
|
||||
e.preventDefault();
|
||||
editor.tf.focus();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue