@sbc-connect/nuxt-forms 0.0.0-test-ld-20260520212549
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/.env.example +14 -0
- package/CHANGELOG.md +394 -0
- package/LICENSE +28 -0
- package/README.md +57 -0
- package/app/components/Connect/Fieldset.vue +87 -0
- package/app/components/Connect/Form/Address/City.vue +20 -0
- package/app/components/Connect/Form/Address/Country.vue +51 -0
- package/app/components/Connect/Form/Address/DeliveryInstructions.vue +20 -0
- package/app/components/Connect/Form/Address/PostalCode.vue +48 -0
- package/app/components/Connect/Form/Address/Region.vue +62 -0
- package/app/components/Connect/Form/Address/Street.vue +64 -0
- package/app/components/Connect/Form/Address/StreetAdditional.vue +18 -0
- package/app/components/Connect/Form/Address/index.vue +116 -0
- package/app/components/Connect/Form/FieldWrapper.vue +52 -0
- package/app/components/Connect/Form/Input.vue +38 -0
- package/app/components/Connect/Form/Phone/CountryCode.vue +128 -0
- package/app/components/Connect/Form/Phone/CountryFlag.vue +14 -0
- package/app/components/Connect/Form/Phone/index.vue +2 -0
- package/app/components/Connect/Form/Textarea.vue +39 -0
- package/app/components/Connect/Layout/Form.vue +21 -0
- package/app/components/HelloWorld/Forms.vue +12 -0
- package/app/composables/useCanadaPost.ts +74 -0
- package/app/coverage/base.css +224 -0
- package/app/coverage/block-navigation.js +87 -0
- package/app/coverage/clover.xml +2803 -0
- package/app/coverage/components/Connect/Fieldset.vue.html +310 -0
- package/app/coverage/components/Connect/Form/Address/City.vue.html +145 -0
- package/app/coverage/components/Connect/Form/Address/Country.vue.html +238 -0
- package/app/coverage/components/Connect/Form/Address/DeliveryInstructions.vue.html +145 -0
- package/app/coverage/components/Connect/Form/Address/PostalCode.vue.html +199 -0
- package/app/coverage/components/Connect/Form/Address/Region.vue.html +271 -0
- package/app/coverage/components/Connect/Form/Address/Street.vue.html +277 -0
- package/app/coverage/components/Connect/Form/Address/StreetAdditional.vue.html +139 -0
- package/app/coverage/components/Connect/Form/Address/index.html +221 -0
- package/app/coverage/components/Connect/Form/Address/index.vue.html +433 -0
- package/app/coverage/components/Connect/Form/FieldWrapper.vue.html +211 -0
- package/app/coverage/components/Connect/Form/Input.vue.html +199 -0
- package/app/coverage/components/Connect/Form/Phone/CountryCode.vue.html +457 -0
- package/app/coverage/components/Connect/Form/Phone/CountryFlag.vue.html +127 -0
- package/app/coverage/components/Connect/Form/Phone/index.html +146 -0
- package/app/coverage/components/Connect/Form/Phone/index.vue.html +91 -0
- package/app/coverage/components/Connect/Form/Textarea.vue.html +196 -0
- package/app/coverage/components/Connect/Form/index.html +146 -0
- package/app/coverage/components/Connect/Layout/Form.vue.html +148 -0
- package/app/coverage/components/Connect/Layout/index.html +116 -0
- package/app/coverage/components/Connect/index.html +116 -0
- package/app/coverage/components/HelloWorld/Forms.vue.html +121 -0
- package/app/coverage/components/HelloWorld/index.html +116 -0
- package/app/coverage/composables/index.html +116 -0
- package/app/coverage/composables/useCanadaPost.ts.html +307 -0
- package/app/coverage/coverage-final.json +29 -0
- package/app/coverage/enums/canada-post-address-datalevel.ts.html +106 -0
- package/app/coverage/enums/index.html +116 -0
- package/app/coverage/favicon.png +0 -0
- package/app/coverage/index.html +296 -0
- package/app/coverage/interfaces/canada-post.ts.html +199 -0
- package/app/coverage/interfaces/index.html +131 -0
- package/app/coverage/interfaces/phone-country.ts.html +127 -0
- package/app/coverage/layouts/ConnectForm.vue.html +100 -0
- package/app/coverage/layouts/index.html +116 -0
- package/app/coverage/prettify.css +1 -0
- package/app/coverage/prettify.js +2 -0
- package/app/coverage/sort-arrow-sprite.png +0 -0
- package/app/coverage/sorter.js +210 -0
- package/app/coverage/types/connect-fieldset.ts.html +88 -0
- package/app/coverage/types/index.html +116 -0
- package/app/coverage/utils/formHelpers.ts.html +124 -0
- package/app/coverage/utils/index.html +146 -0
- package/app/coverage/utils/index.ts.html +88 -0
- package/app/coverage/utils/isoCountriesList.ts.html +6193 -0
- package/app/coverage/utils/zod-schemas/addresses.ts.html +559 -0
- package/app/coverage/utils/zod-schemas/index.html +131 -0
- package/app/coverage/utils/zod-schemas/index.ts.html +88 -0
- package/app/enums/canada-post-address-datalevel.ts +7 -0
- package/app/interfaces/canada-post.ts +38 -0
- package/app/interfaces/phone-country.ts +14 -0
- package/app/layouts/ConnectForm.vue +5 -0
- package/app/types/connect-fieldset.ts +1 -0
- package/app/types/forms-page-meta.d.ts +5 -0
- package/app/utils/formHelpers.ts +13 -0
- package/app/utils/index.ts +1 -0
- package/app/utils/isoCountriesList.ts +2048 -0
- package/app/utils/zod-schemas/addresses.ts +159 -0
- package/app/utils/zod-schemas/index.ts +1 -0
- package/i18n/locales/en-CA.ts +49 -0
- package/i18n/locales/fr-CA.ts +3 -0
- package/nuxt.config.ts +58 -0
- package/package.json +40 -0
- package/playwright-report/index.html +85 -0
- package/public/css/addresscomplete-2.50.min.css +11 -0
- package/public/js/addresscomplete-2.50.min.js +260 -0
- package/test-results/.last-run.json +4 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
parentId: string
|
|
4
|
+
schemaPrefix: string
|
|
5
|
+
disabled?: boolean
|
|
6
|
+
}>()
|
|
7
|
+
|
|
8
|
+
const model = defineModel<string | undefined>({ required: true })
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<ConnectFormTextarea
|
|
13
|
+
v-model="model"
|
|
14
|
+
:data-testid="`${parentId}-field-locationDescription`"
|
|
15
|
+
:input-id="`${parentId}-input-locationDescription`"
|
|
16
|
+
:name="schemaPrefix + '.locationDescription'"
|
|
17
|
+
:disabled
|
|
18
|
+
:label="$t('connect.label.deliveryInstructionsOpt')"
|
|
19
|
+
/>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
parentId: string
|
|
4
|
+
schemaPrefix: string
|
|
5
|
+
disabled?: boolean
|
|
6
|
+
country?: string
|
|
7
|
+
required?: boolean
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
const isOptionalPostalCodeCountry = (country?: string) => {
|
|
11
|
+
if (!country) {
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return isNoPostalCodeCountry(country)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const model = defineModel<string | undefined>({
|
|
19
|
+
set(value) {
|
|
20
|
+
if (value) {
|
|
21
|
+
return value.toUpperCase()
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
required: true
|
|
25
|
+
})
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<ConnectFormInput
|
|
30
|
+
v-model="model"
|
|
31
|
+
:name="schemaPrefix + '.postalCode'"
|
|
32
|
+
:data-testid="`${parentId}-field-postalCode`"
|
|
33
|
+
:input-id="`${parentId}-input-postalCode`"
|
|
34
|
+
:disabled
|
|
35
|
+
:required="required && !isOptionalPostalCodeCountry(country)"
|
|
36
|
+
:label="country === 'US'
|
|
37
|
+
? $t('connect.label.zipCode')
|
|
38
|
+
: isOptionalPostalCodeCountry(country)
|
|
39
|
+
? $t('connect.label.postalCodeOpt')
|
|
40
|
+
: $t('connect.label.postalCode')
|
|
41
|
+
"
|
|
42
|
+
:mask="country === 'CA'
|
|
43
|
+
? '*** ***'
|
|
44
|
+
: country === 'US'
|
|
45
|
+
? '#####-####'
|
|
46
|
+
: undefined"
|
|
47
|
+
/>
|
|
48
|
+
</template>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const {
|
|
3
|
+
maxlength = '1000',
|
|
4
|
+
country
|
|
5
|
+
} = defineProps<{
|
|
6
|
+
parentId: string
|
|
7
|
+
country?: string
|
|
8
|
+
schemaPrefix: string
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
maxlength?: string
|
|
11
|
+
required?: boolean
|
|
12
|
+
}>()
|
|
13
|
+
|
|
14
|
+
const model = defineModel<string | undefined>({ required: true })
|
|
15
|
+
|
|
16
|
+
const regions = computed(() => {
|
|
17
|
+
switch (country) {
|
|
18
|
+
case 'US':
|
|
19
|
+
return countrySubdivisions.us
|
|
20
|
+
case 'CA':
|
|
21
|
+
return countrySubdivisions.ca
|
|
22
|
+
default:
|
|
23
|
+
return []
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<UFormField
|
|
30
|
+
:name="schemaPrefix + '.region'"
|
|
31
|
+
class="grow flex-1"
|
|
32
|
+
:data-testid="`${parentId}-field-region`"
|
|
33
|
+
>
|
|
34
|
+
<template #default="{ error }">
|
|
35
|
+
<ConnectSelect
|
|
36
|
+
v-if="country === 'US' || country === 'CA'"
|
|
37
|
+
:id="`${parentId}-input-region`"
|
|
38
|
+
v-model="model"
|
|
39
|
+
:label="country === 'CA' ? $t('connect.label.province') : $t('connect.label.state')"
|
|
40
|
+
:items="regions"
|
|
41
|
+
value-key="code"
|
|
42
|
+
label-key="name"
|
|
43
|
+
:required
|
|
44
|
+
:disabled
|
|
45
|
+
class="w-full grow"
|
|
46
|
+
/>
|
|
47
|
+
<ConnectInput
|
|
48
|
+
v-else
|
|
49
|
+
:id="`${parentId}-input-region`"
|
|
50
|
+
v-model="model"
|
|
51
|
+
:data-testid="`${parentId}-input-region`"
|
|
52
|
+
:disabled
|
|
53
|
+
:label="$t('connect.label.regionOpt')"
|
|
54
|
+
:maxlength
|
|
55
|
+
/>
|
|
56
|
+
<div
|
|
57
|
+
v-if="!$slots.help && !error"
|
|
58
|
+
class="h-4 mt-1"
|
|
59
|
+
/>
|
|
60
|
+
</template>
|
|
61
|
+
</UFormField>
|
|
62
|
+
</template>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const {
|
|
3
|
+
schemaPrefix,
|
|
4
|
+
helpText = 'none',
|
|
5
|
+
country,
|
|
6
|
+
disableAddressComplete,
|
|
7
|
+
parentId
|
|
8
|
+
} = defineProps<{
|
|
9
|
+
parentId: string
|
|
10
|
+
country?: string
|
|
11
|
+
schemaPrefix: string
|
|
12
|
+
disableAddressComplete?: boolean
|
|
13
|
+
helpText?: 'allow-po' | 'no-po' | 'none'
|
|
14
|
+
required?: boolean
|
|
15
|
+
}>()
|
|
16
|
+
|
|
17
|
+
const model = defineModel<string | undefined>({ required: true })
|
|
18
|
+
|
|
19
|
+
const emit = defineEmits<{
|
|
20
|
+
addressComplete: [value: ConnectAddress]
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const { address: canadaPostAddress, enableAddressComplete, destroyAddressComplete } = useCanadaPost()
|
|
24
|
+
|
|
25
|
+
const addressComplete = (id: string) => {
|
|
26
|
+
if (!disableAddressComplete && country === 'CA') {
|
|
27
|
+
enableAddressComplete(id, country, false)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
watch(canadaPostAddress, (newAddress) => {
|
|
32
|
+
emit('addressComplete', newAddress)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
watch(
|
|
36
|
+
() => country,
|
|
37
|
+
(v) => {
|
|
38
|
+
if (v !== 'CA') {
|
|
39
|
+
destroyAddressComplete()
|
|
40
|
+
} else if (!disableAddressComplete) {
|
|
41
|
+
enableAddressComplete(`${parentId}-input-street`, v, false)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<template>
|
|
48
|
+
<ConnectFormInput
|
|
49
|
+
v-model="model"
|
|
50
|
+
:data-testid="`${parentId}-field-street`"
|
|
51
|
+
:input-id="`${parentId}-input-street`"
|
|
52
|
+
:name="schemaPrefix + '.street'"
|
|
53
|
+
:help="helpText === 'none'
|
|
54
|
+
? undefined
|
|
55
|
+
: helpText === 'no-po'
|
|
56
|
+
? $t('connect.text.addressCannotBePOBox')
|
|
57
|
+
: $t('connect.text.addressCanBePOBox')
|
|
58
|
+
"
|
|
59
|
+
:label="$t('connect.label.street')"
|
|
60
|
+
:required
|
|
61
|
+
@keypress.once="addressComplete(`${parentId}-input-street`)"
|
|
62
|
+
@click="addressComplete(`${parentId}-input-street`)"
|
|
63
|
+
/>
|
|
64
|
+
</template>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
parentId: string
|
|
4
|
+
schemaPrefix: string
|
|
5
|
+
}>()
|
|
6
|
+
|
|
7
|
+
const model = defineModel<string | undefined>({ required: true })
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<ConnectFormInput
|
|
12
|
+
v-model="model"
|
|
13
|
+
:name="schemaPrefix + '.streetAdditional'"
|
|
14
|
+
:data-testid="`${parentId}-field-streetAdditional`"
|
|
15
|
+
:input-id="`${parentId}-input-streetAdditional`"
|
|
16
|
+
:label="$t('connect.label.streetAdditional')"
|
|
17
|
+
/>
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const {
|
|
3
|
+
disabledFields,
|
|
4
|
+
excludedFields = ['streetName', 'streetNumber', 'unitNumber'],
|
|
5
|
+
schemaPrefix,
|
|
6
|
+
streetHelpText = 'none'
|
|
7
|
+
} = defineProps<{
|
|
8
|
+
id: string
|
|
9
|
+
schemaPrefix: string
|
|
10
|
+
disabledFields?: Array<keyof ConnectAddress>
|
|
11
|
+
excludedFields?: Array<keyof ConnectAddress>
|
|
12
|
+
disableAddressComplete?: boolean
|
|
13
|
+
streetHelpText?: 'allow-po' | 'no-po' | 'none'
|
|
14
|
+
required?: boolean
|
|
15
|
+
}>()
|
|
16
|
+
|
|
17
|
+
const emit = defineEmits<{
|
|
18
|
+
'should-validate': []
|
|
19
|
+
}>()
|
|
20
|
+
|
|
21
|
+
const state = defineModel<Partial<ConnectAddress>>({ required: true })
|
|
22
|
+
|
|
23
|
+
async function populateAddressComplete(e: ConnectAddress) {
|
|
24
|
+
// get disabled/excluded fields
|
|
25
|
+
const disabledSet = new Set(disabledFields)
|
|
26
|
+
const excludedSet = new Set(excludedFields)
|
|
27
|
+
|
|
28
|
+
// get array of valid keys
|
|
29
|
+
const validKeys = (Object.keys(e) as Array<keyof ConnectAddress>).filter(key =>
|
|
30
|
+
!disabledSet.has(key) && !excludedSet.has(key)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
// set state for each valid key
|
|
34
|
+
validKeys.forEach((key) => {
|
|
35
|
+
state.value[key] = e[key]
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// wait for dom to populate inputs
|
|
39
|
+
await nextTick()
|
|
40
|
+
emit('should-validate')
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<div class="space-y-2" :data-testid="id + '-container'">
|
|
46
|
+
<ConnectFormAddressCountry
|
|
47
|
+
v-if="!excludedFields.includes('country')"
|
|
48
|
+
v-model="state.country"
|
|
49
|
+
:parent-id="id"
|
|
50
|
+
:schema-prefix="schemaPrefix"
|
|
51
|
+
:disabled="disabledFields?.includes('country')"
|
|
52
|
+
:required
|
|
53
|
+
@change="state.region = ''"
|
|
54
|
+
/>
|
|
55
|
+
|
|
56
|
+
<ConnectFormAddressStreet
|
|
57
|
+
v-if="!excludedFields.includes('street')"
|
|
58
|
+
v-model="state.street"
|
|
59
|
+
:parent-id="id"
|
|
60
|
+
:schema-prefix="schemaPrefix"
|
|
61
|
+
:country="state.country"
|
|
62
|
+
:disable-address-complete="disableAddressComplete"
|
|
63
|
+
:disabled="disabledFields?.includes('street')"
|
|
64
|
+
:help-text="streetHelpText"
|
|
65
|
+
:required
|
|
66
|
+
@address-complete="populateAddressComplete"
|
|
67
|
+
/>
|
|
68
|
+
|
|
69
|
+
<ConnectFormAddressStreetAdditional
|
|
70
|
+
v-if="!excludedFields.includes('streetAdditional')"
|
|
71
|
+
v-model="state.streetAdditional"
|
|
72
|
+
:parent-id="id"
|
|
73
|
+
:schema-prefix="schemaPrefix"
|
|
74
|
+
:disabled="disabledFields?.includes('streetAdditional')"
|
|
75
|
+
/>
|
|
76
|
+
|
|
77
|
+
<div class="flex flex-col gap-2 sm:gap-4 sm:flex-row">
|
|
78
|
+
<ConnectFormAddressCity
|
|
79
|
+
v-if="!excludedFields.includes('city')"
|
|
80
|
+
v-model="state.city"
|
|
81
|
+
:parent-id="id"
|
|
82
|
+
:schema-prefix="schemaPrefix"
|
|
83
|
+
:disabled="disabledFields?.includes('city')"
|
|
84
|
+
:required
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
<ConnectFormAddressRegion
|
|
88
|
+
v-if="!excludedFields.includes('region')"
|
|
89
|
+
v-model="state.region"
|
|
90
|
+
:parent-id="id"
|
|
91
|
+
:schema-prefix="schemaPrefix"
|
|
92
|
+
:country="state.country"
|
|
93
|
+
:disabled="disabledFields?.includes('region')"
|
|
94
|
+
:required
|
|
95
|
+
/>
|
|
96
|
+
|
|
97
|
+
<ConnectFormAddressPostalCode
|
|
98
|
+
v-if="!excludedFields.includes('postalCode')"
|
|
99
|
+
v-model="state.postalCode"
|
|
100
|
+
:parent-id="id"
|
|
101
|
+
:schema-prefix="schemaPrefix"
|
|
102
|
+
:country="state.country"
|
|
103
|
+
:disabled="disabledFields?.includes('postalCode')"
|
|
104
|
+
:required
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<ConnectFormAddressDeliveryInstructions
|
|
109
|
+
v-if="!excludedFields.includes('locationDescription')"
|
|
110
|
+
v-model="state.locationDescription"
|
|
111
|
+
:parent-id="id"
|
|
112
|
+
:schema-prefix="schemaPrefix"
|
|
113
|
+
:disabled="disabledFields?.includes('locationDescription')"
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
</template>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { FormError } from '@nuxt/ui'
|
|
3
|
+
|
|
4
|
+
const {
|
|
5
|
+
orientation = 'horizontal',
|
|
6
|
+
paddingClass = 'x-default',
|
|
7
|
+
detailsAriaHidden = false
|
|
8
|
+
} = defineProps<{
|
|
9
|
+
label: string
|
|
10
|
+
error?: FormError | boolean
|
|
11
|
+
detailsAriaHidden?: boolean
|
|
12
|
+
showErrorMsg?: boolean
|
|
13
|
+
orientation?: 'vertical' | 'horizontal'
|
|
14
|
+
paddingClass?: 'x-default' | 'xy-default' | string
|
|
15
|
+
}>()
|
|
16
|
+
|
|
17
|
+
const padding = paddingClass === 'x-default'
|
|
18
|
+
? 'padding-x-default'
|
|
19
|
+
: paddingClass === 'xy-default'
|
|
20
|
+
? 'padding-xy-default'
|
|
21
|
+
: paddingClass
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<div
|
|
26
|
+
:class="[
|
|
27
|
+
'flex gap-4 sm:gap-6',
|
|
28
|
+
padding,
|
|
29
|
+
orientation === 'horizontal' ? 'flex-col sm:flex-row' : 'flex-col',
|
|
30
|
+
error ? 'shadow-section-error' : '',
|
|
31
|
+
]"
|
|
32
|
+
>
|
|
33
|
+
<span
|
|
34
|
+
:aria-hidden="detailsAriaHidden"
|
|
35
|
+
class="text-base text-neutral-highlighted font-bold"
|
|
36
|
+
:class="{ 'w-full sm:basis-1/4': orientation === 'horizontal' }"
|
|
37
|
+
>
|
|
38
|
+
<div class="flex flex-col gap-1">
|
|
39
|
+
<span>{{ label }}</span>
|
|
40
|
+
<span
|
|
41
|
+
v-if="error && typeof error === 'object' && 'message' in error && showErrorMsg"
|
|
42
|
+
class="font-normal text-error"
|
|
43
|
+
>
|
|
44
|
+
{{ error.message }}
|
|
45
|
+
</span>
|
|
46
|
+
</div>
|
|
47
|
+
</span>
|
|
48
|
+
<div class="flex-1">
|
|
49
|
+
<slot />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</template>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
name: string
|
|
4
|
+
label: string
|
|
5
|
+
inputId: string
|
|
6
|
+
required?: boolean
|
|
7
|
+
autofocus?: boolean
|
|
8
|
+
mask?: string
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
help?: string
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
const model = defineModel<string | number | bigint | null | undefined>({ required: true })
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<UFormField
|
|
18
|
+
:name
|
|
19
|
+
:help
|
|
20
|
+
class="grow flex-1"
|
|
21
|
+
>
|
|
22
|
+
<template #default="{ error }">
|
|
23
|
+
<ConnectInput
|
|
24
|
+
:id="inputId"
|
|
25
|
+
v-model="model"
|
|
26
|
+
:mask
|
|
27
|
+
:aria-required="required"
|
|
28
|
+
:label
|
|
29
|
+
:autofocus
|
|
30
|
+
:disabled
|
|
31
|
+
/>
|
|
32
|
+
<div
|
|
33
|
+
v-if="!help && !error"
|
|
34
|
+
class="h-4 mt-1"
|
|
35
|
+
/>
|
|
36
|
+
</template>
|
|
37
|
+
</UFormField>
|
|
38
|
+
</template>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import countryList from 'country-codes-list'
|
|
3
|
+
import CountryFlag from '#forms/app/components/Connect/Form/Phone/CountryFlag.vue'
|
|
4
|
+
import type { PhoneCountry } from '#forms/app/interfaces/phone-country'
|
|
5
|
+
|
|
6
|
+
defineProps<{
|
|
7
|
+
isInvalid: boolean | undefined
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
const countryCallingCode = defineModel<string | undefined>('countryCallingCode', { required: false })
|
|
11
|
+
watch(countryCallingCode, (val) => {
|
|
12
|
+
// this is needed for when something outside this component changes the callingCode model value
|
|
13
|
+
if (!val) {
|
|
14
|
+
countryIso2.value = undefined
|
|
15
|
+
} else if (selectedCountry.value?.callingCode !== val) {
|
|
16
|
+
countryIso2.value = search(val)[0]?.iso2
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const countryIso2 = defineModel<string | undefined>('countryIso2', { required: false })
|
|
21
|
+
watch(countryIso2, (val) => {
|
|
22
|
+
if (!val) {
|
|
23
|
+
selectedCountry.value = undefined
|
|
24
|
+
} else if (val && selectedCountry.value?.iso2 !== val) {
|
|
25
|
+
selectCountry(val)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const selectedCountry = ref<PhoneCountry | undefined>(undefined)
|
|
30
|
+
watch(selectedCountry, (newVal) => {
|
|
31
|
+
if (newVal?.callingCode !== countryCallingCode.value) {
|
|
32
|
+
countryCallingCode.value = newVal?.callingCode
|
|
33
|
+
}
|
|
34
|
+
if (newVal?.iso2 !== countryIso2.value) {
|
|
35
|
+
countryIso2.value = newVal?.iso2
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const _countryListOptions = countryList.customList(
|
|
40
|
+
'countryCode', '{countryCallingCode},{countryNameEn},{countryNameLocal}')
|
|
41
|
+
|
|
42
|
+
const manualInput = (event: Event) => {
|
|
43
|
+
const target = event.target as HTMLInputElement
|
|
44
|
+
selectedCountry.value = {
|
|
45
|
+
callingCode: target.value
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const countryListOptions: Array<PhoneCountry> = Object.keys(_countryListOptions).map((key) => {
|
|
50
|
+
const parts = (_countryListOptions[key] ?? '').split(',')
|
|
51
|
+
const callingCode = parts[0] ?? ''
|
|
52
|
+
const nameEn = parts[1]
|
|
53
|
+
const nameLocal = parts[2]
|
|
54
|
+
return {
|
|
55
|
+
iso2: key,
|
|
56
|
+
callingCode,
|
|
57
|
+
label: `+${callingCode}`,
|
|
58
|
+
nameLocal,
|
|
59
|
+
nameEn
|
|
60
|
+
}
|
|
61
|
+
}).sort((a, b) => a.callingCode.localeCompare(b.callingCode))
|
|
62
|
+
|
|
63
|
+
const search = (q: string) => countryListOptions.filter((lo) => {
|
|
64
|
+
return lo.callingCode.includes(q)
|
|
65
|
+
|| lo.iso2?.includes(q)
|
|
66
|
+
|| lo.nameLocal?.includes(q)
|
|
67
|
+
|| lo.nameEn?.includes(q)
|
|
68
|
+
|| lo.label?.includes(q)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const selectCountry = (iso2: string) => {
|
|
72
|
+
selectedCountry.value = countryListOptions.find(item => item.iso2 === iso2)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
onMounted(() => {
|
|
76
|
+
if (countryIso2.value !== undefined) {
|
|
77
|
+
selectCountry(countryIso2.value)
|
|
78
|
+
} else if (countryCallingCode.value) {
|
|
79
|
+
// Set a country based on the calling code if available
|
|
80
|
+
const iso2 = search(countryCallingCode.value)[0]?.iso2
|
|
81
|
+
if (iso2) {
|
|
82
|
+
selectCountry(iso2)
|
|
83
|
+
} else {
|
|
84
|
+
selectedCountry.value = {
|
|
85
|
+
callingCode: countryCallingCode.value,
|
|
86
|
+
label: `+${countryCallingCode.value}`
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<template>
|
|
94
|
+
<UInputMenu
|
|
95
|
+
v-model="selectedCountry"
|
|
96
|
+
:items="countryListOptions"
|
|
97
|
+
:color="selectedCountry?.callingCode ? 'primary' : undefined"
|
|
98
|
+
:search="search"
|
|
99
|
+
:label-key="undefined"
|
|
100
|
+
:trailing-icon="'i-mdi-chevron-down'"
|
|
101
|
+
class="cursor-pointer"
|
|
102
|
+
:aria-required="true"
|
|
103
|
+
:aria-invalid="isInvalid"
|
|
104
|
+
:aria-label="$t('connect.label.countryCode')"
|
|
105
|
+
@input="manualInput($event)"
|
|
106
|
+
>
|
|
107
|
+
<template v-if="!!selectedCountry?.iso2" #leading="{ modelValue }">
|
|
108
|
+
<div class="flex">
|
|
109
|
+
<CountryFlag
|
|
110
|
+
:tooltip-text="selectedCountry?.nameEn"
|
|
111
|
+
:country-code-iso2letter="selectedCountry?.iso2"
|
|
112
|
+
/>
|
|
113
|
+
<span class="ml-2 mt-2 truncate">{{ (modelValue as PhoneCountry)?.label }}</span>
|
|
114
|
+
</div>
|
|
115
|
+
</template>
|
|
116
|
+
|
|
117
|
+
<template #item="{ item }">
|
|
118
|
+
<div v-if="item" class="flex items-end cursor-pointer">
|
|
119
|
+
<CountryFlag
|
|
120
|
+
:tooltip-text="(item as PhoneCountry).nameLocal"
|
|
121
|
+
:country-code-iso2letter="(item as PhoneCountry).iso2 ?? ''"
|
|
122
|
+
/>
|
|
123
|
+
<span class="ml-2 truncate">{{ (item as PhoneCountry).label }}</span>
|
|
124
|
+
<span class="sr-only">{{ (item as PhoneCountry).nameLocal }}</span>
|
|
125
|
+
</div>
|
|
126
|
+
</template>
|
|
127
|
+
</UInputMenu>
|
|
128
|
+
</template>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TextareaProps } from '@nuxt/ui'
|
|
3
|
+
|
|
4
|
+
defineProps<{
|
|
5
|
+
name: string
|
|
6
|
+
label: string
|
|
7
|
+
inputId: string
|
|
8
|
+
required?: boolean
|
|
9
|
+
autofocus?: boolean
|
|
10
|
+
disabled?: boolean
|
|
11
|
+
help?: string
|
|
12
|
+
}>()
|
|
13
|
+
|
|
14
|
+
const model = defineModel<Exclude<TextareaProps['modelValue'], null>>({ required: true })
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<UFormField
|
|
19
|
+
:name
|
|
20
|
+
:help
|
|
21
|
+
class="grow flex-1"
|
|
22
|
+
>
|
|
23
|
+
<template #default="{ error }">
|
|
24
|
+
<ConnectTextarea
|
|
25
|
+
:id="inputId"
|
|
26
|
+
v-model="model"
|
|
27
|
+
:data-testid="inputId"
|
|
28
|
+
:required
|
|
29
|
+
:label
|
|
30
|
+
:autofocus
|
|
31
|
+
:disabled
|
|
32
|
+
/>
|
|
33
|
+
<div
|
|
34
|
+
v-if="!help && !error"
|
|
35
|
+
class="h-4 mt-1"
|
|
36
|
+
/>
|
|
37
|
+
</template>
|
|
38
|
+
</UFormField>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ConnectLayout>
|
|
3
|
+
<template #header>
|
|
4
|
+
<!-- Exposes the header slot of the base layout -->
|
|
5
|
+
<slot name="header">
|
|
6
|
+
<ConnectHeader />
|
|
7
|
+
</slot>
|
|
8
|
+
</template>
|
|
9
|
+
<template #body>
|
|
10
|
+
<!-- Exposes the body slot of the base layout -->
|
|
11
|
+
<slot name="body">
|
|
12
|
+
<div class="grow max-w-full overflow-hidden">
|
|
13
|
+
<slot />
|
|
14
|
+
</div>
|
|
15
|
+
</slot>
|
|
16
|
+
</template>
|
|
17
|
+
<template #topFooters>
|
|
18
|
+
<ConnectButtonControlWrapper />
|
|
19
|
+
</template>
|
|
20
|
+
</ConnectLayout>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="bg-red-100 p-5">
|
|
3
|
+
Forms - should have bg-red-100
|
|
4
|
+
<HelloWorld />
|
|
5
|
+
<h3 class="text-green-900">
|
|
6
|
+
Forms - should be green-900
|
|
7
|
+
</h3>
|
|
8
|
+
<p class="text-neutral-highlighted">
|
|
9
|
+
Forms - should be neutral-highlighted
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|