11<nav class =" navbar navbar-expand-lg navbar-filters mb-0 py-0 shadow-none" >
2- {{-- Brand and toggle get grouped for better mobile display --}}
3- <a class =" nav-item d-none d-lg-block my-auto" ><span class =" la la-filter" ></span ></a >
4- <button class =" navbar-toggler ms-3"
5- type =" button"
6- data-toggle =" collapse" {{-- for Bootstrap v4 --}}
7- data-target =" #bp-filters-navbar" {{-- for Bootstrap v4 --}}
8- data-bs-toggle =" collapse" {{-- for Bootstrap v5 --}}
9- data-bs-target =" #bp-filters-navbar" {{-- for Bootstrap v5 --}}
10- aria-controls =" bp-filters-navbar"
11- aria-expanded =" false"
12- aria-label =" {{ trans (' backpack::crud.toggle_filters' ) } }" >
2+ {{-- Brand and toggle get grouped for better mobile display --}}
3+ <a class =" nav-item d-none d-lg-block my-auto" ><span class =" la la-filter" ></span ></a >
4+ <button class =" navbar-toggler ms-3"
5+ type =" button"
6+ data-toggle =" collapse" {{-- for Bootstrap v4 --}}
7+ data-target =" #bp-filters-navbar" {{-- for Bootstrap v4 --}}
8+ data-bs-toggle =" collapse" {{-- for Bootstrap v5 --}}
9+ data-bs-target =" #bp-filters-navbar" {{-- for Bootstrap v5 --}}
10+ aria-controls =" bp-filters-navbar"
11+ aria-expanded =" false"
12+ aria-label =" {{ trans (' backpack::crud.toggle_filters' ) } }" >
1313 <span class =" la la-filter" ></span > {{ trans (' backpack::crud.filters' ) } }
14- </button >
14+ </button >
1515
16- {{-- Collect the nav links, forms, and other content for toggling --}}
17- <div class =" collapse navbar-collapse" id =" bp-filters-navbar" >
16+ {{-- Collect the nav links, forms, and other content for toggling --}}
17+ <div class =" collapse navbar-collapse" id =" bp-filters-navbar" >
1818 <ul class =" nav navbar-nav" >
19- {{-- THE ACTUAL FILTERS --}}
20- @foreach ($crud -> filters () as $filter )
21- @includeFirst ($filter -> getNamespacedViewWithFallbacks () )
22- @endforeach
23- <li class =" nav-item" ><a href =" #" id = " remove_filters_button " class =" nav-link {{ count (Request:: input ()) != 0 ? ' ' : ' invisible' } }" ><i class =" la la-eraser" ></i > {{ trans (' backpack::crud.remove_filters' ) } } </a ></li >
19+ {{-- THE ACTUAL FILTERS --}}
20+ @foreach ($crud -> filters () as $filter )
21+ @includeFirst ($filter -> getNamespacedViewWithFallbacks () )
22+ @endforeach
23+ <li class =" nav-item" ><a href =" #" class =" nav-link remove_filters_button {{ count (Request:: input ()) != 0 ? ' ' : ' invisible' } }" ><i class =" la la-eraser" ></i > {{ trans (' backpack::crud.remove_filters' ) } } </a ></li >
2424 </ul >
25- </div >{{-- /.navbar-collapse --}}
26- </nav >
27-
28- @push (' crud_list_scripts ' )
25+ </div >{{-- /.navbar-collapse --}}
26+ </nav >
27+
28+ @push (' after_scripts ' )
2929 @basset (
' https://unpkg.com/[email protected] /src/URI.min.js' )
3030 <script >
31- function addOrUpdateUriParameter (uri , parameter , value ) {
32- var new_url = normalizeAmpersand (uri);
33-
34- new_url = URI (new_url).normalizeQuery ();
31+ if (typeof addOrUpdateUriParameter !== ' function' ) {
32+ function addOrUpdateUriParameter (uri , parameter , value ) {
33+ let new_url = URI (uri).normalizeQuery ();
3534
3635 // this param is only needed in datatables persistent url redirector
3736 // not when applying filters so we remove it.
@@ -40,106 +39,160 @@ function addOrUpdateUriParameter(uri, parameter, value) {
4039 }
4140
4241 if (new_url .hasQuery (parameter)) {
43- new_url .removeQuery (parameter);
42+ new_url .removeQuery (parameter);
4443 }
4544
4645 if (value !== ' ' && value != null ) {
47- new_url = new_url .addQuery (parameter, value);
46+ new_url = new_url .addQuery (parameter, value);
4847 }
4948
5049 $ (' #remove_filters_button' ).toggleClass (' invisible' , ! new_url .query ());
5150
52- return new_url .toString ();
53-
54- }
55-
56- function updateDatatablesOnFilterChange (filterName , filterValue , update_url = false , debounce = 500 ) {
57- // behaviour for ajax table
58- var current_url = crud .table .ajax .url ();
59- var new_url = addOrUpdateUriParameter (current_url, filterName, filterValue);
60-
61- new_url = normalizeAmpersand (new_url);
62-
63- // add filter to URL
64- crud .updateUrl (new_url);
65- crud .table .ajax .url (new_url);
66-
67- // when we are clearing ALL filters, we would not update the table url here, because this is done PER filter
68- // and we have a function that will do this update for us after all filters had been cleared.
69- if (update_url) {
70- // replace the datatables ajax url with new_url and reload it
71- callFunctionOnce (function () { refreshDatatablesOnFilterChange (new_url) }, debounce, ' refreshDatatablesOnFilterChange' );
51+ return new_url .normalizeQuery ().toString ();
52+ }
53+ }
54+
55+ if (typeof updatePageUrl !== ' function' ) {
56+ function updatePageUrl (filterName , filterValue , currentUrl = null ) {
57+ currentUrl = currentUrl || window .location .href ;
58+ let newUrl = addOrUpdateUriParameter (currentUrl, filterName, filterValue);
59+ crud .updateUrl (newUrl);
60+ return newUrl;
7261 }
62+ }
63+
64+ if (typeof updateDatatablesOnFilterChange !== ' function' ) {
65+ function updateDatatablesOnFilterChange (filterName , filterValue , update_url = false , debounce = 500 ) {
66+ // behaviour for ajax tables
67+ let new_url = updatePageUrl (filterName, filterValue, crud .table .ajax .url ());
68+ crud .table .ajax .url (new_url);
69+
70+ // when we are clearing ALL filters, we would not update the table url here, because this is done PER filter
71+ // and we have a function that will do this update for us after all filters had been cleared.
72+ if (update_url) {
73+ // replace the datatables ajax url with new_url and reload it
74+ callFunctionOnce (function () { refreshDatatablesOnFilterChange (new_url) }, debounce, ' refreshDatatablesOnFilterChange' );
75+ }
7376
74- return new_url;
75- }
76-
77- /* *
78- * calls the function func once within the within time window.
79- * this is a debounce function which actually calls the func as
80- * opposed to returning a function that would call func.
81- *
82- * @param func the function to call
83- * @param within the time window in milliseconds, defaults to 300
84- * @param timerId an optional key, defaults to func
85- *
86- * FROM: https://stackoverflow.com/questions/27787768/debounce-function-in-jquery
87- */
88- if (typeof callFunctionOnce !== ' function' ) {
77+ return new_url;
78+ }
79+ }
80+
81+ /* *
82+ * calls the function func once within the within time window.
83+ * this is a debounce function which actually calls the func as
84+ * opposed to returning a function that would call func.
85+ *
86+ * @param func the function to call
87+ * @param within the time window in milliseconds, defaults to 300
88+ * @param timerId an optional key, defaults to func
89+ *
90+ * FROM: https://stackoverflow.com/questions/27787768/debounce-function-in-jquery
91+ */
92+ if (typeof callFunctionOnce !== ' function' ) {
8993 function callFunctionOnce (func , within = 300 , timerId = null ) {
90- window .callOnceTimers = window .callOnceTimers || {};
91- timerId = timerId || func;
92- if (window .callOnceTimers [timerId]) {
93- clearTimeout (window .callOnceTimers [timerId]);
94- }
95- window .callOnceTimers [timerId] = setTimeout (func, within);
94+ window .callOnceTimers = window .callOnceTimers || {};
95+ timerId = timerId || func;
96+ if (window .callOnceTimers [timerId]) {
97+ clearTimeout (window .callOnceTimers [timerId]);
98+ }
99+ window .callOnceTimers [timerId] = setTimeout (func, within);
96100 }
97- }
98-
99- function refreshDatatablesOnFilterChange (url )
100- {
101- // replace the datatables ajax url with new_url and reload it
102- crud .table .ajax .url (url).load ();
103- }
101+ }
104102
103+ if (typeof refreshDatatablesOnFilterChange !== ' function' ) {
104+ function refreshDatatablesOnFilterChange (url )
105+ {
106+ // replace the datatables ajax url with new_url and reload it
107+ crud .table .ajax .url (url).load ();
108+ }
109+ }
105110
106- function normalizeAmpersand (string ) {
107- return string .replace (/ &/ g , " &" ).replace (/ amp%3B/ g , " " );
108- }
111+ // button to remove all filters
112+ document .addEventListener (' DOMContentLoaded' , function () {
109113
110- // button to remove all filters
111- jQuery (document ).ready (function ($ ) {
112- $ (" #remove_filters_button" ).click (function (e ) {
113- e .preventDefault ();
114+ // find all nav.navbar-filters
115+ let filtersNavbar = document .querySelectorAll (' .navbar-filters' );
114116
115- // behaviour for ajax table
116- var new_url = ' {{ url ($crud -> getOperationSetting (" datatablesUrl" ). ' /search' ) } }' ;
117- var ajax_table = $ (" #crudTable" ).DataTable ();
117+ // if there are no navbars, return
118+ if (! filtersNavbar .length ) {
119+ return ;
120+ }
118121
119- // replace the datatables ajax url with new_url and reload it
120- ajax_table .ajax .url (new_url).load ();
122+ // run the init function for each filter
123+ filtersNavbar .forEach (function (navbar ) {
124+ let filters = navbar .querySelectorAll (' li[filter-init-function]' );
121125
122- // clear all filters
123- $ (" .navbar-filters li[filter-name]" ).trigger (' filter:clear' );
126+ if (filters .length === 0 ) {
127+ return ;
128+ }
124129
125- // remove filters from URL
126- crud .updateUrl (new_url);
127- });
130+ document .addEventListener (' backpack:filter:changed' , function (event ) {
131+
132+ // check if any of the filters are active
133+ let anyActiveFilters = false ;
134+
135+ filters .forEach (function (filter ) {
136+ if (filter .classList .contains (' active' )) {
137+ anyActiveFilters = true ;
138+ }
139+ });
140+
141+ if (anyActiveFilters === true ) {
142+ navbar .querySelector (' .remove_filters_button' ).classList .remove (' invisible' );
143+ }else {
144+ navbar .querySelector (' .remove_filters_button' ).classList .add (' invisible' );
145+ }
146+ });
147+
148+ filters .forEach (function (filter ) {
149+ let initFunction = filter .getAttribute (' filter-init-function' );
150+ if (window [initFunction]) {
151+ window [initFunction](filter, navbar);
152+ }
153+ });
154+
155+ if (filtersNavbar .length === 0 ) {
156+ return ;
157+ }
128158
129- // hide the Remove filters button when no filter is active
130- $ (" .navbar-filters li[filter-name]" ).on (' filter:clear' , function () {
131- var anyActiveFilters = false ;
132- $ (" .navbar-filters li[filter-name]" ).each (function () {
133- if ($ (this ).hasClass (' active' )) {
134- anyActiveFilters = true ;
135- // console.log('ACTIVE FILTER');
159+ let removeFiltersButton = navbar .querySelector (' .remove_filters_button' );
160+ if (removeFiltersButton) {
161+ removeFiltersButton .addEventListener (' click' , function (e ) {
162+ e .preventDefault ();
163+
164+ document .dispatchEvent (new Event (' backpack:filters:cleared' , {
165+ detail: {
166+ navbar: navbar,
167+ filters: filters,
168+ }
169+ }));
170+
171+ filters .forEach (function (filter ) {
172+ filter .dispatchEvent (new CustomEvent (' backpack:filter:clear' , {
173+ detail: {
174+ clearAllFilters: true ,
175+ }
176+ }));
177+ });
178+ });
136179 }
137- });
138180
139- if (anyActiveFilters == false ) {
140- $ (' #remove_filters_button' ).addClass (' invisible' );
141- }
181+ filters .forEach (function (filter ) {
182+ filter .addEventListener (' backpack:filter:clear' , function () {
183+ let anyActiveFilters = false ;
184+ filters .forEach (function (filterInstance ) {
185+ if (filterInstance .classList .contains (' active' )) {
186+ anyActiveFilters = true ;
187+ }
188+ });
189+
190+ if (anyActiveFilters === false ) {
191+ removeFiltersButton? .classList .add (' invisible' );
192+ }
193+ });
194+ });
142195 });
143- });
196+ });
144197 < / script>
145- @endpush
198+ @endpush
0 commit comments