Add a text edit area

This commit is contained in:
DD 2018-03-20 10:48:35 -04:00
parent cbd2a89cd0
commit da0864b81b
4 changed files with 80 additions and 29 deletions

View file

@ -0,0 +1,15 @@
.text-area {
background: transparent;
border: none;
display: none;
font-family: Times;
font-size: 30px;
left: 0;
outline: none;
overflow: hidden;
position: absolute;
resize: none;
top: 0;
white-space: nowrap;
z-index: 1;
}

View file

@ -3,23 +3,32 @@ import PropTypes from 'prop-types';
import ToolSelectComponent from '../tool-select-base/tool-select-base.jsx'; import ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';
import textIcon from './text.svg'; import textIcon from './text.svg';
import styles from './text-mode.css';
const TextModeComponent = props => ( const TextModeComponent = props => (
<ToolSelectComponent <div>
imgDescriptor={{ <ToolSelectComponent
defaultMessage: 'Text', imgDescriptor={{
description: 'Label for the text tool', defaultMessage: 'Text',
id: 'paint.textMode.text' description: 'Label for the text tool',
}} id: 'paint.textMode.text'
imgSrc={textIcon} }}
isSelected={props.isSelected} imgSrc={textIcon}
onMouseDown={props.onMouseDown} isSelected={props.isSelected}
/> onMouseDown={props.onMouseDown}
/>
<textarea
className={styles.textArea}
ref={props.setTextArea}
/>
</div>
); );
TextModeComponent.propTypes = { TextModeComponent.propTypes = {
isSelected: PropTypes.bool.isRequired, isSelected: PropTypes.bool.isRequired,
onMouseDown: PropTypes.func.isRequired onMouseDown: PropTypes.func.isRequired,
setTextArea: PropTypes.func.isRequired
}; };
export default TextModeComponent; export default TextModeComponent;

View file

@ -21,7 +21,8 @@ class TextMode extends React.Component {
super(props); super(props);
bindAll(this, [ bindAll(this, [
'activateTool', 'activateTool',
'deactivateTool' 'deactivateTool',
'setTextArea'
]); ]);
} }
componentDidMount () { componentDidMount () {
@ -64,6 +65,7 @@ class TextMode extends React.Component {
this.props.onChangeStrokeColor(null); this.props.onChangeStrokeColor(null);
} }
this.tool = new TextTool( this.tool = new TextTool(
this.textArea,
this.props.setSelectedItems, this.props.setSelectedItems,
this.props.clearSelectedItems, this.props.clearSelectedItems,
this.props.onUpdateSvg, this.props.onUpdateSvg,
@ -77,11 +79,15 @@ class TextMode extends React.Component {
this.tool.remove(); this.tool.remove();
this.tool = null; this.tool = null;
} }
setTextArea (element) {
this.textArea = element;
}
render () { render () {
return ( return (
<TextModeComponent <TextModeComponent
isSelected={this.props.isTextModeActive} isSelected={this.props.isTextModeActive}
onMouseDown={this.props.handleMouseDown} onMouseDown={this.props.handleMouseDown}
setTextArea={this.setTextArea}
/> />
); );
} }

View file

@ -28,13 +28,15 @@ class TextTool extends paper.Tool {
return 8; return 8;
} }
/** /**
* @param {HTMLTextAreaElement} textAreaElement dom element for the editable text field
* @param {function} setSelectedItems Callback to set the set of selected items in the Redux state * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
* @param {!function} onUpdateSvg A callback to call when the image visibly changes * @param {!function} onUpdateSvg A callback to call when the image visibly changes
* @param {!function} setTextEditTarget Call to set text editing target whenever text editing is active * @param {!function} setTextEditTarget Call to set text editing target whenever text editing is active
*/ */
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg, setTextEditTarget) { constructor (textAreaElement, setSelectedItems, clearSelectedItems, onUpdateSvg, setTextEditTarget) {
super(); super();
this.element = textAreaElement;
this.setSelectedItems = setSelectedItems; this.setSelectedItems = setSelectedItems;
this.clearSelectedItems = clearSelectedItems; this.clearSelectedItems = clearSelectedItems;
this.onUpdateSvg = onUpdateSvg; this.onUpdateSvg = onUpdateSvg;
@ -141,10 +143,6 @@ class TextTool extends paper.Tool {
const hitResults = paper.project.hitTestAll(event.point, this.getTextEditHitOptions()); const hitResults = paper.project.hitTestAll(event.point, this.getTextEditHitOptions());
if (hitResults.length) { if (hitResults.length) {
// Clicking a text item to begin text edit mode on that item // Clicking a text item to begin text edit mode on that item
if (this.guide) {
this.guide.remove();
this.guide = null;
}
this.textBox = hitResults[0].item; this.textBox = hitResults[0].item;
this.mode = TextTool.TEXT_EDIT_MODE; this.mode = TextTool.TEXT_EDIT_MODE;
} else if (this.mode === TextTool.TEXT_EDIT_MODE) { } else if (this.mode === TextTool.TEXT_EDIT_MODE) {
@ -171,13 +169,9 @@ class TextTool extends paper.Tool {
} }
if (this.mode === TextTool.TEXT_EDIT_MODE) { if (this.mode === TextTool.TEXT_EDIT_MODE) {
this.guide = hoverBounds(this.textBox, TextTool.TEXT_PADDING); this.beginTextEdit(event.point);
this.guide.dashArray = [4, 4]; } else {
this.setTextEditTarget(this.textBox.id); this.endTextEdit();
} else if (this.guide) {
this.guide.remove();
this.guide = null;
this.setTextEditTarget();
} }
} }
handleMouseDrag (event) { handleMouseDrag (event) {
@ -224,17 +218,44 @@ class TextTool extends paper.Tool {
this.guide.dashArray = [4, 4]; this.guide.dashArray = [4, 4];
} }
} }
beginTextEdit (location) {
if (this.guide) {
this.guide.remove();
}
this.guide = hoverBounds(this.textBox, TextTool.TEXT_PADDING);
this.guide.dashArray = [4, 4];
this.setTextEditTarget(this.textBox.id);
const canvasRect = paper.view.element.getBoundingClientRect();
this.element.style.display = 'initial';
this.element.style.text = 'hello';
this.element.style['text-fill-color'] = this.colorState.fillColor;
this.element.style['text-stroke-color'] = this.colorState.strokeColor;
this.element.style['text-stroke-width'] = this.colorState.strokeWidth;
this.element.style['-webkit-text-fill-color'] = this.colorState.fillColor;
this.element.style['-webkit-text-stroke-color'] = this.colorState.strokeColor;
this.element.style['-webkit-text-stroke-width'] = this.colorState.strokeWidth + 'px';
this.element.style.transform =
`translate(${location.x + canvasRect.x}px, ${location.y + canvasRect.y}px)`;
this.element.focus();
}
endTextEdit () {
if (this.guide) {
this.guide.remove();
this.guide = null;
this.setTextEditTarget();
}
this.element.style.display = 'none';
}
deactivateTool () { deactivateTool () {
this.boundingBoxTool.removeBoundsPath(); this.boundingBoxTool.removeBoundsPath();
if (this.textBox && this.textBox.content.trim() === '') { if (this.textBox && this.textBox.content.trim() === '') {
this.textBox.remove(); this.textBox.remove();
this.textBox = null; this.textBox = null;
} }
if (this.guide) { this.endTextEdit();
this.guide.remove();
this.guide = null;
this.setTextEditTarget();
}
} }
} }