mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 23:40:13 +01:00
Global search development
* search input * parse query with operators
This commit is contained in:
parent
4e8ee8eb86
commit
5913a4af1b
4 changed files with 272 additions and 0 deletions
37
client/components/main/globalSearch.jade
Normal file
37
client/components/main/globalSearch.jade
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
template(name="globalSearchHeaderBar")
|
||||||
|
h1
|
||||||
|
i.fa.fa-search
|
||||||
|
| {{_ 'globalSearch-title'}}
|
||||||
|
|
||||||
|
template(name="globalSearchModalTitle")
|
||||||
|
h2
|
||||||
|
i.fa.fa-keyboard-o
|
||||||
|
| {{_ 'globalSearch-title'}}
|
||||||
|
|
||||||
|
template(name="globalSearch")
|
||||||
|
if isPageReady.get
|
||||||
|
.wrapper
|
||||||
|
form.js-search-query-form
|
||||||
|
input(type="text" name="searchQuery" placeholder="{{_ 'search-example'}}" autofocus dir="auto")
|
||||||
|
else
|
||||||
|
+spinner
|
||||||
|
|
||||||
|
template(name="globalSearchViewChangePopup")
|
||||||
|
ul.pop-over-list
|
||||||
|
li
|
||||||
|
with "globalSearchViewChange-choice-me"
|
||||||
|
a.js-due-cards-view-me
|
||||||
|
i.fa.fa-user.colorful
|
||||||
|
| {{_ 'globalSearchViewChange-choice-me'}}
|
||||||
|
if $eq Utils.globalSearchView "me"
|
||||||
|
i.fa.fa-check
|
||||||
|
li
|
||||||
|
with "globalSearchViewChange-choice-all"
|
||||||
|
a.js-due-cards-view-all
|
||||||
|
i.fa.fa-users.colorful
|
||||||
|
| {{_ 'globalSearchViewChange-choice-all'}}
|
||||||
|
span.sub-name
|
||||||
|
+viewer
|
||||||
|
| {{_ 'globalSearchViewChange-choice-all-description' }}
|
||||||
|
if $eq Utils.globalSearchView "all"
|
||||||
|
i.fa.fa-check
|
||||||
148
client/components/main/globalSearch.js
Normal file
148
client/components/main/globalSearch.js
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
const subManager = new SubsManager();
|
||||||
|
|
||||||
|
BlazeComponent.extendComponent({
|
||||||
|
events() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'click .js-due-cards-view-change': Popup.open('globalSearchViewChange'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}).register('globalSearchHeaderBar');
|
||||||
|
|
||||||
|
Template.globalSearch.helpers({
|
||||||
|
userId() {
|
||||||
|
return Meteor.userId();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
BlazeComponent.extendComponent({
|
||||||
|
events() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'click .js-due-cards-view-me'() {
|
||||||
|
Utils.setDueCardsView('me');
|
||||||
|
Popup.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
'click .js-due-cards-view-all'() {
|
||||||
|
Utils.setDueCardsView('all');
|
||||||
|
Popup.close();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}).register('globalSearchViewChangePopup');
|
||||||
|
|
||||||
|
BlazeComponent.extendComponent({
|
||||||
|
onCreated() {
|
||||||
|
this.isPageReady = new ReactiveVar(true);
|
||||||
|
this.query = new ReactiveVar('');
|
||||||
|
|
||||||
|
// this.autorun(() => {
|
||||||
|
// const handle = subManager.subscribe('globalSearch');
|
||||||
|
// Tracker.nonreactive(() => {
|
||||||
|
// Tracker.autorun(() => {
|
||||||
|
// this.isPageReady.set(handle.ready());
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
Meteor.subscribe('setting');
|
||||||
|
},
|
||||||
|
|
||||||
|
events() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'submit .js-search-query-form'(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
this.query.set(evt.target.searchQuery.value);
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('query:', this.query.get());
|
||||||
|
|
||||||
|
let query = this.query.get();
|
||||||
|
const reUser = /^@(?<user>\w+)(\s+|$)/;
|
||||||
|
const reLabel = /^#(?<label>\w+)(\s+|$)/;
|
||||||
|
const reOperator1 = /^(?<operator>\w+):(?<value>\w+)(\s+|$)/;
|
||||||
|
const reOperator2 = /^(?<operator>\w+):(?<quote>["']*)(?<value>.*?)\k<quote>(\s+|$)/;
|
||||||
|
const reText = /^(?<text>[^:@#\s]+)(\s+|$)/;
|
||||||
|
const reQuotedText = /^(?<quote>["'])(?<text>\w+)\k<quote>(\s+|$)/;
|
||||||
|
const operatorMap = {
|
||||||
|
board: 'boards',
|
||||||
|
b: 'boards',
|
||||||
|
label: 'labels',
|
||||||
|
lable: 'labels',
|
||||||
|
user: 'users',
|
||||||
|
u: 'users',
|
||||||
|
swimlane: 'swimlanes',
|
||||||
|
swim: 'swimlanes',
|
||||||
|
s: 'swimlanes',
|
||||||
|
list: 'lists',
|
||||||
|
l: 'lists',
|
||||||
|
};
|
||||||
|
const selector = {
|
||||||
|
boards: [],
|
||||||
|
swimlanes: [],
|
||||||
|
lists: [],
|
||||||
|
users: [],
|
||||||
|
labels: [],
|
||||||
|
};
|
||||||
|
let text = '';
|
||||||
|
while (query) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('query:', query);
|
||||||
|
let m = query.match(reUser);
|
||||||
|
if (m) {
|
||||||
|
query = query.replace(reUser, '');
|
||||||
|
selector.users.push(m.groups.user);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = query.match(reLabel);
|
||||||
|
if (m) {
|
||||||
|
query = query.replace(reLabel, '');
|
||||||
|
selector.labels.push(m.groups.label);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = query.match(reOperator1);
|
||||||
|
if (!m) {
|
||||||
|
m = query.match(reOperator2);
|
||||||
|
if (m) {
|
||||||
|
query = query.replace(reOperator2, '');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query = query.replace(reOperator1, '');
|
||||||
|
}
|
||||||
|
if (m) {
|
||||||
|
const op = m.groups.operator.toLowerCase();
|
||||||
|
if (op in operatorMap) {
|
||||||
|
selector[operatorMap[op]].push(m.groups.value);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = query.match(reQuotedText);
|
||||||
|
if (!m) {
|
||||||
|
m = query.match(reText);
|
||||||
|
if (m) {
|
||||||
|
query = query.replace(reText, '');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query = query.replace(reQuotedText, '');
|
||||||
|
}
|
||||||
|
if (m) {
|
||||||
|
text += (text ? ' ' : '') + m.groups.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('selector:', selector);
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('text:', text);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
globalSearchList() {},
|
||||||
|
}).register('globalSearch');
|
||||||
69
client/components/main/globalSearch.styl
Normal file
69
client/components/main/globalSearch.styl
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
.due-cards-board-wrapper
|
||||||
|
border-radius: 8px
|
||||||
|
//padding: 0.5rem
|
||||||
|
min-width: 400px
|
||||||
|
border-width: 8px
|
||||||
|
border-color: grey
|
||||||
|
border-style: solid
|
||||||
|
margin-bottom: 2rem
|
||||||
|
margin-right: auto
|
||||||
|
margin-left: auto
|
||||||
|
|
||||||
|
.due-cards-board-title
|
||||||
|
font-size: 1.4rem
|
||||||
|
font-weight: bold
|
||||||
|
padding: 0.5rem
|
||||||
|
background-color: grey
|
||||||
|
color: white
|
||||||
|
|
||||||
|
.due-cards-swimlane-title
|
||||||
|
font-size: 1.1rem
|
||||||
|
font-weight: bold
|
||||||
|
padding: 0.5rem
|
||||||
|
padding-bottom: 0.4rem
|
||||||
|
margin-top: 0
|
||||||
|
margin-bottom: 0.5rem
|
||||||
|
//border-top: black 1px solid
|
||||||
|
//border-bottom: black 1px solid
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.swimlane-default-color
|
||||||
|
background-color: lightgrey
|
||||||
|
|
||||||
|
.due-cards-list-title
|
||||||
|
font-weight: bold
|
||||||
|
font-size: 1.1rem
|
||||||
|
//padding-bottom: 0
|
||||||
|
//margin-bottom: 0
|
||||||
|
text-align: center
|
||||||
|
margin-bottom: 0.7rem
|
||||||
|
|
||||||
|
.due-cards-list-wrapper
|
||||||
|
margin: 1rem
|
||||||
|
border-radius: 5px
|
||||||
|
padding: 1.5rem
|
||||||
|
padding-top: 0.75rem
|
||||||
|
display: inline-block
|
||||||
|
min-width: 250px
|
||||||
|
max-width: 350px
|
||||||
|
|
||||||
|
.due-cards-card-wrapper
|
||||||
|
margin-top: 0
|
||||||
|
margin-bottom: 10px
|
||||||
|
|
||||||
|
.due-cards-dueat-list-wrapper
|
||||||
|
max-width: 500px
|
||||||
|
margin-right: auto
|
||||||
|
margin-left: auto
|
||||||
|
|
||||||
|
.due-cards-field-name
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
.due-cards-context
|
||||||
|
display: inline-block
|
||||||
|
|
||||||
|
.due-cards-context-separator
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
.due-cards-context-list
|
||||||
|
margin-bottom: 0.7rem
|
||||||
|
|
@ -149,6 +149,24 @@ FlowRouter.route('/due-cards', {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
FlowRouter.route('/global-search', {
|
||||||
|
name: 'global-search',
|
||||||
|
action() {
|
||||||
|
Filter.reset();
|
||||||
|
// EscapeActions.executeAll();
|
||||||
|
EscapeActions.executeUpTo('popup-close');
|
||||||
|
|
||||||
|
Utils.manageCustomUI();
|
||||||
|
Utils.manageMatomo();
|
||||||
|
|
||||||
|
BlazeLayout.render('defaultLayout', {
|
||||||
|
headerBar: 'globalSearchHeaderBar',
|
||||||
|
content: 'globalSearch',
|
||||||
|
});
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
FlowRouter.route('/import/:source', {
|
FlowRouter.route('/import/:source', {
|
||||||
name: 'import',
|
name: 'import',
|
||||||
triggersEnter: [AccountsTemplates.ensureSignedIn],
|
triggersEnter: [AccountsTemplates.ensureSignedIn],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue