Skip to content

Commit 229b496

Browse files
committed
Course and professor modal
Needed to update Preact to get type support for onClose (thank the gods they fixed it three months ago)
1 parent d1ce005 commit 229b496

File tree

5 files changed

+156
-12
lines changed

5 files changed

+156
-12
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/** @jsxImportSource preact */
2+
/// <reference no-default-lib="true"/>
3+
/// <reference lib="dom" />
4+
/// <reference lib="deno.ns" />
5+
6+
import { useEffect, useRef } from 'preact/hooks'
7+
import { Course } from '../../../scheduleofclasses/group-sections.ts'
8+
import { AbbrevHeading } from '../AbbrevHeading.tsx'
9+
10+
export type ModalView =
11+
| { type: 'course'; course: Course }
12+
| { type: 'professor' }
13+
14+
export type ResultModalProps = {
15+
view: ModalView
16+
open: boolean
17+
onClose: () => void
18+
}
19+
export function ResultModal ({ view, open, onClose }: ResultModalProps) {
20+
const ref = useRef<HTMLDialogElement>(null)
21+
22+
useEffect(() => {
23+
if (ref.current && open) {
24+
ref.current.showModal()
25+
}
26+
}, [open])
27+
28+
return (
29+
<dialog
30+
class='modal'
31+
ref={ref}
32+
onClick={e => {
33+
if (e.target === e.currentTarget) {
34+
e.currentTarget.close()
35+
}
36+
}}
37+
onClose={onClose}
38+
>
39+
<form method='dialog' class='modal-body'>
40+
{view.type === 'course' ? (
41+
<AbbrevHeading
42+
heading='h1'
43+
abbrev={view.course.code}
44+
class='modal-title-course-code'
45+
>
46+
{view.course.title}
47+
</AbbrevHeading>
48+
) : (
49+
'Professor Name'
50+
)}
51+
</form>
52+
</dialog>
53+
)
54+
}

classrooms/components/search/SearchBar.tsx

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
/// <reference lib="deno.ns" />
55

66
import { useState } from 'preact/hooks'
7+
import { Course } from '../../../scheduleofclasses/group-sections.ts'
78
import { termCode } from '../../../terms/index.ts'
9+
import { useLast } from '../../../util/useLast.ts'
810
import { Term, TermCache } from '../../lib/TermCache.ts'
911
import { SearchIcon } from '../icons/SearchIcon.tsx'
10-
import { SearchData, SearchResults } from './SearchResults.tsx'
12+
import { ModalView, ResultModal } from './ResultModal.tsx'
13+
import { SearchData, SearchResults, View } from './SearchResults.tsx'
1114

1215
type State =
1316
| { type: 'unloaded' }
@@ -35,9 +38,15 @@ export function SearchBar ({
3538
}: SearchBarProps) {
3639
const termsId = terms.map(term => termCode(term.year, term.quarter)).join(' ')
3740
const [query, setQuery] = useState('')
38-
const [index, setIndex] = useState<number | null>(null)
41+
const [index, setIndex] = useState(0)
42+
const [showResults, setShowResults] = useState(true)
3943
const [state, setState] = useState<State>({ type: 'unloaded' })
4044
const loaded = state.type === 'loaded' && state.terms === termsId
45+
const [viewing, setViewing] = useState<ModalView | null>(null)
46+
const modalView = useLast<ModalView>(
47+
{ type: 'course', course: { code: '', title: '', groups: [] } },
48+
viewing
49+
)
4150

4251
async function loadTerms (): Promise<void> {
4352
const maybePromise = termCache.requestTerms(terms, true)
@@ -75,9 +84,28 @@ export function SearchBar ({
7584
})
7685
}
7786

87+
function handleView (view: View) {
88+
if (view.type === 'building') {
89+
onBuilding(view.id)
90+
} else if (view.type === 'course') {
91+
if (state.type === 'loaded') {
92+
const course = state.data.courses.find(
93+
course => course.code === view.id
94+
)
95+
if (course) {
96+
setViewing({ type: 'course', course })
97+
}
98+
}
99+
}
100+
}
101+
78102
// TODO: show loading, offline errors with retry button
79103
return (
80-
<div class={`search-wrapper ${visible ? '' : 'hide-search'}`}>
104+
<div
105+
class={`search-wrapper ${visible ? '' : 'hide-search'} ${
106+
loaded && showResults && query !== '' ? 'showing-results' : ''
107+
}`}
108+
>
81109
<label class='search-bar'>
82110
<SearchIcon />
83111
<input
@@ -87,7 +115,8 @@ export function SearchBar ({
87115
value={query}
88116
onInput={e => {
89117
setQuery(e.currentTarget.value)
90-
setIndex(null)
118+
setIndex(0)
119+
setShowResults(true)
91120
}}
92121
onKeyDown={e => {
93122
if (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey) {
@@ -116,6 +145,7 @@ export function SearchBar ({
116145
}
117146
}}
118147
onFocus={() => {
148+
setShowResults(true)
119149
if (
120150
state.type === 'unloaded' ||
121151
(state.type === 'loaded' && state.terms !== termsId)
@@ -125,20 +155,23 @@ export function SearchBar ({
125155
}}
126156
/>
127157
</label>
128-
{loaded && (
158+
{loaded && showResults && (
129159
<SearchResults
130160
terms={terms}
131161
query={query}
132162
data={{ ...state.data, buildings }}
133163
index={index}
134164
onSelect={view => {
135-
if (view.type === 'building') {
136-
onBuilding(view.id)
137-
}
138-
console.log(view)
165+
setShowResults(false)
166+
handleView(view)
139167
}}
140168
/>
141169
)}
170+
<ResultModal
171+
view={modalView}
172+
open={viewing !== null}
173+
onClose={() => setViewing(null)}
174+
/>
142175
</div>
143176
)
144177
}

classrooms/components/search/SearchResults.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
/// <reference lib="deno.ns" />
55

66
import { useMemo } from 'preact/hooks'
7+
import { Course } from '../../../scheduleofclasses/group-sections.ts'
78
import { termName } from '../../../terms/index.ts'
89
import { buildings } from '../../lib/buildings.ts'
910
import { Term } from '../../lib/TermCache.ts'
1011
import { SearchResult } from './SearchResult.tsx'
1112

1213
export type SearchData = {
13-
courses: { code: string; title: string }[]
14+
courses: Course[]
1415
professors: { first: string; last: string }[]
1516
buildings: string[]
1617
}

classrooms/public/styles.css

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,12 @@ input[type='checkbox']:checked {
956956
right: 0;
957957
width: unset;
958958
margin: 10px;
959+
max-height: calc(100% - 20px);
960+
}
961+
.showing-results {
962+
margin: 0;
963+
padding: 10px;
964+
border-radius: 0;
959965
}
960966
}
961967
.hide-search {
@@ -1023,6 +1029,56 @@ input[type='checkbox']:checked {
10231029
color: white;
10241030
}
10251031

1032+
.modal {
1033+
display: block;
1034+
visibility: hidden;
1035+
border: none;
1036+
padding: 0;
1037+
position: fixed;
1038+
top: 0;
1039+
left: 0;
1040+
width: 100%;
1041+
height: 100%;
1042+
box-sizing: border-box;
1043+
padding: 40px;
1044+
display: flex;
1045+
justify-content: center;
1046+
align-items: center;
1047+
background-color: rgba(0, 0, 0, 0.5);
1048+
opacity: 0;
1049+
pointer-events: none;
1050+
user-select: none;
1051+
transition: visibility 0.5s, opacity 0.5s;
1052+
}
1053+
.modal[open] {
1054+
visibility: visible;
1055+
opacity: 1;
1056+
pointer-events: unset;
1057+
user-select: text;
1058+
}
1059+
.modal:modal {
1060+
max-width: unset;
1061+
max-height: unset;
1062+
}
1063+
.modal::backdrop {
1064+
background: none;
1065+
}
1066+
.modal-body {
1067+
background-color: var(--modal);
1068+
box-shadow: var(--modal-shadow);
1069+
border-radius: 20px;
1070+
padding: 20px;
1071+
transform: scale(0.9);
1072+
transition: transform 0.5s;
1073+
}
1074+
.modal[open] .modal-body {
1075+
transform: none;
1076+
}
1077+
.modal-title-course-code {
1078+
font-size: 120px;
1079+
margin: 0;
1080+
}
1081+
10261082
.college-revelle {
10271083
background-image: linear-gradient(120deg, #2045c5, #273b80);
10281084
color: white;

deno.jsonc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"imports": {
33
"deno_dom/": "https://deno.land/x/[email protected]/",
4-
"preact": "https://esm.sh/preact@10.13.1",
5-
"preact/": "https://esm.sh/preact@10.13.1/",
4+
"preact": "https://esm.sh/preact@10.17.1",
5+
"preact/": "https://esm.sh/preact@10.17.1/",
66
"std/": "https://deno.land/[email protected]/"
77
},
88
"tasks": {

0 commit comments

Comments
 (0)