Skip to content

Commit

Permalink
Calculator layout, non-working text entry (for a moment).
Browse files Browse the repository at this point in the history
  • Loading branch information
davepeck committed Jan 22, 2024
1 parent ea9ba4f commit 3e78f64
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 73 deletions.
10 changes: 8 additions & 2 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import clsx from "clsx";

interface Props {
title: string;
className?: string;
onClick: () => void;
}

const Button: React.FC<Props> = ({ title, onClick }) => (
const Button: React.FC<Props> = ({ title, className, onClick }) => (
<button
className="font-plein font-medium text-[18px] leading-[25.2px] border border-sun uppercase bg-transparent px-4 py-2 text-sun hover:text-inherit hover:bg-sun transition-colors duration-200"
className={clsx(
"cursor-pointer font-plein font-medium text-[18px] leading-[25.2px] border border-sun uppercase bg-transparent px-4 py-2 text-sun hover:text-inherit hover:bg-sun transition-colors duration-200",
className
)}
onClick={onClick}
>
{title}
Expand Down
133 changes: 81 additions & 52 deletions src/components/Calculator.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
import { useState } from "react";
import type { ReactNode } from "react";
import clsx from "clsx";
import Button from "./Button";

import { P21, P28, H2 } from "./Typeography";
import { P21, P28, Em28, H2 } from "./Typeography";

/** Format dollars as dollars, with commas. */
const formatUSD = (usd: number, showCents: boolean = false) =>
usd.toLocaleString("en-US", {
const formatUSD = (
usd: number,
showCents: boolean = true,
showDollarSign: boolean = true
) => {
const formatted = usd.toLocaleString("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: showCents ? 2 : 0,
});
return showDollarSign ? formatted : formatted.slice(1);
};

/** Format cents as dollars, with commas. */
const formatCents = (cents: number, showCents: boolean = false) =>
formatUSD(cents / 100);
const formatCents = (
cents: number,
showCents: boolean = true,
showDollarSign: boolean = true
) => formatUSD(cents / 100, showCents, showDollarSign);

/** Format a percentage (from 0.0 to 1.0) in human-readable form. */
const formatPerc = (perc: number) => `${Math.round(perc * 100)}%`;
Expand All @@ -32,7 +43,7 @@ type Distribution = Allocation[];
/** The suggested distribution of donations for the 2024 election cycle. */
const DISTRIBUTION: Distribution = [
{
name: "Voter turnout",
name: "Voter Turnout",
description: (
<>
<P21 className="text-medium">
Expand All @@ -43,21 +54,21 @@ const DISTRIBUTION: Distribution = [
<P21 className="text-medium">
We’ve chosen{" "}
<a
href="#"
onClick={() => alert("TODO: link")}
className="underline hover:text-white transition-colors duration-200"
>
Everybody Votes
</a>
,{" "}
<a
href="#"
onClick={() => alert("TODO: link")}
className="underline hover:text-white transition-colors duration-200"
>
Working America
</a>
, and the{" "}
<a
href="#"
onClick={() => alert("TODO: link")}
className="underline hover:text-white transition-colors duration-200"
>
Movement Voter Project
Expand All @@ -70,7 +81,7 @@ const DISTRIBUTION: Distribution = [
url: () => "https://secure.actblue.com/donate/dsdefeattrump1",
},
{
name: "Community specific",
name: "Community Specific",
description: (
<>
<P21 className="text-medium">
Expand All @@ -82,21 +93,21 @@ const DISTRIBUTION: Distribution = [
<P21 className="text-medium">
We’ve chosen{" "}
<a
href="#"
onClick={() => alert("TODO: link")}
className="underline hover:text-white transition-colors duration-200"
>
Somos Votantes
</a>
,{" "}
<a
href="#"
onClick={() => alert("TODO: link")}
className="underline hover:text-white transition-colors duration-200"
>
Black Leaders Organizing Communities
</a>
, and the{" "}
<a
href="#"
onClick={() => alert("TODO: link")}
className="underline hover:text-white transition-colors duration-200"
>
New George Project
Expand All @@ -109,7 +120,7 @@ const DISTRIBUTION: Distribution = [
url: () => "https://secure.actblue.com/donate/dsdefeattrump1",
},
{
name: "Biden campaign",
name: "Biden Campaign",
description: (
<P21 className="text-medium">
Donating to the Biden campaign is impactful because their analysts know
Expand All @@ -121,7 +132,7 @@ const DISTRIBUTION: Distribution = [
url: () => "https://secure.joebiden.com/a/west-bvf-2?attr=115663298",
},
{
name: "Competitive house races",
name: "Competitive House Races",
description: (
<P21 className="text-medium">
Winning the house is an important backup in case Trump wins, but a good
Expand All @@ -132,7 +143,7 @@ const DISTRIBUTION: Distribution = [
url: () => "https://secure.actblue.com/donate/swingleft_house_2024/",
},
{
name: "Infrastructure & innovation",
name: "Infrastructure & Innovation",
description: (
<>
<P21 className="text-medium">
Expand All @@ -142,14 +153,14 @@ const DISTRIBUTION: Distribution = [
<P21 className="text-medium">
We’ve chosen{" "}
<a
href="#"
onClick={() => alert("TODO: link")}
className="underline hover:text-white transition-colors duration-200"
>
Accelerate Change
</a>{" "}
and the{" "}
<a
href="#"
onClick={() => alert("TODO: link")}
className="underline hover:text-white transition-colors duration-200"
>
DNC
Expand Down Expand Up @@ -180,19 +191,35 @@ const DonationLink = ({
const AllocationComponent = ({
allocation,
usd,
index,
}: {
allocation: Allocation;
usd: number;
index: number;
}) => {
return (
<div className="allocation mb-8">
<div className="allocation-name text-4xl mb-4">
{formatUSD(usd)} {allocation.name}
<div className="flex flex-row lg:space-x-8 items-start flex-wrap lg:flex-nowrap">
<P28>
<Em28 className="text-white">{index}.</Em28>
</P28>
<div className="flex flex-col flex-shrink pt-4 lg:pt-0">
<P28>
<Em28 className="text-white">
{allocation.name} &mdash; {formatPerc(allocation.perc)}
</Em28>
</P28>
<div className="allocation-description space-y-8 mt-2">
{allocation.description}
</div>
</div>
<div className="allocation-description">{allocation.description}</div>
{/* <div className="allocation-perc">{formatPerc(allocation.perc)}</div> */}
<div className="allocation-link mt-4 underline hovere:bg-gray-200">
<a href={allocation.url(usd)}>(add a donation link to actblue here)</a>
<div className="min-w-[21%] pt-6 lg:pt-0">
<Button
title={`Donate ${formatUSD(usd, true, true)}`}
onClick={() => {
window.open(allocation.url(usd), "_blank");
}}
className="w-full text-[20px] leading-[28px] py-4"
/>
</div>
</div>
);
Expand All @@ -207,10 +234,11 @@ const DistributionComponent = ({
usd: number;
}) => {
return (
<div className="distribution pt-8">
{distribution.map((allocation) => (
<div className="flex flex-col py-8 space-y-8">
{distribution.map((allocation, i) => (
<AllocationComponent
key={allocation.name}
index={i + 1}
allocation={allocation}
usd={usd * allocation.perc}
/>
Expand All @@ -226,39 +254,40 @@ const DonationAmountBox = ({
}: {
usd: number;
setUSD: (usd: number) => void;
}) => (
<div>
<p className="font-switzer font-bold uppercase text-[15px] leading-[22.5px] text-white">
My donation amount
</p>
<div className="flex flex-row items-center max-w-[364px] border border-medium focus-within:border-sun transition-colors duration-200 bg-transparent text-white font-switzer font-normal text-[56px] leading-[56px]">
<div className="text-white pl-4 flex-grow leading-10">$</div>
<div className="max-w-[100%] flex-shrink leading-10">
<input
type="number"
step={0.01}
min={5.0}
max={250000}
value={usd}
onChange={(e) => {
const parsed = parseFloat(e.target.value);
if (!isNaN(parsed) && parsed > 0 && parsed < 250000) {
setUSD(parseInt(e.target.value));
}
}}
className="text-right bg-transparent pr-4 outline-none transition-colors duration-200 leading-10 focus:bg-dark w-[100%] [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
/>
}) => {
const [text, setText] = useState<string>(formatUSD(usd, true, false));

return (
<div>
<p className="font-switzer font-bold uppercase text-[15px] leading-[22.5px] text-white">
My donation amount
</p>
<div className="flex flex-row items-center max-w-[364px] border border-medium focus-within:border-sun transition-colors duration-200 bg-transparent text-white font-switzer font-normal text-[56px] leading-[56px]">
<div className="text-white pl-4 flex-grow leading-10">$</div>
<div className="max-w-[100%] flex-shrink leading-10">
<input
type="text"
value={text}
onChange={(e) => {
const parsed = parseFloat(e.target.value);
if (!isNaN(parsed) && parsed > 0 && parsed < 250000) {
setUSD(parseInt(e.target.value));
}
}}
className="text-right bg-transparent pr-4 outline-none transition-colors duration-200 leading-10 focus:bg-dark w-[100%] [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
/>
</div>
</div>
</div>
</div>
);
);
};

/** React component that displays a calculator. */
const Calculator = () => {
const [usd, setUSD] = useState(5000);
return (
<div className="flex flex-col space-y-6">
<H2 className="text-white">Make your donation (TODO)</H2>
<H2 className="text-white">Make your donation</H2>
<P28 className="text-white">
Edit the amount you’d like to donate in the box below and we’ll
calculate how much to donate to where:
Expand Down
20 changes: 17 additions & 3 deletions src/components/Typeography.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ export type TypographicComponent = React.FC<
>;

export type TypographicLink = React.FC<
React.PropsWithChildren<{ className?: string; href: string }>
React.PropsWithChildren<{
className?: string;
href?: string;
onClick?: () => void;
}>
>;

export const H1: TypographicComponent = ({ children, className }) => (
Expand Down Expand Up @@ -111,10 +115,20 @@ export const PMedium: TypographicComponent = ({ children, className }) => (
<p className={clsx(SWITZER_MEDIUM, className)}>{children}</p>
);

export const AMedium: TypographicLink = ({ children, className, href }) => (
export const AMedium: TypographicLink = ({
children,
className,
href,
onClick,
}) => (
<a
href={href}
className={clsx(SWITZER_MEDIUM, "underline !leading-[44.8px]", className)}
onClick={onClick}
className={clsx(
SWITZER_MEDIUM,
"cursor-pointer underline !leading-[44.8px]",
className
)}
>
{children}
</a>
Expand Down
Loading

0 comments on commit 3e78f64

Please sign in to comment.