@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
JavaScript
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));
}
}