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

v0.2.357-4

Published

> Directive tạo bản đồ thu nhỏ (minimap) cho các vùng nội dung lớn, hỗ trợ điều hướng trực quan và đồng bộ cuộn hai chiều.

Readme

@libs-ui/components-minimap

Directive tạo bản đồ thu nhỏ (minimap) cho các vùng nội dung lớn, hỗ trợ điều hướng trực quan và đồng bộ cuộn hai chiều.

Giới thiệu

LibsUiComponentsMinimapDirective là một standalone directive Angular giúp tạo ra một minimap nổi (floating) hiển thị toàn bộ nội dung được thu nhỏ theo tỷ lệ, đồng thời cho phép người dùng kéo viewport trên minimap để cuộn nội dung chính. Directive sử dụng MutationObserver để tự động cập nhật khi DOM thay đổi và lắng nghe sự kiện scroll cùng resize để đồng bộ trạng thái liên tục.

Tính năng

  • Standalone Directive: Tích hợp trực tiếp bằng attribute selector, không cần module.
  • Đồng bộ hai chiều: Cuộn nội dung chính cập nhật viewport trên minimap, kéo viewport trên minimap cuộn nội dung chính.
  • Tự động cập nhật DOM: Dùng MutationObserver để phát hiện thay đổi cấu trúc hoặc thuộc tính và vẽ lại minimap.
  • Hỗ trợ Scale Transform: Nhận diện tỷ lệ phóng to/thu nhỏ của nội dung (qua CSS transform matrix) để tính toán viewport chính xác.
  • Tùy biến cao: Cấu hình vị trí (top/bottom/left/right), kích thước, border, borderRadius, background, z-index cho cả khung minimap và khung viewport.
  • Function Controls: Cung cấp API show(), hidden(), toggle() để điều khiển minimap từ code.
  • Tự dọn bộ nhớ: Dùng DestroyRef + takeUntilDestroyedMutationObserver.disconnect() khi component destroy.

Khi nào sử dụng

  • Khi có các vùng chứa nội dung lớn vượt quá kích thước màn hình (sơ đồ, bản đồ văn phòng, diagram editor, canvas đồ họa).
  • Khi người dùng cần cái nhìn tổng quan về cấu trúc nội dung và khả năng di chuyển nhanh đến các vùng cụ thể.
  • Thay thế hoặc hỗ trợ cho thanh cuộn truyền thống trên các giao diện phức tạp, nhiều node.
  • Khi cần tích hợp điều hướng vào các màn hình diagram (flow chart, org chart, network topology).

Cài đặt

npm install @libs-ui/components-minimap

Import

import { LibsUiComponentsMinimapDirective } from '@libs-ui/components-minimap';
import { IMiniMapFunctionControl, IMiniMapStyleConfig } from '@libs-ui/components-minimap';

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

Ví dụ sử dụng

Ví dụ 1 — Cơ bản

Gán directive vào bất kỳ phần tử nào có overflow: auto hoặc overflow: scroll.

// your.component.ts
import { LibsUiComponentsMinimapDirective } from '@libs-ui/components-minimap';

@Component({
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [LibsUiComponentsMinimapDirective],
  template: `
    <div
      class="w-full h-[500px] overflow-auto border rounded-lg"
      LibsUiComponentsMinimapDirective>
      <div class="w-[3000px] h-[3000px] p-10 bg-gray-50">
        <p class="text-2xl font-bold">Nội dung lớn</p>
      </div>
    </div>
  `,
})
export class BasicMinimapComponent {}

Ví dụ 2 — Tùy chỉnh vị trí và style, điều khiển qua FunctionControl

Đặt minimap ở góc dưới bên phải, tùy chỉnh màu viền viewport, và lấy controls để ẩn/hiện từ nút bấm.

// diagram.component.ts
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
import { LibsUiComponentsMinimapDirective, IMiniMapFunctionControl } from '@libs-ui/components-minimap';

@Component({
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [LibsUiComponentsMinimapDirective],
  template: `
    <div class="flex gap-2 mb-3">
      <button class="px-4 py-2 bg-blue-600 text-white rounded text-sm" (click)="handlerShow($event)">
        Hiện Minimap
      </button>
      <button class="px-4 py-2 border border-blue-600 text-blue-600 rounded text-sm" (click)="handlerHide($event)">
        Ẩn Minimap
      </button>
      <button class="px-4 py-2 border border-gray-300 rounded text-sm" (click)="handlerToggle($event)">
        Toggle
      </button>
    </div>

    <div
      class="w-full h-[500px] overflow-auto border rounded-xl bg-black relative"
      LibsUiComponentsMinimapDirective
      [styleImageElement]="{ bottom: 20, right: 20, width: 220, height: 130, background: 'white', border: '1px solid #cbd5e1', zIndex: 10 }"
      [styleRectangleElement]="{ border: '2px solid #3b82f6', background: 'rgba(59, 130, 246, 0.1)' }"
      [minimapCloneBackground]="'white'"
      [timerStartDrawMinimap]="300"
      (outFunctionControls)="handlerControlsInit($event)">
      <div class="w-[3000px] h-[3000px] p-10">
        <div class="grid grid-cols-6 gap-10">
          @for (item of items(); track item) {
            <div class="h-64 bg-gray-800 rounded-2xl border border-dashed border-gray-600 flex items-center justify-center">
              <span class="text-4xl font-black text-gray-500">{{ item }}</span>
            </div>
          }
        </div>
      </div>
    </div>
  `,
})
export class DiagramComponent {
  protected items = signal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
  private minimapControls?: IMiniMapFunctionControl;

  handlerControlsInit(controls: IMiniMapFunctionControl) {
    this.minimapControls = controls;
    this.minimapControls.show();
  }

  handlerShow(event: MouseEvent) {
    event.stopPropagation();
    this.minimapControls?.show();
  }

  handlerHide(event: MouseEvent) {
    event.stopPropagation();
    this.minimapControls?.hidden();
  }

  handlerToggle(event: MouseEvent) {
    event.stopPropagation();
    this.minimapControls?.toggle();
  }
}

Ví dụ 3 — Scroll theo phần tử khác (elementScroll)

Khi vùng cuộn thực tế là một phần tử khác với host element của directive.

// canvas-layout.component.ts
import { Component, ChangeDetectionStrategy, ViewChild, ElementRef } from '@angular/core';
import { LibsUiComponentsMinimapDirective, IMiniMapFunctionControl } from '@libs-ui/components-minimap';

@Component({
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [LibsUiComponentsMinimapDirective],
  template: `
    <div
      LibsUiComponentsMinimapDirective
      [elementScroll]="scrollContainer"
      [styleImageElement]="{ top: 10, right: 10, width: 180, height: 100 }"
      (outFunctionControls)="handlerControlsInit($event)">

      <div #scrollContainer class="w-full h-[600px] overflow-auto border rounded">
        <div class="w-[4000px] h-[4000px] bg-gray-50 p-8">
          <p class="text-xl font-semibold">Canvas nội dung</p>
        </div>
      </div>
    </div>
  `,
})
export class CanvasLayoutComponent {
  @ViewChild('scrollContainer') scrollContainerRef!: ElementRef<HTMLElement>;
  private minimapControls?: IMiniMapFunctionControl;

  get scrollContainer(): HTMLElement {
    return this.scrollContainerRef?.nativeElement;
  }

  handlerControlsInit(controls: IMiniMapFunctionControl) {
    this.minimapControls = controls;
  }
}

Ví dụ 4 — Nội dung có scale transform (diagram zoom)

Khi nội dung bên trong có phần tử áp dụng CSS transform: scale(...), truyền class vào classNameFlagElementScale để directive tính toán tỷ lệ đúng.

// zoom-diagram.component.ts
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { LibsUiComponentsMinimapDirective } from '@libs-ui/components-minimap';

@Component({
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [LibsUiComponentsMinimapDirective],
  template: `
    <div
      class="w-full h-[600px] overflow-auto border rounded"
      LibsUiComponentsMinimapDirective
      classNameFlagElementScale="my-diagram-scale-element"
      [styleImageElement]="{ bottom: 16, right: 16, width: 200, height: 120 }">

      <div class="my-diagram-scale-element" style="transform: scale(0.8); transform-origin: top left;">
        <!-- Nội dung diagram được zoom out 80% -->
        <div class="w-[5000px] h-[5000px] bg-white">
          <p class="text-lg font-bold p-4">Diagram đã scale 80%</p>
        </div>
      </div>
    </div>
  `,
})
export class ZoomDiagramComponent {}

@Input()

| Input | Type | Default | Mô tả | Ví dụ | |---|---|---|---|---| | [elementScroll] | HTMLElement \| undefined | Host element | Phần tử đích để theo dõi và đồng bộ cuộn. Nếu không truyền, sử dụng host element của directive. | [elementScroll]="scrollContainerRef" | | [timerStartDrawMinimap] | number \| undefined | 250 | Thời gian delay (ms) trước khi vẽ minimap lần đầu sau ngAfterViewInit. Tăng giá trị nếu nội dung cần thêm thời gian render. | [timerStartDrawMinimap]="500" | | [styleImageElement] | IMiniMapStyleConfig \| undefined | undefined | Cấu hình style cho khung container bao ngoài của minimap (vị trí, kích thước, màu nền, viền, z-index). | [styleImageElement]="{ bottom: 20, right: 20, width: 220, height: 130 }" | | [styleRectangleElement] | IMiniMapStyleConfig \| undefined | undefined | Cấu hình style cho khung viewport (hình chữ nhật di động) trên minimap thể hiện vùng đang xem. | [styleRectangleElement]="{ border: '2px solid #3b82f6', background: 'rgba(59,130,246,0.1)' }" | | [classNameFlagElementScale] | string \| undefined | 'libs-ui-minimap-flag-element-scale' | Tên class CSS dùng để xác định phần tử chứa CSS transform: scale(...). Directive đọc matrix để tính toán tỷ lệ viewport chính xác khi nội dung bị zoom. | classNameFlagElementScale="my-scale-node" | | [minimapCloneBackground] | string \| undefined | undefined | Override giá trị background của cloned node trên minimap. Hữu ích khi element gốc có nền trong suốt hoặc màu tối, muốn minimap hiển thị với nền sáng hơn. | [minimapCloneBackground]="'white'" |

@Output()

| Output | Type | Mô tả | Handler TS | Binding HTML | |---|---|---|---|---| | (outFunctionControls) | IMiniMapFunctionControl | Phát ra object chứa ba hàm điều khiển minimap sau khi directive khởi tạo xong. Dùng để ẩn/hiện/toggle minimap từ code component. | handlerControlsInit(controls: IMiniMapFunctionControl): void { event.stopPropagation(); this.minimapControls = controls; } | (outFunctionControls)="handlerControlsInit($event)" |

IMiniMapFunctionControl — Methods

Các hàm điều khiển được nhận qua (outFunctionControls):

| Method | Signature | Mô tả | |---|---|---| | show() | () => void | Hiển thị minimap (xóa class invisible khỏi container và viewport). | | hidden() | () => void | Ẩn minimap (thêm class invisible vào container và viewport). | | toggle() | () => void | Chuyển đổi trạng thái ẩn/hiện dựa trên trạng thái hiện tại. |

// Nhận controls và gọi các method
handlerControlsInit(controls: IMiniMapFunctionControl): void {
  this.minimapControls = controls;
  this.minimapControls.show(); // Hiện ngay sau khi init
}

handlerToggleMinimap(event: MouseEvent): void {
  event.stopPropagation();
  this.minimapControls?.toggle();
}

Types & Interfaces

import { IMiniMapStyleConfig, IMiniMapFunctionControl } from '@libs-ui/components-minimap';

/**
 * Cấu hình style và vị trí cho container minimap hoặc viewport rectangle.
 * Tất cả các trường đều là tùy chọn — chỉ truyền những gì cần override.
 */
export interface IMiniMapStyleConfig {
  /** Z-index của phần tử. Mặc định container: 1300, viewport: 1301 */
  zIndex?: number;
  /** CSS border string. VD: '1px solid #E6E7EA' */
  border?: string;
  /** Border radius tính bằng px. Mặc định container: 4 */
  borderRadius?: number;
  /** Màu nền. Mặc định container: 'white' */
  background?: string;
  /** Khoảng cách từ cạnh trên (px). Áp dụng khi không có bottom. */
  top?: number;
  /** Khoảng cách từ cạnh trái (px). Áp dụng khi không có right. */
  left?: number;
  /** Khoảng cách từ cạnh dưới (px). Ưu tiên hơn top khi cả hai được truyền. */
  bottom?: number;
  /** Khoảng cách từ cạnh phải (px). Ưu tiên hơn left khi cả hai được truyền. */
  right?: number;
  /** Chiều rộng container tính bằng px. Mặc định: 198 */
  width?: number;
  /** Chiều cao container tính bằng px. Mặc định: 108 */
  height?: number;
  /** Tọa độ X (dùng nội bộ, không cần truyền thủ công) */
  x?: number;
  /** Tọa độ Y (dùng nội bộ, không cần truyền thủ công) */
  y?: number;
  /** CSS overflow. Mặc định: 'hidden' */
  overflow?: string;
}

/**
 * Object chứa các hàm điều khiển minimap, nhận được qua (outFunctionControls).
 */
export interface IMiniMapFunctionControl {
  /** Hiển thị minimap */
  show: () => void;
  /** Ẩn minimap */
  hidden: () => void;
  /** Chuyển đổi ẩn/hiện */
  toggle: () => void;
}

Lưu ý quan trọng

⚠️ Container phải có overflow scroll/auto: Vùng chứa host element (hoặc phần tử truyền vào elementScroll) bắt buộc phải có overflow: auto hoặc overflow: scroll. Nếu không, sự kiện scroll sẽ không được kích hoạt và minimap sẽ không đồng bộ.

⚠️ Minimap được append vào document.body: Container minimap được tạo động và gắn thẳng vào document.body với position: fixed. Mặc định z-index là 1300. Đảm bảo không có phần tử nào trong ứng dụng có z-index cao hơn che khuất minimap ngoài ý muốn.

⚠️ Hiệu năng với DOM lớn: Directive dùng cloneNode(true) để render nội dung lên minimap mỗi khi có thay đổi DOM (MutationObserver). Với các DOM tree cực lớn (hàng nghìn node), tần suất mutation cao có thể ảnh hưởng đến hiệu năng. Cân nhắc giảm số lượng node hoặc tối ưu cấu trúc DOM.

⚠️ timerStartDrawMinimap: Minimap không vẽ ngay lập tức sau ngAfterViewInit mà chờ một khoảng delay (mặc định 250ms) để đảm bảo nội dung đã render xong. Nếu nội dung cần thêm thời gian (load ảnh, animation), tăng giá trị này lên.

⚠️ Scale transform: Khi nội dung bên trong có phần tử áp dụng CSS transform: scale(), truyền đúng tên class vào classNameFlagElementScale. Nếu không, viewport trên minimap sẽ không khớp với vùng thực sự đang hiển thị.

⚠️ Minimap ẩn theo mặc định: Sau khi init, minimap được tạo ra nhưng ở trạng thái invisible. Cần gọi controls.show() trong handler (outFunctionControls) để hiện ngay, hoặc gọi sau theo logic nghiệp vụ.

Demo

npx nx serve core-ui

Truy cập: http://localhost:4500/components/minimap

hoặc: http://localhost:4500/directives/minimap