fix: Presentation page responsive issue
This commit is contained in:
parent
f112934662
commit
76841ae4ce
12 changed files with 77 additions and 62 deletions
|
|
@ -1,5 +1,5 @@
|
|||
'use client'
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { DashboardApi } from "@/app/(presentation-generator)/services/api/dashboard";
|
||||
|
|
@ -12,7 +12,7 @@ import {
|
|||
import { useRouter } from "next/navigation";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoader";
|
||||
import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoad";
|
||||
import SlideScale from "@/app/(presentation-generator)/components/PresentationRender";
|
||||
import MarkdownRenderer from "@/components/MarkDownRender";
|
||||
|
||||
|
|
@ -28,11 +28,52 @@ export const PresentationCard = ({
|
|||
onDeleted?: (presentationId: string) => void;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
useFontLoader(presentation.fonts || []);
|
||||
|
||||
const handlePreview = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
router.push(`/presentation?id=${id}&type=standard`);
|
||||
};
|
||||
useEffect(() => {
|
||||
applyTheme(presentation.theme)
|
||||
}, [])
|
||||
const applyTheme = async (theme: any) => {
|
||||
const element = document.getElementById(`dashboard-presentation-card-${id}`)
|
||||
if (!element) return;
|
||||
|
||||
if (!theme || !theme.data || !theme.data.colors['graph_0']) { return; }
|
||||
const cssVariables = {
|
||||
'--primary-color': theme.data.colors['primary'],
|
||||
'--background-color': theme.data.colors['background'],
|
||||
'--card-color': theme.data.colors['card'],
|
||||
'--stroke': theme.data.colors['stroke'],
|
||||
'--primary-text': theme.data.colors['primary_text'],
|
||||
'--background-text': theme.data.colors['background_text'],
|
||||
'--graph-0': theme.data.colors['graph_0'],
|
||||
'--graph-1': theme.data.colors['graph_1'],
|
||||
'--graph-2': theme.data.colors['graph_2'],
|
||||
'--graph-3': theme.data.colors['graph_3'],
|
||||
'--graph-4': theme.data.colors['graph_4'],
|
||||
'--graph-5': theme.data.colors['graph_5'],
|
||||
'--graph-6': theme.data.colors['graph_6'],
|
||||
'--graph-7': theme.data.colors['graph_7'],
|
||||
'--graph-8': theme.data.colors['graph_8'],
|
||||
'--graph-9': theme.data.colors['graph_9'],
|
||||
}
|
||||
Object.entries(cssVariables).forEach(([key, value]) => {
|
||||
element.style.setProperty(key, value)
|
||||
})
|
||||
//
|
||||
if (theme.data.fonts.textFont.url && theme.data.fonts.textFont.name) {
|
||||
useFontLoader({ [theme.data.fonts.textFont.name]: theme.data.fonts.textFont.url })
|
||||
}
|
||||
|
||||
// Apply fonts to preview container
|
||||
element.style.setProperty('font-family', `"${theme.data.fonts.textFont.name}"`)
|
||||
element.style.setProperty('--heading-font-family', `"${theme.data.fonts.textFont.name}"`)
|
||||
element.style.setProperty('--body-font-family', `"${theme.data.fonts.textFont.name}"`)
|
||||
|
||||
|
||||
}
|
||||
|
||||
const handleDelete = async (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -59,7 +100,9 @@ export const PresentationCard = ({
|
|||
onClick={handlePreview}
|
||||
className="bg-[#F8FBFB] font-syne shadow-none sm:shadow-none presentation-card rounded-[12px] p-0 group hover:shadow-md transition-all duration-500 slide-theme cursor-pointer overflow-hidden flex flex-col"
|
||||
>
|
||||
<div suppressHydrationWarning={true} className="flex flex-col flex-1 relative z-40">
|
||||
<div
|
||||
id={`dashboard-presentation-card-${id}`}
|
||||
suppressHydrationWarning={true} className="flex flex-col flex-1 relative z-40">
|
||||
{/* <p className=" text-xs font-syne absolute top-2 flex gap-1 capitalize items-center left-2 rounded-[100px] px-2.5 py-1 bg-[#3A3A3AF5] text-white font-semibold z-40 ">
|
||||
|
||||
{presentation.type}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ interface EditableLayoutWrapperProps {
|
|||
slideData: any;
|
||||
isEditMode?: boolean;
|
||||
properties?: any;
|
||||
|
||||
|
||||
}
|
||||
|
||||
interface EditableElement {
|
||||
|
|
@ -29,7 +29,7 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
|
|||
slideIndex,
|
||||
slideData,
|
||||
properties,
|
||||
|
||||
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
|
@ -245,7 +245,7 @@ const EditableLayoutWrapper: React.FC<EditableLayoutWrapperProps> = ({
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Process SVG icons
|
||||
svgElements.forEach((svg, index) => {
|
||||
const svgEl = svg as SVGElement;
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ const ImageEditor = ({
|
|||
(properties &&
|
||||
properties[imageIdx] &&
|
||||
properties[imageIdx].initialObjectFit) ||
|
||||
"cover"
|
||||
"cover"
|
||||
);
|
||||
|
||||
// Refs
|
||||
|
|
@ -104,7 +104,7 @@ const ImageEditor = ({
|
|||
console.error("error in getting previous generated images", error);
|
||||
setError(
|
||||
error.message ||
|
||||
"Failed to get previous generated images. Please try again."
|
||||
"Failed to get previous generated images. Please try again."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
@ -233,7 +233,7 @@ const ImageEditor = ({
|
|||
trackEvent(MixpanelEvent.ImageEditor_UploadImage_API_Call);
|
||||
const result = await ImagesApi.uploadImage(file);
|
||||
setUploadedImageUrl(result.file_url || result.path);
|
||||
} catch (err:any) {
|
||||
} catch (err: any) {
|
||||
setUploadError("Failed to upload image. Please try again.");
|
||||
toast.error(err.message || "Failed to upload image. Please try again.");
|
||||
console.log("Upload error:", err.message);
|
||||
|
|
@ -247,7 +247,7 @@ const ImageEditor = ({
|
|||
setUploadedImagesLoading(true);
|
||||
const result = await ImagesApi.getUploadedImages();
|
||||
setUploadedImages(result);
|
||||
} catch (err:any) {
|
||||
} catch (err: any) {
|
||||
toast.error(err.message || "Failed to get uploaded images. Please try again.");
|
||||
console.log("Get uploaded images error:", err.message);
|
||||
} finally {
|
||||
|
|
@ -265,7 +265,7 @@ const ImageEditor = ({
|
|||
const result = await ImagesApi.deleteImage(image_id);
|
||||
setUploadedImages(uploadedImages.filter((image) => image.id !== image_id));
|
||||
toast.success(result.message || "Image deleted successfully");
|
||||
} catch (err:any) {
|
||||
} catch (err: any) {
|
||||
toast.error(err.message || "Failed to delete image. Please try again.");
|
||||
}
|
||||
};
|
||||
|
|
@ -480,12 +480,12 @@ const ImageEditor = ({
|
|||
}
|
||||
className="cursor-pointer group aspect-[4/3] rounded-lg overflow-hidden relative border border-gray-200"
|
||||
>
|
||||
<Trash className="absolute group-hover:opacity-100 opacity-0 transition-opacity z-10 w-4 h-4 top-2 right-2 text-red-500" onClick={(e) =>{
|
||||
<Trash className="absolute group-hover:opacity-100 opacity-0 transition-opacity z-10 w-4 h-4 top-2 right-2 text-red-500" onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDeleteImage(image.id)
|
||||
}}/>
|
||||
}} />
|
||||
<img
|
||||
src={image.file_url || image.path}
|
||||
src={image.file_url || image.path}
|
||||
alt="Uploaded preview"
|
||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform"
|
||||
/>
|
||||
|
|
@ -496,7 +496,7 @@ const ImageEditor = ({
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
|
|
@ -578,7 +578,7 @@ const ImageEditor = ({
|
|||
variant="outline"
|
||||
className={cn(
|
||||
objectFit === "cover" &&
|
||||
"bg-blue-50 border-blue-500"
|
||||
"bg-blue-50 border-blue-500"
|
||||
)}
|
||||
onClick={() => handleFitChange("cover")}
|
||||
>
|
||||
|
|
@ -588,7 +588,7 @@ const ImageEditor = ({
|
|||
variant="outline"
|
||||
className={cn(
|
||||
objectFit === "contain" &&
|
||||
"bg-blue-50 border-blue-500"
|
||||
"bg-blue-50 border-blue-500"
|
||||
)}
|
||||
onClick={() => handleFitChange("contain")}
|
||||
>
|
||||
|
|
@ -607,7 +607,7 @@ const ImageEditor = ({
|
|||
</div>
|
||||
}
|
||||
{/* Focus Point */}
|
||||
{}
|
||||
{ }
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { V1ContentRender } from '../../(presentation-generator)/components/V1Con
|
|||
const BASE_WIDTH = 1280;
|
||||
const BASE_HEIGHT = 720;
|
||||
|
||||
const SlideScale = ({ slide }: { slide: any }) => {
|
||||
const SlideScale = ({ slide, theme }: { slide: any, theme?: any }) => {
|
||||
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [containerWidth, setContainerWidth] = useState<number>(0);
|
||||
|
|
@ -62,12 +62,12 @@ const SlideScale = ({ slide }: { slide: any }) => {
|
|||
} as React.CSSProperties}
|
||||
>
|
||||
|
||||
<div
|
||||
{/* <div
|
||||
className="absolute inset-0 bg-transparent z-30 w-full h-full select-none"
|
||||
aria-hidden="true"
|
||||
|
||||
/>
|
||||
<V1ContentRender slide={slide} isEditMode={true} />
|
||||
/> */}
|
||||
<V1ContentRender slide={slide} isEditMode={true} theme={theme} />
|
||||
</div>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
|
||||
export const useFontLoader = ( fonts:string[]) => {
|
||||
const injectFonts = (fontUrls: string[]) => {
|
||||
fontUrls.forEach((fontUrl) => {
|
||||
if (!fontUrl) return;
|
||||
let newFontUrl = fontUrl.includes('fonts.googleapis') ? fontUrl : `https://localhost:5000${fontUrl}`;
|
||||
const existingStyle = document.querySelector(`style[data-font-url="${newFontUrl}"]`);
|
||||
if (existingStyle) return;
|
||||
const style = document.createElement("style");
|
||||
style.setAttribute("data-font-url", newFontUrl);
|
||||
style.textContent = `@import url('${newFontUrl}');`;
|
||||
document.head.appendChild(style);
|
||||
});
|
||||
};
|
||||
injectFonts(fonts);
|
||||
};
|
||||
|
|
@ -57,18 +57,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
|
|||
</span>
|
||||
</div>
|
||||
)}
|
||||
{/* <div className="flex items-center justify-between">
|
||||
<h5 className="text-lg font-medium">
|
||||
Presentation Outline
|
||||
</h5>
|
||||
{isStreaming && (
|
||||
<div className="flex items-center text-sm text-blue-600">
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-600 mr-2"></div>
|
||||
Generating outlines...
|
||||
</div>
|
||||
)}
|
||||
</div> */}
|
||||
{/* Skeleton loading state */}
|
||||
|
||||
{isLoading && (
|
||||
<div className="space-y-4 bg-white">
|
||||
{[...Array(6)].map((_, index) => (
|
||||
|
|
@ -93,7 +82,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
|
|||
{/* Outlines content */}
|
||||
|
||||
{outlines && outlines.length > 0 && (
|
||||
<div className="bg-[#F9F8F8] p-7 relative z-20 rounded-[20px]">
|
||||
<div className="bg-[#F9F8F8] p-7 relative z-20 rounded-[20px] min-h-[calc(100vh-200px)]">
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ const PresentationHeader = ({
|
|||
return (
|
||||
<>
|
||||
<div className="py-7 sticky top-0 bg-white z-50 mb-[17px] font-syne flex justify-between items-center">
|
||||
<h2 className="text-lg text-[#101323] font-unbounded "><MarkdownRenderer content={presentationData?.title || "Presentation"} className="mb-0 w-[600px] truncate text-sm text-[#101323] " /></h2>
|
||||
<h2 className="text-lg text-[#101323] font-unbounded "><MarkdownRenderer content={presentationData?.title || "Presentation"} className="mb-0 max-w-[600px] overflow-ellipsis line-clamp-1 text-sm text-[#101323] " /></h2>
|
||||
<div className="flex items-center gap-2.5">
|
||||
|
||||
{isPresentationSaving && <div className="flex items-center gap-2">
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ const SidePanel = ({
|
|||
<Separator orientation="horizontal" className="my-6 " />
|
||||
<div
|
||||
className={`
|
||||
fixed xl:relative h-full z-50 xl:z-auto
|
||||
relative bg-[#F6F6F9] h-full z-50 xl:z-auto
|
||||
transition-all duration-300 ease-in-out
|
||||
`}
|
||||
>
|
||||
|
|
@ -141,14 +141,14 @@ const SidePanel = ({
|
|||
className="w-full h-[calc(100vh-120px)] hide-scrollbar overflow-hidden slide-theme "
|
||||
>
|
||||
|
||||
<p className="text-xl font-normal pb-3.5 text-[#000000]">Slides</p>
|
||||
<p className="text-xl font-normal pb-3.5 text-[#000000]">Slides ({presentationData?.slides?.length})</p>
|
||||
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<div className=" overflow-y-auto hide-scrollbar h-[calc(100%-140px)] space-y-3.5">
|
||||
<div className=" overflow-y-auto w-full hide-scrollbar h-[calc(100%-140px)] space-y-3.5">
|
||||
{isStreaming ? (
|
||||
presentationData &&
|
||||
presentationData?.slides.map((slide: any, index: number) => (
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { trackEvent, MixpanelEvent } from "@/utils/mixpanel";
|
|||
import { addToHistory } from "@/store/slices/undoRedoSlice";
|
||||
import { V1ContentRender } from "../../components/V1ContentRender";
|
||||
import NewSlide from "./NewSlide";
|
||||
import SlideScale from "../../components/PresentationRender";
|
||||
|
||||
interface SlideContentProps {
|
||||
slide: any;
|
||||
|
|
@ -145,7 +146,8 @@ const SlideContent = ({ slide, index, presentationId }: SlideContentProps) => {
|
|||
data-group={slide.layout_group}
|
||||
className={` w-full group font-syne `}
|
||||
>
|
||||
<V1ContentRender slide={slide} isEditMode={true} theme={null} />
|
||||
{/* <V1ContentRender slide={slide} isEditMode={true} theme={null} /> */}
|
||||
<SlideScale slide={slide} theme={presentationData?.theme || null} />
|
||||
{!showNewSlideSelection && (
|
||||
<div className="group-hover:opacity-100 hidden md:block opacity-0 transition-opacity my-4 duration-300">
|
||||
<ToolTip content="Add new slide below">
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ interface SortableSlideProps {
|
|||
const SCALE = 0.125;
|
||||
|
||||
export function SortableSlide({ slide, index, selectedSlide, onSlideClick }: SortableSlideProps) {
|
||||
const searchParams = useSearchParams();
|
||||
const type = searchParams.get("type") as 'standard' | 'smart';
|
||||
const lastClickTime = useRef(0);
|
||||
const {
|
||||
attributes,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
import { useRouter } from "next/navigation";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoader";
|
||||
import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoad";
|
||||
import SlideScale from "@/app/(presentation-generator)/components/PresentationRender";
|
||||
import MarkdownRenderer from "@/components/MarkDownRender";
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ export const PresentationCard = ({
|
|||
onDeleted?: (presentationId: string) => void;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
useFontLoader(presentation.fonts || []);
|
||||
|
||||
const handlePreview = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
router.push(`/presentation?id=${id}&type=standard`);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import { Card } from "@/components/ui/card";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { ArrowLeft, Home, Loader2, Trash2 } from "lucide-react";
|
||||
|
||||
import { useFontLoader } from "../../hooks/useFontLoader";
|
||||
import { MixpanelEvent, trackEvent } from "@/utils/mixpanel";
|
||||
import TemplateService from "../../services/api/template";
|
||||
import Header from "../../(dashboard)/dashboard/components/Header";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue