@brickclay-org/ui 0.1.85 → 0.1.86

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
@@ -6,19 +6,20 @@ import { Observable } from 'rxjs';
6
6
  import * as i1 from '@angular/common';
7
7
  import * as i1$3 from '@angular/cdk/drag-drop';
8
8
  import { CdkDragDrop, CdkDragMove, CdkDragStart } from '@angular/cdk/drag-drop';
9
+ import * as i2 from '@angular/cdk/overlay';
10
+ import { CdkConnectedOverlay, ConnectedOverlayPositionChange, ConnectedPosition, ScrollStrategy } from '@angular/cdk/overlay';
9
11
  import { NgxMaskDirective } from 'ngx-mask';
10
12
  import * as i1$2 from '@angular/cdk/dialog';
11
13
  import { CdkDialogContainer, DialogRef } from '@angular/cdk/dialog';
12
- import * as i2 from '@angular/cdk/overlay';
13
- import { ScrollStrategy } from '@angular/cdk/overlay';
14
14
  import * as i3 from '@angular/cdk/portal';
15
15
  import { ComponentPortal } from '@angular/cdk/portal';
16
16
  import * as i1$1 from '@angular/cdk/scrolling';
17
- import * as _brickclay_org_ui from '@brickclay-org/ui';
18
17
 
19
18
  declare const BrickclayIcons: {
20
19
  readonly arrowleft: "assets/icons/chevron-left.svg";
21
20
  readonly arrowRight: "assets/icons/chevron-right.svg";
21
+ readonly arrowLeftDouble: "assets/icons/double-chevron-left.svg";
22
+ readonly arrowRightDouble: "assets/icons/double-chevron-right.svg";
22
23
  readonly calenderIcon: "assets/icons/custom-calender.svg";
23
24
  readonly timerIcon: "assets/icons/timer.svg";
24
25
  };
@@ -171,6 +172,8 @@ declare class BkCustomCalendar implements OnInit, OnDestroy, OnChanges, ControlV
171
172
  brickclayIcons: {
172
173
  readonly arrowleft: "assets/icons/chevron-left.svg";
173
174
  readonly arrowRight: "assets/icons/chevron-right.svg";
175
+ readonly arrowLeftDouble: "assets/icons/double-chevron-left.svg";
176
+ readonly arrowRightDouble: "assets/icons/double-chevron-right.svg";
174
177
  readonly calenderIcon: "assets/icons/custom-calender.svg";
175
178
  readonly timerIcon: "assets/icons/timer.svg";
176
179
  };
@@ -562,6 +565,8 @@ declare class BkTimePicker implements OnInit, OnChanges, OnDestroy, AfterViewIni
562
565
  brickclayIcons: {
563
566
  readonly arrowleft: "assets/icons/chevron-left.svg";
564
567
  readonly arrowRight: "assets/icons/chevron-right.svg";
568
+ readonly arrowLeftDouble: "assets/icons/double-chevron-left.svg";
569
+ readonly arrowRightDouble: "assets/icons/double-chevron-right.svg";
565
570
  readonly calenderIcon: "assets/icons/custom-calender.svg";
566
571
  readonly timerIcon: "assets/icons/timer.svg";
567
572
  };
@@ -954,6 +959,8 @@ declare class BkGrid<T = any> {
954
959
  noRecordFoundHeight: string;
955
960
  /** Custom illustration for the empty state. Falls back to a built-in SVG when omitted. */
956
961
  noRecordImgUrl?: string;
962
+ /** Show the empty-state illustration above the message. Set false to show the message only. */
963
+ showNoRecordImg: boolean;
957
964
  noRecordMessage: string;
958
965
  /** Static row class or a function that returns a class per row. Row data may also include `class` via TableRows. */
959
966
  rows?: string | ((row: T) => string | undefined);
@@ -994,7 +1001,7 @@ declare class BkGrid<T = any> {
994
1001
  onDragStart(event: CdkDragStart<any>): void;
995
1002
  get noRecordImagePath(): string;
996
1003
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkGrid<any>, never>;
997
- 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; }; "rows": { "alias": "rows"; "required": false; }; }, { "change": "change"; "actionClick": "actionClick"; "sortChange": "sortChange"; "dragDropChange": "dragDropChange"; }, never, never, true, never>;
1004
+ 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; }; "showNoRecordImg": { "alias": "showNoRecordImg"; "required": false; }; "noRecordMessage": { "alias": "noRecordMessage"; "required": false; }; "rows": { "alias": "rows"; "required": false; }; }, { "change": "change"; "actionClick": "actionClick"; "sortChange": "sortChange"; "dragDropChange": "dragDropChange"; }, never, never, true, never>;
998
1005
  }
999
1006
 
1000
1007
  /** How an accent colour is applied to a control. */
@@ -1060,7 +1067,7 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
1060
1067
  dropdownView: _angular_core.InputSignal<"list" | "grid">;
1061
1068
  gridColumns: _angular_core.InputSignal<BkSelectGridColumn[]>;
1062
1069
  /** Grid density. `inherit` follows the trigger's existing `variation`. */
1063
- gridVariation: _angular_core.InputSignal<"sm" | "default" | "inherit">;
1070
+ gridVariation: _angular_core.InputSignal<"inherit" | "default" | "sm">;
1064
1071
  /** Stage multi-grid changes until Apply is clicked. */
1065
1072
  gridSelectionActions: _angular_core.InputSignal<boolean>;
1066
1073
  gridMinWidth: _angular_core.InputSignal<string>;
@@ -1110,9 +1117,21 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
1110
1117
  disabled: _angular_core.ModelSignal<boolean>;
1111
1118
  loading: _angular_core.InputSignal<boolean>;
1112
1119
  closeOnSelect: _angular_core.InputSignal<boolean>;
1120
+ /**
1121
+ * When on, the dropdown opens as soon as the control receives keyboard focus
1122
+ * (e.g. tabbing to it) and closes once focus leaves it. Off by default so the
1123
+ * existing click/Enter-to-open behaviour is unchanged.
1124
+ */
1125
+ openOnFocus: _angular_core.InputSignal<boolean>;
1113
1126
  dropdownPosition: _angular_core.InputSignal<"top" | "bottom">;
1114
1127
  hasError: boolean;
1115
1128
  errorMessage: string;
1129
+ /**
1130
+ * @deprecated No-op, kept only so existing `[appendToBody]="true"` bindings don't break.
1131
+ * The dropdown now always positions via Angular CDK Overlay, which portals into the shared
1132
+ * `cdk-overlay-container` unconditionally — the exact clipping/stacking escape this input used
1133
+ * to opt into by hand is now the only behaviour there is. Safe to remove from call sites.
1134
+ */
1116
1135
  appendToBody: _angular_core.InputSignal<boolean>;
1117
1136
  open: _angular_core.OutputEmitterRef<void>;
1118
1137
  close: _angular_core.OutputEmitterRef<void>;
@@ -1130,6 +1149,7 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
1130
1149
  optionsRef: QueryList<ElementRef>;
1131
1150
  controlWrapper: ElementRef<HTMLDivElement>;
1132
1151
  dropdownPanel?: ElementRef<HTMLDivElement>;
1152
+ selectOverlay?: CdkConnectedOverlay;
1133
1153
  chipsViewport?: ElementRef<HTMLDivElement>;
1134
1154
  measureChips?: QueryList<ElementRef<HTMLElement>>;
1135
1155
  measurePlus?: ElementRef<HTMLElement>;
@@ -1143,17 +1163,15 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
1143
1163
  searchTerm: _angular_core.WritableSignal<string>;
1144
1164
  markedIndex: _angular_core.WritableSignal<number>;
1145
1165
  placement: _angular_core.WritableSignal<"top" | "bottom">;
1146
- private originalPanelParent;
1147
- private originalPanelAnchor;
1148
- private panelInBody;
1149
1166
  visibleCount: _angular_core.WritableSignal<number>;
1150
1167
  private resizeObserver?;
1151
- dropdownStyle: _angular_core.WritableSignal<{
1152
- top?: string;
1153
- bottom?: string;
1154
- left: string;
1155
- width: string;
1156
- }>;
1168
+ /**
1169
+ * Panel width, kept equal to the control's — matches the old `dropdownStyle().width`
1170
+ * behaviour. Set on open and re-measured by the same ResizeObserver that already tracks
1171
+ * the control for chip recomputation, so a responsive layout keeps the panel in sync while
1172
+ * it's open.
1173
+ */
1174
+ dropdownWidth: _angular_core.WritableSignal<number | null>;
1157
1175
  filteredItems: _angular_core.Signal<any[]>;
1158
1176
  /**
1159
1177
  * True when the user is allowed to change the selection. Both disabled and
@@ -1164,25 +1182,23 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
1164
1182
  constructor();
1165
1183
  ngAfterViewInit(): void;
1166
1184
  ngOnDestroy(): void;
1167
- private reposition;
1168
- /**
1169
- * True when the control is fully outside the visible area — either the
1170
- * viewport or a scrollable/clipping ancestor (e.g. a dialog's scroll body).
1171
- * Used to close the floating (append-to-body) panel once its anchor is no
1172
- * longer on screen, instead of leaving it hanging.
1173
- */
1174
- private isControlClipped;
1175
- /**
1176
- * Relocate the panel node to <body> so it truly escapes any transformed or
1177
- * overflow-clipped ancestor (dialogs, drawers, cards with overflow, etc.).
1178
- * Setting position:fixed alone is not enough when an ancestor has a transform.
1179
- */
1180
- private movePanelToBody;
1181
1185
  /**
1182
- * Return the panel to its original DOM position so Angular's @if can remove
1183
- * it cleanly on close. Must run before isOpen is set to false.
1184
- */
1185
- private restorePanel;
1186
+ * CDK's default `reposition` scroll strategy only reacts to real `document`/`window` scroll
1187
+ * it has no way to know an app shell might scroll a nested container instead (this one
1188
+ * commonly does — dashboard layouts with a fixed header/sidebar and a scrollable content pane).
1189
+ * A capture-phase listener on `document` still sees scroll events fired on any descendant
1190
+ * scrollable element (scroll doesn't bubble, but capture does) — same trick `bk-custom-calendar`
1191
+ * and the `bk-input` phone dropdown already use. rAF-throttled so a fast scroll doesn't force
1192
+ * layout on every tick.
1193
+ */
1194
+ private overlayScrollRafId;
1195
+ private readonly onOverlayScroll;
1196
+ private attachOverlayScrollTracking;
1197
+ private detachOverlayScrollTracking;
1198
+ /** Derives `placement` (for the `[data-position="top"]` CSS margin tweak) from which of
1199
+ * `selectDropdownPositions` CDK actually applied — replaces the old hand-rolled space
1200
+ * comparison now that the fit/flip decision itself lives in CDK. */
1201
+ onPositionChange(event: ConnectedOverlayPositionChange): void;
1186
1202
  private scheduleRecompute;
1187
1203
  /**
1188
1204
  * Measures the off-screen chip row and works out how many chips fit on a
@@ -1207,15 +1223,12 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
1207
1223
  toggleDropdown(event: Event | null): void;
1208
1224
  openDropdown(): void;
1209
1225
  closeDropdown(): void;
1210
- getTop(): string | null;
1211
- getBottom(): string | null;
1212
1226
  /**
1213
- * Decides which side the panel should open on (auto-flip) and, for
1214
- * append-to-body mode, computes the fixed coordinates. Shared by both modes
1215
- * and re-run on open and on every scroll/resize so the panel snaps to
1216
- * whichever side has room — honouring `dropdownPosition` when it fits.
1227
+ * Preferred position list handed to CDK, in try-order — honours `dropdownPosition` first,
1228
+ * falling back to the other side only when the preferred one genuinely doesn't fit. Replaces
1229
+ * the old hand-rolled `applyPlacement()` space comparison entirely.
1217
1230
  */
1218
- applyPlacement(): void;
1231
+ get selectDropdownPositions(): ConnectedPosition[];
1219
1232
  toggleSelectAll(event: Event): void;
1220
1233
  handleSelection(item: any, event?: Event): void;
1221
1234
  handleGridSelection(item: any, event?: Event): void;
@@ -1225,6 +1238,29 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
1225
1238
  handleClear(event: Event): void;
1226
1239
  private updateModel;
1227
1240
  onSearchInput(event: Event): void;
1241
+ /**
1242
+ * Options in the exact order they are rendered (groups flattened). This is the
1243
+ * space Up/Down navigation and the marked-index operate in — using the flat
1244
+ * `filteredItems()` would desync from the on-screen order once grouping
1245
+ * reshuffles rows. For an ungrouped list this equals `filteredItems()`.
1246
+ */
1247
+ flatOptions: _angular_core.Signal<any[]>;
1248
+ /** The option the marker currently sits on, or null. */
1249
+ private markedOption;
1250
+ /**
1251
+ * Whether an option carries the keyboard/hover marker. Matched by identity
1252
+ * rather than by row position so two groups can't share a highlight just
1253
+ * because the marked item sits at the same index within each group.
1254
+ */
1255
+ isMarked(item: any): boolean;
1256
+ /** Move the marker to a hovered option by its position in the rendered order. */
1257
+ markOption(item: any): void;
1258
+ /**
1259
+ * Rendered-order index that Up/Down navigation starts from when the dropdown
1260
+ * opens: the first already-selected option, or the first option when nothing
1261
+ * is selected. Returns -1 only for an empty list.
1262
+ */
1263
+ private initialMarkedIndex;
1228
1264
  onKeyDown(event: KeyboardEvent): void;
1229
1265
  onTabPress(event: KeyboardEvent): void;
1230
1266
  onScroll(event: Event): void;
@@ -1237,7 +1273,27 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
1237
1273
  setDisabledState(d: boolean): void;
1238
1274
  private resolveSelectedOptions;
1239
1275
  private el;
1240
- onClickOutside(e: Event): void;
1276
+ onOverlayOutsideClick(): void;
1277
+ private pointerFocus;
1278
+ private opening;
1279
+ onPointerDown(): void;
1280
+ /**
1281
+ * Open when *keyboard* focus arrives from outside the control (tabbing to it).
1282
+ * Pointer-initiated focus is ignored — a click opens via its own toggle, so
1283
+ * this stays a keyboard affordance. Focus moves that originate inside the
1284
+ * control or its panel — the panel handing focus to the search field, or focus
1285
+ * falling back to the trigger as the panel closes — must not (re)open it,
1286
+ * which would otherwise trap the user when tabbing backwards out of it.
1287
+ */
1288
+ onFocusIn(event: FocusEvent): void;
1289
+ /**
1290
+ * Close when focus leaves the control entirely — tabbing away, or focus moving
1291
+ * to any element outside the control and its (possibly body-appended) panel.
1292
+ * Internal focus moves keep it open.
1293
+ */
1294
+ onFocusOut(): void;
1295
+ /** True when a node lives inside the control host or its detached panel. */
1296
+ private isFocusInside;
1241
1297
  openFromLabel(event: MouseEvent): void;
1242
1298
  groupedItems: _angular_core.Signal<{
1243
1299
  group: string;
@@ -1285,7 +1341,7 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
1285
1341
  getTooltipIfEllipsed(el: HTMLElement, text: string): string;
1286
1342
  onOptionHover(index: number): void;
1287
1343
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkSelect, never>;
1288
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkSelect, "bk-select", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "bindLabel": { "alias": "bindLabel"; "required": false; "isSignal": true; }; "bindValue": { "alias": "bindValue"; "required": false; "isSignal": true; }; "bindIcon": { "alias": "bindIcon"; "required": false; "isSignal": true; }; "isResponsive": { "alias": "isResponsive"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "notFoundText": { "alias": "notFoundText"; "required": false; "isSignal": true; }; "loadingText": { "alias": "loadingText"; "required": false; "isSignal": true; }; "clearAllText": { "alias": "clearAllText"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "colorKey": { "alias": "colorKey"; "required": false; "isSignal": true; }; "dropdownView": { "alias": "dropdownView"; "required": false; "isSignal": true; }; "gridColumns": { "alias": "gridColumns"; "required": false; "isSignal": true; }; "gridVariation": { "alias": "gridVariation"; "required": false; "isSignal": true; }; "gridSelectionActions": { "alias": "gridSelectionActions"; "required": false; "isSignal": true; }; "gridMinWidth": { "alias": "gridMinWidth"; "required": false; "isSignal": true; }; "gridMaxHeight": { "alias": "gridMaxHeight"; "required": false; "isSignal": true; }; "gridSelectedLabelKeys": { "alias": "gridSelectedLabelKeys"; "required": false; "isSignal": true; }; "gridSelectedLabelSeparator": { "alias": "gridSelectedLabelSeparator"; "required": false; "isSignal": true; }; "gridApplyText": { "alias": "gridApplyText"; "required": false; "isSignal": true; }; "gridClearText": { "alias": "gridClearText"; "required": false; "isSignal": true; }; "showDots": { "alias": "showDots"; "required": false; "isSignal": true; }; "showAvatar": { "alias": "showAvatar"; "required": false; "isSignal": true; }; "avatarKey": { "alias": "avatarKey"; "required": false; "isSignal": true; }; "iconAlt": { "alias": "iconAlt"; "required": false; }; "label": { "alias": "label"; "required": false; }; "required": { "alias": "required"; "required": false; }; "variation": { "alias": "variation"; "required": false; }; "iconSrc": { "alias": "iconSrc"; "required": false; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "maxLabels": { "alias": "maxLabels"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "allSelect": { "alias": "allSelect"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "closeOnSelect": { "alias": "closeOnSelect"; "required": false; "isSignal": true; }; "dropdownPosition": { "alias": "dropdownPosition"; "required": false; "isSignal": true; }; "hasError": { "alias": "hasError"; "required": false; }; "errorMessage": { "alias": "errorMessage"; "required": false; }; "appendToBody": { "alias": "appendToBody"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; }, { "disabled": "disabledChange"; "open": "open"; "close": "close"; "focus": "focus"; "blur": "blur"; "search": "search"; "clear": "clear"; "change": "change"; "scrollToEnd": "scrollToEnd"; }, never, never, true, never>;
1344
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkSelect, "bk-select", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "bindLabel": { "alias": "bindLabel"; "required": false; "isSignal": true; }; "bindValue": { "alias": "bindValue"; "required": false; "isSignal": true; }; "bindIcon": { "alias": "bindIcon"; "required": false; "isSignal": true; }; "isResponsive": { "alias": "isResponsive"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "notFoundText": { "alias": "notFoundText"; "required": false; "isSignal": true; }; "loadingText": { "alias": "loadingText"; "required": false; "isSignal": true; }; "clearAllText": { "alias": "clearAllText"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "colorKey": { "alias": "colorKey"; "required": false; "isSignal": true; }; "dropdownView": { "alias": "dropdownView"; "required": false; "isSignal": true; }; "gridColumns": { "alias": "gridColumns"; "required": false; "isSignal": true; }; "gridVariation": { "alias": "gridVariation"; "required": false; "isSignal": true; }; "gridSelectionActions": { "alias": "gridSelectionActions"; "required": false; "isSignal": true; }; "gridMinWidth": { "alias": "gridMinWidth"; "required": false; "isSignal": true; }; "gridMaxHeight": { "alias": "gridMaxHeight"; "required": false; "isSignal": true; }; "gridSelectedLabelKeys": { "alias": "gridSelectedLabelKeys"; "required": false; "isSignal": true; }; "gridSelectedLabelSeparator": { "alias": "gridSelectedLabelSeparator"; "required": false; "isSignal": true; }; "gridApplyText": { "alias": "gridApplyText"; "required": false; "isSignal": true; }; "gridClearText": { "alias": "gridClearText"; "required": false; "isSignal": true; }; "showDots": { "alias": "showDots"; "required": false; "isSignal": true; }; "showAvatar": { "alias": "showAvatar"; "required": false; "isSignal": true; }; "avatarKey": { "alias": "avatarKey"; "required": false; "isSignal": true; }; "iconAlt": { "alias": "iconAlt"; "required": false; }; "label": { "alias": "label"; "required": false; }; "required": { "alias": "required"; "required": false; }; "variation": { "alias": "variation"; "required": false; }; "iconSrc": { "alias": "iconSrc"; "required": false; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "maxLabels": { "alias": "maxLabels"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "allSelect": { "alias": "allSelect"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "closeOnSelect": { "alias": "closeOnSelect"; "required": false; "isSignal": true; }; "openOnFocus": { "alias": "openOnFocus"; "required": false; "isSignal": true; }; "dropdownPosition": { "alias": "dropdownPosition"; "required": false; "isSignal": true; }; "hasError": { "alias": "hasError"; "required": false; }; "errorMessage": { "alias": "errorMessage"; "required": false; }; "appendToBody": { "alias": "appendToBody"; "required": false; "isSignal": true; }; "compareWith": { "alias": "compareWith"; "required": false; "isSignal": true; }; }, { "disabled": "disabledChange"; "open": "open"; "close": "close"; "focus": "focus"; "blur": "blur"; "search": "search"; "clear": "clear"; "change": "change"; "scrollToEnd": "scrollToEnd"; }, never, never, true, never>;
1289
1345
  }
1290
1346
 
1291
1347
  type BkInputType = 'text' | 'email' | 'password' | 'number' | 'url' | 'tel';
@@ -1301,6 +1357,8 @@ interface CountryOption {
1301
1357
  prefix: string;
1302
1358
  placeholder: string;
1303
1359
  }
1360
+ /** Default country list used when a consumer does not provide their own via `[countryOptions]`. */
1361
+ declare const DEFAULT_COUNTRY_OPTIONS: CountryOption[];
1304
1362
  declare class BkInput implements OnInit, OnDestroy, AfterViewInit, ControlValueAccessor {
1305
1363
  id: string;
1306
1364
  name: string;
@@ -1331,7 +1389,14 @@ declare class BkInput implements OnInit, OnDestroy, AfterViewInit, ControlValueA
1331
1389
  currencyDecimals: number;
1332
1390
  allowNegative: boolean;
1333
1391
  countryCode: string;
1334
- countryOptions: CountryOption[];
1392
+ /**
1393
+ * Consumer-supplied country list. When a non-empty list is provided it replaces the
1394
+ * built-in list; when omitted (or given a null/empty value) the DEFAULT_COUNTRY_OPTIONS
1395
+ * are used so the selector always has values to show.
1396
+ */
1397
+ private _countryOptions;
1398
+ set countryOptions(value: CountryOption[] | null | undefined);
1399
+ get countryOptions(): CountryOption[];
1335
1400
  selectedCountry: CountryOption;
1336
1401
  iconOrientation: IconOrientation;
1337
1402
  password: boolean;
@@ -1342,15 +1407,22 @@ declare class BkInput implements OnInit, OnDestroy, AfterViewInit, ControlValueA
1342
1407
  step: number | null;
1343
1408
  maxlength: number | null;
1344
1409
  minlength: number | null;
1345
- dropdownRef?: ElementRef;
1346
- selectRef?: ElementRef;
1347
1410
  inputField?: ElementRef<HTMLInputElement>;
1348
1411
  maskDirective?: NgxMaskDirective;
1412
+ phoneOverlay?: CdkConnectedOverlay;
1349
1413
  isFocused: boolean;
1350
1414
  inputValue: string;
1351
1415
  isDropdownOpen: boolean;
1352
1416
  private pendingMaskedValue;
1353
1417
  private isPropagatingViewValue;
1418
+ /**
1419
+ * Country dropdown panel, positioned via CDK Overlay instead of an inline absolute-positioned
1420
+ * div. This escapes clipping inside dialogs/scroll containers and stacks correctly above CDK
1421
+ * Overlay-based content (e.g. `bk-dialog`) regardless of Angular version — see the
1422
+ * "Dropdown & Calendar Overlay Strategy" doc. Primary: below the trigger, left-aligned (mirrors
1423
+ * the old `top-full left-0 mt-1`). Fallback: above the trigger when there's no room below.
1424
+ */
1425
+ readonly phoneDropdownPositions: ConnectedPosition[];
1354
1426
  input: EventEmitter<Event>;
1355
1427
  change: EventEmitter<Event>;
1356
1428
  focus: EventEmitter<Event>;
@@ -1375,13 +1447,31 @@ declare class BkInput implements OnInit, OnDestroy, AfterViewInit, ControlValueA
1375
1447
  ngOnInit(): void;
1376
1448
  ngAfterViewInit(): void;
1377
1449
  ngOnDestroy(): void;
1378
- onDocumentClick(event: MouseEvent): void;
1379
1450
  handleFocus(event: Event): void;
1380
1451
  handleBlur(event: Event): void;
1381
1452
  handleInput(event: Event): void;
1382
1453
  handleClicked(): void;
1383
1454
  handleChange(event: Event): void;
1384
1455
  toggleDropdown(event?: Event): void;
1456
+ /**
1457
+ * Shared close path for the CDK overlay: outside click, the overlay detaching (e.g. on
1458
+ * destroy), and another `bk-input` phone field opening (see closeAllDropdownsHandler).
1459
+ */
1460
+ closeDropdown(): void;
1461
+ /**
1462
+ * CDK's `reposition` scroll strategy (the default, and what we want — no `block`/`close`
1463
+ * behaviour) only reacts to real `document`/`window` scroll. It has no way to know about an
1464
+ * app shell that scrolls a nested container instead (this one does — see the layout's
1465
+ * `overflow-y-auto` content wrapper), so without this the panel would stay frozen in place
1466
+ * while its trigger scrolls out from under it. A capture-phase listener on `document` still
1467
+ * sees scroll events fired on any descendant scrollable element (scroll doesn't bubble, but
1468
+ * capture does), same trick `bk-custom-calendar` uses for its own popup. rAF-throttled so a
1469
+ * fast scroll doesn't force layout on every tick.
1470
+ */
1471
+ private phoneOverlayScrollRafId;
1472
+ private readonly onPhoneOverlayScroll;
1473
+ private attachPhoneOverlayScrollTracking;
1474
+ private detachPhoneOverlayScrollTracking;
1385
1475
  selectCountry(country: CountryOption): void;
1386
1476
  togglePasswordVisibility(event: Event): void;
1387
1477
  handleIconClick(event: Event): void;
@@ -2367,7 +2457,12 @@ declare class BkHierarchicalSelect implements ControlValueAccessor, AfterViewIni
2367
2457
  /** Search placeholder. */
2368
2458
  searchPlaceholder: _angular_core.InputSignal<string>;
2369
2459
  searchable: _angular_core.InputSignal<boolean>;
2370
- /** When true, dropdown is positioned fixed and sized to the trigger (use inside modals/popups). */
2460
+ /**
2461
+ * @deprecated No-op, kept only so existing `[appendToBody]="true"` bindings don't break.
2462
+ * The dropdown now always positions via Angular CDK Overlay, which portals into the shared
2463
+ * `cdk-overlay-container` unconditionally — the exact clipping/stacking escape this input used
2464
+ * to opt into by hand is now the only behaviour there is. Safe to remove from call sites.
2465
+ */
2371
2466
  appendToBody: _angular_core.InputSignal<boolean>;
2372
2467
  /** Open above or below the trigger (also used when appendToBody is true for fixed coordinates). */
2373
2468
  dropdownPosition: _angular_core.InputSignal<"top" | "bottom">;
@@ -2398,38 +2493,66 @@ declare class BkHierarchicalSelect implements ControlValueAccessor, AfterViewIni
2398
2493
  searchInput: ElementRef<HTMLInputElement>;
2399
2494
  controlWrapper: ElementRef<HTMLDivElement>;
2400
2495
  dropdownPanel?: ElementRef<HTMLDivElement>;
2496
+ optionsListContainer?: ElementRef<HTMLDivElement>;
2497
+ optionEls?: QueryList<ElementRef<HTMLElement>>;
2498
+ hierarchicalOverlay?: CdkConnectedOverlay;
2499
+ /** Index of the keyboard-highlighted option within the current level (`filteredItems`). */
2500
+ markedIndex: _angular_core.WritableSignal<number>;
2401
2501
  private el;
2402
2502
  placement: _angular_core.WritableSignal<"top" | "bottom">;
2403
- private originalPanelParent;
2404
- private originalPanelAnchor;
2405
- private panelInBody;
2503
+ /**
2504
+ * Panel width, kept equal to the control's — matches the old `dropdownStyle().width`
2505
+ * behaviour. Set on open and re-measured by a ResizeObserver on the control while open, so a
2506
+ * responsive layout keeps the panel in sync.
2507
+ */
2508
+ dropdownWidth: _angular_core.WritableSignal<number | null>;
2509
+ private resizeObserver?;
2406
2510
  constructor();
2407
2511
  ngAfterViewInit(): void;
2408
2512
  ngOnDestroy(): void;
2409
- private reposition;
2410
2513
  /**
2411
- * True when the control is fully outside the visible area either the
2412
- * viewport or a scrollable/clipping ancestor (e.g. a dialog's scroll body).
2514
+ * CDK's default `reposition` scroll strategy only reacts to real `document`/`window` scroll
2515
+ * it has no way to know an app shell might scroll a nested container instead (this one
2516
+ * commonly does — dashboard layouts with a fixed header/sidebar and a scrollable content pane).
2517
+ * A capture-phase listener on `document` still sees scroll events fired on any descendant
2518
+ * scrollable element (scroll doesn't bubble, but capture does) — same trick `bk-custom-calendar`,
2519
+ * `bk-input`'s phone dropdown, and `bk-select` already use. rAF-throttled so a fast scroll
2520
+ * doesn't force layout on every tick.
2521
+ */
2522
+ private overlayScrollRafId;
2523
+ private readonly onOverlayScroll;
2524
+ private attachOverlayScrollTracking;
2525
+ private detachOverlayScrollTracking;
2526
+ /** Derives `placement` (for the `[data-position]` inset CSS) from which of
2527
+ * `hierarchicalDropdownPositions` CDK actually applied — replaces the old hand-rolled space
2528
+ * comparison now that the fit/flip decision itself lives in CDK. */
2529
+ onPositionChange(event: ConnectedOverlayPositionChange): void;
2530
+ /** Runs after the panel has rendered: reset the level highlight + focus. Position/flip is
2531
+ * entirely CDK's job now (see hierarchicalDropdownPositions + onPositionChange). */
2532
+ private afterOpenInit;
2533
+ /**
2534
+ * Set the keyboard highlight for the level now on screen: the selected node if
2535
+ * it's in this level, otherwise the first option. -1 when the level is empty,
2536
+ * which keeps nothing highlighted.
2413
2537
  */
2414
- private isControlClipped;
2538
+ private resetMarked;
2415
2539
  /**
2416
- * Relocate the panel node to <body> so it truly escapes any transformed or
2417
- * overflow-clipped ancestor (dialogs, drawers, overflow cards, etc.).
2540
+ * Keyboard navigation. Up/Down move the highlight within the current level;
2541
+ * Right (or Enter on a parent) drills into children; Left goes back a level;
2542
+ * Enter on a leaf selects it; Escape/Tab close.
2418
2543
  */
2419
- private movePanelToBody;
2420
- /** Return the panel to its original DOM position before Angular removes it. */
2421
- private restorePanel;
2422
- /** Runs after the panel has rendered: relocate (append mode) + position + focus. */
2423
- private afterOpenInit;
2544
+ onKeyDown(event: KeyboardEvent): void;
2545
+ /** Sync the highlight to a hovered option so mouse and keyboard agree. */
2546
+ markOption(index: number): void;
2547
+ /** Scroll the highlighted option into view within the options list (immediate). */
2548
+ private scrollMarkedIntoView;
2549
+ /** Defer a scroll to the marked option (after a keyboard move re-lays the row). */
2550
+ private scrollToMarked;
2551
+ /** Navigate into a node's children and reset the highlight for the new level. */
2552
+ private enterNode;
2424
2553
  isOpen: _angular_core.WritableSignal<boolean>;
2425
2554
  /** Hidden until its real height has been measured and final placement applied. */
2426
2555
  panelReady: _angular_core.WritableSignal<boolean>;
2427
- dropdownStyle: _angular_core.WritableSignal<{
2428
- top?: string;
2429
- bottom?: string;
2430
- left: string;
2431
- width: string;
2432
- }>;
2433
2556
  searchTerm: _angular_core.WritableSignal<string>;
2434
2557
  /** Breadcrumb stack: each entry is the parent node we navigated into. */
2435
2558
  breadcrumb: _angular_core.WritableSignal<HierarchicalNode[]>;
@@ -2473,16 +2596,16 @@ declare class BkHierarchicalSelect implements ControlValueAccessor, AfterViewIni
2473
2596
  private buildPathTo;
2474
2597
  toggleDropdown(event?: Event): void;
2475
2598
  openDropdown(): void;
2599
+ /** Shared tail of openDropdown(): keep the panel exactly as wide as the control (matches the
2600
+ * old dropdownStyle().width), start scroll tracking, and flip on. Position/flip itself is
2601
+ * entirely CDK's job — see hierarchicalDropdownPositions + onPositionChange. */
2602
+ private openPanel;
2476
2603
  /**
2477
- * Decides which side the panel opens on (auto-flip) and, for append-to-body
2478
- * mode, computes the fixed coordinates. Re-run on open and on scroll/resize so
2479
- * the panel snaps to whichever side has room — honouring `dropdownPosition`
2480
- * when it fits. Inline mode is positioned by CSS via the `placement` signal.
2604
+ * Preferred position list handed to CDK, in try-order honours `dropdownPosition` first,
2605
+ * falling back to the other side only when the preferred one genuinely doesn't fit. Replaces
2606
+ * the old hand-rolled `applyPlacement()` space comparison entirely.
2481
2607
  */
2482
- applyPlacement(): void;
2483
- /** Fixed positioning when appendToBody; inset placement uses CSS + .hierarchical-select-field. */
2484
- getTop(): string | null;
2485
- getBottom(): string | null;
2608
+ get hierarchicalDropdownPositions(): ConnectedPosition[];
2486
2609
  closeDropdown(): void;
2487
2610
  goBack(): void;
2488
2611
  onSearchInput(event: Event): void;
@@ -2494,7 +2617,7 @@ declare class BkHierarchicalSelect implements ControlValueAccessor, AfterViewIni
2494
2617
  */
2495
2618
  toggleParentSelection(node: HierarchicalNode, event?: Event): void;
2496
2619
  openFromLabel(event: MouseEvent): void;
2497
- onClickOutside(e: Event): void;
2620
+ onOverlayOutsideClick(): void;
2498
2621
  private _value;
2499
2622
  private valueSignal;
2500
2623
  onChange: (value: any) => void;
@@ -2981,6 +3104,8 @@ declare class BkMenu implements OnChanges, OnDestroy {
2981
3104
  * already sets it apart from the page.
2982
3105
  */
2983
3106
  submenuBackground: boolean;
3107
+ /** Emit parent clicks as selections as well as toggling their inline submenu. */
3108
+ emitParentClick: boolean;
2984
3109
  /** Id of the currently selected leaf item. Two-way bindable. */
2985
3110
  activeItemId: string;
2986
3111
  /** Tint item icons: dark by default, white on the active (dark) row. */
@@ -3069,7 +3194,7 @@ declare class BkMenu implements OnChanges, OnDestroy {
3069
3194
  onDocumentClick(event: MouseEvent): void;
3070
3195
  onViewportChange(): void;
3071
3196
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkMenu, never>;
3072
- 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>;
3197
+ 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; }; "emitParentClick": { "alias": "emitParentClick"; "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>;
3073
3198
  }
3074
3199
 
3075
3200
  /** Which edge of the anchor the panel sits against. */
@@ -3092,41 +3217,14 @@ type PopoverAlign = 'start' | 'center' | 'end';
3092
3217
  type PopoverPlacement = 'top' | 'top-left' | 'top-right' | 'bottom' | 'bottom-left' | 'bottom-right' | 'left' | 'left-top' | 'left-bottom' | 'right' | 'right-top' | 'right-bottom';
3093
3218
  /** Every placement, in the order a placement picker should show them. */
3094
3219
  declare const POPOVER_PLACEMENTS: readonly PopoverPlacement[];
3095
- interface Rect {
3096
- top: number;
3097
- left: number;
3098
- width: number;
3099
- height: number;
3100
- }
3101
- interface Size {
3102
- width: number;
3103
- height: number;
3104
- }
3105
- interface PopoverPositionOptions {
3106
- /** Gap between the anchor and the panel, in px. */
3107
- offset: number;
3108
- /** Smallest gap to leave between the panel and the viewport edge, in px. */
3109
- viewportPadding: number;
3110
- /** Edge length of the (unrotated) arrow square, in px. */
3111
- arrowSize: number;
3112
- /** Whether a panel that doesn't fit may move to the opposite side. */
3113
- flip: boolean;
3114
- viewport: Size;
3115
- }
3116
- interface PopoverPosition {
3117
- /** Viewport coordinates for the panel (it is positioned `fixed`). */
3118
- top: number;
3119
- left: number;
3120
- /** Where the panel actually landed — not necessarily what was requested. */
3121
- side: PopoverSide;
3122
- align: PopoverAlign;
3123
- placement: PopoverPlacement;
3124
- /**
3125
- * Distance from the panel's leading edge to the arrow's centre, along the
3126
- * panel's cross axis (x for top/bottom, y for left/right).
3127
- */
3128
- arrowOffset: number;
3129
- }
3220
+ /** The side CDK should try when the requested one doesn't fit — only ever the direct
3221
+ * opposite, never a diagonal, so the alignment word stays meaningful after a flip. */
3222
+ declare const OPPOSITE_SIDE: Record<PopoverSide, PopoverSide>;
3223
+ /**
3224
+ * How close the arrow's centre may get to a panel corner. The panel is rounded,
3225
+ * so an arrow any nearer would grow out of the curve instead of a flat edge.
3226
+ */
3227
+ declare const ARROW_CORNER_GAP = 14;
3130
3228
  /** `top-left` → side `top`, align `start`. A bare side means centred. */
3131
3229
  declare function splitPlacement(placement: PopoverPlacement): {
3132
3230
  side: PopoverSide;
@@ -3138,19 +3236,12 @@ declare function splitPlacement(placement: PopoverPlacement): {
3138
3236
  * the same cross axis, so the alignment word stays meaningful.
3139
3237
  */
3140
3238
  declare function joinPlacement(side: PopoverSide, align: PopoverAlign): PopoverPlacement;
3141
- /**
3142
- * Places the panel against the anchor, then keeps it on screen in two ways:
3143
- *
3144
- * - flip: a panel with no room on the requested side moves to the opposite one,
3145
- * but only if it actually fits there — flipping into a second bad fit just
3146
- * trades one clipped edge for another.
3147
- * - shift: the panel slides along its cross axis to stay inside the viewport.
3148
- * The arrow is measured against the *anchor*, not the panel, so it keeps
3149
- * pointing at the trigger however far the panel had to slide.
3150
- */
3151
- declare function computePopoverPosition(anchor: Rect, panel: Size, placement: PopoverPlacement, options: PopoverPositionOptions): PopoverPosition;
3239
+ /** min wins when the range is inverted (arrow limit wider than the panel itself on a very
3240
+ * narrow/maxWidth-constrained panel), which keeps the arrow at the panel's leading edge
3241
+ * rather than pushing it past the trailing one. */
3242
+ declare function clamp(value: number, min: number, max: number): number;
3152
3243
 
3153
- declare class BkPopover implements AfterViewInit, OnDestroy {
3244
+ declare class BkPopover implements OnDestroy {
3154
3245
  /**
3155
3246
  * One of the 12 placements (`top-left`, `right`, `bottom-right`, …). It is a
3156
3247
  * preference, not a guarantee: a panel with no room moves to the opposite
@@ -3182,11 +3273,33 @@ declare class BkPopover implements AfterViewInit, OnDestroy {
3182
3273
  openDelay: _angular_core.InputSignal<number>;
3183
3274
  closeDelay: _angular_core.InputSignal<number>;
3184
3275
  maxWidth: _angular_core.InputSignal<string>;
3276
+ /**
3277
+ * Fixed panel width, e.g. `'320px'`. Unset by default, so the panel sizes to its content
3278
+ * (capped by `maxWidth`) as before. Set this when the content's natural width shouldn't drive
3279
+ * layout — a form, a fixed-column list — so the panel doesn't reflow between opens as content
3280
+ * changes.
3281
+ *
3282
+ * Overrides `maxWidth` entirely while set — a plain CSS `max-width` would otherwise silently
3283
+ * cap a `width` larger than it (280px default), which is never what setting an exact width was
3284
+ * asking for. Raise `maxWidth` too only if you want *both* a starting width and a cap it can
3285
+ * still grow past for some other reason (e.g. a responsive breakpoint override elsewhere).
3286
+ */
3287
+ width: _angular_core.InputSignal<string | null>;
3185
3288
  panelClass: _angular_core.InputSignal<string>;
3186
3289
  /**
3187
- * Relocate the panel to <body> while open. `position: fixed` alone resolves
3188
- * against a transformed ancestor rather than the viewport, so a popover
3189
- * inside an animated dialog or a transformed card needs this to escape.
3290
+ * The max-width actually applied to the panel: whichever of `width`/`maxWidth` is in effect,
3291
+ * additionally capped to the viewport so the panel can never extend past the screen edge — an
3292
+ * oversized `width` (or a placement CDK could only push so far before running out of room)
3293
+ * shrinks to fit instead of overflowing. Mirrors the old hand-rolled positioner's clamp(), which
3294
+ * guaranteed the panel stayed fully on-screen; a bare CSS `max-width` alone doesn't provide that
3295
+ * once the requested size is close to (or exceeds) the viewport itself.
3296
+ */
3297
+ effectiveMaxWidth: _angular_core.Signal<string>;
3298
+ /**
3299
+ * @deprecated No-op, kept only so existing `[appendToBody]="true"` bindings don't break.
3300
+ * The panel now always positions via Angular CDK Overlay, which portals into the shared
3301
+ * `cdk-overlay-container` unconditionally — the exact clipping/stacking escape this input used
3302
+ * to opt into by hand is now the only behaviour there is. Safe to remove from call sites.
3190
3303
  */
3191
3304
  appendToBody: _angular_core.InputSignal<boolean>;
3192
3305
  opened: _angular_core.OutputEmitterRef<void>;
@@ -3194,27 +3307,46 @@ declare class BkPopover implements AfterViewInit, OnDestroy {
3194
3307
  openChange: _angular_core.OutputEmitterRef<boolean>;
3195
3308
  anchorRef: ElementRef<HTMLElement>;
3196
3309
  panelRef?: ElementRef<HTMLElement>;
3310
+ popoverOverlay?: CdkConnectedOverlay;
3197
3311
  private static activeInstance;
3198
- private readonly host;
3199
3312
  isOpen: _angular_core.WritableSignal<boolean>;
3200
- /**
3201
- * Null until the panel has been measured. The template keeps the panel
3202
- * hidden while it is null: the first frame exists only to measure, and
3203
- * showing it at 0,0 first would flash it in the corner.
3204
- */
3205
- position: _angular_core.WritableSignal<PopoverPosition | null>;
3313
+ /** True once CDK has positioned the panel at least once since open() — the template keeps
3314
+ * the panel hidden until then, so the first frame (only there to be measured) never flashes
3315
+ * in the corner. */
3316
+ panelReady: _angular_core.WritableSignal<boolean>;
3317
+ /** Placement actually applied, once known — null before the first (positionChange) fires
3318
+ * (or once closed), in which case activePlacement()/side() fall back to the requested one. */
3319
+ private resolvedPlacement;
3206
3320
  /** Where the panel actually is, once positioned — may differ from `placement`. */
3207
3321
  activePlacement: _angular_core.Signal<PopoverPlacement>;
3208
3322
  /** Side the panel sits on. Drives which edge the arrow hangs off (see CSS). */
3209
- side: _angular_core.Signal<_brickclay_org_ui.PopoverSide>;
3323
+ side: _angular_core.Signal<PopoverSide>;
3324
+ /** Distance from the panel's leading edge to the arrow's centre, along the panel's cross
3325
+ * axis — null until measured. Recomputed from the *actual rendered rects*, not derived from
3326
+ * CDK's position strategy, so it's correct however CDK had to push/clamp the panel. */
3327
+ private arrowOffsetPx;
3328
+ /** Arrow offset along the panel's cross axis — x on top/bottom, y on left/right. */
3329
+ arrowLeft: _angular_core.Signal<number | null>;
3330
+ arrowTop: _angular_core.Signal<number | null>;
3331
+ /**
3332
+ * Requested position first, its opposite side as a fallback when `flip` allows it — CDK tries
3333
+ * these in order and only falls to the second when the first genuinely doesn't fit. Push (on
3334
+ * by default) then keeps whichever one it lands on inside the viewport, matching the old
3335
+ * hand-rolled shift-along-the-edge behaviour. Each entry is tagged with the side/align it came
3336
+ * from (see positionMeta) so onPositionChange can read back which one CDK actually used.
3337
+ *
3338
+ * A stable field, computed once per open() — not a template-bound getter. A getter re-runs on
3339
+ * every change-detection pass (several times before CDK ever reads it), reassigning
3340
+ * `positionMeta` with fresh object references each time; onPositionChange would then be matching
3341
+ * against an array CDK was never actually given.
3342
+ */
3343
+ popoverPositions: ConnectedPosition[];
3344
+ private positionMeta;
3345
+ private computePositions;
3210
3346
  private openTimer;
3211
3347
  private closeTimer;
3212
3348
  private pointerInPanel;
3213
3349
  private resizeObserver?;
3214
- private originalPanelParent;
3215
- private originalPanelAnchor;
3216
- private panelInBody;
3217
- ngAfterViewInit(): void;
3218
3350
  ngOnDestroy(): void;
3219
3351
  open(): void;
3220
3352
  close(): void;
@@ -3224,23 +3356,51 @@ declare class BkPopover implements AfterViewInit, OnDestroy {
3224
3356
  onAnchorLeave(): void;
3225
3357
  onPanelEnter(): void;
3226
3358
  onPanelLeave(): void;
3227
- /** Only attached while open — see open(). Arrow fn so it can be removed. */
3228
- private onDocumentClick;
3359
+ /**
3360
+ * Click-outside-to-close, driven by CDK's document-level outside-pointer-event dispatcher —
3361
+ * it already excludes clicks on the cdkOverlayOrigin (the anchor) by design, so onAnchorClick()
3362
+ * stays the sole opener/toggler. No backdrop involved, so it doesn't block page/nested-scroll-
3363
+ * container scroll the way a modal's would (see the phone-dropdown writeup for why that matters).
3364
+ */
3365
+ onOverlayOutsideClick(): void;
3229
3366
  onEscape(): void;
3230
- private reposition;
3231
- /** Measures the anchor and panel, then hands the maths to the utility. */
3232
- private measure;
3233
- /** Arrow offset along the panel's cross axis x on top/bottom, y on left/right. */
3234
- arrowLeft: _angular_core.Signal<number | null>;
3235
- arrowTop: _angular_core.Signal<number | null>;
3236
- private movePanelToBody;
3237
- private restorePanel;
3367
+ /**
3368
+ * CDK's default `reposition` scroll strategy only reacts to real `document`/`window` scroll
3369
+ * it has no way to know an app shell might scroll a nested container instead (this one
3370
+ * commonly does dashboard layouts with a fixed header/sidebar and a scrollable content pane).
3371
+ * A capture-phase listener on `document` still sees scroll events fired on any descendant
3372
+ * scrollable element (scroll doesn't bubble, but capture does) — same trick `bk-select` and the
3373
+ * `bk-input` phone dropdown already use. Also recomputes the arrow, since `updatePosition()`
3374
+ * sliding the panel along the same side doesn't necessarily fire (positionChange) — that only
3375
+ * fires when the *side* changes, not every pixel of a push/slide. rAF-throttled so a fast
3376
+ * scroll doesn't force layout on every tick.
3377
+ */
3378
+ private overlayScrollRafId;
3379
+ private readonly onOverlayScroll;
3380
+ private attachOverlayScrollTracking;
3381
+ private detachOverlayScrollTracking;
3382
+ /**
3383
+ * Fires whenever CDK (re)applies a position, including the first one after open. Reads back
3384
+ * which of `popoverPositions`' tagged entries CDK actually used — matched by field value
3385
+ * (positionsEqual), not object reference: CDK's position strategy reconstructs its own
3386
+ * ConnectedPosition objects internally, so `event.connectionPair` is never `===` to the object
3387
+ * we handed it even when it's the exact same logical position. Recomputes the arrow from the
3388
+ * real rendered rects, and — on the very first call — starts observing the panel for
3389
+ * content-size changes and reveals it.
3390
+ */
3391
+ onPositionChange(event: ConnectedOverlayPositionChange): void;
3392
+ /**
3393
+ * Arrow offset along the panel's cross axis, measured from the actual rendered anchor/panel
3394
+ * rects rather than reverse-engineered from CDK's position strategy — correct however far CDK
3395
+ * had to push/clamp the panel to stay on screen.
3396
+ */
3397
+ private computeArrowOffset;
3238
3398
  private scheduleClose;
3239
3399
  private cancelOpen;
3240
3400
  private cancelClose;
3241
3401
  private clearTimers;
3242
3402
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BkPopover, never>;
3243
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkPopover, "bk-popover", ["bkPopover"], { "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "closable": { "alias": "closable"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "showArrow": { "alias": "showArrow"; "required": false; "isSignal": true; }; "offset": { "alias": "offset"; "required": false; "isSignal": true; }; "flip": { "alias": "flip"; "required": false; "isSignal": true; }; "closeOnClickOutside": { "alias": "closeOnClickOutside"; "required": false; "isSignal": true; }; "closeOnEscape": { "alias": "closeOnEscape"; "required": false; "isSignal": true; }; "openDelay": { "alias": "openDelay"; "required": false; "isSignal": true; }; "closeDelay": { "alias": "closeDelay"; "required": false; "isSignal": true; }; "maxWidth": { "alias": "maxWidth"; "required": false; "isSignal": true; }; "panelClass": { "alias": "panelClass"; "required": false; "isSignal": true; }; "appendToBody": { "alias": "appendToBody"; "required": false; "isSignal": true; }; }, { "opened": "opened"; "closed": "closed"; "openChange": "openChange"; }, never, ["[bkPopoverTrigger]", "*"], true, never>;
3403
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<BkPopover, "bk-popover", ["bkPopover"], { "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "closable": { "alias": "closable"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "showArrow": { "alias": "showArrow"; "required": false; "isSignal": true; }; "offset": { "alias": "offset"; "required": false; "isSignal": true; }; "flip": { "alias": "flip"; "required": false; "isSignal": true; }; "closeOnClickOutside": { "alias": "closeOnClickOutside"; "required": false; "isSignal": true; }; "closeOnEscape": { "alias": "closeOnEscape"; "required": false; "isSignal": true; }; "openDelay": { "alias": "openDelay"; "required": false; "isSignal": true; }; "closeDelay": { "alias": "closeDelay"; "required": false; "isSignal": true; }; "maxWidth": { "alias": "maxWidth"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "panelClass": { "alias": "panelClass"; "required": false; "isSignal": true; }; "appendToBody": { "alias": "appendToBody"; "required": false; "isSignal": true; }; }, { "opened": "opened"; "closed": "closed"; "openChange": "openChange"; }, never, ["[bkPopoverTrigger]", "*"], true, never>;
3244
3404
  }
3245
3405
 
3246
3406
  /** One entry in a stacked avatar group. Mirrors the display inputs of bk-avatar. */
@@ -4258,5 +4418,5 @@ declare class BkTreeDrag<T = any> extends BkTableDrag implements OnDestroy {
4258
4418
 
4259
4419
  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];
4260
4420
 
4261
- export { BKTooltipDirective, BK_DEFAULT_DIALOG_CONFIG, BK_DIALOG_DATA, BK_DIALOG_GLOBAL_CONFIG, BK_TABLE, BkAvatar, BkAvatarGroup, BkAvatarUploader, BkBadge, BkBreadcrumb, 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, BkTooltipInteractionService, BkTrExpand, BkTreeDrag, BkValidator, BkVirtualScroll, BrickclayIcons, BrickclayLib, CalendarModule, CalendarSelection, ColumnFilterOption, NEUTRAL_APPEARANCE, POPOVER_PLACEMENTS, computePopoverPosition, containsBkTreeNode, deriveAppearance, flattenBkTreeData, getDialogBackdropAnimation, getDialogPanelAnimation, joinPlacement, moveBkTreeNode, normalizeBkTableSize, parseColor, splitPlacement };
4262
- 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, BkPageSizeVisibility, BkSelectGridColumn, BkSortDirection, BkSortFn, BkSortOrder, BkTableNoResult, BkTableQueryParams, BkTableScroll, BkTableSelection, BkTableSize, BkTableSizeToken, BkTextAreaAutoCapitalize, BkTextAreaAutoComplete, BkTextAreaInputMode, BkTooltipDismissible, BkTreeDragScope, BkTreeDropEvent, BkTreeDropPosition, BkTreeRow, BreadcrumbItem, 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, TableRows, TabsColors, TabsOrientation, TabsVariant, TimeConfiguration, ToastConfig, ToastMessage, ToastMethodOptions, ToastPosition, ToastSeverity };
4421
+ export { ARROW_CORNER_GAP, BKTooltipDirective, BK_DEFAULT_DIALOG_CONFIG, BK_DIALOG_DATA, BK_DIALOG_GLOBAL_CONFIG, BK_TABLE, BkAvatar, BkAvatarGroup, BkAvatarUploader, BkBadge, BkBreadcrumb, 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, BkTooltipInteractionService, BkTrExpand, BkTreeDrag, BkValidator, BkVirtualScroll, BrickclayIcons, BrickclayLib, CalendarModule, CalendarSelection, ColumnFilterOption, DEFAULT_COUNTRY_OPTIONS, NEUTRAL_APPEARANCE, OPPOSITE_SIDE, POPOVER_PLACEMENTS, clamp, containsBkTreeNode, deriveAppearance, flattenBkTreeData, getDialogBackdropAnimation, getDialogPanelAnimation, joinPlacement, moveBkTreeNode, normalizeBkTableSize, parseColor, splitPlacement };
4422
+ 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, BkPageSizeVisibility, BkSelectGridColumn, BkSortDirection, BkSortFn, BkSortOrder, BkTableNoResult, BkTableQueryParams, BkTableScroll, BkTableSelection, BkTableSize, BkTableSizeToken, BkTextAreaAutoCapitalize, BkTextAreaAutoComplete, BkTextAreaInputMode, BkTooltipDismissible, BkTreeDragScope, BkTreeDropEvent, BkTreeDropPosition, BkTreeRow, BreadcrumbItem, 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, PopoverSide, ScheduledDateSelection, SortDirection, SpinnerSize, TabIconDirection, TabItem, TableAction, TableBadge, TableColumn, TableIcon, TableRows, TabsColors, TabsOrientation, TabsVariant, TimeConfiguration, ToastConfig, ToastMessage, ToastMethodOptions, ToastPosition, ToastSeverity };