diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index db5722fd49..cf07c0cc61 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -3461,7 +3461,7 @@ celerity_register_resource_map(array( ), 'phabricator-timeline-view-css' => array( - 'uri' => '/res/725f6b17/rsrc/css/layout/phabricator-timeline-view.css', + 'uri' => '/res/79b6d385/rsrc/css/layout/phabricator-timeline-view.css', 'type' => 'css', 'requires' => array( diff --git a/src/applications/uiexample/examples/PhabricatorTimelineExample.php b/src/applications/uiexample/examples/PhabricatorTimelineExample.php index b1c53f6d60..a3bca00de7 100644 --- a/src/applications/uiexample/examples/PhabricatorTimelineExample.php +++ b/src/applications/uiexample/examples/PhabricatorTimelineExample.php @@ -111,11 +111,64 @@ final class PhabricatorTimelineExample extends PhabricatorUIExample { ->setColor($color); } + $vhandle = $handle->renderLink(); + + $group_event = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle(pht('%s went to the store.', $vhandle)); + + $group_event->addEventToGroup( + id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle(pht('%s bought an apple.', $vhandle)) + ->setColor('green') + ->setIcon('check')); + + $group_event->addEventToGroup( + id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle(pht('%s bought a banana.', $vhandle)) + ->setColor('yellow') + ->setIcon('check')); + + $group_event->addEventToGroup( + id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle(pht('%s bought a cherry.', $vhandle)) + ->setColor('red') + ->setIcon('check')); + + $group_event->addEventToGroup( + id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle(pht('%s paid for his goods.', $vhandle))); + + $group_event->addEventToGroup( + id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle(pht('%s returned home.', $vhandle)) + ->setIcon('home') + ->setColor('blue')); + + $group_event->addEventToGroup( + id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle(pht('%s related on his adventures.', $vhandle)) + ->appendChild( + pht( + 'Today, I went to the store. I bought an apple. I bought a '. + 'banana. I bought a cherry. I paid for my goods, then I returned '. + 'home.'))); + + $events[] = $group_event; + $anchor = 0; - foreach ($events as $event) { - $event->setUser($user); - $event->setDateCreated(time() + ($anchor * 60 * 8)); - $event->setAnchor(++$anchor); + foreach ($events as $group) { + foreach ($group->getEventGroup() as $event) { + $event->setUser($user); + $event->setDateCreated(time() + ($anchor * 60 * 8)); + $event->setAnchor(++$anchor); + } } $timeline = id(new PhabricatorTimelineView()); diff --git a/src/view/layout/PhabricatorTimelineEventView.php b/src/view/layout/PhabricatorTimelineEventView.php index 0807edcdf4..e190c8f73b 100644 --- a/src/view/layout/PhabricatorTimelineEventView.php +++ b/src/view/layout/PhabricatorTimelineEventView.php @@ -14,6 +14,7 @@ final class PhabricatorTimelineEventView extends AphrontView { private $isEdited; private $transactionPHID; private $isPreview; + private $eventGroup = array(); public function setTransactionPHID($transaction_phid) { $this->transactionPHID = $transaction_phid; @@ -99,7 +100,17 @@ final class PhabricatorTimelineEventView extends AphrontView { return $this; } - public function render() { + public function getEventGroup() { + return array_merge(array($this), $this->eventGroup); + } + + public function addEventToGroup(PhabricatorTimelineEventView $event) { + $this->eventGroup[] = $event; + return $this; + } + + + public function renderEventTitle() { $title = $this->title; if (($title === null) && !$this->hasChildren()) { $title = ''; @@ -115,10 +126,16 @@ final class PhabricatorTimelineEventView extends AphrontView { if ($this->icon) { $title_classes[] = 'phabricator-timeline-title-with-icon'; + $fill_classes = array(); + $fill_classes[] = 'phabricator-timeline-icon-fill'; + if ($this->color) { + $fill_classes[] = 'phabricator-timeline-icon-fill-'.$this->color; + } + $icon = phutil_tag( 'span', array( - 'class' => 'phabricator-timeline-icon-fill', + 'class' => implode(' ', $fill_classes), ), phutil_tag( 'span', @@ -134,9 +151,21 @@ final class PhabricatorTimelineEventView extends AphrontView { array( 'class' => implode(' ', $title_classes), ), - array($title, $extra)); + array($icon, $title, $extra)); + } - $title = array($icon, $title); + return $title; + } + + public function render() { + + $group_titles = array(); + $group_children = array(); + foreach ($this->getEventGroup() as $event) { + $group_titles[] = $event->renderEventTitle(); + if ($event->hasChildren()) { + $group_children[] = $event->renderChildren(); + } } $wedge = phutil_tag( @@ -160,43 +189,53 @@ final class PhabricatorTimelineEventView extends AphrontView { $classes = array(); $classes[] = 'phabricator-timeline-event-view'; - $classes[] = 'phabricator-timeline-border'; - if ($this->hasChildren()) { + if ($group_children) { $classes[] = 'phabricator-timeline-major-event'; $content = phutil_tag( 'div', array( - 'class' => implode(' ', $content_classes), + 'class' => 'phabricator-timeline-inner-content', ), - phutil_tag( - 'div', - array( - 'class' => 'phabricator-timeline-inner-content', - ), - array( - $title, - phutil_tag( - 'div', - array( - 'class' => 'phabricator-timeline-core-content', - ), - $this->renderChildren()), - ))); - $content = array($image, $wedge, $content); + array( + $group_titles, + phutil_tag( + 'div', + array( + 'class' => 'phabricator-timeline-core-content', + ), + $group_children), + )); } else { $classes[] = 'phabricator-timeline-minor-event'; - $content = phutil_tag( - 'div', - array( - 'class' => implode(' ', $content_classes), - ), - array($image, $wedge, $title)); + $content = $group_titles; } + $content = phutil_tag( + 'div', + array( + 'class' => 'phabricator-timeline-group phabricator-timeline-border', + ), + $content); + + $content = phutil_tag( + 'div', + array( + 'class' => implode(' ', $content_classes), + ), + array($image, $wedge, $content)); + $outer_classes = $this->classes; $outer_classes[] = 'phabricator-timeline-shell'; - if ($this->color) { - $outer_classes[] = 'phabricator-timeline-'.$this->color; + $color = null; + foreach ($this->getEventGroup() as $event) { + if ($event->color) { + $color = $event->color; + break; + } + } + + if ($color) { + $outer_classes[] = 'phabricator-timeline-'.$color; } $sigil = null; diff --git a/webroot/rsrc/css/layout/phabricator-timeline-view.css b/webroot/rsrc/css/layout/phabricator-timeline-view.css index afd4b6f869..8d351b0942 100644 --- a/webroot/rsrc/css/layout/phabricator-timeline-view.css +++ b/webroot/rsrc/css/layout/phabricator-timeline-view.css @@ -6,11 +6,10 @@ background: #eeedf0; } -.phabricator-timeline-event-view { - border-width: 0 0 0 3px; +.phabricator-timeline-group { + border-width: 0 3px; border-style: solid; border-color: #c0c5d1; - } .device-desktop .phabricator-timeline-event-view { @@ -21,7 +20,9 @@ .device-desktop .phabricator-timeline-spacer { min-height: 20px; - border-right-width: 0; + border-width: 0 0 0 3px; + border-style: solid; + border-color: #c0c5d1; } .device-desktop .phabricator-timeline-major-event, @@ -91,6 +92,7 @@ .phabricator-timeline-title { padding: 0 5px; overflow-x: auto; + overflow-y: hidden; } .phabricator-timeline-title-with-icon { @@ -112,9 +114,6 @@ position: relative; margin-left: 3px; margin-right: 3px; - - border-right-width: 3px; - border-right-style: solid; } .device .phabricator-timeline-image { @@ -131,7 +130,7 @@ width: 30px; height: 30px; background-color: #c0c5d1; - top: 0; + top: -1px; left: -3px; } @@ -199,43 +198,43 @@ border-color: #333; } -.phabricator-timeline-red .phabricator-timeline-icon-fill { +.phabricator-timeline-icon-fill-red { background-color: {$red}; } -.phabricator-timeline-orange .phabricator-timeline-icon-fill { +.phabricator-timeline-icon-fill-orange { background-color: {$orange}; } -.phabricator-timeline-yellow .phabricator-timeline-icon-fill { +.phabricator-timeline-icon-fill-yellow { background-color: {$yellow}; } -.phabricator-timeline-green .phabricator-timeline-icon-fill { +.phabricator-timeline-icon-fill-green { background-color: {$green}; } -.phabricator-timeline-sky .phabricator-timeline-icon-fill { +.phabricator-timeline-icon-fill-sky { background-color: {$sky}; } -.phabricator-timeline-blue .phabricator-timeline-icon-fill { +.phabricator-timeline-icon-fill-blue { background-color: {$blue}; } -.phabricator-timeline-indigo .phabricator-timeline-icon-fill { +.phabricator-timeline-icon-fill-indigo { background-color: {$indigo}; } -.phabricator-timeline-violet .phabricator-timeline-icon-fill { +.phabricator-timeline-icon-fill-violet { background-color: {$violet}; } -.phabricator-timeline-grey .phabricator-timeline-icon-fill { +.phabricator-timeline-icon-fill-grey { background-color: #888; } -.phabricator-timeline-black .phabricator-timeline-icon-fill { +.phabricator-timeline-icon-fill-black { background-color: #333; }