From b0ec668d9e7aa3c3f94159485e7923bc85ae3064 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 21 Nov 2018 00:28:07 -0400 Subject: [PATCH 01/11] [WIP] feat(form-input): Use new form-text mixin and update events/docs/tests --- src/components/form-input/form-input.js | 157 +++++------------------- 1 file changed, 30 insertions(+), 127 deletions(-) diff --git a/src/components/form-input/form-input.js b/src/components/form-input/form-input.js index ab72e0503f9..93177179181 100644 --- a/src/components/form-input/form-input.js +++ b/src/components/form-input/form-input.js @@ -2,6 +2,7 @@ import idMixin from '../../mixins/id' import formMixin from '../../mixins/form' import formSizeMixin from '../../mixins/form-size' import formStateMixin from '../../mixins/form-state' +import formTextMixin from '../../mixins/form-text' import formSelectionMixin from '../../mixins/form-selection' import formValidityMixin from '../../mixins/form-validity' import { arrayIncludes } from '../../utils/array' @@ -31,26 +32,39 @@ const TYPES = [ 'week' ] -// Custom event to update the model -const MODEL_EVENT = 'update:value' - export default { - mixins: [idMixin, formMixin, formSizeMixin, formStateMixin, formSelectionMixin, formValidityMixin], + mixins: [ + idMixin, + formMixin, + formSizeMixin, + formStateMixin, + formTextMixin, + formSelectionMixin, + formValidityMixin + ], render (h) { var self = this return h('input', { ref: 'input', class: self.inputClass, + directives: [ + { + name: 'model', + rawName: 'v-model', + value: self.localValue, + expression: 'localValue' + } + ], attrs: { id: self.safeId(), name: self.name, - form: this.form || null, + form: self.form || null, type: self.localType, disabled: self.disabled, - required: self.required, - readonly: self.readonly || self.plaintext, placeholder: self.placeholder, + required: self.required, autocomplete: self.autocomplete || null, + readonly: self.readonly || self.plaintext, min: self.min, max: self.max, step: self.step, @@ -60,61 +74,20 @@ export default { domProps: { value: self.localValue }, - directives: [ - { name: 'model', rawName: 'v-model', value: self.localValue, expression: 'localValue' } - ], on: { ...self.$listeners, input: self.onInput, - change: self.onChange + change: self.onChange, + blur: self.onBlur } }) }, - data () { - return { - localValue: this.value - } - }, - model: { - prop: 'value', - event: MODEL_EVENT - }, props: { - value: { - default: '' - }, type: { type: String, default: 'text', validator: type => arrayIncludes(TYPES, type) }, - ariaInvalid: { - type: [Boolean, String], - default: false - }, - readonly: { - type: Boolean, - default: false - }, - plaintext: { - type: Boolean, - default: false - }, - autocomplete: { - type: String, - default: null - }, - placeholder: { - type: String, - default: null - }, - formatter: { - type: Function - }, - lazyFormatter: { - type: Boolean, - default: false - }, noWheel: { // Disable mousewheel to prevent wheel from changing values (i.e. number/date). type: Boolean, @@ -137,35 +110,9 @@ export default { localType () { // We only allow certain types return arrayIncludes(TYPES, this.type) ? this.type : 'text' - }, - inputClass () { - return [ - { - 'custom-range': this.type === 'range', - // plaintext not supported by type=range or type=color - 'form-control-plaintext': this.plaintext && this.type !== 'range' && this.type !== 'color', - // form-control not used by type=range or plaintext. Always used by type=color - 'form-control': (!this.plaintext && this.type !== 'range') || this.type === 'color' - }, - this.sizeFormClass, - this.stateClass - ] - }, - computedAriaInvalid () { - if (!this.ariaInvalid || this.ariaInvalid === 'false') { - // this.ariaInvalid is null or false or 'false' - return this.computedState === false ? 'true' : null - } - if (this.ariaInvalid === true) { - // User wants explicit aria-invalid=true - return 'true' - } - // Most likely a string value (which could be 'true') - return this.ariaInvalid } }, mounted () { - this.setValue(this.lazyFormatter ? this.value : this.getFormatted(this.value, null)) this.setWheelStopper(this.noWheel) }, deactivated () { @@ -183,76 +130,32 @@ export default { this.setWheelStopper(false) }, watch: { - value (newVal) { - this.setValue(this.lazyFormatter ? newVal : this.getFormatted(newVal, null)) - }, noWheel (newVal) { this.setWheelStopper(newVal) } }, methods: { - setValue (val) { - if (val !== this.localVal) { - // Only update value if changed, to minimize duplicte emits - this.localValue = val - this.$emit(MODEL_EVENT, this.localValue) - } - }, - onInput (evt) { - if (evt.target.composing) return - const value = evt.target.value - this.setValue(this.lazyFormatter ? value : this.getFormatted(value, evt)) - this.$emit('input', this.localValue, evt) - }, - onChange (evt) { - if (evt.target.composing) return - this.setValue(this.format(evt.target.value, evt)) - this.$emit('change', this.localValue, evt) - }, - getFormatted (value, event = null) { - return this.formatter ? this.formatter(value, event) : value - }, setWheelStopper (on) { const input = this.$el - // We use native events, so that we don't interfere with prepgation + // We use native events, so that we don't interfere with propgation if (on) { - eventOn(input, 'focus', this.onFocus) - eventOn(input, 'blur', this.onBlur) + eventOn(input, 'focus', this.onWheelFocus) + eventOn(input, 'blur', this.onWheelBlur) } else { - eventOff(input, 'focus', this.onFocus) - eventOff(input, 'blur', this.onBlur) + eventOff(input, 'focus', this.onWheelFocus) + eventOff(input, 'blur', this.onWheelBlur) eventOff(document, 'wheel', this.stopWheel) } }, - onFocus (evt) { + onWheelFocus (evt) { eventOn(document, 'wheel', this.stopWheel) }, - onBlur (evt) { + onWheelBlur (evt) { eventOff(document, 'wheel', this.stopWheel) }, stopWheel (evt) { evt.preventDefault() this.$el.blur() - }, - // Exposed methods - format () { - // Force the formatter to run - this.setValue(this.getFormatted(this.localValue, null)) - return this.localValue - }, - focus () { - // Expose the input focus() method - /* istanbul ignore next */ - if (!this.disabled) { - this.$el.focus() - } - }, - blur () { - // Expose the input blur() method - /* istanbul ignore next */ - if (!this.disabled) { - this.$el.blur() - } } } } From 8a5d6ede5aaf3a8fe2805c3a22aff74320fd102e Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 21 Nov 2018 00:35:48 -0400 Subject: [PATCH 02/11] Update form-textarea.js minor update --- src/components/form-textarea/form-textarea.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/form-textarea/form-textarea.js b/src/components/form-textarea/form-textarea.js index c02615503fc..3de35751718 100644 --- a/src/components/form-textarea/form-textarea.js +++ b/src/components/form-textarea/form-textarea.js @@ -35,7 +35,7 @@ export default { attrs: { id: self.safeId(), name: self.name, - form: this.form || null, + form: self.form || null, disabled: self.disabled, placeholder: self.placeholder, required: self.required, From d6421b4addf28ede7503f608c9cfb676f8a3b888 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 21 Nov 2018 00:43:26 -0400 Subject: [PATCH 03/11] Update form-input.js --- src/components/form-input/form-input.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/form-input/form-input.js b/src/components/form-input/form-input.js index 93177179181..8469d69a017 100644 --- a/src/components/form-input/form-input.js +++ b/src/components/form-input/form-input.js @@ -46,7 +46,7 @@ export default { var self = this return h('input', { ref: 'input', - class: self.inputClass, + class: self.computedClass, directives: [ { name: 'model', From bd96032d81cc61fadb28912389cbf3c7e2f14de6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 21 Nov 2018 00:51:04 -0400 Subject: [PATCH 04/11] Update form-text.js --- src/mixins/form-text.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index d7e1437c5a8..45ca41f6e90 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -66,13 +66,12 @@ export default { computed: { computedClass () { return [ - this.plaintext ? 'form-control-plaintext' : 'form-control', { - // Special class cases for b-form-input input types + // Range input needs class custom-range 'custom-range': this.type === 'range', // plaintext not supported by type=range or type=color 'form-control-plaintext': this.plaintext && this.type !== 'range' && this.type !== 'color', - // form-control not used by type=range or plaintext. Always used by type=color + // form-control not used by type=range or plaintext. Always used by type=color 'form-control': (!this.plaintext && this.type !== 'range') || this.type === 'color' }, this.sizeFormClass, From 1e54089247810e6143778e5601229552a3522009 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 21 Nov 2018 01:14:02 -0400 Subject: [PATCH 05/11] Update form-input.spec.js --- src/components/form-input/form-input.spec.js | 82 ++++++++++++-------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/src/components/form-input/form-input.spec.js b/src/components/form-input/form-input.spec.js index 39c3ea359b2..dbaa21a3db9 100755 --- a/src/components/form-input/form-input.spec.js +++ b/src/components/form-input/form-input.spec.js @@ -296,8 +296,8 @@ describe('form-input', async () => { input.trigger('input') expect(wrapper.emitted('input')).toBeDefined() + expect(wrapper.emitted().input[0].length).toEqual(1) expect(wrapper.emitted().input[0][0]).toEqual('test') - expect(wrapper.emitted().input[0].length).toEqual(2) }) it('emits a native focus event', async () => { @@ -314,18 +314,19 @@ describe('form-input', async () => { expect(spy).toHaveBeenCalled() }) - it('emits a native blur event', async () => { - const spy = jest.fn() + it('emits a blur event with native event as only arg', async () => { const wrapper = mount(Input, { - listeners: { - blur: spy + propsData: { + value: 'TEST' } }) const input = wrapper.find('input') input.trigger('blur') - expect(wrapper.emitted()).toMatchObject({}) - expect(spy).toHaveBeenCalled() + expect(wrapper.emitted('blur')).toBeDefined() + expect(wrapper.emitted('blur')[0].length).toEqual(1) + expect(wrapper.emitted('blur')[0][0] instanceof Event).toBe(true) + expect(wrapper.emitted('blur')[0][0].type).toEqual('blur') }) it('applies formatter on input when not lazy', async () => { @@ -341,12 +342,15 @@ describe('form-input', async () => { input.element.value = 'TEST' input.trigger('input') - expect(wrapper.emitted('update:value')).toBeDefined() - let last = wrapper.emitted('update:value').length - 1 - expect(wrapper.emitted('update:value')[last][0]).toEqual('test') + expect(wrapper.emitted('update')).toBeDefined() + expect(wrapper.emitted('update').length).toEqual(1) + expect(wrapper.emitted('update')[0][0]).toEqual('test') expect(wrapper.emitted('input')).toBeDefined() + expect(wrapper.emitted('input').length).toEqual(1) expect(wrapper.emitted('input')[0][0]).toEqual('test') + + expect(input.vm.localValue).toEqual('test') }) it('does not apply formatter on input when lazy', async () => { @@ -363,15 +367,17 @@ describe('form-input', async () => { input.element.value = 'TEST' input.trigger('input') - expect(wrapper.emitted('update:value')).toBeDefined() - let last = wrapper.emitted('update:value').length - 1 - expect(wrapper.emitted('update:value')[last][0]).toEqual('TEST') + expect(wrapper.emitted('update')).toBeDefined() + expect(wrapper.emitted('update').length).toEqual(1) + expect(wrapper.emitted('update')[0][0]).toEqual('TEST') expect(wrapper.emitted('input')).toBeDefined() + expect(wrapper.emitted('input').length).toEqual(1) expect(wrapper.emitted('input')[0][0]).toEqual('TEST') expect(wrapper.emitted('change')).not.toBeDefined() + expect(input.vm.localValue).toEqual('TEST') }) - it('applies formatter on change when lazy', async () => { + it('applies formatter on blur when lazy', async () => { const wrapper = mount(Input, { propsData: { value: '', @@ -387,17 +393,26 @@ describe('form-input', async () => { // input event needed to set initial value input.element.value = 'TEST' input.trigger('input') + expect(input.vm.localValue).toEqual('TEST') - expect(wrapper.emitted('update:value')).toBeDefined() + expect(wrapper.emitted('update')).toBeDefined() + expect(wrapper.emitted('update').length).toEqual(1) + expect(wrapper.emitted('update')[0][0]).toEqual('TEST') + + input.trigger('blur') - input.trigger('change') + expect(wrapper.emitted('update')).toBeDefined() + expect(wrapper.emitted('update').length).toEqual(2) + expect(wrapper.emitted('update')[1][0]).toEqual('test') + expect(wrapper.emitted('input')).toBeDefined() + expect(wrapper.emitted('update').length).toEqual(1) + expect(wrapper.emitted('change')).not.toBeDefined() + expect(wrapper.emitted('blur')).toBeDefined() + expect(wrapper.emitted('blur').length).toEqual(1) expect(input.vm.localValue).toEqual('test') - expect(wrapper.emitted('update:value')).toBeDefined() - expect(wrapper.emitted('change')).toBeDefined() - expect(wrapper.emitted('change')[0][0]).toEqual('test') }) - it('applies formatter when value supplied on mount and not lazy', async () => { + it('does not apply formatter when value supplied on mount and not lazy', async () => { const wrapper = mount(Input, { propsData: { value: 'TEST', @@ -409,15 +424,14 @@ describe('form-input', async () => { }) const input = wrapper.find('input') - expect(input.vm.localValue).toEqual('test') - expect(wrapper.emitted('update:value')).toBeDefined() - const last = wrapper.emitted('update:value').length - 1 - expect(wrapper.emitted('update:value')[last][0]).toEqual('test') + expect(input.vm.localValue).toEqual('TEST') + expect(wrapper.emitted('update')).not.toBeDefined() expect(wrapper.emitted('input')).not.toBeDefined() expect(wrapper.emitted('change')).not.toBeDefined() + expect(wrapper.emitted('blur')).not.toBeDefined() }) - it('applies formatter when value prop updated and not lazy', async () => { + it('does not apply formatter when value prop updated and not lazy', async () => { const wrapper = mount(Input, { propsData: { value: '', @@ -431,12 +445,13 @@ describe('form-input', async () => { wrapper.setProps({ value: 'TEST' }) const input = wrapper.find('input') - expect(input.element.value).toEqual('test') - expect(wrapper.emitted('update:value')).toBeDefined() - let last = wrapper.emitted('update:value').length - 1 - expect(wrapper.emitted('update:value')[last][0]).toEqual('test') + expect(input.element.value).toEqual('TEST') + expect(wrapper.emitted('update')).toBeDefined() + expect(wrapper.emitted('update').length).toEqual(1) + expect(wrapper.emitted('update')[0][0]).toEqual('TEST') expect(wrapper.emitted('input')).not.toBeDefined() expect(wrapper.emitted('change')).not.toBeDefined() + expect(wrapper.emitted('blur')).not.toBeDefined() }) it('does not apply formatter when value prop updated and lazy', async () => { @@ -450,13 +465,16 @@ describe('form-input', async () => { }, attachToDocument: true }) - wrapper.setProps({ value: 'TEST' }) + const input = wrapper.find('input') - expect(wrapper.emitted('update:value')).toBeDefined() + expect(input.element.value).toEqual('TEST') + expect(wrapper.emitted('update')).toBeDefined() + expect(wrapper.emitted('update').length).toEqual(1) + expect(wrapper.emitted('update')[0][0]).toEqual('TEST') expect(wrapper.emitted('input')).not.toBeDefined() expect(wrapper.emitted('change')).not.toBeDefined() - expect(wrapper.vm.localValue).toBe('TEST') + expect(wrapper.emitted('blur')).not.toBeDefined() }) it('focused number input with no-wheel set to true works', async () => { From 65dea336bdf33b9f0742550ba4ac330f240019c5 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 21 Nov 2018 01:15:41 -0400 Subject: [PATCH 06/11] Update form-text.js --- src/mixins/form-text.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 45ca41f6e90..07965f4de2a 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -71,7 +71,7 @@ export default { 'custom-range': this.type === 'range', // plaintext not supported by type=range or type=color 'form-control-plaintext': this.plaintext && this.type !== 'range' && this.type !== 'color', - // form-control not used by type=range or plaintext. Always used by type=color + // form-control not used by type=range or plaintext. Always used by type=color 'form-control': (!this.plaintext && this.type !== 'range') || this.type === 'color' }, this.sizeFormClass, From 58b89f65540e8a2adfeacd2bf9e3c2caa2700c3d Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 21 Nov 2018 01:20:13 -0400 Subject: [PATCH 07/11] Update form-input.spec.js --- src/components/form-input/form-input.spec.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/form-input/form-input.spec.js b/src/components/form-input/form-input.spec.js index dbaa21a3db9..23ef549c6b4 100755 --- a/src/components/form-input/form-input.spec.js +++ b/src/components/form-input/form-input.spec.js @@ -405,7 +405,6 @@ describe('form-input', async () => { expect(wrapper.emitted('update').length).toEqual(2) expect(wrapper.emitted('update')[1][0]).toEqual('test') expect(wrapper.emitted('input')).toBeDefined() - expect(wrapper.emitted('update').length).toEqual(1) expect(wrapper.emitted('change')).not.toBeDefined() expect(wrapper.emitted('blur')).toBeDefined() expect(wrapper.emitted('blur').length).toEqual(1) @@ -446,9 +445,7 @@ describe('form-input', async () => { const input = wrapper.find('input') expect(input.element.value).toEqual('TEST') - expect(wrapper.emitted('update')).toBeDefined() - expect(wrapper.emitted('update').length).toEqual(1) - expect(wrapper.emitted('update')[0][0]).toEqual('TEST') + expect(wrapper.emitted('update')).not.toBeDefined() // Note emitted as value hasnt changed expect(wrapper.emitted('input')).not.toBeDefined() expect(wrapper.emitted('change')).not.toBeDefined() expect(wrapper.emitted('blur')).not.toBeDefined() @@ -469,9 +466,7 @@ describe('form-input', async () => { const input = wrapper.find('input') expect(input.element.value).toEqual('TEST') - expect(wrapper.emitted('update')).toBeDefined() - expect(wrapper.emitted('update').length).toEqual(1) - expect(wrapper.emitted('update')[0][0]).toEqual('TEST') + expect(wrapper.emitted('update')).not.toBeDefined() // not emitted when value doesnt change expect(wrapper.emitted('input')).not.toBeDefined() expect(wrapper.emitted('change')).not.toBeDefined() expect(wrapper.emitted('blur')).not.toBeDefined() From 9bdf877d0dfba538ccc729b0903b7812bfaae223 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 21 Nov 2018 01:23:46 -0400 Subject: [PATCH 08/11] Update package.json --- src/components/form-input/package.json | 32 ++++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/components/form-input/package.json b/src/components/form-input/package.json index dcd67118373..cb63eb84667 100755 --- a/src/components/form-input/package.json +++ b/src/components/form-input/package.json @@ -8,39 +8,41 @@ "events": [ { "event": "input", - "description": "Emitted when the input receives input from user.", + "description": "Input event triggered by user interaction. Emitted after any formatting and after the v-model is updated", "args": [ { "arg": "value", - "description": "Current value of the input (after any formatting applied)" - }, - { - "arg": "event", - "description": "The native input event object (before any formatting applied)" + "description": "Current value of input" } ] }, { "event": "change", - "description": "Emitted when the input changes based on user input.", + "description": "Change event triggerd by user interaction. Emitted after any formatting and after the v-model is updated.", "args": [ { "arg": "value", - "description": "Current value of the input (after any formatting applied)" - }, - { - "arg": "event", - "description": "The native change event object (before any formatting applied)" + "description": "Current value of input" } ] }, { - "event": "update:value", - "description": "Event that updates the v-model.", + "event": "update", + "description": "Emitted to update the v-model", "args": [ { "arg": "value", - "description": "current value of the input" + "description": "Value of input, after any formatting. Not emitted if the value does nto change" + } + ] + }, + { + "event": "blur", + "description": "Emitted after the input looses focus", + "args": [ + { + "arg": "event", + "description": "Native blur event (before any formmating)" } ] } From f0e6eb0d9352cc534676c348b0f746e744a141a8 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 21 Nov 2018 01:35:18 -0400 Subject: [PATCH 09/11] Update README.md --- src/components/form-input/README.md | 60 ++++++++++++++++++----------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/components/form-input/README.md b/src/components/form-input/README.md index dc0faff832b..9dd32d75cfe 100755 --- a/src/components/form-input/README.md +++ b/src/components/form-input/README.md @@ -75,7 +75,7 @@ as two separate inputs. than what is returned by it's value (i.e. ordering of year-month-date). - Regardless of input type, the value is **always** returned as a string representation. - `v-model.lazy` is not supported by `` (nor any custom vue component). -- `v-model` modifiers `.number` and `.trim` can cause unexpected cursor jumps when the user is typing (this is a Vue issue with `v-model` on custom components). Avoid using these modifiers. +- `v-model` modifiers `.number` and `.trim` can cause unexpected cursor jumps when the user is typing (this is a Vue issue with `v-model` on custom components). _Avoid using these modifiers_. - Older version of firefox may not support `readonly` for `range` type inputs. - Input types that do not support `min`, `max` and `step` (i.e. `text`, `password`, `tel`, `email`, `url`, etc) will silently ignore these values (although they will still be rendered on the input markup). @@ -192,8 +192,8 @@ Generally speaking, you’ll want to use a particular state for specific types o To apply one of the contextual state icons on ``, set the `state` prop to: -- `'invalid'` or `false` for invalid contextual state -- `'valid'` or `true` for the valid contextual state +- The string `'invalid'` or boolean `false` for invalid contextual state +- The string `'valid'` or boolean `true` for the valid contextual state - `null` for no validation contextual state (default) ```html @@ -291,19 +291,17 @@ then the `aria-invalid` attribute on the input will automatically be set to `'tr ## Formatter support -`` optionally supports formatting by passing a function reference to +`` and `` optionally supports formatting by passing a function reference to the `formatter` prop. -Formatting (when a formatter funtion is supplied) occurs when the control's native `input` -event fires. You can use the boolean prop `lazy-formatter` to restrict the formatter -function to being called on the control's native `change` event (which usually occurs on blur). +Formatting (when a formatter funtion is supplied) occurs when the control's native `input` and `change` +events fire. You can use the boolean prop `lazy-formatter` to restrict the formatter +function to being called on the control's native `blur` event. The `formatter` function receives two arguments: the raw `value` of the input element, -and the native `event` object (if available). If the formatter is triggered during a -`v-model` update (or by running the component `.format()` method), then the event argument -will be `null`. +and the native `event` object that triggered teh format (if available). -The `formatter` function should return the formatted value (as a string). +The `formatter` function should return the formatted value as a _string_. Formatting does not occur if a `formatter` is not provided. @@ -322,7 +320,7 @@ Formatting does not occur if a `formatter` is not provided.

Value: {{ text1 }}

- + ` and `` have two boolean props `trim` and `number` +which emulate the native Vue `v-model` modifiers `.trim` and `.number` respectivley. Emulation of the +`.lazy` modifier is _not_ supported (listen for `change` or `blur` events instead). + +**Notes:** +- The `number` prop takes precedence over the `trim` prop (i.e. `trim` will have no effect when `number` is set). +- When using the `number` prop, and if the value can be parsed as a number (via `parseFloat`) it will return a value of type `Number` to the `v-model`, otherwise the original input value is returned as type `String`. This is the same behaviour as the native `.number` modifier. +- The `trim` and `number` modifier props do not affect the value returned by the `input` or `change` events. These events will aways return the string value of the content of `