codexly-ui 0.0.77 → 0.0.79

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,16 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, computed, ChangeDetectionStrategy, ViewEncapsulation, Component, effect, signal, inject, ElementRef, InjectionToken, PLATFORM_ID, Injectable, Renderer2, ApplicationRef, EnvironmentInjector, DestroyRef, createComponent, Directive, output, contentChildren, NgZone, model, untracked, forwardRef, viewChild, HostListener, ViewChildren, contentChild, Injector, ViewChild, ChangeDetectorRef, ContentChildren, Input, numberAttribute, booleanAttribute, runInInjectionContext } from '@angular/core';
2
+ import { input, computed, ChangeDetectionStrategy, ViewEncapsulation, Component, effect, signal, inject, ElementRef, InjectionToken, PLATFORM_ID, Injectable, Renderer2, ApplicationRef, EnvironmentInjector, DestroyRef, createComponent, Directive, output, contentChildren, NgZone, model, untracked, forwardRef, viewChild, HostListener, ViewChildren, contentChild, Injector, ViewChild, ChangeDetectorRef, ContentChildren, Input, numberAttribute, booleanAttribute } from '@angular/core';
3
3
  import { isPlatformBrowser, CurrencyPipe, NgTemplateOutlet, DecimalPipe, NgStyle, DOCUMENT } from '@angular/common';
4
4
  import * as i1 from '@angular/forms';
5
5
  import { NG_VALUE_ACCESSOR, FormsModule, NgControl, FormControl, ReactiveFormsModule, FormGroup, Validators } from '@angular/forms';
6
6
  import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
7
  import { startWith, map, debounceTime, distinctUntilChanged, switchMap, of, catchError } from 'rxjs';
8
8
  import * as i1$1 from '@angular/cdk/overlay';
9
- import { ScrollStrategyOptions, OverlayModule, Overlay, OverlayConfig, OverlayContainer } from '@angular/cdk/overlay';
9
+ import { ScrollStrategyOptions, OverlayModule } from '@angular/cdk/overlay';
10
10
  import { Chart, LineController, BarController, PieController, DoughnutController, RadarController, PolarAreaController, CategoryScale, LinearScale, RadialLinearScale, BarElement, LineElement, PointElement, ArcElement, Filler, Tooltip, Legend } from 'chart.js';
11
- import * as i1$2 from '@angular/cdk/portal';
12
- import { PortalModule, ComponentPortal } from '@angular/cdk/portal';
13
- import { FocusTrapFactory } from '@angular/cdk/a11y';
14
11
  import { CdkScrollable } from '@angular/cdk/scrolling';
15
12
  import { RouterLink, RouterLinkActive } from '@angular/router';
16
13
 
@@ -8906,122 +8903,100 @@ const EDITOR_SIZE_MAP = {
8906
8903
  };
8907
8904
 
8908
8905
  /**
8909
- * Internal shell componentmounted via ComponentPortal onto the CDK overlay.
8910
- * Renders the user's component via a second ComponentPortal inside cdkPortalOutlet.
8911
- * Owns the enter / exit animation so every modal gets consistent motion.
8906
+ * Minimal overlay engineno CDK, no stacking context wars.
8907
+ * Creates a fixed container div in document.body with explicit z-index.
8912
8908
  */
8913
- class ClxModalWrapperComponent {
8914
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
8915
- portal = model(null, ...(ngDevMode ? [{ debugName: "portal" }] : /* istanbul ignore next */ []));
8916
- _animate = inject(ClxAnimateService);
8917
- _el = inject((ElementRef));
8918
- _animConfig = inject(CLX_MODAL_ANIM_CONFIG, { optional: true })
8919
- ?? MODAL_ANIM_DEFAULTS;
8920
- /** Resolved by ClxModalService so closeWithAnimation can call through */
8921
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
8922
- onClose;
8923
- ngOnInit() {
8924
- const target = this._panel();
8925
- if (target) {
8926
- this._animate.animate(target, {
8927
- animation: this._animConfig.animationIn,
8928
- duration: this._animConfig.animationDuration,
8929
- trigger: 'manual',
8930
- });
8931
- }
8909
+ class ClxNativeOverlayService {
8910
+ _appRef = inject(ApplicationRef);
8911
+ _injector = inject(Injector);
8912
+ /** Block body scroll like CDK used to */
8913
+ _scrollBlocked = 0;
8914
+ mount(component, injector, options) {
8915
+ // ── backdrop ────────────────────────────────────────────────────────────
8916
+ let backdropEl = null;
8917
+ if (options.backdrop) {
8918
+ backdropEl = document.createElement('div');
8919
+ backdropEl.style.cssText =
8920
+ `position:fixed;inset:0;z-index:${options.zIndex};`;
8921
+ if (options.backdropClass)
8922
+ backdropEl.className = options.backdropClass;
8923
+ document.body.appendChild(backdropEl);
8924
+ }
8925
+ // ── container ────────────────────────────────────────────────────────────
8926
+ const container = document.createElement('div');
8927
+ container.style.cssText =
8928
+ `position:fixed;inset:0;z-index:${options.zIndex + 1};pointer-events:none;display:flex;align-items:center;justify-content:center;padding:1rem;box-sizing:border-box;`;
8929
+ if (options.containerStyle) {
8930
+ Object.assign(container.style, options.containerStyle);
8931
+ }
8932
+ document.body.appendChild(container);
8933
+ // ── component ────────────────────────────────────────────────────────────
8934
+ const compRef = createComponent(component, {
8935
+ environmentInjector: this._appRef.injector,
8936
+ elementInjector: injector,
8937
+ });
8938
+ this._appRef.attachView(compRef.hostView);
8939
+ const el = compRef.location.nativeElement;
8940
+ el.style.pointerEvents = 'auto';
8941
+ container.appendChild(el);
8942
+ // ── scroll block ─────────────────────────────────────────────────────────
8943
+ this._blockScroll();
8944
+ const overlayRef = {
8945
+ backdrop: backdropEl,
8946
+ container,
8947
+ destroy: () => {
8948
+ compRef.destroy();
8949
+ backdropEl?.remove();
8950
+ container.remove();
8951
+ this._unblockScroll();
8952
+ },
8953
+ };
8954
+ return { compRef, overlayRef };
8932
8955
  }
8933
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
8934
- closeWithAnimation(result) {
8935
- const target = this._panel();
8936
- if (!target) {
8937
- this.onClose?.(result);
8938
- return;
8956
+ _blockScroll() {
8957
+ this._scrollBlocked++;
8958
+ if (this._scrollBlocked === 1) {
8959
+ document.body.style.overflow = 'hidden';
8939
8960
  }
8940
- this._animate
8941
- .animate(target, {
8942
- animation: this._animConfig.animationOut,
8943
- duration: this._animConfig.animationDuration,
8944
- trigger: 'manual',
8945
- fillMode: 'forwards',
8946
- })
8947
- .then(() => this.onClose?.(result));
8948
8961
  }
8949
- /**
8950
- * Returns the first real element child of this wrapper — which is the
8951
- * rendered panel (clx-modal host) inserted by cdkPortalOutlet.
8952
- * The wrapper itself is display:contents so the panel is its direct child.
8953
- */
8954
- _panel() {
8955
- return this._el.nativeElement.firstElementChild;
8962
+ _unblockScroll() {
8963
+ this._scrollBlocked = Math.max(0, this._scrollBlocked - 1);
8964
+ if (this._scrollBlocked === 0) {
8965
+ document.body.style.overflow = '';
8966
+ }
8956
8967
  }
8957
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8958
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.15", type: ClxModalWrapperComponent, isStandalone: true, selector: "clx-modal-wrapper", inputs: { portal: { classPropertyName: "portal", publicName: "portal", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { portal: "portalChange" }, host: { styleAttribute: "display:contents" }, ngImport: i0, template: `<ng-container [cdkPortalOutlet]="portal()" />`, isInline: true, dependencies: [{ kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i1$2.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
8968
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNativeOverlayService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
8969
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNativeOverlayService, providedIn: 'root' });
8959
8970
  }
8960
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalWrapperComponent, decorators: [{
8961
- type: Component,
8962
- args: [{
8963
- selector: 'clx-modal-wrapper',
8964
- standalone: true,
8965
- imports: [PortalModule],
8966
- template: `<ng-container [cdkPortalOutlet]="portal()" />`,
8967
- encapsulation: ViewEncapsulation.None,
8968
- changeDetection: ChangeDetectionStrategy.OnPush,
8969
- host: { style: 'display:contents' },
8970
- }]
8971
- }], propDecorators: { portal: [{ type: i0.Input, args: [{ isSignal: true, alias: "portal", required: false }] }, { type: i0.Output, args: ["portalChange"] }] } });
8971
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNativeOverlayService, decorators: [{
8972
+ type: Injectable,
8973
+ args: [{ providedIn: 'root' }]
8974
+ }] });
8972
8975
 
8976
+ const SIZE_MAX_WIDTH = {
8977
+ sm: '480px',
8978
+ md: '680px',
8979
+ lg: '900px',
8980
+ xl: '1200px',
8981
+ full: '100vw',
8982
+ };
8973
8983
  class ClxModalService {
8974
- _overlay;
8975
- _injector;
8976
- _focusTrapFactory;
8977
- constructor() {
8978
- this._overlay = inject(Overlay);
8979
- this._injector = inject(Injector);
8980
- this._focusTrapFactory = inject(FocusTrapFactory);
8981
- }
8984
+ _overlay = inject(ClxNativeOverlayService);
8985
+ _animate = inject(ClxAnimateService);
8986
+ _injector = inject(Injector);
8982
8987
  open(
8983
8988
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8984
8989
  component, config = {}) {
8985
- const { hasBackdrop = true, disableClose = false, panelClass = [], size = 'md', data, animationIn = MODAL_ANIM_DEFAULTS.animationIn, animationOut = MODAL_ANIM_DEFAULTS.animationOut, animationDuration = MODAL_ANIM_DEFAULTS.animationDuration, } = config;
8990
+ const { hasBackdrop = true, disableClose = false, size = 'md', data, animationIn = MODAL_ANIM_DEFAULTS.animationIn, animationOut = MODAL_ANIM_DEFAULTS.animationOut, animationDuration = MODAL_ANIM_DEFAULTS.animationDuration, } = config;
8986
8991
  const animConfig = { animationIn, animationOut, animationDuration };
8992
+ const maxWidth = SIZE_MAX_WIDTH[size] ?? '680px';
8987
8993
  let _resolve;
8988
8994
  const _afterClosed = new Promise(res => (_resolve = res));
8989
- let _focusTrap = null;
8990
8995
  let _closing = false;
8991
- /** Plays exit animation then tears down the overlay. */
8992
- const closeViaAnimation = (result) => {
8993
- if (_closing)
8994
- return;
8995
- _closing = true;
8996
- wrapperRef.instance.closeWithAnimation(result);
8997
- };
8998
8996
  const modalRef = {
8999
8997
  close(result) { closeViaAnimation(result); },
9000
8998
  afterClosed() { return _afterClosed; },
9001
8999
  };
9002
- const extraClasses = Array.isArray(panelClass) ? panelClass : [panelClass];
9003
- const sizeMaxWidth = {
9004
- sm: '480px',
9005
- md: '680px',
9006
- lg: '900px',
9007
- xl: '1200px',
9008
- full: '100vw',
9009
- };
9010
- const maxWidth = sizeMaxWidth[size] ?? '600px';
9011
- const overlayRef = this._overlay.create(new OverlayConfig({
9012
- hasBackdrop,
9013
- backdropClass: 'clx-modal-backdrop',
9014
- panelClass: extraClasses,
9015
- scrollStrategy: this._overlay.scrollStrategies.block(),
9016
- positionStrategy: this._overlay.position().global().centerHorizontally().centerVertically(),
9017
- width: `min(calc(100vw - 2rem), ${maxWidth})`,
9018
- maxWidth: maxWidth,
9019
- maxHeight: '90vh',
9020
- }));
9021
- Object.assign(overlayRef.overlayElement.style, {
9022
- display: 'flex',
9023
- flexDirection: 'column',
9024
- });
9025
9000
  const innerInjector = Injector.create({
9026
9001
  parent: this._injector,
9027
9002
  providers: [
@@ -9031,27 +9006,49 @@ class ClxModalService {
9031
9006
  { provide: CLX_MODAL_SIZE, useValue: size },
9032
9007
  ],
9033
9008
  });
9034
- // Attach wrapper CDK creates the backdrop on attach()
9035
- const wrapperPortal = new ComponentPortal(ClxModalWrapperComponent, null, innerInjector);
9036
- const wrapperRef = overlayRef.attach(wrapperPortal);
9037
- // Wire the actual teardown through the wrapper instance
9038
- wrapperRef.instance.onClose = (result) => {
9039
- _focusTrap?.destroy();
9040
- overlayRef.dispose();
9041
- _resolve(result);
9009
+ const { compRef, overlayRef } = this._overlay.mount(component, innerInjector, {
9010
+ zIndex: 1000,
9011
+ backdrop: hasBackdrop,
9012
+ backdropClass: 'clx-modal-backdrop',
9013
+ });
9014
+ const panelEl = compRef.location.nativeElement;
9015
+ panelEl.style.width = `min(calc(100vw - 2rem), ${maxWidth})`;
9016
+ panelEl.style.maxWidth = maxWidth;
9017
+ panelEl.style.maxHeight = '90vh';
9018
+ // Enter animation
9019
+ this._animate.animate(panelEl, {
9020
+ animation: animationIn,
9021
+ duration: animationDuration,
9022
+ trigger: 'manual',
9023
+ });
9024
+ const closeViaAnimation = (result) => {
9025
+ if (_closing)
9026
+ return;
9027
+ _closing = true;
9028
+ this._animate.animate(panelEl, {
9029
+ animation: animationOut,
9030
+ duration: animationDuration,
9031
+ trigger: 'manual',
9032
+ fillMode: 'forwards',
9033
+ }).then(() => {
9034
+ overlayRef.destroy();
9035
+ _resolve(result);
9036
+ });
9042
9037
  };
9043
- // Mount user component inside the wrapper via a second portal
9044
- const contentPortal = new ComponentPortal(component, null, innerInjector);
9045
- wrapperRef.instance.portal.set(contentPortal);
9046
- if (hasBackdrop && !disableClose) {
9047
- overlayRef.backdropClick().subscribe(() => closeViaAnimation());
9038
+ // Backdrop click
9039
+ if (hasBackdrop && !disableClose && overlayRef.backdrop) {
9040
+ overlayRef.backdrop.addEventListener('click', () => closeViaAnimation());
9048
9041
  }
9042
+ // Escape key
9049
9043
  if (!disableClose) {
9050
- overlayRef.keydownEvents().subscribe(e => { if (e.key === 'Escape')
9051
- closeViaAnimation(); });
9044
+ const onKey = (e) => {
9045
+ if (e.key === 'Escape') {
9046
+ closeViaAnimation();
9047
+ document.removeEventListener('keydown', onKey);
9048
+ }
9049
+ };
9050
+ document.addEventListener('keydown', onKey);
9052
9051
  }
9053
- _focusTrap = this._focusTrapFactory.create(overlayRef.overlayElement);
9054
- _focusTrap.focusInitialElementWhenReady();
9055
9052
  return modalRef;
9056
9053
  }
9057
9054
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
@@ -9060,7 +9057,7 @@ class ClxModalService {
9060
9057
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalService, decorators: [{
9061
9058
  type: Injectable,
9062
9059
  args: [{ providedIn: 'root' }]
9063
- }], ctorParameters: () => [] });
9060
+ }] });
9064
9061
 
9065
9062
  let _clxEditorId = 0;
9066
9063
  const TOOLBAR_GROUPS = [
@@ -9804,6 +9801,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
9804
9801
  }] } });
9805
9802
 
9806
9803
  const SWITCH_SIZE_MAP = {
9804
+ xs: { track: 'w-7 h-4', thumb: 'w-3 h-3', translate: 'translate-x-3', label: 'text-xs' },
9807
9805
  sm: { track: 'w-9 h-5', thumb: 'w-4 h-4', translate: 'translate-x-4', label: 'text-xs' },
9808
9806
  md: { track: 'w-12 h-6', thumb: 'w-5 h-5', translate: 'translate-x-6', label: 'text-sm' },
9809
9807
  lg: { track: 'w-14 h-7', thumb: 'w-6 h-6', translate: 'translate-x-7', label: 'text-base' },
@@ -13795,45 +13793,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
13795
13793
  }] } });
13796
13794
 
13797
13795
  class ClxAlertService {
13798
- _overlay;
13799
- _injector;
13800
- _focusTrapFactory;
13801
- constructor() {
13802
- this._overlay = inject(Overlay);
13803
- this._injector = inject(Injector);
13804
- this._focusTrapFactory = inject(FocusTrapFactory);
13805
- }
13806
- // ─── Core open ────────────────────────────────────────────────────────────
13796
+ _overlay = inject(ClxNativeOverlayService);
13797
+ _injector = inject(Injector);
13807
13798
  open(options = {}) {
13808
13799
  const backdrop = options.backdrop ?? ALERT_DEFAULTS.backdrop;
13809
13800
  const disableClose = options.disableClose ?? ALERT_DEFAULTS.disableClose;
13810
13801
  let _resolve;
13811
13802
  const promise = new Promise(res => (_resolve = res));
13812
- let _focusTrap = null;
13813
- // Wrap resolve so we can dispose the overlay after the exit animation
13814
13803
  const resolve = (result) => {
13815
- _focusTrap?.destroy();
13816
- overlayRef.dispose();
13804
+ overlayRef.destroy();
13817
13805
  _resolve(result);
13818
13806
  };
13819
- const overlayRef = this._overlay.create(new OverlayConfig({
13820
- hasBackdrop: backdrop,
13821
- backdropClass: 'clx-alert-backdrop',
13822
- scrollStrategy: this._overlay.scrollStrategies.block(),
13823
- positionStrategy: this._overlay.position().global().centerHorizontally().centerVertically(),
13824
- }));
13825
- // Make the overlay pane a flex centering layer
13826
- Object.assign(overlayRef.overlayElement.style, {
13827
- position: 'fixed',
13828
- inset: '0',
13829
- width: '100vw',
13830
- height: '100vh',
13831
- display: 'flex',
13832
- alignItems: 'center',
13833
- justifyContent: 'center',
13834
- padding: '1rem',
13835
- boxSizing: 'border-box',
13836
- });
13837
13807
  const innerInjector = Injector.create({
13838
13808
  parent: this._injector,
13839
13809
  providers: [
@@ -13841,19 +13811,25 @@ class ClxAlertService {
13841
13811
  { provide: CLX_ALERT_RESOLVE, useValue: resolve },
13842
13812
  ],
13843
13813
  });
13844
- overlayRef.attach(new ComponentPortal(ClxAlertComponent, null, innerInjector));
13845
- if (backdrop && !disableClose) {
13846
- overlayRef.backdropClick().subscribe(() => resolve(false));
13814
+ const { overlayRef } = this._overlay.mount(ClxAlertComponent, innerInjector, {
13815
+ zIndex: 1000,
13816
+ backdrop,
13817
+ backdropClass: 'clx-alert-backdrop',
13818
+ });
13819
+ if (backdrop && !disableClose && overlayRef.backdrop) {
13820
+ overlayRef.backdrop.addEventListener('click', () => resolve(false));
13847
13821
  }
13848
13822
  if (!disableClose) {
13849
- overlayRef.keydownEvents().subscribe(e => { if (e.key === 'Escape')
13850
- resolve(false); });
13823
+ const onKey = (e) => {
13824
+ if (e.key === 'Escape') {
13825
+ resolve(false);
13826
+ document.removeEventListener('keydown', onKey);
13827
+ }
13828
+ };
13829
+ document.addEventListener('keydown', onKey);
13851
13830
  }
13852
- _focusTrap = this._focusTrapFactory.create(overlayRef.overlayElement);
13853
- _focusTrap.focusInitialElementWhenReady();
13854
13831
  return promise;
13855
13832
  }
13856
- // ─── Convenience shortcuts ────────────────────────────────────────────────
13857
13833
  success(title, message, opts = {}) {
13858
13834
  return this.open({ type: 'success', title, message, ...opts });
13859
13835
  }
@@ -13867,13 +13843,7 @@ class ClxAlertService {
13867
13843
  return this.open({ type: 'info', title, message, ...opts });
13868
13844
  }
13869
13845
  confirm(title, message, opts = {}) {
13870
- return this.open({
13871
- type: 'confirm',
13872
- title,
13873
- message,
13874
- showCancelButton: true,
13875
- ...opts,
13876
- });
13846
+ return this.open({ type: 'confirm', title, message, showCancelButton: true, ...opts });
13877
13847
  }
13878
13848
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAlertService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
13879
13849
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAlertService, providedIn: 'root' });
@@ -13881,42 +13851,23 @@ class ClxAlertService {
13881
13851
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAlertService, decorators: [{
13882
13852
  type: Injectable,
13883
13853
  args: [{ providedIn: 'root' }]
13884
- }], ctorParameters: () => [] });
13854
+ }] });
13885
13855
 
13886
13856
  class ClxDrawerService {
13887
- _overlay;
13888
- _injector;
13889
- _focusTrapFactory;
13890
- constructor() {
13891
- this._overlay = inject(Overlay);
13892
- this._injector = inject(Injector);
13893
- this._focusTrapFactory = inject(FocusTrapFactory);
13894
- }
13857
+ _overlay = inject(ClxNativeOverlayService);
13858
+ _animate = inject(ClxAnimateService);
13859
+ _injector = inject(Injector);
13895
13860
  open(
13896
13861
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
13897
13862
  component, config = {}) {
13898
- const { hasBackdrop = false, disableClose = false, panelClass = [], size = 'md', data, } = config;
13863
+ const { hasBackdrop = false, disableClose = false, size = 'md', data, } = config;
13899
13864
  let _resolve;
13900
13865
  const _afterClosed = new Promise(res => (_resolve = res));
13901
- let _focusTrap = null;
13866
+ let _closing = false;
13902
13867
  const drawerRef = {
13903
- close(result) {
13904
- _focusTrap?.destroy();
13905
- overlayRef.dispose();
13906
- _resolve(result);
13907
- },
13908
- afterClosed() {
13909
- return _afterClosed;
13910
- },
13868
+ close(result) { closeViaAnimation(result); },
13869
+ afterClosed() { return _afterClosed; },
13911
13870
  };
13912
- const extraClasses = Array.isArray(panelClass) ? panelClass : [panelClass];
13913
- const overlayRef = this._overlay.create(new OverlayConfig({
13914
- hasBackdrop,
13915
- backdropClass: 'clx-modal-backdrop',
13916
- panelClass: extraClasses,
13917
- scrollStrategy: this._overlay.scrollStrategies.block(),
13918
- positionStrategy: this._overlay.position().global().right('0').top('0'),
13919
- }));
13920
13871
  const innerInjector = Injector.create({
13921
13872
  parent: this._injector,
13922
13873
  providers: [
@@ -13925,26 +13876,51 @@ class ClxDrawerService {
13925
13876
  { provide: CLX_DRAWER_SIZE, useValue: size },
13926
13877
  ],
13927
13878
  });
13928
- // Attach wrapper via ComponentPortal CDK creates backdrop on attach()
13929
- const wrapperPortal = new ComponentPortal(ClxModalWrapperComponent, null, innerInjector);
13930
- const wrapperRef = overlayRef.attach(wrapperPortal);
13931
- // El pane ya tiene width/height del OverlayConfig.
13932
- // Solo desactivamos pointer-events en el overlay element (no en el pane)
13933
- // para que el click-through funcione cuando no hay backdrop.
13934
- overlayRef.overlayElement.style.pointerEvents = 'none';
13935
- wrapperRef.location.nativeElement.style.pointerEvents = 'auto';
13936
- // Mount user component inside the wrapper via a second portal
13937
- const contentPortal = new ComponentPortal(component, null, innerInjector);
13938
- wrapperRef.instance.portal.set(contentPortal);
13939
- if (hasBackdrop && !disableClose) {
13940
- overlayRef.backdropClick().subscribe(() => drawerRef.close());
13879
+ const { compRef, overlayRef } = this._overlay.mount(component, innerInjector, {
13880
+ zIndex: 1000,
13881
+ backdrop: hasBackdrop,
13882
+ backdropClass: 'clx-modal-backdrop',
13883
+ containerStyle: {
13884
+ alignItems: 'flex-start',
13885
+ justifyContent: 'flex-end',
13886
+ padding: '0',
13887
+ },
13888
+ });
13889
+ const panelEl = compRef.location.nativeElement;
13890
+ panelEl.style.height = '100vh';
13891
+ panelEl.style.pointerEvents = 'auto';
13892
+ // Slide in from right
13893
+ this._animate.animate(panelEl, {
13894
+ animation: 'fadeInRight',
13895
+ duration: 300,
13896
+ trigger: 'manual',
13897
+ });
13898
+ const closeViaAnimation = (result) => {
13899
+ if (_closing)
13900
+ return;
13901
+ _closing = true;
13902
+ this._animate.animate(panelEl, {
13903
+ animation: 'fadeOutRight',
13904
+ duration: 300,
13905
+ trigger: 'manual',
13906
+ fillMode: 'forwards',
13907
+ }).then(() => {
13908
+ overlayRef.destroy();
13909
+ _resolve(result);
13910
+ });
13911
+ };
13912
+ if (hasBackdrop && !disableClose && overlayRef.backdrop) {
13913
+ overlayRef.backdrop.addEventListener('click', () => closeViaAnimation());
13941
13914
  }
13942
13915
  if (!disableClose) {
13943
- overlayRef.keydownEvents().subscribe(e => { if (e.key === 'Escape')
13944
- drawerRef.close(); });
13916
+ const onKey = (e) => {
13917
+ if (e.key === 'Escape') {
13918
+ closeViaAnimation();
13919
+ document.removeEventListener('keydown', onKey);
13920
+ }
13921
+ };
13922
+ document.addEventListener('keydown', onKey);
13945
13923
  }
13946
- _focusTrap = this._focusTrapFactory.create(overlayRef.overlayElement);
13947
- _focusTrap.focusInitialElementWhenReady();
13948
13924
  return drawerRef;
13949
13925
  }
13950
13926
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxDrawerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
@@ -13953,47 +13929,16 @@ class ClxDrawerService {
13953
13929
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxDrawerService, decorators: [{
13954
13930
  type: Injectable,
13955
13931
  args: [{ providedIn: 'root' }]
13956
- }], ctorParameters: () => [] });
13932
+ }] });
13957
13933
 
13958
- /** Dedicated OverlayContainer for toasts — lives in its own div above all modals */
13959
- class ToastOverlayContainer extends OverlayContainer {
13960
- _createContainer() {
13961
- const el = document.createElement('div');
13962
- el.classList.add('clx-toast-overlay-container');
13963
- el.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:0;z-index:99999;pointer-events:none;';
13964
- document.body.appendChild(el);
13965
- this._containerElement = el;
13966
- }
13967
- }
13968
13934
  class ClxToastService {
13935
+ _appRef = inject(ApplicationRef);
13969
13936
  _injector = inject(Injector);
13970
- _toastOverlay = null;
13937
+ // Each position has its own container div at z-index 9000 (above modals)
13971
13938
  _containers = new Map();
13972
- _getOverlay() {
13973
- if (this._toastOverlay)
13974
- return this._toastOverlay;
13975
- const childInjector = Injector.create({
13976
- parent: this._injector,
13977
- providers: [
13978
- {
13979
- provide: OverlayContainer,
13980
- useFactory: () => runInInjectionContext(this._injector, () => new ToastOverlayContainer()),
13981
- },
13982
- { provide: Overlay },
13983
- ],
13984
- });
13985
- this._toastOverlay = childInjector.get(Overlay);
13986
- return this._toastOverlay;
13987
- }
13988
- // ─── Core show ─────────────────────────────────────────────────────────────
13989
13939
  show(options) {
13990
13940
  const position = options.position ?? TOAST_DEFAULTS.position;
13991
13941
  const container = this._getOrCreateContainer(position);
13992
- // Move our dedicated overlay container to the end of body on every show
13993
- // so it always appears after any modal cdk-overlay-container
13994
- const el = container.overlayRef.overlayElement.closest('.clx-toast-overlay-container');
13995
- if (el)
13996
- document.body.appendChild(el);
13997
13942
  const entry = {
13998
13943
  id: this._uid(),
13999
13944
  type: options.type ?? TOAST_DEFAULTS.type,
@@ -14014,7 +13959,6 @@ class ClxToastService {
14014
13959
  const list = container.instance.entries();
14015
13960
  container.instance.entries.set(isBottom ? [...list, entry] : [entry, ...list]);
14016
13961
  }
14017
- // ─── Convenience shortcuts ─────────────────────────────────────────────────
14018
13962
  success(message, opts = {}) {
14019
13963
  this.show({ ...opts, type: 'green', message });
14020
13964
  }
@@ -14030,25 +13974,26 @@ class ClxToastService {
14030
13974
  dismissAll() {
14031
13975
  this._containers.forEach(c => c.instance.entries.set([]));
14032
13976
  }
14033
- // ─── Container management ──────────────────────────────────────────────────
14034
13977
  _getOrCreateContainer(position) {
14035
13978
  const existing = this._containers.get(position);
14036
13979
  if (existing)
14037
13980
  return existing;
14038
- const overlay = this._getOverlay();
14039
- const overlayRef = overlay.create(new OverlayConfig({
14040
- hasBackdrop: false,
14041
- scrollStrategy: overlay.scrollStrategies.noop(),
14042
- positionStrategy: overlay.position().global(),
14043
- }));
14044
- const portal = new ComponentPortal(ClxToastContainerComponent, null, Injector.create({ parent: this._injector, providers: [] }));
14045
- const compRef = overlayRef.attach(portal);
14046
- compRef.setInput('position', position);
14047
- const entry = { overlayRef, instance: compRef.instance };
13981
+ // Dedicated div — completely separate from modal's DOM tree
13982
+ const el = document.createElement('div');
13983
+ el.style.cssText = 'position:fixed;inset:0;z-index:9000;pointer-events:none;';
13984
+ document.body.appendChild(el);
13985
+ const ref = createComponent(ClxToastContainerComponent, {
13986
+ environmentInjector: this._appRef.injector,
13987
+ elementInjector: this._injector,
13988
+ });
13989
+ ref.setInput('position', position);
13990
+ this._appRef.attachView(ref.hostView);
13991
+ const compEl = ref.location.nativeElement;
13992
+ el.appendChild(compEl);
13993
+ const entry = { el, instance: ref.instance };
14048
13994
  this._containers.set(position, entry);
14049
13995
  return entry;
14050
13996
  }
14051
- // ─── Utilities ─────────────────────────────────────────────────────────────
14052
13997
  _uid() {
14053
13998
  return `toast-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
14054
13999
  }