fixes alignment of the emojis and thumbnails
This commit is contained in:
parent
44b6e2d62e
commit
b2075b5bd1
@ -5,9 +5,8 @@ import UserAvatar from './UserAvatar';
|
||||
|
||||
class StatusTable extends React.Component {
|
||||
componentDidMount() {
|
||||
// This code is needed to prevent the emoji in the first cell
|
||||
// after the username from overflowing
|
||||
const emojis = document.getElementsByClassName('emojiOnFirstCell');
|
||||
// This code is needed to prevent the emojis from overflowing
|
||||
const emojis = document.getElementsByClassName('timeline-emoji');
|
||||
for (let i = 0; i < emojis.length; i += 1) {
|
||||
const emojiStyle = window.getComputedStyle(emojis[i]);
|
||||
let offsetLeft = emojiStyle
|
||||
@ -16,13 +15,28 @@ class StatusTable extends React.Component {
|
||||
.trim();
|
||||
offsetLeft = Number(offsetLeft);
|
||||
if (offsetLeft < 0) {
|
||||
emojis[i].style.offsetLeft = '0px';
|
||||
emojis[i].style.left = '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// This code is needed to prevent the emojis from overflowing
|
||||
const emojis = document.getElementsByClassName('timeline-emoji');
|
||||
for (let i = 0; i < emojis.length; i += 1) {
|
||||
const emojiStyle = window.getComputedStyle(emojis[i]);
|
||||
let offsetLeft = emojiStyle
|
||||
.left
|
||||
.replace(/px/g, '')
|
||||
.trim();
|
||||
offsetLeft = Number(offsetLeft);
|
||||
if (offsetLeft < 0) {
|
||||
emojis[i].style.left = '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const spanMinutes = 10 * 60000; // 10 minutes default
|
||||
const {
|
||||
allUsers, slides, meetingId, intl,
|
||||
} = this.props;
|
||||
@ -40,24 +54,28 @@ class StatusTable extends React.Component {
|
||||
const firstRegisteredOnTime = Math.min(...usersRegisteredTimes);
|
||||
const lastLeftOnTime = Math.max(...usersLeftTimes);
|
||||
|
||||
const hasSlides = slides && Array.isArray(slides) && slides.length > 0;
|
||||
const periods = [];
|
||||
let currPeriod = firstRegisteredOnTime;
|
||||
while (currPeriod < lastLeftOnTime) {
|
||||
periods.push(currPeriod);
|
||||
currPeriod += spanMinutes;
|
||||
}
|
||||
|
||||
function getSlidesOnPeriod(start = null, end = null) {
|
||||
if (start == null && end == null) return slides;
|
||||
|
||||
const filteredSlides = slides.filter((slide) => {
|
||||
if (slide.presentationId === '') return false;
|
||||
if (start == null && slide.setOn <= end) return true;
|
||||
if (end == null && slide.setOn >= start) return true;
|
||||
if (slide.setOn >= start && slide.setOn < end) return true;
|
||||
return false;
|
||||
if (hasSlides) {
|
||||
const filteredSlides = slides.filter((slide) => slide.presentationId !== '');
|
||||
if (firstRegisteredOnTime < slides[0].setOn) {
|
||||
periods.push({
|
||||
start: firstRegisteredOnTime,
|
||||
end: slides[0].setOn - 1,
|
||||
});
|
||||
}
|
||||
filteredSlides.forEach((slide, index, slidesArray) => {
|
||||
periods.push({
|
||||
slide,
|
||||
start: slide.setOn,
|
||||
end: slidesArray[index + 1]?.setOn - 1 || lastLeftOnTime,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
periods.push({
|
||||
start: firstRegisteredOnTime,
|
||||
end: lastLeftOnTime,
|
||||
});
|
||||
return filteredSlides;
|
||||
}
|
||||
|
||||
return (
|
||||
@ -66,42 +84,58 @@ class StatusTable extends React.Component {
|
||||
<tr className="text-xs font-semibold tracking-wide text-gray-500 uppercase border-b bg-gray-100">
|
||||
<th className="px-4 py-3 col-text-left sticky left-0 z-30 bg-inherit">
|
||||
<FormattedMessage id="app.learningDashboard.user" defaultMessage="User" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-4 w-4 inline"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M17 13l-5 5m0 0l-5-5m5 5V6" />
|
||||
</svg>
|
||||
</th>
|
||||
{ periods.map((period) => <th className="px-4 py-3 col-text-left">{ `${tsToHHmmss(period - firstRegisteredOnTime)}` }</th>) }
|
||||
<th
|
||||
className="bg-inherit"
|
||||
colSpan={periods.length}
|
||||
>
|
||||
<span
|
||||
className="invisible"
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'app.learningDashboard.indicators.timeline',
|
||||
defaultMessage: 'Timeline',
|
||||
})}
|
||||
>
|
||||
<FormattedMessage id="app.learningDashboard.indicators.timeline" defaultMessage="Timeline" />
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y">
|
||||
{ slides && Array.isArray(slides) && slides.length > 0 ? (
|
||||
<tr>
|
||||
<td />
|
||||
{ hasSlides ? (
|
||||
<tr className="bg-inherit">
|
||||
<td className="bg-inherit sticky left-0 z-30" />
|
||||
{ periods.map((period) => {
|
||||
const slidesOnPeriod = getSlidesOnPeriod(period, period + spanMinutes);
|
||||
const screenshots = slidesOnPeriod.map((slide) => (
|
||||
<div
|
||||
className="my-2"
|
||||
aria-label={`Slide set on ${tsToHHmmss(slide.setOn - firstRegisteredOnTime)}`}
|
||||
>
|
||||
<img
|
||||
src={`/bigbluebutton/presentation/${meetingId}/${meetingId}/${slide.presentationId}/svg/${slide.pageNum}`}
|
||||
alt="Slide"
|
||||
className="w-36 h-auto"
|
||||
/>
|
||||
<div className="text-xs text-center m-1 text-gray-500">{tsToHHmmss(slide.setOn - firstRegisteredOnTime)}</div>
|
||||
</div>
|
||||
));
|
||||
const { slide, start, end } = period;
|
||||
return (
|
||||
<td>
|
||||
<td
|
||||
style={{
|
||||
paddingRight: `${(end - start) / 1000}px`,
|
||||
}}
|
||||
>
|
||||
<div className="flex">
|
||||
{screenshots}
|
||||
<div
|
||||
className="my-4"
|
||||
aria-label={`Slide set on ${tsToHHmmss(slide.setOn - periods[0].start)}`}
|
||||
>
|
||||
<a
|
||||
href={`/bigbluebutton/presentation/${meetingId}/${meetingId}/${slide.presentationId}/svg/${slide.pageNum}`}
|
||||
className="block border-2 border-gray-300"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<img
|
||||
src={`/bigbluebutton/presentation/${meetingId}/${meetingId}/${slide.presentationId}/thumbnail/${slide.pageNum}`}
|
||||
alt="Slide"
|
||||
style={{
|
||||
maxWidth: '150px',
|
||||
width: '150px',
|
||||
height: 'auto',
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
<div className="text-xs text-center mt-1 text-gray-500">{tsToHHmmss(slide.setOn - periods[0].start)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
@ -133,81 +167,75 @@ class StatusTable extends React.Component {
|
||||
{ periods.map((period) => {
|
||||
const userEmojisInPeriod = filterUserEmojis(user,
|
||||
null,
|
||||
period,
|
||||
period + spanMinutes);
|
||||
period.start,
|
||||
period.end);
|
||||
const { registeredOn, leftOn } = user;
|
||||
const boundaryLeft = period;
|
||||
const boundaryRight = period + spanMinutes - 1;
|
||||
const boundaryLeft = period.start;
|
||||
const boundaryRight = period.end;
|
||||
const interval = period.end - period.start;
|
||||
return (
|
||||
<td className="relative px-4 py-3 text-sm col-text-left">
|
||||
{
|
||||
(registeredOn >= boundaryLeft && registeredOn <= boundaryRight)
|
||||
{ (registeredOn >= boundaryLeft && registeredOn <= boundaryRight)
|
||||
|| (leftOn >= boundaryLeft && leftOn <= boundaryRight)
|
||||
|| (boundaryLeft > registeredOn && boundaryRight < leftOn)
|
||||
|| (boundaryLeft >= registeredOn && leftOn === 0)
|
||||
? (
|
||||
(function makeLineThrough() {
|
||||
let roundedLeft = registeredOn >= boundaryLeft
|
||||
&& registeredOn <= boundaryRight ? 'rounded-l' : '';
|
||||
let roundedRight = leftOn > boundaryLeft
|
||||
&& leftOn < boundaryRight ? 'rounded-r' : '';
|
||||
let offsetLeft = 0;
|
||||
let offsetRight = 0;
|
||||
if (registeredOn >= boundaryLeft && registeredOn <= boundaryRight) {
|
||||
offsetLeft = ((registeredOn - boundaryLeft) * 100) / spanMinutes;
|
||||
}
|
||||
if (leftOn >= boundaryLeft && leftOn <= boundaryRight) {
|
||||
offsetRight = ((boundaryRight - leftOn) * 100) / spanMinutes;
|
||||
}
|
||||
let width = '';
|
||||
if (offsetLeft === 0 && offsetRight >= 99) {
|
||||
|| (boundaryLeft >= registeredOn && leftOn === 0) ? (
|
||||
(function makeLineThrough() {
|
||||
let roundedLeft = registeredOn >= boundaryLeft
|
||||
&& registeredOn <= boundaryRight ? 'rounded-l' : '';
|
||||
let roundedRight = leftOn > boundaryLeft
|
||||
&& leftOn < boundaryRight ? 'rounded-r' : '';
|
||||
let offsetLeft = 0;
|
||||
let offsetRight = 0;
|
||||
if (registeredOn >= boundaryLeft && registeredOn <= boundaryRight) {
|
||||
offsetLeft = ((registeredOn - boundaryLeft) * 100) / (interval);
|
||||
}
|
||||
if (leftOn >= boundaryLeft && leftOn <= boundaryRight) {
|
||||
offsetRight = ((boundaryRight - leftOn) * 100) / (interval);
|
||||
}
|
||||
let width = '';
|
||||
if (offsetLeft === 0 && offsetRight >= 99) {
|
||||
width = 'w-1.5';
|
||||
}
|
||||
if (offsetRight === 0 && offsetLeft >= 99) {
|
||||
width = 'w-1.5';
|
||||
}
|
||||
if (offsetLeft && offsetRight) {
|
||||
const variation = offsetLeft - offsetRight;
|
||||
if (variation > -1 && variation < 1) {
|
||||
width = 'w-1.5';
|
||||
}
|
||||
if (offsetRight === 0 && offsetLeft >= 99) {
|
||||
width = 'w-1.5';
|
||||
}
|
||||
if (offsetLeft && offsetRight) {
|
||||
const variation = offsetLeft - offsetRight;
|
||||
if (
|
||||
variation > -1 && variation < 1
|
||||
) {
|
||||
width = 'w-1.5';
|
||||
}
|
||||
}
|
||||
const isRTL = document.dir === 'rtl';
|
||||
if (isRTL) {
|
||||
const aux = roundedRight;
|
||||
}
|
||||
const isRTL = document.dir === 'rtl';
|
||||
if (isRTL) {
|
||||
const aux = roundedRight;
|
||||
|
||||
if (roundedLeft !== '') roundedRight = 'rounded-r';
|
||||
else roundedRight = '';
|
||||
if (roundedLeft !== '') roundedRight = 'rounded-r';
|
||||
else roundedRight = '';
|
||||
|
||||
if (aux !== '') roundedLeft = 'rounded-l';
|
||||
else roundedLeft = '';
|
||||
}
|
||||
// height / 2
|
||||
const redress = '(0.375rem / 2)';
|
||||
return (
|
||||
<div
|
||||
className={`h-1.5 ${width} bg-gray-200 absolute inset-x-0 z-10 ${roundedLeft} ${roundedRight}`}
|
||||
style={{
|
||||
top: `calc(50% - ${redress})`,
|
||||
left: `${isRTL ? offsetRight : offsetLeft}%`,
|
||||
right: `${isRTL ? offsetLeft : offsetRight}%`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})()
|
||||
) : null
|
||||
}
|
||||
if (aux !== '') roundedLeft = 'rounded-l';
|
||||
else roundedLeft = '';
|
||||
}
|
||||
const redress = '(0.375rem / 2)';
|
||||
return (
|
||||
<div
|
||||
className={`h-1.5 ${width} bg-gray-200 absolute inset-x-0 z-10 ${roundedLeft} ${roundedRight}`}
|
||||
style={{
|
||||
top: `calc(50% - ${redress})`,
|
||||
left: `${isRTL ? offsetRight : offsetLeft}%`,
|
||||
right: `${isRTL ? offsetLeft : offsetRight}%`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})()
|
||||
) : null }
|
||||
{ userEmojisInPeriod.map((emoji) => {
|
||||
const offset = ((emoji.sentOn - period) * 100) / spanMinutes;
|
||||
const offset = ((emoji.sentOn - period.start) * 100)
|
||||
/ (period.end - period.start);
|
||||
const origin = document.dir === 'rtl' ? 'right' : 'left';
|
||||
const onFirstCell = period === firstRegisteredOnTime;
|
||||
// font-size / 2 + padding right/left + border-width
|
||||
const redress = '(0.875rem / 2 + 0.25rem + 2px)';
|
||||
return (
|
||||
<div
|
||||
className={`flex absolute p-1 border-white border-2 rounded-full text-sm z-20 bg-purple-500 text-purple-200 ${onFirstCell ? 'emojiOnFirstCell' : ''}`}
|
||||
className="flex absolute p-1 border-white border-2 rounded-full text-sm z-20 bg-purple-500 text-purple-200 timeline-emoji"
|
||||
role="status"
|
||||
style={{
|
||||
top: `calc(50% - ${redress})`,
|
||||
@ -221,7 +249,7 @@ class StatusTable extends React.Component {
|
||||
<i className={`${emojiConfigs[emoji.name].icon} text-sm bbb-icon-timeline`} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</td>
|
||||
);
|
||||
}) }
|
||||
|
Loading…
Reference in New Issue
Block a user