Skip to content

Commit

Permalink
Merge pull request #3 from fac30/outfit-maker
Browse files Browse the repository at this point in the history
Outfit maker
  • Loading branch information
JoshCodedit authored Dec 10, 2024
2 parents dc08b87 + 8f49155 commit 2c9fb6f
Show file tree
Hide file tree
Showing 3 changed files with 548 additions and 7 deletions.
141 changes: 141 additions & 0 deletions src/components/Canvas.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React, { useState } from "react";

interface CanvasProps {
items: { name: string; image: string; x: number; y: number }[];
onDeleteItem: (index: number) => void;
onUpdateItemPosition: (index: number, x: number, y: number) => void;
}

const Canvas: React.FC<CanvasProps> = ({ items, onDeleteItem, onUpdateItemPosition }) => {
const [draggingItemIndex, setDraggingItemIndex] = useState<number | null>(null);
const [offset, setOffset] = useState<{ x: number; y: number }>({ x: 0, y: 0 });

// Handle the start of the dragging
const handleDragStart = (index: number, e: React.MouseEvent | React.TouchEvent) => {
setDraggingItemIndex(index);
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;

const item = items[index];
setOffset({
x: clientX - item.x,
y: clientY - item.y,
});

// Prevent default behavior to avoid unwanted scrolling or text selection during drag
e.preventDefault();
};

// Handle the dragging movement
const handleDragMove = (e: MouseEvent | TouchEvent) => {
if (draggingItemIndex !== null) {
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;

const newX = clientX - offset.x;
const newY = clientY - offset.y;

// Update the item position while dragging
onUpdateItemPosition(draggingItemIndex, newX, newY);
}
};

// Handle the end of the drag
const handleDragEnd = () => {
setDraggingItemIndex(null); // Stop dragging
};

// Attach mouse and touch event listeners
React.useEffect(() => {
if (draggingItemIndex !== null) {
document.addEventListener("mousemove", handleDragMove);
document.addEventListener("mouseup", handleDragEnd);
document.addEventListener("touchmove", handleDragMove, { passive: false });
document.addEventListener("touchend", handleDragEnd);
} else {
document.removeEventListener("mousemove", handleDragMove);
document.removeEventListener("mouseup", handleDragEnd);
document.removeEventListener("touchmove", handleDragMove);
document.removeEventListener("touchend", handleDragEnd);
}

return () => {
document.removeEventListener("mousemove", handleDragMove);
document.removeEventListener("mouseup", handleDragEnd);
document.removeEventListener("touchmove", handleDragMove);
document.removeEventListener("touchend", handleDragEnd);
};
}, [draggingItemIndex, offset]);

// Handle item deletion on mouse or touch events
const handleDeleteItem = (index: number, e: React.MouseEvent | React.TouchEvent) => {
e.stopPropagation(); // Prevent the event from bubbling up to the parent component
onDeleteItem(index); // Call the delete function passed down as a prop
};

return (
<div
className="canvas-container"
style={{
width: "450px", // Fixed width
height: "350px", // Fixed height
border: "1px solid #ddd", // Optional for clarity
overflow: "hidden", // Prevent items from overflowing
position: "relative", // Allows absolute positioning of items
marginTop: "20px", // Some spacing for clarity
background: "url('/images/grid-lines.png')", // Add grid lines image to the background
backgroundSize: "20px 20px", // Adjust size of grid cells
}}
>
{items.map((item, index) => (
<div
key={index}
style={{
position: "absolute",
top: `${item.y}px`, // Item's vertical position
left: `${item.x}px`, // Item's horizontal position
cursor: "pointer",
}}
onMouseDown={(e) => handleDragStart(index, e)} // Start dragging with mouse
onTouchStart={(e) => handleDragStart(index, e)} // Start dragging with touch
>
<img
src={item.image}
alt={item.name}
style={{
width: "50px", // Fixed width for each item
height: "50px", // Fixed height for each item
objectFit: "cover", // Keep aspect ratio for images
}}
/>
<p style={{ textAlign: "center", fontSize: "12px" }}>{item.name}</p>

{/* Add delete button for each item */}
<button
onClick={(e) => handleDeleteItem(index, e)}
onTouchStart={(e) => handleDeleteItem(index, e)} // Handle touch events for delete
style={{
position: "absolute",
top: "-10px",
right: "-10px",
backgroundColor: "red",
color: "#fff",
border: "none",
borderRadius: "50%",
width: "20px",
height: "20px",
display: "flex",
justifyContent: "center",
alignItems: "center",
cursor: "pointer",
}}
>
X
</button>
</div>
))}
</div>
);
};

export default Canvas;
142 changes: 142 additions & 0 deletions src/components/CategoryMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React from "react";

interface CategoryMenuProps {
selectedCategory: string | null;
selectedSubCategory: string | null;
onCategorySelect: (category: string) => void;
onBackToCategories: () => void;
onItemSelect: (item: { name: string; image: string }) => void;
setSelectedSubCategory: (subCategory: string | null) => void;
}

const categories: Record<
"Tops" | "Bottoms" | "Shoes",
Record<string, { name: string; image: string }[]>
> = {
Tops: {
TShirts: [
{ name: "Red T-Shirt", image: "https://image.uniqlo.com/UQ/ST3/WesternCommon/imagesgoods/465751/item/goods_17_465751_3x4.jpg?width=400" },
{ name: "Blue T-Shirt", image: "https://image.uniqlo.com/UQ/ST3/WesternCommon/imagesgoods/465751/item/goods_61_465751_3x4.jpg?width=400" },
],
Hoodies: [
{ name: "Grey Hoodie", image: "https://image.uniqlo.com/UQ/ST3/WesternCommon/imagesgoods/475855/sub/goods_475855_sub14_3x4.jpg?width=400" },
{ name: "Black Hoodie", image: "https://image.uniqlo.com/UQ/ST3/WesternCommon/imagesgoods/456261/item/goods_09_456261_3x4.jpg?width=400" },
],
},
Bottoms: {
Jeans: [
{ name: "Blue Jeans", image: "https://image.uniqlo.com/UQ/ST3/WesternCommon/imagesgoods/464744/item/goods_64_464744_3x4.jpg?width=400" },
{ name: "Black Jeans", image: "https://image.uniqlo.com/UQ/ST3/WesternCommon/imagesgoods/471374/sub/goods_471374_sub14_3x4.jpg?width=400" },
],
Shorts: [
{ name: "Denim Shorts", image: "https://image.uniqlo.com/UQ/ST3/WesternCommon/imagesgoods/464945001/sub/goods_464945001_sub14_3x4.jpg?width=400" },
{ name: "Khaki Shorts", image: "https://image.uniqlo.com/UQ/ST3/WesternCommon/imagesgoods/467052/item/goods_33_467052_3x4.jpg?width=400" },
],
},
Shoes: {
Sneakers: [
{ name: "White Sneakers", image: "https://d2ob0iztsaxy5v.cloudfront.net/product/340740/3407401060_zm.jpg" },
{ name: "Black Sneakers", image: "https://d2ob0iztsaxy5v.cloudfront.net/product/190032/1900327270_zm.jpg" },
],
Boots: [
{ name: "Brown Boots", image: "https://d2ob0iztsaxy5v.cloudfront.net/product/149501/1495016020_zm.jpg" },
{ name: "Black Boots", image: "https://d2ob0iztsaxy5v.cloudfront.net/product/141179/1411797060_zm.jpg" },
],
},
};

const CategoryMenu: React.FC<CategoryMenuProps> = ({
selectedCategory,
selectedSubCategory,
onCategorySelect,
onBackToCategories,
onItemSelect,
setSelectedSubCategory,
}) => {
const renderMainCategories = () => (
<div>
<h2>Select a Category</h2>
<ul style={{ listStyle: "none", padding: 0 }}>
{Object.keys(categories).map((category) => (
<li key={category}>
<button
style={{ margin: "10px", padding: "10px", cursor: "pointer" }}
onClick={() => onCategorySelect(category)}
>
{category}
</button>
</li>
))}
</ul>
</div>
);

const renderSubCategories = () => (
<div>
<h2>{selectedCategory} Subcategories</h2>
<button
style={{ margin: "10px", padding: "10px", cursor: "pointer" }}
onClick={onBackToCategories}
>
Back to Categories
</button>
<ul style={{ listStyle: "none", padding: 0 }}>
{Object.keys(categories[selectedCategory as "Tops" | "Bottoms" | "Shoes"]).map(
(subCategory) => (
<li key={subCategory}>
<button
style={{ margin: "10px", padding: "10px", cursor: "pointer" }}
onClick={() => setSelectedSubCategory(subCategory)}
>
{subCategory}
</button>
</li>
)
)}
</ul>
</div>
);

const renderItems = () => (
<div>
<h2>{selectedSubCategory} Items</h2>
<button
style={{ margin: "10px", padding: "10px", cursor: "pointer" }}
onClick={() => setSelectedSubCategory(null)}
>
Back to {selectedCategory}
</button>
<ul style={{ listStyle: "none", padding: 0, display: "flex", flexWrap: "wrap" }}>
{categories[selectedCategory as "Tops" | "Bottoms" | "Shoes"][
selectedSubCategory!
].map((item) => (
<li key={item.name} style={{ margin: "10px" }}>
<img
src={item.image}
alt={item.name}
style={{ width: "100px", height: "100px", objectFit: "cover", cursor: "pointer" }}
onClick={() => onItemSelect(item)}
/>
<p style={{ textAlign: "center" }}>{item.name}</p>
</li>
))}
</ul>
</div>
);

return (
<div style={{ border: "1px solid #ddd", padding: "20px", borderRadius: "5px" }}>
{selectedCategory ? (
selectedSubCategory ? (
renderItems()
) : (
renderSubCategories()
)
) : (
renderMainCategories()
)}
</div>
);
};

export default CategoryMenu;
Loading

0 comments on commit 2c9fb6f

Please sign in to comment.