@chatwoot/ninja-keys

中文文档
1.2.3发布于 2024/02/21MITTypeScriptES Module近 30 天下载 14.1万

Ninja Keys

原始使用文档
文档目录21 节
  1. Demo
  2. Motivation
  3. Integrations
  4. Features
  5. Why the "Ninja" name?
  6. Install from NPM
  7. Install from CDN
  8. Usage
  9. Attributes
  10. Data
  11. Methods
  12. Events
  13. Themes
  14. CSS variables
  15. CSS Shadow Parts
  16. Icons
  17. Change or hide footer
  18. Dev Server
  19. Linting
  20. Formatting
  21. License

Ninja Keys

Keyboard shortcut interface for your website that works with Vanilla JS, Vue, and React.

FOSSA Status npm npm

Demo

https://ninja-keys-demo.vercel.app/ Demo

Motivation

A lot of applications support a common pattern where the user hits ⌘+k (or ctrl+k) and a search UI dialog appears. I've recently seen this in Notion, Slack, Linear, Vercel and Algolia, but I'm sure there are plenty more. Apple Spotlight, Alfred and the Raycast app also have a similar pattern, but with different shortcuts. There are already some libraries built for this, but they are too framework specific, like Laravel only or React only Nevertheless, mine is not a silver bullet and if you need more framework integration, check them out too.

I needed a keyboard interface for navigation with static websites without any frameworks. At the same time, I have a few Vue projects where something like this could be useful, so I decided to give it a try for Web Components and Lit Element.

Integrations

Features

  • Keyboard navigation
  • Light and dark theme built in
  • Built-in icon support from Material font and custom svg icons
  • Nested menu - a tree or flat data structure can be used
  • Auto register your shortcuts
  • Root search - for example, if you search "Dark," it will find it within the "Theme" submenu
  • CSS variable to customize the view
  • Customizable hotkeys to open/close etc. Choose what best fits your website.

Why the "Ninja" name?

Because it appears from nowhere and executes any actions quickly... Or because it allows your users to become keyboard ninjas 🙃

Install from NPM

npm i ninja-keys

Import if you are using webpack, rollup, vite or other build system.

import 'ninja-keys';

Install from CDN

Mostly for usage in HTML/JS without a build system.

<script type="module" src="https://unpkg.com/ninja-keys?module"></script>

or inside your module scripts

<script type="module">
  import {NinjaKeys} from 'https://unpkg.com/ninja-keys?module';
</script>

Usage

Add the tag to your HTML.

<ninja-keys> </ninja-keys>
<script>
  const ninja = document.querySelector('ninja-keys');
  ninja.data = [
    {
      id: 'Projects',
      title: 'Open Projects',
      hotkey: 'ctrl+N',
      icon: 'apps',
      section: 'Projects',
      handler: () => {
        // it's auto register above hotkey with this handler
        alert('Your logic to handle');
      },
    },
    {
      id: 'Theme',
      title: 'Change theme...',
      icon: 'desktop_windows',
      children: ['Light Theme', 'Dark Theme', 'System Theme'],
      hotkey: 'ctrl+T',
      handler: () => {
        // open menu if closed. Because you can open directly that menu from it's hotkey
        ninja.open({ parent: 'Theme' });
        // if menu opened that prevent it from closing on select that action, no need if you don't have child actions
        return {keepOpen: true};
      },
    },
    {
      id: 'Light Theme',
      title: 'Change theme to Light',
      icon: 'light_mode',
      parent: 'Theme',
      handler: () => {
        // simple handler
        document.documentElement.classList.remove('dark');
      },
    },
    {
      id: 'Dark Theme',
      title: 'Change theme to Dark',
      icon: 'dark_mode',
      parent: 'Theme',
      handler: () => {
        // simple handler
        document.documentElement.classList.add('dark');
      },
    },
  ];
</script>

Library using flat data structure inside, as in the example above. But you can also use a tree structure as below:

{
  id: 'Theme',
  children: [
    { id: 'light' title: 'light_mode', },
    { id: 'System Theme',
      children: [
        { title: 'Sub item 1' },
        { title: 'Sub item 2' }
      ]
    }
  ]
}

Attributes

FieldDefaultDescription
placeholderType a command or search...Placeholder for search
disableHotkeysfalseIf attribute exist will register all hotkey for all actions
hideBreadcrumbsfalseHide breadcrumbs on header if true
openHotkeycmd+k,ctrl+kOpen or close shortcut
navigationUpHotkeyup,shift+tabNavigation up shortcuts
navigationDownHotkeydown,tabNavigation down shortcuts
closeHotkeyescClose shortcut
goBackHotkeybackspaceGo back on one level if has parent menu
selectHotkeyenterSelect action and execute handler or open submenu
hotKeysJoinedViewfalseIf exist/true will display hotkeys inside one element
noAutoLoadMdIconsfalseIf exist it disable load material icons font on connect
#### Example
```html
```

Data

Array of INinjaAction - interface properties below

NameTypeDescription
idstringUnique id/text. Will be displayed as breadcrumb in multimenu
titlestringTitle of action
hotkeystring(optional)Shortcut to display and register
handlerFunction(optional)Function to execute on select
mdIconstring(optional)Material Design font icon name
iconstring(optional)Html to render as custom icon
parentstring(optional)If using flat structure use id of actions to make a multilevel menu
keywordsstring(optional)Keywords to use for search
childrenArray(optional)If using flat structure then ids of child menu actions. Not required on tree structure
sectionstring(optional)Section text. Like a header will be group with other same sections

Methods

NameArgDescription
open{ parent?: string }Open menu with parent, if null then open root menu
closeClose menu
setParentparent?: stringNavigate to parent menu
#### Example
```js
const ninja = document.querySelector('ninja-keys');
ninja.open()
// or
ninja.open({ parent: 'Theme' })
```

Events

Component wide events

NameDescriptionPayload
changeEmitted when on each change of search input{ detail: { search: string, actions: Array<NinjaAction> } }
selectedEmitted when on user selected action or on submit of input{ detail: { search: string, action: NinjaAction or undefined }}
closedEmitted when user closed the menu

Both handler of action and component event selected emitted when user submit form or select item.

But event selected can be used to handle edge cases, so it's not recommended to write each action logic here. It’s better to use the action handler property.

For example, if a user enters a search query and there is an empty list, listening to this event you can handle that.

ninja.addEventListener('change', (event) => {
  console.log('ninja on change', event.detail);
  // detail = {search: 'your search query', actions: Array<NinjaAction>}
})
ninja.addEventListener('selected', (event) => {
  console.log('ninja on selected', event.detail);
  // detail = {search: 'your search query', action: NinjaAction | undefined }
  if (event.detail.action){
  // perform API search for example
  }
   
})

Themes

Component supports a dark theme out-of-box. You just need to add a class.

<ninja-keys class="dark"></ninja-keys>

If you need more style control, use any of the CSS variables below.

CSS variables

NameDefault
--ninja-width640px;
--ninja-backdrop-filternone;
--ninja-overflow-backgroundrgba(255, 255, 255, 0.5);
--ninja-text-colorrgb(60, 65, 73);
--ninja-font-size16px;
--ninja-top20%;
--ninja-key-border-radius0.25em
--ninja-accent-colorrgb(110, 94, 210);
--ninja-secondary-background-colorrgb(239, 241, 244);
--ninja-secondary-text-colorrgb(107, 111, 118);
--ninja-selected-backgroundrgb(248, 249, 251);
--ninja-icon-colorvar(--ninja-secondary-text-color);
--ninja-icon-size1.2em;
--ninja-separate-border1px solid var(--ninja-secondary-background-color);
--ninja-modal-background#fff;
--ninja-modal-shadowrgb(0 0 0 / 50%) 0px 16px 70px;
--ninja-actions-height300px;
--ninja-group-text-colorrgb(144, 149, 157);
--ninja-footer-backgroundrgba(242, 242, 242, 0.4);
--ninja-placeholder-color#8e8e8e
--ninja-z-index1

Example

ninja-keys {
  --ninja-width: 400px;
}

CSS Shadow Parts

Allowing you to style specific elements from your style. Because styles are encapsulated by Shadow DOM, it will be annoying to create css variables for all properties. That's why you can use ::part to make a custom look for the component. It's supported by all modern browsers

NameDescription
actions-listElement that wraps all child elements.
ninja-actionSingle action
ninja-selectedSelected action
ninja-inputInput element
ninja-input-wrapperWrapper element around div, useful for advanced styles

Example style using parts

ninja-keys::part(actions-list) {
  padding: 8px;
}
ninja-keys::part(ninja-action) {
  border-radius: 8px;
  border-left: none;
}

ninja-keys::part(ninja-selected) {
  background: rgba(51, 51, 51, 0.1);
}


ninja-keys::part(ninja-input) {
  color: #14b8a6;
}

ninja-keys::part(ninja-input)::placeholder {
  color: #f43f5e;
}

ninja-keys::part(ninja-input-wrapper) {
  background: rgba(244, 63, 93, 0.3);
}

Icons

By default, components use icons from https://fonts.google.com/icons

For example, you can just set mdIcon to light_mode to render a sun icon.

To add Material icons for your website, you need to add them to your HTML, for example

<link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet">

If you want custom icons, you can use svg or img to insert it with an icon property for action with ninja-icon class. Example:

{
  title: 'Search projects...',
  icon: `<svg xmlns="http://www.w3.org/2000/svg" class="ninja-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
  </svg>`,
  section: 'Projects',
},

You can also change the width and font using CSS variables

ninja-keys {
  --ninja-icon-size: 1em;
}
<ninja-keys> 
  <div slot="footer">You can use a custom footer or empty div to hide it</div>
</ninja-keys>

Dev Server

npm run start

Linting

To lint the project run:

npm run lint

Formatting

Prettier is used for code formatting. It has been pre-configured according to the Lit's style.

License

Copyright (c) Sergei Sleptsov [email protected]

Licensed under the MIT license.

FOSSA Status

版本与发布记录

@chatwoot/ninja-keys 共发布 1 个版本,首次发布于 2024/02/21;过去 12 个月发布了 0 个版本。

版本发布日期距今
1.2.3latest2024/02/212 年前

关于 @chatwoot/ninja-keys 的常见问题

@chatwoot/ninja-keys 是什么?

Ninja Keys

如何安装 @chatwoot/ninja-keys?

在项目目录运行 npm install @chatwoot/ninja-keys,或使用 pnpm add / yarn add / bun add。

@chatwoot/ninja-keys 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@chatwoot/ninja-keys 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@chatwoot/ninja-keys 可以商用吗?

@chatwoot/ninja-keys 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@chatwoot/ninja-keys 的下载量有多少?

根据 npm 官方下载统计,@chatwoot/ninja-keys 在 2026/10/10 – 2026/11/08 这 30 天内被下载 140,969 次,日均约 4,699 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@chatwoot/ninja-keys 还在维护吗?

最新版本 1.2.3 发布于 2024/02/21(2 年前);至今共发布 1 个版本,其中过去 12 个月发布了 0 个;首次发布于 2024/02/21。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。