npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

inverted-section-list

v0.1.1

Published

A React Native component that implements SectionList with inverted direction and working sticky header

Downloads

91

Readme

InvertedSectionList

A React Native component that implements SectionList with inverted direction and working sticky header

Supported React Native version

Currently we support 66 or above, however, new changes introduce in the future might break it at somepoint.

Demo

import React, { FunctionComponent } from "react";
import {
  SafeAreaView,
  StyleSheet,
  SectionList,
  Text,
  View,
} from "react-native";
import InvertedSectionList from "inverted-section-list";

const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginHorizontal: 16,
  },
  item: {
    backgroundColor: "#f9c2ff",
    padding: 20,
    marginVertical: 8,
  },
  header: {
    fontSize: 32,
    backgroundColor: "#fff",
  },
  title: {
    fontSize: 24,
  },
});

const DATA = [
  {
    title: "Main dishes",
    data: ["Pizza", "Burger", "Risotto", "a", "b", "c", "1", "2", "3"],
  },
  {
    title: "Sides",
    data: ["French Fries", "Onion Rings", "Fried Shrimps"],
  },
  {
    title: "Drinks",
    data: ["Water", "Coke", "Beer"],
  },
  {
    title: "Desserts",
    data: ["Cheese Cake", "Ice Cream"],
  },
];

const Item = ({ title }: { title: string }) => (
  <View style={styles.item}>
    <Text style={styles.title}>{title}</Text>
  </View>
);

const App: FunctionComponent = () => (
  <SafeAreaView style={styles.container}>
    <InvertedSectionList
      sections={DATA}
      keyExtractor={(item, index) => item + index}
      renderItem={({ item }) => <Item title={item} />}
      renderSectionFooter={({ section: { title } }) => (
        <Text style={styles.header}>{title}</Text>
      )}
      stickySectionHeadersEnabled
    />
  </SafeAreaView>
);

export default App;

To run the demo:

cd example
yarn install --dev
yarn start

Install

Run

yarn add inverted-section-list

Why?

The sticky header of inverted SectionList component of React Native is not working as expected. There was an issue #18945 open for years but no sign of the problem been fixed. At Launch Platform, we are building a app product Monoline, and its message list needs to present in inverted direction with working sticky header:

We have no choice but to find a way to fix this problem. Forking React Native is too much effort for us to maintain. We have plan to open Pull Requests to upstream React Native repository, but we anticipate those will take long time before they got reviewed and merged. To solve the problem before it's fixed in the upstream, we build a standalone InvertedSectionList component.

We thought others in the community could benefit from this, so we open sourced it here.

How?

There are different places where the logic needed to be changed in order for the inverted sticky header to work. But those logic are deeply baked inside the build-in component's source code and there's no easy way to change them from the outside. In order to make our InvertedSectionList component's sticky header to work, we copied the source code from React Native 0.64 for following components:

Since we are using TypeScript here, so the original source code are converted into TypeScript. There are following key changes were made from the original source code.

ScrollView

For the StickyHeaderComponent component, we don't just pass in nextHeaderLayoutY, since now the order is inverted, we need to also pass in prevHeaderLayoutY for the next sticky header to calculate the correct position of begin and end. Such as, the sticky header layout update callback needs to set prev header value here:

private _onStickyHeaderLayout(
  index: number,
  event: LayoutChangeEvent,
  key: string
) {
  /* ... */
  const nextHeaderIndex = stickyHeaderIndices[indexOfIndex + 1];
  if (nextHeaderIndex != null) {
    const nextHeader = this._stickyHeaderRefs.get(
      this._getKeyForIndex(nextHeaderIndex, childArray)
    );
    nextHeader &&
      (nextHeader as any).setPrevHeaderY &&
      (nextHeader as any).setPrevHeaderY(layoutY + height);
  }
}

And extra prevLayoutY props value needs to be calculated here:

const prevKey = this._getKeyForIndex(prevIndex, childArray);
const prevLayoutY = this._headerLayoutYs.get(prevKey);
const prevLayoutHeight = this._headerLayoutHeights.get(prevKey);
let prevHeaderLayoutY: number | undefined = undefined;
if (prevLayoutY != null && prevLayoutHeight != null) {
  prevHeaderLayoutY = prevLayoutY + prevLayoutHeight;
}

Then passed into the StickyHeaderComponent here

StickyFooterComponent

The StickyHeaderComponent source code is copied and renamed as StickyFooterComponent, because to make sticky "header" works, we pass the header component as footer instead. New method setPrevHeaderY is added here to receivew the previous header's position from ScrollView.

The another major change here is implementing the correct position calculation logic with the preview header y position provided from our own ScrollView:

// The interpolate looks like this:
//
// ------
//       \
//        \            height = delta
//         \---------
//  prev^   ^current
//        ^ width = delta
//
// Basically, it starts from `prevStickEndPoint`, where the
// previous header stops scrolling. Then we starts the sticking by adding
// negative delta to the `translateY` to cancel the scrolling offset.
// Until the point, where we have scroll to where the current header's original
// position, at this point the `translateY` goes down to 0 so that it
// will scroll with the content
inputRange = [
  prevStickEndPoint - 1,
  prevStickEndPoint,
  stickStartPoint,
  stickStartPoint + 1,
];
outputRange = [-delta, -delta, 0, 0];

InvertedSectionList

We copied and combined the VirtualizedSectionList and SectionList components into InvertedSectionList.

The major change here is that we are passing the footer indices as stickyHeaderIndices instead to the VirtualizedList since we are using footer instead of header:

// Notice: this is different from the original VirtualizedSectionList,
//         since we are actually using footer as the header here, so need to + 1
//         for the header
stickyHeaderIndices.push(
  itemCount + listHeaderOffset + sectionItemCount + 1
);