diff --git a/src/components/gameMaster/mapEditor/partials/MapObjectList.vue b/src/components/gameMaster/mapEditor/partials/MapObjectList.vue index d799305..0316709 100644 --- a/src/components/gameMaster/mapEditor/partials/MapObjectList.vue +++ b/src/components/gameMaster/mapEditor/partials/MapObjectList.vue @@ -1,66 +1,69 @@ <template> - <Modal ref="modalRef" :modal-width="645" :modal-height="260" :bg-style="'none'"> - <template #modalHeader> + <div class="absolute border-0 border-l-2 border-solid border-gray-500 w-1/4 min-w-80 flex flex-col top-0 right-0 z-10 h-dvh bg-gray-800" v-if="isOpen"> + <div class="relative z-10 p-2.5 border-solid border-0 border-b border-gray-500 text-right"> <h3 class="text-lg text-white">Map objects</h3> - </template> - <template #modalBody> - <div class="flex pt-4 pl-4"> - <div class="w-full flex gap-1.5 flex-row"> - <div> - <label class="mb-1.5 font-titles hidden" for="search">Search...</label> - <input @mousedown.stop class="input-field" type="text" name="search" placeholder="Search" v-model="searchQuery" /> + </div> + <div class="overflow-hidden grow relative"> + <div class="absolute w-full h-full top-0 left-0"> + <div class="relative z-10 h-full"> + <div class="flex pt-4 pl-4"> + <div class="w-full flex gap-1.5 flex-row"> + <div> + <label class="mb-1.5 font-titles hidden" for="search">Search...</label> + <input @mousedown.stop class="input-field" type="text" name="search" placeholder="Search" v-model="searchQuery" /> + </div> + </div> </div> - </div> - </div> - <div class="flex flex-col h-full p-4"> - <div class="mb-4 flex flex-wrap gap-2"> - <button v-for="tag in uniqueTags" :key="tag" @click="toggleTag(tag)" class="btn-cyan" :class="{ 'opacity-50': !selectedTags.includes(tag) }"> - {{ tag }} - </button> - </div> - <div class="h-full overflow-auto"> - <div class="flex justify-between flex-wrap gap-2.5 items-center"> - <div v-for="(mapObject, index) in filteredMapObjects" :key="index" class="max-w-1/4 inline-block"> - <img - class="border-2 border-solid max-w-full" - :src="`${config.server_endpoint}/textures/map_objects/${mapObject.id}.png`" - alt="Object" - @click="mapEditor.setSelectedMapObject(mapObject)" - :class="{ - 'cursor-pointer transition-all duration-300': true, - 'border-cyan shadow-lg scale-105': mapEditor.selectedMapObject.value?.id === mapObject.id, - 'border-transparent hover:border-gray-300': mapEditor.selectedMapObject.value?.id !== mapObject.id - }" - /> + <div class="flex flex-col h-[calc(100%_-_170px)] p-4 pb-24"> + <div class="mb-4 flex flex-wrap gap-2"> + <button v-for="tag in uniqueTags" :key="tag" @click="toggleTag(tag)" class="btn-cyan" :class="{ 'opacity-50': !selectedTags.includes(tag) }"> + {{ tag }} + </button> + </div> + <div class="h-full overflow-auto"> + <div class="flex justify-between flex-wrap gap-2.5 items-center"> + <div v-for="(mapObject, index) in filteredMapObjects" :key="index" class="max-w-1/4 inline-block"> + <img + class="border-2 border-solid rounded max-w-full" + :src="`${config.server_endpoint}/textures/map_objects/${mapObject.id}.png`" + alt="Object" + @click="mapEditor.setSelectedMapObject(mapObject)" + :class="{ + 'cursor-pointer transition-all duration-300': true, + 'border-cyan shadow-lg': mapEditor.selectedMapObject.value?.id === mapObject.id, + 'border-transparent hover:border-gray-300': mapEditor.selectedMapObject.value?.id !== mapObject.id + }" + /> + </div> + </div> </div> </div> </div> </div> - </template> - </Modal> + </div> + </div> </template> <script setup lang="ts"> import config from '@/application/config' import type { MapObject } from '@/application/types' -import Modal from '@/components/utilities/Modal.vue' import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { MapObjectStorage } from '@/storage/storages' import { liveQuery } from 'dexie' -import { computed, onMounted, onUnmounted, ref, useTemplateRef } from 'vue' +import { computed, onMounted, onUnmounted, ref } from 'vue' +defineExpose({ + open: () => (isOpen.value = true), + close: () => (isOpen.value = false), + toggle: () => (isOpen.value = !isOpen.value) +}) + +const isOpen = ref(false) const mapObjectStorage = new MapObjectStorage() -const isModalOpen = ref(false) const mapEditor = useMapEditorComposable() const searchQuery = ref('') const selectedTags = ref<string[]>([]) const mapObjectList = ref<MapObject[]>([]) -const modalRef = useTemplateRef('modalRef') - -defineExpose({ - open: () => modalRef.value?.open(), - close: () => modalRef.value?.close() -}) const uniqueTags = computed(() => { const allTags = mapObjectList.value.flatMap((obj) => obj.tags || []) @@ -86,13 +89,12 @@ const toggleTag = (tag: string) => { let subscription: any = null onMounted(() => { - isModalOpen.value = true subscription = liveQuery(() => mapObjectStorage.liveQuery()).subscribe({ next: (result) => { mapObjectList.value = result }, error: (error) => { - console.error('Failed to fetch tiles:', error) + console.error('Failed to fetch objects:', error) } }) }) diff --git a/src/components/gameMaster/mapEditor/partials/TileList.vue b/src/components/gameMaster/mapEditor/partials/TileList.vue index 277425c..a652ea8 100644 --- a/src/components/gameMaster/mapEditor/partials/TileList.vue +++ b/src/components/gameMaster/mapEditor/partials/TileList.vue @@ -1,94 +1,98 @@ <template> - <Modal ref="modalRef" :modal-width="645" :modal-height="600" :bg-style="'none'"> - <template #modalHeader> + <div class="absolute border-0 border-l-2 border-solid border-gray-500 w-1/4 min-w-80 flex flex-col top-0 right-0 z-10 h-dvh bg-gray-800" v-if="isOpen"> + <div class="relative z-10 p-2.5 border-solid border-0 border-b border-gray-500 text-right"> <h3 class="text-lg text-white">Tiles</h3> - </template> - <template #modalBody> - <div class="h-full overflow-auto" v-if="!selectedGroup"> - <div class="flex pt-4 pl-4"> - <div class="w-full flex gap-1.5 flex-row"> - <div> - <label class="mb-1.5 font-titles hidden" for="search">Search...</label> - <input @mousedown.stop class="input-field" type="text" name="search" placeholder="Search" v-model="searchQuery" /> + </div> + <div class="overflow-hidden grow relative"> + <div class="absolute top-0 left-0 h-full w-full"> + <div class="relative z-10 h-full"> + <div class="h-full" v-if="!selectedGroup"> + <div class="flex pt-4 pl-4"> + <div class="w-full flex gap-1.5 flex-row"> + <div> + <label class="mb-1.5 font-titles hidden" for="search">Search...</label> + <input @mousedown.stop class="input-field" type="text" name="search" placeholder="Search" v-model="searchQuery" /> + </div> + </div> </div> - </div> - </div> - <div class="flex flex-col h-full p-4"> - <div class="mb-4 flex flex-wrap gap-2"> - <button v-for="tag in uniqueTags" :key="tag" @click="toggleTag(tag)" class="btn-cyan" :class="{ 'opacity-50': !selectedTags.includes(tag) }"> - {{ tag }} - </button> - </div> - <div class="h-[calc(100%_-_60px)] flex-grow overflow-y-auto"> - <div class="grid grid-cols-8 gap-2 justify-items-center"> - <div v-for="group in groupedTiles" :key="group.parent.id" class="flex flex-col items-center justify-center relative"> - <img - class="max-w-full max-h-full border-2 border-solid cursor-pointer transition-all duration-300" - :src="`${config.server_endpoint}/textures/tiles/${group.parent.id}.png`" - :alt="group.parent.name" - @click="openGroup(group)" - @load="() => processTile(group.parent)" - :class="{ + <div class="flex flex-col h-[calc(100%_-_170px)] p-4 pb-24"> + <div class="mb-4 flex flex-wrap gap-2"> + <button v-for="tag in uniqueTags" :key="tag" @click="toggleTag(tag)" class="btn-cyan" :class="{ 'opacity-50': !selectedTags.includes(tag) }"> + {{ tag }} + </button> + </div> + <div class="h-full flex-grow overflow-y-auto"> + <div class="grid grid-cols-4 gap-2 justify-items-center"> + <div v-for="group in groupedTiles" :key="group.parent.id" class="flex flex-col items-center justify-center relative"> + <img + class="max-w-full max-h-full border-2 border-solid cursor-pointer transition-all duration-300" + :src="`${config.server_endpoint}/textures/tiles/${group.parent.id}.png`" + :alt="group.parent.name" + @click="openGroup(group)" + @load="() => processTile(group.parent)" + :class="{ 'border-cyan shadow-lg scale-105': isActiveTile(group.parent), 'border-transparent hover:border-gray-300': !isActiveTile(group.parent) }" - /> - <span class="text-xs mt-1">{{ getTileCategory(group.parent) }}</span> - <span v-if="group.children.length > 0" class="absolute top-0 right-0 bg-cyan text-white rounded-full w-5 h-5 flex items-center justify-center text-xs"> + /> + <span class="text-xs mt-1">{{ getTileCategory(group.parent) }}</span> + <span v-if="group.children.length > 0" class="absolute top-0 right-0 bg-cyan text-white rounded-full w-5 h-5 flex items-center justify-center text-xs"> {{ group.children.length + 1 }} </span> + </div> + </div> + </div> + </div> + </div> + <div v-else class="h-full overflow-auto"> + <div class="p-4"> + <button @click="closeGroup" class="btn-cyan mb-4">Back to All Tiles</button> + <h4 class="text-lg mb-4">{{ selectedGroup.parent.name }} Group</h4> + <div class="grid grid-cols-8 gap-2 justify-items-center"> + <div class="flex flex-col items-center justify-center"> + <img + class="max-w-full max-h-full border-2 border-solid cursor-pointer transition-all duration-300" + :src="`${config.server_endpoint}/textures/tiles/${selectedGroup.parent.id}.png`" + :alt="selectedGroup.parent.name" + @click="selectTile(selectedGroup.parent.id)" + :class="{ + 'border-cyan shadow-lg scale-105': isActiveTile(selectedGroup.parent), + 'border-transparent hover:border-gray-300': !isActiveTile(selectedGroup.parent) + }" + /> + <span class="text-xs mt-1">{{ getTileCategory(selectedGroup.parent) }}</span> + </div> + <div v-for="childTile in selectedGroup.children" :key="childTile.id" class="flex flex-col items-center justify-center"> + <img + class="max-w-full max-h-full border-2 border-solid cursor-pointer transition-all duration-300" + :src="`${config.server_endpoint}/textures/tiles/${childTile.id}.png`" + :alt="childTile.name" + @click="selectTile(childTile.id)" + :class="{ + 'border-cyan shadow-lg scale-105': isActiveTile(childTile), + 'border-transparent hover:border-gray-300': !isActiveTile(childTile) + }" + /> + <span class="text-xs mt-1">{{ getTileCategory(childTile) }}</span> + </div> </div> </div> </div> </div> </div> - <div v-else class="h-full overflow-auto"> - <div class="p-4"> - <button @click="closeGroup" class="btn-cyan mb-4">Back to All Tiles</button> - <h4 class="text-lg mb-4">{{ selectedGroup.parent.name }} Group</h4> - <div class="grid grid-cols-8 gap-2 justify-items-center"> - <div class="flex flex-col items-center justify-center"> - <img - class="max-w-full max-h-full border-2 border-solid cursor-pointer transition-all duration-300" - :src="`${config.server_endpoint}/textures/tiles/${selectedGroup.parent.id}.png`" - :alt="selectedGroup.parent.name" - @click="selectTile(selectedGroup.parent.id)" - :class="{ - 'border-cyan shadow-lg scale-105': isActiveTile(selectedGroup.parent), - 'border-transparent hover:border-gray-300': !isActiveTile(selectedGroup.parent) - }" - /> - <span class="text-xs mt-1">{{ getTileCategory(selectedGroup.parent) }}</span> - </div> - <div v-for="childTile in selectedGroup.children" :key="childTile.id" class="flex flex-col items-center justify-center"> - <img - class="max-w-full max-h-full border-2 border-solid cursor-pointer transition-all duration-300" - :src="`${config.server_endpoint}/textures/tiles/${childTile.id}.png`" - :alt="childTile.name" - @click="selectTile(childTile.id)" - :class="{ - 'border-cyan shadow-lg scale-105': isActiveTile(childTile), - 'border-transparent hover:border-gray-300': !isActiveTile(childTile) - }" - /> - <span class="text-xs mt-1">{{ getTileCategory(childTile) }}</span> - </div> - </div> - </div> - </div> - </template> - </Modal> + </div> + </div> </template> <script setup lang="ts"> import config from '@/application/config' import type { Tile } from '@/application/types' -import Modal from '@/components/utilities/Modal.vue' import { useMapEditorComposable } from '@/composables/useMapEditorComposable' import { TileStorage } from '@/storage/storages' import { liveQuery } from 'dexie' -import { computed, onMounted, onUnmounted, ref, useTemplateRef } from 'vue' +import { computed, onMounted, onUnmounted, ref } from 'vue' +const isOpen = ref(false) const tileStorage = new TileStorage() const mapEditor = useMapEditorComposable() const searchQuery = ref('') @@ -96,11 +100,11 @@ const selectedTags = ref<string[]>([]) const tileCategories = ref<Map<string, string>>(new Map()) const selectedGroup = ref<{ parent: Tile; children: Tile[] } | null>(null) const tiles = ref<Tile[]>([]) -const modalRef = useTemplateRef('modalRef') defineExpose({ - open: () => modalRef.value?.open(), - close: () => modalRef.value?.close() + open: () => (isOpen.value = true), + close: () => (isOpen.value = false), + toggle: () => (isOpen.value = !isOpen.value) }) const uniqueTags = computed(() => { diff --git a/src/components/gameMaster/mapEditor/partials/Toolbar.vue b/src/components/gameMaster/mapEditor/partials/Toolbar.vue index 9727c3b..a75eb08 100644 --- a/src/components/gameMaster/mapEditor/partials/Toolbar.vue +++ b/src/components/gameMaster/mapEditor/partials/Toolbar.vue @@ -1,6 +1,6 @@ <template> <div class="flex justify-center p-5"> - <div class="toolbar fixed bottom-0 left-0 m-3 rounded flex bg-gray solid border-solid border-2 border-gray-500 text-gray-300 p-1.5 px-3 h-10"> + <div class="toolbar fixed bottom-0 left-0 m-3 rounded flex bg-gray solid border-solid border-2 border-gray-500 text-gray-300 p-1.5 px-3 h-10 z-20"> <div ref="toolbar" class="tools flex gap-2.5" v-if="mapEditor.currentMap.value"> <button class="flex justify-center items-center min-w-10 p-0 relative" :class="{ 'border-0 border-b-[3px] border-solid border-cyan gap-2.5': mapEditor.tool.value === 'move' }" @click="handleClick('move')"> <img class="invert w-5 h-5" src="/assets/icons/mapEditor/move.svg" alt="Move camera" /> <span class="h-5" :class="{ 'ml-2.5': mapEditor.tool.value !== 'move' }">(M)</span> diff --git a/src/components/screens/MapEditor.vue b/src/components/screens/MapEditor.vue index d967abe..1599663 100644 --- a/src/components/screens/MapEditor.vue +++ b/src/components/screens/MapEditor.vue @@ -12,14 +12,14 @@ @open-maps="mapModal?.open" @open-settings="mapSettingsModal?.open" @close-editor="mapEditor.toggleActive" - @close-lists="tileModal?.close" - @closeLists="objectModal?.close" - @open-tile-list="tileModal?.open" - @open-map-object-list="objectModal?.open" + @close-lists="tileList?.close" + @closeLists="objectList?.close" + @open-tile-list="tileList?.open" + @open-map-object-list="objectList?.open" /> <MapList ref="mapModal" @open-create-map="mapSettingsModal?.open" /> - <TileList ref="tileModal" /> - <ObjectList ref="objectModal" /> + <TileList ref="tileList" /> + <ObjectList ref="objectList"/> <MapSettings ref="mapSettingsModal" /> <TeleportModal ref="teleportModal" /> </div> @@ -52,8 +52,8 @@ const gameStore = useGameStore() const toolbar = useTemplateRef('toolbar') const mapModal = useTemplateRef('mapModal') -const tileModal = useTemplateRef('tileModal') -const objectModal = useTemplateRef('objectModal') +const tileList = useTemplateRef('tileList') +const objectList = useTemplateRef('objectList') const mapSettingsModal = useTemplateRef('mapSettingsModal') const teleportModal = useTemplateRef('teleportModal')