@memberjunction/ng-query-viewer 5.48.0 → 5.50.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.
@@ -49,6 +49,14 @@ export declare class QueryDataGridComponent implements OnInit, OnDestroy {
49
49
  set ColumnConfigs(value: QueryGridColumnConfig[] | null);
50
50
  get ColumnConfigs(): QueryGridColumnConfig[] | null;
51
51
  private _data;
52
+ /**
53
+ * Per-row synthetic identity for rows that have no natural key column, so the
54
+ * `getRowId` fallback can never return duplicate ids (ag-Grid silently merges
55
+ * rows that share an id). Keyed by the row object reference; entries are added
56
+ * as Data is assigned. A WeakMap so it never retains rows past their lifetime.
57
+ */
58
+ private _syntheticRowIds;
59
+ private _syntheticRowIdSeq;
52
60
  /**
53
61
  * The query result data to display in the grid.
54
62
  */
@@ -143,6 +151,16 @@ export declare class QueryDataGridComponent implements OnInit, OnDestroy {
143
151
  PageNumber: number;
144
152
  /** Page size for server-side paging */
145
153
  PageSize: number;
154
+ /**
155
+ * Optional provider that returns the FULL result set for export. When the grid is server-side
156
+ * paged, `Data` only holds the current page (default 100 rows), so exporting `Data` would silently
157
+ * cap the export at the page size (bug B3/C1). A host that owns the query (with its parameters) can
158
+ * supply this callback to fetch every matching row on demand; the grid awaits it when the user
159
+ * exports without an explicit row selection. When not provided, export falls back to the loaded page.
160
+ */
161
+ ExportDataProvider: (() => Promise<Record<string, unknown>[]>) | null;
162
+ /** True while an export is fetching the full result set via ExportDataProvider. */
163
+ IsPreparingExport: boolean;
146
164
  /** Fired when the user navigates to a different page */
147
165
  PageChange: EventEmitter<PageChangeEvent>;
148
166
  GridApi: GridApi | null;
@@ -218,7 +236,7 @@ export declare class QueryDataGridComponent implements OnInit, OnDestroy {
218
236
  /**
219
237
  * Opens the export dialog with current grid data
220
238
  */
221
- OpenExportDialog(): void;
239
+ OpenExportDialog(): Promise<void>;
222
240
  /**
223
241
  * Handle export dialog close
224
242
  */
@@ -228,9 +246,12 @@ export declare class QueryDataGridComponent implements OnInit, OnDestroy {
228
246
  */
229
247
  Export(options?: Partial<ExportOptions>, download?: boolean): Promise<ExportResult>;
230
248
  /**
231
- * Get the current grid data formatted for export
249
+ * Resolves the rows to export. An explicit row selection always wins. Otherwise, if a host
250
+ * supplied an ExportDataProvider, the full result set is fetched (so export isn't silently
251
+ * capped at the current page — bug B3/C1); if it isn't supplied or the fetch yields nothing,
252
+ * export falls back to the loaded page (`_data`).
232
253
  */
233
- private getExportData;
254
+ private resolveExportData;
234
255
  /**
235
256
  * Get column definitions for export based on current grid columns
236
257
  */
@@ -259,6 +280,6 @@ export declare class QueryDataGridComponent implements OnInit, OnDestroy {
259
280
  */
260
281
  OnRowDetailEntityLinkClick(event: RowDetailEntityLinkEvent): void;
261
282
  static ɵfac: i0.ɵɵFactoryDeclaration<QueryDataGridComponent, never>;
262
- static ɵcmp: i0.ɵɵComponentDeclaration<QueryDataGridComponent, "mj-query-data-grid", never, { "QueryInfo": { "alias": "QueryInfo"; "required": false; }; "ColumnConfigs": { "alias": "ColumnConfigs"; "required": false; }; "Data": { "alias": "Data"; "required": false; }; "SelectionMode": { "alias": "SelectionMode"; "required": false; }; "ShowToolbar": { "alias": "ShowToolbar"; "required": false; }; "ShowRowCount": { "alias": "ShowRowCount"; "required": false; }; "ShowSelectionCount": { "alias": "ShowSelectionCount"; "required": false; }; "ShowExport": { "alias": "ShowExport"; "required": false; }; "ShowRefresh": { "alias": "ShowRefresh"; "required": false; }; "AllowSorting": { "alias": "AllowSorting"; "required": false; }; "AllowColumnResize": { "alias": "AllowColumnResize"; "required": false; }; "AllowColumnReorder": { "alias": "AllowColumnReorder"; "required": false; }; "VisualConfig": { "alias": "VisualConfig"; "required": false; }; "IsLoading": { "alias": "IsLoading"; "required": false; }; "Height": { "alias": "Height"; "required": false; }; "InitialGridState": { "alias": "InitialGridState"; "required": false; }; "PersistState": { "alias": "PersistState"; "required": false; }; "TotalRowCount": { "alias": "TotalRowCount"; "required": false; }; "PageNumber": { "alias": "PageNumber"; "required": false; }; "PageSize": { "alias": "PageSize"; "required": false; }; }, { "RowClick": "RowClick"; "RowDoubleClick": "RowDoubleClick"; "EntityLinkClick": "EntityLinkClick"; "SelectionChange": "SelectionChange"; "GridStateChange": "GridStateChange"; "RefreshRequest": "RefreshRequest"; "PageChange": "PageChange"; }, never, never, false, never>;
283
+ static ɵcmp: i0.ɵɵComponentDeclaration<QueryDataGridComponent, "mj-query-data-grid", never, { "QueryInfo": { "alias": "QueryInfo"; "required": false; }; "ColumnConfigs": { "alias": "ColumnConfigs"; "required": false; }; "Data": { "alias": "Data"; "required": false; }; "SelectionMode": { "alias": "SelectionMode"; "required": false; }; "ShowToolbar": { "alias": "ShowToolbar"; "required": false; }; "ShowRowCount": { "alias": "ShowRowCount"; "required": false; }; "ShowSelectionCount": { "alias": "ShowSelectionCount"; "required": false; }; "ShowExport": { "alias": "ShowExport"; "required": false; }; "ShowRefresh": { "alias": "ShowRefresh"; "required": false; }; "AllowSorting": { "alias": "AllowSorting"; "required": false; }; "AllowColumnResize": { "alias": "AllowColumnResize"; "required": false; }; "AllowColumnReorder": { "alias": "AllowColumnReorder"; "required": false; }; "VisualConfig": { "alias": "VisualConfig"; "required": false; }; "IsLoading": { "alias": "IsLoading"; "required": false; }; "Height": { "alias": "Height"; "required": false; }; "InitialGridState": { "alias": "InitialGridState"; "required": false; }; "PersistState": { "alias": "PersistState"; "required": false; }; "TotalRowCount": { "alias": "TotalRowCount"; "required": false; }; "PageNumber": { "alias": "PageNumber"; "required": false; }; "PageSize": { "alias": "PageSize"; "required": false; }; "ExportDataProvider": { "alias": "ExportDataProvider"; "required": false; }; }, { "RowClick": "RowClick"; "RowDoubleClick": "RowDoubleClick"; "EntityLinkClick": "EntityLinkClick"; "SelectionChange": "SelectionChange"; "GridStateChange": "GridStateChange"; "RefreshRequest": "RefreshRequest"; "PageChange": "PageChange"; }, never, never, false, never>;
263
284
  }
264
285
  //# sourceMappingURL=query-data-grid.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query-data-grid.component.d.ts","sourceRoot":"","sources":["../../../src/lib/query-data-grid/query-data-grid.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query-data-grid.component.d.ts","sourceRoot":"","sources":["../../../src/lib/query-data-grid/query-data-grid.component.ts"],"names":[],"mappings":"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"}
@@ -13,16 +13,30 @@ import * as i4 from "@memberjunction/ng-shared-generic";
13
13
  import * as i5 from "@memberjunction/ng-pagination";
14
14
  import * as i6 from "@memberjunction/ng-ui-components";
15
15
  import * as i7 from "../query-row-detail/query-row-detail.component";
16
+ function QueryDataGridComponent_Conditional_1_Conditional_2_Conditional_2_Template(rf, ctx) { if (rf & 1) {
17
+ i0.ɵɵtext(0);
18
+ i0.ɵɵpipe(1, "number");
19
+ i0.ɵɵpipe(2, "number");
20
+ } if (rf & 2) {
21
+ const ctx_r0 = i0.ɵɵnextContext(3);
22
+ i0.ɵɵtextInterpolate3(" ", i0.ɵɵpipeBind1(1, 3, ctx_r0.RowCount), " of ", i0.ɵɵpipeBind1(2, 5, ctx_r0.TotalRowCount), " ", ctx_r0.TotalRowCount === 1 ? "row" : "rows", " ");
23
+ } }
24
+ function QueryDataGridComponent_Conditional_1_Conditional_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
25
+ i0.ɵɵtext(0);
26
+ i0.ɵɵpipe(1, "number");
27
+ } if (rf & 2) {
28
+ const ctx_r0 = i0.ɵɵnextContext(3);
29
+ i0.ɵɵtextInterpolate2(" ", i0.ɵɵpipeBind1(1, 2, ctx_r0.RowCount), " ", ctx_r0.RowCount === 1 ? "row" : "rows", " ");
30
+ } }
16
31
  function QueryDataGridComponent_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
17
32
  i0.ɵɵelementStart(0, "span", 10);
18
33
  i0.ɵɵelement(1, "i", 15);
19
- i0.ɵɵtext(2);
20
- i0.ɵɵpipe(3, "number");
34
+ i0.ɵɵconditionalCreate(2, QueryDataGridComponent_Conditional_1_Conditional_2_Conditional_2_Template, 3, 7)(3, QueryDataGridComponent_Conditional_1_Conditional_2_Conditional_3_Template, 2, 4);
21
35
  i0.ɵɵelementEnd();
22
36
  } if (rf & 2) {
23
37
  const ctx_r0 = i0.ɵɵnextContext(2);
24
38
  i0.ɵɵadvance(2);
25
- i0.ɵɵtextInterpolate2(" ", i0.ɵɵpipeBind1(3, 2, ctx_r0.RowCount), " ", ctx_r0.RowCount === 1 ? "row" : "rows", " ");
39
+ i0.ɵɵconditional(ctx_r0.TotalRowCount > ctx_r0.RowCount ? 2 : 3);
26
40
  } }
27
41
  function QueryDataGridComponent_Conditional_1_Conditional_3_Template(rf, ctx) { if (rf & 1) {
28
42
  const _r2 = i0.ɵɵgetCurrentView();
@@ -60,16 +74,18 @@ function QueryDataGridComponent_Conditional_1_Conditional_6_Template(rf, ctx) {
60
74
  i0.ɵɵelementEnd()();
61
75
  } if (rf & 2) {
62
76
  const ctx_r0 = i0.ɵɵnextContext(2);
63
- i0.ɵɵproperty("disabled", ctx_r0.RowCount === 0);
77
+ i0.ɵɵproperty("disabled", ctx_r0.RowCount === 0 || ctx_r0.IsPreparingExport);
78
+ i0.ɵɵadvance();
79
+ i0.ɵɵclassProp("fa-file-export", !ctx_r0.IsPreparingExport)("fa-spinner", ctx_r0.IsPreparingExport)("fa-spin", ctx_r0.IsPreparingExport);
64
80
  } }
65
81
  function QueryDataGridComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
66
82
  i0.ɵɵelementStart(0, "div", 1)(1, "div", 9);
67
- i0.ɵɵconditionalCreate(2, QueryDataGridComponent_Conditional_1_Conditional_2_Template, 4, 4, "span", 10);
83
+ i0.ɵɵconditionalCreate(2, QueryDataGridComponent_Conditional_1_Conditional_2_Template, 4, 1, "span", 10);
68
84
  i0.ɵɵconditionalCreate(3, QueryDataGridComponent_Conditional_1_Conditional_3_Template, 5, 1, "span", 11);
69
85
  i0.ɵɵelementEnd();
70
86
  i0.ɵɵelementStart(4, "div", 12);
71
87
  i0.ɵɵconditionalCreate(5, QueryDataGridComponent_Conditional_1_Conditional_5_Template, 2, 3, "button", 13);
72
- i0.ɵɵconditionalCreate(6, QueryDataGridComponent_Conditional_1_Conditional_6_Template, 4, 1, "button", 14);
88
+ i0.ɵɵconditionalCreate(6, QueryDataGridComponent_Conditional_1_Conditional_6_Template, 4, 7, "button", 14);
73
89
  i0.ɵɵelementEnd()();
74
90
  } if (rf & 2) {
75
91
  const ctx_r0 = i0.ɵɵnextContext();
@@ -171,11 +187,27 @@ export class QueryDataGridComponent {
171
187
  return this._columnConfigs;
172
188
  }
173
189
  _data = [];
190
+ /**
191
+ * Per-row synthetic identity for rows that have no natural key column, so the
192
+ * `getRowId` fallback can never return duplicate ids (ag-Grid silently merges
193
+ * rows that share an id). Keyed by the row object reference; entries are added
194
+ * as Data is assigned. A WeakMap so it never retains rows past their lifetime.
195
+ */
196
+ _syntheticRowIds = new WeakMap();
197
+ _syntheticRowIdSeq = 0;
174
198
  /**
175
199
  * The query result data to display in the grid.
176
200
  */
177
201
  set Data(value) {
178
202
  this._data = value || [];
203
+ // Assign a stable synthetic id to each row up front, so the getRowId
204
+ // fallback (used only when a row has no natural key column) is
205
+ // collision-proof — rows with identical column values must NOT be merged.
206
+ for (const row of this._data) {
207
+ if (row && !this._syntheticRowIds.has(row)) {
208
+ this._syntheticRowIds.set(row, `mjrow_${this._syntheticRowIdSeq++}`);
209
+ }
210
+ }
179
211
  // If we have data but no columns from metadata or explicit configs, build from data
180
212
  if (this._data.length > 0 && this.Columns.length === 0 && !this._columnConfigs) {
181
213
  this.Columns = buildColumnsFromData(this._data);
@@ -281,6 +313,16 @@ export class QueryDataGridComponent {
281
313
  PageNumber = 1;
282
314
  /** Page size for server-side paging */
283
315
  PageSize = 100;
316
+ /**
317
+ * Optional provider that returns the FULL result set for export. When the grid is server-side
318
+ * paged, `Data` only holds the current page (default 100 rows), so exporting `Data` would silently
319
+ * cap the export at the page size (bug B3/C1). A host that owns the query (with its parameters) can
320
+ * supply this callback to fetch every matching row on demand; the grid awaits it when the user
321
+ * exports without an explicit row selection. When not provided, export falls back to the loaded page.
322
+ */
323
+ ExportDataProvider = null;
324
+ /** True while an export is fetching the full result set via ExportDataProvider. */
325
+ IsPreparingExport = false;
284
326
  /** Fired when the user navigates to a different page */
285
327
  PageChange = new EventEmitter();
286
328
  // ========================================
@@ -875,10 +917,18 @@ export class QueryDataGridComponent {
875
917
  /**
876
918
  * Opens the export dialog with current grid data
877
919
  */
878
- OpenExportDialog() {
920
+ async OpenExportDialog() {
879
921
  if (!this._data.length)
880
922
  return;
881
- const data = this.getExportData();
923
+ this.IsPreparingExport = true;
924
+ this.cdr.detectChanges();
925
+ let data;
926
+ try {
927
+ data = await this.resolveExportData();
928
+ }
929
+ finally {
930
+ this.IsPreparingExport = false;
931
+ }
882
932
  const columns = this.getExportColumns();
883
933
  const fileName = this._queryInfo?.Name || 'query-export';
884
934
  this.ExportDialogConfig = {
@@ -906,7 +956,7 @@ export class QueryDataGridComponent {
906
956
  * Export grid data directly without showing dialog
907
957
  */
908
958
  async Export(options, download = true) {
909
- const data = this.getExportData();
959
+ const data = await this.resolveExportData();
910
960
  const columns = this.getExportColumns();
911
961
  const fileName = options?.fileName || this._queryInfo?.Name || 'query-export';
912
962
  const exportOptions = {
@@ -923,12 +973,27 @@ export class QueryDataGridComponent {
923
973
  return result;
924
974
  }
925
975
  /**
926
- * Get the current grid data formatted for export
976
+ * Resolves the rows to export. An explicit row selection always wins. Otherwise, if a host
977
+ * supplied an ExportDataProvider, the full result set is fetched (so export isn't silently
978
+ * capped at the current page — bug B3/C1); if it isn't supplied or the fetch yields nothing,
979
+ * export falls back to the loaded page (`_data`).
927
980
  */
928
- getExportData() {
929
- // Use selected rows if any, otherwise all data
930
- const rows = this.SelectedRows.length > 0 ? this.SelectedRows : this._data;
931
- return rows;
981
+ async resolveExportData() {
982
+ if (this.SelectedRows.length > 0) {
983
+ return this.SelectedRows;
984
+ }
985
+ if (this.ExportDataProvider) {
986
+ try {
987
+ const allRows = await this.ExportDataProvider();
988
+ if (allRows && allRows.length > 0) {
989
+ return allRows;
990
+ }
991
+ }
992
+ catch {
993
+ // Fall through to the loaded page on any fetch failure.
994
+ }
995
+ }
996
+ return this._data;
932
997
  }
933
998
  /**
934
999
  * Get column definitions for export based on current grid columns
@@ -994,22 +1059,31 @@ export class QueryDataGridComponent {
994
1059
  };
995
1060
  }
996
1061
  GetRowId = (params) => {
997
- // Use data index as ID since query results don't have a guaranteed unique key
998
- // We use JSON stringify on a subset of the data to create a deterministic key
999
1062
  const data = params.data;
1000
1063
  if (!data)
1001
- return String(Math.random());
1002
- // Try to find a unique identifier in the data
1003
- // Common patterns: ID, Id, id, RowNumber, __row_index
1064
+ return `mjrow_${this._syntheticRowIdSeq++}`;
1065
+ // Prefer a natural unique key when the result exposes one — this keeps row
1066
+ // identity (and selection) stable across data refreshes.
1004
1067
  const idFields = ['ID', 'Id', 'id', 'RowNumber', 'RowNum', '__row_index'];
1005
1068
  for (const field of idFields) {
1006
1069
  if (data[field] !== undefined && data[field] !== null) {
1007
1070
  return String(data[field]);
1008
1071
  }
1009
1072
  }
1010
- // Fallback: use first few fields to create a hash-like key
1011
- const keys = Object.keys(data).slice(0, 3);
1012
- return keys.map(k => String(data[k] ?? '')).join('_');
1073
+ // No natural key — use the per-row synthetic id assigned when Data was set.
1074
+ // The previous fallback hashed only the first 3 columns, so any rows that
1075
+ // matched on those columns collided to the same id and ag-Grid dropped all
1076
+ // but one (silently collapsing aggregate/GROUP BY results, or any query
1077
+ // without an ID column). A per-row synthetic id can never collide.
1078
+ // Trade-off: the id is per-object (not value-derived), so an id-less row
1079
+ // loses its selection across a data refresh — acceptable for query results,
1080
+ // where NOT dropping rows matters far more than preserving a selection.
1081
+ let synthetic = this._syntheticRowIds.get(data);
1082
+ if (!synthetic) {
1083
+ synthetic = `mjrow_${this._syntheticRowIdSeq++}`;
1084
+ this._syntheticRowIds.set(data, synthetic);
1085
+ }
1086
+ return synthetic;
1013
1087
  };
1014
1088
  // ========================================
1015
1089
  // Row Detail Panel
@@ -1070,7 +1144,7 @@ export class QueryDataGridComponent {
1070
1144
  });
1071
1145
  }
1072
1146
  static ɵfac = function QueryDataGridComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || QueryDataGridComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i1.ExportService)); };
1073
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: QueryDataGridComponent, selectors: [["mj-query-data-grid"]], inputs: { QueryInfo: "QueryInfo", ColumnConfigs: "ColumnConfigs", Data: "Data", SelectionMode: "SelectionMode", ShowToolbar: "ShowToolbar", ShowRowCount: "ShowRowCount", ShowSelectionCount: "ShowSelectionCount", ShowExport: "ShowExport", ShowRefresh: "ShowRefresh", AllowSorting: "AllowSorting", AllowColumnResize: "AllowColumnResize", AllowColumnReorder: "AllowColumnReorder", VisualConfig: "VisualConfig", IsLoading: "IsLoading", Height: "Height", InitialGridState: "InitialGridState", PersistState: "PersistState", TotalRowCount: "TotalRowCount", PageNumber: "PageNumber", PageSize: "PageSize" }, outputs: { RowClick: "RowClick", RowDoubleClick: "RowDoubleClick", EntityLinkClick: "EntityLinkClick", SelectionChange: "SelectionChange", GridStateChange: "GridStateChange", RefreshRequest: "RefreshRequest", PageChange: "PageChange" }, standalone: false, decls: 9, vars: 24, consts: [[1, "query-data-grid-container", 3, "ngClass"], [1, "grid-toolbar"], [1, "loading-overlay"], [1, "grid-wrapper"], [1, "query-grid", 3, "gridReady", "rowClicked", "rowDoubleClicked", "selectionChanged", "sortChanged", "columnResized", "columnMoved", "theme", "rowData", "columnDefs", "defaultColDef", "gridOptions", "rowSelection", "getRowId"], [3, "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], ["Icon", "fa-solid fa-database", "Title", "No results", "Message", "Run the query to see results", 1, "empty-state"], [3, "closed", "visible", "config"], [3, "Close", "EntityLinkClick", "NavigateRow", "RowData", "Columns", "QueryInfo", "Visible", "RowIndex", "TotalRows"], [1, "toolbar-left"], [1, "row-count"], [1, "selection-count"], [1, "toolbar-right"], ["title", "Refresh data", 1, "toolbar-btn", "icon-only", 3, "disabled"], ["title", "Export data", 1, "toolbar-btn", 3, "disabled"], [1, "fa-solid", "fa-table-list"], [1, "fa-solid", "fa-check-square"], ["title", "Clear selection", 1, "clear-selection-btn", 3, "click"], [1, "fa-solid", "fa-times"], ["title", "Refresh data", 1, "toolbar-btn", "icon-only", 3, "click", "disabled"], [1, "fa-solid", "fa-sync-alt"], ["title", "Export data", 1, "toolbar-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-file-export"], [1, "button-text"], ["text", "Loading query results..."], [3, "PageChange", "TotalRowCount", "PageNumber", "PageSize", "IsLoading"]], template: function QueryDataGridComponent_Template(rf, ctx) { if (rf & 1) {
1147
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: QueryDataGridComponent, selectors: [["mj-query-data-grid"]], inputs: { QueryInfo: "QueryInfo", ColumnConfigs: "ColumnConfigs", Data: "Data", SelectionMode: "SelectionMode", ShowToolbar: "ShowToolbar", ShowRowCount: "ShowRowCount", ShowSelectionCount: "ShowSelectionCount", ShowExport: "ShowExport", ShowRefresh: "ShowRefresh", AllowSorting: "AllowSorting", AllowColumnResize: "AllowColumnResize", AllowColumnReorder: "AllowColumnReorder", VisualConfig: "VisualConfig", IsLoading: "IsLoading", Height: "Height", InitialGridState: "InitialGridState", PersistState: "PersistState", TotalRowCount: "TotalRowCount", PageNumber: "PageNumber", PageSize: "PageSize", ExportDataProvider: "ExportDataProvider" }, outputs: { RowClick: "RowClick", RowDoubleClick: "RowDoubleClick", EntityLinkClick: "EntityLinkClick", SelectionChange: "SelectionChange", GridStateChange: "GridStateChange", RefreshRequest: "RefreshRequest", PageChange: "PageChange" }, standalone: false, decls: 9, vars: 24, consts: [[1, "query-data-grid-container", 3, "ngClass"], [1, "grid-toolbar"], [1, "loading-overlay"], [1, "grid-wrapper"], [1, "query-grid", 3, "gridReady", "rowClicked", "rowDoubleClicked", "selectionChanged", "sortChanged", "columnResized", "columnMoved", "theme", "rowData", "columnDefs", "defaultColDef", "gridOptions", "rowSelection", "getRowId"], [3, "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], ["Icon", "fa-solid fa-database", "Title", "No results", "Message", "Run the query to see results", 1, "empty-state"], [3, "closed", "visible", "config"], [3, "Close", "EntityLinkClick", "NavigateRow", "RowData", "Columns", "QueryInfo", "Visible", "RowIndex", "TotalRows"], [1, "toolbar-left"], [1, "row-count"], [1, "selection-count"], [1, "toolbar-right"], ["title", "Refresh data", 1, "toolbar-btn", "icon-only", 3, "disabled"], ["title", "Export data", 1, "toolbar-btn", 3, "disabled"], [1, "fa-solid", "fa-table-list"], [1, "fa-solid", "fa-check-square"], ["title", "Clear selection", 1, "clear-selection-btn", 3, "click"], [1, "fa-solid", "fa-times"], ["title", "Refresh data", 1, "toolbar-btn", "icon-only", 3, "click", "disabled"], [1, "fa-solid", "fa-sync-alt"], ["title", "Export data", 1, "toolbar-btn", 3, "click", "disabled"], [1, "fa-solid"], [1, "button-text"], ["text", "Loading query results..."], [3, "PageChange", "TotalRowCount", "PageNumber", "PageSize", "IsLoading"]], template: function QueryDataGridComponent_Template(rf, ctx) { if (rf & 1) {
1074
1148
  i0.ɵɵelementStart(0, "div", 0);
1075
1149
  i0.ɵɵconditionalCreate(1, QueryDataGridComponent_Conditional_1_Template, 7, 4, "div", 1);
1076
1150
  i0.ɵɵconditionalCreate(2, QueryDataGridComponent_Conditional_2_Template, 2, 1, "div", 2);
@@ -1129,7 +1203,7 @@ export class QueryDataGridComponent {
1129
1203
  animate('100ms ease-in', style({ opacity: 0, transform: 'translateY(-8px)' }))
1130
1204
  ])
1131
1205
  ])
1132
- ], template: "<!-- Query Data Grid Component -->\n<div class=\"query-data-grid-container\" [ngClass]=\"GetContainerClasses()\" [style.height]=\"Height\">\n <!-- Toolbar -->\n @if (ShowToolbar) {\n <div class=\"grid-toolbar\">\n <div class=\"toolbar-left\">\n <!-- Row count -->\n @if (ShowRowCount) {\n <span class=\"row-count\">\n <i class=\"fa-solid fa-table-list\"></i>\n {{ RowCount | number }} {{ RowCount === 1 ? 'row' : 'rows' }}\n </span>\n }\n <!-- Selection count -->\n @if (ShowSelectionCount && SelectionCount > 0) {\n <span class=\"selection-count\">\n <i class=\"fa-solid fa-check-square\"></i>\n {{ SelectionCount }} selected\n <button class=\"clear-selection-btn\" (click)=\"ClearSelection()\" title=\"Clear selection\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </span>\n }\n </div>\n <div class=\"toolbar-right\">\n <!-- Refresh button -->\n @if (ShowRefresh) {\n <button\n (click)=\"Refresh()\"\n [disabled]=\"IsLoading\"\n title=\"Refresh data\"\n class=\"toolbar-btn icon-only\">\n <i class=\"fa-solid fa-sync-alt\" [class.fa-spin]=\"IsLoading\"></i>\n </button>\n }\n <!-- Export button -->\n @if (ShowExport) {\n <button\n [disabled]=\"RowCount === 0\"\n title=\"Export data\"\n class=\"toolbar-btn\"\n (click)=\"OpenExportDialog()\">\n <i class=\"fa-solid fa-file-export\"></i>\n <span class=\"button-text\">Export</span>\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Loading overlay -->\n @if (IsLoading) {\n <div class=\"loading-overlay\" @fadeIn>\n <mj-loading text=\"Loading query results...\"></mj-loading>\n </div>\n }\n\n <!-- AG Grid -->\n <div class=\"grid-wrapper\" [class.loading]=\"IsLoading\">\n <ag-grid-angular\n class=\"query-grid\"\n [theme]=\"Theme\"\n [rowData]=\"Data\"\n [columnDefs]=\"ColumnDefs\"\n [defaultColDef]=\"DefaultColDef\"\n [gridOptions]=\"GridOptions\"\n [rowSelection]=\"GetRowSelectionConfig()\"\n [getRowId]=\"GetRowId\"\n (gridReady)=\"OnGridReady($event)\"\n (rowClicked)=\"OnRowClicked($event)\"\n (rowDoubleClicked)=\"OnRowDoubleClicked($event)\"\n (selectionChanged)=\"OnSelectionChanged($event)\"\n (sortChanged)=\"OnSortChanged($event)\"\n (columnResized)=\"OnColumnResized($event)\"\n (columnMoved)=\"OnColumnMoved($event)\">\n </ag-grid-angular>\n </div>\n\n <!-- Pagination (only shown when server-side paging is active) -->\n @if (TotalRowCount > 0) {\n <mj-pagination\n [TotalRowCount]=\"TotalRowCount\"\n [PageNumber]=\"PageNumber\"\n [PageSize]=\"PageSize\"\n [IsLoading]=\"IsLoading\"\n (PageChange)=\"PageChange.emit($event)\">\n </mj-pagination>\n }\n\n <!-- Empty state -->\n @if (!IsLoading && RowCount === 0) {\n <mj-empty-state\n class=\"empty-state\"\n @fadeIn\n Icon=\"fa-solid fa-database\"\n Title=\"No results\"\n Message=\"Run the query to see results\" />\n }\n</div>\n\n<!-- Export Dialog -->\n<mj-export-dialog\n [visible]=\"ShowExportDialog\"\n [config]=\"ExportDialogConfig\"\n (closed)=\"OnExportDialogClosed($event)\">\n</mj-export-dialog>\n\n<!-- Row Detail Panel -->\n<mj-query-row-detail\n [RowData]=\"RowDetailData\"\n [Columns]=\"Columns\"\n [QueryInfo]=\"QueryInfo\"\n [Visible]=\"ShowRowDetailPanel\"\n [RowIndex]=\"RowDetailIndex\"\n [TotalRows]=\"RowCount\"\n (Close)=\"CloseRowDetailPanel()\"\n (EntityLinkClick)=\"OnRowDetailEntityLinkClick($event)\"\n (NavigateRow)=\"NavigateRowDetail($event)\">\n</mj-query-row-detail>\n", styles: ["/* Query Data Grid Component Styles */\n\n:host {\n display: block;\n height: 100%;\n --grid-accent-color: var(--mj-brand-primary);\n --grid-selection-indicator: var(--mj-status-warning);\n --grid-selection-bg: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n --grid-sort-indicator-color: var(--mj-brand-primary);\n}\n\n.query-data-grid-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n position: relative;\n background: var(--mj-bg-surface);\n border-radius: 4px;\n overflow: hidden;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.grid-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n min-height: 48px;\n flex-shrink: 0;\n}\n\n.toolbar-left,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.row-count,\n.selection-count {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.row-count i,\n.selection-count i {\n color: var(--mj-text-muted);\n}\n\n.selection-count {\n padding: 4px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n border-radius: 4px;\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.clear-selection-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n padding: 0;\n margin-left: 4px;\n border: none;\n background: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n border-radius: 50%;\n transition: all 0.15s ease;\n}\n\n.clear-selection-btn:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 10%, transparent);\n color: var(--mj-text-primary);\n}\n\n/* Toolbar buttons - consistent styling */\n.toolbar-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: background-color 0.2s, border-color 0.2s;\n}\n\n.toolbar-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n}\n\n.toolbar-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-btn i {\n font-size: 14px;\n}\n\n/* Icon-only button */\n.toolbar-btn.icon-only {\n padding: 6px 8px;\n}\n\n.toolbar-btn.icon-only i {\n margin-right: 0;\n}\n\n/* ========================================\n Grid wrapper\n ======================================== */\n\n.grid-wrapper {\n flex: 1;\n min-height: 0;\n position: relative;\n transition: opacity 0.2s ease;\n}\n\n.grid-wrapper.loading {\n opacity: 0.5;\n pointer-events: none;\n}\n\n.query-grid {\n width: 100%;\n height: 100%;\n}\n\n/* ========================================\n Loading overlay\n ======================================== */\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-overlay);\n z-index: 100;\n}\n\n/* ========================================\n Empty state\n ======================================== */\n\n/* Absolute-center the empty state over the grid; mj-empty-state owns its content. */\n.empty-state {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n}\n\n/* ========================================\n Entity link cell\n ======================================== */\n\n::ng-deep .entity-link-cell {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n::ng-deep .entity-link {\n color: var(--grid-accent-color);\n text-decoration: none;\n cursor: pointer;\n transition: color 0.15s ease;\n}\n\n::ng-deep .entity-link:hover {\n color: var(--mj-brand-primary-hover);\n text-decoration: underline;\n}\n\n::ng-deep .entity-link-icon {\n font-size: 10px;\n color: var(--mj-text-disabled);\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n::ng-deep .entity-link-cell:hover .entity-link-icon {\n opacity: 1;\n}\n\n/* ========================================\n AG Grid customizations\n ======================================== */\n\n/* ========================================\n Visual Config: Zebra Striping\n (copied from entity-data-grid pattern)\n ======================================== */\n\n/* Subtle contrast */\n.alternate-rows-subtle ::ng-deep .ag-row-odd {\n background-color: color-mix(in srgb, var(--mj-text-primary) 1.5%, transparent);\n}\n\n.alternate-rows-subtle ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--mj-bg-surface-hover);\n}\n\n/* Medium contrast (default) */\n.alternate-rows-medium ::ng-deep .ag-row-odd {\n background-color: color-mix(in srgb, var(--mj-text-primary) 2.5%, transparent);\n}\n\n.alternate-rows-medium ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--mj-bg-surface-hover);\n}\n\n/* Strong contrast */\n.alternate-rows-strong ::ng-deep .ag-row-odd {\n background-color: color-mix(in srgb, var(--mj-text-primary) 4%, transparent);\n}\n\n.alternate-rows-strong ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--mj-bg-surface-hover);\n}\n\n::ng-deep .ag-header-cell {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: var(--mj-text-secondary);\n}\n\n::ng-deep .ag-header-cell:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 4%, transparent);\n}\n\n/* Selected row styling */\n::ng-deep .ag-row-selected {\n background-color: var(--grid-selection-bg) !important;\n color: var(--mj-text-primary) !important;\n box-shadow: inset 4px 0 0 0 var(--grid-selection-indicator);\n}\n\n::ng-deep .ag-row-selected .ag-cell {\n color: var(--mj-text-primary) !important;\n}\n\n/* Row border */\n::ng-deep .ag-row {\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n/* Header alignment */\n::ng-deep .header-right .ag-header-cell-label {\n justify-content: flex-end;\n}\n\n::ng-deep .header-center .ag-header-cell-label {\n justify-content: center;\n}\n\n/* Checkbox styling */\n::ng-deep .ag-checkbox-input-wrapper {\n border-radius: 4px;\n width: 18px;\n height: 18px;\n}\n\n::ng-deep .ag-checkbox-input-wrapper.ag-checked {\n background-color: var(--grid-accent-color);\n border-color: var(--grid-accent-color);\n}\n\n::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--mj-text-inverse);\n}\n\n/* ========================================\n Sort Indicators - Arrow Only\n ======================================== */\n\n/* Sort icons - colored arrows only */\n:host ::ng-deep .ag-sort-ascending-icon {\n color: var(--mj-brand-primary) !important;\n}\n\n:host ::ng-deep .ag-sort-descending-icon {\n color: var(--mj-status-error) !important;\n}\n\n:host ::ng-deep .ag-sort-ascending-icon .ag-icon,\n:host ::ng-deep .ag-sort-descending-icon .ag-icon {\n font-size: 14px !important;\n}\n\n/* Sort order number (for multi-sort) */\n:host ::ng-deep .ag-sort-order {\n margin-left: 4px;\n font-size: 11px !important;\n font-weight: 700 !important;\n color: var(--mj-text-secondary) !important;\n}\n\n/* Hover state for sortable headers */\n:host ::ng-deep .ag-header-cell-sortable:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 4%, transparent);\n}\n\n/* ========================================\n Scrollbar Styling\n ======================================== */\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ========================================\n Responsive adjustments\n ======================================== */\n\n@media (max-width: 768px) {\n .grid-toolbar {\n flex-wrap: wrap;\n gap: 8px;\n }\n\n .toolbar-left,\n .toolbar-right {\n flex-wrap: wrap;\n }\n\n /* Hide button text on mobile */\n .toolbar-btn .button-text {\n display: none;\n }\n}\n"] }]
1206
+ ], template: "<!-- Query Data Grid Component -->\n<div class=\"query-data-grid-container\" [ngClass]=\"GetContainerClasses()\" [style.height]=\"Height\">\n <!-- Toolbar -->\n @if (ShowToolbar) {\n <div class=\"grid-toolbar\">\n <div class=\"toolbar-left\">\n <!-- Row count -->\n @if (ShowRowCount) {\n <span class=\"row-count\">\n <i class=\"fa-solid fa-table-list\"></i>\n @if (TotalRowCount > RowCount) {\n {{ RowCount | number }} of {{ TotalRowCount | number }} {{ TotalRowCount === 1 ? 'row' : 'rows' }}\n } @else {\n {{ RowCount | number }} {{ RowCount === 1 ? 'row' : 'rows' }}\n }\n </span>\n }\n <!-- Selection count -->\n @if (ShowSelectionCount && SelectionCount > 0) {\n <span class=\"selection-count\">\n <i class=\"fa-solid fa-check-square\"></i>\n {{ SelectionCount }} selected\n <button class=\"clear-selection-btn\" (click)=\"ClearSelection()\" title=\"Clear selection\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </span>\n }\n </div>\n <div class=\"toolbar-right\">\n <!-- Refresh button -->\n @if (ShowRefresh) {\n <button\n (click)=\"Refresh()\"\n [disabled]=\"IsLoading\"\n title=\"Refresh data\"\n class=\"toolbar-btn icon-only\">\n <i class=\"fa-solid fa-sync-alt\" [class.fa-spin]=\"IsLoading\"></i>\n </button>\n }\n <!-- Export button -->\n @if (ShowExport) {\n <button\n [disabled]=\"RowCount === 0 || IsPreparingExport\"\n title=\"Export data\"\n class=\"toolbar-btn\"\n (click)=\"OpenExportDialog()\">\n <i class=\"fa-solid\" [class.fa-file-export]=\"!IsPreparingExport\" [class.fa-spinner]=\"IsPreparingExport\" [class.fa-spin]=\"IsPreparingExport\"></i>\n <span class=\"button-text\">Export</span>\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Loading overlay -->\n @if (IsLoading) {\n <div class=\"loading-overlay\" @fadeIn>\n <mj-loading text=\"Loading query results...\"></mj-loading>\n </div>\n }\n\n <!-- AG Grid -->\n <div class=\"grid-wrapper\" [class.loading]=\"IsLoading\">\n <ag-grid-angular\n class=\"query-grid\"\n [theme]=\"Theme\"\n [rowData]=\"Data\"\n [columnDefs]=\"ColumnDefs\"\n [defaultColDef]=\"DefaultColDef\"\n [gridOptions]=\"GridOptions\"\n [rowSelection]=\"GetRowSelectionConfig()\"\n [getRowId]=\"GetRowId\"\n (gridReady)=\"OnGridReady($event)\"\n (rowClicked)=\"OnRowClicked($event)\"\n (rowDoubleClicked)=\"OnRowDoubleClicked($event)\"\n (selectionChanged)=\"OnSelectionChanged($event)\"\n (sortChanged)=\"OnSortChanged($event)\"\n (columnResized)=\"OnColumnResized($event)\"\n (columnMoved)=\"OnColumnMoved($event)\">\n </ag-grid-angular>\n </div>\n\n <!-- Pagination (only shown when server-side paging is active) -->\n @if (TotalRowCount > 0) {\n <mj-pagination\n [TotalRowCount]=\"TotalRowCount\"\n [PageNumber]=\"PageNumber\"\n [PageSize]=\"PageSize\"\n [IsLoading]=\"IsLoading\"\n (PageChange)=\"PageChange.emit($event)\">\n </mj-pagination>\n }\n\n <!-- Empty state -->\n @if (!IsLoading && RowCount === 0) {\n <mj-empty-state\n class=\"empty-state\"\n @fadeIn\n Icon=\"fa-solid fa-database\"\n Title=\"No results\"\n Message=\"Run the query to see results\" />\n }\n</div>\n\n<!-- Export Dialog -->\n<mj-export-dialog\n [visible]=\"ShowExportDialog\"\n [config]=\"ExportDialogConfig\"\n (closed)=\"OnExportDialogClosed($event)\">\n</mj-export-dialog>\n\n<!-- Row Detail Panel -->\n<mj-query-row-detail\n [RowData]=\"RowDetailData\"\n [Columns]=\"Columns\"\n [QueryInfo]=\"QueryInfo\"\n [Visible]=\"ShowRowDetailPanel\"\n [RowIndex]=\"RowDetailIndex\"\n [TotalRows]=\"RowCount\"\n (Close)=\"CloseRowDetailPanel()\"\n (EntityLinkClick)=\"OnRowDetailEntityLinkClick($event)\"\n (NavigateRow)=\"NavigateRowDetail($event)\">\n</mj-query-row-detail>\n", styles: ["/* Query Data Grid Component Styles */\n\n:host {\n display: block;\n height: 100%;\n --grid-accent-color: var(--mj-brand-primary);\n --grid-selection-indicator: var(--mj-status-warning);\n --grid-selection-bg: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n --grid-sort-indicator-color: var(--mj-brand-primary);\n}\n\n.query-data-grid-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n position: relative;\n background: var(--mj-bg-surface);\n border-radius: 4px;\n overflow: hidden;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.grid-toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 8px 12px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n min-height: 48px;\n flex-shrink: 0;\n}\n\n.toolbar-left,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.row-count,\n.selection-count {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.row-count i,\n.selection-count i {\n color: var(--mj-text-muted);\n}\n\n.selection-count {\n padding: 4px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n border-radius: 4px;\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.clear-selection-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n padding: 0;\n margin-left: 4px;\n border: none;\n background: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n border-radius: 50%;\n transition: all 0.15s ease;\n}\n\n.clear-selection-btn:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 10%, transparent);\n color: var(--mj-text-primary);\n}\n\n/* Toolbar buttons - consistent styling */\n.toolbar-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: background-color 0.2s, border-color 0.2s;\n}\n\n.toolbar-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n}\n\n.toolbar-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.toolbar-btn i {\n font-size: 14px;\n}\n\n/* Icon-only button */\n.toolbar-btn.icon-only {\n padding: 6px 8px;\n}\n\n.toolbar-btn.icon-only i {\n margin-right: 0;\n}\n\n/* ========================================\n Grid wrapper\n ======================================== */\n\n.grid-wrapper {\n flex: 1;\n min-height: 0;\n position: relative;\n transition: opacity 0.2s ease;\n}\n\n.grid-wrapper.loading {\n opacity: 0.5;\n pointer-events: none;\n}\n\n.query-grid {\n width: 100%;\n height: 100%;\n}\n\n/* ========================================\n Loading overlay\n ======================================== */\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-overlay);\n z-index: 100;\n}\n\n/* ========================================\n Empty state\n ======================================== */\n\n/* Absolute-center the empty state over the grid; mj-empty-state owns its content. */\n.empty-state {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n}\n\n/* ========================================\n Entity link cell\n ======================================== */\n\n::ng-deep .entity-link-cell {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n::ng-deep .entity-link {\n color: var(--grid-accent-color);\n text-decoration: none;\n cursor: pointer;\n transition: color 0.15s ease;\n}\n\n::ng-deep .entity-link:hover {\n color: var(--mj-brand-primary-hover);\n text-decoration: underline;\n}\n\n::ng-deep .entity-link-icon {\n font-size: 10px;\n color: var(--mj-text-disabled);\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n::ng-deep .entity-link-cell:hover .entity-link-icon {\n opacity: 1;\n}\n\n/* ========================================\n AG Grid customizations\n ======================================== */\n\n/* ========================================\n Visual Config: Zebra Striping\n (copied from entity-data-grid pattern)\n ======================================== */\n\n/* Subtle contrast */\n.alternate-rows-subtle ::ng-deep .ag-row-odd {\n background-color: color-mix(in srgb, var(--mj-text-primary) 1.5%, transparent);\n}\n\n.alternate-rows-subtle ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--mj-bg-surface-hover);\n}\n\n/* Medium contrast (default) */\n.alternate-rows-medium ::ng-deep .ag-row-odd {\n background-color: color-mix(in srgb, var(--mj-text-primary) 2.5%, transparent);\n}\n\n.alternate-rows-medium ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--mj-bg-surface-hover);\n}\n\n/* Strong contrast */\n.alternate-rows-strong ::ng-deep .ag-row-odd {\n background-color: color-mix(in srgb, var(--mj-text-primary) 4%, transparent);\n}\n\n.alternate-rows-strong ::ng-deep .ag-row-odd:hover:not(.ag-row-selected) {\n background-color: var(--mj-bg-surface-hover);\n}\n\n::ng-deep .ag-header-cell {\n font-weight: 600;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: var(--mj-text-secondary);\n}\n\n::ng-deep .ag-header-cell:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 4%, transparent);\n}\n\n/* Selected row styling */\n::ng-deep .ag-row-selected {\n background-color: var(--grid-selection-bg) !important;\n color: var(--mj-text-primary) !important;\n box-shadow: inset 4px 0 0 0 var(--grid-selection-indicator);\n}\n\n::ng-deep .ag-row-selected .ag-cell {\n color: var(--mj-text-primary) !important;\n}\n\n/* Row border */\n::ng-deep .ag-row {\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n/* Header alignment */\n::ng-deep .header-right .ag-header-cell-label {\n justify-content: flex-end;\n}\n\n::ng-deep .header-center .ag-header-cell-label {\n justify-content: center;\n}\n\n/* Checkbox styling */\n::ng-deep .ag-checkbox-input-wrapper {\n border-radius: 4px;\n width: 18px;\n height: 18px;\n}\n\n::ng-deep .ag-checkbox-input-wrapper.ag-checked {\n background-color: var(--grid-accent-color);\n border-color: var(--grid-accent-color);\n}\n\n::ng-deep .ag-checkbox-input-wrapper.ag-checked::after {\n color: var(--mj-text-inverse);\n}\n\n/* ========================================\n Sort Indicators - Arrow Only\n ======================================== */\n\n/* Sort icons - colored arrows only */\n:host ::ng-deep .ag-sort-ascending-icon {\n color: var(--mj-brand-primary) !important;\n}\n\n:host ::ng-deep .ag-sort-descending-icon {\n color: var(--mj-status-error) !important;\n}\n\n:host ::ng-deep .ag-sort-ascending-icon .ag-icon,\n:host ::ng-deep .ag-sort-descending-icon .ag-icon {\n font-size: 14px !important;\n}\n\n/* Sort order number (for multi-sort) */\n:host ::ng-deep .ag-sort-order {\n margin-left: 4px;\n font-size: 11px !important;\n font-weight: 700 !important;\n color: var(--mj-text-secondary) !important;\n}\n\n/* Hover state for sortable headers */\n:host ::ng-deep .ag-header-cell-sortable:hover {\n background: color-mix(in srgb, var(--mj-text-primary) 4%, transparent);\n}\n\n/* ========================================\n Scrollbar Styling\n ======================================== */\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-card);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb {\n background: var(--mj-border-strong);\n border-radius: 4px;\n}\n\n::ng-deep .ag-body-viewport::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-disabled);\n}\n\n/* ========================================\n Responsive adjustments\n ======================================== */\n\n@media (max-width: 768px) {\n .grid-toolbar {\n flex-wrap: wrap;\n gap: 8px;\n }\n\n .toolbar-left,\n .toolbar-right {\n flex-wrap: wrap;\n }\n\n /* Hide button text on mobile */\n .toolbar-btn .button-text {\n display: none;\n }\n}\n"] }]
1133
1207
  }], () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i1.ExportService }], { QueryInfo: [{
1134
1208
  type: Input
1135
1209
  }], ColumnConfigs: [{
@@ -1182,6 +1256,8 @@ export class QueryDataGridComponent {
1182
1256
  type: Input
1183
1257
  }], PageSize: [{
1184
1258
  type: Input
1259
+ }], ExportDataProvider: [{
1260
+ type: Input
1185
1261
  }], PageChange: [{
1186
1262
  type: Output
1187
1263
  }] }); })();