Shawn Erquhart 18c579d0e9 feat: Code Widget + Markdown Widget Internal Overhaul (#2828)
* wip - upgrade to slate 0.43

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* finish list handling logic

* add plugins directory

* tests wip

* setup testing

* wip

* add selection commands

* finish list testing

* stuff

* add codemirror

* abstract codemirror from slate

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* codemirror mostly working, some bugs

* upgrade to slate 46

* upgrade to slate 47

* wip

* wip

* progress

* wip

* mostly working links with surrounding marks

* wip

* tests passing

* add test

* fix formatting

* update snapshots

* close self closing tag in markdown html output

* wip - commonmark

* hold on commonmark work

* all tests passing

* fix e2e specs

* ignore tests in esm builds

* break/backspace plugins wip

* finish enter/backspace spec

* fix soft break handling

* wip - editor component deletion

* add insertion points

* make insertion points invisible

* fix empty mark nodes output to markdown

* fix pasting

* improve insertion points

* add static bottom insertion point

* improve click handling at insertion points

* restore current table functionality

* add paste support for Slate fragments

* support cut/copy markdown, paste between rich/raw editor

* fix copy paste

* wip - paste/select bug fixing

* fixed known slate issues

* split plugins

* fix editor toggles

* force text cursor in code widget

* wip - reorg plugins

* finish markdown control reorg

* configure plugin types

* quote block adjacent handling with tests

* wip

* finish quote logic and tests

* fix copy paste plugin migration regressions

* fix force insert before node

* fix trailing insertion point

* remove empty headers

* codemirror working properly in markdown widget

* return focus to codemirror on lang select enter

* fix state issues for widgets with local state

* wip - vim working, just need to work out distribution

* add settings pane

* wip - default modes

* fix deps

* add programming language data

* implement linguist langs in code widget

* everything built in

* remove old registration code, fix focus styling

* fix/update linting setup

* fix js lint errors

* remove stylelint from format script

* fix remaining linting errors

* fix reducer test failures

* chore: update commitlint for worktree support

* chore: fix remaining tests

* chore: drop unused monaco plugin

* chore: remove extraneous global styles rendering

* chore: fix failing tests

* fix: tests

* fix: quote/list nesting (tests still broken)

* fix: update quote tests

* chore: bring back code widget test config

* fix: autofocus

* fix: code blocks without the code widget

* fix: code editor component state issues

* fix: error

* fix: add code block test, few fixes

* chore: remove notes

* fix: [wip] update stateful shortcodes on undo/redo

* fix: support code styled links, handle unknown langs

* fix: few fixes

* fix: autofocus on insert, focus on all clicks

* fix: linting

* fix: autofocus

* fix: update code block fixture

* fix: remove unused cypress snapshot plugin

* fix: drop node 8 test, add node 12

* fix: use lodash.flatten instead of Array.flat

* fix: remove console logs
2019-12-16 19:17:37 +02:00

137 lines
3.7 KiB
JavaScript

import React from 'react';
import PropTypes from 'prop-types';
import { css } from '@emotion/core';
import reactDateTimeStyles from 'react-datetime/css/react-datetime.css';
import DateTime from 'react-datetime';
import moment from 'moment';
import { once } from 'lodash';
import { oneLine } from 'common-tags';
const warnDeprecated = once(() =>
console.warn(oneLine`
Netlify CMS config: the date widget has been deprecated and will
be removed in the next major release. Please use the datetime widget instead.
`),
);
/**
* `date` widget is deprecated in favor of the `datetime` widget
*/
export default class DateControl extends React.Component {
static propTypes = {
field: PropTypes.object.isRequired,
forID: PropTypes.string,
onChange: PropTypes.func.isRequired,
classNameWrapper: PropTypes.string.isRequired,
setActiveStyle: PropTypes.func.isRequired,
setInactiveStyle: PropTypes.func.isRequired,
value: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
includeTime: PropTypes.bool,
};
getFormats() {
const { field, includeTime } = this.props;
const format = field.get('format');
// dateFormat and timeFormat are strictly for modifying
// input field with the date/time pickers
const dateFormat = field.get('dateFormat');
// show time-picker? false hides it, true shows it using default format
let timeFormat = field.get('timeFormat');
if (typeof timeFormat === 'undefined') {
timeFormat = !!includeTime;
}
return {
format,
dateFormat,
timeFormat,
};
}
formats = this.getFormats();
componentDidMount() {
warnDeprecated();
const { value } = this.props;
/**
* Set the current date as default value if no default value is provided. An
* empty string means the value is intentionally blank.
*/
if (!value && value !== '') {
setTimeout(() => {
this.handleChange(new Date());
}, 0);
}
}
// Date is valid if datetime is a moment or Date object otherwise it's a string.
// Handle the empty case, if the user wants to empty the field.
isValidDate = datetime =>
moment.isMoment(datetime) || datetime instanceof Date || datetime === '';
handleChange = datetime => {
/**
* Set the date only if it is valid.
*/
if (!this.isValidDate(datetime)) {
return;
}
const { onChange } = this.props;
const { format } = this.formats;
/**
* Produce a formatted string only if a format is set in the config.
* Otherwise produce a date object.
*/
if (format) {
const formattedValue = datetime ? moment(datetime).format(format) : '';
onChange(formattedValue);
} else {
const value = moment.isMoment(datetime) ? datetime.toDate() : datetime;
onChange(value);
}
};
onBlur = datetime => {
const { setInactiveStyle } = this.props;
if (!this.isValidDate(datetime)) {
const parsedDate = moment(datetime);
if (parsedDate.isValid()) {
this.handleChange(datetime);
} else {
window.alert('The date you entered is invalid.');
}
}
setInactiveStyle();
};
render() {
const { forID, value, classNameWrapper, setActiveStyle } = this.props;
const { format, dateFormat, timeFormat } = this.formats;
return (
<div
css={css`
${reactDateTimeStyles};
`}
>
<DateTime
dateFormat={dateFormat}
timeFormat={timeFormat}
value={moment(value, format)}
onChange={this.handleChange}
onFocus={setActiveStyle}
onBlur={this.onBlur}
inputProps={{ className: classNameWrapper, id: forID }}
/>
</div>
);
}
}