diff --git a/index.html b/index.html index a5734ba4..9e1fc93b 100644 --- a/index.html +++ b/index.html @@ -40,6 +40,30 @@ /> + + + diff --git a/src/components/common/AddToCompareButton.tsx b/src/components/common/AddToCompareButton.tsx new file mode 100644 index 00000000..d0ab6f43 --- /dev/null +++ b/src/components/common/AddToCompareButton.tsx @@ -0,0 +1,91 @@ +import { Check, GitCompareArrows } from 'lucide-react'; +import { Tooltip } from '@/components/ui/tooltip'; +import { cn } from '@/lib/utils'; +import { + COMPARE_LIMIT_REASON, + MAX_COMPARE_KEYS, + useCompareStore, + type CompareKey, +} from '@/hooks/useCompareStore'; + +interface AddToCompareButtonProps { + compareKey: CompareKey; + className?: string; +} + +const BASE_CLASS = + 'inline-flex items-center gap-1.5 rounded-sm border px-2.5 py-1.5 font-mono text-[9px] uppercase tracking-wider transition-colors outline-none focus-visible:ring-2 focus-visible:ring-gray-400 dark:focus-visible:ring-white/50'; + +export default function AddToCompareButton({ + compareKey, + className, +}: AddToCompareButtonProps) { + const isSelected = useCompareStore(state => + state.keys.some(key => key.id === compareKey.id) + ); + const isFull = useCompareStore( + state => state.keys.length >= MAX_COMPARE_KEYS + ); + const add = useCompareStore(state => state.add); + const remove = useCompareStore(state => state.remove); + + if (isSelected) { + return ( + + ); + } + + if (isFull) { + // aria-disabled (not `disabled`) keeps the button focusable so + // keyboard users can still reach the tooltip explaining why. + return ( + + + + ); + } + + return ( + + ); +} diff --git a/src/components/common/CompareBar.tsx b/src/components/common/CompareBar.tsx new file mode 100644 index 00000000..97f8f2cd --- /dev/null +++ b/src/components/common/CompareBar.tsx @@ -0,0 +1,87 @@ +import { ArrowRight, X } from 'lucide-react'; +import { Link } from 'react-router'; +import CreatorInitialsAvatar from '@/components/common/CreatorInitialsAvatar'; +import { MAX_COMPARE_KEYS, useCompareStore } from '@/hooks/useCompareStore'; + +/** + * Sticky bottom bar listing the keys queued for comparison (#1086). + * Hidden until at least one key is selected. + */ +export default function CompareBar() { + const keys = useCompareStore(state => state.keys); + const remove = useCompareStore(state => state.remove); + const clear = useCompareStore(state => state.clear); + + if (keys.length === 0) return null; + + const canCompare = keys.length >= 2; + + return ( + <> + {/* Reserves space so the fixed bar never covers the page footer. */} +