Skip to content

Commit

Permalink
Update filter js
Browse files Browse the repository at this point in the history
  • Loading branch information
onEXHovia committed Dec 11, 2024
1 parent 0136c32 commit 6f58d3c
Show file tree
Hide file tree
Showing 12 changed files with 519 additions and 231 deletions.
128 changes: 0 additions & 128 deletions assets/js/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const Admin = {
shared_setup(subject) {
Admin.read_config();
Admin.log('[core|shared_setup] Register services on', subject);
Admin.add_filters(subject);
Admin.setup_select2(subject);
Admin.setup_icheck(subject);
Admin.setup_checkbox_range_selection(subject);
Expand Down Expand Up @@ -256,115 +255,6 @@ const Admin = {
return event.target;
},

add_filters(subject) {
Admin.log('[core|add_filters] configure filters on', subject);

function updateCounter() {
const count = jQuery('a.sonata-toggle-filter .fa-check-square', subject).length;

jQuery('.sonata-filter-count', subject).text(count);
}

jQuery('a.sonata-toggle-filter', subject).on('click', (event) => {
event.preventDefault();
event.stopPropagation();

if (jQuery(event.target).attr('sonata-filter') === 'false') {
return;
}

Admin.log(
'[core|add_filters] handle filter container: ',
jQuery(event.target).attr('filter-container')
);

const filtersContainer = jQuery(`#${jQuery(event.currentTarget).attr('filter-container')}`);

if (jQuery('div[sonata-filter="true"]:visible', filtersContainer).length === 0) {
jQuery(filtersContainer).slideDown();
}

const targetSelector = jQuery(event.currentTarget).attr('filter-target');
const target = jQuery(`div[id="${targetSelector}"]`, filtersContainer);
const filterToggler = jQuery('i', `.sonata-toggle-filter[filter-target="${targetSelector}"]`);
if (jQuery(target).is(':visible')) {
filterToggler
.filter(':not(.fa-minus-circle)')
.removeClass('fa-check-square')
.addClass('fa-square');
target.hide();
} else {
filterToggler
.filter(':not(.fa-minus-circle)')
.removeClass('fa-square')
.addClass('fa-check-square');
target.show();
}

if (jQuery('div[sonata-filter="true"]:visible', filtersContainer).length > 0) {
jQuery(filtersContainer).slideDown();
} else {
jQuery(filtersContainer).slideUp();
}

updateCounter();
});

jQuery('.sonata-filter-form', subject).on('submit', (event) => {
const $form = jQuery(event.target);
$form.find('[sonata-filter="true"]:hidden :input').val('');

if (!event.target.dataset.defaultValues) {
return;
}

const defaults = Admin.convert_query_string_to_object(
jQuery.param({ filter: JSON.parse(event.target.dataset.defaultValues) })
);

// Keep only changed values
$form.find('[name*=filter]').each((index, element) => {
const defaultValue = element.multiple ? [] : '';
const defaultElementValue = defaults[element.name] || defaultValue;
const elementValue = jQuery(element).val() || defaultValue;

if (JSON.stringify(defaultElementValue) === JSON.stringify(elementValue)) {
element.removeAttribute('name');
} else if (element.multiple && JSON.stringify(elementValue) === '[]') {
// Empty array values will not be submitted, but we need to override
// the default value provided by `AdminInterface::getDefaultFilterParameters()`.
// So we change the empty select to an empty input in order to have a submitted value.
// @see https://github.com/sonata-project/SonataAdminBundle/issues/7547

// We remove the `[]` from the select name in order to generate the input name
const name = element.name.substring(0, element.name.length - 2);
$form.append(`<input name="${name}" type="hidden" value="">`);
element.removeAttribute('name');
}
});

// Simulate a reset if no value is different from the default ones.
if ($form.find('[name*=filter]').length === 0) {
$form.append('<input name="filters" type="hidden" value="reset">');
}
});

/* Advanced filters */
if (
jQuery('.advanced-filter :input:visible', subject).filter(function filterWithoutValue() {
return jQuery(this).val();
}).length === 0
) {
jQuery('.advanced-filter').hide();
}

jQuery('[data-toggle="advanced-filter"]', subject).on('click', () => {
jQuery('.advanced-filter').toggle();
});

updateCounter();
},

setup_collection_counter(subject) {
Admin.log('[core|setup_collection_counter] setup collection counter', subject);

Expand Down Expand Up @@ -807,24 +697,6 @@ const Admin = {
});
},

convert_query_string_to_object(str) {
return str.split('&').reduce((accumulator, keyValue) => {
const key = decodeURIComponent(keyValue.split('=')[0]);
const val = keyValue.split('=')[1];

if (key.endsWith('[]')) {
if (!Object.prototype.hasOwnProperty.call(accumulator, key)) {
accumulator[key] = [];
}
accumulator[key].push(val);
} else {
accumulator[key] = val;
}

return accumulator;
}, {});
},

/**
* Remember open tab after refreshing page.
*/
Expand Down
105 changes: 105 additions & 0 deletions assets/js/controllers/filter_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*!
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import qs from 'qs';
import { Controller } from '@hotwired/stimulus';
import { controlValue, convertQueryStringToObject } from '../utils';

export default class extends Controller {
static targets = ['form', 'group', 'advanced', 'submitter'];
static outlets = ['filter-list'];
static values = {
defaultValues: Object,
};

connect() {
const withAdvanced = this.advancedTargets.find((advanced) => !advanced.hidden) !== null;
this.advancedTargets.forEach((advanced) => {
advanced.hidden = !withAdvanced;
});
}

prepareSubmit() {
const defaults = convertQueryStringToObject(
qs.stringify({
filter: this.defaultValuesValue,
})
);

const changed = [];
this.formElements.forEach((element) => {
const defaultValue = element.multiple ? [] : '';
const defaultElementValue = defaults[element.name] || defaultValue;
const elementValue = controlValue(element) || defaultValue;

if (element.closest('[hidden]')) {
element.removeAttribute('name');
} else if (JSON.stringify(defaultElementValue) === JSON.stringify(elementValue)) {
element.removeAttribute('name');
} else if (element.multiple && JSON.stringify(elementValue) === '[]') {
// Empty array values will not be submitted, but we need to override
// the default value provided by `AdminInterface::getDefaultFilterParameters()`.
// So we change the empty select to an empty input in order to have a submitted value.
// @see https://github.com/sonata-project/SonataAdminBundle/issues/7547

// We remove the `[]` from the select name in order to generate the input name
const name = element.name.substring(0, element.name.length - 2);
const input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = '';

this.formTarget.appendChild(input);
element.removeAttribute('name');
} else {
changed.push(element);
}
});

if (changed.length === 0) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'filters';
input.value = 'reset';

this.formTarget.appendChild(input);
}

this.submitterTarget.disabled = true;
}

toggleAdvanced() {
this.advancedTargets.forEach((advanced) => {
advanced.hidden = !advanced.hidden;
});
}

toggleFilter(id, state) {
const group = this.groupTargets.find((el) => id === el.id);
if (group) {
group.hidden = !state;
this.element.hidden = !this.visibleGroups.length;
}
}

hideFilter({ params }) {
this.toggleFilter(params.id, false);
this.filterListOutlet.disable(params.id);
}

get visibleGroups() {
return this.groupTargets.filter((group) => !group.hidden);
}

get formElements() {
return Array.from(this.formTarget.elements)
.filter((tag) => ['select', 'textarea', 'input'].includes(tag.tagName.toLowerCase()))
.filter((element) => element.name.includes('filter'));
}
}
47 changes: 47 additions & 0 deletions assets/js/controllers/filter_list_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*!
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
static targets = ['counter', 'field'];
static outlets = ['filter'];
static classes = ['active'];

connect() {
this.updateCounter();
}

updateCounter() {
this.counterTarget.innerHTML = this.enabledFields.length;
}

disable(id) {
const field = this.fieldTargets.find((el) => id === el.dataset.filter);
if (field) {
field.classList.remove(this.activeClass);
this.updateCounter();
}
}

toggle(event) {
const field = event.target;
const state = field.classList.contains(this.activeClass);
field.classList.toggle(this.activeClass, !state);

this.filterOutlet.toggleFilter(field.dataset.filter, !state);
this.updateCounter();
}

get enabledFields() {
return this.fieldTargets.filter((field) => {
return field.classList.contains(this.activeClass);
});
}
}
37 changes: 37 additions & 0 deletions assets/js/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*!
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

export function controlValue(el) {
if (el.options && el.multiple) {
// prettier-ignore
return el.options
.filter((option) => option.selected)
.map((option) => option.value);
}

return el.value;
}

export function convertQueryStringToObject(str) {
return str.split('&').reduce((accumulator, keyValue) => {
const key = decodeURIComponent(keyValue.split('=')[0]);
const val = keyValue.split('=')[1];

if (key.endsWith('[]')) {
if (!Object.prototype.hasOwnProperty.call(accumulator, key)) {
accumulator[key] = [];
}
accumulator[key].push(val);
} else {
accumulator[key] = val;
}

return accumulator;
}, {});
}
10 changes: 10 additions & 0 deletions assets/scss/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -563,3 +563,13 @@ div.sonata-filters-box div.form-group {
.sonata-search-result-show {
display: block;
}

.sonata-toggle-filter {
i::before {
content: '\f0c8'; // fa-square
}

&.active i::before {
content: '\f14a'; // fa-check-square
}
}
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"symfony/security-core": "^6.4 || ^7.1",
"symfony/security-csrf": "^6.4 || ^7.1",
"symfony/serializer": "^6.4 || ^7.1",
"symfony/stimulus-bundle": "^2.22",
"symfony/string": "^6.4 || ^7.1",
"symfony/translation": "^6.4 || ^7.1",
"symfony/translation-contracts": "^2.3 || ^3.0",
Expand Down
Loading

0 comments on commit 6f58d3c

Please sign in to comment.