Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\field_ui\Element;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Component\Utility\Html;
|
Chris@0
|
6 use Drupal\Core\Render\Element;
|
Chris@0
|
7 use Drupal\Core\Render\Element\Table;
|
Chris@0
|
8
|
Chris@0
|
9 /**
|
Chris@0
|
10 * Provides a field_ui table element.
|
Chris@0
|
11 *
|
Chris@0
|
12 * @RenderElement("field_ui_table")
|
Chris@0
|
13 */
|
Chris@0
|
14 class FieldUiTable extends Table {
|
Chris@0
|
15
|
Chris@0
|
16 /**
|
Chris@0
|
17 * {@inheritdoc}
|
Chris@0
|
18 */
|
Chris@0
|
19 public function getInfo() {
|
Chris@0
|
20 $info = parent::getInfo();
|
Chris@0
|
21 $info['#regions'] = ['' => []];
|
Chris@0
|
22 $info['#theme'] = 'field_ui_table';
|
Chris@0
|
23 // Prepend FieldUiTable's prerender callbacks.
|
Chris@0
|
24 array_unshift($info['#pre_render'], [$this, 'tablePreRender'], [$this, 'preRenderRegionRows']);
|
Chris@0
|
25 return $info;
|
Chris@0
|
26 }
|
Chris@0
|
27
|
Chris@0
|
28 /**
|
Chris@0
|
29 * Performs pre-render tasks on field_ui_table elements.
|
Chris@0
|
30 *
|
Chris@0
|
31 * @param array $elements
|
Chris@0
|
32 * A structured array containing two sub-levels of elements. Properties
|
Chris@0
|
33 * used:
|
Chris@0
|
34 * - #tabledrag: The value is a list of $options arrays that are passed to
|
Chris@0
|
35 * drupal_attach_tabledrag(). The HTML ID of the table is added to each
|
Chris@0
|
36 * $options array.
|
Chris@0
|
37 *
|
Chris@0
|
38 * @return array
|
Chris@0
|
39 * The $element with prepared variables ready for field-ui-table.html.twig.
|
Chris@0
|
40 *
|
Chris@16
|
41 * @see \Drupal\Core\Render\RendererInterface::render()
|
Chris@0
|
42 * @see \Drupal\Core\Render\Element\Table::preRenderTable()
|
Chris@0
|
43 */
|
Chris@0
|
44 public static function tablePreRender($elements) {
|
Chris@0
|
45 $js_settings = [];
|
Chris@0
|
46
|
Chris@0
|
47 // For each region, build the tree structure from the weight and parenting
|
Chris@0
|
48 // data contained in the flat form structure, to determine row order and
|
Chris@0
|
49 // indentation.
|
Chris@0
|
50 $regions = $elements['#regions'];
|
Chris@0
|
51 $tree = ['' => ['name' => '', 'children' => []]];
|
Chris@0
|
52 $trees = array_fill_keys(array_keys($regions), $tree);
|
Chris@0
|
53
|
Chris@0
|
54 $parents = [];
|
Chris@0
|
55 $children = Element::children($elements);
|
Chris@0
|
56 $list = array_combine($children, $children);
|
Chris@0
|
57
|
Chris@0
|
58 // Iterate on rows until we can build a known tree path for all of them.
|
Chris@0
|
59 while ($list) {
|
Chris@0
|
60 foreach ($list as $name) {
|
Chris@0
|
61 $row = &$elements[$name];
|
Chris@0
|
62 $parent = $row['parent_wrapper']['parent']['#value'];
|
Chris@0
|
63 // Proceed if parent is known.
|
Chris@0
|
64 if (empty($parent) || isset($parents[$parent])) {
|
Chris@0
|
65 // Grab parent, and remove the row from the next iteration.
|
Chris@0
|
66 $parents[$name] = $parent ? array_merge($parents[$parent], [$parent]) : [];
|
Chris@0
|
67 unset($list[$name]);
|
Chris@0
|
68
|
Chris@0
|
69 // Determine the region for the row.
|
Chris@0
|
70 $region_name = call_user_func_array($row['#region_callback'], [&$row]);
|
Chris@0
|
71
|
Chris@0
|
72 // Add the element in the tree.
|
Chris@0
|
73 $target = &$trees[$region_name][''];
|
Chris@0
|
74 foreach ($parents[$name] as $key) {
|
Chris@0
|
75 $target = &$target['children'][$key];
|
Chris@0
|
76 }
|
Chris@0
|
77 $target['children'][$name] = ['name' => $name, 'weight' => $row['weight']['#value']];
|
Chris@0
|
78
|
Chris@0
|
79 // Add tabledrag indentation to the first row cell.
|
Chris@0
|
80 if ($depth = count($parents[$name])) {
|
Chris@0
|
81 $children = Element::children($row);
|
Chris@0
|
82 $cell = current($children);
|
Chris@0
|
83 $indentation = [
|
Chris@0
|
84 '#theme' => 'indentation',
|
Chris@0
|
85 '#size' => $depth,
|
Chris@0
|
86 '#suffix' => isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : '',
|
Chris@0
|
87 ];
|
Chris@0
|
88 $row[$cell]['#prefix'] = \Drupal::service('renderer')->render($indentation);
|
Chris@0
|
89 }
|
Chris@0
|
90
|
Chris@0
|
91 // Add row id and associate JS settings.
|
Chris@0
|
92 $id = Html::getClass($name);
|
Chris@0
|
93 $row['#attributes']['id'] = $id;
|
Chris@0
|
94 if (isset($row['#js_settings'])) {
|
Chris@0
|
95 $row['#js_settings'] += [
|
Chris@0
|
96 'rowHandler' => $row['#row_type'],
|
Chris@0
|
97 'name' => $name,
|
Chris@0
|
98 'region' => $region_name,
|
Chris@0
|
99 ];
|
Chris@0
|
100 $js_settings[$id] = $row['#js_settings'];
|
Chris@0
|
101 }
|
Chris@0
|
102 }
|
Chris@0
|
103 }
|
Chris@0
|
104 }
|
Chris@0
|
105
|
Chris@0
|
106 // Determine rendering order from the tree structure.
|
Chris@0
|
107 foreach ($regions as $region_name => $region) {
|
Chris@0
|
108 $elements['#regions'][$region_name]['rows_order'] = array_reduce($trees[$region_name], [static::class, 'reduceOrder']);
|
Chris@0
|
109 }
|
Chris@0
|
110
|
Chris@0
|
111 $elements['#attached']['drupalSettings']['fieldUIRowsData'] = $js_settings;
|
Chris@0
|
112
|
Chris@0
|
113 // If the custom #tabledrag is set and there is a HTML ID, add the table's
|
Chris@0
|
114 // HTML ID to the options and attach the behavior.
|
Chris@0
|
115 // @see \Drupal\Core\Render\Element\Table::preRenderTable()
|
Chris@0
|
116 if (!empty($elements['#tabledrag']) && isset($elements['#attributes']['id'])) {
|
Chris@0
|
117 foreach ($elements['#tabledrag'] as $options) {
|
Chris@0
|
118 $options['table_id'] = $elements['#attributes']['id'];
|
Chris@0
|
119 drupal_attach_tabledrag($elements, $options);
|
Chris@0
|
120 }
|
Chris@0
|
121 }
|
Chris@0
|
122
|
Chris@0
|
123 return $elements;
|
Chris@0
|
124 }
|
Chris@0
|
125
|
Chris@0
|
126 /**
|
Chris@0
|
127 * Performs pre-render to move #regions to rows.
|
Chris@0
|
128 *
|
Chris@0
|
129 * @param array $elements
|
Chris@0
|
130 * A structured array containing two sub-levels of elements. Properties
|
Chris@0
|
131 * used:
|
Chris@0
|
132 * - #tabledrag: The value is a list of $options arrays that are passed to
|
Chris@0
|
133 * drupal_attach_tabledrag(). The HTML ID of the table is added to each
|
Chris@0
|
134 * $options array.
|
Chris@0
|
135 *
|
Chris@0
|
136 * @return array
|
Chris@0
|
137 * The $element with prepared variables ready for field-ui-table.html.twig.
|
Chris@0
|
138 */
|
Chris@0
|
139 public static function preRenderRegionRows($elements) {
|
Chris@0
|
140 // Determine the colspan to use for region rows, by checking the number of
|
Chris@0
|
141 // columns in the headers.
|
Chris@0
|
142 $columns_count = 0;
|
Chris@0
|
143 foreach ($elements['#header'] as $header) {
|
Chris@0
|
144 $columns_count += (is_array($header) && isset($header['colspan']) ? $header['colspan'] : 1);
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@0
|
147 $rows = [];
|
Chris@0
|
148 foreach (Element::children($elements) as $key) {
|
Chris@0
|
149 $rows[$key] = $elements[$key];
|
Chris@0
|
150 unset($elements[$key]);
|
Chris@0
|
151 }
|
Chris@0
|
152
|
Chris@0
|
153 // Render rows, region by region.
|
Chris@0
|
154 foreach ($elements['#regions'] as $region_name => $region) {
|
Chris@0
|
155 $region_name_class = Html::getClass($region_name);
|
Chris@0
|
156
|
Chris@0
|
157 // Add region rows.
|
Chris@0
|
158 if (isset($region['title']) && empty($region['invisible'])) {
|
Chris@0
|
159 $elements['#rows'][] = [
|
Chris@0
|
160 'class' => [
|
Chris@0
|
161 'region-title',
|
Chris@17
|
162 'region-' . $region_name_class . '-title',
|
Chris@0
|
163 ],
|
Chris@0
|
164 'no_striping' => TRUE,
|
Chris@0
|
165 'data' => [
|
Chris@0
|
166 ['data' => $region['title'], 'colspan' => $columns_count],
|
Chris@0
|
167 ],
|
Chris@0
|
168 ];
|
Chris@0
|
169 }
|
Chris@0
|
170 if (isset($region['message'])) {
|
Chris@0
|
171 $class = (empty($region['rows_order']) ? 'region-empty' : 'region-populated');
|
Chris@0
|
172 $elements['#rows'][] = [
|
Chris@0
|
173 'class' => [
|
Chris@0
|
174 'region-message',
|
Chris@0
|
175 'region-' . $region_name_class . '-message', $class,
|
Chris@0
|
176 ],
|
Chris@0
|
177 'no_striping' => TRUE,
|
Chris@0
|
178 'data' => [
|
Chris@0
|
179 ['data' => $region['message'], 'colspan' => $columns_count],
|
Chris@0
|
180 ],
|
Chris@0
|
181 ];
|
Chris@0
|
182 }
|
Chris@0
|
183
|
Chris@0
|
184 // Add form rows, in the order determined at pre-render time.
|
Chris@0
|
185 foreach ($region['rows_order'] as $name) {
|
Chris@0
|
186 $element = $rows[$name];
|
Chris@0
|
187
|
Chris@0
|
188 $row = ['data' => []];
|
Chris@0
|
189 if (isset($element['#attributes'])) {
|
Chris@0
|
190 $row += $element['#attributes'];
|
Chris@0
|
191 }
|
Chris@0
|
192
|
Chris@0
|
193 // Render children as table cells.
|
Chris@0
|
194 foreach (Element::children($element) as $cell_key) {
|
Chris@0
|
195 $child = $element[$cell_key];
|
Chris@0
|
196 // Do not render a cell for children of #type 'value'.
|
Chris@0
|
197 if (!(isset($child['#type']) && $child['#type'] == 'value')) {
|
Chris@0
|
198 $cell = ['data' => $child];
|
Chris@0
|
199 if (isset($child['#cell_attributes'])) {
|
Chris@0
|
200 $cell += $child['#cell_attributes'];
|
Chris@0
|
201 }
|
Chris@0
|
202 $row['data'][] = $cell;
|
Chris@0
|
203 }
|
Chris@0
|
204 }
|
Chris@0
|
205 $elements['#rows'][] = $row;
|
Chris@0
|
206 }
|
Chris@0
|
207 }
|
Chris@0
|
208
|
Chris@0
|
209 return $elements;
|
Chris@0
|
210 }
|
Chris@0
|
211
|
Chris@0
|
212 /**
|
Chris@0
|
213 * Determines the rendering order of an array representing a tree.
|
Chris@0
|
214 *
|
Chris@0
|
215 * Callback for array_reduce() within ::tablePreRender().
|
Chris@0
|
216 *
|
Chris@0
|
217 * @param mixed $array
|
Chris@0
|
218 * Holds the return value of the previous iteration; in the case of the
|
Chris@0
|
219 * first iteration it instead holds the value of the initial array.
|
Chris@0
|
220 * @param mixed $a
|
Chris@0
|
221 * Holds the value of the current iteration.
|
Chris@0
|
222 *
|
Chris@0
|
223 * @return array
|
Chris@0
|
224 * Array where rendering order has been determined.
|
Chris@0
|
225 */
|
Chris@0
|
226 public static function reduceOrder($array, $a) {
|
Chris@0
|
227 $array = !$array ? [] : $array;
|
Chris@0
|
228 if ($a['name']) {
|
Chris@0
|
229 $array[] = $a['name'];
|
Chris@0
|
230 }
|
Chris@0
|
231 if (!empty($a['children'])) {
|
Chris@0
|
232 uasort($a['children'], ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
|
Chris@0
|
233 $array = array_merge($array, array_reduce($a['children'], [static::class, 'reduceOrder']));
|
Chris@0
|
234 }
|
Chris@0
|
235
|
Chris@0
|
236 return $array;
|
Chris@0
|
237 }
|
Chris@0
|
238
|
Chris@0
|
239 }
|