devexpress-reporting
Version:
DevExpress Reporting provides the capability to develop a reporting application to create and customize reports.
433 lines (432 loc) • 20.3 kB
JavaScript
/**
* DevExpress HTML/JS Reporting (viewer\ai\_aiPanelModel.js)
* Version: 24.2.7
* Build date: Apr 29, 2025
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* License: https://www.devexpress.com/Support/EULAs/universal.xml
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import themes from 'devextreme/ui/themes';
import { BaseRenderingModel, mutable } from '@devexpress/analytics-core/analytics-serializer-native';
import { $dx, DxDeferred, formatUnicorn } from '@devexpress/analytics-core/analytics-internal-native';
import { getLocalization, TabInfo } from '@devexpress/analytics-core/analytics-utils-native';
import { getParentContainer } from '@devexpress/analytics-core/analytics-internal-native';
import { AIServicesEnabled, AILanguages } from '../settings';
import { createViewModel, TextSourceTarget } from './_aiPanel.viewmodel';
export class AIPanelViewModel extends BaseRenderingModel {
constructor(reportPreview) {
super();
this.reportPreview = reportPreview;
this.aiProcessingRequestDeferred = new DxDeferred();
this.ignore = false;
this.languages = AILanguages();
this.TextSourceTargetText = {
Document: getLocalization('Entire Document', 'ASPxReportsStringId.AIPanel_TargetTypeDocument'),
Page: getLocalization('Page', 'ASPxReportsStringId.AIPanel_TargetType_Page'),
Selection: getLocalization('Selection', 'ASPxReportsStringId.AIPanel_TargetTypeSelection')
};
this.InvalidLanguageErrorMsg = getLocalization('Select a language', 'ASPxReportsStringId.AIPanel_InvalidLanguageValueErrorMsg');
this.SummarizeActionText = getLocalization('Summarize', 'ASPxReportsStringId.AIPanel_SummarizeAction');
this.TranslateActionText = getLocalization('Translate', 'ASPxReportsStringId.AIPanel_TranslateAction');
this.warningMsg = 'Text is too long to process within a single request. Text will be split into sections and processed in {0} steps. Proceed?';
this.targets = [
{ key: TextSourceTarget.Document, text: this.TextSourceTargetText.Document },
{ key: TextSourceTarget.Page, text: this.TextSourceTargetText.Page },
{ key: TextSourceTarget.Selection, text: this.TextSourceTargetText.Selection }
];
const getDisabled = () => reportPreview.documentBuilding || !reportPreview.documentId || reportPreview.pageIndex === -1;
const tabInfoVisible = () => !getDisabled() && AIServicesEnabled();
this.tabInfo = new TabInfo({
text: getLocalization('AI Operations', 'ASPxReportsStringId.AIPanel_PanelHeaderLabel'),
template: 'dxrd-preview-ai-panel',
model: this,
localizationId: 'ASPxReportsStringId.AIPanel_PanelHeaderLabel',
imageClassName: 'aitab',
imageTemplateName: 'dxrd-svg-tabs-aitab',
visible: tabInfoVisible()
});
const summarizeMenuItem = {
text: this.SummarizeActionText,
key: 'dx_summarize_text'
};
const that = this;
this.smartTagAIMenu = {
dataSource: [{
text: '',
icon: 'dxrd-svg-menu-aimenu',
items: [
summarizeMenuItem,
{
text: this.TranslateActionText,
items: AILanguages()
}
],
}],
hideSubmenuOnMouseLeave: false,
onItemClick(e) {
e.event?.stopPropagation();
if (!e.itemData || !e.itemData['key'] || that.loading)
return;
that.selectedTarget = TextSourceTarget.Selection;
that.expandAITabPanel();
if (e.itemData['key'] === 'dx_summarize_text') {
that.onSummarizeClick();
}
else if (e.itemData['key']) {
that.selectedLanguage = e.itemData['key'];
that.onTranslateClick();
}
},
onSubmenuShowing: (e) => {
const popupContainer = getParentContainer(e['element']);
e['submenu']._overlay.option('container', popupContainer);
e['submenu']._overlay.option('focusStateEnabled', false);
e['submenu']._overlay.option('position', { collision: 'none', boundary: popupContainer });
e['submenu']._attachHoverEvents();
},
visible: AIServicesEnabled()
};
window.addEventListener('resize', that.resizeResultArea);
this.addDisposable(() => window.removeEventListener('resize', that.resizeResultArea), reportPreview.events.on('documentIdChanged', (args) => {
this.tabInfo.visible = tabInfoVisible();
this.dispose();
}), reportPreview.events.on('documentBuildingChanged', (args) => {
this.tabInfo.visible = tabInfoVisible();
this.pagesListDataSource = [];
if (!args.newValue && args.oldValue && this.reportPreview.pageIndex !== -1) {
this.reportPreview.pages.forEach(page => {
this.pagesListDataSource.push({ key: page.pageIndex, text: (page.pageIndex + 1).toString() });
});
}
this.dispose();
}), reportPreview.events.on('reportIdChanged', (args) => {
this.dispose();
}), reportPreview.events.on('currentPageChanged', (args) => {
if (args.newValue) {
const currentPage = args.newValue;
currentPage.smartTagAIMenu = this.smartTagAIMenu;
this.currentPageSelectionSubcriber && this.currentPageSelectionSubcriber();
this.currentPageSelectionSubcriber = currentPage.events.on('activeBricksChanged', (args) => {
this.handleSelectedBrickChangedEvent(args.newValue);
});
}
}));
this.addDisposable(AIServicesEnabled.subscribe(() => {
this.tabInfo.visible = tabInfoVisible();
}));
}
expandAITabPanel() {
this.reportPreview.tabPanel?.toggleTabVisibility({ model: this.tabInfo }, true);
}
handleSelectedBrickChangedEvent(selectedBricks) {
if (selectedBricks.length === 0)
return;
let topBrick = selectedBricks[0];
selectedBricks.forEach(brick => {
if (brick.top < topBrick.top)
topBrick = brick;
else if (brick.top == topBrick.top && brick.left < topBrick.left)
topBrick = brick;
});
const topP = topBrick.topP;
this.reportPreview.currentPage.smartTagAIMenuPosition = {
heightP: topBrick.heightP,
topP: topP,
widthP: topBrick.widthP,
leftP: this.reportPreview.rtlViewer ? '0%' : '92.5%',
rightP: this.reportPreview.rtlViewer ? '92.5%' : '0%'
};
}
updateViewModel(args) {
const viewModel = this.getViewModel();
if (args.propertyName === 'aiProcessingResultText') {
viewModel.aiProcessingResultText = this.aiProcessingResultText;
if (this.aiProcessingResultText && this.aiProcessingResultText.length > 0)
viewModel.resultVisible = true;
else
viewModel.resultVisible = false;
this.resizeResultArea();
}
else if (args.propertyName === 'warningTextLimitPopupVisible') {
viewModel.warningTextLimitPopupVisible = this.warningTextLimitPopupVisible;
}
else if (args.propertyName === 'pageSelectBoxVisible' || args.propertyName === 'pagesListDataSource') {
viewModel.pageSelectBoxOptions.dataSource = this.pagesListDataSource;
viewModel.pageSelectBoxOptions.visible = this.pageSelectBoxVisible;
viewModel.pageSelectBoxVisible = this.pageSelectBoxVisible;
}
else if (args.propertyName === 'proceedForAllChunks') {
viewModel.proceedForAllCheckBoxOptions.value = this.proceedForAllChunks;
}
else if (args.propertyName === 'proceedNextButtonClick') {
viewModel.proceedNextButtonClick = this.proceedNextButtonClick;
}
else if (args.propertyName === 'loading') {
if (!this.loading) {
viewModel.buttons.summarizeTextButton.text = this.SummarizeActionText;
viewModel.buttons.translateTextButton.text = this.TranslateActionText;
viewModel.buttons.translateTextButton.disabled = false;
viewModel.buttons.summarizeTextButton.disabled = false;
}
else {
viewModel.buttons.translateTextButton.disabled = true;
viewModel.buttons.summarizeTextButton.disabled = true;
}
}
else if (args.propertyName === 'selectedPage') {
viewModel.pageSelectBoxOptions.value = this.selectedPage;
}
else if (args.propertyName === 'selectedTarget') {
viewModel.targetSelectBoxOptions.value = this.selectedTarget;
}
else if (args.propertyName === 'selectedLanguage') {
viewModel.languageSelectBoxOptions.value = this.selectedLanguage;
}
else if (args.propertyName === 'copyButtonTemplate') {
viewModel.buttons.copyResultButton.template = this.copyButtonTemplate;
}
else if (args.propertyName === 'limitExceededWarningMsg') {
viewModel.limitExceededWarningMsg = this.limitExceededWarningMsg;
}
}
createViewModel() {
return createViewModel.call(this, super.createViewModel());
}
onPropertyChanged(args) {
if (args.propertyName === 'aiProcessingResultText') {
this.resizeResultArea();
}
else if (args.propertyName === 'loading') {
if (!this.loading) {
this.summarizeButtonLoader?.option('visible', false);
this.translateButtonLoader?.option('visible', false);
}
}
}
get translateWholeDocument() {
return this.selectedTarget === TextSourceTarget.Document;
}
_preparetextProcessingButtonTemplate(data, container) {
const buttonIndicatorElement = document.createElement('div');
const buttonContainer = document.createElement('div');
const buttonTextElement = document.createElement('span');
buttonIndicatorElement.style.height = '20px';
buttonIndicatorElement.style.width = '20px';
buttonContainer.className = 'dxrd-ai-button-container';
buttonTextElement.className = 'dxrd-ai-button-text';
buttonTextElement.textContent = data.text;
buttonContainer.appendChild(buttonTextElement);
$dx(container).append(buttonIndicatorElement);
$dx(container).append(buttonContainer);
return buttonIndicatorElement;
}
_getTextContentToProcess() {
let page;
const useSelectedContent = this.selectedTarget === TextSourceTarget.Selection;
if (useSelectedContent)
page = this.reportPreview.currentPage;
else if (!this.translateWholeDocument)
page = this.reportPreview.pages[this.selectedPage];
return this.translateWholeDocument ? '' : (page?.getBricksTextContent(useSelectedContent) ?? '');
}
copyResultClick(e) {
const animateCopy = () => {
this.copyButtonTemplate = 'dxrd-svg-toolbox-checkbox';
setTimeout(() => {
this.copyButtonTemplate = 'dxrd-svg-toolbar-copy';
}, 3000);
};
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(this.aiProcessingResultText).then(() => {
animateCopy();
});
}
else {
const tempTextArea = document.createElement('textarea');
tempTextArea.value = this.aiProcessingResultText;
tempTextArea.style.position = 'absolute';
tempTextArea.style.left = '-9999px';
document.body.appendChild(tempTextArea);
tempTextArea.select();
document.execCommand('copy');
document.body.removeChild(tempTextArea);
animateCopy();
}
}
onSummarizeClick() {
if (this.loading)
return;
this.clearProcessing();
this.loading = true;
const viewModel = this.getViewModel();
if (this.summarizeButtonLoader) {
viewModel.buttons.summarizeTextButton.text = '';
this.summarizeButtonLoader.option('visible', true);
}
const text = this._getTextContentToProcess();
this.summarizeText(text, this.selectedLanguage);
}
onTranslateClick() {
if (this.loading)
return;
if (!this.selectedLanguage) {
this.languageSelectBoxComponent?.option({
validationStatus: 'invalid',
validationErrors: [{ message: this.InvalidLanguageErrorMsg }]
});
return;
}
this.clearProcessing();
this.loading = true;
const viewModel = this.getViewModel();
if (this.translateButtonLoader) {
viewModel.buttons.translateTextButton.text = '';
this.translateButtonLoader.option('visible', true);
}
const text = this._getTextContentToProcess();
this.translateText(text);
}
translateText(text, prevResponse, currentChunkPosition) {
const ignore = true;
this.aiProcessingRequestDeferred = new DxDeferred();
this.reportPreview.requestWrapper.translateTextRequest(text, this.selectedLanguage ?? 'en', this.translateWholeDocument, prevResponse, currentChunkPosition, ignore)
.then(result => this.aiProcessingRequestDeferred.resolve(result))
.catch(fail => this.failRequestHandler(fail, ignore));
this.aiProcessingRequestDeferred.done((result) => {
this._handleTextProcessingResult(result, (prevResultText, nextChunkPosition) => this.translateText(text, prevResultText, nextChunkPosition));
});
}
summarizeText(text, language, prevResponse, currentChunkPosition) {
this.aiProcessingRequestDeferred = new DxDeferred();
this.reportPreview.requestWrapper.summarizeTextRequest(text, this.translateWholeDocument, prevResponse, currentChunkPosition, language, this.ignore)
.then(result => this.aiProcessingRequestDeferred.resolve(result))
.catch(fail => this.failRequestHandler(fail, this.ignore));
this.aiProcessingRequestDeferred.done((result) => {
result.language = language;
this._handleTextProcessingResult(result, (prevResultText, nextChunkPosition) => this.summarizeText(text, language, prevResultText, nextChunkPosition), true);
});
}
_handleTextProcessingResult(result, nextChunkRequestFunc, isSummary = false) {
if (result.success) {
if (!isSummary)
this.aiProcessingResultText += result.text;
if (result.requestAgain) {
let nextChunkPosition = result.chunkIndex;
if (nextChunkPosition === -1 || (result.text && result.text.length))
nextChunkPosition += 1;
if (this.proceedForAllChunks) {
nextChunkRequestFunc(result.text, nextChunkPosition);
}
else {
const remainingChunkRequests = parseInt(result.totalChunks) - nextChunkPosition;
this.limitExceededWarningMsg = formatUnicorn(getLocalization(this.warningMsg, 'ASPxReportsStringId.AIPanel_LimitExceededWarning'), remainingChunkRequests);
this.warningTextLimitPopupVisible = true;
this.proceedNextButtonClick = (e) => {
this.warningTextLimitPopupVisible = false;
nextChunkRequestFunc(result.text, nextChunkPosition);
};
}
}
else {
if (isSummary) {
if (result.language) {
this.reportPreview.requestWrapper.translateTextRequest(result.text, result.language, false, '', 0, this.ignore)
.then(translateResult => {
if (translateResult.success) {
this.aiProcessingResultText = translateResult.text;
this.clearProcessing(false);
}
else
this.failRequestHandler(result.ErrorMessage, false);
})
.catch(fail => {
this.aiProcessingResultText = result.text;
this.failRequestHandler(fail, this.ignore);
});
}
else {
this.aiProcessingResultText = result.text;
this.clearProcessing(false);
}
}
else {
this.clearProcessing(false);
}
}
}
else {
this.failRequestHandler(result.ErrorMessage, false);
}
}
resizeResultArea() {
const aiPanelElement = document.getElementById('dxrd-preview-ai-panel-id');
const resultTextElement = document.getElementById('dxrd-ai-panel-text-area-id');
if (aiPanelElement && resultTextElement) {
const offset = (themes.current()?.startsWith('material') ?? false) ? 245 : 225;
resultTextElement.style.height = (aiPanelElement.clientHeight - offset) + 'px';
}
else {
resultTextElement && (resultTextElement.style.height = '100%');
}
}
failRequestHandler(errorMessage, ignore) {
this.clearProcessing(false);
}
clearProcessing(clearContent = true) {
this.loading = false;
this.prevRespose = '';
if (clearContent)
this.aiProcessingResultText = '';
this.aiProcessingRequestDeferred?.reject();
this.warningTextLimitPopupVisible = false;
this.proceedForAllChunks = false;
this.warningTextLimitPopupVisible = false;
}
disabled() {
return false;
}
dispose() {
this.clearProcessing();
}
}
__decorate([
mutable('')
], AIPanelViewModel.prototype, "aiProcessingResultText", void 0);
__decorate([
mutable('en')
], AIPanelViewModel.prototype, "selectedLanguage", void 0);
__decorate([
mutable('document')
], AIPanelViewModel.prototype, "selectedTarget", void 0);
__decorate([
mutable(0)
], AIPanelViewModel.prototype, "selectedPage", void 0);
__decorate([
mutable(false)
], AIPanelViewModel.prototype, "proceedForAllChunks", void 0);
__decorate([
mutable(false)
], AIPanelViewModel.prototype, "warningTextLimitPopupVisible", void 0);
__decorate([
mutable(false)
], AIPanelViewModel.prototype, "pageSelectBoxVisible", void 0);
__decorate([
mutable()
], AIPanelViewModel.prototype, "proceedNextButtonClick", void 0);
__decorate([
mutable('dxrd-svg-toolbar-copy')
], AIPanelViewModel.prototype, "copyButtonTemplate", void 0);
__decorate([
mutable('')
], AIPanelViewModel.prototype, "limitExceededWarningMsg", void 0);
__decorate([
mutable(false)
], AIPanelViewModel.prototype, "loading", void 0);
__decorate([
mutable([])
], AIPanelViewModel.prototype, "pagesListDataSource", void 0);