protonemedia / form-components-pro

A set of Vue 3 components to rapidly build forms with Tailwind CSS 3. It supports validation, model binding, integrates with Autosize/Choices.js/Flatpickr, includes default vendor styling and is fully customizable! Even better in conjunction with Laravel Jetstream + Inertia.js.

Home Page:https://protone.media/en/open-source

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Warning: Like the Tables package, I've decided to archive this repository as I've moved away from Inertia.js to focus on Splade and Eddy 🔥

Form Components Pro

A set of Vue 3 components to rapidly build forms with Tailwind CSS 3. It supports validation, model binding, integrates with Autosize/Choices.js/Flatpickr, includes default vendor styling and is fully customizable! Even better in conjunction with Laravel Jetstream + Inertia.js.

Features

  • Components for input, textarea, file, multi-file, select, multi-select, checkbox and radio elements.
  • Support for Tailwind 3 with Tailwind Forms.
  • Support for Vue 3.
  • Support for the Inertia.js Form Helper.
  • Component scripts independent from templates. Templates use the same logic.
  • Bind a target to a form (or a set of elements) to provide default values (model binding).
  • Validation errors.
  • Support for autosize
  • Support for Choices.js
  • Support for Flatpickr

Support

We proudly support the community by developing libraries and packages and giving them away for free. Keeping track of issues and pull requests takes time, but we're happy to help! If this package saves you time or if you're relying on it professionally, please consider supporting the maintenance and development.

Documentation

Quick example

In the example below you'll find two-way binding on the Form Component, validation error evaluation, integration with Autosize, Choices.js and Flatpickr, and the Group component.

<template>
  <div id="app">
    <Form class="p-8 max-w-md" v-model="user" :errors="errors">
      <Input name="name" label="Your name" />

      <Textarea name="biography.en" label="Write your story (English)" autosize />
      <Textarea name="biography.nl" label="Write your story (Dutch)" autosize />
      <Input name="date_of_birth" label="Date of birth" date />
      <Select name="favorite_framework" :options="frameworks" label="Pick your favorite framework" choices />
      <Select name="interests" :options="interests" label="Choose your interests" multiple choices />

      <Group label="Preferred IDE theme" name="theme" inline>
        <Radio name="theme" value="dark" label="Dark theme" />
        <Radio name="theme" value="light" label="Light theme" />
      </Group>

      <Group>
        <Checkbox name="newsletter" label="Do you want to receive my newsletter?" />
      </Group>

      <Submit />
    </Form>
  </div>
</template>

<script setup>
import { reactive } from "vue";

const user = reactive({
    name: "Form Components Pro",
    biography: {
        en: "",
        nl: "",
    },
    date_of_birth: "",
    favorite_framework: "tailwind",
    interests: ["tailwind", "inertiajs", "laravel"],
    theme: "dark",
    newsletter: true,
});

const frameworks = {
    tailwind: "Tailwind CSS",
    bootstrap: "Bootstrap",
};

const interests = [
    { value: "tailwind", label: "Tailwind CSS" },
    { value: "bootstrap", label: "Bootstrap" },
    { value: "inertiajs", label: "Inertia.js" },
    { value: "livewire", label: "Livewire" },
    { value: "laravel", label: "Laravel" },
];

const errors = reactive({
    biography: {
        en: "Tell me!",
    },
    date_of_birth: "Hey, this is required!",
});
</script>

Installation

Make sure you've installed the @tailwindcss/forms plugin correctly. You can install the package with npm or yarn:

npm install @protonemedia/form-components-pro-vue3-tailwind3-simple

Tailwind configuration

Add the repository path to the content array of your Tailwind configuration file. This ensures that the styling also works on production builds.

module.exports = {
  content: [
    './node_modules/@protonemedia/**/*.{js,vue}',
  ]
}

Register components

To start using the components, you need to register the components. You can do this globally for your Vue.js app or per component.

Register globally

By registering the Form Components globally, you can use them in the template of any root Vue instance (new Vue).

import {
  Checkbox,
  Form,
  File,
  Group,
  Input,
  Radio,
  Select,
  Submit,
  Textarea,
} from "@protonemedia/form-components-pro-vue3-tailwind3-simple";

import { createApp } from 'vue'

const app = createApp({})

app
  .component('Checkbox', Checkbox)
  .component('Form', Form)
  .component('File', File)
  .component('Group', Group)
  .component('Input', Input)
  .component('Radio', Radio)
  .component('Select', Select)
  .component('Submit', Submit)
  .component('Textarea', Textarea);

Register in Inertia.js apps

If you're using the createInertiaApp method to boot your Inertia app, you may register the components in the setup section.

createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    resolve: (name) => require(`./Pages/${name}.vue`),
    setup({ el, app, props, plugin }) {
        return createApp({ render: () => h(app, props) })
            .use(plugin)
            .mixin({ methods: { route } })
            .component('Checkbox', Checkbox)
            .component('Form', Form)
            .component('File', File)
            .component('Group', Group)
            .component('Input', Input)
            .component('Radio', Radio)
            .component('Select', Select)
            .component('Submit', Submit)
            .component('Textarea', Textarea)
            .mount(el);
    },
});

Register per component

Instead of registering the components globally, you can also choose to define them in the components object of a Vue component.

<template>
  <div id="user_settings">
    <Form class="p-8">

    </Form>
  </div>
</template>

<script>
import {
  Checkbox,
  Form,
  File,
  Group,
  Input,
  Radio,
  Select,
  Submit,
  Textarea,
} from "@protonemedia/form-components-pro-vue3-tailwind3-simple";

export default {
  components: {
    Checkbox,
    Form,
    File,
    Group,
    Input,
    Radio,
    Select,
    Submit,
    Textarea,
  },
};
</script>

Reactivity

One of the great features of Vue is two-way data bindings on form elements. This is fully supported on all Form Components and all integrations with third-party libraries.

Use v-model on individual form elements

You can set the v-model on each individual form element. The label attribute is optional.

<template>
  <div id="app">
    <Form class="p-8">
      <Input v-model="user.name" label="Your name" />
      <Textarea v-model="user.biography" label="Write your story" autosize />
      <Input v-model="user.date_of_birth" label="Date of birth" date />
      <Select v-model="user.favorite_framework" :options="frameworks" label="Pick your favorite framework"/>
      <Select v-model="user.interests" :options="interests" label="Choose your interests" multiple />

      <Group label="Preferred IDE theme" inline>
        <Radio v-model="user.theme" value="dark" label="Dark theme" />
        <Radio v-model="user.theme" value="light" label="Light theme" />
      </Group>

      <Group>
        <Checkbox v-model="user.newsletter" label="Do you want to receive my newsletter?" />
      </Group>

      <Submit />
    </Form>
  </div>
</template>

<script setup>
import { reactive } from "vue";

const user = reactive({
    name: "",
    biography: "",
    date_of_birth: "",
    favorite_framework: "",
    interests: [],
    theme: "light",
    newsletter: false,
});

const frameworks = {
    tailwind: "Tailwind CSS",
    bootstrap: "Bootstrap",
};

const interests = [
    { value: "tailwind", label: "Tailwind CSS" },
    { value: "bootstrap", label: "Bootstrap" },
    { value: "inertiajs", label: "Inertia.js" },
    { value: "livewire", label: "Livewire" },
    { value: "laravel", label: "Laravel" },
];
</script>

Use v-model on the Form component

You can also pass an object to the form by using v-model on the Form component. The binding of the Form Components is now based on the name attribute. Nested properties using the dot-notation are supported as well (biography example). The package can evaluate validation errors by the name attribute as well. This is great as the name attribute is used for both, so you don't have to define the v-model and error attributes manually.

<template>
  <div id="app">
    <Form class="p-8" v-model="user">
      <Input name="name" label="Your name" />

      <Textarea name="biography.en" label="Write your story (English)" autosize />
      <Textarea name="biography.nl" label="Write your story (Dutch)" autosize />

      <Input name="date_of_birth" label="Date of birth" date />
      <Select name="favorite_framework" :options="frameworks" label="Pick your favorite framework" />
      <Select name="interests" :options="interests" label="Choose your interests" multiple />

      <Group label="Preferred IDE theme" inline>
        <Radio name="theme" value="dark" label="Dark theme" />
        <Radio name="theme" value="light" label="Light theme" />
      </Group>

      <Group>
        <Checkbox name="newsletter" label="Do you want to receive my newsletter?" />
      </Group>

      <Submit />
    </Form>
  </div>
</template>

<script setup>
import { reactive } from "vue";

const user = reactive({
    name: "",
    biography: {
        en: "",
        nl: "",
    },
    date_of_birth: "",
    favorite_framework: "",
    interests: [],
    theme: "light",
    newsletter: false,
});

const frameworks = [
    { value: "tailwind", label: "Tailwind CSS" },
    { value: "bootstrap", label: "Bootstrap" },
],

const interests = [
    { value: "tailwind", label: "Tailwind CSS" },
    { value: "bootstrap", label: "Bootstrap" },
    { value: "inertiajs", label: "Inertia.js" },
    { value: "livewire", label: "Livewire" },
    { value: "laravel", label: "Laravel" },
],
</script>

Validation errors

Specify an error per element

With the error attribute, you can pass a validation error to any form element. This also works for the Group component.

<Form>
  <Input v-model="user.name" :error="errors.name" />

  <Group label="Preferred IDE theme" inline :error="errors.theme">
    <Radio v-model="user.theme" value="dark" label="Dark theme" />
    <Radio v-model="user.theme" value="light" label="Light theme" />
  </Group>

  <Submit />
</Form>

Errors per form

Just like passing an object to v-model on the Form component, you can give it an error object as well. Now each error will be evaluated by the name attribute.

<Form v-model="company" :errors="errors">
  <Input name="business_name" />
  <Input name="vat_number" />

  <Submit />
</Form>

Hiding errors

Suppose you want to hide a validation error. In that case, you can use the show-error attribute, which defaults to true (except on the Radio component).

<Form v-model="company" :errors="errors">
  <Input name="business_name" />
  <Input name="vat_number" :show-error="false" />

  <Submit />
</Form>

Select element

You can give the Select component a set of options that is either an Array or an Object. If you go with an Object, it should be a simple key-value collection. An Array should consists of one or more objects that have a value and label key.

Object example:

<script setup>
const selectOptions = {
    tailwind: "Tailwind CSS",
    bootstrap: "Bootstrap",
};
</script>

Array example:

<script setup>
const selectOptions = [
    { value: "tailwind", label: "Tailwind CSS" },
    { value: "bootstrap", label: "Bootstrap" },
];
</script>

By using an Array, you could supply additional attributes, like disabled:

<script>
const selectOptions = [
    { value: "tailwind", label: "Tailwind CSS" },
    { value: "bootstrap", label: "Bootstrap", disabled: true },
];
</script>

There's support for grouping (optgroup) as well. Each group object should have a label key and an options key, which should be an Array:

<script setup>
const frameworks = [
    {
        label: "CSS Frameworks",
        options: [
            { value: "tailwind", label: "Tailwind CSS" },
            { value: "bootstrap", label: "Bootstrap" },
        ],
    },
    {
        label: "PHP Frameworks",
        options: [
            { value: "laravel", label: "Laravel" },
            { value: "symfony", label: "Symfony" },
        ],
    },
];
</script>

Instead of giving a set of options, you can also pass a custom slot into the Select component:

<Select name="favorite_framework">
  <option value="tailwind">Tailwind CSS</option>
  <option value="bootstrap">Bootstrap</option>
</Select>

File element

There's a dedicated Vue component that you can use to select files. This component is based mainly on the Input component, but provides additional logic to display the filename of the selected file.

<File name="avatar" />

The component supports selecting multiple files as well by adding the multiple attribute.

<File name="documents" multiple />

Inertia.js Form Helper

You can pass an instance of the Inertia.js Form Helper to the form using v-model on the Form component. This will bind the values, as well as the validation errors. You don't have to explicitly set the :errors attribute, as described in the validation documentation.

<template>
  <Form @submit.prevent="form.post('/users')" v-model="form">
    <Input name="name" label="Your name" />
    <Submit />
  </Form>
</template>

<script setup>
import { useForm } from "@inertiajs/inertia-vue3";

const form = useForm({
  name: "",
});
</script>

Integrations with third-party Libaries

Autosize

Add the autosize attribute to the Textarea component:

<Textarea name="biography" autosize />

Flatpickr

Flatpickr uses a Stylus stylesheet to style the library. Our stylesheet extends the vendor stylesheet (of Flatpickr) and adds some Tailwind-specific tweaks. Make sure your bundler handles Stylus stylesheets correctly, for example, by installing stylus and stylus-loader.

You can import the stylesheet in your app or Vue component:

import "@protonemedia/form-components-pro-vue3-tailwind3-simple/src/flatpickr.styl"

To enable the Date Picker, add the date attribute to the Input component:

<Input name="published_at" date />

To enable to Time Picker, add the time attribute to the Input component:

<Input name="scheduled_at" time />

If you want a Date + Time Picker, add both attributes:

<Input name="scheduled_at" date time />

You can instantiate Flatpickr with a custom set of options by passing an object to either the date or time attribute:

<Input name="published_at" :date="{ dateFormat: 'YYYY' }" />

Choices.js

Choices.js uses a SCSS stylesheet to style the library. Our stylesheet extends the vendor stylesheet (of Choices.js) and adds some Tailwind-specific tweaks. Make sure your bundler handles SCSS stylesheets correctly, for example, by installing sass and sass-loader. If you're using Laravel Mix, you may include the stylesheet after the Tailwind directives:

// app.scss

@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

@import "@protonemedia/form-components-pro-vue3-tailwind3-simple/src/choices.scss";
// webpack.mix.js

mix.js('resources/js/app.js', 'public/js')
    .vue()
    .sass('resources/css/app.scss', 'public/css')
    .options({
        postCss: [
            require('postcss-import'),
            require('tailwindcss'),
        ]
    })
    .webpackConfig(require('./webpack.config'));

Add the choices attribute to the Select component:

<Select name="favorite_framework" :options="frameworks" choices />

This works for selecting multiple values as well:

<Select name="favorite_framework" :options="frameworks" mulitple choices />

You can instantiate Choices.js with a custom set of options by passing an object to the choices attribute:

<Select name="favorite_framework" :options="frameworks" :choices="{ searchEnabled: false }" />

Misc

Submit button

The submit button has a default label of Submit, but you set a different label, or pass in a slot:

<Submit label="Save profile" />

<Submit>
  <span class="bg-red-500 text-white">Do something!</span>
</Submit>

Group component

With the Group component, you can group checkboxes and radio elements. You can use the inline attribute to arrange the children horizontally. Just like other the components, the Group component also supports the error and label attributes.

Most of the time, you don't want to show validation errors on each radio element, so the validation errors are hidden by default on the Radio component. Instead, you can use the Group component to show the error just once.

<Form v-model="settings" :errors="errors">
  <Group name="theme" inline>
    <Radio name="theme" value="dark" label="Dark theme" />
    <Radio name="theme" value="light" label="Light theme" />
  </Group>

  <Group>
    <Checkbox name="newsletter" label="Do you want to receive my newsletter?" />
    <Checkbox name="terms" label="Do you agree with the terms?" />
  </Group>
</Form>

If you want to show the error on the Radio component, you can use the show-error attribute:

<Form v-model="settings" :errors="errors">
  <Group>
    <Radio name="theme" value="dark" label="Dark theme" :show-error="true" />
    <Radio name="theme" value="light" label="Light theme" :show-error="true" />
  </Group>
</Form>

Attribute inheritance

Additional attributes are passed down the form element itself. For example, the placeholder attribute is passed down to the inner input element, not to the Input component's outer div.

<Input v-model="user.name" placeholder="Your name" data-foo="bar" />

Rendered template:

<div>
  <input v-model="..." placeholder="Your name" data-foo="bar" />
</div>

Roadmap to first release

  • Add unstyled, underline and solid styling examples from @tailwindcss/forms examples
  • Better imports for the external libraries
  • Support for optgroup in the Select component
  • Support for date ranges (Flatpickr)
  • Support for custom date formats (Flatpickr)
  • Support for time input (Flatpickr)
  • Support for file input (with Filepond integration?)
  • Support for markdown and WYSIWYG editors
  • Support custom Choices.js configuration
  • Support custom Flatpickr configuration
  • Documentation about customization
  • Improve unit tests (too much copypasta)
  • Setup GitHub Actions

Changelog

Please see CHANGELOG for more information about what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Laravel packages

  • Laravel Analytics Event Tracking: Laravel package to easily send events to Google Analytics.
  • Laravel Blade On Demand: Laravel package to compile Blade templates in memory.
  • Laravel Cross Eloquent Search: Laravel package to search through multiple Eloquent models.
  • Laravel Eloquent Scope as Select: Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes and constraints by adding them as a subquery.
  • Laravel Eloquent Where Not: This Laravel package allows you to flip/invert an Eloquent scope, or really any query constraint.
  • Laravel FFMpeg: This package provides an integration with FFmpeg for Laravel. The storage of the files is handled by Laravel's Filesystem.
  • Laravel Form Components: Blade components to rapidly build forms with Tailwind CSS Custom Forms and Bootstrap 4/5. Supports validation, model binding, default values, translations, includes default vendor styling and fully customizable!
  • Laravel Mixins: A collection of Laravel goodies.
  • Laravel Paddle: Paddle.com API integration for Laravel with support for webhooks/events.
  • Laravel Verify New Email: This package adds support for verifying new email addresses: when a user updates its email address, it won't replace the old one until the new one is verified.
  • Laravel WebDAV: WebDAV driver for Laravel's Filesystem.
  • Laravel XSS Protection Middleware: Protect your app against Cross-site scripting (XSS). It sanitizes request input, and it can sanatize Blade echo statements as well.

Security

If you discover any security related issues, please email pascal@protone.media instead of using the issue tracker.

Credits

Security

If you discover any security-related issues, please email pascal@protone.media instead of using the issue tracker.

License

The MIT License (MIT). Please see License File for more information.

Treeware

This package is Treeware. If you use it in production, we ask that you buy the world a tree to thank us for our work. By contributing to the Treeware forest, you'll create employment for local families and restoring wildlife habitats.

About

A set of Vue 3 components to rapidly build forms with Tailwind CSS 3. It supports validation, model binding, integrates with Autosize/Choices.js/Flatpickr, includes default vendor styling and is fully customizable! Even better in conjunction with Laravel Jetstream + Inertia.js.

https://protone.media/en/open-source

License:MIT License


Languages

Language:JavaScript 49.2%Language:Vue 45.9%Language:SCSS 4.0%Language:Stylus 0.9%