Migrate search drop down to `menu-panel` component.
This commit is contained in:
parent
3bc79f6885
commit
d4b987ff32
|
@ -1,13 +1,14 @@
|
||||||
import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
|
import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
const PANEL_BODY_MARGIN = 30;
|
const PANEL_BODY_MARGIN = 30;
|
||||||
|
const mutationSupport = !!window['MutationObserver'];
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNameBindings: [':menu-panel', 'visible::hidden', 'viewMode'],
|
classNameBindings: [':menu-panel', 'visible::hidden', 'viewMode'],
|
||||||
|
|
||||||
showClose: Ember.computed.equal('viewMode', 'slide-in'),
|
showClose: Ember.computed.equal('viewMode', 'slide-in'),
|
||||||
|
|
||||||
_resizeComponent() {
|
_layoutComponent() {
|
||||||
if (!this.get('visible')) { return; }
|
if (!this.get('visible')) { return; }
|
||||||
|
|
||||||
const viewMode = this.get('viewMode');
|
const viewMode = this.get('viewMode');
|
||||||
|
@ -27,26 +28,22 @@ export default Ember.Component.extend({
|
||||||
this.$().css({ left: posLeft + "px", top: posTop + "px" });
|
this.$().css({ left: posLeft + "px", top: posTop + "px" });
|
||||||
|
|
||||||
// adjust panel height
|
// adjust panel height
|
||||||
let contentHeight = parseInt($('.panel-body-contents').height());
|
let contentHeight = parseInt(this.$('.panel-body-contents').height());
|
||||||
const fullHeight = parseInt($(window).height());
|
const fullHeight = parseInt($(window).height());
|
||||||
|
|
||||||
const offsetTop = this.$().offset().top;
|
const offsetTop = this.$().offset().top;
|
||||||
if (contentHeight + offsetTop + PANEL_BODY_MARGIN > fullHeight) {
|
const scrollTop = $(window).scrollTop();
|
||||||
contentHeight = fullHeight - (offsetTop - $(window).scrollTop()) - PANEL_BODY_MARGIN;
|
if (contentHeight + (offsetTop - scrollTop) + PANEL_BODY_MARGIN > fullHeight) {
|
||||||
|
contentHeight = fullHeight - (offsetTop - scrollTop) - PANEL_BODY_MARGIN;
|
||||||
}
|
}
|
||||||
$panelBody.height(contentHeight);
|
$panelBody.height(contentHeight);
|
||||||
} else {
|
} else {
|
||||||
$panelBody.height('auto');
|
$panelBody.height('auto');
|
||||||
|
|
||||||
const headerHeight = parseInt($('header.d-header').height() + 3);
|
const headerHeight = parseInt($('header.d-header').height() + 3);
|
||||||
this.$().css({ left: "auto", top: headerHeight + "px" });
|
this.$().css({ left: "auto", top: headerHeight + "px" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_needsResize() {
|
|
||||||
Ember.run.scheduleOnce('afterRender', this, this._resizeComponent);
|
|
||||||
},
|
|
||||||
|
|
||||||
@computed('force')
|
@computed('force')
|
||||||
viewMode() {
|
viewMode() {
|
||||||
const force = this.get('force');
|
const force = this.get('force');
|
||||||
|
@ -61,6 +58,10 @@ export default Ember.Component.extend({
|
||||||
const markActive = this.get('markActive');
|
const markActive = this.get('markActive');
|
||||||
|
|
||||||
if (this.get('visible')) {
|
if (this.get('visible')) {
|
||||||
|
this.appEvents.on('dropdowns:closeAll', this, this.hide);
|
||||||
|
|
||||||
|
// Allow us to hook into things being shown
|
||||||
|
Ember.run.scheduleOnce('afterRender', () => this.sendAction('onVisible'));
|
||||||
|
|
||||||
if (isDropdown && markActive) {
|
if (isDropdown && markActive) {
|
||||||
$(markActive).addClass('active');
|
$(markActive).addClass('active');
|
||||||
|
@ -72,14 +73,16 @@ export default Ember.Component.extend({
|
||||||
if ($target.closest('.menu-panel').length > 0) { return; }
|
if ($target.closest('.menu-panel').length > 0) { return; }
|
||||||
this.hide();
|
this.hide();
|
||||||
});
|
});
|
||||||
|
this.performLayout();
|
||||||
|
this._watchSizeChanges();
|
||||||
} else {
|
} else {
|
||||||
|
Ember.run.scheduleOnce('afterRender', () => this.sendAction('onHidden'));
|
||||||
if (markActive) {
|
if (markActive) {
|
||||||
$(markActive).removeClass('active');
|
$(markActive).removeClass('active');
|
||||||
}
|
}
|
||||||
$('html').off('click.close-menu-panel');
|
$('html').off('click.close-menu-panel');
|
||||||
|
this._stopWatchingSize();
|
||||||
}
|
}
|
||||||
this._needsResize();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed()
|
@computed()
|
||||||
|
@ -102,9 +105,38 @@ export default Ember.Component.extend({
|
||||||
return this.siteSettings.faq_url ? this.siteSettings.faq_url : Discourse.getURL('/faq');
|
return this.siteSettings.faq_url ? this.siteSettings.faq_url : Discourse.getURL('/faq');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
performLayout() {
|
||||||
|
Ember.run.scheduleOnce('afterRender', this, this._layoutComponent);
|
||||||
|
},
|
||||||
|
|
||||||
|
_watchSizeChanges() {
|
||||||
|
if (mutationSupport) {
|
||||||
|
this._observer.disconnect();
|
||||||
|
this._observer.observe(this.element, { childList: true, subtree: true });
|
||||||
|
} else {
|
||||||
|
clearInterval(this._resizeInterval);
|
||||||
|
this._resizeInterval = setInterval(() => {
|
||||||
|
Ember.run(() => {
|
||||||
|
const contentHeight = parseInt(this.$('.panel-body-contents').height());
|
||||||
|
if (contentHeight !== this._lastHeight) { this.performLayout(); }
|
||||||
|
this._lastHeight = contentHeight;
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_stopWatchingSize() {
|
||||||
|
if (mutationSupport) {
|
||||||
|
this._observer.disconnect();
|
||||||
|
} else {
|
||||||
|
clearInterval(this._resizeInterval);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
@on('didInsertElement')
|
@on('didInsertElement')
|
||||||
_bindEvents() {
|
_bindEvents() {
|
||||||
this.$().on('click.discourse-menu-panel', 'a', () => {
|
this.$().on('click.discourse-menu-panel', 'a', (e) => {
|
||||||
|
if ($(e.target).data('ember-action')) { return; }
|
||||||
this.hide();
|
this.hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -116,11 +148,16 @@ export default Ember.Component.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Recompute styles on resize
|
|
||||||
$(window).on('resize.discourse-menu-panel', () => {
|
$(window).on('resize.discourse-menu-panel', () => {
|
||||||
this.propertyDidChange('viewMode');
|
this.propertyDidChange('viewMode');
|
||||||
this._needsResize();
|
this.performLayout();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (mutationSupport) {
|
||||||
|
this._observer = new MutationObserver(() => {
|
||||||
|
Ember.run(() => this.performLayout());
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@on('willDestroyElement')
|
@on('willDestroyElement')
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
import searchForTerm from 'discourse/lib/search-for-term';
|
||||||
|
import DiscourseURL from 'discourse/lib/url';
|
||||||
|
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||||
|
import showModal from 'discourse/lib/show-modal';
|
||||||
|
|
||||||
|
let _dontSearch = false;
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
searchService: Ember.inject.service('search'),
|
||||||
|
classNames: ['search-menu'],
|
||||||
|
typeFilter: null,
|
||||||
|
|
||||||
|
@observes('searchService.searchContext')
|
||||||
|
contextChanged: function() {
|
||||||
|
if (this.get('searchService.searchContextEnabled')) {
|
||||||
|
_dontSearch = true;
|
||||||
|
this.set('searchService.searchContextEnabled', false);
|
||||||
|
_dontSearch = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('searchService.searchContext', 'searchService.term', 'searchService.searchContextEnabled')
|
||||||
|
fullSearchUrlRelative(searchContext, term, searchContextEnabled) {
|
||||||
|
|
||||||
|
if (searchContextEnabled && Ember.get(searchContext, 'type') === 'topic') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = '/search?q=' + encodeURIComponent(this.get('searchService.term'));
|
||||||
|
if (searchContextEnabled) {
|
||||||
|
if (searchContext.id.toString().toLowerCase() === this.get('currentUser.username_lower') &&
|
||||||
|
searchContext.type === "private_messages"
|
||||||
|
) {
|
||||||
|
url += ' in:private';
|
||||||
|
} else {
|
||||||
|
url += encodeURIComponent(" " + searchContext.type + ":" + searchContext.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('fullSearchUrlRelative')
|
||||||
|
fullSearchUrl(fullSearchUrlRelative) {
|
||||||
|
if (fullSearchUrlRelative) {
|
||||||
|
return Discourse.getURL(fullSearchUrlRelative);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('searchService.searchContext')
|
||||||
|
searchContextDescription(ctx) {
|
||||||
|
if (ctx) {
|
||||||
|
switch(Em.get(ctx, 'type')) {
|
||||||
|
case 'topic':
|
||||||
|
return I18n.t('search.context.topic');
|
||||||
|
case 'user':
|
||||||
|
return I18n.t('search.context.user', {username: Em.get(ctx, 'user.username')});
|
||||||
|
case 'category':
|
||||||
|
return I18n.t('search.context.category', {category: Em.get(ctx, 'category.name')});
|
||||||
|
case 'private_messages':
|
||||||
|
return I18n.t('search.context.private_messages');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@observes('searchService.searchContextEnabled')
|
||||||
|
searchContextEnabledChanged() {
|
||||||
|
if (_dontSearch) { return; }
|
||||||
|
this.newSearchNeeded();
|
||||||
|
},
|
||||||
|
|
||||||
|
// If we need to perform another search
|
||||||
|
@observes('searchService.term', 'typeFilter')
|
||||||
|
newSearchNeeded() {
|
||||||
|
this.set('noResults', false);
|
||||||
|
const term = (this.get('searchService.term') || '').trim();
|
||||||
|
if (term.length >= Discourse.SiteSettings.min_search_term_length) {
|
||||||
|
this.set('loading', true);
|
||||||
|
Ember.run.debounce(this, 'searchTerm', term, this.get('typeFilter'), 400);
|
||||||
|
} else {
|
||||||
|
this.setProperties({ content: null });
|
||||||
|
}
|
||||||
|
this.set('selectedIndex', 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
searchTerm(term, typeFilter) {
|
||||||
|
// for cancelling debounced search
|
||||||
|
if (this._cancelSearch){
|
||||||
|
this._cancelSearch = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._search) {
|
||||||
|
this._search.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchContext = this.get('searchService.searchContextEnabled') ? this.get('searchService.searchContext') : null;
|
||||||
|
this._search = searchForTerm(term, { typeFilter, searchContext, fullSearchUrl: this.get('fullSearchUrl') });
|
||||||
|
|
||||||
|
this._search.then((content) => {
|
||||||
|
this.setProperties({ noResults: !content, content });
|
||||||
|
}).finally(() => {
|
||||||
|
this.set('loading', false);
|
||||||
|
this._search = null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('typeFilter', 'loading')
|
||||||
|
showCancelFilter(typeFilter, loading) {
|
||||||
|
if (loading) { return false; }
|
||||||
|
return !Ember.isEmpty(typeFilter);
|
||||||
|
},
|
||||||
|
|
||||||
|
@observes('searchService.term')
|
||||||
|
termChanged() {
|
||||||
|
this.cancelTypeFilter();
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
fullSearch() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
if (this._search) {
|
||||||
|
this._search.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe we are debounced and delayed
|
||||||
|
// stop that as well
|
||||||
|
this._cancelSearch = true;
|
||||||
|
Em.run.later(function() {
|
||||||
|
self._cancelSearch = false;
|
||||||
|
}, 400);
|
||||||
|
|
||||||
|
const url = this.get('fullSearchUrlRelative');
|
||||||
|
if (url) {
|
||||||
|
DiscourseURL.routeTo(url);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
moreOfType(type) {
|
||||||
|
this.set('typeFilter', type);
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelType() {
|
||||||
|
this.cancelTypeFilter();
|
||||||
|
},
|
||||||
|
|
||||||
|
showedSearch() {
|
||||||
|
$('#search-term').focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
showSearchHelp() {
|
||||||
|
// TODO: @EvitTrout how do we get a loading indicator here?
|
||||||
|
Discourse.ajax("/static/search_help.html", { dataType: 'html' }).then((model) => {
|
||||||
|
showModal('searchHelp', { model });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelHighlight() {
|
||||||
|
this.set('searchService.highlightTerm', null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelTypeFilter() {
|
||||||
|
this.set('typeFilter', null);
|
||||||
|
},
|
||||||
|
|
||||||
|
keyDown(e) {
|
||||||
|
const term = this.get('searchService.term');
|
||||||
|
if (e.which === 13 && term && term.length >= this.siteSettings.min_search_term_length) {
|
||||||
|
this.set('searchVisible', false);
|
||||||
|
this.send('fullSearch');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,7 +1,9 @@
|
||||||
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
import TextField from 'discourse/components/text-field';
|
import TextField from 'discourse/components/text-field';
|
||||||
|
|
||||||
export default TextField.extend({
|
export default TextField.extend({
|
||||||
placeholder: function() {
|
@computed('searchService.searchContextEnabled')
|
||||||
return this.get('searchContextEnabled') ? "" : I18n.t('search.title');
|
placeholder: function(searchContextEnabled) {
|
||||||
}.property('searchContextEnabled')
|
return searchContextEnabled ? "" : I18n.t('search.title');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,5 +15,5 @@ export default Ember.Controller.extend({
|
||||||
@computed
|
@computed
|
||||||
loginRequired() {
|
loginRequired() {
|
||||||
return Discourse.SiteSettings.login_required && !Discourse.User.current();
|
return Discourse.SiteSettings.login_required && !Discourse.User.current();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@ const HeaderController = Ember.Controller.extend({
|
||||||
notifications: null,
|
notifications: null,
|
||||||
loadingNotifications: false,
|
loadingNotifications: false,
|
||||||
hamburgerVisible: false,
|
hamburgerVisible: false,
|
||||||
|
searchVisible: false,
|
||||||
needs: ['application'],
|
needs: ['application'],
|
||||||
|
|
||||||
loginRequired: Em.computed.alias('controllers.application.loginRequired'),
|
loginRequired: Em.computed.alias('controllers.application.loginRequired'),
|
||||||
|
@ -71,9 +72,16 @@ const HeaderController = Ember.Controller.extend({
|
||||||
headerView.showDropdownBySelector("#user-notifications");
|
headerView.showDropdownBySelector("#user-notifications");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toggleSearchMenu() {
|
||||||
|
this.appEvents.trigger('dropdowns:closeAll');
|
||||||
|
this.toggleProperty('searchVisible');
|
||||||
|
},
|
||||||
|
|
||||||
toggleHamburgerMenu() {
|
toggleHamburgerMenu() {
|
||||||
|
this.appEvents.trigger('dropdowns:closeAll');
|
||||||
this.toggleProperty('hamburgerVisible');
|
this.toggleProperty('hamburgerVisible');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,170 +0,0 @@
|
||||||
import searchForTerm from 'discourse/lib/search-for-term';
|
|
||||||
import DiscourseURL from 'discourse/lib/url';
|
|
||||||
import computed from 'ember-addons/ember-computed-decorators';
|
|
||||||
|
|
||||||
let _dontSearch = false;
|
|
||||||
|
|
||||||
export default Em.Controller.extend({
|
|
||||||
typeFilter: null,
|
|
||||||
|
|
||||||
@computed('searchContext')
|
|
||||||
contextType: {
|
|
||||||
get(searchContext) {
|
|
||||||
if (searchContext) {
|
|
||||||
return Ember.get(searchContext, 'type');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
set(value, searchContext) {
|
|
||||||
// a bit hacky, consider cleaning this up, need to work through all observers though
|
|
||||||
const context = $.extend({}, searchContext);
|
|
||||||
context.type = value;
|
|
||||||
this.set('searchContext', context);
|
|
||||||
return this.get('searchContext.type');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
contextChanged: function(){
|
|
||||||
if (this.get('searchContextEnabled')) {
|
|
||||||
_dontSearch = true;
|
|
||||||
this.set('searchContextEnabled', false);
|
|
||||||
_dontSearch = false;
|
|
||||||
}
|
|
||||||
}.observes('searchContext'),
|
|
||||||
|
|
||||||
fullSearchUrlRelative: function(){
|
|
||||||
|
|
||||||
if (this.get('searchContextEnabled') && this.get('searchContext.type') === 'topic') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let url = '/search?q=' + encodeURIComponent(this.get('term'));
|
|
||||||
const searchContext = this.get('searchContext');
|
|
||||||
|
|
||||||
if (this.get('searchContextEnabled')) {
|
|
||||||
if (searchContext.id.toString().toLowerCase() === this.get('currentUser.username_lower') &&
|
|
||||||
searchContext.type === "private_messages"
|
|
||||||
) {
|
|
||||||
url += ' in:private';
|
|
||||||
} else {
|
|
||||||
url += encodeURIComponent(" " + searchContext.type + ":" + searchContext.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
|
|
||||||
}.property('searchContext','term','searchContextEnabled'),
|
|
||||||
|
|
||||||
fullSearchUrl: function(){
|
|
||||||
const url = this.get('fullSearchUrlRelative');
|
|
||||||
if (url) {
|
|
||||||
return Discourse.getURL(url);
|
|
||||||
}
|
|
||||||
}.property('fullSearchUrlRelative'),
|
|
||||||
|
|
||||||
searchContextDescription: function(){
|
|
||||||
const ctx = this.get('searchContext');
|
|
||||||
if (ctx) {
|
|
||||||
switch(Em.get(ctx, 'type')) {
|
|
||||||
case 'topic':
|
|
||||||
return I18n.t('search.context.topic');
|
|
||||||
case 'user':
|
|
||||||
return I18n.t('search.context.user', {username: Em.get(ctx, 'user.username')});
|
|
||||||
case 'category':
|
|
||||||
return I18n.t('search.context.category', {category: Em.get(ctx, 'category.name')});
|
|
||||||
case 'private_messages':
|
|
||||||
return I18n.t('search.context.private_messages');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.property('searchContext'),
|
|
||||||
|
|
||||||
searchContextEnabledChanged: function(){
|
|
||||||
if (_dontSearch) { return; }
|
|
||||||
this.newSearchNeeded();
|
|
||||||
}.observes('searchContextEnabled'),
|
|
||||||
|
|
||||||
// If we need to perform another search
|
|
||||||
newSearchNeeded: function() {
|
|
||||||
this.set('noResults', false);
|
|
||||||
const term = (this.get('term') || '').trim();
|
|
||||||
if (term.length >= Discourse.SiteSettings.min_search_term_length) {
|
|
||||||
this.set('loading', true);
|
|
||||||
|
|
||||||
Ember.run.debounce(this, 'searchTerm', term, this.get('typeFilter'), 400);
|
|
||||||
} else {
|
|
||||||
this.setProperties({ content: null });
|
|
||||||
}
|
|
||||||
this.set('selectedIndex', 0);
|
|
||||||
}.observes('term', 'typeFilter'),
|
|
||||||
|
|
||||||
searchTerm(term, typeFilter) {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
// for cancelling debounced search
|
|
||||||
if (this._cancelSearch){
|
|
||||||
this._cancelSearch = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._search) {
|
|
||||||
this._search.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchContext = this.get('searchContextEnabled') ? this.get('searchContext') : null;
|
|
||||||
|
|
||||||
this._search = searchForTerm(term, {
|
|
||||||
typeFilter,
|
|
||||||
searchContext,
|
|
||||||
fullSearchUrl: this.get('fullSearchUrl')
|
|
||||||
});
|
|
||||||
|
|
||||||
this._search.then(function(results) {
|
|
||||||
self.setProperties({ noResults: !results, content: results });
|
|
||||||
}).finally(function() {
|
|
||||||
self.set('loading', false);
|
|
||||||
self._search = null;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
showCancelFilter: function() {
|
|
||||||
if (this.get('loading')) return false;
|
|
||||||
return !Ember.isEmpty(this.get('typeFilter'));
|
|
||||||
}.property('typeFilter', 'loading'),
|
|
||||||
|
|
||||||
termChanged: function() {
|
|
||||||
this.cancelTypeFilter();
|
|
||||||
}.observes('term'),
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
fullSearch() {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
if (this._search) {
|
|
||||||
this._search.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
// maybe we are debounced and delayed
|
|
||||||
// stop that as well
|
|
||||||
this._cancelSearch = true;
|
|
||||||
Em.run.later(function(){
|
|
||||||
self._cancelSearch = false;
|
|
||||||
}, 400);
|
|
||||||
|
|
||||||
const url = this.get('fullSearchUrlRelative');
|
|
||||||
if (url) {
|
|
||||||
DiscourseURL.routeTo(url);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
moreOfType(type) {
|
|
||||||
this.set('typeFilter', type);
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelType() {
|
|
||||||
this.cancelTypeFilter();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelTypeFilter() {
|
|
||||||
this.set('typeFilter', null);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -8,14 +8,13 @@ import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
import computed from 'ember-addons/ember-computed-decorators';
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
|
export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
|
||||||
|
needs: ['header', 'modal', 'composer', 'quote-button', 'topic-progress', 'application'],
|
||||||
multiSelect: false,
|
multiSelect: false,
|
||||||
needs: ['header', 'modal', 'composer', 'quote-button', 'search', 'topic-progress', 'application'],
|
|
||||||
allPostsSelected: false,
|
allPostsSelected: false,
|
||||||
editingTopic: false,
|
editingTopic: false,
|
||||||
selectedPosts: null,
|
selectedPosts: null,
|
||||||
selectedReplies: null,
|
selectedReplies: null,
|
||||||
queryParams: ['filter', 'username_filters', 'show_deleted'],
|
queryParams: ['filter', 'username_filters', 'show_deleted'],
|
||||||
searchHighlight: null,
|
|
||||||
loadedAllPosts: false,
|
loadedAllPosts: false,
|
||||||
enteredAt: null,
|
enteredAt: null,
|
||||||
firstPostExpanded: false,
|
firstPostExpanded: false,
|
||||||
|
@ -23,10 +22,6 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
|
||||||
|
|
||||||
maxTitleLength: setting('max_topic_title_length'),
|
maxTitleLength: setting('max_topic_title_length'),
|
||||||
|
|
||||||
contextChanged: function() {
|
|
||||||
this.set('controllers.search.searchContext', this.get('model.searchContext'));
|
|
||||||
}.observes('topic'),
|
|
||||||
|
|
||||||
_titleChanged: function() {
|
_titleChanged: function() {
|
||||||
const title = this.get('model.title');
|
const title = this.get('model.title');
|
||||||
if (!Ember.isEmpty(title)) {
|
if (!Ember.isEmpty(title)) {
|
||||||
|
@ -37,20 +32,6 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, {
|
||||||
}
|
}
|
||||||
}.observes('model.title', 'category'),
|
}.observes('model.title', 'category'),
|
||||||
|
|
||||||
termChanged: function() {
|
|
||||||
const dropdown = this.get('controllers.header.visibleDropdown');
|
|
||||||
const term = this.get('controllers.search.term');
|
|
||||||
|
|
||||||
if(dropdown === 'search-dropdown' && term){
|
|
||||||
this.set('searchHighlight', term);
|
|
||||||
} else {
|
|
||||||
if(this.get('searchHighlight')){
|
|
||||||
this.set('searchHighlight', null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}.observes('controllers.search.term', 'controllers.header.visibleDropdown'),
|
|
||||||
|
|
||||||
postStreamLoadedAllPostsChanged: function() {
|
postStreamLoadedAllPostsChanged: function() {
|
||||||
// semantics of loaded all posts are slightly diff at topic level,
|
// semantics of loaded all posts are slightly diff at topic level,
|
||||||
// it just means that we "once" loaded all posts, this means we don't
|
// it just means that we "once" loaded all posts, this means we don't
|
||||||
|
|
|
@ -3,6 +3,7 @@ import KeyboardShortcuts from 'discourse/lib/keyboard-shortcuts';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "keyboard-shortcuts",
|
name: "keyboard-shortcuts",
|
||||||
|
|
||||||
initialize(container) {
|
initialize(container) {
|
||||||
KeyboardShortcuts.bindEvents(Mousetrap, container);
|
KeyboardShortcuts.bindEvents(Mousetrap, container);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,9 @@ export default {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this._stopCallback();
|
this._stopCallback();
|
||||||
|
|
||||||
|
|
||||||
|
this.searchService = this.container.lookup('search-service:main');
|
||||||
|
|
||||||
_.each(PATH_BINDINGS, this._bindToPath, this);
|
_.each(PATH_BINDINGS, this._bindToPath, this);
|
||||||
_.each(CLICK_BINDINGS, this._bindToClick, this);
|
_.each(CLICK_BINDINGS, this._bindToClick, this);
|
||||||
_.each(SELECTED_POST_BINDINGS, this._bindToSelectedPost, this);
|
_.each(SELECTED_POST_BINDINGS, this._bindToSelectedPost, this);
|
||||||
|
@ -131,10 +134,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
showBuiltinSearch() {
|
showBuiltinSearch() {
|
||||||
if ($('#search-dropdown').is(':visible')) {
|
this.searchService.set('searchContextEnabled', false);
|
||||||
this._toggleSearch(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentPath = this.container.lookup('controller:application').get('currentPath'),
|
const currentPath = this.container.lookup('controller:application').get('currentPath'),
|
||||||
blacklist = [ /^discovery\.categories/ ],
|
blacklist = [ /^discovery\.categories/ ],
|
||||||
|
@ -144,11 +144,12 @@ export default {
|
||||||
|
|
||||||
// If we're viewing a topic, only intercept search if there are cloaked posts
|
// If we're viewing a topic, only intercept search if there are cloaked posts
|
||||||
if (showSearch && currentPath.match(/^topic\./)) {
|
if (showSearch && currentPath.match(/^topic\./)) {
|
||||||
showSearch = $('.cooked').length < this.container.lookup('controller:topic').get('postStream.stream.length');
|
showSearch = $('.cooked').length < this.container.lookup('controller:topic').get('model.postStream.stream.length');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showSearch) {
|
if (showSearch) {
|
||||||
this._toggleSearch(true);
|
this.searchService.set('searchContextEnabled', true);
|
||||||
|
this.showSearch();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,8 +169,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
showSearch() {
|
showSearch() {
|
||||||
this._toggleSearch(false);
|
this.container.lookup('controller:header').send('toggleSearchMenu');
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleHamburgerMenu() {
|
toggleHamburgerMenu() {
|
||||||
|
@ -370,12 +370,5 @@ export default {
|
||||||
|
|
||||||
return oldStopCallback(e, element, combo);
|
return oldStopCallback(e, element, combo);
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
_toggleSearch(selectContext) {
|
|
||||||
$('#search-button').click();
|
|
||||||
if (selectContext) {
|
|
||||||
this.container.lookup('controller:search').set('searchContextEnabled', true);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@ import AppEvents from 'discourse/lib/app-events';
|
||||||
import Store from 'discourse/models/store';
|
import Store from 'discourse/models/store';
|
||||||
import DiscourseURL from 'discourse/lib/url';
|
import DiscourseURL from 'discourse/lib/url';
|
||||||
import DiscourseLocation from 'discourse/lib/discourse-location';
|
import DiscourseLocation from 'discourse/lib/discourse-location';
|
||||||
|
import SearchService from 'discourse/services/search';
|
||||||
|
|
||||||
function inject() {
|
function inject() {
|
||||||
const app = arguments[0],
|
const app = arguments[0],
|
||||||
|
@ -35,6 +36,9 @@ export default {
|
||||||
app.register('site-settings:main', Discourse.SiteSettings, { instantiate: false });
|
app.register('site-settings:main', Discourse.SiteSettings, { instantiate: false });
|
||||||
injectAll(app, 'siteSettings');
|
injectAll(app, 'siteSettings');
|
||||||
|
|
||||||
|
app.register('search-service:main', SearchService);
|
||||||
|
injectAll(app, 'searchService');
|
||||||
|
|
||||||
app.register('session:main', Session.current(), { instantiate: false });
|
app.register('session:main', Session.current(), { instantiate: false });
|
||||||
injectAll(app, 'session');
|
injectAll(app, 'session');
|
||||||
|
|
||||||
|
|
|
@ -99,13 +99,6 @@ const ApplicationRoute = Discourse.Route.extend(OpenComposer, {
|
||||||
showModal('keyboard-shortcuts-help', { title: 'keyboard_shortcuts_help.title'});
|
showModal('keyboard-shortcuts-help', { title: 'keyboard_shortcuts_help.title'});
|
||||||
},
|
},
|
||||||
|
|
||||||
showSearchHelp() {
|
|
||||||
// TODO: @EvitTrout how do we get a loading indicator here?
|
|
||||||
Discourse.ajax("/static/search_help.html", { dataType: 'html' }).then(function(model){
|
|
||||||
showModal('searchHelp', { model });
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Close the current modal, and destroy its state.
|
// Close the current modal, and destroy its state.
|
||||||
closeModal() {
|
closeModal() {
|
||||||
this.render('hide-modal', { into: 'modal', outlet: 'modalBody' });
|
this.render('hide-modal', { into: 'modal', outlet: 'modalBody' });
|
||||||
|
|
|
@ -81,7 +81,7 @@ export default function(filter, params) {
|
||||||
expandAllPinned: true
|
expandAllPinned: true
|
||||||
});
|
});
|
||||||
|
|
||||||
this.controllerFor('search').set('searchContext', model.get('searchContext'));
|
this.searchService.set('searchContext', model.get('searchContext'));
|
||||||
this.set('topics', null);
|
this.set('topics', null);
|
||||||
|
|
||||||
this.openTopicDraft(topics);
|
this.openTopicDraft(topics);
|
||||||
|
@ -98,7 +98,7 @@ export default function(filter, params) {
|
||||||
|
|
||||||
deactivate: function() {
|
deactivate: function() {
|
||||||
this._super();
|
this._super();
|
||||||
this.controllerFor('search').set('searchContext', null);
|
this.searchService.set('searchContext', null);
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
@ -25,11 +25,11 @@ export default (viewName, path) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.controllerFor("user").set("pmView", viewName);
|
this.controllerFor("user").set("pmView", viewName);
|
||||||
this.controllerFor("search").set("contextType", "private_messages");
|
this.searchService.set('contextType', 'private_messages');
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
this.controllerFor("search").set("contextType", "user");
|
this.searchService.set('contextType', 'private_messages');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -175,14 +175,12 @@ const TopicRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
const topic = this.modelFor('topic');
|
const topic = this.modelFor('topic');
|
||||||
this.session.set('lastTopicIdViewed', parseInt(topic.get('id'), 10));
|
this.session.set('lastTopicIdViewed', parseInt(topic.get('id'), 10));
|
||||||
this.controllerFor('search').set('searchContext', topic.get('searchContext'));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
// Clear the search context
|
this.searchService.set('searchContext', null);
|
||||||
this.controllerFor('search').set('searchContext', null);
|
|
||||||
this.controllerFor('user-card').set('visible', false);
|
this.controllerFor('user-card').set('visible', false);
|
||||||
|
|
||||||
const topicController = this.controllerFor('topic'),
|
const topicController = this.controllerFor('topic'),
|
||||||
|
@ -220,11 +218,8 @@ const TopicRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
Discourse.TopicRoute.trigger('setupTopicController', this);
|
Discourse.TopicRoute.trigger('setupTopicController', this);
|
||||||
|
|
||||||
this.controllerFor('header').setProperties({
|
this.controllerFor('header').setProperties({ topic: model, showExtraInfo: false });
|
||||||
topic: model,
|
this.searchService.set('searchContext', model.get('searchContext'));
|
||||||
showExtraInfo: false
|
|
||||||
});
|
|
||||||
|
|
||||||
this.controllerFor('topic-admin-menu').set('model', model);
|
this.controllerFor('topic-admin-menu').set('model', model);
|
||||||
|
|
||||||
this.controllerFor('composer').set('topic', model);
|
this.controllerFor('composer').set('topic', model);
|
||||||
|
|
|
@ -65,9 +65,7 @@ export default Discourse.Route.extend({
|
||||||
|
|
||||||
setupController(controller, user) {
|
setupController(controller, user) {
|
||||||
controller.set('model', user);
|
controller.set('model', user);
|
||||||
|
this.searchService.set('searchContext', user.get('searchContext'))
|
||||||
// Add a search context
|
|
||||||
this.controllerFor('search').set('searchContext', user.get('searchContext'));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
activate() {
|
activate() {
|
||||||
|
@ -83,7 +81,7 @@ export default Discourse.Route.extend({
|
||||||
this.messageBus.unsubscribe("/users/" + this.modelFor('user').get('username_lower'));
|
this.messageBus.unsubscribe("/users/" + this.modelFor('user').get('username_lower'));
|
||||||
|
|
||||||
// Remove the search context
|
// Remove the search context
|
||||||
this.controllerFor('search').set('searchContext', null);
|
this.searchService.set('searchContext', null)
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
|
export default Ember.Object.extend({
|
||||||
|
searchContextEnabled: false,
|
||||||
|
searchContext: null,
|
||||||
|
term: null,
|
||||||
|
highlightTerm: null,
|
||||||
|
|
||||||
|
@observes('term')
|
||||||
|
_sethighlightTerm() {
|
||||||
|
this.set('highlightTerm', this.get('term'));
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('searchContext')
|
||||||
|
contextType: {
|
||||||
|
get(searchContext) {
|
||||||
|
if (searchContext) {
|
||||||
|
return Ember.get(searchContext, 'type');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set(value, searchContext) {
|
||||||
|
// a bit hacky, consider cleaning this up, need to work through all observers though
|
||||||
|
const context = $.extend({}, searchContext);
|
||||||
|
context.type = value;
|
||||||
|
this.set('searchContext', context);
|
||||||
|
return this.get('searchContext.type');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,44 @@
|
||||||
|
{{#menu-panel visible=searchVisible
|
||||||
|
markActive=".search-dropdown"
|
||||||
|
onVisible="showedSearch"
|
||||||
|
onHidden="cancelHighlight"}}
|
||||||
|
|
||||||
|
{{plugin-outlet "above-search"}}
|
||||||
|
{{search-text-field value=searchService.term id="search-term"}}
|
||||||
|
|
||||||
|
<div class="search-context">
|
||||||
|
{{#if searchService.searchContext}}
|
||||||
|
<label>
|
||||||
|
{{input type="checkbox" name="searchContext" checked=searchService.searchContextEnabled}} {{searchContextDescription}}
|
||||||
|
</label>
|
||||||
|
{{/if}}
|
||||||
|
<a href class="show-help" {{action "showSearchHelp" bubbles=false}}>{{i18n "show_help"}}</a>
|
||||||
|
<div class='clearfix'></div>
|
||||||
|
</div>
|
||||||
|
{{#if loading}}
|
||||||
|
<div class="searching">{{loading-spinner}}</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="results">
|
||||||
|
{{#if noResults}}
|
||||||
|
<div class="no-results">
|
||||||
|
{{i18n "search.no_results"}}
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
{{#each content.resultTypes as |resultType|}}
|
||||||
|
<ul>
|
||||||
|
<li class="heading row">{{resultType.name}}</li>
|
||||||
|
{{component resultType.componentName results=resultType.results term=searchService.term}}
|
||||||
|
</ul>
|
||||||
|
<div class="no-results">
|
||||||
|
{{#if resultType.moreUrl}}
|
||||||
|
<a href={{resultType.moreUrl}} class="filter">{{i18n "show_more"}} {{fa-icon "chevron-down"}}</a>
|
||||||
|
{{/if}}
|
||||||
|
{{#if resultType.more}}
|
||||||
|
<a href class="filter filter-type" {{action "moreOfType" resultType.type bubbles=false}}>{{i18n "show_more"}} {{fa-icon "chevron-down"}}</a>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/menu-panel}}
|
|
@ -1,4 +1,5 @@
|
||||||
{{hamburger-menu hamburgerVisible=hamburgerVisible showKeyboardAction="showKeyboardShortcutsHelp"}}
|
{{hamburger-menu hamburgerVisible=hamburgerVisible showKeyboardAction="showKeyboardShortcutsHelp"}}
|
||||||
|
{{search-menu searchVisible=searchVisible}}
|
||||||
|
|
||||||
<div class='wrap'>
|
<div class='wrap'>
|
||||||
<div class='contents clearfix'>
|
<div class='contents clearfix'>
|
||||||
|
@ -28,17 +29,15 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li>
|
<li class="search-dropdown">
|
||||||
{{#if loginRequired}}
|
{{#if loginRequired}}
|
||||||
<a id='search-button' class='icon expand' href='#' aria-hidden="true" {{action "showLogin"}}>
|
<a id='search-button' class='icon expand' href aria-hidden="true" {{action "showLogin"}}>
|
||||||
{{fa-icon "search"}}
|
{{fa-icon "search"}}
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a id='search-button'
|
<a {{action "toggleSearchMenu"}} class='icon' href
|
||||||
class='icon expand'
|
id='search-button'
|
||||||
href='#'
|
title={{i18n 'search.title'}}>
|
||||||
data-dropdown="search-dropdown"
|
|
||||||
title='{{i18n 'search.title'}}'>
|
|
||||||
{{fa-icon "search" label="search.title"}}
|
{{fa-icon "search" label="search.title"}}
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -81,7 +80,6 @@
|
||||||
|
|
||||||
{{#if view.renderDropdowns}}
|
{{#if view.renderDropdowns}}
|
||||||
{{plugin-outlet "header-before-dropdowns"}}
|
{{plugin-outlet "header-before-dropdowns"}}
|
||||||
{{render "search"}}
|
|
||||||
{{render "notifications" notifications}}
|
{{render "notifications" notifications}}
|
||||||
{{render "user-dropdown"}}
|
{{render "user-dropdown"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
{{plugin-outlet "above-search"}}
|
|
||||||
{{search-text-field value=term searchContextEnabled=searchContextEnabled id="search-term"}}
|
|
||||||
|
|
||||||
<div class="search-context">
|
|
||||||
{{#if searchContext}}
|
|
||||||
<label>
|
|
||||||
{{input type="checkbox" name="searchContext" checked=searchContextEnabled}} {{searchContextDescription}}
|
|
||||||
</label>
|
|
||||||
{{/if}}
|
|
||||||
<a href class="show-help" {{action "showSearchHelp" bubbles=false}}>{{i18n "show_help"}}</a>
|
|
||||||
</div>
|
|
||||||
{{#if loading}}
|
|
||||||
<div class="searching">{{loading-spinner}}</div>
|
|
||||||
{{else}}
|
|
||||||
<div class="results">
|
|
||||||
{{#if noResults}}
|
|
||||||
<div class="no-results">
|
|
||||||
{{i18n "search.no_results"}}
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
{{#each content.resultTypes as |resultType|}}
|
|
||||||
<ul>
|
|
||||||
<li class="heading row">{{resultType.name}}</li>
|
|
||||||
{{component resultType.componentName results=resultType.results term=term}}
|
|
||||||
</ul>
|
|
||||||
<div class="no-results">
|
|
||||||
{{#if resultType.moreUrl}}
|
|
||||||
<a href="{{resultType.moreUrl}}" class="filter">{{i18n "show_more"}} {{fa-icon "chevron-down"}}</a>
|
|
||||||
{{/if}}
|
|
||||||
{{#if resultType.more}}
|
|
||||||
<a href class="filter filter-type" {{action "moreOfType" resultType.type bubbles=false}}>{{i18n "show_more"}} {{fa-icon "chevron-down"}}</a>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
|
@ -314,7 +314,7 @@ const PostView = Discourse.GroupedView.extend(Ember.Evented, {
|
||||||
}.on('willInsertElement'),
|
}.on('willInsertElement'),
|
||||||
|
|
||||||
_applySearchHighlight: function() {
|
_applySearchHighlight: function() {
|
||||||
const highlight = this.get('controller.searchHighlight');
|
const highlight = this.get('searchService.highlightTerm');
|
||||||
const cooked = this.$('.cooked');
|
const cooked = this.$('.cooked');
|
||||||
|
|
||||||
if (!cooked) { return; }
|
if (!cooked) { return; }
|
||||||
|
@ -330,7 +330,7 @@ const PostView = Discourse.GroupedView.extend(Ember.Evented, {
|
||||||
cooked.unhighlight();
|
cooked.unhighlight();
|
||||||
this._highlighted = false;
|
this._highlighted = false;
|
||||||
}
|
}
|
||||||
}.observes('controller.searchHighlight', 'cooked')
|
}.observes('searchService.highlightTerm', 'cooked')
|
||||||
});
|
});
|
||||||
|
|
||||||
export default PostView;
|
export default PostView;
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
export default Ember.View.extend({
|
|
||||||
tagName: 'div',
|
|
||||||
classNames: ['d-dropdown'],
|
|
||||||
elementId: 'search-dropdown',
|
|
||||||
templateName: 'search',
|
|
||||||
keyDown: function(e){
|
|
||||||
var term = this.get('controller.term');
|
|
||||||
if (e.which === 13 && term && term.length >= Discourse.SiteSettings.min_search_term_length) {
|
|
||||||
this.get('controller').send('fullSearch');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -104,3 +104,4 @@
|
||||||
//= require_tree ./discourse/routes
|
//= require_tree ./discourse/routes
|
||||||
//= require_tree ./discourse/pre-initializers
|
//= require_tree ./discourse/pre-initializers
|
||||||
//= require_tree ./discourse/initializers
|
//= require_tree ./discourse/initializers
|
||||||
|
//= require_tree ./discourse/services
|
||||||
|
|
|
@ -154,27 +154,6 @@
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
li:not(.category):not(.heading) {
|
|
||||||
font-size: 0.929em;
|
|
||||||
line-height: 16px;
|
|
||||||
|
|
||||||
.fa {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
display: block;
|
|
||||||
padding: 5px;
|
|
||||||
transition: all linear .15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover a:not(.badge-notification) {
|
|
||||||
background-color: dark-light-diff($highlight, $secondary, 50%, -55%);
|
|
||||||
}
|
|
||||||
|
|
||||||
button {margin-left: 5px;}
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading a:hover {
|
.heading a:hover {
|
||||||
background-color: dark-light-diff($highlight, $secondary, 50%, -55%);
|
background-color: dark-light-diff($highlight, $secondary, 50%, -55%);
|
||||||
}
|
}
|
||||||
|
@ -229,47 +208,6 @@
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
|
|
||||||
&#search-dropdown {
|
|
||||||
.heading {
|
|
||||||
padding: 5px 0 5px 5px;
|
|
||||||
.filter {
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='text'] {
|
|
||||||
width: 518px;
|
|
||||||
height: 22px;
|
|
||||||
margin: 5px;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-context {
|
|
||||||
padding: 0 5px;
|
|
||||||
label { margin-bottom: 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.searching {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
.spinner {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border-width: 2px;
|
|
||||||
margin: 20px 0 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// I am ghetto using this to display "Show More".. be warned
|
|
||||||
.no-results {
|
|
||||||
padding: 5px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.filter {
|
|
||||||
padding: 0;
|
|
||||||
&:hover {background: transparent;}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Categories
|
// Categories
|
||||||
|
|
||||||
|
@ -286,24 +224,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-link {
|
|
||||||
.badge-category-parent {
|
|
||||||
line-height: 0.8em;
|
|
||||||
}
|
|
||||||
.topic-title {
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topic-statuses {
|
|
||||||
float: none;
|
|
||||||
display: inline-block;
|
|
||||||
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
|
||||||
margin: 0;
|
|
||||||
.fa {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight-strong {
|
.highlight-strong {
|
||||||
background-color: dark-light-diff($highlight, $secondary, 40%, -45%);
|
background-color: dark-light-diff($highlight, $secondary, 40%, -45%);
|
||||||
|
@ -313,27 +233,6 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-context .show-help {
|
|
||||||
position: absolute;
|
|
||||||
right: 10px;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-context {
|
|
||||||
min-height: 30px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d-dropdown#search-dropdown {
|
|
||||||
max-height: none;
|
|
||||||
overflow: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
#search-dropdown .results {
|
|
||||||
max-height: 300px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#search-help table td {
|
#search-help table td {
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 40px;
|
top: 40px;
|
||||||
bottom: 37px;
|
bottom: 37px;
|
||||||
|
width: 96%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hamburger-panel {
|
.hamburger-panel {
|
||||||
|
@ -83,3 +85,89 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-menu {
|
||||||
|
|
||||||
|
.search-context .show-help {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
padding: 5px 0 5px 5px;
|
||||||
|
.filter {
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='text'] {
|
||||||
|
width: 93%;
|
||||||
|
height: 22px;
|
||||||
|
margin: 5px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-context {
|
||||||
|
padding: 0 5px;
|
||||||
|
label { margin-bottom: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.searching {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.1em;
|
||||||
|
right: 1.25em;
|
||||||
|
.spinner {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-width: 2px;
|
||||||
|
margin: 20px 0 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// I am ghetto using this to display "Show More".. be warned
|
||||||
|
.no-results {
|
||||||
|
padding: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.filter {
|
||||||
|
padding: 0;
|
||||||
|
&:hover {background: transparent;}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-link {
|
||||||
|
.badge-category-parent {
|
||||||
|
line-height: 0.8em;
|
||||||
|
}
|
||||||
|
.topic-title {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic-statuses {
|
||||||
|
float: none;
|
||||||
|
display: inline-block;
|
||||||
|
color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));
|
||||||
|
margin: 0;
|
||||||
|
.fa {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li:not(.category):not(.heading) {
|
||||||
|
font-size: 0.929em;
|
||||||
|
line-height: 16px;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
padding: 5px;
|
||||||
|
transition: all linear .15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover a:not(.badge-notification) {
|
||||||
|
background-color: dark-light-diff($highlight, $secondary, 50%, -55%);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {margin-left: 5px;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,15 +23,8 @@ test("header", () => {
|
||||||
// Search
|
// Search
|
||||||
click("#search-button");
|
click("#search-button");
|
||||||
andThen(() => {
|
andThen(() => {
|
||||||
ok(exists("#search-dropdown:visible"), "after clicking a button search box opens");
|
ok(exists(".search-menu:visible"), "after clicking a button search box opens");
|
||||||
not(exists("#search-dropdown .heading"), "initially, immediately after opening, search box is empty");
|
not(exists(".search-menu .heading"), "initially, immediately after opening, search box is empty");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Perform Search
|
|
||||||
// TODO how do I fix the fixture to be a POST instead of a GET @eviltrout
|
|
||||||
// fillIn("#search-term", "hello");
|
|
||||||
// andThen(() => {
|
|
||||||
// ok(exists("#search-dropdown .heading"), "when user completes a search, search box shows search results");
|
|
||||||
// equal(find("#search-dropdown .results a:first").attr("href"), "/t/hello-bar-integration-issues/17638", "there is a search result");
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,11 +8,11 @@ test("search", (assert) => {
|
||||||
|
|
||||||
andThen(() => {
|
andThen(() => {
|
||||||
assert.ok(exists('#search-term'), 'it shows the search bar');
|
assert.ok(exists('#search-term'), 'it shows the search bar');
|
||||||
assert.ok(!exists('#search-dropdown .results ul li'), 'no results by default');
|
assert.ok(!exists('.search-menu .results ul li'), 'no results by default');
|
||||||
});
|
});
|
||||||
|
|
||||||
fillIn('#search-term', 'dev');
|
fillIn('#search-term', 'dev');
|
||||||
andThen(() => {
|
andThen(() => {
|
||||||
assert.ok(exists('#search-dropdown .results ul li'), 'it shows results');
|
assert.ok(exists('.search-menu .results ul li'), 'it shows results');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue