chg: [element:generic_index] Improved quick filter functionality and UI
parent
c4ef14077f
commit
13c8f3f7c5
|
@ -340,6 +340,7 @@ class CRUDComponent extends Component
|
||||||
protected function setQuickFilters(array $params, \Cake\ORM\Query $query, array $quickFilterFields): \Cake\ORM\Query
|
protected function setQuickFilters(array $params, \Cake\ORM\Query $query, array $quickFilterFields): \Cake\ORM\Query
|
||||||
{
|
{
|
||||||
$queryConditions = [];
|
$queryConditions = [];
|
||||||
|
$this->Controller->set('quickFilter', empty($quickFilterFields) ? [] : $quickFilterFields);
|
||||||
if (!empty($params['quickFilter']) && !empty($quickFilterFields)) {
|
if (!empty($params['quickFilter']) && !empty($quickFilterFields)) {
|
||||||
$this->Controller->set('quickFilterValue', $params['quickFilter']);
|
$this->Controller->set('quickFilterValue', $params['quickFilter']);
|
||||||
foreach ($quickFilterFields as $filterField) {
|
foreach ($quickFilterFields as $filterField) {
|
||||||
|
|
|
@ -23,7 +23,6 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
||||||
'button' => __('Filter'),
|
'button' => __('Filter'),
|
||||||
'placeholder' => __('Enter value to search'),
|
'placeholder' => __('Enter value to search'),
|
||||||
'data' => '',
|
'data' => '',
|
||||||
'value' => $quickFilterValue,
|
|
||||||
'searchKey' => 'value'
|
'searchKey' => 'value'
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
empty($data['placeholder']) ? '' : h($data['placeholder']),
|
empty($data['placeholder']) ? '' : h($data['placeholder']),
|
||||||
empty($data['id']) ? 'quickFilterField' : h($data['id']),
|
empty($data['id']) ? 'quickFilterField' : h($data['id']),
|
||||||
empty($data['searchKey']) ? 'searchall' : h($data['searchKey']),
|
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(
|
echo sprintf(
|
||||||
'<div class="input-group" data-table-random-value="%s" style="margin-left: auto;">%s%s</div>',
|
'<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 controller = '<?= $this->request->getParam('controller') ?>';
|
||||||
var action = '<?= $this->request->getParam('action') ?>';
|
var action = '<?= $this->request->getParam('action') ?>';
|
||||||
var additionalUrlParams = '';
|
var additionalUrlParams = '';
|
||||||
|
var quickFilter = <?= json_encode($quickFilter) ?>;
|
||||||
<?php
|
<?php
|
||||||
if (!empty($data['additionalUrlParams'])) {
|
if (!empty($data['additionalUrlParams'])) {
|
||||||
echo sprintf(
|
echo sprintf(
|
||||||
|
@ -53,6 +54,13 @@
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
var randomValue = '<?= h($tableRandomValue) ?>';
|
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) => {
|
$(`#quickFilterButton-${randomValue}`).click((e) => {
|
||||||
doFilter($(e.target))
|
doFilter($(e.target))
|
||||||
});
|
});
|
||||||
|
@ -61,9 +69,14 @@
|
||||||
const $button = $(`#quickFilterButton-${randomValue}`)
|
const $button = $(`#quickFilterButton-${randomValue}`)
|
||||||
doFilter($button)
|
doFilter($button)
|
||||||
}
|
}
|
||||||
|
}).on('focus', (e) => {
|
||||||
|
$(`#quickFilterField-${randomValue}`).popover('show')
|
||||||
|
}).on('focusout', (e) => {
|
||||||
|
$(`#quickFilterField-${randomValue}`).popover('hide')
|
||||||
});
|
});
|
||||||
|
|
||||||
function doFilter($button) {
|
function doFilter($button) {
|
||||||
|
$(`#quickFilterField-${randomValue}`).popover('hide')
|
||||||
const encodedFilters = encodeURIComponent($(`#quickFilterField-${randomValue}`).val())
|
const encodedFilters = encodeURIComponent($(`#quickFilterField-${randomValue}`).val())
|
||||||
const url = `/${controller}/${action}${additionalUrlParams}?quickFilter=${encodedFilters}`
|
const url = `/${controller}/${action}${additionalUrlParams}?quickFilter=${encodedFilters}`
|
||||||
UI.reload(url, $(`#table-container-${randomValue}`), $(`#table-container-${randomValue} table.table`), [{
|
UI.reload(url, $(`#table-container-${randomValue}`), $(`#table-container-${randomValue} table.table`), [{
|
||||||
|
@ -71,5 +84,35 @@
|
||||||
config: {}
|
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>
|
</script>
|
||||||
|
|
|
@ -649,7 +649,7 @@ class OverlayFactory {
|
||||||
static defaultOptions = {
|
static defaultOptions = {
|
||||||
text: '',
|
text: '',
|
||||||
variant: 'light',
|
variant: 'light',
|
||||||
opacity: 0.85,
|
opacity: 0.15,
|
||||||
blur: '2px',
|
blur: '2px',
|
||||||
rounded: false,
|
rounded: false,
|
||||||
auto: true,
|
auto: true,
|
||||||
|
@ -828,4 +828,74 @@ class FormValidationHelper {
|
||||||
$(this.form).find('.invalid-feedback').remove()
|
$(this.form).find('.invalid-feedback').remove()
|
||||||
$(this.form).parent().find('.alert').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
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue