@adaptabletools/adaptable 15.0.0-canary.1 → 15.0.0-canary.2

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 (79) hide show
  1. package/bundle.cjs.js +210 -210
  2. package/package.json +1 -1
  3. package/publishTimestamp.d.ts +1 -1
  4. package/publishTimestamp.js +1 -1
  5. package/src/AdaptableInterfaces/IAdaptable.d.ts +2 -2
  6. package/src/AdaptableOptions/ActionOptions.d.ts +30 -30
  7. package/src/AdaptableOptions/AdaptableOptions.d.ts +13 -13
  8. package/src/AdaptableOptions/AdaptableQLOptions.d.ts +4 -4
  9. package/src/AdaptableOptions/AlertOptions.d.ts +14 -14
  10. package/src/AdaptableOptions/ChartingOptions.d.ts +2 -3
  11. package/src/AdaptableOptions/DataChangeHistoryOptions.d.ts +6 -6
  12. package/src/AdaptableOptions/EditOptions.d.ts +8 -8
  13. package/src/AdaptableOptions/ExportOptions.d.ts +1 -1
  14. package/src/AdaptableOptions/ExpressionOptions.d.ts +1 -1
  15. package/src/AdaptableOptions/FilterOptions.d.ts +3 -3
  16. package/src/AdaptableOptions/GeneralOptions.d.ts +9 -9
  17. package/src/AdaptableOptions/MenuOptions.d.ts +3 -3
  18. package/src/AdaptableOptions/PredicateOptions.d.ts +1 -1
  19. package/src/AdaptableOptions/SearchOptions.d.ts +2 -2
  20. package/src/AdaptableOptions/UserInterfaceOptions.d.ts +23 -23
  21. package/src/Api/ChartingApi.d.ts +18 -19
  22. package/src/Api/EventApi.d.ts +13 -1
  23. package/src/Api/Events/ActionRowSubmitted.d.ts +7 -7
  24. package/src/Api/Events/GridDataChanged.d.ts +3 -3
  25. package/src/Api/Events/ThemeChanged.d.ts +6 -0
  26. package/src/Api/GridApi.d.ts +4 -4
  27. package/src/Api/Implementation/ColumnApiImpl.d.ts +1 -1
  28. package/src/Api/Implementation/ThemeApiImpl.d.ts +4 -0
  29. package/src/Api/Implementation/ThemeApiImpl.js +14 -0
  30. package/src/Api/OptionsApi.d.ts +1 -1
  31. package/src/Api/ThemeApi.d.ts +18 -0
  32. package/src/PredefinedConfig/Common/AdaptableAlert.d.ts +7 -7
  33. package/src/PredefinedConfig/Common/AdaptableColumn.d.ts +2 -2
  34. package/src/PredefinedConfig/Common/AdaptableComparerFunction.d.ts +4 -4
  35. package/src/PredefinedConfig/Common/CellDataChangedInfo.d.ts +4 -4
  36. package/src/PredefinedConfig/Common/CellSummary.d.ts +4 -4
  37. package/src/PredefinedConfig/Common/Menu.d.ts +8 -8
  38. package/src/PredefinedConfig/Selection/GridCell.d.ts +3 -3
  39. package/src/PredefinedConfig/Selection/GridRow.d.ts +3 -3
  40. package/src/PredefinedConfig/Selection/SelectedCellInfo.d.ts +3 -3
  41. package/src/PredefinedConfig/Selection/SelectedRowInfo.d.ts +2 -2
  42. package/src/PredefinedConfig/StatusBarState.d.ts +1 -1
  43. package/src/PredefinedConfig/StatusBarState.js +1 -1
  44. package/src/PredefinedConfig/SystemState.d.ts +1 -2
  45. package/src/PredefinedConfig/ThemeState.d.ts +4 -0
  46. package/src/Redux/ActionsReducers/GridRedux.d.ts +2 -2
  47. package/src/Redux/ActionsReducers/ThemeRedux.d.ts +27 -0
  48. package/src/Redux/ActionsReducers/ThemeRedux.js +63 -1
  49. package/src/Strategy/ExportModule.js +1 -1
  50. package/src/Utilities/Constants/GeneralConstants.d.ts +1 -0
  51. package/src/Utilities/Constants/GeneralConstants.js +3 -2
  52. package/src/Utilities/Defaults/DefaultAdaptableOptions.js +1 -1
  53. package/src/Utilities/Defaults/DefaultSettingsPanel.js +2 -2
  54. package/src/Utilities/ObjectFactory.d.ts +3 -1
  55. package/src/Utilities/ObjectFactory.js +10 -1
  56. package/src/Utilities/Services/Interface/IThemeService.d.ts +3 -0
  57. package/src/Utilities/Services/Interface/IThemeService.js +2 -0
  58. package/src/Utilities/Services/MetamodelService.d.ts +2 -0
  59. package/src/Utilities/Services/MetamodelService.js +26 -19
  60. package/src/Utilities/Services/ThemeService.d.ts +12 -0
  61. package/src/Utilities/Services/ThemeService.js +49 -0
  62. package/src/View/AdaptableWizardView/AdaptableConfigurationDialog/AdaptableOptionsForm.js +5 -5
  63. package/src/View/Charting/ShowChartButton.js +2 -1
  64. package/src/View/Charting/useChartingElements.js +2 -1
  65. package/src/View/Components/FilterForm/QuickFilterForm.js +1 -1
  66. package/src/View/Components/ToolPanel/AdaptableToolPanel.js +1 -1
  67. package/src/View/DataChangeHistory/buildActionColumnButton.d.ts +1 -1
  68. package/src/View/Theme/ThemeEditor.d.ts +7 -0
  69. package/src/View/Theme/ThemeEditor.js +186 -0
  70. package/src/View/Theme/ThemePopup.d.ts +3 -0
  71. package/src/View/Theme/ThemePopup.js +30 -6
  72. package/src/agGrid/Adaptable.d.ts +3 -2
  73. package/src/agGrid/Adaptable.js +8 -17
  74. package/src/agGrid/createAgStatusPanelComponent.js +1 -1
  75. package/src/metamodel/adaptable-metamodel-model.d.ts +9 -9
  76. package/src/metamodel/adaptable.metamodel.d.ts +1698 -3141
  77. package/src/metamodel/adaptable.metamodel.js +1 -1
  78. package/version.d.ts +1 -1
  79. package/version.js +1 -1
@@ -7,14 +7,7 @@ const DefaultAdaptableOptions_1 = require("../Defaults/DefaultAdaptableOptions")
7
7
  const LoggingHelper_1 = require("../Helpers/LoggingHelper");
8
8
  const DocumentationLinkConstants_1 = require("../Constants/DocumentationLinkConstants");
9
9
  const StringExtensions_1 = tslib_1.__importDefault(require("../Extensions/StringExtensions"));
10
- const supportedMetamodelTypes = [
11
- 'string',
12
- 'number',
13
- 'boolean',
14
- 'array',
15
- 'function',
16
- 'REFERENCE',
17
- ];
10
+ const supportedMetamodelTypes = ['s', 'n', 'b', 'a', 'f', 'R'];
18
11
  class MetamodelService {
19
12
  constructor(getAdaptableOptions) {
20
13
  this.gridInfoOptions = new Map();
@@ -57,23 +50,23 @@ class MetamodelService {
57
50
  validateOptionsObject(validationErrors, optionsObjectName, optionsObject, optionsObjectMetamodel, optionsObjectDefaultValues) {
58
51
  Object.entries(optionsObject).forEach(([optionKey, optionValue]) => {
59
52
  var _a;
60
- const optionMetamodel = (_a = optionsObjectMetamodel === null || optionsObjectMetamodel === void 0 ? void 0 : optionsObjectMetamodel.properties) === null || _a === void 0 ? void 0 : _a.find((metamodelProperty) => metamodelProperty.name === optionKey);
53
+ const optionMetamodel = (_a = optionsObjectMetamodel === null || optionsObjectMetamodel === void 0 ? void 0 : optionsObjectMetamodel.props) === null || _a === void 0 ? void 0 : _a.find((metamodelProperty) => metamodelProperty.name === optionKey);
61
54
  if (!optionMetamodel) {
62
55
  validationErrors.push(`${optionsObjectName}.${optionKey} (value=${optionValue}) :: unknown/unsupported property, will be ignored`);
63
56
  return;
64
57
  }
65
58
  // let's try to validate the type of the provided value
66
59
  const expectedOptionsValueType = this.getExpectedOptionsValueType(optionMetamodel);
67
- const providedOptionsValueType = Array.isArray(optionValue) ? 'array' : typeof optionValue;
60
+ const providedOptionsValueType = Array.isArray(optionValue) ? 'a' : typeof optionValue;
68
61
  if (!expectedOptionsValueType) {
69
62
  return;
70
63
  }
71
64
  // if it's a REFERENCE, we try to go (recursively) deeper
72
- if (expectedOptionsValueType === 'REFERENCE') {
73
- const referenceObjectName = optionMetamodel.reference;
65
+ if (expectedOptionsValueType === 'R') {
66
+ const referenceObjectName = optionMetamodel.ref;
74
67
  const referenceObject = optionsObject[optionKey];
75
68
  const referenceObjectMetamodel = this.getAdaptableMetamodel()[referenceObjectName];
76
- if (referenceObject && (referenceObjectMetamodel === null || referenceObjectMetamodel === void 0 ? void 0 : referenceObjectMetamodel.kind) === 'Interface') {
69
+ if (referenceObject && (referenceObjectMetamodel === null || referenceObjectMetamodel === void 0 ? void 0 : referenceObjectMetamodel.kind) === 'I') {
77
70
  this.validateOptionsObject(validationErrors, referenceObjectName, referenceObject, referenceObjectMetamodel, optionsObjectDefaultValues === null || optionsObjectDefaultValues === void 0 ? void 0 : optionsObjectDefaultValues[optionKey]);
78
71
  }
79
72
  }
@@ -107,10 +100,10 @@ class MetamodelService {
107
100
  items: baseOptionsItems,
108
101
  });
109
102
  // map containers
110
- adaptableOptionsMetamodel.properties
103
+ adaptableOptionsMetamodel.props
111
104
  .filter((optionItem) => optionItem.gridInfo === 'container')
112
105
  .forEach((containerOptionItem) => {
113
- const containerMetamodelName = containerOptionItem.reference;
106
+ const containerMetamodelName = containerOptionItem.ref;
114
107
  const adaptableOptionsName = containerOptionItem.name;
115
108
  const containerOptionsMetamodel = adaptableMetamodel[containerMetamodelName];
116
109
  // @ts-ignore
@@ -125,21 +118,21 @@ class MetamodelService {
125
118
  }
126
119
  const optionItems = this.mapGridInfoContainerItems(containerOptionsMetamodel, containerOptionsValues, containerOptionsDefaultValues, filterItemProperty);
127
120
  gridInfoOptions.set(containerOptionItem.name, {
128
- containerLabel: containerOptionItem.uiLabel,
121
+ containerLabel: this.extractUiLabel(containerOptionItem),
129
122
  items: optionItems,
130
123
  });
131
124
  });
132
125
  return gridInfoOptions;
133
126
  }
134
127
  mapGridInfoContainerItems(optionItemContainer, adaptableOptionsValues, defaultAdaptableOptionsValues, filter = (itemProperty) => itemProperty.gridInfo === 'item') {
135
- return optionItemContainer.properties.filter(filter).map((itemProperty) => {
128
+ return optionItemContainer.props.filter(filter).map((itemProperty) => {
136
129
  return {
137
130
  name: itemProperty.name,
138
131
  value: adaptableOptionsValues === null || adaptableOptionsValues === void 0 ? void 0 : adaptableOptionsValues[itemProperty.name],
139
132
  defaultValue: defaultAdaptableOptionsValues[itemProperty.name],
140
133
  kind: itemProperty.kind,
141
- description: StringExtensions_1.default.UnescapeHtmlEntities(itemProperty.description),
142
- uiLabel: itemProperty.uiLabel,
134
+ description: StringExtensions_1.default.UnescapeHtmlEntities(itemProperty.desc),
135
+ uiLabel: this.extractUiLabel(itemProperty),
143
136
  };
144
137
  });
145
138
  }
@@ -152,5 +145,19 @@ class MetamodelService {
152
145
  getAdaptableOptionsMetamodel() {
153
146
  return this.getAdaptableMetamodel()['AdaptableOptions'];
154
147
  }
148
+ extractUiLabel(item) {
149
+ return item.uiLabel || this.formatCamelCaseToHumanText(item.name);
150
+ }
151
+ formatCamelCaseToHumanText(camelCase) {
152
+ if (!camelCase || camelCase == null) {
153
+ return null;
154
+ }
155
+ const rex = /([A-Z])([A-Z])([a-z])|([a-z])([A-Z])/g;
156
+ const words = camelCase.replace(rex, '$1$4 $2$3$5').replace('.', ' ').split(' ');
157
+ return words
158
+ .map((word) => word.substring(0, 1).toUpperCase() +
159
+ (word.length > 1 ? word.substring(1, word.length) : ''))
160
+ .join(' ');
161
+ }
155
162
  }
156
163
  exports.MetamodelService = MetamodelService;
@@ -0,0 +1,12 @@
1
+ import { AdaptableApi, AdaptableTheme } from '../../types';
2
+ import { IThemeService } from './Interface/IThemeService';
3
+ export declare class ThemeService implements IThemeService {
4
+ private api;
5
+ private unsubscribe;
6
+ private styleSheetObject;
7
+ constructor(api: AdaptableApi);
8
+ subscribe(): void;
9
+ onThemeChanged: () => void;
10
+ applyNewThemeVariables(theme: AdaptableTheme): void;
11
+ destroy(): void;
12
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ThemeService = void 0;
4
+ class ThemeService {
5
+ constructor(api) {
6
+ var _a;
7
+ this.unsubscribe = () => { };
8
+ this.onThemeChanged = () => {
9
+ const currentTheme = this.api.themeApi.getCurrentThemeObject();
10
+ this.applyNewThemeVariables(currentTheme);
11
+ };
12
+ this.api = api;
13
+ this.subscribe();
14
+ if (!this.styleSheetObject) {
15
+ this.styleSheetObject = new CSSStyleSheet();
16
+ // ts does not know about adoptedStyleSheets
17
+ document.adoptedStyleSheets = [
18
+ ...((_a = document.adoptedStyleSheets) !== null && _a !== void 0 ? _a : []),
19
+ this.styleSheetObject,
20
+ ];
21
+ }
22
+ }
23
+ subscribe() {
24
+ const themeChangedUnsubscribe = this.api.eventApi.on('ThemeChanged', this.onThemeChanged);
25
+ const themeEditedUnsubscribe = this.api.eventApi.on('ThemeEdited', this.onThemeChanged);
26
+ this.unsubscribe = () => {
27
+ themeChangedUnsubscribe();
28
+ themeEditedUnsubscribe();
29
+ };
30
+ }
31
+ applyNewThemeVariables(theme) {
32
+ var _a;
33
+ const variables = (_a = theme.CSSVariables) !== null && _a !== void 0 ? _a : {};
34
+ let str = ':root {';
35
+ for (const [key, value] of Object.entries(variables)) {
36
+ if (key.includes('--')) {
37
+ str += `${key}: ${value};`;
38
+ }
39
+ }
40
+ str += '}';
41
+ this.styleSheetObject.replaceSync(str);
42
+ }
43
+ destroy() {
44
+ this.api = null;
45
+ this.unsubscribe();
46
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets].filter((sheet) => sheet !== this.styleSheetObject);
47
+ }
48
+ }
49
+ exports.ThemeService = ThemeService;
@@ -12,7 +12,7 @@ const StringExtensions_1 = tslib_1.__importDefault(require("../../../Utilities/E
12
12
  const MetamodelService_1 = require("../../../Utilities/Services/MetamodelService");
13
13
  const AdaptablePopover_1 = require("../../AdaptablePopover");
14
14
  const LABEL_WIDTH = 250;
15
- const SUPPORTED_PRIMITEVE_TYPES = ['string', 'number', 'boolean'];
15
+ const SUPPORTED_PRIMITEVE_TYPES = ['s', 'n', 'b'];
16
16
  const OptionInput = (props) => {
17
17
  const { name, defaultValue, value, description, uiLabel } = props.option;
18
18
  const label = uiLabel !== null && uiLabel !== void 0 ? uiLabel : StringExtensions_1.default.Humanize(name);
@@ -54,13 +54,13 @@ const OptionInput = (props) => {
54
54
  }
55
55
  else {
56
56
  switch (option.kind) {
57
- case 'number':
57
+ case 'n':
58
58
  input = React.createElement(Input_1.default, Object.assign({ type: "number", onChange: handleInputChange }, inputProps));
59
59
  break;
60
- case 'string':
60
+ case 's':
61
61
  input = React.createElement(Input_1.default, Object.assign({ type: "text", onChange: handleInputChange }, inputProps));
62
62
  break;
63
- case 'boolean':
63
+ case 'b':
64
64
  input = (React.createElement(CheckBox_1.CheckBox, { onChange: handleCheckboxChange, checked: Boolean(value), type: "date" }, label));
65
65
  break;
66
66
  }
@@ -71,7 +71,7 @@ const OptionInput = (props) => {
71
71
  " ",
72
72
  info),
73
73
  input));
74
- if (option.kind === 'boolean') {
74
+ if (option.kind === 'b') {
75
75
  content = (React.createElement(rebass_1.Flex, { alignItems: "center" },
76
76
  input,
77
77
  " ",
@@ -7,6 +7,7 @@ const SimpleButton_1 = tslib_1.__importDefault(require("../../components/SimpleB
7
7
  const AdaptableContext_1 = require("../AdaptableContext");
8
8
  const DropdownButton_1 = tslib_1.__importDefault(require("../../components/DropdownButton"));
9
9
  const useChartState_1 = require("./useChartState");
10
+ const GeneralConstants_1 = require("../../Utilities/Constants/GeneralConstants");
10
11
  const ShowChartButton = (props) => {
11
12
  var _a, _b;
12
13
  const adaptable = (0, AdaptableContext_1.useAdaptable)();
@@ -21,7 +22,7 @@ const ShowChartButton = (props) => {
21
22
  }
22
23
  const containerOptions = [
23
24
  {
24
- label: (_b = chartingOptions.agGridContainerName) !== null && _b !== void 0 ? _b : 'Ag Grid Window',
25
+ label: (_b = chartingOptions.agGridContainerName) !== null && _b !== void 0 ? _b : GeneralConstants_1.AG_GRID_CHART_WINDOW,
25
26
  onClick: () => {
26
27
  showChart(null);
27
28
  },
@@ -12,6 +12,7 @@ const AdaptableHelper_1 = tslib_1.__importDefault(require("../../Utilities/Helpe
12
12
  const useChartState_1 = require("./useChartState");
13
13
  const DeleteChartButton_1 = require("./DeleteChartButton");
14
14
  const EditChartButton_1 = require("./EditChartButton");
15
+ const GeneralConstants_1 = require("../../Utilities/Constants/GeneralConstants");
15
16
  const useChartingElements = ({ elementType, accessLevel, size = 'normal', }) => {
16
17
  var _a, _b;
17
18
  const adaptable = (0, AdaptableContext_1.useAdaptable)();
@@ -75,7 +76,7 @@ const useChartingElements = ({ elementType, accessLevel, size = 'normal', }) =>
75
76
  iconSize = 15;
76
77
  }
77
78
  const chartSelector = (React.createElement(DropdownButton_1.default, { style: style, width: "100%", columns: ['label'], className: `ab-${elementType}__Chart__select`, items: options, disabled: !hasCharts }, content));
78
- const containerSelector = Boolean(chartContainers === null || chartContainers === void 0 ? void 0 : chartContainers.length) && (React.createElement(DropdownButton_1.default, { style: style, width: "100%", columns: ['label'], items: containerOptions, disabled: !isSelectedChart }, (selectedContainer === null || selectedContainer === void 0 ? void 0 : selectedContainer.name) || chartingOptions.agGridContainerName || 'Ag Grid Window'));
79
+ const containerSelector = Boolean(chartContainers === null || chartContainers === void 0 ? void 0 : chartContainers.length) && (React.createElement(DropdownButton_1.default, { style: style, width: "100%", columns: ['label'], items: containerOptions, disabled: !isSelectedChart }, (selectedContainer === null || selectedContainer === void 0 ? void 0 : selectedContainer.name) || chartingOptions.agGridContainerName || GeneralConstants_1.AG_GRID_CHART_WINDOW));
79
80
  const chartButton = (React.createElement(SimpleButton_1.default, { style: style, mr: 1, onClick: () => (isOpen ? closeChart() : showChart(selectedContainer)), disabled: !Boolean(selectedChart), variant: 'text', tone: 'neutral', icon: isOpen ? 'visibility-off' : 'visibility', tooltip: isOpen ? 'Hide Chart' : 'Show Chart' }));
80
81
  const deleteButton = (React.createElement(DeleteChartButton_1.DeleteChartButton, { iconSize: iconSize, chart: selectedChart, accessLevel: chartAccessLevel }));
81
82
  const editButton = (React.createElement(EditChartButton_1.EditChartButton, { iconSize: iconSize, chart: selectedChart, accessLevel: chartAccessLevel, isOpen: isOpen }));
@@ -206,7 +206,7 @@ class QuickFilterFormComponent extends React.Component {
206
206
  var _a, _b;
207
207
  return (React.createElement(AdaptableInput_1.default, { disabled: this.isFilterDisabled(), key: index, type: predicateInput.type === 'number' ? 'text' : predicateInput.type,
208
208
  // autoFocus has to be FALSE because if the input receives focus in the init phase,
209
- // it may scroll the ag-grid header viewport into view and de-synchronize it (relative to the content viewport)
209
+ // it may scroll the AG Grid header viewport into view and de-synchronize it (relative to the content viewport)
210
210
  autoFocus: false, value: (_b = (_a = filter.Predicate.Inputs) === null || _a === void 0 ? void 0 : _a[index]) !== null && _b !== void 0 ? _b : '', onChange: (e) => this.changeColumnPredicateInput(e.target.value, index), onKeyDownCapture: (e) => {
211
211
  if (e.nativeEvent.key === 'Escape') {
212
212
  e.nativeEvent.preventDefault();
@@ -244,7 +244,7 @@ const getAdaptableToolPanelAgGridComponent = (adaptable) => {
244
244
  const api = adaptable.api;
245
245
  this.gui = document.createElement('div');
246
246
  this.gui.id = getContainerId();
247
- // preserve ag-grid naming convention
247
+ // preserve AG Grid naming convention
248
248
  this.gui.className = 'ag-adaptable-panel';
249
249
  Object.keys(toolPanelContainerStyle).forEach((key) => {
250
250
  //@ts-ignore
@@ -1,3 +1,3 @@
1
1
  import { IRowNode } from '@ag-grid-community/core';
2
2
  import { ActionColumnContext, AdaptableButton, DataChangeHistoryOptions } from '../../types';
3
- export declare const buildActionColumnButton: (options: DataChangeHistoryOptions, undoRowNode: (rowNode: IRowNode) => void) => AdaptableButton<ActionColumnContext>[];
3
+ export declare const buildActionColumnButton: (options: DataChangeHistoryOptions, undoRowNode: (rowNode: IRowNode) => void) => AdaptableButton<ActionColumnContext<any>>[];
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ import { AccessLevel } from '../../types';
3
+ export interface ThemeEditorProps {
4
+ theme: string;
5
+ accessLevel: AccessLevel;
6
+ }
7
+ export declare const ThemeEditor: React.FunctionComponent<ThemeEditorProps>;
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ThemeEditor = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const React = tslib_1.__importStar(require("react"));
6
+ const ColorPicker_1 = require("../../components/ColorPicker");
7
+ const FormLayout_1 = tslib_1.__importStar(require("../../components/FormLayout"));
8
+ const StyleHelper_1 = require("../../Utilities/Helpers/StyleHelper");
9
+ const AdaptableContext_1 = require("../AdaptableContext");
10
+ const AdaptableInput_1 = tslib_1.__importDefault(require("../Components/AdaptableInput"));
11
+ const throttle_1 = tslib_1.__importDefault(require("lodash/throttle"));
12
+ const SimpleButton_1 = tslib_1.__importDefault(require("../../components/SimpleButton"));
13
+ const rebass_1 = require("rebass");
14
+ const Panel_1 = tslib_1.__importDefault(require("../../components/Panel"));
15
+ const fields = [
16
+ {
17
+ name: 'Primary Color',
18
+ type: 'color',
19
+ variable: '--ab-color-primary',
20
+ },
21
+ {
22
+ name: 'Primary Dark Color',
23
+ type: 'color',
24
+ variable: '--ab-color-primarydark',
25
+ },
26
+ {
27
+ name: 'Primary Light Color',
28
+ type: 'color',
29
+ variable: '--ab-color-primarylight',
30
+ },
31
+ {
32
+ name: 'Text on Primary Color',
33
+ type: 'color',
34
+ variable: '--ab-color-text-on-primary',
35
+ },
36
+ {
37
+ name: 'Text on Primary Dark Color',
38
+ type: 'color',
39
+ variable: '--ab-color-text-on-primarydark',
40
+ },
41
+ {
42
+ name: 'Secondary Color',
43
+ type: 'color',
44
+ variable: '--ab-color-secondary',
45
+ },
46
+ {
47
+ name: 'Secondary Dark Color',
48
+ type: 'color',
49
+ variable: '--ab-color-secondarydark',
50
+ },
51
+ {
52
+ name: 'Secondary Light Color',
53
+ type: 'color',
54
+ variable: '--ab-color-secondarylight',
55
+ },
56
+ {
57
+ name: 'Text on Secondary Color',
58
+ type: 'color',
59
+ variable: '--ab-color-text-on-secondary',
60
+ },
61
+ {
62
+ name: 'Text on Secondary Light Color',
63
+ type: 'color',
64
+ variable: '--ab-color-text-on-secondarylight',
65
+ },
66
+ {
67
+ name: 'Default background',
68
+ type: 'color',
69
+ variable: '--ab-color-defaultbackground',
70
+ },
71
+ {
72
+ name: 'Text Color on Default Background',
73
+ type: 'color',
74
+ variable: '--ab-color-text-on-defaultbackground',
75
+ },
76
+ ];
77
+ const Field = (props) => {
78
+ const adaptable = (0, AdaptableContext_1.useAdaptable)();
79
+ let input = null;
80
+ let value = props.value;
81
+ const throttledOnChange = React.useMemo(() => {
82
+ return (0, throttle_1.default)((value) => {
83
+ props.onChange(value);
84
+ }, 300);
85
+ }, []);
86
+ if (value === undefined) {
87
+ value = (0, StyleHelper_1.getVariableColor)(`var(${props.variable})`);
88
+ }
89
+ switch (props.type) {
90
+ case 'number':
91
+ input = (React.createElement(AdaptableInput_1.default, { disabled: props.disabled, type: "number", onChange: (event) => throttledOnChange(event.target.value), value: value }));
92
+ break;
93
+ case 'color':
94
+ input = (React.createElement(ColorPicker_1.ColorPicker, { disabled: props.disabled, includeAlpha: false, api: adaptable.api, value: value, onChange: (color) => throttledOnChange(color) }));
95
+ break;
96
+ }
97
+ return (React.createElement(FormLayout_1.FormRow, { label: props.name },
98
+ React.createElement(rebass_1.Flex, null,
99
+ input,
100
+ ' ',
101
+ React.createElement(SimpleButton_1.default, { disabled: props.value === undefined, onClick: () => props.onChange(false) }, "Clear"))));
102
+ };
103
+ const ThemeEditor = (props) => {
104
+ var _a, _b;
105
+ const adaptable = (0, AdaptableContext_1.useAdaptable)();
106
+ const allThemes = adaptable.api.themeApi.getUserThemes();
107
+ const disabled = props.accessLevel === 'ReadOnly';
108
+ const [currentThemeObject, setCurrentThemeObject] = React.useState(() => {
109
+ return adaptable.api.themeApi.getCurrentThemeObject();
110
+ });
111
+ // THEME variables
112
+ const themeVariables = currentThemeObject === null || currentThemeObject === void 0 ? void 0 : currentThemeObject.CSSVariables;
113
+ const valuesFromTheme = fields.reduce((acc, field) => {
114
+ if (themeVariables === null || themeVariables === void 0 ? void 0 : themeVariables[field.variable]) {
115
+ acc[field.variable] = themeVariables === null || themeVariables === void 0 ? void 0 : themeVariables[field.variable];
116
+ }
117
+ return acc;
118
+ }, {});
119
+ React.useEffect(() => {
120
+ setCurrentThemeObject(adaptable.api.themeApi.getCurrentThemeObject());
121
+ }, [props.theme]);
122
+ // This is needed when clearing variables, so the new value is pickedup up from the document
123
+ React.useEffect(() => {
124
+ const updateTheme = () => {
125
+ const theme = adaptable.api.themeApi.getCurrentThemeObject();
126
+ setCurrentThemeObject(Object.assign({}, theme));
127
+ };
128
+ const themeChangedUnsubscribe = adaptable.api.eventApi.on('ThemeChanged', updateTheme);
129
+ const themeEditedUnsubscribe = adaptable.api.eventApi.on('ThemeEdited', updateTheme);
130
+ return () => {
131
+ themeChangedUnsubscribe();
132
+ themeEditedUnsubscribe();
133
+ };
134
+ }, []);
135
+ const handleNameDescriptionChangeThrottled = React.useMemo(() => {
136
+ return (0, throttle_1.default)((key, value) => {
137
+ const theme = adaptable.api.themeApi.getCurrentThemeObject();
138
+ const newTheme = Object.assign(Object.assign({}, theme), { [key]: value });
139
+ adaptable.api.themeApi.editTheme(newTheme);
140
+ }, 600);
141
+ }, []);
142
+ const handleDescriptionChange = React.useCallback((key, value) => {
143
+ setCurrentThemeObject((prev) => (Object.assign(Object.assign({}, prev), { [key]: value })));
144
+ handleNameDescriptionChangeThrottled(key, value);
145
+ }, []);
146
+ const handleDeleteTheme = React.useCallback(() => {
147
+ adaptable.api.themeApi.loadLightTheme();
148
+ adaptable.api.themeApi.deleteUserTheme(currentThemeObject);
149
+ }, []);
150
+ const handleSaveName = () => {
151
+ const name = currentThemeObject.Name;
152
+ setCurrentThemeObject((prev) => (Object.assign(Object.assign({}, prev), { Name: name })));
153
+ handleNameDescriptionChangeThrottled('Name', name);
154
+ };
155
+ const nameHasChanged = currentThemeObject.Name !== props.theme;
156
+ const nameIsNotUnique = allThemes.some((theme) => theme.Uuid !== currentThemeObject.Uuid && currentThemeObject.Name === theme.Name);
157
+ const saveNameDisabled = !nameHasChanged || nameIsNotUnique || currentThemeObject.Name === '';
158
+ return (React.createElement(Panel_1.default, { header: React.createElement(rebass_1.Flex, { alignItems: "center", width: "100%" },
159
+ React.createElement(rebass_1.Box, { flex: 1 }, " Edit Custom Theme"),
160
+ React.createElement(SimpleButton_1.default, { "data-name": "delete", disabled: props.accessLevel !== 'Full', icon: "trash", onClick: handleDeleteTheme, variant: "text" })) },
161
+ React.createElement(FormLayout_1.default, null,
162
+ React.createElement(FormLayout_1.FormRow, { label: "Theme Name" },
163
+ React.createElement(AdaptableInput_1.default, { mr: 2, onChange: (event) => setCurrentThemeObject(Object.assign(Object.assign({}, currentThemeObject), { Name: event.target.value.replace(/ /g, '-') })), value: (_a = currentThemeObject.Name) !== null && _a !== void 0 ? _a : '' }),
164
+ React.createElement(SimpleButton_1.default, { onClick: handleSaveName, disabled: saveNameDisabled, icon: "save" }),
165
+ React.createElement(rebass_1.Text, { fontSize: 2, marginTop: 1 }, "The name cannot contain spaces."),
166
+ nameIsNotUnique && (React.createElement(rebass_1.Text, { fontSize: 2, color: "var(--ab-color-error)" }, "Name must be unique."))),
167
+ React.createElement(FormLayout_1.FormRow, { label: "Description" },
168
+ React.createElement(AdaptableInput_1.default, { onChange: (event) => handleDescriptionChange('Description', event.target.value), value: (_b = currentThemeObject === null || currentThemeObject === void 0 ? void 0 : currentThemeObject.Description) !== null && _b !== void 0 ? _b : '' })),
169
+ fields.map((field) => {
170
+ return (React.createElement(Field, { disabled: disabled, key: field.name, type: field.type, name: field.name, value: valuesFromTheme[field.variable], variable: field.variable, onChange: (val) => {
171
+ // needs a fresh copy
172
+ const currentThemeObject = adaptable.api.themeApi.getCurrentThemeObject();
173
+ let newTheme = null;
174
+ if (val === false) {
175
+ newTheme = Object.assign(Object.assign({}, currentThemeObject), { CSSVariables: Object.assign({}, currentThemeObject === null || currentThemeObject === void 0 ? void 0 : currentThemeObject.CSSVariables) });
176
+ delete newTheme.CSSVariables[field.variable];
177
+ }
178
+ else {
179
+ newTheme = Object.assign(Object.assign({}, currentThemeObject), { CSSVariables: Object.assign(Object.assign({}, currentThemeObject === null || currentThemeObject === void 0 ? void 0 : currentThemeObject.CSSVariables), { [field.variable]: val }) });
180
+ }
181
+ setCurrentThemeObject(newTheme);
182
+ adaptable.api.themeApi.editTheme(newTheme);
183
+ } }));
184
+ }))));
185
+ };
186
+ exports.ThemeEditor = ThemeEditor;
@@ -1,13 +1,16 @@
1
1
  import * as React from 'react';
2
2
  import * as ThemeRedux from '../../Redux/ActionsReducers/ThemeRedux';
3
3
  import { ModuleViewPopupProps } from '../Components/SharedProps/ModuleViewPopupProps';
4
+ import { AdaptableTheme } from '../../PredefinedConfig/ThemeState';
4
5
  interface ThemePopupProps extends ModuleViewPopupProps<ThemePopupComponent> {
5
6
  CurrentTheme: string;
7
+ UserThemes: AdaptableTheme[];
6
8
  SelectTheme: (newTheme: string) => ThemeRedux.ThemeSelectAction;
7
9
  }
8
10
  declare class ThemePopupComponent extends React.Component<ThemePopupProps, {}> {
9
11
  render(): JSX.Element;
10
12
  onChangeTheme(value: string): void;
13
+ handleCreateNewTheme: () => void;
11
14
  }
12
15
  export declare let ThemePopup: import("react-redux").ConnectedComponent<typeof ThemePopupComponent, any>;
13
16
  export {};
@@ -8,11 +8,31 @@ const ThemeRedux = tslib_1.__importStar(require("../../Redux/ActionsReducers/The
8
8
  const PopupPanel_1 = require("../Components/Popups/AdaptablePopup/PopupPanel");
9
9
  const FormLayout_1 = tslib_1.__importStar(require("../../components/FormLayout"));
10
10
  const DropdownButton_1 = tslib_1.__importDefault(require("../../components/DropdownButton"));
11
+ const ThemeEditor_1 = require("./ThemeEditor");
12
+ const rebass_1 = require("rebass");
13
+ const ButtonNew_1 = require("../Components/Buttons/ButtonNew");
14
+ const ObjectFactory_1 = tslib_1.__importDefault(require("../../Utilities/ObjectFactory"));
11
15
  class ThemePopupComponent extends React.Component {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.handleCreateNewTheme = () => {
19
+ let nthItem = this.props.UserThemes.length + 1;
20
+ let name = 'Custom-Theme-' + nthItem;
21
+ // make sure it is unique
22
+ while (this.props.UserThemes.some((theme) => theme.Name === name)) {
23
+ nthItem++;
24
+ name = 'Custom-Theme-' + nthItem;
25
+ }
26
+ const newTheme = ObjectFactory_1.default.CreateEmptyTheme(name);
27
+ this.props.api.themeApi.addUserTheme(newTheme);
28
+ this.props.api.themeApi.loadTheme(newTheme.Name);
29
+ };
30
+ }
12
31
  render() {
13
32
  const availableThemes = this.props.api.themeApi.getThemes();
14
- let currentThemeDescription = '';
15
- let optionThemes = availableThemes.map((theme) => {
33
+ const theme = this.props.api.themeApi.getCurrentThemeObject();
34
+ const currentThemeDescription = theme.Description || theme.Name;
35
+ const optionThemes = availableThemes.map((theme) => {
16
36
  if (typeof theme === 'string') {
17
37
  // protection against old state, which could be string
18
38
  theme = {
@@ -20,19 +40,22 @@ class ThemePopupComponent extends React.Component {
20
40
  Description: theme,
21
41
  };
22
42
  }
23
- if (theme.Name === this.props.CurrentTheme) {
24
- currentThemeDescription = theme.Description;
25
- }
26
43
  return {
27
44
  value: theme.Name,
28
45
  label: theme.Description,
29
46
  onClick: () => this.onChangeTheme(theme.Name),
30
47
  };
31
48
  });
49
+ const isCustomTheme = this.props.api.themeApi
50
+ .getUserThemes()
51
+ .some((theme) => theme.Name === this.props.CurrentTheme);
32
52
  return (React.createElement(PopupPanel_1.PopupPanel, { headerText: this.props.moduleInfo.FriendlyName, glyphicon: this.props.moduleInfo.Glyph, infoLink: this.props.moduleInfo.HelpPage, infoLinkDisabled: !this.props.api.internalApi.isDocumentationLinksDisplayed() },
33
53
  React.createElement(FormLayout_1.default, null,
34
54
  React.createElement(FormLayout_1.FormRow, { label: "Current Theme:" },
35
- React.createElement(DropdownButton_1.default, { "data-name": "select-theme-dropdown", columns: ['label'], style: { width: '50%', minWidth: 200 }, placeholder: "Select theme", value: this.props.CurrentTheme, items: optionThemes, accessLevel: this.props.accessLevel }, currentThemeDescription)))));
55
+ React.createElement(DropdownButton_1.default, { "data-name": "select-theme-dropdown", columns: ['label'], style: { width: '50%', minWidth: 200 }, placeholder: "Select theme", value: this.props.CurrentTheme, items: optionThemes, accessLevel: this.props.accessLevel }, currentThemeDescription),
56
+ React.createElement(ButtonNew_1.ButtonNew, { ml: 2, onClick: this.handleCreateNewTheme, accessLevel: this.props.accessLevel }))),
57
+ isCustomTheme && (React.createElement(rebass_1.Box, { mt: 3 },
58
+ React.createElement(ThemeEditor_1.ThemeEditor, { accessLevel: this.props.accessLevel, theme: this.props.CurrentTheme })))));
36
59
  }
37
60
  onChangeTheme(value) {
38
61
  this.props.SelectTheme(value);
@@ -41,6 +64,7 @@ class ThemePopupComponent extends React.Component {
41
64
  function mapStateToProps(state, ownProps) {
42
65
  return {
43
66
  CurrentTheme: state.Theme.CurrentTheme,
67
+ UserThemes: state.Theme.UserThemes,
44
68
  };
45
69
  }
46
70
  function mapDispatchToProps(dispatch) {
@@ -61,6 +61,7 @@ export declare class Adaptable implements IAdaptable {
61
61
  RowEditService: IRowEditService;
62
62
  private LicenseService;
63
63
  private ChartingService;
64
+ private ThemeService;
64
65
  embedColumnMenu: boolean;
65
66
  gridOptions: GridOptions;
66
67
  isInitialised: boolean;
@@ -198,8 +199,8 @@ export declare class Adaptable implements IAdaptable {
198
199
  suppressClientSideFilter?: boolean;
199
200
  gridCells: GridCell[];
200
201
  }>;
201
- getDistinctCustomSortValuesForColumn(column: AdaptableColumn, visibleRowsOnly: boolean, skipRowNode?: IRowNode): Promise<GridCell[]>;
202
- getDistinctBulkUpdateValuesForColumn(column: AdaptableColumn, visibleRowsOnly: boolean, selectedGridCells: GridCell[], skipRowNode?: IRowNode): Promise<GridCell[]>;
202
+ getDistinctCustomSortValuesForColumn(column: AdaptableColumn, visibleRowsOnly: boolean, skipRowNode?: IRowNode): Promise<GridCell<any>[]>;
203
+ getDistinctBulkUpdateValuesForColumn(column: AdaptableColumn, visibleRowsOnly: boolean, selectedGridCells: GridCell[], skipRowNode?: IRowNode): Promise<GridCell<any>[]>;
203
204
  getColumnValueDisplayValuePairList(columnId: string, visibleRowsOnly: boolean, onlyIncludeIds?: {
204
205
  [key: string]: boolean;
205
206
  }): GridCell[];