@memberjunction/ng-entity-card 6.1.4 → 6.2.0-edge.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.
|
@@ -157,15 +157,16 @@ export declare class MJEntityCardComponent extends BaseAngularComponent implemen
|
|
|
157
157
|
get ThumbnailIsImage(): boolean;
|
|
158
158
|
/** Whether the thumbnail is an icon class */
|
|
159
159
|
get ThumbnailIsIcon(): boolean;
|
|
160
|
-
private get
|
|
160
|
+
private get resolvedMaxFields();
|
|
161
161
|
private resolve;
|
|
162
162
|
private resolveEntity;
|
|
163
163
|
private resolveTemplate;
|
|
164
164
|
private resolveTitle;
|
|
165
165
|
private resolveEntityIcon;
|
|
166
166
|
private resolveEntityDisplayName;
|
|
167
|
-
private
|
|
168
|
-
private
|
|
167
|
+
private formatValue;
|
|
168
|
+
private isDateOnlyField;
|
|
169
|
+
private buildEventPayload;
|
|
169
170
|
static ɵfac: i0.ɵɵFactoryDeclaration<MJEntityCardComponent, never>;
|
|
170
171
|
static ɵcmp: i0.ɵɵComponentDeclaration<MJEntityCardComponent, "mj-entity-card", never, { "EntityName": { "alias": "EntityName"; "required": false; }; "Entity": { "alias": "Entity"; "required": false; }; "Record": { "alias": "Record"; "required": false; }; "Variant": { "alias": "Variant"; "required": false; }; "Template": { "alias": "Template"; "required": false; }; "MaxDisplayFields": { "alias": "MaxDisplayFields"; "required": false; }; "ShowAvatar": { "alias": "ShowAvatar"; "required": false; }; "ShowOpenButton": { "alias": "ShowOpenButton"; "required": false; }; "ShowFieldLabels": { "alias": "ShowFieldLabels"; "required": false; }; "CssClass": { "alias": "CssClass"; "required": false; }; }, { "BeforeCardClick": "BeforeCardClick"; "CardClicked": "CardClicked"; "BeforeOpen": "BeforeOpen"; "OpenRequested": "OpenRequested"; }, never, never, true, never>;
|
|
171
172
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-card.component.d.ts","sourceRoot":"","sources":["../../src/lib/entity-card.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACuB,YAAY,EACtC,SAAS,EAAE,aAAa,EAC3B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"entity-card.component.d.ts","sourceRoot":"","sources":["../../src/lib/entity-card.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, inject } from '@angular/core';
|
|
2
|
+
import { IsDateOnlySQLType, FormatDateOnly } from '@memberjunction/core';
|
|
2
3
|
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
3
4
|
import { GenerateCardTemplate, GenerateCardTemplateFromMetadata, CombineTitleFields, FormatKeyAsLabel, } from './entity-card.types';
|
|
4
5
|
import * as i0 from "@angular/core";
|
|
@@ -273,7 +274,7 @@ export class MJEntityCardComponent extends BaseAngularComponent {
|
|
|
273
274
|
// ================================================================
|
|
274
275
|
/** @internal Handle card click with cancelable before/after pattern */
|
|
275
276
|
OnCardClick() {
|
|
276
|
-
const payload = this.
|
|
277
|
+
const payload = this.buildEventPayload();
|
|
277
278
|
const cancelable = { Data: payload, Cancel: false };
|
|
278
279
|
this.BeforeCardClick.emit(cancelable);
|
|
279
280
|
if (!cancelable.Cancel) {
|
|
@@ -283,7 +284,7 @@ export class MJEntityCardComponent extends BaseAngularComponent {
|
|
|
283
284
|
/** @internal Handle open button click with cancelable before/after pattern */
|
|
284
285
|
OnOpenClick(event) {
|
|
285
286
|
event.stopPropagation();
|
|
286
|
-
const payload = this.
|
|
287
|
+
const payload = this.buildEventPayload();
|
|
287
288
|
const cancelable = { Data: payload, Cancel: false };
|
|
288
289
|
this.BeforeOpen.emit(cancelable);
|
|
289
290
|
if (!cancelable.Cancel) {
|
|
@@ -297,7 +298,7 @@ export class MJEntityCardComponent extends BaseAngularComponent {
|
|
|
297
298
|
get DisplayFields() {
|
|
298
299
|
if (!this.EffectiveTemplate)
|
|
299
300
|
return [];
|
|
300
|
-
const max = this.
|
|
301
|
+
const max = this.resolvedMaxFields;
|
|
301
302
|
return this.EffectiveTemplate.DisplayFields.slice(0, max);
|
|
302
303
|
}
|
|
303
304
|
/** Whether to show the avatar based on variant and input */
|
|
@@ -317,7 +318,7 @@ export class MJEntityCardComponent extends BaseAngularComponent {
|
|
|
317
318
|
const value = this.Record[field.Name];
|
|
318
319
|
if (value == null || String(value).trim() === '')
|
|
319
320
|
return '';
|
|
320
|
-
return this.
|
|
321
|
+
return this.formatValue(value, field);
|
|
321
322
|
}
|
|
322
323
|
/** Get the label for a field */
|
|
323
324
|
GetFieldLabel(fieldName) {
|
|
@@ -395,7 +396,7 @@ export class MJEntityCardComponent extends BaseAngularComponent {
|
|
|
395
396
|
// ================================================================
|
|
396
397
|
// Private Methods
|
|
397
398
|
// ================================================================
|
|
398
|
-
get
|
|
399
|
+
get resolvedMaxFields() {
|
|
399
400
|
if (this.MaxDisplayFields != null)
|
|
400
401
|
return this.MaxDisplayFields;
|
|
401
402
|
switch (this.Variant) {
|
|
@@ -433,7 +434,7 @@ export class MJEntityCardComponent extends BaseAngularComponent {
|
|
|
433
434
|
return null;
|
|
434
435
|
const metadataKeys = Object.keys(this.Record);
|
|
435
436
|
if (this.ResolvedEntity) {
|
|
436
|
-
const full = GenerateCardTemplate(this.ResolvedEntity, this.
|
|
437
|
+
const full = GenerateCardTemplate(this.ResolvedEntity, this.resolvedMaxFields);
|
|
437
438
|
// Filter to fields present in the record
|
|
438
439
|
const keySet = new Set(metadataKeys);
|
|
439
440
|
return {
|
|
@@ -446,7 +447,7 @@ export class MJEntityCardComponent extends BaseAngularComponent {
|
|
|
446
447
|
BadgeField: full.BadgeField && keySet.has(full.BadgeField) ? full.BadgeField : null,
|
|
447
448
|
};
|
|
448
449
|
}
|
|
449
|
-
return GenerateCardTemplateFromMetadata(entityName, metadataKeys, this.
|
|
450
|
+
return GenerateCardTemplateFromMetadata(entityName, metadataKeys, this.resolvedMaxFields, this.ProviderToUse);
|
|
450
451
|
}
|
|
451
452
|
resolveTitle() {
|
|
452
453
|
if (!this.EffectiveTemplate || this.EffectiveTemplate.TitleFields.length === 0) {
|
|
@@ -470,7 +471,7 @@ export class MJEntityCardComponent extends BaseAngularComponent {
|
|
|
470
471
|
return metaEntity;
|
|
471
472
|
return this.ResolvedEntity?.Name ?? this.EntityName ?? '';
|
|
472
473
|
}
|
|
473
|
-
|
|
474
|
+
formatValue(value, field) {
|
|
474
475
|
switch (field.Type) {
|
|
475
476
|
case 'number': {
|
|
476
477
|
const num = Number(value);
|
|
@@ -493,7 +494,13 @@ export class MJEntityCardComponent extends BaseAngularComponent {
|
|
|
493
494
|
const d = value instanceof Date ? value : new Date(value);
|
|
494
495
|
if (isNaN(d.getTime()))
|
|
495
496
|
return String(value);
|
|
496
|
-
|
|
497
|
+
const options = { month: 'short', day: 'numeric', year: 'numeric' };
|
|
498
|
+
// A `date` column is a calendar day that arrives as UTC midnight; a local-zone
|
|
499
|
+
// formatter would land on the previous day for every reader west of Greenwich
|
|
500
|
+
// (MJ#4210). A timestamp names an instant and stays in local time.
|
|
501
|
+
if (this.isDateOnlyField(field.Name))
|
|
502
|
+
return FormatDateOnly(d, options);
|
|
503
|
+
return d.toLocaleDateString(undefined, options);
|
|
497
504
|
}
|
|
498
505
|
catch {
|
|
499
506
|
return String(value);
|
|
@@ -505,7 +512,10 @@ export class MJEntityCardComponent extends BaseAngularComponent {
|
|
|
505
512
|
}
|
|
506
513
|
}
|
|
507
514
|
}
|
|
508
|
-
|
|
515
|
+
isDateOnlyField(fieldName) {
|
|
516
|
+
return IsDateOnlySQLType(this.ResolvedEntity?.Fields?.find(f => f.Name === fieldName)?.Type);
|
|
517
|
+
}
|
|
518
|
+
buildEventPayload() {
|
|
509
519
|
return {
|
|
510
520
|
EntityName: this.EntityDisplayName,
|
|
511
521
|
Record: this.Record,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-card.component.js","sourceRoot":"","sources":["../../src/lib/entity-card.component.ts","../../src/lib/entity-card.component.html"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EACZ,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,EAC/E,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAGH,oBAAoB,EAAE,gCAAgC,EACtD,kBAAkB,EAAE,gBAAgB,GACvC,MAAM,qBAAqB,CAAC;;;;ICHb,kCAA6B;IACzB,6BAAqC;IACzC,oBAAM;;;IADG,cAAsB;IAAtB,gEAAsB;;;IAG/B,kCAA2D;IACvD,uBAAgC;IACpC,oBAAM;;;IAFoB,gDAAgC;IACnD,cAAwB;IAAxB,oCAAwB;;;IAG/B,kCAA2D;IACvD,uBAA4B;IAChC,oBAAM;;;IAFoB,gDAAgC;IACnD,cAAoB;IAApB,gCAAoB;;;IAG3B,kCAA2D;IACvD,YACJ;IAAA,oBAAM;;;IAFoB,gDAAgC;IACtD,cACJ;IADI,gDACJ;;;IAhBR,iCAA+B;IAazB,AAJA,AAJA,AAJF,sGAAwC,gFAIM,gFAIrB,gFAIhB;IAKb,oBAAM;;;IAjBF,cAgBC;IAhBD,oJAgBC;;;IAUG,iCAA0B;IAAA,YAAuB;IAAA,oBAAM;;;IAA7B,cAAuB;IAAvB,8CAAuB;;;IAMrD,iCAA4B;IAAA,YAAc;IAAA,oBAAM;;;IAApB,cAAc;IAAd,qCAAc;;;IAK1C,iCAA+B;IAAA,YAAiB;IAAA,oBAAM;;;IAAvB,cAAiB;IAAjB,wCAAiB;;;IAU5B,mCAAgC;IAAA,YAAiB;IAAA,oBAAO;;;IAAxB,cAAiB;IAAjB,oCAAiB;;;IAFzD,kCAAyB;IACrB,2HAAwB;IAGxB,mCAAgC;IAAA,YAAW;IAC/C,AAD+C,oBAAO,EAChD;;;IAJF,cAEC;IAFD,kDAEC;IAC+B,eAAW;IAAX,yBAAW;;;IALnD,4GAAsC;;;;;IAAtC,gFAOC;;;IATT,kCAA2E;IACvE,wGASC;IACL,oBAAM;;;IAXoB,gEAAgD;IACtE,cASC;IATD,mCASC;;;;IAOT,qCAAiF;IAAlD,mMAAS,0BAAmB,KAAC;IACxD,2BAA6C;IACjD,oBAAS;;ADvDjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAQH,MAAM,OAAO,qBAAsB,SAAQ,oBAAoB;IACnD,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExC,mEAAmE;IACnE,cAAc;IACd,mEAAmE;IAEnE;;;;OAIG;IACM,UAAU,GAAkB,IAAI,CAAC;IAE1C;;;OAGG;IACM,MAAM,GAAsB,IAAI,CAAC;IAE1C;;;;;OAKG;IACM,MAAM,GAA4B,EAAE,CAAC;IAE9C;;;OAGG;IACM,OAAO,GAAsB,MAAM,CAAC;IAE7C,mEAAmE;IACnE,uBAAuB;IACvB,mEAAmE;IAEnE;;;OAGG;IACM,QAAQ,GAAwB,IAAI,CAAC;IAE9C;;;;OAIG;IACM,gBAAgB,GAAkB,IAAI,CAAC;IAEhD;;;OAGG;IACM,UAAU,GAAmB,IAAI,CAAC;IAE3C;;;OAGG;IACM,cAAc,GAAG,KAAK,CAAC;IAEhC;;;OAGG;IACM,eAAe,GAAmB,IAAI,CAAC;IAEhD;;OAEG;IACM,QAAQ,GAAG,EAAE,CAAC;IAEvB,mEAAmE;IACnE,wCAAwC;IACxC,mEAAmE;IAEnE;;;OAGG;IACO,eAAe,GAAG,IAAI,YAAY,EAAwC,CAAC;IAErF,yDAAyD;IAC/C,WAAW,GAAG,IAAI,YAAY,EAAmB,CAAC;IAE5D;;;OAGG;IACO,UAAU,GAAG,IAAI,YAAY,EAAwC,CAAC;IAEhF,wEAAwE;IAC9D,aAAa,GAAG,IAAI,YAAY,EAAmB,CAAC;IAE9D,mEAAmE;IACnE,iBAAiB;IACjB,mEAAmE;IAEnE,uEAAuE;IAChE,cAAc,GAAsB,IAAI,CAAC;IAEhD,6DAA6D;IACtD,iBAAiB,GAAwB,IAAI,CAAC;IAErD,gCAAgC;IACzB,KAAK,GAAG,EAAE,CAAC;IAElB,kEAAkE;IAC3D,UAAU,GAAG,EAAE,CAAC;IAEvB,mCAAmC;IAC5B,iBAAiB,GAAG,EAAE,CAAC;IAE9B,mEAAmE;IACnE,YAAY;IACZ,mEAAmE;IAEnE,WAAW,CAAC,OAAsB;QAC9B,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/G,IAAI,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;IACL,CAAC;IAED,mEAAmE;IACnE,iBAAiB;IACjB,mEAAmE;IAEnE,uEAAuE;IAChE,WAAW;QACd,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzC,MAAM,UAAU,GAAyC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAC1F,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YACrB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;IACL,CAAC;IAED,8EAA8E;IACvE,WAAW,CAAC,KAAiB;QAChC,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzC,MAAM,UAAU,GAAyC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAC1F,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACjC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;YACrB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;IAED,mEAAmE;IACnE,qCAAqC;IACrC,mEAAmE;IAEnE,wEAAwE;IACxE,IAAW,aAAa;QACpB,IAAI,CAAC,IAAI,CAAC,iBAAiB;YAAE,OAAO,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,iBAAiB,CAAC;QACnC,OAAO,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9D,CAAC;IAED,4DAA4D;IAC5D,IAAW,gBAAgB;QACvB,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC,UAAU,CAAC;QACpD,OAAO,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC;IACtC,CAAC;IAED,8DAA8D;IAC9D,IAAW,gBAAgB;QACvB,IAAI,IAAI,CAAC,eAAe,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC,eAAe,CAAC;QAC9D,OAAO,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC;IACtC,CAAC;IAED,8CAA8C;IACvC,aAAa,CAAC,KAAuB;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC;QAC5D,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED,gCAAgC;IACzB,aAAa,CAAC,SAAiB;QAClC,OAAO,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,SAAS,CAAC,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACzF,CAAC;IAED,sDAAsD;IACtD,IAAW,QAAQ;QACf,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,GAAG,CAAC;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAChE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACtE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,CAAC;IAED,mEAAmE;IACnE,IAAW,WAAW;QAClB,MAAM,MAAM,GAAG;YACX,yBAAyB,EAAE,0BAA0B;YACrD,0BAA0B,EAAE,uBAAuB;SACtD,CAAC;QACF,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAClC,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,6BAA6B;IAC7B,IAAW,QAAQ;QACf,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,aAAa;YAAE,OAAO,EAAE,CAAC;QACtD,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;QAC5D,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACtC,CAAC;IAED,gCAAgC;IAChC,IAAW,WAAW;QAClB,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,gBAAgB;YAAE,OAAO,EAAE,CAAC;QACzD,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;QAC/D,IAAI,CAAC,IAAI,IAAI;YAAE,OAAO,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,OAAO,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,wCAAwC;IACxC,IAAW,YAAY;QACnB,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,eAAe;YAAE,OAAO,KAAK,CAAC;QAC3D,OAAO,IAAI,CAAC,iBAAiB,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;YACnD,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACzB,OAAO,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QAChD,CAAC,CAAC,CAAC;IACP,CAAC;IAED,0CAA0C;IAC1C,IAAW,cAAc;QACrB,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,eAAe;YAAE,OAAO,EAAE,CAAC;QACxD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC;YACrD,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,EAAE,CAAC;IACd,CAAC;IAED,4CAA4C;IAC5C,IAAW,gBAAgB;QACvB,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC;QAC9B,OAAO,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IAC9F,CAAC;IAED,6CAA6C;IAC7C,IAAW,eAAe;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC;QAC5C,OAAO,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IACtG,CAAC;IAED,mEAAmE;IACnE,kBAAkB;IAClB,mEAAmE;IAEnE,IAAY,iBAAiB;QACzB,IAAI,IAAI,CAAC,gBAAgB,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC,gBAAgB,CAAC;QAChE,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC;YACnB,KAAK,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC;YACzB,KAAK,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;YACtB,KAAK,QAAQ,CAAC,CAAC,OAAO,GAAG,CAAC;QAC9B,CAAC;IACL,CAAC;IAEO,OAAO;QACX,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAC3C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAChD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,wBAAwB,EAAE,CAAC;QACzD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEO,aAAa;QACjB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAClC,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;YAC9B,OAAO,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAEO,eAAe;QACnB,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC;QACxD,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAE7B,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,oBAAoB,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAC/E,yCAAyC;YACzC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;YACrC,OAAO;gBACH,GAAG,IAAI;gBACP,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACxD,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACjE,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI;gBAC/F,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI;gBAC3G,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAChE,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;aACtF,CAAC;QACN,CAAC;QAED,OAAO,gCAAgC,CAAC,UAAU,EAAE,YAAY,EAAE,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAClH,CAAC;IAEO,YAAY;QAChB,IAAI,CAAC,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7E,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC/E,CAAC;IAEO,iBAAiB;QACrB,6BAA6B;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC3C,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9D,mBAAmB;QACnB,IAAI,IAAI,CAAC,cAAc,EAAE,IAAI;YAAE,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;QAC/D,OAAO,mBAAmB,CAAC;IAC/B,CAAC;IAEO,wBAAwB;QAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ;YAAE,OAAO,UAAU,CAAC;QACpE,OAAO,IAAI,CAAC,cAAc,EAAE,IAAI,IAAI,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;IAC9D,CAAC;IAEO,WAAW,CAAC,KAAc,EAAE,KAAuB;QACvD,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACZ,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC1B,IAAI,KAAK,CAAC,GAAG,CAAC;oBAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;gBACrC,IAAI,GAAG,IAAI,OAAO;oBAAE,OAAO,GAAG,CAAC,GAAG,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;gBAC5D,IAAI,GAAG,IAAI,IAAI;oBAAE,OAAO,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;gBACtD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBAC3C,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC5F,OAAO,IAAI,GAAG,CAAC,cAAc,EAAE,EAAE,CAAC;gBACtC,CAAC;gBACD,OAAO,GAAG,CAAC,cAAc,EAAE,CAAC;YAChC,CAAC;YACD,KAAK,SAAS;gBACV,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;YAChC,KAAK,MAAM,CAAC,CAAC,CAAC;gBACV,IAAI,CAAC;oBACD,MAAM,CAAC,GAAG,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAwB,CAAC,CAAC;oBAC7E,IAAI,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;wBAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;oBAC7C,OAAO,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;gBAChG,CAAC;gBAAC,MAAM,CAAC;oBACL,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;gBACzB,CAAC;YACL,CAAC;YACD,OAAO,CAAC,CAAC,CAAC;gBACN,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBACxB,OAAO,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1D,CAAC;QACL,CAAC;IACL,CAAC;IAEO,iBAAiB;QACrB,OAAO;YACH,UAAU,EAAE,IAAI,CAAC,iBAAiB;YAClC,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SACvE,CAAC;IACN,CAAC;iQAlXQ,qBAAqB,yBAArB,qBAAqB;6DAArB,qBAAqB;YChElC,iCAE6B;YAAxB,kGAAS,iBAAa,IAAC;YAGxB,uFAAwB;YA0BhB,AADJ,AAFJ,iCAA2B,aAEG,aACG;YAAA,YAAW;YAAA,oBAAM;YAC1C,uFAAkD;YAGtD,oBAAM;YAGN,uFAAyC;YAKzC,uFAA2C;YAK3C,uFAAgC;YAcpC,oBAAM;YAGN,4FAAsB;YAK1B,oBAAM;;YArED,oEAAuD;YAIxD,cAoBC;YApBD,+CAoBC;YAMgC,eAAW;YAAX,+BAAW;YACpC,cAEC;YAFD,6EAEC;YAIL,cAEC;YAFD,oEAEC;YAGD,cAEC;YAFD,sEAEC;YAGD,cAaC;YAbD,uDAaC;YAIL,cAIC;YAJD,8CAIC;;;iFDLQ,qBAAqB;cAPjC,SAAS;6BACM,IAAI,YACN,gBAAgB,mBAGT,uBAAuB,CAAC,MAAM;;kBAc9C,KAAK;;kBAML,KAAK;;kBAQL,KAAK;;kBAML,KAAK;;kBAUL,KAAK;;kBAOL,KAAK;;kBAML,KAAK;;kBAML,KAAK;;kBAML,KAAK;;kBAKL,KAAK;;kBAUL,MAAM;;kBAGN,MAAM;;kBAMN,MAAM;;kBAGN,MAAM;;kFA9FE,qBAAqB","sourcesContent":["import {\n Component, Input, Output, EventEmitter,\n OnChanges, SimpleChanges, ChangeDetectionStrategy, ChangeDetectorRef, inject\n} from '@angular/core';\nimport { EntityInfo } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport {\n EntityCardVariant, CardTemplate, CardDisplayField,\n CancelableCardEvent, CardRecordEvent,\n GenerateCardTemplate, GenerateCardTemplateFromMetadata,\n CombineTitleFields, FormatKeyAsLabel,\n} from './entity-card.types';\n\n/**\n * `MJEntityCardComponent` — A generic, metadata-driven entity record card.\n *\n * Renders a single entity record as a visual card with automatically prioritized\n * fields based on MemberJunction entity metadata (`IsNameField`, `DefaultInView`,\n * `Sequence`). Designed to work with both full entity records and sparse vector\n * metadata from the Knowledge Hub.\n *\n * ## Variants\n * - `compact` — Tooltip-sized. Title + 2-3 key fields. No avatar.\n * - `card` — Grid card. Avatar + title + subtitle + display fields + badge.\n * - `detail` — Full panel. All available fields with labels.\n *\n * ## Field Resolution\n * 1. **Title**: All `IsNameField` fields combined in Sequence order\n * 2. **Primary fields**: `DefaultInView=true` sorted by Sequence\n * 3. **Fallback**: First 5 non-PK/FK fields when no metadata flags are set\n *\n * ## Events\n * All interactions follow the `BeforeXXX` / `AfterXXX` cancelable event pattern.\n * Set `event.Cancel = true` in a `BeforeXXX` handler to prevent the default action.\n *\n * @example\n * ```html\n * <!-- From entity name + vector metadata -->\n * <mj-entity-card\n * [EntityName]=\"'Members'\"\n * [Record]=\"vectorMetadata\"\n * Variant=\"card\"\n * (CardClicked)=\"onCardClick($event)\">\n * </mj-entity-card>\n *\n * <!-- From EntityInfo + full record -->\n * <mj-entity-card\n * [Entity]=\"entityInfo\"\n * [Record]=\"record.GetAll()\"\n * Variant=\"detail\"\n * [ShowOpenButton]=\"true\"\n * (OpenRequested)=\"onOpen($event)\">\n * </mj-entity-card>\n * ```\n *\n * @selector mj-entity-card\n */\n@Component({\n standalone: true,\n selector: 'mj-entity-card',\n templateUrl: './entity-card.component.html',\n styleUrls: ['./entity-card.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MJEntityCardComponent extends BaseAngularComponent implements OnChanges {\n private cdr = inject(ChangeDetectorRef);\n\n // ================================================================\n // Data Inputs\n // ================================================================\n\n /**\n * The entity name (e.g., \"Members\"). Used to look up `EntityInfo` from\n * the MJ Metadata cache. Mutually exclusive with `Entity` — if both are\n * provided, `Entity` takes precedence.\n */\n @Input() EntityName: string | null = null;\n\n /**\n * Pre-resolved EntityInfo object. Takes precedence over `EntityName`.\n * Use this when you already have the EntityInfo to avoid a redundant lookup.\n */\n @Input() Entity: EntityInfo | null = null;\n\n /**\n * The record data as a plain key-value object. Works with both full entity\n * records (`record.GetAll()`) and sparse vector metadata.\n *\n * Fields not present in the record are gracefully skipped in the display.\n */\n @Input() Record: Record<string, unknown> = {};\n\n /**\n * Visual variant controlling size, layout, and detail level.\n * @default 'card'\n */\n @Input() Variant: EntityCardVariant = 'card';\n\n // ================================================================\n // Customization Inputs\n // ================================================================\n\n /**\n * Custom card template. When provided, overrides the auto-generated template.\n * Use `GenerateCardTemplate()` from the types module to build one programmatically.\n */\n @Input() Template: CardTemplate | null = null;\n\n /**\n * Maximum number of display fields in the card body.\n * Only applies when using the auto-generated template.\n * @default 4 for 'card', 2 for 'compact', unlimited for 'detail'\n */\n @Input() MaxDisplayFields: number | null = null;\n\n /**\n * Whether to show the entity icon/avatar.\n * @default true for 'card', false for 'compact', true for 'detail'\n */\n @Input() ShowAvatar: boolean | null = null;\n\n /**\n * Whether to show the \"Open Record\" button.\n * @default false\n */\n @Input() ShowOpenButton = false;\n\n /**\n * Whether to show field labels alongside values.\n * @default true for 'detail', false for 'compact', true for 'card'\n */\n @Input() ShowFieldLabels: boolean | null = null;\n\n /**\n * CSS class to apply to the outer container for custom styling.\n */\n @Input() CssClass = '';\n\n // ================================================================\n // Events — BeforeXXX / AfterXXX Pattern\n // ================================================================\n\n /**\n * Fires before the card is clicked. Set `Cancel = true` to prevent\n * the `CardClicked` event from firing.\n */\n @Output() BeforeCardClick = new EventEmitter<CancelableCardEvent<CardRecordEvent>>();\n\n /** Fires after the card is clicked (if not canceled). */\n @Output() CardClicked = new EventEmitter<CardRecordEvent>();\n\n /**\n * Fires before the \"Open Record\" button is clicked. Set `Cancel = true`\n * to prevent the `OpenRequested` event from firing.\n */\n @Output() BeforeOpen = new EventEmitter<CancelableCardEvent<CardRecordEvent>>();\n\n /** Fires when the \"Open Record\" button is clicked (if not canceled). */\n @Output() OpenRequested = new EventEmitter<CardRecordEvent>();\n\n // ================================================================\n // Computed State\n // ================================================================\n\n /** The resolved EntityInfo (from Entity input or EntityName lookup) */\n public ResolvedEntity: EntityInfo | null = null;\n\n /** The effective card template (custom or auto-generated) */\n public EffectiveTemplate: CardTemplate | null = null;\n\n /** The combined title string */\n public Title = '';\n\n /** The entity icon class (from vector metadata or entity info) */\n public EntityIcon = '';\n\n /** Resolved entity display name */\n public EntityDisplayName = '';\n\n // ================================================================\n // Lifecycle\n // ================================================================\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['Entity'] || changes['EntityName'] || changes['Record'] || changes['Template'] || changes['Variant']) {\n this.resolve();\n }\n }\n\n // ================================================================\n // Event Handlers\n // ================================================================\n\n /** @internal Handle card click with cancelable before/after pattern */\n public OnCardClick(): void {\n const payload = this.BuildEventPayload();\n const cancelable: CancelableCardEvent<CardRecordEvent> = { Data: payload, Cancel: false };\n this.BeforeCardClick.emit(cancelable);\n if (!cancelable.Cancel) {\n this.CardClicked.emit(payload);\n }\n }\n\n /** @internal Handle open button click with cancelable before/after pattern */\n public OnOpenClick(event: MouseEvent): void {\n event.stopPropagation();\n const payload = this.BuildEventPayload();\n const cancelable: CancelableCardEvent<CardRecordEvent> = { Data: payload, Cancel: false };\n this.BeforeOpen.emit(cancelable);\n if (!cancelable.Cancel) {\n this.OpenRequested.emit(payload);\n }\n }\n\n // ================================================================\n // Display Helpers (used in template)\n // ================================================================\n\n /** Get the resolved display fields, respecting maxFields for variant */\n public get DisplayFields(): CardDisplayField[] {\n if (!this.EffectiveTemplate) return [];\n const max = this.ResolvedMaxFields;\n return this.EffectiveTemplate.DisplayFields.slice(0, max);\n }\n\n /** Whether to show the avatar based on variant and input */\n public get ShouldShowAvatar(): boolean {\n if (this.ShowAvatar != null) return this.ShowAvatar;\n return this.Variant !== 'compact';\n }\n\n /** Whether to show field labels based on variant and input */\n public get ShouldShowLabels(): boolean {\n if (this.ShowFieldLabels != null) return this.ShowFieldLabels;\n return this.Variant !== 'compact';\n }\n\n /** Get formatted value for a display field */\n public GetFieldValue(field: CardDisplayField): string {\n const value = this.Record[field.Name];\n if (value == null || String(value).trim() === '') return '';\n return this.FormatValue(value, field);\n }\n\n /** Get the label for a field */\n public GetFieldLabel(fieldName: string): string {\n return this.EffectiveTemplate?.FieldLabels[fieldName] ?? FormatKeyAsLabel(fieldName);\n }\n\n /** Get initials from the title for avatar fallback */\n public get Initials(): string {\n if (!this.Title || this.Title === 'Unknown') return '?';\n const words = this.Title.split(/\\s+/).filter(w => w.length > 0);\n if (words.length === 1) return words[0].substring(0, 2).toUpperCase();\n return (words[0][0] + words[words.length - 1][0]).toUpperCase();\n }\n\n /** Get a deterministic color for the avatar based on the record */\n public get AvatarColor(): string {\n const colors = [\n 'var(--mj-brand-primary)', 'var(--mj-status-success)',\n 'var(--mj-status-warning)', 'var(--mj-status-info)',\n ];\n let hash = 0;\n const key = this.Title + (this.Record['RecordID'] ?? '');\n for (let i = 0; i < key.length; i++) {\n hash = key.charCodeAt(i) + ((hash << 5) - hash);\n }\n return colors[Math.abs(hash) % colors.length];\n }\n\n /** Get the subtitle value */\n public get Subtitle(): string {\n if (!this.EffectiveTemplate?.SubtitleField) return '';\n const v = this.Record[this.EffectiveTemplate.SubtitleField];\n return v != null ? String(v) : '';\n }\n\n /** Get the description value */\n public get Description(): string {\n if (!this.EffectiveTemplate?.DescriptionField) return '';\n const v = this.Record[this.EffectiveTemplate.DescriptionField];\n if (v == null) return '';\n const s = String(v);\n return s.length > 120 ? s.substring(0, 120) + '...' : s;\n }\n\n /** Check if a thumbnail is available */\n public get HasThumbnail(): boolean {\n if (!this.EffectiveTemplate?.ThumbnailFields) return false;\n return this.EffectiveTemplate.ThumbnailFields.some(f => {\n const v = this.Record[f];\n return v != null && String(v).trim() !== '';\n });\n }\n\n /** Get the thumbnail URL or icon class */\n public get ThumbnailValue(): string {\n if (!this.EffectiveTemplate?.ThumbnailFields) return '';\n for (const f of this.EffectiveTemplate.ThumbnailFields) {\n const v = this.Record[f];\n if (v != null && String(v).trim() !== '') return String(v);\n }\n return '';\n }\n\n /** Whether the thumbnail is an image URL */\n public get ThumbnailIsImage(): boolean {\n const v = this.ThumbnailValue;\n return v.startsWith('http://') || v.startsWith('https://') || v.startsWith('data:image/');\n }\n\n /** Whether the thumbnail is an icon class */\n public get ThumbnailIsIcon(): boolean {\n const v = this.ThumbnailValue.toLowerCase();\n return v.startsWith('fa-') || v.startsWith('fa ') || v.startsWith('fas ') || v.startsWith('far ');\n }\n\n // ================================================================\n // Private Methods\n // ================================================================\n\n private get ResolvedMaxFields(): number {\n if (this.MaxDisplayFields != null) return this.MaxDisplayFields;\n switch (this.Variant) {\n case 'compact': return 3;\n case 'card': return 4;\n case 'detail': return 999;\n }\n }\n\n private resolve(): void {\n this.ResolvedEntity = this.resolveEntity();\n this.EffectiveTemplate = this.resolveTemplate();\n this.Title = this.resolveTitle();\n this.EntityIcon = this.resolveEntityIcon();\n this.EntityDisplayName = this.resolveEntityDisplayName();\n this.cdr.markForCheck();\n }\n\n private resolveEntity(): EntityInfo | null {\n if (this.Entity) return this.Entity;\n if (!this.EntityName) return null;\n try {\n const md = this.ProviderToUse;\n return md.Entities.find(e => e.Name === this.EntityName) ?? null;\n } catch {\n return null;\n }\n }\n\n private resolveTemplate(): CardTemplate | null {\n if (this.Template) return this.Template;\n const entityName = this.Entity?.Name ?? this.EntityName;\n if (!entityName) return null;\n\n const metadataKeys = Object.keys(this.Record);\n if (this.ResolvedEntity) {\n const full = GenerateCardTemplate(this.ResolvedEntity, this.ResolvedMaxFields);\n // Filter to fields present in the record\n const keySet = new Set(metadataKeys);\n return {\n ...full,\n TitleFields: full.TitleFields.filter(f => keySet.has(f)),\n DisplayFields: full.DisplayFields.filter(f => keySet.has(f.Name)),\n SubtitleField: full.SubtitleField && keySet.has(full.SubtitleField) ? full.SubtitleField : null,\n DescriptionField: full.DescriptionField && keySet.has(full.DescriptionField) ? full.DescriptionField : null,\n ThumbnailFields: full.ThumbnailFields.filter(f => keySet.has(f)),\n BadgeField: full.BadgeField && keySet.has(full.BadgeField) ? full.BadgeField : null,\n };\n }\n\n return GenerateCardTemplateFromMetadata(entityName, metadataKeys, this.ResolvedMaxFields, this.ProviderToUse);\n }\n\n private resolveTitle(): string {\n if (!this.EffectiveTemplate || this.EffectiveTemplate.TitleFields.length === 0) {\n return String(this.Record['Name'] ?? this.Record['Title'] ?? 'Unknown');\n }\n return CombineTitleFields(this.Record, this.EffectiveTemplate.TitleFields);\n }\n\n private resolveEntityIcon(): string {\n // From vector metadata first\n const metaIcon = this.Record['EntityIcon'];\n if (metaIcon && typeof metaIcon === 'string') return metaIcon;\n // From entity info\n if (this.ResolvedEntity?.Icon) return this.ResolvedEntity.Icon;\n return 'fa-solid fa-table';\n }\n\n private resolveEntityDisplayName(): string {\n const metaEntity = this.Record['Entity'];\n if (metaEntity && typeof metaEntity === 'string') return metaEntity;\n return this.ResolvedEntity?.Name ?? this.EntityName ?? '';\n }\n\n private FormatValue(value: unknown, field: CardDisplayField): string {\n switch (field.Type) {\n case 'number': {\n const num = Number(value);\n if (isNaN(num)) return String(value);\n if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;\n if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;\n const nameLower = field.Name.toLowerCase();\n if (['amount', 'price', 'cost', 'value', 'revenue', 'total'].some(k => nameLower.includes(k))) {\n return `$${num.toLocaleString()}`;\n }\n return num.toLocaleString();\n }\n case 'boolean':\n return value ? 'Yes' : 'No';\n case 'date': {\n try {\n const d = value instanceof Date ? value : new Date(value as string | number);\n if (isNaN(d.getTime())) return String(value);\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });\n } catch {\n return String(value);\n }\n }\n default: {\n const s = String(value);\n return s.length > 60 ? s.substring(0, 60) + '...' : s;\n }\n }\n }\n\n private BuildEventPayload(): CardRecordEvent {\n return {\n EntityName: this.EntityDisplayName,\n Record: this.Record,\n RecordID: String(this.Record['RecordID'] ?? this.Record['ID'] ?? ''),\n };\n }\n}\n","<div class=\"mj-entity-card\"\n [class]=\"'mj-entity-card--' + Variant + ' ' + CssClass\"\n (click)=\"OnCardClick()\">\n\n <!-- Avatar / Thumbnail -->\n @if (ShouldShowAvatar) {\n <div class=\"mj-ec-avatar-area\">\n @if (HasThumbnail && ThumbnailIsImage) {\n <div class=\"mj-ec-thumbnail\">\n <img [src]=\"ThumbnailValue\" alt=\"\" />\n </div>\n } @else if (HasThumbnail && ThumbnailIsIcon) {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n <i [class]=\"ThumbnailValue\"></i>\n </div>\n } @else if (EntityIcon) {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n <i [class]=\"EntityIcon\"></i>\n </div>\n } @else {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n {{ Initials }}\n </div>\n }\n </div>\n }\n\n <!-- Content -->\n <div class=\"mj-ec-content\">\n <!-- Header: Title + Entity -->\n <div class=\"mj-ec-header\">\n <div class=\"mj-ec-title\">{{ Title }}</div>\n @if (EntityDisplayName && Variant !== 'compact') {\n <div class=\"mj-ec-entity\">{{ EntityDisplayName }}</div>\n }\n </div>\n\n <!-- Subtitle -->\n @if (Subtitle && Variant !== 'compact') {\n <div class=\"mj-ec-subtitle\">{{ Subtitle }}</div>\n }\n\n <!-- Description (detail variant only) -->\n @if (Description && Variant === 'detail') {\n <div class=\"mj-ec-description\">{{ Description }}</div>\n }\n\n <!-- Display Fields -->\n @if (DisplayFields.length > 0) {\n <div class=\"mj-ec-fields\" [class.mj-ec-fields--labeled]=\"ShouldShowLabels\">\n @for (field of DisplayFields; track field.Name) {\n @if (GetFieldValue(field); as value) {\n <div class=\"mj-ec-field\">\n @if (ShouldShowLabels) {\n <span class=\"mj-ec-field-label\">{{ field.Label }}</span>\n }\n <span class=\"mj-ec-field-value\">{{ value }}</span>\n </div>\n }\n }\n </div>\n }\n </div>\n\n <!-- Open Button -->\n @if (ShowOpenButton) {\n <button class=\"mj-ec-open-btn\" (click)=\"OnOpenClick($event)\" title=\"Open record\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n }\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"entity-card.component.js","sourceRoot":"","sources":["../../src/lib/entity-card.component.ts","../../src/lib/entity-card.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, Input, Output, EventEmitter,\n OnChanges, SimpleChanges, ChangeDetectionStrategy, ChangeDetectorRef, inject\n} from '@angular/core';\nimport { EntityInfo, IsDateOnlySQLType, FormatDateOnly } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport {\n EntityCardVariant, CardTemplate, CardDisplayField,\n CancelableCardEvent, CardRecordEvent,\n GenerateCardTemplate, GenerateCardTemplateFromMetadata,\n CombineTitleFields, FormatKeyAsLabel,\n} from './entity-card.types';\n\n/**\n * `MJEntityCardComponent` — A generic, metadata-driven entity record card.\n *\n * Renders a single entity record as a visual card with automatically prioritized\n * fields based on MemberJunction entity metadata (`IsNameField`, `DefaultInView`,\n * `Sequence`). Designed to work with both full entity records and sparse vector\n * metadata from the Knowledge Hub.\n *\n * ## Variants\n * - `compact` — Tooltip-sized. Title + 2-3 key fields. No avatar.\n * - `card` — Grid card. Avatar + title + subtitle + display fields + badge.\n * - `detail` — Full panel. All available fields with labels.\n *\n * ## Field Resolution\n * 1. **Title**: All `IsNameField` fields combined in Sequence order\n * 2. **Primary fields**: `DefaultInView=true` sorted by Sequence\n * 3. **Fallback**: First 5 non-PK/FK fields when no metadata flags are set\n *\n * ## Events\n * All interactions follow the `BeforeXXX` / `AfterXXX` cancelable event pattern.\n * Set `event.Cancel = true` in a `BeforeXXX` handler to prevent the default action.\n *\n * @example\n * ```html\n * <!-- From entity name + vector metadata -->\n * <mj-entity-card\n * [EntityName]=\"'Members'\"\n * [Record]=\"vectorMetadata\"\n * Variant=\"card\"\n * (CardClicked)=\"onCardClick($event)\">\n * </mj-entity-card>\n *\n * <!-- From EntityInfo + full record -->\n * <mj-entity-card\n * [Entity]=\"entityInfo\"\n * [Record]=\"record.GetAll()\"\n * Variant=\"detail\"\n * [ShowOpenButton]=\"true\"\n * (OpenRequested)=\"onOpen($event)\">\n * </mj-entity-card>\n * ```\n *\n * @selector mj-entity-card\n */\n@Component({\n standalone: true,\n selector: 'mj-entity-card',\n templateUrl: './entity-card.component.html',\n styleUrls: ['./entity-card.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MJEntityCardComponent extends BaseAngularComponent implements OnChanges {\n private cdr = inject(ChangeDetectorRef);\n\n // ================================================================\n // Data Inputs\n // ================================================================\n\n /**\n * The entity name (e.g., \"Members\"). Used to look up `EntityInfo` from\n * the MJ Metadata cache. Mutually exclusive with `Entity` — if both are\n * provided, `Entity` takes precedence.\n */\n @Input() EntityName: string | null = null;\n\n /**\n * Pre-resolved EntityInfo object. Takes precedence over `EntityName`.\n * Use this when you already have the EntityInfo to avoid a redundant lookup.\n */\n @Input() Entity: EntityInfo | null = null;\n\n /**\n * The record data as a plain key-value object. Works with both full entity\n * records (`record.GetAll()`) and sparse vector metadata.\n *\n * Fields not present in the record are gracefully skipped in the display.\n */\n @Input() Record: Record<string, unknown> = {};\n\n /**\n * Visual variant controlling size, layout, and detail level.\n * @default 'card'\n */\n @Input() Variant: EntityCardVariant = 'card';\n\n // ================================================================\n // Customization Inputs\n // ================================================================\n\n /**\n * Custom card template. When provided, overrides the auto-generated template.\n * Use `GenerateCardTemplate()` from the types module to build one programmatically.\n */\n @Input() Template: CardTemplate | null = null;\n\n /**\n * Maximum number of display fields in the card body.\n * Only applies when using the auto-generated template.\n * @default 4 for 'card', 2 for 'compact', unlimited for 'detail'\n */\n @Input() MaxDisplayFields: number | null = null;\n\n /**\n * Whether to show the entity icon/avatar.\n * @default true for 'card', false for 'compact', true for 'detail'\n */\n @Input() ShowAvatar: boolean | null = null;\n\n /**\n * Whether to show the \"Open Record\" button.\n * @default false\n */\n @Input() ShowOpenButton = false;\n\n /**\n * Whether to show field labels alongside values.\n * @default true for 'detail', false for 'compact', true for 'card'\n */\n @Input() ShowFieldLabels: boolean | null = null;\n\n /**\n * CSS class to apply to the outer container for custom styling.\n */\n @Input() CssClass = '';\n\n // ================================================================\n // Events — BeforeXXX / AfterXXX Pattern\n // ================================================================\n\n /**\n * Fires before the card is clicked. Set `Cancel = true` to prevent\n * the `CardClicked` event from firing.\n */\n @Output() BeforeCardClick = new EventEmitter<CancelableCardEvent<CardRecordEvent>>();\n\n /** Fires after the card is clicked (if not canceled). */\n @Output() CardClicked = new EventEmitter<CardRecordEvent>();\n\n /**\n * Fires before the \"Open Record\" button is clicked. Set `Cancel = true`\n * to prevent the `OpenRequested` event from firing.\n */\n @Output() BeforeOpen = new EventEmitter<CancelableCardEvent<CardRecordEvent>>();\n\n /** Fires when the \"Open Record\" button is clicked (if not canceled). */\n @Output() OpenRequested = new EventEmitter<CardRecordEvent>();\n\n // ================================================================\n // Computed State\n // ================================================================\n\n /** The resolved EntityInfo (from Entity input or EntityName lookup) */\n public ResolvedEntity: EntityInfo | null = null;\n\n /** The effective card template (custom or auto-generated) */\n public EffectiveTemplate: CardTemplate | null = null;\n\n /** The combined title string */\n public Title = '';\n\n /** The entity icon class (from vector metadata or entity info) */\n public EntityIcon = '';\n\n /** Resolved entity display name */\n public EntityDisplayName = '';\n\n // ================================================================\n // Lifecycle\n // ================================================================\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['Entity'] || changes['EntityName'] || changes['Record'] || changes['Template'] || changes['Variant']) {\n this.resolve();\n }\n }\n\n // ================================================================\n // Event Handlers\n // ================================================================\n\n /** @internal Handle card click with cancelable before/after pattern */\n public OnCardClick(): void {\n const payload = this.buildEventPayload();\n const cancelable: CancelableCardEvent<CardRecordEvent> = { Data: payload, Cancel: false };\n this.BeforeCardClick.emit(cancelable);\n if (!cancelable.Cancel) {\n this.CardClicked.emit(payload);\n }\n }\n\n /** @internal Handle open button click with cancelable before/after pattern */\n public OnOpenClick(event: MouseEvent): void {\n event.stopPropagation();\n const payload = this.buildEventPayload();\n const cancelable: CancelableCardEvent<CardRecordEvent> = { Data: payload, Cancel: false };\n this.BeforeOpen.emit(cancelable);\n if (!cancelable.Cancel) {\n this.OpenRequested.emit(payload);\n }\n }\n\n // ================================================================\n // Display Helpers (used in template)\n // ================================================================\n\n /** Get the resolved display fields, respecting maxFields for variant */\n public get DisplayFields(): CardDisplayField[] {\n if (!this.EffectiveTemplate) return [];\n const max = this.resolvedMaxFields;\n return this.EffectiveTemplate.DisplayFields.slice(0, max);\n }\n\n /** Whether to show the avatar based on variant and input */\n public get ShouldShowAvatar(): boolean {\n if (this.ShowAvatar != null) return this.ShowAvatar;\n return this.Variant !== 'compact';\n }\n\n /** Whether to show field labels based on variant and input */\n public get ShouldShowLabels(): boolean {\n if (this.ShowFieldLabels != null) return this.ShowFieldLabels;\n return this.Variant !== 'compact';\n }\n\n /** Get formatted value for a display field */\n public GetFieldValue(field: CardDisplayField): string {\n const value = this.Record[field.Name];\n if (value == null || String(value).trim() === '') return '';\n return this.formatValue(value, field);\n }\n\n /** Get the label for a field */\n public GetFieldLabel(fieldName: string): string {\n return this.EffectiveTemplate?.FieldLabels[fieldName] ?? FormatKeyAsLabel(fieldName);\n }\n\n /** Get initials from the title for avatar fallback */\n public get Initials(): string {\n if (!this.Title || this.Title === 'Unknown') return '?';\n const words = this.Title.split(/\\s+/).filter(w => w.length > 0);\n if (words.length === 1) return words[0].substring(0, 2).toUpperCase();\n return (words[0][0] + words[words.length - 1][0]).toUpperCase();\n }\n\n /** Get a deterministic color for the avatar based on the record */\n public get AvatarColor(): string {\n const colors = [\n 'var(--mj-brand-primary)', 'var(--mj-status-success)',\n 'var(--mj-status-warning)', 'var(--mj-status-info)',\n ];\n let hash = 0;\n const key = this.Title + (this.Record['RecordID'] ?? '');\n for (let i = 0; i < key.length; i++) {\n hash = key.charCodeAt(i) + ((hash << 5) - hash);\n }\n return colors[Math.abs(hash) % colors.length];\n }\n\n /** Get the subtitle value */\n public get Subtitle(): string {\n if (!this.EffectiveTemplate?.SubtitleField) return '';\n const v = this.Record[this.EffectiveTemplate.SubtitleField];\n return v != null ? String(v) : '';\n }\n\n /** Get the description value */\n public get Description(): string {\n if (!this.EffectiveTemplate?.DescriptionField) return '';\n const v = this.Record[this.EffectiveTemplate.DescriptionField];\n if (v == null) return '';\n const s = String(v);\n return s.length > 120 ? s.substring(0, 120) + '...' : s;\n }\n\n /** Check if a thumbnail is available */\n public get HasThumbnail(): boolean {\n if (!this.EffectiveTemplate?.ThumbnailFields) return false;\n return this.EffectiveTemplate.ThumbnailFields.some(f => {\n const v = this.Record[f];\n return v != null && String(v).trim() !== '';\n });\n }\n\n /** Get the thumbnail URL or icon class */\n public get ThumbnailValue(): string {\n if (!this.EffectiveTemplate?.ThumbnailFields) return '';\n for (const f of this.EffectiveTemplate.ThumbnailFields) {\n const v = this.Record[f];\n if (v != null && String(v).trim() !== '') return String(v);\n }\n return '';\n }\n\n /** Whether the thumbnail is an image URL */\n public get ThumbnailIsImage(): boolean {\n const v = this.ThumbnailValue;\n return v.startsWith('http://') || v.startsWith('https://') || v.startsWith('data:image/');\n }\n\n /** Whether the thumbnail is an icon class */\n public get ThumbnailIsIcon(): boolean {\n const v = this.ThumbnailValue.toLowerCase();\n return v.startsWith('fa-') || v.startsWith('fa ') || v.startsWith('fas ') || v.startsWith('far ');\n }\n\n // ================================================================\n // Private Methods\n // ================================================================\n\n private get resolvedMaxFields(): number {\n if (this.MaxDisplayFields != null) return this.MaxDisplayFields;\n switch (this.Variant) {\n case 'compact': return 3;\n case 'card': return 4;\n case 'detail': return 999;\n }\n }\n\n private resolve(): void {\n this.ResolvedEntity = this.resolveEntity();\n this.EffectiveTemplate = this.resolveTemplate();\n this.Title = this.resolveTitle();\n this.EntityIcon = this.resolveEntityIcon();\n this.EntityDisplayName = this.resolveEntityDisplayName();\n this.cdr.markForCheck();\n }\n\n private resolveEntity(): EntityInfo | null {\n if (this.Entity) return this.Entity;\n if (!this.EntityName) return null;\n try {\n const md = this.ProviderToUse;\n return md.Entities.find(e => e.Name === this.EntityName) ?? null;\n } catch {\n return null;\n }\n }\n\n private resolveTemplate(): CardTemplate | null {\n if (this.Template) return this.Template;\n const entityName = this.Entity?.Name ?? this.EntityName;\n if (!entityName) return null;\n\n const metadataKeys = Object.keys(this.Record);\n if (this.ResolvedEntity) {\n const full = GenerateCardTemplate(this.ResolvedEntity, this.resolvedMaxFields);\n // Filter to fields present in the record\n const keySet = new Set(metadataKeys);\n return {\n ...full,\n TitleFields: full.TitleFields.filter(f => keySet.has(f)),\n DisplayFields: full.DisplayFields.filter(f => keySet.has(f.Name)),\n SubtitleField: full.SubtitleField && keySet.has(full.SubtitleField) ? full.SubtitleField : null,\n DescriptionField: full.DescriptionField && keySet.has(full.DescriptionField) ? full.DescriptionField : null,\n ThumbnailFields: full.ThumbnailFields.filter(f => keySet.has(f)),\n BadgeField: full.BadgeField && keySet.has(full.BadgeField) ? full.BadgeField : null,\n };\n }\n\n return GenerateCardTemplateFromMetadata(entityName, metadataKeys, this.resolvedMaxFields, this.ProviderToUse);\n }\n\n private resolveTitle(): string {\n if (!this.EffectiveTemplate || this.EffectiveTemplate.TitleFields.length === 0) {\n return String(this.Record['Name'] ?? this.Record['Title'] ?? 'Unknown');\n }\n return CombineTitleFields(this.Record, this.EffectiveTemplate.TitleFields);\n }\n\n private resolveEntityIcon(): string {\n // From vector metadata first\n const metaIcon = this.Record['EntityIcon'];\n if (metaIcon && typeof metaIcon === 'string') return metaIcon;\n // From entity info\n if (this.ResolvedEntity?.Icon) return this.ResolvedEntity.Icon;\n return 'fa-solid fa-table';\n }\n\n private resolveEntityDisplayName(): string {\n const metaEntity = this.Record['Entity'];\n if (metaEntity && typeof metaEntity === 'string') return metaEntity;\n return this.ResolvedEntity?.Name ?? this.EntityName ?? '';\n }\n\n private formatValue(value: unknown, field: CardDisplayField): string {\n switch (field.Type) {\n case 'number': {\n const num = Number(value);\n if (isNaN(num)) return String(value);\n if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;\n if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;\n const nameLower = field.Name.toLowerCase();\n if (['amount', 'price', 'cost', 'value', 'revenue', 'total'].some(k => nameLower.includes(k))) {\n return `$${num.toLocaleString()}`;\n }\n return num.toLocaleString();\n }\n case 'boolean':\n return value ? 'Yes' : 'No';\n case 'date': {\n try {\n const d = value instanceof Date ? value : new Date(value as string | number);\n if (isNaN(d.getTime())) return String(value);\n const options: Intl.DateTimeFormatOptions = { month: 'short', day: 'numeric', year: 'numeric' };\n // A `date` column is a calendar day that arrives as UTC midnight; a local-zone\n // formatter would land on the previous day for every reader west of Greenwich\n // (MJ#4210). A timestamp names an instant and stays in local time.\n if (this.isDateOnlyField(field.Name)) return FormatDateOnly(d, options);\n return d.toLocaleDateString(undefined, options);\n } catch {\n return String(value);\n }\n }\n default: {\n const s = String(value);\n return s.length > 60 ? s.substring(0, 60) + '...' : s;\n }\n }\n }\n\n private isDateOnlyField(fieldName: string): boolean {\n return IsDateOnlySQLType(this.ResolvedEntity?.Fields?.find(f => f.Name === fieldName)?.Type);\n }\n\n private buildEventPayload(): CardRecordEvent {\n return {\n EntityName: this.EntityDisplayName,\n Record: this.Record,\n RecordID: String(this.Record['RecordID'] ?? this.Record['ID'] ?? ''),\n };\n }\n}\n","<div class=\"mj-entity-card\"\n [class]=\"'mj-entity-card--' + Variant + ' ' + CssClass\"\n (click)=\"OnCardClick()\">\n\n <!-- Avatar / Thumbnail -->\n @if (ShouldShowAvatar) {\n <div class=\"mj-ec-avatar-area\">\n @if (HasThumbnail && ThumbnailIsImage) {\n <div class=\"mj-ec-thumbnail\">\n <img [src]=\"ThumbnailValue\" alt=\"\" />\n </div>\n } @else if (HasThumbnail && ThumbnailIsIcon) {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n <i [class]=\"ThumbnailValue\"></i>\n </div>\n } @else if (EntityIcon) {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n <i [class]=\"EntityIcon\"></i>\n </div>\n } @else {\n <div class=\"mj-ec-avatar\" [style.background]=\"AvatarColor\">\n {{ Initials }}\n </div>\n }\n </div>\n }\n\n <!-- Content -->\n <div class=\"mj-ec-content\">\n <!-- Header: Title + Entity -->\n <div class=\"mj-ec-header\">\n <div class=\"mj-ec-title\">{{ Title }}</div>\n @if (EntityDisplayName && Variant !== 'compact') {\n <div class=\"mj-ec-entity\">{{ EntityDisplayName }}</div>\n }\n </div>\n\n <!-- Subtitle -->\n @if (Subtitle && Variant !== 'compact') {\n <div class=\"mj-ec-subtitle\">{{ Subtitle }}</div>\n }\n\n <!-- Description (detail variant only) -->\n @if (Description && Variant === 'detail') {\n <div class=\"mj-ec-description\">{{ Description }}</div>\n }\n\n <!-- Display Fields -->\n @if (DisplayFields.length > 0) {\n <div class=\"mj-ec-fields\" [class.mj-ec-fields--labeled]=\"ShouldShowLabels\">\n @for (field of DisplayFields; track field.Name) {\n @if (GetFieldValue(field); as value) {\n <div class=\"mj-ec-field\">\n @if (ShouldShowLabels) {\n <span class=\"mj-ec-field-label\">{{ field.Label }}</span>\n }\n <span class=\"mj-ec-field-value\">{{ value }}</span>\n </div>\n }\n }\n </div>\n }\n </div>\n\n <!-- Open Button -->\n @if (ShowOpenButton) {\n <button class=\"mj-ec-open-btn\" (click)=\"OnOpenClick($event)\" title=\"Open record\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n }\n</div>\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-entity-card",
|
|
3
|
-
"version": "6.1
|
|
3
|
+
"version": "6.2.0-edge.1",
|
|
4
4
|
"description": "MemberJunction: Generic entity record card component with metadata-driven field prioritization",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@angular/compiler": "21.2.22",
|
|
15
15
|
"@angular/compiler-cli": "21.2.22",
|
|
16
|
-
"@memberjunction/ng-test-utils": "6.1
|
|
16
|
+
"@memberjunction/ng-test-utils": "6.2.0-edge.1",
|
|
17
17
|
"vitest": "^4.0.18"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"@angular/core": "^21.2.22"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@memberjunction/core": "6.1
|
|
25
|
-
"@memberjunction/ng-base-types": "6.1
|
|
26
|
-
"@memberjunction/global": "6.1
|
|
24
|
+
"@memberjunction/core": "6.2.0-edge.1",
|
|
25
|
+
"@memberjunction/ng-base-types": "6.2.0-edge.1",
|
|
26
|
+
"@memberjunction/global": "6.2.0-edge.1",
|
|
27
27
|
"tslib": "^2.8.1"
|
|
28
28
|
},
|
|
29
29
|
"repository": {
|