Full Code of fuxingloh/vue-masonry-wall for AI

master a02d49c78cfd cached
21 files
24.4 KB
7.2k tokens
1 requests
Download .txt
Repository: fuxingloh/vue-masonry-wall
Branch: master
Commit: a02d49c78cfd
Files: 21
Total size: 24.4 KB

Directory structure:
gitextract_pvs8vdpn/

├── .browserslistrc
├── .github/
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── auto_assign.yml
│   ├── pr-labeler.yml
│   ├── release-drafter.yml
│   └── workflows/
│       ├── ci.yml
│       ├── publish.yml
│       ├── release-drafter.yml
│       └── triage.yml
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── babel.config.js
├── build/
│   └── rollup.config.js
├── examples/
│   ├── index.vue
│   ├── package.json
│   └── serve.js
├── package.json
└── src/
    ├── entry.js
    └── vue-masonry-wall.vue

================================================
FILE CONTENTS
================================================

================================================
FILE: .browserslistrc
================================================
current node
last 2 versions and > 2%
ie > 10


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Give a brief description of the pull request. 
-->


================================================
FILE: .github/auto_assign.yml
================================================
addAssignees: author
addReviewers: true

reviewers:
  - fuxingloh

numberOfReviewers: 0


================================================
FILE: .github/pr-labeler.yml
================================================
feature: [ 'feature/*', 'feat/*', 'enhancement/*' ]
fix: [ 'fix/*', 'bug/*', 'bugfix/*' ]
chore: [ 'chore/*', 'docs/*' , 'doc/*', 'ci/*' ]


================================================
FILE: .github/release-drafter.yml
================================================
name-template: 'v$RESOLVED_VERSION 🌈'
tag-template: 'v$RESOLVED_VERSION'
categories:
  - title: '🚀 Features'
    labels:
      - 'feature'
      - 'enhancement'
  - title: '🐛 Bug Fixes'
    labels:
      - 'fix'
      - 'bugfix'
      - 'bug'
  - title: '🧰 Maintenance'
    labels:
      - 'chore'
      - 'docs'
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
change-title-escapes: '\<*_&'
version-resolver:
  minor:
    labels:
      - 'feature'
      - 'enhancement'
  patch:
    labels:
      - 'patch'
      - 'bug'
      - 'bugfix'
      - 'fix'
  default: patch
template: |
  ## Changes

  $CHANGES


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  pull_request:
    branches: [ master ]

jobs:
  yarn:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - uses: actions/setup-node@v1
        with:
          node-version: '14'
          registry-url: 'https://registry.npmjs.org'

      - run: yarn install --frozen-lockfile
      - run: yarn build


================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish

on:
  release:
    types: [ published ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v1
        with:
          node-version: '14'
          registry-url: 'https://registry.npmjs.org'

      - run: yarn install --frozen-lockfile

      - name: Check package version
        uses: technote-space/package-version-check-action@v1
        with:
          COMMIT_MESSAGE: 'release: update package version'

      - run: yarn build

      - run: yarn publish --non-interactive
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}


================================================
FILE: .github/workflows/release-drafter.yml
================================================
name: Release Drafter

on:
  push:
    branches: [ master ]


jobs:
  draft-release:
    runs-on: ubuntu-latest
    steps:
      - uses: release-drafter/release-drafter@v5
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .github/workflows/triage.yml
================================================
name: "Triage"

on:
  pull_request:
    types: [ opened, reopened, ready_for_review ]
    branches: [ master ]

jobs:
  triage:
    name: Triage
    runs-on: ubuntu-latest
    steps:
      - uses: TimonVS/pr-labeler-action@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - uses: kentaro-m/auto-assign-action@v1.1.2


================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
/dist

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?


================================================
FILE: .npmignore
================================================
*
!dist/*
!src/vue-masonry-wall.vue


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 Fuxing Loh

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# vue-masonry-wall
A pure vue responsive masonry implementation without direct dom manipulation, ssr friendly with lazy appending. 
I created this because other libraries has no SSR support, and I needed a pure vue implementation. 

* [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)
* [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)

[![vue-masonry-wall screenshot](example.png)](https://nuxt-app.now.sh/vue-masonry-wall-image)

## Installation
```shell script
npm i vue-masonry-wall
# or yarn
yarn add vue-masonry-wall
```

## Features 
* No Direct DOM Manipulation
* SSR friendly, able to load and re hydrate later
* 1 dependency only, no legacy dependencies
* Auto lazy appending, scroll to auto load more
* Auto item placement, will find best column 
* Responsive design

## Usage
```vue
<vue-masonry-wall :items="items" :options="options" @append="append"/>
```

```js
const items = []
const options = {
  width: 300,
  padding: {
    default: 12,
    1: 6,
    2: 8
  }
}

const append = () => {
  // API call and add items
  this.items.push(...[])
}
```

### Basic
```vue
<template>
  <div id="app">
    <h2>Masonry: append endlessly</h2>

    <vue-masonry-wall :items="items" :options="{width: 300, padding: 12}" @append="append">
      <template v-slot:default="{item}">
        <div class="item">
          <h5>{{item.title}}</h5>
          <p>{{item.content}}</p>
        </div>
      </template>
    </vue-masonry-wall>
  </div>
</template>

<script>
  import VueMasonryWall from "vue-masonry-wall";

  export default {
    name: 'app',
    components: {VueMasonryWall},
    data() {
      return {
        items: [
          {title: 'Item 0', content: 'Content'},
          {title: 'Item 1', content: 'Content'},
        ]
      }
    },
    methods: {
      /**
       * I am mocking a API call that load 20 objects at a time.
       */
      append() {
        for (let i = 0; i < 20; i++) {
          this.items.push({title: `Item ${this.items.length}`, content: 'Content'})
        }
      }
    }
  }
</script>
```
### Nuxt SSR
Add `:ssr="{columns: 2}"` to masonry so that during SSR, it will be load in 2 columns.

SSR has no clue what is the size of your height of your element or width of the browser.
You can however guess based on user-agent: https://github.com/nuxt-community/device-module
This param allow you to preload a config for SSR rendering, it will distribute your items into all columns evenly.

```vue
<vue-masonry-wall :items="items" :options="{width: 300, padding: 12}" :ssr="{columns: 2}" @append="append">
  <template v-slot:default="{item}">
    <div class="item">
      <h5>{{item.title}}</h5>
      <p>{{item.content}}</p>
    </div>
  </template>
</vue-masonry-wall>
```

## Dependencies
- [vue-observe-visibility](https://github.com/Akryum/vue-observe-visibility) for [IntersectionObserver](https://github.com/w3c/IntersectionObserver/tree/master/polyfill)

## Contributing
For 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).

## TODO
> These were features from my original project that I removed for brevity of this package. 

* [ ] [nuxt-community/device-module](https://github.com/nuxt-community/device-module) to detect the browser for better SSR support.
* [ ] [vue-scrollto](https://www.npmjs.com/package/vue-scrollto) to scroll to an item in masonry.

## [Vue Horizontal](https://github.com/fuxingloh/vue-horizontal)

I also maintain another project called [Vue Horizontal](https://github.com/fuxingloh/vue-horizontal).

At its core, Vue Horizontal is an ultra simple pure vue horizontal layout for
modern responsive web with zero dependencies.
Vue Horizontal is also an ultra complex code snippet dossier with over 100 SPA/SSR/SSG friendly recipes for your design needs.
Do check it out, might be useful for you!


================================================
FILE: babel.config.js
================================================
const devPresets = ['@vue/babel-preset-app'];
const buildPresets = ['@babel/preset-env'];
module.exports = {
  presets: (process.env.NODE_ENV === 'development' ? devPresets : buildPresets),
};


================================================
FILE: build/rollup.config.js
================================================
// rollup.config.js
import fs from 'fs';
import path from 'path';
import vue from 'rollup-plugin-vue';
import alias from '@rollup/plugin-alias';
import commonjs from '@rollup/plugin-commonjs';
import replace from '@rollup/plugin-replace';
import babel from 'rollup-plugin-babel';
import { terser } from 'rollup-plugin-terser';
import minimist from 'minimist';

// Get browserslist config and remove ie from es build targets
const esbrowserslist = fs.readFileSync('./.browserslistrc')
  .toString()
  .split('\n')
  .filter((entry) => entry && entry.substring(0, 2) !== 'ie');

const argv = minimist(process.argv.slice(2));

const projectRoot = path.resolve(__dirname, '..');

const baseConfig = {
  input: 'src/entry.js',
  plugins: {
    preVue: [
      alias({
        resolve: ['.js', '.jsx', '.ts', '.tsx', '.vue'],
        entries: {
          '@': path.resolve(projectRoot, 'src'),
        },
      }),
    ],
    replace: {
      'process.env.NODE_ENV': JSON.stringify('production'),
      'process.env.ES_BUILD': JSON.stringify('false'),
    },
    vue: {
      css: true,
      template: {
        isProduction: true,
      },
    },
    babel: {
      exclude: 'node_modules/**',
      extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'],
    },
  },
};

// ESM/UMD/IIFE shared settings: externals
// Refer to https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency
const external = [
  // list external dependencies, exactly the way it is written in the import statement.
  // eg. 'jquery'
  'vue',
];

// UMD/IIFE shared settings: output.globals
// Refer to https://rollupjs.org/guide/en#output-globals for details
const globals = {
  // Provide global variable names to replace your external imports
  // eg. jquery: '$'
  vue: 'Vue',
};

// Customize configs for individual targets
const buildFormats = [];
if (!argv.format || argv.format === 'es') {
  const esConfig = {
    ...baseConfig,
    external,
    output: {
      file: 'dist/vue-masonry-wall.esm.js',
      format: 'esm',
      exports: 'named',
    },
    plugins: [
      replace({
        ...baseConfig.plugins.replace,
        'process.env.ES_BUILD': JSON.stringify('true'),
      }),
      ...baseConfig.plugins.preVue,
      vue(baseConfig.plugins.vue),
      babel({
        ...baseConfig.plugins.babel,
        presets: [
          [
            '@babel/preset-env',
            {
              targets: esbrowserslist,
            },
          ],
        ],
      }),
      commonjs(),
    ],
  };
  buildFormats.push(esConfig);
}

if (!argv.format || argv.format === 'cjs') {
  const umdConfig = {
    ...baseConfig,
    external,
    output: {
      compact: true,
      file: 'dist/vue-masonry-wall.ssr.js',
      format: 'cjs',
      name: 'VueMasonryWall',
      exports: 'named',
      globals,
    },
    plugins: [
      replace(baseConfig.plugins.replace),
      ...baseConfig.plugins.preVue,
      vue({
        ...baseConfig.plugins.vue,
        template: {
          ...baseConfig.plugins.vue.template,
          optimizeSSR: true,
        },
      }),
      babel(baseConfig.plugins.babel),
      commonjs(),
    ],
  };
  buildFormats.push(umdConfig);
}

if (!argv.format || argv.format === 'iife') {
  const unpkgConfig = {
    ...baseConfig,
    external,
    output: {
      compact: true,
      file: 'dist/vue-masonry-wall.min.js',
      format: 'iife',
      name: 'VueMasonryWall',
      exports: 'named',
      globals,
    },
    plugins: [
      replace(baseConfig.plugins.replace),
      ...baseConfig.plugins.preVue,
      vue(baseConfig.plugins.vue),
      babel(baseConfig.plugins.babel),
      commonjs(),
      terser({
        output: {
          ecma: 5,
        },
      }),
    ],
  };
  buildFormats.push(unpkgConfig);
}

// Export config
export default buildFormats;


================================================
FILE: examples/index.vue
================================================
<template>
  <div id="app">
    <h2>Masonry: append 100</h2>

    <vue-masonry-wall :items="items" :options="options" @append="append">
      <template v-slot:default="{item}">
        <div class="item">
          <h5>{{ item.title }}</h5>
          <p>{{ item.content }}</p>
        </div>
      </template>
    </vue-masonry-wall>
  </div>
</template>

<script>
import Vue from 'vue';
import VueMasonryWall from '@/vue-masonry-wall.vue';

function content() {
  const length = Math.random() * 300 + 30

  let result = '';
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  const charactersLength = characters.length;
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
  return result;
}

export default Vue.extend({
  name: 'ServeDev',
  components: {
    VueMasonryWall
  },
  data() {
    return {
      options: {
        width: 300,
        padding: {
          default: 12,
          2: 8,
          3: 8,
        }
      },
      items: [
        {title: 'Item 0', content: content()},
        {title: 'Item 1', content: content()},
        {title: 'Item 2', content: content()},
        {title: 'Item 3', content: content()},
      ]
    }
  },
  methods: {
    /**
     * I am mocking a API call that load 20 objects at a time.
     */
    append() {
      if (this.items.length > 100) {
        return
      }

      for (let i = 0; i < 20; i++) {
        this.items.push({title: `Item ${this.items.length}`, content: content()})
      }
    }
  }
});
</script>


<style>
#app {
  font-family: 'Roboto', sans-serif;

  max-width: 1400px;

  margin-left: auto;
  margin-right: auto;

  padding: 80px 24px;
}

@media (min-width: 1200px) {
  #app {
    padding-left: 80px;
    padding-right: 80px;
  }
}

h2 {
  font-size: 30px;
  margin: 0 0 24px;
}

h5 {
  font-size: 18px;
  line-height: 1.5;
  margin: 0 0 8px;
}

p {
  font-size: 17px;
  line-height: 1.5;
  font-weight: 400;
  margin: 0;

  word-break: break-word;
}

.item {
  padding: 16px 24px;
  border-radius: 3px;
  background: #F5F5F5;
}
</style>


================================================
FILE: examples/package.json
================================================
{
  "name": "abc",
  "version": "1.0.0",
  "description": "",

  "main": "dist/abc.ssr.js",
  "browser": "dist/abc.esm.js",
  "module": "dist/abc.esm.js",
  "unpkg": "dist/abc.min.js",

  "files": [
    "dist/*",
    "src/**/*.vue"
  ],

  "scripts": {
    "serve": "vue-cli-service serve dev/serve.js",
    "build": "cross-env NODE_ENV=production rollup --config build/rollup.config.js",
    "build:ssr": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format cjs",
    "build:es": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format es",
    "build:unpkg": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format iife"
  },

  "dependencies": {
  },

  "devDependencies": {
    "@babel/core": "^7.9.0",
    "@babel/preset-env": "^7.9.5",
    "@rollup/plugin-alias": "^2.2.0",
    "@rollup/plugin-commonjs": "^11.1.0",
    "@rollup/plugin-replace": "^2.3.2",
    "@vue/cli-plugin-babel": "^4.3.1",
    "@vue/cli-service": "^4.3.1",
    "cross-env": "^7.0.2",
    "minimist": "^1.2.5",
    "rollup": "^2.7.3",
    "rollup-plugin-babel": "^4.4.0",
    "rollup-plugin-terser": "^5.3.0",
    "rollup-plugin-vue": "^5.1.6",
    "vue": "^2.6.11",
    "vue-template-compiler": "^2.6.11"
  },
  "peerDependencies": {
    "vue": "^2.6.11"
  },
  "engines": {
    "node": ">=10"
  }
}


================================================
FILE: examples/serve.js
================================================
import Vue from "vue";
import Index from "./index.vue";

Vue.config.productionTip = false;

new Vue({
  render: (h) => h(Index),
}).$mount("#app");


================================================
FILE: package.json
================================================
{
  "name": "vue-masonry-wall",
  "version": "0.2.5",
  "description": "100% vue masonry without direct dom manipulation, ssr friendly.",
  "author": "Fuxing Loh",
  "repository": {
    "type": "git",
    "url": "https://github.com/fuxingloh/vue-masonry-wall.git"
  },
  "bugs": "https://github.com/fuxingloh/vue-masonry-wall/issues",
  "keywords": [
    "vue",
    "vuejs",
    "masonry",
    "pinterest"
  ],
  "license": "MIT",
  "private": false,
  "main": "dist/vue-masonry-wall.ssr.js",
  "browser": "dist/vue-masonry-wall.esm.js",
  "module": "dist/vue-masonry-wall.esm.js",
  "unpkg": "dist/vue-masonry-wall.min.js",
  "files": [
    "dist/*",
    "src/**/*.vue"
  ],
  "scripts": {
    "build": "cross-env NODE_ENV=production rollup --config build/rollup.config.js",
    "build:ssr": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format cjs",
    "build:es": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format es",
    "build:unpkg": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format iife",
    "examples:serve": "vue-cli-service serve examples/serve.js",
    "examples:build": "vue-cli-service build examples/serve.js",
    "prepublishOnly": "yarn build"
  },
  "dependencies": {
    "vue-observe-visibility": "^0.4.6"
  },
  "peerDependencies": {
    "lodash": "^4.17.15",
    "vue": "^2.6.10"
  },
  "devDependencies": {
    "@babel/core": "^7.9.0",
    "@babel/preset-env": "^7.9.5",
    "@rollup/plugin-alias": "^2.2.0",
    "@rollup/plugin-commonjs": "^11.1.0",
    "@rollup/plugin-replace": "^2.3.2",
    "@vue/cli-plugin-babel": "^4.3.1",
    "@vue/cli-service": "^4.3.1",
    "cross-env": "^7.0.2",
    "minimist": "^1.2.5",
    "rollup": "^2.7.3",
    "rollup-plugin-babel": "^4.4.0",
    "rollup-plugin-terser": "^5.3.0",
    "rollup-plugin-vue": "^5.1.6",
    "vue": "^2.6.11",
    "vue-template-compiler": "^2.6.11"
  },
  "engines": {
    "node": ">=10"
  }
}


================================================
FILE: src/entry.js
================================================
// Import vue component
import component from '@/vue-masonry-wall.vue';

// install function executed by Vue.use()
const install = function installVueMasonryWall(Vue) {
  if (install.installed) return;
  install.installed = true;
  Vue.component('VueMasonryWall', component);
};

// Create module definition for Vue.use()
const plugin = {
  install,
};

// To auto-install on non-es builds, when vue is found
// eslint-disable-next-line no-redeclare
/* global window, global */
if ("false" === process.env.ES_BUILD) {
  let GlobalVue = null;
  if (typeof window !== "undefined") {
    GlobalVue = window.Vue;
  } else if (typeof global !== "undefined") {
    GlobalVue = global.Vue;
  }
  if (GlobalVue) {
    GlobalVue.use(plugin);
  }
}

// Inject install function into component - allows component
// to be registered via Vue.use() as well as Vue.component()
component.install = install;

// Export component by default
export default component;

// It's possible to expose named exports when writing components that can
// also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo';
// export const RollupDemoDirective = component;


================================================
FILE: src/vue-masonry-wall.vue
================================================
<template>
  <div class="masonry-wall" ref="wall" :style="_style.wall" :class="{ready}">
    <div class="masonry-column" v-for="(lane, index) in columns" :key="index" :style="_style.lane">

      <div class="masonry-item" v-for="i in lane.indexes" :key="i" :style="_style.item" :ref="`item_${i}`">
        <slot v-bind:item="items[i]" :index="i">{{items[i]}}</slot>
      </div>

      <div class="masonry-bottom" ref="bottom" :data-column="index"
           v-observe-visibility="{callback: (v) => v && _fill(),throttle:_options.throttle}"
      />
    </div>
  </div>
</template>

<script>
  import {maxBy} from 'lodash'
  import {ObserveVisibility} from "vue-observe-visibility";

  /**
   * @param count number of columns to create
   * @returns {[{i: number, indexes: []},...]}
   */
  const _newColumns = (count) => {
    const columns = []
    for (let i = 0; i < count; i++) {
      columns.push({i: i, indexes: []})
    }
    return columns
  }

  export default {
    name: "VueMasonryWall",
    directives: {
      'observe-visibility': ObserveVisibility
    },
    props: {

      /**
       * Array of items to add into masonry
       */
      items: {
        type: Array,
        required: true
      },

      /**
       * Options to config masonry.
       *
       * {
       *     width: 300,
       *     padding: {
       *         default: 12,
       *         1: 6,
       *         2: 8,
       *     },
       *     throttle: 300
       * }
       */
      options: {
        type: Object,
        required: false
      },

      /**
       * SSR has no clue what is the size of your height of your element or width of the browser.
       * You can however guess based on user-agent: https://github.com/nuxt-community/device-module
       * This param allow you to preload a config for SSR rendering, it will distribute your items into all columns evenly.
       *
       * Once the client is mounted, it will redraw if the config is different from SSR.
       *
       * {
       *     column: 2
       * }
       */
      ssr: {
        type: Object,
        required: false
      }
    },
    data() {
      const count = this.ssr && this.ssr.columns
      if (count > 0) {
        const columns = _newColumns(count)

        for (let i = 0; i < this.items.length; i++) {
          columns[i % count].indexes.push(i)
        }

        return {
          columns: columns,
          cursor: this.items.length,
          ready: false
        }
      }

      return {
        columns: [],
        cursor: 0,
        ready: false
      }
    },
    /**
     * For detecting browser resize event to redraw the columns.
     */
    mounted() {
      this.$resize = () => {
        if (this.columns.length !== this._columnSize()) {
          this.redraw()
        }
      }

      this.$resize()
    
      // 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.
      if(!this.ready) this.ready = true;
      
      window.addEventListener('resize', this.$resize)
    },
    /**
     * Remove resize event listener
     */
    beforeDestroy() {
      window.removeEventListener('resize', this.$resize)
    },
    computed: {

      /**
       * Options with default override if not given
       *
       * @private
       */
      _options() {
        const options = this.options
        return {
          width: options && options.width || 300,
          padding: {
            default: options && options.padding && options.padding.default || 12
          },
          throttle: options && options.throttle || 300
        }
      },

      /**
       * Style of wall, column and item for padding
       * @private
       */
      _style() {
        let padding = this.options && this.options.padding
        if (padding && typeof padding != 'number') {
          padding = this.options.padding[this.columns.length] || this._options.padding.default
        }

        return {
          wall: {
            margin: `-${padding}px`
          },
          lane: {
            paddingLeft: `${padding}px`,
            paddingRight: `${padding}px`,
          },
          item: {
            paddingTop: `${padding}px`,
            paddingBottom: `${padding}px`,
          }
        }
      },
    },
    methods: {
      /**
       * Redraw masonry
       */
      redraw() {
        this.ready = false
        this.columns.splice(0)
        this.cursor = 0
        this.columns.push(..._newColumns(this._columnSize()))
        this.ready = true
        this._fill()
      },

      /**
       *
       * @returns {number}
       * @private internal component use
       */
      _columnSize() {
        const length = Math.round(this.$refs.wall.scrollWidth / this._options.width)
        if (length < 1) return 1
        return length
      },

      /**
       * Add items into masonry columns, items are added to the shortest column first.
       *
       * @private internal component use
       */
      _fill() {
        if (!this.ready) return

        if (this.cursor >= this.items.length) {
          // Request for more items
          this.$emit('append')
          return
        }

        // Keep filling until no more items
        this.$nextTick(() => {
          const bottom = maxBy(this.$refs.bottom, (spacer) => spacer.clientHeight || 0)
          this._addItem(bottom.dataset.column)
          this._fill()
        })
      },

      /**
       * Items will automatically be taken from items with cursor.
       *
       * @param index of the column to add to
       * @private internal component use
       */
      _addItem(index) {
        const column = this.columns[index]
        if (this.items[this.cursor]) {
          column.indexes.push(this.cursor)
          this.cursor++
        }
      },
    }
  }
</script>

<style scoped>
  .masonry-wall {
    display: flex;
  }

  .masonry-wall:not(.ready) {
    opacity: 0;
  }

  .masonry-column {
    flex-grow: 1;
    flex-basis: 0;

    display: flex;
    flex-direction: column;
  }

  .masonry-bottom {
    flex-grow: 1;
    margin-top: -300px;
    padding-top: 300px;
    min-height: 100px;
    z-index: -1;
  }
</style>
Download .txt
gitextract_pvs8vdpn/

├── .browserslistrc
├── .github/
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── auto_assign.yml
│   ├── pr-labeler.yml
│   ├── release-drafter.yml
│   └── workflows/
│       ├── ci.yml
│       ├── publish.yml
│       ├── release-drafter.yml
│       └── triage.yml
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── babel.config.js
├── build/
│   └── rollup.config.js
├── examples/
│   ├── index.vue
│   ├── package.json
│   └── serve.js
├── package.json
└── src/
    ├── entry.js
    └── vue-masonry-wall.vue
Condensed preview — 21 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (27K chars).
[
  {
    "path": ".browserslistrc",
    "chars": 46,
    "preview": "current node\nlast 2 versions and > 2%\nie > 10\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 56,
    "preview": "<!--\nGive a brief description of the pull request. \n-->\n"
  },
  {
    "path": ".github/auto_assign.yml",
    "chars": 88,
    "preview": "addAssignees: author\naddReviewers: true\n\nreviewers:\n  - fuxingloh\n\nnumberOfReviewers: 0\n"
  },
  {
    "path": ".github/pr-labeler.yml",
    "chars": 139,
    "preview": "feature: [ 'feature/*', 'feat/*', 'enhancement/*' ]\nfix: [ 'fix/*', 'bug/*', 'bugfix/*' ]\nchore: [ 'chore/*', 'docs/*' ,"
  },
  {
    "path": ".github/release-drafter.yml",
    "chars": 606,
    "preview": "name-template: 'v$RESOLVED_VERSION 🌈'\ntag-template: 'v$RESOLVED_VERSION'\ncategories:\n  - title: '🚀 Features'\n    labels:"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 360,
    "preview": "name: CI\n\non:\n  pull_request:\n    branches: [ master ]\n\njobs:\n  yarn:\n    name: Build\n    runs-on: ubuntu-latest\n    ste"
  },
  {
    "path": ".github/workflows/publish.yml",
    "chars": 627,
    "preview": "name: Publish\n\non:\n  release:\n    types: [ published ]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - use"
  },
  {
    "path": ".github/workflows/release-drafter.yml",
    "chars": 237,
    "preview": "name: Release Drafter\n\non:\n  push:\n    branches: [ master ]\n\n\njobs:\n  draft-release:\n    runs-on: ubuntu-latest\n    step"
  },
  {
    "path": ".github/workflows/triage.yml",
    "chars": 342,
    "preview": "name: \"Triage\"\n\non:\n  pull_request:\n    types: [ opened, reopened, ready_for_review ]\n    branches: [ master ]\n\njobs:\n  "
  },
  {
    "path": ".gitignore",
    "chars": 214,
    "preview": ".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"
  },
  {
    "path": ".npmignore",
    "chars": 36,
    "preview": "*\n!dist/*\n!src/vue-masonry-wall.vue\n"
  },
  {
    "path": "LICENSE",
    "chars": 1067,
    "preview": "MIT License\n\nCopyright (c) 2020 Fuxing Loh\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
  },
  {
    "path": "README.md",
    "chars": 4235,
    "preview": "# vue-masonry-wall\nA pure vue responsive masonry implementation without direct dom manipulation, ssr friendly with lazy "
  },
  {
    "path": "babel.config.js",
    "chars": 193,
    "preview": "const devPresets = ['@vue/babel-preset-app'];\nconst buildPresets = ['@babel/preset-env'];\nmodule.exports = {\n  presets: "
  },
  {
    "path": "build/rollup.config.js",
    "chars": 3809,
    "preview": "// rollup.config.js\nimport fs from 'fs';\nimport path from 'path';\nimport vue from 'rollup-plugin-vue';\nimport alias from"
  },
  {
    "path": "examples/index.vue",
    "chars": 2125,
    "preview": "<template>\n  <div id=\"app\">\n    <h2>Masonry: append 100</h2>\n\n    <vue-masonry-wall :items=\"items\" :options=\"options\" @a"
  },
  {
    "path": "examples/package.json",
    "chars": 1350,
    "preview": "{\n  \"name\": \"abc\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n\n  \"main\": \"dist/abc.ssr.js\",\n  \"browser\": \"dist/abc.esm.j"
  },
  {
    "path": "examples/serve.js",
    "chars": 148,
    "preview": "import Vue from \"vue\";\nimport Index from \"./index.vue\";\n\nVue.config.productionTip = false;\n\nnew Vue({\n  render: (h) => h"
  },
  {
    "path": "package.json",
    "chars": 1966,
    "preview": "{\n  \"name\": \"vue-masonry-wall\",\n  \"version\": \"0.2.5\",\n  \"description\": \"100% vue masonry without direct dom manipulation"
  },
  {
    "path": "src/entry.js",
    "chars": 1166,
    "preview": "// Import vue component\nimport component from '@/vue-masonry-wall.vue';\n\n// install function executed by Vue.use()\nconst"
  },
  {
    "path": "src/vue-masonry-wall.vue",
    "chars": 6216,
    "preview": "<template>\n  <div class=\"masonry-wall\" ref=\"wall\" :style=\"_style.wall\" :class=\"{ready}\">\n    <div class=\"masonry-column\""
  }
]

About this extraction

This page contains the full source code of the fuxingloh/vue-masonry-wall GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 21 files (24.4 KB), approximately 7.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!