Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(tables): make b-th extend b-td instead of using functional wrappers #4156

Merged
merged 12 commits into from
Sep 25, 2019
185 changes: 0 additions & 185 deletions src/components/table/helpers/table-cell.js

This file was deleted.

186 changes: 177 additions & 9 deletions src/components/table/td.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,185 @@
import Vue from '../../utils/vue'
import { omit } from '../../utils/object'
import { props as cellProps, BTableCell } from './helpers/table-cell'
import toString from '../../utils/to-string'
import { isUndefinedOrNull } from '../../utils/inspect'
import normalizeSlotMixin from '../../mixins/normalize-slot'

export const props = omit(cellProps, ['header'])
const digitsRx = /^\d+$/

// Parse a rowspan or colspan into a digit (or null if < 1 or NaN)
const parseSpan = val => {
val = parseInt(val, 10)
return digitsRx.test(String(val)) && val > 0 ? val : null
}

/* istanbul ignore next */
const spanValidator = val => isUndefinedOrNull(val) || parseSpan(val) > 0

export const props = {
variant: {
type: String,
default: null
},
colspan: {
type: [Number, String],
default: null,
validator: spanValidator
},
rowspan: {
type: [Number, String],
default: null,
validator: spanValidator
},
stackedHeading: {
type: String,
default: null
},
stickyColumn: {
type: Boolean,
default: false
}
}

// @vue/component
export const BTd = /*#__PURE__*/ Vue.extend({
name: 'BTd',
functional: true,
name: 'BTableCell',
mixins: [normalizeSlotMixin],
inheritAttrs: false,
inject: {
// Injections for feature / attribute detection
bvTable: {
default: null
},
bvTableTbody: {
default: null
},
bvTableThead: {
default: null
},
bvTableTfoot: {
default: null
},
bvTableTr: {
default: null
}
},
props,
render(h, { props, data, children }) {
// `data` already includes any listeners
data.props = { ...props, header: false }
return h(BTableCell, data, children)
computed: {
tag() {
// Overridden by <b-th>
return 'td'
},
isDark() {
return this.bvTable && this.bvTable.dark
},
isStacked() {
return this.bvTable && this.bvTable.isStacked
},
isStackedCell() {
// We only support stacked-heading in tbody in stacked mode
return this.bvTableTbody && this.isStacked
},
isResponsive() {
return this.bvTable && this.bvTable.isResponsive && !this.isStacked
},
isStickyHeader() {
// Needed to handle header background classes, due to lack of
// background color inheritance with Bootstrap v4 table CSS
// Sticky headers only apply to cells in table `thead`
return (
!this.isStacked &&
this.bvTable &&
this.bvTableThead &&
this.bvTableTr &&
this.bvTable.stickyHeader
)
},
isStickyColumn() {
// Needed to handle header background classes, due to lack of
// background color inheritance with Bootstrap v4 table CSS
// Sticky column cells are only available in responsive
// mode (horizontal scrolling) or when sticky header mode
// Applies to cells in `thead`, `tbody` and `tfoot`
return (
(this.isResponsive || this.isStickyHeader) &&
this.stickyColumn &&
!this.isStacked &&
this.bvTable &&
this.bvTableTr
)
},
cellClasses() {
// We use computed props here for improved performance by caching
// the results of the string interpolation
let variant = this.variant
if (
(!variant && this.isStickyHeader && !this.bvTableThead.headVariant) ||
(!variant && this.isStickyColumn)
) {
// Needed for sticky-header mode as Bootstrap v4 table cells do
// not inherit parent's background-color. Boo!
variant = this.bvTableTr.variant || this.bvTable.tableVariant || 'b-table-default'
}
return [
variant ? `${this.isDark ? 'bg' : 'table'}-${variant}` : null,
this.isStickyColumn ? 'b-table-sticky-column' : null
]
},
computedColspan() {
return parseSpan(this.colspan)
},
computedRowspan() {
return parseSpan(this.rowspan)
},
cellAttrs() {
// We use computed props here for improved performance by caching
// the results of the object spread (Object.assign)
const headOrFoot = this.bvTableThead || this.bvTableTfoot
// Make sure col/rowspan's are > 0 or null
const colspan = this.computedColspan
const rowspan = this.computedRowspan
// Default role and scope
let role = 'cell'
let scope = null

// Compute role and scope
// We only add scopes with an explicit span of 1 or greater
if (headOrFoot) {
// Header or footer cells
role = 'columnheader'
scope = colspan > 0 ? 'colspan' : 'col'
} else if (this.tag === 'th') {
// th's in tbody
role = 'rowheader'
scope = rowspan > 0 ? 'rowgroup' : 'row'
}

return {
colspan: colspan,
rowspan: rowspan,
role: role,
scope: scope,
// Allow users to override role/scope plus add other attributes
...this.$attrs,
// Add in the stacked cell label data-attribute if in
// stacked mode (if a stacked heading label is provided)
'data-label':
this.isStackedCell && !isUndefinedOrNull(this.stackedHeading)
? toString(this.stackedHeading)
: null
}
}
},
render(h) {
const content = [this.normalizeSlot('default')]
return h(
this.tag,
{
class: this.cellClasses,
attrs: this.cellAttrs,
// Transfer any native listeners
on: this.$listeners
},
[this.isStackedCell ? h('div', [content]) : content]
)
}
})
Loading