vuse-directive 0.0.3
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 +114 -0
- package/dist/directives/ThrottleClick.d.ts +4 -0
- package/dist/index.d.ts +3 -0
- package/dist/vue-directive.js +63 -0
- package/dist/vue-directive.umd.cjs +1 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# vue-directive
|
|
2
|
+
|
|
3
|
+
A collection of Vue 3 custom directives
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install vuse-directive
|
|
9
|
+
# or
|
|
10
|
+
pnpm add vuse-directive
|
|
11
|
+
# or
|
|
12
|
+
yarn add vuse-directive
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Getting Started
|
|
16
|
+
|
|
17
|
+
### Global Registration
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createApp } from 'vue'
|
|
21
|
+
import App from './App.vue'
|
|
22
|
+
import { throttleClick } from 'vuse-directive'
|
|
23
|
+
|
|
24
|
+
const app = createApp(App)
|
|
25
|
+
app.directive('throttle-click', throttleClick)
|
|
26
|
+
app.mount('#app')
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### On-demand Import
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { throttleClick } from 'vuse-directive'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Directives
|
|
38
|
+
|
|
39
|
+
### `v-throttle-click` — Throttled Click
|
|
40
|
+
|
|
41
|
+
Throttles click events to prevent repeated triggers within a short period. Supports custom delay, async lock, trailing call, and Vue modifier pass-through.
|
|
42
|
+
|
|
43
|
+
#### Basic Usage
|
|
44
|
+
|
|
45
|
+
```vue
|
|
46
|
+
<!-- Default throttle interval: 300ms -->
|
|
47
|
+
<button v-throttle-click="handleClick">Submit</button>
|
|
48
|
+
|
|
49
|
+
<!-- Custom interval (in ms) -->
|
|
50
|
+
<button v-throttle-click:500="handleClick">Submit</button>
|
|
51
|
+
|
|
52
|
+
<!-- Interval 0: no throttle, modifiers still apply -->
|
|
53
|
+
<button v-throttle-click:0="handleClick">Submit</button>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### Modifiers
|
|
57
|
+
|
|
58
|
+
| Modifier | Description |
|
|
59
|
+
|----------|-------------|
|
|
60
|
+
| `.once` | Fire only once; all subsequent clicks are ignored |
|
|
61
|
+
| `.trailing` | If a click is blocked during cooldown, it fires once after the cooldown ends |
|
|
62
|
+
| `.async` | Async mode: new clicks are blocked until the previous callback's Promise resolves/rejects |
|
|
63
|
+
| `.right` | Listen to `contextmenu` (right-click) instead of `click` |
|
|
64
|
+
| `.capture` | Use capture phase for the event listener |
|
|
65
|
+
| `.passive` | Use passive listener mode for better scroll performance |
|
|
66
|
+
| `.stop` | Stop event propagation (passed to Vue's `withModifiers`) |
|
|
67
|
+
| `.prevent` | Prevent default behavior (passed to Vue's `withModifiers`) |
|
|
68
|
+
| `.self` | Only trigger when the event target is the element itself |
|
|
69
|
+
|
|
70
|
+
#### Examples
|
|
71
|
+
|
|
72
|
+
```vue
|
|
73
|
+
<script setup lang="ts">
|
|
74
|
+
async function submitForm() {
|
|
75
|
+
await fetch('/api/submit', { method: 'POST' })
|
|
76
|
+
}
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<template>
|
|
80
|
+
<!-- Async + trailing: blocks repeated clicks during async call,
|
|
81
|
+
fires the last blocked click after the call completes -->
|
|
82
|
+
<button v-throttle-click:1000.async.trailing="submitForm">
|
|
83
|
+
Submit
|
|
84
|
+
</button>
|
|
85
|
+
|
|
86
|
+
<!-- Fire once only -->
|
|
87
|
+
<button v-throttle-click.once="handleClick">Click once</button>
|
|
88
|
+
|
|
89
|
+
<!-- Throttle right-click -->
|
|
90
|
+
<div v-throttle-click.right="openMenu">Right-click area</div>
|
|
91
|
+
</template>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### Callback Signature
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
type ClickHandler = (event: MouseEvent, ...args: unknown[]) => any
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The binding value receives a `MouseEvent` as the first argument:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
function handleClick(e: MouseEvent) {
|
|
104
|
+
console.log('clicked', e)
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
In async mode, return a `Promise` to activate the async lock:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
async function handleAsync(e: MouseEvent) {
|
|
112
|
+
await fetch('/api/submit', { method: 'POST' })
|
|
113
|
+
}
|
|
114
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { withModifiers as d } from "vue";
|
|
2
|
+
const f = {
|
|
3
|
+
mounted(t, e) {
|
|
4
|
+
t._latestBinding = e, t._throttleTimer = null, t._hasCalledOnce = !1, t._trailingEvent = null, t._asyncPending = !1, t._unmounted = !1;
|
|
5
|
+
const s = Object.getOwnPropertyNames(
|
|
6
|
+
e.modifiers
|
|
7
|
+
);
|
|
8
|
+
t._throttleHandler = d((i) => {
|
|
9
|
+
const { modifiers: n, value: r } = t._latestBinding;
|
|
10
|
+
if (n.once && t._hasCalledOnce)
|
|
11
|
+
return;
|
|
12
|
+
if (n.async && t._asyncPending) {
|
|
13
|
+
n.trailing && (t._trailingEvent = i);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const a = typeof t._latestBinding.arg > "u" ? 300 : Number(t._latestBinding.arg);
|
|
17
|
+
if (a > 0 && t._throttleTimer) {
|
|
18
|
+
n.trailing && (t._trailingEvent = i);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const o = (_) => {
|
|
22
|
+
t._hasCalledOnce = !0, t._trailingEvent = null, n.async ? (t._asyncPending = !0, Promise.resolve(r == null ? void 0 : r(_)).finally(() => {
|
|
23
|
+
t._unmounted || (t._asyncPending = !1, n.trailing && t._trailingEvent && t._throttleHandler(t._trailingEvent));
|
|
24
|
+
})) : r == null || r(_);
|
|
25
|
+
};
|
|
26
|
+
if (a === 0) {
|
|
27
|
+
o(i);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
o(i), t._throttleTimer = setTimeout(() => {
|
|
31
|
+
t._throttleTimer = null, !n.async && n.trailing && t._trailingEvent && (t._throttleHandler(t._trailingEvent), t._trailingEvent = null);
|
|
32
|
+
}, a);
|
|
33
|
+
}, s), t._eventName = e.modifiers.right ? "contextmenu" : "click", t._listenerOptions = {
|
|
34
|
+
capture: e.modifiers.capture ?? !1,
|
|
35
|
+
passive: e.modifiers.passive ?? !1
|
|
36
|
+
}, t.addEventListener(
|
|
37
|
+
t._eventName,
|
|
38
|
+
t._throttleHandler,
|
|
39
|
+
t._listenerOptions
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
updated(t, e) {
|
|
43
|
+
const s = e.modifiers.capture ?? !1, i = e.modifiers.passive ?? !1, n = t._listenerOptions;
|
|
44
|
+
(s !== n.capture || i !== n.passive) && (t.removeEventListener(t._eventName, t._throttleHandler, n), t._listenerOptions = {
|
|
45
|
+
capture: s,
|
|
46
|
+
passive: i
|
|
47
|
+
}, t.addEventListener(
|
|
48
|
+
t._eventName,
|
|
49
|
+
t._throttleHandler,
|
|
50
|
+
t._listenerOptions
|
|
51
|
+
)), t._latestBinding = e;
|
|
52
|
+
},
|
|
53
|
+
unmounted(t) {
|
|
54
|
+
t.removeEventListener(
|
|
55
|
+
t._eventName,
|
|
56
|
+
t._throttleHandler,
|
|
57
|
+
t._listenerOptions
|
|
58
|
+
), t._throttleTimer && clearTimeout(t._throttleTimer), t._unmounted = !0, t._trailingEvent = null;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
export {
|
|
62
|
+
f as throttleClick
|
|
63
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(i,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],a):(i=typeof globalThis<"u"?globalThis:i||self,a(i.VueDirective={},i.Vue))})(this,function(i,a){"use strict";const f={mounted(t,n){t._latestBinding=n,t._throttleTimer=null,t._hasCalledOnce=!1,t._trailingEvent=null,t._asyncPending=!1,t._unmounted=!1;const o=Object.getOwnPropertyNames(n.modifiers);t._throttleHandler=a.withModifiers(r=>{const{modifiers:e,value:s}=t._latestBinding;if(e.once&&t._hasCalledOnce)return;if(e.async&&t._asyncPending){e.trailing&&(t._trailingEvent=r);return}const d=typeof t._latestBinding.arg>"u"?300:Number(t._latestBinding.arg);if(d>0&&t._throttleTimer){e.trailing&&(t._trailingEvent=r);return}const _=u=>{t._hasCalledOnce=!0,t._trailingEvent=null,e.async?(t._asyncPending=!0,Promise.resolve(s==null?void 0:s(u)).finally(()=>{t._unmounted||(t._asyncPending=!1,e.trailing&&t._trailingEvent&&t._throttleHandler(t._trailingEvent))})):s==null||s(u)};if(d===0){_(r);return}_(r),t._throttleTimer=setTimeout(()=>{t._throttleTimer=null,!e.async&&e.trailing&&t._trailingEvent&&(t._throttleHandler(t._trailingEvent),t._trailingEvent=null)},d)},o),t._eventName=n.modifiers.right?"contextmenu":"click",t._listenerOptions={capture:n.modifiers.capture??!1,passive:n.modifiers.passive??!1},t.addEventListener(t._eventName,t._throttleHandler,t._listenerOptions)},updated(t,n){const o=n.modifiers.capture??!1,r=n.modifiers.passive??!1,e=t._listenerOptions;(o!==e.capture||r!==e.passive)&&(t.removeEventListener(t._eventName,t._throttleHandler,e),t._listenerOptions={capture:o,passive:r},t.addEventListener(t._eventName,t._throttleHandler,t._listenerOptions)),t._latestBinding=n},unmounted(t){t.removeEventListener(t._eventName,t._throttleHandler,t._listenerOptions),t._throttleTimer&&clearTimeout(t._throttleTimer),t._unmounted=!0,t._trailingEvent=null}};i.throttleClick=f,Object.defineProperty(i,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vuse-directive",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Vue 3 directives collection",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/vue-directive.umd.cjs",
|
|
7
|
+
"module": "./dist/vue-directive.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/vue-directive.js",
|
|
12
|
+
"require": "./dist/vue-directive.umd.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "jest",
|
|
21
|
+
"build": "vite build",
|
|
22
|
+
"dev": "vite",
|
|
23
|
+
"format": "biome format --write .",
|
|
24
|
+
"format:check": "biome format .",
|
|
25
|
+
"prepublishOnly": "npm run build",
|
|
26
|
+
"release": "node scripts/publish.js"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"vue": "^3.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@biomejs/biome": "^2.0.0",
|
|
33
|
+
"@types/jest": "^30.0.0",
|
|
34
|
+
"@types/node": "^25.3.0",
|
|
35
|
+
"@vue/test-utils": "^2.4.6",
|
|
36
|
+
"jest": "^30.2.0",
|
|
37
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
38
|
+
"ts-jest": "^29.4.6",
|
|
39
|
+
"ts-node": "^10.9.2",
|
|
40
|
+
"typescript": "^5.3.3",
|
|
41
|
+
"vite": "^5.0.0",
|
|
42
|
+
"vite-plugin-dts": "^3.6.0",
|
|
43
|
+
"vue": "^3.4.0",
|
|
44
|
+
"vue-tsc": "^1.8.0"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"vue",
|
|
48
|
+
"vue3",
|
|
49
|
+
"directive",
|
|
50
|
+
"directives"
|
|
51
|
+
],
|
|
52
|
+
"sideEffects": false,
|
|
53
|
+
"author": "Stafan Hulk",
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "https://github.com/stafanhulk/vue-directive.git"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://github.com/stafanhulk/vue-directive#readme",
|
|
60
|
+
"bugs": {
|
|
61
|
+
"url": "https://github.com/stafanhulk/vue-directive/issues"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=18"
|
|
65
|
+
}
|
|
66
|
+
}
|