vue-intercept-plugin 1.0.0 → 1.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 +161 -143
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,28 +1,39 @@
|
|
|
1
1
|
# vue-intercept-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[中文](README_ZH.md) | **English**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Intercept DOM events with a single directive — clean and non-intrusive.**
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
> The plugin does **one thing only**: it catches an event and calls your function. Permission checking, validation, or any interception logic? That's entirely up to you.
|
|
10
10
|
|
|
11
|
-
### 🎯 Event Interception Mechanism
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## The Problem
|
|
17
|
+
|
|
18
|
+
You have a delete button. You want to check permissions before allowing the delete. The typical approach is:
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<el-button @click="handleDelete">Delete</el-button>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
const handleDelete = () => {
|
|
26
|
+
if (!checkPermission('delete')) {
|
|
27
|
+
ElMessage.warning('No permission')
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
deleteApi()
|
|
31
|
+
}
|
|
32
|
+
```
|
|
17
33
|
|
|
18
|
-
|
|
34
|
+
What if there are dozens of buttons, selects, and switches across your app? You end up repeating permission checks everywhere. This plugin lets you **signal visually** which actions need interception, and keeps your handler logic clean.
|
|
19
35
|
|
|
20
|
-
|
|
21
|
-
|--------|-------------|
|
|
22
|
-
| `v-intercept="handleFn"` | Pass a function directly. Receives the event object automatically |
|
|
23
|
-
| `v-intercept="[handleFn, arg1, arg2]"` | Array syntax. First item is the function, rest are arguments. **Event object auto-appended to the end** |
|
|
24
|
-
| `v-intercept:change="handleFn"` | Specify event type via directive argument. Defaults to `click` |
|
|
25
|
-
| `v-intercept:change="[handleFn, arg1]"` | Custom event + array arguments, work together |
|
|
36
|
+
---
|
|
26
37
|
|
|
27
38
|
## Installation
|
|
28
39
|
|
|
@@ -30,18 +41,18 @@ A lightweight Vue plugin providing the `v-intercept` custom directive for interc
|
|
|
30
41
|
npm install vue-intercept-plugin
|
|
31
42
|
```
|
|
32
43
|
|
|
33
|
-
##
|
|
44
|
+
## Register the Plugin
|
|
34
45
|
|
|
35
|
-
```
|
|
46
|
+
```ts
|
|
47
|
+
// Vue 3
|
|
36
48
|
import { createApp } from 'vue'
|
|
37
49
|
import VueInterceptPlugin from 'vue-intercept-plugin'
|
|
38
50
|
|
|
39
|
-
// Register plugin (no configuration needed)
|
|
40
51
|
const app = createApp(App)
|
|
41
52
|
app.use(VueInterceptPlugin)
|
|
42
53
|
```
|
|
43
54
|
|
|
44
|
-
```
|
|
55
|
+
```ts
|
|
45
56
|
// Vue 2
|
|
46
57
|
import Vue from 'vue'
|
|
47
58
|
import VueInterceptPlugin from 'vue-intercept-plugin'
|
|
@@ -49,17 +60,25 @@ import VueInterceptPlugin from 'vue-intercept-plugin'
|
|
|
49
60
|
Vue.use(VueInterceptPlugin)
|
|
50
61
|
```
|
|
51
62
|
|
|
52
|
-
|
|
63
|
+
That's it. No configuration needed.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Quick Start
|
|
68
|
+
|
|
69
|
+
Here's a complete example with both template and script:
|
|
70
|
+
|
|
71
|
+
```html
|
|
53
72
|
<template>
|
|
54
|
-
<!--
|
|
73
|
+
<!-- ① Direct function: intercepts click by default -->
|
|
55
74
|
<el-button v-intercept="handleDelete" type="danger">Delete</el-button>
|
|
56
75
|
|
|
57
|
-
<!-- Array syntax:
|
|
76
|
+
<!-- ② Array syntax: extra arguments, event auto-appended -->
|
|
58
77
|
<el-button v-intercept="[handleDeleteById, 1001]" type="warning">
|
|
59
78
|
Delete Order #1001
|
|
60
79
|
</el-button>
|
|
61
80
|
|
|
62
|
-
<!-- Custom event type
|
|
81
|
+
<!-- ③ Custom event type: intercepts "change" instead of "click" -->
|
|
63
82
|
<select v-intercept:change="handleChange">
|
|
64
83
|
<option value="">Select...</option>
|
|
65
84
|
<option value="1">Option 1</option>
|
|
@@ -68,95 +87,126 @@ Vue.use(VueInterceptPlugin)
|
|
|
68
87
|
</template>
|
|
69
88
|
|
|
70
89
|
<script setup lang="ts">
|
|
71
|
-
|
|
72
|
-
|
|
90
|
+
// ① Called as: handleDelete(event)
|
|
91
|
+
// You check permission inside — if not allowed, just return
|
|
92
|
+
const handleDelete = (event: MouseEvent) => {
|
|
93
|
+
if (!checkPermission('delete')) {
|
|
73
94
|
ElMessage.warning('No permission')
|
|
74
|
-
return
|
|
95
|
+
return // ← intercepted
|
|
75
96
|
}
|
|
76
|
-
|
|
97
|
+
deleteApi()
|
|
77
98
|
}
|
|
78
99
|
|
|
100
|
+
// ② Called as: handleDeleteById(1001, event)
|
|
101
|
+
// The plugin appends the event object as the last argument automatically
|
|
79
102
|
const handleDeleteById = (id: number, event: MouseEvent) => {
|
|
80
|
-
if (!
|
|
103
|
+
if (!checkPermission('delete')) return
|
|
81
104
|
deleteApi(id)
|
|
82
105
|
}
|
|
83
106
|
|
|
107
|
+
// ③ Called as: handleChange(event)
|
|
84
108
|
const handleChange = (event: Event) => {
|
|
85
|
-
if (!
|
|
109
|
+
if (!checkPermission('change')) return
|
|
86
110
|
const value = (event.target as HTMLSelectElement).value
|
|
87
|
-
//
|
|
111
|
+
// handle selection...
|
|
88
112
|
}
|
|
89
113
|
</script>
|
|
90
114
|
```
|
|
91
115
|
|
|
92
|
-
|
|
116
|
+
> **Key idea**: The plugin does not block anything. It simply calls your function. **You** decide whether to proceed or return early.
|
|
93
117
|
|
|
94
|
-
|
|
118
|
+
---
|
|
95
119
|
|
|
96
|
-
|
|
120
|
+
## Directive Syntax Reference
|
|
97
121
|
|
|
98
|
-
###
|
|
122
|
+
### Event Type (default: `click`)
|
|
99
123
|
|
|
100
|
-
|
|
124
|
+
| Syntax | Fires on |
|
|
125
|
+
|--------|----------|
|
|
126
|
+
| `v-intercept="handler"` | `click` (default) |
|
|
127
|
+
| `v-intercept:click="handler"` | click |
|
|
128
|
+
| `v-intercept:change="handler"` | change |
|
|
129
|
+
| `v-intercept:submit="handler"` | form submit |
|
|
130
|
+
| `v-intercept:contextmenu="handler"` | right-click |
|
|
131
|
+
| `v-intercept:dblclick="handler"` | double-click |
|
|
132
|
+
| `v-intercept:dragstart="handler"` | drag start |
|
|
133
|
+
| `v-intercept:copy="handler"` | copy |
|
|
134
|
+
| `v-intercept:<any-event>="handler"` | any native DOM event |
|
|
101
135
|
|
|
102
|
-
|
|
103
|
-
|--------|-------------|
|
|
104
|
-
| `v-intercept="handler"` | Default: click |
|
|
105
|
-
| `v-intercept:click="handler"` | Explicit click |
|
|
106
|
-
| `v-intercept:change="handler"` | change event |
|
|
107
|
-
| `v-intercept:submit="handler"` | Form submit |
|
|
108
|
-
| `v-intercept:contextmenu="handler"` | Right-click menu |
|
|
109
|
-
| `v-intercept:dblclick="handler"` | Double-click |
|
|
110
|
-
| `v-intercept:dragstart="handler"` | Drag start |
|
|
111
|
-
| `v-intercept:copy="handler"` | Copy |
|
|
112
|
-
| `...` | Any native DOM event |
|
|
136
|
+
### Handler Value
|
|
113
137
|
|
|
114
|
-
|
|
138
|
+
| Format | Example | Your function receives |
|
|
139
|
+
|--------|---------|----------------------|
|
|
140
|
+
| `handler` | `v-intercept="handleDelete"` | `handleDelete(event)` |
|
|
141
|
+
| `[handler, ...args]` | `v-intercept="[handleDelete, 1001]"` | `handleDelete(1001, event)` |
|
|
142
|
+
| `[handler, ...args]` | `v-intercept="[handleDelete, 1001, 'abc']"` | `handleDelete(1001, 'abc', event)` |
|
|
143
|
+
| `[handler]` | `v-intercept="[handleDelete]"` | `handleDelete(event)` (same as direct) |
|
|
115
144
|
|
|
116
|
-
|
|
117
|
-
|--------|---------|-------------|
|
|
118
|
-
| `handler` | `v-intercept="handleDelete"` | Pass function directly. Receives event object |
|
|
119
|
-
| `[handler, ...args]` | `v-intercept="[handleDelete, 1001]"` | Array syntax. **Event object auto-appended to arguments** |
|
|
120
|
-
| `[handler]` | `v-intercept="[handleDelete]"` | Function only, no extra args. Equivalent to passing function directly |
|
|
145
|
+
> **Rule**: The event object is **always appended as the last argument** when using array syntax.
|
|
121
146
|
|
|
122
|
-
|
|
147
|
+
---
|
|
123
148
|
|
|
124
|
-
|
|
125
|
-
|----------------|-------------------------|
|
|
126
|
-
| `v-intercept="handle"` | `handle(event)` |
|
|
127
|
-
| `v-intercept="[handle, id]"` | `handle(id, event)` |
|
|
128
|
-
| `v-intercept="[handle, id, name]"` | `handle(id, name, event)` |
|
|
129
|
-
| `v-intercept:change="handle"` | `handle(event)` |
|
|
130
|
-
| `v-intercept:change="[handle, id]"` | `handle(id, event)` |
|
|
149
|
+
## Common Scenarios
|
|
131
150
|
|
|
132
|
-
|
|
151
|
+
> Examples below use **Element Plus** components (`el-button`, `el-select`, `el-switch`, etc.). This plugin has no UI library dependency — native `<button>`, `<select>`, etc. work just as well.
|
|
133
152
|
|
|
134
|
-
### Permission-Guarded
|
|
153
|
+
### Permission-Guarded Button
|
|
135
154
|
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
<el-button v-intercept="
|
|
155
|
+
```html
|
|
156
|
+
<!-- Element Plus component -->
|
|
157
|
+
<el-button v-intercept="handleApprove" type="primary">Approve</el-button>
|
|
139
158
|
```
|
|
140
159
|
|
|
141
|
-
```
|
|
142
|
-
const
|
|
143
|
-
if (!
|
|
144
|
-
ElMessage.warning('
|
|
160
|
+
```ts
|
|
161
|
+
const handleApprove = (event: MouseEvent) => {
|
|
162
|
+
if (!userHasRole('admin')) {
|
|
163
|
+
ElMessage.warning('Admins only')
|
|
145
164
|
return
|
|
146
165
|
}
|
|
147
|
-
|
|
166
|
+
approveApi()
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Permission-Guarded Button with Extra Params
|
|
171
|
+
|
|
172
|
+
```html
|
|
173
|
+
<el-button v-intercept="[handleApproveById, orderId]" type="primary">
|
|
174
|
+
Approve
|
|
175
|
+
</el-button>
|
|
176
|
+
<!-- Element Plus component, same as above -->
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
const handleApproveById = (id: string, event: MouseEvent) => {
|
|
181
|
+
if (!userHasRole('admin')) return
|
|
182
|
+
approveApi(id)
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Double-Click Edit Guard
|
|
187
|
+
|
|
188
|
+
```html
|
|
189
|
+
<div v-intercept:dblclick="[handleEdit, item.id]">
|
|
190
|
+
{{ item.name }}
|
|
191
|
+
</div>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
const handleEdit = (id: number, event: MouseEvent) => {
|
|
196
|
+
if (!canEdit) return
|
|
197
|
+
enterEditMode(id)
|
|
148
198
|
}
|
|
149
199
|
```
|
|
150
200
|
|
|
151
201
|
### Right-Click Menu Guard
|
|
152
202
|
|
|
153
|
-
```
|
|
154
|
-
<div v-intercept:contextmenu="handleContextMenu"
|
|
155
|
-
Right-click
|
|
203
|
+
```html
|
|
204
|
+
<div v-intercept:contextmenu="handleContextMenu">
|
|
205
|
+
Right-click here
|
|
156
206
|
</div>
|
|
157
207
|
```
|
|
158
208
|
|
|
159
|
-
```
|
|
209
|
+
```ts
|
|
160
210
|
const handleContextMenu = (event: MouseEvent) => {
|
|
161
211
|
event.preventDefault()
|
|
162
212
|
if (!hasRightClickPermission) return
|
|
@@ -166,18 +216,18 @@ const handleContextMenu = (event: MouseEvent) => {
|
|
|
166
216
|
|
|
167
217
|
### Select Change Guard
|
|
168
218
|
|
|
169
|
-
```
|
|
170
|
-
|
|
219
|
+
```html
|
|
220
|
+
<!-- Element Plus components -->
|
|
221
|
+
<el-select v-intercept:change="[handleRoleChange, 'User Mgmt']">
|
|
171
222
|
<el-option label="Admin" value="admin" />
|
|
172
223
|
<el-option label="Editor" value="editor" />
|
|
173
|
-
<el-option label="Guest" value="guest" />
|
|
174
224
|
</el-select>
|
|
175
225
|
```
|
|
176
226
|
|
|
177
|
-
```
|
|
227
|
+
```ts
|
|
178
228
|
const handleRoleChange = (section: string, event: Event) => {
|
|
179
229
|
if (!canChangeRole) {
|
|
180
|
-
ElMessage.warning(`Cannot change role for
|
|
230
|
+
ElMessage.warning(`Cannot change role for ${section}`)
|
|
181
231
|
return
|
|
182
232
|
}
|
|
183
233
|
updateRole()
|
|
@@ -186,108 +236,76 @@ const handleRoleChange = (section: string, event: Event) => {
|
|
|
186
236
|
|
|
187
237
|
### Toggle Switch Guard
|
|
188
238
|
|
|
189
|
-
```
|
|
190
|
-
|
|
239
|
+
```html
|
|
240
|
+
<!-- Element Plus component -->
|
|
241
|
+
<el-switch v-intercept:change="[handleToggle, 'Export']" />
|
|
191
242
|
```
|
|
192
243
|
|
|
193
|
-
```
|
|
194
|
-
const
|
|
244
|
+
```ts
|
|
245
|
+
const handleToggle = (feature: string, event: Event) => {
|
|
195
246
|
const checked = (event.target as HTMLInputElement).checked
|
|
196
247
|
if (checked && !canEnableFeature(feature)) {
|
|
197
|
-
ElMessage.warning(`Cannot enable
|
|
248
|
+
ElMessage.warning(`Cannot enable ${feature}`)
|
|
198
249
|
return
|
|
199
250
|
}
|
|
200
251
|
toggleFeature(feature, checked)
|
|
201
252
|
}
|
|
202
253
|
```
|
|
203
254
|
|
|
204
|
-
### Double-Click Edit Guard
|
|
205
|
-
|
|
206
|
-
```vue
|
|
207
|
-
<div v-intercept:dblclick="[handleDoubleClick, item.id]">
|
|
208
|
-
{{ item.name }}
|
|
209
|
-
</div>
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
const handleDoubleClick = (id: number, event: MouseEvent) => {
|
|
214
|
-
if (!canEdit) return
|
|
215
|
-
enterEditMode(id)
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
255
|
### Drag Guard
|
|
220
256
|
|
|
221
|
-
```
|
|
222
|
-
<div v-intercept:dragstart="[
|
|
257
|
+
```html
|
|
258
|
+
<div v-intercept:dragstart="[handleDrag, file.name]" draggable="true">
|
|
223
259
|
{{ file.name }}
|
|
224
260
|
</div>
|
|
225
261
|
```
|
|
226
262
|
|
|
227
|
-
```
|
|
228
|
-
const
|
|
263
|
+
```ts
|
|
264
|
+
const handleDrag = (name: string, event: DragEvent) => {
|
|
229
265
|
if (!canDrag) {
|
|
230
266
|
event.preventDefault()
|
|
231
267
|
return
|
|
232
268
|
}
|
|
233
|
-
event.dataTransfer?.setData('text/plain',
|
|
269
|
+
event.dataTransfer?.setData('text/plain', name)
|
|
234
270
|
}
|
|
235
271
|
```
|
|
236
272
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
| File | Format | Description |
|
|
240
|
-
|------|--------|-------------|
|
|
241
|
-
| `dist/vue-intercept-plugin.cjs.js` | CJS | CommonJS for Node.js require |
|
|
242
|
-
| `dist/vue-intercept-plugin.esm.js` | ESM | ES Module, Tree-shakable |
|
|
273
|
+
---
|
|
243
274
|
|
|
244
275
|
## Technical Details
|
|
245
276
|
|
|
246
|
-
### Why Not
|
|
277
|
+
### Why a Directive, Not a Wrapper Component?
|
|
247
278
|
|
|
248
|
-
|
|
249
|
-
-
|
|
250
|
-
-
|
|
251
|
-
-
|
|
279
|
+
If you wrap `el-button` into a `<PermissionButton>`, you have to:
|
|
280
|
+
- Forward every prop (size, type, icon, loading, disabled, ...)
|
|
281
|
+
- Do the same for `<el-select>`, `<el-switch>`, `<el-input>`, etc.
|
|
282
|
+
- Repeat the work for every UI library
|
|
252
283
|
|
|
253
|
-
Vue custom
|
|
284
|
+
A **Vue custom directive** hooks directly into the native DOM event — zero wrapping, one line of code.
|
|
254
285
|
|
|
255
|
-
### Vue 2 / Vue 3
|
|
286
|
+
### Vue 2 / Vue 3 Auto-Detection
|
|
256
287
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
| Vue Version | Bind Hook | Update Hook | Unbind Hook |
|
|
260
|
-
|-------------|-----------|-------------|-------------|
|
|
288
|
+
| Vue | Bind Hook | Update Hook | Unbind Hook |
|
|
289
|
+
|-----|-----------|-------------|-------------|
|
|
261
290
|
| Vue 3 | `mounted` | `updated` | `unmounted` |
|
|
262
291
|
| Vue 2 | `bind` | `update` | `unbind` |
|
|
263
292
|
|
|
264
293
|
### Memory Safety
|
|
265
294
|
|
|
266
|
-
- Old listeners are removed before re-binding
|
|
267
|
-
- Listeners are
|
|
268
|
-
- Different event types use separate storage
|
|
269
|
-
|
|
270
|
-
## Development
|
|
271
|
-
|
|
272
|
-
```bash
|
|
273
|
-
# Install dependencies
|
|
274
|
-
npm install
|
|
295
|
+
- Old listeners are removed before re-binding (prevents duplicates)
|
|
296
|
+
- Listeners are cleaned up on component unmount
|
|
297
|
+
- Different event types use separate storage (isolated, no collisions)
|
|
275
298
|
|
|
276
|
-
|
|
277
|
-
npm run build
|
|
299
|
+
---
|
|
278
300
|
|
|
279
|
-
|
|
280
|
-
npm test
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
## Testing
|
|
301
|
+
## Build Artifacts
|
|
284
302
|
|
|
285
|
-
|
|
303
|
+
| File | Format |
|
|
304
|
+
|------|--------|
|
|
305
|
+
| `dist/vue-intercept-plugin.cjs.js` | CommonJS |
|
|
306
|
+
| `dist/vue-intercept-plugin.esm.js` | ES Module (tree-shakable) |
|
|
286
307
|
|
|
287
|
-
- `resolveHandler` — direct function passthrough, array argument forwarding with event auto-append, multiple arguments, invalid value warnings, null/undefined edge cases
|
|
288
|
-
- `bindHandler / unbindHandler` — click binding, change binding, auto-replacement on re-bind, unbind cleanup, event type isolation, contextmenu/dblclick special events
|
|
289
|
-
- Plugin install — Vue3/Vue2 directive registration, correct hook names, mounted event binding, custom event types, array arguments with event auto-append, unmounted cleanup, graceful handling of invalid values
|
|
290
308
|
|
|
291
309
|
## License
|
|
292
310
|
|
|
293
|
-
MIT
|
|
311
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vue-intercept-plugin",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "v-intercept
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "A lightweight Vue plugin providing v-intercept custom directive — intercept DOM events with a single line of code, supports any event type, compatible with Vue 2 & Vue 3.",
|
|
5
5
|
"main": "dist/vue-intercept-plugin.cjs.js",
|
|
6
6
|
"module": "dist/vue-intercept-plugin.esm.js",
|
|
7
7
|
"files": [
|