react-scroll-snap-tabs

1.0.4 • Public • Published

React Scroll Snap Tabs

React Scroll Snap Tabs for React.js is a versatile and interactive component designed to provide a seamless tab navigation experience, specifically optimized for touch devices. With its intuitive touch gestures and responsive design, this component ensures effortless scrolling and tab switching on mobile devices and tablets. Users can easily swipe between tabs, making it a breeze to navigate through the content. The component is thoughtfully designed to accommodate the unique interaction patterns of touch devices, offering a user-friendly experience for both touch and non-touch environments. Developers can seamlessly integrate this touch-friendly component into their React.js applications, providing a consistent and enjoyable tabbed interface across all devices.

NPM JavaScript Style Guide


Demo 1

Demo 2

Demo 3


Buy Me A Coffee


Table Of Contents

  1. Install
  2. Basic Usage
  3. Advanced Usage
  4. Props
  5. Nav Component Props
  6. Link Component Props
  7. Content Component Props
  8. Pane Component Props
  9. Easing Functions
  10. onIndicatorMove Event
  11. How Indicator Works

Install

npm i react-scroll-snap-tabs

Basic Usage

Note that, in order to prevent unexpected behaviors, the order of links and panes should exactly match based on their eventKeys.

import React from 'react'

import Tabs from 'react-scroll-snap-tabs'
import 'react-scroll-snap-tabs/dist/index.css'

const App = () => {
  return (
    <div style={{ width: '400px', height: '100vh', border: '1px solid gray' }}>
      <Tabs eventKeys={['Tab 1', 'Tab 2']}>
        <Tabs.Content paneStyle={{ border: '1px solid gray' }}>
          <Tabs.Pane eventKey='Tab 1'>Content 1</Tabs.Pane>
          <Tabs.Pane eventKey='Tab 2'>Content 2</Tabs.Pane>
        </Tabs.Content>
      </Tabs>
    </div>
  )
}

export default App

Result:

BasicScrollSnapTabs2

Advanced Usage

import React from 'react'

import Tabs from 'react-scroll-snap-tabs'
import 'react-scroll-snap-tabs/dist/index.css'

const App = () => {
  const width = 10
  const onIndicatorMove = (e) => {
    const indicator = e.target
    const timing = (t) => -12 * (t - 0.5) * (t - 0.5) + 4
    const progress = timing(e.progress)
    indicator.style.width = progress * width + 'px'
  }

  return (
    <div style={{ width: '400px', height: '100vh', backgroundColor: '#333' }}>
      <Tabs style={{ borderRight: '1px solid gray' }} snapDuration={300} defaultKey='tab3'>
        <Tabs.Nav
          activeLinkStyle={{ color: 'white' }}
          linkStyle={{ whiteSpace: 'nowrap' }}
          indicatorStyle={{
            borderRadius: '3px',
            width: width + 'px',
            maxWidth: '100%',
            backgroundColor: 'orange'
          }}
          style={{ color: 'gray', backgroundColor: 'black', borderRadius: '5px' }}
          onIndicatorMove={onIndicatorMove}
        >
          <Tabs.Link eventKey='tab1'>Tab 1</Tabs.Link>
          <Tabs.Link eventKey='tab2'>Very Long Tab 2</Tabs.Link>
          <Tabs.Link eventKey='tab3'>Tab 3</Tabs.Link>
          <Tabs.Link eventKey='tab4'>Very Long Tab 4</Tabs.Link>
          <Tabs.Link eventKey='tab5'>Very Long Tab 5</Tabs.Link>
        </Tabs.Nav>
        <Tabs.Content
          style={{ overscrollBehaviorY: 'contain' }}
          paneStyle={{ minHeight: '100%', minWidth: '100%' }}
        >
          <Tabs.Pane eventKey='tab1'>
            <CustomComponent color='white'>Content 1</CustomComponent>
          </Tabs.Pane>
          <Tabs.Pane eventKey='tab2'>
            <CustomComponent color='white'>Content 2</CustomComponent>
          </Tabs.Pane>
          <Tabs.Pane eventKey='tab3'>
            <CustomComponent color='white'>Content 3</CustomComponent>
          </Tabs.Pane>
          <Tabs.Pane eventKey='tab4'>
            <CustomComponent color='white'>Content 4</CustomComponent>
          </Tabs.Pane>
          <Tabs.Pane eventKey='tab5'>
            <CustomComponent color='white'>Content 5</CustomComponent>
          </Tabs.Pane>
        </Tabs.Content>
      </Tabs>
    </div>
  )
}

function CustomComponent({ children, color }) {
  return (
    <div
      style={{
        backgroundColor: 'transparent',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        fontStyle: 'bold',
        fontSize: '3vw',
        width: '100%',
        height: '100%',
        color
      }}
    >
      {children}
    </div>
  )
}

export default App

Result:

AdvancedScrollSnapTabs

Props

Name Type Default Description
activeLinkClass string Class name to be applied to the active link in the navigation.
activeLinkStyle object {color: '#5A90E4'} Style to be applied to the active link in the navigation.
defaultKey string(eventKey) first tab's event default event key to snap to the corresponding tab on load
easing string | func "ease-out" Sets the easing function for snap animation. The value should be either "ease-in", "ease-out", or "ease-in-out". If a random string value is provided, the default easing function will be (t) => t (linear). read more
eventKeys [string] Manually configure and customize all events without the need for a Nav component
indicatorClass string Assigns a class name to the indicator element.
indicatorColor string "black" Assigns the background color of the indicator element.
indicatorSize string "100%" Assigns the size of the indicator element.
indicatorStyle object Style to be applied to the indicator element
indicatorParentClass string Assigns a class name to the indicator's parent element.
indicatorParentStyle object Style to be applied to the indicator's parent element
layout string "veritical" Sets the layout of the component. The value should be either "horizontal" or "vertical". If a random string value is provided, the default layout will be "vertical"
linkClass string Assigns a class name to all of the link components in the navigation component.
linkStyle object Applies styles to all of the link components in the navigation component.
onIndicatorMove func When the indicator element moves, the onIndicatorMove callback function is triggered along with the movement.
scrollIntoViewEasing string | func "ease-out" Sets the easing function for scroll into view animation. The value should be either "ease-in", "ease-out", or "ease-in-out". If a random string value is provided, the default easing function will be (t) => t (linear). read more
scrollIntoViewDuration number 250 Sets the duration in ms for the scroll into view animation.
scrollIntoViewOffset number 100 Sets an additional scroll offset for the scroll into view animation.
snapDuration number 250 Sets the duration in ms for the scroll snap animation.
snapThreshold number 30 Sets the threshold for the snap action to start. If the threshold value is exceeded, it snaps to the next item; otherwise, it snaps to the current item.
swipeThreshold number 200 Sets the threshold for the snap action to start on touch devices. When a swipe action is performed, the algorithm calculates an inertial value. If the calculated value exceeds the swipe threshold value, it snaps to the next item; otherwise, it snaps to the current item.

Nav Component Props

In the navigation component, you can utilize the same props as described previously, which are as follows:

  • activeLinkClass
  • activeLinkStyle
  • eventKeys
  • indicatorClass
  • indicatorColor
  • indicatorSize
  • indicatorStyle
  • indicatorParentClass
  • indicatorParentStyle
  • linkStyle
  • linkClass
  • onIndicatorMove
  • scrollIntoViewEasing
  • scrollIntoViewDuration
  • scrollIntoViewOffset

Link Component Props

Name Type Default Description
activeClass string The class name to be applied to the link when it's active.
activeStyle object The style to be applied to the link when it is active.
eventKey string The event key that corresponds to the pane component with the same event key.

Content Component Props

Name Type Default Description
paneClass string Assigns a class name to all of the pane components in the content component.
paneStyle object Applies styles to all of the pane components in the content component.

Pane Component Props

Name Type Default Description
eventKey string The event key that corresponds to the link component with the same event key.

Easing Functions

You can use predefined easing functions by providing either "ease-in", "ease-out", or "ease-in-out" string values. If a random string value is provided, the default easing function will be (t) => t (linear). Alternatively, you can provide your own easing function that accepts a number argument between 0 and 1 and returns a number between 0 and 1. You can find more about easing functions on easings.net.

onIndicatorMove Event

When the indicator element moves, the onIndicatorMove callback function is triggered along with the movement.

Event Properties:

Event.progress

It is a fractional value between two snap points that ranges from 0 to 1. The current snap point, representing the current content, is 0, and the target snap point is 1.

Event.target

Returns the indicator element.

Event.isInteracting

Returns a boolean value indicating whether or not the user is interacting with the content area.

Example

import React from 'react'

import Tabs from 'react-scroll-snap-tabs'
import 'react-scroll-snap-tabs/dist/index.css'

const App = () => {
  const width = 10
  const onIndicatorMove = (e) => {
    const indicator = e.target
    const timing = (t) => -12 * (t - 0.5) * (t - 0.5) + 4
    const progress = timing(e.progress)
    indicator.style.width = progress * width + 'px'
  }

  return (
    <div style={{ width: '400px', height: '100vh', backgroundColor: '#333' }}>
      <Tabs
        style={{ borderRight: '1px solid gray' }}
        onIndicatorMove={onIndicatorMove}
        snapDuration={300}
        defaultKey='tab3'
      >
        <Tabs.Nav
          activeLinkStyle={{ color: 'white' }}
          linkStyle={{ whiteSpace: 'nowrap' }}
          indicatorStyle={{
            borderRadius: '3px',
            width: width + 'px',
            maxWidth: '100%',
            backgroundColor: 'orange'
          }}
          style={{ color: 'gray', backgroundColor: 'black', borderRadius: '5px' }}
        >
          <Tabs.Link eventKey='tab1'>Tab 1</Tabs.Link>
          <Tabs.Link eventKey='tab2'>Very Long Tab 2</Tabs.Link>
          <Tabs.Link eventKey='tab3'>Tab 3</Tabs.Link>
          <Tabs.Link eventKey='tab4'>Very Long Tab 4</Tabs.Link>
          <Tabs.Link eventKey='tab5'>Very Long Tab 5</Tabs.Link>
        </Tabs.Nav>
        <Tabs.Content
          style={{ overscrollBehaviorY: 'contain' }}
          paneStyle={{ minHeight: '100%', minWidth: '100%' }}
        >
          <Tabs.Pane eventKey='tab1'>
            <CustomComponent color='white'>Content 1</CustomComponent>
          </Tabs.Pane>
          <Tabs.Pane eventKey='tab2'>
            <CustomComponent color='white'>Content 2</CustomComponent>
          </Tabs.Pane>
          <Tabs.Pane eventKey='tab3'>
            <CustomComponent color='white'>Content 3</CustomComponent>
          </Tabs.Pane>
          <Tabs.Pane eventKey='tab4'>
            <CustomComponent color='white'>Content 4</CustomComponent>
          </Tabs.Pane>
          <Tabs.Pane eventKey='tab5'>
            <CustomComponent color='white'>Content 5</CustomComponent>
          </Tabs.Pane>
        </Tabs.Content>
      </Tabs>
    </div>
  )
}

function CustomComponent({ children, color }) {
  return (
    <div
      style={{
        backgroundColor: 'transparent',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        fontStyle: 'bold',
        fontSize: '3vw',
        width: '100%',
        height: '100%',
        color
      }}
    >
      {children}
    </div>
  )
}

export default App

How Indicator Works

The tabs include an indicator section that consists of two elements: an indicator and its parent. The parent elemet dynamically adjusts its position based on the active tab, while the indicator represents the actual visible part.

When the user scrolls through the content area, if the layout is 'vertical', the indicator parent adjusts its left and width values to align with the target link element's left and width values. If the layout is 'horizontal', it aligns its top and height values.

Example

import React from 'react'

import Tabs from 'react-scroll-snap-tabs'
import 'react-scroll-snap-tabs/dist/index.css'

const App = () => {
  return (
    <div style={{ width: '400px', height: '100vh', border: '1px solid gray' }}>
      <Tabs
        indicatorParentClass='indicator-parent'
        indicatorParentStyle={{ border: '1px solid red' }}
        indicatorClass='indicator'
        indicatorStyle={{ maxWidth: '75%' }}
        eventKeys={['Tab 1', 'Very Long Tab 2']}
      >
        <Tabs.Content paneStyle={{ border: '1px solid gray' }}>
          <Tabs.Pane eventKey='Tab 1'>Content 1</Tabs.Pane>
          <Tabs.Pane eventKey='Very Long Tab 2'>Content 2</Tabs.Pane>
        </Tabs.Content>
      </Tabs>
    </div>
  )
}

export default App

To provide a visual representation of this behavior, consider the following HTML code, which showcases a rendered indicator parent and indicator elements in a vertical layout application:

<div
  class="indicator-parent"
  style="position: absolute; bottom: 0px; border: 1px solid red; left: 53px; width: 128px;"
>
  <div
    class="indicator"
    style="height: 100%; margin: auto; min-height: 3px; background-color: black; max-width: 75%;"
  ></div>
</div>

Result:

IndicatorScrollSnapTabs2

In the provided code, the indicator parent element is represented by the <div> with the class name 'indicator-parent'. It is positioned absolutely and has a red border. Its left and width values are adjusted dynamically based on the corresponding link element's left and width values.

Inside the indicator parent, the indicator element is represented by the <div> with the class name 'indicator'. The background color is black.


Buy Me A Coffee


License

MIT © aacar947

Package Sidebar

Install

npm i react-scroll-snap-tabs

Weekly Downloads

24

Version

1.0.4

License

MIT

Unpacked Size

501 kB

Total Files

7

Last publish

Collaborators

  • aacar947