@alikhalilll/a-tel-input 1.0.2 → 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.
Files changed (42) hide show
  1. package/.media/README.md +3 -0
  2. package/.media/hero.png +0 -0
  3. package/README.md +597 -72
  4. package/dist/_chunks/types.d.ts +661 -0
  5. package/dist/_chunks/types.js +52 -0
  6. package/dist/_chunks/types.js.map +1 -0
  7. package/dist/_chunks/usePhoneValidation.js +539 -0
  8. package/dist/_chunks/usePhoneValidation.js.map +1 -0
  9. package/dist/index.cjs +471 -695
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +122 -587
  12. package/dist/index.d.ts +122 -587
  13. package/dist/index.js +454 -658
  14. package/dist/index.js.map +1 -1
  15. package/dist/styles.css +20 -5
  16. package/dist/vee-validate/index.cjs +113 -0
  17. package/dist/vee-validate/index.cjs.map +1 -0
  18. package/dist/vee-validate/index.d.cts +86 -0
  19. package/dist/vee-validate/index.d.ts +86 -0
  20. package/dist/vee-validate/index.js +112 -0
  21. package/dist/vee-validate/index.js.map +1 -0
  22. package/dist/zod/index.cjs +211 -0
  23. package/dist/zod/index.cjs.map +1 -0
  24. package/dist/zod/index.d.cts +65 -0
  25. package/dist/zod/index.d.ts +65 -0
  26. package/dist/zod/index.js +208 -0
  27. package/dist/zod/index.js.map +1 -0
  28. package/package.json +34 -3
  29. package/src/components/ACountrySelect.vue +17 -3
  30. package/src/components/ATelInput.vue +206 -66
  31. package/src/composables/useCountryDetection.ts +28 -11
  32. package/src/composables/useCountryMatching.ts +160 -20
  33. package/src/composables/useCountrySelection.ts +71 -0
  34. package/src/composables/usePhoneValidation.ts +81 -18
  35. package/src/composables/useSyncedModel.ts +80 -0
  36. package/src/composables/useTelInputValidation.ts +50 -11
  37. package/src/index.ts +2 -0
  38. package/src/types.ts +80 -0
  39. package/src/vee-validate/index.ts +2 -0
  40. package/src/vee-validate/useTelField.ts +202 -0
  41. package/src/zod/index.ts +259 -0
  42. package/web-types.json +44 -1
package/dist/styles.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
2
- @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-outline-style:solid;--tw-ease:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}:root,:host{--spacing:.25rem;--ease-in:cubic-bezier(.4, 0, 1, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1)}.collapse{visibility:collapse}.flex{display:flex}.inline{display:inline}.table{display:table}.flex-shrink{flex-shrink:1}.ps-1{padding-inline-start:calc(var(--spacing) * 1)}.accent-foreground{accent-color:hsl(var(--ak-ui-foreground))}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.visible{visibility:visible}.sticky{position:sticky}.block{display:block}.hidden{display:none}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.border{border-style:var(--tw-border-style);border-width:1px}.text-popover-foreground{color:hsl(var(--ak-ui-popover-foreground))}@property --tw-ease{syntax:"*";inherits:false}.a-popover__overlay{z-index:50;pointer-events:auto;background:#000000b3;position:fixed;inset:0}.a-popover__overlay[data-state=open]{animation:.15s ease-out a-popover-overlay-in}.a-popover__overlay[data-state=closed]{animation:.1s ease-in forwards a-popover-overlay-out}@keyframes a-popover-overlay-in{0%{opacity:0}to{opacity:1}}@keyframes a-popover-overlay-out{0%{opacity:1}to{opacity:0}}@media (prefers-reduced-motion:reduce){.a-popover__overlay[data-state=open],.a-popover__overlay[data-state=closed]{animation:none}}.a-popover__content{z-index:60;border-radius:calc(var(--ak-ui-radius) - 2px);border:1px solid hsl(var(--ak-ui-border) / .7);background:hsl(var(--ak-ui-popover));width:18rem;color:hsl(var(--ak-ui-popover-foreground));outline:none;padding:1rem;box-shadow:0 20px 25px -5px #00000026,0 8px 10px -6px #00000026}.a-popover__content[data-state=open]{animation:.15s ease-out a-popover-in}.a-popover__content[data-state=closed]{animation:.1s ease-in forwards a-popover-out}@keyframes a-popover-in{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}@keyframes a-popover-out{0%{opacity:1}to{opacity:0}}@media (prefers-reduced-motion:reduce){.a-popover__content[data-state=open],.a-popover__content[data-state=closed]{animation:none}}:root,.light{--ak-ui-background:0 0% 100%;--ak-ui-foreground:240 10% 3.9%;--ak-ui-card:0 0% 100%;--ak-ui-card-foreground:240 10% 3.9%;--ak-ui-popover:0 0% 100%;--ak-ui-popover-foreground:240 10% 3.9%;--ak-ui-primary:240 5.9% 10%;--ak-ui-primary-foreground:0 0% 98%;--ak-ui-secondary:240 4.8% 95.9%;--ak-ui-secondary-foreground:240 5.9% 10%;--ak-ui-muted:240 4.8% 95.9%;--ak-ui-muted-foreground:240 3.8% 46.1%;--ak-ui-accent:240 4.8% 95.9%;--ak-ui-accent-foreground:240 5.9% 10%;--ak-ui-destructive:0 84.2% 60.2%;--ak-ui-destructive-foreground:0 0% 98%;--ak-ui-border:240 5.9% 90%;--ak-ui-input:240 5.9% 90%;--ak-ui-ring:240 5.9% 10%;--ak-ui-radius:.5rem}.dark{--ak-ui-background:240 10% 3.9%;--ak-ui-foreground:0 0% 98%;--ak-ui-card:240 10% 3.9%;--ak-ui-card-foreground:0 0% 98%;--ak-ui-popover:240 10% 3.9%;--ak-ui-popover-foreground:0 0% 98%;--ak-ui-primary:0 0% 98%;--ak-ui-primary-foreground:240 5.9% 10%;--ak-ui-secondary:240 3.7% 15.9%;--ak-ui-secondary-foreground:0 0% 98%;--ak-ui-muted:240 3.7% 15.9%;--ak-ui-muted-foreground:240 5% 64.9%;--ak-ui-accent:240 3.7% 15.9%;--ak-ui-accent-foreground:0 0% 98%;--ak-ui-destructive:0 62.8% 30.6%;--ak-ui-destructive-foreground:0 0% 98%;--ak-ui-border:240 3.7% 15.9%;--ak-ui-input:240 3.7% 15.9%;--ak-ui-ring:240 4.9% 83.9%}:root,:host{--ease-in:cubic-bezier(.4, 0, 1, 1)}.text-foreground{color:hsl(var(--ak-ui-foreground))}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.ease-in{--tw-ease:var(--ease-in);transition-timing-function:var(--ease-in)}.a-drawer__overlay{z-index:40;background:#000000b3;position:fixed;inset:0}.a-drawer__overlay[data-state=open]{animation:.2s ease-out a-drawer-overlay-in}.a-drawer__overlay[data-state=closed]{animation:.15s ease-in forwards a-drawer-overlay-out}@keyframes a-drawer-overlay-in{0%{opacity:0}to{opacity:1}}@keyframes a-drawer-overlay-out{0%{opacity:1}to{opacity:0}}@media (prefers-reduced-motion:reduce){.a-drawer__overlay[data-state=open],.a-drawer__overlay[data-state=closed]{animation:none}}.a-drawer__content{z-index:50;background:hsl(var(--ak-ui-background));height:auto;color:hsl(var(--ak-ui-foreground));inset-inline:0;border-top-left-radius:10px;border-top-right-radius:10px;outline:none;flex-direction:column;margin-top:6rem;display:flex;position:fixed;bottom:0;box-shadow:0 25px 50px -12px #0000004d,0 12px 24px -8px #00000040}.a-drawer__handle{background:hsl(var(--ak-ui-muted));border-radius:999px;width:100px;height:.5rem;margin:1rem auto 0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-outline-style:solid;--tw-ease:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}:root,:host{--spacing:.25rem;--ease-in:cubic-bezier(.4, 0, 1, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1)}.collapse{visibility:collapse}.fixed{position:fixed}.static{position:static}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.contents{display:contents}.flex{display:flex}.inline{display:inline}.table{display:table}.flex-shrink{flex-shrink:1}.ps-1{padding-inline-start:calc(var(--spacing) * 1)}.accent-foreground{accent-color:hsl(var(--ak-ui-foreground))}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.visible{visibility:visible}.sticky{position:sticky}.block{display:block}.hidden{display:none}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.border{border-style:var(--tw-border-style);border-width:1px}.text-popover-foreground{color:hsl(var(--ak-ui-popover-foreground))}@property --tw-ease{syntax:"*";inherits:false}.a-popover__overlay{z-index:50;pointer-events:auto;background:#000000b3;position:fixed;inset:0}.a-popover__overlay[data-state=open]{animation:.15s ease-out a-popover-overlay-in}.a-popover__overlay[data-state=closed]{animation:.1s ease-in forwards a-popover-overlay-out}@keyframes a-popover-overlay-in{0%{opacity:0}to{opacity:1}}@keyframes a-popover-overlay-out{0%{opacity:1}to{opacity:0}}@media (prefers-reduced-motion:reduce){.a-popover__overlay[data-state=open],.a-popover__overlay[data-state=closed]{animation:none}}.a-popover__content{z-index:60;border-radius:calc(var(--ak-ui-radius) - 2px);border:1px solid hsl(var(--ak-ui-border) / .7);background:hsl(var(--ak-ui-popover));width:18rem;color:hsl(var(--ak-ui-popover-foreground));outline:none;padding:1rem;box-shadow:0 20px 25px -5px #00000026,0 8px 10px -6px #00000026}.a-popover__content[data-state=open]{animation:.15s ease-out a-popover-in}.a-popover__content[data-state=closed]{animation:.1s ease-in forwards a-popover-out}@keyframes a-popover-in{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}@keyframes a-popover-out{0%{opacity:1}to{opacity:0}}@media (prefers-reduced-motion:reduce){.a-popover__content[data-state=open],.a-popover__content[data-state=closed]{animation:none}}:root,.light{--ak-ui-background:0 0% 100%;--ak-ui-foreground:240 10% 3.9%;--ak-ui-card:0 0% 100%;--ak-ui-card-foreground:240 10% 3.9%;--ak-ui-popover:0 0% 100%;--ak-ui-popover-foreground:240 10% 3.9%;--ak-ui-primary:240 5.9% 10%;--ak-ui-primary-foreground:0 0% 98%;--ak-ui-secondary:240 4.8% 95.9%;--ak-ui-secondary-foreground:240 5.9% 10%;--ak-ui-muted:240 4.8% 95.9%;--ak-ui-muted-foreground:240 3.8% 46.1%;--ak-ui-accent:240 4.8% 95.9%;--ak-ui-accent-foreground:240 5.9% 10%;--ak-ui-destructive:0 84.2% 60.2%;--ak-ui-destructive-foreground:0 0% 98%;--ak-ui-border:240 5.9% 90%;--ak-ui-input:240 5.9% 90%;--ak-ui-ring:240 5.9% 10%;--ak-ui-radius:.5rem}.dark{--ak-ui-background:240 10% 3.9%;--ak-ui-foreground:0 0% 98%;--ak-ui-card:240 10% 3.9%;--ak-ui-card-foreground:0 0% 98%;--ak-ui-popover:240 10% 3.9%;--ak-ui-popover-foreground:0 0% 98%;--ak-ui-primary:0 0% 98%;--ak-ui-primary-foreground:240 5.9% 10%;--ak-ui-secondary:240 3.7% 15.9%;--ak-ui-secondary-foreground:0 0% 98%;--ak-ui-muted:240 3.7% 15.9%;--ak-ui-muted-foreground:240 5% 64.9%;--ak-ui-accent:240 3.7% 15.9%;--ak-ui-accent-foreground:0 0% 98%;--ak-ui-destructive:0 62.8% 30.6%;--ak-ui-destructive-foreground:0 0% 98%;--ak-ui-border:240 3.7% 15.9%;--ak-ui-input:240 3.7% 15.9%;--ak-ui-ring:240 4.9% 83.9%}:root,:host{--ease-in:cubic-bezier(.4, 0, 1, 1)}.text-foreground{color:hsl(var(--ak-ui-foreground))}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.ease-in{--tw-ease:var(--ease-in);transition-timing-function:var(--ease-in)}.a-drawer__overlay{z-index:40;background:#000000b3;position:fixed;inset:0}.a-drawer__overlay[data-state=open]{animation:.2s ease-out a-drawer-overlay-in}.a-drawer__overlay[data-state=closed]{animation:.15s ease-in forwards a-drawer-overlay-out}@keyframes a-drawer-overlay-in{0%{opacity:0}to{opacity:1}}@keyframes a-drawer-overlay-out{0%{opacity:1}to{opacity:0}}@media (prefers-reduced-motion:reduce){.a-drawer__overlay[data-state=open],.a-drawer__overlay[data-state=closed]{animation:none}}.a-drawer__content{z-index:50;background:hsl(var(--ak-ui-background));height:auto;color:hsl(var(--ak-ui-foreground));inset-inline:0;border-top-left-radius:10px;border-top-right-radius:10px;outline:none;flex-direction:column;margin-top:6rem;display:flex;position:fixed;bottom:0;box-shadow:0 25px 50px -12px #0000004d,0 12px 24px -8px #00000040}.a-drawer__handle{background:hsl(var(--ak-ui-muted));border-radius:999px;width:100px;height:.5rem;margin:1rem auto 0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}
3
3
  /* --- bundled SFC styles --- */
4
4
 
5
5
  .a-country-flag[data-v-705c2224] {
@@ -112,7 +112,9 @@ to {
112
112
  }
113
113
  .a-country-select__search {
114
114
  border-bottom: 1px solid hsl(var(--ak-ui-border) / 0.7);
115
- padding: 0.375rem;
115
+ /* Drop bottom padding — the list owns the gap below the search border so the
116
+ sticky group header can overlap it and sit flush against the search bar. */
117
+ padding: 0.375rem 0.375rem 0;
116
118
  }
117
119
  .a-country-select__search-box {
118
120
  position: relative;
@@ -179,6 +181,10 @@ to {
179
181
  .a-country-select__list {
180
182
  flex: 1;
181
183
  overflow-y: auto;
184
+ /* Top padding lives inside the scroll container so the first sticky group header
185
+ can overlap it (via negative `top`) and sit flush against the search border
186
+ with zero visible gap. */
187
+ padding-top: 0.375rem;
182
188
  /* Themed scrollbar — Firefox + WebKit/Blink. Resolves the browser-default
183
189
  light-grey scrollbar that didn't match the popover surface in dark mode. */
184
190
  scrollbar-width: thin;
@@ -207,16 +213,24 @@ to {
207
213
  }
208
214
  .a-country-select__group-header {
209
215
  position: sticky;
210
- top: 0;
216
+ /* Negative top equal to the list's `padding-top` makes the sticky header
217
+ overlap that padding band so when the user scrolls it sits flush against
218
+ the search bar's bottom border — no visible gap. */
219
+ top: -0.375rem;
211
220
  z-index: 10;
212
221
  background: hsl(var(--ak-ui-popover));
213
222
  color: hsl(var(--ak-ui-muted-foreground));
214
- padding: 0.375rem 0.75rem;
223
+ /* Extra top padding compensates for the negative `top` offset so the visible
224
+ label keeps its usual breathing room. */
225
+ padding: 0.5rem 0.75rem 0.375rem;
215
226
  font-size: 10px;
216
227
  font-weight: 500;
217
228
  letter-spacing: 0.05em;
218
229
  text-transform: uppercase;
219
230
  margin: 0;
231
+ /* Hairline under the header that only shows once it's stuck — helps it read
232
+ as a distinct band from the search border above. */
233
+ box-shadow: 0 1px 0 0 hsl(var(--ak-ui-border) / 0.5);
220
234
  }
221
235
  .a-country-select__group-list {
222
236
  list-style: none;
@@ -411,7 +425,8 @@ to {
411
425
  .a-tel-input__input[data-has-dial][data-v-8305dc81] {
412
426
  padding-inline-start: 0.25rem;
413
427
  }
414
- .a-tel-input__detecting[data-v-8305dc81] {
428
+ .a-tel-input__detecting[data-v-8305dc81],
429
+ .a-tel-input__validating[data-v-8305dc81] {
415
430
  display: inline-flex;
416
431
  height: 100%;
417
432
  flex-shrink: 0;
@@ -0,0 +1,113 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_usePhoneValidation = require("../_chunks/usePhoneValidation.js");
3
+ let vue = require("vue");
4
+ let vee_validate = require("vee-validate");
5
+ //#region src/vee-validate/useTelField.ts
6
+ /**
7
+ * VeeValidate adapter for `@alikhalilll/a-tel-input`.
8
+ *
9
+ * `useTelField()` owns the two v-models (`phone` + `country`) and the canonical
10
+ * E.164 string used by VeeValidate / Zod / yup, and returns a single ready-to-bind
11
+ * object so a consumer doesn't have to glue the pieces together.
12
+ *
13
+ * Quick start:
14
+ *
15
+ * ```ts
16
+ * import { useTelField } from '@alikhalilll/a-tel-input/vee-validate';
17
+ * import { toTypedSchema } from '@vee-validate/zod';
18
+ * import { z } from 'zod';
19
+ * import { zPhone } from '@alikhalilll/a-tel-input/zod';
20
+ *
21
+ * const { phone, country, error, handleBlur, fieldProps } = useTelField('phone', {
22
+ * rules: toTypedSchema(zPhone()),
23
+ * validateOn: 'blur',
24
+ * });
25
+ * ```
26
+ *
27
+ * ```vue
28
+ * <ATelInput
29
+ * v-model:phone="phone"
30
+ * v-model:country="country"
31
+ * v-bind="fieldProps"
32
+ * @blur="handleBlur"
33
+ * />
34
+ * ```
35
+ *
36
+ * Server-side validation (e.g. "does this phone already exist?") plugs in via the
37
+ * `rules` callback — VeeValidate already supports async rules:
38
+ *
39
+ * ```ts
40
+ * const { phone, country, error, handleBlur, fieldProps, validating } = useTelField('phone', {
41
+ * rules: async (value: string) => {
42
+ * const sync = await zPhone().safeParseAsync(value);
43
+ * if (!sync.success) return sync.error.issues[0]!.message;
44
+ * const res = await $fetch('/api/phone/exists', { query: { phone: value } });
45
+ * return res.exists ? 'This phone is already registered.' : true;
46
+ * },
47
+ * validateOn: 'blur',
48
+ * });
49
+ * ```
50
+ *
51
+ * The `validating` ref is `true` while VeeValidate's async pipeline is in flight —
52
+ * bind it to ATelInput's `:validating` prop to surface a spinner inside the field.
53
+ *
54
+ * `vee-validate` is an **optional peer dependency** — install it yourself in your app.
55
+ */
56
+ /**
57
+ * Register a phone field with VeeValidate. See file header for usage examples.
58
+ *
59
+ * @param name The field name (used by VeeValidate, also forwarded to the inner
60
+ * `<input name="">` for native form submission).
61
+ * @param options See {@link UseTelFieldOptions}.
62
+ */
63
+ function useTelField(name, options = {}) {
64
+ const phone = (0, vue.ref)(options.initialPhone ?? "");
65
+ const country = (0, vue.ref)(options.initialCountry ?? null);
66
+ const v = require_usePhoneValidation.usePhoneValidation();
67
+ v.getCountries();
68
+ function composeE164() {
69
+ if (!phone.value) return "";
70
+ const dial = country.value;
71
+ if (dial == null) return "";
72
+ const iso2 = v.getCountriesByDial(String(dial))[0]?.value;
73
+ if (!iso2) return "";
74
+ return v.validate({
75
+ country: { iso2 },
76
+ phone: phone.value
77
+ }).full_phone ?? "";
78
+ }
79
+ const initialValue = composeE164();
80
+ const field = (0, vee_validate.useField)(name, options.rules, {
81
+ initialValue,
82
+ validateOnValueUpdate: false,
83
+ ...options.fieldOptions
84
+ });
85
+ (0, vue.watch)([phone, country], () => {
86
+ field.value.value = composeE164();
87
+ }, { flush: "post" });
88
+ const validating = (0, vue.computed)(() => !!field.meta.pending);
89
+ const fieldProps = (0, vue.computed)(() => ({
90
+ name: (0, vue.toValue)(name),
91
+ error: field.errorMessage.value ?? null,
92
+ validateOn: options.validateOn ?? "blur",
93
+ validating: validating.value,
94
+ defaultCountry: options.defaultCountry
95
+ }));
96
+ return {
97
+ phone,
98
+ country,
99
+ e164: (0, vue.computed)(() => field.value.value ?? ""),
100
+ error: field.errorMessage,
101
+ validating,
102
+ meta: field.meta,
103
+ handleBlur: field.handleBlur,
104
+ validate: field.validate,
105
+ setErrors: field.setErrors,
106
+ resetField: field.resetField,
107
+ fieldProps
108
+ };
109
+ }
110
+ //#endregion
111
+ exports.useTelField = useTelField;
112
+
113
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["usePhoneValidation"],"sources":["../../src/vee-validate/useTelField.ts"],"sourcesContent":["/**\n * VeeValidate adapter for `@alikhalilll/a-tel-input`.\n *\n * `useTelField()` owns the two v-models (`phone` + `country`) and the canonical\n * E.164 string used by VeeValidate / Zod / yup, and returns a single ready-to-bind\n * object so a consumer doesn't have to glue the pieces together.\n *\n * Quick start:\n *\n * ```ts\n * import { useTelField } from '@alikhalilll/a-tel-input/vee-validate';\n * import { toTypedSchema } from '@vee-validate/zod';\n * import { z } from 'zod';\n * import { zPhone } from '@alikhalilll/a-tel-input/zod';\n *\n * const { phone, country, error, handleBlur, fieldProps } = useTelField('phone', {\n * rules: toTypedSchema(zPhone()),\n * validateOn: 'blur',\n * });\n * ```\n *\n * ```vue\n * <ATelInput\n * v-model:phone=\"phone\"\n * v-model:country=\"country\"\n * v-bind=\"fieldProps\"\n * @blur=\"handleBlur\"\n * />\n * ```\n *\n * Server-side validation (e.g. \"does this phone already exist?\") plugs in via the\n * `rules` callback — VeeValidate already supports async rules:\n *\n * ```ts\n * const { phone, country, error, handleBlur, fieldProps, validating } = useTelField('phone', {\n * rules: async (value: string) => {\n * const sync = await zPhone().safeParseAsync(value);\n * if (!sync.success) return sync.error.issues[0]!.message;\n * const res = await $fetch('/api/phone/exists', { query: { phone: value } });\n * return res.exists ? 'This phone is already registered.' : true;\n * },\n * validateOn: 'blur',\n * });\n * ```\n *\n * The `validating` ref is `true` while VeeValidate's async pipeline is in flight —\n * bind it to ATelInput's `:validating` prop to surface a spinner inside the field.\n *\n * `vee-validate` is an **optional peer dependency** — install it yourself in your app.\n */\n\nimport { computed, ref, watch, type ComputedRef, type MaybeRefOrGetter } from 'vue';\nimport { toValue } from 'vue';\nimport { useField, type FieldOptions, type RuleExpression } from 'vee-validate';\nimport { usePhoneValidation } from '../composables/usePhoneValidation';\nimport type { ATelInputValidateOn } from '../composables/useTelInputValidation';\n\nexport interface UseTelFieldOptions {\n /**\n * VeeValidate rules — a function, schema, or string. Use when the field stands\n * alone (no `useForm` / no form-level `validationSchema`).\n *\n * **Important**: vee-validate **ignores field-level `rules` whenever the parent\n * `useForm` is configured with `validationSchema`**. If you need async / server-\n * side validation inside a form, chain it onto the form schema via\n * `z.refine(async)` instead — that's what `handleSubmit` actually awaits and what\n * drives this composable's `validating` ref (via `meta.pending`). See the README\n * section \"Server-side validation\" for the full pattern.\n */\n rules?: RuleExpression<string>;\n /**\n * Initial digits-only national number, e.g. `'1066105963'`. Defaults to `''`.\n */\n initialPhone?: string;\n /**\n * Initial dial-digit number, e.g. `20` for Egypt. Defaults to `null`.\n */\n initialCountry?: number | null;\n /**\n * Default country (ISO2 like `'EG'` or dial code like `'20'`). Forwarded as the\n * `defaultCountry` prop on `<ATelInput>` via `fieldProps`.\n */\n defaultCountry?: string;\n /**\n * When to surface validation in the UI. Defaults to `'blur'` (the typical form-library\n * UX). Forwarded as the `validateOn` prop on `<ATelInput>` via `fieldProps`.\n */\n validateOn?: ATelInputValidateOn;\n /**\n * Forwarded to `useField` — extra options passed verbatim to VeeValidate. Use to\n * configure `keepValueOnUnmount`, `syncVModel`, etc.\n */\n fieldOptions?: Omit<FieldOptions<string>, 'initialValue'>;\n}\n\nexport interface UseTelFieldReturn {\n /** `v-model:phone` source — digits-only national number. Bind to `<ATelInput>`. */\n phone: import('vue').Ref<string>;\n /** `v-model:country` source — dial-digit number (e.g., `20`). Bind to `<ATelInput>`. */\n country: import('vue').Ref<number | null>;\n /** The canonical E.164 string fed to VeeValidate's schema (read-only). */\n e164: ComputedRef<string>;\n /** Current validation error message, or `undefined` when valid. From `useField`. */\n error: import('vue').Ref<string | undefined>;\n /** `true` while VeeValidate is running an async rule (e.g. server-side check). */\n validating: ComputedRef<boolean>;\n /** Whether the field has been blurred / dirtied / submitted (from VeeValidate `meta`). */\n meta: ReturnType<typeof useField<string>>['meta'];\n /** Forward this to `<ATelInput @blur=\"handleBlur\">` so VeeValidate's blur trigger fires. */\n handleBlur: ReturnType<typeof useField<string>>['handleBlur'];\n /** Imperatively trigger validation (e.g., after a programmatic value change). */\n validate: ReturnType<typeof useField<string>>['validate'];\n /** Imperatively set the error message — useful for server errors not raised by `rules`. */\n setErrors: ReturnType<typeof useField<string>>['setErrors'];\n /** Reset the field to its initial state. */\n resetField: ReturnType<typeof useField<string>>['resetField'];\n /**\n * Ready-to-bind prop bag for `<ATelInput v-bind=\"fieldProps\">`. Carries `name`,\n * `error`, `validateOn`, `validating`, and `defaultCountry`.\n */\n fieldProps: ComputedRef<{\n name: string;\n error: string | null;\n validateOn: ATelInputValidateOn;\n validating: boolean;\n defaultCountry?: string;\n }>;\n}\n\n/**\n * Register a phone field with VeeValidate. See file header for usage examples.\n *\n * @param name The field name (used by VeeValidate, also forwarded to the inner\n * `<input name=\"\">` for native form submission).\n * @param options See {@link UseTelFieldOptions}.\n */\nexport function useTelField(\n name: MaybeRefOrGetter<string>,\n options: UseTelFieldOptions = {}\n): UseTelFieldReturn {\n const phone = ref<string>(options.initialPhone ?? '');\n const country = ref<number | null>(options.initialCountry ?? null);\n\n const v = usePhoneValidation();\n void v.getCountries();\n\n // Compose E.164 from the two v-models — this is the canonical value VeeValidate\n // tracks. NANP (`+1`) resolves to `'US'` for validation; libphonenumber-js applies\n // the same rule set to every NANP country so this is correct.\n function composeE164(): string {\n if (!phone.value) return '';\n const dial = country.value;\n if (dial == null) return '';\n const matches = v.getCountriesByDial(String(dial));\n const iso2 = matches[0]?.value;\n if (!iso2) return '';\n const res = v.validate({ country: { iso2 }, phone: phone.value });\n return res.full_phone ?? '';\n }\n\n const initialValue = composeE164();\n\n const field = useField<string>(name, options.rules, {\n initialValue,\n // Don't run rules on every keystroke — let validateOn drive when validation fires.\n validateOnValueUpdate: false,\n ...options.fieldOptions,\n });\n\n // Keep VeeValidate's value in sync with the two v-models.\n watch(\n [phone, country],\n () => {\n field.value.value = composeE164();\n },\n { flush: 'post' }\n );\n\n const validating = computed(() => !!field.meta.pending);\n\n const fieldProps = computed(() => ({\n name: toValue(name),\n error: (field.errorMessage.value ?? null) as string | null,\n validateOn: options.validateOn ?? ('blur' as ATelInputValidateOn),\n validating: validating.value,\n defaultCountry: options.defaultCountry,\n }));\n\n return {\n phone,\n country,\n e164: computed(() => field.value.value ?? ''),\n error: field.errorMessage,\n validating,\n meta: field.meta,\n handleBlur: field.handleBlur,\n validate: field.validate,\n setErrors: field.setErrors,\n resetField: field.resetField,\n fieldProps,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwIA,SAAgB,YACd,MACA,UAA8B,CAAC,GACZ;CACnB,MAAM,SAAA,GAAA,IAAA,KAAoB,QAAQ,gBAAgB,EAAE;CACpD,MAAM,WAAA,GAAA,IAAA,KAA6B,QAAQ,kBAAkB,IAAI;CAEjE,MAAM,IAAIA,2BAAAA,mBAAmB;CAC7B,EAAO,aAAa;CAKpB,SAAS,cAAsB;EAC7B,IAAI,CAAC,MAAM,OAAO,OAAO;EACzB,MAAM,OAAO,QAAQ;EACrB,IAAI,QAAQ,MAAM,OAAO;EAEzB,MAAM,OADU,EAAE,mBAAmB,OAAO,IAAI,CAC7B,EAAE,IAAI;EACzB,IAAI,CAAC,MAAM,OAAO;EAElB,OADY,EAAE,SAAS;GAAE,SAAS,EAAE,KAAK;GAAG,OAAO,MAAM;EAAM,CACtD,EAAE,cAAc;CAC3B;CAEA,MAAM,eAAe,YAAY;CAEjC,MAAM,SAAA,GAAA,aAAA,UAAyB,MAAM,QAAQ,OAAO;EAClD;EAEA,uBAAuB;EACvB,GAAG,QAAQ;CACb,CAAC;CAGD,CAAA,GAAA,IAAA,OACE,CAAC,OAAO,OAAO,SACT;EACJ,MAAM,MAAM,QAAQ,YAAY;CAClC,GACA,EAAE,OAAO,OAAO,CAClB;CAEA,MAAM,cAAA,GAAA,IAAA,gBAA4B,CAAC,CAAC,MAAM,KAAK,OAAO;CAEtD,MAAM,cAAA,GAAA,IAAA,iBAA6B;EACjC,OAAA,GAAA,IAAA,SAAc,IAAI;EAClB,OAAQ,MAAM,aAAa,SAAS;EACpC,YAAY,QAAQ,cAAe;EACnC,YAAY,WAAW;EACvB,gBAAgB,QAAQ;CAC1B,EAAE;CAEF,OAAO;EACL;EACA;EACA,OAAA,GAAA,IAAA,gBAAqB,MAAM,MAAM,SAAS,EAAE;EAC5C,OAAO,MAAM;EACb;EACA,MAAM,MAAM;EACZ,YAAY,MAAM;EAClB,UAAU,MAAM;EAChB,WAAW,MAAM;EACjB,YAAY,MAAM;EAClB;CACF;AACF"}
@@ -0,0 +1,86 @@
1
+ import { b as ATelInputValidateOn } from "../_chunks/types.js";
2
+ import { ComputedRef, MaybeRefOrGetter } from "vue";
3
+ import { FieldOptions, RuleExpression, useField } from "vee-validate";
4
+
5
+ //#region src/vee-validate/useTelField.d.ts
6
+ interface UseTelFieldOptions {
7
+ /**
8
+ * VeeValidate rules — a function, schema, or string. Use when the field stands
9
+ * alone (no `useForm` / no form-level `validationSchema`).
10
+ *
11
+ * **Important**: vee-validate **ignores field-level `rules` whenever the parent
12
+ * `useForm` is configured with `validationSchema`**. If you need async / server-
13
+ * side validation inside a form, chain it onto the form schema via
14
+ * `z.refine(async)` instead — that's what `handleSubmit` actually awaits and what
15
+ * drives this composable's `validating` ref (via `meta.pending`). See the README
16
+ * section "Server-side validation" for the full pattern.
17
+ */
18
+ rules?: RuleExpression<string>;
19
+ /**
20
+ * Initial digits-only national number, e.g. `'1066105963'`. Defaults to `''`.
21
+ */
22
+ initialPhone?: string;
23
+ /**
24
+ * Initial dial-digit number, e.g. `20` for Egypt. Defaults to `null`.
25
+ */
26
+ initialCountry?: number | null;
27
+ /**
28
+ * Default country (ISO2 like `'EG'` or dial code like `'20'`). Forwarded as the
29
+ * `defaultCountry` prop on `<ATelInput>` via `fieldProps`.
30
+ */
31
+ defaultCountry?: string;
32
+ /**
33
+ * When to surface validation in the UI. Defaults to `'blur'` (the typical form-library
34
+ * UX). Forwarded as the `validateOn` prop on `<ATelInput>` via `fieldProps`.
35
+ */
36
+ validateOn?: ATelInputValidateOn;
37
+ /**
38
+ * Forwarded to `useField` — extra options passed verbatim to VeeValidate. Use to
39
+ * configure `keepValueOnUnmount`, `syncVModel`, etc.
40
+ */
41
+ fieldOptions?: Omit<FieldOptions<string>, 'initialValue'>;
42
+ }
43
+ interface UseTelFieldReturn {
44
+ /** `v-model:phone` source — digits-only national number. Bind to `<ATelInput>`. */
45
+ phone: import('vue').Ref<string>;
46
+ /** `v-model:country` source — dial-digit number (e.g., `20`). Bind to `<ATelInput>`. */
47
+ country: import('vue').Ref<number | null>;
48
+ /** The canonical E.164 string fed to VeeValidate's schema (read-only). */
49
+ e164: ComputedRef<string>;
50
+ /** Current validation error message, or `undefined` when valid. From `useField`. */
51
+ error: import('vue').Ref<string | undefined>;
52
+ /** `true` while VeeValidate is running an async rule (e.g. server-side check). */
53
+ validating: ComputedRef<boolean>;
54
+ /** Whether the field has been blurred / dirtied / submitted (from VeeValidate `meta`). */
55
+ meta: ReturnType<typeof useField<string>>['meta'];
56
+ /** Forward this to `<ATelInput @blur="handleBlur">` so VeeValidate's blur trigger fires. */
57
+ handleBlur: ReturnType<typeof useField<string>>['handleBlur'];
58
+ /** Imperatively trigger validation (e.g., after a programmatic value change). */
59
+ validate: ReturnType<typeof useField<string>>['validate'];
60
+ /** Imperatively set the error message — useful for server errors not raised by `rules`. */
61
+ setErrors: ReturnType<typeof useField<string>>['setErrors'];
62
+ /** Reset the field to its initial state. */
63
+ resetField: ReturnType<typeof useField<string>>['resetField'];
64
+ /**
65
+ * Ready-to-bind prop bag for `<ATelInput v-bind="fieldProps">`. Carries `name`,
66
+ * `error`, `validateOn`, `validating`, and `defaultCountry`.
67
+ */
68
+ fieldProps: ComputedRef<{
69
+ name: string;
70
+ error: string | null;
71
+ validateOn: ATelInputValidateOn;
72
+ validating: boolean;
73
+ defaultCountry?: string;
74
+ }>;
75
+ }
76
+ /**
77
+ * Register a phone field with VeeValidate. See file header for usage examples.
78
+ *
79
+ * @param name The field name (used by VeeValidate, also forwarded to the inner
80
+ * `<input name="">` for native form submission).
81
+ * @param options See {@link UseTelFieldOptions}.
82
+ */
83
+ declare function useTelField(name: MaybeRefOrGetter<string>, options?: UseTelFieldOptions): UseTelFieldReturn;
84
+ //#endregion
85
+ export { type UseTelFieldOptions, type UseTelFieldReturn, useTelField };
86
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1,86 @@
1
+ import { b as ATelInputValidateOn } from "../_chunks/types.js";
2
+ import { ComputedRef, MaybeRefOrGetter } from "vue";
3
+ import { FieldOptions, RuleExpression, useField } from "vee-validate";
4
+
5
+ //#region src/vee-validate/useTelField.d.ts
6
+ interface UseTelFieldOptions {
7
+ /**
8
+ * VeeValidate rules — a function, schema, or string. Use when the field stands
9
+ * alone (no `useForm` / no form-level `validationSchema`).
10
+ *
11
+ * **Important**: vee-validate **ignores field-level `rules` whenever the parent
12
+ * `useForm` is configured with `validationSchema`**. If you need async / server-
13
+ * side validation inside a form, chain it onto the form schema via
14
+ * `z.refine(async)` instead — that's what `handleSubmit` actually awaits and what
15
+ * drives this composable's `validating` ref (via `meta.pending`). See the README
16
+ * section "Server-side validation" for the full pattern.
17
+ */
18
+ rules?: RuleExpression<string>;
19
+ /**
20
+ * Initial digits-only national number, e.g. `'1066105963'`. Defaults to `''`.
21
+ */
22
+ initialPhone?: string;
23
+ /**
24
+ * Initial dial-digit number, e.g. `20` for Egypt. Defaults to `null`.
25
+ */
26
+ initialCountry?: number | null;
27
+ /**
28
+ * Default country (ISO2 like `'EG'` or dial code like `'20'`). Forwarded as the
29
+ * `defaultCountry` prop on `<ATelInput>` via `fieldProps`.
30
+ */
31
+ defaultCountry?: string;
32
+ /**
33
+ * When to surface validation in the UI. Defaults to `'blur'` (the typical form-library
34
+ * UX). Forwarded as the `validateOn` prop on `<ATelInput>` via `fieldProps`.
35
+ */
36
+ validateOn?: ATelInputValidateOn;
37
+ /**
38
+ * Forwarded to `useField` — extra options passed verbatim to VeeValidate. Use to
39
+ * configure `keepValueOnUnmount`, `syncVModel`, etc.
40
+ */
41
+ fieldOptions?: Omit<FieldOptions<string>, 'initialValue'>;
42
+ }
43
+ interface UseTelFieldReturn {
44
+ /** `v-model:phone` source — digits-only national number. Bind to `<ATelInput>`. */
45
+ phone: import('vue').Ref<string>;
46
+ /** `v-model:country` source — dial-digit number (e.g., `20`). Bind to `<ATelInput>`. */
47
+ country: import('vue').Ref<number | null>;
48
+ /** The canonical E.164 string fed to VeeValidate's schema (read-only). */
49
+ e164: ComputedRef<string>;
50
+ /** Current validation error message, or `undefined` when valid. From `useField`. */
51
+ error: import('vue').Ref<string | undefined>;
52
+ /** `true` while VeeValidate is running an async rule (e.g. server-side check). */
53
+ validating: ComputedRef<boolean>;
54
+ /** Whether the field has been blurred / dirtied / submitted (from VeeValidate `meta`). */
55
+ meta: ReturnType<typeof useField<string>>['meta'];
56
+ /** Forward this to `<ATelInput @blur="handleBlur">` so VeeValidate's blur trigger fires. */
57
+ handleBlur: ReturnType<typeof useField<string>>['handleBlur'];
58
+ /** Imperatively trigger validation (e.g., after a programmatic value change). */
59
+ validate: ReturnType<typeof useField<string>>['validate'];
60
+ /** Imperatively set the error message — useful for server errors not raised by `rules`. */
61
+ setErrors: ReturnType<typeof useField<string>>['setErrors'];
62
+ /** Reset the field to its initial state. */
63
+ resetField: ReturnType<typeof useField<string>>['resetField'];
64
+ /**
65
+ * Ready-to-bind prop bag for `<ATelInput v-bind="fieldProps">`. Carries `name`,
66
+ * `error`, `validateOn`, `validating`, and `defaultCountry`.
67
+ */
68
+ fieldProps: ComputedRef<{
69
+ name: string;
70
+ error: string | null;
71
+ validateOn: ATelInputValidateOn;
72
+ validating: boolean;
73
+ defaultCountry?: string;
74
+ }>;
75
+ }
76
+ /**
77
+ * Register a phone field with VeeValidate. See file header for usage examples.
78
+ *
79
+ * @param name The field name (used by VeeValidate, also forwarded to the inner
80
+ * `<input name="">` for native form submission).
81
+ * @param options See {@link UseTelFieldOptions}.
82
+ */
83
+ declare function useTelField(name: MaybeRefOrGetter<string>, options?: UseTelFieldOptions): UseTelFieldReturn;
84
+ //#endregion
85
+ export { type UseTelFieldOptions, type UseTelFieldReturn, useTelField };
86
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,112 @@
1
+ import { n as usePhoneValidation } from "../_chunks/usePhoneValidation.js";
2
+ import { computed, ref, toValue, watch } from "vue";
3
+ import { useField } from "vee-validate";
4
+ //#region src/vee-validate/useTelField.ts
5
+ /**
6
+ * VeeValidate adapter for `@alikhalilll/a-tel-input`.
7
+ *
8
+ * `useTelField()` owns the two v-models (`phone` + `country`) and the canonical
9
+ * E.164 string used by VeeValidate / Zod / yup, and returns a single ready-to-bind
10
+ * object so a consumer doesn't have to glue the pieces together.
11
+ *
12
+ * Quick start:
13
+ *
14
+ * ```ts
15
+ * import { useTelField } from '@alikhalilll/a-tel-input/vee-validate';
16
+ * import { toTypedSchema } from '@vee-validate/zod';
17
+ * import { z } from 'zod';
18
+ * import { zPhone } from '@alikhalilll/a-tel-input/zod';
19
+ *
20
+ * const { phone, country, error, handleBlur, fieldProps } = useTelField('phone', {
21
+ * rules: toTypedSchema(zPhone()),
22
+ * validateOn: 'blur',
23
+ * });
24
+ * ```
25
+ *
26
+ * ```vue
27
+ * <ATelInput
28
+ * v-model:phone="phone"
29
+ * v-model:country="country"
30
+ * v-bind="fieldProps"
31
+ * @blur="handleBlur"
32
+ * />
33
+ * ```
34
+ *
35
+ * Server-side validation (e.g. "does this phone already exist?") plugs in via the
36
+ * `rules` callback — VeeValidate already supports async rules:
37
+ *
38
+ * ```ts
39
+ * const { phone, country, error, handleBlur, fieldProps, validating } = useTelField('phone', {
40
+ * rules: async (value: string) => {
41
+ * const sync = await zPhone().safeParseAsync(value);
42
+ * if (!sync.success) return sync.error.issues[0]!.message;
43
+ * const res = await $fetch('/api/phone/exists', { query: { phone: value } });
44
+ * return res.exists ? 'This phone is already registered.' : true;
45
+ * },
46
+ * validateOn: 'blur',
47
+ * });
48
+ * ```
49
+ *
50
+ * The `validating` ref is `true` while VeeValidate's async pipeline is in flight —
51
+ * bind it to ATelInput's `:validating` prop to surface a spinner inside the field.
52
+ *
53
+ * `vee-validate` is an **optional peer dependency** — install it yourself in your app.
54
+ */
55
+ /**
56
+ * Register a phone field with VeeValidate. See file header for usage examples.
57
+ *
58
+ * @param name The field name (used by VeeValidate, also forwarded to the inner
59
+ * `<input name="">` for native form submission).
60
+ * @param options See {@link UseTelFieldOptions}.
61
+ */
62
+ function useTelField(name, options = {}) {
63
+ const phone = ref(options.initialPhone ?? "");
64
+ const country = ref(options.initialCountry ?? null);
65
+ const v = usePhoneValidation();
66
+ v.getCountries();
67
+ function composeE164() {
68
+ if (!phone.value) return "";
69
+ const dial = country.value;
70
+ if (dial == null) return "";
71
+ const iso2 = v.getCountriesByDial(String(dial))[0]?.value;
72
+ if (!iso2) return "";
73
+ return v.validate({
74
+ country: { iso2 },
75
+ phone: phone.value
76
+ }).full_phone ?? "";
77
+ }
78
+ const initialValue = composeE164();
79
+ const field = useField(name, options.rules, {
80
+ initialValue,
81
+ validateOnValueUpdate: false,
82
+ ...options.fieldOptions
83
+ });
84
+ watch([phone, country], () => {
85
+ field.value.value = composeE164();
86
+ }, { flush: "post" });
87
+ const validating = computed(() => !!field.meta.pending);
88
+ const fieldProps = computed(() => ({
89
+ name: toValue(name),
90
+ error: field.errorMessage.value ?? null,
91
+ validateOn: options.validateOn ?? "blur",
92
+ validating: validating.value,
93
+ defaultCountry: options.defaultCountry
94
+ }));
95
+ return {
96
+ phone,
97
+ country,
98
+ e164: computed(() => field.value.value ?? ""),
99
+ error: field.errorMessage,
100
+ validating,
101
+ meta: field.meta,
102
+ handleBlur: field.handleBlur,
103
+ validate: field.validate,
104
+ setErrors: field.setErrors,
105
+ resetField: field.resetField,
106
+ fieldProps
107
+ };
108
+ }
109
+ //#endregion
110
+ export { useTelField };
111
+
112
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/vee-validate/useTelField.ts"],"sourcesContent":["/**\n * VeeValidate adapter for `@alikhalilll/a-tel-input`.\n *\n * `useTelField()` owns the two v-models (`phone` + `country`) and the canonical\n * E.164 string used by VeeValidate / Zod / yup, and returns a single ready-to-bind\n * object so a consumer doesn't have to glue the pieces together.\n *\n * Quick start:\n *\n * ```ts\n * import { useTelField } from '@alikhalilll/a-tel-input/vee-validate';\n * import { toTypedSchema } from '@vee-validate/zod';\n * import { z } from 'zod';\n * import { zPhone } from '@alikhalilll/a-tel-input/zod';\n *\n * const { phone, country, error, handleBlur, fieldProps } = useTelField('phone', {\n * rules: toTypedSchema(zPhone()),\n * validateOn: 'blur',\n * });\n * ```\n *\n * ```vue\n * <ATelInput\n * v-model:phone=\"phone\"\n * v-model:country=\"country\"\n * v-bind=\"fieldProps\"\n * @blur=\"handleBlur\"\n * />\n * ```\n *\n * Server-side validation (e.g. \"does this phone already exist?\") plugs in via the\n * `rules` callback — VeeValidate already supports async rules:\n *\n * ```ts\n * const { phone, country, error, handleBlur, fieldProps, validating } = useTelField('phone', {\n * rules: async (value: string) => {\n * const sync = await zPhone().safeParseAsync(value);\n * if (!sync.success) return sync.error.issues[0]!.message;\n * const res = await $fetch('/api/phone/exists', { query: { phone: value } });\n * return res.exists ? 'This phone is already registered.' : true;\n * },\n * validateOn: 'blur',\n * });\n * ```\n *\n * The `validating` ref is `true` while VeeValidate's async pipeline is in flight —\n * bind it to ATelInput's `:validating` prop to surface a spinner inside the field.\n *\n * `vee-validate` is an **optional peer dependency** — install it yourself in your app.\n */\n\nimport { computed, ref, watch, type ComputedRef, type MaybeRefOrGetter } from 'vue';\nimport { toValue } from 'vue';\nimport { useField, type FieldOptions, type RuleExpression } from 'vee-validate';\nimport { usePhoneValidation } from '../composables/usePhoneValidation';\nimport type { ATelInputValidateOn } from '../composables/useTelInputValidation';\n\nexport interface UseTelFieldOptions {\n /**\n * VeeValidate rules — a function, schema, or string. Use when the field stands\n * alone (no `useForm` / no form-level `validationSchema`).\n *\n * **Important**: vee-validate **ignores field-level `rules` whenever the parent\n * `useForm` is configured with `validationSchema`**. If you need async / server-\n * side validation inside a form, chain it onto the form schema via\n * `z.refine(async)` instead — that's what `handleSubmit` actually awaits and what\n * drives this composable's `validating` ref (via `meta.pending`). See the README\n * section \"Server-side validation\" for the full pattern.\n */\n rules?: RuleExpression<string>;\n /**\n * Initial digits-only national number, e.g. `'1066105963'`. Defaults to `''`.\n */\n initialPhone?: string;\n /**\n * Initial dial-digit number, e.g. `20` for Egypt. Defaults to `null`.\n */\n initialCountry?: number | null;\n /**\n * Default country (ISO2 like `'EG'` or dial code like `'20'`). Forwarded as the\n * `defaultCountry` prop on `<ATelInput>` via `fieldProps`.\n */\n defaultCountry?: string;\n /**\n * When to surface validation in the UI. Defaults to `'blur'` (the typical form-library\n * UX). Forwarded as the `validateOn` prop on `<ATelInput>` via `fieldProps`.\n */\n validateOn?: ATelInputValidateOn;\n /**\n * Forwarded to `useField` — extra options passed verbatim to VeeValidate. Use to\n * configure `keepValueOnUnmount`, `syncVModel`, etc.\n */\n fieldOptions?: Omit<FieldOptions<string>, 'initialValue'>;\n}\n\nexport interface UseTelFieldReturn {\n /** `v-model:phone` source — digits-only national number. Bind to `<ATelInput>`. */\n phone: import('vue').Ref<string>;\n /** `v-model:country` source — dial-digit number (e.g., `20`). Bind to `<ATelInput>`. */\n country: import('vue').Ref<number | null>;\n /** The canonical E.164 string fed to VeeValidate's schema (read-only). */\n e164: ComputedRef<string>;\n /** Current validation error message, or `undefined` when valid. From `useField`. */\n error: import('vue').Ref<string | undefined>;\n /** `true` while VeeValidate is running an async rule (e.g. server-side check). */\n validating: ComputedRef<boolean>;\n /** Whether the field has been blurred / dirtied / submitted (from VeeValidate `meta`). */\n meta: ReturnType<typeof useField<string>>['meta'];\n /** Forward this to `<ATelInput @blur=\"handleBlur\">` so VeeValidate's blur trigger fires. */\n handleBlur: ReturnType<typeof useField<string>>['handleBlur'];\n /** Imperatively trigger validation (e.g., after a programmatic value change). */\n validate: ReturnType<typeof useField<string>>['validate'];\n /** Imperatively set the error message — useful for server errors not raised by `rules`. */\n setErrors: ReturnType<typeof useField<string>>['setErrors'];\n /** Reset the field to its initial state. */\n resetField: ReturnType<typeof useField<string>>['resetField'];\n /**\n * Ready-to-bind prop bag for `<ATelInput v-bind=\"fieldProps\">`. Carries `name`,\n * `error`, `validateOn`, `validating`, and `defaultCountry`.\n */\n fieldProps: ComputedRef<{\n name: string;\n error: string | null;\n validateOn: ATelInputValidateOn;\n validating: boolean;\n defaultCountry?: string;\n }>;\n}\n\n/**\n * Register a phone field with VeeValidate. See file header for usage examples.\n *\n * @param name The field name (used by VeeValidate, also forwarded to the inner\n * `<input name=\"\">` for native form submission).\n * @param options See {@link UseTelFieldOptions}.\n */\nexport function useTelField(\n name: MaybeRefOrGetter<string>,\n options: UseTelFieldOptions = {}\n): UseTelFieldReturn {\n const phone = ref<string>(options.initialPhone ?? '');\n const country = ref<number | null>(options.initialCountry ?? null);\n\n const v = usePhoneValidation();\n void v.getCountries();\n\n // Compose E.164 from the two v-models — this is the canonical value VeeValidate\n // tracks. NANP (`+1`) resolves to `'US'` for validation; libphonenumber-js applies\n // the same rule set to every NANP country so this is correct.\n function composeE164(): string {\n if (!phone.value) return '';\n const dial = country.value;\n if (dial == null) return '';\n const matches = v.getCountriesByDial(String(dial));\n const iso2 = matches[0]?.value;\n if (!iso2) return '';\n const res = v.validate({ country: { iso2 }, phone: phone.value });\n return res.full_phone ?? '';\n }\n\n const initialValue = composeE164();\n\n const field = useField<string>(name, options.rules, {\n initialValue,\n // Don't run rules on every keystroke — let validateOn drive when validation fires.\n validateOnValueUpdate: false,\n ...options.fieldOptions,\n });\n\n // Keep VeeValidate's value in sync with the two v-models.\n watch(\n [phone, country],\n () => {\n field.value.value = composeE164();\n },\n { flush: 'post' }\n );\n\n const validating = computed(() => !!field.meta.pending);\n\n const fieldProps = computed(() => ({\n name: toValue(name),\n error: (field.errorMessage.value ?? null) as string | null,\n validateOn: options.validateOn ?? ('blur' as ATelInputValidateOn),\n validating: validating.value,\n defaultCountry: options.defaultCountry,\n }));\n\n return {\n phone,\n country,\n e164: computed(() => field.value.value ?? ''),\n error: field.errorMessage,\n validating,\n meta: field.meta,\n handleBlur: field.handleBlur,\n validate: field.validate,\n setErrors: field.setErrors,\n resetField: field.resetField,\n fieldProps,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwIA,SAAgB,YACd,MACA,UAA8B,CAAC,GACZ;CACnB,MAAM,QAAQ,IAAY,QAAQ,gBAAgB,EAAE;CACpD,MAAM,UAAU,IAAmB,QAAQ,kBAAkB,IAAI;CAEjE,MAAM,IAAI,mBAAmB;CAC7B,EAAO,aAAa;CAKpB,SAAS,cAAsB;EAC7B,IAAI,CAAC,MAAM,OAAO,OAAO;EACzB,MAAM,OAAO,QAAQ;EACrB,IAAI,QAAQ,MAAM,OAAO;EAEzB,MAAM,OADU,EAAE,mBAAmB,OAAO,IAAI,CAC7B,EAAE,IAAI;EACzB,IAAI,CAAC,MAAM,OAAO;EAElB,OADY,EAAE,SAAS;GAAE,SAAS,EAAE,KAAK;GAAG,OAAO,MAAM;EAAM,CACtD,EAAE,cAAc;CAC3B;CAEA,MAAM,eAAe,YAAY;CAEjC,MAAM,QAAQ,SAAiB,MAAM,QAAQ,OAAO;EAClD;EAEA,uBAAuB;EACvB,GAAG,QAAQ;CACb,CAAC;CAGD,MACE,CAAC,OAAO,OAAO,SACT;EACJ,MAAM,MAAM,QAAQ,YAAY;CAClC,GACA,EAAE,OAAO,OAAO,CAClB;CAEA,MAAM,aAAa,eAAe,CAAC,CAAC,MAAM,KAAK,OAAO;CAEtD,MAAM,aAAa,gBAAgB;EACjC,MAAM,QAAQ,IAAI;EAClB,OAAQ,MAAM,aAAa,SAAS;EACpC,YAAY,QAAQ,cAAe;EACnC,YAAY,WAAW;EACvB,gBAAgB,QAAQ;CAC1B,EAAE;CAEF,OAAO;EACL;EACA;EACA,MAAM,eAAe,MAAM,MAAM,SAAS,EAAE;EAC5C,OAAO,MAAM;EACb;EACA,MAAM,MAAM;EACZ,YAAY,MAAM;EAClB,UAAU,MAAM;EAChB,WAAW,MAAM;EACjB,YAAY,MAAM;EAClB;CACF;AACF"}