From 3424b335fe241df0ab7c28fc6134a342367b6e4a Mon Sep 17 00:00:00 2001 From: raviks789 <33730024+raviks789@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:07:44 +0200 Subject: [PATCH] Improve visual representation of the entries Entries starting the previous day or ending the next day must be distinguishable. --- .../Widget/Calendar/BaseGrid.php | 112 +++++++++++++++--- public/css/calendar.less | 87 ++++++++++++++ 2 files changed, 183 insertions(+), 16 deletions(-) diff --git a/library/Notifications/Widget/Calendar/BaseGrid.php b/library/Notifications/Widget/Calendar/BaseGrid.php index 1e01dc2e5..099ad050d 100644 --- a/library/Notifications/Widget/Calendar/BaseGrid.php +++ b/library/Notifications/Widget/Calendar/BaseGrid.php @@ -235,9 +235,15 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void } $this->extraEntriesCount = []; + /** @var Entry $entry */ foreach ($occupiedCells as $entry) { $continuation = false; + /** @var int[][] $rows */ $rows = $occupiedCells->getInfo(); + $fromPrevGrid = $gridStartsAt > $entry->getStart(); + $rowCount = count($rows); + $toNextGrid = false; + foreach ($rows as $row => $hours) { list($rowStart, $rowSpan) = $rowPlacements[spl_object_id($entry)][$row]; $colStart = min($hours); @@ -249,6 +255,7 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void $endOffset = (int) (($row / $sectionsPerStep) * ($gridBorderAt / 48) + $colEnd / 48); $startDate = (clone $this->getGridStart())->add(new DateInterval("P$startOffset" . 'D')); $duration = $endOffset - $startOffset; + for ($i = 0; $i <= $duration; $i++) { $countIdx = $startDate->format('Y-m-d'); if (! isset($this->extraEntriesCount[$countIdx])) { @@ -271,17 +278,49 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void ); $entryClass = 'area-' . implode('-', $gridArea); + $entryColor = $entry->getAttendee()->getColor(); + $lastRow = $rowCount == 1; + + if ($lastRow) { + $toNextGrid = $gridEndsAt < $entry->getEnd(); + } + + $backward = $continuation || $fromPrevGrid; + $forward = ! $lastRow || $toNextGrid; + + if ($forward && $backward) { + $gradientClass = 'two-way-gradient'; + } elseif ($backward) { + $gradientClass = 'opening-gradient'; + } elseif ($forward) { + $gradientClass = 'ending-gradient'; + } else { + $gradientClass = 'no-gradient'; + } $style->add(".$entryClass", [ + '--entry-color' => $entryColor . dechex((int) (256 * 0.1)), '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)) ]); + $startText = null; + $endText = null; + if ($fromPrevGrid) { + $startText = $this->translate( + sprintf('starts %s', $entry->getStart()->format('d/m/y')) + ); + } + + if ($toNextGrid) { + $endText = $this->translate( + sprintf('ends %s', $entry->getEnd()->format('d/m/y H:i')) + ); + } $entryHtml = new HtmlElement( 'div', Attributes::create([ - 'class' => ['entry', $entryClass], + 'class' => ['entry', $gradientClass, $entryClass], 'data-entry-id' => $entry->getId(), 'data-row-start' => $gridArea[0], 'data-col-start' => $gridArea[1], @@ -289,16 +328,24 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void 'data-col-end' => $gridArea[3] ]) ); - $this->assembleEntry($entryHtml, $entry, $continuation); + + $this->assembleEntry($entryHtml, $entry, $startText, $endText, $continuation); $overlay->addHtml($entryHtml); $continuation = true; + $fromPrevGrid = false; + $rowCount -= 1; } } } - protected function assembleEntry(BaseHtmlElement $html, Entry $entry, bool $isContinuation): void - { + protected function assembleEntry( + BaseHtmlElement $html, + Entry $entry, + ?string $startText, + ?string $endText, + bool $isContinuation + ): void { if (($url = $entry->getUrl()) !== null) { $entryContainer = new Link(null, $url); $html->addHtml($entryContainer); @@ -307,6 +354,36 @@ protected function assembleEntry(BaseHtmlElement $html, Entry $entry, bool $isCo } $title = new HtmlElement('div', Attributes::create(['class' => 'title'])); + $content = new HtmlElement( + 'div', + Attributes::create( + [ + 'class' => 'content' + ] + ) + ); + + $titleAttr = $entry->getStart()->format('H:i') + . ' | ' . $entry->getAttendee()->getName() + . ': ' . $entry->getDescription(); + + if ($startText) { + $title->addHtml( + HtmlElement::create( + 'div', + ['class' => 'starts-at'], + $startText + ) + ); + $titleAttr = $startText . ' ' . $titleAttr; + } + + if ($endText) { + $titleAttr = $titleAttr . ' | ' . $endText; + } + + $content->addAttributes(['title' => $titleAttr]); + if (! $isContinuation) { $title->addHtml(new HtmlElement( 'time', @@ -326,16 +403,7 @@ protected function assembleEntry(BaseHtmlElement $html, Entry $entry, bool $isCo ) ); - $entryContainer->addHtml(new HtmlElement( - 'div', - Attributes::create( - [ - 'class' => 'content', - 'title' => $entry->getStart()->format('H:i') - . ' | ' . $entry->getAttendee()->getName() - . ': ' . $entry->getDescription() - ] - ), + $content->addHtml( $title, new HtmlElement( 'div', @@ -346,7 +414,19 @@ protected function assembleEntry(BaseHtmlElement $html, Entry $entry, bool $isCo Text::create($entry->getDescription()) ) ) - )); + ); + + if ($endText) { + $content->addHtml( + HtmlElement::create( + 'div', + ['class' => 'ends-at'], + $endText + ) + ); + } + + $entryContainer->addHtml($content); } protected function roundToNearestThirtyMinute(DateTime $time): DateTime diff --git a/public/css/calendar.less b/public/css/calendar.less index 7867810c7..f38bd8d65 100644 --- a/public/css/calendar.less +++ b/public/css/calendar.less @@ -1,4 +1,5 @@ /* Layout */ +@entry-color: var(--entry-color, @default-bg); .calendar-controls { display: flex; @@ -100,6 +101,7 @@ .title { display: flex; flex-wrap: wrap; + align-content: flex-start; flex: 0 1 auto; column-gap: .5em; overflow: hidden; @@ -120,6 +122,7 @@ .description { flex: 1 1 auto; overflow: hidden; + mix-blend-mode: screen; p { margin: 0; @@ -147,6 +150,36 @@ grid-template-rows: repeat(@weeks * @rowsPerDay, 1fr); grid-template-columns: repeat(@days * @columnsPerDay, minmax(0, 1fr)); overflow: hidden; + + .ends-at { + flex: 1 1 0; + text-align: right; + } + } + + .entry { + &.two-way-gradient { + border-radius: 0; + border-right: none; + border-left: none; + background: linear-gradient(to right, transparent, @entry-color 0.5em, @entry-color calc(100% - 0.5em), transparent); + } + + &.opening-gradient { + border-radius: 0 0.25em 0.25em 0; + border-left: none; + background: linear-gradient(to left, @entry-color calc(100% - 1em), transparent); + } + + &.ending-gradient { + border-radius: 0.25em 0 0 0.25em; + border-right: none; + background: linear-gradient(to right, @entry-color calc(100% - 1em), transparent); + } + + &.no-gradient { + background-color: @entry-color; + } } .step { @@ -183,6 +216,10 @@ .grid, .overlay { border-left: none; + + .title { + width: 100%; + } } .step { @@ -220,6 +257,45 @@ } } +.calendar-grid.week, +.calendar-grid.day { + .entry { + .content { + flex-direction: column; + align-content: flex-start; + justify-content: flex-end; + + .ends-at { + text-align: right; + width: 100%; + } + } + + &.two-way-gradient { + border-radius: 0; + border-top: none; + border-bottom: none; + background: linear-gradient(to bottom, transparent, @entry-color 0.5em, @entry-color calc(100% - 0.5em), transparent); + } + + &.opening-gradient { + border-radius: 0 0 0.25em 0.25em; + border-top: none; + background: linear-gradient(to top, @entry-color calc(100% - 1em), transparent); + } + + &.ending-gradient { + border-radius: 0.25em 0.25em 0 0; + border-bottom: none; + background: linear-gradient(to bottom, @entry-color calc(100% - 1em), transparent); + } + + &.no-gradient { + background-color: @entry-color; + } + } +} + .calendar-grid { display: grid; @@ -298,6 +374,17 @@ > a { text-decoration: none; } + + .starts-at, + .ends-at { + font-weight: normal; + color: @text-color-light; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 11/12em; + line-height: 12/11 * 1.5; + } } .entry {