Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement additional slots #34

Merged
merged 2 commits into from
Nov 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 114 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,80 +1,116 @@
# vue-horizontal-list
A pure vue horizontal list implementation with minimal dependencies, ssr support, mobile friendly, touch friendly and responsive.

A pure vue horizontal list implementation with minimal dependencies, ssr support, mobile friendly, touch friendly and responsive.
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.

Check it out: [LIVE DEMO](https://nuxt-app.now.sh/vue-horizontal-list),
Source is here: [source code](https://github.com/fuxingloh/nuxt-app/blob/master/components/examples/ExampleHorizontalList.vue).

[![vue-horizontal-list screenshot](demo.png)](https://nuxt-app.now.sh/vue-horizontal-list)


## Installation

```shell script
npm i vue-horizontal-list
# or
yarn add vue-horizontal-list
```

## Features
* Lightweight implementation with 1 dependencies.
* SSR supported
* Mobile touch screen friendly
* Invisible scroll bar for consistent Windows and MacOS browsing experience.
* Snap to the nearest item in the horizontal-list when scrolling.
* Windowed & Full-screen mode
* The windowed mode will respect the container and not show overflowing item
* Full-screen mode will show overflowing item, best result for small screen
* Dynamic responsive breakpoint configuration
* Navigation control will show up dynamically for larger screen
* Touch screen friendly
* Minimal config setup
* Tested on chrome, edge and safari

- Lightweight implementation with 1 dependencies.
- SSR supported
- Mobile touch screen friendly
- Invisible scroll bar for consistent Windows and MacOS browsing experience.
- Snap to the nearest item in the horizontal-list when scrolling.
- Windowed & Full-screen mode
- The windowed mode will respect the container and not show overflowing item
- Full-screen mode will show overflowing item, best result for small screen
- Dynamic responsive breakpoint configuration
- Navigation control will show up dynamically for larger screen
- Touch screen friendly
- Let's get slideshow autoplayed
- Slot different content at the beginning or the ending of the items list.
- Minimal config setup
- Tested on chrome, edge and safari

## Options

```js
const options = {
item: {
// css class to inject into each individual item
class: '',
// padding between each item
padding: 12
padding: 12,
},
list: {
// css class for the parent of item
class: '',
class: '',
// maximum width of the list it can extend to before switching to windowed mode, basically think of the bootstrap container max-width
// windowed is used to toggle between full-screen mode and container mode
windowed: 1200,
// padding of the list, if container < windowed what is the left-right padding of the list
// during full-screen mode the padding will added to left & right to centralise the item
padding: 24
padding: 24,
},
responsive: [
// responsive breakpoints to calculate how many items to show in the list at each width interval
// it will always fall back to these:
{end: 576, size: 1},
{start: 576, end: 768, size: 2},
{start: 768, end: 992, size: 3},
{start: 992, end: 1200, size: 4},
{start: 1200, size: 5}
// it will always fall back to these:
{ end: 576, size: 1 },
{ start: 576, end: 768, size: 2 },
{ start: 768, end: 992, size: 3 },
{ start: 992, end: 1200, size: 4 },
{ start: 1200, size: 5 },
],
navigation: {
// when to show navigation
start: 992,
color: '#000'
}
}
color: '#000',
},
autoplay: {
// enable/disable playing slideshow
play: true,
// the start index of the item when autoplay begins
startOnIndex: 2,
// the delay duration between slides in milliseconds
speed: 1800,
// if setup, the slideshow will be in the loop.
repeat: true,
},
};
```

## Examples

### Basic Responsive Usage

- Width between 0 - 576, show 1
- Width between 576 - 768, show 2
- Width fallback, show 3

### Slotting

- default slot of the items is tagged `v-slot:default`, is required.
- additional optional slots could be binding via `v-slot:start` or `v-slot:end`

```vue
<vue-horizontal-list :items="items" :options="{responsive: [{end: 576, size: 1}, {start: 576, end: 768, size: 2},{size: 3}]}">
<vue-horizontal-list
:items="items"
:options="{
responsive: [
{ end: 576, size: 1 },
{ start: 576, end: 768, size: 2 },
{ size: 3 },
],
}"
>
<template v-slot:start>
<div class="start-item" style="background:red;">
<p>Start item</p>
</div>
</template>

<template v-slot:default="{item}">
<div class="item">
<h5>{{item.title}}</h5>
Expand All @@ -85,15 +121,16 @@ const options = {
```

### Full Example

```vue
<template>
<div id="app">
<section>
<vue-horizontal-list :items="items" :options="options">
<template v-slot:default="{item}">
<template v-slot:default="{ item }">
<div class="item">
<h5>{{item.title}}</h5>
<p>{{item.content}}</p>
<h5>{{ item.title }}</h5>
<p>{{ item.content }}</p>
</div>
</template>
</vue-horizontal-list>
Expand All @@ -102,62 +139,62 @@ const options = {
</template>

<script>
import Vue from 'vue';
import VueHorizontalList from '@/vue-horizontal-list.vue';

export default Vue.extend({
name: 'ServeDev',
components: {
VueHorizontalList
},
data() {
return {
options: {
responsive: [
{end: 576, size: 1},
{start: 576, end: 768, size: 2},
{start: 768, end: 992, size: 3},
{size: 4}
],
list: {
// 1200 because @media (min-width: 1200px) and therefore I want to switch to windowed mode
windowed: 1200,

// Because: #app {padding: 80px 24px;}
padding: 24
}
},
items: [
{title: 'Item 0', content: 'Content item with description'},
]
}
}
});
import Vue from 'vue';
import VueHorizontalList from '@/vue-horizontal-list.vue';

export default Vue.extend({
name: 'ServeDev',
components: {
VueHorizontalList,
},
data() {
return {
options: {
responsive: [
{ end: 576, size: 1 },
{ start: 576, end: 768, size: 2 },
{ start: 768, end: 992, size: 3 },
{ size: 4 },
],
list: {
// 1200 because @media (min-width: 1200px) and therefore I want to switch to windowed mode
windowed: 1200,

// Because: #app {padding: 80px 24px;}
padding: 24,
},
autoplay: { play: true, repeat: true }, // 2 seconds delay is by default
},
items: [{ title: 'Item 0', content: 'Content item with description' }],
};
},
});
</script>

<style>
body {
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 0;
}

#app {
max-width: 1400px;
#app {
max-width: 1400px;

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

padding: 80px 24px;
}
padding: 80px 24px;
}

@media (min-width: 1200px) {
#app {
padding-left: 80px;
padding-right: 80px;
}
@media (min-width: 1200px) {
#app {
padding-left: 80px;
padding-right: 80px;
}
}
</style>
```

## Contributing

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).
85 changes: 84 additions & 1 deletion dev/serve.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,78 @@
</template>
</vue-horizontal-list>
</section>

<section>
<vue-horizontal-list :items="items"
:options="{responsive: [{end: 576, size: 1}, {start: 576, end: 768, size: 2},{size: 3}]}">

<template v-slot:start>
<div class="start-item">
<div class="icon">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-new-section" width="68" height="68" viewBox="0 0 24 24" stroke-width="1.5" stroke="#a0aec0" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="9" y1="12" x2="15" y2="12" />
<line x1="12" y1="9" x2="12" y2="15" />
<path d="M4 6v-1a1 1 0 0 1 1 -1h1m5 0h2m5 0h1a1 1 0 0 1 1 1v1m0 5v2m0 5v1a1 1 0 0 1 -1 1h-1m-5 0h-2m-5 0h-1a1 1 0 0 1 -1 -1v-1m0 -5v-2m0 -5" />
</svg>
</div>
</div>
</template>
<template v-slot:default="{item}">
<div class="item">
<h5>{{item.title}}</h5>
<p>{{item.content}}</p>
</div>
</template>
<template v-slot:end>
<div class="start-item">
<div class="icon">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-shield-check" width="68" height="68" viewBox="0 0 24 24" stroke-width="1.5" stroke="#a0aec0" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 12l2 2l4 -4" />
<path d="M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1 -8.5 15a12 12 0 0 1 -8.5 -15a12 12 0 0 0 8.5 -3" />
</svg>
</div>
</div>
</template>
</vue-horizontal-list>
</section>

<section>
<vue-horizontal-list :items="items"
:options="{responsive: [{end: 576, size: 1}, {start: 576, end: 768, size: 2},{size: 3}], autoplay: { play: true, repeat: true, speed: 1200}}">

<template v-slot:start>
<div class="start-item">
<div class="icon">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-new-section" width="68" height="68" viewBox="0 0 24 24" stroke-width="1.5" stroke="#a0aec0" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="9" y1="12" x2="15" y2="12" />
<line x1="12" y1="9" x2="12" y2="15" />
<path d="M4 6v-1a1 1 0 0 1 1 -1h1m5 0h2m5 0h1a1 1 0 0 1 1 1v1m0 5v2m0 5v1a1 1 0 0 1 -1 1h-1m-5 0h-2m-5 0h-1a1 1 0 0 1 -1 -1v-1m0 -5v-2m0 -5" />
</svg>
</div>
</div>
</template>
<template v-slot:default="{item}">
<div class="item">
<h5>{{item.title}}</h5>
<p>{{item.content}}</p>
</div>
</template>
<template v-slot:end>
<div class="start-item">
<div class="icon">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-shield-check" width="68" height="68" viewBox="0 0 24 24" stroke-width="1.5" stroke="#a0aec0" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M9 12l2 2l4 -4" />
<path d="M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1 -8.5 15a12 12 0 0 1 -8.5 -15a12 12 0 0 0 8.5 -3" />
</svg>
</div>
</div>
</template>
</vue-horizontal-list>
</section>

<section>
<vue-horizontal-list :items="items"
Expand Down Expand Up @@ -144,7 +216,18 @@

word-break: break-word;
}

.start-item {
display: flex;
justify-content: center;
align-items: center;
border-radius: 3px;
background: #ffe6e6;
height: 92px;
}
.icon {
display: flex;
justify-content: center;
}
.item {
padding: 16px 24px;
border-radius: 3px;
Expand Down
Loading