import { useContext, useEffect, useRef } from 'react' import SortableItem from '@/components/common/SortableItem' import CountsResizeHandle from '@/components/counts/CountsResizeHandle' import { Button } from '@/components/ui/button' import { cn } from '@/lib/utils' import { useUITranslation } from '@/lib/useUITranslation' import { CountsTableScope, useCountsTableLayout } from '@/lib/countsTableLayout' import type { CountSort } from '@/lib/counts' const CountsSortHeader = ( { field, label, sorts, change, structured = false, rowSpan, colSpan, resizable = true, dragId, totalKind, disabled = false }: { field: string label: { text: string; language: string } sorts: CountSort[] change: (key: string, additive: boolean) => void structured?: boolean rowSpan?: number; colSpan?: number; resizable?: boolean dragId?: string; totalKind?: string; disabled?: boolean }, ) => { const { message } = useUITranslation ('counts') const family = useContext (CountsTableScope) const layout = useCountsTableLayout () const heading = useRef (null) useEffect (() => { if (!resizable || colSpan != null && colSpan > 1) return const node = heading.current const text = node?.querySelector ('[data-counts-label]') const button = text?.closest ('button') if (node == null || text == null || button == null) return const canvas = document.createElement ('canvas') const context = canvas.getContext ('2d') if (context == null) return const measure = () => { context.font = getComputedStyle (text).font const padding = (element: Element) => { const style = getComputedStyle (element) return parseFloat (style.paddingLeft) + parseFloat (style.paddingRight) + parseFloat (style.borderLeftWidth) + parseFloat (style.borderRightWidth) } const handles = Array.from (node.children).filter (child => child !== button) .reduce ((sum, child) => sum + Math.max ( child.scrollWidth, child.getBoundingClientRect ().width), 0) const resize = node.parentElement?.querySelector ('[data-counts-resize]') const resizeWidth = resize?.getBoundingClientRect ().width ?? 0 const indicator = button.querySelector ('[data-counts-sort-indicator]') const sortWidth = indicator?.getBoundingClientRect ().width ?? 0 const gap = parseFloat (getComputedStyle (button).columnGap) || 0 layout.fit (family, field, context.measureText (label.text).width + handles + padding (button) + padding (node.parentElement!) + sortWidth + gap + resizeWidth) } measure () let active = true void document.fonts.ready.then (() => { if (active) measure () }) return () => { active = false } }, [family, field, label.text, dragId, resizable, colSpan, layout]) const press = useRef<{ x: number; y: number timer: ReturnType } | null> (null) const suppressClick = useRef (false) const cancel = () => { if (press.current != null) clearTimeout (press.current.timer) press.current = null } useEffect (() => () => { if (press.current != null) clearTimeout (press.current.timer) }, []) const priority = sorts.findIndex (sort => sort.key === field) const direction = sorts[priority]?.direction const state = priority < 0 ? 'none' : (direction === 'asc' ? 'ascending' : 'descending') const title = message (structured ? 'sort.structured' : 'sort.action') const aria = message (priority < 0 ? 'sort.unsortedLabel' : (sorts.length === 1 ? 'sort.singleLabel' : 'sort.label'), { column: label.text, priority: priority + 1, direction: message (`sort.${state}`).text }) const render = (shell?: Parameters[0]['renderShell']>>[0]) => ( 1 && priority >= 0) ? 'other' : state}>
{shell?.handle}
{resizable && } ) return dragId == null ? render () : { }} renderShell={render}>{null} } export default CountsSortHeader