@bcrs-shared-components/base-address 2.0.11 → 3.0.0
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.stories.ts +42 -58
- package/BaseAddress.vue +236 -437
- package/factories/address-factory.ts +215 -0
- package/factories/countries-provinces-factory.ts +78 -0
- package/factories/index.ts +3 -0
- package/factories/validation-factory.ts +42 -0
- package/factories/vuelidate-validation-factory.ts +118 -0
- package/package.json +6 -3
- package/resources/default-schema.ts +42 -0
- package/resources/index.ts +1 -0
package/BaseAddress.vue
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright © 2020 Province of British Columbia
|
|
3
|
-
//
|
|
4
|
-
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
5
|
-
// the License. You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
10
|
-
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
11
|
-
// specific language governing permissions and limitations under the License.
|
|
12
|
-
//
|
|
13
|
-
|
|
14
1
|
<template>
|
|
15
2
|
<div class="base-address">
|
|
16
3
|
<!-- Display fields -->
|
|
@@ -19,36 +6,30 @@
|
|
|
19
6
|
v-if="!editing"
|
|
20
7
|
class="address-block"
|
|
21
8
|
>
|
|
22
|
-
<div class="address-block__info pre-
|
|
23
|
-
<
|
|
9
|
+
<div class="address-block__info pre-wrap">
|
|
10
|
+
<p class="address-block__info-row">
|
|
24
11
|
{{ addressLocal.streetAddress }}
|
|
25
|
-
</
|
|
26
|
-
|
|
27
|
-
|
|
12
|
+
</p>
|
|
13
|
+
<p
|
|
14
|
+
v-if="addressLocal.streetAddressAdditional"
|
|
15
|
+
class="address-block__info-row"
|
|
16
|
+
>
|
|
28
17
|
{{ addressLocal.streetAddressAdditional }}
|
|
29
|
-
</
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<span
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
{{ getCountryName(addressCountry) }}
|
|
45
|
-
</div>
|
|
46
|
-
|
|
47
|
-
<template v-if="addressLocal.deliveryInstructions">
|
|
48
|
-
<div class="address-block__info-row delivery-instructions mt-5 font-italic">
|
|
49
|
-
{{ addressLocal.deliveryInstructions }}
|
|
50
|
-
</div>
|
|
51
|
-
</template>
|
|
18
|
+
</p>
|
|
19
|
+
<p class="address-block__info-row">
|
|
20
|
+
<span>{{ addressLocal.city }}</span>
|
|
21
|
+
<span v-if="addressLocal.region"> {{ addressLocal.region }} </span>
|
|
22
|
+
<span v-if="addressLocal.postalCode"> {{ addressLocal.postalCode }}</span>
|
|
23
|
+
</p>
|
|
24
|
+
<p class="address-block__info-row">
|
|
25
|
+
{{ getCountryName(country) }}
|
|
26
|
+
</p>
|
|
27
|
+
<p
|
|
28
|
+
v-if="addressLocal.deliveryInstructions"
|
|
29
|
+
class="address-block__info-row delivery-text"
|
|
30
|
+
>
|
|
31
|
+
{{ addressLocal.deliveryInstructions }}
|
|
32
|
+
</p>
|
|
52
33
|
</div>
|
|
53
34
|
</div>
|
|
54
35
|
</v-expand-transition>
|
|
@@ -61,102 +42,115 @@
|
|
|
61
42
|
name="address-form"
|
|
62
43
|
lazy-validation
|
|
63
44
|
>
|
|
45
|
+
<div class="form__row">
|
|
46
|
+
<v-autocomplete
|
|
47
|
+
v-model="addressLocal.country"
|
|
48
|
+
autocomplete="new-password"
|
|
49
|
+
:name="Math.random().toString()"
|
|
50
|
+
variant="filled"
|
|
51
|
+
class="address-country"
|
|
52
|
+
hide-no-data
|
|
53
|
+
item-title="name"
|
|
54
|
+
item-value="code"
|
|
55
|
+
:items="getCountries()"
|
|
56
|
+
:label="countryLabel"
|
|
57
|
+
:rules="[...schemaLocal.country]"
|
|
58
|
+
/>
|
|
59
|
+
<!-- special field to select AddressComplete country, separate from our model field -->
|
|
60
|
+
<input
|
|
61
|
+
:id="countryId"
|
|
62
|
+
type="hidden"
|
|
63
|
+
:value="country"
|
|
64
|
+
>
|
|
65
|
+
</div>
|
|
64
66
|
<div class="form__row">
|
|
65
67
|
<!-- NB1: AddressComplete needs to be enabled each time user clicks in this search field.
|
|
66
68
|
NB2: Only process first keypress -- assumes if user moves between instances of this
|
|
67
69
|
component then they are using the mouse (and thus, clicking). -->
|
|
68
70
|
<v-text-field
|
|
69
|
-
:id="
|
|
70
|
-
v-model="addressLocal.
|
|
71
|
-
autocomplete="
|
|
72
|
-
:name="Math.random()"
|
|
73
|
-
filled
|
|
71
|
+
:id="streetId"
|
|
72
|
+
v-model="addressLocal.street"
|
|
73
|
+
autocomplete="new-password"
|
|
74
74
|
class="street-address"
|
|
75
|
-
|
|
75
|
+
variant="filled"
|
|
76
|
+
:hint="hideAddressHint ? '' : 'Street address, PO box, rural route, or general delivery address'"
|
|
77
|
+
:label="streetLabel"
|
|
78
|
+
:name="Math.random().toString()"
|
|
76
79
|
persistent-hint
|
|
77
|
-
:
|
|
78
|
-
:rules="[...rules.streetAddress, ...spaceRules]"
|
|
80
|
+
:rules="[...schemaLocal.street]"
|
|
79
81
|
@keypress.once="enableAddressComplete()"
|
|
80
82
|
@click="enableAddressComplete()"
|
|
81
83
|
/>
|
|
82
84
|
</div>
|
|
83
85
|
<div class="form__row">
|
|
84
86
|
<v-textarea
|
|
85
|
-
v-model="addressLocal.
|
|
87
|
+
v-model="addressLocal.streetAdditional"
|
|
88
|
+
autocomplete="new-password"
|
|
86
89
|
auto-grow
|
|
87
|
-
filled
|
|
90
|
+
variant="filled"
|
|
88
91
|
class="street-address-additional"
|
|
89
|
-
:label="
|
|
92
|
+
:label="streetAdditionalLabel"
|
|
93
|
+
:name="Math.random().toString()"
|
|
90
94
|
rows="1"
|
|
91
|
-
:rules="[...
|
|
95
|
+
:rules="!!addressLocal.streetAdditional ? [...schemaLocal.streetAdditional] : []"
|
|
92
96
|
/>
|
|
93
97
|
</div>
|
|
94
98
|
<div class="form__row three-column">
|
|
95
99
|
<v-text-field
|
|
96
|
-
v-model="addressLocal.
|
|
97
|
-
|
|
100
|
+
v-model="addressLocal.city"
|
|
101
|
+
autocomplete="new-password"
|
|
102
|
+
variant="filled"
|
|
98
103
|
class="item address-city"
|
|
99
|
-
:label="
|
|
100
|
-
:
|
|
104
|
+
:label="cityLabel"
|
|
105
|
+
:name="Math.random().toString()"
|
|
106
|
+
:rules="[...schemaLocal.city]"
|
|
101
107
|
/>
|
|
102
|
-
<v-
|
|
103
|
-
v-if="useCountryRegions(
|
|
104
|
-
v-model="addressLocal.
|
|
105
|
-
|
|
108
|
+
<v-autocomplete
|
|
109
|
+
v-if="useCountryRegions(country)"
|
|
110
|
+
v-model="addressLocal.region"
|
|
111
|
+
autocomplete="new-password"
|
|
112
|
+
variant="filled"
|
|
106
113
|
class="item address-region"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
item-text="name"
|
|
114
|
+
hide-no-data
|
|
115
|
+
item-title="name"
|
|
110
116
|
item-value="short"
|
|
111
|
-
:items="
|
|
112
|
-
|
|
113
|
-
:
|
|
117
|
+
:items="getCountryRegions(country)"
|
|
118
|
+
:label="regionLabel"
|
|
119
|
+
:menu-props="{ maxHeight: '14rem' }"
|
|
120
|
+
:name="Math.random().toString()"
|
|
121
|
+
:rules="[...schemaLocal.region]"
|
|
114
122
|
/>
|
|
115
123
|
<v-text-field
|
|
116
124
|
v-else
|
|
117
|
-
v-model="addressLocal.
|
|
118
|
-
filled
|
|
125
|
+
v-model="addressLocal.region"
|
|
126
|
+
variant="filled"
|
|
119
127
|
class="item address-region"
|
|
120
|
-
:label="
|
|
121
|
-
:
|
|
128
|
+
:label="regionLabel"
|
|
129
|
+
:name="Math.random().toString()"
|
|
130
|
+
:rules="[...schemaLocal.region]"
|
|
122
131
|
/>
|
|
123
132
|
<v-text-field
|
|
124
133
|
v-model="addressLocal.postalCode"
|
|
125
|
-
filled
|
|
134
|
+
variant="filled"
|
|
126
135
|
class="item postal-code"
|
|
127
136
|
:label="postalCodeLabel"
|
|
128
|
-
:
|
|
137
|
+
:name="Math.random().toString()"
|
|
138
|
+
:rules="[...schemaLocal.postalCode]"
|
|
129
139
|
/>
|
|
130
140
|
</div>
|
|
131
|
-
<div
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
class="address-country"
|
|
136
|
-
:label="addressCountryLabel"
|
|
137
|
-
menu-props="auto"
|
|
138
|
-
item-text="name"
|
|
139
|
-
item-value="code"
|
|
140
|
-
:items="getCountries()"
|
|
141
|
-
:rules="[...rules.addressCountry, ...spaceRules]"
|
|
142
|
-
@change="resetRegion()"
|
|
143
|
-
/>
|
|
144
|
-
<!-- special field to select AddressComplete country, separate from our model field -->
|
|
145
|
-
<input
|
|
146
|
-
:id="addressCountryId"
|
|
147
|
-
type="hidden"
|
|
148
|
-
:value="addressCountry"
|
|
149
|
-
>
|
|
150
|
-
</div>
|
|
151
|
-
<div class="form__row">
|
|
141
|
+
<div
|
|
142
|
+
v-if="!hideDeliveryAddress"
|
|
143
|
+
class="form__row"
|
|
144
|
+
>
|
|
152
145
|
<v-textarea
|
|
153
146
|
v-model="addressLocal.deliveryInstructions"
|
|
154
147
|
auto-grow
|
|
155
|
-
filled
|
|
148
|
+
variant="filled"
|
|
156
149
|
class="delivery-instructions"
|
|
157
150
|
:label="deliveryInstructionsLabel"
|
|
151
|
+
:name="Math.random().toString()"
|
|
158
152
|
rows="2"
|
|
159
|
-
:rules="[...
|
|
153
|
+
:rules="!!addressLocal.deliveryInstructions ? [...schemaLocal.deliveryInstructions] : []"
|
|
160
154
|
/>
|
|
161
155
|
</div>
|
|
162
156
|
</v-form>
|
|
@@ -165,355 +159,174 @@
|
|
|
165
159
|
</template>
|
|
166
160
|
|
|
167
161
|
<script lang="ts">
|
|
168
|
-
import
|
|
169
|
-
import {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
220
|
-
readonly noPoBox: boolean
|
|
221
|
-
|
|
222
|
-
@Prop({ default: '' })
|
|
223
|
-
readonly deliveryInstructionsText: string
|
|
224
|
-
|
|
225
|
-
@Prop({ default: false })
|
|
226
|
-
readonly excludeBC: boolean
|
|
227
|
-
|
|
228
|
-
resetRegion () {
|
|
229
|
-
this.addressLocal['addressRegion'] = ''
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/** A local (working) copy of the address, to contain the fields edited by the component (ie, the model). */
|
|
233
|
-
addressLocal: object = {}
|
|
234
|
-
|
|
235
|
-
/** A local (working) copy of the address schema. */
|
|
236
|
-
schemaLocal: any = {}
|
|
237
|
-
|
|
238
|
-
/** A unique id for this instance of this component. */
|
|
239
|
-
uniqueId = uniqueId()
|
|
240
|
-
|
|
241
|
-
/** A unique id for the Street Address input. */
|
|
242
|
-
get streetAddressId (): string {
|
|
243
|
-
return `street-address-${this.uniqueId}`
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/** A unique id for the Address Country input. */
|
|
247
|
-
addressCountryId (): string {
|
|
248
|
-
return `address-country-${this.uniqueId}`
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/** The Address Country, to simplify the template and so we can watch it below. */
|
|
252
|
-
get addressCountry (): string {
|
|
253
|
-
return this.addressLocal['addressCountry']
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
get isAddressCountryCanadaAndExcludeBc (): boolean {
|
|
257
|
-
return this.addressLocal['addressCountry'] === 'CA' && this.excludeBC
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/** The Street Address Additional label with 'optional' as needed. */
|
|
261
|
-
get streetAddressAdditionalLabel (): string {
|
|
262
|
-
return 'Additional Street Address' + (this.isSchemaRequired('streetAddressAdditional') ? '' : ' (Optional)')
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/** The Street Address label with 'optional' as needed. */
|
|
266
|
-
get streetAddressLabel (): string {
|
|
267
|
-
return 'Street Address' + (this.isSchemaRequired('streetAddress') ? '' : ' (Optional)')
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/** The Address City label with 'optional' as needed. */
|
|
271
|
-
get addressCityLabel (): string {
|
|
272
|
-
return 'City' + (this.isSchemaRequired('addressCity') ? '' : ' (Optional)')
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/** The Address Region label with 'optional' as needed. */
|
|
276
|
-
get addressRegionLabel (): string {
|
|
277
|
-
let label: string
|
|
278
|
-
let required = this.isSchemaRequired('addressRegion')
|
|
279
|
-
|
|
280
|
-
// NB: make region required for Canada and USA
|
|
281
|
-
if (this.addressLocal['addressCountry'] === 'CA') {
|
|
282
|
-
label = 'Province'
|
|
283
|
-
required = true
|
|
284
|
-
} else if (this.addressLocal['addressCountry'] === 'US') {
|
|
285
|
-
label = 'State'
|
|
286
|
-
required = true
|
|
287
|
-
} else {
|
|
288
|
-
label = 'Province/State'
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return label + (required ? '' : ' (Optional)')
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/** The Postal Code label with 'optional' as needed. */
|
|
295
|
-
get postalCodeLabel (): string {
|
|
296
|
-
let label: string
|
|
297
|
-
if (this.addressLocal['addressCountry'] === 'US') {
|
|
298
|
-
label = 'Zip Code'
|
|
299
|
-
} else {
|
|
300
|
-
label = 'Postal Code'
|
|
162
|
+
import { defineComponent, onMounted, toRefs, watch } from 'vue-demi'
|
|
163
|
+
import {
|
|
164
|
+
baseRules,
|
|
165
|
+
useAddress,
|
|
166
|
+
useAddressComplete,
|
|
167
|
+
useCountryRegions,
|
|
168
|
+
useCountriesProvinces,
|
|
169
|
+
useBaseValidations,
|
|
170
|
+
spaceRules
|
|
171
|
+
} from '@/components/base-address/factories'
|
|
172
|
+
import { AddressIF, SchemaIF } from '@bcrs-shared-components/interfaces'
|
|
173
|
+
import { AddressValidationRules } from '@bcrs-shared-components/enums'
|
|
174
|
+
|
|
175
|
+
export default defineComponent({
|
|
176
|
+
name: 'BaseAddress',
|
|
177
|
+
props: {
|
|
178
|
+
value: {
|
|
179
|
+
type: Object as () => AddressIF,
|
|
180
|
+
default: () => ({
|
|
181
|
+
street: '',
|
|
182
|
+
streetAdditional: '',
|
|
183
|
+
city: '',
|
|
184
|
+
region: '',
|
|
185
|
+
postalCode: '',
|
|
186
|
+
country: '',
|
|
187
|
+
deliveryInstructions: ''
|
|
188
|
+
})
|
|
189
|
+
},
|
|
190
|
+
/* used for readonly mode vs edit mode */
|
|
191
|
+
editing: {
|
|
192
|
+
type: Boolean,
|
|
193
|
+
default: false
|
|
194
|
+
},
|
|
195
|
+
/* contains validation for each field */
|
|
196
|
+
schema: {
|
|
197
|
+
type: Object as () => SchemaIF,
|
|
198
|
+
default: null
|
|
199
|
+
},
|
|
200
|
+
/* triggers all current form validation errors */
|
|
201
|
+
triggerErrors: {
|
|
202
|
+
type: Boolean,
|
|
203
|
+
default: false
|
|
204
|
+
},
|
|
205
|
+
/* Hides the persistent hint field on Address Input */
|
|
206
|
+
hideAddressHint: {
|
|
207
|
+
type: Boolean,
|
|
208
|
+
default: false
|
|
209
|
+
},
|
|
210
|
+
/* Hides Delivery Address field (e.g. for Unit Notes) */
|
|
211
|
+
hideDeliveryAddress: {
|
|
212
|
+
type: Boolean,
|
|
213
|
+
default: false
|
|
301
214
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
]
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* The Vuetify rules object. Used to display any validation errors/styling.
|
|
337
|
-
* NB: As a getter, this is initialized between created() and mounted().
|
|
338
|
-
* @returns the Vuetify validation rules object
|
|
339
|
-
*/
|
|
340
|
-
get rules (): { [attr: string]: Array<() => boolean | string> } {
|
|
341
|
-
return this.createVuetifyRulesObject('addressLocal') as { [attr: string]: Array<() => boolean | string> }
|
|
342
|
-
}
|
|
343
|
-
/** Emits an update message for the address prop, so that the caller can ".sync" with it. */
|
|
344
|
-
@Emit('update:address')
|
|
345
|
-
emitAddress (address: object): void { }
|
|
346
|
-
|
|
347
|
-
/** Emits the validity of the address entered by the user. */
|
|
348
|
-
@Emit('valid')
|
|
349
|
-
emitValid (valid: boolean): void { }
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Watches changes to the Schema object, so that if the parent changes the data, then
|
|
353
|
-
* the working copy of it is updated.
|
|
354
|
-
*/
|
|
355
|
-
@Watch('schema', { deep: true, immediate: true })
|
|
356
|
-
onSchemaChanged (): void {
|
|
357
|
-
this.schemaLocal = { ...this.schema }
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Watches changes to the Address object, so that if the parent changes the data, then
|
|
362
|
-
* the working copy of it is updated.
|
|
363
|
-
*/
|
|
364
|
-
@Watch('address', { deep: true, immediate: true })
|
|
365
|
-
onAddressChanged (): void {
|
|
366
|
-
this.addressLocal = { ...this.address }
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Watches changes to the Address Country and updates the schema accordingly.
|
|
371
|
-
*/
|
|
372
|
-
@Watch('addressCountry')
|
|
373
|
-
onAddressCountryChanged (): void {
|
|
374
|
-
// skip this if component is called without a schema (eg, display mode)
|
|
375
|
-
if (this.schema) {
|
|
376
|
-
if (this.useCountryRegions(this.addressLocal['addressCountry'])) {
|
|
377
|
-
// we are using a region list for the current country so make region a required field
|
|
378
|
-
const addressRegion = { ...this.schema.addressRegion, required }
|
|
379
|
-
// re-assign the local schema because Vue does not detect property addition
|
|
380
|
-
this.schemaLocal = { ...this.schema, addressRegion }
|
|
215
|
+
},
|
|
216
|
+
emits: ['valid'],
|
|
217
|
+
setup (props, { emit }) {
|
|
218
|
+
const localSchema = { ...props.schema }
|
|
219
|
+
const {
|
|
220
|
+
addressLocal,
|
|
221
|
+
country,
|
|
222
|
+
schemaLocal,
|
|
223
|
+
isSchemaRequired,
|
|
224
|
+
labels
|
|
225
|
+
} = useAddress(toRefs(props).value, localSchema)
|
|
226
|
+
|
|
227
|
+
const origPostalCodeRules = localSchema.postalCode
|
|
228
|
+
const origRegionRules = localSchema.region
|
|
229
|
+
|
|
230
|
+
const { addressForm, resetValidation, validate } = useBaseValidations()
|
|
231
|
+
|
|
232
|
+
const { enableAddressComplete, uniqueIds } = useAddressComplete(addressLocal)
|
|
233
|
+
|
|
234
|
+
const countryProvincesHelpers = useCountriesProvinces()
|
|
235
|
+
|
|
236
|
+
const countryChangeHandler = (val: string, oldVal: string) => {
|
|
237
|
+
// do not trigger any changes if it is view only (summary instance)
|
|
238
|
+
if (!props.editing) return
|
|
239
|
+
|
|
240
|
+
if (val === 'CA') {
|
|
241
|
+
localSchema.postalCode = origPostalCodeRules.concat([baseRules.postalCode])
|
|
242
|
+
localSchema.region = origRegionRules
|
|
243
|
+
} else if (val === 'US') {
|
|
244
|
+
localSchema.postalCode = origPostalCodeRules.concat([baseRules.zipCode])
|
|
245
|
+
localSchema.region = origRegionRules
|
|
381
246
|
} else {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
// re-assign the local schema because Vue does not detect property deletion
|
|
385
|
-
this.schemaLocal = { ...this.schema, addressRegion }
|
|
247
|
+
localSchema.postalCode = origPostalCodeRules.concat([baseRules[AddressValidationRules.MAX_LENGTH](15)])
|
|
248
|
+
localSchema.region = [baseRules[AddressValidationRules.MAX_LENGTH](2), ...spaceRules]
|
|
386
249
|
}
|
|
250
|
+
// reset other address fields (check is for loading an existing address)
|
|
251
|
+
if (oldVal) {
|
|
252
|
+
addressLocal.value.street = ''
|
|
253
|
+
addressLocal.value.streetAdditional = ''
|
|
254
|
+
addressLocal.value.city = ''
|
|
255
|
+
addressLocal.value.region = ''
|
|
256
|
+
addressLocal.value.postalCode = ''
|
|
257
|
+
}
|
|
258
|
+
// wait for schema update and validate the form
|
|
259
|
+
setTimeout(() => {
|
|
260
|
+
props.triggerErrors ? validate() : resetValidation()
|
|
261
|
+
}, 5)
|
|
387
262
|
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Watches changes to the Address Local object, to catch any changes to the fields within the address.
|
|
392
|
-
* Will notify the parent object with the new address and whether or not the address is valid.
|
|
393
|
-
*/
|
|
394
|
-
@Watch('addressLocal', { deep: true, immediate: true })
|
|
395
|
-
onAddressLocalChanged (): void {
|
|
396
|
-
this.emitAddress(this.addressLocal)
|
|
397
|
-
this.emitValid(!this.$v.$invalid)
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Determines whether to use a country's known regions (ie, provinces/states).
|
|
402
|
-
* @param code the short code of the country
|
|
403
|
-
* @returns whether to use v-select (true) or v-text-field (false) for input
|
|
404
|
-
*/
|
|
405
|
-
useCountryRegions (code: string): boolean {
|
|
406
|
-
return (code === 'CA' || code === 'US')
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/** Enables AddressComplete for this instance of the address. */
|
|
410
|
-
enableAddressComplete (): void {
|
|
411
|
-
// If you want to use this component with the Canada Post AddressComplete service:
|
|
412
|
-
// 1. The AddressComplete JavaScript script (and stylesheet) must be loaded.
|
|
413
|
-
// 2. Your AddressComplete account key must be defined.
|
|
414
|
-
const pca = window['pca']
|
|
415
|
-
const key = window['addressCompleteKey']
|
|
416
|
-
if (!pca || !key) {
|
|
417
|
-
// eslint-disable-next-line no-console
|
|
418
|
-
console.log('AddressComplete not initialized due to missing script and/or key')
|
|
419
|
-
return
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Destroy the old object if it exists, and create a new one.
|
|
423
|
-
if (window['currentAddressComplete']) {
|
|
424
|
-
window['currentAddressComplete'].destroy()
|
|
425
|
-
}
|
|
426
|
-
window['currentAddressComplete'] = this.createAddressComplete(pca, key)
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Creates the AddressComplete object for this instance of the component.
|
|
431
|
-
* @param pca the Postal Code Anywhere object provided by AddressComplete
|
|
432
|
-
* @param key the key for the Canada Post account that is to be charged for lookups
|
|
433
|
-
* @returns an object that is a pca.Address instance
|
|
434
|
-
*/
|
|
435
|
-
createAddressComplete (pca, key: string): object {
|
|
436
|
-
// Set up the two fields that AddressComplete will use for input.
|
|
437
|
-
// Ref: https://www.canadapost.ca/pca/support/guides/advanced
|
|
438
|
-
// Note: Use special field for country, which user can't click, and which AC will overwrite
|
|
439
|
-
// but that we don't care about.
|
|
440
|
-
const fields = [
|
|
441
|
-
{ element: this.streetAddressId, field: 'Line1', mode: pca.fieldMode.SEARCH },
|
|
442
|
-
{ element: this.addressCountryId, field: 'CountryName', mode: pca.fieldMode.COUNTRY }
|
|
443
|
-
]
|
|
444
|
-
const options = { key }
|
|
445
263
|
|
|
446
|
-
|
|
264
|
+
onMounted(() => {
|
|
265
|
+
countryChangeHandler(addressLocal.value.country, null)
|
|
266
|
+
})
|
|
447
267
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
268
|
+
watch(() => addressLocal.value, (val) => {
|
|
269
|
+
let valid = true
|
|
270
|
+
/** checks each field against the schema rules to see if the address is valid or not
|
|
271
|
+
* NOTE: we don't want it to trigger error msgs yet which is why this does not call validate()
|
|
272
|
+
*/
|
|
273
|
+
for (const key in val) {
|
|
274
|
+
for (const index in schemaLocal.value[key]) {
|
|
275
|
+
if (schemaLocal.value[key][index](val[key]) !== true) {
|
|
276
|
+
valid = false
|
|
277
|
+
break
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (!valid) break
|
|
281
|
+
}
|
|
282
|
+
emit('valid', valid)
|
|
283
|
+
}, { immediate: true, deep: true })
|
|
452
284
|
|
|
453
|
-
|
|
454
|
-
|
|
285
|
+
watch(() => country.value, (val, oldVal) => {
|
|
286
|
+
countryChangeHandler(val, oldVal)
|
|
287
|
+
})
|
|
455
288
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
*/
|
|
460
|
-
addressCompletePopulate (address: object): void {
|
|
461
|
-
const newAddressLocal: object = {}
|
|
289
|
+
watch(() => props.triggerErrors, () => {
|
|
290
|
+
validate()
|
|
291
|
+
})
|
|
462
292
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
// In this case, v-text-input will allow manual entry but province info is probably too long
|
|
476
|
-
// so set region to null and add province name to the Street Address Additional field.
|
|
477
|
-
// If length is excessive, user will have to fix it.
|
|
478
|
-
newAddressLocal['addressRegion'] = null
|
|
479
|
-
newAddressLocal['streetAddressAdditional'] = this.combineLines(
|
|
480
|
-
newAddressLocal['streetAddressAdditional'], address['ProvinceName']
|
|
481
|
-
)
|
|
293
|
+
return {
|
|
294
|
+
addressForm,
|
|
295
|
+
addressLocal,
|
|
296
|
+
country,
|
|
297
|
+
...countryProvincesHelpers,
|
|
298
|
+
enableAddressComplete,
|
|
299
|
+
isSchemaRequired,
|
|
300
|
+
...labels,
|
|
301
|
+
schemaLocal,
|
|
302
|
+
useCountryRegions,
|
|
303
|
+
...uniqueIds,
|
|
304
|
+
validate
|
|
482
305
|
}
|
|
483
|
-
newAddressLocal['postalCode'] = address['PostalCode']
|
|
484
|
-
newAddressLocal['addressCountry'] = address['CountryIso2']
|
|
485
|
-
|
|
486
|
-
// re-assign the local address to force Vuetify update
|
|
487
|
-
this.addressLocal = newAddressLocal
|
|
488
|
-
|
|
489
|
-
// Validate the form, in case any fields are missing or incorrect.
|
|
490
|
-
Vue.nextTick(() => { (this.$refs.addressForm as any).validate() })
|
|
491
306
|
}
|
|
492
|
-
|
|
493
|
-
combineLines (line1: string, line2: string) {
|
|
494
|
-
if (!line1) return line2
|
|
495
|
-
if (!line2) return line1
|
|
496
|
-
return line1 + '\n' + line2
|
|
497
|
-
}
|
|
498
|
-
}
|
|
307
|
+
})
|
|
499
308
|
</script>
|
|
500
309
|
|
|
501
310
|
<style lang="scss" scoped>
|
|
502
|
-
@import
|
|
311
|
+
@import '@/assets/styles/theme.scss';
|
|
312
|
+
|
|
313
|
+
.delivery-text {
|
|
314
|
+
font-style: italic;
|
|
315
|
+
margin-top: 10px;
|
|
316
|
+
}
|
|
503
317
|
|
|
504
318
|
// Address Block Layout
|
|
505
319
|
.address-block {
|
|
506
320
|
display: flex;
|
|
321
|
+
p {
|
|
322
|
+
margin-bottom: unset;
|
|
323
|
+
}
|
|
507
324
|
}
|
|
508
325
|
|
|
509
326
|
.address-block__info {
|
|
510
327
|
flex: 1 1 auto;
|
|
511
328
|
}
|
|
512
329
|
|
|
513
|
-
.address-block__info-row {
|
|
514
|
-
color: $gray7;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
330
|
// Form Row Elements
|
|
518
331
|
.form__row.three-column {
|
|
519
332
|
align-items: stretch;
|
|
@@ -530,22 +343,8 @@ export default class BaseAddress extends Mixins(ValidationMixin, CountriesProvin
|
|
|
530
343
|
}
|
|
531
344
|
}
|
|
532
345
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
color: $gray7;
|
|
536
|
-
font-size: $px-16;
|
|
537
|
-
font-weight: normal;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// text field inputs
|
|
541
|
-
::v-deep {
|
|
542
|
-
.v-input input {
|
|
543
|
-
color: $gray9;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
.pre-line {
|
|
548
|
-
white-space: pre-line;
|
|
346
|
+
.pre-wrap {
|
|
347
|
+
white-space: pre-wrap;
|
|
549
348
|
}
|
|
550
349
|
|
|
551
350
|
// make 'readonly' inputs looks disabled
|