@adaptabletools/adaptable 23.0.2 → 23.0.4-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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adaptabletools/adaptable",
3
- "version": "23.0.2",
3
+ "version": "23.0.4-canary.0",
4
4
  "description": "Powerful AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements",
5
5
  "keywords": [
6
6
  "web-components",
@@ -48,7 +48,6 @@
48
48
  "@tanstack/react-virtual": "^3.13.22",
49
49
  "class-variance-authority": "^0.7.1",
50
50
  "clsx": "^2.1.1",
51
- "cron-parser": "^4.9.0",
52
51
  "date-fns": "^4.1.0",
53
52
  "lucide-react": "^0.563.0",
54
53
  "normalize.css": "^8.0.1",
@@ -7,7 +7,7 @@ import { CustomRenderContext } from '../agGrid/AdaptableFrameworkComponent';
7
7
  */
8
8
  export interface SettingsPanelOptions {
9
9
  /**
10
- * Title for the Settings Panel
10
+ * Title for Settings Panel
11
11
  *
12
12
  * @defaultValue 'Settings Panel'
13
13
  */
@@ -45,7 +45,7 @@ export interface SettingsPanelOptions {
45
45
  */
46
46
  navigation?: SettingsPanelNavigationConfigurer;
47
47
  /**
48
- * Initial position of Settings Panel window
48
+ * Initial position of Settings Panel — pixel offset of top-left corner from top-left of viewport (requires `popupType` to be `'window'`)
49
49
  *
50
50
  * @defaultValue Middle of Screen
51
51
  */
@@ -54,7 +54,7 @@ export interface SettingsPanelOptions {
54
54
  y: number;
55
55
  };
56
56
  /**
57
- * Initial size of Settings Panel
57
+ * Initial size (in pixels) of Settings Panel (requires `popupType` to be `'window'`)
58
58
  *
59
59
  * @defaultValue Computed based on size of screen
60
60
  */
@@ -63,7 +63,7 @@ export interface SettingsPanelOptions {
63
63
  height: number;
64
64
  };
65
65
  /**
66
- * Whether Settings Panel is 'window' (i.e. movable, resizable, no backdrop) or 'modal' (centre of screen with backdrop)
66
+ * How the Settings Panel is rendered: `'modal'` (centred and unmovable and resizable) or `'window'` (supports size, position, drag and resize)
67
67
  *
68
68
  * @defaultValue 'modal'
69
69
  */
@@ -38,6 +38,24 @@ export interface GridApi {
38
38
  * Retrieves visible data from the grid (filtered and sorted)
39
39
  */
40
40
  getVisibleData(): any[];
41
+ /**
42
+ * Retrieves all the values for a single Column.
43
+ *
44
+ * @param columnId the Column to read the values from
45
+ */
46
+ getColumnData(columnId: string): any[];
47
+ /**
48
+ * Retrieves the filtered values for a single Column.
49
+ *
50
+ * @param columnId the Column to read the values from
51
+ */
52
+ getFilteredColumnData(columnId: string): any[];
53
+ /**
54
+ * Retrieves the visible (filtered and sorted) values for a single Column.
55
+ *
56
+ * @param columnId the Column to read the values from
57
+ */
58
+ getVisibleColumnData(columnId: string): any[];
41
59
  /**
42
60
  * Loads data into grid and fire a `RowChanged.trigger='Load'` event
43
61
  * @param data data to load
@@ -27,6 +27,9 @@ export declare class GridApiImpl extends ApiBase implements GridApi {
27
27
  getGridData(): any[];
28
28
  getFilteredData(): any[];
29
29
  getVisibleData(): any[];
30
+ getColumnData(columnId: string): any[];
31
+ getFilteredColumnData(columnId: string): any[];
32
+ getVisibleColumnData(columnId: string): any[];
30
33
  updateGridData(dataRows: any[], dataUpdateConfig?: DataUpdateConfig): Promise<IRowNode[]>;
31
34
  addOrUpdateGridData(dataRows: any[], dataUpdateConfig?: DataUpdateConfig): Promise<{
32
35
  addedRows: IRowNode[];
@@ -47,6 +47,33 @@ export class GridApiImpl extends ApiBase {
47
47
  });
48
48
  return data;
49
49
  }
50
+ getColumnData(columnId) {
51
+ const data = [];
52
+ this._adaptable.forAllRowNodesDo((rowNode) => {
53
+ if (!this.isGroupRowNode(rowNode)) {
54
+ data.push(this.getRawValueFromRowNode(rowNode, columnId));
55
+ }
56
+ });
57
+ return data;
58
+ }
59
+ getFilteredColumnData(columnId) {
60
+ const data = [];
61
+ this.getAgGridApi().forEachNodeAfterFilter((rowNode) => {
62
+ if (!this.isGroupRowNode(rowNode)) {
63
+ data.push(this.getRawValueFromRowNode(rowNode, columnId));
64
+ }
65
+ });
66
+ return data;
67
+ }
68
+ getVisibleColumnData(columnId) {
69
+ const data = [];
70
+ this.getAdaptableInternalApi().forAllVisibleRowNodesDo((rowNode) => {
71
+ if (!this.isGroupRowNode(rowNode)) {
72
+ data.push(this.getRawValueFromRowNode(rowNode, columnId));
73
+ }
74
+ });
75
+ return data;
76
+ }
50
77
  async updateGridData(dataRows, dataUpdateConfig) {
51
78
  const rowNodes = await this._adaptable.updateRows(dataRows, dataUpdateConfig);
52
79
  const rowDataChangedInfo = this.getAdaptableInternalApi().buildRowDataChangedInfo(dataRows, rowNodes, 'Update');
@@ -1,3 +1,4 @@
1
+ import type { AgChartInstance, AgSparklineOptions } from 'ag-charts-types';
1
2
  import { ApiBase } from './ApiBase';
2
3
  import { StyledColumnApi } from '../StyledColumnApi';
3
4
  import { StyledColumn, StyledColumnState } from '../../AdaptableState/StyledColumnState';
@@ -25,6 +26,7 @@ export declare class StyledColumnApiImpl extends ApiBase implements StyledColumn
25
26
  hasBulletChartStyle(columnId: string): boolean;
26
27
  hasRatingStyle(columnId: string): boolean;
27
28
  canDisplaySparklines(): boolean;
29
+ renderSparkline(options: AgSparklineOptions): AgChartInstance<AgSparklineOptions> | null;
28
30
  suspendStyledColumn(styledColumn: StyledColumn): void;
29
31
  unSuspendStyledColumn(styledColumn: StyledColumn): void;
30
32
  suspendAllStyledColumn(): void;
@@ -79,6 +79,9 @@ export class StyledColumnApiImpl extends ApiBase {
79
79
  canDisplaySparklines() {
80
80
  return this._adaptable.canDisplaySparklines();
81
81
  }
82
+ renderSparkline(options) {
83
+ return this.internalApi.createSparkline(options);
84
+ }
82
85
  suspendStyledColumn(styledColumn) {
83
86
  this.dispatchAction(StyledColumnRedux.StyledColumnSuspend(styledColumn));
84
87
  }
@@ -4,8 +4,18 @@ import { StyledColumn } from '../../AdaptableState/StyledColumnState';
4
4
  import { CellColorRange, ColumnComparison, NumericStyledColumn } from '../../AdaptableState/StyledColumns/Common/NumericStyledColumn';
5
5
  import { BadgeStyle, BadgeStyleDefinition } from '../../AdaptableState/StyledColumns/BadgeStyle';
6
6
  import { IRowNode } from 'ag-grid-enterprise';
7
+ import type { AgChartInstance, AgSparklineOptions } from 'ag-charts-types';
7
8
  import { PredicateDefHandlerContext } from '../../types';
8
9
  export declare class StyledColumnInternalApi extends ApiBase {
10
+ /**
11
+ * Creates a sparkline described by `options` in its target container and
12
+ * returns the chart instance (used to update / destroy it).
13
+ *
14
+ * Centralises sparkline creation so view components don't import
15
+ * `ag-charts-enterprise` directly: the `createSparkline` factory is sourced
16
+ * from the AG Grid Sparklines module via the grid's registry bean.
17
+ */
18
+ createSparkline(options: AgSparklineOptions): AgChartInstance<AgSparklineOptions> | null;
9
19
  getMinValueForNumericColumn(column: AdaptableColumn | undefined): number | undefined;
10
20
  getMaxValueForNumericColumn(column: AdaptableColumn | undefined): number | undefined;
11
21
  getAvgValueForNumericColumn(column: AdaptableColumn | undefined): number | undefined;
@@ -10,6 +10,26 @@ const DYNAMIC_RANGE_ENDPOINTS = [
10
10
  const isDynamicRangeEndpoint = (value) => typeof value === 'string' &&
11
11
  DYNAMIC_RANGE_ENDPOINTS.includes(value);
12
12
  export class StyledColumnInternalApi extends ApiBase {
13
+ /**
14
+ * Creates a sparkline described by `options` in its target container and
15
+ * returns the chart instance (used to update / destroy it).
16
+ *
17
+ * Centralises sparkline creation so view components don't import
18
+ * `ag-charts-enterprise` directly: the `createSparkline` factory is sourced
19
+ * from the AG Grid Sparklines module via the grid's registry bean.
20
+ */
21
+ createSparkline(options) {
22
+ if (!this.getStyledColumnApi().canDisplaySparklines()) {
23
+ this.logWarn('Cannot render Sparkline: AG Grid Sparklines are not enabled. Register the AG Grid Sparklines module (e.g. `AllEnterpriseModule.with(AgChartsEnterpriseModule)`) to use Sparklines.');
24
+ return null;
25
+ }
26
+ const createSparklineFactory = this._adaptable.agGridAdapter.DANGER_getCreateSparkline();
27
+ if (!createSparklineFactory) {
28
+ this.logWarn('Cannot render Sparkline: unable to obtain the AG Grid Sparkline rendering factory.');
29
+ return null;
30
+ }
31
+ return createSparklineFactory(options);
32
+ }
13
33
  getMinValueForNumericColumn(column) {
14
34
  if (!column || column.dataType !== 'number') {
15
35
  return undefined;
@@ -1,3 +1,4 @@
1
+ import type { AgChartInstance, AgSparklineOptions } from 'ag-charts-types';
1
2
  import { AdaptableColumn } from '../AdaptableState/Common/AdaptableColumn';
2
3
  import { StyledColumn, StyledColumnState } from '../AdaptableState/StyledColumnState';
3
4
  import { LayoutExtendedConfig } from '../types';
@@ -117,4 +118,17 @@ export interface StyledColumnApi {
117
118
  * Can this AdapTable instance display Sparklines (e.g. is AG Grid SparklinesModule installed)
118
119
  */
119
120
  canDisplaySparklines(): boolean;
121
+ /**
122
+ * Renders a standalone [Sparkline](https://www.ag-grid.com/javascript-data-grid/sparklines-overview/)
123
+ * into a DOM element you provide, using the same AG Charts engine as Sparkline Styled Columns.
124
+ *
125
+ * Provide at least `container` (where to render) and `data` (what to plot) on `options`; see
126
+ * [AgSparklineOptions](https://www.ag-grid.com/javascript-data-grid/sparklines-overview/) for the
127
+ * full set (`type`, `width`/`height`, `min`/`max`, etc.).
128
+ *
129
+ * The caller owns the returned instance and is responsible for its lifecycle: call `update()` to
130
+ * re-render it when the data or options change, and `destroy()` to dispose of it (e.g. when the
131
+ * `container` is removed) to avoid memory leaks. AdapTable does not track or clean it up for you.
132
+ */
133
+ renderSparkline(options: AgSparklineOptions): AgChartInstance<AgSparklineOptions> | null;
120
134
  }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Lightweight, dependency-free parser for standard 5-field cron expressions:
3
+ *
4
+ * minute hour day-of-month month day-of-week
5
+ *
6
+ * Each field supports:
7
+ * - `*` — every value in the field range
8
+ * - A single integer (e.g. `9`)
9
+ * - A comma-separated list (e.g. `1,3,5`)
10
+ * - A range (e.g. `1-5`)
11
+ * - A step (e.g. `* /15`, `0-30/5`)
12
+ *
13
+ * For day-of-week, both `0` and `7` represent Sunday.
14
+ *
15
+ * This module exists to keep AdapTable free of the `cron-parser` package (which
16
+ * transitively pulls in `luxon`). AdapTable only uses cron expressions in the
17
+ * Scheduling system, and only ever needs to:
18
+ * 1. validate a user-entered cron string, and
19
+ * 2. compute the next fire time from "now".
20
+ */
21
+ interface ParsedCron {
22
+ minute: Set<number>;
23
+ hour: Set<number>;
24
+ dayOfMonth: Set<number>;
25
+ month: Set<number>;
26
+ dayOfWeek: Set<number>;
27
+ /** True when the day-of-month field is `*` (i.e. unrestricted). */
28
+ dayOfMonthIsStar: boolean;
29
+ /** True when the day-of-week field is `*` (i.e. unrestricted). */
30
+ dayOfWeekIsStar: boolean;
31
+ }
32
+ /**
33
+ * Parses a 5-field cron expression. Throws when the expression is invalid.
34
+ */
35
+ export declare function parseCronExpression(expression: string): ParsedCron;
36
+ /**
37
+ * Returns true if the given expression is a valid 5-field cron expression.
38
+ */
39
+ export declare function isCronExpressionValid(expression: string): boolean;
40
+ /**
41
+ * Returns the next time the cron expression fires after `fromDate` (exclusive).
42
+ * Returns null if no match is found within a 4-year horizon.
43
+ */
44
+ export declare function getNextCronOccurrence(expression: string, fromDate?: Date): Date | null;
45
+ export {};
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Lightweight, dependency-free parser for standard 5-field cron expressions:
3
+ *
4
+ * minute hour day-of-month month day-of-week
5
+ *
6
+ * Each field supports:
7
+ * - `*` — every value in the field range
8
+ * - A single integer (e.g. `9`)
9
+ * - A comma-separated list (e.g. `1,3,5`)
10
+ * - A range (e.g. `1-5`)
11
+ * - A step (e.g. `* /15`, `0-30/5`)
12
+ *
13
+ * For day-of-week, both `0` and `7` represent Sunday.
14
+ *
15
+ * This module exists to keep AdapTable free of the `cron-parser` package (which
16
+ * transitively pulls in `luxon`). AdapTable only uses cron expressions in the
17
+ * Scheduling system, and only ever needs to:
18
+ * 1. validate a user-entered cron string, and
19
+ * 2. compute the next fire time from "now".
20
+ */
21
+ const MINUTE_RANGE = [0, 59];
22
+ const HOUR_RANGE = [0, 23];
23
+ const DAY_OF_MONTH_RANGE = [1, 31];
24
+ const MONTH_RANGE = [1, 12];
25
+ const FIELD_LABELS = ['minute', 'hour', 'day-of-month', 'month', 'day-of-week'];
26
+ /**
27
+ * Parses a 5-field cron expression. Throws when the expression is invalid.
28
+ */
29
+ export function parseCronExpression(expression) {
30
+ if (!expression || typeof expression !== 'string') {
31
+ throw new Error('Cron expression is empty');
32
+ }
33
+ const parts = expression.trim().split(/\s+/);
34
+ if (parts.length !== 5) {
35
+ throw new Error(`Expected 5 cron fields (minute hour day-of-month month day-of-week) but got ${parts.length}`);
36
+ }
37
+ const minute = parseField(parts[0], MINUTE_RANGE, FIELD_LABELS[0]);
38
+ const hour = parseField(parts[1], HOUR_RANGE, FIELD_LABELS[1]);
39
+ const dayOfMonth = parseField(parts[2], DAY_OF_MONTH_RANGE, FIELD_LABELS[2]);
40
+ const month = parseField(parts[3], MONTH_RANGE, FIELD_LABELS[3]);
41
+ const dayOfWeek = parseDayOfWeekField(parts[4]);
42
+ return {
43
+ minute,
44
+ hour,
45
+ dayOfMonth,
46
+ month,
47
+ dayOfWeek,
48
+ dayOfMonthIsStar: parts[2] === '*',
49
+ dayOfWeekIsStar: parts[4] === '*',
50
+ };
51
+ }
52
+ /**
53
+ * Returns true if the given expression is a valid 5-field cron expression.
54
+ */
55
+ export function isCronExpressionValid(expression) {
56
+ try {
57
+ parseCronExpression(expression);
58
+ return true;
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ /**
65
+ * Returns the next time the cron expression fires after `fromDate` (exclusive).
66
+ * Returns null if no match is found within a 4-year horizon.
67
+ */
68
+ export function getNextCronOccurrence(expression, fromDate = new Date()) {
69
+ const cron = parseCronExpression(expression);
70
+ // Bump to the start of the next minute - cron has minute granularity and
71
+ // we never want to return `fromDate` itself.
72
+ const next = new Date(fromDate.getTime());
73
+ next.setSeconds(0, 0);
74
+ next.setMinutes(next.getMinutes() + 1);
75
+ // Safety horizon — patterns like "0 0 31 2 *" (31 Feb) can never match.
76
+ const horizon = new Date(next.getTime());
77
+ horizon.setFullYear(horizon.getFullYear() + 4);
78
+ while (next < horizon) {
79
+ if (!cron.month.has(next.getMonth() + 1)) {
80
+ // Jump to the 1st of the next month.
81
+ next.setDate(1);
82
+ next.setHours(0, 0, 0, 0);
83
+ next.setMonth(next.getMonth() + 1);
84
+ continue;
85
+ }
86
+ if (!matchesDay(cron, next)) {
87
+ next.setHours(0, 0, 0, 0);
88
+ next.setDate(next.getDate() + 1);
89
+ continue;
90
+ }
91
+ if (!cron.hour.has(next.getHours())) {
92
+ next.setMinutes(0, 0, 0);
93
+ next.setHours(next.getHours() + 1);
94
+ continue;
95
+ }
96
+ if (!cron.minute.has(next.getMinutes())) {
97
+ next.setSeconds(0, 0);
98
+ next.setMinutes(next.getMinutes() + 1);
99
+ continue;
100
+ }
101
+ return next;
102
+ }
103
+ return null;
104
+ }
105
+ /**
106
+ * Standard cron day matching:
107
+ * - if both day-of-month and day-of-week are `*`, the day always matches
108
+ * - if only one is `*`, only the other is applied
109
+ * - if neither is `*`, the day matches when **either** matches (OR semantics)
110
+ */
111
+ function matchesDay(cron, date) {
112
+ const domMatches = cron.dayOfMonth.has(date.getDate());
113
+ const dowMatches = cron.dayOfWeek.has(date.getDay());
114
+ if (cron.dayOfMonthIsStar && cron.dayOfWeekIsStar) {
115
+ return true;
116
+ }
117
+ if (cron.dayOfMonthIsStar) {
118
+ return dowMatches;
119
+ }
120
+ if (cron.dayOfWeekIsStar) {
121
+ return domMatches;
122
+ }
123
+ return domMatches || dowMatches;
124
+ }
125
+ function parseField(field, range, label) {
126
+ const [min, max] = range;
127
+ const values = new Set();
128
+ const segments = field.split(',');
129
+ for (const raw of segments) {
130
+ const segment = raw.trim();
131
+ if (segment === '') {
132
+ throw new Error(`Invalid ${label} field "${field}"`);
133
+ }
134
+ const [rangePart, stepPart] = segment.split('/');
135
+ const step = stepPart === undefined ? 1 : Number(stepPart);
136
+ if (!Number.isInteger(step) || step <= 0) {
137
+ throw new Error(`Invalid step value in ${label} field "${segment}"`);
138
+ }
139
+ let from;
140
+ let to;
141
+ if (rangePart === '*') {
142
+ from = min;
143
+ to = max;
144
+ }
145
+ else if (rangePart.includes('-')) {
146
+ const [fromStr, toStr] = rangePart.split('-');
147
+ from = Number(fromStr);
148
+ to = Number(toStr);
149
+ }
150
+ else {
151
+ from = Number(rangePart);
152
+ to = from;
153
+ }
154
+ if (!Number.isInteger(from) || !Number.isInteger(to)) {
155
+ throw new Error(`Invalid ${label} field "${segment}"`);
156
+ }
157
+ if (from < min || to > max || from > to) {
158
+ throw new Error(`Out-of-range ${label} field "${segment}" (expected ${min}-${max})`);
159
+ }
160
+ for (let v = from; v <= to; v += step) {
161
+ values.add(v);
162
+ }
163
+ }
164
+ if (values.size === 0) {
165
+ throw new Error(`Empty ${label} field`);
166
+ }
167
+ return values;
168
+ }
169
+ function parseDayOfWeekField(field) {
170
+ // Cron allows 0 or 7 to represent Sunday — parse with an extended range
171
+ // [0,7] and then collapse 7 → 0 so callers can compare against
172
+ // `Date.getDay()` which returns 0-6.
173
+ const extended = parseField(field, [0, 7], 'day-of-week');
174
+ const normalised = new Set();
175
+ for (const value of extended) {
176
+ normalised.add(value === 7 ? 0 : value);
177
+ }
178
+ return normalised;
179
+ }
@@ -1,5 +1,5 @@
1
- import { parseExpression } from 'cron-parser';
2
1
  import StringExtensions from '../../Extensions/StringExtensions';
2
+ import { getNextCronOccurrence, isCronExpressionValid as isCronExpressionValidInternal, } from './CronExpression';
3
3
  const WEEKDAY_TO_CRON = {
4
4
  Sunday: 0,
5
5
  Monday: 1,
@@ -123,11 +123,9 @@ export function getNextRunDateFromSchedule(schedule) {
123
123
  if (!schedule.CronExpression?.trim()) {
124
124
  return null;
125
125
  }
126
- const interval = parseExpression(schedule.CronExpression.trim(), {
127
- currentDate: new Date(),
128
- });
129
- const next = interval.next().toDate();
130
- return next > new Date() ? next : null;
126
+ const now = new Date();
127
+ const next = getNextCronOccurrence(schedule.CronExpression.trim(), now);
128
+ return next && next > now ? next : null;
131
129
  }
132
130
  catch {
133
131
  return null;
@@ -154,13 +152,10 @@ export function isScheduleValid(schedule) {
154
152
  if (preset === 'selectedDays' && days.length === 0) {
155
153
  return 'Select at least one day';
156
154
  }
157
- try {
158
- parseExpression(schedule.CronExpression.trim());
159
- return true;
160
- }
161
- catch {
155
+ if (!isCronExpressionValidInternal(schedule.CronExpression.trim())) {
162
156
  return 'Cron expression is not valid';
163
157
  }
158
+ return true;
164
159
  }
165
160
  export function getScheduleDescription(schedule) {
166
161
  if (!schedule) {
@@ -226,11 +221,5 @@ export function isValidCronExpression(expression) {
226
221
  if (StringExtensions.IsNullOrEmpty(expression)) {
227
222
  return false;
228
223
  }
229
- try {
230
- parseExpression(expression.trim());
231
- return true;
232
- }
233
- catch {
234
- return false;
235
- }
224
+ return isCronExpressionValidInternal(expression.trim());
236
225
  }
@@ -7,7 +7,7 @@ export declare class ThemeService implements IThemeService {
7
7
  constructor(api: AdaptableApi);
8
8
  destroy(): void;
9
9
  subscribe(): void;
10
- onThemeChanged: () => void;
10
+ onThemeSelected: () => void;
11
11
  applyNewThemeVariables(theme: AdaptableTheme): void;
12
12
  showMissingThemeFiles(theme: AdaptableTheme): void;
13
13
  getDOMPrefferedColorScheme(): 'dark' | 'light';
@@ -20,14 +20,14 @@ export class ThemeService {
20
20
  document.adoptedStyleSheets = [...document.adoptedStyleSheets].filter((sheet) => sheet !== this.styleSheetObject);
21
21
  }
22
22
  subscribe() {
23
- const themeChangedUnsubscribe = this.api.eventApi.on('ThemeSelected', this.onThemeChanged);
23
+ const themeChangedUnsubscribe = this.api.eventApi.on('ThemeSelected', this.onThemeSelected);
24
24
  const prefferedColorSchemeUnsubscribe = this.attachPrefferedColorSchemeListener();
25
25
  this.unsubscribe = () => {
26
26
  themeChangedUnsubscribe();
27
27
  prefferedColorSchemeUnsubscribe();
28
28
  };
29
29
  }
30
- onThemeChanged = () => {
30
+ onThemeSelected = () => {
31
31
  let currentTheme = this.api.themeApi.getCurrentThemeObject();
32
32
  currentTheme = this.mapOsTheme(currentTheme);
33
33
  this.applyNewThemeVariables(currentTheme);
@@ -7,7 +7,7 @@ import { useAdaptable } from '../../../AdaptableContext';
7
7
  import { getActionPanelSize, getMiddlePosition, getSettingsPanelSize } from '../Utilities';
8
8
  import { cn } from '../../../../lib/utils';
9
9
  const WindowDialog = (props) => {
10
- const { onChange, style, baseClassName, className, isActionModule, settingsPanelOptionsKey, onHide, ...dialogProps } = props;
10
+ const { onChange, style, baseClassName, className, isActionModule, settingsPanelOptionsKey, onHide, dataName, ...dialogProps } = props;
11
11
  const adaptable = useAdaptable();
12
12
  const dispatch = useDispatch();
13
13
  const settingsPanelOptions = adaptable.adaptableOptions.settingsPanelOptions;
@@ -34,7 +34,7 @@ const WindowDialog = (props) => {
34
34
  position: popupSettings.position,
35
35
  onChange: handleWindowSettings,
36
36
  };
37
- return (_jsx(Dialog, { windowModal: true, fixed: false, windowModalProps: windowModalProps, style: props.style, onDismiss: onHide, isOpen: true, showCloseButton: true, ...dialogProps, className: cn('twa:p-0 twa:h-full', className) }));
37
+ return (_jsx(Dialog, { "data-name": dataName, windowModal: true, fixed: false, windowModalProps: windowModalProps, style: props.style, onDismiss: onHide, isOpen: true, showCloseButton: true, ...dialogProps, className: cn('twa:p-0 twa:h-full', className) }));
38
38
  };
39
39
  const PopupDialog = (props) => {
40
40
  const { style, className, onHide, children, ...dialogProps } = props;
@@ -1,6 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
- import { AgChartsEnterpriseModule } from 'ag-charts-enterprise';
4
3
  import { AdaptableHelper } from '../../../../../Utilities/Helpers/AdaptableHelper';
5
4
  import { resolveSparklineOptionsForRender } from '../../../../../Utilities/Helpers/StyledColumns/SparklineStyleHelper';
6
5
  import { convertAdaptableStyleToCSS, hasCellBoxStyle, } from '../../../../../Utilities/Helpers/StyleHelper';
@@ -53,23 +52,6 @@ const buildSparklinePreviewOptions = (sparklineStyle, container, data, width, he
53
52
  }
54
53
  return options;
55
54
  };
56
- /**
57
- * AG Grid registers chart modules via `AgChartsEnterpriseModule.setup()` when
58
- * sparklines are enabled. Standalone `AgCharts.createSparkline` needs the same
59
- * setup — otherwise the settings-panel preview throws "No modules registered".
60
- */
61
- let agChartsPreviewModulesReady = false;
62
- const ensureAgChartsEnterpriseModules = () => {
63
- if (agChartsPreviewModulesReady) {
64
- return;
65
- }
66
- AgChartsEnterpriseModule.setup();
67
- agChartsPreviewModulesReady = true;
68
- };
69
- const createSparkline = (options) => {
70
- ensureAgChartsEnterpriseModules();
71
- return AgChartsEnterpriseModule.createSparkline(options);
72
- };
73
55
  const SparklinePreviewCanvas = ({ sparklineStyle, columnId }) => {
74
56
  const adaptable = useAdaptable();
75
57
  const containerRef = React.useRef(null);
@@ -93,7 +75,7 @@ const SparklinePreviewCanvas = ({ sparklineStyle, columnId }) => {
93
75
  const options = buildSparklinePreviewOptions(sparklineStyle, container, data, width, PREVIEW_HEIGHT);
94
76
  try {
95
77
  if (!instanceRef.current) {
96
- instanceRef.current = createSparkline(options);
78
+ instanceRef.current = adaptable.api.styledColumnApi.internalApi.createSparkline(options);
97
79
  }
98
80
  else {
99
81
  instanceRef.current.update(options);
@@ -1,4 +1,5 @@
1
1
  import { ColDef, ColGroupDef, Column, GridApi, GridOptions, IRowNode, ManagedGridOptionKey, ManagedGridOptions, Module } from 'ag-grid-enterprise';
2
+ import type { AgChartInstance, AgSparklineOptions } from 'ag-charts-types';
2
3
  import { AdaptableAgGrid } from './AdaptableAgGrid';
3
4
  import { AdaptableColumn, AdaptableColumnGroup } from '../AdaptableState/Common/AdaptableColumn';
4
5
  import { SelectedCellInfo } from '../AdaptableState/Selection/SelectedCellInfo';
@@ -31,6 +32,7 @@ export declare class AgGridAdapter {
31
32
  monkeyPatchingAggColumnFilters(): void;
32
33
  monkeyPatchingAggFuncLabels(): void;
33
34
  private DANGER_getPrivateAgGridBeans;
35
+ DANGER_getCreateSparkline(): ((options: AgSparklineOptions) => AgChartInstance<AgSparklineOptions>) | undefined;
34
36
  DANGER_getLiveGridOptions(): GridOptions<any>;
35
37
  getAgGridRootElement(): HTMLElement;
36
38
  /**
@@ -239,6 +239,16 @@ export class AgGridAdapter {
239
239
  }
240
240
  return beans;
241
241
  }
242
+ // #sparkline_factory
243
+ // The AG Grid Sparklines module stores the `createSparkline` factory (sourced from
244
+ // `AgChartsEnterpriseModule`) in the registered `agSparklineCellRenderer` default params.
245
+ // We read it straight from the registry bean so Adaptable can render standalone sparkline
246
+ // previews without taking a direct dependency on `ag-charts-enterprise`.
247
+ DANGER_getCreateSparkline() {
248
+ const registry = this.DANGER_getPrivateAgGridBeans()?.registry;
249
+ const sparklineComponent = registry?.getUserComponent('agSparklineCellRenderer', 'agSparklineCellRenderer');
250
+ return sparklineComponent?.params?.createSparkline;
251
+ }
242
252
  DANGER_getLiveGridOptions() {
243
253
  return this.DANGER_getPrivateAgGridBeans()?.gridOptions;
244
254
  }
@@ -35,6 +35,10 @@ export const agGridDataTypeDefinitions = {
35
35
  // boolean: is kept as is
36
36
  // object: is kept as is
37
37
  // Adaptable specific types
38
+ unknown: {
39
+ baseDataType: 'text',
40
+ extendsDataType: 'text',
41
+ },
38
42
  [TEXT_ARRAY_DATA_TYPE]: {
39
43
  baseDataType: 'text',
40
44
  extendsDataType: 'text',
@@ -100,9 +100,22 @@ export const WindowModal = (props) => {
100
100
  }
101
101
  }, []);
102
102
  const ResizableCmp = Resizable;
103
+ // React propagates events through the component tree, not the DOM tree. A
104
+ // nested popup (e.g. a modal wizard) rendered as a React child of this window
105
+ // is portaled elsewhere in the DOM, so a mousedown inside it would bubble here
106
+ // and incorrectly bring this window to the front (covering the popup). Guard
107
+ // against this by only reacting to mousedowns that happen inside this window's
108
+ // own DOM subtree.
109
+ const handleMouseDown = (event) => {
110
+ const node = targetRef.current;
111
+ if (node && event.target instanceof Node && !node.contains(event.target)) {
112
+ return;
113
+ }
114
+ stacking.bringInFront();
115
+ };
103
116
  return createPortal(_jsx("div", { style: style,
104
117
  //@ts-ignore
105
- ref: targetRef, onMouseDown: stacking.bringInFront, children: _jsx(ResizableCmp, { minWidth: props.minWidth, minHeight: props.minHeight, onResizeStop: handleResizeStop, onResize: handleResize, bounds: "window", defaultSize: {
118
+ ref: targetRef, onMouseDown: handleMouseDown, children: _jsx(ResizableCmp, { minWidth: props.minWidth, minHeight: props.minHeight, onResizeStop: handleResizeStop, onResize: handleResize, bounds: "window", defaultSize: {
106
119
  width: props.size.width,
107
120
  height: props.size.height,
108
121
  }, children: props.children }) }), portalElement);
package/src/env.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export default {
2
2
  NEXT_PUBLIC_INFINITE_TABLE_LICENSE_KEY: "StartDate=2021-06-29|EndDate=2030-01-01|Owner=Adaptable|Type=distribution|TS=1624971462479|C=137829811,1004007071,2756196225,1839832928,3994409405,636616862" || '',
3
- PUBLISH_TIMESTAMP: 1782298993334 || Date.now(),
4
- VERSION: "23.0.2" || '--current-version--',
3
+ PUBLISH_TIMESTAMP: 1782412388323 || Date.now(),
4
+ VERSION: "23.0.4-canary.0" || '--current-version--',
5
5
  };