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-device-support

v0.2.357-16

Published

> Directive xử lý sự kiện click đa thiết bị, tự động chọn event type phù hợp (click / pointerdown / touchstart) dựa trên loại thiết bị đang dùng.

Readme

@libs-ui/components-device-support

Directive xử lý sự kiện click đa thiết bị, tự động chọn event type phù hợp (click / pointerdown / touchstart) dựa trên loại thiết bị đang dùng.

Giới thiệu

LibsUiComponentsDeviceSupportHandleClickDirective giải quyết bài toán tương thích click event trên nhiều loại thiết bị: desktop, mobile, và touch screen. Thay vì phải tự phát hiện và bind đúng event type, directive tự động sử dụng getEventNameHandleClick từ @libs-ui/utils để chọn event phù hợp. Mặc định, directive gọi stopPropagation() trên mỗi event để ngăn bubble lên parent, hành vi này có thể tắt qua input [ignoreStopPropagation].

Tính năng

  • ✅ Tự động phát hiện loại thiết bị và chọn event type phù hợp (click / pointerdown / touchstart)
  • ✅ Tự động gọi stopPropagation() theo mặc định, có thể tắt khi cần
  • ✅ Hỗ trợ gắn click listener vào element tùy chỉnh (không nhất thiết là host element)
  • ✅ Tự động cleanup subscription qua DestroyRef + takeUntilDestroyed, không rò rỉ bộ nhớ
  • ✅ Standalone directive, dễ import vào bất kỳ component nào

Khi nào sử dụng

  • Khi cần xử lý click event hoạt động đúng trên cả desktop và mobile/touch devices
  • Khi cần tự động phát hiện loại thiết bị và chọn event phù hợp thay vì hardcode (click)
  • Khi muốn stopPropagation được xử lý tự động, không cần gọi thủ công trong handler
  • Khi cần gắn click listener vào một element bên ngoài host (ví dụ: window, document, hoặc một HTMLElement tùy chọn)
  • Khi xây dựng component dùng chung cần tương thích nhiều thiết bị

Cài đặt

npm install @libs-ui/components-device-support

Import

import { LibsUiComponentsDeviceSupportHandleClickDirective } from '@libs-ui/components-device-support';

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

Ví dụ sử dụng

1. Sử dụng cơ bản trên button

import { Component } from '@angular/core';
import { LibsUiComponentsDeviceSupportHandleClickDirective } from '@libs-ui/components-device-support';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsDeviceSupportHandleClickDirective],
  template: `
    <button
      libsUiComponentsDeviceSupportHandleClickDirective
      (outClick)="onButtonClick($event)"
      class="px-4 py-2 bg-blue-500 text-white rounded">
      Click me
    </button>
  `
})
export class ExampleComponent {
  onButtonClick(event: Event): void {
    console.log('Clicked!', event.type);
  }
}

2. Gắn click listener vào element tùy chỉnh (không phải host)

Hữu ích khi directive host ẩn nhưng cần lắng nghe click trên một element khác trong DOM.

import { Component } from '@angular/core';
import { LibsUiComponentsDeviceSupportHandleClickDirective } from '@libs-ui/components-device-support';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsDeviceSupportHandleClickDirective],
  template: `
    <!-- Element sẽ được lắng nghe click -->
    <div #targetElement class="p-4 border rounded bg-gray-50">
      Target Element - Click vào đây
    </div>

    <!-- Directive host — lắng nghe trên targetElement, không phải div này -->
    <div
      libsUiComponentsDeviceSupportHandleClickDirective
      [elementHandleClick]="targetElement"
      (outClick)="onTargetClick($event)">
      Directive host (không click vào đây)
    </div>
  `
})
export class ExampleComponent {
  onTargetClick(event: Event): void {
    console.log('Target clicked!', event.type);
  }
}

3. Kiểm soát stopPropagation — cho phép event bubble lên parent

import { Component } from '@angular/core';
import { LibsUiComponentsDeviceSupportHandleClickDirective } from '@libs-ui/components-device-support';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsDeviceSupportHandleClickDirective],
  template: `
    <!-- Với stopPropagation (mặc định) -->
    <div (click)="onParentClick()">
      <button
        libsUiComponentsDeviceSupportHandleClickDirective
        (outClick)="onChildClick($event)">
        stopPropagation: ON (parent sẽ KHÔNG nhận click)
      </button>
    </div>

    <!-- Không stopPropagation -->
    <div (click)="onParentClick()">
      <button
        libsUiComponentsDeviceSupportHandleClickDirective
        [ignoreStopPropagation]="true"
        (outClick)="onChildClick($event)">
        stopPropagation: OFF (parent SẼ nhận click)
      </button>
    </div>
  `
})
export class ExampleComponent {
  onChildClick(event: Event): void {
    console.log('Child clicked');
  }

  onParentClick(): void {
    console.log('Parent clicked');
  }
}

4. Kết hợp với Signal để theo dõi trạng thái tương tác

import { Component, signal } from '@angular/core';
import { LibsUiComponentsDeviceSupportHandleClickDirective } from '@libs-ui/components-device-support';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [LibsUiComponentsDeviceSupportHandleClickDirective],
  template: `
    <div>
      <button (click)="toggleStopPropagation()">
        Toggle stopPropagation
      </button>

      <div (click)="onParentClick()">
        <button
          libsUiComponentsDeviceSupportHandleClickDirective
          [ignoreStopPropagation]="ignoreStopPropagation()"
          (outClick)="onInteractiveClick($event)"
          class="px-4 py-2 bg-blue-500 text-white rounded">
          Click me
        </button>
      </div>

      <p>Click count: {{ clickCount() }}</p>
      <p>Last event type: {{ lastEventType() }}</p>
      <p>ignoreStopPropagation: {{ ignoreStopPropagation() }}</p>
    </div>
  `
})
export class ExampleComponent {
  readonly ignoreStopPropagation = signal<boolean>(false);
  readonly clickCount = signal<number>(0);
  readonly lastEventType = signal<string>('');

  toggleStopPropagation(): void {
    this.ignoreStopPropagation.update((v) => !v);
  }

  onInteractiveClick(event: Event): void {
    this.clickCount.update((count) => count + 1);
    this.lastEventType.set(event.type);
  }

  onParentClick(): void {
    console.log('Parent clicked');
  }
}

@Input()

| Input | Type | Default | Mô tả | Ví dụ | |---|---|---|---|---| | [elementHandleClick] | HTMLElement \| Window \| Document \| undefined | host element | Element để lắng nghe event click. Khi không truyền, directive sẽ lắng nghe trên chính host element | [elementHandleClick]="targetEl" | | [ignoreStopPropagation] | boolean | false | Khi true, directive sẽ không gọi stopPropagation() — event sẽ bubble lên parent elements như bình thường | [ignoreStopPropagation]="true" |

@Output()

| Output | Type | Mô tả | Handler TS | Binding HTML | |---|---|---|---|---| | (outClick) | Event | Emit event khi user click/touch vào element. Event type phụ thuộc vào loại thiết bị (click, pointerdown, hoặc touchstart) | handlerClick(event: Event): void { event.stopPropagation(); console.log(event.type); } | (outClick)="handlerClick($event)" |

Logic ẩn quan trọng

Auto Event Detection

Directive sử dụng hàm getEventNameHandleClick từ @libs-ui/utils để tự động chọn event type phù hợp với thiết bị:

  • Touch device (có onpointerdownmaxTouchPoints > 0): sử dụng pointerdown
  • Desktop: sử dụng click
  • Mobile (non-pointer touch): sử dụng touchstart

Việc này đảm bảo click handler luôn phản hồi nhanh và chính xác trên mọi nền tảng.

Default stopPropagation

Mặc định ignoreStopPropagation = false, tức là directive luôn gọi e.stopPropagation() trước khi emit outClick. Điều này ngăn event bubble lên các parent elements, tránh trigger click handler cha không mong muốn.

Để cho phép event bubble (ví dụ: khi cần parent cũng nhận click), truyền [ignoreStopPropagation]="true".

Custom Element Target

Khi truyền [elementHandleClick], directive sẽ gắn fromEvent lên element đó thay vì elementRef.nativeElement. Điều này cho phép directive host có thể là một element ẩn (class="hidden"), trong khi thực sự lắng nghe click trên một element hiển thị khác.

RxJS & Memory Safety

Directive sử dụng fromEvent() từ RxJS kết hợp với takeUntilDestroyed(destroyRef). Subscription tự động bị hủy khi directive bị destroy, không cần ngOnDestroy thủ công.

Lưu ý quan trọng

⚠️ Gắn vào AfterViewInit: Directive khởi tạo fromEvent trong ngAfterViewInit, nên element target (elementHandleClick) phải tồn tại trong DOM trước thời điểm này. Không nên truyền element được tạo động sau ngAfterViewInit.

⚠️ stopPropagation trong handler: Khi directive đã tự gọi stopPropagation() (mặc định), handler (outClick) nhận được event đã bị stop propagation. Tuy nhiên, nếu bạn có thêm event binding Angular (click) trên cùng element, Angular's event system vẫn có thể xử lý — hãy kiểm tra kỹ behavior khi dùng kết hợp.

⚠️ Selector dạng attribute: Đây là attribute directive, selector là [libsUiComponentsDeviceSupportHandleClickDirective]. Phải dùng dạng attribute trên element, không dùng dạng tag.

⚠️ window / document làm target: Khi truyền window hoặc document vào [elementHandleClick], directive sẽ lắng nghe click toàn trang. Hãy đảm bảo chỉ có một instance directive làm điều này tại một thời điểm, và component chứa directive phải được destroy đúng cách khi không còn dùng nữa.