js-kompo-utils 1.0.34 → 1.0.35
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.
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
<input
|
|
57
57
|
ref="phoneInput"
|
|
58
58
|
class="phone-input"
|
|
59
|
+
:class="{ 'has-extension': showExtension }"
|
|
59
60
|
:value="formattedValue"
|
|
60
61
|
v-bind="$_attributes"
|
|
61
62
|
v-on="$_events"
|
|
@@ -64,6 +65,44 @@
|
|
|
64
65
|
@focus="onPhoneFocus"
|
|
65
66
|
@blur="onPhoneBlur"
|
|
66
67
|
/>
|
|
68
|
+
|
|
69
|
+
<!-- Extension ("poste"), opt-in via ->withExtension(). Collapsed until asked for,
|
|
70
|
+
so the common case stays a single field. -->
|
|
71
|
+
<button
|
|
72
|
+
v-if="showExtension && !extensionOpen"
|
|
73
|
+
type="button"
|
|
74
|
+
class="phone-extension-toggle"
|
|
75
|
+
:title="extensionLabel"
|
|
76
|
+
:disabled="$_attributes.disabled"
|
|
77
|
+
@click="openExtension"
|
|
78
|
+
>
|
|
79
|
+
<span class="phone-extension-plus">+</span>
|
|
80
|
+
<span class="phone-extension-toggle-label">{{ extensionLabel }}</span>
|
|
81
|
+
</button>
|
|
82
|
+
|
|
83
|
+
<div v-else-if="showExtension" class="phone-extension-wrap">
|
|
84
|
+
<span class="phone-extension-prefix">{{ extensionLabel }}</span>
|
|
85
|
+
<input
|
|
86
|
+
ref="extensionInput"
|
|
87
|
+
class="phone-extension-input"
|
|
88
|
+
type="text"
|
|
89
|
+
inputmode="numeric"
|
|
90
|
+
autocomplete="off"
|
|
91
|
+
:value="extensionValue"
|
|
92
|
+
:placeholder="extensionPlaceholder"
|
|
93
|
+
:disabled="$_attributes.disabled"
|
|
94
|
+
:readonly="$_attributes.readonly"
|
|
95
|
+
@input="onExtensionInput"
|
|
96
|
+
@keydown.esc="closeExtension"
|
|
97
|
+
/>
|
|
98
|
+
<button
|
|
99
|
+
type="button"
|
|
100
|
+
class="phone-extension-clear"
|
|
101
|
+
:title="extensionClearLabel"
|
|
102
|
+
:disabled="$_attributes.disabled"
|
|
103
|
+
@click="closeExtension"
|
|
104
|
+
>×</button>
|
|
105
|
+
</div>
|
|
67
106
|
</div>
|
|
68
107
|
</vl-form-field>
|
|
69
108
|
</template>
|
|
@@ -96,6 +135,11 @@ export default {
|
|
|
96
135
|
|
|
97
136
|
// Geolocation state
|
|
98
137
|
isDetectingCountry: false,
|
|
138
|
+
|
|
139
|
+
// Optional extension ("poste"), submitted as `<name>_ext`. Starts open only
|
|
140
|
+
// when there is one to show, so an empty field stays a single box.
|
|
141
|
+
extensionValue: this.normalizeExtension(this.$_config('extensionValue')),
|
|
142
|
+
extensionOpen: this.normalizeExtension(this.$_config('extensionValue')) !== '',
|
|
99
143
|
}
|
|
100
144
|
},
|
|
101
145
|
|
|
@@ -114,6 +158,22 @@ export default {
|
|
|
114
158
|
placeholder() {
|
|
115
159
|
return this.$_config('placeholder') || 'Phone number'
|
|
116
160
|
},
|
|
161
|
+
|
|
162
|
+
showExtension() {
|
|
163
|
+
return !!this.$_config('withExtension')
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
extensionLabel() {
|
|
167
|
+
return this.$_config('extensionLabel') || 'ext'
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
extensionPlaceholder() {
|
|
171
|
+
return this.$_config('extensionPlaceholder') || '000'
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
extensionClearLabel() {
|
|
175
|
+
return this.$_config('extensionClearLabel') || 'Remove'
|
|
176
|
+
},
|
|
117
177
|
|
|
118
178
|
inputGroupClass() {
|
|
119
179
|
const baseClass = 'vlInputGroup rounded-lg flex items-stretch'
|
|
@@ -212,6 +272,13 @@ export default {
|
|
|
212
272
|
}
|
|
213
273
|
},
|
|
214
274
|
|
|
275
|
+
// Kompo swaps `component` in place on a refresh without re-running data(), which is why
|
|
276
|
+
// the number re-syncs above. The extension has to follow or it keeps the stale prefill.
|
|
277
|
+
'component.config.extensionValue'(newValue) {
|
|
278
|
+
this.extensionValue = this.normalizeExtension(newValue)
|
|
279
|
+
this.extensionOpen = this.extensionOpen || this.extensionValue !== ''
|
|
280
|
+
},
|
|
281
|
+
|
|
215
282
|
selectedCountryCode(newCode, oldCode) {
|
|
216
283
|
if (newCode !== oldCode && this.phoneValue) {
|
|
217
284
|
this.reformatWithCountry(newCode)
|
|
@@ -239,6 +306,42 @@ export default {
|
|
|
239
306
|
},
|
|
240
307
|
|
|
241
308
|
methods: {
|
|
309
|
+
// The extension is not part of the number, so it rides alongside it as its own
|
|
310
|
+
// key instead of being folded into the value the way E.164 cannot carry it.
|
|
311
|
+
$_fill(jsonFormData) {
|
|
312
|
+
jsonFormData[this.$_name] = (this.$_value === null || this.$_value === undefined) ? '' : this.$_value
|
|
313
|
+
|
|
314
|
+
if (this.showExtension) {
|
|
315
|
+
jsonFormData[this.$_name + '_ext'] = this.extensionValue || ''
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
// `|| ''` would swallow a "0" extension, and the column is a string.
|
|
320
|
+
normalizeExtension(value) {
|
|
321
|
+
return (value === null || value === undefined) ? '' : String(value)
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
onExtensionInput(event) {
|
|
325
|
+
this.extensionValue = event.target.value.replace(/[^0-9#*]/g, '')
|
|
326
|
+
|
|
327
|
+
if (event.target.value !== this.extensionValue) {
|
|
328
|
+
event.target.value = this.extensionValue
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
openExtension() {
|
|
333
|
+
this.extensionOpen = true
|
|
334
|
+
|
|
335
|
+
this.$nextTick(() => this.$refs.extensionInput?.focus())
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
// Collapsing clears the value: the empty companion still posts, so the stored
|
|
339
|
+
// extension is removed rather than silently kept.
|
|
340
|
+
closeExtension() {
|
|
341
|
+
this.extensionValue = ''
|
|
342
|
+
this.extensionOpen = false
|
|
343
|
+
},
|
|
344
|
+
|
|
242
345
|
// Country selection methods
|
|
243
346
|
toggleDropdown() {
|
|
244
347
|
this.isDropdownOpen = !this.isDropdownOpen
|
|
@@ -922,4 +1025,100 @@ export default {
|
|
|
922
1025
|
color: #6b7280;
|
|
923
1026
|
cursor: not-allowed;
|
|
924
1027
|
}
|
|
1028
|
+
|
|
1029
|
+
.phone-input.has-extension {
|
|
1030
|
+
border-radius: 0;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/* Collapsed state: a quiet pill closing the group's right edge. */
|
|
1034
|
+
.phone-extension-toggle {
|
|
1035
|
+
display: flex;
|
|
1036
|
+
align-items: center;
|
|
1037
|
+
gap: 0.2rem;
|
|
1038
|
+
flex: none;
|
|
1039
|
+
padding: 0 0.7rem;
|
|
1040
|
+
margin-left: -1px;
|
|
1041
|
+
border: 1px solid #d1d5db;
|
|
1042
|
+
border-radius: 0 0.5rem 0.5rem 0;
|
|
1043
|
+
background-color: transparent;
|
|
1044
|
+
color: #6b7280;
|
|
1045
|
+
font-size: 0.8125rem;
|
|
1046
|
+
line-height: 1;
|
|
1047
|
+
white-space: nowrap;
|
|
1048
|
+
cursor: pointer;
|
|
1049
|
+
transition: color 0.15s ease, background-color 0.15s ease;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
.phone-extension-toggle:hover:not(:disabled) {
|
|
1053
|
+
color: #3b82f6;
|
|
1054
|
+
background-color: #f3f4f6;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
.phone-extension-toggle:disabled {
|
|
1058
|
+
cursor: not-allowed;
|
|
1059
|
+
opacity: 0.6;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
.phone-extension-plus {
|
|
1063
|
+
font-size: 0.95rem;
|
|
1064
|
+
line-height: 1;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/* Expanded state: label + field + dismiss, sharing one bordered cell. */
|
|
1068
|
+
.phone-extension-wrap {
|
|
1069
|
+
display: flex;
|
|
1070
|
+
align-items: center;
|
|
1071
|
+
flex: none;
|
|
1072
|
+
margin-left: -1px;
|
|
1073
|
+
padding-left: 0.6rem;
|
|
1074
|
+
border: 1px solid #d1d5db;
|
|
1075
|
+
border-radius: 0 0.5rem 0.5rem 0;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
.phone-extension-wrap:focus-within {
|
|
1079
|
+
border-color: #3b82f6;
|
|
1080
|
+
box-shadow: 0 0 0 1px #3b82f6;
|
|
1081
|
+
z-index: 1;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
.phone-extension-prefix {
|
|
1085
|
+
color: #9ca3af;
|
|
1086
|
+
font-size: 0.8125rem;
|
|
1087
|
+
white-space: nowrap;
|
|
1088
|
+
pointer-events: none;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
.phone-extension-input {
|
|
1092
|
+
width: 3.75rem;
|
|
1093
|
+
min-width: 0;
|
|
1094
|
+
padding: 0.5rem 0.25rem;
|
|
1095
|
+
border: none;
|
|
1096
|
+
outline: none;
|
|
1097
|
+
background-color: transparent;
|
|
1098
|
+
font-size: 1rem;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
.phone-extension-input:disabled {
|
|
1102
|
+
color: #6b7280;
|
|
1103
|
+
cursor: not-allowed;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
.phone-extension-clear {
|
|
1107
|
+
padding: 0 0.55rem 0 0.15rem;
|
|
1108
|
+
border: none;
|
|
1109
|
+
background-color: transparent;
|
|
1110
|
+
color: #9ca3af;
|
|
1111
|
+
font-size: 1.05rem;
|
|
1112
|
+
line-height: 1;
|
|
1113
|
+
cursor: pointer;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
.phone-extension-clear:hover:not(:disabled) {
|
|
1117
|
+
color: #ef4444;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
.phone-extension-clear:disabled {
|
|
1121
|
+
cursor: not-allowed;
|
|
1122
|
+
opacity: 0.6;
|
|
1123
|
+
}
|
|
925
1124
|
</style>
|