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

Commit

Permalink
feat: keyboard support datetime input (#902)
Browse files Browse the repository at this point in the history
  • Loading branch information
KaneFreeman authored Oct 3, 2023
1 parent 9c655f0 commit 6f129d4
Show file tree
Hide file tree
Showing 11 changed files with 364 additions and 356 deletions.
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

0 comments on commit 6f129d4

Please sign in to comment.