{"name":"input-otp","title":"Input OTP","description":"Accessible one-time password component with copy paste functionality.","installCommand":"bunx marko-ui add input-otp -y","usageTags":"<InputOTP>","importSnippet":"import InputOTP from \"@/components/ui/input-otp/input-otp.marko\";","usageSnippet":"<InputOTP length=6/>","tags":["InputOtp"],"parts":[],"props":[{"name":"autoFocus","type":"boolean | undefined","required":false,"description":"Whether to auto-focus the first input."},{"name":"autoSubmit","type":"boolean | undefined","required":false,"description":"Whether to auto-submit the owning form when all inputs are filled."},{"name":"blurOnComplete","type":"boolean | undefined","required":false,"description":"Whether to blur the input when the value is complete"},{"name":"count","type":"number | undefined","required":false,"description":"The number of inputs to render to improve SSR aria attributes.\nThis will be required in next major version."},{"name":"defaultValue","type":"string[] | undefined","required":false,"description":"The initial value of the the pin input when rendered.\nUse when you don't need to control the value of the pin input."},{"name":"dir","type":"'ltr' | 'rtl' | undefined","required":false,"default":"ltr","description":"The document's text/writing direction."},{"name":"disabled","type":"boolean | undefined","required":false,"description":"Whether the inputs are disabled"},{"name":"form","type":"string | undefined","required":false,"description":"The associate form of the underlying input element."},{"name":"getRootNode","type":"(() => ShadowRoot | Document | Node) | undefined","required":false,"description":"A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron."},{"name":"groupSize","type":"number","required":false},{"name":"ids","type":"ElementIds | undefined","required":false,"description":"The ids of the elements in the pin input. Useful for composition."},{"name":"invalid","type":"boolean | undefined","required":false,"description":"Whether the pin input is in the invalid state"},{"name":"length","type":"number","required":false},{"name":"mask","type":"boolean | undefined","required":false,"description":"If `true`, the input's value will be masked just like `type=password`"},{"name":"name","type":"string | undefined","required":false,"description":"The name of the input element. Useful for form submission."},{"name":"otp","type":"boolean | undefined","required":false,"description":"If `true`, the pin input component signals to its fields that they should\nuse `autocomplete=\"one-time-code\"`."},{"name":"pattern","type":"string | undefined","required":false,"description":"The regular expression that the user-entered input value is checked against."},{"name":"placeholder","type":"string | undefined","required":false,"default":"○","description":"The placeholder text for the input"},{"name":"readOnly","type":"boolean | undefined","required":false,"description":"Whether the pin input is in the valid state"},{"name":"required","type":"boolean | undefined","required":false,"description":"Whether the pin input is required"},{"name":"sanitizeValue","type":"((value: string) => string) | undefined","required":false,"description":"Function to sanitize pasted values before validation.\nUseful for stripping dashes, spaces, or other formatting."},{"name":"selectOnFocus","type":"boolean | undefined","required":false,"description":"Whether to select input value when input is focused"},{"name":"translations","type":"IntlTranslations | undefined","required":false,"description":"Specifies the localized strings that identifies the accessibility elements and their states"},{"name":"type","type":"'numeric' | 'alphanumeric' | 'alphabetic' | undefined","required":false,"default":"numeric","description":"The type of value the pin-input should allow"},{"name":"value","type":"string[] | undefined","required":false,"description":"The controlled value of the the pin input."}],"events":[{"name":"onValueChange","arg":"ValueChangeDetails","description":"Function called on input change"},{"name":"onValueComplete","arg":"ValueChangeDetails","description":"Function called when all inputs have valid values"},{"name":"onValueInvalid","arg":"ValueInvalidDetails","description":"Function called when an invalid value is entered"},{"name":"valueChange","arg":"string[]"},{"name":"valueCompleteChange","arg":"string[]"}],"keyboard":[{"keys":"0-9 / a-z","description":"Types into the focused slot and advances to the next one (character set constrained by `pattern` or `type`)."},{"keys":"Backspace","description":"Clears the focused slot and moves focus to the previous slot."},{"keys":"Left / Right","description":"Moves focus to the adjacent slot."},{"keys":"Ctrl/Cmd+V","description":"Pastes and distributes a full code across the slots, validated against `pattern`/`type`."}],"accessibilityNotes":["Each slot renders as a real `<input>` with `aria-label` identifying its position (\"digit N of M\"), `aria-invalid` when the `invalid` prop is set, and `data-complete`/`data-disabled` state attributes shared with the root for styling hooks.","Only one slot is ever tabbable at a time — the machine keeps `tabindex=0` on the next empty slot (or the last filled one when complete) and `tabindex=-1` on the rest, so Tab moves straight from the input into and back out of the whole group instead of stopping at every slot.","A visually-hidden native `<input>` mirrors the joined value for native form participation (`name`, `required`, `form` all pass through to it), so the component works inside a plain HTML form submit without extra wiring.","Pasting a full code is supported and validated against `pattern`/`type` before it's accepted; an invalid paste is rejected rather than partially applied."],"examples":[{"id":"input-otp-demo","title":"Basic","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\n\n<InputOTP length=6/>","description":"Set `length` to the number of slots the code should have."},{"id":"input-otp-pattern","title":"Pattern","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\nimport Field from \"@marko-ui/shadcn/ui/field/field.marko\";\nimport FieldLabel from \"@marko-ui/shadcn/ui/field/field-label.marko\";\n\n<Field class=\"w-fit\">\n  <FieldLabel for=\"digits-only\">Digits Only</FieldLabel>\n  <InputOTP id=\"digits-only\" length=6 pattern=\"^\\d+$\"/>\n</Field>","description":"Constrain accepted characters with `pattern`, a regular-expression source string. Upstream imports the `REGEXP_ONLY_DIGITS`/`REGEXP_ONLY_DIGITS_AND_CHARS` constants from the `input-otp` package; this port inlines the equivalent regex source directly since that package isn't a dependency here."},{"id":"input-otp-separator","title":"Grouped","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\n\n<InputOTP length=6 groupSize=3/>","description":"Set `groupSize` to insert a separator between fixed-size groups of slots."},{"id":"input-otp-masked","title":"Masked","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\n\n<InputOTP length=6 mask/>","description":"Pass `mask` to render entered characters as dots, like a password field."},{"id":"input-otp-disabled","title":"Disabled","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\n\n<InputOTP length=6 disabled/>","description":"A disabled input is skipped by the tab order and ignores pointer input."},{"id":"input-otp-invalid","title":"Invalid","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\n\n<InputOTP length=6 defaultValue=[\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"] groupSize=2 invalid/>","description":"Pass `invalid` to mark every slot's `aria-invalid`/`data-invalid` state. The machine applies this at the root, not per slot — upstream instead sets `aria-invalid` on individual `InputOTPSlot`s, which this single-tag port has no equivalent for."},{"id":"input-otp-four-digits","title":"Four Digits","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\n\n<InputOTP length=4 pattern=\"^\\d+$\"/>","description":"A common pattern for PIN codes: set `length=4` with a digits-only `pattern`."},{"id":"input-otp-alphanumeric","title":"Alphanumeric","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\n\n<InputOTP length=6 groupSize=3 pattern=\"^[a-zA-Z0-9]+$\"/>","description":"Use a `pattern` that accepts both letters and numbers."},{"id":"input-otp-controlled","title":"Controlled","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\n\n<let/value=[\"1\", \"2\", \"3\", \"\", \"\", \"\"]/>\n\n<div class=\"space-y-2\">\n  <InputOTP\n    length=6\n    value=value\n    valueChange(next) { value = next; }\n  />\n  <p class=\"text-sm text-muted-foreground\">Value: ${value.join(\"\")}</p>\n</div>","description":"Zag machines are controlled: a `value` prop without a change handler never moves. Pair it with `valueChange`, which receives the array of per-slot characters.","essential":true},{"id":"input-otp-form","title":"Form","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\nimport Card from \"@marko-ui/shadcn/ui/card/card.marko\";\nimport CardHeader from \"@marko-ui/shadcn/ui/card/header.marko\";\nimport CardTitle from \"@marko-ui/shadcn/ui/card/title.marko\";\nimport CardDescription from \"@marko-ui/shadcn/ui/card/description.marko\";\nimport CardContent from \"@marko-ui/shadcn/ui/card/content.marko\";\nimport CardFooter from \"@marko-ui/shadcn/ui/card/footer.marko\";\nimport Field from \"@marko-ui/shadcn/ui/field/field.marko\";\nimport FieldLabel from \"@marko-ui/shadcn/ui/field/field-label.marko\";\nimport FieldDescription from \"@marko-ui/shadcn/ui/field/field-description.marko\";\nimport Button from \"@marko-ui/shadcn/ui/button/button.marko\";\nimport Icon from \"@marko-ui/shadcn/ui/icon/icon.marko\";\n\n<Card class=\"mx-auto max-w-md\">\n  <CardHeader>\n    <CardTitle>Verify your login</CardTitle>\n    <CardDescription>\n      Enter the verification code we sent to your email address: <span class=\"font-medium\">m@example.com</span>.\n    </CardDescription>\n  </CardHeader>\n  <CardContent>\n    <Field>\n      <div class=\"flex items-center justify-between\">\n        <FieldLabel for=\"otp-verification\">Verification code</FieldLabel>\n        <Button type=\"button\" variant=\"outline\" size=\"xs\">\n          <Icon name=\"RefreshCwIcon\"/>\n          Resend Code\n        </Button>\n      </div>\n      <InputOTP length=6 groupSize=3 id=\"otp-verification\" required\n        class=\"*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl\"\n      />\n      <FieldDescription>\n        <a href=\"#\">I no longer have access to this email address.</a>\n      </FieldDescription>\n    </Field>\n  </CardContent>\n  <CardFooter>\n    <Field>\n      <Button type=\"submit\" class=\"w-full\">Verify</Button>\n      <div class=\"text-sm text-muted-foreground\">\n        Having trouble signing in? <a href=\"#\" class=\"underline underline-offset-4 transition-colors hover:text-primary\">Contact support</a>\n      </div>\n    </Field>\n  </CardFooter>\n</Card>","description":"A full verification-code form built from `Card`, `Field`, and `InputOTP`."},{"id":"input-otp-rtl","title":"RTL","source":"import InputOTP from \"@marko-ui/shadcn/ui/input-otp/input-otp.marko\";\nimport Field from \"@marko-ui/shadcn/ui/field/field.marko\";\nimport FieldLabel from \"@marko-ui/shadcn/ui/field/field-label.marko\";\n\n<Field class=\"mx-auto max-w-xs\">\n  <FieldLabel for=\"input-otp-rtl\">رمز التحقق</FieldLabel>\n  <InputOTP id=\"input-otp-rtl\" length=6 defaultValue=[\"1\",\"2\",\"3\",\"4\",\"5\",\"6\"] dir=\"rtl\"/>\n</Field>","description":"The machine's own `dir` prop drives right-to-left slot layout and arrow-key navigation directly, with no separate RTL configuration step."}],"importInstallCommand":"bun add @marko-ui/shadcn marko-zag","nativeAttributes":"div","composition":"`InputOTP` is a single component, not four. Where upstream composes `InputOTP` / `InputOTPGroup` / `InputOTPSlot` / `InputOTPSeparator`, this port folds all four into one tag backed by a Zag `pin-input` machine: `length` sets the slot count (sugar for the machine's `count` prop) and `groupSize` inserts a separator automatically between fixed-size groups — there is no separate `InputOTPGroup`, `InputOTPSlot`, or `InputOTPSeparator` tag to import or compose by hand."}
