@memberjunction/ng-entity-viewer 5.39.0 → 5.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/__tests__/view-types.test.d.ts +2 -0
  2. package/dist/__tests__/view-types.test.d.ts.map +1 -0
  3. package/dist/__tests__/view-types.test.js +102 -0
  4. package/dist/__tests__/view-types.test.js.map +1 -0
  5. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
  6. package/dist/lib/entity-data-grid/entity-data-grid.component.js +8 -0
  7. package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
  8. package/dist/lib/entity-viewer/entity-viewer.component.d.ts +356 -341
  9. package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
  10. package/dist/lib/entity-viewer/entity-viewer.component.js +993 -1097
  11. package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
  12. package/dist/lib/view-config-panel/view-config-panel.component.d.ts +126 -126
  13. package/dist/lib/view-config-panel/view-config-panel.component.js +635 -635
  14. package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
  15. package/dist/lib/view-selector/view-selector.component.d.ts +226 -0
  16. package/dist/lib/view-selector/view-selector.component.d.ts.map +1 -0
  17. package/dist/lib/view-selector/view-selector.component.js +861 -0
  18. package/dist/lib/view-selector/view-selector.component.js.map +1 -0
  19. package/dist/lib/view-type-switcher/view-type-switcher.component.d.ts +114 -0
  20. package/dist/lib/view-type-switcher/view-type-switcher.component.d.ts.map +1 -0
  21. package/dist/lib/view-type-switcher/view-type-switcher.component.js +209 -0
  22. package/dist/lib/view-type-switcher/view-type-switcher.component.js.map +1 -0
  23. package/dist/lib/view-types/descriptors/cards-view-type.d.ts +18 -0
  24. package/dist/lib/view-types/descriptors/cards-view-type.d.ts.map +1 -0
  25. package/dist/lib/view-types/descriptors/cards-view-type.js +31 -0
  26. package/dist/lib/view-types/descriptors/cards-view-type.js.map +1 -0
  27. package/dist/lib/view-types/descriptors/grid-view-type.d.ts +17 -0
  28. package/dist/lib/view-types/descriptors/grid-view-type.d.ts.map +1 -0
  29. package/dist/lib/view-types/descriptors/grid-view-type.js +30 -0
  30. package/dist/lib/view-types/descriptors/grid-view-type.js.map +1 -0
  31. package/dist/lib/view-types/descriptors/map-view-type.d.ts +21 -0
  32. package/dist/lib/view-types/descriptors/map-view-type.d.ts.map +1 -0
  33. package/dist/lib/view-types/descriptors/map-view-type.js +35 -0
  34. package/dist/lib/view-types/descriptors/map-view-type.js.map +1 -0
  35. package/dist/lib/view-types/descriptors/timeline-view-type.d.ts +22 -0
  36. package/dist/lib/view-types/descriptors/timeline-view-type.d.ts.map +1 -0
  37. package/dist/lib/view-types/descriptors/timeline-view-type.js +40 -0
  38. package/dist/lib/view-types/descriptors/timeline-view-type.js.map +1 -0
  39. package/dist/lib/view-types/index.d.ts +20 -0
  40. package/dist/lib/view-types/index.d.ts.map +1 -0
  41. package/dist/lib/view-types/index.js +29 -0
  42. package/dist/lib/view-types/index.js.map +1 -0
  43. package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts +93 -0
  44. package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts.map +1 -0
  45. package/dist/lib/view-types/renderers/cards-view-renderer.component.js +144 -0
  46. package/dist/lib/view-types/renderers/cards-view-renderer.component.js.map +1 -0
  47. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts +273 -0
  48. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts.map +1 -0
  49. package/dist/lib/view-types/renderers/grid-view-renderer.component.js +558 -0
  50. package/dist/lib/view-types/renderers/grid-view-renderer.component.js.map +1 -0
  51. package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts +135 -0
  52. package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts.map +1 -0
  53. package/dist/lib/view-types/renderers/map-view-renderer.component.js +216 -0
  54. package/dist/lib/view-types/renderers/map-view-renderer.component.js.map +1 -0
  55. package/dist/lib/view-types/renderers/timeline-view-renderer.component.d.ts +176 -0
  56. package/dist/lib/view-types/renderers/timeline-view-renderer.component.d.ts.map +1 -0
  57. package/dist/lib/view-types/renderers/timeline-view-renderer.component.js +535 -0
  58. package/dist/lib/view-types/renderers/timeline-view-renderer.component.js.map +1 -0
  59. package/dist/lib/view-types/view-type.contracts.d.ts +235 -0
  60. package/dist/lib/view-types/view-type.contracts.d.ts.map +1 -0
  61. package/dist/lib/view-types/view-type.contracts.js +51 -0
  62. package/dist/lib/view-types/view-type.contracts.js.map +1 -0
  63. package/dist/lib/view-types/view-type.engine.d.ts +76 -0
  64. package/dist/lib/view-types/view-type.engine.d.ts.map +1 -0
  65. package/dist/lib/view-types/view-type.engine.js +138 -0
  66. package/dist/lib/view-types/view-type.engine.js.map +1 -0
  67. package/dist/lib/view-workspace/view-workspace.component.d.ts +451 -0
  68. package/dist/lib/view-workspace/view-workspace.component.d.ts.map +1 -0
  69. package/dist/lib/view-workspace/view-workspace.component.js +1212 -0
  70. package/dist/lib/view-workspace/view-workspace.component.js.map +1 -0
  71. package/dist/module.d.ts +20 -11
  72. package/dist/module.d.ts.map +1 -1
  73. package/dist/module.js +50 -8
  74. package/dist/module.js.map +1 -1
  75. package/dist/public-api.d.ts +8 -0
  76. package/dist/public-api.d.ts.map +1 -1
  77. package/dist/public-api.js +14 -0
  78. package/dist/public-api.js.map +1 -1
  79. package/package.json +16 -15
@@ -0,0 +1,235 @@
1
+ import { Type, EventEmitter } from '@angular/core';
2
+ import { EntityInfo, IMetadataProvider } from '@memberjunction/core';
3
+ /**
4
+ * View-Type Plugin Architecture — Contracts
5
+ * -----------------------------------------
6
+ * This file defines the framework-level contracts for the entity-viewer's pluggable
7
+ * view-type system. A "view type" is a way of rendering a set of entity records — the
8
+ * built-in ones being Grid, Cards, Timeline, and Map, with future additions like
9
+ * Cluster and Tag Cloud.
10
+ *
11
+ * The design separates three concerns:
12
+ * 1. {@link IViewTypeDescriptor} — metadata + availability + the component types to mount.
13
+ * Descriptors are discovered at runtime via the {@link https MemberJunction ClassFactory}
14
+ * keyed off the `DriverClass` stored in the `MJ: View Types` entity.
15
+ * 2. {@link IViewRenderer} — the contract a renderer component honors so the host can
16
+ * feed it data and listen for selection/open/config events uniformly.
17
+ * 3. {@link IViewPropSheet} — the contract a configuration prop-sheet component honors.
18
+ *
19
+ * Because this lives in an Angular package, it is acceptable for the descriptor to reference
20
+ * Angular's `Type<T>` (it points at the concrete component classes to instantiate).
21
+ */
22
+ /**
23
+ * Framework-agnostic-ish descriptor for a single view type.
24
+ *
25
+ * A descriptor is the bridge between the `MJ: View Types` metadata row (which stores
26
+ * the `DriverClass` name) and the concrete Angular components that actually render the
27
+ * view and edit its configuration. Descriptors are registered with the ClassFactory via
28
+ * `@RegisterClass(BaseViewTypeDescriptor, '<DriverClass>')` so they can be discovered by
29
+ * name without a hard import from the host.
30
+ */
31
+ export interface IViewTypeDescriptor {
32
+ /**
33
+ * Stable internal key for the view type — matches the `DriverClass` column on the
34
+ * `MJ: View Types` entity and the `@RegisterClass` registration key
35
+ * (e.g. "GridViewType", "CardsViewType").
36
+ */
37
+ readonly Name: string;
38
+ /** User-facing label shown in the view-mode switcher (e.g. "Grid", "Tag Cloud"). */
39
+ readonly DisplayName: string;
40
+ /** Font Awesome icon class shown in the switcher (e.g. "fa-solid fa-table"). */
41
+ readonly Icon: string;
42
+ /**
43
+ * The Angular component class that renders this view type. The host instantiates /
44
+ * mounts this component and feeds it data per the {@link IViewRenderer} contract.
45
+ */
46
+ readonly RendererComponent: Type<unknown>;
47
+ /**
48
+ * Optional Angular component class for this view type's configuration prop-sheet
49
+ * (honors {@link IViewPropSheet}). Undefined when the view type has no configurable
50
+ * options.
51
+ */
52
+ readonly PropSheetComponent?: Type<unknown>;
53
+ /**
54
+ * Predicate that decides whether this view type is available for a given entity.
55
+ * For example, Timeline requires a date field; Map requires geocoding support.
56
+ * Grid and Cards are always available.
57
+ *
58
+ * @param entity the entity the viewer is currently displaying
59
+ * @param provider optional metadata provider (for multi-provider scenarios)
60
+ */
61
+ IsAvailableFor(entity: EntityInfo, provider?: IMetadataProvider): boolean;
62
+ /**
63
+ * Optional async hook to load any data this descriptor's {@link IsAvailableFor} predicate
64
+ * depends on (e.g. the Cluster view type needs the set of entities that have an active
65
+ * Entity Document with vectors). The host awaits this once — before computing availability —
66
+ * so the synchronous predicate can read from a now-populated cache. Implementations should
67
+ * be cheap/idempotent (typically `await SomeEngine.Instance.Config(false, ...)`). Omit when
68
+ * availability is computable purely from the {@link EntityInfo} (Grid/Cards/Timeline/Map).
69
+ *
70
+ * @param provider optional metadata provider (for multi-provider scenarios)
71
+ */
72
+ EnsureAvailabilityData?(provider?: IMetadataProvider): Promise<void>;
73
+ }
74
+ /**
75
+ * Contract honored by a view renderer component. The host binds these inputs and
76
+ * subscribes to these outputs uniformly across all view types.
77
+ *
78
+ * @typeParam TConfig the shape of this view type's configuration object
79
+ */
80
+ export interface IViewRenderer<TConfig = unknown> {
81
+ /** The entity whose records are being rendered. */
82
+ entity: EntityInfo | null;
83
+ /** The records to render (already loaded/filtered by the host). */
84
+ records: Record<string, unknown>[];
85
+ /** Primary-key string of the currently selected record, if any. */
86
+ selectedRecordId: string | null;
87
+ /** Active filter text (for highlighting / client-side concerns). */
88
+ filterText: string | null;
89
+ /** View-type-specific configuration. */
90
+ config: TConfig;
91
+ /**
92
+ * Optional: the metadata provider, handed to plug-ins that issue their own data calls
93
+ * (multi-provider safety). Generic — not specific to any one view type.
94
+ */
95
+ provider?: IMetadataProvider | null;
96
+ /**
97
+ * Optional generic data-context the host supplies alongside {@link records}. These are
98
+ * universal "a view of records" concepts (counts, current page, loading) — NOT specific to any
99
+ * view type — so a renderer that paginates or shows a total can read them, and one that doesn't
100
+ * simply ignores them. The host owns the actual data fetch; these describe its current result.
101
+ */
102
+ totalRecordCount?: number;
103
+ /** One-based current page of {@link records} when the host is paginating. */
104
+ page?: number;
105
+ /** Page size the host is using. */
106
+ pageSize?: number;
107
+ /** Whether the host is currently (re)loading the record set. */
108
+ isLoading?: boolean;
109
+ /** Emitted when a record is selected (single click). Payload is the raw record object. */
110
+ recordSelected: EventEmitter<unknown>;
111
+ /**
112
+ * Emitted when THIS entity's record should be opened (double-click / open). Payload is the raw
113
+ * record. This is a NAVIGATION request — the only category of signal that legitimately bubbles
114
+ * to the outer app (routing lives there). Everything else a view does (export, add-to-list,
115
+ * delete, …) is self-contained in the plug-in via Generic dialogs and never bubbles up.
116
+ */
117
+ recordOpened: EventEmitter<unknown>;
118
+ /**
119
+ * Optional: NAVIGATION request to open a *related* record on a DIFFERENT entity (e.g. a
120
+ * foreign-key drill-through in a grid cell). Bubbles to the outer app for routing. Generic —
121
+ * the container forwards it without acting on it.
122
+ */
123
+ openRelatedRecordRequested?: EventEmitter<ViewRelatedRecordNavigation>;
124
+ /**
125
+ * Optional: NAVIGATION request to create a new record of the current entity (e.g. a grid's
126
+ * "New" button) — opening the create form is a routing concern owned by the outer app. Bubbles
127
+ * up; the container forwards it without acting on it.
128
+ */
129
+ createRecordRequested?: EventEmitter<void>;
130
+ /**
131
+ * Emitted when the renderer mutates its own opaque {@link config} (e.g. timeline date field,
132
+ * grid columns/sort/widths, map render mode). The container persists the blob verbatim against
133
+ * the active `ViewTypeID` and never inspects it. (Container ↔ plug-in coordination inside the
134
+ * Generic layer — NOT a signal that drives the outer app.)
135
+ */
136
+ configChanged: EventEmitter<TConfig>;
137
+ /**
138
+ * Optional: ask the container to (re)load the record set differently. The ONLY data-access
139
+ * channel a plug-in has, fully generic — the container honors the request (sort / page /
140
+ * load-all) without knowing which plug-in asked or why. A grid emits sort/page; a map emits
141
+ * `{ loadAll: true }`; a plug-in happy with the records it's given emits nothing. The container
142
+ * owns the actual `RunView`; no per-view-type branching. (Container ↔ plug-in coordination —
143
+ * NOT a signal that drives the outer app.)
144
+ */
145
+ dataRequest?: EventEmitter<ViewDataRequest>;
146
+ }
147
+ /**
148
+ * A generic, view-type-agnostic request from a renderer for the host to change how it loads the
149
+ * record set. Every field is optional; the host applies whatever is present. No field names a
150
+ * specific view type — these are universal data-access concepts.
151
+ */
152
+ export interface ViewDataRequest {
153
+ /** Desired sort, applied to the host's RunView OrderBy. Empty/omitted clears sorting. */
154
+ sort?: Array<{
155
+ field: string;
156
+ direction: 'asc' | 'desc';
157
+ }>;
158
+ /** One-based page to load (for paginated views). */
159
+ page?: number;
160
+ /** Page size to load. */
161
+ pageSize?: number;
162
+ /** When true, the host loads the full result set (up to its safety cap) instead of paginating. */
163
+ loadAll?: boolean;
164
+ }
165
+ /**
166
+ * A NAVIGATION request to open a record on a (possibly different) entity — the generic payload
167
+ * for {@link IViewRenderer.openRelatedRecordRequested}. Routing lives in the outer app, so this
168
+ * is one of the few signals that legitimately bubbles up. The container forwards it untouched.
169
+ */
170
+ export interface ViewRelatedRecordNavigation {
171
+ /** The entity whose record should be opened. */
172
+ entityName: string;
173
+ /** The record's key — a composite-key string or the raw key value(s). Opaque to the container. */
174
+ recordKey: unknown;
175
+ }
176
+ /**
177
+ * Contract honored by a view-type configuration prop-sheet component.
178
+ *
179
+ * @typeParam TConfig the shape of the configuration object being edited
180
+ */
181
+ export interface IViewPropSheet<TConfig = unknown> {
182
+ /** The entity the configuration applies to. */
183
+ entity: EntityInfo | null;
184
+ /** The current configuration being edited. */
185
+ config: TConfig;
186
+ /** Emitted when the user changes the configuration. */
187
+ configChange: EventEmitter<TConfig>;
188
+ }
189
+ /**
190
+ * Abstract base class for all view-type descriptors.
191
+ *
192
+ * Concrete descriptors extend this and register themselves with the ClassFactory:
193
+ *
194
+ * ```typescript
195
+ * @RegisterClass(BaseViewTypeDescriptor, 'GridViewType')
196
+ * export class GridViewType extends BaseViewTypeDescriptor {
197
+ * readonly Name = 'GridViewType';
198
+ * readonly DisplayName = 'Grid';
199
+ * readonly Icon = 'fa-solid fa-table';
200
+ * readonly RendererComponent = EntityDataGridComponent;
201
+ * IsAvailableFor(): boolean { return true; }
202
+ * }
203
+ * ```
204
+ *
205
+ * The {@link ViewTypeEngine} resolves descriptors by `DriverClass` name using
206
+ * `MJGlobal.Instance.ClassFactory.CreateInstance(BaseViewTypeDescriptor, driverClass)`.
207
+ *
208
+ * NOTE: This class is registered with the ClassFactory itself (via subclasses) and is
209
+ * intentionally NOT decorated — only concrete subclasses carry `@RegisterClass`.
210
+ */
211
+ export declare abstract class BaseViewTypeDescriptor implements IViewTypeDescriptor {
212
+ abstract readonly Name: string;
213
+ abstract readonly DisplayName: string;
214
+ abstract readonly Icon: string;
215
+ abstract readonly RendererComponent: Type<unknown>;
216
+ readonly PropSheetComponent?: Type<unknown>;
217
+ /**
218
+ * Default availability: available for every entity. Override in subclasses that have
219
+ * requirements (e.g. Timeline needs a date field, Map needs geocoding).
220
+ */
221
+ IsAvailableFor(_entity: EntityInfo, _provider?: IMetadataProvider): boolean;
222
+ /**
223
+ * Default: nothing to preload. Override in subclasses whose availability predicate needs
224
+ * async data (e.g. the Cluster view type loading which entities have Entity Documents).
225
+ */
226
+ EnsureAvailabilityData(_provider?: IMetadataProvider): Promise<void>;
227
+ }
228
+ /**
229
+ * Tree-shaking guard. Force-references {@link RegisterClass} so bundlers don't drop the
230
+ * decorator import in builds that only touch this contracts module. Concrete descriptor
231
+ * modules each carry their own `@RegisterClass` and load guard, but this keeps the base
232
+ * module self-consistent.
233
+ */
234
+ export declare function LoadViewTypeContracts(): void;
235
+ //# sourceMappingURL=view-type.contracts.d.ts.map
@@ -0,0 +1 @@
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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"}
@@ -0,0 +1,51 @@
1
+ import { RegisterClass } from '@memberjunction/global';
2
+ /**
3
+ * Abstract base class for all view-type descriptors.
4
+ *
5
+ * Concrete descriptors extend this and register themselves with the ClassFactory:
6
+ *
7
+ * ```typescript
8
+ * @RegisterClass(BaseViewTypeDescriptor, 'GridViewType')
9
+ * export class GridViewType extends BaseViewTypeDescriptor {
10
+ * readonly Name = 'GridViewType';
11
+ * readonly DisplayName = 'Grid';
12
+ * readonly Icon = 'fa-solid fa-table';
13
+ * readonly RendererComponent = EntityDataGridComponent;
14
+ * IsAvailableFor(): boolean { return true; }
15
+ * }
16
+ * ```
17
+ *
18
+ * The {@link ViewTypeEngine} resolves descriptors by `DriverClass` name using
19
+ * `MJGlobal.Instance.ClassFactory.CreateInstance(BaseViewTypeDescriptor, driverClass)`.
20
+ *
21
+ * NOTE: This class is registered with the ClassFactory itself (via subclasses) and is
22
+ * intentionally NOT decorated — only concrete subclasses carry `@RegisterClass`.
23
+ */
24
+ export class BaseViewTypeDescriptor {
25
+ PropSheetComponent;
26
+ /**
27
+ * Default availability: available for every entity. Override in subclasses that have
28
+ * requirements (e.g. Timeline needs a date field, Map needs geocoding).
29
+ */
30
+ IsAvailableFor(_entity, _provider) {
31
+ return true;
32
+ }
33
+ /**
34
+ * Default: nothing to preload. Override in subclasses whose availability predicate needs
35
+ * async data (e.g. the Cluster view type loading which entities have Entity Documents).
36
+ */
37
+ async EnsureAvailabilityData(_provider) {
38
+ // no-op by default
39
+ }
40
+ }
41
+ /**
42
+ * Tree-shaking guard. Force-references {@link RegisterClass} so bundlers don't drop the
43
+ * decorator import in builds that only touch this contracts module. Concrete descriptor
44
+ * modules each carry their own `@RegisterClass` and load guard, but this keeps the base
45
+ * module self-consistent.
46
+ */
47
+ export function LoadViewTypeContracts() {
48
+ // no-op; presence prevents tree-shaking of this module's side-effect-free exports
49
+ void RegisterClass;
50
+ }
51
+ //# sourceMappingURL=view-type.contracts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-type.contracts.js","sourceRoot":"","sources":["../../../src/lib/view-types/view-type.contracts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAmNvD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAgB,sBAAsB;IAKjC,kBAAkB,CAAiB;IAE5C;;;OAGG;IACH,cAAc,CAAC,OAAmB,EAAE,SAA6B;QAC/D,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,sBAAsB,CAAC,SAA6B;QACxD,mBAAmB;IACrB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB;IACnC,kFAAkF;IAClF,KAAK,aAAa,CAAC;AACrB,CAAC","sourcesContent":["import { Type, EventEmitter } from '@angular/core';\nimport { EntityInfo, IMetadataProvider } from '@memberjunction/core';\nimport { RegisterClass } from '@memberjunction/global';\n\n/**\n * View-Type Plugin Architecture — Contracts\n * -----------------------------------------\n * This file defines the framework-level contracts for the entity-viewer's pluggable\n * view-type system. A \"view type\" is a way of rendering a set of entity records — the\n * built-in ones being Grid, Cards, Timeline, and Map, with future additions like\n * Cluster and Tag Cloud.\n *\n * The design separates three concerns:\n * 1. {@link IViewTypeDescriptor} — metadata + availability + the component types to mount.\n * Descriptors are discovered at runtime via the {@link https MemberJunction ClassFactory}\n * keyed off the `DriverClass` stored in the `MJ: View Types` entity.\n * 2. {@link IViewRenderer} — the contract a renderer component honors so the host can\n * feed it data and listen for selection/open/config events uniformly.\n * 3. {@link IViewPropSheet} — the contract a configuration prop-sheet component honors.\n *\n * Because this lives in an Angular package, it is acceptable for the descriptor to reference\n * Angular's `Type<T>` (it points at the concrete component classes to instantiate).\n */\n\n/**\n * Framework-agnostic-ish descriptor for a single view type.\n *\n * A descriptor is the bridge between the `MJ: View Types` metadata row (which stores\n * the `DriverClass` name) and the concrete Angular components that actually render the\n * view and edit its configuration. Descriptors are registered with the ClassFactory via\n * `@RegisterClass(BaseViewTypeDescriptor, '<DriverClass>')` so they can be discovered by\n * name without a hard import from the host.\n */\nexport interface IViewTypeDescriptor {\n /**\n * Stable internal key for the view type — matches the `DriverClass` column on the\n * `MJ: View Types` entity and the `@RegisterClass` registration key\n * (e.g. \"GridViewType\", \"CardsViewType\").\n */\n readonly Name: string;\n\n /** User-facing label shown in the view-mode switcher (e.g. \"Grid\", \"Tag Cloud\"). */\n readonly DisplayName: string;\n\n /** Font Awesome icon class shown in the switcher (e.g. \"fa-solid fa-table\"). */\n readonly Icon: string;\n\n /**\n * The Angular component class that renders this view type. The host instantiates /\n * mounts this component and feeds it data per the {@link IViewRenderer} contract.\n */\n readonly RendererComponent: Type<unknown>;\n\n /**\n * Optional Angular component class for this view type's configuration prop-sheet\n * (honors {@link IViewPropSheet}). Undefined when the view type has no configurable\n * options.\n */\n readonly PropSheetComponent?: Type<unknown>;\n\n /**\n * Predicate that decides whether this view type is available for a given entity.\n * For example, Timeline requires a date field; Map requires geocoding support.\n * Grid and Cards are always available.\n *\n * @param entity the entity the viewer is currently displaying\n * @param provider optional metadata provider (for multi-provider scenarios)\n */\n IsAvailableFor(entity: EntityInfo, provider?: IMetadataProvider): boolean;\n\n /**\n * Optional async hook to load any data this descriptor's {@link IsAvailableFor} predicate\n * depends on (e.g. the Cluster view type needs the set of entities that have an active\n * Entity Document with vectors). The host awaits this once — before computing availability —\n * so the synchronous predicate can read from a now-populated cache. Implementations should\n * be cheap/idempotent (typically `await SomeEngine.Instance.Config(false, ...)`). Omit when\n * availability is computable purely from the {@link EntityInfo} (Grid/Cards/Timeline/Map).\n *\n * @param provider optional metadata provider (for multi-provider scenarios)\n */\n EnsureAvailabilityData?(provider?: IMetadataProvider): Promise<void>;\n}\n\n/**\n * Contract honored by a view renderer component. The host binds these inputs and\n * subscribes to these outputs uniformly across all view types.\n *\n * @typeParam TConfig the shape of this view type's configuration object\n */\nexport interface IViewRenderer<TConfig = unknown> {\n /** The entity whose records are being rendered. */\n entity: EntityInfo | null;\n\n /** The records to render (already loaded/filtered by the host). */\n records: Record<string, unknown>[];\n\n /** Primary-key string of the currently selected record, if any. */\n selectedRecordId: string | null;\n\n /** Active filter text (for highlighting / client-side concerns). */\n filterText: string | null;\n\n /** View-type-specific configuration. */\n config: TConfig;\n\n /**\n * Optional: the metadata provider, handed to plug-ins that issue their own data calls\n * (multi-provider safety). Generic — not specific to any one view type.\n */\n provider?: IMetadataProvider | null;\n\n /**\n * Optional generic data-context the host supplies alongside {@link records}. These are\n * universal \"a view of records\" concepts (counts, current page, loading) — NOT specific to any\n * view type — so a renderer that paginates or shows a total can read them, and one that doesn't\n * simply ignores them. The host owns the actual data fetch; these describe its current result.\n */\n totalRecordCount?: number;\n /** One-based current page of {@link records} when the host is paginating. */\n page?: number;\n /** Page size the host is using. */\n pageSize?: number;\n /** Whether the host is currently (re)loading the record set. */\n isLoading?: boolean;\n\n /** Emitted when a record is selected (single click). Payload is the raw record object. */\n recordSelected: EventEmitter<unknown>;\n\n /**\n * Emitted when THIS entity's record should be opened (double-click / open). Payload is the raw\n * record. This is a NAVIGATION request — the only category of signal that legitimately bubbles\n * to the outer app (routing lives there). Everything else a view does (export, add-to-list,\n * delete, …) is self-contained in the plug-in via Generic dialogs and never bubbles up.\n */\n recordOpened: EventEmitter<unknown>;\n\n /**\n * Optional: NAVIGATION request to open a *related* record on a DIFFERENT entity (e.g. a\n * foreign-key drill-through in a grid cell). Bubbles to the outer app for routing. Generic —\n * the container forwards it without acting on it.\n */\n openRelatedRecordRequested?: EventEmitter<ViewRelatedRecordNavigation>;\n\n /**\n * Optional: NAVIGATION request to create a new record of the current entity (e.g. a grid's\n * \"New\" button) — opening the create form is a routing concern owned by the outer app. Bubbles\n * up; the container forwards it without acting on it.\n */\n createRecordRequested?: EventEmitter<void>;\n\n /**\n * Emitted when the renderer mutates its own opaque {@link config} (e.g. timeline date field,\n * grid columns/sort/widths, map render mode). The container persists the blob verbatim against\n * the active `ViewTypeID` and never inspects it. (Container ↔ plug-in coordination inside the\n * Generic layer — NOT a signal that drives the outer app.)\n */\n configChanged: EventEmitter<TConfig>;\n\n /**\n * Optional: ask the container to (re)load the record set differently. The ONLY data-access\n * channel a plug-in has, fully generic — the container honors the request (sort / page /\n * load-all) without knowing which plug-in asked or why. A grid emits sort/page; a map emits\n * `{ loadAll: true }`; a plug-in happy with the records it's given emits nothing. The container\n * owns the actual `RunView`; no per-view-type branching. (Container ↔ plug-in coordination —\n * NOT a signal that drives the outer app.)\n */\n dataRequest?: EventEmitter<ViewDataRequest>;\n}\n\n/**\n * A generic, view-type-agnostic request from a renderer for the host to change how it loads the\n * record set. Every field is optional; the host applies whatever is present. No field names a\n * specific view type — these are universal data-access concepts.\n */\nexport interface ViewDataRequest {\n /** Desired sort, applied to the host's RunView OrderBy. Empty/omitted clears sorting. */\n sort?: Array<{ field: string; direction: 'asc' | 'desc' }>;\n /** One-based page to load (for paginated views). */\n page?: number;\n /** Page size to load. */\n pageSize?: number;\n /** When true, the host loads the full result set (up to its safety cap) instead of paginating. */\n loadAll?: boolean;\n}\n\n/**\n * A NAVIGATION request to open a record on a (possibly different) entity — the generic payload\n * for {@link IViewRenderer.openRelatedRecordRequested}. Routing lives in the outer app, so this\n * is one of the few signals that legitimately bubbles up. The container forwards it untouched.\n */\nexport interface ViewRelatedRecordNavigation {\n /** The entity whose record should be opened. */\n entityName: string;\n /** The record's key — a composite-key string or the raw key value(s). Opaque to the container. */\n recordKey: unknown;\n}\n\n/**\n * Contract honored by a view-type configuration prop-sheet component.\n *\n * @typeParam TConfig the shape of the configuration object being edited\n */\nexport interface IViewPropSheet<TConfig = unknown> {\n /** The entity the configuration applies to. */\n entity: EntityInfo | null;\n\n /** The current configuration being edited. */\n config: TConfig;\n\n /** Emitted when the user changes the configuration. */\n configChange: EventEmitter<TConfig>;\n}\n\n/**\n * Abstract base class for all view-type descriptors.\n *\n * Concrete descriptors extend this and register themselves with the ClassFactory:\n *\n * ```typescript\n * @RegisterClass(BaseViewTypeDescriptor, 'GridViewType')\n * export class GridViewType extends BaseViewTypeDescriptor {\n * readonly Name = 'GridViewType';\n * readonly DisplayName = 'Grid';\n * readonly Icon = 'fa-solid fa-table';\n * readonly RendererComponent = EntityDataGridComponent;\n * IsAvailableFor(): boolean { return true; }\n * }\n * ```\n *\n * The {@link ViewTypeEngine} resolves descriptors by `DriverClass` name using\n * `MJGlobal.Instance.ClassFactory.CreateInstance(BaseViewTypeDescriptor, driverClass)`.\n *\n * NOTE: This class is registered with the ClassFactory itself (via subclasses) and is\n * intentionally NOT decorated — only concrete subclasses carry `@RegisterClass`.\n */\nexport abstract class BaseViewTypeDescriptor implements IViewTypeDescriptor {\n abstract readonly Name: string;\n abstract readonly DisplayName: string;\n abstract readonly Icon: string;\n abstract readonly RendererComponent: Type<unknown>;\n readonly PropSheetComponent?: Type<unknown>;\n\n /**\n * Default availability: available for every entity. Override in subclasses that have\n * requirements (e.g. Timeline needs a date field, Map needs geocoding).\n */\n IsAvailableFor(_entity: EntityInfo, _provider?: IMetadataProvider): boolean {\n return true;\n }\n\n /**\n * Default: nothing to preload. Override in subclasses whose availability predicate needs\n * async data (e.g. the Cluster view type loading which entities have Entity Documents).\n */\n async EnsureAvailabilityData(_provider?: IMetadataProvider): Promise<void> {\n // no-op by default\n }\n}\n\n/**\n * Tree-shaking guard. Force-references {@link RegisterClass} so bundlers don't drop the\n * decorator import in builds that only touch this contracts module. Concrete descriptor\n * modules each carry their own `@RegisterClass` and load guard, but this keeps the base\n * module self-consistent.\n */\nexport function LoadViewTypeContracts(): void {\n // no-op; presence prevents tree-shaking of this module's side-effect-free exports\n void RegisterClass;\n}\n"]}
@@ -0,0 +1,76 @@
1
+ import { BaseEngine, EntityInfo, IMetadataProvider, UserInfo } from '@memberjunction/core';
2
+ import { MJViewTypeEntity } from '@memberjunction/core-entities';
3
+ import { BaseViewTypeDescriptor, IViewTypeDescriptor } from './view-type.contracts';
4
+ /**
5
+ * ViewTypeEngine
6
+ * --------------
7
+ * Loads the `MJ: View Types` metadata rows (active, ordered by Sequence) and, given an
8
+ * `EntityInfo`, returns the list of view-type descriptors that are AVAILABLE for that
9
+ * entity.
10
+ *
11
+ * "Available" means two things both hold:
12
+ * 1. The view type is active and seeded in `MJ: View Types`.
13
+ * 2. A descriptor is registered with the ClassFactory under the row's `DriverClass`
14
+ * AND that descriptor's `IsAvailableFor(entity)` predicate returns true.
15
+ *
16
+ * This is intentionally Angular-free — it depends only on `@memberjunction/core`,
17
+ * `@memberjunction/global`, and `@memberjunction/core-entities` so it can run anywhere.
18
+ * It extends {@link BaseEngine} for automatic caching + entity-event auto-refresh.
19
+ */
20
+ export declare class ViewTypeEngine extends BaseEngine<ViewTypeEngine> {
21
+ /**
22
+ * Returns the global singleton instance. Do not construct directly.
23
+ */
24
+ static get Instance(): ViewTypeEngine;
25
+ private _viewTypes;
26
+ /**
27
+ * Loads the view-type metadata. Cheap to call repeatedly — a no-op once loaded
28
+ * (unless `forceRefresh`). Callers should `await ViewTypeEngine.Instance.Config(...)`
29
+ * at entry before reading {@link ViewTypes} or calling {@link GetAvailableViewTypes}.
30
+ */
31
+ Config(forceRefresh?: boolean, contextUser?: UserInfo, provider?: IMetadataProvider): Promise<void>;
32
+ /** All active view-type metadata rows, ordered by Sequence. */
33
+ get ViewTypes(): MJViewTypeEntity[];
34
+ /**
35
+ * Resolve a single descriptor instance from a `DriverClass` name via the ClassFactory.
36
+ * Returns null when no descriptor is registered under that key (e.g. Cluster / Tag Cloud,
37
+ * whose renderer plugins are out of scope for this round).
38
+ */
39
+ GetDescriptor(driverClass: string): BaseViewTypeDescriptor | null;
40
+ /**
41
+ * Returns the descriptors that are available for the given entity, in metadata
42
+ * Sequence order. For each active `MJ: View Types` row we resolve its `DriverClass`
43
+ * descriptor from the ClassFactory and keep it only when `IsAvailableFor(entity)` is true.
44
+ *
45
+ * Rows whose `DriverClass` has no registered descriptor (e.g. Cluster, Tag Cloud this
46
+ * round) are silently skipped — so seeding metadata for a not-yet-implemented view type
47
+ * is harmless.
48
+ *
49
+ * @param entity the entity the viewer is displaying
50
+ * @param provider optional metadata provider (multi-provider scenarios)
51
+ */
52
+ GetAvailableViewTypes(entity: EntityInfo, provider?: IMetadataProvider): IViewTypeDescriptor[];
53
+ /**
54
+ * Like {@link GetAvailableViewTypes} but returns each available view type's metadata
55
+ * row paired with its resolved descriptor — so callers that need the `MJ: View Types`
56
+ * row ID (for `UserView.ViewTypeID` persistence and per-view-type config keying) get
57
+ * both without a second lookup.
58
+ *
59
+ * @param entity the entity the viewer is displaying
60
+ * @param provider optional metadata provider (multi-provider scenarios)
61
+ */
62
+ GetAvailableViewTypeRows(entity: EntityInfo, provider?: IMetadataProvider): Array<{
63
+ ViewType: MJViewTypeEntity;
64
+ Descriptor: IViewTypeDescriptor;
65
+ }>;
66
+ /**
67
+ * Awaits each registered descriptor's optional {@link IViewTypeDescriptor.EnsureAvailabilityData}
68
+ * hook so synchronous {@link GetAvailableViewTypeRows} predicates can read from now-populated
69
+ * caches (e.g. the Cluster view type preloading which entities have Entity Documents). Each
70
+ * DriverClass is prepared at most once; a descriptor whose hook throws simply stays unavailable.
71
+ *
72
+ * @param provider optional metadata provider (multi-provider scenarios)
73
+ */
74
+ EnsureAvailabilityData(provider?: IMetadataProvider): Promise<void>;
75
+ }
76
+ //# sourceMappingURL=view-type.engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-type.engine.d.ts","sourceRoot":"","sources":["../../../src/lib/view-types/view-type.engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4B,UAAU,EAAE,iBAAiB,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE/H,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEpF;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,cAAe,SAAQ,UAAU,CAAC,cAAc,CAAC;IAC5D;;OAEG;IACH,WAAkB,QAAQ,IAAI,cAAc,CAE3C;IAED,OAAO,CAAC,UAAU,CAA0B;IAE5C;;;;OAIG;IACU,MAAM,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAchH,+DAA+D;IAC/D,IAAW,SAAS,IAAI,gBAAgB,EAAE,CAEzC;IAED;;;;OAIG;IACI,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI;IAoBxE;;;;;;;;;;;OAWG;IACI,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,mBAAmB,EAAE;IAQrG;;;;;;;;OAQG;IACI,wBAAwB,CAC7B,MAAM,EAAE,UAAU,EAClB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,KAAK,CAAC;QAAE,QAAQ,EAAE,gBAAgB,CAAC;QAAC,UAAU,EAAE,mBAAmB,CAAA;KAAE,CAAC;IAezE;;;;;;;OAOG;IACU,sBAAsB,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;CAmBjF"}
@@ -0,0 +1,138 @@
1
+ import { BaseEngine, Metadata } from '@memberjunction/core';
2
+ import { MJGlobal } from '@memberjunction/global';
3
+ import { BaseViewTypeDescriptor } from './view-type.contracts';
4
+ /**
5
+ * ViewTypeEngine
6
+ * --------------
7
+ * Loads the `MJ: View Types` metadata rows (active, ordered by Sequence) and, given an
8
+ * `EntityInfo`, returns the list of view-type descriptors that are AVAILABLE for that
9
+ * entity.
10
+ *
11
+ * "Available" means two things both hold:
12
+ * 1. The view type is active and seeded in `MJ: View Types`.
13
+ * 2. A descriptor is registered with the ClassFactory under the row's `DriverClass`
14
+ * AND that descriptor's `IsAvailableFor(entity)` predicate returns true.
15
+ *
16
+ * This is intentionally Angular-free — it depends only on `@memberjunction/core`,
17
+ * `@memberjunction/global`, and `@memberjunction/core-entities` so it can run anywhere.
18
+ * It extends {@link BaseEngine} for automatic caching + entity-event auto-refresh.
19
+ */
20
+ export class ViewTypeEngine extends BaseEngine {
21
+ /**
22
+ * Returns the global singleton instance. Do not construct directly.
23
+ */
24
+ static get Instance() {
25
+ return super.getInstance();
26
+ }
27
+ _viewTypes = [];
28
+ /**
29
+ * Loads the view-type metadata. Cheap to call repeatedly — a no-op once loaded
30
+ * (unless `forceRefresh`). Callers should `await ViewTypeEngine.Instance.Config(...)`
31
+ * at entry before reading {@link ViewTypes} or calling {@link GetAvailableViewTypes}.
32
+ */
33
+ async Config(forceRefresh, contextUser, provider) {
34
+ const c = [
35
+ {
36
+ Type: 'entity',
37
+ EntityName: 'MJ: View Types',
38
+ PropertyName: '_viewTypes',
39
+ Filter: 'IsActive = 1',
40
+ OrderBy: 'Sequence ASC',
41
+ CacheLocal: true
42
+ }
43
+ ];
44
+ await this.Load(c, provider ?? Metadata.Provider, forceRefresh, contextUser);
45
+ }
46
+ /** All active view-type metadata rows, ordered by Sequence. */
47
+ get ViewTypes() {
48
+ return this._viewTypes;
49
+ }
50
+ /**
51
+ * Resolve a single descriptor instance from a `DriverClass` name via the ClassFactory.
52
+ * Returns null when no descriptor is registered under that key (e.g. Cluster / Tag Cloud,
53
+ * whose renderer plugins are out of scope for this round).
54
+ */
55
+ GetDescriptor(driverClass) {
56
+ if (!driverClass) {
57
+ return null;
58
+ }
59
+ const descriptor = MJGlobal.Instance.ClassFactory.CreateInstance(BaseViewTypeDescriptor, driverClass);
60
+ // When no concrete descriptor is registered for this DriverClass, the ClassFactory falls
61
+ // back to instantiating the abstract BaseViewTypeDescriptor itself — which yields a useless
62
+ // instance with undefined Name/DisplayName/Icon/RendererComponent (and IsAvailableFor()===true
63
+ // by default). That would render as a blank, clickable item in the switcher. Treat that
64
+ // fallback as "not registered" so unimplemented view types (e.g. a seeded TagCloud row with
65
+ // no TagCloudViewType class) are simply omitted.
66
+ if (!descriptor || !descriptor.RendererComponent || descriptor.Name !== driverClass) {
67
+ return null;
68
+ }
69
+ return descriptor;
70
+ }
71
+ /**
72
+ * Returns the descriptors that are available for the given entity, in metadata
73
+ * Sequence order. For each active `MJ: View Types` row we resolve its `DriverClass`
74
+ * descriptor from the ClassFactory and keep it only when `IsAvailableFor(entity)` is true.
75
+ *
76
+ * Rows whose `DriverClass` has no registered descriptor (e.g. Cluster, Tag Cloud this
77
+ * round) are silently skipped — so seeding metadata for a not-yet-implemented view type
78
+ * is harmless.
79
+ *
80
+ * @param entity the entity the viewer is displaying
81
+ * @param provider optional metadata provider (multi-provider scenarios)
82
+ */
83
+ GetAvailableViewTypes(entity, provider) {
84
+ if (!entity) {
85
+ return [];
86
+ }
87
+ return this.GetAvailableViewTypeRows(entity, provider).map(r => r.Descriptor);
88
+ }
89
+ /**
90
+ * Like {@link GetAvailableViewTypes} but returns each available view type's metadata
91
+ * row paired with its resolved descriptor — so callers that need the `MJ: View Types`
92
+ * row ID (for `UserView.ViewTypeID` persistence and per-view-type config keying) get
93
+ * both without a second lookup.
94
+ *
95
+ * @param entity the entity the viewer is displaying
96
+ * @param provider optional metadata provider (multi-provider scenarios)
97
+ */
98
+ GetAvailableViewTypeRows(entity, provider) {
99
+ if (!entity) {
100
+ return [];
101
+ }
102
+ const result = [];
103
+ for (const row of this._viewTypes) {
104
+ const descriptor = this.GetDescriptor(row.DriverClass);
105
+ if (descriptor && descriptor.IsAvailableFor(entity, provider)) {
106
+ result.push({ ViewType: row, Descriptor: descriptor });
107
+ }
108
+ }
109
+ return result;
110
+ }
111
+ /**
112
+ * Awaits each registered descriptor's optional {@link IViewTypeDescriptor.EnsureAvailabilityData}
113
+ * hook so synchronous {@link GetAvailableViewTypeRows} predicates can read from now-populated
114
+ * caches (e.g. the Cluster view type preloading which entities have Entity Documents). Each
115
+ * DriverClass is prepared at most once; a descriptor whose hook throws simply stays unavailable.
116
+ *
117
+ * @param provider optional metadata provider (multi-provider scenarios)
118
+ */
119
+ async EnsureAvailabilityData(provider) {
120
+ const prepared = new Set();
121
+ await Promise.all(this._viewTypes.map(async (row) => {
122
+ if (prepared.has(row.DriverClass)) {
123
+ return;
124
+ }
125
+ prepared.add(row.DriverClass);
126
+ const descriptor = this.GetDescriptor(row.DriverClass);
127
+ if (descriptor) {
128
+ try {
129
+ await descriptor.EnsureAvailabilityData(provider);
130
+ }
131
+ catch {
132
+ // Availability data failed to load — the descriptor's predicate stays false.
133
+ }
134
+ }
135
+ }));
136
+ }
137
+ }
138
+ //# sourceMappingURL=view-type.engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-type.engine.js","sourceRoot":"","sources":["../../../src/lib/view-types/view-type.engine.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseEngine, BaseEnginePropertyConfig, EntityInfo, IMetadataProvider, Metadata, UserInfo } from '@memberjunction/core';\nimport { MJGlobal } from '@memberjunction/global';\nimport { MJViewTypeEntity } from '@memberjunction/core-entities';\nimport { BaseViewTypeDescriptor, IViewTypeDescriptor } from './view-type.contracts';\n\n/**\n * ViewTypeEngine\n * --------------\n * Loads the `MJ: View Types` metadata rows (active, ordered by Sequence) and, given an\n * `EntityInfo`, returns the list of view-type descriptors that are AVAILABLE for that\n * entity.\n *\n * \"Available\" means two things both hold:\n * 1. The view type is active and seeded in `MJ: View Types`.\n * 2. A descriptor is registered with the ClassFactory under the row's `DriverClass`\n * AND that descriptor's `IsAvailableFor(entity)` predicate returns true.\n *\n * This is intentionally Angular-free — it depends only on `@memberjunction/core`,\n * `@memberjunction/global`, and `@memberjunction/core-entities` so it can run anywhere.\n * It extends {@link BaseEngine} for automatic caching + entity-event auto-refresh.\n */\nexport class ViewTypeEngine extends BaseEngine<ViewTypeEngine> {\n /**\n * Returns the global singleton instance. Do not construct directly.\n */\n public static get Instance(): ViewTypeEngine {\n return super.getInstance<ViewTypeEngine>();\n }\n\n private _viewTypes: MJViewTypeEntity[] = [];\n\n /**\n * Loads the view-type metadata. Cheap to call repeatedly — a no-op once loaded\n * (unless `forceRefresh`). Callers should `await ViewTypeEngine.Instance.Config(...)`\n * at entry before reading {@link ViewTypes} or calling {@link GetAvailableViewTypes}.\n */\n public async Config(forceRefresh?: boolean, contextUser?: UserInfo, provider?: IMetadataProvider): Promise<void> {\n const c: Partial<BaseEnginePropertyConfig>[] = [\n {\n Type: 'entity',\n EntityName: 'MJ: View Types',\n PropertyName: '_viewTypes',\n Filter: 'IsActive = 1',\n OrderBy: 'Sequence ASC',\n CacheLocal: true\n }\n ];\n await this.Load(c, provider ?? Metadata.Provider, forceRefresh, contextUser);\n }\n\n /** All active view-type metadata rows, ordered by Sequence. */\n public get ViewTypes(): MJViewTypeEntity[] {\n return this._viewTypes;\n }\n\n /**\n * Resolve a single descriptor instance from a `DriverClass` name via the ClassFactory.\n * Returns null when no descriptor is registered under that key (e.g. Cluster / Tag Cloud,\n * whose renderer plugins are out of scope for this round).\n */\n public GetDescriptor(driverClass: string): BaseViewTypeDescriptor | null {\n if (!driverClass) {\n return null;\n }\n const descriptor = MJGlobal.Instance.ClassFactory.CreateInstance<BaseViewTypeDescriptor>(\n BaseViewTypeDescriptor,\n driverClass\n );\n // When no concrete descriptor is registered for this DriverClass, the ClassFactory falls\n // back to instantiating the abstract BaseViewTypeDescriptor itself — which yields a useless\n // instance with undefined Name/DisplayName/Icon/RendererComponent (and IsAvailableFor()===true\n // by default). That would render as a blank, clickable item in the switcher. Treat that\n // fallback as \"not registered\" so unimplemented view types (e.g. a seeded TagCloud row with\n // no TagCloudViewType class) are simply omitted.\n if (!descriptor || !descriptor.RendererComponent || descriptor.Name !== driverClass) {\n return null;\n }\n return descriptor;\n }\n\n /**\n * Returns the descriptors that are available for the given entity, in metadata\n * Sequence order. For each active `MJ: View Types` row we resolve its `DriverClass`\n * descriptor from the ClassFactory and keep it only when `IsAvailableFor(entity)` is true.\n *\n * Rows whose `DriverClass` has no registered descriptor (e.g. Cluster, Tag Cloud this\n * round) are silently skipped — so seeding metadata for a not-yet-implemented view type\n * is harmless.\n *\n * @param entity the entity the viewer is displaying\n * @param provider optional metadata provider (multi-provider scenarios)\n */\n public GetAvailableViewTypes(entity: EntityInfo, provider?: IMetadataProvider): IViewTypeDescriptor[] {\n if (!entity) {\n return [];\n }\n\n return this.GetAvailableViewTypeRows(entity, provider).map(r => r.Descriptor);\n }\n\n /**\n * Like {@link GetAvailableViewTypes} but returns each available view type's metadata\n * row paired with its resolved descriptor — so callers that need the `MJ: View Types`\n * row ID (for `UserView.ViewTypeID` persistence and per-view-type config keying) get\n * both without a second lookup.\n *\n * @param entity the entity the viewer is displaying\n * @param provider optional metadata provider (multi-provider scenarios)\n */\n public GetAvailableViewTypeRows(\n entity: EntityInfo,\n provider?: IMetadataProvider\n ): Array<{ ViewType: MJViewTypeEntity; Descriptor: IViewTypeDescriptor }> {\n if (!entity) {\n return [];\n }\n\n const result: Array<{ ViewType: MJViewTypeEntity; Descriptor: IViewTypeDescriptor }> = [];\n for (const row of this._viewTypes) {\n const descriptor = this.GetDescriptor(row.DriverClass);\n if (descriptor && descriptor.IsAvailableFor(entity, provider)) {\n result.push({ ViewType: row, Descriptor: descriptor });\n }\n }\n return result;\n }\n\n /**\n * Awaits each registered descriptor's optional {@link IViewTypeDescriptor.EnsureAvailabilityData}\n * hook so synchronous {@link GetAvailableViewTypeRows} predicates can read from now-populated\n * caches (e.g. the Cluster view type preloading which entities have Entity Documents). Each\n * DriverClass is prepared at most once; a descriptor whose hook throws simply stays unavailable.\n *\n * @param provider optional metadata provider (multi-provider scenarios)\n */\n public async EnsureAvailabilityData(provider?: IMetadataProvider): Promise<void> {\n const prepared = new Set<string>();\n await Promise.all(\n this._viewTypes.map(async row => {\n if (prepared.has(row.DriverClass)) {\n return;\n }\n prepared.add(row.DriverClass);\n const descriptor = this.GetDescriptor(row.DriverClass);\n if (descriptor) {\n try {\n await descriptor.EnsureAvailabilityData(provider);\n } catch {\n // Availability data failed to load — the descriptor's predicate stays false.\n }\n }\n })\n );\n }\n}\n"]}