├── .changeset
└── config.json
├── .editorconfig
├── .eslintignore
├── .eslintrc.cjs
├── .gitattributes
├── .github
└── workflows
│ ├── lint.yml
│ └── release.yml
├── .gitignore
├── .husky
├── .gitignore
└── pre-commit
├── .nojekyll
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .prettierrc.cjs
├── .versionrc
├── LICENSE.md
├── README.md
├── apps
└── site
│ ├── .env.example
│ ├── .eslintignore
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── .prettierignore
│ ├── .prettierrc.cjs
│ ├── .versionrc
│ ├── CHANGELOG.md
│ ├── docker-compose.yml
│ ├── package.json
│ ├── playwright.config.ts
│ ├── postcss.config.cjs
│ ├── src
│ ├── app.css
│ ├── app.d.ts
│ ├── app.html
│ ├── hooks.server.ts
│ ├── lib
│ │ ├── components
│ │ │ ├── NavLink.svelte
│ │ │ ├── examples
│ │ │ │ ├── AdvancedPhoneInput.svelte
│ │ │ │ ├── BasicPhoneInput.svelte
│ │ │ │ └── EventDrivenPhoneInput.svelte
│ │ │ └── utils
│ │ │ │ ├── PayloadBlock.svelte
│ │ │ │ ├── Seo.svelte
│ │ │ │ └── SeoTypes.d.ts
│ │ ├── index.ts
│ │ ├── stores
│ │ │ ├── DevExampleStores.ts
│ │ │ └── index.ts
│ │ ├── utils
│ │ │ ├── directives
│ │ │ │ ├── clickOutsideAction.ts
│ │ │ │ ├── focusAction.ts
│ │ │ │ └── seoJsonLdAction.ts
│ │ │ ├── examples
│ │ │ │ └── exampleHelpers.ts
│ │ │ ├── helpers.ts
│ │ │ └── typeCheck.ts
│ │ └── views
│ │ │ ├── NewHeader.svelte
│ │ │ ├── OptionsPanel.svelte
│ │ │ ├── TheFooter.svelte
│ │ │ ├── TheHeader.svelte
│ │ │ ├── ThemeSwitch.svelte
│ │ │ └── Usage.svelte
│ └── routes
│ │ ├── +layout.svelte
│ │ ├── +layout.ts
│ │ └── +page.svelte
│ ├── static
│ ├── demo.gif
│ ├── favicon.ico
│ └── robots.txt
│ ├── svelte.config.js
│ ├── tailwind.config.cjs
│ ├── tsconfig.json
│ └── vite.config.ts
├── commitlint.config.cjs
├── package.json
├── packages
└── svelte-tel-input
│ ├── .env.example
│ ├── .eslintignore
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── .prettierignore
│ ├── .prettierrc.cjs
│ ├── CHANGELOG.md
│ ├── LICENSE.md
│ ├── README.md
│ ├── package.json
│ ├── postcss.config.cjs
│ ├── scripts
│ └── convertExamples.js
│ ├── src
│ ├── app.d.ts
│ ├── app.html
│ ├── lib
│ │ ├── assets
│ │ │ ├── allCountry.ts
│ │ │ ├── examplePhoneNumbers.ts
│ │ │ ├── flags_responsive.png
│ │ │ └── index.ts
│ │ ├── components
│ │ │ └── input
│ │ │ │ └── TelInput.svelte
│ │ ├── index.ts
│ │ ├── styles
│ │ │ └── flags.css
│ │ ├── types
│ │ │ └── index.d.ts
│ │ └── utils
│ │ │ ├── directives
│ │ │ ├── clickOutsideAction.ts
│ │ │ ├── focusAction.ts
│ │ │ └── telInputAction.ts
│ │ │ ├── helpers.ts
│ │ │ └── index.ts
│ ├── next
│ │ ├── assets
│ │ │ ├── regions.ts
│ │ │ └── telTypes.ts
│ │ └── utils
│ │ │ ├── helpers.ts
│ │ │ └── typeCheck.ts
│ └── tests
│ │ ├── inputParserTest.spec.ts
│ │ └── testnumbers.spec.json
│ ├── svelte.config.js
│ ├── tailwind.config.cjs
│ ├── tsconfig.json
│ └── vite.config.ts
├── playwright.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── scripts
├── changelog-github-custom.cjs
├── changelog-github-custom.js
├── changelog-github-custom.test.ts
└── changelog-github-custom.ts
└── static
├── demo.gif
├── favicon.ico
└── robots.txt
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json",
3 | "changelog": ["../scripts/changelog-github-custom.cjs", { "repo": "gyurielf/svelte-tel-input" }],
4 | "commit": false,
5 | "fixed": [],
6 | "linked": [],
7 | "access": "public",
8 | "baseBranch": "main",
9 | "updateInternalDependencies": "patch",
10 | "bumpVersionsWithWorkspaceProtocolOnly": true,
11 | "ignore": ["!(@gyurielf/*|svelte-tel-input)"]
12 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome:
2 | # This file is for unifying the coding style for different editors and IDEs.
3 | # More information at http://editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | charset = utf-8
9 | indent_size = 4
10 | indent_style = tab
11 | end_of_line = lf
12 | insert_final_newline = true
13 | trim_trailing_whitespace = true
14 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | build
4 | .svelte-kit
5 | dist
6 | /docs
7 | .env
8 | .env.*
9 | !.env.example
10 | coverage
11 | **/dist/*
12 |
13 | # Ignore files for PNPM, NPM and YARN
14 | pnpm-lock.yaml
15 | package-lock.json
16 | yarn.lock
17 |
18 | # don't lint nyc coverage output
19 | coverage
20 | static
21 |
22 | # Utility and CI tools
23 | zarf
24 | scripts
25 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: '@typescript-eslint/parser',
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:svelte/recommended',
8 | 'prettier'
9 | ],
10 | plugins: ['@typescript-eslint', 'import'],
11 | ignorePatterns: ['*.cjs', '.temp/**/*'],
12 | overrides: [
13 | {
14 | files: ['*.svelte'],
15 | parser: 'svelte-eslint-parser',
16 | parserOptions: {
17 | parser: '@typescript-eslint/parser'
18 | }
19 | }
20 | ],
21 | env: {
22 | browser: true,
23 | es2017: true,
24 | node: true
25 | },
26 | rules: {
27 | 'svelte/no-at-html-tags': 'off',
28 | // 'import/extensions': [
29 | // 'error',
30 | // 'always',
31 | // {
32 | // ignorePackages: true,
33 | // pattern: {
34 | // js: 'always',
35 | // ts: 'never'
36 | // }
37 | // }
38 | // ],
39 | 'no-restricted-imports': [
40 | 'warn',
41 | {
42 | paths: [
43 | {
44 | name: '.',
45 | message: 'Usage of local index imports is not allowed.'
46 | },
47 | {
48 | name: './index',
49 | message: 'Import from the source file instead.'
50 | }
51 | ]
52 | }
53 | ],
54 | '@typescript-eslint/no-unused-vars': [
55 | 'warn', // or "error"
56 | {
57 | argsIgnorePattern: '_',
58 | varsIgnorePattern: '_',
59 | caughtErrorsIgnorePattern: '_'
60 | }
61 | ],
62 | '@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }]
63 | }
64 | };
65 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | /app/** -linguist-detectable
2 | /**/*.svelte linguist-detectable
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 | on: [push]
3 | permissions:
4 | contents: read # to fetch code (actions/checkout)
5 | jobs:
6 | lint-code:
7 | runs-on: ubuntu-latest
8 | timeout-minutes: 5
9 | steps:
10 | - uses: actions/checkout@v4
11 | - uses: pnpm/action-setup@v4
12 | - uses: actions/setup-node@v4
13 | with:
14 | node-version: 18.x
15 | cache: pnpm
16 | - run: 'pnpm i && pnpm run sync && pnpm run package && pnpm run lint'
17 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 |
8 | permissions: {}
9 |
10 | jobs:
11 | release:
12 | # prevents this action from running on forks
13 | if: github.repository == 'gyurielf/svelte-tel-input'
14 | permissions:
15 | contents: write # to create release (changesets/action)
16 | pull-requests: write # to create pull request (changesets/action)
17 | name: Release
18 | runs-on: ubuntu-latest
19 | steps:
20 | - name: Checkout Repo
21 | uses: actions/checkout@v4
22 | with:
23 | # This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
24 | fetch-depth: 0
25 | - uses: pnpm/action-setup@v4
26 | with:
27 | version: 8.6.0
28 | - name: Setup Node.js 18.x
29 | uses: actions/setup-node@v4
30 | with:
31 | node-version: 18.x
32 | cache: pnpm
33 |
34 | - name: Install dependencies
35 | run: pnpm install --no-frozen-lockfile
36 |
37 | - name: Package
38 | run: pnpm package
39 |
40 | - name: Create Release Pull Request or Publish to npm
41 | id: changesets
42 | uses: changesets/action@v1
43 | with:
44 | # This expects you to have a script called release which does a build for your packages and calls changeset publish
45 | publish: pnpm ci:release
46 | env:
47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
49 |
50 | # TODO alert discord
51 | # - name: Send a Slack notification if a publish happens
52 | # if: steps.changesets.outputs.published == 'true'
53 | # # You can do something when a publish happens.
54 | # run: my-slack-bot send-notification --message "A new version of ${GITHUB_REPOSITORY} was published!"
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # testing
9 | coverage
10 |
11 | # svelte
12 | .svelte-kit
13 |
14 | # misc
15 | !.vscode/launch.json
16 | .vscode
17 | .DS_Store
18 | *.pem
19 |
20 | # debug
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 | # local env files
26 | .env
27 | .env.local
28 | .env.development.local
29 | .env.test.local
30 | .env.production.local
31 | !.env.example
32 |
33 | # vercel build output
34 | .vercel
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | currentBranch="$(git rev-parse --abbrev-ref HEAD)"
5 |
6 | # if [ "$currentBranch" = "main" ]; then
7 | # echo "You can't commit directly to main branch"
8 | # exit 1
9 | # fi
10 |
11 | # pnpm install ; pnpm run package ; pnpm install ; pnpm lint
--------------------------------------------------------------------------------
/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gyurielf/svelte-tel-input/cad0abb3775424ecda8d4860122c502be12828de/.nojekyll
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 | auto-install-peers=true
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v18.15.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .svelte-kit
3 | node_modules
4 | /build
5 | /package
6 | .env
7 | .env.*
8 | !.env.example
9 | vite.config.js*
10 | apps/site/.vercel
11 |
12 | # Ignore files for PNPM, NPM and YARN
13 | pnpm-lock.yaml
14 | pnpm-workspace.yaml
15 | package-lock.json
16 | yarn.lock
--------------------------------------------------------------------------------
/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | trailingComma: 'none',
4 | printWidth: 100,
5 | tabWidth: 4,
6 | plugins: ['prettier-plugin-svelte'],
7 | overrides: [
8 | { files: ['*.svelte'], options: { parser: 'svelte', bracketSameLine: false } },
9 | {
10 | files: ['**/CHANGELOG.md'],
11 | options: {
12 | requirePragma: true
13 | }
14 | },
15 | {
16 | files: ['README.md', 'packages/*/README.md'],
17 | options: {
18 | useTabs: false,
19 | tabWidth: 2
20 | }
21 | }
22 | ]
23 | };
24 |
--------------------------------------------------------------------------------
/.versionrc:
--------------------------------------------------------------------------------
1 | {
2 | "skip": {
3 | "tag": true
4 | },
5 | "types": [
6 | {
7 | "type": "chore",
8 | "section": "Others (chore)",
9 | "hidden": false
10 | },
11 | {
12 | "type": "revert",
13 | "section": "Reverts",
14 | "hidden": false
15 | },
16 | {
17 | "type": "feat",
18 | "section": "Features",
19 | "hidden": false
20 | },
21 | {
22 | "type": "fix",
23 | "section": "Bug Fixes",
24 | "hidden": false
25 | },
26 | {
27 | "type": "improvement",
28 | "section": "Feature Improvements",
29 | "hidden": false
30 | },
31 | {
32 | "type": "docs",
33 | "section": "Docs",
34 | "hidden": false
35 | },
36 | {
37 | "type": "style",
38 | "section": "Styling",
39 | "hidden": false
40 | },
41 | {
42 | "type": "refactor",
43 | "section": "Code Refactoring",
44 | "hidden": false
45 | },
46 | {
47 | "type": "perf",
48 | "section": "Performance Improvements",
49 | "hidden": false
50 | },
51 | {
52 | "type": "test",
53 | "section": "Tests",
54 | "hidden": false
55 | },
56 | {
57 | "type": "build",
58 | "section": "Build System",
59 | "hidden": false
60 | },
61 | {
62 | "type": "ci",
63 | "section": "CI",
64 | "hidden": false
65 | }
66 | ]
67 | }
68 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2022 - Present Gyorgy Kallai, Budapest, Hungary.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | packages/svelte-tel-input/README.md
--------------------------------------------------------------------------------
/apps/site/.env.example:
--------------------------------------------------------------------------------
1 | VITE_ENV_MODE=dev
--------------------------------------------------------------------------------
/apps/site/.eslintignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | build
4 | .svelte-kit
5 | dist
6 | /docs
7 | .env
8 | .env.*
9 | !.env.example
10 | coverage
11 | **/dist/*
12 |
13 | # Ignore files for PNPM, NPM and YARN
14 | pnpm-lock.yaml
15 | package-lock.json
16 | yarn.lock
17 |
18 | # don't lint nyc coverage output
19 | coverage
20 | static
21 |
22 | # Utility and CI tools
23 | zarf
24 | scripts
25 |
--------------------------------------------------------------------------------
/apps/site/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: '@typescript-eslint/parser',
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:svelte/recommended',
8 | 'prettier'
9 | ],
10 | plugins: ['@typescript-eslint', 'import'],
11 | ignorePatterns: ['*.cjs', '.temp/**/*'],
12 | overrides: [
13 | {
14 | files: ['*.svelte'],
15 | parser: 'svelte-eslint-parser',
16 | parserOptions: {
17 | parser: '@typescript-eslint/parser'
18 | }
19 | },
20 | {
21 | files: ['*.test.ts'],
22 | rules: {
23 | '@typescript-eslint/no-restricted-imports': ['off'],
24 | 'no-restricted-imports': ['off']
25 | }
26 | }
27 | ],
28 | parserOptions: {
29 | sourceType: 'module',
30 | ecmaVersion: 2020,
31 | extraFileExtensions: ['.svelte']
32 | },
33 | env: {
34 | browser: true,
35 | es2017: true,
36 | node: true
37 | },
38 | globals: { $$Generic: 'readable' },
39 | rules: {
40 | 'svelte/no-at-html-tags': 'off',
41 | // 'import/extensions': [
42 | // 'error',
43 | // 'always',
44 | // {
45 | // ignorePackages: true,
46 | // js: 'always',
47 | // ts: 'never'
48 | // }
49 | // ],
50 | 'no-restricted-imports': [
51 | 'warn',
52 | {
53 | paths: [
54 | {
55 | name: '.',
56 | message: 'Usage of local index imports is not allowed.'
57 | },
58 | {
59 | name: './index',
60 | message: 'Import from the source file instead.'
61 | }
62 | ]
63 | }
64 | ],
65 | // '@typescript-eslint/no-restricted-imports': [
66 | // 'error',
67 | // {
68 | // patterns: [
69 | // {
70 | // group: ['$app', '$app/*', '!./*', '!../*'],
71 | // message: 'Please only use RELATIVE import paths instead.'
72 | // }
73 | // ]
74 | // }
75 | // ],
76 | '@typescript-eslint/no-unused-vars': [
77 | 'warn', // or "error"
78 | {
79 | argsIgnorePattern: '_',
80 | varsIgnorePattern: '_',
81 | caughtErrorsIgnorePattern: '_'
82 | }
83 | ],
84 | 'no-empty-function': 'off',
85 | '@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }]
86 | }
87 | };
88 |
--------------------------------------------------------------------------------
/apps/site/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /dist
5 | /.svelte-kit
6 | /package
7 | .env
8 | .env.*
9 | !.env.example
10 | .vercel
11 | .output
12 | coverage
13 | .vscode
14 | /src/lib/exportGen.sh
--------------------------------------------------------------------------------
/apps/site/.prettierignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /dist
5 | /.svelte-kit
6 | /package
7 | /docs
8 | .env
9 | .env.*
10 | !.env.example
11 | CHANGELOG.md
12 | .changeset
13 | static
14 | /coverage
15 |
16 | # Ignore files for PNPM, NPM and YARN
17 | pnpm-lock.yaml
18 | package-lock.json
19 | yarn.lock
20 |
21 | # Utility and CI tools
22 | zarf
23 | scripts
--------------------------------------------------------------------------------
/apps/site/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | useTabs: true,
3 | singleQuote: true,
4 | trailingComma: 'none',
5 | printWidth: 100,
6 | tabWidth: 4,
7 | plugins: ['prettier-plugin-svelte'],
8 | overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }]
9 | };
10 |
--------------------------------------------------------------------------------
/apps/site/.versionrc:
--------------------------------------------------------------------------------
1 | {
2 | "skip": {
3 | "tag": true
4 | },
5 | "types": [
6 | {
7 | "type": "chore",
8 | "section": "Others (chore)",
9 | "hidden": false
10 | },
11 | {
12 | "type": "revert",
13 | "section": "Reverts",
14 | "hidden": false
15 | },
16 | {
17 | "type": "feat",
18 | "section": "Features",
19 | "hidden": false
20 | },
21 | {
22 | "type": "fix",
23 | "section": "Bug Fixes",
24 | "hidden": false
25 | },
26 | {
27 | "type": "improvement",
28 | "section": "Feature Improvements",
29 | "hidden": false
30 | },
31 | {
32 | "type": "docs",
33 | "section": "Docs",
34 | "hidden": false
35 | },
36 | {
37 | "type": "style",
38 | "section": "Styling",
39 | "hidden": false
40 | },
41 | {
42 | "type": "refactor",
43 | "section": "Code Refactoring",
44 | "hidden": false
45 | },
46 | {
47 | "type": "perf",
48 | "section": "Performance Improvements",
49 | "hidden": false
50 | },
51 | {
52 | "type": "test",
53 | "section": "Tests",
54 | "hidden": false
55 | },
56 | {
57 | "type": "build",
58 | "section": "Build System",
59 | "hidden": false
60 | },
61 | {
62 | "type": "ci",
63 | "section": "CI",
64 | "hidden": false
65 | }
66 | ]
67 | }
68 |
--------------------------------------------------------------------------------
/apps/site/CHANGELOG.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gyurielf/svelte-tel-input/cad0abb3775424ecda8d4860122c502be12828de/apps/site/CHANGELOG.md
--------------------------------------------------------------------------------
/apps/site/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.9'
2 | services:
3 | node:
4 | image: 'node:18.15-alpine3.17'
5 | container_name: mono-sti
6 | working_dir: /home/node/app
7 | command: 'sh -c "pnpm i && pnpm run prepare && pnpm run dev -- --host"'
8 | user: node
9 | tty: true
10 | ports:
11 | # Server
12 | - '3000:3000'
13 | # Vite
14 | - '24678:24678'
15 | volumes:
16 | - ./:/home/node/app
17 |
--------------------------------------------------------------------------------
/apps/site/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sti-examples",
3 | "description": "sti-examples",
4 | "version": "1.0.0",
5 | "private": true,
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/gyurielf/svelte-tel-input.git"
9 | },
10 | "homepage": "https://github.com/gyurielf/svelte-tel-input#readme",
11 | "keywords": [
12 | "svelte",
13 | "tel input",
14 | "phone",
15 | "phone input",
16 | "intl",
17 | "intl tel input"
18 | ],
19 | "engines": {
20 | "npm": ">= 8",
21 | "yarn": ">= 1",
22 | "node": ">= 16",
23 | "pnpm": ">= 8"
24 | },
25 | "scripts": {
26 | "build": "vite build",
27 | "dev": "vite dev",
28 | "eslint": "eslint --ext .js,.ts,.svelte .",
29 | "eslint:fix": "eslint --fix",
30 | "lint": "npm run prettier:check && npm run eslint && npm run ts && npm run svelte-check",
31 | "lint:fix": "npm run eslint:fix && npm run prettier:fix",
32 | "preview": "vite preview",
33 | "sync": "svelte-kit sync",
34 | "test": "playwright test",
35 | "ts": "tsc --noEmit",
36 | "prettier:check": "prettier --check .",
37 | "prettier:fix": "prettier --write .",
38 | "svelte-check": "svelte-kit sync && svelte-check --ignore 'dist,build,coverage,.svelte-kit,package' --fail-on-warnings"
39 | },
40 | "dependencies": {
41 | "svelte-tel-input": "workspace:*",
42 | "svelte": "^4.2.18"
43 | },
44 | "devDependencies": {
45 | "@macfja/svelte-persistent-store": "^2.4.1",
46 | "@playwright/test": "^1.45.0",
47 | "@sveltejs/adapter-static": "^3.0.2",
48 | "@sveltejs/kit": "^2.5.17",
49 | "@sveltejs/vite-plugin-svelte": "^3.1.1",
50 | "@types/micromatch": "^4.0.8",
51 | "@typescript-eslint/eslint-plugin": "^7.14.1",
52 | "@typescript-eslint/parser": "^7.14.1",
53 | "autoprefixer": "^10.4.19",
54 | "cssnano": "^7.0.3",
55 | "dotenv": "^16.4.5",
56 | "eslint": "^8.57.0",
57 | "eslint-config-prettier": "^9.1.0",
58 | "eslint-plugin-svelte": "^2.41.0",
59 | "postcss": "^8.4.38",
60 | "prettier": "^3.3.2",
61 | "prettier-plugin-svelte": "^3.2.5",
62 | "schema-dts": "^1.1.2",
63 | "svelte-check": "^3.8.4",
64 | "svelte2tsx": "^0.7.13",
65 | "tailwindcss": "^3.4.4",
66 | "tslib": "^2.6.3",
67 | "typescript": "^5.5.2",
68 | "vite": "^5.3.1"
69 | },
70 | "type": "module",
71 | "license": "MIT"
72 | }
73 |
--------------------------------------------------------------------------------
/apps/site/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import type { PlaywrightTestConfig } from '@playwright/test';
2 |
3 | const config: PlaywrightTestConfig = {
4 | webServer: {
5 | command: 'npm run build && npm run preview',
6 | port: 4173
7 | },
8 | testDir: 'tests'
9 | };
10 |
11 | export default config;
12 |
--------------------------------------------------------------------------------
/apps/site/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | const tailwindcss = require('tailwindcss');
2 | const autoprefixer = require('autoprefixer');
3 | const cssnano = require('cssnano');
4 |
5 | const mode = process.env.NODE_ENV;
6 | const dev = mode === 'development';
7 |
8 | const config = {
9 | plugins: [
10 | //Some plugins, like tailwindcss/nesting, need to run before Tailwind,
11 | tailwindcss(),
12 | //But others, like autoprefixer, need to run after,
13 | autoprefixer(),
14 | !dev &&
15 | cssnano({
16 | preset: 'default'
17 | })
18 | ]
19 | };
20 |
21 | module.exports = config;
22 |
--------------------------------------------------------------------------------
/apps/site/src/app.css:
--------------------------------------------------------------------------------
1 | /* Write your global styles here, in PostCSS syntax */
2 | @tailwind base;
3 | @tailwind components;
4 | @tailwind utilities;
5 |
--------------------------------------------------------------------------------
/apps/site/src/app.d.ts:
--------------------------------------------------------------------------------
1 | // See https://kit.svelte.dev/docs/types#app
2 | // for information about these interfaces
3 | // and what to do when importing types
4 | declare global {
5 | declare namespace App {
6 | // interface Error {}
7 | // interface Locals {}
8 | // interface PageData {}
9 | // interface Platform {}
10 | }
11 | }
12 |
13 | export {};
14 |
--------------------------------------------------------------------------------
/apps/site/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
27 | %sveltekit.head%
28 |
29 |
30 |
33 | %sveltekit.body%
34 |
35 |
36 |
--------------------------------------------------------------------------------
/apps/site/src/hooks.server.ts:
--------------------------------------------------------------------------------
1 | import type { Handle } from '@sveltejs/kit';
2 |
3 | export const handle: Handle = async ({ event, resolve }) => {
4 | return await resolve(event);
5 | };
6 |
--------------------------------------------------------------------------------
/apps/site/src/lib/components/NavLink.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 | {#if external}
18 |
19 | {:else}
20 |
23 | {/if}
24 |
--------------------------------------------------------------------------------
/apps/site/src/lib/components/examples/AdvancedPhoneInput.svelte:
--------------------------------------------------------------------------------
1 |
100 |
101 |
106 |
107 |
118 | {#if selectedCountry && selectedCountry !== null}
119 |
120 |
121 | {#if options.format === 'national'}
122 | +{selectedCountryDialCode}
125 | {/if}
126 |
127 | {:else}
128 | Please select
129 | {/if}
130 |
137 |
142 |
143 |
144 | {#if isOpen}
145 |
155 |
160 |
167 | {#each sortCountries(normalizedCountries, searchText) as country (country.id)}
168 | {@const isActive = isSelected(country.iso2, selectedCountry)}
169 |
170 |
{
179 | handleSelect(country.iso2, e);
180 | }}
181 | >
182 |
183 |
186 | {country.name}
187 | +{country.dialCode}
188 |
189 |
190 |
191 | {/each}
192 |
193 |
194 | {/if}
195 |
196 |
197 |
207 |
208 |
--------------------------------------------------------------------------------
/apps/site/src/lib/components/examples/BasicPhoneInput.svelte:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
41 | Please select
42 | {#each normalizedCountries as currentCountry (currentCountry.id)}
43 |
48 | {currentCountry.iso2} (+{currentCountry.dialCode})
49 |
50 | {/each}
51 |
52 |
53 |
64 |
65 |
--------------------------------------------------------------------------------
/apps/site/src/lib/components/examples/EventDrivenPhoneInput.svelte:
--------------------------------------------------------------------------------
1 |
42 |
43 |
44 |
59 | Please select
60 | {#each normalizedCountries as currentCountry (currentCountry.id)}
61 |
66 | {currentCountry.iso2} (+{currentCountry.dialCode})
67 |
68 | {/each}
69 |
70 |
71 |
85 | {
88 | value = null;
89 | country = null;
90 | valid = true;
91 | }}>Reset
93 |
94 |
--------------------------------------------------------------------------------
/apps/site/src/lib/components/utils/PayloadBlock.svelte:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
{
30 | isOpen = !isOpen;
31 | }}
32 | >
33 | Properties
34 |
35 | {#if isOpen}
36 |
44 |
49 |
50 | {:else}
51 |
59 |
64 |
65 | {/if}
66 |
67 |
68 |
69 | {#if isOpen}
70 |
74 |
75 |
76 |
77 | value
81 |
82 |
83 |
84 |
88 |
89 |
Value
90 |
{value}
91 |
92 |
93 |
94 |
Payload
95 | {#key value}
96 |
"{value}"
97 | {/key}
98 |
99 |
100 |
101 |
102 |
103 | detailedValue
107 |
108 |
109 |
110 |
111 |
Key
112 | {#each exampleDataEntries as [key, _]}
113 |
{key}
114 | {/each}
115 |
116 |
117 |
Value
118 | {#each exampleDataEntries as [_, val]}
119 |
{val}
120 | {/each}
121 |
122 |
123 |
124 |
Payload
125 | {#key exampleData}
126 | {#if exampleData !== null}
127 |
132 | {:else}
133 |
"{exampleData}"
134 | {/if}
135 | {/key}
136 |
137 |
138 |
139 |
140 |
141 | valid
145 |
146 |
147 |
148 |
149 |
Key
150 |
valid
151 |
152 |
153 |
Value
154 |
{valid}
155 |
156 |
157 |
158 |
Payload
159 | {#key valid}
160 |
"{valid}"
161 | {/key}
162 |
163 |
164 |
165 |
166 |
167 | country
171 |
172 |
173 |
174 |
175 |
Key
176 |
country
177 |
178 |
179 |
Value
180 |
{country}
181 |
182 |
183 |
184 |
Payload
185 | {#key country}
186 |
"{country}"
187 | {/key}
188 |
189 |
190 | {/if}
191 |
192 |
--------------------------------------------------------------------------------
/apps/site/src/lib/components/utils/Seo.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 | {#if title}
18 | {title}
19 | {/if}
20 |
21 |
25 |
29 |
30 | {#if description}
31 |
32 | {/if}
33 |
34 | {#if canonical}
35 |
36 | {/if}
37 |
38 | {#if openGraph}
39 | {#if openGraph.title}
40 |
41 | {/if}
42 |
43 | {#if openGraph.description}
44 |
45 | {/if}
46 |
47 | {#if openGraph.url || canonical}
48 |
49 | {/if}
50 |
51 | {#if openGraph.type}
52 |
53 | {/if}
54 |
55 | {#if openGraph.article}
56 | {#if openGraph.article.publishedTime}
57 |
58 | {/if}
59 |
60 | {#if openGraph.article.modifiedTime}
61 |
62 | {/if}
63 |
64 | {#if openGraph.article.expirationTime}
65 |
69 | {/if}
70 |
71 | {#if openGraph.article.section}
72 |
73 | {/if}
74 |
75 | {#if openGraph.article.authors && openGraph.article.authors.length}
76 | {#each openGraph.article.authors as author}
77 |
78 | {/each}
79 | {/if}
80 |
81 | {#if openGraph.article.tags && openGraph.article.tags.length}
82 | {#each openGraph.article.tags as tag}
83 |
84 | {/each}
85 | {/if}
86 | {/if}
87 |
88 | {#if openGraph.images && openGraph.images.length}
89 | {#each openGraph.images as image}
90 |
91 | {#if image.alt}
92 |
93 | {/if}
94 | {#if image.width}
95 |
96 | {/if}
97 | {#if image.height}
98 |
99 | {/if}
100 | {/each}
101 | {/if}
102 | {/if}
103 |
104 | {#if twitter}
105 |
106 | {#if twitter.site}
107 |
108 | {/if}
109 | {#if twitter.title}
110 |
111 | {/if}
112 | {#if twitter.description}
113 |
114 | {/if}
115 | {#if twitter.image}
116 |
117 | {/if}
118 | {#if twitter.imageAlt}
119 |
120 | {/if}
121 | {#if twitter.player}
122 |
123 | {/if}
124 | {#if twitter.playerWidth}
125 |
126 | {/if}
127 | {#if twitter.playerHeight}
128 |
129 | {/if}
130 | {/if}
131 |
132 | {#if jsonLd}
133 |
134 | {/if}
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/apps/site/src/lib/components/utils/SeoTypes.d.ts:
--------------------------------------------------------------------------------
1 | export interface OpenGraph {
2 | title?: string;
3 | description?: string;
4 | url?: string;
5 | type?: string;
6 | article?: OpenGraphArticle;
7 | images?: OpenGraphImage[];
8 | }
9 |
10 | export interface OpenGraphArticle {
11 | publishedTime?: string;
12 | modifiedTime?: string;
13 | expirationTime?: string;
14 | section?: string;
15 | authors?: string[];
16 | tags?: string[];
17 | }
18 |
19 | export interface OpenGraphImage {
20 | url: string;
21 | alt?: string;
22 | width?: number | string;
23 | height?: number | string;
24 | }
25 |
26 | export interface Twitter {
27 | card?: TwitterCard;
28 | site?: string;
29 | title?: string;
30 | description?: string;
31 | image?: string;
32 | imageAlt?: string;
33 | player?: string;
34 | playerWidth?: string;
35 | playerHeight?: string;
36 | }
37 |
38 | export type TwitterCard = 'summary' | 'summary_large_image' | 'player';
39 |
--------------------------------------------------------------------------------
/apps/site/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | export { clickOutsideAction } from './utils/directives/clickOutsideAction.js';
2 |
--------------------------------------------------------------------------------
/apps/site/src/lib/stores/DevExampleStores.ts:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 | import { createLocalStorage, persist } from '@macfja/svelte-persistent-store';
3 | import type { PersistentStore } from '@macfja/svelte-persistent-store';
4 | import type { DetailedValue } from 'svelte-tel-input/types';
5 |
6 | export const exampleDataStore = writable();
7 |
8 | // Theme
9 | export const theme: PersistentStore = persist(
10 | writable(null),
11 | createLocalStorage(),
12 | 'theme'
13 | );
14 |
15 | let preferedTheme: string | null = null;
16 |
17 | export const initTheme = () => {
18 | try {
19 | const unsubscribe = theme.subscribe((theme) => {
20 | preferedTheme = theme;
21 | });
22 | unsubscribe();
23 | } catch (error) {
24 | console.error(error);
25 | }
26 | };
27 |
28 | export const toggleTheme = (): void => {
29 | initTheme();
30 |
31 | const { classList } = document.querySelector('html') as HTMLElement;
32 |
33 | if (preferedTheme !== null) {
34 | classList.remove(preferedTheme);
35 | preferedTheme = null;
36 | } else {
37 | preferedTheme = 'dark';
38 | classList.add(preferedTheme);
39 | }
40 |
41 | theme.update((newTheme) => {
42 | newTheme = preferedTheme;
43 | return newTheme;
44 | });
45 | };
46 |
--------------------------------------------------------------------------------
/apps/site/src/lib/stores/index.ts:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | // Watch variable changes.
4 | export const watcher = (
5 | initialValue: string | null,
6 | watchFunction: (oldVal: string | null, newVal: string | null) => void
7 | ) => {
8 | const { subscribe, update } = writable(initialValue);
9 | return {
10 | subscribe,
11 | set: (value: string | null) => {
12 | update((oldvalue) => {
13 | watchFunction(oldvalue, value);
14 | return value;
15 | });
16 | }
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/apps/site/src/lib/utils/directives/clickOutsideAction.ts:
--------------------------------------------------------------------------------
1 | export const clickOutsideAction = (
2 | node: HTMLElement,
3 | handler: () => void
4 | ): { destroy: () => void } => {
5 | const onClick = (event: MouseEvent) =>
6 | node && !node.contains(event.target as HTMLElement) && !event.defaultPrevented && handler();
7 | document.addEventListener('click', onClick, true);
8 | return {
9 | destroy() {
10 | document.removeEventListener('click', onClick, true);
11 | }
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/apps/site/src/lib/utils/directives/focusAction.ts:
--------------------------------------------------------------------------------
1 | export function focusable_children(node: HTMLElement) {
2 | const nodes = Array.from(
3 | node.querySelectorAll(
4 | 'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
5 | )
6 | );
7 |
8 | const index = document.activeElement ? nodes.indexOf(document.activeElement) : -1;
9 |
10 | const update = (d: number) => {
11 | let i = index + d;
12 | i += nodes.length;
13 | i %= nodes.length;
14 |
15 | // @ts-expect-error Element is not HTMLElement
16 | nodes[i].focus();
17 | };
18 |
19 | return {
20 | next: (selector: string) => {
21 | const reordered = [...nodes.slice(index + 1), ...nodes.slice(0, index + 1)];
22 |
23 | for (let i = 0; i < reordered.length; i += 1) {
24 | if (!selector || reordered[i].matches(selector)) {
25 | // @ts-expect-error Element is not HTMLElement
26 | reordered[i].focus();
27 | return;
28 | }
29 | }
30 | },
31 | prev: (selector: string) => {
32 | const reordered = [...nodes.slice(index + 1), ...nodes.slice(0, index + 1)];
33 |
34 | for (let i = reordered.length - 2; i >= 0; i -= 1) {
35 | if (!selector || reordered[i].matches(selector)) {
36 | // @ts-expect-error Element is not HTMLElement
37 | reordered[i].focus();
38 | return;
39 | }
40 | }
41 | },
42 | update
43 | };
44 | }
45 |
46 | export function trap(node: HTMLElement) {
47 | const handle_keydown = (e: KeyboardEvent) => {
48 | if (e.key === 'Tab') {
49 | e.preventDefault();
50 |
51 | const group = focusable_children(node);
52 | if (e.shiftKey) {
53 | // @ts-expect-error Element is not HTMLElement
54 | group.prev();
55 | } else {
56 | // @ts-expect-error Element is not HTMLElement
57 | group.next();
58 | }
59 | }
60 | };
61 |
62 | node.addEventListener('keydown', handle_keydown);
63 |
64 | return {
65 | destroy: () => {
66 | node.removeEventListener('keydown', handle_keydown);
67 | }
68 | };
69 | }
70 |
71 | // Put onto the element, where you want to use the keyboard navigation.
72 | // on:keydown={(e) => {
73 | // if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
74 | // e.preventDefault();
75 | // const group = focusable_children(e.currentTarget);
76 | // // when using arrow keys (as opposed to tab), don't focus buttons
77 | // const selector = 'a, input';
78 | // if (e.key === 'ArrowDown') {
79 | // group.next(selector);
80 | // } else {
81 | // group.prev(selector);
82 | // }
83 | // }
84 | // }}
85 | // use:trap
86 |
--------------------------------------------------------------------------------
/apps/site/src/lib/utils/directives/seoJsonLdAction.ts:
--------------------------------------------------------------------------------
1 | import type { Thing, WithContext } from 'schema-dts';
2 |
3 | export const jsonLdScript = (node: HTMLScriptElement, jsonLd: (Thing | WithContext)[]) => {
4 | if (node) {
5 | node.type = 'application/ld+json';
6 | node.text = JSON.stringify(jsonLd, null, 2);
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/apps/site/src/lib/utils/examples/exampleHelpers.ts:
--------------------------------------------------------------------------------
1 | import type { DetailedValue } from 'svelte-tel-input/types';
2 |
3 | export const jsonPrettyParser = (node: HTMLElement, data: DetailedValue | null) => {
4 | data !== null && (node.innerHTML = `${JSON.stringify(data, null, 2)}
`);
5 | return {
6 | destroy: () => {
7 | node.remove();
8 | }
9 | };
10 | };
11 |
12 | export const cubeIn = (
13 | node: HTMLElement,
14 | { rotateFrom, duration }: { rotateFrom: number; duration: number }
15 | ) => ({
16 | duration,
17 | css: (t: number) => {
18 | const o = +getComputedStyle(node).opacity;
19 |
20 | return `
21 | transform: translateZ(${-64 + t * 64}px) translateY(${-64 + t * 64}%) rotate3d(1, 0, 0, ${
22 | rotateFrom - t * 90
23 | }deg);
24 | opacity: ${0.25 + t * o}
25 | `;
26 | }
27 | });
28 |
29 | export const cubeOut = (
30 | node: HTMLElement,
31 | { rotateTo, duration }: { rotateTo: number; duration: number }
32 | ) => ({
33 | duration,
34 | css: (t: number) => {
35 | const o = +getComputedStyle(node).opacity;
36 |
37 | return `
38 | transform: translateZ(${-64 + t * 64}px) translateY(${(1 - t) * 64}%) rotate3d(1, 0, 0, ${
39 | (1 - t) * rotateTo
40 | }deg);
41 | opacity: ${0.25 + t * o}
42 | `;
43 | }
44 | });
45 |
--------------------------------------------------------------------------------
/apps/site/src/lib/utils/helpers.ts:
--------------------------------------------------------------------------------
1 | export const capitalize = (str: string) => {
2 | return (str && str[0].toUpperCase() + str.slice(1).toLowerCase()) || '';
3 | };
4 |
5 | export const isNumber = (value: number) => {
6 | return typeof value === 'number' && isFinite(value);
7 | };
8 |
--------------------------------------------------------------------------------
/apps/site/src/lib/utils/typeCheck.ts:
--------------------------------------------------------------------------------
1 | export const isStringArray = (items: unknown): items is string[] => {
2 | return Array.isArray(items) && items.length > 0 && typeof items[0] === 'string';
3 | };
4 |
--------------------------------------------------------------------------------
/apps/site/src/lib/views/NewHeader.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
95 |
--------------------------------------------------------------------------------
/apps/site/src/lib/views/OptionsPanel.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
12 |
13 |
14 |
{
19 | isOpen = !isOpen;
20 | }}
21 | >
22 | Options
23 |
24 | {#if isOpen}
25 |
33 |
38 |
39 | {:else}
40 |
48 |
53 |
54 | {/if}
55 |
56 |
57 | {#if isOpen}
58 |
156 | {/if}
157 |
158 |
--------------------------------------------------------------------------------
/apps/site/src/lib/views/TheFooter.svelte:
--------------------------------------------------------------------------------
1 |
27 |
--------------------------------------------------------------------------------
/apps/site/src/lib/views/TheHeader.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
159 |
--------------------------------------------------------------------------------
/apps/site/src/lib/views/ThemeSwitch.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
19 |
25 |
35 |
36 |
43 |
44 |
45 |
46 |
52 |
62 |
63 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/apps/site/src/lib/views/Usage.svelte:
--------------------------------------------------------------------------------
1 |
81 |
82 |
83 |
84 |
85 |
Advanced Example
86 |
108 |
109 | {#key advancedExampleOptions}
110 |
117 | {/key}
118 |
119 |
125 |
126 |
127 |
128 |
129 |
Basic Example with init value
130 |
152 |
153 | {#key basicExampleWithE164Options}
154 |
161 | {/key}
162 |
163 |
169 |
170 |
171 |
172 |
173 |
Basic Example without init value
174 |
196 |
197 | {#key basicExampleWithNullOptions}
198 |
205 | {/key}
206 |
207 |
213 |
214 |
215 |
216 |
217 |
Event driven example
218 |
240 |
241 | {#key eventDrivenExampleOptions}
242 |
249 | {/key}
250 |
251 |
257 |
258 |
259 |
--------------------------------------------------------------------------------
/apps/site/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/apps/site/src/routes/+layout.ts:
--------------------------------------------------------------------------------
1 | export const trailingSlash = 'always';
2 | export const ssr = true;
3 | export const prerender = true;
4 |
--------------------------------------------------------------------------------
/apps/site/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
20 |
21 |
39 |
40 |
41 |
44 |
47 |
48 | {#key headline}
49 | {headline}
54 | {/key}
55 | phone numbers easily.
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/apps/site/static/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gyurielf/svelte-tel-input/cad0abb3775424ecda8d4860122c502be12828de/apps/site/static/demo.gif
--------------------------------------------------------------------------------
/apps/site/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gyurielf/svelte-tel-input/cad0abb3775424ecda8d4860122c502be12828de/apps/site/static/favicon.ico
--------------------------------------------------------------------------------
/apps/site/static/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/apps/site/svelte.config.js:
--------------------------------------------------------------------------------
1 | import staticAdapter from '@sveltejs/adapter-static';
2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | export default {
6 | preprocess: [vitePreprocess()],
7 | kit: {
8 | adapter: staticAdapter({
9 | pages: 'build',
10 | assets: 'build'
11 | })
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/apps/site/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | const defaultTheme = require('tailwindcss/defaultTheme');
2 |
3 | /** @type {import('tailwindcss').Config} */
4 | const config = {
5 | darkMode: 'class',
6 | content: ['./src/**/*.{html,js,svelte,ts}'],
7 | theme: {
8 | extend: {},
9 | screens: {
10 | sm: '640px',
11 |
12 | md: '768px',
13 |
14 | lg: '1024px',
15 |
16 | xl: '1280px',
17 |
18 | '2xl': '1536px'
19 | }
20 | },
21 | plugins: []
22 | };
23 |
24 | module.exports = config;
25 |
--------------------------------------------------------------------------------
/apps/site/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/site/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import { defineConfig, searchForWorkspaceRoot } from 'vite';
3 |
4 | export default defineConfig(({ mode }) => {
5 | return {
6 | plugins: [sveltekit()],
7 | server: {
8 | fs: {
9 | // search up for workspace root
10 | allow: [searchForWorkspaceRoot(process.cwd())]
11 | }
12 | },
13 | test: {
14 | include: ['src/**/*.{test,spec}.{js,ts}']
15 | },
16 | ...(mode === 'development' && {
17 | optimizeDeps: {
18 | include: ['@macfja/svelte-persistent-store']
19 | }
20 | })
21 | };
22 | });
23 |
--------------------------------------------------------------------------------
/commitlint.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = { extends: ['@commitlint/config-conventional'] };
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-tel-input-monorepo",
3 | "description": "svelte-tel-input-monorepo",
4 | "version": "1.0.0",
5 | "private": true,
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/gyurielf/svelte-tel-input.git",
9 | "directory": "packages/svelte-tel-input"
10 | },
11 | "homepage": "https://github.com/gyurielf/svelte-tel-input#readme",
12 | "keywords": [
13 | "svelte",
14 | "svelte kit",
15 | "sveltekit",
16 | "tel input",
17 | "phone",
18 | "phone input",
19 | "svelte phone input",
20 | "svelte tel input",
21 | "intl",
22 | "intl tel input",
23 | "svelte intl tel input"
24 | ],
25 | "engines": {
26 | "npm": ">= 8",
27 | "yarn": ">= 1",
28 | "node": ">= 18",
29 | "pnpm": ">= 8"
30 | },
31 | "scripts": {
32 | "build:apps": "pnpm -r --filter=./apps/* build",
33 | "dev:apps": "pnpm -r --filter=./apps/* dev",
34 | "preview:apps": "pnpm -r --filter=./apps/* preview",
35 | "changeset:publish": "changeset publish",
36 | "changeset:version": "changeset version && pnpm -r generate:version && git add --all",
37 | "changeset": "changeset",
38 | "ci:release": "changeset publish",
39 | "ci:version": "changeset version",
40 | "lint:fix": "pnpm -r lint:fix",
41 | "lint": "pnpm -r lint",
42 | "package": "pnpm -r package",
43 | "package:watch": "pnpm -r package:watch",
44 | "prepare": "husky install",
45 | "sync": "pnpm -r sync",
46 | "test": "pnpm test -r --filter=./packages/*"
47 | },
48 | "devDependencies": {
49 | "@changesets/cli": "^2.27.6",
50 | "@changesets/get-github-info": "^0.6.0",
51 | "@changesets/types": "^6.0.0",
52 | "@typescript-eslint/eslint-plugin": "^7.14.1",
53 | "dotenv": "^16.4.5",
54 | "eslint": "^8.57.0",
55 | "eslint-plugin-svelte": "^2.41.0",
56 | "eslint-plugin-unicorn": "^54.0.0",
57 | "eslint-config-prettier": "^9.1.0",
58 | "husky": "^9.0.11",
59 | "prettier": "^3.3.2",
60 | "prettier-plugin-svelte": "^3.2.5"
61 | },
62 | "type": "module",
63 | "license": "MIT",
64 | "packageManager": "pnpm@8.6.0"
65 | }
66 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/.env.example:
--------------------------------------------------------------------------------
1 | VITE_ENV_MODE=dev
--------------------------------------------------------------------------------
/packages/svelte-tel-input/.eslintignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /.svelte-kit
5 | /dist
6 | /docs
7 | .env
8 | .env.*
9 | !.env.example
10 | /coverage
11 |
12 | # Ignore files for PNPM, NPM and YARN
13 | pnpm-lock.yaml
14 | package-lock.json
15 | yarn.lock
16 |
17 | # don't lint nyc coverage output
18 | coverage
19 | static
20 |
21 | # Utility and CI tools
22 | zarf
23 | scripts
24 | **/reash/*
--------------------------------------------------------------------------------
/packages/svelte-tel-input/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: '@typescript-eslint/parser',
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:svelte/recommended',
8 | 'prettier'
9 | ],
10 | plugins: ['@typescript-eslint', 'import'],
11 | ignorePatterns: ['*.cjs', '.temp/**/*'],
12 | overrides: [
13 | {
14 | files: ['*.svelte'],
15 | parser: 'svelte-eslint-parser',
16 | parserOptions: {
17 | parser: '@typescript-eslint/parser'
18 | }
19 | },
20 | {
21 | files: ['*.test.ts'],
22 | rules: {
23 | '@typescript-eslint/no-restricted-imports': ['off'],
24 | 'no-restricted-imports': ['off']
25 | }
26 | }
27 | ],
28 | parserOptions: {
29 | sourceType: 'module',
30 | ecmaVersion: 2020,
31 | extraFileExtensions: ['.svelte']
32 | },
33 | env: {
34 | browser: true,
35 | es2017: true,
36 | node: true
37 | },
38 | globals: { $$Generic: 'readable' },
39 | rules: {
40 | 'svelte/no-at-html-tags': 'off',
41 | 'import/extensions': [
42 | 'error',
43 | 'always',
44 | {
45 | ignorePackages: true,
46 | js: 'always',
47 | ts: 'never'
48 | }
49 | ],
50 | 'no-restricted-imports': [
51 | 'warn',
52 | {
53 | paths: [
54 | {
55 | name: '.',
56 | message: 'Usage of local index imports is not allowed.'
57 | },
58 | {
59 | name: './index',
60 | message: 'Import from the source file instead.'
61 | }
62 | ]
63 | }
64 | ],
65 | '@typescript-eslint/no-restricted-imports': [
66 | 'error',
67 | {
68 | patterns: [
69 | {
70 | group: ['$app', '$app/*', '!./*', '!../*'],
71 | message: 'Please only use RELATIVE import paths instead.'
72 | }
73 | ]
74 | }
75 | ],
76 | 'no-empty-function': 'off',
77 | '@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }]
78 | }
79 | };
80 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /dist
5 | /.svelte-kit
6 | /package
7 | .env
8 | .env.*
9 | !.env.example
10 | .vercel
11 | .output
12 | coverage
13 | .vscode
14 | reash/*
--------------------------------------------------------------------------------
/packages/svelte-tel-input/.prettierignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /build
4 | /dist
5 | /.svelte-kit
6 | /package
7 | /docs
8 | .env
9 | .env.*
10 | !.env.example
11 | CHANGELOG.md
12 | .changeset
13 | static
14 | /coverage
15 |
16 | # Ignore files for PNPM, NPM and YARN
17 | pnpm-lock.yaml
18 | package-lock.json
19 | yarn.lock
20 |
21 | # Utility and CI tools
22 | zarf
23 | scripts
24 | examplePhoneNumbers.ts
25 | **/reash/*
--------------------------------------------------------------------------------
/packages/svelte-tel-input/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | trailingComma: 'none',
4 | printWidth: 100,
5 | tabWidth: 4,
6 | plugins: ['prettier-plugin-svelte'],
7 | overrides: [
8 | { files: ['*.svelte'], options: { parser: 'svelte', bracketSameLine: false } },
9 | {
10 | files: ['**/CHANGELOG.md'],
11 | options: {
12 | requirePragma: true
13 | }
14 | },
15 | {
16 | files: ['README.md', 'packages/*/README.md'],
17 | options: {
18 | useTabs: false,
19 | tabWidth: 2
20 | }
21 | }
22 | ]
23 | };
24 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2022 - Present Gyorgy Kallai, Budapest, Hungary.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | [](https://badge.fury.io/js/svelte-tel-input)
4 |
5 | # Svelte Tel Input
6 |
7 | > Lightweight svelte tel/phone input standardizer.
8 |
9 |
10 |
11 | 🔥 Check it out live [here](https://svelte-tel-input.vercel.app/)
12 |
13 | ## Installation
14 |
15 | Svelte Tel Input is distributed via [npm](https://www.npmjs.com/package/svelte-tel-input).
16 |
17 | ```bash
18 | npm install svelte-tel-input
19 | ```
20 |
21 | ## Features
22 |
23 | - Support SSR/SSG.
24 | - Parse and validate phone number.You can store one exact format (`E164`), no matter how users type their phone numbers.
25 | - Format (specified to its country), to make it more readable.
26 | - Prevent non-digits typing into the input, except the leading `+` sign (and `space` optionally).
27 | - Handle copy-pasted phone numbers, it's sanitize non-digit characters except the leading `+` sign (and `space` optionally).
28 | - Automatic placeholder generation for the selected country.
29 | - International or National formatted phone numbers.
30 |
31 | ## Usage
32 |
33 | ### Advanced
34 |
35 | _Snippet would be too long_ - [Example](https://github.com/gyurielf/svelte-tel-input/blob/main/apps/site/src/lib/components/examples/AdvancedPhoneInput.svelte) - [REPL](https://stackblitz.com/edit/svelte-tel-input-repl-1jfaar?file=README.md) (StackBlitz)
36 |
37 | ### Basic
38 |
39 | [Example](https://github.com/gyurielf/svelte-tel-input/blob/main/apps/site/src/lib/components/examples/BasicPhoneInput.svelte) - [REPL](https://stackblitz.com/edit/svelte-tel-input-repl?file=README.md) (StackBlitz)
40 |
41 | ```svelte
42 |
58 |
59 |
60 |
66 | Please select
67 | {#each normalizedCountries as currentCountry (currentCountry.id)}
68 |
73 | {currentCountry.iso2} (+{currentCountry.dialCode})
74 |
75 | {/each}
76 |
77 |
84 |
85 |
86 |
109 | ```
110 |
111 | (back to top )
112 |
113 | ## Props
114 |
115 | The default export of the library is the main TelInput component. It has the following props:
116 |
117 | | Property name | Type | Default Value | Usage |
118 | | ------------- | ---------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
119 | | value | `E164Number \| null` | `null` | [E164](https://en.wikipedia.org/wiki/E.164) is the international format of phone.numbers. This is the main entry point to store and/or load an existent phone number. |
120 | | country | `CountryCode \| null` | `null` | It's accept any Country Code Alpha-2 (ISO 3166). You can set manually (e.g: by the user via a select). The parser will inspect the entered phone number and if it detect a valid country calling code, then it's automatically set the country to according to the detected country calling code. E.g: `+36` -> `HU` |
121 | | disabled | `boolean` | `false` | It's block the parser and prevent entering input. You must handle its styling on your own. |
122 | | valid | `boolean` | `true` | Indicates whether the entered tel number validity. |
123 | | detailedValue | `DetailedValue \|null` | `null` | All of the formatted results of the tel input. |
124 | | class | `string` | `` | You can pass down any classname to the component |
125 | | required | `boolean \| null` | `null` | Set the required attribute on the input element |
126 | | options | `TelInputOptions` | check below | Allow or disallow spaces in the input field |
127 | | id | `string \| null` | uid | HTMLInputElement's attribute |
128 | | name | `string \| null` | `null` | HTMLInputElement's attribute |
129 | | readonly | `boolean \| null` | `null` | HTMLInputElement's attribute |
130 | | size | `number \| null` | `null` | HTMLInputElement's attribute |
131 | | autocomplete | `string \| null` | `null` | HTMLInputElement's attribute |
132 | | |
133 |
134 | Config options:
135 |
136 | ```javascript
137 | {
138 | // Generates country specific placeholder for the selected country.
139 | autoPlaceholder: true,
140 | // Allow or disallow spaces in the input field
141 | spaces: true,
142 | // If you have a parsed phone number and you change country manually from outside, then it's set the `valid` prop to false.
143 | invalidateOnCountryChange: false,
144 | // Formatted output `national` | `international`
145 | format: 'national'
146 | }
147 | ```
148 |
149 | (back to top )
150 |
151 | ## Dispatched Events
152 |
153 | The default export of the library is the main TelInput component. It has the following props:
154 |
155 | | Event name | Type |
156 | | ------------------- | ---------------------- |
157 | | updateValue | `E164Number \| null` |
158 | | updateDetailedValue | `DetailedValue \|null` |
159 | | updateCountry | `CountryCode \| null` |
160 | | updateValid | `boolean` |
161 | | parseError | `string` |
162 |
163 | ## Use case of the event driven behavior
164 |
165 | ```typescript
166 |
174 |
175 |
176 | ```
177 |
178 | (back to top )
179 |
180 | ## Caveats
181 |
182 | - In order to reset `value` and/or `country` from outside (you must pass (or set if you binded) `null` for the property) have some side-effects:
183 |
184 | - Reseting the `value` will set (keep the `country` as is):
185 | - `detailedValue` to `null`
186 | - dispatch `updateDetailedValue` event
187 | - Reseting the `country` will set:
188 | - `value` to `null`
189 | - `detailedValue` to `null`
190 | - `valid` to `true` if `invalidateOnCountryChange` config option is false (_@default false_). Otherwise it will be `false`.
191 | - and dispatch `updateValid`, `updateValue` `updateDetailedValue` events
192 | - Reseting both `value` and `country` will set:
193 | - `valid` to `true`
194 | - `detailedValue` to `null`;
195 |
196 | - Let's assume you pass a `US` `E164` number, which can be a partial `E164`, but long enough to determine the country and you pass `DE` country directly. The country will be updated to `US`, which is determined from the `E164` in this example. If the `E164` is not long enough to determine its country, then the country will stay what you passed to the component (`DE`).
197 |
198 | (back to top )
199 |
200 | ## Goals
201 |
202 | - Solve the problem that a users can enter the same phone number in different formats.
203 | - Storing a phone number in a standard format, that can be indexable and searchable in any database.
204 | - Should be accessible for the the browser. Eg. for a `( back to top )
212 |
213 | ## Changelog
214 |
215 | | Package | Changelog |
216 | | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
217 | | [@gyurielf/svelte-tel-input](https://github.com/gyurielf/svelte-tel-input/tree/main/packages/svelte-tel-input) | [Changelog](https://github.com/gyurielf/svelte-tel-input/blob/main/packages/svelte-tel-input/CHANGELOG.md) |
218 |
219 | (back to top )
220 |
221 | ## Roadmap
222 |
223 | - [x] Add Changelog
224 | - [x] Add CI/CD
225 | - [x] Integrate libphonenumber
226 | - [x] Implement parser
227 | - [x] Add basics docs and examples
228 | - [x] Add advanced examples
229 | - [x] Generate placeholders autimatically
230 | - [x] Move to monorepo
231 | - [ ] Improve A11Y
232 |
233 | See the [open issues](https://github.com/gyurielf/svelte-tel-input/issues) for a list of proposed features (and known issues).
234 |
235 | (back to top )
236 |
237 | ## Support
238 |
239 |
240 |
241 | (back to top )
242 |
243 | ## License
244 |
245 | Distributed under the MIT License. See `LICENSE.md` for more information.
246 |
247 | (back to top )
248 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-tel-input",
3 | "description": "svelte-tel-input",
4 | "version": "3.6.0",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/gyurielf/svelte-tel-input.git"
8 | },
9 | "homepage": "https://github.com/gyurielf/svelte-tel-input#readme",
10 | "keywords": [
11 | "svelte",
12 | "svelte kit",
13 | "sveltekit",
14 | "tel input",
15 | "phone",
16 | "phone input",
17 | "svelte phone input",
18 | "svelte tel input",
19 | "intl",
20 | "intl tel input",
21 | "svelte intl tel input"
22 | ],
23 | "engines": {
24 | "npm": ">= 8",
25 | "yarn": ">= 1",
26 | "node": ">= 18",
27 | "pnpm": ">= 8"
28 | },
29 | "scripts": {
30 | "check:publint": "publint . --strict",
31 | "convert": "node ./scripts/convertExamples.js",
32 | "eslint": "eslint --ext .js,.ts,.svelte .",
33 | "eslint:fix": "eslint --fix",
34 | "lint": "pnpm run prettier:check && pnpm run eslint && pnpm run ts && pnpm run svelte-check",
35 | "lint:fix": "pnpm run eslint:fix && pnpm run prettier:fix",
36 | "package:watch": "svelte-kit sync && svelte-package --watch",
37 | "package": "svelte-kit sync && pnpm run convert && svelte-package && pnpm run check:publint",
38 | "prettier:check": "prettier --check .",
39 | "prettier:fix": "prettier --write .",
40 | "svelte-check": "svelte-kit sync && svelte-check --ignore 'dist,build,coverage,.svelte-kit,package' --fail-on-warnings",
41 | "sync": "svelte-kit sync",
42 | "test": "vitest",
43 | "ts": "tsc --noEmit"
44 | },
45 | "peerDependencies": {
46 | "svelte": "^3.58.0 || ^4.0.0 || ^5.0.0"
47 | },
48 | "dependencies": {
49 | "libphonenumber-js": "1.10.43"
50 | },
51 | "devDependencies": {
52 | "@sveltejs/adapter-auto": "3.2.2",
53 | "@sveltejs/kit": "^2.5.17",
54 | "@sveltejs/package": "^2.3.2",
55 | "@sveltejs/vite-plugin-svelte": "^3.1.1",
56 | "@testing-library/svelte": "^5.1.0",
57 | "@testing-library/user-event": "^14.5.2",
58 | "@types/micromatch": "^4.0.8",
59 | "@typescript-eslint/eslint-plugin": "^7.14.1",
60 | "@typescript-eslint/parser": "^7.14.1",
61 | "autoprefixer": "^10.4.19",
62 | "cssnano": "^7.0.3",
63 | "dotenv": "^16.4.5",
64 | "eslint": "^8.57.0",
65 | "eslint-config-prettier": "^9.1.0",
66 | "eslint-plugin-import": "^2.29.1",
67 | "eslint-plugin-svelte": "^2.41.0",
68 | "jsdom": "^24.1.0",
69 | "micromatch": "^4.0.7",
70 | "postcss": "^8.4.38",
71 | "prettier": "^3.3.2",
72 | "prettier-plugin-svelte": "^3.2.5",
73 | "publint": "^0.2.8",
74 | "svelte": "^4.2.18",
75 | "svelte-check": "^3.8.4",
76 | "svelte2tsx": "^0.7.13",
77 | "tailwindcss": "^3.4.4",
78 | "tslib": "^2.6.3",
79 | "typescript": "^5.5.2",
80 | "vite": "^5.3.1",
81 | "vitest": "^1.6.0"
82 | },
83 | "type": "module",
84 | "license": "MIT",
85 | "files": [
86 | "dist"
87 | ],
88 | "main": "./dist/index.js",
89 | "types": "./dist/index.d.ts",
90 | "svelte": "./dist/index.js",
91 | "typesVersions": {
92 | "*": {
93 | "types": [
94 | "./dist/types/index.d.ts"
95 | ],
96 | "utils": [
97 | "./dist/utils/index.d.ts"
98 | ],
99 | "assets": [
100 | "./dist/assets/index.d.ts"
101 | ]
102 | }
103 | },
104 | "exports": {
105 | ".": {
106 | "types": "./dist/index.d.ts",
107 | "svelte": "./dist/index.js"
108 | },
109 | "./types": {
110 | "types": "./dist/types/index.d.ts",
111 | "default": "./dist/types/index.d.ts"
112 | },
113 | "./utils": {
114 | "types": "./dist/utils/index.d.ts",
115 | "default": "./dist/utils/index.js"
116 | },
117 | "./assets": {
118 | "types": "./dist/assets/index.d.ts",
119 | "default": "./dist/assets/index.js"
120 | },
121 | "./styles/flags.css": "./dist/styles/flags.css"
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | const tailwindcss = require('tailwindcss');
2 | const autoprefixer = require('autoprefixer');
3 | const cssnano = require('cssnano');
4 |
5 | const mode = process.env.NODE_ENV;
6 | const dev = mode === 'development';
7 |
8 | const config = {
9 | plugins: [
10 | //Some plugins, like tailwindcss/nesting, need to run before Tailwind,
11 | tailwindcss(),
12 | //But others, like autoprefixer, need to run after,
13 | autoprefixer(),
14 | !dev &&
15 | cssnano({
16 | preset: 'default'
17 | })
18 | ]
19 | };
20 |
21 | module.exports = config;
22 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/scripts/convertExamples.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | const sourceFilePath = 'node_modules/libphonenumber-js/examples.mobile.json.js'; // Direct path to the file in node_modules
3 | const targetFilePath = './src/lib/assets/examplePhoneNumbers.ts'; // Change to desired path
4 | const content = fs.readFileSync(sourceFilePath, 'utf-8');
5 | const extendedContent = `import type { Examples } from 'libphonenumber-js'; \n${content} as Examples;`;
6 | fs.writeFileSync(targetFilePath, extendedContent, 'utf-8');
7 | console.log(`Duplicated ${sourceFilePath} to ${targetFilePath}`);
8 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/app.d.ts:
--------------------------------------------------------------------------------
1 | // See https://kit.svelte.dev/docs/types#app
2 | // for information about these interfaces
3 | // and what to do when importing types
4 | declare global {
5 | declare namespace App {
6 | // interface Error {}
7 | // interface Locals {}
8 | // interface PageData {}
9 | // interface Platform {}
10 | }
11 | }
12 |
13 | export {};
14 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %sveltekit.head%
8 |
9 |
10 |
11 | %sveltekit.body%
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/assets/allCountry.ts:
--------------------------------------------------------------------------------
1 | // Array of country objects for the flag dropdown.
2 |
3 | // Here is the criteria for the plugin to support a given country/territory
4 | // - It has an iso2 code: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
5 | // - It has it's own country calling code (it is not a sub-region of another country): https://en.wikipedia.org/wiki/List_of_country_calling_codes
6 | // - It has a flag in the region-flags project: https://github.com/behdad/region-flags/tree/gh-pages/png
7 | // - It is supported by libphonenumber (it must be listed on this page): https://github.com/googlei18n/libphonenumber/blob/master/resources/ShortNumberMetadata.xml
8 |
9 | // Each country array has the following information:
10 | // [
11 | // Country name,
12 | // iso2 code,
13 | // International dial code,
14 | // Order (if >1 country with same dial code),
15 | // Area codes
16 | // ]
17 |
18 | import type { Country, CountryCode } from '$lib/types/index.js';
19 |
20 | const allCountries = [
21 | ['Afghanistan (افغانستان)', 'af', '93'],
22 | ['Albania (Shqipëri)', 'al', '355'],
23 | ['Algeria (الجزائر)', 'dz', '213'],
24 | ['American Samoa', 'as', '1', 5, ['684']],
25 | ['Andorra', 'ad', '376'],
26 | ['Angola', 'ao', '244'],
27 | ['Anguilla', 'ai', '1', 6, ['264']],
28 | ['Antigua and Barbuda', 'ag', '1', 7, ['268']],
29 | ['Argentina', 'ar', '54'],
30 | ['Armenia (Հայաստան)', 'am', '374'],
31 | ['Aruba', 'aw', '297'],
32 | ['Ascension Island', 'ac', '247'],
33 | ['Australia', 'au', '61', 0],
34 | ['Austria (Österreich)', 'at', '43'],
35 | ['Azerbaijan (Azərbaycan)', 'az', '994'],
36 | ['Bahamas', 'bs', '1', 8, ['242']],
37 | ['Bahrain (البحرين)', 'bh', '973'],
38 | ['Bangladesh (বাংলাদেশ)', 'bd', '880'],
39 | ['Barbados', 'bb', '1', 9, ['246']],
40 | ['Belarus (Беларусь)', 'by', '375'],
41 | ['Belgium (België)', 'be', '32'],
42 | ['Belize', 'bz', '501'],
43 | ['Benin (Bénin)', 'bj', '229'],
44 | ['Bermuda', 'bm', '1', 10, ['441']],
45 | ['Bhutan (འབྲུག)', 'bt', '975'],
46 | ['Bolivia', 'bo', '591'],
47 | ['Bosnia and Herzegovina (Босна и Херцеговина)', 'ba', '387'],
48 | ['Botswana', 'bw', '267'],
49 | ['Brazil (Brasil)', 'br', '55'],
50 | ['British Indian Ocean Territory', 'io', '246'],
51 | ['British Virgin Islands', 'vg', '1', 11, ['284']],
52 | ['Brunei', 'bn', '673'],
53 | ['Bulgaria (България)', 'bg', '359'],
54 | ['Burkina Faso', 'bf', '226'],
55 | ['Burundi (Uburundi)', 'bi', '257'],
56 | ['Cambodia (កម្ពុជា)', 'kh', '855'],
57 | ['Cameroon (Cameroun)', 'cm', '237'],
58 | [
59 | 'Canada',
60 | 'ca',
61 | '1',
62 | 1,
63 | [
64 | '204',
65 | '226',
66 | '236',
67 | '249',
68 | '250',
69 | '289',
70 | '306',
71 | '343',
72 | '365',
73 | '387',
74 | '403',
75 | '416',
76 | '418',
77 | '431',
78 | '437',
79 | '438',
80 | '450',
81 | '506',
82 | '514',
83 | '519',
84 | '548',
85 | '579',
86 | '581',
87 | '587',
88 | '604',
89 | '613',
90 | '639',
91 | '647',
92 | '672',
93 | '705',
94 | '709',
95 | '742',
96 | '778',
97 | '780',
98 | '782',
99 | '807',
100 | '819',
101 | '825',
102 | '867',
103 | '873',
104 | '902',
105 | '905'
106 | ]
107 | ],
108 | ['Cape Verde (Kabu Verdi)', 'cv', '238'],
109 | ['Caribbean Netherlands', 'bq', '599', 1, ['3', '4', '7']],
110 | ['Cayman Islands', 'ky', '1', 12, ['345']],
111 | ['Central African Republic (République centrafricaine)', 'cf', '236'],
112 | ['Chad (Tchad)', 'td', '235'],
113 | ['Chile', 'cl', '56'],
114 | ['China (中国)', 'cn', '86'],
115 | ['Christmas Island', 'cx', '61', 2, ['89164']],
116 | ['Cocos (Keeling) Islands', 'cc', '61', 1, ['89162']],
117 | ['Colombia', 'co', '57'],
118 | ['Comoros (جزر القمر)', 'km', '269'],
119 | ['Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)', 'cd', '243'],
120 | ['Congo (Republic) (Congo-Brazzaville)', 'cg', '242'],
121 | ['Cook Islands', 'ck', '682'],
122 | ['Costa Rica', 'cr', '506'],
123 | ['Côte d’Ivoire', 'ci', '225'],
124 | ['Croatia (Hrvatska)', 'hr', '385'],
125 | ['Cuba', 'cu', '53'],
126 | ['Curaçao', 'cw', '599', 0],
127 | ['Cyprus (Κύπρος)', 'cy', '357'],
128 | ['Czech Republic (Česká republika)', 'cz', '420'],
129 | ['Denmark (Danmark)', 'dk', '45'],
130 | ['Djibouti', 'dj', '253'],
131 | ['Dominica', 'dm', '1', 13, ['767']],
132 | ['Dominican Republic (República Dominicana)', 'do', '1', 2, ['809', '829', '849']],
133 | ['Ecuador', 'ec', '593'],
134 | ['Egypt (مصر)', 'eg', '20'],
135 | ['El Salvador', 'sv', '503'],
136 | ['Equatorial Guinea (Guinea Ecuatorial)', 'gq', '240'],
137 | ['Eritrea', 'er', '291'],
138 | ['Estonia (Eesti)', 'ee', '372'],
139 | ['Eswatini', 'sz', '268'],
140 | ['Ethiopia', 'et', '251'],
141 | ['Falkland Islands (Islas Malvinas)', 'fk', '500'],
142 | ['Faroe Islands (Føroyar)', 'fo', '298'],
143 | ['Fiji', 'fj', '679'],
144 | ['Finland (Suomi)', 'fi', '358', 0],
145 | ['France', 'fr', '33'],
146 | ['French Guiana (Guyane française)', 'gf', '594'],
147 | ['French Polynesia (Polynésie française)', 'pf', '689'],
148 | ['Gabon', 'ga', '241'],
149 | ['Gambia', 'gm', '220'],
150 | ['Georgia (საქართველო)', 'ge', '995'],
151 | ['Germany (Deutschland)', 'de', '49'],
152 | ['Ghana (Gaana)', 'gh', '233'],
153 | ['Gibraltar', 'gi', '350'],
154 | ['Greece (Ελλάδα)', 'gr', '30'],
155 | ['Greenland (Kalaallit Nunaat)', 'gl', '299'],
156 | ['Grenada', 'gd', '1', 14, ['473']],
157 | ['Guadeloupe', 'gp', '590', 0],
158 | ['Guam', 'gu', '1', 15, ['671']],
159 | ['Guatemala', 'gt', '502'],
160 | ['Guernsey', 'gg', '44', 1, ['1481', '7781', '7839', '7911']],
161 | ['Guinea (Guinée)', 'gn', '224'],
162 | ['Guinea-Bissau (Guiné Bissau)', 'gw', '245'],
163 | ['Guyana', 'gy', '592'],
164 | ['Haiti', 'ht', '509'],
165 | ['Honduras', 'hn', '504'],
166 | ['Hong Kong (香港)', 'hk', '852'],
167 | ['Hungary (Magyarország)', 'hu', '36'],
168 | ['Iceland (Ísland)', 'is', '354'],
169 | ['India (भारत)', 'in', '91'],
170 | ['Indonesia', 'id', '62'],
171 | ['Iran (ایران)', 'ir', '98'],
172 | ['Iraq (العراق)', 'iq', '964'],
173 | ['Ireland', 'ie', '353'],
174 | ['Isle of Man', 'im', '44', 2, ['1624', '74576', '7524', '7924', '7624']],
175 | ['Israel (ישראל)', 'il', '972'],
176 | ['Italy (Italia)', 'it', '39', 0],
177 | ['Jamaica', 'jm', '1', 4, ['876', '658']],
178 | ['Japan (日本)', 'jp', '81'],
179 | ['Jersey', 'je', '44', 3, ['1534', '7509', '7700', '7797', '7829', '7937']],
180 | ['Jordan (الأردن)', 'jo', '962'],
181 | ['Kazakhstan (Казахстан)', 'kz', '7', 1, ['33', '7']],
182 | ['Kenya', 'ke', '254'],
183 | ['Kiribati', 'ki', '686'],
184 | ['Kosovo', 'xk', '383'],
185 | ['Kuwait (الكويت)', 'kw', '965'],
186 | ['Kyrgyzstan (Кыргызстан)', 'kg', '996'],
187 | ['Laos (ລາວ)', 'la', '856'],
188 | ['Latvia (Latvija)', 'lv', '371'],
189 | ['Lebanon (لبنان)', 'lb', '961'],
190 | ['Lesotho', 'ls', '266'],
191 | ['Liberia', 'lr', '231'],
192 | ['Libya (ليبيا)', 'ly', '218'],
193 | ['Liechtenstein', 'li', '423'],
194 | ['Lithuania (Lietuva)', 'lt', '370'],
195 | ['Luxembourg', 'lu', '352'],
196 | ['Macau (澳門)', 'mo', '853'],
197 | ['North Macedonia (Македонија)', 'mk', '389'],
198 | ['Madagascar (Madagasikara)', 'mg', '261'],
199 | ['Malawi', 'mw', '265'],
200 | ['Malaysia', 'my', '60'],
201 | ['Maldives', 'mv', '960'],
202 | ['Mali', 'ml', '223'],
203 | ['Malta', 'mt', '356'],
204 | ['Marshall Islands', 'mh', '692'],
205 | ['Martinique', 'mq', '596'],
206 | ['Mauritania (موريتانيا)', 'mr', '222'],
207 | ['Mauritius (Moris)', 'mu', '230'],
208 | ['Mayotte', 'yt', '262', 1, ['269', '639']],
209 | ['Mexico (México)', 'mx', '52'],
210 | ['Micronesia', 'fm', '691'],
211 | ['Moldova (Republica Moldova)', 'md', '373'],
212 | ['Monaco', 'mc', '377'],
213 | ['Mongolia (Монгол)', 'mn', '976'],
214 | ['Montenegro (Crna Gora)', 'me', '382'],
215 | ['Montserrat', 'ms', '1', 16, ['664']],
216 | ['Morocco (المغرب)', 'ma', '212', 0],
217 | ['Mozambique (Moçambique)', 'mz', '258'],
218 | ['Myanmar (Burma) (မြန်မာ)', 'mm', '95'],
219 | ['Namibia (Namibië)', 'na', '264'],
220 | ['Nauru', 'nr', '674'],
221 | ['Nepal (नेपाल)', 'np', '977'],
222 | ['Netherlands (Nederland)', 'nl', '31'],
223 | ['New Caledonia (Nouvelle-Calédonie)', 'nc', '687'],
224 | ['New Zealand', 'nz', '64'],
225 | ['Nicaragua', 'ni', '505'],
226 | ['Niger (Nijar)', 'ne', '227'],
227 | ['Nigeria', 'ng', '234'],
228 | ['Niue', 'nu', '683'],
229 | ['Norfolk Island', 'nf', '672'],
230 | ['North Korea (조선 민주주의 인민 공화국)', 'kp', '850'],
231 | ['Northern Mariana Islands', 'mp', '1', 17, ['670']],
232 | ['Norway (Norge)', 'no', '47', 0],
233 | ['Oman (عُمان)', 'om', '968'],
234 | ['Pakistan (پاکستان)', 'pk', '92'],
235 | ['Palau', 'pw', '680'],
236 | ['Palestine (فلسطين)', 'ps', '970'],
237 | ['Panama (Panamá)', 'pa', '507'],
238 | ['Papua New Guinea', 'pg', '675'],
239 | ['Paraguay', 'py', '595'],
240 | ['Peru (Perú)', 'pe', '51'],
241 | ['Philippines', 'ph', '63'],
242 | ['Poland (Polska)', 'pl', '48'],
243 | ['Portugal', 'pt', '351'],
244 | ['Puerto Rico', 'pr', '1', 3, ['787', '939']],
245 | ['Qatar (قطر)', 'qa', '974'],
246 | ['Réunion (La Réunion)', 're', '262', 0],
247 | ['Romania (România)', 'ro', '40'],
248 | ['Russia (Россия)', 'ru', '7', 0],
249 | ['Rwanda', 'rw', '250'],
250 | ['Saint Barthélemy', 'bl', '590', 1],
251 | ['Saint Helena', 'sh', '290'],
252 | ['Saint Kitts and Nevis', 'kn', '1', 18, ['869']],
253 | ['Saint Lucia', 'lc', '1', 19, ['758']],
254 | ['Saint Martin (Saint-Martin (partie française))', 'mf', '590', 2],
255 | ['Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)', 'pm', '508'],
256 | ['Saint Vincent and the Grenadines', 'vc', '1', 20, ['784']],
257 | ['Samoa', 'ws', '685'],
258 | ['San Marino', 'sm', '378'],
259 | ['São Tomé and Príncipe (São Tomé e Príncipe)', 'st', '239'],
260 | ['Saudi Arabia (المملكة العربية السعودية)', 'sa', '966'],
261 | ['Senegal (Sénégal)', 'sn', '221'],
262 | ['Serbia (Србија)', 'rs', '381'],
263 | ['Seychelles', 'sc', '248'],
264 | ['Sierra Leone', 'sl', '232'],
265 | ['Singapore', 'sg', '65'],
266 | ['Sint Maarten', 'sx', '1', 21, ['721']],
267 | ['Slovakia (Slovensko)', 'sk', '421'],
268 | ['Slovenia (Slovenija)', 'si', '386'],
269 | ['Solomon Islands', 'sb', '677'],
270 | ['Somalia (Soomaaliya)', 'so', '252'],
271 | ['South Africa', 'za', '27'],
272 | ['South Korea (대한민국)', 'kr', '82'],
273 | ['South Sudan (جنوب السودان)', 'ss', '211'],
274 | ['Spain (España)', 'es', '34'],
275 | ['Sri Lanka (ශ්රී ලංකාව)', 'lk', '94'],
276 | ['Sudan (السودان)', 'sd', '249'],
277 | ['Suriname', 'sr', '597'],
278 | ['Svalbard and Jan Mayen', 'sj', '47', 1, ['79']],
279 | ['Sweden (Sverige)', 'se', '46'],
280 | ['Switzerland (Schweiz)', 'ch', '41'],
281 | ['Syria (سوريا)', 'sy', '963'],
282 | ['Taiwan (台灣)', 'tw', '886'],
283 | ['Tajikistan', 'tj', '992'],
284 | ['Tanzania', 'tz', '255'],
285 | ['Thailand (ไทย)', 'th', '66'],
286 | ['Timor-Leste', 'tl', '670'],
287 | ['Togo', 'tg', '228'],
288 | ['Tokelau', 'tk', '690'],
289 | ['Tonga', 'to', '676'],
290 | ['Trinidad and Tobago', 'tt', '1', 22, ['868']],
291 | ['Tristan da Cunha', 'ta', '290'],
292 | ['Tunisia (تونس)', 'tn', '216'],
293 | ['Turkey (Türkiye)', 'tr', '90'],
294 | ['Turkmenistan', 'tm', '993'],
295 | ['Turks and Caicos Islands', 'tc', '1', 23, ['649']],
296 | ['Tuvalu', 'tv', '688'],
297 | ['U.S. Virgin Islands', 'vi', '1', 24, ['340']],
298 | ['Uganda', 'ug', '256'],
299 | ['Ukraine (Україна)', 'ua', '380'],
300 | ['United Arab Emirates (الإمارات العربية المتحدة)', 'ae', '971'],
301 | ['United Kingdom', 'gb', '44', 0],
302 | ['United States', 'us', '1', 0],
303 | ['Uruguay', 'uy', '598'],
304 | ['Uzbekistan (Oʻzbekiston)', 'uz', '998'],
305 | ['Vanuatu', 'vu', '678'],
306 | ['Vatican City (Città del Vaticano)', 'va', '39', 1, ['06698']],
307 | ['Venezuela', 've', '58'],
308 | ['Vietnam (Việt Nam)', 'vn', '84'],
309 | ['Wallis and Futuna (Wallis-et-Futuna)', 'wf', '681'],
310 | ['Western Sahara (الصحراء الغربية)', 'eh', '212', 1, ['5288', '5289']],
311 | ['Yemen (اليمن)', 'ye', '967'],
312 | ['Zambia', 'zm', '260'],
313 | ['Zimbabwe', 'zw', '263'],
314 | ['Åland Islands', 'ax', '358', 1, ['18']]
315 | ];
316 |
317 | export const normalizedCountries = allCountries.map((country): Country => {
318 | return {
319 | id: (country[1] as string).toUpperCase(),
320 | label: `${country[0] as string} +${country[2] as string}`,
321 | name: country[0] as string,
322 | iso2: (country[1] as string).toUpperCase() as CountryCode,
323 | dialCode: country[2] as string,
324 | priority: country[3] || 0,
325 | areaCodes: country[4] || null
326 | };
327 | });
328 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/assets/examplePhoneNumbers.ts:
--------------------------------------------------------------------------------
1 | import type { Examples } from 'libphonenumber-js';
2 | // This file is a workaround for a bug in web browsers' "native"
3 | // ES6 importing system which is uncapable of importing "*.json" files.
4 | // https://github.com/catamphetamine/libphonenumber-js/issues/239
5 | export default {"AC":"40123","AD":"312345","AE":"501234567","AF":"701234567","AG":"2684641234","AI":"2642351234","AL":"672123456","AM":"77123456","AO":"923123456","AR":"91123456789","AS":"6847331234","AT":"664123456","AU":"412345678","AW":"5601234","AX":"412345678","AZ":"401234567","BA":"61123456","BB":"2462501234","BD":"1812345678","BE":"470123456","BF":"70123456","BG":"43012345","BH":"36001234","BI":"79561234","BJ":"90011234","BL":"690001234","BM":"4413701234","BN":"7123456","BO":"71234567","BQ":"3181234","BR":"11961234567","BS":"2423591234","BT":"17123456","BW":"71123456","BY":"294911911","BZ":"6221234","CA":"5062345678","CC":"412345678","CD":"991234567","CF":"70012345","CG":"061234567","CH":"781234567","CI":"0123456789","CK":"71234","CL":"221234567","CM":"671234567","CN":"13123456789","CO":"3211234567","CR":"83123456","CU":"51234567","CV":"9911234","CW":"95181234","CX":"412345678","CY":"96123456","CZ":"601123456","DE":"15123456789","DJ":"77831001","DK":"32123456","DM":"7672251234","DO":"8092345678","DZ":"551234567","EC":"991234567","EE":"51234567","EG":"1001234567","EH":"650123456","ER":"7123456","ES":"612345678","ET":"911234567","FI":"412345678","FJ":"7012345","FK":"51234","FM":"3501234","FO":"211234","FR":"612345678","GA":"06031234","GB":"7400123456","GD":"4734031234","GE":"555123456","GF":"694201234","GG":"7781123456","GH":"231234567","GI":"57123456","GL":"221234","GM":"3012345","GN":"601123456","GP":"690001234","GQ":"222123456","GR":"6912345678","GT":"51234567","GU":"6713001234","GW":"955012345","GY":"6091234","HK":"51234567","HN":"91234567","HR":"921234567","HT":"34101234","HU":"201234567","ID":"812345678","IE":"850123456","IL":"502345678","IM":"7924123456","IN":"8123456789","IO":"3801234","IQ":"7912345678","IR":"9123456789","IS":"6111234","IT":"3123456789","JE":"7797712345","JM":"8762101234","JO":"790123456","JP":"9012345678","KE":"712123456","KG":"700123456","KH":"91234567","KI":"72001234","KM":"3212345","KN":"8697652917","KP":"1921234567","KR":"1020000000","KW":"50012345","KY":"3453231234","KZ":"7710009998","LA":"2023123456","LB":"71123456","LC":"7582845678","LI":"660234567","LK":"712345678","LR":"770123456","LS":"50123456","LT":"61234567","LU":"628123456","LV":"21234567","LY":"912345678","MA":"650123456","MC":"612345678","MD":"62112345","ME":"67622901","MF":"690001234","MG":"321234567","MH":"2351234","MK":"72345678","ML":"65012345","MM":"92123456","MN":"88123456","MO":"66123456","MP":"6702345678","MQ":"696201234","MR":"22123456","MS":"6644923456","MT":"96961234","MU":"52512345","MV":"7712345","MW":"991234567","MX":"12221234567","MY":"123456789","MZ":"821234567","NA":"811234567","NC":"751234","NE":"93123456","NF":"381234","NG":"8021234567","NI":"81234567","NL":"612345678","NO":"40612345","NP":"9841234567","NR":"5551234","NU":"8884012","NZ":"211234567","OM":"92123456","PA":"61234567","PE":"912345678","PF":"87123456","PG":"70123456","PH":"9051234567","PK":"3012345678","PL":"512345678","PM":"551234","PR":"7872345678","PS":"599123456","PT":"912345678","PW":"6201234","PY":"961456789","QA":"33123456","RE":"692123456","RO":"712034567","RS":"601234567","RU":"9123456789","RW":"720123456","SA":"512345678","SB":"7421234","SC":"2510123","SD":"911231234","SE":"701234567","SG":"81234567","SH":"51234","SI":"31234567","SJ":"41234567","SK":"912123456","SL":"25123456","SM":"66661212","SN":"701234567","SO":"71123456","SR":"7412345","SS":"977123456","ST":"9812345","SV":"70123456","SX":"7215205678","SY":"944567890","SZ":"76123456","TA":"8999","TC":"6492311234","TD":"63012345","TG":"90112345","TH":"812345678","TJ":"917123456","TK":"7290","TL":"77212345","TM":"66123456","TN":"20123456","TO":"7715123","TR":"5012345678","TT":"8682911234","TV":"901234","TW":"912345678","TZ":"621234567","UA":"501234567","UG":"712345678","US":"2015550123","UY":"94231234","UZ":"912345678","VA":"3123456789","VC":"7844301234","VE":"4121234567","VG":"2843001234","VI":"3406421234","VN":"912345678","VU":"5912345","WF":"821234","WS":"7212345","XK":"43201234","YE":"712345678","YT":"639012345","ZA":"711234567","ZM":"955123456","ZW":"712345678"} as Examples;
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/assets/flags_responsive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gyurielf/svelte-tel-input/cad0abb3775424ecda8d4860122c502be12828de/packages/svelte-tel-input/src/lib/assets/flags_responsive.png
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/assets/index.ts:
--------------------------------------------------------------------------------
1 | export { normalizedCountries } from './allCountry.js';
2 | export { default as examplePhoneNumbers } from './examplePhoneNumbers.js';
3 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/components/input/TelInput.svelte:
--------------------------------------------------------------------------------
1 |
258 |
259 |
290 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | export { default as TelInput } from './components/input/TelInput.svelte';
2 | export {
3 | getCurrentCountry,
4 | inputParser,
5 | inspectAllowedChars,
6 | normalizeTelInput,
7 | getCountryForPartialE164Number,
8 | clickOutsideAction,
9 | isSelected
10 | } from '$lib/utils/index.js';
11 | export { parsePhoneNumberWithError, ParseError } from 'libphonenumber-js/max';
12 | export { normalizedCountries } from '$lib/assets/index.js';
13 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/styles/flags.css:
--------------------------------------------------------------------------------
1 | span.flag {
2 | width: 28px;
3 | height: 19px;
4 | display: inline-block;
5 | }
6 | img.flag {
7 | width: 30px;
8 | }
9 | .flag {
10 | background: url(../assets/flags_responsive.png) no-repeat;
11 | background-size: 100%;
12 | }
13 | .flag-ad {
14 | background-position: 0 0.413223%;
15 | }
16 | .flag-ae {
17 | background-position: 0 0.826446%;
18 | }
19 | .flag-af {
20 | background-position: 0 1.239669%;
21 | }
22 | .flag-ag {
23 | background-position: 0 1.652893%;
24 | }
25 | .flag-ai {
26 | background-position: 0 2.066116%;
27 | }
28 | .flag-al {
29 | background-position: 0 2.479339%;
30 | }
31 | .flag-am {
32 | background-position: 0 2.892562%;
33 | }
34 | .flag-an {
35 | background-position: 0 3.305785%;
36 | }
37 | .flag-ao {
38 | background-position: 0 3.719008%;
39 | }
40 | .flag-aq {
41 | background-position: 0 4.132231%;
42 | }
43 | .flag-ar {
44 | background-position: 0 4.545455%;
45 | }
46 | .flag-as {
47 | background-position: 0 4.958678%;
48 | }
49 | .flag-at {
50 | background-position: 0 5.371901%;
51 | }
52 | .flag-au {
53 | background-position: 0 5.785124%;
54 | }
55 | .flag-aw {
56 | background-position: 0 6.198347%;
57 | }
58 | .flag-az {
59 | background-position: 0 6.61157%;
60 | }
61 | .flag-ba {
62 | background-position: 0 7.024793%;
63 | }
64 | .flag-bb {
65 | background-position: 0 7.438017%;
66 | }
67 | .flag-bd {
68 | background-position: 0 7.85124%;
69 | }
70 | .flag-be {
71 | background-position: 0 8.264463%;
72 | }
73 | .flag-bf {
74 | background-position: 0 8.677686%;
75 | }
76 | .flag-bg {
77 | background-position: 0 9.090909%;
78 | }
79 | .flag-bh {
80 | background-position: 0 9.504132%;
81 | }
82 | .flag-bi {
83 | background-position: 0 9.917355%;
84 | }
85 | .flag-bj {
86 | background-position: 0 10.330579%;
87 | }
88 | .flag-bm {
89 | background-position: 0 10.743802%;
90 | }
91 | .flag-bn {
92 | background-position: 0 11.157025%;
93 | }
94 | .flag-bo {
95 | background-position: 0 11.570248%;
96 | }
97 | .flag-br {
98 | background-position: 0 11.983471%;
99 | }
100 | .flag-bs {
101 | background-position: 0 12.396694%;
102 | }
103 | .flag-bt {
104 | background-position: 0 12.809917%;
105 | }
106 | .flag-bv {
107 | background-position: 0 13.22314%;
108 | }
109 | .flag-bw {
110 | background-position: 0 13.636364%;
111 | }
112 | .flag-by {
113 | background-position: 0 14.049587%;
114 | }
115 | .flag-bz {
116 | background-position: 0 14.46281%;
117 | }
118 | .flag-ca {
119 | background-position: 0 14.876033%;
120 | }
121 | .flag-cc {
122 | background-position: 0 15.289256%;
123 | }
124 | .flag-cd {
125 | background-position: 0 15.702479%;
126 | }
127 | .flag-cf {
128 | background-position: 0 16.115702%;
129 | }
130 | .flag-cg {
131 | background-position: 0 16.528926%;
132 | }
133 | .flag-ch {
134 | background-position: 0 16.942149%;
135 | }
136 | .flag-ci {
137 | background-position: 0 17.355372%;
138 | }
139 | .flag-ck {
140 | background-position: 0 17.768595%;
141 | }
142 | .flag-cl {
143 | background-position: 0 18.181818%;
144 | }
145 | .flag-cm {
146 | background-position: 0 18.595041%;
147 | }
148 | .flag-cn {
149 | background-position: 0 19.008264%;
150 | }
151 | .flag-co {
152 | background-position: 0 19.421488%;
153 | }
154 | .flag-cr {
155 | background-position: 0 19.834711%;
156 | }
157 | .flag-cu {
158 | background-position: 0 20.247934%;
159 | }
160 | .flag-cv {
161 | background-position: 0 20.661157%;
162 | }
163 | .flag-cx {
164 | background-position: 0 21.07438%;
165 | }
166 | .flag-cy {
167 | background-position: 0 21.487603%;
168 | }
169 | .flag-cz {
170 | background-position: 0 21.900826%;
171 | }
172 | .flag-de {
173 | background-position: 0 22.31405%;
174 | }
175 | .flag-dj {
176 | background-position: 0 22.727273%;
177 | }
178 | .flag-dk {
179 | background-position: 0 23.140496%;
180 | }
181 | .flag-dm {
182 | background-position: 0 23.553719%;
183 | }
184 | .flag-do {
185 | background-position: 0 23.966942%;
186 | }
187 | .flag-dz {
188 | background-position: 0 24.380165%;
189 | }
190 | .flag-ec {
191 | background-position: 0 24.793388%;
192 | }
193 | .flag-ee {
194 | background-position: 0 25.206612%;
195 | }
196 | .flag-eg {
197 | background-position: 0 25.619835%;
198 | }
199 | .flag-eh {
200 | background-position: 0 26.033058%;
201 | }
202 | .flag-er {
203 | background-position: 0 26.446281%;
204 | }
205 | .flag-es {
206 | background-position: 0 26.859504%;
207 | }
208 | .flag-et {
209 | background-position: 0 27.272727%;
210 | }
211 | .flag-fi {
212 | background-position: 0 27.68595%;
213 | }
214 | .flag-fj {
215 | background-position: 0 28.099174%;
216 | }
217 | .flag-fk {
218 | background-position: 0 28.512397%;
219 | }
220 | .flag-fm {
221 | background-position: 0 28.92562%;
222 | }
223 | .flag-fo {
224 | background-position: 0 29.338843%;
225 | }
226 | .flag-fr {
227 | background-position: 0 29.752066%;
228 | }
229 | .flag-ga {
230 | background-position: 0 30.165289%;
231 | }
232 | .flag-gd {
233 | background-position: 0 30.578512%;
234 | }
235 | .flag-ge {
236 | background-position: 0 30.991736%;
237 | }
238 | .flag-gf {
239 | background-position: 0 31.404959%;
240 | }
241 | .flag-gh {
242 | background-position: 0 31.818182%;
243 | }
244 | .flag-gi {
245 | background-position: 0 32.231405%;
246 | }
247 | .flag-gl {
248 | background-position: 0 32.644628%;
249 | }
250 | .flag-gm {
251 | background-position: 0 33.057851%;
252 | }
253 | .flag-gn {
254 | background-position: 0 33.471074%;
255 | }
256 | .flag-gp {
257 | background-position: 0 33.884298%;
258 | }
259 | .flag-gq {
260 | background-position: 0 34.297521%;
261 | }
262 | .flag-gr {
263 | background-position: 0 34.710744%;
264 | }
265 | .flag-gs {
266 | background-position: 0 35.123967%;
267 | }
268 | .flag-gt {
269 | background-position: 0 35.53719%;
270 | }
271 | .flag-gu {
272 | background-position: 0 35.950413%;
273 | }
274 | .flag-gw {
275 | background-position: 0 36.363636%;
276 | }
277 | .flag-gy {
278 | background-position: 0 36.77686%;
279 | }
280 | .flag-hk {
281 | background-position: 0 37.190083%;
282 | }
283 | .flag-hm {
284 | background-position: 0 37.603306%;
285 | }
286 | .flag-hn {
287 | background-position: 0 38.016529%;
288 | }
289 | .flag-hr {
290 | background-position: 0 38.429752%;
291 | }
292 | .flag-ht {
293 | background-position: 0 38.842975%;
294 | }
295 | .flag-hu {
296 | background-position: 0 39.256198%;
297 | }
298 | .flag-id {
299 | background-position: 0 39.669421%;
300 | }
301 | .flag-ie {
302 | background-position: 0 40.082645%;
303 | }
304 | .flag-il {
305 | background-position: 0 40.495868%;
306 | }
307 | .flag-in {
308 | background-position: 0 40.909091%;
309 | }
310 | .flag-io {
311 | background-position: 0 41.322314%;
312 | }
313 | .flag-iq {
314 | background-position: 0 41.735537%;
315 | }
316 | .flag-ir {
317 | background-position: 0 42.14876%;
318 | }
319 | .flag-is {
320 | background-position: 0 42.561983%;
321 | }
322 | .flag-it {
323 | background-position: 0 42.975207%;
324 | }
325 | .flag-jm {
326 | background-position: 0 43.38843%;
327 | }
328 | .flag-jo {
329 | background-position: 0 43.801653%;
330 | }
331 | .flag-jp {
332 | background-position: 0 44.214876%;
333 | }
334 | .flag-ke {
335 | background-position: 0 44.628099%;
336 | }
337 | .flag-kg {
338 | background-position: 0 45.041322%;
339 | }
340 | .flag-kh {
341 | background-position: 0 45.454545%;
342 | }
343 | .flag-ki {
344 | background-position: 0 45.867769%;
345 | }
346 | .flag-km {
347 | background-position: 0 46.280992%;
348 | }
349 | .flag-kn {
350 | background-position: 0 46.694215%;
351 | }
352 | .flag-kp {
353 | background-position: 0 47.107438%;
354 | }
355 | .flag-kr {
356 | background-position: 0 47.520661%;
357 | }
358 | .flag-kw {
359 | background-position: 0 47.933884%;
360 | }
361 | .flag-ky {
362 | background-position: 0 48.347107%;
363 | }
364 | .flag-kz {
365 | background-position: 0 48.760331%;
366 | }
367 | .flag-la {
368 | background-position: 0 49.173554%;
369 | }
370 | .flag-lb {
371 | background-position: 0 49.586777%;
372 | }
373 | .flag-lc {
374 | background-position: 0 50%;
375 | }
376 | .flag-li {
377 | background-position: 0 50.413223%;
378 | }
379 | .flag-lk {
380 | background-position: 0 50.826446%;
381 | }
382 | .flag-lr {
383 | background-position: 0 51.239669%;
384 | }
385 | .flag-ls {
386 | background-position: 0 51.652893%;
387 | }
388 | .flag-lt {
389 | background-position: 0 52.066116%;
390 | }
391 | .flag-lu {
392 | background-position: 0 52.479339%;
393 | }
394 | .flag-lv {
395 | background-position: 0 52.892562%;
396 | }
397 | .flag-ly {
398 | background-position: 0 53.305785%;
399 | }
400 | .flag-ma {
401 | background-position: 0 53.719008%;
402 | }
403 | .flag-mc {
404 | background-position: 0 54.132231%;
405 | }
406 | .flag-md {
407 | background-position: 0 54.545455%;
408 | }
409 | .flag-me {
410 | background-position: 0 54.958678%;
411 | }
412 | .flag-mg {
413 | background-position: 0 55.371901%;
414 | }
415 | .flag-mh {
416 | background-position: 0 55.785124%;
417 | }
418 | .flag-mk {
419 | background-position: 0 56.198347%;
420 | }
421 | .flag-ml {
422 | background-position: 0 56.61157%;
423 | }
424 | .flag-mm {
425 | background-position: 0 57.024793%;
426 | }
427 | .flag-mn {
428 | background-position: 0 57.438017%;
429 | }
430 | .flag-mo {
431 | background-position: 0 57.85124%;
432 | }
433 | .flag-mp {
434 | background-position: 0 58.264463%;
435 | }
436 | .flag-mq {
437 | background-position: 0 58.677686%;
438 | }
439 | .flag-mr {
440 | background-position: 0 59.090909%;
441 | }
442 | .flag-ms {
443 | background-position: 0 59.504132%;
444 | }
445 | .flag-mt {
446 | background-position: 0 59.917355%;
447 | }
448 | .flag-mu {
449 | background-position: 0 60.330579%;
450 | }
451 | .flag-mv {
452 | background-position: 0 60.743802%;
453 | }
454 | .flag-mw {
455 | background-position: 0 61.157025%;
456 | }
457 | .flag-mx {
458 | background-position: 0 61.570248%;
459 | }
460 | .flag-my {
461 | background-position: 0 61.983471%;
462 | }
463 | .flag-mz {
464 | background-position: 0 62.396694%;
465 | }
466 | .flag-na {
467 | background-position: 0 62.809917%;
468 | }
469 | .flag-nc {
470 | background-position: 0 63.22314%;
471 | }
472 | .flag-ne {
473 | background-position: 0 63.636364%;
474 | }
475 | .flag-nf {
476 | background-position: 0 64.049587%;
477 | }
478 | .flag-ng {
479 | background-position: 0 64.46281%;
480 | }
481 | .flag-ni {
482 | background-position: 0 64.876033%;
483 | }
484 | .flag-nl {
485 | background-position: 0 65.289256%;
486 | }
487 | .flag-no {
488 | background-position: 0 65.702479%;
489 | }
490 | .flag-np {
491 | background-position: 0 66.115702%;
492 | }
493 | .flag-nr {
494 | background-position: 0 66.528926%;
495 | }
496 | .flag-nu {
497 | background-position: 0 66.942149%;
498 | }
499 | .flag-nz {
500 | background-position: 0 67.355372%;
501 | }
502 | .flag-om {
503 | background-position: 0 67.768595%;
504 | }
505 | .flag-pa {
506 | background-position: 0 68.181818%;
507 | }
508 | .flag-pe {
509 | background-position: 0 68.595041%;
510 | }
511 | .flag-pf {
512 | background-position: 0 69.008264%;
513 | }
514 | .flag-pg {
515 | background-position: 0 69.421488%;
516 | }
517 | .flag-ph {
518 | background-position: 0 69.834711%;
519 | }
520 | .flag-pk {
521 | background-position: 0 70.247934%;
522 | }
523 | .flag-pl {
524 | background-position: 0 70.661157%;
525 | }
526 | .flag-pm {
527 | background-position: 0 71.07438%;
528 | }
529 | .flag-pn {
530 | background-position: 0 71.487603%;
531 | }
532 | .flag-pr {
533 | background-position: 0 71.900826%;
534 | }
535 | .flag-pt {
536 | background-position: 0 72.31405%;
537 | }
538 | .flag-pw {
539 | background-position: 0 72.727273%;
540 | }
541 | .flag-py {
542 | background-position: 0 73.140496%;
543 | }
544 | .flag-qa {
545 | background-position: 0 73.553719%;
546 | }
547 | .flag-re {
548 | background-position: 0 73.966942%;
549 | }
550 | .flag-ro {
551 | background-position: 0 74.380165%;
552 | }
553 | .flag-rs {
554 | background-position: 0 74.793388%;
555 | }
556 | .flag-ru {
557 | background-position: 0 75.206612%;
558 | }
559 | .flag-rw {
560 | background-position: 0 75.619835%;
561 | }
562 | .flag-sa {
563 | background-position: 0 76.033058%;
564 | }
565 | .flag-sb {
566 | background-position: 0 76.446281%;
567 | }
568 | .flag-sc {
569 | background-position: 0 76.859504%;
570 | }
571 | .flag-sd {
572 | background-position: 0 77.272727%;
573 | }
574 | .flag-se {
575 | background-position: 0 77.68595%;
576 | }
577 | .flag-sg {
578 | background-position: 0 78.099174%;
579 | }
580 | .flag-sh {
581 | background-position: 0 78.512397%;
582 | }
583 | .flag-si {
584 | background-position: 0 78.92562%;
585 | }
586 | .flag-sj {
587 | background-position: 0 79.338843%;
588 | }
589 | .flag-sk {
590 | background-position: 0 79.752066%;
591 | }
592 | .flag-sl {
593 | background-position: 0 80.165289%;
594 | }
595 | .flag-sm {
596 | background-position: 0 80.578512%;
597 | }
598 | .flag-sn {
599 | background-position: 0 80.991736%;
600 | }
601 | .flag-so {
602 | background-position: 0 81.404959%;
603 | }
604 | .flag-sr {
605 | background-position: 0 81.818182%;
606 | }
607 | .flag-ss {
608 | background-position: 0 82.231405%;
609 | }
610 | .flag-st {
611 | background-position: 0 82.644628%;
612 | }
613 | .flag-sv {
614 | background-position: 0 83.057851%;
615 | }
616 | .flag-sy {
617 | background-position: 0 83.471074%;
618 | }
619 | .flag-sz {
620 | background-position: 0 83.884298%;
621 | }
622 | .flag-tc {
623 | background-position: 0 84.297521%;
624 | }
625 | .flag-td {
626 | background-position: 0 84.710744%;
627 | }
628 | .flag-tf {
629 | background-position: 0 85.123967%;
630 | }
631 | .flag-tg {
632 | background-position: 0 85.53719%;
633 | }
634 | .flag-th {
635 | background-position: 0 85.950413%;
636 | }
637 | .flag-tj {
638 | background-position: 0 86.363636%;
639 | }
640 | .flag-tk {
641 | background-position: 0 86.77686%;
642 | }
643 | .flag-tl {
644 | background-position: 0 87.190083%;
645 | }
646 | .flag-tm {
647 | background-position: 0 87.603306%;
648 | }
649 | .flag-tn {
650 | background-position: 0 88.016529%;
651 | }
652 | .flag-to {
653 | background-position: 0 88.429752%;
654 | }
655 | .flag-tp {
656 | background-position: 0 88.842975%;
657 | }
658 | .flag-tr {
659 | background-position: 0 89.256198%;
660 | }
661 | .flag-tt {
662 | background-position: 0 89.669421%;
663 | }
664 | .flag-tv {
665 | background-position: 0 90.082645%;
666 | }
667 | .flag-tw {
668 | background-position: 0 90.495868%;
669 | }
670 | .flag-ty {
671 | background-position: 0 90.909091%;
672 | }
673 | .flag-tz {
674 | background-position: 0 91.322314%;
675 | }
676 | .flag-ua {
677 | background-position: 0 91.735537%;
678 | }
679 | .flag-ug {
680 | background-position: 0 92.14876%;
681 | }
682 | .flag-gb,
683 | .flag-uk {
684 | background-position: 0 92.561983%;
685 | }
686 | .flag-um {
687 | background-position: 0 92.975207%;
688 | }
689 | .flag-us {
690 | background-position: 0 93.38843%;
691 | }
692 | .flag-uy {
693 | background-position: 0 93.801653%;
694 | }
695 | .flag-uz {
696 | background-position: 0 94.214876%;
697 | }
698 | .flag-va {
699 | background-position: 0 94.628099%;
700 | }
701 | .flag-vc {
702 | background-position: 0 95.041322%;
703 | }
704 | .flag-ve {
705 | background-position: 0 95.454545%;
706 | }
707 | .flag-vg {
708 | background-position: 0 95.867769%;
709 | }
710 | .flag-vi {
711 | background-position: 0 96.280992%;
712 | }
713 | .flag-vn {
714 | background-position: 0 96.694215%;
715 | }
716 | .flag-vu {
717 | background-position: 0 97.107438%;
718 | }
719 | .flag-wf {
720 | background-position: 0 97.520661%;
721 | }
722 | .flag-ws {
723 | background-position: 0 97.933884%;
724 | }
725 | .flag-ye {
726 | background-position: 0 98.347107%;
727 | }
728 | .flag-za {
729 | background-position: 0 98.760331%;
730 | }
731 | .flag-zm {
732 | background-position: 0 99.173554%;
733 | }
734 | .flag-zr {
735 | background-position: 0 99.586777%;
736 | }
737 | .flag-zw {
738 | background-position: 0 100%;
739 | }
740 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | CountryCallingCode,
3 | CountryCode,
4 | E164Number,
5 | NationalNumber,
6 | MetadataJson,
7 | PhoneNumber
8 | } from 'libphonenumber-js';
9 | import type { Countries } from 'libphonenumber-js/types';
10 |
11 | export interface Country {
12 | id: string;
13 | label: string;
14 | name: string;
15 | iso2: CountryCode;
16 | dialCode: string | number | string[];
17 | priority: string | number | string[];
18 | areaCodes: string | number | string[] | null;
19 | }
20 |
21 | export type CountrySelectEvents = {
22 | add: { option: T };
23 | remove: { option: T };
24 | same: { option: T };
25 | change: {
26 | option: T;
27 | };
28 | focus: unknown;
29 | blur: unknown;
30 | };
31 |
32 | export interface PhoneNumberError {
33 | isValid: false;
34 | error: PhoneNumberParseError;
35 | }
36 |
37 | export interface DetailedValue {
38 | countryCode?: CountryCode | null;
39 | isValid: boolean;
40 | phoneNumber: string | null;
41 | countryCallingCode: CountryCallingCode | null;
42 | formattedNumber: E164Number | null;
43 | formatOriginal: E164Number | null;
44 | nationalNumber: E164Number | null;
45 | formatInternational: E164Number | null;
46 | formatNational: E164Number | null;
47 | uri: string | null;
48 | e164: E164Number | null;
49 | error?: string;
50 | }
51 |
52 | export type PhoneNumberParseError = 'NOT_A_NUMBER' | 'INVALID_COUNTRY' | 'TOO_SHORT' | 'TOO_LONG';
53 | export type PhoneType = 'FIXED_LINE' | 'MOBILE';
54 |
55 | export interface TelInputValidity {
56 | value: boolean | null;
57 | errorMessage?: string;
58 | }
59 |
60 | export interface TelInputOptions {
61 | /**
62 | * It generates a placeholder into your input for the selected country. E.g. if the country is `US`, the placeholder will be `201 555 0123` by default.
63 | * If you need other format, you can use tha `national` -> `(201) 555-0123` and `international` -> `+1 201 555 0123` mode.
64 | * @default true
65 | */
66 |
67 | autoPlaceholder?: boolean;
68 | /**
69 | * Allow or disallow spaces in the input field
70 | * @default true
71 | */
72 | spaces?: boolean;
73 | /**
74 | * Set validation to false if you change the country property.
75 | * @default false
76 | */
77 | invalidateOnCountryChange?: boolean;
78 | /**
79 | * Prevent automatic country parsing from the input value. It validates via the passed `country` property value.
80 | * @default false
81 | */
82 | strictCountry?: boolean;
83 | /**
84 | * "international": `+36 20 123 4567`,
85 | * "default": `20 123 4567`
86 | * @default "national"
87 | */
88 | format?: 'national' | 'international';
89 | }
90 |
91 | export type {
92 | CountryCallingCode,
93 | CountryCode,
94 | E164Number,
95 | NationalNumber,
96 | PhoneNumber,
97 | Countries,
98 | MetadataJson
99 | };
100 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/utils/directives/clickOutsideAction.ts:
--------------------------------------------------------------------------------
1 | export const clickOutsideAction = (
2 | node: HTMLElement,
3 | handler: () => void,
4 | skipPrevented = true
5 | ): { destroy: () => void } => {
6 | const handleClick = async (event: MouseEvent) => {
7 | if (skipPrevented) {
8 | if (!node.contains(event.target as HTMLElement) && !event.defaultPrevented) handler();
9 | } else {
10 | if (!node.contains(event.target as HTMLElement)) handler();
11 | }
12 | };
13 |
14 | document.addEventListener('click', handleClick, true);
15 | return {
16 | destroy() {
17 | document.removeEventListener('click', handleClick, true);
18 | }
19 | };
20 | };
21 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/utils/directives/focusAction.ts:
--------------------------------------------------------------------------------
1 | export function focusable_children(node: HTMLElement) {
2 | const nodes = Array.from(
3 | node.querySelectorAll(
4 | 'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
5 | )
6 | );
7 |
8 | const index = document.activeElement ? nodes.indexOf(document.activeElement) : -1;
9 |
10 | const update = (d: number) => {
11 | let i = index + d;
12 | i += nodes.length;
13 | i %= nodes.length;
14 |
15 | // @ts-expect-error Element is not HTMLElement
16 | nodes[i].focus();
17 | };
18 |
19 | return {
20 | next: (selector: string) => {
21 | const reordered = [...nodes.slice(index + 1), ...nodes.slice(0, index + 1)];
22 |
23 | for (let i = 0; i < reordered.length; i += 1) {
24 | if (!selector || reordered[i].matches(selector)) {
25 | // @ts-expect-error Element is not HTMLElement
26 | reordered[i].focus();
27 | return;
28 | }
29 | }
30 | },
31 | prev: (selector: string) => {
32 | const reordered = [...nodes.slice(index + 1), ...nodes.slice(0, index + 1)];
33 |
34 | for (let i = reordered.length - 2; i >= 0; i -= 1) {
35 | if (!selector || reordered[i].matches(selector)) {
36 | // @ts-expect-error Element is not HTMLElement
37 | reordered[i].focus();
38 | return;
39 | }
40 | }
41 | },
42 | update
43 | };
44 | }
45 |
46 | export function trap(node: HTMLElement) {
47 | const handle_keydown = (e: KeyboardEvent) => {
48 | if (e.key === 'Tab') {
49 | e.preventDefault();
50 |
51 | const group = focusable_children(node);
52 | if (e.shiftKey) {
53 | // @ts-expect-error Element is not HTMLElement
54 | group.prev();
55 | } else {
56 | // @ts-expect-error Element is not HTMLElement
57 | group.next();
58 | }
59 | }
60 | };
61 |
62 | node.addEventListener('keydown', handle_keydown);
63 |
64 | return {
65 | destroy: () => {
66 | node.removeEventListener('keydown', handle_keydown);
67 | }
68 | };
69 | }
70 |
71 | // Put onto the element, where you want to use the keyboard navigation.
72 | // on:keydown={(e) => {
73 | // if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
74 | // e.preventDefault();
75 | // const group = focusable_children(e.currentTarget);
76 | // // when using arrow keys (as opposed to tab), don't focus buttons
77 | // const selector = 'a, input';
78 | // if (e.key === 'ArrowDown') {
79 | // group.next(selector);
80 | // } else {
81 | // group.prev(selector);
82 | // }
83 | // }
84 | // }}
85 | // use:trap
86 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/utils/directives/telInputAction.ts:
--------------------------------------------------------------------------------
1 | import { inspectAllowedChars, inputParser } from '$lib/index.js';
2 | import type { E164Number } from 'libphonenumber-js';
3 | export const telInputAction = (
4 | node: HTMLInputElement,
5 | {
6 | handler,
7 | spaces,
8 | strictCountryCode
9 | }: {
10 | handler: (val: string) => void;
11 | spaces: boolean;
12 | value: E164Number | null;
13 | strictCountryCode: boolean;
14 | }
15 | ) => {
16 | const onInput = (event: Event) => {
17 | if (node && node.contains(event.target as HTMLInputElement)) {
18 | const currentValue = (event.target as HTMLInputElement).value;
19 | const formattedInput = inputParser(currentValue, {
20 | parseCharacter: inspectAllowedChars,
21 | allowSpaces: spaces,
22 | disallowPlusSign: strictCountryCode
23 | });
24 | node.value = formattedInput;
25 | handler(formattedInput);
26 | }
27 | };
28 | node.addEventListener('input', onInput, true);
29 | return {
30 | update(params: {
31 | handler: (val: string) => void;
32 | spaces: boolean;
33 | value: E164Number | null;
34 | }) {
35 | if (params.value === null || params.value === '') {
36 | node.value = '';
37 | }
38 | },
39 | destroy() {
40 | node.removeEventListener('input', onInput, true);
41 | }
42 | };
43 | };
44 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/utils/helpers.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AsYouType,
3 | Metadata,
4 | getCountryCallingCode,
5 | getExampleNumber
6 | } from 'libphonenumber-js/max';
7 | import type {
8 | PhoneNumber,
9 | MetadataJson,
10 | Countries,
11 | E164Number,
12 | CountryCode
13 | } from '$lib/types/index.js';
14 | import { examplePhoneNumbers } from '$lib/assets/index.js';
15 |
16 | export const capitalize = (str: string) => {
17 | if (!str) return '';
18 | return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
19 | };
20 |
21 | // Use carefully, it can be rate limited.
22 | export const getCurrentCountry = async () => {
23 | try {
24 | const response = await (await fetch('https://ip2c.org/s')).text();
25 | const result = (response || '').toString();
26 |
27 | if (!result || result[0] !== '1') {
28 | console.warn('Unable to fetch the country');
29 | return;
30 | }
31 |
32 | return result.substring(2, 4);
33 | } catch (error) {
34 | console.warn('Unable to fetch the country');
35 | return;
36 | }
37 | };
38 |
39 | export const isNumber = (value: number) => {
40 | return typeof value === 'number' && isFinite(value);
41 | };
42 |
43 | export const normalizeTelInput = (input?: PhoneNumber) => {
44 | const filteredResult = Object.fromEntries(
45 | Object.entries({
46 | countryCode: input ? input.country : null,
47 | isValid: input ? input.isValid() : false,
48 | isPossible: input ? input.isPossible() : false,
49 | phoneNumber: input ? input.number : null,
50 | countryCallingCode: input ? input.countryCallingCode : null,
51 | formattedNumber: input ? new AsYouType().input(input.number) : null,
52 | nationalNumber: input ? input.nationalNumber : null,
53 | formatInternational: input ? new AsYouType().input(input.number) : null,
54 | formatOriginal: input
55 | ? new AsYouType()
56 | .input(input.number)
57 | .slice(input.countryCallingCode.length + 1)
58 | .trim()
59 | : null,
60 | formatNational: input ? new AsYouType(input.country).input(input.number) : null,
61 | uri: input ? input.getURI() : null,
62 | e164: input ? input.number : null
63 | }).filter(([, value]) => value !== null)
64 | );
65 | return filteredResult;
66 | };
67 |
68 | export const generatePlaceholder = (
69 | country: CountryCode,
70 | { format, spaces }: { format: 'international' | 'national'; spaces: boolean } = {
71 | format: 'national',
72 | spaces: true
73 | }
74 | ) => {
75 | const examplePhoneNumber = getExampleNumber(country, examplePhoneNumbers);
76 | if (examplePhoneNumber) {
77 | switch (format) {
78 | case 'international':
79 | return spaces
80 | ? examplePhoneNumber.formatInternational()
81 | : examplePhoneNumber.number;
82 | default:
83 | return spaces
84 | ? examplePhoneNumber
85 | .formatInternational()
86 | .slice(examplePhoneNumber.countryCallingCode.length + 1)
87 | .trim()
88 | : examplePhoneNumber.nationalNumber;
89 | }
90 | } else {
91 | throw new Error(`No country found with this country code: ${country}`);
92 | }
93 | };
94 |
95 | export const isSelected = <
96 | T extends {
97 | id: string;
98 | }
99 | >(
100 | itemToSelect: T | string,
101 | selectedItem: (T | undefined | null) | string
102 | ) => {
103 | if (!selectedItem) {
104 | return false;
105 | }
106 |
107 | if (typeof selectedItem === 'object' && typeof itemToSelect === 'object') {
108 | return selectedItem.id === itemToSelect.id;
109 | }
110 |
111 | return itemToSelect === selectedItem;
112 | };
113 |
114 | export const getInternationalPhoneNumberPrefix = (country: CountryCode) => {
115 | const ONLY_DIGITS_REGEXP = /^\d+$/;
116 | // Standard international phone number prefix: "+" and "country calling code".
117 | let prefix = '+' + getCountryCallingCode(country);
118 | // Get "leading digits" for a phone number of the country.
119 | // If there're "leading digits" then they can be part of the prefix too.
120 | const newMetadata = new Metadata();
121 | const leadingDigits = newMetadata.numberingPlan?.leadingDigits();
122 | if (leadingDigits && ONLY_DIGITS_REGEXP.test(leadingDigits)) {
123 | prefix += leadingDigits;
124 | }
125 | return prefix;
126 | };
127 |
128 | /**
129 | * Trims phone number digits if they exceed the maximum possible length
130 | * for a national (significant) number for the country.
131 | * @param {string} number - A possibly incomplete phone number digits string. Can be a possibly incomplete E.164 phone number.
132 | * @param {string} country
133 | * @return {string} Can be empty.
134 | */
135 | export const trimNumber = (number: E164Number, country: CountryCode) => {
136 | const nationalSignificantNumberPart = getNationalSignificantNumberDigits(number, country);
137 | if (nationalSignificantNumberPart) {
138 | const overflowDigitsCount =
139 | nationalSignificantNumberPart.length - getMaxNumberLength(country);
140 | if (overflowDigitsCount > 0) {
141 | return number.slice(0, number.length - overflowDigitsCount);
142 | }
143 | }
144 | return number;
145 | };
146 |
147 | export const getMaxNumberLength = (country: CountryCode) => {
148 | // Get "possible lengths" for a phone number of the country.
149 | const newMetadata = new Metadata();
150 | newMetadata.selectNumberingPlan(country);
151 | // Return the last "possible length".
152 |
153 | if (newMetadata.numberingPlan) {
154 | return newMetadata.numberingPlan.possibleLengths()[
155 | newMetadata.numberingPlan.possibleLengths().length - 1
156 | ];
157 | } else {
158 | throw new Error('There is no metadata object.');
159 | }
160 | };
161 |
162 | /**
163 | * If the phone number being input is an international one
164 | * then tries to derive the country from the phone number.
165 | * (regardless of whether there's any country currently selected)
166 | * @param {string} partialE164Number - A possibly incomplete E.164 phone number.
167 | * @param {string?} country - Currently selected country.
168 | * @param {string[]?} countries - A list of available countries. If not passed then "all countries" are assumed.
169 | * @return {string?}
170 | */
171 | export const getCountryForPartialE164Number = (
172 | partialE164Number: E164Number,
173 | {
174 | country,
175 | countries,
176 | required
177 | }: {
178 | country?: CountryCode;
179 | countries?: Countries[];
180 | required?: boolean;
181 | } = {}
182 | ) => {
183 | if (partialE164Number === '+') {
184 | // Don't change the currently selected country yet.
185 | return country;
186 | }
187 |
188 | const derived_country =
189 | getCountryFromPossiblyIncompleteInternationalPhoneNumber(partialE164Number);
190 |
191 | // If a phone number is being input in international form
192 | // and the country can already be derived from it,
193 | // then select that country.
194 | if (derived_country && (!countries || countries.indexOf(derived_country as Countries) >= 0)) {
195 | return derived_country;
196 | }
197 | // If "International" country option has not been disabled
198 | // and the international phone number entered doesn't correspond
199 | // to the currently selected country then reset the currently selected country.
200 | else if (country && !required && !couldNumberBelongToCountry(partialE164Number, country)) {
201 | return undefined;
202 | }
203 |
204 | // Don't change the currently selected country.
205 | return country;
206 | };
207 |
208 | /**
209 | * Determines the country for a given (possibly incomplete) E.164 phone number.
210 | * @param {string} number - A possibly incomplete E.164 phone number.
211 | * @return {string?}
212 | */
213 | export const getCountryFromPossiblyIncompleteInternationalPhoneNumber = (number: E164Number) => {
214 | const formatter = new AsYouType();
215 | formatter.input(number);
216 | // // `001` is a special "non-geograpical entity" code
217 | // // in Google's `libphonenumber` library.
218 | // if (formatter.getCountry() === '001') {
219 | // return
220 | // }
221 | return formatter.getCountry();
222 | };
223 |
224 | /**
225 | * Parses a partially entered national phone number digits
226 | * (or a partially entered E.164 international phone number)
227 | * and returns the national significant number part.
228 | * National significant number returned doesn't come with a national prefix.
229 | * @param {string} number - National number digits. Or possibly incomplete E.164 phone number.
230 | * @param {string?} country
231 | * @return {string} [result]
232 | */
233 | export const getNationalSignificantNumberDigits = (number: E164Number, country: CountryCode) => {
234 | // Create "as you type" formatter.
235 | const formatter = new AsYouType(country);
236 | // Input partial national phone number.
237 | formatter.input(number);
238 | // Return the parsed partial national phone number.
239 | const phoneNumber = formatter.getNumber();
240 | return phoneNumber && phoneNumber.nationalNumber;
241 | };
242 |
243 | /**
244 | * Checks if a partially entered E.164 phone number could belong to a country.
245 | * @param {string} number
246 | * @param {CountryCode} country
247 | * @return {boolean}
248 | */
249 | export const couldNumberBelongToCountry = (number: E164Number, country: CountryCode) => {
250 | const intlPhoneNumberPrefix = getInternationalPhoneNumberPrefix(country);
251 | let i = 0;
252 | while (i < number.length && i < intlPhoneNumberPrefix.length) {
253 | if (number[i] !== intlPhoneNumberPrefix[i]) {
254 | return false;
255 | }
256 | i++;
257 | }
258 | return true;
259 | };
260 |
261 | export const isSupportedCountry = (country: CountryCode, metadata: MetadataJson) => {
262 | return metadata.countries[country] !== undefined;
263 | };
264 |
265 | /**
266 | * These mappings map a character (key) to a specific digit that should
267 | * replace it for normalization purposes.
268 | * @param {string} character
269 | * @returns {string}
270 | */
271 | export const allowedCharacters = (
272 | character: string,
273 | { spaces }: { spaces?: boolean } = {
274 | spaces: true
275 | }
276 | ) => {
277 | const DIGITS = {
278 | '0': '0',
279 | '1': '1',
280 | '2': '2',
281 | '3': '3',
282 | '4': '4',
283 | '5': '5',
284 | '6': '6',
285 | '7': '7',
286 | '8': '8',
287 | '9': '9',
288 | '\uFF10': '0', // Fullwidth digit 0
289 | '\uFF11': '1', // Fullwidth digit 1
290 | '\uFF12': '2', // Fullwidth digit 2
291 | '\uFF13': '3', // Fullwidth digit 3
292 | '\uFF14': '4', // Fullwidth digit 4
293 | '\uFF15': '5', // Fullwidth digit 5
294 | '\uFF16': '6', // Fullwidth digit 6
295 | '\uFF17': '7', // Fullwidth digit 7
296 | '\uFF18': '8', // Fullwidth digit 8
297 | '\uFF19': '9', // Fullwidth digit 9
298 | '\u0660': '0', // Arabic-indic digit 0
299 | '\u0661': '1', // Arabic-indic digit 1
300 | '\u0662': '2', // Arabic-indic digit 2
301 | '\u0663': '3', // Arabic-indic digit 3
302 | '\u0664': '4', // Arabic-indic digit 4
303 | '\u0665': '5', // Arabic-indic digit 5
304 | '\u0666': '6', // Arabic-indic digit 6
305 | '\u0667': '7', // Arabic-indic digit 7
306 | '\u0668': '8', // Arabic-indic digit 8
307 | '\u0669': '9', // Arabic-indic digit 9
308 | '\u06F0': '0', // Eastern-Arabic digit 0
309 | '\u06F1': '1', // Eastern-Arabic digit 1
310 | '\u06F2': '2', // Eastern-Arabic digit 2
311 | '\u06F3': '3', // Eastern-Arabic digit 3
312 | '\u06F4': '4', // Eastern-Arabic digit 4
313 | '\u06F5': '5', // Eastern-Arabic digit 5
314 | '\u06F6': '6', // Eastern-Arabic digit 6
315 | '\u06F7': '7', // Eastern-Arabic digit 7
316 | '\u06F8': '8', // Eastern-Arabic digit 8
317 | '\u06F9': '9' // Eastern-Arabic digit 9,
318 | };
319 |
320 | // Allow spaces
321 | if (spaces) {
322 | const regex = new RegExp(
323 | '[\\t\\n\\v\\f\\r \\u00a0\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u200b\\u2028\\u2029\\u3000]',
324 | 'g'
325 | );
326 | if (regex.test(character)) {
327 | return character;
328 | }
329 | }
330 |
331 | // Allow digits
332 | return DIGITS[character as keyof typeof DIGITS];
333 | };
334 |
335 | export const inputParser = (
336 | text: string,
337 | {
338 | allowSpaces,
339 | parseCharacter,
340 | disallowPlusSign
341 | }: {
342 | allowSpaces: boolean;
343 | disallowPlusSign: boolean;
344 | parseCharacter: (
345 | char: string,
346 | val: string,
347 | allowSpaces: boolean,
348 | disallowPlusSign: boolean
349 | ) => string | undefined;
350 | }
351 | ) => {
352 | let value = '';
353 |
354 | for (let index = 0; index < text.length; index++) {
355 | const character = parseCharacter(text[index], value, allowSpaces, disallowPlusSign);
356 | if (character !== undefined) {
357 | value += character;
358 | }
359 | }
360 |
361 | return value;
362 | };
363 |
364 | export const inspectAllowedChars = (
365 | character: string,
366 | value: string,
367 | allowSpaces: boolean,
368 | disallowPlusSign: boolean
369 | ) => {
370 | // Leading plus is allowed
371 | if (!disallowPlusSign && character === '+') {
372 | if (!value) {
373 | return character;
374 | }
375 | }
376 | // Allowed characters
377 | return allowedCharacters(character, { spaces: allowSpaces });
378 | };
379 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/lib/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './helpers.js';
2 | export * from './directives/clickOutsideAction.js';
3 | export * from './directives/telInputAction.js';
4 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/next/assets/regions.ts:
--------------------------------------------------------------------------------
1 | // Country model:
2 | // [
3 | // Country name,
4 | // Regions,
5 | // iso2 code,
6 | // International dial code,
7 | // Format (if available),
8 | // Order priority (if >1 country with same dial code),
9 | // Area codes (if >1 country with same dial code)
10 | // ]
11 | //
12 | // Regions:
13 | // ['america', 'europe', 'asia', 'oceania', 'africa']
14 | //
15 | // Sub-regions:
16 | // ['north-america', 'south-america', 'central-america', 'carribean',
17 | // 'eu-union', 'ex-ussr', 'ex-yugos', 'baltic', 'middle-east', 'north-africa']
18 |
19 | export const rawRegions = [
20 | ['American Samoa', ['oceania'], 'as', '1684'],
21 | ['Anguilla', ['america', 'carribean'], 'ai', '1264'],
22 | ['Bermuda', ['america', 'north-america'], 'bm', '1441'],
23 | ['British Virgin Islands', ['america', 'carribean'], 'vg', '1284'],
24 | ['Cayman Islands', ['america', 'carribean'], 'ky', '1345'],
25 | ['Cook Islands', ['oceania'], 'ck', '682'],
26 | ['Falkland Islands', ['america', 'south-america'], 'fk', '500'],
27 | ['Faroe Islands', ['europe'], 'fo', '298'],
28 | ['Gibraltar', ['europe'], 'gi', '350'],
29 | ['Greenland', ['america'], 'gl', '299'],
30 | ['Jersey', ['europe', 'eu-union'], 'je', '44', '.... ......'],
31 | ['Montserrat', ['america', 'carribean'], 'ms', '1664'],
32 | ['Niue', ['asia'], 'nu', '683'],
33 | ['Norfolk Island', ['oceania'], 'nf', '672'],
34 | ['Northern Mariana Islands', ['oceania'], 'mp', '1670'],
35 | ['Saint Barthélemy', ['america', 'carribean'], 'bl', '590', '', 1],
36 | ['Saint Helena', ['africa'], 'sh', '290'],
37 | ['Saint Martin', ['america', 'carribean'], 'mf', '590', '', 2],
38 | ['Saint Pierre and Miquelon', ['america', 'north-america'], 'pm', '508'],
39 | ['Sint Maarten', ['america', 'carribean'], 'sx', '1721'],
40 | ['Tokelau', ['oceania'], 'tk', '690'],
41 | ['Turks and Caicos Islands', ['america', 'carribean'], 'tc', '1649'],
42 | ['U.S. Virgin Islands', ['america', 'carribean'], 'vi', '1340'],
43 | ['Wallis and Futuna', ['oceania'], 'wf', '681']
44 | ];
45 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/next/assets/telTypes.ts:
--------------------------------------------------------------------------------
1 | import { capitalize } from '$lib/utils/index.js';
2 | export const telTypes = [
3 | 'PREMIUM_RATE',
4 | 'TOLL_FREE',
5 | 'SHARED_COST',
6 | 'VOIP',
7 | 'PERSONAL_NUMBER',
8 | 'PAGER',
9 | 'UAN',
10 | 'VOICEMAIL',
11 | 'FIXED_LINE_OR_MOBILE',
12 | 'FIXED_LINE',
13 | 'MOBILE'
14 | ].map((el) => {
15 | return { id: el, value: el, label: capitalize(el.split('_').join(' ')) };
16 | });
17 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/next/utils/helpers.ts:
--------------------------------------------------------------------------------
1 | // Use carefully, it can be rate limited.
2 | export const getCurrentCountry = async () => {
3 | try {
4 | const response = await (await fetch('https://ip2c.org/s')).text();
5 | const result = (response || '').toString();
6 |
7 | if (!result || result[0] !== '1') {
8 | console.warn('Unable to fetch the country');
9 | return;
10 | }
11 |
12 | return result.substring(2, 4);
13 | } catch (error) {
14 | console.warn('Unable to fetch the country');
15 | return;
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/next/utils/typeCheck.ts:
--------------------------------------------------------------------------------
1 | export const isStringArray = (items: unknown): items is string[] => {
2 | return Array.isArray(items) && items.length > 0 && typeof items[0] === 'string';
3 | };
4 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/tests/inputParserTest.spec.ts:
--------------------------------------------------------------------------------
1 | import { inputParser, inspectAllowedChars } from '$lib/utils/index.js';
2 | import { describe, it, expect } from 'vitest';
3 |
4 | describe('Hello.svelte', () => {
5 | it('Input should be eleminate leters and special chars, and resul should be trimmed.', () => {
6 | const testValue = '+36 30 1aS 34 .9 ';
7 | const resut = inputParser(testValue, {
8 | allowSpaces: true,
9 | parseCharacter: inspectAllowedChars,
10 | disallowPlusSign: false
11 | });
12 | expect(resut).toStrictEqual('+36 30 1 34 9 ');
13 | });
14 |
15 | it('Input should be eleminate leters and special chars, and resul should be untrimmed.', () => {
16 | const testValue = '+36 30 1aS 34 .9 ';
17 | const resut = inputParser(testValue, {
18 | allowSpaces: true,
19 | parseCharacter: inspectAllowedChars,
20 | disallowPlusSign: false
21 | });
22 | expect(resut).toStrictEqual('+36 30 1 34 9 ');
23 | });
24 |
25 | it('Trimmed input with spaces', () => {
26 | const testValue = '+36 30 1a3 45 67';
27 | const resut = inputParser(testValue, {
28 | allowSpaces: true,
29 | parseCharacter: inspectAllowedChars,
30 | disallowPlusSign: false
31 | });
32 | expect(resut).toStrictEqual('+36 30 13 45 67');
33 | });
34 |
35 | it('Perfect input without spaces', () => {
36 | const testValue = '+36a301234567';
37 | const resut = inputParser(testValue, {
38 | allowSpaces: false,
39 | parseCharacter: inspectAllowedChars,
40 | disallowPlusSign: false
41 | });
42 | expect(resut).toStrictEqual('+36301234567');
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/src/tests/testnumbers.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "numbers": [
3 | {
4 | "number": "+12023041258",
5 | "country": "US"
6 | },
7 | {
8 | "number": "+442079460196",
9 | "country": "UK"
10 | },
11 | {
12 | "number": "+611300654321",
13 | "country": "AU"
14 | },
15 | {
16 | "number": "+33178965432",
17 | "country": "FR"
18 | },
19 | {
20 | "number": "+919167299896",
21 | "country": "IN"
22 | },
23 | {
24 | "number": "+525512341234",
25 | "country": "MX"
26 | },
27 | {
28 | "number": "+81303121212",
29 | "country": "JP"
30 | },
31 | {
32 | "number": "+551130405060",
33 | "country": "BR"
34 | },
35 | {
36 | "number": "+862155555555",
37 | "country": "CN"
38 | },
39 | {
40 | "number": "+97225555555",
41 | "country": "IL"
42 | },
43 | {
44 | "number": "+12023041258",
45 | "country": "CA"
46 | },
47 | {
48 | "number": "+12023041258",
49 | "country": "DE"
50 | },
51 | {
52 | "number": "+12023041258",
53 | "country": "RU"
54 | },
55 | {
56 | "number": "+12023041258",
57 | "country": "IT"
58 | },
59 | {
60 | "number": "+12023041258",
61 | "country": "ES"
62 | },
63 | {
64 | "number": "+12023041258",
65 | "country": "SE"
66 | },
67 | {
68 | "number": "+12023041258",
69 | "country": "CH"
70 | },
71 | {
72 | "number": "+12023041258",
73 | "country": "DK"
74 | },
75 | {
76 | "number": "+12023041258",
77 | "country": "NO"
78 | }
79 | ]
80 | }
81 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/svelte.config.js:
--------------------------------------------------------------------------------
1 | import autoAdapter from '@sveltejs/adapter-auto';
2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | export default {
6 | preprocess: [vitePreprocess()],
7 | kit: {
8 | adapter: autoAdapter()
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | const defaultTheme = require('tailwindcss/defaultTheme');
2 |
3 | /** @type {import('tailwindcss').Config} */
4 | const config = {
5 | darkMode: 'class',
6 | content: ['./src/**/*.{html,js,svelte,ts}'],
7 | theme: {
8 | extend: {},
9 | screens: {
10 | sm: '640px',
11 | md: '768px',
12 | lg: '1024px',
13 | xl: '1280px',
14 | '2xl': '1536px'
15 | }
16 | },
17 | plugins: []
18 | };
19 |
20 | module.exports = config;
21 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "dist",
5 | "allowJs": true,
6 | "checkJs": true,
7 | "esModuleInterop": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "resolveJsonModule": true,
10 | "skipLibCheck": true,
11 | "sourceMap": true,
12 | "strict": true,
13 | "moduleResolution": "Bundler",
14 | "module": "ESNext"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/svelte-tel-input/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig(() => {
5 | return {
6 | plugins: [sveltekit()],
7 | test: {
8 | include: ['src/**/*.{test,spec}.{js,ts}']
9 | }
10 | };
11 | });
12 |
--------------------------------------------------------------------------------
/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import type { PlaywrightTestConfig } from '@playwright/test';
2 |
3 | const config: PlaywrightTestConfig = {
4 | webServer: {
5 | command: 'npm run build && npm run preview',
6 | port: 4173
7 | },
8 | testDir: 'tests'
9 | };
10 |
11 | export default config;
12 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'apps/*'
3 | - 'packages/*'
--------------------------------------------------------------------------------
/scripts/changelog-github-custom.cjs:
--------------------------------------------------------------------------------
1 | const { config } = require('dotenv');
2 | const { getInfo, getInfoFromPullRequest } = require('@changesets/get-github-info');
3 |
4 | config();
5 |
6 | const changelogFunctions = {
7 | getDependencyReleaseLine: async (changesets, dependenciesUpdated, options) => {
8 | if (!options.repo) {
9 | throw new Error(
10 | 'Please provide a repo to this changelog generator like this:\n"changelog": ["@changesets/changelog-github", { "repo": "org/repo" }]'
11 | );
12 | }
13 | if (dependenciesUpdated.length === 0) return '';
14 |
15 | const changesetLink = `- Updated dependencies [${(
16 | await Promise.all(
17 | changesets.map(async (cs) => {
18 | if (cs.commit) {
19 | let { links } = await getInfo({
20 | repo: options.repo,
21 | commit: cs.commit
22 | });
23 | return links.commit;
24 | }
25 | })
26 | )
27 | )
28 | .filter((_) => _)
29 | .join(', ')}]:`;
30 |
31 | const updatedDepenenciesList = dependenciesUpdated.map(
32 | (dependency) => ` - ${dependency.name}@${dependency.newVersion}`
33 | );
34 |
35 | return [changesetLink, ...updatedDepenenciesList].join('\n');
36 | },
37 | getReleaseLine: async (changeset, type, options) => {
38 | if (!options || !options.repo) {
39 | throw new Error(
40 | 'Please provide a repo to this changelog generator like this:\n"changelog": ["@changesets/changelog-github", { "repo": "org/repo" }]'
41 | );
42 | }
43 |
44 | let prFromSummary;
45 | let commitFromSummary;
46 | let usersFromSummary;
47 |
48 | const replacedChangelog = changeset.summary
49 | .replace(/^\s*(?:pr|pull|pull\s+request):\s*#?(\d+)/im, (_, pr) => {
50 | let num = Number(pr);
51 | if (!isNaN(num)) prFromSummary = num;
52 | return '';
53 | })
54 | .replace(/^\s*commit:\s*([^\s]+)/im, (_, commit) => {
55 | commitFromSummary = commit;
56 | return '';
57 | })
58 | .replace(/^\s*(?:author|user):\s*@?([^\s]+)/gim, (_, user) => {
59 | usersFromSummary.push(user);
60 | return '';
61 | })
62 | .trim();
63 |
64 | const [firstLine, ...futureLines] = replacedChangelog.split('\n').map((l) => l.trimRight());
65 |
66 | const links = await (async () => {
67 | if (prFromSummary !== undefined) {
68 | let { links } = await getInfoFromPullRequest({
69 | repo: options.repo,
70 | pull: prFromSummary
71 | });
72 | if (commitFromSummary) {
73 | links = {
74 | ...links,
75 | commit: `[\`${commitFromSummary}\`](https://github.com/${options.repo}/commit/${commitFromSummary})`
76 | };
77 | }
78 | return links;
79 | }
80 | const commitToFetchFrom = commitFromSummary || changeset.commit;
81 | if (commitToFetchFrom) {
82 | let { links } = await getInfo({
83 | repo: options.repo,
84 | commit: commitToFetchFrom
85 | });
86 | return links;
87 | }
88 | return {
89 | commit: null,
90 | pull: null,
91 | user: null
92 | };
93 | })();
94 |
95 | const suffix = [
96 | links.pull === null ? '' : ` (${links.pull})`
97 | //links.commit === null ? '' : ` (${links.commit})`
98 | ].join('');
99 |
100 | return `\n\n- ${firstLine}${suffix}\n${futureLines.map((l) => ` ${l}`).join('\n')}`;
101 | }
102 | };
103 |
104 | module.exports = changelogFunctions;
105 |
--------------------------------------------------------------------------------
/scripts/changelog-github-custom.js:
--------------------------------------------------------------------------------
1 | import { config } from "dotenv";
2 | import { getInfo, getInfoFromPullRequest } from "@changesets/get-github-info";
3 | config();
4 | function validate(options) {
5 | if (!options || !options.repo) {
6 | throw new Error(
7 | 'Please provide a repo to this changelog generator like this:\n"changelog": ["@svitejs/changesets-changelog-github-compact", { "repo": "org/repo" }]'
8 | );
9 | }
10 | }
11 | var changelogFunctions = {
12 | getDependencyReleaseLine: async (changesets, dependenciesUpdated, options) => {
13 | validate(options);
14 | if (dependenciesUpdated.length === 0)
15 | return "";
16 | const changesetLink = `- Updated dependencies [${(await Promise.all(
17 | changesets.map(async (cs) => {
18 | if (cs.commit) {
19 | const { links } = await getInfo({
20 | repo: options.repo,
21 | commit: cs.commit
22 | });
23 | return links.commit;
24 | }
25 | })
26 | )).filter((_) => _).join(", ")}]:`;
27 | const updatedDepenenciesList = dependenciesUpdated.map(
28 | (dependency) => ` - ${dependency.name}@${dependency.newVersion}`
29 | );
30 | return [changesetLink, ...updatedDepenenciesList].join("\n");
31 | },
32 | getReleaseLine: async (changeset, type, options) => {
33 | validate(options);
34 | const repo = options.repo;
35 | let prFromSummary;
36 | let commitFromSummary;
37 | const replacedChangelog = changeset.summary.replace(/^\s*(?:pr|pull|pull\s+request):\s*#?(\d+)/im, (_, pr) => {
38 | const num = Number(pr);
39 | if (!isNaN(num))
40 | prFromSummary = num;
41 | return "";
42 | }).replace(/^\s*commit:\s*([^\s]+)/im, (_, commit) => {
43 | commitFromSummary = commit;
44 | return "";
45 | }).replace(/^\s*(?:author|user):\s*@?([^\s]+)/gim, "").trim();
46 | const linkifyIssueHints = (line) => line.replace(/(?<=\( ?(?:fix|fixes|see) )(#\d+)(?= ?\))/g, (issueHash) => {
47 | return `[${issueHash}](https://github.com/${repo}/issues/${issueHash.substring(1)})`;
48 | });
49 | const [firstLine, ...futureLines] = replacedChangelog.split("\n").map((l) => linkifyIssueHints(l.trimRight()));
50 | const links = await (async () => {
51 | if (prFromSummary !== void 0) {
52 | let { links: links2 } = await getInfoFromPullRequest({
53 | repo,
54 | pull: prFromSummary
55 | });
56 | if (commitFromSummary) {
57 | links2 = {
58 | ...links2,
59 | commit: `[\`${commitFromSummary}\`](https://github.com/${repo}/commit/${commitFromSummary})`
60 | };
61 | }
62 | return links2;
63 | }
64 | const commitToFetchFrom = commitFromSummary || changeset.commit;
65 | if (commitToFetchFrom) {
66 | const { links: links2 } = await getInfo({
67 | repo,
68 | commit: commitToFetchFrom
69 | });
70 | return links2;
71 | }
72 | return {
73 | commit: null,
74 | pull: null,
75 | user: null
76 | };
77 | })();
78 | const suffix = links.pull ? ` (${links.pull})` : links.commit ? ` (${links.commit})` : "";
79 | return `
80 | - ${firstLine}${suffix}
81 | ${futureLines.map((l) => ` ${l}`).join("\n")}`;
82 | }
83 | };
84 | var src_default = changelogFunctions;
85 | export {
86 | src_default as default
87 | };
--------------------------------------------------------------------------------
/scripts/changelog-github-custom.test.ts:
--------------------------------------------------------------------------------
1 | import changelogFunctions from './changelog-github-custom';
2 | import parse from '@changesets/parse';
3 |
4 | const getReleaseLine = changelogFunctions.getReleaseLine;
5 |
6 | describe('Changeset test suite', () => {
7 | test('Should test changeset commit..', () => {
8 | jest.mock(
9 | '@changesets/get-github-info',
10 | (): typeof import('@changesets/get-github-info') => {
11 | // this is duplicated because jest.mock reordering things
12 | const data = {
13 | commit: 'a085003',
14 | user: 'Andarist',
15 | pull: 1613,
16 | repo: 'emotion-js/emotion'
17 | };
18 | const links = {
19 | user: `[@${data.user}](https://github.com/${data.user})`,
20 | pull: `[#${data.pull}](https://github.com/${data.repo}/pull/${data.pull})`,
21 | commit: `[\`${data.commit}\`](https://github.com/${data.repo}/commit/${data.commit})`
22 | };
23 | return {
24 | async getInfo({ commit, repo }) {
25 | expect(commit).toBe(data.commit);
26 | expect(repo).toBe(data.repo);
27 | return {
28 | pull: data.pull,
29 | user: data.user,
30 | links
31 | };
32 | },
33 | async getInfoFromPullRequest({ pull, repo }) {
34 | expect(pull).toBe(data.pull);
35 | expect(repo).toBe(data.repo);
36 | return {
37 | commit: data.commit,
38 | user: data.user,
39 | links
40 | };
41 | }
42 | };
43 | }
44 | );
45 |
46 | const getChangeset = (content: string, commit: string | undefined) => {
47 | return [
48 | {
49 | ...parse(
50 | `---
51 | pkg: "minor"
52 | ---
53 |
54 | something
55 | ${content}
56 | `
57 | ),
58 | id: 'some-id',
59 | commit
60 | },
61 | 'minor',
62 | { repo: data.repo }
63 | ] as const;
64 | };
65 |
66 | const data = {
67 | commit: 'a085003',
68 | user: 'Andarist',
69 | pull: 1613,
70 | repo: 'emotion-js/emotion'
71 | };
72 |
73 | describe.each([data.commit, 'wrongcommit', undefined])(
74 | 'with commit from changeset of %s',
75 | (commitFromChangeset) => {
76 | describe.each(['pr', 'pull request', 'pull'])(
77 | 'override pr with %s keyword',
78 | (keyword) => {
79 | test.each(['with #', 'without #'] as const)('%s', async (kind) => {
80 | expect(
81 | await getReleaseLine(
82 | ...getChangeset(
83 | `${keyword}: ${kind === 'with #' ? '#' : ''}${data.pull}`,
84 | commitFromChangeset
85 | )
86 | )
87 | ).toEqual(
88 | `\n\n- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something\n`
89 | );
90 | });
91 | }
92 | );
93 | test('override commit with commit keyword', async () => {
94 | expect(
95 | await getReleaseLine(
96 | ...getChangeset(`commit: ${data.commit}`, commitFromChangeset)
97 | )
98 | ).toEqual(
99 | `\n\n- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist)! - something\n`
100 | );
101 | });
102 | }
103 | );
104 |
105 | describe.each(['author', 'user'])('override author with %s keyword', (keyword) => {
106 | test.each(['with @', 'without @'] as const)('%s', async (kind) => {
107 | expect(
108 | await getReleaseLine(
109 | ...getChangeset(
110 | `${keyword}: ${kind === 'with @' ? '@' : ''}other`,
111 | data.commit
112 | )
113 | )
114 | ).toEqual(
115 | `\n\n- [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@other](https://github.com/other)! - something\n`
116 | );
117 | });
118 | });
119 |
120 | it('with multiple authors', async () => {
121 | expect(
122 | await getReleaseLine(
123 | ...getChangeset(
124 | ['author: @Andarist', 'author: @mitchellhamilton'].join('\n'),
125 | data.commit
126 | )
127 | )
128 | ).toMatchInlineSnapshot(`
129 | "
130 |
131 | - [#1613](https://github.com/emotion-js/emotion/pull/1613) [\`a085003\`](https://github.com/emotion-js/emotion/commit/a085003) Thanks [@Andarist](https://github.com/Andarist), [@mitchellhamilton](https://github.com/mitchellhamilton)! - something
132 | "
133 | `);
134 | });
135 | });
136 | });
137 |
--------------------------------------------------------------------------------
/scripts/changelog-github-custom.ts:
--------------------------------------------------------------------------------
1 | import type { ChangelogFunctions } from '@changesets/types/dist/declarations/src/index';
2 | import { config } from 'dotenv';
3 | import { getInfo, getInfoFromPullRequest } from '@changesets/get-github-info';
4 |
5 | config();
6 |
7 | const changelogFunctions: ChangelogFunctions = {
8 | getDependencyReleaseLine: async (changesets, dependenciesUpdated, options) => {
9 | if (!options.repo) {
10 | throw new Error(
11 | 'Please provide a repo to this changelog generator like this:\n"changelog": ["@changesets/changelog-github", { "repo": "org/repo" }]'
12 | );
13 | }
14 | if (dependenciesUpdated.length === 0) return '';
15 |
16 | const changesetLink = `- Updated dependencies [${(
17 | await Promise.all(
18 | changesets.map(async (cs) => {
19 | if (cs.commit) {
20 | const { links } = await getInfo({
21 | repo: options.repo,
22 | commit: cs.commit
23 | });
24 | return links.commit;
25 | }
26 | })
27 | )
28 | )
29 | .filter((_) => _)
30 | .join(', ')}]:`;
31 |
32 | const updatedDepenenciesList = dependenciesUpdated.map(
33 | (dependency) => ` - ${dependency.name}@${dependency.newVersion}`
34 | );
35 |
36 | return [changesetLink, ...updatedDepenenciesList].join('\n');
37 | },
38 | getReleaseLine: async (changeset, type, options) => {
39 | if (!options || !options.repo) {
40 | throw new Error(
41 | 'Please provide a repo to this changelog generator like this:\n"changelog": ["@changesets/changelog-github", { "repo": "org/repo" }]'
42 | );
43 | }
44 |
45 | let prFromSummary: number | undefined;
46 | let commitFromSummary: string | undefined;
47 | const usersFromSummary: string[] = [];
48 |
49 | const replacedChangelog = changeset.summary
50 | .replace(/^\s*(?:pr|pull|pull\s+request):\s*#?(\d+)/im, (_, pr) => {
51 | const num = Number(pr);
52 | if (!isNaN(num)) prFromSummary = num;
53 | return '';
54 | })
55 | .replace(/^\s*commit:\s*([^\s]+)/im, (_, commit) => {
56 | commitFromSummary = commit;
57 | return '';
58 | })
59 | .replace(/^\s*(?:author|user):\s*@?([^\s]+)/gim, (_, user) => {
60 | usersFromSummary.push(user);
61 | return '';
62 | })
63 | .trim();
64 |
65 | const [firstLine, ...futureLines] = replacedChangelog.split('\n').map((l) => l.trimEnd());
66 |
67 | const links = await (async () => {
68 | if (prFromSummary !== undefined) {
69 | let { links } = await getInfoFromPullRequest({
70 | repo: options.repo,
71 | pull: prFromSummary
72 | });
73 | if (commitFromSummary) {
74 | links = {
75 | ...links,
76 | commit: `[\`${commitFromSummary}\`](https://github.com/${options.repo}/commit/${commitFromSummary})`
77 | };
78 | }
79 | return links;
80 | }
81 | const commitToFetchFrom = commitFromSummary || changeset.commit;
82 | if (commitToFetchFrom) {
83 | const { links } = await getInfo({
84 | repo: options.repo,
85 | commit: commitToFetchFrom
86 | });
87 | return links;
88 | }
89 | return {
90 | commit: null,
91 | pull: null,
92 | user: null
93 | };
94 | })();
95 |
96 | // ORIGINAL
97 | // const users = usersFromSummary.length
98 | // ? usersFromSummary
99 | // .map(
100 | // (userFromSummary) =>
101 | // `[@${userFromSummary}](https://github.com/${userFromSummary})`
102 | // )
103 | // .join(', ')
104 | // : links.user;
105 |
106 | // const prefix = [
107 | // links.pull === null ? '' : ` ${links.pull}`,
108 | // links.commit === null ? '' : ` ${links.commit}`,
109 | // users === null ? '' : ` Thanks ${users}!`
110 | // ].join('');
111 |
112 | // ORIGINAL
113 | // return `\n\n-${prefix ? `${prefix} -` : ''} ${firstLine}\n${futureLines
114 | // .map((l) => ` ${l}`)
115 | // .join('\n')}`;
116 |
117 | // OWN
118 | const suffix = [
119 | links.pull === null ? '' : ` (${links.pull})`
120 | //links.commit === null ? '' : ` (${links.commit})`
121 | ].join('');
122 |
123 | return `\n\n- ${firstLine}${suffix}\n${futureLines.map((l) => ` ${l}`).join('\n')}`;
124 | }
125 | };
126 |
127 | export default changelogFunctions;
128 |
--------------------------------------------------------------------------------
/static/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gyurielf/svelte-tel-input/cad0abb3775424ecda8d4860122c502be12828de/static/demo.gif
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gyurielf/svelte-tel-input/cad0abb3775424ecda8d4860122c502be12828de/static/favicon.ico
--------------------------------------------------------------------------------
/static/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------