@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
1<?php
2
3final class PHUIListItemView extends AphrontTagView {
4
5 const TYPE_LINK = 'type-link';
6 const TYPE_SPACER = 'type-spacer';
7 const TYPE_LABEL = 'type-label';
8 const TYPE_BUTTON = 'type-button';
9 const TYPE_CUSTOM = 'type-custom';
10 const TYPE_DIVIDER = 'type-divider';
11 const TYPE_ICON = 'type-icon';
12
13 const STATUS_WARN = 'phui-list-item-warn';
14 const STATUS_FAIL = 'phui-list-item-fail';
15
16 private $name;
17 private $href;
18 private $type = self::TYPE_LINK;
19 private $isExternal;
20 private $key;
21 private $icon;
22 private $selected;
23 private $disabled;
24 private $renderNameAsTooltip;
25 private $statusColor;
26 private $order;
27 private $aural;
28 private $profileImage;
29 private $indented;
30 private $hideInApplicationMenu;
31 private $icons = array();
32 private $openInNewWindow = false;
33 private $tooltip;
34 private $actionIcon;
35 private $actionIconHref;
36 private $count;
37 private $rel;
38 private $dropdownMenu;
39 private $keyCommand;
40 private $ariaLabel;
41
42 public function setOpenInNewWindow($open_in_new_window) {
43 $this->openInNewWindow = $open_in_new_window;
44 return $this;
45 }
46
47 public function getOpenInNewWindow() {
48 return $this->openInNewWindow;
49 }
50
51 public function setRel($rel) {
52 $this->rel = $rel;
53 return $this;
54 }
55
56 public function getRel() {
57 return $this->rel;
58 }
59
60 public function setHideInApplicationMenu($hide) {
61 $this->hideInApplicationMenu = $hide;
62 return $this;
63 }
64
65 public function getHideInApplicationMenu() {
66 return $this->hideInApplicationMenu;
67 }
68
69 public function setDropdownMenu(PhabricatorActionListView $actions) {
70
71 $this->dropdownMenu = $actions;
72
73 // TODO: "PHUICrumbsView" currently creates a bad copy of list items
74 // by reading some of their properties. To survive this copy step, we
75 // need to mutate "$this" immediately or the "Create Object" dropdown
76 // when multiple create forms exist breaks.
77
78 if (!$this->actionIcon) {
79 Javelin::initBehavior('phui-dropdown-menu');
80 $this->addSigil('phui-dropdown-menu');
81 $this->setMetadata($actions->getDropdownMenuMetadata());
82 }
83
84 return $this;
85 }
86
87 public function setAural($aural) {
88 $this->aural = $aural;
89 return $this;
90 }
91
92 public function getAural() {
93 return $this->aural;
94 }
95
96 public function setOrder($order) {
97 $this->order = $order;
98 return $this;
99 }
100
101 public function getOrder() {
102 return $this->order;
103 }
104
105 public function setRenderNameAsTooltip($render_name_as_tooltip) {
106 $this->renderNameAsTooltip = $render_name_as_tooltip;
107 return $this;
108 }
109
110 public function getRenderNameAsTooltip() {
111 return $this->renderNameAsTooltip;
112 }
113
114 public function setSelected($selected) {
115 $this->selected = $selected;
116 return $this;
117 }
118
119 public function getSelected() {
120 return $this->selected;
121 }
122
123 public function setIcon($icon) {
124 $this->icon = $icon;
125 return $this;
126 }
127
128 public function setProfileImage($image) {
129 $this->profileImage = $image;
130 return $this;
131 }
132
133 public function getIcon() {
134 return $this->icon;
135 }
136
137 public function setCount($count) {
138 $this->count = $count;
139 return $this;
140 }
141
142 public function setIndented($indented) {
143 $this->indented = $indented;
144 return $this;
145 }
146
147 public function getIndented() {
148 return $this->indented;
149 }
150
151 public function setKey($key) {
152 $this->key = (string)$key;
153 return $this;
154 }
155
156 public function getKey() {
157 return $this->key;
158 }
159
160 public function setType($type) {
161 $this->type = $type;
162 return $this;
163 }
164
165 public function getType() {
166 return $this->type;
167 }
168
169 public function setHref($href) {
170 $this->href = $href;
171 return $this;
172 }
173
174 public function getHref() {
175 return $this->href;
176 }
177
178 public function setName($name) {
179 $this->name = $name;
180 return $this;
181 }
182
183 public function getName() {
184 return $this->name;
185 }
186
187 public function setActionIcon($icon, $href) {
188 $this->actionIcon = $icon;
189 $this->actionIconHref = $href;
190 return $this;
191 }
192
193 public function setIsExternal($is_external) {
194 $this->isExternal = $is_external;
195 return $this;
196 }
197
198 public function getIsExternal() {
199 return $this->isExternal;
200 }
201
202 public function setStatusColor($color) {
203 $this->statusColor = $color;
204 return $this;
205 }
206
207 public function addIcon($icon) {
208 $this->icons[] = $icon;
209 return $this;
210 }
211
212 public function getIcons() {
213 return $this->icons;
214 }
215
216 public function setTooltip($tooltip) {
217 $this->tooltip = $tooltip;
218 return $this;
219 }
220
221 /**
222 * Explicitly set an aria-label attribute for accessibility. Only to be used
223 * when no other means of differentiation are already available.
224 * @param string $aria_label aria-label text to add to a list item
225 */
226 public function setAriaLabel($aria_label) {
227 $this->ariaLabel = $aria_label;
228 return $this;
229 }
230
231 protected function getTagName() {
232 return 'li';
233 }
234
235 public function setKeyCommand($key_command) {
236 $this->keyCommand = $key_command;
237 return $this;
238 }
239
240 public function getKeyCommand() {
241 return $this->keyCommand;
242 }
243
244 protected function getTagAttributes() {
245 $classes = array();
246 $classes[] = 'phui-list-item-view';
247 $classes[] = 'phui-list-item-'.$this->type;
248
249 if ($this->icon || $this->profileImage) {
250 $classes[] = 'phui-list-item-has-icon';
251 }
252
253 if ($this->selected) {
254 $classes[] = 'phui-list-item-selected';
255 }
256
257 if ($this->disabled) {
258 $classes[] = 'phui-list-item-disabled';
259 }
260
261 if ($this->statusColor) {
262 $classes[] = $this->statusColor;
263 }
264
265 if ($this->actionIcon) {
266 $classes[] = 'phui-list-item-has-action-icon';
267 }
268
269 $sigil = null;
270 $metadata = null;
271 if ($this->dropdownMenu) {
272 $classes[] = 'dropdown';
273 if (!$this->actionIcon) {
274 $classes[] = 'dropdown-with-caret';
275 Javelin::initBehavior('phui-dropdown-menu');
276 $sigil = 'phui-dropdown-menu';
277 $metadata = $this->dropdownMenu->getDropdownMenuMetadata();
278 }
279 }
280
281 return array(
282 'class' => $classes,
283 'sigil' => $sigil,
284 'meta' => $metadata,
285 );
286 }
287
288 public function setDisabled($disabled) {
289 $this->disabled = $disabled;
290 return $this;
291 }
292
293 public function getDisabled() {
294 return $this->disabled;
295 }
296
297 protected function getTagContent() {
298 $name = null;
299 $icon = null;
300 $meta = null;
301 $sigil = array();
302
303 if ($this->name) {
304 if ($this->getRenderNameAsTooltip()) {
305 Javelin::initBehavior('phabricator-tooltips');
306 $sigil[] = 'has-tooltip';
307 $meta = array(
308 'tip' => $this->name,
309 'align' => 'E',
310 );
311 } else {
312 if ($this->tooltip) {
313 Javelin::initBehavior('phabricator-tooltips');
314 $sigil[] = 'has-tooltip';
315 $meta = array(
316 'tip' => $this->tooltip,
317 'align' => 'E',
318 'size' => 300,
319 );
320 }
321
322 $external = null;
323 if ($this->isExternal) {
324 $external = " \xE2\x86\x97";
325 }
326
327 // If this element has an aural representation, make any name visual
328 // only. This is primarily dealing with the links in the main menu like
329 // "Profile" and "Logout". If we don't hide the name, the mobile
330 // version of these elements will have two redundant names.
331
332 $classes = array();
333 $classes[] = 'phui-list-item-name';
334 if ($this->aural !== null) {
335 $classes[] = 'visual-only';
336 }
337
338 $name = phutil_tag(
339 'span',
340 array(
341 'class' => implode(' ', $classes),
342 ),
343 array(
344 $this->name,
345 $external,
346 ));
347 }
348 }
349
350 $aural = null;
351 if ($this->aural !== null) {
352 $aural = javelin_tag(
353 'span',
354 array(
355 'aural' => true,
356 ),
357 $this->aural);
358 }
359
360 if ($this->icon) {
361 $icon_name = $this->icon;
362 if ($this->getDisabled()) {
363 $icon_name .= ' grey';
364 }
365
366 $icon = id(new PHUIIconView())
367 ->addClass('phui-list-item-icon')
368 ->setIcon($icon_name);
369 }
370
371 if ($this->profileImage) {
372 $icon = id(new PHUIIconView())
373 ->setHeadSize(PHUIIconView::HEAD_SMALL)
374 ->addClass('phui-list-item-icon')
375 ->setImage($this->profileImage);
376 }
377
378 $classes = array();
379 if ($this->href) {
380 $classes[] = 'phui-list-item-href';
381 }
382
383 if ($this->indented) {
384 $classes[] = 'phui-list-item-indented';
385 }
386
387 $action_link = $this->newActionIconView();
388
389 $count = null;
390 if ($this->count) {
391 $count = phutil_tag(
392 'span',
393 array(
394 'class' => 'phui-list-item-count',
395 ),
396 $this->count);
397 }
398
399 $caret = null;
400 if ($this->dropdownMenu && !$this->actionIcon) {
401 $caret = id(new PHUIIconView())
402 ->setIcon('fa-caret-down');
403 }
404
405 $icons = $this->getIcons();
406
407 $key_command = null;
408 if ($this->keyCommand) {
409 $key_command = phutil_tag(
410 'span',
411 array(
412 'class' => 'keyboard-shortcut-key',
413 ),
414 $this->keyCommand);
415 $sigil[] = 'has-key-command';
416 $meta['keyCommand'] = $this->keyCommand;
417 }
418
419 $list_item = javelin_tag(
420 $this->href ? 'a' : 'div',
421 array(
422 'href' => $this->href,
423 'class' => implode(' ', $classes),
424 'meta' => $meta,
425 'sigil' => implode(' ', $sigil),
426 'aria-label' => $this->ariaLabel,
427 'target' => $this->getOpenInNewWindow() ? '_blank' : null,
428 'rel' => $this->rel,
429 ),
430 array(
431 $aural,
432 $icon,
433 $icons,
434 $this->renderChildren(),
435 $name,
436 $count,
437 $key_command,
438 $caret,
439 ));
440
441 return array($list_item, $action_link);
442 }
443
444 private function newActionIconView() {
445 $action_icon = $this->actionIcon;
446 $action_href = $this->actionIconHref;
447
448 if ($action_icon === null) {
449 return null;
450 }
451
452 $icon_view = id(new PHUIIconView())
453 ->setIcon($action_icon)
454 ->addClass('phui-list-item-action-icon');
455
456 if ($this->dropdownMenu) {
457 Javelin::initBehavior('phui-dropdown-menu');
458 $sigil = 'phui-dropdown-menu';
459 $metadata = $this->dropdownMenu->getDropdownMenuMetadata();
460 } else {
461 $sigil = null;
462 $metadata = null;
463 }
464
465 return javelin_tag(
466 'a',
467 array(
468 'href' => $action_href,
469 'class' => 'phui-list-item-action-href',
470 'sigil' => $sigil,
471 'meta' => $metadata,
472 ),
473 $icon_view);
474 }
475
476}