ADD new frontend
This commit is contained in:
171
frontend/src/components/ui/StatusIndicator/StatusIndicator.tsx
Normal file
171
frontend/src/components/ui/StatusIndicator/StatusIndicator.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import { forwardRef } from 'react';
|
||||
import { clsx } from 'clsx';
|
||||
|
||||
export interface StatusIndicatorProps {
|
||||
status: 'success' | 'warning' | 'danger' | 'error' | 'info' | 'neutral';
|
||||
size?: 'xs' | 'sm' | 'md' | 'lg';
|
||||
variant?: 'dot' | 'badge' | 'pill';
|
||||
label?: string;
|
||||
showLabel?: boolean;
|
||||
pulse?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const StatusIndicator = forwardRef<HTMLDivElement, StatusIndicatorProps>(({
|
||||
status,
|
||||
size = 'md',
|
||||
variant = 'dot',
|
||||
label,
|
||||
showLabel = false,
|
||||
pulse = false,
|
||||
className,
|
||||
...props
|
||||
}, ref) => {
|
||||
const statusClasses = {
|
||||
success: {
|
||||
bg: 'bg-[var(--color-success)]',
|
||||
text: 'text-[var(--color-success)]',
|
||||
border: 'border-[var(--color-success)]',
|
||||
},
|
||||
warning: {
|
||||
bg: 'bg-[var(--color-warning)]',
|
||||
text: 'text-[var(--color-warning)]',
|
||||
border: 'border-[var(--color-warning)]',
|
||||
},
|
||||
danger: {
|
||||
bg: 'bg-[var(--color-error)]',
|
||||
text: 'text-[var(--color-error)]',
|
||||
border: 'border-[var(--color-error)]',
|
||||
},
|
||||
error: {
|
||||
bg: 'bg-[var(--color-error)]',
|
||||
text: 'text-[var(--color-error)]',
|
||||
border: 'border-[var(--color-error)]',
|
||||
},
|
||||
info: {
|
||||
bg: 'bg-[var(--color-info)]',
|
||||
text: 'text-[var(--color-info)]',
|
||||
border: 'border-[var(--color-info)]',
|
||||
},
|
||||
neutral: {
|
||||
bg: 'bg-[var(--text-tertiary)]',
|
||||
text: 'text-[var(--text-tertiary)]',
|
||||
border: 'border-[var(--text-tertiary)]',
|
||||
},
|
||||
};
|
||||
|
||||
const sizeClasses = {
|
||||
xs: {
|
||||
dot: 'w-2 h-2',
|
||||
badge: 'w-4 h-4',
|
||||
text: 'text-xs',
|
||||
},
|
||||
sm: {
|
||||
dot: 'w-3 h-3',
|
||||
badge: 'w-5 h-5',
|
||||
text: 'text-sm',
|
||||
},
|
||||
md: {
|
||||
dot: 'w-4 h-4',
|
||||
badge: 'w-6 h-6',
|
||||
text: 'text-base',
|
||||
},
|
||||
lg: {
|
||||
dot: 'w-5 h-5',
|
||||
badge: 'w-8 h-8',
|
||||
text: 'text-lg',
|
||||
},
|
||||
};
|
||||
|
||||
const renderDot = () => (
|
||||
<div
|
||||
className={clsx(
|
||||
'rounded-full',
|
||||
sizeClasses[size].dot,
|
||||
statusClasses[status].bg,
|
||||
{
|
||||
'animate-pulse': pulse,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
const renderBadge = () => (
|
||||
<div
|
||||
className={clsx(
|
||||
'rounded-full border-2 flex items-center justify-center',
|
||||
sizeClasses[size].badge,
|
||||
statusClasses[status].border,
|
||||
'bg-white',
|
||||
{
|
||||
'animate-pulse': pulse,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
'rounded-full',
|
||||
`w-${parseInt(sizeClasses[size].badge.split(' ')[0].substring(2)) - 2}`,
|
||||
`h-${parseInt(sizeClasses[size].badge.split(' ')[1].substring(2)) - 2}`,
|
||||
statusClasses[status].bg
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderPill = () => (
|
||||
<div
|
||||
className={clsx(
|
||||
'rounded-full px-2 py-1 text-white text-xs font-medium',
|
||||
statusClasses[status].bg,
|
||||
{
|
||||
'animate-pulse': pulse,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{label || status.charAt(0).toUpperCase() + status.slice(1)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderIndicator = () => {
|
||||
switch (variant) {
|
||||
case 'badge':
|
||||
return renderBadge();
|
||||
case 'pill':
|
||||
return renderPill();
|
||||
default:
|
||||
return renderDot();
|
||||
}
|
||||
};
|
||||
|
||||
if (showLabel && variant !== 'pill') {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={clsx('flex items-center gap-2', className)}
|
||||
{...props}
|
||||
>
|
||||
{renderIndicator()}
|
||||
<span
|
||||
className={clsx(
|
||||
'font-medium',
|
||||
sizeClasses[size].text,
|
||||
statusClasses[status].text
|
||||
)}
|
||||
>
|
||||
{label || status.charAt(0).toUpperCase() + status.slice(1)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={ref} className={clsx(className)} {...props}>
|
||||
{renderIndicator()}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
StatusIndicator.displayName = 'StatusIndicator';
|
||||
|
||||
export default StatusIndicator;
|
||||
2
frontend/src/components/ui/StatusIndicator/index.ts
Normal file
2
frontend/src/components/ui/StatusIndicator/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as StatusIndicator } from './StatusIndicator';
|
||||
export type { StatusIndicatorProps } from './StatusIndicator';
|
||||
Reference in New Issue
Block a user