|
@@ -75,14 +75,18 @@ export function bannersForWeek(week, spans) {
|
|
|
const weekEnd = week[6];
|
|
const weekEnd = week[6];
|
|
|
const banners = [];
|
|
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(
|
|
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) {
|
|
for (const span of spans) {
|
|
|
if (span.end < weekStart || span.start > weekEnd) continue;
|
|
if (span.end < weekStart || span.start > weekEnd) continue;
|
|
@@ -92,19 +96,17 @@ export function bannersForWeek(week, spans) {
|
|
|
|
|
|
|
|
const startCol = daysBetween(weekStart, clipStart);
|
|
const startCol = daysBetween(weekStart, clipStart);
|
|
|
const spanCols = daysBetween(clipStart, clipEnd) + 1;
|
|
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 leftPct = ((startCol + leftInset) / 7) * 100;
|
|
|
|
|
+ const widthPct = ((spanCols - leftInset - rightBleed) / 7) * 100;
|
|
|
|
|
|
|
|
banners.push({
|
|
banners.push({
|
|
|
...span,
|
|
...span,
|