{"id":21538128,"url":"https://github.com/tsvetan-ganev/otp-input","last_synced_at":"2026-05-18T03:11:47.729Z","repository":{"id":263863393,"uuid":"891025736","full_name":"tsvetan-ganev/otp-input","owner":"tsvetan-ganev","description":"Angular component for entering one time password codes.","archived":false,"fork":false,"pushed_at":"2024-11-22T14:34:42.000Z","size":213,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-24T07:44:57.556Z","etag":null,"topics":["angular","component","mfa","otp"],"latest_commit_sha":null,"homepage":"https://ng-otp-input.vercel.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tsvetan-ganev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-19T15:43:24.000Z","updated_at":"2024-11-22T20:28:15.000Z","dependencies_parsed_at":"2024-11-20T22:45:13.922Z","dependency_job_id":null,"html_url":"https://github.com/tsvetan-ganev/otp-input","commit_stats":null,"previous_names":["tsvetan-ganev/otp-input"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsvetan-ganev%2Fotp-input","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsvetan-ganev%2Fotp-input/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsvetan-ganev%2Fotp-input/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsvetan-ganev%2Fotp-input/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tsvetan-ganev","download_url":"https://codeload.github.com/tsvetan-ganev/otp-input/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244110086,"owners_count":20399561,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","component","mfa","otp"],"created_at":"2024-11-24T04:10:41.121Z","updated_at":"2025-10-06T03:24:20.787Z","avatar_url":"https://github.com/tsvetan-ganev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🚧 UNDER DEVELOPMENT 🚧\n\n# @parabolabs/otp-input\n\nAngular component for entering one time passwords built with modern Angular features (signals, zoneless change detection, new control flow syntax).\n\n[Storybook Demo](https://ng-otp-input.vercel.app/)\n\n## Features\n\n- accessibility out of the box\n- zero dependencies\n- high performance (`ChangeDetectionStrategy.OnPush` + signals)\n- customizability\n- handles paste event\n- support for the Forms API (implements `ControlValueAccessor`)\n- core functionality is covered by unit tests\n\n## Installation\n\n```bash\nnpm i @parabolabs/otp-input\n```\n\n## How to use\n\nThe following diagram displays the logical structure of the component:\n\n```mermaid\nflowchart TD\n    A[otp-input] --\u003e B1[group 1]\n    A --\u003e B2[group 2]\n    A --\u003e BN[group N]\n    B1 --\u003e C1[cell]\n    B1 --\u003e C2[cell]\n    B1 --\u003e C3[cell]\n    B2 --\u003e D1[cell]\n    B2 --\u003e D2[cell]\n    B2 --\u003e D3[cell]\n    BN --\u003e E1[cell]\n    BN --\u003e E2[cell]\n    BN --\u003e E3[cell]\n```\n\nCustomizations are possible via CSS custom properties and custom `ng-template`s the cells.\n\nYou can provide any of the following to change the default styling (if not using a custom template):\n\n```text\n// group\n--otp-input-group-gap\n\n// cell\n--otp-input-cell-font-family\n--otp-input-cell-font-size\n--otp-input-cell-color\n--otp-input-cell-width\n--otp-input-cell-height\n--otp-input-cell-border-width\n--otp-input-cell-border-color\n--otp-input-cell-outer-border-radius\n--otp-input-cell-inner-border-radius\n--otp-input-cell-padding\n--otp-input-cell-focus-outline-color\n--otp-input-cell-focus-outline-width\n--otp-input-cell-background\n\n// caret\n--otp-input-caret-width\n--otp-input-caret-height\n--otp-input-caret-color\n--otp-input-caret-animation-duration\n```\n\n\u003e [!TIP]\n\u003e You should use as many of these to make the component match your project's styling preferences.\n\n### Single Input Group\n\n```ts\nimport {\n  OtpInputComponent,\n  OtpInputGroupComponent\n} from '@parabolabs/otp-input';\n\n@Component({\n  selector: 'my-component',\n  standalone: true,\n  imports: [\n    OtpInputComponent,\n    OtpInputGroupComponent,\n    ...\n  ],\n  ...\n})\n```\n\n```html\n\u003cprbl-otp-input [codeLength]=\"6\" [formControl]=\"otpFormControl\"\u003e\n  \u003cprbl-otp-input-group [cells]=\"6\" /\u003e\n\u003c/prbl-otp-input\u003e\n```\n\n### Multiple Input Groups\n\n```html\n\u003cprbl-otp-input [codeLength]=\"8\" [formControl]=\"otpFormControl\"\u003e\n  \u003cprbl-otp-input-group [cells]=\"4\" /\u003e\n  \u003cspan class=\"separator\" aria-hidden=\"true\"\u003e\u003c/span\u003e\n  \u003cprbl-otp-input-group [cells]=\"4\" [cellStartIndex]=\"4\" /\u003e\n\u003c/prbl-otp-input\u003e\n```\n\n### Custom pattern\n\nYou can use one of the patterns provided by the library (`OTP_INPUT_DIGIT_REGEXP` - default and `OTP_INPUT_ALPHANUMERIC_REGEXP`) or provide your own.\n\n\u003e [!TIP]\n\u003e By default on mobile devices the keyboard will only display digits. If you provide your own regex, make sure to also set the `inputMode` to `'text'`.\n\n```html\n\u003cprbl-otp-input [codeLength]=\"8\" [pattern]=\"my_custom_regex\" [inputMode]=\"'text'\"\u003e\n  \u003cprbl-otp-input-group [cells]=\"4\" /\u003e\n  \u003cspan class=\"separator\" aria-hidden=\"true\"\u003e\u003c/span\u003e\n  \u003cprbl-otp-input-group [cells]=\"4\" [cellStartIndex]=\"4\" /\u003e\n\u003c/prbl-otp-input\u003e\n```\n\n### Custom Input Cell Template\n\nYou can provide your own custom `ng-template` that will be rendered for each input cell.\n\n\n\u003e [!NOTE]\n\u003e This is the most powerful and probably most useful feature of this library - you can make the input cells match your project's UI/UX design.\n\n\u003e [!WARNING]\n\u003e Too keep the component accessible, do not use an `input` (or interactive HTML elements) in the cell's content - all the needed events are already handled by the underlying `input` element inside `prbl-otp-input`.\n\nYou can even provide your own caret element, use the built-in one `\u003cprbl-otp-input-caret /\u003e` or not show a caret at all.\n\n```ts\nimport {\n  OtpInputComponent,\n  OtpInputGroupComponent\n} from '@parabolabs/otp-input';\n\n@Component({\n  selector: 'my-component',\n  standalone: true,\n  imports: [\n    OtpInputComponent,\n    OtpInputGroupComponent,\n    OtpInputCaret,\n    ...\n  ],\n  ...\n})\n```\n\n```html\n\u003cprbl-otp-input [codeLength]=\"6\" [formControl]=\"otpFormControl\"\u003e\n  \u003cprbl-otp-input-group [cells]=\"3\" [cellTemplate]=\"customCell\" /\u003e\n  \u003cng-container [ngTemplateOutlet]=\"separator\" /\u003e\n  \u003cprbl-otp-input-group [cells]=\"3\" [cellStartIndex]=\"3\" [cellTemplate]=\"customCell\" /\u003e\n\n  \u003cng-template #separator\u003e\n    \u003cspan class=\"separator-2\" aria-hidden=\"true\"\u003e\u003c/span\u003e\n  \u003c/ng-template\u003e\n\n  \u003c!-- custom template --\u003e\n  \u003cng-template #customCell let-index=\"index\" let-isSelected=\"isSelected\" let-symbol=\"symbol\"\u003e\n    \u003cdiv class=\"custom-otp-cell\" [class.selected]=\"isSelected\" aria-hidden=\"true\"\u003e\n      \u003cng-container *ngIf=\"symbol\"\u003e{{ symbol }}\u003c/ng-container\u003e\n      \u003cng-container *ngIf=\"!symbol \u0026\u0026 isSelected\"\u003e\n        \u003cprbl-otp-input-caret /\u003e\n        \u003c!-- you can also provide your own caret --\u003e\n      \u003c/ng-container\u003e\n    \u003c/div\u003e\n  \u003c/ng-template\u003e\n\u003c/prbl-otp-input\u003e\n```\n\nThe template receives the following context:\n\n```ts\nexport interface OtpInputTemplateRefContext {\n  index: number;\n  isSelected: boolean;\n  symbol: string | undefined;\n}\n```\n\n## Built-in behavior\n\n- The first empty cell is focused or if all values are filled - the last cell.\n- Focus moves to the next empty cell after a value was entered.\n- When focus is on the last cell, pressing a key will update its value.\n- When focus is on a cell with value, pressing delete will delete the value and keep focus in the cell.\n- By default only digits are allowed to be entered. Can be changed via the `pattern` input (see examples).\n- You can use the arrow keys to move backwards and forwards inside the cells - however, the rightmost allowed cell is always the first empty one.\n- You can delete/replace a filled cell's value at any position (using the arrow keys).\n- Pasting replaces everything that's already been entered by the user.\n- Pasting only triggers if all the characters in the pasted text pass the `pattern` validation.\n- Entering characters in arbitrary order (e.g. fill them from right to left) is intentionally not supported.\n- `FormControl` value is either the full code or an empty string.\n\n## Component API\n\n### `prbl-otp-input`\n\n| Input         | Type             | Required | Default               | Description                                                   |\n|---------------|------------------|:--------:|----------------------:|---------------------------------------------------------------|\n| `codeLength`  | `number`         | ✔️        |                       | The length of the OTP code                                    |\n| `pattern`     | `RegExp`         | ✖️        | `/^[0-9]$/`           | Regex which validates each entered symbol                     |\n| `label`       | `string`         | ✖️        | `undefined`           | `aria-label` for the input                                    |\n| `id`          | `string`         | ✖️        | `crypto.randomUUID()` | `id` for the input                                            |\n| `inputMode`   | `string`         | ✖️        | `numeric`             | Sets the `inputMode` for the underlying `input` element       |\n| `disabled`    | `boolean`        | ✖️        | `false`               | Whether the input is disabled                                 |\n| `codeEntered` | `output\u003cstring\u003e` | ✖️        |                       | Called when the user fills in all the characters of the code  |\n\n### `prbl-otp-input-group`\n\n| Input            | Type          | Required | Default     | Description                                         |\n|------------------|---------------|:--------:|------------:|-----------------------------------------------------|\n| `cells`          | `number`      | ️️️️️✔️        |             | How many character cells should be rendered.        |\n| `cellStartIndex` | `number`      | ✖️        | `0`         | The starting index for the first cell in the group. |\n| `cellTemplate`   | `TemplateRef` | ✖️        | `undefined` | `` instance which will be rendered for each cell    |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsvetan-ganev%2Fotp-input","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftsvetan-ganev%2Fotp-input","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsvetan-ganev%2Fotp-input/lists"}