improve action button rendering performance on desktop version (#547)

This commit is contained in:
MaysWind
2026-04-06 23:31:49 +08:00
parent ae7ee274d5
commit b4c31fc9d0
5 changed files with 236 additions and 239 deletions
+36 -42
View File
@@ -115,7 +115,8 @@
v-model="categories" v-model="categories"
@change="onMove"> @change="onMove">
<template #item="{ element }"> <template #item="{ element }">
<tr class="transaction-category-table-row text-sm" v-if="showHidden || !element.hidden"> <tr class="transaction-category-table-row text-sm" v-if="showHidden || !element.hidden"
@mouseenter="hoveredCategoryId = element.id" @mouseleave="hoveredCategoryId = ''">
<td> <td>
<div class="d-flex align-center"> <div class="d-flex align-center">
<div class="d-flex align-center" :class="{ 'cursor-pointer': isCategorySupportSwitch(element) }" <div class="d-flex align-center" :class="{ 'cursor-pointer': isCategorySupportSwitch(element) }"
@@ -131,42 +132,42 @@
<v-spacer/> <v-spacer/>
<v-btn class="px-2 ms-2" color="default" <template v-if="hoveredCategoryId === element.id && !loading">
density="comfortable" variant="text" <v-btn class="px-2 ms-2" color="default"
:class="{ 'd-none': loading, 'hover-display': !loading }" density="comfortable" variant="text"
:prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline" :prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline"
:loading="categoryHiding[element.id]" :loading="categoryHiding[element.id]"
:disabled="loading || updating" :disabled="loading || updating"
@click="hide(element, !element.hidden)"> @click="hide(element, !element.hidden)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20" width="2"/> <v-progress-circular indeterminate size="20" width="2"/>
</template> </template>
{{ element.hidden ? tt('Show') : tt('Hide') }} {{ element.hidden ? tt('Show') : tt('Hide') }}
</v-btn> </v-btn>
<v-btn class="px-2" color="default" <v-btn class="px-2" color="default"
density="comfortable" variant="text" density="comfortable" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :prepend-icon="mdiPencilOutline"
:prepend-icon="mdiPencilOutline" :disabled="loading || updating"
:disabled="loading || updating" @click="edit(element)">
@click="edit(element)"> {{ tt('Edit') }}
{{ tt('Edit') }} </v-btn>
</v-btn> <v-btn class="px-2" color="default"
<v-btn class="px-2" color="default" density="comfortable" variant="text"
density="comfortable" variant="text" :prepend-icon="mdiDeleteOutline"
:class="{ 'd-none': loading, 'hover-display': !loading }" :loading="categoryRemoving[element.id]"
:prepend-icon="mdiDeleteOutline" :disabled="loading || updating"
:loading="categoryRemoving[element.id]" @click="remove(element)">
:disabled="loading || updating" <template #loader>
@click="remove(element)"> <v-progress-circular indeterminate size="20" width="2"/>
<template #loader> </template>
<v-progress-circular indeterminate size="20" width="2"/> {{ tt('Delete') }}
</template> </v-btn>
{{ tt('Delete') }} </template>
</v-btn>
<span class="ms-2"> <span class="ms-2">
<v-icon :class="!loading && !updating && availableCategoryCount > 1 ? 'drag-handle' : 'disabled'" <v-icon :class="!loading && !updating && availableCategoryCount > 1 ? 'drag-handle' : 'disabled'"
:icon="mdiDrag"/> :icon="mdiDrag"/>
<v-tooltip activator="parent" v-if="!loading && !updating && availableCategoryCount > 1">{{ tt('Drag to Reorder') }}</v-tooltip> <v-tooltip activator="parent" v-if="!loading && !updating && availableCategoryCount > 1 && hoveredCategoryId === element.id">{{ tt('Drag to Reorder') }}</v-tooltip>
</span> </span>
</div> </div>
</td> </td>
@@ -245,6 +246,7 @@ const editDialog = useTemplateRef<EditDialogType>('editDialog');
const activeCategoryType = ref<CategoryType>(CategoryType.Expense); const activeCategoryType = ref<CategoryType>(CategoryType.Expense);
const activeTab = ref<string>('categoryPage'); const activeTab = ref<string>('categoryPage');
const updating = ref<boolean>(false); const updating = ref<boolean>(false);
const hoveredCategoryId = ref<string>('');
const categoryHiding = ref<Record<string, boolean>>({}); const categoryHiding = ref<Record<string, boolean>>({});
const categoryRemoving = ref<Record<string, boolean>>({}); const categoryRemoving = ref<Record<string, boolean>>({});
const displayOrderModified = ref<boolean>(false); const displayOrderModified = ref<boolean>(false);
@@ -496,14 +498,6 @@ reload(false);
</script> </script>
<style> <style>
.transaction-category-table tr.transaction-category-table-row .hover-display {
display: none;
}
.transaction-category-table tr.transaction-category-table-row:hover .hover-display {
display: grid;
}
.transaction-category-table .transaction-category-comment { .transaction-category-table .transaction-category-comment {
font-size: 0.8rem; font-size: 0.8rem;
color: rgba(var(--v-theme-on-background), var(--v-medium-emphasis-opacity)) !important; color: rgba(var(--v-theme-on-background), var(--v-medium-emphasis-opacity)) !important;
+24 -32
View File
@@ -106,7 +106,8 @@
</tr> </tr>
<tr class="exchange-rates-table-row-data" :key="exchangeRate.currencyCode" <tr class="exchange-rates-table-row-data" :key="exchangeRate.currencyCode"
v-for="exchangeRate in availableExchangeRates"> v-for="exchangeRate in availableExchangeRates"
@mouseenter="hoveredCurrency = exchangeRate.currencyCode" @mouseleave="hoveredCurrency = ''">
<td> <td>
<div class="d-flex align-center"> <div class="d-flex align-center">
<span class="text-sm">{{ exchangeRate.currencyDisplayName }}</span> <span class="text-sm">{{ exchangeRate.currencyDisplayName }}</span>
@@ -114,26 +115,27 @@
<v-spacer/> <v-spacer/>
<v-btn class="px-2 ms-2" color="default" <template v-if="hoveredCurrency === exchangeRate.currencyCode && !loading">
density="comfortable" variant="text" <v-btn class="px-2 ms-2" color="default"
:class="{ 'd-none': loading, 'hover-display': !loading }" density="comfortable" variant="text"
v-if="exchangeRate.currencyCode !== baseCurrency" v-if="exchangeRate.currencyCode !== baseCurrency"
@click="setAsBaseline(exchangeRate.currencyCode, getFinalConvertedAmount(exchangeRate, false))"> @click="setAsBaseline(exchangeRate.currencyCode, getFinalConvertedAmount(exchangeRate, false))">
{{ tt('Set as Base') }} {{ tt('Set as Base') }}
</v-btn> </v-btn>
<v-btn class="px-2" color="default" <v-btn class="px-2" color="default"
density="comfortable" variant="text" density="comfortable" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :prepend-icon="mdiDeleteOutline"
:prepend-icon="mdiDeleteOutline" :loading="customExchangeRateRemoving[exchangeRate.currencyCode]"
:loading="customExchangeRateRemoving[exchangeRate.currencyCode]" :disabled="loading || updating"
:disabled="loading || updating" v-if="exchangeRate.currencyCode !== defaultCurrency && isUserCustomExchangeRates"
v-if="exchangeRate.currencyCode !== defaultCurrency && isUserCustomExchangeRates" @click="remove(exchangeRate.currencyCode)">
@click="remove(exchangeRate.currencyCode)"> <template #loader>
<template #loader> <v-progress-circular indeterminate size="20" width="2"/>
<v-progress-circular indeterminate size="20" width="2"/> </template>
</template> {{ tt('Delete') }}
{{ tt('Delete') }} </v-btn>
</v-btn> </template>
<span class="ms-3">{{ getFinalConvertedAmount(exchangeRate, true) }}</span> <span class="ms-3">{{ getFinalConvertedAmount(exchangeRate, true) }}</span>
</div> </div>
</td> </td>
@@ -208,6 +210,7 @@ const updateDialog = useTemplateRef<UpdateDialogType>('updateDialog');
const activeTab = ref<string>('exchangeRatesPage'); const activeTab = ref<string>('exchangeRatesPage');
const loading = ref<boolean>(true); const loading = ref<boolean>(true);
const updating = ref<boolean>(false); const updating = ref<boolean>(false);
const hoveredCurrency = ref<string>('');
const customExchangeRateRemoving = ref<Record<string, boolean>>({}); const customExchangeRateRemoving = ref<Record<string, boolean>>({});
const alwaysShowNav = ref<boolean>(mdAndUp.value); const alwaysShowNav = ref<boolean>(mdAndUp.value);
const showNav = ref<boolean>(mdAndUp.value); const showNav = ref<boolean>(mdAndUp.value);
@@ -332,14 +335,3 @@ watch(mdAndUp, (newValue) => {
reload(false); reload(false);
</script> </script>
<style>
.exchange-rates-table tr.exchange-rates-table-row-data .hover-display {
display: none;
}
.exchange-rates-table tr.exchange-rates-table-row-data:hover .hover-display {
display: grid;
}
</style>
@@ -59,7 +59,8 @@
v-model="allExplorers" v-model="allExplorers"
@change="onMove"> @change="onMove">
<template #item="{ element }"> <template #item="{ element }">
<tr class="explorers-table-row text-sm" v-if="showHidden || !element.hidden"> <tr class="explorers-table-row text-sm" v-if="showHidden || !element.hidden"
@mouseenter="hoveredExplorerId = element.id" @mouseleave="hoveredExplorerId = ''">
<td> <td>
<div class="d-flex align-center"> <div class="d-flex align-center">
<div class="d-flex align-center"> <div class="d-flex align-center">
@@ -68,22 +69,24 @@
<v-spacer/> <v-spacer/>
<v-btn class="px-2 ms-2" color="default" <template v-if="hoveredExplorerId === element.id && !loading">
density="compact" variant="text" <v-btn class="px-2 ms-2" color="default"
:class="{ 'd-none': loading, 'hover-display': !loading }" density="compact" variant="text"
:prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline" :prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline"
:loading="explorerHiding[element.id]" :loading="explorerHiding[element.id]"
:disabled="loading || updating" :disabled="loading || updating"
@click="hide(element, !element.hidden)"> @click="hide(element, !element.hidden)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20" width="2"/> <v-progress-circular indeterminate size="20" width="2"/>
</template> </template>
{{ element.hidden ? tt('Show') : tt('Hide') }} {{ element.hidden ? tt('Show') : tt('Hide') }}
</v-btn> </v-btn>
</template>
<span class="ms-2"> <span class="ms-2">
<v-icon :class="!loading && !updating && !noAvailableExplorer ? 'drag-handle' : 'disabled'" <v-icon :class="!loading && !updating && !noAvailableExplorer ? 'drag-handle' : 'disabled'"
:icon="mdiDrag"/> :icon="mdiDrag"/>
<v-tooltip activator="parent" v-if="!loading && !updating && !noAvailableExplorer">{{ tt('Drag to Reorder') }}</v-tooltip> <v-tooltip activator="parent" v-if="!loading && !updating && !noAvailableExplorer && hoveredExplorerId === element.id">{{ tt('Drag to Reorder') }}</v-tooltip>
</span> </span>
</div> </div>
</td> </td>
@@ -137,6 +140,7 @@ const snackbar = useTemplateRef<SnackBarType>('snackbar');
const showState = ref<boolean>(false); const showState = ref<boolean>(false);
const loading = ref<boolean>(true); const loading = ref<boolean>(true);
const updating = ref<boolean>(false); const updating = ref<boolean>(false);
const hoveredExplorerId = ref<string>('');
const explorerHiding = ref<Record<string, boolean>>({}); const explorerHiding = ref<Record<string, boolean>>({});
const displayOrderModified = ref<boolean>(false); const displayOrderModified = ref<boolean>(false);
const showHidden = ref<boolean>(false); const showHidden = ref<boolean>(false);
@@ -274,13 +278,3 @@ defineExpose({
open open
}); });
</script> </script>
<style>
.explorers-table tr.explorers-table-row .hover-display {
display: none;
}
.explorers-table tr.explorers-table-row:hover .hover-display {
display: inline-grid;
}
</style>
+119 -96
View File
@@ -141,7 +141,8 @@
v-model="tags" v-model="tags"
@change="onMove"> @change="onMove">
<template #item="{ element }"> <template #item="{ element }">
<tr class="transaction-tags-table-row-tag text-sm" v-if="showHidden || !element.hidden"> <tr class="transaction-tags-table-row-tag text-sm" v-if="showHidden || !element.hidden"
@mouseenter="hoveredTagId = element.id" @mouseleave="hoveredTagId = ''">
<td> <td>
<div class="d-flex align-center"> <div class="d-flex align-center">
<div class="d-flex align-center" v-if="editingTag.id !== element.id"> <div class="d-flex align-center" v-if="editingTag.id !== element.id">
@@ -174,95 +175,82 @@
<v-spacer/> <v-spacer/>
<v-btn class="px-2 ms-2" color="default" <template v-if="hoveredTagId === element.id && !loading">
density="comfortable" variant="text" <v-btn class="px-2 ms-2" color="default"
:class="{ 'd-none': loading, 'hover-display': !loading }" density="comfortable" variant="text"
:prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline" :prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline"
:loading="tagHiding[element.id]" :loading="tagHiding[element.id]"
:disabled="loading || updating" :disabled="loading || updating"
v-if="editingTag.id !== element.id" v-if="editingTag.id !== element.id"
@click="hide(element, !element.hidden)"> @click="hide(element, !element.hidden)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20" width="2"/> <v-progress-circular indeterminate size="20" width="2"/>
</template> </template>
{{ element.hidden ? tt('Show') : tt('Hide') }} {{ element.hidden ? tt('Show') : tt('Hide') }}
</v-btn> </v-btn>
<v-btn class="px-2" color="default" <v-btn class="px-2" color="default"
density="comfortable" variant="text" density="comfortable" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :prepend-icon="mdiFolderMoveOutline"
:prepend-icon="mdiFolderMoveOutline" :loading="tagMoving[element.id]"
:loading="tagMoving[element.id]" :disabled="loading || updating || allTagGroupsWithDefault.length < 2"
:disabled="loading || updating || allTagGroupsWithDefault.length < 2" v-if="editingTag.id !== element.id"
v-if="editingTag.id !== element.id"> @click="showMoveTagDialog(element)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20" width="2"/> <v-progress-circular indeterminate size="20" width="2"/>
</template> </template>
{{ tt('Move') }} {{ tt('Move') }}
<v-menu activator="parent" max-height="500"> </v-btn>
<v-list> <v-btn class="px-2" color="default"
<v-list-subheader :title="tt('Move to...')"/> density="comfortable" variant="text"
<template :key="tagGroup.id" v-for="tagGroup in allTagGroupsWithDefault"> :prepend-icon="mdiPencilOutline"
<v-list-item class="text-sm" density="compact" :loading="tagUpdating[element.id]"
:value="tagGroup.id" v-if="activeTagGroupId !== tagGroup.id"> :disabled="loading || updating"
<v-list-item-title class="cursor-pointer" v-if="editingTag.id !== element.id"
@click="moveTagToGroup(element, tagGroup.id)"> @click="edit(element)">
<div class="d-flex align-center"> <template #loader>
<span class="text-sm ms-3">{{ tagGroup.name }}</span> <v-progress-circular indeterminate size="20" width="2"/>
</div> </template>
</v-list-item-title> {{ tt('Edit') }}
</v-list-item> </v-btn>
</template> <v-btn class="px-2" color="default"
</v-list> density="comfortable" variant="text"
</v-menu> :prepend-icon="mdiDeleteOutline"
</v-btn> :loading="tagRemoving[element.id]"
<v-btn class="px-2" color="default" :disabled="loading || updating"
density="comfortable" variant="text" v-if="editingTag.id !== element.id"
:class="{ 'd-none': loading, 'hover-display': !loading }" @click="remove(element)">
:prepend-icon="mdiPencilOutline" <template #loader>
:loading="tagUpdating[element.id]" <v-progress-circular indeterminate size="20" width="2"/>
:disabled="loading || updating" </template>
v-if="editingTag.id !== element.id" {{ tt('Delete') }}
@click="edit(element)"> </v-btn>
<template #loader> </template>
<v-progress-circular indeterminate size="20" width="2"/>
</template> <template v-if="editingTag.id === element.id">
{{ tt('Edit') }} <v-btn class="px-2"
</v-btn> density="comfortable" variant="text"
<v-btn class="px-2" color="default" :prepend-icon="mdiCheck"
density="comfortable" variant="text" :loading="tagUpdating[element.id]"
:class="{ 'd-none': loading, 'hover-display': !loading }" :disabled="loading || updating || !isTagModified(element)"
:prepend-icon="mdiDeleteOutline" @click="save(editingTag)">
:loading="tagRemoving[element.id]" <template #loader>
:disabled="loading || updating" <v-progress-circular indeterminate size="20" width="2"/>
v-if="editingTag.id !== element.id" </template>
@click="remove(element)"> {{ tt('Save') }}
<template #loader> </v-btn>
<v-progress-circular indeterminate size="20" width="2"/> <v-btn class="px-2" color="default"
</template> density="comfortable" variant="text"
{{ tt('Delete') }} :prepend-icon="mdiClose"
</v-btn> :disabled="loading || updating"
<v-btn class="px-2" @click="cancelSave(editingTag)">
density="comfortable" variant="text" {{ tt('Cancel') }}
:prepend-icon="mdiCheck" </v-btn>
:loading="tagUpdating[element.id]" </template>
:disabled="loading || updating || !isTagModified(element)"
v-if="editingTag.id === element.id" @click="save(editingTag)">
<template #loader>
<v-progress-circular indeterminate size="20" width="2"/>
</template>
{{ tt('Save') }}
</v-btn>
<v-btn class="px-2" color="default"
density="comfortable" variant="text"
:prepend-icon="mdiClose"
:disabled="loading || updating"
v-if="editingTag.id === element.id" @click="cancelSave(editingTag)">
{{ tt('Cancel') }}
</v-btn>
<span class="ms-2"> <span class="ms-2">
<v-icon :class="!loading && !updating && !hasEditingTag && availableTagCount > 1 ? 'drag-handle' : 'disabled'" <v-icon :class="!loading && !updating && !hasEditingTag && availableTagCount > 1 ? 'drag-handle' : 'disabled'"
:icon="mdiDrag"/> :icon="mdiDrag"/>
<v-tooltip activator="parent" v-if="!loading && !updating && !hasEditingTag && availableTagCount > 1">{{ tt('Drag to Reorder') }}</v-tooltip> <v-tooltip activator="parent" v-if="!loading && !updating && !hasEditingTag && availableTagCount > 1 && hoveredTagId === element.id">{{ tt('Drag to Reorder') }}</v-tooltip>
</span> </span>
</div> </div>
</td> </td>
@@ -319,6 +307,32 @@
</v-col> </v-col>
</v-row> </v-row>
<v-dialog width="640" v-model="showTagMoveToDialog">
<v-card class="pa-sm-1 pa-md-2">
<template #title>
<div class="d-flex align-center">
<h4 class="text-h4">{{ tt('Move to...') }}</h4>
</div>
</template>
<v-card-text class="d-flex flex-column flex-md-row flex-grow-1 overflow-y-auto">
<v-table hover density="comfortable" class="w-100 table-striped">
<tbody>
<tr class="text-sm cursor-pointer" :key="tagGroup.id" v-for="tagGroup in allTagGroupsWithDefault" v-show="activeTagGroupId !== tagGroup.id">
<td @click="moveTagToGroup(currentMovingTag, tagGroup.id)">
<span>{{ tagGroup.name }}</span>
</td>
</tr>
</tbody>
</v-table>
</v-card-text>
<v-card-text class="overflow-y-visible">
<div class="w-100 d-flex justify-center flex-wrap mt-sm-1 mt-md-2 gap-4">
<v-btn color="secondary" variant="tonal" :disabled="loading || updating" @click="showTagMoveToDialog = false">{{ tt('Close') }}</v-btn>
</div>
</v-card-text>
</v-card>
</v-dialog>
<tag-group-change-display-order-dialog ref="tagGroupChangeDisplayOrderDialog" /> <tag-group-change-display-order-dialog ref="tagGroupChangeDisplayOrderDialog" />
<rename-dialog ref="renameDialog" <rename-dialog ref="renameDialog"
@@ -407,10 +421,14 @@ const updating = ref<boolean>(false);
const activeTab = ref<string>('tagListPage'); const activeTab = ref<string>('tagListPage');
const alwaysShowNav = ref<boolean>(display.mdAndUp.value); const alwaysShowNav = ref<boolean>(display.mdAndUp.value);
const showNav = ref<boolean>(display.mdAndUp.value); const showNav = ref<boolean>(display.mdAndUp.value);
const hoveredTagId = ref<string>('');
const tagUpdating = ref<Record<string, boolean>>({}); const tagUpdating = ref<Record<string, boolean>>({});
const tagHiding = ref<Record<string, boolean>>({}); const tagHiding = ref<Record<string, boolean>>({});
const tagMoving = ref<Record<string, boolean>>({}); const tagMoving = ref<Record<string, boolean>>({});
const tagRemoving = ref<Record<string, boolean>>({}); const tagRemoving = ref<Record<string, boolean>>({});
const currentMovingTag = ref<TransactionTag | null>(null);
const currentMoveTargetGroupId = ref<string>(DEFAULT_TAG_GROUP_ID);
const showTagMoveToDialog = ref<boolean>(false);
const totalAvailableTagsCount = computed<number>(() => transactionTagsStore.allAvailableTagsCount); const totalAvailableTagsCount = computed<number>(() => transactionTagsStore.allAvailableTagsCount);
const displayTotalAvailableTagsCount = computed<string>(() => formatNumberToLocalizedNumerals(transactionTagsStore.allAvailableTagsCount)); const displayTotalAvailableTagsCount = computed<string>(() => formatNumberToLocalizedNumerals(transactionTagsStore.allAvailableTagsCount));
@@ -543,7 +561,18 @@ function removeTagGroup(): void {
}); });
} }
function moveTagToGroup(tag: TransactionTag, targetTagGroupId: string): void { function showMoveTagDialog(tag: TransactionTag): void {
currentMovingTag.value = tag;
currentMoveTargetGroupId.value = tag.groupId || DEFAULT_TAG_GROUP_ID;
showTagMoveToDialog.value = true;
}
function moveTagToGroup(tag: TransactionTag | null, targetTagGroupId: string): void {
if (!tag) {
snackbar.value?.showMessage('Unable to move tag');
return;
}
updating.value = true; updating.value = true;
tagMoving.value[tag.id] = true; tagMoving.value[tag.id] = true;
@@ -563,6 +592,8 @@ function moveTagToGroup(tag: TransactionTag, targetTagGroupId: string): void {
snackbar.value?.showError(error); snackbar.value?.showError(error);
} }
}); });
showTagMoveToDialog.value = false;
} }
function save(tag: TransactionTag): void { function save(tag: TransactionTag): void {
@@ -717,14 +748,6 @@ watch(() => display.mdAndUp.value, (newValue) => {
font-size: 1rem; font-size: 1rem;
} }
.transaction-tags-table tr.transaction-tags-table-row-tag .hover-display {
display: none;
}
.transaction-tags-table tr.transaction-tags-table-row-tag:hover .hover-display {
display: inline-grid;
}
.transaction-tags-table tr:not(:last-child) > td > div { .transaction-tags-table tr:not(:last-child) > td > div {
padding-bottom: 1px; padding-bottom: 1px;
} }
+39 -45
View File
@@ -74,7 +74,8 @@
v-model="templates" v-model="templates"
@change="onMove"> @change="onMove">
<template #item="{ element }"> <template #item="{ element }">
<tr class="transaction-templates-table-row text-sm" v-if="showHidden || !element.hidden"> <tr class="transaction-templates-table-row text-sm" v-if="showHidden || !element.hidden"
@mouseenter="hoveredTemplateId = element.id" @mouseleave="hoveredTemplateId = ''">
<td> <td>
<div class="d-flex align-center"> <div class="d-flex align-center">
<div class="d-flex align-center"> <div class="d-flex align-center">
@@ -89,45 +90,45 @@
<v-spacer/> <v-spacer/>
<v-btn class="px-2 ms-2" color="default" <template v-if="hoveredTemplateId === element.id && !loading">
density="comfortable" variant="text" <v-btn class="px-2 ms-2" color="default"
:class="{ 'd-none': loading, 'hover-display': !loading }" density="comfortable" variant="text"
:prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline" :prepend-icon="element.hidden ? mdiEyeOutline : mdiEyeOffOutline"
:loading="templateHiding[element.id]" :loading="templateHiding[element.id]"
:disabled="loading || updating" :disabled="loading || updating"
@click="hide(element, !element.hidden)"> @click="hide(element, !element.hidden)">
<template #loader> <template #loader>
<v-progress-circular indeterminate size="20" width="2"/> <v-progress-circular indeterminate size="20" width="2"/>
</template> </template>
{{ element.hidden ? tt('Show') : tt('Hide') }} {{ element.hidden ? tt('Show') : tt('Hide') }}
</v-btn> </v-btn>
<v-btn class="px-2" color="default" <v-btn class="px-2" color="default"
density="comfortable" variant="text" density="comfortable" variant="text"
:class="{ 'd-none': loading, 'hover-display': !loading }" :prepend-icon="mdiPencilOutline"
:prepend-icon="mdiPencilOutline" :disabled="loading || updating"
:disabled="loading || updating" @click="edit(element)">
@click="edit(element)"> <template #loader>
<template #loader> <v-progress-circular indeterminate size="20" width="2"/>
<v-progress-circular indeterminate size="20" width="2"/> </template>
</template> {{ tt('Edit') }}
{{ tt('Edit') }} </v-btn>
</v-btn> <v-btn class="px-2" color="default"
<v-btn class="px-2" color="default" density="comfortable" variant="text"
density="comfortable" variant="text" :prepend-icon="mdiDeleteOutline"
:class="{ 'd-none': loading, 'hover-display': !loading }" :loading="templateRemoving[element.id]"
:prepend-icon="mdiDeleteOutline" :disabled="loading || updating"
:loading="templateRemoving[element.id]" @click="remove(element)">
:disabled="loading || updating" <template #loader>
@click="remove(element)"> <v-progress-circular indeterminate size="20" width="2"/>
<template #loader> </template>
<v-progress-circular indeterminate size="20" width="2"/> {{ tt('Delete') }}
</template> </v-btn>
{{ tt('Delete') }} </template>
</v-btn>
<span class="ms-2"> <span class="ms-2">
<v-icon :class="!loading && !updating && availableTemplateCount > 1 ? 'drag-handle' : 'disabled'" <v-icon :class="!loading && !updating && availableTemplateCount > 1 ? 'drag-handle' : 'disabled'"
:icon="mdiDrag"/> :icon="mdiDrag"/>
<v-tooltip activator="parent" v-if="!loading && !updating && availableTemplateCount > 1">{{ tt('Drag to Reorder') }}</v-tooltip> <v-tooltip activator="parent" v-if="!loading && !updating && availableTemplateCount > 1 && hoveredTemplateId === element.id">{{ tt('Drag to Reorder') }}</v-tooltip>
</span> </span>
</div> </div>
</td> </td>
@@ -196,6 +197,7 @@ const editDialog = useTemplateRef<EditDialogType>('editDialog');
const templateType = ref<number>(TemplateType.Normal.type); const templateType = ref<number>(TemplateType.Normal.type);
const loading = ref<boolean>(true); const loading = ref<boolean>(true);
const updating = ref<boolean>(false); const updating = ref<boolean>(false);
const hoveredTemplateId = ref<string>('');
const templateHiding = ref<Record<string, boolean>>({}); const templateHiding = ref<Record<string, boolean>>({});
const templateRemoving = ref<Record<string, boolean>>({}); const templateRemoving = ref<Record<string, boolean>>({});
const displayOrderModified = ref<boolean>(false); const displayOrderModified = ref<boolean>(false);
@@ -366,14 +368,6 @@ init();
</script> </script>
<style> <style>
.transaction-templates-table tr.transaction-templates-table-row .hover-display {
display: none;
}
.transaction-templates-table tr.transaction-templates-table-row:hover .hover-display {
display: inline-grid;
}
.transaction-templates-table tr:not(:last-child) > td > div { .transaction-templates-table tr:not(:last-child) > td > div {
padding-bottom: 1px; padding-bottom: 1px;
} }