Skip to content

Commit

Permalink
Finished mockup editting mode for Media Card
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulHo0501 committed Oct 4, 2024
1 parent 4046460 commit 7b83d10
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 20 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@types/node": "^20.11.27",
"@types/react": "^18.2.51",
"@types/react-dom": "^18.0.10",
"@types/react-grid-layout": "^1.3.5",
"@types/react-test-renderer": "^18.0.0",
"@vitejs/plugin-react": "^3.1.0",
"autoprefixer": "^10.4.13",
Expand Down
128 changes: 121 additions & 7 deletions src/components/MediaCard.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { displayToast } from "@/utils";
import { Copy } from "@phosphor-icons/react";
import { Copy, Check, WarningCircle } from "@phosphor-icons/react";
import { useLayoutEffect, useRef, useState } from "react";
import Button from "./Button";
import clsx from "clsx";

interface MediaCardProps {
src: string;
title: string;
caption?: string | null;
date: Date;
id: number;
selectable?: boolean;
}

const options: Intl.DateTimeFormatOptions = {
Expand All @@ -14,22 +18,132 @@ const options: Intl.DateTimeFormatOptions = {
day: "numeric",
};

function MediaCard({ src, title, date, id }: MediaCardProps) {
function MediaCard({
src,
caption,
date,
id,
selectable = false,
}: MediaCardProps) {
const [isEditingCaption, setIsEditingCaption] = useState(false);
const [newCaption, setNewCaption] = useState<string | null | undefined>(
caption,
);
const [isSelected, setIsSelected] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const [temporaryCaption, setTemporaryCaption] = useState(caption);
const [error, setError] = useState<string | null>(null);
const captionTextAreaRef = useRef<HTMLTextAreaElement | null>(null);

const copyHandler = () => {
navigator.clipboard.writeText(id.toString());
displayToast("ID Copied!", "success");
};

useLayoutEffect(() => {
if (captionTextAreaRef.current) {
captionTextAreaRef.current.style.height = "auto";
const scrollHeight = captionTextAreaRef.current.scrollHeight;
captionTextAreaRef.current.style.height = `${scrollHeight + 16}px`;
}
}, [temporaryCaption, newCaption, caption, isEditingCaption]);

return (
<div className=" flex flex-col rounded-lg">
<div className="relative rounded-lg">
<div
className={clsx(
"flex flex-col rounded-lg relative",
selectable && "hover:shadow-zero hover:shadow-primary-main",
isSelected && "shadow-zero shadow-primary-main",
)}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{selectable && (
<span
className={clsx(
"absolute right-0 top-0 w-8 h-8 rounded-lg m-2 z-[999] flex justify-center items-center",

isSelected
? " bg-primary-light border-2 border-primary-dark hover:bg-primary-main"
: isHovered &&
"bg-white border-neutrals-dark-300 border-2 hover:bg-neutrals-dark-100",
)}
onClick={() => setIsSelected(!isSelected)}
>
{isSelected && (
<Check
fill="white"
size={16}
></Check>
)}
</span>
)}
<div className={clsx("relative rounded-lg")}>
<img
src={src}
alt="Image"
className="object-fit rounded-t-lg"
className={clsx("object-fit rounded-t-lg")}
/>
</div>
<div className="bg-white p-2 rounded-b-lg flex flex-col gap-4">
<h3 className="text-body-reg">{title}</h3>
{!isEditingCaption ? (
<div
className="text-body-reg whitespace-pre-line"
onDoubleClick={() => setIsEditingCaption(true)}
>
{newCaption}
</div>
) : (
<div className="flex flex-col justify-center bg-neutrals-light-300">
<textarea
className="w-full bg-neutrals-light-300 p-2 resize-none overflow-hidden"
defaultValue={
newCaption || caption || "Add Text..."
}
ref={captionTextAreaRef}
onChange={() => {
setTemporaryCaption(
captionTextAreaRef.current?.value,
);
}}
/>
<div className="flex w-1/4 self-end justify-center items-end gap-2">
<Button
text="Cancel"
className="h-1/4"
fill={false}
onClick={() => setIsEditingCaption(false)}
/>
<Button
className="h-1/4"
text="Save"
onClick={() => {
if (
captionTextAreaRef.current?.value &&
captionTextAreaRef.current?.value.split(
" ",
).length > 50
) {
setError(
"Please keep caption within 50 words",
);
} else {
setError(null);
setNewCaption(
captionTextAreaRef.current?.value.trim(),
);
setIsEditingCaption(false);
}
}}
/>
</div>
</div>
)}
{error && (
<div className="text-status-red-main flex flex-row gap-2 items-center">
<WarningCircle /> {error}
</div>
)}
<div className="flex justify-between">
<span className="text-sm text-neutrals-dark-200">
{date.toLocaleDateString("en-US", options)}
Expand Down
4 changes: 4 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
@tailwind components;
@tailwind utilities;

* {
box-sizing: border-box;
}

@font-face {
font-family: "Proxima Nova";
src: url(@/assets/fonts/ProximaNovaRegular.otf);
Expand Down
10 changes: 4 additions & 6 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import ReactDOM from "react-dom/client";
import App from "@/App";
import "@/index.css";

ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
).render(
<React.StrictMode>
<App />
</React.StrictMode>
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
31 changes: 24 additions & 7 deletions src/routes/test/MediaCardTestRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import MediaCard from "@/components/MediaCard";
import { ToastContainer } from "react-toastify";

const getMeta = (
url: string,
cb: (error: string | Event | null, img?: HTMLImageElement | null) => void,
) => {
const img = new Image();
img.onload = () => cb(null, img);
img.onerror = (err) => cb(err);
img.src = url;
};

function MediaCardTestRoute() {
getMeta(
"https://images.unsplash.com/photo-1724579243894-6a8c9bbfe88c",
(err, img) => {
console.log(img?.naturalWidth, img?.naturalHeight);
},
);
return (
<div className="bg-blue-200 min-h-screen flex justify-center items-center p-4">
<ToastContainer
Expand All @@ -11,40 +27,41 @@ function MediaCardTestRoute() {
closeButton={false}
hideProgressBar={true}
/>
<div className="grid grid-cols-3 gap-4">
<div className="grid grid-flow-row grid-cols-4 gap-8 md:gap-12 lg:gap-16">
<MediaCard
src="https://images.unsplash.com/photo-1724579243894-6a8c9bbfe88c"
title="Channeling their inner artist with a paintbrush and a splash of color!"
caption="Channeling their inner artist with a paintbrush and a splash of color!"
id={1234}
date={new Date("Oct 2, 2024")}
selectable
/>
<MediaCard
src="https://images.unsplash.com/photo-1723984834599-5357b87f727c"
title="Channeling their inner artist with a paintbrush and a splash of color!"
caption="Channeling their inner artist with a paintbrush and a splash of color!"
id={1234}
date={new Date("Oct 2, 2024")}
/>
<MediaCard
src="https://images.unsplash.com/photo-1724505599369-2c1d43324fdc"
title="Channeling their inner artist with a paintbrush and a splash of color!"
caption="Channeling their inner artist with a paintbrush and a splash of color!"
id={1234}
date={new Date("Oct 2, 2024")}
/>
<MediaCard
src="https://images.unsplash.com/photo-1724579243894-6a8c9bbfe88c"
title="Channeling their inner artist with a paintbrush and a splash of color!"
caption="Channeling their inner artist with a paintbrush and a splash of color!"
id={1234}
date={new Date("Oct 2, 2024")}
/>
<MediaCard
src="https://images.unsplash.com/photo-1724505599369-2c1d43324fdc"
title="Channeling their inner artist with a paintbrush and a splash of color!"
caption="Channeling their inner artist with a paintbrush and a splash of color!"
id={1234}
date={new Date("Oct 2, 2024")}
/>
<MediaCard
src="https://images.unsplash.com/photo-1723984834599-5357b87f727c"
title="Channeling their inner artist with a paintbrush and a splash of color!"
caption="Channeling their inner artist with a paintbrush and a splash of color!"
id={1234}
date={new Date("Oct 2, 2024")}
/>
Expand Down
3 changes: 3 additions & 0 deletions tailwind.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ module.exports = {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
boxShadow: {
zero: "0 0 0 2px",
},
colors: {
primary: {
light: "#6EC2E4",
Expand Down

0 comments on commit 7b83d10

Please sign in to comment.