Skip to content

Commit 309ae0d

Browse files
committed
Implement localization functionality with <LocalizationProvider /> powered by date-io
1 parent 57e0e71 commit 309ae0d

File tree

14 files changed

+185
-5
lines changed

14 files changed

+185
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Ref: https://github.com/mui/mui-x/blob/5dc5bd4b04eecf21c45cf6ea1169727ba5ab3be3/packages/x-date-pickers/src/LocalizationProvider/LocalizationProvider.tsx
2+
3+
import * as React from "react";
4+
import { DateIOFormats } from "@date-io/core/IUtils";
5+
import { IUtils } from "@date-io/core/IUtils";
6+
7+
type DateAdapter<TDate> = IUtils<TDate>;
8+
9+
export interface DateAdapterContextValue<TDate> {
10+
utils: DateAdapter<TDate>;
11+
}
12+
13+
export const DateAdapterContext =
14+
React.createContext<DateAdapterContextValue<unknown> | null>(null);
15+
if (process.env.NODE_ENV !== "production") {
16+
DateAdapterContext.displayName = "DateAdapterContext";
17+
}
18+
19+
export interface LocalizationProviderProps {
20+
children?: React.ReactNode;
21+
/** DateIO adapter class function */
22+
dateAdapter: new (...args: any) => DateAdapter<unknown>;
23+
/** Formats that are used for the child dropdown */
24+
dateFormats?: Partial<DateIOFormats>;
25+
/**
26+
* Date library instance you are using, if it has some global overrides
27+
* ```jsx
28+
* dateLibInstance={momentTimeZone}
29+
* ```
30+
*/
31+
dateLibInstance?: any;
32+
/** Locale for the date library you are using */
33+
locale?: string | object;
34+
}
35+
36+
/**
37+
* @ignore - do not document.
38+
*/
39+
export function LocalizationProvider(props: LocalizationProviderProps) {
40+
const {
41+
children,
42+
dateAdapter: Utils,
43+
dateFormats,
44+
dateLibInstance,
45+
locale,
46+
} = props;
47+
const utils = React.useMemo(
48+
() =>
49+
new Utils({ locale, formats: dateFormats, instance: dateLibInstance }),
50+
[Utils, locale, dateFormats, dateLibInstance]
51+
);
52+
53+
const contextValue: DateAdapterContextValue<unknown> = React.useMemo(() => {
54+
return { utils };
55+
}, [utils]);
56+
57+
return (
58+
<DateAdapterContext.Provider value={contextValue}>
59+
{children}
60+
</DateAdapterContext.Provider>
61+
);
62+
}

src/LocalizationProvider/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./LocalizationProvider";

src/LocalizationProvider/useUtils.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as React from "react";
2+
import { DateAdapterContext } from "./LocalizationProvider";
3+
4+
export const useUtils = () => {
5+
const localization = React.useContext(DateAdapterContext);
6+
7+
if (localization == null) {
8+
return null;
9+
}
10+
11+
return localization.utils;
12+
};

src/adapters/date-fns-jalali.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "@date-io/date-fns-jalali";

src/adapters/date-fns.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "@date-io/date-fns";

src/adapters/dayjs.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "@date-io/dayjs";

src/adapters/jalaali.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "@date-io/jalaali";

src/adapters/luxon.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "@date-io/luxon";

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export {
77
export * from "./use-date-select";
88
export { getDateString } from "./date-string";
99
export * from "./types";
10+
export * from "./LocalizationProvider";

src/use-date-select.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
22
import { range } from "./range";
33
import { compileDateString, parseDateString } from "./date-string";
44
import { Option, Options } from "./types";
5+
import { useUtils } from "./LocalizationProvider/useUtils";
56

67
const DEFAULT_MIN_YEAR = 1960;
78
const DEFAULT_MAX_YEAR = new Date().getFullYear();
@@ -17,9 +18,6 @@ function compileOption(value: string): Option {
1718
return { value, label: value }; // TODO: Be customizable for localization
1819
}
1920

20-
const monthOptions: Options = range(1, 12).map((i) =>
21-
compileOption(convertToSelectValue(i))
22-
);
2321
const dayOptions: Options = range(1, 31).map((i) =>
2422
compileOption(convertToSelectValue(i))
2523
);
@@ -169,6 +167,8 @@ export const useDateSelect = (
169167
}
170168
}, [setDate, value]);
171169

170+
const utils = useUtils();
171+
172172
const yearOptions = useMemo(() => {
173173
const minYear = opts.minYear != null ? opts.minYear : DEFAULT_MIN_YEAR;
174174
const maxYear = opts.maxYear != null ? opts.maxYear : DEFAULT_MAX_YEAR;
@@ -182,6 +182,15 @@ export const useDateSelect = (
182182
return raw;
183183
}, [opts.minYear, opts.maxYear, state.yearValue]);
184184

185+
const monthOptions: Options = useMemo(() => {
186+
return range(1, 12).map((i) => {
187+
const label = utils
188+
? utils.format(new Date(1960, i - 1, 1), "monthShort")
189+
: convertToSelectValue(i);
190+
return { value: convertToSelectValue(i), label };
191+
});
192+
}, [utils]);
193+
185194
return {
186195
yearValue: state.yearValue,
187196
monthValue: state.monthValue,

website/Main.mdx

+6
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ import HideDaySampleCode from "./samples/options/hide-day?raw";
8282

8383
`hideDay` prop to hide the day select.
8484

85+
### Localization
86+
87+
import LocalizationSampleCode from "./samples/options/localization?raw";
88+
89+
<CodePreview initialCode={LocalizationSampleCode} language="tsx" />
90+
8591
### A custom component wrapping the preset component
8692

8793
import ReusedPresetComponentSampleCode from "./samples/options/reused-preset-component?raw";

website/components/CodePreview/scope.ts

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import * as reactYmdDateSelect from "react-ymd-date-select";
55
import * as vanillaPreset from "react-ymd-date-select/presets/vanilla";
66
import * as chakraPreset from "react-ymd-date-select/presets/chakra-ui";
77
import * as muiPreset from "react-ymd-date-select/presets/mui";
8+
import { default as DateFnsAdapter } from "react-ymd-date-select/adapters/date-fns";
9+
import dateFnsFrLocale from "date-fns/locale/fr";
10+
import dateFnsRuLocale from "date-fns/locale/ru";
11+
import dateFnsDeLocale from "date-fns/locale/de";
12+
import dateFnsEnLocale from "date-fns/locale/en-US";
13+
import dateFnsJaLocale from "date-fns/locale/ja";
814

915
export const scope = {
1016
import: {
@@ -15,5 +21,11 @@ export const scope = {
1521
"react-ymd-date-select/presets/vanilla": vanillaPreset,
1622
"react-ymd-date-select/presets/chakra-ui": chakraPreset,
1723
"react-ymd-date-select/presets/material": muiPreset,
24+
"react-ymd-date-select/adapters/date-fns": DateFnsAdapter,
25+
"date-fns/locale/fr": dateFnsFrLocale,
26+
"date-fns/locale/ru": dateFnsRuLocale,
27+
"date-fns/locale/de": dateFnsDeLocale,
28+
"date-fns/locale/en-US": dateFnsEnLocale,
29+
"date-fns/locale/ja": dateFnsJaLocale,
1830
},
1931
};

website/components/EyeCatchDateSelect/index.tsx

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { useState } from "react";
22
import { FormControl, FormErrorMessage, Select } from "@chakra-ui/react";
33
import styled from "@emotion/styled";
4-
import { useDateSelect, getDateString } from "react-ymd-date-select";
4+
import DateFnsAdapter from "@date-io/date-fns";
5+
import {
6+
useDateSelect,
7+
getDateString,
8+
LocalizationProvider,
9+
} from "react-ymd-date-select";
510

611
const dropdownIconColor = "#be5f6f";
712
const errorColor = "#fcdfff";
@@ -75,4 +80,12 @@ function EyeCatchDateSelect() {
7580
);
7681
}
7782

78-
export default EyeCatchDateSelect;
83+
function LocalizedEyeCatchDateSelect() {
84+
return (
85+
<LocalizationProvider dateAdapter={DateFnsAdapter}>
86+
<EyeCatchDateSelect />
87+
</LocalizationProvider>
88+
);
89+
}
90+
91+
export default LocalizedEyeCatchDateSelect;
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { useState } from "react";
2+
import frLocale from "date-fns/locale/fr";
3+
import ruLocale from "date-fns/locale/ru";
4+
import deLocale from "date-fns/locale/de";
5+
import enLocale from "date-fns/locale/en-US";
6+
import jaLocale from "date-fns/locale/ja";
7+
import { LocalizationProvider } from "react-ymd-date-select";
8+
import { DateSelect } from "react-ymd-date-select/presets/vanilla";
9+
/* Select the appropriate adapter for the library you are using as below. */
10+
import DateAdapter from "react-ymd-date-select/adapters/date-fns";
11+
// import DateAdapter from "react-ymd-date-select/adapters/dayjs";
12+
// import DateAdapter from "react-ymd-date-select/adapters/luxon";
13+
// import DateAdapter from "react-ymd-date-select/adapters/date-fns-jalali";
14+
// import DateAdapter from "react-ymd-date-select/adapters/jalaali";
15+
16+
const localeMap = {
17+
en: enLocale,
18+
fr: frLocale,
19+
ru: ruLocale,
20+
de: deLocale,
21+
ja: jaLocale,
22+
};
23+
24+
type Locale = keyof typeof localeMap;
25+
26+
function Sample() {
27+
const [locale, setLocale] = useState<Locale>("en");
28+
const [date, setDate] = useState<string>("");
29+
30+
return (
31+
<div>
32+
<div>
33+
<label>
34+
Locale:
35+
<select
36+
value={locale}
37+
onChange={(e) => setLocale(e.target.value as Locale)}
38+
>
39+
{Object.keys(localeMap).map((localeItem) => (
40+
<option key={localeItem} value={localeItem}>
41+
{localeItem}
42+
</option>
43+
))}
44+
</select>
45+
</label>
46+
</div>
47+
48+
<LocalizationProvider
49+
dateAdapter={DateAdapter}
50+
locale={localeMap[locale]}
51+
>
52+
<DateSelect value={date} onChange={setDate} defaultMonth="now" />
53+
<p>Selected date is: {date}</p>
54+
</LocalizationProvider>
55+
</div>
56+
);
57+
}
58+
59+
export default Sample;

0 commit comments

Comments
 (0)