# @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
