11import Vue from '../../utils/vue'
2+ import { VBVisible } from '../../directives/visible'
23import idMixin from '../../mixins/id'
34import formMixin from '../../mixins/form'
45import formSizeMixin from '../../mixins/form-size'
56import formStateMixin from '../../mixins/form-state'
67import formTextMixin from '../../mixins/form-text'
78import formSelectionMixin from '../../mixins/form-selection'
89import formValidityMixin from '../../mixins/form-validity'
9- import { getCS , isVisible } from '../../utils/dom'
10+ import listenOnRootMixin from '../../mixins/listen-on-root'
11+ import { getCS , isVisible , requestAF } from '../../utils/dom'
1012import { isNull } from '../../utils/inspect'
1113
1214// @vue /component
1315export const BFormTextarea = /*#__PURE__*/ Vue . extend ( {
1416 name : 'BFormTextarea' ,
17+ directives : {
18+ 'b-visible' : VBVisible
19+ } ,
1520 mixins : [
1621 idMixin ,
22+ listenOnRootMixin ,
1723 formMixin ,
1824 formSizeMixin ,
1925 formStateMixin ,
@@ -48,7 +54,6 @@ export const BFormTextarea = /*#__PURE__*/ Vue.extend({
4854 } ,
4955 data ( ) {
5056 return {
51- dontResize : true ,
5257 heightInPx : null
5358 }
5459 } ,
@@ -60,64 +65,52 @@ export const BFormTextarea = /*#__PURE__*/ Vue.extend({
6065 resize : ! this . computedRows || this . noResize ? 'none' : null
6166 }
6267 if ( ! this . computedRows ) {
63- // Conditionaly set the computed CSS height when auto rows/height is enabled.
64- // We avoid setting the style to null, which can override user manual resize handle.
68+ // Conditionally set the computed CSS height when auto rows/height is enabled
69+ // We avoid setting the style to ` null` , which can override user manual resize handle
6570 styles . height = this . heightInPx
6671 // We always add a vertical scrollbar to the textarea when auto-height is
67- // enabled so that the computed height calcaultion returns a stable value.
72+ // enabled so that the computed height calculation returns a stable value
6873 styles . overflowY = 'scroll'
6974 }
7075 return styles
7176 } ,
7277 computedMinRows ( ) {
73- // Ensure rows is at least 2 and positive (2 is the native textarea value).
74- // A value of 1 can cause issues in some browsers, and most browsers only support
75- // 2 as the smallest value.
78+ // Ensure rows is at least 2 and positive (2 is the native textarea value)
79+ // A value of 1 can cause issues in some browsers, and most browsers
80+ // only support 2 as the smallest value
7681 return Math . max ( parseInt ( this . rows , 10 ) || 2 , 2 )
7782 } ,
7883 computedMaxRows ( ) {
7984 return Math . max ( this . computedMinRows , parseInt ( this . maxRows , 10 ) || 0 )
8085 } ,
8186 computedRows ( ) {
82- // This is used to set the attribute 'rows' on the textarea.
83- // If auto-height is enabled, then we return null as we use CSS to control height.
87+ // This is used to set the attribute 'rows' on the textarea
88+ // If auto-height is enabled, then we return ` null` as we use CSS to control height
8489 return this . computedMinRows === this . computedMaxRows ? this . computedMinRows : null
8590 }
8691 } ,
8792 watch : {
88- dontResize ( newVal , oldval ) {
89- if ( ! newVal ) {
90- this . setHeight ( )
91- }
92- } ,
9393 localValue ( newVal , oldVal ) {
9494 this . setHeight ( )
9595 }
9696 } ,
9797 mounted ( ) {
98- // Enable opt-in resizing once mounted
99- this . $nextTick ( ( ) => {
100- this . dontResize = false
101- } )
102- } ,
103- activated ( ) {
104- // If we are being re-activated in <keep-alive>, enable opt-in resizing
105- this . $nextTick ( ( ) => {
106- this . dontResize = false
107- } )
108- } ,
109- deactivated ( ) {
110- // If we are in a deactivated <keep-alive>, disable opt-in resizing
111- this . dontResize = true
112- } ,
113- beforeDestroy ( ) {
114- /* istanbul ignore next */
115- this . dontResize = true
98+ this . setHeight ( )
11699 } ,
117100 methods : {
101+ // Called by intersection observer directive
102+ visibleCallback ( visible ) /* istanbul ignore next */ {
103+ if ( visible ) {
104+ // We use a `$nextTick()` here just to make sure any
105+ // transitions or portalling have completed
106+ this . $nextTick ( this . setHeight )
107+ }
108+ } ,
118109 setHeight ( ) {
119110 this . $nextTick ( ( ) => {
120- this . heightInPx = this . computeHeight ( )
111+ requestAF ( ( ) => {
112+ this . heightInPx = this . computeHeight ( )
113+ } )
121114 } )
122115 } ,
123116 computeHeight ( ) /* istanbul ignore next: can't test getComputedStyle in JSDOM */ {
@@ -127,7 +120,7 @@ export const BFormTextarea = /*#__PURE__*/ Vue.extend({
127120
128121 const el = this . $el
129122
130- // Element must be visible (not hidden) and in document.
123+ // Element must be visible (not hidden) and in document
131124 // Must be checked after above checks
132125 if ( ! isVisible ( el ) ) {
133126 return null
@@ -153,18 +146,18 @@ export const BFormTextarea = /*#__PURE__*/ Vue.extend({
153146 // Probe scrollHeight by temporarily changing the height to `auto`
154147 el . style . height = 'auto'
155148 const scrollHeight = el . scrollHeight
156- // Place the original old height back on the element, just in case this computedProp
157- // returns the same value as before.
149+ // Place the original old height back on the element, just in case ` computedProp`
150+ // returns the same value as before
158151 el . style . height = oldHeight
159152
160- // Calculate content height in " rows" (scrollHeight includes padding but not border)
153+ // Calculate content height in ' rows' (scrollHeight includes padding but not border)
161154 const contentRows = Math . max ( ( scrollHeight - padding ) / lineHeight , 2 )
162155 // Calculate number of rows to display (limited within min/max rows)
163156 const rows = Math . min ( Math . max ( contentRows , this . computedMinRows ) , this . computedMaxRows )
164157 // Calculate the required height of the textarea including border and padding (in pixels)
165158 const height = Math . max ( Math . ceil ( rows * lineHeight + offset ) , minHeight )
166159
167- // Computed height remains the larger of oldHeight and new height,
160+ // Computed height remains the larger of ` oldHeight` and new ` height` ,
168161 // when height is in `sticky` mode (prop `no-auto-shrink` is true)
169162 if ( this . noAutoShrink && ( parseFloat ( oldHeight ) || 0 ) > height ) {
170163 return oldHeight
@@ -184,9 +177,13 @@ export const BFormTextarea = /*#__PURE__*/ Vue.extend({
184177 directives : [
185178 {
186179 name : 'model' ,
187- rawName : 'v-model' ,
188- value : self . localValue ,
189- expression : 'localValue'
180+ value : self . localValue
181+ } ,
182+ {
183+ name : 'b-visible' ,
184+ value : this . visibleCallback ,
185+ // If textarea is within 640px of viewport, consider it visible
186+ modifiers : { '640' : true }
190187 }
191188 ] ,
192189 attrs : {
0 commit comments