From 0bcc76151e6f4b439124980a8404e4879d1f971f Mon Sep 17 00:00:00 2001 From: Chad Little Date: Sat, 30 Mar 2013 09:51:35 -0700 Subject: [PATCH] Workboards Summary: Adds Workboards and workpanels. This is a preliminary diff, I'm still working on mobile and tablet and a few missing features (header actions) Test Plan: FF, Chrome, iOS, iPad, iPhone, IE Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Differential Revision: https://secure.phabricator.com/D5455 --- src/__celerity_resource_map__.php | 25 +++ src/__phutil_library_map__.php | 6 + .../examples/PhabricatorWorkboardExample.php | 160 ++++++++++++++++++ src/view/layout/PhabricatorWorkboardView.php | 54 ++++++ src/view/layout/PhabricatorWorkpanelView.php | 78 +++++++++ .../css/layout/phabricator-workboard-view.css | 54 ++++++ .../css/layout/phabricator-workpanel-view.css | 93 ++++++++++ .../image/texture/panel-header-gradient.png | Bin 0 -> 1025 bytes 8 files changed, 470 insertions(+) create mode 100644 src/applications/uiexample/examples/PhabricatorWorkboardExample.php create mode 100644 src/view/layout/PhabricatorWorkboardView.php create mode 100644 src/view/layout/PhabricatorWorkpanelView.php create mode 100644 webroot/rsrc/css/layout/phabricator-workboard-view.css create mode 100644 webroot/rsrc/css/layout/phabricator-workpanel-view.css create mode 100644 webroot/rsrc/image/texture/panel-header-gradient.png diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 4c08578711..f6751d72dc 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -595,6 +595,13 @@ celerity_register_resource_map(array( 'disk' => '/rsrc/image/texture/grip.png', 'type' => 'png', ), + '/rsrc/image/texture/panel-header-gradient.png' => + array( + 'hash' => 'ad9204dd3ef5b12b645d80677d8ccead', + 'uri' => '/res/ad9204dd/rsrc/image/texture/panel-header-gradient.png', + 'disk' => '/rsrc/image/texture/panel-header-gradient.png', + 'type' => 'png', + ), '/rsrc/image/texture/pholio-background.gif' => array( 'hash' => 'cf4561af116edf393dc583e5119fb412', @@ -3378,6 +3385,24 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/uiexample/ReactorSendPropertiesExample.js', ), + 'phabricator-workboard-view-css' => + array( + 'uri' => '/res/98971c26/rsrc/css/layout/phabricator-workboard-view.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/layout/phabricator-workboard-view.css', + ), + 'phabricator-workpanel-view-css' => + array( + 'uri' => '/res/96a4b5cd/rsrc/css/layout/phabricator-workpanel-view.css', + 'type' => 'css', + 'requires' => + array( + ), + 'disk' => '/rsrc/css/layout/phabricator-workpanel-view.css', + ), 'phabricator-zindex-css' => array( 'uri' => '/res/fcbf82ad/rsrc/css/core/z-index.css', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 12e3440a2b..afaf6a9cb9 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1433,6 +1433,8 @@ phutil_register_library_map(array( 'PhabricatorUserStatusInvalidEpochException' => 'applications/people/exception/PhabricatorUserStatusInvalidEpochException.php', 'PhabricatorUserStatusOverlapException' => 'applications/people/exception/PhabricatorUserStatusOverlapException.php', 'PhabricatorUserTestCase' => 'applications/people/storage/__tests__/PhabricatorUserTestCase.php', + 'PhabricatorWorkboardExample' => 'applications/uiexample/examples/PhabricatorWorkboardExample.php', + 'PhabricatorWorkboardView' => 'view/layout/PhabricatorWorkboardView.php', 'PhabricatorWorker' => 'infrastructure/daemon/workers/PhabricatorWorker.php', 'PhabricatorWorkerActiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerActiveTask.php', 'PhabricatorWorkerArchiveTask' => 'infrastructure/daemon/workers/storage/PhabricatorWorkerArchiveTask.php', @@ -1444,6 +1446,7 @@ phutil_register_library_map(array( 'PhabricatorWorkerTaskDetailController' => 'applications/daemon/controller/PhabricatorWorkerTaskDetailController.php', 'PhabricatorWorkerTaskUpdateController' => 'applications/daemon/controller/PhabricatorWorkerTaskUpdateController.php', 'PhabricatorWorkerTestCase' => 'infrastructure/daemon/workers/__tests__/PhabricatorWorkerTestCase.php', + 'PhabricatorWorkpanelView' => 'view/layout/PhabricatorWorkpanelView.php', 'PhabricatorXHPASTViewController' => 'applications/phpast/controller/PhabricatorXHPASTViewController.php', 'PhabricatorXHPASTViewDAO' => 'applications/phpast/storage/PhabricatorXHPASTViewDAO.php', 'PhabricatorXHPASTViewFrameController' => 'applications/phpast/controller/PhabricatorXHPASTViewFrameController.php', @@ -3077,6 +3080,8 @@ phutil_register_library_map(array( 'PhabricatorUserStatusInvalidEpochException' => 'Exception', 'PhabricatorUserStatusOverlapException' => 'Exception', 'PhabricatorUserTestCase' => 'PhabricatorTestCase', + 'PhabricatorWorkboardExample' => 'PhabricatorUIExample', + 'PhabricatorWorkboardView' => 'AphrontView', 'PhabricatorWorkerActiveTask' => 'PhabricatorWorkerTask', 'PhabricatorWorkerArchiveTask' => 'PhabricatorWorkerTask', 'PhabricatorWorkerDAO' => 'PhabricatorLiskDAO', @@ -3087,6 +3092,7 @@ phutil_register_library_map(array( 'PhabricatorWorkerTaskDetailController' => 'PhabricatorDaemonController', 'PhabricatorWorkerTaskUpdateController' => 'PhabricatorDaemonController', 'PhabricatorWorkerTestCase' => 'PhabricatorTestCase', + 'PhabricatorWorkpanelView' => 'AphrontView', 'PhabricatorXHPASTViewController' => 'PhabricatorController', 'PhabricatorXHPASTViewDAO' => 'PhabricatorLiskDAO', 'PhabricatorXHPASTViewFrameController' => 'PhabricatorXHPASTViewController', diff --git a/src/applications/uiexample/examples/PhabricatorWorkboardExample.php b/src/applications/uiexample/examples/PhabricatorWorkboardExample.php new file mode 100644 index 0000000000..f655121cfe --- /dev/null +++ b/src/applications/uiexample/examples/PhabricatorWorkboardExample.php @@ -0,0 +1,160 @@ +setCards(true); + $list->setFlush(true); + + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Business Card')) + ->setBarColor('red')); + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Playing Card')) + ->setBarColor('orange')); + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('House of Cards')) + ->setBarColor('yellow')); + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Cardigan')) + ->setBarColor('green')); + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Cardamom')) + ->addFootIcon('highlight-white', 'Spice') + ->setBarColor('blue')); + + /* List 2 */ + + $list2 = new PhabricatorObjectItemListView(); + $list2->setCards(true); + $list2->setFlush(true); + + $list2->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Business Card')) + ->setBarColor('red')); + $list2->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Playing Card')) + ->setBarColor('orange')); + + /* List 3 */ + + $list3 = new PhabricatorObjectItemListView(); + $list3->setCards(true); + $list3->setFlush(true); + + $list3->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Business Card')) + ->setBarColor('red')); + $list3->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Playing Card')) + ->setBarColor('orange')); + $list3->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('House of Cards')) + ->setBarColor('yellow')); + $list3->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Cardigan')) + ->setBarColor('green')); + $list3->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Cardamom')) + ->addFootIcon('highlight-white', 'Spice') + ->setBarColor('blue')); + $list3->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Business Card')) + ->setBarColor('red')); + $list3->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Playing Card')) + ->setBarColor('orange')); + $list3->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Business Card')) + ->setBarColor('red')); + $list3->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Playing Card')) + ->setBarColor('orange')); + + $panel = id(new PhabricatorWorkpanelView) + ->setCards($list) + ->setHeader('Business Stuff'); + + $panel2 = id(new PhabricatorWorkpanelView) + ->setCards($list2) + ->setHeader('Under Duress'); + + $panel3 = id(new PhabricatorWorkpanelView) + ->setCards($list3) + ->setHeader('Spicy Thai Chicken'); + + $board = id(new PhabricatorWorkboardView) + ->addPanel($panel) + ->addPanel($panel2) + ->addPanel($panel2) + ->addPanel($panel3); + + $board2 = id(new PhabricatorWorkboardView) + ->setFlexLayout(true) + ->addPanel($panel) + ->addPanel($panel2) + ->addPanel($panel2) + ->addPanel($panel2) + ->addPanel($panel2) + ->addPanel($panel3); + + $head1 = id(new PhabricatorHeaderView()) + ->setHeader(pht('Fixed Panel')); + + $head2 = id(new PhabricatorHeaderView()) + ->setHeader(pht('Fluid Panel')); + + + $wrap1 = phutil_tag( + 'div', + array( + 'class' => 'ml' + ), + $board); + + $wrap2 = phutil_tag( + 'div', + array( + 'class' => 'ml' + ), + $board2); + + return phutil_tag( + 'div', + array(), + array( + $head1, + $wrap1, + $head2, + $wrap2 + )); + } +} diff --git a/src/view/layout/PhabricatorWorkboardView.php b/src/view/layout/PhabricatorWorkboardView.php new file mode 100644 index 0000000000..806233372a --- /dev/null +++ b/src/view/layout/PhabricatorWorkboardView.php @@ -0,0 +1,54 @@ +panels[] = $panel; + return $this; + } + + public function setFlexLayout($layout) { + $this->flexLayout = $layout; + return $this; + } + + public function render() { + require_celerity_resource('phabricator-workboard-view-css'); + + $classes = array(); + $classes[] = 'phabricator-workboard-view-inner'; + + if (count($this->panels) > 6) { + throw new Exception("No more than 6 panels per workboard."); + } + + $classes[] = 'workboard-'.count($this->panels).'-up'; + + $view = phutil_tag( + 'div', + array( + 'class' => implode(' ', $classes), + ), + array( + $this->panels, + )); + + $classes = array(); + $classes[] = 'phabricator-workboard-view-outer'; + if ($this->flexLayout) { + $classes[] = 'phabricator-workboard-flex'; + } else { + $classes[] = 'phabricator-workboard-fixed'; + } + + return phutil_tag( + 'div', + array( + 'class' => implode(' ', $classes) + ), + $view); + } +} diff --git a/src/view/layout/PhabricatorWorkpanelView.php b/src/view/layout/PhabricatorWorkpanelView.php new file mode 100644 index 0000000000..600009c6ff --- /dev/null +++ b/src/view/layout/PhabricatorWorkpanelView.php @@ -0,0 +1,78 @@ +cards[] = $cards; + return $this; + } + + public function setHeader($header) { + $this->header = $header; + return $this; + } + + public function setHeaderAction($header_action) { + $this->headerAction = $header_action; + return $this; + } + + public function setFooterAction($footer_action) { + $this->footerAction = $footer_action; + return $this; + } + + public function render() { + require_celerity_resource('phabricator-workpanel-view-css'); + + $footer = ''; + if ($this->footerAction) { + $action = $this->footerAction; + $footer = javelin_tag( + 'a', + array( + 'href' => $action->getHref(), + 'class' => 'phabricator-workpanel-footer-action', + 'sigil' => $action->getWorkflow() ? 'workflow' : null, + ), + $action->getName()); + } + + $header = phutil_tag( + 'div', + array( + 'class' => 'phabricator-workpanel-header' + ), + $this->header); + + $body = phutil_tag( + 'div', + array( + 'class' => 'phabricator-workpanel-body' + ), + $this->cards); + + $view = phutil_tag( + 'div', + array( + 'class' => 'phabricator-workpanel-view-inner', + ), + array( + $header, + $body, + $footer, + )); + + return phutil_tag( + 'div', + array( + 'class' => 'phabricator-workpanel-view' + ), + $view); + } +} diff --git a/webroot/rsrc/css/layout/phabricator-workboard-view.css b/webroot/rsrc/css/layout/phabricator-workboard-view.css new file mode 100644 index 0000000000..c5cd08ad19 --- /dev/null +++ b/webroot/rsrc/css/layout/phabricator-workboard-view.css @@ -0,0 +1,54 @@ +/** + * @provides phabricator-workboard-view-css + */ + +.phabricator-workboard-view-outer { + padding: 8px; + overflow-x: scroll; + border-radius: 5px; + background: rgba(150,150,150,.1); + box-shadow: inset 0 0 5px rgba(0,0,0,.5); +} + +.device-phone .phabricator-workboard-view-outer { + background: none; + box-shadow: none; + padding: 0; + margin: 0 auto; + width: 100%; +} + +/* math here is based on panel width and margins */ +.phabricator-workboard-fixed + .phabricator-workboard-view-inner.workboard-1-up { + width: 310px; +} + +.phabricator-workboard-fixed + .phabricator-workboard-view-inner.workboard-2-up { + width: 620px; +} + +.phabricator-workboard-fixed + .phabricator-workboard-view-inner.workboard-3-up { + width: 930px; +} + +.phabricator-workboard-fixed + .phabricator-workboard-view-inner.workboard-4-up { + width: 1008px; +} + +.phabricator-workboard-fixed + .phabricator-workboard-view-inner.workboard-5-up { + width: 1250px; +} + +.phabricator-workboard-fixed + .phabricator-workboard-view-inner.workboard-6-up { + width: 1500px; +} + +.device-phone .phabricator-workboard-fixed .phabricator-workboard-view-inner { + width: 100%; +} diff --git a/webroot/rsrc/css/layout/phabricator-workpanel-view.css b/webroot/rsrc/css/layout/phabricator-workpanel-view.css new file mode 100644 index 0000000000..b6b694e056 --- /dev/null +++ b/webroot/rsrc/css/layout/phabricator-workpanel-view.css @@ -0,0 +1,93 @@ +/** + * @provides phabricator-workpanel-view-css + */ + +.phabricator-workpanel-view { + float: left; +} + +.phabricator-workpanel-view-inner { + margin: 0 10px 0 0; +} + +.device-phone .phabricator-workpanel-view-inner { + margin: 0; +} + +.phabricator-workboard-flex .phabricator-workpanel-view:last-child + .phabricator-workpanel-view-inner { + margin: 0; +} + +/* Panels require a fixed width to overflow well. */ +.phabricator-workboard-fixed .workboard-1-up .phabricator-workpanel-view, +.phabricator-workboard-fixed .workboard-2-up .phabricator-workpanel-view, +.phabricator-workboard-fixed .workboard-3-up .phabricator-workpanel-view { + width: 300px; +} + +.phabricator-workboard-fixed .workboard-4-up .phabricator-workpanel-view, +.phabricator-workboard-fixed .workboard-5-up .phabricator-workpanel-view, +.phabricator-workboard-fixed .workboard-6-up .phabricator-workpanel-view { + width: 240px; +} + +.device-phone .phabricator-workboard-view-outer div.phabricator-workpanel-view { + width: 100%; + margin-bottom: 20px; +} + +.phabricator-workpanel-view .phabricator-workpanel-header { + font-size: 14px; + font-weight: bold; + color: #333; + border: 1px solid #b3b5b6; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + padding: 8px 10px; + background: #f0f0f0 url(/rsrc/image/texture/panel-header-gradient.png) repeat-x; + text-shadow: 0 1px 1px #fff; +} + +.phabricator-workpanel-view .phabricator-workpanel-header-action { + float: right; + width: 24px; + border-left: 1px solid #b3b5b6; +} + +.phabricator-workpanel-view .phabricator-workpanel-body { + background: #c4cde0; + padding: 5px 5px 1px 5px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + box-shadow: inset 0 0px 5px rgba(0,0,0,.4); +} + +.phabricator-workpanel-view .phabricator-workpanel-footer { + padding: 8px 5px; +} + +/* fluid/flex styles */ +.phabricator-workboard-flex .workboard-1-up .phabricator-workpanel-view { + width: 100%; +} + +.phabricator-workboard-flex .workboard-2-up .phabricator-workpanel-view { + width: 50%; +} + +.phabricator-workboard-flex .workboard-3-up .phabricator-workpanel-view { + width: 33.3333%; +} + +.phabricator-workboard-flex .workboard-4-up .phabricator-workpanel-view { + width: 25%; +} + +.phabricator-workboard-flex .workboard-5-up .phabricator-workpanel-view { + width: 20%; +} + +.phabricator-workboard-flex .workboard-6-up .phabricator-workpanel-view { + width: 16.6666%; +} diff --git a/webroot/rsrc/image/texture/panel-header-gradient.png b/webroot/rsrc/image/texture/panel-header-gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..14520d9f0b7fe12601aebc2b80042e8ae29c6515 GIT binary patch literal 1025 zcmbVLJ#W)M7&eqjP^lXOFu>t5fJAJc?Kt+Srb^<3M#62BMj|7QeNC*^K6Abjw?kF2 zR7^0!)P1Vts2s6X5+haZV z5%NhSdAW|1MF~d>!s1evc>V@vppOS(Y>VGNd=x<#*y7`|tGG!W55wghir02ly8h0F zZw2Dg1F)DOP7q<{fh^jLQ2PVE|x_wR$&{qcZoiWwE`xO$gR;}9d(@M#1=o<;*hZf$?|A4l17?D=s<>+W#t^I zTH+d|bUS8VR*KVwg2BP5Ps4y@DeK_BZcNoqyW0ttJ2)j<)aUhBUnsylcjp_*6}dNPm4>`1-ljwRtq8}g<=7&> zk%BNl3N(2ZE4orq6~lzkRMl!zF`I^|nOfa23yx`62o>GXn^m(>b0BP})pA*Bn5L#R zteRt1^a9(8Q|84!F6@Tf?gVR{i$!&cJw|Akkj-k}v3-09w zI*r%CG<=4GCM6LVXBdT3JW#YLksFR-)~sr?u50218~jh!WbTZdx5q#2QiymD=G(L8 z=ZCZL!7-m1%7d$@n~=i47w-mJbm inK{}${JsBbcTboR&@b)Qk$>