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

snapgrab

v1.5.0

Published

Snapgrab is a customizable scroll snapping component for web applications.

Downloads

2,929

Readme

Snapgrab

Lightweight scroll snap slider with smooth navigation for touch devices, mouse dragging, and trackpad swiping. It includes accessible controls, dynamic height adjustment, and customizable autoplay.

Table of Contents

Features

  • Native Scroll Snap: Provides a smooth, natural scrolling experience on all touch devices.
  • Intuitive Controls: Built-in dots and arrows for easy navigation.
  • Dynamic Height Adjustment: Automatically adapts to the height of visible slides, ensuring seamless transitions.
  • Customizable Autoplay: Set your preferred interval for automatic sliding.
  • Have a Unique Requirement? Open an issue, and we'll explore adding it!

Demo

Check out the live demo of Snapgrab in action:
Snapgrab Demo

Installation

To install Snapgrab, use npm:

npm install snapgrab

Usage

HTML

Add the following HTML structure to your project:

<div class="slider" role="region" id="sliderControls">
    <ul class="slider__wrapper" aria-live="polite" data-ref="wrapper">
        <li class="slider__slide" aria-hidden="false" aria-current="true">Slide 1</li>
        <li class="slider__slide" aria-hidden="true">Slide 2</li>
        <li class="slider__slide" aria-hidden="true">Slide 3</li>
        <li class="slider__slide" aria-hidden="true">Slide 4</li>
    </ul>
    <div class="slider__buttons">
        <button data-ref="prev" aria-label="Previous slide" aria-controls="sliderControls"></button>
        <button data-ref="next" aria-label="Next slide" aria-controls="sliderControls"></button>
    </div>
    <div class="slider__dots" data-ref="dots" aria-label="Dots"></div>
</div>

JavaScript

Import and initialize Snapgrab in your JavaScript file:

import { Snapgrab } from 'snapgrab'

const slider = new Snapgrab(document.querySelector('.slider'), {
    autoplay: 6000,
    autoplayStopOnInteraction: true,
    autoheight: true,
})
slider.init()

CSS

Add some basic CSS to style the component:

.slider {
    position: relative;

    &__wrapper {
        display: flex;
        margin-bottom: 24px;
        scroll-behavior: smooth;
        scroll-snap-stop: always;
        scroll-snap-type: x mandatory;
        touch-action: pan-x pan-y;
        overflow: scroll hidden;
        transition: height 0.4s cubic-bezier(0.19, 1, 0.22, 1);
        scrollbar-width: none;

        &::-webkit-scrollbar {
            display: none;
        }
    }

    &__slide {
        position: relative;
        display: flex;
        flex-direction: column;
        justify-content: center;
        scroll-snap-align: start;
        scroll-snap-stop: normal;
        padding: 24px;
        margin-right: 24px;
        height: max-content;
        min-height: 200px;
        min-width: 100%;
        font-size: 24px;
        font-weight: 500;
        text-align: center;
        user-select: none;
        background: #bdc3c7;
        border-radius: 10px;

        // Layout shift fix
        &:not(&:first-of-type) {
            &:not(.is-loaded &) {
                min-height: 0;
            }
        }

        &:nth-child(even) {
            min-height: 250px;
            background-color: #95a5a6;
        }
    }

    &__buttons {
        display: flex;
        justify-content: space-between;
        width: 100%;
        gap: 32px;
        position: absolute;
        right: 0;
        bottom: -48px;

        button {
            display: block;
            height: 40px;
            width: 40px;
            border-radius: 50%;
            background-color: white;
            cursor: pointer;
            border: 1px solid #eaecf0;
            transition: background 0.6s cubic-bezier(0.19, 1, 0.22, 1);
            color: rgba(0, 0, 0, 60%);

            &:after {
                content: '';
                transition: color 0.6s cubic-bezier(0.19, 1, 0.22, 1);
                font-size: 2rem;
            }

            &[data-ref='prev'] {
                &:after {
                    content: '←';
                }
            }

            &[data-ref='next'] {
                &:after {
                    content: '→';
                }
            }

            @media (pointer: fine) {
                &:hover {
                    &:not([disabled]) {
                        background: #eaecf0;
                    }
                }
            }

            &[disabled] {
                cursor: default;

                &:after,
                &:before {
                    color: rgba(0, 0, 0, 10%);
                }
            }
        }
    }

    &__dots {
        position: absolute;
        left: 50%;
        transform: translate(-50%);
        display: flex;
        justify-content: center;
        gap: 8px;

        button {
            display: block;
            height: 8px;
            width: 8px;
            border-radius: 50%;
            cursor: pointer;
            background: rgba(0, 0, 0, 42%);
            transition: background 0.6s cubic-bezier(0.19, 1, 0.22, 1);

            &:hover {
                background: rgba(0, 0, 0, 54%);
            }

            &.is-active {
                background: rgba(0, 0, 0, 80%);
            }
        }
    }

    .no-more-right {
        animation: shake 0.5s ease;
    }

    .no-more-left {
        animation: shake-left 0.5s ease;
    }

    @keyframes shake {
        0%,
        100% {
            transform: translateX(0);
        }

        25% {
            transform: translateX(-5px);
        }

        75% {
            transform: translateX(5px);
        }
    }

    @keyframes shake-left {
        0%,
        100% {
            transform: translateX(0);
        }

        25% {
            transform: translateX(5px);
        }

        75% {
            transform: translateX(-5px);
        }
    }
}

Options

Snapgrab accepts an optional configuration object. Below are the available options:

  • autoplay (number): Interval in milliseconds for autoplay. If not provided, autoplay is disabled.
  • autoplayStopOnInteraction (boolean): Whether to stop autoplay on any user interaction.
  • autoheight (boolean): Dynamically adjusts the slider height based on visible slides.

API

  • init(): Initializes the Snapgrab component and binds all necessary event listeners.
  • destroy(): Unbinds event listeners and cleans up the component.
  • goToSlide(index): Scrolls to a specific slide by its index.
  • updateButtonState(): Updates the state of the navigation buttons based on the current scroll position.

Methods

handleHeight()

The handleHeight() method dynamically recalculates and adjusts the height of the slider based on the current visible slides. This method can be called again if needed to ensure the height is updated correctly. For instance, you may want to call handleHeight() after a specific event like a window resize, a slide change, or any dynamic content update that affects the height of the slider.

Example

// Assuming `slider` is an instance of Snapgrab
slider.handleHeight() // Call this method to adjust the height manually

Events

Snapgrab emits custom events during its lifecycle:

  • slideChange: Dispatched when the current slide changes. It includes a detail object with the current slide index.

Example:

snapgrab.wrapper.addEventListener('slideChange', (e) => {
    console.log('Current slide index:', e.detail.slideIndex)
})

Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository.
  2. Create your feature branch (git checkout -b feature/AmazingFeature).
  3. Commit your changes (git commit -m 'Add some AmazingFeature').
  4. Push to the branch (git push origin feature/AmazingFeature).
  5. Open a pull request.

License

This project is licensed under the MIT License - see the LICENSE file for details.