@libs-ui/services-grid-layout 0.2.357-21 → 0.2.357-23
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 +116 -1
- package/canvas/grid-layout-canvas.component.d.ts +1 -0
- package/defines/collision.define.d.ts +26 -1
- package/drag-controller.d.ts +34 -0
- package/esm2022/canvas/grid-layout-canvas.component.mjs +6 -3
- package/esm2022/defines/collision.define.mjs +75 -1
- package/esm2022/drag-controller.mjs +89 -0
- package/esm2022/grid-layout.service.mjs +130 -2
- package/esm2022/index.mjs +2 -1
- package/esm2022/interfaces/grid-layout.interface.mjs +1 -1
- package/fesm2022/libs-ui-services-grid-layout.mjs +295 -4
- package/fesm2022/libs-ui-services-grid-layout.mjs.map +1 -1
- package/grid-layout.service.d.ts +54 -0
- package/index.d.ts +1 -0
- package/interfaces/grid-layout.interface.d.ts +16 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -125,6 +125,10 @@ protected handlerAddInside(event: T_gridLayout_addEvent): void {
|
|
|
125
125
|
| `outRemoveNode` | bấm nút xoá | `() => boolean` |
|
|
126
126
|
| `outToggleType` | bấm gộp (khối đơn → container) hoặc tách | `() => boolean` |
|
|
127
127
|
| `outAddBlockInside` | bấm + trên container | `(item) => node \| undefined` |
|
|
128
|
+
| `outDropNode` | vừa **thả** một thẻ kéo từ ngoài vào | — (khối đã chèn rồi) |
|
|
129
|
+
| `outSelectNode` | bấm chọn một khối | — |
|
|
130
|
+
| `outConfigNode` | bấm nút bánh răng trên khối | `() => true` |
|
|
131
|
+
| `outZoomChange` | tỷ lệ thu phóng vừa đổi | — |
|
|
128
132
|
|
|
129
133
|
`outChange` **không** bắn khi chính bạn gọi `addBlock`/`removeBlock`/`compact` — bạn đã biết mình vừa
|
|
130
134
|
làm gì, khỏi phải chống dội.
|
|
@@ -152,7 +156,13 @@ làm gì, khỏi phải chống dội.
|
|
|
152
156
|
| `compact()` | dồn khối lên, bỏ khoảng trống. Đệ quy vào container |
|
|
153
157
|
| `findBlock(id)` · `findParentBlock(id)` | tra cây |
|
|
154
158
|
| `exportLayout()` | cây hiện tại đã bỏ hàm, `cloneDeep` — gửi thẳng lên backend được |
|
|
155
|
-
| `
|
|
159
|
+
| `insertBlockWithCollision(item, col, row, parentId?)` | chèn vào ĐÚNG ô và đẩy khối va chạm xuống. `addBlock` thì tự tìm hàng trống kế tiếp |
|
|
160
|
+
| `duplicateBlock(id, transformData?)` | nhân bản; `transformData` để bạn tự đổi dữ liệu của mình (thư viện không đoán trường nào là tiêu đề) |
|
|
161
|
+
| `resizeBlock(id, { cols, rows })` | đổi kích thước bằng code; rộng quá mép phải thì tự dịch sang trái |
|
|
162
|
+
| `setDragPayload(p)` · `clearDragPayload()` | báo cho canvas biết đang kéo thẻ gì từ palette ngoài vào |
|
|
163
|
+
| `setZoom(v)` · `zoomIn()` · `zoomOut()` · `resetZoom()` · `fitToScreen(w)` | thu phóng |
|
|
164
|
+
| `Root` · `IsEditMode` · `Version` · `Zoom` | signal chỉ đọc |
|
|
165
|
+
| `totalColumns` | số cột thật của lưới cấp trang (mặc định 12) |
|
|
156
166
|
|
|
157
167
|
Mọi hàm sửa cây **tự vẽ lại** — không phải gọi `refresh()` theo sau.
|
|
158
168
|
|
|
@@ -176,6 +186,111 @@ this.gridLayout.init({ data: UtilsCache.Get(KEY) ?? defaultLayout(), getComponen
|
|
|
176
186
|
`exportLayout()` đã lược `getComponentOutlet` (hàm không tuần tự hoá được) nên `JSON.stringify` được
|
|
177
187
|
ngay. Mở lại chỉ cần đưa `getComponentOutlet` vào `init` — cây không mang hàm.
|
|
178
188
|
|
|
189
|
+
## Kéo thẻ từ palette bên ngoài vào
|
|
190
|
+
|
|
191
|
+
Nơi dùng chỉ làm **hai việc ở thẻ palette**; canvas lo phần còn lại.
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
protected handlerPaletteDragStart(event: DragEvent, item: T_paletteItem): void {
|
|
195
|
+
this.gridLayout.setDragPayload({
|
|
196
|
+
type: item.type,
|
|
197
|
+
title: item.label, // chữ trên khung xem trước
|
|
198
|
+
cols: item.cols, // thư viện đo khung xem trước theo hai số này
|
|
199
|
+
rows: item.rows,
|
|
200
|
+
// Khối THẬT sẽ tạo. `isInside` = con trỏ đang trong một khối ghép ⇒ thu cho vừa ruột
|
|
201
|
+
// container (lưới con luôn 12 cột).
|
|
202
|
+
resolveNode: (isInside: boolean) => ({
|
|
203
|
+
cols: isInside ? 12 : item.cols,
|
|
204
|
+
rows: isInside ? 4 : item.rows,
|
|
205
|
+
data: { ...buildBlockData(item), label: '' }, // nhãn TRỐNG — đánh số ở `outDropNode`
|
|
206
|
+
}),
|
|
207
|
+
});
|
|
208
|
+
event.dataTransfer?.setData('text/plain', item.label); // thiếu dòng này Firefox không kéo được
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
protected handlerPaletteDragEnd(): void {
|
|
212
|
+
this.gridLayout.clearDragPayload(); // người dùng thả RA NGOÀI canvas thì thư viện không bắn gì
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
```html
|
|
217
|
+
<div draggable="true"
|
|
218
|
+
(dragstart)="handlerPaletteDragStart($event, item)"
|
|
219
|
+
(dragend)="handlerPaletteDragEnd()">…</div>
|
|
220
|
+
|
|
221
|
+
<libs_ui-services-grid_layout-canvas (outDropNode)="handlerDropNode($event)" />
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
🔴 **CẤM tự viết `(dragover)` / `(drop)` trên canvas.** Canvas đã làm: vẽ khung xem trước bám ô lưới,
|
|
225
|
+
bật ô li của container khi con trỏ đi vào trong rồi tắt đúng lúc, chèn khối qua
|
|
226
|
+
`insertBlockWithCollision` và đẩy khối va chạm xuống.
|
|
227
|
+
|
|
228
|
+
🔴 **`resolveNode` phải THUẦN.** Thư viện gọi nó ở **mỗi nhịp `dragover`** chỉ để ĐO khung xem trước.
|
|
229
|
+
Cấm tăng bộ đếm, sinh id, gọi API bên trong — đánh số khối thì đánh ở `outDropNode`.
|
|
230
|
+
|
|
231
|
+
🔴 **`outDropNode` không có `confirm()`** — lúc nó bắn thì khối đã chèn rồi. Chặn trước thì chặn ở
|
|
232
|
+
`dragstart`; gỡ sau thì `removeBlock(event.node.id)`. `event.parentId` khác `undefined` ⇒ người dùng
|
|
233
|
+
thả vào ruột một khối ghép.
|
|
234
|
+
|
|
235
|
+
`LibsUiGridLayoutDragController` là lớp **tuỳ chọn** cho trường hợp bạn tự dựng bề mặt thả riêng
|
|
236
|
+
ngoài canvas. Dùng canvas sẵn có thì không cần đụng tới.
|
|
237
|
+
|
|
238
|
+
## Thu phóng và Stage
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
this.gridLayout.init({
|
|
242
|
+
getComponentOutlet,
|
|
243
|
+
data: layoutFromServer(),
|
|
244
|
+
enableZoom: true,
|
|
245
|
+
stageWidth: 1400, // 12 cột chia trên con số NÀY, không phải bề rộng cửa sổ
|
|
246
|
+
stageMinHeight: 900,
|
|
247
|
+
zoomMin: 0.4,
|
|
248
|
+
zoomMax: 1.5,
|
|
249
|
+
storageKey: 'bao_cao_ban_hang_zoom', // 🔴 RIÊNG từng màn
|
|
250
|
+
autoFitOnLoad: true,
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Bật `enableZoom` là canvas dựng một **stage** khổ cố định rồi phóng to/thu nhỏ cả stage — bố cục
|
|
255
|
+
nhìn giống nhau trên mọi khổ màn. Không bật thì 12 cột chia theo bề rộng cửa sổ, mỗi máy một kích
|
|
256
|
+
thước khối.
|
|
257
|
+
|
|
258
|
+
🔴 **`storageKey` phải riêng từng màn** — dùng chung thì hai màn ghi đè tỷ lệ của nhau.
|
|
259
|
+
|
|
260
|
+
Canvas có sẵn cụm nút thu phóng ở góc; muốn đặt nút chỗ khác thì `[showZoomDock]="false"` rồi gọi
|
|
261
|
+
`zoomIn()` · `zoomOut()` · `resetZoom()` · `setZoom(v)` · `fitToScreen(width)`.
|
|
262
|
+
|
|
263
|
+
## Input của canvas
|
|
264
|
+
|
|
265
|
+
| Input | Mặc định | Việc |
|
|
266
|
+
|---|---|---|
|
|
267
|
+
| `[(selectedNodeId)]` | `undefined` | khối đang chọn — **hai chiều**, thư viện ghi id vào signal của bạn |
|
|
268
|
+
| `[(zoom)]` | `undefined` | tỷ lệ thu phóng — **hai chiều** |
|
|
269
|
+
| `[showGridLines]` | `false` | vẽ ô li của lưới (lúc KÉO thư viện tự bật rồi tự tắt) |
|
|
270
|
+
| `[showZoomDock]` | `true` | cụm nút thu phóng ở góc canvas |
|
|
271
|
+
| `[enableExternalDrop]` | `true` | cho thả thẻ kéo từ ngoài vào |
|
|
272
|
+
| `[hiddenToolbar]` | `false` | ẩn hẳn bộ nút mặc định trên khối |
|
|
273
|
+
| `[templateToolbar]` | `undefined` | tự vẽ thanh công cụ của khối |
|
|
274
|
+
|
|
275
|
+
🔴 **`node` và `depth` là input NỘI BỘ** — thư viện tự truyền khi vẽ lưới con. Mặt phẳng gốc đọc cây
|
|
276
|
+
từ service (đã đưa vào qua `init()`), truyền thêm là khai hai lần cùng một thứ.
|
|
277
|
+
|
|
278
|
+
```html
|
|
279
|
+
<libs_ui-services-grid_layout-canvas [templateToolbar]="toolbar" />
|
|
280
|
+
|
|
281
|
+
<ng-template #toolbar let-node
|
|
282
|
+
let-isContainer="isContainer" let-canAddInside="canAddInside"
|
|
283
|
+
let-remove="remove" let-addInside="addInside" let-toggleType="toggleType">
|
|
284
|
+
@if (canAddInside) { <button (click)="addInside()">+</button> }
|
|
285
|
+
<button (click)="toggleType()">{{ isContainer ? 'Tách' : 'Gộp' }}</button>
|
|
286
|
+
<button (click)="remove()">Xoá</button>
|
|
287
|
+
</ng-template>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
🔴 `templateToolbar` đổi **giao diện**, không đổi **luật**: ba hàm trong context chỉ phát đúng sự
|
|
291
|
+
kiện (`outRemoveNode` · `outAddBlockInside` · `outToggleType`), y như bấm nút mặc định. Vẫn phải bắt
|
|
292
|
+
sự kiện rồi gọi `event.confirm()`.
|
|
293
|
+
|
|
179
294
|
## Vì sao cấu hình gridster lại như vậy
|
|
180
295
|
|
|
181
296
|
`defines/gridster-config.define.ts` giữ toàn bộ cấu hình đã vá. Mỗi dòng có dấu 🔴 tương ứng một lỗi
|
|
@@ -152,6 +152,7 @@ export declare class LibsUiGridLayoutCanvasComponent implements AfterViewInit, O
|
|
|
152
152
|
resetZoom: () => void;
|
|
153
153
|
fitToScreen: () => void;
|
|
154
154
|
setZoom: (val: number) => void;
|
|
155
|
+
scrollArea: () => HTMLElement | undefined;
|
|
155
156
|
};
|
|
156
157
|
protected handlerZoomIn(): void;
|
|
157
158
|
protected handlerZoomOut(): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T_gridLayout_dropGhost, T_gridLayout_externalDragPayload, T_gridLayout_node } from '../interfaces/grid-layout.interface';
|
|
1
|
+
import { T_gridLayout_dropGhost, T_gridLayout_externalDragPayload, T_gridLayout_newNode, T_gridLayout_node } from '../interfaces/grid-layout.interface';
|
|
2
2
|
/**
|
|
3
3
|
* Checks whether the rectangular region [x, x + cols) x [y, y + rows)
|
|
4
4
|
* is completely vacant among existing items.
|
|
@@ -28,4 +28,29 @@ export declare const findHoveredGroup: (elements: Element[], canvasContainer: HT
|
|
|
28
28
|
* Calculates snapped spatial coordinates and pixel dimensions for drop ghost preview box.
|
|
29
29
|
*/
|
|
30
30
|
export declare const calculateDropGhost: (event: DragEvent, canvasContainer: HTMLElement, root: T_gridLayout_node, payload: T_gridLayout_externalDragPayload, totalCols?: number) => T_gridLayout_dropGhost | undefined;
|
|
31
|
+
/** Tìm khối theo id trong cây, đi sâu vào mọi container. */
|
|
32
|
+
export declare const findNodeRecursive: (root: T_gridLayout_node, id: string) => T_gridLayout_node | undefined;
|
|
33
|
+
/** Tìm khối CHA trực tiếp của một khối, đi sâu vào mọi container. */
|
|
34
|
+
export declare const findParentRecursive: (root: T_gridLayout_node, id: string) => T_gridLayout_node | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Thả khối vào đúng ô mà ghost đang chỉ, đẩy các khối va chạm xuống dưới.
|
|
37
|
+
*
|
|
38
|
+
* Ghost đang nằm trong một container (`isInsideGroup`) thì thả vào ruột container đó; không tìm thấy
|
|
39
|
+
* container thì trả `false` chứ KHÔNG rơi về lưới gốc — thả nhầm cấp khó nhận ra hơn là không thả.
|
|
40
|
+
*/
|
|
41
|
+
export declare const dropNodeAtCoordinates: (root: T_gridLayout_node, ghost: T_gridLayout_dropGhost, newNode: T_gridLayout_newNode) => boolean;
|
|
42
|
+
/** Khối có phải CONTAINER không — tức có khối con bên trong. */
|
|
43
|
+
export declare const isContainerNode: (node: T_gridLayout_node) => boolean;
|
|
44
|
+
/** Khối có nằm LỒNG bên trong một container không (không phải con trực tiếp của gốc). */
|
|
45
|
+
export declare const isNodeNestedInside: (root: T_gridLayout_node | undefined, nodeId: string) => boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Có được đổi khối đơn ↔ container không.
|
|
48
|
+
*
|
|
49
|
+
* 🔴 Ba hàm cờ này PHẢI là nguồn duy nhất cho cả thanh công cụ của thư viện lẫn menu do nơi dùng tự
|
|
50
|
+
* dựng. Trước đây nơi dùng phải chép lại quy tắc để dựng menu trước lúc render, và hai bản lệch nhau
|
|
51
|
+
* mỗi khi thư viện đổi trần lồng cấp mà không có gì báo.
|
|
52
|
+
*/
|
|
53
|
+
export declare const canToggleTypeNode: (root: T_gridLayout_node | undefined, node: T_gridLayout_node) => boolean;
|
|
54
|
+
/** Có được thêm khối vào BÊN TRONG khối này không. */
|
|
55
|
+
export declare const canAddInsideNode: (root: T_gridLayout_node | undefined, node: T_gridLayout_node) => boolean;
|
|
31
56
|
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { T_gridLayout_dropGhost, T_gridLayout_externalDragPayload, T_gridLayout_node } from './interfaces/grid-layout.interface';
|
|
2
|
+
/**
|
|
3
|
+
* Giữ trạng thái một lượt kéo khối từ palette bên ngoài vào mặt phẳng.
|
|
4
|
+
*
|
|
5
|
+
* Việc nó lo: bật ô li của lưới đang được kéo tới (lưới gốc, và lưới của container khi con trỏ đi
|
|
6
|
+
* vào trong), tắt đúng lúc, và trả về ghost để nơi dùng vẽ khung xem trước.
|
|
7
|
+
*
|
|
8
|
+
* 🔴 Tự bật/tắt ô li bằng tay ở nơi dùng là chỗ rất dễ sót: kéo ra khỏi container rồi mà quên tắt
|
|
9
|
+
* thì ô li của nó nằm lại trên màn, kéo sang lưới khác lại chồng thêm một lớp nữa.
|
|
10
|
+
*/
|
|
11
|
+
export declare class LibsUiGridLayoutDragController {
|
|
12
|
+
private payload?;
|
|
13
|
+
private hoveredElement?;
|
|
14
|
+
private rootGridsterElement?;
|
|
15
|
+
/** Payload của khối đang được kéo; `undefined` khi không có lượt kéo nào. */
|
|
16
|
+
get ActivePayload(): T_gridLayout_externalDragPayload | undefined;
|
|
17
|
+
/** Gọi ở `dragstart` của thẻ trong palette. */
|
|
18
|
+
onDragStart(payload: T_gridLayout_externalDragPayload): void;
|
|
19
|
+
/** Gọi ở `dragend` — dọn sạch mọi ô li đang bật. */
|
|
20
|
+
onDragEnd(container?: HTMLElement): void;
|
|
21
|
+
/** Tắt ô li của container đang hover. */
|
|
22
|
+
cleanupHoveredGroup(): void;
|
|
23
|
+
/** Tắt ô li của lưới gốc, và quét sạch lớp còn sót trong khung chứa. */
|
|
24
|
+
cleanupRootGrid(container?: HTMLElement): void;
|
|
25
|
+
/** Tắt toàn bộ ô li đang bật. */
|
|
26
|
+
cleanupAll(container?: HTMLElement): void;
|
|
27
|
+
/**
|
|
28
|
+
* Gọi ở `dragover` của khung chứa mặt phẳng. Trả ghost để nơi dùng vẽ khung xem trước, đồng thời
|
|
29
|
+
* tự bật ô li của lưới đang được kéo tới.
|
|
30
|
+
*/
|
|
31
|
+
onDragOver(event: DragEvent, container: HTMLElement | undefined, root: T_gridLayout_node | undefined, totalCols?: number): T_gridLayout_dropGhost | undefined;
|
|
32
|
+
/** Bật ô li của lưới CẤP TRANG trong suốt lượt kéo, tắt lưới cũ nếu khung chứa đã đổi. */
|
|
33
|
+
private showRootGrid;
|
|
34
|
+
}
|