@brickclay-org/ui 0.1.76 → 0.1.78

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/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { OnInit, OnDestroy, OnChanges, EventEmitter, ElementRef, Renderer2, SimpleChanges, AfterViewInit, QueryList, ComponentRef, Type, InjectionToken, ApplicationRef, EnvironmentInjector, ChangeDetectorRef } from '@angular/core';
2
+ import { OnInit, OnDestroy, OnChanges, EventEmitter, ElementRef, Renderer2, SimpleChanges, AfterViewInit, QueryList, ComponentRef, Type, InjectionToken, ApplicationRef, EnvironmentInjector, ChangeDetectorRef, TemplateRef, WritableSignal } from '@angular/core';
3
3
  import { ControlValueAccessor, Validator, AbstractControl, ValidationErrors, NgControl, NgModel } from '@angular/forms';
4
4
  import * as rxjs from 'rxjs';
5
5
  import { Observable } from 'rxjs';
6
6
  import * as i1 from '@angular/common';
7
+ import * as i1$3 from '@angular/cdk/drag-drop';
7
8
  import { CdkDragDrop, CdkDragMove, CdkDragStart } from '@angular/cdk/drag-drop';
8
9
  import { NgxMaskDirective } from 'ngx-mask';
9
10
  import * as i1$2 from '@angular/cdk/dialog';
@@ -915,6 +916,9 @@ declare class BkGrid<T = any> {
915
916
  actionClass: string;
916
917
  showNoRecords: boolean;
917
918
  noRecordFoundHeight: string;
919
+ /** Custom illustration for the empty state. Falls back to a built-in SVG when omitted. */
920
+ noRecordImgUrl?: string;
921
+ noRecordMessage: string;
918
922
  change: EventEmitter<{
919
923
  row: T;
920
924
  column: TableColumn<T>;
@@ -948,8 +952,9 @@ declare class BkGrid<T = any> {
948
952
  dropList(event: CdkDragDrop<T[]>): void;
949
953
  onDragMoved(event: CdkDragMove<any>): void;
950
954
  onDragStart(event: CdkDragStart<any>): void;
955
+ get noRecordImagePath(): string;
951
956
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkGrid<any>, never>;
952
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkGrid<any>, "bk-grid", never, { "draggable": { "alias": "draggable"; "required": false; }; "columns": { "alias": "columns"; "required": false; }; "result": { "alias": "result"; "required": false; }; "actions": { "alias": "actions"; "required": false; }; "customClass": { "alias": "customClass"; "required": false; }; "actionIconClass": { "alias": "actionIconClass"; "required": false; }; "actionClass": { "alias": "actionClass"; "required": false; }; "showNoRecords": { "alias": "showNoRecords"; "required": false; }; "noRecordFoundHeight": { "alias": "noRecordFoundHeight"; "required": false; }; }, { "change": "change"; "actionClick": "actionClick"; "sortChange": "sortChange"; "dragDropChange": "dragDropChange"; }, never, never, true, never>;
957
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkGrid<any>, "bk-grid", never, { "draggable": { "alias": "draggable"; "required": false; }; "columns": { "alias": "columns"; "required": false; }; "result": { "alias": "result"; "required": false; }; "actions": { "alias": "actions"; "required": false; }; "customClass": { "alias": "customClass"; "required": false; }; "actionIconClass": { "alias": "actionIconClass"; "required": false; }; "actionClass": { "alias": "actionClass"; "required": false; }; "showNoRecords": { "alias": "showNoRecords"; "required": false; }; "noRecordFoundHeight": { "alias": "noRecordFoundHeight"; "required": false; }; "noRecordImgUrl": { "alias": "noRecordImgUrl"; "required": false; }; "noRecordMessage": { "alias": "noRecordMessage"; "required": false; }; }, { "change": "change"; "actionClick": "actionClick"; "sortChange": "sortChange"; "dragDropChange": "dragDropChange"; }, never, never, true, never>;
953
958
  }
954
959
 
955
960
  /** How an accent colour is applied to a control. */
@@ -969,9 +974,9 @@ interface Rgb {
969
974
  /** The neutral look used when there is no colour (or an unparseable one). */
970
975
  declare const NEUTRAL_APPEARANCE: ColorAppearance;
971
976
  /**
972
- * Parses hex and rgb()/rgba() colours. Named CSS colours ("red") are not
973
- * supported — they'd need a live DOM to resolve — and return null, which makes
974
- * the caller fall back to the neutral (uncoloured) appearance.
977
+ * Parses hex, rgb()/rgba() and hsl()/hsla() colours. Named CSS colours ("red")
978
+ * are not supported — they'd need a live DOM to resolve — and return null, which
979
+ * makes the caller fall back to the neutral (uncoloured) appearance.
975
980
  */
976
981
  declare function parseColor(input: string | null | undefined): Rgb | null;
977
982
  /**
@@ -1204,6 +1209,7 @@ type BkInputAutoComplete = 'on' | 'off' | 'name' | 'honorific-prefix' | 'given-n
1204
1209
  type BkInputAutoCapitalize = 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters';
1205
1210
  type BkInputMode = 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search';
1206
1211
  type IconOrientation = 'left' | 'right';
1212
+ type BkInputSize = 'default' | 'sm';
1207
1213
  interface CountryOption {
1208
1214
  code: string;
1209
1215
  name: string;
@@ -1222,6 +1228,8 @@ declare class BkInput implements OnInit, OnDestroy, AfterViewInit, ControlValueA
1222
1228
  hint: string;
1223
1229
  required: boolean;
1224
1230
  type: BkInputType;
1231
+ /** 'sm' shrinks height, padding, font size and icon sizing for compact contexts (e.g. dropdown search boxes). */
1232
+ size: BkInputSize;
1225
1233
  value: string;
1226
1234
  hasError: boolean;
1227
1235
  showErrorIcon: boolean;
@@ -1290,7 +1298,7 @@ declare class BkInput implements OnInit, OnDestroy, AfterViewInit, ControlValueA
1290
1298
  get currentInputType(): string;
1291
1299
  private writeMaskedViewValue;
1292
1300
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkInput, never>;
1293
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkInput, "bk-input", never, { "id": { "alias": "id"; "required": false; }; "name": { "alias": "name"; "required": false; }; "mask": { "alias": "mask"; "required": false; }; "dropSpecialCharacters": { "alias": "dropSpecialCharacters"; "required": false; }; "autoComplete": { "alias": "autoComplete"; "required": false; }; "label": { "alias": "label"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "hint": { "alias": "hint"; "required": false; }; "required": { "alias": "required"; "required": false; }; "type": { "alias": "type"; "required": false; }; "value": { "alias": "value"; "required": false; }; "hasError": { "alias": "hasError"; "required": false; }; "showErrorIcon": { "alias": "showErrorIcon"; "required": false; }; "errorMessage": { "alias": "errorMessage"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "tabIndex": { "alias": "tabIndex"; "required": false; }; "readOnly": { "alias": "readOnly"; "required": false; }; "autoCapitalize": { "alias": "autoCapitalize"; "required": false; }; "inputMode": { "alias": "inputMode"; "required": false; }; "iconSrc": { "alias": "iconSrc"; "required": false; }; "iconAlt": { "alias": "iconAlt"; "required": false; }; "showIcon": { "alias": "showIcon"; "required": false; }; "phone": { "alias": "phone"; "required": false; }; "countryCode": { "alias": "countryCode"; "required": false; }; "countryOptions": { "alias": "countryOptions"; "required": false; }; "iconOrientation": { "alias": "iconOrientation"; "required": false; }; "password": { "alias": "password"; "required": false; }; "showPassword": { "alias": "showPassword"; "required": false; }; "pattern": { "alias": "pattern"; "required": false; }; "max": { "alias": "max"; "required": false; }; "min": { "alias": "min"; "required": false; }; "step": { "alias": "step"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "minlength": { "alias": "minlength"; "required": false; }; }, { "input": "input"; "change": "change"; "focus": "focus"; "blur": "blur"; "clicked": "clicked"; }, never, never, true, never>;
1301
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkInput, "bk-input", never, { "id": { "alias": "id"; "required": false; }; "name": { "alias": "name"; "required": false; }; "mask": { "alias": "mask"; "required": false; }; "dropSpecialCharacters": { "alias": "dropSpecialCharacters"; "required": false; }; "autoComplete": { "alias": "autoComplete"; "required": false; }; "label": { "alias": "label"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "hint": { "alias": "hint"; "required": false; }; "required": { "alias": "required"; "required": false; }; "type": { "alias": "type"; "required": false; }; "size": { "alias": "size"; "required": false; }; "value": { "alias": "value"; "required": false; }; "hasError": { "alias": "hasError"; "required": false; }; "showErrorIcon": { "alias": "showErrorIcon"; "required": false; }; "errorMessage": { "alias": "errorMessage"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "tabIndex": { "alias": "tabIndex"; "required": false; }; "readOnly": { "alias": "readOnly"; "required": false; }; "autoCapitalize": { "alias": "autoCapitalize"; "required": false; }; "inputMode": { "alias": "inputMode"; "required": false; }; "iconSrc": { "alias": "iconSrc"; "required": false; }; "iconAlt": { "alias": "iconAlt"; "required": false; }; "showIcon": { "alias": "showIcon"; "required": false; }; "phone": { "alias": "phone"; "required": false; }; "countryCode": { "alias": "countryCode"; "required": false; }; "countryOptions": { "alias": "countryOptions"; "required": false; }; "iconOrientation": { "alias": "iconOrientation"; "required": false; }; "password": { "alias": "password"; "required": false; }; "showPassword": { "alias": "showPassword"; "required": false; }; "pattern": { "alias": "pattern"; "required": false; }; "max": { "alias": "max"; "required": false; }; "min": { "alias": "min"; "required": false; }; "step": { "alias": "step"; "required": false; }; "maxlength": { "alias": "maxlength"; "required": false; }; "minlength": { "alias": "minlength"; "required": false; }; }, { "input": "input"; "change": "change"; "focus": "focus"; "blur": "blur"; "clicked": "clicked"; }, never, never, true, never>;
1294
1302
  }
1295
1303
 
1296
1304
  declare class BkInputChips implements ControlValueAccessor, AfterViewInit {
@@ -2076,6 +2084,9 @@ declare class BKTooltipDirective implements OnInit, OnChanges, OnDestroy {
2076
2084
  onInteract(): void;
2077
2085
  onWindowChange(): void;
2078
2086
  private isTooltipContentEmpty;
2087
+ /** Snap-hide with no fade: used when something else (a dropdown panel, another
2088
+ * tooltip) is opening right now, so the two never visibly overlap mid-fade. */
2089
+ private hideTooltipInstant;
2079
2090
  private hideTooltip;
2080
2091
  private setupTooltipHoverListeners;
2081
2092
  private cleanupTooltipListeners;
@@ -2224,8 +2235,6 @@ declare class BkHierarchicalSelect implements ControlValueAccessor, AfterViewIni
2224
2235
  dropdownPosition: _angular_core.InputSignal<"top" | "bottom">;
2225
2236
  /** Key for option color (e.g. "color"). When set, option label and selected value use this color. */
2226
2237
  colorKey: _angular_core.InputSignal<string>;
2227
- /** Whether to show clear button when a value is selected. */
2228
- clearable: _angular_core.InputSignal<boolean>;
2229
2238
  /**
2230
2239
  * Show an accent dot beside each option, driven by `colorKey`, and tint the
2231
2240
  * control with the selected node's colour (same treatment as bk-status-select).
@@ -2237,6 +2246,8 @@ declare class BkHierarchicalSelect implements ControlValueAccessor, AfterViewIni
2237
2246
  * trees render exactly as before.
2238
2247
  */
2239
2248
  inheritColor: _angular_core.InputSignal<boolean>;
2249
+ /** Whether to show clear button when a value is selected. */
2250
+ clearable: _angular_core.InputSignal<boolean>;
2240
2251
  /** Optional anchor key to scope dropdown to one subtree (e.g. module/category key). */
2241
2252
  restrictKey: _angular_core.InputSignal<any>;
2242
2253
  hasError: _angular_core.InputSignal<boolean>;
@@ -2336,6 +2347,12 @@ declare class BkHierarchicalSelect implements ControlValueAccessor, AfterViewIni
2336
2347
  goBack(): void;
2337
2348
  onSearchInput(event: Event): void;
2338
2349
  selectItem(node: HierarchicalNode, event?: Event): void;
2350
+ /**
2351
+ * Select/deselect any node from its checkbox — parent, child or leaf, at any
2352
+ * depth. Stops the event so a parent row's own handler doesn't also navigate
2353
+ * into its children.
2354
+ */
2355
+ toggleParentSelection(node: HierarchicalNode, event?: Event): void;
2339
2356
  openFromLabel(event: MouseEvent): void;
2340
2357
  onClickOutside(e: Event): void;
2341
2358
  private _value;
@@ -2349,7 +2366,7 @@ declare class BkHierarchicalSelect implements ControlValueAccessor, AfterViewIni
2349
2366
  private findNodeByValue;
2350
2367
  handleClear(event: Event): void;
2351
2368
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkHierarchicalSelect, never>;
2352
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkHierarchicalSelect, "bk-hierarchical-select", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "valueKey": { "alias": "valueKey"; "required": false; "isSignal": true; }; "childrenKey": { "alias": "childrenKey"; "required": false; "isSignal": true; }; "clearTooltip": { "alias": "clearTooltip"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "iconSrc": { "alias": "iconSrc"; "required": false; "isSignal": true; }; "iconAlt": { "alias": "iconAlt"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "allowParentSelection": { "alias": "allowParentSelection"; "required": false; "isSignal": true; }; "backToMainText": { "alias": "backToMainText"; "required": false; "isSignal": true; }; "searchPlaceholder": { "alias": "searchPlaceholder"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "appendToBody": { "alias": "appendToBody"; "required": false; "isSignal": true; }; "dropdownPosition": { "alias": "dropdownPosition"; "required": false; "isSignal": true; }; "colorKey": { "alias": "colorKey"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "showDots": { "alias": "showDots"; "required": false; "isSignal": true; }; "inheritColor": { "alias": "inheritColor"; "required": false; "isSignal": true; }; "restrictKey": { "alias": "restrictKey"; "required": false; "isSignal": true; }; "hasError": { "alias": "hasError"; "required": false; "isSignal": true; }; }, { "selectionChange": "selectionChange"; "valueChange": "valueChange"; "clear": "clear"; }, never, never, true, never>;
2369
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkHierarchicalSelect, "bk-hierarchical-select", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "labelKey": { "alias": "labelKey"; "required": false; "isSignal": true; }; "valueKey": { "alias": "valueKey"; "required": false; "isSignal": true; }; "childrenKey": { "alias": "childrenKey"; "required": false; "isSignal": true; }; "clearTooltip": { "alias": "clearTooltip"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "iconSrc": { "alias": "iconSrc"; "required": false; "isSignal": true; }; "iconAlt": { "alias": "iconAlt"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "allowParentSelection": { "alias": "allowParentSelection"; "required": false; "isSignal": true; }; "backToMainText": { "alias": "backToMainText"; "required": false; "isSignal": true; }; "searchPlaceholder": { "alias": "searchPlaceholder"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "appendToBody": { "alias": "appendToBody"; "required": false; "isSignal": true; }; "dropdownPosition": { "alias": "dropdownPosition"; "required": false; "isSignal": true; }; "colorKey": { "alias": "colorKey"; "required": false; "isSignal": true; }; "showDots": { "alias": "showDots"; "required": false; "isSignal": true; }; "inheritColor": { "alias": "inheritColor"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "restrictKey": { "alias": "restrictKey"; "required": false; "isSignal": true; }; "hasError": { "alias": "hasError"; "required": false; "isSignal": true; }; }, { "selectionChange": "selectionChange"; "valueChange": "valueChange"; "clear": "clear"; }, never, never, true, never>;
2353
2370
  }
2354
2371
 
2355
2372
  type ToastSeverity = 'success' | 'error' | 'warn' | 'info' | 'default';
@@ -2493,6 +2510,9 @@ declare class BkColumnFilterService {
2493
2510
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<BkColumnFilterService>;
2494
2511
  }
2495
2512
 
2513
+ type BkColumnSelectVertical = 'top' | 'bottom';
2514
+ type BkColumnSelectHorizontal = 'left' | 'right';
2515
+ type BkColumnSelectPosition = `${BkColumnSelectVertical}-${BkColumnSelectHorizontal}`;
2496
2516
  declare class BkColumnSelect implements ControlValueAccessor, OnChanges {
2497
2517
  columnFilterService: BkColumnFilterService;
2498
2518
  searchable: boolean;
@@ -2502,8 +2522,15 @@ declare class BkColumnSelect implements ControlValueAccessor, OnChanges {
2502
2522
  isOpened: boolean;
2503
2523
  label: string;
2504
2524
  lefticon: string;
2525
+ /** Corner the dropdown opens from, relative to the trigger button. */
2526
+ position: BkColumnSelectPosition;
2527
+ /** When true, flips each axis independently if there isn't enough viewport room for `position`. */
2528
+ autoFlip: boolean;
2505
2529
  isOpenedChange: EventEmitter<boolean>;
2506
2530
  formBoxRef: ElementRef;
2531
+ triggerRef?: ElementRef<HTMLElement>;
2532
+ /** Corner actually rendered on — starts from `position`, auto-flipped on open when needed. */
2533
+ resolvedPosition: BkColumnSelectPosition;
2507
2534
  /** Value bound via ngModel – only updated when user ticks/unticks a checkbox, never on search. */
2508
2535
  columnsFilterList: ColumnFilterOption[];
2509
2536
  search: string;
@@ -2512,6 +2539,12 @@ declare class BkColumnSelect implements ControlValueAccessor, OnChanges {
2512
2539
  constructor(columnFilterService: BkColumnFilterService);
2513
2540
  writeValue(value: ColumnFilterOption[] | null | undefined): void;
2514
2541
  ngOnChanges(changes: SimpleChanges): void;
2542
+ /** Picks `position`, flipping each axis independently if the trigger doesn't have room for it. */
2543
+ private computePlacement;
2544
+ /** Flips `current` to its opposite when it doesn't have `needed` space and the opposite has more room. */
2545
+ private flipIfShort;
2546
+ /** Tailwind classes placing the panel on `resolvedPosition`'s corner of the trigger. */
2547
+ get panelPositionClass(): string;
2515
2548
  private shouldLoadFromService;
2516
2549
  private loadColumnsFilterListFromService;
2517
2550
  registerOnChange(fn: (value: ColumnFilterOption[]) => void): void;
@@ -2525,7 +2558,7 @@ declare class BkColumnSelect implements ControlValueAccessor, OnChanges {
2525
2558
  onClick(event: Event): void;
2526
2559
  get list(): ColumnFilterOption[];
2527
2560
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkColumnSelect, never>;
2528
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkColumnSelect, "bk-column-select", never, { "searchable": { "alias": "searchable"; "required": false; }; "buttonClass": { "alias": "buttonClass"; "required": false; }; "cacheKey": { "alias": "cacheKey"; "required": false; }; "columns": { "alias": "columns"; "required": false; }; "isOpened": { "alias": "isOpened"; "required": false; }; "label": { "alias": "label"; "required": false; }; "lefticon": { "alias": "lefticon"; "required": false; }; }, { "isOpenedChange": "isOpenedChange"; }, never, never, true, never>;
2561
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkColumnSelect, "bk-column-select", never, { "searchable": { "alias": "searchable"; "required": false; }; "buttonClass": { "alias": "buttonClass"; "required": false; }; "cacheKey": { "alias": "cacheKey"; "required": false; }; "columns": { "alias": "columns"; "required": false; }; "isOpened": { "alias": "isOpened"; "required": false; }; "label": { "alias": "label"; "required": false; }; "lefticon": { "alias": "lefticon"; "required": false; }; "position": { "alias": "position"; "required": false; }; "autoFlip": { "alias": "autoFlip"; "required": false; }; }, { "isOpenedChange": "isOpenedChange"; }, never, never, true, never>;
2529
2562
  }
2530
2563
 
2531
2564
  declare class BkFilePicker implements ControlValueAccessor, Validator {
@@ -2702,6 +2735,10 @@ declare class BkDropdown {
2702
2735
 
2703
2736
  type MenuOrientation = 'horizontal' | 'vertical';
2704
2737
  type MenuSubmenuMode = 'inline' | 'popup';
2738
+ type MenuTrigger = 'hover' | 'click';
2739
+ type MenuSize = 'default' | 'compact';
2740
+ /** Side a fly-out ended up on, once measured against the viewport. */
2741
+ type MenuPopupSide = 'right' | 'left' | 'down' | 'up';
2705
2742
  interface MenuItem {
2706
2743
  /** Unique id — matched against `activeItemId`. */
2707
2744
  id: string;
@@ -2713,6 +2750,17 @@ interface MenuItem {
2713
2750
  iconAlt?: string;
2714
2751
  /** Disables just this item (and blocks opening its submenu). */
2715
2752
  disabled?: boolean;
2753
+ /** Heading shown above this item's children, titling the group. */
2754
+ childrenLabel?: string;
2755
+ /**
2756
+ * Splits this item among its siblings into a labeled section — a heading
2757
+ * above the run of siblings that share the same `groupLabel`, with a rule
2758
+ * before each section after the first. Unlike `childrenLabel` (one heading
2759
+ * for a whole `children` array), this groups items within a single flat
2760
+ * list, so several named sections can sit in one always-visible panel (e.g.
2761
+ * a mega-menu). Siblings without a `groupLabel` render ungrouped, as today.
2762
+ */
2763
+ groupLabel?: string;
2716
2764
  /** Nested items — presence of children turns this into an expandable node. */
2717
2765
  children?: MenuItem[];
2718
2766
  }
@@ -2723,21 +2771,46 @@ interface MenuItem {
2723
2771
  * - Orientation: `vertical` (stacked) / `horizontal` (menubar row).
2724
2772
  * - Items with `children` are expandable, shown with a chevron that rotates
2725
2773
  * open/closed.
2726
- * - Submenus render inline (accordion) or as pop-ups. Pop-ups adapt to the
2727
- * orientation: they drop *below* a horizontal top-level item and fly out to
2728
- * the *right* for vertical menus / deeper levels.
2774
+ * - Submenus render inline (accordion) or as pop-ups, opened by hover or click.
2775
+ * Pop-ups adapt to the orientation: they drop *below* a horizontal top-level
2776
+ * item and fly out to the side for vertical menus / deeper levels. Side
2777
+ * fly-outs flip when the preferred side has no room; menubar drop-downs never
2778
+ * flip — they stay under their trigger and scroll instead.
2729
2779
  */
2730
- declare class BkMenu {
2780
+ declare class BkMenu implements OnChanges, OnDestroy {
2731
2781
  private readonly host;
2732
2782
  items: MenuItem[];
2733
2783
  orientation: MenuOrientation;
2734
2784
  submenuMode: MenuSubmenuMode;
2785
+ /**
2786
+ * How pop-up submenus open: on `hover` (fly-out follows the pointer and
2787
+ * closes when it leaves) or on `click` (stays put until you click the parent
2788
+ * again, pick a leaf, or click outside). Inline submenus are always click.
2789
+ */
2790
+ trigger: MenuTrigger;
2791
+ /** `compact` shrinks the type, padding and icons for dense layouts. */
2792
+ size: MenuSize;
2793
+ /** Fallback grace period for the pointer to cross the gap into a fly-out. */
2794
+ private static readonly HOVER_CLOSE_DELAY_MS;
2795
+ /**
2796
+ * Hover mode only: how long (ms) a fly-out survives after the pointer leaves,
2797
+ * giving it time to cross the gap into the panel. Raise it for menus whose
2798
+ * fly-outs sit further from their trigger, or for touch-ish pointers.
2799
+ */
2800
+ hoverCloseDelay: number;
2735
2801
  /**
2736
2802
  * Keep only one submenu open per level: opening a parent collapses its
2737
2803
  * siblings (and their descendants) so the menu stays compact. Pop-up mode
2738
2804
  * always behaves this way; this input adds it to inline (accordion) mode.
2739
2805
  */
2740
2806
  singleOpen: boolean;
2807
+ /**
2808
+ * Tints an inline (accordion) submenu's whole block — title + rows — with
2809
+ * the lightest gray token. Off by default. Pop-up submenus don't offer
2810
+ * this: their padding/insets are different, and the panel's own border
2811
+ * already sets it apart from the page.
2812
+ */
2813
+ submenuBackground: boolean;
2741
2814
  /** Id of the currently selected leaf item. Two-way bindable. */
2742
2815
  activeItemId: string;
2743
2816
  /** Tint item icons: dark by default, white on the active (dark) row. */
@@ -2752,33 +2825,81 @@ declare class BkMenu {
2752
2825
  itemClick: EventEmitter<MenuItem>;
2753
2826
  /** Ids of currently expanded/opened parent nodes. */
2754
2827
  private readonly openIds;
2755
- /** Viewport coordinates for each open fixed-position fly-out, keyed by id. */
2756
- readonly popupPos: Map<string, {
2757
- top: number;
2758
- left: number;
2759
- }>;
2828
+ /** Viewport box for each open fixed-position fly-out, keyed by id. */
2829
+ private readonly popupPos;
2830
+ /** Side each open fly-out resolved to — drives the chevron and hover bridge. */
2831
+ private readonly popupSide;
2832
+ /**
2833
+ * Fly-outs whose real size has been measured. Until then the panel is in the
2834
+ * DOM (it has to be, to be measurable) but transparent, so the estimated
2835
+ * position never flashes on the wrong side.
2836
+ */
2837
+ private readonly measuredIds;
2838
+ /** Floor for a fly-out's height cap, so a trigger near the bottom edge still
2839
+ * gets a usable (scrollable) panel rather than a sliver. */
2840
+ private static readonly MIN_POPUP_H;
2841
+ /**
2842
+ * Ceiling for a fly-out's height — automatic, not a consumer input. Without
2843
+ * one, a popup with many items grows to fill nearly the whole viewport just
2844
+ * because the room is there. Same three-tier breakpoints bk-select uses for
2845
+ * its options list, so a "large" panel reads the same across the design
2846
+ * system regardless of which component renders it.
2847
+ */
2848
+ private static popupHeightCeiling;
2849
+ private closeTimer;
2760
2850
  constructor(host: ElementRef<HTMLElement>);
2851
+ /**
2852
+ * Inline mode: whenever the active leaf is (re)set — including the very
2853
+ * first binding, so a deep link or a page refresh lands with the section
2854
+ * already open — expand every ancestor on its path. Pop-up fly-outs are
2855
+ * driven by hover/click, not by which leaf is active, so this is skipped
2856
+ * in `popup` mode.
2857
+ */
2858
+ ngOnChanges(changes: SimpleChanges): void;
2859
+ private expandAncestorsOfActive;
2860
+ /** Ids of every ancestor leading to `targetId` (root-first), `[]` if it's top-level, or `null` if not found. */
2861
+ private findAncestorChain;
2761
2862
  get isVertical(): boolean;
2762
2863
  get isPopup(): boolean;
2864
+ get isCompact(): boolean;
2865
+ get isHoverTrigger(): boolean;
2763
2866
  get maxHeightStyle(): string | null;
2764
2867
  popupTop(item: MenuItem): number | null;
2765
2868
  popupLeft(item: MenuItem): number | null;
2869
+ popupMaxWidth(item: MenuItem): string | null;
2870
+ popupMaxHeight(item: MenuItem): string | null;
2871
+ onLabelEnter(event: MouseEvent): void;
2872
+ /** Resolved side of an open fly-out, or null before it has been placed. */
2873
+ submenuSide(item: MenuItem): MenuPopupSide | null;
2874
+ /** True while a fly-out is open but not yet measured — kept transparent. */
2875
+ isPlacing(item: MenuItem): boolean;
2876
+ /** Which way the parent's chevron points. */
2877
+ chevronDir(item: MenuItem, level: number): 'down' | 'right' | 'left';
2766
2878
  hasChildren(item: MenuItem): boolean;
2879
+ isGroupStart(items: MenuItem[], index: number): boolean;
2767
2880
  isOpen(item: MenuItem): boolean;
2768
2881
  isActive(item: MenuItem): boolean;
2769
2882
  isActiveAncestor(item: MenuItem): boolean;
2770
2883
  isHighlighted(item: MenuItem): boolean;
2771
2884
  inlineIndent(level: number): number | null;
2772
- onItemClick(item: MenuItem, siblings: MenuItem[], event: Event): void;
2773
- onItemEnter(item: MenuItem, siblings: MenuItem[], event: MouseEvent, level: number): void;
2774
- private computePopupPos;
2885
+ groupTitleIndent(level: number): number | null;
2886
+ onItemClick(item: MenuItem, siblings: MenuItem[], event: Event, level: number, parent: MenuItem | null): void;
2887
+ onItemEnter(item: MenuItem, siblings: MenuItem[], event: MouseEvent, level: number, parent: MenuItem | null): void;
2775
2888
  onItemLeave(item: MenuItem): void;
2889
+ private cancelPendingClose;
2890
+ ngOnDestroy(): void;
2891
+ private placePopup;
2892
+ private preferredSide;
2893
+ private applyPos;
2894
+ private computePopupPos;
2895
+ private clamp;
2776
2896
  private toggle;
2777
2897
  private closeBranch;
2898
+ private closeAll;
2778
2899
  onDocumentClick(event: MouseEvent): void;
2779
2900
  onViewportChange(): void;
2780
2901
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkMenu, never>;
2781
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkMenu, "bk-menu", never, { "items": { "alias": "items"; "required": false; }; "orientation": { "alias": "orientation"; "required": false; }; "submenuMode": { "alias": "submenuMode"; "required": false; }; "singleOpen": { "alias": "singleOpen"; "required": false; }; "activeItemId": { "alias": "activeItemId"; "required": false; }; "tintIcons": { "alias": "tintIcons"; "required": false; }; "maxHeight": { "alias": "maxHeight"; "required": false; }; }, { "activeItemIdChange": "activeItemIdChange"; "itemClick": "itemClick"; }, never, never, true, never>;
2902
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkMenu, "bk-menu", never, { "items": { "alias": "items"; "required": false; }; "orientation": { "alias": "orientation"; "required": false; }; "submenuMode": { "alias": "submenuMode"; "required": false; }; "trigger": { "alias": "trigger"; "required": false; }; "size": { "alias": "size"; "required": false; }; "hoverCloseDelay": { "alias": "hoverCloseDelay"; "required": false; }; "singleOpen": { "alias": "singleOpen"; "required": false; }; "submenuBackground": { "alias": "submenuBackground"; "required": false; }; "activeItemId": { "alias": "activeItemId"; "required": false; }; "tintIcons": { "alias": "tintIcons"; "required": false; }; "maxHeight": { "alias": "maxHeight"; "required": false; }; }, { "activeItemIdChange": "activeItemIdChange"; "itemClick": "itemClick"; }, never, never, true, never>;
2782
2903
  }
2783
2904
 
2784
2905
  /** Which edge of the anchor the panel sits against. */
@@ -2992,5 +3113,965 @@ declare class BkAvatarGroup {
2992
3113
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkAvatarGroup, "bk-avatar-group", never, { "avatars": { "alias": "avatars"; "required": false; }; "size": { "alias": "size"; "required": false; }; "max": { "alias": "max"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "showOverflow": { "alias": "showOverflow"; "required": false; }; }, {}, never, never, true, never>;
2993
3114
  }
2994
3115
 
2995
- export { BKTooltipDirective, BK_DEFAULT_DIALOG_CONFIG, BK_DIALOG_DATA, BK_DIALOG_GLOBAL_CONFIG, BkAvatar, BkAvatarGroup, BkAvatarUploader, BkBadge, BkButton, BkButtonGroup, BkCalendarManagerService, BkCheckbox, BkColumnFilterService, BkColumnSelect, BkCustomCalendar, BkDialogActions, BkDialogClose, BkDialogContent, BkDialogModule, BkDialogRef, BkDialogService, BkDialogTitle, BkDropdown, BkFileCard, BkFilePicker, BkGrid, BkHierarchicalSelect, BkIconButton, BkInput, BkInputChips, BkLoader, BkMenu, BkPagination, BkPill, BkPopover, BkRadioButton, BkScheduledDatePicker, BkSelect, BkSpinner, BkTabs, BkTextarea, BkTimePicker, BkToastr, BkToastrService, BkToggle, BkValidator, BrickclayIcons, BrickclayLib, CalendarModule, CalendarSelection, ColumnFilterOption, NEUTRAL_APPEARANCE, POPOVER_PLACEMENTS, computePopoverPosition, deriveAppearance, getDialogBackdropAnimation, getDialogPanelAnimation, joinPlacement, parseColor, splitPlacement };
2996
- export type { AvatarFallback, AvatarGroupItem, AvatarSize, AvatarVariant, BadgeColor, BadgeSize, BadgeVariant, BkAnimationKeyframes, BkAvatarFallback, BkAvatarSize, BkDialogAnimation, BkDialogConfig, BkDialogPosition, BkInputAutoCapitalize, BkInputAutoComplete, BkInputMode, BkInputType, BkLoaderVariant, BkPageSize, BkTextAreaAutoCapitalize, BkTextAreaAutoComplete, BkTextAreaInputMode, ButtonSize, ButtonVariant, CalendarRange, ColorAppearance, ColorFill, CountryOption, CustomRangesConfig, DotPosition, DotStatus, DropdownItem, DropdownPlacement, DropdownSize, DropdownTrigger, DropdownVariant, FileState, GroupItem, GroupMode, HierarchicalNode, IconButtonSize, IconButtonVariant, IconOrientation, MenuItem, MenuOrientation, MenuSubmenuMode, PillColor, PillSize, PillVariant, PopoverAlign, PopoverPlacement, PopoverPosition, PopoverPositionOptions, PopoverSide, Rect, ScheduledDateSelection, Size, SortDirection, SpinnerSize, TabIconDirection, TabItem, TableAction, TableBadge, TableColumn, TableIcon, TabsColors, TabsOrientation, TabsVariant, TimeConfiguration, ToastConfig, ToastMessage, ToastMethodOptions, ToastPosition, ToastSeverity };
3116
+ /**
3117
+ * Types for bk-table.
3118
+ *
3119
+ * The control is template-driven: consumers write their own <thead>/<tbody>
3120
+ * markup and the component supplies behaviour. These types describe the
3121
+ * contracts passed *into* that markup — sorting, filtering and query state.
3122
+ */
3123
+ /** Ascending, descending, or unsorted. Matches the tri-state sort cycle. */
3124
+ type BkSortOrder = 'ascend' | 'descend' | null;
3125
+ /** Which orders a column cycles through, in order. `null` clears the sort. */
3126
+ type BkSortDirection = BkSortOrder;
3127
+ /**
3128
+ * How a column sorts.
3129
+ *
3130
+ * A comparator sorts client-side. `true` means "this column is sortable but
3131
+ * the server does the work" — the table emits queryParams and leaves the rows
3132
+ * untouched.
3133
+ */
3134
+ type BkSortFn<T = any> = ((a: T, b: T, sortOrder: BkSortOrder) => number) | boolean;
3135
+ /** One entry in a column's filter menu. */
3136
+ interface BkFilterOption {
3137
+ text: string;
3138
+ value: any;
3139
+ /** Pre-select this option on first render. */
3140
+ byDefault?: boolean;
3141
+ }
3142
+ /**
3143
+ * How a column filters.
3144
+ *
3145
+ * The predicate receives the selected values (always an array, even in single
3146
+ * mode) and a row, and returns whether the row survives. `true` defers to the
3147
+ * server, like the sort equivalent.
3148
+ */
3149
+ type BkFilterFn<T = any> = ((selected: any[], item: T) => boolean) | boolean;
3150
+ /** Everything the server needs to reproduce the current view. */
3151
+ interface BkTableQueryParams {
3152
+ pageIndex: number;
3153
+ pageSize: number;
3154
+ sort: {
3155
+ key: string;
3156
+ value: BkSortOrder;
3157
+ }[];
3158
+ filter: {
3159
+ key: string;
3160
+ value: any[];
3161
+ }[];
3162
+ }
3163
+ /**
3164
+ * Table density, roomiest first: `lg`, `default`, `md`, `sm`.
3165
+ *
3166
+ * `sm` is the one to reach for inside a modal, where the surrounding chrome
3167
+ * has already spent most of the height. `middle` and `small` are ng-zorro's
3168
+ * names for `md` and `sm`, kept so existing markup keeps working.
3169
+ */
3170
+ type BkTableSize = 'lg' | 'default' | 'md' | 'sm' | 'middle' | 'small';
3171
+ /** The four real densities, once the ng-zorro aliases are resolved. */
3172
+ type BkTableSizeToken = 'lg' | 'default' | 'md' | 'sm';
3173
+ declare function normalizeBkTableSize(size: BkTableSize): BkTableSizeToken;
3174
+ /**
3175
+ * Fixed-header / fixed-column configuration.
3176
+ *
3177
+ * `x` sets a minimum table width so columns stop collapsing and scroll
3178
+ * horizontally instead; `y` caps body height and pins the header.
3179
+ */
3180
+ interface BkTableScroll {
3181
+ x?: string | null;
3182
+ y?: string | null;
3183
+ /**
3184
+ * `y` is a ceiling by default (`max-height`) — a table with fewer rows
3185
+ * than that shrinks to fit them, same as `nz-table`. Set `fill` to make it
3186
+ * a floor instead (`height`): the body always occupies that space, even
3187
+ * with one row, so surrounding layout (a pager below it, a page built
3188
+ * around a fixed grid height) doesn't shift with the row count.
3189
+ */
3190
+ fill?: boolean;
3191
+ }
3192
+ /** A custom entry in the select-all dropdown (e.g. "Select odd rows"). */
3193
+ interface BkTableSelection {
3194
+ text: string;
3195
+ onSelect: () => void;
3196
+ }
3197
+ /** Horizontal cell alignment. */
3198
+ type BkCellAlign = 'left' | 'center' | 'right';
3199
+ /**
3200
+ * Flattens a tree into the linear array a <tbody> can iterate, keeping only
3201
+ * the branches whose ancestors are all expanded.
3202
+ *
3203
+ * The table can't do this itself: it never sees the tree, only the rows you
3204
+ * hand it. Call this in your component whenever expansion state changes.
3205
+ *
3206
+ * @param root top-level nodes
3207
+ * @param childrenKey property holding each node's children
3208
+ * @param isExpanded whether a node's children should be visible
3209
+ */
3210
+ interface BkTreeRow<T> {
3211
+ data: T;
3212
+ level: number;
3213
+ /** Undefined at the root. Carrying it is what makes a drop resolvable. */
3214
+ parent?: T;
3215
+ }
3216
+ declare function flattenBkTreeData<T extends Record<string, any>>(root: T[], childrenKey?: keyof T & string, isExpanded?: (node: T) => boolean): BkTreeRow<T>[];
3217
+ /**
3218
+ * Whether `node` sits anywhere inside `ancestor`'s subtree.
3219
+ *
3220
+ * The guard a tree drag cannot do without: dropping a node into its own
3221
+ * descendant detaches that whole branch from the tree, and nothing afterwards
3222
+ * can find it again.
3223
+ */
3224
+ declare function containsBkTreeNode<T extends Record<string, any>>(ancestor: T, node: T, childrenKey?: keyof T & string): boolean;
3225
+ /**
3226
+ * Where a tree drop lands relative to its target row: as the sibling before
3227
+ * it, the sibling after it, or as its child.
3228
+ */
3229
+ type BkTreeDropPosition = 'before' | 'after' | 'inside';
3230
+ /** What `BkTreeDrag` resolves a completed drag into. */
3231
+ interface BkTreeDropEvent<T = any> {
3232
+ dragged: BkTreeRow<T>;
3233
+ target: BkTreeRow<T>;
3234
+ position: BkTreeDropPosition;
3235
+ }
3236
+ /**
3237
+ * Applies a resolved tree drop to the tree behind the rows.
3238
+ *
3239
+ * `before` / `after` insert into the target's sibling list; `inside` makes the
3240
+ * dragged node the target's **first** child — first, not last, so the row
3241
+ * appears directly beneath the row it was dropped on rather than teleporting
3242
+ * to the bottom of a long branch.
3243
+ *
3244
+ * A node's subtree travels with it, because it is nested inside it. Dropping a
3245
+ * node into its own descendant is refused: that move would detach the branch
3246
+ * from the tree with nothing left pointing at it.
3247
+ *
3248
+ * Mutates `root` in place and reports whether anything moved; re-emit your
3249
+ * signal with a fresh array identity when it returns true.
3250
+ */
3251
+ declare function moveBkTreeNode<T extends Record<string, any>>(root: T[], dragged: BkTreeRow<T>, target: BkTreeRow<T>, position: BkTreeDropPosition, childrenKey?: keyof T & string): boolean;
3252
+
3253
+ /** Marks projected content as the table's title bar. */
3254
+ declare class BkTableTitle {
3255
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkTableTitle, never>;
3256
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BkTableTitle, "[bkTableTitle]", never, {}, {}, never, never, true, never>;
3257
+ }
3258
+ /** Marks projected content as the table's footer bar. */
3259
+ declare class BkTableFooter {
3260
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkTableFooter, never>;
3261
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BkTableFooter, "[bkTableFooter]", never, {}, {}, never, never, true, never>;
3262
+ }
3263
+ /**
3264
+ * Row template for virtual scroll mode.
3265
+ *
3266
+ * Virtual scrolling can't use a projected <tbody> — the rows have to be created
3267
+ * and destroyed by the viewport as it scrolls, so they must come from a
3268
+ * template the table instantiates itself:
3269
+ *
3270
+ * <tbody>
3271
+ * <ng-template bkVirtualScroll let-row let-i="index">
3272
+ * <tr>…</tr>
3273
+ * </ng-template>
3274
+ * </tbody>
3275
+ */
3276
+ declare class BkVirtualScroll {
3277
+ templateRef: TemplateRef<{
3278
+ $implicit: any;
3279
+ index: number;
3280
+ }>;
3281
+ constructor(templateRef: TemplateRef<{
3282
+ $implicit: any;
3283
+ index: number;
3284
+ }>);
3285
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkVirtualScroll, never>;
3286
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BkVirtualScroll, "[bkVirtualScroll]", never, {}, {}, never, never, true, never>;
3287
+ }
3288
+ /**
3289
+ * Anything a column needs to expose so the table can sort and filter it.
3290
+ * `th[bk-th]` implements this and registers itself on construction; the
3291
+ * interface exists only to keep the two files from importing each other.
3292
+ */
3293
+ interface BkColumnRef {
3294
+ columnKey: string | null;
3295
+ width: string | null;
3296
+ sortOrder: WritableSignal<BkSortOrder>;
3297
+ sortPriority: number | null;
3298
+ clientSortFn: ((a: any, b: any, order: BkSortOrder) => number) | null;
3299
+ filterValue: WritableSignal<any[]>;
3300
+ clientFilterFn: ((selected: any[], item: any) => boolean) | null;
3301
+ clearSort(): void;
3302
+ clearFilter(): void;
3303
+ }
3304
+ /**
3305
+ * Template-driven table.
3306
+ *
3307
+ * Unlike `bk-grid`, this component renders none of your cells — you write the
3308
+ * `<thead>`/`<tbody>` markup and it supplies behaviour: sort and filter state,
3309
+ * pagination, sticky header and columns, virtual scrolling. That's what makes
3310
+ * colspan, nested tables, editable cells and grouped headers possible at all;
3311
+ * none of them can be expressed as a column config object.
3312
+ *
3313
+ * <bk-table #t [data]="rows" [bordered]="true" size="middle">
3314
+ * <thead>
3315
+ * <tr><th bk-th [sortFn]="byName">Name</th></tr>
3316
+ * </thead>
3317
+ * <tbody>
3318
+ * @for (row of t.renderData(); track row.id) {
3319
+ * <tr><td bk-td>{{ row.name }}</td></tr>
3320
+ * }
3321
+ * </tbody>
3322
+ * </bk-table>
3323
+ *
3324
+ * Client vs server work is decided per column, exactly like ng-zorro: pass a
3325
+ * comparator to `sortFn` and the table sorts; pass `true` and it only reports
3326
+ * the change through `queryParams` for you to handle.
3327
+ */
3328
+ declare class BkTable<T = any> implements AfterViewInit, OnDestroy {
3329
+ private readonly host;
3330
+ private readonly zone;
3331
+ constructor();
3332
+ /** Full row set. In server mode, only the current page's rows. */
3333
+ data: _angular_core.InputSignal<readonly T[]>;
3334
+ /** Roomiest first: `lg`, `default`, `md`, `sm`. `sm` suits modals. */
3335
+ size: _angular_core.InputSignal<BkTableSize>;
3336
+ /** One class rather than a binding per size, and it resolves the aliases. */
3337
+ readonly sizeClass: _angular_core.Signal<string>;
3338
+ bordered: _angular_core.InputSignal<boolean>;
3339
+ /** Convenience alternative to projecting `[bkTableTitle]`. */
3340
+ title: _angular_core.InputSignal<string>;
3341
+ /** Convenience alternative to projecting `[bkTableFooter]`. */
3342
+ footer: _angular_core.InputSignal<string>;
3343
+ /**
3344
+ * `x` is a min-width that forces horizontal scrolling instead of column
3345
+ * collapse; `y` caps the body height and pins the header. Fixed columns
3346
+ * (`th[bk-th][left]`) need `x` set to have anything to scroll over.
3347
+ */
3348
+ scroll: _angular_core.InputSignal<BkTableScroll>;
3349
+ loading: _angular_core.InputSignal<boolean>;
3350
+ /**
3351
+ * `loading` always suppresses the empty state — a fetch in flight isn't
3352
+ * "no data," and shouldn't flash that message just because the previous
3353
+ * page's rows haven't arrived yet. This is only about the *spinner*
3354
+ * overlay `loading` draws on top of the table. Turn it off when a
3355
+ * consumer already shows its own loading indicator elsewhere and doesn't
3356
+ * want a second one competing with it — `loading` keeps doing its other
3357
+ * job either way.
3358
+ */
3359
+ showLoadingOverlay: _angular_core.InputSignal<boolean>;
3360
+ /** Shown when there are no rows to render. */
3361
+ noResultText: _angular_core.InputSignal<string>;
3362
+ noResultImage: _angular_core.InputSignal<string>;
3363
+ /**
3364
+ * Set once `noResultImage()` fails to load, so the template can drop the
3365
+ * `<img>` instead of leaving the browser's broken-image glyph sitting next
3366
+ * to the empty-state text — bk-grid's empty state does exactly that on any
3367
+ * deployment where the asset path doesn't resolve. The text alone still
3368
+ * communicates "no data"; a broken icon just looks like the component is
3369
+ * broken.
3370
+ */
3371
+ readonly emptyImageFailed: WritableSignal<boolean>;
3372
+ onEmptyImageError(): void;
3373
+ showPagination: _angular_core.InputSignal<boolean>;
3374
+ /**
3375
+ * When true the table slices `data` itself. Turn it off for server-side
3376
+ * paging and feed `total` instead.
3377
+ */
3378
+ frontPagination: _angular_core.InputSignal<boolean>;
3379
+ pageIndex: _angular_core.ModelSignal<number>;
3380
+ /**
3381
+ * Defaults to 25 because that is the smallest size `bk-pagination` offers
3382
+ * (25/50/75/100). A value outside that list matches no option and leaves its
3383
+ * "rows per page" select rendering blank.
3384
+ */
3385
+ pageSize: _angular_core.ModelSignal<number>;
3386
+ /** Row count for server mode. Ignored when `frontPagination` is on. */
3387
+ total: _angular_core.InputSignal<number>;
3388
+ /**
3389
+ * Row height in px. Required for virtual scroll — the viewport can't measure
3390
+ * rows it hasn't created. Rows must be uniform height, which is why virtual
3391
+ * scroll does not combine with rowspan, tree data or expandable rows.
3392
+ */
3393
+ virtualItemSize: _angular_core.InputSignal<number>;
3394
+ virtualTrackBy: _angular_core.InputSignal<(index: number, item: T) => any>;
3395
+ private viewport?;
3396
+ /**
3397
+ * Scrolls the virtual viewport so the row at `index` (into `renderData()`)
3398
+ * lands at the top.
3399
+ *
3400
+ * Indexes are 0-based against the rendered rows, not the raw `data` input —
3401
+ * sorting/filtering already reduced it to what's actually on screen, and
3402
+ * jumping against the raw array would land on the wrong row the moment
3403
+ * either is active.
3404
+ */
3405
+ scrollToIndex(index: number, behavior?: ScrollBehavior): void;
3406
+ /**
3407
+ * Hold the columns still once they have been measured.
3408
+ *
3409
+ * A table left on `table-layout: auto` sizes its columns from whatever
3410
+ * content is currently on screen, so sorting, filtering or paging — none of
3411
+ * which change the columns — visibly resizes them. Freezing the proportions
3412
+ * after the first real layout keeps the header from jumping under the
3413
+ * pointer that just clicked it.
3414
+ *
3415
+ * Turn it off if the content genuinely should drive the widths on every
3416
+ * change. Columns with an explicit `width` are never overwritten either way.
3417
+ */
3418
+ stableColumnWidth: _angular_core.InputSignal<boolean>;
3419
+ /** Everything the server needs to reproduce the current view. */
3420
+ queryParams: _angular_core.OutputEmitterRef<BkTableQueryParams>;
3421
+ /** The rows actually rendered, after filter/sort/paging. */
3422
+ currentPageDataChange: _angular_core.OutputEmitterRef<readonly T[]>;
3423
+ titleContent?: BkTableTitle;
3424
+ footerContent?: BkTableFooter;
3425
+ virtualScroll?: BkVirtualScroll;
3426
+ /**
3427
+ * Registered `th[bk-th]` instances. Sorting and filtering are driven by
3428
+ * column state, so the pipeline below reads through this signal — replacing
3429
+ * the array (rather than mutating it) is what makes the computeds re-run.
3430
+ */
3431
+ readonly columns: WritableSignal<BkColumnRef[]>;
3432
+ registerColumn(column: BkColumnRef): void;
3433
+ unregisterColumn(column: BkColumnRef): void;
3434
+ /**
3435
+ * Re-emits query params. Columns call this when their sort or filter
3436
+ * changes; the data pipeline reacts on its own through signals, so this
3437
+ * exists purely to notify the consumer.
3438
+ */
3439
+ onColumnStateChange(): void;
3440
+ /**
3441
+ * Rows surviving every column's client-side filter. Columns whose filter is
3442
+ * handled server-side contribute nothing here.
3443
+ */
3444
+ private readonly filtered;
3445
+ /**
3446
+ * Filtered rows in sort order.
3447
+ *
3448
+ * Multi-sort runs columns by descending `sortPriority`, and comparators are
3449
+ * applied in that order until one breaks the tie — so a priority-2 column
3450
+ * only orders rows a priority-3 column considers equal.
3451
+ */
3452
+ private readonly sorted;
3453
+ /** Row count before paging — what the pager needs to size itself. */
3454
+ readonly totalCount: _angular_core.Signal<number>;
3455
+ /**
3456
+ * The rows to render. Iterate this in your `<tbody>`, not the raw input:
3457
+ * it is the input after filtering, sorting and paging.
3458
+ */
3459
+ readonly renderData: _angular_core.Signal<readonly T[]>;
3460
+ readonly isVirtual: _angular_core.Signal<boolean>;
3461
+ /**
3462
+ * Column widths for the virtual-scroll `<colgroup>`.
3463
+ *
3464
+ * Virtual scroll splits the header and body into two tables, so they can no
3465
+ * longer size each other — a shared colgroup is what keeps their columns
3466
+ * lined up, and it needs a declared width per column to do it.
3467
+ */
3468
+ readonly colWidths: _angular_core.Signal<(string | null)[]>;
3469
+ readonly hasTitle: _angular_core.Signal<boolean>;
3470
+ readonly hasFooter: _angular_core.Signal<boolean>;
3471
+ readonly isEmpty: _angular_core.Signal<boolean>;
3472
+ /** Floor for the empty state's home: `.bk-table-empty` is absolutely
3473
+ * positioned against this container (see table.css), so without either
3474
+ * `scroll.y` or this fallback it'd have a ~0px-tall box to center in
3475
+ * (just the header row, since an empty `<tbody>` contributes nothing)
3476
+ * and get clipped by `overflow-auto`. */
3477
+ private static readonly EMPTY_STATE_MIN_HEIGHT;
3478
+ readonly containerStyle: _angular_core.Signal<{
3479
+ 'min-height': string;
3480
+ 'overflow-y'?: undefined;
3481
+ } | {
3482
+ 'min-height'?: undefined;
3483
+ 'overflow-y'?: undefined;
3484
+ } | {
3485
+ [x: string]: string;
3486
+ 'overflow-y': string;
3487
+ 'min-height'?: undefined;
3488
+ }>;
3489
+ /** Set once the columns have been measured and pinned. */
3490
+ readonly layoutFixed: WritableSignal<boolean>;
3491
+ readonly tableStyle: _angular_core.Signal<Record<string, string>>;
3492
+ virtualHead?: ElementRef<HTMLElement>;
3493
+ private headResizeObserver?;
3494
+ private layoutResizeObserver?;
3495
+ ngAfterViewInit(): void;
3496
+ ngOnDestroy(): void;
3497
+ /**
3498
+ * Publishes the first header row's height as `--bk-thead-row-1`.
3499
+ *
3500
+ * A grouped header's second row has to stick *below* the first, which means
3501
+ * knowing how tall the first is — and that changes with density, with font
3502
+ * loading, and with any header that wraps. Measuring it is the only offset
3503
+ * that stays correct; a constant is right for exactly one configuration.
3504
+ */
3505
+ private observeHeaderHeight;
3506
+ /**
3507
+ * Two unrelated jobs that both boil down to "re-check this whenever the
3508
+ * table's or the container's box size changes," so one `ResizeObserver`
3509
+ * watching both elements does them together.
3510
+ *
3511
+ * **Retries the column freeze.** A table that mounts inside a `[hidden]`
3512
+ * ancestor — a collapsed `tr[bkExpand]` holding a nested `bk-table`, most
3513
+ * commonly — measures 0 on its first freeze attempt, so
3514
+ * `freezeColumnWidths()` correctly declines to pin widths it can't see
3515
+ * (`if (!tableWidth) return;`). Nothing else ever asks it to try again: the
3516
+ * constructor effect only reruns when one of its own signals changes, and
3517
+ * none of them do just because the table became visible. Left alone, that
3518
+ * table stays on `table-layout: auto` forever — and an auto-layout cell
3519
+ * can't give a nested table's `min-width: 100%` a real number to resolve
3520
+ * against, which is what leaves a nested `bk-table` stuck at its own
3521
+ * shrink-to-fit width even after the row expands. Once `layoutFixed` is
3522
+ * true the call is a no-op, so this can just stay connected for the
3523
+ * table's life rather than tracking whether it still needs to be.
3524
+ *
3525
+ * **Tracks whether the rows fall short of the container.** See
3526
+ * `updateUnderfilled`.
3527
+ */
3528
+ private observeTableVisibility;
3529
+ /**
3530
+ * Marks the host when rows end well short of a reserved `scroll.y`
3531
+ * (`fill`, most commonly) height, so `.bk-table-underfilled` in the
3532
+ * stylesheet can close the real data off with a border instead of letting
3533
+ * it trail into blank reserved space with nothing marking where it
3534
+ * stopped. Left off whenever rows fill or overflow the container — the
3535
+ * frame's own border (or the scroll cutoff) already closes those cases,
3536
+ * and adding another border would double up against it.
3537
+ */
3538
+ private updateUnderfilled;
3539
+ /**
3540
+ * Pins the columns at the proportions auto layout arrived at.
3541
+ *
3542
+ * Widths are stored as percentages, not pixels: under `table-layout: fixed`
3543
+ * a percentage resolves against the table, so the columns keep their
3544
+ * relationship when the container resizes. Pixels would freeze the table at
3545
+ * whatever width it happened to have when the rows first arrived.
3546
+ *
3547
+ * Only the first header row is written to — that is the row `table-layout:
3548
+ * fixed` reads widths from. A grouped header's second row inherits its share
3549
+ * of whatever the group above it was given.
3550
+ */
3551
+ private freezeColumnWidths;
3552
+ /** The cells this component wrote a width to, so it can undo exactly those. */
3553
+ private frozenCells;
3554
+ /** Column count the frozen widths were measured against. */
3555
+ private frozenColumnCount;
3556
+ /** Guards against stacking timeouts while columns are still registering. */
3557
+ private freezeScheduled;
3558
+ /**
3559
+ * Drops the frozen widths so the columns are measured again. Call it after
3560
+ * replacing the dataset with one whose shape is different enough that the
3561
+ * old proportions no longer suit it.
3562
+ */
3563
+ remeasureColumns(): void;
3564
+ /**
3565
+ * Keeps the detached virtual-scroll header aligned with the body it no
3566
+ * longer shares a table with.
3567
+ */
3568
+ onViewportScroll(event: Event): void;
3569
+ onPageIndexChange(index: number): void;
3570
+ onPageSizeChange(size: number): void;
3571
+ /** Clears every column's sort. Backs "reset sorters" in the demo. */
3572
+ clearSort(): void;
3573
+ /** Clears every column's filter selection. */
3574
+ clearFilter(): void;
3575
+ /** Clears sorts and filters together and returns to the first page. */
3576
+ reset(): void;
3577
+ private emitQueryParams;
3578
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkTable<any>, never>;
3579
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkTable<any>, "bk-table", ["bkTable"], { "data": { "alias": "data"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "bordered": { "alias": "bordered"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "footer": { "alias": "footer"; "required": false; "isSignal": true; }; "scroll": { "alias": "scroll"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "showLoadingOverlay": { "alias": "showLoadingOverlay"; "required": false; "isSignal": true; }; "noResultText": { "alias": "noResultText"; "required": false; "isSignal": true; }; "noResultImage": { "alias": "noResultImage"; "required": false; "isSignal": true; }; "showPagination": { "alias": "showPagination"; "required": false; "isSignal": true; }; "frontPagination": { "alias": "frontPagination"; "required": false; "isSignal": true; }; "pageIndex": { "alias": "pageIndex"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "total": { "alias": "total"; "required": false; "isSignal": true; }; "virtualItemSize": { "alias": "virtualItemSize"; "required": false; "isSignal": true; }; "virtualTrackBy": { "alias": "virtualTrackBy"; "required": false; "isSignal": true; }; "stableColumnWidth": { "alias": "stableColumnWidth"; "required": false; "isSignal": true; }; }, { "pageIndex": "pageIndexChange"; "pageSize": "pageSizeChange"; "queryParams": "queryParams"; "currentPageDataChange": "currentPageDataChange"; }, ["titleContent", "footerContent", "virtualScroll"], ["thead", "tbody", "tfoot", "[bkTableTitle]", "[bkTableFooter]"], true, never>;
3580
+ }
3581
+
3582
+ /**
3583
+ * Behaviour shared by `th[bk-th]` and `td[bk-td]`: fixed columns, text
3584
+ * overflow handling and alignment.
3585
+ *
3586
+ * Fixed columns are `position: sticky`, which needs a pixel offset — and the
3587
+ * correct offset is the cell's distance from the edge of the table, which is
3588
+ * only knowable after layout. Subclasses get that measurement for free.
3589
+ */
3590
+ declare abstract class BkCellBase implements AfterViewInit, OnDestroy {
3591
+ protected readonly el: ElementRef<any>;
3592
+ private readonly zone;
3593
+ /**
3594
+ * Pin this column to the left edge while the table scrolls horizontally.
3595
+ * `true` measures the offset; a string sets it explicitly.
3596
+ *
3597
+ * Pinned columns must be contiguous from the edge — a gap leaves the columns
3598
+ * beyond it overlapping as they scroll underneath.
3599
+ */
3600
+ left: _angular_core.InputSignal<string | boolean>;
3601
+ /** Pin to the right edge. Same rules as `left`. */
3602
+ right: _angular_core.InputSignal<string | boolean>;
3603
+ /**
3604
+ * Truncate overflow with an ellipsis instead of wrapping.
3605
+ *
3606
+ * Put it on the `td`, not the `th`. It works by setting `max-width: 0` on
3607
+ * whichever cell carries it, and `table-layout: fixed` sizes a column from
3608
+ * its header cell alone — a `max-width` on the header collapses the whole
3609
+ * column toward zero regardless of any declared `width`. A `td`'s own width
3610
+ * is never consulted by `fixed` layout, so the same rule there only clips
3611
+ * the content, which is the point.
3612
+ */
3613
+ ellipsis: _angular_core.InputSignal<boolean>;
3614
+ /**
3615
+ * Allow breaks *inside* words. Without it a long unbroken string (a URL, an
3616
+ * ID) pushes past the cell instead of wrapping.
3617
+ */
3618
+ breakWord: _angular_core.InputSignal<boolean>;
3619
+ align: _angular_core.InputSignal<BkCellAlign | null>;
3620
+ /** Resolved sticky offsets, written after measurement. */
3621
+ protected readonly stickyLeft: _angular_core.WritableSignal<string | null>;
3622
+ protected readonly stickyRight: _angular_core.WritableSignal<string | null>;
3623
+ private resizeObserver?;
3624
+ get isSticky(): boolean;
3625
+ ngAfterViewInit(): void;
3626
+ ngOnDestroy(): void;
3627
+ /**
3628
+ * Resolves the sticky offsets.
3629
+ *
3630
+ * Offsets are summed from sibling `offsetWidth`s rather than read from
3631
+ * `getBoundingClientRect()`: once a cell is sticky its rect reflects where it
3632
+ * has been *held*, not where the column sits, so measuring a scrolled table
3633
+ * that way feeds each cell its own displacement back. `offsetWidth` is
3634
+ * unaffected by the sticky shift.
3635
+ */
3636
+ protected measure(): void;
3637
+ private sumWidths;
3638
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkCellBase, never>;
3639
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BkCellBase, never, never, { "left": { "alias": "left"; "required": false; "isSignal": true; }; "right": { "alias": "right"; "required": false; "isSignal": true; }; "ellipsis": { "alias": "ellipsis"; "required": false; "isSignal": true; }; "breakWord": { "alias": "breakWord"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3640
+ }
3641
+
3642
+ /**
3643
+ * Header cell.
3644
+ *
3645
+ * Carries a column's sort state, filter menu and select-all checkbox, and
3646
+ * registers itself with the parent `bk-table` so the table's data pipeline can
3647
+ * read them. It renders whatever you project as the label:
3648
+ *
3649
+ * <th bk-th [sortFn]="byName" [filters]="statuses" [filterFn]="byStatus">
3650
+ * Name
3651
+ * </th>
3652
+ *
3653
+ * Use the native `colspan` / `rowspan` attributes for grouped headers — they
3654
+ * need no wrapper input.
3655
+ */
3656
+ declare class BkTh extends BkCellBase implements BkColumnRef, OnInit, OnDestroy {
3657
+ /** Optional: the parent table only exists when the th is inside one. */
3658
+ private readonly table;
3659
+ /** Identifies the column in `queryParams`. Required for server-side work. */
3660
+ columnKeyInput: _angular_core.InputSignal<string | null>;
3661
+ /**
3662
+ * Column width. Required in virtual-scroll mode, where the header is a
3663
+ * separate table and has no body cells to size itself against.
3664
+ */
3665
+ widthInput: _angular_core.InputSignal<string | null>;
3666
+ /** Plain-value views of the inputs, so the table can read them off BkColumnRef. */
3667
+ get columnKey(): string | null;
3668
+ get width(): string | null;
3669
+ /**
3670
+ * A comparator sorts client-side; `true` marks the column sortable but
3671
+ * leaves the sorting to the server.
3672
+ */
3673
+ sortFn: _angular_core.InputSignal<BkSortFn | null>;
3674
+ sortOrder: _angular_core.ModelSignal<BkSortOrder>;
3675
+ /** The cycle the header steps through on each click. */
3676
+ sortDirections: _angular_core.InputSignal<BkSortOrder[]>;
3677
+ /**
3678
+ * Ranks this column against other sorted columns — higher wins. Columns
3679
+ * without a priority sort in registration order after those with one.
3680
+ */
3681
+ sortPriorityInput: _angular_core.InputSignal<number | null>;
3682
+ /** Options for the filter menu. An empty list hides the funnel icon. */
3683
+ filters: _angular_core.InputSignal<BkFilterOption[]>;
3684
+ /** Predicate for client-side filtering, or `true` to defer to the server. */
3685
+ filterFn: _angular_core.InputSignal<BkFilterFn | null>;
3686
+ /** Multiple shows checkboxes; single shows radios and closes on pick. */
3687
+ filterMultiple: _angular_core.InputSignal<boolean>;
3688
+ checkbox: _angular_core.InputSignal<boolean>;
3689
+ checked: _angular_core.ModelSignal<boolean>;
3690
+ indeterminate: _angular_core.InputSignal<boolean>;
3691
+ disabled: _angular_core.InputSignal<boolean>;
3692
+ /**
3693
+ * Extra entries in the select-all dropdown ("Select odd rows", …). Providing
3694
+ * any adds a caret beside the checkbox.
3695
+ */
3696
+ selections: _angular_core.InputSignal<BkTableSelection[]>;
3697
+ /** Reserves the narrow width used by the expand-icon column. */
3698
+ showExpand: _angular_core.InputSignal<boolean>;
3699
+ readonly filterValue: _angular_core.WritableSignal<any[]>;
3700
+ /** Menu selection while the popover is open, before OK is pressed. */
3701
+ readonly pendingFilter: _angular_core.WritableSignal<any[]>;
3702
+ readonly showSort: _angular_core.Signal<boolean>;
3703
+ readonly showFilter: _angular_core.Signal<boolean>;
3704
+ readonly isFiltered: _angular_core.Signal<boolean>;
3705
+ /** Only comparators run in-process; `true` means the server sorts. */
3706
+ get clientSortFn(): ((a: any, b: any, order: BkSortOrder) => number) | null;
3707
+ get clientFilterFn(): ((selected: any[], item: any) => boolean) | null;
3708
+ get sortPriority(): number | null;
3709
+ ngOnInit(): void;
3710
+ ngOnDestroy(): void;
3711
+ /** Advances to the next order in `sortDirections`, wrapping at the end. */
3712
+ onSortClick(): void;
3713
+ clearSort(): void;
3714
+ isPending(value: any): boolean;
3715
+ /**
3716
+ * Single-select view of the pending selection, for the radio list. The
3717
+ * underlying state stays an array in both modes so the filter predicate has
3718
+ * one shape to handle.
3719
+ */
3720
+ get singleFilter(): any;
3721
+ set singleFilter(value: any);
3722
+ toggleFilterOption(value: any, selected: boolean): void;
3723
+ applyFilter(popover?: BkPopover): void;
3724
+ resetFilter(popover?: BkPopover): void;
3725
+ clearFilter(): void;
3726
+ /** Re-syncs the menu to the applied state so a cancelled edit doesn't stick. */
3727
+ onFilterMenuOpen(): void;
3728
+ onCheckedChange(value: boolean): void;
3729
+ runSelection(selection: BkTableSelection, popover?: BkPopover): void;
3730
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkTh, never>;
3731
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkTh, "th[bk-th]", never, { "columnKeyInput": { "alias": "columnKey"; "required": false; "isSignal": true; }; "widthInput": { "alias": "width"; "required": false; "isSignal": true; }; "sortFn": { "alias": "sortFn"; "required": false; "isSignal": true; }; "sortOrder": { "alias": "sortOrder"; "required": false; "isSignal": true; }; "sortDirections": { "alias": "sortDirections"; "required": false; "isSignal": true; }; "sortPriorityInput": { "alias": "sortPriority"; "required": false; "isSignal": true; }; "filters": { "alias": "filters"; "required": false; "isSignal": true; }; "filterFn": { "alias": "filterFn"; "required": false; "isSignal": true; }; "filterMultiple": { "alias": "filterMultiple"; "required": false; "isSignal": true; }; "checkbox": { "alias": "checkbox"; "required": false; "isSignal": true; }; "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "indeterminate": { "alias": "indeterminate"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "selections": { "alias": "selections"; "required": false; "isSignal": true; }; "showExpand": { "alias": "showExpand"; "required": false; "isSignal": true; }; }, { "sortOrder": "sortOrderChange"; "checked": "checkedChange"; }, never, ["*"], true, never>;
3732
+ }
3733
+
3734
+ /**
3735
+ * Body cell.
3736
+ *
3737
+ * Handles the parts of a cell the consumer shouldn't have to rebuild each
3738
+ * time — row checkbox, expand toggle, tree indentation, sticky positioning —
3739
+ * and projects everything else through untouched:
3740
+ *
3741
+ * <td bk-td [(checked)]="row.checked"></td>
3742
+ * <td bk-td [showExpand]="row.hasChildren" [(expand)]="row.expand"
3743
+ * [indentLevel]="row.level" [indentSize]="20">{{ row.name }}</td>
3744
+ *
3745
+ * Use the native `colspan` / `rowspan` attributes for spanning cells.
3746
+ */
3747
+ declare class BkTd extends BkCellBase implements AfterViewInit, OnDestroy {
3748
+ private readonly ngZone;
3749
+ private ellipsisResizeObserver?;
3750
+ /**
3751
+ * The tooltip's content, and the switch for whether it shows at all — see
3752
+ * `observeEllipsisTruncation()`. Bound in the template onto the innermost
3753
+ * `.bk-td-ellipsis-text` span wrapping the projected content, not the `td`
3754
+ * or `.bk-td-content`: both of those are wider than the actual (possibly
3755
+ * truncated) text, and the tooltip should point at what's actually there.
3756
+ */
3757
+ protected readonly truncatedTooltip: _angular_core.WritableSignal<string>;
3758
+ /** Forwarded to the content wrapper's `bkTooltip`. Override on a column whose default 'right' would run into its neighbour. */
3759
+ tooltipPosition: _angular_core.InputSignal<"left" | "right" | "top" | "bottom">;
3760
+ /** Only present when `ellipsis()` is true — see the `@else` branch in the template. */
3761
+ private ellipsisTextRef?;
3762
+ checkbox: _angular_core.InputSignal<boolean>;
3763
+ checked: _angular_core.ModelSignal<boolean>;
3764
+ disabled: _angular_core.InputSignal<boolean>;
3765
+ indeterminate: _angular_core.InputSignal<boolean>;
3766
+ /**
3767
+ * Renders the expand toggle. Set it false on leaf rows in a tree — the cell
3768
+ * still reserves the icon's width so labels stay aligned down the column.
3769
+ */
3770
+ showExpand: _angular_core.InputSignal<boolean>;
3771
+ expand: _angular_core.ModelSignal<boolean>;
3772
+ /**
3773
+ * Custom expand icon. The template receives the open state as `$implicit`:
3774
+ *
3775
+ * <ng-template #icon let-expanded>{{ expanded ? '−' : '+' }}</ng-template>
3776
+ */
3777
+ expandIcon: _angular_core.InputSignal<TemplateRef<{
3778
+ $implicit: boolean;
3779
+ }> | null>;
3780
+ /** Depth of this row in the tree. Multiplied by `indentSize`. */
3781
+ indentLevel: _angular_core.InputSignal<number>;
3782
+ /** Pixels of indent per level. */
3783
+ indentSize: _angular_core.InputSignal<number>;
3784
+ /**
3785
+ * Marks the column that carries the tree. Leaf rows in such a column get a
3786
+ * blank spacer where the toggle would be, so their labels line up with the
3787
+ * labels of siblings that do have children — including at depth 0, where
3788
+ * indentation alone gives nothing to align against.
3789
+ */
3790
+ treeCell: _angular_core.InputSignal<boolean>;
3791
+ /**
3792
+ * The projected content is block-level and should fill the cell — a nested
3793
+ * `bk-table`, most commonly — rather than the default shrink-to-fit inline
3794
+ * box that lets ordinary text/icon content size to itself and still honour
3795
+ * `align` via `text-align`. Turning this on trades that `align` behaviour
3796
+ * away for whatever the block content does with the full width itself.
3797
+ */
3798
+ block: _angular_core.InputSignal<boolean>;
3799
+ readonly indentPx: _angular_core.Signal<number>;
3800
+ /** A tree cell with no toggle of its own still reserves the toggle's width. */
3801
+ readonly isLeafSpacer: _angular_core.Signal<boolean>;
3802
+ onExpandClick(): void;
3803
+ onCheckedChange(value: boolean): void;
3804
+ ngAfterViewInit(): void;
3805
+ ngOnDestroy(): void;
3806
+ /**
3807
+ * Drives `truncatedTooltip` from measured truncation rather than the
3808
+ * content being present unconditionally: `bkTooltip`'s own emptiness check
3809
+ * already suppresses the tooltip when there's nothing to show, so setting
3810
+ * it only when the cell is genuinely clipped (`scrollWidth > clientWidth`)
3811
+ * is what makes it show "for the truncated item only" — an `ellipsis` cell
3812
+ * whose text happens to fit never gets a tooltip at all.
3813
+ *
3814
+ * Measures the inner `.bk-td-ellipsis-text` span, not the `td` — that span
3815
+ * is now the element that actually owns `overflow: hidden` (see
3816
+ * `.bk-td-ellipsis-text` in table.css, and why: the ellipsis marker needs
3817
+ * a plain, non-flex element to render on at all). Because it clips its own
3818
+ * overflow, that overflow never reaches the `td`'s own `scrollWidth`
3819
+ * anymore — measuring the `td` here would read `scrollWidth ===
3820
+ * clientWidth` even when the text is visibly cut off, since the span
3821
+ * absorbs it first.
3822
+ *
3823
+ * Measured on resize, not on hover: `bkTooltip`'s own `mouseenter` listener
3824
+ * fires independently of this, so content has to already be correct
3825
+ * *before* any hover — computing it reactively to the hover would risk
3826
+ * showing whatever was last set instead of what's true at that moment.
3827
+ */
3828
+ private observeEllipsisTruncation;
3829
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkTd, never>;
3830
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkTd, "td[bk-td]", never, { "tooltipPosition": { "alias": "tooltipPosition"; "required": false; "isSignal": true; }; "checkbox": { "alias": "checkbox"; "required": false; "isSignal": true; }; "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "indeterminate": { "alias": "indeterminate"; "required": false; "isSignal": true; }; "showExpand": { "alias": "showExpand"; "required": false; "isSignal": true; }; "expand": { "alias": "expand"; "required": false; "isSignal": true; }; "expandIcon": { "alias": "expandIcon"; "required": false; "isSignal": true; }; "indentLevel": { "alias": "indentLevel"; "required": false; "isSignal": true; }; "indentSize": { "alias": "indentSize"; "required": false; "isSignal": true; }; "treeCell": { "alias": "treeCell"; "required": false; "isSignal": true; }; "block": { "alias": "block"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; "expand": "expandChange"; }, never, ["*"], true, never>;
3831
+ }
3832
+
3833
+ /**
3834
+ * A detail row that appears beneath its parent when expanded.
3835
+ *
3836
+ * <tr [bkExpand]="row.expand">
3837
+ * <td colspan="5">…anything…</td>
3838
+ * </tr>
3839
+ *
3840
+ * The row is hidden rather than destroyed, so state inside it (a scrolled
3841
+ * nested table, a half-filled form) survives being collapsed and reopened.
3842
+ * Wrap it in `@if` instead when the content is expensive enough that keeping
3843
+ * it alive costs more than rebuilding it.
3844
+ */
3845
+ declare class BkTrExpand {
3846
+ bkExpand: _angular_core.InputSignal<boolean>;
3847
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkTrExpand, never>;
3848
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BkTrExpand, "tr[bkExpand]", never, { "bkExpand": { "alias": "bkExpand"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3849
+ }
3850
+ /**
3851
+ * Summary / totals rows, placed in a `<tfoot>`.
3852
+ *
3853
+ * <tfoot bkTableSummary [fixed]="true">
3854
+ * <tr><td bk-td>Total</td><td bk-td>{{ total }}</td></tr>
3855
+ * </tfoot>
3856
+ *
3857
+ * `fixed` pins the footer to the bottom of the scroll container so totals stay
3858
+ * visible while the body scrolls — the counterpart to the fixed header.
3859
+ */
3860
+ declare class BkTableSummary {
3861
+ fixed: _angular_core.InputSignal<boolean>;
3862
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkTableSummary, never>;
3863
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BkTableSummary, "tfoot[bkTableSummary]", never, { "fixed": { "alias": "fixed"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3864
+ }
3865
+
3866
+ /**
3867
+ * Makes CDK row dragging behave inside a `<table>`.
3868
+ *
3869
+ * Two problems the CDK doesn't solve on its own, both carried over from
3870
+ * `bk-grid` where they were already working:
3871
+ *
3872
+ * 1. The drag preview is a detached copy of the `<tr>`, so it loses the column
3873
+ * widths the table gave it and collapses to its content. Widths are copied
3874
+ * across at drag start — from the *placeholder*, not the dragged row: by
3875
+ * the time `cdkDragStarted` fires, the CDK has already swapped the row out
3876
+ * for the placeholder and moved the row itself out to `<body>`, so the row
3877
+ * is no longer sitting inside the table and no longer reports the table's
3878
+ * widths. The placeholder is a clone left in the row's place, still inside
3879
+ * the table, so it's the one still telling the truth.
3880
+ * 2. Dragging to the edge of a scrolling table doesn't scroll it, so rows below
3881
+ * the fold are unreachable. Nearing an edge scrolls proportionally to how
3882
+ * close the pointer is.
3883
+ *
3884
+ * <div class="wrap" #d="bkTableDrag" bkTableDrag>
3885
+ * <bk-table>
3886
+ * <tbody cdkDropList (cdkDropListDropped)="drop($event)">
3887
+ * @for (row of rows; track row.id) {
3888
+ * <tr cdkDrag cdkDragLockAxis="y"
3889
+ * (cdkDragStarted)="d.onStart($event)"
3890
+ * (cdkDragMoved)="d.onMove($event)">
3891
+ *
3892
+ * The scroll container is resolved from the host, so put this on the element
3893
+ * that actually scrolls (or any ancestor of it).
3894
+ */
3895
+ declare class BkTableDrag {
3896
+ private readonly el;
3897
+ /** Distance from an edge, in px, at which auto-scroll kicks in. */
3898
+ private readonly edgeThreshold;
3899
+ /** Fastest scroll step per move event, in px. */
3900
+ private readonly maxScrollSpeed;
3901
+ onStart(event: CdkDragStart): void;
3902
+ onMove(event: CdkDragMove): void;
3903
+ /**
3904
+ * The nearest descendant that actually scrolls. `bk-table` puts the overflow
3905
+ * on an inner container, so the host itself usually isn't the scroller.
3906
+ * Protected so `BkTreeDrag` can position its drop indicator inside it.
3907
+ */
3908
+ protected scrollContainer(): HTMLElement | null;
3909
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkTableDrag, never>;
3910
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BkTableDrag, "[bkTableDrag]", ["bkTableDrag"], {}, {}, never, never, true, never>;
3911
+ }
3912
+
3913
+ /**
3914
+ * The grip a row is dragged by — a reusable handle instead of a glyph copied
3915
+ * into every drag-sort table.
3916
+ *
3917
+ * `hostDirectives: [CdkDragHandle]` puts `cdkDragHandle` on the component's
3918
+ * own host element, so it registers as the drag origin the moment it appears
3919
+ * anywhere inside a `cdkDrag` row — no need to add the CDK directive by hand,
3920
+ * and no requirement about *where* in the row it sits:
3921
+ *
3922
+ * <!-- its own column -->
3923
+ * <td bk-td><bk-drag-handle></bk-drag-handle></td>
3924
+ * <td bk-td>{{ row.name }}</td>
3925
+ *
3926
+ * <!-- inline with the first column, no dedicated column at all -->
3927
+ * <td bk-td><bk-drag-handle></bk-drag-handle> {{ row.name }}</td>
3928
+ *
3929
+ * The icon is a default grip — swap it per table by passing a template:
3930
+ *
3931
+ * <ng-template #bars><svg>…</svg></ng-template>
3932
+ * <bk-drag-handle [icon]="bars"></bk-drag-handle>
3933
+ *
3934
+ * Same shape as `expandIcon` on `td[bk-td]`: a `TemplateRef` input the
3935
+ * component falls back from when it's `null`, rather than a projected slot —
3936
+ * `<ng-content>` has no way to render a default only when nothing was
3937
+ * projected into it.
3938
+ */
3939
+ declare class BkDragHandle {
3940
+ icon: _angular_core.InputSignal<TemplateRef<void> | null>;
3941
+ ariaLabel: _angular_core.InputSignal<string>;
3942
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkDragHandle, never>;
3943
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkDragHandle, "bk-drag-handle", ["bkDragHandle"], { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof i1$3.CdkDragHandle; inputs: {}; outputs: {}; }]>;
3944
+ }
3945
+
3946
+ /**
3947
+ * How far a drag is allowed to travel through the tree.
3948
+ *
3949
+ * - `'anywhere'` — full re-parenting: before, after or inside any legal target
3950
+ * at any depth. Right for a generic outliner or file explorer, where the
3951
+ * levels carry no meaning of their own.
3952
+ * - `'siblings'` — reorder only, among the dragged node's current siblings.
3953
+ * No re-parenting, no nesting. Right whenever the levels *do* mean
3954
+ * something — an org chart, a category tree, a fixed-depth menu — where an
3955
+ * unrestricted drag lets you build a tree that violates whatever the levels
3956
+ * were supposed to represent. The cycle guard stops a node from swallowing
3957
+ * itself either way, but nothing else stops a leaf from ending up three
3958
+ * levels deep under an unrelated parent unless this is set.
3959
+ */
3960
+ type BkTreeDragScope = 'anywhere' | 'siblings';
3961
+ /**
3962
+ * Tree-aware row dragging: before / after / inside.
3963
+ *
3964
+ * A table flattens the tree into one list of `<tr>` — every row is a sibling
3965
+ * in the same `<tbody>` whatever its depth, so nested `cdkDropList`s have
3966
+ * nothing to nest on. Instead the CDK is kept for what it is good at (the
3967
+ * preview, the placeholder, returning the row on release) with its sorting
3968
+ * turned off, and this directive does the targeting: the pointer's position
3969
+ * within the row under it picks the relationship —
3970
+ *
3971
+ * top third insert BEFORE the target, as a sibling
3972
+ * middle third drop INSIDE the target, as its child
3973
+ * bottom third insert AFTER the target, as a sibling
3974
+ *
3975
+ * — drawn live as a line above, a line below, or a tinted row, so the drop
3976
+ * never lands somewhere the indicator didn't say it would. Illegal targets
3977
+ * (the dragged row itself, anything in its own subtree, anything the `canDrop`
3978
+ * predicate vetoes) simply show no indicator.
3979
+ *
3980
+ * Set `scope="siblings"` to restrict dragging to reordering among the node's
3981
+ * current siblings — no re-parenting, no nesting, and no `inside` zone at all
3982
+ * (see `BkTreeDragScope`). That is the right default whenever the tree's
3983
+ * levels mean something specific; `canDrop` covers anything more particular
3984
+ * than the two built-in scopes.
3985
+ *
3986
+ * The directive never touches the tree. It resolves the drag into one event —
3987
+ * `{ dragged, target, position }` — and the consumer applies it, usually with
3988
+ * `moveBkTreeNode`. Hovering the middle of a row for `expandDelay` emits
3989
+ * `treeExpand` so collapsed branches can be opened mid-drag; acting on it is
3990
+ * likewise the consumer's call.
3991
+ *
3992
+ * <div bkTreeDrag #td="bkTreeDrag" [treeRows]="t.renderData()"
3993
+ * (treeDrop)="onDrop($event)" (treeExpand)="open($event)">
3994
+ * <bk-table #t [data]="rows()">
3995
+ * <tbody cdkDropList [cdkDropListSortingDisabled]="true">
3996
+ * @for (row of t.renderData(); track row.data.id) {
3997
+ * <tr cdkDrag cdkDragLockAxis="y"
3998
+ * (cdkDragStarted)="td.onDragStart($event, row)"
3999
+ * (cdkDragMoved)="td.onDragMove($event)"
4000
+ * (cdkDragEnded)="td.onDragEnd()">
4001
+ *
4002
+ * Sorting stays disabled on the drop list deliberately: with rows shuffling
4003
+ * under the pointer, an open branch's children sit still while their parent
4004
+ * moves. With a static list and an indicator, nothing lies.
4005
+ *
4006
+ * Extends `BkTableDrag`, so the preview keeps its column widths and dragging
4007
+ * near an edge still scrolls the table.
4008
+ */
4009
+ declare class BkTreeDrag<T = any> extends BkTableDrag implements OnDestroy {
4010
+ /**
4011
+ * The flattened rows currently rendered, in render order — the same array
4012
+ * the `<tbody>` iterates. Row *i* of the tbody is entry *i* here; that
4013
+ * index mapping is the whole basis of the hit-testing.
4014
+ */
4015
+ treeRows: _angular_core.InputSignal<readonly BkTreeRow<T>[]>;
4016
+ /** See `BkTreeDragScope`. Defaults to full re-parenting. */
4017
+ scope: _angular_core.InputSignal<BkTreeDragScope>;
4018
+ /**
4019
+ * Consumer veto over and above the built-in guards — e.g. "a person cannot
4020
+ * contain a team". Return false and the indicator never appears.
4021
+ */
4022
+ canDrop: _angular_core.InputSignal<((dragged: BkTreeRow<T>, target: BkTreeRow<T>, position: BkTreeDropPosition) => boolean) | null>;
4023
+ /** How long the pointer rests on a row's middle before `treeExpand` fires. */
4024
+ expandDelay: _angular_core.InputSignal<number>;
4025
+ treeDrop: _angular_core.OutputEmitterRef<BkTreeDropEvent<T>>;
4026
+ treeExpand: _angular_core.OutputEmitterRef<BkTreeRow<T>>;
4027
+ private dragged;
4028
+ private target;
4029
+ private position;
4030
+ private line;
4031
+ private insideRow;
4032
+ private expandTimer;
4033
+ private expandFor;
4034
+ onDragStart(event: CdkDragStart, row: BkTreeRow<T>): void;
4035
+ onDragMove(event: CdkDragMove): void;
4036
+ onDragEnd(): void;
4037
+ ngOnDestroy(): void;
4038
+ /**
4039
+ * Structural legality, before the zone is even known. Dropping a node
4040
+ * anywhere inside its own subtree would detach that branch from the tree
4041
+ * with nothing left pointing at it — walking the parent chain is cheaper
4042
+ * than searching the subtree, and `flattenBkTreeData` already carries it.
4043
+ *
4044
+ * In `'siblings'` scope, the only legal targets are the dragged node's
4045
+ * current siblings — same `parent` reference, root-level nodes included
4046
+ * (`undefined === undefined`). That alone also rules out every cycle: a
4047
+ * node's siblings can never be its own ancestor.
4048
+ */
4049
+ private isLegal;
4050
+ private canDropAt;
4051
+ private setTarget;
4052
+ private drawLine;
4053
+ private clearIndicator;
4054
+ /**
4055
+ * One timer per hovered node: moving within the same row keeps it running,
4056
+ * moving to another row restarts it. The directive only reports the dwell —
4057
+ * whether the node has children to show is the consumer's knowledge.
4058
+ */
4059
+ private armExpandTimer;
4060
+ private clearExpandTimer;
4061
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkTreeDrag<any>, never>;
4062
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BkTreeDrag<any>, "[bkTreeDrag]", ["bkTreeDrag"], { "treeRows": { "alias": "treeRows"; "required": false; "isSignal": true; }; "scope": { "alias": "scope"; "required": false; "isSignal": true; }; "canDrop": { "alias": "canDrop"; "required": false; "isSignal": true; }; "expandDelay": { "alias": "expandDelay"; "required": false; "isSignal": true; }; }, { "treeDrop": "treeDrop"; "treeExpand": "treeExpand"; }, never, never, true, never>;
4063
+ }
4064
+
4065
+ /**
4066
+ * bk-table public surface.
4067
+ *
4068
+ * `BK_TABLE` is the convenience bundle — spread it into a standalone
4069
+ * component's `imports` rather than listing eight symbols by hand:
4070
+ *
4071
+ * imports: [...BK_TABLE]
4072
+ */
4073
+
4074
+ declare const BK_TABLE: readonly [typeof BkTable, typeof BkTableTitle, typeof BkTableFooter, typeof BkVirtualScroll, typeof BkTh, typeof BkTd, typeof BkTrExpand, typeof BkTableSummary, typeof BkTableDrag, typeof BkDragHandle, typeof BkTreeDrag];
4075
+
4076
+ export { BKTooltipDirective, BK_DEFAULT_DIALOG_CONFIG, BK_DIALOG_DATA, BK_DIALOG_GLOBAL_CONFIG, BK_TABLE, BkAvatar, BkAvatarGroup, BkAvatarUploader, BkBadge, BkButton, BkButtonGroup, BkCalendarManagerService, BkCheckbox, BkColumnFilterService, BkColumnSelect, BkCustomCalendar, BkDialogActions, BkDialogClose, BkDialogContent, BkDialogModule, BkDialogRef, BkDialogService, BkDialogTitle, BkDragHandle, BkDropdown, BkFileCard, BkFilePicker, BkGrid, BkHierarchicalSelect, BkIconButton, BkInput, BkInputChips, BkLoader, BkMenu, BkPagination, BkPill, BkPopover, BkRadioButton, BkScheduledDatePicker, BkSelect, BkSpinner, BkTable, BkTableDrag, BkTableFooter, BkTableSummary, BkTableTitle, BkTabs, BkTd, BkTextarea, BkTh, BkTimePicker, BkToastr, BkToastrService, BkToggle, BkTrExpand, BkTreeDrag, BkValidator, BkVirtualScroll, BrickclayIcons, BrickclayLib, CalendarModule, CalendarSelection, ColumnFilterOption, NEUTRAL_APPEARANCE, POPOVER_PLACEMENTS, computePopoverPosition, containsBkTreeNode, deriveAppearance, flattenBkTreeData, getDialogBackdropAnimation, getDialogPanelAnimation, joinPlacement, moveBkTreeNode, normalizeBkTableSize, parseColor, splitPlacement };
4077
+ export type { AvatarFallback, AvatarGroupItem, AvatarSize, AvatarVariant, BadgeColor, BadgeSize, BadgeVariant, BkAnimationKeyframes, BkAvatarFallback, BkAvatarSize, BkCellAlign, BkColumnRef, BkColumnSelectPosition, BkDialogAnimation, BkDialogConfig, BkDialogPosition, BkFilterFn, BkFilterOption, BkInputAutoCapitalize, BkInputAutoComplete, BkInputMode, BkInputSize, BkInputType, BkLoaderVariant, BkPageSize, BkSortDirection, BkSortFn, BkSortOrder, BkTableQueryParams, BkTableScroll, BkTableSelection, BkTableSize, BkTableSizeToken, BkTextAreaAutoCapitalize, BkTextAreaAutoComplete, BkTextAreaInputMode, BkTreeDragScope, BkTreeDropEvent, BkTreeDropPosition, BkTreeRow, ButtonSize, ButtonVariant, CalendarRange, ColorAppearance, ColorFill, CountryOption, CustomRangesConfig, DotPosition, DotStatus, DropdownItem, DropdownPlacement, DropdownSize, DropdownTrigger, DropdownVariant, FileState, GroupItem, GroupMode, HierarchicalNode, IconButtonSize, IconButtonVariant, IconOrientation, MenuItem, MenuOrientation, MenuPopupSide, MenuSize, MenuSubmenuMode, MenuTrigger, PillColor, PillSize, PillVariant, PopoverAlign, PopoverPlacement, PopoverPosition, PopoverPositionOptions, PopoverSide, Rect, ScheduledDateSelection, Size, SortDirection, SpinnerSize, TabIconDirection, TabItem, TableAction, TableBadge, TableColumn, TableIcon, TabsColors, TabsOrientation, TabsVariant, TimeConfiguration, ToastConfig, ToastMessage, ToastMethodOptions, ToastPosition, ToastSeverity };