styling test version

This commit is contained in:
Artur AGH
2023-12-19 09:36:29 +01:00
parent 10326e0d5c
commit 977b750476
12 changed files with 439 additions and 137 deletions

View File

@@ -3,7 +3,9 @@ import React from "react";
import { Sidebar } from "@/components/layout/sidebar/sidebar";
import Header from "@/components/layout/header/Header";
import { Separator } from "@/components/ui/separator";
import { Tabs } from "@/components/ui/tabs";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import { PlusCircledIcon } from "@radix-ui/react-icons";
type Props = {
children: ReactNode;
@@ -11,14 +13,17 @@ type Props = {
export const Layout = ({ children }: Props) => {
return (
<div className="h-full overflow-hidden rounded-[0.5rem] border bg-background shadow">
<div className="h-full overflow-hidden rounded-[0.5rem] border bg-background shadow ">
<Header />
<Separator />
<div className="container py-6 ">
<div className="grid h-full items-stretch gap-6 md:min-h-[600px] md:grid-cols-[1fr_350px] lg:min-h-[700px] ">
<div
className="container py-6
"
>
<div className="grid h-full items-stretch gap-6 md:grid-cols-[1fr_350px] lg:min-h-[680px] ">
<div className="rounded-md border md:order-1">{children}</div>
<div className="hidden flex-col space-y-4 sm:flex md:order-2">
<div className="hidden flex-col space-y-4 rounded-md border sm:flex md:order-2 lg:min-h-[680px]">
<Sidebar />
</div>
</div>

View File

@@ -101,40 +101,7 @@ export const ImageGallery = () => {
<LuLayoutTemplate />
</Button>
</PopoverTrigger>
<PopoverContent side="right" className="mt-4">
<Card className="p-3">
<CardHeader className="mb-2 p-2 text-center">
<CardTitle>Template</CardTitle>
</CardHeader>
<Input
type="text"
placeholder="Search Images"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<div
className=" grid h-64 w-full grid-cols-2 gap-4 overflow-auto"
ref={scrollAreaRef}
>
{photos?.map((p, i) => (
<div
key={i}
className="rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
>
<Image
key={p.i}
src={p.urls.small}
alt={"image"}
height={100}
width={100}
/>
</div>
))}
</div>
</Card>
</PopoverContent>
<PopoverContent side="right" className="mt-4"></PopoverContent>
</Popover>
);
};

View File

@@ -0,0 +1,146 @@
import React, {
ChangeEvent,
type ElementRef,
useEffect,
useRef,
useState,
} from "react";
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
import axios from "axios";
import * as process from "process";
import Image from "next/image";
import { Input } from "@/components/ui/input";
import { addImage } from "@/store/app.slice";
type Props = {
open: boolean;
};
export const ImageGallery = ({ open }: Props) => {
const [query, setQuery] = useState<string>("");
const [page, setPage] = useState<number>(1);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [photos, setPhotos] = useState<any[]>([]);
const scrollAreaRef = useRef<ElementRef<"div">>(null);
const mainUrl = "https://api.unsplash.com/photos";
const searchUrl = "https://api.unsplash.com/search/photos";
const clientID = `?client_id=${process.env.NEXT_PUBLIC_ACCESS_KEY}`;
useEffect(() => {
fetchImages();
}, [query, page]);
const fetchImages = async () => {
setIsLoading(true);
let url;
const urlPage = `&page=${page}`;
const urlQuery = `&query=${query}`;
if (query) {
url = `${searchUrl}${clientID}${urlPage}${urlQuery}`;
console.log(url);
} else {
url = `${mainUrl}${clientID}${urlPage}`;
}
try {
const { data } = await axios.get(url);
setPhotos((oldPhotos) => {
if (query && page === 1) {
return data.results;
} else if (query) {
return [...oldPhotos, ...data.results];
} else {
console.log(data);
return [...oldPhotos, ...data];
}
});
setIsLoading(false);
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
useEffect(() => {
const ref = scrollAreaRef.current;
const handler = () => {
if (scrollAreaRef.current) {
console.log(
scrollAreaRef.current.clientHeight + scrollAreaRef.current.scrollTop,
scrollAreaRef.current.scrollHeight - 2,
);
}
if (
isLoading ||
!scrollAreaRef.current ||
scrollAreaRef.current.clientHeight + scrollAreaRef.current.scrollTop <
scrollAreaRef.current.scrollHeight - 2
) {
return;
}
setPage((oldPage) => {
return oldPage + 1;
});
};
const handleImageUploaded = () => {
if (!file) return;
const img = document.createElement("img");
const imageUrl = URL.createObjectURL(file);
img.onload = () => {
dispatch(addImage({ imageUrl, width: img.width, height: img.height }));
img.remove();
};
img.src = imageUrl;
img.style.cssText = "display: none;";
document.body.appendChild(img);
};
scrollAreaRef.current?.addEventListener("scroll", handler);
return () => ref?.removeEventListener("scroll", handler);
}, [isLoading, open]);
const handleImageAdd = (e) => {
console.log(e.target);
};
return (
<Card className="min-h-full p-3">
<CardHeader className="mb-2 p-2 text-center">
<CardTitle>Template</CardTitle>
</CardHeader>
<Input
type="text"
placeholder="Search Images"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<div
className="mt-3 max-h-64 w-full grid-cols-2 justify-center overflow-auto"
ref={scrollAreaRef}
>
{photos?.map((p, i) => (
<div
key={i}
onClick={(e) => handleImageAdd(e)}
className="rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
>
<Image
key={p.i}
src={p.urls.small}
alt={"image"}
height={100}
width={100}
/>
</div>
))}
</div>
</Card>
);
};

View File

@@ -0,0 +1,36 @@
import React, { type ChangeEvent } from "react";
import { addImage } from "@/store/app.slice";
import { useAppDispatch } from "@/hooks";
import {
Card,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
export default function ImageInput() {
const dispatch = useAppDispatch();
const handleImageUploaded = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const img = document.createElement("img");
const imageUrl = URL.createObjectURL(file);
img.onload = () => {
dispatch(addImage({ imageUrl, width: img.width, height: img.height }));
img.remove();
};
img.src = imageUrl;
img.style.cssText = "display: none;";
document.body.appendChild(img);
};
return (
<Card className="p-2">
<Input type="file" onChange={handleImageUploaded} />
<CardDescription>
Téléchargez votre image et ajoutez-la à votre étiquette.
</CardDescription>
</Card>
);
}

View File

@@ -1,67 +0,0 @@
import React from "react";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { RxStack } from "react-icons/rx";
import { useAppDispatch, useAppSelector } from "@/hooks";
import { selectItem, setStageItems } from "@/store/app.slice";
import { closestCenter, DndContext, type DragEndEvent } from "@dnd-kit/core";
import {
arrayMove,
SortableContext,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import LayerItem from "@/components/layer-item";
export const Layers = () => {
const dispatch = useAppDispatch();
const currentStep = useAppSelector((state) => state.app.currentStep);
const items = useAppSelector(
(state) => state.app.history[currentStep]?.items,
);
if (!items) return;
const handleDragEnd = (e: DragEndEvent) => {
const { active, over } = e;
const oldIndex = items.findIndex((item) => item.id === active.id);
const newIndex = items.findIndex((item) => item.id === over?.id);
const result = arrayMove(items, oldIndex, newIndex);
dispatch(setStageItems(result));
dispatch(selectItem(active.id));
};
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="text-xl">
<RxStack />
</Button>
</PopoverTrigger>
<PopoverContent side="right">
<Card className="p-2">
<CardHeader>
<CardTitle className="text-center">Layers</CardTitle>
</CardHeader>
<CardContent className="grid items-center gap-2 p-2 ">
<DndContext
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext
items={items}
strategy={verticalListSortingStrategy}
>
{items.map((item) => (
<LayerItem item={item} key={item.id} />
))}
</SortableContext>
</DndContext>
</CardContent>
</Card>
</PopoverContent>
</Popover>
);
};

View File

@@ -0,0 +1,48 @@
import React from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { useAppDispatch, useAppSelector } from "@/hooks";
import { selectItem, setStageItems } from "@/store/app.slice";
import { closestCenter, DndContext, type DragEndEvent } from "@dnd-kit/core";
import {
arrayMove,
SortableContext,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import LayerItem from "@/components/layer-item";
export const Layers = () => {
const dispatch = useAppDispatch();
const currentStep = useAppSelector((state) => state.app.currentStep);
const items = useAppSelector(
(state) => state.app.history[currentStep]?.items,
);
if (!items) return;
const handleDragEnd = (e: DragEndEvent) => {
const { active, over } = e;
const oldIndex = items.findIndex((item) => item.id === active.id);
const newIndex = items.findIndex((item) => item.id === over?.id);
const result = arrayMove(items, oldIndex, newIndex);
dispatch(setStageItems(result));
dispatch(selectItem(active.id));
};
return (
<Card className="p-2">
<CardHeader>
<CardTitle className="text-center">Layers</CardTitle>
</CardHeader>
<CardContent className="grid items-center gap-2 p-2 ">
<DndContext
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext items={items} strategy={verticalListSortingStrategy}>
{items.map((item) => (
<LayerItem item={item} key={item.id} />
))}
</SortableContext>
</DndContext>
</CardContent>
</Card>
);
};

View File

@@ -1,21 +1,64 @@
import { TextInput } from "@/components/layout/sidebar/text-input";
import ImageInput from "@/components/layout/sidebar/image-input";
import SizeSelect from "@/components/layout/sidebar/size-select";
import { BackgroundSelect } from "@/components/layout/sidebar/background-select";
import { Layers } from "@/components/layout/sidebar/layers";
import { SelectTemplate } from "@/components/layout/sidebar/select-template";
import { ImageGallery } from "@/components/layout/sidebar/image-gallery";
import TextInput from "@/components/layout/sidebar/text-input/text-input";
import ImageInput from "@/components/layout/sidebar/image-input/image-input";
import { Layers } from "@/components/layout/sidebar/layers/layers";
import { ImageGallery } from "@/components/layout/sidebar/image-gallery/image-gallery";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import React, { useState } from "react";
import { LuLayoutTemplate } from "react-icons/lu";
import {
GearIcon,
ImageIcon,
LayersIcon,
TextIcon,
} from "@radix-ui/react-icons";
import { StageSize } from "@/components/layout/sidebar/stage-settings/stage-size";
import { StageBackground } from "@/components/layout/sidebar/stage-settings/stage-background";
export function Sidebar() {
const [openGallery, setOpenGallery] = useState<boolean>(false);
return (
<div className="flex h-full w-20 flex-col items-center gap-2 p-2 pt-4">
<SizeSelect />
<TextInput />
<ImageInput />
<SelectTemplate />
<BackgroundSelect />
<ImageGallery />
<Layers />
<div className="items-left flex w-full flex-col p-2 pt-4">
<Tabs>
<TabsList className="grid grid-cols-5">
<TabsTrigger value="stage">
<GearIcon />
</TabsTrigger>
<TabsTrigger value="image">
<ImageIcon />
</TabsTrigger>
<TabsTrigger value="text">
<TextIcon />
</TabsTrigger>
<TabsTrigger
onClick={() => setOpenGallery(true)}
open={openGallery}
onOpenChange={setOpenGallery}
value="gallery"
>
<LuLayoutTemplate />
</TabsTrigger>
<TabsTrigger value="layers">
<LayersIcon />
</TabsTrigger>
</TabsList>
<TabsContent value="stage">
<StageSize />
<StageBackground />
</TabsContent>
<TabsContent value="image">
<ImageInput />
</TabsContent>
<TabsContent value="text">
<TextInput />
</TabsContent>
<TabsContent value="gallery">
<ImageGallery open={openGallery} />
</TabsContent>
<TabsContent value="layers">
<Layers />
</TabsContent>
</Tabs>
</div>
);
}

View File

@@ -0,0 +1,28 @@
import React, { useState } from "react";
import { Label } from "@/components/ui/label";
import { useAppDispatch, useAppSelector } from "@/hooks";
import { selectBackground } from "@/store/app.slice";
export const StageBackground = () => {
const dispatch = useAppDispatch();
const bgColor = useAppSelector((state) => state.app.backgroundColor);
const handleBackgroundSelect = (color: string) => {
dispatch(selectBackground(color));
};
return (
<div>
<Label
htmlFor="apple"
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
>
<input
className="m-2 "
type="color"
value={bgColor ?? undefined}
onChange={(e) => handleBackgroundSelect(e.target.value)}
/>
</Label>
</div>
);
};

View File

@@ -0,0 +1,42 @@
import React, { useState } from "react";
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { useAppDispatch } from "@/hooks";
import { updateStage } from "@/store/app.slice";
export const StageSize = () => {
const dispatch = useAppDispatch();
const handleStageSizeSelect = (
width: number | undefined,
height: number | undefined,
) => {
dispatch(updateStage({ width, height }));
};
return (
<Card className="p-3">
<CardHeader>
<CardTitle>{`Sélectionnez la taille d'étiquette`}</CardTitle>
</CardHeader>
<div className="flex flex-col gap-2">
<Button onClick={() => handleStageSizeSelect(269.32, 165.87)}>
Petit Format
</Button>
<Button onClick={() => handleStageSizeSelect(323.15, 227)}>
Moyen Format
</Button>
<Button onClick={() => handleStageSizeSelect(410, 289)}>
Grand Format
</Button>
</div>
{/*<CardFooter className="mt-8 justify-between">*/}
{/* <Button variant="ghost" onClick={() => setOpen(false)}>*/}
{/* Cancel*/}
{/* </Button>*/}
{/* <Button onClick={handleSizeSelect}>Submit</Button>*/}
{/*</CardFooter>*/}
</Card>
);
};

View File

@@ -8,7 +8,13 @@ import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";
import { addText } from "@/store/app.slice";
import { useAppDispatch } from "@/hooks";
import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { RxText } from "react-icons/rx";
export function TextInput() {
@@ -51,6 +57,9 @@ export function TextInput() {
</Button>
<Button onClick={handleTextAdd}>Submit</Button>
</CardFooter>
<CardDescription>
Téléchargez votre image et ajoutez-la à votre étiquette.
</CardDescription>
</Card>
</PopoverContent>
</Popover>

View File

@@ -0,0 +1,45 @@
import React, { type ChangeEvent, useState } from "react";
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";
import { addText } from "@/store/app.slice";
import { useAppDispatch } from "@/hooks";
export default function TextInput() {
const dispatch = useAppDispatch();
const [inputText, setInputText] = useState("");
const handleTextAdd = () => {
dispatch(addText({ initialValue: inputText }));
};
const handleInputChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
setInputText(e.target.value);
};
return (
<Card className="p-3">
<CardHeader className="pt-2">
<CardTitle className="text-center">Text</CardTitle>
</CardHeader>
<Textarea
id="description"
placeholder="Entrez votre text ..."
onChange={handleInputChange}
/>
<CardFooter className="mt-8 justify-between">
<Button variant="ghost" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button onClick={handleTextAdd}>Submit</Button>
</CardFooter>
<CardDescription>
Téléchargez votre image et ajoutez-la à votre étiquette.
</CardDescription>
</Card>
);
}

View File

@@ -1,9 +1,9 @@
import * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs"
import * as React from "react";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const Tabs = TabsPrimitive.Root
const Tabs = TabsPrimitive.Root;
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
@@ -12,13 +12,13 @@ const TabsList = React.forwardRef<
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className
"inline-flex h-10 w-[20rem] items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className,
)}
{...props}
/>
))
TabsList.displayName = TabsPrimitive.List.displayName
));
TabsList.displayName = TabsPrimitive.List.displayName;
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
@@ -28,12 +28,12 @@ const TabsTrigger = React.forwardRef<
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
className
className,
)}
{...props}
/>
))
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
));
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
@@ -43,11 +43,11 @@ const TabsContent = React.forwardRef<
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
className,
)}
{...props}
/>
))
TabsContent.displayName = TabsPrimitive.Content.displayName
));
TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsList, TabsTrigger, TabsContent }
export { Tabs, TabsList, TabsTrigger, TabsContent };