timc1 / kbar Goto Github PK
View Code? Open in Web Editor NEWfast, portable, and extensible cmd+k interface for your site
Home Page: https://kbar.vercel.app
License: MIT License
fast, portable, and extensible cmd+k interface for your site
Home Page: https://kbar.vercel.app
License: MIT License
KBar currently takes a static data structure and handles all of the parsing internally. The current component structure is as follows:
KBar
KeyboardShortcuts
KBarAnimator
KBarSearch
KBar
handles the primary cmd+k/esc listeners, and coordinates when to show, animate, and hide the contentKeyboardShortcuts
attaches keydown listeners and matches the keystroke patterns with our actions objectKBarAnimator
leverages ResizeObserver
and the Web Animations API to coordinate the smooth transitions when the content updatesKBarSearch
handles the search and rendering of resultsSince all state management and rendering logic is within KBar
itself, it is currently impossible for users to customize their command menu to fit their brand.
Although we can theoretically expose props which enable users to pass certain configurations, this would lead us to quite a restrictive API.
Let's take a look at an approach:
Provide a way to hook into the internal state; e.g. visualState
, currentActionId
, actions
, and
a method to trigger internal state updates.
The component heirarchy would look something like this:
// app.tsx
<KBarProvider actions={actions}>
<Breadcrumbs /> // custom user component
<KBarAnimator>
<KBarSearch />
<KBarResults
//
onRender={(result) => (
<div>
// ...
</div>
)}
/>
</KBarAnimator>
</KBarProvider>
// <MyApp />
User defined components within the KBarProvider
can hook into the internal state through a useKBar
hook.
// breadcrumbs.tsx
function Breadcrumbs() {
const { actions, breadcrumbs } = useKBar((state) => ({
breadcrumbs: getBreadcrumbs(state)
}));
return (
<ul>
{breadcrumbs.map(breadcrumb => (
<li key={breadcrumb.id}>
<Button onClick={() => actions.setCurrentRootActionId(breadcrumb.id)}>{breadcrumb.name}</Button>
</li>
))}
</ul>
)
}
Getting multiple can't resolves like this
ERROR in ./example/src/index.tsx 2:0-35 Module not found: Error: Can't resolve 'react-dom' in '/.../kbar-main/example/src'
ERROR in ./example/src/index.tsx 4:0-59 Module not found: Error: Can't resolve 'react-router-dom' in '/.../kbar- main/example/src'
ERROR in ./src/InternalKeyboardEvents.tsx 1:0-31 Module not found: Error: Can't resolve 'react' in '/.../kbar-main/src'
@ ./src/KBarContextProvider.tsx 3:0-62 9:41-63
@ ./example/src/App.tsx 23:0-61 42:45-57
@ ./example/src/index.tsx 3:0-24 5:153-156
Do you know what might be happening?
hey tim! Awsome work with the Kbar, i've been using it in my preact app - in dev mode it works fine but after production build it doesn't work. consider me a noob trying to solve things.
it shows something like this in console
vendor.822cbe4a.js:29 Uncaught TypeError: (0 , Zt.default) is not a function
at Gf (vendor.822cbe4a.js:29)
at Y.qf [as constructor] (vendor.822cbe4a.js:29)
at Y.Ho [as render] (vendor.822cbe4a.js:1)
at dr (vendor.822cbe4a.js:1)
at ti (vendor.822cbe4a.js:1)
at dr (vendor.822cbe4a.js:1)
at ti (vendor.822cbe4a.js:1)
at dr (vendor.822cbe4a.js:1)
at ti (vendor.822cbe4a.js:1)
at dr (vendor.822cbe4a.js:1)
Gf @ vendor.822cbe4a.js:29
qf @ vendor.822cbe4a.js:29
Ho @ vendor.822cbe4a.js:1
dr @ vendor.822cbe4a.js:1
ti @ vendor.822cbe4a.js:1
dr @ vendor.822cbe4a.js:1
ti @ vendor.822cbe4a.js:1
dr @ vendor.822cbe4a.js:1
ti @ vendor.822cbe4a.js:1
dr @ vendor.822cbe4a.js:1
Ie @ vendor.822cbe4a.js:1
(anonymous) @ index.dc5496f6.js:formatted:113
in index.js (pretty-printed) it highlights this portion
y(i(r.KBarProvider, {
actions: S,
children: [o(r.KBarPortal, {
children: o(r.KBarPositioner, {
children: i(r.KBarAnimator, {
style: B,
children: [o(r.KBarSearch, {
style: k,
placeholder: "Type a command or search\u2026"
}), o(r.KBarResults, {})]
})
})
}), o(g, {
children: o(v, {})
})]
}), document.getElementById("app"));
it'd be really great if you can help me with this. thanks in advance
Currently actions must be registered up front – though this works well for static actions; e.g. navigating pages, triggering DOM events, it falls short of easily enabling users to generate actions on the fly.
Perhaps an API like so:
const { registerActions } = useKBar(() => {...});
React.useEffect(() => {
registerActions([
// ...
])
}, [])
Currently, styles can be applied via props; e.g. contentStyle
, backgroundStyle
for KBarContent
, and directly as style
for KBarSearch
. There should be a simpler way to customize styles which enable full flexibility for:
When using useRegisterActions
to dynamically add actions while components are mounted it's difficult to predict which order items will appear in, adding an optional priority
attribute to actions would put control of this in the developers hands while handling the boilerplate of sorting internally.
Hi @timc1 !
Really love this project, it's great.
I have been checking out the component in a pretty barebones project and noticed that storybook stops working with a pretty plain build config when I added this package.
Specifically I'm getting a bunch of loader issues like:
ERROR in ./node_modules/kbar/lib/useStore.js 104:12
Module parse failed: Unexpected token (104:12)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| exports.default = useStore;
| class Publisher {
> getState;
| subscribers = [];
| constructor(getState) {
@ ./node_modules/kbar/lib/KBarContextProvider.js 26:35-56
@ ./node_modules/kbar/lib/index.js
@ ./packages/common/src/ui/layout/tables/columns/CheckboxSelectionColumn.tsx
@ ./packages/common/src/ui/layout/tables/columns/index.ts
@ ./packages/common/src/ui/layout/tables/index.ts
@ ./packages/common/src/utils/testing.tsx
@ ./.storybook/preview.js
@ ./.storybook/preview.js-generated-config-entry.js
@ multi ./node_modules/@storybook/react/node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js ./node_modules/@storybook/core-client/dist/esm/globals/polyfills.js ./node_modules/@storybook/core-client/dist/esm/globals/globals.js ./.storybook/storybook-init-framework-entry.js ./node_modules/@storybook/addon-docs/dist/esm/frameworks/common/config.js-generated-other-entry.js ./node_modules/@storybook/addon-docs/dist/esm/frameworks/react/config.js-generated-other-entry.js ./node_modules/@storybook/addon-links/dist/esm/preset/addDecorator.js-generated-other-entry.js ./node_modules/@storybook/addon-actions/dist/esm/preset/addDecorator.js-generated-other-entry.js ./node_modules/@storybook/addon-actions/dist/esm/preset/addArgs.js-generated-other-entry.js ./node_modules/@storybook/addon-backgrounds/dist/esm/preset/addDecorator.js-generated-other-entry.js ./node_modules/@storybook/addon-backgrounds/dist/esm/preset/addParameter.js-generated-other-entry.js ./node_modules/@storybook/addon-measure/dist/esm/preset/preview.js-generated-other-entry.js ./node_modules/storybook-addon-outline/dist/esm/preset/addDecorator.js-generated-other-entry.js ./.storybook/preview.js-generated-config-entry.js ./.storybook/generated-stories-entry.js (webpack)-hot-middleware/client.js?reload=true&quiet=false&noInfo=undefined ./node_modules/@storybook/react/node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ErrorOverlayEntry.js
Everything works fine if I alter the tsconfig.json
in this project to be es5
rather than ESNext
and rebuild - was curious if making this change would be something you'd think about doing (Or, if you can recommend a different change I can make that is easier?)
I have a field on a form ( @mui Input, not that it matters, I think ) and when click on the empty field, Chrome helpfully gives me a list of values which have been entered previously. Clicking on one of them populates the field and raises an error with kbar:
TypeError
Cannot read properties of undefined (reading 'toLowerCase')
Call Stack
handleKeyDown
@openmsupply-client/host/../../node_modules/kbar/lib/InternalEvents.js:132:33
checking for null on the event.key
property works around it for me:
internalEvents.tsx:124
const key = event.key?.toLowerCase();
Shouldn't cause any issues setting key to null like this afaik - it just helps the early return a few lines later 🤷♂️
As discussed, currently you have to pass an options object in order to get animations but it would be nice to enable these by default given they are a library selling point.
Hi there! First of all, thanks for this :)
I it really necessary to wrap our app with your context provider?
From what I can tell, only KBar
components actually need to access that context. There are no other exports (like custom hooks) that we can use anywhere else in our app (that would require the provider) – and, honestly, I don't see a use-case where that would be necessary.
If I'm not completely mistaken, this would just require an update to the docs.
Probably easiest to reproduce by temporarily increasing the enter animation, but I have repeatedly done it on my installation.
cmd+k
on "0.1.0-beta.1", I was using this to blur my background when Kbar is visible.
<KBarContent
contentStyle={{
...
}}
backgroundStyle={{
backdropFilter: 'blur(16px)'
}}
>
How can i use this in "0.1.0-beta.4"
It would be great if I could select an icons that would display to the right of the action option like the picture above.
A simple solution would be to allow for an icon
key in the actions array that will take an icon component
comet BlogIcon = () => <AiNewspaperOutline />;
const actions = [
{
id: "blog",
name: "Blog",
shortcut: ["b"],
keywords: "writing words",
perform: () => (window.location.pathname = "blog"),
icon: BlogIcon
}
];
Just a little heads-up: The landing page at https://kbar.vercel.app/ doesn't actually contain the word "React" anywhere, this confused me a little when I saw the sample code with JSX syntax.
Hi 👋🏻
I discovered the project today and I am a big fan! The problem is that it uses React, and it's not a framework I use :/
So I was wondering if it would be better to make the project monorepo in order to add a lot of integrations, like @kbar/react
, @kbar/vue
.... and @kbar/core
which contains the vanilla part.
What do you think about it?
Hi, I'm trying kbar
in my app (created with create-react-app
where I use React 18 (18.0.0-alpha-cb8a50619-20210909
) and I get this error when starting the app:
Failed to compile.
./node_modules/kbar/lib/useStore.js 120:10
Module parse failed: Unexpected token (120:10)
File was processed with these loaders:
* ./node_modules/babel-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
|
| class Publisher {
> getState;
| subscribers = [];
|
In my App.js I have:
const actions = [
{
id: "blog",
name: "Blog",
shortcut: ["b"],
keywords: "writing words",
perform: () => (window.location.pathname = "blog"),
},
{
id: "contact",
name: "Contact",
shortcut: ["c"],
keywords: "email",
perform: () => (window.location.pathname = "contact"),
},
];
export default function App() {
return (
<KBarProvider actions={actions}>
<App />
</KBarProvider>
);
}
EDIT: Seem like the importing of anything from import { ... } from "kbar";
is crashing the app.
Stub for now, but I'd imagine users needing to temporarily disable kbar; e.g. when editing a rich text field.
Hi Tim! Nice to contribute with you again.
I've been exploring your solution with a Next.js with Tailwind + TypeScript and I see some improvements.
<KBarContent
accepts contentStyle
, but not contentClassname
, using Tailwind classes with style is not possible (at least as far as I know)KBarResults
component onRender
function uses three types action
, handlers
, state
, could you export them for creating custom components with this fully typed?KBarProvider
context provider .. options
prop is required, why not auto-configure some default animations? Also actions
prop is required, when they could be loaded dynamically.Tell me what do you think :)
Ideally if you have a shortcut on an action with no perform
attribute, but a children
attribute it should open the kbar with the correct active root action. eg,
Hello!
My config
https://monosnap.com/file/z1jWbzjMxXpYXwp0CSOrk3dLMHUFEn
On website, the only changes which happens after press cmd + k is overflow hidden added to html tag but not any dropdown is appearing
When adding children actions dynamically to existing parent actions, we need to update the parent action.children
list.
https://github.com/timc1/kbar/blob/main/src/useStore.tsx#L46-L58
For mobile devices the buttons can help toggle but going back the last step is not there.
eg. I clicked on change theme, but want to go back to main menu instead of changing it.
Currently, searching "dark" will only display the "Theme…" action. Clicking the "Theme…" action will then expose the "Light" and "Dark" actions.
It would be neat to displaying search results for nested actions by displaying a breadcrumb-like UI. Searching "dark" would then display a result like:
Theme… > Dark
Hey,
I really like this component, but macOS have less than 10% of market share.
It would be nice to add alternative shortcuts for Windows (and probably Linux).
CTRL + K works well, menu could be opened and closed, but the shortcuts like "h for home" are not working, just nothing happens.
We can probably remove the requirement to explicitly define parent
for an action and infer the the value based on the defined children
.
Check for role=textbox
We could have some actions immediately available and additional actions would be appended after a promise is resolved (or several promises for that matter). A dev defined loading indicator could optionally be displayed in the interim.
We should probably bind the Ctrl key for non Mac users. Temporary solution is to use Window + ctrl + k
When using the useRegisterActions
hook only the initial value is considered – PR incoming with fix.
Currently state is managed directly in KBarContextProvider
. This breaks our usage of the publisher/subscriber as changes directly within the provider re render children.
Instead, we can move the state management to a separate hook, e.g. useStore
, which would return the memoized state.
I love this concept, and I actually use a custom cmd+k
implementation for a project of mine. I'd love to swap that out with kbar, but my app does not use React. Do you have any plans to make a framework-agnostic (only typescript) version of kbar?
When the cursor stays hovered on top of, say the third result, keyboard navigating will jump between the first result and the third.
In the video below, we expect that each time the search query changes, the active result should be the first:
Similar to spotlight, it may be nice to be able to place our actions in different sections for more complex applications. For example lets say I wanted a "Navigation" section for all of my navigation items, then a "Contacts" section to search contacts, a "Posts" section to search posts, etc.
I am building an application that I'd like the user to be able to search the navigation, various commands, along with about 4 or 5 different entities at the same time in this universal search.
Most likely caused by scrollIntoView
:
Cool idea, but the implementation seems pretty lacking at the moment. I installed it, added it to my app, and received these errors:
honeybadger.js?e9d7:1057 ./node_modules/kbar/lib/KBarContent.js:26:0
Module not found: Can't resolve '@reach/portal'
Import trace for requested module:
./node_modules/kbar/lib/index.js
./components/GlobalProviders/index.tsx
./pages/_app.tsx
https://nextjs.org/docs/messages/module-not-found
eval @ honeybadger.js?e9d7:1057
r @ fs.js:3
handleErrors @ hot-dev-client.js?5345:120
processMessage @ hot-dev-client.js?5345:170
eval @ hot-dev-client.js?5345:32
eval @ eventsource.js?a343:38
handleMessage @ eventsource.js?a343:36
honeybadger.js?e9d7:1057 ./node_modules/kbar/lib/KBarResults.js:25:0
Module not found: Can't resolve 'match-sorter'
Import trace for requested module:
./node_modules/kbar/lib/index.js
./components/GlobalProviders/index.tsx
./pages/_app.tsx
https://nextjs.org/docs/messages/module-not-found
eval @ honeybadger.js?e9d7:1057
r @ fs.js:3
handleErrors @ hot-dev-client.js?5345:120
processMessage @ hot-dev-client.js?5345:170
eval @ hot-dev-client.js?5345:32
eval @ eventsource.js?a343:38
handleMessage @ eventsource.js?a343:36
honeybadger.js?e9d7:1057 ./node_modules/kbar/lib/useStore.js:22:0
Module not found: Can't resolve 'fast-equals'
Import trace for requested module:
./node_modules/kbar/lib/KBarContextProvider.js
./node_modules/kbar/lib/index.js
./components/GlobalProviders/index.tsx
./pages/_app.tsx
https://nextjs.org/docs/messages/module-not-found
We already have a portal, so I'm not keen to install reach portal, or any of the other reach components. But also, shouldn't this thing just handle its own dependencies?
Also, seems not to be TypeScript ready. I get this error from TS:
Property 'options' is missing in type '{ children: ReactNode; actions: { id: string; name: string; shortcut: string[]; keywords: string; perform: () => Promise<boolean>; }[]; }' but required in type 'KBarProviderProps'.ts(2741)
... though I've seen no indication that "options" should be a required property.
I hope this might be worth a second look later on, when it's more fully baked.
I have an application (dashboard.plaid.com), where we would like to display some search results if the query matches a certain regex. From what I can tell, this isn't currently supported but could be a useful feature!
It's possible (in the case of search for example) to have hundreds of results, currently all matching results are displayed even those outside of the scrollable area which slows down on large result sets.
Kbar should allow developers the option to provide more context to users by displaying a subtitle along with the name. In my specific use-case I'd like to expose my complex navigation to kbar. Because things are nested, it may be helpful to users to see a navigation path under the name.
This would look something like this.
Gizmo
Products▶
Electronics
To display the subtitle as shown above, we may do something like this in our actions array.
const actions = [
...
{
name: "Gizmo",
subtitle: "Products ▶ Electronics",
perform: () => window.location.pathname = "/products/electronics/gizmo"
},
...
];
If the search string contained words in subtitle
, kbar would return that given item.
@timc1 little hiccup on the ctrl+k
implementation. I think the command bar closes if k
is pressed? ctrl+j
works fine
Now that we have the new Results
API, it's probably good to get rid of the deprecated KBarResults
component completely.
Nice project! I recommend adding the keywords "command palette" in package.json
, README.md
and so on, because that's how most people refer to this kind of functionality.
It may be nice to allow developers to pass in their own custom keyboard shortcut to instantiate kbar if they didn't want to use the preconfigured cmd+k. Some apps for example use cmd+/. Others use cmd+shift+P.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.