├── .babelrc
├── .circleci
└── config.yml
├── .github
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── question.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
├── _internals.js
├── debounce.js
├── directive.js
└── index.js
├── tests
└── test.js
└── types
└── index.d.ts
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": "defaults"
7 | }
8 | ]
9 | ],
10 | "exclude": [
11 | "node_modules/**",
12 | "dist"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 | jobs:
3 | build:
4 | docker:
5 | - image: circleci/node:12
6 |
7 | working_directory: ~/vue-debounce
8 |
9 | steps:
10 | - checkout
11 |
12 | - run:
13 | name: install-deps
14 | command: npm ci
15 |
16 | # lint
17 | - run:
18 | name: lint
19 | command: npm run lint
20 | # run tests and report coverage
21 | - run:
22 | name: test
23 | command: npm t
24 | workflows:
25 | version: 2
26 | main:
27 | jobs:
28 | - build:
29 | filters:
30 | branches:
31 | only:
32 | - master
33 | - development
34 | ignore: gh-pages
35 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at dustinh17@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | I am open to, and grateful for, any contributions made to this project.
4 |
5 | Please abide by the linter setup which uses [Standardjs](http://standardjs.com) rules
6 |
7 | Please make sure all PRs are pointed at and processed through the main Development branch
8 |
9 | ## Commit Messages
10 |
11 | Please write and use meaningful and helpful commit messages for your contributions and changes.
12 |
13 | ## Testing
14 |
15 | All changes are expected to continue to pass the tests in place.
16 |
17 | If you are adding new functionality to the library you are expected to also unit test and create appropriate testing for your additions
18 |
19 | To run all tests use `npm t`
20 |
21 | If you want to test only your functionality feel free to change the test script to your `.js` file but **please** remember to change it back to `*.js` and re run `npm t` afterwards!
22 |
23 | ## Documentation
24 |
25 | If you are editing or adding functionality please make sure the readme reflects youyr changes accordingly.
26 |
27 | ## Developing
28 |
29 | - Run unit tests with `npm test`
30 | - You can use `npm run watch` to run the rollup watcher for the source code as you use the html file for testing
31 | - Before opening a PR make sure you do `npm run build` to generate new build files
32 |
33 | ## Releasing
34 |
35 | Currently the CHANGELOG is updated manually before each release. You can document your changes here, or I will document them using your provided commit messages.
36 |
37 | Releases follow standard [semantic versioning](https://semver.org/).
38 |
39 | I will handle publishing to npm for now after your PR is merged in
40 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help vue-debounce improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **Version of Vue**
11 | Which version of vue are you on? (2 or 3)
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Pass it '...'
17 | 3. See error
18 |
19 | **Expected behavior**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Desktop (please complete the following information):**
23 | - Browser [e.g. chrome, safari, node]
24 |
25 | **Screenshots**
26 | If applicable/needed, add screenshots to help explain your problem.
27 |
28 | **Additional context**
29 | Add any other context about the problem here. (If none needed feel free to remove this section)
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Question
3 | about: Unsure of how to use something or just need general help?
4 |
5 | ---
6 |
7 | **Version of Vue**
8 | Which version of vue are you using?
9 |
10 | **Browser**
11 | What browser are you using?
12 |
13 | Type out the question to the best of your ability!
14 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | > Delete any section you're not using and please delete this line too
2 |
3 | ## Breaking Changes
4 |
5 | - List of breaking changes if any
6 |
7 | ## New
8 |
9 | - List of new changes if any
10 |
11 | ## Improved
12 |
13 | - List of improved changes if any
14 |
15 | ## Fixed
16 |
17 | - List of bug or functionality fixes if any
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Stupid Mac Files
2 |
3 | .DS_Store
4 |
5 | test.html
6 | test-func.html
7 | test-vue3.html
8 | test-vuetify.html
9 |
10 | dist/
11 |
12 | # Logs
13 | logs
14 | *.log
15 | npm-debug.log*
16 | yarn-debug.log*
17 | yarn-error.log*
18 |
19 | # Runtime data
20 | pids
21 | *.pid
22 | *.seed
23 | *.pid.lock
24 |
25 | # Directory for instrumented libs generated by jscoverage/JSCover
26 | lib-cov
27 |
28 | # Coverage directory used by tools like istanbul
29 | coverage
30 |
31 | # nyc test coverage
32 | .nyc_output
33 |
34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
35 | .grunt
36 |
37 | # Bower dependency directory (https://bower.io/)
38 | bower_components
39 |
40 | # node-waf configuration
41 | .lock-wscript
42 |
43 | # Compiled binary addons (https://nodejs.org/api/addons.html)
44 | build/Release
45 |
46 | # Dependency directories
47 | node_modules/
48 | jspm_packages/
49 |
50 | # TypeScript v1 declaration files
51 | typings/
52 |
53 | # Optional npm cache directory
54 | .npm
55 |
56 | # Optional eslint cache
57 | .eslintcache
58 |
59 | # Optional REPL history
60 | .node_repl_history
61 |
62 | # Output of 'npm pack'
63 | *.tgz
64 |
65 | # Yarn Integrity file
66 | .yarn-integrity
67 |
68 | # dotenv environment variables file
69 | .env
70 |
71 | # next.js build output
72 | .next
73 |
74 | exp.js
75 |
76 | # IDE #
77 | .idea/
78 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Stupid Mac Files
2 |
3 | .DS_Store
4 |
5 | test.html
6 | test-vue3.html
7 |
8 | rollup.config.js
9 | exp.js
10 |
11 | # Logs
12 | logs
13 | *.log
14 | npm-debug.log*
15 | yarn-debug.log*
16 | yarn-error.log*
17 |
18 | # Runtime data
19 | pids
20 | *.pid
21 | *.seed
22 | *.pid.lock
23 |
24 | # Directory for instrumented libs generated by jscoverage/JSCover
25 | lib-cov
26 |
27 | # Coverage directory used by tools like istanbul
28 | coverage
29 |
30 | # nyc test coverage
31 | .nyc_output
32 |
33 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
34 | .grunt
35 |
36 | # Bower dependency directory (https://bower.io/)
37 | bower_components
38 |
39 | # node-waf configuration
40 | .lock-wscript
41 |
42 | # Compiled binary addons (https://nodejs.org/api/addons.html)
43 | build/Release
44 |
45 | # Dependency directories
46 | node_modules/
47 | jspm_packages/
48 |
49 | # TypeScript v1 declaration files
50 | typings/
51 |
52 | # Optional npm cache directory
53 | .npm
54 |
55 | # Optional eslint cache
56 | .eslintcache
57 |
58 | # Optional REPL history
59 | .node_repl_history
60 |
61 | # Output of 'npm pack'
62 | *.tgz
63 |
64 | # Yarn Integrity file
65 | .yarn-integrity
66 |
67 | # dotenv environment variables file
68 | .env
69 |
70 | # next.js build output
71 | .next
72 |
73 | exp.js
74 |
75 | # IDE #
76 | .idea/
77 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## v5.0.1
4 |
5 | ### Fixed
6 |
7 | - Package.json now contains a `main` and fixes #89 ([@andreclemente](https://github.com/andreclemente))
8 |
9 | ## v5.0.0
10 |
11 | ### Breaking Changes
12 |
13 | - Remove support for Vue 2
14 | - If you need to continue supporting Vue2 please switch to the [vue2-debounce](https://github.com/dhershman1/vue2-debounce) package or stay of v4 of this one
15 | - vue-debounce is now registered as a ES `module` instead of a cjs package
16 | - This helps the transition over to Vue3
17 | - This will help keep the typing system clean
18 |
19 | ### Fixed
20 |
21 | - README now reflects how to use vue-debounce with only vue3
22 |
23 | ## v4.0.1
24 |
25 | ### Improved
26 |
27 | - Added PR #79 for export support
28 | - Added PR #82 for fixed typings
29 |
30 | ### Chores
31 |
32 | - Dependency updates
33 |
34 | ## v4.0.0
35 |
36 | ### Breaking Changes
37 |
38 | - Removed `getDirective` flow there is now a dedicated import for vue 2, and vue 3
39 |
40 | ### Improved
41 |
42 | - Made usage easier, instead of trying to support complex backwards compatability, I simply separated the two into their own imports
43 | - Usage should be less convoluted
44 |
45 | ### Fixed
46 |
47 | - Vue 3 compatability should be stabalized and working again
48 |
49 | ## v3.1.1
50 |
51 | ### Fixed
52 |
53 | - Erroring out when there were no attributes on your element tag [#71](https://github.com/dhershman1/vue-debounce/issues/71)
54 | - Potential issue if passed events was just an empty array or string would cause debounce to stop listening all together
55 | - This falls back on the value of `listenTo`
56 |
57 | ## v3.1.0
58 |
59 | ### Improved
60 |
61 | - Now using vNode instead of raw events, this allows us to use events no matter how deep [#66](https://github.com/dhershman1/vue-debounce/issues/66)
62 | - The above improves compatability with libraries like Vuetify
63 |
64 | ### Chore
65 |
66 | - Dependency Updates and Audit fixes
67 |
68 | ## v3.0.2
69 |
70 | ### New
71 |
72 | - The build will now push out a esm module as well with the other minified versions check the readme on how to use it
73 |
74 | ### Chores
75 |
76 | - Updated dependencies
77 | - Ran Audit on dev deps (and fixed)
78 |
79 | ### Fixed
80 |
81 | - Some documentation mis communications
82 |
83 | ## v3.0.1
84 |
85 | ### Improved
86 |
87 | - Types for debounce (thanks to [hrobertson](https://github.com/hrobertson))
88 | - Placement of the readme for `getDirective`
89 |
90 | ## v3.0.0
91 |
92 | ### BREAKING CHANGES
93 |
94 | - Lots of code condencing, shouldn't break anything but just in case
95 | - Changed how debouncing `fireonempty` works. It had a bug so I made it a bit more strict to fix this
96 | - Please open issues ASAP if this functionality is not working as expected
97 |
98 | ### New
99 |
100 | - Added new `trim` option and modifier, this allows you to trim inputs that come through
101 | - The value given to your function will **NOT** be the trimmed value, trim is only used for logic to see if things should be ran
102 | - Added `getDirective` function which allows you to create a directive instance at lower levels other than global
103 |
104 | ### Improved
105 |
106 | - Some small code cleanup
107 | - Updated dependencies
108 |
109 | ## v2.6.0
110 |
111 | ### New
112 |
113 | - Added Vue 3 Compatibility :tada:
114 | - This involves the backwards compatibility change I brought up in discussions
115 |
116 | ### Improved
117 |
118 | - Dropped `dist/` from the repo
119 | - This is so it doesnt bog down PRs
120 | - dist is still available on npm as its built before deploy
121 | - Your CDN (if using) should _NOT_ be affected
122 |
123 | ## v2.5.8
124 |
125 | ### Improved
126 |
127 | - Updated all dev dependencies
128 | - Started planning vue 3 compatibility
129 |
130 | ### Fixed
131 |
132 | - Removed david-md badges from readme since it seems to be down for good
133 | - Linting fixes in the test files
134 |
135 | ## v2.5.7
136 |
137 | ### Fixed
138 |
139 | - Took out Kyanite for compatibility reasons
140 |
141 | ## v2.5.6
142 |
143 | ### Fixed
144 |
145 | - Restored IE11 support by updating kyanite to its latest version
146 |
147 | ## v2.5.5
148 |
149 | ### Improved
150 |
151 | - Converted over to `Terser` instead of `uglify`
152 | - Converted over to `babel` instead of `buble`
153 | - Switched over to my library `kyanite` for some of the utility work, removing some excess code
154 |
155 | ## v2.5.4
156 |
157 | ### Improved
158 |
159 | - Caveats section added [#36](https://github.com/dhershman1/vue-debounce/issues/36)
160 |
161 | ### Fixed
162 |
163 | - Unclear documentation for `modifiers` [#37](https://github.com/dhershman1/vue-debounce/issues/37)
164 |
165 | ## v2.5.3
166 |
167 | ### Fixed
168 |
169 | - Unclear documentation on using the `debounce` function [#34](https://github.com/dhershman1/vue-debounce/issues/34)
170 |
171 | ## v2.5.2
172 |
173 | ### Improved
174 |
175 | - Added support for scope inheritance for `debounce` utility function this might address [#28](https://github.com/dhershman1/vue-debounce/issues/28) [ilyasfoo](https://github.com/ilyasfoo)
176 | - Dependencies updated
177 |
178 | ## v2.5.1
179 |
180 | ### Fixed
181 |
182 | - typescript typing for the return on the debounce function [@bobvandevijver](https://github.com/bobvandevijver)
183 |
184 | ## v2.5.0
185 |
186 | ### New
187 |
188 | - Added `fireOnEmpty` modifier, which allow you to choose individual inputs you want to have fireOnEmpty
189 | - Added `cancelOnEmpty` modifier which cancels the debounce all together if the input value is empty
190 |
191 | ### Improved
192 |
193 | - Drastic code cleanup
194 |
195 | ### Fixed
196 |
197 | - Bug with `fireOnEmpty` where debounce function would fire twice even when input was empty
198 |
199 | ## v2.4.0
200 |
201 | ### New
202 |
203 | - Added the `event` Object itself to the function call, it is passed as the 2nd parameter
204 |
205 | ## v2.3.0
206 |
207 | ### New
208 |
209 | - Added a new `fireOnEmpty` option that you can enable to fire the debounce function immediately if the input is empty
210 |
211 | ## v2.2.1
212 |
213 | ### Fixed
214 |
215 | - Added Typescript section to the table of contents
216 | - Fixed author data to have my email
217 |
218 | ## v2.2.0
219 |
220 | ### New
221 |
222 | - Added Typescript Support thanks to @itmayziii
223 |
224 | ## v2.1.0
225 |
226 | ### New
227 |
228 | - Added instructions on how to just use the debounce api
229 | - Added ability to send the debounce function just a number and it will get treated as miliseconds
230 |
231 | ## v2.0.0
232 |
233 | ### BREAKING CHANGES
234 |
235 | - Now using the `addEventListener` method attached to the given el to properly set the event rather than overriding the native callback
236 |
237 | ## v1.3.0
238 |
239 | - Added ability to set default timeout within the options of the plugin
240 |
241 | ## v1.2.0
242 |
243 | - Added ability to send an array of events to have the elments listen to
244 | - Removed deprecated `keyCode` in favor of just `key`
245 | - `listenTo` is no longer case sensitive
246 | - Added ability to listen for the `debounce-events` attribute
247 |
248 | ## v1.1.0
249 |
250 | - Tweaked the Syntax of the debounce function
251 | - Added additional tests
252 | - Updated dependencies
253 | - Added ability to set the event to listen to in options
254 |
255 | ## v1.0.0
256 |
257 | - Some small tweaks to code nothing breaking
258 | - Removed the long deleted `min` from the regex check
259 | - Removed unused code
260 | - Mainly to just get it to v1.0.0 no breaking changes added
261 |
262 | ## v0.2.0
263 |
264 | - Slight Tweaks to optimization
265 | - Removed unused data pieces
266 | - Switched the event to `keyup` instead of `input` for key events
267 | - Added some options and modifiers support (See next tick)
268 | - Pressing enter on a debounced input now automatically fires the desired function
269 | - You can disable this by using the `.lock` modifier on the directive
270 | - You can also disable it by passing `lock` to the directive as an option, however this will disable it for _ALL_ debounced inputs
271 | - If you are using the lock option and want to make an exception for an input you can use the `.unlock` modifier to do so
272 | - Removed support for minutes
273 |
274 | ## v0.1.2
275 |
276 | - Replaced `null` type with a `false` Boolean
277 | - Removed empty config object
278 | - Added `Vue` as a peer dependency
279 | - Removed `Vue` as a dev dependency
280 | - Created the changelog md
281 |
282 | ## v0.1.1
283 |
284 | - Fixed the Build files
285 |
286 | ## v0.1.0
287 |
288 | - Initial Release
289 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Dustin Hershman
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-debounce
2 |
3 | [](https://www.npmjs.com/package/vue-debounce)
4 | [](https://www.npmjs.com/package/vue-debounce)
5 |
6 | A simple to use directive for debounce solutions
7 |
8 | It attaches itself to an event for actions
9 |
10 | [](https://github.com/standard/standard)
11 |
12 | ## Important
13 |
14 | As of now [vue2-debounce](https://github.com/dhershman1/vue2-debounce) is published on npm, if you are using vue2 I highly recommend migrating to this package as this one is going to start focusing on vue3 from v5 onward. Consider vue2 support deprecated for this module.
15 |
16 | ## Content
17 |
18 | - [Features](#features)
19 | - [Installation](#installation)
20 | - [Modifiers](#modifiers)
21 | - [Options](#options)
22 | - [Option Defaults](#option-defaults)
23 | - [CDN Support](#cdn-support)
24 | - [Setup](#setup)
25 | - [Use Just Debounce](#using-just-debounce)
26 | - [Usage](#usage)
27 | - [Modifier Usage](#modifier-usage)
28 | - [Overwriting Events](#overwriting-events)
29 | - [Typescript Support](#typescript-support)
30 | - [Caveats](#caveats)
31 |
32 | ## Features
33 |
34 | - Dynamic debouncing for input based requests
35 | - Easy to use, just place it into your vue instance and attach it to your inputs/components
36 | - Self regulating no need to worry about it, set it and forget it
37 | - Multiple time formats supported (miliseconds and seconds)
38 | - Enter key support to automatically fire the desired function when the user hits the enter key in the desired input (Can also be disabled)
39 | - Supports the ability to have multiple event listeners, and specify events at the element level
40 |
41 | ## Installation
42 | ```
43 | npm i vue-debounce
44 | ```
45 |
46 | ## Modifiers
47 |
48 | - `lock` : Used to lock the debounce and prevent the enter key from triggering the function when pressed
49 | - Example: `v-debounce:400ms.lock="cb"`
50 | - `unlock` : Used to unlock the enter key on a debounced input, useful if you want to use the `lock` option and only want a few debounced inputs unlocked
51 | - `fireonempty` : Use to signify that when that specific input is emptied, you want the function to fire right away
52 | - `cancelonempty` : Use this to specify that when the input is emptied you **DO NOT** want your debounced function to trigger at all
53 | - `trim` : `Boolean` - Tells debounce to trim out white space using the `String.prototype.trim()` function
54 |
55 | ## Options
56 |
57 | - `lock` : `Boolean` - This works the same way as the modifier does, however using the option will lock _ALL_ of the debounced inputs within that vue instance, where as using the modifer only locks the one it's attached to
58 | - `listenTo` : `String|Array` - Allows you to set a custom event attached to an element like `input` for example
59 | - This is given to the `addEventListener` method attached to the element
60 | - `defaultTime` : `String` - Set the default timer for debounce directives that you don't give a time to
61 | - `fireOnEmpty` : `Boolean` - Tells debounce that if the input is empty, then fire the function immediately
62 | - `trim` : `Boolean` - Tells debounce to trim out white space using the `String.prototype.trim()` function
63 |
64 | ## Option Defaults
65 |
66 | ```js
67 | {
68 | lock: false,
69 | listenTo: 'keyup',
70 | defaultTime: '300ms',
71 | fireOnEmpty: false,
72 | trim: false
73 | }
74 | ```
75 |
76 | ## CDN Support
77 |
78 | You can use vue debounce via CDN like so: (It is recommended that you don't use `@latest` however)
79 |
80 | ```html
81 |
85 | ```
86 |
87 | ## Setup
88 |
89 | With vue3 we simply need to import the new directive function `vueDebounce` this function takes in an object of options (found above)
90 |
91 | Using `vue-debounce` Globally:
92 | ```js
93 | import vueDebounce from 'vue-debounce'
94 | import { createApp } from 'vue';
95 | import App from './App.vue';
96 |
97 | const app = createApp(App)
98 | app
99 | .directive('debounce', vueDebounce({ lock: true }))
100 | .mount('#app');
101 | ```
102 |
103 | Using the setup API at the component level:
104 | ```vue
105 |
110 | ```
111 |
112 | Using `vue-debounce` at a component level using the option API:
113 | ```js
114 | import vueDebounce from 'vue-debounce'
115 |
116 | export default {
117 | directives: {
118 | debounce: vueDebounce({ lock: true })
119 | }
120 | }
121 | ```
122 |
123 | ## Using Just Debounce
124 |
125 | With Vue-debounce you're also able to just use the debouncing function.
126 |
127 | Simply require the debounce file.
128 |
129 | ```js
130 | import { debounce } from 'vue-debounce'
131 | ```
132 |
133 | The `debounce` function returns a function back which in turn is debounced, so you can set them up ahead of time:
134 |
135 | ```js
136 | const dFn = debounce(val => console.log('normal format', val), '400ms')
137 |
138 | dFn(10) // => 'normal format' 10
139 | // Or
140 | debounce(val => console.log('just a number!'), 400)(10) // => 'just a number!' 10
141 | ```
142 |
143 |
144 | ## Usage
145 |
146 | Then attach a time:format to the directive, and set the value to the function you want to call and attach it to your input element
147 |
148 | Example:
149 |
150 | ```vue
151 |
152 | ```
153 |
154 | If no wait timer is passed in, then the directive will default to whatever you set `defaultTime` to, **OR** `300ms` if that isn't set.
155 |
156 | You can pass the time in multiple formats:
157 |
158 | ```vue
159 |
160 |
161 |
162 |
163 |
164 | ```
165 |
166 | The value of the input is passed along to your function as the first parameter, and the 2nd parameter is the event object itself.
167 |
168 | ## Modifier Usage
169 |
170 | Using modifiers works just like normal Vue directives. You can chain them to the timeout value and each other. Some examples include:
171 |
172 | > **IMPORTANT NOTE**: Modifiers WILL overwrite options you have set, for example if you set the `fireOnEmpty` option set to true and then tag a input with the `cancelonempty` modifier then the debounced function will cancel when **THAT** input is empty instead of fire.
173 |
174 | ```vue
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 | ```
188 |
189 | ## Overwriting Events
190 |
191 | As of Version 1.2.0 you can assign specific event listeners to specific inputs. Doing so overwrites **ANY** of the listed events set with `listenTo`
192 |
193 | Example:
194 | ```vue
195 | // This can accept an array or a single string when using the bind `:` syntax
196 |
197 |
198 |
199 | // You can also just use it as an attribute, though if passing multiple events binding it is preferred
200 |
201 | ```
202 |
203 | A full example:
204 |
205 | ```vue
206 |
207 |
208 |
209 |
210 |
220 | ```
221 |
222 | ## Typescript Support
223 | While this project is not written in typescript, we do define types in the `types` directory. Unfortunately the way Vue is currently typed
224 | the only type support you will get is when you `Vue.use(vueDebounce)`.
225 |
226 | i.e.
227 |
228 | ```typescript
229 | import Vue from 'vue'
230 | import vueDebounce, { PluginConfig, debounce } from 'vue-debounce'
231 |
232 | debounce(() => console.log('just a number!'), 400)
233 | debounce(() => console.log('normal format'), '400ms')
234 |
235 | Vue.use(vueDebounce, { lock: true, defaultTime: '400ms', listenTo: 'keyup' })
236 | ```
237 |
238 | Hopefully in the future Vue will allow directives to type the modifiers and values that are accepted.
239 |
240 | ## Caveats
241 |
242 | If a library you are using such as `Vueftify` is already using a specified event, it will block vue debounce from being able to listen to that event.
243 |
244 | As of `v3.1.0` I have significantly improved compatability with these kinds of libraries, however this problem still remains.
245 |
246 | For example, Vuetify makes pretty heavy use of the `onblur` event for a lot of it's styles/animatons, so I'd recommend telling vue-debounce to listen for `focusout` instead, if you want debounce to trigger on a blur like event.
247 |
248 | I will keep doing research into a better way to solve this little issue, but for now the improved compatability should help a lot!
249 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-debounce",
3 | "version": "5.0.1",
4 | "description": "A simple vue directive for debounce",
5 | "module": "src/index.js",
6 | "main": "src/index.js",
7 | "types": "types/index.d.ts",
8 | "type": "module",
9 | "unpkg": "dist/vueDebounce.min.js",
10 | "jsdelivr": "dist/vueDebounce.min.js",
11 | "scripts": {
12 | "test": "tape tests/test.js | tap-on",
13 | "prepack": "npm run lint && npm run build",
14 | "build": "rollup -c",
15 | "watch": "rollup -c -w",
16 | "lint": "standard src/*.js",
17 | "lint:fix": "standard --fix src/*.js"
18 | },
19 | "standard": {
20 | "ignore": [
21 | "dist/*",
22 | "types/*"
23 | ]
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/dhershman1/vue-debounce.git"
28 | },
29 | "keywords": [
30 | "vue",
31 | "vue3",
32 | "debounce",
33 | "directive",
34 | "simple",
35 | "v-debounce",
36 | "events"
37 | ],
38 | "author": "Dustin Hershman ",
39 | "contributors": [
40 | "Tommy May III "
41 | ],
42 | "license": "MIT",
43 | "bugs": {
44 | "url": "https://github.com/dhershman1/vue-debounce/issues"
45 | },
46 | "homepage": "https://github.com/dhershman1/vue-debounce#readme",
47 | "devDependencies": {
48 | "@babel/core": "7.23.5",
49 | "@babel/preset-env": "7.23.5",
50 | "@rollup/plugin-babel": "6.0.4",
51 | "@rollup/plugin-terser": "0.4.4",
52 | "rollup": "^4.6.1",
53 | "rollup-plugin-filesize": "9.1.2",
54 | "standard": "^17.1.0",
55 | "tap-on": "0.3.1",
56 | "tape": "^5.7.2"
57 | },
58 | "peerDependencies": {
59 | "vue": ">= 3.0.0"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from '@rollup/plugin-babel'
2 | import filesize from 'rollup-plugin-filesize'
3 | import terser from '@rollup/plugin-terser'
4 |
5 | export default [{
6 | input: './src/index.js',
7 | plugins: [
8 | babel({ babelHelpers: 'bundled' }),
9 | terser(),
10 | filesize({
11 | showMinifiedSize: false
12 | })
13 | ],
14 | output: {
15 | format: 'umd',
16 | name: 'vueDebounce',
17 | file: 'dist/vue-debounce.min.js'
18 | }
19 | }, {
20 | input: './src/debounce.js',
21 | plugins: [
22 | babel({ babelHelpers: 'bundled' }),
23 | terser(),
24 | filesize({
25 | showMinifiedSize: false
26 | })
27 | ],
28 | output: {
29 | format: 'umd',
30 | name: 'debounce',
31 | file: 'dist/debounce.min.js'
32 | }
33 | }]
34 |
--------------------------------------------------------------------------------
/src/_internals.js:
--------------------------------------------------------------------------------
1 | // Helper Functions
2 | /**
3 | * Maps through an array of strings and lowercases all of them
4 | * @param {Array} list an array of strings to map through
5 | */
6 | export function toLowerMap (list) {
7 | return list.map(x => x.toLowerCase())
8 | }
9 |
10 | /**
11 | * Takes in a value and ensures its wrapped within an array
12 | * @param {Any} value The value to ensure is an array
13 | */
14 | export function ensureArray (value) {
15 | if (Array.isArray(value)) {
16 | return value
17 | }
18 |
19 | if (value == null) {
20 | return []
21 | }
22 |
23 | return [value]
24 | }
25 |
26 | // Figures out the event we are using with the bound element
27 | export function mapOutListeningEvents (attrs, listenTo) {
28 | // Make sure attributes exist on the element
29 | const elEvents = attrs ? attrs['debounce-events'] : []
30 | // If they set an events attribute that overwrites everything
31 | if (elEvents && elEvents.length > 0) {
32 | // Since they can send in an array or a string we need to be prepared for both
33 | if (Array.isArray(elEvents)) {
34 | return toLowerMap(elEvents)
35 | }
36 | return toLowerMap(elEvents.split(','))
37 | }
38 |
39 | return toLowerMap(ensureArray(listenTo))
40 | }
41 |
42 | export function isEmpty (str) {
43 | return str === ''
44 | }
45 |
46 | export function isLocked (key, modifiers) {
47 | return key === 'Enter' && (!modifiers.lock || modifiers.unlock)
48 | }
49 |
50 | export function shouldFireOnEmpty (value, key, modifiers) {
51 | return isEmpty(value) && modifiers.fireonempty && (key === 'Enter' || key === ' ')
52 | }
53 |
--------------------------------------------------------------------------------
/src/debounce.js:
--------------------------------------------------------------------------------
1 | function convertTime (time) {
2 | const [amt, t = 'ms'] = String(time).split(/(ms|s)/i)
3 | const types = {
4 | ms: 1,
5 | s: 1000
6 | }
7 |
8 | return Number(amt) * types[t]
9 | }
10 |
11 | function debounce (fn, wait) {
12 | let timeout = null
13 | const timer = typeof wait === 'number' ? wait : convertTime(wait)
14 |
15 | const debounced = function (...args) {
16 | const later = () => {
17 | timeout = null
18 |
19 | fn.apply(this, args)
20 | }
21 |
22 | clearTimeout(timeout)
23 | timeout = setTimeout(later, timer)
24 |
25 | if (!timeout) {
26 | fn.apply(this, args)
27 | }
28 | }
29 |
30 | debounced.cancel = () => {
31 | clearTimeout(timeout)
32 | timeout = null
33 | }
34 |
35 | return debounced
36 | }
37 |
38 | export default debounce
39 |
--------------------------------------------------------------------------------
/src/directive.js:
--------------------------------------------------------------------------------
1 | import debounce from './debounce.js'
2 | import { mapOutListeningEvents, isEmpty, isLocked, shouldFireOnEmpty } from './_internals.js'
3 |
4 | export default function ({
5 | lock = false,
6 | listenTo = 'keyup',
7 | defaultTime = '300ms',
8 | fireOnEmpty = false,
9 | cancelOnEmpty = false,
10 | trim = false
11 | } = {}) {
12 | return {
13 | created (el, {
14 | value: debouncedFn,
15 | arg: timer = defaultTime,
16 | modifiers
17 | }, vnode) {
18 | const combinedRules = Object.assign({
19 | lock,
20 | trim,
21 | fireonempty: fireOnEmpty,
22 | cancelonempty: cancelOnEmpty
23 | }, modifiers)
24 | const events = mapOutListeningEvents(vnode.props, listenTo)
25 | const fn = debounce(e => {
26 | debouncedFn(e.target.value, e)
27 | }, timer)
28 |
29 | function handler (event) {
30 | const value = combinedRules.trim ? event.target.value.trim() : event.target.value
31 |
32 | if (isEmpty(value) && combinedRules.cancelonempty) {
33 | fn.cancel()
34 | } else if (isLocked(event.key, combinedRules) || shouldFireOnEmpty(value, event.key, combinedRules)) {
35 | fn.cancel()
36 | debouncedFn(event.target.value, event)
37 | } else {
38 | fn(event)
39 | }
40 | }
41 |
42 | events.forEach(e => {
43 | el.addEventListener(e, handler)
44 | })
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import debounce from './debounce.js'
2 | import vueDebounce from './directive.js'
3 |
4 | export {
5 | debounce,
6 | vueDebounce
7 | }
8 |
9 | export default vueDebounce
10 |
--------------------------------------------------------------------------------
/tests/test.js:
--------------------------------------------------------------------------------
1 | import test from 'tape'
2 | import debounce from '../src/debounce.js'
3 |
4 | test('Handles defaults', t => {
5 | const runner = debounce((val) => {
6 | t.ok(val)
7 | t.same(val, 'testing')
8 | t.end()
9 | })
10 |
11 | runner('testing')
12 | })
13 |
14 | test('Handles number type', t => {
15 | const runner = debounce(val => {
16 | t.ok(val)
17 | t.same(val, 'testing')
18 | t.end()
19 | }, 400)
20 |
21 | runner('testing')
22 | })
23 |
24 | test('Handles no format set', t => {
25 | const runner = debounce(val => {
26 | t.ok(val)
27 | t.same(val, 'testing')
28 | t.end()
29 | }, '500')
30 |
31 | runner('testing')
32 | })
33 |
34 | test('Handles sepcific time', t => {
35 | const runner = debounce((val) => {
36 | t.ok(val)
37 | t.same(val, 'testing')
38 | t.end()
39 | }, '500ms')
40 |
41 | runner('testing')
42 | })
43 |
44 | test('Handles seconds', t => {
45 | const runner = debounce((val) => {
46 | t.ok(val)
47 | t.same(val, 'testing')
48 | t.end()
49 | }, '1s')
50 |
51 | runner('testing')
52 | })
53 |
54 | test('Inherits parent scope', t => {
55 | const runner = function (val) {
56 | const Someclass = function ($options) {
57 | this.somevalue = ''
58 | this.$options = $options
59 |
60 | this.bind = function (fn, ctx) {
61 | return function (a) {
62 | const l = arguments.length
63 | return l
64 | ? l > 1
65 | ? fn.apply(ctx, arguments)
66 | : fn.call(ctx, a)
67 | : fn.call(ctx)
68 | }
69 | }
70 |
71 | this._initMethods = function () {
72 | const methods = this.$options.methods
73 | if (methods) {
74 | for (const key in methods) {
75 | this[key] = this.bind(methods[key], this)
76 | }
77 | }
78 | }
79 |
80 | this._initMethods()
81 | }
82 |
83 | const vm = new Someclass({
84 | methods: {
85 | somefunction: debounce(function () {
86 | t.ok(val)
87 | t.same(this.somevalue, 'testing')
88 | t.end()
89 | })
90 | }
91 | })
92 |
93 | vm.somevalue = val
94 | vm.somefunction()
95 | }
96 |
97 | runner('testing')
98 | })
99 |
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * TODO 12/1/2019 - It would be nice if we could add types to the modifiers and expected callback value of the directive but this does not seem to be possible right now.
3 | *
4 | * Both of the types are marked as any without any generic support so not much we can do. This might change in the future when Vue is actually written in typescript as per the road map for Vue 3 https://github.com/vuejs/roadmap#3x
5 | * "value" type - https://github.com/vuejs/vue/blob/fd0eaf92948bb5a4882d538362091fb287d642e3/types/vnode.d.ts#L70
6 | * "modifiers" type - https://github.com/vuejs/vue/blob/fd0eaf92948bb5a4882d538362091fb287d642e3/types/options.d.ts#L184
7 | */
8 |
9 | export interface PluginConfig {
10 | lock?: boolean
11 | listenTo?: string | string[]
12 | defaultTime?: string | number
13 | fireOnEmpty?: boolean,
14 | cancelOnEmpty?: boolean,
15 | trim?: boolean
16 | }
17 |
18 | interface DirectiveObject {
19 | [key: string]: (el: HTMLElement, binding: any, vnode: any) => void
20 | }
21 |
22 | interface DebounceInstance {
23 | (...args: A): void | Promise,
24 | cancel(): void,
25 | }
26 |
27 | interface Debounce {
28 | (fn: (...args: A) => void | Promise, wait: number | string): DebounceInstance
29 | }
30 |
31 | declare const debounce: Debounce
32 | declare const vueDebounce: (opts: PluginConfig) => DirectiveObject
33 |
34 | export { debounce, vueDebounce, DebounceInstance, DirectiveObject }
35 | export default vueDebounce
36 |
--------------------------------------------------------------------------------