Primitives.tsx (TS2339): - asChild branch read children.props.className before the cast applied, making props typed as unknown. Extract typedChild first, then read props. hermes/page.tsx + agents/page.tsx + tasks/page.tsx + tasks/[id]/page.tsx (TS2322): - Badge.variant accepts 'neutral'|'success'|'warning'|'error'|'info' but callers were passing 'danger' (should be 'error') and 'default' (should be 'neutral'). MetricCard.tone is a separate type and is correct as-is. Changes: - statusTone map in hermes/page.tsx: 'danger' → 'error', 'default' → 'neutral' - getTaskTone fallback: 'default' → 'neutral'; explicit return type added - levelTone in tasks/[id]/page.tsx: 'danger' → 'error'; explicit return type added - Inline Badge variants: all remaining 'danger' → 'error' across 3 files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
108 lines
3.8 KiB
TypeScript
108 lines
3.8 KiB
TypeScript
import * as React from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
// Basic button component using design tokens
|
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
variant?: 'primary' | 'secondary' | 'ghost' | 'link';
|
|
size?: 'sm' | 'md' | 'lg';
|
|
asChild?: boolean;
|
|
}
|
|
|
|
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
({ variant = 'primary', size = 'md', asChild = false, className, children, ...props }, ref) => {
|
|
const baseStyles = 'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50';
|
|
|
|
const variantStyles = {
|
|
primary: 'bg-[var(--bl-accent)] text-[var(--bl-accent-foreground)] hover:opacity-90 focus-visible:ring-[var(--bl-focus-ring)]',
|
|
secondary: 'bg-[var(--bl-surface-muted)] text-[var(--bl-text-primary)] hover:bg-[var(--bl-surface-highlight)] focus-visible:ring-[var(--bl-focus-ring)]',
|
|
ghost: 'text-[var(--bl-text-primary)] hover:bg-[var(--bl-surface-muted)] focus-visible:ring-[var(--bl-focus-ring)]',
|
|
link: 'text-[var(--bl-accent)] hover:underline focus-visible:ring-[var(--bl-focus-ring)]',
|
|
};
|
|
|
|
const sizeStyles = {
|
|
sm: 'h-9 px-3 text-sm',
|
|
md: 'h-10 px-4 text-sm',
|
|
lg: 'h-11 px-8 text-base',
|
|
};
|
|
|
|
const classes = cn(baseStyles, variantStyles[variant], sizeStyles[size], className);
|
|
|
|
if (asChild && React.isValidElement(children)) {
|
|
const typedChild = children as React.ReactElement<{ className?: string }>;
|
|
return React.cloneElement(typedChild, {
|
|
className: cn(typedChild.props.className, classes),
|
|
});
|
|
}
|
|
|
|
return (
|
|
<button
|
|
ref={ref}
|
|
className={classes}
|
|
{...props}
|
|
>
|
|
{children}
|
|
</button>
|
|
);
|
|
},
|
|
);
|
|
|
|
Button.displayName = 'Button';
|
|
|
|
// Basic badge component using design tokens
|
|
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
variant?: 'neutral' | 'success' | 'warning' | 'error' | 'info';
|
|
dot?: boolean;
|
|
}
|
|
|
|
export function Badge({ variant = 'neutral', dot = false, className, children, ...props }: BadgeProps) {
|
|
const variantStyles = {
|
|
neutral: 'bg-[var(--bl-surface-muted)] text-[var(--bl-fg-muted)]',
|
|
success: 'bg-[var(--bl-success-bg)] text-[var(--bl-success-fg)]',
|
|
warning: 'bg-[var(--bl-warning-bg)] text-[var(--bl-warning-fg)]',
|
|
error: 'bg-[var(--bl-danger-bg)] text-[var(--bl-danger-fg)]',
|
|
info: 'bg-[var(--bl-info-bg)] text-[var(--bl-info-fg)]',
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium',
|
|
variantStyles[variant],
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
{dot && <span className="h-1.5 w-1.5 rounded-full current-color" />}
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Input component using design tokens
|
|
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
variant?: 'surface' | 'muted';
|
|
}
|
|
|
|
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
({ variant = 'surface', className, ...props }, ref) => {
|
|
const variantStyles = {
|
|
surface: 'bg-[var(--bl-input)] border-[var(--bl-border)]',
|
|
muted: 'bg-[var(--bl-surface-muted)] border-[var(--bl-border)]',
|
|
};
|
|
|
|
return (
|
|
<input
|
|
ref={ref}
|
|
className={cn(
|
|
'flex h-10 w-full rounded-md border px-3.5 py-2.5 text-sm placeholder:text-[var(--bl-fg-muted)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--bl-primary)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
|
variantStyles[variant],
|
|
className,
|
|
)}
|
|
{...props}
|
|
/>
|
|
);
|
|
},
|
|
);
|
|
|
|
Input.displayName = 'Input';
|