design-comuni-plone-theme
Version:
Volto Theme for Italia design guidelines
311 lines (291 loc) • 8.48 kB
JSX
/**
* DatetimeWidget component.
* @module components/manage/Widgets/DatetimeWidget
*
* https://github.com/plone/volto/blob/16.x.x/src/components/manage/Widgets/DatetimeWidget.jsx
* https://github.com/plone/volto/blob/7ec45f7b4d46233236c651f39a951bad8e389184/src/components/manage/Widgets/DatetimeWidget.jsx
*
* CUSTOMIZATIONS:
* - accept calendar `openDirection` prop and use it in SingleDatePicker,
* default to down if no openDirection is given
*/
/**
* DatetimeWidget component.
* @module components/manage/Widgets/DatetimeWidget
*/
import React, { Component } from 'react';
import { compose } from 'redux';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import loadable from '@loadable/component';
import cx from 'classnames';
import { Icon, FormFieldWrapper } from '@plone/volto/components';
import { parseDateTime, toBackendLang } from '@plone/volto/helpers';
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
import leftKey from '@plone/volto/icons/left-key.svg';
import rightKey from '@plone/volto/icons/right-key.svg';
import clearSVG from '@plone/volto/icons/clear.svg';
import 'rc-time-picker/assets/index.css';
import 'react-dates/initialize';
import 'react-dates/lib/css/_datepicker.css';
const TimePicker = loadable(() => import('rc-time-picker'));
const messages = defineMessages({
date: {
id: 'Date',
defaultMessage: 'Date',
},
time: {
id: 'Time',
defaultMessage: 'Time',
},
});
const PrevIcon = () => (
<div
style={{
color: '#000',
left: '22px',
padding: '5px',
position: 'absolute',
top: '15px',
}}
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex="0"
>
<Icon name={leftKey} size="30px" />
</div>
);
const NextIcon = () => (
<div
style={{
color: '#000',
right: '22px',
padding: '5px',
position: 'absolute',
top: '15px',
}}
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex="0"
>
<Icon name={rightKey} size="30px" />
</div>
);
const defaultTimeDateOnly = {
hour: 12,
minute: 0,
second: 0,
};
/**
* DatetimeWidget component class
* @class DatetimeWidget
* @extends Component
*
* To use it, in schema properties, declare a field like:
*
* ```jsx
* {
* title: "Publish date",
* type: 'datetime',
* }
* ```
*/
export class DatetimeWidgetComponent extends Component {
/**
* Constructor
* @method constructor
* @param {Object} props Component properties
* @constructs DatetimeWidget
*/
constructor(props) {
super(props);
this.moment = props.moment.default;
this.state = {
focused: false,
// if passed value matches the construction time, we guess it's a default
isDefault:
parseDateTime(
toBackendLang(this.props.lang),
this.props.value,
undefined,
this.moment,
)?.toISOString() === this.moment().utc().toISOString(),
};
}
getInternalValue() {
return parseDateTime(
toBackendLang(this.props.lang),
this.props.value,
undefined,
this.moment,
);
}
getDateOnly() {
return this.props.dateOnly || this.props.widget === 'date';
}
/**
* Update date storage
* @method onDateChange
* @param {Object} date updated momentjs Object for date
* @returns {undefined}
*/
onDateChange = (date) => {
if (date) {
const moment = this.props.moment.default;
const isDateOnly = this.getDateOnly();
const base = (this.getInternalValue() || moment()).set({
year: date.year(),
month: date.month(),
date: date.date(),
...(isDateOnly ? defaultTimeDateOnly : {}),
});
const dateValue = isDateOnly
? base.format('YYYY-MM-DD')
: base.toISOString();
this.props.onChange(this.props.id, dateValue);
}
this.setState({ isDefault: false });
};
/**
* Update date storage
* @method onTimeChange
* @param {Object} time updated momentjs Object for time
* @returns {undefined}
*/
onTimeChange = (time) => {
const moment = this.props.moment.default;
if (time) {
const base = (this.getInternalValue() || moment()).set({
hours: time?.hours() ?? 0,
minutes: time?.minutes() ?? 0,
seconds: 0,
});
const dateValue = base.toISOString();
this.props.onChange(this.props.id, dateValue);
}
};
onResetDates = () => {
this.setState({ isDefault: false });
this.props.onChange(this.props.id, null);
};
/**
* Handle SingleDatePicker focus
* @method onFocusChange
* @param {boolean} focused component focus state.
* @returns {undefined}
*/
onFocusChange = ({ focused }) => this.setState({ focused });
render() {
const { id, resettable, intl, reactDates, widgetOptions, lang } =
this.props;
const noPastDates =
this.props.noPastDates || widgetOptions?.pattern_options?.noPastDates;
const moment = this.props.moment.default;
const datetime = this.getInternalValue();
const dateOnly = this.getDateOnly();
const { SingleDatePicker } = reactDates;
return (
<FormFieldWrapper {...this.props}>
<div className="date-time-widget-wrapper">
<div
className={cx('ui input date-input', {
'default-date': this.state.isDefault,
})}
>
<SingleDatePicker
date={datetime}
disabled={this.props.isDisabled}
onDateChange={this.onDateChange}
focused={this.state.focused}
numberOfMonths={1}
{...(noPastDates ? {} : { isOutsideRange: () => false })}
onFocusChange={this.onFocusChange}
noBorder
displayFormat={moment
.localeData(toBackendLang(lang))
.longDateFormat('L')}
navPrev={<PrevIcon />}
navNext={<NextIcon />}
id={`${id}-date`}
placeholder={intl.formatMessage(messages.date)}
openDirection={this.props.openDirection ?? 'down'}
/>
</div>
{!dateOnly && (
<div
className={cx('ui input time-input', {
'default-date': this.state.isDefault,
})}
>
<TimePicker
disabled={this.props.isDisabled}
defaultValue={datetime}
value={datetime}
onChange={this.onTimeChange}
allowEmpty={false}
showSecond={false}
use12Hours={lang === 'en'}
id={`${id}-time`}
format={moment
.localeData(toBackendLang(lang))
.longDateFormat('LT')}
placeholder={intl.formatMessage(messages.time)}
focusOnOpen
placement="bottomRight"
/>
</div>
)}
{resettable && (
<button
disabled={this.props.isDisabled || !datetime}
onClick={() => this.onResetDates()}
className="item ui noborder button"
>
<Icon name={clearSVG} size="24px" className="close" />
</button>
)}
</div>
</FormFieldWrapper>
);
}
}
/**
* Property types.
* @property {Object} propTypes Property types.
* @static
*/
DatetimeWidgetComponent.propTypes = {
id: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
description: PropTypes.string,
required: PropTypes.bool,
error: PropTypes.arrayOf(PropTypes.string),
dateOnly: PropTypes.bool,
noPastDates: PropTypes.bool,
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
wrapped: PropTypes.bool,
resettable: PropTypes.bool,
openDirection: PropTypes.oneOf(['up', 'down']),
};
/**
* Default properties.
* @property {Object} defaultProps Default properties.
* @static
*/
DatetimeWidgetComponent.defaultProps = {
description: null,
required: false,
error: [],
dateOnly: false,
noPastDates: false,
value: null,
resettable: true,
openDirection: 'down',
};
export default compose(
injectLazyLibs(['reactDates', 'moment']),
connect((state) => ({
lang: state.intl.locale,
})),
injectIntl,
)(DatetimeWidgetComponent);