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>
This commit is contained in:
154
app/components/ui/EmptyState.tsx
Normal file
154
app/components/ui/EmptyState.tsx
Normal file
@@ -0,0 +1,154 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user