• Stars
    star
    757
  • Rank 59,989 (Top 2 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 2 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

๐ŸŒˆ An animated and accessible command menu

image

kmenu

๐ŸŒˆ Animated and accessible cmdk interface

Demo ยท NPM ยท Product Hunt ยท StackBlitz ยท CodeSandbox

๐Ÿš€ Quickstart

Having trouble? Unsure of something? Feel free to ask away in the discussions.

Install the npm package:

yarn add kmenu

Using the Provider

After you install, you must wrap your application around the MenuProvider component. Wanna learn how you can customize your menu configuration? Check out the MenuConfig section.

Inside the MenuProvider, you can pass in the theme configuration which all the menus will use. All props are optional, and you can also pass in props if your commands or sections have extra margin between them. Here's a look:

Parameter Description Type Optional
config The config file passed onto the palette Config โœ…
dimensions The values of the height of the palettes (px) Dimension โœ…

Now, here's a look at the dimensions object:

Parameter Description Type Default Optional
commandHeight The height of each command in the palette (px) number 54 โœ…
sectionHeight The height of each category/section in the palette (px) number 31 โœ…

Below is an example:

import { MenuProvider, MenuConfig } from 'kmenu'

const App = () => {
  const config: MenuConfig = {
    /* ... */
  }

  return <MenuProvider config={config}>{/* ... */}</MenuProvider>
}

Adding commands

After you've installed the package, you can now begin adding commands onto the command menu.

The commands are broken up into two arrays. One array contains the different categories of the commands, and another array contains the commands itself. Here's a look at how you can define categories:

Parameter Description Type Optional
category The name of the category the command will be displayed in string โŒ
commands An array of commands passed onto the category Command โŒ

Awesome. Now here's a look at how you can create commands:

Parameter Description Type Optional
icon The icon displayed next to the command ReactElement โœ…
text The text displayed on the command String โŒ
perform The action to perform void โœ…
href The link to open void โœ…
newTab Whether or not the link should open in a new tab boolean โœ…
keywords Search keywords for the command string โœ…
shorcuts The keyboard shortcuts to activate this command Shortcut โœ…
closeOnComplete Whether the menu should close when the command is run (default: false) boolean โœ…

As you might notice, the commands give you the ability to define custom shortcuts.

Each shortcut can have two target keys and a modifier that would be used in conjunction with a single target key. Note that if you're using a modifier, you can only use a SINGLE target key. Here's a look at how you can create shortcuts:

Parameter Description Type Optional
modifier The modifier key used in conjunction with the target key enum (shift/alt/ctrl/meta) โœ…
keys The target keys for this command Tuple [string, string?] โŒ

After you've created all your commands, you must pass them into the useCommands hook, which returns a getter and a setter for the commands. For a reference, check out the section on the useCommands hook.

NOTE: THE SHORTCUTS PROPERTY IS PURELY COSMETIC AND HAS NO FUNCTIONALITY.

Now that you have an underlying idea of how commands work, here's an example of how to create the commands (using TypeScript):

import {
  Search,
  Copy,
  Globe,
  GitHub,
  AlertCircle,
  GitPullRequest,
  Zap,
  Edit2,
  Plus,
  Settings,
  Code,
  Command as Cmd,
  Terminal
} from 'react-feather'

const main: Command[] = [
  {
    category: 'Socials',
    commands: [
      {
        icon: <FiGlobe />,
        text: 'Website',
        href: 'https://hxrsh.in',
        newTab: true,
        keywords: 'home'
      },

      {
        icon: <FiTwitter />,
        text: 'Twitter',
        href: 'https://twitter.com/harshhhdev',
        newTab: true,
        shortcuts: { modifier: 'alt', keys: ['t'] }
      },
      {
        icon: <FiGithub />,
        text: 'GitHub',
        href: 'https://github.com/harshhhdev',
        newTab: true,
        shortcuts: { keys: ['g', 'h'] }
      },
      {
        text: 'Dribbble',
        href: 'https://dribbble.com/harshhhdev',
        newTab: true
      },
      {
        icon: <FiLinkedin />,
        text: 'Linkedin',
        href: 'https://linkedin.com/in/harshhhdev',
        newTab: true
      }
    ]
  }
]

const Component = () => {
  const [commands, setCommands] = useCommands(main)

  /* ... */
}

useKmenu Hook

useKmenu is a utility hook that gives you some useful functions and information about the current status of the menu. You can use these for a multitude of different things such as nested routes on the command menu or for toggling the menu through a button on your UI.

Here's a list of all the information it provides:

Parameter Description Type
input The current text in the search bar of the menu that is currently open string
setInput The setter function to change the open state Dispatch<SetStateAction>
open The index of the menu is currently open number
setOpen The setter function to change the open state (index: number) => void
toggle The function for toggling the main menu open/close void

With that, here's also a code example of how you could use this hook.

NOTE: YOU MUST WRAP YOUR COMPONENT INSIDE THE MenuProvider TO USE THIS HOOK.

import { useKmenu } from 'kmenu'

const Component = () => {
  const { input, open, toggle } = useKmenu()

  return (
    <div>
      <p>The current text on the search bar is: {input}</p>
      <p>The index of the menu which is currently open is: {open}</p>
      <button onClick={toggle}>Toggle Menu</button>
    </div>
  )
}

useCommands Hook

With kmenu v1, you can now dynamically compute and define commands.

When commands are inputted into the useCommands hook, they're returned into an object of command-menu parsable commands, and they require an initial value of the commands you'd like to pass in.

NOTE: YOU CANNOT USE SetCommands DIRECTLY INSIDE OF A useEffect HOOK AT RENDER

Here's an example of the hook live in action:

import { CommandMenu, Command, useCommands } from 'kmenu'

const Component = () => {
  const main: Command[] = [
    /* ... */
  ]

  const [commands, setCommands] = useCommands(main)

  return
  ;<CommandMenu commands={commands} crumbs={['Home']} index={1} main />
}

Customizing the menu

You can easily customize the colors on your command menu as well. Here's a list of properties that are customisable:

NOTE: ALL PROPERTIES ARE OPTIONAL

Parameter Description Type Default
backdropColor The color of the backdrop (include opacity) string #FFFFFF90
backdropBlur The backround blur of the backdrop (px) number 2px
backgroundColor The background color of the menu string #FFFFFF
breadcrumbColor The background color of the breadcrumbs string #FFFFFF
breadcrumbRadius The border radius of the breadcrumbs string 5px
borderWidth Width of the border surrounding the menu number 1px
borderColor The color of the border surrounding the menu string #3F3F3F
borderRadius The radius of the menu (px) number 10px
boxShadow The shadow of the menu string 0px 0px 60px 10px #00000020
inputBorder The color of the border below the search bar string #E9ECEF
inputColor The color of the text in the search bar string #000000
headingColor The color of the command category headings string #777777
commandInactive The color of the icon and text when the command is inactive string #828282
commandActive The color of the icon and text when the command is active string #343434
barBackground The background color of the active bar (include opacity) string #FFFFFF20
shortcutBackground The background color of the keyboard shortcut string #82828220
animationDuration The duration of the dialog opening animation number 0.1

Setting up the menu

Be sure to wrap our menu around a CommandWrapper component. Here are all the properties you can pass into it:

Parameter Description Type Optional
value The default value on this particular menu string โœ…

Here are all the options available on the menu:

Parameter Description Type Optional
commands The commands for this menu to display Command[] โŒ
subCommands Commands availiable only by search Command[] โœ…
index The index of this menu number โŒ
crumbs The current path of the command menu string[] โŒ
preventSearch Disable filtering results for the menu string โœ…
loadingPlaceholder Element to be displayed while commands load ReactElement โœ…
loadingState Whether or not the data is currently loading boolean โœ…
placeholder The placeholder text on this particular menu string โœ…

Once you have added commands to the menu and configured it to you preferences, you can add it into your application. Add in the CSS file for styling. Optionally, if you'd like to FULLY customize the styles on the menu to your likings then you can copy the index.css file from the repository and import that instead. You'll also need to create a useState hook for handling the state.

NOTE: YOU MUST WRAP YOUR MENU INSIDE OF THE MenuProvider FOR IT TO WORK

import { useState } from 'react'
import { CommandMenu, Command, MenuConfig } from 'kmenu'
import 'kmenu/dist/index.css'

const Component = () => {
  const commands: Command[] = [
    /* ... */
  ]
  const config: MenuConfig = {
    /* ... */
  }
  const categories: string[] = [
    /* ... */
  ]

  return (
    <MenuProvider config={config}>
      /* ... */
      <CommandWrapper>
        <CommandMenu commands={commands} index={1} crumbs={['Home']} main />
      </CommandWrapper>
      /* ... */
    </MenuProvider>
  )
}
/* ... */
export default Component

That's about all the configuration you'll need to do in order to get a basic command menu to work.

Nested Menus

This library also provides support for nested menus and commands. Here's an example to help you out:

import { useState } from 'react'
import { Menu, Command, useKmenu, useCommands } from 'kmenu'
import 'kmenu/dist/index.css'

const Component = () => {
  const main: Command[] = [
    {
      category: 'Navigation',
      commands: [
        {
          icon: <FiGlobe />,
          text: 'Website',
          href: 'https://hxrsh.in',
          newTab: true,
        },
        {
          icon: <FiArrowRight />,
          text: 'Nested Example...',
          perform: () => setOpen(2),
        },
      ]
    }
  ]

  const nested: Command[] = [
    {
      category: 'Navigation',
      commands: [
        {
          icon: <FiGlobe />,
          text: 'Demo',
          href: 'https://kmenu.hxrsh.in',
          newTab: true,
        },
        {
          icon: <FiGlobe />,
          text: 'GitHub',
          href: 'https://github.com/harshhhdev/kmenu',
          newTab: true,
        },
      ]
    }
  ]

  const { setOpen } = useKmenu()
  const [mainCommands, setMainCommands] = useCommands(main)
  const [nestedCommands, setNestedCommands] = useCommands(nested)

  return (
    {/* ... */}
    <CommandMenu
      commands={mainCommands}
      crumbs={['Home']}
      index={1}
      main
    />
    <CommandMenu
      commands={nestedCommands}
      crumbs={['Home', 'Example']}
      index={2}
    />
    {/* ... */}
  )
}
/* ... */
export default Component

If this isn't enough, there's also an example directory which you can clone and experiment around with to build nested routes.

useShortcut hook

This library also ships with a custom React hook called useShortcut which you can use to define your own shortcuts within your application.

Parameter Description Type Optional
targetKey The key that the shortcut is listening for string (must be valid key) โŒ
modifier The modifier key which can will activate the shortcut enum (shift/alt/ctrl/meta) โœ…

Here's an example:

import { useShortcut } from 'kmenu'

const Shortcut = () => {
  const shiftS = useShortcut({ targetKey: 's', modifier: 'shift' })

  /* ... */
}

export default Shortcut

The example below will run when someone uses the keyboard shortcut shift+s.

๐Ÿ’ป Development

Run the project locally

git clone https://github.com/harshhhdev/kmenu.git

Setting up the project

cd kmenu
yarn

# Setup example directory
cd example
yarn

Next, start the development server:

yarn start

This should compile an instance of your project to the dist folder. It should re-build everytime you save a new change.

Using the package

You can test the built package locally by running the example repository:

cd example

# Start development server
yarn start

Awesome. Your React development server should now be running on port 3000.

๐Ÿ”ง Tools Used

Inspirations

More Repositories

1

www

๐Ÿ‘‹
TypeScript
202
star
2

snip

Fast pastebin written in Rust
TypeScript
62
star
3

dots

Pastel-themed i3wm and Arch Linux rice
CSS
59
star
4

react-pointers

๐Ÿคธโ€โ™‚๏ธ Custom cursors for React
TypeScript
26
star
5

custom-pointer-react

RENAMEDโ€”see harshhhdev/react-pointers
16
star
6

debutur

๐Ÿš€ Create, and personalise your own simple and beautiful homepage
TypeScript
14
star
7

geethoob

๐Ÿ˜ Generate beautiful portfolios from your GitHub profile
TypeScript
13
star
8

app-idea-generator

๐Ÿ’ก Generate app ideas to take inspiration from, or to have a laugh
Svelte
12
star
9

snipbin

Sharing code made easier โšก
TypeScript
12
star
10

fast-bot

A customisable, fun, and moderation bot for your servers.
JavaScript
12
star
11

glassmorphicssm

๐Ÿฅƒ Generate glass UI CSS code for your glassmorphism needs
TypeScript
10
star
12

issure

๐Ÿค” Confused on what issue to work on next? Issure fetches a random issue from your GitHub repository to help you decide!
TypeScript
7
star
13

harshhhdev

Yet another profile readme ๐Ÿ˜Ž
6
star
14

pastemyst.js

๐Ÿ“ A Node.js API wrapper for PasteMyst
TypeScript
6
star
15

parti

๐Ÿค“ ๐ŸŽ‰ A geeky way to wish your friends or loved ones happy birthday in the terminal.
TypeScript
6
star
16

blog

Harsh and Thoughts - Harsh Singh's personal blog ๐Ÿ“š
CSS
6
star
17

ui-gallery

๐ŸŽจ Harsh Singh's user interface gallery
TypeScript
5
star
18

gitssues

๐Ÿค” Confused on what to work on? Gitssues fetches a random issue from a GitHub repository.
TypeScript
4
star
19

oponion

โšก Creating, voting, and sharing polls made simple
TypeScript
4
star
20

sniplink.js

โœ‚๏ธ A Node.js API wrapper for SnipLink
TypeScript
3
star
21

playyourshot.com

The official homepage for the upcoming top-down game, YourShot.
Vue
3
star
22

snipbin.js

โšก A TypeScript API wrapper for SnipBin
TypeScript
3
star
23

npx

Terminal business card
TypeScript
3
star
24

comment-of-code

A comment in every programming language! ๐Ÿ’ฌ
Makefile
3
star
25

birthday-celebration

๐Ÿฅณ๐ŸŽ‰ A fun, confetti filled birthday celebration.
HTML
3
star
26

kraftuur

๐ŸŽจ A colour system for designing beautiful and accessible interfaces
TypeScript
3
star
27

leetifier

A simple Python script to turn 73x7 1n70 numb3r5
Python
2
star
28

github-follower-counter

A simple follower counter made for a dev.to blog post as an intro to web scrapping ๐Ÿ•ธ๏ธ๐Ÿ’ป
JavaScript
1
star
29

the-basket

ExtraCredits Game jam 2020 โšก - Can you stop the birds?
C#
1
star
30

guestbook

๐Ÿ–– Harsh's Guestbook
TypeScript
1
star
31

calvera

A minimal portfolio + blog template for Abell.js
JavaScript
1
star
32

sketchmessage

๐ŸŽจ An online drawing app where you draw to communicate with people all over the world.
CSS
1
star