@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.
Files changed (184) hide show
  1. package/AGENTS.md +44 -0
  2. package/CLAUDE.md +48 -0
  3. package/README.md +222 -197
  4. package/dist/App.vue.d.ts +2 -1
  5. package/dist/components/HelloWorld.vue.d.ts +2 -1
  6. package/dist/components/VibeAccordion.vue.d.ts +39 -40
  7. package/dist/components/VibeAlert.vue.d.ts +21 -37
  8. package/dist/components/VibeAutocomplete.vue.d.ts +59 -31
  9. package/dist/components/VibeBadge.vue.d.ts +10 -18
  10. package/dist/components/VibeBreadcrumb.vue.d.ts +25 -21
  11. package/dist/components/VibeButton.vue.d.ts +13 -17
  12. package/dist/components/VibeButtonGroup.vue.d.ts +11 -19
  13. package/dist/components/VibeCard.vue.d.ts +51 -24
  14. package/dist/components/VibeCarousel.vue.d.ts +27 -49
  15. package/dist/components/VibeChartBar.vue.d.ts +61 -0
  16. package/dist/components/VibeChartLine.vue.d.ts +70 -0
  17. package/dist/components/VibeChartPie.vue.d.ts +34 -0
  18. package/dist/components/VibeCloseButton.vue.d.ts +9 -7
  19. package/dist/components/VibeCol.vue.d.ts +9 -17
  20. package/dist/components/VibeCollapse.vue.d.ts +24 -32
  21. package/dist/components/VibeContainer.vue.d.ts +9 -17
  22. package/dist/components/VibeDataTable.vue.d.ts +122 -63
  23. package/dist/components/VibeDatePicker.vue.d.ts +4 -6
  24. package/dist/components/VibeDraggable.vue.d.ts +9 -13
  25. package/dist/components/VibeDropdown.vue.d.ts +46 -43
  26. package/dist/components/VibeDroppable.vue.d.ts +9 -13
  27. package/dist/components/VibeFileInput.vue.d.ts +14 -20
  28. package/dist/components/VibeFormCheckbox.vue.d.ts +30 -22
  29. package/dist/components/VibeFormDatepicker.vue.d.ts +27 -30
  30. package/dist/components/VibeFormErrorSummary.vue.d.ts +17 -0
  31. package/dist/components/VibeFormGroup.vue.d.ts +10 -13
  32. package/dist/components/VibeFormInput.vue.d.ts +60 -25
  33. package/dist/components/VibeFormRadio.vue.d.ts +23 -24
  34. package/dist/components/VibeFormSelect.vue.d.ts +28 -34
  35. package/dist/components/VibeFormSpinbutton.vue.d.ts +31 -34
  36. package/dist/components/VibeFormSwitch.vue.d.ts +22 -23
  37. package/dist/components/VibeFormTextarea.vue.d.ts +24 -25
  38. package/dist/components/VibeFormWysiwyg.vue.d.ts +23 -22
  39. package/dist/components/VibeHero.vue.d.ts +117 -0
  40. package/dist/components/VibeIcon.vue.d.ts +27 -7
  41. package/dist/components/VibeInputGroup.vue.d.ts +12 -14
  42. package/dist/components/VibeLink.vue.d.ts +14 -18
  43. package/dist/components/VibeListGroup.vue.d.ts +26 -22
  44. package/dist/components/VibeModal.vue.d.ts +47 -45
  45. package/dist/components/VibeNav.vue.d.ts +40 -20
  46. package/dist/components/VibeNavbar.vue.d.ts +12 -19
  47. package/dist/components/VibeNavbarBrand.vue.d.ts +9 -17
  48. package/dist/components/VibeNavbarNav.vue.d.ts +52 -33
  49. package/dist/components/VibeNavbarToggle.vue.d.ts +7 -5
  50. package/dist/components/VibeOffcanvas.vue.d.ts +28 -43
  51. package/dist/components/VibePagination.vue.d.ts +38 -29
  52. package/dist/components/VibePlaceholder.vue.d.ts +9 -17
  53. package/dist/components/VibePopover.vue.d.ts +17 -40
  54. package/dist/components/VibeProgress.vue.d.ts +13 -21
  55. package/dist/components/VibeResizable.vue.d.ts +10 -14
  56. package/dist/components/VibeRow.vue.d.ts +9 -17
  57. package/dist/components/VibeScrollspy.vue.d.ts +37 -25
  58. package/dist/components/VibeSkeleton.vue.d.ts +2 -1
  59. package/dist/components/VibeSlider.vue.d.ts +4 -5
  60. package/dist/components/VibeSortable.vue.d.ts +37 -18
  61. package/dist/components/VibeSpinner.vue.d.ts +4 -7
  62. package/dist/components/VibeStepper.vue.d.ts +32 -31
  63. package/dist/components/VibeTab.vue.d.ts +7 -11
  64. package/dist/components/VibeTabContent.vue.d.ts +13 -31
  65. package/dist/components/VibeTabs.vue.d.ts +9 -13
  66. package/dist/components/VibeToast.vue.d.ts +31 -44
  67. package/dist/components/VibeToastHost.vue.d.ts +2 -1
  68. package/dist/components/VibeTooltip.vue.d.ts +16 -39
  69. package/dist/components/chart/chartColors.d.ts +3 -0
  70. package/dist/components/chart/chartResize.d.ts +2 -0
  71. package/dist/components/chart/chartTooltip.d.ts +2 -0
  72. package/dist/components/chart/chartTypes.d.ts +6 -0
  73. package/dist/components/chart/drawBar.d.ts +5 -0
  74. package/dist/components/chart/drawLine.d.ts +18 -0
  75. package/dist/components/chart/drawPie.d.ts +4 -0
  76. package/dist/components/dndStore.d.ts +1 -0
  77. package/dist/components/index.d.ts +6 -1
  78. package/dist/composables/useBreakpoints.d.ts +1 -0
  79. package/dist/composables/useColorMode.d.ts +1 -1
  80. package/dist/composables/useId.d.ts +1 -4
  81. package/dist/directives/vTooltip.d.ts +2 -3
  82. package/dist/injectionKeys.d.ts +34 -0
  83. package/dist/purify.es-eYghtX9j.mjs +735 -0
  84. package/dist/types.d.ts +23 -2
  85. package/dist/utils/routeKey.d.ts +11 -0
  86. package/dist/utils/safeCss.d.ts +16 -0
  87. package/dist/utils/safeHref.d.ts +7 -0
  88. package/dist/utils/sanitizeHtml.d.ts +13 -0
  89. package/dist/vibeui.css +2 -1
  90. package/dist/vibeui.es.js +8527 -5855
  91. package/dist/vibeui.umd.js +3 -1
  92. package/docs/README.md +225 -0
  93. package/docs/components/advanced/popover.md +125 -0
  94. package/docs/components/advanced/scrollspy.md +106 -0
  95. package/docs/components/advanced/tooltip.md +90 -0
  96. package/docs/components/card/card.md +223 -0
  97. package/docs/components/charts/chart-bar.md +129 -0
  98. package/docs/components/charts/chart-line.md +136 -0
  99. package/docs/components/charts/chart-pie.md +102 -0
  100. package/docs/components/core/alert.md +94 -0
  101. package/docs/components/core/badge.md +75 -0
  102. package/docs/components/core/button-group.md +99 -0
  103. package/docs/components/core/button.md +145 -0
  104. package/docs/components/core/close-button.md +82 -0
  105. package/docs/components/core/link.md +79 -0
  106. package/docs/components/core/placeholder.md +129 -0
  107. package/docs/components/core/skeleton.md +40 -0
  108. package/docs/components/core/spinner.md +103 -0
  109. package/docs/components/data/datatable.md +437 -0
  110. package/docs/components/hero/hero.md +65 -0
  111. package/docs/components/interactive/accordion.md +121 -0
  112. package/docs/components/interactive/carousel.md +112 -0
  113. package/docs/components/interactive/collapse.md +106 -0
  114. package/docs/components/interactive/date-picker.md +78 -0
  115. package/docs/components/interactive/draggable.md +91 -0
  116. package/docs/components/interactive/dropdown.md +129 -0
  117. package/docs/components/interactive/modal.md +151 -0
  118. package/docs/components/interactive/offcanvas.md +92 -0
  119. package/docs/components/interactive/resizable.md +73 -0
  120. package/docs/components/interactive/slider.md +57 -0
  121. package/docs/components/interactive/sortable.md +52 -0
  122. package/docs/components/interactive/stepper.md +83 -0
  123. package/docs/components/interactive/tabs.md +66 -0
  124. package/docs/components/interactive/toast.md +177 -0
  125. package/docs/components/layout/col.md +117 -0
  126. package/docs/components/layout/container.md +53 -0
  127. package/docs/components/layout/row.md +107 -0
  128. package/docs/components/list/list-group.md +225 -0
  129. package/docs/components/navigation/breadcrumb.md +120 -0
  130. package/docs/components/navigation/nav.md +151 -0
  131. package/docs/components/navigation/navbar.md +114 -0
  132. package/docs/components/navigation/pagination.md +163 -0
  133. package/docs/components/progress/progress.md +188 -0
  134. package/docs/composables/back-button.md +28 -0
  135. package/docs/composables/breakpoints.md +54 -0
  136. package/docs/composables/color-mode.md +179 -0
  137. package/docs/composables/use-form.md +78 -0
  138. package/docs/composables/use-position.md +68 -0
  139. package/docs/composables/use-toast.md +91 -0
  140. package/docs/directives/v-tooltip.md +58 -0
  141. package/docs/forms/README.md +87 -0
  142. package/docs/forms/autocomplete.md +96 -0
  143. package/docs/forms/file-input.md +97 -0
  144. package/docs/forms/form-checkbox.md +100 -0
  145. package/docs/forms/form-datepicker.md +80 -0
  146. package/docs/forms/form-error-summary.md +96 -0
  147. package/docs/forms/form-group.md +118 -0
  148. package/docs/forms/form-input.md +140 -0
  149. package/docs/forms/form-radio.md +82 -0
  150. package/docs/forms/form-select.md +98 -0
  151. package/docs/forms/form-spinbutton.md +94 -0
  152. package/docs/forms/form-switch.md +75 -0
  153. package/docs/forms/form-textarea.md +82 -0
  154. package/docs/forms/form-wysiwyg.md +99 -0
  155. package/docs/forms/input-group.md +70 -0
  156. package/docs/forms/validation.md +213 -0
  157. package/docs/getting-started/starter-template.md +136 -0
  158. package/docs/superpowers/plans/2026-05-12-vibe-charts.md +1999 -0
  159. package/docs/superpowers/plans/2026-05-29-e2e-browser-testing.md +615 -0
  160. package/docs/superpowers/specs/2026-05-12-charting-design.md +185 -0
  161. package/docs/superpowers/specs/2026-05-29-e2e-browser-testing-design.md +168 -0
  162. package/docs/utilities/position.md +82 -0
  163. package/docs/versioning.md +57 -0
  164. package/examples/README.md +201 -0
  165. package/examples/album.html +262 -0
  166. package/examples/blog.html +166 -0
  167. package/examples/carousel.html +114 -0
  168. package/examples/checkout.html +275 -0
  169. package/examples/cover.html +89 -0
  170. package/examples/dashboard.html +156 -0
  171. package/examples/index.html +129 -0
  172. package/examples/jumbotron.html +56 -0
  173. package/examples/mobile-dashboard.html +138 -0
  174. package/examples/pricing.html +172 -0
  175. package/examples/product.html +145 -0
  176. package/examples/sidebars.html +166 -0
  177. package/examples/sign-in.html +119 -0
  178. package/examples/starter.html +314 -0
  179. package/examples/sticky-footer-navbar.html +70 -0
  180. package/examples/sticky-footer.html +60 -0
  181. package/examples/test-simple.html +91 -0
  182. package/llms.txt +801 -0
  183. package/package.json +44 -14
  184. 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
+ ```