Skip to content

Commit b67ed78

Browse files
committed
Add support for filtering branch menu
1 parent 3ba0bf5 commit b67ed78

File tree

2 files changed

+136
-2
lines changed

2 files changed

+136
-2
lines changed

src/components/NewBranchDialog.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ import {
1616
closeButtonClass,
1717
contentWrapperClass,
1818
createButtonClass,
19+
filterClass,
20+
filterClearClass,
21+
filterInputClass,
22+
filterWrapperClass,
1923
listItemBoldTitleClass,
2024
listItemClass,
2125
listItemContentClass,
@@ -69,6 +73,11 @@ export interface INewBranchDialogState {
6973
*/
7074
base: string;
7175

76+
/**
77+
* Menu filter.
78+
*/
79+
filter: string;
80+
7281
/**
7382
* Current branch name.
7483
*/
@@ -102,6 +111,7 @@ export class NewBranchDialog extends React.Component<
102111
this.state = {
103112
name: '',
104113
base: repo ? this.props.model.currentBranch.name : '',
114+
filter: '',
105115
current: repo ? this.props.model.currentBranch.name : '',
106116
branches: repo ? this.props.model.branches : []
107117
};
@@ -150,6 +160,27 @@ export class NewBranchDialog extends React.Component<
150160
title="Enter a branch name"
151161
/>
152162
<p>Create branch based on...</p>
163+
<div className={filterWrapperClass}>
164+
<div className={filterClass}>
165+
<input
166+
className={filterInputClass}
167+
type="text"
168+
onChange={this._onFilterChange}
169+
value={this.state.filter}
170+
placeholder="Filter"
171+
title="Filter branch menu"
172+
/>
173+
{this.state.filter ? (
174+
<button className={filterClearClass}>
175+
<ClearIcon
176+
titleAccess="Clear the current filter"
177+
fontSize="small"
178+
onClick={this._resetFilter}
179+
/>
180+
</button>
181+
) : null}
182+
</div>
183+
</div>
153184
<div className={listWrapperClass}>
154185
<List disablePadding>{this._renderItems()}</List>
155186
</div>
@@ -210,6 +241,10 @@ export class NewBranchDialog extends React.Component<
210241
* @returns fragment
211242
*/
212243
private _renderItem(branch: Git.IBranch, idx: number) {
244+
// Perform a "simple" filter... (TODO: consider implementing fuzzy filtering)
245+
if (this.state.filter && !branch.name.includes(this.state.filter)) {
246+
return null;
247+
}
213248
const isBase = branch.name === this.state.base;
214249
const isCurr = branch.name === this.state.current;
215250

@@ -277,6 +312,26 @@ export class NewBranchDialog extends React.Component<
277312
});
278313
}
279314

315+
/**
316+
* Callback invoked upon a change to the menu filter.
317+
*
318+
* @param event - event object
319+
*/
320+
private _onFilterChange = (event: any): void => {
321+
this.setState({
322+
filter: event.target.value
323+
});
324+
};
325+
326+
/**
327+
* Callback invoked to reset the menu filter.
328+
*/
329+
private _resetFilter = (): void => {
330+
this.setState({
331+
filter: ''
332+
});
333+
};
334+
280335
/**
281336
* Returns a callback which is invoked upon clicking a branch name.
282337
*

src/style/NewBranchDialog.ts

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { style } from 'typestyle';
22

33
export const branchDialogClass = style({
44
width: '400px',
5+
height: '460px',
56

67
borderRadius: '3px!important'
78
});
@@ -47,7 +48,7 @@ export const contentWrapperClass = style({
4748

4849
$nest: {
4950
'> p': {
50-
marginBottom: '10px'
51+
marginBottom: '7px'
5152
}
5253
}
5354
});
@@ -79,10 +80,88 @@ export const nameInputClass = style({
7980
}
8081
});
8182

83+
export const filterWrapperClass = style({
84+
padding: 0,
85+
paddingBottom: '4px'
86+
});
87+
88+
export const filterClass = style({
89+
boxSizing: 'border-box',
90+
display: 'inline-block',
91+
position: 'relative',
92+
93+
width: '100%',
94+
95+
marginRight: '11px',
96+
97+
fontSize: 'var(--jp-ui-font-size1)'
98+
});
99+
100+
export const filterInputClass = style({
101+
boxSizing: 'border-box',
102+
103+
width: '100%',
104+
height: '2em',
105+
106+
/* top | right | bottom | left */
107+
padding: '1px 18px 2px 7px',
108+
109+
color: 'var(--jp-ui-font-color0)',
110+
fontSize: 'var(--jp-ui-font-size1)',
111+
fontWeight: 300,
112+
113+
backgroundColor: 'var(--jp-layout-color1)',
114+
115+
border: 'var(--jp-border-width) solid var(--jp-border-color2)',
116+
borderRadius: '3px',
117+
118+
$nest: {
119+
'&:active': {
120+
border: 'var(--jp-border-width) solid var(--jp-brand-color1)'
121+
},
122+
'&:focus': {
123+
border: 'var(--jp-border-width) solid var(--jp-brand-color1)'
124+
}
125+
}
126+
});
127+
128+
export const filterClearClass = style({
129+
position: 'absolute',
130+
right: '5px',
131+
top: '0.6em',
132+
133+
height: '1.1em',
134+
width: '1.1em',
135+
136+
padding: 0,
137+
138+
backgroundColor: 'var(--jp-inverse-layout-color4)',
139+
140+
border: 'none',
141+
borderRadius: '50%',
142+
143+
$nest: {
144+
svg: {
145+
width: '0.5em!important',
146+
height: '0.5em!important',
147+
148+
fill: 'var(--jp-ui-inverse-font-color0)'
149+
},
150+
'&:hover': {
151+
backgroundColor: 'var(--jp-inverse-layout-color3)'
152+
},
153+
'&:active': {
154+
backgroundColor: 'var(--jp-inverse-layout-color2)'
155+
}
156+
}
157+
});
158+
82159
export const listWrapperClass = style({
160+
boxSizing: 'border-box',
83161
display: 'block',
162+
84163
width: '100%',
85-
maxHeight: '400px',
164+
height: '200px',
86165

87166
border: 'var(--jp-border-width) solid var(--jp-border-color2)',
88167
borderRadius: '3px',

0 commit comments

Comments
 (0)