blocks/community/event-list/event-list.tsx (171 lines of code) (raw):

import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useRouter } from 'next/router'; import Switcher from '@rescui/switcher'; import ISO6391 from 'iso-639-1'; import { ModeSelect, SelectOption } from './mode-select'; import { EventCard } from './event-card'; import styles from './event-list.module.css'; import { communityEvents } from './community-events'; import { CommunityEvent } from './community-event'; import cn from 'classnames'; import Button from '@rescui/button'; interface EventType { value: string; label: string; } const upcomingEvent: EventType = { value: 'upcoming', label: 'Upcoming', }; const pastEvent: EventType = { value: 'past', label: 'Past', }; const materialTypes = { examples: 'Examples', slides: 'Slides', video: 'Video', pdf: 'PDF', article: 'Article', }; const defaultOption: SelectOption = { id: 'all', label: 'All', }; export function CommunityAddEvent(props) { return ( <Button target="_blank" mode="outline" href="https://github.com/JetBrains/kotlin-web-site/#community-events" {...props} > Add New Event ↗ </Button> ); } export const EventList = () => { const router = useRouter(); const [eventMode, setMode] = useState<string>(upcomingEvent.value); const [language, setLanguage] = useState<SelectOption>(defaultOption); const [material, setMaterial] = useState<SelectOption>(defaultOption); useEffect(() => { setMode(router.query.time === pastEvent.value ? pastEvent.value : upcomingEvent.value); }, [router.query]); const switchMode = useCallback( (value: string) => { if (value === pastEvent.value) { router.push({ query: { time: pastEvent.value }, }); } else { router.push({}); } setMaterial(defaultOption); }, [router] ); const events = useMemo(() => { return communityEvents .filter((event) => (eventMode === upcomingEvent.value ? event.isUpcoming() : !event.isUpcoming())) .sort(sortBy(eventMode === upcomingEvent.value ? 'desc' : 'asc')); }, [eventMode]); const visibleEvents = useMemo(() => { return events.filter((event) => { let visible = true; if (language.id !== defaultOption.id && event.lang !== language.id) { visible = false; } if (material.id !== defaultOption.id && (!event?.content || !event.content[material.id])) { visible = false; } return visible; }); }, [events, language, material]); const visibleLanguages: SelectOption[] = useMemo(() => { const arr = events .map((event) => event.lang) .filter((value, index, array) => array.indexOf(value) === index) .map((id) => ({ id: id, label: ISO6391.getName(id) || id })); arr.sort((a, b) => a.label.localeCompare(b.label)); arr.unshift(defaultOption); return arr; }, [events]); useEffect(() => { setLanguage(defaultOption); }, [visibleLanguages]); const visibleMaterials: SelectOption[] = useMemo(() => { const allValues: { [key: string]: SelectOption } = events.reduce((dict, event) => { if (event?.content) { for (let materialKey in event.content) { if (materialTypes[materialKey] && !dict[materialKey]) { dict[materialKey] = { id: materialKey, label: materialTypes[materialKey] }; } } } return dict; }, {}); const arr = Object.values(allValues); return arr.length ? [defaultOption, ...arr] : []; }, [events]); useEffect(() => { setMaterial(defaultOption); }, [visibleMaterials]); return ( <div className={styles.wrapper}> <div className={'ktl-layout ktl-layout--center'}> <h1 className={'ktl-h1'}>Events</h1> <div className={styles.actions}> <div> <Switcher mode={'rock'} value={eventMode} onChange={switchMode} options={[upcomingEvent, pastEvent]} /> <CommunityAddEvent className={styles.add}/> </div> <div className={styles.selects}> {!!visibleMaterials.length && ( <ModeSelect options={visibleMaterials} value={material} label={'Material'} onSelect={setMaterial} className={styles.select} /> )} <ModeSelect options={visibleLanguages} value={language} label={'Language'} onSelect={setLanguage} className={cn(styles.select, styles.selectEnd)} /> </div> </div> {visibleEvents.length ? ( <div className={styles.list}> {visibleEvents.map((communityEvent) => ( <EventCard key={communityEvent.id} event={communityEvent} /> ))} </div> ) : ( <div className={cn('ktl-t1', styles.empty)}>No Events Found</div> )} </div> </div> ); }; function sortBy(direction: 'asc' | 'desc') { return ({ endDate: first }: CommunityEvent, { endDate: second }: CommunityEvent) => { if (first === second) { return 0; } if (direction === 'asc') { return first < second ? 1 : -1; } else { return first < second ? -1 : 1; } }; }