svelte-ux
Version:
- Increment version in `package.json` and commit as `Version bump to x.y.z` - `npm run publish`
156 lines (155 loc) • 6.52 kB
JavaScript
import { merge } from 'lodash-es';
import { sticky } from './sticky';
import { getCellValue } from '../utils/table';
import DomTracker from './_domTracker';
export function tableCell(node, options) {
// Track changes so they can be reversed on an update
const tracker = new DomTracker(node);
function update(options) {
const { order, rowData, rowIndex } = options;
const column = merge({}, options.column, options.overrides);
// TODO: Should we keep a stingified copy of the resolved `column` config in `lastChanges` and exit early if no changes. Maybe just add it if performance dictates
// Reset state from last update
tracker.reset();
if (node.nodeName === 'TH') {
if (column.orderBy !== false) {
tracker.addClass('cursor-pointer');
if (order) {
tracker.addEventListener('click', () => order.onHeaderClick(column));
}
}
}
if (column.colSpan) {
tracker.addAttribute('colspan', column.colSpan.toString());
}
if (column.rowSpan) {
tracker.addAttribute('rowspan', column.rowSpan.toString());
}
if (column.align) {
// Explicit column alignment
switch (column.align) {
case 'left':
tracker.addClass('text-left');
break;
case 'center':
tracker.addClass('text-center');
break;
case 'right':
tracker.addClass('text-right');
break;
case 'justify':
tracker.addClass('text-justify');
break;
}
}
else if (typeof column.format === 'string') {
// Implicit column alignment based on format
switch (column.format) {
case 'currency':
case 'decimal':
case 'integer':
case 'percent':
tracker.addClass('text-right');
break;
}
}
else {
// Default column alignment
tracker.addClass('text-left');
}
const context = {
column,
rowData,
cellValue: rowData && getCellValue(column, rowData, rowIndex !== null && rowIndex !== void 0 ? rowIndex : -1),
};
if (column.class) {
if (node.nodeName === 'TH' && column.class.header) {
const classes = getClasses(column.class.header, context);
classes === null || classes === void 0 ? void 0 : classes.forEach((className) => tracker.addClass(className));
}
else if (node.nodeName === 'TD' && column.class.data) {
const classes = getClasses(column.class.data, context);
classes === null || classes === void 0 ? void 0 : classes.forEach((className) => tracker.addClass(className));
}
}
if (column.style) {
if (node.nodeName === 'TH' && column.style.header) {
const styleProperties = getStyleProperties(column.style.header, context);
styleProperties === null || styleProperties === void 0 ? void 0 : styleProperties.forEach(([property, value]) => {
tracker.addStyle(property, value);
});
}
else if (node.nodeName === 'TD' && column.style.data) {
const styleProperties = getStyleProperties(column.style.data, context);
styleProperties === null || styleProperties === void 0 ? void 0 : styleProperties.forEach(([property, value]) => {
tracker.addStyle(property, value);
});
}
}
if (column.sticky) {
if (node.nodeName === 'TH') {
// Ignore sticky bottom for header cell
tracker.addAction(sticky(node, { ...column.sticky, bottom: false }));
// Increase z-index for other sticky headers (scrolled left) as well as sticky cells below (scrolled up)
// Only need to increase z-index for first and last headers (and higher than sticky data cells below them)
if (column.sticky.left || column.sticky.right) {
tracker.addClass('z-20');
}
}
if (node.nodeName === 'TD') {
// Ignore sticky top for data cell, and only apply sticky bottom if last row
// Note: Rows are sometimes rendered one by one by Svelte (HierarchyTable) so best to set this explicitly at call site
// TODO: Once sticky/stickyContext actions supported offsetting bottom, this should be removed
const isLastRow = node.closest('table tr:last-child') === node.closest('tr');
tracker.addAction(sticky(node, {
...column.sticky,
top: false,
bottom: column.sticky.bottom && isLastRow,
}));
// Increase column z-index for sticky columns
if (column.sticky.left) {
tracker.addClass('z-10');
}
if (column.sticky.right) {
tracker.addClass('z-10');
}
}
}
}
function destroy() {
// Do we always need to reset if being unmounted?
tracker.reset();
}
update(options);
return {
update,
destroy,
};
}
function getClasses(classProp, context) {
const resolvedClassProp = typeof classProp === 'function' ? classProp(context) : classProp;
if (typeof resolvedClassProp === 'string') {
return resolvedClassProp
.split(' ')
.map((x) => x.trim())
.filter((x) => x !== '');
}
else {
return resolvedClassProp;
}
}
function getStyleProperties(styleProp, context) {
const resolvedStyleProp = typeof styleProp === 'function' ? styleProp(context) : styleProp;
if (typeof resolvedStyleProp === 'string') {
const styles = resolvedStyleProp
.split(';')
.map((x) => x.trim())
.filter((x) => x !== '');
return styles.map((style) => {
return style.split(':').map((x) => x.trim());
});
}
else {
return Object.entries(resolvedStyleProp);
}
}