diff options
| author | Maze Winther <mazewinther@gmail.com> | 2025-07-20 16:36:21 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2025-07-20 16:36:21 +0200 |
| commit | ba3da43728bc051aa033dc7b45042b0f0f788e63 (patch) | |
| tree | 6039c0ddd5b19c7ecbcfbc6635e2495d85bd948b /apps/web/src/components/editor/media-panel/tabbar.tsx | |
| parent | afa8f426fbe8617980d848cdda977f2ab47b5e4e (diff) | |
| parent | baaf773b809674c07b4ab17ae3098abd768050ab (diff) | |
| download | opencut-ba3da43728bc051aa033dc7b45042b0f0f788e63.tar.gz | |
Merge branch 'staging'
Diffstat (limited to 'apps/web/src/components/editor/media-panel/tabbar.tsx')
| -rw-r--r-- | apps/web/src/components/editor/media-panel/tabbar.tsx | 248 |
1 files changed, 124 insertions, 124 deletions
diff --git a/apps/web/src/components/editor/media-panel/tabbar.tsx b/apps/web/src/components/editor/media-panel/tabbar.tsx index 50793243..b05bee7e 100644 --- a/apps/web/src/components/editor/media-panel/tabbar.tsx +++ b/apps/web/src/components/editor/media-panel/tabbar.tsx @@ -1,124 +1,124 @@ -"use client"; - -import { cn } from "@/lib/utils"; -import { Tab, tabs, useMediaPanelStore } from "./store"; -import { Button } from "@/components/ui/button"; -import { ChevronRight, ChevronLeft } from "lucide-react"; -import { useRef, useState, useEffect } from "react"; - -export function TabBar() { - const { activeTab, setActiveTab } = useMediaPanelStore(); - const scrollContainerRef = useRef<HTMLDivElement>(null); - const [isAtEnd, setIsAtEnd] = useState(false); - const [isAtStart, setIsAtStart] = useState(true); - - const scrollToEnd = () => { - if (scrollContainerRef.current) { - scrollContainerRef.current.scrollTo({ - left: scrollContainerRef.current.scrollWidth, - }); - setIsAtEnd(true); - setIsAtStart(false); - } - }; - - const scrollToStart = () => { - if (scrollContainerRef.current) { - scrollContainerRef.current.scrollTo({ - left: 0, - }); - setIsAtStart(true); - setIsAtEnd(false); - } - }; - - const checkScrollPosition = () => { - if (scrollContainerRef.current) { - const { scrollLeft, scrollWidth, clientWidth } = - scrollContainerRef.current; - const isAtEndNow = scrollLeft + clientWidth >= scrollWidth - 1; - const isAtStartNow = scrollLeft <= 1; - setIsAtEnd(isAtEndNow); - setIsAtStart(isAtStartNow); - } - }; - - // We're using useEffect because we need to sync with external DOM scroll events - useEffect(() => { - const container = scrollContainerRef.current; - if (!container) return; - - checkScrollPosition(); - container.addEventListener("scroll", checkScrollPosition); - - const resizeObserver = new ResizeObserver(checkScrollPosition); - resizeObserver.observe(container); - - return () => { - container.removeEventListener("scroll", checkScrollPosition); - resizeObserver.disconnect(); - }; - }, []); - - return ( - <div className="flex"> - <ScrollButton - direction="left" - onClick={scrollToStart} - isVisible={!isAtStart} - /> - <div - ref={scrollContainerRef} - className="h-12 bg-panel-accent px-3 flex justify-start items-center gap-5 overflow-x-auto scrollbar-x-hidden relative w-full" - > - {(Object.keys(tabs) as Tab[]).map((tabKey) => { - const tab = tabs[tabKey]; - return ( - <div - className={cn( - "flex flex-col gap-0.5 items-center cursor-pointer", - activeTab === tabKey ? "text-primary" : "text-muted-foreground", - )} - onClick={() => setActiveTab(tabKey)} - key={tabKey} - > - <tab.icon className="!size-[1.1rem]" /> - <span className="text-[0.65rem]">{tab.label}</span> - </div> - ); - })} - </div> - <ScrollButton - direction="right" - onClick={scrollToEnd} - isVisible={!isAtEnd} - /> - </div> - ); -} - -function ScrollButton({ - direction, - onClick, - isVisible, -}: { - direction: "left" | "right"; - onClick: () => void; - isVisible: boolean; -}) { - if (!isVisible) return null; - - const Icon = direction === "left" ? ChevronLeft : ChevronRight; - - return ( - <div className="bg-panel-accent w-12 h-full flex items-center justify-center"> - <Button - size="icon" - className="rounded-[0.4rem] w-4 h-7 !bg-foreground/10" - onClick={onClick} - > - <Icon className="!size-4 text-foreground" /> - </Button> - </div> - ); -} +"use client";
+
+import { cn } from "@/lib/utils";
+import { Tab, tabs, useMediaPanelStore } from "./store";
+import { Button } from "@/components/ui/button";
+import { ChevronRight, ChevronLeft } from "lucide-react";
+import { useRef, useState, useEffect } from "react";
+
+export function TabBar() {
+ const { activeTab, setActiveTab } = useMediaPanelStore();
+ const scrollContainerRef = useRef<HTMLDivElement>(null);
+ const [isAtEnd, setIsAtEnd] = useState(false);
+ const [isAtStart, setIsAtStart] = useState(true);
+
+ const scrollToEnd = () => {
+ if (scrollContainerRef.current) {
+ scrollContainerRef.current.scrollTo({
+ left: scrollContainerRef.current.scrollWidth,
+ });
+ setIsAtEnd(true);
+ setIsAtStart(false);
+ }
+ };
+
+ const scrollToStart = () => {
+ if (scrollContainerRef.current) {
+ scrollContainerRef.current.scrollTo({
+ left: 0,
+ });
+ setIsAtStart(true);
+ setIsAtEnd(false);
+ }
+ };
+
+ const checkScrollPosition = () => {
+ if (scrollContainerRef.current) {
+ const { scrollLeft, scrollWidth, clientWidth } =
+ scrollContainerRef.current;
+ const isAtEndNow = scrollLeft + clientWidth >= scrollWidth - 1;
+ const isAtStartNow = scrollLeft <= 1;
+ setIsAtEnd(isAtEndNow);
+ setIsAtStart(isAtStartNow);
+ }
+ };
+
+ // We're using useEffect because we need to sync with external DOM scroll events
+ useEffect(() => {
+ const container = scrollContainerRef.current;
+ if (!container) return;
+
+ checkScrollPosition();
+ container.addEventListener("scroll", checkScrollPosition);
+
+ const resizeObserver = new ResizeObserver(checkScrollPosition);
+ resizeObserver.observe(container);
+
+ return () => {
+ container.removeEventListener("scroll", checkScrollPosition);
+ resizeObserver.disconnect();
+ };
+ }, []);
+
+ return (
+ <div className="flex">
+ <ScrollButton
+ direction="left"
+ onClick={scrollToStart}
+ isVisible={!isAtStart}
+ />
+ <div
+ ref={scrollContainerRef}
+ className="h-12 bg-panel-accent px-3 flex justify-start items-center gap-5 overflow-x-auto scrollbar-x-hidden relative w-full"
+ >
+ {(Object.keys(tabs) as Tab[]).map((tabKey) => {
+ const tab = tabs[tabKey];
+ return (
+ <div
+ className={cn(
+ "flex flex-col gap-0.5 items-center cursor-pointer",
+ activeTab === tabKey ? "text-primary" : "text-muted-foreground"
+ )}
+ onClick={() => setActiveTab(tabKey)}
+ key={tabKey}
+ >
+ <tab.icon className="!size-[1.1rem]" />
+ <span className="text-[0.65rem]">{tab.label}</span>
+ </div>
+ );
+ })}
+ </div>
+ <ScrollButton
+ direction="right"
+ onClick={scrollToEnd}
+ isVisible={!isAtEnd}
+ />
+ </div>
+ );
+}
+
+function ScrollButton({
+ direction,
+ onClick,
+ isVisible,
+}: {
+ direction: "left" | "right";
+ onClick: () => void;
+ isVisible: boolean;
+}) {
+ if (!isVisible) return null;
+
+ const Icon = direction === "left" ? ChevronLeft : ChevronRight;
+
+ return (
+ <div className="bg-panel-accent w-12 h-full flex items-center justify-center">
+ <Button
+ size="icon"
+ className="rounded-[0.4rem] w-4 h-7 !bg-foreground/10"
+ onClick={onClick}
+ >
+ <Icon className="!size-4 text-foreground" />
+ </Button>
+ </div>
+ );
+}
|
