mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-15 04:13:47 +00:00
feat: public view
This commit is contained in:
@@ -49,9 +49,17 @@ export default function NavLogo() {
|
||||
Backstage
|
||||
</Link>
|
||||
<Link
|
||||
to='/lower'
|
||||
to='/public'
|
||||
className={style.navItem}
|
||||
tabIndex={3}
|
||||
onKeyDownCapture={() => <Redirect push to='/public' />}
|
||||
>
|
||||
Public
|
||||
</Link>
|
||||
<Link
|
||||
to='/lower'
|
||||
className={style.navItem}
|
||||
tabIndex={4}
|
||||
onKeyDownCapture={() => <Redirect push to='/lower' />}
|
||||
>
|
||||
Lower Thirds
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import TodayItem from './TodayItem';
|
||||
import style from './Paginator.module.css';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useInterval } from '../../../app/hooks/useInterval';
|
||||
|
||||
export default function Paginator(props) {
|
||||
const { events, selectedId } = props;
|
||||
const LIMIT_PER_PAGE = 7;
|
||||
const SCROLL_TIME = 5000;
|
||||
const SCROLL_PAST = false;
|
||||
const [numEvents, setNumEvents] = useState(0);
|
||||
const [page, setPage] = useState([]);
|
||||
const [pages, setPages] = useState(0);
|
||||
const [selPage, setSelPage] = useState(0);
|
||||
|
||||
// Keep track of order
|
||||
// 0 - events before
|
||||
// 1 - running event
|
||||
// 2 - future event
|
||||
let selectedYet = 0;
|
||||
|
||||
useEffect(() => {
|
||||
if (events == null) return;
|
||||
|
||||
// how many events in list
|
||||
let n = events.length;
|
||||
setNumEvents(n);
|
||||
|
||||
// how many paginated views
|
||||
let p = Math.ceil(n / LIMIT_PER_PAGE);
|
||||
setPages(p);
|
||||
|
||||
// divide events in parts of LIMIT_PER_PAGE
|
||||
const eventStart = LIMIT_PER_PAGE * selPage;
|
||||
const eventEnd = LIMIT_PER_PAGE * (selPage + 1);
|
||||
let e = events.slice(eventStart, eventEnd);
|
||||
setPage(e);
|
||||
|
||||
// if array is completely in past, show depending on SCROLL_PAST
|
||||
}, [events, selPage]);
|
||||
|
||||
// every SCROLL_TIME go to the next array
|
||||
useInterval(() => {
|
||||
if (numEvents > LIMIT_PER_PAGE) {
|
||||
const next = (selPage + 1) % pages;
|
||||
setSelPage(next);
|
||||
}
|
||||
}, SCROLL_TIME);
|
||||
|
||||
return (
|
||||
<>
|
||||
{pages > 1 && (
|
||||
<div className={style.nav}>
|
||||
{[...Array(pages)].map((p, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={i === selPage ? style.navItemSelected : style.navItem}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={style.entries}>
|
||||
{page.map((e) => {
|
||||
if (e.id === selectedId) selectedYet = 1;
|
||||
else if (selectedYet === 1) selectedYet = 2;
|
||||
return (
|
||||
<TodayItem
|
||||
key={e.id}
|
||||
selected={selectedYet}
|
||||
timeStart={e.timeStart}
|
||||
timeEnd={e.timeEnd}
|
||||
title={e.title}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;800&display=swap');
|
||||
|
||||
.entries {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.navItem,
|
||||
.navItemSelected {
|
||||
background-color: rgba(255, 255, 255, 0.39);
|
||||
width: 0.7vw;
|
||||
height: 0.7vw;
|
||||
border-radius: 0.35vw;
|
||||
}
|
||||
|
||||
.navItemSelected {
|
||||
background-color: rgba(255, 255, 255, 0.79);
|
||||
}
|
||||
|
||||
.nav {
|
||||
align-self: center;
|
||||
justify-content: flex-end;
|
||||
display: flex;
|
||||
margin-bottom: 2.5vh;
|
||||
}
|
||||
|
||||
.nav > div {
|
||||
margin-left: 0.5vw;
|
||||
}
|
||||
|
||||
.entryStart,
|
||||
.entryEnd {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
min-width: 5vw;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.entryTitle {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.entryPast,
|
||||
.entryNow,
|
||||
.entryFuture {
|
||||
background-color: rgba(255, 255, 255, 0.03);
|
||||
display: flex;
|
||||
font-size: 1.3vw;
|
||||
padding: 0.2vh 1vw;
|
||||
margin-bottom: 1.5vh;
|
||||
}
|
||||
|
||||
.entryPast {
|
||||
color: #aaa;
|
||||
font-size: 1.2vw;
|
||||
}
|
||||
|
||||
.entryFuture {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.entryNow {
|
||||
color: #fff;
|
||||
line-height: 5vh;
|
||||
background-color: rgba(255, 255, 255, 0.09);
|
||||
border-bottom: 0.5vh solid #ff7597aa;
|
||||
margin-left: -0.5vw;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { stringFromMillis } from '../../../common/dateConfig';
|
||||
import style from './Paginator.module.css';
|
||||
export default function TodayItem(props) {
|
||||
const { selected, timeStart, timeEnd, title } = props;
|
||||
|
||||
// Format timers
|
||||
const start = stringFromMillis(timeStart, false) || '';
|
||||
const end = stringFromMillis(timeEnd, false) || '';
|
||||
|
||||
// select styling
|
||||
let selectStyle = style.entryPast;
|
||||
if (selected === 1) selectStyle = style.entryNow;
|
||||
else if (selected === 2) selectStyle = style.entryFuture;
|
||||
return (
|
||||
<div className={selectStyle}>
|
||||
<div className={style.entryStart}>{start}</div>
|
||||
<div className={style.entryEnd}>{end}</div>
|
||||
<div className={style.entryTitle}>{title}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user