vuetifyjs / vuetify
1 1
import './VVirtualTable.sass'
2

3
// Components
4 1
import VSimpleTable from './VSimpleTable'
5

6
// Types
7
import { VNode, VNodeChildren } from 'vue'
8
import { PropValidator } from 'vue/types/options'
9 1
import mixins from '../../util/mixins'
10

11
// Utiltiies
12 1
import { convertToUnit, debounce } from '../../util/helpers'
13

14
// Types
15 1
const baseMixins = mixins(VSimpleTable)
16

17
interface options extends InstanceType<typeof baseMixins> {
18
  $refs: {
19
    table: HTMLElement
20
  }
21
  cachedItems: VNodeChildren
22
}
23

24 1
export default baseMixins.extend<options>().extend({
25
  name: 'v-virtual-table',
26

27
  props: {
28
    chunkSize: {
29
      type: Number,
30
      default: 25,
31
    },
32
    headerHeight: {
33
      type: Number,
34
      default: 48,
35
    },
36
    items: {
37
      type: Array,
38 0
      default: () => ([]),
39
    } as PropValidator<any[]>,
40
    rowHeight: {
41
      type: Number,
42
      default: 48,
43
    },
44
  },
45

46 1
  data: () => ({
47
    scrollTop: 0,
48
    oldChunk: 0,
49
    scrollDebounce: null as any,
50
    invalidateCache: false,
51
  }),
52

53
  computed: {
54 1
    itemsLength (): number {
55 1
      return this.items.length
56
    },
57 0
    totalHeight (): number {
58 0
      return (this.itemsLength * this.rowHeight) + this.headerHeight
59
    },
60 1
    topIndex (): number {
61 1
      return Math.floor(this.scrollTop / this.rowHeight)
62
    },
63 1
    chunkIndex (): number {
64 1
      return Math.floor(this.topIndex / this.chunkSize)
65
    },
66 1
    startIndex (): number {
67 1
      return Math.max(0, (this.chunkIndex * this.chunkSize) - this.chunkSize)
68
    },
69 1
    offsetTop (): number {
70 1
      return Math.max(0, this.startIndex * this.rowHeight)
71
    },
72 1
    stopIndex (): number {
73 1
      return Math.min(this.startIndex + (this.chunkSize * 3), this.itemsLength)
74
    },
75 1
    offsetBottom (): number {
76 1
      return Math.max(0, (this.itemsLength - this.stopIndex - this.startIndex) * this.rowHeight)
77
    },
78
  },
79

80
  watch: {
81 0
    chunkIndex (newValue, oldValue) {
82 0
      this.oldChunk = oldValue
83
    },
84 1
    items () {
85 1
      this.cachedItems = null
86 1
      this.$refs.table.scrollTop = 0
87
    },
88
  },
89

90 1
  created () {
91 1
    this.cachedItems = null
92
  },
93

94 1
  mounted () {
95 1
    this.scrollDebounce = debounce(this.onScroll, 50)
96

97 1
    this.$refs.table.addEventListener('scroll', this.scrollDebounce, { passive: true })
98
  },
99

100 0
  beforeDestroy () {
101 0
    this.$refs.table.removeEventListener('scroll', this.scrollDebounce)
102
  },
103

104
  methods: {
105 1
    createStyleHeight (height: number) {
106 1
      return {
107
        height: `${height}px`,
108
      }
109
    },
110 1
    genBody () {
111 1
      if (this.cachedItems === null || this.chunkIndex !== this.oldChunk) {
112 1
        this.cachedItems = this.genItems()
113 1
        this.oldChunk = this.chunkIndex
114
      }
115

116 1
      return this.$createElement('tbody', [
117
        this.$createElement('tr', { style: this.createStyleHeight(this.offsetTop) }),
118
        this.cachedItems,
119
        this.$createElement('tr', { style: this.createStyleHeight(this.offsetBottom) }),
120
      ])
121
    },
122 1
    genItems () {
123 1
      return this.$scopedSlots.items!({ items: this.items.slice(this.startIndex, this.stopIndex) })
124
    },
125 0
    onScroll (e: Event) {
126 0
      const target = e.target as Element
127 0
      this.scrollTop = target.scrollTop
128
    },
129 1
    genTable () {
130 1
      return this.$createElement('div', {
131
        ref: 'table',
132
        staticClass: 'v-virtual-table__table',
133
      }, [
134
        this.$createElement('table', [
135
          this.$slots['body.before'],
136
          this.genBody(),
137
          this.$slots['body.after'],
138
        ]),
139
      ])
140
    },
141 1
    genWrapper () {
142 1
      return this.$createElement('div', {
143
        staticClass: 'v-virtual-table__wrapper',
144
        style: {
145
          height: convertToUnit(this.height),
146
        },
147
      }, [
148
        this.genTable(),
149
      ])
150
    },
151
  },
152

153 1
  render (h): VNode {
154 1
    return h('div', {
155
      staticClass: 'v-data-table v-virtual-table',
156
      class: this.classes,
157
    }, [
158
      this.$slots.top,
159
      this.genWrapper(),
160
      this.$slots.bottom,
161
    ])
162
  },
163
})

Read our documentation on viewing source code .

Loading