Skip to content

Commit

Permalink
feat[STK-33]: create stack frequency section
Browse files Browse the repository at this point in the history
  • Loading branch information
berteotti committed Jul 13, 2023
1 parent 80c9baf commit ef105c9
Show file tree
Hide file tree
Showing 11 changed files with 344 additions and 34 deletions.
6 changes: 5 additions & 1 deletion packages/app/app/ui/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ import {
HeadingText,
OverlineText,
DialogContent,
CaptionText,
Calendar,
} from "@/ui";
import { CaptionText } from "@/ui/text/CaptionText";
import { useRef, useState } from "react";
import { DatePicker } from "../../components/DatePicker";

export default function Page() {
// radioButtons
Expand All @@ -37,12 +39,14 @@ export default function Page() {
const [isOpenCancelStackingDialog, setOpenCancelStackingDialog] =
useState(false);
const dialogBtnRef = useRef<HTMLButtonElement>(null);
const [date, setDate] = useState<Date>(new Date());

return (
<div className="my-10">
<HeadingText as="h1" size={6}>
Stackly UI
</HeadingText>
<DatePicker date={date} setDate={setDate} timeCaption="Start time" />
<UISection
title="Buttons"
description="This is a collection of all our current buttons divided by actions."
Expand Down
128 changes: 128 additions & 0 deletions packages/app/components/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"use client";

import { format, isToday, isTomorrow } from "date-fns";

import { Button, Calendar, Icon } from "@/ui";
import { Dialog, Popover } from "@headlessui/react";

import { Dispatch, Ref, SetStateAction, useState } from "react";
import { usePopper } from "react-popper";
import { twMerge } from "tailwind-merge";

interface IDatePicker {
dateTime: Date;
setDateTime: Dispatch<SetStateAction<Date>>;
className?: string;
timeCaption: string;
}

export function DatePicker({
dateTime,
setDateTime,
className,
timeCaption,
}: IDatePicker) {
const [currentDate, setCurrentDate] = useState<Date>(dateTime);
const [referenceElement, setReferenceElement] = useState<
HTMLButtonElement | undefined
>();
const [popperElement, setPopperElement] = useState<
HTMLDivElement | undefined
>();
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: "bottom",
});

const formattedDate = () => {
if (!dateTime) return "Pick a date";
if (isToday(dateTime)) return format(dateTime, "'Today at' HH:mm");
if (isTomorrow(dateTime)) return format(dateTime, "'Tomorrow at' HH:mm");

return format(dateTime, "dd MMM Y @ HH:mm");
};

return (
<Popover>
<Popover.Button
ref={setReferenceElement as Ref<HTMLButtonElement>}
className={twMerge("flex justify-between items-center", className)} // text-em-low if default dates
>
<span>{formattedDate()}</span>
<Icon name="caret-down" className="mr-2 h-4 w-4 text-black" />
</Popover.Button>
<Popover.Panel
ref={setPopperElement as Ref<HTMLDivElement>}
style={styles.popper}
{...attributes.popper}
className="w-fit mx-auto max-md:!fixed max-md:!inset-0 max-md:!flex max-md:!items-center max-md:!justify-center max-md:!p-4 max-md:!transform" /* This styles will make it hover on mobile */
>
{({ close }) => (
<div className="flex flex-col space-y-2 bg-white divide-y divide-surface-50 rounded-2xl border border-surface-50">
<Calendar
mode="single"
selected={currentDate}
onSelect={(newDate: Date | undefined) => {
if (!newDate) return;
if (currentDate) {
newDate.setHours(currentDate.getHours());
newDate.setMinutes(currentDate.getMinutes());
}
setCurrentDate(newDate);
}}
initialFocus
className=""
fromDate={new Date()}
/>
<div className="flex flex-col space-y-2 p-4 pt-2">
<div className="flex justify-between items-center">
<span>{timeCaption}</span>
<div className="flex space-x-2 items-center">
<div className="flex items-center border border-surface-75 rounded-xl py-2 px-3 text-em-low">
<input
className="outline-none font-semibold flex-grow text-sm w-5 text-center"
type="number"
value={currentDate ? format(currentDate, "HH") : ""}
onChange={(event) => {
const value = Number(event.target.value);
if (value >= 0 && value < 24) {
const newDate = new Date(currentDate);
newDate.setHours(value);
setCurrentDate(newDate);
}
}}
/>
</div>
<span>:</span>
<div className="flex items-center border border-surface-75 rounded-xl py-2 px-3 text-em-low">
<input
className="outline-none font-semibold flex-grow text-sm w-5 text-center"
type="number"
value={currentDate ? format(currentDate, "mm") : ""}
onChange={(event) => {
const value = Number(event.target.value);
if (value >= 0 && value < 60) {
const newDate = new Date(currentDate);
newDate.setMinutes(value);
setCurrentDate(newDate);
}
}}
/>
</div>
</div>
</div>
<Button
action="secondary"
onClick={() => {
setDateTime(currentDate);
close();
}}
>
Set Date and Time
</Button>
</div>
</div>
)}
</Popover.Panel>
</Popover>
);
}
2 changes: 2 additions & 0 deletions packages/app/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from "./navbar";
export * from "./ConnectButton";
export * from "./SelectNetwork";
export * from "./DatePicker";
export * from "./token-picker";
97 changes: 66 additions & 31 deletions packages/app/components/stackbox/Stackbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
import { BodyText, Button, Icon, RadioButton, TitleText } from "@/ui";
import { useState } from "react";
import { ConfirmStackModal } from "./ConfirmStackModal";
import { TokenPicker } from "@/components/token-picker/TokenPicker";
import { TokenPicker, DatePicker } from "@/components";

export const Stackbox = () => {
const [isConfirmStackOpen, setConfirmStackIsOpen] = useState(false);
const [isTokenPickerOpen, setTokenPickerIsOpen] = useState(false);

const [frequency, setFrequency] = useState<string>("0");

const [startDateTime, setStartDateTime] = useState<Date>(new Date());
const [endDateTime, setEndDateTime] = useState<Date>(new Date());

const openConfirmStack = () => setConfirmStackIsOpen(true);
const closeConfirmStack = () => setConfirmStackIsOpen(false);

Expand Down Expand Up @@ -44,42 +49,72 @@ export const Stackbox = () => {
/>
</div>
</div>
<div className="px-5 py-6">
<div className="px-5 py-6 space-y-6">
<div className="space-y-2">
<TitleText weight="bold" className="text-em-med">
Stack WETH every
</TitleText>
<div className="flex space-x-2">
<RadioButton
name="hour"
id="hour"
checked={true}
value={"0"}
onChange={() => {}}
>
Hour
</RadioButton>
<RadioButton
name="week"
id="week"
checked={false}
value={"1"}
onChange={() => {}}
>
Week
</RadioButton>
<RadioButton
name="month"
id="month"
checked={false}
value={"2"}
onChange={() => {}}
>
Month
</RadioButton>
<div className="space-y-6">
<div className="flex space-x-2">
<RadioButton
name="hour"
id="hour"
checked={frequency === "0"}
value={"0"}
onChange={(event) => setFrequency(event.target.value)}
>
<BodyText size={2}>Hour</BodyText>
</RadioButton>
<RadioButton
name="day"
id="day"
checked={frequency === "1"}
value={"1"}
onChange={(event) => setFrequency(event.target.value)}
>
<BodyText size={2}>Day</BodyText>
</RadioButton>
<RadioButton
name="week"
id="week"
checked={frequency === "2"}
value={"2"}
onChange={(event) => setFrequency(event.target.value)}
>
<BodyText size={2}>Week</BodyText>
</RadioButton>
<RadioButton
name="month"
id="month"
checked={frequency === "4"}
value={"4"}
onChange={(event) => setFrequency(event.target.value)}
>
<BodyText size={2}>Month</BodyText>
</RadioButton>
</div>
<div className="flex rounded-2xl border border-surface-50 divide-x divide-surface-50">
<div className="flex flex-col w-full px-4 py-3 space-y-2">
<BodyText size={2}>Starting from</BodyText>
<DatePicker
dateTime={startDateTime}
setDateTime={setStartDateTime}
timeCaption="Start time"
className="w-full"
/>
</div>
<div className="flex flex-col w-full px-4 py-3 space-y-2">
<BodyText size={2}>Until</BodyText>
<DatePicker
dateTime={endDateTime}
setDateTime={setEndDateTime}
timeCaption="End time"
className="w-full"
/>
</div>
</div>
</div>
</div>
<p className="py-12 mx-auto w-fit text-em-low">The stackbox™</p>
<Button width="full" onClick={openConfirmStack}>
Stack Now
</Button>
Expand Down
1 change: 1 addition & 0 deletions packages/app/components/token-picker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./TokenPicker";
4 changes: 4 additions & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
"@cowprotocol/cow-sdk": "^2.1.0",
"@headlessui/react": "^1.7.14",
"@headlessui/tailwindcss": "^0.1.3",
"@popperjs/core": "^2.11.8",
"class-variance-authority": "^0.6.0",
"connectkit": "^1.4.0",
"date-fns": "^2.30.0",
"next": "^13.4.1",
"react": "^18.2.0",
"react-day-picker": "^8.8.0",
"react-dom": "^18.2.0",
"react-popper": "^2.3.0",
"tailwind-merge": "^1.12.0",
"viem": "^1.0.2",
"wagmi": "^1.1.1"
Expand Down
8 changes: 8 additions & 0 deletions packages/app/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,11 @@

@tailwind components;
@tailwind utilities;

@layer base {
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
}
66 changes: 66 additions & 0 deletions packages/app/ui/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"use client";

import * as React from "react";

import { DayPicker } from "react-day-picker";

import { buttonStyles, Icon } from ".";
import { twMerge } from "tailwind-merge";

export type CalendarProps = React.ComponentProps<typeof DayPicker>;

function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={className}
classNames={{
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
month: "space-y-4",
caption: "flex justify-center relative items-center mx-4 py-3",
caption_label: "text-sm font-medium",
nav: "space-x-1 flex items-center",
nav_button: twMerge(
buttonStyles({ size: "icon", action: "quaternary" }),
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-y-1 mx-2",
head_row: "flex",
head_cell: "rounded-md w-9 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: "text-center text-sm p-0 relative [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
day: twMerge(
buttonStyles({ size: "icon", action: "quaternary" }),
"h-9 w-9 p-0 font-normal aria-selected:opacity-100 hover:bg-primary-400 focus:bg-primary-400"
),
day_selected: "bg-primary-400 text-primary-foreground ",
day_today: "bg-accent text-accent-foreground",
day_outside: "opacity-50",
day_disabled: "opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
...classNames,
}}
components={{
IconLeft: ({ ...props }) => (
<Icon name="caret-left" className="h-4 w-4" />
),
IconRight: ({ ...props }) => (
<Icon name="caret-right" className="h-4 w-4" />
),
}}
{...props}
/>
);
}
Calendar.displayName = "Calendar";

export { Calendar };
1 change: 1 addition & 0 deletions packages/app/ui/buttons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./Button";
export * from "./ButtonLink";
export * from "./ChipButton";
export * from "./RadioButton";
export * from "./base";
1 change: 1 addition & 0 deletions packages/app/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./icon/Icon";
export * from "./modal";
export * from "./table/Table";
export * from "./text";
export * from "./Calendar";
Loading

0 comments on commit ef105c9

Please sign in to comment.