@sbc-connect/nuxt-forms 0.1.8 → 0.1.10

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.
Files changed (30) hide show
  1. package/.env.example +10 -0
  2. package/CHANGELOG.md +20 -0
  3. package/app/components/Connect/ButtonControl/Buttons.vue +19 -0
  4. package/app/components/Connect/ButtonControl/Index.vue +59 -0
  5. package/app/components/Connect/ButtonControl/Wrapper.vue +10 -0
  6. package/app/components/Connect/Fieldset.vue +41 -0
  7. package/app/components/Connect/Form/Address/City.vue +20 -0
  8. package/app/components/Connect/Form/Address/Country.vue +94 -0
  9. package/app/components/Connect/Form/Address/DeliveryInstructions.vue +21 -0
  10. package/app/components/Connect/Form/Address/PostalCode.vue +37 -0
  11. package/app/components/Connect/Form/Address/Region.vue +107 -0
  12. package/app/components/Connect/Form/Address/Street.vue +52 -0
  13. package/app/components/Connect/Form/Address/StreetAdditional.vue +19 -0
  14. package/app/components/Connect/Form/Address/index.vue +112 -0
  15. package/app/components/Connect/Form/Input.vue +39 -0
  16. package/app/components/Connect/Form/Textarea.vue +38 -0
  17. package/app/components/Connect/Layout/Form.vue +22 -0
  18. package/app/components/HelloWorld/Forms.vue +2 -2
  19. package/app/composables/useButtonControl.ts +72 -0
  20. package/app/interfaces/connect-button-control.ts +17 -0
  21. package/app/layouts/ConnectForm.vue +5 -0
  22. package/app/types/forms-page-meta.d.ts +7 -0
  23. package/app/utils/isoCountriesList.ts +2036 -0
  24. package/i18n/locales/en-CA.ts +38 -0
  25. package/i18n/locales/fr-CA.ts +3 -0
  26. package/modules/form-assets/index.ts +15 -0
  27. package/modules/form-assets/runtime/assets/connect-form-tw.css +2 -0
  28. package/nuxt.config.ts +23 -2
  29. package/package.json +6 -6
  30. package/app/assets/css/tw-forms.css +0 -2
package/.env.example CHANGED
@@ -1 +1,11 @@
1
+ # Web Urls
1
2
  NUXT_PUBLIC_BASE_URL="http://localhost:3000/"
3
+ NUXT_PUBLIC_REGISTRY_HOME_URL="https://dev.bcregistry.gov.bc.ca/"
4
+
5
+ # API Urls
6
+ # Status API
7
+ NUXT_PUBLIC_STATUS_API_URL="https://status-api-dev.apps.gold.devops.gov.bc.ca"
8
+ NUXT_PUBLIC_STATUS_API_VERSION="/api/v1"
9
+
10
+ # LaunchDarkly
11
+ NUXT_PUBLIC_LD_CLIENT_ID=""
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @sbc-connect/nuxt-forms
2
2
 
3
+ ## 0.1.10
4
+
5
+ ### Patch Changes
6
+
7
+ - [#43](https://github.com/bcgov/connect-nuxt/pull/43) [`3b521b7`](https://github.com/bcgov/connect-nuxt/commit/3b521b70bf1a0cc5a0feea63a825ef5544347aa8) Thanks [@deetz99](https://github.com/deetz99)! - Form components and new lint rules
8
+
9
+ - Forms layer address components
10
+ - Lint rules and fixes to prevent rule collision and promote best practices
11
+
12
+ issue: bcgov/entity#29338
13
+
14
+ - Updated dependencies [[`3b521b7`](https://github.com/bcgov/connect-nuxt/commit/3b521b70bf1a0cc5a0feea63a825ef5544347aa8)]:
15
+ - @sbc-connect/nuxt-base@0.1.9
16
+
17
+ ## 0.1.9
18
+
19
+ ### Patch Changes
20
+
21
+ - [#37](https://github.com/bcgov/connect-nuxt/pull/37) [`216dff5`](https://github.com/bcgov/connect-nuxt/commit/216dff5eea1cf8459fc516dc8a1985f922e84533) Thanks [@kialj876](https://github.com/kialj876)! - Button Control Component and Form Layout bcgov/entity/29338
22
+
3
23
  ## 0.1.8
4
24
 
5
25
  ### Patch Changes
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ defineProps<{ buttons: ConnectButton[], position: 'left' | 'right' }>()
3
+ </script>
4
+
5
+ <template>
6
+ <div
7
+ v-for="(button, i) in buttons"
8
+ :key="`${position}-button-` + i"
9
+ data-testid="button-wrapper"
10
+ >
11
+ <UButton
12
+ class="max-w-fit px-7 py-3 mb-1"
13
+ v-bind="button"
14
+ />
15
+ <p :class="['text-center', !button.removeAlertSpacing ? 'h-[1.25rem]' : '']">
16
+ <span v-if="button.alertText" class="text-error">{{ button.alertText }}</span>
17
+ </p>
18
+ </div>
19
+ </template>
@@ -0,0 +1,59 @@
1
+ <script setup lang="ts">
2
+ const buttonControl = computed(() => useRoute().meta.buttonControl as ConnectButtonControl)
3
+ const leftGroup = computed(() => buttonControl.value?.leftGroup || [])
4
+ const rightGroup = computed(() => buttonControl.value?.rightGroup || [])
5
+ const leftAlertText = computed(() => buttonControl.value?.leftGroup.alertText || undefined)
6
+ const rightAlertText = computed(() => buttonControl.value?.rightGroup.alertText || undefined)
7
+ </script>
8
+
9
+ <template>
10
+ <div
11
+ class="@container"
12
+ data-testid="connect-button-control"
13
+ >
14
+ <div class="grid grid-cols-1 gap-4 @3xl:grid-cols-2">
15
+ <div>
16
+ <div
17
+ class="flex justify-center @3xl:justify-start"
18
+ data-testid="left-buttons"
19
+ >
20
+ <div
21
+ class="relative flex flex-col flex-wrap gap-4 justify-center"
22
+ :class="leftGroup.stacked ? '' : 'flex-row'"
23
+ >
24
+ <div
25
+ v-if="leftAlertText"
26
+ class="absolute flex flex-wrap justify-start bottom-1/3 right-[-13rem] w-[12rem]"
27
+ >
28
+ <p class="text-error" role="alert">
29
+ {{ leftAlertText }}
30
+ </p>
31
+ </div>
32
+ <ConnectButtonControlButtons :buttons="leftGroup.buttons" :position="'left'" />
33
+ </div>
34
+ </div>
35
+ </div>
36
+ <div>
37
+ <div
38
+ class="flex justify-center @3xl:justify-end"
39
+ data-testid="right-buttons"
40
+ >
41
+ <div
42
+ class="relative flex flex-col flex-wrap gap-4 justify-center"
43
+ :class="rightGroup.stacked ? '' : 'flex-row'"
44
+ >
45
+ <div
46
+ v-if="rightAlertText"
47
+ class="absolute flex flex-wrap justify-end bottom-1/3 left-[-13rem] w-[12rem]"
48
+ >
49
+ <p class="text-error" role="alert">
50
+ {{ rightAlertText }}
51
+ </p>
52
+ </div>
53
+ <ConnectButtonControlButtons :buttons="rightGroup.buttons" :position="'right'" />
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </template>
@@ -0,0 +1,10 @@
1
+ <template>
2
+ <div
3
+ class="bg-shade-inverted pb-6 pt-10"
4
+ data-testid="connect-button-control-wrapper"
5
+ >
6
+ <div class="app-inner-container app-body">
7
+ <ConnectButtonControl />
8
+ </div>
9
+ </div>
10
+ </template>
@@ -0,0 +1,41 @@
1
+ <script setup lang="ts">
2
+ import type { FormError } from '@nuxt/ui'
3
+
4
+ const {
5
+ orientation = 'vertical'
6
+ } = defineProps<{
7
+ label: string
8
+ error?: FormError
9
+ showErrorMsg?: boolean
10
+ orientation?: 'vertical' | 'horizontal'
11
+ }>()
12
+ // TODO: figure out why text-error doesnt work on the legend text
13
+ </script>
14
+
15
+ <template>
16
+ <fieldset
17
+ class="flex"
18
+ :class="[orientation === 'horizontal' ? 'flex-col gap-6 sm:flex-row sm:gap-4' : 'flex-col gap-6']"
19
+ >
20
+ <div :class="{ 'w-full sm:w-1/4': orientation === 'horizontal' }">
21
+ <legend
22
+ class="text-base text-neutral-highlighted font-bold"
23
+ :class="{ 'text-red-600': !!error }"
24
+ >
25
+ <div class="flex flex-wrap gap-4">
26
+ <span>{{ label }}</span>
27
+ <span
28
+ v-if="!!error && showErrorMsg"
29
+ class="font-normal"
30
+ >
31
+ {{ error.message }}
32
+ </span>
33
+ </div>
34
+ </legend>
35
+ </div>
36
+
37
+ <div class="flex-1">
38
+ <slot />
39
+ </div>
40
+ </fieldset>
41
+ </template>
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps<{
3
+ id: string
4
+ schemaPrefix: string
5
+ }>()
6
+
7
+ const model = defineModel<string>({ default: '' })
8
+
9
+ const inputId = props.id + '-city'
10
+ </script>
11
+
12
+ <template>
13
+ <ConnectFormInput
14
+ v-model="model"
15
+ :name="schemaPrefix + '.city'"
16
+ :input-id="inputId"
17
+ required
18
+ :label="$t('connect.label.city')"
19
+ />
20
+ </template>
@@ -0,0 +1,94 @@
1
+ <script setup lang="ts">
2
+ import { iscCountriesListSortedByName as countries } from '#forms/app/utils/isoCountriesList'
3
+
4
+ const props = defineProps<{
5
+ id: string
6
+ schemaPrefix: string
7
+ disabled?: boolean
8
+ }>()
9
+
10
+ defineEmits<{
11
+ change: []
12
+ }>()
13
+
14
+ const model = defineModel<string>({ default: '' })
15
+
16
+ const inputId = props.id + '-country'
17
+
18
+ const displayedCountryName = computed(() => {
19
+ if (model.value) {
20
+ const found = countries.find(c => c.alpha_2 === model.value)?.name
21
+ return found ?? ''
22
+ }
23
+ return ''
24
+ })
25
+
26
+ const options = [
27
+ countries.find(c => c.alpha_2 === 'CA')!,
28
+ countries.find(c => c.alpha_2 === 'US')!,
29
+ ...countries
30
+ ]
31
+ </script>
32
+
33
+ <template>
34
+ <UFormField
35
+ :name="schemaPrefix + '.country'"
36
+ class="grow"
37
+ >
38
+ <template #default="{ error }">
39
+ <USelect
40
+ :id="inputId"
41
+ v-model="model"
42
+ :data-testid="inputId"
43
+ :items="options"
44
+ value-key="alpha_2"
45
+ label-key="name"
46
+ class="w-full"
47
+ :class="error ? 'shadow-input-error ring-0' : ''"
48
+ :disabled
49
+ :aria-label="$t('connect.label.country')"
50
+ :aria-required="true"
51
+ :ui="{
52
+ base: error
53
+ ? 'shadow-input-error focus:shadow-input-error data-[state=open]:shadow-input-error'
54
+ : '',
55
+ item: 'nth-2:border-b nth-2:border-gray-200',
56
+ itemTrailingIcon: 'hidden',
57
+ }"
58
+ @change="$emit('change')"
59
+ >
60
+ <template #default="{ modelValue }">
61
+ <div class="relative px-2.5 pb-2 pt-6 w-full">
62
+ <span
63
+ aria-hidden="true"
64
+ :class="[
65
+ !modelValue
66
+ ? 'top-1/2 -translate-y-1/2'
67
+ : 'top-1 -translate-y-none text-xs',
68
+ error
69
+ ? 'text-red-600'
70
+ : '',
71
+ 'absolute left-0 px-2.5 text-sm transition-all',
72
+ 'group-data-[state=open]:text-primary group-focus:text-primary',
73
+ ]"
74
+ >
75
+ {{ $t('connect.label.country') }}
76
+ </span>
77
+ <div class="h-6">
78
+ <span
79
+ v-if="modelValue"
80
+ class="line-clamp-1 text-left"
81
+ >
82
+ {{ displayedCountryName }}
83
+ </span>
84
+ </div>
85
+ </div>
86
+ </template>
87
+ </USelect>
88
+ <div
89
+ v-if="!$slots.help && !error"
90
+ class="h-4 mt-1"
91
+ />
92
+ </template>
93
+ </UFormField>
94
+ </template>
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps<{
3
+ id: string
4
+ schemaPrefix: string
5
+ disabled?: boolean
6
+ }>()
7
+
8
+ const model = defineModel<string>({ default: '' })
9
+
10
+ const inputId = props.id + '-locationDescription'
11
+ </script>
12
+
13
+ <template>
14
+ <ConnectFormTextarea
15
+ v-model="model"
16
+ :input-id="inputId"
17
+ :name="schemaPrefix + '.locationDescription'"
18
+ :disabled
19
+ :label="$t('connect.label.deliveryInstructionsOpt')"
20
+ />
21
+ </template>
@@ -0,0 +1,37 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps<{
3
+ id: string
4
+ schemaPrefix: string
5
+ disabled?: boolean
6
+ country?: string
7
+ }>()
8
+
9
+ const model = defineModel({
10
+ set(value) {
11
+ return value.toUpperCase()
12
+ },
13
+ type: String,
14
+ default: ''
15
+ })
16
+
17
+ const inputId = props.id + '-postalCode'
18
+ </script>
19
+
20
+ <template>
21
+ <ConnectFormInput
22
+ v-model="model"
23
+ :name="schemaPrefix + '.postalCode'"
24
+ :input-id="inputId"
25
+ :disabled
26
+ required
27
+ :label="country === 'US'
28
+ ? $t('connect.label.zipCode')
29
+ : $t('connect.label.postalCode')
30
+ "
31
+ :mask="country === 'CA'
32
+ ? 'V#@ #@#'
33
+ : country === 'US'
34
+ ? '#####-####'
35
+ : undefined"
36
+ />
37
+ </template>
@@ -0,0 +1,107 @@
1
+ <script setup lang="ts">
2
+ const {
3
+ maxlength = '1000',
4
+ country,
5
+ id
6
+ } = defineProps<{
7
+ id: string
8
+ country?: string
9
+ schemaPrefix: string
10
+ disabled?: boolean
11
+ maxlength?: string
12
+ help?: string
13
+ }>()
14
+
15
+ const model = defineModel<string>({ default: '' })
16
+
17
+ const regions = computed(() => {
18
+ switch (country) {
19
+ case 'US':
20
+ return countrySubdivisions.us
21
+ case 'CA':
22
+ return countrySubdivisions.ca
23
+ default:
24
+ return []
25
+ }
26
+ })
27
+
28
+ const displayedRegionName = computed(() => {
29
+ if (model.value) {
30
+ const found = regions.value.find(r => r.code === model.value)?.name
31
+ return found ?? ''
32
+ }
33
+ return ''
34
+ })
35
+
36
+ const inputId = id + '-region'
37
+ </script>
38
+
39
+ <template>
40
+ <UFormField
41
+ :name="schemaPrefix + '.region'"
42
+ class="grow flex-1"
43
+ >
44
+ <template #default="{ error }">
45
+ <USelect
46
+ v-if="country === 'US' || country === 'CA'"
47
+ :id="inputId"
48
+ v-model="model"
49
+ :data-testid="inputId"
50
+ :items="regions"
51
+ :aria-label="country === 'CA' ? $t('connect.label.province') : $t('connect.label.state')"
52
+ value-key="code"
53
+ label-key="name"
54
+ :aria-required="true"
55
+ :disabled
56
+ class="w-full grow ring-0"
57
+ :ui="{
58
+ base: error
59
+ ? 'shadow-input-error focus:shadow-input-error data-[state=open]:shadow-input-error'
60
+ : '',
61
+ }"
62
+ >
63
+ <template #default="{ modelValue }">
64
+ <div class="relative px-2.5 pb-2 pt-6 w-full">
65
+ <span
66
+ aria-hidden="true"
67
+ class="absolute left-0 px-2.5 text-sm transition-all"
68
+ :class="[
69
+ !modelValue
70
+ ? 'top-1/2 -translate-y-1/2'
71
+ : 'top-1 -translate-y-none text-xs',
72
+ error
73
+ ? 'text-error'
74
+ : '',
75
+ 'absolute left-0 px-2.5 text-sm transition-all',
76
+ 'group-data-[state=open]:text-primary group-focus:text-primary',
77
+ ]"
78
+ >
79
+ {{ country === 'CA' ? $t('connect.label.province') : $t('connect.label.state') }}
80
+ </span>
81
+ <div class="h-6">
82
+ <span
83
+ v-if="modelValue"
84
+ class="line-clamp-1 text-left"
85
+ >
86
+ {{ displayedRegionName }}
87
+ </span>
88
+ </div>
89
+ </div>
90
+ </template>
91
+ </USelect>
92
+ <ConnectInput
93
+ v-else
94
+ :id="inputId"
95
+ v-model="model"
96
+ :invalid="!!error"
97
+ :disabled
98
+ :label="$t('connect.label.regionOpt')"
99
+ :maxlength
100
+ />
101
+ <div
102
+ v-if="!help && !error"
103
+ class="h-4 mt-1"
104
+ />
105
+ </template>
106
+ </UFormField>
107
+ </template>
@@ -0,0 +1,52 @@
1
+ <script setup lang="ts">
2
+ const {
3
+ id,
4
+ schemaPrefix,
5
+ helpText = 'none'
6
+ } = defineProps<{
7
+ id: string
8
+ // country?: string
9
+ schemaPrefix: string
10
+ // disableAddressComplete?: boolean
11
+ helpText?: 'allow-po' | 'no-po' | 'none'
12
+ }>()
13
+
14
+ const model = defineModel<string>({ default: '' })
15
+
16
+ // TODO: add address complete, and address complete disable options
17
+ // const emit = defineEmits<{
18
+ // addressComplete: [value: ConnectAddress]
19
+ // }>()
20
+
21
+ const inputId = id + '-street'
22
+
23
+ // const { address: canadaPostAddress, enableAddressComplete } = useCanadaPost()
24
+
25
+ // const addressComplete = (id: string) => {
26
+ // if (props.country && props.country.trim() !== '') {
27
+ // enableAddressComplete(id, props.country, props.disableAddressComplete)
28
+ // }
29
+ // }
30
+
31
+ // watch(canadaPostAddress, (newAddress) => {
32
+ // emit('addressComplete', newAddress)
33
+ // })
34
+ </script>
35
+
36
+ <template>
37
+ <ConnectFormInput
38
+ v-model="model"
39
+ :input-id="inputId"
40
+ :name="schemaPrefix + '.street'"
41
+ :help="helpText === 'none'
42
+ ? undefined
43
+ : helpText === 'no-po'
44
+ ? $t('connect.text.addressCannotBePOBox')
45
+ : $t('connect.text.addressCanBePOBox')
46
+ "
47
+ :label="$t('connect.label.street')"
48
+ required
49
+ />
50
+ <!-- @keypress.once="addressComplete(inputId)"
51
+ @click="addressComplete(inputId)" -->
52
+ </template>
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps<{
3
+ id: string
4
+ schemaPrefix: string
5
+ }>()
6
+
7
+ const model = defineModel<string>({ default: '' })
8
+
9
+ const inputId = props.id + '-streetAdditional'
10
+ </script>
11
+
12
+ <template>
13
+ <ConnectFormInput
14
+ v-model="model"
15
+ :name="schemaPrefix + '.streetAdditional'"
16
+ :input-id="inputId"
17
+ :label="$t('connect.label.streetAdditional')"
18
+ />
19
+ </template>
@@ -0,0 +1,112 @@
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
+ }>()
15
+
16
+ const emit = defineEmits<{
17
+ shouldValidate: [keys: Array<keyof ConnectAddress>]
18
+ }>()
19
+
20
+ const state = defineModel<Partial<ConnectAddress>>({ required: true })
21
+
22
+ async function populateAddressComplete(e: ConnectAddress) {
23
+ // get disabled/excluded fields
24
+ const disabledSet = new Set(disabledFields)
25
+ const excludedSet = new Set(excludedFields)
26
+
27
+ // get array of valid keys
28
+ const validKeys = (Object.keys(e) as Array<keyof ConnectAddress>).filter(key =>
29
+ !disabledSet.has(key) && !excludedSet.has(key)
30
+ )
31
+
32
+ // set state for each valid key
33
+ validKeys.forEach((key) => {
34
+ state.value[key] = e[key]
35
+ })
36
+
37
+ // wait for dom to populate inputs
38
+ await nextTick()
39
+
40
+ // emit valid keys to be validated by parent UForm
41
+ emit('shouldValidate', validKeys.map(k => schemaPrefix + k) as Array<keyof ConnectAddress>)
42
+ }
43
+ </script>
44
+
45
+ <template>
46
+ <div class="space-y-2" :data-testid="id">
47
+ <ConnectFormAddressCountry
48
+ v-if="!excludedFields.includes('country')"
49
+ :id
50
+ v-model="state.country"
51
+ :schema-prefix="schemaPrefix"
52
+ :disabled="disabledFields?.includes('country')"
53
+ @change="state.region = ''"
54
+ />
55
+
56
+ <ConnectFormAddressStreet
57
+ v-if="!excludedFields.includes('street')"
58
+ :id
59
+ v-model="state.street"
60
+ :schema-prefix="schemaPrefix"
61
+ :country="state.country"
62
+ :disable-address-complete="disableAddressComplete"
63
+ :disabled="disabledFields?.includes('street')"
64
+ :help-text="streetHelpText"
65
+ @address-complete="populateAddressComplete"
66
+ />
67
+
68
+ <ConnectFormAddressStreetAdditional
69
+ v-if="!excludedFields.includes('streetAdditional')"
70
+ :id
71
+ v-model="state.streetAdditional"
72
+ :schema-prefix="schemaPrefix"
73
+ :disabled="disabledFields?.includes('streetAdditional')"
74
+ />
75
+
76
+ <div class="flex flex-col gap-2 sm:gap-4 sm:flex-row">
77
+ <ConnectFormAddressCity
78
+ v-if="!excludedFields.includes('city')"
79
+ :id
80
+ v-model="state.city"
81
+ :schema-prefix="schemaPrefix"
82
+ :disabled="disabledFields?.includes('city')"
83
+ />
84
+
85
+ <ConnectFormAddressRegion
86
+ v-if="!excludedFields.includes('region')"
87
+ :id
88
+ v-model="state.region"
89
+ :schema-prefix="schemaPrefix"
90
+ :country="state.country"
91
+ :disabled="disabledFields?.includes('region')"
92
+ />
93
+
94
+ <ConnectFormAddressPostalCode
95
+ v-if="!excludedFields.includes('postalCode')"
96
+ :id
97
+ v-model="state.postalCode"
98
+ :schema-prefix="schemaPrefix"
99
+ :country="state.country"
100
+ :disabled="disabledFields?.includes('postalCode')"
101
+ />
102
+ </div>
103
+
104
+ <ConnectFormAddressDeliveryInstructions
105
+ v-if="!excludedFields.includes('locationDescription')"
106
+ :id
107
+ v-model="state.locationDescription"
108
+ :schema-prefix="schemaPrefix"
109
+ :disabled="disabledFields?.includes('locationDescription')"
110
+ />
111
+ </div>
112
+ </template>
@@ -0,0 +1,39 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ name: string
4
+ label: string
5
+ help?: string
6
+ inputId: string
7
+ required?: boolean
8
+ autofocus?: boolean
9
+ mask?: string
10
+ disabled?: boolean
11
+ }>()
12
+
13
+ const model = defineModel<string>({ 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
+ :required
28
+ :invalid="!!error"
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>