@velkymx/vibeui 0.9.0 → 1.1.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/AGENTS.md +44 -0
- package/CLAUDE.md +48 -0
- package/README.md +222 -197
- package/dist/App.vue.d.ts +2 -1
- package/dist/components/HelloWorld.vue.d.ts +2 -1
- package/dist/components/VibeAccordion.vue.d.ts +39 -40
- package/dist/components/VibeAlert.vue.d.ts +21 -37
- package/dist/components/VibeAutocomplete.vue.d.ts +59 -31
- package/dist/components/VibeBadge.vue.d.ts +10 -18
- package/dist/components/VibeBreadcrumb.vue.d.ts +25 -21
- package/dist/components/VibeButton.vue.d.ts +13 -17
- package/dist/components/VibeButtonGroup.vue.d.ts +11 -19
- package/dist/components/VibeCard.vue.d.ts +51 -24
- package/dist/components/VibeCarousel.vue.d.ts +27 -49
- package/dist/components/VibeChartBar.vue.d.ts +61 -0
- package/dist/components/VibeChartLine.vue.d.ts +70 -0
- package/dist/components/VibeChartPie.vue.d.ts +34 -0
- package/dist/components/VibeCloseButton.vue.d.ts +9 -7
- package/dist/components/VibeCol.vue.d.ts +9 -17
- package/dist/components/VibeCollapse.vue.d.ts +24 -32
- package/dist/components/VibeContainer.vue.d.ts +9 -17
- package/dist/components/VibeDataTable.vue.d.ts +122 -63
- package/dist/components/VibeDatePicker.vue.d.ts +4 -6
- package/dist/components/VibeDraggable.vue.d.ts +9 -13
- package/dist/components/VibeDropdown.vue.d.ts +46 -43
- package/dist/components/VibeDroppable.vue.d.ts +9 -13
- package/dist/components/VibeFileInput.vue.d.ts +14 -20
- package/dist/components/VibeFormCheckbox.vue.d.ts +30 -22
- package/dist/components/VibeFormDatepicker.vue.d.ts +27 -30
- package/dist/components/VibeFormErrorSummary.vue.d.ts +17 -0
- package/dist/components/VibeFormGroup.vue.d.ts +10 -13
- package/dist/components/VibeFormInput.vue.d.ts +60 -25
- package/dist/components/VibeFormRadio.vue.d.ts +23 -24
- package/dist/components/VibeFormSelect.vue.d.ts +28 -34
- package/dist/components/VibeFormSpinbutton.vue.d.ts +31 -34
- package/dist/components/VibeFormSwitch.vue.d.ts +22 -23
- package/dist/components/VibeFormTextarea.vue.d.ts +24 -25
- package/dist/components/VibeFormWysiwyg.vue.d.ts +23 -22
- package/dist/components/VibeHero.vue.d.ts +117 -0
- package/dist/components/VibeIcon.vue.d.ts +27 -7
- package/dist/components/VibeInputGroup.vue.d.ts +12 -14
- package/dist/components/VibeLink.vue.d.ts +14 -18
- package/dist/components/VibeListGroup.vue.d.ts +26 -22
- package/dist/components/VibeModal.vue.d.ts +47 -45
- package/dist/components/VibeNav.vue.d.ts +40 -20
- package/dist/components/VibeNavbar.vue.d.ts +12 -19
- package/dist/components/VibeNavbarBrand.vue.d.ts +9 -17
- package/dist/components/VibeNavbarNav.vue.d.ts +52 -33
- package/dist/components/VibeNavbarToggle.vue.d.ts +7 -5
- package/dist/components/VibeOffcanvas.vue.d.ts +28 -43
- package/dist/components/VibePagination.vue.d.ts +38 -29
- package/dist/components/VibePlaceholder.vue.d.ts +9 -17
- package/dist/components/VibePopover.vue.d.ts +17 -40
- package/dist/components/VibeProgress.vue.d.ts +13 -21
- package/dist/components/VibeResizable.vue.d.ts +10 -14
- package/dist/components/VibeRow.vue.d.ts +9 -17
- package/dist/components/VibeScrollspy.vue.d.ts +37 -25
- package/dist/components/VibeSkeleton.vue.d.ts +2 -1
- package/dist/components/VibeSlider.vue.d.ts +4 -5
- package/dist/components/VibeSortable.vue.d.ts +37 -18
- package/dist/components/VibeSpinner.vue.d.ts +4 -7
- package/dist/components/VibeStepper.vue.d.ts +32 -31
- package/dist/components/VibeTab.vue.d.ts +7 -11
- package/dist/components/VibeTabContent.vue.d.ts +13 -31
- package/dist/components/VibeTabs.vue.d.ts +9 -13
- package/dist/components/VibeToast.vue.d.ts +31 -44
- package/dist/components/VibeToastHost.vue.d.ts +2 -1
- package/dist/components/VibeTooltip.vue.d.ts +16 -39
- package/dist/components/chart/chartColors.d.ts +3 -0
- package/dist/components/chart/chartResize.d.ts +2 -0
- package/dist/components/chart/chartTooltip.d.ts +2 -0
- package/dist/components/chart/chartTypes.d.ts +6 -0
- package/dist/components/chart/drawBar.d.ts +5 -0
- package/dist/components/chart/drawLine.d.ts +18 -0
- package/dist/components/chart/drawPie.d.ts +4 -0
- package/dist/components/dndStore.d.ts +1 -0
- package/dist/components/index.d.ts +6 -1
- package/dist/composables/useBreakpoints.d.ts +1 -0
- package/dist/composables/useColorMode.d.ts +1 -1
- package/dist/composables/useId.d.ts +1 -4
- package/dist/directives/vTooltip.d.ts +2 -3
- package/dist/injectionKeys.d.ts +34 -0
- package/dist/purify.es-eYghtX9j.mjs +735 -0
- package/dist/types.d.ts +23 -2
- package/dist/utils/routeKey.d.ts +11 -0
- package/dist/utils/safeCss.d.ts +16 -0
- package/dist/utils/safeHref.d.ts +7 -0
- package/dist/utils/sanitizeHtml.d.ts +13 -0
- package/dist/vibeui.css +2 -1
- package/dist/vibeui.es.js +8527 -5855
- package/dist/vibeui.umd.js +3 -1
- package/docs/README.md +225 -0
- package/docs/components/advanced/popover.md +125 -0
- package/docs/components/advanced/scrollspy.md +106 -0
- package/docs/components/advanced/tooltip.md +90 -0
- package/docs/components/card/card.md +223 -0
- package/docs/components/charts/chart-bar.md +129 -0
- package/docs/components/charts/chart-line.md +136 -0
- package/docs/components/charts/chart-pie.md +102 -0
- package/docs/components/core/alert.md +94 -0
- package/docs/components/core/badge.md +75 -0
- package/docs/components/core/button-group.md +99 -0
- package/docs/components/core/button.md +145 -0
- package/docs/components/core/close-button.md +82 -0
- package/docs/components/core/link.md +79 -0
- package/docs/components/core/placeholder.md +129 -0
- package/docs/components/core/skeleton.md +40 -0
- package/docs/components/core/spinner.md +103 -0
- package/docs/components/data/datatable.md +437 -0
- package/docs/components/hero/hero.md +65 -0
- package/docs/components/interactive/accordion.md +121 -0
- package/docs/components/interactive/carousel.md +112 -0
- package/docs/components/interactive/collapse.md +106 -0
- package/docs/components/interactive/date-picker.md +78 -0
- package/docs/components/interactive/draggable.md +91 -0
- package/docs/components/interactive/dropdown.md +129 -0
- package/docs/components/interactive/modal.md +151 -0
- package/docs/components/interactive/offcanvas.md +92 -0
- package/docs/components/interactive/resizable.md +73 -0
- package/docs/components/interactive/slider.md +57 -0
- package/docs/components/interactive/sortable.md +52 -0
- package/docs/components/interactive/stepper.md +83 -0
- package/docs/components/interactive/tabs.md +66 -0
- package/docs/components/interactive/toast.md +177 -0
- package/docs/components/layout/col.md +117 -0
- package/docs/components/layout/container.md +53 -0
- package/docs/components/layout/row.md +107 -0
- package/docs/components/list/list-group.md +225 -0
- package/docs/components/navigation/breadcrumb.md +120 -0
- package/docs/components/navigation/nav.md +151 -0
- package/docs/components/navigation/navbar.md +114 -0
- package/docs/components/navigation/pagination.md +163 -0
- package/docs/components/progress/progress.md +188 -0
- package/docs/composables/back-button.md +28 -0
- package/docs/composables/breakpoints.md +54 -0
- package/docs/composables/color-mode.md +179 -0
- package/docs/composables/use-form.md +78 -0
- package/docs/composables/use-position.md +68 -0
- package/docs/composables/use-toast.md +91 -0
- package/docs/directives/v-tooltip.md +58 -0
- package/docs/forms/README.md +87 -0
- package/docs/forms/autocomplete.md +96 -0
- package/docs/forms/file-input.md +97 -0
- package/docs/forms/form-checkbox.md +100 -0
- package/docs/forms/form-datepicker.md +80 -0
- package/docs/forms/form-error-summary.md +96 -0
- package/docs/forms/form-group.md +118 -0
- package/docs/forms/form-input.md +140 -0
- package/docs/forms/form-radio.md +82 -0
- package/docs/forms/form-select.md +98 -0
- package/docs/forms/form-spinbutton.md +94 -0
- package/docs/forms/form-switch.md +75 -0
- package/docs/forms/form-textarea.md +82 -0
- package/docs/forms/form-wysiwyg.md +99 -0
- package/docs/forms/input-group.md +70 -0
- package/docs/forms/validation.md +213 -0
- package/docs/getting-started/starter-template.md +136 -0
- package/docs/superpowers/plans/2026-05-12-vibe-charts.md +1999 -0
- package/docs/superpowers/plans/2026-05-29-e2e-browser-testing.md +615 -0
- package/docs/superpowers/specs/2026-05-12-charting-design.md +185 -0
- package/docs/superpowers/specs/2026-05-29-e2e-browser-testing-design.md +168 -0
- package/docs/utilities/position.md +82 -0
- package/docs/versioning.md +57 -0
- package/examples/README.md +201 -0
- package/examples/album.html +262 -0
- package/examples/blog.html +166 -0
- package/examples/carousel.html +114 -0
- package/examples/checkout.html +275 -0
- package/examples/cover.html +89 -0
- package/examples/dashboard.html +156 -0
- package/examples/index.html +129 -0
- package/examples/jumbotron.html +56 -0
- package/examples/mobile-dashboard.html +138 -0
- package/examples/pricing.html +172 -0
- package/examples/product.html +145 -0
- package/examples/sidebars.html +166 -0
- package/examples/sign-in.html +119 -0
- package/examples/starter.html +314 -0
- package/examples/sticky-footer-navbar.html +70 -0
- package/examples/sticky-footer.html +60 -0
- package/examples/test-simple.html +91 -0
- package/llms.txt +801 -0
- package/package.json +44 -14
- package/dist/types/index.d.ts +0 -6
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# VibeSpinner
|
|
2
|
+
|
|
3
|
+
Loading spinner with border or grow animation styles.
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
|------|------|---------|-------------|
|
|
9
|
+
| `variant` | `Variant` | `undefined` | Text color variant |
|
|
10
|
+
| `type` | `SpinnerType` | `'border'` | Spinner type: `'border'` or `'grow'` |
|
|
11
|
+
| `size` | `Size` | `undefined` | Spinner size: `'sm'` or `'lg'` |
|
|
12
|
+
| `label` | `String` | `'Loading...'` | Screen reader text |
|
|
13
|
+
| `tag` | `String` | `'div'` | HTML tag to render |
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Basic Spinner
|
|
18
|
+
|
|
19
|
+
```vue
|
|
20
|
+
<template>
|
|
21
|
+
<VibeSpinner />
|
|
22
|
+
</template>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Grow Spinner
|
|
26
|
+
|
|
27
|
+
```vue
|
|
28
|
+
<template>
|
|
29
|
+
<VibeSpinner type="grow" />
|
|
30
|
+
</template>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Colored Spinners
|
|
34
|
+
|
|
35
|
+
```vue
|
|
36
|
+
<template>
|
|
37
|
+
<div>
|
|
38
|
+
<VibeSpinner variant="primary" />
|
|
39
|
+
<VibeSpinner variant="success" />
|
|
40
|
+
<VibeSpinner variant="danger" />
|
|
41
|
+
<VibeSpinner variant="warning" />
|
|
42
|
+
<VibeSpinner variant="info" />
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Small Spinners
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<template>
|
|
51
|
+
<div>
|
|
52
|
+
<VibeSpinner size="sm" />
|
|
53
|
+
<VibeSpinner type="grow" size="sm" />
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### In Button
|
|
59
|
+
|
|
60
|
+
```vue
|
|
61
|
+
<template>
|
|
62
|
+
<VibeButton variant="primary" disabled>
|
|
63
|
+
<VibeSpinner size="sm" tag="span" />
|
|
64
|
+
Loading...
|
|
65
|
+
</VibeButton>
|
|
66
|
+
</template>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Custom Label
|
|
70
|
+
|
|
71
|
+
```vue
|
|
72
|
+
<template>
|
|
73
|
+
<VibeSpinner label="Please wait while we load your data..." />
|
|
74
|
+
</template>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Loading State Example
|
|
78
|
+
|
|
79
|
+
```vue
|
|
80
|
+
<script setup>
|
|
81
|
+
import { ref } from 'vue'
|
|
82
|
+
|
|
83
|
+
const loading = ref(true)
|
|
84
|
+
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
loading.value = false
|
|
87
|
+
}, 3000)
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<template>
|
|
91
|
+
<div>
|
|
92
|
+
<VibeSpinner v-if="loading" variant="primary" />
|
|
93
|
+
<div v-else>Content loaded!</div>
|
|
94
|
+
</div>
|
|
95
|
+
</template>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Bootstrap CSS Classes
|
|
99
|
+
|
|
100
|
+
- `.spinner-border` or `.spinner-grow`
|
|
101
|
+
- `.text-{variant}`
|
|
102
|
+
- `.spinner-{type}-{size}`
|
|
103
|
+
- `.visually-hidden` (for label)
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
# VibeDataTable
|
|
2
|
+
|
|
3
|
+
Powerful data table component with search, sorting, and pagination - similar to DataTables.net but built for Vue 3 and Bootstrap 5.3.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Search/Filter** - Real-time search across all searchable columns
|
|
8
|
+
- **Column Sorting** - Click column headers to sort (asc/desc)
|
|
9
|
+
- **Pagination** - Built-in pagination with customizable page sizes
|
|
10
|
+
- **Responsive** - Mobile-friendly with responsive table wrapper
|
|
11
|
+
- **Bootstrap Styling** - All Bootstrap table variants (striped, bordered, hover, etc.)
|
|
12
|
+
- **Custom Cell Rendering** - Slots for custom cell content
|
|
13
|
+
- **Formatters** - Custom data formatters per column
|
|
14
|
+
- **TypeScript** - Fully typed with comprehensive interfaces
|
|
15
|
+
|
|
16
|
+
## Props
|
|
17
|
+
|
|
18
|
+
### Data Props
|
|
19
|
+
|
|
20
|
+
| Prop | Type | Default | Description |
|
|
21
|
+
|------|------|---------|-------------|
|
|
22
|
+
| `items` | `T[]` | `[]` | Array of data objects to display |
|
|
23
|
+
| `columns` | `DataTableColumn<T>[]` | `[]` | Column definitions. Defaults to an empty array, so an unset/loading state renders an empty table rather than erroring. |
|
|
24
|
+
| `rowKey` | `String` | `'id'` | Property name used as the unique key for each row. **Recommended** — set it to a unique field in your data (e.g. `'id'`, `'uuid'`) so Vue tracks rows correctly across sorting, filtering, and pagination. Falls back to a positional key (with a DEV warning) when missing. |
|
|
25
|
+
|
|
26
|
+
> **Typing tip**: `DataTableColumn` is generic over your row type. For full slot-prop / formatter typing, annotate the column array:
|
|
27
|
+
>
|
|
28
|
+
> ```ts
|
|
29
|
+
> import type { DataTableColumn } from '@velkymx/vibeui'
|
|
30
|
+
> interface User { id: number; name: string; active: boolean }
|
|
31
|
+
> const columns: DataTableColumn<User>[] = [
|
|
32
|
+
> { key: 'name', label: 'Name' }, // key narrowed to keyof User
|
|
33
|
+
> { key: 'active', label: 'Active', formatter: v => v ? 'Yes' : 'No' }
|
|
34
|
+
> ]
|
|
35
|
+
> ```
|
|
36
|
+
>
|
|
37
|
+
> Without the annotation, `T` defaults to `Record<string, unknown>` and slot props arrive as `unknown`.
|
|
38
|
+
|
|
39
|
+
### Table Styling Props
|
|
40
|
+
|
|
41
|
+
| Prop | Type | Default | Description |
|
|
42
|
+
|------|------|---------|-------------|
|
|
43
|
+
| `striped` | `Boolean` | `false` | Striped table rows |
|
|
44
|
+
| `bordered` | `Boolean` | `false` | Bordered table |
|
|
45
|
+
| `borderless` | `Boolean` | `false` | Remove all borders |
|
|
46
|
+
| `hover` | `Boolean` | `false` | Hover effect on rows |
|
|
47
|
+
| `small` | `Boolean` | `false` | Compact table |
|
|
48
|
+
| `responsive` | `Boolean` | `true` | Responsive table wrapper |
|
|
49
|
+
| `stack` | `Boolean` | `false` | Transforms table into cards on mobile screens |
|
|
50
|
+
| `variant` | `String` | `undefined` | Table color variant |
|
|
51
|
+
|
|
52
|
+
### Feature Props
|
|
53
|
+
|
|
54
|
+
| Prop | Type | Default | Description |
|
|
55
|
+
|------|------|---------|-------------|
|
|
56
|
+
| `searchable` | `Boolean` | `true` | Enable search functionality |
|
|
57
|
+
| `sortable` | `Boolean` | `true` | Enable column sorting |
|
|
58
|
+
| `paginated` | `Boolean` | `true` | Enable pagination |
|
|
59
|
+
| `clickable` | `Boolean` | `false` | Show a pointer cursor on rows to signal they are interactive (pair with a `@row-clicked` listener) |
|
|
60
|
+
|
|
61
|
+
### Search Props
|
|
62
|
+
|
|
63
|
+
| Prop | Type | Default | Description |
|
|
64
|
+
|------|------|---------|-------------|
|
|
65
|
+
| `searchPlaceholder` | `String` | `'Search...'` | Search input placeholder |
|
|
66
|
+
| `searchDebounce` | `Number` | `300` | Search debounce delay (ms) |
|
|
67
|
+
|
|
68
|
+
### Pagination Props
|
|
69
|
+
|
|
70
|
+
| Prop | Type | Default | Description |
|
|
71
|
+
|------|------|---------|-------------|
|
|
72
|
+
| `perPage` | `Number` | `10` | Items per page |
|
|
73
|
+
| `currentPage` | `Number` | `1` | Current page number |
|
|
74
|
+
| `perPageOptions` | `Number[]` | `[5, 10, 25, 50, 100]` | Page size options |
|
|
75
|
+
|
|
76
|
+
### Sorting Props
|
|
77
|
+
|
|
78
|
+
| Prop | Type | Default | Description |
|
|
79
|
+
|------|------|---------|-------------|
|
|
80
|
+
| `sortBy` | `String` | `undefined` | Initial sort column key |
|
|
81
|
+
| `sortDesc` | `Boolean` | `false` | Initial sort direction (true = descending) |
|
|
82
|
+
|
|
83
|
+
### Display Props
|
|
84
|
+
|
|
85
|
+
| Prop | Type | Default | Description |
|
|
86
|
+
|------|------|---------|-------------|
|
|
87
|
+
| `showEmpty` | `Boolean` | `true` | Show message when no data |
|
|
88
|
+
| `emptyText` | `String` | `'No data available'` | Empty state message |
|
|
89
|
+
| `showPerPage` | `Boolean` | `true` | Show per-page selector |
|
|
90
|
+
| `showInfo` | `Boolean` | `true` | Show info text (X to Y of Z entries) |
|
|
91
|
+
| `infoText` | `String` | `'Showing {start} to {end} of {total} entries'` | Info text template |
|
|
92
|
+
| `filteredInfoText` | `String` | `'Showing {start} to {end} of {total} entries (filtered from {totalRows} total entries)'` | Filtered info text template |
|
|
93
|
+
|
|
94
|
+
## Column Definition
|
|
95
|
+
|
|
96
|
+
The `columns` prop accepts an array of `DataTableColumn` objects:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
interface DataTableColumn {
|
|
100
|
+
key: string // Property key in data object
|
|
101
|
+
label: string // Column header label
|
|
102
|
+
sortable?: boolean // Enable/disable sorting (default: true)
|
|
103
|
+
searchable?: boolean // Include in search (default: true)
|
|
104
|
+
formatter?: (value: any, row: any) => string | number // Custom formatter
|
|
105
|
+
class?: string // CSS class for td
|
|
106
|
+
headerClass?: string // CSS class for th
|
|
107
|
+
thStyle?: Record<string, string> // Inline styles for th (sanitized — see note)
|
|
108
|
+
tdStyle?: Record<string, string> // Inline styles for td (sanitized — see note)
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> **`thStyle` / `tdStyle` are sanitized.** Both objects are filtered against a safe CSS-property allowlist before being applied, as a defense against CSS injection (data exfiltration / UI spoofing) when column config comes from an API or untrusted source. Properties outside the allowlist are dropped.
|
|
113
|
+
|
|
114
|
+
## Events
|
|
115
|
+
|
|
116
|
+
| Event | Payload | Description |
|
|
117
|
+
|-------|---------|-------------|
|
|
118
|
+
| `update:currentPage` | `Number` | Emitted when page changes |
|
|
119
|
+
| `update:perPage` | `Number` | Emitted when per-page changes |
|
|
120
|
+
| `update:sortBy` | `String` | Emitted when sort column changes |
|
|
121
|
+
| `update:sortDesc` | `Boolean` | Emitted when sort direction changes |
|
|
122
|
+
| `row-clicked` | `(item, globalIndex)` | Emitted when a row is clicked. `globalIndex` is the index within the full filtered/sorted dataset, not the current page. Only emitted when a `@row-clicked` listener is attached (rows show a pointer cursor in that case). |
|
|
123
|
+
| `component-error` | `ComponentError` | Emitted if an internal error occurs |
|
|
124
|
+
|
|
125
|
+
## Slots
|
|
126
|
+
|
|
127
|
+
| Slot | Props | Description |
|
|
128
|
+
|------|-------|-------------|
|
|
129
|
+
| `cell({columnKey})` | `{ item, value, index }` | Custom cell rendering for specific column |
|
|
130
|
+
|
|
131
|
+
## Usage
|
|
132
|
+
|
|
133
|
+
### Basic DataTable
|
|
134
|
+
|
|
135
|
+
```vue
|
|
136
|
+
<script setup>
|
|
137
|
+
import { ref } from 'vue'
|
|
138
|
+
|
|
139
|
+
const columns = [
|
|
140
|
+
{ key: 'id', label: 'ID' },
|
|
141
|
+
{ key: 'name', label: 'Name' },
|
|
142
|
+
{ key: 'email', label: 'Email' },
|
|
143
|
+
{ key: 'status', label: 'Status' }
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
const items = [
|
|
147
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'Active' },
|
|
148
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'Inactive' },
|
|
149
|
+
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'Active' }
|
|
150
|
+
]
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<template>
|
|
154
|
+
<VibeDataTable :columns="columns" :items="items" row-key="id" />
|
|
155
|
+
</template>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
> Always pass `row-key` pointing at a unique field (here `id`) so Vue keys rows stably across sorting, filtering, and pagination.
|
|
159
|
+
|
|
160
|
+
### With Bootstrap Styling
|
|
161
|
+
|
|
162
|
+
```vue
|
|
163
|
+
<template>
|
|
164
|
+
<VibeDataTable
|
|
165
|
+
:columns="columns"
|
|
166
|
+
:items="items"
|
|
167
|
+
striped
|
|
168
|
+
hover
|
|
169
|
+
bordered
|
|
170
|
+
/>
|
|
171
|
+
</template>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Custom Column Configuration
|
|
175
|
+
|
|
176
|
+
```vue
|
|
177
|
+
<script setup>
|
|
178
|
+
const columns = [
|
|
179
|
+
{
|
|
180
|
+
key: 'id',
|
|
181
|
+
label: 'ID',
|
|
182
|
+
sortable: true,
|
|
183
|
+
searchable: false,
|
|
184
|
+
headerClass: 'bg-primary text-white'
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
key: 'name',
|
|
188
|
+
label: 'Full Name',
|
|
189
|
+
sortable: true
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
key: 'salary',
|
|
193
|
+
label: 'Salary',
|
|
194
|
+
formatter: (value) => `$${value.toLocaleString()}`,
|
|
195
|
+
tdStyle: { textAlign: 'right' }
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
key: 'status',
|
|
199
|
+
label: 'Status',
|
|
200
|
+
class: 'text-center'
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
</script>
|
|
204
|
+
|
|
205
|
+
<template>
|
|
206
|
+
<VibeDataTable :columns="columns" :items="items" />
|
|
207
|
+
</template>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### With Custom Cell Rendering
|
|
211
|
+
|
|
212
|
+
```vue
|
|
213
|
+
<script setup>
|
|
214
|
+
const columns = [
|
|
215
|
+
{ key: 'name', label: 'Name' },
|
|
216
|
+
{ key: 'email', label: 'Email' },
|
|
217
|
+
{ key: 'status', label: 'Status' },
|
|
218
|
+
{ key: 'actions', label: 'Actions', sortable: false }
|
|
219
|
+
]
|
|
220
|
+
</script>
|
|
221
|
+
|
|
222
|
+
<template>
|
|
223
|
+
<VibeDataTable :columns="columns" :items="items">
|
|
224
|
+
<!-- Custom status column -->
|
|
225
|
+
<template #cell(status)="{ value }">
|
|
226
|
+
<VibeBadge :variant="value === 'Active' ? 'success' : 'danger'">
|
|
227
|
+
{{ value }}
|
|
228
|
+
</VibeBadge>
|
|
229
|
+
</template>
|
|
230
|
+
|
|
231
|
+
<!-- Custom actions column -->
|
|
232
|
+
<template #cell(actions)="{ item }">
|
|
233
|
+
<VibeButton size="sm" variant="primary" @click="editItem(item)">
|
|
234
|
+
Edit
|
|
235
|
+
</VibeButton>
|
|
236
|
+
<VibeButton size="sm" variant="danger" @click="deleteItem(item)">
|
|
237
|
+
Delete
|
|
238
|
+
</VibeButton>
|
|
239
|
+
</template>
|
|
240
|
+
</VibeDataTable>
|
|
241
|
+
</template>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Controlled State (v-model)
|
|
245
|
+
|
|
246
|
+
```vue
|
|
247
|
+
<script setup>
|
|
248
|
+
import { ref } from 'vue'
|
|
249
|
+
|
|
250
|
+
const currentPage = ref(1)
|
|
251
|
+
const perPage = ref(25)
|
|
252
|
+
const sortBy = ref('name')
|
|
253
|
+
const sortDesc = ref(false)
|
|
254
|
+
|
|
255
|
+
const handleRowClick = (item, globalIndex) => {
|
|
256
|
+
console.log('Clicked row:', item, globalIndex)
|
|
257
|
+
}
|
|
258
|
+
</script>
|
|
259
|
+
|
|
260
|
+
<template>
|
|
261
|
+
<VibeDataTable
|
|
262
|
+
:columns="columns"
|
|
263
|
+
:items="items"
|
|
264
|
+
row-key="id"
|
|
265
|
+
v-model:current-page="currentPage"
|
|
266
|
+
v-model:per-page="perPage"
|
|
267
|
+
v-model:sort-by="sortBy"
|
|
268
|
+
v-model:sort-desc="sortDesc"
|
|
269
|
+
@row-clicked="handleRowClick"
|
|
270
|
+
/>
|
|
271
|
+
</template>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Disable Features
|
|
275
|
+
|
|
276
|
+
```vue
|
|
277
|
+
<template>
|
|
278
|
+
<!-- No search, no pagination -->
|
|
279
|
+
<VibeDataTable
|
|
280
|
+
:columns="columns"
|
|
281
|
+
:items="items"
|
|
282
|
+
:searchable="false"
|
|
283
|
+
:paginated="false"
|
|
284
|
+
/>
|
|
285
|
+
|
|
286
|
+
<!-- No sorting -->
|
|
287
|
+
<VibeDataTable
|
|
288
|
+
:columns="columns"
|
|
289
|
+
:items="items"
|
|
290
|
+
:sortable="false"
|
|
291
|
+
/>
|
|
292
|
+
</template>
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Custom Page Sizes
|
|
296
|
+
|
|
297
|
+
```vue
|
|
298
|
+
<template>
|
|
299
|
+
<VibeDataTable
|
|
300
|
+
:columns="columns"
|
|
301
|
+
:items="items"
|
|
302
|
+
:per-page="20"
|
|
303
|
+
:per-page-options="[10, 20, 50, 100, 500]"
|
|
304
|
+
/>
|
|
305
|
+
</template>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Large Dataset Example
|
|
309
|
+
|
|
310
|
+
```vue
|
|
311
|
+
<script setup>
|
|
312
|
+
import { ref } from 'vue'
|
|
313
|
+
|
|
314
|
+
// Generate large dataset
|
|
315
|
+
const items = ref(
|
|
316
|
+
Array.from({ length: 1000 }, (_, i) => ({
|
|
317
|
+
id: i + 1,
|
|
318
|
+
name: `User ${i + 1}`,
|
|
319
|
+
email: `user${i + 1}@example.com`,
|
|
320
|
+
department: ['Sales', 'Marketing', 'Engineering', 'HR'][i % 4],
|
|
321
|
+
salary: Math.floor(Math.random() * 100000) + 50000,
|
|
322
|
+
joinDate: new Date(2020 + Math.floor(Math.random() * 5), Math.floor(Math.random() * 12), 1)
|
|
323
|
+
.toISOString()
|
|
324
|
+
.split('T')[0]
|
|
325
|
+
}))
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
const columns = [
|
|
329
|
+
{ key: 'id', label: 'ID', sortable: true },
|
|
330
|
+
{ key: 'name', label: 'Name', sortable: true },
|
|
331
|
+
{ key: 'email', label: 'Email', sortable: true },
|
|
332
|
+
{ key: 'department', label: 'Department', sortable: true },
|
|
333
|
+
{
|
|
334
|
+
key: 'salary',
|
|
335
|
+
label: 'Salary',
|
|
336
|
+
sortable: true,
|
|
337
|
+
formatter: (value) => `$${value.toLocaleString()}`
|
|
338
|
+
},
|
|
339
|
+
{ key: 'joinDate', label: 'Join Date', sortable: true }
|
|
340
|
+
]
|
|
341
|
+
</script>
|
|
342
|
+
|
|
343
|
+
<template>
|
|
344
|
+
<VibeDataTable
|
|
345
|
+
:columns="columns"
|
|
346
|
+
:items="items"
|
|
347
|
+
:per-page="25"
|
|
348
|
+
striped
|
|
349
|
+
hover
|
|
350
|
+
small
|
|
351
|
+
/>
|
|
352
|
+
</template>
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### With Custom Empty State
|
|
356
|
+
|
|
357
|
+
```vue
|
|
358
|
+
<template>
|
|
359
|
+
<VibeDataTable
|
|
360
|
+
:columns="columns"
|
|
361
|
+
:items="[]"
|
|
362
|
+
empty-text="No users found. Try adjusting your search."
|
|
363
|
+
/>
|
|
364
|
+
</template>
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Advanced Features
|
|
368
|
+
|
|
369
|
+
### Formatters
|
|
370
|
+
|
|
371
|
+
Use formatters to transform data before display:
|
|
372
|
+
|
|
373
|
+
```vue
|
|
374
|
+
<script setup>
|
|
375
|
+
const columns = [
|
|
376
|
+
{
|
|
377
|
+
key: 'price',
|
|
378
|
+
label: 'Price',
|
|
379
|
+
formatter: (value) => `$${value.toFixed(2)}`
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
key: 'date',
|
|
383
|
+
label: 'Date',
|
|
384
|
+
formatter: (value) => new Date(value).toLocaleDateString()
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
key: 'percentage',
|
|
388
|
+
label: 'Complete',
|
|
389
|
+
formatter: (value) => `${value}%`
|
|
390
|
+
}
|
|
391
|
+
]
|
|
392
|
+
</script>
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Accessing Full Row in Formatter
|
|
396
|
+
|
|
397
|
+
```vue
|
|
398
|
+
<script setup>
|
|
399
|
+
const columns = [
|
|
400
|
+
{
|
|
401
|
+
key: 'fullName',
|
|
402
|
+
label: 'Full Name',
|
|
403
|
+
formatter: (value, row) => `${row.firstName} ${row.lastName}`
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
key: 'discount',
|
|
407
|
+
label: 'Discount',
|
|
408
|
+
formatter: (value, row) => {
|
|
409
|
+
return row.isPremium ? `${value}% (Premium)` : `${value}%`
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
]
|
|
413
|
+
</script>
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Bootstrap CSS Classes
|
|
417
|
+
|
|
418
|
+
- `.table` - Base table
|
|
419
|
+
- `.table-striped` - Striped rows
|
|
420
|
+
- `.table-bordered` - Bordered table
|
|
421
|
+
- `.table-borderless` - Borderless table
|
|
422
|
+
- `.table-hover` - Hover effect
|
|
423
|
+
- `.table-sm` - Compact table
|
|
424
|
+
- `.table-{variant}` - Color variants
|
|
425
|
+
- `.table-responsive` - Responsive wrapper
|
|
426
|
+
- `.vibe-table-stack` - Mobile card view transformation
|
|
427
|
+
- `.pagination` - Pagination controls
|
|
428
|
+
- `.form-control` - Search input
|
|
429
|
+
- `.form-select` - Per-page selector
|
|
430
|
+
|
|
431
|
+
## Tips
|
|
432
|
+
|
|
433
|
+
1. **Large Datasets**: For datasets with 1000+ rows, consider server-side pagination
|
|
434
|
+
2. **Stable keys**: Set the `row-key` prop to a unique field in your data for correct behavior during sorting/filtering and best reactivity
|
|
435
|
+
3. **Search Debounce**: Adjust `searchDebounce` prop for performance with large datasets
|
|
436
|
+
4. **Custom Styling**: Use column `class`, `headerClass`, `thStyle`, `tdStyle` for styling (style objects are sanitized to a safe property allowlist)
|
|
437
|
+
5. **Slots**: Use slots for complex cell rendering (badges, buttons, images, etc.)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# VibeHero
|
|
2
|
+
|
|
3
|
+
A hero / banner section. Renders a `<section>` with consistent padding, an inner container, and content alignment — covering the Bootstrap 5.3 hero patterns (centered, two-column, dark, bordered) plus full-bleed background image / gradient heroes.
|
|
4
|
+
|
|
5
|
+
```vue
|
|
6
|
+
<VibeHero variant="dark" text-variant="light">
|
|
7
|
+
<h1 class="display-4 fw-bold">Build faster with VibeUI</h1>
|
|
8
|
+
<p class="lead">A modern Vue 3 component library for Bootstrap 5.3.</p>
|
|
9
|
+
<VibeButton variant="primary" size="lg">Get started</VibeButton>
|
|
10
|
+
</VibeHero>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Props
|
|
14
|
+
|
|
15
|
+
| Prop | Type | Default | Description |
|
|
16
|
+
|------|------|---------|-------------|
|
|
17
|
+
| `variant` | `Variant` | `undefined` | Background color → `bg-{variant}` (same vocabulary as `VibeCard`) |
|
|
18
|
+
| `textVariant` | `Variant` | `undefined` | Text color → `text-{variant}` |
|
|
19
|
+
| `border` | `Variant` | `undefined` | Adds `border border-{variant} rounded-3` (the bordered-hero pattern) |
|
|
20
|
+
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | Content alignment → `text-{align}` |
|
|
21
|
+
| `fluid` | `Boolean \| ContainerType` | `false` | Inner container width (same vocabulary as `VibeContainer`) |
|
|
22
|
+
| `minHeight` | `String` | `undefined` | Sanitized via `safeLength`; when set, content is vertically centered |
|
|
23
|
+
| `bgImage` | `String` | `undefined` | Full-bleed background image URL; sanitized via `safeHref` (blocks `javascript:`/`data:`), rendered `cover`/`center` |
|
|
24
|
+
| `overlay` | `Boolean \| String` | `false` | Darkening layer over `bgImage` for legibility — `true` for a default dark scrim, or a CSS color (sanitized via `safeColor`) |
|
|
25
|
+
| `overlayOpacity` | `Number` | `0.5` | Opacity of the default dark scrim when `overlay` is `true`; clamped to `[0, 1]`. Ignored when `overlay` is a CSS color string |
|
|
26
|
+
| `gradient` | `String` | `undefined` | CSS gradient background (e.g. `linear-gradient(135deg, #667eea, #764ba2)`); validated to `*-gradient(...)` only — no `url()`/`expression()` |
|
|
27
|
+
| `tag` | `Tag` | `'section'` | Root element tag |
|
|
28
|
+
|
|
29
|
+
## Slots
|
|
30
|
+
|
|
31
|
+
| Slot | Description |
|
|
32
|
+
|------|-------------|
|
|
33
|
+
| `default` | Hero content. You supply the inner layout (a centered column, or a `VibeRow`/`VibeCol` split). |
|
|
34
|
+
|
|
35
|
+
## Examples
|
|
36
|
+
|
|
37
|
+
**Centered (Bootstrap "centered hero")**
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<VibeHero>
|
|
41
|
+
<VibeCol :lg="7" class="mx-auto">
|
|
42
|
+
<h1 class="display-3 fw-bold">Photo Gallery</h1>
|
|
43
|
+
<p class="lead">Curated photography from around the world.</p>
|
|
44
|
+
</VibeCol>
|
|
45
|
+
</VibeHero>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Two-column with gradient**
|
|
49
|
+
|
|
50
|
+
```vue
|
|
51
|
+
<VibeHero gradient="linear-gradient(135deg, #667eea 0%, #764ba2 100%)" align="start" class="text-white">
|
|
52
|
+
<VibeRow class="align-items-center">
|
|
53
|
+
<VibeCol :lg="6"><h1 class="display-3 fw-bold">Headline</h1></VibeCol>
|
|
54
|
+
<VibeCol :lg="6"><img src="/demo.png" class="img-fluid rounded-4 shadow-lg"></VibeCol>
|
|
55
|
+
</VibeRow>
|
|
56
|
+
</VibeHero>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Full-bleed image with overlay**
|
|
60
|
+
|
|
61
|
+
```vue
|
|
62
|
+
<VibeHero bg-image="/banner.jpg" overlay min-height="400px" class="text-white">
|
|
63
|
+
<h1 class="display-3 fw-bold">Welcome</h1>
|
|
64
|
+
</VibeHero>
|
|
65
|
+
```
|