Link: https://fairshoppingio-production.up.railway.app/
I currently use Nuxt 3.4.2 along with Supabase. I have build a skeleton loading along with pagination, prior to this the Flowbite off-canvas drawer was working fine. However, since implementing the skeleton loading and pagination I am running into an issue where the data-drawer-target is not being recognised once the v-if has been satisfied.
Current error on Console
Drawer with id drawer-right-example has not been initialised. Please initialise it using the data-drawer-target attribute.
I may be going about the skeleton loading incorrectly as I am learning as I go.
Also having the same issue with the tooltip data attribute.
Index.vue
<meta name="viewport" content= "width=device-width, initial-scale=1.0">
<template>
<Header />
<!-- drawer init and toggle -->
<!-- drawer component -->
<div
id="drawer-right-example"
class="
fixed
top-0
right-0
z-40
h-full
p-4
overflow-y-auto
transition-transform
translate-x-full
bg-white
w-96
"
tabindex="-1"
aria-labelledby="drawer-right-label"
>
<button
type="button"
data-drawer-hide="drawer-right-example"
aria-controls="drawer-right-example"
class="
text-gray-400
bg-transparent
hover:bg-gray-200 hover:text-gray-900
rounded-lg
text-sm
p-1.5
absolute
top-2.5
right-2.5
inline-flex
items-center
dark:hover:bg-gray-600 dark:hover:text-white
"
>
<svg
aria-hidden="true"
class="w-5 h-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"
></path>
</svg>
<span class="sr-only">Close menu</span>
</button>
<LoadItem />
</div>
<!--End of Drawer-->
<div class="mx-auto max-w-7xl mt-5">
<ProductGrid />
</div>
<!--End Shadow-->
<Footer/>
</template>
<script setup lang="ts">
import { Drawer } from 'flowbite';
import { onMounted } from 'vue';
import {
initDrawers,
initTooltips,
initDropdowns,
} from 'flowbite';
// initialize components based on data attribute selectors
onMounted(() => {
initDrawers();
initTooltips();
initDropdowns();
document.documentElement.style.setProperty("--vh", window.innerHeight * 0.01 + 'px');
});
</script>
<style type="css">
.bg-gray-900 {
background-color: #111827;
opacity: 0.8;
}
.h-screen {
height: 100vh; /* Fallback for browsers that do not support Custom Properties */
height: calc(var(--vh, 1vh) * 100);
}
</style>
ProductGrid.vue
<template>
<section class="">
<div class="mx-auto">
<div
class="
grid grid-cols-1
gap-5
xs:grid-cols-1
sm:grid-cols-2
md:grid-cols-3
lg:grid-cols-4
xl:grid-cols-5
pb-5
pl-3
pr-3
"
>
<div
class="
relative
overflow-hidden
bg-white
border border-gray-200
rounded-md
group
"
v-if="returnData != null"
v-for="ProductData in returnData"
>
<div class="absolute z-10 left-3 top-3">
<button
type="button"
class="inline-flex items-center justify-center"
>
<template v-for="item in ProductData.category">
<div v-if="item" class="flex">
<div
v-if="item == 'Electronics'"
class="
rounded-full
ring-2 ring-gray-200
p-1.5
relative
bg-amber-500
"
:data-tooltip-target="
'tooltip-electronics-' + ProductData.id
"
>
<div
:id="'tooltip-electronics-' + ProductData.id"
role="tooltip"
class="
absolute
z-30
invisible
inline-block
px-3
py-2
text-sm
font-medium
text-white
transition-opacity
duration-300
bg-gray-900
rounded-lg
shadow-sm
opacity-0
tooltip
dark:bg-gray-700
"
>
Electronics
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
<div
v-if="item == 'Video Games'"
class="
rounded-full
ring-2 ring-gray-200
p-1.5
relative
bg-violet-600
"
:data-tooltip-target="
'tooltip-videogames-' + ProductData.id
"
>
<div
:id="'tooltip-videogames-' + ProductData.id"
role="tooltip"
class="
absolute
z-30
invisible
inline-block
px-3
py-2
text-sm
font-medium
text-white
transition-opacity
duration-300
bg-gray-900
rounded-lg
shadow-sm
opacity-0
tooltip
dark:bg-gray-700
whitespace-nowrap
"
>
Video Games
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
<div
v-if="item == 'Entertainment'"
class="
rounded-full
ring-2 ring-gray-200
p-1.5
relative
bg-cyan-600
"
:data-tooltip-target="
'tooltip-entertainment-' + ProductData.id
"
>
<div
:id="'tooltip-entertainment-' + ProductData.id"
role="tooltip"
class="
absolute
z-30
invisible
inline-block
px-3
py-2
text-sm
font-medium
text-white
transition-opacity
duration-300
bg-gray-900
rounded-lg
shadow-sm
opacity-0
tooltip
dark:bg-gray-700
"
>
Entertainment
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
<div
v-if="item == 'Smart Home'"
class="rounded-full ring-2 ring-gray-200 p-1.5 bg-rose-300"
:data-tooltip-target="'tooltip-smarthome-' + ProductData.id"
>
<div
:id="'tooltip-smarthome-' + ProductData.id"
role="tooltip"
class="
absolute
invisible
z-30
inline-block
px-3
py-2
text-sm
font-medium
text-white
transition-opacity
duration-300
bg-gray-800
rounded-lg
shadow-sm
opacity-0
tooltip
dark:bg-gray-700
whitespace-nowrap
"
>
Smart Home
<div class="tooltip-arrow" data-popper-arrow></div>
</div>
</div>
</div>
</template>
</button>
</div>
<div
role="button"
class="relative"
@click="
appStore.updatefinishLoading(0),
appStore.updateStoreID(ProductData.id)
"
data-drawer-target="drawer-right-example"
data-drawer-show="drawer-right-example"
data-drawer-placement="right"
aria-controls="drawer-right-example"
>
<div class="aspect-w-1 aspect-h-1">
<img
class="
object-scale-down
w-full
h-full
top-5
relative
content-center
"
:src="
ProductData.Image + '&tr=h-160,w-160,cm-pad_resize,bg-fff'
"
alt=""
/>
</div>
<div class="px-4 py-4">
<span
class="
text-md
font-semibold
tracking-tight
text-gray-900
truncate
block
"
>
{{ ProductData.product_name }}
<span class="absolute inset-0" aria-hidden="true"></span>
</span>
<div class="flex items-center -ml-0.5 mt-1">
<svg
aria-hidden="true"
class="w-4 h-5 text-yellow-400"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<title>Rating star</title>
<path
d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"
></path>
</svg>
<template v-if="ProductData.current_rating == null">
<p class="ml-1 text-sm font-bold text-gray-900">0</p>
<span
class="
bg-blue-100
text-blue-800 text-xs
font-semibold
mr-2
px-2.5
py-0.5
rounded
dark:bg-blue-200 dark:text-blue-800
ml-3
"
>
N/A
</span>
</template>
<template v-else>
<p class="ml-1 text-sm font-bold text-gray-900">
{{ ProductData.current_rating }}
</p>
<span
class="
bg-blue-100
text-blue-800 text-xs
font-semibold
mr-2
px-2.5
py-0.5
rounded
dark:bg-blue-200 dark:text-blue-800
ml-3
"
>
{{ ProductData.current_reviews }}
</span>
</template>
</div>
<p class="mt-1.5">
<span
class="text-md font-semibold tracking-tight text-gray-900"
>
<span
v-if="
ProductData.current_price !== null &&
ProductData.current_price < ProductData.previous_price_day
"
>
<!--Show Previous price-->
<span class="line-through">
{{
Number(ProductData.previous_price_day).toLocaleString(
'en-US',
{
style: 'currency',
currency: 'USD',
}
)
}}
</span>
<!-- End of Previous Price-->
<!--Show Current Price-->
<span class="text-green-600 pl-1">
{{
Number(ProductData.current_price).toLocaleString(
'en-US',
{
style: 'currency',
currency: 'USD',
}
)
}}
</span>
<!--End of Current Price-->
</span>
<span v-else>
{{
Number(ProductData.current_price).toLocaleString(
'en-US',
{
style: 'currency',
currency: 'USD',
}
)
}}
</span>
</span>
</p>
</div>
</div>
</div>
<!--Before Data is loaded we are showing this-->
<template v-if="returnData == null" v-for="n in 15">
<div
class="
relative
overflow-hidden
bg-white
border border-gray-200
rounded-md
group
"
>
<div class="absolute z-10 left-3 top-0">
<button
type="button"
class="inline-flex items-center justify-center"
>
<div role="status" class="max-w-sm animate-pulse">
<div class="h-2.5 bg-gray-200 rounded-full w-12"></div>
<span class="sr-only">Loading...</span>
</div>
</button>
</div>
<div>
<div class="aspect-w-1 aspect-h-1 mt-1">
<div
role="status"
class="
animate-pulse
space-y-0 space-x-8
flex
items-center
md:h-[19.9rem]
lg:h-[19rem]
sm:h-[22.4rem]
h-[38rem]
p-3
"
>
<div
class="
flex
items-center
justify-center
w-full
bg-gray-300
rounded
sm:w-96
object-scale-down
w-full
h-full
top-5
relative
content-center
"
>
<svg
class="w-12 h-12 text-gray-200"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
fill="currentColor"
viewBox="0 0 640 512"
>
<path
d="M480 80C480 35.82 515.8 0 560 0C604.2 0 640 35.82 640 80C640 124.2 604.2 160 560 160C515.8 160 480 124.2 480 80zM0 456.1C0 445.6 2.964 435.3 8.551 426.4L225.3 81.01C231.9 70.42 243.5 64 256 64C268.5 64 280.1 70.42 286.8 81.01L412.7 281.7L460.9 202.7C464.1 196.1 472.2 192 480 192C487.8 192 495 196.1 499.1 202.7L631.1 419.1C636.9 428.6 640 439.7 640 450.9C640 484.6 612.6 512 578.9 512H55.91C25.03 512 .0006 486.1 .0006 456.1L0 456.1z"
/>
</svg>
</div>
<span class="sr-only">Loading...</span>
</div>
</div>
<div class="px-4 py-4">
<span
class="
text-md
font-semibold
tracking-tight
text-gray-900
truncate
block
"
>
<div role="status" class="max-w-sm animate-pulse">
<div class="h-2.5 bg-gray-200 rounded-full w-48 mt-5"></div>
<span class="sr-only">Loading...</span>
</div>
<span class="absolute inset-0" aria-hidden="true"></span>
</span>
<div class="flex items-center mt-1">
<div role="status" class="max-w-sm animate-pulse">
<div class="h-2.5 bg-gray-200 rounded-full w-12"></div>
<span class="sr-only">Loading...</span>
</div>
<p class="ml-1 text-sm font-bold text-gray-900"></p>
<div role="status" class="max-w-sm animate-pulse">
<div class="h-2.5 bg-gray-200 rounded-full w-12"></div>
<span class="sr-only">Loading...</span>
</div>
</div>
<p class="">
<span
class="text-md font-semibold tracking-tight text-gray-900"
>
<div role="status" class="max-w-sm animate-pulse">
<div
class="h-2.5 bg-gray-200 rounded-full w-48 mt-1"
></div>
<span class="sr-only">Loading...</span>
</div>
</span>
</p>
</div>
</div>
</div>
</template>
<!--end of before data-->
<!--Before Data is loaded we are showing this-->
<template v-if="load == true" v-for="n in 5">
<div
class="
relative
overflow-hidden
bg-white
border border-gray-200
rounded-md
group
"
>
<div class="absolute z-10 left-3 top-0">
<button
type="button"
class="inline-flex items-center justify-center"
>
<div role="status" class="max-w-sm animate-pulse">
<div class="h-2.5 bg-gray-200 rounded-full w-12"></div>
<span class="sr-only">Loading...</span>
</div>
</button>
</div>
<div>
<div class="aspect-w-1 aspect-h-1 mt-1">
<div
role="status"
class="
animate-pulse
space-y-0 space-x-8
flex
items-center
md:h-[19.9rem]
lg:h-[19rem]
sm:h-[22.4rem]
h-[38rem]
p-3
"
>
<div
class="
flex
items-center
justify-center
w-full
bg-gray-300
rounded
sm:w-96
object-scale-down
w-full
h-full
top-5
relative
content-center
"
>
<svg
class="w-12 h-12 text-gray-200"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
fill="currentColor"
viewBox="0 0 640 512"
>
<path
d="M480 80C480 35.82 515.8 0 560 0C604.2 0 640 35.82 640 80C640 124.2 604.2 160 560 160C515.8 160 480 124.2 480 80zM0 456.1C0 445.6 2.964 435.3 8.551 426.4L225.3 81.01C231.9 70.42 243.5 64 256 64C268.5 64 280.1 70.42 286.8 81.01L412.7 281.7L460.9 202.7C464.1 196.1 472.2 192 480 192C487.8 192 495 196.1 499.1 202.7L631.1 419.1C636.9 428.6 640 439.7 640 450.9C640 484.6 612.6 512 578.9 512H55.91C25.03 512 .0006 486.1 .0006 456.1L0 456.1z"
/>
</svg>
</div>
<span class="sr-only">Loading...</span>
</div>
</div>
<div class="px-4 py-4">
<span
class="
text-md
font-semibold
tracking-tight
text-gray-900
truncate
block
"
>
<div role="status" class="max-w-sm animate-pulse">
<div class="h-2.5 bg-gray-200 rounded-full w-48 mt-5"></div>
<span class="sr-only">Loading...</span>
</div>
<span class="absolute inset-0" aria-hidden="true"></span>
</span>
<div class="flex items-center mt-1">
<div role="status" class="max-w-sm animate-pulse">
<div class="h-2.5 bg-gray-200 rounded-full w-12"></div>
<span class="sr-only">Loading...</span>
</div>
<p class="ml-1 text-sm font-bold text-gray-900"></p>
<div role="status" class="max-w-sm animate-pulse">
<div class="h-2.5 bg-gray-200 rounded-full w-12"></div>
<span class="sr-only">Loading...</span>
</div>
</div>
<p class="">
<span
class="text-md font-semibold tracking-tight text-gray-900"
>
<div role="status" class="max-w-sm animate-pulse">
<div
class="h-2.5 bg-gray-200 rounded-full w-48 mt-1"
></div>
<span class="sr-only">Loading...</span>
</div>
</span>
</p>
</div>
</div>
</div>
</template>
<!--end of before data-->
</div>
<!--Show more Button-->
<div
v-if="returnData && end == false"
class="inline-flex items-center justify-center w-full"
>
<hr class="w-2/5 h-px my-8 bg-gray-200 border-0" />
<span
class="
absolute
px-6
font-medium
text-gray-900
-translate-x-1/2
left-1/2
bg-slate-50
capitalize
"
>
<button
@click="supaPagination"
type="button"
class="
text-white
bg-blue-700
hover:bg-blue-800
focus:ring-4 focus:ring-blue-300
font-medium
rounded-lg
text-sm
px-5
py-2.5
text-center
mr-2
dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
inline-flex
items-center
"
>
<svg
aria-hidden="true"
role="status"
class="inline w-4 h-4 mr-2 text-white animate-spin"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="#E5E7EB"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentColor"
/>
</svg>
Show More Products {{ load }}
</button>
</span>
</div>
</div>
</section>
</template>
<script setup>
//import Store Data
import { useAppStore } from '~/store/app';
import { initTooltips, initDropdowns, initDrawers } from 'flowbite';
const supabase = useSupabaseClient();
const to = ref(14);
const load = ref(false);
const supaPagination = () => ((to.value = to.value + 5), (load.value = true));
const returnData = ref(null);
const end = ref(false);
//we are fetching count for rows in database
const { count } = await supabase
.from('productinfo1')
.select('*', { count: 'exact' });
watch(
() => to.value,
async () => {
// added async keyword here
if (to.value && to.value <= count) {
const { data, error } = await supabase
.from('productinfo1')
.select('*', { count: 'exact' })
.range(0, to.value);
if (to.value == 14) {
setTimeout(function () {
returnData.value = data;
load.value = false;
}, 500);
} else {
load.value = true;
setTimeout(function () {
returnData.value = data;
load.value = false;
}, 800);
}
//we are using to.value + 1 to set the value end.value = true once the last item has loaded
if (to.value + 1 >= count) {
//we are emting this to let the DOM know there are no more items to load.
end.value = true;
}
}
},
{ immediate: true }
);
//set Store as a Constant
const appStore = useAppStore();
watch(
() => appStore.storeID,
() => {
// added async keyword here
if (appStore.storeID > 0) {
appStore.updateDataID(0);
}
}
);
</script>