Verified Commit 2aa5aede authored by Dave Long's avatar Dave Long
Browse files

feat: #3555916 Update to htmx 4

By: nod_
By: fathershawn
By: catch
By: longwave
By: godotislate
By: smustgrave
By: cosmicdreams
By: latent
parent c82feae0
Loading
Loading
Loading
Loading
Loading
+1877 −4868

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1

File changed.

Preview size limit exceeded, changes collapsed.

+5 −3
Changes for core/core.libraries.yml: 5 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -642,14 +642,16 @@ drupal.vertical-tabs:
    - core/drupal.collapse

htmx:
  aggregate_target:
    js: 'htmx'
  remote: https://github.com/bigskysoftware/htmx
  version: "2.0.4"
  version: "4.0.0"
  license:
    name: Zero-Clause BSD
    url:  https://raw.githubusercontent.com/bigskysoftware/htmx/refs/tags/v2.0.4/LICENSE
    url:  https://raw.githubusercontent.com/bigskysoftware/htmx/refs/tags/v4.0.0/LICENSE
    gpl-compatible: true
  js:
    assets/vendor/htmx/htmx.min.js: { minified: true, preprocess: false }
    assets/vendor/htmx/htmx.min.js: { minified: true }

internal.floating-ui:
  aggregate_target:
+25 −12
Changes for core/lib/Drupal/Core/Form/FormBuilder.php: 25 added lines, 12 removed lines.
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\Exception\BrokenPostRequestException;
use Drupal\Core\Htmx\Htmx;
use Drupal\Core\Htmx\HtmxRequestInfoTrait;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\FormElementBase;
use Drupal\Core\Render\ElementInfoManagerInterface;
@@ -33,6 +34,8 @@
 */
class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormSubmitterInterface, FormCacheInterface, TrustedCallbackInterface {

  use HtmxRequestInfoTrait;

  /**
   * The module handler.
   *
@@ -247,7 +250,7 @@ public function buildForm($form_arg, FormStateInterface &$form_state) {
    // Ensure the form ID is prepared.
    $form_id = $this->getFormId($form_arg, $form_state);

    $request = $this->requestStack->getCurrentRequest();
    $request = $this->getRequest();

    // Inform $form_state about the request method that's building it, so that
    // it can prevent persisting state changes during HTTP methods for which
@@ -326,7 +329,7 @@ public function buildForm($form_arg, FormStateInterface &$form_state) {
    // If this form is an AJAX request or an HTMX request,
    // disable all form redirects.
    $ajax_form_request = $request->query->has(static::AJAX_FORM_REQUEST);
    if ($ajax_form_request || $request->headers->has(static::HTMX_REQUEST)) {
    if ($ajax_form_request || $this->isHtmxRequest()) {
      $form_state->disableRedirect();
    }

@@ -768,7 +771,6 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
      '#attributes' => ['autocomplete' => 'off'],
    ];

    $current_request_headers = $this->requestStack->getCurrentRequest()->headers;
    // Figure out if we need to update the form_build_id value, this is
    // specific to HTMX requests. The corresponding code path in the Ajax
    // framework is in `FormAjaxResponseBuilder::buildResponse`.
@@ -777,18 +779,17 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
    $input = $form_state->getUserInput();
    $old_build_id = $input['form_build_id'] ?? NULL;
    $returned_form_id = $input['form_id'] ?? NULL;
    if ($current_request_headers->has(self::HTMX_REQUEST) && $form_id === $returned_form_id && $old_build_id) {
    if ($this->isHtmxRequest() && $form_id === $returned_form_id && $old_build_id) {
      // Update the build_id by using an oob swap only
      // in the following situation:
      // - Headers `HX-Target` and `HX-Trigger` on the request show this is an
      //   HTMX request.
      // - The target to replace is not a whole form, the build_id will not be
      //   part of the main swap.
      // - This is an HTMX request.
      // - The target to replace is not a whole form, so the build_id will not
      //   be part of the main swap.
      // - The target is a different form from the one that triggered the
      //   call, update the build id of the calling form.
      $hx_target = $current_request_headers->get('hx-target');
      $hx_trigger = $current_request_headers->get('hx-trigger');
      $target_is_form = str_ends_with($hx_target ?? '', '-form');
      $hx_target = $this->getHtmxTarget();
      $hx_trigger = $this->getHtmxSource();
      $target_is_form = str_starts_with($hx_target, 'form');
      if (!$target_is_form || ($target_is_form && $hx_target !== $hx_trigger)) {
        (new Htmx())
          ->swapOob('outerHTML:input[name="form_build_id"][value="' . $old_build_id . '"]')
@@ -1022,7 +1023,7 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state
            // Ignore all submitted values.
            $form_state->setUserInput([]);

            $request = $this->requestStack->getCurrentRequest();
            $request = $this->getRequest();
            // Do not trust any POST data.
            $request->request = new InputBag();
            // Make sure file uploads do not get processed.
@@ -1480,6 +1481,18 @@ protected function currentUser() {
    return $this->currentUser;
  }

  /**
   * Gets the current request.
   *
   * Required by HtmxRequestInfoTrait.
   *
   * @return \Symfony\Component\HttpFoundation\Request
   *   The current request.
   */
  protected function getRequest() {
    return $this->requestStack->getCurrentRequest();
  }

  /**
   * {@inheritdoc}
   */
+4 −0
Changes for core/lib/Drupal/Core/Form/FormBuilderInterface.php: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -24,6 +24,10 @@ interface FormBuilderInterface {

  /**
   * Header name for HTMX requests.
   *
   * @deprecated in drupal:12.0.0 and is removed from drupal:13.0.0. Use
   * HtmxRequestInfoTrait::isHtmxRequest() instead.
   * @see https://www.drupal.org/node/3583674
   */
  const HTMX_REQUEST = 'HX-Request';

Loading