Skip to content

Commit

Permalink
Add projects section to homepage
Browse files Browse the repository at this point in the history
  • Loading branch information
wkirby committed Nov 26, 2024
1 parent 2d0820a commit ab40d91
Show file tree
Hide file tree
Showing 14 changed files with 398 additions and 11 deletions.
27 changes: 27 additions & 0 deletions data/projects.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
- title: "Ream: Contract management made simple"
content: |
Built on top of our in-house contract management solution, **Ream**
offers an all-in-one platform to your small-business contracting needs.
Draft, review, send, and sign your most used documents all in one
easy-to-use platform.
link: https://reamdocs.com
button: Learn more
image: '/img/projects/ream.png'
- title: Daily Doublet
content: |
**The Daily Doublet** is a modern twist on a classic word puzzle invented
by Lewis Carroll in 1877. Also known as word-links, or laddergrams,
this game challenges you to transform one word into another by changing
just one letter at a time. New puzzle every day!
link: https://dailydoublet.com
button: Play now
image: '/img/projects/dailydoublet.png'
- title: 🌹 beautyshot
content: |
**beautyshot** is a simple screenshot beautifier. Quickly style and
resize your screenshots for sharing to X (nee twitter), LinkedIn,
youtube, instagram, and more.
link: https://shot.beauty/
button: Try it out
image: '/img/projects/beautyshot.png'

50 changes: 50 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
"homepage": "https://apsis.io",
"dependencies": {
"clsx": "^2.1.1",
"embla-carousel": "^8.5.1",
"embla-carousel-react": "^8.5.1",
"fast-glob": "^3.3.2",
"gray-matter": "^4.0.3",
"highlight.js": "^11.10.0",
Expand Down
Binary file added public/img/projects/beautyshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/projects/dailydoublet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/projects/ream.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 18 additions & 3 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import clsx from "clsx";
import { HTMLProps } from "react";
import styles from "styles/components/Button.module.scss";

type ButtonProps = {
type BaseButtonProps = {
children: React.ReactNode;
tag?: React.ComponentType | keyof JSX.IntrinsicElements;
EndIcon?: React.ComponentType | keyof JSX.IntrinsicElements;
className?: string;
href?: string;
variant?: "primary" | "secondary" | "tertiary";
size?: "lg" | "sm";
};
disabled?: boolean;
}

type LinkButtonProps = BaseButtonProps & {
href: string;
onClick?: undefined;
} & HTMLProps<HTMLAnchorElement>;

type ButtonButtonProps = BaseButtonProps & {
href?: undefined;
onClick: React.MouseEventHandler<HTMLButtonElement>;
} & HTMLProps<HTMLButtonElement>;


type ButtonProps = LinkButtonProps | ButtonButtonProps;

export const Button: React.FC<ButtonProps> = ({
children,
Expand All @@ -24,6 +38,7 @@ export const Button: React.FC<ButtonProps> = ({
const Component = href ? "a" : tag;

return (
// @ts-ignore: this is fine
<Component
href={href}
className={clsx(
Expand Down
94 changes: 94 additions & 0 deletions src/components/Carousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import clsx from "clsx";
import styles from "styles/components/Carousel.module.scss";
import { EmblaCarouselType } from "embla-carousel";

import { EmblaOptionsType } from "embla-carousel";
import useEmblaCarousel from "embla-carousel-react";
import { ReactNode, useCallback, useEffect, useState } from "react";
import { Button } from "./Button";
import { MarkdownContent } from "./MarkdownContent";
import { ChevronLeft } from "lucide-react";

type PropType = {
slides: ReactNode[];
options?: EmblaOptionsType;
};

type UsePrevNextButtonsType = {
prevBtnDisabled: boolean;
nextBtnDisabled: boolean;
onPrevButtonClick: () => void;
onNextButtonClick: () => void;
};

export const usePrevNextButtons = (
emblaApi: EmblaCarouselType | undefined,
): UsePrevNextButtonsType => {
const [prevBtnDisabled, setPrevBtnDisabled] = useState(true);
const [nextBtnDisabled, setNextBtnDisabled] = useState(true);

const onPrevButtonClick = useCallback(() => {
if (!emblaApi) return;
emblaApi.scrollPrev();
}, [emblaApi]);

const onNextButtonClick = useCallback(() => {
if (!emblaApi) return;
emblaApi.scrollNext();
}, [emblaApi]);

const onSelect = useCallback((emblaApi: EmblaCarouselType) => {
setPrevBtnDisabled(!emblaApi.canScrollPrev());
setNextBtnDisabled(!emblaApi.canScrollNext());
}, []);

useEffect(() => {
if (!emblaApi) return;

onSelect(emblaApi);
emblaApi.on("reInit", onSelect).on("select", onSelect);
}, [emblaApi, onSelect]);

return {
prevBtnDisabled,
nextBtnDisabled,
onPrevButtonClick,
onNextButtonClick,
};
};

export const Carousel: React.FC<PropType> = ({ slides = [], options }) => {
const [emblaRef, emblaApi] = useEmblaCarousel(options);

const {
prevBtnDisabled,
nextBtnDisabled,
onPrevButtonClick,
onNextButtonClick,
} = usePrevNextButtons(emblaApi);

return (
<section className={styles.carousel}>
<div className={clsx(styles.carousel__viewport)} ref={emblaRef}>
<div className={clsx(styles.carousel__container)}>
{slides.map((slide, idx) => (
<div className={clsx(styles.carousel__slide)} key={idx}>
{slide}
</div>
))}
</div>
</div>

<div className={clsx(styles.carousel__controls)}>
<div className={clsx(styles.carousel__buttons)}>
<Button variant="secondary" onClick={onPrevButtonClick} disabled={prevBtnDisabled}>
Prev
</Button>
<Button variant="secondary" onClick={onNextButtonClick} disabled={nextBtnDisabled}>
Next
</Button>
</div>
</div>
</section>
);
};
44 changes: 44 additions & 0 deletions src/components/ProjectSlide.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import clsx from "clsx";
import styles from "styles/components/ProjectSlide.module.scss";
import { Button } from "./Button";
import { ChevronRight } from "lucide-react";

type ProjectSlideProps = {
title: string;
image: string;
link: string;
button: string;
content: React.ReactNode;
className?: string;
};

export const ProjectSlide: React.FC<ProjectSlideProps> = ({
title,
content,
image,
button,
link,
className,
}) => {
return (
<div className={clsx(styles.project_slide, className)}>
<img src={image} alt={title} className={styles.project_slide__image} />

<div className={clsx(styles.project_slide__content)}>
<header className={styles.project_slide__header}>
<h2 className={styles.project_slide__title}>{title}</h2>
</header>

<div className="typography">{content}</div>

{button && link && (
<footer>
<Button EndIcon={ChevronRight} target="_blank" href={link}>
{button}
</Button>
</footer>
)}
</div>
</div>
);
};
2 changes: 1 addition & 1 deletion src/lib/posts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { PostFrontmatter, Post, PostParams, Person } from "lib/types";
import _ from "lodash";

const postsDirectory = path.join(process.cwd(), "posts");
const dataDirectory = path.join(process.cwd(), "data");
export const dataDirectory = path.join(process.cwd(), "data");

export const getCtas = () => {
const ctas = readFileSync(path.join(dataDirectory, "ctas.yml"));
Expand Down
50 changes: 45 additions & 5 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,54 @@
import { Button } from "components/Button";
import { Carousel } from "components/Carousel";
import { Clients } from "components/Clients";
import { Hero } from "components/Hero";
import { MarkdownContent } from "components/MarkdownContent";
import { PageMeta } from "components/PageMeta";
import { ProjectSlide } from "components/ProjectSlide";
import { Row } from "components/Row";
import { Section } from "components/Section";
import { ServicesSection } from "components/ServicesSection";
import { SiteLayout } from "components/SiteLayout";
import { siteConf } from "conf";
import { readFileSync } from "fs";
import yaml from "js-yaml";
import { formattedTitle } from "lib/metadata";
import { dataDirectory, getRandomCta } from "lib/posts";
import {
ChevronRight,
FileJson,
FormInput,
Gem,
Github,
List,
PresentationIcon,
SquareTerminalIcon,
} from "lucide-react";
import { NextPage } from "next";
import Head from "next/head";
import path from "path";
import { RepoCard } from "../components/RepoCard";
import { ServicesSection } from "../components/ServicesSection";
import { CtaProps } from "components/Cta";

export const IndexPage: NextPage = () => {
export async function getStaticProps() {
const projectsData = readFileSync(path.join(dataDirectory, "projects.yml"));

return {
props: {
projects: yaml.load(projectsData),
cta: getRandomCta()
},
};
}

export const IndexPage: NextPage<{ projects: any[], cta: CtaProps }> = ({ projects, cta }) => {
return (
<>
<Head>
<title>{formattedTitle(siteConf.meta.title)}</title>
<PageMeta />
</Head>

<SiteLayout showTagline navTheme="blue" navGuides>
<SiteLayout showTagline navTheme="blue" navGuides cta={cta}>
<Section theme="blue" className="overflow-hidden">
<Hero />
</Section>
Expand All @@ -41,7 +61,12 @@ export const IndexPage: NextPage = () => {
<ServicesSection />
</Section>

<Section label="Open Source" centerLabel spaced Icon={Github}>
<Section
label="Open Source"
centerLabel
spaced
Icon={SquareTerminalIcon}
>
<Row>
<RepoCard
title="phi_attrs"
Expand Down Expand Up @@ -105,6 +130,21 @@ export const IndexPage: NextPage = () => {
</footer>
</section>
</Section>

<Section label="Projects" Icon={PresentationIcon} spaced>
<Carousel
slides={projects.map((p, i) => (
<ProjectSlide
key={i}
title={p.title}
content={<MarkdownContent content={p.content} />}
image={p.image}
button={p.button}
link={p.link}
/>
))}
/>
</Section>
</SiteLayout>
</>
);
Expand Down
Loading

0 comments on commit ab40d91

Please sign in to comment.