I am currently working on developing a table preview feature to display events. I previously sought assistance here regarding positioning elements within the table and successfully resolved that issue. Applying the same principles, I am now attempting to create a preview based on specific hour durations corresponding to each column location in order to populate an events list.
However, I am encountering problems with the preview functionality as it inconsistently appears and disappears. Despite spending several hours trying to troubleshoot the issue, I have not been able to find a solution. Additionally, once the preview is displayed, clicking on the event should save it.
What is causing the erratic blinking behavior of the preview?
Here is the code I'm using:
Stackblitz: https://stackblitz.com/edit/angular-ivy-k22v8c?file=src/app/app.component.ts
HTML:
<div class="calendar-container">
<table
(mouseenter)="activatePreview()"
(mouseleave)="deactivatePreview()"
(mousemove)="calculatePreview($event)"
>
<tr>
<th></th>
<th class="cell-place" *ngFor="let place of places">{{ place }}</th>
</tr>
<tr *ngFor="let hour of hours">
<td class="cell-hour">{{ hour }}</td>
<td class="cell" *ngFor="let place of places"></td>
</tr>
</table>
<div *ngIf="previewActive">
<div class="preview" [ngStyle]="preview.position">
<p>{{ preview.location }}</p>
</div>
</div>
<div *ngFor="let event of events">
<div class="event" [ngStyle]="event.position">
<p>{{ event.location }}</p>
</div>
</div>
</div>
TS:
export class AppComponent implements AfterViewInit {
hours = [
'8:00 AM',
'9:00 AM',
'10:00 AM',
'11:00 AM',
'12:00 PM',
'1:00 PM',
'2:00 PM',
'3:00 PM',
'4:00 PM',
'5:00 PM',
];
places = ['P1', 'P2', 'P3', 'P4', 'P5'];
events: any = [];
cellWidth = 0;
cellWidthHour = 0;
cellHeight = 0;
previewActive = false;
previewDuration = 2;
preview: any = {};
currentCell = null;
activatePreview() {
this.previewActive = true;
}
deactivatePreview() {
this.previewActive = false;
this.currentCell = null;
this.preview = {};
}
calculatePreview(event: any) {
if (!this.previewActive) {
return;
}
// Get the position of the cursor
const x = event.clientX;
const y = event.clientY;
// Calculate the column (location) of the preview
const columns = document.getElementsByClassName('cell-place');
let column;
for (const col of Array.from(columns)) {
if (
col.getBoundingClientRect().left <= x &&
x <= col.getBoundingClientRect().right
) {
column = col;
break;
}
}
if (!column) {
return;
}
console.log(column);
// Calculate the start and end times of the preview
const rows = document.getElementsByClassName('cell-hour');
let startIndex = -1;
let endIndex = -1;
for (let i = 0; i < rows.length; i++) {
const row = rows.item(i);
if (
row.getBoundingClientRect().top <= y &&
y <= row.getBoundingClientRect().bottom
) {
startIndex = i;
endIndex = i + this.previewDuration - 1;
break;
}
}
console.log(startIndex, endIndex);
// Check if the preview goes beyond the limit of the table
if (startIndex === -1 || endIndex === -1 || endIndex >= rows.length) {
return;
}
// Check if the cursor is in a new cell
const newCell = startIndex + '-' + endIndex;
console.log(newCell);
if (newCell === this.currentCell) {
return;
}
this.currentCell = newCell;
// Update the preview based on the calculations
const startTime = this.hours[startIndex];
const endTime = this.hours[endIndex];
const location = column.innerHTML;
this.preview = { startTime, endTime, location };
this.preview.position = this.calculateEventPosition(
startTime,
endTime,
location
);
console.log(this.preview);
}
constructor() {}
ngAfterViewInit() {
// this.getCellSize();
this.createEvent('11:00 AM', '12:00 PM', 'P3');
}
createEvent(startTime: string, endTime: string, location: string) {
const event: any = { startTime, endTime, location };
event.position = this.calculateEventPosition(startTime, endTime, location);
this.events.push(event);
}
calculateEventPosition(startTime: string, endTime: string, location: string) {
const rect = document
.getElementsByTagName('table')[0]
.getBoundingClientRect();
const columns = document.getElementsByClassName('cell-place');
const rows = document.getElementsByClassName('cell-hour');
const column = Array.from(columns).find((x) => x.innerHTML == location);
const start = rows.item(this.hours.indexOf(startTime));
const end = rows.item(this.hours.indexOf(endTime));
const left = column.getBoundingClientRect().left - rect.left;
const top = start.getBoundingClientRect().top - rect.top;
const width = column.getBoundingClientRect().width;
const height =
end.getBoundingClientRect().top - start.getBoundingClientRect().top;
return {
height: height + 'px',
top: top + 'px',
left: left + 'px',
width: width + 'px',
};
}
}
CSS:
.calendar-container {
position: relative;
}
table {
border-collapse: collapse;
width: 100%;
}
td,
th {
border: 1px solid #dddddd;
text-align: center;
}
table td {
height: 25px;
}
.event {
box-sizing: border-box;
position: absolute;
background-color: #f2f2f2;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
.preview {
box-sizing: border-box;
position: absolute;
background-color: yellow;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
z-index: 9;
}
I have made progress in setting up the preview mechanism but it's not functioning correctly, exhibiting flickering behavior and occasional delays. The preview should strictly pertain to hourly increments, and I am uncertain about how to implement click-to-save functionality based on event duration correlated to the location column.