@jojovms/ninja-keys-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.mjs +160 -0
- package/dist/index.umd.js +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @jojovms/ninja-keys-core 🥷
|
|
2
|
+
|
|
3
|
+
A zero-dependency "Command Palette" interface (Ctrl+K) for Vanilla JS.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @jojovms/ninja-keys-core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import { NinjaKeys } from '@jojovms/ninja-keys-core';
|
|
15
|
+
|
|
16
|
+
const ninja = new NinjaKeys({
|
|
17
|
+
actions: [
|
|
18
|
+
{ id: 'home', title: 'Go Home', icon: '🏠', handler: () => window.location = '/' },
|
|
19
|
+
{ id: 'theme', title: 'Toggle Dark Mode', icon: '🌙', handler: () => toggleMyTheme() }
|
|
20
|
+
],
|
|
21
|
+
hotkey: 'k' // Ctrl+K or Cmd+K
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
ninja.init();
|
|
25
|
+
|
|
26
|
+
// You can also open it manually via a button
|
|
27
|
+
document.getElementById('my-btn').onclick = () => ninja.toggle();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Options
|
|
31
|
+
|
|
32
|
+
| Option | Type | Default | Description |
|
|
33
|
+
|---|---|---|---|
|
|
34
|
+
| `actions` | `NinjaAction[]` | `[]` | Array of actions (see below). |
|
|
35
|
+
| `placeholder` | `string` | `'Type a command...'` | Search input placeholder. |
|
|
36
|
+
| `theme` | `'light' \| 'dark'` | `'light'` | Color theme. |
|
|
37
|
+
| `hotkey` | `string` | `'k'` | Hotkey to toggle (combined with Ctrl/Cmd). |
|
|
38
|
+
|
|
39
|
+
## Action Object
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
{
|
|
43
|
+
id: string;
|
|
44
|
+
title: string;
|
|
45
|
+
handler: () => void;
|
|
46
|
+
icon?: string; // Emoji or SVG
|
|
47
|
+
hotkey?: string; // Visual hint (e.g., 'ctrl+h')
|
|
48
|
+
section?: string; // (Future support) Grouping
|
|
49
|
+
keywords?: string; // Hidden search terms
|
|
50
|
+
}
|
|
51
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface NinjaAction {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
hotkey?: string;
|
|
5
|
+
handler?: () => void;
|
|
6
|
+
icon?: string;
|
|
7
|
+
section?: string;
|
|
8
|
+
keywords?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface NinjaKeysOptions {
|
|
11
|
+
actions: NinjaAction[];
|
|
12
|
+
placeholder?: string;
|
|
13
|
+
theme?: 'light' | 'dark';
|
|
14
|
+
}
|
|
15
|
+
export declare class NinjaKeys {
|
|
16
|
+
private options;
|
|
17
|
+
private isOpen;
|
|
18
|
+
private overlay;
|
|
19
|
+
private input;
|
|
20
|
+
private resultsList;
|
|
21
|
+
private selectedIndex;
|
|
22
|
+
private filteredActions;
|
|
23
|
+
private cleanupFns;
|
|
24
|
+
constructor(options: NinjaKeysOptions);
|
|
25
|
+
init(): void;
|
|
26
|
+
cleanup(): void;
|
|
27
|
+
open(): void;
|
|
28
|
+
close(): void;
|
|
29
|
+
toggle(): void;
|
|
30
|
+
private createOverlay;
|
|
31
|
+
private filterActions;
|
|
32
|
+
private moveSelection;
|
|
33
|
+
private ensureVisible;
|
|
34
|
+
private executeSelected;
|
|
35
|
+
private renderList;
|
|
36
|
+
}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
var c = Object.defineProperty;
|
|
2
|
+
var p = (l, e, t) => e in l ? c(l, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[e] = t;
|
|
3
|
+
var n = (l, e, t) => p(l, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
+
class f {
|
|
5
|
+
constructor(e) {
|
|
6
|
+
n(this, "options");
|
|
7
|
+
n(this, "isOpen", !1);
|
|
8
|
+
n(this, "overlay", null);
|
|
9
|
+
n(this, "input", null);
|
|
10
|
+
n(this, "resultsList", null);
|
|
11
|
+
n(this, "selectedIndex", 0);
|
|
12
|
+
n(this, "filteredActions", []);
|
|
13
|
+
n(this, "cleanupFns", []);
|
|
14
|
+
this.options = {
|
|
15
|
+
placeholder: "Type a command or search...",
|
|
16
|
+
theme: "light",
|
|
17
|
+
...e
|
|
18
|
+
}, this.filteredActions = this.options.actions;
|
|
19
|
+
}
|
|
20
|
+
init() {
|
|
21
|
+
this.createOverlay();
|
|
22
|
+
const e = (t) => {
|
|
23
|
+
(t.ctrlKey || t.metaKey) && t.key === "k" && (t.preventDefault(), this.toggle()), t.key === "Escape" && this.isOpen && this.close(), this.isOpen && (t.key === "ArrowDown" ? (t.preventDefault(), this.moveSelection(1)) : t.key === "ArrowUp" ? (t.preventDefault(), this.moveSelection(-1)) : t.key === "Enter" && (t.preventDefault(), this.executeSelected()));
|
|
24
|
+
};
|
|
25
|
+
window.addEventListener("keydown", e), this.cleanupFns.push(() => window.removeEventListener("keydown", e));
|
|
26
|
+
}
|
|
27
|
+
cleanup() {
|
|
28
|
+
this.cleanupFns.forEach((e) => e()), this.overlay && this.overlay.remove();
|
|
29
|
+
}
|
|
30
|
+
open() {
|
|
31
|
+
this.isOpen || !this.overlay || (this.isOpen = !0, this.overlay.style.display = "flex", this.overlay.style.opacity = "0", requestAnimationFrame(() => {
|
|
32
|
+
this.overlay && (this.overlay.style.opacity = "1");
|
|
33
|
+
}), this.input && (this.input.value = "", this.input.focus(), this.filterActions("")));
|
|
34
|
+
}
|
|
35
|
+
close() {
|
|
36
|
+
!this.isOpen || !this.overlay || (this.overlay.style.opacity = "0", setTimeout(() => {
|
|
37
|
+
this.overlay && (this.overlay.style.display = "none"), this.isOpen = !1;
|
|
38
|
+
}, 200));
|
|
39
|
+
}
|
|
40
|
+
toggle() {
|
|
41
|
+
this.isOpen ? this.close() : this.open();
|
|
42
|
+
}
|
|
43
|
+
createOverlay() {
|
|
44
|
+
if (this.overlay) return;
|
|
45
|
+
this.overlay = document.createElement("div"), Object.assign(this.overlay.style, {
|
|
46
|
+
position: "fixed",
|
|
47
|
+
top: "0",
|
|
48
|
+
left: "0",
|
|
49
|
+
width: "100%",
|
|
50
|
+
height: "100%",
|
|
51
|
+
background: "rgba(0,0,0,0.5)",
|
|
52
|
+
zIndex: "9999",
|
|
53
|
+
display: "none",
|
|
54
|
+
justifyContent: "center",
|
|
55
|
+
alignItems: "flex-start",
|
|
56
|
+
paddingTop: "10vh",
|
|
57
|
+
transition: "opacity 0.2s ease",
|
|
58
|
+
fontFamily: "sans-serif",
|
|
59
|
+
backdropFilter: "blur(2px)"
|
|
60
|
+
}), this.overlay.addEventListener("click", (r) => {
|
|
61
|
+
r.target === this.overlay && this.close();
|
|
62
|
+
});
|
|
63
|
+
const e = document.createElement("div"), t = this.options.theme === "dark" ? "#1f2937" : "#ffffff", s = this.options.theme === "dark" ? "#f3f4f6" : "#111827", o = this.options.theme === "dark" ? "#374151" : "#e5e7eb";
|
|
64
|
+
Object.assign(e.style, {
|
|
65
|
+
width: "600px",
|
|
66
|
+
maxWidth: "90%",
|
|
67
|
+
maxHeight: "80vh",
|
|
68
|
+
background: t,
|
|
69
|
+
color: s,
|
|
70
|
+
borderRadius: "8px",
|
|
71
|
+
boxShadow: "0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04)",
|
|
72
|
+
overflow: "hidden",
|
|
73
|
+
display: "flex",
|
|
74
|
+
flexDirection: "column",
|
|
75
|
+
border: `1px solid ${o}`
|
|
76
|
+
});
|
|
77
|
+
const i = document.createElement("div");
|
|
78
|
+
i.style.borderBottom = `1px solid ${o}`, i.style.padding = "16px", this.input = document.createElement("input"), Object.assign(this.input.style, {
|
|
79
|
+
width: "100%",
|
|
80
|
+
background: "transparent",
|
|
81
|
+
border: "none",
|
|
82
|
+
outline: "none",
|
|
83
|
+
fontSize: "18px",
|
|
84
|
+
color: s
|
|
85
|
+
}), this.input.placeholder = this.options.placeholder || "Type to search...", this.input.addEventListener("input", (r) => {
|
|
86
|
+
this.filterActions(r.target.value);
|
|
87
|
+
}), i.appendChild(this.input), e.appendChild(i), this.resultsList = document.createElement("div"), Object.assign(this.resultsList.style, {
|
|
88
|
+
overflowY: "auto",
|
|
89
|
+
padding: "8px 0",
|
|
90
|
+
maxHeight: "50vh"
|
|
91
|
+
}), e.appendChild(this.resultsList), this.overlay.appendChild(e), document.body.appendChild(this.overlay);
|
|
92
|
+
}
|
|
93
|
+
filterActions(e) {
|
|
94
|
+
const t = e.toLowerCase().trim();
|
|
95
|
+
t ? this.filteredActions = this.options.actions.filter((s) => {
|
|
96
|
+
var o, i;
|
|
97
|
+
return s.title.toLowerCase().includes(t) || ((o = s.keywords) == null ? void 0 : o.toLowerCase().includes(t)) || ((i = s.section) == null ? void 0 : i.toLowerCase().includes(t));
|
|
98
|
+
}) : this.filteredActions = this.options.actions, this.selectedIndex = 0, this.renderList();
|
|
99
|
+
}
|
|
100
|
+
moveSelection(e) {
|
|
101
|
+
this.selectedIndex += e, this.selectedIndex < 0 && (this.selectedIndex = this.filteredActions.length - 1), this.selectedIndex >= this.filteredActions.length && (this.selectedIndex = 0), this.renderList(), this.ensureVisible();
|
|
102
|
+
}
|
|
103
|
+
ensureVisible() {
|
|
104
|
+
if (!this.resultsList) return;
|
|
105
|
+
const e = this.resultsList.children[this.selectedIndex];
|
|
106
|
+
e && e.scrollIntoView({ block: "nearest" });
|
|
107
|
+
}
|
|
108
|
+
executeSelected() {
|
|
109
|
+
const e = this.filteredActions[this.selectedIndex];
|
|
110
|
+
e && e.handler && (e.handler(), this.close());
|
|
111
|
+
}
|
|
112
|
+
renderList() {
|
|
113
|
+
if (!this.resultsList) return;
|
|
114
|
+
this.resultsList.innerHTML = "";
|
|
115
|
+
const e = this.options.theme === "dark" ? "#374151" : "#f3f4f6", t = this.options.theme === "dark" ? "#9ca3af" : "#6b7280";
|
|
116
|
+
if (this.filteredActions.forEach((s, o) => {
|
|
117
|
+
const i = document.createElement("div"), r = o === this.selectedIndex;
|
|
118
|
+
Object.assign(i.style, {
|
|
119
|
+
padding: "12px 16px",
|
|
120
|
+
display: "flex",
|
|
121
|
+
alignItems: "center",
|
|
122
|
+
justifyContent: "space-between",
|
|
123
|
+
cursor: "pointer",
|
|
124
|
+
background: r ? e : "transparent",
|
|
125
|
+
borderLeft: r ? "4px solid #6366f1" : "4px solid transparent"
|
|
126
|
+
});
|
|
127
|
+
const d = document.createElement("div");
|
|
128
|
+
if (d.style.display = "flex", d.style.alignItems = "center", d.style.gap = "12px", s.icon) {
|
|
129
|
+
const a = document.createElement("span");
|
|
130
|
+
a.innerHTML = s.icon, d.appendChild(a);
|
|
131
|
+
}
|
|
132
|
+
const h = document.createElement("span");
|
|
133
|
+
if (h.textContent = s.title, d.appendChild(h), i.appendChild(d), s.hotkey) {
|
|
134
|
+
const a = document.createElement("div");
|
|
135
|
+
a.textContent = s.hotkey, Object.assign(a.style, {
|
|
136
|
+
fontSize: "12px",
|
|
137
|
+
padding: "2px 6px",
|
|
138
|
+
borderRadius: "4px",
|
|
139
|
+
background: this.options.theme === "dark" ? "#4b5563" : "#e5e7eb",
|
|
140
|
+
color: t
|
|
141
|
+
}), i.appendChild(a);
|
|
142
|
+
}
|
|
143
|
+
i.addEventListener("click", () => {
|
|
144
|
+
s.handler && (s.handler(), this.close());
|
|
145
|
+
}), i.addEventListener("mouseenter", () => {
|
|
146
|
+
this.selectedIndex = o, this.renderList();
|
|
147
|
+
}), this.resultsList.appendChild(i);
|
|
148
|
+
}), this.filteredActions.length === 0) {
|
|
149
|
+
const s = document.createElement("div");
|
|
150
|
+
s.textContent = "No results found.", Object.assign(s.style, {
|
|
151
|
+
padding: "20px",
|
|
152
|
+
textAlign: "center",
|
|
153
|
+
color: t
|
|
154
|
+
}), this.resultsList.appendChild(s);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export {
|
|
159
|
+
f as NinjaKeys
|
|
160
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(o,n){typeof exports=="object"&&typeof module<"u"?n(exports):typeof define=="function"&&define.amd?define(["exports"],n):(o=typeof globalThis<"u"?globalThis:o||self,n(o.NinjaKeysCore={}))})(this,function(o){"use strict";var f=Object.defineProperty;var u=(o,n,c)=>n in o?f(o,n,{enumerable:!0,configurable:!0,writable:!0,value:c}):o[n]=c;var l=(o,n,c)=>u(o,typeof n!="symbol"?n+"":n,c);class n{constructor(e){l(this,"options");l(this,"isOpen",!1);l(this,"overlay",null);l(this,"input",null);l(this,"resultsList",null);l(this,"selectedIndex",0);l(this,"filteredActions",[]);l(this,"cleanupFns",[]);this.options={placeholder:"Type a command or search...",theme:"light",...e},this.filteredActions=this.options.actions}init(){this.createOverlay();const e=t=>{(t.ctrlKey||t.metaKey)&&t.key==="k"&&(t.preventDefault(),this.toggle()),t.key==="Escape"&&this.isOpen&&this.close(),this.isOpen&&(t.key==="ArrowDown"?(t.preventDefault(),this.moveSelection(1)):t.key==="ArrowUp"?(t.preventDefault(),this.moveSelection(-1)):t.key==="Enter"&&(t.preventDefault(),this.executeSelected()))};window.addEventListener("keydown",e),this.cleanupFns.push(()=>window.removeEventListener("keydown",e))}cleanup(){this.cleanupFns.forEach(e=>e()),this.overlay&&this.overlay.remove()}open(){this.isOpen||!this.overlay||(this.isOpen=!0,this.overlay.style.display="flex",this.overlay.style.opacity="0",requestAnimationFrame(()=>{this.overlay&&(this.overlay.style.opacity="1")}),this.input&&(this.input.value="",this.input.focus(),this.filterActions("")))}close(){!this.isOpen||!this.overlay||(this.overlay.style.opacity="0",setTimeout(()=>{this.overlay&&(this.overlay.style.display="none"),this.isOpen=!1},200))}toggle(){this.isOpen?this.close():this.open()}createOverlay(){if(this.overlay)return;this.overlay=document.createElement("div"),Object.assign(this.overlay.style,{position:"fixed",top:"0",left:"0",width:"100%",height:"100%",background:"rgba(0,0,0,0.5)",zIndex:"9999",display:"none",justifyContent:"center",alignItems:"flex-start",paddingTop:"10vh",transition:"opacity 0.2s ease",fontFamily:"sans-serif",backdropFilter:"blur(2px)"}),this.overlay.addEventListener("click",d=>{d.target===this.overlay&&this.close()});const e=document.createElement("div"),t=this.options.theme==="dark"?"#1f2937":"#ffffff",i=this.options.theme==="dark"?"#f3f4f6":"#111827",r=this.options.theme==="dark"?"#374151":"#e5e7eb";Object.assign(e.style,{width:"600px",maxWidth:"90%",maxHeight:"80vh",background:t,color:i,borderRadius:"8px",boxShadow:"0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04)",overflow:"hidden",display:"flex",flexDirection:"column",border:`1px solid ${r}`});const s=document.createElement("div");s.style.borderBottom=`1px solid ${r}`,s.style.padding="16px",this.input=document.createElement("input"),Object.assign(this.input.style,{width:"100%",background:"transparent",border:"none",outline:"none",fontSize:"18px",color:i}),this.input.placeholder=this.options.placeholder||"Type to search...",this.input.addEventListener("input",d=>{this.filterActions(d.target.value)}),s.appendChild(this.input),e.appendChild(s),this.resultsList=document.createElement("div"),Object.assign(this.resultsList.style,{overflowY:"auto",padding:"8px 0",maxHeight:"50vh"}),e.appendChild(this.resultsList),this.overlay.appendChild(e),document.body.appendChild(this.overlay)}filterActions(e){const t=e.toLowerCase().trim();t?this.filteredActions=this.options.actions.filter(i=>{var r,s;return i.title.toLowerCase().includes(t)||((r=i.keywords)==null?void 0:r.toLowerCase().includes(t))||((s=i.section)==null?void 0:s.toLowerCase().includes(t))}):this.filteredActions=this.options.actions,this.selectedIndex=0,this.renderList()}moveSelection(e){this.selectedIndex+=e,this.selectedIndex<0&&(this.selectedIndex=this.filteredActions.length-1),this.selectedIndex>=this.filteredActions.length&&(this.selectedIndex=0),this.renderList(),this.ensureVisible()}ensureVisible(){if(!this.resultsList)return;const e=this.resultsList.children[this.selectedIndex];e&&e.scrollIntoView({block:"nearest"})}executeSelected(){const e=this.filteredActions[this.selectedIndex];e&&e.handler&&(e.handler(),this.close())}renderList(){if(!this.resultsList)return;this.resultsList.innerHTML="";const e=this.options.theme==="dark"?"#374151":"#f3f4f6",t=this.options.theme==="dark"?"#9ca3af":"#6b7280";if(this.filteredActions.forEach((i,r)=>{const s=document.createElement("div"),d=r===this.selectedIndex;Object.assign(s.style,{padding:"12px 16px",display:"flex",alignItems:"center",justifyContent:"space-between",cursor:"pointer",background:d?e:"transparent",borderLeft:d?"4px solid #6366f1":"4px solid transparent"});const a=document.createElement("div");if(a.style.display="flex",a.style.alignItems="center",a.style.gap="12px",i.icon){const h=document.createElement("span");h.innerHTML=i.icon,a.appendChild(h)}const p=document.createElement("span");if(p.textContent=i.title,a.appendChild(p),s.appendChild(a),i.hotkey){const h=document.createElement("div");h.textContent=i.hotkey,Object.assign(h.style,{fontSize:"12px",padding:"2px 6px",borderRadius:"4px",background:this.options.theme==="dark"?"#4b5563":"#e5e7eb",color:t}),s.appendChild(h)}s.addEventListener("click",()=>{i.handler&&(i.handler(),this.close())}),s.addEventListener("mouseenter",()=>{this.selectedIndex=r,this.renderList()}),this.resultsList.appendChild(s)}),this.filteredActions.length===0){const i=document.createElement("div");i.textContent="No results found.",Object.assign(i.style,{padding:"20px",textAlign:"center",color:t}),this.resultsList.appendChild(i)}}}o.NinjaKeys=n,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jojovms/ninja-keys-core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Commandō: Your commands, unleashed. A VS Code-style command palette for your web applications.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"commando",
|
|
7
|
+
"command-palette",
|
|
8
|
+
"ninja-keys",
|
|
9
|
+
"keyboard-shortcuts",
|
|
10
|
+
"a11y",
|
|
11
|
+
"framework",
|
|
12
|
+
"jojovms"
|
|
13
|
+
],
|
|
14
|
+
"author": "Shijo Shaji (https://shijoshaji.in)",
|
|
15
|
+
"homepage": "https://shijoshaji.in",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/shijoshaji/ninja-keys.git",
|
|
19
|
+
"directory": "core"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/shijoshaji/ninja-keys/issues"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"main": "dist/index.js",
|
|
26
|
+
"module": "dist/index.mjs",
|
|
27
|
+
"types": "dist/index.d.ts",
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "vite build",
|
|
33
|
+
"dev": "vite build --watch"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"typescript": "^5.0.0",
|
|
37
|
+
"vite": "^5.0.0",
|
|
38
|
+
"vite-plugin-dts": "^3.0.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
}
|
|
43
|
+
}
|