Adjust styles to match the design system (#11118)

### What problem does this PR solve?

- Modify and adjust styles (CSS vars, components) to match the design
system
- Adjust file and directory structure of admin UI

### Type of change

- [x] Refactoring
This commit is contained in:
Jimmy Ben Klieve
2025-11-10 10:05:19 +08:00
committed by GitHub
parent 660386d3b5
commit 1cd54832b5
42 changed files with 685 additions and 539 deletions

View File

@ -2,6 +2,9 @@ import { cn } from '@/lib/utils';
import { t } from 'i18next';
import { useIsDarkTheme } from '../theme-provider';
import noDataIcon from './no data bri.svg';
import noDataIconDark from './no data.svg';
type EmptyProps = {
className?: string;
children?: React.ReactNode;
@ -10,6 +13,14 @@ type EmptyProps = {
const EmptyIcon = () => {
const isDarkTheme = useIsDarkTheme();
return (
<img
className="h-20"
src={isDarkTheme ? noDataIconDark : noDataIcon}
alt={t('common.noData')}
/>
);
return (
<svg
width="184"
@ -67,13 +78,14 @@ const Empty = (props: EmptyProps) => {
return (
<div
className={cn(
'flex flex-col justify-center items-center text-center gap-3',
'flex flex-col justify-center items-center text-center gap-2',
className,
)}
>
<EmptyIcon />
{!children && (
<div className="empty-text mt-4 text-text-secondary">
<div className="empty-text text-text-secondary">
{t('common.noData')}
</div>
)}

View File

@ -0,0 +1,24 @@
<svg width="42" height="52" viewBox="0 0 42 52" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.175 16.22L6 30V42C6 43.0609 6.31607 44.0783 6.87868 44.8284C7.44129 45.5786 8.20435 46 9 46H33C33.7956 46 34.5587 45.5786 35.1213 44.8284C35.6839 44.0783 36 43.0609 36 42V30L30.825 16.22C30.5766 15.5536 30.1938 14.9927 29.7194 14.6006C29.2451 14.2084 28.6981 14.0004 28.14 14H13.86C13.3019 14.0004 12.7549 14.2084 12.2806 14.6006C11.8062 14.9927 11.4234 15.5536 11.175 16.22Z" stroke="url(#paint0_linear_493_44259)" stroke-opacity="0.2" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.175 8.22L6 22V34C6 35.0609 6.31607 36.0783 6.87868 36.8284C7.44129 37.5786 8.20435 38 9 38H33C33.7957 38 34.5587 37.5786 35.1213 36.8284C35.6839 36.0783 36 35.0609 36 34V22L30.825 8.22C30.5766 7.55357 30.1938 6.99274 29.7194 6.60056C29.2451 6.20838 28.6981 6.00039 28.14 6H13.86C13.3019 6.00039 12.7549 6.20838 12.2806 6.60056C11.8062 6.99274 11.4234 7.55357 11.175 8.22Z" fill="white" stroke="url(#paint1_linear_493_44259)" stroke-opacity="0.8" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.175 12.22L6 26V38C6 39.0609 6.31607 40.0783 6.87868 40.8284C7.44129 41.5786 8.20435 42 9 42H33C33.7957 42 34.5587 41.5786 35.1213 40.8284C35.6839 40.0783 36 39.0609 36 38V26L30.825 12.22C30.5766 11.5536 30.1938 10.9927 29.7194 10.6006C29.2451 10.2084 28.6981 10.0004 28.14 10H13.86C13.3019 10.0004 12.7549 10.2084 12.2806 10.6006C11.8062 10.9927 11.4234 11.5536 11.175 12.22Z" fill="white" stroke="url(#paint2_linear_493_44259)" stroke-opacity="0.5" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M36 22.1641H27L24 28.1641H18L15 22.1641H6M36 22.1641V34.1641C36 35.2249 35.6839 36.2423 35.1213 36.9925C34.5587 37.7426 33.7956 38.1641 33 38.1641H9C8.20435 38.1641 7.44129 37.7426 6.87868 36.9925C6.31607 36.2423 6 35.2249 6 34.1641V22.1641M36 22.1641L30.825 8.38406C30.5766 7.71764 30.1938 7.1568 29.7194 6.76462C29.2451 6.37244 28.6981 6.16446 28.14 6.16406H13.86C13.3019 6.16446 12.7549 6.37244 12.2806 6.76462C11.8062 7.1568 11.4234 7.71764 11.175 8.38406L6 22.1641" stroke="url(#paint3_linear_493_44259)" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<defs>
<linearGradient id="paint0_linear_493_44259" x1="21" y1="14" x2="9.71922" y2="50.3541" gradientUnits="userSpaceOnUse">
<stop offset="0.195167" stop-color="#161618" stop-opacity="0"/>
<stop offset="1" stop-color="#C2C2C2"/>
</linearGradient>
<linearGradient id="paint1_linear_493_44259" x1="21" y1="6" x2="9.71922" y2="42.3541" gradientUnits="userSpaceOnUse">
<stop offset="0.195167" stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="#C2C2C2"/>
</linearGradient>
<linearGradient id="paint2_linear_493_44259" x1="21" y1="10" x2="9.71922" y2="46.3541" gradientUnits="userSpaceOnUse">
<stop offset="0.195167" stop-color="#161618" stop-opacity="0"/>
<stop offset="1" stop-color="#C2C2C2"/>
</linearGradient>
<linearGradient id="paint3_linear_493_44259" x1="21" y1="6.16406" x2="21" y2="38.1641" gradientUnits="userSpaceOnUse">
<stop stop-color="#161618"/>
<stop offset="1" stop-color="#7B7B7C"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,24 @@
<svg width="42" height="52" viewBox="0 0 42 52" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.175 16.22L6 30V42C6 43.0609 6.31607 44.0783 6.87868 44.8284C7.44129 45.5786 8.20435 46 9 46H33C33.7956 46 34.5587 45.5786 35.1213 44.8284C35.6839 44.0783 36 43.0609 36 42V30L30.825 16.22C30.5766 15.5536 30.1938 14.9927 29.7194 14.6006C29.2451 14.2084 28.6981 14.0004 28.14 14H13.86C13.3019 14.0004 12.7549 14.2084 12.2806 14.6006C11.8062 14.9927 11.4234 15.5536 11.175 16.22Z" stroke="url(#paint0_linear_486_6708)" stroke-opacity="0.2" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.175 8.22L6 22V34C6 35.0609 6.31607 36.0783 6.87868 36.8284C7.44129 37.5786 8.20435 38 9 38H33C33.7957 38 34.5587 37.5786 35.1213 36.8284C35.6839 36.0783 36 35.0609 36 34V22L30.825 8.22C30.5766 7.55357 30.1938 6.99274 29.7194 6.60056C29.2451 6.20838 28.6981 6.00039 28.14 6H13.86C13.3019 6.00039 12.7549 6.20838 12.2806 6.60056C11.8062 6.99274 11.4234 7.55357 11.175 8.22Z" fill="#222224" stroke="url(#paint1_linear_486_6708)" stroke-opacity="0.8" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.175 12.22L6 26V38C6 39.0609 6.31607 40.0783 6.87868 40.8284C7.44129 41.5786 8.20435 42 9 42H33C33.7957 42 34.5587 41.5786 35.1213 40.8284C35.6839 40.0783 36 39.0609 36 38V26L30.825 12.22C30.5766 11.5536 30.1938 10.9927 29.7194 10.6006C29.2451 10.2084 28.6981 10.0004 28.14 10H13.86C13.3019 10.0004 12.7549 10.2084 12.2806 10.6006C11.8062 10.9927 11.4234 11.5536 11.175 12.22Z" fill="#222224" stroke="url(#paint2_linear_486_6708)" stroke-opacity="0.5" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M36 22.1641H27L24 28.1641H18L15 22.1641H6M36 22.1641V34.1641C36 35.2249 35.6839 36.2423 35.1213 36.9925C34.5587 37.7426 33.7956 38.1641 33 38.1641H9C8.20435 38.1641 7.44129 37.7426 6.87868 36.9925C6.31607 36.2423 6 35.2249 6 34.1641V22.1641M36 22.1641L30.825 8.38406C30.5766 7.71764 30.1938 7.1568 29.7194 6.76462C29.2451 6.37244 28.6981 6.16446 28.14 6.16406H13.86C13.3019 6.16446 12.7549 6.37244 12.2806 6.76462C11.8062 7.1568 11.4234 7.71764 11.175 8.38406L6 22.1641" stroke="url(#paint3_linear_486_6708)" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
<defs>
<linearGradient id="paint0_linear_486_6708" x1="21" y1="14" x2="9.71922" y2="50.3541" gradientUnits="userSpaceOnUse">
<stop offset="0.195167" stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="#C2C2C2"/>
</linearGradient>
<linearGradient id="paint1_linear_486_6708" x1="21" y1="6" x2="9.71922" y2="42.3541" gradientUnits="userSpaceOnUse">
<stop offset="0.195167" stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="#C2C2C2"/>
</linearGradient>
<linearGradient id="paint2_linear_486_6708" x1="21" y1="10" x2="9.71922" y2="46.3541" gradientUnits="userSpaceOnUse">
<stop offset="0.195167" stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="#C2C2C2"/>
</linearGradient>
<linearGradient id="paint3_linear_486_6708" x1="21" y1="6.16406" x2="21" y2="38.1641" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#7B7B7C"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1,4 +1,5 @@
import { useIsDarkTheme } from '@/components/theme-provider';
import { cn } from '@/lib/utils';
import { parseColorToRGB } from '@/utils/common-util';
import React from 'react';
@ -36,7 +37,7 @@ const Spotlight: React.FC<SpotlightProps> = ({
: '194, 221, 243';
return (
<div
className={`absolute inset-0 opacity-80 ${className} rounded-lg`}
className={cn('absolute inset-0 opacity-80 rounded-lg', className)}
style={{
backdropFilter: 'blur(30px)',
zIndex: -1,

View File

@ -39,7 +39,7 @@ const AvatarFallback = React.forwardRef<
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
'flex h-full w-full items-center justify-center rounded-full bg-muted',
'flex h-full w-full items-center justify-center rounded-full bg-bg-member',
className,
)}
{...props}

View File

@ -4,16 +4,18 @@ import * as React from 'react';
import { cn } from '@/lib/utils';
const badgeVariants = cva(
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors outline-none focus:outline-none',
{
variants: {
variant: {
default:
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
'border-transparent bg-bg-primary text-text-primary hover:bg-primary/80',
secondary:
'border-transparent bg-bg-card text-text-sub-title-invert hover:bg-secondary/80 rounded-md',
'border-transparent bg-bg-card text-text-secondary rounded-md',
success:
'border-transparent bg-state-success/5 text-state-success rounded-md',
destructive:
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
'border-transparent bg-state-error/5 text-state-error rounded-md',
outline: 'text-foreground',
},
},

View File

@ -3,28 +3,56 @@ import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react';
import { cn } from '@/lib/utils';
import { Loader2, Plus } from 'lucide-react';
import { LucideLoader2, Plus } from 'lucide-react';
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
cn(
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-0',
'disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*="size-"])]:size-4 shrink-0 [&_svg]:shrink-0',
),
{
variants: {
variant: {
default:
'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
destructive:
'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
outline:
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
'bg-text-primary text-bg-base shadow-xs hover:bg-text-primary/90 focus-visible:bg-text-primary/90',
destructive: `
bg-state-error text-white shadow-xs
hover:bg-state-error/90 focus-visible:ring-state-error/20 dark:focus-visible:ring-state-error/40
`,
outline: `
text-text-secondary bg-bg-input border-0.5 border-border-button
hover:text-text-primary hover:bg-border-button hover:border-border-default
focus-visible:text-text-primary focus-visible:bg-border-button focus-visible:border-border-button
`,
secondary:
'bg-bg-input text-text-primary shadow-xs hover:bg-bg-input/80 border border-border-button',
ghost:
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
ghost: `
text-text-secondary
hover:bg-border-button hover:text-text-primary
focus-visible:text-text-primary focus-visible:bg-border-button
`,
link: 'text-primary underline-offset-4 hover:underline',
icon: 'bg-colors-background-inverse-standard text-foreground hover:bg-colors-background-inverse-standard/80',
dashed: 'border border-dashed border-input hover:bg-accent',
transparent: 'bg-transparent hover:bg-accent border',
danger: 'bg-transparent border border-state-error text-state-error',
transparent: `
text-text-secondary bg-transparent border-0.5 border-border-button
hover:text-text-primary hover:bg-border-button
focus-visible:text-text-primary focus-visible:bg-border-button focus-visible:border-border-button
`,
danger: `
bg-transparent border border-state-error text-state-error
hover:bg-state-error/10 focus-visible:bg-state-error/10
`,
highlighted: `
bg-text-primary text-bg-base border-b-4 border-b-accent-primary
hover:bg-text-primary/90 focus-visible:bg-text-primary/90
`,
},
size: {
default: 'h-8 px-2.5 py-1.5 ',
@ -45,46 +73,48 @@ export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
loading?: boolean;
block?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
(
{
children,
className,
variant,
size,
asChild = false,
loading = false,
disabled = false,
block = false,
...props
},
ref,
) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp
className={cn(
'bg-bg-card',
{ 'block w-full': block },
buttonVariants({ variant, size, className }),
)}
ref={ref}
disabled={loading || disabled}
{...props}
/>
>
{loading && <LucideLoader2 className="animate-spin" />}
{children}
</Comp>
);
},
);
Button.displayName = 'Button';
export const ButtonLoading = React.forwardRef<
HTMLButtonElement,
Omit<ButtonProps, 'asChild'> & { loading?: boolean }
>(
(
{ className, variant, size, children, loading = false, disabled, ...props },
ref,
) => {
return (
<Button
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
disabled={loading || disabled}
>
{loading && <Loader2 className="animate-spin" />}
{children}
</Button>
);
},
);
export const ButtonLoading = Button;
ButtonLoading.displayName = 'ButtonLoading';

View File

@ -9,7 +9,7 @@ const Card = React.forwardRef<
<div
ref={ref}
className={cn(
'rounded-lg border-border-default border shadow-sm bg-bg-input',
'rounded-lg border-border-button border-0.5 shadow-sm bg-bg-input transition-shadow',
className,
)}
{...props}
@ -60,7 +60,11 @@ const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
<div
ref={ref}
className={cn('p-6 pt-0 transition-shadow', className)}
{...props}
/>
));
CardContent.displayName = 'CardContent';

View File

@ -13,7 +13,11 @@ const Checkbox = React.forwardRef<
<CheckboxPrimitive.Root
ref={ref}
className={cn(
'peer h-3.5 w-3.5 shrink-0 rounded-sm border border-text-secondary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
'peer size-4 shrink-0 rounded-sm border border-border-button outline-0 transition-colors bg-bg-component',
'hover:border-border-default hover:bg-border-button',
'focus-visible:border-border-default focus-visible:bg-border-default',
'disabled:cursor-not-allowed disabled:opacity-50',
'data-[state=checked]:text-text-primary data-[state=checked]:border-border-default',
className,
)}
{...props}
@ -21,7 +25,7 @@ const Checkbox = React.forwardRef<
<CheckboxPrimitive.Indicator
className={cn('flex items-center justify-center text-current')}
>
<Check className="h-3.5 w-3.5" />
<Check className="size-3" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));

View File

@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
'fixed inset-0 z-50 bg-black/50 backdrop-blur-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className,
)}
{...props}
@ -38,13 +38,20 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-xl translate-x-[-50%] translate-y-[-50%] gap-4 border bg-bg-base p-6 shadow-lg duration-200 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 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
'outline-0 fixed left-[50%] top-[50%] z-50 grid w-full max-w-xl translate-x-[-50%] translate-y-[-50%] gap-4 border bg-bg-base p-6 shadow-lg duration-200 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 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
className,
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<DialogPrimitive.Close
className="
absolute right-4 top-4 p-2 rounded-sm opacity-70 outline-none text-text-secondary transition-colors
hover:bg-border-button hover:text-text-primary
focus-visible:bg-border-button focus-visible:text-text-primary
disabled:pointer-events-none data-[state=open]:bg-bg-accent data-[state=open]:text-muted-foreground
"
>
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
@ -102,7 +109,7 @@ const DialogDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
className={cn('text-sm text-text-primary', className)}
{...props}
/>
));

View File

@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-bg-base p-1 text-popover-foreground shadow-md 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className,
)}
{...props}

View File

@ -97,19 +97,18 @@ const FormLabel = React.forwardRef<
required?: boolean;
}
>(({ className, tooltip, required = false, ...props }, ref) => {
const { error, formItemId } = useFormField();
const { formItemId } = useFormField();
return (
<Label
ref={ref}
className={cn(error && 'text-destructive', className, 'flex pb-0.5')}
className={cn(className, 'flex pb-0.5')}
htmlFor={formItemId}
{...props}
>
<section>
{required && <span className="text-destructive">*</span>}
{props.children}
</section>
{required && <span className="text-state-error">*</span>}
{props.children}
{tooltip && <FormTooltip tooltip={tooltip}></FormTooltip>}
</Label>
);
@ -171,7 +170,7 @@ const FormMessage = React.forwardRef<
<p
ref={ref}
id={formMessageId}
className={cn('text-sm font-medium text-destructive', className)}
className={cn('text-sm font-medium text-state-error', className)}
{...props}
>
{body}

View File

@ -3,14 +3,17 @@ import * as React from 'react';
import { cn } from '@/lib/utils';
import { Eye, EyeOff, Search } from 'lucide-react';
import { useState } from 'react';
import { Button } from './button';
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
value?: string | number | readonly string[] | undefined;
prefix?: React.ReactNode;
suffix?: React.ReactNode;
}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, value, onChange, ...props }, ref) => {
({ className, type, value, onChange, prefix, suffix, ...props }, ref) => {
const isControlled = value !== undefined;
const { defaultValue, ...restProps } = props;
const inputValue = isControlled ? value : defaultValue;
@ -29,99 +32,80 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
onChange?.(e);
}
};
return (
<>
{type !== 'password' && (
<input
type={type === 'password' && showPassword ? 'text' : type}
className={cn(
'flex h-8 w-full rounded-md border border-input bg-bg-input px-2 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-text-disabled focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 text-text-primary',
className,
)}
ref={ref}
value={inputValue ?? ''}
onChange={handleChange}
{...restProps}
/>
const isPasswordInput = type === 'password';
const inputEl = (
<input
ref={ref}
type={isPasswordInput && showPassword ? 'text' : type}
className={cn(
'flex h-8 w-full rounded-md border-0.5 border-input bg-bg-input px-3 py-2 outline-none text-sm text-text-primary',
'file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-text-disabled',
'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent-primary',
'disabled:cursor-not-allowed disabled:opacity-50 transition-colors',
{
'pl-12': !!prefix,
'pr-12': !!suffix || isPasswordInput,
'pr-24': !!suffix && isPasswordInput,
},
className,
)}
{type === 'password' && (
<div className="relative w-full">
<input
type={type === 'password' && showPassword ? 'text' : type}
className={cn(
'flex h-8 w-full rounded-md border border-input bg-bg-input px-2 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-text-disabled focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 text-text-primary',
className,
)}
ref={ref}
value={inputValue ?? ''}
onChange={handleChange}
{...restProps}
/>
<button
value={inputValue ?? ''}
onChange={handleChange}
{...restProps}
/>
);
if (prefix || suffix || isPasswordInput) {
return (
<div className="relative">
{prefix && (
<span className="absolute left-0 top-[50%] translate-y-[-50%]">
{prefix}
</span>
)}
{inputEl}
{suffix && (
<span
className={cn('absolute right-0 top-[50%] translate-y-[-50%]', {
'right-14': isPasswordInput,
})}
>
{suffix}
</span>
)}
{isPasswordInput && (
<Button
variant="transparent"
type="button"
className="absolute inset-y-0 right-0 pr-3 flex items-center"
className="border-0 absolute right-1 top-[50%] translate-y-[-50%]"
onClick={() => setShowPassword(!showPassword)}
>
{showPassword ? (
<EyeOff className="h-4 w-4 text-text-secondary" />
<EyeOff className="size-[1em]" />
) : (
<Eye className="h-4 w-4 text-text-secondary" />
<Eye className="size-[1em]" />
)}
</button>
</div>
)}
</>
);
</Button>
)}
</div>
);
}
return inputEl;
},
);
Input.displayName = 'Input';
export interface ExpandedInputProps extends Omit<InputProps, 'prefix'> {
prefix?: React.ReactNode;
suffix?: React.ReactNode;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ExpandedInputProps extends InputProps {}
const ExpandedInput = ({
suffix,
prefix,
className,
...props
}: ExpandedInputProps) => {
return (
<div className="relative">
<span
className={cn({
['absolute left-3 top-[50%] translate-y-[-50%]']: prefix,
})}
>
{prefix}
</span>
<Input
className={cn(
{ 'pr-8': !!suffix, 'pl-8': !!prefix },
'bg-bg-base',
className,
)}
{...props}
></Input>
<span
className={cn({
['absolute right-3 top-[50%] translate-y-[-50%]']: suffix,
})}
>
{suffix}
</span>
</div>
);
};
const ExpandedInput = Input;
const SearchInput = (props: InputProps) => {
return (
<ExpandedInput
prefix={<Search className="size-3.5" />}
{...props}
></ExpandedInput>
);
return <Input {...props} prefix={<Search className="ml-3 size-[1em]" />} />;
};
type Value = string | readonly string[] | number | undefined;

View File

@ -46,6 +46,7 @@ const PaginationLink = ({
...props
}: PaginationLinkProps) => (
<a
href="#"
aria-current={isActive ? 'page' : undefined}
className={cn(
'size-8',
@ -70,7 +71,7 @@ const PaginationPrevious = ({
className={cn('gap-1 pl-2.5', className)}
{...props}
>
<ChevronLeft className="h-4 w-4" />
<ChevronLeft className="size-4" />
</PaginationLink>
);
PaginationPrevious.displayName = 'PaginationPrevious';
@ -85,7 +86,7 @@ const PaginationNext = ({
className={cn('gap-1 pr-2.5', className)}
{...props}
>
<ChevronRight className="h-4 w-4" />
<ChevronRight className="size-4" />
</PaginationLink>
);
PaginationNext.displayName = 'PaginationNext';
@ -96,7 +97,7 @@ const PaginationEllipsis = ({
}: React.ComponentProps<'span'>) => (
<span
aria-hidden
className={cn('flex h-9 w-9 items-center justify-center', className)}
className={cn('flex items-center justify-center', className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />

View File

@ -39,7 +39,11 @@ const PopoverContent = React.forwardRef<
align={align}
sideOffset={sideOffset}
className={cn(
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
'z-50 w-72 rounded-md border-0.5 border-border-button bg-bg-base p-4 text-text-primary shadow-lg outline-none',
'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-9',
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className,
)}
{...props}

View File

@ -27,7 +27,12 @@ function RadioGroupItem({
<RadioGroupPrimitive.Item
data-slot="radio-group-item"
className={cn(
'border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
'text-primary aspect-square size-4 shrink-0 rounded-full',
'transition-all outline-none border border-border-button bg-bg-component',
'hover:border-border-default hover:bg-border-default',
'focus-visible:border-border-default focus-visible:bg-border-default',
'aria-[invalid]:border-state-error aria-[invalid]:bg-state-error/5 aria-[invalid]:text-state-error',
'disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
{...props}
@ -36,7 +41,7 @@ function RadioGroupItem({
data-slot="radio-group-indicator"
className="relative flex items-center justify-center"
>
<CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
<CircleIcon className="fill-current absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
);

View File

@ -49,7 +49,7 @@ function Radio({ value, checked, disabled, onChange, children }: RadioProps) {
<span
className={cn(
'flex h-4 w-4 items-center justify-center rounded-full border border-border transition-colors',
'peer ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
'peer outline-none focus-visible:border-border-button',
isChecked && 'border-primary bg-primary/10',
mergedDisabled && 'border-muted',
)}

View File

@ -23,7 +23,7 @@ export type RAGFlowPaginationType = {
export function RAGFlowPagination({
current = 1,
pageSize = 10,
pageSize = 5,
total = 0,
onChange,
showSizeChanger = true,
@ -172,13 +172,14 @@ export function RAGFlowPagination({
</PaginationItem>
</PaginationContent>
</Pagination>
{showSizeChanger && (
<RAGFlowSelect
options={sizeChangerOptions}
value={currentPageSize}
onChange={handlePageSizeChange}
triggerClassName="bg-bg-card"
></RAGFlowSelect>
triggerClassName="bg-bg-card border-transparent"
/>
)}
</section>
);

View File

@ -26,7 +26,11 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger
ref={ref}
className={cn(
'flex h-8 w-full items-center bg-bg-input justify-between rounded-md border border-input px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
'flex h-8 w-full items-center bg-bg-input justify-between rounded-md border-0.5 border-border-button',
'px-3 py-2 text-sm outline-none transition-colors text-text-secondary placeholder:text-muted-foreground',
'hover:text-text-primary hover:bg-border-button',
'focus-visible:text-text-primary focus-visible:bg-border-button',
'disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
className,
)}
{...props}
@ -91,7 +95,11 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content
ref={ref}
className={cn(
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border-0.5 border-border-card bg-bg-base text-popover-foreground shadow-md',
'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',
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
position === 'popper' &&
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
className,
@ -134,7 +142,8 @@ const SelectItem = React.forwardRef<
<SelectPrimitive.Item
ref={ref}
className={cn(
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-2 text-sm outline-none text-text-secondary',
'focus:bg-border-button focus:text-text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className,
)}
{...props}

View File

@ -11,16 +11,23 @@ const Switch = React.forwardRef<
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
'peer inline-flex h-3.5 w-6 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-accent-primary data-[state=unchecked]:bg-text-sub-title',
'group/switch inline-flex h-4 w-7 shrink-0 cursor-pointer items-center rounded-full',
'border-2 border-transparent overflow-hidden transition-colors',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary',
'disabled:cursor-not-allowed disabled:opacity-50',
'data-[state=checked]:bg-accent-primary data-[state=unchecked]:bg-text-sub-title',
className,
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
'pointer-events-none block size-3 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-2 data-[state=unchecked]:translate-x-0',
)}
className="
pointer-events-none block w-3 h-3 rounded-full bg-white shadow-lg ring-0 transition-all ease-out
group-hover/switch:w-4 group-focus-visible/switch:w-4
data-[state=checked]:translate-x-3 data-[state=unchecked]:translate-x-0
group-hover/switch:data-[state=checked]:translate-x-2 group-focus-visible/switch:data-[state=checked]:translate-x-2
"
/>
</SwitchPrimitives.Root>
));

View File

@ -27,7 +27,10 @@ const TableHeader = React.forwardRef<
>(({ className, ...props }, ref) => (
<thead
ref={ref}
className={cn('[&_tr]:border-b top-0 sticky bg-bg-title z-10', className)}
className={cn(
'[&_tr]:border-b-0.5 top-0 sticky bg-bg-title z-10',
className,
)}
{...props}
/>
));
@ -52,7 +55,7 @@ const TableFooter = React.forwardRef<
<tfoot
ref={ref}
className={cn(
'border-t bg-muted/50 font-medium [&>tr]:last:border-b-0',
'border-t-0.5 border-border-button bg-muted/50 font-medium [&>tr]:last:border-b-0',
className,
)}
{...props}
@ -67,7 +70,7 @@ const TableRow = React.forwardRef<
<tr
ref={ref}
className={cn(
'border-b border-border-button transition-colors hover:bg-bg-card data-[state=selected]:bg-bg-card',
'border-b-0.5 border-border-button transition-colors hover:bg-bg-card data-[state=selected]:bg-bg-card',
className,
)}
{...props}
@ -82,7 +85,7 @@ const TableHead = React.forwardRef<
<th
ref={ref}
className={cn(
'h-12 px-4 text-left align-middle font-normal text-text-secondary [&:has([role=checkbox])]:pr-0 ',
'first-of-type:pl-6 last-of-type:pr-6 h-14 px-4 text-left align-middle font-normal text-text-secondary [&:has([role=checkbox])]:pr-0 ',
className,
)}
{...props}
@ -97,7 +100,7 @@ const TableCell = React.forwardRef<
<td
ref={ref}
className={cn(
'p-4 align-middle [&:has([role=checkbox])]:pr-0 text-text-primary font-normal',
'first-of-type:pl-6 last-of-type:pr-6 px-4 py-3 align-middle [&:has([role=checkbox])]:pr-0 text-text-primary font-normal',
className,
)}
{...props}

View File

@ -29,7 +29,11 @@ const TabsTrigger = React.forwardRef<
<TabsPrimitive.Trigger
ref={ref}
className={cn(
'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-bg-base data-[state=active]:text-text-primary data-[state=active]:shadow-sm',
'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5',
'text-sm font-medium ring-offset-background transition-all',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
'disabled:pointer-events-none disabled:opacity-50',
'data-[state=active]:bg-bg-base data-[state=active]:text-text-primary data-[state=active]:shadow-sm',
className,
)}
{...props}