updated layout on info page, added background image. Work on times/dates

This commit is contained in:
Benjamin Jones 2021-05-15 20:14:41 +02:00
parent 7b9a675e31
commit 90b3a71b2f
23 changed files with 446 additions and 128 deletions

1
app.js
View File

@ -52,6 +52,7 @@ export default () => {
data={data}
loading={loading || !minLoadTimePassed}
infoActive={infoActive}
// infoActive
currentVideo={currentVideo}
setInfoActive={setInfoActive}
/>

View File

@ -20,6 +20,7 @@
"date-fns": "^2.19.0",
"ical": "^0.8.0",
"ical.js": "^1.4.0",
"markdown-to-jsx": "^7.1.2",
"preact": "^10.5.12",
"prop-types": "^15.7.2",
"styled-components": "^5.2.1"
@ -31,6 +32,7 @@
"babel-preset-env": "^1.6.1",
"eslint": "^4.12.1",
"eslint-config-flying-rocket": "^1.1.1",
"marked": "^2.0.3",
"module-alias": "^2.0.3",
"parcel-bundler": "1.12.3",
"prettier": "^1.9.1",

BIN
src/assets/img/hero/1lg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 KiB

BIN
src/assets/img/hero/1sm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
src/assets/img/hero/2lg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

BIN
src/assets/img/hero/2md.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

BIN
src/assets/img/hero/2sm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -7,6 +7,7 @@ export const colours = {
highlight: '#03a59e',
roseDarker: '#FEB9B3',
rose: '#F1CFCD',
roseLight: '#F8E5E1',
}
colours.text = colours.offwhite

View File

@ -0,0 +1,35 @@
import styled from 'styled-components'
import { colours } from '../../assets/theme'
const Button = styled.button`
background-color: transparent;
border: 1px solid ${colours.midnightDarker};
padding: 0.3em 1em;
font-family: Karla;
font-weight: inherit;
color: ${colours.midnightDarker};
opacity: 1;
text-decoration: none;
font-size: 24px;
cursor: pointer;
width: 100%;
svg {
margin-right: 12px;
position: relative;
top: 2px;
}
:hover {
background-color: ${colours.midnightDarker};
color: ${colours.roseLight};
svg {
path {
stroke: ${colours.roseLight};
}
}
}
`
export default Button

View File

@ -2,7 +2,8 @@
import { h, Fragment } from 'preact'
import { isFuture, isPast } from 'date-fns'
import { P } from '../Text'
import { H1 } from '../Text'
import Markdown from '../Markdown'
import translations from '../../data/strings'
import InfoLayout from '../InfoLayout'
import {
@ -10,8 +11,15 @@ import {
Title,
InfoContent,
PositionedCross as CrossSvg,
TopContent,
} from './styles'
import intro from '../../data/intro.md'
import credits from '../../data/credits.md'
import Button from '../Button'
import PlaySvg from '../Svg/Play'
import { colours } from '../../assets/theme'
const Info = ({ data, loading, infoActive, setInfoActive, currentVideo }) => {
const pastStreams =
data && data.length
@ -20,20 +28,34 @@ const Info = ({ data, loading, infoActive, setInfoActive, currentVideo }) => {
const futureStreams =
data && data.length
? data.filter(
feeditem =>
feeditem.id !== (currentVideo && currentVideo.id) &&
isFuture(new Date(feeditem.start))
)
? data
.filter(
feeditem =>
feeditem.id !== (currentVideo && currentVideo.id) &&
isFuture(new Date(feeditem.start))
)
.sort(
(a, b) =>
// Turn your strings into dates, and then subtract them
// to get a value that is either negative, positive, or zero.
new Date(a.start) - new Date(b.start)
)
: []
console.log({ currentVideo })
return (
<InfoLayout loading={loading}>
{infoActive && <CrossSvg onClick={() => setInfoActive(false)} />}
{!loading && (
<InfoContent>
<Fragment>
<InfoContent>
<H1>The Para-Real:</H1>
<H1>Finding the Future in Unexpected Places</H1>
<Markdown withLinebreaks>{intro}</Markdown>
<Button>
<PlaySvg colour={colours.midnightDarker} size="20" />
Watch the trailer
</Button>
</InfoContent>
{currentVideo && (
<Fragment>
<Title>{translations.en.nowPlaying}:</Title>
@ -58,7 +80,10 @@ const Info = ({ data, loading, infoActive, setInfoActive, currentVideo }) => {
))}
</Fragment>
) : null}
</InfoContent>
<InfoContent>
<Markdown>{credits}</Markdown>
</InfoContent>
</Fragment>
)}
</InfoLayout>
)

View File

@ -1,7 +1,7 @@
import { format } from 'date-fns'
import { h, Fragment } from 'preact'
import styled from 'styled-components'
import { colours } from '../../assets/theme'
import { colours, textSizes } from '../../assets/theme'
import config from '../../data/config'
import Logo from '../Logo'
import translations from '../../data/strings'
@ -10,6 +10,8 @@ import CrossSvg from '../Svg/Cross'
import { P, H1, H2, Span, Label } from '../Text'
import Link from '../Link'
import { bool, instanceOf, string } from 'prop-types'
import Markdown from '../Markdown'
import Button from '../Button'
export const Wrapper = styled.div`
height: 100vh;
@ -34,10 +36,21 @@ export const Wrapper = styled.div`
}
`
export const Top = styled.div``
export const InfoContent = styled.div`
max-width: 600px;
margin: 0 0 0em 2px;
padding-bottom: 1em;
h1 {
display: none;
&:last-of-type {
margin-bottom: 32px;
}
@media screen and (max-width: 1000px) {
display: block;
}
}
`
export const PositionedLogo = styled(Logo)`
@ -68,18 +81,22 @@ export const PositionedCross = styled(CrossSvg)`
}
`
const VCWrapper = styled.div`
export const VCWrapper = styled.div`
max-width: 600px;
margin: 0 0 6em 2px;
border-left: 5px solid ${colours.midnightDarker};
padding-left: 1em;
button {
margin-top: 16px;
}
/* border-left: 5px solid ${colours.midnightDarker}; */
/* padding-left: 1em; */
`
const VCImg = styled.img`
width: 100%;
`
const ItemTitle = styled(H2)`
const ItemTitleWrapper = styled.div`
margin-bottom: 0.3em;
`
@ -93,46 +110,49 @@ const LinkBlock = styled(Link)`
width: 100%;
`
const renderTitles = titles =>
titles.split('\\n').map(title => <H2 key={title}>{title}</H2>)
export const VideoCard = ({
title,
description,
start,
end,
previewPath,
hasPassed,
videoUrl,
}) => (
<VCWrapper>
{videoUrl && hasPassed ? (
<LinkBlock href={videoUrl}>
<ItemTitle>{title}</ItemTitle>
<VCImg src={`${config.peertube_root}${previewPath}`} alt="" />
</LinkBlock>
) : (
<Fragment>
<ItemTitle>{title}</ItemTitle>
<VCImg src={`${config.peertube_root}${previewPath}`} alt="" />
</Fragment>
)}
<DateLabel colour={colours.midnight} size="18">
{`${
hasPassed
? translations.en.streamDatePast
: translations.en.streamDateFuture
}`}
<Span bold colour={colours.midnight}>
{format(new Date(start), 'hh:mm dd/MM/yy')}
</Span>
</DateLabel>
<P>{description}</P>
</VCWrapper>
)
}) => {
console.log('start', start)
return (
<VCWrapper>
<DateLabel colour={colours.midnight} size={textSizes.lg}>
{`${hasPassed ? translations.en.streamDatePast : ''}`}
<Span bold colour={colours.midnight}>
{hasPassed
? format(new Date(start), 'dd/MM/yy')
: format(new Date(start), 'do LLLL y // HH:mm')}
</Span>
</DateLabel>
{videoUrl && hasPassed ? (
<LinkBlock href={videoUrl}>
<ItemTitleWrapper>{renderTitles(title)}</ItemTitleWrapper>
<VCImg src={`${config.peertube_root}${previewPath}`} alt="" />
</LinkBlock>
) : (
<Fragment>
<ItemTitleWrapper>{renderTitles(title)}</ItemTitleWrapper>
<VCImg src={`${config.peertube_root}${previewPath}`} alt="" />
</Fragment>
)}
<P>{description}</P>
<Button>Subscribe to this event</Button>
</VCWrapper>
)
}
VideoCard.propTypes = {
title: string,
description: string,
start: instanceOf(Date),
end: instanceOf(Date),
previewPath: string,
hasPassed: bool,
videoUrl: string,

View File

@ -2,47 +2,58 @@ import { h } from 'preact'
import { useEffect, useRef, useState } from 'preact/hooks'
import { bool, string } from 'prop-types'
import { H1, H2 } from '../Text'
import { H1, H2, Label, Span } from '../Text'
import {
Wrapper,
PositionedLogo as Logo,
TaglineContainer,
LoaderWrapper,
Content,
Fade,
Hero,
FadeBottom,
} from './styles'
import translations from '../../data/strings'
import { colours } from '../../assets/theme'
import Loader from '../Loader'
import { useTimeout } from '../../hooks/timerHooks'
import { NdcLogo, RFLogo } from '../Logo'
const InfoLayout = ({ children, loading }) => {
return (
<Wrapper>
<Fade>
<Logo active colour={colours.midnightDarker} />
</Fade>
<Content>
{loading ? (
<LoaderWrapper>
<Loader />
</LoaderWrapper>
) : (
children
)}
</Content>
const InfoLayout = ({ children, loading }) => (
<Wrapper>
<Content>
{loading ? (
<LoaderWrapper>
<Loader />
</LoaderWrapper>
) : (
children
)}
</Content>
<Hero>
<div>
<H1>The</H1>
<H1>Para-</H1>
<H1>Real</H1>
<H1
css={`
max-width: 50%;
`}
>
Finding the Future in Unexpected Places
</H1>
</div>
<TaglineContainer>
{translations &&
translations.en.underscoreTagline.map(line => (
<H1 align="right" key={line}>
{line}
</H1>
))}
<a href="https://newdesigncongress.org/">
<NdcLogo active colour={colours.offwhite} />
</a>
<Label size="24">{'//'}</Label>
<a href="https://reclaimfutures.org/">
<RFLogo active colour={colours.offwhite} />
</a>
</TaglineContainer>
</Wrapper>
)
}
<FadeBottom />
</Hero>
</Wrapper>
)
InfoLayout.propTypes = {
loading: bool,

View File

@ -1,25 +1,20 @@
import styled from 'styled-components'
import { colours } from '../../assets/theme'
import bg from '../../assets/img/hero/2md.png'
import { H1 } from '../Text'
import Logo from '../Logo'
const heroWidth = 'calc(100vw - 600px - 4em)'
export const Wrapper = styled.div`
height: 100vh;
width: 100vw;
padding: 6em 2em 2em 2em;
padding: 2em;
display: flex;
/* justify-content: space-between; */
/* flex-direction: column; */
background: url(https://images.unsplash.com/photo-1579762715118-a6f1d4b934f1?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=3031&q=80)
${colours.rose};
background-color: ${colours.roseLight};
box-sizing: border-box;
background-size: cover;
background-position-y: 50%;
background-position-x: 23vw;
background-blend-mode: soft-light;
position: fixed;
overflow-y: scroll;
@ -30,14 +25,10 @@ export const Wrapper = styled.div`
}
@media screen and (max-width: 1200px) {
padding: 6em 1.5em 1.5em 1.5em;
background-size: 150%;
background-position-x: 0vw;
padding: 1.5em;
}
@media screen and (max-width: 800px) {
padding: 6em 1em 1em 1em;
background-size: 250%;
background-position-x: -50vw;
padding: 1em;
}
`
@ -45,9 +36,14 @@ export const Top = styled.div`
width: 50%;
`
const gradientColour = '#F8E5E2'
const getGradient = direction =>
`linear-gradient(to ${direction}, ${gradientColour}ee 0%,${gradientColour}00 100%);`
const gradientColourLight = '#F8E5E2'
const gradientColourDark = colours.midnightDarker
const getGradient = (direction, lightDark) =>
`linear-gradient(to ${direction}, ${
lightDark === 'dark' ? gradientColourDark : gradientColourLight
}ee 0%,${
lightDark === 'dark' ? gradientColourDark : gradientColourLight
}00 100%);`
// prettier-ignore
export const Fade = styled.div`
@ -59,6 +55,44 @@ export const Fade = styled.div`
left: 0;
background: ${getGradient('bottom')};
`
export const Hero = styled.div`
width: ${heroWidth};
height: 100vh;
background: url(${bg});
background-size: cover;
background-position-x: right;
position: fixed;
padding: 0;
right: 0;
top: 0;
padding: 2em 2em 8px 2em;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
pointer-events: none;
h1,
h2 {
color: ${colours.offwhite};
}
h1:not(:last-of-type) {
font-size: 30vh;
@media screen and (max-height: 600px) {
font-size: 20vh;
}
@media screen and (max-width: 1200px) {
font-size: 20vh;
}
}
@media screen and (max-width: 1000px) {
display: none;
}
`
export const LoaderWrapper = styled.div`
display: flex;
justify-content: center;
@ -66,7 +100,7 @@ export const LoaderWrapper = styled.div`
height: 100vh;
position: fixed;
top: 0;
width: 50vw;
width: 600px;
`
export const Content = styled.div`
@ -75,27 +109,37 @@ export const Content = styled.div`
export const PositionedLogo = styled(Logo)``
export const TaglineContainer = styled.div`
background: ${getGradient('top')};
export const FadeBottom = styled.div`
background: ${getGradient('top', 'dark')};
width: ${heroWidth};
position: fixed;
bottom: 0em;
padding-bottom: 0.5em;
right: 1em;
right: 0;
/* left: 0; */
pointer-events: none;
min-height: 75px;
@media screen and (max-width: 1200px) {
width: 100vw;
right: auto;
left: 1.5em;
h1 {
font-size: 32px;
text-align: left;
}
}
@media screen and (max-width: 800px) {
/* @media screen and (max-width: 800px) {
h1 {
font-size: 24px;
}
} */
`
export const TaglineContainer = styled.div`
width: 100%;
bottom: 0em;
padding-bottom: 0.5em;
right: 0;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
z-index: 2;
a {
pointer-events: all;
}
`

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,49 @@
/* eslint-disable jsx-a11y/anchor-has-content */
import { h, Fragment } from 'preact'
import MarkdownRenderer from 'markdown-to-jsx'
import { MarkdownWrapper } from './styles'
import { P, A, H1, H2, Span } from '../Text'
const Markdown = ({ children, withLinebreaks, options, ...rest }) => (
<MarkdownWrapper $withLinebreaks={withLinebreaks}>
<MarkdownRenderer
options={{
overrides: {
p: {
component: P,
},
span: {
component: P,
},
em: {
component: ({ children: spanChildren }) => (
<Span fontStyle="italic">{spanChildren}</Span>
),
},
a: {
component: props => (
<A
data-hoverable
target="_blank"
rel="noopener noreferrer"
{...props}
/>
),
},
h1: {
component: H1,
},
h2: {
component: H2,
},
},
...options,
}}
{...rest}
>
{children}
</MarkdownRenderer>
</MarkdownWrapper>
)
export default Markdown

View File

@ -0,0 +1,40 @@
import { h } from 'preact'
import styled from 'styled-components'
import { colours } from '../../assets/theme'
import { P } from '../Text'
export const MarkdownWrapper = styled.span`
a {
color: ${colours.highlight};
}
img {
max-width: 200px;
float: left;
padding: 0 12px 6px 0;
}
h1 {
margin-bottom: 1em;
}
h2 {
margin-bottom: 0.5em;
}
p {
margin-bottom: ${props => (props.$withLinebreaks ? '32px' : '0')};
}
p > p {
display: inline;
margin-bottom: 0;
}
li {
margin-left: 1em;
}
ul {
margin-bottom: 1em;
}
`

View File

@ -0,0 +1,19 @@
import { h } from 'preact'
import { number, string } from 'prop-types'
const Play = ({ size = '24', colour = 'inherit', ...rest }) => (
<svg viewBox="0 0 24 24" height={size} width={size} {...rest}>
<path
stroke={colour}
fill="transparent"
d="M2.12436,1.73205 C2.12436,0.96225 2.95769,0.481125 3.62436,0.866025 L21.6244,11.2583 C22.291,11.6432 22.291,12.6055 21.6244,12.9904 L3.62436,23.3827 C2.95769,23.7676 2.12436,23.2865 2.12436,22.5167 L2.12436,1.73205 Z"
/>
</svg>
)
Play.propTypes = {
size: number,
colour: string,
}
export default Play

View File

@ -13,7 +13,9 @@ const Text = ({
fontFamily = 'Karla',
children,
size,
sizeUnit,
selectable = true,
fontStyle,
...rest
}) => {
const colour = colourProp || colours.text
@ -26,6 +28,8 @@ const Text = ({
lineHeight={lineHeight}
$fontFamily={fontFamily}
$size={size}
$sizeUnit={sizeUnit}
$fontStyle={fontStyle}
selectable={selectable}
{...rest}
>
@ -61,20 +65,18 @@ Text.propTypes = {
selectable: bool,
}
export const H1 = ({ children, ...rest }) => {
return (
<Text
tag="h1"
weight="700"
$size="48"
lineHeight="0.8"
fontFamily="Lunchtype24"
{...rest}
>
{children}
</Text>
)
}
export const H1 = ({ children, size, ...rest }) => (
<Text
tag="h1"
weight="700"
$size={size || '48'}
lineHeight="0.8"
fontFamily="Lunchtype24"
{...rest}
>
{children}
</Text>
)
export const H2 = ({ children, ...rest }) => (
<Text
@ -102,7 +104,7 @@ export const P = ({ children, ...rest }) => (
</Text>
)
export const Span = ({ children, ...rest }) => (
<Text tag="span" $size="inherit" weight="inherit" {...rest}>
<Text tag="span" colour="inherit" $size="inherit" weight="inherit" {...rest}>
{children}
</Text>
)

View File

@ -4,6 +4,7 @@ import { colours } from '../../assets/theme'
export const TextBase = styled.span`
${({
$size,
$fontStyle,
weight,
colour,
align,
@ -12,6 +13,7 @@ export const TextBase = styled.span`
$fontFamily: fontFamily,
selectable,
underline,
$sizeUnit,
}) => css`
font-family: ${fontFamily};
font-weight: ${weight};
@ -21,7 +23,8 @@ export const TextBase = styled.span`
opacity: ${opacity};
user-select: ${selectable ? 'inherit' : 'none'};
text-decoration: ${underline ? 'underline' : 'none'};
font-size: ${$size}px;
font-size: ${`${$size}${$sizeUnit || 'px'}`};
font-style: ${$fontStyle};
::selection {
background-color: ${colours.midnightDarker};

23
src/data/credits.md Normal file
View File

@ -0,0 +1,23 @@
## Credits
A [NEW DESIGN CONGRESS](https://newdesigncongress.org) project
Stream Design by **[BENJAMIN JONES](https://benjaminjon.es)**
Host & Research by **[CADE DIEHM](https://shiba.computer)**
Stream Backgrounds by **[IGNATIUS GILFEDDER](https://ignatius.design)**
Series operated by **[ReclaimFutures](https://reclaimfutures.org)**
Infrastructure by **[UNDERSCO.RE](https://undersco.re)**
Music \*\*\*\*by:
- [♥ GOJII ♥](https://gojii.bandcamp.com/)
- [音 LIGHT システム](https://saphronsquares.bandcamp.com/)
- [ABELARD](https://abelard.bandcamp.com/)
- [Frog of Earth](https://frogoftheearth.bandcamp.com/)
- [ .](https://spatialmanufactureltd.bandcamp.com/)\*\*
Simulcasted at [UNDERSCORE TV](https://stream.undersco.re) and [Twitch](https://twitch.tv/newdesigncongress)

7
src/data/intro.md Normal file
View File

@ -0,0 +1,7 @@
[NEW DESIGN CONGRESS](https://newdesigncongress.org) x [RECLAIMFUTURES](https://reclaimfutures.org) present _The Para-Real: Finding the Future in Unexpected Places,_ a Spring/Summer livestream series about economic subcultures and emergent technology use. Over 12 episodes streamed weekly, we meet filmmakers who have never met their actors, artists building their own networks of value, documentarians exploring digital identity, and members of resilient subcultures. All of these people share a commonality: they have an innate understanding of the _Para-Real,_ and have seized upon it to better their surroundings.
Between the digital realm and our physical world is a third space, equally material but poorly understood. The _Para-Real_ is a tangible place where class structures, economics and the outcomes of hardware and infrastructure design collide. The Para-Real manifests in many ways — It can be the desire for play that turns young Minecraft players into network administrators, the drive that fosters the resilience of subculture-driven mutually-supportive marketplaces, or the tension of class structures inherent in Virtual Reality's encroachment on living room space.
_We shape our tools, and thereafter our tools shape us._
The Para-Real is everywhere. Whether attending a Furcon at the height of pandemic, organising community for resilience, or navigating a commissioned open world, we must consider ourselves as inhabiting not a real or online world, but a bridge between the two, one whose rules are not yet settled. _The future is not a Zoom call_. The digital systems we are confined to today merge protocol with platform to prey on isolation and extract value from labour. That we grapple with this incarnation of the digital realm indicates a dominant cartel in decline. In its place is a vacuum. We must resist the immature groupthink of the 90s vision of what the Internet can be — the Para-Real is a contested space*. Never trust someone who says the Internet is boring.* Thanks to the Para-Real, the Internet has never been weirder.

View File

@ -1,11 +1,13 @@
export default {
en: {
nextStream: 'Next streams',
nextStream: 'Program',
pastStream: 'Latest streams',
nowPlaying: 'Now playing',
noStreams: 'No upcoming streams, check back soon.',
underscoreTagline: ['LEAVE THE', 'SURVEILLANCE ECONOMY', '— TOGETHER.'],
streamDateFuture: 'Going live at: ',
streamDatePast: 'First broadcast: ',
main_text:
"[NEW DESIGN CONGRESS](https://newdesigncongress.org) x [RECLAIMFUTURES](https://reclaimfutures.org) present *The Para-Real: Finding the Future in Unexpected Places,* a Spring/Summer livestream series about economic subcultures and emergent technology use. Over 12 episodes streamed weekly, we meet filmmakers who have never met their actors, artists building their own networks of value, documentarians exploring digital identity, and members of resilient subcultures. All of these people share a commonality: they have an innate understanding of the *Para-Real,* and have seized upon it to better their surroundings. \nBetween the digital realm and our physical world is a third space, equally material but poorly understood. The *Para-Real* is a tangible place where class structures, economics and the outcomes of hardware and infrastructure design collide. The Para-Real manifests in many ways — It can be the desire for play that turns young Minecraft players into network administrators, the drive that fosters the resilience of subculture-driven mutually-supportive marketplaces, or the tension of class structures inherent in Virtual Reality's encroachment on living room space.\n\nWe shape our tools, and thereafter our tools shape us.* The Para-Real is everywhere. Whether attending a Furcon at the height of pandemic, organising community for resilience, or navigating a commissioned open world, we must consider ourselves as inhabiting not a real or online world, but a bridge between the two, one whose rules are not yet settled. *The future is not a Zoom call*. The digital systems we are confined to today merge protocol with platform to prey on isolation and extract value from labour. That we grapple with this incarnation of the digital realm indicates a dominant cartel in decline. In its place is a vacuum. We must resist the immature groupthink of the 90s vision of what the Internet can be — the Para-Real is a contested space*. Never trust someone who says the Internet is boring.* Thanks to the Para-Real, the Internet has never been weirder.",
},
}

View File

@ -24,7 +24,6 @@ export const useEventStream = () => {
calEvents.map(async calItem => {
const url = calItem.component.getAllProperties('url')[0]
if (url) {
console.log('url', url)
const id = url
.getFirstValue()
.split('/')
@ -61,6 +60,14 @@ export const useEventStream = () => {
videoUrl: url.getFirstValue(),
}
setData(arr => [...arr, item])
} else {
const item = {
title: calItem.summary,
description: calItem.description,
start: calItem.startDate.toJSDate(),
end: calItem.endDate.toJSDate(),
}
setData(arr => [...arr, item])
}
})
)