Actions.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. <?php
  2. namespace Drupal\Core\Render\Element;
  3. use Drupal\Core\Form\FormStateInterface;
  4. use Drupal\Core\Render\BubbleableMetadata;
  5. use Drupal\Core\Render\Element;
  6. /**
  7. * Provides a wrapper element to group one or more buttons in a form.
  8. *
  9. * Use of a single Actions element with an array key of 'actions' to group the
  10. * primary submit buttons on a form helps to ensure proper styling in themes,
  11. * and enables other modules to properly alter a form's actions.
  12. *
  13. * Usage example:
  14. * @code
  15. * $form['actions'] = array('#type' => 'actions');
  16. * $form['actions']['submit'] = array(
  17. * '#type' => 'submit',
  18. * '#value' => $this->t('Save'),
  19. * );
  20. * @endcode
  21. *
  22. * @RenderElement("actions")
  23. */
  24. class Actions extends Container {
  25. /**
  26. * {@inheritdoc}
  27. */
  28. public function getInfo() {
  29. $class = get_class($this);
  30. return [
  31. '#process' => [
  32. // @todo Move this to #pre_render.
  33. [$class, 'preRenderActionsDropbutton'],
  34. [$class, 'processActions'],
  35. [$class, 'processContainer'],
  36. ],
  37. '#weight' => 100,
  38. '#theme_wrappers' => ['container'],
  39. ];
  40. }
  41. /**
  42. * Processes a form actions container element.
  43. *
  44. * @param array $element
  45. * An associative array containing the properties and children of the
  46. * form actions container.
  47. * @param \Drupal\Core\Form\FormStateInterface $form_state
  48. * The current state of the form.
  49. * @param array $complete_form
  50. * The complete form structure.
  51. *
  52. * @return array
  53. * The processed element.
  54. */
  55. public static function processActions(&$element, FormStateInterface $form_state, &$complete_form) {
  56. $element['#attributes']['class'][] = 'form-actions';
  57. return $element;
  58. }
  59. /**
  60. * #pre_render callback for #type 'actions'.
  61. *
  62. * This callback iterates over all child elements of the #type 'actions'
  63. * container to look for elements with a #dropbutton property, so as to group
  64. * those elements into dropbuttons. As such, it works similar to #group, but is
  65. * specialized for dropbuttons.
  66. *
  67. * The value of #dropbutton denotes the dropbutton to group the child element
  68. * into. For example, two different values of 'foo' and 'bar' on child elements
  69. * would generate two separate dropbuttons, which each contain the corresponding
  70. * buttons.
  71. *
  72. * @param array $element
  73. * The #type 'actions' element to process.
  74. * @param \Drupal\Core\Form\FormStateInterface $form_state
  75. * The current state of the form.
  76. * @param array $complete_form
  77. * The complete form structure.
  78. *
  79. * @return array
  80. * The processed #type 'actions' element, including individual buttons grouped
  81. * into new #type 'dropbutton' elements.
  82. */
  83. public static function preRenderActionsDropbutton(&$element, FormStateInterface $form_state, &$complete_form) {
  84. $dropbuttons = [];
  85. foreach (Element::children($element, TRUE) as $key) {
  86. if (isset($element[$key]['#dropbutton'])) {
  87. $dropbutton = $element[$key]['#dropbutton'];
  88. // If there is no dropbutton for this button group yet, create one.
  89. if (!isset($dropbuttons[$dropbutton])) {
  90. $dropbuttons[$dropbutton] = [
  91. '#type' => 'dropbutton',
  92. ];
  93. }
  94. // Add this button to the corresponding dropbutton.
  95. // @todo Change #type 'dropbutton' to be based on item-list.html.twig
  96. // instead of links.html.twig to avoid this preemptive rendering.
  97. $button = \Drupal::service('renderer')->renderPlain($element[$key]);
  98. $dropbuttons[$dropbutton]['#links'][$key] = [
  99. 'title' => $button,
  100. ];
  101. // Merge metadata like drupalSettings.
  102. BubbleableMetadata::createFromRenderArray($dropbuttons[$dropbutton])
  103. ->merge(BubbleableMetadata::createFromRenderArray($element[$key]))
  104. ->applyTo($dropbuttons[$dropbutton]);
  105. }
  106. }
  107. // @todo For now, all dropbuttons appear first. Consider to invent a more
  108. // fancy sorting/injection algorithm here.
  109. return $dropbuttons + $element;
  110. }
  111. }