@brickclay-org/ui 0.1.70 → 0.1.72

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { OnInit, OnDestroy, OnChanges, EventEmitter, ElementRef, SimpleChanges, AfterViewInit, QueryList, ComponentRef, Type, InjectionToken, Renderer2, ApplicationRef, EnvironmentInjector, ChangeDetectorRef } from '@angular/core';
2
+ import { OnInit, OnDestroy, OnChanges, EventEmitter, ElementRef, Renderer2, SimpleChanges, AfterViewInit, QueryList, ComponentRef, Type, InjectionToken, ApplicationRef, EnvironmentInjector, ChangeDetectorRef } 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';
@@ -76,6 +76,7 @@ declare class CalendarSelection {
76
76
  }
77
77
  declare class BkCustomCalendar implements OnInit, OnDestroy, OnChanges, ControlValueAccessor, Validator {
78
78
  private calendarManager;
79
+ private renderer;
79
80
  enableTimepicker: boolean;
80
81
  autoApply: boolean;
81
82
  closeOnAutoApply: boolean;
@@ -205,7 +206,25 @@ declare class BkCustomCalendar implements OnInit, OnDestroy, OnChanges, ControlV
205
206
  private unregisterFn?;
206
207
  private closeAllSubscription?;
207
208
  private closeFn?;
208
- constructor(calendarManager: BkCalendarManagerService);
209
+ constructor(calendarManager: BkCalendarManagerService, renderer: Renderer2);
210
+ /**
211
+ * appendToBody popup relocation.
212
+ *
213
+ * `appendToBody` uses `position: fixed`, which only anchors to the viewport when NO ancestor
214
+ * establishes a containing block (transform / filter / perspective / will-change / contain /
215
+ * backdrop-filter). Consumers frequently place the calendar inside such an ancestor (an animated
216
+ * panel, a CSS-transformed modal, etc.), which silently re-anchors the fixed popup and pushes it
217
+ * off-position. To be robust everywhere we physically move the popup node to <body> while it is
218
+ * open, then move it back before Angular's *ngIf tears it down. Angular keeps managing the node by
219
+ * reference after the move, so its event bindings, change detection, and emulated-encapsulation
220
+ * styles all continue to work.
221
+ */
222
+ private popupMovedToBody;
223
+ private popupOriginalParent;
224
+ private popupOriginalNextSibling;
225
+ private movePopupToBody;
226
+ /** Return the popup to its original DOM slot so *ngIf can destroy it cleanly (safe to call twice). */
227
+ private restorePopupFromBody;
209
228
  /** Weekday headers; falls back to Mon–Sun if the input is not length 7. */
210
229
  get resolvedWeekDayLabels(): string[];
211
230
  /** When dual range mode, Apply is blocked until both endpoints exist. */
@@ -239,7 +258,28 @@ declare class BkCustomCalendar implements OnInit, OnDestroy, OnChanges, ControlV
239
258
  /** Format to "H:MM AM/PM" string. */
240
259
  private formatTimeToAmPm;
241
260
  onClickOutside(event: MouseEvent): void;
242
- onWindowEvents(): void;
261
+ /** True while capture-phase scroll + resize listeners are attached (only while the popup is open). */
262
+ private viewportListenersAttached;
263
+ /** Pending rAF id so bursts of scroll events collapse into one layout pass. */
264
+ private viewportRafId;
265
+ /**
266
+ * Recipe steps 2 & 4: while the popup is open, keep it glued to the trigger as the page
267
+ * (or any nested scroll container) scrolls, and dismiss it once the trigger leaves the viewport.
268
+ * Throttled through requestAnimationFrame to avoid layout thrash during fast scrolling.
269
+ */
270
+ private readonly onViewportChange;
271
+ private handleViewportChange;
272
+ /**
273
+ * Trigger is no longer visible and the fixed popup should be dismissed. True when the trigger is
274
+ * either entirely outside the viewport, or clipped out of ANY ancestor scroll container — e.g.
275
+ * scrolled behind the header/footer of a modal body. This makes step 4 work both on the plain
276
+ * page and inside a dialog when `appendToBody` positions the popup with `fixed`.
277
+ */
278
+ private isTriggerOutOfView;
279
+ /** An ancestor that clips overflowing content on an axis where its content actually overflows. */
280
+ private isScrollClippingContainer;
281
+ private attachViewportListeners;
282
+ private detachViewportListeners;
243
283
  ngOnInit(): void;
244
284
  ngOnChanges(changes: SimpleChanges): void;
245
285
  private regenerateCalendarsForWeekStart;
@@ -249,6 +289,18 @@ declare class BkCustomCalendar implements OnInit, OnDestroy, OnChanges, ControlV
249
289
  toggle(): void;
250
290
  /** Update popup position when appendToBody is true (fixed positioning relative to viewport). */
251
291
  updatePosition(): void;
292
+ /**
293
+ * Horizontal placement for the fixed (appendToBody) popup.
294
+ *
295
+ * The base edge follows the author's {@link opens} preference:
296
+ * • 'left' → align left edges, panel opens rightwards (default)
297
+ * • 'right' → align right edges, panel opens leftwards
298
+ * • 'center' → centred on the trigger
299
+ * When {@link autoPosition} is on, the panel auto-flips to the opposite edge if the preferred
300
+ * side would overflow the viewport, then clamps to an 8px margin so it can never leave the screen.
301
+ * When autoPosition is off, the explicit `opens` value is honoured as-is.
302
+ */
303
+ private computePopupLeft;
252
304
  /** Non–append-to-body: set `popupPlacementAbove` for CSS `drop-up` with viewport flip. */
253
305
  refreshPopupPlacement(): void;
254
306
  private computePlacementAbove;
@@ -422,7 +474,8 @@ declare class BkScheduledDatePicker implements OnInit {
422
474
  static ɵcmp: i0.ɵɵComponentDeclaration<BkScheduledDatePicker, "bk-scheduled-date-picker", never, { "timeFormat": { "alias": "timeFormat"; "required": false; }; "enableSeconds": { "alias": "enableSeconds"; "required": false; }; }, { "scheduled": "scheduled"; "cleared": "cleared"; }, never, never, true, never>;
423
475
  }
424
476
 
425
- declare class BkTimePicker implements OnInit, OnChanges, AfterViewInit, ControlValueAccessor {
477
+ declare class BkTimePicker implements OnInit, OnChanges, OnDestroy, AfterViewInit, ControlValueAccessor {
478
+ private renderer;
426
479
  required: boolean;
427
480
  /** @deprecated Prefer [(ngModel)] */
428
481
  value: string | null;
@@ -435,6 +488,11 @@ declare class BkTimePicker implements OnInit, OnChanges, AfterViewInit, ControlV
435
488
  closePicker: number;
436
489
  timeFormat: 12 | 24;
437
490
  showSeconds: boolean;
491
+ /** When true, auto-flip the dropdown above/below and left/right based on available viewport space. */
492
+ autoPosition: boolean;
493
+ /** When true, position the dropdown with `fixed` so it escapes ancestor overflow (dialogs, scroll containers),
494
+ * follows the trigger on scroll, and closes when the trigger is scrolled out of view. */
495
+ appendToBody: boolean;
438
496
  change: EventEmitter<string | null>;
439
497
  timeChange: EventEmitter<string | null>;
440
498
  pickerOpened: EventEmitter<string>;
@@ -442,20 +500,35 @@ declare class BkTimePicker implements OnInit, OnChanges, AfterViewInit, ControlV
442
500
  private onChange;
443
501
  private onTouched;
444
502
  disabled: boolean;
445
- timeScrollElements: QueryList<ElementRef>;
446
- minuteScrollElements: QueryList<ElementRef>;
447
- secondScrollElements: QueryList<ElementRef>;
448
- private readonly WHEEL_CYCLE;
503
+ hourScrollEl?: ElementRef<HTMLElement>;
504
+ minuteScrollEl?: ElementRef<HTMLElement>;
505
+ secondScrollEl?: ElementRef<HTMLElement>;
506
+ /** The positioned trigger (its rect anchors the dropdown). */
507
+ tpWrapper?: ElementRef<HTMLElement>;
508
+ /** The dropdown panel element (measured for flip decisions). */
509
+ tpDropdown?: ElementRef<HTMLElement>;
510
+ /** Resolved placement state, driven by updatePosition(). */
511
+ placeAbove: boolean;
512
+ resolvedPosition: 'left' | 'right';
513
+ dropdownStyle: Record<string, string>;
449
514
  /** Row height in px. MUST match the .time-item height in CSS for the active variation. */
450
515
  private get ITEM_HEIGHT();
451
- private get MIDDLE_OFFSET();
452
- wheelMinutes: number[];
453
- wheelSeconds: number[];
516
+ /** Finite lists (no cyclic loop) — same as the hours column, so 00..59 has a hard start/end. */
517
+ minutes: number[];
518
+ seconds: number[];
454
519
  showPicker: boolean;
455
520
  currentHour: number;
456
521
  currentMinute: number;
457
522
  currentSecond: number;
458
523
  currentAMPM: string;
524
+ /**
525
+ * Highlight that follows a column while scrolling. It is purely visual: scrolling moves it
526
+ * but does NOT commit the value. The value is only selected/committed when the user clicks
527
+ * an item (see onHourChange / onMinuteChange / onSecondChange).
528
+ */
529
+ activeHour: number;
530
+ activeMinute: number;
531
+ activeSecond: number;
459
532
  private _modelValue;
460
533
  brickclayIcons: {
461
534
  readonly arrowleft: "assets/icons/chevron-left.svg";
@@ -463,6 +536,21 @@ declare class BkTimePicker implements OnInit, OnChanges, AfterViewInit, ControlV
463
536
  readonly calenderIcon: "assets/icons/custom-calender.svg";
464
537
  readonly timerIcon: "assets/icons/timer.svg";
465
538
  };
539
+ constructor(renderer: Renderer2);
540
+ /**
541
+ * `appendToBody` anchors the dropdown with `position: fixed`, which only tracks the viewport when
542
+ * NO ancestor establishes a containing block (transform / filter / perspective / will-change /
543
+ * contain / backdrop-filter). Inside such an ancestor the fixed coordinates resolve against that
544
+ * ancestor instead and the dropdown drifts off-position. To be robust we physically move the panel
545
+ * to <body> while open (Angular keeps managing it by reference) and move it back before *ngIf
546
+ * tears it down. The panel carries a `tp-compact` self-class so its `default`-variation sizing —
547
+ * otherwise scoped under the `.time-input-group.default` ancestor it just left — still applies.
548
+ */
549
+ private dropdownMovedToBody;
550
+ private dropdownOriginalParent;
551
+ private moveDropdownToBody;
552
+ /** Return the dropdown to its original slot so *ngIf can destroy it cleanly (safe to call twice). */
553
+ private restoreDropdownFromBody;
466
554
  writeValue(value: string | null): void;
467
555
  registerOnChange(fn: (value: string | null) => void): void;
468
556
  registerOnTouched(fn: () => void): void;
@@ -484,22 +572,45 @@ declare class BkTimePicker implements OnInit, OnChanges, AfterViewInit, ControlV
484
572
  };
485
573
  formatTimeFromComponents(hour: number, minute: number, second: number, ampm: string): string;
486
574
  togglePicker(): void;
575
+ /** Central close path so listeners are always detached and events emitted once. */
576
+ private dismiss;
487
577
  onHourChange(hour: number): void;
488
578
  onMinuteChange(minute: number): void;
489
579
  onSecondChange(second: number): void;
490
580
  onAMPMChange(ampm: string): void;
491
581
  updateTime(): void;
582
+ /** Position each column so the committed value sits at the top of its viewport. */
492
583
  scrollToSelectedTimes(): void;
493
- private initMinuteWheelScroll;
494
- private initSecondWheelScroll;
584
+ private scrollColumnTo;
585
+ /** Item nearest the viewport center for the current scroll position, clamped to [0, count - 1]. */
586
+ private scrollIndex;
495
587
  onDocumentClick(event: MouseEvent): void;
496
588
  private previousCloseCounter;
497
589
  getHours(): number[];
498
590
  getAMPMOptions(): string[];
499
- onMinuteWheelScroll(): void;
500
- onSecondWheelScroll(): void;
591
+ onHourScroll(): void;
592
+ onMinuteScroll(): void;
593
+ onSecondScroll(): void;
594
+ /**
595
+ * Resolve the dropdown placement against the current viewport.
596
+ * - autoPosition: flip above when there isn't room below, and flip the horizontal
597
+ * side when the preferred side would overflow.
598
+ * - appendToBody: additionally anchor with `position: fixed` (inline style) so the
599
+ * dropdown escapes ancestor overflow and follows the trigger on scroll.
600
+ */
601
+ private updatePosition;
602
+ private viewportListenersAttached;
603
+ private viewportRafId;
604
+ private readonly onViewportChange;
605
+ private handleViewportChange;
606
+ /** True when the trigger has scrolled out of the viewport or out of any clipping ancestor. */
607
+ private isTriggerOutOfView;
608
+ private isScrollClippingContainer;
609
+ private attachViewportListeners;
610
+ private detachViewportListeners;
611
+ ngOnDestroy(): void;
501
612
  static ɵfac: i0.ɵɵFactoryDeclaration<BkTimePicker, never>;
502
- static ɵcmp: i0.ɵɵComponentDeclaration<BkTimePicker, "bk-time-picker", never, { "required": { "alias": "required"; "required": false; }; "value": { "alias": "value"; "required": false; }; "label": { "alias": "label"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "clearable": { "alias": "clearable"; "required": false; }; "position": { "alias": "position"; "required": false; }; "variation": { "alias": "variation"; "required": false; }; "pickerId": { "alias": "pickerId"; "required": false; }; "closePicker": { "alias": "closePicker"; "required": false; }; "timeFormat": { "alias": "timeFormat"; "required": false; }; "showSeconds": { "alias": "showSeconds"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, { "change": "change"; "timeChange": "timeChange"; "pickerOpened": "pickerOpened"; "pickerClosed": "pickerClosed"; }, never, never, true, never>;
613
+ static ɵcmp: i0.ɵɵComponentDeclaration<BkTimePicker, "bk-time-picker", never, { "required": { "alias": "required"; "required": false; }; "value": { "alias": "value"; "required": false; }; "label": { "alias": "label"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "clearable": { "alias": "clearable"; "required": false; }; "position": { "alias": "position"; "required": false; }; "variation": { "alias": "variation"; "required": false; }; "pickerId": { "alias": "pickerId"; "required": false; }; "closePicker": { "alias": "closePicker"; "required": false; }; "timeFormat": { "alias": "timeFormat"; "required": false; }; "showSeconds": { "alias": "showSeconds"; "required": false; }; "autoPosition": { "alias": "autoPosition"; "required": false; }; "appendToBody": { "alias": "appendToBody"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, { "change": "change"; "timeChange": "timeChange"; "pickerOpened": "pickerOpened"; "pickerClosed": "pickerClosed"; }, never, never, true, never>;
503
614
  }
504
615
 
505
616
  /**
@@ -885,6 +996,7 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
885
996
  optionsListContainer: ElementRef<HTMLDivElement>;
886
997
  optionsRef: QueryList<ElementRef>;
887
998
  controlWrapper: ElementRef<HTMLDivElement>;
999
+ dropdownPanel?: ElementRef<HTMLDivElement>;
888
1000
  chipsViewport?: ElementRef<HTMLDivElement>;
889
1001
  measureChips?: QueryList<ElementRef<HTMLElement>>;
890
1002
  measurePlus?: ElementRef<HTMLElement>;
@@ -894,6 +1006,10 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
894
1006
  selectedOptions: i0.WritableSignal<any[]>;
895
1007
  searchTerm: i0.WritableSignal<string>;
896
1008
  markedIndex: i0.WritableSignal<number>;
1009
+ placement: i0.WritableSignal<"top" | "bottom">;
1010
+ private originalPanelParent;
1011
+ private originalPanelAnchor;
1012
+ private panelInBody;
897
1013
  visibleCount: i0.WritableSignal<number>;
898
1014
  private resizeObserver?;
899
1015
  dropdownStyle: i0.WritableSignal<{
@@ -907,6 +1023,25 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
907
1023
  constructor();
908
1024
  ngAfterViewInit(): void;
909
1025
  ngOnDestroy(): void;
1026
+ private reposition;
1027
+ /**
1028
+ * True when the control is fully outside the visible area — either the
1029
+ * viewport or a scrollable/clipping ancestor (e.g. a dialog's scroll body).
1030
+ * Used to close the floating (append-to-body) panel once its anchor is no
1031
+ * longer on screen, instead of leaving it hanging.
1032
+ */
1033
+ private isControlClipped;
1034
+ /**
1035
+ * Relocate the panel node to <body> so it truly escapes any transformed or
1036
+ * overflow-clipped ancestor (dialogs, drawers, cards with overflow, etc.).
1037
+ * Setting position:fixed alone is not enough when an ancestor has a transform.
1038
+ */
1039
+ private movePanelToBody;
1040
+ /**
1041
+ * Return the panel to its original DOM position so Angular's @if can remove
1042
+ * it cleanly on close. Must run before isOpen is set to false.
1043
+ */
1044
+ private restorePanel;
910
1045
  private scheduleRecompute;
911
1046
  /**
912
1047
  * Measures the off-screen chip row and works out how many chips fit on a
@@ -925,8 +1060,13 @@ declare class BkSelect implements ControlValueAccessor, AfterViewInit, OnDestroy
925
1060
  closeDropdown(): void;
926
1061
  getTop(): string | null;
927
1062
  getBottom(): string | null;
928
- updatePosition(): void;
929
- onWindowEvents(): void;
1063
+ /**
1064
+ * Decides which side the panel should open on (auto-flip) and, for
1065
+ * append-to-body mode, computes the fixed coordinates. Shared by both modes
1066
+ * and re-run on open and on every scroll/resize so the panel snaps to
1067
+ * whichever side has room — honouring `dropdownPosition` when it fits.
1068
+ */
1069
+ applyPlacement(): void;
930
1070
  toggleSelectAll(event: Event): void;
931
1071
  handleSelection(item: any, event?: Event): void;
932
1072
  removeOption(item: any, event: Event): void;
@@ -1130,6 +1270,18 @@ interface TabItem {
1130
1270
  iconTooltip?: string | string[];
1131
1271
  iconTooltipDirection?: 'left' | 'right' | 'top' | 'bottom';
1132
1272
  error?: boolean;
1273
+ /**
1274
+ * When the tab is in an error state, render an error icon (on the side given
1275
+ * by `direction`) with `iconTooltip` shown on hover of the icon.
1276
+ * When false/omitted, no error icon is drawn and `iconTooltip` falls back to
1277
+ * the tab button itself. Works for both variants and orientations.
1278
+ */
1279
+ showErrorIcon?: boolean;
1280
+ /**
1281
+ * Optional custom error icon URL. If omitted while `showErrorIcon` is true,
1282
+ * the component's default error icon is used.
1283
+ */
1284
+ errorIcon?: string;
1133
1285
  }
1134
1286
  declare class BkTabs {
1135
1287
  list: TabItem[];
@@ -1138,16 +1290,26 @@ declare class BkTabs {
1138
1290
  orientation: TabsOrientation;
1139
1291
  variant: TabsVariant;
1140
1292
  colors: TabsColors;
1293
+ /**
1294
+ * When true, if the currently active tab is in an error state, navigation to
1295
+ * the other tabs is blocked (they render disabled) until the error clears.
1296
+ * Optional — defaults to false, so navigation stays free unless opted in.
1297
+ */
1298
+ lockNavigationOnError: boolean;
1299
+ /** Fallback error icon used when a tab has `showErrorIcon` but no `errorIcon`. */
1300
+ private readonly defaultErrorIcon;
1141
1301
  get isVertical(): boolean;
1142
1302
  get isSegmented(): boolean;
1143
1303
  get wrapperClass(): string;
1144
1304
  segmentedTabClass(tab: TabItem): string;
1145
1305
  change: EventEmitter<TabItem>;
1306
+ get activeTabHasError(): boolean;
1307
+ isLocked(tab: TabItem): boolean;
1146
1308
  setActiveTab(tab: TabItem): void;
1147
1309
  isActive(tabId: string): boolean;
1148
1310
  getTabIcon(tab: TabItem): string | undefined;
1149
1311
  static ɵfac: i0.ɵɵFactoryDeclaration<BkTabs, never>;
1150
- static ɵcmp: i0.ɵɵComponentDeclaration<BkTabs, "bk-tabs", never, { "list": { "alias": "list"; "required": false; }; "activeTabId": { "alias": "activeTabId"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "orientation": { "alias": "orientation"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "colors": { "alias": "colors"; "required": false; }; }, { "change": "change"; }, never, never, true, never>;
1312
+ static ɵcmp: i0.ɵɵComponentDeclaration<BkTabs, "bk-tabs", never, { "list": { "alias": "list"; "required": false; }; "activeTabId": { "alias": "activeTabId"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "orientation": { "alias": "orientation"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "colors": { "alias": "colors"; "required": false; }; "lockNavigationOnError": { "alias": "lockNavigationOnError"; "required": false; }; }, { "change": "change"; }, never, never, true, never>;
1151
1313
  }
1152
1314
 
1153
1315
  /**
@@ -1929,7 +2091,7 @@ interface HierarchicalNode {
1929
2091
  children?: HierarchicalNode[];
1930
2092
  disabled?: boolean;
1931
2093
  }
1932
- declare class BkHierarchicalSelect implements ControlValueAccessor {
2094
+ declare class BkHierarchicalSelect implements ControlValueAccessor, AfterViewInit, OnDestroy {
1933
2095
  /** Tree data (root-level array). */
1934
2096
  items: i0.InputSignal<HierarchicalNode[]>;
1935
2097
  /** Key for display label (e.g. 'noteType'). */
@@ -1979,8 +2141,30 @@ declare class BkHierarchicalSelect implements ControlValueAccessor {
1979
2141
  clear: i0.OutputEmitterRef<any>;
1980
2142
  searchInput: ElementRef<HTMLInputElement>;
1981
2143
  controlWrapper: ElementRef<HTMLDivElement>;
2144
+ dropdownPanel?: ElementRef<HTMLDivElement>;
1982
2145
  private el;
2146
+ placement: i0.WritableSignal<"top" | "bottom">;
2147
+ private originalPanelParent;
2148
+ private originalPanelAnchor;
2149
+ private panelInBody;
1983
2150
  constructor();
2151
+ ngAfterViewInit(): void;
2152
+ ngOnDestroy(): void;
2153
+ private reposition;
2154
+ /**
2155
+ * True when the control is fully outside the visible area — either the
2156
+ * viewport or a scrollable/clipping ancestor (e.g. a dialog's scroll body).
2157
+ */
2158
+ private isControlClipped;
2159
+ /**
2160
+ * Relocate the panel node to <body> so it truly escapes any transformed or
2161
+ * overflow-clipped ancestor (dialogs, drawers, overflow cards, etc.).
2162
+ */
2163
+ private movePanelToBody;
2164
+ /** Return the panel to its original DOM position before Angular removes it. */
2165
+ private restorePanel;
2166
+ /** Runs after the panel has rendered: relocate (append mode) + position + focus. */
2167
+ private afterOpenInit;
1984
2168
  isOpen: i0.WritableSignal<boolean>;
1985
2169
  dropdownStyle: i0.WritableSignal<{
1986
2170
  top?: string;
@@ -2012,11 +2196,16 @@ declare class BkHierarchicalSelect implements ControlValueAccessor {
2012
2196
  private buildPathTo;
2013
2197
  toggleDropdown(event?: Event): void;
2014
2198
  openDropdown(): void;
2015
- updatePosition(): void;
2199
+ /**
2200
+ * Decides which side the panel opens on (auto-flip) and, for append-to-body
2201
+ * mode, computes the fixed coordinates. Re-run on open and on scroll/resize so
2202
+ * the panel snaps to whichever side has room — honouring `dropdownPosition`
2203
+ * when it fits. Inline mode is positioned by CSS via the `placement` signal.
2204
+ */
2205
+ applyPlacement(): void;
2016
2206
  /** Fixed positioning when appendToBody; inset placement uses CSS + .hierarchical-select-field. */
2017
2207
  getTop(): string | null;
2018
2208
  getBottom(): string | null;
2019
- onWindowScrollOrResize(): void;
2020
2209
  closeDropdown(): void;
2021
2210
  goBack(): void;
2022
2211
  onSearchInput(event: Event): void;
@@ -2383,5 +2572,70 @@ declare class BkDropdown {
2383
2572
  static ɵcmp: i0.ɵɵComponentDeclaration<BkDropdown, "bk-dropdown", never, { "label": { "alias": "label"; "required": false; }; "items": { "alias": "items"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "size": { "alias": "size"; "required": false; }; "trigger": { "alias": "trigger"; "required": false; }; "placement": { "alias": "placement"; "required": false; }; "arrow": { "alias": "arrow"; "required": false; }; "splitButton": { "alias": "splitButton"; "required": false; }; "selectedId": { "alias": "selectedId"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "iconSrc": { "alias": "iconSrc"; "required": false; }; "customClass": { "alias": "customClass"; "required": false; }; }, { "select": "select"; "openChange": "openChange"; "buttonClick": "buttonClick"; }, never, never, true, never>;
2384
2573
  }
2385
2574
 
2386
- export { BKTooltipDirective, BK_DEFAULT_DIALOG_CONFIG, BK_DIALOG_DATA, BK_DIALOG_GLOBAL_CONFIG, BkAvatar, 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, BkPagination, BkPill, BkRadioButton, BkScheduledDatePicker, BkSelect, BkSpinner, BkTabs, BkTextarea, BkTimePicker, BkToastr, BkToastrService, BkToggle, BkValidator, BrickclayIcons, BrickclayLib, CalendarModule, CalendarSelection, ColumnFilterOption, getDialogBackdropAnimation, getDialogPanelAnimation };
2387
- export type { AvatarFallback, AvatarSize, AvatarVariant, BadgeColor, BadgeSize, BadgeVariant, BkAnimationKeyframes, BkAvatarFallback, BkAvatarSize, BkDialogAnimation, BkDialogConfig, BkDialogPosition, BkInputAutoCapitalize, BkInputAutoComplete, BkInputMode, BkInputType, BkLoaderVariant, BkPageSize, BkTextAreaAutoCapitalize, BkTextAreaAutoComplete, BkTextAreaInputMode, ButtonSize, ButtonVariant, CalendarRange, CountryOption, CustomRangesConfig, DotPosition, DotStatus, DropdownItem, DropdownPlacement, DropdownSize, DropdownTrigger, DropdownVariant, FileState, GroupItem, GroupMode, HierarchicalNode, IconButtonSize, IconButtonVariant, IconOrientation, PillColor, PillSize, PillVariant, ScheduledDateSelection, SortDirection, SpinnerSize, TabIconDirection, TabItem, TableAction, TableBadge, TableColumn, TableIcon, TabsColors, TabsOrientation, TabsVariant, TimeConfiguration, ToastConfig, ToastMessage, ToastMethodOptions, ToastPosition, ToastSeverity };
2575
+ type MenuOrientation = 'horizontal' | 'vertical';
2576
+ type MenuSubmenuMode = 'inline' | 'popup';
2577
+ interface MenuItem {
2578
+ /** Unique id — matched against `activeItemId`. */
2579
+ id: string;
2580
+ /** Visible menu text. */
2581
+ label: string;
2582
+ /** Optional icon URL (tinted to the theme unless `tintIcons` is false). */
2583
+ icon?: string;
2584
+ /** Alt text for the icon; defaults to `label`. */
2585
+ iconAlt?: string;
2586
+ /** Disables just this item (and blocks opening its submenu). */
2587
+ disabled?: boolean;
2588
+ /** Nested items — presence of children turns this into an expandable node. */
2589
+ children?: MenuItem[];
2590
+ }
2591
+ /**
2592
+ * A simple, responsive navigation menu. Styled to match the default `bk-tabs`
2593
+ * theme (dark active pill, light hover).
2594
+ *
2595
+ * - Orientation: `vertical` (stacked) / `horizontal` (menubar row).
2596
+ * - Items with `children` are expandable, shown with a chevron that rotates
2597
+ * open/closed.
2598
+ * - Submenus render inline (accordion) or as pop-ups. Pop-ups adapt to the
2599
+ * orientation: they drop *below* a horizontal top-level item and fly out to
2600
+ * the *right* for vertical menus / deeper levels.
2601
+ */
2602
+ declare class BkMenu {
2603
+ private readonly host;
2604
+ items: MenuItem[];
2605
+ orientation: MenuOrientation;
2606
+ submenuMode: MenuSubmenuMode;
2607
+ /**
2608
+ * Keep only one submenu open per level: opening a parent collapses its
2609
+ * siblings (and their descendants) so the menu stays compact. Pop-up mode
2610
+ * always behaves this way; this input adds it to inline (accordion) mode.
2611
+ */
2612
+ singleOpen: boolean;
2613
+ /** Id of the currently selected leaf item. Two-way bindable. */
2614
+ activeItemId: string;
2615
+ /** Tint item icons: dark by default, white on the active (dark) row. */
2616
+ tintIcons: boolean;
2617
+ activeItemIdChange: EventEmitter<string>;
2618
+ itemClick: EventEmitter<MenuItem>;
2619
+ /** Ids of currently expanded/opened parent nodes. */
2620
+ private readonly openIds;
2621
+ constructor(host: ElementRef<HTMLElement>);
2622
+ get isVertical(): boolean;
2623
+ get isPopup(): boolean;
2624
+ hasChildren(item: MenuItem): boolean;
2625
+ isOpen(item: MenuItem): boolean;
2626
+ isActive(item: MenuItem): boolean;
2627
+ isActiveAncestor(item: MenuItem): boolean;
2628
+ isHighlighted(item: MenuItem): boolean;
2629
+ inlineIndent(level: number): number | null;
2630
+ onItemClick(item: MenuItem, siblings: MenuItem[], event: Event): void;
2631
+ onItemEnter(item: MenuItem, siblings: MenuItem[]): void;
2632
+ onItemLeave(item: MenuItem): void;
2633
+ private toggle;
2634
+ private closeBranch;
2635
+ onDocumentClick(event: MouseEvent): void;
2636
+ static ɵfac: i0.ɵɵFactoryDeclaration<BkMenu, never>;
2637
+ static ɵcmp: i0.ɵɵ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; }; }, { "activeItemIdChange": "activeItemIdChange"; "itemClick": "itemClick"; }, never, never, true, never>;
2638
+ }
2639
+
2640
+ export { BKTooltipDirective, BK_DEFAULT_DIALOG_CONFIG, BK_DIALOG_DATA, BK_DIALOG_GLOBAL_CONFIG, BkAvatar, 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, BkRadioButton, BkScheduledDatePicker, BkSelect, BkSpinner, BkTabs, BkTextarea, BkTimePicker, BkToastr, BkToastrService, BkToggle, BkValidator, BrickclayIcons, BrickclayLib, CalendarModule, CalendarSelection, ColumnFilterOption, getDialogBackdropAnimation, getDialogPanelAnimation };
2641
+ export type { AvatarFallback, AvatarSize, AvatarVariant, BadgeColor, BadgeSize, BadgeVariant, BkAnimationKeyframes, BkAvatarFallback, BkAvatarSize, BkDialogAnimation, BkDialogConfig, BkDialogPosition, BkInputAutoCapitalize, BkInputAutoComplete, BkInputMode, BkInputType, BkLoaderVariant, BkPageSize, BkTextAreaAutoCapitalize, BkTextAreaAutoComplete, BkTextAreaInputMode, ButtonSize, ButtonVariant, CalendarRange, CountryOption, CustomRangesConfig, DotPosition, DotStatus, DropdownItem, DropdownPlacement, DropdownSize, DropdownTrigger, DropdownVariant, FileState, GroupItem, GroupMode, HierarchicalNode, IconButtonSize, IconButtonVariant, IconOrientation, MenuItem, MenuOrientation, MenuSubmenuMode, PillColor, PillSize, PillVariant, ScheduledDateSelection, SortDirection, SpinnerSize, TabIconDirection, TabItem, TableAction, TableBadge, TableColumn, TableIcon, TabsColors, TabsOrientation, TabsVariant, TimeConfiguration, ToastConfig, ToastMessage, ToastMethodOptions, ToastPosition, ToastSeverity };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brickclay-org/ui",
3
- "version": "0.1.70",
3
+ "version": "0.1.72",
4
4
  "schematics": "./schematics/collection.json",
5
5
  "ng-add": {
6
6
  "save": "dependencies"
@@ -0,0 +1,12 @@
1
+ <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect x="3.57007" y="3.56995" width="40" height="40" rx="20" fill="#FEE2E2"/>
3
+ <rect x="3.57007" y="3.56995" width="40" height="40" rx="20" stroke="#FEE2E2" stroke-width="7.14"/>
4
+ <g clip-path="url(#clip0_1_149094)">
5
+ <path d="M23.5702 20.2366V23.5699M23.5702 26.9032H23.5785M31.9035 23.5699C31.9035 28.1723 28.1725 31.9032 23.5702 31.9032C18.9678 31.9032 15.2368 28.1723 15.2368 23.5699C15.2368 18.9675 18.9678 15.2366 23.5702 15.2366C28.1725 15.2366 31.9035 18.9675 31.9035 23.5699Z" stroke="#EF4444" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
6
+ </g>
7
+ <defs>
8
+ <clipPath id="clip0_1_149094">
9
+ <rect width="20" height="20" fill="white" transform="translate(13.5701 13.5699)"/>
10
+ </clipPath>
11
+ </defs>
12
+ </svg>