@memberjunction/ng-entity-viewer 6.2.0-edge.0 → 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.
Files changed (67) hide show
  1. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts +22 -0
  2. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.d.ts.map +1 -1
  3. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js +57 -13
  4. package/dist/lib/aggregate-setup-dialog/aggregate-setup-dialog.component.js.map +1 -1
  5. package/dist/lib/entity-cards/entity-cards.component.d.ts +67 -5
  6. package/dist/lib/entity-cards/entity-cards.component.d.ts.map +1 -1
  7. package/dist/lib/entity-cards/entity-cards.component.js +202 -71
  8. package/dist/lib/entity-cards/entity-cards.component.js.map +1 -1
  9. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts +159 -16
  10. package/dist/lib/entity-data-grid/entity-data-grid.component.d.ts.map +1 -1
  11. package/dist/lib/entity-data-grid/entity-data-grid.component.js +446 -147
  12. package/dist/lib/entity-data-grid/entity-data-grid.component.js.map +1 -1
  13. package/dist/lib/entity-data-grid/events/grid-events.js +67 -67
  14. package/dist/lib/entity-data-grid/events/grid-events.js.map +1 -1
  15. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts +112 -8
  16. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.d.ts.map +1 -1
  17. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js +245 -61
  18. package/dist/lib/entity-record-detail-panel/entity-record-detail-panel.component.js.map +1 -1
  19. package/dist/lib/entity-viewer/entity-viewer.component.d.ts +6 -0
  20. package/dist/lib/entity-viewer/entity-viewer.component.d.ts.map +1 -1
  21. package/dist/lib/entity-viewer/entity-viewer.component.js +28 -9
  22. package/dist/lib/entity-viewer/entity-viewer.component.js.map +1 -1
  23. package/dist/lib/pill/pill.component.d.ts +13 -3
  24. package/dist/lib/pill/pill.component.d.ts.map +1 -1
  25. package/dist/lib/pill/pill.component.js +34 -10
  26. package/dist/lib/pill/pill.component.js.map +1 -1
  27. package/dist/lib/quick-save-dialog/quick-save-dialog.component.d.ts +29 -3
  28. package/dist/lib/quick-save-dialog/quick-save-dialog.component.d.ts.map +1 -1
  29. package/dist/lib/quick-save-dialog/quick-save-dialog.component.js +173 -114
  30. package/dist/lib/quick-save-dialog/quick-save-dialog.component.js.map +1 -1
  31. package/dist/lib/recycle-bin/recycle-bin.component.d.ts +4 -0
  32. package/dist/lib/recycle-bin/recycle-bin.component.d.ts.map +1 -1
  33. package/dist/lib/recycle-bin/recycle-bin.component.js +10 -2
  34. package/dist/lib/recycle-bin/recycle-bin.component.js.map +1 -1
  35. package/dist/lib/utils/highlight.util.d.ts +6 -0
  36. package/dist/lib/utils/highlight.util.d.ts.map +1 -1
  37. package/dist/lib/utils/highlight.util.js +23 -11
  38. package/dist/lib/utils/highlight.util.js.map +1 -1
  39. package/dist/lib/utils/record.util.d.ts +10 -0
  40. package/dist/lib/utils/record.util.d.ts.map +1 -1
  41. package/dist/lib/utils/record.util.js +20 -4
  42. package/dist/lib/utils/record.util.js.map +1 -1
  43. package/dist/lib/view-config-panel/view-config-panel.component.d.ts +17 -1
  44. package/dist/lib/view-config-panel/view-config-panel.component.d.ts.map +1 -1
  45. package/dist/lib/view-config-panel/view-config-panel.component.js +39 -6
  46. package/dist/lib/view-config-panel/view-config-panel.component.js.map +1 -1
  47. package/dist/lib/view-selector/view-selector.component.js +2 -2
  48. package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts +15 -3
  49. package/dist/lib/view-types/renderers/cards-view-renderer.component.d.ts.map +1 -1
  50. package/dist/lib/view-types/renderers/cards-view-renderer.component.js +33 -5
  51. package/dist/lib/view-types/renderers/cards-view-renderer.component.js.map +1 -1
  52. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts +70 -2
  53. package/dist/lib/view-types/renderers/grid-view-renderer.component.d.ts.map +1 -1
  54. package/dist/lib/view-types/renderers/grid-view-renderer.component.js +155 -33
  55. package/dist/lib/view-types/renderers/grid-view-renderer.component.js.map +1 -1
  56. package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts +16 -2
  57. package/dist/lib/view-types/renderers/map-view-renderer.component.d.ts.map +1 -1
  58. package/dist/lib/view-types/renderers/map-view-renderer.component.js +37 -9
  59. package/dist/lib/view-types/renderers/map-view-renderer.component.js.map +1 -1
  60. package/dist/lib/view-types/view-type.contracts.d.ts +8 -0
  61. package/dist/lib/view-types/view-type.contracts.d.ts.map +1 -1
  62. package/dist/lib/view-types/view-type.contracts.js.map +1 -1
  63. package/dist/lib/view-workspace/view-workspace.component.d.ts +243 -25
  64. package/dist/lib/view-workspace/view-workspace.component.d.ts.map +1 -1
  65. package/dist/lib/view-workspace/view-workspace.component.js +631 -180
  66. package/dist/lib/view-workspace/view-workspace.component.js.map +1 -1
  67. package/package.json +21 -20
@@ -1,7 +1,7 @@
1
1
  import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core';
2
2
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
3
3
  import { EntityFieldValueListType, RunView, CoerceImageSrc, IsDateOnlySQLType, FormatDateOnly } from '@memberjunction/core';
4
- import { buildCompositeKey, buildPkString, computeFieldsList } from '../utils/record.util';
4
+ import { BuildCompositeKey, BuildPkString, ComputeFieldsList } from '../utils/record.util';
5
5
  import { PillColorUtil } from '../pill/pill.component';
6
6
  import { HighlightUtil } from '../utils/highlight.util';
7
7
  import * as i0 from "@angular/core";
@@ -203,11 +203,14 @@ function EntityCardsComponent_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1
203
203
  } }
204
204
  function EntityCardsComponent_Conditional_2_Conditional_3_Template(rf, ctx) { if (rf & 1) {
205
205
  i0.ɵɵelement(0, "mj-empty-state", 5);
206
+ } if (rf & 2) {
207
+ const ctx_r2 = i0.ɵɵnextContext(2);
208
+ i0.ɵɵproperty("Title", ctx_r2.entity ? "No " + ctx_r2.entity.DisplayNamePlural + " to display" : "No records to display");
206
209
  } }
207
210
  function EntityCardsComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
208
211
  i0.ɵɵelementStart(0, "div", 2);
209
212
  i0.ɵɵrepeaterCreate(1, EntityCardsComponent_Conditional_2_For_2_Template, 15, 9, "div", 4, _forTrack0);
210
- i0.ɵɵconditionalCreate(3, EntityCardsComponent_Conditional_2_Conditional_3_Template, 1, 0, "mj-empty-state", 5);
213
+ i0.ɵɵconditionalCreate(3, EntityCardsComponent_Conditional_2_Conditional_3_Template, 1, 1, "mj-empty-state", 5);
211
214
  i0.ɵɵelementEnd();
212
215
  } if (rf & 2) {
213
216
  const ctx_r2 = i0.ɵɵnextContext();
@@ -260,12 +263,28 @@ export class EntityCardsComponent extends BaseAngularComponent {
260
263
  /**
261
264
  * Custom card template (optional - auto-generated if not provided)
262
265
  */
263
- cardTemplate = null;
266
+ CardTemplate = null;
267
+ /** @deprecated Use {@link CardTemplate}. */
268
+ set cardTemplate(value) {
269
+ this.CardTemplate = value;
270
+ }
271
+ /** @deprecated Use {@link CardTemplate}. */
272
+ get cardTemplate() {
273
+ return this.CardTemplate;
274
+ }
264
275
  /**
265
276
  * Map of record IDs to hidden field names that matched the filter
266
277
  * Used to display an indicator when a match occurred in a non-visible field
267
278
  */
268
- hiddenFieldMatches = new Map();
279
+ HiddenFieldMatches = new Map();
280
+ /** @deprecated Use {@link HiddenFieldMatches}. */
281
+ set hiddenFieldMatches(value) {
282
+ this.HiddenFieldMatches = value;
283
+ }
284
+ /** @deprecated Use {@link HiddenFieldMatches}. */
285
+ get hiddenFieldMatches() {
286
+ return this.HiddenFieldMatches;
287
+ }
269
288
  /**
270
289
  * Current filter text for highlighting matches
271
290
  * Supports SQL-style % wildcards
@@ -285,7 +304,15 @@ export class EntityCardsComponent extends BaseAngularComponent {
285
304
  */
286
305
  recordOpened = new EventEmitter();
287
306
  /** Auto-generated card template */
288
- autoCardTemplate = null;
307
+ AutoCardTemplate = null;
308
+ /** @deprecated Use {@link AutoCardTemplate}. */
309
+ get autoCardTemplate() {
310
+ return this.AutoCardTemplate;
311
+ }
312
+ /** @deprecated Use {@link AutoCardTemplate}. */
313
+ set autoCardTemplate(value) {
314
+ this.AutoCardTemplate = value;
315
+ }
289
316
  /** Internal records when loading standalone */
290
317
  internalRecords = [];
291
318
  /** Track if we're in standalone mode */
@@ -301,11 +328,19 @@ export class EntityCardsComponent extends BaseAngularComponent {
301
328
  * buildPkString() allocations + 4 RegExp-building highlight calls that the
302
329
  * previous per-card-per-CD method bindings incurred.
303
330
  */
304
- cardViewModels = [];
331
+ CardViewModels = [];
332
+ /** @deprecated Use {@link CardViewModels}. */
333
+ get cardViewModels() {
334
+ return this.CardViewModels;
335
+ }
336
+ /** @deprecated Use {@link CardViewModels}. */
337
+ set cardViewModels(value) {
338
+ this.CardViewModels = value;
339
+ }
305
340
  ngOnInit() {
306
341
  this.standaloneMode = this.records === null;
307
- if (this.entity?.Fields && !this.effectiveTemplate) {
308
- this.autoCardTemplate = this.generateCardTemplate(this.entity);
342
+ if (this.entity?.Fields && !this.EffectiveTemplate) {
343
+ this.AutoCardTemplate = this.generateCardTemplate(this.entity);
309
344
  }
310
345
  if (this.standaloneMode && this.entity) {
311
346
  this.loadData();
@@ -314,10 +349,10 @@ export class EntityCardsComponent extends BaseAngularComponent {
314
349
  }
315
350
  ngOnChanges(changes) {
316
351
  if (changes['entity'] && this.entity?.Fields) {
317
- this.autoCardTemplate = this.generateCardTemplate(this.entity);
352
+ this.AutoCardTemplate = this.generateCardTemplate(this.entity);
318
353
  }
319
354
  else if (changes['entity'] && !this.entity) {
320
- this.autoCardTemplate = null;
355
+ this.AutoCardTemplate = null;
321
356
  }
322
357
  if (changes['entity'] && this.standaloneMode && this.entity) {
323
358
  this.loadData();
@@ -374,9 +409,13 @@ export class EntityCardsComponent extends BaseAngularComponent {
374
409
  /**
375
410
  * Get effective records (external or internal)
376
411
  */
377
- get effectiveRecords() {
412
+ get EffectiveRecords() {
378
413
  return this.records ?? this.internalRecords;
379
414
  }
415
+ /** @deprecated Use {@link EffectiveRecords}. */
416
+ get effectiveRecords() {
417
+ return this.EffectiveRecords;
418
+ }
380
419
  /**
381
420
  * Load data in standalone mode
382
421
  */
@@ -389,7 +428,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
389
428
  const result = await rv.RunView({
390
429
  EntityName: this.entity.Name,
391
430
  ResultType: 'simple',
392
- Fields: computeFieldsList(this.entity),
431
+ Fields: ComputeFieldsList(this.entity),
393
432
  MaxRows: this.pageSize
394
433
  });
395
434
  if (result.Success) {
@@ -407,8 +446,12 @@ export class EntityCardsComponent extends BaseAngularComponent {
407
446
  /**
408
447
  * Get the effective card template (custom or auto-generated)
409
448
  */
449
+ get EffectiveTemplate() {
450
+ return this.CardTemplate || this.AutoCardTemplate;
451
+ }
452
+ /** @deprecated Use {@link EffectiveTemplate}. */
410
453
  get effectiveTemplate() {
411
- return this.cardTemplate || this.autoCardTemplate;
454
+ return this.EffectiveTemplate;
412
455
  }
413
456
  // ========================================
414
457
  // TEMPLATE GENERATION
@@ -491,7 +534,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
491
534
  displayFields.push({
492
535
  name: field.Name,
493
536
  type: this.getFieldType(field),
494
- label: this.getFieldLabel(field)
537
+ label: this.GetFieldLabel(field)
495
538
  });
496
539
  }
497
540
  if (displayFields.length >= 2)
@@ -506,7 +549,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
506
549
  displayFields.push({
507
550
  name: field.Name,
508
551
  type: this.getFieldType(field),
509
- label: this.getFieldLabel(field)
552
+ label: this.GetFieldLabel(field)
510
553
  });
511
554
  }
512
555
  }
@@ -547,7 +590,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
547
590
  // ========================================
548
591
  // VALUE FORMATTING
549
592
  // ========================================
550
- getFieldValue(record, fieldName) {
593
+ GetFieldValue(record, fieldName) {
551
594
  if (!fieldName)
552
595
  return '';
553
596
  const value = record[fieldName];
@@ -555,7 +598,11 @@ export class EntityCardsComponent extends BaseAngularComponent {
555
598
  return '';
556
599
  return String(value);
557
600
  }
558
- getNumericValue(record, fieldName) {
601
+ /** @deprecated Use {@link GetFieldValue}. */
602
+ getFieldValue(record, fieldName) {
603
+ return this.GetFieldValue(record, fieldName);
604
+ }
605
+ GetNumericValue(record, fieldName) {
559
606
  const value = record[fieldName];
560
607
  if (value === null || value === undefined)
561
608
  return '-';
@@ -572,7 +619,11 @@ export class EntityCardsComponent extends BaseAngularComponent {
572
619
  }
573
620
  return num.toLocaleString();
574
621
  }
575
- getBooleanValue(record, fieldName) {
622
+ /** @deprecated Use {@link GetNumericValue}. */
623
+ getNumericValue(record, fieldName) {
624
+ return this.GetNumericValue(record, fieldName);
625
+ }
626
+ GetBooleanValue(record, fieldName) {
576
627
  const value = record[fieldName];
577
628
  if (value === null || value === undefined)
578
629
  return false;
@@ -584,15 +635,23 @@ export class EntityCardsComponent extends BaseAngularComponent {
584
635
  return value.toLowerCase() === 'true' || value === '1';
585
636
  return Boolean(value);
586
637
  }
587
- getTextValue(record, fieldName, maxLength = 50) {
588
- const value = this.getFieldValue(record, fieldName);
638
+ /** @deprecated Use {@link GetBooleanValue}. */
639
+ getBooleanValue(record, fieldName) {
640
+ return this.GetBooleanValue(record, fieldName);
641
+ }
642
+ GetTextValue(record, fieldName, maxLength = 50) {
643
+ const value = this.GetFieldValue(record, fieldName);
589
644
  if (!value)
590
645
  return '-';
591
646
  if (value.length <= maxLength)
592
647
  return value;
593
648
  return value.substring(0, maxLength) + '...';
594
649
  }
595
- getDateValue(record, fieldName) {
650
+ /** @deprecated Use {@link GetTextValue}. */
651
+ getTextValue(record, fieldName, maxLength = 50) {
652
+ return this.GetTextValue(record, fieldName, maxLength);
653
+ }
654
+ GetDateValue(record, fieldName) {
596
655
  const value = record[fieldName];
597
656
  if (value === null || value === undefined)
598
657
  return '-';
@@ -612,53 +671,77 @@ export class EntityCardsComponent extends BaseAngularComponent {
612
671
  return String(value);
613
672
  }
614
673
  }
674
+ /** @deprecated Use {@link GetDateValue}. */
675
+ getDateValue(record, fieldName) {
676
+ return this.GetDateValue(record, fieldName);
677
+ }
615
678
  isDateOnlyField(fieldName) {
616
679
  return IsDateOnlySQLType(this.entity?.Fields.find(f => f.Name === fieldName)?.Type);
617
680
  }
618
681
  /**
619
682
  * Get display label for a field using EntityFieldInfo's built-in DisplayNameOrName property
620
683
  */
621
- getFieldLabel(field) {
684
+ GetFieldLabel(field) {
622
685
  return field.DisplayNameOrName;
623
686
  }
687
+ /** @deprecated Use {@link GetFieldLabel}. */
688
+ getFieldLabel(field) {
689
+ return this.GetFieldLabel(field);
690
+ }
624
691
  // ========================================
625
692
  // CARD DISPLAY HELPERS
626
693
  // ========================================
627
- getRecordTrackId(record, index) {
694
+ GetRecordTrackId(record, index) {
628
695
  if (!this.entity)
629
696
  return `record_${index}`;
630
697
  try {
631
- const pk = buildPkString(record, this.entity);
698
+ const pk = BuildPkString(record, this.entity);
632
699
  if (pk && pk.trim().length > 0)
633
700
  return pk;
634
701
  }
635
702
  catch { /* ignore */ }
636
703
  return `record_${index}`;
637
704
  }
638
- isSelected(record) {
705
+ /** @deprecated Use {@link GetRecordTrackId}. */
706
+ getRecordTrackId(record, index) {
707
+ return this.GetRecordTrackId(record, index);
708
+ }
709
+ IsSelected(record) {
639
710
  if (!this.entity)
640
711
  return false;
641
- return buildPkString(record, this.entity) === this.selectedRecordId;
712
+ return BuildPkString(record, this.entity) === this.selectedRecordId;
642
713
  }
643
- onCardClick(record) {
714
+ /** @deprecated Use {@link IsSelected}. */
715
+ isSelected(record) {
716
+ return this.IsSelected(record);
717
+ }
718
+ OnCardClick(record) {
644
719
  if (!this.entity)
645
720
  return;
646
721
  this.recordSelected.emit({
647
722
  record,
648
723
  entity: this.entity,
649
- compositeKey: buildCompositeKey(record, this.entity)
724
+ compositeKey: BuildCompositeKey(record, this.entity)
650
725
  });
651
726
  }
652
- onOpenClick(event, record) {
727
+ /** @deprecated Use {@link OnCardClick}. */
728
+ onCardClick(record) {
729
+ return this.OnCardClick(record);
730
+ }
731
+ OnOpenClick(event, record) {
653
732
  event.stopPropagation();
654
733
  if (!this.entity)
655
734
  return;
656
735
  this.recordOpened.emit({
657
736
  record,
658
737
  entity: this.entity,
659
- compositeKey: buildCompositeKey(record, this.entity)
738
+ compositeKey: BuildCompositeKey(record, this.entity)
660
739
  });
661
740
  }
741
+ /** @deprecated Use {@link OnOpenClick}. */
742
+ onOpenClick(event, record) {
743
+ return this.OnOpenClick(event, record);
744
+ }
662
745
  // ========================================
663
746
  // PRECOMPUTED VIEW-MODELS (perf)
664
747
  // ========================================
@@ -669,8 +752,8 @@ export class EntityCardsComponent extends BaseAngularComponent {
669
752
  * template method bindings avoids re-running it on every Angular CD cycle.
670
753
  */
671
754
  buildCardViewModels() {
672
- const records = this.effectiveRecords;
673
- this.cardViewModels = records.map((record, index) => this.buildCardViewModel(record, index));
755
+ const records = this.EffectiveRecords;
756
+ this.CardViewModels = records.map((record, index) => this.buildCardViewModel(record, index));
674
757
  }
675
758
  /**
676
759
  * Cheaply update the isSelected flag on the EXISTING card view-models in place,
@@ -681,7 +764,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
681
764
  * compare per card with zero allocation or regex work.
682
765
  */
683
766
  updateSelectionFlags() {
684
- for (const vm of this.cardViewModels) {
767
+ for (const vm of this.CardViewModels) {
685
768
  vm.isSelected = vm.pkString.length > 0 && vm.pkString === this.selectedRecordId;
686
769
  }
687
770
  }
@@ -692,9 +775,9 @@ export class EntityCardsComponent extends BaseAngularComponent {
692
775
  buildCardViewModel(record, index) {
693
776
  const pkString = this.computePkString(record);
694
777
  const trackId = pkString && pkString.trim().length > 0 ? pkString : `record_${index}`;
695
- const template = this.effectiveTemplate;
778
+ const template = this.EffectiveTemplate;
696
779
  const subtitleField = template?.subtitleField ?? null;
697
- const subtitleValue = subtitleField ? this.getFieldValue(record, subtitleField) : '';
780
+ const subtitleValue = subtitleField ? this.GetFieldValue(record, subtitleField) : '';
698
781
  const descriptionField = template?.descriptionField ?? null;
699
782
  return {
700
783
  record,
@@ -702,17 +785,17 @@ export class EntityCardsComponent extends BaseAngularComponent {
702
785
  trackId,
703
786
  isSelected: pkString.length > 0 && pkString === this.selectedRecordId,
704
787
  color: this.computeRecordColor(pkString),
705
- thumbnailType: this.getThumbnailType(record),
706
- thumbnailUrl: this.getThumbnailUrl(record),
707
- initials: this.getInitials(record),
708
- highlightedTitle: this.highlightMatch(this.getCombinedTitle(record)),
788
+ thumbnailType: this.GetThumbnailType(record),
789
+ thumbnailUrl: this.GetThumbnailUrl(record),
790
+ initials: this.GetInitials(record),
791
+ highlightedTitle: this.HighlightMatch(this.GetCombinedTitle(record)),
709
792
  subtitleValue,
710
- highlightedSubtitle: this.highlightMatch(subtitleValue),
793
+ highlightedSubtitle: this.HighlightMatch(subtitleValue),
711
794
  highlightedDescription: descriptionField
712
- ? this.highlightMatch(this.getTextValue(record, descriptionField, 100))
795
+ ? this.HighlightMatch(this.GetTextValue(record, descriptionField, 100))
713
796
  : '',
714
797
  displayFields: this.buildDisplayFieldVMs(record, template),
715
- badgeValue: template?.badgeField ? this.getFieldValue(record, template.badgeField) : '',
798
+ badgeValue: template?.badgeField ? this.GetFieldValue(record, template.badgeField) : '',
716
799
  hasHiddenFieldMatch: this.computeHasHiddenFieldMatch(pkString),
717
800
  hiddenMatchFieldName: this.computeHiddenMatchFieldName(pkString)
718
801
  };
@@ -725,10 +808,10 @@ export class EntityCardsComponent extends BaseAngularComponent {
725
808
  return [];
726
809
  return template.displayFields.map(field => ({
727
810
  field,
728
- booleanValue: this.getBooleanValue(record, field.name),
729
- numericValue: this.getNumericValue(record, field.name),
730
- dateValue: this.getDateValue(record, field.name),
731
- highlightedText: this.highlightMatch(this.getTextValue(record, field.name, 40))
811
+ booleanValue: this.GetBooleanValue(record, field.name),
812
+ numericValue: this.GetNumericValue(record, field.name),
813
+ dateValue: this.GetDateValue(record, field.name),
814
+ highlightedText: this.HighlightMatch(this.GetTextValue(record, field.name, 40))
732
815
  }));
733
816
  }
734
817
  /**
@@ -739,7 +822,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
739
822
  if (!this.entity)
740
823
  return '';
741
824
  try {
742
- return buildPkString(record, this.entity);
825
+ return BuildPkString(record, this.entity);
743
826
  }
744
827
  catch {
745
828
  return '';
@@ -764,7 +847,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
764
847
  computeHasHiddenFieldMatch(pkString) {
765
848
  if (!this.entity || !pkString)
766
849
  return false;
767
- return this.hiddenFieldMatches.has(pkString);
850
+ return this.HiddenFieldMatches.has(pkString);
768
851
  }
769
852
  /**
770
853
  * Resolve the display name of the hidden field that matched, from a PK string.
@@ -772,7 +855,7 @@ export class EntityCardsComponent extends BaseAngularComponent {
772
855
  computeHiddenMatchFieldName(pkString) {
773
856
  if (!this.entity || !pkString)
774
857
  return '';
775
- const fieldName = this.hiddenFieldMatches.get(pkString);
858
+ const fieldName = this.HiddenFieldMatches.get(pkString);
776
859
  if (!fieldName)
777
860
  return '';
778
861
  const field = this.entity.Fields.find(f => f.Name === fieldName);
@@ -782,17 +865,21 @@ export class EntityCardsComponent extends BaseAngularComponent {
782
865
  * Get the combined title from all title fields for a record.
783
866
  * Joins multiple IsNameField values with spaces (e.g., "Elizabeth Rodriguez").
784
867
  */
785
- getCombinedTitle(record) {
786
- const template = this.effectiveTemplate;
868
+ GetCombinedTitle(record) {
869
+ const template = this.EffectiveTemplate;
787
870
  if (!template?.titleFields || template.titleFields.length === 0)
788
871
  return '';
789
872
  const parts = template.titleFields
790
- .map(f => this.getFieldValue(record, f))
873
+ .map(f => this.GetFieldValue(record, f))
791
874
  .filter(v => v && v.trim().length > 0);
792
875
  return parts.length > 0 ? parts.join(' ') : '';
793
876
  }
794
- getInitials(record) {
795
- const title = this.getCombinedTitle(record);
877
+ /** @deprecated Use {@link GetCombinedTitle}. */
878
+ getCombinedTitle(record) {
879
+ return this.GetCombinedTitle(record);
880
+ }
881
+ GetInitials(record) {
882
+ const title = this.GetCombinedTitle(record);
796
883
  if (!title)
797
884
  return '?';
798
885
  const words = title.split(/\s+/).filter(w => w.length > 0);
@@ -800,10 +887,14 @@ export class EntityCardsComponent extends BaseAngularComponent {
800
887
  return words[0].substring(0, 2).toUpperCase();
801
888
  return (words[0][0] + words[words.length - 1][0]).toUpperCase();
802
889
  }
890
+ /** @deprecated Use {@link GetInitials}. */
891
+ getInitials(record) {
892
+ return this.GetInitials(record);
893
+ }
803
894
  /**
804
895
  * Get the thumbnail type for a record, with per-record fallback through thumbnailFields
805
896
  */
806
- getThumbnailType(record) {
897
+ GetThumbnailType(record) {
807
898
  const fieldInfo = this.getEffectiveThumbnailField(record);
808
899
  if (!fieldInfo)
809
900
  return 'none';
@@ -819,26 +910,34 @@ export class EntityCardsComponent extends BaseAngularComponent {
819
910
  return 'icon';
820
911
  return 'none';
821
912
  }
913
+ /** @deprecated Use {@link GetThumbnailType}. */
914
+ getThumbnailType(record) {
915
+ return this.GetThumbnailType(record);
916
+ }
822
917
  /**
823
918
  * Get the thumbnail URL/value for a record, with per-record fallback
824
919
  */
825
- getThumbnailUrl(record) {
920
+ GetThumbnailUrl(record) {
826
921
  const fieldInfo = this.getEffectiveThumbnailField(record);
827
922
  if (!fieldInfo)
828
923
  return '';
829
924
  return CoerceImageSrc(fieldInfo.value) || fieldInfo.value;
830
925
  }
926
+ /** @deprecated Use {@link GetThumbnailUrl}. */
927
+ getThumbnailUrl(record) {
928
+ return this.GetThumbnailUrl(record);
929
+ }
831
930
  /**
832
931
  * Find the first thumbnail field that has a value for this record
833
932
  * Returns both the field name and value for type determination
834
933
  */
835
934
  getEffectiveThumbnailField(record) {
836
- const template = this.effectiveTemplate;
935
+ const template = this.EffectiveTemplate;
837
936
  if (!template?.thumbnailFields || template.thumbnailFields.length === 0)
838
937
  return null;
839
938
  // Try each field in priority order until we find one with a value
840
939
  for (const fieldName of template.thumbnailFields) {
841
- const value = this.getFieldValue(record, fieldName);
940
+ const value = this.GetFieldValue(record, fieldName);
842
941
  if (value && value.trim() !== '') {
843
942
  return { fieldName, value };
844
943
  }
@@ -862,10 +961,14 @@ export class EntityCardsComponent extends BaseAngularComponent {
862
961
  return true;
863
962
  return false;
864
963
  }
865
- getRecordColor(record) {
964
+ GetRecordColor(record) {
866
965
  return this.computeRecordColor(this.computePkString(record));
867
966
  }
868
- isEnumField(fieldName) {
967
+ /** @deprecated Use {@link GetRecordColor}. */
968
+ getRecordColor(record) {
969
+ return this.GetRecordColor(record);
970
+ }
971
+ IsEnumField(fieldName) {
869
972
  if (!this.entity)
870
973
  return false;
871
974
  const field = this.entity.Fields.find(f => f.Name === fieldName);
@@ -873,45 +976,69 @@ export class EntityCardsComponent extends BaseAngularComponent {
873
976
  return false;
874
977
  return field.ValueListTypeEnum !== EntityFieldValueListType.None && field.EntityFieldValues.length > 0;
875
978
  }
876
- get subtitleIsPill() {
877
- const template = this.effectiveTemplate;
979
+ /** @deprecated Use {@link IsEnumField}. */
980
+ isEnumField(fieldName) {
981
+ return this.IsEnumField(fieldName);
982
+ }
983
+ get SubtitleIsPill() {
984
+ const template = this.EffectiveTemplate;
878
985
  if (!template?.subtitleField || !this.entity)
879
986
  return false;
880
- return this.isEnumField(template.subtitleField);
987
+ return this.IsEnumField(template.subtitleField);
881
988
  }
882
- getPillColorType(value) {
989
+ /** @deprecated Use {@link SubtitleIsPill}. */
990
+ get subtitleIsPill() {
991
+ return this.SubtitleIsPill;
992
+ }
993
+ GetPillColorType(value) {
883
994
  return PillColorUtil.getColorType(value);
884
995
  }
996
+ /** @deprecated Use {@link GetPillColorType}. */
997
+ getPillColorType(value) {
998
+ return this.GetPillColorType(value);
999
+ }
885
1000
  /**
886
1001
  * Check if a record matched on a hidden field
887
1002
  */
888
- hasHiddenFieldMatch(record) {
1003
+ HasHiddenFieldMatch(record) {
889
1004
  if (!this.entity)
890
1005
  return false;
891
- return this.hiddenFieldMatches.has(buildPkString(record, this.entity));
1006
+ return this.HiddenFieldMatches.has(BuildPkString(record, this.entity));
1007
+ }
1008
+ /** @deprecated Use {@link HasHiddenFieldMatch}. */
1009
+ hasHiddenFieldMatch(record) {
1010
+ return this.HasHiddenFieldMatch(record);
892
1011
  }
893
1012
  /**
894
1013
  * Get the display name of the hidden field that matched
895
1014
  */
896
- getHiddenMatchFieldName(record) {
1015
+ GetHiddenMatchFieldName(record) {
897
1016
  if (!this.entity)
898
1017
  return '';
899
- const fieldName = this.hiddenFieldMatches.get(buildPkString(record, this.entity));
1018
+ const fieldName = this.HiddenFieldMatches.get(BuildPkString(record, this.entity));
900
1019
  if (!fieldName || !this.entity)
901
1020
  return '';
902
1021
  // Look up the field in entity metadata and use DisplayNameOrName
903
1022
  const field = this.entity.Fields.find(f => f.Name === fieldName);
904
1023
  return field ? field.DisplayNameOrName : fieldName;
905
1024
  }
1025
+ /** @deprecated Use {@link GetHiddenMatchFieldName}. */
1026
+ getHiddenMatchFieldName(record) {
1027
+ return this.GetHiddenMatchFieldName(record);
1028
+ }
906
1029
  /**
907
1030
  * Highlight matching text in a string based on the filter text
908
1031
  * Uses HighlightUtil which only highlights if the text actually matches the pattern
909
1032
  */
910
- highlightMatch(text) {
1033
+ HighlightMatch(text) {
911
1034
  return HighlightUtil.highlight(text, this.filterText, false);
912
1035
  }
1036
+ /** @deprecated Use {@link HighlightMatch}. */
1037
+ highlightMatch(text) {
1038
+ return this.HighlightMatch(text);
1039
+ }
913
1040
  static ɵfac = function EntityCardsComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || EntityCardsComponent)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
914
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EntityCardsComponent, selectors: [["mj-entity-cards"]], inputs: { entity: "entity", records: "records", selectedRecordId: "selectedRecordId", cardTemplate: "cardTemplate", hiddenFieldMatches: "hiddenFieldMatches", filterText: "filterText", pageSize: "pageSize" }, outputs: { recordSelected: "recordSelected", recordOpened: "recordOpened" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 3, vars: 1, consts: [[1, "cards-view-wrapper"], [1, "loading-container"], [1, "cards-container"], ["text", "Loading records..."], [1, "data-card", 3, "selected"], ["Variant", "no-results", "Title", "No records to display", 1, "no-results"], [1, "data-card", 3, "click"], [1, "card-header"], [1, "card-thumbnail"], [1, "card-avatar", 3, "background-color"], [1, "card-header-content"], [1, "card-title", 3, "innerHTML"], ["title", "Open Record", 1, "card-open-btn", 3, "click"], [1, "fa-solid", "fa-external-link-alt"], [1, "card-body"], [1, "card-description", 3, "innerHTML"], [1, "card-fields"], [1, "card-footer"], [1, "hidden-match-indicator", 3, "title"], ["alt", "", 3, "src"], [1, "card-avatar"], [3, "value"], [1, "card-subtitle", 3, "innerHTML"], [1, "field-item", 3, "field-boolean", "field-text"], [1, "field-item"], [1, "field-icon"], [1, "field-label"], [1, "field-value"], [1, "field-value", "field-date"], [1, "field-text-value", 3, "innerHTML"], [1, "fa-solid", "fa-magnifying-glass"]], template: function EntityCardsComponent_Template(rf, ctx) { if (rf & 1) {
1041
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EntityCardsComponent, selectors: [["mj-entity-cards"]], inputs: { entity: "entity", records: "records", selectedRecordId: "selectedRecordId", CardTemplate: "CardTemplate", cardTemplate: "cardTemplate", HiddenFieldMatches: "HiddenFieldMatches", hiddenFieldMatches: "hiddenFieldMatches", filterText: "filterText", pageSize: "pageSize" }, outputs: { recordSelected: "recordSelected", recordOpened: "recordOpened" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 3, vars: 1, consts: [[1, "cards-view-wrapper"], [1, "loading-container"], [1, "cards-container"], ["text", "Loading records..."], [1, "data-card", 3, "selected"], ["Variant", "no-results", 1, "no-results", 3, "Title"], [1, "data-card", 3, "click"], [1, "card-header"], [1, "card-thumbnail"], [1, "card-avatar", 3, "background-color"], [1, "card-header-content"], [1, "card-title", 3, "innerHTML"], ["title", "Open Record", 1, "card-open-btn", 3, "click"], [1, "fa-solid", "fa-external-link-alt"], [1, "card-body"], [1, "card-description", 3, "innerHTML"], [1, "card-fields"], [1, "card-footer"], [1, "hidden-match-indicator", 3, "title"], ["alt", "", 3, "src"], [1, "card-avatar"], [3, "value"], [1, "card-subtitle", 3, "innerHTML"], [1, "field-item", 3, "field-boolean", "field-text"], [1, "field-item"], [1, "field-icon"], [1, "field-label"], [1, "field-value"], [1, "field-value", "field-date"], [1, "field-text-value", 3, "innerHTML"], [1, "fa-solid", "fa-magnifying-glass"]], template: function EntityCardsComponent_Template(rf, ctx) { if (rf & 1) {
915
1042
  i0.ɵɵelementStart(0, "div", 0);
916
1043
  i0.ɵɵconditionalCreate(1, EntityCardsComponent_Conditional_1_Template, 2, 0, "div", 1)(2, EntityCardsComponent_Conditional_2_Template, 4, 1, "div", 2);
917
1044
  i0.ɵɵelementEnd();
@@ -922,15 +1049,19 @@ export class EntityCardsComponent extends BaseAngularComponent {
922
1049
  }
923
1050
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(EntityCardsComponent, [{
924
1051
  type: Component,
925
- args: [{ standalone: false, selector: 'mj-entity-cards', encapsulation: ViewEncapsulation.None, template: "<div class=\"cards-view-wrapper\">\n @if (isLoading && effectiveRecords.length === 0) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading records...\"></mj-loading>\n </div>\n } @else {\n <div class=\"cards-container\">\n @for (vm of cardViewModels; track vm.trackId) {\n <div\n class=\"data-card\"\n [class.selected]=\"vm.isSelected\"\n (click)=\"onCardClick(vm.record)\">\n\n <!-- Card Header with Avatar/Icon -->\n <div class=\"card-header\">\n @switch (vm.thumbnailType) {\n @case ('image') {\n <div class=\"card-thumbnail\">\n <img [src]=\"vm.thumbnailUrl\" alt=\"\" />\n </div>\n }\n @case ('icon') {\n <div class=\"card-avatar\" [style.background-color]=\"vm.color\">\n <i [class]=\"vm.thumbnailUrl\"></i>\n </div>\n }\n @default {\n <div class=\"card-avatar\" [style.background-color]=\"vm.color\">\n {{ vm.initials }}\n </div>\n }\n }\n\n <div class=\"card-header-content\">\n <h3 class=\"card-title\" [innerHTML]=\"vm.highlightedTitle\"></h3>\n @if (effectiveTemplate?.subtitleField) {\n @if (subtitleIsPill) {\n <mj-pill [value]=\"vm.subtitleValue\"></mj-pill>\n } @else {\n <span class=\"card-subtitle\" [innerHTML]=\"vm.highlightedSubtitle\"></span>\n }\n }\n </div>\n\n <button class=\"card-open-btn\" (click)=\"onOpenClick($event, vm.record)\" title=\"Open Record\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n </div>\n\n <!-- Card Body -->\n <div class=\"card-body\">\n @if (effectiveTemplate?.descriptionField) {\n <p class=\"card-description\" [innerHTML]=\"vm.highlightedDescription\"></p>\n }\n\n <!-- Display Fields -->\n @if (vm.displayFields.length > 0) {\n <div class=\"card-fields\">\n @for (df of vm.displayFields; track df.field.name) {\n <div class=\"field-item\" [class.field-boolean]=\"df.field.type === 'boolean'\" [class.field-text]=\"df.field.type === 'text'\">\n @switch (df.field.type) {\n @case ('boolean') {\n <i class=\"field-icon\" [class.fa-solid]=\"true\"\n [class.fa-check]=\"df.booleanValue\"\n [class.fa-minus]=\"!df.booleanValue\"\n [class.bool-true]=\"df.booleanValue\"\n [class.bool-false]=\"!df.booleanValue\"></i>\n <span class=\"field-label\">{{ df.field.label }}</span>\n }\n @case ('number') {\n <span class=\"field-value\">{{ df.numericValue }}</span>\n <span class=\"field-label\">{{ df.field.label }}</span>\n }\n @case ('date') {\n <span class=\"field-value field-date\">{{ df.dateValue }}</span>\n <span class=\"field-label\">{{ df.field.label }}</span>\n }\n @default {\n <span class=\"field-label\">{{ df.field.label }}</span>\n <span class=\"field-text-value\" [innerHTML]=\"df.highlightedText\"></span>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Card Footer with Badge -->\n @if (effectiveTemplate?.badgeField) {\n <div class=\"card-footer\">\n <mj-pill [value]=\"vm.badgeValue\"></mj-pill>\n </div>\n }\n\n <!-- Hidden field match indicator -->\n @if (vm.hasHiddenFieldMatch) {\n <div class=\"hidden-match-indicator\" [title]=\"'Matched in: ' + vm.hiddenMatchFieldName\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <span>{{ vm.hiddenMatchFieldName }}</span>\n </div>\n }\n </div>\n }\n\n @if (effectiveRecords.length === 0) {\n <mj-empty-state\n class=\"no-results\"\n Variant=\"no-results\"\n Title=\"No records to display\" />\n }\n </div>\n }\n</div>\n", styles: ["/* Scoped to mj-entity-cards to prevent style leakage with ViewEncapsulation.None */\nmj-entity-cards .cards-view-wrapper {\n height: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\nmj-entity-cards .cards-container {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 20px;\n padding: 4px;\n}\n\nmj-entity-cards .data-card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n/* Hover state for unselected cards */\nmj-entity-cards .data-card:hover:not(.selected) {\n border-color: var(--mj-border-strong);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n transform: translateY(-2px);\n}\nmj-entity-cards .data-card:hover .card-open-btn {\n opacity: 1;\n}\n\n/* Selected state - subtle but clear */\nmj-entity-cards .data-card.selected {\n border-color: var(--mj-brand-primary);\n}\n\nmj-entity-cards .data-card.selected .card-title {\n color: var(--mj-brand-primary-hover);\n}\n\n/* Selected + hover state - same as regular hover */\nmj-entity-cards .data-card.selected:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n transform: translateY(-2px);\n}\n\nmj-entity-cards .card-header {\n display: flex;\n align-items: flex-start;\n padding: 16px;\n gap: 12px;\n}\n\nmj-entity-cards .card-thumbnail {\n width: 48px;\n height: 48px;\n border-radius: 50%;\n overflow: hidden;\n flex-shrink: 0;\n background: var(--mj-bg-surface-sunken);\n}\nmj-entity-cards .card-thumbnail img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n object-position: center;\n display: block;\n}\n\nmj-entity-cards .card-avatar {\n width: 48px;\n height: 48px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n flex-shrink: 0;\n}\n\nmj-entity-cards .card-header-content {\n flex: 1;\n min-width: 0;\n}\n\nmj-entity-cards .card-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\nmj-entity-cards .card-subtitle {\n display: block;\n margin-top: 2px;\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\nmj-entity-cards .card-open-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n opacity: 0;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\nmj-entity-cards .card-open-btn:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-brand-primary);\n}\n\nmj-entity-cards .card-body {\n padding: 0 16px 16px 16px;\n}\n\nmj-entity-cards .card-description {\n margin: 0 0 12px 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n/* Card Display Fields */\nmj-entity-cards .card-fields {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\nmj-entity-cards .field-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 60px;\n}\n\nmj-entity-cards .field-item .field-value {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\nmj-entity-cards .field-item .field-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n/* Boolean fields */\nmj-entity-cards .field-item.field-boolean {\n flex-direction: row;\n align-items: center;\n gap: 6px;\n min-width: auto;\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon {\n font-size: 14px;\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon.bool-true {\n color: var(--mj-status-success);\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon.bool-false {\n color: var(--mj-text-muted);\n}\n\nmj-entity-cards .field-item.field-boolean .field-label {\n font-size: 12px;\n text-transform: none;\n color: var(--mj-text-secondary);\n}\n\n/* Text fields */\nmj-entity-cards .field-item.field-text {\n flex: 1 1 100%;\n max-width: 100%;\n}\n\nmj-entity-cards .field-item .field-text-value {\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n word-break: break-word;\n}\n\n/* Date fields */\nmj-entity-cards .field-item .field-date {\n font-size: 13px;\n font-weight: 500;\n}\n\nmj-entity-cards .card-footer {\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n/* Highlight matches - scoped to work with innerHTML */\nmj-entity-cards .highlight-match {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n border-radius: 2px;\n}\n\n/* Pill spacing when used as subtitle */\nmj-entity-cards .card-header-content mj-pill {\n display: block;\n margin-top: 4px;\n}\n\n/* Hidden field match indicator */\nmj-entity-cards .hidden-match-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: var(--mj-status-warning-bg);\n border-top: 1px solid var(--mj-status-warning-border);\n font-size: 11px;\n color: var(--mj-status-warning);\n}\n\nmj-entity-cards .hidden-match-indicator i {\n font-size: 10px;\n}\n\nmj-entity-cards .hidden-match-indicator span {\n font-weight: 500;\n}\n\n/* Loading state */\nmj-entity-cards .loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 200px;\n padding: 40px;\n}\n\n/* No results state \u2014 span the full grid width */\nmj-entity-cards .no-results {\n grid-column: 1 / -1;\n}\n"] }]
1052
+ args: [{ standalone: false, selector: 'mj-entity-cards', encapsulation: ViewEncapsulation.None, template: "<div class=\"cards-view-wrapper\">\n @if (isLoading && effectiveRecords.length === 0) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading records...\"></mj-loading>\n </div>\n } @else {\n <div class=\"cards-container\">\n @for (vm of cardViewModels; track vm.trackId) {\n <div\n class=\"data-card\"\n [class.selected]=\"vm.isSelected\"\n (click)=\"onCardClick(vm.record)\">\n\n <!-- Card Header with Avatar/Icon -->\n <div class=\"card-header\">\n @switch (vm.thumbnailType) {\n @case ('image') {\n <div class=\"card-thumbnail\">\n <img [src]=\"vm.thumbnailUrl\" alt=\"\" />\n </div>\n }\n @case ('icon') {\n <div class=\"card-avatar\" [style.background-color]=\"vm.color\">\n <i [class]=\"vm.thumbnailUrl\"></i>\n </div>\n }\n @default {\n <div class=\"card-avatar\" [style.background-color]=\"vm.color\">\n {{ vm.initials }}\n </div>\n }\n }\n\n <div class=\"card-header-content\">\n <h3 class=\"card-title\" [innerHTML]=\"vm.highlightedTitle\"></h3>\n @if (effectiveTemplate?.subtitleField) {\n @if (subtitleIsPill) {\n <mj-pill [value]=\"vm.subtitleValue\"></mj-pill>\n } @else {\n <span class=\"card-subtitle\" [innerHTML]=\"vm.highlightedSubtitle\"></span>\n }\n }\n </div>\n\n <button class=\"card-open-btn\" (click)=\"onOpenClick($event, vm.record)\" title=\"Open Record\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n </button>\n </div>\n\n <!-- Card Body -->\n <div class=\"card-body\">\n @if (effectiveTemplate?.descriptionField) {\n <p class=\"card-description\" [innerHTML]=\"vm.highlightedDescription\"></p>\n }\n\n <!-- Display Fields -->\n @if (vm.displayFields.length > 0) {\n <div class=\"card-fields\">\n @for (df of vm.displayFields; track df.field.name) {\n <div class=\"field-item\" [class.field-boolean]=\"df.field.type === 'boolean'\" [class.field-text]=\"df.field.type === 'text'\">\n @switch (df.field.type) {\n @case ('boolean') {\n <i class=\"field-icon\" [class.fa-solid]=\"true\"\n [class.fa-check]=\"df.booleanValue\"\n [class.fa-minus]=\"!df.booleanValue\"\n [class.bool-true]=\"df.booleanValue\"\n [class.bool-false]=\"!df.booleanValue\"></i>\n <span class=\"field-label\">{{ df.field.label }}</span>\n }\n @case ('number') {\n <span class=\"field-value\">{{ df.numericValue }}</span>\n <span class=\"field-label\">{{ df.field.label }}</span>\n }\n @case ('date') {\n <span class=\"field-value field-date\">{{ df.dateValue }}</span>\n <span class=\"field-label\">{{ df.field.label }}</span>\n }\n @default {\n <span class=\"field-label\">{{ df.field.label }}</span>\n <span class=\"field-text-value\" [innerHTML]=\"df.highlightedText\"></span>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Card Footer with Badge -->\n @if (effectiveTemplate?.badgeField) {\n <div class=\"card-footer\">\n <mj-pill [value]=\"vm.badgeValue\"></mj-pill>\n </div>\n }\n\n <!-- Hidden field match indicator -->\n @if (vm.hasHiddenFieldMatch) {\n <div class=\"hidden-match-indicator\" [title]=\"'Matched in: ' + vm.hiddenMatchFieldName\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <span>{{ vm.hiddenMatchFieldName }}</span>\n </div>\n }\n </div>\n }\n\n @if (effectiveRecords.length === 0) {\n <mj-empty-state\n class=\"no-results\"\n Variant=\"no-results\"\n [Title]=\"entity ? 'No ' + entity.DisplayNamePlural + ' to display' : 'No records to display'\" />\n }\n </div>\n }\n</div>\n", styles: ["/* Scoped to mj-entity-cards to prevent style leakage with ViewEncapsulation.None */\nmj-entity-cards .cards-view-wrapper {\n height: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\nmj-entity-cards .cards-container {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 20px;\n padding: 4px;\n}\n\nmj-entity-cards .data-card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n border: 1px solid var(--mj-border-default);\n overflow: hidden;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n/* Hover state for unselected cards */\nmj-entity-cards .data-card:hover:not(.selected) {\n border-color: var(--mj-border-strong);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n transform: translateY(-2px);\n}\nmj-entity-cards .data-card:hover .card-open-btn {\n opacity: 1;\n}\n\n/* Selected state - subtle but clear */\nmj-entity-cards .data-card.selected {\n border-color: var(--mj-brand-primary);\n}\n\nmj-entity-cards .data-card.selected .card-title {\n color: var(--mj-brand-primary-hover);\n}\n\n/* Selected + hover state - same as regular hover */\nmj-entity-cards .data-card.selected:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n transform: translateY(-2px);\n}\n\nmj-entity-cards .card-header {\n display: flex;\n align-items: flex-start;\n padding: 16px;\n gap: 12px;\n}\n\nmj-entity-cards .card-thumbnail {\n width: 48px;\n height: 48px;\n border-radius: 50%;\n overflow: hidden;\n flex-shrink: 0;\n background: var(--mj-bg-surface-sunken);\n}\nmj-entity-cards .card-thumbnail img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n object-position: center;\n display: block;\n}\n\nmj-entity-cards .card-avatar {\n width: 48px;\n height: 48px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-inverse);\n flex-shrink: 0;\n}\n\nmj-entity-cards .card-header-content {\n flex: 1;\n min-width: 0;\n}\n\nmj-entity-cards .card-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n line-height: 1.3;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\nmj-entity-cards .card-subtitle {\n display: block;\n margin-top: 2px;\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\nmj-entity-cards .card-open-btn {\n width: 32px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-muted);\n opacity: 0;\n transition: all 0.15s ease;\n flex-shrink: 0;\n}\nmj-entity-cards .card-open-btn:hover {\n background: var(--mj-bg-surface-card);\n color: var(--mj-brand-primary);\n}\n\nmj-entity-cards .card-body {\n padding: 0 16px 16px 16px;\n}\n\nmj-entity-cards .card-description {\n margin: 0 0 12px 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n/* Card Display Fields */\nmj-entity-cards .card-fields {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n padding-top: 12px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\nmj-entity-cards .field-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 60px;\n}\n\nmj-entity-cards .field-item .field-value {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\nmj-entity-cards .field-item .field-label {\n font-size: 10px;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n/* Boolean fields */\nmj-entity-cards .field-item.field-boolean {\n flex-direction: row;\n align-items: center;\n gap: 6px;\n min-width: auto;\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon {\n font-size: 14px;\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon.bool-true {\n color: var(--mj-status-success);\n}\n\nmj-entity-cards .field-item.field-boolean .field-icon.bool-false {\n color: var(--mj-text-muted);\n}\n\nmj-entity-cards .field-item.field-boolean .field-label {\n font-size: 12px;\n text-transform: none;\n color: var(--mj-text-secondary);\n}\n\n/* Text fields */\nmj-entity-cards .field-item.field-text {\n flex: 1 1 100%;\n max-width: 100%;\n}\n\nmj-entity-cards .field-item .field-text-value {\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.4;\n word-break: break-word;\n}\n\n/* Date fields */\nmj-entity-cards .field-item .field-date {\n font-size: 13px;\n font-weight: 500;\n}\n\nmj-entity-cards .card-footer {\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n/* Highlight matches - scoped to work with innerHTML */\nmj-entity-cards .highlight-match {\n background-color: color-mix(in srgb, var(--mj-status-warning) 40%, var(--mj-bg-surface));\n border-radius: 2px;\n}\n\n/* Pill spacing when used as subtitle */\nmj-entity-cards .card-header-content mj-pill {\n display: block;\n margin-top: 4px;\n}\n\n/* Hidden field match indicator */\nmj-entity-cards .hidden-match-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: var(--mj-status-warning-bg);\n border-top: 1px solid var(--mj-status-warning-border);\n font-size: 11px;\n color: var(--mj-status-warning);\n}\n\nmj-entity-cards .hidden-match-indicator i {\n font-size: 10px;\n}\n\nmj-entity-cards .hidden-match-indicator span {\n font-weight: 500;\n}\n\n/* Loading state */\nmj-entity-cards .loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n min-height: 200px;\n padding: 40px;\n}\n\n/* No results state \u2014 span the full grid width */\nmj-entity-cards .no-results {\n grid-column: 1 / -1;\n}\n"] }]
926
1053
  }], () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], { entity: [{
927
1054
  type: Input
928
1055
  }], records: [{
929
1056
  type: Input
930
1057
  }], selectedRecordId: [{
931
1058
  type: Input
1059
+ }], CardTemplate: [{
1060
+ type: Input
932
1061
  }], cardTemplate: [{
933
1062
  type: Input
1063
+ }], HiddenFieldMatches: [{
1064
+ type: Input
934
1065
  }], hiddenFieldMatches: [{
935
1066
  type: Input
936
1067
  }], filterText: [{