Files
STARTLINER/src/components/ProfileListEntry.vue
2025-04-19 19:48:08 +00:00

177 lines
4.8 KiB
Vue

<script setup lang="ts">
import { ref } from 'vue';
import Button from 'primevue/button';
import InputText from 'primevue/inputtext';
import { useConfirm } from 'primevue/useconfirm';
import * as path from '@tauri-apps/api/path';
import { invoke } from '../invoke';
import { useGeneralStore, usePrfStore } from '../stores';
import { ProfileMeta } from '../types';
const general = useGeneralStore();
const prf = usePrfStore();
const confirmDialog = useConfirm();
const isEditing = ref(false);
const props = defineProps({
p: Object as () => ProfileMeta,
});
if (props.p === undefined) {
throw new Error('Invalid ProfileListEntry');
}
const renameProfile = async (event: KeyboardEvent) => {
if (event.key !== 'Enter') {
return;
}
isEditing.value = false;
if (
event.target !== null &&
'value' in event.target &&
typeof event.target.value === 'string'
) {
const value = event.target.value
.trim()
.replaceAll(' ', '-')
.replaceAll('..', '')
.replaceAll('\\', '')
.replaceAll('/', '');
if (value.length > 0) {
await prf.rename(props.p!, value);
}
}
};
const duplicateProfile = async () => {
await invoke('duplicate_profile', { profile: props.p });
await prf.reloadList();
};
const deleteProfile = async () => {
await invoke('delete_profile', { profile: props.p });
await prf.reloadList();
await prf.reload();
};
const promptDeleteProfile = async () => {
confirmDialog.require({
message: `Are you sure you want to delete ${props.p?.game}-${props.p?.name}?`,
header: 'Delete profile',
accept: deleteProfile,
});
};
const dataExists = ref(false);
path.join(general.dataDir, `profile-${props.p!.game}-${props.p!.name}`).then(
async (p) => {
dataExists.value = await invoke('file_exists', { path: p });
}
);
</script>
<template>
<div class="flex flex-row flex-wrap align-middle gap-2">
<Button
:disabled="
prf.current?.meta.game === p!.game &&
prf.current?.meta.name === p!.name
"
:class="
(p!.game === 'chunithm' ? 'chunithm-button' : 'ongeki-button') +
' ' +
'self-center profile-button'
"
@click="prf.switchTo(p!.game, p!.name)"
>
<div v-if="!isEditing">{{ p!.name }}</div>
<div v-else>
<InputText
unstyled
class="text-center"
:model-value="p!.name"
@vue:mounted="$event?.el?.focus()"
@keyup="renameProfile"
@focusout="isEditing = false"
>
</InputText></div
></Button>
<Button
rounded
icon="pi pi-trash"
severity="danger"
aria-label="remove"
size="small"
class="self-center ml-2"
style="width: 2rem; height: 2rem"
@click="promptDeleteProfile"
/>
<Button
rounded
icon="pi pi-clone"
severity="help"
aria-label="duplicate"
size="small"
class="self-center"
style="width: 2rem; height: 2rem"
@click="duplicateProfile"
/>
<Button
rounded
icon="pi pi-pencil"
severity="help"
aria-label="rename"
size="small"
class="self-center"
style="width: 2rem; height: 2rem"
@click="isEditing = true"
/>
<Button
rounded
icon="pi pi-cog"
severity="help"
aria-label="open-config-directory"
size="small"
class="self-center"
style="width: 2rem; height: 2rem"
@click="
path
.join(general.configDir, `profile-${p!.game}-${p!.name}`)
.then(async (path) => {
await invoke('open_file', { path });
})
"
/>
<Button
v-if="dataExists"
rounded
icon="pi pi-folder"
severity="help"
aria-label="open-data-directory"
size="small"
class="self-center"
style="width: 2rem; height: 2rem"
@click="
path
.join(general.dataDir, `profile-${p!.game}-${p!.name}`)
.then(async (path) => {
await invoke('open_file', { path });
})
"
/>
</div>
</template>
<style lang="css">
.p-tablist-tab-list {
border: none !important;
border-color: transparent !important;
border-radius: 0 !important;
}
</style>