mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-27 01:02:42 +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:
parent
df76ed9545
commit
10a9f2a15f
4 changed files with 234 additions and 68 deletions
|
@ -35,6 +35,90 @@ final class PhabricatorPropertyListExample extends PhabricatorUIExample {
|
||||||
'viverra. Nunc tempus tempor quam id iaculis. Maecenas lectus '.
|
'viverra. Nunc tempus tempor quam id iaculis. Maecenas lectus '.
|
||||||
'velit, aliquam et consequat quis, tincidunt id dolor.');
|
'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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,16 @@ abstract class AphrontView {
|
||||||
|
|
||||||
protected $children = array();
|
protected $children = array();
|
||||||
|
|
||||||
|
protected function canAppendChild() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
final public function appendChild($child) {
|
final public function appendChild($child) {
|
||||||
|
if (!$this->canAppendChild()) {
|
||||||
|
$class = get_class($this);
|
||||||
|
throw new Exception(
|
||||||
|
"View '{$class}' does not support children.");
|
||||||
|
}
|
||||||
$this->children[] = $child;
|
$this->children[] = $child;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,56 +2,69 @@
|
||||||
|
|
||||||
final class PhabricatorPropertyListView extends AphrontView {
|
final class PhabricatorPropertyListView extends AphrontView {
|
||||||
|
|
||||||
private $properties = array();
|
private $parts = array();
|
||||||
|
|
||||||
|
protected function canAppendChild() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function addProperty($key, $value) {
|
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;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addTextContent($content) {
|
public function addTextContent($content) {
|
||||||
return $this->appendChild(
|
$this->parts[] = array(
|
||||||
phutil_render_tag(
|
'type' => 'text',
|
||||||
'div',
|
'content' => $content,
|
||||||
array(
|
);
|
||||||
'class' => 'phabricator-property-list-text-content',
|
return $this;
|
||||||
),
|
|
||||||
$content));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
require_celerity_resource('phabricator-property-list-view-css');
|
require_celerity_resource('phabricator-property-list-view-css');
|
||||||
|
|
||||||
$items = array();
|
$items = array();
|
||||||
foreach ($this->properties as $key => $value) {
|
foreach ($this->parts as $part) {
|
||||||
$items[] = phutil_render_tag(
|
$type = $part['type'];
|
||||||
'dt',
|
switch ($type) {
|
||||||
array(
|
case 'property':
|
||||||
'class' => 'phabricator-property-key',
|
$items[] = $this->renderPropertyPart($part);
|
||||||
),
|
break;
|
||||||
phutil_escape_html($key));
|
case 'section':
|
||||||
$items[] = phutil_render_tag(
|
$items[] = $this->renderSectionPart($part);
|
||||||
'dd',
|
break;
|
||||||
array(
|
case 'text':
|
||||||
'class' => 'phabricator-property-value',
|
$items[] = $this->renderTextPart($part);
|
||||||
),
|
break;
|
||||||
$this->renderSingleView($value));
|
default:
|
||||||
|
throw new Exception("Unknown part type '{$type}'!");
|
||||||
}
|
}
|
||||||
|
|
||||||
$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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return phutil_render_tag(
|
return phutil_render_tag(
|
||||||
|
@ -59,14 +72,61 @@ final class PhabricatorPropertyListView extends AphrontView {
|
||||||
array(
|
array(
|
||||||
'class' => 'phabricator-property-list-view',
|
'class' => 'phabricator-property-list-view',
|
||||||
),
|
),
|
||||||
$list.
|
$this->renderSingleView($items));
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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']);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,30 +5,34 @@
|
||||||
.phabricator-property-list-view {
|
.phabricator-property-list-view {
|
||||||
border-color: #dbdbdb;
|
border-color: #dbdbdb;
|
||||||
border-style: solid;
|
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;
|
background-color: #f9f9f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phabriator-property-list-view-end {
|
.device-desktop .phabricator-property-list-container {
|
||||||
clear: both;
|
padding: 1em 0 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-desktop .phabricator-property-list-view {
|
.device-tablet .phabricator-property-list-container,
|
||||||
padding: 1em 0 0.75em;
|
.device-phone .phabricator-property-list-container {
|
||||||
|
padding: .5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-tablet .phabricator-property-list-view,
|
.phabricator-property-list-key {
|
||||||
.device-phone .phabricator-property-list-view {
|
|
||||||
padding: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.phabricator-property-key {
|
|
||||||
color: #333333;
|
color: #333333;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-desktop .phabricator-property-key {
|
.device-desktop .phabricator-property-list-key {
|
||||||
width: 12%;
|
width: 15%;
|
||||||
margin-left: 1%;
|
margin-left: 1%;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -36,35 +40,44 @@
|
||||||
margin-bottom: .5em;
|
margin-bottom: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-tablet .phabricator-property-key,
|
.device-tablet .phabricator-property-list-key,
|
||||||
.device-phone .phabricator-property-key {
|
.device-phone .phabricator-property-list-key {
|
||||||
padding-left: .5em;
|
padding-left: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phabricator-property-value {
|
.phabricator-property-list-value {
|
||||||
color: #333333;
|
color: #333333;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-desktop .phabricator-property-value {
|
.device-desktop .phabricator-property-list-value {
|
||||||
width: 53%;
|
width: 50%;
|
||||||
margin-left: 1%;
|
margin-left: 1%;
|
||||||
float: left;
|
float: left;
|
||||||
margin-bottom: .5em;
|
margin-bottom: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-tablet .phabricator-property-value,
|
.device-tablet .phabricator-property-list-value,
|
||||||
.device-phone .phabricator-property-value {
|
.device-phone .phabricator-property-list-value {
|
||||||
padding-left: 1.5em;
|
padding-left: 1.5em;
|
||||||
margin-bottom: .5em;
|
margin-bottom: .5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phabricator-property-list-content {
|
.phabriator-property-list-view-end {
|
||||||
background: #fdfdfd;
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phabricator-property-list-section-header {
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: #666666;
|
||||||
|
padding: 4px 18px;
|
||||||
border-bottom: 1px solid #dbdbdb;
|
border-bottom: 1px solid #dbdbdb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.phabricator-property-list-text-content {
|
.phabricator-property-list-text-content {
|
||||||
padding: 12px 18px;
|
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
|
/* When we follow an action list view on the Desktop, move down 30px so the
|
||||||
|
|
Loading…
Reference in a new issue