@jgjergj/vuetify-toolkit
Version:
Vuetify.js Additional components set
403 lines (398 loc) • 13.4 kB
text/typescript
import Vue, { VNode } from 'vue'
import { VTreeviewNodeProps, getObjectValueByPath } from '../../vuetify-import'
import { VCardA, VDividerA, VTreeviewA, VPaginationA, VRowA, VIconA } from '../../shims-vuetify'
import VAdvDataTable from '../VAdvDataTable'
import treeviewScopedSlots from '../../utils/TreeviewScopedSlots'
import tableScopedSlots from '../../utils/TableScopedSlots'
import { ScopedSlot } from 'vue/types/vnode'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const VDataTableProps = ((VAdvDataTable as any).options as any).props
type ScopedSlotType = { [key: string]: ScopedSlot | undefined }
/* @vue/component */
export default Vue.extend({
name: 'v-md-view',
props: {
...VDataTableProps,
...VTreeviewNodeProps,
multiple: {
type: Boolean,
default: false
},
hierarchy: {
type: Boolean,
default: true
},
folderIcon: {
type: [String, Function],
default: undefined
},
folderOpenIcon: {
type: [String, Function],
default: undefined
},
itemIcon: {
type: [String, Function],
default: undefined
},
folderIconColor: {
type: [String, Function],
default: undefined
},
itemIconColor: {
type: [String, Function],
default: undefined
}
},
data: () => ({
currentNode: null,
openNodes: [] as any[],
treeviewCashe: new Map(),
parents: new Map(),
currentPage: 1,
x: 0,
clientwidth: 0,
treeWidth: 300,
filteredTableItemsCount: 0
}),
computed: {
treeItems (): any[] {
return this.buildTree(this.$props.items)
},
tableHeaders (): any[] {
if (this.$props.folderIcon || this.$props.folderOpenIcon || this.$props.itemIcon) {
const iconHeader = {
text: '',
value: 'itemIcon',
align: 'start',
sortable: false,
filterable: false,
divider: false,
width: 20
}
return [iconHeader, ...this.$props.headers]
}
return this.$props.headers
},
tableItems (): any[] {
if (this.$props.hierarchy) {
const currentItem = this.treeviewCashe.get(this.currentNode)
return getObjectValueByPath(currentItem, this.$props.itemChildren, [])
} else {
const allItems: any[] = []
this.getAllItems(this.$props.items, allItems)
return allItems
}
},
pageCount (): number {
if (this.$props.itemsPerPage && Array.isArray(this.tableItems)) {
if (this.$props.itemsPerPage < this.filteredTableItemsCount) {
let res = Math.floor(this.filteredTableItemsCount / this.$props.itemsPerPage)
res += (this.filteredTableItemsCount % this.$props.itemsPerPage) > 0 ? 1 : 0
return res
}
}
return 0
},
tableWidth (): number {
return this.clientwidth - 10 - this.treeWidth
}
},
methods: {
buildTree (items: any, parentkey?: any): any[] {
const newItems: any[] = []
items.map((item: any) => {
const localChildren = getObjectValueByPath(item, this.$props.itemChildren, [])
const itemKey = getObjectValueByPath(item, this.$props.itemKey, [])
if (localChildren.length > 0) {
const newChildren = this.buildTree(localChildren, itemKey)
const clone = Object.assign({}, item)
clone[this.$props.itemChildren] = newChildren
newItems.push(clone)
this.treeviewCashe.set(itemKey, item)
this.parents.set(itemKey, parentkey)
} else {
this.parents.set(itemKey, parentkey)
}
})
return newItems
},
getAllItems (items: any[], allItems: any[]) {
items.map(item => {
const localChildren = getObjectValueByPath(item, this.$props.itemChildren, [])
if (localChildren.length > 0) {
this.getAllItems(localChildren, allItems)
} else {
allItems.push(item)
}
})
},
updateTable (e: any[]) {
if (this.$props.hierarchy) {
this.currentNode = e[0]
}
},
genDivider (): VNode {
return this.$createElement(VDividerA, {
props: {
vertical: true
},
style: {
cursor: 'col-resize',
width: '10px'
},
on: {
mousedown: this.startResizing
}
})
},
/* resising parts of component */
updateDimensions () {
const rect = this.$el.getBoundingClientRect()
this.x = rect.x
this.clientwidth = rect.width
this.treeWidth = Math.min(this.clientwidth - 10, this.treeWidth)
this.$nextTick()
},
startResizing () {
this.updateDimensions();
(this.$el as HTMLBaseElement).addEventListener('mousemove', this.Resizing)
},
Resizing (e: MouseEvent) {
(this.$el as HTMLBaseElement).style.cursor = 'col-resize'
this.$el.addEventListener('mouselive', this.endResizing)
this.$el.addEventListener('mouseup', this.endResizing)
this.treeWidth = e.x - this.x
},
endResizing () {
(this.$el as HTMLBaseElement).removeEventListener('mousemove', this.Resizing);
(this.$el as HTMLBaseElement).removeEventListener('mouselive', this.endResizing);
(this.$el as HTMLBaseElement).removeEventListener('mouseup', this.endResizing);
(this.$el as HTMLBaseElement).style.cursor = ''
},
/* resising parts of component */
genPagination (): VNode| VNode[]| undefined {
if (this.$props.itemsPerPage && Array.isArray(this.tableItems)) {
// if (this.$props.itemsPerPage < this.tableItems.length) {
if (this.$props.itemsPerPage < this.filteredTableItemsCount) {
return [
this.$createElement(VDividerA, {
props: {
vertical: false
}
}),
this.$createElement(VPaginationA, {
ref: 'data-table-pagination',
props: {
length: this.pageCount,
dark: this.$props.dark,
value: this.currentPage
},
style: {
'vertical-align': 'bottom'
},
on: {
input: (e: number) => { this.currentPage = e }
}
})
]
}
}
return undefined
},
genItemIcon (item: any): VNode {
const itemKey = getObjectValueByPath(item.item, this.$props.itemKey, [])
let fn
let color
if (this.treeviewCashe.get(itemKey)) {
if (this.openNodes.indexOf(itemKey) >= 0) {
fn = this.$props.folderOpenIcon
} else {
fn = this.$props.folderIcon
}
color = this.$props.folderIconColor
} else {
fn = this.$props.itemIcon
color = this.$props.itemIconColor
}
return this.$createElement(VIconA, {
props: {
color: color
}
}, fn)
},
genTableScopedSlots (): ScopedSlotType {
let slots = tableScopedSlots(this.$scopedSlots)
if (this.$props.folderIcon || this.$props.folderOpenIcon || this.$props.itemIcon) {
slots = Object.assign(slots, { 'item.itemIcon': this.genItemIcon })
}
return slots as ScopedSlotType
},
genTable (): VNode {
const slots = [
'body',
'body.append',
'body.prepend',
'footer',
'loading',
'no-data',
'no-results',
'header',
'progress',
'top'
]
.filter(slotName => this.$slots[slotName])
.map(slotName => this.$createElement('template', {
slot: slotName
}, this.$slots[slotName]))
const TableScopedSlots = this.genTableScopedSlots()
return this.$createElement(VAdvDataTable, {
ref: 'data-table',
props: {
selected: this.$props.selectable,
dense: this.$props.dense,
headers: this.tableHeaders,
items: this.tableItems,
itemsPerPage: this.$props.itemsPerPage,
page: this.currentPage,
hideDefaultFooter: true,
showSelect: this.$props.selectable,
singleSelect: !this.$props.multiple,
headerIcon: this.$props.headerIcon,
headerIconColor: this.$props.headerIconColor,
upIcon: this.$props.upIcon,
downIcon: this.$props.downIcon,
filterIcon: this.$props.filterIcon,
filterActiveIcon: this.$props.filterActiveIcon,
filterSelectionIcon: this.$props.filterSelectionIcon,
filterActiveIconColor: this.$props.filterActiveIconColor,
sortIcon: this.$props.sortIcon
},
style: {
width: this.tableWidth + 'px'
},
scopedSlots: TableScopedSlots,
on: {
'click:row': (e: any) => this.synchronyzeActiveNode(e),
'items-count-change': (e: number) => { this.filteredTableItemsCount = e }
}
}, slots)
},
getParents (key: number|string): any[] {
const result = []
const parent = this.parents.get(key)
if (parent) {
result.push(parent)
const p = this.getParents(parent)
result.push(...p)
}
return result
},
synchronyzeActiveNode (e: object) {
const itemKey = getObjectValueByPath(e, this.$props.itemKey, [])
this.currentNode = this.parents.get(itemKey)
const parents = this.getParents(itemKey)
this.openNodes.push(...parents)
this.$nextTick(() => {})
},
genCombinedTreeViewSlot (props: any): VNode {
if (this.$scopedSlots.prependTree) {
return this.$createElement('div', {},
[this.genItemIcon(props), this.$scopedSlots.prependTree(props)]
)
} else {
return this.$createElement('div', {},
[this.genItemIcon(props)]
)
}
},
genTreeViewScopedSlots (): ScopedSlotType {
let slots
if (this.$scopedSlots.prependTree && (this.$props.folderIcon || this.$props.folderOpenIcon)) {
// needs combined scoped slot
slots = Object.assign({}, { prepend: this.genCombinedTreeViewSlot })
if (this.$scopedSlots.appendTree) {
const slotAppend = this.$scopedSlots.appendTree
slots = Object.assign({}, { append: (props: any) => slotAppend(props) })
}
if (this.$scopedSlots.labelTree) {
const slotLabel = this.$scopedSlots.labelTree
slots = Object.assign({}, { label: (props: any) => slotLabel(props) })
}
} else {
slots = treeviewScopedSlots(this.$scopedSlots)
}
if (this.$props.folderIcon || this.$props.folderOpenIcon) {
// if (!slots.prependTree) {
// const slotPrepend = this.$scopedSlots.labelTree
// slots = Object.assign(slots, { prepend: this.genItemIcon })
// }
}
return slots as ScopedSlotType
},
genTreeView (): VNode {
return this.$createElement(VTreeviewA, {
ref: 'treeview',
props: {
dense: this.$props.dense,
hoverable: this.$props.hoverable,
multipleActive: false,
search: this.$props.search,
items: this.treeItems,
active: [this.currentNode],
activatable: true,
activeClass: this.$props.activeClass,
color: this.$props.color,
expandIcon: this.$props.expandIcon,
itemChildren: this.$props.itemChildren,
itemDisabled: this.$props.itemDisabled,
itemKey: this.$props.itemKey,
itemText: this.$props.itemText,
loadChildren: this.$props.loadChildren,
loadingIcon: this.$props.loadingIcon,
offIcon: this.$props.offIcon,
onIcon: this.$props.onIcon,
open: this.openNodes,
openOnClick: this.$props.openOnClick,
rounded: this.$props.rounded,
selectable: false,
selectedColor: this.$props.selectedColor,
shaped: this.$props.shaped,
transition: this.$props.transition
},
style: {
width: this.treeWidth + 'px',
padding: '16px'
},
scopedSlots: this.genTreeViewScopedSlots(),
on: {
'update:active': this.updateTable,
'update:open': (e: any) => { this.openNodes = e }
}
})
}
},
mounted () {
window.addEventListener('resize', this.updateDimensions)
window.addEventListener('load', this.updateDimensions)
this.updateDimensions()
},
beforeDestroy () {
window.removeEventListener('resize', this.updateDimensions)
window.removeEventListener('load', this.updateDimensions)
},
render (): VNode {
return this.$createElement(VCardA, {},
[
this.$createElement(VRowA, {
props: {
align: 'stretch'
}
}, [
this.genTreeView(),
this.genDivider(),
this.genTable()
]),
this.genPagination()
])
}
})