Skip to content

Commit f2c461b

Browse files
author
春秋一语
authored
Merge pull request #483 from chunqiuyiyu/dashboard-dev-search
Modify search widget style
2 parents a795d43 + 7d2b237 commit f2c461b

File tree

6 files changed

+179
-352
lines changed

6 files changed

+179
-352
lines changed

app/commons/Search/state.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const ws = observable({
66
first: true
77
})
88

9-
export const searching = map({
9+
export const searching = observable({
1010
pattern: '',
1111
path: '',
1212
caseSensitive: false,

app/components/Panel/PanelContent.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import FileTree from '../FileTree'
99
import SideBar from './SideBar/SideBar'
1010
import { SidePanelContainer, SidePanelView } from './SideBar/SidePanel'
1111
import FileList from '../Tab/fileList'
12-
import SearchPanel from '../Search/search.new'
12+
import SearchPanel from '../Search/search'
1313
import config from '../../config'
1414
import PluginDev from 'components/PluginDev'
1515
import FileTreeToolBar from 'components/FileTreeToolBar'

app/components/Search/search.jsx

Lines changed: 173 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,173 +1,198 @@
1-
import React, { Component } from 'react'
1+
import { Component } from "react";
22
import { observer } from 'mobx-react'
3-
import debounce from 'lodash/debounce'
4-
import { autorun } from 'mobx'
5-
import { FsSocketClient } from 'backendAPI/websocketClients'
6-
import api from 'backendAPI'
3+
import subscribeToSearch from 'commons/Search/subscribeToSearch'
4+
import state from 'commons/Search/state'
75
import cx from 'classnames'
8-
import icons from 'file-icons-js'
9-
10-
import { notify, NOTIFY_TYPE } from 'components/Notification/actions'
116
import { openFile } from 'commands/commandBindings/file'
12-
import { createTab, activateTab } from 'components/Tab/actions'
13-
import FileTreeState from 'components/FileTree/state'
14-
import dispatchCommand from 'commands/dispatchCommand'
15-
import state from './state'
7+
import * as delegate from 'commons/Search/action'
8+
import icons from 'file-icons-js'
9+
10+
export class SearchResultItem extends Component {
11+
constructor(props) {
12+
super(props);
13+
this.state = {
14+
isFolded: false
15+
};
16+
}
1617

17-
class SearchResultItem extends Component {
18-
constructor (props) {
19-
super(props)
20-
this.state = {
21-
isFolded: false
18+
handlePathClick = () => {
19+
this.setState({
20+
isFolded: !this.state.isFolded
21+
})
2222
}
23-
}
24-
handlePathClick = () => {
25-
this.setState({
26-
isFolded: !this.state.isFolded
27-
})
28-
}
29-
handleItemClick = (path, line) => {
30-
const selection = new monaco.Selection(
31-
line.line,
32-
line.indexes[0] + 1,
33-
line.line,
34-
line.indexes[0] + this.props.keyword.length + 1,
35-
)
3623

37-
openFile({ path, editor: { filePath: path, selection } })
38-
// const fileTreeNode = FileTreeState.entities.get(path)
39-
// if (fileTreeNode) {
40-
// dispatchCommand('file:open_file', {
41-
// path: fileTreeNode.path,
42-
// editor: { filePath: path, selection },
43-
// })
44-
// } else {
45-
// openFile({ path, editor: { filePath: path, selection } })
46-
// }
47-
}
48-
render () {
49-
const { path, value } = this.props
50-
const iconStr = 'file-icon ' + (icons.getClassWithColor(path) || 'fa fa-file-text-o')
51-
const idx = path.lastIndexOf('/') + 1
52-
const fileName = path.substring(idx)
53-
const filePath = path.substring(1, idx)
54-
const count = value.length
55-
return (
56-
<div className='search-item' key={path}>
57-
<div className='search-item-path' onClick={this.handlePathClick}>
58-
<i
59-
className={cx({
60-
'fa fa-caret-right': this.state.isFolded,
61-
'fa fa-caret-down': !this.state.isFolded
62-
})}
63-
/>
64-
<i className={iconStr} />
65-
{fileName}
66-
<span className='search-item-path-path'>{filePath}</span>
67-
<span className='search-item-count'>{count}</span>
68-
</div>
24+
handleItemClick = (path, start, end, lineNum) => {
25+
const selection = new monaco.Selection(
26+
lineNum,
27+
start + 1,
28+
lineNum,
29+
end + 1,
30+
)
31+
32+
openFile({ path, editor: { filePath: path, selection } })
33+
}
6934

70-
{!this.state.isFolded && value.map(line => {
71-
const contentStart = line.content.substring(0, line.indexes[0])
72-
const contentMiddle = line.content.substring(line.indexes[0], line.indexes[0] + this.props.keyword.length)
73-
const contentEnd = line.content.substring(line.indexes[0] + this.props.keyword.length)
74-
return (
75-
<div key={`${path}-${line.line}`} className='search-item-line' onClick={() => this.handleItemClick(path, line)}>
76-
<span className='search-item-content'>{contentStart}<h>{contentMiddle}</h>{contentEnd}</span>
35+
render() {
36+
const {fileName, pattern, path, results} = this.props;
37+
const resultSize = results.length;
38+
const iconStr = 'file-icon ' + (icons.getClassWithColor(path) || 'fa fa-file-text-o');
39+
return (
40+
<div className='search-item' key={path}>
41+
<div className='search-item-path' onClick={this.handlePathClick}>
42+
<i
43+
className={cx({
44+
'fa fa-caret-right': this.state.isFolded,
45+
'fa fa-caret-down': !this.state.isFolded
46+
})}
47+
/>
48+
<i className={iconStr} />
49+
{fileName}
50+
<span className='search-item-path-path'>{path}</span>
51+
<span className='search-item-count'>{resultSize}</span>
52+
</div>
53+
54+
{
55+
!this.state.isFolded && results.map(result => {
56+
let {start, end, length, innerStart, innerEnd, line, lineNum} = result;
57+
return (
58+
<div key={`${fileName}-${resultSize}-${lineNum}-${start}`} className='search-item-line' onClick={() => this.handleItemClick(path, innerStart, innerEnd, lineNum)}>
59+
{line && <span className='search-item-content'>{line.substring(0, innerStart)}<b>{line.substring(innerStart, innerEnd)}</b>{line.substring(innerEnd)}</span>}
60+
</div>
61+
)})
62+
}
7763
</div>
78-
)
79-
})}
80-
</div>
81-
)
82-
}
64+
)
65+
}
66+
8367
}
8468

8569
@observer
8670
class SearchPanel extends Component {
87-
componentDidMount () {
88-
this.subscribeToFilesearch()
89-
}
90-
onKeyDown = (e) => {
91-
if (e.keyCode === 13) {
92-
this.searchTxt()
71+
componentDidMount () {
72+
subscribeToSearch()
9373
}
94-
}
9574

96-
handleKeywordChange = (e) => {
97-
state.keyword = e.target.value
98-
e.stopPropagation()
99-
e.nativeEvent.stopImmediatePropagation()
100-
// this.confirm()
101-
}
75+
onKeyDown = (e) => {
76+
if (e.keyCode === 13) {
77+
this.searchTxt()
78+
}
79+
}
80+
81+
handleKeywordChange = (e) => {
82+
state.searching.pattern = e.target.value
83+
e.stopPropagation()
84+
e.nativeEvent.stopImmediatePropagation()
85+
}
86+
87+
searchTxt = () => {
88+
if(state.searching.pattern.length == 0 || state.searching.pattern.trim() == '') {
89+
return ;
90+
}
91+
if(state.ws.first) {
92+
state.ws.first = false
93+
}
94+
if(state.searching.isPattern) {
95+
delegate.searchPattern(state.searching);
96+
} else {
97+
delegate.searchString(state.searching);
98+
}
99+
}
100+
101+
renderResult () {
102+
let content = '';
103+
if (state.ws.first) {
104+
content = ''
105+
} else if (!state.searched.end) {
106+
content = i18n`panel.left.searching`
107+
} else if(state.searched.message !== '') {
108+
content = state.searched.message;
109+
} else if (state.searched.results.length != 0) {
110+
const pattern = state.searching.pattern;
111+
const files = state.searched.results.length
112+
let number = 0
113+
let count = 0
114+
115+
content = state.searched.results.map((searchChunk) => {
116+
count++
117+
number += searchChunk.results.length
118+
return (<SearchResultItem
119+
key={`${pattern}-${count}`}
120+
fileName={searchChunk.fileName}
121+
results={searchChunk.results}
122+
pattern={pattern}
123+
path={searchChunk.path}
124+
result={searchChunk.results}/>);
125+
})
126+
return (
127+
<div className='search-result-list'>
128+
<div>{i18n`panel.result.tip${{ files, number }}`}</div>
129+
{content}
130+
</div>
131+
)
132+
} else if(state.searched.taskId) {
133+
content = `${i18n.get('panel.result.blank')}`;
134+
}
135+
136+
return (
137+
<div key={`search-result-list`} className='search-result-list'>
138+
{content}
139+
</div>
140+
)
141+
}
102142

103-
confirm = debounce(() => {
104-
this.searchTxt()
105-
}, 1000)
143+
render () {
144+
const { caseSensitive, word, isPattern } = state.searching
106145

107-
searchTxt = () => {
108-
state.searching = true
109-
api.searchTxt(state.keyword).then((data) => {
110-
state.taskId = data.taskId
111-
}).catch((res) => {
112-
notify({ message: `Search failed: ${res.msg}`, notifyType: NOTIFY_TYPE.ERROR })
113-
})
114-
}
115-
subscribeToFilesearch = () => {
116-
autorun(() => {
117-
if (!config.fsSocketConnected) return
118-
const client = FsSocketClient.$$singleton.stompClient
119-
client.subscribe(`/topic/ws/${config.spaceKey}/txt/search`, (frame) => {
120-
const data = JSON.parse(frame.body)
121-
this.setDate(data)
122-
})
123-
})
124-
}
146+
return (
147+
<div className='search-panel'>
148+
<div className='search-panel-title'>
149+
{i18n`panel.left.find`}
150+
</div>
151+
<div className='search-panel-input'>
152+
<div className='search-controls'>
153+
<input type='text'
154+
className='form-control'
155+
value={state.keyword}
156+
onChange={this.handleKeywordChange}
157+
onKeyDown={this.onKeyDown}
158+
placeholder={i18n.get(`panel.left.placeholder`)}
159+
/>
160+
</div>
161+
<div className='search-checkbox'>
162+
<span title={i18n.get('panel.checkbox.case')}
163+
onClick={() => this.caseSensitive(caseSensitive)}
164+
className={caseSensitive ? 'active' : ''}>{'Aa'}</span>
165+
<span title={i18n.get('panel.checkbox.word')}
166+
onClick={() => this.word(word)}
167+
className={word ? 'active' : ''}>{'Al'}</span>
168+
<span title={i18n.get('panel.checkbox.pattern')}
169+
onClick={() => this.pattern(isPattern)}
170+
className={isPattern ? 'active' : ''}>{'.*'}</span>
171+
</div>
172+
</div>
173+
{this.renderResult()}
174+
</div>
175+
)
176+
}
125177

126-
setDate = debounce((data) => {
127-
if (data.taskId === state.taskId) {
128-
state.result = data
129-
state.searching = false
178+
caseSensitive = caseSensitive => {
179+
state.searching.caseSensitive = !caseSensitive
130180
}
131-
}, 500)
132181

133-
renderResult () {
134-
let content = ''
135-
if (state.searching) {
136-
content = 'Searching...'
137-
} else if (state.result.results) {
138-
const keyword = state.result.keyword
139-
content = Object.entries(state.result.results).map(([key, value]) => {
140-
if (key.startsWith('/.git/')) return null
141-
return (<SearchResultItem keyword={keyword} path={key} key={key} value={value} />)
142-
})
182+
word = word => {
183+
state.searching.word = !word
184+
if (!word) {
185+
state.searching.isPattern = false
186+
}
143187
}
144188

145-
return (
146-
<div className='search-result-list'>
147-
{content}
148-
</div>
149-
)
150-
}
189+
pattern = isPattern => {
190+
state.searching.isPattern = !isPattern
191+
if (!isPattern) {
192+
state.searching.word = false
193+
}
194+
}
151195

152-
render () {
153-
return (
154-
<div className='search-panel'>
155-
<div className='search-panel-title'>
156-
{i18n`panel.left.find`}
157-
</div>
158-
<div className='search-panel-input'>
159-
<input type='text'
160-
className='form-control'
161-
value={state.keyword}
162-
onChange={this.handleKeywordChange}
163-
onKeyDown={this.onKeyDown}
164-
placeholder={i18n.get('panel.left.find')}
165-
/>
166-
</div>
167-
{this.renderResult()}
168-
</div>
169-
)
170-
}
171196
}
172197

173198
export default SearchPanel

0 commit comments

Comments
 (0)