style: various improvements for regex card styling

This commit is contained in:
Sam Chau
2025-01-19 12:04:10 +10:30
parent 5608afe91d
commit e60b6cb30a

View File

@@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Copy, Check} from 'lucide-react';
import {Copy, Check, FlaskConical} from 'lucide-react';
import Tooltip from '@ui/Tooltip';
import ReactMarkdown from 'react-markdown';
const RegexCard = ({
pattern,
@@ -33,132 +34,151 @@ const RegexCard = ({
};
const handleMouseDown = e => {
// Prevent text selection when shift-clicking
if (e.shiftKey) {
e.preventDefault();
}
};
const getTestColor = () => {
if (totalTests === 0) return 'text-gray-400';
if (passRate === 100) return 'text-green-400';
if (passRate >= 80) return 'text-yellow-400';
return 'text-red-400';
};
return (
<div
className={`h-full w-full bg-white dark:bg-gray-800 border ${
className={`w-full h-[20rem] bg-gradient-to-br from-gray-800/95 to-gray-900 border ${
isSelected
? 'border-blue-500 dark:border-blue-400'
? 'border-blue-500'
: willBeSelected
? 'border-blue-300 dark:border-blue-600'
: 'border-gray-200 dark:border-gray-700'
} rounded-lg shadow hover:shadow-lg ${
? 'border-blue-300'
: 'border-gray-700'
} rounded-lg shadow-lg hover:shadow-xl ${
isSelectionMode
? isSelected
? 'hover:border-blue-400'
: 'hover:border-gray-400'
: 'hover:border-blue-400'
} dark:hover:border-blue-500 transition-all cursor-pointer`}
} transition-all cursor-pointer overflow-hidden`}
onClick={handleClick}
onMouseDown={handleMouseDown}>
<div className='flex flex-col p-6 gap-3 h-full'>
<div className='p-6 flex flex-col h-full'>
{/* Header Section */}
<div className='flex justify-between items-center gap-4'>
<h3 className='text-xl font-semibold text-gray-900 dark:text-gray-100 truncate'>
{pattern.name}
</h3>
<div className='w-8 h-8 flex items-center justify-center'>
{isSelectionMode ? (
<Tooltip
content={
isSelected
? 'Selected'
: willBeSelected
? 'Will be selected'
: 'Select'
}>
<div
className={`w-6 h-6 rounded-full flex items-center justify-center ${
isSelected
? 'bg-blue-500'
: willBeSelected
? 'bg-blue-200 dark:bg-blue-800'
: 'bg-gray-200 dark:bg-gray-700'
} transition-colors hover:bg-blue-600`}>
{isSelected && (
<Check
size={14}
className='text-white'
/>
)}
{willBeSelected && !isSelected && (
<div className='w-1.5 h-1.5 rounded-full bg-blue-400' />
)}
<div className='flex-none'>
<div className='flex justify-between items-start'>
<div className='flex items-center gap-3 flex-wrap'>
<h3 className='text-lg font-bold text-gray-100'>
{pattern.name}
</h3>
{pattern.tags && pattern.tags.length > 0 && (
<div className='flex flex-wrap gap-2'>
{pattern.tags.map(tag => (
<span
key={tag}
className='bg-blue-600/20 text-blue-400 px-1.5 py-0.5 rounded text-xs shadow-sm'>
{tag}
</span>
))}
</div>
</Tooltip>
) : (
<button
onClick={handleCloneClick}
className='w-8 h-8 rounded-full transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 flex items-center justify-center'>
<Copy className='w-5 h-5 text-gray-500 dark:text-gray-400' />
</button>
)}
)}
</div>
<div className='flex items-center'>
<div className='w-8 h-8 flex items-center justify-center'>
{isSelectionMode ? (
<Tooltip
content={
isSelected
? 'Selected'
: willBeSelected
? 'Will be selected'
: 'Select'
}>
<div
className={`w-6 h-6 rounded-full flex items-center justify-center ${
isSelected
? 'bg-blue-500'
: willBeSelected
? 'bg-blue-200/20'
: 'bg-gray-200/20'
} transition-colors hover:bg-blue-600`}>
{isSelected && (
<Check
size={14}
className='text-white'
/>
)}
{willBeSelected && !isSelected && (
<div className='w-1.5 h-1.5 rounded-full bg-blue-400' />
)}
</div>
</Tooltip>
) : (
<button
onClick={handleCloneClick}
className='text-gray-400 hover:text-white transition-colors'>
<Copy className='w-5 h-5' />
</button>
)}
</div>
</div>
</div>
{/* Pattern Display */}
<div className='mt-4 bg-gray-900/50 rounded-md p-3 font-mono text-xs border border-gray-700/50'>
<code className='text-gray-200 break-all line-clamp-3'>
{pattern.pattern}
</code>
</div>
</div>
{/* Pattern Display */}
<div className='bg-gray-50 dark:bg-gray-900/50 rounded-md p-3 font-mono text-sm'>
<code className='text-gray-800 dark:text-gray-200 break-all line-clamp-3'>
{pattern.pattern}
</code>
</div>
<hr className='border-gray-700 my-3' />
{/* Description */}
{pattern.description && (
<p className='text-gray-600 dark:text-gray-300 text-sm line-clamp-2'>
{pattern.description}
</p>
)}
{/* Bottom Metadata */}
<div className='flex flex-wrap items-center gap-4 text-sm mt-auto'>
{/* Test Results */}
<div className='flex items-center gap-2 bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded-md'>
{totalTests > 0 ? (
<>
<span
className={`text-sm font-medium ${
passRate === 100
? 'text-green-600 dark:text-green-400'
: passRate >= 80
? 'text-yellow-600 dark:text-yellow-400'
: 'text-red-600 dark:text-red-400'
}`}>
{passRate}% Pass Rate
</span>
<span className='text-gray-500 dark:text-gray-400 text-xs'>
({passedTests}/{totalTests} tests)
</span>
</>
) : (
<span className='text-gray-500 dark:text-gray-400 text-sm'>
No tests
</span>
)}
</div>
{/* Tags */}
{pattern.tags && pattern.tags.length > 0 && (
<div className='flex flex-wrap gap-2 max-h-20 overflow-y-auto'>
{pattern.tags.map(tag => (
<span
key={tag}
className='bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-300 px-2.5 py-0.5 rounded text-xs'>
{tag}
</span>
))}
{/* Description and Footer Section */}
<div className='flex-1 overflow-hidden'>
{pattern.description && (
<div
className='text-gray-300 text-xs h-full overflow-y-auto prose prose-invert prose-gray max-w-none
[&>ul]:list-disc [&>ul]:ml-4 [&>ul]:mt-2 [&>ul]:mb-4
[&>ol]:list-decimal [&>ol]:ml-4 [&>ol]:mt-2 [&>ol]:mb-4
[&>ul>li]:mt-0.5 [&>ol>li]:mt-0.5
[&_code]:bg-gray-900/50 [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded-md [&_code]:text-blue-300 [&_code]:border [&_code]:border-gray-700/50'>
<ReactMarkdown>{pattern.description}</ReactMarkdown>
</div>
)}
</div>
<hr className='border-gray-700 my-3' />
<div className='flex items-center justify-between'>
<div className='flex items-center'>
{totalTests > 0 ? (
<div
className={`px-2.5 py-1 rounded-md flex items-center gap-2 ${
passRate === 100
? 'bg-green-500/10 text-green-400'
: passRate >= 80
? 'bg-yellow-500/10 text-yellow-400'
: 'bg-red-500/10 text-red-400'
}`}>
<FlaskConical className='w-3.5 h-3.5' />
<span className='text-xs font-medium'>
{passedTests}/{totalTests} passing
</span>
</div>
) : (
<div className='px-2.5 py-1 rounded-md bg-gray-500/10 text-gray-400 flex items-center gap-2'>
<FlaskConical className='w-3.5 h-3.5' />
<span className='text-xs font-medium'>
No tests
</span>
</div>
)}
</div>
{/* Modified Date */}
{sortBy === 'dateModified' && pattern.modified_date && (
<span className='text-xs text-gray-500 dark:text-gray-400 text-left'>
Modified: {formatDate(pattern.modified_date)}
<span className='text-xs text-gray-400'>
Modified {formatDate(pattern.modified_date)}
</span>
)}
</div>