@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at recaptime-dev/main 298 lines 6.8 kB view raw
1<?php 2 3final class PHUIPropertyListView extends AphrontView { 4 5 private $parts = array(); 6 private $object; 7 private $invokedWillRenderEvent; 8 private $actionList = null; 9 private $classes = array(); 10 private $stacked; 11 12 const ICON_SUMMARY = 'fa-align-left'; 13 const ICON_TESTPLAN = 'fa-file-text-o'; 14 15 protected function canAppendChild() { 16 return false; 17 } 18 19 public function setObject($object) { 20 $this->object = $object; 21 return $this; 22 } 23 24 public function setActionList(PhabricatorActionListView $list) { 25 $this->actionList = $list; 26 return $this; 27 } 28 29 public function getActionList() { 30 return $this->actionList; 31 } 32 33 public function setStacked($stacked) { 34 $this->stacked = $stacked; 35 return $this; 36 } 37 38 public function addClass($class) { 39 $this->classes[] = $class; 40 return $this; 41 } 42 43 public function addProperty($key, $value) { 44 $current = array_pop($this->parts); 45 46 if (!$current || $current['type'] != 'property') { 47 if ($current) { 48 $this->parts[] = $current; 49 } 50 $current = array( 51 'type' => 'property', 52 'list' => array(), 53 ); 54 } 55 56 $current['list'][] = array( 57 'key' => $key, 58 'value' => $value, 59 ); 60 61 $this->parts[] = $current; 62 return $this; 63 } 64 65 public function addSectionHeader($name, $icon = null) { 66 $this->parts[] = array( 67 'type' => 'section', 68 'name' => $name, 69 'icon' => $icon, 70 ); 71 return $this; 72 } 73 74 public function addTextContent($content) { 75 $this->parts[] = array( 76 'type' => 'text', 77 'content' => $content, 78 ); 79 return $this; 80 } 81 82 public function addRawContent($content) { 83 $this->parts[] = array( 84 'type' => 'raw', 85 'content' => $content, 86 ); 87 return $this; 88 } 89 90 public function addImageContent($content) { 91 $this->parts[] = array( 92 'type' => 'image', 93 'content' => $content, 94 ); 95 return $this; 96 } 97 98 public function invokeWillRenderEvent() { 99 if ($this->object && $this->getUser() && !$this->invokedWillRenderEvent) { 100 $event = new PhabricatorEvent( 101 PhabricatorEventType::TYPE_UI_WILLRENDERPROPERTIES, 102 array( 103 'object' => $this->object, 104 'view' => $this, 105 )); 106 $event->setUser($this->getUser()); 107 PhutilEventEngine::dispatchEvent($event); 108 } 109 $this->invokedWillRenderEvent = true; 110 } 111 112 /** 113 * Whether there are any properties to display in the list view. 114 * Useful to e.g. display an explanation via PHUIInfoView if false. 115 * 116 * @return bool 117 */ 118 public function hasAnyProperties() { 119 $this->invokeWillRenderEvent(); 120 121 if ($this->parts) { 122 return true; 123 } 124 125 return false; 126 } 127 128 public function render() { 129 $this->invokeWillRenderEvent(); 130 131 require_celerity_resource('phui-property-list-view-css'); 132 133 $items = array(); 134 135 $parts = $this->parts; 136 137 // If we have an action list, make sure we render a property part, even 138 // if there are no properties. Otherwise, the action list won't render. 139 if ($this->actionList) { 140 $this->classes[] = 'phui-property-list-has-actions'; 141 $have_property_part = false; 142 foreach ($this->parts as $part) { 143 if ($part['type'] == 'property') { 144 $have_property_part = true; 145 break; 146 } 147 } 148 if (!$have_property_part) { 149 $parts[] = array( 150 'type' => 'property', 151 'list' => array(), 152 ); 153 } 154 } 155 156 foreach ($parts as $part) { 157 $type = $part['type']; 158 switch ($type) { 159 case 'property': 160 $items[] = $this->renderPropertyPart($part); 161 break; 162 case 'section': 163 $items[] = $this->renderSectionPart($part); 164 break; 165 case 'text': 166 case 'image': 167 $items[] = $this->renderTextPart($part); 168 break; 169 case 'raw': 170 $items[] = $this->renderRawPart($part); 171 break; 172 default: 173 throw new Exception(pht("Unknown part type '%s'!", $type)); 174 } 175 } 176 $this->classes[] = 'phui-property-list-section'; 177 $classes = implode(' ', $this->classes); 178 179 return phutil_tag( 180 'div', 181 array( 182 'class' => $classes, 183 ), 184 array( 185 $items, 186 )); 187 } 188 189 private function renderPropertyPart(array $part) { 190 $items = array(); 191 foreach ($part['list'] as $spec) { 192 $key = $spec['key']; 193 $value = $spec['value']; 194 195 // NOTE: We append a space to each value to improve the behavior when the 196 // user double-clicks a property value (like a URI) to select it. Without 197 // the space, the label is also selected. 198 199 $items[] = phutil_tag( 200 'dt', 201 array( 202 'class' => 'phui-property-list-key', 203 ), 204 array($key, ' ')); 205 206 $items[] = phutil_tag( 207 'dd', 208 array( 209 'class' => 'phui-property-list-value', 210 ), 211 array($value, ' ')); 212 } 213 214 $stacked = ''; 215 if ($this->stacked) { 216 $stacked = 'phui-property-list-stacked'; 217 } 218 219 $list = phutil_tag( 220 'dl', 221 array( 222 'class' => 'phui-property-list-properties', 223 ), 224 $items); 225 226 $list = phutil_tag( 227 'div', 228 array( 229 'class' => 'phui-property-list-properties-wrap '.$stacked, 230 ), 231 array($list)); 232 233 $action_list = null; 234 if ($this->actionList) { 235 $action_list = phutil_tag( 236 'div', 237 array( 238 'class' => 'phui-property-list-actions', 239 ), 240 $this->actionList); 241 $this->actionList = null; 242 } 243 244 return phutil_tag( 245 'div', 246 array( 247 'class' => 'phui-property-list-container grouped', 248 ), 249 array($action_list, $list)); 250 } 251 252 private function renderSectionPart(array $part) { 253 $name = $part['name']; 254 if ($part['icon']) { 255 $icon = id(new PHUIIconView()) 256 ->setIcon($part['icon'].' bluegrey'); 257 $name = phutil_tag( 258 'span', 259 array( 260 'class' => 'phui-property-list-section-header-icon', 261 ), 262 array($icon, $name)); 263 } 264 265 return phutil_tag( 266 'div', 267 array( 268 'class' => 'phui-property-list-section-header', 269 ), 270 $name); 271 } 272 273 private function renderTextPart(array $part) { 274 $classes = array(); 275 $classes[] = 'phui-property-list-text-content'; 276 if ($part['type'] == 'image') { 277 $classes[] = 'phui-property-list-image-content'; 278 } 279 return phutil_tag( 280 'div', 281 array( 282 'class' => implode(' ', $classes), 283 ), 284 $part['content']); 285 } 286 287 private function renderRawPart(array $part) { 288 $classes = array(); 289 $classes[] = 'phui-property-list-raw-content'; 290 return phutil_tag( 291 'div', 292 array( 293 'class' => implode(' ', $classes), 294 ), 295 $part['content']); 296 } 297 298}