{"version":3,"file":"TrackHeader.mjs","names":[],"sources":["../../../src/components/tracks/TrackHeader.tsx"],"sourcesContent":["import React from 'react';\nimport {\n  defaultTimelineInteractionGeometry,\n  type TimelineTrackGeometryOptions,\n} from '@techsquidtv/canvas-timeline-core';\nimport { clamp } from '@techsquidtv/canvas-timeline-utils';\nimport {\n  useTimelineTrack,\n  useTimelineTrackHeader,\n  useTimelineScrollTop,\n  type UseTimelineTrackHeaderResult,\n} from '#react/hooks';\n\n/** Props for the track header list column. */\nexport interface TrackHeaderListProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Height of the timeline ruler area above track rows. */\n  rulerHeight?: number;\n}\n\nexport const TrackHeaderList = React.forwardRef<HTMLDivElement, TrackHeaderListProps>(\n  (\n    {\n      children,\n      className = '',\n      rulerHeight = defaultTimelineInteractionGeometry.rulerHeight,\n      style,\n      ...props\n    },\n    ref\n  ) => {\n    const scrollTop = useTimelineScrollTop();\n    const listStyle: React.CSSProperties & {\n      '--track-header-ruler-height': string;\n    } = {\n      '--track-header-ruler-height': `${rulerHeight}px`,\n      paddingTop: `${rulerHeight}px`,\n      ...style,\n    };\n\n    return (\n      <div\n        ref={ref}\n        className={['timeline-track-header-list', className].filter(Boolean).join(' ')}\n        aria-label=\"Timeline track headers\"\n        role=\"group\"\n        style={listStyle}\n        {...props}\n      >\n        <div\n          className=\"timeline-track-header-list-content\"\n          style={{ transform: `translateY(${-scrollTop}px)` }}\n        >\n          {children}\n        </div>\n      </div>\n    );\n  }\n);\n\nTrackHeaderList.displayName = 'Timeline.TrackHeaderList';\n\n/** Render prop accepted by `Timeline.TrackHeader`. */\nexport type TrackHeaderChildren =\n  | React.ReactNode\n  | ((header: UseTimelineTrackHeaderResult) => React.ReactNode);\n\n/** Props for one timeline track header row. */\nexport interface TrackHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n  /** Id of the track row represented by this header. */\n  trackId: string;\n  /** Track geometry overrides matching the renderer. */\n  geometry?: TimelineTrackGeometryOptions;\n  /** Custom content or render prop receiving the headless header state. */\n  children?: TrackHeaderChildren;\n}\n\nexport const TrackHeader = React.forwardRef<HTMLDivElement, TrackHeaderProps>(\n  ({ trackId, geometry, children, className = '', style, ...props }, ref) => {\n    const header = useTimelineTrackHeader(trackId, geometry);\n    const { style: headerStyle, ...rootProps } = header.rootProps;\n    const content = typeof children === 'function' ? children(header) : children;\n\n    return (\n      <div\n        {...rootProps}\n        {...props}\n        ref={ref}\n        className={['timeline-track-header', className].filter(Boolean).join(' ')}\n        style={{ ...headerStyle, ...style }}\n      >\n        {content}\n      </div>\n    );\n  }\n);\n\nTrackHeader.displayName = 'Timeline.TrackHeader';\n\n/** Props for the track header resize handle. */\nexport interface TrackHeaderResizeHandleProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Id of the track row resized by this handle. */\n  trackId: string;\n  /** Track geometry overrides matching the renderer. */\n  geometry?: TimelineTrackGeometryOptions;\n  /** Minimum expanded track height in pixels. */\n  minHeight?: number;\n  /** Maximum expanded track height in pixels. */\n  maxHeight?: number;\n}\n\nexport const TrackHeaderResizeHandle = React.forwardRef<\n  HTMLDivElement,\n  TrackHeaderResizeHandleProps\n>(\n  (\n    {\n      trackId,\n      geometry,\n      className = '',\n      minHeight = defaultTimelineInteractionGeometry.collapsedTrackHeight,\n      maxHeight,\n      onKeyDown,\n      onPointerCancel,\n      onPointerDown,\n      onPointerMove,\n      onPointerUp,\n      ...props\n    },\n    ref\n  ) => {\n    const trackState = useTimelineTrack(trackId, geometry);\n    const currentHeight = trackState.height;\n    const resizeState = React.useRef<{\n      pointerId: number;\n      startHeight: number;\n      startY: number;\n    } | null>(null);\n\n    const finishResize = React.useCallback((event: React.PointerEvent<HTMLDivElement>) => {\n      const activeResize = resizeState.current;\n      if (activeResize === null || activeResize.pointerId !== event.pointerId) {\n        return;\n      }\n\n      try {\n        event.currentTarget.releasePointerCapture(event.pointerId);\n      } catch {\n        // Pointer capture may already be released by the browser.\n      }\n      resizeState.current = null;\n    }, []);\n\n    const handlePointerDown = React.useCallback(\n      (event: React.PointerEvent<HTMLDivElement>) => {\n        onPointerDown?.(event);\n        if (event.defaultPrevented) {\n          return;\n        }\n        if (event.pointerType !== 'touch' && event.button !== 0) {\n          return;\n        }\n\n        event.stopPropagation();\n        event.currentTarget.setPointerCapture(event.pointerId);\n        resizeState.current = {\n          pointerId: event.pointerId,\n          startHeight: trackState.collapsed\n            ? (trackState.track?.height ?? defaultTimelineInteractionGeometry.trackHeight)\n            : trackState.height,\n          startY: event.clientY,\n        };\n      },\n      [onPointerDown, trackState.collapsed, trackState.height, trackState.track]\n    );\n\n    const handlePointerMove = React.useCallback(\n      (event: React.PointerEvent<HTMLDivElement>) => {\n        onPointerMove?.(event);\n        const activeResize = resizeState.current;\n        if (\n          event.defaultPrevented ||\n          activeResize === null ||\n          activeResize.pointerId !== event.pointerId\n        ) {\n          return;\n        }\n\n        const rawHeight = activeResize.startHeight + event.clientY - activeResize.startY;\n        const boundedHeight =\n          maxHeight === undefined ? Math.max(minHeight, rawHeight) : Math.min(maxHeight, rawHeight);\n        trackState.setTrackHeight(Math.max(minHeight, boundedHeight));\n      },\n      [maxHeight, minHeight, onPointerMove, trackState]\n    );\n\n    const handleKeyDown = React.useCallback(\n      (event: React.KeyboardEvent<HTMLDivElement>) => {\n        onKeyDown?.(event);\n        if (event.defaultPrevented) {\n          return;\n        }\n\n        const maxTrackHeight = maxHeight ?? Number.POSITIVE_INFINITY;\n        const setHeight = (height: number) => {\n          trackState.setTrackHeight(clamp(height, minHeight, maxTrackHeight));\n        };\n\n        switch (event.key) {\n          case 'ArrowUp':\n          case 'ArrowLeft':\n            event.preventDefault();\n            setHeight(currentHeight - 8);\n            break;\n          case 'ArrowDown':\n          case 'ArrowRight':\n            event.preventDefault();\n            setHeight(currentHeight + 8);\n            break;\n          case 'PageUp':\n            event.preventDefault();\n            setHeight(currentHeight - 24);\n            break;\n          case 'PageDown':\n            event.preventDefault();\n            setHeight(currentHeight + 24);\n            break;\n          case 'Home':\n            event.preventDefault();\n            setHeight(minHeight);\n            break;\n          case 'End':\n            if (maxHeight !== undefined) {\n              event.preventDefault();\n              setHeight(maxHeight);\n            }\n            break;\n          default:\n            break;\n        }\n      },\n      [currentHeight, maxHeight, minHeight, onKeyDown, trackState]\n    );\n\n    const handlePointerUp = React.useCallback(\n      (event: React.PointerEvent<HTMLDivElement>) => {\n        onPointerUp?.(event);\n        finishResize(event);\n      },\n      [finishResize, onPointerUp]\n    );\n\n    const handlePointerCancel = React.useCallback(\n      (event: React.PointerEvent<HTMLDivElement>) => {\n        onPointerCancel?.(event);\n        finishResize(event);\n      },\n      [finishResize, onPointerCancel]\n    );\n\n    return (\n      <div\n        ref={ref}\n        aria-label=\"Resize track\"\n        aria-orientation=\"horizontal\"\n        aria-valuemin={minHeight}\n        aria-valuemax={maxHeight}\n        aria-valuenow={currentHeight}\n        aria-valuetext={`${currentHeight} pixels`}\n        className={['timeline-track-header-resize-handle', className].filter(Boolean).join(' ')}\n        data-track-id={trackId}\n        onKeyDown={handleKeyDown}\n        onPointerCancel={handlePointerCancel}\n        onPointerDown={handlePointerDown}\n        onPointerMove={handlePointerMove}\n        onPointerUp={handlePointerUp}\n        role=\"separator\"\n        tabIndex={0}\n        {...props}\n      />\n    );\n  }\n);\n\nTrackHeaderResizeHandle.displayName = 'Timeline.TrackHeaderResizeHandle';\n"],"mappings":"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"}