diff --git a/site-new/assets/icons/ArrowRight.tsx b/site-new/assets/icons/ArrowRight.tsx new file mode 100644 index 000000000..7efb9ed06 --- /dev/null +++ b/site-new/assets/icons/ArrowRight.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; +import { SVGProps } from "react"; +const ArrowRight = (props: SVGProps) => ( + + + + + +); +export default ArrowRight; diff --git a/site-new/lib/utils.ts b/site-new/lib/utils.ts index 3cf737bdc..19284d8a8 100644 --- a/site-new/lib/utils.ts +++ b/site-new/lib/utils.ts @@ -114,3 +114,21 @@ export function typeWriter({ } type(); } + +export const createGoogleCalendarLink = (event) => { + const startTime = new Date(event.start) + .toISOString() + .replace(/-|:|\.\d\d\d/g, ""); + const endTime = new Date(event.end) + .toISOString() + .replace(/-|:|\.\d\d\d/g, ""); + const details = encodeURIComponent( + event.description || "No details provided.", + ); + const location = encodeURIComponent( + event.location || "No location provided.", + ); + const text = encodeURIComponent(event.summary || "No title"); + + return `https://calendar.google.com/calendar/render?action=TEMPLATE&text=${text}&dates=${startTime}/${endTime}&details=${details}&location=${location}`; +}; diff --git a/site-new/src/components/Calendar/Calendar.tsx b/site-new/src/components/Calendar/Calendar.tsx index 3ea43b1ac..d10056450 100644 --- a/site-new/src/components/Calendar/Calendar.tsx +++ b/site-new/src/components/Calendar/Calendar.tsx @@ -44,7 +44,7 @@ const Calendar = ({
FRIDAY
SATURDAY
-
+
{days.map((day, dayIdx) => { const isNotInThemonth = isBefore(day, firstDayCurrentMonth) || diff --git a/site-new/src/components/Calendar/MonthSwitcher.tsx b/site-new/src/components/Calendar/MonthSwitcher.tsx index a13d35efc..4fac8f866 100644 --- a/site-new/src/components/Calendar/MonthSwitcher.tsx +++ b/site-new/src/components/Calendar/MonthSwitcher.tsx @@ -1,37 +1,49 @@ import React from "react"; import { useCalendar } from "./CalendarCtx"; -import { format } from "date-fns"; +import { add, format } from "date-fns"; import Heading from "@theme/Heading"; +import { cn } from "@site/lib/utils"; -const MonthSwitcher = () => { +const MonthSwitcher = ({ + maxDate, + minDate, + className, +}: { + maxDate: Date; + minDate: Date; + className?: string; +}) => { const { currentMonth, previousMonth, nextMonth } = useCalendar(); + const isPreviouseMonthDisabled = add(currentMonth, { months: -1 }) < minDate; + const isNextMonthDisabled = add(currentMonth, { months: 1 }) > maxDate; + return ( - <> -
- +
+ - - {format(currentMonth, "MMMM yyyy")} - - -
- + + {format(currentMonth, "MMMM yyyy")} + + +
); }; diff --git a/site-new/src/components/Loading/Loading.tsx b/site-new/src/components/Loading/Loading.tsx new file mode 100644 index 000000000..3f3af9c43 --- /dev/null +++ b/site-new/src/components/Loading/Loading.tsx @@ -0,0 +1,21 @@ +import clsx from "clsx"; +import styles from "./loading.module.css"; +import React from "react"; + +const Loading = () => { + return ( +
+
+
+
+
+
+
+
+
+
+
+ ); +}; + +export default Loading; diff --git a/site-new/src/components/Loading/index.ts b/site-new/src/components/Loading/index.ts new file mode 100644 index 000000000..7f6397f73 --- /dev/null +++ b/site-new/src/components/Loading/index.ts @@ -0,0 +1 @@ +export { default as Loading } from "./Loading"; diff --git a/site-new/src/components/Loading/loading.module.css b/site-new/src/components/Loading/loading.module.css new file mode 100644 index 000000000..0aa303563 --- /dev/null +++ b/site-new/src/components/Loading/loading.module.css @@ -0,0 +1,102 @@ +/* From Uiverse.io by alexruix */ +.loader { + --cell-size: 52px; + --cell-spacing: 1px; + --cells: 3; + --total-size: calc(var(--cells) * (var(--cell-size) + 2 * var(--cell-spacing))); + display: flex; + flex-wrap: wrap; + width: var(--total-size); + height: var(--total-size); +} + +.cell { + flex: 0 0 var(--cell-size); + margin: var(--cell-spacing); + background-color: transparent; + box-sizing: border-box; + border-radius: 4px; + animation: 1.5s ripple ease infinite; +} + +.cell.d-1 { + animation-delay: 100ms; +} + +.cell.d-2 { + animation-delay: 200ms; +} + +.cell.d-3 { + animation-delay: 300ms; +} + +.cell.d-4 { + animation-delay: 400ms; +} + +.cell:nth-child(1) { + --cell-color: #FFF870; + /* tbd-yellow-tint-2 */ +} + +.cell:nth-child(2) { + --cell-color: #FFF53D; + /* tbd-yellow-tint-1 */ +} + +.cell:nth-child(3) { + --cell-color: #FFEC19; + /* tbd-yellow */ +} + +.cell:nth-child(4) { + --cell-color: #FAE100; + /* tbd-yellow-shade-1 */ +} + +.cell:nth-child(5) { + --cell-color: #F5D800; + /* tbd-yellow-shade-2 */ +} + +.cell:nth-child(6) { + --cell-color: #FFF870; + /* tbd-yellow-tint-2 */ +} + +.cell:nth-child(7) { + --cell-color: #FFF53D; + /* tbd-yellow-tint-1 */ +} + +.cell:nth-child(8) { + --cell-color: #FFEC19; + /* tbd-yellow */ +} + +.cell:nth-child(9) { + --cell-color: #FAE100; +} + + + + +/*Animation*/ +@keyframes ripple { + 0% { + background-color: transparent; + } + + 30% { + background-color: var(--cell-color); + } + + 60% { + background-color: transparent; + } + + 100% { + background-color: transparent; + } +} \ No newline at end of file diff --git a/site-new/src/css/custom.css b/site-new/src/css/custom.css index 2ac08468e..a615f7164 100644 --- a/site-new/src/css/custom.css +++ b/site-new/src/css/custom.css @@ -648,7 +648,9 @@ a:has(.footer__logo) { @apply p; } - + button { + @apply font-spaceGrotesk text-xs leading-[100%] font-medium lg:text-lg + } } .sidenav-list-header { diff --git a/site-new/src/pages/events.tsx b/site-new/src/pages/events.tsx new file mode 100644 index 000000000..e7128af02 --- /dev/null +++ b/site-new/src/pages/events.tsx @@ -0,0 +1,201 @@ +import React, { useEffect, useMemo, useState } from "react"; +import { + Calendar, + CalendarProvider, + MonthSwitcher, +} from "../components/Calendar"; +import { format } from "date-fns"; +import { max, min } from "date-fns"; +import Link from "@docusaurus/Link"; +import { cn } from "@site/lib/utils"; +import { Loading } from "../components/Loading"; +import ArrowRight from "@site/assets/icons/ArrowRight"; + +export interface EventType { + start: string; + end: string; + summary: string; + htmlLink: string; + location: string; + description: string; +} + +const createGoogleCalendarLink = (event: EventType) => { + const startTime = new Date(event.start) + .toISOString() + .replace(/-|:|\.\d\d\d/g, ""); + const endTime = new Date(event.end) + .toISOString() + .replace(/-|:|\.\d\d\d/g, ""); + const details = encodeURIComponent( + event.description || "No details provided.", + ); + const location = encodeURIComponent( + event.location || "No location provided.", + ); + const text = encodeURIComponent(event.summary || "No title"); + + return `https://calendar.google.com/calendar/render?action=TEMPLATE&text=${text}&dates=${startTime}/${endTime}&details=${details}&location=${location}`; +}; + +type EventGroup = Record; + +const groupEventsByDate = (events: EventType[]) => { + return events.reduce((acc, event) => { + const date = new Date(event.start).toLocaleDateString(); + if (!acc[date]) { + acc[date] = []; + } + acc[date].push(event); + return acc; + }, {} satisfies EventGroup); +}; + +const lineClampMap = { + 1: "line-clamp-3", + 2: "line-clamp-2", + 3: "line-clamp-1", +}; + +const CalenderSwitcherButton = ({ + children, + onClick, + isActive, +}: { + children: React.ReactNode; + onClick?: React.MouseEventHandler; + isActive?: boolean; +}) => { + return ( + + ); +}; + +const CalendarEvent = ({ + event, + noOfEvents, +}: { + event: EventType; + noOfEvents: number; +}) => { + const startTime = format(event.start, "h.mm a"); + const noOfEventsNormalized = noOfEvents > 3 ? 3 : noOfEvents; + const lineClamp = lineClampMap[noOfEventsNormalized]; + + return ( +
+ + +

+ {event.summary} +

+ +
+ ); +}; + +const getEventsAsync = async () => { + const resp = await fetch( + "https://developer-tbd-website-calendar-service.tbddev.org/events", + ); + const eventsData = (await resp.json()) as EventType[]; + return eventsData; +}; + +const Events = () => { + const [calendarView, setCalendarView] = useState<"monthly" | "daily">( + "monthly", + ); + const [isLoading, setIsLoading] = useState(true); + + const [eventsData, setEventsData] = useState(); + const allDates = useMemo(() => { + if (eventsData) { + return Object.keys(eventsData); + } + return []; + }, [eventsData]); + + useEffect(() => { + getEventsAsync() + .then((eventsData) => { + setEventsData(groupEventsByDate(eventsData)); + }) + .finally(() => { + setIsLoading(false); + }); + }, []); + + return ( +
+ +
+
+ +
+ setCalendarView("monthly")} + isActive={calendarView === "monthly"} + > + Month View + {calendarView === "monthly" && } + + setCalendarView("daily")} + isActive={calendarView === "daily"} + > + {calendarView === "daily" && ( + + )} + Day View + +
+
+ + {isLoading && ( +
+ +
+ )} + {calendarView === "monthly" && !isLoading && ( + { + const date = calendarDate.toLocaleDateString(); + if (eventsData && eventsData[date]) { + return ( +
+ {eventsData[date].map((event) => ( + + ))} +
+ ); + } + }} + /> + )} + +
+ ); +}; + +export default Events; diff --git a/site-new/tailwind.config.js b/site-new/tailwind.config.js index 198e81918..f204a3061 100644 --- a/site-new/tailwind.config.js +++ b/site-new/tailwind.config.js @@ -32,6 +32,9 @@ module.exports = { "col-start-5", "col-start-6", "col-start-7", + "line-clamp-3", + "line-clamp-2", + "line-clamp-1", ], mode: "jit", corePlugins: {