@libs-ui/services-grid-layout 0.2.357-19 → 0.2.357-21
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.
- package/README.md +119 -25
- package/canvas/grid-layout-canvas.component.d.ts +111 -4
- package/defines/collision.define.d.ts +31 -0
- package/esm2022/canvas/grid-layout-canvas.component.mjs +384 -8
- package/esm2022/defines/collision.define.mjs +255 -0
- package/esm2022/defines/gridster-config.define.mjs +4 -5
- package/esm2022/grid-layout.service.mjs +122 -3
- package/esm2022/index.mjs +2 -1
- package/esm2022/interfaces/grid-layout.interface.mjs +1 -1
- package/esm2022/tree.service.mjs +20 -1
- package/fesm2022/libs-ui-services-grid-layout.mjs +780 -14
- package/fesm2022/libs-ui-services-grid-layout.mjs.map +1 -1
- package/grid-layout.service.d.ts +46 -0
- package/index.d.ts +1 -0
- package/interfaces/grid-layout.interface.d.ts +92 -0
- package/package.json +3 -3
- package/tree.service.d.ts +4 -0
package/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# @libs-ui/services-grid-layout
|
|
2
2
|
|
|
3
|
-
Mặt phẳng kéo–thả. Thư viện lo toàn
|
|
4
|
-
|
|
3
|
+
Mặt phẳng kéo–thả. Thư viện lo **toàn bộ** việc vẽ lưới, kéo thả, đổi kích thước, lồng container,
|
|
4
|
+
tay cầm kéo, bộ nút trên khối, viền chế độ sửa, và dọn dẹp khi component chết.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Nơi dùng chỉ đưa **cây dữ liệu** và **cách phân giải component nội dung**. Component nội dung của bạn
|
|
7
|
+
chỉ vẽ phần nội dung — không phải tự lo kích thước, không phải tự vẽ viền chế độ sửa.
|
|
7
8
|
|
|
8
9
|
## Cài
|
|
9
10
|
|
|
@@ -18,70 +19,162 @@ Lib dùng `angular-gridster2` qua **alias npm** để chạy song song nhiều b
|
|
|
18
19
|
Nơi dùng đang có `angular-gridster2` bản khác thì **không bị đụng** — hai bản nằm ở hai thư mục
|
|
19
20
|
`node_modules` riêng.
|
|
20
21
|
|
|
22
|
+
Chạy Jest thì phải map **cả hai** tên, vì package tự khai `name: angular-gridster2`:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// jest.config.ts của nơi dùng
|
|
26
|
+
moduleNameMapper: {
|
|
27
|
+
'^gridster18$': '<rootDir>/../../node_modules/gridster18/fesm2022/angular-gridster2.mjs',
|
|
28
|
+
'^angular-gridster2$': '<rootDir>/../../node_modules/gridster18/fesm2022/angular-gridster2.mjs',
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
21
32
|
## Dùng
|
|
22
33
|
|
|
23
34
|
```ts
|
|
24
35
|
@Component({
|
|
25
36
|
imports: [LibsUiGridLayoutCanvasComponent],
|
|
26
37
|
// 🔴 Khai trong providers của CHÍNH component — service theo từng mặt phẳng, không phải root.
|
|
27
|
-
providers: [LibsUiGridLayoutService
|
|
28
|
-
template: `<libs_ui-services-grid_layout-canvas
|
|
38
|
+
providers: [LibsUiGridLayoutService],
|
|
39
|
+
template: `<libs_ui-services-grid_layout-canvas
|
|
40
|
+
(outChange)="handlerChange($event)"
|
|
41
|
+
(outRemoveNode)="handlerRemoveNode($event)"
|
|
42
|
+
(outAddBlockInside)="handlerAddInside($event)"
|
|
43
|
+
(outToggleType)="handlerToggleType($event)" />`,
|
|
29
44
|
})
|
|
30
|
-
export class MyPageComponent
|
|
45
|
+
export class MyPageComponent {
|
|
31
46
|
private readonly gridLayout = inject(LibsUiGridLayoutService);
|
|
32
47
|
|
|
33
|
-
protected readonly root = signal<T_gridLayout_node>(initialLayout());
|
|
34
|
-
|
|
35
48
|
constructor() {
|
|
36
49
|
this.gridLayout.init({
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
data: layoutFromServer(), // cây khối, thường lấy từ backend
|
|
51
|
+
getComponentOutlet: resolveNodeComponent,
|
|
39
52
|
mode: E_gridLayout_mode_string.grid,
|
|
40
53
|
isEditMode: true,
|
|
41
|
-
allowOverlap: false,
|
|
42
54
|
});
|
|
43
55
|
}
|
|
56
|
+
// KHÔNG cần ngOnDestroy — thư viện tự dọn qua DestroyRef.
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Không truyền `[node]`** — `init()` đã giữ cây. Input `node`/`depth` của canvas chỉ dành cho lưới
|
|
61
|
+
lồng bên trong thư viện.
|
|
44
62
|
|
|
45
|
-
|
|
46
|
-
|
|
63
|
+
### Phân giải component theo từng khối
|
|
64
|
+
|
|
65
|
+
Mỗi khối tự quyết định component của nó, đọc từ `node.data` (dữ liệu hình dạng server). Trả
|
|
66
|
+
`Observable` nên `import()` động dùng được ngay — mỗi loại khối là một chunk riêng:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
export const resolveNodeComponent = (node: T_gridLayout_node): Observable<Type<unknown>> => {
|
|
70
|
+
switch ((node.data as T_serverElement | undefined)?.type) {
|
|
71
|
+
case E_serverElement_type_string.chart:
|
|
72
|
+
return from(import('./chart-card.component').then((c) => c.ChartCardComponent));
|
|
73
|
+
case E_serverElement_type_string.table:
|
|
74
|
+
return from(import('./table-card.component').then((c) => c.TableCardComponent));
|
|
75
|
+
default:
|
|
76
|
+
return from(import('./sample-card.component').then((c) => c.SampleCardComponent));
|
|
47
77
|
}
|
|
48
|
-
}
|
|
78
|
+
};
|
|
49
79
|
```
|
|
50
80
|
|
|
51
|
-
|
|
81
|
+
Khai ở `config.getComponentOutlet` cho cả mặt phẳng, hoặc ở `node.getComponentOutlet` cho riêng một
|
|
82
|
+
khối (khối tự khai thì thắng).
|
|
83
|
+
|
|
84
|
+
### Component nội dung
|
|
85
|
+
|
|
86
|
+
Khai đủ **bốn** input — thư viện `setInput` đúng bốn tên này, thiếu một cái là lỗi lúc chạy mà build
|
|
87
|
+
không bắt được:
|
|
52
88
|
|
|
53
89
|
```ts
|
|
54
90
|
@Component({ changeDetection: ChangeDetectionStrategy.OnPush, /* … */ })
|
|
55
91
|
export class MyCardComponent {
|
|
92
|
+
readonly data = input.required<T_myData>(); // = node.data — thường chỉ cần cái này
|
|
56
93
|
readonly node = input.required<T_gridLayout_node>();
|
|
57
94
|
readonly isEditMode = input<boolean>(false);
|
|
58
95
|
readonly depth = input<number>(0);
|
|
59
96
|
}
|
|
60
97
|
```
|
|
61
98
|
|
|
99
|
+
Type sẵn có: `T_gridLayout_nodeInputs<TData>`.
|
|
100
|
+
|
|
101
|
+
## Nút sẵn có bắn event kèm callback
|
|
102
|
+
|
|
103
|
+
Xoá · gộp · tách · thêm-khối-bên-trong là nút của thư viện. Bấm vào, thư viện **chưa làm gì** — nó
|
|
104
|
+
bắn event mang theo `confirm()`. Nghiệp vụ cần hỏi xác nhận thì mở modal rồi mới gọi; không cần thì
|
|
105
|
+
gọi luôn; không cho phép thì đừng gọi:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
protected handlerRemoveNode(event: T_gridLayout_actionEvent): void {
|
|
109
|
+
if (!event.node.children?.length) {
|
|
110
|
+
event.confirm(); // khối đơn — xoá luôn
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
this.pendingRemove.set(event); // khối ghép — hỏi trước, confirm() gọi sau
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
protected handlerAddInside(event: T_gridLayout_addEvent): void {
|
|
117
|
+
// confirm nhận khối mới, trả về khối đã tạo kèm id thư viện sinh
|
|
118
|
+
const added = event.confirm({ cols: 3, rows: 2, data: { type: 'metric' } });
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
| Output | Khi nào bắn | `confirm()` |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `outChange` | người dùng kéo / đổi kích thước — **chỉ** thao tác thật của người dùng | — |
|
|
125
|
+
| `outRemoveNode` | bấm nút xoá | `() => boolean` |
|
|
126
|
+
| `outToggleType` | bấm gộp (khối đơn → container) hoặc tách | `() => boolean` |
|
|
127
|
+
| `outAddBlockInside` | bấm + trên container | `(item) => node \| undefined` |
|
|
128
|
+
|
|
129
|
+
`outChange` **không** bắn khi chính bạn gọi `addBlock`/`removeBlock`/`compact` — bạn đã biết mình vừa
|
|
130
|
+
làm gì, khỏi phải chống dội.
|
|
131
|
+
|
|
62
132
|
## Hai chế độ
|
|
63
133
|
|
|
64
134
|
| Chế độ | Khối bám lưới | Chồng lên nhau | Dùng khi |
|
|
65
135
|
|---|---|---|---|
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
136
|
+
| `grid` | có | chỉ khi `allowOverlap` bật **và** hai khối khác `layerIndex` | dashboard, bố cục dạng bảng |
|
|
137
|
+
| `free` | không, toạ độ px | tự do | bố cục kiểu canvas — *engine chưa dựng, hiện vẫn chạy trên lưới* |
|
|
68
138
|
|
|
69
139
|
## API
|
|
70
140
|
|
|
71
|
-
**`LibsUiGridLayoutService`**
|
|
141
|
+
**`LibsUiGridLayoutService`** — một thực thể cho mỗi mặt phẳng.
|
|
72
142
|
|
|
73
143
|
| Thành viên | Việc |
|
|
74
144
|
|---|---|
|
|
75
|
-
| `init(config)` | dựng mặt phẳng; gọi lại sẽ dọn sạch rồi dựng lại |
|
|
76
|
-
| `setEditMode(
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
145
|
+
| `init(config)` | dựng mặt phẳng; gọi lại sẽ dọn sạch rồi dựng lại. Tự san phẳng container lồng quá trần |
|
|
146
|
+
| `setEditMode(value)` | bật/tắt chế độ sửa |
|
|
147
|
+
| `setAllowOverlap(value)` | cho phép xếp chồng; tự gán `layerIndex`. KHÔNG dựng lại component nào |
|
|
148
|
+
| `refresh(event?)` | báo cây vừa đổi từ bên ngoài — hiếm khi cần |
|
|
149
|
+
| `addBlock(item, parentId?)` | thêm khối; bỏ `parentId` là thêm ở gốc. Trả khối vừa tạo (kèm `id`) |
|
|
150
|
+
| `removeBlock(id)` | bỏ khối; xoá container là xoá cả ruột |
|
|
151
|
+
| `toContainer(id)` · `toSingle(id)` | gộp thành container / tách về khối đơn — không mất dữ liệu |
|
|
152
|
+
| `compact()` | dồn khối lên, bỏ khoảng trống. Đệ quy vào container |
|
|
153
|
+
| `findBlock(id)` · `findParentBlock(id)` | tra cây |
|
|
154
|
+
| `exportLayout()` | cây hiện tại đã bỏ hàm, `cloneDeep` — gửi thẳng lên backend được |
|
|
79
155
|
| `Root` · `IsEditMode` · `Version` | signal chỉ đọc |
|
|
80
156
|
|
|
81
|
-
|
|
157
|
+
Mọi hàm sửa cây **tự vẽ lại** — không phải gọi `refresh()` theo sau.
|
|
158
|
+
|
|
159
|
+
**Thư viện tự dọn** qua `DestroyRef` — `destroy()` là `private`, **CẤM** gọi trong `ngOnDestroy`.
|
|
160
|
+
|
|
161
|
+
**KHÔNG tự đặt `id`** cho khối: `addBlock` sinh bằng `uuid()` và trả về khối vừa tạo. Tự đặt là mời
|
|
162
|
+
trùng id, mà trùng thì `findBlock` trả nhầm khối và cây hỏng im lặng.
|
|
163
|
+
|
|
164
|
+
`LibsUiGridLayoutTreeService` **cố ý không xuất**: mọi thao tác trên cây đã có trên service với chữ ký
|
|
165
|
+
gọn hơn (không phải truyền `root` — thư viện đang giữ nó). Cấu hình gridster cũng không xuất, xem lý
|
|
166
|
+
do ở mục dưới.
|
|
167
|
+
|
|
168
|
+
### Lưu và mở lại bố cục
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
UtilsCache.Set(KEY, this.gridLayout.exportLayout(), UtilsCache.CACHE_EXPIRE_NONE);
|
|
172
|
+
// mở lại
|
|
173
|
+
this.gridLayout.init({ data: UtilsCache.Get(KEY) ?? defaultLayout(), getComponentOutlet });
|
|
174
|
+
```
|
|
82
175
|
|
|
83
|
-
`
|
|
84
|
-
`
|
|
176
|
+
`exportLayout()` đã lược `getComponentOutlet` (hàm không tuần tự hoá được) nên `JSON.stringify` được
|
|
177
|
+
ngay. Mở lại chỉ cần đưa `getComponentOutlet` vào `init` — cây không mang hàm.
|
|
85
178
|
|
|
86
179
|
## Vì sao cấu hình gridster lại như vậy
|
|
87
180
|
|
|
@@ -99,6 +192,7 @@ export class MyCardComponent {
|
|
|
99
192
|
| tay cầm resize ở `0`, không âm | `overflow: hidden` cắt mất nửa tay cầm → kéo mép không ăn |
|
|
100
193
|
| `setGridSize: false` ở lưới lồng | vòng lặp đo–ghi, lưới con co dần về 64×64px |
|
|
101
194
|
| `disableScroll*` ở lưới lồng | khối con bị đẩy ra ngoài container rồi kẹt |
|
|
195
|
+
| `layerIndex` gán vào cây ĐANG VẼ | gán vào `config.data` thì z-index đứng ở 1 — cây đang vẽ là bản clone |
|
|
102
196
|
|
|
103
197
|
## Kiểm
|
|
104
198
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { OnDestroy } from '@angular/core';
|
|
2
|
-
import { T_gridLayout_actionEvent, T_gridLayout_addEvent, T_gridLayout_changeEvent, T_gridLayout_node } from '../interfaces/grid-layout.interface';
|
|
1
|
+
import { AfterViewInit, OnDestroy, TemplateRef } from '@angular/core';
|
|
2
|
+
import { T_gridLayout_actionEvent, T_gridLayout_addEvent, T_gridLayout_changeEvent, T_gridLayout_dropEvent, T_gridLayout_dropGhost, T_gridLayout_node, T_gridLayout_toolbarContext, T_gridLayout_zoomPreset } from '../interfaces/grid-layout.interface';
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
/**
|
|
5
5
|
* Mặt phẳng kéo thả. Đệ quy: khối có `children` thì ruột nó lại là một mặt phẳng nữa.
|
|
@@ -7,7 +7,7 @@ import * as i0 from "@angular/core";
|
|
|
7
7
|
* Nơi dùng KHÔNG dựng component này trực tiếp cho từng khối — chỉ đặt MỘT thẻ ở gốc và truyền
|
|
8
8
|
* `nodeComponentType` qua `LibsUiGridLayoutService.init()`. Thư viện tự gắn component cho mọi khối.
|
|
9
9
|
*/
|
|
10
|
-
export declare class LibsUiGridLayoutCanvasComponent implements OnDestroy {
|
|
10
|
+
export declare class LibsUiGridLayoutCanvasComponent implements AfterViewInit, OnDestroy {
|
|
11
11
|
/**
|
|
12
12
|
* @internal Chỉ mặt phẳng LỒNG dùng — thư viện tự truyền khi vẽ ruột container.
|
|
13
13
|
*
|
|
@@ -17,6 +17,23 @@ export declare class LibsUiGridLayoutCanvasComponent implements OnDestroy {
|
|
|
17
17
|
readonly node: import("@angular/core").InputSignal<T_gridLayout_node | undefined>;
|
|
18
18
|
/** @internal Chỉ mặt phẳng LỒNG dùng — thư viện tự truyền. */
|
|
19
19
|
readonly depth: import("@angular/core").InputSignal<number>;
|
|
20
|
+
/**
|
|
21
|
+
* Ẩn hẳn thanh công cụ mặc định (thêm-vào-trong · gộp/tách · xoá) của MỌI khối.
|
|
22
|
+
*
|
|
23
|
+
* Dùng khi sản phẩm gom các hành động đó vào chỗ khác — vd một nút ⋮ nằm trong chính nội dung
|
|
24
|
+
* khối. Ẩn rồi thì `outRemoveNode` · `outAddBlockInside` · `outToggleType` không còn nguồn phát
|
|
25
|
+
* từ thư viện; nơi dùng tự gọi `removeBlock` · `addBlock` · `toggleType` của service.
|
|
26
|
+
*/
|
|
27
|
+
readonly hiddenToolbar: import("@angular/core").InputSignalWithTransform<boolean, boolean | undefined>;
|
|
28
|
+
/**
|
|
29
|
+
* Template tự vẽ thanh công cụ, thay cho bộ nút mặc định.
|
|
30
|
+
*
|
|
31
|
+
* Nhận context `{ $implicit: node, isContainer, canAddInside, canToggleType }` để nơi dùng vẽ đúng
|
|
32
|
+
* nút cho từng khối, rồi gọi ngược qua `outRemoveNode` · `outAddBlockInside` · `outToggleType`
|
|
33
|
+
* bằng chính các hàm trong context. Truyền cả `hiddenToolbar` và `templateToolbar` thì
|
|
34
|
+
* `hiddenToolbar` thắng — không vẽ gì.
|
|
35
|
+
*/
|
|
36
|
+
readonly templateToolbar: import("@angular/core").InputSignal<TemplateRef<T_gridLayout_toolbarContext> | undefined>;
|
|
20
37
|
/**
|
|
21
38
|
* Bố cục vừa đổi do NGƯỜI DÙNG kéo hoặc đổi kích thước khối.
|
|
22
39
|
*
|
|
@@ -36,12 +53,60 @@ export declare class LibsUiGridLayoutCanvasComponent implements OnDestroy {
|
|
|
36
53
|
readonly outAddBlockInside: import("@angular/core").OutputEmitterRef<T_gridLayout_addEvent>;
|
|
37
54
|
/** Người dùng bấm nút GỘP/TÁCH. Thư viện chưa làm gì — gọi `event.confirm()` để thực hiện. */
|
|
38
55
|
readonly outToggleType: import("@angular/core").OutputEmitterRef<T_gridLayout_actionEvent>;
|
|
56
|
+
/** Bật tính năng thu phóng (zoom) canvas. Chỉ áp dụng cho canvas gốc (depth 0). */
|
|
57
|
+
readonly enableZoom: import("@angular/core").InputSignalWithTransform<boolean, boolean | undefined>;
|
|
58
|
+
/** Tỷ lệ thu phóng hiện tại (hỗ trợ two-way binding [(zoom)]="myZoom"). */
|
|
59
|
+
readonly zoom: import("@angular/core").ModelSignal<number | undefined>;
|
|
60
|
+
/** Giới hạn thu phóng tối thiểu. Mặc định 0.4 (40%). */
|
|
61
|
+
readonly zoomMin: import("@angular/core").InputSignal<number>;
|
|
62
|
+
/** Giới hạn thu phóng tối đa. Mặc định 1.5 (150%). */
|
|
63
|
+
readonly zoomMax: import("@angular/core").InputSignal<number>;
|
|
64
|
+
/** Bước nhảy thu phóng mỗi lần bấm +/- hoặc lăn chuột. Mặc định 0.05 (5%). */
|
|
65
|
+
readonly zoomStep: import("@angular/core").InputSignal<number>;
|
|
66
|
+
/** Chiều rộng chuẩn của canvas stage tính bằng px. Mặc định 1668. */
|
|
67
|
+
readonly stageWidth: import("@angular/core").InputSignal<number>;
|
|
68
|
+
/** Chiều cao tối thiểu của canvas stage tính bằng px. Mặc định 937. */
|
|
69
|
+
readonly stageMinHeight: import("@angular/core").InputSignal<number>;
|
|
70
|
+
/** Khóa lưu tỷ lệ thu phóng vào localStorage. Mặc định 'ocb_bi_canvas_zoom'. */
|
|
71
|
+
readonly storageKey: import("@angular/core").InputSignal<string | undefined>;
|
|
72
|
+
/** Hiển thị thanh dock điều khiển thu phóng nổi ở góc dưới phải. Mặc định true. */
|
|
73
|
+
readonly showZoomDock: import("@angular/core").InputSignalWithTransform<boolean, boolean | undefined>;
|
|
74
|
+
/** Tự động vừa màn hình khi tải trang lần đầu nếu khung nhìn nhỏ hơn stageWidth. Mặc định true. */
|
|
75
|
+
readonly autoFitOnLoad: import("@angular/core").InputSignalWithTransform<boolean, boolean | undefined>;
|
|
76
|
+
/** Phát ra khi tỷ lệ thu phóng thay đổi. */
|
|
77
|
+
readonly outZoomChange: import("@angular/core").OutputEmitterRef<number>;
|
|
78
|
+
/** Bật tính năng kéo thả thẻ từ bên ngoài vào canvas (mặc định true) */
|
|
79
|
+
readonly enableExternalDrop: import("@angular/core").InputSignalWithTransform<boolean, boolean | undefined>;
|
|
80
|
+
/** Luôn hiển thị đường kẻ lưới 24 cột */
|
|
81
|
+
readonly showGridLines: import("@angular/core").InputSignalWithTransform<boolean, boolean | undefined>;
|
|
82
|
+
/** Mã id của khối đang được chọn */
|
|
83
|
+
readonly selectedNodeId: import("@angular/core").ModelSignal<string | null | undefined>;
|
|
84
|
+
/** Phát ra khi thả một thẻ mới từ bên ngoài vào canvas */
|
|
85
|
+
readonly outDropNode: import("@angular/core").OutputEmitterRef<T_gridLayout_dropEvent>;
|
|
86
|
+
/** Phát ra khi người dùng click chọn một khối */
|
|
87
|
+
readonly outSelectNode: import("@angular/core").OutputEmitterRef<T_gridLayout_node>;
|
|
88
|
+
/** Phát ra khi người dùng bấm nút cấu hình trên thanh công cụ của khối */
|
|
89
|
+
readonly outConfigNode: import("@angular/core").OutputEmitterRef<T_gridLayout_actionEvent>;
|
|
39
90
|
private readonly gridsterRef;
|
|
91
|
+
private readonly scrollAreaRef;
|
|
92
|
+
private readonly zoomDockRef;
|
|
40
93
|
/** mỗi ô một chỗ gắn riêng — cùng thứ tự với `children()` */
|
|
41
94
|
private readonly itemHosts;
|
|
95
|
+
/** Khung xem trước vị trí thả thẻ từ bên ngoài (snapped ghost preview) */
|
|
96
|
+
protected readonly dropGhost: import("@angular/core").WritableSignal<T_gridLayout_dropGhost | undefined>;
|
|
97
|
+
private activeHoveredEl?;
|
|
98
|
+
private rootGridsterEl?;
|
|
99
|
+
/** Menu chọn preset thu phóng đang mở hay đóng */
|
|
100
|
+
protected readonly isMenuOpen: import("@angular/core").WritableSignal<boolean>;
|
|
101
|
+
/** Các mốc thu phóng định sẵn */
|
|
102
|
+
protected readonly zoomPresets: Array<T_gridLayout_zoomPreset>;
|
|
42
103
|
/** Sáu chấm của tay cầm kéo — chỉ để `@for` dựng đủ 6 thẻ, giá trị không dùng tới. */
|
|
43
104
|
protected readonly dragBarDots: number[];
|
|
44
105
|
private readonly service;
|
|
106
|
+
/** Chế độ thu phóng có đang hoạt động trên canvas này không (chỉ root canvas depth 0) */
|
|
107
|
+
protected readonly isZoomActive: import("@angular/core").Signal<boolean>;
|
|
108
|
+
protected readonly currentZoom: import("@angular/core").Signal<number>;
|
|
109
|
+
protected readonly zoomPercent: import("@angular/core").Signal<number>;
|
|
45
110
|
/**
|
|
46
111
|
* Node THẬT của mặt phẳng này.
|
|
47
112
|
*
|
|
@@ -55,6 +120,13 @@ export declare class LibsUiGridLayoutCanvasComponent implements OnDestroy {
|
|
|
55
120
|
*/
|
|
56
121
|
protected readonly currentNode: import("@angular/core").Signal<T_gridLayout_node>;
|
|
57
122
|
protected readonly children: import("@angular/core").Signal<T_gridLayout_node[]>;
|
|
123
|
+
/**
|
|
124
|
+
* Bối cảnh cho `templateToolbar`, dựng sẵn theo từng khối.
|
|
125
|
+
*
|
|
126
|
+
* Dựng ở `computed` chứ không gọi hàm trên template: gọi hàm trong template chạy lại mỗi vòng
|
|
127
|
+
* dò thay đổi (rule `fe-coding-convention-templates` mục 6).
|
|
128
|
+
*/
|
|
129
|
+
protected readonly toolbarContexts: import("@angular/core").Signal<T_gridLayout_toolbarContext[]>;
|
|
58
130
|
protected readonly isNestedCanvas: import("@angular/core").Signal<boolean>;
|
|
59
131
|
/** trần lồng container — khối đã chạm trần thì không cho chuyển thành container */
|
|
60
132
|
protected readonly maxContainerDepth: import("@angular/core").Signal<number>;
|
|
@@ -70,11 +142,28 @@ export declare class LibsUiGridLayoutCanvasComponent implements OnDestroy {
|
|
|
70
142
|
protected readonly isEditMode: import("@angular/core").Signal<boolean>;
|
|
71
143
|
protected readonly gridOptions: import("@angular/core").Signal<import("angular-gridster2").GridsterConfig>;
|
|
72
144
|
constructor();
|
|
145
|
+
ngAfterViewInit(): void;
|
|
73
146
|
ngOnDestroy(): void;
|
|
74
147
|
/** Vẽ lại lưới — gọi khi khung ngoài đổi kích thước hoặc cây vừa đổi diện rộng. */
|
|
75
148
|
get FunctionControl(): {
|
|
76
149
|
veLaiLuoi: () => void | undefined;
|
|
150
|
+
zoomIn: () => void;
|
|
151
|
+
zoomOut: () => void;
|
|
152
|
+
resetZoom: () => void;
|
|
153
|
+
fitToScreen: () => void;
|
|
154
|
+
setZoom: (val: number) => void;
|
|
77
155
|
};
|
|
156
|
+
protected handlerZoomIn(): void;
|
|
157
|
+
protected handlerZoomOut(): void;
|
|
158
|
+
protected handlerResetZoom(): void;
|
|
159
|
+
protected handlerFitScreen(): void;
|
|
160
|
+
protected handlerToggleMenu(): void;
|
|
161
|
+
protected handlerSelectPreset(value: number): void;
|
|
162
|
+
protected isCurrentPreset(value: number): boolean;
|
|
163
|
+
private syncZoomOut;
|
|
164
|
+
protected handlerWheel(event: WheelEvent): void;
|
|
165
|
+
protected handlerDocumentKeyDown(event: KeyboardEvent): void;
|
|
166
|
+
protected handlerDocumentClick(event: MouseEvent): void;
|
|
78
167
|
protected handlerTrackNode(_index: number, item: T_gridLayout_node): string;
|
|
79
168
|
/**
|
|
80
169
|
* 🔴 Chặn `mousedown` nổi lên lưới CHA khi thao tác trong lưới con.
|
|
@@ -94,6 +183,17 @@ export declare class LibsUiGridLayoutCanvasComponent implements OnDestroy {
|
|
|
94
183
|
protected handlerRemove(event: Event, node: T_gridLayout_node): void;
|
|
95
184
|
protected handlerAddInside(event: Event, node: T_gridLayout_node): void;
|
|
96
185
|
protected handlerToggleType(event: Event, node: T_gridLayout_node): void;
|
|
186
|
+
/**
|
|
187
|
+
* Chặn cú bấm trong thanh công cụ nổi lên gridster.
|
|
188
|
+
*
|
|
189
|
+
* Chỉ `stopPropagation`, KHÔNG `preventDefault`: nút bên trong template của nơi dùng vẫn cần
|
|
190
|
+
* hành vi mặc định của mình (mở dropdown, focus…).
|
|
191
|
+
*/
|
|
192
|
+
protected handlerStopEvent(event: Event): void;
|
|
193
|
+
/** Phát `outAddBlockInside` — dùng chung cho nút mặc định và `templateToolbar`. */
|
|
194
|
+
private emitAddInside;
|
|
195
|
+
/** Phát `outToggleType` — dùng chung cho nút mặc định và `templateToolbar`. */
|
|
196
|
+
private emitToggleType;
|
|
97
197
|
/** khối này có phải container không (có khối con) */
|
|
98
198
|
protected isContainer(item: T_gridLayout_node): boolean;
|
|
99
199
|
/** container đã chạm trần lồng thì không cho thêm khối vào trong nữa */
|
|
@@ -102,6 +202,13 @@ export declare class LibsUiGridLayoutCanvasComponent implements OnDestroy {
|
|
|
102
202
|
protected canToggleType(item: T_gridLayout_node): boolean;
|
|
103
203
|
protected handlerBlockParentDrag(event: Event): void;
|
|
104
204
|
protected handlerMouseDownTrongLuoi(event: Event): void;
|
|
205
|
+
protected handlerSelectNode(node: T_gridLayout_node, event?: MouseEvent): void;
|
|
206
|
+
protected handlerConfig(event: Event, node: T_gridLayout_node): void;
|
|
207
|
+
protected handlerDragOver(event: DragEvent): void;
|
|
208
|
+
protected handlerDragLeave(event: DragEvent): void;
|
|
209
|
+
protected handlerDrop(event: DragEvent): void;
|
|
210
|
+
private cleanupHoveredGroup;
|
|
211
|
+
private cleanupDragGrid;
|
|
105
212
|
static ɵfac: i0.ɵɵFactoryDeclaration<LibsUiGridLayoutCanvasComponent, never>;
|
|
106
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<LibsUiGridLayoutCanvasComponent, "libs_ui-services-grid_layout-canvas", never, { "node": { "alias": "node"; "required": false; "isSignal": true; }; "depth": { "alias": "depth"; "required": false; "isSignal": true; }; }, { "outChange": "outChange"; "outRemoveNode": "outRemoveNode"; "outAddBlockInside": "outAddBlockInside"; "outToggleType": "outToggleType"; }, never, never, true, never>;
|
|
213
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<LibsUiGridLayoutCanvasComponent, "libs_ui-services-grid_layout-canvas", never, { "node": { "alias": "node"; "required": false; "isSignal": true; }; "depth": { "alias": "depth"; "required": false; "isSignal": true; }; "hiddenToolbar": { "alias": "hiddenToolbar"; "required": false; "isSignal": true; }; "templateToolbar": { "alias": "templateToolbar"; "required": false; "isSignal": true; }; "enableZoom": { "alias": "enableZoom"; "required": false; "isSignal": true; }; "zoom": { "alias": "zoom"; "required": false; "isSignal": true; }; "zoomMin": { "alias": "zoomMin"; "required": false; "isSignal": true; }; "zoomMax": { "alias": "zoomMax"; "required": false; "isSignal": true; }; "zoomStep": { "alias": "zoomStep"; "required": false; "isSignal": true; }; "stageWidth": { "alias": "stageWidth"; "required": false; "isSignal": true; }; "stageMinHeight": { "alias": "stageMinHeight"; "required": false; "isSignal": true; }; "storageKey": { "alias": "storageKey"; "required": false; "isSignal": true; }; "showZoomDock": { "alias": "showZoomDock"; "required": false; "isSignal": true; }; "autoFitOnLoad": { "alias": "autoFitOnLoad"; "required": false; "isSignal": true; }; "enableExternalDrop": { "alias": "enableExternalDrop"; "required": false; "isSignal": true; }; "showGridLines": { "alias": "showGridLines"; "required": false; "isSignal": true; }; "selectedNodeId": { "alias": "selectedNodeId"; "required": false; "isSignal": true; }; }, { "outChange": "outChange"; "outRemoveNode": "outRemoveNode"; "outAddBlockInside": "outAddBlockInside"; "outToggleType": "outToggleType"; "zoom": "zoomChange"; "outZoomChange": "outZoomChange"; "selectedNodeId": "selectedNodeIdChange"; "outDropNode": "outDropNode"; "outSelectNode": "outSelectNode"; "outConfigNode": "outConfigNode"; }, never, never, true, never>;
|
|
107
214
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { T_gridLayout_dropGhost, T_gridLayout_externalDragPayload, T_gridLayout_node } from '../interfaces/grid-layout.interface';
|
|
2
|
+
/**
|
|
3
|
+
* Checks whether the rectangular region [x, x + cols) x [y, y + rows)
|
|
4
|
+
* is completely vacant among existing items.
|
|
5
|
+
*/
|
|
6
|
+
export declare const isRegionFree: (children: Array<T_gridLayout_node>, x: number, y: number, cols: number, rows: number, ignoredId?: string) => boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Computes optimal (col, row) inside a group container.
|
|
9
|
+
* Snaps flush to adjacent card right edge if slot is vacant.
|
|
10
|
+
*/
|
|
11
|
+
export declare const findSnapPositionInsideGroup: (children: Array<T_gridLayout_node>, rawCol: number, rawRow: number, innerCols: number, innerRows: number, totalCols?: number) => {
|
|
12
|
+
col: number;
|
|
13
|
+
row: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Resolves vertical collisions when inserting a new card at (newNode.x, newNode.y).
|
|
17
|
+
* Any existing card that horizontally overlaps and whose vertical span overlaps
|
|
18
|
+
* or is below the insertion point is pushed down so there is no overlap.
|
|
19
|
+
*/
|
|
20
|
+
export declare const resolveCollisionsAndInsert: (children: Array<T_gridLayout_node>, newNode: T_gridLayout_node) => void;
|
|
21
|
+
export declare function parseDropPayload<T>(activePayload: T | undefined, dataTransfer: DataTransfer | null): T | undefined;
|
|
22
|
+
interface I_hoveredGroup {
|
|
23
|
+
groupNode: T_gridLayout_node;
|
|
24
|
+
groupGridster: HTMLElement;
|
|
25
|
+
}
|
|
26
|
+
export declare const findHoveredGroup: (elements: Element[], canvasContainer: HTMLElement, root: T_gridLayout_node) => I_hoveredGroup | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Calculates snapped spatial coordinates and pixel dimensions for drop ghost preview box.
|
|
29
|
+
*/
|
|
30
|
+
export declare const calculateDropGhost: (event: DragEvent, canvasContainer: HTMLElement, root: T_gridLayout_node, payload: T_gridLayout_externalDragPayload, totalCols?: number) => T_gridLayout_dropGhost | undefined;
|
|
31
|
+
export {};
|