telpick 1.0.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.

Potentially problematic release.


This version of telpick might be problematic. Click here for more details.

Files changed (271) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +215 -0
  3. package/dist/style.css +1 -0
  4. package/dist/telpick.es.js +241 -0
  5. package/dist/telpick.umd.js +1 -0
  6. package/package.json +93 -0
  7. package/src/assets/country-code.json +94 -0
  8. package/src/assets/flags/ad.webp +0 -0
  9. package/src/assets/flags/ae.webp +0 -0
  10. package/src/assets/flags/af.png +0 -0
  11. package/src/assets/flags/ag.webp +0 -0
  12. package/src/assets/flags/ai.webp +0 -0
  13. package/src/assets/flags/al.webp +0 -0
  14. package/src/assets/flags/am.webp +0 -0
  15. package/src/assets/flags/ao.webp +0 -0
  16. package/src/assets/flags/aq.webp +0 -0
  17. package/src/assets/flags/ar.webp +0 -0
  18. package/src/assets/flags/as.webp +0 -0
  19. package/src/assets/flags/at.webp +0 -0
  20. package/src/assets/flags/au.webp +0 -0
  21. package/src/assets/flags/aw.webp +0 -0
  22. package/src/assets/flags/ax.webp +0 -0
  23. package/src/assets/flags/az.webp +0 -0
  24. package/src/assets/flags/ba.webp +0 -0
  25. package/src/assets/flags/bb.webp +0 -0
  26. package/src/assets/flags/bd.webp +0 -0
  27. package/src/assets/flags/be.webp +0 -0
  28. package/src/assets/flags/bf.webp +0 -0
  29. package/src/assets/flags/bg.webp +0 -0
  30. package/src/assets/flags/bh.webp +0 -0
  31. package/src/assets/flags/bi.webp +0 -0
  32. package/src/assets/flags/bj.webp +0 -0
  33. package/src/assets/flags/bl.webp +0 -0
  34. package/src/assets/flags/bm.webp +0 -0
  35. package/src/assets/flags/bn.webp +0 -0
  36. package/src/assets/flags/bo.webp +0 -0
  37. package/src/assets/flags/bq.webp +0 -0
  38. package/src/assets/flags/br.webp +0 -0
  39. package/src/assets/flags/bs.webp +0 -0
  40. package/src/assets/flags/bt.webp +0 -0
  41. package/src/assets/flags/bv.webp +0 -0
  42. package/src/assets/flags/bw.webp +0 -0
  43. package/src/assets/flags/by.webp +0 -0
  44. package/src/assets/flags/bz.webp +0 -0
  45. package/src/assets/flags/ca.webp +0 -0
  46. package/src/assets/flags/cc.webp +0 -0
  47. package/src/assets/flags/cd.webp +0 -0
  48. package/src/assets/flags/cf.webp +0 -0
  49. package/src/assets/flags/cg.webp +0 -0
  50. package/src/assets/flags/ch.webp +0 -0
  51. package/src/assets/flags/ci.webp +0 -0
  52. package/src/assets/flags/ck.webp +0 -0
  53. package/src/assets/flags/cl.webp +0 -0
  54. package/src/assets/flags/cm.webp +0 -0
  55. package/src/assets/flags/cn.webp +0 -0
  56. package/src/assets/flags/co.webp +0 -0
  57. package/src/assets/flags/cr.webp +0 -0
  58. package/src/assets/flags/cu.webp +0 -0
  59. package/src/assets/flags/cv.webp +0 -0
  60. package/src/assets/flags/cw.webp +0 -0
  61. package/src/assets/flags/cx.webp +0 -0
  62. package/src/assets/flags/cy.webp +0 -0
  63. package/src/assets/flags/cz.webp +0 -0
  64. package/src/assets/flags/de.webp +0 -0
  65. package/src/assets/flags/dj.webp +0 -0
  66. package/src/assets/flags/dk.webp +0 -0
  67. package/src/assets/flags/dm.webp +0 -0
  68. package/src/assets/flags/do.webp +0 -0
  69. package/src/assets/flags/dz.webp +0 -0
  70. package/src/assets/flags/ec.webp +0 -0
  71. package/src/assets/flags/ee.webp +0 -0
  72. package/src/assets/flags/eg.webp +0 -0
  73. package/src/assets/flags/eh.webp +0 -0
  74. package/src/assets/flags/er.webp +0 -0
  75. package/src/assets/flags/es.webp +0 -0
  76. package/src/assets/flags/et.webp +0 -0
  77. package/src/assets/flags/fi.webp +0 -0
  78. package/src/assets/flags/fj.webp +0 -0
  79. package/src/assets/flags/fk.webp +0 -0
  80. package/src/assets/flags/fm.webp +0 -0
  81. package/src/assets/flags/fo.webp +0 -0
  82. package/src/assets/flags/fr.webp +0 -0
  83. package/src/assets/flags/ga.webp +0 -0
  84. package/src/assets/flags/gb-eng.webp +0 -0
  85. package/src/assets/flags/gb-nir.webp +0 -0
  86. package/src/assets/flags/gb-sct.webp +0 -0
  87. package/src/assets/flags/gb-wls.webp +0 -0
  88. package/src/assets/flags/gb.webp +0 -0
  89. package/src/assets/flags/gd.webp +0 -0
  90. package/src/assets/flags/ge.webp +0 -0
  91. package/src/assets/flags/gf.webp +0 -0
  92. package/src/assets/flags/gg.webp +0 -0
  93. package/src/assets/flags/gh.webp +0 -0
  94. package/src/assets/flags/gi.webp +0 -0
  95. package/src/assets/flags/gl.webp +0 -0
  96. package/src/assets/flags/gm.webp +0 -0
  97. package/src/assets/flags/gn.webp +0 -0
  98. package/src/assets/flags/gp.webp +0 -0
  99. package/src/assets/flags/gq.webp +0 -0
  100. package/src/assets/flags/gr.webp +0 -0
  101. package/src/assets/flags/gs.webp +0 -0
  102. package/src/assets/flags/gt.webp +0 -0
  103. package/src/assets/flags/gu.webp +0 -0
  104. package/src/assets/flags/gw.webp +0 -0
  105. package/src/assets/flags/gy.webp +0 -0
  106. package/src/assets/flags/hk.webp +0 -0
  107. package/src/assets/flags/hm.webp +0 -0
  108. package/src/assets/flags/hn.webp +0 -0
  109. package/src/assets/flags/hr.webp +0 -0
  110. package/src/assets/flags/ht.webp +0 -0
  111. package/src/assets/flags/hu.webp +0 -0
  112. package/src/assets/flags/id.webp +0 -0
  113. package/src/assets/flags/ie.webp +0 -0
  114. package/src/assets/flags/il.webp +0 -0
  115. package/src/assets/flags/im.webp +0 -0
  116. package/src/assets/flags/in.webp +0 -0
  117. package/src/assets/flags/io.webp +0 -0
  118. package/src/assets/flags/iq.webp +0 -0
  119. package/src/assets/flags/ir.webp +0 -0
  120. package/src/assets/flags/is.webp +0 -0
  121. package/src/assets/flags/it.webp +0 -0
  122. package/src/assets/flags/je.webp +0 -0
  123. package/src/assets/flags/jm.webp +0 -0
  124. package/src/assets/flags/jo.webp +0 -0
  125. package/src/assets/flags/jp.webp +0 -0
  126. package/src/assets/flags/ke.webp +0 -0
  127. package/src/assets/flags/kg.webp +0 -0
  128. package/src/assets/flags/kh.webp +0 -0
  129. package/src/assets/flags/ki.webp +0 -0
  130. package/src/assets/flags/km.webp +0 -0
  131. package/src/assets/flags/kn.webp +0 -0
  132. package/src/assets/flags/kp.webp +0 -0
  133. package/src/assets/flags/kr.webp +0 -0
  134. package/src/assets/flags/kw.webp +0 -0
  135. package/src/assets/flags/ky.webp +0 -0
  136. package/src/assets/flags/kz.webp +0 -0
  137. package/src/assets/flags/la.webp +0 -0
  138. package/src/assets/flags/lb.webp +0 -0
  139. package/src/assets/flags/lc.webp +0 -0
  140. package/src/assets/flags/li.webp +0 -0
  141. package/src/assets/flags/lista.txt +0 -0
  142. package/src/assets/flags/lk.webp +0 -0
  143. package/src/assets/flags/lr.webp +0 -0
  144. package/src/assets/flags/ls.webp +0 -0
  145. package/src/assets/flags/lt.webp +0 -0
  146. package/src/assets/flags/lu.webp +0 -0
  147. package/src/assets/flags/lv.webp +0 -0
  148. package/src/assets/flags/ly.webp +0 -0
  149. package/src/assets/flags/ma.webp +0 -0
  150. package/src/assets/flags/mc.webp +0 -0
  151. package/src/assets/flags/md.webp +0 -0
  152. package/src/assets/flags/me.webp +0 -0
  153. package/src/assets/flags/mf.webp +0 -0
  154. package/src/assets/flags/mg.webp +0 -0
  155. package/src/assets/flags/mh.webp +0 -0
  156. package/src/assets/flags/mk.webp +0 -0
  157. package/src/assets/flags/ml.webp +0 -0
  158. package/src/assets/flags/mm.webp +0 -0
  159. package/src/assets/flags/mn.webp +0 -0
  160. package/src/assets/flags/mo.webp +0 -0
  161. package/src/assets/flags/mp.webp +0 -0
  162. package/src/assets/flags/mq.webp +0 -0
  163. package/src/assets/flags/mr.webp +0 -0
  164. package/src/assets/flags/ms.webp +0 -0
  165. package/src/assets/flags/mt.webp +0 -0
  166. package/src/assets/flags/mu.webp +0 -0
  167. package/src/assets/flags/mv.webp +0 -0
  168. package/src/assets/flags/mw.webp +0 -0
  169. package/src/assets/flags/mx.webp +0 -0
  170. package/src/assets/flags/my.webp +0 -0
  171. package/src/assets/flags/mz.webp +0 -0
  172. package/src/assets/flags/na.webp +0 -0
  173. package/src/assets/flags/nc.webp +0 -0
  174. package/src/assets/flags/ne.webp +0 -0
  175. package/src/assets/flags/nf.webp +0 -0
  176. package/src/assets/flags/ng.webp +0 -0
  177. package/src/assets/flags/ni.webp +0 -0
  178. package/src/assets/flags/nl.webp +0 -0
  179. package/src/assets/flags/no.webp +0 -0
  180. package/src/assets/flags/np.webp +0 -0
  181. package/src/assets/flags/nr.webp +0 -0
  182. package/src/assets/flags/nu.webp +0 -0
  183. package/src/assets/flags/nz.webp +0 -0
  184. package/src/assets/flags/om.webp +0 -0
  185. package/src/assets/flags/pa.webp +0 -0
  186. package/src/assets/flags/pe.webp +0 -0
  187. package/src/assets/flags/pf.webp +0 -0
  188. package/src/assets/flags/pg.webp +0 -0
  189. package/src/assets/flags/ph.webp +0 -0
  190. package/src/assets/flags/pk.webp +0 -0
  191. package/src/assets/flags/pl.webp +0 -0
  192. package/src/assets/flags/pm.webp +0 -0
  193. package/src/assets/flags/pn.webp +0 -0
  194. package/src/assets/flags/pr.webp +0 -0
  195. package/src/assets/flags/ps.webp +0 -0
  196. package/src/assets/flags/pt.webp +0 -0
  197. package/src/assets/flags/pw.webp +0 -0
  198. package/src/assets/flags/py.webp +0 -0
  199. package/src/assets/flags/qa.webp +0 -0
  200. package/src/assets/flags/re.webp +0 -0
  201. package/src/assets/flags/ro.webp +0 -0
  202. package/src/assets/flags/rs.webp +0 -0
  203. package/src/assets/flags/ru.webp +0 -0
  204. package/src/assets/flags/rw.webp +0 -0
  205. package/src/assets/flags/sa.webp +0 -0
  206. package/src/assets/flags/sb.webp +0 -0
  207. package/src/assets/flags/sc.webp +0 -0
  208. package/src/assets/flags/sd.webp +0 -0
  209. package/src/assets/flags/se (1).webp +0 -0
  210. package/src/assets/flags/se.webp +0 -0
  211. package/src/assets/flags/sg.webp +0 -0
  212. package/src/assets/flags/sh.webp +0 -0
  213. package/src/assets/flags/si.webp +0 -0
  214. package/src/assets/flags/sj.webp +0 -0
  215. package/src/assets/flags/sk.webp +0 -0
  216. package/src/assets/flags/sl.webp +0 -0
  217. package/src/assets/flags/sm.webp +0 -0
  218. package/src/assets/flags/sn.webp +0 -0
  219. package/src/assets/flags/so.webp +0 -0
  220. package/src/assets/flags/sr.webp +0 -0
  221. package/src/assets/flags/ss.webp +0 -0
  222. package/src/assets/flags/st.webp +0 -0
  223. package/src/assets/flags/sv.webp +0 -0
  224. package/src/assets/flags/sx.webp +0 -0
  225. package/src/assets/flags/sy.webp +0 -0
  226. package/src/assets/flags/sz.webp +0 -0
  227. package/src/assets/flags/tc.webp +0 -0
  228. package/src/assets/flags/td.webp +0 -0
  229. package/src/assets/flags/tf.webp +0 -0
  230. package/src/assets/flags/tg.webp +0 -0
  231. package/src/assets/flags/th.webp +0 -0
  232. package/src/assets/flags/tj.webp +0 -0
  233. package/src/assets/flags/tk.webp +0 -0
  234. package/src/assets/flags/tl.webp +0 -0
  235. package/src/assets/flags/tm.webp +0 -0
  236. package/src/assets/flags/tn.webp +0 -0
  237. package/src/assets/flags/to.webp +0 -0
  238. package/src/assets/flags/tr.webp +0 -0
  239. package/src/assets/flags/tt.webp +0 -0
  240. package/src/assets/flags/tv.webp +0 -0
  241. package/src/assets/flags/tw.webp +0 -0
  242. package/src/assets/flags/tz.webp +0 -0
  243. package/src/assets/flags/ua.webp +0 -0
  244. package/src/assets/flags/ug.webp +0 -0
  245. package/src/assets/flags/um.webp +0 -0
  246. package/src/assets/flags/us.webp +0 -0
  247. package/src/assets/flags/uy.webp +0 -0
  248. package/src/assets/flags/uz.webp +0 -0
  249. package/src/assets/flags/va.webp +0 -0
  250. package/src/assets/flags/vc.webp +0 -0
  251. package/src/assets/flags/ve.webp +0 -0
  252. package/src/assets/flags/vg.webp +0 -0
  253. package/src/assets/flags/vi.webp +0 -0
  254. package/src/assets/flags/vn.webp +0 -0
  255. package/src/assets/flags/vu.webp +0 -0
  256. package/src/assets/flags/wf.webp +0 -0
  257. package/src/assets/flags/ws.webp +0 -0
  258. package/src/assets/flags/xk.webp +0 -0
  259. package/src/assets/flags/ye.webp +0 -0
  260. package/src/assets/flags/yt.webp +0 -0
  261. package/src/assets/flags/za.webp +0 -0
  262. package/src/assets/flags/zm.webp +0 -0
  263. package/src/assets/flags/zw.webp +0 -0
  264. package/src/telpick.cdn.js +7 -0
  265. package/src/telpick.css +289 -0
  266. package/src/telpick.js +273 -0
  267. package/src/telpick.react.tsx +140 -0
  268. package/src/telpick.ts +157 -0
  269. package/src/telpick.vanilla.js +199 -0
  270. package/src/telpick.vue.ts +144 -0
  271. package/src/telpick.wrappers.jsx +37 -0
@@ -0,0 +1,199 @@
1
+ import { getDefaultCountry, useClickOutside, CountryCode, TelpickProps } from './telpick'
2
+ import './telpick.css'
3
+
4
+ export class TelpickVanilla {
5
+ constructor(options = {}) {
6
+ this.code = options.code || null
7
+ this.onChange = options.onChange || (() => {})
8
+ this.styleOverrides = options.styleOverrides || {}
9
+ this.codes = []
10
+ this.selectedCode = this.code
11
+ this.isDropdownOpen = false
12
+ this.searchQuery = ''
13
+ this.container = null
14
+ this.dropdown = null
15
+ this.searchInput = null
16
+ }
17
+
18
+ async init(container) {
19
+ this.container = container
20
+ const res = await fetch('/resources/api/country-codes.json')
21
+ const data = await res.json()
22
+ this.codes = data.sort((a, b) => a.country.localeCompare(b.country, 'es'))
23
+ if (!this.code) {
24
+ const def = await getDefaultCountry(this.codes)
25
+ if (def) this.selectedCode = def.country_code
26
+ }
27
+ this.render()
28
+ useClickOutside(this.container, () => {
29
+ this.closeDropdown()
30
+ })
31
+ }
32
+
33
+ render() {
34
+ if (this.isDropdownOpen && this.dropdown && this.searchInput) {
35
+ const ul = this.dropdown.querySelector('ul')
36
+ if (ul) {
37
+ ul.innerHTML = ''
38
+ const filtered = !this.searchQuery
39
+ ? this.codes
40
+ : this.codes.filter(c => c.country.toLowerCase().includes(this.searchQuery.toLowerCase()))
41
+
42
+ filtered.forEach((item, index) => {
43
+ const li = document.createElement('li')
44
+ const isSelected = item.country_code === this.selectedCode && this.selectedCode !== null && this.selectedCode !== undefined
45
+ li.className = `telpick-item ${isSelected ? 'telpick-item-selected' : ''}`
46
+ li.setAttribute('role', 'option')
47
+ li.setAttribute('aria-selected', isSelected)
48
+ li.onclick = () => {
49
+ this.selectedCode = item.country_code
50
+ this.onChange(item)
51
+ this.isDropdownOpen = false
52
+ this.searchQuery = ''
53
+ this.render()
54
+ }
55
+
56
+ const flag = document.createElement('div')
57
+ flag.className = 'telpick-flag'
58
+ const img = document.createElement('img')
59
+ img.src = item.flag
60
+ img.alt = item.country
61
+ flag.appendChild(img)
62
+ li.appendChild(flag)
63
+
64
+ const countrySpan = document.createElement('span')
65
+ countrySpan.textContent = item.country
66
+ li.appendChild(countrySpan)
67
+
68
+ const codeSpan = document.createElement('span')
69
+ codeSpan.className = 'ml-auto'
70
+ codeSpan.textContent = item.code
71
+ li.appendChild(codeSpan)
72
+
73
+ ul.appendChild(li)
74
+ })
75
+ }
76
+ return
77
+ }
78
+
79
+ this.container.innerHTML = ''
80
+ this.container.className = 'telpick-wrapper relative'
81
+
82
+ const btn = document.createElement('button')
83
+ btn.className = 'telpick-btn'
84
+ btn.setAttribute('aria-expanded', this.isDropdownOpen)
85
+ btn.setAttribute('aria-haspopup', 'listbox')
86
+ Object.assign(btn.style, this.styleOverrides)
87
+ btn.onclick = () => {
88
+ this.isDropdownOpen = !this.isDropdownOpen
89
+ this.render()
90
+ }
91
+
92
+ const flagDiv = document.createElement('div')
93
+ flagDiv.className = 'telpick-flag'
94
+ const selectedCountry = this.codes.find(c => c.country_code === this.selectedCode) || { flag: '', code: '', country: '' }
95
+ if (selectedCountry.flag) {
96
+ const img = document.createElement('img')
97
+ img.src = selectedCountry.flag
98
+ img.alt = selectedCountry.country || 'flag'
99
+ flagDiv.appendChild(img)
100
+ }
101
+ btn.appendChild(flagDiv)
102
+
103
+ const codeSpan = document.createElement('span')
104
+ codeSpan.textContent = selectedCountry.code
105
+ btn.appendChild(codeSpan)
106
+
107
+ const arrowSpan = document.createElement('span')
108
+ arrowSpan.className = 'telpick-arrow ml-auto'
109
+ arrowSpan.textContent = '▼'
110
+ btn.appendChild(arrowSpan)
111
+ this.container.appendChild(btn)
112
+
113
+ if (this.isDropdownOpen) {
114
+ this.dropdown = document.createElement('div')
115
+ this.dropdown.className = 'telpick-dropdown'
116
+ this.dropdown.setAttribute('role', 'listbox')
117
+ this.dropdown.onclick = e => e.stopPropagation()
118
+ this.dropdown.onmousedown = e => e.stopPropagation()
119
+
120
+ const input = document.createElement('input')
121
+ input.className = 'telpick-search'
122
+ input.type = 'text'
123
+ input.placeholder = 'Buscar país...'
124
+ input.value = this.searchQuery
125
+ input.autofocus = true
126
+ this.searchInput = input
127
+ input.oninput = e => {
128
+ e.stopPropagation()
129
+ const inputEl = e.target
130
+ const cursorPos = inputEl.selectionStart || 0
131
+ const newValue = inputEl.value
132
+ this.searchQuery = newValue
133
+ this.render()
134
+ requestAnimationFrame(() => {
135
+ if (this.searchInput) {
136
+ this.searchInput.focus()
137
+ const newCursorPos = Math.min(cursorPos + 1, newValue.length)
138
+ this.searchInput.setSelectionRange(newCursorPos, newCursorPos)
139
+ }
140
+ })
141
+ }
142
+ input.onclick = e => e.stopPropagation()
143
+ input.onmousedown = e => e.stopPropagation()
144
+ this.dropdown.appendChild(input)
145
+
146
+ const ul = document.createElement('ul')
147
+ const filtered = !this.searchQuery
148
+ ? this.codes
149
+ : this.codes.filter(c => c.country.toLowerCase().includes(this.searchQuery.toLowerCase()))
150
+
151
+ filtered.forEach((item, index) => {
152
+ const li = document.createElement('li')
153
+ const isSelected = item.country_code === this.selectedCode && this.selectedCode !== null && this.selectedCode !== undefined
154
+ li.className = `telpick-item ${isSelected ? 'telpick-item-selected' : ''}`
155
+ li.setAttribute('role', 'option')
156
+ li.setAttribute('aria-selected', isSelected)
157
+ li.onclick = () => {
158
+ this.selectedCode = item.country_code
159
+ this.onChange(item)
160
+ this.isDropdownOpen = false
161
+ this.searchQuery = ''
162
+ this.render()
163
+ }
164
+
165
+ const flag = document.createElement('div')
166
+ flag.className = 'telpick-flag'
167
+ const img = document.createElement('img')
168
+ img.src = item.flag
169
+ img.alt = item.country
170
+ flag.appendChild(img)
171
+ li.appendChild(flag)
172
+
173
+ const countrySpan = document.createElement('span')
174
+ countrySpan.textContent = item.country
175
+ li.appendChild(countrySpan)
176
+
177
+ const codeSpan = document.createElement('span')
178
+ codeSpan.className = 'ml-auto'
179
+ codeSpan.textContent = item.code
180
+ li.appendChild(codeSpan)
181
+
182
+ ul.appendChild(li)
183
+ })
184
+ this.dropdown.appendChild(ul)
185
+ this.container.appendChild(this.dropdown)
186
+
187
+ requestAnimationFrame(() => {
188
+ if (this.searchInput) {
189
+ this.searchInput.focus()
190
+ }
191
+ })
192
+ }
193
+ }
194
+
195
+ closeDropdown() {
196
+ this.isDropdownOpen = false
197
+ this.render()
198
+ }
199
+ }
@@ -0,0 +1,144 @@
1
+ import { defineComponent, ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
2
+ import { getDefaultCountry, useClickOutside, CountryCode, TelpickProps } from './telpick'
3
+ import './telpick.css'
4
+
5
+ export const TelpickVue = defineComponent({
6
+ name: 'TelpickVue',
7
+ props: {
8
+ code: { type: String, default: null },
9
+ styleOverrides: { type: Object, default: () => ({}) }
10
+ },
11
+ emits: ['update:code'],
12
+ setup(props, { emit }) {
13
+ const codes = ref<CountryCode[]>([])
14
+ const selectedCode = ref(props.code)
15
+ const isDropdownOpen = ref(false)
16
+ const searchQuery = ref('')
17
+ const dropdownRef = ref<HTMLElement | null>(null)
18
+ const searchInputRef = ref<HTMLInputElement | null>(null)
19
+
20
+ const handleSearchInput = (e: Event) => {
21
+ const input = e.target as HTMLInputElement
22
+ const cursorPos = input.selectionStart || 0
23
+ const newValue = input.value
24
+ searchQuery.value = newValue
25
+ requestAnimationFrame(() => {
26
+ if (searchInputRef.value) {
27
+ searchInputRef.value.focus()
28
+ const newCursorPos = Math.min(cursorPos + 1, newValue.length)
29
+ searchInputRef.value.setSelectionRange(newCursorPos, newCursorPos)
30
+ }
31
+ })
32
+ }
33
+
34
+ const selectCode = (item: CountryCode) => {
35
+ selectedCode.value = item.country_code
36
+ emit('update:code', item)
37
+ isDropdownOpen.value = false
38
+ searchQuery.value = ''
39
+ }
40
+
41
+ const selectedCountry = computed(() => {
42
+ return (
43
+ codes.value.find((c) => c.country_code === selectedCode.value) || {
44
+ country_code: selectedCode.value,
45
+ flag: '',
46
+ country: '',
47
+ code: '',
48
+ }
49
+ )
50
+ })
51
+
52
+ const filteredCodes = computed(() => {
53
+ if (!searchQuery.value) return codes.value
54
+ return codes.value.filter((c) =>
55
+ c.country.toLowerCase().includes(searchQuery.value.toLowerCase())
56
+ )
57
+ })
58
+
59
+ watch(() => props.code, (newCode) => {
60
+ if (newCode !== undefined && newCode !== null) {
61
+ selectedCode.value = newCode
62
+ }
63
+ })
64
+
65
+ watch(isDropdownOpen, async () => {
66
+ if (isDropdownOpen.value) {
67
+ await nextTick()
68
+ if (searchInputRef.value) {
69
+ searchInputRef.value.focus()
70
+ }
71
+ }
72
+ })
73
+
74
+ onMounted(async () => {
75
+ const response = await fetch('/resources/api/country-codes.json')
76
+ const data = await response.json()
77
+ codes.value = data.sort((a, b) => a.country.localeCompare(b.country, 'es'))
78
+ if (!props.code) {
79
+ const def = await getDefaultCountry(codes.value)
80
+ if (def) selectCode(def)
81
+ }
82
+ if (dropdownRef.value) {
83
+ const remove = useClickOutside(dropdownRef.value, () => {
84
+ isDropdownOpen.value = false
85
+ })
86
+ onUnmounted(remove)
87
+ }
88
+ })
89
+
90
+ return {
91
+ codes,
92
+ selectedCode,
93
+ isDropdownOpen,
94
+ searchQuery,
95
+ selectCode,
96
+ selectedCountry,
97
+ filteredCodes,
98
+ dropdownRef,
99
+ searchInputRef,
100
+ handleSearchInput
101
+ }
102
+ },
103
+ template: `
104
+ <div class="telpick-wrapper relative" ref="dropdownRef">
105
+ <button
106
+ @click="isDropdownOpen = !isDropdownOpen"
107
+ class="telpick-btn"
108
+ :style="styleOverrides"
109
+ :aria-expanded="isDropdownOpen"
110
+ aria-haspopup="listbox"
111
+ >
112
+ <div class="telpick-flag">
113
+ <img v-if="selectedCountry.flag" :src="selectedCountry.flag" :alt="selectedCountry.country || 'flag'" />
114
+ </div>
115
+ <span>{{ selectedCountry.code }}</span>
116
+ <span class="telpick-arrow ml-auto">▼</span>
117
+ </button>
118
+ <Transition name="telpick-dropdown">
119
+ <div v-if="isDropdownOpen" class="telpick-dropdown" role="listbox" @click.stop @mousedown.stop>
120
+ <input
121
+ ref="searchInputRef"
122
+ v-model="searchQuery"
123
+ type="text"
124
+ placeholder="Buscar país..."
125
+ class="telpick-search"
126
+ @input="handleSearchInput"
127
+ @click.stop
128
+ @mousedown.stop
129
+ autofocus
130
+ />
131
+ <ul>
132
+ <li v-for="(item, index) in filteredCodes" :key="\`\${item.country_code}-\${index}\`" @click="selectCode(item)" :class="['telpick-item', { 'telpick-item-selected': item.country_code === selectedCode && selectedCode !== null && selectedCode !== undefined }]" role="option" :aria-selected="item.country_code === selectedCode && selectedCode !== null && selectedCode !== undefined">
133
+ <div class="telpick-flag">
134
+ <img :src="item.flag" :alt="item.country" />
135
+ </div>
136
+ <span>{{ item.country }}</span>
137
+ <span class="ml-auto">{{ item.code }}</span>
138
+ </li>
139
+ </ul>
140
+ </div>
141
+ </Transition>
142
+ </div>
143
+ `
144
+ })
@@ -0,0 +1,37 @@
1
+ // Wrapper para Vue, React y CDN usando Vanilla JS
2
+ // Vue y React pueden usar el componente vanilla vía ref
3
+
4
+ // Vue wrapper
5
+ export function TelpickVue(props, { emit }) {
6
+ let telpickInstance = null
7
+ return {
8
+ mounted() {
9
+ telpickInstance = new window.Telpick({
10
+ code: props.code,
11
+ onChange: country => emit('update:code', country),
12
+ styleOverrides: props.styleOverrides || {}
13
+ })
14
+ telpickInstance.init(this.$el)
15
+ },
16
+ beforeUnmount() {
17
+ telpickInstance && telpickInstance.destroy()
18
+ },
19
+ render() {
20
+ return null // Render handled by vanilla
21
+ }
22
+ }
23
+ }
24
+
25
+ // React wrapper
26
+ import React, { useRef, useEffect } from 'react'
27
+ export function TelpickReact({ code, onChange, styleOverrides }) {
28
+ const ref = useRef()
29
+ useEffect(() => {
30
+ const telpickInstance = new window.Telpick({ code, onChange, styleOverrides })
31
+ telpickInstance.init(ref.current)
32
+ return () => telpickInstance.destroy()
33
+ }, [code, onChange, styleOverrides])
34
+ return <div ref={ref}></div>
35
+ }
36
+
37
+ // CDN usage: window.Telpick