mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-22 10:51:02 +01:00
style: various improvements for regex card styling
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user