Intermediate prototype state

dev
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 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')} &ndash;
{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" />

@ -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)}
/>
</>
)
}

@ -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 {

Loading…
Cancel
Save