sforkowany z mirror/soapbox
ScheduledStatuses: improve picker input UI
rodzic
4b141ef605
commit
2939b9e495
|
@ -3,20 +3,30 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import DatePicker from 'react-datepicker';
|
import DatePicker from 'react-datepicker';
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
|
import IconButton from 'soapbox/components/icon_button';
|
||||||
|
import { removeSchedule } from 'soapbox/actions/compose';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
schedule: { id: 'schedule.post_time', defaultMessage: 'Post Date/Time' },
|
schedule: { id: 'schedule.post_time', defaultMessage: 'Post Date/Time' },
|
||||||
|
remove: { id: 'schedule.remove', defaultMessage: 'Remove schedule' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
|
schedule: state.getIn(['compose', 'schedule']),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class ScheduleForm extends React.Component {
|
class ScheduleForm extends React.Component {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
schedule: PropTypes.instanceOf(Date),
|
schedule: PropTypes.instanceOf(Date),
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
onSchedule: PropTypes.func.isRequired,
|
onSchedule: PropTypes.func.isRequired,
|
||||||
|
dispatch: PropTypes.func,
|
||||||
active: PropTypes.bool,
|
active: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,33 +64,43 @@ class ScheduleForm extends React.Component {
|
||||||
return fiveMinutesFromNow.getTime() < selectedDate.getTime();
|
return fiveMinutesFromNow.getTime() < selectedDate.getTime();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleRemove = e => {
|
||||||
|
this.props.dispatch(removeSchedule());
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.active || !this.state) {
|
if (!this.props.active || !this.state) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { intl } = this.props;
|
||||||
const { schedule } = this.state;
|
const { schedule } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DatePicker
|
<div className='datepicker'>
|
||||||
selected={schedule}
|
<div className='datepicker__hint'>
|
||||||
showTimeSelect
|
<FormattedMessage id='datepicker.hint' defaultMessage='Scheduled to post at…' />
|
||||||
dateFormat='MMMM d, yyyy h:mm aa'
|
</div>
|
||||||
timeIntervals={15}
|
<div className='datepicker__input'>
|
||||||
wrapperClassName='react-datepicker-wrapper'
|
<DatePicker
|
||||||
onChange={this.setSchedule}
|
selected={schedule}
|
||||||
placeholderText={this.props.intl.formatMessage(messages.schedule)}
|
showTimeSelect
|
||||||
filterDate={this.isCurrentOrFutureDate}
|
dateFormat='MMMM d, yyyy h:mm aa'
|
||||||
filterTime={this.isFiveMinutesFromNow}
|
timeIntervals={15}
|
||||||
ref={this.isCurrentOrFutureDate(schedule) ? null : this.openDatePicker}
|
wrapperClassName='react-datepicker-wrapper'
|
||||||
/>
|
onChange={this.setSchedule}
|
||||||
|
placeholderText={this.props.intl.formatMessage(messages.schedule)}
|
||||||
|
filterDate={this.isCurrentOrFutureDate}
|
||||||
|
filterTime={this.isFiveMinutesFromNow}
|
||||||
|
ref={this.isCurrentOrFutureDate(schedule) ? null : this.openDatePicker}
|
||||||
|
/>
|
||||||
|
<div className='datepicker__cancel'>
|
||||||
|
<IconButton size={20} title={intl.formatMessage(messages.remove)} icon='times' onClick={this.handleRemove} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => ({
|
|
||||||
schedule: state.getIn(['compose', 'schedule']),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default injectIntl(connect(mapStateToProps)(ScheduleForm));
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ const mapStateToProps = state => ({
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
||||||
onClick() {
|
onClick() {
|
||||||
dispatch((_, getState) => {
|
dispatch((dispatch, getState) => {
|
||||||
if (getState().getIn(['compose', 'schedule'])) {
|
if (getState().getIn(['compose', 'schedule'])) {
|
||||||
dispatch(removeSchedule());
|
dispatch(removeSchedule());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -51,7 +51,7 @@ class ScheduledStatuses extends ImmutablePureComponent {
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.scheduled_statuses' defaultMessage="You don't have any scheduled statuses yet. When you add one, it will show up here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.scheduled_statuses' defaultMessage="You don't have any scheduled statuses yet. When you add one, it will show up here." />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column icon='edit' heading={intl.formatMessage(messages.heading)} backBtnSlim>
|
<Column icon='calendar' heading={intl.formatMessage(messages.heading)} backBtnSlim>
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='scheduled_statuses'
|
scrollKey='scheduled_statuses'
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
|
|
|
@ -1,4 +1,28 @@
|
||||||
.ui .react-datepicker {
|
.datepicker {
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px 10px 0;
|
||||||
|
width: 100%;
|
||||||
|
border-top: 1px solid var(--foreground-color);
|
||||||
|
|
||||||
|
&__hint {
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--primary-text-color--faint);
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__input {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__cancel {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.datepicker .react-datepicker {
|
||||||
box-shadow: 0 0 6px 0 rgb(0 0 0 / 30%);
|
box-shadow: 0 0 6px 0 rgb(0 0 0 / 30%);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
@ -7,9 +31,45 @@
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
|
|
||||||
&-wrapper {
|
&-wrapper {
|
||||||
margin-left: 10px;
|
width: 100%;
|
||||||
background: var(--foreground-color);
|
}
|
||||||
|
|
||||||
|
&__input-container {
|
||||||
border: 2px solid var(--brand-color);
|
border: 2px solid var(--brand-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
background: var(--background-color);
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
padding: 10px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 16px;
|
||||||
|
resize: vertical;
|
||||||
|
outline: 0;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '\f073';
|
||||||
|
display: inline-block;
|
||||||
|
font: normal normal normal 14px/1 ForkAwesome;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--primary-text-color--faint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__current-month,
|
&__current-month,
|
||||||
|
|
Ładowanie…
Reference in New Issue