[
  {
    "path": ".browserslistrc",
    "content": "current node\nlast 2 versions and > 2%\nie > 10\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nGive a brief description of the pull request. \n-->\n"
  },
  {
    "path": ".github/auto_assign.yml",
    "content": "addAssignees: author\naddReviewers: true\n\nreviewers:\n  - fuxingloh\n\nnumberOfReviewers: 0\n"
  },
  {
    "path": ".github/pr-labeler.yml",
    "content": "feature: [ 'feature/*', 'feat/*', 'enhancement/*' ]\nfix: [ 'fix/*', 'bug/*', 'bugfix/*' ]\nchore: [ 'chore/*', 'docs/*' , 'doc/*', 'ci/*' ]\n"
  },
  {
    "path": ".github/release-drafter.yml",
    "content": "name-template: 'v$RESOLVED_VERSION 🌈'\ntag-template: 'v$RESOLVED_VERSION'\ncategories:\n  - title: '🚀 Features'\n    labels:\n      - 'feature'\n      - 'enhancement'\n  - title: '🐛 Bug Fixes'\n    labels:\n      - 'fix'\n      - 'bugfix'\n      - 'bug'\n  - title: '🧰 Maintenance'\n    labels:\n      - 'chore'\n      - 'docs'\nchange-template: '- $TITLE @$AUTHOR (#$NUMBER)'\nchange-title-escapes: '\\<*_&'\nversion-resolver:\n  minor:\n    labels:\n      - 'feature'\n      - 'enhancement'\n  patch:\n    labels:\n      - 'patch'\n      - 'bug'\n      - 'bugfix'\n      - 'fix'\n  default: patch\ntemplate: |\n  ## Changes\n\n  $CHANGES\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  pull_request:\n    branches: [ master ]\n\njobs:\n  yarn:\n    name: Build\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n\n      - uses: actions/setup-node@v1\n        with:\n          node-version: '14'\n          registry-url: 'https://registry.npmjs.org'\n\n      - run: yarn install --frozen-lockfile\n      - run: yarn build\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\n\non:\n  release:\n    types: [ published ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v1\n        with:\n          node-version: '14'\n          registry-url: 'https://registry.npmjs.org'\n\n      - run: yarn install --frozen-lockfile\n\n      - name: Check package version\n        uses: technote-space/package-version-check-action@v1\n        with:\n          COMMIT_MESSAGE: 'release: update package version'\n\n      - run: yarn build\n\n      - run: yarn publish --non-interactive\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/release-drafter.yml",
    "content": "name: Release Drafter\n\non:\n  push:\n    branches: [ master ]\n\n\njobs:\n  draft-release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: release-drafter/release-drafter@v5\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/triage.yml",
    "content": "name: \"Triage\"\n\non:\n  pull_request:\n    types: [ opened, reopened, ready_for_review ]\n    branches: [ master ]\n\njobs:\n  triage:\n    name: Triage\n    runs-on: ubuntu-latest\n    steps:\n      - uses: TimonVS/pr-labeler-action@v3\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - uses: kentaro-m/auto-assign-action@v1.1.2\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules\n/dist\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": ".npmignore",
    "content": "*\n!dist/*\n!src/vue-masonry-wall.vue\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Fuxing Loh\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-masonry-wall\nA pure vue responsive masonry implementation without direct dom manipulation, ssr friendly with lazy appending. \nI created this because other libraries has no SSR support, and I needed a pure vue implementation. \n\n* [Live Demo: Image](https://nuxt-app.now.sh/vue-masonry-wall-image) and the [Source](https://github.com/fuxingloh/nuxt-app/blob/0725466dcf2d3d5338573ca7612f38ecd8fa2fa0/components/examples/ExampleMasonryImage.vue)\n* [Live Demo: Text](https://nuxt-app.now.sh/vue-masonry-wall) and the [Source](https://github.com/fuxingloh/nuxt-app/blob/0725466dcf2d3d5338573ca7612f38ecd8fa2fa0/components/examples/ExampleMasonry.vue)\n\n[![vue-masonry-wall screenshot](example.png)](https://nuxt-app.now.sh/vue-masonry-wall-image)\n\n## Installation\n```shell script\nnpm i vue-masonry-wall\n# or yarn\nyarn add vue-masonry-wall\n```\n\n## Features \n* No Direct DOM Manipulation\n* SSR friendly, able to load and re hydrate later\n* 1 dependency only, no legacy dependencies\n* Auto lazy appending, scroll to auto load more\n* Auto item placement, will find best column \n* Responsive design\n\n## Usage\n```vue\n<vue-masonry-wall :items=\"items\" :options=\"options\" @append=\"append\"/>\n```\n\n```js\nconst items = []\nconst options = {\n  width: 300,\n  padding: {\n    default: 12,\n    1: 6,\n    2: 8\n  }\n}\n\nconst append = () => {\n  // API call and add items\n  this.items.push(...[])\n}\n```\n\n### Basic\n```vue\n<template>\n  <div id=\"app\">\n    <h2>Masonry: append endlessly</h2>\n\n    <vue-masonry-wall :items=\"items\" :options=\"{width: 300, padding: 12}\" @append=\"append\">\n      <template v-slot:default=\"{item}\">\n        <div class=\"item\">\n          <h5>{{item.title}}</h5>\n          <p>{{item.content}}</p>\n        </div>\n      </template>\n    </vue-masonry-wall>\n  </div>\n</template>\n\n<script>\n  import VueMasonryWall from \"vue-masonry-wall\";\n\n  export default {\n    name: 'app',\n    components: {VueMasonryWall},\n    data() {\n      return {\n        items: [\n          {title: 'Item 0', content: 'Content'},\n          {title: 'Item 1', content: 'Content'},\n        ]\n      }\n    },\n    methods: {\n      /**\n       * I am mocking a API call that load 20 objects at a time.\n       */\n      append() {\n        for (let i = 0; i < 20; i++) {\n          this.items.push({title: `Item ${this.items.length}`, content: 'Content'})\n        }\n      }\n    }\n  }\n</script>\n```\n### Nuxt SSR\nAdd `:ssr=\"{columns: 2}\"` to masonry so that during SSR, it will be load in 2 columns.\n\nSSR has no clue what is the size of your height of your element or width of the browser.\nYou can however guess based on user-agent: https://github.com/nuxt-community/device-module\nThis param allow you to preload a config for SSR rendering, it will distribute your items into all columns evenly.\n\n```vue\n<vue-masonry-wall :items=\"items\" :options=\"{width: 300, padding: 12}\" :ssr=\"{columns: 2}\" @append=\"append\">\n  <template v-slot:default=\"{item}\">\n    <div class=\"item\">\n      <h5>{{item.title}}</h5>\n      <p>{{item.content}}</p>\n    </div>\n  </template>\n</vue-masonry-wall>\n```\n\n## Dependencies\n- [vue-observe-visibility](https://github.com/Akryum/vue-observe-visibility) for [IntersectionObserver](https://github.com/w3c/IntersectionObserver/tree/master/polyfill)\n\n## Contributing\nFor any question or feature request please feel free to create an [issue](https://github.com/fuxingloh/vue-masonry-wall/issues/new) or [pull request](https://github.com/fuxingloh/vue-masonry-wall/pulls).\n\n## TODO\n> These were features from my original project that I removed for brevity of this package. \n\n* [ ] [nuxt-community/device-module](https://github.com/nuxt-community/device-module) to detect the browser for better SSR support.\n* [ ] [vue-scrollto](https://www.npmjs.com/package/vue-scrollto) to scroll to an item in masonry.\n\n## [Vue Horizontal](https://github.com/fuxingloh/vue-horizontal)\n\nI also maintain another project called [Vue Horizontal](https://github.com/fuxingloh/vue-horizontal).\n\nAt its core, Vue Horizontal is an ultra simple pure vue horizontal layout for\nmodern responsive web with zero dependencies.\nVue Horizontal is also an ultra complex code snippet dossier with over 100 SPA/SSR/SSG friendly recipes for your design needs.\nDo check it out, might be useful for you!\n"
  },
  {
    "path": "babel.config.js",
    "content": "const devPresets = ['@vue/babel-preset-app'];\nconst buildPresets = ['@babel/preset-env'];\nmodule.exports = {\n  presets: (process.env.NODE_ENV === 'development' ? devPresets : buildPresets),\n};\n"
  },
  {
    "path": "build/rollup.config.js",
    "content": "// rollup.config.js\nimport fs from 'fs';\nimport path from 'path';\nimport vue from 'rollup-plugin-vue';\nimport alias from '@rollup/plugin-alias';\nimport commonjs from '@rollup/plugin-commonjs';\nimport replace from '@rollup/plugin-replace';\nimport babel from 'rollup-plugin-babel';\nimport { terser } from 'rollup-plugin-terser';\nimport minimist from 'minimist';\n\n// Get browserslist config and remove ie from es build targets\nconst esbrowserslist = fs.readFileSync('./.browserslistrc')\n  .toString()\n  .split('\\n')\n  .filter((entry) => entry && entry.substring(0, 2) !== 'ie');\n\nconst argv = minimist(process.argv.slice(2));\n\nconst projectRoot = path.resolve(__dirname, '..');\n\nconst baseConfig = {\n  input: 'src/entry.js',\n  plugins: {\n    preVue: [\n      alias({\n        resolve: ['.js', '.jsx', '.ts', '.tsx', '.vue'],\n        entries: {\n          '@': path.resolve(projectRoot, 'src'),\n        },\n      }),\n    ],\n    replace: {\n      'process.env.NODE_ENV': JSON.stringify('production'),\n      'process.env.ES_BUILD': JSON.stringify('false'),\n    },\n    vue: {\n      css: true,\n      template: {\n        isProduction: true,\n      },\n    },\n    babel: {\n      exclude: 'node_modules/**',\n      extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'],\n    },\n  },\n};\n\n// ESM/UMD/IIFE shared settings: externals\n// Refer to https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency\nconst external = [\n  // list external dependencies, exactly the way it is written in the import statement.\n  // eg. 'jquery'\n  'vue',\n];\n\n// UMD/IIFE shared settings: output.globals\n// Refer to https://rollupjs.org/guide/en#output-globals for details\nconst globals = {\n  // Provide global variable names to replace your external imports\n  // eg. jquery: '$'\n  vue: 'Vue',\n};\n\n// Customize configs for individual targets\nconst buildFormats = [];\nif (!argv.format || argv.format === 'es') {\n  const esConfig = {\n    ...baseConfig,\n    external,\n    output: {\n      file: 'dist/vue-masonry-wall.esm.js',\n      format: 'esm',\n      exports: 'named',\n    },\n    plugins: [\n      replace({\n        ...baseConfig.plugins.replace,\n        'process.env.ES_BUILD': JSON.stringify('true'),\n      }),\n      ...baseConfig.plugins.preVue,\n      vue(baseConfig.plugins.vue),\n      babel({\n        ...baseConfig.plugins.babel,\n        presets: [\n          [\n            '@babel/preset-env',\n            {\n              targets: esbrowserslist,\n            },\n          ],\n        ],\n      }),\n      commonjs(),\n    ],\n  };\n  buildFormats.push(esConfig);\n}\n\nif (!argv.format || argv.format === 'cjs') {\n  const umdConfig = {\n    ...baseConfig,\n    external,\n    output: {\n      compact: true,\n      file: 'dist/vue-masonry-wall.ssr.js',\n      format: 'cjs',\n      name: 'VueMasonryWall',\n      exports: 'named',\n      globals,\n    },\n    plugins: [\n      replace(baseConfig.plugins.replace),\n      ...baseConfig.plugins.preVue,\n      vue({\n        ...baseConfig.plugins.vue,\n        template: {\n          ...baseConfig.plugins.vue.template,\n          optimizeSSR: true,\n        },\n      }),\n      babel(baseConfig.plugins.babel),\n      commonjs(),\n    ],\n  };\n  buildFormats.push(umdConfig);\n}\n\nif (!argv.format || argv.format === 'iife') {\n  const unpkgConfig = {\n    ...baseConfig,\n    external,\n    output: {\n      compact: true,\n      file: 'dist/vue-masonry-wall.min.js',\n      format: 'iife',\n      name: 'VueMasonryWall',\n      exports: 'named',\n      globals,\n    },\n    plugins: [\n      replace(baseConfig.plugins.replace),\n      ...baseConfig.plugins.preVue,\n      vue(baseConfig.plugins.vue),\n      babel(baseConfig.plugins.babel),\n      commonjs(),\n      terser({\n        output: {\n          ecma: 5,\n        },\n      }),\n    ],\n  };\n  buildFormats.push(unpkgConfig);\n}\n\n// Export config\nexport default buildFormats;\n"
  },
  {
    "path": "examples/index.vue",
    "content": "<template>\n  <div id=\"app\">\n    <h2>Masonry: append 100</h2>\n\n    <vue-masonry-wall :items=\"items\" :options=\"options\" @append=\"append\">\n      <template v-slot:default=\"{item}\">\n        <div class=\"item\">\n          <h5>{{ item.title }}</h5>\n          <p>{{ item.content }}</p>\n        </div>\n      </template>\n    </vue-masonry-wall>\n  </div>\n</template>\n\n<script>\nimport Vue from 'vue';\nimport VueMasonryWall from '@/vue-masonry-wall.vue';\n\nfunction content() {\n  const length = Math.random() * 300 + 30\n\n  let result = '';\n  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n  const charactersLength = characters.length;\n  for (let i = 0; i < length; i++) {\n    result += characters.charAt(Math.floor(Math.random() * charactersLength));\n  }\n  return result;\n}\n\nexport default Vue.extend({\n  name: 'ServeDev',\n  components: {\n    VueMasonryWall\n  },\n  data() {\n    return {\n      options: {\n        width: 300,\n        padding: {\n          default: 12,\n          2: 8,\n          3: 8,\n        }\n      },\n      items: [\n        {title: 'Item 0', content: content()},\n        {title: 'Item 1', content: content()},\n        {title: 'Item 2', content: content()},\n        {title: 'Item 3', content: content()},\n      ]\n    }\n  },\n  methods: {\n    /**\n     * I am mocking a API call that load 20 objects at a time.\n     */\n    append() {\n      if (this.items.length > 100) {\n        return\n      }\n\n      for (let i = 0; i < 20; i++) {\n        this.items.push({title: `Item ${this.items.length}`, content: content()})\n      }\n    }\n  }\n});\n</script>\n\n\n<style>\n#app {\n  font-family: 'Roboto', sans-serif;\n\n  max-width: 1400px;\n\n  margin-left: auto;\n  margin-right: auto;\n\n  padding: 80px 24px;\n}\n\n@media (min-width: 1200px) {\n  #app {\n    padding-left: 80px;\n    padding-right: 80px;\n  }\n}\n\nh2 {\n  font-size: 30px;\n  margin: 0 0 24px;\n}\n\nh5 {\n  font-size: 18px;\n  line-height: 1.5;\n  margin: 0 0 8px;\n}\n\np {\n  font-size: 17px;\n  line-height: 1.5;\n  font-weight: 400;\n  margin: 0;\n\n  word-break: break-word;\n}\n\n.item {\n  padding: 16px 24px;\n  border-radius: 3px;\n  background: #F5F5F5;\n}\n</style>\n"
  },
  {
    "path": "examples/package.json",
    "content": "{\n  \"name\": \"abc\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n\n  \"main\": \"dist/abc.ssr.js\",\n  \"browser\": \"dist/abc.esm.js\",\n  \"module\": \"dist/abc.esm.js\",\n  \"unpkg\": \"dist/abc.min.js\",\n\n  \"files\": [\n    \"dist/*\",\n    \"src/**/*.vue\"\n  ],\n\n  \"scripts\": {\n    \"serve\": \"vue-cli-service serve dev/serve.js\",\n    \"build\": \"cross-env NODE_ENV=production rollup --config build/rollup.config.js\",\n    \"build:ssr\": \"cross-env NODE_ENV=production rollup --config build/rollup.config.js --format cjs\",\n    \"build:es\": \"cross-env NODE_ENV=production rollup --config build/rollup.config.js --format es\",\n    \"build:unpkg\": \"cross-env NODE_ENV=production rollup --config build/rollup.config.js --format iife\"\n  },\n\n  \"dependencies\": {\n  },\n\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.9.0\",\n    \"@babel/preset-env\": \"^7.9.5\",\n    \"@rollup/plugin-alias\": \"^2.2.0\",\n    \"@rollup/plugin-commonjs\": \"^11.1.0\",\n    \"@rollup/plugin-replace\": \"^2.3.2\",\n    \"@vue/cli-plugin-babel\": \"^4.3.1\",\n    \"@vue/cli-service\": \"^4.3.1\",\n    \"cross-env\": \"^7.0.2\",\n    \"minimist\": \"^1.2.5\",\n    \"rollup\": \"^2.7.3\",\n    \"rollup-plugin-babel\": \"^4.4.0\",\n    \"rollup-plugin-terser\": \"^5.3.0\",\n    \"rollup-plugin-vue\": \"^5.1.6\",\n    \"vue\": \"^2.6.11\",\n    \"vue-template-compiler\": \"^2.6.11\"\n  },\n  \"peerDependencies\": {\n    \"vue\": \"^2.6.11\"\n  },\n  \"engines\": {\n    \"node\": \">=10\"\n  }\n}\n"
  },
  {
    "path": "examples/serve.js",
    "content": "import Vue from \"vue\";\nimport Index from \"./index.vue\";\n\nVue.config.productionTip = false;\n\nnew Vue({\n  render: (h) => h(Index),\n}).$mount(\"#app\");\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"vue-masonry-wall\",\n  \"version\": \"0.2.5\",\n  \"description\": \"100% vue masonry without direct dom manipulation, ssr friendly.\",\n  \"author\": \"Fuxing Loh\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/fuxingloh/vue-masonry-wall.git\"\n  },\n  \"bugs\": \"https://github.com/fuxingloh/vue-masonry-wall/issues\",\n  \"keywords\": [\n    \"vue\",\n    \"vuejs\",\n    \"masonry\",\n    \"pinterest\"\n  ],\n  \"license\": \"MIT\",\n  \"private\": false,\n  \"main\": \"dist/vue-masonry-wall.ssr.js\",\n  \"browser\": \"dist/vue-masonry-wall.esm.js\",\n  \"module\": \"dist/vue-masonry-wall.esm.js\",\n  \"unpkg\": \"dist/vue-masonry-wall.min.js\",\n  \"files\": [\n    \"dist/*\",\n    \"src/**/*.vue\"\n  ],\n  \"scripts\": {\n    \"build\": \"cross-env NODE_ENV=production rollup --config build/rollup.config.js\",\n    \"build:ssr\": \"cross-env NODE_ENV=production rollup --config build/rollup.config.js --format cjs\",\n    \"build:es\": \"cross-env NODE_ENV=production rollup --config build/rollup.config.js --format es\",\n    \"build:unpkg\": \"cross-env NODE_ENV=production rollup --config build/rollup.config.js --format iife\",\n    \"examples:serve\": \"vue-cli-service serve examples/serve.js\",\n    \"examples:build\": \"vue-cli-service build examples/serve.js\",\n    \"prepublishOnly\": \"yarn build\"\n  },\n  \"dependencies\": {\n    \"vue-observe-visibility\": \"^0.4.6\"\n  },\n  \"peerDependencies\": {\n    \"lodash\": \"^4.17.15\",\n    \"vue\": \"^2.6.10\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.9.0\",\n    \"@babel/preset-env\": \"^7.9.5\",\n    \"@rollup/plugin-alias\": \"^2.2.0\",\n    \"@rollup/plugin-commonjs\": \"^11.1.0\",\n    \"@rollup/plugin-replace\": \"^2.3.2\",\n    \"@vue/cli-plugin-babel\": \"^4.3.1\",\n    \"@vue/cli-service\": \"^4.3.1\",\n    \"cross-env\": \"^7.0.2\",\n    \"minimist\": \"^1.2.5\",\n    \"rollup\": \"^2.7.3\",\n    \"rollup-plugin-babel\": \"^4.4.0\",\n    \"rollup-plugin-terser\": \"^5.3.0\",\n    \"rollup-plugin-vue\": \"^5.1.6\",\n    \"vue\": \"^2.6.11\",\n    \"vue-template-compiler\": \"^2.6.11\"\n  },\n  \"engines\": {\n    \"node\": \">=10\"\n  }\n}\n"
  },
  {
    "path": "src/entry.js",
    "content": "// Import vue component\nimport component from '@/vue-masonry-wall.vue';\n\n// install function executed by Vue.use()\nconst install = function installVueMasonryWall(Vue) {\n  if (install.installed) return;\n  install.installed = true;\n  Vue.component('VueMasonryWall', component);\n};\n\n// Create module definition for Vue.use()\nconst plugin = {\n  install,\n};\n\n// To auto-install on non-es builds, when vue is found\n// eslint-disable-next-line no-redeclare\n/* global window, global */\nif (\"false\" === process.env.ES_BUILD) {\n  let GlobalVue = null;\n  if (typeof window !== \"undefined\") {\n    GlobalVue = window.Vue;\n  } else if (typeof global !== \"undefined\") {\n    GlobalVue = global.Vue;\n  }\n  if (GlobalVue) {\n    GlobalVue.use(plugin);\n  }\n}\n\n// Inject install function into component - allows component\n// to be registered via Vue.use() as well as Vue.component()\ncomponent.install = install;\n\n// Export component by default\nexport default component;\n\n// It's possible to expose named exports when writing components that can\n// also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo';\n// export const RollupDemoDirective = component;\n"
  },
  {
    "path": "src/vue-masonry-wall.vue",
    "content": "<template>\n  <div class=\"masonry-wall\" ref=\"wall\" :style=\"_style.wall\" :class=\"{ready}\">\n    <div class=\"masonry-column\" v-for=\"(lane, index) in columns\" :key=\"index\" :style=\"_style.lane\">\n\n      <div class=\"masonry-item\" v-for=\"i in lane.indexes\" :key=\"i\" :style=\"_style.item\" :ref=\"`item_${i}`\">\n        <slot v-bind:item=\"items[i]\" :index=\"i\">{{items[i]}}</slot>\n      </div>\n\n      <div class=\"masonry-bottom\" ref=\"bottom\" :data-column=\"index\"\n           v-observe-visibility=\"{callback: (v) => v && _fill(),throttle:_options.throttle}\"\n      />\n    </div>\n  </div>\n</template>\n\n<script>\n  import {maxBy} from 'lodash'\n  import {ObserveVisibility} from \"vue-observe-visibility\";\n\n  /**\n   * @param count number of columns to create\n   * @returns {[{i: number, indexes: []},...]}\n   */\n  const _newColumns = (count) => {\n    const columns = []\n    for (let i = 0; i < count; i++) {\n      columns.push({i: i, indexes: []})\n    }\n    return columns\n  }\n\n  export default {\n    name: \"VueMasonryWall\",\n    directives: {\n      'observe-visibility': ObserveVisibility\n    },\n    props: {\n\n      /**\n       * Array of items to add into masonry\n       */\n      items: {\n        type: Array,\n        required: true\n      },\n\n      /**\n       * Options to config masonry.\n       *\n       * {\n       *     width: 300,\n       *     padding: {\n       *         default: 12,\n       *         1: 6,\n       *         2: 8,\n       *     },\n       *     throttle: 300\n       * }\n       */\n      options: {\n        type: Object,\n        required: false\n      },\n\n      /**\n       * SSR has no clue what is the size of your height of your element or width of the browser.\n       * You can however guess based on user-agent: https://github.com/nuxt-community/device-module\n       * This param allow you to preload a config for SSR rendering, it will distribute your items into all columns evenly.\n       *\n       * Once the client is mounted, it will redraw if the config is different from SSR.\n       *\n       * {\n       *     column: 2\n       * }\n       */\n      ssr: {\n        type: Object,\n        required: false\n      }\n    },\n    data() {\n      const count = this.ssr && this.ssr.columns\n      if (count > 0) {\n        const columns = _newColumns(count)\n\n        for (let i = 0; i < this.items.length; i++) {\n          columns[i % count].indexes.push(i)\n        }\n\n        return {\n          columns: columns,\n          cursor: this.items.length,\n          ready: false\n        }\n      }\n\n      return {\n        columns: [],\n        cursor: 0,\n        ready: false\n      }\n    },\n    /**\n     * For detecting browser resize event to redraw the columns.\n     */\n    mounted() {\n      this.$resize = () => {\n        if (this.columns.length !== this._columnSize()) {\n          this.redraw()\n        }\n      }\n\n      this.$resize()\n    \n      // set opacity to 1 when ssr.columns is recieved and  this.columns.length === this._columnSize() so redraw does not get called for the first time.\n      if(!this.ready) this.ready = true;\n      \n      window.addEventListener('resize', this.$resize)\n    },\n    /**\n     * Remove resize event listener\n     */\n    beforeDestroy() {\n      window.removeEventListener('resize', this.$resize)\n    },\n    computed: {\n\n      /**\n       * Options with default override if not given\n       *\n       * @private\n       */\n      _options() {\n        const options = this.options\n        return {\n          width: options && options.width || 300,\n          padding: {\n            default: options && options.padding && options.padding.default || 12\n          },\n          throttle: options && options.throttle || 300\n        }\n      },\n\n      /**\n       * Style of wall, column and item for padding\n       * @private\n       */\n      _style() {\n        let padding = this.options && this.options.padding\n        if (padding && typeof padding != 'number') {\n          padding = this.options.padding[this.columns.length] || this._options.padding.default\n        }\n\n        return {\n          wall: {\n            margin: `-${padding}px`\n          },\n          lane: {\n            paddingLeft: `${padding}px`,\n            paddingRight: `${padding}px`,\n          },\n          item: {\n            paddingTop: `${padding}px`,\n            paddingBottom: `${padding}px`,\n          }\n        }\n      },\n    },\n    methods: {\n      /**\n       * Redraw masonry\n       */\n      redraw() {\n        this.ready = false\n        this.columns.splice(0)\n        this.cursor = 0\n        this.columns.push(..._newColumns(this._columnSize()))\n        this.ready = true\n        this._fill()\n      },\n\n      /**\n       *\n       * @returns {number}\n       * @private internal component use\n       */\n      _columnSize() {\n        const length = Math.round(this.$refs.wall.scrollWidth / this._options.width)\n        if (length < 1) return 1\n        return length\n      },\n\n      /**\n       * Add items into masonry columns, items are added to the shortest column first.\n       *\n       * @private internal component use\n       */\n      _fill() {\n        if (!this.ready) return\n\n        if (this.cursor >= this.items.length) {\n          // Request for more items\n          this.$emit('append')\n          return\n        }\n\n        // Keep filling until no more items\n        this.$nextTick(() => {\n          const bottom = maxBy(this.$refs.bottom, (spacer) => spacer.clientHeight || 0)\n          this._addItem(bottom.dataset.column)\n          this._fill()\n        })\n      },\n\n      /**\n       * Items will automatically be taken from items with cursor.\n       *\n       * @param index of the column to add to\n       * @private internal component use\n       */\n      _addItem(index) {\n        const column = this.columns[index]\n        if (this.items[this.cursor]) {\n          column.indexes.push(this.cursor)\n          this.cursor++\n        }\n      },\n    }\n  }\n</script>\n\n<style scoped>\n  .masonry-wall {\n    display: flex;\n  }\n\n  .masonry-wall:not(.ready) {\n    opacity: 0;\n  }\n\n  .masonry-column {\n    flex-grow: 1;\n    flex-basis: 0;\n\n    display: flex;\n    flex-direction: column;\n  }\n\n  .masonry-bottom {\n    flex-grow: 1;\n    margin-top: -300px;\n    padding-top: 300px;\n    min-height: 100px;\n    z-index: -1;\n  }\n</style>\n"
  }
]