theme draft 1
This commit is contained in:
@@ -11,13 +11,13 @@ type LinkProps = BaseButtonProps & AnchorHTMLAttributes<HTMLAnchorElement> & { h
|
||||
|
||||
const getVariantClasses = (variant: 'default' | 'secondary' = 'default'): string => {
|
||||
const baseClass =
|
||||
'transition duration-100 ease-in font-medium w-full h-full px-2 py-1 text-white disabled:cursor-not-allowed disabled:opacity-50';
|
||||
'h-full w-full px-2 py-1 font-medium transition duration-100 ease-in disabled:cursor-not-allowed disabled:opacity-50';
|
||||
|
||||
if (variant === 'secondary') {
|
||||
return `${baseClass} bg-black shadow-md hover:text-black hover:bg-white disabled:hover:text-white disabled:hover:bg-black`;
|
||||
return `${baseClass} bg-content text-content-inverse shadow-md hover:bg-content-muted disabled:hover:bg-content`;
|
||||
}
|
||||
|
||||
return `${baseClass} bg-gray-500 dark:text-gray-800 hover:bg-gray-800 dark:hover:bg-gray-100 disabled:hover:bg-gray-500 dark:disabled:hover:bg-transparent`;
|
||||
return `${baseClass} bg-primary-500 text-primary-foreground hover:bg-primary-700 disabled:hover:bg-primary-500`;
|
||||
};
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
|
||||
@@ -26,7 +26,6 @@ const adminSubItems: NavItem[] = [
|
||||
{ path: '/admin/logs', label: 'Logs', icon: SettingsIcon, title: 'Logs' },
|
||||
];
|
||||
|
||||
// Helper function to check if pathname has a prefix
|
||||
function hasPrefix(path: string, prefix: string): boolean {
|
||||
return path.startsWith(prefix);
|
||||
}
|
||||
@@ -37,10 +36,9 @@ export default function HamburgerMenu() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const isAdmin = user?.is_admin ?? false;
|
||||
|
||||
// Fetch server info for version
|
||||
const { data: infoData } = useGetInfo({
|
||||
query: {
|
||||
staleTime: Infinity, // Info doesn't change frequently
|
||||
staleTime: Infinity,
|
||||
},
|
||||
});
|
||||
const version =
|
||||
@@ -50,7 +48,6 @@ export default function HamburgerMenu() {
|
||||
|
||||
return (
|
||||
<div className="relative z-40 ml-6 flex flex-col">
|
||||
{/* Checkbox input for state management */}
|
||||
<input
|
||||
type="checkbox"
|
||||
className="absolute -top-2 z-50 flex size-7 cursor-pointer opacity-0 lg:hidden"
|
||||
@@ -59,9 +56,8 @@ export default function HamburgerMenu() {
|
||||
onChange={e => setIsOpen(e.target.checked)}
|
||||
/>
|
||||
|
||||
{/* Hamburger icon lines with CSS animations - hidden on desktop */}
|
||||
<span
|
||||
className="z-40 mt-0.5 h-0.5 w-7 bg-black transition-opacity duration-500 lg:hidden dark:bg-white"
|
||||
className="z-40 mt-0.5 h-0.5 w-7 bg-content transition-opacity duration-500 lg:hidden"
|
||||
style={{
|
||||
transformOrigin: '5px 0px',
|
||||
transition:
|
||||
@@ -70,7 +66,7 @@ export default function HamburgerMenu() {
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
className="z-40 mt-1 h-0.5 w-7 bg-black transition-opacity duration-500 lg:hidden dark:bg-white"
|
||||
className="z-40 mt-1 h-0.5 w-7 bg-content transition-opacity duration-500 lg:hidden"
|
||||
style={{
|
||||
transformOrigin: '0% 100%',
|
||||
transition:
|
||||
@@ -80,7 +76,7 @@ export default function HamburgerMenu() {
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
className="z-40 mt-1 h-0.5 w-7 bg-black transition-opacity duration-500 lg:hidden dark:bg-white"
|
||||
className="z-40 mt-1 h-0.5 w-7 bg-content transition-opacity duration-500 lg:hidden"
|
||||
style={{
|
||||
transformOrigin: '0% 0%',
|
||||
transition:
|
||||
@@ -89,21 +85,17 @@ export default function HamburgerMenu() {
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Navigation menu with slide animation */}
|
||||
<div
|
||||
id="menu"
|
||||
className="fixed -ml-6 h-full w-56 bg-white shadow-lg lg:w-48 dark:bg-gray-700"
|
||||
className="fixed -ml-6 h-full w-56 bg-surface shadow-lg lg:w-48"
|
||||
style={{
|
||||
top: 0,
|
||||
paddingTop: 'env(safe-area-inset-top)',
|
||||
transformOrigin: '0% 0%',
|
||||
// On desktop (lg), always show the menu via CSS class
|
||||
// On mobile, control via state
|
||||
transform: isOpen ? 'none' : 'translate(-100%, 0)',
|
||||
transition: 'transform 0.5s cubic-bezier(0.77, 0.2, 0.05, 1)',
|
||||
}}
|
||||
>
|
||||
{/* Desktop override - always visible */}
|
||||
<style>{`
|
||||
@media (min-width: 1024px) {
|
||||
#menu {
|
||||
@@ -112,9 +104,7 @@ export default function HamburgerMenu() {
|
||||
}
|
||||
`}</style>
|
||||
<div className="flex h-16 justify-end lg:justify-around">
|
||||
<p className="my-auto pr-8 text-right text-xl font-bold lg:pr-0 dark:text-white">
|
||||
AnthoLume
|
||||
</p>
|
||||
<p className="my-auto pr-8 text-right text-xl font-bold text-content lg:pr-0">AnthoLume</p>
|
||||
</div>
|
||||
<nav>
|
||||
{navItems.map(item => (
|
||||
@@ -124,8 +114,8 @@ export default function HamburgerMenu() {
|
||||
onClick={() => setIsOpen(false)}
|
||||
className={`my-2 flex w-full items-center justify-start border-l-4 p-2 pl-6 transition-colors duration-200 ${
|
||||
location.pathname === item.path
|
||||
? 'border-purple-500 dark:text-white'
|
||||
: 'border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-100'
|
||||
? 'border-primary-500 text-content'
|
||||
: 'border-transparent text-content-subtle hover:text-content'
|
||||
}`}
|
||||
>
|
||||
<item.icon size={20} />
|
||||
@@ -133,23 +123,21 @@ export default function HamburgerMenu() {
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{/* Admin section - only visible for admins */}
|
||||
{isAdmin && (
|
||||
<div
|
||||
className={`my-2 flex flex-col gap-4 border-l-4 p-2 pl-6 transition-colors duration-200 ${
|
||||
hasPrefix(location.pathname, '/admin')
|
||||
? 'border-purple-500 dark:text-white'
|
||||
: 'border-transparent text-gray-400'
|
||||
? 'border-primary-500 text-content'
|
||||
: 'border-transparent text-content-subtle'
|
||||
}`}
|
||||
>
|
||||
{/* Admin header - always shown */}
|
||||
<Link
|
||||
to="/admin"
|
||||
onClick={() => setIsOpen(false)}
|
||||
className={`flex w-full justify-start ${
|
||||
location.pathname === '/admin' && !hasPrefix(location.pathname, '/admin/')
|
||||
? 'dark:text-white'
|
||||
: 'text-gray-400 hover:text-gray-800 dark:hover:text-gray-100'
|
||||
? 'text-content'
|
||||
: 'text-content-subtle hover:text-content'
|
||||
}`}
|
||||
>
|
||||
<SettingsIcon size={20} />
|
||||
@@ -165,8 +153,8 @@ export default function HamburgerMenu() {
|
||||
onClick={() => setIsOpen(false)}
|
||||
className={`flex w-full justify-start ${
|
||||
location.pathname === item.path
|
||||
? 'dark:text-white'
|
||||
: 'text-gray-400 hover:text-gray-800 dark:hover:text-gray-100'
|
||||
? 'text-content'
|
||||
: 'text-content-subtle hover:text-content'
|
||||
}`}
|
||||
style={{ paddingLeft: '1.75em' }}
|
||||
>
|
||||
@@ -179,7 +167,7 @@ export default function HamburgerMenu() {
|
||||
)}
|
||||
</nav>
|
||||
<a
|
||||
className="absolute bottom-0 flex w-full flex-col items-center justify-center gap-2 p-6 text-black dark:text-white"
|
||||
className="absolute bottom-0 flex w-full flex-col items-center justify-center gap-2 p-6 text-content"
|
||||
target="_blank"
|
||||
href="https://gitea.va.reichard.io/evan/AnthoLume"
|
||||
rel="noreferrer"
|
||||
|
||||
@@ -3,11 +3,16 @@ import { Link, useLocation, Outlet, Navigate } from 'react-router-dom';
|
||||
import { useGetMe } from '../generated/anthoLumeAPIV1';
|
||||
import { useAuth } from '../auth/AuthContext';
|
||||
import { UserIcon, DropdownIcon } from '../icons';
|
||||
import { useTheme } from '../theme/ThemeProvider';
|
||||
import type { ThemeMode } from '../utils/localSettings';
|
||||
import HamburgerMenu from './HamburgerMenu';
|
||||
|
||||
const themeModes: ThemeMode[] = ['light', 'dark', 'system'];
|
||||
|
||||
export default function Layout() {
|
||||
const location = useLocation();
|
||||
const { isAuthenticated, user, logout, isCheckingAuth } = useAuth();
|
||||
const { themeMode, setThemeMode } = useTheme();
|
||||
const { data } = useGetMe(isAuthenticated ? {} : undefined);
|
||||
const fetchedUser =
|
||||
data?.status === 200 && data.data && 'username' in data.data ? data.data : null;
|
||||
@@ -20,7 +25,6 @@ export default function Layout() {
|
||||
setIsUserDropdownOpen(false);
|
||||
};
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
||||
@@ -34,7 +38,6 @@ export default function Layout() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Get current page title
|
||||
const navItems = [
|
||||
{ path: '/admin/import-results', title: 'Admin - Import' },
|
||||
{ path: '/admin/import', title: 'Admin - Import' },
|
||||
@@ -57,43 +60,62 @@ export default function Layout() {
|
||||
document.title = `AnthoLume - ${currentPageTitle}`;
|
||||
}, [currentPageTitle]);
|
||||
|
||||
// Show loading while checking authentication status
|
||||
if (isCheckingAuth) {
|
||||
return <div className="text-gray-500 dark:text-white">Loading...</div>;
|
||||
return <div className="text-content-muted">Loading...</div>;
|
||||
}
|
||||
|
||||
// Redirect to login if not authenticated
|
||||
if (!isAuthenticated) {
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-100 dark:bg-gray-800">
|
||||
{/* Header */}
|
||||
<div className="min-h-screen bg-canvas">
|
||||
<div className="flex h-16 w-full items-center justify-between">
|
||||
{/* Mobile Navigation Button with CSS animations */}
|
||||
<HamburgerMenu />
|
||||
|
||||
{/* Header Title */}
|
||||
<h1 className="whitespace-nowrap px-6 text-xl font-bold lg:ml-44 dark:text-white">
|
||||
<h1 className="whitespace-nowrap px-6 text-xl font-bold text-content lg:ml-44">
|
||||
{currentPageTitle}
|
||||
</h1>
|
||||
|
||||
{/* User Dropdown */}
|
||||
<div
|
||||
className="relative flex w-full items-center justify-end space-x-4 p-4"
|
||||
ref={dropdownRef}
|
||||
>
|
||||
<button
|
||||
onClick={() => setIsUserDropdownOpen(!isUserDropdownOpen)}
|
||||
className="relative block text-gray-800 dark:text-gray-200"
|
||||
className="relative block text-content"
|
||||
>
|
||||
<UserIcon size={20} />
|
||||
</button>
|
||||
|
||||
{isUserDropdownOpen && (
|
||||
<div className="absolute right-4 top-16 z-20 pt-4 transition duration-200">
|
||||
<div className="w-40 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black/5 dark:bg-gray-700 dark:shadow-gray-800">
|
||||
<div className="w-64 origin-top-right rounded-md bg-surface shadow-lg ring-1 ring-black/5 dark:shadow-gray-800">
|
||||
<div
|
||||
className="border-b border-border px-4 py-3"
|
||||
role="group"
|
||||
aria-label="Theme mode"
|
||||
>
|
||||
<p className="mb-2 text-xs font-semibold uppercase tracking-wide text-content-subtle">
|
||||
Theme
|
||||
</p>
|
||||
<div className="inline-flex w-full rounded border border-border bg-surface-muted p-1">
|
||||
{themeModes.map(mode => (
|
||||
<button
|
||||
key={mode}
|
||||
type="button"
|
||||
onClick={() => setThemeMode(mode)}
|
||||
className={`flex-1 rounded px-2 py-1 text-xs font-medium capitalize transition-colors ${
|
||||
themeMode === mode
|
||||
? 'bg-content text-content-inverse'
|
||||
: 'text-content-muted hover:bg-surface hover:text-content'
|
||||
}`}
|
||||
>
|
||||
{mode}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="py-1"
|
||||
role="menu"
|
||||
@@ -103,7 +125,7 @@ export default function Layout() {
|
||||
<Link
|
||||
to="/settings"
|
||||
onClick={() => setIsUserDropdownOpen(false)}
|
||||
className="block px-4 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
|
||||
className="block px-4 py-2 text-content-muted hover:bg-surface-muted hover:text-content"
|
||||
role="menuitem"
|
||||
>
|
||||
<span className="flex flex-col">
|
||||
@@ -112,7 +134,7 @@ export default function Layout() {
|
||||
</Link>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="block w-full px-4 py-2 text-left text-gray-700 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
|
||||
className="block w-full px-4 py-2 text-left text-content-muted hover:bg-surface-muted hover:text-content"
|
||||
role="menuitem"
|
||||
>
|
||||
<span className="flex flex-col">
|
||||
@@ -126,11 +148,11 @@ export default function Layout() {
|
||||
|
||||
<button
|
||||
onClick={() => setIsUserDropdownOpen(!isUserDropdownOpen)}
|
||||
className="flex cursor-pointer items-center gap-2 py-4 text-gray-500 dark:text-white"
|
||||
className="flex cursor-pointer items-center gap-2 py-4 text-content-muted"
|
||||
>
|
||||
<span>{userData ? ('username' in userData ? userData.username : 'User') : 'User'}</span>
|
||||
<span
|
||||
className="text-gray-800 transition-transform duration-200 dark:text-gray-200"
|
||||
className="text-content transition-transform duration-200"
|
||||
style={{ transform: isUserDropdownOpen ? 'rotate(180deg)' : 'rotate(0deg)' }}
|
||||
>
|
||||
<DropdownIcon size={20} />
|
||||
@@ -139,7 +161,6 @@ export default function Layout() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<main
|
||||
className="relative overflow-hidden"
|
||||
style={{ height: 'calc(100dvh - 4rem - env(safe-area-inset-top))' }}
|
||||
|
||||
@@ -13,13 +13,8 @@ export function LoadingState({
|
||||
iconSize = 24,
|
||||
}: LoadingStateProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center justify-center gap-3 text-gray-500 dark:text-gray-400',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<LoadingIcon size={iconSize} className="text-purple-600 dark:text-purple-400" />
|
||||
<div className={cn('flex items-center justify-center gap-3 text-content-muted', className)}>
|
||||
<LoadingIcon size={iconSize} className="text-primary-500" />
|
||||
<span className="text-sm font-medium">{message}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@ export function Skeleton({
|
||||
|
||||
const variantClasses = {
|
||||
default: 'rounded',
|
||||
text: 'rounded-md h-4',
|
||||
text: 'h-4 rounded-md',
|
||||
circular: 'rounded-full',
|
||||
rectangular: 'rounded-none',
|
||||
};
|
||||
@@ -97,12 +97,7 @@ export function SkeletonCard({
|
||||
textLines = 3,
|
||||
}: SkeletonCardProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'bg-white dark:bg-gray-700 rounded-lg p-4 border dark:border-gray-600',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className={cn('rounded-lg border border-border bg-surface p-4', className)}>
|
||||
{showAvatar && (
|
||||
<div className="mb-4 flex items-start gap-4">
|
||||
<SkeletonAvatar />
|
||||
@@ -132,11 +127,11 @@ export function SkeletonTable({
|
||||
showHeader = true,
|
||||
}: SkeletonTableProps) {
|
||||
return (
|
||||
<div className={cn('bg-white dark:bg-gray-700 rounded-lg overflow-hidden', className)}>
|
||||
<div className={cn('overflow-hidden rounded-lg bg-surface', className)}>
|
||||
<table className="min-w-full">
|
||||
{showHeader && (
|
||||
<thead>
|
||||
<tr className="border-b dark:border-gray-600">
|
||||
<tr className="border-b border-border">
|
||||
{Array.from({ length: columns }).map((_, i) => (
|
||||
<th key={i} className="p-3">
|
||||
<Skeleton variant="text" className="h-5 w-3/4" />
|
||||
@@ -147,7 +142,7 @@ export function SkeletonTable({
|
||||
)}
|
||||
<tbody>
|
||||
{Array.from({ length: rows }).map((_, rowIndex) => (
|
||||
<tr key={rowIndex} className="border-b last:border-0 dark:border-gray-600">
|
||||
<tr key={rowIndex} className="border-b border-border last:border-0">
|
||||
{Array.from({ length: columns }).map((_, colIndex) => (
|
||||
<td key={colIndex} className="p-3">
|
||||
<Skeleton
|
||||
@@ -187,11 +182,11 @@ interface PageLoaderProps {
|
||||
|
||||
export function PageLoader({ message = 'Loading...', className = '' }: PageLoaderProps) {
|
||||
return (
|
||||
<div className={cn('flex flex-col items-center justify-center min-h-[400px] gap-4', className)}>
|
||||
<div className={cn('flex min-h-[400px] flex-col items-center justify-center gap-4', className)}>
|
||||
<div className="relative">
|
||||
<div className="size-12 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500 dark:border-gray-600" />
|
||||
<div className="size-12 animate-spin rounded-full border-4 border-gray-200 border-t-secondary-500 dark:border-gray-600" />
|
||||
</div>
|
||||
<p className="text-sm font-medium text-gray-500 dark:text-gray-400">{message}</p>
|
||||
<p className="text-sm font-medium text-content-muted">{message}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -203,19 +198,18 @@ interface InlineLoaderProps {
|
||||
|
||||
export function InlineLoader({ size = 'md', className = '' }: InlineLoaderProps) {
|
||||
const sizeMap = {
|
||||
sm: 'w-4 h-4 border-2',
|
||||
md: 'w-6 h-6 border-3',
|
||||
lg: 'w-8 h-8 border-4',
|
||||
sm: 'h-4 w-4 border-2',
|
||||
md: 'h-6 w-6 border-[3px]',
|
||||
lg: 'h-8 w-8 border-4',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('flex items-center justify-center', className)}>
|
||||
<div
|
||||
className={`${sizeMap[size]} animate-spin rounded-full border-gray-200 border-t-blue-500 dark:border-gray-600`}
|
||||
className={`${sizeMap[size]} animate-spin rounded-full border-gray-200 border-t-secondary-500 dark:border-gray-600`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Re-export SkeletonTable for backward compatibility
|
||||
export { SkeletonTable as SkeletonTableExport };
|
||||
|
||||
@@ -27,10 +27,10 @@ function SkeletonTable({
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={cn('overflow-hidden rounded-lg bg-white dark:bg-gray-700', className)}>
|
||||
<div className={cn('overflow-hidden rounded-lg bg-surface', className)}>
|
||||
<table className="min-w-full">
|
||||
<thead>
|
||||
<tr className="border-b dark:border-gray-600">
|
||||
<tr className="border-b border-border">
|
||||
{Array.from({ length: columns }).map((_, i) => (
|
||||
<th key={i} className="p-3">
|
||||
<Skeleton variant="text" className="h-5 w-3/4" />
|
||||
@@ -40,7 +40,7 @@ function SkeletonTable({
|
||||
</thead>
|
||||
<tbody>
|
||||
{Array.from({ length: rows }).map((_, rowIndex) => (
|
||||
<tr key={rowIndex} className="border-b last:border-0 dark:border-gray-600">
|
||||
<tr key={rowIndex} className="border-b border-border last:border-0">
|
||||
{Array.from({ length: columns }).map((_, colIndex) => (
|
||||
<td key={colIndex} className="p-3">
|
||||
<Skeleton
|
||||
@@ -81,13 +81,13 @@ export function Table<T extends object>({
|
||||
return (
|
||||
<div className="overflow-x-auto">
|
||||
<div className="inline-block min-w-full overflow-hidden rounded shadow">
|
||||
<table className="min-w-full bg-white dark:bg-gray-700">
|
||||
<table className="min-w-full bg-surface">
|
||||
<thead>
|
||||
<tr className="border-b dark:border-gray-600">
|
||||
<tr className="border-b border-border">
|
||||
{columns.map(column => (
|
||||
<th
|
||||
key={String(column.key)}
|
||||
className={`p-3 text-left text-gray-500 dark:text-white ${column.className || ''}`}
|
||||
className={`p-3 text-left text-content-muted ${column.className || ''}`}
|
||||
>
|
||||
{column.header}
|
||||
</th>
|
||||
@@ -97,20 +97,17 @@ export function Table<T extends object>({
|
||||
<tbody>
|
||||
{data.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={columns.length}
|
||||
className="p-3 text-center text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
<td colSpan={columns.length} className="p-3 text-center text-content-muted">
|
||||
{emptyMessage}
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
data.map((row, index) => (
|
||||
<tr key={getRowKey(row, index)} className="border-b dark:border-gray-600">
|
||||
<tr key={getRowKey(row, index)} className="border-b border-border">
|
||||
{columns.map(column => (
|
||||
<td
|
||||
key={`${getRowKey(row, index)}-${String(column.key)}`}
|
||||
className={`p-3 text-gray-700 dark:text-gray-300 ${column.className || ''}`}
|
||||
className={`p-3 text-content ${column.className || ''}`}
|
||||
>
|
||||
{column.render
|
||||
? column.render(row[column.key], row, index)
|
||||
|
||||
@@ -13,24 +13,24 @@ export interface ToastProps {
|
||||
|
||||
const getToastStyles = (_type: ToastType) => {
|
||||
const baseStyles =
|
||||
'flex items-center gap-3 p-4 rounded-lg shadow-lg border-l-4 transition-all duration-300';
|
||||
'flex items-center gap-3 rounded-lg border-l-4 p-4 shadow-lg transition-all duration-300';
|
||||
|
||||
const typeStyles = {
|
||||
info: 'bg-blue-50 dark:bg-blue-900/30 border-blue-500 dark:border-blue-400',
|
||||
warning: 'bg-yellow-50 dark:bg-yellow-900/30 border-yellow-500 dark:border-yellow-400',
|
||||
error: 'bg-red-50 dark:bg-red-900/30 border-red-500 dark:border-red-400',
|
||||
info: 'border-secondary-500 bg-secondary-50 dark:bg-secondary-100/20',
|
||||
warning: 'border-yellow-500 bg-yellow-50 dark:bg-yellow-100/20',
|
||||
error: 'border-red-500 bg-red-50 dark:bg-red-100/20',
|
||||
};
|
||||
|
||||
const iconStyles = {
|
||||
info: 'text-blue-600 dark:text-blue-400',
|
||||
warning: 'text-yellow-600 dark:text-yellow-400',
|
||||
info: 'text-secondary-600 dark:text-secondary-500',
|
||||
warning: 'text-yellow-700 dark:text-yellow-500',
|
||||
error: 'text-red-600 dark:text-red-400',
|
||||
};
|
||||
|
||||
const textStyles = {
|
||||
info: 'text-blue-800 dark:text-blue-200',
|
||||
warning: 'text-yellow-800 dark:text-yellow-200',
|
||||
error: 'text-red-800 dark:text-red-200',
|
||||
info: 'text-secondary-900 dark:text-secondary-700',
|
||||
warning: 'text-yellow-900 dark:text-yellow-700',
|
||||
error: 'text-red-900 dark:text-red-700',
|
||||
};
|
||||
|
||||
return { baseStyles, typeStyles, iconStyles, textStyles };
|
||||
|
||||
Reference in New Issue
Block a user