fix: gallery animation emoji layout

This commit is contained in:
pa
2026-03-27 13:13:28 +09:00
parent 9b2eb7ea36
commit 4da9e1cf46
+106 -75
View File
@@ -90,9 +90,17 @@
:class="compareCurrentProfilePic(image.id) ? 'x-highlight-ring' : ''" :class="compareCurrentProfilePic(image.id) ? 'x-highlight-ring' : ''"
as-child> as-child>
<div <div
v-if="image.versions && image.versions.length > 0 && image.versions[image.versions.length - 1].file.url" v-if="
image.versions &&
image.versions.length > 0 &&
image.versions[image.versions.length - 1].file.url
"
class="overflow-hidden rounded-[inherit]"> class="overflow-hidden rounded-[inherit]">
<ItemHeader class="cursor-pointer" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)"> <ItemHeader
class="cursor-pointer"
@click="
showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)
">
<img <img
:src="image.versions[image.versions.length - 1].file.url" :src="image.versions[image.versions.length - 1].file.url"
loading="lazy" loading="lazy"
@@ -162,9 +170,17 @@
:class="compareCurrentVRCPlusIcon(image.id) ? 'x-highlight-ring' : ''" :class="compareCurrentVRCPlusIcon(image.id) ? 'x-highlight-ring' : ''"
as-child> as-child>
<div <div
v-if="image.versions && image.versions.length > 0 && image.versions[image.versions.length - 1].file.url" v-if="
image.versions &&
image.versions.length > 0 &&
image.versions[image.versions.length - 1].file.url
"
class="overflow-hidden rounded-[inherit]"> class="overflow-hidden rounded-[inherit]">
<ItemHeader class="cursor-pointer" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)"> <ItemHeader
class="cursor-pointer"
@click="
showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)
">
<img <img
:src="image.versions[image.versions.length - 1].file.url" :src="image.versions[image.versions.length - 1].file.url"
loading="lazy" loading="lazy"
@@ -200,52 +216,51 @@
accept="image/*" accept="image/*"
@change="onFileChangeEmoji" @change="onFileChangeEmoji"
style="display: none" /> style="display: none" />
<div class="flex items-center gap-2"> <div class="flex flex-col gap-2">
<ButtonGroup> <div class="flex items-center gap-2">
<Button variant="outline" size="sm" @click="refreshEmojiTable"> <ButtonGroup>
<RefreshCw /> <Button variant="outline" size="sm" @click="refreshEmojiTable">
{{ t('dialog.gallery_icons.refresh') }} <RefreshCw />
</Button> {{ t('dialog.gallery_icons.refresh') }}
<Button </Button>
variant="outline" <Button
size="sm" variant="outline"
:disabled="!isLocalUserVrcPlusSupporter || isUploading" size="sm"
@click="displayEmojiUpload"> :disabled="!isLocalUserVrcPlusSupporter || isUploading"
<Upload /> @click="displayEmojiUpload">
{{ t('dialog.gallery_icons.upload') }} <Upload />
</Button> {{ t('dialog.gallery_icons.upload') }}
</ButtonGroup> </Button>
<div class="flex-1 min-w-0 max-w-120"> </ButtonGroup>
<VirtualCombobox <div class="flex-1 min-w-0 max-w-120">
v-model="emojiAnimationStyle" <VirtualCombobox
:groups="emojiStylePickerGroups" v-model="emojiAnimationStyle"
:placeholder="t('dialog.gallery_icons.emoji_animation_styles')" :groups="emojiStylePickerGroups"
:search-placeholder="t('dialog.gallery_icons.emoji_animation_styles')" :placeholder="t('dialog.gallery_icons.emoji_animation_styles')"
:clearable="false" :search-placeholder="t('dialog.gallery_icons.emoji_animation_styles')"
:close-on-select="true"> :clearable="false"
<template #item="{ item, selected }"> :close-on-select="true">
<div class="flex w-full items-center gap-2"> <template #item="{ item, selected }">
<div class="h-10 w-10 shrink-0 overflow-hidden rounded-sm bg-black/5"> <div class="flex w-full items-center gap-2">
<img <div class="h-10 w-10 shrink-0 overflow-hidden rounded-sm bg-black/5">
class="h-full w-full object-cover" <img
:src="`${emojiAnimationStyleUrl}${item.fileName}`" class="h-full w-full object-cover"
loading="lazy" /> :src="`${emojiAnimationStyleUrl}${item.fileName}`"
loading="lazy" />
</div>
<span class="truncate text-sm" v-text="item.label"></span>
<span v-if="selected" class="ml-auto opacity-70">✓</span>
</div> </div>
<span class="truncate text-sm" v-text="item.label"></span> </template>
<span v-if="selected" class="ml-auto opacity-70"></span> </VirtualCombobox>
</div> </div>
</template> <label class="inline-flex items-center gap-2">
</VirtualCombobox> <Checkbox v-model="emojiAnimType" />
<span>{{ t('dialog.gallery_icons.emoji_animation_type') }}</span>
</label>
</div> </div>
<label class="inline-flex items-center gap-2"> <div v-if="emojiAnimType" class="flex items-center gap-2">
<Checkbox v-model="emojiAnimType" /> <Button size="sm" variant="outline" @click="openExternalLink('https://vrcemoji.com')">
<span>{{ t('dialog.gallery_icons.emoji_animation_type') }}</span>
</label>
<template v-if="emojiAnimType">
<Button
size="sm"
variant="outline"
@click="openExternalLink('https://vrcemoji.com')">
{{ t('dialog.gallery_icons.create_animated_emoji') }} {{ t('dialog.gallery_icons.create_animated_emoji') }}
</Button> </Button>
<span class="text-sm">{{ t('dialog.gallery_icons.emoji_animation_fps') }}</span> <span class="text-sm">{{ t('dialog.gallery_icons.emoji_animation_fps') }}</span>
@@ -262,9 +277,7 @@
<NumberFieldIncrement /> <NumberFieldIncrement />
</NumberFieldContent> </NumberFieldContent>
</NumberField> </NumberField>
<span class="text-sm">{{ <span class="text-sm">{{ t('dialog.gallery_icons.emoji_animation_frame_count') }}</span>
t('dialog.gallery_icons.emoji_animation_frame_count')
}}</span>
<NumberField <NumberField
v-model="emojiAnimFrameCount" v-model="emojiAnimFrameCount"
:min="2" :min="2"
@@ -282,8 +295,10 @@
<Checkbox v-model="emojiAnimLoopPingPong" /> <Checkbox v-model="emojiAnimLoopPingPong" />
<span>{{ t('dialog.gallery_icons.emoji_loop_pingpong') }}</span> <span>{{ t('dialog.gallery_icons.emoji_loop_pingpong') }}</span>
</label> </label>
</template> </div>
<span v-if="emojiAnimType" class="basis-full text-sm text-muted-foreground">{{ t('dialog.gallery_icons.flipbook_info') }}</span> <span v-if="emojiAnimType" class="basis-full text-sm text-muted-foreground">{{
t('dialog.gallery_icons.flipbook_info')
}}</span>
</div> </div>
<ItemGroup <ItemGroup
class="grid gap-3 mt-3" class="grid gap-3 mt-3"
@@ -296,11 +311,20 @@
class="p-0 x-hover-card hover:bg-accent hover:shadow-sm" class="p-0 x-hover-card hover:bg-accent hover:shadow-sm"
as-child> as-child>
<div <div
v-if="image.versions && image.versions.length > 0 && image.versions[image.versions.length - 1].file.url" v-if="
image.versions &&
image.versions.length > 0 &&
image.versions[image.versions.length - 1].file.url
"
class="overflow-hidden"> class="overflow-hidden">
<ItemHeader <ItemHeader
class="cursor-pointer" class="cursor-pointer"
@click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url, getEmojiFileName(image))"> @click="
showFullscreenImageDialog(
image.versions[image.versions.length - 1].file.url,
getEmojiFileName(image)
)
">
<Emoji <Emoji
:imageUrl="image.versions[image.versions.length - 1].file.url" :imageUrl="image.versions[image.versions.length - 1].file.url"
:size="200" :size="200"
@@ -364,9 +388,17 @@
class="p-0 x-hover-card hover:bg-accent hover:shadow-sm" class="p-0 x-hover-card hover:bg-accent hover:shadow-sm"
as-child> as-child>
<div <div
v-if="image.versions && image.versions.length > 0 && image.versions[image.versions.length - 1].file.url" v-if="
image.versions &&
image.versions.length > 0 &&
image.versions[image.versions.length - 1].file.url
"
class="overflow-hidden"> class="overflow-hidden">
<ItemHeader class="cursor-pointer" @click="showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)"> <ItemHeader
class="cursor-pointer"
@click="
showFullscreenImageDialog(image.versions[image.versions.length - 1].file.url)
">
<img <img
:src="image.versions[image.versions.length - 1].file.url" :src="image.versions[image.versions.length - 1].file.url"
loading="lazy" loading="lazy"
@@ -433,7 +465,9 @@
class="p-0 x-hover-card hover:bg-accent hover:shadow-sm" class="p-0 x-hover-card hover:bg-accent hover:shadow-sm"
as-child> as-child>
<div class="overflow-hidden"> <div class="overflow-hidden">
<ItemHeader class="cursor-pointer" @click="showFullscreenImageDialog(image.files.image, getPrintFileName(image))"> <ItemHeader
class="cursor-pointer"
@click="showFullscreenImageDialog(image.files.image, getPrintFileName(image))">
<img <img
:src="image.files.image" :src="image.files.image"
loading="lazy" loading="lazy"
@@ -518,17 +552,23 @@
{{ formatDateFilter(item.created_at, 'long') }} {{ formatDateFilter(item.created_at, 'long') }}
</ItemDescription> </ItemDescription>
<ItemDescription class="text-xs"> <ItemDescription class="text-xs">
<span v-if="item.itemType === 'prop'">{{ t('dialog.gallery_icons.item') }}</span> <span v-if="item.itemType === 'prop'">{{
<span v-else-if="item.itemType === 'sticker'">{{ t('dialog.gallery_icons.sticker') }}</span> t('dialog.gallery_icons.item')
<span v-else-if="item.itemType === 'droneskin'">{{ t('dialog.gallery_icons.drone_skin') }}</span> }}</span>
<span v-else-if="item.itemType === 'emoji'">{{ t('dialog.gallery_icons.emoji') }}</span> <span v-else-if="item.itemType === 'sticker'">{{
t('dialog.gallery_icons.sticker')
}}</span>
<span v-else-if="item.itemType === 'droneskin'">{{
t('dialog.gallery_icons.drone_skin')
}}</span>
<span v-else-if="item.itemType === 'emoji'">{{
t('dialog.gallery_icons.emoji')
}}</span>
<span v-else v-text="item.itemTypeLabel"></span> <span v-else v-text="item.itemTypeLabel"></span>
</ItemDescription> </ItemDescription>
</ItemContent> </ItemContent>
<ItemFooter v-if="item.itemType === 'bundle'" class="p-2"> <ItemFooter v-if="item.itemType === 'bundle'" class="p-2">
<Button <Button size="sm" @click="consumeInventoryBundle(item.id)">
size="sm"
@click="consumeInventoryBundle(item.id)">
{{ t('dialog.gallery_icons.consume_bundle') }} {{ t('dialog.gallery_icons.consume_bundle') }}
</Button> </Button>
</ItemFooter> </ItemFooter>
@@ -779,16 +819,7 @@
* errorMessage?: string * errorMessage?: string
* }} options * }} options
*/ */
function openImageUploadFlow( function openImageUploadFlow(e, { inputSelector, aspectRatio, beforeCrop, upload, errorMessage = 'Failed to upload' }) {
e,
{
inputSelector,
aspectRatio,
beforeCrop,
upload,
errorMessage = 'Failed to upload'
}
) {
const { file, clearInput } = handleImageUploadInput(e, { const { file, clearInput } = handleImageUploadInput(e, {
inputSelector, inputSelector,
tooLargeMessage: () => t('message.file.too_large'), tooLargeMessage: () => t('message.file.too_large'),