UNPKG

vuetify

Version:

Vue Material Component Framework

379 lines (347 loc) 11.3 kB
// Helpers import { wrapInArray, sortItems, deepEqual, groupItems, searchItems, fillArray } from '../../util/helpers' import Vue, { VNode } from 'vue' // Types import { DataOptions, DataPagination, DataScopeProps, DataSortFunction, DataGroupFunction, DataSearchFunction, ItemGroup, } from 'vuetify/types' import { PropValidator } from 'vue/types/options' export default Vue.extend({ name: 'v-data', inheritAttrs: false, props: { items: { type: Array, default: () => [], } as PropValidator<any[]>, options: { type: Object, default: () => ({}), } as PropValidator<Partial<DataOptions>>, sortBy: { type: [String, Array], default: () => [], } as PropValidator<string | string[]>, sortDesc: { type: [Boolean, Array], default: () => [], } as PropValidator<boolean | boolean[]>, customSort: { type: Function, default: sortItems, } as PropValidator<DataSortFunction>, mustSort: Boolean, multiSort: Boolean, page: { type: Number, default: 1, }, itemsPerPage: { type: Number, default: 10, }, groupBy: { type: [String, Array], default: () => [], } as PropValidator<string | string[]>, groupDesc: { type: [Boolean, Array], default: () => [], } as PropValidator<boolean | boolean[]>, customGroup: { type: Function, default: groupItems, } as PropValidator<DataGroupFunction>, locale: { type: String, default: 'en-US', }, disableSort: Boolean, disablePagination: Boolean, disableFiltering: Boolean, search: String, customFilter: { type: Function, default: searchItems, } as PropValidator<DataSearchFunction>, serverItemsLength: { type: Number, default: -1, }, }, data () { let internalOptions: DataOptions = { page: this.page, itemsPerPage: this.itemsPerPage, sortBy: wrapInArray(this.sortBy), sortDesc: wrapInArray(this.sortDesc), groupBy: wrapInArray(this.groupBy), groupDesc: wrapInArray(this.groupDesc), mustSort: this.mustSort, multiSort: this.multiSort, } if (this.options) { internalOptions = Object.assign(internalOptions, this.options) } const { sortBy, sortDesc, groupBy, groupDesc } = internalOptions const sortDiff = sortBy.length - sortDesc.length const groupDiff = groupBy.length - groupDesc.length if (sortDiff > 0) { internalOptions.sortDesc.push(...fillArray(sortDiff, false)) } if (groupDiff > 0) { internalOptions.groupDesc.push(...fillArray(groupDiff, false)) } return { internalOptions, } }, computed: { itemsLength (): number { return this.serverItemsLength >= 0 ? this.serverItemsLength : this.filteredItems.length }, pageCount (): number { return this.internalOptions.itemsPerPage <= 0 ? 1 : Math.ceil(this.itemsLength / this.internalOptions.itemsPerPage) }, pageStart (): number { if (this.internalOptions.itemsPerPage === -1 || !this.items.length) return 0 return (this.internalOptions.page - 1) * this.internalOptions.itemsPerPage }, pageStop (): number { if (this.internalOptions.itemsPerPage === -1) return this.itemsLength if (!this.items.length) return 0 return Math.min(this.itemsLength, this.internalOptions.page * this.internalOptions.itemsPerPage) }, isGrouped (): boolean { return !!this.internalOptions.groupBy.length }, pagination (): DataPagination { return { page: this.internalOptions.page, itemsPerPage: this.internalOptions.itemsPerPage, pageStart: this.pageStart, pageStop: this.pageStop, pageCount: this.pageCount, itemsLength: this.itemsLength, } }, filteredItems (): any[] { let items = this.items.slice() if (!this.disableFiltering && this.serverItemsLength <= 0) { items = this.customFilter(items, this.search) } return items }, computedItems (): any[] { let items = this.filteredItems.slice() if (!this.disableSort && this.serverItemsLength <= 0) { items = this.sortItems(items) } if (!this.disablePagination && this.serverItemsLength <= 0) { items = this.paginateItems(items) } return items }, groupedItems (): ItemGroup<any>[] | null { return this.isGrouped ? this.groupItems(this.computedItems) : null }, scopedProps (): DataScopeProps { return { sort: this.sort, sortArray: this.sortArray, group: this.group, items: this.computedItems, options: this.internalOptions, updateOptions: this.updateOptions, pagination: this.pagination, groupedItems: this.groupedItems, originalItemsLength: this.items.length, } }, computedOptions (): DataOptions { return { ...this.options } as DataOptions }, }, watch: { computedOptions: { handler (options: DataOptions, old: DataOptions) { if (deepEqual(options, old)) return this.updateOptions(options) }, deep: true, immediate: true, }, internalOptions: { handler (options: DataOptions, old: DataOptions) { if (deepEqual(options, old)) return this.$emit('update:options', options) }, deep: true, immediate: true, }, page (page: number) { this.updateOptions({ page }) }, 'internalOptions.page' (page: number) { this.$emit('update:page', page) }, itemsPerPage (itemsPerPage: number) { this.updateOptions({ itemsPerPage }) }, 'internalOptions.itemsPerPage' (itemsPerPage: number) { this.$emit('update:items-per-page', itemsPerPage) }, sortBy (sortBy: string | string[]) { this.updateOptions({ sortBy: wrapInArray(sortBy) }) }, 'internalOptions.sortBy' (sortBy: string[], old: string[]) { !deepEqual(sortBy, old) && this.$emit('update:sort-by', Array.isArray(this.sortBy) ? sortBy : sortBy[0]) }, sortDesc (sortDesc: boolean | boolean[]) { this.updateOptions({ sortDesc: wrapInArray(sortDesc) }) }, 'internalOptions.sortDesc' (sortDesc: boolean[], old: boolean[]) { !deepEqual(sortDesc, old) && this.$emit('update:sort-desc', Array.isArray(this.sortDesc) ? sortDesc : sortDesc[0]) }, groupBy (groupBy: string | string[]) { this.updateOptions({ groupBy: wrapInArray(groupBy) }) }, 'internalOptions.groupBy' (groupBy: string[], old: string[]) { !deepEqual(groupBy, old) && this.$emit('update:group-by', Array.isArray(this.groupBy) ? groupBy : groupBy[0]) }, groupDesc (groupDesc: boolean | boolean[]) { this.updateOptions({ groupDesc: wrapInArray(groupDesc) }) }, 'internalOptions.groupDesc' (groupDesc: boolean[], old: boolean[]) { !deepEqual(groupDesc, old) && this.$emit('update:group-desc', Array.isArray(this.groupDesc) ? groupDesc : groupDesc[0]) }, multiSort (multiSort: boolean) { this.updateOptions({ multiSort }) }, 'internalOptions.multiSort' (multiSort: boolean) { this.$emit('update:multi-sort', multiSort) }, mustSort (mustSort: boolean) { this.updateOptions({ mustSort }) }, 'internalOptions.mustSort' (mustSort: boolean) { this.$emit('update:must-sort', mustSort) }, pageCount: { handler (pageCount: number) { this.$emit('page-count', pageCount) }, immediate: true, }, computedItems: { handler (computedItems: any[]) { this.$emit('current-items', computedItems) }, immediate: true, }, pagination: { handler (pagination: DataPagination, old: DataPagination) { if (deepEqual(pagination, old)) return this.$emit('pagination', this.pagination) }, immediate: true, }, }, methods: { toggle (key: string, oldBy: string[], oldDesc: boolean[], page: number, mustSort: boolean, multiSort: boolean) { let by = oldBy.slice() let desc = oldDesc.slice() const byIndex = by.findIndex((k: string) => k === key) if (byIndex < 0) { if (!multiSort) { by = [] desc = [] } by.push(key) desc.push(false) } else if (byIndex >= 0 && !desc[byIndex]) { desc[byIndex] = true } else if (!mustSort) { by.splice(byIndex, 1) desc.splice(byIndex, 1) } else { desc[byIndex] = false } // Reset page to 1 if sortBy or sortDesc have changed if (!deepEqual(by, oldBy) || !deepEqual(desc, oldDesc)) { page = 1 } return { by, desc, page } }, group (key: string): void { const { by: groupBy, desc: groupDesc, page } = this.toggle( key, this.internalOptions.groupBy, this.internalOptions.groupDesc, this.internalOptions.page, true, false ) this.updateOptions({ groupBy, groupDesc, page }) }, sort (key: string | string[]): void { if (Array.isArray(key)) return this.sortArray(key) const { by: sortBy, desc: sortDesc, page } = this.toggle( key, this.internalOptions.sortBy, this.internalOptions.sortDesc, this.internalOptions.page, this.internalOptions.mustSort, this.internalOptions.multiSort ) this.updateOptions({ sortBy, sortDesc, page }) }, sortArray (sortBy: string[]) { const sortDesc = sortBy.map(s => { const i = this.internalOptions.sortBy.findIndex((k: string) => k === s) return i > -1 ? this.internalOptions.sortDesc[i] : false }) this.updateOptions({ sortBy, sortDesc }) }, updateOptions (options: any) { this.internalOptions = { ...this.internalOptions, ...options, page: this.serverItemsLength < 0 ? Math.max(1, Math.min(options.page || this.internalOptions.page, this.pageCount)) : options.page || this.internalOptions.page, } }, sortItems (items: any[]): any[] { let sortBy = this.internalOptions.sortBy let sortDesc = this.internalOptions.sortDesc if (this.internalOptions.groupBy.length) { sortBy = [...this.internalOptions.groupBy, ...sortBy] sortDesc = [...this.internalOptions.groupDesc, ...sortDesc] } return this.customSort(items, sortBy, sortDesc, this.locale) }, groupItems (items: any[]): ItemGroup<any>[] { return this.customGroup(items, this.internalOptions.groupBy, this.internalOptions.groupDesc) }, paginateItems (items: any[]): any[] { // Make sure we don't try to display non-existant page if items suddenly change // TODO: Could possibly move this to pageStart/pageStop? if (this.serverItemsLength === -1 && items.length <= this.pageStart) { this.internalOptions.page = Math.max(1, this.internalOptions.page - 1) } return items.slice(this.pageStart, this.pageStop) }, }, render (): VNode { return this.$scopedSlots.default && this.$scopedSlots.default(this.scopedProps) as any }, })