Work on sprite stuff
This commit is contained in:
118
src/components/utilities/assetManager/partials/SpriteDetails.vue
Normal file
118
src/components/utilities/assetManager/partials/SpriteDetails.vue
Normal file
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="h-full overflow-auto">
|
||||
<div class="relative p-2.5 flex flex-col items-center justify-between h-[300px]">
|
||||
<div class="filler"></div>
|
||||
<img class="max-h-[280px]" :src="`${config.server_endpoint}/assets/sprites/${selectedSprite?.id}.png`" :alt="'Sprite ' + selectedSprite?.id" />
|
||||
<button class="btn-bordeaux px-[15px] py-1.5 min-w-[100px]" type="button" @click.prevent="removeSprite">Remove</button>
|
||||
<div class="absolute left-0 bottom-0 w-full h-[1px] bg-cyan-200"></div>
|
||||
</div>
|
||||
<div class="m-2.5 p-2.5 block">
|
||||
<form class="flex gap-2.5 flex-wrap" @submit.prevent="saveSprite">
|
||||
<div class="w-full flex flex-col mb-5">
|
||||
<label class="mb-1.5 font-titles" for="name">Name</label>
|
||||
<input v-model="spriteName" class="input-cyan" type="text" name="name" placeholder="Wall #1" />
|
||||
</div>
|
||||
<div class="w-[calc(50%_-_5px)] flex flex-col mb-5">
|
||||
<label class="mb-1.5 font-titles" for="origin-x">Origin X</label>
|
||||
<input v-model="spriteOriginX" class="input-cyan" type="number" step="any" name="origin-x" placeholder="Origin X" />
|
||||
</div>
|
||||
<div class="w-[calc(50%_-_5px)] flex flex-col mb-5">
|
||||
<label class="mb-1.5 font-titles" for="origin-y">Origin Y</label>
|
||||
<input v-model="spriteOriginY" class="input-cyan" type="number" step="any" name="origin-y" placeholder="Origin Y" />
|
||||
</div>
|
||||
<div class="w-full flex flex-col mb-5">
|
||||
<label class="mb-1.5 font-titles" for="origin-x">Tags</label>
|
||||
<ChipsInput v-model="spriteTags" @update:modelValue="spriteTags = $event" />
|
||||
</div>
|
||||
<button class="btn-cyan px-[15px] py-1.5 min-w-[100px]" type="submit">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Sprite } from '@/types'
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { useAssetManagerStore } from '@/stores/assetManager'
|
||||
import { useGameStore } from '@/stores/game'
|
||||
import config from '@/config'
|
||||
import ChipsInput from '@/components/forms/ChipsInput.vue'
|
||||
|
||||
const gameStore = useGameStore()
|
||||
const assetManagerStore = useAssetManagerStore()
|
||||
|
||||
const selectedSprite = computed(() => assetManagerStore.selectedSprite)
|
||||
|
||||
const spriteName = ref('')
|
||||
const spriteTags = ref([] as string[])
|
||||
const spriteOriginX = ref(0)
|
||||
const spriteOriginY = ref(0)
|
||||
|
||||
if (!selectedSprite.value) {
|
||||
console.error('No sprite selected')
|
||||
}
|
||||
|
||||
if (selectedSprite.value) {
|
||||
spriteName.value = selectedSprite.value.name
|
||||
spriteOriginX.value = selectedSprite.value.origin_x
|
||||
spriteOriginY.value = selectedSprite.value.origin_y
|
||||
}
|
||||
|
||||
function removeSprite() {
|
||||
gameStore.connection?.emit('gm:sprite:remove', { sprite: selectedSprite.value?.id }, (response: boolean) => {
|
||||
if (!response) {
|
||||
console.error('Failed to remove sprite')
|
||||
return
|
||||
}
|
||||
refreshSpriteList()
|
||||
})
|
||||
}
|
||||
|
||||
function refreshSpriteList() {
|
||||
gameStore.connection?.emit('gm:sprite:list', {}, (response: Sprite[]) => {
|
||||
assetManagerStore.setSpriteList(response)
|
||||
assetManagerStore.setSelectedSprite(null)
|
||||
})
|
||||
}
|
||||
|
||||
function saveSprite() {
|
||||
if (!selectedSprite.value) {
|
||||
console.error('No sprite selected')
|
||||
return
|
||||
}
|
||||
|
||||
console.log(spriteTags.value)
|
||||
gameStore.connection?.emit(
|
||||
'gm:sprite:update',
|
||||
{
|
||||
id: selectedSprite.value.id,
|
||||
name: spriteName.value,
|
||||
tags: spriteTags.value,
|
||||
origin_x: spriteOriginX.value,
|
||||
origin_y: spriteOriginY.value
|
||||
},
|
||||
(response: boolean) => {
|
||||
if (!response) {
|
||||
console.error('Failed to save sprite')
|
||||
return
|
||||
}
|
||||
refreshSpriteList()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
watch(selectedSprite, (sprite: Sprite | null) => {
|
||||
if (!sprite) return
|
||||
spriteName.value = sprite.name
|
||||
spriteOriginX.value = sprite.origin_x
|
||||
spriteOriginY.value = sprite.origin_y
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (!selectedSprite.value) return
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
assetManagerStore.setSelectedSprite(null)
|
||||
})
|
||||
</script>
|
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="relative p-2.5 cursor-pointer flex gap-y-2.5 gap-x-5 flex-wrap">
|
||||
<label for="upload-asset" class="bg-cyan/50 border border-solid border-white/25 rounded drop-shadow-20 py-1.5 px-[15px] inline-flex hover:bg-cyan hover:cursor-pointer">
|
||||
<input class="hidden" id="upload-asset" ref="spriteUploadField" type="file" accept="image/png" multiple @change="handleFileUpload" />
|
||||
Upload sprite(s)
|
||||
</label>
|
||||
<input v-model="searchQuery" class="input-cyan w-full" placeholder="Search..." @input="handleSearch" />
|
||||
<div class="absolute left-0 bottom-0 w-full h-[1px] bg-cyan-200"></div>
|
||||
</div>
|
||||
<a class="relative p-2.5 cursor-pointer" :class="{ 'bg-cyan/80': assetManagerStore.selectedSprite?.id === sprite.id }" v-for="(sprite, index) in filteredSprites" :key="index" @click="assetManagerStore.setSelectedSprite(sprite as Sprite)">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div class="h-[28px] w-[75px] max-w-[75px] flex justify-center">
|
||||
<img class="h-[28px]" :src="`${config.server_endpoint}/assets/sprites/${sprite.id}.png`" alt="Sprite" />
|
||||
</div>
|
||||
<span>{{ sprite.name }}</span>
|
||||
</div>
|
||||
<div class="absolute left-0 bottom-0 w-full h-[1px] bg-cyan-200"></div>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import config from '@/config'
|
||||
import { useGameStore } from '@/stores/game'
|
||||
import { onMounted, ref, computed } from 'vue'
|
||||
import { useAssetManagerStore } from '@/stores/assetManager'
|
||||
import { useAssetStore } from '@/stores/assets'
|
||||
import type { Sprite } from '@/types'
|
||||
|
||||
const gameStore = useGameStore()
|
||||
const spriteUploadField = ref(null)
|
||||
const assetManagerStore = useAssetManagerStore()
|
||||
const assetStore = useAssetStore()
|
||||
|
||||
const searchQuery = ref('')
|
||||
|
||||
const handleFileUpload = (e: Event) => {
|
||||
const files = (e.target as HTMLInputElement).files
|
||||
if (!files) return
|
||||
gameStore.connection?.emit('gm:sprite:upload', files, (response: boolean) => {
|
||||
if (!response) {
|
||||
if (config.development) console.error('Failed to upload sprite')
|
||||
return
|
||||
}
|
||||
|
||||
assetStore.fetchAssets()
|
||||
|
||||
gameStore.connection?.emit('gm:sprite:list', {}, (response: Sprite[]) => {
|
||||
assetManagerStore.setSpriteList(response)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
// The filtering is handled by the computed property, so we don't need to do anything here
|
||||
// This function is kept in case you want to add debounce or other functionality later
|
||||
}
|
||||
|
||||
const filteredSprites = computed(() => {
|
||||
if (!searchQuery.value) {
|
||||
return assetManagerStore.spriteList
|
||||
}
|
||||
return assetManagerStore.spriteList.filter((sprite) => sprite.name.toLowerCase().includes(searchQuery.value.toLowerCase()))
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
gameStore.connection?.emit('gm:sprite:list', {}, (response: Sprite[]) => {
|
||||
assetManagerStore.setSpriteList(response)
|
||||
})
|
||||
})
|
||||
</script>
|
@ -7,14 +7,20 @@
|
||||
<input v-model="searchQuery" class="input-cyan w-full" placeholder="Search..." @input="handleSearch" />
|
||||
<div class="absolute left-0 bottom-0 w-full h-[1px] bg-cyan-200"></div>
|
||||
</div>
|
||||
<div v-bind="containerProps">
|
||||
<div v-bind="containerProps" style="height: 400px; overflow-y: auto;">
|
||||
<div v-bind="wrapperProps">
|
||||
<a class="relative p-2.5 cursor-pointer block" :class="{ 'bg-cyan/80': assetManagerStore.selectedTile?.id === tile.data.id }" v-for="(tile, index) in list" :key="index" @click="assetManagerStore.setSelectedTile(tile.data as Tile)">
|
||||
<a
|
||||
v-for="{ data: tile } in list"
|
||||
:key="tile.id"
|
||||
class="relative p-2.5 cursor-pointer block"
|
||||
:class="{ 'bg-cyan/80': assetManagerStore.selectedTile?.id === tile.id }"
|
||||
@click="assetManagerStore.setSelectedTile(tile)"
|
||||
>
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div class="h-[28px] w-[75px] max-w-[75px] flex justify-center">
|
||||
<img class="h-[28px]" :src="`${config.server_endpoint}/assets/tiles/${tile.data.id}.png`" alt="Tile" />
|
||||
<img class="h-[28px]" :src="`${config.server_endpoint}/assets/tiles/${tile.id}.png`" alt="Tile" />
|
||||
</div>
|
||||
<span>{{ tile.data.name }}</span>
|
||||
<span>{{ tile.name }}</span>
|
||||
</div>
|
||||
<div class="absolute left-0 bottom-0 w-full h-[1px] bg-cyan-200"></div>
|
||||
</a>
|
||||
@ -56,28 +62,31 @@ const handleFileUpload = (e: Event) => {
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
// The filtering is handled by the computed property, so we don't need to do anything here
|
||||
// This function is kept in case you want to add debounce or other functionality later
|
||||
// Trigger a re-render of the virtual list
|
||||
virtualList.value?.scrollTo(0)
|
||||
}
|
||||
|
||||
const filteredTiles = computed(() => {
|
||||
if (!searchQuery.value) {
|
||||
return assetManagerStore.tileList
|
||||
}
|
||||
return assetManagerStore.tileList.filter((tile) => tile.name.toLowerCase().includes(searchQuery.value.toLowerCase()))
|
||||
return assetManagerStore.tileList.filter((tile) =>
|
||||
tile.name.toLowerCase().includes(searchQuery.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
|
||||
const { list, containerProps, wrapperProps } = useVirtualList(
|
||||
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
|
||||
filteredTiles,
|
||||
{
|
||||
// Keep `itemHeight` in sync with the item's row.
|
||||
itemHeight: 28,
|
||||
},
|
||||
)
|
||||
|
||||
const virtualList = ref({ scrollTo })
|
||||
|
||||
onMounted(() => {
|
||||
gameStore.connection?.emit('gm:tile:list', {}, (response: Tile[]) => {
|
||||
assetManagerStore.setTileList(response)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</script>
|
Reference in New Issue
Block a user