Skip to content

Commit

Permalink
ui: collapse and expand files
Browse files Browse the repository at this point in the history
Fixes #182
  • Loading branch information
vracini authored and salemhilal committed Apr 17, 2022
1 parent 7ee516d commit c1ffa46
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 61 deletions.
29 changes: 29 additions & 0 deletions ui/assets/css/hound.css
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,16 @@ button:focus {
margin-right: 10px;
}

#result > .actions {
padding: 5px 0 30px 0;
}

#result > .actions > button {
margin: 2px;
float: right;
background-color: #f5f5f5;
color: #666;
}
.repo {
margin-bottom: 100px;
}
Expand All @@ -227,6 +237,7 @@ button:focus {
color: #767676;
font-size: 24px;
padding-bottom: 5px;
cursor: pointer;
}

.repo > .title > .name {
Expand All @@ -238,6 +249,11 @@ button:focus {
margin-right: 10px;
}

.repo > .title > .indicator {
vertical-align: text-top;
padding-left: 10px;
}

.files > .moar {
height: 55px;
vertical-align: top;
Expand All @@ -250,11 +266,16 @@ button:focus {
border: 1px solid #d8d8d8;
}

.file.closed {
margin: 10px 0 0 0;
}

.file > .title {
padding: 10px 10px 10px 20px;
display: block;
line-height: 30px;
background-color: #f5f5f5;
cursor: pointer;
}

.title a {
Expand All @@ -266,6 +287,14 @@ button:focus {
overflow: auto;
}

.file.closed > .file-body {
display: none;
}

.file.open > .file-boby {
display: block;
}

.match {
border-bottom: 2px solid #f0f0f0;
}
Expand Down
2 changes: 1 addition & 1 deletion ui/assets/js/common.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EscapeRegExp, ExpandVars, UrlToRepo } from "./common";
import { EscapeRegExp, ExpandVars, UrlToRepo, UrlParts } from "./common";

describe("EscapeRegExp", () => {
const testRegs = [
Expand Down
163 changes: 128 additions & 35 deletions ui/assets/js/hound.js
Original file line number Diff line number Diff line change
Expand Up @@ -691,55 +691,82 @@ var ContentFor = function(line, regexp) {
return buffer.join('');
};

var FilesView = React.createClass({
onLoadMore: function(event) {
Model.LoadMore(this.props.repo);
var FileContentView = React.createClass({
getInitialState: function() {
return { open: true };
},

render: function() {
var rev = this.props.rev,
repo = this.props.repo,
regexp = this.props.regexp,
matches = this.props.matches,
totalMatches = this.props.totalMatches;
var files = matches.map(function(match, index) {
var filename = match.Filename,
blocks = CoalesceMatches(match.Matches);
toggleContent: function() {
this.state.open ? this.closeContent(): this.openContent();
},
openContent: function() {
this.setState({open: true});
},
closeContent: function() {
this.setState({open: false});
},
render: function () {
var repo = this.props.repo,
rev = this.props.rev,
regexp = this.props.regexp,
fileName = this.props.fileName,
blocks = this.props.blocks;
var matches = blocks.map(function(block) {
var lines = block.map(function(line) {
var content = ContentFor(line, regexp);
return (
<div className="line">
<a href={Model.UrlToRepo(repo, filename, line.Number, rev)}
<a href={Model.UrlToRepo(repo, fileName, line.Number, rev)}
className="lnum"
target="_blank"
rel="noopener noreferrer">{line.Number}</a>
<span className="lval" dangerouslySetInnerHTML={{__html:content}} />
</div>
);
});

return (
<div className="match">{lines}</div>
);
});

return (
<div className="file">
<div className="title">
<a href={Model.UrlToRepo(repo, match.Filename, null, rev)}
target="_blank"
rel="noopener noreferrer">
{match.Filename}
<div className={"file " + (this.state.open ? 'open' : 'closed')}>
<div className="title" onClick={this.toggleContent}>
<a href={Model.UrlToRepo(repo, fileName, null, rev)}
target="_blank"
rel="noopener noreferrer">
{fileName}
</a>
</div>
<div className="file-body">
{matches}
</div>
</div>
);
}
});

var FilesView = React.createClass({
onLoadMore: function(event) {
Model.LoadMore(this.props.repo);
},

render: function() {
var rev = this.props.rev,
repo = this.props.repo,
regexp = this.props.regexp,
matches = this.props.matches,
totalMatches = this.props.totalMatches;

var files = matches.map(function (match, index) {
return <FileContentView ref={"file-"+index}
repo={repo}
rev={rev}
fileName={match.Filename}
blocks={CoalesceMatches(match.Matches)}
regexp={regexp}/>
});


var more = '';
if (matches.length < totalMatches) {
more = (<button className="moar" onClick={this.onLoadMore}>Load all {totalMatches} matches in {Model.NameForRepo(repo)}</button>);
Expand All @@ -754,6 +781,50 @@ var FilesView = React.createClass({
}
});

var RepoView = React.createClass({
getInitialState: function() {
return { open: true };
},
toggleRepo: function() {
this.state.open ? this.closeRepo(): this.openRepo();
},
openOrCloseRepo: function (to_open) {
for (var ref in this.refs.filesView.refs) {
if (ref.startsWith("file-")) {
if (to_open) {
this.refs.filesView.refs[ref].openContent();
} else {
this.refs.filesView.refs[ref].closeContent();
}
}
}
this.setState({open: to_open});
},
openRepo: function() {
this.openOrCloseRepo(true);
},
closeRepo: function() {
this.openOrCloseRepo(false);
},
render: function() {
return (
<div className={"repo " + (this.state.open? "open":"closed")}>
<div className="title" onClick={this.toggleRepo}>
<span className="mega-octicon octicon-repo"></span>
<span className="name">{Model.NameForRepo(this.props.repo)}</span>
<span className={"indicator octicon octicon-chevron-"+ (this.state.open? "up":"down")} onClick={this.toggleRepo}></span>
</div>
<FilesView ref="filesView"
matches={this.props.matches}
rev={this.props.rev}
repo={this.props.repo}
regexp={this.props.regexp}
totalMatches={this.props.files} />
</div>
);
}
});

var ResultView = React.createClass({
componentWillMount: function() {
var _this = this;
Expand All @@ -764,6 +835,23 @@ var ResultView = React.createClass({
});
});
},
openOrCloseAll: function (to_open) {
for (var ref in this.refs) {
if (ref.startsWith("repo-")) {
if (to_open) {
this.refs[ref].openRepo();
} else {
this.refs[ref].closeRepo();
}
}
}
},
openAll: function () {
this.openOrCloseAll(true);
},
closeAll: function () {
this.openOrCloseAll(false);
},
getInitialState: function() {
return { results: null };
},
Expand Down Expand Up @@ -793,23 +881,28 @@ var ResultView = React.createClass({
results = this.state.results || [];
var repos = results.map(function(result, index) {
return (
<div className="repo">
<div className="title">
<span className="mega-octicon octicon-repo"></span>
<span className="name">
<a href={Model.UrlToRoot(result.Repo)}>{Model.NameForRepo(result.Repo)}</a>
</span>
</div>
<FilesView matches={result.Matches}
rev={result.Rev}
repo={result.Repo}
regexp={regexp}
totalMatches={result.FilesWithMatch} />
</div>
<RepoView ref={"repo-"+index}
matches={result.Matches}
rev={result.Rev}
repo={result.Repo}
regexp={regexp}
files={result.FilesWithMatch}/>
);
});
var actions = '';
if (results.length > 0) {
actions = (
<div className="actions">
<button onClick={this.openAll}><span className="octicon octicon-chevron-down"></span> Expand all</button>
<button onClick={this.closeAll}><span className="octicon octicon-chevron-up"></span> Collapse all</button>
</div>
)
}
return (
<div id="result">{repos}</div>
<div id="result">
{actions}
{repos}
</div>
);
}
});
Expand Down
Loading

0 comments on commit c1ffa46

Please sign in to comment.