@memberjunction/ng-entity-viewer 6.1.3 → 6.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/aggregate-panel/aggregate-panel.component.d.ts +18 -2
- package/dist/lib/aggregate-panel/aggregate-panel.component.d.ts.map +1 -1
- package/dist/lib/aggregate-panel/aggregate-panel.component.js +47 -18
- package/dist/lib/aggregate-panel/aggregate-panel.component.js.map +1 -1
- package/dist/lib/entity-cards/entity-cards.component.d.ts +1 -0
- package/dist/lib/entity-cards/entity-cards.component.d.ts.map +1 -1
- package/dist/lib/entity-cards/entity-cards.component.js +11 -2
- package/dist/lib/entity-cards/entity-cards.component.js.map +1 -1
- package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts +40 -7
- package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
- package/dist/lib/entity-data-grid/entity-data-grid.component.js +137 -76
- package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts.map +1 -1
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js +10 -8
- package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js.map +1 -1
- package/dist/lib/entity-viewer/entity-viewer.component.d.ts +6 -0
- package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
- package/dist/lib/entity-viewer/entity-viewer.component.js +12 -1
- package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
- package/dist/lib/utils/aggregate-field.util.d.ts +21 -0
- package/dist/lib/utils/aggregate-field.util.d.ts.map +1 -0
- package/dist/lib/utils/aggregate-field.util.js +40 -0
- package/dist/lib/utils/aggregate-field.util.js.map +1 -0
- package/dist/lib/utils/export-column.util.d.ts +14 -0
- package/dist/lib/utils/export-column.util.d.ts.map +1 -0
- package/dist/lib/utils/export-column.util.js +34 -0
- package/dist/lib/utils/export-column.util.js.map +1 -0
- package/dist/lib/view-config-panel/view-config-panel.component.d.ts +5 -1
- package/dist/lib/view-config-panel/view-config-panel.component.d.ts.map +1 -1
- package/dist/lib/view-config-panel/view-config-panel.component.js +15 -8
- package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
- package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts +3 -0
- package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts.map +1 -1
- package/dist/lib/view-types/renderers/grid-view-renderer.component.js +5 -1
- package/dist/lib/view-types/renderers/grid-view-renderer.component.js.map +1 -1
- package/dist/lib/view-types/renderers/timeline-view-renderer.component.d.ts +2 -0
- package/dist/lib/view-types/renderers/timeline-view-renderer.component.d.ts.map +1 -1
- package/dist/lib/view-types/renderers/timeline-view-renderer.component.js +11 -2
- package/dist/lib/view-types/renderers/timeline-view-renderer.component.js.map +1 -1
- package/dist/lib/view-types/view-type.contracts.d.ts +8 -0
- package/dist/lib/view-types/view-type.contracts.d.ts.map +1 -1
- package/dist/lib/view-types/view-type.contracts.js.map +1 -1
- package/dist/lib/view-workspace/view-workspace.component.d.ts +9 -1
- package/dist/lib/view-workspace/view-workspace.component.d.ts.map +1 -1
- package/dist/lib/view-workspace/view-workspace.component.js +39 -8
- package/dist/lib/view-workspace/view-workspace.component.js.map +1 -1
- package/dist/public-api.d.ts +2 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +2 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +20 -19
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from '@angular/core';
|
|
2
2
|
import { EntityInfo, RunViewParams } from '@memberjunction/core';
|
|
3
|
+
import { ExportColumn } from '@memberjunction/export-engine';
|
|
3
4
|
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
4
5
|
import { PageChangeEvent } from '@memberjunction/ng-pagination';
|
|
5
6
|
import { ListManagementDialogConfig, ListManagementResult } from '@memberjunction/ng-list-management';
|
|
@@ -170,6 +171,8 @@ export declare class GridViewRendererComponent extends BaseAngularComponent impl
|
|
|
170
171
|
* @returns true when the export succeeded, false when the grid isn't ready or the export failed.
|
|
171
172
|
*/
|
|
172
173
|
exportRecords(format?: 'csv' | 'excel' | 'json'): Promise<boolean>;
|
|
174
|
+
/** The hosted grid's on-screen columns ({@link IViewRenderer.GetExportColumns}); empty before it mounts. */
|
|
175
|
+
GetExportColumns(): ExportColumn[];
|
|
173
176
|
/** Whether the add-to-list dialog is visible. Toggled internally by {@link onAddToListRequested}. */
|
|
174
177
|
protected showListManagementDialog: boolean;
|
|
175
178
|
/** Config fed into `<mj-list-management-dialog>`. Built from the entity + selected records. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid-view-renderer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/grid-view-renderer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAA2D,MAAM,eAAe,CAAC;AAChI,OAAO,EAAE,UAAU,EAAE,aAAa,EAA0B,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"grid-view-renderer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/grid-view-renderer.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -153,6 +153,10 @@ export class GridViewRendererComponent extends BaseAngularComponent {
|
|
|
153
153
|
const result = await this.grid.Export(format ? { format } : undefined, true);
|
|
154
154
|
return !!result?.success;
|
|
155
155
|
}
|
|
156
|
+
/** The hosted grid's on-screen columns ({@link IViewRenderer.GetExportColumns}); empty before it mounts. */
|
|
157
|
+
GetExportColumns() {
|
|
158
|
+
return this.grid?.GetExportColumns() ?? [];
|
|
159
|
+
}
|
|
156
160
|
// ================================================================
|
|
157
161
|
// Self-contained dialog state (never surfaced to the host)
|
|
158
162
|
// ================================================================
|
|
@@ -593,7 +597,7 @@ export class GridViewRendererComponent extends BaseAngularComponent {
|
|
|
593
597
|
type: ViewChild,
|
|
594
598
|
args: ['grid']
|
|
595
599
|
}] }); })();
|
|
596
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(GridViewRendererComponent, { className: "GridViewRendererComponent", filePath: "src/lib/view-types/renderers/grid-view-renderer.component.ts", lineNumber:
|
|
600
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(GridViewRendererComponent, { className: "GridViewRendererComponent", filePath: "src/lib/view-types/renderers/grid-view-renderer.component.ts", lineNumber: 187 }); })();
|
|
597
601
|
/**
|
|
598
602
|
* Tree-shaking guard. Force-references this renderer so bundlers (ESBuild/Vite) don't drop the
|
|
599
603
|
* component in builds that only mount it dynamically via the ClassFactory/descriptor. Mirrors the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grid-view-renderer.component.js","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/grid-view-renderer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ViewEncapsulation, ViewChild, ChangeDetectorRef, inject } from '@angular/core';\nimport { EntityInfo, RunViewParams, LogError, CompositeKey } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { PageChangeEvent } from '@memberjunction/ng-pagination';\nimport { ListManagementDialogConfig, ListManagementResult } from '@memberjunction/ng-list-management';\nimport { IViewRenderer, ViewDataRequest, ViewRelatedRecordNavigation } from '../view-type.contracts';\nimport { ViewGridState } from '../../types';\nimport { GridSelectionMode, GridToolbarConfig, ForeignKeyClickEvent } from '../../entity-data-grid/models/grid-types';\nimport { AfterRowClickEventArgs, AfterRowDoubleClickEventArgs, AfterSortEventArgs } from '../../entity-data-grid/events/grid-events';\nimport { GridStateChangedEvent } from '../../types';\nimport { buildPkString, buildCompositeKey } from '../../utils/record.util';\nimport { EntityDataGridComponent } from '../../entity-data-grid/entity-data-grid.component';\n\n/**\n * Opaque per-view configuration for the Grid view type.\n * -----------------------------------------------------\n * This is the blob the host persists verbatim against the active `ViewTypeID` and never\n * inspects (see {@link IViewRenderer.configChanged}). The renderer seeds the\n * `<mj-entity-data-grid>` bindings from these fields, applying sensible defaults when a\n * field is absent (see {@link GridViewRendererComponent}). It is intentionally a plain,\n * fully-serializable shape — every field is optional so an empty `{}` is a valid config.\n */\nexport interface GridViewConfig {\n /**\n * Persisted grid state (columns/widths/order/sort) from a User View. Mutated in place when\n * the grid emits sort or generic grid-state changes, then surfaced via `configChanged` so the\n * host persists it. Fed straight into `[GridState]`.\n */\n gridState?: ViewGridState;\n /** Whether the grid's own toolbar is shown. Defaults to `true` when absent. */\n showToolbar?: boolean;\n /** The grid toolbar configuration. Fed into `[ToolbarConfig]`. */\n toolbarConfig?: GridToolbarConfig;\n /** Row selection mode. Defaults to `'checkbox'` when absent. */\n selectionMode?: GridSelectionMode;\n /** Whether the \"Add to List\" button is shown. Defaults to `true` when absent. */\n showAddToListButton?: boolean;\n /** Whether the pager is shown. Defaults to `true` when absent. */\n showPager?: boolean;\n /** Page size for the grid's pager. Falls back to the `pageSize` input when absent. */\n pageSize?: number;\n /**\n * Explicit RunViewParams for the grid's data source. When absent the renderer builds a minimal\n * dynamic-view params object from {@link entity}. Fed into `[Params]`.\n */\n params?: RunViewParams;\n}\n\n/**\n * GridViewRendererComponent\n * -------------------------\n * The Grid **view type** renderer — a fully self-contained {@link IViewRenderer} adapter that hosts\n * the existing {@link EntityDataGridComponent} (`<mj-entity-data-grid>`) inside the entity-viewer's\n * pluggable view-type system. It is the dynamic-mounted plug-in that the `GridViewType` descriptor\n * points at.\n *\n * **Architectural intent — the container knows nothing about grids, and grid *features* never bubble\n * up.** The host (entity-viewer) binds only the generic {@link IViewRenderer} surface: the core\n * inputs (`entity` / `provider` / `records` / `selectedRecordId` / `filterText` / `config`), the\n * generic data-context inputs (`totalRecordCount` / `page` / `pageSize` / `isLoading`), and a small\n * set of generic outputs. There is **no opaque `hostAction` channel** — everything a grid does is\n * resolved through one of these three categories:\n *\n * 1. **Self-contained (owned end-to-end — never bubbles up):**\n * - **Export** → owned entirely by `<mj-entity-data-grid>` itself (its `onExportClick` opens its\n * OWN export dialog with format/sampling via `ExportService`). This wrapper does NOT host an\n * export dialog — doing so produced two stacked dialogs.\n * - **Add to List** → hosts `<mj-list-management-dialog>` ({@link ListManagementModule}). On\n * `(AddToListRequested)` the wrapper builds a {@link ListManagementDialogConfig} from the\n * entity + selected records and opens the dialog.\n * - **Delete** → hosts the Generic `<mj-ev-confirm-dialog>` (from {@link EntityViewerModule}).\n * On `(DeleteButtonClick)` the wrapper confirms, deletes via the MJ entity layer, then\n * re-requests data so the host reloads the current page.\n * - **Refresh** → on `(RefreshButtonClick)` the wrapper re-emits {@link dataRequest} so the host\n * reloads. No feature event leaves the wrapper.\n * - **Selection** → kept INTERNAL; it only drives the wrapper's own add-to-list. Never bubbles.\n *\n * 2. **Navigation (the ONLY legitimate upward signals — routing lives in the outer app):**\n * - Row double-click → {@link recordOpened}.\n * - Foreign-key cell click `(ForeignKeyClick)` → {@link openRelatedRecordRequested}.\n * - New button `(NewButtonClick)` → {@link createRecordRequested}.\n *\n * 3. **Container ↔ plug-in generic coordination (NOT outer-app signals):**\n * - Sort `(AfterSort)` → {@link dataRequest} (`sort`) + persisted into `config.gridState` via\n * {@link configChanged}.\n * - Pager `(PageChange)` → {@link dataRequest} (`page` / `pageSize`).\n * - Generic grid-state `(GridStateChanged)` → merged into `config.gridState` + {@link configChanged}.\n *\n * **Seeding the grid from `config` with defaults:** absent config fields fall back to sensible\n * defaults so a brand-new view (`config === {}`) renders a fully-functional grid — toolbar on,\n * checkbox selection, add-to-list on, pager on. The grid never loads its own data\n * (`[AllowLoad]=\"false\"`) — the host owns the fetch.\n *\n * This is an NgModule-declared (`standalone: false`) component, declared in `EntityViewerModule`.\n * It renders `<mj-entity-data-grid>` + the Generic `<mj-list-management-dialog>` / `<mj-ev-confirm-dialog>`\n * straight from the module's compilation scope (the module imports `ListManagementModule` and declares\n * the grid + confirm dialog; the grid component brings its own export dialog) — so there's\n * no `imports` array and, crucially, no self-import of `EntityViewerModule`: the module loads the\n * view-type descriptors, which reference these wrappers, so a wrapper importing the module back would\n * form a runtime import cycle (NG0919).\n *\n * Inputs use the camelCase names mandated by the {@link IViewRenderer} contract (the host binds them\n * by those exact names via `setInput`), rather than MJ's usual PascalCase for public members —\n * mirroring the Cards and Cluster renderers.\n */\n@Component({\n standalone: false,\n selector: 'mj-grid-view-renderer',\n encapsulation: ViewEncapsulation.None,\n template: `\n <mj-entity-data-grid\n #grid\n [Provider]=\"Provider\"\n [Data]=\"records\"\n [ExportDataProvider]=\"exportDataProvider\"\n [Params]=\"effectiveParams\"\n [FilterText]=\"filterText ?? ''\"\n [GridState]=\"config.gridState ?? null\"\n [Height]=\"'auto'\"\n [AllowLoad]=\"false\"\n [AutoLoadEntityActions]=\"AutoLoadEntityActions\"\n [ShowToolbar]=\"effectiveShowToolbar\"\n [ShowSearch]=\"effectiveShowSearch\"\n [ToolbarConfig]=\"config.toolbarConfig ?? {}\"\n [SelectionMode]=\"effectiveSelectionMode\"\n [ShowAddToListButton]=\"effectiveShowAddToListButton\"\n [ShowPager]=\"effectiveShowPager\"\n [PageSize]=\"effectivePageSize\"\n [TotalRowCount]=\"totalRecordCount ?? 0\"\n [PagerPageNumber]=\"page ?? 1\"\n (AfterRowClick)=\"onAfterRowClick($event)\"\n (AfterRowDoubleClick)=\"onAfterRowDoubleClick($event)\"\n (AfterSort)=\"onAfterSort($event)\"\n (GridStateChanged)=\"onGridStateChanged($event)\"\n (SelectionChange)=\"onSelectionChange($event)\"\n (NewButtonClick)=\"onNewButtonClick()\"\n (RefreshButtonClick)=\"onRefreshButtonClick()\"\n (DeleteButtonClick)=\"onDeleteButtonClick($event)\"\n (AddToListRequested)=\"onAddToListRequested($event)\"\n (ForeignKeyClick)=\"onForeignKeyClick($event)\"\n (PageChange)=\"onPageChange($event)\"\n (ManageColumnsRequested)=\"configureRequested.emit()\"\n >\n </mj-entity-data-grid>\n\n <!-- NOTE: Export is NOT handled here — mj-entity-data-grid hosts its OWN export dialog\n (its onExportClick opens it with formats/sampling via ExportService). Adding a second\n dialog here produced two stacked export dialogs. The grid owns export self-contained. -->\n\n <!-- Self-contained Add-to-List dialog (Generic) — owned by this wrapper, never bubbles up. -->\n @if (listManagementConfig) {\n <mj-list-management-dialog\n [Provider]=\"Provider\"\n [visible]=\"showListManagementDialog\"\n [config]=\"listManagementConfig\"\n (complete)=\"onListManagementComplete($event)\"\n (cancel)=\"onListManagementCancel()\"\n >\n </mj-list-management-dialog>\n }\n\n <!-- Self-contained Delete confirmation (Generic) — owned by this wrapper, never bubbles up. -->\n <mj-ev-confirm-dialog\n [IsOpen]=\"showDeleteConfirm\"\n Title=\"Delete Records\"\n [Message]=\"deleteConfirmMessage\"\n DetailMessage=\"This action cannot be undone.\"\n ConfirmText=\"Delete\"\n ConfirmStyle=\"danger\"\n Icon=\"fa-solid fa-trash\"\n (Confirmed)=\"onDeleteConfirmed()\"\n (Cancelled)=\"onDeleteCancelled()\"\n >\n </mj-ev-confirm-dialog>\n `,\n styles: [\n `\n :host {\n display: block;\n height: 100%;\n }\n `,\n ],\n})\nexport class GridViewRendererComponent extends BaseAngularComponent implements IViewRenderer<GridViewConfig> {\n /** Change detection ref used to flush dialog visibility toggles driven by grid events. */\n private cdr = inject(ChangeDetectorRef);\n\n // ---- IViewRenderer core inputs (camelCase per the host contract) ----\n\n /** The entity whose records are being rendered. Used to build default params + resolve selection. */\n @Input() entity: EntityInfo | null = null;\n\n /** The records to render (already loaded / filtered / paged by the host). Fed into `[Data]`. */\n @Input() records: Record<string, unknown>[] = [];\n\n /**\n * Optional full-result-set fetcher supplied by the host, forwarded to the grid's ExportDataProvider\n * so exports cover every matching row rather than just the loaded page (bug C1).\n */\n @Input() exportDataProvider: (() => Promise<Record<string, unknown>[]>) | null = null;\n\n /** Primary-key string of the currently selected record, if any. */\n @Input() selectedRecordId: string | null = null;\n\n /** Active filter text — passed through for the grid's cell highlighting. */\n @Input() filterText: string | null = null;\n\n /** Opaque per-view configuration. Seeds the grid bindings; mutated + re-emitted on grid changes. */\n @Input() config: GridViewConfig = {};\n\n /**\n * When true (default), the grid self-loads the entity's active EntityActions and shows them — buttons\n * appear only for entities that actually have actions (data-driven). Actions whose invocation names a\n * `RuntimeUXDriverClass` mount that interactive driver (e.g. the Record Process runner) in-place.\n */\n @Input() AutoLoadEntityActions = true;\n\n // ---- IViewRenderer generic data-context inputs ----\n\n /** Total record count across all pages, for the grid's pager. Fed into `[TotalRowCount]`. */\n @Input() totalRecordCount?: number;\n\n /** One-based current page the host is showing. Fed into `[PagerPageNumber]` (already 1-based). */\n @Input() page?: number;\n\n /** Page size the host is using — fallback when `config.pageSize` is absent. */\n @Input() pageSize?: number;\n\n /** Whether the host is currently (re)loading the record set. Accepted per contract; unused here. */\n @Input() isLoading?: boolean;\n\n // ---- IViewRenderer outputs (generic + navigation channels only) ----\n\n /** Emitted when a row is single-clicked — payload is the raw record object (host builds the key). */\n @Output() recordSelected = new EventEmitter<unknown>();\n\n /** Emitted when a row is double-clicked — payload is the raw record object (host builds the key). */\n @Output() recordOpened = new EventEmitter<unknown>();\n\n /** Emitted when this renderer mutates its opaque {@link config} (sort / grid-state persistence). */\n @Output() configChanged = new EventEmitter<GridViewConfig>();\n\n /** Generic data-access channel: ask the host to re-load with different sort / page. */\n @Output() dataRequest = new EventEmitter<ViewDataRequest>();\n\n /**\n * Ask the host to open this view's configuration UI. Raised when the grid emits\n * `ManageColumnsRequested` (its \"Manage Columns\" toolbar affordance). Part of the generic\n * {@link IViewRenderer} contract; the container forwards it to the workspace, which opens the\n * config panel (Columns tab) — the canonical column editor.\n */\n @Output() configureRequested = new EventEmitter<void>();\n\n /**\n * NAVIGATION: open a related record on a (possibly different) entity from a foreign-key cell click.\n * Routing lives in the outer app, so this is one of the few signals that legitimately bubbles up.\n */\n @Output() openRelatedRecordRequested = new EventEmitter<ViewRelatedRecordNavigation>();\n\n /** NAVIGATION: create a new record of the current entity (grid \"New\" button). Bubbles up for routing. */\n @Output() createRecordRequested = new EventEmitter<void>();\n\n /**\n * Reference to the hosted grid. Currently only retained for parity / future selection resolution;\n * selection is mapped from the {@link records} input rather than read from the grid, per contract.\n */\n @ViewChild('grid') protected grid?: EntityDataGridComponent;\n\n /**\n * IMPERATIVE export entry point ({@link IViewRenderer.exportRecords}). Lets the host trigger\n * an export programmatically (ultimately for the AI agent) by delegating to the hosted grid's\n * self-contained {@link EntityDataGridComponent.Export} (which downloads the file). The grid's\n * toolbar export button remains the interactive path; this is the no-UI equivalent.\n *\n * @param format optional output format; the grid defaults to 'excel' when omitted.\n * @returns true when the export succeeded, false when the grid isn't ready or the export failed.\n */\n async exportRecords(format?: 'csv' | 'excel' | 'json'): Promise<boolean> {\n if (!this.grid) {\n return false;\n }\n const result = await this.grid.Export(format ? { format } : undefined, true);\n return !!result?.success;\n }\n\n // ================================================================\n // Self-contained dialog state (never surfaced to the host)\n // ================================================================\n // NOTE: Export has no state here — mj-entity-data-grid owns its export dialog.\n\n /** Whether the add-to-list dialog is visible. Toggled internally by {@link onAddToListRequested}. */\n protected showListManagementDialog = false;\n\n /** Config fed into `<mj-list-management-dialog>`. Built from the entity + selected records. */\n protected listManagementConfig: ListManagementDialogConfig | null = null;\n\n /** Whether the delete-confirmation dialog is visible. Toggled internally by {@link onDeleteButtonClick}. */\n protected showDeleteConfirm = false;\n\n /** Records staged for deletion while the confirm dialog is open. */\n private pendingDeleteRecords: Record<string, unknown>[] = [];\n\n /** Dynamic message for the delete-confirmation dialog (reflects the staged record count). */\n protected deleteConfirmMessage = 'Are you sure you want to delete the selected records?';\n\n // ================================================================\n // Effective binding accessors — seed grid inputs from config + defaults\n // ================================================================\n\n /** Effective toolbar visibility — defaults to `true` when not set in config. */\n get effectiveShowToolbar(): boolean {\n return this.config.showToolbar ?? true;\n }\n\n /**\n * Grid search defaults OFF when hosted in entity-viewer. The container already owns\n * `filterText` (\"Filter records...\"). Opt in with `toolbarConfig.showSearch: true`.\n */\n get effectiveShowSearch(): boolean {\n return this.config.toolbarConfig?.showSearch ?? false;\n }\n\n /** Effective selection mode — defaults to `'checkbox'` when not set in config. */\n get effectiveSelectionMode(): GridSelectionMode {\n return this.config.selectionMode ?? 'checkbox';\n }\n\n /** Effective add-to-list button visibility — defaults to `true` when not set in config. */\n get effectiveShowAddToListButton(): boolean {\n return this.config.showAddToListButton ?? true;\n }\n\n /** Effective pager visibility — defaults to `true` when not set in config. */\n get effectiveShowPager(): boolean {\n return this.config.showPager ?? true;\n }\n\n /** Effective page size — config wins, else the generic `pageSize` input, else the grid's own default. */\n get effectivePageSize(): number {\n return this.config.pageSize ?? this.pageSize ?? 100;\n }\n\n /**\n * Effective RunViewParams for the grid. Uses `config.params` when supplied; otherwise builds a\n * minimal dynamic-view params object from {@link entity}. Returns `null` when no entity is known\n * yet (the grid renders empty until the host provides one).\n */\n get effectiveParams(): RunViewParams | null {\n if (this.config.params) {\n return this.config.params;\n }\n if (this.entity) {\n return { EntityName: this.entity.Name };\n }\n return null;\n }\n\n // ================================================================\n // Navigation + generic coordination output mapping\n // ================================================================\n\n /**\n * Row single-click → {@link recordSelected}. Extracts the raw record (`event.row`) so the host\n * builds the composite key itself, matching the other renderers.\n */\n onAfterRowClick(event: AfterRowClickEventArgs): void {\n if (event.row) {\n this.recordSelected.emit(event.row);\n }\n }\n\n /** Row double-click → {@link recordOpened} (NAVIGATION), emitting the raw record object. */\n onAfterRowDoubleClick(event: AfterRowDoubleClickEventArgs): void {\n if (event.row) {\n this.recordOpened.emit(event.row);\n }\n }\n\n /**\n * Sort change → both generic coordination channels:\n * 1. {@link dataRequest} with the generic `sort` shape so the host re-loads with a new OrderBy.\n * 2. persists the sort into `config.gridState.sortSettings` and emits {@link configChanged} so\n * the grid's sort survives view reloads via the opaque config channel.\n */\n onAfterSort(event: AfterSortEventArgs): void {\n const sort = (event.newSortState ?? []).map((s) => ({ field: s.field, direction: s.direction }));\n this.dataRequest.emit({ sort });\n\n const gridState: ViewGridState = { ...(this.config.gridState ?? {}) };\n gridState.sortSettings = (event.newSortState ?? []).map((s) => ({ field: s.field, dir: s.direction }));\n this.config = { ...this.config, gridState };\n this.configChanged.emit(this.config);\n }\n\n /**\n * Generic grid-state change (column resize / reorder / visibility) → merge the grid's updated\n * {@link ViewGridState} into the opaque config and emit {@link configChanged}; the host persists\n * the blob verbatim. No `dataRequest` here — column changes don't alter the loaded record set.\n */\n onGridStateChanged(event: GridStateChangedEvent): void {\n this.config = { ...this.config, gridState: event.gridState };\n this.configChanged.emit(this.config);\n }\n\n /**\n * Selection change → kept INTERNAL. The grid drives its own add-to-list from the selection, so the\n * wrapper has no need to surface it. We intentionally do not bubble selection anywhere.\n */\n onSelectionChange(_selectedKeys: string[]): void {\n // No-op: selection is internal to the grid + this wrapper's add-to-list. It never bubbles up.\n }\n\n /**\n * Foreign-key link click → {@link openRelatedRecordRequested} (NAVIGATION). Maps the grid's\n * {@link ForeignKeyClickEvent} (which carries the related entity + the FK value) onto the generic\n * {@link ViewRelatedRecordNavigation} the container forwards to the outer app for routing. The\n * related entity name is taken from the event when present, else resolved from the metadata by ID.\n */\n onForeignKeyClick(event: ForeignKeyClickEvent): void {\n const entityName = event.relatedEntityName ?? this.resolveEntityNameById(event.relatedEntityId);\n if (!entityName) {\n return;\n }\n this.openRelatedRecordRequested.emit({ entityName, recordKey: event.recordId });\n }\n\n /** New button → {@link createRecordRequested} (NAVIGATION) — opening the create form is routing. */\n onNewButtonClick(): void {\n this.createRecordRequested.emit();\n }\n\n /**\n * Pager navigation → generic {@link dataRequest}. Maps the grid's {@link PageChangeEvent}\n * (`PageNumber` is already 1-based, `PageSize`) onto the generic `{ page, pageSize }` shape the\n * host honors against its own RunView.\n */\n onPageChange(event: PageChangeEvent): void {\n this.dataRequest.emit({ page: event.PageNumber, pageSize: event.PageSize });\n }\n\n // ================================================================\n // Self-contained: Refresh\n // ================================================================\n\n /**\n * Refresh button → re-emit {@link dataRequest} so the host reloads the current page. This is\n * generic container ↔ plug-in coordination (a data-access request), NOT a feature event surfaced\n * to the outer app.\n */\n onRefreshButtonClick(): void {\n this.dataRequest.emit(this.currentPageDataRequest());\n }\n\n // ================================================================\n // Export: handled entirely by mj-entity-data-grid (its onExportClick opens its own export dialog\n // via ExportService). This wrapper intentionally does NOT host an export dialog — doing so produced\n // two stacked dialogs. Nothing to do here.\n // ================================================================\n\n // ================================================================\n // Self-contained: Add to List (hosts <mj-list-management-dialog>)\n // ================================================================\n\n /**\n * Add-to-list button → build a {@link ListManagementDialogConfig} from the entity + the records the\n * grid supplied, then open the Generic list-management dialog. The dialog persists membership\n * changes itself — nothing bubbles up. Mirrors the config construction the legacy host did in\n * `data-explorer-dashboard`'s `onAddToListRequested()`.\n */\n onAddToListRequested(event: { entityInfo: EntityInfo; records: Record<string, unknown>[]; recordIds: string[] }): void {\n const entity = event.entityInfo ?? this.entity;\n if (!entity || !event.records || event.records.length === 0) {\n return;\n }\n\n const recordDisplayNames = event.records.map((record) => this.getRecordDisplayName(record, entity));\n const recordIds = this.buildRawRecordIds(event.records, entity);\n if (recordIds.length === 0) {\n return;\n }\n\n const recordCount = event.records.length;\n const dialogTitle = recordCount === 1 ? `Manage Lists for \"${recordDisplayNames[0]}\"` : `Add ${recordCount} Records to List`;\n\n this.listManagementConfig = {\n mode: 'manage',\n entityId: entity.ID,\n entityName: entity.Name,\n recordIds,\n recordDisplayNames,\n allowCreate: true,\n allowRemove: recordCount === 1,\n showMembership: recordCount === 1,\n dialogTitle,\n };\n this.showListManagementDialog = true;\n this.cdr.detectChanges();\n }\n\n /** List-management dialog completed (membership changes applied) → tear down state. */\n onListManagementComplete(_result: ListManagementResult): void {\n this.showListManagementDialog = false;\n this.listManagementConfig = null;\n this.cdr.detectChanges();\n }\n\n /** List-management dialog cancelled → tear down state. */\n onListManagementCancel(): void {\n this.showListManagementDialog = false;\n this.listManagementConfig = null;\n this.cdr.detectChanges();\n }\n\n /**\n * Resolve a human-friendly display name for a record using the entity's NameField, falling back to\n * the composite-key string. Mirrors the legacy host's display-name resolution.\n */\n private getRecordDisplayName(record: Record<string, unknown>, entity: EntityInfo): string {\n if (entity.NameField) {\n const nameValue = record[entity.NameField.Name];\n if (nameValue != null) {\n return String(nameValue);\n }\n }\n return buildPkString(record, entity) || 'Unknown';\n }\n\n /**\n * Build the record-id strings used by list membership matching, in the compact form `MJ: List Details`\n * stores in `RecordID`: the bare value for a single-column primary key (whatever the column is called),\n * or the full `Field1|Value1||Field2|Value2` segment for a composite key. The entity is arbitrary, so the\n * key is built from all of its primary-key columns rather than assuming a single `ID`.\n */\n private buildRawRecordIds(records: Record<string, unknown>[], entity: EntityInfo): string[] {\n if (entity.PrimaryKeys.length === 0) {\n return [];\n }\n return records\n .map((record) => CompositeKey.FromEntityRecord(entity, record).ToCompactURLSegment())\n .filter((id) => id !== '');\n }\n\n // ================================================================\n // Self-contained: Delete (hosts <mj-ev-confirm-dialog> + MJ entity layer)\n // ================================================================\n\n /**\n * Delete button → stage the records and open the Generic confirm dialog. The actual delete happens\n * in {@link onDeleteConfirmed} after the user confirms — nothing bubbles up.\n */\n onDeleteButtonClick(records: Record<string, unknown>[]): void {\n if (!records || records.length === 0) {\n return;\n }\n this.pendingDeleteRecords = records;\n const count = records.length;\n this.deleteConfirmMessage = count === 1 ? 'Are you sure you want to delete this record?' : `Are you sure you want to delete these ${count} records?`;\n this.showDeleteConfirm = true;\n this.cdr.detectChanges();\n }\n\n /**\n * Delete confirmed → delete each staged record through the MJ entity layer\n * (`ProviderToUse.GetEntityObject(name, compositeKey, user)` → `Delete`), checking the boolean\n * result and surfacing failures via {@link LogError}. After deletion, re-request the current page\n * so the host reloads. Self-contained — no feature event leaves the wrapper.\n */\n async onDeleteConfirmed(): Promise<void> {\n this.showDeleteConfirm = false;\n const entity = this.entity;\n const records = this.pendingDeleteRecords;\n this.pendingDeleteRecords = [];\n\n if (!entity || records.length === 0) {\n return;\n }\n\n let anyDeleted = false;\n const provider = this.ProviderToUse;\n const user = provider.CurrentUser;\n\n for (const record of records) {\n const key = buildCompositeKey(record, entity);\n try {\n // The (name, key, user) overload instantiates AND loads in one call (throws if it can't load).\n const obj = await provider.GetEntityObject(entity.Name, key, user);\n const deleted = await obj.Delete();\n if (deleted) {\n anyDeleted = true;\n } else {\n LogError(`Delete failed: ${obj.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n } catch (err) {\n LogError(`Delete failed for ${entity.Name} (${key.ToString()}): ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n if (anyDeleted) {\n // Re-request the current page so the host reloads the (now smaller) record set.\n this.dataRequest.emit(this.currentPageDataRequest());\n }\n this.cdr.detectChanges();\n }\n\n /** Delete cancelled → discard the staged records and close the confirm dialog. */\n onDeleteCancelled(): void {\n this.showDeleteConfirm = false;\n this.pendingDeleteRecords = [];\n this.cdr.detectChanges();\n }\n\n // ================================================================\n // Helpers\n // ================================================================\n\n /** Build a {@link ViewDataRequest} that re-requests the host's current page/pageSize, if known. */\n private currentPageDataRequest(): ViewDataRequest {\n const req: ViewDataRequest = {};\n if (this.page != null) {\n req.page = this.page;\n }\n const ps = this.config.pageSize ?? this.pageSize;\n if (ps != null) {\n req.pageSize = ps;\n }\n return req;\n }\n\n /**\n * Resolve an entity's name from its ID via the active provider's metadata. Used when a foreign-key\n * click event omits the related entity name and only carries its ID.\n */\n private resolveEntityNameById(entityId: string): string | null {\n if (!entityId) {\n return null;\n }\n const match = this.ProviderToUse.Entities.find((e) => UUIDsEqual(e.ID, entityId));\n return match ? match.Name : null;\n }\n}\n\n/**\n * Tree-shaking guard. Force-references this renderer so bundlers (ESBuild/Vite) don't drop the\n * component in builds that only mount it dynamically via the ClassFactory/descriptor. Mirrors the\n * Cards/Cluster renderers' load guards; the parent wires this into a barrel/module load path.\n */\nexport function LoadGridViewRenderer(): void {\n // no-op; presence prevents tree-shaking of this component\n}\n"]}
|
|
1
|
+
{"version":3,"file":"grid-view-renderer.component.js","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/grid-view-renderer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, ViewEncapsulation, ViewChild, ChangeDetectorRef, inject } from '@angular/core';\nimport { EntityInfo, RunViewParams, LogError, CompositeKey } from '@memberjunction/core';\nimport { ExportColumn } from '@memberjunction/export-engine';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { PageChangeEvent } from '@memberjunction/ng-pagination';\nimport { ListManagementDialogConfig, ListManagementResult } from '@memberjunction/ng-list-management';\nimport { IViewRenderer, ViewDataRequest, ViewRelatedRecordNavigation } from '../view-type.contracts';\nimport { ViewGridState } from '../../types';\nimport { GridSelectionMode, GridToolbarConfig, ForeignKeyClickEvent } from '../../entity-data-grid/models/grid-types';\nimport { AfterRowClickEventArgs, AfterRowDoubleClickEventArgs, AfterSortEventArgs } from '../../entity-data-grid/events/grid-events';\nimport { GridStateChangedEvent } from '../../types';\nimport { buildPkString, buildCompositeKey } from '../../utils/record.util';\nimport { EntityDataGridComponent } from '../../entity-data-grid/entity-data-grid.component';\n\n/**\n * Opaque per-view configuration for the Grid view type.\n * -----------------------------------------------------\n * This is the blob the host persists verbatim against the active `ViewTypeID` and never\n * inspects (see {@link IViewRenderer.configChanged}). The renderer seeds the\n * `<mj-entity-data-grid>` bindings from these fields, applying sensible defaults when a\n * field is absent (see {@link GridViewRendererComponent}). It is intentionally a plain,\n * fully-serializable shape — every field is optional so an empty `{}` is a valid config.\n */\nexport interface GridViewConfig {\n /**\n * Persisted grid state (columns/widths/order/sort) from a User View. Mutated in place when\n * the grid emits sort or generic grid-state changes, then surfaced via `configChanged` so the\n * host persists it. Fed straight into `[GridState]`.\n */\n gridState?: ViewGridState;\n /** Whether the grid's own toolbar is shown. Defaults to `true` when absent. */\n showToolbar?: boolean;\n /** The grid toolbar configuration. Fed into `[ToolbarConfig]`. */\n toolbarConfig?: GridToolbarConfig;\n /** Row selection mode. Defaults to `'checkbox'` when absent. */\n selectionMode?: GridSelectionMode;\n /** Whether the \"Add to List\" button is shown. Defaults to `true` when absent. */\n showAddToListButton?: boolean;\n /** Whether the pager is shown. Defaults to `true` when absent. */\n showPager?: boolean;\n /** Page size for the grid's pager. Falls back to the `pageSize` input when absent. */\n pageSize?: number;\n /**\n * Explicit RunViewParams for the grid's data source. When absent the renderer builds a minimal\n * dynamic-view params object from {@link entity}. Fed into `[Params]`.\n */\n params?: RunViewParams;\n}\n\n/**\n * GridViewRendererComponent\n * -------------------------\n * The Grid **view type** renderer — a fully self-contained {@link IViewRenderer} adapter that hosts\n * the existing {@link EntityDataGridComponent} (`<mj-entity-data-grid>`) inside the entity-viewer's\n * pluggable view-type system. It is the dynamic-mounted plug-in that the `GridViewType` descriptor\n * points at.\n *\n * **Architectural intent — the container knows nothing about grids, and grid *features* never bubble\n * up.** The host (entity-viewer) binds only the generic {@link IViewRenderer} surface: the core\n * inputs (`entity` / `provider` / `records` / `selectedRecordId` / `filterText` / `config`), the\n * generic data-context inputs (`totalRecordCount` / `page` / `pageSize` / `isLoading`), and a small\n * set of generic outputs. There is **no opaque `hostAction` channel** — everything a grid does is\n * resolved through one of these three categories:\n *\n * 1. **Self-contained (owned end-to-end — never bubbles up):**\n * - **Export** → owned entirely by `<mj-entity-data-grid>` itself (its `onExportClick` opens its\n * OWN export dialog with format/sampling via `ExportService`). This wrapper does NOT host an\n * export dialog — doing so produced two stacked dialogs.\n * - **Add to List** → hosts `<mj-list-management-dialog>` ({@link ListManagementModule}). On\n * `(AddToListRequested)` the wrapper builds a {@link ListManagementDialogConfig} from the\n * entity + selected records and opens the dialog.\n * - **Delete** → hosts the Generic `<mj-ev-confirm-dialog>` (from {@link EntityViewerModule}).\n * On `(DeleteButtonClick)` the wrapper confirms, deletes via the MJ entity layer, then\n * re-requests data so the host reloads the current page.\n * - **Refresh** → on `(RefreshButtonClick)` the wrapper re-emits {@link dataRequest} so the host\n * reloads. No feature event leaves the wrapper.\n * - **Selection** → kept INTERNAL; it only drives the wrapper's own add-to-list. Never bubbles.\n *\n * 2. **Navigation (the ONLY legitimate upward signals — routing lives in the outer app):**\n * - Row double-click → {@link recordOpened}.\n * - Foreign-key cell click `(ForeignKeyClick)` → {@link openRelatedRecordRequested}.\n * - New button `(NewButtonClick)` → {@link createRecordRequested}.\n *\n * 3. **Container ↔ plug-in generic coordination (NOT outer-app signals):**\n * - Sort `(AfterSort)` → {@link dataRequest} (`sort`) + persisted into `config.gridState` via\n * {@link configChanged}.\n * - Pager `(PageChange)` → {@link dataRequest} (`page` / `pageSize`).\n * - Generic grid-state `(GridStateChanged)` → merged into `config.gridState` + {@link configChanged}.\n *\n * **Seeding the grid from `config` with defaults:** absent config fields fall back to sensible\n * defaults so a brand-new view (`config === {}`) renders a fully-functional grid — toolbar on,\n * checkbox selection, add-to-list on, pager on. The grid never loads its own data\n * (`[AllowLoad]=\"false\"`) — the host owns the fetch.\n *\n * This is an NgModule-declared (`standalone: false`) component, declared in `EntityViewerModule`.\n * It renders `<mj-entity-data-grid>` + the Generic `<mj-list-management-dialog>` / `<mj-ev-confirm-dialog>`\n * straight from the module's compilation scope (the module imports `ListManagementModule` and declares\n * the grid + confirm dialog; the grid component brings its own export dialog) — so there's\n * no `imports` array and, crucially, no self-import of `EntityViewerModule`: the module loads the\n * view-type descriptors, which reference these wrappers, so a wrapper importing the module back would\n * form a runtime import cycle (NG0919).\n *\n * Inputs use the camelCase names mandated by the {@link IViewRenderer} contract (the host binds them\n * by those exact names via `setInput`), rather than MJ's usual PascalCase for public members —\n * mirroring the Cards and Cluster renderers.\n */\n@Component({\n standalone: false,\n selector: 'mj-grid-view-renderer',\n encapsulation: ViewEncapsulation.None,\n template: `\n <mj-entity-data-grid\n #grid\n [Provider]=\"Provider\"\n [Data]=\"records\"\n [ExportDataProvider]=\"exportDataProvider\"\n [Params]=\"effectiveParams\"\n [FilterText]=\"filterText ?? ''\"\n [GridState]=\"config.gridState ?? null\"\n [Height]=\"'auto'\"\n [AllowLoad]=\"false\"\n [AutoLoadEntityActions]=\"AutoLoadEntityActions\"\n [ShowToolbar]=\"effectiveShowToolbar\"\n [ShowSearch]=\"effectiveShowSearch\"\n [ToolbarConfig]=\"config.toolbarConfig ?? {}\"\n [SelectionMode]=\"effectiveSelectionMode\"\n [ShowAddToListButton]=\"effectiveShowAddToListButton\"\n [ShowPager]=\"effectiveShowPager\"\n [PageSize]=\"effectivePageSize\"\n [TotalRowCount]=\"totalRecordCount ?? 0\"\n [PagerPageNumber]=\"page ?? 1\"\n (AfterRowClick)=\"onAfterRowClick($event)\"\n (AfterRowDoubleClick)=\"onAfterRowDoubleClick($event)\"\n (AfterSort)=\"onAfterSort($event)\"\n (GridStateChanged)=\"onGridStateChanged($event)\"\n (SelectionChange)=\"onSelectionChange($event)\"\n (NewButtonClick)=\"onNewButtonClick()\"\n (RefreshButtonClick)=\"onRefreshButtonClick()\"\n (DeleteButtonClick)=\"onDeleteButtonClick($event)\"\n (AddToListRequested)=\"onAddToListRequested($event)\"\n (ForeignKeyClick)=\"onForeignKeyClick($event)\"\n (PageChange)=\"onPageChange($event)\"\n (ManageColumnsRequested)=\"configureRequested.emit()\"\n >\n </mj-entity-data-grid>\n\n <!-- NOTE: Export is NOT handled here — mj-entity-data-grid hosts its OWN export dialog\n (its onExportClick opens it with formats/sampling via ExportService). Adding a second\n dialog here produced two stacked export dialogs. The grid owns export self-contained. -->\n\n <!-- Self-contained Add-to-List dialog (Generic) — owned by this wrapper, never bubbles up. -->\n @if (listManagementConfig) {\n <mj-list-management-dialog\n [Provider]=\"Provider\"\n [visible]=\"showListManagementDialog\"\n [config]=\"listManagementConfig\"\n (complete)=\"onListManagementComplete($event)\"\n (cancel)=\"onListManagementCancel()\"\n >\n </mj-list-management-dialog>\n }\n\n <!-- Self-contained Delete confirmation (Generic) — owned by this wrapper, never bubbles up. -->\n <mj-ev-confirm-dialog\n [IsOpen]=\"showDeleteConfirm\"\n Title=\"Delete Records\"\n [Message]=\"deleteConfirmMessage\"\n DetailMessage=\"This action cannot be undone.\"\n ConfirmText=\"Delete\"\n ConfirmStyle=\"danger\"\n Icon=\"fa-solid fa-trash\"\n (Confirmed)=\"onDeleteConfirmed()\"\n (Cancelled)=\"onDeleteCancelled()\"\n >\n </mj-ev-confirm-dialog>\n `,\n styles: [\n `\n :host {\n display: block;\n height: 100%;\n }\n `,\n ],\n})\nexport class GridViewRendererComponent extends BaseAngularComponent implements IViewRenderer<GridViewConfig> {\n /** Change detection ref used to flush dialog visibility toggles driven by grid events. */\n private cdr = inject(ChangeDetectorRef);\n\n // ---- IViewRenderer core inputs (camelCase per the host contract) ----\n\n /** The entity whose records are being rendered. Used to build default params + resolve selection. */\n @Input() entity: EntityInfo | null = null;\n\n /** The records to render (already loaded / filtered / paged by the host). Fed into `[Data]`. */\n @Input() records: Record<string, unknown>[] = [];\n\n /**\n * Optional full-result-set fetcher supplied by the host, forwarded to the grid's ExportDataProvider\n * so exports cover every matching row rather than just the loaded page (bug C1).\n */\n @Input() exportDataProvider: (() => Promise<Record<string, unknown>[]>) | null = null;\n\n /** Primary-key string of the currently selected record, if any. */\n @Input() selectedRecordId: string | null = null;\n\n /** Active filter text — passed through for the grid's cell highlighting. */\n @Input() filterText: string | null = null;\n\n /** Opaque per-view configuration. Seeds the grid bindings; mutated + re-emitted on grid changes. */\n @Input() config: GridViewConfig = {};\n\n /**\n * When true (default), the grid self-loads the entity's active EntityActions and shows them — buttons\n * appear only for entities that actually have actions (data-driven). Actions whose invocation names a\n * `RuntimeUXDriverClass` mount that interactive driver (e.g. the Record Process runner) in-place.\n */\n @Input() AutoLoadEntityActions = true;\n\n // ---- IViewRenderer generic data-context inputs ----\n\n /** Total record count across all pages, for the grid's pager. Fed into `[TotalRowCount]`. */\n @Input() totalRecordCount?: number;\n\n /** One-based current page the host is showing. Fed into `[PagerPageNumber]` (already 1-based). */\n @Input() page?: number;\n\n /** Page size the host is using — fallback when `config.pageSize` is absent. */\n @Input() pageSize?: number;\n\n /** Whether the host is currently (re)loading the record set. Accepted per contract; unused here. */\n @Input() isLoading?: boolean;\n\n // ---- IViewRenderer outputs (generic + navigation channels only) ----\n\n /** Emitted when a row is single-clicked — payload is the raw record object (host builds the key). */\n @Output() recordSelected = new EventEmitter<unknown>();\n\n /** Emitted when a row is double-clicked — payload is the raw record object (host builds the key). */\n @Output() recordOpened = new EventEmitter<unknown>();\n\n /** Emitted when this renderer mutates its opaque {@link config} (sort / grid-state persistence). */\n @Output() configChanged = new EventEmitter<GridViewConfig>();\n\n /** Generic data-access channel: ask the host to re-load with different sort / page. */\n @Output() dataRequest = new EventEmitter<ViewDataRequest>();\n\n /**\n * Ask the host to open this view's configuration UI. Raised when the grid emits\n * `ManageColumnsRequested` (its \"Manage Columns\" toolbar affordance). Part of the generic\n * {@link IViewRenderer} contract; the container forwards it to the workspace, which opens the\n * config panel (Columns tab) — the canonical column editor.\n */\n @Output() configureRequested = new EventEmitter<void>();\n\n /**\n * NAVIGATION: open a related record on a (possibly different) entity from a foreign-key cell click.\n * Routing lives in the outer app, so this is one of the few signals that legitimately bubbles up.\n */\n @Output() openRelatedRecordRequested = new EventEmitter<ViewRelatedRecordNavigation>();\n\n /** NAVIGATION: create a new record of the current entity (grid \"New\" button). Bubbles up for routing. */\n @Output() createRecordRequested = new EventEmitter<void>();\n\n /**\n * Reference to the hosted grid. Currently only retained for parity / future selection resolution;\n * selection is mapped from the {@link records} input rather than read from the grid, per contract.\n */\n @ViewChild('grid') protected grid?: EntityDataGridComponent;\n\n /**\n * IMPERATIVE export entry point ({@link IViewRenderer.exportRecords}). Lets the host trigger\n * an export programmatically (ultimately for the AI agent) by delegating to the hosted grid's\n * self-contained {@link EntityDataGridComponent.Export} (which downloads the file). The grid's\n * toolbar export button remains the interactive path; this is the no-UI equivalent.\n *\n * @param format optional output format; the grid defaults to 'excel' when omitted.\n * @returns true when the export succeeded, false when the grid isn't ready or the export failed.\n */\n async exportRecords(format?: 'csv' | 'excel' | 'json'): Promise<boolean> {\n if (!this.grid) {\n return false;\n }\n const result = await this.grid.Export(format ? { format } : undefined, true);\n return !!result?.success;\n }\n\n /** The hosted grid's on-screen columns ({@link IViewRenderer.GetExportColumns}); empty before it mounts. */\n GetExportColumns(): ExportColumn[] {\n return this.grid?.GetExportColumns() ?? [];\n }\n\n // ================================================================\n // Self-contained dialog state (never surfaced to the host)\n // ================================================================\n // NOTE: Export has no state here — mj-entity-data-grid owns its export dialog.\n\n /** Whether the add-to-list dialog is visible. Toggled internally by {@link onAddToListRequested}. */\n protected showListManagementDialog = false;\n\n /** Config fed into `<mj-list-management-dialog>`. Built from the entity + selected records. */\n protected listManagementConfig: ListManagementDialogConfig | null = null;\n\n /** Whether the delete-confirmation dialog is visible. Toggled internally by {@link onDeleteButtonClick}. */\n protected showDeleteConfirm = false;\n\n /** Records staged for deletion while the confirm dialog is open. */\n private pendingDeleteRecords: Record<string, unknown>[] = [];\n\n /** Dynamic message for the delete-confirmation dialog (reflects the staged record count). */\n protected deleteConfirmMessage = 'Are you sure you want to delete the selected records?';\n\n // ================================================================\n // Effective binding accessors — seed grid inputs from config + defaults\n // ================================================================\n\n /** Effective toolbar visibility — defaults to `true` when not set in config. */\n get effectiveShowToolbar(): boolean {\n return this.config.showToolbar ?? true;\n }\n\n /**\n * Grid search defaults OFF when hosted in entity-viewer. The container already owns\n * `filterText` (\"Filter records...\"). Opt in with `toolbarConfig.showSearch: true`.\n */\n get effectiveShowSearch(): boolean {\n return this.config.toolbarConfig?.showSearch ?? false;\n }\n\n /** Effective selection mode — defaults to `'checkbox'` when not set in config. */\n get effectiveSelectionMode(): GridSelectionMode {\n return this.config.selectionMode ?? 'checkbox';\n }\n\n /** Effective add-to-list button visibility — defaults to `true` when not set in config. */\n get effectiveShowAddToListButton(): boolean {\n return this.config.showAddToListButton ?? true;\n }\n\n /** Effective pager visibility — defaults to `true` when not set in config. */\n get effectiveShowPager(): boolean {\n return this.config.showPager ?? true;\n }\n\n /** Effective page size — config wins, else the generic `pageSize` input, else the grid's own default. */\n get effectivePageSize(): number {\n return this.config.pageSize ?? this.pageSize ?? 100;\n }\n\n /**\n * Effective RunViewParams for the grid. Uses `config.params` when supplied; otherwise builds a\n * minimal dynamic-view params object from {@link entity}. Returns `null` when no entity is known\n * yet (the grid renders empty until the host provides one).\n */\n get effectiveParams(): RunViewParams | null {\n if (this.config.params) {\n return this.config.params;\n }\n if (this.entity) {\n return { EntityName: this.entity.Name };\n }\n return null;\n }\n\n // ================================================================\n // Navigation + generic coordination output mapping\n // ================================================================\n\n /**\n * Row single-click → {@link recordSelected}. Extracts the raw record (`event.row`) so the host\n * builds the composite key itself, matching the other renderers.\n */\n onAfterRowClick(event: AfterRowClickEventArgs): void {\n if (event.row) {\n this.recordSelected.emit(event.row);\n }\n }\n\n /** Row double-click → {@link recordOpened} (NAVIGATION), emitting the raw record object. */\n onAfterRowDoubleClick(event: AfterRowDoubleClickEventArgs): void {\n if (event.row) {\n this.recordOpened.emit(event.row);\n }\n }\n\n /**\n * Sort change → both generic coordination channels:\n * 1. {@link dataRequest} with the generic `sort` shape so the host re-loads with a new OrderBy.\n * 2. persists the sort into `config.gridState.sortSettings` and emits {@link configChanged} so\n * the grid's sort survives view reloads via the opaque config channel.\n */\n onAfterSort(event: AfterSortEventArgs): void {\n const sort = (event.newSortState ?? []).map((s) => ({ field: s.field, direction: s.direction }));\n this.dataRequest.emit({ sort });\n\n const gridState: ViewGridState = { ...(this.config.gridState ?? {}) };\n gridState.sortSettings = (event.newSortState ?? []).map((s) => ({ field: s.field, dir: s.direction }));\n this.config = { ...this.config, gridState };\n this.configChanged.emit(this.config);\n }\n\n /**\n * Generic grid-state change (column resize / reorder / visibility) → merge the grid's updated\n * {@link ViewGridState} into the opaque config and emit {@link configChanged}; the host persists\n * the blob verbatim. No `dataRequest` here — column changes don't alter the loaded record set.\n */\n onGridStateChanged(event: GridStateChangedEvent): void {\n this.config = { ...this.config, gridState: event.gridState };\n this.configChanged.emit(this.config);\n }\n\n /**\n * Selection change → kept INTERNAL. The grid drives its own add-to-list from the selection, so the\n * wrapper has no need to surface it. We intentionally do not bubble selection anywhere.\n */\n onSelectionChange(_selectedKeys: string[]): void {\n // No-op: selection is internal to the grid + this wrapper's add-to-list. It never bubbles up.\n }\n\n /**\n * Foreign-key link click → {@link openRelatedRecordRequested} (NAVIGATION). Maps the grid's\n * {@link ForeignKeyClickEvent} (which carries the related entity + the FK value) onto the generic\n * {@link ViewRelatedRecordNavigation} the container forwards to the outer app for routing. The\n * related entity name is taken from the event when present, else resolved from the metadata by ID.\n */\n onForeignKeyClick(event: ForeignKeyClickEvent): void {\n const entityName = event.relatedEntityName ?? this.resolveEntityNameById(event.relatedEntityId);\n if (!entityName) {\n return;\n }\n this.openRelatedRecordRequested.emit({ entityName, recordKey: event.recordId });\n }\n\n /** New button → {@link createRecordRequested} (NAVIGATION) — opening the create form is routing. */\n onNewButtonClick(): void {\n this.createRecordRequested.emit();\n }\n\n /**\n * Pager navigation → generic {@link dataRequest}. Maps the grid's {@link PageChangeEvent}\n * (`PageNumber` is already 1-based, `PageSize`) onto the generic `{ page, pageSize }` shape the\n * host honors against its own RunView.\n */\n onPageChange(event: PageChangeEvent): void {\n this.dataRequest.emit({ page: event.PageNumber, pageSize: event.PageSize });\n }\n\n // ================================================================\n // Self-contained: Refresh\n // ================================================================\n\n /**\n * Refresh button → re-emit {@link dataRequest} so the host reloads the current page. This is\n * generic container ↔ plug-in coordination (a data-access request), NOT a feature event surfaced\n * to the outer app.\n */\n onRefreshButtonClick(): void {\n this.dataRequest.emit(this.currentPageDataRequest());\n }\n\n // ================================================================\n // Export: handled entirely by mj-entity-data-grid (its onExportClick opens its own export dialog\n // via ExportService). This wrapper intentionally does NOT host an export dialog — doing so produced\n // two stacked dialogs. Nothing to do here.\n // ================================================================\n\n // ================================================================\n // Self-contained: Add to List (hosts <mj-list-management-dialog>)\n // ================================================================\n\n /**\n * Add-to-list button → build a {@link ListManagementDialogConfig} from the entity + the records the\n * grid supplied, then open the Generic list-management dialog. The dialog persists membership\n * changes itself — nothing bubbles up. Mirrors the config construction the legacy host did in\n * `data-explorer-dashboard`'s `onAddToListRequested()`.\n */\n onAddToListRequested(event: { entityInfo: EntityInfo; records: Record<string, unknown>[]; recordIds: string[] }): void {\n const entity = event.entityInfo ?? this.entity;\n if (!entity || !event.records || event.records.length === 0) {\n return;\n }\n\n const recordDisplayNames = event.records.map((record) => this.getRecordDisplayName(record, entity));\n const recordIds = this.buildRawRecordIds(event.records, entity);\n if (recordIds.length === 0) {\n return;\n }\n\n const recordCount = event.records.length;\n const dialogTitle = recordCount === 1 ? `Manage Lists for \"${recordDisplayNames[0]}\"` : `Add ${recordCount} Records to List`;\n\n this.listManagementConfig = {\n mode: 'manage',\n entityId: entity.ID,\n entityName: entity.Name,\n recordIds,\n recordDisplayNames,\n allowCreate: true,\n allowRemove: recordCount === 1,\n showMembership: recordCount === 1,\n dialogTitle,\n };\n this.showListManagementDialog = true;\n this.cdr.detectChanges();\n }\n\n /** List-management dialog completed (membership changes applied) → tear down state. */\n onListManagementComplete(_result: ListManagementResult): void {\n this.showListManagementDialog = false;\n this.listManagementConfig = null;\n this.cdr.detectChanges();\n }\n\n /** List-management dialog cancelled → tear down state. */\n onListManagementCancel(): void {\n this.showListManagementDialog = false;\n this.listManagementConfig = null;\n this.cdr.detectChanges();\n }\n\n /**\n * Resolve a human-friendly display name for a record using the entity's NameField, falling back to\n * the composite-key string. Mirrors the legacy host's display-name resolution.\n */\n private getRecordDisplayName(record: Record<string, unknown>, entity: EntityInfo): string {\n if (entity.NameField) {\n const nameValue = record[entity.NameField.Name];\n if (nameValue != null) {\n return String(nameValue);\n }\n }\n return buildPkString(record, entity) || 'Unknown';\n }\n\n /**\n * Build the record-id strings used by list membership matching, in the compact form `MJ: List Details`\n * stores in `RecordID`: the bare value for a single-column primary key (whatever the column is called),\n * or the full `Field1|Value1||Field2|Value2` segment for a composite key. The entity is arbitrary, so the\n * key is built from all of its primary-key columns rather than assuming a single `ID`.\n */\n private buildRawRecordIds(records: Record<string, unknown>[], entity: EntityInfo): string[] {\n if (entity.PrimaryKeys.length === 0) {\n return [];\n }\n return records\n .map((record) => CompositeKey.FromEntityRecord(entity, record).ToCompactURLSegment())\n .filter((id) => id !== '');\n }\n\n // ================================================================\n // Self-contained: Delete (hosts <mj-ev-confirm-dialog> + MJ entity layer)\n // ================================================================\n\n /**\n * Delete button → stage the records and open the Generic confirm dialog. The actual delete happens\n * in {@link onDeleteConfirmed} after the user confirms — nothing bubbles up.\n */\n onDeleteButtonClick(records: Record<string, unknown>[]): void {\n if (!records || records.length === 0) {\n return;\n }\n this.pendingDeleteRecords = records;\n const count = records.length;\n this.deleteConfirmMessage = count === 1 ? 'Are you sure you want to delete this record?' : `Are you sure you want to delete these ${count} records?`;\n this.showDeleteConfirm = true;\n this.cdr.detectChanges();\n }\n\n /**\n * Delete confirmed → delete each staged record through the MJ entity layer\n * (`ProviderToUse.GetEntityObject(name, compositeKey, user)` → `Delete`), checking the boolean\n * result and surfacing failures via {@link LogError}. After deletion, re-request the current page\n * so the host reloads. Self-contained — no feature event leaves the wrapper.\n */\n async onDeleteConfirmed(): Promise<void> {\n this.showDeleteConfirm = false;\n const entity = this.entity;\n const records = this.pendingDeleteRecords;\n this.pendingDeleteRecords = [];\n\n if (!entity || records.length === 0) {\n return;\n }\n\n let anyDeleted = false;\n const provider = this.ProviderToUse;\n const user = provider.CurrentUser;\n\n for (const record of records) {\n const key = buildCompositeKey(record, entity);\n try {\n // The (name, key, user) overload instantiates AND loads in one call (throws if it can't load).\n const obj = await provider.GetEntityObject(entity.Name, key, user);\n const deleted = await obj.Delete();\n if (deleted) {\n anyDeleted = true;\n } else {\n LogError(`Delete failed: ${obj.LatestResult?.CompleteMessage ?? 'unknown error'}`);\n }\n } catch (err) {\n LogError(`Delete failed for ${entity.Name} (${key.ToString()}): ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n if (anyDeleted) {\n // Re-request the current page so the host reloads the (now smaller) record set.\n this.dataRequest.emit(this.currentPageDataRequest());\n }\n this.cdr.detectChanges();\n }\n\n /** Delete cancelled → discard the staged records and close the confirm dialog. */\n onDeleteCancelled(): void {\n this.showDeleteConfirm = false;\n this.pendingDeleteRecords = [];\n this.cdr.detectChanges();\n }\n\n // ================================================================\n // Helpers\n // ================================================================\n\n /** Build a {@link ViewDataRequest} that re-requests the host's current page/pageSize, if known. */\n private currentPageDataRequest(): ViewDataRequest {\n const req: ViewDataRequest = {};\n if (this.page != null) {\n req.page = this.page;\n }\n const ps = this.config.pageSize ?? this.pageSize;\n if (ps != null) {\n req.pageSize = ps;\n }\n return req;\n }\n\n /**\n * Resolve an entity's name from its ID via the active provider's metadata. Used when a foreign-key\n * click event omits the related entity name and only carries its ID.\n */\n private resolveEntityNameById(entityId: string): string | null {\n if (!entityId) {\n return null;\n }\n const match = this.ProviderToUse.Entities.find((e) => UUIDsEqual(e.ID, entityId));\n return match ? match.Name : null;\n }\n}\n\n/**\n * Tree-shaking guard. Force-references this renderer so bundlers (ESBuild/Vite) don't drop the\n * component in builds that only mount it dynamically via the ClassFactory/descriptor. Mirrors the\n * Cards/Cluster renderers' load guards; the parent wires this into a barrel/module load path.\n */\nexport function LoadGridViewRenderer(): void {\n // no-op; presence prevents tree-shaking of this component\n}\n"]}
|
|
@@ -152,6 +152,8 @@ export declare class TimelineViewRendererComponent extends BaseAngularComponent
|
|
|
152
152
|
* when there is no entity or no selected date field.
|
|
153
153
|
*/
|
|
154
154
|
private updateTimelineGroups;
|
|
155
|
+
/** Whether the named field of the current entity is a SQL `date` column (a calendar day). */
|
|
156
|
+
private isDateOnlyField;
|
|
155
157
|
/**
|
|
156
158
|
* Find the best field to use as the event title: the entity's NameField, then a
|
|
157
159
|
* `DefaultInView` string field matching a common name pattern, then the first string field.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-view-renderer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/timeline-view-renderer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,EAEZ,MAAM,EAGP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"timeline-view-renderer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/timeline-view-renderer.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component, Input, Output, EventEmitter, ChangeDetectorRef, ViewEncapsulation, inject, } from '@angular/core';
|
|
2
|
-
import { EntityFieldTSType } from '@memberjunction/core';
|
|
2
|
+
import { EntityFieldTSType, IsDateOnlySQLType } from '@memberjunction/core';
|
|
3
3
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
4
4
|
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
5
5
|
import { TimelineGroup, } from '@memberjunction/ng-timeline';
|
|
@@ -335,6 +335,9 @@ export class TimelineViewRendererComponent extends BaseAngularComponent {
|
|
|
335
335
|
group.DateFieldName = this.SelectedTimelineDateField;
|
|
336
336
|
group.IdFieldName = 'ID';
|
|
337
337
|
group.GroupLabel = this._entity.Name;
|
|
338
|
+
// The records are plain `simple` rows with no EntityInfo of their own: the group needs the
|
|
339
|
+
// entity's metadata to know a SQL `date` field is a calendar day, not an instant.
|
|
340
|
+
group.EntityInfo = this._entity;
|
|
338
341
|
const descField = this.findDescriptionField();
|
|
339
342
|
if (descField) {
|
|
340
343
|
group.DescriptionFieldName = descField;
|
|
@@ -348,13 +351,19 @@ export class TimelineViewRendererComponent extends BaseAngularComponent {
|
|
|
348
351
|
collapsible: true,
|
|
349
352
|
defaultExpanded: false,
|
|
350
353
|
showDate: true,
|
|
351
|
-
|
|
354
|
+
// A calendar day has no time: "12:00 AM" on it would be invented.
|
|
355
|
+
dateFormat: this.isDateOnlyField(this.SelectedTimelineDateField) ? 'MMM d, yyyy' : 'MMM d, yyyy h:mm a',
|
|
352
356
|
...(imageField
|
|
353
357
|
? { imageField, imagePosition: 'left', imageSize: 'small' }
|
|
354
358
|
: {}),
|
|
355
359
|
};
|
|
356
360
|
this.TimelineGroups = [group];
|
|
357
361
|
}
|
|
362
|
+
/** Whether the named field of the current entity is a SQL `date` column (a calendar day). */
|
|
363
|
+
isDateOnlyField(fieldName) {
|
|
364
|
+
const field = this._entity?.Fields.find((f) => f.Name.toLowerCase() === fieldName.toLowerCase());
|
|
365
|
+
return IsDateOnlySQLType(field?.Type);
|
|
366
|
+
}
|
|
358
367
|
/**
|
|
359
368
|
* Find the best field to use as the event title: the entity's NameField, then a
|
|
360
369
|
* `DefaultInView` string field matching a common name pattern, then the first string field.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline-view-renderer.component.js","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/timeline-view-renderer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n ChangeDetectorRef,\n OnInit,\n ViewEncapsulation,\n inject,\n} from '@angular/core';\nimport { EntityInfo, EntityFieldInfo, EntityFieldTSType } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport {\n TimelineGroup,\n TimeSegmentGrouping,\n TimelineSortOrder,\n AfterEventClickArgs,\n} from '@memberjunction/ng-timeline';\nimport { TimelineOrientation } from '../../types';\nimport { IViewRenderer } from '../view-type.contracts';\n\n/**\n * The persisted configuration shape for the Timeline view type.\n *\n * This is the `config` payload the host hands to the renderer on mount and that the\n * renderer emits back (in full) via {@link TimelineViewRendererComponent.configChanged}\n * whenever the user changes any of the timeline chrome controls. The host treats it as an\n * opaque blob — only this renderer (and an eventual prop-sheet) understands its keys — and\n * persists it per `MJ: View Types` row.\n *\n * All fields are optional: on first mount the renderer falls back to sensible defaults\n * (first available date field, vertical orientation, descending sort, monthly segments).\n */\nexport interface TimelineViewConfig {\n /** The entity date-field used to place records on the timeline (e.g. `__mj_CreatedAt`). */\n dateFieldName?: string;\n /** Vertical (alternating) vs. horizontal (single-row) timeline layout. */\n orientation?: TimelineOrientation;\n /** Newest-first (`desc`) vs. oldest-first (`asc`) event ordering. */\n sortOrder?: TimelineSortOrder;\n /** How events are bucketed into collapsible segments (day/week/month/...). */\n segmentGrouping?: TimeSegmentGrouping;\n}\n\n/**\n * TimelineViewRendererComponent\n * -----------------------------\n * The Timeline **view type** as a self-contained {@link IViewRenderer} plug-in. Where the\n * Timeline view used to be hard-wired into the generic `mj-entity-viewer` host (which owned\n * the date-field detection, the record→{@link TimelineGroup} transformation, and the\n * date-field / orientation / sort chrome), this component now OWNS all of it. The host simply\n * dynamic-mounts this renderer when Timeline is the active view type and feeds it the standard\n * renderer inputs (`entity` / `records` / `selectedRecordId` / `filterText` / `config`).\n *\n * It renders a small chrome header row (date-field selector + orientation toggle + sort toggle)\n * above a flex-filling `<mj-timeline>`, exactly reproducing the host's previous timeline chrome\n * and bindings. Because the host only mounts this plug-in when Timeline is active, the chrome\n * always renders — no extra `EffectiveViewMode === 'timeline'` guard is needed.\n *\n * **What was absorbed from the host** (`EntityViewerComponent`): `detectDateFields()`,\n * `sortDateFieldsByPriority()`, `AvailableDateFields`/`HasDateFields`, `configureTimeline()`,\n * `updateTimelineGroups()` (+ `findTitleField`/`findDescriptionField`/`findSubtitleField`),\n * `ToggleTimelineOrientation()`, `ToggleTimelineSortOrder()`, `SetTimelineDateField()`,\n * `SelectedDateFieldDisplayName`, `OnTimelineEventClick()`, and the `TimelineSelectedEventID`\n * composite-key→raw-id derivation.\n *\n * Inputs use the camelCase names mandated by the {@link IViewRenderer} contract (the host binds\n * them by those exact names) rather than MJ's usual PascalCase for public members.\n */\n@Component({\n standalone: false,\n selector: 'mj-timeline-view-renderer',\n encapsulation: ViewEncapsulation.None,\n template: `\n <!-- Chrome row: date-field selector + orientation toggle + sort toggle.\n Always rendered — this plug-in only mounts when Timeline is the active view. -->\n <div class=\"timeline-view-chrome\">\n <!-- Date Field Selector -->\n <div class=\"timeline-date-selector\">\n <i class=\"fa-solid fa-calendar-days\"></i>\n @if (AvailableDateFields.length === 1) {\n <span class=\"date-field-label\">{{ SelectedDateFieldDisplayName }}</span>\n } @else if (AvailableDateFields.length > 1) {\n <select\n class=\"date-field-select\"\n [value]=\"SelectedTimelineDateField\"\n (change)=\"SetTimelineDateField($any($event.target).value)\">\n @for (field of AvailableDateFields; track field.Name) {\n <option [value]=\"field.Name\">{{ field.DisplayNameOrName }}</option>\n }\n </select>\n }\n </div>\n\n <!-- Orientation Toggle -->\n <div class=\"timeline-orientation-toggle\">\n <button\n class=\"toggle-btn\"\n (click)=\"ToggleTimelineOrientation()\"\n [title]=\"TimelineOrientationState === 'vertical' ? 'Switch to Horizontal' : 'Switch to Vertical'\">\n <i [class]=\"TimelineOrientationState === 'vertical' ? 'fa-solid fa-ellipsis-vertical' : 'fa-solid fa-ellipsis'\"></i>\n </button>\n </div>\n\n <!-- Sort Order Toggle -->\n <div class=\"timeline-sort-toggle\">\n <button\n class=\"toggle-btn\"\n (click)=\"ToggleTimelineSortOrder()\"\n [title]=\"TimelineSortOrderState === 'desc' ? 'Showing Newest First (click for Oldest First)' : 'Showing Oldest First (click for Newest First)'\">\n <i [class]=\"TimelineSortOrderState === 'desc' ? 'fa-solid fa-arrow-down-wide-short' : 'fa-solid fa-arrow-up-wide-short'\"></i>\n </button>\n </div>\n </div>\n\n @if (HasDateFields) {\n <mj-timeline\n [groups]=\"TimelineGroups\"\n [orientation]=\"TimelineOrientationState\"\n [layout]=\"TimelineOrientationState === 'vertical' ? 'alternating' : 'single'\"\n [sortOrder]=\"TimelineSortOrderState\"\n [segmentGrouping]=\"TimelineSegmentGrouping\"\n [segmentsCollapsible]=\"true\"\n [segmentsDefaultExpanded]=\"true\"\n [selectedEventId]=\"TimelineSelectedEventID\"\n (afterEventClick)=\"OnTimelineEventClick($event)\">\n </mj-timeline>\n } @else {\n <mj-empty-state\n class=\"timeline-view-empty-fill\"\n Variant=\"warning\"\n Icon=\"fa-solid fa-calendar-xmark\"\n Title=\"This entity has no date fields to plot on a timeline.\" />\n }\n `,\n styles: [\n `\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .timeline-view-chrome {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n flex: 0 0 auto;\n }\n\n .timeline-view-renderer mj-timeline,\n :host mj-timeline {\n flex: 1 1 auto;\n min-height: 0;\n }\n\n /* Timeline Date Field Selector */\n .timeline-date-selector {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n }\n\n .timeline-date-selector i {\n color: var(--mj-text-disabled);\n }\n\n .date-field-label {\n color: var(--mj-text-secondary);\n font-weight: 500;\n }\n\n .date-field-select {\n padding: 4px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 13px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n outline: none;\n transition: border-color 0.15s ease;\n }\n\n .date-field-select:hover {\n border-color: var(--mj-border-strong);\n }\n\n .date-field-select:focus {\n border-color: var(--mj-brand-primary);\n }\n\n /* Orientation / Sort toggles */\n .timeline-orientation-toggle,\n .timeline-sort-toggle {\n display: flex;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n padding: 2px;\n }\n\n .toggle-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n }\n\n .toggle-btn:hover {\n color: var(--mj-text-secondary);\n }\n\n .toggle-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px color-mix(in srgb, var(--mj-text-primary) 10%, transparent);\n }\n\n /* Flex-fill so the empty-state placeholder occupies the timeline area\n (the host centers its content but does not grow to fill the parent). */\n .timeline-view-empty-fill {\n flex: 1 1 auto;\n }\n `,\n ],\n})\nexport class TimelineViewRendererComponent\n extends BaseAngularComponent\n implements IViewRenderer<TimelineViewConfig>, OnInit\n{\n // ========================================\n // IViewRenderer inputs (camelCase per the host contract)\n // ========================================\n\n private _entity: EntityInfo | null = null;\n /** The entity whose records are being rendered. Re-detects date fields + rebuilds groups on change. */\n @Input()\n set entity(value: EntityInfo | null) {\n const changed = !UUIDsEqual(value?.ID, this._entity?.ID);\n this._entity = value;\n if (changed) {\n this.detectDateFields();\n this.cdr.detectChanges();\n }\n }\n get entity(): EntityInfo | null {\n return this._entity;\n }\n\n private _records: Record<string, unknown>[] = [];\n /** The records to render (already loaded/filtered by the host). Rebuilds groups on change. */\n @Input()\n set records(value: Record<string, unknown>[]) {\n this._records = value ?? [];\n this.updateTimelineGroups();\n this.cdr.detectChanges();\n }\n get records(): Record<string, unknown>[] {\n return this._records;\n }\n\n /** Primary-key string of the currently selected record, if any (composite-key encoded). */\n @Input() selectedRecordId: string | null = null;\n\n /** Active filter text — accepted for contract uniformity; the host pre-filters `records`. */\n @Input() filterText: string | null = null;\n\n private _config: TimelineViewConfig = {};\n /**\n * This view's persisted configuration. Seeding the chrome controls from it is deferred to\n * {@link applyConfigToState}, called both here (for late updates) and in {@link ngOnInit}\n * (for the initial mount, where the host sets `config` via `setInput` before init runs).\n */\n @Input()\n set config(value: TimelineViewConfig) {\n this._config = value ?? {};\n if (this._initialized) {\n this.applyConfigToState();\n this.cdr.detectChanges();\n }\n }\n get config(): TimelineViewConfig {\n return this._config;\n }\n\n // ========================================\n // IViewRenderer outputs\n // ========================================\n\n /** Emitted when a timeline event is clicked — payload is the RAW record object. */\n @Output() recordSelected = new EventEmitter<unknown>();\n\n /** Emitted when a record should be opened — payload is the RAW record object. */\n @Output() recordOpened = new EventEmitter<unknown>();\n\n /** Emitted (with the FULL updated config) whenever the user changes a chrome control. */\n @Output() configChanged = new EventEmitter<TimelineViewConfig>();\n\n // ========================================\n // RENDER STATE (absorbed from the host)\n // ========================================\n\n /** Whether the current entity has any date fields available for timeline view. */\n public HasDateFields = false;\n\n /** Available date fields from the entity, sorted by priority. */\n public AvailableDateFields: EntityFieldInfo[] = [];\n\n /** The currently selected date field name for the timeline. */\n public SelectedTimelineDateField: string | null = null;\n\n /** The built timeline groups handed to `<mj-timeline>`. */\n public TimelineGroups: TimelineGroup<Record<string, unknown>>[] = [];\n\n /**\n * Timeline sort order. Named `…State` (not `TimelineSortOrder`) to avoid colliding with the\n * imported {@link TimelineSortOrder} type symbol used in the template / signatures.\n */\n public TimelineSortOrderState: TimelineSortOrder = 'desc';\n\n /** Timeline segment grouping (day/week/month/...). */\n public TimelineSegmentGrouping: TimeSegmentGrouping = 'month';\n\n /**\n * Timeline orientation. Named `…State` to avoid colliding with the imported\n * {@link TimelineOrientation} type symbol.\n */\n public TimelineOrientationState: TimelineOrientation = 'vertical';\n\n private _initialized = false;\n private cdr = inject(ChangeDetectorRef);\n\n // ========================================\n // LIFECYCLE\n // ========================================\n\n ngOnInit(): void {\n // The host sets entity/records/config via setInput BEFORE ngOnInit, so detection may have\n // already run. Apply the seeded config now (controls + defaults), then (re)build groups.\n this._initialized = true;\n this.applyConfigToState();\n this.detectDateFields();\n this.cdr.detectChanges();\n }\n\n // ========================================\n // COMPUTED\n // ========================================\n\n /** Display name of the currently selected timeline date field. */\n get SelectedDateFieldDisplayName(): string {\n if (!this.SelectedTimelineDateField) {\n return '';\n }\n const field = this.AvailableDateFields.find((f) => f.Name === this.SelectedTimelineDateField);\n return field?.DisplayNameOrName || this.SelectedTimelineDateField;\n }\n\n /**\n * The raw ID value derived from {@link selectedRecordId} for timeline selection. The host\n * passes the selection in composite-key format (`ID|abc-123` or `ID=abc-123`), but the\n * timeline stores just the raw ID value — so strip the field-name prefix.\n */\n get TimelineSelectedEventID(): string | null {\n if (!this.selectedRecordId) {\n return null;\n }\n if (this.selectedRecordId.includes('|')) {\n const parts = this.selectedRecordId.split('|');\n return parts.length > 1 ? parts[1] : this.selectedRecordId;\n }\n if (this.selectedRecordId.includes('=')) {\n const parts = this.selectedRecordId.split('=');\n return parts.length > 1 ? parts[1] : this.selectedRecordId;\n }\n return this.selectedRecordId;\n }\n\n // ========================================\n // CHROME ACTIONS (absorbed from the host)\n // ========================================\n\n /** Toggle timeline orientation between vertical and horizontal, then persist via config. */\n ToggleTimelineOrientation(): void {\n this.TimelineOrientationState = this.TimelineOrientationState === 'vertical' ? 'horizontal' : 'vertical';\n this.emitConfigChange();\n this.cdr.detectChanges();\n }\n\n /** Toggle timeline sort order between newest-first (desc) and oldest-first (asc), then persist. */\n ToggleTimelineSortOrder(): void {\n this.TimelineSortOrderState = this.TimelineSortOrderState === 'desc' ? 'asc' : 'desc';\n this.emitConfigChange();\n this.cdr.detectChanges();\n }\n\n /** Change the date field used for the timeline, rebuild groups, then persist via config. */\n SetTimelineDateField(fieldName: string): void {\n if (this.AvailableDateFields.some((f) => f.Name === fieldName)) {\n this.SelectedTimelineDateField = fieldName;\n this.updateTimelineGroups();\n this.emitConfigChange();\n this.cdr.detectChanges();\n }\n }\n\n // ========================================\n // TIMELINE EVENT → RECORD MAPPING\n // ========================================\n\n /**\n * Handle a timeline event click. Maps the {@link AfterEventClickArgs} to the underlying raw\n * record (`event.event.entity`) and emits it on {@link recordSelected}. The host's dynamic\n * handler builds the composite key from the raw record, so we emit the record object as-is.\n */\n OnTimelineEventClick(event: AfterEventClickArgs): void {\n const record = event.event.entity as Record<string, unknown> | undefined;\n if (record) {\n this.recordSelected.emit(record);\n }\n }\n\n // ========================================\n // CONFIG <-> STATE\n // ========================================\n\n /**\n * Seed the chrome control state from {@link config}, applying sensible defaults where the\n * config is silent (vertical orientation, descending sort, monthly segments). The selected\n * date field is resolved against the currently-available date fields in {@link detectDateFields}.\n */\n private applyConfigToState(): void {\n const c = this._config;\n this.TimelineOrientationState = c.orientation ?? 'vertical';\n this.TimelineSortOrderState = c.sortOrder ?? 'desc';\n this.TimelineSegmentGrouping = c.segmentGrouping ?? 'month';\n }\n\n /** Emit the FULL current config object so the host can persist the change. */\n private emitConfigChange(): void {\n this._config = {\n dateFieldName: this.SelectedTimelineDateField ?? undefined,\n orientation: this.TimelineOrientationState,\n sortOrder: this.TimelineSortOrderState,\n segmentGrouping: this.TimelineSegmentGrouping,\n };\n this.configChanged.emit(this._config);\n }\n\n // ========================================\n // DATE-FIELD DETECTION (absorbed from the host)\n // ========================================\n\n /**\n * Detect the entity's date fields and configure the timeline. Resolves the selected date\n * field (preferring the configured one, falling back to the highest-priority available field)\n * and rebuilds the groups. No-ops to an empty state when the entity has no date fields.\n */\n private detectDateFields(): void {\n if (!this._entity) {\n this.HasDateFields = false;\n this.AvailableDateFields = [];\n this.TimelineGroups = [];\n return;\n }\n\n const dateFields = this._entity.Fields.filter((f) => f.TSType === EntityFieldTSType.Date);\n if (dateFields.length === 0) {\n this.HasDateFields = false;\n this.AvailableDateFields = [];\n this.TimelineGroups = [];\n return;\n }\n\n this.AvailableDateFields = this.sortDateFieldsByPriority(dateFields);\n this.HasDateFields = true;\n this.SelectedTimelineDateField = this.getEffectiveTimelineDateField();\n this.updateTimelineGroups();\n }\n\n /**\n * Sort date fields by priority: `DefaultInView` fields first (by Sequence), then the rest\n * (by Sequence).\n */\n private sortDateFieldsByPriority(dateFields: EntityFieldInfo[]): EntityFieldInfo[] {\n const defaultInView = dateFields.filter((f) => f.DefaultInView).sort((a, b) => a.Sequence - b.Sequence);\n const others = dateFields.filter((f) => !f.DefaultInView).sort((a, b) => a.Sequence - b.Sequence);\n return [...defaultInView, ...others];\n }\n\n /**\n * Resolve which date field to use: the configured `dateFieldName` when it's still a valid\n * available field, otherwise the first (highest-priority) available date field.\n */\n private getEffectiveTimelineDateField(): string {\n if (this._config.dateFieldName) {\n const configField = this.AvailableDateFields.find((f) => f.Name === this._config.dateFieldName);\n if (configField) {\n return configField.Name;\n }\n }\n return this.AvailableDateFields[0].Name;\n }\n\n // ========================================\n // GROUP BUILDING (absorbed from the host)\n // ========================================\n\n /**\n * Build the single {@link TimelineGroup} for the current entity + records + selected date\n * field, mirroring the host's previous `updateTimelineGroups()`. No-ops to an empty state\n * when there is no entity or no selected date field.\n */\n private updateTimelineGroups(): void {\n // Emit NO group until there is an entity, a resolved date field, AND records. Emitting an empty\n // group (records not loaded yet) makes the inner <mj-timeline> run its first refresh with zero\n // events and mark itself loaded; the later records-populated update can then be dropped by its\n // concurrent-refresh / already-loaded guards, leaving \"No events to display\". Waiting for records\n // means its first real load happens with data.\n if (!this._entity || !this.SelectedTimelineDateField || this._records.length === 0) {\n this.TimelineGroups = [];\n return;\n }\n\n const titleField = this.findTitleField();\n\n const group = new TimelineGroup<Record<string, unknown>>();\n group.DataSourceType = 'array';\n group.EntityObjects = this._records;\n group.TitleFieldName = titleField;\n group.DateFieldName = this.SelectedTimelineDateField;\n group.IdFieldName = 'ID';\n group.GroupLabel = this._entity.Name;\n\n const descField = this.findDescriptionField();\n if (descField) {\n group.DescriptionFieldName = descField;\n }\n\n const subtitleField = this.findSubtitleField(titleField);\n if (subtitleField) {\n group.SubtitleFieldName = subtitleField;\n }\n\n const imageField = this.findImageField();\n group.CardConfig = {\n collapsible: true,\n defaultExpanded: false,\n showDate: true,\n dateFormat: 'MMM d, yyyy h:mm a',\n ...(imageField\n ? { imageField, imagePosition: 'left' as const, imageSize: 'small' as const }\n : {}),\n };\n\n this.TimelineGroups = [group];\n }\n\n /**\n * Find the best field to use as the event title: the entity's NameField, then a\n * `DefaultInView` string field matching a common name pattern, then the first string field.\n */\n private findTitleField(): string {\n if (!this._entity) {\n return 'ID';\n }\n if (this._entity.NameField) {\n return this._entity.NameField.Name;\n }\n\n const stringFields = this._entity.Fields.filter(\n (f) => f.TSType === EntityFieldTSType.String && f.DefaultInView && !f.Name.startsWith('__mj_'),\n ).sort((a, b) => a.Sequence - b.Sequence);\n\n const namePatterns = ['name', 'title', 'subject', 'label'];\n for (const pattern of namePatterns) {\n const match = stringFields.find((f) => f.Name.toLowerCase().includes(pattern));\n if (match) {\n return match.Name;\n }\n }\n\n return stringFields.length > 0 ? stringFields[0].Name : 'ID';\n }\n\n /** Find a suitable description field by common naming pattern, or null. */\n private findDescriptionField(): string | null {\n if (!this._entity) {\n return null;\n }\n const descPatterns = ['description', 'notes', 'summary', 'content', 'body', 'details'];\n const textFields = this._entity.Fields.filter(\n (f) => f.TSType === EntityFieldTSType.String && !f.Name.startsWith('__mj_'),\n );\n for (const pattern of descPatterns) {\n const match = textFields.find((f) => f.Name.toLowerCase().includes(pattern));\n if (match) {\n return match.Name;\n }\n }\n return null;\n }\n\n /**\n * Find a suitable subtitle field (different from the title): a `DefaultInView` string field\n * matching a classification pattern (status/type/category/...), else the first other string\n * field, else null.\n */\n private findSubtitleField(excludeField: string): string | null {\n if (!this._entity) {\n return null;\n }\n const patterns = ['status', 'type', 'category', 'state', 'priority'];\n const fields = this._entity.Fields.filter(\n (f) =>\n f.TSType === EntityFieldTSType.String &&\n f.DefaultInView &&\n f.Name !== excludeField &&\n !f.Name.startsWith('__mj_'),\n ).sort((a, b) => a.Sequence - b.Sequence);\n\n for (const pattern of patterns) {\n const match = fields.find((f) => f.Name.toLowerCase().includes(pattern));\n if (match) {\n return match.Name;\n }\n }\n\n const firstOther = fields.find((f) => f.Name !== excludeField);\n return firstOther?.Name || null;\n }\n\n /** Image field (ExtendedType='Image') so timeline cards crop a thumbnail, same as grid/cards. */\n private findImageField(): string | null {\n if (!this._entity) {\n return null;\n }\n const match = this._entity.Fields.find(\n (f) => f.ExtendedType === 'Image' && f.TSType === EntityFieldTSType.String,\n );\n return match?.Name ?? null;\n }\n}\n\n/**\n * Tree-shaking guard. Force-references the component class so bundlers don't drop this module\n * when it's only mounted dynamically via the view-type descriptor's `RendererComponent`.\n */\nexport function LoadTimelineViewRenderer(): void {\n // no-op; presence prevents tree-shaking of the dynamically-mounted renderer\n void TimelineViewRendererComponent;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"timeline-view-renderer.component.js","sourceRoot":"","sources":["../../../../src/lib/view-types/renderers/timeline-view-renderer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n ChangeDetectorRef,\n OnInit,\n ViewEncapsulation,\n inject,\n} from '@angular/core';\nimport { EntityInfo, EntityFieldInfo, EntityFieldTSType, IsDateOnlySQLType } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport {\n TimelineGroup,\n TimeSegmentGrouping,\n TimelineSortOrder,\n AfterEventClickArgs,\n} from '@memberjunction/ng-timeline';\nimport { TimelineOrientation } from '../../types';\nimport { IViewRenderer } from '../view-type.contracts';\n\n/**\n * The persisted configuration shape for the Timeline view type.\n *\n * This is the `config` payload the host hands to the renderer on mount and that the\n * renderer emits back (in full) via {@link TimelineViewRendererComponent.configChanged}\n * whenever the user changes any of the timeline chrome controls. The host treats it as an\n * opaque blob — only this renderer (and an eventual prop-sheet) understands its keys — and\n * persists it per `MJ: View Types` row.\n *\n * All fields are optional: on first mount the renderer falls back to sensible defaults\n * (first available date field, vertical orientation, descending sort, monthly segments).\n */\nexport interface TimelineViewConfig {\n /** The entity date-field used to place records on the timeline (e.g. `__mj_CreatedAt`). */\n dateFieldName?: string;\n /** Vertical (alternating) vs. horizontal (single-row) timeline layout. */\n orientation?: TimelineOrientation;\n /** Newest-first (`desc`) vs. oldest-first (`asc`) event ordering. */\n sortOrder?: TimelineSortOrder;\n /** How events are bucketed into collapsible segments (day/week/month/...). */\n segmentGrouping?: TimeSegmentGrouping;\n}\n\n/**\n * TimelineViewRendererComponent\n * -----------------------------\n * The Timeline **view type** as a self-contained {@link IViewRenderer} plug-in. Where the\n * Timeline view used to be hard-wired into the generic `mj-entity-viewer` host (which owned\n * the date-field detection, the record→{@link TimelineGroup} transformation, and the\n * date-field / orientation / sort chrome), this component now OWNS all of it. The host simply\n * dynamic-mounts this renderer when Timeline is the active view type and feeds it the standard\n * renderer inputs (`entity` / `records` / `selectedRecordId` / `filterText` / `config`).\n *\n * It renders a small chrome header row (date-field selector + orientation toggle + sort toggle)\n * above a flex-filling `<mj-timeline>`, exactly reproducing the host's previous timeline chrome\n * and bindings. Because the host only mounts this plug-in when Timeline is active, the chrome\n * always renders — no extra `EffectiveViewMode === 'timeline'` guard is needed.\n *\n * **What was absorbed from the host** (`EntityViewerComponent`): `detectDateFields()`,\n * `sortDateFieldsByPriority()`, `AvailableDateFields`/`HasDateFields`, `configureTimeline()`,\n * `updateTimelineGroups()` (+ `findTitleField`/`findDescriptionField`/`findSubtitleField`),\n * `ToggleTimelineOrientation()`, `ToggleTimelineSortOrder()`, `SetTimelineDateField()`,\n * `SelectedDateFieldDisplayName`, `OnTimelineEventClick()`, and the `TimelineSelectedEventID`\n * composite-key→raw-id derivation.\n *\n * Inputs use the camelCase names mandated by the {@link IViewRenderer} contract (the host binds\n * them by those exact names) rather than MJ's usual PascalCase for public members.\n */\n@Component({\n standalone: false,\n selector: 'mj-timeline-view-renderer',\n encapsulation: ViewEncapsulation.None,\n template: `\n <!-- Chrome row: date-field selector + orientation toggle + sort toggle.\n Always rendered — this plug-in only mounts when Timeline is the active view. -->\n <div class=\"timeline-view-chrome\">\n <!-- Date Field Selector -->\n <div class=\"timeline-date-selector\">\n <i class=\"fa-solid fa-calendar-days\"></i>\n @if (AvailableDateFields.length === 1) {\n <span class=\"date-field-label\">{{ SelectedDateFieldDisplayName }}</span>\n } @else if (AvailableDateFields.length > 1) {\n <select\n class=\"date-field-select\"\n [value]=\"SelectedTimelineDateField\"\n (change)=\"SetTimelineDateField($any($event.target).value)\">\n @for (field of AvailableDateFields; track field.Name) {\n <option [value]=\"field.Name\">{{ field.DisplayNameOrName }}</option>\n }\n </select>\n }\n </div>\n\n <!-- Orientation Toggle -->\n <div class=\"timeline-orientation-toggle\">\n <button\n class=\"toggle-btn\"\n (click)=\"ToggleTimelineOrientation()\"\n [title]=\"TimelineOrientationState === 'vertical' ? 'Switch to Horizontal' : 'Switch to Vertical'\">\n <i [class]=\"TimelineOrientationState === 'vertical' ? 'fa-solid fa-ellipsis-vertical' : 'fa-solid fa-ellipsis'\"></i>\n </button>\n </div>\n\n <!-- Sort Order Toggle -->\n <div class=\"timeline-sort-toggle\">\n <button\n class=\"toggle-btn\"\n (click)=\"ToggleTimelineSortOrder()\"\n [title]=\"TimelineSortOrderState === 'desc' ? 'Showing Newest First (click for Oldest First)' : 'Showing Oldest First (click for Newest First)'\">\n <i [class]=\"TimelineSortOrderState === 'desc' ? 'fa-solid fa-arrow-down-wide-short' : 'fa-solid fa-arrow-up-wide-short'\"></i>\n </button>\n </div>\n </div>\n\n @if (HasDateFields) {\n <mj-timeline\n [groups]=\"TimelineGroups\"\n [orientation]=\"TimelineOrientationState\"\n [layout]=\"TimelineOrientationState === 'vertical' ? 'alternating' : 'single'\"\n [sortOrder]=\"TimelineSortOrderState\"\n [segmentGrouping]=\"TimelineSegmentGrouping\"\n [segmentsCollapsible]=\"true\"\n [segmentsDefaultExpanded]=\"true\"\n [selectedEventId]=\"TimelineSelectedEventID\"\n (afterEventClick)=\"OnTimelineEventClick($event)\">\n </mj-timeline>\n } @else {\n <mj-empty-state\n class=\"timeline-view-empty-fill\"\n Variant=\"warning\"\n Icon=\"fa-solid fa-calendar-xmark\"\n Title=\"This entity has no date fields to plot on a timeline.\" />\n }\n `,\n styles: [\n `\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .timeline-view-chrome {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n flex: 0 0 auto;\n }\n\n .timeline-view-renderer mj-timeline,\n :host mj-timeline {\n flex: 1 1 auto;\n min-height: 0;\n }\n\n /* Timeline Date Field Selector */\n .timeline-date-selector {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n }\n\n .timeline-date-selector i {\n color: var(--mj-text-disabled);\n }\n\n .date-field-label {\n color: var(--mj-text-secondary);\n font-weight: 500;\n }\n\n .date-field-select {\n padding: 4px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 13px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n outline: none;\n transition: border-color 0.15s ease;\n }\n\n .date-field-select:hover {\n border-color: var(--mj-border-strong);\n }\n\n .date-field-select:focus {\n border-color: var(--mj-brand-primary);\n }\n\n /* Orientation / Sort toggles */\n .timeline-orientation-toggle,\n .timeline-sort-toggle {\n display: flex;\n background: var(--mj-bg-surface-card);\n border-radius: 6px;\n padding: 2px;\n }\n\n .toggle-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 4px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n transition: all 0.15s ease;\n }\n\n .toggle-btn:hover {\n color: var(--mj-text-secondary);\n }\n\n .toggle-btn.active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px color-mix(in srgb, var(--mj-text-primary) 10%, transparent);\n }\n\n /* Flex-fill so the empty-state placeholder occupies the timeline area\n (the host centers its content but does not grow to fill the parent). */\n .timeline-view-empty-fill {\n flex: 1 1 auto;\n }\n `,\n ],\n})\nexport class TimelineViewRendererComponent\n extends BaseAngularComponent\n implements IViewRenderer<TimelineViewConfig>, OnInit\n{\n // ========================================\n // IViewRenderer inputs (camelCase per the host contract)\n // ========================================\n\n private _entity: EntityInfo | null = null;\n /** The entity whose records are being rendered. Re-detects date fields + rebuilds groups on change. */\n @Input()\n set entity(value: EntityInfo | null) {\n const changed = !UUIDsEqual(value?.ID, this._entity?.ID);\n this._entity = value;\n if (changed) {\n this.detectDateFields();\n this.cdr.detectChanges();\n }\n }\n get entity(): EntityInfo | null {\n return this._entity;\n }\n\n private _records: Record<string, unknown>[] = [];\n /** The records to render (already loaded/filtered by the host). Rebuilds groups on change. */\n @Input()\n set records(value: Record<string, unknown>[]) {\n this._records = value ?? [];\n this.updateTimelineGroups();\n this.cdr.detectChanges();\n }\n get records(): Record<string, unknown>[] {\n return this._records;\n }\n\n /** Primary-key string of the currently selected record, if any (composite-key encoded). */\n @Input() selectedRecordId: string | null = null;\n\n /** Active filter text — accepted for contract uniformity; the host pre-filters `records`. */\n @Input() filterText: string | null = null;\n\n private _config: TimelineViewConfig = {};\n /**\n * This view's persisted configuration. Seeding the chrome controls from it is deferred to\n * {@link applyConfigToState}, called both here (for late updates) and in {@link ngOnInit}\n * (for the initial mount, where the host sets `config` via `setInput` before init runs).\n */\n @Input()\n set config(value: TimelineViewConfig) {\n this._config = value ?? {};\n if (this._initialized) {\n this.applyConfigToState();\n this.cdr.detectChanges();\n }\n }\n get config(): TimelineViewConfig {\n return this._config;\n }\n\n // ========================================\n // IViewRenderer outputs\n // ========================================\n\n /** Emitted when a timeline event is clicked — payload is the RAW record object. */\n @Output() recordSelected = new EventEmitter<unknown>();\n\n /** Emitted when a record should be opened — payload is the RAW record object. */\n @Output() recordOpened = new EventEmitter<unknown>();\n\n /** Emitted (with the FULL updated config) whenever the user changes a chrome control. */\n @Output() configChanged = new EventEmitter<TimelineViewConfig>();\n\n // ========================================\n // RENDER STATE (absorbed from the host)\n // ========================================\n\n /** Whether the current entity has any date fields available for timeline view. */\n public HasDateFields = false;\n\n /** Available date fields from the entity, sorted by priority. */\n public AvailableDateFields: EntityFieldInfo[] = [];\n\n /** The currently selected date field name for the timeline. */\n public SelectedTimelineDateField: string | null = null;\n\n /** The built timeline groups handed to `<mj-timeline>`. */\n public TimelineGroups: TimelineGroup<Record<string, unknown>>[] = [];\n\n /**\n * Timeline sort order. Named `…State` (not `TimelineSortOrder`) to avoid colliding with the\n * imported {@link TimelineSortOrder} type symbol used in the template / signatures.\n */\n public TimelineSortOrderState: TimelineSortOrder = 'desc';\n\n /** Timeline segment grouping (day/week/month/...). */\n public TimelineSegmentGrouping: TimeSegmentGrouping = 'month';\n\n /**\n * Timeline orientation. Named `…State` to avoid colliding with the imported\n * {@link TimelineOrientation} type symbol.\n */\n public TimelineOrientationState: TimelineOrientation = 'vertical';\n\n private _initialized = false;\n private cdr = inject(ChangeDetectorRef);\n\n // ========================================\n // LIFECYCLE\n // ========================================\n\n ngOnInit(): void {\n // The host sets entity/records/config via setInput BEFORE ngOnInit, so detection may have\n // already run. Apply the seeded config now (controls + defaults), then (re)build groups.\n this._initialized = true;\n this.applyConfigToState();\n this.detectDateFields();\n this.cdr.detectChanges();\n }\n\n // ========================================\n // COMPUTED\n // ========================================\n\n /** Display name of the currently selected timeline date field. */\n get SelectedDateFieldDisplayName(): string {\n if (!this.SelectedTimelineDateField) {\n return '';\n }\n const field = this.AvailableDateFields.find((f) => f.Name === this.SelectedTimelineDateField);\n return field?.DisplayNameOrName || this.SelectedTimelineDateField;\n }\n\n /**\n * The raw ID value derived from {@link selectedRecordId} for timeline selection. The host\n * passes the selection in composite-key format (`ID|abc-123` or `ID=abc-123`), but the\n * timeline stores just the raw ID value — so strip the field-name prefix.\n */\n get TimelineSelectedEventID(): string | null {\n if (!this.selectedRecordId) {\n return null;\n }\n if (this.selectedRecordId.includes('|')) {\n const parts = this.selectedRecordId.split('|');\n return parts.length > 1 ? parts[1] : this.selectedRecordId;\n }\n if (this.selectedRecordId.includes('=')) {\n const parts = this.selectedRecordId.split('=');\n return parts.length > 1 ? parts[1] : this.selectedRecordId;\n }\n return this.selectedRecordId;\n }\n\n // ========================================\n // CHROME ACTIONS (absorbed from the host)\n // ========================================\n\n /** Toggle timeline orientation between vertical and horizontal, then persist via config. */\n ToggleTimelineOrientation(): void {\n this.TimelineOrientationState = this.TimelineOrientationState === 'vertical' ? 'horizontal' : 'vertical';\n this.emitConfigChange();\n this.cdr.detectChanges();\n }\n\n /** Toggle timeline sort order between newest-first (desc) and oldest-first (asc), then persist. */\n ToggleTimelineSortOrder(): void {\n this.TimelineSortOrderState = this.TimelineSortOrderState === 'desc' ? 'asc' : 'desc';\n this.emitConfigChange();\n this.cdr.detectChanges();\n }\n\n /** Change the date field used for the timeline, rebuild groups, then persist via config. */\n SetTimelineDateField(fieldName: string): void {\n if (this.AvailableDateFields.some((f) => f.Name === fieldName)) {\n this.SelectedTimelineDateField = fieldName;\n this.updateTimelineGroups();\n this.emitConfigChange();\n this.cdr.detectChanges();\n }\n }\n\n // ========================================\n // TIMELINE EVENT → RECORD MAPPING\n // ========================================\n\n /**\n * Handle a timeline event click. Maps the {@link AfterEventClickArgs} to the underlying raw\n * record (`event.event.entity`) and emits it on {@link recordSelected}. The host's dynamic\n * handler builds the composite key from the raw record, so we emit the record object as-is.\n */\n OnTimelineEventClick(event: AfterEventClickArgs): void {\n const record = event.event.entity as Record<string, unknown> | undefined;\n if (record) {\n this.recordSelected.emit(record);\n }\n }\n\n // ========================================\n // CONFIG <-> STATE\n // ========================================\n\n /**\n * Seed the chrome control state from {@link config}, applying sensible defaults where the\n * config is silent (vertical orientation, descending sort, monthly segments). The selected\n * date field is resolved against the currently-available date fields in {@link detectDateFields}.\n */\n private applyConfigToState(): void {\n const c = this._config;\n this.TimelineOrientationState = c.orientation ?? 'vertical';\n this.TimelineSortOrderState = c.sortOrder ?? 'desc';\n this.TimelineSegmentGrouping = c.segmentGrouping ?? 'month';\n }\n\n /** Emit the FULL current config object so the host can persist the change. */\n private emitConfigChange(): void {\n this._config = {\n dateFieldName: this.SelectedTimelineDateField ?? undefined,\n orientation: this.TimelineOrientationState,\n sortOrder: this.TimelineSortOrderState,\n segmentGrouping: this.TimelineSegmentGrouping,\n };\n this.configChanged.emit(this._config);\n }\n\n // ========================================\n // DATE-FIELD DETECTION (absorbed from the host)\n // ========================================\n\n /**\n * Detect the entity's date fields and configure the timeline. Resolves the selected date\n * field (preferring the configured one, falling back to the highest-priority available field)\n * and rebuilds the groups. No-ops to an empty state when the entity has no date fields.\n */\n private detectDateFields(): void {\n if (!this._entity) {\n this.HasDateFields = false;\n this.AvailableDateFields = [];\n this.TimelineGroups = [];\n return;\n }\n\n const dateFields = this._entity.Fields.filter((f) => f.TSType === EntityFieldTSType.Date);\n if (dateFields.length === 0) {\n this.HasDateFields = false;\n this.AvailableDateFields = [];\n this.TimelineGroups = [];\n return;\n }\n\n this.AvailableDateFields = this.sortDateFieldsByPriority(dateFields);\n this.HasDateFields = true;\n this.SelectedTimelineDateField = this.getEffectiveTimelineDateField();\n this.updateTimelineGroups();\n }\n\n /**\n * Sort date fields by priority: `DefaultInView` fields first (by Sequence), then the rest\n * (by Sequence).\n */\n private sortDateFieldsByPriority(dateFields: EntityFieldInfo[]): EntityFieldInfo[] {\n const defaultInView = dateFields.filter((f) => f.DefaultInView).sort((a, b) => a.Sequence - b.Sequence);\n const others = dateFields.filter((f) => !f.DefaultInView).sort((a, b) => a.Sequence - b.Sequence);\n return [...defaultInView, ...others];\n }\n\n /**\n * Resolve which date field to use: the configured `dateFieldName` when it's still a valid\n * available field, otherwise the first (highest-priority) available date field.\n */\n private getEffectiveTimelineDateField(): string {\n if (this._config.dateFieldName) {\n const configField = this.AvailableDateFields.find((f) => f.Name === this._config.dateFieldName);\n if (configField) {\n return configField.Name;\n }\n }\n return this.AvailableDateFields[0].Name;\n }\n\n // ========================================\n // GROUP BUILDING (absorbed from the host)\n // ========================================\n\n /**\n * Build the single {@link TimelineGroup} for the current entity + records + selected date\n * field, mirroring the host's previous `updateTimelineGroups()`. No-ops to an empty state\n * when there is no entity or no selected date field.\n */\n private updateTimelineGroups(): void {\n // Emit NO group until there is an entity, a resolved date field, AND records. Emitting an empty\n // group (records not loaded yet) makes the inner <mj-timeline> run its first refresh with zero\n // events and mark itself loaded; the later records-populated update can then be dropped by its\n // concurrent-refresh / already-loaded guards, leaving \"No events to display\". Waiting for records\n // means its first real load happens with data.\n if (!this._entity || !this.SelectedTimelineDateField || this._records.length === 0) {\n this.TimelineGroups = [];\n return;\n }\n\n const titleField = this.findTitleField();\n\n const group = new TimelineGroup<Record<string, unknown>>();\n group.DataSourceType = 'array';\n group.EntityObjects = this._records;\n group.TitleFieldName = titleField;\n group.DateFieldName = this.SelectedTimelineDateField;\n group.IdFieldName = 'ID';\n group.GroupLabel = this._entity.Name;\n // The records are plain `simple` rows with no EntityInfo of their own: the group needs the\n // entity's metadata to know a SQL `date` field is a calendar day, not an instant.\n group.EntityInfo = this._entity;\n\n const descField = this.findDescriptionField();\n if (descField) {\n group.DescriptionFieldName = descField;\n }\n\n const subtitleField = this.findSubtitleField(titleField);\n if (subtitleField) {\n group.SubtitleFieldName = subtitleField;\n }\n\n const imageField = this.findImageField();\n group.CardConfig = {\n collapsible: true,\n defaultExpanded: false,\n showDate: true,\n // A calendar day has no time: \"12:00 AM\" on it would be invented.\n dateFormat: this.isDateOnlyField(this.SelectedTimelineDateField) ? 'MMM d, yyyy' : 'MMM d, yyyy h:mm a',\n ...(imageField\n ? { imageField, imagePosition: 'left' as const, imageSize: 'small' as const }\n : {}),\n };\n\n this.TimelineGroups = [group];\n }\n\n /** Whether the named field of the current entity is a SQL `date` column (a calendar day). */\n private isDateOnlyField(fieldName: string): boolean {\n const field = this._entity?.Fields.find((f) => f.Name.toLowerCase() === fieldName.toLowerCase());\n return IsDateOnlySQLType(field?.Type);\n }\n\n /**\n * Find the best field to use as the event title: the entity's NameField, then a\n * `DefaultInView` string field matching a common name pattern, then the first string field.\n */\n private findTitleField(): string {\n if (!this._entity) {\n return 'ID';\n }\n if (this._entity.NameField) {\n return this._entity.NameField.Name;\n }\n\n const stringFields = this._entity.Fields.filter(\n (f) => f.TSType === EntityFieldTSType.String && f.DefaultInView && !f.Name.startsWith('__mj_'),\n ).sort((a, b) => a.Sequence - b.Sequence);\n\n const namePatterns = ['name', 'title', 'subject', 'label'];\n for (const pattern of namePatterns) {\n const match = stringFields.find((f) => f.Name.toLowerCase().includes(pattern));\n if (match) {\n return match.Name;\n }\n }\n\n return stringFields.length > 0 ? stringFields[0].Name : 'ID';\n }\n\n /** Find a suitable description field by common naming pattern, or null. */\n private findDescriptionField(): string | null {\n if (!this._entity) {\n return null;\n }\n const descPatterns = ['description', 'notes', 'summary', 'content', 'body', 'details'];\n const textFields = this._entity.Fields.filter(\n (f) => f.TSType === EntityFieldTSType.String && !f.Name.startsWith('__mj_'),\n );\n for (const pattern of descPatterns) {\n const match = textFields.find((f) => f.Name.toLowerCase().includes(pattern));\n if (match) {\n return match.Name;\n }\n }\n return null;\n }\n\n /**\n * Find a suitable subtitle field (different from the title): a `DefaultInView` string field\n * matching a classification pattern (status/type/category/...), else the first other string\n * field, else null.\n */\n private findSubtitleField(excludeField: string): string | null {\n if (!this._entity) {\n return null;\n }\n const patterns = ['status', 'type', 'category', 'state', 'priority'];\n const fields = this._entity.Fields.filter(\n (f) =>\n f.TSType === EntityFieldTSType.String &&\n f.DefaultInView &&\n f.Name !== excludeField &&\n !f.Name.startsWith('__mj_'),\n ).sort((a, b) => a.Sequence - b.Sequence);\n\n for (const pattern of patterns) {\n const match = fields.find((f) => f.Name.toLowerCase().includes(pattern));\n if (match) {\n return match.Name;\n }\n }\n\n const firstOther = fields.find((f) => f.Name !== excludeField);\n return firstOther?.Name || null;\n }\n\n /** Image field (ExtendedType='Image') so timeline cards crop a thumbnail, same as grid/cards. */\n private findImageField(): string | null {\n if (!this._entity) {\n return null;\n }\n const match = this._entity.Fields.find(\n (f) => f.ExtendedType === 'Image' && f.TSType === EntityFieldTSType.String,\n );\n return match?.Name ?? null;\n }\n}\n\n/**\n * Tree-shaking guard. Force-references the component class so bundlers don't drop this module\n * when it's only mounted dynamically via the view-type descriptor's `RendererComponent`.\n */\nexport function LoadTimelineViewRenderer(): void {\n // no-op; presence prevents tree-shaking of the dynamically-mounted renderer\n void TimelineViewRendererComponent;\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Type, EventEmitter } from '@angular/core';
|
|
2
2
|
import { EntityInfo, IMetadataProvider } from '@memberjunction/core';
|
|
3
|
+
import { ExportColumn } from '@memberjunction/export-engine';
|
|
3
4
|
/**
|
|
4
5
|
* View-Type Plugin Architecture — Contracts
|
|
5
6
|
* -----------------------------------------
|
|
@@ -190,6 +191,13 @@ export interface IViewRenderer<TConfig = unknown> {
|
|
|
190
191
|
* @returns true when the export was initiated, false when it couldn't be (no data / unsupported).
|
|
191
192
|
*/
|
|
192
193
|
exportRecords?(format?: 'csv' | 'excel' | 'json'): Promise<boolean>;
|
|
194
|
+
/**
|
|
195
|
+
* Optional: the columns this renderer is showing, in on-screen order and with on-screen headers.
|
|
196
|
+
* A host that exports on the renderer's behalf (the workspace toolbar's Export) uses this so its
|
|
197
|
+
* export matches the screen. Renderers without a column layout (Cards, Map, Timeline) omit it and
|
|
198
|
+
* the host falls back to the view's saved columns.
|
|
199
|
+
*/
|
|
200
|
+
GetExportColumns?(): ExportColumn[];
|
|
193
201
|
}
|
|
194
202
|
/**
|
|
195
203
|
* A generic, view-type-agnostic request from a renderer for the host to change how it loads the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-type.contracts.d.ts","sourceRoot":"","sources":["../../../src/lib/view-types/view-type.contracts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"view-type.contracts.d.ts","sourceRoot":"","sources":["../../../src/lib/view-types/view-type.contracts.ts"],"names":[],"mappings":"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"}
|