<template>
  <div :id="id" class="map-zoom">
    <svg :view-box.camel="viewBox">
      <g :transform="transform">
        <slot name="default" />
      </g>
    </svg>

    <button-zoom
      v-bind="{ minZoom, maxZoom, value: scale }"
      @zoom="onZoom"
    />
  </div>
</template>

<script>
import { select, zoom, zoomIdentity } from 'd3'


export default {
  name: 'MapZoom',

  props: {
    id: { type: String, required: true },
    minZoom: { type: Number, default: 0.3 },
    maxZoom: { type: Number, default: 1 },
    initialZoom: { type: Number, default: 1 },
    center: { type: Object, default: null }
  },

  data () {
    return {
      zoom: zoom().scaleExtent([this.minZoom, this.maxZoom]),
      svg: undefined, // d3 select(svg)
      width: undefined,
      height: undefined,
      scale: this.initialZoom,
      transform: zoomIdentity.translate(0, 0).scale(this.initialZoom)
    }
  },

  computed: {
    viewBox () {
      const { width, height } = this
      if (width === undefined) return
      return `-${width / 2} -${height / 2} ${width} ${height}`
    }
  },

  watch: {
    center (val, prevVal) {
      this.transform = this.transform.translate(prevVal.x - val.x, prevVal.y - val.y)
    }
  },

  methods: {
    updateSize () {
      const { width, height } = this.$el.getBoundingClientRect()
      Object.assign(this.$data, { width, height })
      this.$emit('input', { width, height })
    },

    onZoom (direction) {
      this.zoom.scaleBy(this.svg, direction === 1 ? 1.3 : 1 / 1.3, [0, 0])
    },

    reset () {
      this.transform = zoomIdentity.translate(0, 0).scale(this.initialZoom)
      this.svg.call(this.zoom.transform, this.transform)
    }
  },

  mounted () {
    window.addEventListener('resize', () => this.updateSize())

    this.zoom.on('zoom', ({ transform }) => {
      this.transform = this.center ? transform.translate(-this.center.x, -this.center.y) : transform
      this.scale = transform.k
    })

    this.updateSize()
    this.svg = select('#' + this.id + ' svg')
    this.svg.call(this.zoom).call(this.zoom.transform, this.transform)
  }
}
</script>

<style lang="scss" scoped>
.map-zoom {
  position: relative;
  width: 100%;
  height: 100%;
  cursor: grab;

  svg {
    width: 100%;
    height: 100%;
  }
}
</style>
