@memberjunction/ng-entity-viewer 5.49.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.
Files changed (27) hide show
  1. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts +23 -6
  2. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
  3. package/dist/lib/entity-data-grid/entity-data-grid.component.js +142 -55
  4. package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
  5. package/dist/lib/entity-viewer/entity-viewer.component.d.ts +29 -0
  6. package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
  7. package/dist/lib/entity-viewer/entity-viewer.component.js +90 -1
  8. package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
  9. package/dist/lib/view-selector/view-selector.component.js +2 -2
  10. package/dist/lib/view-selector/view-selector.component.js.map +1 -1
  11. package/dist/lib/view-types/descriptors/grid-view-type.d.ts +6 -0
  12. package/dist/lib/view-types/descriptors/grid-view-type.d.ts.map +1 -1
  13. package/dist/lib/view-types/descriptors/grid-view-type.js +6 -0
  14. package/dist/lib/view-types/descriptors/grid-view-type.js.map +1 -1
  15. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts +6 -1
  16. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts.map +1 -1
  17. package/dist/lib/view-types/renderers/grid-view-renderer.component.js +11 -3
  18. package/dist/lib/view-types/renderers/grid-view-renderer.component.js.map +1 -1
  19. package/dist/lib/view-types/view-type.contracts.d.ts +15 -0
  20. package/dist/lib/view-types/view-type.contracts.d.ts.map +1 -1
  21. package/dist/lib/view-types/view-type.contracts.js +6 -0
  22. package/dist/lib/view-types/view-type.contracts.js.map +1 -1
  23. package/dist/lib/view-workspace/view-workspace.component.d.ts +19 -0
  24. package/dist/lib/view-workspace/view-workspace.component.d.ts.map +1 -1
  25. package/dist/lib/view-workspace/view-workspace.component.js +100 -24
  26. package/dist/lib/view-workspace/view-workspace.component.js.map +1 -1
  27. package/package.json +19 -18
@@ -3,22 +3,24 @@ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
3
3
  import { LogError } from '@memberjunction/core';
4
4
  import { UUIDsEqual } from '@memberjunction/global';
5
5
  import { UserInfoEngine } from '@memberjunction/core-entities';
6
+ import { MJNotificationService } from '@memberjunction/ng-notifications';
6
7
  import { EntityViewerComponent } from '../entity-viewer/entity-viewer.component';
7
8
  import { ViewSelectorComponent } from '../view-selector/view-selector.component';
8
9
  import { ViewConfigPanelComponent } from '../view-config-panel/view-config-panel.component';
9
10
  import * as i0 from "@angular/core";
10
- import * as i1 from "@memberjunction/ng-ui-components";
11
- import * as i2 from "@memberjunction/ng-filter-builder";
12
- import * as i3 from "../entity-viewer/entity-viewer.component";
13
- import * as i4 from "../view-config-panel/view-config-panel.component";
14
- import * as i5 from "../quick-save-dialog/quick-save-dialog.component";
15
- import * as i6 from "../duplicate-view-dialog/duplicate-view-dialog.component";
16
- import * as i7 from "../shared-view-warning-dialog/shared-view-warning-dialog.component";
17
- import * as i8 from "../view-selector/view-selector.component";
18
- import * as i9 from "../view-type-switcher/view-type-switcher.component";
11
+ import * as i1 from "@memberjunction/ng-export-service";
12
+ import * as i2 from "@memberjunction/ng-ui-components";
13
+ import * as i3 from "@memberjunction/ng-filter-builder";
14
+ import * as i4 from "../entity-viewer/entity-viewer.component";
15
+ import * as i5 from "../view-config-panel/view-config-panel.component";
16
+ import * as i6 from "../quick-save-dialog/quick-save-dialog.component";
17
+ import * as i7 from "../duplicate-view-dialog/duplicate-view-dialog.component";
18
+ import * as i8 from "../shared-view-warning-dialog/shared-view-warning-dialog.component";
19
+ import * as i9 from "../view-selector/view-selector.component";
20
+ import * as i10 from "../view-type-switcher/view-type-switcher.component";
19
21
  function ViewWorkspaceComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
20
22
  const _r1 = i0.ɵɵgetCurrentView();
21
- i0.ɵɵelementStart(0, "mj-entity-viewer", 11);
23
+ i0.ɵɵelementStart(0, "mj-entity-viewer", 12);
22
24
  i0.ɵɵlistener("FilterTextChange", function ViewWorkspaceComponent_Conditional_6_Template_mj_entity_viewer_FilterTextChange_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onFilterTextChanged($event)); })("RecordSelected", function ViewWorkspaceComponent_Conditional_6_Template_mj_entity_viewer_RecordSelected_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onRecordSelected($event)); })("RecordOpened", function ViewWorkspaceComponent_Conditional_6_Template_mj_entity_viewer_RecordOpened_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onRecordOpened($event)); })("DataLoaded", function ViewWorkspaceComponent_Conditional_6_Template_mj_entity_viewer_DataLoaded_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onDataLoaded($event)); })("FilteredCountChanged", function ViewWorkspaceComponent_Conditional_6_Template_mj_entity_viewer_FilteredCountChanged_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onFilteredCountChanged($event)); })("OpenRelatedRecordRequested", function ViewWorkspaceComponent_Conditional_6_Template_mj_entity_viewer_OpenRelatedRecordRequested_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onOpenRelatedRecordRequested($event)); })("CreateRecordRequested", function ViewWorkspaceComponent_Conditional_6_Template_mj_entity_viewer_CreateRecordRequested_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCreateRecordRequested()); })("ConfigureRequested", function ViewWorkspaceComponent_Conditional_6_Template_mj_entity_viewer_ConfigureRequested_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onConfigureViewRequested()); });
23
25
  i0.ɵɵelementEnd();
24
26
  } if (rf & 2) {
@@ -27,27 +29,27 @@ function ViewWorkspaceComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
27
29
  } }
28
30
  function ViewWorkspaceComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
29
31
  const _r3 = i0.ɵɵgetCurrentView();
30
- i0.ɵɵelementStart(0, "div", 12);
32
+ i0.ɵɵelementStart(0, "div", 13);
31
33
  i0.ɵɵlistener("click", function ViewWorkspaceComponent_Conditional_8_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCloseFilterDialog()); });
32
34
  i0.ɵɵelementEnd();
33
- i0.ɵɵelementStart(1, "div", 13)(2, "div", 14)(3, "div", 15);
34
- i0.ɵɵelement(4, "i", 16);
35
+ i0.ɵɵelementStart(1, "div", 14)(2, "div", 15)(3, "div", 16);
36
+ i0.ɵɵelement(4, "i", 17);
35
37
  i0.ɵɵelementStart(5, "span");
36
38
  i0.ɵɵtext(6, "Edit Filters");
37
39
  i0.ɵɵelementEnd()();
38
- i0.ɵɵelementStart(7, "button", 17);
40
+ i0.ɵɵelementStart(7, "button", 18);
39
41
  i0.ɵɵlistener("click", function ViewWorkspaceComponent_Conditional_8_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCloseFilterDialog()); });
40
- i0.ɵɵelement(8, "i", 18);
42
+ i0.ɵɵelement(8, "i", 19);
41
43
  i0.ɵɵelementEnd()();
42
- i0.ɵɵelementStart(9, "div", 19)(10, "mj-filter-builder", 20);
44
+ i0.ɵɵelementStart(9, "div", 20)(10, "mj-filter-builder", 21);
43
45
  i0.ɵɵlistener("apply", function ViewWorkspaceComponent_Conditional_8_Template_mj_filter_builder_apply_10_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onFilterApplied($event)); })("filterChange", function ViewWorkspaceComponent_Conditional_8_Template_mj_filter_builder_filterChange_10_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.filterDialogState = $event); });
44
46
  i0.ɵɵelementEnd()();
45
- i0.ɵɵelementStart(11, "div", 21)(12, "button", 22);
47
+ i0.ɵɵelementStart(11, "div", 22)(12, "button", 23);
46
48
  i0.ɵɵlistener("click", function ViewWorkspaceComponent_Conditional_8_Template_button_click_12_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.filterDialogState && ctx_r1.onFilterApplied(ctx_r1.filterDialogState)); });
47
- i0.ɵɵelement(13, "i", 23);
49
+ i0.ɵɵelement(13, "i", 24);
48
50
  i0.ɵɵtext(14, " Apply Filters ");
49
51
  i0.ɵɵelementEnd();
50
- i0.ɵɵelementStart(15, "button", 24);
52
+ i0.ɵɵelementStart(15, "button", 25);
51
53
  i0.ɵɵlistener("click", function ViewWorkspaceComponent_Conditional_8_Template_button_click_15_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onCloseFilterDialog()); });
52
54
  i0.ɵɵtext(16, " Cancel ");
53
55
  i0.ɵɵelementEnd()()();
@@ -351,6 +353,75 @@ export class ViewWorkspaceComponent extends BaseAngularComponent {
351
353
  super();
352
354
  this.cdr = cdr;
353
355
  }
356
+ /** Controls the workspace-level export dialog (the same generic dialog the grid uses). */
357
+ showExportDialog = false;
358
+ exportDialogConfig = null;
359
+ /**
360
+ * Handles the toolbar's Export button (the `<mj-view-selector>` `ExportRequested` output). This is
361
+ * the always-visible, view-type-agnostic export for the workspace — previously the event wasn't
362
+ * wired at all, so the button did nothing on every view type. It fetches the FULL result set via the
363
+ * inner viewer's capped, filter/sort-aware fetch (bounded + warns past the cap), then opens the SAME
364
+ * generic export dialog the grid uses — with format (Excel/CSV/JSON), sampling, and file-name
365
+ * options. Because the dialog only needs data + columns (it has no view-type dependency), this works
366
+ * identically for Grid, Cards, Map and Timeline. Never fails silently — every path notifies.
367
+ */
368
+ async onExportRequested() {
369
+ const viewer = this.entityViewerRef;
370
+ if (!viewer || !this._entity) {
371
+ console.error('[ViewWorkspace] Export: viewer not ready', { hasViewer: !!viewer, hasEntity: !!this._entity });
372
+ MJNotificationService.Instance.CreateSimpleNotification('Export is not ready yet — try again in a moment.', 'error', 5000);
373
+ return;
374
+ }
375
+ MJNotificationService.Instance.CreateSimpleNotification('Preparing your export…', 'info', 2000);
376
+ try {
377
+ const rows = await viewer.FetchAllRowsForExport();
378
+ if (!rows || rows.length === 0) {
379
+ MJNotificationService.Instance.CreateSimpleNotification('Nothing to export — the view returned no records.', 'warning', 5000);
380
+ return;
381
+ }
382
+ this.exportDialogConfig = {
383
+ data: rows,
384
+ columns: this.buildExportColumns(),
385
+ defaultFileName: this.buildExportFileName(),
386
+ availableFormats: ['excel', 'csv', 'json'],
387
+ defaultFormat: 'excel',
388
+ showSamplingOptions: true,
389
+ defaultSamplingMode: 'all',
390
+ dialogTitle: `Export ${this._entity.Name}`
391
+ };
392
+ this.showExportDialog = true;
393
+ this.cdr.detectChanges();
394
+ }
395
+ catch (e) {
396
+ MJNotificationService.Instance.CreateSimpleNotification('Error preparing export — see console for details.', 'error', 5000);
397
+ console.error('[ViewWorkspace] Export error:', e);
398
+ }
399
+ }
400
+ /** Closes the workspace export dialog (the dialog performs the export + download itself). */
401
+ onExportDialogClosed(_result) {
402
+ this.showExportDialog = false;
403
+ this.exportDialogConfig = null;
404
+ this.cdr.detectChanges();
405
+ }
406
+ /** Columns to export — from the active grid state, else the view's columns, else the entity fields. */
407
+ buildExportColumns() {
408
+ if (!this._entity) {
409
+ return [];
410
+ }
411
+ const gridCols = this.currentGridState?.columnSettings;
412
+ if (gridCols && gridCols.length > 0) {
413
+ return gridCols
414
+ .filter(c => c.hidden !== true)
415
+ .map(c => ({ name: c.Name, displayName: c.DisplayName || c.Name }));
416
+ }
417
+ return this._entity.Fields
418
+ .filter(f => !f.IsVirtual)
419
+ .map(f => ({ name: f.Name, displayName: f.DisplayNameOrName }));
420
+ }
421
+ buildExportFileName() {
422
+ const viewName = this.currentViewEntity?.Name || 'Data';
423
+ return `${this._entity.Name}_${viewName}_${new Date().toISOString().split('T')[0]}`;
424
+ }
354
425
  // ========================================
355
426
  // LIFECYCLE
356
427
  // ========================================
@@ -1161,9 +1232,9 @@ export class ViewWorkspaceComponent extends BaseAngularComponent {
1161
1232
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.viewSelectorRef = _t.first);
1162
1233
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.entityViewerRef = _t.first);
1163
1234
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.viewConfigPanelRef = _t.first);
1164
- } }, inputs: { Entity: "Entity", EntityName: "EntityName", EntityID: "EntityID", AutoSaveView: "AutoSaveView", SelectedView: "SelectedView", FilterText: "FilterText", SelectedRecordId: "SelectedRecordId", ViewerConfig: "ViewerConfig" }, outputs: { OpenRecordRequested: "OpenRecordRequested", OpenViewInTabRequested: "OpenViewInTabRequested", CreateNewRecordRequested: "CreateNewRecordRequested", OpenRelatedRecordRequested: "OpenRelatedRecordRequested", RecordSelected: "RecordSelected", ViewSelected: "ViewSelected", SelectedViewChange: "SelectedViewChange", FilterTextChanged: "FilterTextChanged", DataLoaded: "DataLoaded", FilteredCountChanged: "FilteredCountChanged", BeforeViewSave: "BeforeViewSave", AfterViewSave: "AfterViewSave", BeforeViewDelete: "BeforeViewDelete", AfterViewDelete: "AfterViewDelete", SaveViewRequested: "SaveViewRequested", DeleteViewRequested: "DeleteViewRequested", DuplicateViewRequested: "DuplicateViewRequested", SaveDefaultsRequested: "SaveDefaultsRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 12, vars: 32, consts: [[1, "view-workspace"], [1, "workspace-toolbar"], [3, "ViewSelected", "SaveViewRequested", "OpenInTabRequested", "ConfigureViewRequested", "CreateNewRecordRequested", "DuplicateViewRequested", "QuickSaveRequested", "RevertRequested", "Provider", "Entity", "SelectedViewID", "ViewModified"], [1, "workspace-toolbar-spacer"], [3, "ViewTypeSelected", "Provider", "Entity", "ActiveViewTypeID"], [1, "workspace-body"], [3, "Provider", "Entity", "ViewEntity", "FilterText", "SelectedRecordID", "GridState", "AutoSaveView", "Config"], [3, "Close", "Save", "SaveDefaults", "Delete", "Duplicate", "OpenFilterDialogRequest", "Provider", "Entity", "ViewEntity", "IsOpen", "CurrentGridState", "SampleData", "ExternalFilterState", "IsSaving", "DefaultSaveAsNew", "PendingNewViewName", "PendingNewViewDescription", "PendingNewViewIsShared"], [3, "Save", "Close", "OpenAdvanced", "IsOpen", "ViewEntity", "EntityName", "Summary", "IsSaving", "DefaultSaveAsNew"], [3, "Duplicate", "Cancel", "IsOpen", "SourceViewName", "Summary"], [3, "Action", "Cancel", "IsOpen", "ViewName"], [3, "FilterTextChange", "RecordSelected", "RecordOpened", "DataLoaded", "FilteredCountChanged", "OpenRelatedRecordRequested", "CreateRecordRequested", "ConfigureRequested", "Provider", "Entity", "ViewEntity", "FilterText", "SelectedRecordID", "GridState", "AutoSaveView", "Config"], [1, "filter-dialog-backdrop", 3, "click"], ["role", "dialog", "aria-modal", "true", 1, "filter-dialog"], [1, "filter-dialog-header"], [1, "filter-dialog-title"], [1, "fa-solid", "fa-filter"], ["type", "button", "title", "Close", 1, "filter-dialog-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "filter-dialog-content"], [3, "apply", "filterChange", "fields", "filter", "showSummary"], [1, "filter-dialog-footer"], ["mjButton", "", "variant", "primary", "size", "sm", "type", "button", 3, "click"], [1, "fa-solid", "fa-check"], ["mjButton", "", "variant", "flat", "size", "sm", "type", "button", 3, "click"]], template: function ViewWorkspaceComponent_Template(rf, ctx) { if (rf & 1) {
1235
+ } }, inputs: { Entity: "Entity", EntityName: "EntityName", EntityID: "EntityID", AutoSaveView: "AutoSaveView", SelectedView: "SelectedView", FilterText: "FilterText", SelectedRecordId: "SelectedRecordId", ViewerConfig: "ViewerConfig" }, outputs: { OpenRecordRequested: "OpenRecordRequested", OpenViewInTabRequested: "OpenViewInTabRequested", CreateNewRecordRequested: "CreateNewRecordRequested", OpenRelatedRecordRequested: "OpenRelatedRecordRequested", RecordSelected: "RecordSelected", ViewSelected: "ViewSelected", SelectedViewChange: "SelectedViewChange", FilterTextChanged: "FilterTextChanged", DataLoaded: "DataLoaded", FilteredCountChanged: "FilteredCountChanged", BeforeViewSave: "BeforeViewSave", AfterViewSave: "AfterViewSave", BeforeViewDelete: "BeforeViewDelete", AfterViewDelete: "AfterViewDelete", SaveViewRequested: "SaveViewRequested", DeleteViewRequested: "DeleteViewRequested", DuplicateViewRequested: "DuplicateViewRequested", SaveDefaultsRequested: "SaveDefaultsRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 13, vars: 34, consts: [[1, "view-workspace"], [1, "workspace-toolbar"], [3, "ViewSelected", "SaveViewRequested", "OpenInTabRequested", "ConfigureViewRequested", "CreateNewRecordRequested", "ExportRequested", "DuplicateViewRequested", "QuickSaveRequested", "RevertRequested", "Provider", "Entity", "SelectedViewID", "ViewModified"], [1, "workspace-toolbar-spacer"], [3, "ViewTypeSelected", "Provider", "Entity", "ActiveViewTypeID"], [1, "workspace-body"], [3, "Provider", "Entity", "ViewEntity", "FilterText", "SelectedRecordID", "GridState", "AutoSaveView", "Config"], [3, "Close", "Save", "SaveDefaults", "Delete", "Duplicate", "OpenFilterDialogRequest", "Provider", "Entity", "ViewEntity", "IsOpen", "CurrentGridState", "SampleData", "ExternalFilterState", "IsSaving", "DefaultSaveAsNew", "PendingNewViewName", "PendingNewViewDescription", "PendingNewViewIsShared"], [3, "Save", "Close", "OpenAdvanced", "IsOpen", "ViewEntity", "EntityName", "Summary", "IsSaving", "DefaultSaveAsNew"], [3, "Duplicate", "Cancel", "IsOpen", "SourceViewName", "Summary"], [3, "Action", "Cancel", "IsOpen", "ViewName"], [3, "closed", "visible", "config"], [3, "FilterTextChange", "RecordSelected", "RecordOpened", "DataLoaded", "FilteredCountChanged", "OpenRelatedRecordRequested", "CreateRecordRequested", "ConfigureRequested", "Provider", "Entity", "ViewEntity", "FilterText", "SelectedRecordID", "GridState", "AutoSaveView", "Config"], [1, "filter-dialog-backdrop", 3, "click"], ["role", "dialog", "aria-modal", "true", 1, "filter-dialog"], [1, "filter-dialog-header"], [1, "filter-dialog-title"], [1, "fa-solid", "fa-filter"], ["type", "button", "title", "Close", 1, "filter-dialog-close", 3, "click"], [1, "fa-solid", "fa-times"], [1, "filter-dialog-content"], [3, "apply", "filterChange", "fields", "filter", "showSummary"], [1, "filter-dialog-footer"], ["mjButton", "", "variant", "primary", "size", "sm", "type", "button", 3, "click"], [1, "fa-solid", "fa-check"], ["mjButton", "", "variant", "flat", "size", "sm", "type", "button", 3, "click"]], template: function ViewWorkspaceComponent_Template(rf, ctx) { if (rf & 1) {
1165
1236
  i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "mj-view-selector", 2);
1166
- i0.ɵɵlistener("ViewSelected", function ViewWorkspaceComponent_Template_mj_view_selector_ViewSelected_2_listener($event) { return ctx.onViewSelected($event); })("SaveViewRequested", function ViewWorkspaceComponent_Template_mj_view_selector_SaveViewRequested_2_listener($event) { return ctx.onSaveViewRequested($event); })("OpenInTabRequested", function ViewWorkspaceComponent_Template_mj_view_selector_OpenInTabRequested_2_listener($event) { return ctx.onOpenInTabRequested($event); })("ConfigureViewRequested", function ViewWorkspaceComponent_Template_mj_view_selector_ConfigureViewRequested_2_listener() { return ctx.onConfigureViewRequested(); })("CreateNewRecordRequested", function ViewWorkspaceComponent_Template_mj_view_selector_CreateNewRecordRequested_2_listener() { return ctx.onCreateNewRecordRequested(); })("DuplicateViewRequested", function ViewWorkspaceComponent_Template_mj_view_selector_DuplicateViewRequested_2_listener($event) { return ctx.onDuplicateViewRequested($event); })("QuickSaveRequested", function ViewWorkspaceComponent_Template_mj_view_selector_QuickSaveRequested_2_listener($event) { return ctx.onQuickSaveRequested($event); })("RevertRequested", function ViewWorkspaceComponent_Template_mj_view_selector_RevertRequested_2_listener() { return ctx.onRevertView(); });
1237
+ i0.ɵɵlistener("ViewSelected", function ViewWorkspaceComponent_Template_mj_view_selector_ViewSelected_2_listener($event) { return ctx.onViewSelected($event); })("SaveViewRequested", function ViewWorkspaceComponent_Template_mj_view_selector_SaveViewRequested_2_listener($event) { return ctx.onSaveViewRequested($event); })("OpenInTabRequested", function ViewWorkspaceComponent_Template_mj_view_selector_OpenInTabRequested_2_listener($event) { return ctx.onOpenInTabRequested($event); })("ConfigureViewRequested", function ViewWorkspaceComponent_Template_mj_view_selector_ConfigureViewRequested_2_listener() { return ctx.onConfigureViewRequested(); })("CreateNewRecordRequested", function ViewWorkspaceComponent_Template_mj_view_selector_CreateNewRecordRequested_2_listener() { return ctx.onCreateNewRecordRequested(); })("ExportRequested", function ViewWorkspaceComponent_Template_mj_view_selector_ExportRequested_2_listener() { return ctx.onExportRequested(); })("DuplicateViewRequested", function ViewWorkspaceComponent_Template_mj_view_selector_DuplicateViewRequested_2_listener($event) { return ctx.onDuplicateViewRequested($event); })("QuickSaveRequested", function ViewWorkspaceComponent_Template_mj_view_selector_QuickSaveRequested_2_listener($event) { return ctx.onQuickSaveRequested($event); })("RevertRequested", function ViewWorkspaceComponent_Template_mj_view_selector_RevertRequested_2_listener() { return ctx.onRevertView(); });
1167
1238
  i0.ɵɵelementEnd();
1168
1239
  i0.ɵɵelement(3, "span", 3);
1169
1240
  i0.ɵɵelementStart(4, "mj-view-type-switcher", 4);
@@ -1184,6 +1255,9 @@ export class ViewWorkspaceComponent extends BaseAngularComponent {
1184
1255
  i0.ɵɵelementEnd();
1185
1256
  i0.ɵɵelementStart(11, "mj-shared-view-warning-dialog", 10);
1186
1257
  i0.ɵɵlistener("Action", function ViewWorkspaceComponent_Template_mj_shared_view_warning_dialog_Action_11_listener($event) { return ctx.onSharedViewAction($event); })("Cancel", function ViewWorkspaceComponent_Template_mj_shared_view_warning_dialog_Cancel_11_listener() { return ctx.onSharedViewWarningCancel(); });
1258
+ i0.ɵɵelementEnd();
1259
+ i0.ɵɵelementStart(12, "mj-export-dialog", 11);
1260
+ i0.ɵɵlistener("closed", function ViewWorkspaceComponent_Template_mj_export_dialog_closed_12_listener($event) { return ctx.onExportDialogClosed($event); });
1187
1261
  i0.ɵɵelementEnd()();
1188
1262
  } if (rf & 2) {
1189
1263
  i0.ɵɵadvance(2);
@@ -1202,11 +1276,13 @@ export class ViewWorkspaceComponent extends BaseAngularComponent {
1202
1276
  i0.ɵɵproperty("IsOpen", ctx.showDuplicateDialog)("SourceViewName", ctx.duplicateSourceViewName)("Summary", ctx.duplicateSummary);
1203
1277
  i0.ɵɵadvance();
1204
1278
  i0.ɵɵproperty("IsOpen", ctx.showSharedViewWarning)("ViewName", (ctx.currentViewEntity == null ? null : ctx.currentViewEntity.Name) ?? "");
1205
- } }, dependencies: [i1.MJButtonDirective, i2.FilterBuilderComponent, i3.EntityViewerComponent, i4.ViewConfigPanelComponent, i5.QuickSaveDialogComponent, i6.DuplicateViewDialogComponent, i7.SharedViewWarningDialogComponent, i8.ViewSelectorComponent, i9.ViewTypeSwitcherComponent], styles: ["[_nghost-%COMP%] {\n display: flex; \n\n flex: 1 1 auto; \n\n width: 100%;\n height: 100%;\n min-width: 0; \n\n}\n\n.view-workspace[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n flex: 1 1 auto; \n\n width: 100%; \n\n min-width: 0; \n\n background: var(--mj-bg-page);\n}\n\n\n\n.workspace-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n flex-shrink: 0;\n}\n\n\n\n.workspace-toolbar-spacer[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n\n\n.workspace-body[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-height: 0;\n position: relative;\n overflow: hidden;\n}\n\n.workspace-body[_ngcontent-%COMP%] mj-entity-viewer[_ngcontent-%COMP%] {\n display: block;\n height: 100%;\n}\n\n\n\n\n\n.filter-dialog-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 2000;\n animation: _ngcontent-%COMP%_vw-fade-in 0.15s ease;\n}\n\n.filter-dialog[_ngcontent-%COMP%] {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 90%;\n max-width: 800px;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 20px 60px color-mix(in srgb, var(--mj-text-primary) 25%, transparent);\n z-index: 2001;\n animation: _ngcontent-%COMP%_vw-slide-in 0.2s ease;\n}\n\n.filter-dialog-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.filter-dialog-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.filter-dialog-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.filter-dialog-close[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s ease, color 0.15s ease;\n}\n\n.filter-dialog-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.filter-dialog-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n min-height: 200px;\n}\n\n.filter-dialog-footer[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 0 0 12px 12px;\n flex-shrink: 0;\n}\n\n@keyframes _ngcontent-%COMP%_vw-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes _ngcontent-%COMP%_vw-slide-in {\n from {\n opacity: 0;\n transform: translate(-50%, -48%);\n }\n to {\n opacity: 1;\n transform: translate(-50%, -50%);\n }\n}\n\n@media (max-width: 600px) {\n .filter-dialog[_ngcontent-%COMP%] {\n width: 95%;\n max-height: 90vh;\n border-radius: 8px;\n }\n\n .filter-dialog-footer[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n }\n}"] });
1279
+ i0.ɵɵadvance();
1280
+ i0.ɵɵproperty("visible", ctx.showExportDialog)("config", ctx.exportDialogConfig);
1281
+ } }, dependencies: [i1.ExportDialogComponent, i2.MJButtonDirective, i3.FilterBuilderComponent, i4.EntityViewerComponent, i5.ViewConfigPanelComponent, i6.QuickSaveDialogComponent, i7.DuplicateViewDialogComponent, i8.SharedViewWarningDialogComponent, i9.ViewSelectorComponent, i10.ViewTypeSwitcherComponent], styles: ["[_nghost-%COMP%] {\n display: flex; \n\n flex: 1 1 auto; \n\n width: 100%;\n height: 100%;\n min-width: 0; \n\n}\n\n.view-workspace[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n flex: 1 1 auto; \n\n width: 100%; \n\n min-width: 0; \n\n background: var(--mj-bg-page);\n}\n\n\n\n.workspace-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n flex-shrink: 0;\n}\n\n\n\n.workspace-toolbar-spacer[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n\n\n.workspace-body[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-height: 0;\n position: relative;\n overflow: hidden;\n}\n\n.workspace-body[_ngcontent-%COMP%] mj-entity-viewer[_ngcontent-%COMP%] {\n display: block;\n height: 100%;\n}\n\n\n\n\n\n.filter-dialog-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 2000;\n animation: _ngcontent-%COMP%_vw-fade-in 0.15s ease;\n}\n\n.filter-dialog[_ngcontent-%COMP%] {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 90%;\n max-width: 800px;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 20px 60px color-mix(in srgb, var(--mj-text-primary) 25%, transparent);\n z-index: 2001;\n animation: _ngcontent-%COMP%_vw-slide-in 0.2s ease;\n}\n\n.filter-dialog-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.filter-dialog-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.filter-dialog-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.filter-dialog-close[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s ease, color 0.15s ease;\n}\n\n.filter-dialog-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.filter-dialog-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n min-height: 200px;\n}\n\n.filter-dialog-footer[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 0 0 12px 12px;\n flex-shrink: 0;\n}\n\n@keyframes _ngcontent-%COMP%_vw-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes _ngcontent-%COMP%_vw-slide-in {\n from {\n opacity: 0;\n transform: translate(-50%, -48%);\n }\n to {\n opacity: 1;\n transform: translate(-50%, -50%);\n }\n}\n\n@media (max-width: 600px) {\n .filter-dialog[_ngcontent-%COMP%] {\n width: 95%;\n max-height: 90vh;\n border-radius: 8px;\n }\n\n .filter-dialog-footer[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n }\n}"] });
1206
1282
  }
1207
1283
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewWorkspaceComponent, [{
1208
1284
  type: Component,
1209
- args: [{ standalone: false, selector: 'mj-view-workspace', template: "<div class=\"view-workspace\">\n <!-- Toolbar: view selector + view-type switcher (single row) -->\n <div class=\"workspace-toolbar\">\n <mj-view-selector\n [Provider]=\"Provider\"\n [Entity]=\"Entity\"\n [SelectedViewID]=\"selectedViewId\"\n [ViewModified]=\"viewModified\"\n (ViewSelected)=\"onViewSelected($event)\"\n (SaveViewRequested)=\"onSaveViewRequested($event)\"\n (OpenInTabRequested)=\"onOpenInTabRequested($event)\"\n (ConfigureViewRequested)=\"onConfigureViewRequested()\"\n (CreateNewRecordRequested)=\"onCreateNewRecordRequested()\"\n (DuplicateViewRequested)=\"onDuplicateViewRequested($event)\"\n (QuickSaveRequested)=\"onQuickSaveRequested($event)\"\n (RevertRequested)=\"onRevertView()\">\n </mj-view-selector>\n\n <span class=\"workspace-toolbar-spacer\"></span>\n\n <!-- View-type switcher lifted into the top toolbar (same row as the selector). The inner\n entity-viewer's own header switcher is suppressed via innerViewerConfig so it appears\n exactly once. Switching routes to the viewer via SelectViewTypeById. -->\n <mj-view-type-switcher\n [Provider]=\"Provider\"\n [Entity]=\"Entity\"\n [ActiveViewTypeID]=\"activeViewTypeId\"\n (ViewTypeSelected)=\"onToolbarViewTypeSelected($event)\">\n </mj-view-type-switcher>\n </div>\n\n <!-- Data renderer -->\n <div class=\"workspace-body\">\n @if (Entity) {\n <mj-entity-viewer\n [Provider]=\"Provider\"\n [Entity]=\"Entity\"\n [ViewEntity]=\"currentViewEntity\"\n [FilterText]=\"filterText\"\n [SelectedRecordID]=\"selectedRecordId\"\n [GridState]=\"currentGridState\"\n [AutoSaveView]=\"AutoSaveView\"\n [Config]=\"innerViewerConfig\"\n (FilterTextChange)=\"onFilterTextChanged($event)\"\n (RecordSelected)=\"onRecordSelected($event)\"\n (RecordOpened)=\"onRecordOpened($event)\"\n (DataLoaded)=\"onDataLoaded($event)\"\n (FilteredCountChanged)=\"onFilteredCountChanged($event)\"\n (OpenRelatedRecordRequested)=\"onOpenRelatedRecordRequested($event)\"\n (CreateRecordRequested)=\"onCreateRecordRequested()\"\n (ConfigureRequested)=\"onConfigureViewRequested()\">\n </mj-entity-viewer>\n }\n </div>\n\n <!-- View configuration panel (slide-in) -->\n <mj-view-config-panel\n [Provider]=\"Provider\"\n [Entity]=\"Entity\"\n [ViewEntity]=\"currentViewEntity\"\n [IsOpen]=\"isConfigPanelOpen\"\n [CurrentGridState]=\"currentGridState\"\n [SampleData]=\"loadedRecords\"\n [ExternalFilterState]=\"filterDialogState\"\n [IsSaving]=\"isSavingView\"\n [DefaultSaveAsNew]=\"defaultSaveAsNew\"\n [PendingNewViewName]=\"pendingNewViewName\"\n [PendingNewViewDescription]=\"pendingNewViewDescription\"\n [PendingNewViewIsShared]=\"pendingNewViewIsShared\"\n (Close)=\"onCloseConfigPanel()\"\n (Save)=\"onSaveView($event)\"\n (SaveDefaults)=\"onSaveDefaultViewSettings($event)\"\n (Delete)=\"onDeleteView()\"\n (Duplicate)=\"onDuplicateFromPanel()\"\n (OpenFilterDialogRequest)=\"onOpenFilterDialogRequest($event)\">\n </mj-view-config-panel>\n\n <!-- Full-width filter dialog -->\n @if (isFilterDialogOpen) {\n <div class=\"filter-dialog-backdrop\" (click)=\"onCloseFilterDialog()\"></div>\n <div class=\"filter-dialog\" role=\"dialog\" aria-modal=\"true\">\n <div class=\"filter-dialog-header\">\n <div class=\"filter-dialog-title\">\n <i class=\"fa-solid fa-filter\"></i>\n <span>Edit Filters</span>\n </div>\n <button class=\"filter-dialog-close\" type=\"button\" (click)=\"onCloseFilterDialog()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"filter-dialog-content\">\n <mj-filter-builder\n [fields]=\"filterDialogFields\"\n [filter]=\"filterDialogState\"\n [showSummary]=\"true\"\n (apply)=\"onFilterApplied($event)\"\n (filterChange)=\"filterDialogState = $event\">\n </mj-filter-builder>\n </div>\n <div class=\"filter-dialog-footer\">\n <button mjButton variant=\"primary\" size=\"sm\" type=\"button\"\n (click)=\"filterDialogState && onFilterApplied(filterDialogState)\">\n <i class=\"fa-solid fa-check\"></i> Apply Filters\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" type=\"button\" (click)=\"onCloseFilterDialog()\">\n Cancel\n </button>\n </div>\n </div>\n }\n\n <!-- Quick save dialog -->\n <mj-quick-save-dialog\n [IsOpen]=\"showQuickSaveDialog\"\n [ViewEntity]=\"currentViewEntity\"\n [EntityName]=\"Entity?.DisplayNameOrName ?? ''\"\n [Summary]=\"quickSaveSummary\"\n [IsSaving]=\"isSavingView\"\n [DefaultSaveAsNew]=\"defaultSaveAsNew\"\n (Save)=\"onQuickSave($event)\"\n (Close)=\"onQuickSaveClose()\"\n (OpenAdvanced)=\"onQuickSaveOpenAdvanced($event)\">\n </mj-quick-save-dialog>\n\n <!-- Duplicate view dialog -->\n <mj-duplicate-view-dialog\n [IsOpen]=\"showDuplicateDialog\"\n [SourceViewName]=\"duplicateSourceViewName\"\n [Summary]=\"duplicateSummary\"\n (Duplicate)=\"onDuplicateConfirmed($event)\"\n (Cancel)=\"onDuplicateCancel()\">\n </mj-duplicate-view-dialog>\n\n <!-- Shared view warning dialog -->\n <mj-shared-view-warning-dialog\n [IsOpen]=\"showSharedViewWarning\"\n [ViewName]=\"currentViewEntity?.Name ?? ''\"\n (Action)=\"onSharedViewAction($event)\"\n (Cancel)=\"onSharedViewWarningCancel()\">\n </mj-shared-view-warning-dialog>\n</div>\n", styles: [":host {\n display: flex; /* keep block if you prefer; width:100% still works */\n flex: 1 1 auto; /* grow to fill the new flex row in .content-body */\n width: 100%;\n height: 100%;\n min-width: 0; /* lets it shrink too, prevents overflow when narrow */\n}\n\n.view-workspace {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n flex: 1 1 auto; /* add: grow to fill the host's flex row */\n width: 100%; /* add */\n min-width: 0; /* add: mirror min-height so it can shrink too */\n background: var(--mj-bg-page);\n}\n\n/* Toolbar row: view selector + view-type switcher */\n.workspace-toolbar {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n flex-shrink: 0;\n}\n\n/* Pushes the view-type switcher to the right end of the toolbar row */\n.workspace-toolbar-spacer {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n/* Data renderer fills remaining space */\n.workspace-body {\n flex: 1 1 auto;\n min-height: 0;\n position: relative;\n overflow: hidden;\n}\n\n.workspace-body mj-entity-viewer {\n display: block;\n height: 100%;\n}\n\n/* ============================\n Full-width filter dialog\n ============================ */\n.filter-dialog-backdrop {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 2000;\n animation: vw-fade-in 0.15s ease;\n}\n\n.filter-dialog {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 90%;\n max-width: 800px;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 20px 60px color-mix(in srgb, var(--mj-text-primary) 25%, transparent);\n z-index: 2001;\n animation: vw-slide-in 0.2s ease;\n}\n\n.filter-dialog-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.filter-dialog-title {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.filter-dialog-title i {\n color: var(--mj-brand-primary);\n}\n\n.filter-dialog-close {\n width: 36px;\n height: 36px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s ease, color 0.15s ease;\n}\n\n.filter-dialog-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.filter-dialog-content {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n min-height: 200px;\n}\n\n.filter-dialog-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 0 0 12px 12px;\n flex-shrink: 0;\n}\n\n@keyframes vw-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes vw-slide-in {\n from {\n opacity: 0;\n transform: translate(-50%, -48%);\n }\n to {\n opacity: 1;\n transform: translate(-50%, -50%);\n }\n}\n\n@media (max-width: 600px) {\n .filter-dialog {\n width: 95%;\n max-height: 90vh;\n border-radius: 8px;\n }\n\n .filter-dialog-footer {\n flex-direction: column;\n align-items: stretch;\n }\n}\n"] }]
1285
+ args: [{ standalone: false, selector: 'mj-view-workspace', template: "<div class=\"view-workspace\">\n <!-- Toolbar: view selector + view-type switcher (single row) -->\n <div class=\"workspace-toolbar\">\n <mj-view-selector\n [Provider]=\"Provider\"\n [Entity]=\"Entity\"\n [SelectedViewID]=\"selectedViewId\"\n [ViewModified]=\"viewModified\"\n (ViewSelected)=\"onViewSelected($event)\"\n (SaveViewRequested)=\"onSaveViewRequested($event)\"\n (OpenInTabRequested)=\"onOpenInTabRequested($event)\"\n (ConfigureViewRequested)=\"onConfigureViewRequested()\"\n (CreateNewRecordRequested)=\"onCreateNewRecordRequested()\"\n (ExportRequested)=\"onExportRequested()\"\n (DuplicateViewRequested)=\"onDuplicateViewRequested($event)\"\n (QuickSaveRequested)=\"onQuickSaveRequested($event)\"\n (RevertRequested)=\"onRevertView()\">\n </mj-view-selector>\n\n <span class=\"workspace-toolbar-spacer\"></span>\n\n <!-- View-type switcher lifted into the top toolbar (same row as the selector). The inner\n entity-viewer's own header switcher is suppressed via innerViewerConfig so it appears\n exactly once. Switching routes to the viewer via SelectViewTypeById. -->\n <mj-view-type-switcher\n [Provider]=\"Provider\"\n [Entity]=\"Entity\"\n [ActiveViewTypeID]=\"activeViewTypeId\"\n (ViewTypeSelected)=\"onToolbarViewTypeSelected($event)\">\n </mj-view-type-switcher>\n </div>\n\n <!-- Data renderer -->\n <div class=\"workspace-body\">\n @if (Entity) {\n <mj-entity-viewer\n [Provider]=\"Provider\"\n [Entity]=\"Entity\"\n [ViewEntity]=\"currentViewEntity\"\n [FilterText]=\"filterText\"\n [SelectedRecordID]=\"selectedRecordId\"\n [GridState]=\"currentGridState\"\n [AutoSaveView]=\"AutoSaveView\"\n [Config]=\"innerViewerConfig\"\n (FilterTextChange)=\"onFilterTextChanged($event)\"\n (RecordSelected)=\"onRecordSelected($event)\"\n (RecordOpened)=\"onRecordOpened($event)\"\n (DataLoaded)=\"onDataLoaded($event)\"\n (FilteredCountChanged)=\"onFilteredCountChanged($event)\"\n (OpenRelatedRecordRequested)=\"onOpenRelatedRecordRequested($event)\"\n (CreateRecordRequested)=\"onCreateRecordRequested()\"\n (ConfigureRequested)=\"onConfigureViewRequested()\">\n </mj-entity-viewer>\n }\n </div>\n\n <!-- View configuration panel (slide-in) -->\n <mj-view-config-panel\n [Provider]=\"Provider\"\n [Entity]=\"Entity\"\n [ViewEntity]=\"currentViewEntity\"\n [IsOpen]=\"isConfigPanelOpen\"\n [CurrentGridState]=\"currentGridState\"\n [SampleData]=\"loadedRecords\"\n [ExternalFilterState]=\"filterDialogState\"\n [IsSaving]=\"isSavingView\"\n [DefaultSaveAsNew]=\"defaultSaveAsNew\"\n [PendingNewViewName]=\"pendingNewViewName\"\n [PendingNewViewDescription]=\"pendingNewViewDescription\"\n [PendingNewViewIsShared]=\"pendingNewViewIsShared\"\n (Close)=\"onCloseConfigPanel()\"\n (Save)=\"onSaveView($event)\"\n (SaveDefaults)=\"onSaveDefaultViewSettings($event)\"\n (Delete)=\"onDeleteView()\"\n (Duplicate)=\"onDuplicateFromPanel()\"\n (OpenFilterDialogRequest)=\"onOpenFilterDialogRequest($event)\">\n </mj-view-config-panel>\n\n <!-- Full-width filter dialog -->\n @if (isFilterDialogOpen) {\n <div class=\"filter-dialog-backdrop\" (click)=\"onCloseFilterDialog()\"></div>\n <div class=\"filter-dialog\" role=\"dialog\" aria-modal=\"true\">\n <div class=\"filter-dialog-header\">\n <div class=\"filter-dialog-title\">\n <i class=\"fa-solid fa-filter\"></i>\n <span>Edit Filters</span>\n </div>\n <button class=\"filter-dialog-close\" type=\"button\" (click)=\"onCloseFilterDialog()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"filter-dialog-content\">\n <mj-filter-builder\n [fields]=\"filterDialogFields\"\n [filter]=\"filterDialogState\"\n [showSummary]=\"true\"\n (apply)=\"onFilterApplied($event)\"\n (filterChange)=\"filterDialogState = $event\">\n </mj-filter-builder>\n </div>\n <div class=\"filter-dialog-footer\">\n <button mjButton variant=\"primary\" size=\"sm\" type=\"button\"\n (click)=\"filterDialogState && onFilterApplied(filterDialogState)\">\n <i class=\"fa-solid fa-check\"></i> Apply Filters\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" type=\"button\" (click)=\"onCloseFilterDialog()\">\n Cancel\n </button>\n </div>\n </div>\n }\n\n <!-- Quick save dialog -->\n <mj-quick-save-dialog\n [IsOpen]=\"showQuickSaveDialog\"\n [ViewEntity]=\"currentViewEntity\"\n [EntityName]=\"Entity?.DisplayNameOrName ?? ''\"\n [Summary]=\"quickSaveSummary\"\n [IsSaving]=\"isSavingView\"\n [DefaultSaveAsNew]=\"defaultSaveAsNew\"\n (Save)=\"onQuickSave($event)\"\n (Close)=\"onQuickSaveClose()\"\n (OpenAdvanced)=\"onQuickSaveOpenAdvanced($event)\">\n </mj-quick-save-dialog>\n\n <!-- Duplicate view dialog -->\n <mj-duplicate-view-dialog\n [IsOpen]=\"showDuplicateDialog\"\n [SourceViewName]=\"duplicateSourceViewName\"\n [Summary]=\"duplicateSummary\"\n (Duplicate)=\"onDuplicateConfirmed($event)\"\n (Cancel)=\"onDuplicateCancel()\">\n </mj-duplicate-view-dialog>\n\n <!-- Shared view warning dialog -->\n <mj-shared-view-warning-dialog\n [IsOpen]=\"showSharedViewWarning\"\n [ViewName]=\"currentViewEntity?.Name ?? ''\"\n (Action)=\"onSharedViewAction($event)\"\n (Cancel)=\"onSharedViewWarningCancel()\">\n </mj-shared-view-warning-dialog>\n\n <!-- Export dialog \u2014 opened by the toolbar Export button for ANY view type. The dialog is generic\n (data + columns only), so Excel/CSV/JSON + sampling work identically on Grid/Cards/Map/Timeline. -->\n <mj-export-dialog\n [visible]=\"showExportDialog\"\n [config]=\"exportDialogConfig\"\n (closed)=\"onExportDialogClosed($event)\">\n </mj-export-dialog>\n</div>\n", styles: [":host {\n display: flex; /* keep block if you prefer; width:100% still works */\n flex: 1 1 auto; /* grow to fill the new flex row in .content-body */\n width: 100%;\n height: 100%;\n min-width: 0; /* lets it shrink too, prevents overflow when narrow */\n}\n\n.view-workspace {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n flex: 1 1 auto; /* add: grow to fill the host's flex row */\n width: 100%; /* add */\n min-width: 0; /* add: mirror min-height so it can shrink too */\n background: var(--mj-bg-page);\n}\n\n/* Toolbar row: view selector + view-type switcher */\n.workspace-toolbar {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 16px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n flex-shrink: 0;\n}\n\n/* Pushes the view-type switcher to the right end of the toolbar row */\n.workspace-toolbar-spacer {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n/* Data renderer fills remaining space */\n.workspace-body {\n flex: 1 1 auto;\n min-height: 0;\n position: relative;\n overflow: hidden;\n}\n\n.workspace-body mj-entity-viewer {\n display: block;\n height: 100%;\n}\n\n/* ============================\n Full-width filter dialog\n ============================ */\n.filter-dialog-backdrop {\n position: fixed;\n inset: 0;\n background: var(--mj-bg-overlay);\n z-index: 2000;\n animation: vw-fade-in 0.15s ease;\n}\n\n.filter-dialog {\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 90%;\n max-width: 800px;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 12px;\n box-shadow: 0 20px 60px color-mix(in srgb, var(--mj-text-primary) 25%, transparent);\n z-index: 2001;\n animation: vw-slide-in 0.2s ease;\n}\n\n.filter-dialog-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.filter-dialog-title {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.filter-dialog-title i {\n color: var(--mj-brand-primary);\n}\n\n.filter-dialog-close {\n width: 36px;\n height: 36px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.15s ease, color 0.15s ease;\n}\n\n.filter-dialog-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.filter-dialog-content {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n min-height: 200px;\n}\n\n.filter-dialog-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 0 0 12px 12px;\n flex-shrink: 0;\n}\n\n@keyframes vw-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes vw-slide-in {\n from {\n opacity: 0;\n transform: translate(-50%, -48%);\n }\n to {\n opacity: 1;\n transform: translate(-50%, -50%);\n }\n}\n\n@media (max-width: 600px) {\n .filter-dialog {\n width: 95%;\n max-height: 90vh;\n border-radius: 8px;\n }\n\n .filter-dialog-footer {\n flex-direction: column;\n align-items: stretch;\n }\n}\n"] }]
1210
1286
  }], () => [{ type: i0.ChangeDetectorRef }], { Entity: [{
1211
1287
  type: Input
1212
1288
  }], EntityName: [{
@@ -1269,5 +1345,5 @@ export class ViewWorkspaceComponent extends BaseAngularComponent {
1269
1345
  type: ViewChild,
1270
1346
  args: [ViewConfigPanelComponent]
1271
1347
  }] }); })();
1272
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ViewWorkspaceComponent, { className: "ViewWorkspaceComponent", filePath: "src/lib/view-workspace/view-workspace.component.ts", lineNumber: 129 }); })();
1348
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ViewWorkspaceComponent, { className: "ViewWorkspaceComponent", filePath: "src/lib/view-workspace/view-workspace.component.ts", lineNumber: 132 }); })();
1273
1349
  //# sourceMappingURL=view-workspace.component.js.map