chg: [element:generic_index] Improved quick filter functionality and UI

pull/37/head
mokaddem 2021-01-15 16:58:46 +01:00
parent c4ef14077f
commit 13c8f3f7c5
4 changed files with 116 additions and 3 deletions

View File

@ -340,6 +340,7 @@ class CRUDComponent extends Component
protected function setQuickFilters(array $params, \Cake\ORM\Query $query, array $quickFilterFields): \Cake\ORM\Query
{
$queryConditions = [];
$this->Controller->set('quickFilter', empty($quickFilterFields) ? [] : $quickFilterFields);
if (!empty($params['quickFilter']) && !empty($quickFilterFields)) {
$this->Controller->set('quickFilterValue', $params['quickFilter']);
foreach ($quickFilterFields as $filterField) {

View File

@ -23,7 +23,6 @@ echo $this->element('genericElements/IndexTable/index_table', [
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'data' => '',
'value' => $quickFilterValue,
'searchKey' => 'value'
]
]

View File

@ -29,7 +29,7 @@
empty($data['placeholder']) ? '' : h($data['placeholder']),
empty($data['id']) ? 'quickFilterField' : h($data['id']),
empty($data['searchKey']) ? 'searchall' : h($data['searchKey']),
empty($data['value']) ? '' : h($data['value'])
empty($data['value']) ? (!empty($quickFilterValue) ? h($quickFilterValue) : '') : h($data['value'])
);
echo sprintf(
'<div class="input-group" data-table-random-value="%s" style="margin-left: auto;">%s%s</div>',
@ -44,6 +44,7 @@
var controller = '<?= $this->request->getParam('controller') ?>';
var action = '<?= $this->request->getParam('action') ?>';
var additionalUrlParams = '';
var quickFilter = <?= json_encode($quickFilter) ?>;
<?php
if (!empty($data['additionalUrlParams'])) {
echo sprintf(
@ -53,6 +54,13 @@
}
?>
var randomValue = '<?= h($tableRandomValue) ?>';
$(`#quickFilterField-${randomValue}`).popover({
title: '<?= __('Searcheable fields') ?>',
content: function() { return buildPopoverQuickFilterBody(quickFilter) },
html: true,
sanitize: false,
trigger: 'manual',
})
$(`#quickFilterButton-${randomValue}`).click((e) => {
doFilter($(e.target))
});
@ -61,9 +69,14 @@
const $button = $(`#quickFilterButton-${randomValue}`)
doFilter($button)
}
}).on('focus', (e) => {
$(`#quickFilterField-${randomValue}`).popover('show')
}).on('focusout', (e) => {
$(`#quickFilterField-${randomValue}`).popover('hide')
});
function doFilter($button) {
$(`#quickFilterField-${randomValue}`).popover('hide')
const encodedFilters = encodeURIComponent($(`#quickFilterField-${randomValue}`).val())
const url = `/${controller}/${action}${additionalUrlParams}?quickFilter=${encodedFilters}`
UI.reload(url, $(`#table-container-${randomValue}`), $(`#table-container-${randomValue} table.table`), [{
@ -71,5 +84,35 @@
config: {}
}])
}
function buildPopoverQuickFilterBody(quickFilter) {
let tableData = []
quickFilter.forEach(field => {
let fieldName, searchContain
if (typeof field === 'object') {
fieldName = Object.keys(field)[0];
searchContain = field[fieldName]
} else {
fieldName = field
searchContain = false
}
$searchType = $('<span/>')
.text(searchContain ? '<?= __('Contain') ?>' : '<?= __('Exact match') ?>')
.attr('title', searchContain ? '<?= __('The search value will be used as a substring') ?>' : '<?= __('The search value must strictly match') ?>')
tableData.push([fieldName, $searchType])
});
tableData.sort((a, b) => a[0] < b[0] ? -1 : 1)
$table = HtmlHelper.table(
['<?= __('Field name') ?>', '<?= __('Search type') ?>'],
tableData,
{
small: true,
tableClass: ['mb-0'],
caption: '<?= __('All these fields will be searched simultaneously') ?>'
}
)
return $table[0].outerHTML
}
});
</script>

View File

@ -649,7 +649,7 @@ class OverlayFactory {
static defaultOptions = {
text: '',
variant: 'light',
opacity: 0.85,
opacity: 0.15,
blur: '2px',
rounded: false,
auto: true,
@ -828,4 +828,74 @@ class FormValidationHelper {
$(this.form).find('.invalid-feedback').remove()
$(this.form).parent().find('.alert').remove()
}
}
class HtmlHelper {
static table(head=[], body=[], options={}) {
const $table = $('<table/>')
const $thead = $('<thead/>')
const $tbody = $('<tbody/>')
$table.addClass('table')
if (options.striped) {
$table.addClass('table-striped')
}
if (options.bordered) {
$table.addClass('table-bordered')
}
if (options.borderless) {
$table.addClass('table-borderless')
}
if (options.hoverable) {
$table.addClass('table-hover')
}
if (options.small) {
$table.addClass('table-sm')
}
if (options.variant) {
$table.addClass(`table-${options.variant}`)
}
if (options.tableClass) {
$table.addClass(options.tableClass)
}
const $caption = $('<caption/>')
if (options.caption) {
if (options.caption instanceof jQuery) {
$caption = options.caption
} else {
$caption.text(options.caption)
}
}
const $theadRow = $('<tr/>')
head.forEach(head => {
if (head instanceof jQuery) {
$theadRow.append($('<td/>').append(head))
} else {
$theadRow.append($('<th/>').text(head))
}
})
$thead.append($theadRow)
body.forEach(row => {
const $bodyRow = $('<tr/>')
row.forEach(item => {
if (item instanceof jQuery) {
$bodyRow.append($('<td/>').append(item))
} else {
$bodyRow.append($('<td/>').text(item))
}
})
$tbody.append($bodyRow)
})
$table.append($caption, $thead, $tbody)
if (options.responsive) {
options.responsiveBreakpoint = options.responsiveBreakpoint !== undefined ? options.responsiveBreakpoint : ''
$table = $('<div/>').addClass(options.responsiveBreakpoint !== undefined ? `table-responsive-${options.responsiveBreakpoint}` : 'table-responsive').append($table)
}
return $table
}
}