Files
2025-11-25 14:13:44 +01:00

302 lines
8.4 KiB
Markdown

# @isa/utils/scroll-position
Utility library providing scroll position restoration and scroll-to-top functionality for Angular applications.
## Overview
The `@isa/utils/scroll-position` library enables automatic scroll position preservation across route navigations and provides a reusable scroll-to-top button component. It stores scroll positions in session storage and restores them when users navigate back to previously visited routes, creating a smoother user experience for applications with long, scrollable content.
**Key Features:**
- **Automatic Scroll Restoration**: Preserve and restore scroll position across route navigation
- **Session Storage Integration**: Persists scroll positions in session storage for reliability
- **Route-Level Control**: Enable/disable restoration per route via route data configuration
- **Scroll-to-Top Button**: Ready-to-use component with accessibility support
- **Configurable Delays**: Control timing of scroll restoration with optional delays
**Type:** Utility library with functions, providers, and components
## Installation
```typescript
import {
provideScrollPositionRestoration,
injectRestoreScrollPosition,
storeScrollPosition,
ScrollTopButtonComponent
} from '@isa/utils/scroll-position';
```
## API Reference
### Providers
#### `provideScrollPositionRestoration()`
Provides an environment initializer that automatically stores scroll positions during navigation and page unload events.
**Returns:** `EnvironmentProviders`
**Behavior:**
- Listens to `NavigationStart` events from the Angular Router
- Listens to the `beforeunload` window event
- Only stores scroll position for routes with `scrollPositionRestoration: true` in their route data
- Stores positions in session storage using the current URL as the key
**Usage:**
```typescript
// In app.config.ts
import { provideScrollPositionRestoration } from '@isa/utils/scroll-position';
export const appConfig: ApplicationConfig = {
providers: [
provideScrollPositionRestoration(),
// other providers...
]
};
```
### Functions
#### `storeScrollPosition()`
Stores the current viewport scroll position in session storage using the current router URL as the key.
**Returns:** `Promise<void>`
**Usage:**
```typescript
import { Component } from '@angular/core';
import { storeScrollPosition } from '@isa/utils/scroll-position';
@Component({
selector: 'app-product-list',
// ...
})
export class ProductListComponent {
async onNavigateAway() {
// Manually store scroll position before navigating
await storeScrollPosition();
// navigation logic...
}
}
```
#### `injectRestoreScrollPosition()`
Returns a function that restores the scroll position from session storage for the current route.
**Returns:** `(delay?: number) => Promise<void>` - An async function that accepts an optional delay parameter
**Parameters (returned function):**
- `delay?: number` - Optional delay in milliseconds before restoring scroll position (default: 0)
**Behavior:**
- Retrieves stored scroll position using the current router URL as the key
- Waits for the specified delay to ensure the DOM is ready
- Clears the stored position after restoration
- Does nothing if no position was stored for the current URL
**Usage:**
```typescript
import { Component, OnInit } from '@angular/core';
import { injectRestoreScrollPosition } from '@isa/utils/scroll-position';
@Component({
selector: 'app-product-list',
// ...
})
export class ProductListComponent implements OnInit {
private restoreScrollPosition = injectRestoreScrollPosition();
async ngOnInit() {
// Restore scroll position after 100ms to ensure content is rendered
await this.restoreScrollPosition(100);
}
}
```
### Components
#### `ScrollTopButtonComponent`
A standalone component that displays a button to scroll back to the top of the page or a specific scrollable element.
**Selector:** `utils-scroll-top-button`
**Inputs:**
- `target: Window | HTMLElement` - The scroll target (default: `window`)
**Features:**
- Automatically shows/hides based on scroll position (shows when scrolled down)
- Respects user's `prefers-reduced-motion` setting
- Uses smooth scrolling when motion is not reduced
- Includes proper accessibility attributes (`aria-label`)
- E2E testing ready with `data-what` attribute
**Styling:**
- Host class: `utils-scroll-top-button`
- Uses `ViewEncapsulation.None` for flexible styling
- Button uses tertiary color and large size from `@isa/ui/buttons`
**Usage:**
```typescript
// Basic usage (scrolls window)
import { ScrollTopButtonComponent } from '@isa/utils/scroll-position';
@Component({
selector: 'app-product-list',
imports: [ScrollTopButtonComponent],
template: `
<div class="content">
<!-- Your scrollable content -->
</div>
<utils-scroll-top-button />
`
})
export class ProductListComponent {}
```
```typescript
// Custom scroll target (specific element)
@Component({
selector: 'app-modal',
imports: [ScrollTopButtonComponent],
template: `
<div #scrollContainer class="modal-content">
<!-- Long content -->
</div>
<utils-scroll-top-button [target]="scrollContainer" />
`
})
export class ModalComponent {
@ViewChild('scrollContainer') scrollContainer!: ElementRef<HTMLElement>;
}
```
**Styling Example:**
```scss
// Custom positioning and appearance
utils-scroll-top-button {
position: fixed;
bottom: 2rem;
right: 2rem;
z-index: 1000;
}
```
## Usage Examples
### Complete Setup with Automatic Restoration
```typescript
// 1. Configure in app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideScrollPositionRestoration } from '@isa/utils/scroll-position';
export const appConfig: ApplicationConfig = {
providers: [
provideScrollPositionRestoration(),
]
};
// 2. Enable in route configuration
export const routes: Routes = [
{
path: 'products',
component: ProductListComponent,
data: { scrollPositionRestoration: true }
}
];
// 3. Restore position in component
import { Component, OnInit } from '@angular/core';
import { injectRestoreScrollPosition, ScrollTopButtonComponent } from '@isa/utils/scroll-position';
@Component({
selector: 'app-product-list',
imports: [ScrollTopButtonComponent],
template: `
<div class="product-grid">
@for (product of products(); track product.id) {
<app-product-card [product]="product" />
}
</div>
<utils-scroll-top-button />
`
})
export class ProductListComponent implements OnInit {
private restoreScrollPosition = injectRestoreScrollPosition();
async ngOnInit() {
await this.loadProducts();
// Restore scroll after content loads
await this.restoreScrollPosition(100);
}
}
```
### Manual Control for Custom Scenarios
```typescript
import { Component } from '@angular/core';
import { storeScrollPosition, injectRestoreScrollPosition } from '@isa/utils/scroll-position';
@Component({
selector: 'app-custom-navigation',
// ...
})
export class CustomNavigationComponent {
private restoreScrollPosition = injectRestoreScrollPosition();
async onCustomNavigateAway() {
// Store before custom navigation logic
await storeScrollPosition();
this.customNavigationService.navigate();
}
async onCustomNavigateBack() {
// Restore after returning
await this.restoreScrollPosition(200);
}
}
```
### Route Configuration
To enable automatic scroll restoration on specific routes, add the `scrollPositionRestoration` property in route data:
```typescript
export const routes: Routes = [
{
path: 'example',
component: ExampleComponent,
data: {
scrollPositionRestoration: true
}
}
];
```
## Dependencies
- `@angular/common` - ViewportScroller for scroll operations
- `@angular/core` - Core Angular functionality (inject, signals, etc.)
- `@angular/router` - Router integration for navigation events
- `@isa/core/storage` - Session storage provider for persistence
- `@isa/ui/buttons` - Icon button component for ScrollTopButtonComponent
- `@isa/icons` - Icon assets (isaSortByUpMedium)
- `@ng-icons/core` - Icon system integration
## Notes
- Scroll positions are stored in **session storage** and cleared after restoration
- The automatic restoration only applies to routes with `scrollPositionRestoration: true` in their data
- The scroll-to-top button respects accessibility preferences (`prefers-reduced-motion`)
- Manual restoration includes a configurable delay to ensure DOM readiness before scrolling
- The scroll-to-top button automatically shows/hides based on scroll position