Skip to content
This repository was archived by the owner on Apr 17, 2022. It is now read-only.

Commit 0dce831

Browse files
committed
feat: Add formatLocale for localized date format
1 parent 8376fbb commit 0dce831

File tree

6 files changed

+52
-35
lines changed

6 files changed

+52
-35
lines changed

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ interface Vue3DatePicker {
172172
openMenuOnFocus?: boolean;
173173
spaceConfirm?: boolean;
174174
monthChangeOnArrows?: boolean;
175+
formatLocale?: Locale;
175176
}
176177

177178
interface PublicMethods extends MethodOptions {

src/Vue3DatePicker/Vue3DatePicker.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
useSlots,
129129
watch,
130130
} from 'vue';
131-
import { getHours, getMinutes, getSeconds } from 'date-fns';
131+
import { getHours, getMinutes, getSeconds, Locale } from 'date-fns';
132132
133133
import DatepickerInput from './components/DatepickerInput.vue';
134134
import DatepickerMenu from './components/DatepickerMenu.vue';
@@ -251,13 +251,16 @@
251251
escClose: { type: Boolean as PropType<boolean>, default: true },
252252
spaceConfirm: { type: Boolean as PropType<boolean>, default: true },
253253
monthChangeOnArrows: { type: Boolean as PropType<boolean>, default: true },
254+
formatLocale: { type: Object as PropType<Locale>, default: null },
254255
});
255256
const slots = useSlots();
256257
const isOpen = ref(false);
257258
const modelValueMap = toRef(props, 'modelValue');
258259
const dpMenuRef = ref(null);
259260
const inputRef = ref(null);
260261
provide('autoApply', props.autoApply);
262+
const formatLocaleRef = computed(() => props.formatLocale);
263+
provide('formatLocale', formatLocaleRef);
261264
262265
onMounted(() => {
263266
parseExternalModelValue(props.modelValue);
@@ -306,6 +309,7 @@
306309
props.is24,
307310
props.enableTimePicker,
308311
props.enableSeconds,
312+
formatLocaleRef,
309313
emit,
310314
);
311315

src/Vue3DatePicker/components/ActionRow.vue

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
</template>
3131

3232
<script lang="ts" setup>
33-
import { computed, PropType } from 'vue';
33+
import { computed, ComputedRef, inject, PropType } from 'vue';
34+
import { Locale } from 'date-fns';
3435
import { IFormat, InternalModuleValue, ITimeValue } from '../interfaces';
3536
import { formatDate, getMonthVal, getTImeForExternal, isValidTime } from '../utils/date-utils';
3637
import { isModelValueRange } from '../utils/type-guard';
@@ -57,7 +58,7 @@
5758
maxTime: { type: Object as PropType<ITimeValue>, default: null },
5859
enableTimePicker: { type: Boolean as PropType<boolean>, default: true },
5960
});
60-
61+
const formatLocale = inject<ComputedRef<Locale>>('formatLocale');
6162
const selectClass = computed(() => ({
6263
dp__action: true,
6364
dp__select: true,
@@ -77,19 +78,20 @@
7778
if (isModelValueRange(props.internalModelValue)) {
7879
if (props.internalModelValue.length === 2 && props.internalModelValue[1]) {
7980
if (props.twoCalendars) {
80-
return `${formatDate(props.internalModelValue[0], props.previewFormat)} - ${formatDate(
81-
props.internalModelValue[1],
81+
return `${formatDate(
82+
props.internalModelValue[0],
8283
props.previewFormat,
83-
)}`;
84+
formatLocale?.value,
85+
)} - ${formatDate(props.internalModelValue[1], props.previewFormat, formatLocale?.value)}`;
8486
}
8587
return [
86-
formatDate(props.internalModelValue[0], props.previewFormat),
87-
formatDate(props.internalModelValue[1], props.previewFormat),
88+
formatDate(props.internalModelValue[0], props.previewFormat, formatLocale?.value),
89+
formatDate(props.internalModelValue[1], props.previewFormat, formatLocale?.value),
8890
];
8991
}
90-
return `${formatDate(props.internalModelValue[0], props.previewFormat)} -`;
92+
return `${formatDate(props.internalModelValue[0], props.previewFormat, formatLocale?.value)} -`;
9193
}
92-
return formatDate(props.internalModelValue, props.previewFormat);
94+
return formatDate(props.internalModelValue, props.previewFormat, formatLocale?.value);
9395
}
9496
if (props.timePicker) {
9597
return props.previewFormat(getTImeForExternal(props.internalModelValue));

src/Vue3DatePicker/components/composition/external-internal-mapper.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Ref, ref, watch } from 'vue';
1+
import { ComputedRef, Ref, ref, watch } from 'vue';
22

33
import {
44
formatDate,
@@ -11,6 +11,7 @@ import {
1111
import { IFormat, ModelValue, VueEmit } from '../../interfaces';
1212
import { isMonth, isRangeArray, isSingle, isTime, isTimeArray } from '../../utils/type-guard';
1313
import { getMonthVal } from '../../utils/date-utils';
14+
import { Locale } from 'date-fns';
1415

1516
interface IExternalInternalMapper {
1617
parseExternalModelValue: (value: ModelValue) => void;
@@ -33,6 +34,7 @@ export const useExternalInternalMapper = (
3334
is24: boolean,
3435
enableTimePicker: boolean,
3536
enableSeconds: boolean,
37+
formatLocale: ComputedRef<Locale>,
3638
emit: VueEmit,
3739
): IExternalInternalMapper => {
3840
const inputValue = ref('');
@@ -91,7 +93,7 @@ export const useExternalInternalMapper = (
9193
inputValue.value = '';
9294
} else if (!format || typeof format === 'string') {
9395
const pattern = getDefaultPattern(format, is24, enableSeconds, monthPicker, timePicker, enableTimePicker);
94-
inputValue.value = formatDate(internalModelValue.value, pattern);
96+
inputValue.value = formatDate(internalModelValue.value, pattern, formatLocale?.value);
9597
} else if (timePicker) {
9698
inputValue.value = format(getTImeForExternal(internalModelValue.value));
9799
} else if (monthPicker) {

src/Vue3DatePicker/utils/date-utils.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
set,
2323
add,
2424
sub,
25+
Locale,
2526
} from 'date-fns';
2627

2728
import { IMonthValue, InternalModuleValue, ITimeValue } from '../interfaces';
@@ -134,12 +135,19 @@ export const getTImeForExternal = (date: Date | Date[]): ITimeValue | ITimeValue
134135
return getTimeVal(date);
135136
};
136137

137-
export const formatDate = (value: Date | Date[], pattern: string): string => {
138+
const formatFn = (value: Date, pattern: string, locale?: Locale | null): string => {
139+
if (locale) {
140+
return format(value, pattern, { locale });
141+
}
142+
return format(value, pattern);
143+
};
144+
145+
export const formatDate = (value: Date | Date[], pattern: string, locale?: Locale | null): string => {
138146
if (Array.isArray(value)) {
139-
return `${format(value[0], pattern)} - ${value[1] ? format(value[1], pattern) : ''}`;
147+
return `${formatFn(value[0], pattern, locale)} - ${value[1] ? formatFn(value[1], pattern, locale) : ''}`;
140148
}
141149

142-
return format(value, pattern);
150+
return formatFn(value, pattern, locale);
143151
};
144152

145153
export const isDateAfter = (date: Date | string | null, dateToCompare: Date | string | null): boolean => {

tests/unit/logic.spec.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22
import { mount, VueWrapper } from '@vue/test-utils';
33
import addDays from 'date-fns/addDays';
4+
import { ja } from 'date-fns/locale';
45

56
import Datepicker from '../../src/Vue3DatePicker/Vue3DatePicker.vue';
67
import DatepickerMenu from '../../src/Vue3DatePicker/components/DatepickerMenu.vue';
@@ -241,24 +242,23 @@ describe('Logic connection', () => {
241242
expect(actionRow.text().includes(format(selected))).toBeTruthy();
242243
});
243244

244-
// todo - improve locale support in v2.4.3
245-
// it('Should format with locale', async () => {
246-
// const selected = new Date(0);
247-
// // The day of the week (E) is locale-sensitive in Japanese.
248-
// // Since epoch time zero (1970/1/1) was a Thursday, the 'Thu' must be localized to '木'.
249-
// const dp = mount(Datepicker, { props: { modelValue: null, format: 'E', locale: 'ja-JP' } });
250-
//
251-
// dp.vm.openMenu();
252-
//
253-
// await dp.vm.$nextTick();
254-
//
255-
// const menu: VueWrapper<any> = dp.findComponent(DatepickerMenu);
256-
// const calendar = menu.findComponent(Calendar);
257-
// calendar.vm.$emit('selectDate', { value: selected, current: true });
258-
// await calendar.vm.$nextTick();
259-
// dp.vm.selectDate();
260-
// await dp.vm.$nextTick();
261-
//
262-
// expect(dp.vm.inputValue).toEqual('木');
263-
// });
245+
it('Should format with locale', async () => {
246+
const selected = new Date(0);
247+
// The day of the week (E) is locale-sensitive in Japanese.
248+
// Since epoch time zero (1970/1/1) was a Thursday, the 'Thu' must be localized to '木'.
249+
const dp = mount(Datepicker, { props: { modelValue: null, format: 'E', locale: 'ja-JP', formatLocale: ja } });
250+
251+
dp.vm.openMenu();
252+
253+
await dp.vm.$nextTick();
254+
255+
const menu: VueWrapper<any> = dp.findComponent(DatepickerMenu);
256+
const calendar = menu.findComponent(Calendar);
257+
calendar.vm.$emit('selectDate', { value: selected, current: true });
258+
await calendar.vm.$nextTick();
259+
dp.vm.selectDate();
260+
await dp.vm.$nextTick();
261+
262+
expect(dp.vm.inputValue).toEqual('木');
263+
});
264264
});

0 commit comments

Comments
 (0)