Skip to content

Instantly share code, notes, and snippets.

@Avi-E-Koenig
Created May 20, 2025 16:48
Show Gist options
  • Save Avi-E-Koenig/9f692934e456d78de6a83214bf0eec6a to your computer and use it in GitHub Desktop.
Save Avi-E-Koenig/9f692934e456d78de6a83214bf0eec6a to your computer and use it in GitHub Desktop.
// React and types
import * as React from 'react';
import type { Dayjs } from 'dayjs/esm';
// Dayjs and locales
import 'dayjs/locale/he';
import 'dayjs/locale/en';
// MUI X Date Pickers
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
// MUI Core and Components
import Box from '@mui/material/Box';
import { createTheme, ThemeProvider, useTheme } from '@mui/material/styles';
// Project hooks/constants
import { useApplicationConfigs } from '@/hooks/useApplicationConfig';
import { LOCALE } from '@/theme';
// RTL and styling utilities
import { prefixer } from 'stylis';
import rtlPlugin from 'stylis-plugin-rtl';
import createCache from '@emotion/cache';
import { CacheProvider } from '@emotion/react';
/**
* DateRangeSelector - Two MUI DatePickers (from/to) with i18n labels, using latest MUI X API and Dayjs.
* Controlled via props. Handles RTL layout and text alignment with full MUI RTL support.
*/
export default function DateRangeSelector({
from,
to,
setFrom,
setTo,
}: {
from: Dayjs | null;
to: Dayjs | null;
setFrom: (date: Dayjs | null) => void;
setTo: (date: Dayjs | null) => void;
}) {
const { locale, dir, isDark } = useApplicationConfigs();
const content = {
[LOCALE.EN]: {
from: 'From',
to: 'To',
error: 'Invalid date range',
fromError: 'Start date cannot be after end date',
toError: 'End date cannot be before start date',
},
[LOCALE.HE]: {
from: 'מתאריך',
to: 'עד תאריך',
error: 'טווח תאריכים לא תקין',
fromError: 'תאריך התחלה לא יכול להיות אחרי תאריך סיום',
toError: 'תאריך סיום לא יכול להיות לפני תאריך התחלה',
},
};
const t = content[locale] || content[LOCALE.EN];
const adapterLocale = locale === LOCALE.HE ? 'he' : 'en';
// RTL support setup
const existingTheme = useTheme();
const theme = React.useMemo(
() =>
dir === 'rtl'
? createTheme(existingTheme, {
direction: 'rtl',
})
: existingTheme,
[existingTheme, dir]
);
const cacheRtl = React.useMemo(
() => createCache({ key: 'pickers-rtl-demo', stylisPlugins: [prefixer, rtlPlugin] }),
[]
);
// Validation
const isInvalid = !!(from && to && from.isAfter(to));
const textAlign: 'left' | 'right' = dir === 'rtl' ? 'right' : 'left';
const pickerProps = (
label: string,
value: Dayjs | null,
onChange: (date: Dayjs | null) => void,
error: boolean,
helperText: string
) => ({
label,
value,
onChange,
sx: {
'& .MuiFormLabel-root': {
color: isDark ? 'white !important' : 'black !important',
},
'& fieldset': {
borderColor: isDark ? 'white !important' : 'black !important',
},
'& .MuiFormHelperText-root': {
color: isDark ? 'white !important' : 'black !important',
},
},
slotProps: {
textField: {
inputProps: { style: { textAlign } },
error,
helperText,
},
...(dir === 'rtl' && {
desktopPaper: { dir: 'rtl' },
mobilePaper: { dir: 'rtl' },
}),
},
});
// const dynamicBgColor = isDark ? 'var(--secondary-bg-dark)' : 'var(--secondary-bg-light)';
const PickerContent = (
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale={adapterLocale}>
<Box
dir={dir}
sx={{
// backgroundColor: dynamicBgColor,
borderRadius: 1,
width: 'max-content',
padding: 1,
display: 'flex',
gap: 2,
flexDirection: 'column',
}}
>
<Box sx={{ display: 'flex', gap: 2 }}>
<DatePicker
{...pickerProps(t.from, from, setFrom, isInvalid, isInvalid ? t.fromError : '')}
/>
<DatePicker {...pickerProps(t.to, to, setTo, isInvalid, isInvalid ? t.toError : '')} />
</Box>
</Box>
</LocalizationProvider>
);
if (dir === 'rtl') {
return (
<CacheProvider value={cacheRtl}>
<ThemeProvider theme={theme}>{PickerContent}</ThemeProvider>
</CacheProvider>
);
}
return PickerContent;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment