@velkymx/vibeui 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -122,6 +122,217 @@ For maximum flexibility and custom content:
122
122
 
123
123
  Components with dual-mode support include: `VibeBreadcrumb`, `VibeNav`, `VibeNavbarNav`, `VibePagination`, `VibeListGroup`, `VibeAccordion`, `VibeDropdown`, and `VibeCarousel`.
124
124
 
125
+ ## Form Components with Validation
126
+
127
+ VibeUI provides comprehensive form components with built-in validation support for both front-end and API-based validation:
128
+
129
+ ### Basic Form Example
130
+
131
+ ```vue
132
+ <script setup lang="ts">
133
+ import { ref } from 'vue'
134
+ import { validators } from '@velkymx/vibeui'
135
+
136
+ const email = ref('')
137
+ const emailValidationState = ref(null)
138
+ const emailValidationMessage = ref('')
139
+
140
+ const validateEmail = async () => {
141
+ const emailRules = [validators.required(), validators.email()]
142
+
143
+ for (const rule of emailRules) {
144
+ const result = await rule.validator(email.value)
145
+ if (result !== true) {
146
+ emailValidationState.value = 'invalid'
147
+ emailValidationMessage.value = typeof result === 'string' ? result : rule.message
148
+ return
149
+ }
150
+ }
151
+
152
+ emailValidationState.value = 'valid'
153
+ emailValidationMessage.value = ''
154
+ }
155
+ </script>
156
+
157
+ <template>
158
+ <VibeFormInput
159
+ v-model="email"
160
+ id="email"
161
+ type="email"
162
+ label="Email Address"
163
+ placeholder="Enter your email"
164
+ :validation-state="emailValidationState"
165
+ :validation-message="emailValidationMessage"
166
+ @validate="validateEmail"
167
+ required
168
+ />
169
+ </template>
170
+ ```
171
+
172
+ ### Advanced Form with Composable
173
+
174
+ ```vue
175
+ <script setup lang="ts">
176
+ import { ref } from 'vue'
177
+ import { useFormValidation, validators } from '@velkymx/vibeui'
178
+
179
+ const form = {
180
+ username: useFormValidation(''),
181
+ password: useFormValidation(''),
182
+ age: useFormValidation(0),
183
+ country: useFormValidation(''),
184
+ agreeToTerms: useFormValidation(false)
185
+ }
186
+
187
+ const handleSubmit = async () => {
188
+ const usernameValid = await form.username.validate([
189
+ validators.required(),
190
+ validators.minLength(3)
191
+ ])
192
+
193
+ const passwordValid = await form.password.validate([
194
+ validators.required(),
195
+ validators.minLength(8)
196
+ ])
197
+
198
+ const ageValid = await form.age.validate([
199
+ validators.required(),
200
+ validators.min(18)
201
+ ])
202
+
203
+ if (usernameValid.valid && passwordValid.valid && ageValid.valid) {
204
+ console.log('Form is valid!')
205
+ }
206
+ }
207
+ </script>
208
+
209
+ <template>
210
+ <form @submit.prevent="handleSubmit">
211
+ <VibeFormInput
212
+ v-model="form.username.value"
213
+ id="username"
214
+ label="Username"
215
+ :validation-state="form.username.validationState"
216
+ :validation-message="form.username.validationMessage"
217
+ @validate="() => form.username.validate([validators.required(), validators.minLength(3)])"
218
+ required
219
+ />
220
+
221
+ <VibeFormInput
222
+ v-model="form.password.value"
223
+ id="password"
224
+ type="password"
225
+ label="Password"
226
+ :validation-state="form.password.validationState"
227
+ :validation-message="form.password.validationMessage"
228
+ @validate="() => form.password.validate([validators.required(), validators.minLength(8)])"
229
+ required
230
+ />
231
+
232
+ <VibeFormSpinbutton
233
+ v-model="form.age.value"
234
+ id="age"
235
+ label="Age"
236
+ :min="0"
237
+ :max="120"
238
+ :validation-state="form.age.validationState"
239
+ :validation-message="form.age.validationMessage"
240
+ @validate="() => form.age.validate([validators.required(), validators.min(18)])"
241
+ required
242
+ />
243
+
244
+ <VibeFormCheckbox
245
+ v-model="form.agreeToTerms.value"
246
+ id="terms"
247
+ label="I agree to the terms and conditions"
248
+ required
249
+ />
250
+
251
+ <VibeButton type="submit" variant="primary">Submit</VibeButton>
252
+ </form>
253
+ </template>
254
+ ```
255
+
256
+ ### API Validation Example
257
+
258
+ ```vue
259
+ <script setup lang="ts">
260
+ import { ref } from 'vue'
261
+ import { validators } from '@velkymx/vibeui'
262
+
263
+ const username = ref('')
264
+ const usernameValidationState = ref(null)
265
+ const usernameValidationMessage = ref('')
266
+
267
+ // Custom async validator for checking username availability
268
+ const checkUsernameAvailability = validators.async(async (value) => {
269
+ if (!value) return true
270
+
271
+ try {
272
+ const response = await fetch(`/api/check-username?username=${value}`)
273
+ const data = await response.json()
274
+
275
+ if (data.available) {
276
+ return true
277
+ } else {
278
+ return 'Username is already taken'
279
+ }
280
+ } catch (error) {
281
+ return 'Error checking username availability'
282
+ }
283
+ })
284
+
285
+ const validateUsername = async () => {
286
+ const rules = [
287
+ validators.required(),
288
+ validators.minLength(3),
289
+ checkUsernameAvailability
290
+ ]
291
+
292
+ usernameValidationState.value = null
293
+
294
+ for (const rule of rules) {
295
+ const result = await rule.validator(username.value)
296
+ if (result !== true) {
297
+ usernameValidationState.value = 'invalid'
298
+ usernameValidationMessage.value = typeof result === 'string' ? result : rule.message
299
+ return
300
+ }
301
+ }
302
+
303
+ usernameValidationState.value = 'valid'
304
+ usernameValidationMessage.value = 'Username is available!'
305
+ }
306
+ </script>
307
+
308
+ <template>
309
+ <VibeFormInput
310
+ v-model="username"
311
+ id="username"
312
+ label="Username"
313
+ :validation-state="usernameValidationState"
314
+ :validation-message="usernameValidationMessage"
315
+ @validate="validateUsername"
316
+ validate-on="blur"
317
+ required
318
+ />
319
+ </template>
320
+ ```
321
+
322
+ ### Available Validators
323
+
324
+ VibeUI provides built-in validators:
325
+
326
+ - `validators.required(message?)` - Field is required
327
+ - `validators.email(message?)` - Valid email format
328
+ - `validators.minLength(min, message?)` - Minimum string length
329
+ - `validators.maxLength(max, message?)` - Maximum string length
330
+ - `validators.min(min, message?)` - Minimum numeric value
331
+ - `validators.max(max, message?)` - Maximum numeric value
332
+ - `validators.pattern(regex, message?)` - Custom regex pattern
333
+ - `validators.url(message?)` - Valid URL format
334
+ - `validators.async(asyncFn)` - Custom async validator
335
+
125
336
  ## Components
126
337
 
127
338
  VibeUI includes all major Bootstrap 5.3 components:
@@ -184,6 +395,25 @@ VibeUI includes all major Bootstrap 5.3 components:
184
395
  ### Data Components
185
396
  * **VibeDataTable** - Powerful data table with search, sorting, and pagination
186
397
 
398
+ ### Form Components
399
+ * **VibeFormInput** - Text, email, password, number inputs with validation
400
+ * **VibeFormSelect** - Select dropdowns with single/multiple selection
401
+ * **VibeFormTextarea** - Multi-line text input with character count
402
+ * **VibeFormSpinbutton** - Number input with increment/decrement buttons
403
+ * **VibeFormDatepicker** - Date, time, and datetime input controls
404
+ * **VibeFormCheckbox** - Checkboxes with support for arrays
405
+ * **VibeFormRadio** - Radio button groups
406
+ * **VibeFormSwitch** - Toggle switches
407
+ * **VibeFormGroup** - Form group container with floating labels
408
+ * **VibeFormWysiwyg** - WYSIWYG editor with QuillJS (requires quill package)
409
+
410
+ All form components support:
411
+ - Front-end validation with built-in validators
412
+ - Async validation for API calls
413
+ - Bootstrap 5 styling and validation states
414
+ - Accessibility features
415
+ - Custom validation messages
416
+
187
417
  ## Contributing
188
418
 
189
419
  Pull requests and contributions are very welcome! Please fork the repo, create a branch for your feature, and submit a PR.
package/dist/vibeui.css CHANGED
@@ -1 +1 @@
1
- .vibe-datatable[data-v-48221c7b]{width:100%}.datatable-info[data-v-48221c7b]{padding:.5rem 0;color:#6c757d}
1
+ .vibe-datatable[data-v-48221c7b]{width:100%}.datatable-info[data-v-48221c7b]{padding:.5rem 0;color:#6c757d}.input-group-vertical[data-v-e706d3dc]{flex-direction:column}.input-group-vertical[data-v-e706d3dc]>*{width:100%}.vibe-wysiwyg-container[data-v-2eb8f4ff]{border:1px solid #ced4da;border-radius:.375rem}.vibe-wysiwyg-container.is-valid[data-v-2eb8f4ff]{border-color:#198754}.vibe-wysiwyg-container.is-invalid[data-v-2eb8f4ff]{border-color:#dc3545}.vibe-wysiwyg-container.disabled[data-v-2eb8f4ff]{background-color:#e9ecef;opacity:.6;cursor:not-allowed}.vibe-wysiwyg-container[data-v-2eb8f4ff] .ql-container{border:none;font-size:1rem}.vibe-wysiwyg-container[data-v-2eb8f4ff] .ql-toolbar{border:none;border-bottom:1px solid #ced4da;border-top-left-radius:.375rem;border-top-right-radius:.375rem}.vibe-wysiwyg-container[data-v-2eb8f4ff] .ql-editor{min-height:150px}