Pārlūkot izejas kodu

Ajout de ux-turbo

Sangfroid 1 dienu atpakaļ
vecāks
revīzija
b00c10b399

+ 13 - 0
assets/controllers.json

@@ -6,6 +6,19 @@
                 "fetch": "eager"
             }
         },
+        "@symfony/ux-turbo": {
+            "turbo-core": {
+                "enabled": true,
+                "fetch": "eager",
+                "autoimport": {
+                    "@symfony/ux-turbo/dist/mercure_stream_source_element.js": true
+                }
+            },
+            "mercure-turbo-stream": {
+                "enabled": false,
+                "fetch": "eager"
+            }
+        },
         "@symfony/ux-vue": {
             "vue": {
                 "enabled": true,

+ 23 - 7
assets/js/addCollectionVanilla.js

@@ -1,26 +1,39 @@
 import { autocomplete } from './autocomplete.js'
 
-(() => {
+const initCollectionForms = () => {
     document
         .querySelectorAll('ul.collection-form li')
         .forEach(item => {
-            item.querySelector('input').readOnly = true
-            addDeleteLink(item)
+            // Élément déjà initialisé (page restaurée depuis le cache Turbo)
+            if (item.dataset.collectionFormInit) {
+                return
+            }
+            item.dataset.collectionFormInit = 'true'
+
+            const input = item.querySelector('input')
+            if (input) {
+                input.readOnly = true
+                addDeleteLink(item)
+            }
         });
 
     document
     .querySelectorAll('.add_item_link')
     .forEach(btn => {
+        if (btn.dataset.collectionFormInit) {
+            return
+        }
+        btn.dataset.collectionFormInit = 'true'
         btn.addEventListener("click", addFormToCollection)
     });
 
     function addFormToCollection(e) {
         const collectionHolder = document.querySelector('#' + e.currentTarget.dataset.collectionHolderId);
         const path = e.currentTarget.dataset.path ?? '';
-    
+
         const item = document.createElement('li');
         item.className = collectionHolder.dataset.classLi
-    
+
         item.innerHTML = collectionHolder
         .dataset
         .prototype
@@ -36,7 +49,7 @@ import { autocomplete } from './autocomplete.js'
         collectionHolder.appendChild(item);
 
         addDeleteLink(item);
-    
+
         collectionHolder.dataset.index++;
     };
 
@@ -56,4 +69,7 @@ import { autocomplete } from './autocomplete.js'
 
         input.after(deleteLink);
     };
-})();
+};
+
+initCollectionForms();
+document.addEventListener('turbo:load', initCollectionForms);

+ 3 - 0
assets/js/autocomplete.js

@@ -37,6 +37,9 @@ export function autocomplete (inputField, path) {
     });*/
 
     document.addEventListener('click', () => {
+        if (!results.isConnected) {
+            return; // le <ul> a été détaché du DOM (navigation Turbo)
+        }
         results.hidden = true;
     })
 

+ 19 - 8
assets/js/colorModeBootstrap.js

@@ -63,18 +63,26 @@
     }
   }
 
-  window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
-    const storedTheme = getStoredTheme()
-    if (storedTheme !== 'light' && storedTheme !== 'dark') {
-      setTheme(getPreferredTheme())
-    }
-  })
+  if (!window.__colorModeMediaBound) {
+    window.__colorModeMediaBound = true;
+    window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
+      const storedTheme = getStoredTheme()
+      if (storedTheme !== 'light' && storedTheme !== 'dark') {
+        setTheme(getPreferredTheme())
+      }
+    })
+  }
 
-  window.addEventListener('DOMContentLoaded', () => {
+  const initColorMode = () => {
+    setTheme(getPreferredTheme())
     showActiveTheme(getPreferredTheme())
 
     document.querySelectorAll('[data-bs-theme-value]')
       .forEach(toggle => {
+        if (toggle.dataset.colorModeBound) {
+          return
+        }
+        toggle.dataset.colorModeBound = 'true'
         toggle.addEventListener('click', () => {
           const theme = toggle.getAttribute('data-bs-theme-value')
           setStoredTheme(theme)
@@ -85,5 +93,8 @@
           })
         })
       })
-  })
+  }
+
+  initColorMode()
+  document.addEventListener('turbo:load', initColorMode)
 })()

+ 1 - 0
composer.json

@@ -36,6 +36,7 @@
         "symfony/translation": "7.4.*",
         "symfony/twig-bundle": "7.4.*",
         "symfony/ux-chartjs": "^2.20",
+        "symfony/ux-turbo": "^3.7",
         "symfony/ux-vue": "^3.7",
         "symfony/validator": "7.4.*",
         "symfony/web-link": "7.4.*",

+ 106 - 1
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "e469414552682464fe990c80ed3b0d23",
+    "content-hash": "796c99c844a615aaa3961e2964c01bf5",
     "packages": [
         {
             "name": "beberlei/doctrineextensions",
@@ -8137,6 +8137,111 @@
             "time": "2026-05-06T04:31:36+00:00",
             "published-time": "2026-05-29T07:00:02+00:00"
         },
+        {
+            "name": "symfony/ux-turbo",
+            "version": "v3.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/ux-turbo.git",
+                "reference": "0a21824980e0e6d79da464d7ad3b9d3beb9a46af"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/0a21824980e0e6d79da464d7ad3b9d3beb9a46af",
+                "reference": "0a21824980e0e6d79da464d7ad3b9d3beb9a46af",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.4",
+                "psr/log": "^1|^2|^3",
+                "symfony/stimulus-bundle": "^2.9.1|^3.0"
+            },
+            "conflict": {
+                "symfony/flex": "<1.13",
+                "symfony/mercure": ">=0.7.0 <0.7.2"
+            },
+            "require-dev": {
+                "doctrine/doctrine-bundle": "^2.14|^3.0|^4.0",
+                "doctrine/orm": "^2.8|^3.0",
+                "phpstan/phpstan": "^2.1.17",
+                "phpunit/phpunit": "^11.1|^12.0",
+                "symfony/asset-mapper": "^7.4|^8.0",
+                "symfony/browser-kit": "^7.4|^8.0",
+                "symfony/debug-bundle": "^7.4|^8.0",
+                "symfony/expression-language": "^7.4|^8.0",
+                "symfony/form": "^7.4|^8.0",
+                "symfony/framework-bundle": "^7.4|^8.0",
+                "symfony/mercure-bundle": "^0.3.7|^0.4.1|^0.5|^0.6",
+                "symfony/messenger": "^7.4|^8.0",
+                "symfony/property-access": "^7.4|^8.0",
+                "symfony/security-core": "^7.4|^8.0",
+                "symfony/stopwatch": "^7.4|^8.0",
+                "symfony/twig-bundle": "^7.4|^8.0",
+                "symfony/ux-twig-component": "^2.21|^3.0",
+                "symfony/web-profiler-bundle": "^7.4|^8.0",
+                "twig/html-extra": "^3.29|^4.0",
+                "twig/twig": "^3.24|^4.0"
+            },
+            "type": "symfony-bundle",
+            "extra": {
+                "thanks": {
+                    "url": "https://github.com/symfony/ux",
+                    "name": "symfony/ux"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\UX\\Turbo\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Kévin Dunglas",
+                    "email": "kevin@dunglas.fr"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Hotwire Turbo integration for Symfony",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "hotwire",
+                "javascript",
+                "mercure",
+                "symfony-ux",
+                "turbo",
+                "turbo-stream"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/ux-turbo/tree/v3.7.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/nicolas-grekas",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2026-11-04T08:53:23+00:00",
+            "published-time": "2026-11-04T08:54:43+00:00"
+        },
         {
             "name": "symfony/ux-vue",
             "version": "v3.7.0",

+ 1 - 0
config/bundles.php

@@ -15,4 +15,5 @@ return [
     Symfony\UX\Chartjs\ChartjsBundle::class => ['all' => true],
     Symfony\Reprise\RepriseBundle::class => ['all' => true],
     Symfony\UX\Vue\VueBundle::class => ['all' => true],
+    Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
 ];

+ 4 - 0
config/packages/ux_turbo.yaml

@@ -0,0 +1,4 @@
+# Accept the CSRF token sent in a header, as Turbo form submissions do
+framework:
+    csrf_protection:
+        check_header: true

+ 17 - 0
config/reference.php

@@ -1552,6 +1552,19 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
  *     controllers_path?: scalar|Param|null, // The path to the directory where Vue controller components are stored - relevant only when using symfony/asset-mapper. // Default: "%kernel.project_dir%/assets/vue/controllers"
  *     name_glob?: list<scalar|Param|null>,
  * }
+ * @psalm-type TurboConfig = array{
+ *     broadcast?: bool|array{
+ *         enabled?: bool|Param, // Default: true
+ *         entity_template_prefixes?: list<scalar|Param|null>,
+ *         doctrine_orm?: bool|array{ // Enable the Doctrine ORM integration
+ *             enabled?: bool|Param, // Default: true
+ *         },
+ *     },
+ *     default_transport?: scalar|Param|null, // Default: "default"
+ *     mercure?: array{
+ *         ignore_cookie_domain_errors?: bool|Param, // Render nothing and log a warning, instead of failing, when the authorization cookie of a private stream source cannot be created because the page is served on another domain than the hub // Default: false
+ *     },
+ * }
  * @psalm-type ConfigType = array{
  *     imports?: ImportsConfig,
  *     parameters?: ParametersConfig,
@@ -1566,6 +1579,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
  *     stimulus?: StimulusConfig,
  *     reprise?: RepriseConfig,
  *     vue?: VueConfig,
+ *     turbo?: TurboConfig,
  *     "when@dev"?: array{
  *         imports?: ImportsConfig,
  *         parameters?: ParametersConfig,
@@ -1583,6 +1597,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
  *         stimulus?: StimulusConfig,
  *         reprise?: RepriseConfig,
  *         vue?: VueConfig,
+ *         turbo?: TurboConfig,
  *     },
  *     "when@prod"?: array{
  *         imports?: ImportsConfig,
@@ -1598,6 +1613,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
  *         stimulus?: StimulusConfig,
  *         reprise?: RepriseConfig,
  *         vue?: VueConfig,
+ *         turbo?: TurboConfig,
  *     },
  *     "when@test"?: array{
  *         imports?: ImportsConfig,
@@ -1614,6 +1630,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
  *         stimulus?: StimulusConfig,
  *         reprise?: RepriseConfig,
  *         vue?: VueConfig,
+ *         turbo?: TurboConfig,
  *     },
  *     ...<string, ExtensionType|array{ // extra keys must follow the when@%env% pattern or match an extension alias
  *         imports?: ImportsConfig,

+ 30 - 0
package-lock.json

@@ -7,10 +7,12 @@
       "devDependencies": {
         "@fortawesome/fontawesome-free": "^7.3.1",
         "@hotwired/stimulus": "^3.0.0",
+        "@hotwired/turbo": "^7.1.0 || ^8.0",
         "@popperjs/core": "^2.11.8",
         "@symfony/reprise": "^1.4.0",
         "@symfony/stimulus-bridge": "^3.2.0 || ^4.0.0",
         "@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/assets",
+        "@symfony/ux-turbo": "file:vendor/symfony/ux-turbo/assets",
         "@symfony/ux-vue": "file:vendor/symfony/ux-vue/assets",
         "@vitejs/plugin-vue": "^6.0.10",
         "bootstrap": "^5.3.8",
@@ -266,6 +268,16 @@
         "@hotwired/stimulus": ">= 3.0"
       }
     },
+    "node_modules/@hotwired/turbo": {
+      "version": "8.0.23",
+      "resolved": "https://registry.npmjs.org/@hotwired/turbo/-/turbo-8.0.23.tgz",
+      "integrity": "sha512-GZ7cijxEZ6Ig71u7rD6LHaRv/wcE/hNsc+nEfiWOkLNqUgLOwo5MNGWOy5ZV9ZUDSiQx1no7YxjTNnT4O6//cQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 18"
+      }
+    },
     "node_modules/@jridgewell/gen-mapping": {
       "version": "0.3.13",
       "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -996,6 +1008,10 @@
       "resolved": "vendor/symfony/ux-chartjs/assets",
       "link": true
     },
+    "node_modules/@symfony/ux-turbo": {
+      "resolved": "vendor/symfony/ux-turbo/assets",
+      "link": true
+    },
     "node_modules/@symfony/ux-vue": {
       "resolved": "vendor/symfony/ux-vue/assets",
       "link": true
@@ -3570,6 +3586,20 @@
         "chart.js": "^3.4.1 || ^4.0"
       }
     },
+    "vendor/symfony/ux-turbo/assets": {
+      "name": "@symfony/ux-turbo",
+      "version": "3.7.0",
+      "dev": true,
+      "license": "MIT",
+      "devDependencies": {
+        "@hotwired/stimulus": "^3.0.0",
+        "@hotwired/turbo": "^7.1.0 || ^8.0"
+      },
+      "peerDependencies": {
+        "@hotwired/stimulus": "^3.0.0",
+        "@hotwired/turbo": "^7.1.0 || ^8.0"
+      }
+    },
     "vendor/symfony/ux-vue/assets": {
       "name": "@symfony/ux-vue",
       "version": "3.7.0",

+ 2 - 0
package.json

@@ -2,10 +2,12 @@
   "devDependencies": {
     "@fortawesome/fontawesome-free": "^7.3.1",
     "@hotwired/stimulus": "^3.0.0",
+    "@hotwired/turbo": "^7.1.0 || ^8.0",
     "@popperjs/core": "^2.11.8",
     "@symfony/reprise": "^1.4.0",
     "@symfony/stimulus-bridge": "^3.2.0 || ^4.0.0",
     "@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/assets",
+    "@symfony/ux-turbo": "file:vendor/symfony/ux-turbo/assets",
     "@symfony/ux-vue": "file:vendor/symfony/ux-vue/assets",
     "@vitejs/plugin-vue": "^6.0.10",
     "bootstrap": "^5.3.8",

+ 12 - 0
symfony.lock

@@ -547,6 +547,18 @@
     "symfony/ux-chartjs": {
         "version": "v2.20.0"
     },
+    "symfony/ux-turbo": {
+        "version": "3.7",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "main",
+            "version": "2.20",
+            "ref": "eaff62fc37864d355804c778309a226ae539831f"
+        },
+        "files": [
+            "config/packages/ux_turbo.yaml"
+        ]
+    },
     "symfony/ux-vue": {
         "version": "3.7",
         "recipe": {