create datatable component
This commit is contained in:
86
resources/js/Components/DataTable/Builder.vue
Normal file
86
resources/js/Components/DataTable/Builder.vue
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
|
import axios from 'axios'
|
||||||
|
import Swal from 'sweetalert2'
|
||||||
|
import { getCurrentInstance, onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const { url, sticky } = defineProps({
|
||||||
|
url: String,
|
||||||
|
sticky: Boolean,
|
||||||
|
})
|
||||||
|
|
||||||
|
const paginator = ref({})
|
||||||
|
const last = ref(null)
|
||||||
|
const config = useForm({
|
||||||
|
page: 1,
|
||||||
|
search: '',
|
||||||
|
per_page: 10,
|
||||||
|
order: {
|
||||||
|
key: '',
|
||||||
|
dir: 'asc',
|
||||||
|
},
|
||||||
|
sticky,
|
||||||
|
})
|
||||||
|
|
||||||
|
const fetch = async u => {
|
||||||
|
try {
|
||||||
|
const a = last.value = u || url
|
||||||
|
const response = await axios.post(a || last.value, config.data())
|
||||||
|
paginator.value = response.data
|
||||||
|
} catch (e) {
|
||||||
|
const respose = await Swal.fire({
|
||||||
|
title: 'error',
|
||||||
|
text: `${e}`,
|
||||||
|
icon: 'error',
|
||||||
|
showCancelButton: true,
|
||||||
|
showCloseButton: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (respose.isConfirmed) {
|
||||||
|
return fetch(last.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(fetch)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col w-full">
|
||||||
|
<div class="flex flex-col sm:flex-row items-center sm:justify-between space-y-2 sm:space-y-0 sm:space-x-2 p-2">
|
||||||
|
<div class="w-full sm:max-w-xs flex items-center space-x-2">
|
||||||
|
<label for="per_page" class="w-1/4 sm:w-auto lowercase first-letter:capitalize">per page</label>
|
||||||
|
<select name="per_page" v-model="config.per_page" @change.prevent="fetch()" class="w-full sm:w-auto bg-transparent border dark:border-gray-600 rounded-md px-3 py-1">
|
||||||
|
<option value="10">10</option>
|
||||||
|
<option value="15">15</option>
|
||||||
|
<option value="25">25</option>
|
||||||
|
<option value="50">50</option>
|
||||||
|
<option value="100">100</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full sm:max-w-sm flex items-center space-x-2 sm:justify-end">
|
||||||
|
<label for="search" class="w-1/4 sm:w-auto lowercase first-letter:capitalize">search</label>
|
||||||
|
<input v-model="config.search" type="search" name="search" class="w-full bg-transparent border dark border-gray-600 rounded-md px-3 py-1 placeholder:capitalize" placeholder="search">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-2">
|
||||||
|
<div class="overflow-auto rounded-md border dark:border-gray-900">
|
||||||
|
<table class="w-full border-collapse">
|
||||||
|
<thead class="border-inherit">
|
||||||
|
<slot name="thead" :config="config" :paginator="paginator" :data="paginator.data" :refresh="fetch" />
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tfoot class="border-inherit">
|
||||||
|
<slot name="tfoot" :config="config" :paginator="paginator" :data="paginator.data" :refresh="fetch" />
|
||||||
|
</tfoot>
|
||||||
|
|
||||||
|
<tbody class="border-inherit">
|
||||||
|
<slot name="tbody" :config="config" :paginator="paginator" :data="paginator.data" :refresh="fetch" />
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
80
resources/js/Components/DataTable/Th.vue
Normal file
80
resources/js/Components/DataTable/Th.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<script setup>
|
||||||
|
import { getCurrentInstance, onMounted, ref } from 'vue'
|
||||||
|
import Icon from '@/Components/Icon.vue'
|
||||||
|
|
||||||
|
const self = getCurrentInstance()
|
||||||
|
const { table, sort, name } = defineProps({
|
||||||
|
table: Object,
|
||||||
|
sort: Boolean,
|
||||||
|
name: String,
|
||||||
|
class: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
const current = ref(table && table.config && name === table.config.order.key)
|
||||||
|
const asc = ref(current.value && table.config.order.dir === 'asc')
|
||||||
|
const desc = ref(current.value && table.config.order.dir === 'desc')
|
||||||
|
|
||||||
|
const click = () => {
|
||||||
|
if (current.value) {
|
||||||
|
const dir = table.config.order.dir = table.config.order.dir === 'asc' ? 'desc' : 'asc'
|
||||||
|
asc.value = dir === 'asc'
|
||||||
|
desc.value = dir === 'desc'
|
||||||
|
} else {
|
||||||
|
table.config.order.key = name
|
||||||
|
table.config.order.dir = 'asc'
|
||||||
|
current.value = true
|
||||||
|
asc.value = true
|
||||||
|
desc.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
table.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
const floating = () => {
|
||||||
|
if (table && table.config.sticky) {
|
||||||
|
const { th, float } = self.refs
|
||||||
|
|
||||||
|
if (th && float) {
|
||||||
|
float.style.width = th.clientWidth + 'px'
|
||||||
|
float.style.height = th.clientHeight + 'px'
|
||||||
|
|
||||||
|
const parent = th.parentElement
|
||||||
|
|
||||||
|
if (parent.firstElementChild == th) {
|
||||||
|
float.classList.add('rounded-tl-md')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent.children[parent.children.length - 1] === th) {
|
||||||
|
float.classList.add('rounded-tr-md')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(floating)
|
||||||
|
onMounted(() => {
|
||||||
|
const main = document.getElementById('main')
|
||||||
|
|
||||||
|
main.addEventListener('scroll', e => floating())
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<th ref="th" class="relative uppercase font-semibold" :class="`${table?.config?.sticky && 'sticky top-0 left-0'} ${sort && 'cursor-pointer'} ${$props.class}`" @click.prevent="sort && table && click()">
|
||||||
|
<div class="flex items-center justify-center space-x-2">
|
||||||
|
<div>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="table && sort">
|
||||||
|
<div class="flex flex-col items-center justify-center text-xs">
|
||||||
|
<Icon name="caret-up" class="transition-all duration-300" :class="asc ? 'dark:text-white' : 'dark:text-gray-400'" />
|
||||||
|
<Icon name="caret-down" class="transition-all duration-300" :class="desc ? 'dark:text-white' : 'dark:text-gray-400'" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ref="float" v-if="table?.config?.sticky" class="absolute top-0 left-0" :class="`${sort && 'cursor-pointer'} ${$props.class} bg-transparent dark:bg-transparent`">
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user