UNPKG

@zendesk/retrace

Version:

define and capture Product Operation Traces along with computed metrics with an optional friendly React beacon API

142 lines (123 loc) 3.5 kB
import { useCallback, useMemo, useState } from 'react' import type { HierarchicalSpanAndAnnotation } from '../types' interface UseSpanExpansionOptions { initialExpandedSpans?: Set<string> persistKey?: string } export function useSpanExpansion(options: UseSpanExpansionOptions = {}) { const { initialExpandedSpans = new Set(), persistKey } = options const [expandedSpans, setExpandedSpans] = useState<Set<string>>(() => { if (persistKey && typeof localStorage !== 'undefined') { try { const stored = localStorage.getItem(`span-expansion-${persistKey}`) if (stored) { const parsed = JSON.parse(stored) as string[] return new Set(parsed) } return initialExpandedSpans } catch { return initialExpandedSpans } } return initialExpandedSpans }) const toggleSpanExpansion = useCallback( (spanId: string) => { setExpandedSpans((prev) => { const next = new Set(prev) if (next.has(spanId)) { next.delete(spanId) } else { next.add(spanId) } if (persistKey && typeof localStorage !== 'undefined') { try { localStorage.setItem( `span-expansion-${persistKey}`, JSON.stringify([...next]), ) } catch { // Ignore storage errors } } return next }) }, [persistKey], ) const isSpanExpanded = useCallback( (spanId: string) => { return expandedSpans.has(spanId) }, [expandedSpans], ) const collapseAll = useCallback(() => { setExpandedSpans(new Set()) if (persistKey && typeof localStorage !== 'undefined') { try { localStorage.setItem(`span-expansion-${persistKey}`, JSON.stringify([])) } catch { // Ignore storage errors } } }, [persistKey]) const expandAll = useCallback( (spans: HierarchicalSpanAndAnnotation[]) => { const allSpanIds = new Set<string>() const collectSpanIds = (span: HierarchicalSpanAndAnnotation) => { if (span.children.length > 0) { allSpanIds.add(span.span.id) } span.children.forEach(collectSpanIds) } spans.forEach(collectSpanIds) setExpandedSpans(allSpanIds) if (persistKey && typeof localStorage !== 'undefined') { try { localStorage.setItem( `span-expansion-${persistKey}`, JSON.stringify([...allSpanIds]), ) } catch { // Ignore storage errors } } }, [persistKey], ) const getVisibleSpans = useCallback( ( spans: HierarchicalSpanAndAnnotation[], ): HierarchicalSpanAndAnnotation[] => { const visibleSpans: HierarchicalSpanAndAnnotation[] = [] const addVisibleSpans = (span: HierarchicalSpanAndAnnotation) => { visibleSpans.push(span) if (expandedSpans.has(span.span.id)) { span.children.forEach(addVisibleSpans) } } spans.forEach(addVisibleSpans) return visibleSpans }, [expandedSpans], ) const expansionState = useMemo( () => ({ expandedSpans, toggleSpanExpansion, isSpanExpanded, collapseAll, expandAll, getVisibleSpans, }), [ expandedSpans, toggleSpanExpansion, isSpanExpanded, collapseAll, expandAll, getVisibleSpans, ], ) return expansionState }