You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
orario/src/components/view/WorkWeek.jsx

151 lines
6.1 KiB
JavaScript

import { useEffect, useRef, useState } from 'preact/hooks'
import _ from 'lodash'
import { differenceInMinutes, startOfDay } from 'date-fns'
import { hashString, normalizeCourseName, WEEK_DAYS } from '../../utils.jsx'
import { layoutIntervals } from '../../interval-layout.js'
export const WorkWeek = ({ events, selection, setSelection, hideOtherCourses }) => {
const selectionSet = new Set(selection)
const eventsByWeekday = _.groupBy(
!hideOtherCourses ? events : events.filter(e => selectionSet.has(e.id)),
event => event.start.getDay()
)
// const dayIntervalLayout = _.mapValues(Object.assign(base, eventsByWeekday), events =>
const dayIntervalLayout = _.mapValues(eventsByWeekday, events =>
layoutIntervals(
events.map(e => ({
start: differenceInMinutes(e.start, startOfDay(e.start)),
end: differenceInMinutes(e.end, startOfDay(e.start)),
data: e,
}))
)
)
const [currentlyHovered, setCurrentlyHovered] = useState(null)
const element = useRef()
useEffect(() => {
if (element.current) {
const l = e => {
const $event = e.target.closest('.event')
if ($event) {
setCurrentlyHovered($event.dataset.eventId)
} else {
setCurrentlyHovered(null)
}
}
element.current.addEventListener('mousemove', l)
element.current.addEventListener('mouseleave', l)
return () => {
element.current.removeEventListener('mousemove', l)
element.current.removeEventListener('mouseleave', l)
}
}
}, [element.current])
return (
<div class="work-week-v-view" ref={element}>
<div class="pivot"></div>
<div class="left-header">
<div class="blocks">
<div
class="block skip-border"
style={{
'--start': 9 - 7,
'--size': 2,
}}
>
9:00 &ndash; 11:00
</div>
<div
class="block"
style={{
'--start': 11 - 7,
'--size': 2,
}}
>
11:00 &ndash; 13:00
</div>
<div
class="block skip-border"
style={{
'--start': 14 - 7,
'--size': 2,
}}
>
14:00 &ndash; 16:00
</div>
<div
class="block"
style={{
'--start': 16 - 7,
'--size': 2,
}}
>
16:00 &ndash; 18:00
</div>
</div>
</div>
{Object.entries(dayIntervalLayout).map(([index, layout]) => (
<div class="day" style={{ '--size': Math.max(1, layout.length) }}>
<div class="top-header">{WEEK_DAYS[parseInt(index)]}</div>
<div class="events">
{layout.map((events, stackIndex) => (
<>
{events.map(event => (
<div
class={
'event' +
(currentlyHovered === event.data.id
? ' highlight'
: '') +
(selectionSet.has(event.data.id) ? ' selected' : '')
}
data-event-id={event.data.id}
style={{
'--start': event.start / 60 - 7,
'--stack': stackIndex + 1,
'--size': (event.end - event.start) / 60,
'--hue':
(Math.abs(hashString('seed3' + event.data.id)) %
360) +
'deg',
}}
onClick={() => {
if (!selectionSet.has(event.data.id))
setSelection([...selection, event.data.id])
else
setSelection(
selection.filter(id => event.data.id !== id)
)
}}
>
<div class="title">
{normalizeCourseName(event.data.name)}
</div>
<div class="aula">{event.data.aula}</div>
</div>
))}
</>
))}
{/* Grid Tracks */}
{[1, 3, 5].map(i => (
<div class="grid-line-h" style={{ '--track': i }}></div>
))}
{[6, 8, 10].map(i => (
<div class="grid-line-h" style={{ '--track': i }}></div>
))}
</div>
</div>
))}
</div>
)
}