diff --git a/library/Notifications/Widget/Calendar/BaseGrid.php b/library/Notifications/Widget/Calendar/BaseGrid.php index 5c9c03282..40711fae3 100644 --- a/library/Notifications/Widget/Calendar/BaseGrid.php +++ b/library/Notifications/Widget/Calendar/BaseGrid.php @@ -242,9 +242,15 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void } $this->extraEntriesCount = []; + /** @var Entry $entry */ foreach ($occupiedCells as $entry) { $continuation = false; $rows = $occupiedCells->getInfo(); + $entryStartDate = clone $entry->getStart(); + $entryEndDate = clone $entry->getEnd(); + $startText = null; + $endText = null; + foreach ($rows as $row => $hours) { list($rowStart, $rowSpan) = $rowPlacements[spl_object_id($entry)][$row]; $rowEnd = $rowStart + $rowSpan; @@ -256,11 +262,11 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void // Calculate number of entries that are not displayed in the grid for each date if ($rowStart > $row + $sectionsPerStep) { - $stepRow = $rowStart - $this->getSectionsPerStep(); - $startOffset = $this->getStepOffset($stepRow, $gridArea[1] - 1); - $endOffset = $this->getStepOffset($stepRow, $gridArea[3] - 2); + $startOffset = $this->getStepOffset($rowStart - $sectionsPerStep, $colStart - 1); + $endOffset = $this->getStepOffset($rowEnd - $sectionsPerStep, $colEnd - 2); $startDate = (clone $this->getGridStart())->add(new DateInterval("P$startOffset" . 'D')); $duration = $endOffset - $startOffset; + for ($i = 0; $i <= $duration; $i++) { $startDate->add(new DateInterval("P$i" . 'D')); $countIdx = $startDate->format('Y-m-d'); @@ -274,11 +280,137 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void continue; } - $style->add(".$entryClass", [ - 'grid-area' => sprintf('~"%d / %d / %d / %d"', ...$gridArea), - 'background-color' => $entry->getAttendee()->getColor() . dechex((int) (256 * 0.1)), - 'border-color' => $entry->getAttendee()->getColor() . dechex((int) (256 * 0.5)) - ]); + $startOffset = $this->getStepOffset($rowStart, $colStart - 1); + $endOffset = $this->getStepOffset($rowEnd - 2, $colEnd - 2); + + $startDate = clone $this->getGridStart(); + $startDate->add(new DateInterval("P$startOffset" . 'D')); + + $noOfVisuallyConnectedDays = intval($this->getNoOfVisuallyConnectedHours() / 24); + if ($noOfVisuallyConnectedDays === 7) { + $rowStartDay = clone $gridStartsAt; + $startDaysOffset = $startOffset - $startOffset % 7; + $rowStartDay = $rowStartDay->add(new DateInterval('P' . $startDaysOffset . 'D')); + + $endDaysOffset = $endOffset + (6 - $endOffset % 7); + $rowEndDay = clone $gridStartsAt; + $rowEndDay = $rowEndDay->add(new DateInterval('P' . $endDaysOffset . 'D')); + $gradDirection = 'horizontal'; + } else { + $rowStartDay = clone $startDate; + $rowEndDay = clone $startDate; + $gradDirection = 'vertical'; + } + + $rowEndDay->setTime(23, 59); + $alpha = dechex((int) (256 * 0.25)); + + if ($entryStartDate < $rowStartDay && $entryEndDate > $rowEndDay) { + // fade both ends + // Add start text + // add end text as well + $gradientTowards = $gradDirection === 'horizontal' ? 'right' : 'top'; + $oppDirection = $gradientTowards === 'top' ? 'bottom' : 'left'; + $style->add(".$entryClass", [ + 'grid-area' => sprintf('~"%d / %d / %d / %d"', ...$gridArea), + 'background' => sprintf( + '~"linear-gradient(to %3$s, transparent, %1$s%2$s 0.5em, %1$s%2$s calc(100%% - 0.5em), ' + . 'transparent)"', + $entry->getAttendee()->getColor(), + $alpha, + $gradientTowards + ), + 'border-color' => $entry->getAttendee()->getColor() . dechex((int) (256 * 0.5)), + 'border-left' => 'none', + "border-$gradientTowards" => 'none', + "border-$oppDirection" => 'none' + ]); + + if ( + $rowStartDay->format('Y-m-d') === $gridStartsAt->format('Y-m-d') + && $entryStartDate < $gridStartsAt + ) { + $startText = HtmlElement::create( + 'div', + ['class' => 'starts-at'], + $this->translate(sprintf('starts %s', $entryStartDate->format('d/m/y H:i'))) + ); + } + + $rowEndDay->add(new DateInterval('P1D')); + if ( + $rowEndDay->format('Y-m-d') === $gridEndsAt->format('Y-m-d') + && $entryEndDate > $gridEndsAt + ) { + $endText = HtmlElement::create( + 'div', + ['class' => 'ends-at'], + $this->translate(sprintf('ends %s', $entryEndDate->format('d/m/y H:i'))) + ); + } + } elseif ($entryEndDate > $rowEndDay) { + // fade the end + $gradientTowards = $gradDirection === 'horizontal' ? 'right' : 'bottom'; + $borderRadius = $gradDirection === 'horizontal' ? '0.25em 0 0 0.25em' : '0.25em 0.25em 0 0'; + $style->add(".$entryClass", [ + 'grid-area' => sprintf('~"%d / %d / %d / %d"', ...$gridArea), + 'background' => sprintf( + '~"linear-gradient(to %s, %s%s calc(100%% - 1em), transparent)"', + $gradientTowards, + $entry->getAttendee()->getColor(), + $alpha + ), + 'border-color' => $entry->getAttendee()->getColor() . dechex((int) (256 * 0.5)), + "border-$gradientTowards" => 'none', + 'border-radius' => $borderRadius + ]); + + $rowEndDay->add(new DateInterval('P1D')); + if ( + $rowEndDay->format('Y-m-d') === $gridEndsAt->format('Y-m-d') + && $entryEndDate > $gridEndsAt + ) { + $endText = HtmlElement::create( + 'span', + ['class' => 'ends-at'], + $this->translate(sprintf('ends %s', $entryEndDate->format('d/m/y H:i'))) + ); + } + } elseif ($entryStartDate < $rowStartDay) { + // fade the start + // add end text as well + $gradientTowards = $gradDirection === 'horizontal' ? 'left' : 'top'; + $borderRadius = $gradDirection === 'horizontal' ? '0 0.25em 0.25em 0' : '0 0 0.25em 0.25em'; + $style->add(".$entryClass", [ + 'grid-area' => sprintf('~"%d / %d / %d / %d"', ...$gridArea), + 'background' => sprintf( + '~"linear-gradient(to %s, %s%s calc(100%% - 1em), transparent)"', + $gradientTowards, + $entry->getAttendee()->getColor(), + $alpha + ), + 'border-color' => $entry->getAttendee()->getColor() . dechex((int) (256 * 0.5)), + "border-$gradientTowards" => 'none', + 'border-radius' => $borderRadius + ]); + + if ( + $rowStartDay->format('Y-m-d') === $gridStartsAt->format('Y-m-d') + && $entryStartDate < $gridStartsAt + ) { + $startText = HtmlElement::create( + 'div', + ['class' => 'starts-at'], + $this->translate(sprintf('starts %s', $entryStartDate->format('d/m/y H:i'))) + ); + } + } else { + $style->add(".$entryClass", [ + 'grid-area' => sprintf('~"%d / %d / %d / %d"', ...$gridArea), + 'background-color' => $entry->getAttendee()->getColor() . $alpha, + 'border-color' => $entry->getAttendee()->getColor() . dechex((int) (256 * 0.5)) + ]); + } $entryHtml = new HtmlElement( 'div', @@ -291,7 +423,10 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void 'data-col-end' => $gridArea[3] ]) ); + + $entryHtml->add($startText); $this->assembleEntry($entryHtml, $entry, $continuation); + $entryHtml->add($endText); $overlay->addHtml($entryHtml); $continuation = true; diff --git a/public/css/calendar.less b/public/css/calendar.less index 7f8f5630a..4376373b6 100644 --- a/public/css/calendar.less +++ b/public/css/calendar.less @@ -318,6 +318,15 @@ text-decoration: none; } + .starts-at, + .ends-at { + mix-blend-mode: multiply; + color: @text-color-light; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .extra-count:hover { text-decoration: underline; }