feat: add components
This commit is contained in:
parent
c7f97a3dc2
commit
627733ceb9
32
components/Feed.js
Normal file
32
components/Feed.js
Normal file
@ -0,0 +1,32 @@
|
||||
const Feed = ({
|
||||
media,
|
||||
type,
|
||||
setOverlayContent,
|
||||
currentIndex,
|
||||
limitPerPage,
|
||||
}) => (
|
||||
<main className="grid grid-cols-3 gap-1">
|
||||
{media
|
||||
.slice(currentIndex, limitPerPage)
|
||||
.map(({ id, url, previewPath, embedPath = url }) => (
|
||||
<a
|
||||
key={id}
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferer"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
|
||||
setOverlayContent({
|
||||
url: embedPath,
|
||||
type,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img alt={url} src={previewPath} className={`aspect-${type}`} />
|
||||
</a>
|
||||
))}
|
||||
</main>
|
||||
);
|
||||
|
||||
export default Feed;
|
49
components/Footer.js
Normal file
49
components/Footer.js
Normal file
@ -0,0 +1,49 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import { YEAR_TO_BUMP } from "../constants";
|
||||
|
||||
const Footer = ({ license, links, repo, commitID }) => (
|
||||
<footer className="flex items-center justify-between flex-wrap py-4 text-center mt-10">
|
||||
<div className="w-10/12 md:w-7/12 mx-auto font-semibold">
|
||||
<div className="text-sm mb-10">
|
||||
{links.map(({ url, label }) => (
|
||||
<Link key={url + label} href={url} className="mx-4">
|
||||
{label}
|
||||
</Link>
|
||||
))}
|
||||
<Link href={repo} className="mx-4">
|
||||
Source Code
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="text-sm font-normal text-neutral-400">
|
||||
<p>
|
||||
© {YEAR_TO_BUMP}{" "}
|
||||
<Link
|
||||
className="text-neutral-600"
|
||||
target="_blank"
|
||||
rel="noopener noreferer"
|
||||
href="https://github.com/faultables"
|
||||
>
|
||||
faultables
|
||||
</Link>{" "}
|
||||
• All media is licensed under{" "}
|
||||
<Link className="underline" href={license.url}>
|
||||
{license.name}
|
||||
</Link>{" "}
|
||||
unless stated otherwise •{" "}
|
||||
<Link
|
||||
className="text-neutral-600"
|
||||
target="_blank"
|
||||
rel="noopener noreferer"
|
||||
href={`${repo}/commit/${commitID}`}
|
||||
>
|
||||
{commitID}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
|
||||
export default Footer;
|
21
components/Navbar.js
Normal file
21
components/Navbar.js
Normal file
@ -0,0 +1,21 @@
|
||||
import Link from "next/link";
|
||||
|
||||
const Navbar = ({ name, logo }) => (
|
||||
<nav className="flex py-2 border-b">
|
||||
<div className="w-3/12 md:w-7/12 md:mx-auto">
|
||||
<div className="md:w-2/12">
|
||||
<Link href="/">
|
||||
{logo ? (
|
||||
<img alt={name} src={logo} className="w-full" />
|
||||
) : (
|
||||
<h1 className="font-bold leading-relaxed tracking-tight text-2xl text-neutral-800">
|
||||
{name}
|
||||
</h1>
|
||||
)}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
|
||||
export default Navbar;
|
57
components/Overlay.js
Normal file
57
components/Overlay.js
Normal file
@ -0,0 +1,57 @@
|
||||
import { MEDIA_TYPE } from "../constants";
|
||||
|
||||
const OverlayContent = ({ overlayContent }) => {
|
||||
if (overlayContent?.type === MEDIA_TYPE.PHOTOS) {
|
||||
return (
|
||||
<img
|
||||
alt={overlayContent?.url}
|
||||
src={overlayContent?.url}
|
||||
className="z-20 cursor-default"
|
||||
/>
|
||||
);
|
||||
} else if (overlayContent?.type === MEDIA_TYPE.VIDEOS) {
|
||||
return (
|
||||
<iframe
|
||||
width="50%"
|
||||
height="50%"
|
||||
allow="fullscreen"
|
||||
sandbox="allow-same-origin allow-scripts allow-popups"
|
||||
src={overlayContent?.url}
|
||||
></iframe>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const Overlay = ({ overlayContent, closeOverlay }) => {
|
||||
const isOverlayOpen = overlayContent?.type !== undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={closeOverlay}
|
||||
className={`${
|
||||
isOverlayOpen
|
||||
? "bg-neutral-800/95 fixed w-screen h-screen left-0 top-0 cursor-pointer"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{isOverlayOpen ? (
|
||||
<p
|
||||
onClick={closeOverlay}
|
||||
className="fixed right-0 bottom-0 m-5 text-white rounded text-sm"
|
||||
>
|
||||
Click anywhere or press "Escape" to close
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
<div
|
||||
className={`flex justify-center items-center ${
|
||||
isOverlayOpen ? "h-screen w-screen" : "h-0 w-0"
|
||||
}`}
|
||||
>
|
||||
<OverlayContent overlayContent={overlayContent} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Overlay;
|
46
components/Profile.js
Normal file
46
components/Profile.js
Normal file
@ -0,0 +1,46 @@
|
||||
const Profile = ({ profile, totalPosts }) => (
|
||||
<div className="flex flex-warp">
|
||||
<div className="lg:w-4/12 w-3/12 md:py-10 lg:px-20 md:px-10">
|
||||
<img
|
||||
alt={profile.display_name}
|
||||
src={profile.avatar}
|
||||
className="w-100 rounded-full mx-auto border border-gray-200 p-1"
|
||||
/>
|
||||
</div>
|
||||
<div className="lg:w-9/12 md:w-10/12 px-10 mb-5 md:p-5">
|
||||
<div className="flex items-center">
|
||||
<h2 className="text-2xl font-semibold">{profile.username}</h2>
|
||||
<div className="ml-4">
|
||||
<a
|
||||
className="rounded-md bg-gray-100 px-5 font-semibold py-2 ml-2 text-sm leading-relaxed"
|
||||
href={profile.follow_url}
|
||||
>
|
||||
Follow
|
||||
</a>
|
||||
<a
|
||||
className="rounded-md bg-gray-100 px-5 font-semibold py-2 ml-2 text-sm leading-relaxed"
|
||||
href={profile.message_url}
|
||||
>
|
||||
Message
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-5 font-bold text-sm">
|
||||
<p>{totalPosts} posts</p>
|
||||
</div>
|
||||
<p className="font-bold mb-1">{profile.display_name}</p>
|
||||
<p>{profile.about}</p>
|
||||
<p className="leading-loose font-semibold text-blue-900">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferer"
|
||||
href={`https://${profile.link}`}
|
||||
>
|
||||
{profile.link}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Profile;
|
27
components/Tab.js
Normal file
27
components/Tab.js
Normal file
@ -0,0 +1,27 @@
|
||||
const isActiveTab = (currentTab, activeTab) =>
|
||||
currentTab === activeTab ? "border-t" : "";
|
||||
|
||||
const Tab = ({ activeTab, setActiveTab }) => (
|
||||
<div className="flex items-center justify-center text-center gap-5 border-t mb-4">
|
||||
<button
|
||||
onClick={() => setActiveTab("photos")}
|
||||
className={`uppercase tracking-tight font-semibold text-sm border-black pt-3 px-3 ${isActiveTab(
|
||||
"photos",
|
||||
activeTab
|
||||
)}`}
|
||||
>
|
||||
Photos
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("videos")}
|
||||
className={`uppercase tracking-tight font-semibold text-sm pt-3 px-3 border-black ${isActiveTab(
|
||||
"videos",
|
||||
activeTab
|
||||
)}`}
|
||||
>
|
||||
Videos
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Tab;
|
Loading…
Reference in New Issue
Block a user