UNPKG

bootstrap-vue

Version:

With more than 85 components, over 45 available plugins, several directives, and 1000+ icons, BootstrapVue provides one of the most comprehensive implementations of the Bootstrap v4 component and grid system available for Vue.js v2.6, complete with extens

217 lines (191 loc) 8.78 kB
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } import Vue from '../../vue'; import { NAME_FORM_TEXTAREA } from '../../constants/components'; import { getCS, getStyle, isVisible, requestAF, setStyle } from '../../utils/dom'; import { isNull } from '../../utils/inspect'; import { mathCeil, mathMax, mathMin } from '../../utils/math'; import { toInteger, toFloat } from '../../utils/number'; import formMixin from '../../mixins/form'; import formSelectionMixin from '../../mixins/form-selection'; import formSizeMixin from '../../mixins/form-size'; import formStateMixin from '../../mixins/form-state'; import formTextMixin from '../../mixins/form-text'; import formValidityMixin from '../../mixins/form-validity'; import idMixin from '../../mixins/id'; import listenOnRootMixin from '../../mixins/listen-on-root'; import listenersMixin from '../../mixins/listeners'; import { VBVisible } from '../../directives/visible/visible'; // @vue/component export var BFormTextarea = /*#__PURE__*/Vue.extend({ name: NAME_FORM_TEXTAREA, directives: { 'b-visible': VBVisible }, // Mixin order is important! mixins: [listenersMixin, idMixin, listenOnRootMixin, formMixin, formSizeMixin, formStateMixin, formTextMixin, formSelectionMixin, formValidityMixin], props: { rows: { type: [Number, String], default: 2 }, maxRows: { type: [Number, String] // default: null }, wrap: { // 'soft', 'hard' or 'off'. Browser default is 'soft' type: String, default: 'soft' }, noResize: { // Disable the resize handle of textarea type: Boolean, default: false }, noAutoShrink: { // When in auto resize mode, disable shrinking to content height type: Boolean, default: false } }, data: function data() { return { heightInPx: null }; }, computed: { computedStyle: function computedStyle() { var styles = { // Setting `noResize` to true will disable the ability for the user to // manually resize the textarea. We also disable when in auto height mode resize: !this.computedRows || this.noResize ? 'none' : null }; if (!this.computedRows) { // Conditionally set the computed CSS height when auto rows/height is enabled // We avoid setting the style to `null`, which can override user manual resize handle styles.height = this.heightInPx; // We always add a vertical scrollbar to the textarea when auto-height is // enabled so that the computed height calculation returns a stable value styles.overflowY = 'scroll'; } return styles; }, computedMinRows: function computedMinRows() { // Ensure rows is at least 2 and positive (2 is the native textarea value) // A value of 1 can cause issues in some browsers, and most browsers // only support 2 as the smallest value return mathMax(toInteger(this.rows, 2), 2); }, computedMaxRows: function computedMaxRows() { return mathMax(this.computedMinRows, toInteger(this.maxRows, 0)); }, computedRows: function computedRows() { // This is used to set the attribute 'rows' on the textarea // If auto-height is enabled, then we return `null` as we use CSS to control height return this.computedMinRows === this.computedMaxRows ? this.computedMinRows : null; }, computedAttrs: function computedAttrs() { var disabled = this.disabled, required = this.required; return { id: this.safeId(), name: this.name || null, form: this.form || null, disabled: disabled, placeholder: this.placeholder || null, required: required, autocomplete: this.autocomplete || null, readonly: this.readonly || this.plaintext, rows: this.computedRows, wrap: this.wrap || null, 'aria-required': this.required ? 'true' : null, 'aria-invalid': this.computedAriaInvalid }; }, computedListeners: function computedListeners() { return _objectSpread(_objectSpread({}, this.bvListeners), {}, { input: this.onInput, change: this.onChange, blur: this.onBlur }); } }, watch: { localValue: function localValue() { this.setHeight(); } }, mounted: function mounted() { this.setHeight(); }, methods: { // Called by intersection observer directive visibleCallback: function visibleCallback(visible) /* istanbul ignore next */ { if (visible) { // We use a `$nextTick()` here just to make sure any // transitions or portalling have completed this.$nextTick(this.setHeight); } }, setHeight: function setHeight() { var _this = this; this.$nextTick(function () { requestAF(function () { _this.heightInPx = _this.computeHeight(); }); }); }, computeHeight: function computeHeight() /* istanbul ignore next: can't test getComputedStyle in JSDOM */ { if (this.$isServer || !isNull(this.computedRows)) { return null; } var el = this.$el; // Element must be visible (not hidden) and in document // Must be checked after above checks if (!isVisible(el)) { return null; } // Get current computed styles var computedStyle = getCS(el); // Height of one line of text in px var lineHeight = toFloat(computedStyle.lineHeight, 1); // Calculate height of border and padding var border = toFloat(computedStyle.borderTopWidth, 0) + toFloat(computedStyle.borderBottomWidth, 0); var padding = toFloat(computedStyle.paddingTop, 0) + toFloat(computedStyle.paddingBottom, 0); // Calculate offset var offset = border + padding; // Minimum height for min rows (which must be 2 rows or greater for cross-browser support) var minHeight = lineHeight * this.computedMinRows + offset; // Get the current style height (with `px` units) var oldHeight = getStyle(el, 'height') || computedStyle.height; // Probe scrollHeight by temporarily changing the height to `auto` setStyle(el, 'height', 'auto'); var scrollHeight = el.scrollHeight; // Place the original old height back on the element, just in case `computedProp` // returns the same value as before setStyle(el, 'height', oldHeight); // Calculate content height in 'rows' (scrollHeight includes padding but not border) var contentRows = mathMax((scrollHeight - padding) / lineHeight, 2); // Calculate number of rows to display (limited within min/max rows) var rows = mathMin(mathMax(contentRows, this.computedMinRows), this.computedMaxRows); // Calculate the required height of the textarea including border and padding (in pixels) var height = mathMax(mathCeil(rows * lineHeight + offset), minHeight); // Computed height remains the larger of `oldHeight` and new `height`, // when height is in `sticky` mode (prop `no-auto-shrink` is true) if (this.noAutoShrink && toFloat(oldHeight, 0) > height) { return oldHeight; } // Return the new computed CSS height in px units return "".concat(height, "px"); } }, render: function render(h) { return h('textarea', { ref: 'input', class: this.computedClass, style: this.computedStyle, directives: [{ name: 'b-visible', value: this.visibleCallback, // If textarea is within 640px of viewport, consider it visible modifiers: { '640': true } }], attrs: this.computedAttrs, domProps: { value: this.localValue }, on: this.computedListeners }); } });