@@ -90,6 +90,7 @@ class="table data-table striped row-hover cell-border"
9090 data-rownum =" false"
9191 data-check =" false"
9292 data-check-style =" 1"
93+ data-search =" true"
9394 >
9495 <thead >
9596 <tr >
@@ -180,92 +181,142 @@ class="table data-table striped row-hover cell-border"
180181 </tbody >
181182</table >
182183</div >
183- <script >
184- document .addEventListener (" DOMContentLoaded" , function () {
185- // Récupère le paramètre search
186- let params = new URLSearchParams (window .location .search );
187- const searchValue = params .get (' search' );
188- if (searchValue) {
189- // get serach filter
190- let searchInput = document .querySelector (' .table-search-block input' );
191- searchInput .value = searchValue;
192- // Trouve la table et applique la recherche
193- let tableElement = document .querySelector (' .data-table' );
194- let table = Metro .getPlugin (tableElement, " table" );
195- if (table)
196- table .search (searchValue);
184+ <script >
185+ document .addEventListener (" DOMContentLoaded" , () => {
186+ const $ = (sel , root = document ) => root .querySelector (sel);
187+ const getParam = (k ) => new URLSearchParams (location .search ).get (k) ?? ' ' ;
197188
198- }
199- // Auto submits
200- var select = document .getElementById (' domain' );
201- select .addEventListener (' change' , function (){
202- let url = new URL (window .location .href );
203- url .pathname = ' /bob/index' ;
204- url .searchParams .set (' domain' , this .value );
205- let searchInput = document .querySelector (' .table-search-block input' );
206- url .searchParams .set (' search' , searchInput .value );
207- window .location = url .toString ();
208- }, false );
189+ // --- Attendre que Metro ait fini d'initialiser ses selects
190+ let ready = false ;
191+ window .addEventListener (' load' , () => {
192+ // 2 rAF pour laisser Metro construire le DOM
193+ requestAnimationFrame (() => requestAnimationFrame (() => { ready = true ; }));
194+ });
209195
210- var select = document .getElementById (' scope' );
211- select .addEventListener (' change' , function (){
212- let url = new URL (window .location .href );
213- url .pathname = ' /bob/index' ;
214- url .searchParams .set (' scope' , this .value );
215- let searchInput = document .querySelector (' .table-search-block input' );
216- url .searchParams .set (' search' , searchInput .value );
217- window .location = url .toString ();
218- }, false );
196+ // --- Snapshot de tous les filtres (UI -> fallback URL)
197+ function snapshotFilters () {
198+ const searchInput = $ (' .table-search-block input' );
199+ return {
200+ domain : document .getElementById (' domain' )? .value ?? getParam (' domain' ),
201+ clause : (document .getElementById (' clause' )? .value ?? getParam (' clause' )).trim (),
202+ scope : (document .getElementById (' scope' )? .value ?? getParam (' scope' )).trim (),
203+ period : document .getElementById (' cur_period' )? .value ?? getParam (' period' ),
204+ status : (document .getElementById (' status0' )? .checked ? ' 0' :
205+ document .getElementById (' status1' )? .checked ? ' 1' :
206+ document .getElementById (' status2' )? .checked ? ' 2' : getParam (' status' )),
207+ search : (searchInput && searchInput .value !== ' ' ) ? searchInput .value : getParam (' search' )
208+ };
209+ }
219210
220- var select = document .getElementById (' clause' );
221- select .addEventListener (' change' , function (){
222- let url = new URL (window .location .href );
223- url .pathname = ' /bob/index' ;
224- url .searchParams .set (' clause' , this .value );
225- let searchInput = document .querySelector (' .table-search-block input' );
226- url .searchParams .set (' search' , searchInput .value );
227- window .location = url .toString ();
228- }, false );
211+ // --- Navigation: toujours poster TOUTES les valeurs
212+ function navigateWithAll (patch = {}) {
213+ const cur = new URL (location .href );
214+ const next = new URL (location .href );
215+ next .pathname = ' /bob/index' ;
229216
230- select = document .getElementById (' cur_period' );
231- select .addEventListener (' change' , function (){
232- let url = new URL (window .location .href );
233- url .pathname = ' /bob/index' ;
234- url .searchParams .set (' period' , this .value );
235- let searchInput = document .querySelector (' .table-search-block input' );
236- url .searchParams .set (' search' , searchInput .value );
237- window .location = url .toString ();
238- }, false );
217+ const all = { ... snapshotFilters (), ... patch };
218+ for (const [k ,v ] of Object .entries (all)) {
219+ if (v == null || String (v) === ' ' ) next .searchParams .delete (k);
220+ else next .searchParams .set (k, String (v));
221+ }
222+ if (next .toString () !== location .href ) location .assign (next .toString ());
223+ }
239224
240- select = document .getElementById (' status0' );
241- select .addEventListener (' change' , function () {
242- let url = new URL (window .location .href );
243- url .pathname = ' /bob/index' ;
244- url .searchParams .set (' status' , 0 );
245- let searchInput = document .querySelector (' .table-search-block input' );
246- url .searchParams .set (' search' , searchInput .value );
247- window .location = url .toString ();
248- }, false );
225+ // ==================================================================
249226
250- select = document .getElementById (' status1' );
251- select .addEventListener (' change' , function (){
252- let url = new URL (window .location .href );
253- url .pathname = ' /bob/index' ;
254- url .searchParams .set (' status' , 1 );
255- let searchInput = document .querySelector (' .table-search-block input' );
256- url .searchParams .set (' search' , searchInput .value );
257- window .location = url .toString ();
258- }, false );
227+ // --- 1) assure-toi que la table génère bien la barre de recherche
228+ // (dans ton HTML de la table) :
229+ // data-search="true"
259230
260- select = document .getElementById (' status2' );
261- select .addEventListener (' change' , function (){
262- let url = new URL (window .location .href );
263- url .pathname = ' /bob/index' ;
264- url .searchParams .set (' status' , 2 );
265- let searchInput = document .querySelector (' .table-search-block input' );
266- url .searchParams .set (' search' , searchInput .value );
267- window .location = url .toString ();
268- }, false );
269- }, false );
270- </script >
231+ // --- 2) lit la valeur dans l'URL
232+ const params = new URLSearchParams (location .search );
233+ const searchValue = params .get (' search' ) || ' ' ;
234+
235+ // --- 3) applique à l'API table (filtrage effectif)
236+ const applyToAPI = (val ) => {
237+ const tableEl = document .getElementById (' controls' );
238+ const api = tableEl ? Metro .getPlugin (tableEl, ' table' ) : null ;
239+ if (api && typeof api .search === ' function' ) {
240+ api .search (val);
241+ return true ;
242+ }
243+ return false ;
244+ };
245+
246+ // --- 4) pose la valeur dans l'input quand il existe (et quand il est recréé)
247+ function setSearchInputWhenReady (val , timeoutMs = 3000 ) {
248+ if (! val) return ;
249+
250+ const trySet = () => {
251+ const input = $ (' .table-search-block input' );
252+ if (! input) return false ;
253+ if (input .value !== val) {
254+ input .value = val;
255+ input .dispatchEvent (new Event (' input' , { bubbles: true }));
256+ input .dispatchEvent (new Event (' change' , { bubbles: true }));
257+ }
258+ return true ;
259+ };
260+
261+ if (trySet ()) return ;
262+
263+ // a) quelques frames
264+ let tries = 0 , maxTries = 20 ;
265+ const raf = () => {
266+ if (trySet ()) return ;
267+ if (++ tries >= maxTries) return ;
268+ requestAnimationFrame (raf);
269+ };
270+ requestAnimationFrame (raf);
271+
272+ // b) garde-fou si l’input apparaît plus tard (reconstruction Metro)
273+ const mo = new MutationObserver (() => {
274+ if (trySet ()) mo .disconnect ();
275+ });
276+ mo .observe (document .body , { childList: true , subtree: true });
277+ setTimeout (() => mo .disconnect (), timeoutMs);
278+ }
279+
280+ // --- 5) exécution au chargement
281+ if (searchValue) {
282+ // filtre la table côté API (immédiat ou dès que prêt)
283+ if (! applyToAPI (searchValue)) {
284+ requestAnimationFrame (() => applyToAPI (searchValue));
285+ }
286+ // force l’affichage dans l’input dès qu’il existe
287+ setSearchInputWhenReady (searchValue);
288+ }
289+
290+
291+ // ==================================================================
292+ // --- Bind: ne PAS utiliser e.isTrusted (Metro déclenche un change programmatique)
293+ const bindChange = (id , key , coerce = v => v ) => {
294+ const el = document .getElementById (id);
295+ if (! el) return ;
296+ el .addEventListener (' change' , () => {
297+ if (! ready) return ; // on ignore les changements pendant l'init
298+ const newVal = coerce (el .value );
299+ navigateWithAll ({ [key]: newVal });
300+ }, false );
301+ };
302+
303+ bindChange (' domain' , ' domain' , v => String (v));
304+ bindChange (' clause' , ' clause' , v => String (v).trim ());
305+ bindChange (' scope' , ' scope' , v => String (v).trim ());
306+ bindChange (' cur_period' , ' period' , v => String (v));
307+
308+ // Radios status (OK de garder le flag ready)
309+ const bindStatus = (id , value ) => {
310+ const el = document .getElementById (id);
311+ if (! el) return ;
312+ el .addEventListener (' change' , () => {
313+ if (! ready) return ;
314+ navigateWithAll ({ status: String (value) });
315+ }, false );
316+ };
317+ bindStatus (' status0' , 0 );
318+ bindStatus (' status1' , 1 );
319+ bindStatus (' status2' , 2 );
320+ }, false );
321+ < / script>
271322@endsection
0 commit comments