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-drag-drop

v0.2.355-15

Published

> Thư viện Angular directives hỗ trợ kéo thả (drag and drop) linh hoạt, hỗ trợ kéo thả giữa các container, virtual scrolling, auto-scroll và custom boundary.

Readme

@libs-ui/components-drag-drop

Thư viện Angular directives hỗ trợ kéo thả (drag and drop) linh hoạt, hỗ trợ kéo thả giữa các container, virtual scrolling, auto-scroll và custom boundary.

Version

0.2.355-14

Author

Mobio Dev Team

Khi nào sử dụng

  • Khi cần sắp xếp lại thứ tự các phần tử trong một danh sách bằng thao tác kéo thả.
  • Khi cần di chuyển phần tử giữa nhiều container (cross-container drag and drop).
  • Khi cần triển khai Kanban board với nhiều cột có thể kéo thả qua lại.
  • Khi danh sách lớn cần kết hợp virtual scroll để tối ưu hiệu năng.
  • Khi cần giới hạn vùng kéo thả trong một boundary cụ thể.
  • Khi cần auto-scroll khi kéo phần tử gần mép container.

Lưu ý quan trọng

⚠️ Two-way binding: [(items)] là bắt buộc trên Container directive. Danh sách items sẽ tự động cập nhật khi kéo thả.

⚠️ Group Name: Khi kéo thả giữa các container, cần thiết lập [groupName][dropToGroupName] để xác định nguồn và đích.

⚠️ Virtual Scroll: Khi sử dụng virtual scroll, cần bật [itemInContainerVirtualScroll]="true" trên Item directive và cung cấp [fieldId] để định danh item.

⚠️ ViewEncapsulation: Mặc định là 'emulated'. Chuyển sang 'none' nếu cần style xuyên qua shadow DOM.

⚠️ Mode copy: Khi [mode]="'copy'", item sẽ được sao chép thay vì di chuyển sang container đích.

⚠️ Performance: Sử dụng [throttleTimeHandlerDraggingEvent] để throttle event dragging, giảm tải khi danh sách lớn.

⚠️ Padding thay vì Margin: Khoảng cách giữa các drag item PHẢI dùng padding (ví dụ pb-2), KHÔNG dùng margin (ví dụ mb-2). Khi dùng margin, vùng margin giữa các item không thuộc bất kỳ element nào, khiến thư viện không phát hiện được vị trí hover chính xác và sẽ thêm phần tử xuống cuối danh sách thay vì vị trí đang hover.

Cài đặt & Import

import {
  LibsUiComponentsDragContainerDirective,
  LibsUiDragItemDirective,
  LibsUiComponentsDragScrollDirective,
  LibsUiDragItemInContainerVirtualScrollDirective,
} from '@libs-ui/components-drag-drop';

Ví dụ sử dụng

Basic - Sắp xếp danh sách đơn giản

import { Component } from '@angular/core';
import {
  LibsUiComponentsDragContainerDirective,
  LibsUiDragItemDirective,
} from '@libs-ui/components-drag-drop';

@Component({
  selector: 'app-basic-drag-drop',
  standalone: true,
  imports: [LibsUiComponentsDragContainerDirective, LibsUiDragItemDirective],
  template: `
    <div
      LibsUiComponentsDragContainerDirective
      [(items)]="items">
      <div
        *ngFor="let item of items"
        LibsUiDragItemDirective
        [item]="item">
        {{ item.name }}
      </div>
    </div>
  `,
})
export class BasicDragDropComponent {
  items = [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' },
  ];
}

Cross Container - Kéo thả giữa hai container

@Component({
  standalone: true,
  imports: [LibsUiComponentsDragContainerDirective, LibsUiDragItemDirective],
  template: `
    <div class="flex gap-4">
      <!-- Container nguồn -->
      <div
        LibsUiComponentsDragContainerDirective
        [(items)]="sourceItems"
        [groupName]="'source'"
        [dropToGroupName]="['target']"
        (outDroppedContainer)="onDrop($event)">
        <div
          *ngFor="let item of sourceItems"
          LibsUiDragItemDirective
          [item]="item">
          {{ item.name }}
        </div>
      </div>

      <!-- Container đích -->
      <div
        LibsUiComponentsDragContainerDirective
        [(items)]="targetItems"
        [groupName]="'target'"
        [dropToGroupName]="['source']"
        (outDroppedContainer)="onDrop($event)">
        <div
          *ngFor="let item of targetItems"
          LibsUiDragItemDirective
          [item]="item">
          {{ item.name }}
        </div>
      </div>
    </div>
  `,
})
export class CrossContainerComponent {
  sourceItems = [
    { id: 1, name: 'Source 1' },
    { id: 2, name: 'Source 2' },
  ];
  targetItems = [
    { id: 3, name: 'Target 1' },
    { id: 4, name: 'Target 2' },
  ];

  onDrop(event: IDrop) {
    console.log('Dropped:', event);
  }
}

Kanban Board

@Component({
  standalone: true,
  imports: [LibsUiComponentsDragContainerDirective, LibsUiDragItemDirective],
  template: `
    <div class="flex gap-4">
      @for (column of columns; track column.id) {
        <div class="flex-1 bg-gray-100 p-4 rounded-lg">
          <h3>{{ column.title }}</h3>
          <div
            LibsUiComponentsDragContainerDirective
            [(items)]="column.items"
            [groupName]="column.id"
            [dropToGroupName]="allGroupNames"
            [directionDrag]="'vertical'">
            @for (item of column.items; track item.id) {
              <div
                class="bg-white p-3 mb-2 rounded shadow-sm"
                LibsUiDragItemDirective
                [item]="item">
                {{ item.title }}
              </div>
            }
          </div>
        </div>
      }
    </div>
  `,
})
export class KanbanBoardComponent {
  columns = [
    { id: 'todo', title: 'To Do', items: [{ id: 1, title: 'Task 1' }] },
    { id: 'doing', title: 'Doing', items: [{ id: 2, title: 'Task 2' }] },
    { id: 'done', title: 'Done', items: [{ id: 3, title: 'Task 3' }] },
  ];
  allGroupNames = ['todo', 'doing', 'done'];
}

Virtual Scroll

@Component({
  standalone: true,
  imports: [
    LibsUiComponentsDragContainerDirective,
    LibsUiDragItemDirective,
    LibsUiComponentsDragScrollDirective,
    LibsUiDragItemInContainerVirtualScrollDirective,
  ],
  template: `
    <virtual-scroller #scroll [items]="items">
      <div
        LibsUiComponentsDragContainerDirective
        LibsUiComponentsDragScrollDirective
        [(items)]="items">
        <div
          *ngFor="let item of scroll.viewPortItems"
          LibsUiDragItemDirective
          [item]="item"
          [itemInContainerVirtualScroll]="true"
          [fieldId]="'id'">
          {{ item.name }}
        </div>
      </div>
    </virtual-scroller>
  `,
})
export class VirtualScrollDragDropComponent {
  items = Array.from({ length: 1000 }, (_, i) => ({ id: i, name: `Item ${i}` }));
}

Custom Boundary - Giới hạn vùng kéo thả

@Component({
  standalone: true,
  imports: [LibsUiComponentsDragContainerDirective, LibsUiDragItemDirective],
  template: `
    <div class="relative w-[500px] h-[500px] border-2 border-gray-800">
      <div
        LibsUiComponentsDragContainerDirective
        [(items)]="items">
        <div
          *ngFor="let item of items"
          LibsUiDragItemDirective
          [item]="item"
          [dragBoundary]="true">
          {{ item.name }}
        </div>
      </div>
    </div>
  `,
})
export class CustomBoundaryComponent {
  items = [
    { id: 1, name: 'Bounded Item 1' },
    { id: 2, name: 'Bounded Item 2' },
  ];
}

API Reference

LibsUiComponentsDragContainerDirective

Selector: [LibsUiComponentsDragContainerDirective]

Inputs

| Property | Type | Default | Mô tả | |---|---|---|---| | [acceptDragSameGroup] | boolean | false | Cho phép kéo thả trong cùng group. | | [directionDrag] | 'horizontal' \| 'vertical' | undefined | Hướng kéo thả (ngang hoặc dọc). | | [disableDragContainer] | boolean | undefined | Vô hiệu hóa toàn bộ kéo thả trong container. | | [dropToGroupName] | Array<string> \| null | null | Danh sách group name được phép drop vào container này. | | [groupName] | string | 'groupDragAndDropDefault' | Tên group để định danh container. | | [(items)] | Array<unknown> | bắt buộc | Danh sách items (two-way binding). | | [mode] | 'move' \| 'copy' | 'move' | Chế độ kéo thả: di chuyển hoặc sao chép. | | [placeholder] | boolean | true | Hiển thị placeholder tại vị trí drop. | | [stylesOverride] | Array<{className, styles}> | undefined | Ghi đè styles cho container. | | [viewEncapsulation] | 'emulated' \| 'none' | 'emulated' | Chế độ View Encapsulation cho styles. |

Outputs

| Property | Type | Mô tả | |---|---|---| | (outDragEndContainer) | IDragEnd | Emit khi kết thúc kéo phần tử trong container. | | (outDragLeaveContainer) | IDragLeave | Emit khi phần tử kéo rời khỏi container. | | (outDragOverContainer) | IDragOver | Emit khi phần tử kéo di chuyển qua container. | | (outDragStartContainer) | IDragStart | Emit khi bắt đầu kéo phần tử trong container. | | (outDroppedContainer) | IDrop | Emit khi thả phần tử vào container. | | (outDroppedContainerEmpty) | IDrop | Emit khi thả phần tử vào container rỗng. | | (outFunctionControl) | IDragDropFunctionControlEvent | Emit object chứa các hàm điều khiển container. |

LibsUiDragItemDirective

Selector: [LibsUiDragItemDirective]

Inputs

| Property | Type | Default | Mô tả | |---|---|---|---| | [disable] | boolean | undefined | Vô hiệu hóa kéo thả cho item này. | | [dragBoundary] | boolean | undefined | Giới hạn kéo trong vùng boundary của container. | | [dragBoundaryAcceptMouseLeaveContainer] | boolean | undefined | Cho phép chuột rời container khi đang giới hạn boundary. | | [dragRootElement] | boolean | undefined | Sử dụng root element làm gốc kéo. | | [elementContainer] | HTMLElement | undefined | Element container tùy chỉnh cho item. | | [fieldId] | string | '' | Tên field dùng làm ID định danh item. | | [ignoreStopEvent] | boolean | undefined | Bỏ qua stop event mặc định. | | [ignoreUserSelectNone] | boolean | undefined | Bỏ qua việc set user-select: none khi kéo. | | [item] | any | undefined | Dữ liệu của item. | | [itemInContainerVirtualScroll] | boolean | undefined | Đánh dấu item nằm trong virtual scroll container. | | [onlyMouseDownStopEvent] | boolean | undefined | Chỉ stop event khi mousedown. | | [throttleTimeHandlerDraggingEvent] | number | 0 | Thời gian throttle (ms) cho dragging event. | | [zIndex] | number | 1300 | z-index của phần tử khi đang kéo. |

Outputs

| Property | Type | Mô tả | |---|---|---| | (outDragEnd) | IDragEnd | Emit khi kết thúc kéo item. | | (outDragLeave) | IDragLeave | Emit khi item rời khỏi vùng drop. | | (outDragOver) | IDragOver | Emit khi item di chuyển qua vùng drop. | | (outDragStart) | IDragStart | Emit khi bắt đầu kéo item. | | (outDropped) | IDrop | Emit khi item được thả. |

LibsUiComponentsDragScrollDirective

Selector: [LibsUiComponentsDragScrollDirective]

Directive tự động scroll container khi phần tử kéo di chuyển gần mép.

Inputs

| Property | Type | Default | Mô tả | |---|---|---|---| | [ignoreAutoScroll] | boolean | undefined | Tắt tính năng auto scroll. | | [widthZoneDetect] | number | 16 | Chiều rộng vùng phát hiện (px) gần mép để kích hoạt scroll. | | [movementLength] | number | 6 | Khoảng cách scroll mỗi lần (px). | | [rootElementScroll] | HTMLElement | undefined | Element gốc để scroll (mặc định là host element). | | [virtualScrollerComponent] | VirtualScrollerComponent | undefined | Tham chiếu đến VirtualScrollerComponent. |

LibsUiDragItemInContainerVirtualScrollDirective

Selector: [LibsUiDragItemInContainerVirtualScrollDirective]

Directive hỗ trợ kéo thả item trong container sử dụng virtual scroll (@iharbeck/ngx-virtual-scroller).

Types & Interfaces

export interface IDragging {
  mousePosition: IMousePosition;
  elementDrag: HTMLElement;
  elementKeepContainer?: boolean;
  itemDragInfo?: IItemDragInfo;
}

export interface IDragStart {
  mousePosition: IMousePosition;
  elementDrag: HTMLElement;
  itemDragInfo?: IItemDragInfo;
}

export interface IDragOver {
  mousePosition: IMousePosition;
  elementDrag: HTMLElement;
  elementDragOver: HTMLElement;
  itemDragInfo?: IItemDragInfo;
}

export interface IDragLeave {
  elementDrag: HTMLElement;
  elementDragLeave: HTMLElement;
  itemDragInfo?: IItemDragInfo;
}

export interface IDragEnd {
  mousePosition: IMousePosition;
  elementDrag: HTMLElement;
  itemDragInfo?: IItemDragInfo;
}

export interface IDrop {
  elementDrag: HTMLElement;
  elementDrop: HTMLElement;
  itemDragInfo?: IItemDragInfo;
}

export interface IItemDragInfo {
  item: object;
  itemsMove?: WritableSignal<Array<unknown>>;
  indexDrag?: number;
  indexDrop?: number;
  itemsDrag: WritableSignal<Array<unknown>>;
  itemsDrop?: WritableSignal<Array<unknown>>;
  containerDrag?: HTMLElement;
  containerDrop?: HTMLElement;
}

export interface IDragItemInContainerVirtualScroll {
  itemDragInfo?: IItemDragInfo;
  elementDrag: HTMLElement;
  distanceStartElementAndMouseTop: number;
  distanceStartElementAndMouseLeft: number;
  elementContainer?: HTMLElement;
  dragBoundary?: boolean;
  dragBoundaryAcceptMouseLeaveContainer?: boolean;
  ignoreStopEvent?: boolean;
  ignoreUserSelectNone: boolean;
}

export interface IMousePosition {
  clientX: number;
  clientY: number;
}

export interface IDragDropFunctionControlEvent {
  setAttributeElementAndItemDrag: () => Promise<void>;
}

Tùy chỉnh giao diện (Styling)

CSS Classes mặc định

| Class | Mô tả | |---|---| | .libs-ui-drag-drop-container | Class cho container kéo thả. | | .libs-ui-drag-drop-item | Class cho mỗi item kéo thả. | | .libs-ui-drag-drop-item-dragging | Class được thêm vào item đang được kéo. | | .libs-ui-drag-drop-item-placeholder | Class cho placeholder tại vị trí drop. |

Sử dụng stylesOverride

Ghi đè styles thông qua input [stylesOverride] trên Container directive:

@Component({
  standalone: true,
  imports: [LibsUiComponentsDragContainerDirective, LibsUiDragItemDirective],
  template: `
    <div
      LibsUiComponentsDragContainerDirective
      [(items)]="items"
      [stylesOverride]="customStyles">
      <div
        *ngFor="let item of items"
        LibsUiDragItemDirective
        [item]="item">
        {{ item.name }}
      </div>
    </div>
  `,
})
export class CustomStyledComponent {
  items = [{ id: 1, name: 'Item 1' }];

  customStyles = [
    {
      className: 'custom-drag-container',
      styles: `
        .custom-drag-container {
          background: #f5f5f5;
          border-radius: 8px;
          padding: 16px;
        }
      `,
    },
    {
      className: 'custom-drag-item-dragging',
      styles: `
        .custom-drag-item-dragging {
          opacity: 0.6;
          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        }
      `,
    },
  ];
}

Demo

  • Local: http://localhost:4500/drag-drop