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-click-outside

v0.2.357-35

Published

> Directive phát hiện click bên ngoài hoặc bên trong một element — hữu ích cho dropdown, modal, popover và mọi UI cần đóng khi người dùng click ra ngoài.

Readme

@libs-ui/components-click-outside

Directive phát hiện click bên ngoài hoặc bên trong một element — hữu ích cho dropdown, modal, popover và mọi UI cần đóng khi người dùng click ra ngoài.

Giới thiệu

LibsUiComponentsClickOutsideDirective là một Angular Standalone Directive lắng nghe sự kiện mousedown trên window và phân biệt click xảy ra bên trong hay bên ngoài element được gắn directive. Directive tự động dọn dẹp subscription khi bị destroy thông qua takeUntilDestroyed, không gây memory leak. Sự kiện được phát sớm hơn so với click do dùng mousedown, giúp UI phản hồi nhanh hơn.

Tính năng

  • Phát hiện click bên ngoài element và emit (outOutside)
  • Phát hiện click bên trong element và emit (outInSide)
  • Sử dụng mousedown để bắt sự kiện sớm hơn click
  • Tự động cleanup với takeUntilDestroyed — không cần ngOnDestroy
  • Standalone directive — import trực tiếp, không cần NgModule
  • Không có dependency ngoài Angular và RxJS

Khi nào sử dụng

  • Đóng dropdown/menu khi người dùng click bên ngoài
  • Đóng modal/dialog khi click vào vùng overlay xung quanh
  • Đóng tooltip hoặc popover khi click ra ngoài
  • Phát hiện tương tác của người dùng với một vùng element cụ thể
  • Implement hành vi "click-away" cho custom components

Cài đặt

npm install @libs-ui/components-click-outside

Import

import { LibsUiComponentsClickOutsideDirective } from '@libs-ui/components-click-outside';

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

Ví dụ sử dụng

1. Cơ bản — lắng nghe cả hai sự kiện

import { Component } from '@angular/core';
import { LibsUiComponentsClickOutsideDirective } from '@libs-ui/components-click-outside';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsClickOutsideDirective],
  template: `
    <div
      LibsUiComponentsClickOutsideDirective
      (outOutside)="onClickOutside($event)"
      (outInSide)="onClickInside($event)"
      class="p-4 border-2 border-blue-500 rounded-lg">
      <p>Click inside or outside this box</p>
    </div>
  `,
})
export class ExampleComponent {
  onClickOutside(event: Event): void {
    console.log('Clicked outside!', event);
  }

  onClickInside(event: Event): void {
    console.log('Clicked inside!', event);
  }
}

2. Dropdown — đóng khi click ra ngoài

import { Component, signal } from '@angular/core';
import { LibsUiComponentsClickOutsideDirective } from '@libs-ui/components-click-outside';

@Component({
  standalone: true,
  imports: [LibsUiComponentsClickOutsideDirective],
  template: `
    <div class="relative">
      <button
        (click)="isOpen.set(!isOpen())"
        class="px-4 py-2 bg-blue-500 text-white rounded">
        Toggle Dropdown
      </button>

      @if (isOpen()) {
        <div
          LibsUiComponentsClickOutsideDirective
          (outOutside)="isOpen.set(false)"
          class="absolute mt-2 w-48 bg-white border rounded-lg shadow-lg">
          <ul class="py-2">
            <li class="px-4 py-2 hover:bg-gray-100 cursor-pointer">Option 1</li>
            <li class="px-4 py-2 hover:bg-gray-100 cursor-pointer">Option 2</li>
            <li class="px-4 py-2 hover:bg-gray-100 cursor-pointer">Option 3</li>
          </ul>
        </div>
      }
    </div>
  `,
})
export class DropdownExample {
  readonly isOpen = signal<boolean>(false);
}

3. Modal — đóng khi click vào overlay

import { Component, signal } from '@angular/core';
import { LibsUiComponentsClickOutsideDirective } from '@libs-ui/components-click-outside';

@Component({
  standalone: true,
  imports: [LibsUiComponentsClickOutsideDirective],
  template: `
    @if (showModal()) {
      <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center">
        <div
          LibsUiComponentsClickOutsideDirective
          (outOutside)="showModal.set(false)"
          class="bg-white p-6 rounded-lg shadow-xl max-w-md">
          <h3 class="text-lg font-semibold mb-4">Modal Title</h3>
          <p class="text-gray-600 mb-4">Click outside to close this modal</p>
          <button
            (click)="showModal.set(false)"
            class="px-4 py-2 bg-blue-500 text-white rounded">
            Close
          </button>
        </div>
      </div>
    }
  `,
})
export class ModalExample {
  readonly showModal = signal<boolean>(false);
}

4. Click counter — đếm số lần click inside/outside

import { Component, signal } from '@angular/core';
import { LibsUiComponentsClickOutsideDirective } from '@libs-ui/components-click-outside';

@Component({
  standalone: true,
  imports: [LibsUiComponentsClickOutsideDirective],
  template: `
    <div
      LibsUiComponentsClickOutsideDirective
      (outOutside)="onClickOutside()"
      (outInSide)="onClickInside()"
      class="p-6 border-2 border-purple-500 rounded-lg bg-purple-50">
      <h4 class="font-semibold mb-2">Click Counter</h4>
      <p>Inside clicks: {{ insideCount() }}</p>
      <p>Outside clicks: {{ outsideCount() }}</p>
    </div>
  `,
})
export class CounterExample {
  readonly insideCount = signal<number>(0);
  readonly outsideCount = signal<number>(0);

  onClickInside(): void {
    this.insideCount.update((count) => count + 1);
  }

  onClickOutside(): void {
    this.outsideCount.update((count) => count + 1);
  }
}

@Output()

| Output | Type | Mô tả | Handler TS | Binding HTML | |---|---|---|---|---| | (outOutside) | Event | Emit khi mousedown xảy ra bên ngoài element | handlerClickOutside(event: Event): void { event.stopPropagation(); ... } | (outOutside)="handlerClickOutside($event)" | | (outInSide) | Event | Emit khi mousedown xảy ra bên trong element | handlerClickInside(event: Event): void { event.stopPropagation(); ... } | (outInSide)="handlerClickInside($event)" |

Selector

Directive được gắn vào element bằng attribute selector:

<div LibsUiComponentsClickOutsideDirective ...>

Selector: [LibsUiComponentsClickOutsideDirective]

Lưu ý quan trọng

⚠️ Event Timing: Directive setup listener trong ngAfterViewInit — event chỉ hoạt động sau khi view được khởi tạo hoàn chỉnh.

⚠️ mousedown thay vì click: Directive dùng mousedown để bắt sự kiện sớm hơn click, đảm bảo UI phản hồi ngay khi người dùng nhấn chuột xuống chứ không chờ nhả ra.

⚠️ stopPropagation nội bộ: Bên trong directive, sự kiện đã được gọi stopPropagation() để tránh conflict với các handler ở parent. Tuy nhiên, các handler ở phía consumer (component sử dụng directive) vẫn nên gọi event.stopPropagation() theo convention của project để ngăn sự kiện lan lên trên.

⚠️ Không có @Input(): Directive này không nhận bất kỳ input nào — chỉ cần gắn vào element và lắng nghe output.

⚠️ Cleanup tự động: Subscription được dọn dẹp qua takeUntilDestroyed(destroyRef) — không cần tự unsubscribe hay implement ngOnDestroy.