Skip to content

Commit 0043b92

Browse files
Istadortmorehouse
authored andcommitted
fix(form-textarea): improved computedHeight calculation when in auto resize mode (#3012)
1 parent 436e8c1 commit 0043b92

File tree

1 file changed

+33
-27
lines changed

1 file changed

+33
-27
lines changed

src/components/form-textarea/form-textarea.js

+33-27
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export default {
6060
// The computed height for auto resize.
6161
// We avoid setting the style to null, which can override user manual resize.
6262
styles.height = this.computedHeight
63+
// We always add a vertical scrollbar to the textarea when auto-resize is
64+
// enabled so that the computed height calcaultion returns a stable value.
65+
styles.overflowY = 'scroll'
6366
}
6467
return styles
6568
},
@@ -77,9 +80,10 @@ export default {
7780
// If auto-resize is enabled, then we return null as we use CSS to control height.
7881
return this.computedMinRows === this.computedMaxRows ? this.computedMinRows : null
7982
},
80-
computedHeight() /* istanbul ignore next: can't test getComputedProperties */ {
83+
computedHeight() /* istanbul ignore next: can't test getComputedStyle in JSDOM */ {
8184
// We compare `computedRows` and `localValue` to `true`, a value
82-
// they both can't have at any time, to ensure reactivity
85+
// they both can't have at any time, to ensure reactivity of this
86+
// computed property.
8387
if (
8488
this.$isServer ||
8589
this.dontResize ||
@@ -91,48 +95,50 @@ export default {
9195

9296
const el = this.$el
9397

94-
// Element must be visible (not hidden) and in document
95-
// *Must* be checked after above checks
98+
// Element must be visible (not hidden) and in document.
99+
// Must be checked after above checks
96100
if (!isVisible(el)) {
97101
return null
98102
}
99103

100-
// Remember old height (includes `px` units) and reset it temporarily to `auto`
101-
const oldHeight = el.style.height
102-
el.style.height = 'auto'
103-
104104
// Get current computed styles
105105
const computedStyle = getCS(el)
106106
// Height of one line of text in px
107107
const lineHeight = parseFloat(computedStyle.lineHeight)
108-
// Minimum height for min rows (browser dependant)
109-
const minHeight = parseInt(computedStyle.height, 10) || lineHeight * this.computedMinRows
110-
// Calculate height of content
111-
const offset =
108+
// Calculate height of border and padding
109+
const border =
112110
(parseFloat(computedStyle.borderTopWidth) || 0) +
113-
(parseFloat(computedStyle.borderBottomWidth) || 0) +
114-
(parseFloat(computedStyle.paddingTop) || 0) +
115-
(parseFloat(computedStyle.paddingBottom) || 0)
116-
// Calculate content height in "rows"
117-
const contentRows = Math.max((el.scrollHeight - offset) / lineHeight, 2)
111+
(parseFloat(computedStyle.borderBottomWidth) || 0)
112+
const padding =
113+
(parseFloat(computedStyle.paddingTop) || 0) + (parseFloat(computedStyle.paddingBottom) || 0)
114+
// Calculate offset
115+
const offset = border + padding
116+
// Minimum height for min rows (which must be 2 rows or greater for cross-browser support)
117+
const minHeight = lineHeight * this.computedMinRows + offset
118+
119+
// Get the current style height (with `px` units)
120+
const oldHeight = el.style.height || computedStyle.height
121+
// Probe scrollHeight by temporarily changing the height to `auto`
122+
el.style.height = 'auto'
123+
const scrollHeight = el.scrollHeight
124+
// Place the original old height back on the element, just in case this computedProp
125+
// returns the same value as before.
126+
el.style.height = oldHeight
127+
128+
// Calculate content height in "rows" (scrollHeight includes padding but not border)
129+
const contentRows = Math.max((scrollHeight - padding) / lineHeight, 2)
118130
// Calculate number of rows to display (limited within min/max rows)
119131
const rows = Math.min(Math.max(contentRows, this.computedMinRows), this.computedMaxRows)
120132
// Calculate the required height of the textarea including border and padding (in pixels)
121133
const height = Math.max(Math.ceil(rows * lineHeight + offset), minHeight)
122134

123-
// Place old height back on element, just in case this computed prop returns the same value
124-
el.style.height = oldHeight
125-
126-
// Value of previous height (without px units appended)
127-
const oldHeightPx = parseFloat(oldHeight) || 0
128-
129-
if (this.noAutoShrink && oldHeightPx > height) {
130-
// Computed height remains the larger of oldHeight and new height
131-
// When height is `sticky` (no-auto-shrink is true)
135+
// Computed height remains the larger of oldHeight and new height,
136+
// when height is in `sticky` mode (prop `no-auto-shrink` is true)
137+
if (this.noAutoShrink && (parseFloat(oldHeight) || 0) > height) {
132138
return oldHeight
133139
}
134140

135-
// Return the new computed height in px units
141+
// Return the new computed CSS height in px units
136142
return `${height}px`
137143
}
138144
},

0 commit comments

Comments
 (0)