UNPKG

@adaptabletools/adaptable

Version:

Powerful AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

321 lines (320 loc) 14.3 kB
import * as PlusMinusRedux from '../Redux/ActionsReducers/PlusMinusRedux'; import * as ModuleConstants from '../Utilities/Constants/ModuleConstants'; import { ArrayExtensions } from '../Utilities/Extensions/ArrayExtensions'; import { Helper } from '../Utilities/Helpers/Helper'; import { AdaptableModuleBase } from './AdaptableModuleBase'; import { PlusMinusModuleId } from '../Utilities/Constants/ModuleConstants'; import { getObjectTagsViewItems } from '../Utilities/getObjectTagsViewItems'; import { getRuleViewItems } from '../Utilities/getRuleViewItems'; import { getScopeViewItems } from '../Utilities/getScopeViewItems'; import { MovePlusMinus } from '../View/PlusMinus/MovePlusMinus'; import { PlusMinusWizard } from '../View/PlusMinus/Wizard/PlusMinusWizard'; import { errorOnce } from '../agGrid/AdaptableLogger'; import NumberExtensions from '../Utilities/Extensions/NumberExtensions'; import { buildPlusMinusTriggerKeyLookup, plusMinusEventMatchesTrigger, plusMinusKeyCouldTrigger, resolvePlusMinusTriggerKeysForNudge, } from '../Utilities/Helpers/PlusMinusHelper'; export class PlusMinusModule extends AdaptableModuleBase { shouldHandleKeyDown; adaptable; keyDownUnsubscribe; plusMinusTriggerLookup; constructor(api) { super(ModuleConstants.PlusMinusModuleId, ModuleConstants.PlusMinusFriendlyName, 'add-circle', 'PlusMinusPopup', 'Configure how a cell value will edit when increment / decrement keys are pressed (defaults + and −; per-rule keys in each nudge, with optional plusMinusOptions fallback)', api); this.shouldHandleKeyDown = false; this.adaptable = api.internalApi.getAdaptableInstance(); } isModuleAvailable() { return super.isModuleAvailable() && this.api.gridApi.isGridRangeSelectable(); } getModuleUnavailableMessage() { const baseMessage = super.getModuleUnavailableMessage(); if (baseMessage) { return baseMessage; } if (!this.api.gridApi.isGridRangeSelectable()) { return 'grid range selection is not enabled - set the "cellSelection" gridOption to enable it'; } return undefined; } getModuleAdaptableObjects(config) { return this.api.plusMinusApi.getAllPlusMinus(config); } getExplicitlyReferencedColumnIds(plusMinusNudge) { if (this.api.columnScopeApi.scopeHasColumns(plusMinusNudge.Scope)) { return this.api.columnScopeApi .getColumnsInScope(plusMinusNudge.Scope) .map((adaptableColumn) => adaptableColumn.columnId); } return []; } getReferencedNamedQueryNames(plusMinusNudge) { if (!plusMinusNudge.Rule?.BooleanExpression) { return []; } return this.api.namedQueryApi.internalApi.getReferencedNamedQueryNames(plusMinusNudge.Rule?.BooleanExpression); } onAdaptableReady() { this.checkListenToKeyDown(); if (ArrayExtensions.IsNotNullOrEmpty(this.api.plusMinusApi.getAllPlusMinus()) && !this.api.gridApi.isGridRangeSelectable()) { this.api.consoleWarn('Plus/Minus module is not available because AG Grid is not selectable'); } } setupKeyDownListener() { if (this.keyDownUnsubscribe) { return; } this.keyDownUnsubscribe = this.adaptable._on('KeyDown', (keyDownEvent) => { this.handleKeyDown(keyDownEvent); }); } teardownKeyDownListener() { if (this.keyDownUnsubscribe) { this.keyDownUnsubscribe(); this.keyDownUnsubscribe = undefined; } } checkListenToKeyDown() { const moduleAvailable = this.isModuleAvailable(); const activeNudges = this.api.plusMinusApi.getAllActivePlusMinus(); const hasActiveNudges = ArrayExtensions.IsNotNullOrEmpty(activeNudges); const newShouldHandleKeyDown = moduleAvailable && hasActiveNudges; if (newShouldHandleKeyDown) { const pmOpts = this.api.optionsApi.getEditOptions().plusMinusOptions ?? {}; this.plusMinusTriggerLookup = buildPlusMinusTriggerKeyLookup(activeNudges, pmOpts); this.setupKeyDownListener(); } else { this.plusMinusTriggerLookup = undefined; this.teardownKeyDownListener(); } this.shouldHandleKeyDown = newShouldHandleKeyDown; } createColumnMenuItems(column) { if (column && this.isModuleEditable() && column.dataType == 'number') { let popupParam = { column: column, action: 'New', source: 'ColumnMenu', }; return [ this.createMenuItemShowPopup('plus-minus-add', 'Create Plus/Minus Rule', this.moduleInfo.Popup, this.moduleInfo.Glyph, popupParam), ]; } } handleKeyDown(keyEvent) { if (!this.shouldHandleKeyDown || !this.plusMinusTriggerLookup) { return; } const keyEventString = Helper.getStringRepresentionFromKey(keyEvent); if (!plusMinusKeyCouldTrigger(keyEventString, this.plusMinusTriggerLookup)) { return; } const pmOpts = this.api.optionsApi.getEditOptions().plusMinusOptions ?? {}; const allActive = this.api.plusMinusApi.getAllActivePlusMinus(); if (!ArrayExtensions.IsNotNullOrEmpty(allActive)) { return; } const decrementMatches = allActive.filter((nudge) => { const { decrementKey } = resolvePlusMinusTriggerKeysForNudge(nudge, pmOpts); return plusMinusEventMatchesTrigger(keyEvent, decrementKey); }); const incrementMatches = allActive.filter((nudge) => { const { incrementKey } = resolvePlusMinusTriggerKeysForNudge(nudge, pmOpts); return plusMinusEventMatchesTrigger(keyEvent, incrementKey); }); let side; let plusMinusNudges; if (decrementMatches.length) { side = 'down'; plusMinusNudges = decrementMatches; } else if (incrementMatches.length) { side = 'up'; plusMinusNudges = incrementMatches; } else { return; } const isPlusMinusApplicable = this.applyPlusMinus(plusMinusNudges, this.api.gridApi.getSelectedCellInfo().gridCells, side); if (isPlusMinusApplicable) { keyEvent.preventDefault(); keyEvent.stopImmediatePropagation?.(); keyEvent.stopPropagation?.(); } } applyPlusMinus(plusMinusNudges, cellsToUpdate, direction) { let shouldApplyPlusMinus = false; let successfulValues = []; let failedPreventEdits = []; let side = direction == 'up' ? 1 : -1; cellsToUpdate.forEach((gridCell) => { if (gridCell.column && gridCell.column.dataType == 'number' && this.api.gridApi.isCellEditable(gridCell)) { let foundRule; let replacementGridCell; plusMinusNudges.forEach((pmr) => { if (!foundRule) { if (this.api.columnScopeApi.isColumnInScope(gridCell.column, pmr.Scope)) { if (gridCell.rawValue == null || gridCell.rawValue == 'null' || gridCell.rawValue == undefined || gridCell.rawValue == 'undefined') { return; } if (typeof gridCell.rawValue != 'number') { gridCell.rawValue = parseFloat(gridCell.rawValue); } const isSatisfiedExpression = this.isPlusMinusNudgeApplied(pmr, gridCell); if (isSatisfiedExpression) { foundRule = pmr; shouldApplyPlusMinus = true; let computedValue = gridCell.rawValue + pmr.NudgeValue * side; computedValue = NumberExtensions.avoidJavascriptPrecisionIssues(computedValue); replacementGridCell = { primaryKeyValue: gridCell.primaryKeyValue, column: gridCell.column, rawValue: computedValue, displayValue: computedValue, normalisedValue: computedValue, rowNode: gridCell.rowNode, isPivotCell: false, isRowGroupCell: false, }; } } } }); if (foundRule && replacementGridCell) { const dataChangedEvent = this.api.internalApi.buildCellDataChangedInfo({ oldValue: Number(gridCell.rawValue), newValue: replacementGridCell.rawValue, column: gridCell.column, primaryKeyValue: gridCell.primaryKeyValue, rowNode: gridCell.rowNode, trigger: 'edit', }); let validationRules = this.api.internalApi .getValidationService() .getValidationRulesForDataChange(dataChangedEvent); if (validationRules.length > 0) { failedPreventEdits.push(validationRules[0]); } else { successfulValues.push(replacementGridCell); } } } }); this.ShowErrorPreventMessage(failedPreventEdits); if (ArrayExtensions.IsNotNullOrEmpty(successfulValues)) { const cellUpdateRequests = []; successfulValues.forEach((gridCell) => { cellUpdateRequests.push({ columnId: gridCell.column.columnId, newValue: gridCell.normalisedValue, primaryKeyValue: gridCell.primaryKeyValue, rowNode: gridCell.rowNode, }); }); this.api.plusMinusApi.applyPlusMinus(cellUpdateRequests); } return shouldApplyPlusMinus; } isPlusMinusNudgeApplied(plusMinusNudge, gridCell) { if (plusMinusNudge.Rule?.BooleanExpression) { return this.evaluateExpression(plusMinusNudge, gridCell.rowNode, gridCell.column); } return true; } evaluateExpression(plusMinusNudge, node, column) { try { return this.api.internalApi .getQueryLanguageService() .evaluateBooleanExpression(plusMinusNudge.Rule?.BooleanExpression, PlusMinusModuleId, node, { columnScope: column?.columnId }); } catch (error) { errorOnce(error.message); return false; } } ShowErrorPreventMessage(failedRules) { if (failedRules.length > 0) { let failedMessages = []; failedRules.forEach((fr) => { let failedMessage = this.api.internalApi.getValidationService().createValidationMessage(fr) + '\n'; let existingMessage = failedMessages.find((f) => f == failedMessage); if (existingMessage == null) { failedMessages.push(failedMessage); } }); this.api.alertApi.showAlertError('Plus/Minus', 'One or more nudge values were rejected by validation rules: ' + failedMessages.toString()); } } getTeamSharingAction() { return { ModuleEntities: this.api.plusMinusApi.getAllPlusMinus(), AddAction: PlusMinusRedux.PlusMinusNudgeAdd, EditAction: PlusMinusRedux.PlusMinusNudgeEdit, }; } toView(plusMinus) { const keys = resolvePlusMinusTriggerKeysForNudge(plusMinus, this.api.optionsApi.getEditOptions().plusMinusOptions ?? {}); return { abObject: plusMinus, items: [ { name: 'Name', values: [plusMinus.Name], }, { name: 'Target', ...getScopeViewItems(plusMinus.Scope, this.api), }, plusMinus.Rule && { name: 'Settings', label: 'Rule', values: getRuleViewItems(plusMinus.Rule, this.api).values, }, { name: 'Settings', label: 'Nudge Value', values: [plusMinus.NudgeValue], }, { name: 'Settings', label: 'Increment Key', values: [keys.incrementKey], }, { name: 'Settings', label: 'Decrement Key', values: [keys.decrementKey], }, getObjectTagsViewItems(plusMinus, this.api), ].filter(Boolean), }; } toViewAll() { return this.getModuleAdaptableObjects({ includeLayoutNotExtendedObjects: this.showLayoutNonExtendedObjects(), }).map((plusMinus) => this.toView(plusMinus)); } getViewProperties() { return { actions: [MovePlusMinus], getEditAction: PlusMinusRedux.PlusMinusNudgeEdit, getDeleteAction: PlusMinusRedux.PlusMinusNudgeDelete, getSuspendAction: PlusMinusRedux.PlusMinusNudgeSuspend, getUnSuspendAction: PlusMinusRedux.PlusMinusNudgeUnSuspend, getSuspendAllAction: PlusMinusRedux.PlusMinusNudgeSuspendAll, getUnSuspendAllAction: PlusMinusRedux.PlusMinusNudgeUnSuspendAll, getEditWizard() { return PlusMinusWizard; }, }; } containsLayoutExtensions() { return true; } }