@libs-ui/services-grid-layout 0.2.357-19
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 +109 -0
- package/canvas/grid-layout-canvas.component.d.ts +107 -0
- package/defines/gridster-config.define.d.ts +21 -0
- package/esm2022/canvas/grid-layout-canvas.component.mjs +193 -0
- package/esm2022/defines/gridster-config.define.mjs +123 -0
- package/esm2022/grid-layout.service.mjs +433 -0
- package/esm2022/index.mjs +10 -0
- package/esm2022/interfaces/grid-layout.interface.mjs +2 -0
- package/esm2022/libs-ui-services-grid-layout.mjs +5 -0
- package/esm2022/tree.service.mjs +180 -0
- package/fesm2022/libs-ui-services-grid-layout.mjs +933 -0
- package/fesm2022/libs-ui-services-grid-layout.mjs.map +1 -0
- package/grid-layout.service.d.ts +157 -0
- package/index.d.ts +3 -0
- package/interfaces/grid-layout.interface.d.ts +171 -0
- package/package.json +29 -0
- package/tree.service.d.ts +59 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"libs-ui-services-grid-layout.mjs","sources":["../../../../../libs-ui/services/grid-layout/src/defines/gridster-config.define.ts","../../../../../libs-ui/services/grid-layout/src/tree.service.ts","../../../../../libs-ui/services/grid-layout/src/grid-layout.service.ts","../../../../../libs-ui/services/grid-layout/src/canvas/grid-layout-canvas.component.ts","../../../../../libs-ui/services/grid-layout/src/canvas/grid-layout-canvas.component.html","../../../../../libs-ui/services/grid-layout/src/index.ts","../../../../../libs-ui/services/grid-layout/src/libs-ui-services-grid-layout.ts"],"sourcesContent":["import { CompactType, DisplayGrid, GridsterConfig, GridType } from 'gridster18';\nimport { T_gridLayout_node } from '../interfaces/grid-layout.interface';\n\n/**\n * Cấu hình gridster cho lưới CẤP TRANG.\n *\n * Mọi giá trị dưới đây đều đến từ một lỗi đã đo được trên sản phẩm thật, không phải chọn theo cảm\n * tính. Đổi bất kỳ dòng nào có dấu 🔴 sẽ làm sống lại đúng lỗi ghi trong comment.\n */\nexport const gridLayoutRootConfig = (): GridsterConfig => ({\n // 🔴 VerticalFixed — CẤM đổi sang Fit hay ScrollVertical.\n // `Fit` chia đều chiều cao khung cho số hàng đang dùng, nên card khai `rows: 2` phình từ 124px lên\n // 247px. `ScrollVertical` KHÔNG đọc `fixedRowHeight` (mã lib: chỉ `Fixed` và `VerticalFixed` đọc).\n gridType: GridType.VerticalFixed,\n\n // 🔴 None — CẤM quay lại CompactUp.\n // `CompactUp` hút mọi khối lên sát nhau NGAY sau mỗi thao tác, nên:\n // · kéo mép dưới cho khối cao thêm → chỗ khối dưới vừa nhường bị hút lấp lại tức thì, cú kéo bị\n // `checkGridCollision` báo va chạm rồi HOÀN NGUYÊN (khối đứng im dù kéo);\n // · kéo thả đổi vị trí → khối vừa nhường chỗ bị hút về ngay, khối đang kéo không có chỗ đặt và\n // treo lơ lửng giữa hai khối.\n // Muốn dồn khít thì gọi `sapGon()` của service.\n compactType: CompactType.None,\n displayGrid: DisplayGrid.OnDragAndResize,\n\n // 🔴 pushItems: true — CẤM để false.\n // `swap` của lib CHỈ hoán vị được HAI KHỐI CÙNG KÍCH THƯỚC (mã lib `checkSwap`: tính diffX/diffY\n // rồi hoán vị, lệch kích thước thì `checkCollision` báo va chạm và hoàn nguyên). Khối 3x2 gặp\n // container 7x5 nên không swap được, mà `pushItems: false` cũng không cho container nhường đường\n // → BẾ TẮC: khối đang kéo không đặt được ở đâu.\n pushItems: true,\n // chỉ đẩy XUỐNG: đẩy ngang làm xô lệch bố cục cột người dùng vừa đặt\n pushDirections: { north: false, east: false, south: true, west: false },\n pushResizeItems: true,\n swap: true,\n swapWhileDragging: true,\n\n draggable: {\n enabled: true,\n // 🔴 KHÔNG được bỏ. Không có nó thì gridster vào chế độ kéo ngay từ `mousedown`, nên chỉ bấm\n // CHỌN một khối là nó đã nhích theo dù chuột đứng yên. Lib tự huỷ kéo khi nhả chuột trước mốc\n // này nên cú bấm chọn vẫn ăn nguyên vẹn.\n delayStart: 160,\n // 🔴 `checkDragHandleClass` duyệt DOM từ chỗ bấm NGƯỢC LÊN CHA, và trên MỖI node nó kiểm\n // `dragHandleClass` TRƯỚC `ignoreContentClass`. Vì vậy một phần tử KHÔNG được mang cả hai class\n // cùng lúc — mang cả hai thì lib gặp tay cầm trước, trả `true`, class chặn không tới lượt.\n ignoreContentClass: 'libs-ui-grid-layout-block-parent-drag',\n ignoreContent: true,\n dragHandleClass: 'libs-ui-grid-layout-drag-handle',\n },\n\n resizable: {\n enabled: true,\n // đủ 8 hướng như cửa sổ thường; chỉ có góc dưới-phải thì quá khó trúng\n handles: { s: true, e: true, n: true, w: true, se: true, ne: true, sw: true, nw: true },\n },\n\n margin: 12,\n outerMargin: true,\n outerMarginBottom: 40,\n minCols: 12,\n maxCols: 12,\n minRows: 2,\n\n // 🔴 KHÔNG giới hạn chiều cao khối. Lib có BA trần mặc định phải vô hiệu hoá CÙNG LÚC, thiếu một\n // cái là vẫn bị chặn: `maxItemRows: 50` · `maxItemCols: 50` · `maxItemArea: 2500` (trần DIỆN TÍCH\n // cols × rows — khó thấy nhất: khối 12 cột chỉ cần 209 hàng là đụng trần dù hai trần kia còn dư).\n // `maxRows` là trần của CẢ LƯỚI, cũng phải nới theo.\n maxRows: 5000,\n maxItemRows: 5000,\n maxItemCols: 12,\n maxItemArea: 60000,\n\n // 🔴 Trần LỚP CHỒNG. Mặc định của lib là 2 — quá thấp: khi bật chồng khối, thư viện gán mỗi khối\n // một lớp riêng để chúng khác nhau, nên chỉ 3 khối là đã đụng trần và các khối sau lại rơi về\n // cùng lớp, tức chồng nhau bị chặn trở lại.\n maxLayerIndex: 5000,\n\n setGridSize: true,\n fixedRowHeight: 56,\n defaultItemCols: 3,\n defaultItemRows: 2,\n minItemRows: 1,\n mobileBreakpoint: -1,\n});\n\n/**\n * Cấu hình cho lưới LỒNG (ruột một container).\n *\n * Kế thừa lưới trang rồi ghi đè phần khác biệt. Ba điểm quan trọng nhất: `setGridSize` phải TẮT,\n * tay cầm phải mang TÊN RIÊNG, và tự-cuộn-khi-kéo phải TẮT.\n */\nexport const gridLayoutNestedConfig = (): GridsterConfig => ({\n ...gridLayoutRootConfig(),\n\n // 🔴 PHẢI tắt cho lưới lồng. `setGridSize` làm gridster tự ghi style width/height lên CHÍNH nó,\n // sinh vòng lặp đo-rồi-ghi với ResizeObserver của khung ngoài (khung đổi → onResize xoá style →\n // setGridSize ghi lại) → lưới con co dần về 64x64px trong khung 868px.\n setGridSize: false,\n\n // khe 2px: khối con là các dòng thông tin của CÙNG một card, khe rộng làm chúng trông như card rời\n margin: 2,\n // Lưới lồng KHÔNG tự chừa lề — lề là của VỎ container (`containerPadding`), không phải của lưới.\n // Đặt ở vỏ thì ruột co lại đúng bằng phần còn lại, tay cầm resize của khối con luôn nằm trong tầm\n // chuột (người dùng chốt 28/08/2026: \"lưới cha có padding chứ ko phải con\").\n outerMargin: false,\n outerMarginBottom: 0,\n\n // hàng 16px cho bước kéo mịn, và sàn 1 hàng = 16px nên khối co được nhỏ hơn nhiều\n fixedRowHeight: 16,\n minRows: 1,\n minItemRows: 1,\n defaultItemCols: 2,\n defaultItemRows: 2,\n\n // 🔴 Tay cầm RIÊNG — CẤM dùng chung tên với lưới trang.\n // `checkDragHandleClass` trả `true` ở node ĐẦU TIÊN có `dragHandleClass`. Dùng chung một tên thì\n // lưới CHA cũng nhận ra tay cầm của khối con và nhấc luôn container.\n draggable: {\n ...gridLayoutRootConfig().draggable,\n dragHandleClass: 'libs-ui-grid-layout-drag-handle-child',\n },\n\n // 🔴 KÉO MÉP thì khối dưới ĐỨNG YÊN, chỉ khoảng trống giữa hai khối co giãn. Bật (kế thừa từ lưới\n // trang) thì mỗi cú kéo đẩy dồn cả cột xuống — người dùng chỉ muốn một khối to hơn một chút mà cả\n // card dài ra. `pushItems` vẫn giữ: nó lo việc KÉO THẢ đổi chỗ, khác việc đổi kích thước.\n pushResizeItems: false,\n\n // 🔴 Tắt tự cuộn khi kéo tới sát mép. Lib thấy chuột gần biên là cuộn lưới theo\n // (`scrollSensitivity`), nhưng lưới con nằm trong card có `overflow: hidden` — cuộn ở đây đẩy khối\n // con ra NGOÀI khung container rồi kẹt luôn ở đó.\n disableScrollHorizontal: true,\n disableScrollVertical: true,\n});\n\n/**\n * Cây rỗng dùng khi chưa có dữ liệu — mặt phẳng vẽ ra một lưới trống thay vì vỡ.\n * Xảy ra trong nhịp giữa lúc component dựng và lúc `init()` chạy.\n */\nexport const emptyRootNode = (): T_gridLayout_node => ({ id: 'root', x: 0, y: 0, cols: 12, rows: 0, children: [] });\n","import { Injectable } from '@angular/core';\nimport { uuid } from '@libs-ui/utils';\nimport { T_gridLayout_newNode, T_gridLayout_node } from './interfaces/grid-layout.interface';\n\n/**\n * Thao tác trên cây khối: tìm, thêm, xoá, dồn khít, chặn lồng cấp quá sâu.\n *\n * 🔴 NỘI BỘ thư viện — không xuất ra ngoài. Nơi dùng đi qua `LibsUiGridLayoutService`, ở đó mỗi\n * thao tác sửa bố cục vừa vẽ lại vừa bắn `outChange` đúng MỘT lần. Các hàm ở đây cố tình KHÔNG tự\n * vẽ lại: để chúng tự gọi thì mỗi thao tác vẽ hai lượt và bắn hai sự kiện (người dùng chốt\n * 28/08/2026: \"đặt đúng các điểm mà làm cho layout có thể thay đổi thôi\").\n */\n@Injectable()\nexport class LibsUiGridLayoutTreeService {\n findById(node: T_gridLayout_node, id: string): T_gridLayout_node | undefined {\n if (node.id === id) {\n return node;\n }\n\n for (const child of node.children ?? []) {\n const found = this.findById(child, id);\n\n if (found) {\n return found;\n }\n }\n\n return undefined;\n }\n\n findParent(node: T_gridLayout_node, id: string): T_gridLayout_node | undefined {\n for (const child of node.children ?? []) {\n if (child.id === id) {\n return node;\n }\n\n const found = this.findParent(child, id);\n\n if (found) {\n return found;\n }\n }\n\n return undefined;\n }\n\n /**\n * Thêm khối vào cuối một container. Thư viện tự sinh `id` và tự đặt `x` · `y` xuống hàng trống.\n *\n * @returns khối vừa tạo (kèm `id` vừa sinh), hoặc `undefined` khi không tìm thấy container.\n */\n addChild(root: T_gridLayout_node, parentId: string, item: T_gridLayout_newNode): T_gridLayout_node | undefined {\n const parent = this.findById(root, parentId);\n\n if (!parent) {\n return undefined;\n }\n\n parent.children = parent.children ?? [];\n // 🔴 Thư viện tự sinh `id`. Đây là mã máy dùng để tra cứu, không phải thứ người dùng nghĩ ra —\n // bắt họ tự đặt là mời họ tạo trùng, mà trùng id thì `findById` trả nhầm khối và cả cây hỏng\n // im lặng (người dùng chốt 28/08/2026).\n const node: T_gridLayout_node = { ...item, id: item.id ?? uuid(), x: 0, y: this.nextFreeRow(parent.children) };\n\n parent.children.push(node);\n\n // Trả về khối vừa tạo để nơi dùng biết `id` thư viện vừa sinh.\n return node;\n }\n\n /** Xoá một khối khỏi cây. Không cho xoá gốc. */\n removeById(root: T_gridLayout_node, id: string): boolean {\n if (root.id === id) {\n return false;\n }\n\n const parent = this.findParent(root, id);\n\n if (!parent?.children) {\n return false;\n }\n\n parent.children = parent.children.filter((item) => item.id !== id);\n\n return true;\n }\n\n /**\n * Chuyển một khối đơn thành CONTAINER: nội dung cũ trở thành khối con đầu tiên.\n *\n * Không mất dữ liệu — `data` của khối được mang theo xuống khối con.\n *\n * @returns `false` khi không tìm thấy khối, hoặc khối đã là container rồi.\n */\n toContainer(root: T_gridLayout_node, id: string, colCount: number): boolean {\n const node = this.findById(root, id);\n\n if (!node || (node.children?.length ?? 0) > 0) {\n return false;\n }\n\n node.children = [{ ...node, id: uuid(), x: 0, y: 0, cols: colCount, rows: 4, children: [] }];\n\n return true;\n }\n\n /**\n * Chuyển một CONTAINER về khối đơn: các khối con được NÂNG lên cấp trên, không bị xoá.\n *\n * @returns `false` khi không tìm thấy khối, hoặc khối vốn không phải container.\n */\n toSingle(root: T_gridLayout_node, id: string): boolean {\n const node = this.findById(root, id);\n\n if (!node || (node.children?.length ?? 0) === 0) {\n return false;\n }\n\n const parent = this.findParent(root, id);\n\n for (const child of node.children ?? []) {\n parent?.children?.push({ ...child, cols: node.cols, rows: 2 });\n }\n\n node.children = [];\n\n return true;\n }\n\n /**\n * Làm phẳng mọi container lồng vượt trần.\n *\n * 🔴 Cần thiết vì bố cục ĐÃ LƯU không đi qua luật chặn nào của giao diện. Một bản lưu cũ — sinh\n * trước khi có trần, hoặc do thao tác lỗi — vẫn nạp thẳng vào và hiện container lồng container.\n * Container vượt trần bị hạ thành khối thường, con của nó nâng lên cấp trên: KHÔNG mất dữ liệu.\n *\n * @returns số container đã làm phẳng\n */\n flattenOverDepthContainers(node: T_gridLayout_node, maxDepth: number): number {\n return this.walkFlatten(node, 0, maxDepth);\n }\n\n /**\n * Dồn khối khít lên trên theo thứ tự đọc, lấp hết chỗ trống.\n *\n * 🔴 KHÔNG sửa `node.rows` của container. Chiều cao container tính theo đơn vị hàng của lưới CHA,\n * còn `currentRow` đếm theo đơn vị hàng của lưới CON — trộn hai thang đo là container phình lên\n * gấp mấy lần.\n */\n compact(node: T_gridLayout_node, colCount: number): void {\n this.walkCompact(node, colCount);\n }\n\n private walkCompact(node: T_gridLayout_node, colCount: number): void {\n const children = node.children;\n\n if (!children?.length) {\n return;\n }\n\n const inReadingOrder = [...children].sort((a, b) => a.y - b.y || a.x - b.x);\n let currentRow = 0;\n let usedCols = 0;\n let tallestInRow = 0;\n\n for (const item of inReadingOrder) {\n const width = Math.min(item.cols, colCount);\n\n if (usedCols > 0 && usedCols + width > colCount) {\n currentRow += tallestInRow;\n usedCols = 0;\n tallestInRow = 0;\n }\n\n item.x = usedCols;\n item.y = currentRow;\n item.cols = width;\n usedCols += width;\n tallestInRow = Math.max(tallestInRow, item.rows);\n\n if ((item.children?.length ?? 0) > 0) {\n this.walkCompact(item, colCount);\n }\n }\n }\n\n private walkFlatten(node: T_gridLayout_node, containerDepth: number, maxDepth: number): number {\n const children = node.children;\n\n if (!children?.length) {\n return 0;\n }\n\n let fixedCount = 0;\n const result: Array<T_gridLayout_node> = [];\n\n for (const child of children) {\n const isContainer = (child.children?.length ?? 0) > 0;\n\n if (isContainer && containerDepth >= maxDepth) {\n fixedCount += 1;\n\n for (const item of child.children ?? []) {\n fixedCount += this.walkFlatten(item, containerDepth, maxDepth);\n result.push(item);\n }\n\n continue;\n }\n\n fixedCount += this.walkFlatten(child, isContainer ? containerDepth + 1 : containerDepth, maxDepth);\n result.push(child);\n }\n\n node.children = result;\n\n return fixedCount;\n }\n\n private nextFreeRow(children: Array<T_gridLayout_node>): number {\n return children.reduce((max, child) => Math.max(max, child.y + child.rows), 0);\n }\n}\n","import { ComponentRef, DestroyRef, Injectable, ViewContainerRef, inject, signal } from '@angular/core';\nimport { GridsterConfig, GridsterItem } from 'gridster18';\nimport { cloneDeep } from '@libs-ui/utils';\nimport { Subscription, take } from 'rxjs';\nimport { gridLayoutNestedConfig, gridLayoutRootConfig } from './defines/gridster-config.define';\nimport { LibsUiGridLayoutTreeService } from './tree.service';\nimport {\n E_gridLayout_mode_string,\n T_gridLayout_changeEvent,\n T_gridLayout_newNode,\n T_gridLayout_config,\n T_gridLayout_node,\n} from './interfaces/grid-layout.interface';\n\n/**\n * Dựng một mặt phẳng kéo thả và tự gắn component của nơi dùng lên từng khối.\n *\n * Nơi dùng chỉ cần đưa vào: loại component muốn dựng\n * (`nodeComponentType`) và cây dữ liệu (`data`). Toàn bộ việc vẽ lưới, kéo thả, đổi kích thước,\n * lồng container và dọn dẹp do thư viện lo.\n *\n * 🔴 Đây là service theo TỪNG mặt phẳng, KHÔNG `providedIn: 'root'`. Khai trong `providers` của\n * component dùng nó; hai mặt phẳng trên cùng một màn phải là hai thực thể riêng, dùng chung thì\n * `componentRefs` của mặt phẳng này bị mặt phẳng kia dọn mất.\n */\n@Injectable()\nexport class LibsUiGridLayoutService {\n // 🔴 TỰ TẠO, không `inject`. Nơi dùng chỉ phải viết `providers: [LibsUiGridLayoutService]` —\n // không phải biết bên trong thư viện còn service nào khác (người dùng chốt 28/08/2026).\n // `TreeService` không giữ trạng thái dùng chung nên mỗi mặt phẳng một bản là đúng.\n private readonly treeService = new LibsUiGridLayoutTreeService();\n /** Lượt tải component đang chờ, theo id khối — để huỷ khi khối bị gỡ giữa chừng. */\n private readonly pendingLoads = new Map<string, Subscription>();\n\n constructor() {\n // 🔴 Service khai trong `providers` của component nên Angular huỷ nó cùng component — thư viện\n // tự dọn ở đây, nơi dùng KHÔNG phải gọi `destroy()` trong `ngOnDestroy` (người dùng chốt\n // 28/08/2026). Bắt nhớ một bước dọn dẹp là kiểu API dễ quên, và quên thì rò rỉ bộ nhớ im lặng.\n inject(DestroyRef).onDestroy(() => this.destroy());\n }\n\n private config?: T_gridLayout_config;\n private readonly componentRefs = new Map<string, ComponentRef<unknown>>();\n\n private readonly rootNode = signal<T_gridLayout_node | undefined>(undefined);\n private readonly editMode = signal<boolean>(false);\n /** tăng mỗi lần cây đổi — component con đọc để biết phải tính lại */\n private readonly version = signal<number>(0);\n\n /**\n * Signal chỉ đọc để nơi dùng theo dõi.\n *\n * 🔴 Là THUỘC TÍNH, không phải getter. Getter trả signal thì chỗ đọc phải viết `Version()()` —\n * quên cặp ngoặc thứ hai là computed KHÔNG phụ thuộc vào nó, cây đổi mà lưới đứng im, và\n * TypeScript không hề báo lỗi (đo 28/08/2026: thêm khối không hiện, sắp gọn không nhúc nhích).\n */\n readonly Root = this.rootNode.asReadonly();\n readonly IsEditMode = this.editMode.asReadonly();\n /** tăng mỗi lần cây hoặc cấu hình đổi */\n readonly Version = this.version.asReadonly();\n\n /**\n * trần số cấp container lồng nhau — canvas đọc để biết có cho chuyển thành container không */\n private maxContainerDepth(): number {\n return this.config?.maxContainerDepth ?? 1;\n }\n\n /**\n * lề trong của container, px — canvas đọc để đặt padding lên vỏ container */\n private containerPadding(): number {\n return this.config?.containerPadding ?? 12;\n }\n\n /** Khởi tạo mặt phẳng. Gọi lại sẽ dọn sạch component cũ rồi dựng lại từ đầu. */\n init(config: T_gridLayout_config): void {\n this.destroy();\n this.config = config;\n this.assignOverlapLayers(config.data);\n // 🔴 Nhân bản như `refresh()`: `init()` lại (đổi chế độ, bật/tắt chồng khối) trên CÙNG object cây\n // thì `@for` không dựng lại `gridster-item`, nên `layerIndex` vừa gán không tới `$item` và khối\n // vẫn không đè nhau được (đo 28/08/2026: `item.layerIndex` = 1|2|3|4|7 mà `$item.layerIndex`\n // toàn `undefined`).\n this.rootNode.set(this.shallowCloneTree(config.data));\n this.editMode.set(config.isEditMode ?? false);\n this.version.update((v) => v + 1);\n }\n\n setEditMode(value: boolean): void {\n this.editMode.set(value);\n // 🔴 PHẢI bơm `version`: `gridsterOptions()` đọc `editMode` để bật/tắt kéo–thả, mà computed\n // `gridOptions` bên canvas chỉ chạy lại khi `Version` đổi. Thiếu dòng này thì đổi chế độ xong\n // gridster vẫn giữ options cũ — chuyển sang XEM mà khối vẫn kéo được.\n this.version.update((v) => v + 1);\n }\n\n /**\n * Bật/tắt cho khối chồng lên nhau — KHÔNG dựng lại mặt phẳng.\n *\n * Trước đây nơi dùng phải gọi `init()` lại để đổi cờ này, mà `init()` huỷ sạch rồi dựng lại mọi\n * component nội dung — lãng phí, và người dùng thấy màn chớp (người dùng chốt 28/08/2026).\n */\n setAllowOverlap(value: boolean): void {\n if (!this.config) {\n return;\n }\n\n this.config = { ...this.config, allowOverlap: value };\n\n const root = this.rootNode();\n\n if (!root) {\n return;\n }\n\n // 🔴 Gán lớp vào CÂY ĐANG VẼ (`rootNode`), KHÔNG phải `config.data`.\n // `init()` đã clone `config.data` sang `rootNode` nên hai object KHÁC nhau; gán vào `config.data`\n // thì `refresh()` clone lại từ `rootNode` — lớp vừa gán không tới `gridster-item` nào, `z-index`\n // của mọi khối đứng ở `1` và khối nào vẽ sau thì nằm trên: chồng khối \"lúc được lúc không\"\n // (đo 04/09/2026: `item.layerIndex` undefined, `getLayerIndex()` = 0 cho cả 8 khối).\n this.assignOverlapLayers(root);\n // Thay tham chiếu cây để `layerIndex` vừa gán tới được `$item` của gridster; component nội dung\n // KHÔNG bị dựng lại vì `attachNode` nhận ra id cũ và chỉ `setInput`.\n this.refresh();\n }\n\n /**\n *\n * Cấu hình gridster cho một cấp lưới.\n *\n * @param depth 0 = lưới cấp trang, ≥ 1 = lưới lồng trong container\n */\n private buildGridsterOptions(depth: number, itemChangeCallback?: () => void): GridsterConfig {\n const base = depth === 0 ? gridLayoutRootConfig() : gridLayoutNestedConfig();\n const config = this.config;\n\n if (!config) {\n return base;\n }\n\n const isFreeMode = config.mode === E_gridLayout_mode_string.free;\n const canEdit = this.editMode();\n const canOverlap = isFreeMode || (config.allowOverlap ?? false);\n\n return {\n ...base,\n ...(config.cols ? { minCols: config.cols, maxCols: config.cols, maxItemCols: config.cols } : {}),\n ...(config.rowHeight && depth === 0 ? { fixedRowHeight: config.rowHeight } : {}),\n // Khe và chiều cao hàng khác nhau theo cấp: lưới trang thoáng, lưới lồng khít.\n ...(depth === 0 && config.margin !== undefined ? { margin: config.margin } : {}),\n ...(depth > 0 && config.nestedMargin !== undefined ? { margin: config.nestedMargin } : {}),\n ...(depth > 0 && config.nestedRowHeight !== undefined ? { fixedRowHeight: config.nestedRowHeight } : {}),\n ...(depth === 0 && config.outerMargin !== undefined ? { outerMargin: config.outerMargin } : {}),\n ...(depth === 0 && config.outerMarginBottom !== undefined ? { outerMarginBottom: config.outerMarginBottom } : {}),\n ...(config.minItemRows !== undefined ? { minItemRows: config.minItemRows } : {}),\n ...(config.defaultItemCols !== undefined ? { defaultItemCols: config.defaultItemCols } : {}),\n ...(config.defaultItemRows !== undefined ? { defaultItemRows: config.defaultItemRows } : {}),\n\n // 🔴 Chế độ XEM phải TẮT hẳn kéo và đổi kích thước.\n // `base` luôn để `enabled: true`; không ghi đè theo `editMode` thì người xem vẫn kéo được khối\n // và đổi bố cục của người khác (người dùng báo 28/08/2026: \"đang xem mà vẫn kéo thả thay đổi\n // vị trí và thêm khối được\").\n draggable: { ...base.draggable, enabled: canEdit },\n resizable: { ...base.resizable, enabled: canEdit },\n\n // Chế độ tự do LUÔN cho chồng; chế độ lưới thì theo cờ của nơi dùng.\n allowMultiLayer: canOverlap,\n // 🔴 Bật `allowMultiLayer` MỘT MÌNH không đủ. Mã lib so `layerIndex` của hai khối: KHÁC nhau\n // thì cho đè, GIỐNG nhau vẫn báo va chạm. Khối không khai `layerIndex` đều rơi về\n // `defaultLayerIndex` — tức cùng lớp, và vẫn bị đẩy nhau như lưới thường (người dùng báo\n // 28/08/2026: \"bật chồng khối nhưng khi kéo thì các khối vẫn di chuyển theo\").\n // Cho chồng thì phải TẮT đẩy khối, nếu không khối bị dạt đi trước khi kịp đè lên nhau.\n ...(canOverlap ? { pushItems: false, pushResizeItems: false, swap: false, swapWhileDragging: false } : {}),\n\n itemChangeCallback: (item: GridsterItem) => {\n // 🔴 Khối vừa thao tác phải lên LỚP TRÊN CÙNG.\n // `layerIndex` gán một lần theo thứ tự cây lúc khởi tạo, nên khối kéo lên chỉ nổi TRONG LÚC\n // kéo (nhờ `z-index: 30` của lớp `gridster-item-moving`); thả tay ra là về lớp cũ và bị khối\n // đứng sau trong cây đè ngược lên (người dùng báo 28/08/2026, đo được: kéo z=30, thả về\n // z=10 trong khi hai khối kế bên đang z=11 và z=12).\n if (canOverlap) {\n this.raiseToTopLayer(item);\n }\n\n this.version.update((v) => v + 1);\n itemChangeCallback?.();\n },\n };\n }\n\n /**\n *\n * Gắn component của nơi dùng lên một khối.\n *\n * @param host chỗ gắn RIÊNG của ô đó — mỗi khối một `ViewContainerRef`, dùng chung một cái thì\n * mọi component bị dồn hết vào một ô.\n */\n private attachNode(node: T_gridLayout_node, depth: number, host: ViewContainerRef): void {\n const config = this.config;\n\n if (!config) {\n return;\n }\n\n const existing = this.componentRefs.get(node.id);\n\n if (existing) {\n this.applyInputs(existing, node, depth);\n\n return;\n }\n\n // Khối tự khai thì hỏi nó; không thì hỏi hàm phân giải chung. Cả hai đều nhận `node` để đọc\n // được `data.type` mà quyết định trả component nào.\n const stream = (node.getComponentOutlet ?? config.getComponentOutlet)?.(node);\n\n if (!stream) {\n return;\n }\n\n // 🔴 Huỷ lượt tải CŨ của đúng khối này trước khi mở lượt mới.\n // Khối bị gỡ rồi gắn lại (đổi bố cục, sắp gọn) trong lúc `import()` chưa xong thì lượt cũ vẫn\n // chạy tiếp và dựng component vào một `ViewContainerRef` đã chết.\n this.pendingLoads.get(node.id)?.unsubscribe();\n\n const sub = stream.pipe(take(1)).subscribe((componentType) => {\n this.pendingLoads.delete(node.id);\n\n // Khối có thể đã bị xoá trong lúc chờ tải — dựng vào đây là rò rỉ.\n if (!this.treeService.findById(this.rootNode() ?? node, node.id)) {\n return;\n }\n\n const ref = host.createComponent(componentType);\n\n this.applyInputs(ref, node, depth);\n this.componentRefs.set(node.id, ref);\n });\n\n this.pendingLoads.set(node.id, sub);\n }\n\n /**\n * Gỡ component của một khối và huỷ nó. */\n private detachNode(nodeId: string): void {\n // Khối bị gỡ khi component của nó còn đang tải — phải huỷ lượt tải, không thì nó dựng vào một\n // `ViewContainerRef` đã chết.\n this.pendingLoads.get(nodeId)?.unsubscribe();\n this.pendingLoads.delete(nodeId);\n\n const ref = this.componentRefs.get(nodeId);\n\n if (!ref) {\n return;\n }\n\n ref.destroy();\n this.componentRefs.delete(nodeId);\n }\n\n private destroy(): void {\n for (const sub of this.pendingLoads.values()) {\n sub.unsubscribe();\n }\n\n for (const ref of this.componentRefs.values()) {\n ref.destroy();\n }\n\n this.pendingLoads.clear();\n this.componentRefs.clear();\n this.config = undefined;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────────────────\n // Nhóm hàm cho nơi dùng CHỦ ĐỘNG gọi — không phải chờ người dùng bấm nút trên khối.\n // Sửa cây thì dùng thẳng `TreeService` rồi gọi `refresh()` — xem demo trong core-ui.\n // ─────────────────────────────────────────────────────────────────────────────────────────\n\n /**\n * Báo cho service biết cây vừa đổi từ bên ngoài (thêm/xoá khối, sắp gọn, nạp bố cục khác).\n *\n * 🔴 PHẢI thay THAM CHIẾU của node gốc, không chỉ bơm `version`.\n * `@for … track item.id` giữ nguyên object khi id không đổi, nên `<gridster-item [item]=\"…\">` vẫn\n * nhận đúng object cũ và gridster không hề biết `x/y/cols/rows/layerIndex` vừa bị sửa. Bơm mỗi\n * `version` thì computed chạy lại nhưng trả về CÙNG mảng cũ — lưới đứng im (đo 28/08/2026: bấm\n * \"Sắp gọn\" cây đã dồn khít mà màn hình không nhúc nhích; bật chồng khối mà `$item.layerIndex`\n * vẫn trống).\n *\n * Nhân bản NÔNG cả node gốc và danh sách con là đủ: mảng mới → `@for` dựng lại item, object con\n * mới → gridster đọc lại toàn bộ toạ độ.\n */\n refresh(event?: T_gridLayout_changeEvent): void {\n const root = event?.root ?? this.rootNode();\n\n if (!root) {\n this.version.update((v) => v + 1);\n\n return;\n }\n\n if (this.config) {\n this.config = { ...this.config, data: root };\n }\n\n this.rootNode.set(this.shallowCloneTree(root));\n this.version.update((v) => v + 1);\n }\n\n /** Nhân bản nông theo cả chiều sâu: mỗi node và mảng `children` thành tham chiếu mới. */\n /**\n * Nhân bản SÂU cả cây — dùng cho `exportLayout()`.\n *\n * 🔴 BỎ `getComponentOutlet` khỏi bản sao: nó là một HÀM, không tuần tự hoá được nên gửi xuống\n * backend là hỏng (`JSON.stringify` nuốt mất, `structuredClone` ném lỗi). Nạp lại thì hàm phân\n * giải chung của cấu hình lo — component suy ra từ `data.type`, không cần lưu.\n */\n private deepCloneTree(node: T_gridLayout_node): T_gridLayout_node {\n const copy: T_gridLayout_node = { ...node };\n\n // Cắt hàm phân giải — xem chú thích trên.\n delete copy.getComponentOutlet;\n\n const { children, data, ...rest } = copy;\n\n return {\n ...rest,\n ...(data === undefined ? {} : { data: cloneDeep(data) }),\n ...(children ? { children: children.map((child) => this.deepCloneTree(child)) } : {}),\n };\n }\n\n private shallowCloneTree(node: T_gridLayout_node): T_gridLayout_node {\n if (!node.children?.length) {\n return { ...node };\n }\n\n return { ...node, children: node.children.map((child) => this.shallowCloneTree(child)) };\n }\n\n /**\n * Gán `layerIndex` khác nhau cho từng khối khi bật chồng khối.\n *\n * 🔴 `allowMultiLayer: true` MỘT MÌNH không cho khối đè nhau. Mã lib so `layerIndex` của hai khối:\n * KHÁC nhau thì cho đè, GIỐNG nhau vẫn báo va chạm. Khối không khai `layerIndex` đều rơi về\n * `defaultLayerIndex` — tức CÙNG lớp, nên kéo vào nhau là bị chặn y như lưới thường (người dùng\n * báo 28/08/2026: \"bật chồng khối nhưng khi kéo thì các khối vẫn di chuyển theo\").\n *\n * Nơi dùng tự khai `layerIndex` thì giữ nguyên; ai không khai thì thư viện xếp mỗi khối một lớp\n * theo thứ tự, đủ để mọi cặp đều khác lớp.\n */\n private assignOverlapLayers(root: T_gridLayout_node): void {\n const config = this.config;\n const canOverlap = config?.mode === E_gridLayout_mode_string.free || (config?.allowOverlap ?? false);\n\n if (!canOverlap) {\n return;\n }\n\n let layer = 0;\n const walk = (node: T_gridLayout_node): void => {\n for (const child of node.children ?? []) {\n if (child.layerIndex === undefined) {\n layer += 1;\n child.layerIndex = layer;\n }\n\n walk(child);\n }\n };\n\n walk(root);\n\n }\n\n /** Đưa một khối lên trên tất cả: lớp của nó = lớp lớn nhất đang có + 1. */\n private raiseToTopLayer(item: GridsterItem): void {\n const root = this.rootNode();\n\n if (!root) {\n return;\n }\n\n let topLayer = 0;\n const walk = (node: T_gridLayout_node): void => {\n for (const child of node.children ?? []) {\n topLayer = Math.max(topLayer, child.layerIndex ?? 0);\n walk(child);\n }\n };\n\n walk(root);\n\n // `item` gridster trả về CHÍNH là object node của cây (truyền thẳng qua `[item]`), nên gán vào\n // đây là cây cũng đổi theo.\n const node = item as unknown as T_gridLayout_node;\n\n if (node.layerIndex === topLayer) {\n\n return;\n }\n\n node.layerIndex = topLayer + 1;\n }\n\n // ─────────────────────────────────────────────────────────────────────────────────────────\n // SỬA CÂY — thư viện đã giữ cây nên bạn KHÔNG phải truyền nó vào.\n // Mọi hàm dưới đây tự vẽ lại; không cần gọi `refresh()`.\n // ─────────────────────────────────────────────────────────────────────────────────────────\n\n /**\n * Thêm một khối.\n *\n * @param item khối mới — KHÔNG cần `id` (thư viện sinh) và `x`/`y` (tự đặt xuống hàng trống).\n * @param parentId container muốn thêm vào; bỏ trống thì thêm vào mặt phẳng gốc.\n * @returns khối vừa tạo (kèm `id` vừa sinh), hoặc `undefined` khi không tìm thấy container.\n */\n addBlock(item: T_gridLayout_newNode, parentId?: string): T_gridLayout_node | undefined {\n const root = this.rootNode();\n const added = root ? this.treeService.addChild(root, parentId ?? root.id, item) : undefined;\n\n if (added) {\n this.refresh();\n }\n\n return added;\n }\n\n /** Xoá một khối. Xoá container là xoá cả ruột. Trả `false` khi không xoá được (vd khối gốc). */\n removeBlock(id: string): boolean {\n const root = this.rootNode();\n const done = root ? this.treeService.removeById(root, id) : false;\n\n if (done) {\n this.refresh();\n }\n\n return done;\n }\n\n /** 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. */\n toContainer(id: string): boolean {\n const root = this.rootNode();\n const done = root ? this.treeService.toContainer(root, id, this.config?.cols ?? 12) : false;\n\n if (done) {\n this.refresh();\n }\n\n return done;\n }\n\n /** Đưa CONTAINER về khối đơn — khối con được nâng lên cấp trên, không bị xoá. */\n toSingle(id: string): boolean {\n const root = this.rootNode();\n const done = root ? this.treeService.toSingle(root, id) : false;\n\n if (done) {\n this.refresh();\n }\n\n return done;\n }\n\n /** Dồn khối khít lên trên, lấp hết chỗ trống. Đệ quy vào container. */\n compact(): void {\n const root = this.rootNode();\n\n if (!root) {\n return;\n }\n\n this.treeService.compact(root, this.config?.cols ?? 12);\n this.refresh();\n }\n\n /**\n * Bố cục HIỆN TẠI, đã cắt mọi tham chiếu — gửi thẳng xuống backend được.\n *\n * Dùng khi bấm nút Lưu. Khác `Root()` ở chỗ đây là bản SAO: nơi dùng có sửa gì trên kết quả cũng\n * không đụng tới cây mà thư viện đang vẽ.\n *\n * @returns `undefined` khi mặt phẳng chưa `init()`.\n */\n exportLayout(): T_gridLayout_node | undefined {\n const root = this.rootNode();\n\n return root ? this.deepCloneTree(root) : undefined;\n }\n\n /** Tìm một khối theo id. */\n findBlock(id: string): T_gridLayout_node | undefined {\n const root = this.rootNode();\n\n return root ? this.treeService.findById(root, id) : undefined;\n }\n\n /** Tìm khối cha trực tiếp của một khối. */\n findParentBlock(id: string): T_gridLayout_node | undefined {\n const root = this.rootNode();\n\n return root ? this.treeService.findParent(root, id) : undefined;\n }\n\n /**\n * Cổng DUY NHẤT cho canvas của thư viện. Nơi dùng KHÔNG gọi tới đây.\n *\n * Mọi thứ canvas cần đều là `private` để không lọt ra API công khai; gom vào một getter theo đúng\n * khuôn `FunctionsControl` của bộ quy ước, thay vì để rải rác hàm public mà người ngoài tưởng\n * mình phải gọi (người dùng chốt 28/08/2026).\n */\n get FunctionsControl() {\n return {\n maxContainerDepth: () => this.maxContainerDepth(),\n containerPadding: () => this.containerPadding(),\n gridsterOptions: (depth: number, cb?: () => void) => this.buildGridsterOptions(depth, cb),\n attachNode: (node: T_gridLayout_node, depth: number, host: ViewContainerRef) => this.attachNode(node, depth, host),\n detachNode: (nodeId: string) => this.detachNode(nodeId),\n };\n }\n\n private applyInputs(ref: ComponentRef<unknown>, node: T_gridLayout_node, depth: number): void {\n ref.setInput('data', node.data);\n ref.setInput('node', node);\n ref.setInput('isEditMode', this.editMode());\n ref.setInput('depth', depth);\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n OnDestroy,\n ViewContainerRef,\n computed,\n inject,\n input,\n output,\n effect,\n viewChild,\n viewChildren,\n} from '@angular/core';\nimport { LibsUiComponentsButtonsButtonComponent } from '@libs-ui/components-buttons-button';\nimport { GridsterComponent, GridsterModule } from 'gridster18';\nimport { emptyRootNode } from '../defines/gridster-config.define';\nimport { LibsUiGridLayoutService } from '../grid-layout.service';\nimport {\n T_gridLayout_actionEvent,\n T_gridLayout_addEvent,\n T_gridLayout_changeEvent,\n T_gridLayout_newNode,\n T_gridLayout_node,\n} from '../interfaces/grid-layout.interface';\n\n/**\n * 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.\n *\n * 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\n * `nodeComponentType` qua `LibsUiGridLayoutService.init()`. Thư viện tự gắn component cho mọi khối.\n */\n@Component({\n selector: 'libs_ui-services-grid_layout-canvas',\n standalone: true,\n imports: [GridsterModule, LibsUiComponentsButtonsButtonComponent],\n templateUrl: './grid-layout-canvas.component.html',\n styleUrl: './grid-layout-canvas.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class LibsUiGridLayoutCanvasComponent implements OnDestroy {\n /**\n * @internal Chỉ mặt phẳng LỒNG dùng — thư viện tự truyền khi vẽ ruột container.\n *\n * Nơi dùng KHÔNG truyền: mặt phẳng gốc đọc cây từ service (đã đưa vào qua `init()`), truyền thêm\n * ở đây là khai hai lần cùng một thứ (người dùng chốt 28/08/2026).\n */\n readonly node = input<T_gridLayout_node>();\n /** @internal Chỉ mặt phẳng LỒNG dùng — thư viện tự truyền. */\n readonly depth = input<number>(0);\n\n /**\n * Bố cục vừa đổi do NGƯỜI DÙNG kéo hoặc đổi kích thước khối.\n *\n * Nơi dùng tự gọi `addBlock` · `removeBlock` · `compact` thì KHÔNG bắn — họ vừa gọi thì đã biết,\n * bắn ngược lại là thừa (người dùng chốt 28/08/2026).\n */\n readonly outChange = output<T_gridLayout_changeEvent>();\n /**\n * Người dùng bấm nút XOÁ trên một khối. Thư viện CHƯA xoá — gọi `event.confirm()` để xoá thật.\n * Cần hỏi trước thì hỏi xong mới gọi; không cho phép thì đừng gọi.\n */\n readonly outRemoveNode = output<T_gridLayout_actionEvent>();\n /**\n * Người dùng bấm nút THÊM trên một container. Thư viện KHÔNG tự thêm — nó không biết bạn có\n * những loại khối nào. Mở menu chọn loại rồi gọi `event.confirm(item)` với khối bạn muốn thêm.\n */\n readonly outAddBlockInside = output<T_gridLayout_addEvent>();\n /** 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. */\n readonly outToggleType = output<T_gridLayout_actionEvent>();\n\n\n private readonly gridsterRef = viewChild(GridsterComponent);\n /** mỗi ô một chỗ gắn riêng — cùng thứ tự với `children()` */\n private readonly itemHosts = viewChildren('itemHost', { read: ViewContainerRef });\n\n /** 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. */\n protected readonly dragBarDots = [0, 1, 2, 3, 4, 5];\n\n private readonly service = inject(LibsUiGridLayoutService);\n\n /**\n * Node THẬT của mặt phẳng này.\n *\n * 🔴 Mặt phẳng GỐC (depth 0) đọc cây từ SERVICE, không dùng input.\n * Nơi dùng thường sửa cây TẠI CHỖ (`treeService.compact(tree)`) rồi gọi `refresh()`, nên signal\n * `root()` của họ giữ nguyên tham chiếu và input `node` không hề đổi — `@for` không dựng lại\n * `gridster-item`, lưới đứng im dù dữ liệu đã khác (đo 28/08/2026: bấm \"Sắp gọn\" không tác dụng).\n * `refresh()` của service thay tham chiếu cây, nên đọc từ đó mới thấy thay đổi.\n *\n * Mặt phẳng LỒNG vẫn dùng input: nó nhận node con từ mặt phẳng cha, vốn đã là object mới.\n */\n protected readonly currentNode = computed(() => {\n this.service.Version();\n\n if (this.depth() > 0) {\n return this.node() ?? emptyRootNode();\n }\n\n return this.service.Root() ?? emptyRootNode();\n });\n\n protected readonly children = computed(() => this.currentNode().children ?? []);\n\n protected readonly isNestedCanvas = computed(() => this.depth() > 0);\n\n /** trần lồng container — khối đã chạm trần thì không cho chuyển thành container */\n protected readonly maxContainerDepth = computed(() => this.service.FunctionsControl.maxContainerDepth());\n\n /**\n * Lề trong của container, px.\n *\n * 🔴 Đặt trên VỎ container, không đặt vào lưới con. Lưới con giữ nguyên `margin: 2` như cũ; lề\n * này chỉ co ruột lại khỏi biên vỏ, nhờ đó tay cầm đổi kích thước của khối con luôn nằm trong tầm\n * chuột (người dùng chốt 28/08/2026).\n */\n protected readonly containerPadding = computed(() => `${this.service.FunctionsControl.containerPadding()}px`);\n\n /** Dải kéo chỉ hiện ở chế độ sửa — chế độ xem không kéo được nên hiện dải là gây nhầm. */\n protected readonly isEditMode = computed(() => this.service.IsEditMode());\n\n protected readonly gridOptions = computed(() => {\n // Đọc `Version` để `init()` lại (đổi chế độ, bật/tắt chồng khối) sinh ra options MỚI —\n // gridster chỉ áp dụng cấu hình khi tham chiếu `options` đổi.\n this.service.Version();\n\n return this.service.FunctionsControl.gridsterOptions(this.depth(), () => this.outChange.emit({ root: this.currentNode() }));\n });\n\n constructor() {\n // Gắn component của nơi dùng vào từng ô, và gắn LẠI mỗi khi cây đổi.\n // Dùng effect vì `itemHosts()` chỉ có giá trị sau khi Angular dựng xong @for.\n effect(() => {\n const hosts = this.itemHosts();\n const items = this.children();\n\n items.forEach((item, i) => {\n const host = hosts[i];\n\n if (!host) {\n return;\n }\n\n // Khối có con là CONTAINER: ruột nó do mặt phẳng lồng vẽ, không gắn component nội dung.\n if ((item.children?.length ?? 0) > 0) {\n return;\n }\n\n this.service.FunctionsControl.attachNode(item, this.depth() + 1, host);\n });\n });\n }\n\n ngOnDestroy(): void {\n for (const item of this.children()) {\n this.service.FunctionsControl.detachNode(item.id);\n }\n }\n\n /** 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. */\n get FunctionControl() {\n return {\n veLaiLuoi: () => this.gridsterRef()?.optionsChanged(),\n };\n }\n\n protected handlerTrackNode(_index: number, item: T_gridLayout_node): string {\n return item.id;\n }\n\n /**\n * 🔴 Chặn `mousedown` nổi lên lưới CHA khi thao tác trong lưới con.\n * Không chặn thì cả hai lưới cùng nhận một cú bấm: kéo khối con là nhấc luôn container.\n */\n /**\n * Chặn `mousedown` của khối trong lưới LỒNG nổi lên lưới cha.\n *\n * Chỉ chặn ở lưới lồng: lưới cấp trang không có cha nào để chặn, và chặn ở đó sẽ nuốt luôn thao\n * tác của chính nó.\n */\n /**\n * 🔴 Ba handler dưới đây PHẢI `stopPropagation`.\n * Nút nằm trong vùng mang `dragHandleClass`, không chặn thì cú bấm bị gridster hiểu là bắt đầu kéo\n * và `delayStart: 160` nuốt luôn `click` — người dùng thấy như nút chết.\n */\n protected handlerRemove(event: Event, node: T_gridLayout_node): void {\n event.stopPropagation();\n event.preventDefault();\n this.outRemoveNode.emit({ nodeId: node.id, node, confirm: () => this.service.removeBlock(node.id) });\n }\n\n protected handlerAddInside(event: Event, node: T_gridLayout_node): void {\n event.stopPropagation();\n event.preventDefault();\n this.outAddBlockInside.emit({\n nodeId: node.id,\n node,\n confirm: (item: T_gridLayout_newNode) => this.service.addBlock(item, node.id),\n });\n }\n\n protected handlerToggleType(event: Event, node: T_gridLayout_node): void {\n event.stopPropagation();\n event.preventDefault();\n this.outToggleType.emit({\n nodeId: node.id,\n node,\n // Gộp hay tách suy ra từ chính khối: đang là container thì tách, không thì gộp.\n confirm: () => (this.isContainer(node) ? this.service.toSingle(node.id) : this.service.toContainer(node.id)),\n });\n }\n\n /** khối này có phải container không (có khối con) */\n protected isContainer(item: T_gridLayout_node): boolean {\n return (item.children?.length ?? 0) > 0;\n }\n\n /** container đã chạm trần lồng thì không cho thêm khối vào trong nữa */\n protected canAddInside(item: T_gridLayout_node): boolean {\n return this.isContainer(item) && this.depth() < this.maxContainerDepth();\n }\n\n /** khối đơn ở cấp chưa chạm trần mới được chuyển thành container */\n protected canToggleType(item: T_gridLayout_node): boolean {\n if (this.isContainer(item)) {\n return true;\n }\n\n return this.depth() < this.maxContainerDepth();\n }\n\n protected handlerBlockParentDrag(event: Event): void {\n if (!this.isNestedCanvas()) {\n return;\n }\n\n event.stopPropagation();\n }\n\n protected handlerMouseDownTrongLuoi(event: Event): void {\n if (this.depth() === 0) {\n return;\n }\n\n event.stopPropagation();\n }\n}\n","<!-- 🔴 `relative` là BẮT BUỘC, CẤM bỏ.\n gridster đổi vị trí chuột thành ô lưới bằng `e.clientY + (el.scrollTop - el.offsetTop)` — công\n thức đó chỉ đúng khi `offsetParent` của <gridster> CHÍNH LÀ khối cuộn này. Thiếu `relative` thì\n `offsetParent` nhảy lên khối bao ngoài: `offsetTop` trả 16 trong khi lệch thật là 65 → gridster\n tính ô sai 49px, ô chờ đặt hiện lệch khỏi con trỏ nên kéo thả không đổi được vị trí, và kéo mép\n cũng tính sai điểm dừng. -->\n<div\n class=\"libs-ui-grid-layout-scroll-area relative h-full min-h-0 w-full min-w-0\"\n [class.overflow-y-auto]=\"!isNestedCanvas()\"\n (mousedown)=\"handlerMouseDownTrongLuoi($event)\">\n <gridster class=\"h-full w-full\" [options]=\"gridOptions()\">\n @for (item of children(); track item.id) {\n <!-- 🔴 Chặn `mousedown` tại ĐÂY cho lưới lồng — CẤM bỏ và CẤM chuyển sang chỗ khác.\n `ignoreContentClass` một mình KHÔNG đủ: `delayStart: 160` làm MỖI lưới lên lịch\n `dragStart` bằng `setTimeout` ngay tại `mousedown`, còn `stopPropagation` mà lib gọi bên\n trong `dragStart` chạy sau 160ms nên quá muộn — lưới CHA đã kịp nhận (đo 28/08/2026: kéo\n dải khối con, chuỗi class duyệt lên ĐÚNG mà container vẫn nhảy 0,0 → 0,1).\n Gắn ở `gridster-item` là đúng biên: lưới con nghe trên chính phần tử này nên đã nhận xong,\n lưới cha ở ngoài bị chặn. -->\n <gridster-item\n [item]=\"item\"\n (mousedown)=\"handlerBlockParentDrag($event)\">\n <!-- 🔴 Vỏ này mang `dragHandleClass` — CẤM bỏ.\n `ignoreContent: true` bắt gridster CHỈ cho kéo khi chỗ bấm nằm dưới một phần tử có\n `dragHandleClass`. Không có vỏ này thì component của nơi dùng (thư viện không kiểm soát\n được class của nó) sẽ không có tay cầm nào, và KHÔNG khối nào kéo được — đo 28/08/2026:\n kéo 320px, toạ độ khối giữ nguyên x=3,y=0.\n Tên class theo cấp: lưới trang và lưới lồng dùng hai tên KHÁC nhau, nếu không lưới cha\n cũng nhận ra tay cầm của khối con và nhấc luôn container. -->\n <!-- 🔴 Vỏ KHÔNG mang `dragHandleClass` — CẤM thêm lại.\n Kéo chỉ được phép bắt đầu từ DẢI XANH (người dùng chốt 28/08/2026: \"phải hover vào vùng\n màu xanh thì mới kéo thả được cho đồng nhất\"). Cho cả vỏ làm tay cầm thì bấm đâu cũng\n kéo, và khối con nằm trong container sẽ nhấc luôn container vì lưới cha duyệt lên gặp\n tay cầm của vỏ container trước.\n Vỏ khối con vẫn giữ class CHẶN để lưới cha dừng đúng ở biên. -->\n <div\n class=\"libs-ui-grid-layout-block-shell group relative h-full w-full min-w-0\"\n [class.libs-ui-grid-layout-block-parent-drag]=\"isNestedCanvas()\">\n <!-- Dải kéo: hiện khi rê chuột vào khối, ba chấm là dấu hiệu kéo quen thuộc.\n Người dùng cần một chỗ bám RÕ RÀNG thay vì đoán xem bấm đâu thì kéo được\n (chốt 28/08/2026, theo đúng dải đang dùng ở màn Customer 360 của mobio-web). -->\n @if (isEditMode()) {\n <!-- 🔴 Dải PHẢI mang tay cầm ĐÚNG CẤP của nó. Dải nhận chuột (`pointer-events: auto`),\n nên nếu không mang tay cầm thì `checkDragHandleClass` duyệt lên gặp tay cầm của\n container và nhấc container thay vì khối con (đo 28/08/2026: kéo dải khối con,\n container nhảy 0,0 → 0,1 còn khối con đứng im).\n 🔴 Dải CHỈ mang tay cầm, CẤM mang thêm class chặn: `checkDragHandleClass` xét hai\n class đó trên CÙNG một node nên để chung là kéo hỏng (đo 28/08/2026: bấm dải khối\n con kéo 140px, khối đứng im). Việc chặn lưới cha do VỎ KHỐI lo. -->\n <div\n class=\"libs-ui-grid-layout-drag-bar\"\n [class.libs-ui-grid-layout-drag-bar-child]=\"isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle]=\"!isNestedCanvas()\"\n [class.libs-ui-grid-layout-drag-handle-child]=\"isNestedCanvas()\">\n <span class=\"libs-ui-grid-layout-drag-bar-dots\">\n @for (dot of dragBarDots; track dot) {\n <span class=\"libs-ui-grid-layout-drag-bar-dot\"></span>\n }\n </span>\n </div>\n }\n\n <!-- Khung chứa component của nơi dùng.\n 🔴 PHẢI có khung thật (không phải `ng-container` trống): nó lo hộ nơi dùng phần lấp đầy\n ô và viền báo chế độ sửa. Không có khung thì mỗi component nội dung lại phải tự viết\n `:host { width/height: 100% }` + viền đứt — đó là việc của thư viện, không phải của\n người dùng (người dùng chốt 28/08/2026).\n Mỗi khối một ViewContainerRef riêng nên component nằm đúng ô của nó. -->\n <div\n class=\"libs-ui-grid-layout-content-frame\"\n [class.libs-ui-grid-layout-content-frame-edit]=\"isEditMode() && !isContainer(item)\"\n [class.libs-ui-grid-layout-content-frame-empty]=\"isContainer(item)\">\n <ng-container #itemHost />\n </div>\n\n <!-- Dấu hiệu CONTAINER: nhãn góc trên-phải + viền đứt bao ruột. Không có dấu hiệu thì\n người dùng không biết khối nào chứa được khối khác (chốt 28/08/2026, theo đúng cách\n màn Customer 360 của mobio-web đang làm). -->\n @if (isEditMode() && isContainer(item)) {\n <div class=\"libs-ui-grid-layout-container-label\">Khối ghép</div>\n <div class=\"libs-ui-grid-layout-container-border\"></div>\n }\n\n <!-- Thanh công cụ: hiện khi rê chuột vào khối.\n 🔴 Dùng `libs_ui-components-buttons-button` — CẤM tự dựng thẻ `<button>`.\n Component này đã có sẵn kiểu nút, cỡ nút và bong bóng chú thích theo đúng bộ giao diện\n của sản phẩm; tự dựng là mỗi nơi một kiểu và mất luôn tooltip. -->\n @if (isEditMode()) {\n <div\n class=\"libs-ui-grid-layout-toolbar\"\n [class.libs-ui-grid-layout-toolbar-child]=\"isNestedCanvas()\">\n @if (canAddInside(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-add'\"\n [popover]=\"{ config: { content: 'Thêm khối vào trong', zIndex: 1300 } }\"\n (outClick)=\"handlerAddInside($event, item)\" />\n }\n @if (canToggleType(item)) {\n <libs_ui-components-buttons-button\n [type]=\"'button-third'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"isContainer(item) ? 'libs-ui-icon-split-cell' : 'libs-ui-icon-merge-cell'\"\n [popover]=\"{ config: { content: isContainer(item) ? 'Đưa về khối đơn' : 'Chuyển thành khối ghép', zIndex: 1300 } }\"\n (outClick)=\"handlerToggleType($event, item)\" />\n }\n <libs_ui-components-buttons-button\n [type]=\"'button-third-hover-danger'\"\n [iconOnlyType]=\"true\"\n [sizeButton]=\"'smaller'\"\n [classIconLeft]=\"'libs-ui-icon-remove'\"\n [popover]=\"{ config: { content: isContainer(item) ? 'Xoá cả khối ghép' : 'Xoá khối', zIndex: 1300 } }\"\n (outClick)=\"handlerRemove($event, item)\" />\n </div>\n }\n\n @if (item.children && item.children.length > 0) {\n <!-- Khối có con → ruột nó lại là một mặt phẳng nữa. Đệ quy ở đây, nơi dùng không phải lo.\n Lề đặt ở ĐÂY (vỏ container), không đặt vào lưới con — lưới con giữ nguyên khe 2px. -->\n <libs_ui-services-grid_layout-canvas\n class=\"block h-full w-full\"\n [style.padding]=\"containerPadding()\"\n [node]=\"item\"\n [depth]=\"depth() + 1\"\n (outChange)=\"outChange.emit($event)\"\n (outRemoveNode)=\"outRemoveNode.emit($event)\"\n (outAddBlockInside)=\"outAddBlockInside.emit($event)\"\n (outToggleType)=\"outToggleType.emit($event)\" />\n }\n </div>\n </gridster-item>\n }\n </gridster>\n</div>\n","// Bề mặt CÔNG KHAI — chỉ những gì nơi dùng thật sự cần.\n//\n// `LibsUiGridLayoutTreeService` CỐ Ý không xuất: mọi thao tác trên cây đã có mặt trên\n// `LibsUiGridLayoutService` với chữ ký gọn hơn (không phải truyền `root` — thư viện đang giữ nó).\n// Cấu hình gridster cũng không xuất: nó đã được vá theo hàng chục lỗi đo trên sản phẩm thật, để nơi\n// dùng sửa lại là mở đường cho chính những lỗi đó quay về.\nexport * from './grid-layout.service';\nexport * from './canvas/grid-layout-canvas.component';\nexport * from './interfaces/grid-layout.interface';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { ViewContainerRef } from '@angular/core';
|
|
2
|
+
import { GridsterConfig } from 'gridster18';
|
|
3
|
+
import { T_gridLayout_changeEvent, T_gridLayout_newNode, T_gridLayout_config, T_gridLayout_node } from './interfaces/grid-layout.interface';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Dựng một mặt phẳng kéo thả và tự gắn component của nơi dùng lên từng khối.
|
|
7
|
+
*
|
|
8
|
+
* Nơi dùng chỉ cần đưa vào: loại component muốn dựng
|
|
9
|
+
* (`nodeComponentType`) và cây dữ liệu (`data`). Toàn bộ việc vẽ lưới, kéo thả, đổi kích thước,
|
|
10
|
+
* lồng container và dọn dẹp do thư viện lo.
|
|
11
|
+
*
|
|
12
|
+
* 🔴 Đây là service theo TỪNG mặt phẳng, KHÔNG `providedIn: 'root'`. Khai trong `providers` của
|
|
13
|
+
* component dùng nó; hai mặt phẳng trên cùng một màn phải là hai thực thể riêng, dùng chung thì
|
|
14
|
+
* `componentRefs` của mặt phẳng này bị mặt phẳng kia dọn mất.
|
|
15
|
+
*/
|
|
16
|
+
export declare class LibsUiGridLayoutService {
|
|
17
|
+
private readonly treeService;
|
|
18
|
+
/** Lượt tải component đang chờ, theo id khối — để huỷ khi khối bị gỡ giữa chừng. */
|
|
19
|
+
private readonly pendingLoads;
|
|
20
|
+
constructor();
|
|
21
|
+
private config?;
|
|
22
|
+
private readonly componentRefs;
|
|
23
|
+
private readonly rootNode;
|
|
24
|
+
private readonly editMode;
|
|
25
|
+
/** tăng mỗi lần cây đổi — component con đọc để biết phải tính lại */
|
|
26
|
+
private readonly version;
|
|
27
|
+
/**
|
|
28
|
+
* Signal chỉ đọc để nơi dùng theo dõi.
|
|
29
|
+
*
|
|
30
|
+
* 🔴 Là THUỘC TÍNH, không phải getter. Getter trả signal thì chỗ đọc phải viết `Version()()` —
|
|
31
|
+
* quên cặp ngoặc thứ hai là computed KHÔNG phụ thuộc vào nó, cây đổi mà lưới đứng im, và
|
|
32
|
+
* TypeScript không hề báo lỗi (đo 28/08/2026: thêm khối không hiện, sắp gọn không nhúc nhích).
|
|
33
|
+
*/
|
|
34
|
+
readonly Root: import("@angular/core").Signal<T_gridLayout_node | undefined>;
|
|
35
|
+
readonly IsEditMode: import("@angular/core").Signal<boolean>;
|
|
36
|
+
/** tăng mỗi lần cây hoặc cấu hình đổi */
|
|
37
|
+
readonly Version: import("@angular/core").Signal<number>;
|
|
38
|
+
/**
|
|
39
|
+
* trần số cấp container lồng nhau — canvas đọc để biết có cho chuyển thành container không */
|
|
40
|
+
private maxContainerDepth;
|
|
41
|
+
/**
|
|
42
|
+
* lề trong của container, px — canvas đọc để đặt padding lên vỏ container */
|
|
43
|
+
private containerPadding;
|
|
44
|
+
/** Khởi tạo mặt phẳng. Gọi lại sẽ dọn sạch component cũ rồi dựng lại từ đầu. */
|
|
45
|
+
init(config: T_gridLayout_config): void;
|
|
46
|
+
setEditMode(value: boolean): void;
|
|
47
|
+
/**
|
|
48
|
+
* Bật/tắt cho khối chồng lên nhau — KHÔNG dựng lại mặt phẳng.
|
|
49
|
+
*
|
|
50
|
+
* Trước đây nơi dùng phải gọi `init()` lại để đổi cờ này, mà `init()` huỷ sạch rồi dựng lại mọi
|
|
51
|
+
* component nội dung — lãng phí, và người dùng thấy màn chớp (người dùng chốt 28/08/2026).
|
|
52
|
+
*/
|
|
53
|
+
setAllowOverlap(value: boolean): void;
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
* Cấu hình gridster cho một cấp lưới.
|
|
57
|
+
*
|
|
58
|
+
* @param depth 0 = lưới cấp trang, ≥ 1 = lưới lồng trong container
|
|
59
|
+
*/
|
|
60
|
+
private buildGridsterOptions;
|
|
61
|
+
/**
|
|
62
|
+
*
|
|
63
|
+
* Gắn component của nơi dùng lên một khối.
|
|
64
|
+
*
|
|
65
|
+
* @param host chỗ gắn RIÊNG của ô đó — mỗi khối một `ViewContainerRef`, dùng chung một cái thì
|
|
66
|
+
* mọi component bị dồn hết vào một ô.
|
|
67
|
+
*/
|
|
68
|
+
private attachNode;
|
|
69
|
+
/**
|
|
70
|
+
* Gỡ component của một khối và huỷ nó. */
|
|
71
|
+
private detachNode;
|
|
72
|
+
private destroy;
|
|
73
|
+
/**
|
|
74
|
+
* Báo cho service biết cây vừa đổi từ bên ngoài (thêm/xoá khối, sắp gọn, nạp bố cục khác).
|
|
75
|
+
*
|
|
76
|
+
* 🔴 PHẢI thay THAM CHIẾU của node gốc, không chỉ bơm `version`.
|
|
77
|
+
* `@for … track item.id` giữ nguyên object khi id không đổi, nên `<gridster-item [item]="…">` vẫn
|
|
78
|
+
* nhận đúng object cũ và gridster không hề biết `x/y/cols/rows/layerIndex` vừa bị sửa. Bơm mỗi
|
|
79
|
+
* `version` thì computed chạy lại nhưng trả về CÙNG mảng cũ — lưới đứng im (đo 28/08/2026: bấm
|
|
80
|
+
* "Sắp gọn" cây đã dồn khít mà màn hình không nhúc nhích; bật chồng khối mà `$item.layerIndex`
|
|
81
|
+
* vẫn trống).
|
|
82
|
+
*
|
|
83
|
+
* Nhân bản NÔNG cả node gốc và danh sách con là đủ: mảng mới → `@for` dựng lại item, object con
|
|
84
|
+
* mới → gridster đọc lại toàn bộ toạ độ.
|
|
85
|
+
*/
|
|
86
|
+
refresh(event?: T_gridLayout_changeEvent): void;
|
|
87
|
+
/** Nhân bản nông theo cả chiều sâu: mỗi node và mảng `children` thành tham chiếu mới. */
|
|
88
|
+
/**
|
|
89
|
+
* Nhân bản SÂU cả cây — dùng cho `exportLayout()`.
|
|
90
|
+
*
|
|
91
|
+
* 🔴 BỎ `getComponentOutlet` khỏi bản sao: nó là một HÀM, không tuần tự hoá được nên gửi xuống
|
|
92
|
+
* backend là hỏng (`JSON.stringify` nuốt mất, `structuredClone` ném lỗi). Nạp lại thì hàm phân
|
|
93
|
+
* giải chung của cấu hình lo — component suy ra từ `data.type`, không cần lưu.
|
|
94
|
+
*/
|
|
95
|
+
private deepCloneTree;
|
|
96
|
+
private shallowCloneTree;
|
|
97
|
+
/**
|
|
98
|
+
* Gán `layerIndex` khác nhau cho từng khối khi bật chồng khối.
|
|
99
|
+
*
|
|
100
|
+
* 🔴 `allowMultiLayer: true` MỘT MÌNH không cho khối đè nhau. Mã lib so `layerIndex` của hai khối:
|
|
101
|
+
* KHÁC nhau thì cho đè, GIỐNG nhau vẫn báo va chạm. Khối không khai `layerIndex` đều rơi về
|
|
102
|
+
* `defaultLayerIndex` — tức CÙNG lớp, nên kéo vào nhau là bị chặn y như lưới thường (người dùng
|
|
103
|
+
* báo 28/08/2026: "bật chồng khối nhưng khi kéo thì các khối vẫn di chuyển theo").
|
|
104
|
+
*
|
|
105
|
+
* Nơi dùng tự khai `layerIndex` thì giữ nguyên; ai không khai thì thư viện xếp mỗi khối một lớp
|
|
106
|
+
* theo thứ tự, đủ để mọi cặp đều khác lớp.
|
|
107
|
+
*/
|
|
108
|
+
private assignOverlapLayers;
|
|
109
|
+
/** Đưa một khối lên trên tất cả: lớp của nó = lớp lớn nhất đang có + 1. */
|
|
110
|
+
private raiseToTopLayer;
|
|
111
|
+
/**
|
|
112
|
+
* Thêm một khối.
|
|
113
|
+
*
|
|
114
|
+
* @param item khối mới — KHÔNG cần `id` (thư viện sinh) và `x`/`y` (tự đặt xuống hàng trống).
|
|
115
|
+
* @param parentId container muốn thêm vào; bỏ trống thì thêm vào mặt phẳng gốc.
|
|
116
|
+
* @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
|
+
*/
|
|
118
|
+
addBlock(item: T_gridLayout_newNode, parentId?: string): T_gridLayout_node | undefined;
|
|
119
|
+
/** 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
|
+
removeBlock(id: string): boolean;
|
|
121
|
+
/** 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
|
+
toContainer(id: string): boolean;
|
|
123
|
+
/** Đưa CONTAINER về khối đơn — khối con được nâng lên cấp trên, không bị xoá. */
|
|
124
|
+
toSingle(id: string): boolean;
|
|
125
|
+
/** Dồn khối khít lên trên, lấp hết chỗ trống. Đệ quy vào container. */
|
|
126
|
+
compact(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Bố cục HIỆN TẠI, đã cắt mọi tham chiếu — gửi thẳng xuống backend được.
|
|
129
|
+
*
|
|
130
|
+
* Dùng khi bấm nút Lưu. Khác `Root()` ở chỗ đây là bản SAO: nơi dùng có sửa gì trên kết quả cũng
|
|
131
|
+
* không đụng tới cây mà thư viện đang vẽ.
|
|
132
|
+
*
|
|
133
|
+
* @returns `undefined` khi mặt phẳng chưa `init()`.
|
|
134
|
+
*/
|
|
135
|
+
exportLayout(): T_gridLayout_node | undefined;
|
|
136
|
+
/** Tìm một khối theo id. */
|
|
137
|
+
findBlock(id: string): T_gridLayout_node | undefined;
|
|
138
|
+
/** Tìm khối cha trực tiếp của một khối. */
|
|
139
|
+
findParentBlock(id: string): T_gridLayout_node | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* Cổng DUY NHẤT cho canvas của thư viện. Nơi dùng KHÔNG gọi tới đây.
|
|
142
|
+
*
|
|
143
|
+
* Mọi thứ canvas cần đều là `private` để không lọt ra API công khai; gom vào một getter theo đúng
|
|
144
|
+
* khuôn `FunctionsControl` của bộ quy ước, thay vì để rải rác hàm public mà người ngoài tưởng
|
|
145
|
+
* mình phải gọi (người dùng chốt 28/08/2026).
|
|
146
|
+
*/
|
|
147
|
+
get FunctionsControl(): {
|
|
148
|
+
maxContainerDepth: () => number;
|
|
149
|
+
containerPadding: () => number;
|
|
150
|
+
gridsterOptions: (depth: number, cb?: () => void) => GridsterConfig;
|
|
151
|
+
attachNode: (node: T_gridLayout_node, depth: number, host: ViewContainerRef) => void;
|
|
152
|
+
detachNode: (nodeId: string) => void;
|
|
153
|
+
};
|
|
154
|
+
private applyInputs;
|
|
155
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<LibsUiGridLayoutService, never>;
|
|
156
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<LibsUiGridLayoutService>;
|
|
157
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { Type } from '@angular/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
/** Chế độ sắp xếp khối trên mặt phẳng */
|
|
4
|
+
export declare const enum E_gridLayout_mode_string {
|
|
5
|
+
/** Bám lưới: khối thẳng hàng, tự đẩy nhau, sắp gọn được */
|
|
6
|
+
grid = "grid",
|
|
7
|
+
/** Tự do: khối đặt ở toạ độ bất kỳ, chồng lên nhau thoải mái */
|
|
8
|
+
free = "free"
|
|
9
|
+
}
|
|
10
|
+
/** Một khối trên mặt phẳng. `children` khác rỗng thì khối là CONTAINER, ruột nó là một lưới con. */
|
|
11
|
+
export type T_gridLayout_node = {
|
|
12
|
+
id: string;
|
|
13
|
+
/** cột bắt đầu (chế độ lưới) — px theo trục X (chế độ tự do) */
|
|
14
|
+
x: number;
|
|
15
|
+
/** hàng bắt đầu (chế độ lưới) — px theo trục Y (chế độ tự do) */
|
|
16
|
+
y: number;
|
|
17
|
+
/** số cột chiếm (chế độ lưới) — px chiều rộng (chế độ tự do) */
|
|
18
|
+
cols: number;
|
|
19
|
+
/** số hàng chiếm (chế độ lưới) — px chiều cao (chế độ tự do) */
|
|
20
|
+
rows: number;
|
|
21
|
+
/**
|
|
22
|
+
* Lớp chồng. Hai khối CHỒNG nhau được khi khác `layerIndex` và `allowOverlap` bật.
|
|
23
|
+
* Bỏ trống thì mọi khối cùng lớp 0 — chồng nhau bị chặn như lưới thường.
|
|
24
|
+
*/
|
|
25
|
+
layerIndex?: number;
|
|
26
|
+
/** khối con; khác rỗng thì đây là container */
|
|
27
|
+
children?: Array<T_gridLayout_node>;
|
|
28
|
+
/**
|
|
29
|
+
* Component vẽ RIÊNG cho khối này, trả về dạng Observable để LAZY LOAD thật sự.
|
|
30
|
+
* Bỏ trống thì mặt phẳng hỏi `getComponentOutlet` chung của cấu hình.
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* getComponentOutlet: (node) => from(import('./chart-card.component').then((c) => c.ChartCardComponent))
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
getComponentOutlet?: (node: T_gridLayout_node) => Observable<Type<unknown>>;
|
|
37
|
+
/** dữ liệu riêng của nơi dùng — thư viện KHÔNG đọc, chuyển nguyên vào input `data` của component */
|
|
38
|
+
data?: unknown;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Đi kèm sự kiện của một NÚT built-in trên khối.
|
|
42
|
+
*
|
|
43
|
+
* Thư viện CHƯA làm gì khi bắn — nó chỉ báo "người dùng vừa bấm nút này". Nơi dùng quyết định:
|
|
44
|
+
* - cần hỏi trước (modal xác nhận, gọi API kiểm tra) → hỏi xong rồi gọi `confirm()`;
|
|
45
|
+
* - không cần hỏi → gọi `confirm()` ngay;
|
|
46
|
+
* - không cho phép → không gọi gì cả.
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
49
|
+
* protected handlerRemove(event: T_gridLayout_actionEvent): void {
|
|
50
|
+
* this.dialog.confirm('Xoá khối này?').subscribe((dongY) => dongY && event.confirm());
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export type T_gridLayout_actionEvent = {
|
|
55
|
+
/** khối người dùng vừa bấm nút */
|
|
56
|
+
nodeId: string;
|
|
57
|
+
/** khối đó, khi bạn cần đọc `data` để quyết định (vd khối đang có dữ liệu thì mới hỏi) */
|
|
58
|
+
node: T_gridLayout_node;
|
|
59
|
+
/**
|
|
60
|
+
* Gọi để thư viện thực hiện thao tác. Không gọi = không làm gì.
|
|
61
|
+
*
|
|
62
|
+
* @returns `false` khi thao tác không thực hiện được (khối đã bị xoá, chạm trần lồng cấp…).
|
|
63
|
+
*/
|
|
64
|
+
confirm: () => boolean;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Khối MỚI truyền vào `addChild`. Khác `T_gridLayout_node` ở ba chỗ do thư viện tự lo:
|
|
68
|
+
* - `id` tự sinh (mã máy dùng để tra cứu, không phải thứ người dùng nghĩ ra),
|
|
69
|
+
* - `x` · `y` tự đặt xuống hàng trống kế tiếp.
|
|
70
|
+
*/
|
|
71
|
+
export type T_gridLayout_newNode = Omit<T_gridLayout_node, 'id' | 'x' | 'y'> & {
|
|
72
|
+
/** để trống thì thư viện sinh; chỉ truyền khi bạn CẦN một id cố định (vd khôi phục từ bản lưu) */
|
|
73
|
+
id?: string;
|
|
74
|
+
x?: number;
|
|
75
|
+
y?: number;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Đi kèm sự kiện bấm nút THÊM trên một container.
|
|
79
|
+
*
|
|
80
|
+
* Khác `T_gridLayout_actionEvent` ở chỗ `confirm` nhận khối muốn thêm: thư viện không biết sản phẩm
|
|
81
|
+
* của bạn có những loại khối nào, nên bạn mở menu chọn loại rồi truyền khối đã dựng vào đây.
|
|
82
|
+
*/
|
|
83
|
+
export type T_gridLayout_addEvent = {
|
|
84
|
+
/** container người dùng muốn thêm khối vào */
|
|
85
|
+
nodeId: string;
|
|
86
|
+
node: T_gridLayout_node;
|
|
87
|
+
/** @returns khối vừa tạo (kèm `id` thư viện sinh), hoặc `undefined` nếu không thêm được */
|
|
88
|
+
confirm: (item: T_gridLayout_newNode) => T_gridLayout_node | undefined;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Đầu vào mà thư viện truyền cho component của nơi dùng.
|
|
92
|
+
* Component BẮT BUỘC khai đủ ba input này (dùng `input()`), thiếu cái nào thì `setInput` ném lỗi.
|
|
93
|
+
*/
|
|
94
|
+
export type T_gridLayout_nodeInputs<TData = unknown> = {
|
|
95
|
+
/** đúng `node.data` của khối — thứ nơi dùng cần nhất, nên để đầu */
|
|
96
|
+
data: TData;
|
|
97
|
+
/** cả khối, khi cần biết id · kích thước · có phải container không */
|
|
98
|
+
node: T_gridLayout_node;
|
|
99
|
+
/** đang ở chế độ sửa hay chỉ xem */
|
|
100
|
+
isEditMode: boolean;
|
|
101
|
+
/** độ sâu: 0 = gốc, 1 = khối cấp trang, 2 = khối trong container */
|
|
102
|
+
depth: number;
|
|
103
|
+
};
|
|
104
|
+
/** Cấu hình khởi tạo một mặt phẳng kéo thả */
|
|
105
|
+
export type T_gridLayout_config = {
|
|
106
|
+
/**
|
|
107
|
+
* Hàm PHÂN GIẢI component cho một khối — chỗ bạn map từ dữ liệu server sang giao diện.
|
|
108
|
+
*
|
|
109
|
+
* Mặt phẳng gọi hàm này cho MỖI khối không tự khai `getComponentOutlet`. Trả `Observable` nên
|
|
110
|
+
* `import()` động chạy được: bó mã của từng loại khối chỉ tải khi màn thật sự có khối loại đó.
|
|
111
|
+
*
|
|
112
|
+
* ```ts
|
|
113
|
+
* getComponentOutlet: (node) => {
|
|
114
|
+
* const { type } = node.data as { type: string };
|
|
115
|
+
*
|
|
116
|
+
* if (type === 'chart') {
|
|
117
|
+
* return from(import('./chart-card.component').then((c) => c.ChartCardComponent));
|
|
118
|
+
* }
|
|
119
|
+
*
|
|
120
|
+
* return from(import('./text-card.component').then((c) => c.TextCardComponent));
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
getComponentOutlet?: (node: T_gridLayout_node) => Observable<Type<unknown>>;
|
|
125
|
+
/** cây khối ban đầu */
|
|
126
|
+
data: T_gridLayout_node;
|
|
127
|
+
mode?: E_gridLayout_mode_string;
|
|
128
|
+
isEditMode?: boolean;
|
|
129
|
+
/** số cột của lưới (chế độ lưới). Mặc định 12 */
|
|
130
|
+
cols?: number;
|
|
131
|
+
/** chiều cao một hàng, px (chế độ lưới). Mặc định 56 */
|
|
132
|
+
rowHeight?: number;
|
|
133
|
+
/** khe giữa hai khối ở lưới CẤP TRANG, px. Mặc định 12 */
|
|
134
|
+
margin?: number;
|
|
135
|
+
/** 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
|
|
136
|
+
* một card, khe rộng làm chúng trông như card rời nhau */
|
|
137
|
+
nestedMargin?: number;
|
|
138
|
+
/** chiều cao một hàng bên TRONG container, px. Mặc định 16 — hàng nhỏ cho bước kéo mịn */
|
|
139
|
+
nestedRowHeight?: number;
|
|
140
|
+
/**
|
|
141
|
+
* Lề trong của CONTAINER, px — áp đều bốn chiều. Mặc định 12.
|
|
142
|
+
*
|
|
143
|
+
* Chỉ container có lề này; khối đơn không có, vì nó không chứa gì bên trong.
|
|
144
|
+
*
|
|
145
|
+
* 🔴 KHÔNG nên để 0: tay cầm đổi kích thước của khối con nằm sát biên container, không chừa lề
|
|
146
|
+
* thì chuột không trúng được nó và khối con mất luôn khả năng resize.
|
|
147
|
+
*/
|
|
148
|
+
containerPadding?: number;
|
|
149
|
+
/** viền ngoài quanh lưới cấp trang. Mặc định bật */
|
|
150
|
+
outerMargin?: boolean;
|
|
151
|
+
/** chừa thêm bao nhiêu px dưới đáy lưới cấp trang. Mặc định 40 */
|
|
152
|
+
outerMarginBottom?: number;
|
|
153
|
+
/** số hàng tối thiểu một khối được co lại. Mặc định 2 */
|
|
154
|
+
minItemRows?: number;
|
|
155
|
+
/** kích thước mặc định của khối mới, tính theo ô lưới. Mặc định 3 cột × 2 hàng */
|
|
156
|
+
defaultItemCols?: number;
|
|
157
|
+
defaultItemRows?: number;
|
|
158
|
+
/**
|
|
159
|
+
* Cho phép hai khối KHÁC `layerIndex` nằm chồng nhau.
|
|
160
|
+
* Chế độ `tuDo` luôn bật, không đọc cờ này.
|
|
161
|
+
*/
|
|
162
|
+
allowOverlap?: boolean;
|
|
163
|
+
/** trần số cấp container lồng nhau. Mặc định 1 (container không chứa container) */
|
|
164
|
+
maxContainerDepth?: number;
|
|
165
|
+
};
|
|
166
|
+
/** Sự kiện thư viện bắn ra khi bố cục đổi */
|
|
167
|
+
export type T_gridLayout_changeEvent = {
|
|
168
|
+
root: T_gridLayout_node;
|
|
169
|
+
/** 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
|
+
nodeId?: string;
|
|
171
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@libs-ui/services-grid-layout",
|
|
3
|
+
"version": "0.2.357-19",
|
|
4
|
+
"peerDependencies": {
|
|
5
|
+
"@angular/core": "^18.2.0",
|
|
6
|
+
"@angular/common": "^18.2.0",
|
|
7
|
+
"gridster18": "npm:angular-gridster2@18.0.1",
|
|
8
|
+
"rxjs": "~7.8.0",
|
|
9
|
+
"@libs-ui/components-buttons-button": "0.2.357-19",
|
|
10
|
+
"@libs-ui/utils": "0.2.357-19"
|
|
11
|
+
},
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"module": "fesm2022/libs-ui-services-grid-layout.mjs",
|
|
14
|
+
"typings": "index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
"./package.json": {
|
|
17
|
+
"default": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./index.d.ts",
|
|
21
|
+
"esm2022": "./esm2022/libs-ui-services-grid-layout.mjs",
|
|
22
|
+
"esm": "./esm2022/libs-ui-services-grid-layout.mjs",
|
|
23
|
+
"default": "./fesm2022/libs-ui-services-grid-layout.mjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"tslib": "^2.3.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { T_gridLayout_newNode, T_gridLayout_node } from './interfaces/grid-layout.interface';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
/**
|
|
4
|
+
* Thao tác trên cây khối: tìm, thêm, xoá, dồn khít, chặn lồng cấp quá sâu.
|
|
5
|
+
*
|
|
6
|
+
* 🔴 NỘI BỘ thư viện — không xuất ra ngoài. Nơi dùng đi qua `LibsUiGridLayoutService`, ở đó mỗi
|
|
7
|
+
* thao tác sửa bố cục vừa vẽ lại vừa bắn `outChange` đúng MỘT lần. Các hàm ở đây cố tình KHÔNG tự
|
|
8
|
+
* vẽ lại: để chúng tự gọi thì mỗi thao tác vẽ hai lượt và bắn hai sự kiện (người dùng chốt
|
|
9
|
+
* 28/08/2026: "đặt đúng các điểm mà làm cho layout có thể thay đổi thôi").
|
|
10
|
+
*/
|
|
11
|
+
export declare class LibsUiGridLayoutTreeService {
|
|
12
|
+
findById(node: T_gridLayout_node, id: string): T_gridLayout_node | undefined;
|
|
13
|
+
findParent(node: T_gridLayout_node, id: string): T_gridLayout_node | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Thêm khối vào cuối một container. Thư viện tự sinh `id` và tự đặt `x` · `y` xuống hàng trống.
|
|
16
|
+
*
|
|
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
|
+
*/
|
|
19
|
+
addChild(root: T_gridLayout_node, parentId: string, item: T_gridLayout_newNode): T_gridLayout_node | undefined;
|
|
20
|
+
/** Xoá một khối khỏi cây. Không cho xoá gốc. */
|
|
21
|
+
removeById(root: T_gridLayout_node, id: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Chuyển một khối đơn thành CONTAINER: nội dung cũ trở thành khối con đầu tiên.
|
|
24
|
+
*
|
|
25
|
+
* Không mất dữ liệu — `data` của khối được mang theo xuống khối con.
|
|
26
|
+
*
|
|
27
|
+
* @returns `false` khi không tìm thấy khối, hoặc khối đã là container rồi.
|
|
28
|
+
*/
|
|
29
|
+
toContainer(root: T_gridLayout_node, id: string, colCount: number): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Chuyển một CONTAINER về khối đơn: các khối con được NÂNG lên cấp trên, không bị xoá.
|
|
32
|
+
*
|
|
33
|
+
* @returns `false` khi không tìm thấy khối, hoặc khối vốn không phải container.
|
|
34
|
+
*/
|
|
35
|
+
toSingle(root: T_gridLayout_node, id: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Làm phẳng mọi container lồng vượt trần.
|
|
38
|
+
*
|
|
39
|
+
* 🔴 Cần thiết vì bố cục ĐÃ LƯU không đi qua luật chặn nào của giao diện. Một bản lưu cũ — sinh
|
|
40
|
+
* trước khi có trần, hoặc do thao tác lỗi — vẫn nạp thẳng vào và hiện container lồng container.
|
|
41
|
+
* Container vượt trần bị hạ thành khối thường, con của nó nâng lên cấp trên: KHÔNG mất dữ liệu.
|
|
42
|
+
*
|
|
43
|
+
* @returns số container đã làm phẳng
|
|
44
|
+
*/
|
|
45
|
+
flattenOverDepthContainers(node: T_gridLayout_node, maxDepth: number): number;
|
|
46
|
+
/**
|
|
47
|
+
* Dồn khối khít lên trên theo thứ tự đọc, lấp hết chỗ trống.
|
|
48
|
+
*
|
|
49
|
+
* 🔴 KHÔNG sửa `node.rows` của container. Chiều cao container tính theo đơn vị hàng của lưới CHA,
|
|
50
|
+
* còn `currentRow` đếm theo đơn vị hàng của lưới CON — trộn hai thang đo là container phình lên
|
|
51
|
+
* gấp mấy lần.
|
|
52
|
+
*/
|
|
53
|
+
compact(node: T_gridLayout_node, colCount: number): void;
|
|
54
|
+
private walkCompact;
|
|
55
|
+
private walkFlatten;
|
|
56
|
+
private nextFreeRow;
|
|
57
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<LibsUiGridLayoutTreeService, never>;
|
|
58
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<LibsUiGridLayoutTreeService>;
|
|
59
|
+
}
|