{"version":3,"file":"use-horizontal-event-resize.cjs","names":["parseTimeString","clampIntervalMinutes"],"sources":["../../src/hooks/use-horizontal-event-resize.ts"],"sourcesContent":["import dayjs from 'dayjs';\nimport { useCallback, useEffect, useEffectEvent, useRef, useState } from 'react';\nimport { DateTimeStringValue, ScheduleEventData, ScheduleMode } from '../types';\nimport { clampIntervalMinutes } from '../utils/clamp-interval-minutes/clamp-interval-minutes';\nimport { parseTimeString } from '../utils/parse-time-string/parse-time-string';\n\ntype ResizeEdge = 'start' | 'end';\n\ninterface ResizeState {\n  eventId: string | number;\n  event: ScheduleEventData;\n  edge: ResizeEdge;\n  container: HTMLElement;\n  originalLeft: number;\n  originalWidth: number;\n  currentLeft: number;\n  currentWidth: number;\n  eventDate: string;\n  originalStart: DateTimeStringValue;\n  originalEnd: DateTimeStringValue;\n}\n\nexport interface UseHorizontalEventResizeInput {\n  enabled?: boolean;\n  mode?: ScheduleMode;\n  startTime: string;\n  endTime: string;\n  intervalMinutes: number;\n  onEventResize?: (data: {\n    eventId: string | number;\n    newStart: DateTimeStringValue;\n    newEnd: DateTimeStringValue;\n    event: ScheduleEventData;\n  }) => void;\n  canResizeEvent?: (event: ScheduleEventData) => boolean;\n}\n\nexport function useHorizontalEventResize({\n  enabled = false,\n  mode = 'default',\n  startTime,\n  endTime,\n  intervalMinutes,\n  onEventResize,\n  canResizeEvent,\n}: UseHorizontalEventResizeInput) {\n  const [resizeState, setResizeState] = useState<ResizeState | null>(null);\n  const resizeRef = useRef<ResizeState | null>(null);\n  const justResizedRef = useRef(false);\n  const stableOnEventResize = useEffectEvent(onEventResize || (() => {}));\n\n  const parsedStartTime = parseTimeString(startTime);\n  const parsedEndTime = parseTimeString(endTime);\n  const startMinutes = parsedStartTime.hours * 60 + parsedStartTime.minutes;\n  const endMinutes = parsedEndTime.hours * 60 + parsedEndTime.minutes;\n  const clampedInterval = clampIntervalMinutes(intervalMinutes);\n  const literalRange = endMinutes - startMinutes;\n  const totalMinutes = Math.ceil(literalRange / clampedInterval) * clampedInterval;\n  const minWidthPercent = (clampedInterval / totalMinutes) * 100;\n\n  const clampAndSnap = useCallback(\n    (minutes: number): number => {\n      const snapped = Math.round(minutes / clampedInterval) * clampedInterval;\n      return Math.max(0, Math.min(literalRange, snapped));\n    },\n    [literalRange, clampedInterval]\n  );\n\n  const percentToDateTime = useCallback(\n    (percent: number, eventDate: string): DateTimeStringValue => {\n      const minutes = (percent / 100) * totalMinutes;\n      const snappedMinutes = clampAndSnap(minutes);\n      const totalMins = startMinutes + snappedMinutes;\n      const hours = Math.floor(totalMins / 60);\n      const mins = totalMins % 60;\n      return `${eventDate} ${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}:00`;\n    },\n    [totalMinutes, startMinutes, clampAndSnap]\n  );\n\n  const snapPercent = useCallback(\n    (percent: number): number => {\n      const minutes = (percent / 100) * totalMinutes;\n      const snappedMinutes = clampAndSnap(minutes);\n      return (snappedMinutes / totalMinutes) * 100;\n    },\n    [totalMinutes, clampAndSnap]\n  );\n\n  const handleResizeStart = useCallback(\n    ({\n      event,\n      edge,\n      container,\n      originalLeft,\n      originalWidth,\n      eventDate,\n      pointerEvent,\n    }: {\n      event: ScheduleEventData;\n      edge: ResizeEdge;\n      container: HTMLElement;\n      originalLeft: number;\n      originalWidth: number;\n      eventDate: string;\n      pointerEvent: React.PointerEvent;\n    }) => {\n      if (!enabled || mode === 'static') {\n        return;\n      }\n\n      pointerEvent.preventDefault();\n      pointerEvent.stopPropagation();\n\n      const state: ResizeState = {\n        eventId: event.id,\n        event,\n        edge,\n        container,\n        originalLeft,\n        originalWidth,\n        currentLeft: originalLeft,\n        currentWidth: originalWidth,\n        eventDate,\n        originalStart: dayjs(event.start).format('YYYY-MM-DD HH:mm:ss'),\n        originalEnd: dayjs(event.end).format('YYYY-MM-DD HH:mm:ss'),\n      };\n\n      resizeRef.current = state;\n      setResizeState(state);\n    },\n    [enabled, mode]\n  );\n\n  const isResizing = resizeState !== null;\n\n  useEffect(() => {\n    if (!isResizing) {\n      return undefined;\n    }\n\n    const savedUserSelect = document.body.style.userSelect;\n    const savedCursor = document.body.style.cursor;\n    document.body.style.userSelect = 'none';\n    document.body.style.cursor = 'ew-resize';\n\n    const handlePointerMove = (e: PointerEvent) => {\n      const state = resizeRef.current;\n      if (!state) {\n        return;\n      }\n\n      const containerRect = state.container.getBoundingClientRect();\n      const relativeX = e.clientX - containerRect.left;\n      const rawPercent = Math.max(0, Math.min(100, (relativeX / containerRect.width) * 100));\n      const snappedPercent = snapPercent(rawPercent);\n\n      let newLeft = state.originalLeft;\n      let newWidth = state.originalWidth;\n\n      if (state.edge === 'end') {\n        newWidth = Math.max(minWidthPercent, snappedPercent - state.originalLeft);\n      } else {\n        const originalRight = state.originalLeft + state.originalWidth;\n        newLeft = Math.min(snappedPercent, originalRight - minWidthPercent);\n        newWidth = originalRight - newLeft;\n      }\n\n      resizeRef.current = { ...state, currentLeft: newLeft, currentWidth: newWidth };\n      setResizeState(resizeRef.current);\n    };\n\n    const handlePointerUp = () => {\n      const state = resizeRef.current;\n      if (state) {\n        if (\n          state.currentLeft !== state.originalLeft ||\n          state.currentWidth !== state.originalWidth\n        ) {\n          let newStart: DateTimeStringValue;\n          let newEnd: DateTimeStringValue;\n\n          if (state.edge === 'start') {\n            newStart = percentToDateTime(state.currentLeft, state.eventDate);\n            newEnd = state.originalEnd;\n          } else {\n            newStart = state.originalStart;\n            newEnd = percentToDateTime(state.currentLeft + state.currentWidth, state.eventDate);\n          }\n\n          stableOnEventResize({\n            eventId: state.eventId,\n            newStart,\n            newEnd,\n            event: state.event,\n          });\n        }\n      }\n      resizeRef.current = null;\n      setResizeState(null);\n      justResizedRef.current = true;\n      requestAnimationFrame(() => {\n        justResizedRef.current = false;\n      });\n    };\n\n    document.addEventListener('pointermove', handlePointerMove);\n    document.addEventListener('pointerup', handlePointerUp);\n\n    return () => {\n      document.body.style.userSelect = savedUserSelect;\n      document.body.style.cursor = savedCursor;\n      document.removeEventListener('pointermove', handlePointerMove);\n      document.removeEventListener('pointerup', handlePointerUp);\n    };\n  }, [isResizing]);\n\n  const getResizePosition = useCallback(\n    (eventId: string | number) => {\n      if (!resizeState || resizeState.eventId !== eventId) {\n        return null;\n      }\n      return { left: resizeState.currentLeft, width: resizeState.currentWidth };\n    },\n    [resizeState]\n  );\n\n  const isResizableEvent = useCallback(\n    (event: ScheduleEventData) => {\n      if (!enabled || mode === 'static' || event.display === 'background') {\n        return false;\n      }\n      return canResizeEvent ? canResizeEvent(event) : true;\n    },\n    [enabled, mode, canResizeEvent]\n  );\n\n  const wasResizing = useCallback(() => justResizedRef.current, []);\n\n  return {\n    handleResizeStart,\n    isResizing,\n    resizingEventId: resizeState?.eventId ?? null,\n    resizingEdge: resizeState?.edge ?? null,\n    getResizePosition,\n    isResizableEvent,\n    wasResizing,\n  };\n}\n"],"mappings":"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"}