Intermediate prototype state
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import { Icon } from './Icon.jsx'
|
||||
|
||||
export const ToolOverlay = ({ visibility, toggleVisibility }) => (
|
||||
<div class="overlay">
|
||||
<button class="icon primary" onClick={toggleVisibility}>
|
||||
<Icon name={visibility ? 'visibility' : 'visibility_off'} />
|
||||
</button>
|
||||
<button class="icon">
|
||||
<Icon name="close" />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
@@ -124,8 +124,15 @@ const WorkWeekView = ({ events }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const WorkWeekVerticalView = ({ events }) => {
|
||||
const eventsByWeekday = _.groupBy(events, event => event.start.getDay())
|
||||
const WorkWeekVerticalView = ({ events, selection, setSelection, hideOtherCourses }) => {
|
||||
const selectionSet = new Set(selection)
|
||||
|
||||
console.log(hideOtherCourses)
|
||||
|
||||
const eventsByWeekday = _.groupBy(
|
||||
!hideOtherCourses ? events : events.filter(e => selectionSet.has(e.name)),
|
||||
event => event.start.getDay()
|
||||
)
|
||||
|
||||
const dayIntervalLayout = _.mapValues(eventsByWeekday, events =>
|
||||
layoutIntervals(
|
||||
@@ -216,7 +223,8 @@ const WorkWeekVerticalView = ({ events }) => {
|
||||
'event' +
|
||||
(currentlyHovered === event.data.name
|
||||
? ' highlight'
|
||||
: '')
|
||||
: '') +
|
||||
(selectionSet.has(event.data.name) ? ' selected' : '')
|
||||
}
|
||||
data-event-id={event.data.name}
|
||||
style={{
|
||||
@@ -228,6 +236,16 @@ const WorkWeekVerticalView = ({ events }) => {
|
||||
360) +
|
||||
'deg',
|
||||
}}
|
||||
onClick={() => {
|
||||
if (!selectionSet.has(event.data.name))
|
||||
setSelection([...selection, event.data.name])
|
||||
else
|
||||
setSelection(
|
||||
selection.filter(
|
||||
name => event.data.name !== name
|
||||
)
|
||||
)
|
||||
}}
|
||||
>
|
||||
<div class="title">
|
||||
{_.startCase(_.lowerCase(event.data.name))
|
||||
@@ -255,13 +273,48 @@ const WorkWeekVerticalView = ({ events }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const CourseView = ({ events }) => {
|
||||
const CourseView = ({ events, selection, setSelection }) => {
|
||||
const eventsByCourse = _.groupBy(events, 'name')
|
||||
const selectionSet = new Set(selection)
|
||||
|
||||
const [currentlyHovered, setCurrentlyHovered] = useState(null)
|
||||
const element = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
if (element.current) {
|
||||
const l = e => {
|
||||
const $course = e.target.closest('.course')
|
||||
if ($course) {
|
||||
setCurrentlyHovered($course.dataset.courseId)
|
||||
} else {
|
||||
setCurrentlyHovered(null)
|
||||
}
|
||||
}
|
||||
|
||||
element.current.addEventListener('mousemove', l)
|
||||
|
||||
return () => {
|
||||
element.current.removeEventListener('mousemove', l)
|
||||
}
|
||||
}
|
||||
}, [element.current])
|
||||
|
||||
return (
|
||||
<div class="course-view">
|
||||
<div class="course-view" ref={element}>
|
||||
<div class="wrap-container">
|
||||
{Object.entries(eventsByCourse).map(([name, courseEvents]) => (
|
||||
<div class="course">
|
||||
<div
|
||||
class={
|
||||
'course' +
|
||||
(currentlyHovered === name ? ' highlight' : '') +
|
||||
(selectionSet.has(name) ? ' selected' : '')
|
||||
}
|
||||
data-course-id={name}
|
||||
onClick={() => {
|
||||
if (!selectionSet.has(name)) setSelection([...selection, name])
|
||||
else setSelection(selection.filter(name => name !== name))
|
||||
}}
|
||||
>
|
||||
<div class="title">
|
||||
{_.startCase(_.lowerCase(name))
|
||||
.replaceAll('Ii', 'II')
|
||||
@@ -271,7 +324,7 @@ const CourseView = ({ events }) => {
|
||||
<div class="docenti">{courseEvents[0].docenti.join(', ')}</div>
|
||||
<div class="events">
|
||||
{courseEvents.map(course => (
|
||||
<div class="event">
|
||||
<div>
|
||||
{_.capitalize(format(course.start, 'EEEE', { locale: it }))}{' '}
|
||||
{format(course.start, 'H:mm')} –
|
||||
{format(course.end, 'H:mm')} {course.aula}
|
||||
@@ -298,9 +351,11 @@ const viewModeMap = {
|
||||
export const EventsView = ({ mode, ...viewProps }) => {
|
||||
const Mode = viewModeMap[mode]
|
||||
|
||||
const [selectedCourses, setSelectedCourses] = useState([])
|
||||
|
||||
return (
|
||||
<div class="events-view">
|
||||
<Mode {...viewProps} />
|
||||
<Mode selection={selectedCourses} setSelection={setSelectedCourses} {...viewProps} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,11 +27,6 @@ export const Toolbar = ({ mode, setMode }) => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item option">
|
||||
<button class="icon">
|
||||
<Icon name="filter_list" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="item option">
|
||||
<button class="icon">
|
||||
<Icon name="print" />
|
||||
|
||||
+11
-1
@@ -1,6 +1,7 @@
|
||||
import _ from 'lodash'
|
||||
import { render } from 'preact'
|
||||
import { useEffect, useState } from 'preact/hooks'
|
||||
import { ToolOverlay } from './components/CourseVisibility.jsx'
|
||||
|
||||
import { EventsView } from './components/EventsView.jsx'
|
||||
import { Toolbar } from './components/Toolbar.jsx'
|
||||
@@ -38,13 +39,18 @@ const App = ({}) => {
|
||||
})
|
||||
}, [])
|
||||
|
||||
const [mode, setMode] = useState('work-week-v')
|
||||
const [mode, setMode] = useState('course')
|
||||
|
||||
const [hideOtherCourses, setHideOtherCourses] = useState(false)
|
||||
|
||||
console.log(hideOtherCourses)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toolbar {...{ mode, setMode }} />
|
||||
<EventsView
|
||||
mode={mode}
|
||||
hideOtherCourses={hideOtherCourses}
|
||||
start={new Date(2022, 10, 3)}
|
||||
events={eventi.map(({ nome, dataInizio, dataFine, docenti, aule }) => ({
|
||||
name: _.split(nome, '-', 1)[0].trim(),
|
||||
@@ -59,6 +65,10 @@ const App = ({}) => {
|
||||
),
|
||||
}))}
|
||||
/>
|
||||
<ToolOverlay
|
||||
visibility={hideOtherCourses}
|
||||
toggleVisibility={() => setHideOtherCourses(s => !s)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
+92
-7
@@ -20,15 +20,21 @@ html {
|
||||
--gray-350: #e8e8e8;
|
||||
--gray-500: #d8d8d8;
|
||||
--gray-600: #c0c0c0;
|
||||
--gray-700: #b0b0b0;
|
||||
--gray-800: #909090;
|
||||
--gray-900: #282828;
|
||||
|
||||
// --accent-100: #e6ceff;
|
||||
// --accent-400: #d0b8ef;
|
||||
// --accent-500: #aa82db;
|
||||
|
||||
--accent-100: hsl(221, 100%, 88%);
|
||||
--accent-400: hsl(221, 75%, 78%);
|
||||
--accent-500: hsl(221, 70%, 75%);
|
||||
// --accent-100: hsl(221, 100%, 88%);
|
||||
// --accent-400: hsl(221, 75%, 78%);
|
||||
// --accent-500: hsl(221, 70%, 75%);
|
||||
|
||||
--accent-100: #d6ffc2;
|
||||
--accent-400: #6cc16c;
|
||||
--accent-500: #5aa05a;
|
||||
}
|
||||
|
||||
$device-s-width: 480px;
|
||||
@@ -62,12 +68,25 @@ button,
|
||||
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
&:hover::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
||||
inset: 0;
|
||||
background: #00000008;
|
||||
background: #0000000c;
|
||||
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&.primary {
|
||||
background: var(--accent-100);
|
||||
border-color: var(--accent-400);
|
||||
|
||||
&:hover::after {
|
||||
background: #00000020;
|
||||
}
|
||||
}
|
||||
|
||||
&.icon {
|
||||
@@ -85,7 +104,8 @@ button,
|
||||
border-radius: 0;
|
||||
border-right: none;
|
||||
|
||||
padding: inherit 0.5rem;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 1rem;
|
||||
@@ -110,6 +130,10 @@ button,
|
||||
& + .radio {
|
||||
border-left-color: var(--accent-400);
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
background: #00000018;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,6 +174,10 @@ body {
|
||||
align-items: center;
|
||||
|
||||
gap: 0.5rem;
|
||||
|
||||
@media screen and (max-width: $device-s-width) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +213,26 @@ body {
|
||||
|
||||
flex-grow: 1;
|
||||
|
||||
position: relative;
|
||||
|
||||
&.selected {
|
||||
background: var(--gray-100);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
border-radius: 1rem;
|
||||
|
||||
border: 3px solid var(--gray-800);
|
||||
}
|
||||
}
|
||||
|
||||
&.highlight {
|
||||
background: var(--gray-300);
|
||||
border-color: var(--gray-700);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
}
|
||||
@@ -474,6 +522,8 @@ body {
|
||||
gap: 1rem 0.5rem;
|
||||
|
||||
.event {
|
||||
position: relative;
|
||||
|
||||
grid-row: var(--start) / span var(--size);
|
||||
grid-column: var(--stack) / span 1;
|
||||
|
||||
@@ -491,9 +541,22 @@ body {
|
||||
|
||||
padding: 0.5rem 1rem 0.5rem 0.5rem;
|
||||
|
||||
&.highlight {
|
||||
&.selected {
|
||||
background: var(--gray-100);
|
||||
border-color: var(--gray-500);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
border: 3px solid var(--gray-800);
|
||||
}
|
||||
}
|
||||
|
||||
&.highlight {
|
||||
background: var(--gray-300);
|
||||
border-color: var(--gray-700);
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -527,6 +590,28 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.overlay {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
|
||||
bottom: 3rem;
|
||||
right: 3rem;
|
||||
|
||||
gap: 0.5rem;
|
||||
|
||||
button {
|
||||
padding: 0.8rem;
|
||||
|
||||
border-radius: 100%;
|
||||
|
||||
.material-symbols-outlined {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
box-shadow: 0 0.25rem 0.75rem #00000033;
|
||||
}
|
||||
}
|
||||
|
||||
// Utilities
|
||||
|
||||
span.bold {
|
||||
|
||||
Reference in New Issue
Block a user