204 lines
9.3 KiB
Vue
204 lines
9.3 KiB
Vue
<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 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>
|
|
</button>
|
|
|
|
<div class="w-px bg-cyan"></div>
|
|
|
|
<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 === 'pencil' }" @click="handleClick('pencil')">
|
|
<img class="invert w-5 h-5" src="/assets/icons/mapEditor/pencil.svg" alt="Pencil" /> <span class="h-5" :class="{ 'ml-2.5': mapEditor.tool.value !== 'pencil' }">(P)</span>
|
|
<div class="select" v-if="mapEditor.tool.value === 'pencil'">
|
|
<div class="select-trigger group capitalize flex gap-3.5" :class="{ open: selectPencilOpen }">
|
|
{{ mapEditor.drawMode.value.replace('_', ' ') }}
|
|
<img class="group-[.open]:rotate-180 invert w-5 h-5 rotate-0 transition ease-in-out duration-200" src="/assets/icons/mapEditor/chevron.svg" alt="" />
|
|
</div>
|
|
<div class="flex flex-col absolute bottom-full mb-5 left-1/2 -translate-x-1/2 bg-gray rounded min-w-28 border border-gray-500 border-solid text-left" v-show="selectPencilOpen && mapEditor.tool.value === 'pencil'">
|
|
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="() => handleModeClick('tile', 'pencil')">
|
|
Tile
|
|
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
|
|
</span>
|
|
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="() => handleModeClick('map_object', 'pencil')">
|
|
Map object
|
|
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
|
|
</span>
|
|
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="() => handleModeClick('teleport', 'pencil')">
|
|
Teleport
|
|
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
|
|
</span>
|
|
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="() => handleModeClick('blocking tile', 'pencil')">Blocking tile</span>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
|
|
<div class="w-px bg-cyan"></div>
|
|
|
|
<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 === 'eraser' }" @click="handleClick('eraser')">
|
|
<img class="invert w-5 h-5" src="/assets/icons/mapEditor/eraser.svg" alt="Eraser" /> <span class="h-5" :class="{ 'ml-2.5': mapEditor.tool.value !== 'eraser' }">(E)</span>
|
|
<div class="select" v-if="mapEditor.tool.value === 'eraser'">
|
|
<div class="select-trigger group capitalize flex gap-3.5" :class="{ open: selectEraserOpen }">
|
|
{{ mapEditor.drawMode.value.replace('_', ' ') }}
|
|
<img class="group-[.open]:rotate-180 invert w-5 h-5 rotate-0 transition ease-in-out duration-200" src="/assets/icons/mapEditor/chevron.svg" />
|
|
</div>
|
|
<div class="flex flex-col absolute bottom-full mb-5 left-1/2 -translate-x-1/2 bg-gray rounded min-w-28 border border-gray-500 border-solid text-left" v-show="selectEraserOpen">
|
|
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="() => handleModeClick('tile', 'eraser')">
|
|
Tile
|
|
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
|
|
</span>
|
|
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="() => handleModeClick('map_object', 'eraser')">
|
|
Map object
|
|
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
|
|
</span>
|
|
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="() => handleModeClick('teleport', 'eraser')">
|
|
Teleport
|
|
<div class="absolute w-4/5 left-1/2 -translate-x-1/2 bottom-0 h-px bg-cyan"></div>
|
|
</span>
|
|
<span class="py-2 px-2.5 relative hover:bg-cyan hover:text-white" @click="() => handleModeClick('blocking tile', 'eraser')">Blocking tile</span>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
|
|
<div class="w-px bg-cyan"></div>
|
|
|
|
<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 === 'paint' }" @click="handleClick('paint')">
|
|
<img class="invert w-5 h-5" src="/assets/icons/mapEditor/paint.svg" alt="Paint bucket" /> <span class="h-5" :class="{ 'ml-2.5': mapEditor.tool.value !== 'paint' }">(B)</span>
|
|
</button>
|
|
|
|
<div class="w-px bg-cyan"></div>
|
|
|
|
<button class="flex justify-center items-center min-w-10 p-0 relative" @click="handleClick('settings')"><img class="invert w-5 h-5" src="/assets/icons/mapEditor/gear.svg" alt="Map settings" /> <span class="h-5 ml-2.5">(Z)</span></button>
|
|
|
|
<div class="w-px bg-cyan"></div>
|
|
|
|
<label class="my-auto gap-0" for="checkbox">Continuous Drawing</label>
|
|
<input @change="handleCheck" v-model="checkboxValue" type="checkbox"/>
|
|
</div>
|
|
|
|
<div class="toolbar fixed bottom-0 right-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 space-x-2">
|
|
<button class="btn-cyan px-3.5" @click="() => emit('open-maps')">Load</button>
|
|
<button class="btn-cyan px-3.5" @click="() => emit('save')" v-if="mapEditor.currentMap.value">Save</button>
|
|
<button class="btn-cyan px-3.5" @click="() => emit('clear')" v-if="mapEditor.currentMap.value">Clear</button>
|
|
<button class="btn-cyan px-3.5" @click="() => emit('close-editor')">Exit</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
|
|
import { onClickOutside } from '@vueuse/core'
|
|
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
|
|
|
const mapEditor = useMapEditorComposable()
|
|
|
|
const emit = defineEmits(['save', 'clear', 'open-maps', 'open-settings', 'close-editor', 'open-tile-list', 'open-map-object-list', 'close-lists'])
|
|
|
|
// track when clicked outside of toolbar items
|
|
const toolbar = ref(null)
|
|
|
|
// track select state
|
|
let selectPencilOpen = ref(false)
|
|
let selectEraserOpen = ref(false)
|
|
|
|
let tileListShown = ref(false)
|
|
let mapObjectListShown = ref(false)
|
|
|
|
let checkboxValue = ref(false)
|
|
|
|
defineExpose({ tileListShown, mapObjectListShown })
|
|
|
|
// drawMode
|
|
function setDrawMode(value: string) {
|
|
if (mapEditor.tool.value === 'paint' || mapEditor.tool.value === 'pencil') {
|
|
emit('close-lists')
|
|
if (value === 'tile') emit('open-tile-list')
|
|
if (value === 'map_object') emit('open-map-object-list')
|
|
}
|
|
|
|
mapEditor.setDrawMode(value)
|
|
selectPencilOpen.value = false
|
|
selectEraserOpen.value = false
|
|
}
|
|
|
|
function setPencilMode() {
|
|
mapEditor.setTool('pencil')
|
|
selectPencilOpen.value = false
|
|
}
|
|
|
|
// drawMode
|
|
function setEraserMode() {
|
|
mapEditor.setTool('eraser')
|
|
selectEraserOpen.value = false
|
|
}
|
|
|
|
function handleCheck() {
|
|
mapEditor.setInputMode(checkboxValue.value ? 'hold' : 'tap')
|
|
}
|
|
|
|
function handleModeClick(mode: string, type: 'pencil' | 'eraser') {
|
|
setDrawMode(mode)
|
|
type === 'pencil' ? setPencilMode() : setEraserMode()
|
|
}
|
|
|
|
function handleClick(tool: string) {
|
|
if (tool === 'settings') {
|
|
emit('open-settings')
|
|
} else {
|
|
mapEditor.setTool(tool)
|
|
}
|
|
|
|
selectPencilOpen.value = tool === 'pencil' ? !selectPencilOpen.value : false
|
|
selectEraserOpen.value = tool === 'eraser' ? !selectEraserOpen.value : false
|
|
}
|
|
|
|
function cycleToolMode(tool: 'pencil' | 'eraser') {
|
|
const modes = ['tile', 'map_object', 'teleport', 'blocking tile']
|
|
const currentIndex = modes.indexOf(mapEditor.drawMode.value)
|
|
const nextIndex = (currentIndex + 1) % modes.length
|
|
const nextMode = modes[nextIndex]
|
|
|
|
setDrawMode(nextMode)
|
|
}
|
|
|
|
function initKeyShortcuts(event: KeyboardEvent) {
|
|
// Check if map is set
|
|
if (!mapEditor.currentMap.value) return
|
|
|
|
// prevent if focused on composables
|
|
if (document.activeElement?.tagName === 'INPUT') return
|
|
|
|
const keyActions: { [key: string]: string } = {
|
|
m: 'move',
|
|
p: 'pencil',
|
|
e: 'eraser',
|
|
b: 'paint',
|
|
z: 'settings'
|
|
}
|
|
|
|
if (keyActions.hasOwnProperty(event.key)) {
|
|
const tool = keyActions[event.key]
|
|
if ((tool === 'pencil' || tool === 'eraser') && mapEditor.tool.value === tool) {
|
|
cycleToolMode(tool)
|
|
} else {
|
|
handleClick(tool)
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleClickOutside() {
|
|
selectPencilOpen.value = false
|
|
selectEraserOpen.value = false
|
|
}
|
|
onClickOutside(toolbar, handleClickOutside)
|
|
|
|
onMounted(() => {
|
|
addEventListener('keydown', initKeyShortcuts)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
removeEventListener('keydown', initKeyShortcuts)
|
|
})
|
|
</script>
|