Use searchbar and debouncer
This commit is contained in:
parent
72cd9e6cd6
commit
e39cab82f6
|
@ -12,7 +12,7 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'Search'
|
default: ''
|
||||||
},
|
},
|
||||||
sqlQuery: {
|
sqlQuery: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
|
@ -1,25 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||||
<!-- <QInput
|
<div class="q-gutter-x-sm row">
|
||||||
:placeholder="$t('search')"
|
<VnSearchBar :search-term="search" @on-search-error="items = []" />
|
||||||
v-model="search"
|
|
||||||
debounce="500"
|
|
||||||
class="search q-mr-sm"
|
|
||||||
rounded
|
|
||||||
dark
|
|
||||||
dense
|
|
||||||
standout
|
|
||||||
>
|
|
||||||
<template #prepend>
|
|
||||||
<QIcon v-if="search === ''" name="search" />
|
|
||||||
<QIcon
|
|
||||||
v-else
|
|
||||||
name="clear"
|
|
||||||
class="cursor-pointer"
|
|
||||||
@click="search = ''"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</QInput> -->
|
|
||||||
<QBtn
|
<QBtn
|
||||||
:icon="viewTypeButtonContent.icon"
|
:icon="viewTypeButtonContent.icon"
|
||||||
:label="viewTypeButtonContent.label"
|
:label="viewTypeButtonContent.label"
|
||||||
|
@ -42,6 +24,7 @@
|
||||||
{{ t('shoppingCart') }}
|
{{ t('shoppingCart') }}
|
||||||
</QTooltip>
|
</QTooltip>
|
||||||
</QBtn>
|
</QBtn>
|
||||||
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<div style="padding-bottom: 5em">
|
<div style="padding-bottom: 5em">
|
||||||
<QDrawer v-model="rightDrawerOpen" side="right" :width="250">
|
<QDrawer v-model="rightDrawerOpen" side="right" :width="250">
|
||||||
|
@ -96,7 +79,7 @@
|
||||||
</QBtn>
|
</QBtn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="q-mt-md" v-if="category || search">
|
<div class="q-mt-md" v-if="selectedCategory">
|
||||||
<div class="q-mb-xs text-grey-7">
|
<div class="q-mb-xs text-grey-7">
|
||||||
{{ t('filterBy') }}
|
{{ t('filterBy') }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -157,69 +140,6 @@
|
||||||
Elige una categoria
|
Elige una categoria
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="q-pa-md" v-if="typeId || search">
|
|
||||||
<div class="q-mb-md" v-for="tag in tags" :key="tag.uid">
|
|
||||||
<div class="q-mb-xs text-caption text-grey-7">
|
|
||||||
{{ tag.name }}
|
|
||||||
<QIcon
|
|
||||||
v-if="tag.hasFilter"
|
|
||||||
style="font-size: 1.3em"
|
|
||||||
name="cancel"
|
|
||||||
:title="$t('deleteFilter')"
|
|
||||||
class="cursor-pointer"
|
|
||||||
@click="onResetTagFilterClick(tag)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="!tag.useRange">
|
|
||||||
<div
|
|
||||||
v-for="value in tag.values.slice(0, tag.showCount)"
|
|
||||||
:key="value"
|
|
||||||
>
|
|
||||||
<QCheckbox
|
|
||||||
v-model="tag.filter"
|
|
||||||
:dense="true"
|
|
||||||
:val="value"
|
|
||||||
:label="value"
|
|
||||||
@input="onCheck(tag)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="tag.values.length > tag.showCount">
|
|
||||||
<span
|
|
||||||
class="cursor-pointer text-blue"
|
|
||||||
@click="tag.showCount = Infinity"
|
|
||||||
>
|
|
||||||
<QIcon name="keyboard_arrow_down" />
|
|
||||||
{{ $t('viewMore') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="tag.showCount == Infinity">
|
|
||||||
<span
|
|
||||||
class="cursor-pointer text-blue"
|
|
||||||
@click="tag.showCount = tag.initialCount"
|
|
||||||
>
|
|
||||||
<QIcon name="keyboard_arrow_up" />
|
|
||||||
{{ $t('viewLess') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="q-mx-md">
|
|
||||||
<QRange
|
|
||||||
class="q-mt-lg"
|
|
||||||
v-if="tag.useRange"
|
|
||||||
v-model="tag.filter"
|
|
||||||
:min="tag.min"
|
|
||||||
:max="tag.max"
|
|
||||||
:step="tag.step"
|
|
||||||
:color="tag.hasFilter ? 'primary' : 'grey-6'"
|
|
||||||
@input="onRangeChange(tag, true)"
|
|
||||||
@change="onRangeChange(tag)"
|
|
||||||
label-always
|
|
||||||
markers
|
|
||||||
snap
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
</QDrawer>
|
</QDrawer>
|
||||||
<QInfiniteScroll>
|
<QInfiniteScroll>
|
||||||
<div
|
<div
|
||||||
|
@ -342,17 +262,19 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { inject, onBeforeMount, ref, computed } from 'vue';
|
import { inject, onBeforeMount, ref, computed, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import VnImg from 'src/components/ui/VnImg.vue';
|
import VnImg from 'src/components/ui/VnImg.vue';
|
||||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
import CatalogCard from 'src/pages/Ecomerce/CatalogCard.vue';
|
import CatalogCard from 'src/pages/Ecomerce/CatalogCard.vue';
|
||||||
|
import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
|
||||||
|
|
||||||
import { useAppStore } from 'stores/app';
|
import { useAppStore } from 'stores/app';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { formatDateTitle, currency } from 'src/lib/filters.js';
|
import { formatDateTitle, currency } from 'src/lib/filters.js';
|
||||||
import useNotify from 'src/composables/useNotify.js';
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import debounce from 'src/utils/debouncer.js';
|
||||||
|
|
||||||
const jApi = inject('jApi');
|
const jApi = inject('jApi');
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -378,6 +300,7 @@ const itemFamilies = ref([]);
|
||||||
const itemProducers = ref([]);
|
const itemProducers = ref([]);
|
||||||
const itemOrigins = ref([]);
|
const itemOrigins = ref([]);
|
||||||
const itemSubcategories = ref([]);
|
const itemSubcategories = ref([]);
|
||||||
|
const search = ref(null);
|
||||||
// Filters values
|
// Filters values
|
||||||
const category = ref(null);
|
const category = ref(null);
|
||||||
const type = ref(null);
|
const type = ref(null);
|
||||||
|
@ -437,11 +360,10 @@ const selectedCategory = computed({
|
||||||
},
|
},
|
||||||
async set(value) {
|
async set(value) {
|
||||||
category.value = value;
|
category.value = value;
|
||||||
router.push({ params: { category: category.value, type: null } });
|
|
||||||
onCategoryChange();
|
onCategoryChange();
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
getFilters();
|
debouncedGetFilters();
|
||||||
getItems();
|
debouncedGetItems();
|
||||||
refreshTitle();
|
refreshTitle();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -452,10 +374,13 @@ const selectedType = computed({
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
type.value = value;
|
type.value = value;
|
||||||
router.push({ params: { category: category.value, type: type.value } });
|
router.push({
|
||||||
|
params: { category: category.value, type: type.value },
|
||||||
|
query: { ...route.query }
|
||||||
|
});
|
||||||
if (!selectedCategory.value) return;
|
if (!selectedCategory.value) return;
|
||||||
getFilters();
|
debouncedGetFilters();
|
||||||
getItems();
|
debouncedGetItems();
|
||||||
refreshTitle();
|
refreshTitle();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -467,8 +392,8 @@ const selectedColor = computed({
|
||||||
set(value) {
|
set(value) {
|
||||||
color.value = value;
|
color.value = value;
|
||||||
if (!selectedCategory.value) return;
|
if (!selectedCategory.value) return;
|
||||||
getFilters();
|
debouncedGetFilters();
|
||||||
getItems();
|
debouncedGetItems();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -479,8 +404,8 @@ const selectedProducer = computed({
|
||||||
set(value) {
|
set(value) {
|
||||||
producer.value = value;
|
producer.value = value;
|
||||||
if (!value || !selectedCategory.value) return;
|
if (!value || !selectedCategory.value) return;
|
||||||
getFilters();
|
debouncedGetFilters();
|
||||||
getItems();
|
debouncedGetItems();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -491,8 +416,8 @@ const selectedOrigin = computed({
|
||||||
set(value) {
|
set(value) {
|
||||||
origin.value = value;
|
origin.value = value;
|
||||||
if (!value || !selectedCategory.value) return;
|
if (!value || !selectedCategory.value) return;
|
||||||
getFilters();
|
debouncedGetFilters();
|
||||||
getItems();
|
debouncedGetItems();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -503,8 +428,8 @@ const selectedSubcategory = computed({
|
||||||
set(value) {
|
set(value) {
|
||||||
subcategory.value = value;
|
subcategory.value = value;
|
||||||
if (!value && !selectedCategory.value) return;
|
if (!value && !selectedCategory.value) return;
|
||||||
getFilters();
|
debouncedGetFilters();
|
||||||
getItems();
|
debouncedGetItems();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -518,7 +443,7 @@ const selectedOrderBy = computed({
|
||||||
} else {
|
} else {
|
||||||
orderBy.value = 'i.relevancy DESC, longName';
|
orderBy.value = 'i.relevancy DESC, longName';
|
||||||
}
|
}
|
||||||
getItems();
|
debouncedGetItems();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -530,6 +455,13 @@ const queryFilter = computed(() => {
|
||||||
filters.push(`(t.categoryFk = ${selectedCategory.value})`);
|
filters.push(`(t.categoryFk = ${selectedCategory.value})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filtro búsqueda
|
||||||
|
if (search.value) {
|
||||||
|
filters.push(
|
||||||
|
`((i.longName LIKE '%${search.value}%') OR (i.subname LIKE '%${search.value}%'))`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const otherFilters = {
|
const otherFilters = {
|
||||||
'i.typeFk': selectedType.value,
|
'i.typeFk': selectedType.value,
|
||||||
'i.inkFk': selectedColor?.value ? `'${selectedColor.value}'` : null,
|
'i.inkFk': selectedColor?.value ? `'${selectedColor.value}'` : null,
|
||||||
|
@ -550,6 +482,7 @@ const queryFilter = computed(() => {
|
||||||
|
|
||||||
const isSomeFilterSelected = computed(() => {
|
const isSomeFilterSelected = computed(() => {
|
||||||
return (
|
return (
|
||||||
|
search.value ||
|
||||||
selectedType.value ||
|
selectedType.value ||
|
||||||
selectedColor.value ||
|
selectedColor.value ||
|
||||||
selectedProducer.value ||
|
selectedProducer.value ||
|
||||||
|
@ -565,6 +498,67 @@ const viewTypeButtonContent = computed(() => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getFilters = async () => {
|
||||||
|
console.log('getFilters');
|
||||||
|
const promises = [
|
||||||
|
getItemFamilies(),
|
||||||
|
getItemColors(),
|
||||||
|
getProducers(),
|
||||||
|
getOrigins(),
|
||||||
|
getSubcategories()
|
||||||
|
];
|
||||||
|
|
||||||
|
await Promise.allSettled(promises);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getItems = async () => {
|
||||||
|
console.log('getItems');
|
||||||
|
try {
|
||||||
|
if (!basketOrderId.value || !isSomeFilterSelected.value) return;
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
const res = await jApi.execQuery(
|
||||||
|
`DROP TEMPORARY TABLE IF EXISTS tmp.item;
|
||||||
|
CREATE TEMPORARY TABLE tmp.item
|
||||||
|
(INDEX (itemFk))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT i.id itemFk
|
||||||
|
FROM vn.item i
|
||||||
|
JOIN vn.itemType t ON t.id = i.typeFk
|
||||||
|
WHERE (${queryFilter.value});
|
||||||
|
CALL myOrder_calcCatalogFull(#orderId);
|
||||||
|
SELECT i.id, i.longName item, i.subName,
|
||||||
|
i.tag5, i.value5, i.tag6, i.value6,
|
||||||
|
i.tag7, i.value7, i.tag8, i.value8,
|
||||||
|
i.relevancy, i.size, i.category, b.minQuantity,
|
||||||
|
k.name ink, p.name producer, o.name origin,
|
||||||
|
b.available, b.price, b.grouping,
|
||||||
|
i.image, im.updated
|
||||||
|
FROM tmp.ticketCalculateItem b
|
||||||
|
JOIN vn.item i ON i.id = b.itemFk
|
||||||
|
LEFT JOIN vn.ink k ON k.id = i.inkFk
|
||||||
|
LEFT JOIN vn.producer p ON p.id = i.producerFk
|
||||||
|
LEFT JOIN vn.origin o ON o.id = i.originFk
|
||||||
|
LEFT JOIN image im ON im.collectionFk = 'catalog'
|
||||||
|
AND im.name = i.image
|
||||||
|
WHERE b.available > 0
|
||||||
|
ORDER BY ${selectedOrderBy.value}
|
||||||
|
LIMIT 5000;
|
||||||
|
DROP TEMPORARY TABLE tmp.item;
|
||||||
|
CALL vn.ticketCalculatePurge();`,
|
||||||
|
{ orderId: basketOrderId.value }
|
||||||
|
);
|
||||||
|
items.value = res.results[3].data;
|
||||||
|
await onItemsFetched();
|
||||||
|
loading.value = false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting items:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const debouncedGetItems = debounce(getItems, 400);
|
||||||
|
const debouncedGetFilters = debounce(getFilters, 400);
|
||||||
|
|
||||||
const getOrder = async () => {
|
const getOrder = async () => {
|
||||||
try {
|
try {
|
||||||
const [data] = await jApi.query(
|
const [data] = await jApi.query(
|
||||||
|
@ -653,55 +647,6 @@ const onItemsFetched = async () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getItems = async () => {
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
!selectedCategory.value ||
|
|
||||||
!basketOrderId.value ||
|
|
||||||
!isSomeFilterSelected.value
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
loading.value = true;
|
|
||||||
|
|
||||||
const res = await jApi.execQuery(
|
|
||||||
`DROP TEMPORARY TABLE IF EXISTS tmp.item;
|
|
||||||
CREATE TEMPORARY TABLE tmp.item
|
|
||||||
(INDEX (itemFk))
|
|
||||||
ENGINE = MEMORY
|
|
||||||
SELECT i.id itemFk
|
|
||||||
FROM vn.item i
|
|
||||||
JOIN vn.itemType t ON t.id = i.typeFk
|
|
||||||
WHERE (${queryFilter.value});
|
|
||||||
CALL myOrder_calcCatalogFull(#orderId);
|
|
||||||
SELECT i.id, i.longName item, i.subName,
|
|
||||||
i.tag5, i.value5, i.tag6, i.value6,
|
|
||||||
i.tag7, i.value7, i.tag8, i.value8,
|
|
||||||
i.relevancy, i.size, i.category, b.minQuantity,
|
|
||||||
k.name ink, p.name producer, o.name origin,
|
|
||||||
b.available, b.price, b.grouping,
|
|
||||||
i.image, im.updated
|
|
||||||
FROM tmp.ticketCalculateItem b
|
|
||||||
JOIN vn.item i ON i.id = b.itemFk
|
|
||||||
LEFT JOIN vn.ink k ON k.id = i.inkFk
|
|
||||||
LEFT JOIN vn.producer p ON p.id = i.producerFk
|
|
||||||
LEFT JOIN vn.origin o ON o.id = i.originFk
|
|
||||||
LEFT JOIN image im ON im.collectionFk = 'catalog'
|
|
||||||
AND im.name = i.image
|
|
||||||
WHERE b.available > 0
|
|
||||||
ORDER BY ${selectedOrderBy.value}
|
|
||||||
LIMIT 5000;
|
|
||||||
DROP TEMPORARY TABLE tmp.item;
|
|
||||||
CALL vn.ticketCalculatePurge();`,
|
|
||||||
{ orderId: basketOrderId.value }
|
|
||||||
);
|
|
||||||
items.value = res.results[3].data;
|
|
||||||
await onItemsFetched();
|
|
||||||
loading.value = false;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error getting items:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getProducers = async () => {
|
const getProducers = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await jApi.execQuery(
|
const res = await jApi.execQuery(
|
||||||
|
@ -762,18 +707,6 @@ const getSubcategories = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFilters = async () => {
|
|
||||||
const promises = [
|
|
||||||
getItemFamilies(),
|
|
||||||
getItemColors(),
|
|
||||||
getProducers(),
|
|
||||||
getOrigins(),
|
|
||||||
getSubcategories()
|
|
||||||
];
|
|
||||||
|
|
||||||
await Promise.allSettled(promises);
|
|
||||||
};
|
|
||||||
|
|
||||||
const showItem = async item => {
|
const showItem = async item => {
|
||||||
if (isGuest.value) return;
|
if (isGuest.value) return;
|
||||||
|
|
||||||
|
@ -796,7 +729,9 @@ const onCategoryChange = () => {
|
||||||
selectedProducer.value = null;
|
selectedProducer.value = null;
|
||||||
selectedOrigin.value = null;
|
selectedOrigin.value = null;
|
||||||
selectedSubcategory.value = null;
|
selectedSubcategory.value = null;
|
||||||
|
search.value = '';
|
||||||
items.value = [];
|
items.value = [];
|
||||||
|
router.push({ params: { category: category.value, type: null } });
|
||||||
};
|
};
|
||||||
|
|
||||||
const getItemTags = async itemFk => {
|
const getItemTags = async itemFk => {
|
||||||
|
@ -950,6 +885,16 @@ const onViewModeClick = () => {
|
||||||
viewMode.value = viewMode.value === 'list' ? 'grid' : 'list';
|
viewMode.value = viewMode.value === 'list' ? 'grid' : 'list';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.query.search,
|
||||||
|
val => {
|
||||||
|
if (val) {
|
||||||
|
search.value = val;
|
||||||
|
debouncedGetItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
if (!isGuest.value) {
|
if (!isGuest.value) {
|
||||||
await appStore.check('catalog');
|
await appStore.check('catalog');
|
||||||
|
@ -1021,13 +966,10 @@ en-US:
|
||||||
added: Added
|
added: Added
|
||||||
listView: List view
|
listView: List view
|
||||||
gridView: Grid view
|
gridView: Grid view
|
||||||
|
filterBy: Filter by
|
||||||
es-ES:
|
es-ES:
|
||||||
warehouse: Almacén
|
|
||||||
agency: Agencia
|
|
||||||
modify: Modificar
|
|
||||||
category: Categoría
|
category: Categoría
|
||||||
deleteFilter: Quitar filtro
|
deleteFilter: Quitar filtro
|
||||||
filterBy: Filtrar por
|
|
||||||
family: Familia
|
family: Familia
|
||||||
search: Buscar
|
search: Buscar
|
||||||
color: Color
|
color: Color
|
||||||
|
@ -1045,6 +987,7 @@ es-ES:
|
||||||
added: Añadido
|
added: Añadido
|
||||||
listView: Vista de lista
|
listView: Vista de lista
|
||||||
gridView: Vista de rejilla
|
gridView: Vista de rejilla
|
||||||
|
filterBy: Filtrar por
|
||||||
ca-ES:
|
ca-ES:
|
||||||
category: Categoría
|
category: Categoría
|
||||||
deleteFilter: Eliminar filtro
|
deleteFilter: Eliminar filtro
|
||||||
|
@ -1063,6 +1006,7 @@ ca-ES:
|
||||||
add: Afegir
|
add: Afegir
|
||||||
listView: Vista de llista
|
listView: Vista de llista
|
||||||
gridView: Vista de graella
|
gridView: Vista de graella
|
||||||
|
filterBy: Filtrar per
|
||||||
fr-FR:
|
fr-FR:
|
||||||
category: Catégorie
|
category: Catégorie
|
||||||
deleteFilter: Supprimer le filtre
|
deleteFilter: Supprimer le filtre
|
||||||
|
@ -1081,6 +1025,7 @@ fr-FR:
|
||||||
add: Ajouter
|
add: Ajouter
|
||||||
listView: Vue en liste
|
listView: Vue en liste
|
||||||
gridView: Vue en grille
|
gridView: Vue en grille
|
||||||
|
filterBy: Filtrer par
|
||||||
pt-PT:
|
pt-PT:
|
||||||
category: Categoria
|
category: Categoria
|
||||||
deleteFilter: Apagar filtro
|
deleteFilter: Apagar filtro
|
||||||
|
@ -1099,4 +1044,5 @@ pt-PT:
|
||||||
add: Adicionar
|
add: Adicionar
|
||||||
listView: Vista de lista
|
listView: Vista de lista
|
||||||
gridView: Vista de grade
|
gridView: Vista de grade
|
||||||
|
filterBy: Filtrar por
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
export default function debounce(callback, delay) {
|
||||||
|
let timeoutId;
|
||||||
|
return (...args) => {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
callback(...args);
|
||||||
|
}, delay);
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue