refactor(gallery): modularize gallery and timeline
Break down the monolithic Gallery and Timeline components into smaller, highly cohesive modules to improve maintainability and separation of concerns. - Extract core UI elements into dedicated components (`ImageTile`, `TruncatedFilename`, `ScrubberYearBlock`). - Separate loading and empty states into dedicated files (`GalleryEmptyState`, `TimelineEmptyState`). - Move complex timeline grouping, row virtualization mapping, and duration formatting logic into utility files (`timelineModel.ts` and `format.ts`). - Abstract shared data structures into `types.ts` to cleanly decouple domain logic from the presentation layer.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import { useLayoutEffect, useRef, useState } from "react";
|
||||
import { Tooltip } from "../Tooltip";
|
||||
|
||||
export function TruncatedFilename({ filename }: { filename: string }) {
|
||||
const textRef = useRef<HTMLParagraphElement>(null);
|
||||
const [isTruncated, setIsTruncated] = useState(false);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const text = textRef.current;
|
||||
if (!text) return;
|
||||
|
||||
const update = () => {
|
||||
setIsTruncated(text.scrollWidth > text.clientWidth);
|
||||
};
|
||||
|
||||
update();
|
||||
|
||||
const observer = new ResizeObserver(update);
|
||||
observer.observe(text);
|
||||
return () => observer.disconnect();
|
||||
}, [filename]);
|
||||
|
||||
const label = (
|
||||
<p ref={textRef} className="truncate text-[12px] font-medium leading-tight text-white">
|
||||
{filename}
|
||||
</p>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip label={filename} delay={500} block followCursor disabled={!isTruncated}>
|
||||
{label}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user