Skip to content

vue-saform

Koleksi form input components Vue 3 — text/number/currency, select (sync & async), date/time picker, textarea, dan rich text editor — dengan styling self-contained.

Versi: 0.1.0-beta.0 Kategori: UI Component (Vue 3)

npm versionTypeScriptVueTailwind CSS


TL;DR

vue-saform menyediakan 5 komponen form (FormInput, FormTextarea, FormSelect, FormDatetime, FormQuill) yang berbagi wrapper chrome dan validation pipeline yang sama. Styling self-contained — tidak butuh class dari parent project, tidak ada dependency ke FontAwesome atau icon library manapun.

Components:

ts
import { FormInput, FormTextarea, FormSelect, FormDatetime } from '@bpmlib/vue-saform';
import { FormQuill } from '@bpmlib/vue-saform/quill'; // subpath terpisah, lihat Installation

Functions (validators):

ts
import { required, minLength, maxLength, email, pattern, numeric, min, max, sameAs, minSelected, maxSelected, or, and } from '@bpmlib/vue-saform';

Lihat daftar lengkap tiap component di Component Catalog.


Installation & Setup

Requirements

Peer Dependencies

DependencyVersiStatusDeskripsi
vue^3.3.0RequiredVue 3 framework
quill^2.0.3OptionalHanya dibutuhkan untuk FormQuill
vue-quilly^1.1.5OptionalVue wrapper untuk Quill, dibutuhkan FormQuill
@enzedonline/quill-blot-formatter2^3.0.3OptionalImage/blot resize toolbar untuk FormQuill
quill2-image-uploader^1.0.4OptionalPaste/drop image upload module untuk FormQuill

vue-virtual-scroller dan @bpmlib/vue-sabutton sudah termasuk sebagai dependency biasa — tidak perlu diinstall terpisah.

Package Installation

bash
npm install @bpmlib/vue-saform
bash
yarn add @bpmlib/vue-saform
bash
pnpm add @bpmlib/vue-saform
bash
bun install @bpmlib/vue-saform

Import

Basic Import:

ts
import { FormInput, FormTextarea, FormSelect, FormDatetime } from '@bpmlib/vue-saform';

CSS Import:

ts
// main.ts
import '@bpmlib/vue-saform/style.css';

Import CSS ini sekali saja di entry point aplikasi — semua komponen (kecuali FormQuill) memakainya, tidak ada langkah app.use(...).

FormQuill (opsional, subpath terpisah):

ts
import { FormQuill } from '@bpmlib/vue-saform/quill';
import '@bpmlib/vue-saform/quill.css';

FormQuill sengaja tidak ada di barrel utama — importing FormInput/FormSelect/dst dari . tidak pernah resolve atau bundle Quill sama sekali. Install quill, vue-quilly, @enzedonline/quill-blot-formatter2, dan quill2-image-uploader hanya jika memakai FormQuill; tidak perlu registrasi apapun secara manual.

Quick Start

vue
<script setup lang="ts">
import { ref } from 'vue';
import { FormInput, required } from '@bpmlib/vue-saform';

const email = ref('');
</script>

<template>
  <FormInput id="email" v-model="email" label="Email" type="email" required :validation="required()" />
</template>

Untuk contoh penggunaan lengkap tiap component, buka halaman masing-masing di Component Catalog.


Core Concepts

Notch Family & Floating Label

FormInput, FormTextarea, FormSelect, dan FormDatetime berbagi wrapper chrome yang sama ("notch family") — border dengan notch accent, floating label, char counter, dan baris #description/#errors di bawah field. Floating label state (posisi resting vs floated) JS-driven, bukan CSS pseudo-class (:placeholder-shown) — dibutuhkan karena FormSelect/FormDatetime adalah button/div-driven, bukan native <input>.

FormQuill adalah outlier struktural — reuse class label yang sama tapi permanen di posisi floated (rich-text field tidak punya ambiguitas "kosong vs terisi" yang layak dianimasikan).

Unified Validation Pipeline

Kelima component memakai satu pipeline validasi yang sama secara internal:

  • Prop validation (function atau array of function) adalah extension point utama — lihat Validators untuk built-in rule factories.
  • Prop HTML-attribute (required/minlength/pattern/minVal/maxVal — tergantung component) feed ke pipeline yang sama, bukan pipeline paralel terpisah.
  • Setiap rule di-await secara seragam (sync maupun async).
  • Validity muncul satu microtask setelah value berubah — gunakan method validate() yang di-expose tiap component untuk pembacaan sinkron setelah mutasi manual.
  • Prop messages — bag override Laravel-style, di-shallow-merge di atas default (Bahasa Indonesia), support :token interpolation (mis. 'Minimal :min karakter!').

Detach Mode (FormSelect & FormDatetime)

FormSelect dan FormDatetime berbagi prop detach: boolean | 'small' untuk mengontrol presentasi panel dropdown/calendar:

  • false (default) — panel inline, absolute-positioned, anchored ke field via floating-ui, Teleported ke document.body supaya tidak ke-clip ancestor manapun.
  • true — panel selalu Teleport+backdrop+centered modal card.
  • 'small' — inline di viewport md breakpoint ke atas, detached di bawahnya (matchMedia-driven).

Khusus FormSelect: kombinasi multiselect + detached mengaktifkan staged/draft-commit workflow (tombol Cancel/Reset/Submit) alih-alih live-commit setiap klik.

Self-Contained Styling

Semua styling di-compile ke dist/style.css milik package ini sendiri — tidak bergantung pada class Tailwind dari parent project. Satu-satunya integrasi yang disediakan untuk consumer adalah override CSS custom property (--color-* di tokens.css, plus beberapa variable spesifik per component). Lihat halaman masing-masing component untuk detail CSS Variables.


Validators

Rule factory built-in untuk prop validation — semuanya menghasilkan ValidationRule<unknown>, dipakai di semua 5 component. Bisa dipassing tunggal (validation="required()") atau sebagai array (dikombinasikan otomatis oleh pipeline internal).

Contains:

required

Gagal hanya untuk undefined/null/''/array kosong — angka 0 dianggap terisi (penting untuk field currency/numeric di mana 0 adalah nilai valid tapi falsy di JS).

Signature:

ts
function required(msgKey?: string): ValidationRule<unknown>

Parameters

NameTypeDefaultDescription
msgKeystring'required'Key ke messages bag untuk pesan error

Contoh:

ts
<FormInput id="name" v-model="name" :validation="required()" />

minLength() / maxLength()

Skip validasi saat value kosong — pasangkan dengan required() untuk "wajib diisi DAN cukup panjang".

Signature:

ts
function minLength(n: number, msgKey?: string): ValidationRule<unknown>
function maxLength(n: number, msgKey?: string): ValidationRule<unknown>

Parameters

NameTypeDefaultDescription
nnumber-Batas panjang karakter
msgKeystring'minLength' / 'maxLength'Key ke messages bag

Contoh:

ts
:validation="[required(), minLength(8)]"

email

Regex-based email check. Skip saat value kosong (pasangkan dengan required() jika wajib diisi).

Signature:

ts
function email(msgKey?: string): ValidationRule<unknown>

pattern

Signature:

ts
function pattern(regex: string | RegExp, msgKey?: string): ValidationRule<unknown>

Parameters

NameTypeDefaultDescription
regexstring | RegExp-Pattern yang harus di-match
msgKeystring'pattern'Key ke messages bag

numeric

Memastikan value adalah string angka (/^-?\d+(\.\d+)?$/). Skip saat value kosong.

Signature:

ts
function numeric(msgKey?: string): ValidationRule<unknown>

min() / max()

Batas nilai numerik. Silently passes (bukan tanggung jawab rule ini) jika value tidak bisa di-parse sebagai angka — pasangkan dengan numeric() jika kedua check dibutuhkan.

Signature:

ts
function min(n: number, msgKey?: string): ValidationRule<unknown>
function max(n: number, msgKey?: string): ValidationRule<unknown>

Parameters

NameTypeDefaultDescription
nnumber-Batas nilai
msgKeystring'min' / 'max'Key ke messages bag

sameAs

Cross-field match, mis. konfirmasi password.

Signature:

ts
function sameAs(otherValue: { value: unknown } | unknown, msgKey?: string): ValidationRule<unknown>

Parameters

NameTypeDefaultDescription
otherValue{ value: unknown } | unknown-Nilai pembanding — boleh plain value atau ref (dibaca .value-nya saat rule dijalankan, jadi reaktif)
msgKeystring'sameAs'Key ke messages bag

Contoh:

ts
const password = ref('');
// ...
<FormInput id="confirm" type="password" :validation="sameAs(password)" />

minSelected() / maxSelected()

Untuk FormSelect multiselect — validasi jumlah item terpilih pada modelValue array.

Signature:

ts
function minSelected(n: number, msgKey?: string): ValidationRule<unknown>
function maxSelected(n: number, msgKey?: string): ValidationRule<unknown>

Parameters

NameTypeDefaultDescription
nnumber-Batas jumlah item terpilih
msgKeystring'minSelected' / 'maxSelected'Key ke messages bag

or() / and()

Compound combinator — nest rule lain secara arbitrary. or() lolos jika salah satu sub-rule lolos; and() gagal di sub-rule pertama yang gagal (short-circuit), pesan error dari sub-rule yang gagal menang (lebih spesifik) atas pesan key milik combinator.

Signature:

ts
function or(key: string, ...rules: ValidationRule<unknown>[]): ValidationRule<unknown>
function and(key: string, ...rules: ValidationRule<unknown>[]): ValidationRule<unknown>

Parameters

NameTypeDefaultDescription
keystring-Key ke messages bag — wajib diisi, tidak ada default message otomatis untuk kombinasi arbitrary
rulesValidationRule<unknown>[]-Sub-rule yang di-nest

Contoh:

ts
:validation="or('emailOrPhone', email(), pattern(/^\d{10,13}$/))"

Component Catalog

Notch

ComponentTypeDescription
FormInputStylizedText/number/email/password/currency input dengan floating label
FormTextareaStylizedVersi <textarea> dari FormInput
FormSelectStylizedSingle/multi-select, sync & async, inline & detached
FormDatetimeStylized8-mode date/time picker

Rich Text

ComponentTypeDescription
FormQuillStylizedQuill-based rich text editor, subpath @bpmlib/vue-saform/quill