npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nuxt-input-dialog

v1.1.0

Published

A beautiful, framework-agnostic input dialog module for Nuxt 3 and Nuxt 4 — text, password, number, textarea, select, and autocomplete fields with validation. No Vuetify required.

Downloads

217

Readme

nuxt-input-dialog

CI License: MIT

A beautiful, zero-dependency input dialog module for Nuxt 3 and Nuxt 4 — no Vuetify or icon-font required. Drop it in, call useInputDialog().promptText(...), await the typed value.

Rename file dialog with a single text input — floating label inside the input box, OK / Cancel buttons

  • 🎨 Polished look — dark radial gradient, colored borders per type, decorative top icon, blurred backdrop
  • 🧩 Standalone — no Vuetify, no MDI, no extra CSS framework
  • ⚡️ Auto-mounted — no boilerplate, just call useInputDialog().promptText('Rename', 'New name')
  • 🎯 Promise-based APIawait dialog.promptText(...) returns the value or null; show() returns { action, values }
  • 🧠 Fully typed — written in TypeScript with full IntelliSense
  • 📝 7 field typestext, password, email, number, textarea, select, autocomplete
  • Validation rules(value) => true | string, runs on blur and on submit
  • 🔍 Smart autocomplete — type a value not in the list to "Create new", with optional path-formatting (auto trailing slash) or custom formatter
  • 🔘 1 / 2 / 3 button layouts — default Cancel/Submit or custom buttons with named actions
  • ⌨️ Keyboard friendlyEnter submits, Esc cancels, focus moves to first field on open and is restored on close
  • 🔤 Modern typography — bundled Shabnam for Persian/Arabic + Inter for Latin (both opt-out)
  • 🌐 Auto RTL — dialogs containing Arabic/Persian script switch to dir="rtl" automatically (detection runs across title, message, warning, and every field's label/placeholder/hint)
  • Accessiblerole="alertdialog", aria-modal, aria-labelledby

Table of contents

For deeper technical reference (architecture, design rationale, contributing), see docs/.


Installation

npm install nuxt-input-dialog
# or
pnpm add nuxt-input-dialog
# or
yarn add nuxt-input-dialog
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-input-dialog'],
})

That's it — useInputDialog() is auto-imported and an <InputDialogContainer> is mounted automatically on the client.


Quick start

<script setup lang="ts">
const dialog = useInputDialog()

const onRename = async () => {
  const newName = await dialog.promptText('Rename file', 'New file name', {
    defaultValue: 'untitled',
    rules: [v => (typeof v === 'string' && v.length > 0) || 'Name is required'],
  })
  if (newName !== null) await api.rename(newName)
}

const onSaveAs = async () => {
  const result = await dialog.promptSaveAs('document', {
    extensions: ['.txt', '.md', '.json'],
    defaultExtension: '.md',
  })
  if (result) await api.save(result.fullName)  // 'document.md'
}

const onSignup = async () => {
  const values = await dialog.promptForm('Create account', [
    { key: 'username', type: 'text',     label: 'Username', rules: [v => /^[a-z0-9_]{3,20}$/.test(String(v)) || 'Invalid'] },
    { key: 'email',    type: 'email',    label: 'Email',    rules: [v => /\S+@\S+\.\S+/.test(String(v)) || 'Invalid email'] },
    { key: 'password', type: 'password', label: 'Password', rules: [v => String(v).length >= 8 || 'At least 8 chars'] },
    { key: 'role',     type: 'select',   label: 'Role',     items: ['Member', 'Admin'], defaultValue: 'Member' },
  ])
  if (values) await api.signup(values)
}
</script>

The bundled playground exercises every feature — convenience methods, custom show(), validation, autocomplete with create-new, multi-field forms, and Persian RTL:

Quick demo page with the six convenience-method buttons and the custom show() controls


Module options

Configure under the inputDialog key in nuxt.config.ts:

export default defineNuxtConfig({
  modules: ['nuxt-input-dialog'],
  inputDialog: {
    autoMount: true,
    closeOnBackdropClick: false,
    escapeToCancel: true,
    prefix: 'Input',
    loadShabnamFont: true,
    loadInterFont: true,
  },
})

| Option | Type | Default | Description | | ---------------------- | --------- | ----------- | ----------- | | autoMount | boolean | true | Mounts an <InputDialogContainer> automatically on the client. | | closeOnBackdropClick | boolean | false | Backdrop click cancels. Default off so a misclick doesn't lose typed data. | | escapeToCancel | boolean | true | Escape key cancels. | | prefix | string | 'Input' | Component name prefix. With the default, components are <InputDialog> and <InputDialogContainer>. | | theme | 'dark' \| 'light' | 'dark' | Initial visual theme. Switch at runtime with useInputDialog().setTheme(...) or override per-container with the theme prop. | | loadShabnamFont | boolean | true | Inject the bundled Persian "Shabnam" font (gated by unicode-range). | | loadInterFont | boolean | true | Add Inter (Google Fonts) via a <link> in the head. |


Composable API — useInputDialog()

const dialog = useInputDialog()

show(options)

Open a dialog. Returns Promise<{ action, values }>. Always resolves, never rejects.

const { action, values } = await dialog.show({
  title: 'Rename',
  fields: [{ key: 'name', type: 'text', label: 'New name' }],
})
if (action === 'confirm') console.log(values.name)

| Option | Type | Default | | -------------- | ----------------------------------------------- | ------------- | | type | 'success' \| 'warning' \| 'error' \| 'info' | 'info' | | title | string | required | | message | string \| null | null | | fields | InputField[] | required | | initialValues| Record<string, unknown> | {} | | warningText | string \| null | null | | confirmText | string | 'Submit' | | cancelText | string | 'Cancel' | | buttons | InputDialogButton[] \| null | null |

Convenience methods

All return primitive values (or null on cancel) — no boilerplate to extract from values:

const name:    string       | null = await dialog.promptText('Title', 'Label')
const pwd:     string       | null = await dialog.promptPassword('Authenticate')
const qty:     number       | null = await dialog.promptNumber('Quantity', 'How many?')
const color:   string       | null = await dialog.promptSelect('Color', 'Pick', ['red', 'green', 'blue'])
const file = await dialog.promptSaveAs('document', { extensions: ['.md', '.txt'] })
//        ↑ { fileName, extension?, fullName? } | null

const values: Record<string, unknown> | null = await dialog.promptForm('Title', [...fields])

Field types

interface InputField {
  key: string
  type: 'text' | 'password' | 'email' | 'number' | 'textarea' | 'select' | 'autocomplete'
  label: string
  placeholder?: string
  hint?: string | null
  rules?: InputFieldRule[]
  defaultValue?: unknown
  // for textarea
  rows?: number
  // for select / autocomplete
  items?: unknown[]
  // for autocomplete
  createNew?: false | 'path' | ((rawValue: string) => string)
}

| Type | Renders | Notes | | ------------- | ------------------ | ----------------------------------------------- | | text | <input type=text> | Pressing Enter submits the form | | password | <input type=password> | | | email | <input type=email> | Use the @ validation rule of your choice | | number | <input type=number> | | | textarea | <textarea> | rows controls initial height | | select | Custom dropdown | Single-select from items array | | autocomplete| Custom search-box | Filter items, optionally suggest a new value |


Validation rules

A rule is a function that takes the value and returns true (valid) or a string (error message). Multiple rules per field run in order.

{
  key: 'username',
  type: 'text',
  label: 'Username',
  hint: '3–20 chars, lowercase letters, numbers, underscores only',
  rules: [
    v => (typeof v === 'string' && v.length >= 3) || 'At least 3 chars',
    v => (typeof v === 'string' && v.length <= 20) || 'At most 20 chars',
    v => /^[a-z0-9_]+$/.test(String(v)) || 'Only lowercase letters, numbers, and underscores',
  ],
}

Rules run on blur (after the first interaction) and on submit (all fields). Field-level errors render below each field; a form-level error appears under the form when any rules fail on submit.

Username dialog showing the hint "3-20 chars, lowercase letters, numbers, underscores only" below the input


Autocomplete with create-new

Autocomplete fields can suggest creating a new value when the typed text isn't in items. Three modes via the createNew option:

// Mode 1 — disabled (filter-only autocomplete; default)
{ key: 'country', type: 'autocomplete', label: 'Country', items: [...], createNew: false }

// Mode 2 — 'path' formatter (built-in: appends a trailing slash if missing)
{ key: 'folder', type: 'autocomplete', label: 'Folder', items: ['/var/log/'], createNew: 'path' }
//   typing  '/etc'  →  Create "/etc/"

// Mode 3 — custom formatter function
{
  key: 'name',
  type: 'autocomplete',
  label: 'Constant',
  items: ['MAX_RETRIES'],
  createNew: (raw: string) => raw.toUpperCase().replace(/\s+/g, '_'),
}
//   typing  'min retries'  →  Create "MIN_RETRIES"

When create-new mode is on, an extra "Create" item appears at the top of the dropdown with a + icon and a "New" badge. Press Enter in the input to commit the typed (formatted) value, or click the suggestion.

Autocomplete folder picker with "G" typed and a "Create G/" suggestion at the top of the dropdown with a NEW badge, followed by /var/log/ and /var/log/nginx/


Custom buttons

Pass a buttons array to override the default Cancel/Submit pair. Supports 1, 2, or 3 buttons.

await dialog.show({
  title: 'Save changes?',
  message: 'You have unsaved edits.',
  fields: [{ key: 'note', type: 'textarea', label: 'Note (optional)' }],
  buttons: [
    { text: 'Cancel',  action: 'cancel',  variant: 'outlined', color: 'default' },
    { text: 'Discard', action: 'discard', variant: 'flat',     color: 'default' },
    { text: 'Save',    action: 'save',    variant: 'flat',     color: 'info' },
  ],
})
// resolves with action: 'cancel' | 'discard' | 'save'

| Field | Type | Default | | ---------- | ------------------------------------------------------------- | -------------------------------------- | | text | string | required | | action | string | 'confirm' | | variant | 'flat' \| 'outlined' | 'outlined' for cancel, else 'flat' | | color | 'success' \| 'warning' \| 'error' \| 'info' \| 'default' | dialog type for confirm | | disabled | boolean | false |


Component API

<InputDialogContainer>

The container that renders the active dialog. Auto-mounted by default — only use this directly if autoMount: false.

| Prop | Type | Default | Description | | ---------------------- | --------- | ------- | ----------- | | teleport | boolean | true | Render into document.body via <Teleport> so the overlay always covers the viewport. | | closeOnBackdropClick | boolean | false | Backdrop click cancels. | | escapeToCancel | boolean | true | Escape key cancels. |

<InputDialog>

The single-dialog component. You normally don't render this directly — useInputDialog() and <InputDialogContainer> handle it. Exposed for static / non-composable usage with v-model.

Emits: update:modelValue, confirm (with values), cancel, action (with name + values).


Theme

Ships with a dark theme (default) and a light theme. Switch globally at runtime, or override per-container.

// nuxt.config.ts — initial theme
inputDialog: { theme: 'light' }
<script setup lang="ts">
const dialog = useInputDialog()

console.log(dialog.theme.value) // 'dark' or 'light'

dialog.setTheme('light')

// Optional: follow the user's system preference
const sync = () => dialog.setTheme(
  window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light',
)
sync()
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', sync)
</script>

Per-container override:

<InputDialogContainer theme="light" />

The theme prop, when set, takes precedence over useInputDialog().theme. Type-color borders (#30e0a1 / #FFD700 / #DC143C / #00FFFF) stay constant in both themes — only the card, overlay backdrop, input field colors, dropdown panel, and outlined-button neutrals swap.


Customization

The auto-mounted container lives at #nuxt-input-dialog-root. Override styles globally by targeting that root:

/* assets/css/main.css */
#nuxt-input-dialog-root .dialog-card {
  max-width: 600px;
  border-radius: 16px;
}

#nuxt-input-dialog-root .dialog-input {
  font-family: ui-monospace, monospace;
}

Fonts

Two fonts loaded by default:

  • Inter (English / Latin) — Google Fonts via <link>. Disable with loadInterFont: false.
  • Shabnam (Persian / Arabic) — bundled woff2 (5 weights), gated by unicode-range. Disable with loadShabnamFont: false.

Right-to-left support

When the title, message, warning text, or any field's label/placeholder/hint contains Arabic / Persian script, the dialog auto-switches to dir="rtl". Detection is per-instance — you can mix LTR and RTL dialogs in the same app without configuration.

Persian "تغییر نام فایل" rename dialog — RTL layout with ذخیره (Save) and انصراف (Cancel) buttons reversed and Shabnam font rendering

Manual mounting

// nuxt.config.ts
inputDialog: { autoMount: false }
<!-- app.vue -->
<template>
  <NuxtLayout><NuxtPage /></NuxtLayout>
  <InputDialogContainer />
</template>

The state is global — only one dialog is active at a time, by design. If you mount a container inside an element with backdrop-filter, transform, filter, perspective, will-change, or contain, <Teleport to="body"> ensures the overlay still covers the viewport.


TypeScript

import type {
  InputDialogType,         // 'success' | 'warning' | 'error' | 'info'
  InputFieldType,          // 'text' | 'password' | 'email' | 'number' | 'textarea' | 'select' | 'autocomplete'
  InputField,              // single field definition
  InputFieldRule,          // (value) => true | string
  InputDialogButton,       // { text, action?, variant?, color?, disabled?, loading? }
  InputDialogOptions,      // arg to show()
  InputDialogResult,       // { action, values }
  InputDialogInstance,     // shape of currentDialog.value
} from 'nuxt-input-dialog'

ModuleOptions is also exported.


Development

npm install
npm run dev:prepare
npm run dev            # playground at http://localhost:3000

npm run lint
npm run test
npm run prepack        # build dist/

The playground at playground/ exercises every field type, every validation pattern, real-world forms (sign-up, server config, save-as), the autocomplete with create-new, and Persian / RTL.

CI

GitHub Actions runs lint (Node 22) and tests + build (Node 20 and 22) on every push and PR against main.


License

MIT