Skip to content

Instantly share code, notes, and snippets.

@bowbowbow
Last active November 14, 2016 04:54
Show Gist options
  • Save bowbowbow/bec0961944aa1bd02b30ceab5121039a to your computer and use it in GitHub Desktop.
Save bowbowbow/bec0961944aa1bd02b30ceab5121039a to your computer and use it in GitHub Desktop.
/**
* Created by clsrn1581 on 2016. 09. 22...
*/
// Common Modules...
// 예전 버전에서 사용하던 태그
import React from 'react';
import { Additory } from 'additor-elements';
// Own Modules...
import Panel from '../../../../basic/components/Panel.jsx';
/**
* string에서 substring의 모든 시작 인덱스를 배열형태로 반환
* @param substring
* @param string
* @returns {Array}
*/
function locations(substring, string) {
const a = [];
let i = -1;
while ((i = string.indexOf(substring, i + 1)) >= 0) {
a.push(i);
}
return a;
}
export default class TagPanel extends Panel {
constructor(props) {
super(props);
this.state = {
tagInput: '',
tags: [],
suggestions: ['Banana', 'Apple', 'Melon'], // 자동완성 아직 미 구현
};
this.handleChangeTag = this._handleChangeTag.bind(this);
this.handleKeyPressTag = this._handleKeyPressTag.bind(this);
this.handleKeyDownTag = this._handleKeyDownTag.bind(this);
this.handleFocus = this._handleFocus.bind(this);
this.moveCursorEnd = this._moveCursorEnd.bind(this);
}
_handleChangeTag(e) {
this.setState({
tagInput: e.target.value,
});
}
_handleKeyPressTag(e) {
const { tagInput } = this.state;
if (e.key === 'Enter') {
// 엔터가 입력됐을 때 진입
this.setState({
tagInput: `${tagInput}∙`,
}, () => {
// 태그 입력들의 일관성을 유지시키는 과정
const tagRaws = tagInput.split('∙');
const tags = [];
// 입력에 존재하는 태그에서 중복과 스페이스를 제거 함
tagRaws.forEach((tag) => {
const tagS = tag.replace(/ /g, '_');
const index = tags.indexOf(tagS);
if (tagS !== '') {
if (index === -1) {
tags.push(tagS);
} else {
tags.splice(index, 1);
tags.push(tagS);
}
}
});
this.setState({
tags,
tagInput: `${tags.join('∙')}∙`,
}, () => {
// 상위 컴포넌트에서 태그 값 설정
this.props.setTags(tags);
// ∙이 추가되서 커서 포커스가 가장 오른쪽으로 안 옮겨지는 상황이 있어서
this.moveCursorEnd();
});
});
}
}
/**
* backspace 입력 발생 시 구분자 바깥에 있는 태그는 한 글자식 지우고
* 구분 자 안에 있는 태그는 태그 단위로 지운다.
* @param e
*/
_handleKeyDownTag(e) {
if (e.keyCode === 8) {
const { tags } = this.state;
const input = this.refs['addit-widget-card-tag'];
let cursorPos = input.selectionEnd;
if (cursorPos !== 0) {
/**
* 커서는 단어 사이마다 인덱스가 부여되기 때문에 단어와 인덱스를 맞춰주기 위함
*/
cursorPos -= 1;
}
// 어떤 태그 단락을 삭제할 지 탐색하는 과정
const indexes = locations('∙', input.value);
indexes.unshift(-1);
let posIndex = -1;
for (let i = 0; i < indexes.length - 1; i++) {
if (indexes[i] < cursorPos && cursorPos <= indexes[i + 1]) {
posIndex = i;
break;
}
}
if (posIndex !== -1) {
// 삭제 할 단락이 존재할 때 진입
_.pullAt(tags, posIndex);
let tagInput = tags.join('∙');
if (tagInput !== '') {
tagInput += '∙';
}
this.setState({
tags,
tagInput,
}, () => {
// 상위 컴포넌트에서 태그 값 설정
this.props.setTags(tags);
// 삭제 된 이후에도 커서가 가장 오른쪽으로 옮겨지지 않고 원래 위치를 유지하기 위해서
input.selectionStart = input.selectionEnd = cursorPos;
input.scrollLeft = cursorPos;
});
// backspace 입력을 차단 함
e.preventDefault();
e.stopPropagation();
}
}
}
/**
* 포커스 시 커서를 인풋 가장 뒤로 옮긴다.
* @param e
* @private
*/
_handleFocus(e) {
this.moveCursorEnd();
}
/**
* 인풋창의 커서를 가장 오른쪽으로 옮긴다.
* @private
*/
_moveCursorEnd() {
const { tagInput } = this.state;
/**
* 포커스 시 커서를 오른쪽으로 당겨야 하는데 포커스 시 마우스가 놓인 자리에
* 커서가 생기기 때문에 setTimeout으로 약간 딜레이를 줌
*/
setTimeout(() => {
const input = this.refs['addit-widget-card-tag'];
input.selectionStart = input.selectionEnd = tagInput.length;
input.scrollLeft = input.scrollWidth;
}, 10);
}
render() {
const { tagInput } = this.state;
return (
<div id="addit-widget-card-tag-wrapper">
<input
ref="addit-widget-card-tag"
id="addit-widget-card-tag"
value={tagInput}
onChange={this.handleChangeTag}
onKeyPress={this.handleKeyPressTag}
onKeyDown={this.handleKeyDownTag}
onFocus={this.handleFocus}
placeholder="Add a tag"
style={{ color: (tagInput.length > 0 ? '#3A434C' : '#B8BDC2') }}
/>
</div>
);
}
}
TagPanel.propTypes = {
setTags: React.PropTypes.func,
};
TagPanel.defaultProps = {};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment