mirror of
https://github.com/r2r90/canvas-label.git
synced 2026-02-03 12:37:26 +00:00
styling test version
This commit is contained in:
@@ -3,7 +3,9 @@ import React from "react";
|
|||||||
import { Sidebar } from "@/components/layout/sidebar/sidebar";
|
import { Sidebar } from "@/components/layout/sidebar/sidebar";
|
||||||
import Header from "@/components/layout/header/Header";
|
import Header from "@/components/layout/header/Header";
|
||||||
import { Separator } from "@/components/ui/separator";
|
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 = {
|
type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -11,14 +13,17 @@ type Props = {
|
|||||||
|
|
||||||
export const Layout = ({ children }: Props) => {
|
export const Layout = ({ children }: Props) => {
|
||||||
return (
|
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 />
|
<Header />
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
<div className="container py-6 ">
|
<div
|
||||||
<div className="grid h-full items-stretch gap-6 md:min-h-[600px] md:grid-cols-[1fr_350px] lg:min-h-[700px] ">
|
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="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 />
|
<Sidebar />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -101,40 +101,7 @@ export const ImageGallery = () => {
|
|||||||
<LuLayoutTemplate />
|
<LuLayoutTemplate />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent side="right" className="mt-4">
|
<PopoverContent side="right" className="mt-4"></PopoverContent>
|
||||||
<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>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
146
src/components/layout/sidebar/image-gallery/image-gallery.tsx
Normal file
146
src/components/layout/sidebar/image-gallery/image-gallery.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
||||||
36
src/components/layout/sidebar/image-input/image-input.tsx
Normal file
36
src/components/layout/sidebar/image-input/image-input.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
48
src/components/layout/sidebar/layers/layers.tsx
Normal file
48
src/components/layout/sidebar/layers/layers.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,21 +1,64 @@
|
|||||||
import { TextInput } from "@/components/layout/sidebar/text-input";
|
import TextInput from "@/components/layout/sidebar/text-input/text-input";
|
||||||
import ImageInput from "@/components/layout/sidebar/image-input";
|
import ImageInput from "@/components/layout/sidebar/image-input/image-input";
|
||||||
import SizeSelect from "@/components/layout/sidebar/size-select";
|
import { Layers } from "@/components/layout/sidebar/layers/layers";
|
||||||
import { BackgroundSelect } from "@/components/layout/sidebar/background-select";
|
import { ImageGallery } from "@/components/layout/sidebar/image-gallery/image-gallery";
|
||||||
import { Layers } from "@/components/layout/sidebar/layers";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { SelectTemplate } from "@/components/layout/sidebar/select-template";
|
import React, { useState } from "react";
|
||||||
import { ImageGallery } from "@/components/layout/sidebar/image-gallery";
|
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() {
|
export function Sidebar() {
|
||||||
|
const [openGallery, setOpenGallery] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-20 flex-col items-center gap-2 p-2 pt-4">
|
<div className="items-left flex w-full flex-col p-2 pt-4">
|
||||||
<SizeSelect />
|
<Tabs>
|
||||||
<TextInput />
|
<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 />
|
<ImageInput />
|
||||||
<SelectTemplate />
|
</TabsContent>
|
||||||
<BackgroundSelect />
|
<TabsContent value="text">
|
||||||
<ImageGallery />
|
<TextInput />
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="gallery">
|
||||||
|
<ImageGallery open={openGallery} />
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="layers">
|
||||||
<Layers />
|
<Layers />
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
42
src/components/layout/sidebar/stage-settings/stage-size.tsx
Normal file
42
src/components/layout/sidebar/stage-settings/stage-size.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -8,7 +8,13 @@ import { Textarea } from "@/components/ui/textarea";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { addText } from "@/store/app.slice";
|
import { addText } from "@/store/app.slice";
|
||||||
import { useAppDispatch } from "@/hooks";
|
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";
|
import { RxText } from "react-icons/rx";
|
||||||
|
|
||||||
export function TextInput() {
|
export function TextInput() {
|
||||||
@@ -51,6 +57,9 @@ export function TextInput() {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleTextAdd}>Submit</Button>
|
<Button onClick={handleTextAdd}>Submit</Button>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
|
<CardDescription>
|
||||||
|
Téléchargez votre image et ajoutez-la à votre étiquette.
|
||||||
|
</CardDescription>
|
||||||
</Card>
|
</Card>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|||||||
45
src/components/layout/sidebar/text-input/text-input.tsx
Normal file
45
src/components/layout/sidebar/text-input/text-input.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as React from "react"
|
import * as React from "react";
|
||||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
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<
|
const TabsList = React.forwardRef<
|
||||||
React.ElementRef<typeof TabsPrimitive.List>,
|
React.ElementRef<typeof TabsPrimitive.List>,
|
||||||
@@ -12,13 +12,13 @@ const TabsList = React.forwardRef<
|
|||||||
<TabsPrimitive.List
|
<TabsPrimitive.List
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
"inline-flex h-10 w-[20rem] items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
||||||
className
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TabsList.displayName = TabsPrimitive.List.displayName
|
TabsList.displayName = TabsPrimitive.List.displayName;
|
||||||
|
|
||||||
const TabsTrigger = React.forwardRef<
|
const TabsTrigger = React.forwardRef<
|
||||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||||
@@ -28,12 +28,12 @@ const TabsTrigger = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
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",
|
"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}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
||||||
|
|
||||||
const TabsContent = React.forwardRef<
|
const TabsContent = React.forwardRef<
|
||||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||||
@@ -43,11 +43,11 @@ const TabsContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
"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}
|
{...props}
|
||||||
/>
|
/>
|
||||||
))
|
));
|
||||||
TabsContent.displayName = TabsPrimitive.Content.displayName
|
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
||||||
|
|
||||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
||||||
|
|||||||
Reference in New Issue
Block a user