@flyanra/vue-core 1.3.8 → 1.4.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.
Files changed (42) hide show
  1. package/dist/types/components/form/index.d.ts +1 -3
  2. package/dist/types/components/form/input-renderers/country-input/FlyCountryCitySelect.vue.d.ts +19 -0
  3. package/dist/types/components/form/input-renderers/country-input/FlyCountrySelect.types.d.ts +4 -0
  4. package/dist/types/components/form/input-renderers/country-input/FlyCountrySelect.vue.d.ts +30 -0
  5. package/dist/types/components/form/input-renderers/country-input/FlyCountryStatesSelect.vue.d.ts +18 -0
  6. package/dist/types/components/form/input-renderers/country-input/countries.d.ts +49 -0
  7. package/dist/types/components/form/input-renderers/country-input/index.d.ts +5 -0
  8. package/dist/types/components/form/input-renderers/index.d.ts +5 -0
  9. package/dist/types/components/form/input-renderers/phone-number/FlyPhoneNumberInput.types.d.ts +1 -0
  10. package/dist/types/components/form/input-renderers/phone-number/FlyPhoneNumberInput.vue.d.ts +17 -0
  11. package/dist/types/components/form/input-renderers/phone-number/index.d.ts +3 -0
  12. package/dist/types/components/form/input-validators.d.ts +5 -0
  13. package/dist/types/composables/useLocaleInjector/index.d.ts +2 -2
  14. package/dist/types/composables/useLocaleInjector/useLocaleInjector.d.ts +13 -0
  15. package/dist/types/utils/country-utils.d.ts +3 -0
  16. package/dist/types/utils/index.d.ts +1 -0
  17. package/package.json +8 -4
  18. package/src/components/basic/typography/FlyCollapseTitleRenderer.vue +2 -2
  19. package/src/components/form/form.utils.ts +3 -3
  20. package/src/components/form/index.ts +1 -4
  21. package/src/components/form/input-renderers/country-input/FlyCountryCitySelect.vue +73 -0
  22. package/src/components/form/input-renderers/country-input/FlyCountrySelect.types.ts +4 -0
  23. package/src/components/form/input-renderers/country-input/FlyCountrySelect.vue +76 -0
  24. package/src/components/form/input-renderers/country-input/FlyCountryStatesSelect.vue +80 -0
  25. package/src/components/form/input-renderers/country-input/countries.ts +5746 -0
  26. package/src/components/form/input-renderers/country-input/index.ts +6 -0
  27. package/src/components/form/input-renderers/form-item-renderer/FlyFormItemRenderer.vue +2 -2
  28. package/src/components/form/input-renderers/index.ts +7 -0
  29. package/src/components/form/input-renderers/phone-number/FlyPhoneNumberInput.scss +1 -0
  30. package/src/components/form/input-renderers/phone-number/FlyPhoneNumberInput.types.ts +0 -0
  31. package/src/components/form/input-renderers/phone-number/FlyPhoneNumberInput.vue +99 -0
  32. package/src/components/form/input-renderers/phone-number/index.ts +4 -0
  33. package/src/components/form/input-renderers/regional-tenant-form-renderer/FlyRegionalTenantFormRenderer.vue +2 -2
  34. package/src/components/form/input-validators.ts +109 -2
  35. package/src/composables/useLocaleInjector/index.ts +2 -2
  36. package/src/composables/useLocaleInjector/useLocaleInjector.ts +34 -0
  37. package/src/styles/element/overrides/form.scss +0 -2
  38. package/src/utils/country-utils.ts +27 -0
  39. package/src/utils/index.ts +2 -0
  40. package/src/utils/validators.ts +3 -3
  41. package/dist/types/composables/useLocaleInjector/UseLocaleInjector.d.ts +0 -14
  42. package/src/composables/useLocaleInjector/UseLocaleInjector.ts +0 -37
@@ -0,0 +1,6 @@
1
+ import FlyCountryCitySelect from './FlyCountryCitySelect.vue'
2
+ import FlyCountrySelect from './FlyCountrySelect.vue'
3
+ import FlyCountryStatesSelect from './FlyCountryStatesSelect.vue'
4
+
5
+ export default FlyCountrySelect
6
+ export { FlyCountrySelect, FlyCountryCitySelect, FlyCountryStatesSelect }
@@ -20,7 +20,7 @@ import { computed } from 'vue'
20
20
 
21
21
  import { ElFormItem } from 'element-plus'
22
22
 
23
- import { UseLocaleInjector } from '../../../../composables/useLocaleInjector/UseLocaleInjector'
23
+ import { useLocaleInjector } from '../../../../composables/useLocaleInjector/useLocaleInjector'
24
24
  import FlyInfoTooltip from '../../../feedback/tooltip/FlyInfoTooltip.vue'
25
25
  import type { T_InputRendererBase } from '../input-renderer.types'
26
26
  import FlyInputRenderer from '../input-renderer/FlyInputRenderer.vue'
@@ -39,7 +39,7 @@ const emit = defineEmits<{
39
39
  (e: 'focus'): void
40
40
  }>()
41
41
 
42
- const { t } = UseLocaleInjector()
42
+ const { t } = useLocaleInjector()
43
43
 
44
44
  const modelProxy = computed({
45
45
  get: () => props.modelValue,
@@ -0,0 +1,7 @@
1
+ export * from './form-item-renderer'
2
+ export * from './input-renderer'
3
+
4
+ export * from './country-input'
5
+ export * from './phone-number'
6
+
7
+ export type { T_InputRendererBase } from './input-renderer.types'
@@ -0,0 +1 @@
1
+ @use '../text/FlyText.scss';
@@ -0,0 +1,99 @@
1
+ <template>
2
+ <el-input
3
+ v-model="localInput"
4
+ :disabled="!countryCode"
5
+ :maxlength="phoneMaxLength"
6
+ clearable
7
+ @input="updateModel"
8
+ @change="emit('change', props.modelValue)"
9
+ @blur="emit('blur')"
10
+ @focus="emit('focus')"
11
+ >
12
+ <template #prepend>
13
+ <FlyCountrySelect
14
+ v-model="countryCode"
15
+ style="width: 115px"
16
+ @update:model-value="handleCountryChange"
17
+ >
18
+ <template #label="{ scope }">
19
+ <span v-if="scope.value"> +{{ getCountryCallingCode(scope.value as CountryCode) }} </span>
20
+ </template>
21
+ </FlyCountrySelect>
22
+ </template>
23
+ </el-input>
24
+ </template>
25
+
26
+ <script setup lang="ts">
27
+ import { computed, ref, watch } from 'vue'
28
+
29
+ import { ElInput } from 'element-plus'
30
+
31
+ import {
32
+ type CountryCode,
33
+ getCountryCallingCode,
34
+ getExampleNumber,
35
+ parsePhoneNumberFromString
36
+ } from 'libphonenumber-js'
37
+ import examples from 'libphonenumber-js/examples.mobile.json'
38
+
39
+ import FlyCountrySelect from '../country-input/FlyCountrySelect.vue'
40
+
41
+ import './FlyPhoneNumberInput.scss'
42
+
43
+ const props = defineProps<{
44
+ modelValue?: string
45
+ }>()
46
+
47
+ const emit = defineEmits<{
48
+ (e: 'update:modelValue', value: string | undefined): void
49
+ (e: 'change', value: string | undefined): void
50
+ (e: 'blur'): void
51
+ (e: 'focus'): void
52
+ }>()
53
+
54
+ const countryCode = ref<CountryCode>()
55
+ const localInput = ref('')
56
+
57
+ const phoneMaxLength = computed(() => {
58
+ if (!countryCode.value) return 15
59
+
60
+ return getExampleNumber(countryCode.value, examples)?.nationalNumber.length ?? 15
61
+ })
62
+
63
+ const updateModel = () => {
64
+ if (!countryCode.value || !localInput.value.trim()) {
65
+ emit('update:modelValue', undefined)
66
+ return
67
+ }
68
+
69
+ const phone = parsePhoneNumberFromString(localInput.value, countryCode.value)
70
+
71
+ emit('update:modelValue', phone?.number)
72
+ }
73
+
74
+ const handleCountryChange = (value?: string) => {
75
+ countryCode.value = value as CountryCode | undefined
76
+ localInput.value = ''
77
+ emit('update:modelValue', undefined)
78
+ emit('change', undefined)
79
+ }
80
+
81
+ watch(
82
+ () => props.modelValue,
83
+ (value) => {
84
+ console.log(props.modelValue)
85
+ if (!value) {
86
+ localInput.value = ''
87
+ return
88
+ }
89
+
90
+ const phone = parsePhoneNumberFromString(value)
91
+
92
+ if (!phone?.country) return
93
+
94
+ countryCode.value = phone.country
95
+ localInput.value = phone.nationalNumber
96
+ },
97
+ { immediate: true }
98
+ )
99
+ </script>
@@ -0,0 +1,4 @@
1
+ import FlyPhoneNumberInput from './FlyPhoneNumberInput.vue'
2
+
3
+ export default FlyPhoneNumberInput
4
+ export { FlyPhoneNumberInput }
@@ -46,7 +46,7 @@ import { type ComponentPublicInstance, reactive, ref } from 'vue'
46
46
 
47
47
  import { ElCollapse, ElCollapseItem } from 'element-plus'
48
48
 
49
- import { UseLocaleInjector } from '../../../../composables/useLocaleInjector/UseLocaleInjector'
49
+ import { useLocaleInjector } from '../../../../composables/useLocaleInjector/useLocaleInjector'
50
50
  import '../../../../styles/element/overrides/collapse.scss'
51
51
  import FlyCollapseTitleRenderer from '../../../basic/typography/FlyCollapseTitleRenderer.vue'
52
52
  import type { T_FlyFormSchemaRendererInstance } from '../form-schema-renderer/FlyFormSchemaRenderer.types'
@@ -60,7 +60,7 @@ enum E_CollapsibleSections {
60
60
 
61
61
  defineProps<T_TenantRegionalFormProps>()
62
62
 
63
- const { t } = UseLocaleInjector()
63
+ const { t } = useLocaleInjector()
64
64
 
65
65
  const activeNames = ref<string | number>(E_CollapsibleSections.mainForm)
66
66
  const formRefs = reactive<{ [K: string]: T_FlyFormSchemaRendererInstance }>({})
@@ -1,8 +1,9 @@
1
1
  import type { FormItemRule } from 'element-plus'
2
2
 
3
+ import { isValidPhoneNumber } from 'libphonenumber-js'
3
4
  import { isNil } from 'lodash-es'
4
5
 
5
- import { UseLocaleInjector } from '../../composables/useLocaleInjector/UseLocaleInjector'
6
+ import { useLocaleInjector } from '../../composables/useLocaleInjector/useLocaleInjector'
6
7
  import { type checkInputRange, isRequiredError } from '../../utils/validators'
7
8
 
8
9
  import type { T_ValueUnitValue } from './input-renderers/value-unit/FlyValueUnit.types'
@@ -33,7 +34,7 @@ export const requiredFieldNumber = (
33
34
  languageKey = 'validation.field',
34
35
  trigger: InputTriggers = 'change'
35
36
  ): FormItemRule => {
36
- const { t } = UseLocaleInjector()
37
+ const { t } = useLocaleInjector()
37
38
 
38
39
  return {
39
40
  type: 'number',
@@ -97,3 +98,109 @@ export const requiredFieldArray = (
97
98
  trigger: trigger,
98
99
  message: useRequiredMessage ? isRequiredError(label) : label
99
100
  })
101
+
102
+ const emailRegexValidation = (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
103
+
104
+ export const requiredFieldEmail = (
105
+ label = 'validation.field',
106
+ trigger: InputTriggers = 'blur',
107
+ useRequiredMessage = true
108
+ ): FormItemRule => ({
109
+ required: true,
110
+ trigger: trigger,
111
+ message: useRequiredMessage ? isRequiredError(label) : label
112
+ })
113
+
114
+ export const fieldEmail = (
115
+ languageKey = 'validation.field',
116
+ trigger: InputTriggers = 'blur'
117
+ ): FormItemRule => {
118
+ const { t } = useLocaleInjector()
119
+
120
+ return {
121
+ trigger: trigger,
122
+ validator: (_rule, value, callback) => {
123
+ // used in element form validator
124
+ if (!value) return callback()
125
+
126
+ if (!emailRegexValidation(value)) {
127
+ return callback(
128
+ new Error(
129
+ t('validation.fieldMustBeValidEmail', {
130
+ fieldName: t(languageKey)
131
+ })
132
+ )
133
+ )
134
+ }
135
+
136
+ return callback()
137
+ }
138
+ }
139
+ }
140
+
141
+ export const requiredFieldEmailArray = (
142
+ label = 'validation.field',
143
+ trigger: InputTriggers = 'blur',
144
+ useRequiredMessage = true,
145
+ required = true
146
+ ): FormItemRule => {
147
+ const { t } = useLocaleInjector()
148
+
149
+ return {
150
+ required: required,
151
+ trigger: trigger,
152
+ validator: (_rule, value: string[], callback) => {
153
+ // not required
154
+ if (value.length === 0 && !required) {
155
+ return callback()
156
+ }
157
+
158
+ // required but empty data passed
159
+ if (value.length === 0) {
160
+ return callback(new Error(useRequiredMessage ? isRequiredError(label) : label))
161
+ }
162
+
163
+ const invalidEmails = value.filter((email) => !emailRegexValidation(email))
164
+
165
+ if (invalidEmails.length > 0) {
166
+ return callback(
167
+ new Error(
168
+ t('validation.theseEmailsMustBeValid', {
169
+ emails: invalidEmails.join(', ')
170
+ })
171
+ )
172
+ )
173
+ }
174
+
175
+ return callback()
176
+ }
177
+ }
178
+ }
179
+
180
+ export const requiredFieldE164PhoneNumber = (
181
+ label = 'validation.field',
182
+ trigger: InputTriggers = 'blur',
183
+ useRequiredMessage = true
184
+ ): FormItemRule => ({
185
+ required: true,
186
+ trigger: trigger,
187
+ message: useRequiredMessage ? isRequiredError(label) : label
188
+ })
189
+
190
+ export const fieldE164PhoneNumber = (trigger: InputTriggers = 'blur'): FormItemRule => {
191
+ const { t } = useLocaleInjector()
192
+
193
+ return {
194
+ trigger: trigger,
195
+ validator: (_rule, value: string, callback) => {
196
+ // used in element form validator
197
+ if (!value) return callback()
198
+
199
+ if (!isValidPhoneNumber(value)) {
200
+ return callback(new Error(t('validation.phoneNumberInvalid')))
201
+ }
202
+
203
+ return callback()
204
+ }
205
+ }
206
+ }
@@ -1,2 +1,2 @@
1
- export { initLocaleInjector, UseLocaleInjector } from './UseLocaleInjector'
2
- export type { T_LocaleInjector } from './UseLocaleInjector'
1
+ export { initLocaleInjector, useLocaleInjector } from './useLocaleInjector'
2
+ export type { T_LocaleInjector, T_I18nAdapter as I18nAdapter } from './useLocaleInjector'
@@ -0,0 +1,34 @@
1
+ import { computed, isRef } from 'vue'
2
+ import type { ComputedRef, MaybeRef } from 'vue'
3
+
4
+ import { FallbackTranslateFn } from '@flyanra/js-utils/constants'
5
+ import type { Locale } from '@flyanra/js-utils/locales'
6
+ import type { ComposerTranslation } from 'vue-i18n'
7
+
8
+ export type T_LocaleInjector = {
9
+ t: ComposerTranslation
10
+ locale: ComputedRef<Locale>
11
+ }
12
+
13
+ export type T_I18nAdapter = {
14
+ t: ComposerTranslation
15
+ locale: MaybeRef<Locale>
16
+ }
17
+
18
+ let _t: ComposerTranslation = FallbackTranslateFn
19
+ let _locale: ComputedRef<Locale> = computed(() => 'en')
20
+ let _initialized = false
21
+
22
+ export function initLocaleInjector(i18n: T_I18nAdapter): void {
23
+ _t = i18n.t
24
+ _locale = computed(() => (isRef(i18n.locale) ? i18n.locale.value : i18n.locale))
25
+ _initialized = true
26
+ }
27
+
28
+ export function useLocaleInjector(): T_LocaleInjector {
29
+ if (!_initialized) {
30
+ console.warn('[vue-core] useLocaleInjector: call initLocaleInjector() before use.')
31
+ }
32
+
33
+ return { t: _t, locale: _locale }
34
+ }
@@ -78,8 +78,6 @@
78
78
  }
79
79
 
80
80
  .el-select__wrapper {
81
- --el-fill-color-blank: var(--anra-bg-20);
82
-
83
81
  &.is-disabled {
84
82
  background-color: var(--anra-disabled-bg);
85
83
  }
@@ -0,0 +1,27 @@
1
+ const AdministrativeAreaLabels: Partial<Record<string, string>> = {
2
+ AE: 'address.administrativeAreaLabel.emirate',
3
+ GB: 'address.administrativeAreaLabel.county',
4
+ JP: 'address.administrativeAreaLabel.prefecture'
5
+ }
6
+
7
+ function getAdministrativeAreaLabel(countryCode?: string): string {
8
+ return (
9
+ AdministrativeAreaLabels[countryCode?.toUpperCase() ?? ''] ??
10
+ 'address.administrativeAreaLabel.stateOrProvince'
11
+ )
12
+ }
13
+
14
+ const nationalIdLabels: Partial<Record<string, string>> = {
15
+ AE: 'user.nationalIdLabels.emiratesId',
16
+ GB: 'user.nationalIdLabels.nationalInsuranceNumber',
17
+ US: 'user.nationalIdLabels.socialSecurityNumber',
18
+ NG: 'user.nationalIdLabels.nationalIdentificationNumber'
19
+ }
20
+
21
+ function getNationalIdLabel(countryCode?: string) {
22
+ return countryCode
23
+ ? (nationalIdLabels[countryCode] ?? 'user.nationalIdLabels.defaultLabel')
24
+ : 'user.nationalIdLabels.defaultLabel'
25
+ }
26
+
27
+ export { getAdministrativeAreaLabel, getNationalIdLabel }
@@ -1,2 +1,4 @@
1
1
  export { checkRange } from './pure-functions'
2
2
  export { checkInputRange, isRequiredError } from './validators'
3
+
4
+ export * from './country-utils'
@@ -1,11 +1,11 @@
1
1
  import type { FormItemRule } from 'element-plus'
2
2
 
3
- import { UseLocaleInjector } from '../composables/useLocaleInjector/UseLocaleInjector'
3
+ import { useLocaleInjector } from '../composables/useLocaleInjector/useLocaleInjector'
4
4
 
5
5
  import { checkRange } from './pure-functions'
6
6
 
7
7
  export const isRequiredError = (languageKey: string) => {
8
- const { t } = UseLocaleInjector()
8
+ const { t } = useLocaleInjector()
9
9
 
10
10
  return t('validation.fieldIsRequired', {
11
11
  fieldName: t(languageKey)
@@ -22,7 +22,7 @@ export const checkInputRange = (
22
22
  valueTypeLanguageKey: string = 'validation.valueTypes.characters',
23
23
  errorMessage?: string
24
24
  ): FormItemRule['validator'] => {
25
- const { t } = UseLocaleInjector()
25
+ const { t } = useLocaleInjector()
26
26
 
27
27
  // used in element form validator
28
28
  return (_rule: unknown, value: unknown, callback: (_?: string | Error | undefined) => void) => {
@@ -1,14 +0,0 @@
1
- import type { MaybeRef } from 'vue';
2
- import type { Locale } from '@flyanra/js-utils/locales';
3
- import type { ComposerTranslation } from 'vue-i18n';
4
- export type T_LocaleInjector = {
5
- t: ComposerTranslation;
6
- locale: Locale;
7
- };
8
- type I18nLike = {
9
- t: ComposerTranslation;
10
- locale: MaybeRef<Locale>;
11
- };
12
- export declare function initLocaleInjector(i18n: I18nLike): void;
13
- export declare function UseLocaleInjector(): T_LocaleInjector;
14
- export {};
@@ -1,37 +0,0 @@
1
- import { isRef } from 'vue'
2
- import type { MaybeRef } from 'vue'
3
-
4
- import { FallbackTranslateFn } from '@flyanra/js-utils/constants'
5
- import type { Locale } from '@flyanra/js-utils/locales'
6
- import type { ComposerTranslation } from 'vue-i18n'
7
-
8
- export type T_LocaleInjector = {
9
- t: ComposerTranslation
10
- locale: Locale
11
- }
12
-
13
- type I18nLike = {
14
- t: ComposerTranslation
15
- locale: MaybeRef<Locale>
16
- }
17
-
18
- let _t: ComposerTranslation = FallbackTranslateFn
19
- let _locale: MaybeRef<Locale> = 'en'
20
- let _initialized = false
21
-
22
- export function initLocaleInjector(i18n: I18nLike): void {
23
- _t = i18n.t
24
- _locale = i18n.locale
25
- _initialized = true
26
- }
27
-
28
- export function UseLocaleInjector(): T_LocaleInjector {
29
- if (!_initialized) {
30
- console.warn('[vue-core] UseLocaleInjector: call initLocaleInjector() before use.')
31
- }
32
-
33
- return {
34
- t: _t,
35
- locale: isRef(_locale) ? _locale.value : _locale
36
- }
37
- }