@excelwebzone/symfony-admin-ui
Version:
Symfony Admin UI is a simple set of UI behaviors and components used with your [symfony-admin](https://github.com/excelwebzone/symfony-admin-bundle) application.
515 lines (431 loc) • 17.5 kB
JavaScript
import $ from 'jquery';
import numeral from 'numeral';
import Odometer from 'odometer';
import axios from '../../lib/utils/axios_utils';
import toaster from '../../lib/utils/toaster';
import DataViewer from './data_viewer';
import { mergeUrlParams, removeParams, getParameterValues } from '../../lib/utils/url_utility';
export default class ListPage {
constructor(containerEl, getChartCallback = null, allowDecimals = true, showSingleRowInfo = true) {
this.getChartCallback = getChartCallback;
this.allowDecimals = allowDecimals;
this.showSingleRowInfo = showSingleRowInfo;
this.initDomElements(containerEl);
this.bindEvents();
this.createDataViewer();
}
initDomElements(containerEl) {
this.$container = containerEl ? $(containerEl) : $('body');
this.$selectAllCheckbox = this.$container.find('.js-bulk-select-all');
this.$chartTotals = this.$container.find('.list-chart-summary-number-item');
this.$chartContainer = this.$container.find('.chart-container-chart');
this.$chartLoading = this.$container.find('.chart-loading-overlay');
this.odCurrencies = {};
for (let currency of this.$container.find('.js-odometer-currency')) {
this.odCurrencies[$(currency).data('currency-field')] = new Odometer({
el: currency,
duration: 200,
format: '(,ddd)',
theme: 'default'
});
}
if (this.$container.find('.list-page-header.add-header-toggle').length) {
this.$container.find('.list-page-header.add-header-toggle').after(`
<div class="list-page-header-toggle">
<i class="ledger-icons ledger-icon-chevron-up"></i>
</div>
`);
}
}
bindEvents() {
this.$container.on('click', '.list-page-header-toggle', (e) => {
this.$container.find('.list-page-container').toggleClass('is-collapsed');
// resize table
if (this.dataViewer.getDatagrid().getTable()) {
this.dataViewer.getDatagrid().resizeTable();
this.dataViewer.getDatagrid().rebindEvents();
}
});
$(document).on('reload:list', '.js-list-page-activity-log-form', () => {
this.dataViewer.getFilter().loadActiveFilter();
});
this.$container.on('click', '.js-grouping-type', () => {
if (this.$chartContainer.length && !this.$chartContainer.data('reload-list')) {
this.loadChartData(this.dataViewer.getFilter().getData());
return;
}
this.dataViewer.filterData();
});
$(document).on('click', '.js-list-page-limit .option-list-item', (e) => {
const limit = parseInt($(e.currentTarget).data('value'));
if (limit > 0) {
this.dataViewer.getPager().setLimit(limit);
this.dataViewer.filterData();
}
});
this.$selectAllCheckbox.on('click', () => this.selectAllRows());
this.$container.on('click', '.js-bulk-select', () => this.selectRow());
this.$container.on('click', '.js-print-list', (e) => this.printList(e));
this.$container.on('click', '.js-bulk-export,.js-bulk-print,.js-bulk-action', (e) => this.bulkGeneric(e));
this.$container.on('click', '.js-bulk-email', (e) => this.bulkMailto(e));
this.$container.on('click', '.js-bulk-follow', (e) => this.bulkFollow(e));
this.$container.on('click', '.js-bulk-complete', (e) => this.bulkComplete(e));
this.$container.on('click', '.js-bulk-unread', (e) => this.bulkUnread(e));
this.$container.on('drawer:shown', '.js-bulk-edit', (e, drawer) => this.bulkEditDrawer(e, drawer));
this.$container.on('modal:shown', '.js-bulk-delete', (e, modal) => this.bulkDeleteModal(e, modal));
// toggle between list and card view (reload page)
this.$container.on('click', '.js-toggle-card-view', (e) => {
const $button = $(e.currentTarget);
if ($button.data('endpoint')) {
window.location.href = $button.data('endpoint') + window.location.href.substring(window.location.href.indexOf('?'));
return !1;
}
window.location.href = parseInt(getParameterValues('cardView')[0])
? removeParams(['cardView'])
: mergeUrlParams({ cardView: 1 }, window.location.href);
});
// toggle search state
$(document).on('shown.bs.collapse', '.list-search', (e) => {
$('.application-content').addClass('has-search');
// focus on search field
$(e.currentTarget).find('.input-text').focus();
// resize table
if (this.dataViewer.getDatagrid().getTable()) {
this.dataViewer.getDatagrid().resizeTable();
this.dataViewer.getDatagrid().rebindEvents();
}
});
$(document).on('hidden.bs.collapse', '.list-search', (e) => {
$('.application-content').removeClass('has-search');
// resize table
if (this.dataViewer.getDatagrid().getTable()) {
this.dataViewer.getDatagrid().resizeTable();
this.dataViewer.getDatagrid().rebindEvents();
}
});
}
createDataViewer() {
const self = this;
// run before append html
const preCallback = (viewer, data) => {
if (data.page === 1) {
const $dropdownCount = viewer.$container.find('.dropdown-count');
if ($dropdownCount) {
$dropdownCount.text(data.total);
}
if (typeof data.totals === 'object') {
for (let [key, value] of Object.entries(data.totals)) {
const $cellContent = viewer.$table.find(`.table-header-cell[data-field="${key}"] .table-header-cell-content`);
// remove decimals
if ((!self.allowDecimals && (value.indexOf('.') !== -1))
|| (self.allowDecimals && parseInt(value.substring(value.indexOf('.') + 1)) === 0)
) {
value = value.replace(/^(\W)?([0-9,]+)([0-9.]+)(\W)?$/g, '$1$2$4');
}
$cellContent.append(`<span class="total-value ${value.replace(/\D/g, '') < 0 ? 'is-negative' : ''}">${value}</span>`);
}
}
}
if (data.currency && self.odCurrencies) {
for (let [key, value] of Object.entries(data.currency)) {
self.odCurrencies[key].update(value || 0);
}
}
viewer.$table.trigger('data:received', data);
};
// run after append html
const postCallback = (viewer, data) => {
// auto select all new checkboxes when select-all is checked
if (self.$selectAllCheckbox.is(':checked')) {
self.$selectAllCheckbox.prop('checked', false);
self.$selectAllCheckbox.click();
}
// automatically open single result (ignore under for multi block)
if (self.showSingleRowInfo && data.page === 1 && data.total === 1
&& !viewer.$container.hasClass('js-datagrid-block')
&& !viewer.$table.hasClass('activity-list')
) {
viewer.$table.find('.datagrid-body-container .datagrid-left-table-block>div .js-entity-drawer:eq(0)').click();
}
viewer.$table.trigger('data:loaded', data);
};
// run before reseting html
const preFilterLoad = (viewer) => {
const $dropdownCount = viewer.$container.find('.dropdown-count');
if ($dropdownCount) {
$dropdownCount.text(0);
}
const $headers = viewer.$table.find('.table-header-cell[data-field]');
for (let header of $headers) {
$(header).find('.table-header-cell-content>span.total-value').remove();
}
if (self.odCurrency) {
self.odCurrency.update(0);
}
if (self.$chartTotals.length) {
for (let total of self.$chartTotals) {
const $total = $(total).find('>div:eq(0)');
let format = '0';
if ($total.data('money')) format = '$0';
if ($total.data('percent')) format = '0%';
$total.html(numeral(0).format(format));
}
}
};
// run after setting
const postFilterLoad = (viewer, filters) => {
self.loadChartData(filters);
};
// extends the filter request params
const setFilterParams = (viewer) => {
const params = {};
const groupingType = $('.js-grouping-type.is-selected');
if (groupingType.length) {
params.groupingType = groupingType.data('value');
}
if (viewer.$table.data('totals')) {
params.showTotals = 1;
}
if (viewer.$table.data('card-view')) {
params.cardView = 1;
}
return params;
};
this.dataViewer = new DataViewer(this.$container, {
preCallback: preCallback,
postCallback: postCallback,
preFilterLoad: preFilterLoad,
postFilterLoad: postFilterLoad,
setFilterParams: setFilterParams
});
}
loadChartData(filters) {
if (this.$chartContainer.length && this.getChartCallback) {
this.$chartLoading.show();
this.$chartContainer.html('');
const params = { filters: filters };
const groupingType = $('.js-grouping-type.is-selected');
if (groupingType.length) {
params.groupingType = groupingType.data('value');
}
axios.get(this.$chartContainer.data('endpoint'), {
params: params
})
.then(({ data }) => {
this.$chartLoading.hide();
for (let total of this.$chartTotals) {
const $total = $(total).find('>div:eq(0)');
let value = data.total[$total.data('name')];
let format = '0,0[.]00';
if ($total.data('money')) format = '$0,0[.]00';
if ($total.data('percent')) {
format = '0,0[.]00%';
value /= 100; // @hack: value is a percent
}
if (!this.allowDecimals && !this.$chartContainer.data('allow-decimals')) {
format = format.replace('[.]00', '');
}
$total.html(numeral(value).format(format));
}
const chartFunc = this.getChartCallback(this.$chartContainer.data('token'));
if (chartFunc) {
const colors = this.$chartContainer.data('colors') || null;
const format = this.$chartContainer.data('format');
chartFunc(this.$chartContainer, data.labels, data.items, colors, format);
}
}).catch(() => this.$chartLoading.hide());
}
}
printList(e) {
const params = {};
if (this.dataViewer.getFilter().getData()) {
params.filters = this.dataViewer.getFilter().getData();
}
if (this.dataViewer.getSortColumn()) {
params.sort = this.dataViewer.getSortColumn();
}
const groupingType = $('.js-grouping-type.is-selected');
if (groupingType.length) {
params.groupingType = groupingType.data('value');
}
axios.get($(e.currentTarget).data('endpoint'), {
params: params
})
.then(({ data }) => {
if (data.message) {
toaster(data.message, 'default', data.actionConfig);
}
});
}
toggleBulkTools(toggle) {
this.$container.find('.list-filter-selector').toggle(!toggle);
this.$container.find('.bulk-buttons-container').toggle(toggle);
this.$container.find('.bulk-buttons-container').find('.selection-count span').html(this.$container.find('.js-bulk-select:checked').length);
}
selectAllRows() {
if (this.$container.find('.js-bulk-select').length === 0) {
this.$selectAllCheckbox.prop('checked', false);
return;
}
this.$container.find('.js-bulk-select').prop('checked', this.$selectAllCheckbox.is(':checked'));
this.toggleBulkTools(this.$selectAllCheckbox.is(':checked'));
}
selectRow() {
let checked = 0;
for (let checkbox of this.$container.find('.js-bulk-select')) {
if ($(checkbox).is(':checked')) {
checked++;
}
}
this.$selectAllCheckbox.prop('checked', checked === this.$container.find('.js-bulk-select').length);
this.toggleBulkTools(checked > 0);
}
getSelectedItems() {
let ids = [];
for (let checkbox of this.$container.find('.js-bulk-select')) {
if ($(checkbox).is(':checked')) {
ids.push($(checkbox).val());
}
}
return ids;
}
getFormData() {
const ids = this.getSelectedItems();
const formData = new FormData();
for (let i = 0; i < ids.length; i++) {
formData.set(`ids[${i}]`, ids[i]);
}
return formData;
}
bulkGeneric(e) {
const self = this;
const $button = $(e.currentTarget);
axios.post($button.data('endpoint'), self.getFormData())
.then(({ data }) => {
if (data.message) {
toaster(data.message, 'default', data.actionConfig);
}
if ($button.data('refresh')) {
self.dataViewer.filterData();
self.toggleBulkTools(false);
const $drawer = $('.drawer-frame');
if ($drawer.length) {
$drawer.find('.js-drawer-close').click();
}
}
});
}
bulkMailto(e) {
const $button = $(e.currentTarget);
axios.post($button.data('endpoint'), this.getFormData())
.then(({ data }) => {
if (data.emails.length) {
location.href = 'mailto:' + encodeURIComponent(data.emails.join(', '));
}
});
}
bulkFollow(e) {
const $button = $(e.currentTarget);
const ids = this.getSelectedItems();
axios.post($button.data('endpoint'), this.getFormData())
.then(({ data }) => {
if (data.message) {
toaster(data.message, 'default', data.actionConfig);
} else if (data.error && data.error.message) {
toaster(data.error.message, 'error', data.actionConfig);
}
for (let id of ids) {
const $datagridRow = this.$container.find(`.js-entity-drawer[data-id="${id}"]`);
if ($datagridRow.length) {
$datagridRow.data('is-follow', !$datagridRow.data('is-follow'));
$datagridRow.find('.js-follow-item').prop('checked', !$datagridRow.find('.js-follow-item').prop('checked'));
const $drawer = $(`.drawer-frame[data-id="${$datagridRow.data('id')}"]`);
if ($drawer.length) {
$drawer.find('.js-follow-item>i')
.toggleClass('ledger-icon-star-outline')
.toggleClass('ledger-icon-star');
}
}
}
$button.trigger('item:bulk-follow');
});
}
bulkComplete(e) {
const $button = $(e.currentTarget);
const ids = this.getSelectedItems();
axios.post($button.data('endpoint'), this.getFormData())
.then(({ data }) => {
if (data.message) {
toaster(data.message, 'default', data.actionConfig);
} else if (data.error && data.error.message) {
toaster(data.error.message, 'error', data.actionConfig);
}
for (let id of ids) {
const $datagridRowLeft = this.$container.find(`.js-entity-drawer[data-id="${id}"]`);
if ($datagridRowLeft.length) {
$datagridRowLeft.data('is-complete', !$datagridRowLeft.data('is-complete'));
$datagridRowLeft.find('.task-check-box-container').toggleClass('is-completed');
$datagridRowLeft.find('.js-complete-item').prop('checked', !$datagridRowLeft.find('.js-complete-item').prop('checked'));
$datagridRowLeft.find('.table-cell-name').toggleClass('is-completed');
const $datagridRowRight = $datagridRowLeft.closest('.list-page-table').find('.datagrid-body-container .datagrid-right-table-block .datagrid-table-row').eq($datagridRowLeft.index());
$datagridRowRight.find('.table-cell-name').toggleClass('is-completed');
const $drawer = $(`.drawer-frame[data-id="${$datagridRowLeft.data('id')}"]`);
if ($drawer.length) {
$drawer.toggleClass('is-complete');
$drawer.find('.task-check-box-container').toggleClass('is-completed');
$drawer.find('.js-complete-item').prop('checked', !$drawer.find('.js-complete-item').prop('checked'));
}
}
}
$button.trigger('item:bulk-complete');
});
}
bulkUnread(e) {
const $button = $(e.currentTarget);
const ids = this.getSelectedItems();
axios.post($button.data('endpoint'), this.getFormData())
.then(({ data }) => {
if (data.message) {
toaster(data.message, 'default', data.actionConfig);
} else if (data.error && data.error.message) {
toaster(data.error.message, 'error', data.actionConfig);
}
for (let id of ids) {
const $datagridRow = this.$container.find(`.js-entity-drawer[data-id="${id}"]`);
if ($datagridRow.length) {
$datagridRow.toggleClass('is-unread');
}
}
$button.trigger('item:bulk-unread');
});
}
bulkEditDrawer(e, drawer) {
const self = this;
$(drawer).on('drawer:hidden', (e, data) => {
$(drawer).off('drawer:hidden');
if (data.total) {
self.dataViewer.filterData();
self.toggleBulkTools(false);
}
});
}
bulkDeleteModal(e, modal) {
const self = this;
const $button = $(e.currentTarget);
const ids = self.getSelectedItems();
$(modal).on('modal:hidden', (e, data) => {
$(modal).off('modal:hidden');
if (data.total) {
self.dataViewer.filterData();
self.toggleBulkTools(false);
const $drawer = $('.drawer-frame');
if ($drawer.length) {
$drawer.find('.js-drawer-close').click();
}
}
if ($button.data('trigger')) {
$button.trigger($button.data('trigger'), { ids: ids });
}
});
}
}