migrate date range selection sheet / dialog to composition API and typescript
This commit is contained in:
+5
-4
@@ -15,6 +15,7 @@ import { useI18n } from '@/locales/helpers.ts';
|
||||
|
||||
import { useRootStore } from '@/stores/index.js';
|
||||
import { useSettingsStore } from '@/stores/setting.ts';
|
||||
import { useEnvironmentsStore } from '@/stores/environment.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
import { useTokensStore } from '@/stores/token.ts';
|
||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
||||
@@ -32,6 +33,7 @@ const { tt, getCurrentLanguageInfo, setLanguage, initLocale } = useI18n();
|
||||
|
||||
const rootStore = useRootStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const environmentsStore = useEnvironmentsStore();
|
||||
const userStore = useUserStore();
|
||||
const tokensStore = useTokensStore();
|
||||
const exchangeRatesStore = useExchangeRatesStore();
|
||||
@@ -102,7 +104,6 @@ const f7params = ref<object>({
|
||||
|
||||
const notification = ref<Notification.Notification | null>(null);
|
||||
|
||||
const isDarkMode = ref<boolean | undefined>(undefined);
|
||||
const hasPushPopupBackdrop = ref<boolean | undefined>(undefined);
|
||||
const hasBackdrop = ref<boolean | undefined>(undefined);
|
||||
const currentNotificationContent = computed<string | null>(() => rootStore.currentNotification);
|
||||
@@ -145,14 +146,14 @@ function onBackdropChanged(element: { push?: boolean, opened?: boolean }): void
|
||||
hasBackdrop.value = element.opened;
|
||||
}
|
||||
|
||||
setThemeColorMeta(isDarkMode.value);
|
||||
setThemeColorMeta(environmentsStore.framework7DarkMode);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setAppFontSize(settingsStore.appSettings.fontSize);
|
||||
|
||||
f7ready((f7) => {
|
||||
isDarkMode.value = f7.darkMode;
|
||||
environmentsStore.framework7DarkMode = f7.darkMode;
|
||||
setThemeColorMeta(f7.darkMode);
|
||||
|
||||
f7.on('actionsOpen', (actions: Actions.Actions) => onBackdropChanged(actions));
|
||||
@@ -177,7 +178,7 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
f7.on('darkModeChange', (darkMode) => {
|
||||
isDarkMode.value = darkMode;
|
||||
environmentsStore.framework7DarkMode = darkMode;
|
||||
setThemeColorMeta(darkMode);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
import { type TimeRangeAndDateType, type PresetDateRange, type UnixTimeRange, DateRange } from '@/core/datetime.ts';
|
||||
import { arrangeArrayWithNewStartIndex } from '@/lib/common.ts';
|
||||
import {
|
||||
getCurrentUnixTime,
|
||||
getCurrentYear,
|
||||
getUnixTime,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getTodayFirstUnixTime,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getActualUnixTimeForStore,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getDateRangeByDateType
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
|
||||
export interface CommonDateRangeSelectionProps {
|
||||
minTime?: number;
|
||||
maxTime?: number;
|
||||
title?: string;
|
||||
hint?: string;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
function getDateRangeFromProps(props: CommonDateRangeSelectionProps): { minDate: number; maxDate: number } {
|
||||
let minDate = getTodayFirstUnixTime();
|
||||
let maxDate = getCurrentUnixTime();
|
||||
|
||||
if (props.minTime) {
|
||||
minDate = props.minTime;
|
||||
}
|
||||
|
||||
if (props.maxTime) {
|
||||
maxDate = props.maxTime;
|
||||
}
|
||||
|
||||
return {
|
||||
minDate,
|
||||
maxDate
|
||||
};
|
||||
}
|
||||
|
||||
export function useDateRangeSelectionBase(props: CommonDateRangeSelectionProps) {
|
||||
const { tt, getAllMinWeekdayNames, formatUnixTimeToLongDateTime, isLongDateMonthAfterYear, isLongTime24HourFormat } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const { minDate, maxDate } = getDateRangeFromProps(props);
|
||||
|
||||
const yearRange = ref<number[]>([
|
||||
2000,
|
||||
getCurrentYear() + 1
|
||||
]);
|
||||
|
||||
const dateRange = ref<Date[]>([
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(minDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(maxDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
]);
|
||||
|
||||
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
|
||||
const dayNames = computed<string[]>(() => arrangeArrayWithNewStartIndex(getAllMinWeekdayNames(), firstDayOfWeek.value));
|
||||
const isYearFirst = computed<boolean>(() => isLongDateMonthAfterYear());
|
||||
const is24Hour = computed<boolean>(() => isLongTime24HourFormat());
|
||||
const beginDateTime = computed<string>(() => {
|
||||
const actualBeginUnixTime = getActualUnixTimeForStore(getUnixTime(dateRange.value[0]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
return formatUnixTimeToLongDateTime(actualBeginUnixTime);
|
||||
});
|
||||
const endDateTime = computed<string>(() => {
|
||||
const actualEndUnixTime = getActualUnixTimeForStore(getUnixTime(dateRange.value[1]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
return formatUnixTimeToLongDateTime(actualEndUnixTime);
|
||||
});
|
||||
const presetRanges = computed<PresetDateRange[]>(() => {
|
||||
const presetRanges:PresetDateRange[] = [];
|
||||
|
||||
[
|
||||
DateRange.Today,
|
||||
DateRange.LastSevenDays,
|
||||
DateRange.LastThirtyDays,
|
||||
DateRange.ThisWeek,
|
||||
DateRange.ThisMonth,
|
||||
DateRange.ThisYear
|
||||
].forEach(dateRangeType => {
|
||||
const dateRange = getDateRangeByDateType(dateRangeType.type, firstDayOfWeek.value) as TimeRangeAndDateType;
|
||||
|
||||
presetRanges.push({
|
||||
label: tt(dateRangeType.name),
|
||||
value: [
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
return presetRanges;
|
||||
});
|
||||
|
||||
function getFinalDateRange(): UnixTimeRange | null {
|
||||
if (!dateRange.value[0] || !dateRange.value[1]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const currentMinDate = dateRange.value[0];
|
||||
const currentMaxDate = dateRange.value[1];
|
||||
|
||||
let minUnixTime = getUnixTime(currentMinDate);
|
||||
let maxUnixTime = getUnixTime(currentMaxDate);
|
||||
|
||||
if (minUnixTime < 0 || maxUnixTime < 0) {
|
||||
throw new Error('Date is too early');
|
||||
}
|
||||
|
||||
minUnixTime = getActualUnixTimeForStore(minUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
maxUnixTime = getActualUnixTimeForStore(maxUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
|
||||
return {
|
||||
minUnixTime,
|
||||
maxUnixTime
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
// states
|
||||
yearRange,
|
||||
dateRange,
|
||||
// computed states
|
||||
dayNames,
|
||||
isYearFirst,
|
||||
is24Hour,
|
||||
beginDateTime,
|
||||
endDateTime,
|
||||
presetRanges,
|
||||
// functions
|
||||
getFinalDateRange
|
||||
};
|
||||
}
|
||||
@@ -19,7 +19,6 @@
|
||||
</template>
|
||||
<v-card-text class="mb-md-4 w-100 d-flex justify-center">
|
||||
<vue-date-picker inline enable-seconds auto-apply
|
||||
ref="datetimepicker"
|
||||
month-name-format="long"
|
||||
six-weeks="center"
|
||||
:clearable="false"
|
||||
@@ -39,186 +38,94 @@
|
||||
{{ getMonthShortName(text) }}
|
||||
</template>
|
||||
<template #am-pm-button="{ toggle, value }">
|
||||
<button class="dp__pm_am_button" tabindex="0" @click="toggle">{{ $t(`datetime.${value}.content`) }}</button>
|
||||
<button class="dp__pm_am_button" tabindex="0" @click="toggle">{{ tt(`datetime.${value}.content`) }}</button>
|
||||
</template>
|
||||
</vue-date-picker>
|
||||
</v-card-text>
|
||||
<v-card-text class="overflow-y-visible">
|
||||
<div class="w-100 d-flex justify-center gap-4">
|
||||
<v-btn :disabled="!dateRange[0] || !dateRange[1]" @click="confirm">{{ $t('OK') }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="cancel">{{ $t('Cancel') }}</v-btn>
|
||||
<v-btn :disabled="!dateRange[0] || !dateRange[1]" @click="confirm">{{ tt('OK') }}</v-btn>
|
||||
<v-btn color="secondary" variant="tonal" @click="cancel">{{ tt('Cancel') }}</v-btn>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup lang="ts">
|
||||
import { computed, watch } from 'vue';
|
||||
import { useTheme } from 'vuetify';
|
||||
|
||||
import { mapStores } from 'pinia';
|
||||
import { type CommonDateRangeSelectionProps, useDateRangeSelectionBase } from '@/components/base/DateRangeSelectionBase.ts';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
|
||||
import { DateRange } from '@/core/datetime.ts';
|
||||
import { ThemeType } from '@/core/theme.ts';
|
||||
import { arrangeArrayWithNewStartIndex } from '@/lib/common.ts';
|
||||
|
||||
import {
|
||||
getCurrentUnixTime,
|
||||
getCurrentYear,
|
||||
getUnixTime,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getTodayFirstUnixTime,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getActualUnixTimeForStore,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getDateRangeByDateType
|
||||
getBrowserTimezoneOffsetMinutes
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'minTime',
|
||||
'maxTime',
|
||||
'title',
|
||||
'hint',
|
||||
'persistent',
|
||||
'show'
|
||||
],
|
||||
emits: [
|
||||
'update:show',
|
||||
'dateRange:change'
|
||||
],
|
||||
data() {
|
||||
const self = this;
|
||||
let minDate = getTodayFirstUnixTime();
|
||||
let maxDate = getCurrentUnixTime();
|
||||
interface DesktopDateRangeSelectionProps extends CommonDateRangeSelectionProps {
|
||||
persistent?: boolean;
|
||||
}
|
||||
|
||||
if (self.minTime) {
|
||||
minDate = self.minTime;
|
||||
const props = defineProps<DesktopDateRangeSelectionProps>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:show', value: boolean): void;
|
||||
(e: 'dateRange:change', minUnixTime: number, maxUnixTime: number): void;
|
||||
(e: 'error', message: string): void;
|
||||
}>();
|
||||
|
||||
const theme = useTheme();
|
||||
const { tt, getMonthShortName } = useI18n();
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const { yearRange, dateRange, dayNames, isYearFirst, is24Hour, beginDateTime, endDateTime, presetRanges, getFinalDateRange } = useDateRangeSelectionBase(props);
|
||||
|
||||
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
|
||||
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
|
||||
const showState = computed<boolean>({
|
||||
get: () => props.show || false,
|
||||
set: (value) => emit('update:show', value)
|
||||
});
|
||||
|
||||
function confirm(): void {
|
||||
try {
|
||||
const finalDateRange = getFinalDateRange();
|
||||
|
||||
if (!finalDateRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.maxTime) {
|
||||
maxDate = self.maxTime;
|
||||
}
|
||||
|
||||
return {
|
||||
yearRange: [
|
||||
2000,
|
||||
getCurrentYear() + 1
|
||||
],
|
||||
dateRange: [
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(minDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(maxDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore),
|
||||
showState: {
|
||||
get: function () {
|
||||
return this.show;
|
||||
},
|
||||
set: function (value) {
|
||||
this.$emit('update:show', value);
|
||||
}
|
||||
},
|
||||
isDarkMode() {
|
||||
return this.globalTheme.global.name.value === ThemeType.Dark;
|
||||
},
|
||||
firstDayOfWeek() {
|
||||
return this.userStore.currentUserFirstDayOfWeek;
|
||||
},
|
||||
dayNames() {
|
||||
return arrangeArrayWithNewStartIndex(this.$locale.getAllMinWeekdayNames(), this.firstDayOfWeek);
|
||||
},
|
||||
isYearFirst() {
|
||||
return this.$locale.isLongDateMonthAfterYear(this.userStore);
|
||||
},
|
||||
is24Hour() {
|
||||
return this.$locale.isLongTime24HourFormat(this.userStore);
|
||||
},
|
||||
beginDateTime() {
|
||||
const actualBeginUnixTime = getActualUnixTimeForStore(getUnixTime(this.dateRange[0]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, actualBeginUnixTime);
|
||||
},
|
||||
endDateTime() {
|
||||
const actualEndUnixTime = getActualUnixTimeForStore(getUnixTime(this.dateRange[1]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, actualEndUnixTime);
|
||||
},
|
||||
presetRanges() {
|
||||
const presetRanges = [];
|
||||
|
||||
[
|
||||
DateRange.Today,
|
||||
DateRange.LastSevenDays,
|
||||
DateRange.LastThirtyDays,
|
||||
DateRange.ThisWeek,
|
||||
DateRange.ThisMonth,
|
||||
DateRange.ThisYear
|
||||
].forEach(dateRangeType => {
|
||||
const dateRange = getDateRangeByDateType(dateRangeType.type, this.firstDayOfWeek);
|
||||
|
||||
presetRanges.push({
|
||||
label: this.$t(dateRangeType.name),
|
||||
value: [
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
return presetRanges;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'minTime': function (newValue) {
|
||||
if (newValue) {
|
||||
this.dateRange[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(newValue, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
}
|
||||
},
|
||||
'maxTime': function (newValue) {
|
||||
if (newValue) {
|
||||
this.dateRange[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(newValue, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
}
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const theme = useTheme();
|
||||
|
||||
return {
|
||||
globalTheme: theme
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
if (!this.dateRange[0] || !this.dateRange[1]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentMinDate = this.dateRange[0];
|
||||
const currentMaxDate = this.dateRange[1];
|
||||
|
||||
let minUnixTime = getUnixTime(currentMinDate);
|
||||
let maxUnixTime = getUnixTime(currentMaxDate);
|
||||
|
||||
if (minUnixTime < 0 || maxUnixTime < 0) {
|
||||
this.$toast('Date is too early');
|
||||
return;
|
||||
}
|
||||
|
||||
minUnixTime = getActualUnixTimeForStore(minUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
maxUnixTime = getActualUnixTimeForStore(maxUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
|
||||
this.$emit('dateRange:change', minUnixTime, maxUnixTime);
|
||||
},
|
||||
cancel() {
|
||||
this.$emit('update:show', false);
|
||||
},
|
||||
getMonthShortName(month) {
|
||||
return this.$locale.getMonthShortName(month);
|
||||
emit('dateRange:change', finalDateRange.minUnixTime, finalDateRange.maxUnixTime);
|
||||
} catch (ex: unknown) {
|
||||
if (ex instanceof Error) {
|
||||
emit('error', ex.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cancel(): void {
|
||||
emit('update:show', false);
|
||||
}
|
||||
|
||||
watch(() => props.minTime, (newValue) => {
|
||||
if (newValue) {
|
||||
dateRange.value[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(newValue, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => props.maxTime, (newValue) => {
|
||||
if (newValue) {
|
||||
dateRange.value[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(newValue, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -36,170 +36,97 @@
|
||||
{{ getMonthShortName(text) }}
|
||||
</template>
|
||||
<template #am-pm-button="{ toggle, value }">
|
||||
<button class="dp__pm_am_button" tabindex="0" @click="toggle">{{ $t(`datetime.${value}.content`) }}</button>
|
||||
<button class="dp__pm_am_button" tabindex="0" @click="toggle">{{ tt(`datetime.${value}.content`) }}</button>
|
||||
</template>
|
||||
</vue-date-picker>
|
||||
<f7-button large fill
|
||||
:class="{ 'disabled': !dateRange[0] || !dateRange[1] }"
|
||||
:text="$t('Continue')"
|
||||
:text="tt('Continue')"
|
||||
@click="confirm">
|
||||
</f7-button>
|
||||
<div class="margin-top text-align-center">
|
||||
<f7-link @click="cancel" :text="$t('Cancel')"></f7-link>
|
||||
<f7-link @click="cancel" :text="tt('Cancel')"></f7-link>
|
||||
</div>
|
||||
</div>
|
||||
</f7-page-content>
|
||||
</f7-sheet>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapStores } from 'pinia';
|
||||
<script setup lang="ts">
|
||||
import { computed, useTemplateRef } from 'vue';
|
||||
import VueDatePicker from '@vuepic/vue-datepicker';
|
||||
|
||||
import { type CommonDateRangeSelectionProps, useDateRangeSelectionBase } from '@/components/base/DateRangeSelectionBase.ts';
|
||||
|
||||
import { useI18n } from '@/locales/helpers.ts';
|
||||
import { useI18nUIComponents } from '@/lib/ui/mobile.ts';
|
||||
import { useEnvironmentsStore } from '@/stores/environment.ts';
|
||||
import { useUserStore } from '@/stores/user.ts';
|
||||
|
||||
import { DateRange } from '@/core/datetime.ts';
|
||||
import { arrangeArrayWithNewStartIndex } from '@/lib/common.ts';
|
||||
import {
|
||||
getCurrentUnixTime,
|
||||
getCurrentYear,
|
||||
getUnixTime,
|
||||
getLocalDatetimeFromUnixTime,
|
||||
getTodayFirstUnixTime,
|
||||
getDummyUnixTimeForLocalUsage,
|
||||
getActualUnixTimeForStore,
|
||||
getTimezoneOffsetMinutes,
|
||||
getBrowserTimezoneOffsetMinutes,
|
||||
getDateRangeByDateType
|
||||
getBrowserTimezoneOffsetMinutes
|
||||
} from '@/lib/datetime.ts';
|
||||
|
||||
export default {
|
||||
props: [
|
||||
'minTime',
|
||||
'maxTime',
|
||||
'title',
|
||||
'hint',
|
||||
'show'
|
||||
],
|
||||
emits: [
|
||||
'update:show',
|
||||
'dateRange:change'
|
||||
],
|
||||
data() {
|
||||
const self = this;
|
||||
let minDate = getTodayFirstUnixTime();
|
||||
let maxDate = getCurrentUnixTime();
|
||||
type VueDatePickerType = InstanceType<typeof VueDatePicker>;
|
||||
|
||||
if (self.minTime) {
|
||||
minDate = self.minTime;
|
||||
const props = defineProps<CommonDateRangeSelectionProps>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:show', value: boolean): void;
|
||||
(e: 'dateRange:change', minUnixTime: number, maxUnixTime: number): void;
|
||||
}>();
|
||||
|
||||
const { tt, getMonthShortName } = useI18n();
|
||||
const { showToast } = useI18nUIComponents();
|
||||
|
||||
const environmentsStore = useEnvironmentsStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const { yearRange, dateRange, dayNames, isYearFirst, is24Hour, beginDateTime, endDateTime, presetRanges, getFinalDateRange } = useDateRangeSelectionBase(props);
|
||||
|
||||
const datetimepicker = useTemplateRef<VueDatePickerType>('datetimepicker');
|
||||
const isDarkMode = computed<boolean>(() => environmentsStore.framework7DarkMode || false);
|
||||
const firstDayOfWeek = computed<number>(() => userStore.currentUserFirstDayOfWeek);
|
||||
|
||||
function confirm(): void {
|
||||
try {
|
||||
const finalDateRange = getFinalDateRange();
|
||||
|
||||
if (!finalDateRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.maxTime) {
|
||||
maxDate = self.maxTime;
|
||||
}
|
||||
|
||||
return {
|
||||
yearRange: [
|
||||
2000,
|
||||
getCurrentYear() + 1
|
||||
],
|
||||
dateRange: [
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(minDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(maxDate, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUserStore),
|
||||
isDarkMode() {
|
||||
return this.$root.isDarkMode;
|
||||
},
|
||||
firstDayOfWeek() {
|
||||
return this.userStore.currentUserFirstDayOfWeek;
|
||||
},
|
||||
dayNames() {
|
||||
return arrangeArrayWithNewStartIndex(this.$locale.getAllMinWeekdayNames(), this.firstDayOfWeek);
|
||||
},
|
||||
isYearFirst() {
|
||||
return this.$locale.isLongDateMonthAfterYear(this.userStore);
|
||||
},
|
||||
is24Hour() {
|
||||
return this.$locale.isLongTime24HourFormat(this.userStore);
|
||||
},
|
||||
beginDateTime() {
|
||||
const actualBeginUnixTime = getActualUnixTimeForStore(getUnixTime(this.dateRange[0]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, actualBeginUnixTime);
|
||||
},
|
||||
endDateTime() {
|
||||
const actualEndUnixTime = getActualUnixTimeForStore(getUnixTime(this.dateRange[1]), getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
return this.$locale.formatUnixTimeToLongDateTime(this.userStore, actualEndUnixTime);
|
||||
},
|
||||
presetRanges() {
|
||||
const presetRanges = [];
|
||||
|
||||
[
|
||||
DateRange.Today,
|
||||
DateRange.LastSevenDays,
|
||||
DateRange.LastThirtyDays,
|
||||
DateRange.ThisWeek,
|
||||
DateRange.ThisMonth,
|
||||
DateRange.ThisYear
|
||||
].forEach(dateRangeType => {
|
||||
const dateRange = getDateRangeByDateType(dateRangeType.type, this.firstDayOfWeek);
|
||||
|
||||
presetRanges.push({
|
||||
label: this.$t(dateRangeType.name),
|
||||
value: [
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes())),
|
||||
getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(dateRange.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()))
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
return presetRanges;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSheetOpen() {
|
||||
if (this.minTime) {
|
||||
this.dateRange[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(this.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
}
|
||||
|
||||
if (this.maxTime) {
|
||||
this.dateRange[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(this.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
}
|
||||
|
||||
window.dispatchEvent(new Event('resize')); // fix vue-datepicker preset max-width
|
||||
this.$refs.datetimepicker.switchView('calendar');
|
||||
},
|
||||
onSheetClosed() {
|
||||
this.$emit('update:show', false);
|
||||
},
|
||||
confirm() {
|
||||
if (!this.dateRange[0] || !this.dateRange[1]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentMinDate = this.dateRange[0];
|
||||
const currentMaxDate = this.dateRange[1];
|
||||
|
||||
let minUnixTime = getUnixTime(currentMinDate);
|
||||
let maxUnixTime = getUnixTime(currentMaxDate);
|
||||
|
||||
if (minUnixTime < 0 || maxUnixTime < 0) {
|
||||
this.$toast('Date is too early');
|
||||
return;
|
||||
}
|
||||
|
||||
minUnixTime = getActualUnixTimeForStore(minUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
maxUnixTime = getActualUnixTimeForStore(maxUnixTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes());
|
||||
|
||||
this.$emit('dateRange:change', minUnixTime, maxUnixTime);
|
||||
},
|
||||
cancel() {
|
||||
this.$emit('update:show', false);
|
||||
},
|
||||
getMonthShortName(month) {
|
||||
return this.$locale.getMonthShortName(month);
|
||||
emit('dateRange:change', finalDateRange.minUnixTime, finalDateRange.maxUnixTime);
|
||||
} catch (ex: unknown) {
|
||||
if (ex instanceof Error) {
|
||||
showToast(ex.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cancel(): void {
|
||||
emit('update:show', false);
|
||||
}
|
||||
|
||||
function onSheetOpen(): void {
|
||||
if (props.minTime) {
|
||||
dateRange.value[0] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(props.minTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
}
|
||||
|
||||
if (props.maxTime) {
|
||||
dateRange.value[1] = getLocalDatetimeFromUnixTime(getDummyUnixTimeForLocalUsage(props.maxTime, getTimezoneOffsetMinutes(), getBrowserTimezoneOffsetMinutes()));
|
||||
}
|
||||
|
||||
window.dispatchEvent(new Event('resize')); // fix vue-datepicker preset max-width
|
||||
|
||||
if (datetimepicker.value) {
|
||||
datetimepicker.value.switchView('calendar');
|
||||
}
|
||||
}
|
||||
|
||||
function onSheetClosed(): void {
|
||||
emit('update:show', false);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -52,6 +52,11 @@ export interface RecentMonthDateRange {
|
||||
readonly month: number;
|
||||
}
|
||||
|
||||
export interface PresetDateRange {
|
||||
readonly label: string;
|
||||
readonly value: Date[];
|
||||
}
|
||||
|
||||
export interface LocalizedRecentMonthDateRange {
|
||||
readonly dateType: number;
|
||||
readonly minTime: number;
|
||||
|
||||
+157
-1
@@ -1,6 +1,8 @@
|
||||
import { useI18n as useVueI18n } from 'vue-i18n';
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import type { TypeAndDisplayName } from '@/core/base.ts';
|
||||
|
||||
import { type LanguageInfo, allLanguages, DEFAULT_LANGUAGE } from '@/locales/index.ts';
|
||||
|
||||
import {
|
||||
@@ -23,6 +25,7 @@ import { KnownErrorCode, SPECIFIED_API_NOT_FOUND_ERRORS, PARAMETERIZED_ERRORS }
|
||||
|
||||
import {
|
||||
isString,
|
||||
isNumber,
|
||||
isObject
|
||||
} from '@/lib/common.ts';
|
||||
|
||||
@@ -235,8 +238,12 @@ export function useI18n() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getLocalizedDateTimeType<T extends DateFormat | TimeFormat>(allFormatMap: Record<string, T>, allFormatArray: T[], formatTypeValue: number, languageDefaultTypeNameKey: string, systemDefaultFormatType: T): T {
|
||||
return getDateTimeFormatType(allFormatMap, allFormatArray, formatTypeValue, t(`default.${languageDefaultTypeNameKey}`), systemDefaultFormatType);
|
||||
}
|
||||
|
||||
function getLocalizedDateTimeFormat<T extends DateFormat | TimeFormat>(type: string, allFormatMap: Record<string, T>, allFormatArray: T[], formatTypeValue: number, languageDefaultTypeNameKey: string, systemDefaultFormatType: T): string {
|
||||
const formatType = getDateTimeFormatType(allFormatMap, allFormatArray, formatTypeValue, t(`default.${languageDefaultTypeNameKey}`), systemDefaultFormatType);
|
||||
const formatType = getLocalizedDateTimeType(allFormatMap, allFormatArray, formatTypeValue, languageDefaultTypeNameKey, systemDefaultFormatType);
|
||||
return t(`format.${type}.${formatType.key}`);
|
||||
}
|
||||
|
||||
@@ -306,6 +313,16 @@ export function useI18n() {
|
||||
return t(finalMessage, parameters);
|
||||
}
|
||||
|
||||
function joinMultiText(textArray: string[]): string {
|
||||
if (!textArray || !textArray.length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const separator = t('format.misc.multiTextJoinSeparator');
|
||||
|
||||
return textArray.join(separator);
|
||||
}
|
||||
|
||||
function getCurrentLanguageTag(): string {
|
||||
return locale.value;
|
||||
}
|
||||
@@ -375,6 +392,129 @@ export function useI18n() {
|
||||
return getAllWeekdayNames('min');
|
||||
}
|
||||
|
||||
function getAllWeekDays(firstDayOfWeek: number): TypeAndDisplayName[] {
|
||||
const ret: TypeAndDisplayName[] = [];
|
||||
const allWeekDays = WeekDay.values();
|
||||
|
||||
if (!isNumber(firstDayOfWeek)) {
|
||||
firstDayOfWeek = WeekDay.DefaultFirstDay.type;
|
||||
}
|
||||
|
||||
for (let i = firstDayOfWeek; i < allWeekDays.length; i++) {
|
||||
const weekDay = allWeekDays[i];
|
||||
|
||||
ret.push({
|
||||
type: weekDay.type,
|
||||
displayName: t(`datetime.${weekDay.name}.long`)
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < firstDayOfWeek; i++) {
|
||||
const weekDay = allWeekDays[i];
|
||||
|
||||
ret.push({
|
||||
type: weekDay.type,
|
||||
displayName: t(`datetime.${weekDay.name}.long`)
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getMonthShortName(monthName: string): string {
|
||||
return t(`datetime.${monthName}.short`);
|
||||
}
|
||||
|
||||
function getMonthLongName(monthName: string): string {
|
||||
return t(`datetime.${monthName}.long`);
|
||||
}
|
||||
|
||||
function getMonthdayOrdinal(monthDay: number): string {
|
||||
return t(`datetime.monthDayOrdinal.${monthDay}`);
|
||||
}
|
||||
|
||||
function getMonthdayShortName(monthDay: number): string {
|
||||
return t('format.misc.monthDay', {
|
||||
ordinal: getMonthdayOrdinal(monthDay)
|
||||
});
|
||||
}
|
||||
|
||||
function getWeekdayShortName(weekDayName: string): string {
|
||||
return t(`datetime.${weekDayName}.short`);
|
||||
}
|
||||
|
||||
function getWeekdayLongName(weekDayName: string): string {
|
||||
return t(`datetime.${weekDayName}.long`);
|
||||
}
|
||||
|
||||
function getMultiMonthdayShortNames(monthDays: number[]): string {
|
||||
if (!monthDays) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (monthDays.length === 1) {
|
||||
return t('format.misc.monthDay', {
|
||||
ordinal: getMonthdayOrdinal(monthDays[0])
|
||||
});
|
||||
} else {
|
||||
return t('format.misc.monthDays', {
|
||||
multiMonthDays: joinMultiText(monthDays.map(monthDay =>
|
||||
t('format.misc.eachMonthDayInMonthDays', {
|
||||
ordinal: getMonthdayOrdinal(monthDay)
|
||||
})))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getMultiWeekdayLongNames(weekdayTypes: number[], firstDayOfWeek?: number): string {
|
||||
const weekdayTypesMap: Record<number, boolean> = {};
|
||||
|
||||
if (!isNumber(firstDayOfWeek)) {
|
||||
firstDayOfWeek = WeekDay.DefaultFirstDay.type;
|
||||
}
|
||||
|
||||
for (let i = 0; i < weekdayTypes.length; i++) {
|
||||
weekdayTypesMap[weekdayTypes[i]] = true;
|
||||
}
|
||||
|
||||
const allWeekDays = getAllWeekDays(firstDayOfWeek as number);
|
||||
const finalWeekdayNames = [];
|
||||
|
||||
for (let i = 0; i < allWeekDays.length; i++) {
|
||||
const weekDay = allWeekDays[i];
|
||||
|
||||
if (weekdayTypesMap[weekDay.type]) {
|
||||
finalWeekdayNames.push(weekDay.displayName);
|
||||
}
|
||||
}
|
||||
|
||||
return joinMultiText(finalWeekdayNames);
|
||||
}
|
||||
|
||||
function isLongDateMonthAfterYear() {
|
||||
return getLocalizedDateTimeType(LongDateFormat.all(), LongDateFormat.values(), userStore.currentUserLongDateFormat, 'longDateFormat', LongDateFormat.Default).isMonthAfterYear;
|
||||
}
|
||||
|
||||
function isShortDateMonthAfterYear() {
|
||||
return getLocalizedDateTimeType(ShortDateFormat.all(), ShortDateFormat.values(), userStore.currentUserShortDateFormat, 'shortDateFormat', ShortDateFormat.Default).isMonthAfterYear;
|
||||
}
|
||||
|
||||
function isLongTime24HourFormat() {
|
||||
return getLocalizedDateTimeType(LongTimeFormat.all(), LongTimeFormat.values(), userStore.currentUserLongTimeFormat, 'longTimeFormat', LongTimeFormat.Default).is24HourFormat;
|
||||
}
|
||||
|
||||
function isLongTimeMeridiemIndicatorFirst() {
|
||||
return getLocalizedDateTimeType(LongTimeFormat.all(), LongTimeFormat.values(), userStore.currentUserLongTimeFormat, 'longTimeFormat', LongTimeFormat.Default).isMeridiemIndicatorFirst;
|
||||
}
|
||||
|
||||
function isShortTime24HourFormat() {
|
||||
return getLocalizedDateTimeType(ShortTimeFormat.all(), ShortTimeFormat.values(), userStore.currentUserShortTimeFormat, 'shortTimeFormat', ShortTimeFormat.Default).is24HourFormat;
|
||||
}
|
||||
|
||||
function isShortTimeMeridiemIndicatorFirst() {
|
||||
return getLocalizedDateTimeType(ShortTimeFormat.all(), ShortTimeFormat.values(), userStore.currentUserShortTimeFormat, 'shortTimeFormat', ShortTimeFormat.Default).isMeridiemIndicatorFirst;
|
||||
}
|
||||
|
||||
function formatYearQuarter(year: number, quarter: number): string {
|
||||
if (1 <= quarter && quarter <= 4) {
|
||||
return t('format.yearQuarter.q' + quarter, {
|
||||
@@ -469,6 +609,7 @@ export function useI18n() {
|
||||
tt: t,
|
||||
ti: translateIf,
|
||||
te: translateError,
|
||||
joinMultiText,
|
||||
getCurrentLanguageTag,
|
||||
getCurrentLanguageInfo,
|
||||
getCurrentLanguageDisplayName,
|
||||
@@ -481,6 +622,21 @@ export function useI18n() {
|
||||
getAllLongWeekdayNames,
|
||||
getAllShortWeekdayNames,
|
||||
getAllMinWeekdayNames,
|
||||
getAllWeekDays,
|
||||
getMonthShortName,
|
||||
getMonthLongName,
|
||||
getMonthdayOrdinal,
|
||||
getMonthdayShortName,
|
||||
getWeekdayShortName,
|
||||
getWeekdayLongName,
|
||||
getMultiMonthdayShortNames,
|
||||
getMultiWeekdayLongNames,
|
||||
isLongDateMonthAfterYear,
|
||||
isShortDateMonthAfterYear,
|
||||
isLongTime24HourFormat,
|
||||
isLongTimeMeridiemIndicatorFirst,
|
||||
isShortTime24HourFormat,
|
||||
isShortTimeMeridiemIndicatorFirst,
|
||||
formatUnixTimeToLongDateTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongDateFormat() + ' ' + getLocalizedLongTimeFormat(), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToShortDateTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortDateFormat() + ' ' + getLocalizedShortTimeFormat(), utcOffset, currentUtcOffset),
|
||||
formatUnixTimeToLongDate: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedLongDateFormat(), utcOffset, currentUtcOffset),
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useEnvironmentsStore = defineStore('environments', () => {
|
||||
const framework7DarkMode = ref<boolean | undefined>(undefined);
|
||||
|
||||
return {
|
||||
// states
|
||||
framework7DarkMode
|
||||
};
|
||||
});
|
||||
@@ -287,7 +287,8 @@
|
||||
:min-time="query.categoricalChartStartTime"
|
||||
:max-time="query.categoricalChartEndTime"
|
||||
v-model:show="showCustomDateRangeDialog"
|
||||
@dateRange:change="setCustomDateFilter" />
|
||||
@dateRange:change="setCustomDateFilter"
|
||||
@error="showError" />
|
||||
|
||||
<month-range-selection-dialog :title="$t('Custom Date Range')"
|
||||
:min-time="query.trendChartStartYearMonth"
|
||||
@@ -1042,6 +1043,9 @@ export default {
|
||||
clickTrendChartItem(item) {
|
||||
this.$router.push(this.getTransactionItemLinkUrl(item.itemId, item.dateRange));
|
||||
},
|
||||
showError(message) {
|
||||
this.$refs.snackbar.showError(message);
|
||||
},
|
||||
getDisplayAmount(amount, currency, textLimit) {
|
||||
amount = this.getDisplayCurrency(amount, currency);
|
||||
|
||||
|
||||
@@ -554,7 +554,8 @@
|
||||
:min-time="customMinDatetime"
|
||||
:max-time="customMaxDatetime"
|
||||
v-model:show="showCustomDateRangeDialog"
|
||||
@dateRange:change="changeCustomDateFilter" />
|
||||
@dateRange:change="changeCustomDateFilter"
|
||||
@error="showError" />
|
||||
<edit-dialog ref="editDialog" type="transaction" :persistent="true" />
|
||||
<import-dialog ref="importDialog" :persistent="true" />
|
||||
|
||||
@@ -1611,6 +1612,9 @@ export default {
|
||||
scrollToSelectedItem(menu.contentEl, 'div.v-list', 'div.v-list-item.list-item-selected');
|
||||
});
|
||||
},
|
||||
showError(message) {
|
||||
this.$refs.snackbar.showError(message);
|
||||
},
|
||||
getDisplayTime(transaction) {
|
||||
return this.$locale.formatUnixTimeToShortTime(this.userStore, transaction.time, transaction.utcOffset, this.currentTimezoneOffsetMinutes);
|
||||
},
|
||||
|
||||
@@ -585,7 +585,8 @@
|
||||
:min-time="filters.minDatetime"
|
||||
:max-time="filters.maxDatetime"
|
||||
v-model:show="showCustomDateRangeDialog"
|
||||
@dateRange:change="changeCustomDateFilter" />
|
||||
@dateRange:change="changeCustomDateFilter"
|
||||
@error="showError" />
|
||||
<batch-replace-dialog ref="batchReplaceDialog" />
|
||||
<confirm-dialog ref="confirmDialog"/>
|
||||
<snack-bar ref="snackbar" />
|
||||
@@ -1568,6 +1569,9 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
showError(message) {
|
||||
this.$refs.snackbar.showError(message);
|
||||
},
|
||||
getAllUsedCategoryNames() {
|
||||
const categoryNames = {};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user