Source

lib/components/card/card.component.ts

/**
 * @module lib.components.card
 * @description Reusable Card UI component module.
 * @summary
 * Exports the `CardComponent`, a standalone Angular component built on Ionic's `IonCard` primitives.
 * The component exposes inputs to control visual style, content and layout and integrates with
 * the application's media service to react to dark-mode changes. See {@link CardComponent}.
 */
import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { Color } from '@ionic/core';
import { IonCard, IonCardContent, IonCardHeader, IonCardTitle , IonCardSubtitle } from '@ionic/angular/standalone';
import { Dynamic } from '../../engine/decorators';
import { NgxComponentDirective, } from '../../engine/NgxComponentDirective';
import { SafeHtml } from '@angular/platform-browser';
import { TranslatePipe } from '@ngx-translate/core';
import { AngularEngineKeys } from '../../engine/constants';

/**
 * @description Reusable, presentational card UI component for use across the application.
 * @summary
 * CardComponent is a standalone Angular component built on Ionic's `IonCard` primitives.
 * It exposes several `@Input()` properties to control appearance and content:
 * `type`, `title`, `body`, `subtitle`, `color`, `separator`, `borders`, `inlineContent`, and `inlineContentPosition`.
 * The component integrates with the application's media service to react to dark-mode changes
 * and toggles the dark-palette CSS class on the host element accordingly.
 *
 * @param {('clear'|'shadow')} type - Visual rendering style for the card; 'clear' (default) or 'shadow'.
 * @param {string} title - Primary title text displayed in the card header.
 * @param {('small'|'default'|'blank')} body - Body size preset controlling padding/typography; defaults to 'default'.
 * @param {string} subtitle - Optional subtitle rendered under the title.
 * @param {Color} color - Ionic color token applied to the card header/title.
 * @param {boolean} separator - When true, renders a divider between header and body.
 * @param {boolean} borders - Controls whether borders are rendered; defaults to true.
 * @param {string|SafeHtml} inlineContent - Inline HTML/SafeHtml to render inside the body.
 * @param {('top'|'bottom')} inlineContentPosition - Where to render `inlineContent` relative to the body; defaults to 'bottom'.
 * @return {void}
 * @class CardComponent
 * @example
 * <ngx-decaf-card
 *   [type]="'shadow'"
 *   [title]="'Account overview'"
 *   [subtitle]="'Summary for the current user'"
 *   [color]="'primary'"
 *   [separator]="true"
 *   [borders]="true"
 *   [inlineContent]="safeHtmlValue"
 *   inlineContentPosition="top"
 * >
 *   <!-- card content here -->
 * </ngx-decaf-card>
 *
 * @mermaid
 * sequenceDiagram
 *   participant App as Consumer
 *   participant Card as CardComponent
 *   participant Media as MediaService
 *   App->>Card: instantiate
 *   Card->>Media: isDarkMode()
 *   Media-->>Card: Observable<boolean> (isDark)
 *   Card->>Card: toggleClass(..., isDark)
 */
@Dynamic()
@Component({
  selector: 'ngx-decaf-card',
  templateUrl: './card.component.html',
  styleUrls: ['./card.component.scss'],
  imports: [TranslatePipe, IonCard, IonCardHeader, IonCardContent, IonCardTitle, IonCardSubtitle],
  standalone: true,
  encapsulation: ViewEncapsulation.None,
})
export class CardComponent extends NgxComponentDirective implements OnInit {

  /**
   * @description Visual rendering style for the card.
   * @summary Controls the card's surface treatment. Use 'clear' for a flat look or 'shadow' to add elevation.
   * @type {'clear'|'shadow'}
   * @default 'clear'
   */
  @Input()
  type: 'clear' | 'shadow' = 'clear';

  /**
   * @description Primary title text for the card header.
   * @summary Rendered prominently at the top of the card; consumers should pass a short, human-friendly string.
   * @type {string}
   * @default ''
   */
  @Input()
  title: string = '';

  /**
   * @description Body size preset for the card.
   * @summary Adjusts padding and typographic scale inside the card body. 'small' reduces spacing, 'blank' hides the body area.
   * @type {'small'|'default'|'blank'}
   * @default 'default'
   */
  @Input()
  body: 'small'| 'default' | 'blank' = 'default';

  /**
   * @description Optional subtitle shown below the title in the header area.
   * @summary Use for short secondary text such as an explanation or contextual note.
   * @type {string}
   * @default ''
   */
  @Input()
  subtitle: string = '';

  /**
   * @description Ionic color token applied to the card.
   * @summary When provided, the color token (for example 'primary' or 'tertiary') is applied to title/header elements where supported.
   * @type {Color}
   * @default ''
   */
  @Input()
  color: Color = '';

  /**
   * @description Toggle to render a visual separator between header and content.
   * @summary When true, a divider line (or equivalent styling) is rendered to separate the header from the body.
   * @type {boolean}
   * @default false
   */
  @Input()
  separator: boolean = false;

  /**
   * @description Controls whether the card renders borders.
   * @summary Set to false to remove borders for inline or transparent card designs. Marked `override` to explicitly shadow the base directive's value.
   * @type {boolean}
   * @default true
   */
  @Input()
  override borders: boolean = true;

  /**
   * @description Inline HTML or SafeHtml content to render inside the card body.
   * @summary Useful for short snippets of rich content provided by the consumer. When passing raw HTML prefer `SafeHtml` to avoid sanitization issues.
   * @type {string|SafeHtml}
   */
  @Input()
  inlineContent?: string | SafeHtml;

  /**
   * @description Position where `inlineContent` is rendered within the body.
   * @summary Pass 'top' to render inline content above the body or 'bottom' to render it below. Defaults to 'bottom'.
   * @type {'top'|'bottom'}
   */
  @Input()
  inlineContentPosition: 'top' | 'bottom'  = 'bottom';

  /**
   * @description Internal component identifier used by the base `NgxComponentDirective`.
   * @summary Read-only-ish string identifying the concrete component class for instrumentation, styling helpers and debug logs.
   * @type {string}
   */
  protected override componentName: string  = 'CardComponent';

 /**
  * @description Angular lifecycle hook: component initialization.
  * @summary
  * ngOnInit sets the component as initialized and subscribes to the application's media service
  * dark-mode observable. On each emission it updates the local isDarkMode flag and calls the
  * media service helper to toggle the dark-palette CSS class on the component host element.
  * The subscription uses the provided mediaService observable and performs side effects only.
  *
  * @return {void}
  */
  ngOnInit(): void {
    this.mediaService.isDarkMode().subscribe(isDark => {
      this.isDarkMode = isDark;
      this.mediaService.toggleClass(
        [this.component],
        AngularEngineKeys.DARK_PALETTE_CLASS,
        this.isDarkMode
      );
    });
    this.initialize();
  }
}