Skip to content

Commit 3844251

Browse files
authored
Merge pull request #48 from xsnippet/syntaxes
Make Plain text as default and fix active index on search
2 parents 3c88cbc + 2546f5a commit 3844251

File tree

4 files changed

+124
-86
lines changed

4 files changed

+124
-86
lines changed

src/components/ListBox.jsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from 'react';
2+
import { Scrollbars } from 'react-custom-scrollbars';
3+
4+
class ListBox extends React.Component {
5+
constructor(props) {
6+
super(props);
7+
this.state = {
8+
selected: null,
9+
};
10+
this.onClick = this.onClick.bind(this);
11+
}
12+
13+
componentWillReceiveProps(nextProps) {
14+
// Consumers can either pass selected item via props, or let the component
15+
// to handle it on its own. The former might be useful when you want to
16+
// extend behaviour (e.g. some ListBoxWithSearchbar), while the latter -
17+
// when you want a standalone ListBox component.
18+
let selected = nextProps.selected || this.state.selected;
19+
20+
// If selected item is not a part of new items, aggressively fallback to
21+
// first item from the list. We're doing it to be protected from cases
22+
// when nothing is selected.
23+
if (!selected || !nextProps.items.contains(selected)) {
24+
selected = nextProps.items.get(0);
25+
nextProps.onClick(selected);
26+
}
27+
28+
this.setState({ selected });
29+
}
30+
31+
onClick(e) {
32+
const { item } = e.target.dataset;
33+
34+
this.setState({ selected: item });
35+
this.props.onClick(item);
36+
}
37+
38+
render() {
39+
const { items } = this.props;
40+
const { selected } = this.state;
41+
42+
return (
43+
// TODO: Get rid of domain specific CSS classes in favor of general one;
44+
// this component is a good candidate to be extracted into separate
45+
// library.
46+
<Scrollbars>
47+
<ul className="new-snippet-lang-list" role="presentation" onClick={this.onClick}>
48+
{items.size ? null : <li className="new-snippet-lang-empty">No results found</li>}
49+
{items.map(item => (
50+
<li
51+
className={`new-snippet-lang-item ${item === selected ? 'active' : ''}`}
52+
data-item={item}
53+
key={item}
54+
>
55+
{item}
56+
</li>
57+
))}
58+
</ul>
59+
</Scrollbars>
60+
);
61+
}
62+
}
63+
64+
export default ListBox;

src/components/ListBoxWithSearch.jsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react';
2+
3+
import ListBox from './ListBox';
4+
import { regExpEscape } from '../helpers';
5+
6+
class ListBoxWithSearch extends React.PureComponent {
7+
constructor(props) {
8+
super(props);
9+
this.state = {
10+
searchQuery: null,
11+
};
12+
this.onSearch = this.onSearch.bind(this);
13+
}
14+
15+
onSearch(e) {
16+
this.setState({ searchQuery: e.target.value.trim() });
17+
}
18+
19+
render() {
20+
const { searchQuery } = this.state;
21+
let { items } = this.props;
22+
23+
// Filter out only those items that match search query. If no query is
24+
// set, do nothing and use the entire set.
25+
if (searchQuery) {
26+
const regExp = new RegExp(regExpEscape(searchQuery), 'gi');
27+
items = items.filter(item => item.match(regExp));
28+
}
29+
30+
return (
31+
[
32+
<div className="new-snippet-lang-header" key="Syntax input">
33+
<input className="input" placeholder="Type to search..." onChange={this.onSearch} />
34+
</div>,
35+
<div className="new-snippet-lang-list-wrapper" key="Syntax list">
36+
<ListBox
37+
items={items}
38+
onClick={this.props.onClick}
39+
/>
40+
</div>,
41+
]
42+
);
43+
}
44+
}
45+
46+
export default ListBoxWithSearch;

src/components/NewSnippet.jsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'codemirror/lib/codemirror.css';
66

77
import Title from './common/Title';
88
import Input from './common/Input';
9-
import Syntaxes from './Syntaxes';
9+
import ListBoxWithSearch from './ListBoxWithSearch';
1010
import * as actions from '../actions';
1111

1212
import '../styles/NewSnippet.styl';
@@ -25,6 +25,12 @@ class NewSnippet extends React.Component {
2525
this.onInputChange = this.onInputChange.bind(this);
2626
}
2727

28+
componentDidMount() {
29+
const { dispatch } = this.props;
30+
31+
dispatch(actions.fetchSyntaxes);
32+
}
33+
2834
onSyntaxClick(syntax) {
2935
this.setState({ syntax }); // eslint-disable-line react/no-unused-state
3036
}
@@ -78,12 +84,17 @@ class NewSnippet extends React.Component {
7884
</div>
7985
</div>
8086
<div className="new-snippet-lang-wrapper">
81-
<Syntaxes onClick={this.onSyntaxClick} />
87+
<ListBoxWithSearch
88+
items={this.props.syntaxes}
89+
onClick={this.onSyntaxClick}
90+
/>
8291
</div>
8392
</form>,
8493
]
8594
);
8695
}
8796
}
8897

89-
export default connect()(NewSnippet);
98+
export default connect(state => ({
99+
syntaxes: state.get('syntaxes'),
100+
}))(NewSnippet);

src/components/Syntaxes.jsx

Lines changed: 0 additions & 83 deletions
This file was deleted.

0 commit comments

Comments
 (0)