136 lines
4.3 KiB
Vue
136 lines
4.3 KiB
Vue
<script setup>
|
|
const props = defineProps({
|
|
activeTab: {
|
|
type: String,
|
|
default: 'library'
|
|
},
|
|
getMediaBadgeClass: {
|
|
type: Function,
|
|
required: true
|
|
},
|
|
getMediaBadgeLabel: {
|
|
type: Function,
|
|
required: true
|
|
},
|
|
getMediaGridPreviewUrl: {
|
|
type: Function,
|
|
required: true
|
|
},
|
|
getMediaItemKind: {
|
|
type: Function,
|
|
required: true
|
|
},
|
|
getMediaUsageBadgeCount: {
|
|
type: Function,
|
|
required: true
|
|
},
|
|
items: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
selectedUrls: {
|
|
type: Array,
|
|
default: () => []
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits([
|
|
'drag-start',
|
|
'image-error',
|
|
'open-detail',
|
|
'toggle-selection'
|
|
])
|
|
|
|
/**
|
|
* 미디어 선택 여부를 확인한다.
|
|
* @param {Object} item - 미디어 항목
|
|
* @returns {boolean} 선택 여부
|
|
*/
|
|
const isMediaSelected = (item) => props.selectedUrls.includes(item?.url)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="admin-media-grid admin-media__grid mt-5 grid grid-cols-3 gap-2 sm:grid-cols-4 md:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-10">
|
|
<div
|
|
v-for="(item, index) in props.items"
|
|
:key="item.url"
|
|
class="admin-media__item group relative overflow-hidden border border-line bg-white text-left outline-none transition hover:border-[#15171a]"
|
|
:draggable="props.activeTab === 'library'"
|
|
@dragstart="emit('drag-start', $event, item)"
|
|
>
|
|
<button
|
|
type="button"
|
|
class="admin-media__select-toggle absolute left-1.5 top-1.5 z-10 grid size-7 place-items-center rounded-md border-2 shadow-md outline-none transition focus-visible:ring-2 focus-visible:ring-[#15171a] focus-visible:ring-offset-1"
|
|
:class="isMediaSelected(item)
|
|
? 'border-white bg-[#15171a] text-white'
|
|
: 'border-[#394047] bg-white/95 text-[#15171a] hover:border-[#15171a] hover:bg-white'"
|
|
:aria-label="`${item.name} 선택`"
|
|
:aria-pressed="isMediaSelected(item)"
|
|
@click.stop="emit('toggle-selection', item, index, $event)"
|
|
>
|
|
<svg
|
|
v-if="isMediaSelected(item)"
|
|
class="size-4 shrink-0"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2.5"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
aria-hidden="true"
|
|
>
|
|
<path d="M20 6 9 17l-5-5" />
|
|
</svg>
|
|
</button>
|
|
<button
|
|
class="admin-media__thumb flex w-full flex-col text-left outline-none"
|
|
type="button"
|
|
@click="emit('open-detail', item)"
|
|
>
|
|
<span class="admin-media__thumb-frame relative block w-full overflow-hidden">
|
|
<img
|
|
v-if="props.getMediaItemKind(item) === 'image'"
|
|
class="admin-media__image aspect-square w-full bg-surface object-cover"
|
|
:src="props.getMediaGridPreviewUrl(item)"
|
|
:alt="item.title"
|
|
loading="lazy"
|
|
decoding="async"
|
|
@error="emit('image-error', $event, item)"
|
|
>
|
|
<AdminMediaVideoThumbnail
|
|
v-else-if="props.getMediaItemKind(item) === 'video'"
|
|
:src="item.url"
|
|
:alt="item.title"
|
|
/>
|
|
<span
|
|
v-else
|
|
class="admin-media__image flex aspect-square w-full items-center justify-center bg-surface text-xs font-bold uppercase tracking-[0.18em] text-muted"
|
|
>
|
|
{{ props.getMediaItemKind(item) }}
|
|
</span>
|
|
<span
|
|
v-if="props.getMediaUsageBadgeCount(item)"
|
|
class="admin-media__usage-count-badge pointer-events-none absolute right-1.5 top-1.5 z-[1] rounded bg-[#15171a] px-1.5 py-0.5 text-[10px] font-semibold text-white"
|
|
>
|
|
{{ props.getMediaUsageBadgeCount(item) }}
|
|
</span>
|
|
<span
|
|
v-if="props.getMediaBadgeLabel(item)"
|
|
class="admin-media__usage-badge pointer-events-none absolute right-1.5 z-[1] rounded px-1.5 py-0.5 text-[10px] font-semibold"
|
|
:class="[
|
|
props.getMediaBadgeClass(item),
|
|
props.getMediaUsageBadgeCount(item) ? 'top-8' : 'top-1.5'
|
|
]"
|
|
>
|
|
{{ props.getMediaBadgeLabel(item) }}
|
|
</span>
|
|
</span>
|
|
<span class="admin-media__name block truncate px-2 py-1.5 text-xs font-semibold text-ink">
|
|
{{ item.name }}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|