diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2423908015..ade3a48d7a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1113,7 +1113,10 @@ phutil_register_library_map(array( 'PhabricatorTimelineDAO' => 'infrastructure/daemon/timeline/storage/PhabricatorTimelineDAO.php', 'PhabricatorTimelineEvent' => 'infrastructure/daemon/timeline/storage/PhabricatorTimelineEvent.php', 'PhabricatorTimelineEventData' => 'infrastructure/daemon/timeline/storage/PhabricatorTimelineEventData.php', + 'PhabricatorTimelineEventView' => 'view/layout/PhabricatorTimelineEventView.php', + 'PhabricatorTimelineExample' => 'applications/uiexample/examples/PhabricatorTimelineExample.php', 'PhabricatorTimelineIterator' => 'infrastructure/daemon/timeline/cursor/PhabricatorTimelineIterator.php', + 'PhabricatorTimelineView' => 'view/layout/PhabricatorTimelineView.php', 'PhabricatorTimer' => 'applications/countdown/storage/PhabricatorTimer.php', 'PhabricatorTransactionView' => 'view/layout/PhabricatorTransactionView.php', 'PhabricatorTransformedFile' => 'applications/files/storage/PhabricatorTransformedFile.php', @@ -2295,7 +2298,10 @@ phutil_register_library_map(array( 'PhabricatorTimelineDAO' => 'PhabricatorLiskDAO', 'PhabricatorTimelineEvent' => 'PhabricatorTimelineDAO', 'PhabricatorTimelineEventData' => 'PhabricatorTimelineDAO', + 'PhabricatorTimelineEventView' => 'AphrontView', + 'PhabricatorTimelineExample' => 'PhabricatorUIExample', 'PhabricatorTimelineIterator' => 'Iterator', + 'PhabricatorTimelineView' => 'AphrontView', 'PhabricatorTimer' => 'PhabricatorCountdownDAO', 'PhabricatorTransactionView' => 'AphrontView', 'PhabricatorTransformedFile' => 'PhabricatorFileDAO', diff --git a/src/applications/uiexample/examples/PhabricatorTimelineExample.php b/src/applications/uiexample/examples/PhabricatorTimelineExample.php new file mode 100644 index 0000000000..8b7f3bb7c6 --- /dev/null +++ b/src/applications/uiexample/examples/PhabricatorTimelineExample.php @@ -0,0 +1,103 @@ +PhabricatorTimelineView to comments and transactions.'; + } + + public function renderExample() { + $request = $this->getRequest(); + $user = $request->getUser(); + + $handle = PhabricatorObjectHandleData::loadOneHandle( + $user->getPHID(), + $user); + + $events = array(); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle('A major event.') + ->appendChild('This is a major timeline event.'); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle('A minor event.'); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->appendChild('A major event with no title.'); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle('Another minor event.'); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle('Major Red Event') + ->appendChild('This event is red!') + ->addClass('phabricator-timeline-red'); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle('Minor Red Event') + ->addClass('phabricator-timeline-red'); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle('Minor Not-Red Event'); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle('Minor Red Event') + ->addClass('phabricator-timeline-red'); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle('Minor Not-Red Event'); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle('Unstyled event') + ->appendChild('This event disables standard title and content styling.') + ->setDisableStandardTitleStyle(true) + ->setDisableStandardContentStyle(true); + + $events[] = id(new PhabricatorTimelineEventView()) + ->setUserHandle($handle) + ->setTitle('Major Green Event') + ->appendChild('This event is green!') + ->addClass('phabricator-timeline-green'); + + $timeline = id(new PhabricatorTimelineView()); + foreach ($events as $event) { + $timeline->addEvent($event); + } + + return $timeline; + } +} diff --git a/src/view/layout/PhabricatorTimelineEventView.php b/src/view/layout/PhabricatorTimelineEventView.php new file mode 100644 index 0000000000..6bc755c569 --- /dev/null +++ b/src/view/layout/PhabricatorTimelineEventView.php @@ -0,0 +1,143 @@ +userHandle = $handle; + return $this; + } + + public function setTitle($title) { + $this->title = $title; + return $this; + } + + public function addClass($class) { + $this->classes[] = $class; + return $this; + } + + public function setDisableStandardTitleStyle($disable) { + $this->disableStandardTitleStyle = $disable; + return $this; + } + + public function setDisableStandardContentStyle($disable) { + $this->disableStandardContentStyle = $disable; + return $this; + } + + public function render() { + $content = $this->renderChildren(); + + $title = $this->title; + if (($title === null) && !strlen($content)) { + $title = ''; + } + + if ($title !== null) { + $title_classes = array(); + $title_classes[] = 'phabricator-timeline-title'; + if (!$this->disableStandardTitleStyle) { + $title_classes[] = 'phabricator-timeline-standard-title'; + } + + $title = phutil_render_tag( + 'div', + array( + 'class' => implode(' ', $title_classes), + ), + $title); + } + + $wedge = phutil_render_tag( + 'div', + array( + 'class' => 'phabricator-timeline-wedge phabricator-timeline-border', + ), + ''); + + $image_uri = $this->userHandle->getImageURI(); + $image = phutil_render_tag( + 'div', + array( + 'style' => 'background-image: url('.$image_uri.')', + 'class' => 'phabricator-timeline-image', + ), + ''); + + $content_classes = array(); + $content_classes[] = 'phabricator-timeline-content'; + if (!$this->disableStandardContentStyle) { + $content_classes[] = 'phabricator-timeline-standard-content'; + } + + $classes = array(); + $classes[] = 'phabricator-timeline-event-view'; + $classes[] = 'phabricator-timeline-border'; + if ($content) { + $classes[] = 'phabricator-timeline-major-event'; + $content = phutil_render_tag( + 'div', + array( + 'class' => implode(' ', $content_classes), + ), + phutil_render_tag( + 'div', + array( + 'class' => 'phabricator-timeline-inner-content', + ), + $title. + phutil_render_tag( + 'div', + array( + 'class' => 'phabricator-timeline-core-content', + ), + $content))); + $content = $image.$wedge.$content; + } else { + $classes[] = 'phabricator-timeline-minor-event'; + $content = phutil_render_tag( + 'div', + array( + 'class' => implode(' ', $content_classes), + ), + $image.$wedge.$title); + } + + return phutil_render_tag( + 'div', + array( + 'class' => implode(' ', $this->classes), + ), + phutil_render_tag( + 'div', + array( + 'class' => implode(' ', $classes), + ), + $content)); + } + +} diff --git a/src/view/layout/PhabricatorTimelineView.php b/src/view/layout/PhabricatorTimelineView.php new file mode 100644 index 0000000000..12116d3c93 --- /dev/null +++ b/src/view/layout/PhabricatorTimelineView.php @@ -0,0 +1,51 @@ +events[] = $event; + return $this; + } + + public function render() { + require_celerity_resource('phabricator-timeline-view-css'); + + $events = array(); + foreach ($this->events as $event) { + $events[] = phutil_render_tag( + 'div', + array( + 'class' => 'phabricator-timeline-event-view '. + 'phabricator-timeline-spacer', + ), + ''); + $events[] = $this->renderSingleView($event); + } + + return phutil_render_tag( + 'div', + array( + 'class' => 'phabricator-timeline-view', + ), + implode('', $events)); + } + +} diff --git a/webroot/rsrc/css/layout/phabricator-timeline-view.css b/webroot/rsrc/css/layout/phabricator-timeline-view.css new file mode 100644 index 0000000000..b9584ec2cb --- /dev/null +++ b/webroot/rsrc/css/layout/phabricator-timeline-view.css @@ -0,0 +1,136 @@ +/** + * @provides phabricator-timeline-view-css + */ + +.phabricator-timeline-event-view { + border-width: 0 0 0 3px; + border-style: solid; + border-color: #eaeaea; +} + +.device-desktop .phabricator-timeline-event-view { + margin-left: 80px; + margin-right: 12px; + position: relative; +} + +.device-desktop .phabricator-timeline-spacer { + min-height: 10px; + border-right-width: 0; +} + +.device-desktop .phabricator-timeline-major-event { + border-right-width: 3px; +} + +.device-desktop .phabricator-timeline-wedge { + border-bottom: 3px solid #eaeaea; + position: absolute; + width: 10px; +} + +.device-desktop .phabricator-timeline-major-event + .phabricator-timeline-content { + min-height: 70px; +} + +.phabricator-timeline-major-event .phabricator-timeline-content { + border-style: solid; + border-color: #eaeaea; + border-width: 1px 0; +} + +.device-desktop .phabricator-timeline-minor-event + .phabricator-timeline-content { + margin-left: 35px; + padding: 5px 0; + min-height: 25px; +} + +.device-desktop .phabricator-timeline-title { + line-height: 25px; +} + +.device-desktop .phabricator-timeline-major-event .phabricator-timeline-wedge { + left: -10px; + top: 34px; +} + +.device-desktop .phabricator-timeline-minor-event .phabricator-timeline-wedge { + left: 0px; + top: 17px; +} + +.phabricator-timeline-image { + background: #eaeaea; + background-repeat: no-repeat; + position: absolute; +} + +.device-desktop .phabricator-timeline-major-event .phabricator-timeline-image { + width: 50px; + height: 50px; + top: 10px; + left: -60px; +} + +.device-desktop .phabricator-timeline-minor-event .phabricator-timeline-image { + width: 25px; + height: 25px; + background-size: 25px auto; + top: 5px; + left: 10px; +} + +.device-desktop .phabricator-timeline-major-event + .phabricator-timeline-standard-title { + background: #f7f7f7; +} + +.device-desktop .phabricator-timeline-standard-title { + padding: 0 5px; +} + +.phabricator-timeline-major-event .phabricator-timeline-standard-content + .phabricator-timeline-core-content { + padding: 5px; +} + + +.phabricator-timeline-red .phabricator-timeline-border { + border-color: #dd0000; +} + +.phabricator-timeline-green .phabricator-timeline-border { + border-color: #009966; +} + +.device-tablet .phabricator-timeline-event-view, +.device-phone .phabricator-timeline-event-view { + min-height: 25px; + position: relative; + margin-left: 3px; + margin-right: 3px; + + border-right-width: 3px; + border-right-style: solid; +} + +.device-tablet .phabricator-timeline-image, +.device-phone .phabricator-timeline-image { + display: none; +} + +.device-tablet .phabricator-timeline-title, +.device-phone .phabricator-timeline-title { + line-height: 25px; + min-height: 25px; + background: #f7f7f7; + padding: 0 5px; +} + +.device-tablet .phabricator-timeline-spacer, +.device-phone .phabricator-timeline-spacer { + min-height: 8px; + border-width: 0; +}