<scroll-coordinator>

A header that folds away as the list below it scrolls, as on a profile page. It hands scrolling between the header and the list, which nested scroll views can't do.

Usage

src/app/profile.ts
import { Component, signal } from '@angular/core';
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';

@Component({
  selector: 'app-profile',
  imports: [LYNX_ELEMENTS],
  template: `
    <scroll-coordinator
      style="height: 100vh; display: flex; flex-direction: column;"
    >
      <scroll-coordinator-header
        style="position: absolute; width: 100%; height: 200px;"
      >
        <text>Profile</text>
      </scroll-coordinator-header>
      <scroll-coordinator-slot style="width: 100%; flex: 1;">
        <list style="height: 100%;">
          @for (post of posts(); track post.id) {
            <list-item [attr.item-key]="post.id">
              <text>{{ post.title }}</text>
            </list-item>
          }
        </list>
      </scroll-coordinator-slot>
    </scroll-coordinator>
  `,
})
export class Profile {
  readonly posts = signal([{ id: '1', title: 'First post' }]);
}

Import LYNX_ELEMENTS in your component (see Elements).

Warning

Needs XElement in custom native apps (see Embedding).

Attributes

AttributeTypeDefaultDescription
enable-scrollbooleantrueWhether it scrolls vertically.
bouncesbooleantrueiOS only. Bounce at the edges.
enable-scroll-barbooleanfalseiOS only. Shows the scroll bar while it scrolls.
granularitynumber0.01How often bindoffset fires, as a fraction of the distance the header can scroll. It can fire less often.
header-over-slotbooleanfalseDraws the header above the slot where they overlap, instead of below it. Set it before the first render.
refresh-mode'none' | 'page' | 'fold''none'iOS only. Pull-to-refresh inside a <refresh>: off, per tab (page) or for the whole page (fold).
ios-scrolls-to-topbooleanfalseiOS only. Tapping the status bar scrolls back to the top.
ios-force-scroll-detachbooleanfalseiOS only. Turns off the hand-off between the header and the slot.
android-nested-scroll-as-childbooleanfalseAndroid only. Lets the coordinator itself scroll inside another scrolling container.
android-adapt-to-empty-slotbooleanfalseAndroid only. Limits folding when the active slot is empty or too short to scroll.

Plus the common attributes, such as id, class and style.

Events

Read the payload from $event.detail (see Handling taps and events).

EventdetailDescription
bindoffset{ offset: number; height: number }The header folded or unfolded. offset is how far it has scrolled, and height how far it can (header minus toolbar), in px.

Plus the common events, such as bindtap.

Methods

This UI method (Glossary) folds the header from code. Call it with invoke() on the element (see Call element methods).

MethodParametersDescription
setFoldExpanded{ offset: string; smooth?: boolean }Scrolls the header to offset, in px or rpx. '0px' unfolds it completely.
import { Component, ElementRef, viewChild } from '@angular/core';
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';

@Component({
  selector: 'app-folding',
  imports: [LYNX_ELEMENTS],
  template: `
    <scroll-coordinator
      #profile
      style="height: 100vh; display: flex; flex-direction: column;"
    >
      <scroll-coordinator-header
        style="position: absolute; width: 100%; height: 200px;"
      />
      <scroll-coordinator-slot style="flex: 1;" />
    </scroll-coordinator>
    <view (bindtap)="expand()"><text>Show the header</text></view>
  `,
})
export class Folding {
  readonly profile = viewChild.required('profile', { read: ElementRef });

  expand(): void {
    this.profile().nativeElement.invoke?.('setFoldExpanded', {
      offset: '0px',
      smooth: true,
    });
  }
}

Children

Use these elements as direct children, and make the coordinator a column (display: flex; flex-direction: column). Its height must equal the slot's height plus the toolbar's.

ChildRole
<scroll-coordinator-header>The header that folds away. Give it position: absolute.
<scroll-coordinator-toolbar>A bar, such as tabs, that stays on screen once the header has folded.
<scroll-coordinator-slot>The scrolling content: a <list>, or a <viewpager> of lists. Give it flex: 1.
<scroll-coordinator-slot-drag>A slot whose content the user can drag up and down to fold and unfold the header.

<scroll-coordinator-slot-drag>

AttributeTypeDefaultDescription
enable-dragbooleantrueWhether dragging the slot folds and unfolds the header.