cf890cf22b128ecb367671b9ca4a4f8857002deb
[yaffs-website] / web / themes / contrib / bootstrap / src / Plugin / ProcessManager.php
1 <?php
2 /**
3  * @file
4  * Contains \Drupal\bootstrap\Plugin\ProcessManager.
5  */
6
7 namespace Drupal\bootstrap\Plugin;
8
9 use Drupal\bootstrap\Bootstrap;
10 use Drupal\bootstrap\Theme;
11 use Drupal\bootstrap\Utility\Element;
12 use Drupal\Component\Utility\NestedArray;
13 use Drupal\Core\Form\FormStateInterface;
14
15 /**
16  * Manages discovery and instantiation of Bootstrap form process callbacks.
17  *
18  * @ingroup plugins_process
19  */
20 class ProcessManager extends PluginManager {
21
22   /**
23    * Constructs a new \Drupal\bootstrap\Plugin\ProcessManager object.
24    *
25    * @param \Drupal\bootstrap\Theme $theme
26    *   The theme to use for discovery.
27    */
28   public function __construct(Theme $theme) {
29     parent::__construct($theme, 'Plugin/Process', 'Drupal\bootstrap\Plugin\Process\ProcessInterface', 'Drupal\bootstrap\Annotation\BootstrapProcess');
30     $this->setCacheBackend(\Drupal::cache('discovery'), 'theme:' . $theme->getName() . ':process', $this->getCacheTags());
31   }
32
33   /**
34    * Global #process callback for form elements.
35    *
36    * @param array $element
37    *   The element render array.
38    * @param \Drupal\Core\Form\FormStateInterface $form_state
39    *   The current state of the form.
40    * @param array $complete_form
41    *   The complete form structure.
42    *
43    * @return array
44    *   The altered element array.
45    *
46    * @see \Drupal\bootstrap\Plugin\Alter\ElementInfo::alter
47    */
48   public static function process(array $element, FormStateInterface $form_state, array &$complete_form) {
49     if (!empty($element['#bootstrap_ignore_process'])) {
50       return $element;
51     }
52
53     static $theme;
54     if (!isset($theme)) {
55       $theme = Bootstrap::getTheme();
56     }
57
58     $e = Element::create($element, $form_state);
59
60     // Process AJAX.
61     if (($e->getProperty('ajax') && !$e->isButton()) || $e->getProperty('autocomplete_route_name')) {
62       static::processAjax($e, $form_state, $complete_form);
63     }
64
65     // Add "form-inline" class.
66     if ($e->hasClass('container-inline')) {
67       $e->replaceClass('container-inline', 'form-inline');
68     }
69     if ($e->isType(['color', 'date', 'number', 'range', 'tel', 'weight'])) {
70       $e->addClass('form-inline', 'wrapper_attributes');
71     }
72
73     // Process input groups.
74     if ($e->getProperty('input') && ($e->getProperty('input_group') || $e->getProperty('input_group_button'))) {
75       static::processInputGroups($e, $form_state, $complete_form);
76     }
77
78     return $element;
79   }
80
81   /**
82    * Processes elements with AJAX properties.
83    *
84    * @param \Drupal\bootstrap\Utility\Element $element
85    *   The element object.
86    * @param \Drupal\Core\Form\FormStateInterface $form_state
87    *   The current state of the form.
88    * @param array $complete_form
89    *   The complete form structure.
90    */
91   public static function processAjax(Element $element, FormStateInterface $form_state, array &$complete_form) {
92     $ajax = $element->getProperty('ajax');
93
94     // Show throbber AJAX requests in an input button group.
95     if (!$element->isType('hidden') && (!isset($ajax['progress']['type']) || $ajax['progress']['type'] === 'throbber')) {
96       // Use an icon for autocomplete "throbber".
97       $icon = Bootstrap::glyphicon('refresh');
98       $element->appendProperty('field_suffix', Element::create($icon)->addClass(['ajax-progress', 'ajax-progress-throbber']));
99       $element->setProperty('input_group', TRUE);
100     }
101   }
102
103   /**
104    * Processes elements that have input groups.
105    *
106    * @param \Drupal\bootstrap\Utility\Element $element
107    *   The element object.
108    * @param \Drupal\Core\Form\FormStateInterface $form_state
109    *   The current state of the form.
110    * @param array $complete_form
111    *   The complete form structure.
112    */
113   protected static function processInputGroups(Element $element, FormStateInterface $form_state, array &$complete_form) {
114     // Automatically inject the nearest button found after this element if
115     // #input_group_button exists.
116     if ($element->getProperty('input_group_button')) {
117       // Obtain the parent array to limit search.
118       $array_parents = $element->getProperty('array_parents', []);
119
120       // Remove the current element from the array.
121       array_pop($array_parents);
122
123       // Retrieve the parent element.
124       $parent = Element::create(NestedArray::getValue($complete_form, $array_parents), $form_state);
125
126       // Find the closest button.
127       if ($button = self::findButton($parent)) {
128         // Since this button is technically being "moved", it needs to be
129         // rendered now, so it doesn't get printed twice (in the original spot).
130         $element->appendProperty('field_suffix', $button->setIcon()->render());
131       }
132     }
133
134     $input_group_attributes = ['class' => ['input-group-' . ($element->getProperty('input_group_button') ? 'btn' : 'addon')]];
135     if ($prefix = $element->getProperty('field_prefix')) {
136       $element->setProperty('field_prefix', [
137         '#type' => 'html_tag',
138         '#tag' => 'span',
139         '#attributes' => $input_group_attributes,
140         '#value' => Element::create($prefix)->renderPlain(),
141         '#weight' => -1,
142       ]);
143     }
144     if ($suffix = $element->getProperty('field_suffix')) {
145       $element->setProperty('field_suffix', [
146         '#type' => 'html_tag',
147         '#tag' => 'span',
148         '#attributes' => $input_group_attributes,
149         '#value' => Element::create($suffix)->renderPlain(),
150         '#weight' => 1,
151       ]);
152     }
153   }
154
155   /**
156    * Traverses an element to find the closest button.
157    *
158    * @param \Drupal\bootstrap\Utility\Element $element
159    *   The element to iterate over.
160    *
161    * @return \Drupal\bootstrap\Utility\Element|FALSE
162    *   The first button element or FALSE if no button could be found.
163    */
164   protected static function &findButton(Element $element) {
165     $button = FALSE;
166     foreach ($element->children() as $child) {
167       if ($child->isButton()) {
168         $button = $child;
169       }
170       if ($result = &self::findButton($child)) {
171         $button = $result;
172       }
173     }
174     return $button;
175   }
176
177 }