UNPKG

@limetech/lime-elements

Version:
226 lines (225 loc) • 7.29 kB
import { escape } from 'html-escaper'; import { pickBy, negate } from 'lodash-es'; export class ColumnDefinitionFactory { constructor(pool) { this.pool = pool; this.create = this.create.bind(this); } /** * Create Tabulator column definitions from a limel-table column configuration * * @param column - config describing the column * @returns Tabulator column */ create(column) { var _a; const definition = { title: column.title, field: column.field, hozAlign: column.horizontalAlign, headerSort: column.headerSort, headerSortStartingDir: 'desc', }; if (column.headerComponent) { definition.titleFormatter = formatHeader(column); } if (((_a = column.component) === null || _a === void 0 ? void 0 : _a.name) || column.formatter) { definition.formatter = createFormatter(column, this.pool); definition.formatterParams = column; } if (column.aggregator) { definition.bottomCalc = getColumnAggregator(column); } return definition; } } /** * Formats the header of the column * * @param column - the configuration for the column * @returns custom component that renders a column header */ export const formatHeader = (column) => () => { const headerElement = document.createElement('div'); headerElement.setAttribute('class', 'lime-col-title__custom-component'); const titleElement = document.createElement('span'); titleElement.setAttribute('class', 'title-component-text'); titleElement.textContent = column.title; const customElement = document.createElement(column.headerComponent.name); customElement.setAttribute('class', 'title-component-slot'); let props = column.headerComponent.props || {}; if (column.headerComponent.propsFactory) { props = Object.assign(Object.assign({}, props), column.headerComponent.propsFactory(null)); } setElementProperties(customElement, props); headerElement.append(titleElement); headerElement.append(customElement); return headerElement; }; /** * Create a formatter to be used to format values in a column * * @param column - config describing the column * @param pool - pool to get custom components from * @returns Tabulator formatter */ export function createFormatter(column, pool) { var _a; if (!((_a = column.component) === null || _a === void 0 ? void 0 : _a.name)) { return formatCell; } if (!columnElementExists(column)) { console.warn(`Failed to render custom component for column "${column.field.toString()}". Custom element <${column.component.name}/> does not exist. Using the default formatter.`); return formatCell; } return (cell) => { const value = formatCell(cell, column); return createCustomComponent(cell, column, value, pool); }; } function columnElementExists(column) { const name = column.component.name; if (typeof name === 'string') { const isNativeElement = !name.includes('-'); const customElementExists = customElements.get(column.component.name); return isNativeElement || customElementExists; } else { return false; } } /** * Format the value of a cell in the table * * @param cell - the cell being rendered in the table * @param column - configuration for the current column * @returns the formatted value */ export function formatCell(cell, column) { const data = cell.getData(); let value = cell.getValue(); if (column.formatter) { value = column.formatter(value, data); } if (typeof value === 'string' && !column.component) { value = escape(value); } return value; } /** * Create a custom component that renders a cell value * * @param cell - Tabulator cell * @param column - lime-elements column configuration * @param value - the value of the cell being rendered * @param pool - pool to get custom components from * @returns custom component that renders a value in the table */ export function createCustomComponent(cell, column, value, pool) { const field = cell.getField(); const data = cell.getData(); const element = pool.get(column.component.name); let props = column.component.props || {}; if (column.component.propsFactory) { props = Object.assign(Object.assign({}, props), column.component.propsFactory(data)); } props = Object.assign(Object.assign({}, props), { field: field, value: value, data: data }); element.style.display = 'inline-block'; setElementProperties(element, props); createResizeObserver(element, cell.getColumn()); return element; } /** * Set all properties for a custom element, including event listeners * * @param element - the custom element * @param props - object of properties and event listeners */ export function setElementProperties(element, props) { const properties = pickBy(props, negate(isEventListener)); Object.assign(element, properties); const listeners = pickBy(props, isEventListener); for (const [key, value] of Object.entries(listeners)) { const event = getEventName(key); element.addEventListener(event, value); } } /** * Check if a property is an event listener. * * An event listener has to be a function and its property name have to start * with "on" followed by the name of the event in camel case, e.g. "onEventName" * * @param value - the value to check * @param key - name of the property * @returns true if the property of the object is an event listener */ function isEventListener(value, key) { if (typeof value !== 'function') { return false; } return /^on[A-Z]/.test(key); } /** * Get the name of an event from the name of an event listener * * E.g. "onMyEvent" will return "myEvent" * * @param eventListener - name of the event listener * @returns the name of the event */ function getEventName(eventListener) { return eventListener.charAt(2).toLowerCase() + eventListener.slice(3); } function createResizeObserver(element, column) { if (!('ResizeObserver' in window)) { return; } const RESIZE_TIMEOUT = 1000; const COLUMN_PADDING = 16; const observer = new ResizeObserver(() => { const width = element.getBoundingClientRect().width; const newWidth = width + COLUMN_PADDING; if (newWidth < column.getWidth()) { return; } column.setWidth(newWidth); }); observer.observe(element); // We give the component some time to resize itself before we // stop listening for resize events setTimeout(() => { observer.unobserve(element); }, RESIZE_TIMEOUT); } /** * Create a column sorter from a tabulator sorter * * @param columns - all available columns in the table * @returns function that creates a sorter from a tabulator sorter */ export const createColumnSorter = (columns) => (sorter) => { const column = columns.find((col) => col.field === sorter.field); const direction = sorter.dir.toUpperCase(); return { column: column, direction: direction, }; }; /** * * @param column */ export function getColumnAggregator(column) { const aggregator = column.aggregator; if (isAggregatorFunction(aggregator)) { return (values, data) => { return aggregator(column, values, data); }; } return aggregator; } function isAggregatorFunction(value) { return typeof value === 'function'; } //# sourceMappingURL=columns.js.map