JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar@^1.0.0-beta.0/dist/quasar.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/swiper@4.0.7/dist/js/swiper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-awesome-swiper@3.1.2/dist/vue-awesome-swiper.js"></script>
<link href="https://fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet" type="text/css" />
<link href="https://cdn.jsdelivr.net/npm/quasar@^1.0.0-beta.0/dist/quasar.min.css" rel="stylesheet" type="text/css" />
<link href="https://cdn.jsdelivr.net/npm/swiper@4.0.7/dist/css/swiper.min.css" rel="stylesheet">
<div id="app" class="q-pa-sm">
<q-layout>
<q-header>
<q-toolbar>
<q-btn icon="menu" flat></q-btn>
<q-toolbar-title>Services</q-toolbar-title>
</q-toolbar>
</q-header>
<q-page-container>
<q-page>
<swiper :options="swiperOption" class="swipeFullWrap">
<swiper-slide class="swipeFull">
<q-table row-key="id" :data="items" :columns="columns" class="oTable">
<template v-slot:body="props">
<q-tr
:props="props"
@mouseenter.native="props.row.hover = true"
@mouseleave.native="props.row.hover = false"
>
<q-td v-for="{ name } in columns" :key="name" :props="props">
{{ props.row[name] }}
</q-td>
</q-tr>
</template>
</q-table>
</swiper-slide>
</swiper>
</q-page>
</q-page-container>
</q-layout>
</div>
CSS
.swipeFull {
width: fit-content;
min-width: 100%;
}
.swiper-container.swipeFullWrap > .swiper-wrapper {
width: 100%;
}
.swipe-container {
display: block;
position: relative;
width: fit-content;
min-width: 100%;
}
.oTable {
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
min-width: 100%;
max-width: -webkit-fit-content;
max-width: -moz-fit-content;
max-width: fit-content;
}
JavaScript
Vue.use(VueAwesomeSwiper);
new Vue({
el: '#app',
data: () => ({
columns: [],
items: [],
swiperOption: {
direction: 'horizontal',
slidesPerView: 'auto',
virtualTranslate: true,
on: {
touchEnd() {
this.LEFT = parseInt(this.$wrapperEl.css('left'));
},
setTranslate(translate) {
if (!translate) {
return;
}
const $w = this.$wrapperEl;
const max = $w[0].offsetWidth - $w.find('.q-table__container')[0].offsetWidth;
const left = Math.max(max, Math.min(0, (this.LEFT || 0) + translate));
$w.css('left', `${left}px`);
},
},
},
}),
created() {
const N = 30;
this.columns = [
{ name: 'id', label: 'Id', align: 'right', type: 'number' },
{ name: 'descr', label: 'Description', align: 'left', type: 'text' },
{ name: 'start_date', label: 'Start date', align: 'left', type: 'date' },
...[...Array(N)].map((n, i) => ({
name: `notes${i}`,
label: `Notes${i}`,
align: 'left',
type: 'text',
})),
];
this.items = [
{ descr: 'Plumbing', start_date: '2019-03-09', notes: 'Libero non voluptas est' },
{ descr: 'Electric', start_date: '2019-04-19', notes: 'Vel exercitationem nam cumque', },
{ descr: 'Freight', start_date: '2019-05-17', notes: 'Quisquam molestiae in dicta dolores' },
].map((n, i) => ({
...n,
id: i + 1,
...[...Array(N)].reduce((acc, n, i) => (acc[`notes${i}`] = `something${i}`, acc), {}),
}));
},
});