custom-app
Version:
ITIMS��Ʒ�鿪��ר��React���,�Dz��ý��ּ�dhcc-app���������
472 lines (394 loc) • 17.1 kB
JavaScript
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule DraftEditor.react
* @format
*
* @preventMunge
*/
'use strict';
var _assign = require('object-assign');
var _extends = _assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var DefaultDraftBlockRenderMap = require('./DefaultDraftBlockRenderMap');
var DefaultDraftInlineStyle = require('./DefaultDraftInlineStyle');
var DraftEditorCompositionHandler = require('./DraftEditorCompositionHandler');
var DraftEditorContents = require('./DraftEditorContents.react');
var DraftEditorDragHandler = require('./DraftEditorDragHandler');
var DraftEditorEditHandler = require('./DraftEditorEditHandler');
var DraftEditorPlaceholder = require('./DraftEditorPlaceholder.react');
var EditorState = require('./EditorState');
var React = require('react');
var ReactDOM = require('react-dom');
var Scroll = require('fbjs/lib/Scroll');
var Style = require('fbjs/lib/Style');
var UserAgent = require('fbjs/lib/UserAgent');
var cx = require('fbjs/lib/cx');
var emptyFunction = require('fbjs/lib/emptyFunction');
var generateRandomKey = require('./generateRandomKey');
var getDefaultKeyBinding = require('./getDefaultKeyBinding');
var getScrollPosition = require('fbjs/lib/getScrollPosition');
var invariant = require('fbjs/lib/invariant');
var nullthrows = require('fbjs/lib/nullthrows');
var isIE = UserAgent.isBrowser('IE');
// IE does not support the `input` event on contentEditable, so we can't
// observe spellcheck behavior.
var allowSpellCheck = !isIE;
// Define a set of handler objects to correspond to each possible `mode`
// of editor behavior.
var handlerMap = {
edit: DraftEditorEditHandler,
composite: DraftEditorCompositionHandler,
drag: DraftEditorDragHandler,
cut: null,
render: null
};
/**
* `DraftEditor` is the root editor component. It composes a `contentEditable`
* div, and provides a wide variety of useful function props for managing the
* state of the editor. See `DraftEditorProps` for details.
*/
var DraftEditor = function (_React$Component) {
_inherits(DraftEditor, _React$Component);
function DraftEditor(props) {
_classCallCheck(this, DraftEditor);
var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));
_this.focus = function (scrollPosition) {
var editorState = _this.props.editorState;
var alreadyHasFocus = editorState.getSelection().getHasFocus();
var editorNode = ReactDOM.findDOMNode(_this.editor);
if (!editorNode) {
// once in a while people call 'focus' in a setTimeout, and the node has
// been deleted, so it can be null in that case.
return;
}
var scrollParent = Style.getScrollParent(editorNode);
var _ref = scrollPosition || getScrollPosition(scrollParent),
x = _ref.x,
y = _ref.y;
!(editorNode instanceof HTMLElement) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'editorNode is not an HTMLElement') : invariant(false) : void 0;
editorNode.focus();
// Restore scroll position
if (scrollParent === window) {
window.scrollTo(x, y);
} else {
Scroll.setTop(scrollParent, y);
}
// On Chrome and Safari, calling focus on contenteditable focuses the
// cursor at the first character. This is something you don't expect when
// you're clicking on an input element but not directly on a character.
// Put the cursor back where it was before the blur.
if (!alreadyHasFocus) {
_this.update(EditorState.forceSelection(editorState, editorState.getSelection()));
}
};
_this.blur = function () {
var editorNode = ReactDOM.findDOMNode(_this.editor);
!(editorNode instanceof HTMLElement) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'editorNode is not an HTMLElement') : invariant(false) : void 0;
editorNode.blur();
};
_this.setMode = function (mode) {
_this._handler = handlerMap[mode];
};
_this.exitCurrentMode = function () {
_this.setMode('edit');
};
_this.restoreEditorDOM = function (scrollPosition) {
_this.setState({ contentsKey: _this.state.contentsKey + 1 }, function () {
_this.focus(scrollPosition);
});
};
_this.setClipboard = function (clipboard) {
_this._clipboard = clipboard;
};
_this.getClipboard = function () {
return _this._clipboard;
};
_this.update = function (editorState) {
_this._latestEditorState = editorState;
_this.props.onChange(editorState);
};
_this.onDragEnter = function () {
_this._dragCount++;
};
_this.onDragLeave = function () {
_this._dragCount--;
if (_this._dragCount === 0) {
_this.exitCurrentMode();
}
};
_this._blockSelectEvents = false;
_this._clipboard = null;
_this._handler = null;
_this._dragCount = 0;
_this._editorKey = props.editorKey || generateRandomKey();
_this._placeholderAccessibilityID = 'placeholder-' + _this._editorKey;
_this._latestEditorState = props.editorState;
_this._latestCommittedEditorState = props.editorState;
_this._onBeforeInput = _this._buildHandler('onBeforeInput');
_this._onBlur = _this._buildHandler('onBlur');
_this._onCharacterData = _this._buildHandler('onCharacterData');
_this._onCompositionEnd = _this._buildHandler('onCompositionEnd');
_this._onCompositionStart = _this._buildHandler('onCompositionStart');
_this._onCopy = _this._buildHandler('onCopy');
_this._onCut = _this._buildHandler('onCut');
_this._onDragEnd = _this._buildHandler('onDragEnd');
_this._onDragOver = _this._buildHandler('onDragOver');
_this._onDragStart = _this._buildHandler('onDragStart');
_this._onDrop = _this._buildHandler('onDrop');
_this._onInput = _this._buildHandler('onInput');
_this._onFocus = _this._buildHandler('onFocus');
_this._onKeyDown = _this._buildHandler('onKeyDown');
_this._onKeyPress = _this._buildHandler('onKeyPress');
_this._onKeyUp = _this._buildHandler('onKeyUp');
_this._onMouseDown = _this._buildHandler('onMouseDown');
_this._onMouseUp = _this._buildHandler('onMouseUp');
_this._onPaste = _this._buildHandler('onPaste');
_this._onSelect = _this._buildHandler('onSelect');
_this.getEditorKey = function () {
return _this._editorKey;
};
// See `restoreEditorDOM()`.
_this.state = { contentsKey: 0 };
return _this;
}
/**
* Build a method that will pass the event to the specified handler method.
* This allows us to look up the correct handler function for the current
* editor mode, if any has been specified.
*/
/**
* Define proxies that can route events to the current handler.
*/
DraftEditor.prototype._buildHandler = function _buildHandler(eventName) {
var _this2 = this;
return function (e) {
if (!_this2.props.readOnly) {
var method = _this2._handler && _this2._handler[eventName];
method && method(_this2, e);
}
};
};
DraftEditor.prototype._showPlaceholder = function _showPlaceholder() {
return !!this.props.placeholder && !this.props.editorState.isInCompositionMode() && !this.props.editorState.getCurrentContent().hasText();
};
DraftEditor.prototype._renderPlaceholder = function _renderPlaceholder() {
if (this._showPlaceholder()) {
var placeHolderProps = {
text: nullthrows(this.props.placeholder),
editorState: this.props.editorState,
textAlignment: this.props.textAlignment,
accessibilityID: this._placeholderAccessibilityID
};
return React.createElement(DraftEditorPlaceholder, placeHolderProps);
}
return null;
};
DraftEditor.prototype.render = function render() {
var _this3 = this;
var _props = this.props,
blockRenderMap = _props.blockRenderMap,
blockRendererFn = _props.blockRendererFn,
blockStyleFn = _props.blockStyleFn,
customStyleFn = _props.customStyleFn,
customStyleMap = _props.customStyleMap,
editorState = _props.editorState,
readOnly = _props.readOnly,
textAlignment = _props.textAlignment,
textDirectionality = _props.textDirectionality;
var rootClass = cx({
'DraftEditor/root': true,
'DraftEditor/alignLeft': textAlignment === 'left',
'DraftEditor/alignRight': textAlignment === 'right',
'DraftEditor/alignCenter': textAlignment === 'center'
});
var contentStyle = {
outline: 'none',
// fix parent-draggable Safari bug. #1326
userSelect: 'text',
WebkitUserSelect: 'text',
whiteSpace: 'pre-wrap',
wordWrap: 'break-word'
};
// The aria-expanded and aria-haspopup properties should only be rendered
// for a combobox.
var ariaRole = this.props.role || 'textbox';
var ariaExpanded = ariaRole === 'combobox' ? !!this.props.ariaExpanded : null;
var editorContentsProps = {
blockRenderMap: blockRenderMap,
blockRendererFn: blockRendererFn,
blockStyleFn: blockStyleFn,
customStyleMap: _extends({}, DefaultDraftInlineStyle, customStyleMap),
customStyleFn: customStyleFn,
editorKey: this._editorKey,
editorState: editorState,
key: 'contents' + this.state.contentsKey,
textDirectionality: textDirectionality
};
return React.createElement(
'div',
{ className: rootClass },
this._renderPlaceholder(),
React.createElement(
'div',
{
className: cx('DraftEditor/editorContainer'),
ref: function ref(_ref3) {
return _this3.editorContainer = _ref3;
} },
React.createElement(
'div',
{
'aria-activedescendant': readOnly ? null : this.props.ariaActiveDescendantID,
'aria-autocomplete': readOnly ? null : this.props.ariaAutoComplete,
'aria-controls': readOnly ? null : this.props.ariaControls,
'aria-describedby': this.props.ariaDescribedBy || this._placeholderAccessibilityID,
'aria-expanded': readOnly ? null : ariaExpanded,
'aria-label': this.props.ariaLabel,
'aria-labelledby': this.props.ariaLabelledBy,
'aria-multiline': this.props.ariaMultiline,
autoCapitalize: this.props.autoCapitalize,
autoComplete: this.props.autoComplete,
autoCorrect: this.props.autoCorrect,
className: cx({
// Chrome's built-in translation feature mutates the DOM in ways
// that Draft doesn't expect (ex: adding <font> tags inside
// DraftEditorLeaf spans) and causes problems. We add notranslate
// here which makes its autotranslation skip over this subtree.
notranslate: !readOnly,
'public/DraftEditor/content': true
}),
contentEditable: !readOnly,
'data-testid': this.props.webDriverTestID,
onBeforeInput: this._onBeforeInput,
onBlur: this._onBlur,
onCompositionEnd: this._onCompositionEnd,
onCompositionStart: this._onCompositionStart,
onCopy: this._onCopy,
onCut: this._onCut,
onDragEnd: this._onDragEnd,
onDragEnter: this.onDragEnter,
onDragLeave: this.onDragLeave,
onDragOver: this._onDragOver,
onDragStart: this._onDragStart,
onDrop: this._onDrop,
onFocus: this._onFocus,
onInput: this._onInput,
onKeyDown: this._onKeyDown,
onKeyPress: this._onKeyPress,
onKeyUp: this._onKeyUp,
onMouseUp: this._onMouseUp,
onPaste: this._onPaste,
onSelect: this._onSelect,
ref: function ref(_ref2) {
return _this3.editor = _ref2;
},
role: readOnly ? null : ariaRole,
spellCheck: allowSpellCheck && this.props.spellCheck,
style: contentStyle,
suppressContentEditableWarning: true,
tabIndex: this.props.tabIndex },
React.createElement(DraftEditorContents, editorContentsProps)
)
)
);
};
DraftEditor.prototype.componentDidMount = function componentDidMount() {
this.setMode('edit');
/**
* IE has a hardcoded "feature" that attempts to convert link text into
* anchors in contentEditable DOM. This breaks the editor's expectations of
* the DOM, and control is lost. Disable it to make IE behave.
* See: http://blogs.msdn.com/b/ieinternals/archive/2010/09/15/
* ie9-beta-minor-change-list.aspx
*/
if (isIE) {
document.execCommand('AutoUrlDetect', false, false);
}
};
/**
* Prevent selection events from affecting the current editor state. This
* is mostly intended to defend against IE, which fires off `selectionchange`
* events regardless of whether the selection is set via the browser or
* programmatically. We only care about selection events that occur because
* of browser interaction, not re-renders and forced selections.
*/
DraftEditor.prototype.componentWillUpdate = function componentWillUpdate(nextProps) {
this._blockSelectEvents = true;
this._latestEditorState = nextProps.editorState;
};
DraftEditor.prototype.componentDidUpdate = function componentDidUpdate() {
this._blockSelectEvents = false;
this._latestCommittedEditorState = this.props.editorState;
};
/**
* Used via `this.focus()`.
*
* Force focus back onto the editor node.
*
* We attempt to preserve scroll position when focusing. You can also pass
* a specified scroll position (for cases like `cut` behavior where it should
* be restored to a known position).
*/
/**
* Used via `this.setMode(...)`.
*
* Set the behavior mode for the editor component. This switches the current
* handler module to ensure that DOM events are managed appropriately for
* the active mode.
*/
/**
* Used via `this.restoreEditorDOM()`.
*
* Force a complete re-render of the DraftEditorContents based on the current
* EditorState. This is useful when we know we are going to lose control of
* the DOM state (cut command, IME) and we want to make sure that
* reconciliation occurs on a version of the DOM that is synchronized with
* our EditorState.
*/
/**
* Used via `this.setClipboard(...)`.
*
* Set the clipboard state for a cut/copy event.
*/
/**
* Used via `this.getClipboard()`.
*
* Retrieve the clipboard state for a cut/copy event.
*/
/**
* Used via `this.update(...)`.
*
* Propagate a new `EditorState` object to higher-level components. This is
* the method by which event handlers inform the `DraftEditor` component of
* state changes. A component that composes a `DraftEditor` **must** provide
* an `onChange` prop to receive state updates passed along from this
* function.
*/
/**
* Used in conjunction with `onDragLeave()`, by counting the number of times
* a dragged element enters and leaves the editor (or any of its children),
* to determine when the dragged element absolutely leaves the editor.
*/
/**
* See `onDragEnter()`.
*/
return DraftEditor;
}(React.Component);
DraftEditor.defaultProps = {
blockRenderMap: DefaultDraftBlockRenderMap,
blockRendererFn: emptyFunction.thatReturnsNull,
blockStyleFn: emptyFunction.thatReturns(''),
keyBindingFn: getDefaultKeyBinding,
readOnly: false,
spellCheck: false,
stripPastedStyles: false
};
module.exports = DraftEditor;