{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "input",
  "title": "Input",
  "type": "registry:ui",
  "description": "Text input — single-line. Three sizes, three variants (outlined / filled / borderless), error / warning status, prefix / suffix nodes, addonBefore / addonAfter, allow-clear, password toggle, char count, and composite InputGroup with addons and action buttons.",
  "categories": [
    "control"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/input/input-group.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { cn } from '../../lib/utils'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\n\nconst groupSizeClasses: Record<string, string> = {\n  small: 'h-8 text-xs',\n  middle: 'h-9 text-sm',\n  large: 'h-11 text-base',\n}\n\n/**\n * <uip-input-group> — React's `InputGroup`: one bordered row holding\n * <uip-input-group-addon>, <uip-input> and <uip-input-group-button> children.\n *\n * Two shadow-DOM adaptations:\n *  - React restyles the nested Input through descendant selectors\n *    (`[&_[data-slot=input]]:…`, `[&_input]:…`); those can't reach into\n *    <uip-input>'s shadow root, so <uip-input> applies the same classes itself\n *    when it's inside a group (the strings are kept here for parity, inert).\n *  - `first:` / `last:` on the addon/button can't see light-DOM siblings from\n *    inside their shadow roots, so the group marks its first and last child\n *    with `data-first` / `data-last` and they use `data-[first]:` / `data-[last]:`.\n */\nexport class UipInputGroup extends LitElement {\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  static properties = {\n    size: { reflect: true },\n    disabled: { type: Boolean, reflect: true },\n  }\n\n  size: 'small' | 'middle' | 'large' = 'middle'\n  disabled = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'input-group')\n  }\n\n  willUpdate() {\n    this.setAttribute('data-size', this.size)\n    this.toggleAttribute('data-disabled', this.disabled)\n  }\n\n  private onSlotChange(e: Event) {\n    const els = (e.target as HTMLSlotElement).assignedElements()\n    els.forEach((el, i) => {\n      el.toggleAttribute('data-first', i === 0)\n      el.toggleAttribute('data-last', i === els.length - 1)\n    })\n  }\n\n  render() {\n    return html`<div\n      part=\"base\"\n      data-slot=\"input-group\"\n      data-size=${this.size}\n      ?data-disabled=${this.disabled}\n      class=${cn(\n        'group/input-group border-input bg-background relative flex w-full items-stretch rounded-md border shadow-xs transition-[color,box-shadow]',\n        'focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px] focus-within:outline-none',\n        '[&_[data-slot=input]]:rounded-none [&_[data-slot=input]]:border-0 [&_[data-slot=input]]:bg-transparent [&_[data-slot=input]]:shadow-none [&_[data-slot=input]]:focus-within:ring-0',\n        '[&_input]:h-full [&_input]:flex-1 [&_input]:border-0 [&_input]:bg-transparent [&_input]:px-3 [&_input]:text-sm [&_input]:outline-none [&_input]:focus-visible:ring-0',\n        this.disabled && 'bg-muted/30 pointer-events-none cursor-not-allowed opacity-50',\n        groupSizeClasses[this.size] ?? groupSizeClasses.middle,\n      )}\n    >\n      <slot class=\"[&::slotted(uip-input)]:w-full\" @slotchange=${this.onSlotChange}></slot>\n    </div>`\n  }\n}\n\n/** <uip-input-group-addon> — React's `InputGroupAddon` (static text or an icon). */\nexport class UipInputGroupAddon extends LitElement {\n  // flex so the inner addon stretches to the group's height like React's flex item.\n  static styles = [tailwind, css`:host { display: flex; }`]\n\n  static properties = {\n    align: { reflect: true },\n    first: { type: Boolean, attribute: 'data-first' },\n    last: { type: Boolean, attribute: 'data-last' },\n  }\n\n  align: 'inline' | 'block' = 'inline'\n  first = false\n  last = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'input-group-addon')\n  }\n\n  render() {\n    return html`<div\n      part=\"base\"\n      data-slot=\"input-group-addon\"\n      ?data-first=${this.first}\n      ?data-last=${this.last}\n      class=${cn(\n        'text-muted-foreground flex shrink-0 items-center justify-center px-3 text-sm select-none',\n        'border-input data-[first]:rounded-l-[calc(var(--radius)-1px)] data-[last]:rounded-r-[calc(var(--radius)-1px)]',\n        'border-r data-[first]:border-l-0 data-[last]:border-r-0',\n      )}\n    >\n      <slot></slot>\n    </div>`\n  }\n}\n\nconst buttonVariantClasses: Record<string, string> = {\n  default: 'bg-primary text-primary-foreground hover:bg-primary/90',\n  secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',\n  ghost: 'hover:bg-accent hover:text-accent-foreground',\n  outline: 'border-l border-input hover:bg-accent hover:text-accent-foreground',\n}\n\n/**\n * <uip-input-group-button> — React's `InputGroupButton`. `type=\"submit\"` /\n * `\"reset\"` act on the host's form (a shadow <button> can't on its own).\n */\nexport class UipInputGroupButton extends LitElement {\n  static formAssociated = true\n  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n  // flex so the inner button stretches to the group's height like React's flex item.\n  static styles = [tailwind, css`:host { display: flex; }`]\n\n  static properties = {\n    variant: { reflect: true },\n    type: { reflect: true },\n    disabled: { type: Boolean, reflect: true },\n    accessibleLabel: { attribute: 'aria-label' },\n    first: { type: Boolean, attribute: 'data-first' },\n    last: { type: Boolean, attribute: 'data-last' },\n  }\n\n  variant: 'default' | 'secondary' | 'ghost' | 'outline' = 'ghost'\n  type: 'button' | 'submit' | 'reset' = 'button'\n  disabled = false\n  accessibleLabel?: string\n  first = false\n  last = false\n  private internals = this.attachInternals()\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'input-group-button')\n  }\n\n  private onClick() {\n    const form = this.internals.form\n    if (this.disabled || !form) return\n    if (this.type === 'submit') form.requestSubmit()\n    else if (this.type === 'reset') form.reset()\n  }\n\n  render() {\n    return html`<button\n      part=\"base\"\n      type=\"button\"\n      data-slot=\"input-group-button\"\n      ?data-first=${this.first}\n      ?data-last=${this.last}\n      ?disabled=${this.disabled}\n      aria-label=${this.accessibleLabel ?? nothing}\n      class=${cn(\n        'inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 px-3 text-sm font-medium transition-colors select-none',\n        'data-[first]:rounded-l-[calc(var(--radius)-1px)] data-[last]:rounded-r-[calc(var(--radius)-1px)]',\n        'focus-visible:ring-ring focus-visible:ring-1 focus-visible:outline-none',\n        'disabled:pointer-events-none disabled:opacity-50',\n        buttonVariantClasses[this.variant] ?? buttonVariantClasses.ghost,\n      )}\n      @click=${this.onClick}\n    >\n      <slot></slot>\n    </button>`\n  }\n}\n\ncustomElements.get('uip-input-group') || customElements.define('uip-input-group', UipInputGroup)\ncustomElements.get('uip-input-group-addon') || customElements.define('uip-input-group-addon', UipInputGroupAddon)\ncustomElements.get('uip-input-group-button') || customElements.define('uip-input-group-button', UipInputGroupButton)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-input-group': UipInputGroup\n    'uip-input-group-addon': UipInputGroupAddon\n    'uip-input-group-button': UipInputGroupButton\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/input/input-group.ts"
    },
    {
      "path": "packages/elements-lit/src/components/input/input.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { live } from 'lit/directives/live.js'\nimport { Eye, EyeOff, X } from 'lucide'\nimport { cn } from '../../lib/utils'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { icon } from '../../lib/icon'\n\ntype Size = 'small' | 'middle' | 'large'\ntype Variant = 'outlined' | 'filled' | 'borderless'\ntype Status = 'error' | 'warning'\n\nconst sizeClasses: Record<Size, string> = {\n  small: 'h-8 text-xs',\n  middle: 'h-9 text-base md:text-sm',\n  large: 'h-11 text-base',\n}\n\nconst variantMap: Record<Variant, string> = {\n  outlined: 'border-input bg-transparent shadow-xs',\n  filled: 'border-transparent bg-muted/50 shadow-none',\n  borderless: 'border-transparent bg-transparent shadow-none',\n}\n\nconst statusMap: Record<Status, string> = {\n  error:\n    'border-destructive focus-within:border-destructive focus-within:ring-destructive/20 dark:focus-within:ring-destructive/40',\n  warning: 'border-warning focus-within:border-warning focus-within:ring-warning/20',\n}\n\n// React's <InputGroup> styles a nested Input through descendant selectors\n// (`[&_[data-slot=input]]:…`, `[&_input]:…`), which can't reach into this\n// shadow root — so the input applies those same classes itself when it sits\n// inside a <uip-input-group>.\nconst groupWrapperClasses = 'rounded-none border-0 bg-transparent shadow-none focus-within:ring-0'\nconst groupInputClasses = 'h-full flex-1 border-0 bg-transparent px-3 text-sm outline-none focus-visible:ring-0'\n\nconst actionButtonClasses =\n  'text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 shrink-0 rounded p-0.5 transition-colors focus-visible:ring-1 focus-visible:outline-none'\n\nconst SLOTS = ['prefix', 'suffix', 'addon-before', 'addon-after'] as const\n\n/**\n * <uip-input> — the registry Input as a web component.\n *\n * Class strings are React's `Input` verbatim. React's node props become named\n * slots; their string forms stay attributes:\n *  - `prefix` / `suffix` attributes, or `slot=\"prefix\"` / `slot=\"suffix\"`\n *    (also covers React's `prefixIcon` / `suffixIcon` shorthands);\n *  - `addon-before` / `addon-after` attributes, or the same-named slots.\n * Other props: `size` (small | middle | large), `variant`, `status`,\n * `allow-clear`, `show-count` (needs `maxlength`), `show-password-toggle`,\n * plus the native `type`, `placeholder`, `name`, `disabled`, `readonly`,\n * `required`, `maxlength`, `minlength`, `pattern`, `min`, `max`, `step`,\n * `autocomplete`, `spellcheck` (`spellcheck=\"false\"`, or the `spellCheck`\n * property — what React's `spellCheck={false}` sets). The `value` attribute is\n * the default value; the `value` property is the live one.\n *\n * Form-associated: submits `name=value`, mirrors the inner <input>'s native\n * validity (required, pattern, type=email…), resets to the `value` attribute.\n * A light-DOM `<label for>` pointing at the host focuses and names the input.\n * Enter submits the host's form (`form.requestSubmit()`), like a native\n * single-line input's implicit submission; cancel the `keydown` to prevent it.\n *\n * Events: `input` (the native, composed one) and `change` (re-dispatched,\n * composed). Read `.value` on the host.\n */\nexport class UipInput extends LitElement {\n  static formAssociated = true\n  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n  // :host display can't be a utility class (the host has no template of its own).\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  static properties = {\n    value: {},\n    size: { reflect: true },\n    variant: { reflect: true },\n    status: { reflect: true },\n    // `prefix` is already a (read-only) Element property, hence prefixText.\n    prefixText: { attribute: 'prefix' },\n    suffix: {},\n    addonBefore: { attribute: 'addon-before' },\n    addonAfter: { attribute: 'addon-after' },\n    allowClear: { type: Boolean, attribute: 'allow-clear' },\n    showCount: { type: Boolean, attribute: 'show-count' },\n    showPasswordToggle: { type: Boolean, attribute: 'show-password-toggle' },\n    type: {},\n    placeholder: {},\n    name: { reflect: true },\n    disabled: { type: Boolean, reflect: true },\n    readOnly: { type: Boolean, attribute: 'readonly', reflect: true },\n    required: { type: Boolean, reflect: true },\n    maxLength: { type: Number, attribute: 'maxlength' },\n    minLength: { type: Number, attribute: 'minlength' },\n    pattern: {},\n    min: {},\n    max: {},\n    step: {},\n    autocomplete: {},\n    // `spellcheck` is already an HTMLElement property (and does nothing on the\n    // host), so the forwarded value lives on `spellCheck` — React's prop name.\n    spellCheck: {\n      attribute: 'spellcheck',\n      converter: { fromAttribute: (v: string | null) => (v === null ? undefined : v !== 'false') },\n    },\n    accessibleLabel: { attribute: 'aria-label' },\n    ariaInvalidAttr: { attribute: 'aria-invalid' },\n    focused: { state: true },\n    hovered: { state: true },\n    passwordVisible: { state: true },\n    slotted: { state: true },\n    inGroup: { state: true },\n    labelText: { state: true },\n  }\n\n  value = ''\n  size: Size = 'middle'\n  variant: Variant = 'outlined'\n  status?: Status\n  prefixText?: string\n  suffix?: string\n  addonBefore?: string\n  addonAfter?: string\n  allowClear = false\n  showCount = false\n  showPasswordToggle = false\n  type = 'text'\n  placeholder?: string\n  name?: string\n  disabled = false\n  readOnly = false\n  required = false\n  maxLength?: number\n  minLength?: number\n  pattern?: string\n  min?: string\n  max?: string\n  step?: string\n  autocomplete?: string\n  spellCheck?: boolean\n  accessibleLabel?: string\n  ariaInvalidAttr?: string\n  private focused = false\n  private hovered = false\n  private passwordVisible = false\n  private slotted = new Set<string>()\n  private inGroup = false\n  private labelText?: string\n  private defaultValue = ''\n  private internals = this.attachInternals()\n  private childObserver?: MutationObserver\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'input')\n    this.defaultValue = this.getAttribute('value') ?? ''\n    this.inGroup = !!this.parentElement?.closest('uip-input-group')\n    // Named-slot content decides whether the prefix/suffix/addon containers\n    // render at all (React doesn't mount them when empty), and frameworks may\n    // add it after connect.\n    this.readSlots()\n    this.childObserver = new MutationObserver(() => this.readSlots())\n    this.childObserver.observe(this, { childList: true, attributes: true, attributeFilter: ['slot'], subtree: true })\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback()\n    this.childObserver?.disconnect()\n  }\n\n  private readSlots() {\n    this.slotted = new Set([...this.children].map((c) => c.slot).filter((s) => (SLOTS as readonly string[]).includes(s)))\n  }\n\n  protected willUpdate(changed: Map<string, unknown>) {\n    if (changed.has('value')) this.internals.setFormValue(this.value ?? '')\n    const labels = [...(this.internals.labels ?? [])] as HTMLElement[]\n    this.labelText = labels.map((l) => l.textContent?.trim()).filter(Boolean).join(' ') || undefined\n  }\n\n  protected updated() {\n    this.syncValidity()\n  }\n\n  private get input() {\n    return this.renderRoot?.querySelector<HTMLInputElement>('input')\n  }\n\n  // The inner <input> already computes native validity for every constraint\n  // (required, pattern, type, min/max…); mirror it onto the host.\n  private syncValidity() {\n    const i = this.input\n    if (!i) return\n    if (i.validity.valid) this.internals.setValidity({})\n    else this.internals.setValidity(i.validity, i.validationMessage, i)\n  }\n\n  formResetCallback() {\n    this.value = this.defaultValue\n  }\n  formDisabledCallback(disabled: boolean) {\n    this.disabled = disabled\n  }\n\n  private onInput(e: Event) {\n    // The native `input` event is composed, so it already reaches host\n    // listeners — just keep `.value` current before it gets there.\n    this.value = (e.target as HTMLInputElement).value\n  }\n\n  // Native implicit submission runs on the Enter key's default action, which a\n  // shadow-root input can't reach the host's form with. `keypress` only fires\n  // when `keydown` wasn't cancelled (and not mid-IME composition), so a\n  // consumer's keydown preventDefault still stops it, as it would natively.\n  private onKeypress(e: KeyboardEvent) {\n    if (e.key !== 'Enter' || e.defaultPrevented || this.disabled) return\n    const form = this.internals.form\n    if (!form) return\n    e.preventDefault()\n    form.requestSubmit()\n  }\n\n  private onChange() {\n    this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n  }\n\n  private handleClear() {\n    this.value = ''\n    this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n    this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n    this.input?.focus()\n  }\n\n  private togglePassword() {\n    this.passwordVisible = !this.passwordVisible\n    this.input?.focus()\n  }\n\n  private addonClasses(position: 'before' | 'after') {\n    const roundedClass =\n      position === 'before' ? 'rounded-l-md rounded-r-none border-r-0' : 'rounded-r-md rounded-l-none border-l-0'\n    return cn(\n      'flex items-center bg-muted px-3 text-sm text-muted-foreground border border-input',\n      roundedClass,\n      sizeClasses[this.size] ?? sizeClasses.middle,\n    )\n  }\n\n  render() {\n    const size: Size = this.size in sizeClasses ? this.size : 'middle'\n    const variant: Variant = this.variant in variantMap ? this.variant : 'outlined'\n    const status = this.status && this.status in statusMap ? this.status : undefined\n    const currentValue = this.value ?? ''\n\n    const isPassword = this.type === 'password'\n    const hasPrefix = !!this.prefixText || this.slotted.has('prefix')\n    const hasSuffix = !!this.suffix || this.slotted.has('suffix')\n    const hasAddonBefore = !!this.addonBefore || this.slotted.has('addon-before')\n    const hasAddonAfter = !!this.addonAfter || this.slotted.has('addon-after')\n\n    const hasPasswordToggle = this.showPasswordToggle && isPassword\n    const hasCount = this.showCount && this.maxLength != null\n    const hasRightConfig = this.allowClear || hasPasswordToggle || hasCount\n\n    const showClear =\n      this.allowClear && currentValue.length > 0 && (this.focused || this.hovered) && !this.disabled && !this.readOnly\n    const computedType = !isPassword ? this.type : this.passwordVisible ? 'text' : 'password'\n\n    const wrapperRounded =\n      hasAddonBefore && hasAddonAfter\n        ? 'rounded-none'\n        : hasAddonBefore\n          ? 'rounded-l-none rounded-r-md'\n          : hasAddonAfter\n            ? 'rounded-r-none rounded-l-md'\n            : 'rounded-md'\n\n    const wrapperClasses = cn(\n      'flex w-full items-center gap-1.5 overflow-hidden border transition-[color,box-shadow] outline-none',\n      sizeClasses[size],\n      variantMap[variant],\n      status ? statusMap[status] : '',\n      !status ? 'focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]' : '',\n      this.disabled ? 'pointer-events-none opacity-50 cursor-not-allowed bg-muted/30' : '',\n      'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',\n      wrapperRounded,\n      this.inGroup && groupWrapperClasses,\n    )\n\n    const sidePad =\n      size === 'small' ? { l: 'pl-2', r: 'pr-2' } : size === 'large' ? { l: 'pl-3', r: 'pr-3' } : { l: 'pl-2.5', r: 'pr-2.5' }\n\n    const hasLeft = hasPrefix\n    const hasRight = hasSuffix || hasRightConfig\n    const inputPadding =\n      !hasLeft && !hasRight\n        ? cn(sidePad.l, sidePad.r)\n        : hasLeft && !hasRight\n          ? cn('pl-0', sidePad.r)\n          : !hasLeft && hasRight\n            ? cn(sidePad.l, 'pr-0')\n            : 'px-0'\n\n    // status=error implies invalid for AT; also set on the native input.\n    const resolvedAriaInvalid = status === 'error' ? 'true' : this.ariaInvalidAttr\n\n    return html`<div class=\"flex w-full\">\n      ${hasAddonBefore\n        ? html`<div class=${this.addonClasses('before')}>${this.addonBefore ?? nothing}<slot name=\"addon-before\"></slot></div>`\n        : nothing}\n\n      <div\n        part=\"control\"\n        class=${wrapperClasses}\n        data-slot=\"input\"\n        aria-invalid=${resolvedAriaInvalid ?? nothing}\n        @mouseenter=${() => (this.hovered = true)}\n        @mouseleave=${() => (this.hovered = false)}\n        @click=${() => this.input?.focus()}\n      >\n        ${hasPrefix\n          ? html`<span class=${cn('text-muted-foreground pointer-events-none shrink-0 select-none', sidePad.l)}\n              >${this.prefixText ?? nothing}<slot name=\"prefix\"></slot\n            ></span>`\n          : nothing}\n\n        <input\n          part=\"input\"\n          .value=${live(currentValue)}\n          type=${computedType}\n          name=${this.name ?? nothing}\n          placeholder=${this.placeholder ?? nothing}\n          ?disabled=${this.disabled}\n          ?readonly=${this.readOnly}\n          ?required=${this.required}\n          maxlength=${this.maxLength ?? nothing}\n          minlength=${this.minLength ?? nothing}\n          pattern=${this.pattern ?? nothing}\n          min=${this.min ?? nothing}\n          max=${this.max ?? nothing}\n          step=${this.step ?? nothing}\n          autocomplete=${(this.autocomplete as AutoFill | undefined) ?? nothing}\n          spellcheck=${this.spellCheck == null ? nothing : this.spellCheck ? 'true' : 'false'}\n          aria-label=${this.accessibleLabel ?? this.labelText ?? nothing}\n          aria-invalid=${resolvedAriaInvalid ?? nothing}\n          @input=${this.onInput}\n          @change=${this.onChange}\n          @keypress=${this.onKeypress}\n          @focus=${() => (this.focused = true)}\n          @blur=${() => (this.focused = false)}\n          class=${cn(\n            'w-full min-w-0 flex-1 bg-transparent outline-none',\n            'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground',\n            'file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium',\n            'disabled:cursor-not-allowed',\n            inputPadding,\n            this.inGroup && groupInputClasses,\n          )}\n        />\n\n        ${hasRight\n          ? html`<div class=${cn('flex shrink-0 items-center gap-1', sidePad.r)}>\n              ${showClear\n                ? html`<button\n                    type=\"button\"\n                    aria-label=\"Clear input\"\n                    class=${actionButtonClasses}\n                    @mousedown=${(e: MouseEvent) => e.preventDefault()}\n                    @click=${this.handleClear}\n                  >\n                    ${icon(X, 'x', 'size-4')}\n                  </button>`\n                : nothing}\n              ${hasPasswordToggle && !this.disabled && !this.readOnly\n                ? html`<button\n                    type=\"button\"\n                    aria-label=${this.passwordVisible ? 'Hide password' : 'Show password'}\n                    aria-pressed=${this.passwordVisible ? 'true' : 'false'}\n                    class=${actionButtonClasses}\n                    @mousedown=${(e: MouseEvent) => e.preventDefault()}\n                    @click=${this.togglePassword}\n                  >\n                    ${this.passwordVisible ? icon(EyeOff, 'eye-off', 'size-4') : icon(Eye, 'eye', 'size-4')}\n                  </button>`\n                : nothing}\n              ${hasCount\n                ? html`<span class=\"text-muted-foreground pointer-events-none text-xs select-none\"\n                    >${currentValue.length}/${this.maxLength}</span\n                  >`\n                : nothing}\n              ${hasSuffix\n                ? html`<span class=\"text-muted-foreground pointer-events-none select-none\"\n                    >${this.suffix ?? nothing}<slot name=\"suffix\"></slot\n                  ></span>`\n                : nothing}\n            </div>`\n          : nothing}\n      </div>\n\n      ${hasAddonAfter\n        ? html`<div class=${this.addonClasses('after')}>${this.addonAfter ?? nothing}<slot name=\"addon-after\"></slot></div>`\n        : nothing}\n    </div>`\n  }\n}\n\ncustomElements.get('uip-input') || customElements.define('uip-input', UipInput)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-input': UipInput\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/input/input.ts"
    }
  ],
  "dependencies": [
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
