import * as _angular_core from '@angular/core'; import { TemplateRef, Signal, InjectionToken, AfterViewInit, OnDestroy } from '@angular/core'; import { SdBadge } from '@sdcorejs/angular/components/badge'; import { SdSearch } from '@sdcorejs/angular/forms/models'; import { Operator, NestedKeyOf, Order, PagingReq, Color } from '@sdcorejs/utils/models'; import { SdUnwrapSignal } from '@sdcorejs/angular/utilities/models'; import { MatPaginatorIntl, MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { MatTable } from '@angular/material/table'; import { SdExcelColumn } from '@sdcorejs/angular/services'; import { Observable } from 'rxjs'; import { SdIconSet } from '@sdcorejs/angular/modules/icon'; import { SdButton } from '@sdcorejs/angular/components/button'; import { CdkDrag, CdkDropList, CdkDragDrop } from '@angular/cdk/drag-drop'; import { SdQuickAction } from '@sdcorejs/angular/components/quick-action'; import { SdScrollDirective } from '@sdcorejs/angular/directives'; import { FormGroup } from '@angular/forms'; import { SdSideDrawer } from '@sdcorejs/angular/components/side-drawer'; import { SdModal } from '@sdcorejs/angular/components/modal'; import { MatSelectionList } from '@angular/material/list'; type SdTableColumn = SdTableColumnText | SdTableColumnNumber | SdTableColumnBool | SdTableColumnDate | SdTableColumnValues | SdTableColumnLazyValues | SdTableColumnChildren; type SdTableColumnTransformFunc = (value: any, rowData: T, args?: { isExport?: boolean; }) => string | Promise; interface Badge { type?: SdUnwrapSignal; color?: SdUnwrapSignal; icon?: SdUnwrapSignal; title?: SdUnwrapSignal; } type UseBadgeFunc = (value: any, rowData: T) => Badge; type UseBadgeValuesFunc = (value: any, rowData: T, items: K[]) => Badge; interface ColumnTitleOption { title: string; templateRef?: TemplateRef; } interface ColumnCellOption { templateRef?: TemplateRef; copiable?: boolean; truncate?: { enable?: boolean; type?: 'more' | 'tooltip'; }; } interface SdTableColumnBase { title: string | ColumnTitleOption; cell?: ColumnCellOption; width?: string; minWidth?: string; maxWidth?: string; hidden?: boolean; invisible?: boolean; fixed?: boolean; align?: 'right'; htmlTemplate?: (value: any, rowData: T) => string; transform?: SdTableColumnTransformFunc; tooltip?: (value: any, rowData: T) => string; click?: (value: any, rowData: T) => void; sortable?: boolean; filter?: { disabled?: boolean; default?: any; onChange?: (value: any, column?: SdTableColumn, columnFilter?: Record) => void; operator?: { default?: Operator; enable?: boolean; list?: Operator[]; }; filterDef?: TemplateRef; }; export?: { disabled?: boolean; description?: string; }; } interface SdTableColumnText extends SdTableColumnBase { field: NestedKeyOf; type: 'string'; useBadge?: UseBadgeFunc; } interface SdTableColumnNumber extends SdTableColumnBase { field: NestedKeyOf; type: 'number'; useBadge?: UseBadgeFunc; filter?: SdTableColumnBase['filter'] & { type?: 'split-number'; }; } interface SdTableColumnBool extends SdTableColumnBase { field: NestedKeyOf; type: 'boolean'; useBadge?: UseBadgeFunc; option?: { displayOnTrue?: string; displayOnFalse?: string; }; } interface SdTableColumnDate extends SdTableColumnBase { field: NestedKeyOf; type: 'date' | 'datetime' | 'time'; useBadge?: UseBadgeFunc; filter?: SdTableColumnBase['filter'] & { type?: 'daterange' | 'date' | 'split-date'; }; } interface SdTableColumnValues> extends SdTableColumnBase { field: NestedKeyOf; type: 'values'; useBadge?: UseBadgeValuesFunc; option: { items: K[] | Signal | (() => Promise); valueField: NestedKeyOf; displayField: NestedKeyOf; selection?: 'MULTIPLE'; }; } interface SdTableColumnLazyValues> extends SdTableColumnBase { field: NestedKeyOf; type: 'lazy-values'; useBadge?: UseBadgeFunc; option: { items: SdSearch; valueField: NestedKeyOf; displayField: NestedKeyOf; views?: (values: string[]) => Promise; selection?: 'MULTIPLE'; }; } type SdTableColumnNormal = Exclude, SdTableColumnChildren>; interface SdTableColumnChildren extends SdTableColumnBase { field: string; type: 'children'; children: SdTableColumnNormal[]; } interface Context$4 { item: T; column: SdTableColumn; autoId: string; } declare class SdTableCellDefDirective { static ngTemplateContextGuard(_dir: SdTableCellDefDirective, _ctx: unknown): _ctx is Context$4; sdTableCellDef: _angular_core.InputSignal; templateRef: TemplateRef>; static ɵfac: _angular_core.ɵɵFactoryDeclaration, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration, "[sdTableCellDef]", never, { "sdTableCellDef": { "alias": "sdTableCellDef"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>; } interface SdTableMetaSelector { selectable?: boolean; isSelected?: boolean; actions?: string[]; } interface SdTableMetaExpand { isExpanded?: boolean; isExpanding?: boolean; data?: any; } interface SdTableMetaTree { level: number; hasChildren: boolean; isExpanded: boolean; isExpanding?: boolean; parentId?: string; /** Cached formatted child SdTableItem[] — populated on first expand/format */ childItems?: SdTableItem[]; /** * Hierarchical index path từ root tới row hiện tại — sibling-index (1-based) per level. * Vd root thứ nhất = [1], child thứ 2 của root đó = [1, 2], grandchild thứ 1 = [1, 2, 1]. * Template STT join('.') → "1", "1.2", "1.2.1". Populate trong `flattenTree`. */ indexPath?: number[]; } interface SdTableDisplay { tooltip?: string; badge?: { type: SdUnwrapSignal; color: SdUnwrapSignal; icon?: SdUnwrapSignal; title?: SdUnwrapSignal; }; cellStyle?: Record; data: string | number | undefined | null; isHtml: boolean; click?: () => void; } interface SdTableMetaGroup { /** Children items thuộc group này (chỉ set trên group header row). */ items?: SdTableItem[]; /** true = row này là synthetic group header (sinh bởi SdGroupPipe). */ isGroupHeader?: boolean; /** Hash key của group (= Utilities.hash(values)). */ key?: string; /** Resolved group-field values (vd `{ customerId: 1, customerName: 'A' }`). */ values?: Record; /** Trạng thái expand. Chỉ ý nghĩa khi `option.group.collapsible = true`. */ isExpanded?: boolean; } interface SdTableMeta { id: string; display: Record; selector?: SdTableMetaSelector; expand?: SdTableMetaExpand; group?: SdTableMetaGroup; tree?: SdTableMetaTree; } interface SdTableItem { meta: SdTableMeta; data: T; } interface Context$3 { item: SdTableItem; } declare class SdTableExpandDefDirective { static ngTemplateContextGuard(_dir: SdTableExpandDefDirective, _ctx: unknown): _ctx is Context$3; templateRef: TemplateRef>; static ɵfac: _angular_core.ɵɵFactoryDeclaration, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration, "[sdTableExpandDef]", never, {}, {}, never, never, true, never>; } interface Context$2 { externalFilter?: Record; } declare class SdTableFilterDefDirective { static ngTemplateContextGuard(_dir: SdTableFilterDefDirective, _ctx: unknown): _ctx is Context$2; sdTableFilterDef: _angular_core.InputSignal; defaultShowing: _angular_core.InputSignalWithTransform; templateRef: TemplateRef | null; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵdir: _angular_core.ɵɵDirectiveDeclaration; } interface Context$1 { items: SdTableItem[]; column: SdTableColumn; } declare class SdMaterialFooterDefDirective { static ngTemplateContextGuard(_dir: SdMaterialFooterDefDirective, _ctx: unknown): _ctx is Context$1; sdTableFooterDef: _angular_core.InputSignal; templateRef: TemplateRef>; static ɵfac: _angular_core.ɵɵFactoryDeclaration, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration, "[sdTableFooterDef]", never, { "sdTableFooterDef": { "alias": "sdTableFooterDef"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>; } interface SdTableGroupDefContext { /** Children items thuộc group hiện tại (kèm wrapper SdTableItem). */ items: SdTableItem[]; /** Children raw data (đã unwrap khỏi SdTableItem) — tiện dùng trong template. */ data: T[]; /** Hash key của group (Utilities.hash(values)). */ key: string; /** Resolved giá trị các field group — vd `{ customerId: 1, customerName: 'A' }`. */ values: Record; /** true nếu group đang expand, false nếu collapse (chỉ ý nghĩa khi option.group.collapsible). */ isExpanded: boolean; /** true nếu mọi children selectable đều selected. */ isSelected: boolean; /** true nếu một phần children selected (partial). */ indeterminate: boolean; /** Toggle expand/collapse (chỉ chạy nếu collapsible). */ toggleExpand: () => void; /** Toggle select-all cho cả group (chỉ chạy nếu selector visible). */ toggleSelect: () => void; } /** * Directive cung cấp custom template render group header row trong sd-table. * Thay thế cho htmlTemplate callback cũ — cho phép Angular template binding, * pipes, components con (sd-badge, sd-button, …) bên trong. * * Ví dụ: * ```html * * * Khách hàng: {{ values.customerName }} * {{ items.length }} đơn hàng * * * ``` */ declare class SdTableGroupDefDirective { static ngTemplateContextGuard(_dir: SdTableGroupDefDirective, _ctx: unknown): _ctx is SdTableGroupDefContext; templateRef: TemplateRef>; static ɵfac: _angular_core.ɵɵFactoryDeclaration, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration, "[sdTableGroupDef]", never, {}, {}, never, never, true, never>; } interface Context { column: SdTableColumn; } declare class SdTableTitleDefDirective { static ngTemplateContextGuard(_dir: SdTableTitleDefDirective, _ctx: unknown): _ctx is Context; sdTableTitleDef: _angular_core.InputSignal; templateRef: TemplateRef>; static ɵfac: _angular_core.ɵɵFactoryDeclaration, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration, "[sdTableTitleDef]", never, { "sdTableTitleDef": { "alias": "sdTableTitleDef"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>; } interface SdTableQuickFilter { code: string; columnFilter: Record; externalFilter: Record; } interface TableFilterConfiguration { inlineExternal: Record; } interface TableFilterValue { columnOperator?: Record; columnFilter?: Record; externalFilter?: Record; notReload?: boolean; filtered?: boolean; } interface SdTableFilterRequest { columnOperator: Record, Operator>; rawColumnFilter: Record, any>; rawExternalFilter: Record; pageNumber: number; pageSize: number; orderBy?: string; orderDirection?: Order['direction']; isExported?: boolean; visibledColumns?: SdTableColumn[]; } interface SdTableOptionFilter { /** Key định danh nếu muốn lưu cache */ key?: string; /** Có bật cache giá trị filter hay không */ cacheable?: boolean; /** Vô hiệu toàn bộ filter */ disabled?: boolean; /** Ẩn tất cả inline filter (chỉ còn external), nếu chọn auto thì sẽ ẩn khi total row <= 10, phù hợp cho các màn detail có ít dữ liệu */ hideInlineFilter?: boolean | 'auto'; /** Hàm xử lý khi đổi Operator của filter column */ operatorChange?: (column?: SdTableColumn, operator?: Operator) => void; /** Danh sách external filter */ externalFilters?: SdTableExternalFilter[]; /** Số lượng cột external filter trên mỗi hàng (mặc định: 6) */ externalFilterPerRow?: 4 | 6; /** Chế độ thủ công: phải nhấn nút áp dụng mới gửi filter */ manualFilter?: boolean; /** Có thể thu gọn bộ lọc hay không */ collapsible?: boolean; /** Ẩn toolbar (xóa bộ lọc, thiết lập) của external filter, khi có ít external filter user không cần chức năng này */ hideExternalFilterToolbar?: boolean; /** Hàm xử lý khi trigger clear filter từ external filter */ onClearFilter?: () => void; } declare const SdConvertToPagingReq: (filterRequest: SdTableFilterRequest, args: { columns?: SdTableColumn[]; externalFilters?: SdTableExternalFilter[]; fieldMapping?: Record; orders?: Order[]; }) => PagingReq; declare type SdTableExternalFilter = TextFilter | DateFilter | DateTimeFilter | DateRangeFilter | NumberFilter | ValuesFilter | LazyValuesFilter | BooleanFilter | CustomFilter; interface BaseFilter { field: string; title: string; defaultShowing?: boolean; required?: boolean; hidden?: boolean; defaultOperator?: Operator; data?: TData; onChange?: (value: any) => void; } interface TextFilter extends BaseFilter { type: 'string'; default?: string; } interface DateFilter extends BaseFilter { type: 'date'; default?: Date | string; minDate?: string | number | Date; maxDate?: string | number | Date; } interface DateTimeFilter extends BaseFilter { type: 'datetime'; default?: Date | string; minDate?: string | number | Date; maxDate?: string | number | Date; } interface DateRangeFilter extends BaseFilter { type: 'daterange'; enableTime?: boolean; minDate?: string | number | Date; maxDate?: string | number | Date; default?: { from?: Date | string; to?: Date | string; }; } interface NumberFilter extends BaseFilter { type: 'number'; default?: number; } interface ValuesFilter extends BaseFilter { type: 'values'; option: { valueField: string; displayField: string; items: any[] | (() => Promise); selection?: 'AUTOCOMPLETE' | 'MULTIPLE' | 'MULTIPLEAUTOCOMPLETE'; }; default?: string | string[]; } interface LazyValuesFilter extends BaseFilter { type: 'lazy-values'; option: { valueField: string; displayField: string; items: SdSearch; selection?: 'MULTIPLE'; }; default?: string | string[]; } interface BooleanFilter extends BaseFilter { type: 'boolean'; option?: { displayOnTrue?: string; displayOnFalse?: string; }; default?: boolean; } interface CustomFilter extends BaseFilter { type: 'custom'; filterDef?: TemplateRef; default?: any; } interface TableFilterRegister { configuration: { get: () => TableFilterConfiguration; set: (configuration: Partial) => TableFilterConfiguration; remove: () => void; observer: Observable; }; value: { get: () => TableFilterValue; set: (value: Partial) => TableFilterValue; remove: () => void; observer: Observable; }; } interface SdTableCommandOption { align?: 'left' | 'right'; commands?: SdTableCommand[]; } type SdTableCommand = SdTableCommandNormal | SdTableCommandChildren; interface SdTableCommandNormal { color?: Color; icon?: string | ((rowData: T) => string); fontSet?: SdIconSet; title?: string | ((rowData: T) => string); disabled?: boolean | ((rowData: T) => boolean); hidden?: boolean | ((rowData: T) => boolean | Promise); click: (rowData: T) => void; htmlTemplate?: (rowData: T) => string; } interface SdTableCommandChildren { icon?: string | ((rowData: T) => string); fontSet?: SdIconSet; title?: string | ((rowData: T) => string); disabled?: boolean | ((rowData: T) => boolean); hidden?: boolean | ((rowData: T) => boolean | Promise); children: SdTableCommandNormal[]; } interface TableOptionConfig { visible?: boolean; resizable?: boolean; onResize?: (field: string, width: string, columnWidth: Record) => void; } interface OriginColumn { field: string; title: string; width?: string; invisible?: boolean; fixed?: boolean; truncate?: boolean; } interface ConfiguredColumn { origin: OriginColumn; title?: string; width?: string; invisible?: boolean; fixed?: boolean; truncate?: boolean; } interface ConfiguredTable { columns?: ConfiguredColumn[]; } interface ConfiguredTableResult { column: Record; fixedColumn: Record; firstColumns: SdTableColumn[]; secondColumns: SdTableColumn[]; firstHeaders: string[]; secondHeaders: string[]; displayedColumns: string[]; displayedFooters: string[]; multipleHeader: boolean; } interface SdTableOptionExpand { disabled?: (rowData: T) => boolean; onExpand?: (rowData: T) => Promise | any; multiple?: boolean; always?: boolean; } type SdTableOptionExport = SdTableOptionExportDefault | SdTableOptionExportCustom; interface SdTableOptionExportDefault { type?: 'default'; key?: string; visible?: 'ALL' | 'EXCEL' | 'CSV'; enableUpload?: boolean; fileName?: string; max?: number; maxItemsPerRequest?: number; batch?: number; items?: (filterRequest: SdTableFilterRequest) => T[] | Promise | Promise<{ items: any[]; total: number; }>; mapping?: (items: T[], fileName?: string) => T[] | Promise; columns?: SdTableOptionExportColumn[]; sheets?: SdTableOptionExportSheet[]; } interface SdTableOptionExportCustom { type: 'custom'; onExport: (filterRequest: SdTableFilterRequest) => Promise; } interface SdTableOptionExportColumn { field: NestedKeyOf; title: string; description?: string; width?: string; transform?: (value: any, rowData: T) => string; export?: { disabled: boolean; }; } interface SdTableOptionExportSheet { name: string; items: T[] | (() => T[] | Promise); headers: { value: NestedKeyOf; display: string; }[]; } interface SdTableOptionGroup { /** * Danh sách field dùng để nhóm. Item có cùng giá trị tất cả `fields` sẽ vào cùng group. * Ví dụ `['customerId']` group orders theo khách hàng. * Type-safe qua `NestedKeyOf` — hỗ trợ dot-notation cho nested object (vd `'customer.id'`). */ fields: NestedKeyOf[]; /** * Mặc định group có collapse được không. true = render với UI có thể expand/collapse; * false = luôn show toàn bộ children (mặc định). */ collapsible?: boolean; /** Nếu collapsible, mặc định trạng thái ban đầu: true = collapsed, false = expanded (default). */ defaultCollapsed?: boolean; /** * Predicate tùy chọn cho selection. Khi check `selectable` của group header. * Mặc định: chỉ nhóm có >=1 child `selectable` mới hiển thị checkbox. */ selectable?: (children: T[]) => boolean; } interface SdTableOptionPaginate { pageSize?: number; pages?: number[]; showFirstLastButtons?: boolean; hidePageSize?: boolean; hidden?: boolean; } interface SdTableOptionReload { visible?: boolean; onReload?: (items: SdTableItem[], args?: { fromSource?: 'PAGING' | 'RELOAD'; }) => Promise | void; } interface SdTableOptionSelector { /** Bật/tắt cột checkbox selector. Mặc định: true. */ visible?: boolean; /** Chỉ cho chọn 1 dòng tại một thời điểm. */ single?: boolean; /** Danh sách action hiển thị khi có item được chọn. */ actions?: SdTableAction[]; /** Nội dung hiển thị bên cạnh selector/action bar. */ message?: string | ((selectedItems?: T[]) => string); /** Callback khi user chọn/bỏ chọn một dòng. */ onSelect?: (rowData?: T, selectedItems?: T[]) => void; /** Callback khi user chọn tất cả hoặc bỏ chọn tất cả. */ onSelectAll?: (selectedItems: T[]) => void; /** Disable checkbox của từng dòng theo điều kiện. */ disabled?: (rowData?: T, selectedItems?: T[]) => boolean; /** * Predicate để tự động pre-select item sau mỗi lần load. * Table sẽ gọi hàm này cho từng item và set isSelected = true nếu trả về true. * Dùng khi cần programmatically set selected items từ bên ngoài. */ defaultSelected?: (rowData: T) => boolean; /** * Giữ lại selection khi user chuyển trang / filter / sort / reload. * Selection chỉ bị clear khi user bấm nút X (onClearSelection) hoặc bỏ chọn từng item. * * - Default (false): selection bị mất khi data items được re-fetch (server-side) hoặc * item references thay đổi. Action bar chỉ hiển thị số item selected ở page hiện tại. * - Enabled (true): table giữ map nội bộ selectedItems theo `meta.id` (hash của data). * Sau mỗi #render, restore `isSelected` cho item nào id đã có trong map. * `selectedTableItems()` trả về TOÀN BỘ item đã chọn xuyên trang (kể cả off-page), * nên action bar + callback `click(items)` nhận đầy đủ data đang được chọn. * * Lưu ý: matching dựa trên `Utilities.hash(data)` — hai item có cùng data shape sẽ * trùng id (mong muốn). Nếu data có timestamp/random field thay đổi giữa các lần fetch, * id sẽ khác và selection không restore được. */ preserveSelection?: boolean; } type SdTableAction = SdTableActionNormal | SdTableActionChildren; interface SdTableActionNormal { /** * Tên icon hiển thị trên action button. * Giá trị này được render qua `sd-icon`, vì vậy nên dùng đúng tên glyph của configured icon set. * Tra cứu icon tại: https://fonts.google.com/icons */ icon?: string; /** * Font set của icon. * Kiểu thực tế lấy từ `SdButton['fontSet']` và chỉ hỗ trợ các giá trị thuộc `SdIconSet`, * ví dụ: `material-icons`, `material-icons-outlined`, `material-icons-round`, * `material-icons-sharp`. * Nếu không truyền, `SdButton` sẽ dùng font set mặc định của nó. */ fontSet?: SdUnwrapSignal; /** Tooltip hiển thị khi hover action button. */ tooltip?: SdUnwrapSignal; /** Text label hiển thị trên button action. */ title?: SdUnwrapSignal; /** * Màu của `SdButton`. * Kiểu thực tế map theo `SdButton['color']`, hiện dùng các token như * `primary`, `secondary`, `info`, `success`, `warning`, `error`. */ color?: SdUnwrapSignal; /** * Variant hiển thị của `SdButton`. * Kiểu thực tế là `fill | light | outline | link`. */ type?: SdUnwrapSignal; /** Ẩn action theo cờ tĩnh hoặc theo điều kiện của dữ liệu dòng. */ hidden?: boolean | ((rowData?: T) => boolean); /** Gom action vào nhóm hiển thị compact nếu table hỗ trợ grouped actions. */ isGrouped?: boolean; /** Hàm xử lý khi click action, nhận toàn bộ item đang được chọn. */ click: (selectedItems?: T[]) => void; } interface SdTableActionChildren { /** * Tên icon của action cha (group action). * Giá trị nên là tên glyph configured icon set: https://fonts.google.com/icons */ icon?: string; /** * Font set của icon cho action cha. * Dùng cùng kiểu dữ liệu với `SdButton['fontSet']`. */ fontSet?: SdUnwrapSignal; /** Tooltip của action cha. */ tooltip?: SdUnwrapSignal; /** Label của action cha. */ title?: SdUnwrapSignal; /** Màu của action cha theo kiểu `SdButton['color']`. */ color?: SdUnwrapSignal; /** Variant hiển thị của action cha theo kiểu `SdButton['type']`. */ type?: SdUnwrapSignal; /** Ẩn action cha theo cờ tĩnh hoặc theo điều kiện của dữ liệu dòng. */ hidden?: boolean | ((rowData?: T) => boolean); /** Gom action cha vào nhóm hiển thị compact nếu table hỗ trợ grouped actions. */ isGrouped?: boolean; /** Danh sách action con hiển thị bên trong group action này. */ children: SdTableActionNormal[]; } interface SdTableOptionSort { enable?: boolean; } interface SdTableRowCssContext { level: number; hasChildren: boolean; isExpanded: boolean; } interface SdTableOptionStyle { shadow?: boolean; maxHeight?: string; minHeight?: string; rowCss?: (rowData: T, index?: number, ctx?: SdTableRowCssContext) => Record; } /** * Cấu hình tree-rows cho `` — render dòng con inline dưới dòng cha * kèm expand/collapse. * * Đây là discriminated union theo `loadType`: * - `'static'` ({@link TableOptionTreeStatic}) — children đã nằm sẵn trong data * (embedded array tại `childrenKey`). Không gọi API khi bung. * - `'lazy'` ({@link TableOptionTreeLazy}) — children nạp theo yêu cầu qua * `onExpandChildren` khi người dùng bung dòng lần đầu. * * `loadType` là discriminant: TypeScript dựa vào nó để narrow union (vd chỉ * `static` mới có `childrenKey`/`defaultExpanded`, chỉ `lazy` mới có * `onExpandChildren`). Dùng type-guard `isLazyTree()` trong `tree.util.ts` để * narrow an toàn thay vì truy cập field trực tiếp trên union. * * @example Static (children embedded) * ```ts * tree: { loadType: 'static', childrenKey: 'children', defaultExpanded: 1 } * ``` * @example Lazy (nạp theo yêu cầu) * ```ts * tree: { loadType: 'lazy', onExpandChildren: row => api.getChildren(row.id) } * ``` */ type SdTableOptionTree = TableOptionTreeStatic | TableOptionTreeLazy; /** * Tree với dữ liệu children **embedded** sẵn trong mỗi row. * * Table đọc mảng con tại `childrenKey` ngay trên object data; không có lời gọi * bất đồng bộ nào khi bung dòng. Đây cũng là chế độ hỗ trợ **search ở cấp con** * cho table `type: 'local'` (xem `tree.util.ts#subtreeMatches` / * `filterMatchingChildren`): khi lọc, một nhánh được giữ lại nếu chính nó hoặc * bất kỳ hậu duệ nào khớp từ khoá, và nhánh khớp sẽ tự bung. */ interface TableOptionTreeStatic { /** Chế độ nạp children: dữ liệu đã embedded sẵn trong row. */ loadType: 'static'; /** * Key trên object row chứa mảng children. * Mặc định: `'children'`. */ childrenKey?: NestedKeyOf | 'children'; /** * Giới hạn độ sâu cây (level gốc = 0). * `undefined` = không giới hạn. */ maxDepth?: number; /** * Trạng thái bung mặc định khi load: * - `false` — thu gọn hết (mặc định) * - `true` — bung toàn bộ * - `number N` — bung tới cấp `N` (row có `level < N` được bung) */ defaultExpanded?: boolean | number; /** * Số pixel thụt lề (indent) cho mỗi cấp sâu. * Mặc định: `20`. */ indentSize?: number; } /** * Tree với children **nạp lười (lazy)** theo yêu cầu. * * Khi người dùng bung một dòng lần đầu mà dòng đó chưa có children embedded, * table gọi `onExpandChildren(row)` để nạp; kết quả được cache lại trên row * (ghi vào key `'children'`) nên các lần bung sau không gọi lại. * * Lazy tree **không hỗ trợ** `childrenKey` tuỳ biến (luôn dùng `'children'` làm * nơi lưu kết quả) và **không hỗ trợ** `defaultExpanded` (không thể bung mặc * định nhánh chưa nạp). */ interface TableOptionTreeLazy { /** Chế độ nạp children: nạp lười theo yêu cầu khi bung. */ loadType: 'lazy'; /** * Giới hạn độ sâu cây (level gốc = 0). * `undefined` = không giới hạn. */ maxDepth?: number; /** * Hàm nạp children khi bung dòng chưa có children embedded. * Trả về `Promise`; kết quả được ghi vào key `'children'` của row data * và cache cho các lần bung sau. */ onExpandChildren: (rowData: T) => Promise; /** * Xác định một row có con hay không — để quyết định có hiển thị icon expand. * - Không truyền: MỌI lazy node đều hiện icon expand (không biết trước được). * - Truyền: chỉ hiện icon khi trả `true`; trả `false` ⇒ row là lá (ẩn icon, * không gọi `onExpandChildren`). * Lưu ý: nếu row đã có children embedded sẵn thì luôn coi là có con (bỏ qua hàm này). */ hasChildren?: (rowData: T) => boolean; /** * Số pixel thụt lề (indent) cho mỗi cấp sâu. * Mặc định: `20`. */ indentSize?: number; } type SdTableOption = SdTableLocalOption | SdTableServerOption; interface SdTableBaseOption { /** * Key định danh của table option. * Thường dùng khi một số tính năng con cần lưu trạng thái riêng theo table, * ví dụ các option có hỗ trợ cache/config theo key. */ key?: string; /** * Cấu hình hiển thị/phục hồi config của table. * Xem thêm kiểu chi tiết tại `TableOptionConfig`. */ config?: TableOptionConfig; /** * Cấu hình chọn dòng (checkbox selector, action theo selection, pre-select...). * Xem thêm tại `SdTableOptionSelector`. */ selector?: SdTableOptionSelector; /** * Cấu hình expand row. * Xem thêm tại `SdTableOptionExpand` để biết cách disable, expand nhiều dòng, * hoặc luôn hiển thị vùng expand. */ expand?: SdTableOptionExpand; /** * Bật/tắt sorting ở cấp table. * Xem thêm tại `SdTableOptionSort`. */ sort?: SdTableOptionSort; /** * Cấu hình phân trang. * Xem thêm tại `SdTableOptionPaginate`. */ paginate?: SdTableOptionPaginate; /** * Cấu hình hành vi reload dữ liệu. * Xem thêm tại `SdTableOptionReload`. */ reload?: SdTableOptionReload; /** * Cấu hình export dữ liệu. * Xem thêm tại `SdTableOptionExport` để biết mode `default`/`custom`, mapping, * sheet export và giới hạn số dòng. */ export?: SdTableOptionExport; /** * Cấu hình group dữ liệu theo nhiều field. * Xem thêm tại `SdTableOptionGroup`. */ group?: SdTableOptionGroup; /** * Cấu hình tree rows — render children inline dưới parent. * Xem thêm tại `SdTableOptionTree`. */ tree?: SdTableOptionTree; /** * Cấu hình filter của table. * Xem thêm tại `SdTableOptionFilter` để biết filter inline, external filter, * manual filter, cacheable và callback clear filter. */ filter?: SdTableOptionFilter; /** * Cấu hình cột đánh số thứ tự (STT) ở đầu mỗi dòng. * Khi enabled = true, table render thêm 1 cột STT (sticky đầu, sau selector). * Số thứ tự tính theo global index (pageIndex * pageSize + i + 1) để tương thích phân trang. */ index?: { /** Bật/tắt cột STT. */ enabled?: boolean; /** Tiêu đề header (mặc định '#'). */ title?: string; /** Width của cột (mặc định '50px'). */ width?: string; }; /** * Bật cột filler ở cuối row để hấp thụ leftover width khi tổng width các cột data < container. * Khi enabled = true, các cột utility (selection / STT / command) không bị browser stretch trên màn rộng. * Mặc định off — table tự stretch cột data theo `width: 100%` như cũ. */ filler?: { /** Bật/tắt cột filler. */ enabled?: boolean; }; /** * Cấu hình kéo-thả đổi thứ tự dòng. * Chỉ bật khi table cho phép reorder dữ liệu phía client. */ rowReorder?: { /** Bật/tắt tính năng reorder row. */ enabled?: boolean; /** * Callback sau khi reorder xong. * `newRows` là mảng sau khi đã đổi vị trí ở UI, * `movedItem` là item vừa được kéo, * `fromIndex` và `toIndex` là vị trí cũ/mới. */ onChange?: (newRows: T[], movedItem: T, fromIndex: number, toIndex: number) => void; /** * Tên icon hiển thị cho drag handle. * Nếu không truyền thì table dùng icon reorder mặc định của Material. * Nếu truyền custom icon, nên dùng tên glyph của Material Icons: * https://fonts.google.com/icons */ icon?: string; /** * Disable khả năng reorder theo từng dòng. * Trả về `true` để chặn kéo-thả dòng tương ứng. */ disabled?: (row: T, index: number) => boolean; }; /** * Danh sách command thao tác theo từng dòng. * Xem thêm tại `SdTableCommand`. */ commands?: SdTableCommand[]; /** * Cấu hình vùng command tập trung. * Dùng khi cần set thêm alignment hoặc bọc danh sách command trong object option. * Xem thêm tại `SdTableCommandOption`. */ command?: SdTableCommandOption; /** * Danh sách cột hiển thị của table. * Đây là phần bắt buộc. Xem thêm tại `SdTableColumn` để cấu hình field, filter, * sort, transform, template và export của từng cột. */ columns: SdTableColumn[]; /** * Cấu hình style/layout chung của table. * Xem thêm tại `SdTableOptionStyle`. */ style?: SdTableOptionStyle; } interface SdTableLocalOption extends SdTableBaseOption { /** Dùng dữ liệu local tại client. */ type: 'local'; /** * Hàm trả dữ liệu local cho table. * Table sẽ tự gọi hàm này để lấy mảng item nguồn. * Có thể trả về đồng bộ hoặc `Promise`. */ items: () => T[] | Promise; } interface SdTableServerOption extends SdTableBaseOption { /** Dùng dữ liệu từ server, có paging/filter/sort theo request của table. */ type: 'server'; /** * Hàm lấy dữ liệu từ server. * `filterRequest` chứa trạng thái filter/sort/visible columns ở dạng của table. * `pagingReq` là dữ liệu paging đã được chuẩn hóa để dễ map sang API backend. * Hàm cần trả về `{ items, total }` để table render và tính phân trang. */ items: (filterRequest: SdTableFilterRequest, pagingReq: PagingReq) => Promise<{ items: T[]; total: number; }>; /** * Hook chạy khi table phát sinh filter request. * Dùng để can thiệp thêm trước/sau khi gọi API, ví dụ sync external state, * theo dõi trạng thái valid của external filter, hoặc ghi log request filter hiện tại. */ onFilter?: (filterRequest: SdTableFilterRequest, args: { /** Cho biết toàn bộ external filter hiện tại có hợp lệ để trigger filter hay không. */ externalFilterValid: boolean; }) => void; } declare class ExternalFilterComponent { #private; autoIdInput: _angular_core.InputSignal; filter: _angular_core.InputSignal | undefined>; externalFilters: _angular_core.InputSignal; filterRegister: _angular_core.InputSignal; externalFilter: _angular_core.WritableSignal>; inlineExternal: _angular_core.WritableSignal>; filtered: _angular_core.WritableSignal; isMobileOrTablet: boolean; readonly form: FormGroup<{}>; autoId: _angular_core.Signal; col: _angular_core.Signal<2 | 3>; filterItems: _angular_core.Signal; constructor(); onKeyupEnter: () => void; onFilter: (externalFilter: SdTableExternalFilter) => void; onSubmit: () => void; updateFilter: () => void; clearFilter: (event?: Event) => void; onCheckboxChange: (event: Event, externalFilter: SdTableExternalFilter) => void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** * Mobile-filter — side-drawer chiếm ~90% width thiết bị. Dùng cho table khi * render trên màn hình điện thoại (inline filter bị ẩn). * * Layout (mỗi filter 1 hàng, size='sm'): * 1. External filter (render inline — KHÔNG reuse external-filter component * vì component đó dùng grid 2/3 cột + có chọn ẩn/hiện không phù hợp mobile). * 2. filterDefs — custom templates do app khai báo qua `[sdTableFilterDef]`. * 3. Column filter — 1 column-filter / column. * * State: local working copy. Cancel = đóng (không ghi). Apply = flush vào * filterRegister + đóng. */ declare class MobileFilterComponent { autoIdInput: _angular_core.InputSignal; filter: _angular_core.InputSignal | undefined>; externalFilters: _angular_core.InputSignal; columns: _angular_core.InputSignal; filterDefs: _angular_core.InputSignal; filterRegister: _angular_core.InputSignal; cacheValues: _angular_core.InputSignal | undefined>; drawer: _angular_core.Signal; workingColumnFilter: _angular_core.WritableSignal>; workingColumnOperator: _angular_core.WritableSignal>; workingExternalFilter: _angular_core.WritableSignal>; autoId: _angular_core.Signal; externalFilterItems: _angular_core.Signal; columnItems: _angular_core.Signal; filterDefItems: _angular_core.Signal; open: () => void; close: () => void; onApply: () => void; onCancel: () => void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } declare class ConfigComponent { #private; autoIdInput: _angular_core.InputSignal; tableOption: _angular_core.InputSignal; changes: _angular_core.OutputEmitterRef; autoId: _angular_core.Signal; modal: _angular_core.Signal; table: _angular_core.Signal>; configuration: _angular_core.WritableSignal; dragDisabled: boolean; constructor(); open: () => Promise; close: () => void; onSave: () => void; onReset: () => Promise; dropTable(event: any): void; handleMouseDown(): void; handleMouseUp(): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } declare class SdPopupExport { #private; private ref; private loadingService; private excelService; private notifyService; private confirmService; tableOption: SdTableOption; columns: SdExcelColumn[]; templateName?: string | null; enableCreating: boolean; modal?: SdModal; listGroups?: MatSelectionList; selected: Record; files: any[]; readonly export: _angular_core.OutputEmitterRef<{ file: any | null; columns: SdExcelColumn[]; isCSV?: boolean; }>; form: FormGroup<{}>; exportOption?: SdTableOptionExportDefault; set _tableOption(option: SdTableOption); readonly configuration: _angular_core.InputSignal; private get key(); private get tableColumns(); private get additionalColumns(); /** Inserted by Angular inject() migration for backwards compatibility */ constructor(...args: unknown[]); open: () => Promise; exportDefault: () => void; exportCSV: () => void; loadFiles: (group?: string | undefined) => Promise; generateTemplate: () => Promise; generateAndUploadTemplate: (fileName?: string) => Promise; onExport: (file: any) => void; uploadTemplate: () => Promise; removeFile: (file: any) => void; createTemplate: () => Promise; templateNameValidator: (val: string) => string; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } interface ISdTableConfiguration { paginate?: { /** Số dòng mỗi trang. */ pageSize?: SdTableOptionPaginate['pageSize']; /** Danh sách các tuỳ chọn số dòng/trang mà người dùng có thể chọn. */ pages?: SdTableOptionPaginate['pages']; showFirstLastButtons?: SdTableOptionPaginate['showFirstLastButtons']; }; filter?: { /** Ẩn bộ lọc dưới cột, nếu không cấu hình thì giá trị mặc định là false */ hideInlineFilter?: SdTableOptionFilter['hideInlineFilter']; /** Số lượng external filter mỗi dòng, mặc định là 6 */ externalFilterPerRow?: SdTableOptionFilter['externalFilterPerRow']; /** Kích hoạt chế độ lọc thủ công, hiển thị nút áp dụng, mặc định là false */ manualFilter?: SdTableOptionFilter['manualFilter']; /** Ẩn toolbar (xóa bộ lọc, thiết lập) của external filter, khi có ít external filter user không cần chức năng này, mặc định là false */ hideExternalFilterToolbar?: SdTableOptionFilter['hideExternalFilterToolbar']; operator?: { list?: Partial>; default?: Partial>; }; }; images?: { filterRequired?: string; dataEmpty?: string; filterEmpty?: string; }; } declare const SD_TABLE_CONFIGURATION: InjectionToken; declare class MatPaginatorIntlCro extends MatPaginatorIntl { #private; firstPageLabel: string; lastPageLabel: string; itemsPerPageLabel: string; nextPageLabel: string; previousPageLabel: string; getRangeLabel: (page: number, pageSize: number, length: number) => string; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵprov: _angular_core.ɵɵInjectableDeclaration; } declare class SdTable implements AfterViewInit, OnDestroy { #private; autoIdInput: _angular_core.InputSignal; autoId: _angular_core.Signal; option: _angular_core.InputSignal>; table: _angular_core.Signal | undefined>; configComponent: _angular_core.Signal; sdPopupExport: _angular_core.Signal; scroll: _angular_core.Signal; quickAction: _angular_core.Signal; externalFilter: _angular_core.Signal; mobileFilter: _angular_core.Signal; paginator: _angular_core.Signal; sort: _angular_core.Signal; sdSubInformation: _angular_core.Signal | undefined>; sdGroupDef: _angular_core.Signal | undefined>; sdCellDefs: _angular_core.Signal[]>; sdFooterDefs: _angular_core.Signal[]>; sdFilterDefs: _angular_core.Signal; sdTitleDefs: _angular_core.Signal[]>; cellDef: _angular_core.Signal>>; footerDef: _angular_core.Signal>>; hasFooter: _angular_core.Signal; titleDef: _angular_core.Signal>>; tableOption: _angular_core.WritableSignal | undefined>; configuration: _angular_core.WritableSignal; items: _angular_core.WritableSignal[]>; selectedTableItems: _angular_core.WritableSignal[]>; total: _angular_core.WritableSignal; loading: _angular_core.WritableSignal; isSelectAll: _angular_core.WritableSignal; isFiltered: _angular_core.WritableSignal; requireFiltered: _angular_core.WritableSignal; filterRegister: TableFilterRegister; key: string; columnOperator: Record; columnFilter?: NonNullable; cacheValues: Record; treeRevision: _angular_core.WritableSignal; exporting: _angular_core.WritableSignal; exportTitle: _angular_core.WritableSignal; tableConfiguration: ISdTableConfiguration | null; constructor(); ngAfterViewInit(): void; ngOnDestroy(): void; getFilterRequest: () => SdTableFilterRequest; reload: (force?: boolean, scrollTop?: boolean) => Promise; exportExcel: (columns?: SdExcelColumn[]) => void; exportCSV: (columns?: SdExcelColumn[]) => void; exportCustom: () => void; onTreeToggle: (row: SdTableItem) => Promise; onFilterChange: () => void; onFilterCommit: () => void; onExpand: (rowData: SdTableItem) => Promise; onSelect: (rowData: SdTableItem) => void; onSelectAll: () => void; onClearSelection: (items?: SdTableItem[]) => void; /** true nếu MỌI selectable child trong group đều selected. Group rỗng (không child) → false. */ isGroupAllSelected: (header: SdTableItem) => boolean; /** true nếu CÓ MỘT VÀI selectable child selected nhưng KHÔNG phải tất cả. */ isGroupIndeterminate: (header: SdTableItem) => boolean; /** Toggle select-all cho 1 group: set isSelected cho mọi selectable child, sync state. */ onSelectGroup: (header: SdTableItem, checked: boolean) => void; groupExpandState: Map; /** Toggle expand/collapse cho 1 group (chỉ ý nghĩa khi option.group.collapsible). */ toggleGroupExpand: (header: SdTableItem) => void; /** * Predicate cho matRowDef.when — phân biệt group header row vs data row. * Group header row dùng matRowDef riêng với column list ['sdGroupHeader'] (1 cell duy nhất). * Data row dùng displayedColumns nguyên vẹn. */ isGroupHeaderRow: (_idx: number, row: SdTableItem) => boolean; isDataRow: (_idx: number, row: SdTableItem) => boolean; /** * Colspan cho cell sdGroupHeader trên group row = displayedColumns.length. * Span TOÀN BỘ width data row vì group row chỉ có 1 cell. */ sdGroupColspan: _angular_core.Signal; /** Build context object truyền vào SdTableGroupDefDirective template. */ groupContext: (header: SdTableItem) => SdTableGroupDefContext; rowStyle: (row: SdTableItem, index?: number) => Record | null; clearFilter: () => void; setFilter: (args?: Pick) => void; get dataItems(): T[]; get selectedItems(): T[]; detectChanges: () => void; onColumnResize: (field: string, width: string) => void; onOperatorChange: (column: SdTableColumn, operator: Operator | undefined) => void; trackBy: (index: number, item: SdTableItem) => string; pageOffset: _angular_core.Signal; isReorderDisabled(item: SdTableItem): boolean; reorderSortPredicate: (index: number, drag: CdkDrag>, drop: CdkDropList[]>) => boolean; onReorderDrop(event: CdkDragDrop[]>): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration, never>; static ɵcmp: _angular_core.ɵɵComponentDeclaration, "sd-table", never, { "autoIdInput": { "alias": "autoId"; "required": false; "isSignal": true; }; "option": { "alias": "option"; "required": true; "isSignal": true; }; }, {}, ["sdSubInformation", "sdGroupDef", "sdCellDefs", "sdFooterDefs", "sdFilterDefs", "sdTitleDefs"], ["[sdTableTop]"], true, never>; } export { MatPaginatorIntlCro, SD_TABLE_CONFIGURATION, SdConvertToPagingReq, SdMaterialFooterDefDirective, SdTable, SdTableCellDefDirective, SdTableExpandDefDirective, SdTableFilterDefDirective, SdTableGroupDefDirective, SdTableTitleDefDirective }; export type { ISdTableConfiguration, SdTableColumn, SdTableColumnChildren, SdTableColumnLazyValues, SdTableColumnNormal, SdTableColumnTransformFunc, SdTableColumnValues, SdTableCommand, SdTableCommandChildren, SdTableCommandNormal, SdTableCommandOption, SdTableDisplay, SdTableExternalFilter, SdTableFilterRequest, SdTableGroupDefContext, SdTableItem, SdTableMeta, SdTableOption, SdTableOptionFilter, SdTableOptionTree, SdTableQuickFilter, TableFilterConfiguration, TableFilterRegister, TableFilterValue, TableOptionTreeLazy, TableOptionTreeStatic };