@libs-ui/services-grid-layout 0.2.357-20 → 0.2.357-22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,6 +17,13 @@ export declare class LibsUiGridLayoutService {
17
17
  private readonly treeService;
18
18
  /** Lượt tải component đang chờ, theo id khối — để huỷ khi khối bị gỡ giữa chừng. */
19
19
  private readonly pendingLoads;
20
+ /**
21
+ * Bộ theo dõi bề rộng của từng lưới LỒNG, để ngắt khi service bị huỷ.
22
+ *
23
+ * 🔴 Giữ tham chiếu là BẮT BUỘC: `ResizeObserver` không tự tắt theo vòng đời component. Không dọn
24
+ * thì mỗi lần nạp lại bố cục (đổi tab) lại chồng thêm một bộ, và chúng vẫn chạy trên DOM đã gỡ.
25
+ */
26
+ private readonly nestedResizeObservers;
20
27
  constructor();
21
28
  private config?;
22
29
  private readonly componentRefs;
@@ -24,6 +31,7 @@ export declare class LibsUiGridLayoutService {
24
31
  private readonly editMode;
25
32
  /** tăng mỗi lần cây đổi — component con đọc để biết phải tính lại */
26
33
  private readonly version;
34
+ private readonly zoomLevel;
27
35
  /**
28
36
  * Signal chỉ đọc để nơi dùng theo dõi.
29
37
  *
@@ -35,6 +43,7 @@ export declare class LibsUiGridLayoutService {
35
43
  readonly IsEditMode: import("@angular/core").Signal<boolean>;
36
44
  /** tăng mỗi lần cây hoặc cấu hình đổi */
37
45
  readonly Version: import("@angular/core").Signal<number>;
46
+ readonly Zoom: import("@angular/core").Signal<number>;
38
47
  /**
39
48
  * trần số cấp container lồng nhau — canvas đọc để biết có cho chuyển thành container không */
40
49
  private maxContainerDepth;
@@ -58,6 +67,30 @@ export declare class LibsUiGridLayoutService {
58
67
  * @param depth 0 = lưới cấp trang, ≥ 1 = lưới lồng trong container
59
68
  */
60
69
  private buildGridsterOptions;
70
+ /**
71
+ * Bắt lưới LỒNG đo lại NGAY khi khung chứa nó đổi bề rộng — dùng lúc người dùng kéo mép khối cha.
72
+ *
73
+ * Gridster có sẵn đường tự đo (`resize$`), nhưng luồng đó là `switchMap(() => timer(100))`: mỗi
74
+ * lần phát lại huỷ timer trước, mà kéo chuột bắn dày hơn 100ms nên timer không bao giờ tới đích.
75
+ * Đo được (11/09/2026): kéo mép nhóm thì lưới con co theo cha từng khung hình (234 → 384 px) còn
76
+ * THẺ con đứng im ở 106 px suốt cú kéo, chỉ nhảy đúng sau khi thả tay.
77
+ *
78
+ * Giữ CẢ HAI đường: `itemResizeCallback` bắn đồng bộ ngay khi gridster ghi bề rộng mới lên khối
79
+ * (sớm hơn `ResizeObserver` một khung hình) lo độ tức thì; `ResizeObserver` lo những lần khung đổi
80
+ * vì lý do khác — đổi zoom, thu gọn cột bên trái.
81
+ *
82
+ * Tắt bằng `config.autoResizeNestedGrids: false`.
83
+ */
84
+ private buildNestedResizeHooks;
85
+ /** Gọi `resize()` của một thẻ `<gridster>` qua chính instance mà nó tự gắn lên phần tử. */
86
+ private resizeGridsterElement;
87
+ /**
88
+ * Gắn instance lên chính phần tử `<gridster>` rồi theo dõi bề rộng của nó.
89
+ *
90
+ * Phải gắn ngược lên DOM vì `itemResizeCallback` của khối CHA chỉ cầm được `HTMLElement`, không có
91
+ * đường nào khác đi từ đó về instance của lưới lồng bên trong.
92
+ */
93
+ private registerNestedGrid;
61
94
  /**
62
95
  *
63
96
  * Gắn component của nơi dùng lên một khối.
@@ -116,8 +149,43 @@ export declare class LibsUiGridLayoutService {
116
149
  * @returns khối vừa tạo (kèm `id` vừa sinh), hoặc `undefined` khi không tìm thấy container.
117
150
  */
118
151
  addBlock(item: T_gridLayout_newNode, parentId?: string): T_gridLayout_node | undefined;
152
+ /**
153
+ * Thêm một khối tại toạ độ (col, row) cụ thể và tự động đẩy các khối va chạm xuống dưới.
154
+ */
155
+ insertBlockWithCollision(item: T_gridLayout_newNode, targetCol?: number, targetRow?: number, parentId?: string): T_gridLayout_node | undefined;
156
+ private readonly activeDragPayload;
157
+ /** Đặt payload khi bắt đầu kéo từ bên ngoài vào (ví dụ: palette, drawer) */
158
+ setDragPayload(payload: unknown): void;
159
+ /** Lấy payload đang kéo */
160
+ getDragPayload(): unknown;
161
+ /** Xoá payload khi kết thúc kéo */
162
+ clearDragPayload(): void;
119
163
  /** Xoá một khối. Xoá container là xoá cả ruột. Trả `false` khi không xoá được (vd khối gốc). */
120
164
  removeBlock(id: string): boolean;
165
+ /**
166
+ * Số cột thật của lưới cấp trang.
167
+ *
168
+ * 🔴 Mặc định phải TRÙNG `minCols/maxCols` của `gridLayoutRootConfig()`. Trước đây chỗ này trả 24
169
+ * trong khi lưới vẽ 12: nơi dùng không truyền `cols` thì va chạm, `toContainer` và `compact` tính
170
+ * theo 24 cột còn màn hình chỉ có 12 — khối rơi ra ngoài mép phải mà không ai hiểu vì sao.
171
+ */
172
+ get totalColumns(): number;
173
+ /**
174
+ * Đổi số cột / số hàng của một khối rồi vẽ lại.
175
+ *
176
+ * Kéo `cols` rộng quá mép phải thì dịch khối sang trái cho vừa, thay vì để nó tràn ra ngoài lưới.
177
+ */
178
+ resizeBlock(id: string, size: {
179
+ cols?: number;
180
+ rows?: number;
181
+ }): boolean;
182
+ /**
183
+ * Nhân bản một khối — đặt kế bên phải nếu còn chỗ, không thì xuống dòng dưới.
184
+ *
185
+ * `transformData` để nơi dùng đổi phần dữ liệu riêng của mình (vd nối thêm "(Bản sao)" vào tiêu
186
+ * đề); thư viện không tự đoán trường nào là tiêu đề.
187
+ */
188
+ duplicateBlock(id: string, transformData?: (data: unknown) => unknown): T_gridLayout_node | undefined;
121
189
  /** Biến khối đơn thành CONTAINER — nội dung cũ thành khối con đầu tiên, không mất dữ liệu. */
122
190
  toContainer(id: string): boolean;
123
191
  /** Đưa CONTAINER về khối đơn — khối con được nâng lên cấp trên, không bị xoá. */
@@ -137,6 +205,31 @@ export declare class LibsUiGridLayoutService {
137
205
  findBlock(id: string): T_gridLayout_node | undefined;
138
206
  /** Tìm khối cha trực tiếp của một khối. */
139
207
  findParentBlock(id: string): T_gridLayout_node | undefined;
208
+ /**
209
+ * Đặt tỷ lệ thu phóng của canvas (kẹp giữa min và max).
210
+ *
211
+ * @param value Tỷ lệ (0.4 = 40%, 1.0 = 100%, 1.5 = 150%)
212
+ * @param persistKey Khóa localStorage để lưu lại (bỏ trống thì lấy từ config)
213
+ */
214
+ setZoom(value: number, persistKey?: string): void;
215
+ /** Phóng to thêm một bước (mặc định 0.05 tức 5%) */
216
+ zoomIn(step?: number, persistKey?: string): void;
217
+ /** Thu nhỏ bớt một bước (mặc định 0.05 tức 5%) */
218
+ zoomOut(step?: number, persistKey?: string): void;
219
+ /** Đặt lại tỷ lệ thu phóng gốc 100% (1.0) */
220
+ resetZoom(persistKey?: string): void;
221
+ /**
222
+ * Tự động tính toán và đặt tỷ lệ thu phóng để canvas vừa vặn với chiều rộng container.
223
+ *
224
+ * @param containerWidth Chiều rộng container đo được (px)
225
+ * @param stageWidth Chiều rộng chuẩn của canvas (mặc định 1668px)
226
+ * @param padding Khoảng cách lề hai bên (mặc định 48px)
227
+ * @param persistKey Khóa localStorage
228
+ * @returns Tỷ lệ thu phóng vừa tính
229
+ */
230
+ fitToScreen(containerWidth: number, stageWidth?: number, padding?: number, persistKey?: string): number;
231
+ /** Kiểm tra tính năng thu phóng có được cấu hình bật không */
232
+ isZoomConfigured(): boolean;
140
233
  /**
141
234
  * Cổng DUY NHẤT cho canvas của thư viện. Nơi dùng KHÔNG gọi tới đây.
142
235
  *
@@ -147,6 +240,13 @@ export declare class LibsUiGridLayoutService {
147
240
  get FunctionsControl(): {
148
241
  maxContainerDepth: () => number;
149
242
  containerPadding: () => number;
243
+ isZoomEnabled: () => boolean;
244
+ zoom: () => number;
245
+ setZoom: (v: number, key?: string) => void;
246
+ zoomIn: (step?: number, key?: string) => void;
247
+ zoomOut: (step?: number, key?: string) => void;
248
+ resetZoom: (key?: string) => void;
249
+ fitToScreen: (cW: number, sW?: number, pad?: number, key?: string) => number;
150
250
  gridsterOptions: (depth: number, cb?: () => void) => GridsterConfig;
151
251
  attachNode: (node: T_gridLayout_node, depth: number, host: ViewContainerRef) => void;
152
252
  detachNode: (nodeId: string) => void;
package/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from './grid-layout.service';
2
2
  export * from './canvas/grid-layout-canvas.component';
3
3
  export * from './interfaces/grid-layout.interface';
4
+ export * from './defines/collision.define';
5
+ export * from './drag-controller';
@@ -130,6 +130,22 @@ export type T_gridLayout_config = {
130
130
  cols?: number;
131
131
  /** chiều cao một hàng, px (chế độ lưới). Mặc định 56 */
132
132
  rowHeight?: number;
133
+ /**
134
+ * Số hàng TỐI THIỂU của lưới CẤP TRANG. Mặc định 2.
135
+ *
136
+ * Đặt lớn hơn khi muốn ô li phủ kín màn hình ngay cả lúc bố cục còn ít khối — lưới chỉ cao bằng
137
+ * khối cuối cùng thì phần dưới trống trơn, người dùng không biết còn thả được vào đâu.
138
+ */
139
+ minRows?: number;
140
+ /** Số hàng TỐI THIỂU của lưới bên TRONG container. Mặc định 1 */
141
+ nestedMinRows?: number;
142
+ /**
143
+ * Cho lưới LỒNG tự đo lại khi khung chứa nó đổi bề rộng. Mặc định BẬT.
144
+ *
145
+ * Tắt đi thì lúc kéo mép khối cha, thẻ con đứng im tới khi thả tay — chỉ tắt nếu nơi dùng tự lo
146
+ * việc đo lại.
147
+ */
148
+ autoResizeNestedGrids?: boolean;
133
149
  /** khe giữa hai khối ở lưới CẤP TRANG, px. Mặc định 12 */
134
150
  margin?: number;
135
151
  /** khe giữa hai khối bên TRONG container, px. Mặc định 2 — khối con là dòng thông tin của cùng
@@ -162,6 +178,30 @@ export type T_gridLayout_config = {
162
178
  allowOverlap?: boolean;
163
179
  /** trần số cấp container lồng nhau. Mặc định 1 (container không chứa container) */
164
180
  maxContainerDepth?: number;
181
+ /** Bật tính năng thu phóng (zoom) canvas. Mặc định false */
182
+ enableZoom?: boolean;
183
+ /** Tỷ lệ thu phóng ban đầu. Mặc định 1 (100%) */
184
+ initialZoom?: number;
185
+ /** Giới hạn thu phóng tối thiểu. Mặc định 0.4 (40%) */
186
+ zoomMin?: number;
187
+ /** Giới hạn thu phóng tối đa. Mặc định 1.5 (150%) */
188
+ zoomMax?: number;
189
+ /** Bước nhảy mỗi lần tăng/giảm thu phóng. Mặc định 0.05 (5%) */
190
+ zoomStep?: number;
191
+ /** Chiều rộng chuẩn của canvas stage, px. Mặc định 1668 */
192
+ stageWidth?: number;
193
+ /** Chiều cao tối thiểu của canvas stage, px. Mặc định 937 */
194
+ stageMinHeight?: number;
195
+ /** Khóa lưu tỷ lệ thu phóng vào localStorage (vd 'ocb_bi_canvas_zoom') */
196
+ storageKey?: string;
197
+ /** Tự động vừa màn hình khi tải trang lần đầu nếu khung nhìn nhỏ hơn stage. Mặc định true */
198
+ autoFitOnLoad?: boolean;
199
+ };
200
+ /** Lựa chọn tỷ lệ thu phóng trên menu dropdown */
201
+ export type T_gridLayout_zoomPreset = {
202
+ /** Khoá i18n, KHÔNG phải chữ hiển thị — template dịch qua `translate` pipe. */
203
+ label: string;
204
+ value: number;
165
205
  };
166
206
  /** Sự kiện thư viện bắn ra khi bố cục đổi */
167
207
  export type T_gridLayout_changeEvent = {
@@ -169,3 +209,71 @@ export type T_gridLayout_changeEvent = {
169
209
  /** khối vừa bị tác động; bỏ trống khi đổi diện rộng (sắp gọn, nạp lại) */
170
210
  nodeId?: string;
171
211
  };
212
+ /**
213
+ * Bối cảnh truyền vào `templateToolbar` — template tự vẽ thanh công cụ thay bộ nút mặc định.
214
+ *
215
+ * ```html
216
+ * <ng-template #toolbar let-node let-ctx="ctx">
217
+ * <libs_ui-components-buttons-button
218
+ * [classIconLeft]="'libs-ui-icon-remove'"
219
+ * (outClick)="ctx.remove()" />
220
+ * </ng-template>
221
+ * ```
222
+ */
223
+ export type T_gridLayout_toolbarContext = {
224
+ /** khối đang vẽ — dùng `let-node` trong template */
225
+ $implicit: T_gridLayout_node;
226
+ /** khối này là container (có ruột) hay khối đơn */
227
+ isContainer: boolean;
228
+ /** còn thêm được khối vào trong không (chưa chạm trần lồng cấp) */
229
+ canAddInside: boolean;
230
+ /** đổi được giữa khối đơn ↔ khối ghép không */
231
+ canToggleType: boolean;
232
+ /** gọi để thư viện phát `outRemoveNode` như khi bấm nút xoá mặc định */
233
+ remove: () => void;
234
+ /** gọi để thư viện phát `outAddBlockInside` */
235
+ addInside: () => void;
236
+ /** gọi để thư viện phát `outToggleType` */
237
+ toggleType: () => void;
238
+ };
239
+ /** Khung xem trước (ghost preview) khi kéo thẻ từ bên ngoài vào canvas */
240
+ export type T_gridLayout_dropGhost = {
241
+ col: number;
242
+ row: number;
243
+ cols: number;
244
+ rows: number;
245
+ pixelLeft: number;
246
+ pixelTop: number;
247
+ pixelWidth: number;
248
+ pixelHeight: number;
249
+ isInsideGroup: boolean;
250
+ targetParentId?: string;
251
+ groupTitle?: string;
252
+ cardTitle: string;
253
+ cardIconText?: string;
254
+ cardIconClass?: string;
255
+ hoveredGridster?: HTMLElement;
256
+ };
257
+ /** Sự kiện phát ra khi thả một thẻ mới từ bên ngoài vào canvas */
258
+ export type T_gridLayout_dropEvent = {
259
+ node: T_gridLayout_node;
260
+ parentId?: string;
261
+ col: number;
262
+ row: number;
263
+ cols: number;
264
+ rows: number;
265
+ rawEvent: DragEvent;
266
+ payload?: unknown;
267
+ };
268
+ /** Dữ liệu của thẻ kéo từ bên ngoài vào canvas */
269
+ export type T_gridLayout_externalDragPayload = {
270
+ kind?: string;
271
+ type?: string;
272
+ title?: string;
273
+ cols?: number;
274
+ rows?: number;
275
+ iconText?: string;
276
+ iconClass?: string;
277
+ data?: Record<string, unknown>;
278
+ [key: string]: unknown;
279
+ };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@libs-ui/services-grid-layout",
3
- "version": "0.2.357-20",
3
+ "version": "0.2.357-22",
4
4
  "peerDependencies": {
5
5
  "@angular/core": "^18.2.0",
6
6
  "@angular/common": "^18.2.0",
7
7
  "gridster18": "npm:angular-gridster2@18.0.1",
8
8
  "rxjs": "~7.8.0",
9
- "@libs-ui/components-buttons-button": "0.2.357-20",
10
- "@libs-ui/utils": "0.2.357-20"
9
+ "@libs-ui/components-buttons-button": "0.2.357-22",
10
+ "@libs-ui/utils": "0.2.357-22"
11
11
  },
12
12
  "sideEffects": false,
13
13
  "module": "fesm2022/libs-ui-services-grid-layout.mjs",
package/tree.service.d.ts CHANGED
@@ -17,6 +17,10 @@ export declare class LibsUiGridLayoutTreeService {
17
17
  * @returns khối vừa tạo (kèm `id` vừa sinh), hoặc `undefined` khi không tìm thấy container.
18
18
  */
19
19
  addChild(root: T_gridLayout_node, parentId: string, item: T_gridLayout_newNode): T_gridLayout_node | undefined;
20
+ /**
21
+ * Thêm khối vào vị trí (x, y) cụ thể và tự động đẩy các khối va chạm xuống dưới.
22
+ */
23
+ insertWithCollision(root: T_gridLayout_node, parentId: string | undefined, item: T_gridLayout_newNode, targetCol?: number, targetRow?: number): T_gridLayout_node | undefined;
20
24
  /** Xoá một khối khỏi cây. Không cho xoá gốc. */
21
25
  removeById(root: T_gridLayout_node, id: string): boolean;
22
26
  /**