Sink-UrlShortener/components/dashboard/links/Link.vue
2024-05-25 08:09:30 +08:00

187 lines
5.5 KiB
Vue

<script setup>
import { CalendarPlus2, Copy, CopyCheck, Eraser, Hourglass, Link as LinkIcon, QrCode, SquareChevronDown, SquarePen } from 'lucide-vue-next'
import { useClipboard } from '@vueuse/core'
import { toast } from 'vue-sonner'
import { parseURL } from 'ufo'
import QRCode from './QRCode.vue'
const props = defineProps({
link: {
type: Object,
required: true,
},
})
const emit = defineEmits(['update:link'])
const editPopoverOpen = ref(false)
const { host, origin } = location
function getLinkHost(url) {
const { host } = parseURL(url)
return host
}
const shortLink = computed(() => `${origin}/${props.link.slug}`)
const linkIcon = computed(() => `https://unavatar.io/${getLinkHost(props.link.url)}?fallback=https://sink.cool/sink.png`)
const { copy, copied } = useClipboard({ source: shortLink.value, copiedDuring: 400 })
function updateLink(link, type) {
emit('update:link', link, type)
editPopoverOpen.value = false
}
</script>
<template>
<Card>
<NuxtLink
class="flex flex-col p-4 space-y-3"
:to="`/dashboard/link?slug=${link.slug}`"
>
<div class="flex items-center justify-center space-x-3">
<Avatar>
<AvatarImage
:src="linkIcon"
alt="@radix-vue"
/>
<AvatarFallback>
<img
src="/sink.png"
alt="Sink"
>
</AvatarFallback>
</Avatar>
<div class="flex-1 overflow-hidden">
<div class="flex items-center">
<div class="font-bold leading-5 truncate text-md">
{{ host }}/{{ link.slug }}
</div>
<CopyCheck
v-if="copied"
class="w-4 h-4 ml-1 shrink-0"
@click.prevent
/>
<Copy
v-else
class="w-4 h-4 ml-1 shrink-0"
@click.prevent="copy(shortLink);toast('Copy successful!')"
/>
</div>
<TooltipProvider>
<Tooltip>
<TooltipTrigger as-child>
<p class="text-sm truncate">
{{ link.comment || link.title || link.description }}
</p>
</TooltipTrigger>
<TooltipContent>
<p class="max-w-[90svw] break-all">
{{ link.comment || link.title || link.description }}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<a
:href="link.url"
target="_blank"
rel="noopener noreferrer"
@click.stop
>
<LinkIcon class="w-5 h-5" />
</a>
<Popover>
<PopoverTrigger>
<QrCode
class="w-5 h-5"
@click.prevent
/>
</PopoverTrigger>
<PopoverContent>
<QRCode
:data="shortLink"
:image="linkIcon"
/>
</PopoverContent>
</Popover>
<Popover v-model:open="editPopoverOpen">
<PopoverTrigger>
<SquareChevronDown
class="w-5 h-5"
@click.prevent
/>
</PopoverTrigger>
<PopoverContent
class="w-auto p-0"
:hide-when-detached="false"
>
<DashboardLinksEditor
:link="link"
@update:link="updateLink"
>
<div
class="cursor-pointer flex select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground"
>
<SquarePen
class="w-5 h-5 mr-2"
/>
Edit
</div>
</DashboardLinksEditor>
<Separator />
<DashboardLinksDelete
:link="link"
@update:link="updateLink"
>
<div
class="cursor-pointer flex select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground"
>
<Eraser
class="w-5 h-5 mr-2"
/> Delete
</div>
</DashboardLinksDelete>
</PopoverContent>
</Popover>
</div>
<div class="flex w-full h-5 space-x-2 text-sm">
<TooltipProvider>
<Tooltip>
<TooltipTrigger as-child>
<span class="inline-flex items-center leading-5"><CalendarPlus2 class="w-4 h-4 mr-1" /> {{ shortDate(link.createdAt) }}</span>
</TooltipTrigger>
<TooltipContent>
<p>Created At: {{ longDate(link.createdAt) }}</p>
<p>Updated At: {{ longDate(link.updatedAt) }}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<template v-if="link.expiration">
<Separator orientation="vertical" />
<TooltipProvider>
<Tooltip>
<TooltipTrigger as-child>
<span class="inline-flex items-center leading-5"><Hourglass class="w-4 h-4 mr-1" /> {{ shortDate(link.expiration) }}</span>
</TooltipTrigger>
<TooltipContent>
<p>Expires At: {{ longDate(link.expiration) }}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</template>
<Separator orientation="vertical" />
<span class="truncate">{{ link.url }}</span>
</div>
</NuxtLink>
</Card>
</template>