Skip to content
This repository has been archived by the owner on Sep 9, 2024. It is now read-only.

feat: keyboard support datetime input #902

Merged
merged 1 commit into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@
"@lezer/common": "1.0.2",
"@mdx-js/mdx": "2.3.0",
"@mdx-js/react": "2.3.0",
"@mui/base": "5.0.0-beta.14",
"@mui/material": "5.11.16",
"@mui/system": "5.11.16",
"@mui/x-date-pickers": "5.0.20",
"@mui/base": "5.0.0-beta.17",
"@mui/material": "5.14.11",
"@mui/system": "5.14.11",
"@mui/x-date-pickers": "6.16.0",
"@reduxjs/toolkit": "1.9.5",
"@styled-icons/bootstrap": "10.47.0",
"@styled-icons/fa-brands": "10.47.0",
Expand Down
18 changes: 12 additions & 6 deletions packages/core/src/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -427,31 +427,37 @@
--scrollbar-background: var(--background-dark);
}

.CMS_Scrollbar_root.CMS_Scrollbar_secondary {
.CMS_Scrollbar_root.CMS_Scrollbar_secondary,
.MuiMultiSectionDigitalClock-root {
--scrollbar-foreground: var(--scrollbar-light);
--scrollbar-background: var(--background-main);
}

.CMS_Scrollbar_root {
.CMS_Scrollbar_root,
.MuiMultiSectionDigitalClock-root {
/* Foreground, Background */
scrollbar-color: var(--scrollbar-foreground) var(--scrollbar-background);
}

.CMS_Scrollbar_root::-webkit-scrollbar {
.CMS_Scrollbar_root::-webkit-scrollbar,
.MuiMultiSectionDigitalClock-root::-webkit-scrollbar {
width: 10px; /* Mostly for vertical scrollbars */
height: 10px; /* Mostly for horizontal scrollbars */
}

.CMS_Scrollbar_root::-webkit-scrollbar-corner {
.CMS_Scrollbar_root::-webkit-scrollbar-corner,
.MuiMultiSectionDigitalClock-root::-webkit-scrollbar-corner {
background: rgba(0, 0, 0, 0);
}

.CMS_Scrollbar_root::-webkit-scrollbar-thumb {
.CMS_Scrollbar_root::-webkit-scrollbar-thumb,
.MuiMultiSectionDigitalClock-root::-webkit-scrollbar-thumb {
/* Foreground */
background: var(--scrollbar-foreground);
}

.CMS_Scrollbar_root::-webkit-scrollbar-track {
.CMS_Scrollbar_root::-webkit-scrollbar-track,
.MuiMultiSectionDigitalClock-root::-webkit-scrollbar-track {
/* Background */
background: var(--scrollbar-background);
}
Expand Down
40 changes: 35 additions & 5 deletions packages/core/src/widgets/datetime/DateTimeControl.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,45 @@
}

.CMS_WidgetDateTime_wrapper {
@apply !w-date-widget;
}

.CMS_WidgetDateTime_date-input {
.CMS_WidgetDateTime_date {
@apply flex-grow;
}

.CMS_WidgetDateTime_time-input {
.CMS_WidgetDateTime_time {
@apply flex-grow;
}

.CMS_WidgetDateTime_datetime-input {
@apply truncate;
.CMS_WidgetDateTime_datetime {
}

.CMS_WidgetDateTime_inputs {
@apply flex
items-center
w-full
ps-1.5
pe-2.5
gap-2;

& .CMS_WidgetDateTime_input-wrapper {
@apply flex-grow;

& .CMS_WidgetDateTime_input {
@apply py-1
pl-2;
}

& .MuiOutlinedInput-root {
& .MuiOutlinedInput-notchedOutline {
@apply border-0;
}

&.Mui-focused {
& .MuiOutlinedInput-notchedOutline {
@apply border-0;
}
}
}
}
}
160 changes: 62 additions & 98 deletions packages/core/src/widgets/datetime/DateTimeControl.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { unstable_useForkRef as useForkRef } from '@mui/utils';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { MobileDatePicker } from '@mui/x-date-pickers/MobileDatePicker';
import { MobileDateTimePicker } from '@mui/x-date-pickers/MobileDateTimePicker';
import { MobileTimePicker } from '@mui/x-date-pickers/MobileTimePicker';
import { TimePicker } from '@mui/x-date-pickers/TimePicker';
import formatDate from 'date-fns/format';
import parse from 'date-fns/parse';
import parseISO from 'date-fns/parseISO';
import React, { useCallback, useMemo, useRef, useState } from 'react';

import Field from '@staticcms/core/components/common/field/Field';
import TextField from '@staticcms/core/components/common/text-field/TextField';
import classNames from '@staticcms/core/lib/util/classNames.util';
import { generateClassNames } from '@staticcms/core/lib/util/theming.util';
import NowButton from './components/NowButton';
import { DEFAULT_DATETIME_FORMAT } from './constants';
import { useDatetimeFormats } from './datetime.util';
import { localToUTC } from './utc.util';

import type { TextFieldProps as MuiTextFieldProps } from '@mui/material/TextField';
import type { TextFieldProps } from '@staticcms/core/components/common/text-field/TextField';
import type { DateTimeField, WidgetControlProps } from '@staticcms/core/interface';
import type { FC } from 'react';

Expand All @@ -31,28 +29,11 @@ export const classes = generateClassNames('WidgetDateTime', [
'disabled',
'for-single-list',
'wrapper',
'date-input',
'time-input',
'datetime-input',
'inputs',
'input-wrapper',
'input',
]);

function convertMuiTextFieldProps({
inputProps,
disabled,
onClick,
}: MuiTextFieldProps): TextFieldProps {
const value: string = inputProps?.value ?? '';

return {
type: 'text',
value,
disabled,
// eslint-disable-next-line @typescript-eslint/no-empty-function
onChange: () => {},
onClick,
};
}

const DateTimeControl: FC<WidgetControlProps<string | Date, DateTimeField>> = ({
field,
label,
Expand Down Expand Up @@ -106,7 +87,7 @@ const DateTimeControl: FC<WidgetControlProps<string | Date, DateTimeField>> = ({

const handleChange = useCallback(
(datetime: Date | null) => {
if (datetime === null) {
if (datetime === null || isNaN(datetime.getTime())) {
setInternalValue(defaultValue);
onChange(defaultValue);
return;
Expand All @@ -121,114 +102,89 @@ const DateTimeControl: FC<WidgetControlProps<string | Date, DateTimeField>> = ({
[defaultValue, field.picker_utc, storageFormat, onChange],
);

const inputRef = useRef<HTMLInputElement>();
const rootRef = useForkRef(ref, inputRef);

const dateTimePicker = useMemo(() => {
if (dateFormat && !timeFormat) {
return (
<MobileDatePicker
key="mobile-date-picker"
inputFormat={displayFormat}
label={label}
<DatePicker
key="date-picker"
format={displayFormat}
value={dateValue}
disabled={disabled}
onChange={handleChange}
onOpen={handleOpen}
onClose={handleClose}
renderInput={props => (
<>
<TextField
key="mobile-date-input"
data-testid="date-input"
{...convertMuiTextFieldProps(props)}
inputRef={ref}
cursor="pointer"
inputClassName={classes['date-input']}
/>
<NowButton
key="mobile-date-now"
handleChange={v => handleChange(v)}
disabled={disabled}
field={field}
/>
</>
)}
className={classes['input-wrapper']}
inputRef={rootRef}
slotProps={{
textField: {
inputProps: {
'data-testid': 'date-input',
className: classes.input,
},
},
}}
/>
);
}

if (!dateFormat && timeFormat) {
return (
<MobileTimePicker
<TimePicker
key="time-picker"
label={label}
inputFormat={displayFormat}
format={displayFormat}
value={dateValue}
disabled={disabled}
onChange={handleChange}
onOpen={handleOpen}
onClose={handleClose}
renderInput={props => (
<>
<TextField
key="mobile-time-input"
data-testid="time-input"
{...convertMuiTextFieldProps(props)}
inputRef={ref}
cursor="pointer"
inputClassName={classes['time-input']}
/>
<NowButton
key="mobile-date-now"
handleChange={v => handleChange(v)}
disabled={disabled}
field={field}
/>
</>
)}
className={classes['input-wrapper']}
inputRef={rootRef}
slotProps={{
textField: {
inputProps: {
'data-testid': 'time-input',
className: classes.input,
},
},
}}
/>
);
}

return (
<MobileDateTimePicker
key="mobile-date-time-picker"
inputFormat={displayFormat}
label={label}
<DateTimePicker
key="date-time-picker"
format={displayFormat}
value={dateValue}
disabled={disabled}
onChange={handleChange}
onOpen={handleOpen}
onClose={handleClose}
renderInput={props => (
<>
<TextField
key="mobile-date-time-input"
data-testid="date-time-input"
{...convertMuiTextFieldProps(props)}
inputRef={ref}
cursor="pointer"
inputClassName={classes['datetime-input']}
/>
<NowButton
key="mobile-date-now"
handleChange={v => handleChange(v)}
disabled={disabled}
field={field}
/>
</>
)}
className={classes['input-wrapper']}
inputRef={rootRef}
slotProps={{
textField: {
inputProps: {
'data-testid': 'date-time-input',
className: classes.input,
},
},
}}
/>
);
}, [
dateFormat,
timeFormat,
displayFormat,
label,
dateValue,
disabled,
handleChange,
handleOpen,
handleClose,
field,
rootRef,
]);

return (
Expand All @@ -238,7 +194,7 @@ const DateTimeControl: FC<WidgetControlProps<string | Date, DateTimeField>> = ({
errors={errors}
hint={field.hint}
forSingleList={forSingleList}
cursor="pointer"
cursor="text"
disabled={disabled}
rootClassName={classNames(
classes.root,
Expand All @@ -249,9 +205,17 @@ const DateTimeControl: FC<WidgetControlProps<string | Date, DateTimeField>> = ({
)}
wrapperClassName={classes.wrapper}
>
<LocalizationProvider key="localization-provider" dateAdapter={AdapterDateFns}>
{dateTimePicker}
</LocalizationProvider>
<div className={classes['inputs']}>
<LocalizationProvider key="localization-provider" dateAdapter={AdapterDateFns}>
{dateTimePicker}
</LocalizationProvider>
<NowButton
key="date-now"
field={field}
handleChange={v => handleChange(v)}
disabled={disabled}
/>
</div>
</Field>
);
};
Expand Down
Loading