'actions'); * $form['actions']['submit'] = array( * '#type' => 'submit', * '#value' => $this->t('Save'), * ); * @endcode * * @RenderElement("actions") */ class Actions extends Container { /** * {@inheritdoc} */ public function getInfo() { $class = get_class($this); return [ '#process' => [ // @todo Move this to #pre_render. [$class, 'preRenderActionsDropbutton'], [$class, 'processActions'], [$class, 'processContainer'], ], '#weight' => 100, '#theme_wrappers' => ['container'], ]; } /** * Processes a form actions container element. * * @param array $element * An associative array containing the properties and children of the * form actions container. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. * @param array $complete_form * The complete form structure. * * @return array * The processed element. */ public static function processActions(&$element, FormStateInterface $form_state, &$complete_form) { $element['#attributes']['class'][] = 'form-actions'; return $element; } /** * #pre_render callback for #type 'actions'. * * This callback iterates over all child elements of the #type 'actions' * container to look for elements with a #dropbutton property, so as to group * those elements into dropbuttons. As such, it works similar to #group, but is * specialized for dropbuttons. * * The value of #dropbutton denotes the dropbutton to group the child element * into. For example, two different values of 'foo' and 'bar' on child elements * would generate two separate dropbuttons, which each contain the corresponding * buttons. * * @param array $element * The #type 'actions' element to process. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. * @param array $complete_form * The complete form structure. * * @return array * The processed #type 'actions' element, including individual buttons grouped * into new #type 'dropbutton' elements. */ public static function preRenderActionsDropbutton(&$element, FormStateInterface $form_state, &$complete_form) { $dropbuttons = []; foreach (Element::children($element, TRUE) as $key) { if (isset($element[$key]['#dropbutton'])) { $dropbutton = $element[$key]['#dropbutton']; // If there is no dropbutton for this button group yet, create one. if (!isset($dropbuttons[$dropbutton])) { $dropbuttons[$dropbutton] = [ '#type' => 'dropbutton', ]; } // Add this button to the corresponding dropbutton. // @todo Change #type 'dropbutton' to be based on item-list.html.twig // instead of links.html.twig to avoid this preemptive rendering. $button = \Drupal::service('renderer')->renderPlain($element[$key]); $dropbuttons[$dropbutton]['#links'][$key] = [ 'title' => $button, ]; // Merge metadata like drupalSettings. BubbleableMetadata::createFromRenderArray($dropbuttons[$dropbutton]) ->merge(BubbleableMetadata::createFromRenderArray($element[$key])) ->applyTo($dropbuttons[$dropbutton]); } } // @todo For now, all dropbuttons appear first. Consider to invent a more // fancy sorting/injection algorithm here. return $dropbuttons + $element; } }