Skip to content

Commit

Permalink
Commit new files
Browse files Browse the repository at this point in the history
Oops
  • Loading branch information
psvenk committed Dec 10, 2024
1 parent c3b6c91 commit 8cc9add
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 0 deletions.
35 changes: 35 additions & 0 deletions src/components/PreregLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Activity } from "../lib/activity";
import { Class } from "../lib/class";
import { LuClipboardList, LuExternalLink } from "react-icons/lu";

import { Tooltip } from "./ui/tooltip";

Check failure on line 5 in src/components/PreregLink.tsx

View workflow job for this annotation

GitHub Actions / Build

'Tooltip' is defined but never used. Allowed unused vars must match /^_/u
import { LinkButton } from "./ui/link-button";

/** A link to SIPB Matrix's class group chat importer UI */
export function PreregLink(props: { selectedActivities: Array<Activity> }) {
const { selectedActivities } = props;

// reference: https://github.com/gabrc52/class_group_chats/tree/main/src/routes/import
const matrixLink = `https://student.mit.edu/cgi-bin/sfprwtrm.sh?${(
selectedActivities.filter(
(activity) => activity instanceof Class,
) as Class[]
)
.map((cls) => cls.number)
.join(",")}`;

return (
<LinkButton
colorPalette="red"
size="sm"
href={matrixLink}
target="_blank"
rel="noreferrer"
fontWeight={"semibold"}
>
<LuClipboardList />
Pre-register for these classes
<LuExternalLink />
</LinkButton>
);
}
99 changes: 99 additions & 0 deletions src/components/TermSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import {
SelectContent,
SelectItem,
SelectLabel,
SelectRoot,
SelectTrigger,
SelectValueText,
} from "./ui/select";

import { createListCollection } from "@chakra-ui/react";

import { Term } from "../lib/dates";

/** Given a urlName like i22, return its corresponding URL. */
function toFullUrl(urlName: string, latestUrlName: string): string {
const url = new URL(window.location.href);
Array.from(url.searchParams.keys()).forEach((key) => {
url.searchParams.delete(key);
});
if (urlName !== latestUrlName) {
url.searchParams.set("t", urlName);
}
return url.href;
}

/** Given a urlName like "i22", return the previous one, "f21". */
function getLastUrlName(urlName: string): string {
const { semester, year } = new Term({ urlName });
if (semester === "f") {
return `s${year}`;
} else if (semester === "s") {
return `i${year}`;
} else {
return `f${parseInt(year, 10) - 1}`;
}
}

/** urlNames that don't have a State */
const EXCLUDED_URLS = ["i23", "i24", "i25"];

/** Earliest urlName we have a State for. */
const EARLIEST_URL = "f22";

/** Return all urlNames before the given one. */
function getUrlNames(latestTerm: string): Array<string> {
let urlName = latestTerm;
const res = [];
while (urlName !== EARLIEST_URL) {
res.push(urlName);
do {
urlName = getLastUrlName(urlName);
} while (EXCLUDED_URLS.includes(urlName));
}
res.push(EARLIEST_URL);
return res;
}

export function TermSwitcher(props: { state: State }) {
const { state } = props;
const toUrl = (urlName: string) =>
toFullUrl(urlName, state.latestTerm.urlName);
const defaultValue = toUrl(state.term.urlName);

return (
<SelectRoot
collection={createListCollection({
items: getUrlNames(state.latestTerm.urlName).map((urlName) => {
const { niceName } = new Term({ urlName });
return {
label: niceName,
value: toUrl(urlName),
};
}),
})}
value={[defaultValue]}
onValueChange={(e) => {
window.location.href = e.value[0];
}}
size="sm"
w="8rem"
mr={3}
>
<SelectLabel hidden>Select semester</SelectLabel>
<SelectTrigger>
<SelectValueText />
</SelectTrigger>
<SelectContent>
{getUrlNames(state.latestTerm.urlName).map((urlName) => {
const { niceName } = new Term({ urlName });
return (
<SelectItem item={toUrl(urlName)} key={toUrl(urlName)}>
{niceName}
</SelectItem>
);
})}
</SelectContent>
</SelectRoot>
);
}
110 changes: 110 additions & 0 deletions src/components/ui/menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"use client"

import { AbsoluteCenter, Menu as ChakraMenu, Portal } from "@chakra-ui/react"
import * as React from "react"
import { LuCheck, LuChevronRight } from "react-icons/lu"

interface MenuContentProps extends ChakraMenu.ContentProps {
portalled?: boolean
portalRef?: React.RefObject<HTMLElement>
}

export const MenuContent = React.forwardRef<HTMLDivElement, MenuContentProps>(
function MenuContent(props, ref) {
const { portalled = true, portalRef, ...rest } = props
return (
<Portal disabled={!portalled} container={portalRef}>
<ChakraMenu.Positioner>
<ChakraMenu.Content ref={ref} {...rest} />
</ChakraMenu.Positioner>
</Portal>
)
},
)

export const MenuArrow = React.forwardRef<
HTMLDivElement,
ChakraMenu.ArrowProps
>(function MenuArrow(props, ref) {
return (
<ChakraMenu.Arrow ref={ref} {...props}>
<ChakraMenu.ArrowTip />
</ChakraMenu.Arrow>
)
})

export const MenuCheckboxItem = React.forwardRef<
HTMLDivElement,
ChakraMenu.CheckboxItemProps
>(function MenuCheckboxItem(props, ref) {
return (
<ChakraMenu.CheckboxItem ref={ref} {...props}>
<ChakraMenu.ItemIndicator hidden={false}>
<LuCheck />
</ChakraMenu.ItemIndicator>
{props.children}
</ChakraMenu.CheckboxItem>
)
})

export const MenuRadioItem = React.forwardRef<
HTMLDivElement,
ChakraMenu.RadioItemProps
>(function MenuRadioItem(props, ref) {
const { children, ...rest } = props
return (
<ChakraMenu.RadioItem ps="8" ref={ref} {...rest}>
<AbsoluteCenter axis="horizontal" left="4" asChild>
<ChakraMenu.ItemIndicator>
<LuCheck />
</ChakraMenu.ItemIndicator>
</AbsoluteCenter>
<ChakraMenu.ItemText>{children}</ChakraMenu.ItemText>
</ChakraMenu.RadioItem>
)
})

export const MenuItemGroup = React.forwardRef<
HTMLDivElement,
ChakraMenu.ItemGroupProps
>(function MenuItemGroup(props, ref) {
const { title, children, ...rest } = props
return (
<ChakraMenu.ItemGroup ref={ref} {...rest}>
{title && (
<ChakraMenu.ItemGroupLabel userSelect="none">
{title}
</ChakraMenu.ItemGroupLabel>
)}
{children}
</ChakraMenu.ItemGroup>
)
})

export interface MenuTriggerItemProps extends ChakraMenu.ItemProps {
startIcon?: React.ReactNode
}

export const MenuTriggerItem = React.forwardRef<
HTMLDivElement,
MenuTriggerItemProps
>(function MenuTriggerItem(props, ref) {
const { startIcon, children, ...rest } = props
return (
<ChakraMenu.TriggerItem ref={ref} {...rest}>
{startIcon}
{children}
<LuChevronRight />
</ChakraMenu.TriggerItem>
)
})

export const MenuRadioItemGroup = ChakraMenu.RadioItemGroup
export const MenuContextTrigger = ChakraMenu.ContextTrigger
export const MenuRoot = ChakraMenu.Root
export const MenuSeparator = ChakraMenu.Separator

export const MenuItem = ChakraMenu.Item
export const MenuItemText = ChakraMenu.ItemText
export const MenuItemCommand = ChakraMenu.ItemCommand
export const MenuTrigger = ChakraMenu.Trigger

0 comments on commit 8cc9add

Please sign in to comment.