mirror of
https://github.com/coaidev/coai.git
synced 2025-05-20 13:30:13 +09:00
feat: optimize form scrollbars
This commit is contained in:
parent
9087779a96
commit
c993fe34e2
@ -82,4 +82,8 @@
|
||||
overflow-y: auto;
|
||||
touch-action: pan-y;
|
||||
background: hsla(var(--background-container));
|
||||
|
||||
& > .scrollarea-viewport > div {
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
|
@ -636,14 +636,19 @@
|
||||
}
|
||||
|
||||
.chat-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
touch-action: pan-y;
|
||||
padding: 18px;
|
||||
|
||||
.chat-messages-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 18px 24px;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 0.75rem;
|
||||
|
@ -106,6 +106,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.horizontal-scrollbar {
|
||||
--radix-scroll-area-thumb-height: 6px;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
-webkit-appearance: textfield;
|
||||
margin: 0;
|
||||
|
@ -419,7 +419,9 @@ function UserTable() {
|
||||
<TableCell>{user.quota}</TableCell>
|
||||
<TableCell>{user.used_quota}</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>
|
||||
{useDeeptrain && (
|
||||
<TableCell>{t(user.enterprise.toString())}</TableCell>
|
||||
|
@ -36,20 +36,22 @@ function ChatInterface({ scrollable, setTarget }: ChatInterfaceProps) {
|
||||
|
||||
return (
|
||||
<ScrollArea className={`chat-content`} ref={ref}>
|
||||
{messages.map((message, i) => (
|
||||
<MessageSegment
|
||||
message={message}
|
||||
end={i === messages.length - 1}
|
||||
onEvent={(event: string, index?: number, message?: string) => {
|
||||
process({ id: current, event, index, message });
|
||||
}}
|
||||
key={i}
|
||||
index={i}
|
||||
selected={selected === i}
|
||||
onFocus={() => setSelected(i)}
|
||||
onFocusLeave={() => setSelected(-1)}
|
||||
/>
|
||||
))}
|
||||
<div className={`chat-messages-wrapper`}>
|
||||
{messages.map((message, i) => (
|
||||
<MessageSegment
|
||||
message={message}
|
||||
end={i === messages.length - 1}
|
||||
onEvent={(event: string, index?: number, message?: string) => {
|
||||
process({ id: current, event, index, message });
|
||||
}}
|
||||
key={i}
|
||||
index={i}
|
||||
selected={selected === i}
|
||||
onFocus={() => setSelected(i)}
|
||||
onFocusLeave={() => setSelected(-1)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
);
|
||||
}
|
||||
|
117
app/src/components/ui/pagination.tsx
Normal file
117
app/src/components/ui/pagination.tsx
Normal 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,
|
||||
};
|
@ -13,7 +13,7 @@ const ScrollArea = React.forwardRef<
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport
|
||||
ref={ref}
|
||||
className="h-full w-full rounded-[inherit]"
|
||||
className="scrollarea-viewport h-full w-full rounded-[inherit]"
|
||||
>
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
@ -35,7 +35,7 @@ const ScrollBar = React.forwardRef<
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
||||
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,
|
||||
)}
|
||||
{...props}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "./lib/utils.ts";
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area.tsx";
|
||||
|
||||
type TableProps = React.HTMLAttributes<HTMLTableElement> & {
|
||||
classNameWrapper?: string;
|
||||
@ -8,13 +9,16 @@ type TableProps = React.HTMLAttributes<HTMLTableElement> & {
|
||||
|
||||
const Table = React.forwardRef<HTMLTableElement, TableProps>(
|
||||
({ className, classNameWrapper, ...props }, ref) => (
|
||||
<div className={cn("relative w-full overflow-auto", classNameWrapper)}>
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
<ScrollArea type="always">
|
||||
<div className={cn("relative w-full mb-2", classNameWrapper)}>
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
<ScrollBar className="cursor-pointer" orientation="horizontal" />
|
||||
</ScrollArea>
|
||||
),
|
||||
);
|
||||
Table.displayName = "Table";
|
||||
|
@ -244,7 +244,7 @@
|
||||
"download": "Download {{name}} format"
|
||||
},
|
||||
"api": {
|
||||
"title": "API Settings",
|
||||
"title": "API Key",
|
||||
"copied": "Copied",
|
||||
"copied-description": "API key has been copied to clipboard",
|
||||
"learn-more": "Learn more",
|
||||
@ -273,7 +273,7 @@
|
||||
"copied-description": "Link has been copied to clipboard",
|
||||
"not-found": "Conversation not found",
|
||||
"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",
|
||||
"name": "Conversation Title",
|
||||
"time": "Time",
|
||||
@ -290,7 +290,7 @@
|
||||
"check-success": "Redeem Success",
|
||||
"check-success-description": "Redeem Success! You have received {{amount}} points, start your AI journey!",
|
||||
"check-failed": "Redeem Failed",
|
||||
"invitation": "Invitation Code"
|
||||
"invitation": "Gift"
|
||||
},
|
||||
"contact": {
|
||||
"title": "Contact Us"
|
||||
|
@ -5,6 +5,7 @@ import { useSelector } from "react-redux";
|
||||
import { selectAdmin, selectInit } from "@/store/auth.ts";
|
||||
import { useEffect } from "react";
|
||||
import router from "@/router.tsx";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area.tsx";
|
||||
|
||||
function Admin() {
|
||||
const init = useSelector(selectInit);
|
||||
@ -17,9 +18,9 @@ function Admin() {
|
||||
return (
|
||||
<div className={`admin-page`}>
|
||||
<MenuBar />
|
||||
<div className={`admin-content thin-scrollbar`}>
|
||||
<ScrollArea className={`admin-content`}>
|
||||
<Outlet />
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -126,9 +126,11 @@ function SharingForm({ refer, data }: SharingFormProps) {
|
||||
<div className={`time`}>{time}</div>
|
||||
</div>
|
||||
<ScrollArea className={`body`}>
|
||||
{data.messages.map((message, i) => (
|
||||
<MessageSegment message={message} key={i} index={i} />
|
||||
))}
|
||||
<div className={`chat-messages-wrapper`}>
|
||||
{data.messages.map((message, i) => (
|
||||
<MessageSegment message={message} key={i} index={i} />
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
<div className={`action`}>
|
||||
<Button
|
||||
|
Loading…
Reference in New Issue
Block a user