[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    [\"env\", {\n      \"modules\": false,\n      \"targets\": {\n        \"browsers\": [\"> 1%\", \"last 2 versions\", \"not ie <= 8\"]\n      }\n    }]\n  ],\n  \"plugins\": [\"transform-runtime\"],\n  \"env\": {\n    \"test\": {\n      \"presets\": [\"env\"]\n    }\n  }\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true"
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  root: true,\n  parser: 'babel-eslint',\n  parserOptions: {\n    sourceType: 'module'\n  },\n  env: {\n    browser: true,\n  },\n  extends: 'standard',\n  'rules': {\n    'arrow-parens': 0\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules/\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\ncoverage/\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\n\nnode_js:\n  - \"6\"\n  - \"7\"\n  - \"8\"\n\nafter_script: \"cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js\"\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 Heavyy IvS\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Vue Intersect\n**A Vue component to add intersection-observer to a Vue component or HTML element.**\n\n[![npm version](https://badge.fury.io/js/vue-intersect.svg)](https://badge.fury.io/js/vue-intersect) [![Coverage Status](https://coveralls.io/repos/github/heavyy/vue-intersect/badge.svg)](https://coveralls.io/github/heavyy/vue-intersect) [![Build status](https://img.shields.io/travis/heavyy/vue-intersect.svg)](https://travis-ci.org/heavyy/vue-intersect)\n\n\n\n## Table of content\n\n* [Introduction](#introduction)\n* [Demo](#demo)\n* [Installation](#installation)\n* [Usage](#usage)\n* [Properties](#properties)\n* [Events](#events)\n* [Polyfill](#polyfill)\n\n## Introduction\n\nThe [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) is an amazing API which allows you to observe one or more HTMLElement for when it has entered or left the viewport.\n\nThis API has many use cases like, infinite-scroll, lazy-loading or animations when an element enters the viewport.\n\n\n\n## Demo\n\nWe've made a basic demo of how you might want to use **vue-intersect**. The code is available in the [gh-pages branch](https://github.com/heavyy/vue-intersect/tree/gh-pages) and the part where **vue-intersect** is used [can be found here](https://github.com/heavyy/vue-intersect/blob/gh-pages/src/components/InfiniteScrollItem.vue#L33).\n\n[Hackernews infinite scroll demo](https://heavyy.github.io/vue-intersect/)\n\n> Please keep in mind that the demo is not production code. Use it as an inspiration.\n\n\n\n## Installation\n\nSimply install using your favorite package manager 🔥\n\n> ⚠️ If you're using Vue 3 then install `vue-intersect@next`\n\n#### NPM\n\n```bash\nnpm install vue-intersect --save\n```\n\n#### Yarn\n```bash\nyarn add vue-intersect\n```\n\n\n\n## Usage\n\nThe package acts as an abstract component, much like what you may know from [keep-alive](https://vuejs.org/v2/api/#keep-alive) or [transition](https://vuejs.org/v2/api/#transition).\n\nThis means that it's basically a \"decorator\". A component which does not output any markup to the DOM, but adds the functionality under the hood 😱.\n\n#### .vue\n\n```html\n<template>\n  <intersect @enter=\"msg = 'Intersected'\" @leave=\"msg = 'Not intersected'\">\n    <div>{{ msg }}</div>\n  </intersect>\n</template>\n\n<script>\n  import Intersect from 'vue-intersect'\n\n  export default {\n    components: { Intersect },\n    data () {\n      return {\n        msg: 'I will change'\n      }\n    }\n  }\n</script>\n```\n\n\n\n## Properties\n\n| Property   | Type        | Default           | Required | Description                              |\n| ---------- | ----------- | ----------------- | -------- | ---------------------------------------- |\n| threshold  | Array       | [0, 0.2]          | *no*     | [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) |\n| root       | HTMLElement | null              | *no*     | [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) |\n| rootMargin | String      | *0px 0px 0px 0px* | *no*     | [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Intersection_observer_options) |\n\n\n\n## Events\n\n| Name       | Arguments                                | Description                              |\n| ---------- | ---------------------------------------- | ---------------------------------------- |\n| **change** | [*IntersectionObserverEntry*](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) | Event fired on any inte.                 |\n| **enter**  | [*IntersectionObserverEntry*](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) | Event fired when the element is intersected (visible on the screen) |\n| **leave**  | [*IntersectionObserverEntry*](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) | Event fired when the element is *not* intersected (not visible on the screen) |\n| **destroyed** | None | Fired when the underlying element is destroyed |\n\n\n\n> The **enter** and **leave** event is sugar, for an often performed operation. You still have to set the threshold to e.g. [0, 0.2] (default). If you leave out \"0\", it will never call the **leave** event.\n\n\n\nThe events is compliant with Vue's [event modifiers](https://vuejs.org/v2/guide/events.html#Event-Modifiers). This means that you could add `.once` to the events to make sure you only trigger your event handler once.\n\n\n\n## Polyfill\n\nThe IntersectionObserver API is not currently available in all browsers ([IE11, Safari and iOS Safari](http://caniuse.com/#feat=intersectionobserver)). If you intend to support these browsers, you'll need to add a poylfill to your bundle.\n\n[WICG IntersectionObserver Polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill) is highly recommended.\n"
  },
  {
    "path": "__mocks__/intersection-observer.js",
    "content": "export default class IntersectionObserver {\n  constructor (cb, options) {\n    this.cb = cb\n    this.options = options\n    this.observables = []\n  }\n\n  observe (el) {\n    this.observables.push(el)\n  }\n\n  disconnect () {\n    return true\n  }\n}\n"
  },
  {
    "path": "__tests__/index.js",
    "content": "import Vue from 'vue/dist/vue.js'\nimport IntersectionObserver from '../__mocks__/intersection-observer.js'\nimport Intersect from '../src'\n\n// Mock\nglobal.IntersectionObserver = IntersectionObserver\n\ntest('It should be a function', () => {\n  expect(typeof Intersect.mounted).toBe('function')\n})\n\ntest('It should create a instance of IntersectionObserver', async () => {\n  global.console.warn = jest.fn()\n  const vm = new Vue(Intersect).$mount()\n  expect(vm.observer).toBeInstanceOf(IntersectionObserver)\n\n  await vm.$nextTick\n\n  expect(global.console.warn).toHaveBeenCalledTimes(1)\n})\n\ntest('It should mount correctly and add the item to the observers list', async () => {\n  const mockedIntersect = Object.assign({}, Intersect)\n\n  const spy = {\n    mounted: jest.spyOn(mockedIntersect, 'mounted')\n  }\n\n  const vm = new Vue({\n    template: `<intersect><div ref=\"intersect\">this is my component</div></intersect>`,\n    components: {Intersect: mockedIntersect}\n  }).$mount()\n\n  await Vue.nextTick()\n\n  expect(vm._vnode.componentInstance.observer.observables.length).toBe(1)\n\n  expect(spy.mounted).toHaveBeenCalledTimes(1)\n\n  expect(vm.$el.outerHTML).toBe(`<div>this is my component</div>`)\n  expect(vm.$el.textContent).toBe('this is my component')\n})\n\ntest('It should emit \"enter\" event when the component is intersected', async () => {\n  const mockedIntersect = Object.assign({}, Intersect)\n  const spy = jest.fn()\n\n  const vm = new Vue({\n    template: `<intersect @enter=\"onEnter\"><div></div></intersect>`,\n    components: {Intersect: mockedIntersect},\n    methods: {\n      onEnter: spy\n    }\n  }).$mount()\n\n  await vm.$nextTick()\n\n  vm._vnode.componentInstance.observer.cb([{\n    isIntersecting: true\n  }])\n\n  expect(spy).toHaveBeenCalledTimes(1)\n})\n\ntest('It should emit \"leave\" event when the component is not intersected', async () => {\n  const mockedIntersect = Object.assign({}, Intersect)\n  const spy = jest.fn()\n\n  const vm = new Vue({\n    template: `<intersect @leave=\"onLeave\"><div></div></intersect>`,\n    components: {Intersect: mockedIntersect},\n    methods: {\n      onLeave: spy\n    }\n  }).$mount()\n\n  await vm.$nextTick()\n\n  vm._vnode.componentInstance.observer.cb([{\n    isIntersecting: false\n  }])\n\n  expect(spy).toHaveBeenCalledTimes(1)\n})\n\ntest('It should emit \"change\" on any intersection change', async () => {\n  const mockedIntersect = Object.assign({}, Intersect)\n  const spy = jest.fn()\n\n  const vm = new Vue({\n    template: `<intersect @change=\"onChange\"><div></div></intersect>`,\n    components: {Intersect: mockedIntersect},\n    methods: {\n      onChange: spy\n    }\n  }).$mount()\n\n  await vm.$nextTick()\n\n  vm._vnode.componentInstance.observer.cb([{\n    isIntersecting: false\n  }])\n\n  vm._vnode.componentInstance.observer.cb([{\n    isIntersecting: true\n  }])\n\n  expect(spy).toHaveBeenCalledTimes(2)\n})\n\ntest('It should be possible to set the threshold property', async () => {\n  const mockedIntersect = Object.assign({}, Intersect)\n  const vm = new Vue({\n    template: `<intersect :threshold=\"[0, 0.5]\"><div></div></intersect>`,\n    components: {Intersect: mockedIntersect}\n  }).$mount()\n\n  await vm.$nextTick()\n\n  expect(vm._vnode.componentInstance.$options.propsData.threshold).toEqual([0, 0.5])\n})\n\ntest('It should be possible to set the root property', async () => {\n  const mockedIntersect = Object.assign({}, Intersect)\n  const vm = new Vue({\n    template: `<intersect :root=\"viewPort\"><div></div></intersect>`,\n    components: {Intersect: mockedIntersect},\n    data () {\n      return {\n        viewPort: document.body\n      }\n    }\n  }).$mount()\n\n  await vm.$nextTick()\n  expect(vm._vnode.componentInstance.$options.propsData.root).toBeInstanceOf(HTMLElement)\n})\n\ntest('It should be possible to set the rootMargin property', async () => {\n  const mockedIntersect = Object.assign({}, Intersect)\n  const vm = new Vue({\n    template: `<intersect root-margin=\"1px 1px 1px 1px\"><div></div></intersect>`,\n    components: {Intersect: mockedIntersect}\n  }).$mount()\n\n  await vm.$nextTick()\n  expect(vm._vnode.componentInstance.$options.propsData.rootMargin).toBe('1px 1px 1px 1px')\n})\n\ntest('It should disconnect the IntersectionObserver when the component is destroyed', async () => {\n  const mockedIntersect = Object.assign({}, Intersect)\n\n  const spy = {\n    destroyed: jest.spyOn(mockedIntersect, 'destroyed'),\n    disconnect: jest.spyOn(global.IntersectionObserver.prototype, 'disconnect')\n  }\n\n  const vm = new Vue({\n    template: `<intersect><div></div></intersect>`,\n    components: {Intersect: mockedIntersect}\n  }).$mount()\n\n  await vm.$nextTick()\n\n  vm.$destroy()\n  expect(spy.destroyed).toHaveBeenCalledTimes(1)\n  expect(spy.disconnect).toHaveBeenCalledTimes(1)\n  spy.disconnect.mockClear()\n})\n\ntest('It should emit event when component is destroyed', async () => {\n  const mockedIntersect = Object.assign({}, Intersect)\n  const onDestroy = jest.fn()\n\n  const spy = {\n    destroyed: jest.spyOn(mockedIntersect, 'destroyed'),\n    disconnect: jest.spyOn(global.IntersectionObserver.prototype, 'disconnect')\n  }\n\n  const vm = new Vue({\n    template: `<intersect @destroyed=\"onDestroy\"><div></div></intersect>`,\n    components: {Intersect: mockedIntersect},\n    methods: {\n      onDestroy\n    }\n  }).$mount()\n\n  await vm.$nextTick()\n\n  vm.$destroy()\n\n  expect(spy.destroyed).toHaveBeenCalledTimes(1)\n  expect(spy.disconnect).toHaveBeenCalledTimes(1)\n  expect(onDestroy).toHaveBeenCalledTimes(1)\n})\n\ntest('It should warn when no child component is defined', async () => {\n  global.console.warn = jest.fn()\n\n  const vm = new Vue({\n    template: `<intersect></intersect>`,\n    components: {Intersect}\n  }).$mount()\n\n  await vm.$nextTick()\n\n  expect(global.console.warn).toHaveBeenCalledTimes(1)\n  expect(vm._vnode.componentInstance.observer.observables.length).toBe(0)\n\n  global.console.warn.mockReset()\n})\n\ntest('It should warn if more than one child component is defined', async () => {\n  global.console.warn = jest.fn()\n  const vm = new Vue({\n    template: `<intersect><div></div><div></div></intersect>`,\n    components: {Intersect}\n  }).$mount()\n\n  await vm.$nextTick()\n\n  expect(global.console.warn).toHaveBeenCalledTimes(1)\n  expect(vm._vnode.componentInstance.observer.observables.length).toBe(1)\n\n  global.console.warn.mockReset()\n})\n\ntest('It should not warn if Vue.config.silent is set to false', async () => {\n  require('vue').config.silent = true\n\n  global.console.warn = jest.fn()\n\n  const vm = new Vue({\n    template: `<intersect><div></div><div></div></intersect>`,\n    components: {Intersect}\n  }).$mount()\n\n  await vm.$nextTick()\n\n  expect(global.console.warn).toHaveBeenCalledTimes(0)\n  expect(vm._vnode.componentInstance.observer.observables.length).toBe(1)\n\n  global.console.warn.mockReset()\n})\n"
  },
  {
    "path": "dist/index.js",
    "content": "import Vue from 'vue';\n\nvar warn = function warn(msg) {\n  if (!Vue.config.silent) {\n    console.warn(msg);\n  }\n};\n\nexport default {\n  name: 'intersect',\n  abstract: true,\n  props: {\n    threshold: {\n      type: Array,\n      required: false,\n      default: function _default() {\n        return [0.2];\n      }\n    },\n    root: {\n      type: HTMLElement,\n      required: false,\n      default: function _default() {\n        return null;\n      }\n    },\n    rootMargin: {\n      type: String,\n      required: false,\n      default: function _default() {\n        return '0px 0px 0px 0px';\n      }\n    }\n  },\n  created: function created() {\n    var _this = this;\n\n    this.observer = new IntersectionObserver(function (entries) {\n      if (!entries[0].isIntersecting) {\n        _this.$emit('leave', [entries[0]]);\n      } else {\n        _this.$emit('enter', [entries[0]]);\n      }\n\n      _this.$emit('change', [entries[0]]);\n    }, {\n      threshold: this.threshold,\n      root: this.root,\n      rootMargin: this.rootMargin\n    });\n  },\n  mounted: function mounted() {\n    var _this2 = this;\n\n    this.$nextTick(function () {\n      if (_this2.$slots.default && _this2.$slots.default.length > 1) {\n        warn('[VueIntersect] You may only wrap one element in a <intersect> component.');\n      } else if (!_this2.$slots.default || _this2.$slots.default.length < 1) {\n        warn('[VueIntersect] You must have one child inside a <intersect> component.');\n        return;\n      }\n\n      _this2.observer.observe(_this2.$slots.default[0].elm);\n    });\n  },\n  destroyed: function destroyed() {\n    this.observer.disconnect();\n  },\n  render: function render() {\n    return this.$slots.default ? this.$slots.default[0] : null;\n  }\n};"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"vue-intersect\",\n  \"version\": \"1.1.5\",\n  \"description\": \"A Vue component to add intersection-observer to a Vue component or HTML element.\",\n  \"main\": \"dist/index.js\",\n  \"repository\": \"git@github.com:heavyy/vue-intersect.git\",\n  \"author\": \"Heavyy <hello@heavyy.io>\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"test\": \"./node_modules/.bin/jest --coverage --coverageDirectory coverage\",\n    \"lint\": \"./node_modules/.bin/eslint -c ./.eslintrc.js src/**/*.js\",\n    \"lint:fix\": \"./node_modules/.bin/eslint -c ./.eslintrc.js src/**/*.js --fix\",\n    \"build\": \"./node_modules/.bin/babel ./src -d ./dist/\"\n  },\n  \"peerDependencies\": {\n    \"vue\": \"^2.4.2\"\n  },\n  \"devDependencies\": {\n    \"vue\": \"^2.4.2\",\n    \"babel-cli\": \"^6.24.1\",\n    \"babel-core\": \"^6.25.0\",\n    \"babel-eslint\": \"^7.2.3\",\n    \"babel-plugin-transform-runtime\": \"^6.23.0\",\n    \"babel-preset-env\": \"^1.6.0\",\n    \"coveralls\": \"^2.13.1\",\n    \"eslint\": \"^4.4.1\",\n    \"eslint-config-standard\": \"^10.2.1\",\n    \"eslint-plugin-import\": \"^2.7.0\",\n    \"eslint-plugin-node\": \"^5.1.1\",\n    \"eslint-plugin-promise\": \"^3.5.0\",\n    \"eslint-plugin-standard\": \"^3.0.1\",\n    \"jest\": \"^20.0.4\"\n  }\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "import Vue from 'vue'\n\nconst warn = (msg) => {\n  if (!Vue.config.silent) {\n    console.warn(msg)\n  }\n}\n\nexport default {\n  name: 'intersect',\n  abstract: true,\n  props: {\n    threshold: {\n      type: Array,\n      required: false,\n      default: () => [0, 0.2]\n    },\n    root: {\n      type: typeof HTMLElement !== 'undefined' ? HTMLElement : Object,\n      required: false,\n      default: () => null\n    },\n    rootMargin: {\n      type: String,\n      required: false,\n      default: () => '0px 0px 0px 0px'\n    }\n  },\n  mounted () {\n    this.observer = new IntersectionObserver((entries) => {\n      if (!entries[0].isIntersecting) {\n        this.$emit('leave', [entries[0]])\n      } else {\n        this.$emit('enter', [entries[0]])\n      }\n\n      this.$emit('change', [entries[0]])\n    }, {\n      threshold: this.threshold,\n      root: this.root,\n      rootMargin: this.rootMargin\n    })\n\n    this.$nextTick(() => {\n      if (this.$slots.default && this.$slots.default.length > 1) {\n        warn('[VueIntersect] You may only wrap one element in a <intersect> component.')\n      } else if (!this.$slots.default || this.$slots.default.length < 1) {\n        warn('[VueIntersect] You must have one child inside a <intersect> component.')\n        return\n      }\n\n      this.observer.observe(this.$slots.default[0].elm)\n    })\n  },\n  destroyed () {\n    this.$emit('destroyed')\n    this.observer.disconnect()\n  },\n  render () {\n    return this.$slots.default ? this.$slots.default[0] : null\n  }\n}\n"
  }
]