From 1d5551789ddae281dec5e6ac22860fc7badabee4 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 21 Nov 2012 17:24:56 -0800 Subject: [PATCH] Add basic transaction/timeline view Summary: This is still rough and not completely accurate to the mocks (and the mobile view is quite crude and mostly just "hey this technically works"), but I want to build Pholio on top of it rather than building it on something else and then swapping it out later and the API is reasonable enough. This should probably be called `PhabricatorTransactionView` but we already have one of those. I might juggle the names in a future diff. Test Plan: Desktop {F22352} Mobile {F22351} Reviewers: chad, btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2097 Differential Revision: https://secure.phabricator.com/D3833 --- src/__phutil_library_map__.php | 6 + .../examples/PhabricatorTimelineExample.php | 103 +++++++++++++ .../layout/PhabricatorTimelineEventView.php | 143 ++++++++++++++++++ src/view/layout/PhabricatorTimelineView.php | 51 +++++++ .../css/layout/phabricator-timeline-view.css | 136 +++++++++++++++++ 5 files changed, 439 insertions(+) create mode 100644 src/applications/uiexample/examples/PhabricatorTimelineExample.php create mode 100644 src/view/layout/PhabricatorTimelineEventView.php create mode 100644 src/view/layout/PhabricatorTimelineView.php create mode 100644 webroot/rsrc/css/layout/phabricator-timeline-view.css 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; +}