DEV: Support search menu APIs in glimmer version (#24061)

This commit is contained in:
Mark VanLandingham 2023-11-17 11:17:45 -06:00 committed by GitHub
parent 2b7ecee06e
commit 1e0de818e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 162 additions and 86 deletions

View File

@ -18,6 +18,11 @@
/>
{{/if}}
<PluginOutlet
@name="search-menu-before-term-input"
@outletArgs={{hash openSearchMenu=this.open}}
/>
<SearchMenu::SearchTerm
@searchTermChanged={{this.searchTermChanged}}
@typeFilter={{this.typeFilter}}

View File

@ -79,6 +79,10 @@ export default class SearchMenu extends Component {
classes.push("menu-panel-results");
}
if (this.loading) {
classes.push("loading");
}
return classes.join(" ");
}
@ -266,6 +270,7 @@ export default class SearchMenu extends Component {
this.loading = false;
this.invalidTerm = true;
} else {
this.loading = true;
this.invalidTerm = false;
this._activeSearch = searchForTerm(this.search.activeGlobalSearchTerm, {

View File

@ -1 +1,3 @@
{{html-safe @string}}
<span {{did-insert this.highlight}}>
{{html-safe @string}}
</span>

View File

@ -1,15 +1,13 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import highlightSearch from "discourse/lib/highlight-search";
export default class HighlightedSearch extends Component {
@service search;
constructor() {
super(...arguments);
const span = document.createElement("span");
span.textContent = this.args.string;
highlightSearch(span, this.search.activeGlobalSearchTerm);
@action
highlight(element) {
highlightSearch(element, this.search.activeGlobalSearchTerm);
}
}

View File

@ -19,38 +19,55 @@
@searchTermChanged={{@searchTermChanged}}
/>
{{else}}
{{#if @searchTopics}}
{{#if (and (not @searchTopics) (not @inPMInboxContext))}}
{{! render the first couple suggestions before a search has been performed}}
<SearchMenu::Results::InitialOptions
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
/>
{{/if}}
<PluginOutlet
@name="search-menu-results-top"
@outletArgs={{hash closeSearchMenu=@closeSearchMenu}}
/>
{{#if (and @searchTopics this.resultTypesWithComponent)}}
{{! render results after a search has been performed }}
{{#if this.resultTypesWithComponent}}
<SearchMenu::Results::Types
@resultTypes={{this.resultTypesWithComponent}}
@topicResultsOnly={{true}}
@closeSearchMenu={{@closeSearchMenu}}
/>
<SearchMenu::Results::MoreLink
@updateTypeFilter={{@updateTypeFilter}}
@triggerSearch={{@triggerSearch}}
@resultTypes={{this.resultTypesWithComponent}}
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
/>
{{/if}}
{{else}}
{{#unless @inPMInboxContext}}
{{! render the first couple suggestions before a search has been performed}}
<SearchMenu::Results::InitialOptions
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
/>
{{#if this.resultTypesWithComponent}}
<SearchMenu::Results::Types
@resultTypes={{this.resultTypesWithComponent}}
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
/>
{{/if}}
{{/unless}}
<SearchMenu::Results::Types
@resultTypes={{this.resultTypesWithComponent}}
@topicResultsOnly={{true}}
@closeSearchMenu={{@closeSearchMenu}}
/>
<SearchMenu::Results::MoreLink
@updateTypeFilter={{@updateTypeFilter}}
@triggerSearch={{@triggerSearch}}
@resultTypes={{this.resultTypesWithComponent}}
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
/>
{{else if
(and
(not @inPMInboxContext)
(not @searchTopics)
this.resultTypesWithComponent
)
}}
<SearchMenu::Results::Types
@resultTypes={{this.resultTypesWithComponent}}
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
/>
{{/if}}
{{/if}}
<PluginOutlet
@name="search-menu-results-bottom"
@outletArgs={{hash
inTopicContext=this.search.inTopicContext
searchTermChanged=@searchTermChanged
searchTopics=@searchTopics
closeSearchMenu=@closeSearchMenu
}}
/>
</div>
{{/if}}

View File

@ -1,10 +1,10 @@
<span class="blurb">
{{format-age @result.created_at}}
<span> - </span>
<span class="blurb__separator"> - </span>
{{#if this.siteSettings.use_pg_headlines_for_excerpt}}
<span>{{@result.blurb}}</span>
{{else}}
<span>
<span class="blurb__text">
<SearchMenu::HighlightedSearch @string={{@result.blurb}} />
</span>
{{/if}}

View File

@ -1,45 +1,61 @@
<ul class="search-menu-initial-options">
{{#if this.termMatchesContextTypeKeyword}}
<SearchMenu::Results::AssistantItem
@slug={{this.slug}}
@extraHint={{true}}
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
@suggestionKeyword={{this.contextTypeKeyword}}
/>
{{else}}
{{#if (or this.search.activeGlobalSearchTerm this.search.searchContext)}}
{{#if this.search.activeGlobalSearchTerm}}
<SearchMenu::Results::AssistantItem
@suffix={{i18n "search.in_topics_posts"}}
@closeSearchMenu={{@closeSearchMenu}}
@searchAllTopics={{true}}
@extraHint={{true}}
@searchTermChanged={{@searchTermChanged}}
@suggestionKeyword={{this.contextTypeKeyword}}
/>
{{/if}}
{{#if this.search.searchContext}}
<this.contextTypeComponent
@slug={{this.slug}}
@suggestionKeyword={{this.contextTypeKeyword}}
@results={{this.initialResults}}
@withInLabel={{this.withInLabel}}
@suffix={{this.suffix}}
@label={{this.label}}
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
/>
{{/if}}
<PluginOutlet
@name="search-menu-initial-options"
@outletArgs={{hash
termMatchesContextTypeKeyword=this.termMatchesContextTypeKeyword
contextTypeComponent=this.contextTypeComponent
slug=this.slug
suggestionKeyword=this.contextTypeKeyword
results=this.initialResults
withInLabel=this.withInLabel
suffix=this.suffix
label=this.label
closeSearchMenu=@closeSearchMenu
searchTermChanged=@searchTermChanged
}}
>
{{#if this.termMatchesContextTypeKeyword}}
<SearchMenu::Results::AssistantItem
@slug={{this.slug}}
@extraHint={{true}}
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
@suggestionKeyword={{this.contextTypeKeyword}}
/>
{{else}}
<SearchMenu::Results::RandomQuickTip />
{{#if (and this.currentUser this.siteSettings.log_search_queries)}}
<SearchMenu::Results::RecentSearches
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
/>
{{#if (or this.search.activeGlobalSearchTerm this.search.searchContext)}}
{{#if this.search.activeGlobalSearchTerm}}
<SearchMenu::Results::AssistantItem
@suffix={{i18n "search.in_topics_posts"}}
@closeSearchMenu={{@closeSearchMenu}}
@searchAllTopics={{true}}
@extraHint={{true}}
@searchTermChanged={{@searchTermChanged}}
@suggestionKeyword={{this.contextTypeKeyword}}
/>
{{/if}}
{{#if this.search.searchContext}}
<this.contextTypeComponent
@slug={{this.slug}}
@suggestionKeyword={{this.contextTypeKeyword}}
@results={{this.initialResults}}
@withInLabel={{this.withInLabel}}
@suffix={{this.suffix}}
@label={{this.label}}
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
/>
{{/if}}
{{else}}
<SearchMenu::Results::RandomQuickTip />
{{#if (and this.currentUser this.siteSettings.log_search_queries)}}
<SearchMenu::Results::RecentSearches
@closeSearchMenu={{@closeSearchMenu}}
@searchTermChanged={{@searchTermChanged}}
/>
{{/if}}
{{/if}}
{{/if}}
{{/if}}
</PluginOutlet>
</ul>

View File

@ -34,9 +34,7 @@ export default class InitialOptions extends Component {
}
get termMatchesContextTypeKeyword() {
return this.search.activeGlobalSearchTerm?.match(MODIFIER_REGEXP)
? true
: false;
return this.search.activeGlobalSearchTerm?.match(MODIFIER_REGEXP);
}
setAttributesForSearchContextType(type) {

View File

@ -47,6 +47,10 @@ export function resetQuickSearchRandomTips() {
QUICK_TIPS = [].concat(DEFAULT_QUICK_TIPS);
}
export function removeDefaultQuickSearchRandomTips() {
QUICK_TIPS = QUICK_TIPS.filter((tip) => !DEFAULT_QUICK_TIPS.includes(tip));
}
resetQuickSearchRandomTips();
export default class RandomQuickTip extends Component {

View File

@ -1,5 +1,9 @@
{{#each this.filteredResultTypes as |resultType|}}
<div class={{resultType.componentName}}>
<PluginOutlet
@name="search-menu-results-type-top"
@outletArgs={{hash resultType=resultType}}
/>
<ul
class="list"
aria-label={{concat (i18n "search.results") " " resultType.type}}

View File

@ -10,6 +10,15 @@ import { isiPad } from "discourse/lib/utilities";
const SECOND_ENTER_MAX_DELAY = 15000;
const onKeyUpCallbacks = [];
export function addOnKeyUpCallback(fn) {
onKeyUpCallbacks.push(fn);
}
export function resetOnKeyUpCallbacks() {
onKeyUpCallbacks.clear();
}
export default class SearchTerm extends Component {
@service search;
@service appEvents;
@ -42,6 +51,14 @@ export default class SearchTerm extends Component {
@action
onKeyup(e) {
if (
onKeyUpCallbacks.length &&
!onKeyUpCallbacks.some((fn) => fn(this, e))
) {
// Return early if any callbacks return false
return;
}
if (e.key === "Escape") {
this.args.closeSearchMenu();
e.preventDefault();

View File

@ -18,6 +18,11 @@ import {
} from "discourse/components/reviewable-item";
import { addAdvancedSearchOptions } from "discourse/components/search-advanced-options";
import { addSearchSuggestion as addGlimmerSearchSuggestion } from "discourse/components/search-menu/results/assistant";
import {
addQuickSearchRandomTip,
removeDefaultQuickSearchRandomTips,
} from "discourse/components/search-menu/results/random-quick-tip";
import { addOnKeyUpCallback } from "discourse/components/search-menu/search-term";
import { REFRESH_COUNTS_APP_EVENT_NAME as REFRESH_USER_SIDEBAR_CATEGORIES_SECTION_COUNTS_APP_EVENT_NAME } from "discourse/components/sidebar/user/categories-section";
import { addTopicTitleDecorator } from "discourse/components/topic-title";
import { addUserMenuProfileTabItem } from "discourse/components/user-menu/profile-tab-content";
@ -107,9 +112,9 @@ import {
import { disableNameSuppression } from "discourse/widgets/poster-name";
import { addOnKeyDownCallback } from "discourse/widgets/search-menu";
import {
addQuickSearchRandomTip,
addQuickSearchRandomTip as addWidgetQuickSearchRandomTip,
addSearchSuggestion,
removeDefaultQuickSearchRandomTips,
removeDefaultQuickSearchRandomTips as removeWidgetDefaultQuickSearchRandomTips,
} from "discourse/widgets/search-menu-results";
import { addTopicParticipantClassesCallback } from "discourse/widgets/topic-map";
import {
@ -1863,6 +1868,7 @@ class PluginApi {
*/
addSearchMenuOnKeyDownCallback(fn) {
addOnKeyDownCallback(fn);
addOnKeyUpCallback(fn);
}
/**
@ -1882,6 +1888,7 @@ class PluginApi {
*/
addQuickSearchRandomTip(tip) {
addQuickSearchRandomTip(tip);
addWidgetQuickSearchRandomTip(tip);
}
/**
@ -1893,8 +1900,9 @@ class PluginApi {
* ```
*
*/
removeDefaultQuickSearchRandomTips(tip) {
removeDefaultQuickSearchRandomTips(tip);
removeDefaultQuickSearchRandomTips() {
removeDefaultQuickSearchRandomTips();
removeWidgetDefaultQuickSearchRandomTips();
}
/**

View File

@ -21,6 +21,7 @@ import { clearToolbarCallbacks } from "discourse/components/d-editor";
import { clearBulkButtons } from "discourse/components/modal/topic-bulk-actions";
import { resetWidgetCleanCallbacks } from "discourse/components/mount-widget";
import { resetDecorators as resetPluginOutletDecorators } from "discourse/components/plugin-connector";
import { resetOnKeyUpCallbacks } from "discourse/components/search-menu/search-term";
import { resetTopicTitleDecorators } from "discourse/components/topic-title";
import { resetUserMenuProfileTabItems } from "discourse/components/user-menu/profile-tab-content";
import { resetCustomPostMessageCallbacks } from "discourse/controllers/topic";
@ -227,6 +228,7 @@ export function testCleanup(container, app) {
resetSidebarPanels();
clearExtraHeaderIcons();
resetOnKeyDownCallbacks();
resetOnKeyUpCallbacks();
resetUserMenuTabs();
resetLinkLookup();
resetModelTransformers();