npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vue-cmd-menu

v0.1.9

Published

Build beautiful and extensible Command + k menus with Vue

Downloads

262

Readme

Vue Command Menu

Build beautiful and extensible + k menus with Vue

🔮 Demo - 🚀 Get started - 📚 Docs

image

👋 Introduction

vue-cmd-menu lets you built a beatiful, fast and extensible command menu like the ones you may know from Vercel, GitHub and Linear or MacOS's Spotlight/Raycast. It can be invoked anywhere in your app via Command + k to perform actions users would typically be able to do via an interface.

  • 💅 Beautiful by default, easy to style to your liking
  • 🔎 Built-in filtering with Fuse.js
  • ⌨️ Keyboard shortcuts for registering keystrokes to specific actions
  • 🧭 Easy navigation with your keyboard or mouse
  • 📁 Nested actions for folder like navigation experience
  • 🔩 Simple data structure to define and customise actions

🚀 Get started

Install it via NPM:

npm install vue-cmd-menu

It currently only supports Vue 2

Global Usage

import Vue from 'vue';
import CommandMenu from 'vue-cmd-menu';

Vue.component('CommandMenu', CommandMenu);

In Single File Components

import CommandMenu from 'vue-cmd-menu';

export default {
  // ...
  components: {
    CommandMenu,
  },
  // ...
};

📚 Usage

While command menus sound easy to build in theory, in practice handling the different interactions, nagivation options and state management can quickly become complicated. vue-cmd-menu provides a simple abstraction over this, simply pass it the actions you want your users to be able to perform and it will handle the rest.

<template>
  <div id="app">
    <CommandMenu :actions="commandItems"></CommandMenu>
  </div>
</template>

<script lang="ts">
import Vue from 'vue';
import CommandMenu from 'vue-cmd-menu';

export default Vue.extend({
  components: {
    CommandMenu
  },
  computed: {
    actions() {
      return [
        {
          id: 'new',
          text: 'Create New Page',
          tag: 'New',
          childPlaceholder: 'Page title',
          action: (value) => {
            // Handle the action, value contains the entered title
            console.log(value)
          }
        },
        {
          id: 'docs',
          text: 'Documentation',
          keybindings: [ '?' ],
          childPlaceholder: 'Search Docs',
          childActions: [
            {
              id: 'overview',
              text: 'Overview',
              action: () => {}
            },
            {
              id: 'get-started',
              text: 'Get Started',
              action: () => {}
            }
          ]
        }
      ]
    }
  }
})
</script>

Available Props

  • name: string | boolean | null - namespace the modal and the open event (default: '')
  • actions: Array<any | Record<string, any>> - the available actions to filter and execute (required)
  • keybinding: Array<string> | null - combination of keys that need to be pressed (default: ['meta', 'k'])
  • placeholder: string - placeholder to show for the root command (default: Type a command or search)
  • shadow: boolean - add a shadow to the view box (default: true)
  • overlay: boolean - show an overlay under the view box (default: true)
  • theme: string - which theme to use, dark or light (default: light)
  • blur: boolean - enable background blur (default: true)
  • animations: boolean - enable animations (default: true)
  • nestedSearch: boolean - search/filter nested actions
  • fuseOptions: Fuse.IFuseOptions<string> - options to pass to Fuse.js (see options page) (default: {})

Actions

There are different type of actions you can define. They all require at least a id, text and if it doesn't have any child actions, a action handler:

const action ={
  id: 'ID',
  text: 'Title',
  action: () => {}
}

Here are all the options available to an action:

| name | description | type | required | |--------------|-------------------------------------------------------------------------------------|-----------------------------------------------|----------| | id | Internal ID of the action | string | true | | text | Text which will be shown for each action in the list | string | true | | action | Hanlder which will be called when the action is selected | function | true | | icon | Icon to show before the text in the UI | Vue Component/string (only when slot is used) | false | | section | Section to group the action in | string | false | | keybindings | Keystrokes to attach to the action | string[] | false | | tag | Tag to show before the input field after the action is selected | string | false | | childPlaceholder | Placeholder to show when waiting for user input after the parent action is selected | string | false | | value | Value to insert into the search field when the action is selected | string | false | | hidden | Show the action in the result list | boolean | false | | childTitle | Display a title instead of the input field when the action is selected |string | false | | childActions | Array of child actions to show once the action is selected | Actions[] | false |

Icons

The icon property either excepts a Vue component or a string which will be passed to the icon slot:

<template v-slot:icon="{ icon }">
  <Icon :name="icon" />
</template>

You can use the provided value with different icon libraries or with your custom one.

Inputs

If your action needs an input, specify a tag which will be shown before the input. When the user hits enter your action handler will be executed with the value as its parameter.

Nested actions

Each of your actions can also have nested/child actions which will be shown once the parent action is selected. Specify them with the childActions parameter:

 [{
    id: 'docs',
    text: 'Documentation',
    tag: 'Docs',
    childPlaceholder: 'Search Docs',
    childActions: [
      {
        id: 'overview',
        text: 'Overview',
        action: () => {}
      },
      {
        id: 'get-started',
        text: 'Get Started',
        action: () => {}
      },
    ]
  }]

You can also define a childPlaceholder which will be shown in the input field before any child action is selected and a tag which will be shown before the input to indicate what type of actions are being shown (kind of like a breadcrumb).

Keybindings

Each of your actions can either be triggered by selecting it in the menu, or directly with a keyboard shortcut. You can define the keys to attach to a action with the keybindings parameter:

[{
  id: 'help',
  keybindings: [ '?' ],
  text: 'Help',
  action: () => {}
}]

The shortcut by default listens to the meta key (CMD on Mac, Win on Windows) and then the defined keybinding.

Opening Programmatically

<!-- the `openCommandMenu` event can be called anyway and will trigger the modal to open -->
<button type="button" @click.prevent="$root.$emit('openCommandMenu')">Show Omnibar</button>
<!-- if there is a `name`, the event will have the name appended: `'openCommandMenu.myName'` -->
<button type="button" @click.prevent="$root.$emit('openCommandMenu.myName')">Show "myName" modal</button>
<!-- you can also close the modal with the opposite event: `'closeCommandMenu.myName'` -->
<button type="button" @click.prevent="$root.$emit('closeCommandMenu.myName')">Close "myName" modal</button>

Filtering

vue-cmd-menu uses Fuse.js under the hood to filter the specified actions. This allows for fuzzy searching i.e. the search term doesn't have to be a exact match. By default it searches the action's title and keys defined with the keywords property.

If you want to search nested actions as well, enable it with the nestedSearch prop.

You can further fine-tune fuse.js with the fuseOptions prop.

📖 Examples

Here are some examples for different type of actions:

[
  {
    id: 'home',
    keybindings: [ 'backspace' ],
    text: 'Go home',
    action: () => {
      window.location.pathname = '/'
    }
  },
  {
    id: 'copy',
    keybindings: [ 'c' ],
    text: 'Copy',
    action: () => {}
  },
  {
    id: 'help',
    keybindings: [ '?' ],
    icon: HelpIcon,
    text: 'Help',
    action: () => {
      window.location.pathname = '/'
    }
  },
  {
    id: 'new',
    text: 'Create New Page',
    tag: 'New',
    childPlaceholder: 'Page title',
    action: (value) => {
      window.location.pathname = '/'
    }
  },
  {
    id: 'docs',
    icon: DocsIcon,
    text: 'Documentation',
    childPlaceholder: 'Search Docs',
    childActions: [
      {
        id: 'overview',
        text: 'Overview',
        action: () => {}
      },
      {
        id: 'get-started',
        text: 'Get Started',
        action: () => {}
      },
    ]
  },

💻 Development

Issues and PRs are very welcome!

The actual source code of this library is in the src folder.

# install dependencies
yarn install

# serve app with hot reload
yarn run dev

# build electron application for production
yarn run build

# lint all JS/Vue component files in `src/`
yarn run lint

📋 To Do

  • [ ] Standardize parameter of Action handler
  • [ ] Don't hard code meta key for keybindings
  • [ ] Add options to change styling
  • [x] Sections
  • [x] Light mode
  • [ ] Animations
  • [ ] Hook to add actions dynamicly
  • [ ] Improve naming of properties and options
  • [ ] Keep focus on input when navigating through list
  • [x] Limit modal height and add scrollbar
  • [ ] Async actions and loading states
  • [ ] Use slot to customize the rendering of the results
  • [ ] Vue 3 support

❔ About

This project was developed by me (@betahuhn) in my free time. If you want to support me:

Donate via PayPal

ko-fi

Credits

This Action was inspired by:

📄 License

Copyright 2022 Maximilian Schiller

This project is licensed under the MIT License - see the LICENSE file for details.