@bcrs-shared-components/base-address 3.0.3 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/BaseAddress.vue
CHANGED
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
:label="countryLabel"
|
|
57
57
|
:rules="[...schemaLocal.country]"
|
|
58
58
|
>
|
|
59
|
-
<template #item="{item, props}">
|
|
59
|
+
<template #item="{item, props: autoCompleteProps}">
|
|
60
60
|
<v-divider v-if="item.raw.divider" />
|
|
61
61
|
<v-list-item
|
|
62
62
|
v-else
|
|
63
|
-
v-bind="
|
|
63
|
+
v-bind="autoCompleteProps"
|
|
64
64
|
/>
|
|
65
65
|
</template>
|
|
66
66
|
</v-autocomplete>
|
|
@@ -128,11 +128,11 @@
|
|
|
128
128
|
:name="Math.random().toString()"
|
|
129
129
|
:rules="[...schemaLocal.region]"
|
|
130
130
|
>
|
|
131
|
-
<template #item="{item, props}">
|
|
131
|
+
<template #item="{item, props: autoCompleteProps}">
|
|
132
132
|
<v-divider v-if="item.raw.divider" />
|
|
133
133
|
<v-list-item
|
|
134
134
|
v-else
|
|
135
|
-
v-bind="
|
|
135
|
+
v-bind="autoCompleteProps"
|
|
136
136
|
/>
|
|
137
137
|
</template>
|
|
138
138
|
</v-autocomplete>
|
|
@@ -174,153 +174,145 @@
|
|
|
174
174
|
</div>
|
|
175
175
|
</template>
|
|
176
176
|
|
|
177
|
-
<script lang="ts">
|
|
178
|
-
import {
|
|
177
|
+
<script setup lang="ts">
|
|
178
|
+
import { AddressIF, SchemaIF } from '@bcrs-shared-components/interfaces'
|
|
179
|
+
import { Ref, onMounted, toRef, watch } from 'vue'
|
|
179
180
|
import {
|
|
180
181
|
baseRules,
|
|
182
|
+
spaceRules,
|
|
181
183
|
useAddress,
|
|
182
184
|
useAddressComplete,
|
|
183
|
-
useCountryRegions,
|
|
184
|
-
useCountriesProvinces,
|
|
185
185
|
useBaseValidations,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
useCountriesProvinces,
|
|
187
|
+
useCountryRegions
|
|
188
|
+
} from '@bcrs-shared-components/base-address/factories'
|
|
189
189
|
import { AddressValidationRules } from '@bcrs-shared-components/enums'
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
default: false
|
|
220
|
-
},
|
|
221
|
-
/* Hides the persistent hint field on Address Input */
|
|
222
|
-
hideAddressHint: {
|
|
223
|
-
type: Boolean,
|
|
224
|
-
default: false
|
|
225
|
-
},
|
|
226
|
-
/* Hides Delivery Address field (e.g. for Unit Notes) */
|
|
227
|
-
hideDeliveryAddress: {
|
|
228
|
-
type: Boolean,
|
|
229
|
-
default: false
|
|
230
|
-
}
|
|
231
|
-
},
|
|
232
|
-
emits: ['valid'],
|
|
233
|
-
setup (props, { emit }) {
|
|
234
|
-
// eslint-disable-next-line vue/no-setup-props-destructure
|
|
235
|
-
const localSchema = { ...props.schema }
|
|
236
|
-
const {
|
|
237
|
-
addressLocal,
|
|
238
|
-
country,
|
|
239
|
-
schemaLocal,
|
|
240
|
-
isSchemaRequired,
|
|
241
|
-
labels
|
|
242
|
-
} = useAddress(toRefs(props).value, localSchema)
|
|
243
|
-
|
|
244
|
-
const origPostalCodeRules = localSchema.postalCode
|
|
245
|
-
const origRegionRules = localSchema.region
|
|
191
|
+
const props = withDefaults(defineProps<{
|
|
192
|
+
value: AddressIF
|
|
193
|
+
editing: boolean
|
|
194
|
+
schema: SchemaIF
|
|
195
|
+
triggerErrors: boolean
|
|
196
|
+
hideAddressHint: boolean
|
|
197
|
+
hideDeliveryAddress: boolean
|
|
198
|
+
}>(), {
|
|
199
|
+
value: () => ({
|
|
200
|
+
streetAddress: '',
|
|
201
|
+
streetAddressAdditional: '',
|
|
202
|
+
addressCity: '',
|
|
203
|
+
addressRegion: '',
|
|
204
|
+
postalCode: '',
|
|
205
|
+
addressCountry: null,
|
|
206
|
+
deliveryInstructions: ''
|
|
207
|
+
}),
|
|
208
|
+
/* used for readonly mode vs edit mode */
|
|
209
|
+
editing: false,
|
|
210
|
+
/* contains validation for each field */
|
|
211
|
+
schema: null,
|
|
212
|
+
/* triggers all current form validation errors */
|
|
213
|
+
triggerErrors: false,
|
|
214
|
+
/* Hides the persistent hint field on Address Input */
|
|
215
|
+
hideAddressHint: false,
|
|
216
|
+
/* Hides Delivery Address field (e.g. for Unit Notes) */
|
|
217
|
+
hideDeliveryAddress: false
|
|
218
|
+
})
|
|
246
219
|
|
|
247
|
-
|
|
220
|
+
const emits = defineEmits<{
|
|
221
|
+
'update-address': [address: AddressIF]
|
|
222
|
+
valid: [valid: boolean]
|
|
223
|
+
}>()
|
|
248
224
|
|
|
249
|
-
|
|
225
|
+
// eslint-disable-next-line vue/no-setup-props-destructure
|
|
226
|
+
const localSchema = { ...props.schema }
|
|
227
|
+
const {
|
|
228
|
+
addressLocal,
|
|
229
|
+
country,
|
|
230
|
+
schemaLocal,
|
|
231
|
+
labels
|
|
232
|
+
} = useAddress(toRef(props.value) as Ref<AddressIF>, localSchema)
|
|
250
233
|
|
|
251
|
-
|
|
234
|
+
const origPostalCodeRules = localSchema.postalCode
|
|
235
|
+
const origRegionRules = localSchema.region
|
|
252
236
|
|
|
253
|
-
|
|
254
|
-
// do not trigger any changes if it is view only (summary instance)
|
|
255
|
-
if (!props.editing) return
|
|
237
|
+
const { addressForm, resetValidation, validate } = useBaseValidations()
|
|
256
238
|
|
|
257
|
-
|
|
258
|
-
localSchema.postalCode = origPostalCodeRules.concat([baseRules.postalCode])
|
|
259
|
-
localSchema.region = origRegionRules
|
|
260
|
-
} else if (val === 'US') {
|
|
261
|
-
localSchema.postalCode = origPostalCodeRules.concat([baseRules.zipCode])
|
|
262
|
-
localSchema.region = origRegionRules
|
|
263
|
-
} else {
|
|
264
|
-
localSchema.postalCode = origPostalCodeRules.concat([baseRules[AddressValidationRules.MAX_LENGTH](15)])
|
|
265
|
-
localSchema.region = [baseRules[AddressValidationRules.MAX_LENGTH](2), ...spaceRules]
|
|
266
|
-
}
|
|
267
|
-
// reset other address fields (check is for loading an existing address)
|
|
268
|
-
if (oldVal) {
|
|
269
|
-
addressLocal.value.streetAddress = ''
|
|
270
|
-
addressLocal.value.streetAddressAdditional = ''
|
|
271
|
-
addressLocal.value.addressCity = ''
|
|
272
|
-
addressLocal.value.addressRegion = ''
|
|
273
|
-
addressLocal.value.postalCode = ''
|
|
274
|
-
}
|
|
275
|
-
// wait for schema update and validate the form
|
|
276
|
-
setTimeout(() => {
|
|
277
|
-
props.triggerErrors ? validate() : resetValidation()
|
|
278
|
-
}, 5)
|
|
279
|
-
}
|
|
239
|
+
const { enableAddressComplete, uniqueIds } = useAddressComplete(addressLocal)
|
|
280
240
|
|
|
281
|
-
|
|
282
|
-
countryChangeHandler(addressLocal.value.addressCountry, null)
|
|
283
|
-
})
|
|
241
|
+
const countryProvincesHelpers = useCountriesProvinces()
|
|
284
242
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
* NOTE: we don't want it to trigger error msgs yet which is why this does not call validate()
|
|
289
|
-
*/
|
|
290
|
-
for (const key in val) {
|
|
291
|
-
for (const index in schemaLocal.value[key]) {
|
|
292
|
-
if (schemaLocal.value[key][index](val[key]) !== true) {
|
|
293
|
-
valid = false
|
|
294
|
-
break
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
if (!valid) break
|
|
298
|
-
}
|
|
299
|
-
emit('valid', valid)
|
|
300
|
-
}, { immediate: true, deep: true })
|
|
243
|
+
const countryChangeHandler = (val: string, oldVal: string) => {
|
|
244
|
+
// do not trigger any changes if it is view only (summary instance)
|
|
245
|
+
if (!props.editing) return
|
|
301
246
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
247
|
+
if (val === 'CA') {
|
|
248
|
+
localSchema.postalCode = origPostalCodeRules.concat([baseRules.postalCode])
|
|
249
|
+
localSchema.region = origRegionRules
|
|
250
|
+
} else if (val === 'US') {
|
|
251
|
+
localSchema.postalCode = origPostalCodeRules.concat([baseRules.zipCode])
|
|
252
|
+
localSchema.region = origRegionRules
|
|
253
|
+
} else {
|
|
254
|
+
localSchema.postalCode = origPostalCodeRules.concat([baseRules[AddressValidationRules.MAX_LENGTH](15)])
|
|
255
|
+
localSchema.region = [baseRules[AddressValidationRules.MAX_LENGTH](2), ...spaceRules]
|
|
256
|
+
}
|
|
257
|
+
// reset other address fields (check is for loading an existing address)
|
|
258
|
+
if (oldVal) {
|
|
259
|
+
addressLocal.value.streetAddress = ''
|
|
260
|
+
addressLocal.value.streetAddressAdditional = ''
|
|
261
|
+
addressLocal.value.addressCity = ''
|
|
262
|
+
addressLocal.value.addressRegion = ''
|
|
263
|
+
addressLocal.value.postalCode = ''
|
|
264
|
+
}
|
|
265
|
+
// wait for schema update and validate the form
|
|
266
|
+
setTimeout(() => {
|
|
267
|
+
props.triggerErrors ? validate() : resetValidation()
|
|
268
|
+
}, 5)
|
|
269
|
+
}
|
|
305
270
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
271
|
+
onMounted(() => {
|
|
272
|
+
countryChangeHandler(addressLocal.value.addressCountry, null)
|
|
273
|
+
})
|
|
309
274
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
validate
|
|
275
|
+
watch(() => addressLocal.value, (val) => {
|
|
276
|
+
let valid = true
|
|
277
|
+
/** checks each field against the schema rules to see if the address is valid or not
|
|
278
|
+
* NOTE: we don't want it to trigger error msgs yet which is why this does not call validate()
|
|
279
|
+
*/
|
|
280
|
+
for (const key in val) {
|
|
281
|
+
for (const index in schemaLocal.value[key]) {
|
|
282
|
+
if (schemaLocal.value[key][index](val[key]) !== true) {
|
|
283
|
+
valid = false
|
|
284
|
+
break
|
|
285
|
+
}
|
|
322
286
|
}
|
|
287
|
+
if (!valid) break
|
|
323
288
|
}
|
|
289
|
+
emits('valid', valid)
|
|
290
|
+
emits('update-address', val)
|
|
291
|
+
}, { immediate: true, deep: true })
|
|
292
|
+
|
|
293
|
+
watch(() => country.value, (val, oldVal) => {
|
|
294
|
+
countryChangeHandler(val, oldVal)
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
watch(() => props.triggerErrors, () => {
|
|
298
|
+
validate()
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
const { getCountries, getCountryName, getCountryRegions } = countryProvincesHelpers
|
|
302
|
+
const {
|
|
303
|
+
countryLabel,
|
|
304
|
+
streetLabel,
|
|
305
|
+
streetAdditionalLabel,
|
|
306
|
+
cityLabel,
|
|
307
|
+
regionLabel,
|
|
308
|
+
postalCodeLabel,
|
|
309
|
+
deliveryInstructionsLabel
|
|
310
|
+
} = labels
|
|
311
|
+
const { streetId, countryId } = uniqueIds
|
|
312
|
+
|
|
313
|
+
defineExpose({
|
|
314
|
+
addressForm,
|
|
315
|
+
validate
|
|
324
316
|
})
|
|
325
317
|
</script>
|
|
326
318
|
|
|
@@ -370,17 +362,17 @@ export default defineComponent({
|
|
|
370
362
|
.v-text-field.v-input--is-readonly {
|
|
371
363
|
pointer-events: none;
|
|
372
364
|
|
|
373
|
-
|
|
365
|
+
:deep(.v-label) {
|
|
374
366
|
// set label colour to same as disabled
|
|
375
367
|
color: rgba(0,0,0,.38);
|
|
376
368
|
}
|
|
377
369
|
|
|
378
|
-
|
|
370
|
+
:deep(.v-select__selection) {
|
|
379
371
|
// set selection colour to same as disabled
|
|
380
372
|
color: rgba(0,0,0,.38);
|
|
381
373
|
}
|
|
382
374
|
|
|
383
|
-
|
|
375
|
+
:deep(.v-icon) {
|
|
384
376
|
// set error icon colour to same as disabled
|
|
385
377
|
color: rgba(0,0,0,.38) !important;
|
|
386
378
|
opacity: 0.6;
|
|
@@ -37,7 +37,7 @@ export function useCountriesProvinces () {
|
|
|
37
37
|
* @param code The short code of the country.
|
|
38
38
|
* @returns The long name of the country.
|
|
39
39
|
*/
|
|
40
|
-
const getCountryName = (code: string): string => {
|
|
40
|
+
const getCountryName = (code: string): string | null => {
|
|
41
41
|
if (!code) return null
|
|
42
42
|
if (window['countryNameCache'][code]) return window['countryNameCache'][code]
|
|
43
43
|
const country = window['countries'].find(c => c.code === code)
|
package/package.json
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bcrs-shared-components/base-address",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@bcrs-shared-components/enums": "^2.0.0",
|
|
9
|
-
"@bcrs-shared-components/interfaces": "^2.0.
|
|
10
|
-
"@bcrs-shared-components/mixins": "^2.0.
|
|
11
|
-
"
|
|
12
|
-
"lodash.uniqueid": "^4.0.1",
|
|
13
|
-
"vue": "^2.7.14",
|
|
14
|
-
"vuelidate": "0.6.2"
|
|
9
|
+
"@bcrs-shared-components/interfaces": "^2.0.1",
|
|
10
|
+
"@bcrs-shared-components/mixins": "^2.0.2",
|
|
11
|
+
"lodash.uniqueid": "^4.0.1"
|
|
15
12
|
},
|
|
16
|
-
"
|
|
17
|
-
"vue-property-decorator": "^9.1.2",
|
|
18
|
-
"vuelidate-property-decorators": "1.0.28"
|
|
19
|
-
},
|
|
20
|
-
"gitHead": "d80832aa4a621fdabf26d3e1d479f0701cf50fad"
|
|
13
|
+
"gitHead": "60abfe87e91cdc56175ad09b16fb27b5acaea9ea"
|
|
21
14
|
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/* FUTURE:
|
|
2
|
-
* Delete this if we decide not to move forward with vuelidate
|
|
3
|
-
* Fix it to work otherwise
|
|
4
|
-
*/
|
|
5
|
-
import { computed, Ref, reactive } from 'vue-demi'
|
|
6
|
-
|
|
7
|
-
import { AddressIF, SchemaIF } from '@bcrs-shared-components/interfaces'
|
|
8
|
-
|
|
9
|
-
import useVuelidate from '@vuelidate/core'
|
|
10
|
-
|
|
11
|
-
export function useValidations (schema: Ref<SchemaIF>, address: Ref<AddressIF>) {
|
|
12
|
-
const validations = reactive({ addressLocal: schema.value })
|
|
13
|
-
const $v = useVuelidate(validations, reactive({ addressLocal: address.value }))
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Misc Vuetify rules.
|
|
17
|
-
* @param prop The name of the property object to validate.
|
|
18
|
-
* @param key The name of the property key (field) to validate.
|
|
19
|
-
* @returns True if the rule passes, otherwise an error string.
|
|
20
|
-
*/
|
|
21
|
-
const vuetifyRules = {
|
|
22
|
-
requiredRule: (prop: string, key: string): boolean | string => {
|
|
23
|
-
return Boolean($v.value[prop] && !$v.value[prop][key].required.$invalid) || 'This field is required'
|
|
24
|
-
},
|
|
25
|
-
minLengthRule: (prop: string, key: string): boolean | string => {
|
|
26
|
-
const min = validations.addressLocal[key].max
|
|
27
|
-
const msg = min ? `Minimum length is ${min}` : 'Text is below minimum length'
|
|
28
|
-
return Boolean($v.value[prop] && !$v.value[prop][key].minLength.$invalid) || msg
|
|
29
|
-
},
|
|
30
|
-
maxLengthRule: (prop: string, key: string): boolean | string => {
|
|
31
|
-
const max = validations.addressLocal[key].max
|
|
32
|
-
const msg = max ? `Maximum length is ${max}` : 'Text is over maximum length'
|
|
33
|
-
return Boolean($v.value[prop] && !$v.value[prop][key].maxLength.$invalid) || msg
|
|
34
|
-
},
|
|
35
|
-
// FUTURE: generalize this rule to take a validation parameter (ie, 'CA')
|
|
36
|
-
isCanadaRule: (prop: string, key: string): boolean | string => {
|
|
37
|
-
return Boolean($v.value[prop] && !$v.value[prop][key].isCanada.$invalid) || 'Address must be in Canada'
|
|
38
|
-
},
|
|
39
|
-
// FUTURE: generalize this rule to take a validation parameter (ie, 'BC')
|
|
40
|
-
isBCRule: (prop: string, key: string): boolean | string => {
|
|
41
|
-
return Boolean($v.value[prop] && !$v.value[prop][key].isBC.$invalid) || 'Address must be in BC'
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Creates a Vuetify rules object from the Vuelidate state.
|
|
47
|
-
* @param model The name of the model we are validating.
|
|
48
|
-
* @returns A Vuetify rules object.
|
|
49
|
-
*/
|
|
50
|
-
const createVuetifyRulesObject = (model: string): { [attr: string]: Array<Function> } => {
|
|
51
|
-
const obj = {
|
|
52
|
-
street: [],
|
|
53
|
-
streetAdditional: [],
|
|
54
|
-
city: [],
|
|
55
|
-
region: [],
|
|
56
|
-
postalCode: [],
|
|
57
|
-
country: [],
|
|
58
|
-
deliveryInstructions: []
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// ensure Vuelidate state object is initialized
|
|
62
|
-
if ($v && $v.value[model]) {
|
|
63
|
-
// iterate over Vuelidate object properties
|
|
64
|
-
Object.keys($v.value[model])
|
|
65
|
-
// only look at validation properties
|
|
66
|
-
.filter(prop => prop.charAt(0) !== '$')
|
|
67
|
-
.forEach(prop => {
|
|
68
|
-
// create array for each validation property
|
|
69
|
-
obj[prop] = []
|
|
70
|
-
// iterate over validation property params
|
|
71
|
-
Object.keys($v.value[model][prop])
|
|
72
|
-
.forEach(param => {
|
|
73
|
-
// add specified validation functions to array
|
|
74
|
-
switch (param) {
|
|
75
|
-
case 'required': obj[prop].push(() => vuetifyRules.requiredRule(model, prop)); break
|
|
76
|
-
case 'minLength': obj[prop].push(() => vuetifyRules.minLengthRule(model, prop)); break
|
|
77
|
-
case 'maxLength': obj[prop].push(() => vuetifyRules.maxLengthRule(model, prop)); break
|
|
78
|
-
case 'isCanada': obj[prop].push(() => vuetifyRules.isCanadaRule(model, prop)); break
|
|
79
|
-
case 'isBC': obj[prop].push(() => vuetifyRules.isBCRule(model, prop)); break
|
|
80
|
-
// FUTURE: add extra validation functions here
|
|
81
|
-
default: break
|
|
82
|
-
}
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// sample return object
|
|
88
|
-
// street: [
|
|
89
|
-
// () => this.requiredRule('addressLocal', 'street'),
|
|
90
|
-
// () => this.minLengthRule('addressLocal', 'street'),
|
|
91
|
-
// () => this.maxLengthRule('addressLocal', 'street')
|
|
92
|
-
// ],
|
|
93
|
-
// ...
|
|
94
|
-
|
|
95
|
-
return obj
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* The Vuetify rules object. Used to display any validation errors/styling.
|
|
100
|
-
* NB: As a getter, this is initialized between created() and mounted().
|
|
101
|
-
* @returns the Vuetify validation rules object
|
|
102
|
-
*/
|
|
103
|
-
const rules = computed((): { [attr: string]: Array<Function> } => {
|
|
104
|
-
return createVuetifyRulesObject('addressLocal')
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
/** Array of validation rules used by input elements to prevent extra whitespace. */
|
|
108
|
-
const spaceRules = [
|
|
109
|
-
(v: string) => !/^\s/g.test(v) || 'Invalid spaces', // leading spaces
|
|
110
|
-
(v: string) => !/\s$/g.test(v) || 'Invalid spaces', // trailing spaces
|
|
111
|
-
(v: string) => !/\s\s/g.test(v) || 'Invalid word spacing' // multiple inline spaces
|
|
112
|
-
]
|
|
113
|
-
return {
|
|
114
|
-
$v,
|
|
115
|
-
rules,
|
|
116
|
-
spaceRules
|
|
117
|
-
}
|
|
118
|
-
}
|