UNPKG

colletch

Version:

A collection of etch components

212 lines (167 loc) 6.27 kB
/** @babel */ /** @jsx etch.dom */ /* global WeakMap */ import etch from 'etch' import EtchComponent from './etch-component' import symbols from './symbols' import {clone, makeObjectIterable} from './utils' const defaultComparator = (left, right, sortAsc) => { if (typeof left === 'string' && typeof right === 'string') { if (sortAsc) { return left.localeCompare(right) } return right.localeCompare(left) } if (sortAsc) { return left > right ? -1 : (left < right ? 1 : 0) } return left > right ? 1 : (left < right ? -1 : 0) } export default class EtchTable extends EtchComponent { update (props, children) { super.update(props, children, {update: false}) this[symbols.self].properties.data = makeObjectIterable(this[symbols.self].properties.data) return etch.update(this) } [symbols.initialize] () { this[symbols.self].properties.data = makeObjectIterable(this[symbols.self].properties.data) } getCallersContext () { if (this[symbols.self].children.length) { return this[symbols.self].children[0].context } return null } *getColumnTemplates () { const context = this.getCallersContext() for (const child of this[symbols.self].children) { const template = clone(child) template.props = Object.assign(template.props || {}, {':context': context}) yield template } } onSort (index, comparator, resolver, dir) { const element = this.refs[index] const column = index.split('-')[1] let sortAsc = dir ? dir === 'asc' : element.classList.contains('etch-table-sort-asc') const map = new WeakMap() const values = [] if (!resolver) { resolver = (sortColumn) => { return sortColumn.dataset.value !== undefined ? sortColumn.dataset.value : sortColumn.innerText.toLowerCase() } } for (const row of this.refs['body'].childNodes) { const sortColumn = row.childNodes[column].firstChild const sortValue = resolver(sortColumn) // make unique by wrapping in an object const instance = { sortValue } values.push(instance) map.set(instance, row) } values.sort((a, b) => { return comparator(a.sortValue, b.sortValue, sortAsc) }) this[symbols.scheduleUpdate](() => { if (this.prevSort) { this.prevSort.classList.remove('etch-table-sort-asc') this.prevSort.classList.remove('etch-table-sort-desc') } if (sortAsc) { element.classList.add('etch-table-sort-desc') } else { element.classList.add('etch-table-sort-asc') } this.prevSort = element for (const sortValue of values) { this.refs['body'].appendChild(map.get(sortValue)) } }) } buildHeader () { let column = 0 const cells = [] for (const template of this.getColumnTemplates()) { const sortable = template.props.sortable const events = {} const ref = 'col-' + column++ let child if (template.children.length && template.children[0].text) { child = <span>{template.children[0]}</span> } else if (typeof template.props.value === 'function') { child = template.props.value() } else if (template.props.value) { child = <span>{template.props.value}</span> } else if (Array.isArray(template.props.field)) { child = <span>{template.props.field.join(',')}</span> } else { throw new Error('Unbale to determine value for etch table header.') } const classNames = ['etch-table-cell', template.props.className] if (sortable) { let comparator = defaultComparator if (sortable.comparator && typeof sortable.comparator === 'function') { comparator = sortable.comparator } let resolver = null if (sortable.resolver && typeof sortable.resolver === 'function') { resolver = sortable.resolver } classNames.push('etch-table-sort') events.click = this.onSort.bind(this, ref, comparator, resolver, null) if (sortable.initial) { this[symbols.scheduleUpdate](() => { this.onSort(ref, comparator, resolver, sortable.initial === 'asc' ? 'desc' : 'asc') }) } } cells.push( <th ref={ref} className={classNames.join(' ')} on={events}> {child} </th> ) } return cells } buildRows () { let count = 0 const rows = [] for (const rowData of this[symbols.self].properties.data) { const ref = 'row-' + count++ const cells = [] for (const template of this.getColumnTemplates()) { template.props[':fieldData'] = clone(rowData) cells.push(template) } rows.push( <tr ref={ref} className="etch-table-row"> {cells} </tr> ) } return rows } render () { const header = this.buildHeader() const rows = this.buildRows() return ( <table className={this[symbols.getClassName]('etch-table')}> <thead ref='header' className='etch-table-header'> <tr className="etch-table-row"> { header } </tr> </thead> <tbody ref='body' className='etch-table-body'> { rows } </tbody> </table> ) } }