Intermediate prototype state

pull/1/head
Antonio De Lucreziis 2 years ago
parent 45cd07fa85
commit 5eac4e7d5a

@ -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 WorkWeekVerticalView = ({ events, selection, setSelection, hideOtherCourses }) => {
const eventsByWeekday = _.groupBy(events, event => event.start.getDay()) 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 => const dayIntervalLayout = _.mapValues(eventsByWeekday, events =>
layoutIntervals( layoutIntervals(
@ -216,7 +223,8 @@ const WorkWeekVerticalView = ({ events }) => {
'event' + 'event' +
(currentlyHovered === event.data.name (currentlyHovered === event.data.name
? ' highlight' ? ' highlight'
: '') : '') +
(selectionSet.has(event.data.name) ? ' selected' : '')
} }
data-event-id={event.data.name} data-event-id={event.data.name}
style={{ style={{
@ -228,6 +236,16 @@ const WorkWeekVerticalView = ({ events }) => {
360) + 360) +
'deg', '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"> <div class="title">
{_.startCase(_.lowerCase(event.data.name)) {_.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 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 ( return (
<div class="course-view"> <div class="course-view" ref={element}>
<div class="wrap-container"> <div class="wrap-container">
{Object.entries(eventsByCourse).map(([name, courseEvents]) => ( {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"> <div class="title">
{_.startCase(_.lowerCase(name)) {_.startCase(_.lowerCase(name))
.replaceAll('Ii', 'II') .replaceAll('Ii', 'II')
@ -271,7 +324,7 @@ const CourseView = ({ events }) => {
<div class="docenti">{courseEvents[0].docenti.join(', ')}</div> <div class="docenti">{courseEvents[0].docenti.join(', ')}</div>
<div class="events"> <div class="events">
{courseEvents.map(course => ( {courseEvents.map(course => (
<div class="event"> <div>
{_.capitalize(format(course.start, 'EEEE', { locale: it }))}{' '} {_.capitalize(format(course.start, 'EEEE', { locale: it }))}{' '}
{format(course.start, 'H:mm')} &ndash; {format(course.start, 'H:mm')} &ndash;
{format(course.end, 'H:mm')} {course.aula} {format(course.end, 'H:mm')} {course.aula}
@ -298,9 +351,11 @@ const viewModeMap = {
export const EventsView = ({ mode, ...viewProps }) => { export const EventsView = ({ mode, ...viewProps }) => {
const Mode = viewModeMap[mode] const Mode = viewModeMap[mode]
const [selectedCourses, setSelectedCourses] = useState([])
return ( return (
<div class="events-view"> <div class="events-view">
<Mode {...viewProps} /> <Mode selection={selectedCourses} setSelection={setSelectedCourses} {...viewProps} />
</div> </div>
) )
} }

@ -27,11 +27,6 @@ export const Toolbar = ({ mode, setMode }) => {
</button> </button>
</div> </div>
</div> </div>
<div class="item option">
<button class="icon">
<Icon name="filter_list" />
</button>
</div>
<div class="item option"> <div class="item option">
<button class="icon"> <button class="icon">
<Icon name="print" /> <Icon name="print" />

@ -1,6 +1,7 @@
import _ from 'lodash' import _ from 'lodash'
import { render } from 'preact' import { render } from 'preact'
import { useEffect, useState } from 'preact/hooks' import { useEffect, useState } from 'preact/hooks'
import { ToolOverlay } from './components/CourseVisibility.jsx'
import { EventsView } from './components/EventsView.jsx' import { EventsView } from './components/EventsView.jsx'
import { Toolbar } from './components/Toolbar.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 ( return (
<> <>
<Toolbar {...{ mode, setMode }} /> <Toolbar {...{ mode, setMode }} />
<EventsView <EventsView
mode={mode} mode={mode}
hideOtherCourses={hideOtherCourses}
start={new Date(2022, 10, 3)} start={new Date(2022, 10, 3)}
events={eventi.map(({ nome, dataInizio, dataFine, docenti, aule }) => ({ events={eventi.map(({ nome, dataInizio, dataFine, docenti, aule }) => ({
name: _.split(nome, '-', 1)[0].trim(), name: _.split(nome, '-', 1)[0].trim(),
@ -59,6 +65,10 @@ const App = ({}) => {
), ),
}))} }))}
/> />
<ToolOverlay
visibility={hideOtherCourses}
toggleVisibility={() => setHideOtherCourses(s => !s)}
/>
</> </>
) )
} }

@ -20,15 +20,21 @@ html {
--gray-350: #e8e8e8; --gray-350: #e8e8e8;
--gray-500: #d8d8d8; --gray-500: #d8d8d8;
--gray-600: #c0c0c0; --gray-600: #c0c0c0;
--gray-700: #b0b0b0;
--gray-800: #909090;
--gray-900: #282828; --gray-900: #282828;
// --accent-100: #e6ceff; // --accent-100: #e6ceff;
// --accent-400: #d0b8ef; // --accent-400: #d0b8ef;
// --accent-500: #aa82db; // --accent-500: #aa82db;
--accent-100: hsl(221, 100%, 88%); // --accent-100: hsl(221, 100%, 88%);
--accent-400: hsl(221, 75%, 78%); // --accent-400: hsl(221, 75%, 78%);
--accent-500: hsl(221, 70%, 75%); // --accent-500: hsl(221, 70%, 75%);
--accent-100: #d6ffc2;
--accent-400: #6cc16c;
--accent-500: #5aa05a;
} }
$device-s-width: 480px; $device-s-width: 480px;
@ -62,12 +68,25 @@ button,
position: relative; position: relative;
overflow: hidden;
&:hover::after { &:hover::after {
content: ''; content: '';
position: absolute; position: absolute;
inset: 0; inset: 0;
background: #00000008; background: #0000000c;
z-index: 1;
}
&.primary {
background: var(--accent-100);
border-color: var(--accent-400);
&:hover::after {
background: #00000020;
}
} }
&.icon { &.icon {
@ -85,7 +104,8 @@ button,
border-radius: 0; border-radius: 0;
border-right: none; border-right: none;
padding: inherit 0.5rem; padding-left: 0.75rem;
padding-right: 0.75rem;
&:first-child { &:first-child {
padding-left: 1rem; padding-left: 1rem;
@ -110,6 +130,10 @@ button,
& + .radio { & + .radio {
border-left-color: var(--accent-400); border-left-color: var(--accent-400);
} }
&:hover::after {
background: #00000018;
}
} }
} }
} }
@ -150,6 +174,10 @@ body {
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.5rem;
@media screen and (max-width: $device-s-width) {
display: none;
}
} }
} }
@ -185,6 +213,26 @@ body {
flex-grow: 1; 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 { .title {
font-size: 24px; font-size: 24px;
} }
@ -474,6 +522,8 @@ body {
gap: 1rem 0.5rem; gap: 1rem 0.5rem;
.event { .event {
position: relative;
grid-row: var(--start) / span var(--size); grid-row: var(--start) / span var(--size);
grid-column: var(--stack) / span 1; grid-column: var(--stack) / span 1;
@ -491,9 +541,22 @@ body {
padding: 0.5rem 1rem 0.5rem 0.5rem; padding: 0.5rem 1rem 0.5rem 0.5rem;
&.highlight { &.selected {
background: var(--gray-100); 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 { .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 // Utilities
span.bold { span.bold {

Loading…
Cancel
Save