@jojovms/react-ninja-keys 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 ADDED
@@ -0,0 +1,37 @@
1
+ # @jojovms/react-ninja-keys 🥷
2
+
3
+ React wrapper for `@jojovms/ninja-keys-core`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @jojovms/react-ninja-keys
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```jsx
14
+ import { NinjaKeys } from '@jojovms/react-ninja-keys';
15
+ import { useRef } from 'react';
16
+
17
+ function App() {
18
+ const ninjaRef = useRef(null);
19
+
20
+ const actions = [
21
+ { id: '1', title: 'Action 1', handler: () => alert('Action 1') }
22
+ ];
23
+
24
+ return (
25
+ <>
26
+ <button onClick={() => ninjaRef.current.toggle()}>Open Cmd+K</button>
27
+
28
+ <NinjaKeys
29
+ ref={ninjaRef}
30
+ actions={actions}
31
+ theme="dark"
32
+ hotkey="k"
33
+ />
34
+ </>
35
+ );
36
+ }
37
+ ```
@@ -0,0 +1,10 @@
1
+ import { NinjaKeysOptions } from '@jojovms/ninja-keys-core';
2
+
3
+ export interface NinjaKeysRef {
4
+ open: () => void;
5
+ close: () => void;
6
+ toggle: () => void;
7
+ }
8
+ export type NinjaKeysProps = NinjaKeysOptions;
9
+ export declare const NinjaKeys: import('react').ForwardRefExoticComponent<NinjaKeysOptions & import('react').RefAttributes<NinjaKeysRef>>;
10
+ export type { NinjaAction } from '@jojovms/ninja-keys-core';
package/dist/index.mjs ADDED
@@ -0,0 +1,171 @@
1
+ import { forwardRef as h, useRef as p, useEffect as u, useImperativeHandle as y } from "react";
2
+ var f = Object.defineProperty, v = (n, e, t) => e in n ? f(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t, r = (n, e, t) => v(n, typeof e != "symbol" ? e + "" : e, t);
3
+ class m {
4
+ constructor(e) {
5
+ r(this, "options"), r(this, "isOpen", !1), r(this, "overlay", null), r(this, "input", null), r(this, "resultsList", null), r(this, "selectedIndex", 0), r(this, "filteredActions", []), r(this, "cleanupFns", []), this.options = {
6
+ placeholder: "Type a command or search...",
7
+ theme: "light",
8
+ hotkey: "k",
9
+ ...e
10
+ }, this.filteredActions = this.options.actions;
11
+ }
12
+ init() {
13
+ this.createOverlay();
14
+ const e = (t) => {
15
+ (t.ctrlKey || t.metaKey) && t.key === (this.options.hotkey || "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()));
16
+ };
17
+ window.addEventListener("keydown", e), this.cleanupFns.push(() => window.removeEventListener("keydown", e));
18
+ }
19
+ cleanup() {
20
+ this.cleanupFns.forEach((e) => e()), this.overlay && this.overlay.remove();
21
+ }
22
+ open() {
23
+ this.isOpen || !this.overlay || (this.isOpen = !0, this.overlay.style.display = "flex", this.overlay.style.opacity = "0", requestAnimationFrame(() => {
24
+ this.overlay && (this.overlay.style.opacity = "1");
25
+ }), this.input && (this.input.value = "", this.input.focus(), this.filterActions("")));
26
+ }
27
+ close() {
28
+ !this.isOpen || !this.overlay || (this.overlay.style.opacity = "0", setTimeout(() => {
29
+ this.overlay && (this.overlay.style.display = "none"), this.isOpen = !1;
30
+ }, 200));
31
+ }
32
+ toggle() {
33
+ this.isOpen ? this.close() : this.open();
34
+ }
35
+ createOverlay() {
36
+ if (this.overlay) return;
37
+ this.overlay = document.createElement("div"), Object.assign(this.overlay.style, {
38
+ position: "fixed",
39
+ top: "0",
40
+ left: "0",
41
+ width: "100%",
42
+ height: "100%",
43
+ background: "rgba(0,0,0,0.5)",
44
+ zIndex: "9999",
45
+ display: "none",
46
+ justifyContent: "center",
47
+ alignItems: "flex-start",
48
+ paddingTop: "10vh",
49
+ transition: "opacity 0.2s ease",
50
+ fontFamily: "sans-serif",
51
+ backdropFilter: "blur(2px)"
52
+ }), this.overlay.addEventListener("click", (l) => {
53
+ l.target === this.overlay && this.close();
54
+ });
55
+ const e = document.createElement("div"), t = this.options.theme === "dark" ? "#1f2937" : "#ffffff", i = this.options.theme === "dark" ? "#f3f4f6" : "#111827", o = this.options.theme === "dark" ? "#374151" : "#e5e7eb";
56
+ Object.assign(e.style, {
57
+ width: "600px",
58
+ maxWidth: "90%",
59
+ maxHeight: "80vh",
60
+ background: t,
61
+ color: i,
62
+ borderRadius: "8px",
63
+ boxShadow: "0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04)",
64
+ overflow: "hidden",
65
+ display: "flex",
66
+ flexDirection: "column",
67
+ border: `1px solid ${o}`
68
+ });
69
+ const s = document.createElement("div");
70
+ s.style.borderBottom = `1px solid ${o}`, s.style.padding = "16px", this.input = document.createElement("input"), Object.assign(this.input.style, {
71
+ width: "100%",
72
+ background: "transparent",
73
+ border: "none",
74
+ outline: "none",
75
+ fontSize: "18px",
76
+ color: i
77
+ }), this.input.placeholder = this.options.placeholder || "Type to search...", this.input.addEventListener("input", (l) => {
78
+ this.filterActions(l.target.value);
79
+ }), s.appendChild(this.input), e.appendChild(s), this.resultsList = document.createElement("div"), Object.assign(this.resultsList.style, {
80
+ overflowY: "auto",
81
+ padding: "8px 0",
82
+ maxHeight: "50vh"
83
+ }), e.appendChild(this.resultsList), this.overlay.appendChild(e), document.body.appendChild(this.overlay);
84
+ }
85
+ filterActions(e) {
86
+ const t = e.toLowerCase().trim();
87
+ t ? this.filteredActions = this.options.actions.filter((i) => {
88
+ var o, s;
89
+ return i.title.toLowerCase().includes(t) || ((o = i.keywords) == null ? void 0 : o.toLowerCase().includes(t)) || ((s = i.section) == null ? void 0 : s.toLowerCase().includes(t));
90
+ }) : this.filteredActions = this.options.actions, this.selectedIndex = 0, this.renderList();
91
+ }
92
+ moveSelection(e) {
93
+ this.selectedIndex += e, this.selectedIndex < 0 && (this.selectedIndex = this.filteredActions.length - 1), this.selectedIndex >= this.filteredActions.length && (this.selectedIndex = 0), this.renderList(), this.ensureVisible();
94
+ }
95
+ ensureVisible() {
96
+ if (!this.resultsList) return;
97
+ const e = this.resultsList.children[this.selectedIndex];
98
+ e && e.scrollIntoView({ block: "nearest" });
99
+ }
100
+ executeSelected() {
101
+ const e = this.filteredActions[this.selectedIndex];
102
+ e && e.handler && (e.handler(), this.close());
103
+ }
104
+ renderList() {
105
+ if (!this.resultsList) return;
106
+ this.resultsList.innerHTML = "";
107
+ const e = this.options.theme === "dark" ? "#374151" : "#f3f4f6", t = this.options.theme === "dark" ? "#9ca3af" : "#6b7280";
108
+ if (this.filteredActions.forEach((i, o) => {
109
+ const s = document.createElement("div"), l = o === this.selectedIndex;
110
+ Object.assign(s.style, {
111
+ padding: "12px 16px",
112
+ display: "flex",
113
+ alignItems: "center",
114
+ justifyContent: "space-between",
115
+ cursor: "pointer",
116
+ background: l ? e : "transparent",
117
+ borderLeft: l ? "4px solid #6366f1" : "4px solid transparent"
118
+ });
119
+ const a = document.createElement("div");
120
+ if (a.style.display = "flex", a.style.alignItems = "center", a.style.gap = "12px", i.icon) {
121
+ const d = document.createElement("span");
122
+ d.innerHTML = i.icon, a.appendChild(d);
123
+ }
124
+ const c = document.createElement("span");
125
+ if (c.textContent = i.title, a.appendChild(c), s.appendChild(a), i.hotkey) {
126
+ const d = document.createElement("div");
127
+ d.textContent = i.hotkey, Object.assign(d.style, {
128
+ fontSize: "12px",
129
+ padding: "2px 6px",
130
+ borderRadius: "4px",
131
+ background: this.options.theme === "dark" ? "#4b5563" : "#e5e7eb",
132
+ color: t
133
+ }), s.appendChild(d);
134
+ }
135
+ s.addEventListener("click", () => {
136
+ i.handler && (i.handler(), this.close());
137
+ }), s.addEventListener("mouseenter", () => {
138
+ this.selectedIndex = o, this.renderList();
139
+ }), this.resultsList.appendChild(s);
140
+ }), this.filteredActions.length === 0) {
141
+ const i = document.createElement("div");
142
+ i.textContent = "No results found.", Object.assign(i.style, {
143
+ padding: "20px",
144
+ textAlign: "center",
145
+ color: t
146
+ }), this.resultsList.appendChild(i);
147
+ }
148
+ }
149
+ }
150
+ const g = h((n, e) => {
151
+ const t = p(null);
152
+ return u(() => (t.current = new m(n), t.current.init(), () => {
153
+ t.current && t.current.cleanup();
154
+ }), [JSON.stringify(n.actions), n.theme, n.placeholder, n.hotkey]), y(e, () => ({
155
+ open: () => {
156
+ var i;
157
+ return (i = t.current) == null ? void 0 : i.open();
158
+ },
159
+ close: () => {
160
+ var i;
161
+ return (i = t.current) == null ? void 0 : i.close();
162
+ },
163
+ toggle: () => {
164
+ var i;
165
+ return (i = t.current) == null ? void 0 : i.toggle();
166
+ }
167
+ })), null;
168
+ });
169
+ export {
170
+ g as NinjaKeys
171
+ };
@@ -0,0 +1 @@
1
+ (function(d,o){typeof exports=="object"&&typeof module<"u"?o(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],o):(d=typeof globalThis<"u"?globalThis:d||self,o(d.ReactNinjaKeys={},d.React))})(this,function(d,o){"use strict";var u=Object.defineProperty,f=(n,e,t)=>e in n?u(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,r=(n,e,t)=>f(n,typeof e!="symbol"?e+"":e,t);class y{constructor(e){r(this,"options"),r(this,"isOpen",!1),r(this,"overlay",null),r(this,"input",null),r(this,"resultsList",null),r(this,"selectedIndex",0),r(this,"filteredActions",[]),r(this,"cleanupFns",[]),this.options={placeholder:"Type a command or search...",theme:"light",hotkey:"k",...e},this.filteredActions=this.options.actions}init(){this.createOverlay();const e=t=>{(t.ctrlKey||t.metaKey)&&t.key===(this.options.hotkey||"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",a=>{a.target===this.overlay&&this.close()});const e=document.createElement("div"),t=this.options.theme==="dark"?"#1f2937":"#ffffff",i=this.options.theme==="dark"?"#f3f4f6":"#111827",l=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 ${l}`});const s=document.createElement("div");s.style.borderBottom=`1px solid ${l}`,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",a=>{this.filterActions(a.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 l,s;return i.title.toLowerCase().includes(t)||((l=i.keywords)==null?void 0:l.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,l)=>{const s=document.createElement("div"),a=l===this.selectedIndex;Object.assign(s.style,{padding:"12px 16px",display:"flex",alignItems:"center",justifyContent:"space-between",cursor:"pointer",background:a?e:"transparent",borderLeft:a?"4px solid #6366f1":"4px solid transparent"});const c=document.createElement("div");if(c.style.display="flex",c.style.alignItems="center",c.style.gap="12px",i.icon){const h=document.createElement("span");h.innerHTML=i.icon,c.appendChild(h)}const p=document.createElement("span");if(p.textContent=i.title,c.appendChild(p),s.appendChild(c),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=l,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)}}}const v=o.forwardRef((n,e)=>{const t=o.useRef(null);return o.useEffect(()=>(t.current=new y(n),t.current.init(),()=>{t.current&&t.current.cleanup()}),[JSON.stringify(n.actions),n.theme,n.placeholder,n.hotkey]),o.useImperativeHandle(e,()=>({open:()=>{var i;return(i=t.current)==null?void 0:i.open()},close:()=>{var i;return(i=t.current)==null?void 0:i.close()},toggle:()=>{var i;return(i=t.current)==null?void 0:i.toggle()}})),null});d.NinjaKeys=v,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})});
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@jojovms/react-ninja-keys",
3
+ "version": "0.0.1",
4
+ "description": "Commandō: React wrapper for @jojovms/ninja-keys-core. Your commands, unleashed.",
5
+ "keywords": [
6
+ "react",
7
+ "commando",
8
+ "command-palette",
9
+ "ninja-keys",
10
+ "keyboard-shortcuts",
11
+ "hook",
12
+ "component",
13
+ "jojovms"
14
+ ],
15
+ "author": "Shijo Shaji (https://shijoshaji.in)",
16
+ "homepage": "https://shijoshaji.in",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/shijoshaji/ninja-keys.git",
20
+ "directory": "react"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/shijoshaji/ninja-keys/issues"
24
+ },
25
+ "license": "MIT",
26
+ "main": "dist/index.js",
27
+ "module": "dist/index.mjs",
28
+ "types": "dist/index.d.ts",
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "scripts": {
33
+ "build": "vite build",
34
+ "dev": "vite build --watch"
35
+ },
36
+ "peerDependencies": {
37
+ "react": ">=16.8.0",
38
+ "react-dom": ">=16.8.0"
39
+ },
40
+ "dependencies": {
41
+ "@jojovms/ninja-keys-core": "*"
42
+ },
43
+ "devDependencies": {
44
+ "@types/react": "^18.0.0",
45
+ "@types/react-dom": "^18.0.0",
46
+ "react": "^18.0.0",
47
+ "react-dom": "^18.0.0",
48
+ "typescript": "^5.0.0",
49
+ "vite": "^5.0.0",
50
+ "vite-plugin-dts": "^3.0.0"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
54
+ }
55
+ }