Przeglądaj źródła

Fix banner overlap at week-end: suppress degenerate 1-col clipped slivers

When multiple reservations all clip at the right edge of a week row,
a 1-column sliver (only the last column visible) has no gap from the
earlier banner because neither has rightBleed. Suppress the sliver so
only the earlier, wider banner is shown in the row. The suppressed
reservation still appears as a carry-over in the following week row.

Also covers the same-day checkout/checkin case at week-end where a
non-clipped banner ends the same day the 1-col clipped banner starts
(gap collapses to ~4px, now suppressed instead).
ZiB 4 miesięcy temu
rodzic
commit
e8078d8da0
1 zmienionych plików z 18 dodań i 16 usunięć
  1. 18 16
      room_cal/vue/src/lib/calendar-grid/index.js

+ 18 - 16
room_cal/vue/src/lib/calendar-grid/index.js

@@ -75,14 +75,18 @@ export function bannersForWeek(week, spans) {
   const weekEnd   = week[6];
   const banners   = [];
 
-  // Timestamps of span ends that fall within the week (these banners have rightBleed=0.8).
-  // Used to detect same-day checkout/checkin at the week-end boundary, where the normal
-  // rightBleed+leftInset gap collapses to ~4px because both banners share the same column.
+  // A 1-column clipped banner (only the last column of the week is visible) should be
+  // suppressed when another banner already covers that column, because there is not enough
+  // space for a legible gap. It reappears as a carry-over in the following week row.
+  // Two overlap scenarios trigger suppression:
+  //   (a) an earlier-starting clipped banner already extends to the same last column
+  //   (b) a non-clipped banner ends on the same day (gap collapses to ~4px)
   const endsInWeek = new Set(
-    spans
-      .filter(s => s.end >= weekStart && s.end <= weekEnd)
-      .map(s => +s.end)
+    spans.filter(s => s.end >= weekStart && s.end <= weekEnd).map(s => +s.end)
   );
+  const clippedStartTimes = spans
+    .filter(s => s.start >= weekStart && s.start <= weekEnd && s.end > weekEnd)
+    .map(s => +s.start);
 
   for (const span of spans) {
     if (span.end < weekStart || span.start > weekEnd) continue;
@@ -92,19 +96,17 @@ export function bannersForWeek(week, spans) {
 
     const startCol   = daysBetween(weekStart, clipStart);
     const spanCols   = daysBetween(clipStart, clipEnd) + 1;
-    const rightBleed = span.end <= weekEnd ? 0.8 : 0;
+    const leftInset  = span.start >= weekStart ? 0.2 : 0;
+    const rightBleed = span.end   <= weekEnd   ? 0.8 : 0;
 
-    // A clipped banner (rightBleed=0) that starts on the same day a non-clipped banner
-    // ends would normally share the same pixel column boundary, leaving only a 4px gap.
-    // Shift it right by an extra 0.8 columns so the gap matches a normal transition.
-    const sameDayAfterCheckout =
-      rightBleed === 0 && span.start >= weekStart && endsInWeek.has(+clipStart);
-    const leftInset = span.start >= weekStart ? (sameDayAfterCheckout ? 1.0 : 0.2) : 0;
-
-    const widthPct = ((spanCols - leftInset - rightBleed) / 7) * 100;
-    if (widthPct <= 0) continue;  // degenerate (e.g. single-col clipped same-day): skip, shows in next row
+    if (rightBleed === 0 && spanCols === 1 && span.start >= weekStart) {
+      const precedingClipped = clippedStartTimes.some(t => t < +span.start);
+      const sameDayNonClipped = endsInWeek.has(+clipStart);
+      if (precedingClipped || sameDayNonClipped) continue;
+    }
 
     const leftPct  = ((startCol + leftInset) / 7) * 100;
+    const widthPct = ((spanCols - leftInset - rightBleed) / 7) * 100;
 
     banners.push({
       ...span,