DEV: Plugin outlets / extension points in search-menu (#21642)

This commit is contained in:
Mark VanLandingham 2023-06-01 08:33:14 -05:00 committed by GitHub
parent bb21476f68
commit c3a734380e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 123 additions and 6 deletions

View File

@ -95,9 +95,11 @@ import { replaceTagRenderer } from "discourse/lib/render-tag";
import { registerCustomLastUnreadUrlCallback } from "discourse/models/topic";
import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
import { addSearchResultsCallback } from "discourse/lib/search";
import { addOnKeyDownCallback } from "discourse/widgets/search-menu";
import {
addQuickSearchRandomTip,
addSearchSuggestion,
removeDefaultQuickSearchRandomTips,
} from "discourse/widgets/search-menu-results";
import { CUSTOM_USER_SEARCH_OPTIONS } from "select-kit/components/user-chooser";
import { downloadCalendar } from "discourse/lib/download-calendar";
@ -1707,6 +1709,25 @@ class PluginApi {
downloadCalendar(title, dates);
}
/**
* Add a function to be called when there is a keyDown even on the search-menu widget.
* This function runs before the default logic, and if one callback returns a falsey value
* the logic chain will stop, to prevent the core behavior from occuring.
*
* Example usage:
* ```
* api.addSearchMenuOnKeyDownCallback((searchMenu, event) => {
* if (searchMenu.term === "stop") {
* return false;
* }
* });
* ```
*
*/
addSearchMenuOnKeyDownCallback(fn) {
addOnKeyDownCallback(fn);
}
/**
* Add a quick search tip shown randomly when the search dropdown is invoked on desktop.
*
@ -1726,6 +1747,19 @@ class PluginApi {
addQuickSearchRandomTip(tip);
}
/**
* Remove the default quick search tips shown randomly when the search dropdown is invoked on desktop.
*
* Usage:
* ```
* api.removeDefaultQuickSearchRandomTips();
* ```
*
*/
removeDefaultQuickSearchRandomTips(tip) {
removeDefaultQuickSearchRandomTips(tip);
}
/**
* Add custom user search options.
* It is heavily correlated with `register_groups_callback_for_users_search_controller_action` which allows defining custom filter.

View File

@ -8,12 +8,14 @@ import { dateNode } from "discourse/helpers/node";
import { emojiUnescape } from "discourse/lib/text";
import getURL from "discourse-common/lib/get-url";
import { h } from "virtual-dom";
import hbs from "discourse/widgets/hbs-compiler";
import widgetHbs from "discourse/widgets/hbs-compiler";
import highlightSearch from "discourse/lib/highlight-search";
import { iconNode } from "discourse-common/lib/icon-library";
import renderTag from "discourse/lib/render-tag";
import { MODIFIER_REGEXP } from "discourse/widgets/search-menu";
import User from "discourse/models/user";
import { hbs } from "ember-cli-htmlbars";
import RenderGlimmer from "discourse/widgets/render-glimmer";
const suggestionShortcuts = [
"in:title",
@ -73,6 +75,10 @@ export function addQuickSearchRandomTip(tip) {
}
}
export function removeDefaultQuickSearchRandomTips() {
QUICK_TIPS = QUICK_TIPS.filter((tip) => !DEFAULT_QUICK_TIPS.includes(tip));
}
export function resetQuickSearchRandomTips() {
QUICK_TIPS = [].concat(DEFAULT_QUICK_TIPS);
}
@ -284,7 +290,14 @@ createWidget("search-menu-results", {
tagName: "div.results",
html(attrs) {
const { term, suggestionKeyword, results, searchTopics } = attrs;
const {
term,
suggestionKeyword,
inTopicContext,
results,
searchTopics,
onLinkClicked,
} = attrs;
if (suggestionKeyword) {
return this.attach("search-menu-assistant", {
@ -385,9 +398,41 @@ createWidget("search-menu-results", {
}
}
content.push(
new RenderGlimmer(
this,
"div",
hbs`<PluginOutlet @name="search-menu-results-top" @outletArgs={{@data.outletArgs}}/>`,
{
outletArgs: {
searchTerm: term,
inTopicContext,
onLinkClicked,
searchTopics,
},
}
)
);
content.push(categoriesAndTags);
content.push(usersAndGroups);
content.push(
new RenderGlimmer(
this,
"div",
hbs`<PluginOutlet @name="search-menu-results-bottom" @outletArgs={{@data.outletArgs}}/>`,
{
outletArgs: {
searchTerm: term,
inTopicContext,
onLinkClicked,
searchTopics,
},
}
)
);
return content;
},
});
@ -778,6 +823,7 @@ createWidget("search-menu-assistant-item", {
value: this.attrs.slug,
searchTopics: true,
setTopicContext: this.attrs.setTopicContext,
origin: this.attrs.origin,
});
e.preventDefault();
return false;
@ -790,10 +836,16 @@ createWidget("random-quick-tip", {
buildKey: () => "random-quick-tip",
defaultState() {
return QUICK_TIPS[Math.floor(Math.random() * QUICK_TIPS.length)];
return QUICK_TIPS.length
? QUICK_TIPS[Math.floor(Math.random() * QUICK_TIPS.length)]
: {};
},
html(attrs, state) {
if (!Object.keys(state).length) {
return;
}
return [
h(
`span.tip-label${state.clickable ? ".tip-clickable" : ""}`,
@ -819,7 +871,7 @@ createWidget("random-quick-tip", {
createWidget("search-menu-recent-searches", {
tagName: "div.search-menu-recent",
template: hbs`
template: widgetHbs`
<div class="heading">
<h4>{{i18n "search.recent"}}</h4>
{{flat-button
@ -833,7 +885,7 @@ createWidget("search-menu-recent-searches", {
{{#each this.currentUser.recent_searches as |slug|}}
{{attach
widget="search-menu-assistant-item"
attrs=(hash slug=slug icon="history")
attrs=(hash slug=slug icon="history" origin="recent-search")
}}
{{/each}}
`,

View File

@ -27,6 +27,15 @@ export const DEFAULT_TYPE_FILTER = "exclude_topics";
const searchData = {};
const onKeyDownCallbacks = [];
export function addOnKeyDownCallback(fn) {
onKeyDownCallbacks.push(fn);
}
export function resetOnKeyDownCallbacks() {
onKeyDownCallbacks.clear();
}
export function initSearchData() {
searchData.loading = false;
searchData.results = {};
@ -322,6 +331,8 @@ export default createWidget("search-menu", {
suggestionResults: searchData.suggestionResults,
searchTopics: SearchHelper.includesTopics(),
inPMInboxContext: this.state.inPMInboxContext,
inTopicContext: this.state.inTopicContext,
onLinkClicked: this.onLinkClicked.bind(this),
})
);
}
@ -348,6 +359,10 @@ export default createWidget("search-menu", {
});
},
onLinkClicked() {
return this.sendWidgetAction("linkClickedEvent");
},
mouseDown(e) {
if (e.target === document.querySelector("input#search-term")) {
this.state.inputSelectionEvent = true;
@ -371,6 +386,18 @@ export default createWidget("search-menu", {
},
keyDown(e) {
if (
onKeyDownCallbacks.length &&
!onKeyDownCallbacks.some((fn) => fn(this, e))
) {
// Return early if any callbacks return false
return;
}
this.handleKeyDown(e);
},
handleKeyDown(e) {
if (e.key === "Escape") {
this.sendWidgetAction("toggleSearchMenu");
document.querySelector("#search-button").focus();

View File

@ -26,7 +26,10 @@ import { _clearSnapshots } from "select-kit/components/composer-actions";
import { clearHTMLCache } from "discourse/helpers/custom-html";
import deprecated from "discourse-common/lib/deprecated";
import { restoreBaseUri } from "discourse-common/lib/get-url";
import { initSearchData } from "discourse/widgets/search-menu";
import {
initSearchData,
resetOnKeyDownCallbacks,
} from "discourse/widgets/search-menu";
import { resetPostMenuExtraButtons } from "discourse/widgets/post-menu";
import { isEmpty } from "@ember/utils";
import { resetCustomPostMessageCallbacks } from "discourse/controllers/topic";
@ -213,6 +216,7 @@ export function testCleanup(container, app) {
resetSidebarSection();
resetNotificationTypeRenderers();
clearExtraHeaderIcons();
resetOnKeyDownCallbacks();
resetUserMenuTabs();
resetLinkLookup();
resetModelTransformers();