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)
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:
import { FormInput, FormTextarea, FormSelect, FormDatetime } from '@bpmlib/vue-saform';
import { FormQuill } from '@bpmlib/vue-saform/quill'; // subpath terpisah, lihat InstallationFunctions (validators):
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
| Dependency | Versi | Status | Deskripsi |
|---|---|---|---|
vue | ^3.3.0 | Required | Vue 3 framework |
quill | ^2.0.3 | Optional | Hanya dibutuhkan untuk FormQuill |
vue-quilly | ^1.1.5 | Optional | Vue wrapper untuk Quill, dibutuhkan FormQuill |
@enzedonline/quill-blot-formatter2 | ^3.0.3 | Optional | Image/blot resize toolbar untuk FormQuill |
quill2-image-uploader | ^1.0.4 | Optional | Paste/drop image upload module untuk FormQuill |
vue-virtual-scroller dan @bpmlib/vue-sabutton sudah termasuk sebagai dependency biasa — tidak perlu diinstall terpisah.
Package Installation
npm install @bpmlib/vue-saformyarn add @bpmlib/vue-saformpnpm add @bpmlib/vue-saformbun install @bpmlib/vue-saformImport
Basic Import:
import { FormInput, FormTextarea, FormSelect, FormDatetime } from '@bpmlib/vue-saform';CSS Import:
// 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):
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
<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:tokeninterpolation (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 kedocument.bodysupaya tidak ke-clip ancestor manapun.true— panel selalu Teleport+backdrop+centered modal card.'small'— inline di viewportmdbreakpoint 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()
- minLength() / maxLength()
- email()
- pattern()
- numeric()
- min() / max()
- sameAs()
- minSelected() / maxSelected()
- or() / and()
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:
function required(msgKey?: string): ValidationRule<unknown>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
msgKey | string | 'required' | Key ke messages bag untuk pesan error |
Contoh:
<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:
function minLength(n: number, msgKey?: string): ValidationRule<unknown>
function maxLength(n: number, msgKey?: string): ValidationRule<unknown>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
n | number | - | Batas panjang karakter |
msgKey | string | 'minLength' / 'maxLength' | Key ke messages bag |
Contoh:
:validation="[required(), minLength(8)]"email
Regex-based email check. Skip saat value kosong (pasangkan dengan required() jika wajib diisi).
Signature:
function email(msgKey?: string): ValidationRule<unknown>pattern
Signature:
function pattern(regex: string | RegExp, msgKey?: string): ValidationRule<unknown>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
regex | string | RegExp | - | Pattern yang harus di-match |
msgKey | string | 'pattern' | Key ke messages bag |
numeric
Memastikan value adalah string angka (/^-?\d+(\.\d+)?$/). Skip saat value kosong.
Signature:
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:
function min(n: number, msgKey?: string): ValidationRule<unknown>
function max(n: number, msgKey?: string): ValidationRule<unknown>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
n | number | - | Batas nilai |
msgKey | string | 'min' / 'max' | Key ke messages bag |
sameAs
Cross-field match, mis. konfirmasi password.
Signature:
function sameAs(otherValue: { value: unknown } | unknown, msgKey?: string): ValidationRule<unknown>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
otherValue | { value: unknown } | unknown | - | Nilai pembanding — boleh plain value atau ref (dibaca .value-nya saat rule dijalankan, jadi reaktif) |
msgKey | string | 'sameAs' | Key ke messages bag |
Contoh:
const password = ref('');
// ...
<FormInput id="confirm" type="password" :validation="sameAs(password)" />minSelected() / maxSelected()
Untuk FormSelect multiselect — validasi jumlah item terpilih pada modelValue array.
Signature:
function minSelected(n: number, msgKey?: string): ValidationRule<unknown>
function maxSelected(n: number, msgKey?: string): ValidationRule<unknown>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
n | number | - | Batas jumlah item terpilih |
msgKey | string | '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:
function or(key: string, ...rules: ValidationRule<unknown>[]): ValidationRule<unknown>
function and(key: string, ...rules: ValidationRule<unknown>[]): ValidationRule<unknown>Parameters
| Name | Type | Default | Description |
|---|---|---|---|
key | string | - | Key ke messages bag — wajib diisi, tidak ada default message otomatis untuk kombinasi arbitrary |
rules | ValidationRule<unknown>[] | - | Sub-rule yang di-nest |
Contoh:
:validation="or('emailOrPhone', email(), pattern(/^\d{10,13}$/))"Component Catalog
Notch
| Component | Type | Description |
|---|---|---|
| FormInput | Stylized | Text/number/email/password/currency input dengan floating label |
| FormTextarea | Stylized | Versi <textarea> dari FormInput |
| FormSelect | Stylized | Single/multi-select, sync & async, inline & detached |
| FormDatetime | Stylized | 8-mode date/time picker |
Rich Text
| Component | Type | Description |
|---|---|---|
| FormQuill | Stylized | Quill-based rich text editor, subpath @bpmlib/vue-saform/quill |