@indielayer/ui
Version:
Indielayer UI Components with Tailwind CSS build for Vue 3
286 lines (233 loc) β’ 7.93 kB
Markdown
# Indielayer - Virtual Vue
Vue 3 port of [react-window](https://github.com/bvaughn/react-window) - Efficient virtualized list and grid components using the Composition API.
## Overview
High-performance virtualized list and grid components for Vue 3 applications. It renders only the visible items in large datasets, dramatically improving performance when dealing with thousands of rows or cells.
## Quick Start
### List Example
```vue
<script setup lang="ts">
import { XVirtualList } from '@indielayer/ui'
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i}`)
</script>
<template>
<XVirtualList
:row-count="items.length"
:row-height="50"
:style="{ height: '400px' }"
>
<template #row="{ index, style }">
<div :style="style" class="row">
{{ items[index] }}
</div>
</template>
</XVirtualList>
</template>
<style scoped>
.row {
display: flex;
align-items: center;
padding: 0 1rem;
border-bottom: 1px solid #eee;
}
</style>
```
### Grid Example
```vue
<script setup lang="ts">
import { XVirtualGrid } from '@indielayer/ui'
</script>
<template>
<XVirtualGrid
:row-count="1000"
:column-count="1000"
:row-height="50"
:column-width="100"
:style="{ height: '500px', width: '800px' }"
>
<template #cell="{ rowIndex, columnIndex, style }">
<div :style="style" class="cell">
Row {{ rowIndex }}, Col {{ columnIndex }}
</div>
</template>
</XVirtualGrid>
</template>
<style scoped>
.cell {
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #ddd;
}
</style>
```
## Features
- β‘οΈ **Virtual scrolling** - Only renders visible items
- π **Variable sizes** - Fixed, variable, or dynamic item sizes
- π **Dynamic measurement** - Automatic height measurement with `useDynamicRowHeight`
- π **RTL support** - Right-to-left language support
- π₯οΈ **SSR compatible** - Works with server-side rendering
- π **TypeScript** - Full type definitions included
- π― **Imperative API** - Programmatic scrolling via template refs
- βΏοΈ **ARIA support** - Accessibility attributes built-in
- π **Overscan** - Render extra items to reduce flicker
- βΎοΈ **Infinite loading** - Built-in support for infinite scroll/pagination
## Components & Composables
### List
Virtualized list component for rendering large datasets with many rows.
```vue
<script setup lang="ts">
import { XVirtualList } from '@indielayer/ui'
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i}`)
</script>
<template>
<XVirtualList
:row-count="items.length"
:row-height="50"
:style="{ height: '400px' }"
>
<template #row="{ index, style }">
<div :style="style">
{{ items[index] }}
</div>
</template>
</XVirtualList>
</template>
```
### Grid
Virtualized grid component for rendering data with rows and columns.
```vue
<script setup lang="ts">
import { XVirtualGrid } from '@indielayer/ui'
const rowCount = 1000
const columnCount = 1000
</script>
<template>
<XVirtualGrid
:row-count="rowCount"
:column-count="columnCount"
:row-height="50"
:column-width="100"
:style="{ height: '400px', width: '600px' }"
>
<template #cell="{ rowIndex, columnIndex, style }">
<div :style="style">
Row {{ rowIndex }}, Col {{ columnIndex }}
</div>
</template>
</XVirtualGrid>
</template>
```
### Infinite Loader
Utility for loading data on-demand as users scroll through lists.
**Using the composable (recommended):**
```vue
<script setup lang="ts">
import { ref } from 'vue'
import { XVirtualList, useInfiniteLoader } from '@indielayer/ui'
const items = ref<string[]>([])
const hasMoreData = ref(true)
const isRowLoaded = (index: number) => index < items.value.length
const loadMoreRows = async (startIndex: number, stopIndex: number) => {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000))
for (let i = startIndex; i <= stopIndex; i++) {
items.value[i] = `Item ${i}`
}
if (items.value.length >= 1000) {
hasMoreData.value = false
}
}
const { onRowsRendered } = useInfiniteLoader({
isRowLoaded,
loadMoreRows,
rowCount: hasMoreData.value ? items.value.length + 50 : items.value.length,
minimumBatchSize: 20,
threshold: 15,
})
</script>
<template>
<XVirtualList
:row-count="hasMoreData ? items.length + 50 : items.length"
:row-height="50"
:style="{ height: '400px' }"
:on-rows-rendered="onRowsRendered"
>
<template #row="{ index, style }">
<div :style="style">
{{ items[index] || 'Loading...' }}
</div>
</template>
</XVirtualList>
</template>
```
**Using the component:**
```vue
<script setup lang="ts">
import { ref } from 'vue'
import { XVirtualList, XInfiniteLoader } from '@indielayer/ui'
const items = ref<string[]>([])
const isRowLoaded = (index: number) => index < items.value.length
const loadMoreRows = async (startIndex: number, stopIndex: number) => {
for (let i = startIndex; i <= stopIndex; i++) {
items.value[i] = `Item ${i}`
}
}
</script>
<template>
<XInfiniteLoader
:is-row-loaded="isRowLoaded"
:load-more-rows="loadMoreRows"
:row-count="1000"
>
<template #default="{ onRowsRendered }">
<XVirtualList
:row-count="1000"
:row-height="50"
:style="{ height: '400px' }"
:on-rows-rendered="onRowsRendered"
>
<template #row="{ index, style }">
<div :style="style">
{{ items[index] || 'Loading...' }}
</div>
</template>
</XVirtualList>
</template>
</XInfiniteLoader>
</template>
```
## API
### XVirtualList Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `rowCount` | `number` | β
| Number of rows in the list |
| `rowHeight` | `number \| string \| function` | β
| Row height (px, %, or function) |
| `style` | `CSSProperties` | β | Container styles (should include height) |
| `rowProps` | `object` | β | Additional props passed to row slot |
| `overscanCount` | `number` | β | Extra rows to render (default: 3) |
| `defaultHeight` | `number` | β | Default height for SSR |
| `onRowsRendered` | `function` | β | Callback when visible rows change |
| `onResize` | `function` | β | Callback when container resizes |
### XVirtualGrid Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `rowCount` | `number` | β
| Number of rows |
| `columnCount` | `number` | β
| Number of columns |
| `rowHeight` | `number \| string \| function` | β
| Row height |
| `columnWidth` | `number \| string \| function` | β
| Column width |
| `style` | `CSSProperties` | β | Container styles |
| `cellProps` | `object` | β | Additional props passed to cell slot |
| `overscanCount` | `number` | β | Extra rows/columns (default: 3) |
| `dir` | `'ltr' \| 'rtl' \| 'auto'` | β | Text direction |
| `onCellsRendered` | `function` | β | Callback when visible cells change |
| `onResize` | `function` | β | Callback when container resizes |
### Infinite Loader Props
| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `isRowLoaded` | `(index: number) => boolean` | β
| - | Function to check if a row is loaded |
| `loadMoreRows` | `(start: number, stop: number) => Promise<void>` | β
| - | Async function to load more rows |
| `rowCount` | `number` | β
| - | Total row count (can be estimated) |
| `minimumBatchSize` | `number` | β | 10 | Min rows to load per request |
| `threshold` | `number` | β | 15 | Pre-fetch distance in rows |
## Credits
This library is a Vue 3 port of [react-window](https://github.com/bvaughn/react-window) by Brian Vaughn. All credit for the original design and virtualization algorithms goes to the original author and contributors.