scene-dropdown.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. "use client";
  2. import { useState, useRef, useEffect } from "react";
  3. import { ChevronDown, Plus, Copy, Trash2 } from "lucide-react";
  4. import { useEditorStore } from "@/lib/store";
  5. import { useIsMobile } from "@/lib/use-mobile";
  6. export function SceneDropdown() {
  7. const [open, setOpen] = useState(false);
  8. const ref = useRef<HTMLDivElement>(null);
  9. const {
  10. scenes,
  11. activeSceneId,
  12. setActiveScene,
  13. createScene,
  14. duplicateScene,
  15. deleteScene,
  16. } = useEditorStore();
  17. const activeScene = scenes.find((s) => s.id === activeSceneId) || scenes[0];
  18. const isMobile = useIsMobile();
  19. useEffect(() => {
  20. function handleClickOutside(e: PointerEvent) {
  21. if (ref.current && !ref.current.contains(e.target as Node)) {
  22. setOpen(false);
  23. }
  24. }
  25. document.addEventListener("pointerdown", handleClickOutside);
  26. return () =>
  27. document.removeEventListener("pointerdown", handleClickOutside);
  28. }, []);
  29. return (
  30. <div ref={ref} className="relative">
  31. <button
  32. onClick={() => setOpen(!open)}
  33. className="flex items-center gap-1.5 px-2 py-1 rounded text-xs text-[#ccc] hover:text-white hover:bg-white/5 transition-colors"
  34. >
  35. <span className="truncate max-w-[120px]">{activeScene?.name}</span>
  36. <ChevronDown
  37. size={12}
  38. className={`transition-transform ${open ? "rotate-180" : ""}`}
  39. />
  40. </button>
  41. {open && (
  42. <div className="absolute top-full left-0 mt-1 w-56 bg-[#1a1a1a] border border-[#2a2a2a] rounded-md shadow-xl z-50 overflow-hidden">
  43. <button
  44. onClick={() => {
  45. createScene(`Scene ${scenes.length + 1}`);
  46. setOpen(false);
  47. }}
  48. className={`w-full flex items-center gap-2 px-3 ${isMobile ? "py-2.5" : "py-2"} text-xs text-[#ccc] hover:bg-white/5 hover:text-white active:bg-white/10 transition-colors`}
  49. >
  50. <Plus size={12} />
  51. New Scene
  52. </button>
  53. <div className="h-px bg-[#2a2a2a]" />
  54. <div className="max-h-60 overflow-y-auto py-1">
  55. {scenes.map((scene) => (
  56. <div
  57. key={scene.id}
  58. className={`group flex items-center justify-between px-3 ${isMobile ? "py-2.5" : "py-1.5"} cursor-pointer text-xs transition-colors ${
  59. scene.id === activeSceneId
  60. ? "bg-white/8 text-white"
  61. : "text-[#888] hover:text-[#ccc] hover:bg-white/3"
  62. }`}
  63. onClick={() => {
  64. setActiveScene(scene.id);
  65. setOpen(false);
  66. }}
  67. >
  68. <span className="truncate">{scene.name}</span>
  69. <div
  70. className={`${isMobile ? "flex" : "hidden group-hover:flex"} items-center gap-0.5`}
  71. >
  72. <button
  73. onClick={(e) => {
  74. e.stopPropagation();
  75. duplicateScene(scene.id);
  76. }}
  77. className={`${isMobile ? "p-1.5" : "p-0.5"} text-[#666] hover:text-white active:text-white`}
  78. title="Duplicate"
  79. >
  80. <Copy size={isMobile ? 14 : 10} />
  81. </button>
  82. {scenes.length > 1 && (
  83. <button
  84. onClick={(e) => {
  85. e.stopPropagation();
  86. deleteScene(scene.id);
  87. }}
  88. className={`${isMobile ? "p-1.5" : "p-0.5"} text-[#666] hover:text-red-400 active:text-red-400`}
  89. title="Delete"
  90. >
  91. <Trash2 size={isMobile ? 14 : 10} />
  92. </button>
  93. )}
  94. </div>
  95. </div>
  96. ))}
  97. </div>
  98. </div>
  99. )}
  100. </div>
  101. );
  102. }