@ramstack/alpinegear-typegrab 1.4.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/README.md +77 -0
- package/alpinegear-typegrab.esm.js +34 -0
- package/alpinegear-typegrab.esm.min.js +1 -0
- package/alpinegear-typegrab.js +39 -0
- package/alpinegear-typegrab.min.js +1 -0
- package/package.json +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @ramstack/alpinegear-typegrab
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@ramstack/alpinegear-typegrab)
|
|
4
|
+
[](https://github.com/rameel/ramstack.alpinegear.js/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
`@ramstack/alpinegear-typegrab` is a lightweight plugin for [Alpine.js](https://alpinejs.dev/) that provides the `x-typegrab` directive.
|
|
7
|
+
|
|
8
|
+
The directive automatically focuses an element when the user starts typing **any printable, non-whitespace character**,
|
|
9
|
+
as long as no editable element is currently focused. This is useful for search inputs, command palettes,
|
|
10
|
+
and similar UX patterns where typing should immediately direct input to a specific field.
|
|
11
|
+
|
|
12
|
+
## How it works
|
|
13
|
+
|
|
14
|
+
* Listens globally to the `keydown` event.
|
|
15
|
+
* Triggers only for **printable, non-whitespace characters**.
|
|
16
|
+
* Ignores events with `Ctrl`, `Alt`, or `Meta` modifiers.
|
|
17
|
+
* Does nothing if the active element is `input`, `textarea`, or an element with `contenteditable`.
|
|
18
|
+
* Focuses the element marked with `x-typegrab`.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
### Using CDN
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<!-- alpine.js plugin -->
|
|
26
|
+
<script src="https://cdn.jsdelivr.net/npm/@ramstack/alpinegear-typegrab@1/alpinegear-typegrab.min.js" defer></script>
|
|
27
|
+
|
|
28
|
+
<!-- alpine.js -->
|
|
29
|
+
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js" defer></script>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Using NPM
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install --save @ramstack/alpinegear-typegrab
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import Alpine from "alpinejs";
|
|
40
|
+
import Typegrab from "@ramstack/alpinegear-typegrab";
|
|
41
|
+
|
|
42
|
+
Alpine.plugin(Typegrab);
|
|
43
|
+
Alpine.start();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<input
|
|
50
|
+
type="search"
|
|
51
|
+
placeholder="Type to search..."
|
|
52
|
+
x-typegrab
|
|
53
|
+
/>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
When the user presses any printable character key (excluding whitespace) while no other editable element is focused,
|
|
57
|
+
this input will automatically receive focus.
|
|
58
|
+
|
|
59
|
+
## Notes
|
|
60
|
+
|
|
61
|
+
* The directive does not cancel or modify the original keyboard event.
|
|
62
|
+
* The target element must be focusable.
|
|
63
|
+
* Focus will not be stolen from active editable elements.
|
|
64
|
+
|
|
65
|
+
## Source code
|
|
66
|
+
|
|
67
|
+
You can find the source code for this plugin on GitHub:
|
|
68
|
+
https://github.com/rameel/ramstack.alpinegear.js/tree/main/src/plugins/typegrab
|
|
69
|
+
|
|
70
|
+
## Contributions
|
|
71
|
+
|
|
72
|
+
Bug reports and contributions are welcome.
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
This package is released as open source under the **MIT License**.
|
|
77
|
+
See the [LICENSE](https://github.com/rameel/ramstack.alpinegear.js/blob/main/LICENSE) file for more details.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const listen = (target, type, listener, options) => {
|
|
2
|
+
target.addEventListener(type, listener, options);
|
|
3
|
+
return () => target.removeEventListener(type, listener, options);
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const matches = (el, selector) => el.matches(selector);
|
|
7
|
+
|
|
8
|
+
const is_active_element_editable = () => {
|
|
9
|
+
const el = document.activeElement;
|
|
10
|
+
if (el === document.body || !el) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return matches(el, "input")
|
|
15
|
+
|| matches(el, "textarea")
|
|
16
|
+
|| el.isContentEditable;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const is_printable_key_pressed = ({ key, metaKey, ctrlKey, altKey }) =>
|
|
20
|
+
!metaKey && !ctrlKey && !altKey && /^[^\p{M}\p{Z}\p{C}]$/u.test(key);
|
|
21
|
+
|
|
22
|
+
function plugin({ directive }) {
|
|
23
|
+
directive("typegrab", (el, _, { cleanup }) => {
|
|
24
|
+
cleanup(
|
|
25
|
+
listen(document, "keydown", e => {
|
|
26
|
+
if (!is_active_element_editable() && is_printable_key_pressed(e)) {
|
|
27
|
+
el.focus();
|
|
28
|
+
}
|
|
29
|
+
}, { passive: true })
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { plugin as typegrab };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=(e,t)=>e.matches(t);function t({directive:t}){t("typegrab",(t,n,{cleanup:a})=>{var o,c,r;a(((o=document).addEventListener("keydown",c=n=>{!(()=>{const t=document.activeElement;return!(t===document.body||!t)&&(e(t,"input")||e(t,"textarea")||t.isContentEditable)})()&&(({key:e,metaKey:t,ctrlKey:n,altKey:a})=>!t&&!n&&!a&&/^[^\p{M}\p{Z}\p{C}]$/u.test(e))(n)&&t.focus()},r={passive:!0}),()=>o.removeEventListener("keydown",c,r)))})}export{t as typegrab};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const listen = (target, type, listener, options) => {
|
|
5
|
+
target.addEventListener(type, listener, options);
|
|
6
|
+
return () => target.removeEventListener(type, listener, options);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const matches = (el, selector) => el.matches(selector);
|
|
10
|
+
|
|
11
|
+
const is_active_element_editable = () => {
|
|
12
|
+
const el = document.activeElement;
|
|
13
|
+
if (el === document.body || !el) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return matches(el, "input")
|
|
18
|
+
|| matches(el, "textarea")
|
|
19
|
+
|| el.isContentEditable;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const is_printable_key_pressed = ({ key, metaKey, ctrlKey, altKey }) =>
|
|
23
|
+
!metaKey && !ctrlKey && !altKey && /^[^\p{M}\p{Z}\p{C}]$/u.test(key);
|
|
24
|
+
|
|
25
|
+
function plugin({ directive }) {
|
|
26
|
+
directive("typegrab", (el, _, { cleanup }) => {
|
|
27
|
+
cleanup(
|
|
28
|
+
listen(document, "keydown", e => {
|
|
29
|
+
if (!is_active_element_editable() && is_printable_key_pressed(e)) {
|
|
30
|
+
el.focus();
|
|
31
|
+
}
|
|
32
|
+
}, { passive: true })
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
document.addEventListener("alpine:init", () => { Alpine.plugin(plugin); });
|
|
38
|
+
|
|
39
|
+
})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(){"use strict";const e=(e,t)=>e.matches(t);function t({directive:t}){t("typegrab",(t,n,{cleanup:i})=>{var c,a,o;i(((c=document).addEventListener("keydown",a=n=>{!(()=>{const t=document.activeElement;return!(t===document.body||!t)&&(e(t,"input")||e(t,"textarea")||t.isContentEditable)})()&&(({key:e,metaKey:t,ctrlKey:n,altKey:i})=>!t&&!n&&!i&&/^[^\p{M}\p{Z}\p{C}]$/u.test(e))(n)&&t.focus()},o={passive:!0}),()=>c.removeEventListener("keydown",a,o)))})}document.addEventListener("alpine:init",()=>{Alpine.plugin(t)})}();
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ramstack/alpinegear-typegrab",
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "@ramstack/alpinegear-typegrab provides the x-typegrab Alpine.js directive, which automatically focuses an element when the user starts typing and no editable element is active.",
|
|
5
|
+
"author": "Rameel Burhan",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rameel/ramstack.alpinegear.js.git",
|
|
10
|
+
"directory": "src/plugins/typegrab"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"alpine.js",
|
|
14
|
+
"alpinejs",
|
|
15
|
+
"alpinejs-directive",
|
|
16
|
+
"alpinejs-plugin",
|
|
17
|
+
"keyboard",
|
|
18
|
+
"focus",
|
|
19
|
+
"ux"
|
|
20
|
+
],
|
|
21
|
+
"main": "alpinegear-typegrab.js",
|
|
22
|
+
"module": "alpinegear-typegrab.esm.js"
|
|
23
|
+
}
|