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-popover

v0.2.357-11

Published

> Component/Directive hiển thị nội dung nổi (popover/tooltip) gắn liền với phần tử gốc, tự động định vị thông minh, hỗ trợ nhiều chế độ kích hoạt và nội dung tùy chỉnh.

Downloads

3,369

Readme

@libs-ui/components-popover

Component/Directive hiển thị nội dung nổi (popover/tooltip) gắn liền với phần tử gốc, tự động định vị thông minh, hỗ trợ nhiều chế độ kích hoạt và nội dung tùy chỉnh.

Giới thiệu

@libs-ui/components-popover cung cấp giải pháp hiển thị thông tin ngữ cảnh linh hoạt trong ứng dụng Angular. Lib hỗ trợ cả hai cách dùng: dạng Directive gắn trực tiếp vào phần tử HTML bất kỳ, hoặc dạng Component Selector để bọc nội dung phức tạp. Popover tự động tính toán vị trí tối ưu dựa trên viewport, fallback sang hướng khác nếu không đủ chỗ, và tự dọn dẹp khi component bị destroy.

Tính năng

  • Hai cách dùng: Directive ([LibsUiComponentsPopoverDirective]) và Component Selector (libs_ui-components-popover)
  • Tự động định vị thông minh: Tính toán hướng tốt nhất dựa trên viewport, tự fallback nếu bị tràn
  • Nhiều chế độ kích hoạt: hover, click, click-toggle, click_open_and_click_panel_content_hidden
  • Hỗ trợ ng-template: Truyền nội dung phức tạp qua TemplateRef
  • Chế độ text-ellipsis: Chỉ hiển thị khi text bị cắt ngắn (type="text")
  • FunctionControl API: Điều khiển popover từ bên ngoài qua outFunctionsControl
  • Hỗ trợ iframe: Render popover vào parent document khi chạy trong iframe
  • Tự cleanup: Tự dọn dẹp overlay khi component bị destroy, không memory leak
  • OnPush + Angular Signals

Khi nào sử dụng

  • Hiển thị tooltip thông tin bổ sung khi di chuột qua icon hoặc văn bản bị truncate
  • Hiển thị dropdown menu hoặc panel tùy chọn khi nhấn vào nút
  • Hiển thị card chi tiết (user profile, product info) khi hover
  • Xây dựng các component dropdown tùy chỉnh cần định vị thông minh
  • Thông báo hướng dẫn, hint tại chỗ cho người dùng

Cài đặt

npm install @libs-ui/components-popover

Import

import { LibsUiComponentsPopoverComponent } from '@libs-ui/components-popover';

@Component({
  standalone: true,
  imports: [LibsUiComponentsPopoverComponent],
  // ...
})
export class YourComponent {}

Lưu ý: LibsUiComponentsPopoverComponent bao gồm cả selector dạng Directive ([LibsUiComponentsPopoverDirective]). Chỉ cần import một lần cho cả hai cách dùng.

Ví dụ sử dụng

Ví dụ 1 — Dạng Directive (Tooltip đơn giản)

import { LibsUiComponentsPopoverComponent } from '@libs-ui/components-popover';

@Component({
  standalone: true,
  imports: [LibsUiComponentsPopoverComponent],
  template: `
    <span
      LibsUiComponentsPopoverDirective
      [config]="{ content: 'Tên đầy đủ: Nguyễn Văn A', direction: 'bottom' }">
      Nguyễn V. A
    </span>
  `,
})
export class BasicTooltipComponent {}

Ví dụ 2 — Dạng Component Selector với nội dung phức tạp

import { LibsUiComponentsPopoverComponent } from '@libs-ui/components-popover';
import { IPopoverFunctionControlEvent } from '@libs-ui/components-popover';

@Component({
  standalone: true,
  imports: [LibsUiComponentsPopoverComponent],
  template: `
    <libs_ui-components-popover
      [config]="popoverConfig"
      [mode]="'click-toggle'"
      (outEvent)="handlerPopoverEvent($event)"
      (outFunctionsControl)="handlerFunctionsControl($event)">
      <button class="px-4 py-2 bg-blue-600 text-white rounded">
        Mở menu
      </button>
    </libs_ui-components-popover>
  `,
})
export class ClickPopoverComponent {
  protected popoverConfig = {
    content: 'Nội dung menu',
    direction: 'bottom' as const,
    maxWidth: 200,
  };

  private popoverControl?: IPopoverFunctionControlEvent;

  handlerFunctionsControl(control: IPopoverFunctionControlEvent): void {
    event?.stopPropagation();
    this.popoverControl = control;
  }

  handlerPopoverEvent(event: string): void {
    event?.stopPropagation();
    console.log('Popover event:', event);
  }

  closePopover(): void {
    this.popoverControl?.removePopoverOverlay();
  }
}

Ví dụ 3 — Dùng ng-template cho nội dung tùy chỉnh

import { LibsUiComponentsPopoverComponent } from '@libs-ui/components-popover';

@Component({
  standalone: true,
  imports: [LibsUiComponentsPopoverComponent],
  template: `
    <div
      LibsUiComponentsPopoverDirective
      [config]="{ template: profileTpl, maxWidth: 280, direction: 'right' }">
      <img src="avatar.png" alt="Avatar" class="w-10 h-10 rounded-full cursor-pointer" />
    </div>

    <ng-template #profileTpl>
      <div class="p-4">
        <div class="font-bold text-gray-800">Nguyễn Văn A</div>
        <div class="text-sm text-gray-500">[email protected]</div>
        <hr class="my-3" />
        <button class="text-sm text-red-500">Đăng xuất</button>
      </div>
    </ng-template>
  `,
})
export class ProfilePopoverComponent {}

Ví dụ 4 — Chế độ text-ellipsis (chỉ hiện khi text bị cắt)

import { LibsUiComponentsPopoverComponent } from '@libs-ui/components-popover';

@Component({
  standalone: true,
  imports: [LibsUiComponentsPopoverComponent],
  template: `
    <div
      LibsUiComponentsPopoverDirective
      [type]="'text'"
      [config]="{ content: 'Đây là đoạn văn bản dài sẽ bị cắt ngắn khi container quá nhỏ', direction: 'top' }"
      style="width: 120px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
      Đây là đoạn văn bản dài sẽ bị cắt ngắn khi container quá nhỏ
    </div>
  `,
})
export class TextEllipsisPopoverComponent {}

Ví dụ 5 — Điều khiển từ bên ngoài (FunctionControl)

import { LibsUiComponentsPopoverComponent } from '@libs-ui/components-popover';
import { IPopoverFunctionControlEvent } from '@libs-ui/components-popover';

@Component({
  standalone: true,
  imports: [LibsUiComponentsPopoverComponent],
  template: `
    <libs_ui-components-popover
      [config]="{ content: 'Popover được mở từ bên ngoài', direction: 'bottom' }"
      [mode]="'click'"
      [ignoreShowPopover]="true"
      (outFunctionsControl)="handlerFunctionsControl($event)">
      <span>Phần tử target</span>
    </libs_ui-components-popover>

    <button (click)="handlerOpenPopover($event)">Mở Popover từ nút khác</button>
    <button (click)="handlerClosePopover($event)">Đóng Popover</button>
  `,
})
export class ControlledPopoverComponent {
  private popoverControl?: IPopoverFunctionControlEvent;

  handlerFunctionsControl(control: IPopoverFunctionControlEvent): void {
    event?.stopPropagation();
    this.popoverControl = control;
  }

  handlerOpenPopover(event: MouseEvent): void {
    event.stopPropagation();
    this.popoverControl?.showPopover();
  }

  handlerClosePopover(event: MouseEvent): void {
    event.stopPropagation();
    this.popoverControl?.removePopoverOverlay();
  }
}

@Input()

| Input | Type | Default | Mô tả | Ví dụ | |---|---|---|---|---| | [config] | IPopoverOverlay | {} | Cấu hình nội dung, hướng, kích thước và style của popover. Bắt buộc truyền để popover hiển thị. | [config]="{ content: 'Hello', direction: 'top' }" | | [mode] | TYPE_POPOVER_MODE | 'hover' | Chế độ kích hoạt hiển thị popover. Xem bảng TYPE_POPOVER_MODE bên dưới. | [mode]="'click-toggle'" | | [type] | 'text' \| 'other' | 'other' | Khi là 'text', popover chỉ hiển thị khi nội dung bị truncate (clientWidth >= scrollWidth). | [type]="'text'" | | [ignoreShowPopover] | boolean | undefined | Khi true, vô hiệu hóa hoàn toàn việc hiển thị popover. Hữu ích khi muốn kiểm soát thủ công qua FunctionControl. | [ignoreShowPopover]="isDisabled()" | | [elementRefCustom] | HTMLElement | undefined | Element tùy chỉnh để gắn popover vào thay vì host element mặc định. | [elementRefCustom]="myElementRef" | | [initEventInElementRefCustom] | boolean | false | Khi true, gắn event listener vào elementRefCustom thay vì host element. Phải dùng kèm elementRefCustom. | [initEventInElementRefCustom]="true" | | [classInclude] | string | '' | Các CSS class bổ sung sẽ được thêm vào host element. | [classInclude]="'cursor-pointer text-blue-500'" | | [flagMouse] | IFlagMouse | { isMouseEnter: false, isMouseEnterContent: false, isContainerHasScroll: false } | Trạng thái chuột từ component cha, dùng để ngăn đóng popover khi chuột còn trong vùng liên quan. | [flagMouse]="parentFlagMouse()" | | [ignoreHiddenPopoverContentWhenMouseLeave] | boolean | undefined | Khi true, không ẩn popover khi chuột rời khỏi content overlay. | [ignoreHiddenPopoverContentWhenMouseLeave]="true" | | [ignoreClickOutside] | boolean | undefined | Khi true, không đóng popover khi click bên ngoài vùng popover. | [ignoreClickOutside]="true" | | [ignoreStopPropagationEvent] | boolean | undefined | Khi true, không gọi stopPropagation trên các event — cho phép event lan truyền lên component cha. | [ignoreStopPropagationEvent]="true" | | [ignoreCursorPointerModeLikeClick] | boolean | undefined | Khi true, không thêm class cursor-pointer vào host element khi mode là click. | [ignoreCursorPointerModeLikeClick]="true" | | [isAddContentToParentDocument] | boolean | undefined | Khi true, render overlay vào parent document. Dùng khi popover nằm trong iframe. | [isAddContentToParentDocument]="true" | | [debugId] | string | undefined | ID dùng cho mục đích debug. | [debugId]="'user-avatar-popover'" |

@Output()

| Output | Type | Mô tả | Handler TS | Binding HTML | |---|---|---|---|---| | (outEvent) | TYPE_POPOVER_EVENT | Phát ra khi popover thay đổi trạng thái: 'show' khi mở, 'remove' khi đóng, 'click' khi click vào host, 'mouseenter-element'/'mouseleave-element' khi chuột vào/ra khỏi host, 'mouseenter-content'/'mouseleave-content' khi chuột vào/ra khỏi overlay. | handlerEvent(event: TYPE_POPOVER_EVENT): void { event?.stopPropagation(); ... } | (outEvent)="handlerEvent($event)" | | (outChangStageFlagMouse) | IFlagMouse | Phát ra mỗi khi trạng thái chuột thay đổi. Truyền kết quả vào [flagMouse] của popover khác để đồng bộ nhiều popover liên quan. | handlerFlagMouseChange(flag: IFlagMouse): void { event?.stopPropagation(); this.flagMouse.set(flag); } | (outChangStageFlagMouse)="handlerFlagMouseChange($event)" | | (outEventPopoverContent) | TYPE_POPOVER_DIRECTION | Phát ra hướng thực tế mà popover đang hiển thị sau khi tính toán. Hữu ích để điều chỉnh UI dựa trên vị trí overlay. | handlerDirectionChange(dir: TYPE_POPOVER_DIRECTION): void { event?.stopPropagation(); this.currentDirection.set(dir); } | (outEventPopoverContent)="handlerDirectionChange($event)" | | (outFunctionsControl) | IPopoverFunctionControlEvent | Phát ra object chứa các hàm điều khiển popover từ bên ngoài. Nhận về ngay khi component khởi tạo. | handlerFunctionsControl(ctrl: IPopoverFunctionControlEvent): void { event?.stopPropagation(); this.popoverCtrl = ctrl; } | (outFunctionsControl)="handlerFunctionsControl($event)" |

Types & Interfaces

import {
  IPopoverOverlay,
  IPopoverFunctionControlEvent,
  IFlagMouse,
  IPopover,
  TYPE_POPOVER_TYPE,
  TYPE_POPOVER_MODE,
  TYPE_POPOVER_DIRECTION,
  TYPE_POPOVER_POSITION_MODE,
  TYPE_POPOVER_EVENT,
} from '@libs-ui/components-popover';

IPopoverOverlay — Cấu hình overlay

import { IPopoverOverlay } from '@libs-ui/components-popover';

export interface IPopoverOverlay {
  // Nội dung
  content?: string;               // Nội dung dạng string HTML. Không dùng kèm template.
  template?: TemplateRef<any>;    // Nội dung dạng ng-template. Không dùng kèm content.
  itemContext?: any;              // Context truyền vào template khi dùng ng-template.

  // Kích thước
  width?: number;                 // Chiều rộng cố định (px)
  minWidth?: number;              // Chiều rộng tối thiểu (px)
  maxWidth?: number;              // Chiều rộng tối đa (px). Default: 250
  maxHeight?: number | null;      // Chiều cao tối đa (px). null = tự tính theo viewport. Default: 140
  widthByParent?: boolean;        // Nếu true, overlay rộng bằng phần tử cha
  parentBorderWidth?: number;     // Bù border của phần tử cha khi dùng widthByParent

  // Vị trí & hướng
  direction?: TYPE_POPOVER_DIRECTION;  // Hướng ưu tiên. Default: 'bottom'
  directionDistance?: number;          // Khoảng cách bổ sung theo hướng (px)
  position?: {
    mode: TYPE_POPOVER_POSITION_MODE;  // Căn chỉnh theo trục phụ: 'start' | 'center' | 'end'
    distance: number;                  // Khoảng offset so với vị trí căn chỉnh (px)
    autoUpdatePosition?: {
      startDistance: number;           // Offset khi mode tự chuyển sang 'start'
      endDistance: number;             // Offset khi mode tự chuyển sang 'end'
    };
  };

  // Padding nội dung overlay
  paddingTop?: number;
  paddingRight?: number;
  paddingBottom?: number;
  paddingLeft?: number;

  // Styling
  classInclude?: string;           // CSS class bổ sung cho wrapper overlay
  classIncludeOverlayBody?: string; // CSS class bổ sung cho body overlay
  whiteTheme?: boolean;            // Dùng theme màu trắng
  ngStyles?: Record<string, any>;  // Inline styles bổ sung

  // Mũi tên
  ignoreArrow?: boolean;           // Ẩn mũi tên chỉ hướng
  arrowPositionCustom?: number;    // Vị trí tùy chỉnh của mũi tên (px)

  // Animation
  animationConfig?: {
    time?: number;       // Thời gian animation (giây). Default: 0.4
    distance?: number;   // Khoảng cách trượt khi animate (px). Default: 12
  };

  // Hành vi
  timerDestroy?: number;              // Delay (ms) trước khi ẩn khi chuột rời. Default: 0
  zIndex?: number;                    // z-index của overlay
  isAddContentToParentDocument?: boolean; // Render vào parent document (dùng trong iframe)
  scrollOverlayOptions?: IScrollOverlayOptions; // Tùy chọn scroll cho overlay
}

TYPE_POPOVER_MODE — Chế độ kích hoạt

import { TYPE_POPOVER_MODE } from '@libs-ui/components-popover';

type TYPE_POPOVER_MODE =
  | 'hover'                                    // Hiển thị khi di chuột vào (mặc định)
  | 'click'                                    // Hiển thị khi click vào, không tự đóng
  | 'click-toggle'                             // Click để mở, click lần nữa để đóng
  | 'click_open_and_click_panel_content_hidden'; // Click để mở, click vào nội dung overlay để đóng

TYPE_POPOVER_DIRECTION — Hướng hiển thị

import { TYPE_POPOVER_DIRECTION } from '@libs-ui/components-popover';

type TYPE_POPOVER_DIRECTION = 'top' | 'right' | 'bottom' | 'left';

Khi hướng ưu tiên không đủ chỗ trong viewport, popover tự động thử theo thứ tự fallback:

  • bottombottom, top, right, left
  • toptop, bottom, right, left
  • rightright, left, bottom, top
  • leftleft, right, bottom, top

TYPE_POPOVER_POSITION_MODE — Căn chỉnh trục phụ

import { TYPE_POPOVER_POSITION_MODE } from '@libs-ui/components-popover';

type TYPE_POPOVER_POSITION_MODE = 'start' | 'center' | 'end';
// 'start'  → Căn về phía bắt đầu của phần tử gốc (trái khi direction là top/bottom)
// 'center' → Căn giữa so với phần tử gốc (mặc định khi không chỉ định)
// 'end'    → Căn về phía cuối của phần tử gốc (phải khi direction là top/bottom)

TYPE_POPOVER_EVENT — Sự kiện

import { TYPE_POPOVER_EVENT } from '@libs-ui/components-popover';

type TYPE_POPOVER_EVENT =
  | 'show'               // Overlay vừa được hiển thị
  | 'remove'             // Overlay vừa bị xóa
  | 'click'              // Click vào host element
  | 'mouseenter-element' // Chuột vào host element
  | 'mouseleave-element' // Chuột rời khỏi host element
  | 'mouseenter-content' // Chuột vào overlay content
  | 'mouseleave-content'; // Chuột rời khỏi overlay content

IPopoverFunctionControlEvent — API điều khiển

import { IPopoverFunctionControlEvent } from '@libs-ui/components-popover';

interface IPopoverFunctionControlEvent {
  showPopover: (elementRef?: HTMLElement) => Promise<void>;         // Hiển thị popover (tùy chọn gắn vào element khác)
  removePopoverOverlay: () => Promise<void>;                        // Đóng và xóa overlay
  updatePopoverOverlayPosition: () => Promise<void>;               // Tính toán lại vị trí overlay
  updatePopoverOverlay: () => Promise<void>;                       // Cập nhật config và vị trí overlay
  getRectContainer: () => Promise<IBoundingClientRect>;            // Lấy vị trí phần tử gốc
  getRectContent: () => Promise<IBoundingClientRect>;              // Lấy vị trí overlay content
}

IFlagMouse — Trạng thái chuột

import { IFlagMouse } from '@libs-ui/components-popover';

interface IFlagMouse {
  isMouseEnter: boolean;           // Chuột đang trong host element
  isMouseEnterContent: boolean;    // Chuột đang trong overlay content
  isContainerHasScroll?: boolean;  // Host element có scroll
}

IPopover — Interface tổng hợp

import { IPopover } from '@libs-ui/components-popover';

interface IPopover {
  type?: TYPE_POPOVER_TYPE;
  mode?: TYPE_POPOVER_MODE;
  dataView?: string;
  config?: IPopoverOverlay;
  classInclude?: string;
  ignoreShowPopover?: boolean;
  ignoreHiddenPopoverContentWhenMouseLeave?: boolean;
  elementRefCustom?: HTMLElement;
  isAddContentToParentDocument?: boolean;
  templateCustomContent?: TemplateRef<any>;
}

Lưu ý quan trọng

⚠️ Một import cho cả hai cách dùng: LibsUiComponentsPopoverComponent đăng ký cả selector dạng element (libs_ui-components-popover) lẫn attribute directive ([LibsUiComponentsPopoverDirective]). Chỉ cần import một lần vào imports[].

⚠️ Không truyền cả contenttemplate cùng lúc: Nếu truyền cả hai, content sẽ bị bỏ qua. Chọn một trong hai cách truyền nội dung.

⚠️ maxHeight: null để tự động tính: Khi đặt maxHeight: null, chiều cao overlay sẽ tự điều chỉnh dựa trên khoảng trống còn lại trong viewport (tính từ top của host element xuống đáy màn hình trừ 44px).

⚠️ Chế độ hover với timerDestroy: Khi dùng nhiều popover lồng nhau (popover trong popover), sử dụng [flagMouse](outChangStageFlagMouse) để truyền trạng thái chuột giữa các popover, tránh đóng popover cha khi chuột đang trong popover con.

⚠️ Debug via URL query: Có thể đặt thời gian delay trước khi ẩn popover qua URL query ?popover-timer-destroy=500 (đơn vị ms) — hữu ích khi debug bằng DevTools.

⚠️ Iframe support: Khi component chạy trong iframe, dùng [isAddContentToParentDocument]="true" hoặc config.isAddContentToParentDocument = true để render overlay vào parent document và tránh bị cắt bởi overflow: hidden.