"use client"; import Link from "next/link"; import { HTMLAttributes } from "react"; import { clsx } from "clsx"; // ============================================================================= // Pill Types // ============================================================================= export interface PillProps extends HTMLAttributes { active?: boolean; disabled?: boolean; href?: string; onClick?: () => void; onRemove?: () => void; removable?: boolean; size?: "xs" | "sm" | "md" | "lg"; variant?: "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | "info" | "violet" | "blue" | "amber" | "emerald"; } // ============================================================================= // SharedPill Component // ============================================================================= export function SharedPill({ children, active = false, disabled = false, href, onClick, onRemove, removable = false, size = "sm", variant = "secondary", className, ...props }: PillProps) { // Size classes const sizeClasses = { xs: "px-2 py-0.5 text-[10px]", sm: "px-2.5 py-1 text-[11px]", md: "px-3 py-1.5 text-[12px]", lg: "px-4 py-2 text-[13px]", }; // Variant classes const variantClasses = { primary: { normal: "bg-[var(--accent-primary)]/10 text-[var(--accent-primary)] border-[var(--accent-primary)]/20", active: "bg-[var(--accent-primary)]/20 text-[var(--accent-primary)] border-[var(--accent-primary)]/30", }, secondary: { normal: "bg-[var(--bg-elevated)] text-[var(--text-secondary)] border-[var(--border-subtle)]", active: "bg-[var(--bg-glass)] text-[var(--text-primary)] border-[var(--border-medium)]", }, success: { normal: "bg-[var(--accent-emerald)]/10 text-[var(--accent-emerald)] border-[var(--accent-emerald)]/20", active: "bg-[var(--accent-emerald)]/20 text-[var(--accent-emerald)] border-[var(--accent-emerald)]/30", }, warning: { normal: "bg-[var(--accent-amber)]/10 text-[var(--accent-amber)] border-[var(--accent-amber)]/20", active: "bg-[var(--accent-amber)]/20 text-[var(--accent-amber)] border-[var(--accent-amber)]/30", }, danger: { normal: "bg-[var(--accent-red)]/10 text-[var(--accent-red)] border-[var(--accent-red)]/20", active: "bg-[var(--accent-red)]/20 text-[var(--accent-red)] border-[var(--accent-red)]/30", }, neutral: { normal: "bg-[var(--bg-glass)] text-[var(--text-muted)] border-[var(--border-subtle)]", active: "bg-[var(--bg-elevated)] text-[var(--text-secondary)] border-[var(--border-medium)]", }, // Additional variants info: { normal: "bg-blue-400/10 text-blue-400 border-blue-400/20", active: "bg-blue-400/20 text-blue-400 border-blue-400/30", }, violet: { normal: "bg-violet-400/10 text-violet-400 border-violet-400/20", active: "bg-violet-400/20 text-violet-400 border-violet-400/30", }, blue: { normal: "bg-blue-400/10 text-blue-400 border-blue-400/20", active: "bg-blue-400/20 text-blue-400 border-blue-400/30", }, amber: { normal: "bg-amber-400/10 text-amber-400 border-amber-400/20", active: "bg-amber-400/20 text-amber-400 border-amber-400/30", }, emerald: { normal: "bg-emerald-400/10 text-emerald-400 border-emerald-400/20", active: "bg-emerald-400/20 text-emerald-400 border-emerald-400/30", }, }; const v = variantClasses[variant]; const stateClasses = active ? v.active : v.normal; const s = sizeClasses[size]; // Build the pill content const pillContent = ( {children} {removable && !disabled && ( )} ); // Decide how to render if (href && !disabled) { return ( {pillContent} ); } if (onClick && !disabled) { return ( ); } return pillContent; } // ============================================================================= // Convenience Pill Components // ============================================================================= // Filter pill (for syllabus filters) export function FilterPill({ children, active, onClick, className, ...props }: Omit) { return ( {children} ); } // Topic pill (for chapter topics) export function TopicPill({ children, className, ...props }: Omit) { return ( {children} ); } // Priority pill (for exam priority) export type PriorityLevel = "critical" | "high" | "moderate" | "optional"; const priorityConfig: Record = { critical: { variant: "danger", label: "Exam critical" }, high: { variant: "warning", label: "High priority" }, moderate: { variant: "success", label: "Moderate" }, optional: { variant: "neutral", label: "Optional" }, }; export function PriorityPill({ level, showLabel = false, className, ...props }: { level: PriorityLevel; showLabel?: boolean } & Omit) { const config = priorityConfig[level]; return ( {showLabel ? config.label : level} ); } // Status pill export type StatusType = "completed" | "in-progress" | "not-started" | "weak"; const statusConfig: Record = { completed: { variant: "success", label: "Completed" }, "in-progress": { variant: "warning", label: "In Progress" }, "not-started": { variant: "neutral", label: "Not Started" }, weak: { variant: "danger", label: "Weak" }, }; export function StatusPill({ status, showLabel = false, className, ...props }: { status: StatusType; showLabel?: boolean } & Omit) { const config = statusConfig[status]; return ( {showLabel ? config.label : status} ); } // Confidence pill (for teach page) export type ConfidenceLevel = "high" | "medium" | "low" | "none"; const confidenceConfig: Record = { high: { variant: "success", label: "High confidence" }, medium: { variant: "warning", label: "Likely match" }, low: { variant: "danger", label: "Best guess" }, none: { variant: "neutral", label: "No clear match" }, }; export function ConfidencePill({ level, className, ...props }: { level: ConfidenceLevel } & Omit) { const config = confidenceConfig[level]; return ( {config.label} ); }