roll-bar.tsx 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. "use client";
  2. /**
  3. * <RollBar /> — two action buttons mounted above the movie list search bar.
  4. *
  5. * 🎲 Roll the Dice! — random winner from the unwatched pool
  6. * 🎭 Genre Roll! — opens the <GenreRollModal> for a filtered roll
  7. *
  8. * Both buttons are disabled (and carry a visible tooltip) when the list is
  9. * still loading OR the unwatched pool is empty. The tooltip strings are
  10. * hardcoded — no user/movie data is ever interpolated into `title=`, which
  11. * keeps the attribute XSS-safe.
  12. *
  13. * Minimum tap target: 44×44 px (WCAG 2.1 AA).
  14. */
  15. interface RollBarProps {
  16. isLoading: boolean;
  17. poolEmpty: boolean;
  18. onRoll: () => void;
  19. onGenreRoll: () => void;
  20. }
  21. function disabledReason(isLoading: boolean, poolEmpty: boolean): string | null {
  22. if (isLoading) return "Loading list\u2026";
  23. if (poolEmpty) return "Nothing to roll";
  24. return null;
  25. }
  26. export function RollBar({ isLoading, poolEmpty, onRoll, onGenreRoll }: RollBarProps) {
  27. const reason = disabledReason(isLoading, poolEmpty);
  28. const disabled = reason !== null;
  29. const randomTooltip = reason ?? "Roll the dice for a random pick";
  30. const genreTooltip = reason ?? "Pick genres and moods, then roll";
  31. const baseClasses =
  32. "inline-flex min-h-[44px] min-w-[44px] items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-semibold transition-colors";
  33. const enabledClasses =
  34. "bg-red-600 text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-400";
  35. const disabledClasses = "bg-gray-700 text-gray-400 cursor-not-allowed";
  36. return (
  37. <div
  38. className="flex flex-wrap items-center gap-3"
  39. aria-label="Randomizer actions"
  40. data-testid="roll-bar"
  41. >
  42. <button
  43. type="button"
  44. onClick={onRoll}
  45. disabled={disabled}
  46. aria-disabled={disabled}
  47. title={randomTooltip}
  48. data-testid="roll-bar-random"
  49. className={`${baseClasses} ${disabled ? disabledClasses : enabledClasses}`}
  50. >
  51. <span aria-hidden="true">🎲</span>
  52. <span>Roll the Dice!</span>
  53. </button>
  54. <button
  55. type="button"
  56. onClick={onGenreRoll}
  57. disabled={disabled}
  58. aria-disabled={disabled}
  59. title={genreTooltip}
  60. data-testid="roll-bar-genre"
  61. className={`${baseClasses} ${
  62. disabled
  63. ? disabledClasses
  64. : "bg-purple-600 text-white hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-400"
  65. }`}
  66. >
  67. <span aria-hidden="true">🎭</span>
  68. <span>Genre Roll!</span>
  69. </button>
  70. </div>
  71. );
  72. }