@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 +230 -0
- package/dist/vibeui.css +1 -1
- package/dist/vibeui.es.js +1724 -736
- package/dist/vibeui.umd.js +1 -1
- package/package.json +6 -1
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}
|