Skip to content

A high-performance, infinite scrolling grid component for Vue that provides smooth touch/mouse interactions with momentum-based scrolling.

License

Notifications You must be signed in to change notification settings

peoray/vue-thiings-grid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

18 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

The scalable and production-ready Nuxt SaaS starter kit. Nuxt SaaS Kit provides all the important building blocks, so you can focus on launching your startup and delivering value to your users.


VueThiingsGrid

A high-performance, infinite scrolling grid component for Vue that provides smooth touch/mouse interactions with momentum-based scrolling. Perfect for displaying large datasets in a grid format with custom cell renderers.

Experience ThiingsGrid in action with interactive examples and copy-paste ready code.

โœจ Features

  • ๐Ÿš€ High Performance: Only renders visible cells with optimized viewport calculations
  • ๐Ÿ“ฑ Touch & Mouse Support: Smooth interactions on both desktop and mobile
  • ๐ŸŽฏ Momentum Scrolling: Natural physics-based scrolling with inertia
  • โ™พ๏ธ Infinite Grid: Supports unlimited grid sizes with efficient rendering
  • ๐ŸŽจ Custom Renderers: Flexible cell rendering with your own components
  • ๐Ÿ”ง TypeScript Support: Full type safety with comprehensive TypeScript definitions

๐Ÿš€ Quick Start

Installation

This component is currently part of this repository. To use it in your project:

  • Copy the lib/ThiingsGrid.vue file to your project

Basic Usage

<template>
  <div :style="{ width: '100vw', height: '100vh' }">
    <ThiingsGrid :grid-size="80">
      <template #default="{ gridIndex, position }">
        <div class="absolute inset-1 flex items-center justify-center">
          {{ gridIndex }}
        </div>
      </template>
    </ThiingsGrid>
  </div>
</template>

<script setup lang="ts">
import ThiingsGrid from './path/to/ThiingsGrid.vue'
</script>

๐Ÿ“š API Reference

Props

Prop Type Required Default Description
gridSize number โœ… - Size of each grid cell in pixels
className string โœ… - CSS class name for the container
initialPosition Position โŒ { x: 0, y: 0 } Initial scroll position

Slot Props

The component provides the following slot props for custom item rendering:

Prop Type Description
gridIndex number Unique index for each grid item
position Position Current position of the item in the grid - { x: number, y: number }
isMoving boolean Whether the grid is currently in motion

Position

interface Position {
  x: number
  y: number
}

๐ŸŽจ Examples

Simple Numbers

<template>
  <ThiingsGrid :gridSize="80" class="bg-gray-100">
    <template #default="{ gridIndex }">
      <div
        class="absolute inset-1 flex items-center justify-center bg-blue-50 border border-blue-500 rounded text-sm font-bold text-blue-800"
      >
        {{ gridIndex }}
      </div>
    </template>
  </ThiingsGrid>
</template>

<script setup lang="ts">
import ThiingsGrid, { type ItemConfig } from '../../lib/ThiingsGrid.vue'
</script>

Colorful Grid

<template>
  <ThiingsGrid :grid-size="100">
    <template #default="{ gridIndex }">
      <div
        :class="`absolute inset-0 flex items-center justify-center ${getColorClass(
          gridIndex
        )} text-xs font-bold text-gray-800 shadow-sm`"
      >
        {{ gridIndex }}
      </div>
    </template>
  </ThiingsGrid>
</template>

<script setup lang="ts">
import ThiingsGrid, { type ItemConfig } from '../../lib/ThiingsGrid.vue'

const colors = [
  'bg-red-300',
  'bg-green-300',
  'bg-blue-300',
  'bg-yellow-300',
  'bg-pink-300',
  'bg-cyan-300',
]

const getColorClass = (gridIndex: number): string => {
  return colors[gridIndex % colors.length]
}
</script>

Interactive Cards

<template>
  <ThiingsGrid :grid-size="150">
    <template #default="{ gridIndex, position, isMoving }">
      <div
        :class="[
          'absolute',
          'inset-1',
          'flex',
          'flex-col',
          'items-center',
          'justify-center',
          'bg-white',
          'border',
          'border-gray-200',
          'rounded-xl',
          'p-2',
          'text-xs',
          'text-gray-800',
          'transition-shadow',
          isMoving ? 'shadow-xl' : 'shadow-md',
        ]"
      >
        <div class="text-base font-bold mb-1">#{{ gridIndex }}</div>
        <div class="text-[10px] text-gray-500">
          {{ position.x }}, {{ position.y }}
        </div>
      </div>
    </template>
  </ThiingsGrid>
</template>

<script setup lang="ts">
import ThiingsGrid, { type ItemConfig } from '../../lib/ThiingsGrid.vue'
</script>

๐ŸŽฏ Best Practices

Cell Positioning

Always use absolute positioning within your cell components for optimal performance:

// โœ… Good
<template #default="{ gridIndex }">
  <div class="absolute inset-1 ...">
    {{ gridIndex }}
  </div>
</template>

// โŒ Avoid - can cause layout issues
<template #default="{ gridIndex }">
  <div class="w-full h-full ...">
    {{ gridIndex }}
  </div>
</template>

Container Setup

Ensure the ThiingsGrid has a defined container size:

// โœ… Good - explicit container size
<div style={ width: '100vw', height: '100vh' }>
  ...
</div>

// โœ… Good - CSS classes with defined dimensions
<div class="w-screen h-screen">
  ...
</div>

๐Ÿ”ง Advanced Usage

Custom Grid Index Calculation

The gridIndex is calculated based on the grid position using a custom algorithm that provides unique indices for each cell position.

Accessing Grid Position

You can access the current grid position programmatically:

<template>
  <div>
    <ThiingsGrid
      ref="gridRef"
      :grid-size="80"
    >
      <template #default="{ gridIndex }">
        <div
        class="absolute inset-1 ...
      >
        {{ gridIndex }}
      </div>
      </template>
    </ThiingsGrid>
    <button @click="getCurrentPosition">Log Current Position</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import ThiingsGrid from '../../lib/ThiingsGrid.vue'; // Adjust path as needed

// Define the grid ref
const gridRef = ref<InstanceType<typeof ThiingsGrid> | null>(null);

// Method to get the current grid position
const getCurrentPosition = () => {
  if (gridRef.value) {
    const position = gridRef.value.publicGetCurrentPosition();
    console.log('Current position:', position);
  }
};
</script>

Responsive Grid Sizes

// useResponsiveGridSize composable
export const useResponsiveGridSize = () => {
  const gridSize = ref(80);

  const handleResize = () => {
    const width = window.innerWidth;
    if (width < 768) {
      gridSize.value = 60; // Smaller on mobile
    } else if (width < 1024) {
      gridSize.value = 80; // Medium on tablet
    } else {
      gridSize.value = 100; // Larger on desktop
    }
  };

  onMounted(() => {
    handleResize();
    window.addEventListener('resize', handleResize);
  });

  onUnmounted(() => {
    window.removeEventListener('resize', handleResize);
  });

  return gridSize;
};
</script>

<!-- ResponsiveGrid Component -->
<template>
  <ThiingsGrid :grid-size="gridSize">
    <template #default="{ gridIndex, position, isMoving }">
      <div>{{ gridIndex }}</div>
    </template>
  </ThiingsGrid>
</template>

<script setup lang="ts">
import ThiingsGrid from './ThiingsGrid.vue';

const gridSize = useResponsiveGridSize();
</script>

๐ŸŽฎ Interaction

Touch/Mouse Events

The component handles:

  • Mouse: Click and drag to pan
  • Touch: Touch and drag to pan
  • Wheel: Scroll wheel for precise movements
  • Momentum: Automatic momentum scrolling with physics

๐Ÿ” Development

Running the Demo

# Install dependencies
npm install

# Start development server
npm run dev

# Build for production
npm run build

Project Structure

src/
โ”œโ”€โ”€ examples/           # Example implementations
โ”‚   โ”œโ”€โ”€ SimpleNumbers.vue
โ”‚   โ”œโ”€โ”€ ColorfulGrid.vue
โ”‚   โ”œโ”€โ”€ EmojiFun.vue
โ”‚   โ””โ”€โ”€ CardLayout.vue
โ”œโ”€โ”€ components/         # Example implementations
โ”‚   โ”œโ”€โ”€ Playground.vue  # Example viewer
โ”‚   โ”œโ”€โ”€ SourceCode.vue  # Source code display
โ”‚   โ””โ”€โ”€ Sidebar.vue		  # Example navigation
โ”œโ”€โ”€ App.vue             # Main demo application
|
lib/
โ””โ”€โ”€ ThiingsGrid.vue    # Main component

๐Ÿค Contributing

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

๐Ÿ“„ License

MIT License - see the LICENSE file for details.

๐Ÿ™ Acknowledgments

  • Charlie Clark's React version
  • Built with Vue and TypeScript
  • Styled with Tailwind CSS
  • Bundled with Vite

About

A high-performance, infinite scrolling grid component for Vue that provides smooth touch/mouse interactions with momentum-based scrolling.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published