devexpress-reporting
Version:
DevExpress Reporting provides the capability to develop a reporting application to create and customize reports.
176 lines (175 loc) • 8.46 kB
JavaScript
/**
* DevExpress HTML/JS Reporting (viewer\widgets\dateRange\dateRangeEditor.viewmodel.js)
* Version: 25.2.3
* Build date: Dec 15, 2025
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* License: https://www.devexpress.com/Support/EULAs/universal.xml
*/
import { createViewModelGenerator } from '@devexpress/analytics-core/analytics-serializer-native';
import { getLocalization, getParentContainer, $dx, KeyboardEnum, guid, $unwrap, calculateWithZoomFactor } from '@devexpress/analytics-core/analytics-internal-native';
import { predefinedDateRanges, predefinedDateRangesModel, predefinedTimeRanges } from './dateRangeEditor.ranges';
import dxButton from 'devextreme/ui/button';
import { getTemplate } from '@devexpress/analytics-core/analytics-utils-native';
const predefinedDateRangesDefaultGroupCount = 4;
const predefinedDateRangesGroupItemsCount = 3;
function getPredefinedTimeRangeItemElement(item, id) {
const element = document.createElement('div');
element.id = id;
element.classList.add(...['dxrv-predefined-timerange', 'dx-accessibility-timerange-item']);
new dxButton(element, {
template: function (data, container) {
const rootElement = $unwrap(container);
rootElement.classList.add('dxrv-predefined-timerange-button');
const icon = document.createElement('span');
icon.classList.add('dxrv-predefined-timerange-button-icon');
icon.innerHTML = getTemplate(item.iconTemplate);
const text = document.createElement('div');
text.classList.add('dxrv-predefined-timerange-text');
text.innerText = data.text;
rootElement.appendChild(icon);
rootElement.appendChild(text);
},
text: item.displayName,
width: '140px',
hint: item.displayName
});
return element;
}
export function createPredefinedGroupedItemsViewModel(editor) {
const items = (editor.isTimeOnlyType ? predefinedTimeRanges : predefinedDateRanges).map(x => {
const timeRangeItemId = `dxrv-timerange-${x.displayName.replace(/\s+/g, '')}-${guid()}`;
return createViewModelGenerator({
...x,
click: () => editor.applyDate(x.range(), true),
template: editor.isTimeOnlyType ? getPredefinedTimeRangeItemElement(x, timeRangeItemId) : undefined,
id: timeRangeItemId,
ratio: 1,
})
.generateProperty('selected', editor._isSelected(x))
.getViewModel();
});
const itemsInGroup = 3;
const groupedItems = [];
for (let i = 0; i < items.length; i += itemsInGroup) {
const group = items.slice(i, i + itemsInGroup);
if (group.length == 2)
group[1].ratio = 2;
groupedItems.push(group);
}
return groupedItems;
}
export function createDateRangeEditorViewModel(baseViewModel) {
let popupSettings = null;
const inRange = (date) => {
const _end = new Date(viewModel.endRange.value.getTime());
const _start = new Date(viewModel.startRange.value.getTime());
return date <= new Date(_end.setHours(0, 0, 0, 0)) &&
date >= new Date(_start.setHours(0, 0, 0, 0));
};
const timeRangeItemEnterEvent = (e, item, dialogKeyboardHelper) => {
if (e.key == KeyboardEnum.Enter || e.key == KeyboardEnum.Space) {
item.itemData.click();
dialogKeyboardHelper.changeSelections(item.itemData);
}
else if (e.key == KeyboardEnum.Esc) {
dialogKeyboardHelper?.itemHandleEscKey(e);
}
};
const createPredefinedItemsViewModel = () => (this.isTimeOnlyType ? predefinedTimeRanges : predefinedDateRanges).map(x => {
return createViewModelGenerator({
...x,
click: () => this.applyDate(x.range(), true)
})
.generateProperty('selected', this._isSelected(x))
.getViewModel();
});
const viewModel = createViewModelGenerator({
...baseViewModel,
showPopup: () => this._showPopup(),
popupTemplate: this.popupTemplate,
items: this.items,
cacheElement: ($element) => this._$element = $dx($element),
scrollViewOptions: {
showScrollbar: 'always',
direction: 'horizontal',
useNative: false,
onInitialized: function (e) { e.component.option('useKeyboard', false); }
},
_editorInputId: this._editorInputId,
_displayName: this._displayName
})
.generateProperty('getPopupSettings', (isTimeOnly) => {
if (!popupSettings) {
const timeOnlyRangePopupMaxHeight = 260 + 35 * (Math.ceil(predefinedTimeRanges.length / predefinedDateRangesGroupItemsCount) - predefinedDateRangesDefaultGroupCount);
popupSettings = createViewModelGenerator({
width: isTimeOnly ? '490px' : 'max-content',
maxHeight: isTimeOnly ? `${timeOnlyRangePopupMaxHeight}px` : '350px',
height: calculateWithZoomFactor(362),
wrapperAttr: { class: 'dxrv-daterange-editor-popup-wrapper' },
elementAttr: { class: isTimeOnly ? 'dx-accessibility-timerange-item' : '' },
position: { my: 'right top', at: 'right bottom', of: this._$element[0], collision: 'fit fit', offset: '1 -1' },
container: getParentContainer(this._$element[0]),
showTitle: false,
showCloseButton: false,
hideOnOutsideClick: true,
onHidden: (e) => this._popupVisible = false,
animation: {},
shading: false,
})
.generateProperty('visible', this._popupVisible)
.getViewModel();
}
return popupSettings;
})
.generateProperty('onRangeItemsRendered', (item) => {
$unwrap(item.itemElement)?.addEventListener('keydown', (e) => timeRangeItemEnterEvent(e, item, this.dialogKeyboardHelper));
})
.generateProperty('onRangeItemClick', (item) => {
item.itemData.click();
this.dialogKeyboardHelper?.changeSelections(item.itemData);
})
.generateProperty('dialogKeyboardHelper', this.dialogKeyboardHelper)
.generateProperty('predefinedDateRanges', createViewModelGenerator({
attr: { 'aria-label': getLocalization('Predefined periods', 'ASPxReportsStringId.WebDocumentViewer_AriaLabelPredefinedPeriods') },
accessibilityKeyboardHelper: !this.isTimeOnlyType ? this.dialogKeyboardHelper.predefinedDateRangesKeyboardHelper : undefined,
scrollViewOptions: {
showScrollbar: 'onHover',
scrollByContent: false,
bounceEnabled: false,
useNative: false,
scrollByThumb: true,
onInitialized: function (e) { e.component.option('useKeyboard', false); }
}
})
.generateProperty('items', createPredefinedItemsViewModel())
.generateProperty('groupedItems', createPredefinedGroupedItemsViewModel(this))
.getViewModel())
.generateProperty('startRange', createViewModelGenerator({
height: this.calendarHeight,
inRange: (date) => inRange(date),
onValueChanged: (e) => this.startDate = e.value
})
.generateProperty('value', this.startDate)
.getViewModel())
.generateProperty('endRange', createViewModelGenerator({
height: this.calendarHeight,
inRange: (date) => inRange(date),
onValueChanged: (e) => this.endDate = e.value
})
.generateProperty('value', this.endDate)
.generateProperty('min', this.startDate)
.getViewModel())
.generateProperty('disabled', this.disabled)
.generateProperty('visible', this.visible)
.generateProperty('displayValue', this._displayText)
.generateProperty('type', this.type)
.getViewModel();
this.addDisposable(predefinedDateRangesModel.events.on('dateRangesChanged', (e) => {
viewModel.predefinedDateRanges.items = createPredefinedItemsViewModel();
}));
this.addDisposable(predefinedDateRangesModel.events.on('timeRangesChanged', (e) => {
viewModel.predefinedDateRanges.groupedItems = createPredefinedGroupedItemsViewModel(this);
}));
viewModel.popupModel = this['_options'].isMobile ? this.popupModel.getViewModel() : viewModel;
return viewModel;
}