More control over the returned matches
ghingis opened this issue · comments
There was already a ticket with something similar: #286
While a permanent element
solution with a bit of tinkering could be used as a workaround in our case, our problem is a bit more complex though.
We would like to dynamically extend the action list based on what is in the search.
Similarly, when you want to search for a sender that has a specific sting in it in Gmail, you can write:from:string
Likewise in Discord if you search for users, you can write: user: string
While I can register actions when the search starts with/contains our token, afterward, the Fuse match will not give a result since the query string has that extra part.
So we had an idea, we could extend the kbar config with an optional matcher function, that would run before the fuse.search
.
In this matcher function, the devs would get the action implementations and the search string as a param and they could return the matches
that they would like to see in the results (like the elements that have the keywords *
in them) and a search
that can be a string
, a Fuse.Expression
(so we could use Fuses' logical query operations) or a specific Symbol that skips the original matcher resulting that the returned matches
will be the result of the whole search.
With a simple useEffect
that reacts to the queryValue
change, registering the users as actions and a matcher config like this, you could inject your user list into the actions and navigate to a specific user profile from kbar.
matcher: (actions, search): ReturnType<Matcher> => {
search = search.trim();
if ( search === '' ) {
return { matches: [], search };
}
if ( search.toLowerCase().startsWith("user:") ) {
search = search.replace(/user:/i, "");
const searchExpression: Fuse.Expression = search.trim() === '' ?
{ keywords: "user" } :
{ $and: [ { $or: [ { name: search }, { subtitle: search } ] }, { keywords: "user" } ] };
return { matches: [], search: searchExpression };
}
return { matches: [], search };
}
Or you can have permanent actions
matcher: (actions, search): ReturnType<Matcher> => {
const matches = [];
actions.forEach((action)=> {
if ( action.keywords === '*' ) {
matches.push({ action, score: 1 });
}
});
return { matches: [], search };
}
Or you can have the option to give no result for specific search words
import { SKIP_ORIGINAL_MATCHER } from "kbar";
//...
matcher: (actions, search): ReturnType<Matcher> => {
const matches = [];
if (search.includes('bad_word')){
return { matches: [], search: SKIP_ORIGINAL_MATCHER };
}
return { matches: [], search }
}
Hey! This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.