website refactor
This commit is contained in:
@@ -1,122 +1,56 @@
|
||||
|
||||
|
||||
import type { MouseEventHandler, ReactNode } from 'react';
|
||||
import { Card } from './Card';
|
||||
|
||||
interface PresetCardStat {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
import React from 'react';
|
||||
import { Box } from './primitives/Box';
|
||||
import { Text } from './Text';
|
||||
import { Surface } from './primitives/Surface';
|
||||
import { Icon } from './Icon';
|
||||
import { LucideIcon } from 'lucide-react';
|
||||
|
||||
export interface PresetCardProps {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
primaryTag?: string;
|
||||
description?: string;
|
||||
stats?: PresetCardStat[];
|
||||
selected?: boolean;
|
||||
disabled?: boolean;
|
||||
onSelect?: () => void;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
description: string;
|
||||
icon: LucideIcon;
|
||||
onClick: () => void;
|
||||
isSelected?: boolean;
|
||||
}
|
||||
|
||||
export function PresetCard({
|
||||
title,
|
||||
subtitle,
|
||||
primaryTag,
|
||||
description,
|
||||
stats,
|
||||
selected,
|
||||
disabled,
|
||||
onSelect,
|
||||
className = '',
|
||||
children,
|
||||
}: PresetCardProps) {
|
||||
const isInteractive = typeof onSelect === 'function' && !disabled;
|
||||
|
||||
const handleClick: MouseEventHandler<HTMLButtonElement | HTMLDivElement> = (event) => {
|
||||
if (!isInteractive) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
onSelect?.();
|
||||
};
|
||||
|
||||
const baseBorder = selected ? 'border-primary-blue' : 'border-charcoal-outline';
|
||||
const baseBg = selected ? 'bg-primary-blue/10' : 'bg-iron-gray';
|
||||
const baseRing = selected ? 'ring-2 ring-primary-blue/40' : '';
|
||||
const disabledClasses = disabled ? 'opacity-60 cursor-not-allowed' : '';
|
||||
const hoverClasses = isInteractive && !disabled ? 'hover:bg-iron-gray/80 hover:scale-[1.01]' : '';
|
||||
|
||||
const content = (
|
||||
<div className="flex h-full flex-col gap-3">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<div className="text-sm font-semibold text-white">{title}</div>
|
||||
{subtitle && (
|
||||
<div className="mt-0.5 text-xs text-gray-400">{subtitle}</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
{primaryTag && (
|
||||
<span className="inline-flex rounded-full bg-primary-blue/15 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-primary-blue">
|
||||
{primaryTag}
|
||||
</span>
|
||||
)}
|
||||
{selected && (
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-primary-blue/10 px-2 py-0.5 text-[10px] font-medium text-primary-blue">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-primary-blue" />
|
||||
Selected
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{description && (
|
||||
<p className="text-xs text-gray-300">{description}</p>
|
||||
)}
|
||||
|
||||
{children}
|
||||
|
||||
{stats && stats.length > 0 && (
|
||||
<div className="mt-1 border-t border-charcoal-outline/70 pt-2">
|
||||
<dl className="grid grid-cols-1 gap-2 text-[11px] text-gray-400 sm:grid-cols-3">
|
||||
{stats.map((stat) => (
|
||||
<div key={stat.label} className="space-y-0.5">
|
||||
<dt className="font-medium text-gray-500">{stat.label}</dt>
|
||||
<dd className="text-xs text-gray-200">{stat.value}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const commonClasses = `${baseBorder} ${baseBg} ${baseRing} ${hoverClasses} ${disabledClasses} ${className}`;
|
||||
|
||||
if (isInteractive) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClick as MouseEventHandler<HTMLButtonElement>}
|
||||
disabled={disabled}
|
||||
className={`group block w-full rounded-lg text-left text-sm shadow-card outline-none transition-all duration-150 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-blue ${commonClasses}`}
|
||||
>
|
||||
<div className="p-4">
|
||||
{content}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export const PresetCard = ({
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
onClick,
|
||||
isSelected = false
|
||||
}: PresetCardProps) => {
|
||||
return (
|
||||
<Card
|
||||
className={commonClasses}
|
||||
onClick={handleClick as MouseEventHandler<HTMLDivElement>}
|
||||
<Surface
|
||||
variant={isSelected ? 'default' : 'muted'}
|
||||
rounded="lg"
|
||||
padding={4}
|
||||
onClick={onClick}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
border: isSelected ? '2px solid var(--ui-color-intent-primary)' : '1px solid var(--ui-color-border-default)',
|
||||
transition: 'all 0.2s ease-in-out'
|
||||
}}
|
||||
className="group hover:bg-white/5"
|
||||
>
|
||||
{content}
|
||||
</Card>
|
||||
<Box display="flex" alignItems="start" gap={4}>
|
||||
<Box
|
||||
padding={3}
|
||||
rounded="lg"
|
||||
bg={isSelected ? 'var(--ui-color-intent-primary)' : 'var(--ui-color-bg-surface-muted)'}
|
||||
className="transition-colors"
|
||||
>
|
||||
<Icon icon={icon} size={6} intent={isSelected ? 'high' : 'low'} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text weight="bold" variant="high" block marginBottom={1}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text size="sm" variant="low">
|
||||
{description}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Surface>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user