kopia lustrzana https://github.com/OpenDroneMap/WebODM
Fixes, some refactoring
rodzic
c852f72b20
commit
c90b575850
|
@ -49,11 +49,11 @@ class ProjectFilter(filters.FilterSet):
|
|||
tag_pattern = re.compile("#[^\s]+")
|
||||
tags = set(re.findall(tag_pattern, value))
|
||||
|
||||
project_tags = set([t for t in tags if t.startswith("##")])
|
||||
deep_tags = tags - project_tags
|
||||
deep_tags = set([t for t in tags if t.startswith("##")])
|
||||
project_tags = tags - deep_tags
|
||||
|
||||
project_tags = [t.replace("##", "") for t in project_tags]
|
||||
deep_tags = [t.replace("#", "") for t in deep_tags]
|
||||
deep_tags = [t.replace("##", "") for t in deep_tags]
|
||||
project_tags = [t.replace("#", "") for t in project_tags]
|
||||
|
||||
names = re.sub("\s+", " ", re.sub(tag_pattern, "", value)).strip()
|
||||
|
||||
|
|
|
@ -32,24 +32,12 @@ class Paginator extends React.Component {
|
|||
}];
|
||||
}
|
||||
|
||||
stop = e => {
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
this.searchPopup.addEventListener("click", this.stop);
|
||||
this.searchButton.addEventListener("click", this.toggleSearch);
|
||||
this.btnSearch.addEventListener("click", this.search);
|
||||
document.body.addEventListener("click", this.closeSearch);
|
||||
document.addEventListener("onProjectListTagClicked", this.addTagAndSearch);
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
document.removeEventListener("onProjectListTagClicked", this.addTagAndSearch);
|
||||
document.body.removeEventListener("click", this.closeSearch);
|
||||
this.btnSearch.removeEventListener("click", this.search);
|
||||
this.searchButton.removeEventListener("click", this.toggleSearch);
|
||||
this.searchPopup.removeEventListener("click", this.stop);
|
||||
}
|
||||
|
||||
closeSearch = () => {
|
||||
|
@ -58,11 +46,9 @@ class Paginator extends React.Component {
|
|||
|
||||
toggleSearch = e => {
|
||||
e.stopPropagation();
|
||||
this.searchContainer.classList.toggle("open");
|
||||
|
||||
setTimeout(() => {
|
||||
this.searchInput.focus();
|
||||
}, 0);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
handleSearchChange = e => {
|
||||
|
@ -107,8 +93,8 @@ class Paginator extends React.Component {
|
|||
if (tag === undefined) return;
|
||||
|
||||
let { searchText } = this.state;
|
||||
if (searchText === "") searchText += "##" + tag;
|
||||
else searchText += " ##" + tag;
|
||||
if (searchText === "") searchText += "#" + tag;
|
||||
else searchText += " #" + tag;
|
||||
|
||||
this.setState({searchText});
|
||||
setTimeout(() => {
|
||||
|
@ -122,31 +108,32 @@ class Paginator extends React.Component {
|
|||
|
||||
let paginator = null;
|
||||
let clearSearch = null;
|
||||
|
||||
let toolbar = (<ul className="pagination pagination-sm toolbar">
|
||||
<li className="btn-group" ref={domNode => { this.searchContainer = domNode; }}>
|
||||
<a href="javascript:void(0);" className="dropdown-toggle"
|
||||
aria-haspopup="true" aria-expanded="false"
|
||||
ref={domNode => { this.searchButton = domNode; }} title={_("Search")}><i className="fa fa-search"></i></a>
|
||||
<ul className="dropdown-menu dropdown-menu-right search-popup" ref={domNode => { this.searchPopup = domNode; }}>
|
||||
<li>
|
||||
<input type="text"
|
||||
ref={(domNode) => { this.searchInput = domNode}}
|
||||
className="form-control search theme-border-secondary-07"
|
||||
placeholder={_("Search names or #tags")}
|
||||
value={searchText}
|
||||
onKeyDown={this.handleSearchKeyDown}
|
||||
onChange={this.handleSearchChange} />
|
||||
<button ref={domNode => {this.btnSearch = domNode;}} className="btn btn-sm btn-default"><i className="fa fa-search"></i></button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className="btn-group">
|
||||
<a href="javascript:void(0);" className="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i className="fa fa-sort-alpha-down" title={_("Sort")}></i></a>
|
||||
<SortPanel selected={this.state.sortKey} items={this.sortItems} onChange={this.sortChanged} />
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
let toolbar = (<ul className={"pagination pagination-sm toolbar " + (totalItems == 0 && !searchText ? "hidden " : " ") + (totalItems / itemsPerPage <= 1 ? "no-margin" : "")}>
|
||||
<li className="btn-group" ref={domNode => { this.searchContainer = domNode; }}>
|
||||
<a href="javascript:void(0);" className="dropdown-toggle"
|
||||
data-toggle-outside
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="false"
|
||||
onClick={this.toggleSearch}
|
||||
title={_("Search")}><i className="fa fa-search"></i></a>
|
||||
<ul className="dropdown-menu dropdown-menu-right search-popup">
|
||||
<li>
|
||||
<input type="text"
|
||||
ref={(domNode) => { this.searchInput = domNode}}
|
||||
className="form-control search theme-border-secondary-07"
|
||||
placeholder={_("Search names or #tags")}
|
||||
value={searchText}
|
||||
onKeyDown={this.handleSearchKeyDown}
|
||||
onChange={this.handleSearchChange} />
|
||||
<button onClick={this.search} className="btn btn-sm btn-default"><i className="fa fa-search"></i></button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className="btn-group">
|
||||
<a href="javascript:void(0);" className="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i className="fa fa-sort-alpha-down" title={_("Sort")}></i></a>
|
||||
<SortPanel selected={this.state.sortKey} items={this.sortItems} onChange={this.sortChanged} />
|
||||
</li>
|
||||
</ul>);
|
||||
|
||||
if (this.props.currentSearch){
|
||||
let currentSearch = decodeSearch(this.props.currentSearch);
|
||||
|
|
|
@ -8,6 +8,7 @@ import Paginator from './Paginator';
|
|||
import ErrorMessage from './ErrorMessage';
|
||||
import { _, interpolate } from '../classes/gettext';
|
||||
import PropTypes from 'prop-types';
|
||||
import Utils from '../classes/Utils';
|
||||
|
||||
class ProjectList extends Paginated {
|
||||
static propTypes = {
|
||||
|
@ -33,8 +34,23 @@ class ProjectList extends Paginated {
|
|||
this.refresh();
|
||||
}
|
||||
|
||||
getParametersHash(source){
|
||||
if (!source) return "";
|
||||
if (source.indexOf("?") === -1) return "";
|
||||
|
||||
let search = source.substr(source.indexOf("?"));
|
||||
let q = Utils.queryParams({search});
|
||||
|
||||
// All parameters that can change via history.push without
|
||||
// triggering a reload of the project list should go here
|
||||
delete q.project_task_open;
|
||||
delete q.project_task_expanded;
|
||||
|
||||
return JSON.stringify(q);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps){
|
||||
if (prevProps.source !== this.props.source){
|
||||
if (this.getParametersHash(prevProps.source) !== this.getParametersHash(this.props.source)){
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ class ProjectListItem extends React.Component {
|
|||
refreshing: false,
|
||||
importing: false,
|
||||
buttons: [],
|
||||
sortKey: "-created_at"
|
||||
sortKey: "-created_at",
|
||||
filterTags: []
|
||||
};
|
||||
|
||||
this.sortItems = [{
|
||||
|
@ -498,8 +499,12 @@ class ProjectListItem extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
tagsChanged = (filterTags) => {
|
||||
this.setState({filterTags});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { refreshing, data } = this.state;
|
||||
const { refreshing, data, filterTags } = this.state;
|
||||
const numTasks = data.tasks.length;
|
||||
const canEdit = this.hasPermission("change");
|
||||
const userTags = Tags.userTags(data.tags);
|
||||
|
@ -580,10 +585,17 @@ class ProjectListItem extends React.Component {
|
|||
|
||||
{this.state.showTaskList && numTasks > 1 ?
|
||||
<div className="task-filters">
|
||||
<i className='fa fa-filter'></i>
|
||||
<a href="javascript:void(0);" onClick={this.toggleTaskList}>
|
||||
{_("Filter")}
|
||||
</a>
|
||||
{filterTags.length > 0 ?
|
||||
<div className="btn-group">
|
||||
<i className='fa fa-filter'></i>
|
||||
<a href="javascript:void(0);" className="dropdown-toggle" data-toggle-outside data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{_("Filter")}
|
||||
</a>
|
||||
<ul className="dropdown-menu dropdown-menu-right">
|
||||
{filterTags.map(t => <li key={t}>{t}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
: ""}
|
||||
<div className="btn-group">
|
||||
<i className='fa fa-sort-alpha-down'></i>
|
||||
<a href="javascript:void(0);" className="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
|
@ -645,6 +657,7 @@ class ProjectListItem extends React.Component {
|
|||
onDelete={this.taskDeleted}
|
||||
onTaskMoved={this.taskMoved}
|
||||
hasPermission={this.hasPermission}
|
||||
onTagsChanged={this.tagsChanged}
|
||||
history={this.props.history}
|
||||
/> : ""}
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ class TaskList extends React.Component {
|
|||
source: PropTypes.string.isRequired, // URL where to load task list
|
||||
onDelete: PropTypes.func,
|
||||
onTaskMoved: PropTypes.func,
|
||||
hasPermission: PropTypes.func.isRequired
|
||||
hasPermission: PropTypes.func.isRequired,
|
||||
onTagsChanged: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props){
|
||||
|
@ -49,6 +50,7 @@ class TaskList extends React.Component {
|
|||
this.setState({
|
||||
tasks: json
|
||||
});
|
||||
setTimeout(() => this.notifyTagsChanged(), 0);
|
||||
})
|
||||
.fail((jqXHR, textStatus, errorThrown) => {
|
||||
this.setState({
|
||||
|
@ -78,6 +80,38 @@ class TaskList extends React.Component {
|
|||
if (this.props.onTaskMoved) this.props.onTaskMoved(task);
|
||||
}
|
||||
|
||||
notifyTagsChanged = () => {
|
||||
const { tasks } = this.state;
|
||||
const tags = [];
|
||||
if (tasks){
|
||||
tasks.forEach(t => {
|
||||
if (t.tags){
|
||||
t.tags.forEach(x => {
|
||||
if (tags.indexOf(x) === -1) tags.push(x);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
tags.sort();
|
||||
|
||||
if (this.props.onTagsChanged) this.props.onTagsChanged(tags);
|
||||
}
|
||||
|
||||
taskEdited = (task) => {
|
||||
// Update
|
||||
const { tasks } = this.state;
|
||||
for (let i = 0; i < tasks.length; i++){
|
||||
if (tasks[i].id === task.id){
|
||||
tasks[i] = task;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.setState({tasks});
|
||||
|
||||
// Tags might have changed
|
||||
setTimeout(() => this.notifyTagsChanged(), 0);
|
||||
}
|
||||
|
||||
render() {
|
||||
let message = "";
|
||||
if (this.state.loading){
|
||||
|
@ -98,6 +132,7 @@ class TaskList extends React.Component {
|
|||
onDelete={this.deleteTask}
|
||||
onMove={this.moveTask}
|
||||
onDuplicate={this.refresh}
|
||||
onEdited={this.taskEdited}
|
||||
hasPermission={this.props.hasPermission}
|
||||
history={this.props.history} />
|
||||
))}
|
||||
|
|
|
@ -24,7 +24,8 @@ class TaskListItem extends React.Component {
|
|||
onDelete: PropTypes.func,
|
||||
onMove: PropTypes.func,
|
||||
onDuplicate: PropTypes.func,
|
||||
hasPermission: PropTypes.func
|
||||
hasPermission: PropTypes.func,
|
||||
onEdited: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props){
|
||||
|
@ -279,6 +280,7 @@ class TaskListItem extends React.Component {
|
|||
|
||||
handleEditTaskSave(task){
|
||||
this.setState({task, editing: false});
|
||||
if (this.props.onEdited) this.props.onEdited(task);
|
||||
this.setAutoRefresh();
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
opacity: 0.8;
|
||||
}
|
||||
margin-right: 8px;
|
||||
&.no-margin{
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.btn-group.open > .dropdown-menu{
|
||||
top: 22px;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*!
|
||||
* Bootstrap v3.3.1 (http://getbootstrap.com)
|
||||
* Bootstrap v3.3.1 (http://getbootstrap.com) modified to allow "data-toggle-outside"
|
||||
* Copyright 2011-2014 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
@ -838,6 +838,15 @@ if (typeof jQuery === 'undefined') {
|
|||
|
||||
if (!$parent.hasClass('open')) return
|
||||
|
||||
// Modification to allow toggling only with click outside
|
||||
if ($this.attr('data-toggle-outside')){
|
||||
if (e && e.target){
|
||||
var sibiling = $this.get(0).nextSibling;
|
||||
if (sibiling === e.target || sibiling.contains(e.target)) return
|
||||
}
|
||||
}
|
||||
// End modification
|
||||
|
||||
$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
|
||||
|
||||
if (e.isDefaultPrevented()) return
|
||||
|
|
File diff suppressed because one or more lines are too long
Ładowanie…
Reference in New Issue