desktop version supports rtl

This commit is contained in:
MaysWind
2025-08-18 00:45:26 +08:00
parent 4eff3a337f
commit c00770201b
57 changed files with 502 additions and 371 deletions
+2 -2
View File
@@ -307,8 +307,8 @@ init(props.length, props.modelValue);
.pin-code-input input {
text-align: center;
padding-left: 10px;
padding-right: 10px;
padding-inline-start: 10px;
padding-inline-end: 10px;
width: 100%;
height: var(--ebk-pin-code-input-height) !important;
}
@@ -13,6 +13,7 @@ import { type CommonAccountBalanceTrendsChartProps, useAccountBalanceTrendsChart
import { useUserStore } from '@/stores/user.ts';
import type { NameValue } from '@/core/base.ts';
import { TextDirection } from '@/core/text.ts';
import type { ColorValue } from '@/core/color.ts';
import { ThemeType } from '@/core/theme.ts';
import { AccountBalanceTrendChartType } from '@/core/statistics.ts';
@@ -47,11 +48,12 @@ interface AccountBalanceTrendsChartDataItem {
const props = defineProps<DesktopAccountBalanceTrendsChartProps>();
const theme = useTheme();
const { tt, formatAmountToLocalizedNumeralsWithCurrency } = useI18n();
const { tt, getCurrentLanguageTextDirection, formatAmountToLocalizedNumeralsWithCurrency } = useI18n();
const { allDataItems, allDisplayDateRanges } = useAccountBalanceTrendsChartBase(props);
const userStore = useUserStore();
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
const allSeries = computed<AccountBalanceTrendsChartDataItem[]>(() => {
@@ -203,7 +205,7 @@ const chartOptions = computed<object>(() => {
for (let i = 0; i < displayItems.length; i++) {
tooltip += `<div><span class="chart-pointer" style="background-color: #${DEFAULT_CHART_COLORS[i]}"></span>`
+ `<span>${displayItems[i].name}</span><span style="margin-left: 20px; float: right">${displayItems[i].value}</span><br/>`
+ `<span>${displayItems[i].name}</span><span class="ms-5" style="float: inline-end">${displayItems[i].value}</span><br/>`
+ `</div>`;
}
@@ -214,7 +216,7 @@ const chartOptions = computed<object>(() => {
return `${params[0].name}<br/>`
+ '<div><span class="chart-pointer" style="background-color: #' + DEFAULT_CHART_COLORS[0] + '"></span>'
+ `<span>${props.legendName}</span><span style="margin-left: 20px; float: right">${value}</span><br/>`
+ `<span>${props.legendName}</span><span class="ms-5" style="float: inline-end">${value}</span><br/>`
+ '</div>';
}
}
@@ -226,7 +228,8 @@ const chartOptions = computed<object>(() => {
xAxis: [
{
type: 'category',
data: allDisplayDateRanges.value
data: allDisplayDateRanges.value,
inverse: textDirection.value === TextDirection.RTL
}
],
yAxis: [
+4 -4
View File
@@ -13,7 +13,7 @@
<div class="text-no-wrap" v-if="currency && appendText">{{ appendText }}</div>
<v-tooltip :text="tt('Enter formula mode')">
<template v-slot:activator="{ props }">
<v-icon class="ml-2" :icon="mdiCalculatorVariantOutline"
<v-icon class="ms-2" :icon="mdiCalculatorVariantOutline"
@keydown.enter="enterFormulaMode" @keydown.space="enterFormulaMode" @click="enterFormulaMode"
v-bind="props" v-if="enableFormula && !formulaMode"></v-icon>
</template>
@@ -34,14 +34,14 @@
<div class="text-no-wrap" v-if="currency && appendText">{{ appendText }}</div>
<v-tooltip :text="tt('Calculate formula result')">
<template v-slot:activator="{ props }">
<v-icon class="ml-2" color="primary" :icon="mdiCheck"
<v-icon class="ms-2" color="primary" :icon="mdiCheck"
@click="calculateFormula" v-bind="props"
v-if="formulaMode"></v-icon>
</template>
</v-tooltip>
<v-tooltip :text="tt('Exit formula mode')">
<template v-slot:activator="{ props }">
<v-icon class="ml-2" color="secondary" :icon="mdiClose"
<v-icon class="ms-2" color="secondary" :icon="mdiClose"
@click="exitFormulaMode" v-bind="props"
v-if="formulaMode"></v-icon>
</template>
@@ -379,6 +379,6 @@ watch(currentValue, (newValue) => {
}
.text-field-with-colored-label.has-pretend-text .v-field__input {
padding-left: 0.5rem;
padding-inline-start: 0.5rem;
}
</style>
+10 -7
View File
@@ -16,7 +16,7 @@
</template>
<template #no-data>
<div class="color-select-dropdown" ref="dropdownMenu">
<div class="color-select-dropdown px-2" ref="dropdownMenu">
<div class="color-item" :class="{ 'row-has-selected-item': hasSelectedIcon(row) }"
:style="`grid-template-columns: repeat(${itemPerRow}, minmax(0, 1fr));`"
:key="idx" v-for="(row, idx) in allColorRows">
@@ -26,7 +26,9 @@
:icon="mdiSquareRounded" :color="getFinalColor(colorInfo.color)"
v-if="!modelValue || modelValue !== colorInfo.color" />
<v-badge class="right-bottom-icon" color="primary"
location="bottom right" offset-x="8" offset-y="8" :icon="mdiCheck"
offset-x="8" offset-y="8"
:location="`bottom ${textDirection === TextDirection.LTR ? 'right' : 'left'}`"
:icon="mdiCheck"
v-if="modelValue && modelValue === colorInfo.color">
<v-icon class="ma-2" size="28" :icon="mdiSquareRounded" :color="getFinalColor(colorInfo.color)" />
</v-badge>
@@ -41,6 +43,9 @@
<script setup lang="ts">
import { ref, computed, useTemplateRef, nextTick } from 'vue';
import { useI18n } from '@/locales/helpers.ts';
import { TextDirection } from '@/core/text.ts';
import type { ColorValue, ColorInfo } from '@/core/color.ts';
import { DEFAULT_ICON_COLOR } from '@/consts/color.ts';
import { arrayContainsFieldValue } from '@/lib/common.ts';
@@ -64,9 +69,12 @@ const emit = defineEmits<{
(e: 'update:modelValue', value: ColorValue): void;
}>();
const { getCurrentLanguageTextDirection } = useI18n();
const dropdownMenu = useTemplateRef<HTMLElement>('dropdownMenu');
const itemPerRow = ref<number>(props.columnCount || 7);
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const allColorRows = computed<ColorInfo[][]>(() => getColorsInRows(props.allColorInfos, itemPerRow.value));
const color = computed<ColorValue>({
@@ -98,11 +106,6 @@ function onMenuStateChanged(state: boolean): void {
</script>
<style>
.color-select-dropdown {
padding-left: 8px;
padding-right: 8px;
}
.color-select-dropdown .color-item {
display: grid;
}
+2 -2
View File
@@ -350,10 +350,10 @@ function onKeyDown(type: string, e: KeyboardEvent): void {
}
.date-time-select-time-picker-container .v-autocomplete.v-input--density-compact .v-field__append-inner .v-autocomplete__menu-icon {
margin-left: 0;
margin-inline-start: 0;
}
.date-time-select-time-picker-container .v-autocomplete .v-field--appended {
padding-right: 8px;
padding-inline-end: 8px;
}
</style>
+10 -7
View File
@@ -16,7 +16,7 @@
</template>
<template #no-data>
<div class="icon-select-dropdown" ref="dropdownMenu">
<div class="icon-select-dropdown px-2" ref="dropdownMenu">
<div class="icon-item" :class="{ 'row-has-selected-item': hasSelectedIcon(row) }"
:style="`grid-template-columns: repeat(${itemPerRow}, minmax(0, 1fr));`"
:key="idx" v-for="(row, idx) in allIconRows">
@@ -24,7 +24,9 @@
<div class="cursor-pointer" @click="icon = iconInfo.id">
<ItemIcon class="ma-2" icon-type="fixed" :icon-id="iconInfo.icon" :color="color" v-if="!modelValue || modelValue !== iconInfo.id" />
<v-badge class="right-bottom-icon" color="primary"
location="bottom right" offset-x="8" offset-y="10" :icon="mdiCheck"
offset-x="8" offset-y="10"
:location="`bottom ${textDirection === TextDirection.LTR ? 'right' : 'left'}`"
:icon="mdiCheck"
v-if="modelValue && modelValue === iconInfo.id">
<ItemIcon class="ma-2" icon-type="fixed" :icon-id="iconInfo.icon" :color="color" />
</v-badge>
@@ -39,6 +41,9 @@
<script setup lang="ts">
import { ref, computed, useTemplateRef, nextTick } from 'vue';
import { useI18n } from '@/locales/helpers.ts';
import { TextDirection } from '@/core/text.ts';
import type { ColorValue } from '@/core/color.ts';
import type { IconInfo, IconInfoWithId } from '@/core/icon.ts';
import { arrayContainsFieldValue } from '@/lib/common.ts';
@@ -63,9 +68,12 @@ const emit = defineEmits<{
(e: 'update:modelValue', value: string): void;
}>();
const { getCurrentLanguageTextDirection } = useI18n();
const dropdownMenu = useTemplateRef<HTMLElement>('dropdownMenu');
const itemPerRow = ref<number>(props.columnCount || 7);
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const allIconRows = computed<IconInfoWithId[][]>(() => getIconsInRows(props.allIconInfos, itemPerRow.value));
const icon = computed<string>({
@@ -89,11 +97,6 @@ function onMenuStateChanged(state: boolean): void {
</script>
<style>
.icon-select-dropdown {
padding-left: 8px;
padding-right: 8px;
}
.icon-select-dropdown .icon-item {
display: grid;
}
+11 -3
View File
@@ -2,9 +2,9 @@
<i class="item-icon" :class="classes" :style="style" v-if="!hiddenStatus">
<slot></slot>
</i>
<v-badge class="right-bottom-icon" color="secondary"
location="bottom right" offset-y="4" :icon="mdiEyeOffOutline"
v-if="hiddenStatus">
<v-badge class="right-bottom-icon" color="secondary" offset-y="4"
:location="`bottom ${textDirection === TextDirection.LTR ? 'right' : 'left'}`"
:icon="mdiEyeOffOutline" v-if="hiddenStatus">
<i class="item-icon" :class="classes" :style="style">
<slot></slot>
</i>
@@ -15,6 +15,10 @@
import { computed } from 'vue';
import { type CommonIconProps, useItemIconBase } from '@/components/base/ItemIconBase.ts';
import { useI18n } from '@/locales/helpers.ts';
import { TextDirection } from '@/core/text.ts';
import {
mdiEyeOffOutline
} from '@mdi/js';
@@ -25,8 +29,12 @@ interface DesktopItemIconProps extends CommonIconProps {
}
const props = defineProps<DesktopItemIconProps>();
const { getCurrentLanguageTextDirection } = useI18n();
const { style, getAccountIcon, getCategoryIcon } = useItemIconBase(props);
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const classes = computed<string>(() => {
let allClasses = props.class ? (props.class + ' ') : '';
@@ -14,6 +14,7 @@ import { type CommonMonthlyTrendsChartProps, type MonthlyTrendsBarChartClickEven
import { useUserStore } from '@/stores/user.ts';
import { TextDirection } from '@/core/text.ts';
import { type Year1BasedMonth, DateRangeScene } from '@/core/datetime.ts';
import type { ColorValue } from '@/core/color.ts';
import { ThemeType } from '@/core/theme.ts';
@@ -71,7 +72,9 @@ const emit = defineEmits<{
const theme = useTheme();
const { tt,
const {
tt,
getCurrentLanguageTextDirection,
formatUnixTimeToShortYear,
formatYearQuarter,
formatUnixTimeToShortYearMonth,
@@ -86,6 +89,7 @@ const userStore = useUserStore();
const selectedLegends = ref<Record<string, boolean>>({});
const textDirection = computed<TextDirection>(() => getCurrentLanguageTextDirection());
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
const itemsMap = computed<Record<string, Record<string, unknown>>>(() => {
@@ -327,7 +331,7 @@ const chartOptions = computed<object>(() => {
if (displayItems.length === 1 || item.totalAmount !== 0) {
const value = formatAmountToLocalizedNumeralsWithCurrency(item.totalAmount, props.defaultCurrency);
tooltip += '<div><span class="chart-pointer" style="background-color: ' + item.color + '"></span>';
tooltip += `<span>${item.name}</span><span style="margin-left: 20px; float: right">${value}</span><br/>`;
tooltip += `<span>${item.name}</span><span class="ms-5" style="float: inline-end">${value}</span><br/>`;
tooltip += '</div>';
}
}
@@ -336,7 +340,7 @@ const chartOptions = computed<object>(() => {
const displayTotalAmount = formatAmountToLocalizedNumeralsWithCurrency(totalAmount, props.defaultCurrency);
tooltip = '<div style="border-bottom: ' + (isDarkMode.value ? '#eee' : '#333') + ' dashed 1px">'
+ '<span class="chart-pointer" style="background-color: ' + (isDarkMode.value ? '#eee' : '#333') + '"></span>'
+ `<span>${tt('Total Amount')}</span><span style="margin-left: 20px; float: right">${displayTotalAmount}</span><br/>`
+ `<span>${tt('Total Amount')}</span><span class="ms-5" style="float: inline-end">${displayTotalAmount}</span><br/>`
+ '</div>' + tooltip;
}
@@ -365,7 +369,8 @@ const chartOptions = computed<object>(() => {
xAxis: [
{
type: 'category',
data: allDisplayDateRanges.value
data: allDisplayDateRanges.value,
inverse: textDirection.value === TextDirection.RTL
}
],
yAxis: [
+3 -3
View File
@@ -143,13 +143,13 @@ const chartOptions = computed<object>(() => {
let tooltip = `<div><span class="chart-pointer" style="background-color: ${params.color}"></span>`;
if (name) {
tooltip += `<span>${name}</span><br/>`;
tooltip += `<div class="d-inline-flex">${name}</div><br/>`;
}
if (props.showValue) {
tooltip += `<span>${value} (${percent})</span>`;
tooltip += `<div class="d-inline-flex"><span>${value}</span><span class="ms-1">(${percent})</span></div>`;
} else {
tooltip += `<span>${percent}</span>`;
tooltip += `<div class="d-inline-flex">${percent}</div>`;
}
tooltip += '</div>';
@@ -34,7 +34,7 @@
:class="{ 'frequency-value-selected v-list-item--active text-primary': isFrequencyValueSelected(weekDay.type) }"
v-for="weekDay in allWeekDays">
<template #prepend="{ isActive }">
<v-checkbox density="compact" class="mr-1" :model-value="isActive"></v-checkbox>
<v-checkbox density="compact" class="me-1" :model-value="isActive"></v-checkbox>
</template>
</v-list-item>
</v-list>
@@ -44,7 +44,7 @@
:class="{ 'frequency-value-selected v-list-item--active text-primary': isFrequencyValueSelected(monthDay.day) }"
v-for="monthDay in allAvailableMonthDays">
<template #prepend="{ isActive }">
<v-checkbox density="compact" class="mr-1" :model-value="isActive"></v-checkbox>
<v-checkbox density="compact" class="me-1" :model-value="isActive"></v-checkbox>
</template>
</v-list-item>
</v-list>
+5 -5
View File
@@ -14,12 +14,12 @@
<template #selection>
<div class="d-flex align-center text-truncate cursor-pointer">
<span class="text-truncate" v-if="customSelectionPrimaryText">{{ customSelectionPrimaryText }}</span>
<v-icon class="disabled" :icon="mdiChevronRight" size="23" v-if="customSelectionPrimaryText && customSelectionSecondaryText" />
<v-icon class="icon-with-direction disabled" :icon="mdiChevronRight" size="23" v-if="customSelectionPrimaryText && customSelectionSecondaryText" />
<span class="text-truncate" v-if="customSelectionPrimaryText && customSelectionSecondaryText">{{ customSelectionSecondaryText }}</span>
<span class="text-truncate" v-if="!customSelectionPrimaryText && !selectedPrimaryItem && !selectedSecondaryItem">{{ noSelectionText }}</span>
<span class="text-truncate" v-if="!customSelectionPrimaryText && showSelectionPrimaryText && selectedPrimaryItem">{{ selectionPrimaryItemText }}</span>
<v-icon class="disabled" :icon="mdiChevronRight" size="23" v-if="!customSelectionPrimaryText && showSelectionPrimaryText && selectedPrimaryItem && selectedSecondaryItem" />
<ItemIcon class="mr-2" icon-type="account" size="21.5px"
<v-icon class="icon-with-direction disabled" :icon="mdiChevronRight" size="23" v-if="!customSelectionPrimaryText && showSelectionPrimaryText && selectedPrimaryItem && selectedSecondaryItem" />
<ItemIcon class="me-2" icon-type="account" size="21.5px"
:icon-id="selectedSecondaryItem && secondaryIconField ? (selectedSecondaryItem as Record<string, unknown>)[secondaryIconField] : null"
:color="selectedSecondaryItem && secondaryColorField ? (selectedSecondaryItem as Record<string, unknown>)[secondaryColorField] : null"
v-if="!customSelectionPrimaryText && selectedSecondaryItem && showSelectionSecondaryIcon" />
@@ -47,7 +47,7 @@
v-for="item in filteredItems"
@click="onPrimaryItemClicked(item)">
<template #prepend>
<ItemIcon class="mr-2" :icon-type="primaryIconType"
<ItemIcon class="me-2" :icon-type="primaryIconType"
:icon-id="primaryIconField ? (item as Record<string, unknown>)[primaryIconField] : undefined" :color="primaryColorField ? (item as Record<string, unknown>)[primaryColorField] : undefined"></ItemIcon>
</template>
<template #title>
@@ -66,7 +66,7 @@
v-for="subItem in filteredSubItems"
@click="onSecondaryItemClicked(subItem)">
<template #prepend>
<ItemIcon class="mr-2" :icon-type="secondaryIconType"
<ItemIcon class="me-2" :icon-type="secondaryIconType"
:icon-id="secondaryIconField ? subItem[secondaryIconField] : undefined" :color="secondaryColorField ? subItem[secondaryColorField] : undefined"></ItemIcon>
</template>
<template #title>