@adaptabletools/adaptable
Version:
Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
281 lines (280 loc) • 14.8 kB
JavaScript
import * as FlashingCellRedux from '../Redux/ActionsReducers/FlashingCellRedux';
import * as ModuleConstants from '../Utilities/Constants/ModuleConstants';
import { ArrayExtensions } from '../Utilities/Extensions/ArrayExtensions';
import { AdaptableModuleBase } from './AdaptableModuleBase';
import { isReactiveQuery } from '../AdaptableState/Common/AdaptableQuery';
import { getFlashingTargetViewItems } from './Utilities/FlashingCell/getFlashingTargetViewItems';
import { getFlashingCellStyleViewItems } from './Utilities/FlashingCell/getFlashingCellStyleViewItems';
import { getObjectTagsViewItems } from '../Utilities/getObjectTagsViewItems';
import { getRuleViewItems } from '../Utilities/getRuleViewItems';
import { getScopeViewItems } from '../Utilities/getScopeViewItems';
import { FlashingCellWizard } from '../View/FlashingCell/Wizard/FlashingCellWizard';
import { getFlashingCellDurationViewItems } from './Utilities/FlashingCell/getFlashingCellDurationViewItems';
import { errorOnce } from '../agGrid/AdaptableLogger';
export class FlashingCellModule extends AdaptableModuleBase {
constructor(api) {
super(ModuleConstants.FlashingCellModuleId, ModuleConstants.FlashingCellFriendlyName, 'lightning', 'FlashingAlert', 'Flash cells when they change', api);
this.isListeningToCellDataChanges = false;
}
onAdaptableReady() {
this.checkListenToCellDataChanged();
}
shouldListenToDataChanges() {
return ArrayExtensions.IsNotNullOrEmpty(this.api.flashingCellApi.getActiveFlashingCellDefinitions());
}
checkListenToCellDataChanged() {
if (!this.isListeningToCellDataChanges) {
if (this.shouldListenToDataChanges()) {
this.setupCellDataChangeListener();
this.isListeningToCellDataChanges = true;
}
}
}
setupCellDataChangeListener() {
this.api.internalApi
.getDataService()
.on('CellDataChanged', (cellDataChangedInfo) => {
if (cellDataChangedInfo.trigger === 'undo') {
// do NOT handle reverted changes
return;
}
const activeFlashingCellDefinitions = this.api.flashingCellApi.getActiveFlashingCellDefinitions();
if (ArrayExtensions.IsNotNullOrEmpty(activeFlashingCellDefinitions)) {
// TODO: think about flashing row value only
// - flash for the underlying small data changes, but the visual does not
if (this.api.optionsApi.getAlertOptions().dataChangeDetectionPolicy === 'formattedValue') {
const { oldValue, newValue, rowNode } = cellDataChangedInfo;
const columnId = cellDataChangedInfo.column.columnId;
const oldFormattedValue = this.api.gridApi.getDisplayValueFromRawValue(rowNode, columnId, oldValue);
const newFormattedValue = this.api.gridApi.getDisplayValueFromRawValue(rowNode, columnId, newValue);
if (oldFormattedValue === newFormattedValue) {
// if the formattedValues are identical, then no alert is fired
return;
}
}
const relevantFlashingCellDefinitions = this.getFlashingCellDefinitionsForDataChange(activeFlashingCellDefinitions, cellDataChangedInfo);
if (ArrayExtensions.IsNotNullOrEmpty(relevantFlashingCellDefinitions)) {
this.showFlashingCellsForDefinitions(cellDataChangedInfo, relevantFlashingCellDefinitions);
}
}
});
}
getFlashingCellDefinitionsForDataChange(activeFlashingCellDefinitions, cellDataChangedInfo) {
const defaultNoPredicateReturn = false;
let relatedFlashingCellsDefinitions = activeFlashingCellDefinitions
.filter((v) => this.api.columnScopeApi.isColumnInScope(cellDataChangedInfo.column, v.Scope))
.filter((flashingCellDefinition) => !isReactiveQuery(flashingCellDefinition.Rule));
let triggeredFlashingCells = [];
if (ArrayExtensions.IsNotNullOrEmpty(relatedFlashingCellsDefinitions)) {
relatedFlashingCellsDefinitions.forEach((flashingCellDefinition) => {
// for aggregation the data-change needs to be aggChange
if (cellDataChangedInfo.trigger !== 'aggChange' &&
this.isFlashingTargetOnlyAggChange(flashingCellDefinition)) {
return;
}
if (flashingCellDefinition.Rule.BooleanExpression) {
let expression = flashingCellDefinition.Rule.BooleanExpression;
let rowNode = cellDataChangedInfo.rowNode;
if (!rowNode) {
rowNode = this.api.gridApi.getRowNodeForPrimaryKey(cellDataChangedInfo.primaryKeyValue);
}
const isValidExpression = this.api.expressionApi.isValidBooleanExpression(expression, this.moduleInfo.ModuleName, `Invalid Alert boolean expression '${expression}'`);
let isSatisfiedExpression = false;
try {
isSatisfiedExpression =
isValidExpression &&
this.api.internalApi
.getQueryLanguageService()
.evaluateBooleanExpression(expression, this.moduleInfo.ModuleName, rowNode, cellDataChangedInfo);
}
catch (error) {
isSatisfiedExpression = false;
errorOnce(error.message);
}
if (isSatisfiedExpression) {
triggeredFlashingCells.push(flashingCellDefinition);
}
}
else if (this.isAlertPredicateTriggered(flashingCellDefinition, cellDataChangedInfo, defaultNoPredicateReturn)) {
triggeredFlashingCells.push(flashingCellDefinition);
}
});
}
return triggeredFlashingCells;
}
showFlashingCellsForDefinitions(cellDataChangedInfo, flashingCellDefinitions = []) {
const allColumnIds = this.api.columnApi.getUIAvailableColumns().map((c) => c.columnId);
const columnDataType = cellDataChangedInfo.column.dataType;
const numeric = columnDataType === 'number';
const isComparableType = numeric || columnDataType === 'date';
let up = false;
let down = false;
let direction = 'neutral';
if (isComparableType) {
const newValue = numeric
? Number(cellDataChangedInfo.newValue)
: cellDataChangedInfo.newValue;
const oldValue = numeric
? Number(cellDataChangedInfo.oldValue)
: cellDataChangedInfo.oldValue;
up = newValue > oldValue;
down = newValue < oldValue;
direction = up ? 'up' : down ? 'down' : 'neutral';
}
flashingCellDefinitions.forEach((flashingCellDefinition) => {
let flashTarget = this.api.flashingCellApi.getFlashingCellFlashTarget(flashingCellDefinition);
const flashColumnIds = { [cellDataChangedInfo.column.columnId]: true };
/**
* When flashing a row, all columns in that row are marked to flash.
*/
if (flashTarget === 'row' || flashTarget?.includes?.('row')) {
allColumnIds.forEach((colId) => {
flashColumnIds[colId] = true;
});
}
const rowPrimaryKey = cellDataChangedInfo.primaryKeyValue;
this.api.flashingCellApi.showFlashingCell({
rowPrimaryKey,
flashingCellDefinition: flashingCellDefinition,
cellDataChangedInfo: cellDataChangedInfo,
direction,
flashTarget,
flashColumnIds,
});
});
}
getModuleAdaptableObjects(config) {
return this.api.flashingCellApi.getFlashingCellDefinitions(config);
}
getExplicitlyReferencedColumnIds(alertDefinition) {
const queryExpression = this.api.expressionApi.getAdaptableQueryExpression(alertDefinition.Rule);
if (queryExpression) {
return this.api.expressionApi.getColumnsFromExpression(queryExpression);
}
else if (this.api.columnScopeApi.scopeHasColumns(alertDefinition.Scope)) {
return this.api.columnScopeApi
.getColumnsInScope(alertDefinition.Scope)
.map((adaptableColumn) => adaptableColumn.columnId);
}
return [];
}
getReferencedNamedQueryNames(alertDefinition) {
const queryExpression = this.api.expressionApi.getAdaptableQueryExpression(alertDefinition.Rule);
if (!queryExpression) {
return [];
}
return this.api.namedQueryApi.internalApi.getReferencedNamedQueryNames(queryExpression);
}
createColumnMenuItems(column) {
if (column && this.isModuleEditable()) {
if (!column.isCalculatedColumn && !column.isTreeColumn) {
const flashingCellDefinitions = this.api.flashingCellApi.getFlashingCellDefinitions();
const flashingCellForCurrentColumn = flashingCellDefinitions.find((flashingCellDefinition) => {
return this.api.columnScopeApi.isColumnInScope(column, flashingCellDefinition.Scope);
});
if (flashingCellForCurrentColumn) {
return [
this.createMenuItemReduxAction('flashing-cell-delete', 'Delete Flashing Cell', this.moduleInfo.Glyph, FlashingCellRedux.FlashingCellDefinitionDelete(flashingCellForCurrentColumn)),
];
}
return [
this.createMenuItemReduxAction('flashing-cell-add', 'Add Flashing Cell', this.moduleInfo.Glyph, FlashingCellRedux.FlashingCellDefinitionAdd({
Scope: {
ColumnIds: [column.columnId],
},
Rule: {
Predicates: [{ PredicateId: 'AnyChange' }],
},
})),
];
}
}
}
createContextMenuItems(menuContext) {
const items = [];
if (!menuContext.isRowGroupColumn && this.isModuleAvailable()) {
if (menuContext.adaptableColumn && menuContext.rowNode) {
const flashingCellForRow = this.api.flashingCellApi.internalApi.getAdaptableFlashingCellFor(menuContext.primaryKeyValue);
const flashingCellForCell = this.api.flashingCellApi.internalApi.getAdaptableFlashingCellFor(menuContext.primaryKeyValue, menuContext.adaptableColumn.columnId);
if (flashingCellForRow) {
if (flashingCellForRow.flashTarget === 'row' ||
(Array.isArray(flashingCellForRow?.flashTarget) &&
flashingCellForRow.flashTarget.includes('row'))) {
items.push(this.createMenuItemClickFunction('flashing-row-clear', 'Clear Flashing Row', this.moduleInfo.Glyph, () => this.api.internalApi
.getFlashingCellService()
.clearGridCellFlash(flashingCellForRow)));
}
}
else if (flashingCellForCell && flashingCellForCell.flashTarget === 'cell') {
items.push(this.createMenuItemClickFunction('flashing-cell-clear', 'Clear Flashing Cell', this.moduleInfo.Glyph, () => this.api.internalApi
.getFlashingCellService()
.clearGridCellFlash(flashingCellForCell)));
}
}
}
return items;
}
isFlashingTargetOnlyAggChange(flashingCellDefinition) {
if (flashingCellDefinition.FlashTarget === 'aggFuncCell') {
return true;
}
return (Array.isArray(flashingCellDefinition.FlashTarget) &&
flashingCellDefinition.FlashTarget.length === 1 &&
flashingCellDefinition.FlashTarget.includes('aggFuncCell'));
}
isAlertPredicateTriggered(alert, dataChangedEvent, defaultNoPredicateReturn = false) {
const predicateDefHandlerContext = {
value: dataChangedEvent.newValue,
oldValue: dataChangedEvent.oldValue,
// TODO send real display value
displayValue: null,
node: dataChangedEvent.rowNode,
column: dataChangedEvent.column,
...this.api.internalApi.buildBaseContext(),
};
return this.api.predicateApi.handleColumnPredicates(alert.Rule?.Predicates, predicateDefHandlerContext, defaultNoPredicateReturn);
}
getTeamSharingAction() {
return {
ModuleEntities: this.api.flashingCellApi.getFlashingCellDefinitions(),
AddAction: FlashingCellRedux.FlashingCellDefinitionAdd,
EditAction: FlashingCellRedux.FlashingCellDefinitionEdit,
};
}
toView(flashingCell) {
return {
items: [
{ ...getScopeViewItems(flashingCell.Scope, this.api), label: 'Trigger' },
{
...getRuleViewItems(flashingCell.Rule, this.api),
label: 'Rule',
name: 'Rule',
},
getFlashingCellDurationViewItems(flashingCell),
getFlashingTargetViewItems(flashingCell),
getFlashingCellStyleViewItems(),
getObjectTagsViewItems(flashingCell, this.api),
],
abObject: flashingCell,
};
}
toViewAll() {
return this.getModuleAdaptableObjects({
includeLayoutNotAssociatedObjects: this.showLayoutNotAssociatedObjects(),
}).map((flashingCell) => this.toView(flashingCell));
}
getViewProperties() {
return {
getEditAction: FlashingCellRedux.FlashingCellDefinitionEdit,
getSuspendAction: FlashingCellRedux.FlashingCellDefinitionSuspend,
getUnSuspendAction: FlashingCellRedux.FlashingCellDefinitionUnSuspend,
getSuspendAllAction: FlashingCellRedux.FlashingCellDefinitionSuspendAll,
getUnSuspendAllAction: FlashingCellRedux.FlashingCellDefinitionUnSuspendAll,
getDeleteAction: FlashingCellRedux.FlashingCellDefinitionDelete,
getEditWizard: () => FlashingCellWizard,
};
}
canBeAssociatedWithLayouts() {
return true;
}
}