element-nice-ui
Version:
A Component Library for Vue.js.
265 lines (222 loc) • 7.83 kB
JavaScript
import Vue from 'vue'
import scrollbarWidth from 'element-nice-ui/src/utils/scrollbar-width'
// import { parseHeight } from './util';
class TableLayout {
constructor(options) {
this.observers = []
this.table = null
this.store = null
this.columns = null
this.fit = true
this.height = null
this.scrollX = false
this.scrollY = false
this.bodyWidth = null
this.fixedWidth = null
this.rightFixedWidth = null
this.tableHeight = null
this.headerHeight = 44 // Table Header Height
this.appendHeight = 0 // Append Slot Height
this.footerHeight = 44 // Table Footer Height
this.viewportHeight = null // Table Height - Scroll Bar Height
this.bodyHeight = null // Table Height - Table Header Height
this.fixedBodyHeight = null // Table Height - Table Header Height - Scroll Bar Height
this.gutterWidth = scrollbarWidth()
for (let name in options) {
if (options.hasOwnProperty(name)) {
this[name] = options[name]
}
}
if (!this.table) {
throw new Error('table is required for Table Layout')
}
if (!this.store) {
throw new Error('store is required for Table Layout')
}
}
updateScrollY() {
const height = this.height
if (height === null) return false
const bodyWrapper = this.table.bodyWrapper
if (this.table.$el && bodyWrapper) {
const body = bodyWrapper.querySelector('.el-table__body')
const prevScrollY = this.scrollY
const scrollY = body.offsetHeight > this.bodyHeight
this.scrollY = scrollY
return prevScrollY !== scrollY
}
return false
}
setHeight(value, prop = 'height') {
if (Vue.prototype.$isServer) return
const el = this.table.$el
if (!el) {
return value !== null ? Vue.nextTick(() => this.setHeight(value, prop)) : null
}
if (!isNaN(+value)) {
el.style[prop] = value + 'px'
} else {
el.style[prop] = value
}
this.height = el.getBoundingClientRect().height
this.updateElsHeight()
}
setMaxHeight(value) {
this.setHeight(value, 'max-height')
}
getFlattenColumns() {
const flattenColumns = []
const columns = this.table.columns
columns.forEach(column => {
if (column.isColumnGroup) {
flattenColumns.push.apply(flattenColumns, column.columns)
} else {
flattenColumns.push(column)
}
})
return flattenColumns
}
updateElsHeight() {
if (!this.table.$ready) return Vue.nextTick(() => this.updateElsHeight())
const { headerWrapper, appendWrapper, footerWrapper } = this.table.$refs
this.appendHeight = appendWrapper ? appendWrapper.offsetHeight : 0
if (this.table.showHeader && !headerWrapper) return
const headerTrElm = headerWrapper ? headerWrapper.querySelector('.el-table__header tr') : null
const noneHeader = this.headerDisplayNone(headerTrElm)
const headerHeight = (this.headerHeight = !this.table.showHeader
? 0
: headerWrapper.offsetHeight)
if (
this.table.showHeader &&
!noneHeader &&
headerWrapper.offsetWidth > 0 &&
(this.table.columns || []).length > 0 &&
headerHeight < 2
) {
return Vue.nextTick(() => this.updateElsHeight())
}
const tableHeight = (this.tableHeight = this.table.$el.clientHeight)
const footerHeight = (this.footerHeight = footerWrapper ? footerWrapper.offsetHeight : 0)
if (this.height !== null) {
this.bodyHeight = tableHeight - headerHeight - footerHeight + (footerWrapper ? 1 : 0)
}
this.fixedBodyHeight = this.scrollX ? this.bodyHeight - this.gutterWidth : this.bodyHeight
const noData = !(this.store.states.data && this.store.states.data.length)
this.viewportHeight = this.scrollX ? tableHeight - (noData ? 0 : this.gutterWidth) : tableHeight
this.updateScrollY()
this.notifyObservers('scrollable')
}
headerDisplayNone(elm) {
if (!elm) return true
let headerChild = elm
while (headerChild.tagName !== 'DIV') {
if (getComputedStyle(headerChild).display === 'none') {
return true
}
headerChild = headerChild.parentElement
}
return false
}
updateColumnsWidth() {
if (Vue.prototype.$isServer) return
const fit = this.fit
const bodyWidth = this.table.$el.clientWidth
let bodyMinWidth = 0
const flattenColumns = this.getFlattenColumns()
let flexColumns = flattenColumns.filter(column => typeof column.width !== 'number')
flattenColumns.forEach(column => {
// Clean those columns whose width changed from flex to unflex
if (typeof column.width === 'number' && column.realWidth) column.realWidth = null
})
if (flexColumns.length > 0 && fit) {
flattenColumns.forEach(column => {
bodyMinWidth += column.width || column.minWidth || 80
})
const scrollYWidth = this.scrollY ? this.gutterWidth : 0
if (bodyMinWidth <= bodyWidth - scrollYWidth) {
// DON'T HAVE SCROLL BAR
this.scrollX = false
const totalFlexWidth = bodyWidth - scrollYWidth - bodyMinWidth
if (flexColumns.length === 1) {
flexColumns[0].realWidth = (flexColumns[0].minWidth || 80) + totalFlexWidth
} else {
const allColumnsWidth = flexColumns.reduce(
(prev, column) => prev + (column.minWidth || 80),
0
)
const flexWidthPerPixel = totalFlexWidth / allColumnsWidth
let noneFirstWidth = 0
flexColumns.forEach((column, index) => {
if (index === 0) return
const flexWidth = Math.floor((column.minWidth || 80) * flexWidthPerPixel)
noneFirstWidth += flexWidth
column.realWidth = (column.minWidth || 80) + flexWidth
})
flexColumns[0].realWidth =
(flexColumns[0].minWidth || 80) + totalFlexWidth - noneFirstWidth
}
} else {
// HAVE HORIZONTAL SCROLL BAR
this.scrollX = true
flexColumns.forEach(function(column) {
column.realWidth = column.minWidth
})
}
this.bodyWidth = Math.max(bodyMinWidth, bodyWidth)
this.table.resizeState.width = this.bodyWidth
} else {
flattenColumns.forEach(column => {
if (!column.width && !column.minWidth) {
column.realWidth = 80
} else {
column.realWidth = column.width || column.minWidth
}
bodyMinWidth += column.realWidth
})
this.scrollX = bodyMinWidth > bodyWidth
this.bodyWidth = bodyMinWidth
}
const fixedColumns = this.store.states.fixedColumns
if (fixedColumns.length > 0) {
let fixedWidth = 0
fixedColumns.forEach(function(column) {
fixedWidth += column.realWidth || column.width
})
this.fixedWidth = fixedWidth
}
const rightFixedColumns = this.store.states.rightFixedColumns
if (rightFixedColumns.length > 0) {
let rightFixedWidth = 0
rightFixedColumns.forEach(function(column) {
rightFixedWidth += column.realWidth || column.width
})
this.rightFixedWidth = rightFixedWidth
}
this.notifyObservers('columns')
}
addObserver(observer) {
this.observers.push(observer)
}
removeObserver(observer) {
const index = this.observers.indexOf(observer)
if (index !== -1) {
this.observers.splice(index, 1)
}
}
notifyObservers(event) {
const observers = this.observers
observers.forEach(observer => {
switch (event) {
case 'columns':
observer.onColumnsChange(this)
break
case 'scrollable':
observer.onScrollableChange(this)
break
default:
throw new Error(`Table Layout don't have event ${event}.`)
}
})
}
}
export default TableLayout