<template>
  <div ref="paneRef" class="tjt-split-pane" :class="{ 'is-resizing': isResizing }">
    <div class="tjt-split-pane__panel tjt-split-pane__panel--left" :style="{ flexBasis: leftBasis }">
      <slot name="left"></slot>
    </div>

    <div
      aria-label="Resize panels"
      class="tjt-split-pane__resize"
      role="separator"
      tabindex="0"
      @keydown="handleKeydown"
      @pointerdown="startResize"
    >
      <span class="tjt-split-pane__resize-handle"></span>
    </div>

    <div class="tjt-split-pane__panel tjt-split-pane__panel--right">
      <slot name="right"></slot>
    </div>
  </div>
</template>

<script lang="ts" setup>
defineOptions({ name: 'TjtSplitPane' })

const props = withDefaults(
  defineProps<{
    defaultPercent?: number
    minPanelWidth?: number
  }>(),
  {
    defaultPercent: 50,
    minPanelWidth: 320
  }
)

const RESIZER_WIDTH = 16
const KEYBOARD_STEP = 2

const paneRef = ref<HTMLElement>()
const leftPercent = ref(props.defaultPercent)
const isResizing = ref(false)

const leftBasis = computed(() => `calc(${leftPercent.value}% - ${RESIZER_WIDTH / 2}px)`)

const getBounds = () => {
  const rect = paneRef.value?.getBoundingClientRect()
  if (!rect?.width) {
    return
  }
  const minPercent = Math.min(
    45,
    ((props.minPanelWidth + RESIZER_WIDTH / 2) / rect.width) * 100
  )
  return {
    rect,
    minPercent,
    maxPercent: 100 - minPercent
  }
}

const setLeftPercent = (percent: number) => {
  const bounds = getBounds()
  if (!bounds) {
    leftPercent.value = percent
    return
  }
  leftPercent.value = Math.min(bounds.maxPercent, Math.max(bounds.minPercent, percent))
}

const updateByPointer = (event: PointerEvent) => {
  const bounds = getBounds()
  if (!bounds) {
    return
  }
  setLeftPercent(((event.clientX - bounds.rect.left) / bounds.rect.width) * 100)
}

const stopResize = () => {
  if (!isResizing.value) {
    return
  }
  isResizing.value = false
  window.removeEventListener('pointermove', updateByPointer)
  window.removeEventListener('pointerup', stopResize)
  window.removeEventListener('pointercancel', stopResize)
  document.body.style.cursor = ''
  document.body.style.userSelect = ''
}

const startResize = (event: PointerEvent) => {
  isResizing.value = true
  event.preventDefault()
  updateByPointer(event)
  document.body.style.cursor = 'col-resize'
  document.body.style.userSelect = 'none'
  window.addEventListener('pointermove', updateByPointer)
  window.addEventListener('pointerup', stopResize)
  window.addEventListener('pointercancel', stopResize)
}

const handleKeydown = (event: KeyboardEvent) => {
  if (event.key === 'ArrowLeft') {
    event.preventDefault()
    setLeftPercent(leftPercent.value - KEYBOARD_STEP)
  }
  if (event.key === 'ArrowRight') {
    event.preventDefault()
    setLeftPercent(leftPercent.value + KEYBOARD_STEP)
  }
}

onBeforeUnmount(() => {
  stopResize()
})
</script>

<style scoped>
.tjt-split-pane {
  display: flex;
  align-items: stretch;
  width: 100%;
  min-width: 0;
}

.tjt-split-pane__panel {
  min-width: 0;
}

.tjt-split-pane__panel--left {
  flex: 0 0 50%;
}

.tjt-split-pane__panel--right {
  flex: 1 1 0;
}

.tjt-split-pane__resize {
  position: relative;
  display: flex;
  flex: 0 0 16px;
  align-items: center;
  justify-content: center;
  cursor: col-resize;
  outline: none;
  touch-action: none;
  user-select: none;
}

.tjt-split-pane__resize::before {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  content: '';
  background: var(--el-border-color);
}

.tjt-split-pane__resize-handle {
  position: sticky;
  top: calc(50vh - 21px);
  z-index: 1;
  width: 6px;
  height: 42px;
  border-radius: 4px;
  background: var(--el-border-color);
  transition:
    width 0.15s ease,
    background-color 0.15s ease;
}

.tjt-split-pane__resize:hover .tjt-split-pane__resize-handle,
.tjt-split-pane__resize:focus-visible .tjt-split-pane__resize-handle,
.tjt-split-pane.is-resizing .tjt-split-pane__resize-handle {
  width: 8px;
  background: var(--el-color-primary);
}

@media (max-width: 900px) {
  .tjt-split-pane {
    flex-direction: column;
  }

  .tjt-split-pane__panel--left,
  .tjt-split-pane__panel--right {
    flex: 0 0 auto;
  }

  .tjt-split-pane__resize {
    display: none;
  }
}
</style>
