From bed2aef7f8f0f0b4281aebfb437b817bf539fb7c Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sun, 3 Mar 2024 20:59:37 +0800 Subject: [PATCH] use time picker in datetime selection sheet for mobile device --- .../mobile/DateTimeSelectionSheet.vue | 145 ++++++++++++++++-- src/consts/datetime.js | 30 +++- src/lib/datetime.js | 41 +++++ src/lib/i18n.js | 25 ++- src/lib/ui.mobile.js | 12 ++ 5 files changed, 233 insertions(+), 20 deletions(-) diff --git a/src/components/mobile/DateTimeSelectionSheet.vue b/src/components/mobile/DateTimeSelectionSheet.vue index 9a349e48..fa8194ed 100644 --- a/src/components/mobile/DateTimeSelectionSheet.vue +++ b/src/components/mobile/DateTimeSelectionSheet.vue @@ -16,12 +16,12 @@ month-name-format="long" six-weeks="center" class="justify-content-center" + :enable-time-picker="false" :clearable="false" :dark="isDarkMode" :week-start="firstDayOfWeek" :year-range="yearRange" :day-names="dayNames" - :is24="is24Hour" v-model="dateTime"> @@ -41,14 +42,18 @@ import { mapStores } from 'pinia'; import { useUserStore } from '@/stores/user.js'; +import datetimeConstants from '@/consts/datetime.js'; import { arrangeArrayWithNewStartIndex } from '@/lib/common.js'; import { getCurrentUnixTime, getCurrentDateTime, getUnixTime, getLocalDatetimeFromUnixTime, - getYear + getYear, + getTimeValues, + getCombinedDatetimeByDateAndTimeValues } from '@/lib/datetime.js'; +import { createInlinePicker } from '@/lib/ui.mobile.js'; export default { props: [ @@ -60,19 +65,28 @@ export default { 'update:show' ], data() { + const userStore = useUserStore(); const self = this; + + self.is24Hour = self.$locale.isLongTime24HourFormat(userStore); + self.isMeridiemIndicatorFirst = self.$locale.isLongTimeMeridiemIndicatorFirst(userStore); + self.timePickerHolder = null; + let value = getCurrentUnixTime(); if (self.modelValue) { value = self.modelValue; } + const datetime = getLocalDatetimeFromUnixTime(value); + return { yearRange: [ 2000, getYear(getCurrentDateTime()) + 1 ], - dateTime: getLocalDatetimeFromUnixTime(value), + dateTime: datetime, + timeValues: self.getTimeValues(datetime), } }, computed: { @@ -85,31 +99,55 @@ export default { }, dayNames() { return arrangeArrayWithNewStartIndex(this.$locale.getAllMinWeekdayNames(), this.firstDayOfWeek); - }, - is24Hour() { - return this.$locale.isLongTime24HourFormat(this.userStore); + } + }, + beforeUnmount() { + if (this.timePickerHolder) { + this.timePickerHolder.destroy(); + this.timePickerHolder = null; } }, methods: { onSheetOpen() { - if (this.modelValue) { - this.dateTime = getLocalDatetimeFromUnixTime(this.modelValue) + const self = this; + + if (self.modelValue) { + self.dateTime = getLocalDatetimeFromUnixTime(self.modelValue); } - this.$refs.datetimepicker.switchView('calendar'); + self.timeValues = self.getTimeValues(self.dateTime); + + if (!self.timePickerHolder) { + self.timePickerHolder = createInlinePicker('#time-picker-container', '#time-picker-input', + self.getTimePickerColumns(), self.timeValues, { + change(picker, values) { + self.timeValues = values; + } + }); + } else { + self.timePickerHolder.setValue(self.timeValues); + } + + self.$refs.datetimepicker.switchView('calendar'); }, onSheetClosed() { this.$emit('update:show', false); }, setCurrentTime() { this.dateTime = getLocalDatetimeFromUnixTime(getCurrentUnixTime()) + this.timeValues = this.getTimeValues(this.dateTime); + + if (this.timePickerHolder) { + this.timePickerHolder.setValue(this.timeValues); + } }, confirm() { if (!this.dateTime) { return; } - const unixTime = getUnixTime(this.dateTime); + const finalDatetime = getCombinedDatetimeByDateAndTimeValues(this.dateTime, this.timeValues, this.is24Hour, this.isMeridiemIndicatorFirst); + const unixTime = getUnixTime(finalDatetime); if (unixTime < 0) { this.$toast('Date is too early'); @@ -121,6 +159,83 @@ export default { }, getMonthShortName(month) { return this.$locale.getMonthShortName(month); + }, + getTimeValues(datetime) { + return getTimeValues(datetime, this.is24Hour, this.isMeridiemIndicatorFirst); + }, + getTimePickerColumns() { + const self = this; + const ret = []; + + if (!self.is24Hour && this.isMeridiemIndicatorFirst) { + ret.push({ + values: datetimeConstants.allMeridiemIndicatorsArray, + displayValues: self.$locale.getAllMeridiemIndicatorNames() + }); + } + + // Hours + ret.push({ + values: self.generateAllHours() + }); + // Divider + ret.push({ + divider: true, + content: ':', + }); + // Minutes + ret.push({ + values: self.generateAllMinutesOrSeconds() + }); + // Divider + ret.push({ + divider: true, + content: ':', + }); + // Seconds + ret.push({ + values: self.generateAllMinutesOrSeconds() + }); + + if (!self.is24Hour && !this.isMeridiemIndicatorFirst) { + ret.push({ + values: datetimeConstants.allMeridiemIndicatorsArray, + displayValues: self.$locale.getAllMeridiemIndicatorNames() + }); + } + + return ret; + }, + getDisplayTimeValue(value) { + if (value < 10) { + return `0${value}`; + } else { + return value.toString(); + } + }, + generateAllHours() { + const ret = []; + const startHour = this.is24Hour ? 0 : 1; + const endHour = this.is24Hour ? 23 : 11; + + if (!this.is24Hour) { + ret.push('12'); + } + + for (let i = startHour; i <= endHour; i++) { + ret.push(this.getDisplayTimeValue(i)); + } + + return ret; + }, + generateAllMinutesOrSeconds() { + const ret = []; + + for (let i = 0; i < 60; i++) { + ret.push(this.getDisplayTimeValue(i)); + } + + return ret; } } } @@ -130,4 +245,8 @@ export default { .date-time-selection-sheet .dp__menu { border: 0; } + +.date-time-selection-sheet .time-picker-container .picker-columns { + justify-content: space-evenly; +} diff --git a/src/consts/datetime.js b/src/consts/datetime.js index 0e28f4d1..505d5be4 100644 --- a/src/consts/datetime.js +++ b/src/consts/datetime.js @@ -1,3 +1,13 @@ +const allMeridiemIndicators = { + AM: 'AM', + PM: 'PM' +}; + +const allMeridiemIndicatorsArray = [ + allMeridiemIndicators.AM, + allMeridiemIndicators.PM +]; + const allMonthsArray = [ 'January', 'February', @@ -100,17 +110,20 @@ const allLongTimeFormat = { HHMMSS: { type: 1, key: 'hh_mm_ss', - is24HourFormat: true + is24HourFormat: true, + isMeridiemIndicatorFirst: null }, AHHMMSS: { type: 2, key: 'a_hh_mm_ss', - is24HourFormat: false + is24HourFormat: false, + isMeridiemIndicatorFirst: true }, HHMMSSA: { type: 3, key: 'hh_mm_ss_a', - is24HourFormat: false + is24HourFormat: false, + isMeridiemIndicatorFirst: false } }; @@ -124,17 +137,20 @@ const allShortTimeFormat = { HHMM: { type: 1, key: 'hh_mm', - is24HourFormat: true + is24HourFormat: true, + isMeridiemIndicatorFirst: null }, AHHMM: { type: 2, key: 'a_hh_mm', - is24HourFormat: false + is24HourFormat: false, + isMeridiemIndicatorFirst: true }, HHMMA: { type: 3, key: 'hh_mm_a', - is24HourFormat: false + is24HourFormat: false, + isMeridiemIndicatorFirst: false } }; @@ -203,6 +219,8 @@ const defaultShortTimeFormat = allShortTimeFormat.HHMM; const defaultDateTimeFormatValue = 0; export default { + allMeridiemIndicators: allMeridiemIndicators, + allMeridiemIndicatorsArray: allMeridiemIndicatorsArray, allWeekDays: allWeekDays, allWeekDaysArray: allWeekDaysArray, allMonthsArray: allMonthsArray, diff --git a/src/lib/datetime.js b/src/lib/datetime.js index a354bbc3..a46f6f6c 100644 --- a/src/lib/datetime.js +++ b/src/lib/datetime.js @@ -3,6 +3,14 @@ import moment from 'moment'; import dateTimeConstants from '@/consts/datetime.js'; import { isNumber } from './common.js'; +export function isPM(hour) { + if (hour > 11) { + return true; + } else { + return false; + } +} + export function getUtcOffsetMinutesByUtcOffset(utcOffset) { if (!utcOffset) { return 0; @@ -143,6 +151,10 @@ export function getMonthName(date) { return dateTimeConstants.allMonthsArray[dayOfWeek]; } +export function getAMOrPM(date) { + return isPM(moment(date).hour()) ? dateTimeConstants.allMeridiemIndicators.PM : dateTimeConstants.allMeridiemIndicators.AM; +} + export function getHour(date) { return moment(date).hour(); } @@ -155,6 +167,35 @@ export function getSecond(date) { return moment(date).second(); } +export function getTimeValues(date, is24Hour, isMeridiemIndicatorFirst) { + if (is24Hour) { + return moment(date).format('HH mm ss').split(' '); + } else if (/*!is24Hour && */isMeridiemIndicatorFirst) { + return [getAMOrPM(date)].concat(moment(date).format('hh mm ss').split(' ')); + } else /* !is24Hour && !isMeridiemIndicatorFirst */ { + return moment(date).format('hh mm ss').split(' ').concat([getAMOrPM(date)]); + } +} + +export function getCombinedDatetimeByDateAndTimeValues(date, timeValues, is24Hour, isMeridiemIndicatorFirst) { + const datetime = moment(date); + let time = datetime; + + if (is24Hour) { + time = moment(timeValues.join(' '), 'HH mm ss'); + } else if (/*!is24Hour && */isMeridiemIndicatorFirst) { + time = moment(timeValues.join(' '), 'A HH mm ss'); + } else /* !is24Hour && !isMeridiemIndicatorFirst */ { + time = moment(timeValues.join(' '), 'HH mm ss A'); + } + + datetime.hour(time.hour()); + datetime.minute(time.minute()); + datetime.second(time.second()); + + return datetime; +} + export function getUnixTimeBeforeUnixTime(unixTime, amount, unit) { return moment.unix(unixTime).subtract(amount, unit).unix(); } diff --git a/src/lib/i18n.js b/src/lib/i18n.js index 6659cd35..1bf06f7f 100644 --- a/src/lib/i18n.js +++ b/src/lib/i18n.js @@ -18,6 +18,7 @@ import { } from './common.js'; import { + isPM, parseDateFromUnixTime, formatUnixTime, formatTime, @@ -307,6 +308,13 @@ function getCurrencyName(currencyCode, translateFn) { return translateFn(`currency.${currencyCode}`); } +function getAllMeridiemIndicatorNames(translateFn) { + return [ + translateFn('datetime.AM.content'), + translateFn('datetime.PM.content') + ]; +} + function getAllLongMonthNames(translateFn) { return [ translateFn('datetime.January.long'), @@ -469,12 +477,24 @@ function isLongTime24HourFormat(translateFn, formatTypeValue) { return type.is24HourFormat; } +function isLongTimeMeridiemIndicatorFirst(translateFn, formatTypeValue) { + const defaultLongTimeFormatTypeName = translateFn('default.longTimeFormat'); + const type = getDateTimeFormatType(datetime.allLongTimeFormat, datetime.allLongTimeFormatArray, defaultLongTimeFormatTypeName, datetime.defaultLongTimeFormat, formatTypeValue); + return type.isMeridiemIndicatorFirst; +} + function isShortTime24HourFormat(translateFn, formatTypeValue) { const defaultShortTimeFormatTypeName = translateFn('default.shortTimeFormat'); const type = getDateTimeFormatType(datetime.allShortTimeFormat, datetime.allShortTimeFormatArray, defaultShortTimeFormatTypeName, datetime.defaultShortTimeFormat, formatTypeValue); return type.is24HourFormat; } +function isShortTimeMeridiemIndicatorFirst(translateFn, formatTypeValue) { + const defaultShortTimeFormatTypeName = translateFn('default.shortTimeFormat'); + const type = getDateTimeFormatType(datetime.allShortTimeFormat, datetime.allShortTimeFormatArray, defaultShortTimeFormatTypeName, datetime.defaultShortTimeFormat, formatTypeValue); + return type.isMeridiemIndicatorFirst; +} + function getDateTimeFormats(translateFn, allFormatMap, allFormatArray, localeFormatPathPrefix, localeDefaultFormatTypeName, systemDefaultFormatType) { const defaultFormat = getDateTimeFormat(translateFn, allFormatMap, allFormatArray, localeFormatPathPrefix, localeDefaultFormatTypeName, systemDefaultFormatType, datetime.defaultDateTimeFormatValue); @@ -1149,7 +1169,7 @@ function setLanguage(i18nGlobal, locale, force) { weekdaysShort : getAllShortWeekdayNames(i18nGlobal.t), weekdaysMin : getAllMinWeekdayNames(i18nGlobal.t), meridiem: function (hours) { - if (hours > 11) { + if (isPM(hours)) { return i18nGlobal.t('datetime.PM.content'); } else { return i18nGlobal.t('datetime.AM.content'); @@ -1256,6 +1276,7 @@ export function i18nFunctions(i18nGlobal) { getDefaultCurrency: () => getDefaultCurrency(i18nGlobal.t), getDefaultFirstDayOfWeek: () => getDefaultFirstDayOfWeek(i18nGlobal.t), getCurrencyName: (currencyCode) => getCurrencyName(currencyCode, i18nGlobal.t), + getAllMeridiemIndicatorNames: () => getAllMeridiemIndicatorNames(i18nGlobal.t), getAllLongMonthNames: () => getAllLongMonthNames(i18nGlobal.t), getAllShortMonthNames: () => getAllShortMonthNames(i18nGlobal.t), getAllLongWeekdayNames: () => getAllLongWeekdayNames(i18nGlobal.t), @@ -1284,7 +1305,9 @@ export function i18nFunctions(i18nGlobal) { formatTimeToLongYearMonth: (userStore, dateTime) => formatTime(dateTime, getI18nLongYearMonthFormat(i18nGlobal.t, userStore.currentUserLongDateFormat)), formatTimeToShortYearMonth: (userStore, dateTime) => formatTime(dateTime, getI18nShortYearMonthFormat(i18nGlobal.t, userStore.currentUserShortDateFormat)), isLongTime24HourFormat: (userStore) => isLongTime24HourFormat(i18nGlobal.t, userStore.currentUserLongTimeFormat), + isLongTimeMeridiemIndicatorFirst: (userStore) => isLongTimeMeridiemIndicatorFirst(i18nGlobal.t, userStore.currentUserLongTimeFormat), isShortTime24HourFormat: (userStore) => isShortTime24HourFormat(i18nGlobal.t, userStore.currentUserShortTimeFormat), + isShortTimeMeridiemIndicatorFirst: (userStore) => isShortTimeMeridiemIndicatorFirst(i18nGlobal.t, userStore.currentUserShortTimeFormat), getAllTimezones: (includeSystemDefault) => getAllTimezones(includeSystemDefault, i18nGlobal.t), getTimezoneDifferenceDisplayText: (utcOffset) => getTimezoneDifferenceDisplayText(utcOffset, i18nGlobal.t), getAllCurrencies: () => getAllCurrencies(i18nGlobal.t), diff --git a/src/lib/ui.mobile.js b/src/lib/ui.mobile.js index f31eab4c..f264fa73 100644 --- a/src/lib/ui.mobile.js +++ b/src/lib/ui.mobile.js @@ -73,6 +73,18 @@ export function hideLoading() { }); } +export function createInlinePicker(containerEl, inputEl, cols, value, events) { + return f7.picker.create({ + containerEl: containerEl, + inputEl: inputEl, + toolbar: false, + rotateEffect: true, + value: value, + cols: cols, + on: events || {} + }); +} + export function routeBackOnError(f7router, errorPropertyName) { const self = this; const router = f7router;