minahil 0.1.8 → 0.1.9

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.
@@ -1,10 +1,15 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, EventEmitter, HostListener, ViewChildren, Output, Input, Injectable, NgModule, forwardRef, ViewEncapsulation } from '@angular/core';
2
+ import { Component, EventEmitter, HostListener, ViewChildren, Output, Input, Injectable, NgModule, forwardRef, ViewEncapsulation, InjectionToken, inject, ElementRef, Directive } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
6
6
  import moment from 'moment';
7
- import { Subject } from 'rxjs';
7
+ import { Subject, filter } from 'rxjs';
8
+ import { DIALOG_DATA as DIALOG_DATA$1, CdkDialogContainer, Dialog, DialogModule } from '@angular/cdk/dialog';
9
+ import { Overlay, OverlayModule } from '@angular/cdk/overlay';
10
+ import { CdkPortalOutlet, PortalModule } from '@angular/cdk/portal';
11
+ import * as i1$1 from '@angular/cdk/scrolling';
12
+ import { CdkScrollable } from '@angular/cdk/scrolling';
8
13
 
9
14
  // Icon paths that will be resolved relative to the library's assets folder
10
15
  // When published to npm, users will need to configure their angular.json to include these assets
@@ -2460,6 +2465,1202 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2460
2465
  type: Output
2461
2466
  }] } });
2462
2467
 
2468
+ /**
2469
+ * Dialog Injection Tokens
2470
+ *
2471
+ * Architecture Decision:
2472
+ * ─────────────────────
2473
+ * • `DIALOG_DATA` — Re-exported from `@angular/cdk/dialog` so that
2474
+ * consumers import from our barrel and the token identity matches
2475
+ * exactly what CDK's `Dialog` service provides in the injector.
2476
+ * This means `@Inject(DIALOG_DATA)` works without any extra wiring.
2477
+ *
2478
+ * • `DIALOG_GLOBAL_CONFIG` — Optional app-level defaults, same pattern
2479
+ * as before.
2480
+ *
2481
+ * • `INTERNAL_DIALOG_CONFIG` — Internal-only token used to pass our
2482
+ * full `DialogConfig` (including animation fields) to the custom
2483
+ * `DialogContainerComponent`. Not part of the public API.
2484
+ */
2485
+ // ──── Public ─────────────────────────────────────────────────────────────
2486
+ /**
2487
+ * Injection token used to pass arbitrary data into a dialog component.
2488
+ *
2489
+ * This is CDK's own `DIALOG_DATA` token — re-exported so that imports
2490
+ * from our barrel (`shared/components/dialog`) resolve to the exact same
2491
+ * token that CDK's `Dialog` service provides.
2492
+ *
2493
+ * Usage inside a dialog component:
2494
+ * ```ts
2495
+ * constructor(@Inject(DIALOG_DATA) public data: MyDataType) {}
2496
+ * ```
2497
+ */
2498
+ const DIALOG_DATA = DIALOG_DATA$1;
2499
+ /**
2500
+ * Optional token for providing global dialog defaults at the
2501
+ * application level.
2502
+ *
2503
+ * Usage in `app.config.ts` or a module's `providers` array:
2504
+ * ```ts
2505
+ * providers: [
2506
+ * { provide: DIALOG_GLOBAL_CONFIG, useValue: { animation: 'zoom', width: '600px' } }
2507
+ * ]
2508
+ * ```
2509
+ */
2510
+ const DIALOG_GLOBAL_CONFIG = new InjectionToken('DIALOG_GLOBAL_CONFIG');
2511
+ // ──── Internal ───────────────────────────────────────────────────────────
2512
+ /**
2513
+ * Internal token that carries our full `DialogConfig` (with animation
2514
+ * settings, position offsets, etc.) into the `DialogContainerComponent`.
2515
+ *
2516
+ * @internal — not exported from the barrel; do not depend on it.
2517
+ */
2518
+ const INTERNAL_DIALOG_CONFIG = new InjectionToken('INTERNAL_DIALOG_CONFIG');
2519
+
2520
+ /**
2521
+ * Dialog Animations
2522
+ *
2523
+ * Architecture Decision:
2524
+ * ─────────────────────
2525
+ * Animations are implemented via the Web Animations API (WAAPI) instead of
2526
+ * Angular's @angular/animations so that:
2527
+ * 1. The dialog container stays truly standalone — no BrowserAnimationsModule
2528
+ * dependency is required.
2529
+ * 2. We have full programmatic control over timing, easing, and cleanup.
2530
+ * 3. Future presets can be added without touching the component template.
2531
+ *
2532
+ * Each preset exports an `enter` and `leave` keyframe array plus a
2533
+ * recommended timing object. The DialogContainerComponent plays these
2534
+ * via `element.animate()`.
2535
+ */
2536
+ /**
2537
+ * Build enter/leave WAAPI keyframes for the dialog **panel** element.
2538
+ */
2539
+ function getDialogPanelAnimation(preset, enterDuration, leaveDuration) {
2540
+ const easeEnter = 'cubic-bezier(0.0, 0.0, 0.2, 1)'; // decelerate
2541
+ const easeLeave = 'cubic-bezier(0.4, 0.0, 1, 1)'; // accelerate
2542
+ switch (preset) {
2543
+ // ─── Fade ────────────────────────────────────────────────────
2544
+ case 'fade':
2545
+ return {
2546
+ enter: {
2547
+ keyframes: [
2548
+ { opacity: 0, transform: 'scale(0.95)' },
2549
+ { opacity: 1, transform: 'scale(1)' },
2550
+ ],
2551
+ options: { duration: enterDuration, easing: easeEnter, fill: 'forwards' },
2552
+ },
2553
+ leave: {
2554
+ keyframes: [
2555
+ { opacity: 1, transform: 'scale(1)' },
2556
+ { opacity: 0, transform: 'scale(0.95)' },
2557
+ ],
2558
+ options: { duration: leaveDuration, easing: easeLeave, fill: 'forwards' },
2559
+ },
2560
+ };
2561
+ // ─── Zoom ────────────────────────────────────────────────────
2562
+ case 'zoom':
2563
+ return {
2564
+ enter: {
2565
+ keyframes: [
2566
+ { opacity: 0, transform: 'scale(0.5)' },
2567
+ { opacity: 1, transform: 'scale(1)' },
2568
+ ],
2569
+ options: { duration: enterDuration, easing: easeEnter, fill: 'forwards' },
2570
+ },
2571
+ leave: {
2572
+ keyframes: [
2573
+ { opacity: 1, transform: 'scale(1)' },
2574
+ { opacity: 0, transform: 'scale(0.5)' },
2575
+ ],
2576
+ options: { duration: leaveDuration, easing: easeLeave, fill: 'forwards' },
2577
+ },
2578
+ };
2579
+ // ─── Slide Top ───────────────────────────────────────────────
2580
+ case 'slide-top':
2581
+ return {
2582
+ enter: {
2583
+ keyframes: [
2584
+ { opacity: 0, transform: 'translateY(-40px)' },
2585
+ { opacity: 1, transform: 'translateY(0)' },
2586
+ ],
2587
+ options: { duration: enterDuration, easing: easeEnter, fill: 'forwards' },
2588
+ },
2589
+ leave: {
2590
+ keyframes: [
2591
+ { opacity: 1, transform: 'translateY(0)' },
2592
+ { opacity: 0, transform: 'translateY(-40px)' },
2593
+ ],
2594
+ options: { duration: leaveDuration, easing: easeLeave, fill: 'forwards' },
2595
+ },
2596
+ };
2597
+ // ─── Slide Bottom ────────────────────────────────────────────
2598
+ case 'slide-bottom':
2599
+ return {
2600
+ enter: {
2601
+ keyframes: [
2602
+ { opacity: 0, transform: 'translateY(40px)' },
2603
+ { opacity: 1, transform: 'translateY(0)' },
2604
+ ],
2605
+ options: { duration: enterDuration, easing: easeEnter, fill: 'forwards' },
2606
+ },
2607
+ leave: {
2608
+ keyframes: [
2609
+ { opacity: 1, transform: 'translateY(0)' },
2610
+ { opacity: 0, transform: 'translateY(40px)' },
2611
+ ],
2612
+ options: { duration: leaveDuration, easing: easeLeave, fill: 'forwards' },
2613
+ },
2614
+ };
2615
+ // ─── Slide Left ──────────────────────────────────────────────
2616
+ case 'slide-left':
2617
+ return {
2618
+ enter: {
2619
+ keyframes: [
2620
+ { opacity: 0, transform: 'translateX(-40px)' },
2621
+ { opacity: 1, transform: 'translateX(0)' },
2622
+ ],
2623
+ options: { duration: enterDuration, easing: easeEnter, fill: 'forwards' },
2624
+ },
2625
+ leave: {
2626
+ keyframes: [
2627
+ { opacity: 1, transform: 'translateX(0)' },
2628
+ { opacity: 0, transform: 'translateX(-40px)' },
2629
+ ],
2630
+ options: { duration: leaveDuration, easing: easeLeave, fill: 'forwards' },
2631
+ },
2632
+ };
2633
+ // ─── Slide Right ─────────────────────────────────────────────
2634
+ case 'slide-right':
2635
+ return {
2636
+ enter: {
2637
+ keyframes: [
2638
+ { opacity: 0, transform: 'translateX(40px)' },
2639
+ { opacity: 1, transform: 'translateX(0)' },
2640
+ ],
2641
+ options: { duration: enterDuration, easing: easeEnter, fill: 'forwards' },
2642
+ },
2643
+ leave: {
2644
+ keyframes: [
2645
+ { opacity: 1, transform: 'translateX(0)' },
2646
+ { opacity: 0, transform: 'translateX(40px)' },
2647
+ ],
2648
+ options: { duration: leaveDuration, easing: easeLeave, fill: 'forwards' },
2649
+ },
2650
+ };
2651
+ // ─── None ────────────────────────────────────────────────────
2652
+ case 'none':
2653
+ default:
2654
+ return {
2655
+ enter: {
2656
+ keyframes: [{ opacity: 1 }],
2657
+ options: { duration: 0, fill: 'forwards' },
2658
+ },
2659
+ leave: {
2660
+ keyframes: [{ opacity: 0 }],
2661
+ options: { duration: 0, fill: 'forwards' },
2662
+ },
2663
+ };
2664
+ }
2665
+ }
2666
+ /**
2667
+ * Build enter/leave WAAPI keyframes for the **backdrop** element.
2668
+ * The backdrop always uses a simple opacity fade regardless of the
2669
+ * panel animation preset.
2670
+ */
2671
+ function getDialogBackdropAnimation(enterDuration, leaveDuration) {
2672
+ return {
2673
+ enter: {
2674
+ keyframes: [{ opacity: 0 }, { opacity: 1 }],
2675
+ options: { duration: enterDuration, easing: 'ease-out', fill: 'forwards' },
2676
+ },
2677
+ leave: {
2678
+ keyframes: [{ opacity: 1 }, { opacity: 0 }],
2679
+ options: { duration: leaveDuration, easing: 'ease-in', fill: 'forwards' },
2680
+ },
2681
+ };
2682
+ }
2683
+
2684
+ /**
2685
+ * DialogContainerComponent
2686
+ *
2687
+ * Architecture Decision:
2688
+ * ─────────────────────
2689
+ * Extends CDK's `CdkDialogContainer` — the headless base class that
2690
+ * provides:
2691
+ * • Focus-trap management (tab key stays inside the dialog).
2692
+ * • Auto-focus / restore-focus behaviour.
2693
+ * • Portal outlet (`<ng-template cdkPortalOutlet />`) for dynamic
2694
+ * component projection.
2695
+ * • ARIA attribute management on the host element.
2696
+ *
2697
+ * We add:
2698
+ * • WAAPI enter / leave animations on the panel (host element) and
2699
+ * the CDK backdrop sibling.
2700
+ * • Flex-column styles on the dynamically created component host so
2701
+ * `.bk-dialog-content` scrolls and `.bk-dialog-actions` stays pinned.
2702
+ * • Position-offset application via CSS margin.
2703
+ *
2704
+ * This component is **never** used directly — it is created internally
2705
+ * by `DialogService` via CDK's `Dialog.open()`.
2706
+ */
2707
+ class DialogContainerComponent extends CdkDialogContainer {
2708
+ /**
2709
+ * Our full config (including animation fields).
2710
+ * Provided by DialogService via the INTERNAL_DIALOG_CONFIG token.
2711
+ */
2712
+ _dialogConfig = inject(INTERNAL_DIALOG_CONFIG);
2713
+ // ──── Opened promise ─────────────────────────────────────────────────
2714
+ /**
2715
+ * Resolves when the enter animation finishes (or immediately for 'none').
2716
+ * `DialogService` subscribes via `.then()` to emit `afterOpened` on the
2717
+ * `DialogRef`.
2718
+ */
2719
+ _resolveOpened;
2720
+ opened = new Promise(resolve => {
2721
+ this._resolveOpened = resolve;
2722
+ });
2723
+ // ──── Lifecycle ──────────────────────────────────────────────────────
2724
+ ngOnInit() {
2725
+ this._playEnterAnimation();
2726
+ this._applyPositionOffsets();
2727
+ }
2728
+ // ──── Portal override ────────────────────────────────────────────────
2729
+ /**
2730
+ * Override the CDK base to apply flex-column layout on the dynamically
2731
+ * created component's host element.
2732
+ *
2733
+ * Angular's emulated view encapsulation prevents scoped CSS from reaching
2734
+ * dynamically-projected elements, so we set styles programmatically.
2735
+ */
2736
+ attachComponentPortal(portal) {
2737
+ const ref = super.attachComponentPortal(portal);
2738
+ const el = ref.location.nativeElement;
2739
+ el.style.display = 'flex';
2740
+ el.style.flexDirection = 'column';
2741
+ el.style.flex = '1 1 auto';
2742
+ el.style.minHeight = '0';
2743
+ el.style.overflow = 'hidden';
2744
+ return ref;
2745
+ }
2746
+ // ──── WAAPI Animations ───────────────────────────────────────────────
2747
+ /**
2748
+ * Play the enter animation on the panel (host element) and backdrop.
2749
+ * Resolves the `opened` promise when the panel animation finishes.
2750
+ */
2751
+ _playEnterAnimation() {
2752
+ const preset = this._dialogConfig.animation ?? 'fade';
2753
+ if (preset === 'none') {
2754
+ this._resolveOpened();
2755
+ return;
2756
+ }
2757
+ const enterDur = this._dialogConfig.animationDurationEnter ?? 200;
2758
+ const leaveDur = this._dialogConfig.animationDurationLeave ?? 150;
2759
+ // ── Panel animation ──
2760
+ const panelAnim = getDialogPanelAnimation(preset, enterDur, leaveDur);
2761
+ const anim = this._elementRef.nativeElement.animate(panelAnim.enter.keyframes, panelAnim.enter.options);
2762
+ // ── Backdrop animation ──
2763
+ const backdropEl = this._getBackdropElement();
2764
+ if (backdropEl) {
2765
+ // Override CDK's CSS transition so our WAAPI timing wins.
2766
+ backdropEl.style.transition = 'none';
2767
+ backdropEl.style.backgroundColor = 'var(--dialog-backdrop-bg, rgba(0, 0, 0, 0.5))';
2768
+ const bdAnim = getDialogBackdropAnimation(enterDur, leaveDur);
2769
+ backdropEl.animate(bdAnim.enter.keyframes, bdAnim.enter.options);
2770
+ }
2771
+ anim.onfinish = () => this._resolveOpened();
2772
+ }
2773
+ /**
2774
+ * Play the leave animation. Returns a Promise that resolves when done.
2775
+ * Called by `DialogRef._runCloseSequence()` before CDK tears down the
2776
+ * overlay.
2777
+ */
2778
+ playLeaveAnimation() {
2779
+ const preset = this._dialogConfig.animation ?? 'fade';
2780
+ if (preset === 'none')
2781
+ return Promise.resolve();
2782
+ const enterDur = this._dialogConfig.animationDurationEnter ?? 200;
2783
+ const leaveDur = this._dialogConfig.animationDurationLeave ?? 150;
2784
+ return new Promise(resolve => {
2785
+ // ── Panel ──
2786
+ const panelAnim = getDialogPanelAnimation(preset, enterDur, leaveDur);
2787
+ const anim = this._elementRef.nativeElement.animate(panelAnim.leave.keyframes, panelAnim.leave.options);
2788
+ // ── Backdrop ──
2789
+ const backdropEl = this._getBackdropElement();
2790
+ if (backdropEl) {
2791
+ const bdAnim = getDialogBackdropAnimation(enterDur, leaveDur);
2792
+ backdropEl.animate(bdAnim.leave.keyframes, bdAnim.leave.options);
2793
+ }
2794
+ anim.onfinish = () => resolve();
2795
+ // Safety net — resolve even if onfinish never fires (SSR, etc.).
2796
+ setTimeout(() => resolve(), leaveDur + 50);
2797
+ });
2798
+ }
2799
+ // ──── Position Offsets ───────────────────────────────────────────────
2800
+ /**
2801
+ * Apply explicit position offsets via CSS margin.
2802
+ * These are additive to CDK's `GlobalPositionStrategy` alignment.
2803
+ */
2804
+ _applyPositionOffsets() {
2805
+ const pos = this._dialogConfig.position;
2806
+ if (!pos)
2807
+ return;
2808
+ const el = this._elementRef.nativeElement;
2809
+ if (pos.top)
2810
+ el.style.marginTop = pos.top;
2811
+ if (pos.bottom)
2812
+ el.style.marginBottom = pos.bottom;
2813
+ if (pos.left)
2814
+ el.style.marginLeft = pos.left;
2815
+ if (pos.right)
2816
+ el.style.marginRight = pos.right;
2817
+ }
2818
+ // ──── Helpers ────────────────────────────────────────────────────────
2819
+ /**
2820
+ * CDK places the backdrop as a sibling of `.cdk-overlay-pane`.
2821
+ * Walk up from our host → pane → wrapper, then query for the backdrop.
2822
+ */
2823
+ _getBackdropElement() {
2824
+ const pane = this._elementRef.nativeElement.closest('.cdk-overlay-pane');
2825
+ return pane?.parentElement?.querySelector('.cdk-overlay-backdrop') ?? null;
2826
+ }
2827
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DialogContainerComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2828
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: DialogContainerComponent, isStandalone: true, selector: "bk-dialog-container", host: { classAttribute: "bk-dialog-container" }, usesInheritance: true, ngImport: i0, template: "<!--\r\n Dialog Container Template (CDK-based)\r\n\r\n Architecture:\r\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n CDK Overlay manages the full overlay stack:\r\n\r\n \u250C\u2500 .cdk-overlay-container (fixed, z-index managed by CDK) \u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n \u2502 \u250C\u2500 .cdk-global-overlay-wrapper (flex-center) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\r\n \u2502 \u2502 .cdk-overlay-backdrop (click \u2192 close, CDK transition) \u2502 \u2502\r\n \u2502 \u2502 \u250C\u2500 .cdk-overlay-pane (width/height from config) \u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <bk-dialog-container> \u2190 this component (panel) \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <ng-template cdkPortalOutlet /> \u2190 user component \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <UserDialogComponent> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <h2 bk-dialog-title>Title</h2> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <div bk-dialog-content>Scrollable body</div> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <div bk-dialog-actions>Pinned buttons</div> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 </UserDialogComponent> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 </bk-dialog-container> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2502\r\n \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\r\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\r\n\r\n The host element IS the visible panel (background, radius, shadow).\r\n CDK Portal projects the caller's component as a direct child.\r\n\r\n Directives (BkDialogTitle, BkDialogContent, BkDialogActions, BkDialogClose)\r\n apply the corresponding CSS classes and add accessibility features.\r\n-->\r\n\r\n<ng-template cdkPortalOutlet />\r\n", styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;max-height:inherit;background:var(--bk-dialog-panel-bg, #ffffff);border-radius:var(--bk-dialog-panel-radius, 8px);box-shadow:var( --bk-dialog-panel-shadow, 0 11px 15px -7px rgba(0, 0, 0, .2), 0 24px 38px 3px rgba(0, 0, 0, .14), 0 9px 46px 8px rgba(0, 0, 0, .12) );outline:0;box-sizing:border-box;overflow:hidden;will-change:transform,opacity}:host ::ng-deep>:first-child{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;overflow:hidden}:host ::ng-deep .bk-dialog-content{flex:1 1 auto;overflow:auto;min-height:0;display:block;-webkit-overflow-scrolling:touch}:host ::ng-deep .bk-dialog-actions{flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;min-height:52px}:host ::ng-deep .bk-dialog-title{flex:0 0 auto;margin:0}:host ::ng-deep .bk-dialog-actions-align-start{justify-content:flex-start}:host ::ng-deep .bk-dialog-actions-align-center{justify-content:center}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }] });
2829
+ }
2830
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DialogContainerComponent, decorators: [{
2831
+ type: Component,
2832
+ args: [{ selector: 'bk-dialog-container', standalone: true, imports: [CdkPortalOutlet], host: {
2833
+ 'class': 'bk-dialog-container',
2834
+ }, template: "<!--\r\n Dialog Container Template (CDK-based)\r\n\r\n Architecture:\r\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n CDK Overlay manages the full overlay stack:\r\n\r\n \u250C\u2500 .cdk-overlay-container (fixed, z-index managed by CDK) \u2500\u2500\u2500\u2500\u2500\u2500\u2510\r\n \u2502 \u250C\u2500 .cdk-global-overlay-wrapper (flex-center) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\r\n \u2502 \u2502 .cdk-overlay-backdrop (click \u2192 close, CDK transition) \u2502 \u2502\r\n \u2502 \u2502 \u250C\u2500 .cdk-overlay-pane (width/height from config) \u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <bk-dialog-container> \u2190 this component (panel) \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <ng-template cdkPortalOutlet /> \u2190 user component \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <UserDialogComponent> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <h2 bk-dialog-title>Title</h2> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <div bk-dialog-content>Scrollable body</div> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 <div bk-dialog-actions>Pinned buttons</div> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 </UserDialogComponent> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2502 </bk-dialog-container> \u2502 \u2502 \u2502\r\n \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2502\r\n \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\r\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\r\n\r\n The host element IS the visible panel (background, radius, shadow).\r\n CDK Portal projects the caller's component as a direct child.\r\n\r\n Directives (BkDialogTitle, BkDialogContent, BkDialogActions, BkDialogClose)\r\n apply the corresponding CSS classes and add accessibility features.\r\n-->\r\n\r\n<ng-template cdkPortalOutlet />\r\n", styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;max-height:inherit;background:var(--bk-dialog-panel-bg, #ffffff);border-radius:var(--bk-dialog-panel-radius, 8px);box-shadow:var( --bk-dialog-panel-shadow, 0 11px 15px -7px rgba(0, 0, 0, .2), 0 24px 38px 3px rgba(0, 0, 0, .14), 0 9px 46px 8px rgba(0, 0, 0, .12) );outline:0;box-sizing:border-box;overflow:hidden;will-change:transform,opacity}:host ::ng-deep>:first-child{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;overflow:hidden}:host ::ng-deep .bk-dialog-content{flex:1 1 auto;overflow:auto;min-height:0;display:block;-webkit-overflow-scrolling:touch}:host ::ng-deep .bk-dialog-actions{flex:0 0 auto;display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;min-height:52px}:host ::ng-deep .bk-dialog-title{flex:0 0 auto;margin:0}:host ::ng-deep .bk-dialog-actions-align-start{justify-content:flex-start}:host ::ng-deep .bk-dialog-actions-align-center{justify-content:center}\n"] }]
2835
+ }] });
2836
+
2837
+ /**
2838
+ * DialogRef — Handle returned to callers by `DialogService.open()`.
2839
+ *
2840
+ * Architecture Decision:
2841
+ * ─────────────────────
2842
+ * Wraps CDK's `DialogRef` to add:
2843
+ * • WAAPI leave-animation before CDK tears down the overlay.
2844
+ * • Per-instance `afterOpened()` observable (CDK only has a service-level
2845
+ * `afterOpened` Subject).
2846
+ * • The same familiar API shape used by callers (`close`, `afterClosed`,
2847
+ * `backdropClick`, `keydownEvents`, `componentInstance`).
2848
+ *
2849
+ * CDK's `DialogRef<R, C>` has Result first, Component second.
2850
+ * Ours keeps `<T, R>` (Component first, Result second) to match the
2851
+ * convention consumers already use.
2852
+ */
2853
+ class DialogRef {
2854
+ _cdkRef;
2855
+ /** Unique dialog identifier (managed by CDK). */
2856
+ id;
2857
+ /** Instance of the component rendered inside the dialog. */
2858
+ componentInstance;
2859
+ // ──── Internal wiring (set by DialogService) ──────────────────────────
2860
+ /** @internal Container reference for leave animation. */
2861
+ _containerInstance;
2862
+ /** @internal Prevent double-close. */
2863
+ _closing = false;
2864
+ /** @internal Per-instance afterOpened Subject. */
2865
+ _afterOpened$ = new Subject();
2866
+ constructor(
2867
+ /**
2868
+ * @internal Wrapped CDK ref.
2869
+ * Typed as `CdkDialogRef<any>` to avoid deep generic-variance issues
2870
+ * that arise from CDK's `config.providers` callback signature.
2871
+ * The public API (`afterClosed`, etc.) re-types the observables.
2872
+ */
2873
+ _cdkRef) {
2874
+ this._cdkRef = _cdkRef;
2875
+ this.id = _cdkRef.id;
2876
+ }
2877
+ // ──── Public API ──────────────────────────────────────────────────────
2878
+ /**
2879
+ * Close the dialog, optionally returning a result.
2880
+ * Plays the WAAPI leave animation, then delegates to CDK for cleanup.
2881
+ */
2882
+ close(result) {
2883
+ if (this._closing)
2884
+ return;
2885
+ this._closing = true;
2886
+ this._runCloseSequence(result);
2887
+ }
2888
+ /**
2889
+ * Observable that emits (and completes) once after the dialog has been
2890
+ * removed from the DOM and all cleanup is done.
2891
+ */
2892
+ afterClosed() {
2893
+ return this._cdkRef.closed;
2894
+ }
2895
+ /**
2896
+ * Observable that emits (and completes) when the enter animation
2897
+ * finishes and the dialog is fully visible.
2898
+ */
2899
+ afterOpened() {
2900
+ return this._afterOpened$.asObservable();
2901
+ }
2902
+ /**
2903
+ * Observable of backdrop / outside-pointer click events.
2904
+ */
2905
+ backdropClick() {
2906
+ return this._cdkRef.outsidePointerEvents;
2907
+ }
2908
+ /**
2909
+ * Observable of keyboard events dispatched while this dialog is open.
2910
+ */
2911
+ keydownEvents() {
2912
+ return this._cdkRef.keydownEvents;
2913
+ }
2914
+ // ──── Internal helpers ────────────────────────────────────────────────
2915
+ /** @internal Called by the container once the enter animation finishes. */
2916
+ _emitOpened() {
2917
+ this._afterOpened$.next();
2918
+ this._afterOpened$.complete();
2919
+ }
2920
+ /** Play leave animation, then close via CDK. */
2921
+ async _runCloseSequence(result) {
2922
+ try {
2923
+ await this._containerInstance.playLeaveAnimation();
2924
+ }
2925
+ catch {
2926
+ // Animation may fail in SSR / test — proceed anyway.
2927
+ }
2928
+ this._cdkRef.close(result);
2929
+ }
2930
+ }
2931
+
2932
+ /**
2933
+ * Dialog Configuration
2934
+ *
2935
+ * Architecture Decision:
2936
+ * ─────────────────────
2937
+ * Our `DialogConfig` mirrors the fields from CDK's `DialogConfig` that we
2938
+ * expose, plus adds custom animation properties powered by WAAPI.
2939
+ *
2940
+ * When `DialogService.open()` is called these values are:
2941
+ * 1. Merged with global defaults and per-call overrides.
2942
+ * 2. Mapped onto CDK's native `DialogConfig` for overlay, backdrop, scroll,
2943
+ * position, ARIA, and focus-trap management.
2944
+ * 3. Passed to `DialogContainerComponent` via an internal token for
2945
+ * animation playback and position offsets.
2946
+ *
2947
+ * CDK handles: unique IDs, z-index stacking, scroll blocking, focus trap,
2948
+ * backdrop rendering, overlay positioning, keyboard events.
2949
+ * We handle: WAAPI animations, panel appearance, convenience config sugar.
2950
+ */
2951
+ // ──── Defaults ───────────────────────────────────────────────────────────
2952
+ const DEFAULT_DIALOG_CONFIG = {
2953
+ id: undefined,
2954
+ width: undefined,
2955
+ height: undefined,
2956
+ minWidth: undefined,
2957
+ minHeight: undefined,
2958
+ maxWidth: '90vw',
2959
+ maxHeight: '90vh',
2960
+ disableClose: false,
2961
+ closeOnBackdrop: true,
2962
+ closeOnEsc: true,
2963
+ hasBackdrop: true,
2964
+ lockScroll: true,
2965
+ panelClass: '',
2966
+ backdropClass: '',
2967
+ data: undefined,
2968
+ animation: 'fade',
2969
+ animationDurationEnter: 200,
2970
+ animationDurationLeave: 150,
2971
+ position: undefined,
2972
+ role: 'dialog',
2973
+ ariaLabel: undefined,
2974
+ ariaLabelledBy: undefined,
2975
+ ariaDescribedBy: undefined,
2976
+ autoFocus: true,
2977
+ restoreFocus: true,
2978
+ scrollStrategy: undefined,
2979
+ };
2980
+
2981
+ /**
2982
+ * DialogService — The core engine of the custom dialog system.
2983
+ *
2984
+ * Architecture Decision:
2985
+ * ─────────────────────
2986
+ * Built on top of Angular CDK's `Dialog` service (the same foundation
2987
+ * that `MatDialog` uses internally):
2988
+ *
2989
+ * 1. CDK creates the overlay, backdrop, scroll-blocking, focus-trap,
2990
+ * z-index stacking, and unique ID management — battle-tested infra
2991
+ * shared with every Angular Material dialog in the ecosystem.
2992
+ *
2993
+ * 2. We provide our own `DialogContainerComponent` (extending
2994
+ * `CdkDialogContainer`) for WAAPI animations and panel styling.
2995
+ *
2996
+ * 3. We wrap CDK's `DialogRef` in our own `DialogRef` to add the
2997
+ * leave-animation step before CDK tears down the overlay, and to
2998
+ * expose the same familiar API shape (`afterClosed()`, etc.).
2999
+ *
3000
+ * 4. Configuration is merged DEFAULT → GLOBAL → per-call, then mapped
3001
+ * to CDK's native config with our extras carried via an internal
3002
+ * injection token.
3003
+ *
3004
+ * What CDK handles for us (no custom code needed):
3005
+ * ─────────────────────────────────────────────────
3006
+ * ✓ Unique dialog IDs (throws on collision)
3007
+ * ✓ Z-index stacking via `.cdk-overlay-container`
3008
+ * ✓ Focus trap (tab key stays inside dialog)
3009
+ * ✓ Auto-focus / restore-focus
3010
+ * ✓ Scroll blocking (BlockScrollStrategy)
3011
+ * ✓ Backdrop rendering and click detection
3012
+ * ✓ Keyboard event forwarding
3013
+ * ✓ Overlay DOM lifecycle (create → attach → detach → dispose)
3014
+ *
3015
+ * Memory safety:
3016
+ * ─────────────
3017
+ * CDK manages the full lifecycle: on close it detaches the overlay,
3018
+ * destroys the container, and disposes the overlay ref.
3019
+ * Our DialogRef subjects complete via CDK's `closed` observable,
3020
+ * preventing lingering subscriptions.
3021
+ */
3022
+ class DialogService {
3023
+ _cdkDialog = inject(Dialog);
3024
+ _overlay = inject(Overlay);
3025
+ _globalConfig = inject(DIALOG_GLOBAL_CONFIG, { optional: true });
3026
+ /** Stack of currently open dialog refs (most recent = last). */
3027
+ _openDialogRefs = [];
3028
+ // ════════════════════════════════════════════════════════════════════
3029
+ // Public API
3030
+ // ════════════════════════════════════════════════════════════════════
3031
+ /**
3032
+ * Open a dialog containing the given component.
3033
+ *
3034
+ * @param component The component class to render inside the dialog.
3035
+ * @param config Optional per-dialog configuration (merged on top
3036
+ * of global and default settings).
3037
+ * @returns A strongly-typed `DialogRef` to interact with.
3038
+ *
3039
+ * @example
3040
+ * ```ts
3041
+ * const ref = this.dialog.open(UserFormComponent, {
3042
+ * width: '500px',
3043
+ * data: { userId: 10 },
3044
+ * });
3045
+ * ref.afterClosed().subscribe(result => console.log(result));
3046
+ * ```
3047
+ */
3048
+ open(component, config) {
3049
+ // ── 1. Merge configs: DEFAULT ← GLOBAL ← per-call ──────────────
3050
+ const mergedConfig = {
3051
+ ...DEFAULT_DIALOG_CONFIG,
3052
+ ...(this._globalConfig ?? {}),
3053
+ ...(config ?? {}),
3054
+ };
3055
+ // ── 2. Prepare the return ref (set inside providers callback) ───
3056
+ let ourRef;
3057
+ // ── 3. Build CDK-native config ──────────────────────────────────
3058
+ const cdkConfig = {
3059
+ // Identity
3060
+ id: mergedConfig.id,
3061
+ // Sizing (applied on .cdk-overlay-pane)
3062
+ width: mergedConfig.width,
3063
+ height: mergedConfig.height,
3064
+ minWidth: mergedConfig.minWidth,
3065
+ minHeight: mergedConfig.minHeight,
3066
+ maxWidth: mergedConfig.maxWidth ?? '90vw',
3067
+ maxHeight: mergedConfig.maxHeight ?? '90vh',
3068
+ // Backdrop
3069
+ hasBackdrop: mergedConfig.hasBackdrop !== false,
3070
+ backdropClass: mergedConfig.backdropClass || 'cdk-overlay-dark-backdrop',
3071
+ // Panel class (always include our base class)
3072
+ panelClass: this._buildPanelClasses(mergedConfig),
3073
+ // Data (CDK provides this as DIALOG_DATA automatically)
3074
+ data: mergedConfig.data,
3075
+ // We manage close ourselves for fine-grained closeOnBackdrop / closeOnEsc.
3076
+ disableClose: true,
3077
+ // Accessibility
3078
+ autoFocus: mergedConfig.autoFocus ?? true,
3079
+ restoreFocus: mergedConfig.restoreFocus ?? true,
3080
+ role: mergedConfig.role ?? 'dialog',
3081
+ ariaLabel: mergedConfig.ariaLabel ?? null,
3082
+ ariaLabelledBy: mergedConfig.ariaLabelledBy ?? null,
3083
+ ariaDescribedBy: mergedConfig.ariaDescribedBy ?? null,
3084
+ // Scroll strategy
3085
+ scrollStrategy: mergedConfig.scrollStrategy ??
3086
+ (mergedConfig.lockScroll !== false
3087
+ ? this._overlay.scrollStrategies.block()
3088
+ : this._overlay.scrollStrategies.noop()),
3089
+ // Position strategy (centered by default, shifted by offsets)
3090
+ positionStrategy: this._buildPositionStrategy(mergedConfig),
3091
+ // ── Custom container ──────────────────────────────────────────
3092
+ container: {
3093
+ type: DialogContainerComponent,
3094
+ providers: () => [
3095
+ { provide: INTERNAL_DIALOG_CONFIG, useValue: mergedConfig },
3096
+ ],
3097
+ },
3098
+ // ── Provider callback ─────────────────────────────────────────
3099
+ // Runs after the container is created but before the user
3100
+ // component. We create our `DialogRef` wrapper here and make
3101
+ // it available for injection in the user component.
3102
+ providers: (cdkRef, _cdkConfig, containerInstance) => {
3103
+ ourRef = new DialogRef(cdkRef);
3104
+ ourRef._containerInstance = containerInstance;
3105
+ // Wire up afterOpened emission.
3106
+ containerInstance.opened
3107
+ .then(() => ourRef._emitOpened());
3108
+ return [
3109
+ { provide: DialogRef, useValue: ourRef },
3110
+ ];
3111
+ },
3112
+ };
3113
+ // ── 4. Open via CDK ─────────────────────────────────────────────
3114
+ const cdkRef = this._cdkDialog.open(component, cdkConfig);
3115
+ // ── 5. Link componentInstance ───────────────────────────────────
3116
+ ourRef.componentInstance = cdkRef.componentInstance;
3117
+ // ── 6. Set up close-on-backdrop / close-on-ESC ──────────────────
3118
+ this._setupCloseListeners(ourRef, mergedConfig);
3119
+ // ── 7. Track in stack ───────────────────────────────────────────
3120
+ this._openDialogRefs.push(ourRef);
3121
+ // ── 8. Cleanup on close ─────────────────────────────────────────
3122
+ ourRef.afterClosed().subscribe(() => {
3123
+ const idx = this._openDialogRefs.indexOf(ourRef);
3124
+ if (idx !== -1)
3125
+ this._openDialogRefs.splice(idx, 1);
3126
+ });
3127
+ return ourRef;
3128
+ }
3129
+ /**
3130
+ * Close all currently open dialogs (most recent first).
3131
+ */
3132
+ closeAll() {
3133
+ for (let i = this._openDialogRefs.length - 1; i >= 0; i--) {
3134
+ this._openDialogRefs[i].close();
3135
+ }
3136
+ }
3137
+ /**
3138
+ * Returns the `DialogRef` of the most recently opened dialog,
3139
+ * or `undefined` if none are open.
3140
+ */
3141
+ getTopDialog() {
3142
+ return this._openDialogRefs[this._openDialogRefs.length - 1];
3143
+ }
3144
+ /**
3145
+ * Get a dialog by its CDK-managed unique ID.
3146
+ */
3147
+ getDialogById(id) {
3148
+ return this._openDialogRefs.find(r => r.id === id);
3149
+ }
3150
+ /**
3151
+ * Number of currently open dialogs.
3152
+ */
3153
+ get openDialogCount() {
3154
+ return this._openDialogRefs.length;
3155
+ }
3156
+ /**
3157
+ * Read-only snapshot of currently open dialog refs.
3158
+ * Used internally by content directives (`BkDialogTitle`, `BkDialogActions`,
3159
+ * `BkDialogClose`) for the DOM-walk fallback when `DialogRef` is not
3160
+ * available via injection (e.g. inside `<ng-template>`).
3161
+ */
3162
+ get openDialogsRef() {
3163
+ return this._openDialogRefs;
3164
+ }
3165
+ // ════════════════════════════════════════════════════════════════════
3166
+ // Convenience Helpers
3167
+ // ════════════════════════════════════════════════════════════════════
3168
+ /**
3169
+ * Open a simple confirmation dialog.
3170
+ * Resolves `true` if the user confirms, `false` otherwise.
3171
+ *
3172
+ * @example
3173
+ * ```ts
3174
+ * const ref = this.dialog.confirm({
3175
+ * message: 'Delete this item?',
3176
+ * component: ConfirmDialogComponent,
3177
+ * });
3178
+ * ref.afterClosed().subscribe(yes => { if (yes) ... });
3179
+ * ```
3180
+ */
3181
+ confirm(options) {
3182
+ return this.open(options.component, {
3183
+ width: options.width ?? '400px',
3184
+ disableClose: true,
3185
+ closeOnBackdrop: false,
3186
+ animation: 'fade',
3187
+ data: {
3188
+ title: options.title ?? 'Confirm',
3189
+ message: options.message,
3190
+ btnOkText: options.btnOkText ?? 'Yes',
3191
+ btnCancelText: options.btnCancelText ?? 'No',
3192
+ },
3193
+ });
3194
+ }
3195
+ /**
3196
+ * Open a simple alert dialog.
3197
+ */
3198
+ alert(options) {
3199
+ return this.open(options.component, {
3200
+ width: options.width ?? '400px',
3201
+ disableClose: true,
3202
+ closeOnBackdrop: false,
3203
+ animation: 'fade',
3204
+ data: {
3205
+ title: options.title ?? 'Alert',
3206
+ message: options.message,
3207
+ btnOkText: options.btnOkText ?? 'OK',
3208
+ },
3209
+ });
3210
+ }
3211
+ // ════════════════════════════════════════════════════════════════════
3212
+ // Private
3213
+ // ════════════════════════════════════════════════════════════════════
3214
+ /**
3215
+ * Subscribe to backdrop-click and ESC events on the CDK ref,
3216
+ * closing the dialog based on our config flags.
3217
+ *
3218
+ * We always set CDK `disableClose: true` so that CDK never auto-closes;
3219
+ * this gives us fine-grained control over `closeOnBackdrop` and
3220
+ * `closeOnEsc` independently.
3221
+ */
3222
+ _setupCloseListeners(ref, config) {
3223
+ if (config.disableClose)
3224
+ return;
3225
+ // Backdrop click
3226
+ if (config.closeOnBackdrop !== false) {
3227
+ ref.backdropClick().subscribe(() => ref.close());
3228
+ }
3229
+ // ESC key
3230
+ if (config.closeOnEsc !== false) {
3231
+ ref.keydownEvents()
3232
+ .pipe(filter((e) => e.key === 'Escape' || e.key === 'Esc'))
3233
+ .subscribe((e) => {
3234
+ e.preventDefault();
3235
+ ref.close();
3236
+ });
3237
+ }
3238
+ }
3239
+ /**
3240
+ * Build the CSS classes for the CDK overlay pane.
3241
+ * Always includes our base class; adds user-provided classes on top.
3242
+ */
3243
+ _buildPanelClasses(config) {
3244
+ const classes = ['bk-dialog-pane'];
3245
+ if (config.panelClass) {
3246
+ if (Array.isArray(config.panelClass)) {
3247
+ classes.push(...config.panelClass);
3248
+ }
3249
+ else {
3250
+ classes.push(config.panelClass);
3251
+ }
3252
+ }
3253
+ return classes;
3254
+ }
3255
+ /**
3256
+ * Build CDK's `GlobalPositionStrategy` from our position config.
3257
+ * Falls back to centred (both axes) when no position is specified.
3258
+ */
3259
+ _buildPositionStrategy(config) {
3260
+ const strategy = this._overlay.position().global();
3261
+ if (config.position?.top) {
3262
+ strategy.top(config.position.top);
3263
+ }
3264
+ else if (config.position?.bottom) {
3265
+ strategy.bottom(config.position.bottom);
3266
+ }
3267
+ else {
3268
+ strategy.centerVertically();
3269
+ }
3270
+ if (config.position?.left) {
3271
+ strategy.left(config.position.left);
3272
+ }
3273
+ else if (config.position?.right) {
3274
+ strategy.right(config.position.right);
3275
+ }
3276
+ else {
3277
+ strategy.centerHorizontally();
3278
+ }
3279
+ return strategy;
3280
+ }
3281
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3282
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DialogService, providedIn: 'root' });
3283
+ }
3284
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DialogService, decorators: [{
3285
+ type: Injectable,
3286
+ args: [{ providedIn: 'root' }]
3287
+ }] });
3288
+
3289
+ /**
3290
+ * Dialog Content Directives
3291
+ *
3292
+ * Architecture Decision:
3293
+ * ─────────────────────
3294
+ * These directives mirror Angular Material's dialog content directives:
3295
+ *
3296
+ * MatDialogTitle → BkDialogTitle
3297
+ * MatDialogContent → BkDialogContent
3298
+ * MatDialogActions → BkDialogActions
3299
+ * MatDialogClose → BkDialogClose
3300
+ *
3301
+ * Each directive:
3302
+ * 1. Applies the corresponding CSS host class (e.g. `bk-dialog-content`)
3303
+ * that is styled in `dialog-container.css` via `::ng-deep`.
3304
+ * 2. Provides extra accessibility or behavioural features on top of the
3305
+ * plain CSS class.
3306
+ *
3307
+ * Consumers can still use the raw CSS classes directly (without importing
3308
+ * these directives) for backward compatibility, but the directives are
3309
+ * preferred because they add CdkScrollable, ARIA integration, and one-click
3310
+ * close behaviour.
3311
+ *
3312
+ * @see https://github.com/angular/components/blob/main/src/material/dialog/dialog-content-directives.ts
3313
+ */
3314
+ // ──── ID Generation ──────────────────────────────────────────────────────
3315
+ let nextTitleId = 0;
3316
+ // ──── Helper ─────────────────────────────────────────────────────────────
3317
+ /**
3318
+ * Finds the closest `DialogRef` by walking up the DOM from the given
3319
+ * element to the nearest `bk-dialog-container` host, then matching its
3320
+ * `id` attribute against the currently open dialogs.
3321
+ *
3322
+ * This fallback is necessary when a directive lives inside an
3323
+ * `<ng-template>` (TemplateRef), where the dialog's custom injector is
3324
+ * not available.
3325
+ *
3326
+ * @see Material's `getClosestDialog` in `dialog-content-directives.ts`
3327
+ */
3328
+ function getClosestDialog(element, openDialogs) {
3329
+ let parent = element.nativeElement.parentElement;
3330
+ while (parent && !parent.classList.contains('bk-dialog-container')) {
3331
+ parent = parent.parentElement;
3332
+ }
3333
+ return parent
3334
+ ? openDialogs.find(ref => ref.id === parent.id) ?? null
3335
+ : null;
3336
+ }
3337
+ // ──── Base for Title / Actions ───────────────────────────────────────────
3338
+ /**
3339
+ * Shared abstract base that resolves the owning `DialogRef` (via DI or
3340
+ * DOM walk) and invokes `_onAdd()` / `_onRemove()` lifecycle hooks.
3341
+ *
3342
+ * Same pattern as Material's internal `MatDialogLayoutSection`.
3343
+ */
3344
+ class BkDialogLayoutSection {
3345
+ _dialogRef = inject(DialogRef, { optional: true });
3346
+ _elementRef = inject((ElementRef));
3347
+ _dialogService = inject(DialogService);
3348
+ ngOnInit() {
3349
+ if (!this._dialogRef) {
3350
+ this._dialogRef = getClosestDialog(this._elementRef, this._dialogService.openDialogsRef);
3351
+ }
3352
+ if (this._dialogRef) {
3353
+ Promise.resolve().then(() => this._onAdd());
3354
+ }
3355
+ }
3356
+ ngOnDestroy() {
3357
+ if (this._dialogRef) {
3358
+ Promise.resolve().then(() => this._onRemove());
3359
+ }
3360
+ }
3361
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogLayoutSection, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3362
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: BkDialogLayoutSection, isStandalone: true, ngImport: i0 });
3363
+ }
3364
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogLayoutSection, decorators: [{
3365
+ type: Directive,
3366
+ args: [{ standalone: true }]
3367
+ }] });
3368
+ // ════════════════════════════════════════════════════════════════════════
3369
+ // BkDialogTitle
3370
+ // ════════════════════════════════════════════════════════════════════════
3371
+ /**
3372
+ * Marks an element as the dialog title.
3373
+ *
3374
+ * • Generates a unique `id` and binds it to the host element.
3375
+ * • Registers the `id` with the CDK container's `aria-labelledby` queue
3376
+ * so screen readers announce the dialog title automatically.
3377
+ *
3378
+ * Usage:
3379
+ * ```html
3380
+ * <h2 bk-dialog-title>Edit User</h2>
3381
+ * ```
3382
+ */
3383
+ class BkDialogTitle extends BkDialogLayoutSection {
3384
+ /** Unique element `id`. Auto-generated but can be overridden. */
3385
+ id = `bk-dialog-title-${nextTitleId++}`;
3386
+ _onAdd() {
3387
+ // Register with the CdkDialogContainer's aria-labelledby queue.
3388
+ // _containerInstance extends CdkDialogContainer which has _addAriaLabelledBy.
3389
+ this._dialogRef?._containerInstance?._addAriaLabelledBy?.(this.id);
3390
+ }
3391
+ _onRemove() {
3392
+ this._dialogRef?._containerInstance?._removeAriaLabelledBy?.(this.id);
3393
+ }
3394
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogTitle, deps: null, target: i0.ɵɵFactoryTarget.Directive });
3395
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: BkDialogTitle, isStandalone: true, selector: "[bk-dialog-title], [bkDialogTitle]", inputs: { id: "id" }, host: { properties: { "id": "id" }, classAttribute: "bk-dialog-title" }, exportAs: ["bkDialogTitle"], usesInheritance: true, ngImport: i0 });
3396
+ }
3397
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogTitle, decorators: [{
3398
+ type: Directive,
3399
+ args: [{
3400
+ selector: '[bk-dialog-title], [bkDialogTitle]',
3401
+ standalone: true,
3402
+ exportAs: 'bkDialogTitle',
3403
+ host: {
3404
+ 'class': 'bk-dialog-title',
3405
+ '[id]': 'id',
3406
+ },
3407
+ }]
3408
+ }], propDecorators: { id: [{
3409
+ type: Input
3410
+ }] } });
3411
+ // ════════════════════════════════════════════════════════════════════════
3412
+ // BkDialogContent
3413
+ // ════════════════════════════════════════════════════════════════════════
3414
+ /**
3415
+ * Scrollable content area of a dialog.
3416
+ *
3417
+ * • Applies the `bk-dialog-content` CSS class → `flex: 1 1 auto; overflow: auto`.
3418
+ * • Composes CDK's `CdkScrollable` for scroll-position monitoring and
3419
+ * integration with CDK's scroll dispatcher.
3420
+ *
3421
+ * Usage (attribute):
3422
+ * ```html
3423
+ * <div bk-dialog-content>
3424
+ * <!-- body content — this area scrolls -->
3425
+ * </div>
3426
+ * ```
3427
+ *
3428
+ * Usage (element):
3429
+ * ```html
3430
+ * <bk-dialog-content>...</bk-dialog-content>
3431
+ * ```
3432
+ */
3433
+ class BkDialogContent {
3434
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3435
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: BkDialogContent, isStandalone: true, selector: "[bk-dialog-content], bk-dialog-content, [bkDialogContent]", host: { classAttribute: "bk-dialog-content" }, exportAs: ["bkDialogContent"], hostDirectives: [{ directive: i1$1.CdkScrollable }], ngImport: i0 });
3436
+ }
3437
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogContent, decorators: [{
3438
+ type: Directive,
3439
+ args: [{
3440
+ selector: '[bk-dialog-content], bk-dialog-content, [bkDialogContent]',
3441
+ standalone: true,
3442
+ exportAs: 'bkDialogContent',
3443
+ host: {
3444
+ 'class': 'bk-dialog-content',
3445
+ },
3446
+ hostDirectives: [CdkScrollable],
3447
+ }]
3448
+ }] });
3449
+ // ════════════════════════════════════════════════════════════════════════
3450
+ // BkDialogActions
3451
+ // ════════════════════════════════════════════════════════════════════════
3452
+ /**
3453
+ * Container for action buttons at the bottom of a dialog.
3454
+ * Stays pinned when the content area scrolls.
3455
+ *
3456
+ * • Applies `bk-dialog-actions` class → `flex: 0 0 auto` (never shrinks).
3457
+ * • Optional `align` input controls horizontal alignment.
3458
+ *
3459
+ * Usage:
3460
+ * ```html
3461
+ * <div bk-dialog-actions>
3462
+ * <button (click)="cancel()">Cancel</button>
3463
+ * <button (click)="save()">Save</button>
3464
+ * </div>
3465
+ *
3466
+ * <!-- With alignment -->
3467
+ * <div bk-dialog-actions align="center">...</div>
3468
+ * ```
3469
+ */
3470
+ class BkDialogActions extends BkDialogLayoutSection {
3471
+ /** Horizontal alignment of action buttons. */
3472
+ align;
3473
+ _onAdd() {
3474
+ // Future: notify container for action section count (like Material).
3475
+ }
3476
+ _onRemove() {
3477
+ // No-op.
3478
+ }
3479
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogActions, deps: null, target: i0.ɵɵFactoryTarget.Directive });
3480
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: BkDialogActions, isStandalone: true, selector: "[bk-dialog-actions], bk-dialog-actions, [bkDialogActions]", inputs: { align: "align" }, host: { properties: { "class.bk-dialog-actions-align-start": "align === \"start\"", "class.bk-dialog-actions-align-center": "align === \"center\"", "class.bk-dialog-actions-align-end": "align === \"end\"" }, classAttribute: "bk-dialog-actions" }, exportAs: ["bkDialogActions"], usesInheritance: true, ngImport: i0 });
3481
+ }
3482
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogActions, decorators: [{
3483
+ type: Directive,
3484
+ args: [{
3485
+ selector: '[bk-dialog-actions], bk-dialog-actions, [bkDialogActions]',
3486
+ standalone: true,
3487
+ exportAs: 'bkDialogActions',
3488
+ host: {
3489
+ 'class': 'bk-dialog-actions',
3490
+ '[class.bk-dialog-actions-align-start]': 'align === "start"',
3491
+ '[class.bk-dialog-actions-align-center]': 'align === "center"',
3492
+ '[class.bk-dialog-actions-align-end]': 'align === "end"',
3493
+ },
3494
+ }]
3495
+ }], propDecorators: { align: [{
3496
+ type: Input
3497
+ }] } });
3498
+ // ════════════════════════════════════════════════════════════════════════
3499
+ // BkDialogClose
3500
+ // ════════════════════════════════════════════════════════════════════════
3501
+ /**
3502
+ * Closes the dialog when the host element is clicked.
3503
+ * Optionally returns a result value to the opener.
3504
+ *
3505
+ * • Sets `type="button"` by default to prevent accidental form submissions.
3506
+ * • Supports an optional `aria-label` for screen readers.
3507
+ *
3508
+ * Usage:
3509
+ * ```html
3510
+ * <!-- Close with no result -->
3511
+ * <button bk-dialog-close>Cancel</button>
3512
+ *
3513
+ * <!-- Close with a result value -->
3514
+ * <button [bk-dialog-close]="'confirmed'">OK</button>
3515
+ *
3516
+ * <!-- Alternative camelCase binding -->
3517
+ * <button [bkDialogClose]="myResult">Done</button>
3518
+ * ```
3519
+ */
3520
+ class BkDialogClose {
3521
+ _dialogRef = inject(DialogRef, { optional: true });
3522
+ _elementRef = inject((ElementRef));
3523
+ _dialogService = inject(DialogService);
3524
+ /** Screen-reader label for the button. */
3525
+ ariaLabel;
3526
+ /** Prevents accidental form submits. Default: `'button'`. */
3527
+ type = 'button';
3528
+ /** Dialog result — set via `[bk-dialog-close]="value"`. */
3529
+ dialogResult;
3530
+ /** Alternative camelCase binding: `[bkDialogClose]="value"`. */
3531
+ _bkDialogClose;
3532
+ ngOnInit() {
3533
+ if (!this._dialogRef) {
3534
+ this._dialogRef = getClosestDialog(this._elementRef, this._dialogService.openDialogsRef);
3535
+ }
3536
+ }
3537
+ ngOnChanges(changes) {
3538
+ const proxiedChange = changes['_bkDialogClose'] || changes['dialogResult'];
3539
+ if (proxiedChange) {
3540
+ this.dialogResult = proxiedChange.currentValue;
3541
+ }
3542
+ }
3543
+ _onButtonClick(_event) {
3544
+ this._dialogRef?.close(this.dialogResult);
3545
+ }
3546
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogClose, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3547
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.16", type: BkDialogClose, isStandalone: true, selector: "[bk-dialog-close], [bkDialogClose]", inputs: { ariaLabel: ["aria-label", "ariaLabel"], type: "type", dialogResult: ["bk-dialog-close", "dialogResult"], _bkDialogClose: ["bkDialogClose", "_bkDialogClose"] }, host: { listeners: { "click": "_onButtonClick($event)" }, properties: { "attr.aria-label": "ariaLabel || null", "attr.type": "type" } }, exportAs: ["bkDialogClose"], usesOnChanges: true, ngImport: i0 });
3548
+ }
3549
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogClose, decorators: [{
3550
+ type: Directive,
3551
+ args: [{
3552
+ selector: '[bk-dialog-close], [bkDialogClose]',
3553
+ standalone: true,
3554
+ exportAs: 'bkDialogClose',
3555
+ host: {
3556
+ '(click)': '_onButtonClick($event)',
3557
+ '[attr.aria-label]': 'ariaLabel || null',
3558
+ '[attr.type]': 'type',
3559
+ },
3560
+ }]
3561
+ }], propDecorators: { ariaLabel: [{
3562
+ type: Input,
3563
+ args: ['aria-label']
3564
+ }], type: [{
3565
+ type: Input
3566
+ }], dialogResult: [{
3567
+ type: Input,
3568
+ args: ['bk-dialog-close']
3569
+ }], _bkDialogClose: [{
3570
+ type: Input,
3571
+ args: ['bkDialogClose']
3572
+ }] } });
3573
+
3574
+ /**
3575
+ * BkDialogModule
3576
+ *
3577
+ * Optional NgModule wrapper for projects that prefer module-based usage.
3578
+ *
3579
+ * Architecture Decision:
3580
+ * ─────────────────────
3581
+ * Follows the exact same pattern as Angular Material's `MatDialogModule`
3582
+ * and `@brickclay-org/ui`'s `CalendarModule`:
3583
+ *
3584
+ * • All components/directives are **standalone**.
3585
+ * • This module simply imports + re-exports them for convenience.
3586
+ * • `DialogService` is `providedIn: 'root'`, so it does NOT need to
3587
+ * be listed in `providers` here — it is tree-shakeable and available
3588
+ * app-wide automatically.
3589
+ *
3590
+ * Two usage styles:
3591
+ *
3592
+ * ───────── Module-based (NgModule) ─────────
3593
+ * ```ts
3594
+ * import { BkDialogModule } from '@shared/components/dialog';
3595
+ *
3596
+ * @NgModule({ imports: [BkDialogModule] })
3597
+ * export class FuelCostModule {}
3598
+ * ```
3599
+ *
3600
+ * ───────── Standalone ──────────────────────
3601
+ * ```ts
3602
+ * @Component({
3603
+ * standalone: true,
3604
+ * imports: [BkDialogContent, BkDialogActions, BkDialogClose],
3605
+ * })
3606
+ * export class MyDialog {}
3607
+ * ```
3608
+ *
3609
+ * @see https://github.com/angular/components/blob/main/src/material/dialog/dialog-module.ts
3610
+ */
3611
+ class BkDialogModule {
3612
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
3613
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.16", ngImport: i0, type: BkDialogModule, imports: [
3614
+ // ── CDK foundations ──────────────────────────────────────────────
3615
+ DialogModule,
3616
+ OverlayModule,
3617
+ PortalModule,
3618
+ // ── Our standalone pieces ───────────────────────────────────────
3619
+ DialogContainerComponent,
3620
+ BkDialogTitle,
3621
+ BkDialogContent,
3622
+ BkDialogActions,
3623
+ BkDialogClose], exports: [
3624
+ // ── Public API for template usage ───────────────────────────────
3625
+ // Consumers import BkDialogModule and get these directives in
3626
+ // their templates automatically — just like MatDialogModule.
3627
+ BkDialogTitle,
3628
+ BkDialogContent,
3629
+ BkDialogActions,
3630
+ BkDialogClose] });
3631
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogModule, imports: [
3632
+ // ── CDK foundations ──────────────────────────────────────────────
3633
+ DialogModule,
3634
+ OverlayModule,
3635
+ PortalModule] });
3636
+ }
3637
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BkDialogModule, decorators: [{
3638
+ type: NgModule,
3639
+ args: [{
3640
+ imports: [
3641
+ // ── CDK foundations ──────────────────────────────────────────────
3642
+ DialogModule,
3643
+ OverlayModule,
3644
+ PortalModule,
3645
+ // ── Our standalone pieces ───────────────────────────────────────
3646
+ DialogContainerComponent,
3647
+ BkDialogTitle,
3648
+ BkDialogContent,
3649
+ BkDialogActions,
3650
+ BkDialogClose,
3651
+ ],
3652
+ exports: [
3653
+ // ── Public API for template usage ───────────────────────────────
3654
+ // Consumers import BkDialogModule and get these directives in
3655
+ // their templates automatically — just like MatDialogModule.
3656
+ BkDialogTitle,
3657
+ BkDialogContent,
3658
+ BkDialogActions,
3659
+ BkDialogClose,
3660
+ ],
3661
+ }]
3662
+ }] });
3663
+
2463
3664
  /*
2464
3665
  * Public API Surface of brickclay-lib
2465
3666
  */
@@ -2469,5 +3670,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2469
3670
  * Generated bundle index. Do not edit.
2470
3671
  */
2471
3672
 
2472
- export { BrickclayIcons, BrickclayLib, CalendarManagerService, CalendarModule, CheckboxComponent, CustomCalendarComponent, RadioComponent, ScheduledDatePickerComponent, TimePickerComponent, ToggleComponent };
3673
+ export { BkDialogActions, BkDialogClose, BkDialogContent, BkDialogModule, BkDialogTitle, BrickclayIcons, BrickclayLib, CalendarManagerService, CalendarModule, CheckboxComponent, CustomCalendarComponent, DEFAULT_DIALOG_CONFIG, DIALOG_DATA, DIALOG_GLOBAL_CONFIG, DialogRef, DialogService, RadioComponent, ScheduledDatePickerComponent, TimePickerComponent, ToggleComponent, getDialogBackdropAnimation, getDialogPanelAnimation };
2473
3674
  //# sourceMappingURL=minahil.mjs.map