101 lines
3.3 KiB
React
101 lines
3.3 KiB
React
import React from 'react';
|
|
|
|
import { Track } from 'livekit-client';
|
|
|
|
import LetterTile from './letter-tile.jsx';
|
|
import VCCarouselItem from './vc-carousel-item.jsx';
|
|
|
|
import { VC_CAROUSEL_ITEM_GAP } from '../constants.js';
|
|
|
|
export default class VCCarousel extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.handlePrevClick = this.handlePrevClick.bind(this);
|
|
this.handleNextClick = this.handleNextClick.bind(this);
|
|
|
|
this.carouselRef = React.createRef();
|
|
this.contentRef = React.createRef();
|
|
this.prevRef = React.createRef();
|
|
this.nextRef = React.createRef();
|
|
}
|
|
|
|
handlePrevClick() {
|
|
const gap = VC_CAROUSEL_ITEM_GAP;
|
|
const width = this.carouselRef.current.offsetWidth;
|
|
this.carouselRef.current.scrollBy(-(width + gap), 0);
|
|
if (this.carouselRef.current.scrollLeft - width - gap <= 0) {
|
|
this.prevRef.current.style.display = 'none';
|
|
}
|
|
if (!this.contentRef.current.scrollWidth - width - gap <= this.carouselRef.current.scrollLeft + width) {
|
|
this.nextRef.current.style.display = 'flex';
|
|
}
|
|
}
|
|
|
|
handleNextClick() {
|
|
const gap = VC_CAROUSEL_ITEM_GAP;
|
|
const width = this.carouselRef.current.offsetWidth;
|
|
this.carouselRef.current.scrollBy(width + gap, 0);
|
|
if (this.carouselRef.current.scrollWidth !== 0) {
|
|
this.prevRef.current.style.display = 'flex';
|
|
}
|
|
if (this.contentRef.current.scrollWidth - width - gap <= this.carouselRef.current.scrollLeft + width) {
|
|
this.nextRef.current.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
render() {
|
|
let peers = [];
|
|
if (this.props.participants) {
|
|
Object.entries(this.props.participants).forEach(([identity, party], i) => {
|
|
const participant = party.participant;
|
|
const cameraPub = participant.getTrack(Track.Source.Camera);
|
|
const micPub = participant.getTrack(Track.Source.Microphone);
|
|
|
|
peers.push(<VCCarouselItem
|
|
tinode={this.props.tinode}
|
|
isSpeaking={participant.isSpeaking}
|
|
cameraPub={cameraPub ? cameraPub : null}
|
|
micPub={micPub ? micPub : null}
|
|
name={party.name}
|
|
identity={identity}
|
|
photo={party.photo}
|
|
key={i}
|
|
/>);
|
|
});
|
|
}
|
|
return (
|
|
<>
|
|
<div id="carousel-wrapper">
|
|
<div id="carousel" ref={this.carouselRef}>
|
|
<div id="carousel-content" ref={this.contentRef}>
|
|
{peers}
|
|
</div>
|
|
</div>
|
|
<button id="carousel-prev" ref={this.prevRef} onClick={this.handlePrevClick}>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="24"
|
|
height="24"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path fill="none" d="M0 0h24v24H0V0z" />
|
|
<path d="M15.61 7.41L14.2 6l-6 6 6 6 1.41-1.41L11.03 12l4.58-4.59z" />
|
|
</svg>
|
|
</button>
|
|
<button id="carousel-next" ref={this.nextRef} onClick={this.handleNextClick}>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="24"
|
|
height="24"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path fill="none" d="M0 0h24v24H0V0z" />
|
|
<path d="M10.02 6L8.61 7.41 13.19 12l-4.58 4.59L10.02 18l6-6-6-6z" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
}
|