Skip to content

Commit

Permalink
feat(package): update to use vue3
Browse files Browse the repository at this point in the history
BREAKING CHANGE: consuming applications require vue3
  • Loading branch information
Sarah Leventhal committed Oct 4, 2022
1 parent 35a95ee commit daa1f9d
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 51 deletions.
189 changes: 189 additions & 0 deletions demo/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<!-- Demo vue3 app for testing purposes -->
<!-- Examples copied from the component markdown -->
<template>
<div id="app">
<div id="value-display-div">
<checkbox v-model="masked" />
</div>

<h1>Basic Usage</h1>
<p><code>lazy</code> custom v-model modifier will only emit input event on change event.</p>
<field label="US Phone Number">
<input-facade mask="(###) ### - ####" v-model.lazy="basicUsageValue" :masked="masked" />
</field>
<display :value="basicUsageValue" />

<h1>Optional character</h1>
<p>
Use a question mark (?) to indicate that a character is optional. Similar to regular expression this means 0 or 1.
</p>
<field label="IP address">
<input-facade
name="ip"
mask="##?#?.##?#?.##?#?.##?#?"
v-model="optionalCharvalue"
:masked="masked"
:formatter="validateIP"
/>
</field>
<display :value="optionalCharvalue" />

<h1>Repeating character</h1>
<p>
Use an asterisk (*) as a suffix to set a masking character as repeating, similar to regular expression. Note that
this means that 0 or more of said character will match. If you need to match 1 or more than you must specify it.
</p>
<field label="One or more numbers">
<input-facade mask="##* AA" v-model="repeatingCharValue" :masked="masked" />
</field>
<display :value="repeatingCharValue" />

<h1>Alternation (Pipe)</h1>
<p>
Use a pipe symbol to indicate altarnative <b>static</b> values that can be used in the mask. This is case
insensitive and can match letters irregarless of accents. For example å = A. Android webview and Opera dont fully
support that type of matching.
<i>
Note that because this only works with static values there is no need to escape characters that are also used as
tokens.
</i>
</p>
<field label="ID Code">
<input-facade mask="A|B|C-####" v-model="alternationValue" :masked="masked" />
</field>
<display :value="alternationValue" />

<h1>Dynamic Masks</h1>
<p>
Accepts an array of masking pattern and dynamically chooses the appropriate one based on the number of characters
in the field.
</p>
<field label="US Zip Code">
<input-facade v-model="USPostal" :mask="['#####', '#####-####']" :masked="masked" />
</field>

<field label="UK Postal Code">
<input-facade v-model="UKPostal" :mask="['A# #AA', 'AXX #AA', 'AA#X #AA']" :masked="masked" />
</field>

<display label="Zip Code" :value="USPostal" />
<display label="Postal Code" :value="UKPostal" />

<h1>Custom Tokens</h1>
<p>
You can override the tokens on a per field basis. Just pass in your own token definition to the field. This can
also be used to add internatilization support.
</p>
<field label="Hex Color">
<input-facade mask="\#FFFFFF" :tokens="hexTokens" :masked="masked" v-model="customTokenValue" />
</field>
<display :value="customTokenValue" />

<h1>Post masking input formatter</h1>
<p>
Returning a string in the format function will re-run that value through the masker routine, Ensuring that the end
result still confirms to the mask.
</p>
<field label="Date as MM/YY">
<input-facade v-model="formatterValue" mask="##/##" :formatter="date" />
</field>
<display :value="formatterValue" />
<p>
Returning a boolean `true` will leave the masked or unmasked value as is, the value is passed by reference so if
you modify them here, that will be their final value. However if a `false` is returned, the user's input will be
ignored and the value will remain as it was prior.
</p>
<field label="Enter an even num">
<input-facade v-model="boolFormatterValue" mask="#########" :formatter="evenMoney" masked />
</field>
<display :value="boolFormatterValue" />
</div>
</template>

<script>
import InputFacade from '../src/component.vue'
import Checkbox from '../styleguide/components/Checkbox.vue'
import Display from '../styleguide/components/Display.vue'
import Field from '../styleguide/components/Field.vue'
export default {
name: 'App',
components: {
Checkbox,
Display,
Field,
InputFacade
},
data() {
return {
basicUsageValue: '',
optionalCharvalue: '',
repeatingCharValue: '',
alternationValue: '',
USPostal: '',
UKPostal: '',
customTokenValue: '',
formatterValue: '',
boolFormatterValue: '',
masked: true,
hexTokens: {
F: {
pattern: /[0-9A-F]/i,
transform: (v) => v.toLocaleUpperCase()
}
}
}
},
methods: {
date(value, event) {
// do not format on deletion, this could leave the input in bad state
// but allows user to delete the leading 0 if needed for some reason
if (event.inputType !== 'deleteContentBackward') {
const [month] = value.masked.split('/')
if (month > 12) {
return '0' + value.unmasked
}
}
},
evenMoney(value, event) {
if (event.data && event.data % 2 !== 0) {
// odd number, ignore it
return false
} else if (value.unmasked) {
const formatted = value.unmasked.match(/\d{1,3}/g).join(',')
value.masked = `$${formatted}`
return true
}
},
validateIP(value) {
const parts = value.masked.split('.')
if (parts.length < 4 && parts[parts.length - 1] > 25) {
return value.masked + '.'
}
return !parts.some((part) => part > 255)
}
}
}
</script>

<style scoped>
#value-display-div {
position: fixed;
background-color: white;
border: solid 0.75px black;
border-radius: 5px;
padding: 5px;
box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5);
top: 15px;
right: 15px;
}
code {
padding: 2px;
border-radius: 2px;
display: inline-block;
background-color: #eee;
}
</style>
4 changes: 4 additions & 0 deletions demo/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')
10 changes: 9 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
module.exports = {
preset: '@vue/cli-plugin-unit-jest',
moduleFileExtensions: ['vue', 'js', 'json', 'jsx'],
testEnvironment: 'jsdom',
testEnvironmentOptions: {
customExportConditions: ['node', 'node-addons']
},
transform: {
'^.+\\.js$': 'babel-jest',
'^.+\\.vue$': '@vue/vue3-jest'
},
testMatch: ['**/tests/**/*.test.js'],
collectCoverageFrom: ['src/*.{js,vue}', '!src/plugin.js'],
coverageThreshold: {
Expand Down
17 changes: 12 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,41 @@
"build:plugin": "vue-cli-service build --target=lib src/plugin.js",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"semantic-release": "semantic-release",
"serve": "vue-cli-service serve",
"dev": "vue-cli-service styleguidist --config styleguide/config.js --mode development",
"build:docs": "vue-cli-service styleguidist:build --config styleguide/config.js",
"test": "jest --coverage",
"test:filter": "func() { yarn test \"$1\" --coverage-reporters=\"lcov\" --coverageThreshold={} ${@:2}; }; func",
"test:watch": "jest --coverage --watchAll"
},
"main": "dist/vue-input-facade.umd.min.js",
"files": [
"dist/*.js"
],
"devDependencies": {
"@babel/core": "^7.14.5",
"@babel/preset-env": "^7.14.7",
"@vue/cli-plugin-babel": "^4.5.15",
"@vue/cli-plugin-eslint": "^4.5.15",
"@vue/cli-plugin-unit-jest": "^4.5.15",
"@vue/cli-service": "^4.5.15",
"@vue/compiler-dom": "^3.2.40",
"@vue/eslint-config-prettier": "^5.0.0",
"@vue/test-utils": "^1.3.0",
"@vue/test-utils": "^2.1.0",
"@vue/vue3-jest": "^28.1.0",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^28.1.3",
"core-js": "^3.19.3",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^5.16.0",
"eslint-plugin-prettier": "^3.4.1",
"eslint-plugin-vue": "^5.0.0",
"husky": "^4.3.8",
"jest": "^28.1.3",
"jest-environment-jsdom": "^29.1.2",
"prettier": "^1.18.2",
"semantic-release": "^17.4.7",
"vue": "^2.6.14",
"vue-cli-plugin-styleguidist": "^4.44.15",
"vue-template-compiler": "^2.6.14"
"vue": "^3.2.40"
},
"config": {
"commitizen": {
Expand Down
36 changes: 16 additions & 20 deletions src/component.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,6 @@ export default {
* @since v1.3
*/
formatter: Function,
/**
* Vue's v-model .lazy modifier does not currently work with custom components. If you wish to have your v-model
* updated only during the change event instead of on input, enable this property. <b>Note: This works by supressing
* input events and only emitting a single input event at the same time as the change event.</b>
* @since v1.3
*/
lazy: {
type: Boolean,
default: false
},
/**
* The mask pattern for this input, it could be a single pattern or multiple patterns when its an array.
*/
Expand Down Expand Up @@ -80,7 +70,10 @@ export default {
* The input's value
* @model
*/
value: [String, Number]
modelValue: [String, Number],
modelModifiers: {
default: () => ({})
}
},
directives: { facade: directive },
data() {
Expand All @@ -90,18 +83,21 @@ export default {
}
},
watch: {
value(newValue) {
modelValue(newValue) {
// avoid trigering the directive's update hook when we emit
// the unmasked value to the parent component
if (newValue !== this.emittedValue) {
this.maskedValue = newValue
}
},
mask(newMask) {
if (!newMask && !this.masked) {
// when removing the masking rule, set the displayed value to the unmasked
// to remove any unwanted masking characters from the input
this.maskedValue = this.unmaskedValue
mask: {
deep: true,
handler(newMask) {
if (!newMask && !this.masked) {
// when removing the masking rule, set the displayed value to the unmasked
// to remove any unwanted masking characters from the input
this.maskedValue = this.unmaskedValue
}
}
},
masked() {
Expand All @@ -128,7 +124,7 @@ export default {
this.maskedValue = target.value
this.unmaskedValue = target.unmaskedValue
if (!this.lazy) {
if (!this.modelModifiers.lazy) {
this.emitInput()
}
},
Expand All @@ -139,7 +135,7 @@ export default {
*/
this.$emit('change', this.emittedValue)
if (this.lazy) {
if (this.modelModifiers.lazy) {
this.emitInput()
}
},
Expand All @@ -148,7 +144,7 @@ export default {
* Fires when the value of the input has been changed.
* @param {String} value The input's current value, masked or unmasked.
*/
this.$emit('input', this.emittedValue)
this.$emit('update:modelValue', this.emittedValue)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export function updateValue(el, vnode, { emit = true, force = false } = {}, even
currentValue = currentValue || ''

if (force || oldValue !== currentValue) {
// to keep the string as short as possible (not append extra chars at the end)
if (['deleteByCut', 'deleteContent', 'deleteContentBackward', 'deleteContentForward'].includes(event?.inputType)) {
config = { ...config, short: true }
}
Expand Down
8 changes: 4 additions & 4 deletions src/directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as core from './core'
const CONFIG_KEY = core.CONFIG_KEY

export default {
bind: (el, { value, modifiers }, vnode) => {
beforeMount: (el, { value, modifiers }, vnode) => {
el = core.getInputElement(el)
const config = core.normalizeConfig(value, modifiers)
el[CONFIG_KEY] = { config }
Expand All @@ -11,7 +11,7 @@ export default {
core.updateValue(el, vnode, { force: config.prefill })
},

inserted: (el) => {
mounted: (el) => {
el = core.getInputElement(el)
const config = el[CONFIG_KEY]
// prefer adding event listener to parent element to avoid Firefox bug which does not
Expand Down Expand Up @@ -52,7 +52,7 @@ export default {
}
},

update: (el, { value, oldValue, modifiers }, vnode) => {
updated: (el, { value, oldValue, modifiers }, vnode) => {
el = core.getInputElement(el)

if (value !== oldValue) {
Expand All @@ -63,7 +63,7 @@ export default {
}
},

unbind: (el) => {
unmounted: (el) => {
core.getInputElement(el)[CONFIG_KEY].cleanup()
}
}

0 comments on commit daa1f9d

Please sign in to comment.