UNPKG

@adaptabletools/adaptable

Version:

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

440 lines (439 loc) 17.2 kB
import * as PopupRedux from '../../Redux/ActionsReducers/PopupRedux'; import * as InternalRedux from '../../Redux/ActionsReducers/InternalRedux'; import { ApiBase } from '../Implementation/ApiBase'; import StringExtensions from '../../Utilities/Extensions/StringExtensions'; import { ADAPTABLE_ID } from '../../Utilities/Constants/GeneralConstants'; import { waitForCondition } from '../../Utilities/waitForCondition'; import { isAdaptableCustomIcon, isAdaptableSystemIcon } from '../../components/Icon'; export class AdaptableInternalApi extends ApiBase { getInternalState() { return this.getAdaptableState().Internal; } getAdaptableJSXElement() { return this._adaptable._PRIVATE_adaptableJSXElement ?? null; } // Popup Redux Actions showPopupConfirmation(confirmation) { this.dispatchAction(PopupRedux.PopupShowConfirmation(confirmation)); } showPopupScreen(module, componentName, popupParams, popupProps) { this.dispatchAction(PopupRedux.PopupShowScreen(module, componentName, popupParams, popupProps)); } showPopupForm(config) { this.dispatchAction(PopupRedux.PopupShowForm(config)); } showPopupWindow({ id, title, icon, popupProps, factoryId, }) { this.dispatchAction(PopupRedux.PopupShowWindow({ Id: id, Title: title, Icon: icon, PopupProps: popupProps, FactoryId: factoryId, })); } hidePopupScreen() { this.dispatchAction(PopupRedux.PopupHideScreen()); } setSettingsPanelItems(menuItems) { this.dispatchAction(InternalRedux.SetSettingsPanelItems(menuItems)); } setDashboardModuleButtons(menuItems) { this.dispatchAction(InternalRedux.SetDasboardModuleButtons(menuItems)); } getToolbarTitle() { let toolbarTitle = this.getDashboardApi().getDashboardState().DashboardTitle; if (StringExtensions.IsNullOrEmpty(toolbarTitle)) { toolbarTitle = this.getOptions().adaptableId; if (toolbarTitle == ADAPTABLE_ID) { toolbarTitle = 'Adaptable '; } } return toolbarTitle; } buildCellDataChangedInfo(config) { return { ...config, rowData: config.rowNode?.data, changedAt: Date.now(), }; } buildRowDataChangedInfo(dataRows, rowNodes, rowTrigger) { const rowDataChangedInfo = { changedAt: Date.now(), rowTrigger, dataRows, rowNodes, }; return rowDataChangedInfo; } getAdaptableInstance() { return this._adaptable; } getAgGridAdapter() { return this._adaptable.agGridAdapter; } getInitialState() { return this.getOptions().initialState; } getState() { return this.getAdaptableState(); } getValidationService() { return this._adaptable.ValidationService; } getModuleService() { return this._adaptable.ModuleService; } getDataService() { return this._adaptable.DataService; } getAnnotationsService() { return this._adaptable.AnnotationsService; } getCalculatedColumnExpressionService() { return this._adaptable.CalculatedColumnExpressionService; } getQueryLanguageService() { return this._adaptable.QueryLanguageService; } getAlertService() { return this._adaptable.AlertService; } getTeamSharingService() { return this._adaptable.TeamSharingService; } getMetamodelService() { return this._adaptable.MetamodelService; } getRowFormService() { return this._adaptable.RowFormService; } getFdc3Service() { return this._adaptable.Fdc3Service; } getFlashingCellService() { return this._adaptable.FlashingCellService; } getModules() { return this._adaptable.adaptableModules; } forAllRowNodesDo(func, config) { this._adaptable.forAllRowNodesDo(func, config); } forAllVisibleRowNodesDo(func, config) { this._adaptable.forAllVisibleRowNodesDo(func, config); } getLabelForButton(button, context) { if (!button.label) { return ''; } if (button.label != null && typeof button.label === 'function') { return button.label(button, context); } else { return button.label; } } getTooltipForButton(button, context) { if (!button.tooltip) { return ''; } if (button.tooltip != null && typeof button.tooltip === 'function') { return button.tooltip(button, context); } else { return button.tooltip; } } getStyleForButton(button, context) { if (!button.buttonStyle) { return undefined; } if (button.buttonStyle != null && typeof button.buttonStyle === 'function') { return button.buttonStyle(button, context); } else { return button.buttonStyle; } } getIconForButton(button, context, defaultWidthHeight) { if (!button.icon) { return; } let buttonIcon; if (button.icon != null && typeof button.icon === 'function') { buttonIcon = button.icon(button, context); } else { buttonIcon = button.icon; } if (!buttonIcon) { return; } if (isAdaptableSystemIcon(buttonIcon) || isAdaptableCustomIcon(buttonIcon)) { const defaultIconWidth = defaultWidthHeight?.width ?? 'var(--ab-cmp-simple-button__width)'; const defaultIconHeight = defaultWidthHeight?.height ?? 'var(--ab-cmp-simple-button__height)'; const buttonIconStyle = Object.assign({}, { width: defaultIconWidth, height: defaultIconHeight }, buttonIcon.style); buttonIcon.style = buttonIconStyle; } return buttonIcon; } isDocumentationLinksDisplayed() { return this.getUserInterfaceOptions().showDocumentationLinks; } // the row data which is used to preview the expression evaluation (usually the first row data) getQueryPreviewData() { const firstRowNode = this.getGridApi().getFirstRowNode(); if (firstRowNode == undefined) { return {}; } const firstRowData = firstRowNode?.data ? { ...firstRowNode?.data } : {}; // handle CalcCols which are not persisted in the rowModel this.getCalculatedColumnApi() .getCalculatedColumns() .forEach((calculatedColumn) => { const columnRawValue = this._adaptable.api.gridApi.getRawValueFromRowNode(firstRowNode, calculatedColumn.ColumnId); firstRowData[calculatedColumn.ColumnId] = columnRawValue; }); // handle FreeTextCols which are not persisted in the rowModel this.getFreeTextColumnApi() .getFreeTextColumns() .forEach((freeTextColumn) => { const columnRawValue = this.getGridApi().getRawValueFromRowNode(firstRowNode, freeTextColumn.ColumnId); firstRowData[freeTextColumn.ColumnId] = columnRawValue; }); return firstRowData; } createFrameworkComponent(containerDomNode, frameworkComponent, componentType) { const createComponentFn = () => // delegate the logic to framework wrapper this._adaptable._emitSync('FrameworkComponentChange', { command: 'create', containerDomNode, frameworkComponent, componentType, }); if (this.isAdapTableReady()) { createComponentFn(); } else { // adaptable may not be initialized yet if the framework component is part of the initial render // AFL 2021.10.14 - not really sure if this is necessary anymore?! (see the other changes in this commit) waitForCondition(() => this.isAdapTableReady()).then(() => { createComponentFn(); }); } } destroyFrameworkComponent(containerDomNode, frameworkComponent, componentType) { this._adaptable._emitSync('FrameworkComponentChange', { command: 'destroy', containerDomNode, frameworkComponent, componentType, }); } initializeDataChangeHistory() { if (this.getDataChangeHistoryOptions().activeByDefault) { this.dispatchAction(InternalRedux.DataChangeHistoryEnable()); } else { this.dispatchAction(InternalRedux.DataChangeHistoryDisable()); } } executeWithProgressIndicator(label, executeFn, errorHandlingFn) { this.getUserInterfaceApi().showProgressIndicator({ text: label, delay: 300 }); // setTimeout required to give the ProgressIndicator rendering a head-start (see RAF in ProgressIndicator implementation) setTimeout(() => { try { executeFn(); } catch (error) { errorHandlingFn?.(); this.getUserInterfaceApi().hideProgressIndicator(); this.logError('Unexpected error while executing a function with a progress indicator', error); } }, 16); } getCorrectEnglishVariant(wordToSpell) { if (wordToSpell.includes('our')) { return this.getUserInterfaceOptions().englishVariant == 'GB' ? wordToSpell : wordToSpell.replace('our', 'or'); } return wordToSpell; } shouldDisplayTagSections() { return this.getAvailableTags() != undefined; } getAvailableTags() { const layoutTags = this.getLayoutTags(); const objectTags = this.getObjectTags(); if (!layoutTags && !objectTags) { return; } return [...(layoutTags || []), ...(objectTags || [])]; } getObjectTags() { const userInterfaceOptions = this.getUserInterfaceOptions(); if (!this.getUserInterfaceOptions().objectTags) { return; } if (Array.isArray(userInterfaceOptions.objectTags)) { return userInterfaceOptions.objectTags; } if (typeof userInterfaceOptions.objectTags === 'function') { // sanitize the provided tags, just to be sure that the user does NOT break the UI return userInterfaceOptions .objectTags(this.buildBaseContext()) .filter((tag) => typeof tag === 'string'); } } getLayoutTags() { const layoutOptions = this.getLayoutOptions(); if (!layoutOptions.layoutTagOptions?.autoGenerateTagsForLayouts) { return; } if (layoutOptions.layoutTagOptions.autoGenerateTagsForLayouts === true) { return this.getLayoutApi() .getLayouts() .map((layout) => { return layout.Name; }); } if (typeof layoutOptions.layoutTagOptions.autoGenerateTagsForLayouts === 'function') { const autoGenerateTagsForLayoutsContext = { layouts: this.getLayoutApi().getLayouts(), objectTags: this.getUserInterfaceApi().getAdaptableObjectTags(), ...this.buildBaseContext(), }; const customGeneratedTags = layoutOptions.layoutTagOptions.autoGenerateTagsForLayouts(autoGenerateTagsForLayoutsContext); // sanitize the provided tags, just to be sure that the user does NOT break the UI return customGeneratedTags.filter((tag) => typeof tag === 'string'); } } // General way to get to store from inside Adaptable... dispatchReduxAction(action) { this.dispatchAction(action); } // doing it this way to avoid having to expose Module Params which is internal showSettingsPanel(module, moduleParams) { this.showModulePopup(module, moduleParams); } getLabelForTag(adaptableObjectTag) { // not very interesting right now, but useful later when tag is not only a plain string return adaptableObjectTag; } getValueForTag(adaptableObjectTag) { // not very interesting right now, but useful later when tag is not only a plain string return adaptableObjectTag; } getAdaptableQueryExpressionText(query) { if (!query) { return ''; } const displayColumnFriendlyNames = this.getExpressionOptions()?.displayColumnFriendlyNamesForExpressions; return displayColumnFriendlyNames ? this.getExpressionApi().getAdaptableQueryExpressionWithColumnFriendlyNames(query) : this.getExpressionApi().getAdaptableQueryExpression(query); } /** * Gets a value from a rowData object using a field name. * Supports deep values (e.g. fieldName = 'address.street') */ // "borrowed" from https://github.com/ag-grid/ag-grid/blob/v28.2.1/community-modules/core/src/ts/utils/object.ts#L205 getValueUsingField(rowData, fieldName) { if (!rowData || !fieldName?.length) { return; } const isColumnValueKeyNested = fieldName?.includes('.'); // if no '.', then it's not a deep value if (!isColumnValueKeyNested) { return rowData[fieldName]; } // otherwise it is a deep value, so need to dig for it const fields = fieldName.split('.'); let currentObject = rowData; for (let i = 0; i < fields.length; i++) { if (currentObject == null) { return undefined; } currentObject = currentObject[fields[i]]; } return currentObject; } /** * Sets a value in a rowData object using a field name. * Supports deep values (e.g. fieldName = 'address.street') */ // "borrowed" from https://github.com/ag-grid/ag-grid/blob/v28.2.1/community-modules/core/src/ts/valueService/valueService.ts#L236 setValueUsingField(rowData, fieldName, newValue) { if (!rowData || !fieldName?.length) { return; } const isColumnValueKeyContainsDots = fieldName?.includes('.'); let valuesAreSame = false; if (!isColumnValueKeyContainsDots) { // soft comparison to match strings and numbers valuesAreSame = rowData[fieldName] == newValue; if (!valuesAreSame) { rowData[fieldName] = newValue; } } else { // otherwise it is a deep value, so need to dig for it const fieldPieces = fieldName.split('.'); let currentObject = rowData; while (fieldPieces.length > 0 && currentObject) { const fieldPiece = fieldPieces.shift(); if (fieldPieces.length === 0) { // soft comparison to match strings and numbers valuesAreSame = currentObject[fieldPiece] == newValue; if (!valuesAreSame) { currentObject[fieldPiece] = newValue; } } else { // NOTE AFL: this was NOT in the original AG Grid code, but we need it to handle inserting deep values (the original implementation only handled updating) if (currentObject[fieldPiece] == null) { currentObject[fieldPiece] = {}; } currentObject = currentObject[fieldPiece]; } } } return rowData; } findAdaptableObjectsByLookupCriteria({ scope, tag, ids }, specificAdaptableObjects) { if (!ids?.length && !tag && !scope) { return []; } let locatedAdaptableObjects = [...specificAdaptableObjects]; // IDs are the most specific locators if (ids?.length) { locatedAdaptableObjects = locatedAdaptableObjects.filter((adaptableObject) => ids.includes(adaptableObject.Uuid)); } // secondly filter by Tags if (tag) { locatedAdaptableObjects = locatedAdaptableObjects.filter((adaptableObject) => adaptableObject.Tags?.includes(tag)); } // lastly filter by scope if (scope) { locatedAdaptableObjects = locatedAdaptableObjects.filter((adaptableObject) => this.getColumnScopeApi().isScopeInScope(adaptableObject.Scope, scope)); } return locatedAdaptableObjects; } buildBaseContext() { const optionsApi = this.getOptionsApi(); return { adaptableApi: this.getAdaptableApi(), adaptableId: optionsApi.getAdaptableId(), adaptableStateKey: optionsApi.getAdaptableStateKey(), userName: optionsApi.getUserName(), adaptableContext: optionsApi.getAdaptableContext(), clientTimestamp: new Date(), }; } setCellSummaryInfo(cellSummaryInfo) { this.dispatchAction(InternalRedux.SetCellSummaryInfo(cellSummaryInfo)); } }