pure-shortcut 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pure-shortcut
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # pure-shortcut
2
+
3
+ [npm version](https://www.npmjs.com/package/pure-shortcut)
4
+ [npm downloads](https://www.npmjs.com/package/pure-shortcut)
5
+ [license](LICENSE)
6
+
7
+ > **Elegant, customizable keyboard shortcut provider for React components (with pure JS support).**
8
+
9
+ ---
10
+
11
+ ## πŸ“š Table of Contents
12
+
13
+ - [Project Description](#project-description)
14
+ - [Demo](#demo)
15
+ - [Installation](#installation)
16
+ - [Dependencies](#dependencies)
17
+ - [Quick Start](#quick-start)
18
+ - [API Reference](#api-reference)
19
+ - [Advanced Usage](#advanced-usage)
20
+ - [Global Shortcuts with JavaScript](#global-shortcuts-with-javascript)
21
+ - [Events System](#events-system)
22
+ - [Examples](#examples)
23
+ - [Contributing & Feedback](#contributing--feedback)
24
+ - [License](#license)
25
+ - [Author](#author)
26
+
27
+ ---
28
+
29
+ ## πŸ“ Project Description
30
+
31
+ **pure-shortcut** is a React component and TypeScript utility enabling easy creation and management of global or scoped keyboard shortcuts (hotkeys). Designed for accessibility and productivity, it lets you assign one or more key combinations to any UI partβ€”ideal for commands, CRUD, navigation, and dashboards.
32
+
33
+ - Supports both React (via the Shortcut component) and vanilla JS/TS (addShortcuts/removeShortcuts).
34
+ - Ignores input, textarea, and contentEditable by default to avoid interfering with user text input.
35
+
36
+ ---
37
+
38
+ ## πŸ”— Dependencies
39
+
40
+ - **React** (version 18+)
41
+ - **TailwindCSS** (optional; only if you utilize `className` for styling)
42
+ - **@iconify/react** (optional; only if your children require these icons)
43
+
44
+ ---
45
+
46
+ ## πŸš€ Quick Start
47
+
48
+ ```tsx
49
+ import React from "react";
50
+ import Shortcut from "pure-shortcut";
51
+
52
+ function App() {
53
+ return (
54
+ <Shortcut
55
+ onShortPressed={[
56
+ {
57
+ key: "s",
58
+ ctrlKey: true,
59
+ onPress: (e) => {
60
+ e.preventDefault();
61
+ alert("Saved!");
62
+ }
63
+ },
64
+ {
65
+ key: "x",
66
+ altKey: true,
67
+ onPress: (e) => {
68
+ e.preventDefault();
69
+ alert("Detected ALT+X combination");
70
+ }
71
+ }
72
+ ]}
73
+ >
74
+ <input placeholder="Press Ctrl+S to save or Alt+X for another action" />
75
+ </Shortcut>
76
+ );
77
+ }
78
+
79
+ export default App;
80
+ ```
81
+
82
+ ---
83
+
84
+ ## πŸ› οΈ API Reference
85
+
86
+ ### `<Shortcut />` Component
87
+
88
+
89
+ | Prop | Type | Required | Description |
90
+ | ---------------- | ---------------------- | -------- | ----------------------------------------------------------- |
91
+ | `children` | `ReactNode` | Yes | The React content to wrap and enable shortcut listening on. |
92
+ | `onShortPressed` | `OnShortPressedItem[]` | Yes | Array of shortcut definitions. See below for details. |
93
+ | `className` | `string` | No | Optional CSS class for the wrapping `<div>`. |
94
+
95
+
96
+ #### **OnShortPressedItem**
97
+
98
+ ```ts
99
+ {
100
+ key: string; // The key to listen for (e.g., "s", "Enter", "ArrowUp")
101
+ ctrlKey?: boolean; // Requires Ctrl key (optional)
102
+ shiftKey?: boolean; // Requires Shift key (optional)
103
+ altKey?: boolean; // Requires Alt key (optional)
104
+ metaKey?: boolean; // Requires Meta (⌘ on Mac, Windows key on PC) (optional)
105
+ onPress: (e: KeyboardEvent) => void; // Callback executed when the shortcut triggers
106
+ }
107
+ ```
108
+
109
+ ##### Example:
110
+
111
+ ```tsx
112
+ onShortPressed={[
113
+ { key: "s", ctrlKey: true, onPress: (e) => { e.preventDefault(); alert("Saved!"); } }
114
+ ]}
115
+ ```
116
+
117
+ ---
118
+
119
+ ## ⚑️ Advanced Usage
120
+
121
+ ### Global Shortcuts with JavaScript (Utility API)
122
+
123
+ You can also use pure JavaScript/TypeScriptβ€”for instance, in non-React contexts or for global shortcuts:
124
+
125
+ ```js
126
+ import { addShortcuts, removeShortcuts } from "pure-shortcut";
127
+
128
+ const handler = (e) => { e.preventDefault(); alert("Saved!"); };
129
+
130
+ addShortcuts([
131
+ { key: "s", ctrlKey: true, onPress: handler }
132
+ ]);
133
+
134
+ // Later, when you want to clean up:
135
+ removeShortcuts();
136
+ ```
137
+
138
+ You can also use in HTML directly:
139
+
140
+ ```html
141
+ <script src="Shortcut.js"></script>
142
+ <script>
143
+ addShortcuts([
144
+ { key: "n", ctrlKey: true, altKey: true, onPress: function(e) { alert('Ctrl+Alt+N!'); } }
145
+ ]);
146
+ </script>
147
+ ```
148
+
149
+ ---
150
+
151
+ ## 🎯 Events System
152
+
153
+ - **Key Events**: All registered shortcuts receive the native `KeyboardEvent` object.
154
+ - **Input Safety**: Shortcuts are ignored when typing in `<input>`, `<textarea>`, or content-editable elements by default (for UX).
155
+
156
+ ---
157
+
158
+ ## πŸ’‘ Examples
159
+
160
+ ### Basic Example
161
+
162
+ ```tsx
163
+ <Shortcut
164
+ onShortPressed={[
165
+ { key: "s", ctrlKey: true, onPress: (e) => { e.preventDefault(); alert("Saved!"); } }
166
+ ]}
167
+ >
168
+ <input />
169
+ </Shortcut>
170
+ ```
171
+
172
+ ### Styled Example
173
+
174
+ ```tsx
175
+ import Shortcut from "pure-shortcut";
176
+
177
+ function Demo() {
178
+ return (
179
+ <Shortcut
180
+ className="bg-gray-100 p-4 rounded"
181
+ onShortPressed={[
182
+ { key: "s", ctrlKey: true, onPress: (e) => { e.preventDefault(); alert("Saved"); } },
183
+ { key: "u", shiftKey: true, onPress: (e) => { e.preventDefault(); alert("Shift+U"); } }
184
+ ]}
185
+ >
186
+ <input placeholder="Try the shortcuts (Ctrl+S, Shift+U)" />
187
+ </Shortcut>
188
+ );
189
+ }
190
+ ```
191
+
192
+ ---
193
+
194
+ ## πŸ™Œ Contributing / Feedback
195
+
196
+ Feedback, issues, and PRs are welcome!
197
+ Please open an [issue](https://github.com/your-repo/issues) or submit a pull request.
198
+
199
+ - To contribute, fork the repo, create your feature branch, and submit a pull request.
200
+ - For questions or feature requests, contact the author below.
201
+
202
+ ---
203
+
204
+ ## πŸ“„ License
205
+
206
+ Distributed under the [MIT License](./LICENSE).
207
+
208
+ ---
209
+
210
+ ## πŸ‘€ Author
211
+
212
+ Developed and maintained by [Antonio Benavides H.](mailto:your-email@example.com)
@@ -0,0 +1,23 @@
1
+ import { default as React, ReactNode } from 'react';
2
+ export interface OnShortPressedItem {
3
+ key: string;
4
+ ctrlKey?: boolean;
5
+ shiftKey?: boolean;
6
+ altKey?: boolean;
7
+ metaKey?: boolean;
8
+ onPress: (e: KeyboardEvent) => void;
9
+ }
10
+ export interface ShortcutProps {
11
+ children: ReactNode;
12
+ onShortPressed: OnShortPressedItem[];
13
+ className?: string;
14
+ }
15
+ /**
16
+ * Shortcut: Component to handle custom keyboard shortcuts.
17
+ * Improvements:
18
+ * - Uses ref to always have freshest onShortPressed handler.
19
+ * - Case-insensitive key support.
20
+ * - Ignores events from input, textarea, or contentEditable by default for better UX.
21
+ */
22
+ declare const Shortcut: React.FC<ShortcutProps>;
23
+ export default Shortcut;
@@ -0,0 +1,4 @@
1
+ export { default as Shortcut } from './Shortcut';
2
+ export { addShortcuts, removeShortcuts } from './tsShortcut';
3
+ export type { ShortcutProps } from './Shortcut';
4
+ export type { OnShortPressedItem } from './Shortcut';
package/dist/index.js ADDED
@@ -0,0 +1,340 @@
1
+ import ne, { useRef as ae, useEffect as F } from "react";
2
+ var k = { exports: {} }, y = {};
3
+ /**
4
+ * @license React
5
+ * react-jsx-runtime.production.js
6
+ *
7
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
8
+ *
9
+ * This source code is licensed under the MIT license found in the
10
+ * LICENSE file in the root directory of this source tree.
11
+ */
12
+ var K;
13
+ function oe() {
14
+ if (K) return y;
15
+ K = 1;
16
+ var t = Symbol.for("react.transitional.element"), c = Symbol.for("react.fragment");
17
+ function f(d, l, u) {
18
+ var a = null;
19
+ if (u !== void 0 && (a = "" + u), l.key !== void 0 && (a = "" + l.key), "key" in l) {
20
+ u = {};
21
+ for (var i in l)
22
+ i !== "key" && (u[i] = l[i]);
23
+ } else u = l;
24
+ return l = u.ref, {
25
+ $$typeof: t,
26
+ type: d,
27
+ key: a,
28
+ ref: l !== void 0 ? l : null,
29
+ props: u
30
+ };
31
+ }
32
+ return y.Fragment = c, y.jsx = f, y.jsxs = f, y;
33
+ }
34
+ var R = {};
35
+ /**
36
+ * @license React
37
+ * react-jsx-runtime.development.js
38
+ *
39
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
40
+ *
41
+ * This source code is licensed under the MIT license found in the
42
+ * LICENSE file in the root directory of this source tree.
43
+ */
44
+ var D;
45
+ function se() {
46
+ return D || (D = 1, process.env.NODE_ENV !== "production" && (function() {
47
+ function t(e) {
48
+ if (e == null) return null;
49
+ if (typeof e == "function")
50
+ return e.$$typeof === ee ? null : e.displayName || e.name || null;
51
+ if (typeof e == "string") return e;
52
+ switch (e) {
53
+ case h:
54
+ return "Fragment";
55
+ case V:
56
+ return "Profiler";
57
+ case J:
58
+ return "StrictMode";
59
+ case X:
60
+ return "Suspense";
61
+ case B:
62
+ return "SuspenseList";
63
+ case Q:
64
+ return "Activity";
65
+ }
66
+ if (typeof e == "object")
67
+ switch (typeof e.tag == "number" && console.error(
68
+ "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
69
+ ), e.$$typeof) {
70
+ case q:
71
+ return "Portal";
72
+ case G:
73
+ return e.displayName || "Context";
74
+ case z:
75
+ return (e._context.displayName || "Context") + ".Consumer";
76
+ case H:
77
+ var r = e.render;
78
+ return e = e.displayName, e || (e = r.displayName || r.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
79
+ case Z:
80
+ return r = e.displayName || null, r !== null ? r : t(e.type) || "Memo";
81
+ case A:
82
+ r = e._payload, e = e._init;
83
+ try {
84
+ return t(e(r));
85
+ } catch {
86
+ }
87
+ }
88
+ return null;
89
+ }
90
+ function c(e) {
91
+ return "" + e;
92
+ }
93
+ function f(e) {
94
+ try {
95
+ c(e);
96
+ var r = !1;
97
+ } catch {
98
+ r = !0;
99
+ }
100
+ if (r) {
101
+ r = console;
102
+ var n = r.error, o = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
103
+ return n.call(
104
+ r,
105
+ "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
106
+ o
107
+ ), c(e);
108
+ }
109
+ }
110
+ function d(e) {
111
+ if (e === h) return "<>";
112
+ if (typeof e == "object" && e !== null && e.$$typeof === A)
113
+ return "<...>";
114
+ try {
115
+ var r = t(e);
116
+ return r ? "<" + r + ">" : "<...>";
117
+ } catch {
118
+ return "<...>";
119
+ }
120
+ }
121
+ function l() {
122
+ var e = O.A;
123
+ return e === null ? null : e.getOwner();
124
+ }
125
+ function u() {
126
+ return Error("react-stack-top-frame");
127
+ }
128
+ function a(e) {
129
+ if (N.call(e, "key")) {
130
+ var r = Object.getOwnPropertyDescriptor(e, "key").get;
131
+ if (r && r.isReactWarning) return !1;
132
+ }
133
+ return e.key !== void 0;
134
+ }
135
+ function i(e, r) {
136
+ function n() {
137
+ L || (L = !0, console.error(
138
+ "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
139
+ r
140
+ ));
141
+ }
142
+ n.isReactWarning = !0, Object.defineProperty(e, "key", {
143
+ get: n,
144
+ configurable: !0
145
+ });
146
+ }
147
+ function m() {
148
+ var e = t(this.type);
149
+ return Y[e] || (Y[e] = !0, console.error(
150
+ "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
151
+ )), e = this.props.ref, e !== void 0 ? e : null;
152
+ }
153
+ function v(e, r, n, o, T, x) {
154
+ var s = n.ref;
155
+ return e = {
156
+ $$typeof: C,
157
+ type: e,
158
+ key: r,
159
+ props: n,
160
+ _owner: o
161
+ }, (s !== void 0 ? s : null) !== null ? Object.defineProperty(e, "ref", {
162
+ enumerable: !1,
163
+ get: m
164
+ }) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
165
+ configurable: !1,
166
+ enumerable: !1,
167
+ writable: !0,
168
+ value: 0
169
+ }), Object.defineProperty(e, "_debugInfo", {
170
+ configurable: !1,
171
+ enumerable: !1,
172
+ writable: !0,
173
+ value: null
174
+ }), Object.defineProperty(e, "_debugStack", {
175
+ configurable: !1,
176
+ enumerable: !1,
177
+ writable: !0,
178
+ value: T
179
+ }), Object.defineProperty(e, "_debugTask", {
180
+ configurable: !1,
181
+ enumerable: !1,
182
+ writable: !0,
183
+ value: x
184
+ }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
185
+ }
186
+ function b(e, r, n, o, T, x) {
187
+ var s = r.children;
188
+ if (s !== void 0)
189
+ if (o)
190
+ if (re(s)) {
191
+ for (o = 0; o < s.length; o++)
192
+ p(s[o]);
193
+ Object.freeze && Object.freeze(s);
194
+ } else
195
+ console.error(
196
+ "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
197
+ );
198
+ else p(s);
199
+ if (N.call(r, "key")) {
200
+ s = t(e);
201
+ var E = Object.keys(r).filter(function(te) {
202
+ return te !== "key";
203
+ });
204
+ o = 0 < E.length ? "{key: someKey, " + E.join(": ..., ") + ": ...}" : "{key: someKey}", $[s + o] || (E = 0 < E.length ? "{" + E.join(": ..., ") + ": ...}" : "{}", console.error(
205
+ `A props object containing a "key" prop is being spread into JSX:
206
+ let props = %s;
207
+ <%s {...props} />
208
+ React keys must be passed directly to JSX without using spread:
209
+ let props = %s;
210
+ <%s key={someKey} {...props} />`,
211
+ o,
212
+ s,
213
+ E,
214
+ s
215
+ ), $[s + o] = !0);
216
+ }
217
+ if (s = null, n !== void 0 && (f(n), s = "" + n), a(r) && (f(r.key), s = "" + r.key), "key" in r) {
218
+ n = {};
219
+ for (var P in r)
220
+ P !== "key" && (n[P] = r[P]);
221
+ } else n = r;
222
+ return s && i(
223
+ n,
224
+ typeof e == "function" ? e.displayName || e.name || "Unknown" : e
225
+ ), v(
226
+ e,
227
+ s,
228
+ n,
229
+ l(),
230
+ T,
231
+ x
232
+ );
233
+ }
234
+ function p(e) {
235
+ w(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === A && (e._payload.status === "fulfilled" ? w(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
236
+ }
237
+ function w(e) {
238
+ return typeof e == "object" && e !== null && e.$$typeof === C;
239
+ }
240
+ var _ = ne, C = Symbol.for("react.transitional.element"), q = Symbol.for("react.portal"), h = Symbol.for("react.fragment"), J = Symbol.for("react.strict_mode"), V = Symbol.for("react.profiler"), z = Symbol.for("react.consumer"), G = Symbol.for("react.context"), H = Symbol.for("react.forward_ref"), X = Symbol.for("react.suspense"), B = Symbol.for("react.suspense_list"), Z = Symbol.for("react.memo"), A = Symbol.for("react.lazy"), Q = Symbol.for("react.activity"), ee = Symbol.for("react.client.reference"), O = _.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, N = Object.prototype.hasOwnProperty, re = Array.isArray, S = console.createTask ? console.createTask : function() {
241
+ return null;
242
+ };
243
+ _ = {
244
+ react_stack_bottom_frame: function(e) {
245
+ return e();
246
+ }
247
+ };
248
+ var L, Y = {}, I = _.react_stack_bottom_frame.bind(
249
+ _,
250
+ u
251
+ )(), M = S(d(u)), $ = {};
252
+ R.Fragment = h, R.jsx = function(e, r, n) {
253
+ var o = 1e4 > O.recentlyCreatedOwnerStacks++;
254
+ return b(
255
+ e,
256
+ r,
257
+ n,
258
+ !1,
259
+ o ? Error("react-stack-top-frame") : I,
260
+ o ? S(d(e)) : M
261
+ );
262
+ }, R.jsxs = function(e, r, n) {
263
+ var o = 1e4 > O.recentlyCreatedOwnerStacks++;
264
+ return b(
265
+ e,
266
+ r,
267
+ n,
268
+ !0,
269
+ o ? Error("react-stack-top-frame") : I,
270
+ o ? S(d(e)) : M
271
+ );
272
+ };
273
+ })()), R;
274
+ }
275
+ var W;
276
+ function le() {
277
+ return W || (W = 1, process.env.NODE_ENV === "production" ? k.exports = oe() : k.exports = se()), k.exports;
278
+ }
279
+ var ue = le();
280
+ const me = ({
281
+ children: t,
282
+ onShortPressed: c,
283
+ className: f
284
+ }) => {
285
+ const d = ae(c);
286
+ return F(() => {
287
+ d.current = c;
288
+ }, [c]), F(() => {
289
+ const l = (a) => {
290
+ if (!a || !(a instanceof HTMLElement)) return !1;
291
+ const i = a.tagName.toLowerCase();
292
+ return i === "input" || i === "textarea" || a.isContentEditable;
293
+ }, u = (a) => {
294
+ l(a.target) || d.current.forEach((i) => {
295
+ const {
296
+ key: m,
297
+ ctrlKey: v = !1,
298
+ shiftKey: b = !1,
299
+ altKey: p = !1,
300
+ metaKey: w = !1
301
+ } = i;
302
+ (m.length === 1 ? a.key.toLowerCase() === m.toLowerCase() : a.key === m) && a.ctrlKey === v && a.shiftKey === b && a.altKey === p && a.metaKey === w && i.onPress(a);
303
+ });
304
+ };
305
+ return window.addEventListener("keydown", u), () => {
306
+ window.removeEventListener("keydown", u);
307
+ };
308
+ }, []), /* @__PURE__ */ ue.jsx("div", { className: f, children: t });
309
+ };
310
+ let j = [], g = !1;
311
+ function ie(t) {
312
+ if (!t || !(t instanceof HTMLElement)) return !1;
313
+ const c = t.tagName.toLowerCase();
314
+ return c === "input" || c === "textarea" || t.isContentEditable === !0;
315
+ }
316
+ function U(t) {
317
+ ie(t.target) || j.forEach(function(c) {
318
+ const {
319
+ key: f,
320
+ ctrlKey: d = !1,
321
+ shiftKey: l = !1,
322
+ altKey: u = !1,
323
+ metaKey: a = !1,
324
+ onPress: i
325
+ } = c;
326
+ (f.length === 1 ? t.key && t.key.toLowerCase() === f.toLowerCase() : t.key === f) && !!t.ctrlKey == !!d && !!t.shiftKey == !!l && !!t.altKey == !!u && !!t.metaKey == !!a && typeof i == "function" && i(t);
327
+ });
328
+ }
329
+ function ce(t) {
330
+ Array.isArray(t) && (j = t, g || (window.addEventListener("keydown", U), g = !0));
331
+ }
332
+ function fe() {
333
+ window.removeEventListener("keydown", U), j = [], g = !1;
334
+ }
335
+ typeof window < "u" && (window.addShortcuts = ce, window.removeShortcuts = fe);
336
+ export {
337
+ me as Shortcut,
338
+ ce as addShortcuts,
339
+ fe as removeShortcuts
340
+ };
@@ -0,0 +1,22 @@
1
+ (function(m,E){typeof exports=="object"&&typeof module<"u"?E(exports,require("react")):typeof define=="function"&&define.amd?define(["exports","react"],E):(m=typeof globalThis<"u"?globalThis:m||self,E(m.shortcut={},m.React))})(this,(function(m,E){"use strict";var T={exports:{}},R={};/**
2
+ * @license React
3
+ * react-jsx-runtime.production.js
4
+ *
5
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */var L;function G(){if(L)return R;L=1;var t=Symbol.for("react.transitional.element"),i=Symbol.for("react.fragment");function f(d,u,l){var o=null;if(l!==void 0&&(o=""+l),u.key!==void 0&&(o=""+u.key),"key"in u){l={};for(var c in u)c!=="key"&&(l[c]=u[c])}else l=u;return u=l.ref,{$$typeof:t,type:d,key:o,ref:u!==void 0?u:null,props:l}}return R.Fragment=i,R.jsx=f,R.jsxs=f,R}var b={};/**
10
+ * @license React
11
+ * react-jsx-runtime.development.js
12
+ *
13
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
14
+ *
15
+ * This source code is licensed under the MIT license found in the
16
+ * LICENSE file in the root directory of this source tree.
17
+ */var Y;function q(){return Y||(Y=1,process.env.NODE_ENV!=="production"&&(function(){function t(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===ce?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case x:return"Fragment";case re:return"Profiler";case ee:return"StrictMode";case ae:return"Suspense";case se:return"SuspenseList";case le:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case Q:return"Portal";case ne:return e.displayName||"Context";case te:return(e._context.displayName||"Context")+".Consumer";case oe:var r=e.render;return e=e.displayName,e||(e=r.displayName||r.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case ue:return r=e.displayName||null,r!==null?r:t(e.type)||"Memo";case P:r=e._payload,e=e._init;try{return t(e(r))}catch{}}return null}function i(e){return""+e}function f(e){try{i(e);var r=!1}catch{r=!0}if(r){r=console;var n=r.error,a=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return n.call(r,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",a),i(e)}}function d(e){if(e===x)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===P)return"<...>";try{var r=t(e);return r?"<"+r+">":"<...>"}catch{return"<...>"}}function u(){var e=g.A;return e===null?null:e.getOwner()}function l(){return Error("react-stack-top-frame")}function o(e){if(W.call(e,"key")){var r=Object.getOwnPropertyDescriptor(e,"key").get;if(r&&r.isReactWarning)return!1}return e.key!==void 0}function c(e,r){function n(){U||(U=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",r))}n.isReactWarning=!0,Object.defineProperty(e,"key",{get:n,configurable:!0})}function _(){var e=t(this.type);return $[e]||($[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function A(e,r,n,a,h,C){var s=n.ref;return e={$$typeof:D,type:e,key:r,props:n,_owner:a},(s!==void 0?s:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:_}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:h}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:C}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function w(e,r,n,a,h,C){var s=r.children;if(s!==void 0)if(a)if(ie(s)){for(a=0;a<s.length;a++)k(s[a]);Object.freeze&&Object.freeze(s)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else k(s);if(W.call(r,"key")){s=t(e);var y=Object.keys(r).filter(function(fe){return fe!=="key"});a=0<y.length?"{key: someKey, "+y.join(": ..., ")+": ...}":"{key: someKey}",z[s+a]||(y=0<y.length?"{"+y.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
18
+ let props = %s;
19
+ <%s {...props} />
20
+ React keys must be passed directly to JSX without using spread:
21
+ let props = %s;
22
+ <%s key={someKey} {...props} />`,a,s,y,s),z[s+a]=!0)}if(s=null,n!==void 0&&(f(n),s=""+n),o(r)&&(f(r.key),s=""+r.key),"key"in r){n={};for(var N in r)N!=="key"&&(n[N]=r[N])}else n=r;return s&&c(n,typeof e=="function"?e.displayName||e.name||"Unknown":e),A(e,s,n,u(),h,C)}function k(e){v(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===P&&(e._payload.status==="fulfilled"?v(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function v(e){return typeof e=="object"&&e!==null&&e.$$typeof===D}var p=E,D=Symbol.for("react.transitional.element"),Q=Symbol.for("react.portal"),x=Symbol.for("react.fragment"),ee=Symbol.for("react.strict_mode"),re=Symbol.for("react.profiler"),te=Symbol.for("react.consumer"),ne=Symbol.for("react.context"),oe=Symbol.for("react.forward_ref"),ae=Symbol.for("react.suspense"),se=Symbol.for("react.suspense_list"),ue=Symbol.for("react.memo"),P=Symbol.for("react.lazy"),le=Symbol.for("react.activity"),ce=Symbol.for("react.client.reference"),g=p.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,W=Object.prototype.hasOwnProperty,ie=Array.isArray,j=console.createTask?console.createTask:function(){return null};p={react_stack_bottom_frame:function(e){return e()}};var U,$={},J=p.react_stack_bottom_frame.bind(p,l)(),V=j(d(l)),z={};b.Fragment=x,b.jsx=function(e,r,n){var a=1e4>g.recentlyCreatedOwnerStacks++;return w(e,r,n,!1,a?Error("react-stack-top-frame"):J,a?j(d(e)):V)},b.jsxs=function(e,r,n){var a=1e4>g.recentlyCreatedOwnerStacks++;return w(e,r,n,!0,a?Error("react-stack-top-frame"):J,a?j(d(e)):V)}})()),b}var M;function H(){return M||(M=1,process.env.NODE_ENV==="production"?T.exports=G():T.exports=q()),T.exports}var X=H();const B=({children:t,onShortPressed:i,className:f})=>{const d=E.useRef(i);return E.useEffect(()=>{d.current=i},[i]),E.useEffect(()=>{const u=o=>{if(!o||!(o instanceof HTMLElement))return!1;const c=o.tagName.toLowerCase();return c==="input"||c==="textarea"||o.isContentEditable},l=o=>{u(o.target)||d.current.forEach(c=>{const{key:_,ctrlKey:A=!1,shiftKey:w=!1,altKey:k=!1,metaKey:v=!1}=c;(_.length===1?o.key.toLowerCase()===_.toLowerCase():o.key===_)&&o.ctrlKey===A&&o.shiftKey===w&&o.altKey===k&&o.metaKey===v&&c.onPress(o)})};return window.addEventListener("keydown",l),()=>{window.removeEventListener("keydown",l)}},[]),X.jsx("div",{className:f,children:t})};let S=[],O=!1;function Z(t){if(!t||!(t instanceof HTMLElement))return!1;const i=t.tagName.toLowerCase();return i==="input"||i==="textarea"||t.isContentEditable===!0}function I(t){Z(t.target)||S.forEach(function(i){const{key:f,ctrlKey:d=!1,shiftKey:u=!1,altKey:l=!1,metaKey:o=!1,onPress:c}=i;(f.length===1?t.key&&t.key.toLowerCase()===f.toLowerCase():t.key===f)&&!!t.ctrlKey==!!d&&!!t.shiftKey==!!u&&!!t.altKey==!!l&&!!t.metaKey==!!o&&typeof c=="function"&&c(t)})}function F(t){Array.isArray(t)&&(S=t,O||(window.addEventListener("keydown",I),O=!0))}function K(){window.removeEventListener("keydown",I),S=[],O=!1}typeof window<"u"&&(window.addShortcuts=F,window.removeShortcuts=K),m.Shortcut=B,m.addShortcuts=F,m.removeShortcuts=K,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"})}));
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Shortcut (HOTKEYS) - Pure JavaScript Utility
3
+ *
4
+ * Allows you to define custom keyboard shortcuts (hotkeys) to execute functions
5
+ * when specific key combinations are pressed.
6
+ *
7
+ * ## Usage in JavaScript (ES6+):
8
+ *
9
+ * ```js
10
+ * import { addShortcuts, removeShortcuts } from "./Shortcut";
11
+ *
12
+ * const handler = (e) => { e.preventDefault(); alert("Saved!"); };
13
+ * addShortcuts([
14
+ * { key: "s", ctrlKey: true, onPress: handler }
15
+ * ]);
16
+ *
17
+ * // To remove all shortcuts later:
18
+ * removeShortcuts();
19
+ * ```
20
+ *
21
+ * ## Direct HTML Usage:
22
+ * ```html
23
+ * <script src="Shortcut.js"></script>
24
+ * <script>
25
+ * addShortcuts([
26
+ * { key: "n", ctrlKey: true, altKey: true, onPress: function(e) { alert('Ctrl+Alt+N!'); } }
27
+ * ]);
28
+ * </script>
29
+ * ```
30
+ *
31
+ * ## Type Definition
32
+ * @typedef {Object} ShortPressedItem
33
+ * @property {string} key - The key to detect (e.g., "s" or "Enter").
34
+ * @property {boolean} [ctrlKey] - Requires Control key. Default: false.
35
+ * @property {boolean} [shiftKey] - Requires Shift key. Default: false.
36
+ * @property {boolean} [altKey] - Requires Alt key. Default: false.
37
+ * @property {boolean} [metaKey] - Requires Meta/Super key (⌘, Windows). Default: false.
38
+ * @property {(e: KeyboardEvent) => void} onPress - Function to execute when shortcut is triggered.
39
+ */
40
+ type ShortPressedItem = {
41
+ key: string;
42
+ ctrlKey?: boolean;
43
+ shiftKey?: boolean;
44
+ altKey?: boolean;
45
+ metaKey?: boolean;
46
+ onPress: (e: KeyboardEvent) => void;
47
+ };
48
+ /**
49
+ * Register global keyboard shortcuts.
50
+ * @param {ShortPressedItem[]} shortcuts
51
+ */
52
+ export declare function addShortcuts(shortcuts: ShortPressedItem[]): void;
53
+ /**
54
+ * Remove all currently active keyboard shortcuts.
55
+ */
56
+ export declare function removeShortcuts(): void;
57
+ declare global {
58
+ interface Window {
59
+ addShortcuts?: typeof addShortcuts;
60
+ removeShortcuts?: typeof removeShortcuts;
61
+ }
62
+ }
63
+ export {};
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "pure-shortcut",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "dist/index.umd.cjs",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "license":"MIT",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.js",
15
+ "require":"./dist/index.umd.cjs"
16
+ }
17
+ },
18
+ "peerDependencies": {
19
+ "react": "^18 || ^19",
20
+ "react-dom": "^18 || ^19",
21
+ "tailwindcss": "^4"
22
+ },
23
+ "dependencies": {
24
+ "@iconify/react": "^6.0.2"
25
+ },
26
+ "devDependencies": {
27
+ "@tailwindcss/cli": "^4.2.1",
28
+ "@types/react": "^19.2.14",
29
+ "tailwindcss": "^4.2.1"
30
+ },
31
+ "scripts": {
32
+ "build": "vite build"
33
+ },
34
+ "keywords": [
35
+ "shortcut",
36
+ "hotkey",
37
+ "react",
38
+ "keyboard",
39
+ "hook",
40
+ "shortcuts",
41
+ "keyboard-shortcut",
42
+ "custom-keyboard-shortcut",
43
+ "react-component",
44
+ "keybinding",
45
+ "keyboard-event",
46
+ "tailwind",
47
+ "typescript",
48
+ "shortcut-provider"
49
+ ]
50
+ }