@jojovms/angular-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,39 @@
1
+ # @jojovms/angular-ninja-keys 🥷
2
+
3
+ Angular wrapper for `@jojovms/ninja-keys-core`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @jojovms/angular-ninja-keys
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { NinjaKeysComponent } from '@jojovms/angular-ninja-keys';
15
+
16
+ @Component({
17
+ standalone: true,
18
+ imports: [NinjaKeysComponent],
19
+ template: `
20
+ <button (click)="ninja.toggle()">Open Command Palette</button>
21
+
22
+ <ninja-keys
23
+ #ninja
24
+ [actions]="myActions"
25
+ theme="dark"
26
+ hotkey="k"
27
+ ></ninja-keys>
28
+ `
29
+ })
30
+ export class AppComponent {
31
+ myActions = [
32
+ { id: '1', title: 'Save', icon: '💾', handler: () => this.save() }
33
+ ];
34
+
35
+ save() {
36
+ console.log('Saved!');
37
+ }
38
+ }
39
+ ```
@@ -0,0 +1,2 @@
1
+ export * from './ninja-keys.component';
2
+ export type { NinjaAction } from '@jojovms/ninja-keys-core';
package/dist/index.mjs ADDED
@@ -0,0 +1,218 @@
1
+ var f = Object.defineProperty;
2
+ var m = (i, e, t) => e in i ? f(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t;
3
+ var d = (i, e, t) => m(i, typeof e != "symbol" ? e + "" : e, t);
4
+ import { Input as u, Component as v } from "@angular/core";
5
+ var x = Object.defineProperty, g = (i, e, t) => e in i ? x(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t, r = (i, e, t) => g(i, typeof e != "symbol" ? e + "" : e, t);
6
+ class b {
7
+ constructor(e) {
8
+ 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 = {
9
+ placeholder: "Type a command or search...",
10
+ theme: "light",
11
+ hotkey: "k",
12
+ ...e
13
+ }, this.filteredActions = this.options.actions;
14
+ }
15
+ init() {
16
+ this.createOverlay();
17
+ const e = (t) => {
18
+ (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()));
19
+ };
20
+ window.addEventListener("keydown", e), this.cleanupFns.push(() => window.removeEventListener("keydown", e));
21
+ }
22
+ cleanup() {
23
+ this.cleanupFns.forEach((e) => e()), this.overlay && this.overlay.remove();
24
+ }
25
+ open() {
26
+ this.isOpen || !this.overlay || (this.isOpen = !0, this.overlay.style.display = "flex", this.overlay.style.opacity = "0", requestAnimationFrame(() => {
27
+ this.overlay && (this.overlay.style.opacity = "1");
28
+ }), this.input && (this.input.value = "", this.input.focus(), this.filterActions("")));
29
+ }
30
+ close() {
31
+ !this.isOpen || !this.overlay || (this.overlay.style.opacity = "0", setTimeout(() => {
32
+ this.overlay && (this.overlay.style.display = "none"), this.isOpen = !1;
33
+ }, 200));
34
+ }
35
+ toggle() {
36
+ this.isOpen ? this.close() : this.open();
37
+ }
38
+ createOverlay() {
39
+ if (this.overlay) return;
40
+ this.overlay = document.createElement("div"), Object.assign(this.overlay.style, {
41
+ position: "fixed",
42
+ top: "0",
43
+ left: "0",
44
+ width: "100%",
45
+ height: "100%",
46
+ background: "rgba(0,0,0,0.5)",
47
+ zIndex: "9999",
48
+ display: "none",
49
+ justifyContent: "center",
50
+ alignItems: "flex-start",
51
+ paddingTop: "10vh",
52
+ transition: "opacity 0.2s ease",
53
+ fontFamily: "sans-serif",
54
+ backdropFilter: "blur(2px)"
55
+ }), this.overlay.addEventListener("click", (l) => {
56
+ l.target === this.overlay && this.close();
57
+ });
58
+ 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";
59
+ Object.assign(e.style, {
60
+ width: "600px",
61
+ maxWidth: "90%",
62
+ maxHeight: "80vh",
63
+ background: t,
64
+ color: s,
65
+ borderRadius: "8px",
66
+ boxShadow: "0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04)",
67
+ overflow: "hidden",
68
+ display: "flex",
69
+ flexDirection: "column",
70
+ border: `1px solid ${o}`
71
+ });
72
+ const n = document.createElement("div");
73
+ n.style.borderBottom = `1px solid ${o}`, n.style.padding = "16px", this.input = document.createElement("input"), Object.assign(this.input.style, {
74
+ width: "100%",
75
+ background: "transparent",
76
+ border: "none",
77
+ outline: "none",
78
+ fontSize: "18px",
79
+ color: s
80
+ }), this.input.placeholder = this.options.placeholder || "Type to search...", this.input.addEventListener("input", (l) => {
81
+ this.filterActions(l.target.value);
82
+ }), n.appendChild(this.input), e.appendChild(n), this.resultsList = document.createElement("div"), Object.assign(this.resultsList.style, {
83
+ overflowY: "auto",
84
+ padding: "8px 0",
85
+ maxHeight: "50vh"
86
+ }), e.appendChild(this.resultsList), this.overlay.appendChild(e), document.body.appendChild(this.overlay);
87
+ }
88
+ filterActions(e) {
89
+ const t = e.toLowerCase().trim();
90
+ t ? this.filteredActions = this.options.actions.filter((s) => {
91
+ var o, n;
92
+ return s.title.toLowerCase().includes(t) || ((o = s.keywords) == null ? void 0 : o.toLowerCase().includes(t)) || ((n = s.section) == null ? void 0 : n.toLowerCase().includes(t));
93
+ }) : this.filteredActions = this.options.actions, this.selectedIndex = 0, this.renderList();
94
+ }
95
+ moveSelection(e) {
96
+ this.selectedIndex += e, this.selectedIndex < 0 && (this.selectedIndex = this.filteredActions.length - 1), this.selectedIndex >= this.filteredActions.length && (this.selectedIndex = 0), this.renderList(), this.ensureVisible();
97
+ }
98
+ ensureVisible() {
99
+ if (!this.resultsList) return;
100
+ const e = this.resultsList.children[this.selectedIndex];
101
+ e && e.scrollIntoView({ block: "nearest" });
102
+ }
103
+ executeSelected() {
104
+ const e = this.filteredActions[this.selectedIndex];
105
+ e && e.handler && (e.handler(), this.close());
106
+ }
107
+ renderList() {
108
+ if (!this.resultsList) return;
109
+ this.resultsList.innerHTML = "";
110
+ const e = this.options.theme === "dark" ? "#374151" : "#f3f4f6", t = this.options.theme === "dark" ? "#9ca3af" : "#6b7280";
111
+ if (this.filteredActions.forEach((s, o) => {
112
+ const n = document.createElement("div"), l = o === this.selectedIndex;
113
+ Object.assign(n.style, {
114
+ padding: "12px 16px",
115
+ display: "flex",
116
+ alignItems: "center",
117
+ justifyContent: "space-between",
118
+ cursor: "pointer",
119
+ background: l ? e : "transparent",
120
+ borderLeft: l ? "4px solid #6366f1" : "4px solid transparent"
121
+ });
122
+ const a = document.createElement("div");
123
+ if (a.style.display = "flex", a.style.alignItems = "center", a.style.gap = "12px", s.icon) {
124
+ const h = document.createElement("span");
125
+ h.innerHTML = s.icon, a.appendChild(h);
126
+ }
127
+ const y = document.createElement("span");
128
+ if (y.textContent = s.title, a.appendChild(y), n.appendChild(a), s.hotkey) {
129
+ const h = document.createElement("div");
130
+ h.textContent = s.hotkey, Object.assign(h.style, {
131
+ fontSize: "12px",
132
+ padding: "2px 6px",
133
+ borderRadius: "4px",
134
+ background: this.options.theme === "dark" ? "#4b5563" : "#e5e7eb",
135
+ color: t
136
+ }), n.appendChild(h);
137
+ }
138
+ n.addEventListener("click", () => {
139
+ s.handler && (s.handler(), this.close());
140
+ }), n.addEventListener("mouseenter", () => {
141
+ this.selectedIndex = o, this.renderList();
142
+ }), this.resultsList.appendChild(n);
143
+ }), this.filteredActions.length === 0) {
144
+ const s = document.createElement("div");
145
+ s.textContent = "No results found.", Object.assign(s.style, {
146
+ padding: "20px",
147
+ textAlign: "center",
148
+ color: t
149
+ }), this.resultsList.appendChild(s);
150
+ }
151
+ }
152
+ }
153
+ var k = Object.defineProperty, L = Object.getOwnPropertyDescriptor, p = (i, e, t, s) => {
154
+ for (var o = s > 1 ? void 0 : s ? L(e, t) : e, n = i.length - 1, l; n >= 0; n--)
155
+ (l = i[n]) && (o = (s ? l(e, t, o) : l(o)) || o);
156
+ return s && o && k(e, t, o), o;
157
+ };
158
+ let c = class {
159
+ constructor() {
160
+ d(this, "actions", []);
161
+ d(this, "placeholder", "Type a command...");
162
+ d(this, "theme", "light");
163
+ d(this, "hotkey", "k");
164
+ d(this, "instance", null);
165
+ }
166
+ ngOnInit() {
167
+ this.initNinja();
168
+ }
169
+ ngOnChanges(i) {
170
+ (i.actions || i.theme || i.placeholder || i.hotkey) && this.initNinja();
171
+ }
172
+ initNinja() {
173
+ this.instance && this.instance.cleanup(), this.instance = new b({
174
+ actions: this.actions,
175
+ placeholder: this.placeholder,
176
+ theme: this.theme,
177
+ hotkey: this.hotkey
178
+ }), this.instance.init();
179
+ }
180
+ // Public API for template reference
181
+ open() {
182
+ var i;
183
+ (i = this.instance) == null || i.open();
184
+ }
185
+ close() {
186
+ var i;
187
+ (i = this.instance) == null || i.close();
188
+ }
189
+ toggle() {
190
+ var i;
191
+ (i = this.instance) == null || i.toggle();
192
+ }
193
+ ngOnDestroy() {
194
+ this.instance && this.instance.cleanup();
195
+ }
196
+ };
197
+ p([
198
+ u()
199
+ ], c.prototype, "actions", 2);
200
+ p([
201
+ u()
202
+ ], c.prototype, "placeholder", 2);
203
+ p([
204
+ u()
205
+ ], c.prototype, "theme", 2);
206
+ p([
207
+ u()
208
+ ], c.prototype, "hotkey", 2);
209
+ c = p([
210
+ v({
211
+ selector: "ninja-keys",
212
+ standalone: !0,
213
+ template: ""
214
+ })
215
+ ], c);
216
+ export {
217
+ c as NinjaKeysComponent
218
+ };
@@ -0,0 +1 @@
1
+ (function(n,o){typeof exports=="object"&&typeof module<"u"?o(exports,require("@angular/core")):typeof define=="function"&&define.amd?define(["exports","@angular/core"],o):(n=typeof globalThis<"u"?globalThis:n||self,o(n.AngularNinjaKeys={},n.ng.core))})(this,function(n,o){"use strict";var b=Object.defineProperty;var k=(n,o,d)=>o in n?b(n,o,{enumerable:!0,configurable:!0,writable:!0,value:d}):n[o]=d;var u=(n,o,d)=>k(n,typeof o!="symbol"?o+"":o,d);var d=Object.defineProperty,m=(r,e,t)=>e in r?d(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,h=(r,e,t)=>m(r,typeof e!="symbol"?e+"":e,t);class v{constructor(e){h(this,"options"),h(this,"isOpen",!1),h(this,"overlay",null),h(this,"input",null),h(this,"resultsList",null),h(this,"selectedIndex",0),h(this,"filteredActions",[]),h(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 p=document.createElement("span");p.innerHTML=i.icon,c.appendChild(p)}const f=document.createElement("span");if(f.textContent=i.title,c.appendChild(f),s.appendChild(c),i.hotkey){const p=document.createElement("div");p.textContent=i.hotkey,Object.assign(p.style,{fontSize:"12px",padding:"2px 6px",borderRadius:"4px",background:this.options.theme==="dark"?"#4b5563":"#e5e7eb",color:t}),s.appendChild(p)}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)}}}var g=Object.defineProperty,x=Object.getOwnPropertyDescriptor,y=(r,e,t,i)=>{for(var l=i>1?void 0:i?x(e,t):e,s=r.length-1,a;s>=0;s--)(a=r[s])&&(l=(i?a(e,t,l):a(l))||l);return i&&l&&g(e,t,l),l};n.NinjaKeysComponent=class{constructor(){u(this,"actions",[]);u(this,"placeholder","Type a command...");u(this,"theme","light");u(this,"hotkey","k");u(this,"instance",null)}ngOnInit(){this.initNinja()}ngOnChanges(e){(e.actions||e.theme||e.placeholder||e.hotkey)&&this.initNinja()}initNinja(){this.instance&&this.instance.cleanup(),this.instance=new v({actions:this.actions,placeholder:this.placeholder,theme:this.theme,hotkey:this.hotkey}),this.instance.init()}open(){var e;(e=this.instance)==null||e.open()}close(){var e;(e=this.instance)==null||e.close()}toggle(){var e;(e=this.instance)==null||e.toggle()}ngOnDestroy(){this.instance&&this.instance.cleanup()}},y([o.Input()],n.NinjaKeysComponent.prototype,"actions",2),y([o.Input()],n.NinjaKeysComponent.prototype,"placeholder",2),y([o.Input()],n.NinjaKeysComponent.prototype,"theme",2),y([o.Input()],n.NinjaKeysComponent.prototype,"hotkey",2),n.NinjaKeysComponent=y([o.Component({selector:"ninja-keys",standalone:!0,template:""})],n.NinjaKeysComponent),Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})});
@@ -0,0 +1,17 @@
1
+ import { OnInit, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
2
+ import { NinjaAction } from '@jojovms/ninja-keys-core';
3
+
4
+ export declare class NinjaKeysComponent implements OnInit, OnDestroy, OnChanges {
5
+ actions: NinjaAction[];
6
+ placeholder: string;
7
+ theme: 'light' | 'dark';
8
+ hotkey: string;
9
+ private instance;
10
+ ngOnInit(): void;
11
+ ngOnChanges(changes: SimpleChanges): void;
12
+ private initNinja;
13
+ open(): void;
14
+ close(): void;
15
+ toggle(): void;
16
+ ngOnDestroy(): void;
17
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@jojovms/angular-ninja-keys",
3
+ "version": "0.0.1",
4
+ "description": "Commandō: Angular wrapper for @jojovms/ninja-keys-core. Your commands, unleashed.",
5
+ "keywords": [
6
+ "angular",
7
+ "commando",
8
+ "command-palette",
9
+ "ninja-keys",
10
+ "keyboard-shortcuts",
11
+ "directive",
12
+ "service",
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": "angular"
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
+ "@angular/core": "^16.0.0 || ^17.0.0 || ^18.0.0",
38
+ "rxjs": "^7.0.0"
39
+ },
40
+ "dependencies": {
41
+ "@jojovms/ninja-keys-core": "*"
42
+ },
43
+ "devDependencies": {
44
+ "@angular/core": "^17.0.0",
45
+ "@angular/common": "^17.0.0",
46
+ "rxjs": "^7.8.0",
47
+ "typescript": "^5.0.0",
48
+ "vite": "^5.0.0",
49
+ "vite-plugin-dts": "^3.0.0",
50
+ "zone.js": "^0.14.0"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
54
+ }
55
+ }