@velkymx/vibeui 0.1.1 → 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 +361 -7
- package/dist/vibeui.css +1 -0
- package/dist/vibeui.es.js +2608 -21
- package/dist/vibeui.umd.js +1 -1
- package/package.json +43 -8
package/README.md
CHANGED
|
@@ -4,11 +4,12 @@ A modern Vue 3 UI component library built with Bootstrap 5, designed to simplify
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
* Vue 3 Composition API
|
|
8
|
-
* Bootstrap 5 Integration
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* **Vue 3 Composition API**: Built from the ground up using modern, reactive Vue.js practices.
|
|
8
|
+
* **Bootstrap 5 Integration**: Directly utilizes Bootstrap 5 CSS for consistency, without additional styling overhead.
|
|
9
|
+
* **Dual-Mode Components**: Use shorthand props for quick setup or composable slots for full control.
|
|
10
|
+
* **Lightweight & Modular**: Import only what you need, keeping your bundle small.
|
|
11
|
+
* **TypeScript Support**: Fully typed components for a great developer experience.
|
|
12
|
+
* **Accessibility First**: Components crafted with accessibility and usability in mind.
|
|
12
13
|
|
|
13
14
|
## Installation
|
|
14
15
|
|
|
@@ -55,10 +56,363 @@ const showAlert = ref(true);
|
|
|
55
56
|
</template>
|
|
56
57
|
```
|
|
57
58
|
|
|
59
|
+
## Dual-Mode Components
|
|
60
|
+
|
|
61
|
+
Many VibeUI components support two usage modes:
|
|
62
|
+
|
|
63
|
+
### Shorthand Mode (Array-Based Props)
|
|
64
|
+
|
|
65
|
+
Perfect for quickly building UIs with data arrays:
|
|
66
|
+
|
|
67
|
+
```vue
|
|
68
|
+
<template>
|
|
69
|
+
<VibeBreadcrumb :items="breadcrumbItems" />
|
|
70
|
+
<VibeNav tabs :items="navItems" />
|
|
71
|
+
<VibeDropdown id="menu" text="Menu" :items="dropdownItems" />
|
|
72
|
+
</template>
|
|
73
|
+
|
|
74
|
+
<script setup>
|
|
75
|
+
const breadcrumbItems = [
|
|
76
|
+
{ text: 'Home', href: '/' },
|
|
77
|
+
{ text: 'Products', href: '/products' },
|
|
78
|
+
{ text: 'Details', active: true }
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
const navItems = [
|
|
82
|
+
{ text: 'Home', href: '#', active: true },
|
|
83
|
+
{ text: 'Features', href: '#' },
|
|
84
|
+
{ text: 'Pricing', href: '#' }
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
const dropdownItems = [
|
|
88
|
+
{ text: 'Action', href: '#' },
|
|
89
|
+
{ text: 'Another action', href: '#' },
|
|
90
|
+
{ divider: true },
|
|
91
|
+
{ text: 'Separated link', href: '#' }
|
|
92
|
+
]
|
|
93
|
+
</script>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Composable Mode (Slot-Based)
|
|
97
|
+
|
|
98
|
+
For maximum flexibility and custom content:
|
|
99
|
+
|
|
100
|
+
```vue
|
|
101
|
+
<template>
|
|
102
|
+
<VibeBreadcrumb>
|
|
103
|
+
<VibeBreadcrumbItem href="/">Home</VibeBreadcrumbItem>
|
|
104
|
+
<VibeBreadcrumbItem href="/products">Products</VibeBreadcrumbItem>
|
|
105
|
+
<VibeBreadcrumbItem active>Details</VibeBreadcrumbItem>
|
|
106
|
+
</VibeBreadcrumb>
|
|
107
|
+
|
|
108
|
+
<VibeNav tabs>
|
|
109
|
+
<VibeNavItem active href="#">Home</VibeNavItem>
|
|
110
|
+
<VibeNavItem href="#">Features</VibeNavItem>
|
|
111
|
+
<VibeNavItem href="#">Pricing</VibeNavItem>
|
|
112
|
+
</VibeNav>
|
|
113
|
+
|
|
114
|
+
<VibeDropdown id="menu" text="Menu">
|
|
115
|
+
<VibeDropdownItem href="#">Action</VibeDropdownItem>
|
|
116
|
+
<VibeDropdownItem href="#">Another action</VibeDropdownItem>
|
|
117
|
+
<VibeDropdownItem divider />
|
|
118
|
+
<VibeDropdownItem href="#">Separated link</VibeDropdownItem>
|
|
119
|
+
</VibeDropdown>
|
|
120
|
+
</template>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Components with dual-mode support include: `VibeBreadcrumb`, `VibeNav`, `VibeNavbarNav`, `VibePagination`, `VibeListGroup`, `VibeAccordion`, `VibeDropdown`, and `VibeCarousel`.
|
|
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
|
+
|
|
58
336
|
## Components
|
|
59
337
|
|
|
60
|
-
|
|
61
|
-
|
|
338
|
+
VibeUI includes all major Bootstrap 5.3 components:
|
|
339
|
+
|
|
340
|
+
### Core Components
|
|
341
|
+
* **VibeAlert** - Alert messages with variants and dismissible option
|
|
342
|
+
* **VibeBadge** - Badges and labels with pill option
|
|
343
|
+
* **VibeButton** - Buttons with variants, sizes, and outline style
|
|
344
|
+
* **VibeButtonGroup** - Button groups with sizing and vertical layout
|
|
345
|
+
* **VibeCloseButton** - Close button with white variant
|
|
346
|
+
* **VibeSpinner** - Loading spinners (border and grow types)
|
|
347
|
+
* **VibePlaceholder** - Placeholder loading states with animations
|
|
348
|
+
|
|
349
|
+
### Card Components
|
|
350
|
+
* **VibeCard** - Card container with variant styling
|
|
351
|
+
* **VibeCardHeader** - Card header section
|
|
352
|
+
* **VibeCardBody** - Card body section
|
|
353
|
+
* **VibeCardFooter** - Card footer section
|
|
354
|
+
* **VibeCardImg** - Card images (top, bottom, or overlay)
|
|
355
|
+
* **VibeCardTitle** - Card title heading
|
|
356
|
+
* **VibeCardText** - Card text paragraph
|
|
357
|
+
|
|
358
|
+
### Navigation Components
|
|
359
|
+
* **VibeBreadcrumb** - Breadcrumb navigation container
|
|
360
|
+
* **VibeBreadcrumbItem** - Individual breadcrumb items
|
|
361
|
+
* **VibeNav** - Navigation tabs and pills
|
|
362
|
+
* **VibeNavItem** - Navigation items with active state
|
|
363
|
+
* **VibeNavbar** - Responsive navbar with variants
|
|
364
|
+
* **VibeNavbarBrand** - Navbar branding section
|
|
365
|
+
* **VibeNavbarToggle** - Navbar mobile toggle button
|
|
366
|
+
* **VibeNavbarNav** - Navbar navigation links container
|
|
367
|
+
* **VibePagination** - Pagination container
|
|
368
|
+
* **VibePaginationItem** - Individual pagination items
|
|
369
|
+
|
|
370
|
+
### List Components
|
|
371
|
+
* **VibeListGroup** - List group container with flush and horizontal options
|
|
372
|
+
* **VibeListGroupItem** - List items with variants and active state
|
|
373
|
+
|
|
374
|
+
### Progress Components
|
|
375
|
+
* **VibeProgress** - Progress bar container
|
|
376
|
+
* **VibeProgressBar** - Progress bar with variants, striped, and animated styles
|
|
377
|
+
|
|
378
|
+
### Interactive Components
|
|
379
|
+
* **VibeAccordion** - Accordion container with flush option
|
|
380
|
+
* **VibeAccordionItem** - Collapsible accordion items
|
|
381
|
+
* **VibeCollapse** - Collapse component for showing/hiding content
|
|
382
|
+
* **VibeDropdown** - Dropdown menus with variants and directions
|
|
383
|
+
* **VibeDropdownItem** - Dropdown menu items, dividers, and headers
|
|
384
|
+
* **VibeModal** - Modal dialogs with sizes and positions
|
|
385
|
+
* **VibeOffcanvas** - Offcanvas sidebars with placement options
|
|
386
|
+
* **VibeToast** - Toast notifications with autohide
|
|
387
|
+
* **VibeCarousel** - Image carousels with controls and indicators
|
|
388
|
+
* **VibeCarouselSlide** - Individual carousel slides
|
|
389
|
+
|
|
390
|
+
### Advanced Components
|
|
391
|
+
* **VibeTooltip** - Tooltips with placement options (requires Bootstrap JS)
|
|
392
|
+
* **VibePopover** - Popovers with title and content (requires Bootstrap JS)
|
|
393
|
+
* **VibeScrollspy** - Scrollspy for navigation highlighting
|
|
394
|
+
|
|
395
|
+
### Data Components
|
|
396
|
+
* **VibeDataTable** - Powerful data table with search, sorting, and pagination
|
|
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
|
|
62
416
|
|
|
63
417
|
## Contributing
|
|
64
418
|
|
package/dist/vibeui.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
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}
|