diff --git a/projects/demo/src/styles.scss b/projects/demo/src/styles.scss index 8575ad86..699b2749 100644 --- a/projects/demo/src/styles.scss +++ b/projects/demo/src/styles.scss @@ -1,7 +1,7 @@ /* You can add global styles to this file, and also import other style files */ @use "@angular/cdk" as cdk; -@import 'prismjs/themes/prism-okaidia.css'; +@import "prismjs/themes/prism-okaidia.css"; @import url("https://rsms.me/inter/inter.css"); @import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&family=Roboto:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap"); @tailwind base; @@ -89,4 +89,4 @@ h1 { h2 { @apply text-headline-small-prominent text-ids-page-fg-surface-default; -} \ No newline at end of file +} diff --git a/projects/widgets/core/public-api.ts b/projects/widgets/core/public-api.ts index 8111af70..dab521b0 100644 --- a/projects/widgets/core/public-api.ts +++ b/projects/widgets/core/public-api.ts @@ -3,6 +3,7 @@ export * from './base-classes/directive-base'; export * from './directives/detect-scrollable.directive'; export * from './pipes/sentence-case.pipe'; export * from './services/resize-observer.service'; +export * from './services/document-scroll-strategy.service'; export * from './tokens/control-container'; export * from './types/orientation.type'; export * from './types/position.type'; diff --git a/projects/widgets/dialog/dialog-scroll-strategy.service.spec.ts b/projects/widgets/core/services/document-scroll-strategy.service.spec.ts similarity index 81% rename from projects/widgets/dialog/dialog-scroll-strategy.service.spec.ts rename to projects/widgets/core/services/document-scroll-strategy.service.spec.ts index 3d7a779b..fc6be335 100644 --- a/projects/widgets/dialog/dialog-scroll-strategy.service.spec.ts +++ b/projects/widgets/core/services/document-scroll-strategy.service.spec.ts @@ -1,16 +1,16 @@ -import { IdsDialogScrollStrategyService } from './dialog-scroll-strategy.service'; +import { IdsDocumentScrollStrategyService } from './document-scroll-strategy.service'; import { Overlay } from '@angular/cdk/overlay'; import { TestBed } from '@angular/core/testing'; -describe('IdsDialogScrollStrategyService', () => { +describe('IdsDocumentScrollStrategyService', () => { const blockScrollStrategy = { attach: jest.fn(), disable: jest.fn(), enable: jest.fn(), }; - let service: IdsDialogScrollStrategyService; + let service: IdsDocumentScrollStrategyService; let originalBodyOverflow: string; let originalHtmlOverflow: string; @@ -23,7 +23,7 @@ describe('IdsDialogScrollStrategyService', () => { TestBed.configureTestingModule({ providers: [ - IdsDialogScrollStrategyService, + IdsDocumentScrollStrategyService, { provide: Overlay, useValue: { @@ -35,7 +35,7 @@ describe('IdsDialogScrollStrategyService', () => { ], }); - service = TestBed.inject(IdsDialogScrollStrategyService); + service = TestBed.inject(IdsDocumentScrollStrategyService); }); afterEach(() => { @@ -64,7 +64,7 @@ describe('IdsDialogScrollStrategyService', () => { expect(blockScrollStrategy.enable).not.toHaveBeenCalled(); }); - it('keeps the document locked until every dialog closes', () => { + it('keeps the document locked until every overlay closes', () => { const firstStrategy = service.create(); const secondStrategy = service.create(); @@ -72,7 +72,7 @@ describe('IdsDialogScrollStrategyService', () => { secondStrategy.enable(); firstStrategy.disable(); - expect(blockScrollStrategy.enable).toHaveBeenCalledTimes(1); + expect(blockScrollStrategy.enable).toHaveBeenCalledTimes(2); expect(blockScrollStrategy.disable).not.toHaveBeenCalled(); secondStrategy.disable(); diff --git a/projects/widgets/dialog/dialog-scroll-strategy.service.ts b/projects/widgets/core/services/document-scroll-strategy.service.ts similarity index 80% rename from projects/widgets/dialog/dialog-scroll-strategy.service.ts rename to projects/widgets/core/services/document-scroll-strategy.service.ts index d8f1484d..cf88d691 100644 --- a/projects/widgets/dialog/dialog-scroll-strategy.service.ts +++ b/projects/widgets/core/services/document-scroll-strategy.service.ts @@ -2,24 +2,21 @@ import { Overlay, OverlayRef, ScrollStrategy } from '@angular/cdk/overlay'; import { DOCUMENT, inject, Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) -export class IdsDialogScrollStrategyService { +export class IdsDocumentScrollStrategyService { private readonly _document = inject(DOCUMENT); private readonly _blockScrollStrategy = inject(Overlay).scrollStrategies.block(); private _lockCount = 0; private _usesBlockScrollStrategy = false; public create(): ScrollStrategy { - return new IdsDialogScrollStrategy(this); + return new IdsDocumentScrollStrategy(this); } public lock(): void { - if (this._lockCount++ > 0) { - return; - } + this._lockCount++; - this._usesBlockScrollStrategy = !this._isDocumentScrollAlreadyBlocked(); - - if (this._usesBlockScrollStrategy) { + if (!this._isDocumentScrollAlreadyBlocked()) { + this._usesBlockScrollStrategy = true; this._blockScrollStrategy.enable(); } } @@ -54,10 +51,10 @@ export class IdsDialogScrollStrategyService { } } -class IdsDialogScrollStrategy implements ScrollStrategy { +class IdsDocumentScrollStrategy implements ScrollStrategy { private _enabled = false; - constructor(private readonly _scrollLock: IdsDialogScrollStrategyService) {} + constructor(private readonly _scrollLock: IdsDocumentScrollStrategyService) {} public attach(overlayRef: OverlayRef): void { void overlayRef; diff --git a/projects/widgets/dialog/dialog.component.ts b/projects/widgets/dialog/dialog.component.ts index 2db99898..0a4a610c 100644 --- a/projects/widgets/dialog/dialog.component.ts +++ b/projects/widgets/dialog/dialog.component.ts @@ -1,6 +1,5 @@ import { IDS_DIALOG_DEFAULT_CONFIG, IDS_DIALOG_DEFAULT_CONFIG_FACTORY, IdsDialogDefaultConfig } from './dialog-defaults'; import { IdsDialogHeaderDirective } from './dialog-header.directive'; -import { IdsDialogScrollStrategyService } from './dialog-scroll-strategy.service'; import { Dialog, DialogRef } from '@angular/cdk/dialog'; import { NgTemplateOutlet } from '@angular/common'; @@ -15,7 +14,7 @@ import { input, viewChild, } from '@angular/core'; -import { ComponentBaseWithDefaults, IdsDetectScrollableDirective, IdsSizeType } from '@i-cell/ids-angular/core'; +import { ComponentBaseWithDefaults, IdsDetectScrollableDirective, IdsDocumentScrollStrategyService, IdsSizeType } from '@i-cell/ids-angular/core'; import { IdsIconComponent } from '@i-cell/ids-angular/icon'; import { IdsIconButtonComponent } from '@i-cell/ids-angular/icon-button'; @@ -46,7 +45,7 @@ export class IdsDialogComponent extends ComponentBaseWithDefaults>(DialogRef, { optional: true }); diff --git a/projects/widgets/dialog/dialog.service.ts b/projects/widgets/dialog/dialog.service.ts index 1378b112..80139b86 100644 --- a/projects/widgets/dialog/dialog.service.ts +++ b/projects/widgets/dialog/dialog.service.ts @@ -1,14 +1,14 @@ import { IdsCustomDialogBase } from './custom-dialog-base'; -import { IdsDialogScrollStrategyService } from './dialog-scroll-strategy.service'; import { Dialog, DialogRef } from '@angular/cdk/dialog'; import { Injectable, Signal, StaticProvider, Type, afterNextRender, inject } from '@angular/core'; +import { IdsDocumentScrollStrategyService } from '@i-cell/ids-angular/core'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class IdsDialogService { private readonly _dialog = inject(Dialog); - private readonly _scrollStrategy = inject(IdsDialogScrollStrategyService); + private readonly _scrollStrategy = inject(IdsDocumentScrollStrategyService); public open, R = unknown>( component: Type, diff --git a/projects/widgets/overlay-panel/nested-block-scroll-strategy.ts b/projects/widgets/overlay-panel/nested-block-scroll-strategy.ts index 81f60c07..d57ad226 100644 --- a/projects/widgets/overlay-panel/nested-block-scroll-strategy.ts +++ b/projects/widgets/overlay-panel/nested-block-scroll-strategy.ts @@ -19,12 +19,20 @@ export class IdsNestedBlockScrollStrategy implements ScrollStrategy { } public enable(): void { + if (this._enabled) { + return; + } + this._enabled = true; this._blockScrollStrategy.enable(); this._lockMarkedElements(); } public disable(): void { + if (!this._enabled) { + return; + } + this._enabled = false; this._unlockElements(); this._blockScrollStrategy.disable(); diff --git a/projects/widgets/overlay-panel/overlay-panel.component.ts b/projects/widgets/overlay-panel/overlay-panel.component.ts index 08b20c5e..52bf8992 100644 --- a/projects/widgets/overlay-panel/overlay-panel.component.ts +++ b/projects/widgets/overlay-panel/overlay-panel.component.ts @@ -14,7 +14,6 @@ import { CdkConnectedOverlay, CdkOverlayOrigin, ConnectedPosition, - Overlay, OverlayModule, } from '@angular/cdk/overlay'; import { NgClass } from '@angular/common'; @@ -34,7 +33,7 @@ import { viewChild, ViewEncapsulation, } from '@angular/core'; -import { ComponentBaseWithDefaults, IdsSizeType } from '@i-cell/ids-angular/core'; +import { ComponentBaseWithDefaults, IdsDocumentScrollStrategyService, IdsSizeType } from '@i-cell/ids-angular/core'; const defaultConfig = IDS_OVERLAY_PANEL_DEFAULT_CONFIG_FACTORY(); @@ -57,7 +56,7 @@ export class IdsOverlayPanelComponent extends ComponentBaseWithDefaults; @@ -45,6 +46,7 @@ export class IdsOverlayService { private readonly _overlay = inject(Overlay); private readonly _injector = inject(Injector); private readonly _scrollLock = inject(IdsOverlayScrollLockService); + private readonly _documentScrollStrategy = inject(IdsDocumentScrollStrategyService); public openComponent( component: ComponentType, @@ -115,14 +117,14 @@ export class IdsOverlayService { } if (scrollStrategy === 'nested-block') { - const nestedBlockScrollStrategy = new IdsNestedBlockScrollStrategy(this._overlay.scrollStrategies.block(), this._scrollLock); + const nestedBlockScrollStrategy = new IdsNestedBlockScrollStrategy(this._documentScrollStrategy.create(), this._scrollLock); nestedBlockScrollStrategy.setScrollLockTargets(scrollLockTargets); return nestedBlockScrollStrategy; } - return this._overlay.scrollStrategies[scrollStrategy](); + return scrollStrategy === 'block' ? this._documentScrollStrategy.create() : this._overlay.scrollStrategies[scrollStrategy](); } private _getScrollLockTargets(config: IdsOverlayOpenConfig): HTMLElement[] {