freeblox/packages/editor/src/components/EditorToolbar.vue

163 lines
3.6 KiB
Vue

<template>
<div class="toolbar">
<template v-for="item of toolbarItems">
<Menu
:id="item.id"
:items="item.items"
:open-sibling="currentlyOpen"
@toggle="(state, reason) => toggleEvent(item.id, state, reason)"
>{{ item.label }}</Menu
>
</template>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { Editor, SelectionEvent } from '../editor';
import Menu from './menu/Menu.vue';
import { WorldFile, instancableGameObjects } from '@freeblox/engine';
import { useEditorEvents } from '../composables/use-editor-events';
import { exportToFile } from '../utils/export-file';
import { readFileToString } from '../utils/read-file';
const selection = ref(false);
const currentlyOpen = ref<string | undefined>(undefined);
const props = defineProps<{
editor: Editor;
}>();
const emit = defineEmits<{
(e: 'update'): void;
}>();
const { register } = useEditorEvents(props.editor);
const fileMenu = [
{
id: 'new',
label: 'New',
onClick: () => props.editor.events.emit('reset'),
},
{
id: 'saveas',
label: 'Save as...',
onClick: () => saveLevelToFile(),
},
{
id: 'openfile',
label: 'Open file...',
onClick: () => loadLevelFromFile(),
},
];
const editMenu = computed(() => [
{
id: 'undo',
label: 'Undo',
shortcut: 'CTRL+Z',
onClick: () => props.editor.events.emit('undo'),
},
{
id: 'redo',
label: 'Redo',
shortcut: 'CTRL+Y',
onClick: () => props.editor.events.emit('redo'),
},
{
id: 'cut',
label: 'Cut',
shortcut: 'CTRL+X',
disabled: !selection.value,
onClick: () => props.editor.events.emit('cut'),
},
{
id: 'copy',
label: 'Copy',
shortcut: 'CTRL+C',
disabled: !selection.value,
onClick: () => props.editor.events.emit('copy'),
},
{
id: 'paste',
label: 'Paste',
shortcut: 'CTRL+V',
onClick: () => props.editor.events.emit('paste', undefined),
},
{
id: 'duplicate',
label: 'Duplicate',
shortcut: 'CTRL+D',
disabled: !selection.value,
onClick: () => props.editor.events.emit('duplicate'),
},
{
id: 'delete',
label: 'Delete',
shortcut: 'Delete',
disabled: !selection.value,
onClick: () => props.editor.events.emit('delete'),
},
]);
const addMenu = computed(() =>
Object.keys(instancableGameObjects).map((item) => ({
id: item,
label: item,
onClick: () => props.editor.events.emit('insert', item),
}))
);
const toolbarItems = computed(() => [
{
id: 'file',
label: 'File',
items: fileMenu,
},
{
id: 'edit',
label: 'Edit',
items: editMenu.value,
},
{
id: 'add',
label: 'Add',
items: addMenu.value,
},
]);
const toggleEvent = (id: string, state: boolean, reason: 'user' | 'leave') => {
if (state) currentlyOpen.value = id;
else if (reason === 'user') currentlyOpen.value = undefined;
};
const saveLevelToFile = () => {
const levelData = props.editor.export('Game');
const json = JSON.stringify(levelData);
exportToFile(json, 'level.json', 'application/json');
};
const loadLevelFromFile = async () => {
const data = await readFileToString('application/json');
const obj = JSON.parse(data) as WorldFile;
props.editor.load(obj);
};
const setSelectionPreset = (event: SelectionEvent) => {
selection.value = !!event.selection?.length;
};
register('selected', setSelectionPreset);
register('deselected', setSelectionPreset);
</script>
<style lang="scss">
.toolbar {
display: flex;
flex-direction: row;
height: 36px;
background-color: #efefef;
}
</style>