ans-ui
Version:
A vue-based UI components library for analysys
221 lines (194 loc) • 6.84 kB
JavaScript
import Vue from 'vue'
import { limitedLoop } from '../../../../src/util'
export default class TableLayout {
constructor (options) {
this.store = null
this.table = null
this.fit = true
this.showHeader = true
this.scrollX = false
this.scrollY = false
this.bodyWidth = null
this.tableWidth = null
this.tableWrapperWidth = null
this.fixedLeftWidth = 0
this.fixedRightWidth = 0
this.height = null
this.tableHeight = null
this.headerHeight = null
this.bodyHeight = null
this.parentHeight = null
this.observers = []
for (const name in options) {
if (Object.prototype.hasOwnProperty.call(options, name)) {
this[name] = options[name]
}
}
}
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
default:
throw new Error(`Table Layout: Can't find event ${event}.`)
}
})
}
updateScrollY () {
const restrict = this.table.restrict
if (!restrict) {
const height = this.height
if (typeof height !== 'string' && typeof height !== 'number') return
}
const bodyTable = this.table.$refs.bodyTable
if (this.table.$el && bodyTable) {
const oldScrollY = this.scrollY
const tableBodyHeight = bodyTable.$el.offsetHeight
if (tableBodyHeight === 0) {
// can't use nexttick, dead loop
return limitedLoop.setTimeout(this.updateScrollY, this, 0)
}
// reset
this.updateScrollY.startTime = null
if (this.table.restrict) {
const parentHeight = this.parentHeight = this.table.$el.parentNode.clientHeight
if (this.table.$refs.scroller.$refs.content.clientHeight + this.headerHeight > parentHeight) {
this.table.$el.style.height = parentHeight + 'px'
this.tableHeight = parentHeight
this.bodyHeight = this.tableHeight - this.headerHeight
this.scrollY = true
} else {
if (!this.bodyHeight) {
this.scrollY = false
}
}
Vue.nextTick(() => this.table.debouncedCheckScrollable())
} else {
this.scrollY = tableBodyHeight > this.bodyHeight
}
if (oldScrollY !== this.scrollY) {
this.updateColumnsWidth()
}
}
}
setHeight (value) {
const el = this.table.$el
if (typeof value === 'string' && /^\d+$/.test(value)) {
value = Number(value)
}
this.height = value
if (!el && (value || value === 0)) {
return limitedLoop.nextTick(this.setHeight, this, value)
}
// reset
this.setHeight.startTime = null
if (typeof value === 'number') {
el.style.height = value + 'px'
this.updateElsHeight()
} else if (typeof value === 'string') {
el.style.height = value
this.updateElsHeight()
}
}
updateElsHeight () {
if (!this.table.$ready) return
const { headerWrapper, scroller } = this.table.$refs
const { $el, restrict } = this.table
if (this.showHeader && !headerWrapper) return
const headerHeight = this.headerHeight = !this.showHeader ? 0 : headerWrapper.offsetHeight
if (this.showHeader && headerWrapper.clientWidth > 0 && (this.store.states.columns || []).length > 0 && headerHeight < 10) {
return limitedLoop.nextTick(this.updateElsHeight, this)
}
// reset
this.updateElsHeight.startTime = null
if (restrict && this.parentHeight) {
const data = this.store.states.data
if (!data || !data.length) return
if (scroller.$el.clientHeight > scroller.$refs.content.clientHeight) {
$el.style.height = scroller.$refs.content.clientHeight + this.headerHeight + 'px'
this.bodyHeight = null
return limitedLoop.nextTick(this.updateElsHeight, this)
} else if (Math.abs(scroller.$el.clientHeight + this.headerHeight - $el.clientHeight) > 2) {
$el.style.height = 'auto'
this.bodyHeight = null
return limitedLoop.nextTick(this.updateElsHeight, this)
} else if ($el.parentNode && this.parentHeight !== $el.parentNode.clientHeight) {
$el.style.height = 'auto'
this.bodyHeight = null
this.parentHeight = $el.parentNode.clientHeight
return limitedLoop.nextTick(this.updateElsHeight, this)
}
}
const tableHeight = this.tableHeight = $el.clientHeight
if (this.height !== null && (!isNaN(this.height) || typeof this.height === 'string')) {
this.bodyHeight = tableHeight - headerHeight
}
this.updateScrollY()
Vue.nextTick(() => this.table.debouncedCheckScrollable())
}
updateColumnsWidth () {
const bodyWidth = this.tableWrapperWidth = this.table.$el.clientWidth
const fit = this.fit
let bodyMinWidth = 0
const columns = this.store.states.leafColumns
const flexColumns = columns.filter((column) => typeof column.width !== 'number')
if (flexColumns.length > 0 && fit) {
columns.forEach((column) => {
bodyMinWidth += column.width || column.minWidth
})
if (bodyMinWidth - bodyWidth <= 1) {
this.scrollX = false
const totalFlexWidth = bodyWidth - bodyMinWidth
const columnFlexWidth = totalFlexWidth / flexColumns.length
flexColumns.forEach((column) => {
column.currentWidth = column.minWidth + columnFlexWidth
})
} else {
this.scrollX = true
flexColumns.forEach((column) => {
column.currentWidth = column.minWidth
})
}
this.bodyWidth = Math.max(bodyMinWidth, bodyWidth)
} else {
columns.forEach((column) => {
bodyMinWidth += column.width
column.currentWidth = column.width
})
this.scrollX = bodyMinWidth - bodyWidth > 1
this.bodyWidth = bodyMinWidth
}
const fixedLeftLeafColumns = this.store.states.fixedLeftLeafColumns
if (fixedLeftLeafColumns.length > 0) {
let fixedLeftWidth = 0
fixedLeftLeafColumns.forEach(column => {
fixedLeftWidth += column.currentWidth
})
this.fixedLeftWidth = fixedLeftWidth
}
const fixedRightLeafColumns = this.store.states.fixedRightLeafColumns
if (fixedRightLeafColumns.length > 0) {
let fixedRightWidth = 0
fixedRightLeafColumns.forEach(column => {
fixedRightWidth += column.currentWidth
})
this.fixedRightWidth = fixedRightWidth
}
this.store.updateRenderData()
}
updateTableWidth (width) {
this.tableWidth = width || this.bodyWidth
}
}