@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 PHUIListView extends AphrontTagView {
4
5 const NAVBAR_LIST = 'phui-list-navbar';
6 const NAVBAR_VERTICAL = 'phui-list-navbar-vertical';
7 const SIDENAV_LIST = 'phui-list-sidenav';
8 const TABBAR_LIST = 'phui-list-tabbar';
9
10 private $items = array();
11 private $type;
12
13 protected function canAppendChild() {
14 return false;
15 }
16
17 public function newLabel($name, $key = null) {
18 $item = id(new PHUIListItemView())
19 ->setType(PHUIListItemView::TYPE_LABEL)
20 ->setName($name);
21
22 if ($key !== null) {
23 $item->setKey($key);
24 }
25
26 $this->addMenuItem($item);
27
28 return $item;
29 }
30
31 public function newLink($name, $href, $key = null) {
32 $item = id(new PHUIListItemView())
33 ->setType(PHUIListItemView::TYPE_LINK)
34 ->setName($name)
35 ->setHref($href);
36
37 if ($key !== null) {
38 $item->setKey($key);
39 }
40
41 $this->addMenuItem($item);
42
43 return $item;
44 }
45
46 public function newButton($name, $href) {
47 $item = id(new PHUIListItemView())
48 ->setType(PHUIListItemView::TYPE_BUTTON)
49 ->setName($name)
50 ->setHref($href);
51
52 $this->addMenuItem($item);
53
54 return $item;
55 }
56
57 public function addMenuItem(PHUIListItemView $item) {
58 return $this->addMenuItemAfter(null, $item);
59 }
60
61 public function addMenuItemAfter($key, PHUIListItemView $item) {
62 if ($key === null) {
63 $this->items[] = $item;
64 return $this;
65 }
66
67 if (!$this->getItem($key)) {
68 throw new Exception(pht("No such key '%s' to add menu item after!",
69 $key));
70 }
71
72 $result = array();
73 foreach ($this->items as $other) {
74 $result[] = $other;
75 if ($other->getKey() == $key) {
76 $result[] = $item;
77 }
78 }
79
80 $this->items = $result;
81 return $this;
82 }
83
84 public function addMenuItemBefore($key, PHUIListItemView $item) {
85 if ($key === null) {
86 array_unshift($this->items, $item);
87 return $this;
88 }
89
90 $this->requireKey($key);
91
92 $result = array();
93 foreach ($this->items as $other) {
94 if ($other->getKey() == $key) {
95 $result[] = $item;
96 }
97 $result[] = $other;
98 }
99
100 $this->items = $result;
101 return $this;
102 }
103
104 public function addMenuItemToLabel($key, PHUIListItemView $item) {
105 $this->requireKey($key);
106
107 $other = $this->getItem($key);
108 if ($other->getType() != PHUIListItemView::TYPE_LABEL) {
109 throw new Exception(pht("Menu item '%s' is not a label!", $key));
110 }
111
112 $seen = false;
113 $after = null;
114 foreach ($this->items as $other) {
115 if (!$seen) {
116 if ($other->getKey() == $key) {
117 $seen = true;
118 }
119 } else {
120 if ($other->getType() == PHUIListItemView::TYPE_LABEL) {
121 break;
122 }
123 }
124 $after = $other->getKey();
125 }
126
127 return $this->addMenuItemAfter($after, $item);
128 }
129
130 private function requireKey($key) {
131 if (!$this->getItem($key)) {
132 throw new Exception(pht("No menu item with key '%s' exists!", $key));
133 }
134 }
135
136 public function getItem($key) {
137 $key = (string)$key;
138
139 // NOTE: We could optimize this, but need to update any map when items have
140 // their keys change. Since that's moderately complex, wait for a profile
141 // or use case.
142
143 foreach ($this->items as $item) {
144 if ($item->getKey() == $key) {
145 return $item;
146 }
147 }
148
149 return null;
150 }
151
152 public function getItems() {
153 return $this->items;
154 }
155
156 public function willRender() {
157 $key_map = array();
158 foreach ($this->items as $item) {
159 $key = $item->getKey();
160 if ($key !== null) {
161 if (isset($key_map[$key])) {
162 throw new Exception(
163 pht("Menu contains duplicate items with key '%s'!", $key));
164 }
165 $key_map[$key] = $item;
166 }
167 }
168 }
169
170 protected function getTagName() {
171 return 'ul';
172 }
173
174 public function setType($type) {
175 $this->type = $type;
176 return $this;
177 }
178
179 protected function getTagAttributes() {
180 require_celerity_resource('phui-list-view-css');
181 $classes = array();
182 $classes[] = 'phui-list-view';
183 if ($this->type) {
184 switch ($this->type) {
185 case self::NAVBAR_LIST:
186 $classes[] = 'phui-list-navbar';
187 $classes[] = 'phui-list-navbar-horizontal';
188 break;
189 case self::NAVBAR_VERTICAL:
190 $classes[] = 'phui-list-navbar';
191 $classes[] = 'phui-list-navbar-vertical';
192 break;
193 default:
194 $classes[] = $this->type;
195 break;
196 }
197 }
198
199 return array(
200 'class' => implode(' ', $classes),
201 );
202 }
203
204 protected function getTagContent() {
205 return $this->items;
206 }
207}