├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── demo ├── Demo.vue ├── index.html └── main.js ├── deploy-docs.sh ├── dist ├── vue-textarea-autosize.common.js ├── vue-textarea-autosize.esm.js ├── vue-textarea-autosize.umd.js └── vue-textarea-autosize.umd.min.js ├── docs ├── .vuepress │ ├── config.js │ └── plugin.js ├── README.md └── guide.md ├── package-lock.json ├── package.json └── src ├── App.vue ├── assets └── logo.png ├── components └── TextareaAutosize.vue ├── index.js ├── main.js └── plugin.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | public 4 | dist/*.gz 5 | dist/*.map 6 | coverage 7 | docs/.vuepress/dist 8 | 9 | # local env files 10 | .env.local 11 | .env.*.local 12 | 13 | # related test files 14 | /tests/e2e/reports 15 | /tests/e2e/videos 16 | /tests/e2e/screenshots 17 | 18 | # editor directories and files 19 | .idea 20 | .vscode 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw* 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.1.0] - 2019-09-07 8 | ### Changed 9 | - make `minHeight` and `maxHeight` props reactive. by [#17](https://github.com/devstark-com/vue-textarea-autosize/issues/17) from [@deathg0d](https://github.com/deathg0d) 10 | - rewrite textarea height updating in the data-first manner. by [#15](https://github.com/devstark-com/vue-textarea-autosize/issues/15) from [@danielSONCCO](https://github.com/danielSONCCO) 11 | - rebuild the plugin with [vue-cli-plugin-p11n](https://github.com/kazupon/vue-cli-plugin-p11n) 12 | - update README.md 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-textarea-autosize 2 | 3 | [![npm](https://img.shields.io/npm/v/vue-textarea-autosize.svg) ![npm](https://img.shields.io/npm/dm/vue-textarea-autosize.svg)](https://www.npmjs.com/package/vue-textarea-autosize) 4 | [![vue2](https://img.shields.io/badge/vue-2.x-brightgreen.svg)](https://vuejs.org/) 5 | 6 | --- 7 | 8 | The plugin for [Vue.js](http://vuejs.org) provides the Vue component implements textarea with automatically adjustable height and without any wrappers and dependencies. 9 | 10 | # Demo 11 | HERE 12 | 13 | # Docs 14 | HERE 15 | 16 | --- 17 | 18 | ## License 19 | 20 | [MIT](http://opensource.org/licenses/MIT) 21 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /demo/Demo.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-textarea-autosize demo 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from '../src/App.vue' 3 | import plugin from '../src/index' 4 | 5 | Vue.use(plugin) 6 | 7 | Vue.config.productionTip = false 8 | 9 | new Vue({ 10 | // NOTE: if you need to inject as option, you can set here! 11 | plugin, 12 | render: h => h(App) 13 | }).$mount('#app') 14 | -------------------------------------------------------------------------------- /deploy-docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # abort on errors 4 | set -e 5 | 6 | # build 7 | npm run docs:build 8 | 9 | # navigate into the build output directory 10 | cd docs/.vuepress/dist 11 | 12 | # if you are deploying to a custom domain 13 | # echo 'www.example.com' > CNAME 14 | 15 | git init 16 | git add -A 17 | git commit -m 'deploy' 18 | 19 | # if you are deploying to https://.github.io 20 | # git push -f git@github.com:/.github.io.git master 21 | 22 | # if you are deploying to https://.github.io/ 23 | git push -f git@github.com:devstark-com/vue-textarea-autosize.git master:gh-pages 24 | -------------------------------------------------------------------------------- /dist/vue-textarea-autosize.common.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-textarea-autosize v1.1.1 3 | * (c) 2019 Saymon 4 | * Released under the MIT License. 5 | */ 6 | 'use strict'; 7 | 8 | // 9 | // 10 | // 11 | // 12 | // 13 | // 14 | // 15 | var script = { 16 | name: 'TextareaAutosize', 17 | props: { 18 | value: { 19 | type: [String, Number], 20 | default: '' 21 | }, 22 | autosize: { 23 | type: Boolean, 24 | default: true 25 | }, 26 | minHeight: { 27 | type: [Number], 28 | 'default': null 29 | }, 30 | maxHeight: { 31 | type: [Number], 32 | 'default': null 33 | }, 34 | 35 | /* 36 | * Force !important for style properties 37 | */ 38 | important: { 39 | type: [Boolean, Array], 40 | default: false 41 | } 42 | }, 43 | data: function data() { 44 | return { 45 | // data property for v-model binding with real textarea tag 46 | val: null, 47 | // works when content height becomes more then value of the maxHeight property 48 | maxHeightScroll: false, 49 | height: 'auto' 50 | }; 51 | }, 52 | computed: { 53 | computedStyles: function computedStyles() { 54 | if (!this.autosize) return {}; 55 | return { 56 | resize: !this.isResizeImportant ? 'none' : 'none !important', 57 | height: this.height, 58 | overflow: this.maxHeightScroll ? 'auto' : !this.isOverflowImportant ? 'hidden' : 'hidden !important' 59 | }; 60 | }, 61 | isResizeImportant: function isResizeImportant() { 62 | var imp = this.important; 63 | return imp === true || Array.isArray(imp) && imp.includes('resize'); 64 | }, 65 | isOverflowImportant: function isOverflowImportant() { 66 | var imp = this.important; 67 | return imp === true || Array.isArray(imp) && imp.includes('overflow'); 68 | }, 69 | isHeightImportant: function isHeightImportant() { 70 | var imp = this.important; 71 | return imp === true || Array.isArray(imp) && imp.includes('height'); 72 | } 73 | }, 74 | watch: { 75 | value: function value(val) { 76 | this.val = val; 77 | }, 78 | val: function val(_val) { 79 | this.$nextTick(this.resize); 80 | this.$emit('input', _val); 81 | }, 82 | minHeight: function minHeight() { 83 | this.$nextTick(this.resize); 84 | }, 85 | maxHeight: function maxHeight() { 86 | this.$nextTick(this.resize); 87 | }, 88 | autosize: function autosize(val) { 89 | if (val) this.resize(); 90 | } 91 | }, 92 | methods: { 93 | resize: function resize() { 94 | var _this = this; 95 | 96 | var important = this.isHeightImportant ? 'important' : ''; 97 | this.height = "auto".concat(important ? ' !important' : ''); 98 | this.$nextTick(function () { 99 | var contentHeight = _this.$el.scrollHeight + 1; 100 | 101 | if (_this.minHeight) { 102 | contentHeight = contentHeight < _this.minHeight ? _this.minHeight : contentHeight; 103 | } 104 | 105 | if (_this.maxHeight) { 106 | if (contentHeight > _this.maxHeight) { 107 | contentHeight = _this.maxHeight; 108 | _this.maxHeightScroll = true; 109 | } else { 110 | _this.maxHeightScroll = false; 111 | } 112 | } 113 | 114 | var heightVal = contentHeight + 'px'; 115 | _this.height = "".concat(heightVal).concat(important ? ' !important' : ''); 116 | }); 117 | return this; 118 | } 119 | }, 120 | created: function created() { 121 | this.val = this.value; 122 | }, 123 | mounted: function mounted() { 124 | this.resize(); 125 | } 126 | }; 127 | 128 | function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier 129 | /* server only */ 130 | , shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { 131 | if (typeof shadowMode !== 'boolean') { 132 | createInjectorSSR = createInjector; 133 | createInjector = shadowMode; 134 | shadowMode = false; 135 | } // Vue.extend constructor export interop. 136 | 137 | 138 | var options = typeof script === 'function' ? script.options : script; // render functions 139 | 140 | if (template && template.render) { 141 | options.render = template.render; 142 | options.staticRenderFns = template.staticRenderFns; 143 | options._compiled = true; // functional template 144 | 145 | if (isFunctionalTemplate) { 146 | options.functional = true; 147 | } 148 | } // scopedId 149 | 150 | 151 | if (scopeId) { 152 | options._scopeId = scopeId; 153 | } 154 | 155 | var hook; 156 | 157 | if (moduleIdentifier) { 158 | // server build 159 | hook = function hook(context) { 160 | // 2.3 injection 161 | context = context || // cached call 162 | this.$vnode && this.$vnode.ssrContext || // stateful 163 | this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional 164 | // 2.2 with runInNewContext: true 165 | 166 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 167 | context = __VUE_SSR_CONTEXT__; 168 | } // inject component styles 169 | 170 | 171 | if (style) { 172 | style.call(this, createInjectorSSR(context)); 173 | } // register component module identifier for async chunk inference 174 | 175 | 176 | if (context && context._registeredComponents) { 177 | context._registeredComponents.add(moduleIdentifier); 178 | } 179 | }; // used by ssr in case component is cached and beforeCreate 180 | // never gets called 181 | 182 | 183 | options._ssrRegister = hook; 184 | } else if (style) { 185 | hook = shadowMode ? function () { 186 | style.call(this, createInjectorShadow(this.$root.$options.shadowRoot)); 187 | } : function (context) { 188 | style.call(this, createInjector(context)); 189 | }; 190 | } 191 | 192 | if (hook) { 193 | if (options.functional) { 194 | // register for functional component in vue file 195 | var originalRender = options.render; 196 | 197 | options.render = function renderWithStyleInjection(h, context) { 198 | hook.call(context); 199 | return originalRender(h, context); 200 | }; 201 | } else { 202 | // inject component registration as beforeCreate hook 203 | var existing = options.beforeCreate; 204 | options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; 205 | } 206 | } 207 | 208 | return script; 209 | } 210 | 211 | var normalizeComponent_1 = normalizeComponent; 212 | 213 | /* script */ 214 | const __vue_script__ = script; 215 | 216 | /* template */ 217 | var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('textarea',{directives:[{name:"model",rawName:"v-model",value:(_vm.val),expression:"val"}],style:(_vm.computedStyles),domProps:{"value":(_vm.val)},on:{"focus":_vm.resize,"input":function($event){if($event.target.composing){ return; }_vm.val=$event.target.value;}}})}; 218 | var __vue_staticRenderFns__ = []; 219 | 220 | /* style */ 221 | const __vue_inject_styles__ = undefined; 222 | /* scoped */ 223 | const __vue_scope_id__ = undefined; 224 | /* module identifier */ 225 | const __vue_module_identifier__ = undefined; 226 | /* functional template */ 227 | const __vue_is_functional_template__ = false; 228 | /* style inject */ 229 | 230 | /* style inject SSR */ 231 | 232 | 233 | 234 | var TextareaAutosize = normalizeComponent_1( 235 | { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, 236 | __vue_inject_styles__, 237 | __vue_script__, 238 | __vue_scope_id__, 239 | __vue_is_functional_template__, 240 | __vue_module_identifier__, 241 | undefined, 242 | undefined 243 | ); 244 | 245 | var version = '1.1.1'; 246 | 247 | var install = function install(Vue) { 248 | Vue.component('TextareaAutosize', TextareaAutosize); 249 | }; 250 | 251 | var plugin = { 252 | install: install, 253 | version: version 254 | }; 255 | 256 | if (typeof window !== 'undefined' && window.Vue) { 257 | window.Vue.use(plugin); 258 | } 259 | 260 | module.exports = plugin; 261 | -------------------------------------------------------------------------------- /dist/vue-textarea-autosize.esm.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-textarea-autosize v1.1.1 3 | * (c) 2019 Saymon 4 | * Released under the MIT License. 5 | */ 6 | // 7 | // 8 | // 9 | // 10 | // 11 | // 12 | // 13 | var script = { 14 | name: 'TextareaAutosize', 15 | props: { 16 | value: { 17 | type: [String, Number], 18 | default: '' 19 | }, 20 | autosize: { 21 | type: Boolean, 22 | default: true 23 | }, 24 | minHeight: { 25 | type: [Number], 26 | 'default': null 27 | }, 28 | maxHeight: { 29 | type: [Number], 30 | 'default': null 31 | }, 32 | 33 | /* 34 | * Force !important for style properties 35 | */ 36 | important: { 37 | type: [Boolean, Array], 38 | default: false 39 | } 40 | }, 41 | data: function data() { 42 | return { 43 | // data property for v-model binding with real textarea tag 44 | val: null, 45 | // works when content height becomes more then value of the maxHeight property 46 | maxHeightScroll: false, 47 | height: 'auto' 48 | }; 49 | }, 50 | computed: { 51 | computedStyles: function computedStyles() { 52 | if (!this.autosize) return {}; 53 | return { 54 | resize: !this.isResizeImportant ? 'none' : 'none !important', 55 | height: this.height, 56 | overflow: this.maxHeightScroll ? 'auto' : !this.isOverflowImportant ? 'hidden' : 'hidden !important' 57 | }; 58 | }, 59 | isResizeImportant: function isResizeImportant() { 60 | var imp = this.important; 61 | return imp === true || Array.isArray(imp) && imp.includes('resize'); 62 | }, 63 | isOverflowImportant: function isOverflowImportant() { 64 | var imp = this.important; 65 | return imp === true || Array.isArray(imp) && imp.includes('overflow'); 66 | }, 67 | isHeightImportant: function isHeightImportant() { 68 | var imp = this.important; 69 | return imp === true || Array.isArray(imp) && imp.includes('height'); 70 | } 71 | }, 72 | watch: { 73 | value: function value(val) { 74 | this.val = val; 75 | }, 76 | val: function val(_val) { 77 | this.$nextTick(this.resize); 78 | this.$emit('input', _val); 79 | }, 80 | minHeight: function minHeight() { 81 | this.$nextTick(this.resize); 82 | }, 83 | maxHeight: function maxHeight() { 84 | this.$nextTick(this.resize); 85 | }, 86 | autosize: function autosize(val) { 87 | if (val) this.resize(); 88 | } 89 | }, 90 | methods: { 91 | resize: function resize() { 92 | var _this = this; 93 | 94 | var important = this.isHeightImportant ? 'important' : ''; 95 | this.height = "auto".concat(important ? ' !important' : ''); 96 | this.$nextTick(function () { 97 | var contentHeight = _this.$el.scrollHeight + 1; 98 | 99 | if (_this.minHeight) { 100 | contentHeight = contentHeight < _this.minHeight ? _this.minHeight : contentHeight; 101 | } 102 | 103 | if (_this.maxHeight) { 104 | if (contentHeight > _this.maxHeight) { 105 | contentHeight = _this.maxHeight; 106 | _this.maxHeightScroll = true; 107 | } else { 108 | _this.maxHeightScroll = false; 109 | } 110 | } 111 | 112 | var heightVal = contentHeight + 'px'; 113 | _this.height = "".concat(heightVal).concat(important ? ' !important' : ''); 114 | }); 115 | return this; 116 | } 117 | }, 118 | created: function created() { 119 | this.val = this.value; 120 | }, 121 | mounted: function mounted() { 122 | this.resize(); 123 | } 124 | }; 125 | 126 | function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier 127 | /* server only */ 128 | , shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { 129 | if (typeof shadowMode !== 'boolean') { 130 | createInjectorSSR = createInjector; 131 | createInjector = shadowMode; 132 | shadowMode = false; 133 | } // Vue.extend constructor export interop. 134 | 135 | 136 | var options = typeof script === 'function' ? script.options : script; // render functions 137 | 138 | if (template && template.render) { 139 | options.render = template.render; 140 | options.staticRenderFns = template.staticRenderFns; 141 | options._compiled = true; // functional template 142 | 143 | if (isFunctionalTemplate) { 144 | options.functional = true; 145 | } 146 | } // scopedId 147 | 148 | 149 | if (scopeId) { 150 | options._scopeId = scopeId; 151 | } 152 | 153 | var hook; 154 | 155 | if (moduleIdentifier) { 156 | // server build 157 | hook = function hook(context) { 158 | // 2.3 injection 159 | context = context || // cached call 160 | this.$vnode && this.$vnode.ssrContext || // stateful 161 | this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional 162 | // 2.2 with runInNewContext: true 163 | 164 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 165 | context = __VUE_SSR_CONTEXT__; 166 | } // inject component styles 167 | 168 | 169 | if (style) { 170 | style.call(this, createInjectorSSR(context)); 171 | } // register component module identifier for async chunk inference 172 | 173 | 174 | if (context && context._registeredComponents) { 175 | context._registeredComponents.add(moduleIdentifier); 176 | } 177 | }; // used by ssr in case component is cached and beforeCreate 178 | // never gets called 179 | 180 | 181 | options._ssrRegister = hook; 182 | } else if (style) { 183 | hook = shadowMode ? function () { 184 | style.call(this, createInjectorShadow(this.$root.$options.shadowRoot)); 185 | } : function (context) { 186 | style.call(this, createInjector(context)); 187 | }; 188 | } 189 | 190 | if (hook) { 191 | if (options.functional) { 192 | // register for functional component in vue file 193 | var originalRender = options.render; 194 | 195 | options.render = function renderWithStyleInjection(h, context) { 196 | hook.call(context); 197 | return originalRender(h, context); 198 | }; 199 | } else { 200 | // inject component registration as beforeCreate hook 201 | var existing = options.beforeCreate; 202 | options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; 203 | } 204 | } 205 | 206 | return script; 207 | } 208 | 209 | var normalizeComponent_1 = normalizeComponent; 210 | 211 | /* script */ 212 | const __vue_script__ = script; 213 | 214 | /* template */ 215 | var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('textarea',{directives:[{name:"model",rawName:"v-model",value:(_vm.val),expression:"val"}],style:(_vm.computedStyles),domProps:{"value":(_vm.val)},on:{"focus":_vm.resize,"input":function($event){if($event.target.composing){ return; }_vm.val=$event.target.value;}}})}; 216 | var __vue_staticRenderFns__ = []; 217 | 218 | /* style */ 219 | const __vue_inject_styles__ = undefined; 220 | /* scoped */ 221 | const __vue_scope_id__ = undefined; 222 | /* module identifier */ 223 | const __vue_module_identifier__ = undefined; 224 | /* functional template */ 225 | const __vue_is_functional_template__ = false; 226 | /* style inject */ 227 | 228 | /* style inject SSR */ 229 | 230 | 231 | 232 | var TextareaAutosize = normalizeComponent_1( 233 | { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, 234 | __vue_inject_styles__, 235 | __vue_script__, 236 | __vue_scope_id__, 237 | __vue_is_functional_template__, 238 | __vue_module_identifier__, 239 | undefined, 240 | undefined 241 | ); 242 | 243 | var version = '1.1.1'; 244 | 245 | var install = function install(Vue) { 246 | Vue.component('TextareaAutosize', TextareaAutosize); 247 | }; 248 | 249 | var plugin = { 250 | install: install, 251 | version: version 252 | }; 253 | 254 | if (typeof window !== 'undefined' && window.Vue) { 255 | window.Vue.use(plugin); 256 | } 257 | 258 | export default plugin; 259 | -------------------------------------------------------------------------------- /dist/vue-textarea-autosize.umd.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-textarea-autosize v1.1.1 3 | * (c) 2019 Saymon 4 | * Released under the MIT License. 5 | */ 6 | (function (global, factory) { 7 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 8 | typeof define === 'function' && define.amd ? define(factory) : 9 | (global = global || self, global.VueTextareaAutosize = factory()); 10 | }(this, function () { 'use strict'; 11 | 12 | // 13 | // 14 | // 15 | // 16 | // 17 | // 18 | // 19 | var script = { 20 | name: 'TextareaAutosize', 21 | props: { 22 | value: { 23 | type: [String, Number], 24 | default: '' 25 | }, 26 | autosize: { 27 | type: Boolean, 28 | default: true 29 | }, 30 | minHeight: { 31 | type: [Number], 32 | 'default': null 33 | }, 34 | maxHeight: { 35 | type: [Number], 36 | 'default': null 37 | }, 38 | 39 | /* 40 | * Force !important for style properties 41 | */ 42 | important: { 43 | type: [Boolean, Array], 44 | default: false 45 | } 46 | }, 47 | data: function data() { 48 | return { 49 | // data property for v-model binding with real textarea tag 50 | val: null, 51 | // works when content height becomes more then value of the maxHeight property 52 | maxHeightScroll: false, 53 | height: 'auto' 54 | }; 55 | }, 56 | computed: { 57 | computedStyles: function computedStyles() { 58 | if (!this.autosize) return {}; 59 | return { 60 | resize: !this.isResizeImportant ? 'none' : 'none !important', 61 | height: this.height, 62 | overflow: this.maxHeightScroll ? 'auto' : !this.isOverflowImportant ? 'hidden' : 'hidden !important' 63 | }; 64 | }, 65 | isResizeImportant: function isResizeImportant() { 66 | var imp = this.important; 67 | return imp === true || Array.isArray(imp) && imp.includes('resize'); 68 | }, 69 | isOverflowImportant: function isOverflowImportant() { 70 | var imp = this.important; 71 | return imp === true || Array.isArray(imp) && imp.includes('overflow'); 72 | }, 73 | isHeightImportant: function isHeightImportant() { 74 | var imp = this.important; 75 | return imp === true || Array.isArray(imp) && imp.includes('height'); 76 | } 77 | }, 78 | watch: { 79 | value: function value(val) { 80 | this.val = val; 81 | }, 82 | val: function val(_val) { 83 | this.$nextTick(this.resize); 84 | this.$emit('input', _val); 85 | }, 86 | minHeight: function minHeight() { 87 | this.$nextTick(this.resize); 88 | }, 89 | maxHeight: function maxHeight() { 90 | this.$nextTick(this.resize); 91 | }, 92 | autosize: function autosize(val) { 93 | if (val) this.resize(); 94 | } 95 | }, 96 | methods: { 97 | resize: function resize() { 98 | var _this = this; 99 | 100 | var important = this.isHeightImportant ? 'important' : ''; 101 | this.height = "auto".concat(important ? ' !important' : ''); 102 | this.$nextTick(function () { 103 | var contentHeight = _this.$el.scrollHeight + 1; 104 | 105 | if (_this.minHeight) { 106 | contentHeight = contentHeight < _this.minHeight ? _this.minHeight : contentHeight; 107 | } 108 | 109 | if (_this.maxHeight) { 110 | if (contentHeight > _this.maxHeight) { 111 | contentHeight = _this.maxHeight; 112 | _this.maxHeightScroll = true; 113 | } else { 114 | _this.maxHeightScroll = false; 115 | } 116 | } 117 | 118 | var heightVal = contentHeight + 'px'; 119 | _this.height = "".concat(heightVal).concat(important ? ' !important' : ''); 120 | }); 121 | return this; 122 | } 123 | }, 124 | created: function created() { 125 | this.val = this.value; 126 | }, 127 | mounted: function mounted() { 128 | this.resize(); 129 | } 130 | }; 131 | 132 | function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier 133 | /* server only */ 134 | , shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { 135 | if (typeof shadowMode !== 'boolean') { 136 | createInjectorSSR = createInjector; 137 | createInjector = shadowMode; 138 | shadowMode = false; 139 | } // Vue.extend constructor export interop. 140 | 141 | 142 | var options = typeof script === 'function' ? script.options : script; // render functions 143 | 144 | if (template && template.render) { 145 | options.render = template.render; 146 | options.staticRenderFns = template.staticRenderFns; 147 | options._compiled = true; // functional template 148 | 149 | if (isFunctionalTemplate) { 150 | options.functional = true; 151 | } 152 | } // scopedId 153 | 154 | 155 | if (scopeId) { 156 | options._scopeId = scopeId; 157 | } 158 | 159 | var hook; 160 | 161 | if (moduleIdentifier) { 162 | // server build 163 | hook = function hook(context) { 164 | // 2.3 injection 165 | context = context || // cached call 166 | this.$vnode && this.$vnode.ssrContext || // stateful 167 | this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional 168 | // 2.2 with runInNewContext: true 169 | 170 | if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { 171 | context = __VUE_SSR_CONTEXT__; 172 | } // inject component styles 173 | 174 | 175 | if (style) { 176 | style.call(this, createInjectorSSR(context)); 177 | } // register component module identifier for async chunk inference 178 | 179 | 180 | if (context && context._registeredComponents) { 181 | context._registeredComponents.add(moduleIdentifier); 182 | } 183 | }; // used by ssr in case component is cached and beforeCreate 184 | // never gets called 185 | 186 | 187 | options._ssrRegister = hook; 188 | } else if (style) { 189 | hook = shadowMode ? function () { 190 | style.call(this, createInjectorShadow(this.$root.$options.shadowRoot)); 191 | } : function (context) { 192 | style.call(this, createInjector(context)); 193 | }; 194 | } 195 | 196 | if (hook) { 197 | if (options.functional) { 198 | // register for functional component in vue file 199 | var originalRender = options.render; 200 | 201 | options.render = function renderWithStyleInjection(h, context) { 202 | hook.call(context); 203 | return originalRender(h, context); 204 | }; 205 | } else { 206 | // inject component registration as beforeCreate hook 207 | var existing = options.beforeCreate; 208 | options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; 209 | } 210 | } 211 | 212 | return script; 213 | } 214 | 215 | var normalizeComponent_1 = normalizeComponent; 216 | 217 | /* script */ 218 | const __vue_script__ = script; 219 | 220 | /* template */ 221 | var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('textarea',{directives:[{name:"model",rawName:"v-model",value:(_vm.val),expression:"val"}],style:(_vm.computedStyles),domProps:{"value":(_vm.val)},on:{"focus":_vm.resize,"input":function($event){if($event.target.composing){ return; }_vm.val=$event.target.value;}}})}; 222 | var __vue_staticRenderFns__ = []; 223 | 224 | /* style */ 225 | const __vue_inject_styles__ = undefined; 226 | /* scoped */ 227 | const __vue_scope_id__ = undefined; 228 | /* module identifier */ 229 | const __vue_module_identifier__ = undefined; 230 | /* functional template */ 231 | const __vue_is_functional_template__ = false; 232 | /* style inject */ 233 | 234 | /* style inject SSR */ 235 | 236 | 237 | 238 | var TextareaAutosize = normalizeComponent_1( 239 | { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, 240 | __vue_inject_styles__, 241 | __vue_script__, 242 | __vue_scope_id__, 243 | __vue_is_functional_template__, 244 | __vue_module_identifier__, 245 | undefined, 246 | undefined 247 | ); 248 | 249 | var version = '1.1.1'; 250 | 251 | var install = function install(Vue) { 252 | Vue.component('TextareaAutosize', TextareaAutosize); 253 | }; 254 | 255 | var plugin = { 256 | install: install, 257 | version: version 258 | }; 259 | 260 | if (typeof window !== 'undefined' && window.Vue) { 261 | window.Vue.use(plugin); 262 | } 263 | 264 | return plugin; 265 | 266 | })); 267 | -------------------------------------------------------------------------------- /dist/vue-textarea-autosize.umd.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-textarea-autosize v1.1.1 3 | * (c) 2019 Saymon 4 | * Released under the MIT License. 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self,t.VueTextareaAutosize=e())}(this,function(){"use strict";function t(t,e,i,n,o,r,a,s,u,c){"boolean"!=typeof a&&(u=s,s=a,a=!1);var h="function"==typeof i?i.options:i;t&&t.render&&(h.render=t.render,h.staticRenderFns=t.staticRenderFns,h._compiled=!0,o&&(h.functional=!0)),n&&(h._scopeId=n);var l;if(r?(l=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),e&&e.call(this,u(t)),t&&t._registeredComponents&&t._registeredComponents.add(r)},h._ssrRegister=l):e&&(l=a?function(){e.call(this,c(this.$root.$options.shadowRoot))}:function(t){e.call(this,s(t))}),l)if(h.functional){var d=h.render;h.render=function(t,e){return l.call(e),d(t,e)}}else{var f=h.beforeCreate;h.beforeCreate=f?[].concat(f,l):[l]}return i}var e={name:"TextareaAutosize",props:{value:{type:[String,Number],default:""},autosize:{type:Boolean,default:!0},minHeight:{type:[Number],default:null},maxHeight:{type:[Number],default:null},important:{type:[Boolean,Array],default:!1}},data:function(){return{val:null,maxHeightScroll:!1,height:"auto"}},computed:{computedStyles:function(){return this.autosize?{resize:this.isResizeImportant?"none !important":"none",height:this.height,overflow:this.maxHeightScroll?"auto":this.isOverflowImportant?"hidden !important":"hidden"}:{}},isResizeImportant:function(){var t=this.important;return!0===t||Array.isArray(t)&&t.includes("resize")},isOverflowImportant:function(){var t=this.important;return!0===t||Array.isArray(t)&&t.includes("overflow")},isHeightImportant:function(){var t=this.important;return!0===t||Array.isArray(t)&&t.includes("height")}},watch:{value:function(t){this.val=t},val:function(t){this.$nextTick(this.resize),this.$emit("input",t)},minHeight:function(){this.$nextTick(this.resize)},maxHeight:function(){this.$nextTick(this.resize)},autosize:function(t){t&&this.resize()}},methods:{resize:function(){var t=this,e=this.isHeightImportant?"important":"";return this.height="auto".concat(e?" !important":""),this.$nextTick(function(){var i=t.$el.scrollHeight+1;t.minHeight&&(i=it.maxHeight?(i=t.maxHeight,t.maxHeightScroll=!0):t.maxHeightScroll=!1);var n=i+"px";t.height="".concat(n).concat(e?" !important":"")}),this}},created:function(){this.val=this.value},mounted:function(){this.resize()}},i=t;const n=e;var o=function(){var t=this,e=t.$createElement;return(t._self._c||e)("textarea",{directives:[{name:"model",rawName:"v-model",value:t.val,expression:"val"}],style:t.computedStyles,domProps:{value:t.val},on:{focus:t.resize,input:function(e){e.target.composing||(t.val=e.target.value)}}})},r=[];var a=i({render:o,staticRenderFns:r},void 0,n,void 0,!1,void 0,void 0,void 0),s=function(t){t.component("TextareaAutosize",a)},u={install:s,version:"1.1.1"};return"undefined"!=typeof window&&window.Vue&&window.Vue.use(u),u}); -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | base: '/vue-textarea-autosize/', 3 | plugins: [ 4 | require('./plugin.js') 5 | ], 6 | locales: { 7 | '/': { 8 | lang: 'en-US', 9 | title: 'VueTextareaAutosize', 10 | description: 'TextareaAutosize plugin for Vue.js' 11 | } 12 | }, 13 | themeConfig: { 14 | repo: 'https://github.com/devstark-com/vue-textarea-autosize/', 15 | docsDir: 'docs', 16 | locales: { 17 | '/': { 18 | label: 'English', 19 | selectText: 'Languages', 20 | editLinkText: 'Edit this page on GitHub', 21 | nav: [{ 22 | text: 'Release Notes', 23 | link: 'https://github.com/devstark-com/vue-textarea-autosize/releases' 24 | }], 25 | sidebar: [ 26 | '/', 27 | '/guide.md' 28 | ] 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/.vuepress/plugin.js: -------------------------------------------------------------------------------- 1 | const { version } = require('../../package.json') 2 | 3 | module.exports = (/*options, ctx*/) => ({ 4 | async enhanceAppFiles () { 5 | const code = `export default ({ Vue }) => { 6 | Vue.mixin({ 7 | computed: { 8 | $version () { 9 | return '${version}' 10 | } 11 | } 12 | }) 13 | }` 14 | return [{ 15 | name: 'vuepress-plugin-vue-cli-plugin-p11n', 16 | content: code 17 | }] 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | The plugin for [Vue.js](http://vuejs.org) provides the dead-simple Vue component implements textarea with automatically adjustable height. 4 | 5 | --- 6 | 7 | # 🛠 Features 8 | 9 | - Bind with `v-model` 10 | - Set min/max height limits with appropriate reactive props 11 | - Enable/disable auto resizing dynamically 12 | - Trigger resize manually with `$refs.myTextarea.resize()` 13 | - Handle all native events with `.native` modifier [read more](https://vuejs.org/v2/guide/components.html#Binding-Native-Events-to-Components) 14 | - Use native attrs like this `` 15 | - Force `!important` for style properties e.g. when using [http://cleanslatecss.com/](http://cleanslatecss.com/) 16 | - There is no CSS out of the box, so feel free to style it as you wish 17 | 18 | --- 19 | 20 | [GitHub](https://github.com/devstark-com/vue-textarea-autosize/) | 21 | [Demo](https://codesandbox.io/s/vuetextareaautosize-demo-vikj3?fontsize=14) | [More packages](https://github.com/devstark-com) by [DevStark](https://devstark.com/) 22 | -------------------------------------------------------------------------------- /docs/guide.md: -------------------------------------------------------------------------------- 1 | # Guide 2 | 3 | ## Installation 4 | 5 | ### NPM 6 | 7 | ```sh 8 | $ npm i -S vue-textarea-autosize 9 | ``` 10 | 11 | ### Yarn 12 | 13 | ```sh 14 | $ yarn add vue-textarea-autosize 15 | ``` 16 | 17 | ### Register the plugin 18 | 19 | ```javascript 20 | import Vue from 'vue' 21 | import VueTextareaAutosize from 'vue-textarea-autosize' 22 | 23 | Vue.use(VueTextareaAutosize) 24 | ``` 25 | ## Usage 26 | 27 | ```html 28 | 36 | ``` 37 | 38 | ```js 39 | this.$refs.myTextarea.$el.focus() 40 | this.$refs.myTextarea.$el.blur() 41 | this.$refs.myTextarea.$el.select() 42 | ``` 43 | ## Interface 44 | 45 | ### Props 46 | 47 | | Props | Required | Type | Default | Description | 48 | | ---------------- | --------- | --------------- | --------- | -------------| 49 | | `v-model` | no | String, Number | '' | value binding | 50 | | `value` | no | String, Number | '' | part of the `v-model` binding | 51 | | `autosize`         | no | Boolean      | true | allows to enable/disable auto resizing dynamically | 52 | | `minHeight` | no | Number | null | min textarea height (reactive behavior) | 53 | | `maxHeight` | no | Number | null | max textarea height (reactive behavior) | 54 | | `important` | no | Boolean, Array | false | Forces `!important` for style properties e.g. when using [http://cleanslatecss.com/](http://cleanslatecss.com/).

Allowed values: `true`, `false` and

all or some of

`['resize', 'overflow', 'height']`| 55 | 56 | ### Events 57 | 58 | | Name | Params | Description | 59 | | ----------|:---------|--------------| 60 | | input | value | fires on textarea content changed. part of the `v-model` binding. [read more](https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events) | 61 | 62 | ### Slots 63 | 64 | There are no slots available 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-textarea-autosize", 3 | "description": "Vue component provides textarea with automatically adjustable height and without any wrappers and dependencies", 4 | "version": "1.1.1", 5 | "author": { 6 | "name": "Saymon", 7 | "email": "saymon.biz@gmail.com" 8 | }, 9 | "scripts": { 10 | "serve": "vue-cli-service serve", 11 | "build": "vue-cli-service build", 12 | "lint": "vue-cli-service lint", 13 | "demo": "vue-cli-service demo", 14 | "docs": "npm run docs:serve", 15 | "docs:build": "vue-cli-service docs --mode build", 16 | "docs:serve": "vue-cli-service docs --mode serve", 17 | "prepublish": "vue-cli-service lint && vue-cli-service docs --mode build && vue-cli-service build", 18 | "start": "vue-cli-service serve" 19 | }, 20 | "main": "dist/vue-textarea-autosize.common.js", 21 | "module": "dist/vue-textarea-autosize.esm.js", 22 | "unpkg": "dist/vue-textarea-autosize.umd.min.js", 23 | "files": [ 24 | "dist/vue-textarea-autosize.common.js", 25 | "dist/vue-textarea-autosize.umd.min.js", 26 | "dist/vue-textarea-autosize.umd.js", 27 | "dist/vue-textarea-autosize.esm.js", 28 | "src" 29 | ], 30 | "dependencies": { 31 | "core-js": "^2.6.5" 32 | }, 33 | "devDependencies": { 34 | "@vue/cli-plugin-babel": "^3.11.0", 35 | "@vue/cli-plugin-eslint": "^3.11.0", 36 | "@vue/cli-service": "^3.11.0", 37 | "babel-eslint": "^10.0.1", 38 | "eslint": "^5.16.0", 39 | "eslint-plugin-vue": "^5.0.0", 40 | "vue-cli-plugin-p11n": "^0.4.0", 41 | "vue-template-compiler": "^2.6.10", 42 | "vue": "^2.6.10" 43 | }, 44 | "eslintConfig": { 45 | "root": true, 46 | "env": { 47 | "node": true 48 | }, 49 | "extends": [ 50 | "plugin:vue/essential", 51 | "eslint:recommended" 52 | ], 53 | "rules": {}, 54 | "parserOptions": { 55 | "parser": "babel-eslint" 56 | } 57 | }, 58 | "postcss": { 59 | "plugins": { 60 | "autoprefixer": {} 61 | } 62 | }, 63 | "browserslist": [ 64 | "> 1%", 65 | "last 2 versions" 66 | ], 67 | "jsdelivr": "dist/textarea-autosize.umd.min.js", 68 | "sideeffects": false, 69 | "keywords": [ 70 | "vue", 71 | "vuejs", 72 | "plugin", 73 | "textarea", 74 | "size", 75 | "auto", 76 | "autosize", 77 | "height" 78 | ], 79 | "license": "MIT", 80 | "repository": { 81 | "type": "git", 82 | "url": "git+https://github.com/devstark-com/vue-textarea-autosize.git" 83 | }, 84 | "bugs": { 85 | "url": "https://github.com/devstark-com/vue-textarea-autosize/issues" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 12 | 24 | 34 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devstark-com/vue-textarea-autosize/8e767ea21863b3e8607b1808b89e7b5a0e3aa98c/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/TextareaAutosize.vue: -------------------------------------------------------------------------------- 1 | 8 | 120 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import TextareaAutosize from './components/TextareaAutosize.vue' 2 | const version = '__VERSION__' 3 | 4 | const install = Vue => { 5 | Vue.component('TextareaAutosize', TextareaAutosize) 6 | } 7 | 8 | const plugin = { 9 | install, 10 | version 11 | } 12 | 13 | export default plugin 14 | 15 | if (typeof window !== 'undefined' && window.Vue) { 16 | window.Vue.use(plugin) 17 | } 18 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import './plugin' 4 | 5 | Vue.config.productionTip = false 6 | 7 | new Vue({ 8 | render: h => h(App), 9 | }).$mount('#app') 10 | -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is plugin stub for main.js 3 | */ 4 | 5 | import Vue from 'vue' 6 | import plugin from './index' 7 | 8 | Vue.use(plugin) 9 | --------------------------------------------------------------------------------