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:
Anish Sarkar 2026-03-08 17:43:19 +05:30
parent 0f7878a3a0
commit 5ce3790847
12 changed files with 38 additions and 34 deletions

View file

@ -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);

View file

@ -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);
}

View file

@ -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>

View file

@ -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>
</>

View file

@ -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()}

View file

@ -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()}

View file

@ -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}

View file

@ -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}

View file

@ -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
)}

View file

@ -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 }) => (

View file

@ -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 }) => (

View file

@ -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();