1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-24 22:40:55 +01:00

Improve PhabricatorPropertyListView and add section headers

Summary:
  - For Drydock, I want to add section headers to separate user-defined attributes from global attributes.
  - Some day for Differential, I want to add "Summary" and "Test Plan" section headers.
  - Clean up some stuff a bit; drop the multiple APIs for setting text content. Explicitly disallow appendChild().
  - Build out the UIExample a bit.

Test Plan:
{F24821}

{F24822}

Reviewers: chad, btrahan

Reviewed By: chad

CC: aran

Maniphest Tasks: T2015

Differential Revision: https://secure.phabricator.com/D4000
This commit is contained in:
epriestley 2012-11-20 18:01:18 -08:00
parent df76ed9545
commit 10a9f2a15f
4 changed files with 234 additions and 68 deletions

View file

@ -35,6 +35,90 @@ final class PhabricatorPropertyListExample extends PhabricatorUIExample {
'viverra. Nunc tempus tempor quam id iaculis. Maecenas lectus '.
'velit, aliquam et consequat quis, tincidunt id dolor.');
return $view;
$view->addSectionHeader('Colors of the Rainbow');
$view->addProperty('R', 'Red');
$view->addProperty('O', 'Orange');
$view->addProperty('Y', 'Yellow');
$view->addProperty('G', 'Green');
$view->addProperty('B', 'Blue');
$view->addProperty('I', 'Indigo');
$view->addProperty('V', 'Violet');
$view->addSectionHeader('Haiku About Pasta');
$view->addTextContent(
'this is a pasta<br />'.
'haiku. it is very bad.<br />'.
'what did you expect?');
$edge_cases_header = id(new PhabricatorHeaderView())
->setHeader(pht('Edge Cases'));
$edge_cases_view = new PhabricatorPropertyListView();
$edge_cases_view->addProperty(
pht('Description'),
pht('These layouts test UI edge cases in the element. This block '.
'tests wrapping and overflow behavior.'));
$edge_cases_view->addProperty(
pht('A Very Very Very Very Very Very Very Very Very Long Property Label'),
pht('This property label and property value are quite long. They '.
'demonstrate the wrapping behavior of the element, or lack thereof '.
'if something terrible has happened.'));
$edge_cases_view->addProperty(
pht('AVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongUnbrokenPropertyLabel'),
pht('Thispropertylabelandpropertyvaluearequitelongandhave'.
'nospacestheydemonstratetheoverflowbehavioroftheelement'.
'orlackthereof.'));
$edge_cases_view->addProperty(
pht('Description'),
pht('The next section is an empty text block.'));
$edge_cases_view->addTextContent('');
$edge_cases_view->addProperty(
pht('Description'),
pht('This section should have multiple properties with the same name.'));
$edge_cases_view->addProperty(
pht('Joe'),
pht('Smith'));
$edge_cases_view->addProperty(
pht('Joe'),
pht('Smith'));
$edge_cases_view->addProperty(
pht('Joe'),
pht('Smith'));
$edge_cases_view->addProperty(
pht('Description'),
pht('The next section shows adjacent section headers.'));
$edge_cases_view->addSectionHeader('Several');
$edge_cases_view->addSectionHeader('Adjacent');
$edge_cases_view->addSectionHeader('Section');
$edge_cases_view->addSectionHeader('Headers');
$edge_cases_view->addProperty(
pht('Description'),
pht('The next section is several adjacent text blocks.'));
$edge_cases_view->addTextContent('Lorem');
$edge_cases_view->addTextContent('ipsum');
$edge_cases_view->addTextContent('dolor');
$edge_cases_view->addTextContent('sit');
$edge_cases_view->addTextContent('amet...');
return array(
$view,
$edge_cases_header,
$edge_cases_view,
);
}
}

View file

@ -4,7 +4,16 @@ abstract class AphrontView {
protected $children = array();
protected function canAppendChild() {
return true;
}
final public function appendChild($child) {
if (!$this->canAppendChild()) {
$class = get_class($this);
throw new Exception(
"View '{$class}' does not support children.");
}
$this->children[] = $child;
return $this;
}

View file

@ -2,56 +2,69 @@
final class PhabricatorPropertyListView extends AphrontView {
private $properties = array();
private $parts = array();
protected function canAppendChild() {
return false;
}
public function addProperty($key, $value) {
$this->properties[$key] = $value;
$current = array_pop($this->parts);
if (!$current || $current['type'] != 'property') {
if ($current) {
$this->parts[] = $current;
}
$current = array(
'type' => 'property',
'list' => array(),
);
}
$current['list'][] = array(
'key' => $key,
'value' => $value,
);
$this->parts[] = $current;
return $this;
}
public function addSectionHeader($name) {
$this->parts[] = array(
'type' => 'section',
'name' => $name,
);
return $this;
}
public function addTextContent($content) {
return $this->appendChild(
phutil_render_tag(
'div',
array(
'class' => 'phabricator-property-list-text-content',
),
$content));
$this->parts[] = array(
'type' => 'text',
'content' => $content,
);
return $this;
}
public function render() {
require_celerity_resource('phabricator-property-list-view-css');
$items = array();
foreach ($this->properties as $key => $value) {
$items[] = phutil_render_tag(
'dt',
array(
'class' => 'phabricator-property-key',
),
phutil_escape_html($key));
$items[] = phutil_render_tag(
'dd',
array(
'class' => 'phabricator-property-value',
),
$this->renderSingleView($value));
}
$list = phutil_render_tag(
'dl',
array(
),
$this->renderSingleView($items));
$content = $this->renderChildren();
if (strlen($content)) {
$content = phutil_render_tag(
'div',
array(
'class' => 'phabricator-property-list-content',
),
$content);
foreach ($this->parts as $part) {
$type = $part['type'];
switch ($type) {
case 'property':
$items[] = $this->renderPropertyPart($part);
break;
case 'section':
$items[] = $this->renderSectionPart($part);
break;
case 'text':
$items[] = $this->renderTextPart($part);
break;
default:
throw new Exception("Unknown part type '{$type}'!");
}
}
return phutil_render_tag(
@ -59,14 +72,61 @@ final class PhabricatorPropertyListView extends AphrontView {
array(
'class' => 'phabricator-property-list-view',
),
$list.
// NOTE: We need this (which is basically a "clear: both;" div) to make
// sure the property list is taller than the action list for objects with
// few properties but many actions. Otherwise, the action list may
// obscure the document content.
'<div class="phabriator-property-list-view-end"></div>').
$content;
$this->renderSingleView($items));
}
private function renderPropertyPart(array $part) {
$items = array();
foreach ($part['list'] as $spec) {
$key = $spec['key'];
$value = $spec['value'];
$items[] = phutil_render_tag(
'dt',
array(
'class' => 'phabricator-property-list-key',
),
phutil_escape_html($key));
$items[] = phutil_render_tag(
'dd',
array(
'class' => 'phabricator-property-list-value',
),
$this->renderSingleView($value));
}
$list = phutil_render_tag(
'dl',
array(
'class' => 'phabricator-property-list-properties',
),
$this->renderSingleView($items));
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-property-list-container',
),
$list.
'<div class="phabriator-property-list-view-end"></div>');
}
private function renderSectionPart(array $part) {
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-property-list-section-header',
),
phutil_escape_html($part['name']));
}
private function renderTextPart(array $part) {
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-property-list-text-content',
),
$part['content']);
}
}

View file

@ -5,30 +5,34 @@
.phabricator-property-list-view {
border-color: #dbdbdb;
border-style: solid;
border-width: 1px 0;
border-width: 1px 0 0 0;
}
.phabricator-property-list-container {
border-color: #dbdbdb;
border-style: solid;
border-width: 0 0 1px;
background-color: #f9f9f9;
}
.phabriator-property-list-view-end {
clear: both;
.device-desktop .phabricator-property-list-container {
padding: 1em 0 0.5em;
}
.device-desktop .phabricator-property-list-view {
padding: 1em 0 0.75em;
.device-tablet .phabricator-property-list-container,
.device-phone .phabricator-property-list-container {
padding: .5em 0;
}
.device-tablet .phabricator-property-list-view,
.device-phone .phabricator-property-list-view {
padding: .5em;
}
.phabricator-property-key {
.phabricator-property-list-key {
color: #333333;
font-weight: bold;
overflow: hidden;
white-space: nowrap;
}
.device-desktop .phabricator-property-key {
width: 12%;
.device-desktop .phabricator-property-list-key {
width: 15%;
margin-left: 1%;
text-align: right;
float: left;
@ -36,35 +40,44 @@
margin-bottom: .5em;
}
.device-tablet .phabricator-property-key,
.device-phone .phabricator-property-key {
.device-tablet .phabricator-property-list-key,
.device-phone .phabricator-property-list-key {
padding-left: .5em;
}
.phabricator-property-value {
.phabricator-property-list-value {
color: #333333;
overflow: hidden;
}
.device-desktop .phabricator-property-value {
width: 53%;
.device-desktop .phabricator-property-list-value {
width: 50%;
margin-left: 1%;
float: left;
margin-bottom: .5em;
}
.device-tablet .phabricator-property-value,
.device-phone .phabricator-property-value {
.device-tablet .phabricator-property-list-value,
.device-phone .phabricator-property-list-value {
padding-left: 1.5em;
margin-bottom: .5em;
}
.phabricator-property-list-content {
background: #fdfdfd;
.phabriator-property-list-view-end {
clear: both;
}
.phabricator-property-list-section-header {
background: #f0f0f0;
color: #666666;
padding: 4px 18px;
border-bottom: 1px solid #dbdbdb;
}
.phabricator-property-list-text-content {
padding: 12px 18px;
background: #fdfdfd;
border-bottom: 1px solid #dbdbdb;
}
/* When we follow an action list view on the Desktop, move down 30px so the