@cnamts/synapse 1.1.0 → 1.1.1
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/dist/{AutocompleteFilter-DXd4szWO.js → AutocompleteFilter-CGF33skz.js} +1 -1
- package/dist/{DateFilter-BD59Kgwf.js → DateFilter-D7-MsKtx.js} +1 -1
- package/dist/{NumberFilter-BSMZE7uw.js → NumberFilter-bjQPPfsj.js} +1 -1
- package/dist/{PeriodFilter-keUdSSk0.js → PeriodFilter-B3wJpK8-.js} +1 -1
- package/dist/{SelectFilter-Dhvvwazl.js → SelectFilter-BN6DbKAV.js} +1 -1
- package/dist/{TextFilter-CU8FpXz0.js → TextFilter-BffP0J2f.js} +1 -1
- package/dist/{apLightTheme2026-DbS7BPUf.js → apLightTheme2026-C4ygwMHC.js} +11 -11
- package/dist/components/Amelipro/AmeliproAutoCompleteField/AmeliproAutoCompleteField.d.ts +6 -6
- package/dist/components/Amelipro/AmeliproSelect/AmeliproSelect.d.ts +6 -6
- package/dist/components/Amelipro/AmeliproTabs/AmeliproTabs.d.ts +6 -6
- package/dist/components/Captcha/Captcha.d.ts +27 -16
- package/dist/components/Captcha/CaptchaForm.d.ts +29 -3
- package/dist/components/Captcha/types.d.ts +14 -0
- package/dist/components/Captcha/useCaptchaValidation.d.ts +37 -0
- package/dist/components/Customs/Selects/SelectBtnField/SelectBtnField.d.ts +33 -13
- package/dist/components/Customs/Selects/SelectBtnField/composables/useSelectBtnFieldValidation.d.ts +23 -0
- package/dist/components/Customs/Selects/SyAutocomplete/composables/useSyAutocompleteValidation.d.ts +2 -2
- package/dist/components/Customs/Selects/SySelect/composables/useSySelectValidation.d.ts +2 -2
- package/dist/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.d.ts +17 -48
- package/dist/components/Customs/SyCheckBoxGroup/composables/useSyCheckBoxGroupValidation.d.ts +29 -0
- package/dist/components/Customs/SyCheckBoxGroup/types.d.ts +46 -0
- package/dist/components/Customs/SyCheckbox/SyCheckbox.d.ts +16 -51
- package/dist/components/Customs/SyCheckbox/composables/useSyCheckboxValidation.d.ts +27 -0
- package/dist/components/Customs/SyCheckbox/types.d.ts +49 -0
- package/dist/components/Customs/SyTextField/FieldState.d.ts +5 -0
- package/dist/components/Customs/SyTextField/useSyTextFieldValidation.d.ts +3 -3
- package/dist/components/DialogBox/DialogBox.d.ts +2 -0
- package/dist/components/DialogBox/locales.d.ts +1 -0
- package/dist/components/FilterSideBar/FilterSideBar.d.ts +4 -0
- package/dist/components/LunarCalendar/LunarCalendar.d.ts +43 -14
- package/dist/components/LunarCalendar/types.d.ts +35 -0
- package/dist/components/LunarCalendar/useLunarCalendarValidation.d.ts +11 -12
- package/dist/components/MonthPicker/MonthPicker.d.ts +72 -1747
- package/dist/components/MonthPicker/MonthPickerText/MonthPickerInput.d.ts +21 -1733
- package/dist/components/MonthPicker/MonthPickerText/useTextField.d.ts +5 -0
- package/dist/components/MonthPicker/locales.d.ts +1 -0
- package/dist/components/MonthPicker/types.d.ts +11 -0
- package/dist/components/MonthPicker/useMonthPickerValidation.d.ts +37 -24
- package/dist/components/NirField/NirField.d.ts +6 -4
- package/dist/components/NirField/useNirValidation.d.ts +7 -5
- package/dist/components/PageContainer/PageContainer.d.ts +8 -0
- package/dist/components/PasswordField/PasswordField.d.ts +2 -2
- package/dist/components/PasswordField/usePasswordFieldValidation.d.ts +2 -2
- package/dist/components/PhoneField/PhoneField.d.ts +960 -1938
- package/dist/components/PhoneField/indicatifs.d.ts +715 -8
- package/dist/components/PhoneField/locales.d.ts +7 -0
- package/dist/components/PhoneField/types.d.ts +29 -0
- package/dist/components/PhoneField/usePhoneFieldValidation.d.ts +45 -0
- package/dist/components/PhoneField/usePhoneIndicatifs.d.ts +947 -0
- package/dist/components/SyTextArea/composables/useSyTextAreaValidation.d.ts +2 -2
- package/dist/composables/unifyValidation/documentationValidationProps.d.ts +1 -1
- package/dist/composables/unifyValidation/useValidation.d.ts +4 -5
- package/dist/design-system-v3.js +2 -2
- package/dist/designTokens/tokens/amelipro/apLightTheme.d.ts +10 -10
- package/dist/designTokens/tokens/baseTokens.d.ts +18 -18
- package/dist/designTokens/tokens/cnam/cnamLightTheme.d.ts +10 -10
- package/dist/designTokens/tokens/pa/paLightTheme.d.ts +10 -10
- package/dist/designTokens/tokens/semanticTokens.d.ts +14 -14
- package/dist/{main-D8ryUoS5.js → main-C4wAktOs.js} +13718 -12991
- package/dist/synapse.css +1 -1
- package/dist/vuetifyConfig.js +1 -1
- package/package.json +7 -7
- package/src/assets/compat/_legacy-tokens.scss +91 -0
- package/src/assets/overrides/_utilities.scss +23 -0
- package/src/components/Accordion/Accordion.stories.ts +121 -1
- package/src/components/BackBtn/BackBtn.mdx +1 -1
- package/src/components/BackToTopBtn/BackToTopBtn.mdx +0 -1
- package/src/components/Captcha/Captcha.stories.ts +134 -31
- package/src/components/Captcha/Captcha.vue +95 -28
- package/src/components/Captcha/CaptchaForm.vue +51 -22
- package/src/components/Captcha/tests/Captcha.focus.spec.ts +214 -0
- package/src/components/Captcha/tests/Captcha.spec.ts +233 -24
- package/src/components/Captcha/tests/CaptchaForm.spec.ts +82 -0
- package/src/components/Captcha/tests/__snapshots__/Captcha.spec.ts.snap +16 -42
- package/src/components/Captcha/types.ts +15 -0
- package/src/components/Captcha/useCaptchaValidation.ts +87 -0
- package/src/components/Captcha/validation/validation.stories.ts +1194 -0
- package/src/components/ChipList/ChipList.mdx +0 -1
- package/src/components/CollapsibleList/CollapsibleList.mdx +0 -1
- package/src/components/CookieBanner/CookieBanner.mdx +0 -1
- package/src/components/CopyBtn/CopyBtn.mdx +0 -1
- package/src/components/Customs/Selects/SelectBtnField/SelectBtnField.stories.ts +123 -439
- package/src/components/Customs/Selects/SelectBtnField/SelectBtnField.vue +147 -41
- package/src/components/Customs/Selects/SelectBtnField/Validation/Validation.stories.ts +600 -0
- package/src/components/Customs/Selects/SelectBtnField/composables/useSelectBtnFieldValidation.ts +87 -0
- package/src/components/Customs/Selects/SelectBtnField/tests/SelectBtnField.spec.ts +402 -33
- package/src/components/Customs/Selects/SelectBtnField/tests/__snapshots__/SelectBtnField.spec.ts.snap +52 -38
- package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.stories.ts +342 -162
- package/src/components/Customs/SyCheckBoxGroup/SyCheckBoxGroup.vue +77 -129
- package/src/components/Customs/SyCheckBoxGroup/Validation/Validation.stories.ts +1008 -0
- package/src/components/Customs/SyCheckBoxGroup/composables/useSyCheckBoxGroupValidation.ts +107 -0
- package/src/components/Customs/SyCheckBoxGroup/tests/SyCheckBoxGroup.spec.ts +180 -7
- package/src/components/Customs/SyCheckBoxGroup/types.ts +49 -0
- package/src/components/Customs/SyCheckbox/SyCheckbox.stories.ts +41 -161
- package/src/components/Customs/SyCheckbox/SyCheckbox.vue +71 -148
- package/src/components/Customs/SyCheckbox/Validation/Validation.stories.ts +654 -0
- package/src/components/Customs/SyCheckbox/composables/useSyCheckboxValidation.ts +105 -0
- package/src/components/Customs/SyCheckbox/tests/SyCheckbox.spec.ts +106 -0
- package/src/components/Customs/SyCheckbox/tests/useSyCheckboxValidation.spec.ts +98 -0
- package/src/components/Customs/SyCheckbox/types.ts +51 -0
- package/src/components/Customs/SyTextField/FieldState.vue +50 -0
- package/src/components/Customs/SyTextField/SyTextField.vue +12 -9
- package/src/components/Customs/SyTextField/useSyTextFieldValidation.ts +2 -11
- package/src/components/DataList/DataList.mdx +0 -1
- package/src/components/DataListGroup/DataListGroup.mdx +0 -1
- package/src/components/DiacriticPicker/DiacriticPicker.mdx +0 -1
- package/src/components/DialogBox/DialogBox.mdx +0 -1
- package/src/components/DialogBox/DialogBox.stories.ts +399 -4
- package/src/components/DialogBox/DialogBox.vue +20 -0
- package/src/components/DialogBox/locales.ts +1 -0
- package/src/components/DialogBox/tests/DialogBox.spec.ts +73 -0
- package/src/components/DialogBox/tests/DialogBox.visual.cy.ts +24 -0
- package/src/components/ErrorPage/ErrorPage.mdx +1 -1
- package/src/components/ExternalLinks/ExternalLinks.mdx +0 -1
- package/src/components/FileList/FileList.mdx +0 -1
- package/src/components/FilterInline/FilterInline.mdx +0 -1
- package/src/components/FilterSideBar/FilterSideBar.mdx +8 -1
- package/src/components/FilterSideBar/FilterSideBar.stories.ts +133 -1
- package/src/components/FilterSideBar/FilterSideBar.vue +19 -2
- package/src/components/FilterSideBar/tests/FilterSideBar.spec.ts +55 -0
- package/src/components/FooterBar/FooterBar.mdx +0 -1
- package/src/components/FranceConnectBtn/FranceConnectBtn.mdx +0 -1
- package/src/components/HeaderBar/HeaderBar.mdx +0 -1
- package/src/components/HeaderLoading/HeaderLoading.mdx +0 -1
- package/src/components/LangBtn/LangBtn.mdx +0 -1
- package/src/components/Logo/Logo.mdx +1 -1
- package/src/components/LunarCalendar/LunarCalendar.mdx +6 -9
- package/src/components/LunarCalendar/LunarCalendar.stories.ts +243 -46
- package/src/components/LunarCalendar/LunarCalendar.vue +61 -26
- package/src/components/LunarCalendar/Validation/Validation.stories.ts +717 -0
- package/src/components/LunarCalendar/tests/LunarCalendar.a11y.spec.ts +1 -1
- package/src/components/LunarCalendar/tests/LunarCalendar.spec.ts +197 -6
- package/src/components/LunarCalendar/tests/useLunarCalendarValidation.spec.ts +287 -0
- package/src/components/LunarCalendar/types.ts +39 -0
- package/src/components/LunarCalendar/useLunarCalendarValidation.ts +115 -39
- package/src/components/MonthPicker/MonthPicker.stories.ts +38 -281
- package/src/components/MonthPicker/MonthPicker.vue +66 -17
- package/src/components/MonthPicker/MonthPickerText/MonthPickerInput.vue +44 -20
- package/src/components/MonthPicker/MonthPickerText/useTextField.ts +5 -0
- package/src/components/MonthPicker/Validation/Validation.stories.ts +1117 -0
- package/src/components/MonthPicker/locales.ts +1 -0
- package/src/components/MonthPicker/tests/MonthPicker.spec.ts +353 -2
- package/src/components/MonthPicker/tests/__snapshots__/MonthPicker.spec.ts.snap +12 -8
- package/src/components/MonthPicker/types.ts +16 -0
- package/src/components/MonthPicker/useMonthPickerValidation.ts +64 -27
- package/src/components/NirField/NirField.mdx +120 -66
- package/src/components/NirField/NirField.stories.ts +216 -0
- package/src/components/NirField/useNirValidation.ts +16 -17
- package/src/components/NotFoundPage/tests/__snapshots__/NotFoundPage.spec.ts.snap +263 -245
- package/src/components/NotificationBar/NotificationBar.mdx +0 -1
- package/src/components/PageContainer/PageContainer.mdx +0 -1
- package/src/components/PageContainer/PageContainer.stories.ts +170 -2
- package/src/components/PageContainer/PageContainer.vue +63 -8
- package/src/components/PageContainer/tests/__snapshots__/PageContainer.spec.ts.snap +19 -11
- package/src/components/PaginatedTable/PaginatedTable.mdx +0 -1
- package/src/components/PeriodField/PeriodField.mdx +0 -1
- package/src/components/PhoneField/PhoneField.mdx +2 -3
- package/src/components/PhoneField/PhoneField.stories.ts +227 -410
- package/src/components/PhoneField/PhoneField.vue +204 -438
- package/src/components/PhoneField/indicatifs.ts +1 -1
- package/src/components/PhoneField/locales.ts +7 -0
- package/src/components/PhoneField/tests/PhoneField.a11y.spec.ts +0 -1
- package/src/components/PhoneField/tests/PhoneField.spec.ts +517 -220
- package/src/components/PhoneField/types.ts +30 -0
- package/src/components/PhoneField/usePhoneFieldValidation.ts +119 -0
- package/src/components/PhoneField/usePhoneIndicatifs.ts +89 -0
- package/src/components/PhoneField/validation/validation.stories.ts +717 -0
- package/src/components/RangeField/RangeField.mdx +0 -1
- package/src/components/RatingPicker/RatingPicker.mdx +0 -1
- package/src/components/SocialMediaLinks/SocialMediaLinks.mdx +0 -1
- package/src/components/StatusPage/StatusPage.vue +1 -0
- package/src/components/StatusPage/tests/__snapshots__/StatusPage.spec.ts.snap +248 -230
- package/src/components/SubHeader/SubHeader.mdx +5 -6
- package/src/components/Tables/common/tests/SyTableFilter.spec.ts +11 -12
- package/src/components/UploadWorkflow/UploadWorkflow.mdx +0 -1
- package/src/components/UserMenuBtn/UserMenuBtn.mdx +0 -1
- package/src/components/UserMenuBtn/UserMenuBtn.stories.ts +177 -0
- package/src/composables/unifyValidation/documentationValidationProps.ts +1 -1
- package/src/composables/unifyValidation/tests/useValidation.spec.ts +13 -1
- package/src/composables/unifyValidation/useValidation.ts +37 -33
- package/src/composantsVuetify/VCard/VCard.mdx +4 -0
- package/src/composantsVuetify/VCard/v-card.stories.ts +93 -1
- package/src/composantsVuetify/VCarousel/VCarousel.mdx +74 -0
- package/src/composantsVuetify/VCarousel/v-carousel.stories.ts +531 -0
- package/src/composantsVuetify/VNavigationDrawer/VNavgationDrawer.mdx +53 -0
- package/src/composantsVuetify/VNavigationDrawer/v-navigation-drawer.stories.ts +310 -0
- package/src/composantsVuetify/VSlideGroup/VSlideGroup.mdx +105 -0
- package/src/composantsVuetify/VSlideGroup/v-slide-group.stories.ts +463 -0
- package/src/designTokens/tokens/baseColors.ts +1 -1
- package/src/designTokens/tokens/baseTokens.ts +18 -18
- package/src/stories/Components/Components.stories.ts +34 -1
- package/src/stories/Demarrer/Releases.stories.ts +16 -2
- package/src/stories/DesignTokens/Arrondis.mdx +1 -1
- package/src/stories/DesignTokens/Correspondances.mdx +219 -0
- package/src/stories/DesignTokens/UtiliserLesTokens.mdx +235 -0
- package/src/stories/DesignTokens/colors.stories.ts +569 -569
- package/src/stories/GuideDuDev/Amelipro.stories.ts +335 -267
- package/dist/components/LunarCalendar/useLunarCalendarRules.d.ts +0 -5
- package/dist/components/PhoneField/tests/types.d.ts +0 -18
- package/src/components/LunarCalendar/tests/useLunarCalendarRules.spec.ts +0 -184
- package/src/components/LunarCalendar/useLunarCalendarRules.ts +0 -96
- package/src/components/PhoneField/tests/types.d.ts +0 -19
|
@@ -1,426 +1,161 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { computed, ref,
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { indicatifs } from './indicatifs'
|
|
6
|
-
import { Mask } from 'maska'
|
|
7
|
-
import { locales } from './locales'
|
|
2
|
+
import { computed, ref, toRef, watch } from 'vue'
|
|
3
|
+
import { mdiPhone, mdiCloseCircle } from '@mdi/js'
|
|
4
|
+
import { locales as defaultLocales } from './locales'
|
|
8
5
|
import SySelect from '@/components/Customs/Selects/SySelect/SySelect.vue'
|
|
9
6
|
import SyTextField from '@/components/Customs/SyTextField/SyTextField.vue'
|
|
10
7
|
import SyIcon from '@/components/Customs/SyIcon/SyIcon.vue'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
displayAsterisk: { type: Boolean, default: false },
|
|
38
|
-
disableErrorHandling: { type: Boolean, default: false },
|
|
39
|
-
showSuccessMessages: { type: Boolean, default: false },
|
|
40
|
-
bgColor: { type: String, default: 'white' },
|
|
41
|
-
readonly: { type: Boolean, default: false },
|
|
42
|
-
disabled: { type: Boolean, default: false },
|
|
43
|
-
helpText: { type: String, default: '' },
|
|
44
|
-
autocompleteCountryCode: { type: String, default: 'tel-country-code' },
|
|
45
|
-
autocompletePhone: { type: String, default: 'tel-national' },
|
|
46
|
-
withoutFieldset: { type: Boolean, default: false },
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
const emit = defineEmits(['update:modelValue', 'update:selectedDialCode', 'change'])
|
|
50
|
-
|
|
51
|
-
const phoneNumber = ref(props.modelValue || '')
|
|
52
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- This is a generic type
|
|
53
|
-
const dialCode = ref<string | Record<string, any>>(props.dialCodeModel || '')
|
|
54
|
-
// Force re-render of SySelect when needed (e.g., after reset)
|
|
55
|
-
const dialSelectKey = ref(0)
|
|
56
|
-
const counter = ref(10)
|
|
57
|
-
const phoneMask = ref('## ## ## ## ##')
|
|
58
|
-
const onBlur = ref(false)
|
|
59
|
-
|
|
60
|
-
const buildDefaultMask = (length: number): string =>
|
|
61
|
-
'#'.repeat(length || 10).replace(/(.{2})/g, '$1 ').trim()
|
|
62
|
-
|
|
63
|
-
const toTrimmedDigits = (value: string, maxDigits: number): string => {
|
|
64
|
-
return value.replace(/\D/g, '').slice(0, maxDigits)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Cache the Mask instance — recreated only when phoneMask changes, not on every keystroke
|
|
68
|
-
const maskInstance = computed(() => new Mask({ mask: phoneMask.value }))
|
|
69
|
-
const applyMask = (digits: string): string => maskInstance.value.masked(digits)
|
|
70
|
-
|
|
71
|
-
// phoneNumber is always masked, so this is just a stable public alias
|
|
72
|
-
const computedValue = computed(() => phoneNumber.value)
|
|
73
|
-
|
|
74
|
-
watch(() => props.modelValue, (newVal) => {
|
|
75
|
-
if (newVal) {
|
|
76
|
-
// Apply mask to incoming value to ensure consistent formatting
|
|
77
|
-
const digits = toTrimmedDigits(newVal, counter.value)
|
|
78
|
-
phoneNumber.value = applyMask(digits)
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
phoneNumber.value = ''
|
|
82
|
-
}
|
|
83
|
-
}, { immediate: true })
|
|
84
|
-
|
|
85
|
-
const isIndicatifLike = (value: unknown): value is Indicatif => {
|
|
86
|
-
return typeof value === 'object'
|
|
87
|
-
&& value !== null
|
|
88
|
-
&& 'code' in value
|
|
89
|
-
&& 'phoneLength' in value
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
watch(dialCode, async (newVal) => {
|
|
93
|
-
// Storybook / composants parents peuvent fournir un objet indicatif "stale" (mask/phoneLength obsolètes).
|
|
94
|
-
// On normalise donc TOUJOURS l'indicatif à partir de la liste dialCodeOptions
|
|
95
|
-
const dialCodeValue = typeof newVal === 'object' && newVal !== null
|
|
96
|
-
? newVal.code
|
|
97
|
-
: newVal
|
|
98
|
-
const resolvedDialCode = dialCodeOptions.value.find(opt => opt.code === dialCodeValue)
|
|
99
|
-
?? (isIndicatifLike(newVal) ? newVal : null)
|
|
100
|
-
const placeholdersCount = (resolvedDialCode?.mask?.match(/#/g) || []).length
|
|
101
|
-
const normalizedDialCode = resolvedDialCode
|
|
102
|
-
? {
|
|
103
|
-
...resolvedDialCode,
|
|
104
|
-
// Si le mask n'est pas cohérent avec phoneLength (ex: 10 placeholders mais phoneLength=9),
|
|
105
|
-
// on reconstruit un mask de secours à partir de phoneLength.
|
|
106
|
-
mask: resolvedDialCode.mask && placeholdersCount === resolvedDialCode.phoneLength
|
|
107
|
-
? resolvedDialCode.mask
|
|
108
|
-
: buildDefaultMask(resolvedDialCode.phoneLength),
|
|
109
|
-
}
|
|
110
|
-
: null
|
|
111
|
-
|
|
112
|
-
emit('update:selectedDialCode', normalizedDialCode ?? newVal)
|
|
113
|
-
|
|
114
|
-
if (normalizedDialCode) {
|
|
115
|
-
counter.value = normalizedDialCode.phoneLength || 10
|
|
116
|
-
phoneMask.value = normalizedDialCode.mask || buildDefaultMask(normalizedDialCode.phoneLength)
|
|
117
|
-
const digits = toTrimmedDigits(phoneNumber.value, counter.value)
|
|
118
|
-
const maskedValue = applyMask(digits)
|
|
119
|
-
phoneNumber.value = maskedValue
|
|
120
|
-
emit('update:modelValue', maskedValue)
|
|
121
|
-
|
|
122
|
-
await nextTick()
|
|
123
|
-
await nextTick()
|
|
124
|
-
|
|
125
|
-
// Le changement d'indicatif modifie les règles (longueur attendue), donc on revalide immédiatement
|
|
126
|
-
// si une valeur est déjà saisie. Objectif: messages à jour sans nécessiter un nouveau blur.
|
|
127
|
-
if (!shouldDisableErrorHandling.value && phoneNumber.value) {
|
|
128
|
-
onBlur.value = true
|
|
129
|
-
runValidation()
|
|
130
|
-
}
|
|
131
|
-
}
|
|
8
|
+
import { validationPropsDefaults } from '@/composables/unifyValidation/useValidation'
|
|
9
|
+
import type { PhoneFieldProps } from './types'
|
|
10
|
+
import { usePhoneIndicatifs } from './usePhoneIndicatifs'
|
|
11
|
+
import { vMaska } from 'maska/vue'
|
|
12
|
+
import { Mask } from 'maska'
|
|
13
|
+
import type { Indicatif } from './types'
|
|
14
|
+
import { usePhoneFieldValidation } from './usePhoneFieldValidation'
|
|
15
|
+
import FieldState from '@/components/Customs/SyTextField/FieldState.vue'
|
|
16
|
+
|
|
17
|
+
const props = withDefaults(defineProps<PhoneFieldProps>(), {
|
|
18
|
+
...validationPropsDefaults,
|
|
19
|
+
modelValue: '',
|
|
20
|
+
dialCodeModel: '',
|
|
21
|
+
outlined: true,
|
|
22
|
+
withCountryCode: false,
|
|
23
|
+
displayFormat: 'code',
|
|
24
|
+
customIndicatifs: () => [],
|
|
25
|
+
useCustomIndicatifsOnly: false,
|
|
26
|
+
displayAsterisk: true,
|
|
27
|
+
bgColor: 'white',
|
|
28
|
+
helpText: '',
|
|
29
|
+
autocompleteCountryCode: 'tel-country-code',
|
|
30
|
+
autocompletePhone: 'tel-national',
|
|
31
|
+
withoutFieldset: false,
|
|
32
|
+
isClearable: false,
|
|
33
|
+
locales: () => defaultLocales,
|
|
132
34
|
})
|
|
133
35
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// Parcours la valeur masquée pour trouver la position qui contient le même nombre de caractères non-espace
|
|
146
|
-
let newPosition = 0
|
|
147
|
-
let digitCount = 0
|
|
148
|
-
|
|
149
|
-
for (let i = 0; i < maskedValue.length; i++) {
|
|
150
|
-
if (maskedValue[i] !== ' ') {
|
|
151
|
-
digitCount++
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (digitCount > digitsBeforeCursor) {
|
|
155
|
-
break
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
newPosition = i + 1
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return newPosition
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const handlePhoneInput = (event: Event) => {
|
|
165
|
-
const inputElement = event.target as HTMLInputElement
|
|
166
|
-
const input = inputElement.value
|
|
167
|
-
|
|
168
|
-
// Sauvegarder la position du curseur
|
|
169
|
-
const cursorPosition = inputElement.selectionStart || 0
|
|
170
|
-
|
|
171
|
-
// Appliquer le masque (en tronquant au nombre de chiffres attendu)
|
|
172
|
-
const digits = toTrimmedDigits(input, counter.value)
|
|
173
|
-
const maskedValue = applyMask(digits)
|
|
174
|
-
|
|
175
|
-
// Mettre à jour la valeur
|
|
176
|
-
phoneNumber.value = maskedValue
|
|
177
|
-
emit('update:modelValue', maskedValue)
|
|
178
|
-
|
|
179
|
-
// Restaurer la position du curseur sur le prochain cycle de rendu
|
|
180
|
-
nextTick(() => {
|
|
181
|
-
const adjustedPosition = calculateAdjustedPosition(cursorPosition, input, maskedValue)
|
|
182
|
-
inputElement.setSelectionRange(adjustedPosition, adjustedPosition)
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const handlePhoneModelUpdate = (value: string | number | null) => {
|
|
187
|
-
const digits = toTrimmedDigits(String(value ?? ''), counter.value)
|
|
188
|
-
const maskedValue = applyMask(digits)
|
|
189
|
-
phoneNumber.value = maskedValue
|
|
190
|
-
emit('update:modelValue', maskedValue)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const handlePhoneKeydown = (event: KeyboardEvent) => {
|
|
194
|
-
if (counter.value <= 0) return
|
|
195
|
-
if (!event.key || !/\d/.test(event.key)) return
|
|
196
|
-
|
|
197
|
-
const inputElement = event.target as HTMLInputElement | null
|
|
198
|
-
const selectionStart = inputElement?.selectionStart ?? null
|
|
199
|
-
const selectionEnd = inputElement?.selectionEnd ?? null
|
|
200
|
-
const hasSelection
|
|
201
|
-
= selectionStart !== null
|
|
202
|
-
&& selectionEnd !== null
|
|
203
|
-
&& selectionEnd > selectionStart
|
|
204
|
-
|
|
205
|
-
const currentDigitsCount = phoneNumber.value.replace(/\D/g, '').length
|
|
206
|
-
if (currentDigitsCount >= counter.value && !hasSelection) {
|
|
207
|
-
event.preventDefault()
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const mergedDialCodes = computed(() =>
|
|
212
|
-
props.useCustomIndicatifsOnly ? props.customIndicatifs : [...indicatifs, ...props.customIndicatifs],
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
const dialCodeOptions = computed(() =>
|
|
216
|
-
mergedDialCodes.value.map(ind => ({
|
|
217
|
-
...ind,
|
|
218
|
-
displayText: generateDisplayText(ind),
|
|
219
|
-
plainDisplayText: generateDisplayText(ind, true),
|
|
220
|
-
})),
|
|
36
|
+
const emits = defineEmits<{
|
|
37
|
+
'update:modelValue': [value: string]
|
|
38
|
+
'update:dialCodeModel': [value: Indicatif | string | undefined]
|
|
39
|
+
}>()
|
|
40
|
+
|
|
41
|
+
const phoneNumber = ref<string>(props.modelValue)
|
|
42
|
+
const { internalDialCode, dialCodeList } = usePhoneIndicatifs(
|
|
43
|
+
toRef(props, 'dialCodeModel'),
|
|
44
|
+
toRef(props, 'displayFormat'),
|
|
45
|
+
toRef(props, 'customIndicatifs'),
|
|
46
|
+
toRef(props, 'useCustomIndicatifsOnly'),
|
|
221
47
|
)
|
|
222
48
|
|
|
223
|
-
watch(
|
|
224
|
-
|
|
225
|
-
runValidation()
|
|
226
|
-
}
|
|
49
|
+
watch (phoneNumber, (newVal) => {
|
|
50
|
+
emits('update:modelValue', newVal)
|
|
227
51
|
})
|
|
228
52
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
// Watcher pour initialiser dialCode à partir de props.dialCodeModel
|
|
233
|
-
watch(() => props.dialCodeModel, (newVal) => {
|
|
234
|
-
if (!newVal) {
|
|
235
|
-
// Par défaut, pré-sélectionner la France (+33) quand l'indicatif est activé
|
|
236
|
-
dialCode.value = props.withCountryCode ? getFranceDefault() : ''
|
|
237
|
-
return
|
|
53
|
+
watch (() => props.modelValue, (newVal) => {
|
|
54
|
+
if (newVal !== phoneNumber.value) {
|
|
55
|
+
phoneNumber.value = newVal || ''
|
|
238
56
|
}
|
|
239
|
-
|
|
240
|
-
if (typeof newVal === 'object') {
|
|
241
|
-
const matchingOption = dialCodeOptions.value.find(opt => opt.code === newVal.code)
|
|
242
|
-
dialCode.value = matchingOption ?? newVal
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
dialCode.value = newVal
|
|
246
|
-
}
|
|
247
|
-
}, { immediate: true })
|
|
248
|
-
|
|
249
|
-
function generateDisplayText(ind: Indicatif, plain = false): string {
|
|
250
|
-
const countryName = ind.countryFr || ind.country
|
|
251
|
-
const abbr = plain ? ind.abbreviation : `<abbr title="${countryName}">${ind.abbreviation}</abbr>`
|
|
252
|
-
const format: Record<DisplayFormat, string> = {
|
|
253
|
-
'code': ind.code,
|
|
254
|
-
'code-abbreviation': `${ind.code} (${abbr})`,
|
|
255
|
-
'code-country': `${ind.code} ${countryName}`,
|
|
256
|
-
'country': countryName,
|
|
257
|
-
'abbreviation': abbr,
|
|
258
|
-
}
|
|
259
|
-
return format[props.displayFormat] ?? ind.code
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const phoneFieldIdentifier = computed(() => props.withCountryCode
|
|
263
|
-
? locales.phoneNumberWithoutCountryLabel
|
|
264
|
-
: locales.label,
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
const validationRules = computed<ValidationRule[]>(() => {
|
|
268
|
-
const rules = [{
|
|
269
|
-
type: 'exactLength',
|
|
270
|
-
options: {
|
|
271
|
-
length: counter.value,
|
|
272
|
-
ignoreSpace: true,
|
|
273
|
-
message: `Le numéro de téléphone doit contenir ${counter.value} chiffres.`,
|
|
274
|
-
successMessage: `Le champ ${phoneFieldIdentifier.value} est valide.`,
|
|
275
|
-
fieldIdentifier: phoneFieldIdentifier.value,
|
|
276
|
-
},
|
|
277
|
-
}] as ValidationRule[]
|
|
278
|
-
|
|
279
|
-
if (props.required) {
|
|
280
|
-
rules.unshift({
|
|
281
|
-
type: 'required',
|
|
282
|
-
options: {
|
|
283
|
-
length: counter.value,
|
|
284
|
-
ignoreSpace: true,
|
|
285
|
-
message: `Le champ ${phoneFieldIdentifier.value} est requis.`,
|
|
286
|
-
fieldIdentifier: phoneFieldIdentifier.value,
|
|
287
|
-
},
|
|
288
|
-
})
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return rules
|
|
292
57
|
})
|
|
293
58
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
// When disabling error handling, immediately clear any existing validation state
|
|
297
|
-
watch(shouldDisableErrorHandling, (disabled) => {
|
|
298
|
-
if (disabled) {
|
|
299
|
-
validation.clearValidation()
|
|
300
|
-
}
|
|
59
|
+
watch (internalDialCode, (newVal) => {
|
|
60
|
+
emits('update:dialCodeModel', newVal)
|
|
301
61
|
})
|
|
302
62
|
|
|
303
|
-
const validation = useValidation({
|
|
304
|
-
showSuccessMessages: props.showSuccessMessages,
|
|
305
|
-
disableErrorHandling: shouldDisableErrorHandling.value,
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
const hasError = computed(() => !shouldDisableErrorHandling.value && validation.hasError.value)
|
|
309
|
-
const hasWarning = computed(() => !shouldDisableErrorHandling.value && validation.hasWarning.value)
|
|
310
|
-
const hasSuccess = computed(() =>
|
|
311
|
-
!shouldDisableErrorHandling.value
|
|
312
|
-
&& !hasError.value
|
|
313
|
-
&& !hasWarning.value
|
|
314
|
-
&& validation.hasSuccess.value,
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
const iconColor = computed(() => {
|
|
318
|
-
if (shouldDisableErrorHandling.value) return '#222324'
|
|
319
|
-
if (hasError.value) return 'error'
|
|
320
|
-
if (hasWarning.value) return 'warning'
|
|
321
|
-
if (hasSuccess.value) return 'success'
|
|
322
|
-
return '#222324'
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
const errors = computed(() => shouldDisableErrorHandling.value ? [] : validation.errors.value)
|
|
326
|
-
const warnings = computed(() => shouldDisableErrorHandling.value ? [] : validation.warnings.value)
|
|
327
|
-
const successes = computed(() =>
|
|
328
|
-
shouldDisableErrorHandling.value || hasError.value || hasWarning.value
|
|
329
|
-
? []
|
|
330
|
-
: validation.displaySuccesses.value,
|
|
331
|
-
)
|
|
332
|
-
|
|
333
63
|
const showHelpTextBelow = computed(() => !!props.helpText?.trim())
|
|
64
|
+
const focused = ref(false)
|
|
334
65
|
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
66
|
+
const {
|
|
67
|
+
errors,
|
|
68
|
+
warnings,
|
|
69
|
+
successes,
|
|
70
|
+
hasError,
|
|
71
|
+
hasWarning,
|
|
72
|
+
hasSuccess,
|
|
73
|
+
state,
|
|
74
|
+
iconColor,
|
|
75
|
+
validate,
|
|
76
|
+
clearValidation,
|
|
77
|
+
} = usePhoneFieldValidation({
|
|
78
|
+
modelValue: phoneNumber,
|
|
79
|
+
readonly: toRef(props, 'readonly'),
|
|
80
|
+
disabled: toRef(props, 'disabled'),
|
|
81
|
+
required: toRef(props, 'required'),
|
|
82
|
+
counter: computed(() => internalDialCode.value.phoneLength || 10),
|
|
83
|
+
phoneFieldIdentifier: computed(() => props.withCountryCode ? props.locales?.phoneNumberWithoutCountryLabel || 'Numéro de téléphone' : props.locales?.label || 'Téléphone'),
|
|
84
|
+
shouldDisableErrorHandling: computed(() => props.disableErrorHandling || props.readonly),
|
|
85
|
+
hasError: toRef(props, 'hasError'),
|
|
86
|
+
hasWarning: toRef(props, 'hasWarning'),
|
|
87
|
+
hasSuccess: toRef(props, 'hasSuccess'),
|
|
88
|
+
showSuccessMessages: toRef(props, 'showSuccessMessages'),
|
|
89
|
+
disableErrorHandling: toRef(props, 'disableErrorHandling'),
|
|
90
|
+
isValidateOnBlur: toRef(props, 'isValidateOnBlur'),
|
|
91
|
+
focused,
|
|
92
|
+
customRules: toRef(props, 'customRules'),
|
|
93
|
+
warningRules: toRef(props, 'customWarningRules'),
|
|
94
|
+
successRules: toRef(props, 'customSuccessRules'),
|
|
95
|
+
rules: toRef(props, 'rules'),
|
|
96
|
+
errorMessages: toRef(props, 'errorMessages'),
|
|
97
|
+
warningMessages: toRef(props, 'warningMessages'),
|
|
98
|
+
successMessages: toRef(props, 'successMessages'),
|
|
99
|
+
locales: toRef(props, 'locales'),
|
|
100
|
+
dialCode: internalDialCode,
|
|
101
|
+
withCountryCode: toRef(props, 'withCountryCode'),
|
|
102
|
+
})
|
|
344
103
|
|
|
345
|
-
|
|
346
|
-
|
|
104
|
+
const validation = {
|
|
105
|
+
clearValidation,
|
|
106
|
+
errors,
|
|
107
|
+
warnings,
|
|
108
|
+
successes,
|
|
109
|
+
hasError,
|
|
110
|
+
hasWarning,
|
|
111
|
+
hasSuccess,
|
|
347
112
|
}
|
|
348
113
|
|
|
349
|
-
|
|
350
|
-
if (shouldDisableErrorHandling.value) return
|
|
114
|
+
const phoneMask = computed(() => internalDialCode.value.mask)
|
|
351
115
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
else if (onBlur.value) {
|
|
358
|
-
// Après un premier blur, effacer les erreurs pendant la frappe —
|
|
359
|
-
// la revalidation se fera au prochain blur (comme SyTextField)
|
|
360
|
-
validation.clearValidation()
|
|
361
|
-
}
|
|
362
|
-
})
|
|
116
|
+
// Rattrape l'autofill du navigateur : avec un champ indicatif séparé, le navigateur
|
|
117
|
+
// remplit `tel-national` sans le préfixe national « 0 » (ex. « 612345678 »). On le détecte
|
|
118
|
+
// via un saut du nombre de chiffres (≠ frappe au clavier) et on repréfixe « 0 ».
|
|
119
|
+
const maskInstance = computed(() => new Mask({ mask: phoneMask.value }))
|
|
120
|
+
const previousDigitCount = ref(0)
|
|
363
121
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
})
|
|
122
|
+
function handleNumberInput(event: Event) {
|
|
123
|
+
const target = event.target as HTMLInputElement | null
|
|
124
|
+
if (!target) return
|
|
369
125
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
*/
|
|
374
|
-
const validateOnSubmit = async (): Promise<boolean> => {
|
|
375
|
-
if (shouldDisableErrorHandling.value) {
|
|
376
|
-
return true
|
|
377
|
-
}
|
|
126
|
+
const digits = target.value.replace(/\D/g, '')
|
|
127
|
+
const jumped = digits.length - previousDigitCount.value > 1
|
|
128
|
+
previousDigitCount.value = digits.length
|
|
378
129
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
130
|
+
const expectedLength = internalDialCode.value.phoneLength || 10
|
|
131
|
+
if (props.withCountryCode && jumped && digits.length === expectedLength - 1 && !digits.startsWith('0')) {
|
|
132
|
+
const normalized = maskInstance.value.masked(`0${digits}`)
|
|
133
|
+
phoneNumber.value = normalized
|
|
134
|
+
previousDigitCount.value = expectedLength
|
|
384
135
|
}
|
|
385
|
-
|
|
386
|
-
return !validation.hasError.value
|
|
387
136
|
}
|
|
388
137
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
138
|
+
const showClear = computed(() => {
|
|
139
|
+
if (!props.isClearable) return false
|
|
140
|
+
if (props.disabled) return false
|
|
141
|
+
return phoneNumber.value !== undefined && phoneNumber.value !== null && String(phoneNumber.value) !== ''
|
|
142
|
+
})
|
|
394
143
|
|
|
395
|
-
|
|
144
|
+
function clearField() {
|
|
396
145
|
phoneNumber.value = ''
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
// Reset dial code : France par défaut si indicatif activé, sinon vide
|
|
400
|
-
const defaultDialCode = props.withCountryCode ? getFranceDefault() : ''
|
|
401
|
-
dialCode.value = defaultDialCode
|
|
402
|
-
emit('update:selectedDialCode', defaultDialCode)
|
|
403
|
-
counter.value = 10
|
|
404
|
-
phoneMask.value = '## ## ## ## ##'
|
|
405
|
-
|
|
406
|
-
// Force SySelect to be recreated to ensure internal classes are reset
|
|
407
|
-
dialSelectKey.value++
|
|
146
|
+
clearValidation()
|
|
408
147
|
}
|
|
409
148
|
|
|
410
|
-
// Intégration avec le système de validation du formulaire
|
|
411
|
-
useValidatable(validateOnSubmit, validation.clearValidation, reset)
|
|
412
|
-
|
|
413
149
|
defineExpose({
|
|
414
|
-
|
|
415
|
-
dialCode,
|
|
416
|
-
phoneMask,
|
|
417
|
-
counter,
|
|
150
|
+
dialCodeList,
|
|
418
151
|
hasError,
|
|
419
|
-
|
|
420
|
-
mergedDialCodes,
|
|
152
|
+
errors,
|
|
421
153
|
validation,
|
|
422
|
-
validateOnSubmit,
|
|
154
|
+
validateOnSubmit: validate,
|
|
155
|
+
phoneMask,
|
|
156
|
+
clearValidation,
|
|
423
157
|
})
|
|
158
|
+
|
|
424
159
|
</script>
|
|
425
160
|
|
|
426
161
|
<template>
|
|
@@ -440,24 +175,20 @@
|
|
|
440
175
|
class="phone-field-country"
|
|
441
176
|
>
|
|
442
177
|
<SySelect
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
:items="dialCodeOptions"
|
|
178
|
+
v-model="internalDialCode"
|
|
179
|
+
:items="dialCodeList"
|
|
446
180
|
:label="locales.indicatifLabel"
|
|
447
|
-
:outlined="
|
|
448
|
-
:required="
|
|
449
|
-
:
|
|
450
|
-
:error="
|
|
451
|
-
:
|
|
452
|
-
:
|
|
453
|
-
:
|
|
181
|
+
:outlined="props.outlined"
|
|
182
|
+
:aria-required="true"
|
|
183
|
+
:display-asterisk="props.displayAsterisk"
|
|
184
|
+
:disable-error-handling="props.disableErrorHandling || props.readonly"
|
|
185
|
+
:bg-color="props.bgColor"
|
|
186
|
+
:readonly="props.readonly"
|
|
187
|
+
:disabled="props.disabled"
|
|
188
|
+
:autocomplete="props.autocompleteCountryCode"
|
|
454
189
|
:return-object="true"
|
|
455
|
-
:
|
|
456
|
-
|
|
457
|
-
:disabled="disabled"
|
|
458
|
-
:allow-html="displayFormat === 'code-abbreviation' || displayFormat === 'abbreviation'"
|
|
459
|
-
:autocomplete="autocompleteCountryCode"
|
|
460
|
-
class="custom-select mr-0 mr-sm-4"
|
|
190
|
+
:allow-html="true"
|
|
191
|
+
class="dial-code-select mr-0 mr-sm-4"
|
|
461
192
|
text-key="displayText"
|
|
462
193
|
plain-text-key="plainDisplayText"
|
|
463
194
|
value-key="code"
|
|
@@ -465,25 +196,26 @@
|
|
|
465
196
|
</div>
|
|
466
197
|
<div class="phone-field-number">
|
|
467
198
|
<SyTextField
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
:counter="
|
|
199
|
+
v-model="phoneNumber"
|
|
200
|
+
v-maska="internalDialCode.mask"
|
|
201
|
+
:counter="internalDialCode.phoneLength"
|
|
471
202
|
:counter-value="(value: string) => value.replace(/\D/g, '').length"
|
|
472
203
|
:label="withCountryCode ? locales.phoneNumberWithoutCountryLabel : locales.label"
|
|
473
|
-
:required="required"
|
|
474
|
-
:aria-required="required"
|
|
204
|
+
:required="props.required"
|
|
205
|
+
:aria-required="props.required"
|
|
475
206
|
:error="hasError"
|
|
476
207
|
:error-messages="errors"
|
|
477
208
|
:warning-messages="warnings"
|
|
478
209
|
:success-messages="successes"
|
|
479
210
|
:show-success-messages="props.showSuccessMessages"
|
|
480
|
-
:disable-error-handling="
|
|
481
|
-
:variant="outlined ? 'outlined' : 'underlined'"
|
|
482
|
-
:display-asterisk="displayAsterisk"
|
|
483
|
-
:readonly="readonly"
|
|
484
|
-
:bg-color="bgColor"
|
|
485
|
-
:disabled="disabled"
|
|
486
|
-
:
|
|
211
|
+
:disable-error-handling="props.disableErrorHandling || props.readonly"
|
|
212
|
+
:variant="props.outlined ? 'outlined' : 'underlined'"
|
|
213
|
+
:display-asterisk="props.displayAsterisk"
|
|
214
|
+
:readonly="props.readonly"
|
|
215
|
+
:bg-color="props.bgColor"
|
|
216
|
+
:disabled="props.disabled"
|
|
217
|
+
:hide-details="props.hideDetails"
|
|
218
|
+
:autocomplete="props.autocompletePhone"
|
|
487
219
|
:class="{
|
|
488
220
|
'phone-field': true,
|
|
489
221
|
'error-field': hasError,
|
|
@@ -492,30 +224,31 @@
|
|
|
492
224
|
}"
|
|
493
225
|
color="primary"
|
|
494
226
|
type="tel"
|
|
495
|
-
@
|
|
496
|
-
@
|
|
497
|
-
@
|
|
498
|
-
@keydown="handlePhoneKeydown"
|
|
227
|
+
@input="handleNumberInput"
|
|
228
|
+
@focus="focused = true"
|
|
229
|
+
@blur="focused = false"
|
|
499
230
|
>
|
|
500
231
|
<template #append-inner>
|
|
501
232
|
<div class="d-flex align-center">
|
|
502
|
-
<
|
|
503
|
-
v-if="
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
233
|
+
<button
|
|
234
|
+
v-if="showClear"
|
|
235
|
+
type="button"
|
|
236
|
+
class="phone-field__clear-button mr-1"
|
|
237
|
+
:aria-label="props.label ? locales.clearButtonAriaLabelWithField(props.label) : locales.clearButtonAriaLabel"
|
|
238
|
+
:title="props.label ? locales.clearButtonTitleWithField(props.label) : locales.clearButtonTitle"
|
|
239
|
+
@click.stop="clearField"
|
|
240
|
+
@keydown.enter.stop
|
|
241
|
+
@keydown.space.stop
|
|
242
|
+
>
|
|
243
|
+
<SyIcon
|
|
244
|
+
class="phone-field__clear-icon"
|
|
245
|
+
:icon="mdiCloseCircle"
|
|
246
|
+
:decorative="true"
|
|
247
|
+
width="24"
|
|
248
|
+
/>
|
|
249
|
+
</button>
|
|
250
|
+
<FieldState
|
|
251
|
+
:state="state"
|
|
519
252
|
/>
|
|
520
253
|
<SyIcon
|
|
521
254
|
class="ml-2"
|
|
@@ -598,7 +331,7 @@
|
|
|
598
331
|
flex: 1 1 auto;
|
|
599
332
|
}
|
|
600
333
|
|
|
601
|
-
.
|
|
334
|
+
.dial-code-select {
|
|
602
335
|
margin-bottom: 0;
|
|
603
336
|
min-width: 144px;
|
|
604
337
|
}
|
|
@@ -630,4 +363,37 @@
|
|
|
630
363
|
opacity: 0.38;
|
|
631
364
|
}
|
|
632
365
|
}
|
|
366
|
+
|
|
367
|
+
.phone-field__clear-button {
|
|
368
|
+
background: transparent;
|
|
369
|
+
border: none;
|
|
370
|
+
padding: 0;
|
|
371
|
+
cursor: pointer;
|
|
372
|
+
display: flex;
|
|
373
|
+
align-items: center;
|
|
374
|
+
justify-content: center;
|
|
375
|
+
|
|
376
|
+
.v-icon {
|
|
377
|
+
position: static;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.phone-field__clear-icon {
|
|
382
|
+
color: rgb(var(--v-theme-onSurface)) !important;
|
|
383
|
+
opacity: var(--v-medium-emphasis-opacity) !important;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/* Icône de validation (état) : atténuée à ~0.6 sur PhoneField */
|
|
387
|
+
.phone-field-number :deep(.field-state-icon .v-icon__svg) {
|
|
388
|
+
opacity: 0.6 !important;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/* …sauf en erreur, où l'icône reste à pleine opacité comme sur les autres composants */
|
|
392
|
+
.phone-field-number :deep(.error-icon .v-icon__svg) {
|
|
393
|
+
opacity: 1 !important;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
:deep(.phone-field__clear-icon .v-icon__svg) {
|
|
397
|
+
fill: rgb(var(--v-theme-onSurface)) !important;
|
|
398
|
+
}
|
|
633
399
|
</style>
|