import { useId, useLayoutEffect, useRef, useState } from 'react' import AnimatedCollapse from '@/components/common/AnimatedCollapse' import SortableList from '@/components/common/SortableList' import { countChildPaths, fieldsForCounts, normaliseCountsParams, orderedCountFields, selectedCountChildren, selectedCountColumns, selectedCountFields } from '@/lib/counts' import { useCountsDefinition } from '@/lib/countsDefinitions' import { useClientMotion } from '@/lib/useClientMotion' import { useUITranslation } from '@/lib/useUITranslation' import type { CountField, CountsResource } from '@/lib/counts' type BranchProps = { field: CountField; path: string; enabled: boolean selected: Set; params: URLSearchParams select: (path: string, checked: boolean) => void update: (key: string, value: string) => void } const FieldBranch = ({ field, path, enabled, selected, params, select, update }: BranchProps) => { const { label, message } = useUITranslation ('counts') const [open, setOpen] = useState (false) const id = useId () const motion = useClientMotion () const input = useRef (null) const leaves = countChildPaths (field.children ?? [], path, true) const count = leaves.filter (key => selected.has (key)).length const checked = field.children == null ? selected.has (path) : count > 0 && count === leaves.length const partial = count > 0 && count < leaves.length useLayoutEffect (() => { if (input.current != null) input.current.indeterminate = partial }, [partial]) return <>
{field.children != null && }
{field.children != null &&
} } const FieldGroup = ({ fields, path, params, enabled, selected, select, update }: { fields: CountField[]; path: string; params: URLSearchParams enabled: boolean; selected: Set; select: BranchProps['select'] update: BranchProps['update'] }, ) => { const { message } = useUITranslation ('counts') const ordered = orderedCountFields (fields, params, path) const prefix = path ? `${path}.` : '' return field.key) }} fixed={path === '' ? ['thumbnail'] : []} name={key => message (fields.find (field => field.key === key)!.label, fields.find (field => field.key === key)!.labelValues)} handleName={key => message ('reorder', { column: message (fields.find (field => field.key === key)!.label, fields.find (field => field.key === key)!.labelValues).text })} renderGroup={(_, content) => content} onChange={groups => { const current = fields.map (field => `${prefix}${field.key}`) const other = (params.get ('order') ?? '').split (' ') .filter (key => key && !current.includes (key)) update ('order', [...other, ...groups.items.map (key => `${prefix}${key}`)].join (' ')) }} renderItem={key => { const field = fields.find (field => field.key === key)! return }}/> } const CountsFieldTree = ( { resource, params, update, fields: suppliedFields, selectionKey = 'columns', parent = '' }: { resource: CountsResource fields?: CountField[] selectionKey?: string parent?: string params: URLSearchParams update: (key: string, value: string) => void }, ) => { const definition = useCountsDefinition () const resolved = normaliseCountsParams (resource, params, definition) const fields = suppliedFields ?? fieldsForCounts (resource, definition) const roots = suppliedFields == null ? selectedCountColumns (resource, resolved, definition) : selectedCountFields (fields, resolved, selectionKey, parent).map (field => field.key) const selected = selectedCountChildren (resolved, fieldsForCounts (resource, definition)) const rootPath = (key: string) => parent ? `${parent}.${key}` : key for (const root of roots) selected.add (rootPath (root)) const select = (path: string, checked: boolean) => { const next = new Set (selected) const paths = countChildPaths (fields, parent) .filter (key => key === path || key.startsWith (`${path}.`)) for (const key of paths) { if (checked) next.add (key) else next.delete (key) } const nextParams = new URLSearchParams (resolved) nextParams.set ('children', [...next].filter (key => key.includes ('.')).join (' ')) const children = selectedCountChildren (nextParams, fieldsForCounts (resource, definition)) update ('children', [...children].join (' ')) update (selectionKey, fields.filter (field => field.children == null ? next.has (rootPath (field.key)) : field.children.some (child => children.has (`${rootPath (field.key)}.${child.key}`))) .map (field => field.key).join (' ')) } const visibleFields = parent === '' ? fields.filter (field => field.key !== 'thumbnail') : fields return ( <> ) } export default CountsFieldTree