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

257 lines (218 loc) 10 kB
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); 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 = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : 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 { extend } from '../vue'; import { EVENT_NAME_BLUR, EVENT_NAME_CHANGE, EVENT_NAME_INPUT, EVENT_NAME_UPDATE } from '../constants/events'; import { PROP_TYPE_BOOLEAN, PROP_TYPE_BOOLEAN_STRING, PROP_TYPE_FUNCTION, PROP_TYPE_NUMBER_STRING, PROP_TYPE_STRING } from '../constants/props'; import { attemptBlur, attemptFocus } from '../utils/dom'; import { stopEvent } from '../utils/events'; import { mathMax } from '../utils/math'; import { makeModelMixin } from '../utils/model'; import { toInteger, toFloat } from '../utils/number'; import { sortKeys } from '../utils/object'; import { hasPropFunction, makeProp, makePropsConfigurable } from '../utils/props'; import { toString } from '../utils/string'; // --- Constants --- var _makeModelMixin = makeModelMixin('value', { type: PROP_TYPE_NUMBER_STRING, defaultValue: '', event: EVENT_NAME_UPDATE }), modelMixin = _makeModelMixin.mixin, modelProps = _makeModelMixin.props, MODEL_PROP_NAME = _makeModelMixin.prop, MODEL_EVENT_NAME = _makeModelMixin.event; export { MODEL_PROP_NAME, MODEL_EVENT_NAME }; // --- Props --- export var props = makePropsConfigurable(sortKeys(_objectSpread(_objectSpread({}, modelProps), {}, { ariaInvalid: makeProp(PROP_TYPE_BOOLEAN_STRING, false), autocomplete: makeProp(PROP_TYPE_STRING), // Debounce timeout (in ms). Not applicable with `lazy` prop debounce: makeProp(PROP_TYPE_NUMBER_STRING, 0), formatter: makeProp(PROP_TYPE_FUNCTION), // Only update the `v-model` on blur/change events lazy: makeProp(PROP_TYPE_BOOLEAN, false), lazyFormatter: makeProp(PROP_TYPE_BOOLEAN, false), number: makeProp(PROP_TYPE_BOOLEAN, false), placeholder: makeProp(PROP_TYPE_STRING), plaintext: makeProp(PROP_TYPE_BOOLEAN, false), readonly: makeProp(PROP_TYPE_BOOLEAN, false), trim: makeProp(PROP_TYPE_BOOLEAN, false) })), 'formTextControls'); // --- Mixin --- // @vue/component export var formTextMixin = extend({ mixins: [modelMixin], props: props, data: function data() { var value = this[MODEL_PROP_NAME]; return { localValue: toString(value), vModelValue: this.modifyValue(value) }; }, computed: { computedClass: function computedClass() { var plaintext = this.plaintext, type = this.type; var isRange = type === 'range'; var isColor = type === 'color'; return [{ // Range input needs class `custom-range` 'custom-range': isRange, // `plaintext` not supported by `type="range"` or `type="color"` 'form-control-plaintext': plaintext && !isRange && !isColor, // `form-control` not used by `type="range"` or `plaintext` // Always used by `type="color"` 'form-control': isColor || !plaintext && !isRange }, this.sizeFormClass, this.stateClass]; }, computedDebounce: function computedDebounce() { // Ensure we have a positive number equal to or greater than 0 return mathMax(toInteger(this.debounce, 0), 0); }, hasFormatter: function hasFormatter() { return hasPropFunction(this.formatter); } }, watch: _defineProperty({}, MODEL_PROP_NAME, function (newValue) { var stringifyValue = toString(newValue); var modifiedValue = this.modifyValue(newValue); if (stringifyValue !== this.localValue || modifiedValue !== this.vModelValue) { // Clear any pending debounce timeout, as we are overwriting the user input this.clearDebounce(); // Update the local values this.localValue = stringifyValue; this.vModelValue = modifiedValue; } }), created: function created() { // Create private non-reactive props this.$_inputDebounceTimer = null; }, beforeDestroy: function beforeDestroy() { this.clearDebounce(); }, methods: { clearDebounce: function clearDebounce() { clearTimeout(this.$_inputDebounceTimer); this.$_inputDebounceTimer = null; }, formatValue: function formatValue(value, event) { var force = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; value = toString(value); if (this.hasFormatter && (!this.lazyFormatter || force)) { value = this.formatter(value, event); } return value; }, modifyValue: function modifyValue(value) { value = toString(value); // Emulate `.trim` modifier behaviour if (this.trim) { value = value.trim(); } // Emulate `.number` modifier behaviour if (this.number) { value = toFloat(value, value); } return value; }, updateValue: function updateValue(value) { var _this = this; var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var lazy = this.lazy; if (lazy && !force) { return; } // Make sure to always clear the debounce when `updateValue()` // is called, even when the v-model hasn't changed this.clearDebounce(); // Define the shared update logic in a method to be able to use // it for immediate and debounced value changes var doUpdate = function doUpdate() { value = _this.modifyValue(value); if (value !== _this.vModelValue) { _this.vModelValue = value; _this.$emit(MODEL_EVENT_NAME, value); } else if (_this.hasFormatter) { // When the `vModelValue` hasn't changed but the actual input value // is out of sync, make sure to change it to the given one // Usually caused by browser autocomplete and how it triggers the // change or input event, or depending on the formatter function // https://github.com/bootstrap-vue/bootstrap-vue/issues/2657 // https://github.com/bootstrap-vue/bootstrap-vue/issues/3498 /* istanbul ignore next: hard to test */ var $input = _this.$refs.input; /* istanbul ignore if: hard to test out of sync value */ if ($input && value !== $input.value) { $input.value = value; } } }; // Only debounce the value update when a value greater than `0` // is set and we are not in lazy mode or this is a forced update var debounce = this.computedDebounce; if (debounce > 0 && !lazy && !force) { this.$_inputDebounceTimer = setTimeout(doUpdate, debounce); } else { // Immediately update the v-model doUpdate(); } }, onInput: function onInput(event) { // `event.target.composing` is set by Vue // https://github.com/vuejs/vue/blob/dev/src/platforms/web/runtime/directives/model.js // TODO: Is this needed now with the latest Vue? /* istanbul ignore if: hard to test composition events */ if (event.target.composing) { return; } var value = event.target.value; var formattedValue = this.formatValue(value, event); // Exit when the `formatter` function strictly returned `false` // or prevented the input event /* istanbul ignore next */ if (formattedValue === false || event.defaultPrevented) { stopEvent(event, { propagation: false }); return; } this.localValue = formattedValue; this.updateValue(formattedValue); this.$emit(EVENT_NAME_INPUT, formattedValue); }, onChange: function onChange(event) { var value = event.target.value; var formattedValue = this.formatValue(value, event); // Exit when the `formatter` function strictly returned `false` // or prevented the input event /* istanbul ignore next */ if (formattedValue === false || event.defaultPrevented) { stopEvent(event, { propagation: false }); return; } this.localValue = formattedValue; this.updateValue(formattedValue, true); this.$emit(EVENT_NAME_CHANGE, formattedValue); }, onBlur: function onBlur(event) { // Apply the `localValue` on blur to prevent cursor jumps // on mobile browsers (e.g. caused by autocomplete) var value = event.target.value; var formattedValue = this.formatValue(value, event, true); if (formattedValue !== false) { // We need to use the modified value here to apply the // `.trim` and `.number` modifiers properly this.localValue = toString(this.modifyValue(formattedValue)); // We pass the formatted value here since the `updateValue` method // handles the modifiers itself this.updateValue(formattedValue, true); } // Emit native blur event this.$emit(EVENT_NAME_BLUR, event); }, focus: function focus() { // For external handler that may want a focus method if (!this.disabled) { attemptFocus(this.$el); } }, blur: function blur() { // For external handler that may want a blur method if (!this.disabled) { attemptBlur(this.$el); } } } });