* [#710] added custom mobile timePicker * [#710] custom mobile datepicker
This commit is contained in:
@@ -1,14 +1,8 @@
|
|||||||
import { DatePicker } from '@mui/x-date-pickers/DatePicker'
|
import { useMediaQuery } from '@linagora/twake-mui'
|
||||||
import { PickerValue } from '@mui/x-date-pickers/internals'
|
import { PickerValue } from '@mui/x-date-pickers/internals'
|
||||||
import { Dayjs } from 'dayjs'
|
import { Dayjs } from 'dayjs'
|
||||||
import React from 'react'
|
import { DesktopDatePickerField } from './DesktopDatePickerField'
|
||||||
import { LONG_DATE_FORMAT } from '../../utils/dateTimeFormatters'
|
import { TouchDatePickerField } from './TouchDatePickerField'
|
||||||
import { ReadOnlyDateField } from '../ReadOnlyPickerField'
|
|
||||||
import {
|
|
||||||
dateCalendarLayoutSx,
|
|
||||||
getDateFieldSlotProps,
|
|
||||||
getDateSlotProps
|
|
||||||
} from './dateTimePickerSlotProps'
|
|
||||||
|
|
||||||
export interface DatePickerFieldProps {
|
export interface DatePickerFieldProps {
|
||||||
value: Dayjs | null
|
value: Dayjs | null
|
||||||
@@ -18,22 +12,11 @@ export interface DatePickerFieldProps {
|
|||||||
hasError?: boolean
|
hasError?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DatePickerField: React.FC<DatePickerFieldProps> = ({
|
export const DatePickerField: React.FC<DatePickerFieldProps> = props => {
|
||||||
value,
|
const isTouch = useMediaQuery('(pointer: coarse)')
|
||||||
onChange,
|
return isTouch ? (
|
||||||
testId,
|
<TouchDatePickerField {...props} />
|
||||||
label,
|
) : (
|
||||||
hasError = false
|
<DesktopDatePickerField {...props} />
|
||||||
}) => (
|
)
|
||||||
<DatePicker
|
}
|
||||||
format={LONG_DATE_FORMAT}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
slots={{ field: ReadOnlyDateField }}
|
|
||||||
slotProps={{
|
|
||||||
...getDateSlotProps(testId, hasError, label),
|
|
||||||
field: getDateFieldSlotProps(testId, hasError, label),
|
|
||||||
layout: { sx: dateCalendarLayoutSx }
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { DatePicker } from '@mui/x-date-pickers/DatePicker'
|
||||||
|
import React from 'react'
|
||||||
|
import { LONG_DATE_FORMAT } from '../../utils/dateTimeFormatters'
|
||||||
|
import { ReadOnlyDateField } from '../ReadOnlyPickerField'
|
||||||
|
import {
|
||||||
|
dateCalendarLayoutSx,
|
||||||
|
getDateFieldSlotProps,
|
||||||
|
getDateSlotProps
|
||||||
|
} from './dateTimePickerSlotProps'
|
||||||
|
import { DatePickerFieldProps } from './DatePickerField'
|
||||||
|
|
||||||
|
export const DesktopDatePickerField: React.FC<DatePickerFieldProps> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
testId,
|
||||||
|
label,
|
||||||
|
hasError = false
|
||||||
|
}) => (
|
||||||
|
<DatePicker
|
||||||
|
format={LONG_DATE_FORMAT}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
slots={{ field: ReadOnlyDateField }}
|
||||||
|
slotProps={{
|
||||||
|
...getDateSlotProps(testId, hasError, label),
|
||||||
|
field: getDateFieldSlotProps(testId, hasError, label),
|
||||||
|
layout: { sx: dateCalendarLayoutSx }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { TimePicker } from '@mui/x-date-pickers/TimePicker'
|
||||||
|
import React from 'react'
|
||||||
|
import { EditableTimeField } from '../EditableTimeField'
|
||||||
|
import {
|
||||||
|
getTimeFieldSlotProps,
|
||||||
|
timePickerPopperSx
|
||||||
|
} from './dateTimePickerSlotProps'
|
||||||
|
import { TimePickerFieldProps } from './TimePickerField'
|
||||||
|
|
||||||
|
export const DesktopTimePickerField: React.FC<TimePickerFieldProps> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
testId,
|
||||||
|
label,
|
||||||
|
hasError = false,
|
||||||
|
disabled = false
|
||||||
|
}) => (
|
||||||
|
<TimePicker
|
||||||
|
ampm={false}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
disabled={disabled}
|
||||||
|
thresholdToRenderTimeInASingleColumn={48}
|
||||||
|
timeSteps={{ minutes: 30 }}
|
||||||
|
slots={{
|
||||||
|
field: EditableTimeField,
|
||||||
|
actionBar: () => null
|
||||||
|
}}
|
||||||
|
slotProps={{
|
||||||
|
openPickerButton: { sx: { display: 'none' } },
|
||||||
|
popper: { sx: timePickerPopperSx },
|
||||||
|
field: getTimeFieldSlotProps(testId, hasError, label)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
@@ -1,12 +1,9 @@
|
|||||||
|
import { useMediaQuery } from '@linagora/twake-mui'
|
||||||
import { PickerValue } from '@mui/x-date-pickers/internals'
|
import { PickerValue } from '@mui/x-date-pickers/internals'
|
||||||
import { TimePicker } from '@mui/x-date-pickers/TimePicker'
|
|
||||||
import { Dayjs } from 'dayjs'
|
import { Dayjs } from 'dayjs'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { EditableTimeField } from '../EditableTimeField'
|
import { DesktopTimePickerField } from './DesktopTimePickerField'
|
||||||
import {
|
import { TouchTimePickerField } from './TouchTimePickerField'
|
||||||
getTimeFieldSlotProps,
|
|
||||||
timePickerPopperSx
|
|
||||||
} from './dateTimePickerSlotProps'
|
|
||||||
|
|
||||||
export interface TimePickerFieldProps {
|
export interface TimePickerFieldProps {
|
||||||
value: Dayjs | null
|
value: Dayjs | null
|
||||||
@@ -17,29 +14,11 @@ export interface TimePickerFieldProps {
|
|||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TimePickerField: React.FC<TimePickerFieldProps> = ({
|
export const TimePickerField: React.FC<TimePickerFieldProps> = props => {
|
||||||
value,
|
const isTouch = useMediaQuery('(pointer: coarse)')
|
||||||
onChange,
|
return isTouch ? (
|
||||||
testId,
|
<TouchTimePickerField {...props} />
|
||||||
label,
|
) : (
|
||||||
hasError = false,
|
<DesktopTimePickerField {...props} />
|
||||||
disabled = false
|
)
|
||||||
}) => (
|
}
|
||||||
<TimePicker
|
|
||||||
ampm={false}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
disabled={disabled}
|
|
||||||
thresholdToRenderTimeInASingleColumn={48}
|
|
||||||
timeSteps={{ minutes: 30 }}
|
|
||||||
slots={{
|
|
||||||
field: EditableTimeField,
|
|
||||||
actionBar: () => null
|
|
||||||
}}
|
|
||||||
slotProps={{
|
|
||||||
openPickerButton: { sx: { display: 'none' } },
|
|
||||||
popper: { sx: timePickerPopperSx },
|
|
||||||
field: getTimeFieldSlotProps(testId, hasError, label)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -0,0 +1,193 @@
|
|||||||
|
import CalendarTodayIcon from '@mui/icons-material/CalendarToday'
|
||||||
|
import EditIcon from '@mui/icons-material/Edit'
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
IconButton,
|
||||||
|
Typography
|
||||||
|
} from '@linagora/twake-mui'
|
||||||
|
import { DateField, DatePicker } from '@mui/x-date-pickers'
|
||||||
|
import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'
|
||||||
|
import { PickerValue } from '@mui/x-date-pickers/internals'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { useI18n } from 'twake-i18n'
|
||||||
|
import { ReadOnlyDateField } from '../ReadOnlyPickerField'
|
||||||
|
import { DatePickerFieldProps } from './DatePickerField'
|
||||||
|
import {
|
||||||
|
dateCalendarLayoutSx,
|
||||||
|
getDateFieldSlotProps,
|
||||||
|
getDateSlotProps
|
||||||
|
} from './dateTimePickerSlotProps'
|
||||||
|
|
||||||
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
|
const DISPLAY_FORMAT = 'D MMMM, YYYY'
|
||||||
|
|
||||||
|
interface DatePickerDialogProps {
|
||||||
|
value: PickerValue
|
||||||
|
onChange: (value: PickerValue) => void
|
||||||
|
onAccept: (value: PickerValue) => void
|
||||||
|
onCancel: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const DatePickerDialogContent: React.FC<DatePickerDialogProps> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
onAccept,
|
||||||
|
onCancel
|
||||||
|
}) => {
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const [viewMode, setViewMode] = useState<'calendar' | 'text'>('calendar')
|
||||||
|
|
||||||
|
const [internalValue, setInternalValue] = useState<PickerValue>(value)
|
||||||
|
|
||||||
|
const handleInternalChange = (newValue: PickerValue): void => {
|
||||||
|
setInternalValue(newValue)
|
||||||
|
onChange(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleToggleView = (): void => {
|
||||||
|
setViewMode(viewMode === 'calendar' ? 'text' : 'calendar')
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayDate = internalValue?.isValid()
|
||||||
|
? internalValue.format(DISPLAY_FORMAT)
|
||||||
|
: t('dateTimeFields.pickADate')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ p: 2, width: 280 }}>
|
||||||
|
{/* Header */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
mb: 1,
|
||||||
|
gap: 1
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="subtitle1" fontWeight={500}>
|
||||||
|
{displayDate}
|
||||||
|
</Typography>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={handleToggleView}
|
||||||
|
aria-label={
|
||||||
|
viewMode === 'calendar'
|
||||||
|
? t('dateTimeFields.switchToTextInput')
|
||||||
|
: t('dateTimeFields.switchToCalendar')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{viewMode === 'calendar' ? (
|
||||||
|
<EditIcon fontSize="small" />
|
||||||
|
) : (
|
||||||
|
<CalendarTodayIcon fontSize="small" />
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Body */}
|
||||||
|
{viewMode === 'calendar' ? (
|
||||||
|
<DateCalendar
|
||||||
|
value={internalValue}
|
||||||
|
onChange={handleInternalChange}
|
||||||
|
sx={{ m: 0, width: '100%' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Box sx={{ mt: 1, mb: 2 }}>
|
||||||
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
{t('dateTimeFields.date')}
|
||||||
|
</Typography>
|
||||||
|
<DateField
|
||||||
|
fullWidth
|
||||||
|
inputMode="numeric"
|
||||||
|
size="small"
|
||||||
|
value={internalValue}
|
||||||
|
onChange={handleInternalChange}
|
||||||
|
autoFocus
|
||||||
|
slotProps={{
|
||||||
|
textField: {
|
||||||
|
inputProps: { inputMode: 'numeric', pattern: '[0-9]*' }
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
gap: 1
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button onClick={onCancel} variant="text">
|
||||||
|
{t('common.cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => onAccept(internalValue)} variant="contained">
|
||||||
|
{t('common.ok')}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TouchDatePickerField: React.FC<DatePickerFieldProps> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
testId,
|
||||||
|
label = 'Date',
|
||||||
|
hasError = false
|
||||||
|
}) => {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const [pendingValue, setPendingValue] = useState<PickerValue>(value)
|
||||||
|
|
||||||
|
const handleOpen = (): void => {
|
||||||
|
setPendingValue(value)
|
||||||
|
setOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAccept = (newValue: PickerValue): void => {
|
||||||
|
onChange(newValue)
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = (): void => {
|
||||||
|
setPendingValue(value)
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DatePicker
|
||||||
|
label={label}
|
||||||
|
value={value}
|
||||||
|
onOpen={handleOpen}
|
||||||
|
onAccept={onChange}
|
||||||
|
open={false}
|
||||||
|
slots={{ field: ReadOnlyDateField }}
|
||||||
|
slotProps={{
|
||||||
|
openPickerButton: { sx: { display: 'none' } },
|
||||||
|
...getDateSlotProps(testId, hasError, label),
|
||||||
|
field: getDateFieldSlotProps(testId, hasError, label),
|
||||||
|
layout: { sx: dateCalendarLayoutSx }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={handleCancel}
|
||||||
|
slotProps={{ paper: { sx: { borderRadius: 2, overflow: 'hidden' } } }}
|
||||||
|
>
|
||||||
|
<DatePickerDialogContent
|
||||||
|
value={pendingValue}
|
||||||
|
onChange={setPendingValue}
|
||||||
|
onAccept={handleAccept}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
Divider,
|
||||||
|
IconButton,
|
||||||
|
SxProps,
|
||||||
|
Theme
|
||||||
|
} from '@linagora/twake-mui'
|
||||||
|
import AccessTime from '@mui/icons-material/AccessTime'
|
||||||
|
import KeyboardAltOutlined from '@mui/icons-material/KeyboardAltOutlined'
|
||||||
|
import {
|
||||||
|
MobileTimePicker,
|
||||||
|
renderTimeViewClock,
|
||||||
|
TimeField
|
||||||
|
} from '@mui/x-date-pickers'
|
||||||
|
import { PickerValue } from '@mui/x-date-pickers/internals'
|
||||||
|
import { StaticTimePicker } from '@mui/x-date-pickers/StaticTimePicker'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useI18n } from 'twake-i18n'
|
||||||
|
import { EditableTimeField } from '../EditableTimeField'
|
||||||
|
import { getTimeFieldSlotProps } from './dateTimePickerSlotProps'
|
||||||
|
import { TimePickerFieldProps } from './TimePickerField'
|
||||||
|
|
||||||
|
const TIME_DISPLAY_SX = {
|
||||||
|
fontSize: '48px',
|
||||||
|
fontWeight: 400
|
||||||
|
} as const
|
||||||
|
|
||||||
|
const TIME_INPUT_SX: SxProps<Theme> = {
|
||||||
|
width: '100%',
|
||||||
|
'& .MuiPickersInputBase-sectionAfter': { mx: '16px' },
|
||||||
|
'& .MuiPickersSectionList-root': { justifyContent: 'end' },
|
||||||
|
'& .MuiPickersInputBase-root': {
|
||||||
|
...TIME_DISPLAY_SX,
|
||||||
|
color: 'text.secondary',
|
||||||
|
backgroundColor: 'transparent !important',
|
||||||
|
'&:before': { borderBottomColor: 'transparent !important' },
|
||||||
|
'&:after': { borderBottomColor: 'transparent !important' }
|
||||||
|
},
|
||||||
|
'& .MuiPickersInputBase-input': {
|
||||||
|
textAlign: 'center'
|
||||||
|
},
|
||||||
|
'& .MuiPickersInputBase-root.Mui-focused .MuiPickersInputBase-input': {
|
||||||
|
backgroundColor: 'transparent !important',
|
||||||
|
color: 'primary.main'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const STATIC_PICKER_SX: SxProps<Theme> = {
|
||||||
|
'& .MuiPickersToolbar-content': { justifyContent: 'center' },
|
||||||
|
'& .MuiTimePickerToolbar-hourMinuteLabel': {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '16px'
|
||||||
|
},
|
||||||
|
'& .MuiTimePickerToolbar-hourMinuteLabel .MuiPickersToolbarText-root[data-selected]':
|
||||||
|
{ color: 'primary.main' },
|
||||||
|
'& .MuiPickersToolbar-root .MuiTypography-root': TIME_DISPLAY_SX,
|
||||||
|
'& .MuiPickersToolbar-title': { display: 'none' },
|
||||||
|
'& .MuiPickersArrowSwitcher-root': { display: 'none' }
|
||||||
|
}
|
||||||
|
|
||||||
|
type DialogView = 'clock' | 'keyboard'
|
||||||
|
|
||||||
|
export const TouchTimePickerField: React.FC<TimePickerFieldProps> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
testId,
|
||||||
|
label,
|
||||||
|
hasError = false,
|
||||||
|
disabled = false
|
||||||
|
}) => {
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const [internalValue, setInternalValue] = useState<PickerValue>(value)
|
||||||
|
const [view, setView] = useState<DialogView>('clock')
|
||||||
|
|
||||||
|
const handleOpen = (): void => {
|
||||||
|
setInternalValue(value)
|
||||||
|
setOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAccept = (): void => {
|
||||||
|
onChange(internalValue)
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = (): void => {
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleView = (): void =>
|
||||||
|
setView(view === 'clock' ? 'keyboard' : 'clock')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MobileTimePicker
|
||||||
|
ampm={false}
|
||||||
|
value={value}
|
||||||
|
onAccept={onChange}
|
||||||
|
disabled={disabled}
|
||||||
|
open={false}
|
||||||
|
onOpen={handleOpen}
|
||||||
|
viewRenderers={{
|
||||||
|
hours: renderTimeViewClock,
|
||||||
|
minutes: renderTimeViewClock
|
||||||
|
}}
|
||||||
|
slots={{ field: EditableTimeField }}
|
||||||
|
slotProps={{
|
||||||
|
openPickerButton: { sx: { display: 'none' } },
|
||||||
|
field: {
|
||||||
|
...getTimeFieldSlotProps(testId, hasError, label),
|
||||||
|
onFocus: e => e.target.blur()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Dialog open={open} onClose={handleCancel}>
|
||||||
|
{view === 'clock' && (
|
||||||
|
<StaticTimePicker
|
||||||
|
ampm={false}
|
||||||
|
value={internalValue}
|
||||||
|
onChange={setInternalValue}
|
||||||
|
viewRenderers={{
|
||||||
|
hours: renderTimeViewClock,
|
||||||
|
minutes: renderTimeViewClock
|
||||||
|
}}
|
||||||
|
slots={{ actionBar: () => null }}
|
||||||
|
sx={STATIC_PICKER_SX}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{view === 'keyboard' && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '16px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TimeField
|
||||||
|
ampm={false}
|
||||||
|
value={internalValue}
|
||||||
|
onChange={setInternalValue}
|
||||||
|
autoFocus
|
||||||
|
sx={TIME_INPUT_SX}
|
||||||
|
slotProps={{
|
||||||
|
textField: {
|
||||||
|
variant: 'filled',
|
||||||
|
InputLabelProps: { shrink: false },
|
||||||
|
inputProps: { inputMode: 'numeric', pattern: '[0-9]*' }
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<DialogActions
|
||||||
|
sx={{ display: 'flex', justifyContent: 'space-between' }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
onClick={toggleView}
|
||||||
|
aria-label={
|
||||||
|
view === 'clock'
|
||||||
|
? t('dateTimeFields.switchToTextInput')
|
||||||
|
: t('dateTimeFields.switchToClockView')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{view === 'clock' ? <KeyboardAltOutlined /> : <AccessTime />}
|
||||||
|
</IconButton>
|
||||||
|
<Box>
|
||||||
|
<Button onClick={handleCancel} variant="text">
|
||||||
|
{t('common.cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleAccept} variant="contained">
|
||||||
|
{t('common.ok')}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
+5
-1
@@ -347,7 +347,11 @@
|
|||||||
"startDate": "Start Date",
|
"startDate": "Start Date",
|
||||||
"startTime": "Start Time",
|
"startTime": "Start Time",
|
||||||
"endDate": "End Date",
|
"endDate": "End Date",
|
||||||
"endTime": "End Time"
|
"endTime": "End Time",
|
||||||
|
"pickADate": "Pick a date",
|
||||||
|
"switchToTextInput": "Switch to text input",
|
||||||
|
"switchToCalendar": "Switch to calendar view",
|
||||||
|
"switchToClockView": "Switch to clock view"
|
||||||
},
|
},
|
||||||
"websocket": {
|
"websocket": {
|
||||||
"browserOnline": "You’re back online. Reconnecting live updates…",
|
"browserOnline": "You’re back online. Reconnecting live updates…",
|
||||||
|
|||||||
+5
-1
@@ -348,7 +348,11 @@
|
|||||||
"startDate": "Date du début",
|
"startDate": "Date du début",
|
||||||
"startTime": "Heure du début",
|
"startTime": "Heure du début",
|
||||||
"endDate": "Date de la fin",
|
"endDate": "Date de la fin",
|
||||||
"endTime": "Heure de la fin"
|
"endTime": "Heure de la fin",
|
||||||
|
"pickADate": "Choisissez une date",
|
||||||
|
"switchToTextInput": "Passer à la saisie de texte",
|
||||||
|
"switchToCalendar": "Passer à la vue calendrier",
|
||||||
|
"switchToClockView": "Passer à la vue horloge"
|
||||||
},
|
},
|
||||||
"websocket": {
|
"websocket": {
|
||||||
"browserOnline": "Vous êtes de nouveau en ligne. Reconnexion des mises à jour en temps réel…",
|
"browserOnline": "Vous êtes de nouveau en ligne. Reconnexion des mises à jour en temps réel…",
|
||||||
|
|||||||
+5
-1
@@ -348,7 +348,11 @@
|
|||||||
"startDate": "Дата начала",
|
"startDate": "Дата начала",
|
||||||
"startTime": "Время начала",
|
"startTime": "Время начала",
|
||||||
"endDate": "Дата окончания",
|
"endDate": "Дата окончания",
|
||||||
"endTime": "Время окончания"
|
"endTime": "Время окончания",
|
||||||
|
"pickADate": "Выберите дату",
|
||||||
|
"switchToTextInput": "Переключиться на текстовый ввод",
|
||||||
|
"switchToCalendar": "Переключиться на календарь",
|
||||||
|
"switchToClockView": "Переключиться на вид часов"
|
||||||
},
|
},
|
||||||
"websocket": {
|
"websocket": {
|
||||||
"browserOnline": "Вы снова в сети. Подключаем обновления в реальном времени…",
|
"browserOnline": "Вы снова в сети. Подключаем обновления в реальном времени…",
|
||||||
|
|||||||
+5
-1
@@ -346,7 +346,11 @@
|
|||||||
"startDate": "Ngày bắt đầu",
|
"startDate": "Ngày bắt đầu",
|
||||||
"startTime": "Giờ bắt đầu",
|
"startTime": "Giờ bắt đầu",
|
||||||
"endDate": "Ngày kết thúc",
|
"endDate": "Ngày kết thúc",
|
||||||
"endTime": "Giờ kết thúc"
|
"endTime": "Giờ kết thúc",
|
||||||
|
"pickADate": "Chọn ngày",
|
||||||
|
"switchToTextInput": "Chuyển sang nhập văn bản",
|
||||||
|
"switchToCalendar": "Chuyển sang chế độ lịch",
|
||||||
|
"switchToClockView": "Chuyển sang chế độ đồng hồ"
|
||||||
},
|
},
|
||||||
"websocket": {
|
"websocket": {
|
||||||
"browserOnline": "Bạn đã trực tuyến trở lại. Đang kết nối lại cập nhật thời gian thực…",
|
"browserOnline": "Bạn đã trực tuyến trở lại. Đang kết nối lại cập nhật thời gian thực…",
|
||||||
|
|||||||
Reference in New Issue
Block a user