bolt.diy/app/components/ui/EmptyState.tsx
Stijnus 870bfc58ee
Some checks are pending
Docker Publish / docker-build-publish (push) Waiting to run
Update Stable Branch / prepare-release (push) Waiting to run
feat: github fix and ui improvements (#1685)
* feat: Add reusable UI components and fix GitHub repository display

* style: Fix linting issues in UI components

* fix: Add close icon to GitHub Connection Required dialog

* fix: Add CloseButton component to fix white background issue in dialog close icons

* Fix close button styling in dialog components to address ghost white issue in dark mode

* fix: update icon color to tertiary for consistency

The icon color was changed from `text-bolt-elements-icon-info` to `text-bolt-elements-icon-tertiary`

* fix: improve repository selection dialog tab styling for dark mode

- Update tab menu styling to prevent white background in dark mode
- Use explicit color values for better dark/light mode compatibility
- Improve hover and active states for better visual hierarchy
- Remove unused Tabs imports

---------

Co-authored-by: KevIsDev <zennerd404@gmail.com>
2025-05-09 15:23:20 +02:00

155 lines
3.8 KiB
TypeScript

import React from 'react';
import { classNames } from '~/utils/classNames';
import { Button } from './Button';
import { motion } from 'framer-motion';
// Variant-specific styles
const VARIANT_STYLES = {
default: {
container: 'py-8 p-6',
icon: {
container: 'w-12 h-12 mb-3',
size: 'w-6 h-6',
},
title: 'text-base',
description: 'text-sm mt-1',
actions: 'mt-4',
buttonSize: 'default' as const,
},
compact: {
container: 'py-4 p-4',
icon: {
container: 'w-10 h-10 mb-2',
size: 'w-5 h-5',
},
title: 'text-sm',
description: 'text-xs mt-0.5',
actions: 'mt-3',
buttonSize: 'sm' as const,
},
};
interface EmptyStateProps {
/** Icon class name */
icon?: string;
/** Title text */
title: string;
/** Optional description text */
description?: string;
/** Primary action button label */
actionLabel?: string;
/** Primary action button callback */
onAction?: () => void;
/** Secondary action button label */
secondaryActionLabel?: string;
/** Secondary action button callback */
onSecondaryAction?: () => void;
/** Additional class name */
className?: string;
/** Component size variant */
variant?: 'default' | 'compact';
}
/**
* EmptyState component
*
* A component for displaying empty states with optional actions.
*/
export function EmptyState({
icon = 'i-ph:folder-simple-dashed',
title,
description,
actionLabel,
onAction,
secondaryActionLabel,
onSecondaryAction,
className,
variant = 'default',
}: EmptyStateProps) {
// Get styles based on variant
const styles = VARIANT_STYLES[variant];
// Animation variants for buttons
const buttonAnimation = {
whileHover: { scale: 1.02 },
whileTap: { scale: 0.98 },
};
return (
<div
className={classNames(
'flex flex-col items-center justify-center',
'text-bolt-elements-textSecondary dark:text-bolt-elements-textSecondary-dark',
'bg-bolt-elements-background-depth-2 dark:bg-bolt-elements-background-depth-3 rounded-lg',
styles.container,
className,
)}
>
{/* Icon */}
<div
className={classNames(
'rounded-full bg-bolt-elements-background-depth-3 dark:bg-bolt-elements-background-depth-4 flex items-center justify-center',
styles.icon.container,
)}
>
<span
className={classNames(
icon,
styles.icon.size,
'text-bolt-elements-textTertiary dark:text-bolt-elements-textTertiary-dark',
)}
/>
</div>
{/* Title */}
<p className={classNames('font-medium', styles.title)}>{title}</p>
{/* Description */}
{description && (
<p
className={classNames(
'text-bolt-elements-textTertiary dark:text-bolt-elements-textTertiary-dark text-center max-w-xs',
styles.description,
)}
>
{description}
</p>
)}
{/* Action buttons */}
{(actionLabel || secondaryActionLabel) && (
<div className={classNames('flex items-center gap-2', styles.actions)}>
{actionLabel && onAction && (
<motion.div {...buttonAnimation}>
<Button
onClick={onAction}
variant="default"
size={styles.buttonSize}
className="bg-purple-500 hover:bg-purple-600 text-white"
>
{actionLabel}
</Button>
</motion.div>
)}
{secondaryActionLabel && onSecondaryAction && (
<motion.div {...buttonAnimation}>
<Button onClick={onSecondaryAction} variant="outline" size={styles.buttonSize}>
{secondaryActionLabel}
</Button>
</motion.div>
)}
</div>
)}
</div>
);
}