UNPKG

compote-ui

Version:

An opinionated UI component library for Svelte, built on top of [Ark UI](https://ark-ui.com) with additional components and features not available in the core Ark UI library.

192 lines (191 loc) 7.27 kB
import { cn } from 'tailwind-variants'; export function alignClass(align) { return align === 'right' ? 'text-right' : align === 'center' ? 'text-center' : 'text-left'; } export function justifyClass(align) { return align === 'right' ? 'justify-end' : align === 'center' ? 'justify-center' : 'justify-start'; } export function sortButtonDirectionClass(align) { return align === 'right' ? 'flex-row-reverse' : 'flex-row'; } export function columnSizeStyle(size) { return `width: ${size}px`; } export function selectionColumnSizeStyle() { return 'width: 40px'; } export function virtualColumnSizeStyle(size) { return `display: flex; flex: 0 0 ${size}px; width: ${size}px`; } export function virtualSelectionColumnSizeStyle() { return 'display: flex; flex: 0 0 40px; width: 40px'; } export function tableSizeStyle(table, isRowSelectionEnabled, state) { void state.columnSizing; void state.columnVisibility; return `width: max(100%, ${table.getTotalSize() + (isRowSelectionEnabled ? 40 : 0)}px)`; } export function virtualGrowColumnSizeStyle() { return 'display: flex; flex: 1; min-width: 0'; } export function virtualGroupWithGrowSizeStyle(fixedPortion) { return `display: flex; flex: 1 0 ${fixedPortion}px; width: ${fixedPortion}px`; } export function resizeHandleStyle(table, header, columnResizing) { if (table.options.columnResizeMode !== 'onEnd') return undefined; const deltaOffset = columnResizing.deltaOffset; if (!header.column.getIsResizing() || deltaOffset === null) return undefined; return `transform: translateX(${deltaOffset}px)`; } export function resizeHandleClass(headerIndex, headerCount) { return cn('absolute top-0 z-10 flex h-full w-2 cursor-col-resize touch-none items-center justify-center select-none before:h-4 before:w-px before:bg-border before:content-[""]', headerIndex === headerCount - 1 ? 'right-0' : '-right-1'); } export function getHeaderSortLabel(sortDirection) { if (sortDirection === 'asc') return 'Sorted ascending'; if (sortDirection === 'desc') return 'Sorted descending'; return 'Not sorted'; } export function getHeaderAriaSort(sortDirection) { if (sortDirection === 'asc') return 'ascending'; if (sortDirection === 'desc') return 'descending'; return 'none'; } export function getRowCells(row, state) { void state.columnVisibility; void state.columnPinning; void state.columnSizing; return [ ...row.getLeftVisibleCells(), ...row.getCenterVisibleCells(), ...row.getRightVisibleCells() ]; } export function getBooleanCellValue(value) { if (value === true) return true; if (value === false) return false; return undefined; } export function getPinningStyle(column, table, state, isHeader = false, isRowSelectionEnabled = false) { void state.columnPinning; void state.columnSizing; const isPinned = column.getIsPinned(); if (!isPinned) return undefined; const zIndex = isHeader ? 15 : 1; const selectionOffset = isRowSelectionEnabled ? 40 : 0; if (isPinned === 'left') { const left = column.getStart('left') + selectionOffset; const leftCols = table.getLeftLeafColumns(); const isLastLeft = leftCols[leftCols.length - 1]?.id === column.id; const shadow = !isHeader && isLastLeft ? 'box-shadow: -4px 0 4px -4px var(--compote-border) inset' : undefined; return ['position: sticky', `z-index: ${zIndex}`, `left: ${left}px`, shadow] .filter(Boolean) .join('; '); } else { const right = column.getAfter('right'); const rightCols = table.getRightLeafColumns(); const isFirstRight = rightCols[0]?.id === column.id; const shadow = !isHeader && isFirstRight ? 'box-shadow: 4px 0 4px -4px var(--compote-border) inset' : undefined; return ['position: sticky', `z-index: ${zIndex}`, `right: ${right}px`, shadow] .filter(Boolean) .join('; '); } } function collectLeafHeaders(header) { if (header.subHeaders.length === 0) return [header]; return header.subHeaders.flatMap((sub) => collectLeafHeaders(sub)); } /** * Sticky positioning for a group header whose leaf columns are pinned. * * A split fragment's subHeaders contain only its own section's children, so the * fragment over the pinned columns sticks at the first/last pinned leaf's offset * while the fragment over the scrolling columns gets no style. */ export function getGroupPinningStyle(header, section, state, isRowSelectionEnabled = false) { void state.columnPinning; void state.columnSizing; if (section !== 'left' && section !== 'right') return undefined; const leafHeaders = collectLeafHeaders(header); if (section === 'left') { const first = leafHeaders[0]; if (first?.column.getIsPinned() !== 'left') return undefined; const left = first.column.getStart('left') + (isRowSelectionEnabled ? 40 : 0); return `position: sticky; z-index: 15; left: ${left}px`; } const last = leafHeaders[leafHeaders.length - 1]; if (last?.column.getIsPinned() !== 'right') return undefined; return `position: sticky; z-index: 15; right: ${last.column.getAfter('right')}px`; } export function getUrlCellValue(value) { if (typeof value !== 'string' || value.trim() === '') return undefined; return value; } export function openUrlCell(value) { window.open(value, '_blank', 'noopener,noreferrer'); } export function getPhoneCellValue(value) { if (typeof value !== 'string' || value.trim() === '') return undefined; return value; } export function openPhoneCell(value) { window.location.href = `tel:${value}`; } // `columnDef.meta` is natively typed as DataTableColumnMeta via the `columnMeta` // type-only slot in features.ts; this accessor just narrows the columnDef shape. export function getColumnMeta(columnDef) { return columnDef.meta; } export function joinStyles(...styles) { return styles.filter(Boolean).join('; '); } // Shared with the cell formatter in create-table.svelte.ts — keep number-typed // columns and their footer sums formatted identically. export const TYPE_NUMBER_FORMAT_DEFAULTS = { currency: { style: 'currency', currency: 'USD' }, percent: { style: 'percent' }, number: {} }; export function formatColumnFooter(meta, values, locale) { if (meta.footer) { const result = meta.footer(values); if (result === null || result === undefined) return undefined; return String(result); } if (meta.sum) { const sum = values.reduce((acc, val) => acc + (typeof val === 'number' ? val : Number(val) || 0), 0); const numDefaults = meta.type ? TYPE_NUMBER_FORMAT_DEFAULTS[meta.type] : undefined; if (numDefaults !== undefined) { return new Intl.NumberFormat(meta.formatLocale ?? locale, { ...numDefaults, ...meta.formatOptions }).format(sum); } return String(sum); } return undefined; }