diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 3cfa6fe9ad..5a0c1c88e2 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -581,6 +581,13 @@ celerity_register_resource_map(array( 'disk' => '/rsrc/image/texture/dust_background.jpg', 'type' => 'jpg', ), + '/rsrc/image/texture/grip.png' => + array( + 'hash' => 'f11bc231d241f1335cfca2933ad234e0', + 'uri' => '/res/f11bc231/rsrc/image/texture/grip.png', + 'disk' => '/rsrc/image/texture/grip.png', + 'type' => 'png', + ), '/rsrc/image/texture/pholio-background.gif' => array( 'hash' => 'cf4561af116edf393dc583e5119fb412', @@ -2988,7 +2995,7 @@ celerity_register_resource_map(array( ), 'phabricator-object-item-list-view-css' => array( - 'uri' => '/res/f3c39d6f/rsrc/css/layout/phabricator-object-item-list-view.css', + 'uri' => '/res/aa09c531/rsrc/css/layout/phabricator-object-item-list-view.css', 'type' => 'css', 'requires' => array( @@ -3686,7 +3693,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - '3f552ecc' => + '6c294512' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -3728,7 +3735,7 @@ celerity_register_resource_map(array( 34 => 'phabricator-object-item-list-view-css', 35 => 'global-drag-and-drop-css', ), - 'uri' => '/res/pkg/3f552ecc/core.pkg.css', + 'uri' => '/res/pkg/6c294512/core.pkg.css', 'type' => 'css', ), '95ceba95' => @@ -3919,16 +3926,16 @@ celerity_register_resource_map(array( 'reverse' => array( 'aphront-attached-file-view-css' => 'c41b4907', - 'aphront-dialog-view-css' => '3f552ecc', - 'aphront-error-view-css' => '3f552ecc', - 'aphront-form-view-css' => '3f552ecc', - 'aphront-list-filter-view-css' => '3f552ecc', - 'aphront-pager-view-css' => '3f552ecc', - 'aphront-panel-view-css' => '3f552ecc', - 'aphront-table-view-css' => '3f552ecc', - 'aphront-tokenizer-control-css' => '3f552ecc', - 'aphront-tooltip-css' => '3f552ecc', - 'aphront-typeahead-control-css' => '3f552ecc', + 'aphront-dialog-view-css' => '6c294512', + 'aphront-error-view-css' => '6c294512', + 'aphront-form-view-css' => '6c294512', + 'aphront-list-filter-view-css' => '6c294512', + 'aphront-pager-view-css' => '6c294512', + 'aphront-panel-view-css' => '6c294512', + 'aphront-table-view-css' => '6c294512', + 'aphront-tokenizer-control-css' => '6c294512', + 'aphront-tooltip-css' => '6c294512', + 'aphront-typeahead-control-css' => '6c294512', 'differential-changeset-view-css' => '8aaacd1b', 'differential-core-view-css' => '8aaacd1b', 'differential-inline-comment-editor' => '322728f3', @@ -3942,7 +3949,7 @@ celerity_register_resource_map(array( 'differential-table-of-contents-css' => '8aaacd1b', 'diffusion-commit-view-css' => 'c8ce2d88', 'diffusion-icons-css' => 'c8ce2d88', - 'global-drag-and-drop-css' => '3f552ecc', + 'global-drag-and-drop-css' => '6c294512', 'inline-comment-summary-css' => '8aaacd1b', 'javelin-aphlict' => '95ceba95', 'javelin-behavior' => 'fe22443b', @@ -4014,48 +4021,48 @@ celerity_register_resource_map(array( 'javelin-util' => 'fe22443b', 'javelin-vector' => 'fe22443b', 'javelin-workflow' => 'fe22443b', - 'lightbox-attachment-css' => '3f552ecc', + 'lightbox-attachment-css' => '6c294512', 'maniphest-task-summary-css' => 'c41b4907', 'maniphest-transaction-detail-css' => 'c41b4907', 'phabricator-busy' => '95ceba95', 'phabricator-content-source-view-css' => '8aaacd1b', - 'phabricator-core-buttons-css' => '3f552ecc', - 'phabricator-core-css' => '3f552ecc', - 'phabricator-crumbs-view-css' => '3f552ecc', - 'phabricator-directory-css' => '3f552ecc', + 'phabricator-core-buttons-css' => '6c294512', + 'phabricator-core-css' => '6c294512', + 'phabricator-crumbs-view-css' => '6c294512', + 'phabricator-directory-css' => '6c294512', 'phabricator-drag-and-drop-file-upload' => '322728f3', 'phabricator-dropdown-menu' => '95ceba95', 'phabricator-file-upload' => '95ceba95', - 'phabricator-filetree-view-css' => '3f552ecc', - 'phabricator-flag-css' => '3f552ecc', - 'phabricator-form-view-css' => '3f552ecc', - 'phabricator-header-view-css' => '3f552ecc', - 'phabricator-jump-nav' => '3f552ecc', + 'phabricator-filetree-view-css' => '6c294512', + 'phabricator-flag-css' => '6c294512', + 'phabricator-form-view-css' => '6c294512', + 'phabricator-header-view-css' => '6c294512', + 'phabricator-jump-nav' => '6c294512', 'phabricator-keyboard-shortcut' => '95ceba95', 'phabricator-keyboard-shortcut-manager' => '95ceba95', - 'phabricator-main-menu-view' => '3f552ecc', + 'phabricator-main-menu-view' => '6c294512', 'phabricator-menu-item' => '95ceba95', - 'phabricator-nav-view-css' => '3f552ecc', + 'phabricator-nav-view-css' => '6c294512', 'phabricator-notification' => '95ceba95', - 'phabricator-notification-css' => '3f552ecc', - 'phabricator-notification-menu-css' => '3f552ecc', - 'phabricator-object-item-list-view-css' => '3f552ecc', + 'phabricator-notification-css' => '6c294512', + 'phabricator-notification-menu-css' => '6c294512', + 'phabricator-object-item-list-view-css' => '6c294512', 'phabricator-object-selector-css' => '8aaacd1b', 'phabricator-paste-file-upload' => '95ceba95', 'phabricator-prefab' => '95ceba95', 'phabricator-project-tag-css' => 'c41b4907', - 'phabricator-remarkup-css' => '3f552ecc', + 'phabricator-remarkup-css' => '6c294512', 'phabricator-shaped-request' => '322728f3', - 'phabricator-side-menu-view-css' => '3f552ecc', - 'phabricator-standard-page-view' => '3f552ecc', + 'phabricator-side-menu-view-css' => '6c294512', + 'phabricator-standard-page-view' => '6c294512', 'phabricator-textareautils' => '95ceba95', 'phabricator-tooltip' => '95ceba95', - 'phabricator-transaction-view-css' => '3f552ecc', - 'phabricator-zindex-css' => '3f552ecc', - 'sprite-apps-large-css' => '3f552ecc', - 'sprite-gradient-css' => '3f552ecc', - 'sprite-icon-css' => '3f552ecc', - 'sprite-menu-css' => '3f552ecc', - 'syntax-highlighting-css' => '3f552ecc', + 'phabricator-transaction-view-css' => '6c294512', + 'phabricator-zindex-css' => '6c294512', + 'sprite-apps-large-css' => '6c294512', + 'sprite-gradient-css' => '6c294512', + 'sprite-icon-css' => '6c294512', + 'sprite-menu-css' => '6c294512', + 'syntax-highlighting-css' => '6c294512', ), )); diff --git a/src/aphront/response/AphrontAjaxResponse.php b/src/aphront/response/AphrontAjaxResponse.php index 0b982000f7..c1431e7ed8 100644 --- a/src/aphront/response/AphrontAjaxResponse.php +++ b/src/aphront/response/AphrontAjaxResponse.php @@ -45,9 +45,16 @@ final class AphrontAjaxResponse extends AphrontResponse { )); } + // Flatten the response first, so we initialize any behaviors and metadata + // we need to. + $content = array( + 'payload' => $this->content, + ); + $this->encodeJSONForHTTPResponse($content); + $response = CelerityAPI::getStaticResourceResponse(); $object = $response->buildAjaxResponse( - $this->content, + $content['payload'], $this->error); $response_json = $this->encodeJSONForHTTPResponse($object); diff --git a/src/aphront/response/AphrontResponse.php b/src/aphront/response/AphrontResponse.php index e410262b39..20ce8f7a62 100644 --- a/src/aphront/response/AphrontResponse.php +++ b/src/aphront/response/AphrontResponse.php @@ -55,6 +55,12 @@ abstract class AphrontResponse { } public static function processValueForJSONEncoding(&$value, $key) { + if ($value instanceof PhutilSafeHTMLProducerInterface) { + // This renders the producer down to PhutilSafeHTML, which will then + // be simplified into a string below. + $value = hsprintf('%s', $value); + } + if ($value instanceof PhutilSafeHTML) { // TODO: Javelin supports implicity conversion of '__html' objects to // JX.HTML, but only for Ajax responses, not behaviors. Just leave things diff --git a/src/applications/uiexample/examples/PhabricatorObjectItemListExample.php b/src/applications/uiexample/examples/PhabricatorObjectItemListExample.php index 30940db29a..d85e9edc06 100644 --- a/src/applications/uiexample/examples/PhabricatorObjectItemListExample.php +++ b/src/applications/uiexample/examples/PhabricatorObjectItemListExample.php @@ -28,16 +28,19 @@ final class PhabricatorObjectItemListExample extends PhabricatorUIExample { $list->addItem( id(new PhabricatorObjectItemView()) + ->setObjectName('FRUIT1') ->setHeader(pht('Apple')) ->setHref('#')); $list->addItem( id(new PhabricatorObjectItemView()) + ->setObjectName('FRUIT2') ->setHeader(pht('Banana')) ->setHref('#')); $list->addItem( id(new PhabricatorObjectItemView()) + ->setObjectName('FRUIT3') ->setHeader(pht('Cherry')) ->setHref('#')); @@ -85,32 +88,68 @@ final class PhabricatorObjectItemListExample extends PhabricatorUIExample { $list->addItem( id(new PhabricatorObjectItemView()) - ->setHeader(pht('Business Card'))); + ->setHeader(pht('Business Card')) + ->setBarColor('red')); $list->addItem( id(new PhabricatorObjectItemView()) - ->setHeader(pht('Playing Card'))); + ->setHeader(pht('Playing Card')) + ->setBarColor('orange') + ->addIcon('comment', pht('Royal Flush!'))); $list->addItem( id(new PhabricatorObjectItemView()) - ->setHeader(pht('House of Cards'))); + ->setHeader(pht('House of Cards')) + ->setBarColor('yellow')); $list->addItem( id(new PhabricatorObjectItemView()) - ->setHeader(pht('Cardigan'))); + ->setHeader(pht('Cardigan')) + ->setBarColor('green')); $list->addItem( id(new PhabricatorObjectItemView()) ->setHeader(pht('Cardamom')) - ->addFootIcon('highlight-white', 'Spice')); + ->addFootIcon('highlight-white', 'Spice') + ->setBarColor('blue')); $list->addItem( id(new PhabricatorObjectItemView()) ->setHeader(pht( 'The human cardiovascular system includes the heart, lungs, and '. 'some other parts; most of these parts are pretty squishy')) ->addFootIcon('search-white', pht('Respiration!')) - ->addHandleIcon($handle, pht('You have a cardiovascular system!'))); + ->addHandleIcon($handle, pht('You have a cardiovascular system!')) + ->setBarColor('magenta')); $out[] = array($head, $list); + $head = id(new PhabricatorHeaderView()) + ->setHeader(pht('Grippable List')); + $list = new PhabricatorObjectItemListView(); + $list->setCards(true); + + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Grab ahold!')) + ->setHref('#') + ->setGrippable(true) + ->setBarColor('red')); + + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Hold on tight!')) + ->setHref('#') + ->setGrippable(true) + ->setBarColor('yellow')); + + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht("Don't let go!")) + ->setHref('#') + ->setGrippable(true) + ->setBarColor('green')); + + $out[] = array($head, $list); + + $head = id(new PhabricatorHeaderView()) ->setHeader(pht('Extras')); @@ -129,6 +168,28 @@ final class PhabricatorObjectItemListExample extends PhabricatorUIExample { ->addHandleIcon($handle, pht('You hold all the cards.')) ->addHandleIcon($handle, pht('You make all the rules.'))); + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Just A Handle')) + ->setHref('#') + ->addHandleIcon($handle, pht('Handle Here'))); + + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Poor Use of Space')) + ->setHref('#') + ->addAttribute('North West') + ->addHandleIcon($handle, pht('South East'))); + + $list->addItem( + id(new PhabricatorObjectItemView()) + ->setHeader(pht('Crowded Eastern Edge')) + ->setHref('#') + ->addIcon('computer', pht('Stuff')) + ->addIcon('computer', pht('Stuff')) + ->addIcon('computer', pht('Stuff')) + ->addHandleIcon($handle, pht('More Stuff'))); + $out[] = array($head, $list); diff --git a/src/view/layout/PhabricatorObjectItemView.php b/src/view/layout/PhabricatorObjectItemView.php index 442b894421..6423b10867 100644 --- a/src/view/layout/PhabricatorObjectItemView.php +++ b/src/view/layout/PhabricatorObjectItemView.php @@ -2,6 +2,7 @@ final class PhabricatorObjectItemView extends AphrontView { + private $objectName; private $header; private $href; private $attributes = array(); @@ -11,6 +12,21 @@ final class PhabricatorObjectItemView extends AphrontView { private $effect; private $footIcons = array(); private $handleIcons = array(); + private $grippable; + + public function setObjectName($name) { + $this->objectName = $name; + return $this; + } + + public function setGrippable($grippable) { + $this->grippable = $grippable; + return $this; + } + + public function getGrippable() { + return $this->grippable; + } public function setEffect($effect) { $this->effect = $effect; @@ -94,15 +110,38 @@ final class PhabricatorObjectItemView extends AphrontView { $item_classes = array(); $content_classes[] = 'phabricator-object-item-content'; - $header = phutil_tag( + $header_name = null; + if ($this->objectName) { + $header_name = array( + phutil_tag( + 'span', + array( + 'class' => 'phabricator-object-item-objname', + ), + $this->objectName), + ' ', + ); + } + + $header_link = phutil_tag( $this->href ? 'a' : 'div', array( 'href' => $this->href, - 'class' => 'phabricator-object-item-name', + 'class' => 'phabricator-object-item-link', ), $this->header); - $icons = null; + $header = phutil_tag( + 'div', + array( + 'class' => 'phabricator-object-item-name', + ), + array( + $header_name, + $header_link, + )); + + $icons = array(); if ($this->icons) { $icon_list = array(); foreach ($this->icons as $spec) { @@ -141,7 +180,7 @@ final class PhabricatorObjectItemView extends AphrontView { $icon_href); } - $icons = phutil_tag( + $icons[] = phutil_tag( 'ul', array( 'class' => 'phabricator-object-item-icons', @@ -150,6 +189,29 @@ final class PhabricatorObjectItemView extends AphrontView { $item_classes[] = 'phabricator-object-item-with-icons'; } + if ($this->handleIcons) { + $handle_bar = array(); + foreach ($this->handleIcons as $icon) { + $handle_bar[] = $this->renderHandleIcon($icon['icon'], $icon['label']); + } + $icons[] = phutil_tag( + 'div', + array( + 'class' => 'phabricator-object-item-handle-icons', + ), + $handle_bar); + $item_classes[] = 'phabricator-object-item-with-handle-icons'; + } + + if ($icons) { + $icons = phutil_tag( + 'div', + array( + 'class' => 'phabricator-object-icon-pane', + ), + $icons); + } + $attrs = null; if ($this->attributes) { $attrs = array(); @@ -181,42 +243,19 @@ final class PhabricatorObjectItemView extends AphrontView { $item_classes[] = 'phabricator-object-item-with-attrs'; } - $foot = array(); - - if ($this->handleIcons) { - $handle_bar = array(); - foreach ($this->handleIcons as $icon) { - $handle_bar[] = $this->renderHandleIcon($icon['icon'], $icon['label']); - } - $foot[] = phutil_tag( - 'div', - array( - 'class' => 'phabricator-object-item-handle-icons', - ), - $handle_bar); - $item_classes[] = 'phabricator-object-item-with-handle-icons'; - } - + $foot = null; if ($this->footIcons) { $foot_bar = array(); foreach ($this->footIcons as $icon) { $foot_bar[] = $this->renderFootIcon($icon['icon'], $icon['label']); } - $foot[] = phutil_tag( + $foot = phutil_tag( 'div', array( 'class' => 'phabricator-object-item-foot-icons', ), $foot_bar); - } - - if ($foot) { - $foot = phutil_tag( - 'div', - array( - 'class' => 'phabricator-object-item-foot', - ), - $foot); + $item_classes[] = 'phabricator-object-item-with-foot-icons'; } $item_classes[] = 'phabricator-object-item'; @@ -237,6 +276,17 @@ final class PhabricatorObjectItemView extends AphrontView { throw new Exception(pht("Invalid effect!")); } + $grippable = null; + if ($this->getGrippable()) { + $item_classes[] = 'phabricator-object-item-grippable'; + $grippable = phutil_tag( + 'div', + array( + 'class' => 'phabricator-object-item-grip', + ), + ''); + } + $content = phutil_tag( 'div', array( @@ -246,6 +296,7 @@ final class PhabricatorObjectItemView extends AphrontView { $header, $attrs, $this->renderChildren(), + $foot, )); return phutil_tag( @@ -253,11 +304,16 @@ final class PhabricatorObjectItemView extends AphrontView { array( 'class' => implode(' ', $item_classes), ), - array( - $icons, - $content, - $foot, - )); + phutil_tag( + 'div', + array( + 'class' => 'phabricator-object-item-frame', + ), + array( + $grippable, + $icons, + $content, + ))); } private function renderFootIcon($icon, $label) { diff --git a/webroot/rsrc/css/layout/phabricator-object-item-list-view.css b/webroot/rsrc/css/layout/phabricator-object-item-list-view.css index bdbee05d1c..f58329f84b 100644 --- a/webroot/rsrc/css/layout/phabricator-object-item-list-view.css +++ b/webroot/rsrc/css/layout/phabricator-object-item-list-view.css @@ -6,31 +6,52 @@ padding: 8px 6px; } - .device-desktop .phabricator-object-item-list-view { padding: 20px; } +.device-desktop .phabricator-object-list-flush { + padding: 0; +} + .phabricator-object-item { background: #ffffff; border-style: solid; border-color: #c0c5d1; - border-width: 1px 1px 1px 3px; + border-width: 0 0 0 3px; margin: 5px 0; overflow: hidden; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.10); } +.phabricator-object-item-frame { + border-style: solid; + border-color: #c0c5d1; + border-width: 1px 1px 1px 0; + overflow: hidden; + position: relative; +} + .device-desktop .phabricator-object-item { margin: 0 0 5px 0; } .phabricator-object-item-name { display: block; - padding: 8px 10px 8px; font-weight: bold; font-size: 14px; + padding: 0 10px; +} + +.phabricator-object-item-link { + display: inline-block; + padding: 8px 0; +} + + +.phabricator-object-item-objname { + color: #222222; } .phabricator-object-item-with-attrs .phabricator-object-item-name { @@ -41,6 +62,23 @@ overflow: hidden; } +.phabricator-object-item-grippable { + cursor: move; +} + +.phabricator-object-item-grip { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15px; + background: url(/rsrc/image/texture/grip.png) center center no-repeat; +} + +.phabricator-object-item-grippable .phabricator-object-item-frame { + padding-left: 9px; +} + /* - Stackable List ------------------------------------------------------------ @@ -54,40 +92,15 @@ } .phabricator-object-list-stackable .phabricator-object-item { - border-width: 1px; + border-left-width: 1px; } .device-desktop .phabricator-object-list-stackable .phabricator-object-item:hover { - border-width: 1px; background: #e9ecf5; } -/* - Card List ----------------------------------------------------------------- - - Rounded card list. - -*/ - -/* Hard to sprite since we can't have other sprites appearing in tall cells */ -.phabricator-object-list-cards .phabricator-object-item { - border-radius: 3px; - border-left-width: 4px; - background: #e8e8e8 url('/rsrc/image/texture/card-gradient.png') repeat-x; -} - - -.phabricator-object-list-cards .phabricator-object-item-name { - display: block; - padding: 6px 10px 6px; - font-weight: normal; - font-size: 13px; - color: #555555; - max-height: 2em; -} - - /* - Attribute List ------------------------------------------------------------ Object attributes, commonly used to render created date, etc. @@ -116,9 +129,17 @@ */ +.phabricator-object-icon-pane { + float: right; +} + +.phabricator-object-item-with-handle-icons .phabricator-object-item-icons { + padding-bottom: 34px; +} + .phabricator-object-item-icons { float: right; - padding: 10px 10px 0; + padding: 6px 10px 0; } /* NOTE: The main content is an "overflow: hidden" div which we give a right @@ -126,11 +147,13 @@ the width + padding of the icon div, so the icons have some space even if the content is wider than available space. */ -.device-desktop .phabricator-object-item-icons { +.device-desktop .phabricator-object-icon-pane { width: 120px; } .device-desktop .phabricator-object-item-with-icons + .phabricator-object-item-content, +.device-desktop .phabricator-object-item-with-handle-icons .phabricator-object-item-content { margin-right: 132px; } @@ -139,16 +162,18 @@ width: 18px; } -.device .phabricator-object-item-with-icons .phabricator-object-item-content { +.device .phabricator-object-item-with-icons .phabricator-object-item-content, +.device .phabricator-object-item-with-handle-icons + .phabricator-object-item-content { margin-right: 30px; } .device .phabricator-object-item-icon-label { display: none; - vertical-align: middle; } .phabricator-object-item-icon { + vertical-align: middle; position: relative; font-size: 11px; color: #666666; @@ -164,6 +189,10 @@ padding-right: 22px; } +.device-desktop .phabricator-object-item-icon-none { + padding-right: 0; +} + .phabricator-object-item-icon-image { position: absolute; right: 2px; @@ -212,6 +241,10 @@ border-left-color: #67009b; } +.phabricator-object-item-bar-color-magenta { + border-left-color: #cc00c0; +} + .phabricator-object-item-bar-color-grey { border-left-color: #999999; } @@ -236,18 +269,6 @@ } -/* - Foot ---------------------------------------------------------------------- - - This goes at the bottom. - -*/ - -.phabricator-object-item-foot { - clear: both; - padding: 0; -} - - /* - Foot Icons ---------------------------------------------------------------- Object counts shown in the footer. @@ -256,6 +277,12 @@ .phabricator-object-item-foot-icons { margin-left: 10px; + bottom: 0; + position: absolute; +} + +.phabricator-object-item-with-foot-icons .phabricator-object-item-content { + padding-bottom: 22px; } .phabricator-object-item-foot-icon { @@ -270,10 +297,6 @@ position: relative; } -.phabricator-object-item-with-handle-icons .phabricator-object-item-foot-icon { - margin-top: 8px; -} - .phabricator-object-item-foot-icon .sprite-icon { width: 14px; height: 14px; @@ -290,9 +313,12 @@ */ .phabricator-object-item-handle-icons { - float: right; height: 28px; margin-right: 10px; + bottom: 0; + right: 0; + text-align: right; + position: absolute; } .phabricator-object-item-handle-icon { @@ -304,3 +330,44 @@ background-size: 28px 28px; background-repeat: no-repeat; } + + +/* - Card List ----------------------------------------------------------------- + + Rounded card list. + +*/ + +/* Hard to sprite since we can't have other sprites appearing in tall cells */ +.phabricator-object-list-cards .phabricator-object-item { + border-radius: 3px; + border-left-width: 4px; + background: #e8e8e8 url('/rsrc/image/texture/card-gradient.png') repeat-x; +} + +.phabricator-object-list-cards .phabricator-object-item-name { + font-size: 13px; +} + +.phabricator-object-list-cards .phabricator-object-item-link { + white-space: nowrap; + padding: 6px 0; +} + +.phabricator-object-list-cards .phabricator-object-item-with-attrs + .phabricator-object-item-name { + padding: 4px 10px 4px; +} + +.phabricator-object-list-cards .phabricator-object-item-selected { + background: #bfdcff; +} + +.phabricator-object-list-cards .phabricator-object-item-selected + .phabricator-object-item-frame { + border-color: #99ccff; +} + +.phabricator-object-list-cards .phabricator-object-item-attributes { + padding-bottom: 4px; +} diff --git a/webroot/rsrc/image/texture/grip.png b/webroot/rsrc/image/texture/grip.png new file mode 100644 index 0000000000..69c06fc877 Binary files /dev/null and b/webroot/rsrc/image/texture/grip.png differ