@awes-io/ui 2.46.0 → 2.47.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [2.47.0](https://github.com/awes-io/client/compare/@awes-io/ui@2.46.0...@awes-io/ui@2.47.0) (2022-03-31)
7
+
8
+
9
+ ### Features
10
+
11
+ * **aw-tel:** placeholder replaced with mask ([e54e324](https://github.com/awes-io/client/commit/e54e324cb9ec576f583070a74a06349c23270685))
12
+
13
+
14
+
15
+
16
+
6
17
  # [2.46.0](https://github.com/awes-io/client/compare/@awes-io/ui@2.45.0...@awes-io/ui@2.46.0) (2022-03-17)
7
18
 
8
19
 
@@ -29,7 +29,10 @@
29
29
 
30
30
  &__dropdown {
31
31
  width: 100%;
32
- max-height: 50vh;
32
+
33
+ &.aw-dropdown--desktop {
34
+ max-height: 50vh;
35
+ }
33
36
  }
34
37
 
35
38
  &__validity {
@@ -3,8 +3,7 @@
3
3
  <AwInput
4
4
  ref="input"
5
5
  v-bind="$attrs"
6
- :value="value"
7
- :placeholder="placeholder"
6
+ :value="phoneValue"
8
7
  v-on="mergedListeners"
9
8
  >
10
9
  <template
@@ -16,11 +15,7 @@
16
15
  v-tooltip.show.prepend="errorTooltip"
17
16
  :aria-describedby="errorText ? errorId : null"
18
17
  :class="cssClass"
19
- :value="
20
- phoneNumber.isValid()
21
- ? phoneNumber.format('INTERNATIONAL')
22
- : phoneNumber.number
23
- "
18
+ :value="phoneValue"
24
19
  type="tel"
25
20
  v-on="mergedListeners"
26
21
  />
@@ -32,7 +27,10 @@
32
27
  @click="_openCountriesDropdown"
33
28
  >
34
29
  <span tabindex="-1" class="aw-tel__flag">
35
- <i class="flag" :class="country.toLowerCase()"></i>
30
+ <i
31
+ class="flag"
32
+ :class="(country || '').toLowerCase()"
33
+ ></i>
36
34
  </span>
37
35
  </button>
38
36
  </template>
@@ -41,15 +39,13 @@
41
39
  v-show="!!value"
42
40
  class="aw-tel__validity"
43
41
  :class="
44
- phoneNumber.isValid()
42
+ phoneNumber.valid
45
43
  ? 'aw-tel__validity--valid'
46
44
  : 'aw-tel__validity--invalid'
47
45
  "
48
46
  >
49
47
  <AwIconSystemMono
50
- :name="
51
- phoneNumber.isValid() ? 'check-light' : 'ban-light'
52
- "
48
+ :name="phoneNumber.valid ? 'check-light' : 'ban-light'"
53
49
  />
54
50
  </span>
55
51
  </template>
@@ -58,21 +54,24 @@
58
54
  ref="dropdown"
59
55
  class="aw-tel__dropdown"
60
56
  :show.sync="showCountries"
57
+ :close-on-action="false"
61
58
  @click.native="_onCountryClick"
62
59
  @keydown.native="_keyFocusItem($event)"
63
60
  >
64
- <AwDropdownButton
65
- v-for="{ name, iso } in countries"
66
- :key="iso"
67
- :active="country === iso"
68
- :data-country="iso"
69
- tabindex="-1"
70
- >
71
- <span class="aw-tel__flag mr-3">
72
- <i class="flag" :class="iso.toLowerCase()"></i>
73
- </span>
74
- {{ name }}
75
- </AwDropdownButton>
61
+ <template v-if="showCountries">
62
+ <AwDropdownButton
63
+ v-for="{ name, iso } in countries"
64
+ :key="iso"
65
+ :active="country === iso"
66
+ :data-country="iso"
67
+ tabindex="-1"
68
+ >
69
+ <span class="aw-tel__flag mr-3">
70
+ <i class="flag" :class="iso.toLowerCase()"></i>
71
+ </span>
72
+ {{ name }}
73
+ </AwDropdownButton>
74
+ </template>
76
75
  </AwDropdown>
77
76
  </div>
78
77
  </template>
@@ -153,19 +152,20 @@ export default {
153
152
  },
154
153
 
155
154
  data() {
156
- let country = this.preselectCountry
157
- const phoneNumber = this._parsePhoneNumber(this.value, country)
158
-
159
- if (phoneNumber.isValid()) {
160
- country = phoneNumber.country
161
- }
162
-
163
- const placeholder = this._getPlaceholder(country)
155
+ const phoneNumber = this._parsePhoneNumber(
156
+ this.value,
157
+ this.preselectCountry
158
+ )
159
+ const country = phoneNumber.country
160
+ const countries = this._getCountries(country)
161
+ const phoneValue = this._getPhoneValue(phoneNumber)
164
162
 
165
163
  return {
164
+ showCountries: false,
166
165
  country,
167
- placeholder,
168
- showCountries: false
166
+ countries,
167
+ phoneNumber,
168
+ phoneValue
169
169
  }
170
170
  },
171
171
 
@@ -174,29 +174,17 @@ export default {
174
174
  return {
175
175
  ...this.$listeners,
176
176
  input: this._onInput,
177
- change: this._onInput
177
+ change: this._onInput,
178
+ paste: this._onPaste,
179
+ focus: this._onFocus,
180
+ click: this._onFocus
178
181
  }
179
182
  },
180
183
 
181
- countries() {
182
- return Array.isArray(this.filterCountries) &&
183
- this.filterCountries.length
184
- ? COUNTRIES.filter(
185
- (country) =>
186
- country.iso === this.country ||
187
- this.filterCountries.includes(country.iso)
188
- )
189
- : COUNTRIES
190
- },
191
-
192
184
  callingCode() {
193
185
  return this._getCallingCode(this.country)
194
186
  },
195
187
 
196
- phoneNumber() {
197
- return this._parsePhoneNumber(this.value, this.country)
198
- },
199
-
200
188
  _arrowFocusSelector() {
201
189
  return '[data-country]'
202
190
  },
@@ -211,22 +199,53 @@ export default {
211
199
  handler(iso, oldIso) {
212
200
  if (iso === oldIso) return
213
201
 
214
- this.placeholder = this._getPlaceholder(iso)
215
-
216
- if (oldIso) {
202
+ if (iso && oldIso) {
217
203
  const value = this.value.replace(
218
204
  '+' + this._getCallingCode(oldIso),
219
205
  '+' + this.callingCode
220
206
  )
221
207
  const phoneNumber = this._parsePhoneNumber(value, iso)
222
208
 
209
+ if (!this.phoneNumber.isEqual(phoneNumber)) {
210
+ this.phoneNumber = phoneNumber
211
+ this.phoneValue = this._getPhoneValue(phoneNumber)
212
+
213
+ this._emitInput(phoneNumber)
214
+ }
215
+ } else {
216
+ const phoneNumber = this._parsePhoneNumber(this.value, iso)
217
+
218
+ this.phoneNumber = phoneNumber
219
+ this.phoneValue = this._getPhoneValue(phoneNumber)
220
+
223
221
  this._emitInput(phoneNumber)
224
222
  }
225
223
 
226
- if (this.$refs.element) {
224
+ this.countries = this._getCountries(iso)
225
+
226
+ if (
227
+ this.$refs.element &&
228
+ this.$el.contains(document.activeElement)
229
+ ) {
227
230
  this.$refs.element.focus()
231
+ this.$nextTick(() => {
232
+ this._setFocusToFirstDash(this.$refs.element)
233
+ })
228
234
  }
229
235
  }
236
+ },
237
+
238
+ value(value) {
239
+ const phoneNumber = this._parsePhoneNumber(value, this.country)
240
+
241
+ if (!this.phoneNumber.isEqual(phoneNumber)) {
242
+ this.phoneNumber = phoneNumber
243
+ this.phoneValue = this._getPhoneValue(phoneNumber)
244
+ }
245
+
246
+ if (phoneNumber.country && phoneNumber.country !== this.country) {
247
+ this.country = phoneNumber.country
248
+ }
230
249
  }
231
250
  },
232
251
 
@@ -234,20 +253,100 @@ export default {
234
253
  _onInput($event) {
235
254
  const value = $event.target.value
236
255
  const phoneNumber = this._parsePhoneNumber(value, this.country)
237
-
238
- if (this.phoneNumber.isEqual(phoneNumber)) {
256
+ const phoneValue = this._getPhoneValue(phoneNumber)
257
+
258
+ // open dropdown on backspace
259
+ if (
260
+ phoneNumber.number === '' &&
261
+ phoneValue === this.phoneValue &&
262
+ $event.data === null
263
+ ) {
264
+ this.$nextTick(this._openCountriesDropdown)
239
265
  return
240
266
  }
241
267
 
268
+ // save caret position
269
+ let pos = $event.target.selectionStart
270
+ let digit = pos > 0 ? value[pos - 1] : '_'
271
+ let adjustPosition = false
272
+
273
+ if (phoneValue !== this.phoneValue) {
274
+ adjustPosition = true
275
+
276
+ this.phoneNumber = phoneNumber
277
+ this.phoneValue = phoneValue
278
+
279
+ this._emitInput(phoneNumber)
280
+ }
281
+
282
+ if ($event.target.value !== this.phoneValue) {
283
+ adjustPosition = true
284
+
285
+ $event.target.value = this.phoneValue
286
+ }
287
+
288
+ if (phoneNumber.country && phoneNumber.country !== this.country) {
289
+ this.country = phoneNumber.country
290
+ }
291
+
242
292
  if (this.$refs.input.hasError) {
243
293
  this.$refs.input.setError('')
244
294
  }
245
295
 
246
- if (phoneNumber.isValid() && phoneNumber.country !== this.country) {
296
+ if ($event.data !== null && this.showCountries) {
297
+ this.showCountries = false
298
+ }
299
+
300
+ if (adjustPosition) {
301
+ this.$nextTick(() => {
302
+ const newValue = $event.target.value
303
+ const firstDashIndex = newValue.indexOf('_')
304
+ const max =
305
+ firstDashIndex > -1 ? firstDashIndex : newValue.length
306
+
307
+ pos = Math.min(pos, max)
308
+
309
+ while (
310
+ newValue[pos - 1] !== digit &&
311
+ pos < firstDashIndex
312
+ ) {
313
+ pos++
314
+ }
315
+
316
+ $event.target.setSelectionRange(pos, pos)
317
+ })
318
+ }
319
+ },
320
+
321
+ _onPaste($event) {
322
+ $event.preventDefault()
323
+
324
+ // replace all Data
325
+ const phone = $event.clipboardData.getData('text')
326
+
327
+ const phoneNumber = this._parsePhoneNumber(phone, this.country)
328
+
329
+ if (!this.phoneNumber.isEqual(phoneNumber)) {
330
+ this.phoneNumber = phoneNumber
331
+ this.phoneValue = this._getPhoneValue(phoneNumber)
332
+
333
+ this._emitInput(phoneNumber)
334
+ }
335
+
336
+ if (phoneNumber.country && phoneNumber.country !== this.country) {
247
337
  this.country = phoneNumber.country
248
338
  }
249
339
 
250
- this._emitInput(phoneNumber)
340
+ $event.target.focus()
341
+
342
+ this.$nextTick(() => {
343
+ this._onFocus({ target: $event.target })
344
+ })
345
+ },
346
+
347
+ _onFocus($event) {
348
+ this._setFocusToFirstDash($event.target)
349
+ this.showCountries = false
251
350
  },
252
351
 
253
352
  _onBlur($event) {
@@ -256,17 +355,22 @@ export default {
256
355
  }
257
356
  },
258
357
 
259
- _getPlaceholder(iso) {
358
+ _getPlaceholder(iso, withCode = false) {
260
359
  const phoneNumber = getExampleNumber(iso, examples)
261
360
 
262
361
  if (phoneNumber) {
263
362
  const number = phoneNumber.format('INTERNATIONAL')
264
- const code = '+' + phoneNumber.countryCallingCode
265
363
 
266
- return number
267
- .replace(code, '{code}')
268
- .replace(/\d/g, '_')
269
- .replace('{code}', code)
364
+ if (withCode) {
365
+ const code = '+' + phoneNumber.countryCallingCode
366
+
367
+ return number
368
+ .replace(code, '{code}')
369
+ .replace(/\d/g, '_')
370
+ .replace('{code}', code)
371
+ } else {
372
+ return number.replace(/\d/g, '_')
373
+ }
270
374
  } else {
271
375
  return ''
272
376
  }
@@ -279,20 +383,45 @@ export default {
279
383
  this.$el
280
384
  )
281
385
  this.showCountries = false
386
+
387
+ this.$refs.element.focus()
388
+ this.$nextTick(() => this._setFocusToFirstDash(this.$refs.element))
282
389
  },
283
390
 
284
- _parsePhoneNumber(value, country) {
285
- const number = typeof value === 'string' ? value : ''
286
- const phoneNumber = parsePhoneNumberFromString(number, country)
391
+ _parsePhoneNumber(value = '', country) {
392
+ let number = typeof value === 'string' ? value : ''
393
+
394
+ number = number.replace(/\D/g, '')
395
+
396
+ number = number === this.callingCode ? '' : number
397
+
398
+ number = number && value.startsWith('+') ? '+' + number : number
399
+
400
+ let phoneNumber = parsePhoneNumberFromString(number, country)
287
401
 
288
402
  if (phoneNumber) {
289
- return phoneNumber
403
+ return {
404
+ number: phoneNumber.number,
405
+ country: phoneNumber.country,
406
+ countryCallingCode: phoneNumber.countryCallingCode,
407
+ valid: phoneNumber.isValid(),
408
+ format() {
409
+ return phoneNumber.format.apply(phoneNumber, arguments)
410
+ },
411
+ isValid() {
412
+ return phoneNumber.isValid.apply(phoneNumber, arguments)
413
+ },
414
+ isEqual() {
415
+ return phoneNumber.isEqual.apply(phoneNumber, arguments)
416
+ }
417
+ }
290
418
  } else {
291
419
  // mock PhoneNumber class
292
420
  return {
293
421
  number,
294
422
  country,
295
423
  countryCallingCode: this.callingCode,
424
+ valid: false,
296
425
  format() {
297
426
  return number
298
427
  },
@@ -306,6 +435,30 @@ export default {
306
435
  }
307
436
  },
308
437
 
438
+ _getPhoneValue(phoneNumber) {
439
+ if (phoneNumber.number === '') {
440
+ return this._getPlaceholder(phoneNumber.country, true)
441
+ }
442
+
443
+ const placeholder = this._getPlaceholder(phoneNumber.country)
444
+
445
+ if (placeholder) {
446
+ const digits = phoneNumber.number.replace(/\D/g, '').split('')
447
+ const replaced = placeholder.replace(
448
+ /_/g,
449
+ () => digits.shift() || '_'
450
+ )
451
+
452
+ return digits.length
453
+ ? replaced + digits.join('')
454
+ : phoneNumber.valid
455
+ ? replaced.replace(/_/g, '')
456
+ : replaced
457
+ } else {
458
+ return phoneNumber.number
459
+ }
460
+ },
461
+
309
462
  _getCallingCode(iso) {
310
463
  return pathOr(
311
464
  '',
@@ -314,11 +467,22 @@ export default {
314
467
  )
315
468
  },
316
469
 
470
+ _getCountries(includeIso) {
471
+ return Array.isArray(this.filterCountries) &&
472
+ this.filterCountries.length
473
+ ? COUNTRIES.filter(
474
+ (country) =>
475
+ country.iso === includeIso ||
476
+ this.filterCountries.includes(country.iso)
477
+ )
478
+ : COUNTRIES
479
+ },
480
+
317
481
  _emitInput(phoneNumber) {
318
482
  if (phoneNumber.number === this.value) return
319
483
 
320
484
  this.$emit('input', phoneNumber.number, {
321
- isValid: phoneNumber.isValid(),
485
+ isValid: phoneNumber.valid,
322
486
  country: phoneNumber.country,
323
487
  number: {
324
488
  national: phoneNumber.format('NATIONAL'),
@@ -341,6 +505,15 @@ export default {
341
505
  el.focus()
342
506
  }
343
507
  })
508
+ },
509
+
510
+ _setFocusToFirstDash(inputEl) {
511
+ const firstDashIndex = inputEl.value.indexOf('_')
512
+ const pos = inputEl.selectionStart
513
+
514
+ if (firstDashIndex > -1 && pos > firstDashIndex) {
515
+ inputEl.setSelectionRange(firstDashIndex, firstDashIndex)
516
+ }
344
517
  }
345
518
  }
346
519
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awes-io/ui",
3
- "version": "2.46.0",
3
+ "version": "2.47.0",
4
4
  "description": "User Interface (UI) components",
5
5
  "keywords": [
6
6
  "ui",
@@ -124,5 +124,5 @@
124
124
  "vue-template-compiler": "^2.6.10",
125
125
  "webfonts-generator": "^0.4.0"
126
126
  },
127
- "gitHead": "45bd6aa3df580ebb16241da5502dbf4f781bf96a"
127
+ "gitHead": "fe6bdb700f0209121131bf1e68c0f9251c793b9a"
128
128
  }