{"version":3,"file":"controller.mjs","names":[],"sources":["../../../../src/base-ui/DraggablePanel/core/controller.ts"],"sourcesContent":["import { animate, type MotionValue, motionValue } from 'motion/react';\n\nimport type { Axis, Placement } from './axes';\nimport { AXES } from './axes';\nimport { isRtl, lockBody } from './dom';\nimport { timing } from './transition';\nimport { clamp, emitter } from './utils';\n\nconst KEY_STEP = 10;\nconst KEY_STEP_FAST = 50;\n\nconst SCREEN_DELTA: Record<string, number> = {\n  ArrowDown: 1,\n  ArrowLeft: -1,\n  ArrowRight: 1,\n  ArrowUp: -1,\n};\n\nexport interface PanelControllerOptions {\n  collapseThreshold?: number;\n  defaultSize: number;\n  expand: boolean;\n  max?: number;\n  min: number;\n  onExpandChange?: (expand: boolean) => void;\n  onSizeChange?: (size: number, delta: number) => void;\n  onSizeDragging?: (size: number, delta: number) => void;\n  placement: Placement;\n  size: number;\n}\n\nexport interface PanelState {\n  collapsing: boolean;\n  dragging: boolean;\n  folding: boolean;\n}\n\nexport interface PanelKeyEvent {\n  key: string;\n  preventDefault: () => void;\n  shiftKey: boolean;\n}\n\nexport interface PanelController {\n  attach: (element: HTMLElement) => () => void;\n  axis: Axis;\n  bounds: () => { max: number; min: number };\n  drag: {\n    cancel: () => void;\n    end: () => void;\n    move: (offset: { x: number; y: number }) => void;\n    start: () => void;\n  };\n  motion: { content: MotionValue<number>; size: MotionValue<number> };\n  options: PanelControllerOptions;\n  reset: () => void;\n  resizeByKey: (event: PanelKeyEvent) => void;\n  state: PanelState;\n  subscribe: (listener: () => void) => () => void;\n  sync: (options: PanelControllerOptions) => void;\n  target: number;\n}\n\nexport const createPanelController = (initial: PanelControllerOptions): PanelController => {\n  const { emit: notify, subscribe } = emitter();\n\n  let element: HTMLElement | null = null;\n  let options = initial;\n  let target = initial.expand ? initial.size : 0;\n  let state: PanelState = { collapsing: false, dragging: false, folding: false };\n  let unlock: (() => void) | undefined;\n  let foldTo = target;\n\n  const size = motionValue(target);\n  const content = motionValue(initial.size);\n\n  const session = {\n    collapsing: false,\n    max: 0,\n    min: 0,\n    sign: 1,\n    start: 0,\n  };\n\n  const patch = (next: Partial<PanelState>) => {\n    const entries = Object.entries(next) as [keyof PanelState, boolean][];\n    if (entries.some(([key, value]) => state[key] !== value)) {\n      state = { ...state, ...next };\n      notify();\n    }\n  };\n\n  const bounds = () => {\n    const min = Math.max(0, options.min);\n    const max = options.max === undefined ? Number.POSITIVE_INFINITY : Math.max(min, options.max);\n    return { max, min };\n  };\n\n  const growSign = () => {\n    const axis = AXES[options.placement];\n    return axis.grow * (axis.vertical || !isRtl(element) ? 1 : -1);\n  };\n\n  const fold = (to: number) => {\n    foldTo = to;\n    patch({ folding: true });\n    if (size.get() === 0) content.jump(to || content.get());\n    else if (to > 0) animate(content, to, timing());\n    animate(size, to, timing());\n  };\n\n  const release = () => {\n    unlock?.();\n    unlock = undefined;\n  };\n\n  const stopDragging = () => {\n    release();\n    patch({ collapsing: false, dragging: false });\n  };\n\n  const drag = {\n    cancel: () => {\n      if (!state.dragging) return;\n      size.jump(session.start);\n      content.jump(session.start);\n      stopDragging();\n    },\n    end: () => {\n      if (!state.dragging) return;\n      const { collapsing, start } = session;\n      const committed = collapsing ? start : size.get();\n      stopDragging();\n      if (collapsing) {\n        content.jump(start);\n        options.onExpandChange?.(false);\n      }\n      options.onSizeChange?.(committed, committed - start);\n    },\n    move: (offset: { x: number; y: number }) => {\n      if (!state.dragging) return;\n      const axis = AXES[options.placement];\n      const pixels = session.start + offset[axis.point] * session.sign;\n      const collapsing =\n        options.collapseThreshold !== undefined && pixels < options.collapseThreshold;\n      const next = collapsing ? 0 : Math.round(clamp(pixels, session.min, session.max));\n\n      size.jump(next);\n      if (!collapsing) content.jump(next);\n      if (collapsing !== session.collapsing) {\n        session.collapsing = collapsing;\n        patch({ collapsing });\n      }\n      const reported = collapsing ? session.start : next;\n      options.onSizeDragging?.(reported, reported - session.start);\n    },\n    start: () => {\n      Object.assign(session, {\n        ...bounds(),\n        collapsing: false,\n        sign: growSign(),\n        start: size.get(),\n      });\n      release();\n      unlock = lockBody(AXES[options.placement].cursor, drag.cancel);\n      patch({ dragging: true, folding: false });\n    },\n  };\n\n  const resizeByKey = (event: PanelKeyEvent) => {\n    if (event.key === 'Enter' || event.key === ' ') {\n      if (!options.onExpandChange) return;\n      event.preventDefault();\n      options.onExpandChange(!options.expand);\n      return;\n    }\n\n    const { max, min } = bounds();\n    const fast = event.shiftKey || event.key === 'PageUp' || event.key === 'PageDown';\n    const step = (fast ? KEY_STEP_FAST : KEY_STEP) * growSign();\n    const axis = AXES[options.placement];\n    const onAxis = axis.vertical\n      ? event.key === 'ArrowUp' || event.key === 'ArrowDown'\n      : event.key === 'ArrowLeft' || event.key === 'ArrowRight';\n    const screen = onAxis ? SCREEN_DELTA[event.key] : undefined;\n    const moves: Record<string, number> = {\n      End: Number.isFinite(max) ? max : target,\n      Home: min,\n      PageDown: target + step,\n      PageUp: target - step,\n    };\n    const next = screen === undefined ? moves[event.key] : target + screen * step;\n    if (next === undefined) return;\n\n    event.preventDefault();\n    const value = clamp(next, min, max);\n    if (!options.expand && value > 0) options.onExpandChange?.(true);\n    options.onSizeChange?.(value, value - target);\n  };\n\n  const sync = (next: PanelControllerOptions) => {\n    options = next;\n    const value = next.expand ? next.size : 0;\n    if (size.get() === 0 && value > 0) content.jump(next.size);\n    if (value !== target) {\n      target = value;\n      notify();\n    }\n    if (state.dragging) return;\n    if (size.get() === target) {\n      // A reversal can land back on the target before the outgoing animation\n      // has moved; without this it keeps running past the new target.\n      size.stop();\n      content.stop();\n      if (state.folding) {\n        patch({ folding: false });\n        content.jump(target || content.get());\n      }\n      // A settled panel still needs its content aligned after a controlled reset.\n      foldTo = target;\n    } else if (!state.folding || foldTo !== target) {\n      fold(target);\n    }\n  };\n\n  return {\n    attach: (node) => {\n      element = node;\n      const stopSettle = size.on('animationComplete', () => {\n        if (size.get() === target) patch({ folding: false });\n      });\n      return () => {\n        stopSettle();\n        release();\n        element = null;\n      };\n    },\n    get axis() {\n      return AXES[options.placement];\n    },\n    bounds,\n    drag,\n    motion: { content, size },\n    get options() {\n      return options;\n    },\n    reset: () => {\n      if (!options.expand) options.onExpandChange?.(true);\n      options.onSizeChange?.(options.defaultSize, options.defaultSize - target);\n    },\n    resizeByKey,\n    get state() {\n      return state;\n    },\n    subscribe,\n    sync,\n    get target() {\n      return target;\n    },\n  };\n};\n"],"mappings":"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"}