@adaptabletools/adaptable-cjs 22.0.10 → 22.1.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/index.css +33 -1
  2. package/index.css.map +1 -1
  3. package/package.json +3 -4
  4. package/src/AdaptableState/Common/AdaptablePredicate.js +1 -1
  5. package/src/AdaptableState/InitialState.d.ts +2 -2
  6. package/src/AdaptableState/LayoutState.d.ts +47 -0
  7. package/src/Api/CalendarApi.d.ts +15 -0
  8. package/src/Api/ColumnScopeApi.d.ts +5 -0
  9. package/src/Api/DataChangeHistoryApi.d.ts +7 -2
  10. package/src/Api/Implementation/CalendarApiImpl.d.ts +3 -0
  11. package/src/Api/Implementation/CalendarApiImpl.js +10 -0
  12. package/src/Api/Implementation/ColumnScopeApiImpl.d.ts +1 -0
  13. package/src/Api/Implementation/ColumnScopeApiImpl.js +12 -0
  14. package/src/Api/Implementation/DataChangeHistoryApiImpl.d.ts +2 -1
  15. package/src/Api/Implementation/DataChangeHistoryApiImpl.js +7 -0
  16. package/src/Api/Implementation/LayoutHelpers.js +12 -0
  17. package/src/Api/Internal/AlertInternalApi.js +4 -1
  18. package/src/Api/Internal/FormatColumnInternalApi.js +3 -3
  19. package/src/Strategy/FlashingCellModule.js +1 -0
  20. package/src/Strategy/PlusMinusModule.js +3 -3
  21. package/src/Utilities/Constants/GeneralConstants.js +2 -1
  22. package/src/Utilities/ExpressionFunctions/booleanExpressionFunctions.d.ts +1 -1
  23. package/src/Utilities/ExpressionFunctions/booleanExpressionFunctions.js +41 -2
  24. package/src/Utilities/ExpressionFunctions/scalarExpressionFunctions.d.ts +1 -1
  25. package/src/Utilities/ExpressionFunctions/scalarExpressionFunctions.js +31 -2
  26. package/src/Utilities/Helpers/AdaptableHelper.js +30 -4
  27. package/src/Utilities/Services/Interface/IQueryLanguageService.d.ts +1 -0
  28. package/src/Utilities/Services/MetamodelService.js +18 -18
  29. package/src/Utilities/Services/QueryLanguageService.d.ts +2 -0
  30. package/src/Utilities/Services/QueryLanguageService.js +20 -8
  31. package/src/Utilities/Services/ValidationService.js +3 -1
  32. package/src/View/Components/EntityRulesEditor/index.js +1 -1
  33. package/src/View/Components/ModuleValueSelector/index.js +9 -1
  34. package/src/View/Components/ReorderDraggable/index.js +21 -35
  35. package/src/View/Components/ValueSelector/index.js +45 -49
  36. package/src/View/Dashboard/PinnedToolbarsSelector.js +1 -1
  37. package/src/View/Layout/Wizard/LayoutWizard.js +16 -1
  38. package/src/View/Layout/Wizard/sections/RowSelectionSection.d.ts +8 -0
  39. package/src/View/Layout/Wizard/sections/RowSelectionSection.js +147 -0
  40. package/src/View/NamedQuery/Wizard/NamedQuerySettingsWizardSection.js +0 -1
  41. package/src/agGrid/AdaptableAgGrid.js +10 -0
  42. package/src/components/Dashboard/Dashboard.js +1 -1
  43. package/src/components/DragAndDropContext/ModuleManager.d.ts +1 -0
  44. package/src/components/DragAndDropContext/ModuleManager.js +11 -36
  45. package/src/components/DragAndDropContext/TabList.d.ts +11 -6
  46. package/src/components/DragAndDropContext/TabList.js +77 -35
  47. package/src/components/DragAndDropContext/UnusedPanel.js +9 -20
  48. package/src/components/ExpressionEditor/BaseEditorInput.d.ts +2 -0
  49. package/src/components/ExpressionEditor/BaseEditorInput.js +4 -0
  50. package/src/components/ExpressionEditor/EditorInput.d.ts +3 -1
  51. package/src/components/ExpressionEditor/EditorInput.js +20 -9
  52. package/src/components/ExpressionEditor/QueryBuilder/QueryBuilder.d.ts +2 -1
  53. package/src/components/ExpressionEditor/QueryBuilder/QueryBuilder.js +1 -10
  54. package/src/components/ExpressionEditor/QueryBuilder/QueryPredicateBuilder.js +16 -18
  55. package/src/components/ExpressionEditor/index.d.ts +2 -1
  56. package/src/components/ExpressionEditor/index.js +1 -1
  57. package/src/components/Tree/TreeDropdown/index.js +37 -26
  58. package/src/components/dnd/index.d.ts +3 -13
  59. package/src/components/dnd/index.js +11 -59
  60. package/src/env.js +2 -2
  61. package/src/layout-manager/src/LayoutManagerModel.d.ts +2 -1
  62. package/src/layout-manager/src/index.d.ts +9 -0
  63. package/src/layout-manager/src/index.js +97 -1
  64. package/src/layout-manager/src/normalizeLayoutModel.js +8 -0
  65. package/src/layout-manager/src/simplifyLayoutModel.js +6 -0
  66. package/src/metamodel/adaptable-metamodel-model.d.ts +22 -13
  67. package/src/metamodel/adaptable.metamodel.d.ts +3773 -5143
  68. package/src/metamodel/adaptable.metamodel.js +1 -1
  69. package/src/parser/src/parser.js +55 -1218
  70. package/src/parser/src/types.d.ts +5 -0
  71. package/tsconfig.cjs.tsbuildinfo +1 -1
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CalendarApiImpl = void 0;
4
4
  const ApiBase_1 = require("./ApiBase");
5
+ const date_fns_1 = require("date-fns");
5
6
  class CalendarApiImpl extends ApiBase_1.ApiBase {
6
7
  getNextWorkingDay() {
7
8
  let counterDate = new Date();
@@ -52,5 +53,14 @@ class CalendarApiImpl extends ApiBase_1.ApiBase {
52
53
  }
53
54
  }
54
55
  }
56
+ isToday(dateToCheck) {
57
+ return (0, date_fns_1.isToday)(dateToCheck);
58
+ }
59
+ isYesterday(dateToCheck) {
60
+ return (0, date_fns_1.isYesterday)(dateToCheck);
61
+ }
62
+ isTomorrow(dateToCheck) {
63
+ return (0, date_fns_1.isTomorrow)(dateToCheck);
64
+ }
55
65
  }
56
66
  exports.CalendarApiImpl = CalendarApiImpl;
@@ -29,5 +29,6 @@ export declare class ColumnScopeApiImpl extends ApiBase implements ColumnScopeAp
29
29
  isColumnInDateScope(column: AdaptableColumn, scope: ColumnScope): boolean;
30
30
  isScopeInScope(a: ColumnScope, b: ColumnScope): boolean;
31
31
  createCellColorRangesForScope(scope: ColumnScope): CellColorRange[];
32
+ getAnyColumnIdForScope(scope: ColumnScope): string | undefined;
32
33
  private distinctColumnValuesAreEmpty;
33
34
  }
@@ -310,6 +310,18 @@ class ColumnScopeApiImpl extends ApiBase_1.ApiBase {
310
310
  }
311
311
  return ranges;
312
312
  }
313
+ getAnyColumnIdForScope(scope) {
314
+ if (!scope) {
315
+ return;
316
+ }
317
+ const allAdaptableColumns = this.getColumnApi()
318
+ .getColumns()
319
+ .filter((col) => !col.isUIHiddenColumn &&
320
+ !col.isGeneratedRowGroupColumn &&
321
+ !col.isGeneratedSelectionColumn &&
322
+ !col.isPivotTotalColumn);
323
+ return allAdaptableColumns.find((col) => this.isColumnInScope(col, scope))?.columnId;
324
+ }
313
325
  distinctColumnValuesAreEmpty(distinctColumnsValues) {
314
326
  if (ArrayExtensions_1.default.IsNullOrEmpty(distinctColumnsValues)) {
315
327
  return true;
@@ -10,7 +10,8 @@ export declare class DataChangeHistoryApiImpl extends ApiBase implements DataCha
10
10
  getDataChangeHistoryLog(): CellDataChangedInfo[];
11
11
  getDataChangeForGridCell(gridCell: GridCell): CellDataChangedInfo | undefined;
12
12
  addDataChangeHistoryEntry(dataChangeInfo: CellDataChangedInfo): void;
13
- undoDataChangeHistoryEntry(dataChangeInfo: CellDataChangedInfo): void;
13
+ undoDataChangeHistoryEntry(dataChangeInfo: CellDataChangedInfo): GridCell | undefined;
14
+ undoAllDataChangeHistoryEntries(): GridCell[];
14
15
  clearDataChangeHistoryEntry(dataChangeInfo: CellDataChangedInfo): void;
15
16
  openDataChangeHistorySettingsPanel(): void;
16
17
  private getDataChangeHistoryKey;
@@ -48,6 +48,13 @@ class DataChangeHistoryApiImpl extends ApiBase_1.ApiBase {
48
48
  undoDataChangeHistoryEntry(dataChangeInfo) {
49
49
  const uniqueKey = this.getDataChangeHistoryKey(dataChangeInfo);
50
50
  this.dispatchAction((0, InternalRedux_1.DataChangeHistoryUndo)(dataChangeInfo, uniqueKey));
51
+ return this.getGridApi().getGridCellFromRowNode(dataChangeInfo.rowNode, dataChangeInfo.column.columnId);
52
+ }
53
+ undoAllDataChangeHistoryEntries() {
54
+ const allChanges = this.getDataChangeHistoryLog();
55
+ return allChanges
56
+ .map((dataChange) => this.undoDataChangeHistoryEntry(dataChange))
57
+ .filter((gridCell) => gridCell != null);
51
58
  }
52
59
  clearDataChangeHistoryEntry(dataChangeInfo) {
53
60
  const uniqueKey = this.getDataChangeHistoryKey(dataChangeInfo);
@@ -227,6 +227,9 @@ const tableLayoutToTableLayoutModel = (tableLayout) => {
227
227
  TableAggregationColumns: TableAggregationColumns,
228
228
  GrandTotalRow: tableLayout.GrandTotalRow,
229
229
  });
230
+ if (tableLayout.RowSelection != null) {
231
+ result.RowSelection = tableLayout.RowSelection;
232
+ }
230
233
  if (tableLayout.RowGroupDisplayType) {
231
234
  result.RowGroupDisplayType = tableLayout.RowGroupDisplayType;
232
235
  }
@@ -304,6 +307,9 @@ const pivotLayoutToPivotLayoutModel = (pivotLayout) => {
304
307
  }
305
308
  : undefined,
306
309
  });
310
+ if (pivotLayout.RowSelection != null) {
311
+ result.RowSelection = pivotLayout.RowSelection;
312
+ }
307
313
  if (pivotLayout.RowGroupDisplayType) {
308
314
  result.RowGroupDisplayType = pivotLayout.RowGroupDisplayType;
309
315
  }
@@ -439,6 +445,9 @@ const tableLayoutModelToTableLayout = (layoutModel) => {
439
445
  else {
440
446
  delete tableLayout.GrandTotalRow;
441
447
  }
448
+ if (layoutModel.RowSelection != null) {
449
+ tableLayout.RowSelection = layoutModel.RowSelection;
450
+ }
442
451
  cleanupAdaptableObjectPrimitives(tableLayout);
443
452
  return tableLayout;
444
453
  };
@@ -556,6 +565,9 @@ const pivotLayoutModelToPivotLayout = (layoutModel) => {
556
565
  // if (layoutModel.RowGroupDisplayType) {
557
566
  pivotLayout.RowGroupDisplayType = layoutModel.RowGroupDisplayType ?? 'single';
558
567
  // }
568
+ if (layoutModel.RowSelection != null) {
569
+ pivotLayout.RowSelection = layoutModel.RowSelection;
570
+ }
559
571
  return pivotLayout;
560
572
  };
561
573
  exports.pivotLayoutModelToPivotLayout = pivotLayoutModelToPivotLayout;
@@ -529,7 +529,10 @@ class AlertInternalApi extends ApiBase_1.ApiBase {
529
529
  isValidExpression &&
530
530
  this.getAdaptableApi()
531
531
  .internalApi.getQueryLanguageService()
532
- .evaluateBooleanExpression(expression, 'Alert', rowNode, { dataChangedEvent });
532
+ .evaluateBooleanExpression(expression, 'Alert', rowNode, {
533
+ dataChangedEvent,
534
+ columnScope: dataChangedEvent.column?.columnId,
535
+ });
533
536
  }
534
537
  catch (error) {
535
538
  isSatisfiedExpression = false;
@@ -257,7 +257,7 @@ class FormatColumnInternalApi extends ApiBase_1.ApiBase {
257
257
  return this.evaluatePredicate(formatColumn, predicateDefHandlerContext);
258
258
  } // then run the Expression
259
259
  else if (formatColumn.Rule.BooleanExpression) {
260
- return this.evaluateExpression(formatColumn, rowNode, pivotResultColumn);
260
+ return this.evaluateExpression(formatColumn, rowNode, column, pivotResultColumn);
261
261
  }
262
262
  // nothing has passed then return false
263
263
  return false;
@@ -281,13 +281,13 @@ class FormatColumnInternalApi extends ApiBase_1.ApiBase {
281
281
  evaluatePredicate(formatColumn, predicateDefHandlerContext) {
282
282
  return this.getPredicateApi().handleColumnPredicates(formatColumn.Rule?.Predicates, predicateDefHandlerContext, false);
283
283
  }
284
- evaluateExpression(formatColumn, node, pivotResultColumn) {
284
+ evaluateExpression(formatColumn, node, columnScope, pivotResultColumn) {
285
285
  const isValidExpression = this.getExpressionApi().isValidBooleanExpression(formatColumn.Rule.BooleanExpression, ModuleConstants.FormatColumnModuleId, `Invalid format column rule '${formatColumn.Rule.BooleanExpression}'`);
286
286
  try {
287
287
  return (isValidExpression &&
288
288
  this.getAdaptableApi()
289
289
  .internalApi.getQueryLanguageService()
290
- .evaluateBooleanExpression(formatColumn.Rule.BooleanExpression, ModuleConstants.FormatColumnModuleId, node, { pivotResultColumn }));
290
+ .evaluateBooleanExpression(formatColumn.Rule.BooleanExpression, ModuleConstants.FormatColumnModuleId, node, { pivotResultColumn, columnScope: columnScope?.columnId }));
291
291
  }
292
292
  catch (error) {
293
293
  (0, AdaptableLogger_1.errorOnce)(error.message);
@@ -92,6 +92,7 @@ class FlashingCellModule extends AdaptableModuleBase_1.AdaptableModuleBase {
92
92
  .getQueryLanguageService()
93
93
  .evaluateBooleanExpression(expression, this.moduleInfo.ModuleName, rowNode, {
94
94
  dataChangedEvent: cellDataChangedInfo,
95
+ columnScope: cellDataChangedInfo.column?.columnId,
95
96
  });
96
97
  }
97
98
  catch (error) {
@@ -181,15 +181,15 @@ class PlusMinusModule extends AdaptableModuleBase_1.AdaptableModuleBase {
181
181
  }
182
182
  isPlusMinusNudgeApplied(plusMinusNudge, gridCell) {
183
183
  if (plusMinusNudge.Rule?.BooleanExpression) {
184
- return this.evaluateExpression(plusMinusNudge, gridCell.rowNode);
184
+ return this.evaluateExpression(plusMinusNudge, gridCell.rowNode, gridCell.column);
185
185
  }
186
186
  return true;
187
187
  }
188
- evaluateExpression(plusMinusNudge, node) {
188
+ evaluateExpression(plusMinusNudge, node, column) {
189
189
  try {
190
190
  return this.api.internalApi
191
191
  .getQueryLanguageService()
192
- .evaluateBooleanExpression(plusMinusNudge.Rule?.BooleanExpression, ModuleConstants_1.PlusMinusModuleId, node);
192
+ .evaluateBooleanExpression(plusMinusNudge.Rule?.BooleanExpression, ModuleConstants_1.PlusMinusModuleId, node, { columnScope: column?.columnId });
193
193
  }
194
194
  catch (error) {
195
195
  (0, AdaptableLogger_1.errorOnce)(error.message);
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.THEME_STYLE = exports.SYSTEM_EXPORT_DESTINATIONS = exports.CLIPBOARD_EXPORT_DESTINATION = exports.DOWNLOAD_EXPORT_DESTINATION = exports.SELECT_REPORT_FORMAT_STRING = exports.SELECT_REPORT_STRING = exports.SYSTEM_REPORT_FORMATS = exports.JSON_FORMAT_REPORT = exports.CSV_FORMAT_REPORT = exports.VISUAL_EXCEL_FORMAT_REPORT = exports.EXCEL_FORMAT_REPORT = exports.SYSTEM_REPORT_NAMES = exports.SELECTED_DATA_REPORT = exports.CURRENT_LAYOUT_REPORT = exports.ALL_DATA_REPORT = exports.SERVER_VALIDATION_MESSAGE_TYPE = exports.SERVER_VALIDATION_HEADER = exports.DEFAULT_LIVE_REPORT_THROTTLE_TIME = exports.MENU_SEPARATOR = exports.QUICK_SEARCH_DEBOUNCE_TIME = exports.DEFAULT_DOUBLE_DISPLAY_VALUE = exports.DEFAULT_INTEGER_DISPLAY_VALUE = exports.DEFAULT_STRING_DISPLAY_VALUE = exports.DEFAULT_DATE_FORMAT_PATTERN_WITH_TIME = exports.DEFAULT_DATE_FORMAT_PATTERN = exports.ADAPTABLE_FDC3_ACTION_COLUMN_FRIENDLY_NAME = exports.AG_GRID_GRAND_TOTAL_ROW_ID = exports.AG_GRID_CHART_WINDOW = exports.AG_GRID_PIVOT_GRAND_TOTAL_COLUMN = exports.AG_GRID_PIVOT_COLUMN = exports.AG_GRID_SELECTION_COLUMN = exports.AG_GRID_GROUPED_COLUMN = exports.GROUP_PATH_SEPARATOR = exports.QUARTER_SECOND = exports.HALF_SECOND = exports.EMPTY_ARRAY = exports.EMPTY_STRING = exports.READ_ONLY_STYLE = exports.AGGRID_TOOLPANEL_COLUMNS = exports.AGGRID_TOOLPANEL_FILTERS = exports.ADAPTABLE_TOOLPANEL_COMPONENT = exports.ADAPTABLE_TOOLPANEL_ID = exports.ADAPTABLE_ID = exports.ADAPTABLE = exports.ERROR_LAYOUT = exports.OS_THEME = exports.DARK_THEME = exports.LIGHT_THEME = exports.MISSING_COLUMN = exports.AUTOGENERATED_PK_COLUMN = void 0;
4
4
  exports.OBSERVABLE_EXPRESSION_ROW_REMOVED = exports.OBSERVABLE_EXPRESSION_ROW_ADDED = exports.STANDALONE_MODULE_POPUPS = exports.LAYOUT_DELETE_TOOLTIP = exports.LAYOUT_CLONE_TOOLTIP = exports.LAYOUT_EDIT_TOOLTIP = exports.LAYOUT_SAVE_TOOLTIP = exports.LAYOUT_NEW_TABLE_OR_PIVOT_TOOLTIP = exports.LAYOUT_NEW_PIVOT_TOOLTIP = exports.LAYOUT_NEW_TABLE_TOOLTIP = void 0;
5
5
  const normalizeLayoutModel_1 = require("../../layout-manager/src/normalizeLayoutModel");
6
+ const src_1 = require("../../layout-manager/src");
6
7
  exports.AUTOGENERATED_PK_COLUMN = '__ADAPTABLE_PK__';
7
8
  exports.MISSING_COLUMN = ' [MISSING]';
8
9
  exports.LIGHT_THEME = 'light';
@@ -26,7 +27,7 @@ exports.HALF_SECOND = 500;
26
27
  exports.QUARTER_SECOND = 250;
27
28
  exports.GROUP_PATH_SEPARATOR = '/';
28
29
  exports.AG_GRID_GROUPED_COLUMN = normalizeLayoutModel_1.AUTO_GROUP_COLUMN_ID__SINGLE;
29
- exports.AG_GRID_SELECTION_COLUMN = 'ag-Grid-SelectionColumn';
30
+ exports.AG_GRID_SELECTION_COLUMN = src_1.LayoutManager.SELECTION_COLUMN_ID;
30
31
  exports.AG_GRID_PIVOT_COLUMN = 'pivot_';
31
32
  exports.AG_GRID_PIVOT_GRAND_TOTAL_COLUMN = 'PivotRowTotal_pivot_';
32
33
  exports.AG_GRID_CHART_WINDOW = 'AG Grid Window';
@@ -2,7 +2,7 @@ import { ExpressionFunction } from '../../parser/src/types';
2
2
  /**
3
3
  * List of all the Boolean Functions available in AdaptableQL
4
4
  */
5
- export type BooleanFunctionName = 'EQ' | 'NEQ' | 'GT' | 'LT' | 'GTE' | 'LTE' | 'AND' | 'OR' | 'NOT' | 'BETWEEN' | 'IN' | 'CONTAINS' | 'STARTS_WITH' | 'ENDS_WITH' | 'ANY_CONTAINS' | 'IS_NUMERIC' | 'REGEX' | 'IS_HOLIDAY' | 'IS_WORKDAY' | 'TRUE' | 'FALSE';
5
+ export type BooleanFunctionName = 'EQ' | 'NEQ' | 'GT' | 'LT' | 'GTE' | 'LTE' | 'AND' | 'OR' | 'NOT' | 'BETWEEN' | 'IN' | 'CONTAINS' | 'STARTS_WITH' | 'ENDS_WITH' | 'ANY_CONTAINS' | 'IS_NUMERIC' | 'REGEX' | 'IS_TODAY' | 'IS_YESTERDAY' | 'IS_TOMORROW' | 'IS_HOLIDAY' | 'IS_WORKDAY' | 'TRUE' | 'FALSE';
6
6
  export declare const booleanExpressionFunctions: Record<BooleanFunctionName, ExpressionFunction>;
7
7
  export declare const booleanExpressionFunctionsNames: BooleanFunctionName[];
8
8
  export declare const isBooleanAdaptableQlFunction: (functionName: string) => functionName is BooleanFunctionName;
@@ -257,8 +257,8 @@ exports.booleanExpressionFunctions = {
257
257
  },
258
258
  category: 'strings',
259
259
  description: 'Returns true if 1st input contains 2nd input',
260
- signatures: ['CONTAINS(a: string, b: string)'],
261
- examples: ['CONTAINS([col1], "S")'],
260
+ signatures: ['value1 CONTAINS value2', 'CONTAINS(a: string, b: string)'],
261
+ examples: ['[col1] CONTAINS "S"', 'CONTAINS([col1], "S")'],
262
262
  returnType: 'boolean',
263
263
  inputs: ['text', 'text'],
264
264
  },
@@ -328,6 +328,45 @@ exports.booleanExpressionFunctions = {
328
328
  category: 'strings',
329
329
  returnType: 'boolean',
330
330
  },
331
+ IS_TODAY: {
332
+ handler(args, context) {
333
+ const dateToCheck = (0, dateUtils_1.normalizeDateParam)(args[0]);
334
+ return context.adaptableApi.calendarApi.isToday(dateToCheck);
335
+ },
336
+ isHiddenFromMenu: false,
337
+ description: 'Returns true if input date is today',
338
+ signatures: ['IS_TODAY(a: date)'],
339
+ examples: ['IS_TODAY([columnName])'],
340
+ category: 'dates',
341
+ returnType: 'boolean',
342
+ inputs: ['date'],
343
+ },
344
+ IS_YESTERDAY: {
345
+ handler(args, context) {
346
+ const dateToCheck = (0, dateUtils_1.normalizeDateParam)(args[0]);
347
+ return context.adaptableApi.calendarApi.isYesterday(dateToCheck);
348
+ },
349
+ isHiddenFromMenu: false,
350
+ description: 'Returns true if input date is yesterday',
351
+ signatures: ['IS_YESTERDAY(a: date)'],
352
+ examples: ['IS_YESTERDAY([columnName])'],
353
+ category: 'dates',
354
+ returnType: 'boolean',
355
+ inputs: ['date'],
356
+ },
357
+ IS_TOMORROW: {
358
+ handler(args, context) {
359
+ const dateToCheck = (0, dateUtils_1.normalizeDateParam)(args[0]);
360
+ return context.adaptableApi.calendarApi.isTomorrow(dateToCheck);
361
+ },
362
+ isHiddenFromMenu: false,
363
+ description: 'Returns true if input date is tomorrow',
364
+ signatures: ['IS_TOMORROW(a: date)'],
365
+ examples: ['IS_TOMORROW([columnName])'],
366
+ category: 'dates',
367
+ returnType: 'boolean',
368
+ inputs: ['date'],
369
+ },
331
370
  IS_HOLIDAY: {
332
371
  handler(args, context) {
333
372
  const dateToCheck = (0, dateUtils_1.normalizeDateParam)(args[0]);
@@ -2,6 +2,6 @@ import { ExpressionFunction } from '../../parser/src/types';
2
2
  /**
3
3
  * List of all the Scalar Functions available in AdaptableQL
4
4
  */
5
- export type ScalarFunctionName = 'ADD' | 'SUB' | 'MUL' | 'DIV' | 'MOD' | 'POW' | 'ABS' | 'CEILING' | 'FLOOR' | 'ROUND' | 'MIN' | 'MAX' | 'AVG' | 'DATE' | 'NOW' | 'CURRENT_DAY' | 'DAY' | 'WEEK' | 'MONTH' | 'YEAR' | 'ADD_DAYS' | 'ADD_WEEKS' | 'ADD_MONTHS' | 'ADD_YEARS' | 'DIFF_DAYS' | 'DIFF_WEEKS' | 'DIFF_MONTHS' | 'DIFF_YEARS' | 'SUB_STRING' | 'REPLACE' | 'COALESCE' | 'NULL' | 'LEN' | 'UPPER' | 'LOWER' | 'CONCAT' | 'IF' | 'CASE' | 'COL' | 'FIELD' | 'VAR' | 'TO_ARRAY' | 'QUERY' | 'PERCENT_CHANGE' | 'ABSOLUTE_CHANGE' | 'ANY_CHANGE' | 'IS_BLANK' | 'IS_NOT_BLANK';
5
+ export type ScalarFunctionName = 'ADD' | 'SUB' | 'MUL' | 'DIV' | 'MOD' | 'POW' | 'ABS' | 'CEILING' | 'FLOOR' | 'ROUND' | 'MIN' | 'MAX' | 'AVG' | 'DATE' | 'NOW' | 'CURRENT_DAY' | 'DAY' | 'WEEK' | 'MONTH' | 'YEAR' | 'ADD_DAYS' | 'ADD_WEEKS' | 'ADD_MONTHS' | 'ADD_YEARS' | 'DIFF_DAYS' | 'DIFF_WEEKS' | 'DIFF_MONTHS' | 'DIFF_YEARS' | 'SUB_STRING' | 'REPLACE' | 'COALESCE' | 'NULL' | 'LEN' | 'UPPER' | 'LOWER' | 'CONCAT' | 'IF' | 'CASE' | 'COL' | 'FIELD' | '$SCOPE' | 'VAR' | 'TO_ARRAY' | 'QUERY' | 'PERCENT_CHANGE' | 'ABSOLUTE_CHANGE' | 'ANY_CHANGE' | 'IS_BLANK' | 'IS_NOT_BLANK';
6
6
  export declare const scalarExpressionFunctions: Record<ScalarFunctionName, ExpressionFunction>;
7
7
  export declare const scalarExpressionFunctionNames: ScalarFunctionName[];
@@ -94,6 +94,35 @@ exports.scalarExpressionFunctions = {
94
94
  category: 'special',
95
95
  returnType: 'any',
96
96
  },
97
+ $SCOPE: {
98
+ handler(args, context) {
99
+ const scopeColumnId = context.columnScope;
100
+ if (!scopeColumnId) {
101
+ throw new ExpressionEvaluationError_1.ExpressionEvaluationError('$SCOPE', `$SCOPE is not available in this context`);
102
+ }
103
+ const column = context.adaptableApi?.columnApi.getColumnWithColumnId(scopeColumnId);
104
+ if (!column) {
105
+ throw new ExpressionEvaluationError_1.ExpressionEvaluationError('$SCOPE', `Column name "${scopeColumnId}" is not found`);
106
+ }
107
+ if (!column.queryable) {
108
+ throw new ExpressionEvaluationError_1.ExpressionEvaluationError('$SCOPE', `Column name "${scopeColumnId}" is not queryable`);
109
+ }
110
+ // see #derived_pivot_cell_style
111
+ if (context.pivotResultColumn) {
112
+ const baseColumnId = context.pivotResultColumn.getColDef()?.pivotValueColumn?.getColId();
113
+ if (baseColumnId === scopeColumnId) {
114
+ // we evaluate the pivot result column instead of the base column
115
+ return context.adaptableApi?.gridApi.getNormalisedValueFromRowNode(context.node, context.pivotResultColumn.getColId());
116
+ }
117
+ }
118
+ return context.adaptableApi?.gridApi.getNormalisedValueFromRowNode(context.node, scopeColumnId);
119
+ },
120
+ description: 'Returns the value of the column in the current scope',
121
+ signatures: ['$SCOPE()'],
122
+ examples: ['$SCOPE() > 10'],
123
+ category: 'special',
124
+ returnType: 'any',
125
+ },
97
126
  QUERY: {
98
127
  handler(args, context) {
99
128
  const namedQueryName = args[0];
@@ -713,8 +742,8 @@ exports.scalarExpressionFunctions = {
713
742
  return context.dataChangedEvent.oldValue !== context.dataChangedEvent.newValue;
714
743
  }
715
744
  // only COL argument is supported
716
- if (columnArg.type !== 'COL') {
717
- throw new ExpressionEvaluationError_1.ExpressionEvaluationError('ANY_CHANGE', 'accepts only a column reference as an argument');
745
+ if (columnArg.type !== 'COL' && columnArg.type !== '$SCOPE') {
746
+ throw new ExpressionEvaluationError_1.ExpressionEvaluationError('ANY_CHANGE', 'accepts only a column reference or scope as an argument');
718
747
  }
719
748
  const currentColumnValue = (0, evaluator_1.evaluateNode)(columnArg, context);
720
749
  const previousValue = context.dataChangedEvent.oldValue;
@@ -20,11 +20,37 @@ const GET_CURRENT_VERSION = () => {
20
20
  : package_json_1.default.version) ?? package_json_1.default.version);
21
21
  };
22
22
  function addUuidsToInitialState(initialState) {
23
+ // Collect only the objects that should receive metadata:
24
+ // module state objects and their direct array items (root-level AdaptableObjects).
25
+ // Deeply nested sub-objects (e.g. CellRanges, ColumnComparison) are excluded.
26
+ const objectsToEnrich = new Set();
27
+ for (const moduleKey in initialState) {
28
+ const moduleState = initialState[moduleKey];
29
+ if ((0, isPlainObject_1.default)(moduleState)) {
30
+ objectsToEnrich.add(moduleState);
31
+ for (const key in moduleState) {
32
+ const value = moduleState[key];
33
+ if (Array.isArray(value)) {
34
+ for (const item of value) {
35
+ if ((0, isPlainObject_1.default)(item)) {
36
+ objectsToEnrich.add(item);
37
+ // ColumnFilters are nested inside Layouts but need Uuid
38
+ // for identification (suspend/unsuspend, edit vs add, etc.)
39
+ if (Array.isArray(item.ColumnFilters)) {
40
+ for (const filter of item.ColumnFilters) {
41
+ if ((0, isPlainObject_1.default)(filter)) {
42
+ objectsToEnrich.add(filter);
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
23
52
  const customizer = (value) => {
24
- // so whenever we clone a plain object,
25
- // we add a Uuid property
26
- // jw added 9/8/20: unless there is on there already
27
- if ((0, isPlainObject_1.default)(value) && value != initialState) {
53
+ if ((0, isPlainObject_1.default)(value) && objectsToEnrich.has(value)) {
28
54
  if (value.Uuid == null || value.Uuid == undefined) {
29
55
  value.Uuid = (0, Uuid_1.createUuid)();
30
56
  }
@@ -13,6 +13,7 @@ import { ExpressionFunctionMap } from '../../../parser/src/types';
13
13
  export interface IQueryLanguageService extends IAdaptableService {
14
14
  evaluateBooleanExpression(expression: string, module: AdaptableModule, rowNode: IRowNode, evalContext?: {
15
15
  dataChangedEvent?: CellDataChangedInfo;
16
+ columnScope?: string;
16
17
  pivotResultColumn?: Column;
17
18
  }): boolean;
18
19
  evaluateScalarExpression(expression: string, module: AdaptableModule, rowNode: IRowNode): any;
@@ -24,7 +24,7 @@ class MetamodelService {
24
24
  getGridInfoNoCodeOptions() {
25
25
  return this.buildGridInfoOptions({
26
26
  filterItemProperty: (itemProperty) => {
27
- return itemProperty.noCode === 'item';
27
+ return itemProperty.nC === 'item';
28
28
  },
29
29
  filterEmptyAdaptableOptions: false,
30
30
  });
@@ -54,7 +54,7 @@ class MetamodelService {
54
54
  if (optionKey === 'fdc3Options') {
55
55
  return;
56
56
  }
57
- const optionMetamodel = optionsObjectMetamodel?.props?.find((metamodelProperty) => metamodelProperty.name === optionKey);
57
+ const optionMetamodel = optionsObjectMetamodel?.p?.find((metamodelProperty) => metamodelProperty.n === optionKey);
58
58
  // Return if property not found - log if not 'Revision' or 'Uuid' or 'AdaptableVersion'
59
59
  if (!optionMetamodel) {
60
60
  if (optionKey === 'Revision' || optionKey === 'Uuid' || optionKey === 'AdaptableVersion') {
@@ -73,10 +73,10 @@ class MetamodelService {
73
73
  }
74
74
  // if it's a REFERENCE, we try to go (recursively) deeper
75
75
  if (expectedOptionsValueType === 'R') {
76
- const referenceObjectName = optionMetamodel.ref;
76
+ const referenceObjectName = optionMetamodel.r;
77
77
  const referenceObject = optionsObject[optionKey];
78
78
  const referenceObjectMetamodel = this.getAdaptableMetamodel()[referenceObjectName];
79
- if (referenceObject && referenceObjectMetamodel?.kind === 'I') {
79
+ if (referenceObject && referenceObjectMetamodel?.k === 'I') {
80
80
  this.validateOptionsObject(validationErrors, referenceObjectName, referenceObject, referenceObjectMetamodel, optionsObjectDefaultValues?.[optionKey]);
81
81
  }
82
82
  }
@@ -87,7 +87,7 @@ class MetamodelService {
87
87
  });
88
88
  }
89
89
  getExpectedOptionsValueType(metamodelProperty) {
90
- const metamodelPropertyKind = metamodelProperty.kind;
90
+ const metamodelPropertyKind = metamodelProperty.k;
91
91
  if (supportedMetamodelTypes.includes(metamodelPropertyKind)) {
92
92
  return metamodelPropertyKind;
93
93
  }
@@ -110,11 +110,11 @@ class MetamodelService {
110
110
  items: baseOptionsItems,
111
111
  });
112
112
  // map containers
113
- adaptableOptionsMetamodel.props
114
- .filter((optionItem) => optionItem.gridInfo === 'container')
113
+ adaptableOptionsMetamodel.p
114
+ .filter((optionItem) => optionItem.g === 'container')
115
115
  .forEach((containerOptionItem) => {
116
- const containerMetamodelName = containerOptionItem.ref;
117
- const adaptableOptionsName = containerOptionItem.name;
116
+ const containerMetamodelName = containerOptionItem.r;
117
+ const adaptableOptionsName = containerOptionItem.n;
118
118
  const containerOptionsMetamodel = adaptableMetamodel[containerMetamodelName];
119
119
  // @ts-ignore
120
120
  const containerOptionsValues = adaptableOptionsValues[adaptableOptionsName];
@@ -127,21 +127,21 @@ class MetamodelService {
127
127
  return;
128
128
  }
129
129
  const optionItems = this.mapGridInfoContainerItems(containerOptionsMetamodel, containerOptionsValues, containerOptionsDefaultValues, filterItemProperty);
130
- gridInfoOptions.set(containerOptionItem.name, {
130
+ gridInfoOptions.set(containerOptionItem.n, {
131
131
  containerLabel: this.extractUiLabel(containerOptionItem),
132
132
  items: optionItems,
133
133
  });
134
134
  });
135
135
  return gridInfoOptions;
136
136
  }
137
- mapGridInfoContainerItems(optionItemContainer, adaptableOptionsValues, defaultAdaptableOptionsValues, filter = (itemProperty) => itemProperty.gridInfo === 'item') {
138
- return optionItemContainer.props.filter(filter).map((itemProperty) => {
137
+ mapGridInfoContainerItems(optionItemContainer, adaptableOptionsValues, defaultAdaptableOptionsValues, filter = (itemProperty) => itemProperty.g === 'item') {
138
+ return optionItemContainer.p.filter(filter).map((itemProperty) => {
139
139
  return {
140
- name: itemProperty.name,
141
- value: adaptableOptionsValues?.[itemProperty.name],
142
- defaultValue: defaultAdaptableOptionsValues[itemProperty.name],
143
- kind: itemProperty.kind,
144
- description: StringExtensions_1.default.UnescapeHtmlEntities(itemProperty.desc),
140
+ name: itemProperty.n,
141
+ value: adaptableOptionsValues?.[itemProperty.n],
142
+ defaultValue: defaultAdaptableOptionsValues[itemProperty.n],
143
+ kind: itemProperty.k,
144
+ description: StringExtensions_1.default.UnescapeHtmlEntities(itemProperty.d ?? ''),
145
145
  uiLabel: this.extractUiLabel(itemProperty),
146
146
  };
147
147
  });
@@ -156,7 +156,7 @@ class MetamodelService {
156
156
  return this.getAdaptableMetamodel()['AdaptableOptions'];
157
157
  }
158
158
  extractUiLabel(item) {
159
- return item.uiLabel || this.formatCamelCaseToHumanText(item.name);
159
+ return item.l || this.formatCamelCaseToHumanText(item.n);
160
160
  }
161
161
  formatCamelCaseToHumanText(camelCase) {
162
162
  if (!camelCase || camelCase == null) {
@@ -19,6 +19,7 @@ export declare class QueryLanguageService implements IQueryLanguageService {
19
19
  evaluateBooleanExpression(expression: string, module: AdaptableModule, rowNode: any, evalContext?: {
20
20
  dataChangedEvent?: CellDataChangedInfo;
21
21
  pivotResultColumn?: Column;
22
+ columnScope?: string;
22
23
  }): any;
23
24
  evaluateScalarExpression(expression: string, module: AdaptableModule, rowNode: IRowNode): any;
24
25
  evaluateAggregatedScalarExpression(expression: string, module: AdaptableModule, getRowNodes?: () => IRowNode[]): ScalarAggregationParameter;
@@ -54,4 +55,5 @@ export declare class QueryLanguageService implements IQueryLanguageService {
54
55
  private getBooleanAndScalarFunctions;
55
56
  private getExpressionCacheKey;
56
57
  getNodesFromExpression(input: string, nodeType: string): string[];
58
+ validateCircularQueryReferences(expression: string, module: AdaptableModule): void;
57
59
  }
@@ -36,6 +36,7 @@ class QueryLanguageService {
36
36
  evaluateCustomQueryVariable: this.evaluateCustomQueryVariable,
37
37
  dataChangedEvent: evalContext?.dataChangedEvent,
38
38
  pivotResultColumn: evalContext?.pivotResultColumn,
39
+ columnScope: evalContext?.columnScope,
39
40
  ...this.adaptableApi.internalApi.buildBaseContext(),
40
41
  });
41
42
  }
@@ -120,14 +121,7 @@ class QueryLanguageService {
120
121
  this.cacheBooleanValidation.set(cacheKey, result);
121
122
  return result;
122
123
  }
123
- // evaluating the expression is the only way to catch circular named query references
124
- const firstRowNode = this.adaptableApi.gridApi.getFirstRowNode();
125
- // Mock datachangedevent for change-based functions e.g. PERCENT_CHANGE
126
- const dataChangedEvent = {
127
- newValue: 100,
128
- oldValue: 150,
129
- };
130
- this.evaluateBooleanExpression(expression, module, firstRowNode, { dataChangedEvent });
124
+ this.validateCircularQueryReferences(expression, module);
131
125
  const result = {
132
126
  isValid: true,
133
127
  errorMessage: '',
@@ -278,6 +272,8 @@ class QueryLanguageService {
278
272
  const columnFriendlyName = this.adaptableApi.columnApi.getFriendlyNameForColumnId(columnId);
279
273
  result = result.split(columnId).join(columnFriendlyName);
280
274
  });
275
+ // replace $SCOPE() with friendly name as well
276
+ result = result.split(`$SCOPE()`).join(`"<Column Scope>"`);
281
277
  return result;
282
278
  }
283
279
  // Returns the ExpressionFunctions available for the given Module as specified in the `QueryLanguageOptions.moduleExpressionFunctions`
@@ -547,5 +543,21 @@ class QueryLanguageService {
547
543
  return [];
548
544
  }
549
545
  }
546
+ validateCircularQueryReferences(expression, module) {
547
+ if (!expression || expression.trim() === '') {
548
+ return;
549
+ }
550
+ const queryNodes = this.getNodesFromExpression(expression, 'QUERY');
551
+ if (!queryNodes.length) {
552
+ return;
553
+ }
554
+ // circular QUERY references can only be detected by actually evaluating the expression
555
+ // we just evaluate against the first row, as we don't care about the result, just about
556
+ // whether an exception is thrown
557
+ const firstRowNode = this.adaptableApi.gridApi.getFirstRowNode();
558
+ queryNodes.forEach((queryNode) => {
559
+ this.evaluateBooleanExpression(`QUERY("${queryNode}")`, module, firstRowNode);
560
+ });
561
+ }
550
562
  }
551
563
  exports.QueryLanguageService = QueryLanguageService;
@@ -32,7 +32,9 @@ class ValidationService {
32
32
  cellDataChangedInfo.rowNode != null &&
33
33
  this.adaptableApi.internalApi
34
34
  .getQueryLanguageService()
35
- .evaluateBooleanExpression(alertDefinition.Rule.BooleanExpression, ModuleConstants_1.AlertModuleId, cellDataChangedInfo.rowNode);
35
+ .evaluateBooleanExpression(alertDefinition.Rule.BooleanExpression, ModuleConstants_1.AlertModuleId, cellDataChangedInfo.rowNode, {
36
+ columnScope: cellDataChangedInfo.column?.columnId,
37
+ });
36
38
  }
37
39
  catch (error) {
38
40
  isSatisfiedExpression = false;
@@ -152,7 +152,7 @@ const EntityRulesEditor = (props) => {
152
152
  React.createElement(EntityRulePredicatesEditor_1.EntityRulePredicatesEditor, { enablePredicateColumnId: props.enablePredicateColumnId, data: data, descriptions: descriptions, predicateDefs: filteredPredicateDefs, getPredicateDefsForColId: props.getPredicateDefsForColId, onChange: props.onChange }))) : null,
153
153
  showBoolean ? (React.createElement(QueryTab, { showRadio: showRadioButtons, value: "BooleanExpression", type: type, label: "Boolean" })) : null,
154
154
  showBoolean ? (React.createElement(Tabs_1.Tabs.Content, { "data-name": "BooleanExpression", value: 'BooleanExpression' }, (() => {
155
- const editor = (React.createElement(ExpressionEditor_1.ExpressionEditor, { type: 'boolean', module: module, value: data.Rule?.BooleanExpression, onChange: setBooleanExpression, initialData: initialData, columns: api.columnApi.internalApi.getQueryableColumnsForUIEditor(), fields: api.expressionApi.internalApi.getAvailableFields(), namedQueries: api.namedQueryApi.getNamedQueries(), api: api, showQueryBuilder: props.showQueryBuilder }));
155
+ const editor = (React.createElement(ExpressionEditor_1.ExpressionEditor, { type: 'boolean', module: module, value: data.Rule?.BooleanExpression, onChange: setBooleanExpression, initialData: initialData, columns: api.columnApi.internalApi.getQueryableColumnsForUIEditor(), fields: api.expressionApi.internalApi.getAvailableFields(), namedQueries: api.namedQueryApi.getNamedQueries(), api: api, showQueryBuilder: props.showQueryBuilder, columnScope: data.Scope }));
156
156
  return props.showQueryBuilder ? React.createElement(Panel_1.default, null, editor) : editor;
157
157
  })())) : null,
158
158
  showObservable ? (React.createElement(QueryTab, { showRadio: showRadioButtons, value: "ObservableExpression", type: type, label: "Observable" })) : null,
@@ -30,6 +30,14 @@ const ModuleValueSelector = (props) => {
30
30
  const sortedOptions = (0, react_1.useMemo)(() => {
31
31
  return ArrayExtensions_1.default.sortArrayWithOrder(options, value, { sortUnorderedItems: true });
32
32
  }, [allowReorder, options, value]);
33
- return (React.createElement(ValueSelector_1.ValueSelector, { ...valueSelectorProps, options: sortedOptions, value: value, onChange: onChange, allowReorder: allowReorder, toIdentifier: (module) => module, toLabel: (module) => moduleLabelMap.get(module), isOptionDisabled: props.isOptionDisabled, disabled: props.disabled }));
33
+ const moduleFilter = (0, react_1.useCallback)((module, searchText) => {
34
+ // the module name is camelCased so I want to split it
35
+ // so we can also use spaces in the search text
36
+ const parts = module.split(/(?=[A-Z])/);
37
+ const withSpaces = parts.join(' ');
38
+ return (module.toLowerCase().includes(searchText.toLowerCase()) ||
39
+ withSpaces.toLowerCase().includes(searchText.toLowerCase()));
40
+ }, []);
41
+ return (React.createElement(ValueSelector_1.ValueSelector, { ...valueSelectorProps, options: sortedOptions, value: value, onChange: onChange, showFilterInput: true, filter: moduleFilter, allowReorder: allowReorder, toIdentifier: (module) => module, toLabel: (module) => moduleLabelMap.get(module), isOptionDisabled: props.isOptionDisabled, disabled: props.disabled }));
34
42
  };
35
43
  exports.ModuleValueSelector = ModuleValueSelector;