feat: optimize form scrollbars

This commit is contained in:
Zhang Minghan 2024-03-09 10:00:58 +08:00
parent 9087779a96
commit c993fe34e2
11 changed files with 176 additions and 35 deletions

View File

@ -82,4 +82,8 @@
overflow-y: auto; overflow-y: auto;
touch-action: pan-y; touch-action: pan-y;
background: hsla(var(--background-container)); background: hsla(var(--background-container));
& > .scrollarea-viewport > div {
display: flex !important;
}
} }

View File

@ -636,14 +636,19 @@
} }
.chat-content { .chat-content {
display: flex;
flex-direction: column;
flex-grow: 1; flex-grow: 1;
width: 100%; width: 100%;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
touch-action: pan-y; touch-action: pan-y;
padding: 18px;
.chat-messages-wrapper {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 18px 24px;
}
.message { .message {
margin-bottom: 0.75rem; margin-bottom: 0.75rem;

View File

@ -106,6 +106,10 @@
} }
} }
.horizontal-scrollbar {
--radix-scroll-area-thumb-height: 6px;
}
input[type="number"] { input[type="number"] {
-webkit-appearance: textfield; -webkit-appearance: textfield;
margin: 0; margin: 0;

View File

@ -419,7 +419,9 @@ function UserTable() {
<TableCell>{user.quota}</TableCell> <TableCell>{user.quota}</TableCell>
<TableCell>{user.used_quota}</TableCell> <TableCell>{user.used_quota}</TableCell>
<TableCell>{t(user.is_subscribed.toString())}</TableCell> <TableCell>{t(user.is_subscribed.toString())}</TableCell>
<TableCell className={`whitespace-nowrap`}>{t(`admin.identity.${userTypeArray[user.level]}`)}</TableCell> <TableCell className={`whitespace-nowrap`}>
{t(`admin.identity.${userTypeArray[user.level]}`)}
</TableCell>
<TableCell>{user.total_month}</TableCell> <TableCell>{user.total_month}</TableCell>
{useDeeptrain && ( {useDeeptrain && (
<TableCell>{t(user.enterprise.toString())}</TableCell> <TableCell>{t(user.enterprise.toString())}</TableCell>

View File

@ -36,20 +36,22 @@ function ChatInterface({ scrollable, setTarget }: ChatInterfaceProps) {
return ( return (
<ScrollArea className={`chat-content`} ref={ref}> <ScrollArea className={`chat-content`} ref={ref}>
{messages.map((message, i) => ( <div className={`chat-messages-wrapper`}>
<MessageSegment {messages.map((message, i) => (
message={message} <MessageSegment
end={i === messages.length - 1} message={message}
onEvent={(event: string, index?: number, message?: string) => { end={i === messages.length - 1}
process({ id: current, event, index, message }); onEvent={(event: string, index?: number, message?: string) => {
}} process({ id: current, event, index, message });
key={i} }}
index={i} key={i}
selected={selected === i} index={i}
onFocus={() => setSelected(i)} selected={selected === i}
onFocusLeave={() => setSelected(-1)} onFocus={() => setSelected(i)}
/> onFocusLeave={() => setSelected(-1)}
))} />
))}
</div>
</ScrollArea> </ScrollArea>
); );
} }

View File

@ -0,0 +1,117 @@
import * as React from "react";
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
import { cn } from "@/components/ui/lib/utils";
import { ButtonProps, buttonVariants } from "src/components/ui/button";
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
<nav
role="navigation"
aria-label="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
{...props}
/>
);
Pagination.displayName = "Pagination";
const PaginationContent = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
>(({ className, ...props }, ref) => (
<ul
ref={ref}
className={cn("flex flex-row items-center gap-1", className)}
{...props}
/>
));
PaginationContent.displayName = "PaginationContent";
const PaginationItem = React.forwardRef<
HTMLLIElement,
React.ComponentProps<"li">
>(({ className, ...props }, ref) => (
<li ref={ref} className={cn("", className)} {...props} />
));
PaginationItem.displayName = "PaginationItem";
type PaginationLinkProps = {
isActive?: boolean;
} & Pick<ButtonProps, "size"> &
React.ComponentProps<"a">;
const PaginationLink = ({
className,
isActive,
size = "icon",
...props
}: PaginationLinkProps) => (
<a
aria-current={isActive ? "page" : undefined}
className={cn(
buttonVariants({
variant: isActive ? "outline" : "ghost",
size,
}),
className,
)}
{...props}
/>
);
PaginationLink.displayName = "PaginationLink";
const PaginationPrevious = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to previous page"
size="default"
className={cn("gap-1 pl-2.5", className)}
{...props}
>
<ChevronLeft className="h-4 w-4" />
<span>Previous</span>
</PaginationLink>
);
PaginationPrevious.displayName = "PaginationPrevious";
const PaginationNext = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to next page"
size="default"
className={cn("gap-1 pr-2.5", className)}
{...props}
>
<span>Next</span>
<ChevronRight className="h-4 w-4" />
</PaginationLink>
);
PaginationNext.displayName = "PaginationNext";
const PaginationEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">More pages</span>
</span>
);
PaginationEllipsis.displayName = "PaginationEllipsis";
export {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
};

View File

@ -13,7 +13,7 @@ const ScrollArea = React.forwardRef<
> >
<ScrollAreaPrimitive.Viewport <ScrollAreaPrimitive.Viewport
ref={ref} ref={ref}
className="h-full w-full rounded-[inherit]" className="scrollarea-viewport h-full w-full rounded-[inherit]"
> >
{children} {children}
</ScrollAreaPrimitive.Viewport> </ScrollAreaPrimitive.Viewport>
@ -35,7 +35,7 @@ const ScrollBar = React.forwardRef<
orientation === "vertical" && orientation === "vertical" &&
"h-full w-2.5 border-l border-l-transparent p-[1px]", "h-full w-2.5 border-l border-l-transparent p-[1px]",
orientation === "horizontal" && orientation === "horizontal" &&
"h-2.5 flex-col border-t border-t-transparent p-[1px]", "horizontal-scrollbar h-2.5 flex-col border-t border-t-transparent p-[1px]",
className, className,
)} )}
{...props} {...props}

View File

@ -1,6 +1,7 @@
import * as React from "react"; import * as React from "react";
import { cn } from "./lib/utils.ts"; import { cn } from "./lib/utils.ts";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area.tsx";
type TableProps = React.HTMLAttributes<HTMLTableElement> & { type TableProps = React.HTMLAttributes<HTMLTableElement> & {
classNameWrapper?: string; classNameWrapper?: string;
@ -8,13 +9,16 @@ type TableProps = React.HTMLAttributes<HTMLTableElement> & {
const Table = React.forwardRef<HTMLTableElement, TableProps>( const Table = React.forwardRef<HTMLTableElement, TableProps>(
({ className, classNameWrapper, ...props }, ref) => ( ({ className, classNameWrapper, ...props }, ref) => (
<div className={cn("relative w-full overflow-auto", classNameWrapper)}> <ScrollArea type="always">
<table <div className={cn("relative w-full mb-2", classNameWrapper)}>
ref={ref} <table
className={cn("w-full caption-bottom text-sm", className)} ref={ref}
{...props} className={cn("w-full caption-bottom text-sm", className)}
/> {...props}
</div> />
</div>
<ScrollBar className="cursor-pointer" orientation="horizontal" />
</ScrollArea>
), ),
); );
Table.displayName = "Table"; Table.displayName = "Table";

View File

@ -244,7 +244,7 @@
"download": "Download {{name}} format" "download": "Download {{name}} format"
}, },
"api": { "api": {
"title": "API Settings", "title": "API Key",
"copied": "Copied", "copied": "Copied",
"copied-description": "API key has been copied to clipboard", "copied-description": "API key has been copied to clipboard",
"learn-more": "Learn more", "learn-more": "Learn more",
@ -273,7 +273,7 @@
"copied-description": "Link has been copied to clipboard", "copied-description": "Link has been copied to clipboard",
"not-found": "Conversation not found", "not-found": "Conversation not found",
"not-found-description": "Conversation not found, please check if the link is correct or the conversation has been deleted", "not-found-description": "Conversation not found, please check if the link is correct or the conversation has been deleted",
"manage": "Share Management", "manage": "Sharing",
"sync-error": "Sync Error", "sync-error": "Sync Error",
"name": "Conversation Title", "name": "Conversation Title",
"time": "Time", "time": "Time",
@ -290,7 +290,7 @@
"check-success": "Redeem Success", "check-success": "Redeem Success",
"check-success-description": "Redeem Success! You have received {{amount}} points, start your AI journey!", "check-success-description": "Redeem Success! You have received {{amount}} points, start your AI journey!",
"check-failed": "Redeem Failed", "check-failed": "Redeem Failed",
"invitation": "Invitation Code" "invitation": "Gift"
}, },
"contact": { "contact": {
"title": "Contact Us" "title": "Contact Us"

View File

@ -5,6 +5,7 @@ import { useSelector } from "react-redux";
import { selectAdmin, selectInit } from "@/store/auth.ts"; import { selectAdmin, selectInit } from "@/store/auth.ts";
import { useEffect } from "react"; import { useEffect } from "react";
import router from "@/router.tsx"; import router from "@/router.tsx";
import { ScrollArea } from "@/components/ui/scroll-area.tsx";
function Admin() { function Admin() {
const init = useSelector(selectInit); const init = useSelector(selectInit);
@ -17,9 +18,9 @@ function Admin() {
return ( return (
<div className={`admin-page`}> <div className={`admin-page`}>
<MenuBar /> <MenuBar />
<div className={`admin-content thin-scrollbar`}> <ScrollArea className={`admin-content`}>
<Outlet /> <Outlet />
</div> </ScrollArea>
</div> </div>
); );
} }

View File

@ -126,9 +126,11 @@ function SharingForm({ refer, data }: SharingFormProps) {
<div className={`time`}>{time}</div> <div className={`time`}>{time}</div>
</div> </div>
<ScrollArea className={`body`}> <ScrollArea className={`body`}>
{data.messages.map((message, i) => ( <div className={`chat-messages-wrapper`}>
<MessageSegment message={message} key={i} index={i} /> {data.messages.map((message, i) => (
))} <MessageSegment message={message} key={i} index={i} />
))}
</div>
</ScrollArea> </ScrollArea>
<div className={`action`}> <div className={`action`}>
<Button <Button