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 🙏

© 2024 – Pkg Stats / Ryan Hefner

ivvy

v2.2.0

Published

Form manager with dynamic type safe validators and i18n/l10n support.

Downloads

30

Readme

Ivvy

version tests codefactor npm bundle size downloads github stars license

Form manager with dynamic type safe validators and i18n/l10n support.

It works out-of-the-box with Svelte 4 due to its simple API.

Yrel is used for data validation and Ukti for error reports l10n and i18n.

Install

For any ESM and CommonJS JavaScript environment. If TypeScript is used, version 4.5+ is required.

npm i ivvy

Yrel and Ukti as peer dependencies.

npm i yrel ukti

Example Usage

Ivvy provides a TypeScript API to configure an universal form manager with easy to define validators and error translations.

<script lang="ts">
  import { onDestroy } from 'svelte'
  import { type IvvyManagerPropsTranslations, createIvvyManager } from 'ivvy'
  import { type InferYrel, y } from 'yrel'

  const schema = y.object({
    name: y.string().min(2).max(20),
    age: y.number().gte(18).lte(150).nullable()
  })

  type FormData = InferYrel<typeof schema>

  const translations: IvvyManagerPropsTranslations = {
    en: {
      err_number: 'A valid number is required.',
      err_number_gte: 'This number should be at least {{gte}}.',
      err_number_lte: 'This number should be at most {{lte}}.',
      err_string: 'A valid text is required.',
      err_string_min: 'This field should have at least {{min}} character{{min === 1 ? "" : "s"}}.',
      err_string_max: 'This field should have at most {{max}} character{{max === 1 ? "" : "s"}}.'
    }
  }

  const manager = createIvvyManager<FormData>({
    initialData: {
      name: 'Ivvy',
      age: 21
    },
    formatters: {
      age: (value) => (Number.isFinite(value) ? Number(value) : null)
    },
    validators: schema,
    translations,
    preventSubmit: 'always',
    onSubmit: (data) => console.log(data)
  })

  const { touches, errors, useFormElement, useFieldElement } = manager

  onDestroy(() => manager.destroy())
</script>

<form use:useFormElement>
  <label>
    Name:
    <input name="name" type="text" use:useFieldElement />
    <div>{($touches.name && $errors.name?.[0]) || ''}</div>
  </label>

  <label>
    Age:
    <input name="age" type="number" use:useFieldElement />
    <div>{($touches.age && $errors.age?.[0]) || ''}</div>
  </label>

  <button type="submit">Submit</button>
</form>

The Ivvy form manager or just the Yrel schema can be reused in multiple environments or multiple forms. The Ukti translations are optional.

Form Data

The manager form data can be an object and each property representing a form field with any kind of data.

The initial data object has to have all the properties. They can be undefined / null.

import { createIvvyManager } from 'ivvy'

type FormData = {
  name: string
  age: number
}

const manager = createIvvyManager<FormData>({
  initialData: {
    name: null,
    age: null
  }
})

Validators

The form data validators have to be explicitely defined for each object property.

The validators can be defined using Yrel schema validations.

Every time there is a validation there is an manager data update with the possibly new validation data transformation. e.g. Yrel data transformations.

import { createIvvyManager } from 'ivvy'
import { type InferYrel, y } from 'yrel'

const schema = y.object({
  name: y.string().min(2).max(20),
  age: y.number().gte(18).lte(150).nullable()
})

type FormData = InferYrel<typeof schema>

const manager = createIvvyManager<FormData>({
  initialData: {
    name: 'Ivvy',
    age: 21
  },
  validators: schema
})

Validators can be individual Yrel schema validators.

import { createIvvyManager } from 'ivvy'
import { y } from 'yrel'

type FormData = {
  name: string
  age: number
}

const manager = createIvvyManager<FormData>({
  initialData: {
    name: 'Ivvy',
    age: 21
  },
  validators: {
    name: y.string().min(2).max(20),
    age: y.number().gte(18).lte(150).nullable()
  }
})

Validators can be dynamic Yrel schema validators based on the current form data.

import { createIvvyManager } from 'ivvy'
import { y } from 'yrel'

type FormData = {
  name: string
  age: number
}

const manager = createIvvyManager<FormData>({
  initialData: {
    name: 'Ivvy',
    age: 21
  },
  validators: {
    name: (data) => y.string().min(2).max(20),
    age: (data) => y.number().gte(18).lte(150).nullable()
  }
})

Validators can be any function validating the input data and returning true as valid or an array of string error reports.

import { createIvvyManager } from 'ivvy'

type FormData = {
  name: string
  age: number
}

const manager = createIvvyManager<FormData>({
  initialData: {
    name: 'Ivvy',
    age: 21
  },
  validators: {
    name: (data) => data.name.length > 2 || ['min length 2'],
    age: (data) => data.age > 18 || ['min value 18']
  }
})

Formatters

A form field value can be formatted from user input before validations. These functions will not apply for the initial data provided in settings nor dynamic data updates.

import { createIvvyManager } from 'ivvy'

type FormData = {
  name: string
  age: number
}

const manager = createIvvyManager<FormData>({
  initialData: {
    name: 'Ivvy',
    age: 21
  },
  formatters: {
    // Make all text uppercase.
    name: (value) => String(value).toUpperCase(),
    // Make it `null` if not a valid number.
    age: (value) => (Number.isFinite(value) ? Number(value) : null)
  }
})

Translations

Ukti can be used for error translations and in combination with Yrel schema validators reports.

import { type IvvyManagerPropsTranslations, createIvvyManager } from 'ivvy'
import { type InferYrel, y } from 'yrel'

const schema = y.object({
  name: y.string().min(2).max(20),
  age: y.number().gte(18).lte(150).nullable()
})

type FormData = InferYrel<typeof schema>

const translations: IvvyManagerPropsTranslations = {
  en: {
    err_number: 'A valid number is required.',
    err_number_gte: 'This number should be at least {{gte}}.',
    err_number_lte: 'This number should be at most {{lte}}.',
    err_string: 'A valid text is required.',
    err_string_min: 'This field should have at least {{min}} character{{min === 1 ? "" : "s"}}.',
    err_string_max: 'This field should have at most {{max}} character{{max === 1 ? "" : "s"}}.'
  }
  es: {
    err_number: 'Un número válido es requerido.',
    err_number_gte: 'El número debe ser al menos {{gte}}.',
    err_number_lte: 'El número debe ser por mucho {{lte}}.',
    err_string: 'Un texto válido es requerido.',
    err_string_min: 'El texto debe tener al menos {{min}} carácteres{{min === 1 ? "" : "s"}}.',
    err_string_max: 'El texto debe tener por mucho {{max}} carácteres{{max === 1 ? "" : "s"}}.'
  }
}

const manager = createIvvyManager<FormData>({
  initialData: {
    name: 'Ivvy',
    age: 21
  },
  validators: schema,
  language: 'en', // Defaults to 'en'.
  translations
})

API

createIvvyManager<Data>(props: IvvyManagerProps<Data>): IvvyManager<Data>

Create a form manager with the provided settings.

IvvyManagerProps<Data>.initialData: { [P in keyof Required<Data>]: Data[P] | undefined | null }

The initial form data. All fields are required. They can be undefined / null.

IvvyManagerProps<Data>.validators: YrelSchema<Data> | { [P in keyof Data]: YrelSchema<Data[P]> | ((data: Data) => true | string[] | YrelSchema<Data[P]>) }

Each data field validator are required. They can be Yrel schema validators or basic functions returning true or an array of string error messages string[].

IvvyManagerProps<Data>.formatters?: { [P in keyof Data]?: (value: unknown, data: Data) => Data[P] }

Optional data fields formatters.

IvvyManagerProps<Data>.preventSubmit?: 'always' | 'onError' | false = 'onError'

Prevent default form submit functionalities.

IvvyManagerProps<Data>.cleanInputFileValue?: boolean = true

Remove the value of input type file after change event. This will allow the user to re-select the same files if necessary.

IvvyManagerProps<Data>.translations?: IvvyManagerPropsTranslations

The translations for the error messages.

IvvyManagerProps<Data>.language?: UktiLanguages = 'en'

Any ISO 639-1 locale/language code to use from the translations provided.

IvvyManagerProps<Data>.onUpdate?: (data: Data) => void

On form field update callback.

IvvyManagerProps<Data>.onSubmit?: (data: Data, event: Event) => void

On form submit event callback when the form data is valid.