ngwr 9.0.0 → 9.1.0

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 +1 @@
1
- {"version":3,"file":"ngwr-dialog.mjs","sources":["../../../projects/lib/dialog/dialog-ref.ts","../../../projects/lib/dialog/tokens/dialog-data.token.ts","../../../projects/lib/dialog/tokens/dialog-ref.token.ts","../../../projects/lib/dialog/dialog.ts","../../../projects/lib/dialog/directives/dialog-title.ts","../../../projects/lib/dialog/directives/dialog-content.ts","../../../projects/lib/dialog/directives/dialog-footer.ts","../../../projects/lib/dialog/directives/dialog-close.ts","../../../projects/lib/dialog/ngwr-dialog.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { ConfigurableFocusTrap } from '@angular/cdk/a11y';\nimport type { OverlayRef } from '@angular/cdk/overlay';\nimport type { ComponentRef } from '@angular/core';\n\nimport { Subject } from 'rxjs';\n\n/**\n * Handle returned by `WrDialog.open()`.\n *\n * Wraps the underlying `OverlayRef` and tracks the close result.\n *\n * @example\n * ```ts\n * const ref = dialog.open<ConfirmComponent, boolean>(ConfirmComponent);\n * const result = await ref.awaitClose(); // boolean | undefined\n * ```\n */\nexport class WrDialogRef<C, R = unknown> {\n /** Emits the close result once and completes. */\n readonly closed = new Subject<R | undefined>();\n\n /** @internal */\n componentRef: ComponentRef<C> | null = null;\n\n /** @internal */\n focusTrap: ConfigurableFocusTrap | null = null;\n\n /** @internal */\n previouslyFocused: HTMLElement | null = null;\n\n constructor(private readonly _overlayRef: OverlayRef) {}\n\n /** The instantiated dialog component. */\n get componentInstance(): C {\n if (!this.componentRef) {\n throw new Error('WrDialogRef: component not yet attached');\n }\n return this.componentRef.instance;\n }\n\n /** Close the dialog, optionally returning a result. */\n close(result?: R): void {\n this.closed.next(result);\n this.closed.complete();\n this.focusTrap?.destroy();\n this.focusTrap = null;\n const restore = this.previouslyFocused;\n this.previouslyFocused = null;\n this._overlayRef.dispose();\n // Restore focus after disposal so the trigger is reachable again.\n if (restore && typeof restore.focus === 'function') {\n restore.focus();\n }\n }\n\n /** Resolves with the close result when the dialog is dismissed. */\n awaitClose(): Promise<R | undefined> {\n return new Promise(resolve => {\n this.closed.subscribe({\n next: value => resolve(value),\n complete: () => resolve(undefined),\n });\n });\n }\n\n /** Underlying overlay ref — escape hatch for advanced cases. */\n get overlayRef(): OverlayRef {\n return this._overlayRef;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\n/**\n * Token that exposes the `data` payload passed to `WrDialog.open()`.\n *\n * @example\n * ```ts\n * @Component({...})\n * export class ConfirmComponent {\n * readonly data = inject<ConfirmData>(WR_DIALOG_DATA);\n * }\n * ```\n */\nexport const WR_DIALOG_DATA = new InjectionToken<unknown>('WR_DIALOG_DATA');\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\nimport type { WrDialogRef } from '../dialog-ref';\n\n/**\n * Token exposing the open dialog's `WrDialogRef` to components and\n * directives rendered inside it (e.g. `[wrDialogClose]`).\n *\n * @internal\n */\nexport const WR_DIALOG_REF = new InjectionToken<WrDialogRef<unknown, unknown>>('WR_DIALOG_REF');\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';\nimport { type OverlayRef, ScrollStrategyOptions } from '@angular/cdk/overlay';\nimport { ComponentPortal, type ComponentType } from '@angular/cdk/portal';\nimport { isPlatformBrowser } from '@angular/common';\nimport { EnvironmentInjector, Service, Injector, PLATFORM_ID, inject } from '@angular/core';\n\nimport { WR_OVERLAY, WR_RESPONSIVE_OVERLAYS, wrPresentAsSheet } from 'ngwr/overlay';\n\nimport { WrDialogRef } from './dialog-ref';\nimport type { WrDialogOptions } from './interfaces';\nimport { WR_DIALOG_DATA, WR_DIALOG_REF } from './tokens';\n\nconst DEFAULT_PANEL_CLASS = 'wr-dialog-panel';\nconst DEFAULT_BACKDROP_CLASS = 'wr-dialog-backdrop';\n\n/**\n * Opens dialog components in an isolated NGWR overlay.\n *\n * Uses `WR_OVERLAY` so it composes cleanly with `provideWrOverlay()`\n * — dialogs render into NGWR's own overlay container and never collide\n * with other CDK consumers (Material, NG-ZORRO, etc.).\n *\n * @example\n * ```ts\n * const dialog = inject(WrDialog);\n *\n * const ref = dialog.open(ConfirmComponent, {\n * data: { message: 'Delete this item?' },\n * width: '24rem',\n * });\n *\n * const ok = await ref.awaitClose();\n * if (ok) remove();\n * ```\n *\n * @see https://ngwr.dev/reference/components/dialog\n */\n@Service()\nexport class WrDialog {\n private readonly overlay = inject(WR_OVERLAY);\n private readonly scrollStrategies = inject(ScrollStrategyOptions);\n private readonly parentInjector = inject(EnvironmentInjector);\n private readonly focusTrapFactory = inject(ConfigurableFocusTrapFactory);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly responsiveConfig = inject(WR_RESPONSIVE_OVERLAYS);\n\n open<C, R = unknown, D = unknown>(component: ComponentType<C>, options: WrDialogOptions<D> = {}): WrDialogRef<C, R> {\n const panelClasses: string[] = [DEFAULT_PANEL_CLASS];\n const extra = options.panelClass;\n if (typeof extra === 'string') {\n panelClasses.push(extra);\n } else if (extra) {\n for (const cls of extra) panelClasses.push(cls);\n }\n\n // On small viewports (when opted in) present as a slide-up sheet pinned\n // to the bottom edge, full-width, instead of a centred modal.\n const asSheet = wrPresentAsSheet(options.responsive, this.responsiveConfig);\n if (asSheet) panelClasses.push('wr-overlay-sheet');\n\n const position = this.overlay.position().global().centerHorizontally();\n const overlayRef: OverlayRef = this.overlay.create({\n positionStrategy: asSheet ? position.bottom('0') : position.centerVertically(),\n scrollStrategy: this.scrollStrategies.block(),\n hasBackdrop: true,\n backdropClass: DEFAULT_BACKDROP_CLASS,\n panelClass: panelClasses,\n width: asSheet ? '100%' : options.width,\n maxWidth: asSheet ? '100%' : options.maxWidth,\n });\n\n const dialogRef = new WrDialogRef<C, R>(overlayRef);\n\n if (this.isBrowser) {\n const active = document.activeElement;\n dialogRef.previouslyFocused = active instanceof HTMLElement ? active : null;\n }\n\n const injector = Injector.create({\n parent: this.parentInjector,\n providers: [\n { provide: WR_DIALOG_DATA, useValue: options.data },\n { provide: WR_DIALOG_REF, useValue: dialogRef },\n ],\n });\n\n const portal = new ComponentPortal(component, null, injector);\n dialogRef.componentRef = overlayRef.attach(portal);\n\n if (this.isBrowser) {\n const host = overlayRef.overlayElement;\n host.setAttribute('role', 'dialog');\n host.setAttribute('aria-modal', 'true');\n // Wire aria-labelledby to wrDialogTitle's auto-id once content is in DOM.\n queueMicrotask(() => {\n const titleEl = host.querySelector<HTMLElement>('[wrDialogTitle], [wr-dialog-title]');\n if (titleEl?.id) host.setAttribute('aria-labelledby', titleEl.id);\n });\n // Trap focus inside the dialog and move initial focus in.\n const trap = this.focusTrapFactory.create(host);\n dialogRef.focusTrap = trap;\n void trap.focusInitialElementWhenReady();\n }\n\n // Overlay subscriptions complete when the overlay is disposed, so no\n // explicit teardown is required.\n if (options.closeOnBackdropClick !== false) {\n overlayRef.backdropClick().subscribe(() => dialogRef.close());\n }\n if (options.closeOnEscape !== false) {\n overlayRef.keydownEvents().subscribe(event => {\n if (event.key === 'Escape') {\n event.preventDefault();\n dialogRef.close();\n }\n });\n }\n\n return dialogRef;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directive, ElementRef, inject } from '@angular/core';\n\nlet uid = 0;\n\n/**\n * Styles a dialog title row. Use on a heading inside an opened dialog.\n * Auto-assigns an `id` so the dialog panel can reference it via\n * `aria-labelledby`.\n *\n * @example\n * ```html\n * <h2 wrDialogTitle>Confirm delete</h2>\n * ```\n */\n@Directive({\n selector: '[wrDialogTitle]',\n host: { class: 'wr-dialog__title', '[attr.id]': 'id' },\n})\nexport class WrDialogTitle {\n /** Generated id used for `aria-labelledby` wiring. */\n readonly id = `wr-dialog-title-${++uid}`;\n\n /** @internal */\n readonly elementRef = inject(ElementRef<HTMLElement>);\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directive } from '@angular/core';\n\n/**\n * Styles the scrollable body section of a dialog.\n *\n * @example\n * ```html\n * <div wrDialogContent>…body…</div>\n * ```\n */\n@Directive({\n selector: '[wrDialogContent]',\n host: { class: 'wr-dialog__content' },\n})\nexport class WrDialogContent {}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directive, computed, input } from '@angular/core';\n\n/**\n * Footer row, typically holding action buttons.\n *\n * @example\n * ```html\n * <div wrDialogFooter align=\"end\">\n * <wr-btn wrDialogClose>Cancel</wr-btn>\n * <wr-btn color=\"danger\" (click)=\"confirm()\">Delete</wr-btn>\n * </div>\n * ```\n */\n@Directive({\n selector: '[wrDialogFooter]',\n host: { '[class]': 'classes()' },\n})\nexport class WrDialogFooter {\n /**\n * Horizontal alignment of the children.\n *\n * @default 'end'\n */\n readonly align = input<'start' | 'center' | 'end'>('end');\n\n protected readonly classes = computed(() => `wr-dialog__footer wr-dialog__footer--${this.align()}`);\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directive, inject, input } from '@angular/core';\n\nimport type { WrDialogRef } from '../dialog-ref';\nimport { WR_DIALOG_REF } from '../tokens';\n\n/**\n * Closes the dialog when the host element is clicked.\n *\n * Optionally passes a `[wrDialogClose]` value as the close result.\n *\n * @example\n * ```html\n * <wr-btn wrDialogClose>Cancel</wr-btn>\n * <wr-btn color=\"primary\" [wrDialogClose]=\"true\">Confirm</wr-btn>\n * ```\n */\n@Directive({\n selector: '[wrDialogClose]',\n host: { '(click)': 'onClick()' },\n})\nexport class WrDialogClose<R = unknown> {\n /**\n * Value to pass to `close()`. When unset, the dialog closes with\n * `undefined`.\n */\n readonly result = input<R | undefined>(undefined, { alias: 'wrDialogClose' });\n\n private readonly ref = inject<WrDialogRef<unknown, R>>(WR_DIALOG_REF, { optional: true });\n\n protected onClick(): void {\n this.ref?.close(this.result());\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-dialog.mjs","sources":["../../../projects/lib/dialog/dialog-ref.ts","../../../projects/lib/dialog/tokens/dialog-data.token.ts","../../../projects/lib/dialog/tokens/dialog-ref.token.ts","../../../projects/lib/dialog/dialog.ts","../../../projects/lib/dialog/directives/dialog-title.ts","../../../projects/lib/dialog/directives/dialog-content.ts","../../../projects/lib/dialog/directives/dialog-footer.ts","../../../projects/lib/dialog/directives/dialog-close.ts","../../../projects/lib/dialog/ngwr-dialog.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport type { ConfigurableFocusTrap } from '@angular/cdk/a11y';\nimport type { OverlayRef } from '@angular/cdk/overlay';\nimport type { ComponentRef } from '@angular/core';\n\nimport { ReplaySubject } from 'rxjs';\n\n/**\n * Handle returned by `WrDialog.open()`.\n *\n * Wraps the underlying `OverlayRef` and tracks the close result. It is also\n * provided inside the dialog's own injector, so the projected content can\n * inject it and close itself — the counterpart to `[wrDialogClose]` when the\n * close is programmatic (after a save succeeds, say) rather than a click.\n *\n * @example\n * ```ts\n * // Caller side.\n * const ref = dialog.open<ConfirmComponent, boolean>(ConfirmComponent);\n * const result = await ref.awaitClose(); // boolean | undefined\n * ```\n *\n * @example\n * ```ts\n * // Content side — the dialog closes itself.\n * @Component({...})\n * export class ConfirmComponent {\n * private readonly ref = inject(WrDialogRef);\n *\n * save(): void {\n * this.store.save();\n * this.ref.close(true);\n * }\n * }\n * ```\n */\nexport class WrDialogRef<C, R = unknown> {\n /**\n * Emits the close result once and completes.\n *\n * A `ReplaySubject`, not a plain `Subject`, so the result survives a late\n * subscriber: content can close itself before `open()` has even returned, and\n * `awaitClose()` may be called long after the dismissal — both would otherwise\n * see only `complete` and resolve `undefined`.\n */\n readonly closed = new ReplaySubject<R | undefined>(1);\n\n /** @internal */\n componentRef: ComponentRef<C> | null = null;\n\n /** @internal */\n focusTrap: ConfigurableFocusTrap | null = null;\n\n /** @internal */\n previouslyFocused: HTMLElement | null = null;\n\n constructor(private readonly _overlayRef: OverlayRef) {}\n\n /** The instantiated dialog component. */\n get componentInstance(): C {\n if (!this.componentRef) {\n throw new Error('WrDialogRef: component not yet attached');\n }\n return this.componentRef.instance;\n }\n\n private isClosed = false;\n\n /** Close the dialog, optionally returning a result. */\n close(result?: R): void {\n if (this.isClosed) return;\n this.isClosed = true;\n this.closed.next(result);\n this.closed.complete();\n // Content that closes itself while constructing gets here with\n // `OverlayRef.attach()` still on the stack — `componentRef` is assigned\n // only once attach returns. Disposing mid-attach leaves the CDK writing to\n // a detached host, so let the current task finish first.\n if (!this.componentRef) {\n queueMicrotask(() => this.teardown());\n return;\n }\n this.teardown();\n }\n\n private teardown(): void {\n this.focusTrap?.destroy();\n this.focusTrap = null;\n const restore = this.previouslyFocused;\n this.previouslyFocused = null;\n this._overlayRef.dispose();\n // Drop the destroyed component so a retained ref doesn't pin its instance,\n // host element and view in memory.\n this.componentRef = null;\n // Restore focus after disposal so the trigger is reachable again.\n if (restore && typeof restore.focus === 'function') {\n restore.focus();\n }\n }\n\n /** Resolves with the close result when the dialog is dismissed. */\n awaitClose(): Promise<R | undefined> {\n return new Promise(resolve => {\n this.closed.subscribe({\n next: value => resolve(value),\n complete: () => resolve(undefined),\n });\n });\n }\n\n /** Underlying overlay ref — escape hatch for advanced cases. */\n get overlayRef(): OverlayRef {\n return this._overlayRef;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\n/**\n * Token that exposes the `data` payload passed to `WrDialog.open()`.\n *\n * @example\n * ```ts\n * @Component({...})\n * export class ConfirmComponent {\n * readonly data = inject<ConfirmData>(WR_DIALOG_DATA);\n * }\n * ```\n */\nexport const WR_DIALOG_DATA = new InjectionToken<unknown>('WR_DIALOG_DATA');\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken } from '@angular/core';\n\nimport type { WrDialogRef } from '../dialog-ref';\n\n/**\n * Token exposing the open dialog's `WrDialogRef` to components and\n * directives rendered inside it (e.g. `[wrDialogClose]`).\n *\n * **Prefer `inject(WrDialogRef)`** — the ref is provided under the class token\n * too, and the class path is the documented one. It already covers both cases\n * you might reach for a token for: `inject(WrDialogRef, { optional: true })`\n * works for content that also renders outside a dialog, and\n * `inject<WrDialogRef<ConfirmComponent, boolean>>(WrDialogRef)` types the\n * generics without a cast.\n *\n * This token is the same object under a second key. It exists for the library's\n * own directives (`[wrDialogClose]`) and for swapping the provider out — a test\n * stub, say — without touching the class.\n *\n * @example\n * ```ts\n * @Component({...})\n * export class ConfirmComponent {\n * private readonly ref = inject<WrDialogRef<ConfirmComponent, boolean>>(WrDialogRef);\n *\n * confirm(): void {\n * this.ref.close(true);\n * }\n * }\n * ```\n */\nexport const WR_DIALOG_REF = new InjectionToken<WrDialogRef<unknown, unknown>>('WR_DIALOG_REF');\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';\nimport { type OverlayRef, ScrollStrategyOptions } from '@angular/cdk/overlay';\nimport { ComponentPortal, type ComponentType } from '@angular/cdk/portal';\nimport { isPlatformBrowser } from '@angular/common';\nimport { EnvironmentInjector, Service, Injector, PLATFORM_ID, afterNextRender, inject } from '@angular/core';\n\nimport { WR_OVERLAY, WR_RESPONSIVE_OVERLAYS, wrPresentAsSheet } from 'ngwr/overlay';\n\nimport { WrDialogRef } from './dialog-ref';\nimport type { WrDialogOptions } from './interfaces';\nimport { WR_DIALOG_DATA, WR_DIALOG_REF } from './tokens';\n\nconst DEFAULT_PANEL_CLASS = 'wr-dialog-panel';\nconst DEFAULT_BACKDROP_CLASS = 'wr-dialog-backdrop';\n\n/**\n * Opens dialog components in an isolated NGWR overlay.\n *\n * Uses `WR_OVERLAY` so it composes cleanly with `provideWrOverlay()`\n * — dialogs render into NGWR's own overlay container and never collide\n * with other CDK consumers (Material, NG-ZORRO, etc.).\n *\n * @example\n * ```ts\n * const dialog = inject(WrDialog);\n *\n * const ref = dialog.open(ConfirmComponent, {\n * data: { message: 'Delete this item?' },\n * width: '24rem',\n * });\n *\n * const ok = await ref.awaitClose();\n * if (ok) remove();\n * ```\n *\n * @see https://ngwr.dev/reference/components/dialog\n */\n@Service()\nexport class WrDialog {\n private readonly overlay = inject(WR_OVERLAY);\n private readonly scrollStrategies = inject(ScrollStrategyOptions);\n private readonly parentInjector = inject(EnvironmentInjector);\n private readonly focusTrapFactory = inject(ConfigurableFocusTrapFactory);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly responsiveConfig = inject(WR_RESPONSIVE_OVERLAYS);\n\n open<C, R = unknown, D = unknown>(component: ComponentType<C>, options: WrDialogOptions<D> = {}): WrDialogRef<C, R> {\n const panelClasses: string[] = [DEFAULT_PANEL_CLASS];\n const extra = options.panelClass;\n if (typeof extra === 'string') {\n panelClasses.push(extra);\n } else if (extra) {\n for (const cls of extra) panelClasses.push(cls);\n }\n\n // On small viewports (when opted in) present as a slide-up sheet pinned\n // to the bottom edge, full-width, instead of a centred modal.\n const asSheet = wrPresentAsSheet(options.responsive, this.responsiveConfig);\n if (asSheet) panelClasses.push('wr-overlay-sheet');\n\n const position = this.overlay.position().global().centerHorizontally();\n const overlayRef: OverlayRef = this.overlay.create({\n positionStrategy: asSheet ? position.bottom('0') : position.centerVertically(),\n scrollStrategy: this.scrollStrategies.block(),\n hasBackdrop: true,\n backdropClass: DEFAULT_BACKDROP_CLASS,\n panelClass: panelClasses,\n width: asSheet ? '100%' : options.width,\n maxWidth: asSheet ? '100%' : options.maxWidth,\n });\n\n const dialogRef = new WrDialogRef<C, R>(overlayRef);\n\n if (this.isBrowser) {\n const active = document.activeElement;\n dialogRef.previouslyFocused = active instanceof HTMLElement ? active : null;\n }\n\n const injector = Injector.create({\n parent: this.parentInjector,\n providers: [\n { provide: WR_DIALOG_DATA, useValue: options.data },\n { provide: WR_DIALOG_REF, useValue: dialogRef },\n // Provided as its own token as well, so dialog content can reach for\n // the familiar `inject(WrDialogRef)` and close itself.\n { provide: WrDialogRef, useValue: dialogRef },\n ],\n });\n\n const portal = new ComponentPortal(component, null, injector);\n dialogRef.componentRef = overlayRef.attach(portal);\n\n if (this.isBrowser) {\n const host = overlayRef.overlayElement;\n host.setAttribute('role', 'dialog');\n host.setAttribute('aria-modal', 'true');\n // Wire aria-labelledby to wrDialogTitle's auto-id once content is in DOM.\n // Has to wait for a render, not a microtask: attaching the portal only\n // schedules change detection, so under zoneless CD a microtask runs while\n // the content is still an empty view and the title id doesn't exist yet.\n afterNextRender(\n () => {\n const titleEl = host.querySelector<HTMLElement>('[wrDialogTitle], [wr-dialog-title]');\n if (titleEl?.id) host.setAttribute('aria-labelledby', titleEl.id);\n },\n { injector: this.parentInjector }\n );\n // Trap focus inside the dialog and move initial focus in.\n const trap = this.focusTrapFactory.create(host);\n dialogRef.focusTrap = trap;\n void trap.focusInitialElementWhenReady();\n }\n\n // Overlay subscriptions complete when the overlay is disposed, so no\n // explicit teardown is required.\n if (options.closeOnBackdropClick !== false) {\n overlayRef.backdropClick().subscribe(() => dialogRef.close());\n }\n if (options.closeOnEscape !== false) {\n overlayRef.keydownEvents().subscribe(event => {\n if (event.key === 'Escape') {\n event.preventDefault();\n dialogRef.close();\n }\n });\n }\n\n return dialogRef;\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directive, ElementRef, inject } from '@angular/core';\n\nlet uid = 0;\n\n/**\n * Styles a dialog title row. Use on a heading inside an opened dialog.\n * Auto-assigns an `id` so the dialog panel can reference it via\n * `aria-labelledby`.\n *\n * @example\n * ```html\n * <h2 wrDialogTitle>Confirm delete</h2>\n * ```\n */\n@Directive({\n selector: '[wrDialogTitle]',\n host: { class: 'wr-dialog__title', '[attr.id]': 'id' },\n})\nexport class WrDialogTitle {\n /** Generated id used for `aria-labelledby` wiring. */\n readonly id = `wr-dialog-title-${++uid}`;\n\n /** @internal */\n readonly elementRef = inject(ElementRef<HTMLElement>);\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directive } from '@angular/core';\n\n/**\n * Styles the scrollable body section of a dialog.\n *\n * @example\n * ```html\n * <div wrDialogContent>…body…</div>\n * ```\n */\n@Directive({\n selector: '[wrDialogContent]',\n host: { class: 'wr-dialog__content' },\n})\nexport class WrDialogContent {}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directive, computed, input } from '@angular/core';\n\n/**\n * Footer row, typically holding action buttons.\n *\n * @example\n * ```html\n * <div wrDialogFooter align=\"end\">\n * <wr-btn wrDialogClose>Cancel</wr-btn>\n * <wr-btn color=\"danger\" (click)=\"confirm()\">Delete</wr-btn>\n * </div>\n * ```\n */\n@Directive({\n selector: '[wrDialogFooter]',\n host: { '[class]': 'classes()' },\n})\nexport class WrDialogFooter {\n /**\n * Horizontal alignment of the children.\n *\n * @default 'end'\n */\n readonly align = input<'start' | 'center' | 'end'>('end');\n\n protected readonly classes = computed(() => `wr-dialog__footer wr-dialog__footer--${this.align()}`);\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Directive, inject, input } from '@angular/core';\n\nimport type { WrDialogRef } from '../dialog-ref';\nimport { WR_DIALOG_REF } from '../tokens';\n\n/**\n * Closes the dialog when the host element is clicked.\n *\n * Optionally passes a `[wrDialogClose]` value as the close result.\n *\n * @example\n * ```html\n * <wr-btn wrDialogClose>Cancel</wr-btn>\n * <wr-btn color=\"primary\" [wrDialogClose]=\"true\">Confirm</wr-btn>\n * ```\n */\n@Directive({\n selector: '[wrDialogClose]',\n host: { '(click)': 'onClick()' },\n})\nexport class WrDialogClose<R = unknown> {\n /**\n * Value to pass to `close()`. When unset — or used as a bare attribute — the\n * dialog closes with `undefined`.\n */\n readonly result = input<R | undefined, R | undefined | ''>(undefined, {\n alias: 'wrDialogClose',\n // A bare `wrDialogClose` attribute arrives as the empty string; that means\n // \"no result\", not a result of `''`. Matches `wrDrawerClose`.\n transform: value => (value === '' ? undefined : value),\n });\n\n private readonly ref = inject<WrDialogRef<unknown, R>>(WR_DIALOG_REF, { optional: true });\n\n protected onClick(): void {\n this.ref?.close(this.result());\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,11 +1,13 @@
1
1
  import { ConfigurableFocusTrapFactory } from '@angular/cdk/a11y';
2
2
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
3
- import { TemplatePortal } from '@angular/cdk/portal';
3
+ import { TemplatePortal, ComponentPortal } from '@angular/cdk/portal';
4
4
  import { isPlatformBrowser } from '@angular/common';
5
5
  import * as i0 from '@angular/core';
6
- import { model, input, viewChild, TemplateRef, inject, ViewContainerRef, DestroyRef, PLATFORM_ID, effect, ViewEncapsulation, Component, Directive, computed } from '@angular/core';
6
+ import { model, input, viewChild, TemplateRef, inject, ViewContainerRef, DestroyRef, PLATFORM_ID, effect, ViewEncapsulation, Component, InjectionToken, EnvironmentInjector, Injector, afterNextRender, Service, Directive, computed } from '@angular/core';
7
7
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
8
  import { WR_OVERLAY } from 'ngwr/overlay';
9
+ import { ScrollStrategyOptions } from '@angular/cdk/overlay';
10
+ import { ReplaySubject } from 'rxjs';
9
11
 
10
12
  /**
11
13
  * @license
@@ -245,6 +247,316 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
245
247
  args: [{ selector: 'wr-drawer', encapsulation: ViewEncapsulation.None, host: { style: 'display:none' }, template: "<ng-template>\n <div #panel [class]=\"panelClass()\">\n @if (showHandle()) {\n <div\n class=\"wr-drawer__handle\"\n aria-hidden=\"true\"\n (touchstart)=\"onSwipeStart($event)\"\n (touchmove)=\"onSwipeMove($event, panel)\"\n (touchend)=\"onSwipeEnd($event, panel)\"\n (touchcancel)=\"onSwipeEnd($event, panel)\"\n >\n <span class=\"wr-drawer__grabber\"></span>\n </div>\n }\n <ng-content />\n </div>\n</ng-template>\n" }]
246
248
  }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], showHandle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHandle", required: false }] }], safeArea: [{ type: i0.Input, args: [{ isSignal: true, alias: "safeArea", required: false }] }], hasBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasBackdrop", required: false }] }], closeOnBackdropClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdropClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], panelTpl: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
247
249
 
250
+ /**
251
+ * @license
252
+ *
253
+ * Use of this source code is governed by an MIT-style license that can be
254
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
255
+ */
256
+ /**
257
+ * Handle returned by `WrDrawerManager.open()`.
258
+ *
259
+ * Wraps the underlying `OverlayRef` and tracks the close result. It is also
260
+ * provided inside the drawer's own injector, so the projected content can
261
+ * inject it and close itself — the counterpart to `[wrDrawerClose]` when the
262
+ * close is programmatic (after a save succeeds, say) rather than a click.
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * // Caller side.
267
+ * const ref = drawers.open<ChatComponent, boolean>(ChatComponent, { position: 'right' });
268
+ * const result = await ref.awaitClose(); // boolean | undefined
269
+ * ```
270
+ *
271
+ * @example
272
+ * ```ts
273
+ * // Content side — the drawer closes itself.
274
+ * @Component({...})
275
+ * export class ChatComponent {
276
+ * private readonly ref = inject(WrDrawerRef);
277
+ *
278
+ * send(): void {
279
+ * this.store.send(this.draft());
280
+ * this.ref.close(true);
281
+ * }
282
+ * }
283
+ * ```
284
+ */
285
+ class WrDrawerRef {
286
+ _overlayRef;
287
+ /**
288
+ * Emits the close result once and completes.
289
+ *
290
+ * A `ReplaySubject`, not a plain `Subject`, so the result survives a late
291
+ * subscriber: content can close itself before `open()` has even returned, and
292
+ * `awaitClose()` may be called long after the dismissal — both would otherwise
293
+ * see only `complete` and resolve `undefined`.
294
+ */
295
+ closed = new ReplaySubject(1);
296
+ /** @internal */
297
+ componentRef = null;
298
+ /** @internal */
299
+ focusTrap = null;
300
+ /** @internal */
301
+ previouslyFocused = null;
302
+ constructor(_overlayRef) {
303
+ this._overlayRef = _overlayRef;
304
+ }
305
+ /** The instantiated drawer component. */
306
+ get componentInstance() {
307
+ if (!this.componentRef) {
308
+ throw new Error('WrDrawerRef: component not yet attached');
309
+ }
310
+ return this.componentRef.instance;
311
+ }
312
+ isClosed = false;
313
+ /** Close the drawer, optionally returning a result. */
314
+ close(result) {
315
+ if (this.isClosed)
316
+ return;
317
+ this.isClosed = true;
318
+ this.closed.next(result);
319
+ this.closed.complete();
320
+ // Content that closes itself while constructing gets here with
321
+ // `OverlayRef.attach()` still on the stack — `componentRef` is assigned
322
+ // only once attach returns. Disposing mid-attach leaves the CDK writing to
323
+ // a detached host, so let the current task finish first.
324
+ if (!this.componentRef) {
325
+ queueMicrotask(() => this.teardown());
326
+ return;
327
+ }
328
+ this.teardown();
329
+ }
330
+ teardown() {
331
+ this.focusTrap?.destroy();
332
+ this.focusTrap = null;
333
+ const restore = this.previouslyFocused;
334
+ this.previouslyFocused = null;
335
+ this._overlayRef.dispose();
336
+ // Drop the destroyed component so a retained ref doesn't pin its instance,
337
+ // host element and view in memory.
338
+ this.componentRef = null;
339
+ // Restore focus after disposal so the trigger is reachable again.
340
+ if (restore && typeof restore.focus === 'function') {
341
+ restore.focus();
342
+ }
343
+ }
344
+ /** Resolves with the close result when the drawer is dismissed. */
345
+ awaitClose() {
346
+ return new Promise(resolve => {
347
+ this.closed.subscribe({
348
+ next: value => resolve(value),
349
+ complete: () => resolve(undefined),
350
+ });
351
+ });
352
+ }
353
+ /** Underlying overlay ref — escape hatch for advanced cases. */
354
+ get overlayRef() {
355
+ return this._overlayRef;
356
+ }
357
+ }
358
+
359
+ /**
360
+ * @license
361
+ *
362
+ * Use of this source code is governed by an MIT-style license that can be
363
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
364
+ */
365
+ /**
366
+ * Token that exposes the `data` payload passed to `WrDrawerManager.open()`.
367
+ *
368
+ * @example
369
+ * ```ts
370
+ * @Component({...})
371
+ * export class ChatComponent {
372
+ * readonly data = inject<ChatData>(WR_DRAWER_DATA);
373
+ * }
374
+ * ```
375
+ */
376
+ const WR_DRAWER_DATA = new InjectionToken('WR_DRAWER_DATA');
377
+
378
+ /**
379
+ * @license
380
+ *
381
+ * Use of this source code is governed by an MIT-style license that can be
382
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
383
+ */
384
+ /**
385
+ * Token exposing the open drawer's `WrDrawerRef` to components and
386
+ * directives rendered inside it (e.g. `[wrDrawerClose]`).
387
+ *
388
+ * **Prefer `inject(WrDrawerRef)`** — the ref is provided under the class token
389
+ * too, and the class path is the documented one. It already covers both cases
390
+ * you might reach for a token for: `inject(WrDrawerRef, { optional: true })`
391
+ * works for content that also renders outside a drawer, and
392
+ * `inject<WrDrawerRef<ChatComponent, boolean>>(WrDrawerRef)` types the generics
393
+ * without a cast.
394
+ *
395
+ * This token is the same object under a second key. It exists for the library's
396
+ * own directives (`[wrDrawerClose]`) and for swapping the provider out — a test
397
+ * stub, say — without touching the class.
398
+ *
399
+ * @example
400
+ * ```ts
401
+ * @Component({...})
402
+ * export class ChatComponent {
403
+ * private readonly ref = inject<WrDrawerRef<ChatComponent, boolean>>(WrDrawerRef);
404
+ *
405
+ * dismiss(): void {
406
+ * this.ref.close(true);
407
+ * }
408
+ * }
409
+ * ```
410
+ */
411
+ const WR_DRAWER_REF = new InjectionToken('WR_DRAWER_REF');
412
+
413
+ /**
414
+ * @license
415
+ *
416
+ * Use of this source code is governed by an MIT-style license that can be
417
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
418
+ */
419
+ const DEFAULT_POSITION = 'right';
420
+ const DEFAULT_WIDTH = '20rem';
421
+ const DEFAULT_HEIGHT = '16rem';
422
+ const BACKDROP_CLASS = 'wr-drawer-backdrop';
423
+ /**
424
+ * Opens drawer components in an isolated NGWR overlay — the programmatic
425
+ * counterpart to `<wr-drawer [(open)]="…">`.
426
+ *
427
+ * Use the component when the drawer is part of a template and its open state
428
+ * is a signal you already own; use this service when the caller doesn't own
429
+ * the markup (opening a chat panel from a toolbar action, from an effect, from
430
+ * a route guard) or when you need the close result.
431
+ *
432
+ * Uses `WR_OVERLAY` so it composes cleanly with `provideWrOverlay()`
433
+ * — drawers render into NGWR's own overlay container and never collide
434
+ * with other CDK consumers (Material, NG-ZORRO, etc.).
435
+ *
436
+ * `showHandle` / swipe-to-dismiss is component-only: it needs the drawer's own
437
+ * wrapper markup, which the service path replaces with your component.
438
+ *
439
+ * @example
440
+ * ```ts
441
+ * const drawers = inject(WrDrawerManager);
442
+ *
443
+ * const ref = drawers.open(ChatComponent, {
444
+ * data: { threadId },
445
+ * position: 'right',
446
+ * width: '28rem',
447
+ * });
448
+ *
449
+ * await ref.awaitClose();
450
+ * ```
451
+ *
452
+ * @see https://ngwr.dev/reference/components/drawer
453
+ */
454
+ class WrDrawerManager {
455
+ overlay = inject(WR_OVERLAY);
456
+ scrollStrategies = inject(ScrollStrategyOptions);
457
+ parentInjector = inject(EnvironmentInjector);
458
+ focusTrapFactory = inject(ConfigurableFocusTrapFactory);
459
+ isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
460
+ open(component, options = {}) {
461
+ const position = options.position ?? DEFAULT_POSITION;
462
+ const isHorizontal = position === 'left' || position === 'right';
463
+ // The content component is attached straight into the overlay pane, so the
464
+ // panel styles land on the pane rather than on an inner wrapper.
465
+ const panelClasses = [
466
+ 'wr-drawer-overlay',
467
+ `wr-drawer-overlay--${position}`,
468
+ 'wr-drawer__panel',
469
+ `wr-drawer__panel--${position}`,
470
+ ];
471
+ if (options.rounded)
472
+ panelClasses.push('wr-drawer__panel--rounded');
473
+ if (options.safeArea)
474
+ panelClasses.push('wr-drawer__panel--safe-area');
475
+ const extra = options.panelClass;
476
+ if (typeof extra === 'string') {
477
+ panelClasses.push(extra);
478
+ }
479
+ else if (extra) {
480
+ for (const cls of extra)
481
+ panelClasses.push(cls);
482
+ }
483
+ const positionStrategy = this.overlay
484
+ .position()
485
+ .global()[position === 'left' ? 'left' : position === 'right' ? 'right' : 'centerHorizontally']();
486
+ if (position === 'top')
487
+ positionStrategy.top();
488
+ else if (position === 'bottom')
489
+ positionStrategy.bottom();
490
+ else
491
+ positionStrategy.centerVertically();
492
+ const cap = options.maxHeight ?? null;
493
+ const overlayRef = this.overlay.create({
494
+ positionStrategy,
495
+ scrollStrategy: this.scrollStrategies.block(),
496
+ hasBackdrop: options.hasBackdrop !== false,
497
+ backdropClass: BACKDROP_CLASS,
498
+ panelClass: panelClasses,
499
+ width: isHorizontal ? (options.width ?? DEFAULT_WIDTH) : '100vw',
500
+ height: isHorizontal ? undefined : (options.height ?? DEFAULT_HEIGHT),
501
+ maxHeight: isHorizontal ? undefined : (cap ?? undefined),
502
+ });
503
+ const drawerRef = new WrDrawerRef(overlayRef);
504
+ if (this.isBrowser) {
505
+ const active = document.activeElement;
506
+ drawerRef.previouslyFocused = active instanceof HTMLElement ? active : null;
507
+ }
508
+ const injector = Injector.create({
509
+ parent: this.parentInjector,
510
+ providers: [
511
+ { provide: WR_DRAWER_DATA, useValue: options.data },
512
+ { provide: WR_DRAWER_REF, useValue: drawerRef },
513
+ // Provided as its own token as well, so drawer content can reach for
514
+ // the familiar `inject(WrDrawerRef)` and close itself.
515
+ { provide: WrDrawerRef, useValue: drawerRef },
516
+ ],
517
+ });
518
+ const portal = new ComponentPortal(component, null, injector);
519
+ drawerRef.componentRef = overlayRef.attach(portal);
520
+ if (this.isBrowser) {
521
+ const host = overlayRef.overlayElement;
522
+ host.setAttribute('role', 'dialog');
523
+ host.setAttribute('aria-modal', 'true');
524
+ // Wire aria-labelledby to wrDrawerTitle's auto-id once content is in DOM.
525
+ // Has to wait for a render, not a microtask: attaching the portal only
526
+ // schedules change detection, so under zoneless CD a microtask runs while
527
+ // the content is still an empty view and the title id doesn't exist yet.
528
+ afterNextRender(() => {
529
+ const titleEl = host.querySelector('[wrDrawerTitle], [wr-drawer-title]');
530
+ if (titleEl?.id)
531
+ host.setAttribute('aria-labelledby', titleEl.id);
532
+ }, { injector: this.parentInjector });
533
+ // Trap focus inside the drawer and move initial focus in.
534
+ const trap = this.focusTrapFactory.create(host);
535
+ drawerRef.focusTrap = trap;
536
+ void trap.focusInitialElementWhenReady();
537
+ }
538
+ // Overlay subscriptions complete when the overlay is disposed, so no
539
+ // explicit teardown is required.
540
+ if (options.closeOnBackdropClick !== false) {
541
+ overlayRef.backdropClick().subscribe(() => drawerRef.close());
542
+ }
543
+ if (options.closeOnEscape !== false) {
544
+ overlayRef.keydownEvents().subscribe(event => {
545
+ if (event.key === 'Escape') {
546
+ event.preventDefault();
547
+ drawerRef.close();
548
+ }
549
+ });
550
+ }
551
+ return drawerRef;
552
+ }
553
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrDrawerManager, deps: [], target: i0.ɵɵFactoryTarget.Service });
554
+ static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: WrDrawerManager });
555
+ }
556
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrDrawerManager, decorators: [{
557
+ type: Service
558
+ }] });
559
+
248
560
  /**
249
561
  * @license
250
562
  *
@@ -348,18 +660,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
348
660
  /**
349
661
  * Closes the parent drawer on click. Attach to any clickable element.
350
662
  *
663
+ * Works for both drawer flavours: inside `<wr-drawer>` it clears the
664
+ * component's `open` model, and inside a `WrDrawerManager.open()` drawer it
665
+ * closes the ref — optionally passing a `[wrDrawerClose]` value as the close
666
+ * result (the component flavour has no result to carry, so the value is
667
+ * ignored there).
668
+ *
351
669
  * @example
352
670
  * ```html
353
671
  * <wr-btn wrDrawerClose>Close</wr-btn>
672
+ * <wr-btn color="primary" [wrDrawerClose]="true">Save</wr-btn>
354
673
  * ```
355
674
  */
356
675
  class WrDrawerClose {
676
+ /**
677
+ * Value to pass to `close()` for service-opened drawers. When unset — or
678
+ * used as a bare attribute — the drawer closes with `undefined`.
679
+ */
680
+ result = input(undefined, { ...(ngDevMode ? { debugName: "result" } : /* istanbul ignore next */ {}), alias: 'wrDrawerClose',
681
+ // A bare `wrDrawerClose` attribute arrives as the empty string; that means
682
+ // "no result", not a result of `''`.
683
+ transform: value => (value === '' ? undefined : value) });
357
684
  drawer = inject(WrDrawer, { optional: true });
685
+ ref = inject(WR_DRAWER_REF, { optional: true });
358
686
  close() {
359
- this.drawer?.open.set(false);
687
+ // Element injectors win, so a surrounding `<wr-drawer>` is the nearer
688
+ // owner even when a service-opened drawer sits further up the tree.
689
+ if (this.drawer) {
690
+ this.drawer.open.set(false);
691
+ return;
692
+ }
693
+ this.ref?.close(this.result());
360
694
  }
361
695
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrDrawerClose, deps: [], target: i0.ɵɵFactoryTarget.Directive });
362
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.0", type: WrDrawerClose, isStandalone: true, selector: "[wrDrawerClose]", host: { listeners: { "click": "close()" } }, ngImport: i0 });
696
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.0", type: WrDrawerClose, isStandalone: true, selector: "[wrDrawerClose]", inputs: { result: { classPropertyName: "result", publicName: "wrDrawerClose", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "close()" } }, ngImport: i0 });
363
697
  }
364
698
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WrDrawerClose, decorators: [{
365
699
  type: Directive,
@@ -367,11 +701,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
367
701
  selector: '[wrDrawerClose]',
368
702
  host: { '(click)': 'close()' },
369
703
  }]
370
- }] });
704
+ }], propDecorators: { result: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrDrawerClose", required: false }] }] } });
371
705
 
372
706
  /**
373
707
  * Generated bundle index. Do not edit.
374
708
  */
375
709
 
376
- export { WrDrawer, WrDrawerClose, WrDrawerContent, WrDrawerFooter, WrDrawerTitle };
710
+ export { WR_DRAWER_DATA, WR_DRAWER_REF, WrDrawer, WrDrawerClose, WrDrawerContent, WrDrawerFooter, WrDrawerManager, WrDrawerRef, WrDrawerTitle };
377
711
  //# sourceMappingURL=ngwr-drawer.mjs.map