├── .prettierignore ├── .browserslistrc ├── demo.png ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── auto_assign.yml ├── pr-labeler.yml ├── workflows │ ├── release-drafter.yml │ ├── triage.yml │ ├── gh-pages.yml │ ├── publish.yml │ └── ci.yml └── release-drafter.yml ├── vue.config.js ├── examples ├── serve.js ├── simple.vue ├── position-start.vue ├── full-width.vue ├── auto-play.vue ├── nav-prev-next-slot.vue ├── responsive.vue ├── item-customization.vue ├── left-right-slot.vue ├── index.vue ├── image-two.vue ├── image-three.vue ├── image-four.vue └── image-five.vue ├── babel.config.js ├── .gitignore ├── LICENSE ├── src ├── entry.js └── vue-horizontal-list.vue ├── package.json └── README.md /.prettierignore: -------------------------------------------------------------------------------- 1 | .github 2 | .idea 3 | dist 4 | gh-pages 5 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | current node 2 | last 2 versions and > 2% 3 | ie > 10 4 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuxingloh/vue-horizontal-list/HEAD/demo.png -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /.github/auto_assign.yml: -------------------------------------------------------------------------------- 1 | addAssignees: author 2 | addReviewers: true 3 | 4 | reviewers: 5 | - fuxingloh 6 | 7 | numberOfReviewers: 0 8 | -------------------------------------------------------------------------------- /.github/pr-labeler.yml: -------------------------------------------------------------------------------- 1 | feature: [ 'feature/*', 'feat/*', 'enhancement/*' ] 2 | fix: [ 'fix/*', 'bug/*', 'bugfix/*' ] 3 | chore: [ 'chore/*', 'docs/*' , 'doc/*', 'ci/*' ] 4 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: 3 | process.env.NODE_ENV === "production" ? "/vue-horizontal-list/" : "/", 4 | outputDir: "gh-pages", 5 | }; 6 | -------------------------------------------------------------------------------- /examples/serve.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Index from "./index.vue"; 3 | 4 | Vue.config.productionTip = false; 5 | 6 | new Vue({ 7 | render: (h) => h(Index), 8 | }).$mount("#app"); 9 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const devPresets = ["@vue/babel-preset-app"]; 2 | const buildPresets = ["@babel/preset-env"]; 3 | module.exports = { 4 | presets: process.env.NODE_ENV === "development" ? devPresets : buildPresets, 5 | }; 6 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | 8 | jobs: 9 | draft-release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: release-drafter/release-drafter@v5 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | 23 | gh-pages 24 | -------------------------------------------------------------------------------- /.github/workflows/triage.yml: -------------------------------------------------------------------------------- 1 | name: "Triage" 2 | 3 | on: 4 | pull_request: 5 | types: [ opened, reopened, ready_for_review ] 6 | branches: [ master ] 7 | 8 | jobs: 9 | triage: 10 | name: Triage 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: TimonVS/pr-labeler-action@v3 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | 17 | - uses: kentaro-m/auto-assign-action@v1.1.2 18 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Pages 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ master ] 7 | 8 | 9 | jobs: 10 | deploy: 11 | name: Deploy 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: '14' 19 | 20 | - run: yarn install --frozen-lockfile 21 | 22 | - run: yarn run examples:build 23 | 24 | - name: Deploy 🚀 25 | uses: JamesIves/github-pages-deploy-action@3.7.1 26 | with: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | BRANCH: gh-pages 29 | FOLDER: gh-pages 30 | CLEAN: true 31 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: '14' 15 | registry-url: 'https://registry.npmjs.org' 16 | 17 | - run: yarn install --frozen-lockfile 18 | 19 | - name: Check package version 20 | uses: technote-space/package-version-check-action@v1 21 | with: 22 | COMMIT_MESSAGE: 'release: update package version' 23 | 24 | - run: yarn build 25 | 26 | - run: yarn publish --non-interactive 27 | env: 28 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 29 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION 🌈' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | categories: 4 | - title: '🚀 Features' 5 | labels: 6 | - 'feature' 7 | - 'enhancement' 8 | - title: '🐛 Bug Fixes' 9 | labels: 10 | - 'fix' 11 | - 'bugfix' 12 | - 'bug' 13 | - title: '🧰 Maintenance' 14 | labels: 15 | - 'chore' 16 | - 'docs' 17 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 18 | change-title-escapes: '\<*_&' 19 | version-resolver: 20 | minor: 21 | labels: 22 | - 'feature' 23 | - 'enhancement' 24 | patch: 25 | labels: 26 | - 'patch' 27 | - 'bug' 28 | - 'bugfix' 29 | - 'fix' 30 | default: patch 31 | template: | 32 | ## Changes 33 | 34 | $CHANGES 35 | -------------------------------------------------------------------------------- /examples/simple.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 33 | 34 | 41 | -------------------------------------------------------------------------------- /examples/position-start.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 37 | 38 | 45 | -------------------------------------------------------------------------------- /examples/full-width.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 38 | 39 | 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Fuxing Loh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | 7 | jobs: 8 | yarn: 9 | name: Build 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: '14' 17 | registry-url: 'https://registry.npmjs.org' 18 | 19 | - run: yarn install --frozen-lockfile 20 | - run: yarn build 21 | 22 | prettier: 23 | name: Prettier 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v2 27 | 28 | - uses: actions/setup-node@v1 29 | with: 30 | node-version: '14' 31 | registry-url: 'https://registry.npmjs.org' 32 | 33 | - run: yarn install --frozen-lockfile 34 | - run: yarn run prettier:check 35 | 36 | - if: ${{ failure() }} 37 | uses: unsplash/comment-on-pr@master 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | with: 41 | msg: "Prettier check failed. You need to run `yarn run prettier:write` before committing." 42 | check_for_duplicate_msg: true 43 | -------------------------------------------------------------------------------- /examples/auto-play.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 40 | 41 | 48 | -------------------------------------------------------------------------------- /examples/nav-prev-next-slot.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 41 | 42 | 49 | -------------------------------------------------------------------------------- /examples/responsive.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 43 | 44 | 51 | -------------------------------------------------------------------------------- /examples/item-customization.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 39 | 40 | 47 | 48 | 54 | -------------------------------------------------------------------------------- /src/entry.js: -------------------------------------------------------------------------------- 1 | // Import vue component 2 | import component from "@/vue-horizontal-list.vue"; 3 | 4 | // install function executed by Vue.use() 5 | const install = function installVueHorizontalList(Vue) { 6 | if (install.installed) return; 7 | install.installed = true; 8 | Vue.component("VueHorizontalList", component); 9 | }; 10 | 11 | // Create module definition for Vue.use() 12 | const plugin = { 13 | install, 14 | }; 15 | 16 | // To auto-install on non-es builds, when vue is found 17 | // eslint-disable-next-line no-redeclare 18 | /* global window, global */ 19 | if ("false" === process.env.ES_BUILD) { 20 | let GlobalVue = null; 21 | if (typeof window !== "undefined") { 22 | GlobalVue = window.Vue; 23 | } else if (typeof global !== "undefined") { 24 | GlobalVue = global.Vue; 25 | } 26 | if (GlobalVue) { 27 | GlobalVue.use(plugin); 28 | } 29 | } 30 | 31 | // Inject install function into component - allows component 32 | // to be registered via Vue.use() as well as Vue.component() 33 | component.install = install; 34 | 35 | // Export component by default 36 | export default component; 37 | 38 | // It's possible to expose named exports when writing components that can 39 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 40 | // export const RollupDemoDirective = component; 41 | -------------------------------------------------------------------------------- /examples/left-right-slot.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 53 | 54 | 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-horizontal-list", 3 | "version": "1.1.0", 4 | "description": "A pure vue ssr friendly horizontal list implementation.", 5 | "author": "Fuxing Loh", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/fuxingloh/vue-horizontal-list.git" 9 | }, 10 | "bugs": "https://github.com/fuxingloh/vue-horizontal-list/issues", 11 | "keywords": [ 12 | "vue", 13 | "vuejs", 14 | "horizontal", 15 | "scroll view", 16 | "airbnb list" 17 | ], 18 | "license": "MIT", 19 | "private": false, 20 | "main": "dist/vue-horizontal-list.ssr.js", 21 | "browser": "dist/vue-horizontal-list.esm.js", 22 | "module": "dist/vue-horizontal-list.esm.js", 23 | "unpkg": "dist/vue-horizontal-list.min.js", 24 | "files": [ 25 | "dist/*", 26 | "src/**/*.vue" 27 | ], 28 | "scripts": { 29 | "serve": "vue-cli-service serve examples/serve.js", 30 | "build": "cross-env NODE_ENV=production rollup --config build/rollup.config.js", 31 | "build:ssr": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format cjs", 32 | "build:es": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format es", 33 | "build:unpkg": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format iife", 34 | "examples:serve": "vue-cli-service serve examples/serve.js", 35 | "examples:build": "vue-cli-service build examples/serve.js", 36 | "prepublishOnly": "yarn build", 37 | "prettier:format": "prettier --write .", 38 | "prettier:check": "prettier --check ." 39 | }, 40 | "dependencies": { 41 | "smoothscroll-polyfill": "^0.4.4" 42 | }, 43 | "devDependencies": { 44 | "@babel/core": "^7.9.0", 45 | "@babel/preset-env": "^7.9.5", 46 | "@rollup/plugin-alias": "^2.2.0", 47 | "@rollup/plugin-commonjs": "^11.1.0", 48 | "@rollup/plugin-node-resolve": "^8.4.0", 49 | "@rollup/plugin-replace": "^2.3.2", 50 | "@vue/cli-plugin-babel": "^4.3.1", 51 | "@vue/cli-service": "^4.3.1", 52 | "cross-env": "^7.0.2", 53 | "husky": "^4.3.0", 54 | "minimist": "^1.2.5", 55 | "prettier": "^2.1.2", 56 | "pretty-quick": "^3.1.0", 57 | "rollup": "^2.7.3", 58 | "rollup-plugin-babel": "^4.4.0", 59 | "rollup-plugin-terser": "^5.3.0", 60 | "rollup-plugin-vue": "^5.1.6", 61 | "vue": "^2.6.11", 62 | "vue-template-compiler": "^2.6.11" 63 | }, 64 | "peerDependencies": { 65 | "vue": "^2.6.11" 66 | }, 67 | "engines": { 68 | "node": ">=10" 69 | }, 70 | "husky": { 71 | "hooks": { 72 | "pre-commit": "pretty-quick --staged" 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/index.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 74 | 75 | 101 | 102 | 123 | -------------------------------------------------------------------------------- /examples/image-two.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 95 | 96 | 136 | -------------------------------------------------------------------------------- /examples/image-three.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 99 | 100 | 140 | -------------------------------------------------------------------------------- /examples/image-four.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 103 | 104 | 142 | -------------------------------------------------------------------------------- /examples/image-five.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 98 | 99 | 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 👋 You might want to use [Vue Horizontal](https://github.com/fuxingloh/vue-horizontal) instead! 2 | 3 | [Vue Horizontal](https://github.com/fuxingloh/vue-horizontal) 4 | is another take on the horizontal layout written by me with an ultra simple implementation that is extensible and moves the 5 | responsibility to the user rather than the library. 6 | With zero dependencies, 3 KB for CDN users it's built for your production needs. 7 | 8 | [Vue Horizontal](https://github.com/fuxingloh/vue-horizontal) is a rewrite of this library with many more focus such as 9 | end-to-end testing on real browsers, SSG/SSR CI testing to make sure all code is SSG/SSR compliant for your SEO! 10 | Documentation are also extensible with everything you can think of about horizontal layout documentation through and through. 11 | 12 | [Vue Horizontal](https://github.com/fuxingloh/vue-horizontal) also contains a snippet dossier with many SPA/SSR/SSG 13 | friendly recipes for your design needs. 14 | [Vue Horizontal](https://vue-horizontal.fuxing.dev/) is not just a library, **it's a place for everything horizontal.** 15 | 16 | ## Vue Horizontal List 17 | 18 | A pure vue native horizontal list implementation for mobile/touch and responsive web. 19 | Check it out here: [fuxingloh.github.io/vue-horizontal-list](https://fuxingloh.github.io/vue-horizontal-list/). 20 | 21 | > I created this because I like how AirBnb does their horizontal list, I couldn't find a library that is simple and close to it. 22 | 23 | [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 24 | [![License MIT](https://img.shields.io/github/license/fuxingloh/vue-horizontal-list)](https://github.com/fuxingloh/vue-horizontal-list/blob/master/LICENSE) 25 | [![Latest Release](https://img.shields.io/github/v/release/fuxingloh/vue-horizontal-list)](https://github.com/fuxingloh/vue-horizontal-list/releases) 26 | [![Contributors](https://img.shields.io/github/contributors-anon/fuxingloh/vue-horizontal-list)](https://github.com/fuxingloh/vue-horizontal-list/graphs/contributors) 27 | [![Issues](https://img.shields.io/github/issues/fuxingloh/vue-horizontal-list)](https://github.com/fuxingloh/vue-horizontal-list/issues) 28 | [![GitHub Pages](https://github.com/fuxingloh/vue-horizontal-list/workflows/GitHub%20Pages/badge.svg)](https://fuxingloh.github.io/vue-horizontal-list/) 29 | [![CI](https://github.com/fuxingloh/vue-horizontal-list/workflows/CI/badge.svg)](https://github.com/fuxingloh/vue-horizontal-list/actions?query=workflow%3ACI) 30 | 31 | --- 32 | 33 | [![vue-horizontal-list screenshot](demo.png)](https://nuxt-app.now.sh/vue-horizontal-list) 34 | 35 | ## Installation 36 | 37 | ```shell script 38 | npm i vue-horizontal-list 39 | # or 40 | yarn add vue-horizontal-list 41 | ``` 42 | 43 | Check out this [alternative project](https://github.com/fuxingloh/vue-horizontal) maintained by me for a new take on horizontal layout in vue. 44 | 45 | #### Basic usage 46 | 47 | ```vue 48 | 58 | 64 | 65 | ``` 66 | 67 | ## Features 68 | 69 | - Lightweight implementation with 1 dependencies. 70 | - SSR supported 71 | - Mobile touch screen friendly 72 | - Invisible scroll bar for consistent Windows and MacOS browsing experience. 73 | - Snap to the nearest item in the horizontal-list when scrolling. 74 | - Windowed & Full-screen mode 75 | - The windowed mode will respect the container and not show overflowing item 76 | - Full-screen mode will show overflowing item, best result for small screen 77 | - Dynamic responsive breakpoint configuration 78 | - Navigation control will show up dynamically for larger screen 79 | - Touch screen friendly 80 | - Slideshow autoplay by [@Draccano](https://github.com/Draccano). 81 | - Slot different content at the beginning, or the ending of the items list by [@Draccano](https://github.com/Draccano). 82 | - Minimal config setup 83 | - Tested on chrome, edge and safari 84 | - [Demo](https://fuxingloh.github.io/vue-horizontal-list/) 85 | - [Examples](https://github.com/fuxingloh/vue-horizontal-list/tree/master/examples) 86 | 87 | ## All available options 88 | 89 | ```js 90 | const options = { 91 | item: { 92 | // css class to inject into each individual item 93 | class: "", 94 | // padding between each item 95 | padding: 12, 96 | }, 97 | list: { 98 | // css class for the parent of item 99 | class: "", 100 | // maximum width of the list it can extend to before switching to windowed mode, basically think of the bootstrap container max-width 101 | // windowed is used to toggle between full-screen mode and container mode 102 | windowed: 1200, 103 | // padding of the list, if container < windowed what is the left-right padding of the list 104 | // during full-screen mode the padding will added to left & right to centralise the item 105 | padding: 24, 106 | }, 107 | responsive: [ 108 | // responsive breakpoints to calculate how many items to show in the list at each width interval 109 | // it will always fall back to these: 110 | { end: 576, size: 1 }, 111 | { start: 576, end: 768, size: 2 }, 112 | { start: 768, end: 992, size: 3 }, 113 | { start: 992, end: 1200, size: 4 }, 114 | { start: 1200, size: 5 }, 115 | ], 116 | navigation: { 117 | // when to show navigation 118 | start: 992, 119 | color: "#000", 120 | }, 121 | position: { 122 | // Start from '1' on mounted. 123 | start: 1, 124 | }, 125 | autoplay: { 126 | // enable/disable playing slideshow 127 | play: true, 128 | // the delay duration between slides in milliseconds 129 | speed: 1800, 130 | // if setup, the slideshow will be in the loop. 131 | repeat: true, 132 | }, 133 | }; 134 | ``` 135 | 136 | ## Advanced usage 137 | 138 | ```vue 139 | 169 | 170 | 209 | 210 | 232 | ``` 233 | 234 | ## Development 235 | 236 | ```shell script 237 | yarn # to setup 238 | yarn run examples:serve # to dev and test 239 | ``` 240 | 241 | ## Contributions 242 | 243 | For any question or feature request please feel free to create an [issue](https://github.com/fuxingloh/vue-horizontal-list/issues/new) or [pull request](https://github.com/fuxingloh/vue-horizontal-list/pulls). 244 | -------------------------------------------------------------------------------- /src/vue-horizontal-list.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 384 | 385 | 465 | --------------------------------------------------------------------------------