Pin selection items to global filters
All checks were successful
Build Docker Image / build (push) Successful in 4m29s
All checks were successful
Build Docker Image / build (push) Successful in 4m29s
This commit is contained in:
@@ -8,21 +8,6 @@
|
|||||||
<Icon name="lucide:x" size="16" />
|
<Icon name="lucide:x" size="16" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-if="pinnedItems.length"
|
|
||||||
class="input input-sm w-full bg-white/10 border-white/20 text-white/80 flex flex-wrap items-center gap-1 py-1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
v-for="item in pinnedItems"
|
|
||||||
:key="item.uuid"
|
|
||||||
class="badge badge-neutral badge-sm flex items-center gap-1"
|
|
||||||
>
|
|
||||||
{{ item.name }}
|
|
||||||
<button class="text-white/70 hover:text-white" @click.stop="togglePin(item)">
|
|
||||||
<Icon name="lucide:x" size="12" />
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content (scrollable) -->
|
<!-- Content (scrollable) -->
|
||||||
@@ -31,7 +16,7 @@
|
|||||||
<span class="loading loading-spinner loading-md text-white" />
|
<span class="loading loading-spinner loading-md text-white" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="displayItems.length === 0" class="text-center py-8 text-white/60">
|
<div v-else-if="items.length === 0" class="text-center py-8 text-white/60">
|
||||||
<Icon name="lucide:search-x" size="32" class="mb-2" />
|
<Icon name="lucide:search-x" size="32" class="mb-2" />
|
||||||
<p>{{ $t('catalog.empty.noResults') }}</p>
|
<p>{{ $t('catalog.empty.noResults') }}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,7 +25,7 @@
|
|||||||
<!-- Products -->
|
<!-- Products -->
|
||||||
<template v-if="selectMode === 'product'">
|
<template v-if="selectMode === 'product'">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in displayItems"
|
v-for="(item, index) in items"
|
||||||
:key="item.uuid ?? index"
|
:key="item.uuid ?? index"
|
||||||
class="relative group"
|
class="relative group"
|
||||||
@mouseenter="emit('hover', item.uuid ?? null)"
|
@mouseenter="emit('hover', item.uuid ?? null)"
|
||||||
@@ -49,9 +34,9 @@
|
|||||||
<button
|
<button
|
||||||
v-if="item.uuid"
|
v-if="item.uuid"
|
||||||
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||||
@click.stop="togglePin(item)"
|
@click.stop="onPin(item)"
|
||||||
>
|
>
|
||||||
<Icon :name="isPinned(item.uuid) ? 'lucide:pin-off' : 'lucide:pin'" size="12" />
|
<Icon name="lucide:pin" size="12" />
|
||||||
</button>
|
</button>
|
||||||
<ProductCard
|
<ProductCard
|
||||||
:product="item"
|
:product="item"
|
||||||
@@ -65,7 +50,7 @@
|
|||||||
<!-- Hubs -->
|
<!-- Hubs -->
|
||||||
<template v-else-if="selectMode === 'hub'">
|
<template v-else-if="selectMode === 'hub'">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in displayItems"
|
v-for="(item, index) in items"
|
||||||
:key="item.uuid ?? index"
|
:key="item.uuid ?? index"
|
||||||
class="relative group"
|
class="relative group"
|
||||||
@mouseenter="emit('hover', item.uuid ?? null)"
|
@mouseenter="emit('hover', item.uuid ?? null)"
|
||||||
@@ -74,9 +59,9 @@
|
|||||||
<button
|
<button
|
||||||
v-if="item.uuid"
|
v-if="item.uuid"
|
||||||
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||||
@click.stop="togglePin(item)"
|
@click.stop="onPin(item)"
|
||||||
>
|
>
|
||||||
<Icon :name="isPinned(item.uuid) ? 'lucide:pin-off' : 'lucide:pin'" size="12" />
|
<Icon name="lucide:pin" size="12" />
|
||||||
</button>
|
</button>
|
||||||
<HubCard
|
<HubCard
|
||||||
:hub="item"
|
:hub="item"
|
||||||
@@ -89,7 +74,7 @@
|
|||||||
<!-- Suppliers -->
|
<!-- Suppliers -->
|
||||||
<template v-else-if="selectMode === 'supplier'">
|
<template v-else-if="selectMode === 'supplier'">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in displayItems"
|
v-for="(item, index) in items"
|
||||||
:key="item.uuid ?? index"
|
:key="item.uuid ?? index"
|
||||||
class="relative group"
|
class="relative group"
|
||||||
@mouseenter="emit('hover', item.uuid ?? null)"
|
@mouseenter="emit('hover', item.uuid ?? null)"
|
||||||
@@ -98,9 +83,9 @@
|
|||||||
<button
|
<button
|
||||||
v-if="item.uuid"
|
v-if="item.uuid"
|
||||||
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity bg-black/40 hover:bg-black/60 text-white rounded-full w-6 h-6 flex items-center justify-center"
|
||||||
@click.stop="togglePin(item)"
|
@click.stop="onPin(item)"
|
||||||
>
|
>
|
||||||
<Icon :name="isPinned(item.uuid) ? 'lucide:pin-off' : 'lucide:pin'" size="12" />
|
<Icon name="lucide:pin" size="12" />
|
||||||
</button>
|
</button>
|
||||||
<SupplierCard
|
<SupplierCard
|
||||||
:supplier="item"
|
:supplier="item"
|
||||||
@@ -144,6 +129,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'select': [type: string, item: Item]
|
'select': [type: string, item: Item]
|
||||||
|
'pin': [type: string, item: Item]
|
||||||
'close': []
|
'close': []
|
||||||
'load-more': []
|
'load-more': []
|
||||||
'hover': [uuid: string | null]
|
'hover': [uuid: string | null]
|
||||||
@@ -152,7 +138,6 @@ const emit = defineEmits<{
|
|||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const loadMoreSentinel = ref<HTMLElement | null>(null)
|
const loadMoreSentinel = ref<HTMLElement | null>(null)
|
||||||
const pinnedIds = ref<string[]>([])
|
|
||||||
|
|
||||||
// Infinite scroll using IntersectionObserver
|
// Infinite scroll using IntersectionObserver
|
||||||
let observer: IntersectionObserver | null = null
|
let observer: IntersectionObserver | null = null
|
||||||
@@ -200,33 +185,16 @@ const items = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const isPinned = (uuid: string) => pinnedIds.value.includes(uuid)
|
|
||||||
|
|
||||||
const togglePin = (item: Item) => {
|
|
||||||
if (!item.uuid) return
|
|
||||||
if (isPinned(item.uuid)) {
|
|
||||||
pinnedIds.value = pinnedIds.value.filter(id => id !== item.uuid)
|
|
||||||
} else {
|
|
||||||
pinnedIds.value = [...pinnedIds.value, item.uuid]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const pinnedItems = computed(() =>
|
|
||||||
items.value.filter(item => item.uuid && isPinned(item.uuid))
|
|
||||||
)
|
|
||||||
|
|
||||||
const displayItems = computed(() => {
|
|
||||||
if (pinnedIds.value.length === 0) return items.value
|
|
||||||
const pinned = pinnedItems.value
|
|
||||||
const pinnedSet = new Set(pinned.map(p => p.uuid).filter(Boolean) as string[])
|
|
||||||
const rest = items.value.filter(item => !item.uuid || !pinnedSet.has(item.uuid))
|
|
||||||
return [...pinned, ...rest]
|
|
||||||
})
|
|
||||||
|
|
||||||
// Select item and emit
|
// Select item and emit
|
||||||
const onSelect = (item: Item) => {
|
const onSelect = (item: Item) => {
|
||||||
if (props.selectMode && item.uuid) {
|
if (props.selectMode && item.uuid) {
|
||||||
emit('select', props.selectMode, item)
|
emit('select', props.selectMode, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onPin = (item: Item) => {
|
||||||
|
if (props.selectMode && item.uuid) {
|
||||||
|
emit('pin', props.selectMode, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
:loading-more="selectionLoadingMore"
|
:loading-more="selectionLoadingMore"
|
||||||
:has-more="selectionHasMore && !filterByBounds"
|
:has-more="selectionHasMore && !filterByBounds"
|
||||||
@select="onSelectItem"
|
@select="onSelectItem"
|
||||||
|
@pin="onPinItem"
|
||||||
@close="onClosePanel"
|
@close="onClosePanel"
|
||||||
@load-more="onLoadMore"
|
@load-more="onLoadMore"
|
||||||
@hover="onHoverItem"
|
@hover="onHoverItem"
|
||||||
@@ -499,6 +500,12 @@ const onSelectItem = (type: string, item: { uuid?: string | null; name?: string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onPinItem = (type: string, item: { uuid?: string | null; name?: string | null }) => {
|
||||||
|
if (item.uuid && item.name) {
|
||||||
|
selectItem(type, item.uuid, item.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Close panel (cancel select mode)
|
// Close panel (cancel select mode)
|
||||||
const onClosePanel = () => {
|
const onClosePanel = () => {
|
||||||
cancelSelect()
|
cancelSelect()
|
||||||
|
|||||||
Reference in New Issue
Block a user