npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@libs-ui/components-card

v0.2.357-4

Published

> Component card có header collapsible, hỗ trợ custom template, two-way binding và điều khiển từ bên ngoài.

Readme

@libs-ui/components-card

Component card có header collapsible, hỗ trợ custom template, two-way binding và điều khiển từ bên ngoài.

Giới thiệu

LibsUiComponentsCardComponent là standalone Angular component hiển thị nội dung trong container gồm header và body, với khả năng collapse/expand. Header hỗ trợ label tùy chỉnh, icon collapse (trái hoặc phải), custom template, và cho phép component cha điều khiển trạng thái thông qua IFunctionControlCard.

Ngoài component chính, lib còn cung cấp LibsUiComponentsCardWrapperComponent — wrapper đơn giản hóa API dành cho các form section có shadow và label chuẩn thiết kế.

Tính năng

  • Header có thể collapse/expand với animation icon
  • Two-way binding trạng thái ẩn/hiện qua model() ([(isHidden)])
  • Custom template cho header (override label)
  • Điều khiển card từ component cha qua IFunctionControlCard
  • Cấu hình styling linh hoạt qua IConfigCard (border, background, icon position, class override)
  • Sub-component CardWrapper với API đơn giản, shadow, border error state
  • Standalone component, ChangeDetectionStrategy.OnPush, Angular Signals

Khi nào sử dụng

  • Hiển thị nội dung trong container có header và body
  • Cần section có thể đóng/mở trong form hoặc dashboard
  • Nhóm các trường thông tin liên quan với tiêu đề rõ ràng
  • Form section cần border error khi có lỗi validation (dùng CardWrapper)

Cài đặt

npm install @libs-ui/components-card

Import

import {
  LibsUiComponentsCardComponent,
  LibsUiComponentsCardWrapperComponent,
  IConfigCard,
  IFunctionControlCard,
} from '@libs-ui/components-card';

@Component({
  standalone: true,
  imports: [LibsUiComponentsCardComponent, LibsUiComponentsCardWrapperComponent],
})
export class YourComponent {}

Ví dụ sử dụng

1. Card cơ bản

<libs_ui-components-card [label]="{ labelLeft: 'Thông tin cơ bản' }">
  <p class="text-gray-700">Nội dung card hiển thị ở đây.</p>
</libs_ui-components-card>
import { Component } from '@angular/core';
import { LibsUiComponentsCardComponent } from '@libs-ui/components-card';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsCardComponent],
  templateUrl: './example.component.html',
})
export class ExampleComponent {}

2. Card có nút collapse (two-way binding)

<libs_ui-components-card
  [label]="{ labelLeft: 'Thông tin hợp đồng', required: true }"
  [hasCollapseBtn]="true"
  [(isHidden)]="isCardHidden"
  (outChangeStateShowContent)="handlerChangeState($event)">
  <div class="p-4">
    <p class="text-gray-700">Nội dung có thể ẩn/hiện.</p>
    <p class="text-sm text-gray-500 mt-2">Trạng thái: {{ isCardHidden() ? 'Đang ẩn' : 'Đang hiện' }}</p>
  </div>
</libs_ui-components-card>
import { Component, signal } from '@angular/core';
import { LibsUiComponentsCardComponent } from '@libs-ui/components-card';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsCardComponent],
  templateUrl: './example.component.html',
})
export class ExampleComponent {
  protected isCardHidden = signal<boolean>(false);

  protected handlerChangeState(event: Event): void {
    event.stopPropagation();
    // isHidden signal đã được cập nhật tự động qua two-way binding
  }
}

3. Card với icon ở phải và custom styling

<libs_ui-components-card
  [label]="{ labelLeft: 'Cấu hình nâng cao' }"
  [hasCollapseBtn]="true"
  [configCard]="{
    iconConRight: true,
    ignoreBorderHeader: false,
    ignoreBackgroundHeader: false,
    classIncludeLabel: 'libs-ui-font-h3s text-blue-600'
  }"
  [classIncludeBody]="'bg-gray-50'">
  <div class="p-4">
    <p class="text-gray-600">Icon collapse nằm bên phải, label màu xanh.</p>
  </div>
</libs_ui-components-card>
import { Component } from '@angular/core';
import { LibsUiComponentsCardComponent } from '@libs-ui/components-card';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsCardComponent],
  templateUrl: './example.component.html',
})
export class ExampleComponent {}

4. Điều khiển card từ bên ngoài (External Control)

<button
  class="px-4 py-2 bg-blue-500 text-white rounded"
  (click)="handlerToggleCard($event)">
  Toggle Card
</button>

<libs_ui-components-card
  [label]="{ labelLeft: 'Card được điều khiển từ ngoài' }"
  [hasCollapseBtn]="true"
  (outFunctionsControl)="handlerReceiveFunctionsControl($event)"
  (outChangeStateShowContent)="handlerChangeState($event)">
  <div class="p-4">
    <p class="text-gray-700">Card này có thể toggle bằng nút bên ngoài.</p>
  </div>
</libs_ui-components-card>
import { Component, signal } from '@angular/core';
import { LibsUiComponentsCardComponent, IFunctionControlCard } from '@libs-ui/components-card';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsCardComponent],
  templateUrl: './example.component.html',
})
export class ExampleComponent {
  private cardControls = signal<IFunctionControlCard | null>(null);
  protected isCardHidden = signal<boolean>(false);

  protected handlerReceiveFunctionsControl(event: Event): void {
    event.stopPropagation();
    this.cardControls.set(event as unknown as IFunctionControlCard);
  }

  protected handlerToggleCard(event: Event): void {
    event.stopPropagation();
    this.cardControls()?.changeHidden();
  }

  protected handlerChangeState(event: Event): void {
    event.stopPropagation();
    this.isCardHidden.set(event as unknown as boolean);
  }
}

Lưu ý: outFunctionsControl emit IFunctionControlCard (không phải DOM Event). Binding đúng là (outFunctionsControl)="handlerReceiveFunctionsControl($event)" với $event kiểu IFunctionControlCard.

5. Card với custom template header

<ng-template #customHeader>
  <div class="flex items-center gap-3 w-full">
    <span class="libs-ui-icon-arrange text-blue-500"></span>
    <span class="libs-ui-font-h3s text-gray-800">Header tùy chỉnh</span>
    <span class="ml-auto text-xs text-gray-400">v2.0</span>
  </div>
</ng-template>

<libs_ui-components-card
  [hasCollapseBtn]="true"
  [templateHeader]="customHeader">
  <div class="p-4">
    <p class="text-gray-700">Nội dung với header hoàn toàn tùy chỉnh.</p>
  </div>
</libs_ui-components-card>
import { Component } from '@angular/core';
import { LibsUiComponentsCardComponent } from '@libs-ui/components-card';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsCardComponent],
  templateUrl: './example.component.html',
})
export class ExampleComponent {}

6. CardWrapper — API đơn giản hóa

<libs_ui-components-card-wrapper
  [labelConfig]="{ labelLeft: 'THÔNG TIN ĐỐI TÁC', required: true }"
  [hasCollapseBtn]="true">
  <div class="p-4">
    <p class="text-gray-700">Wrapper với shadow, label tự động uppercase qua i18n.</p>
  </div>
</libs_ui-components-card-wrapper>
import { Component } from '@angular/core';
import { LibsUiComponentsCardWrapperComponent } from '@libs-ui/components-card';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsCardWrapperComponent],
  templateUrl: './example.component.html',
})
export class ExampleComponent {}

7. CardWrapper — trạng thái lỗi

<libs_ui-components-card-wrapper
  [labelConfig]="{ labelLeft: 'THÔNG TIN BẮT BUỘC', required: true }"
  [borderError]="hasError()"
  [hasCollapseBtn]="true">
  <div class="p-4">
    <p class="text-red-600">Vui lòng điền đầy đủ thông tin bắt buộc.</p>
  </div>
</libs_ui-components-card-wrapper>
import { Component, signal } from '@angular/core';
import { LibsUiComponentsCardWrapperComponent } from '@libs-ui/components-card';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsCardWrapperComponent],
  templateUrl: './example.component.html',
})
export class ExampleComponent {
  protected hasError = signal<boolean>(true);
}

@Input() — libs_ui-components-card

| Input | Type | Default | Mô tả | Ví dụ | |---|---|---|---|---| | [(isHidden)] | boolean (model) | false | Trạng thái ẩn/hiện content, hỗ trợ two-way binding | [(isHidden)]="isCardHidden" | | [classIncludeBody] | string | undefined | Class CSS bổ sung cho phần body | [classIncludeBody]="'bg-gray-50 p-6'" | | [classIncludeHeader] | string | undefined | Class CSS bổ sung cho phần header | [classIncludeHeader]="'!p-0'" | | [classIncludeHeaderWhenHiddenContent] | string | undefined | Class CSS bổ sung cho header khi content đang bị ẩn | [classIncludeHeaderWhenHiddenContent]="'rounded-[4px]'" | | [clickExactly] | boolean | undefined | Khi true: chỉ click icon collapse mới toggle; khi false: click toàn bộ header sẽ toggle | [clickExactly]="true" | | [configCard] | IConfigCard | undefined | Cấu hình styling và behavior tổng hợp của card | [configCard]="{ iconConRight: true }" | | [hasCollapseBtn] | boolean | undefined | Hiển thị nút collapse/expand | [hasCollapseBtn]="true" | | [ignoreTitle] | boolean | undefined | Ẩn phần header, chỉ render body | [ignoreTitle]="true" | | [label] | ILabel | undefined | Cấu hình label hiển thị trong header | [label]="{ labelLeft: 'Tiêu đề', required: true }" | | [templateHeader] | TemplateRef | undefined | Custom template cho header; khi truyền vào, label bị bỏ qua | [templateHeader]="myHeaderTpl" |

@Output() — libs_ui-components-card

| Output | Type | Mô tả | Handler TS | Binding HTML | |---|---|---|---|---| | (outChangeStateShowContent) | boolean | Emit giá trị isHidden mỗi khi trạng thái ẩn/hiện thay đổi | handlerChangeState(isHidden: boolean): void { event.stopPropagation(); this.isCardHidden.set(isHidden); } | (outChangeStateShowContent)="handlerChangeState($event)" | | (outFunctionsControl) | IFunctionControlCard | Emit object chứa các hàm điều khiển card; được emit một lần trong ngOnInit | handlerReceiveFunctionsControl(controls: IFunctionControlCard): void { event.stopPropagation(); this.cardControls.set(controls); } | (outFunctionsControl)="handlerReceiveFunctionsControl($event)" |

@Input() — libs_ui-components-card-wrapper

| Input | Type | Default | Mô tả | Ví dụ | |---|---|---|---|---| | [borderError] | boolean | false | Hiển thị border đỏ trạng thái lỗi | [borderError]="hasValidationError()" | | [hasCollapseBtn] | boolean | true | Hiển thị nút collapse/expand | [hasCollapseBtn]="false" | | [labelConfig] | { labelLeft: string; required?: boolean } | undefined | Cấu hình label cho wrapper; labelLeft sẽ được i18n translate và uppercase tự động | [labelConfig]="{ labelLeft: 'section_title', required: true }" | | [templateHeader] | TemplateRef | undefined | Custom template cho header | [templateHeader]="myHeaderTpl" |

IFunctionControlCard — Methods

| Method | Signature | Mô tả | |---|---|---| | changeHidden | () => Promise<void> | Toggle trạng thái ẩn/hiện của card content từ bên ngoài |

Types & Interfaces

import {
  IConfigCard,
  IFunctionControlCard,
} from '@libs-ui/components-card';

import { ILabel } from '@libs-ui/components-label';
interface IConfigCard {
  /** Bỏ padding-left của header */
  ignorePaddingLeft?: boolean;
  /** Ẩn border header (top, left, right) */
  ignoreBorderHeader?: boolean;
  /** Ẩn background màu xanh nhạt của header */
  ignoreBackgroundHeader?: boolean;
  /** Đặt icon collapse ở bên phải thay vì bên trái */
  iconConRight?: boolean;
  /** Chiều rộng cố định cho vùng label */
  width?: string;
  /** Class CSS bổ sung cho label component */
  classIncludeLabel?: string;
  /** Class CSS bổ sung cho icon collapse */
  classIconInclude?: string;
  /** Class CSS cho icon khi content đang hiện */
  classIconWhenShowContent?: string;
  /** Class CSS cho icon khi content đang ẩn */
  classIconWhenHiddenContent?: string;
  /** Ẩn border-radius của header */
  ignoreBorderRadiusHeader?: boolean;
  /** Ẩn border của body */
  ignoreBorderBody?: boolean;
}

interface IFunctionControlCard {
  /** Toggle trạng thái ẩn/hiện card từ bên ngoài */
  changeHidden: () => Promise<void>;
}

// ILabel được export từ @libs-ui/components-label
interface ILabel {
  labelLeft?: string;
  required?: boolean;
  popover?: string;
}

Lưu ý quan trọng

⚠️ outFunctionsControl emit một lần duy nhất: Output này emit trong ngOnInit. Lưu reference ngay vào signal khi nhận được để sử dụng sau: (outFunctionsControl)="cardControls.set($event)".

⚠️ clickExactly ảnh hưởng hành vi click: Khi [clickExactly]="true", chỉ click trực tiếp lên icon collapse mới toggle; click lên phần còn lại của header không có tác dụng. Khi false hoặc không truyền, click bất kỳ vị trí nào trên header đều toggle (nếu hasCollapseBtntrue).

⚠️ templateHeader override label: Khi truyền [templateHeader], input [label] bị bỏ qua hoàn toàn. Hai input này loại trừ nhau.

⚠️ CardWrapper tự động uppercase + i18n: labelConfig.labelLeft được pipe qua translate rồi uppercase trong template của wrapper. Truyền i18n key nếu có, hoặc truyền chuỗi thường — kết quả sẽ luôn viết hoa.

⚠️ isHidden là model() signal: Hỗ trợ cú pháp two-way binding [(isHidden)]. Có thể dùng [isHidden] (one-way) hoặc [(isHidden)] (two-way) tùy nhu cầu.

Demo

npx nx serve core-ui

Truy cập: http://localhost:4500/card