1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +01:00

First pass at hovercard design

Summary:
Refs T1048 - I'm pretty happy and happy to tell you

Savepoint CR

Everything's dummy right now. If you are testing locally, don't forget to edit the PHIDs. Or populate your own handles. Doesn't really matter.

I'm mainly sending it in for the CSS, not the messy PHP code. Ignore the `margin: auto`, that's just for looking nice.

Test Plan: UI Example » Hovercard

Reviewers: epriestley, chad

CC: aran, Korvin

Maniphest Tasks: T1048

Differential Revision: https://secure.phabricator.com/D5519
This commit is contained in:
Anh Nhan Nguyen 2013-04-02 09:15:33 -07:00 committed by epriestley
parent b0d408c5d3
commit 5bd54e35bc
6 changed files with 355 additions and 0 deletions

View file

@ -2973,6 +2973,15 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/layout/phabricator-header-view.css',
),
'phabricator-hovercard-view-css' =>
array(
'uri' => '/res/061e66df/rsrc/css/layout/phabricator-hovercard-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/layout/phabricator-hovercard-view.css',
),
'phabricator-jump-nav' =>
array(
'uri' => '/res/745c0e89/rsrc/css/application/directory/phabricator-jump-nav.css',

View file

@ -1001,6 +1001,8 @@ phutil_register_library_map(array(
'PhabricatorHeaderView' => 'view/layout/PhabricatorHeaderView.php',
'PhabricatorHelpController' => 'applications/help/controller/PhabricatorHelpController.php',
'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/PhabricatorHelpKeyboardShortcutController.php',
'PhabricatorHovercardExample' => 'applications/uiexample/examples/PhabricatorHovercardExample.php',
'PhabricatorHovercardView' => 'view/widget/hovercard/PhabricatorHovercardView.php',
'PhabricatorIRCBot' => 'infrastructure/daemon/bot/PhabricatorIRCBot.php',
'PhabricatorIRCProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php',
'PhabricatorIRCProtocolHandler' => 'infrastructure/daemon/bot/handler/PhabricatorIRCProtocolHandler.php',
@ -2677,6 +2679,8 @@ phutil_register_library_map(array(
'PhabricatorHeaderView' => 'AphrontView',
'PhabricatorHelpController' => 'PhabricatorController',
'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
'PhabricatorHovercardExample' => 'PhabricatorUIExample',
'PhabricatorHovercardView' => 'AphrontView',
'PhabricatorIRCBot' => 'PhabricatorDaemon',
'PhabricatorIRCProtocolAdapter' => 'PhabricatorBaseProtocolAdapter',
'PhabricatorIRCProtocolHandler' => 'PhabricatorBotHandler',

View file

@ -0,0 +1,77 @@
<?php
final class PhabricatorHovercardExample extends PhabricatorUIExample {
public function getName() {
return 'Hovercard';
}
public function getDescription() {
return hsprintf('Use <tt>PhabricatorHovercardView</tt> to render '.
'hovercards. Aren\'t I genius?');
}
public function renderExample() {
$request = $this->getRequest();
$user = $request->getUser();
$elements = array();
$diff_handle = $this->createBasicDummyHandle(
"Introduce cooler Differential Revisions",
PhabricatorPHIDConstants::PHID_TYPE_DREV);
$panel = $this->createPanel("Differential Hovercard");
$panel->appendChild(id(new PhabricatorHovercardView())
->setObjectHandle($diff_handle)
->addField(pht('Author'), $user->getUsername())
->addField(pht('Updated'), phabricator_datetime(time(), $user))
->addAction(pht('Subscribe'), '/dev/random')
->setUser($user));
$elements[] = $panel;
$task_handle = $this->createBasicDummyHandle(
"Improve Mobile Experience for Phabricator",
PhabricatorPHIDConstants::PHID_TYPE_TASK);
$tag = id(new PhabricatorTagView())
->setType(PhabricatorTagView::TYPE_STATE)
->setBackgroundColor(PhabricatorTagView::COLOR_BLACK)
->setName('Abandoned (Really)');
$panel = $this->createPanel("Maniphest Hovercard");
$panel->appendChild(id(new PhabricatorHovercardView())
->setObjectHandle($task_handle)
->setUser($user)
->addField(pht('Assigned to'), $user->getUsername())
->addField(pht('Dependent Tasks'), 'T123, T124, T125')
->addAction(pht('Subscribe'), '/dev/random')
->addAction(pht('Create Subtask'), '/dev/urandom')
->addTag($tag));
$elements[] = $panel;
$user_handle = $this->createBasicDummyHandle(
'gwashington',
PhabricatorPHIDConstants::PHID_TYPE_USER,
'George Washington');
$user_handle->setImageURI(
celerity_get_resource_uri('/rsrc/image/people/washington.png'));
$panel = $this->createPanel("Whatevery Hovercard");
$panel->appendChild(id(new PhabricatorHovercardView())
->setObjectHandle($user_handle)
->addField(pht('Status'), 'Available')
->addField(pht('Member since'), '30. February 1750')
->addAction(pht('Start a Conpherence'), '/dev/null')
->setUser($user));
$elements[] = $panel;
return phutil_implode_html("", $elements);
}
private function createPanel($header) {
$panel = new AphrontPanelView();
$panel->setNoBackground();
$panel->setHeader($header);
return $panel;
}
}

View file

@ -17,4 +17,30 @@ abstract class PhabricatorUIExample {
abstract public function getDescription();
abstract public function renderExample();
protected function createBasicDummyHandle($name, $type, $fullname = null,
$uri = null) {
$id = mt_rand(15, 9999);
$handle = new PhabricatorObjectHandle();
$handle->setAlternateID(mt_rand(15, 9999));
$handle->setName($name);
$handle->setType($type);
$handle->setPHID(PhabricatorPHID::generateNewPHID($type));
if ($fullname) {
$handle->setFullName($fullname);
} else {
$handle->setFullName(sprintf('%s%d: %s',
substr($type, 0, 1),
$id,
$name));
}
if ($uri) {
$handle->setURI($uri);
}
return $handle;
}
}

View file

@ -0,0 +1,170 @@
<?php
/**
* The default one-for-all hovercard. We may derive from this one to create
* more specialized ones
*/
final class PhabricatorHovercardView extends AphrontView {
/**
* @var PhabricatorObjectHandle
*/
private $handle;
private $title = array();
private $detail;
private $tags = array();
private $fields = array();
private $actions = array();
/**
* For overriding in case of Countdown, Paste, Pholio
*/
private $body;
private $color = 'grey';
public function setObjectHandle(PhabricatorObjectHandle $handle) {
$this->handle = $handle;
return $this;
}
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function setDetail($detail) {
$this->detail = $detail;
return $this;
}
public function addField($label, $value) {
$this->fields[] = array(
'label' => $label,
'value' => $value,
);
return $this;
}
public function addAction($label, $uri, $workflow = false) {
$this->actions[] = array(
'label' => $label,
'uri' => $uri,
'workflow' => $workflow,
);
return $this;
}
public function addTag(PhabricatorTagView $tag) {
$this->tags[] = $tag;
return $this;
}
public function setColor($color) {
$this->color = $color;
return $this;
}
public function render() {
$handle = $this->handle;
$user = $this->getUser();
$id = $handle->getAlternateID();
$type = $handle->getType();
require_celerity_resource("phabricator-hovercard-view-css");
$title = array();
if ($this->tags) {
$title[] = ' ';
$title[] = phutil_tag(
'span',
array(
'class' => 'phabricator-hovercard-tags',
),
array_interleave(' ', $this->tags));
}
$title[] = pht("%s: %s", $handle->getTypeName(), substr($type, 0, 1) . $id);
$body = array();
if ($this->detail) {
$body[] = hsprintf('<strong>%s</strong>', $this->detail);
} else {
// Fallback for object handles
$body[] = hsprintf('<strong>%s</strong>', $handle->getFullName());
}
foreach ($this->fields as $field) {
$body[] = hsprintf('<b>%s:</b> <span>%s</span>',
$field['label'], $field['value']);
}
$body = phutil_implode_html(phutil_tag('br'), $body);
if ($handle->getImageURI()) {
// Probably a user, we don't need to assume something else
// "Prepend" the image by appending $body
$body = phutil_tag(
'div',
array(
'class' => 'profile-header-picture-frame',
'style' => 'background-image: url('.$handle->getImageURI().');',
),
'')
->appendHTML($body);
}
$buttons = array();
foreach ($this->actions as $action) {
$options = array(
'class' => 'button grey',
'href' => $action['uri'],
);
if ($action['workflow']) {
$options['sigil'] = 'workflow';
$buttons[] = javelin_tag(
'a',
$options,
$action['label']);
} else {
$buttons[] = phutil_tag(
'a',
$options,
$action['label']);
}
}
$tail = null;
if ($buttons) {
$tail = phutil_tag('div',
array('class' => 'phabricator-hovercard-tail'),
$buttons);
}
// Assemble container
// TODO: Add color support
$content = hsprintf(
'%s%s%s',
phutil_tag('div', array('class' => 'phabricator-hovercard-head'), $title),
phutil_tag('div', array('class' => 'phabricator-hovercard-body'), $body),
$tail);
$hovercard = phutil_tag("div",
array(
"class" => "phabricator-hovercard-container",
),
$content);
// Wrap for thick border
// and later the tip at the bottom
return phutil_tag('div',
array(
'class' => 'phabricator-hovercard-wrapper',
),
$hovercard);
}
}

View file

@ -0,0 +1,69 @@
/**
* @provides phabricator-hovercard-view-css
*/
.phabricator-hovercard-wrapper {
border-radius: 4px;
width: 400px;
margin: auto;
padding: 4px;
background-color: #cccccc;
}
.device-phone .phabricator-hovercard-wrapper {
width: 300px;
}
.phabricator-hovercard-container {
border-radius: 3px;
border: 1px solid #666666;
}
.phabricator-hovercard-head {
padding: 10px 15px;
font-weight: bold;
font-size: 15px;
white-space: nowrap;
color: white;
text-shadow: 0 1px 0 #333333;
font-weight: bold;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
overflow: hidden;
background-color: #559911;
}
.phabricator-hovercard-tags {
font-size: 13px;
float: right;
white-space: normal;
}
.phabricator-hovercard-body {
padding: 15px;
background-color: white;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
.phabricator-hovercard-body .profile-header-picture-frame {
float: left;
margin: 0;
margin-right: 10px;
margin-bottom: 5px;
width: 50px;
height: 50px;
}
.phabricator-hovercard-tail {
padding: 3px 2px;
background-color: #eeeeee;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
.phabricator-hovercard-tail button,
.phabricator-hovercard-tail a.button {
margin: 3px;
}