From aa3fa14c5b6b415c6b6b8b7af27a800a52d8e3af Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2026 03:23:14 +0000 Subject: [PATCH] Replace flatpickr and react-select with native/MUI inputs, bump auto-animate Co-Authored-By: Rahul Chalamala <22563365+rchalamala@users.noreply.github.com> --- README.md | 4 ++ package-lock.json | 95 ++------------------------------------------- package.json | 5 +-- src/Planner.tsx | 56 +++++++++++++++++--------- src/Workspace.tsx | 82 +++++++++++++++++--------------------- src/css/planner.css | 13 +++++-- 6 files changed, 93 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index f6d627c..cf0f03e 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,7 @@ Made with ❤️ by [Rahul](https://github.com/rchalamala/), [Eric](https://gith In addition, thanks to [Armeet](https://github.com/armeetjatyani/) and others for suggestions/contributions! Favicon art by Audrey Wong. + +## Dependency notes + +- `preact` and `@preact/signals` are never imported in `src/` directly — they are runtime peer dependencies of the Schedule-X calendar (`@schedule-x/*`) and must not be removed. diff --git a/package-lock.json b/package-lock.json index 71985e6..aadf5d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@formkit/auto-animate": "^0.8.2", + "@formkit/auto-animate": "^0.9.0", "@hello-pangea/dnd": "^18.0.1", "@mui/icons-material": "^9.1.0", "@mui/material": "^9.1.0", @@ -19,7 +19,6 @@ "@schedule-x/events-service": "^4.6.0", "@schedule-x/react": "^4.1.0", "@schedule-x/theme-default": "^4.6.0", - "flatpickr": "^4.6.13", "fzf": "^0.5.1", "ics": "^3.12.0", "lz-string": "^1.5.0", @@ -27,8 +26,6 @@ "preact": "^10.29.2", "react": "^19.2.7", "react-dom": "^19.2.7", - "react-flatpickr": "^4.0.11", - "react-select": "^5.9.0", "temporal-polyfill": "^0.3.0", "usehooks-ts": "^3.1.1" }, @@ -1049,35 +1046,10 @@ "node": ">=18" } }, - "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", - "license": "MIT" - }, "node_modules/@formkit/auto-animate": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.4.tgz", - "integrity": "sha512-DHHC01EJ1p70Q0z/ZFRBIY8NDnmfKccQoyoM84Tgb6omLMat6jivCdf272Y8k3nf4Lzdin/Y4R9q8uFtU0GbnA==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.9.0.tgz", + "integrity": "sha512-VhP4zEAacXS3dfTpJpJ88QdLqMTcabMg0jwpOSxZ/VzfQVfl3GkZSCZThhGC5uhq/TxPHPzW0dzr4H9Bb1OgKA==", "license": "MIT" }, "node_modules/@hello-pangea/dnd": { @@ -4248,12 +4220,6 @@ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", "license": "MIT" }, - "node_modules/flatpickr": { - "version": "4.6.13", - "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", - "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==", - "license": "MIT" - }, "node_modules/framer-motion": { "version": "12.40.0", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.40.0.tgz", @@ -5149,18 +5115,6 @@ "react": "^19.2.7" } }, - "node_modules/react-flatpickr": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/react-flatpickr/-/react-flatpickr-4.0.11.tgz", - "integrity": "sha512-S0TAu8eUeJgN8lZ0Efi41WGPM/tPPUvrveXQ8CAUO60+7TTrF5Ehyv8fvPVagKCqJahY7hXYERM7XfH2hSAWdg==", - "license": "MIT", - "dependencies": { - "flatpickr": "^4.6.13" - }, - "peerDependencies": { - "react": ">= 16 <= 19" - } - }, "node_modules/react-is": { "version": "19.2.7", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", @@ -5190,33 +5144,6 @@ } } }, - "node_modules/react-select": { - "version": "5.10.2", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.2.tgz", - "integrity": "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.0", - "@emotion/cache": "^11.4.0", - "@emotion/react": "^11.8.1", - "@floating-ui/dom": "^1.0.1", - "@types/react-transition-group": "^4.4.0", - "memoize-one": "^6.0.0", - "prop-types": "^15.6.0", - "react-transition-group": "^4.3.0", - "use-isomorphic-layout-effect": "^1.2.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-select/node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", - "license": "MIT" - }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -5651,20 +5578,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/use-isomorphic-layout-effect": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", - "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", diff --git a/package.json b/package.json index bdd0aba..286a3d5 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", - "@formkit/auto-animate": "^0.8.2", + "@formkit/auto-animate": "^0.9.0", "@hello-pangea/dnd": "^18.0.1", "@mui/icons-material": "^9.1.0", "@mui/material": "^9.1.0", @@ -14,7 +14,6 @@ "@schedule-x/events-service": "^4.6.0", "@schedule-x/react": "^4.1.0", "@schedule-x/theme-default": "^4.6.0", - "flatpickr": "^4.6.13", "fzf": "^0.5.1", "ics": "^3.12.0", "lz-string": "^1.5.0", @@ -22,8 +21,6 @@ "preact": "^10.29.2", "react": "^19.2.7", "react-dom": "^19.2.7", - "react-flatpickr": "^4.0.11", - "react-select": "^5.9.0", "temporal-polyfill": "^0.3.0", "usehooks-ts": "^3.1.1" }, diff --git a/src/Planner.tsx b/src/Planner.tsx index 20297ac..f56d6ee 100644 --- a/src/Planner.tsx +++ b/src/Planner.tsx @@ -6,15 +6,45 @@ import { createViewWeek, CalendarConfig } from "@schedule-x/calendar"; import { createEventsServicePlugin } from "@schedule-x/events-service"; import { useCalendarApp, ScheduleXCalendar } from "@schedule-x/react"; import { Temporal } from "temporal-polyfill"; -import Flatpickr from "react-flatpickr"; import "@schedule-x/theme-default/dist/index.css"; -import "flatpickr/dist/themes/airbnb.css"; import "./css/planner.css"; const hasWeekendCourse = false; +function formatTime(date: Date): string { + return `${String(date.getHours()).padStart(2, "0")}:${String( + date.getMinutes(), + ).padStart(2, "0")}`; +} + +function TimeInput({ + value, + onChange, + label, +}: { + value: Date; + onChange: (day: Date) => void; + label: string; +}) { + return ( + { + if (!e.target.value) return; + const [hours, minutes] = e.target.value.split(":").map(Number); + const day = new Date(value); + day.setHours(hours, minutes, 0, 0); + onChange(day); + }} + /> + ); +} + function CourseToDates(courses: CourseStorage[]): DateData[] { const dates: DateData[] = []; @@ -162,27 +192,17 @@ function Planner() { className={`flex min-w-0 flex-col items-center gap-y-2 px-0.5 ${idx === 0 ? "col-start-2" : ""}`} key={idx} > - { + onChange={(day) => { state.updateAvailableTimes(idx, true, day); }} /> - { + onChange={(day) => { state.updateAvailableTimes(idx, false, day); }} /> diff --git a/src/Workspace.tsx b/src/Workspace.tsx index 4e0f54b..24cb953 100644 --- a/src/Workspace.tsx +++ b/src/Workspace.tsx @@ -1,7 +1,5 @@ import { use, useState } from "react"; import Modal, { useModal } from "./Modal"; -import Select from "react-select"; -import { SingleValue } from "react-select"; import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd"; import { Fzf } from "fzf"; import { createEvents } from "ics"; @@ -22,7 +20,13 @@ import { decompressFromEncodedURIComponent, } from "lz-string"; -import { Collapse, IconButton, Switch } from "@mui/material"; +import { + Autocomplete, + Collapse, + IconButton, + Switch, + TextField, +} from "@mui/material"; import { UnfoldLess, UnfoldMore } from "@mui/icons-material"; import { useAutoAnimate } from "@formkit/auto-animate/react"; import TERM_START_DATES from "./data/term_start_dates.json"; @@ -193,9 +197,9 @@ function SectionDropdown(props: { course: CourseStorage }) { const course = props.course; const state = use(AppState); - const onChange = (newSection: SingleValue>) => { + const onChange = (newSection: Maybe) => { course.sectionId = - newSection !== null + newSection != null ? course.courseData.sections.findIndex( (s) => s.number === newSection.number, ) @@ -206,27 +210,24 @@ function SectionDropdown(props: { course: CourseStorage }) { return (
- `${course?.number} - ${course?.name}`} - onChange={handleSelect} - isOptionSelected={(course) => course.id === selectedCourse?.id} - onInputChange={sortCourses} - filterOption={() => { - return true; - }} + getOptionLabel={(course) => `${course.number} - ${course.name}`} + isOptionEqualToValue={(option, selected) => option.id === selected.id} + onChange={(_event, courseData) => handleSelect(courseData)} + filterOptions={(allOptions, { inputValue }) => + inputValue ? fzf.find(inputValue).map((item) => item.item) : allOptions + } + renderInput={(params) => ( + + )} /> ); } diff --git a/src/css/planner.css b/src/css/planner.css index bee5884..56bc45d 100644 --- a/src/css/planner.css +++ b/src/css/planner.css @@ -1,4 +1,4 @@ -/* third-party overrides: Schedule-X and flatpickr */ +/* third-party overrides: Schedule-X */ /* the schedule is a single fixed week, so hide navigation chrome */ .sx__calendar-header { @@ -12,11 +12,18 @@ display: none; } -.flatpickr-input { +.planner-time-input { border: 1px solid lightgrey; border-radius: 4px; width: 100%; min-width: 0; - max-width: 4rem; + max-width: 4.5rem; text-align: center; + background-color: white; + font-size: 0.875rem; + line-height: 1.5; +} + +.planner-time-input::-webkit-calendar-picker-indicator { + display: none; }