Skip to content

Commit

Permalink
[components] Clean up tags textfield and make focusable (#369)
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoerge committed Jan 9, 2018
1 parent 8588a39 commit 64157d0
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 101 deletions.
138 changes: 69 additions & 69 deletions packages/@sanity/components/src/tags/TextField.js
Original file line number Diff line number Diff line change
@@ -1,113 +1,112 @@
import PropTypes from 'prop-types'
import React from 'react'
import {uniqueId} from 'lodash'

import styles from 'part:@sanity/components/tags/textfield-style'

function removeAt(array, index) {
const copy = array ? array.slice() : []
copy.splice(index, 1)
return copy
}

export default class TagsTextField extends React.Component {
static propTypes = {
label: PropTypes.string.isRequired,
onAddTag: PropTypes.func.isRequired,
onRemoveTag: PropTypes.func.isRequired,
error: PropTypes.bool,
hasFocus: PropTypes.bool,
isClearable: PropTypes.bool,
tags: PropTypes.arrayOf(PropTypes.string),
description: PropTypes.string,
level: PropTypes.number
onChange: PropTypes.func.isRequired,
onBlur: PropTypes.func,
value: PropTypes.arrayOf(PropTypes.string)
}

static defaultProps = {
tags: []
value: [],
onBlur: () => {}
}

constructor(props, context) {
super(props, context)
this.handleKeyPress = this.handleKeyPress.bind(this)
this.handleKeyDown = this.handleKeyDown.bind(this)
this.handleSetFocus = this.handleSetFocus.bind(this)
this.handleFocus = this.handleFocus.bind(this)
this.handleBlur = this.handleBlur.bind(this)

this.state = {
length: 4,
hasFocus: this.props.hasFocus
}
state = {
inputValue: ''
}

addTag(title) {
this.props.onAddTag(title)
addTag(tagValue) {
const {value, onChange} = this.props
onChange((value || []).concat(tagValue))
}

removeTag(i) {
this.props.onRemoveTag(i)
removeTag(index) {
const {value, onChange} = this.props
onChange(removeAt(value, index))
}

handleKeyDown(event) {
const value = this._input.value
if (event.key === 'Backspace' && value === '') {
this.removeTag(this.props.tags.length - 1)
}

// length is used for styling purpose
addAndClearInput(tagValue) {
this.addTag(tagValue)
// clear input value
this.setState({
length: value.length > 3 ? value.length : 3
inputValue: ''
})
}

handleKeyPress(event) {
const value = this._input.value
handleRemoveTagClick = event => {
this.removeTag(Number(event.currentTarget.getAttribute('data-index')))
}

handleKeyDown = event => {
const {value} = this.props
const {inputValue} = this.state

if (event.key === 'Backspace' && inputValue === '') {
this.removeTag(value.length - 1)
}
}

handleKeyPress = event => {
const {inputValue} = this.state

if (event.key === 'Enter') {
if (value) {
this.addTag(value)
}
this._input.value = ''
if (inputValue && event.key === 'Enter') {
this.addAndClearInput(inputValue)
}
}

handleSetFocus() {
this._input.focus()
handleBlur = event => {
const {inputValue} = this.state
if (inputValue) {
this.addAndClearInput(inputValue)
}
this.props.onBlur(event)
}

handleFocus() {
this.setState({
hasFocus: true
})
handleInputChange = event => {
this.setState({inputValue: event.currentTarget.value})
}

handleBlur() {
const value = this._input.value
if (value) {
this.addTag(value)
this._input.value = ''
focus() {
if (this._input) {
this._input.focus()
}
this.setState({
hasFocus: false
})
}

componentWillMount() {
this._inputId = uniqueId('DefaultTextField')
setInput = el => {
this._input = el
}

render() {
const {tags} = this.props
const setInput = component => {
this._input = component
}
const {inputValue} = this.state
const {
onChange,
value,
...rest
} = this.props

return (
<div className={styles.wrapper}>
<div className={`${styles.inner}`}>
<div className={styles.content} onClick={this.handleSetFocus}>
<div className={styles.content}>
<ul className={styles.tags}>
{
tags && tags.map((tag, i) => {
value && value.map((tag, i) => {
return (
<li key={i} className={styles.tag}>
{tag}
<a
onClick={this.removeTag.bind(this, i)} // eslint-disable-line react/jsx-no-bind
onClick={this.handleRemoveTagClick}
data-index={i}
className={styles.clearTag}
>
×
Expand All @@ -118,14 +117,15 @@ export default class TagsTextField extends React.Component {
}
</ul>
<input
{...rest}
value={inputValue}
className={styles.input}
onKeyDown={this.handleKeyDown}
onKeyPress={this.handleKeyPress}
style={{width: `${this.state.length * 0.8}em`}}
onChange={this.handleInputChange}
style={{width: `${Math.max(3, inputValue.length) * 0.8}em`}}
onBlur={this.handleBlur}
onFocus={this.handleFocus}
ref={setInput}
id={this._inputId}
ref={this.setInput}
autoComplete="off"
/>
</div>
Expand Down
26 changes: 5 additions & 21 deletions packages/@sanity/components/src/tags/story.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,12 @@ class DefaultTextFieldTagsImplementation extends React.Component {
}
constructor(...args) {
super(...args)

this.handleAddTag = this.handleAddTag.bind(this)
this.handleRemoveTag = this.handleRemoveTag.bind(this)

this.state = {
tags: this.props.tags || []
}
}

handleAddTag(tag) {
const tags = this.state.tags.concat()
tags.push(tag)
this.setState({
tags: tags
})
}

handleRemoveTag(i) {
const tags = this.state.tags.concat()
tags.splice(i, 1)
handleChange = tags => {
this.setState({
tags: tags
})
Expand All @@ -41,9 +27,8 @@ class DefaultTextFieldTagsImplementation extends React.Component {
<TagsTextField
label="Tags"
placeholder="This is the placeholder"
tags={this.state.tags}
onAddTag={this.handleAddTag}
onRemoveTag={this.handleRemoveTag}
value={this.state.tags}
onChange={this.handleChange}
/>
)
}
Expand All @@ -61,9 +46,8 @@ storiesOf('Tags')
<TagsTextField
label={text('label (prop)', 'Tags')}
placeholder={text('placeholder (prop)', 'This is the placeholder')}
tags={array('tags (prop)', tags)}
onAddTag={action('onAddTag')}
onRemoveTag={action('onRemoveTag')}
value={array('value (prop)', tags)}
onChange={action('onChange')}
/>
</Sanity>
)
Expand Down
12 changes: 1 addition & 11 deletions packages/@sanity/components/src/tags/styles/TextField.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
overflow: hidden;
padding-left: 0.3em;

@nest .isFocused & {
@nest :focus-within & {
border-color: var(--input-border-color-focus);
}
}
Expand All @@ -81,13 +81,3 @@
.wrapper {
position: relative;
}

.focusHelper {
composes: focusHelper from 'part:@sanity/base/theme/forms/text-input-style';
background-color: transparent;
}

.label {
composes: label from 'part:@sanity/components/labels/default-style';
display: block;
}

0 comments on commit 64157d0

Please sign in to comment.