📏 Fixes for responsiveness

This commit is contained in:
2026-02-17 20:29:03 -05:00
parent b47b38cc8d
commit 37a2d0c75b
6 changed files with 87 additions and 53 deletions

View File

@@ -34,6 +34,11 @@ import { styled } from "styled-components";
import { COMICVINE_SERVICE_URI } from "../../constants/endpoints"; import { COMICVINE_SERVICE_URI } from "../../constants/endpoints";
import { refineQuery } from "filename-parser"; import { refineQuery } from "filename-parser";
// overridden <SlidingPanel> with some styles - moved outside component to prevent recreation
const StyledSlidingPanel = styled(SlidingPane)`
background: #ccc;
`;
interface ComicDetailProps { interface ComicDetailProps {
data: { data: {
_id: string; _id: string;
@@ -98,11 +103,7 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
// ); // );
}, []); }, []);
// overridden <SlidingPanel> with some styles const afterOpenModal = useCallback((things: any) => {
const StyledSlidingPanel = styled(SlidingPane)`
background: #ccc;
`;
const afterOpenModal = useCallback((things) => {
// references are now sync'd and can be accessed. // references are now sync'd and can be accessed.
// subtitle.style.color = "#f00"; // subtitle.style.color = "#f00";
console.log("kolaveri", things); console.log("kolaveri", things);

View File

@@ -14,34 +14,41 @@ import { useStore } from "../../../store";
import { useShallow } from "zustand/react/shallow"; import { useShallow } from "zustand/react/shallow";
import { escapePoundSymbol } from "../../../shared/utils/formatting.utils"; import { escapePoundSymbol } from "../../../shared/utils/formatting.utils";
export const ArchiveOperations = (props): ReactElement => { export const ArchiveOperations = (props: { data: any }): ReactElement => {
const { data } = props; const { data } = props;
const { socketIOInstance } = useStore( const getSocket = useStore((state) => state.getSocket);
useShallow((state) => ({
socketIOInstance: state.socketIOInstance,
})),
);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
// sliding panel config // sliding panel config
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [slidingPanelContentId, setSlidingPanelContentId] = useState(""); const [slidingPanelContentId, setSlidingPanelContentId] = useState("");
// current image // current image
const [currentImage, setCurrentImage] = useState([]); const [currentImage, setCurrentImage] = useState<string>("");
const [uncompressedArchive, setUncompressedArchive] = useState([]); const [uncompressedArchive, setUncompressedArchive] = useState<string[]>([]);
const [imageAnalysisResult, setImageAnalysisResult] = useState({}); const [imageAnalysisResult, setImageAnalysisResult] = useState<any>({});
const [shouldRefetchComicBookData, setShouldRefetchComicBookData] = const [shouldRefetchComicBookData, setShouldRefetchComicBookData] =
useState(false); useState(false);
const constructImagePaths = (data): Array<string> => { const constructImagePaths = (data: string[]): Array<string> => {
return data?.map((path: string) => return data?.map((path: string) =>
escapePoundSymbol(encodeURI(`${LIBRARY_SERVICE_HOST}/${path}`)), escapePoundSymbol(encodeURI(`${LIBRARY_SERVICE_HOST}/${path}`)),
); );
}; };
// Listen to the uncompression complete event and orchestrate the final payload // Listen to the uncompression complete event and orchestrate the final payload
socketIOInstance.on("LS_UNCOMPRESSION_JOB_COMPLETE", (data) => { useEffect(() => {
setUncompressedArchive(constructImagePaths(data?.uncompressedArchive)); const socket = getSocket("/");
}); if (!socket) return;
const handleUncompressionComplete = (data: any) => {
setUncompressedArchive(constructImagePaths(data?.uncompressedArchive));
};
socket.on("LS_UNCOMPRESSION_JOB_COMPLETE", handleUncompressionComplete);
return () => {
socket.off("LS_UNCOMPRESSION_JOB_COMPLETE", handleUncompressionComplete);
};
}, [getSocket]);
useEffect(() => { useEffect(() => {
let isMounted = true; let isMounted = true;
@@ -58,7 +65,7 @@ export const ArchiveOperations = (props): ReactElement => {
}, },
transformResponse: async (responseData) => { transformResponse: async (responseData) => {
const parsedData = JSON.parse(responseData); const parsedData = JSON.parse(responseData);
const paths = parsedData.map((pathObject) => { const paths = parsedData.map((pathObject: any) => {
return `${pathObject.containedIn}/${pathObject.name}${pathObject.extension}`; return `${pathObject.containedIn}/${pathObject.name}${pathObject.extension}`;
}); });
const uncompressedArchive = constructImagePaths(paths); const uncompressedArchive = constructImagePaths(paths);
@@ -131,7 +138,7 @@ export const ArchiveOperations = (props): ReactElement => {
} }
// sliding panel init // sliding panel init
const contentForSlidingPanel = { const contentForSlidingPanel: Record<string, { content: () => JSX.Element }> = {
imageAnalysis: { imageAnalysis: {
content: () => { content: () => {
return ( return (
@@ -143,7 +150,7 @@ export const ArchiveOperations = (props): ReactElement => {
</pre> </pre>
) : null} ) : null}
<pre className="font-hasklig mt-3 text-sm"> <pre className="font-hasklig mt-3 text-sm">
{JSON.stringify(imageAnalysisResult.analyzedData, null, 2)} {JSON.stringify(imageAnalysisResult?.analyzedData, null, 2)}
</pre> </pre>
</div> </div>
); );
@@ -152,7 +159,7 @@ export const ArchiveOperations = (props): ReactElement => {
}; };
// sliding panel handlers // sliding panel handlers
const openImageAnalysisPanel = useCallback((imageFilePath) => { const openImageAnalysisPanel = useCallback((imageFilePath: string) => {
setSlidingPanelContentId("imageAnalysis"); setSlidingPanelContentId("imageAnalysis");
analyzeImage(imageFilePath); analyzeImage(imageFilePath);
setCurrentImage(imageFilePath); setCurrentImage(imageFilePath);

View File

@@ -1,18 +1,18 @@
import { isUndefined } from "lodash"; import { isUndefined } from "lodash";
import React, { ReactElement } from "react"; import React, { ReactElement } from "react";
export const ComicInfoXML = (data): ReactElement => { export const ComicInfoXML = (data: { json: any }): ReactElement => {
const { json } = data; const { json } = data;
return ( return (
<div className="flex md:w-4/5 lg:w-78"> <div className="flex md:w-4/5 lg:w-78">
<dl className="dark:bg-yellow-600 bg-yellow-200 p-3 rounded-lg"> <dl className="dark:bg-yellow-600 bg-yellow-200 p-3 rounded-lg">
<dt> <dt>
<p className="text-lg">{json.series[0]}</p> <p className="text-lg">{json.series?.[0]}</p>
</dt> </dt>
<dd className="text-sm"> <dd className="text-sm">
published by{" "} published by{" "}
<span className="underline"> <span className="underline">
{json.publisher[0]} {json.publisher?.[0]}
<i className="icon-[solar--arrow-right-up-outline] w-4 h-4" /> <i className="icon-[solar--arrow-right-up-outline] w-4 h-4" />
</span> </span>
</dd> </dd>
@@ -30,18 +30,20 @@ export const ComicInfoXML = (data): ReactElement => {
</span> </span>
</dd> </dd>
)} )}
<dd className="my-2"> {/* Genre */}
{/* Genre */} {!isUndefined(json.genre) && (
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-sm font-medium px-2 rounded-md dark:text-slate-900 dark:bg-slate-400"> <dd className="my-2">
<span className="pr-1 pt-1"> <span className="inline-flex items-center bg-slate-50 text-slate-800 text-sm font-medium px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
<i className="icon-[solar--sticker-smile-circle-bold-duotone] w-5 h-5"></i> <span className="pr-1 pt-1">
</span> <i className="icon-[solar--sticker-smile-circle-bold-duotone] w-5 h-5"></i>
</span>
<span className="text-slate-500 dark:text-slate-900"> <span className="text-slate-500 dark:text-slate-900">
{json.genre[0]} {json.genre[0]}
</span>
</span> </span>
</span> </dd>
</dd> )}
</span> </span>
<dd className="my-1"> <dd className="my-1">
@@ -52,12 +54,14 @@ export const ComicInfoXML = (data): ReactElement => {
</span> </span>
)} )}
</dd> </dd>
<dd> {!isUndefined(json.notes) && (
{/* Notes */} <dd>
<span className="text-sm text-slate-500 dark:text-slate-900"> {/* Notes */}
{json.notes[0]} <span className="text-sm text-slate-500 dark:text-slate-900">
</span> {json.notes[0]}
</dd> </span>
</dd>
)}
</dl> </dl>
</div> </div>
); );

View File

@@ -10,6 +10,7 @@ import {
} from "../../shared/utils/metadata.utils"; } from "../../shared/utils/metadata.utils";
import { LIBRARY_SERVICE_HOST } from "../../constants/endpoints"; import { LIBRARY_SERVICE_HOST } from "../../constants/endpoints";
import Header from "../shared/Header"; import Header from "../shared/Header";
import useEmblaCarousel from "embla-carousel-react";
type RecentlyImportedProps = { type RecentlyImportedProps = {
comics: any; comics: any;
@@ -19,6 +20,15 @@ export const RecentlyImported = (
comics: RecentlyImportedProps, comics: RecentlyImportedProps,
): ReactElement => { ): ReactElement => {
console.log(comics); console.log(comics);
// embla carousel
const [emblaRef, emblaApi] = useEmblaCarousel({
loop: false,
align: "start",
containScroll: "trimSnaps",
slidesToScroll: 1,
});
return ( return (
<div> <div>
<Header <Header
@@ -26,8 +36,10 @@ export const RecentlyImported = (
subHeaderContent="Recent Library activity such as imports, tagging, etc." subHeaderContent="Recent Library activity such as imports, tagging, etc."
iconClassNames="fa-solid fa-binoculars mr-2" iconClassNames="fa-solid fa-binoculars mr-2"
/> />
<div className="grid grid-cols-5 gap-6 mt-3"> <div className="overflow-hidden -mr-4 sm:-mr-8 lg:-mr-16 xl:-mr-20 2xl:-mr-24 mt-3">
{comics?.comics.map( <div className="overflow-hidden" ref={emblaRef}>
<div className="flex">
{comics?.comics.map(
( (
{ {
_id, _id,
@@ -56,13 +68,16 @@ export const RecentlyImported = (
!isUndefined(comicvine) && !isUndefined(comicvine) &&
!isUndefined(comicvine.volumeInformation); !isUndefined(comicvine.volumeInformation);
return ( return (
<Card <div
orientation="vertical-2"
key={idx} key={idx}
imageUrl={url} className="flex-[0_0_200px] min-w-0 sm:flex-[0_0_220px] md:flex-[0_0_240px] lg:flex-[0_0_260px] xl:flex-[0_0_280px] pr-[15px]"
title={inferredMetadata.issue.name}
hasDetails
> >
<Card
orientation="vertical-2"
imageUrl={url}
title={inferredMetadata.issue.name}
hasDetails
>
<div> <div>
<dd className="text-sm my-1 flex flex-row gap-1"> <dd className="text-sm my-1 flex flex-row gap-1">
{/* Issue number */} {/* Issue number */}
@@ -99,16 +114,17 @@ export const RecentlyImported = (
<div className="sm:inline-flex sm:shrink-0 sm:items-center sm:gap-2"> <div className="sm:inline-flex sm:shrink-0 sm:items-center sm:gap-2">
{/* ComicInfo.xml presence */} {/* ComicInfo.xml presence */}
{!isNil(comicInfo) && !isEmpty(comicInfo) && ( {!isNil(comicInfo) && !isEmpty(comicInfo) && (
<div mt-1> <div className="mt-1">
<i className="h-7 w-7 icon-[solar--code-file-bold-duotone] text-yellow-500 dark:text-yellow-300"></i> <i className="h-7 w-7 icon-[solar--code-file-bold-duotone] text-yellow-500 dark:text-yellow-300"></i>
</div> </div>
)} )}
{/* ComicVine metadata presence */} {/* ComicVine metadata presence */}
{isComicVineMetadataAvailable && ( {isComicVineMetadataAvailable && (
<span className="w-7 h-7"> <span className="inline-block w-6 h-6 md:w-7 md:h-7 flex-shrink-0">
<img <img
src="/src/client/assets/img/cvlogo.svg" src="/src/client/assets/img/cvlogo.svg"
alt={"ComicVine metadata detected."} alt={"ComicVine metadata detected."}
className="w-full h-full object-contain"
/> />
</span> </span>
)} )}
@@ -121,9 +137,12 @@ export const RecentlyImported = (
)} )}
</div> </div>
</Card> </Card>
</div>
); );
}, },
)} )}
</div>
</div>
</div> </div>
</div> </div>
); );

View File

@@ -102,7 +102,7 @@ export const WantedComicsList = ({
<img <img
src="/src/client/assets/img/cvlogo.svg" src="/src/client/assets/img/cvlogo.svg"
alt={"ComicVine metadata detected."} alt={"ComicVine metadata detected."}
className="w-7 h-7" className="inline-block w-6 h-6 md:w-7 md:h-7 flex-shrink-0 object-contain"
/> />
)} )}
{!isEmpty(locg) && ( {!isEmpty(locg) && (

View File

@@ -72,8 +72,11 @@ export const LibraryGrid = (libraryGridProps: ILibraryGridProps) => {
> >
<div className="content is-flex is-flex-direction-row"> <div className="content is-flex is-flex-direction-row">
{!isEmpty(sourcedMetadata.comicvine) && ( {!isEmpty(sourcedMetadata.comicvine) && (
<span className="icon cv-icon is-small"> <span className="icon cv-icon is-small inline-block w-6 h-6 md:w-7 md:h-7 flex-shrink-0">
<img src="/src/client/assets/img/cvlogo.svg" /> <img
src="/src/client/assets/img/cvlogo.svg"
className="w-full h-full object-contain"
/>
</span> </span>
)} )}
{isNil(rawFileDetails) && ( {isNil(rawFileDetails) && (