mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-21 18:24:26 +08:00
make the query name input field automatically adjust its width to match the text length
This commit is contained in:
@@ -108,6 +108,8 @@ import AccountBalanceTrendsChart from '@/components/desktop/AccountBalanceTrends
|
|||||||
import AccountAndCategorySankeyChart from '@/components/desktop/AccountAndCategorySankeyChart.vue';
|
import AccountAndCategorySankeyChart from '@/components/desktop/AccountAndCategorySankeyChart.vue';
|
||||||
import SwitchToMobileDialog from '@/components/desktop/SwitchToMobileDialog.vue';
|
import SwitchToMobileDialog from '@/components/desktop/SwitchToMobileDialog.vue';
|
||||||
|
|
||||||
|
import TextFieldAutoWidth from '@/directives/desktop/textfieldAutoWidth.ts';
|
||||||
|
|
||||||
import '@/styles/desktop/template/vuetify/index.scss';
|
import '@/styles/desktop/template/vuetify/index.scss';
|
||||||
import '@/styles/desktop/template/template/index.scss';
|
import '@/styles/desktop/template/template/index.scss';
|
||||||
import '@/styles/desktop/template/layout/index.scss';
|
import '@/styles/desktop/template/layout/index.scss';
|
||||||
@@ -550,4 +552,6 @@ app.component('AccountBalanceTrendsChart', AccountBalanceTrendsChart);
|
|||||||
app.component('AccountAndCategorySankeyChart', AccountAndCategorySankeyChart);
|
app.component('AccountAndCategorySankeyChart', AccountAndCategorySankeyChart);
|
||||||
app.component('SwitchToMobileDialog', SwitchToMobileDialog);
|
app.component('SwitchToMobileDialog', SwitchToMobileDialog);
|
||||||
|
|
||||||
|
app.directive('TextFieldAutoWidth', TextFieldAutoWidth);
|
||||||
|
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import type { DirectiveBinding } from 'vue';
|
||||||
|
|
||||||
|
interface AutoWidthOptions {
|
||||||
|
auxSpanId: string;
|
||||||
|
minWidth?: number;
|
||||||
|
maxWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWidth(el: HTMLElement, options: AutoWidthOptions, initStyle: boolean): void {
|
||||||
|
const input = el.querySelector('input');
|
||||||
|
|
||||||
|
if (!input) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auxEl = el.parentElement?.querySelector(`span#${options.auxSpanId}`);
|
||||||
|
|
||||||
|
if (!auxEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const span = auxEl as HTMLSpanElement;
|
||||||
|
|
||||||
|
if (initStyle) {
|
||||||
|
const inputStyle = window.getComputedStyle(input);
|
||||||
|
span.style.position = 'absolute';
|
||||||
|
span.style.visibility = 'hidden';
|
||||||
|
span.style.whiteSpace = 'pre';
|
||||||
|
span.style.font = inputStyle.font;
|
||||||
|
span.style.letterSpacing = inputStyle.letterSpacing;
|
||||||
|
span.style.padding = '0';
|
||||||
|
span.style.margin = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
span.textContent = input.value || input.placeholder || '';
|
||||||
|
|
||||||
|
let width: number = span.offsetWidth;
|
||||||
|
|
||||||
|
if (options.minWidth) {
|
||||||
|
width = Math.max(width, options.minWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.maxWidth) {
|
||||||
|
width = Math.min(width, options.maxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.style.width = `${width}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mounted(el: HTMLElement, binding: DirectiveBinding<AutoWidthOptions>): void {
|
||||||
|
updateWidth(el, binding.value, true);
|
||||||
|
},
|
||||||
|
updated(el: HTMLElement, binding: DirectiveBinding<AutoWidthOptions>): void {
|
||||||
|
updateWidth(el, binding.value, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,13 +15,16 @@
|
|||||||
<v-card border class="card-title-with-bg mt-4">
|
<v-card border class="card-title-with-bg mt-4">
|
||||||
<v-card-title class="d-flex align-center py-2 px-5">
|
<v-card-title class="d-flex align-center py-2 px-5">
|
||||||
<v-icon :icon="mdiTextBoxSearchOutline" size="20" />
|
<v-icon :icon="mdiTextBoxSearchOutline" size="20" />
|
||||||
<span class="text-subtitle-1 ms-2" v-if="editingQuery !== query">{{ query.name || `${tt('Query')} #${queryIndex + 1}` }}</span>
|
<span class="query-name text-subtitle-1 ms-2" v-if="editingQuery !== query">{{ query.name || `${tt('Query')} #${queryIndex + 1}` }}</span>
|
||||||
<div class="query-name-edit ms-2" v-if="editingQuery === query">
|
<div class="query-name-edit ms-2" v-if="editingQuery === query">
|
||||||
<v-text-field type="text" density="compact" variant="underlined"
|
<v-text-field autofocus type="text" density="compact" variant="underlined"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
:placeholder="`${tt('Query')} #${queryIndex + 1}`"
|
:placeholder="`${tt('Query')} #${queryIndex + 1}`"
|
||||||
|
v-text-field-auto-width="{ minWidth: 20, maxWidth: 300, auxSpanId: `query-name-aux-span-${queryIndex + 1}` }"
|
||||||
v-model="editingQueryName"
|
v-model="editingQueryName"
|
||||||
|
@keyup.esc="cancelUpdateQueryName"
|
||||||
@keyup.enter="updateQueryName(query)" />
|
@keyup.enter="updateQueryName(query)" />
|
||||||
|
<span :id="`query-name-aux-span-${queryIndex + 1}`" />
|
||||||
</div>
|
</div>
|
||||||
<v-btn class="ms-2" density="compact" color="primary" variant="text" size="small"
|
<v-btn class="ms-2" density="compact" color="primary" variant="text" size="small"
|
||||||
:icon="true" :disabled="loading"
|
:icon="true" :disabled="loading"
|
||||||
@@ -683,8 +686,11 @@ if (queries.value.length === 0) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.query-name {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
.query-name-edit {
|
.query-name-edit {
|
||||||
width: 200px;
|
|
||||||
height: 36px;
|
height: 36px;
|
||||||
|
|
||||||
> .v-text-field {
|
> .v-text-field {
|
||||||
|
|||||||
Reference in New Issue
Block a user