Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC] "Agnostic" filters #5714

Open
wants to merge 4 commits into
base: allow-datatables-url-configuration
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/app/View/Components/FiltersNavbar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Backpack\CRUD\app\View\Components;

use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

class FiltersNavbar extends Component
{
public $crud;

/**
* Create a new component instance.
*/
public function __construct($crud)
{
$this->crud = $crud;
}

/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return backpack_view('components.filters-navbar');
}
}
39 changes: 38 additions & 1 deletion src/resources/views/crud/inc/datatables_logic.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,47 @@ functionsToRunOnDataTablesDrawEvent: [],
"<'table-footer row mt-2 d-print-none align-items-center '<'col-sm-12 col-md-4'l><'col-sm-0 col-md-4 text-center'B><'col-sm-12 col-md-4 'p>>",
}
}
</script>
</script>
@include('crud::inc.export_buttons')

<script type="text/javascript">
// listen to `Backpack:filters-cleared` event
document.addEventListener('Backpack:filters-cleared', function (event) {
// behaviour for ajax table
var new_url = '{{ url($crud->getOperationSetting("datatablesUrl").'/search') }}';
var ajax_table = $("#crudTable").DataTable();

// replace the datatables ajax url with new_url and reload it
ajax_table.ajax.url(new_url).load();

// clear all filters
$(".navbar-filters li[filter-name]").trigger('filter:clear');

// remove filters from URL
crud.updateUrl(new_url);
});

document.addEventListener('Backpack:filter-changed', function (event) {
let filterName = event.detail.filterName;
let filterValue = event.detail.filterValue;
let shouldUpdateUrl = event.detail.shouldUpdateUrl;
let debounce = event.detail.debounce;
let afterUpdateCallback = event.detail.afterUpdateCallback;

let new_url = updateDatatablesOnFilterChange(filterName, filterValue, filterValue || shouldUpdateUrl, debounce);

let extraOptions = Object.keys(event.detail).reduce((obj, key) => {
if (!['filterName', 'filterValue', 'shouldUpdateUrl', 'debounce', 'afterUpdateCallback'].includes(key)) {
obj[key] = event.detail[key];
}
return obj;
}, {});

if (afterUpdateCallback) {
afterUpdateCallback(new_url, extraOptions);
}
});

jQuery(document).ready(function($) {

window.crud.table = $("#crudTable").DataTable(window.crud.dataTableConfiguration);
Expand Down
143 changes: 0 additions & 143 deletions src/resources/views/crud/inc/filters_navbar.blade.php

This file was deleted.

2 changes: 1 addition & 1 deletion src/resources/views/crud/list.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

{{-- Backpack List Filters --}}
@if ($crud->filtersEnabled())
@include('crud::inc.filters_navbar')
<x-backpack::filters-navbar :crud="$crud"/>
@endif

<div class="{{ backpack_theme_config('classes.tableWrapper') }}">
Expand Down
132 changes: 132 additions & 0 deletions src/resources/views/ui/components/filters-navbar.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<nav class="navbar navbar-expand-lg navbar-filters mb-0 py-0 shadow-none">
{{-- Brand and toggle get grouped for better mobile display --}}
<a class="nav-item d-none d-lg-block my-auto"><span class="la la-filter"></span></a>
<button class="navbar-toggler ms-3"
type="button"
data-toggle="collapse" {{-- for Bootstrap v4 --}}
data-target="#bp-filters-navbar" {{-- for Bootstrap v4 --}}
data-bs-toggle="collapse" {{-- for Bootstrap v5 --}}
data-bs-target="#bp-filters-navbar" {{-- for Bootstrap v5 --}}
aria-controls="bp-filters-navbar"
aria-expanded="false"
aria-label="{{ trans('backpack::crud.toggle_filters') }}">
<span class="la la-filter"></span> {{ trans('backpack::crud.filters') }}
</button>

{{-- Collect the nav links, forms, and other content for toggling --}}
<div class="collapse navbar-collapse" id="bp-filters-navbar">
<ul class="nav navbar-nav">
{{-- THE ACTUAL FILTERS --}}
@foreach ($crud->filters() as $filter)
@includeFirst($filter->getNamespacedViewWithFallbacks())
@endforeach
<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>
</ul>
</div>{{-- /.navbar-collapse --}}
</nav>

@push('after_scripts')
@basset('https://unpkg.com/[email protected]/src/URI.min.js')
<script>
function addOrUpdateUriParameter(uri, parameter, value) {
var new_url = normalizeAmpersand(uri);

new_url = URI(new_url).normalizeQuery();

// this param is only needed in datatables persistent url redirector
// not when applying filters so we remove it.
if (new_url.hasQuery('persistent-table')) {
new_url.removeQuery('persistent-table');
}

if (new_url.hasQuery(parameter)) {
new_url.removeQuery(parameter);
}

if (value !== '' && value != null) {
new_url = new_url.addQuery(parameter, value);
}

$('#remove_filters_button').toggleClass('invisible', !new_url.query());

return normalizeAmpersand(new_url.toString());
}

function updatePageUrl(filterName, filterValue, currentUrl = null) {
currentUrl = currentUrl || window.location.href;
let newUrl = addOrUpdateUriParameter(currentUrl, filterName, filterValue);
crud.updateUrl(newUrl);
return newUrl;
}

function updateDatatablesOnFilterChange(filterName, filterValue, update_url = false, debounce = 500) {
// behaviour for ajax tables
let new_url = updatePageUrl(filterName, filterValue, crud.table.ajax.url());
crud.table.ajax.url(new_url);

// when we are clearing ALL filters, we would not update the table url here, because this is done PER filter
// and we have a function that will do this update for us after all filters had been cleared.
if(update_url) {
// replace the datatables ajax url with new_url and reload it
callFunctionOnce(function() { refreshDatatablesOnFilterChange(new_url) }, debounce, 'refreshDatatablesOnFilterChange');
}

return new_url;
}

/**
* calls the function func once within the within time window.
* this is a debounce function which actually calls the func as
* opposed to returning a function that would call func.
*
* @param func the function to call
* @param within the time window in milliseconds, defaults to 300
* @param timerId an optional key, defaults to func
*
* FROM: https://stackoverflow.com/questions/27787768/debounce-function-in-jquery
*/
function callFunctionOnce(func, within = 300, timerId = null) {
window.callOnceTimers = window.callOnceTimers || {};
timerId = timerId || func;
if (window.callOnceTimers[timerId]) {
clearTimeout(window.callOnceTimers[timerId]);
}
window.callOnceTimers[timerId] = setTimeout(func, within);
}

function refreshDatatablesOnFilterChange(url)
{
// replace the datatables ajax url with new_url and reload it
crud.table.ajax.url(url).load();
}


function normalizeAmpersand(string) {
return string.replace(/&amp;/g, "&").replace(/amp%3B/g, "");
}

// button to remove all filters
jQuery(document).ready(function($) {
$("#remove_filters_button").click(function(e) {
e.preventDefault();

// emit a new custom Backpack:filter-removed event
document.dispatchEvent(new CustomEvent('Backpack:filters-cleared'));
});

// hide the Remove filters button when no filter is active
$(".navbar-filters li[filter-name]").on('filter:clear', function() {
var anyActiveFilters = false;
$(".navbar-filters li[filter-name]").each(function () {
if ($(this).hasClass('active')) {
anyActiveFilters = true;
}
});

if (anyActiveFilters == false) {
$('#remove_filters_button').addClass('invisible');
}
});
});
</script>
@endpush