@velkymx/vibeui 0.8.2 → 1.0.2

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 (200) hide show
  1. package/AGENTS.md +44 -0
  2. package/README.md +245 -58
  3. package/dist/purify.es-1D-VntFD.mjs +679 -0
  4. package/dist/src/App.vue.d.ts +3 -0
  5. package/dist/src/components/HelloWorld.vue.d.ts +6 -0
  6. package/dist/src/components/VibeAccordion.vue.d.ts +89 -0
  7. package/dist/{components → src/components}/VibeAlert.vue.d.ts +24 -30
  8. package/dist/src/components/VibeAutocomplete.vue.d.ts +74 -0
  9. package/dist/{components → src/components}/VibeBadge.vue.d.ts +10 -18
  10. package/dist/src/components/VibeBreadcrumb.vue.d.ts +51 -0
  11. package/dist/{components → src/components}/VibeButton.vue.d.ts +16 -20
  12. package/dist/{components → src/components}/VibeButtonGroup.vue.d.ts +10 -18
  13. package/dist/{components → src/components}/VibeCard.vue.d.ts +51 -24
  14. package/dist/{components → src/components}/VibeCarousel.vue.d.ts +27 -49
  15. package/dist/src/components/VibeChartBar.vue.d.ts +61 -0
  16. package/dist/src/components/VibeChartLine.vue.d.ts +70 -0
  17. package/dist/src/components/VibeChartPie.vue.d.ts +34 -0
  18. package/dist/{components → src/components}/VibeCloseButton.vue.d.ts +9 -7
  19. package/dist/{components → src/components}/VibeCol.vue.d.ts +9 -17
  20. package/dist/src/components/VibeCollapse.vue.d.ts +76 -0
  21. package/dist/src/components/VibeContainer.vue.d.ts +36 -0
  22. package/dist/src/components/VibeDataTable.vue.d.ts +131 -0
  23. package/dist/src/components/VibeDatePicker.vue.d.ts +120 -0
  24. package/dist/src/components/VibeDraggable.vue.d.ts +77 -0
  25. package/dist/{components → src/components}/VibeDropdown.vue.d.ts +46 -43
  26. package/dist/src/components/VibeDroppable.vue.d.ts +70 -0
  27. package/dist/src/components/VibeFileInput.vue.d.ts +127 -0
  28. package/dist/{components → src/components}/VibeFormCheckbox.vue.d.ts +30 -22
  29. package/dist/{components → src/components}/VibeFormDatepicker.vue.d.ts +27 -30
  30. package/dist/{components → src/components}/VibeFormGroup.vue.d.ts +11 -14
  31. package/dist/{components → src/components}/VibeFormInput.vue.d.ts +22 -23
  32. package/dist/{components → src/components}/VibeFormRadio.vue.d.ts +23 -24
  33. package/dist/{components → src/components}/VibeFormSelect.vue.d.ts +28 -34
  34. package/dist/{components → src/components}/VibeFormSpinbutton.vue.d.ts +31 -34
  35. package/dist/{components → src/components}/VibeFormSwitch.vue.d.ts +22 -23
  36. package/dist/{components → src/components}/VibeFormTextarea.vue.d.ts +24 -25
  37. package/dist/{components → src/components}/VibeFormWysiwyg.vue.d.ts +24 -23
  38. package/dist/src/components/VibeHero.vue.d.ts +108 -0
  39. package/dist/{components → src/components}/VibeIcon.vue.d.ts +27 -7
  40. package/dist/{components → src/components}/VibeInputGroup.vue.d.ts +11 -13
  41. package/dist/{components → src/components}/VibeLink.vue.d.ts +14 -18
  42. package/dist/{components → src/components}/VibeListGroup.vue.d.ts +26 -22
  43. package/dist/{components → src/components}/VibeModal.vue.d.ts +29 -45
  44. package/dist/{components → src/components}/VibeNav.vue.d.ts +40 -20
  45. package/dist/{components → src/components}/VibeNavbar.vue.d.ts +12 -19
  46. package/dist/src/components/VibeNavbarBrand.vue.d.ts +34 -0
  47. package/dist/src/components/VibeNavbarNav.vue.d.ts +80 -0
  48. package/dist/{components → src/components}/VibeNavbarToggle.vue.d.ts +7 -5
  49. package/dist/{components → src/components}/VibeOffcanvas.vue.d.ts +28 -43
  50. package/dist/{components → src/components}/VibePagination.vue.d.ts +38 -29
  51. package/dist/{components → src/components}/VibePlaceholder.vue.d.ts +9 -17
  52. package/dist/src/components/VibePopover.vue.d.ts +72 -0
  53. package/dist/src/components/VibeProgress.vue.d.ts +37 -0
  54. package/dist/src/components/VibeResizable.vue.d.ts +140 -0
  55. package/dist/{components → src/components}/VibeRow.vue.d.ts +9 -17
  56. package/dist/src/components/VibeScrollspy.vue.d.ts +89 -0
  57. package/dist/src/components/VibeSkeleton.vue.d.ts +54 -0
  58. package/dist/src/components/VibeSlider.vue.d.ts +77 -0
  59. package/dist/src/components/VibeSortable.vue.d.ts +59 -0
  60. package/dist/{components → src/components}/VibeSpinner.vue.d.ts +4 -7
  61. package/dist/src/components/VibeStepper.vue.d.ts +133 -0
  62. package/dist/src/components/VibeTab.vue.d.ts +41 -0
  63. package/dist/src/components/VibeTabContent.vue.d.ts +56 -0
  64. package/dist/src/components/VibeTabs.vue.d.ts +78 -0
  65. package/dist/{components → src/components}/VibeToast.vue.d.ts +40 -42
  66. package/dist/src/components/VibeToastHost.vue.d.ts +16 -0
  67. package/dist/src/components/VibeTooltip.vue.d.ts +63 -0
  68. package/dist/src/components/chart/chartColors.d.ts +3 -0
  69. package/dist/src/components/chart/chartResize.d.ts +2 -0
  70. package/dist/src/components/chart/chartTooltip.d.ts +2 -0
  71. package/dist/src/components/chart/chartTypes.d.ts +6 -0
  72. package/dist/src/components/chart/drawBar.d.ts +5 -0
  73. package/dist/src/components/chart/drawLine.d.ts +18 -0
  74. package/dist/src/components/chart/drawPie.d.ts +4 -0
  75. package/dist/src/components/dndStore.d.ts +7 -0
  76. package/dist/{components → src/components}/index.d.ts +18 -1
  77. package/dist/{composables → src/composables}/useBreakpoints.d.ts +1 -0
  78. package/dist/{composables → src/composables}/useColorMode.d.ts +1 -1
  79. package/dist/src/composables/useForm.d.ts +22 -0
  80. package/dist/src/composables/useId.d.ts +2 -0
  81. package/dist/src/composables/usePosition.d.ts +20 -0
  82. package/dist/src/composables/useToast.d.ts +52 -0
  83. package/dist/src/directives/vTooltip.d.ts +24 -0
  84. package/dist/{index.d.ts → src/index.d.ts} +6 -0
  85. package/dist/src/injectionKeys.d.ts +28 -0
  86. package/dist/{types.d.ts → src/types.d.ts} +32 -6
  87. package/dist/src/utils/safeCss.d.ts +16 -0
  88. package/dist/src/utils/safeHref.d.ts +7 -0
  89. package/dist/src/utils/sanitizeHtml.d.ts +13 -0
  90. package/dist/vibeui.css +2 -1
  91. package/dist/vibeui.es.js +8351 -3508
  92. package/dist/vibeui.umd.js +3 -1
  93. package/docs/README.md +85 -13
  94. package/docs/components/advanced/popover.md +27 -52
  95. package/docs/components/advanced/scrollspy.md +57 -15
  96. package/docs/components/advanced/tooltip.md +26 -47
  97. package/docs/components/card/card.md +9 -1
  98. package/docs/components/charts/chart-bar.md +129 -0
  99. package/docs/components/charts/chart-line.md +136 -0
  100. package/docs/components/charts/chart-pie.md +102 -0
  101. package/docs/components/core/alert.md +27 -5
  102. package/docs/components/core/badge.md +0 -6
  103. package/docs/components/core/button-group.md +0 -6
  104. package/docs/components/core/button.md +1 -1
  105. package/docs/components/core/close-button.md +5 -5
  106. package/docs/components/core/link.md +60 -17
  107. package/docs/components/core/placeholder.md +0 -6
  108. package/docs/components/core/skeleton.md +40 -0
  109. package/docs/components/core/spinner.md +0 -6
  110. package/docs/components/data/datatable.md +29 -10
  111. package/docs/components/hero/hero.md +64 -0
  112. package/docs/components/interactive/accordion.md +29 -0
  113. package/docs/components/interactive/carousel.md +19 -4
  114. package/docs/components/interactive/collapse.md +3 -2
  115. package/docs/components/interactive/date-picker.md +78 -0
  116. package/docs/components/interactive/draggable.md +91 -0
  117. package/docs/components/interactive/dropdown.md +54 -18
  118. package/docs/components/interactive/modal.md +15 -20
  119. package/docs/components/interactive/offcanvas.md +4 -1
  120. package/docs/components/interactive/resizable.md +73 -0
  121. package/docs/components/interactive/slider.md +57 -0
  122. package/docs/components/interactive/sortable.md +52 -0
  123. package/docs/components/interactive/stepper.md +83 -0
  124. package/docs/components/interactive/tabs.md +66 -0
  125. package/docs/components/interactive/toast.md +93 -30
  126. package/docs/components/layout/col.md +0 -6
  127. package/docs/components/layout/container.md +0 -6
  128. package/docs/components/layout/row.md +0 -6
  129. package/docs/components/list/list-group.md +4 -0
  130. package/docs/components/navigation/breadcrumb.md +4 -0
  131. package/docs/components/navigation/nav.md +64 -1
  132. package/docs/components/navigation/navbar.md +2 -0
  133. package/docs/components/navigation/pagination.md +16 -0
  134. package/docs/components/progress/progress.md +7 -1
  135. package/docs/composables/color-mode.md +44 -6
  136. package/docs/composables/use-form.md +78 -0
  137. package/docs/composables/use-position.md +68 -0
  138. package/docs/composables/use-toast.md +91 -0
  139. package/docs/directives/v-tooltip.md +58 -0
  140. package/docs/forms/README.md +60 -61
  141. package/docs/forms/autocomplete.md +96 -0
  142. package/docs/forms/file-input.md +97 -0
  143. package/docs/forms/form-checkbox.md +75 -25
  144. package/docs/forms/form-datepicker.md +58 -28
  145. package/docs/forms/form-group.md +79 -44
  146. package/docs/forms/form-input.md +88 -31
  147. package/docs/forms/form-radio.md +60 -36
  148. package/docs/forms/form-select.md +75 -31
  149. package/docs/forms/form-spinbutton.md +72 -33
  150. package/docs/forms/form-switch.md +53 -25
  151. package/docs/forms/form-textarea.md +60 -29
  152. package/docs/forms/form-wysiwyg.md +73 -38
  153. package/docs/forms/input-group.md +53 -34
  154. package/docs/forms/validation.md +135 -521
  155. package/docs/getting-started/starter-template.md +136 -0
  156. package/docs/superpowers/plans/2026-05-12-vibe-charts.md +1999 -0
  157. package/docs/superpowers/plans/2026-05-29-e2e-browser-testing.md +615 -0
  158. package/docs/superpowers/specs/2026-05-12-charting-design.md +185 -0
  159. package/docs/superpowers/specs/2026-05-29-e2e-browser-testing-design.md +168 -0
  160. package/docs/utilities/position.md +82 -0
  161. package/docs/versioning.md +57 -0
  162. package/examples/README.md +201 -0
  163. package/examples/album.html +262 -0
  164. package/examples/blog.html +166 -0
  165. package/examples/carousel.html +114 -0
  166. package/examples/checkout.html +275 -0
  167. package/examples/cover.html +89 -0
  168. package/examples/dashboard.html +156 -0
  169. package/examples/index.html +129 -0
  170. package/examples/jumbotron.html +56 -0
  171. package/examples/mobile-dashboard.html +138 -0
  172. package/examples/pricing.html +172 -0
  173. package/examples/product.html +145 -0
  174. package/examples/sidebars.html +166 -0
  175. package/examples/sign-in.html +119 -0
  176. package/examples/starter.html +314 -0
  177. package/examples/sticky-footer-navbar.html +70 -0
  178. package/examples/sticky-footer.html +60 -0
  179. package/examples/test-simple.html +91 -0
  180. package/llms.txt +407 -47
  181. package/package.json +42 -13
  182. package/dist/App.vue.d.ts +0 -2
  183. package/dist/components/HelloWorld.vue.d.ts +0 -5
  184. package/dist/components/VibeAccordion.vue.d.ts +0 -90
  185. package/dist/components/VibeBreadcrumb.vue.d.ts +0 -47
  186. package/dist/components/VibeCollapse.vue.d.ts +0 -84
  187. package/dist/components/VibeContainer.vue.d.ts +0 -44
  188. package/dist/components/VibeDataTable.vue.d.ts +0 -76
  189. package/dist/components/VibeNavbarBrand.vue.d.ts +0 -42
  190. package/dist/components/VibeNavbarNav.vue.d.ts +0 -61
  191. package/dist/components/VibePopover.vue.d.ts +0 -95
  192. package/dist/components/VibeProgress.vue.d.ts +0 -45
  193. package/dist/components/VibeScrollspy.vue.d.ts +0 -77
  194. package/dist/components/VibeTabContent.vue.d.ts +0 -74
  195. package/dist/components/VibeTooltip.vue.d.ts +0 -86
  196. package/dist/composables/useId.d.ts +0 -5
  197. package/dist/types/index.d.ts +0 -6
  198. /package/dist/{composables → src/composables}/useBackButton.d.ts +0 -0
  199. /package/dist/{composables → src/composables}/useFormValidation.d.ts +0 -0
  200. /package/dist/{main.d.ts → src/main.d.ts} +0 -0
@@ -0,0 +1,73 @@
1
+ # VibeResizable
2
+
3
+ Wrap a child element with up to 8 directional handles for user-driven resizing. Useful for dashboard panels, image crops, draggable widgets.
4
+
5
+ ## Props
6
+
7
+ | Prop | Type | Default | Description |
8
+ |------|------|---------|-------------|
9
+ | `handles` | `Handle[]` | `['se']` | Subset of `'n' \| 's' \| 'e' \| 'w' \| 'ne' \| 'nw' \| 'se' \| 'sw'` |
10
+ | `width` | `Number` | `200` | Initial width in px (use `update:width` v-model) |
11
+ | `height` | `Number` | `150` | Initial height in px (use `update:height` v-model) |
12
+ | `minWidth` | `Number` | `20` | |
13
+ | `maxWidth` | `Number` | `Infinity` | |
14
+ | `minHeight` | `Number` | `20` | |
15
+ | `maxHeight` | `Number` | `Infinity` | |
16
+ | `aspectRatio` | `Number` | `undefined` | Lock width/height ratio (e.g. `16/9`) |
17
+ | `grid` | `Number` | `1` | Snap step in px |
18
+ | `disabled` | `Boolean` | `false` | Hide handles, ignore drags |
19
+
20
+ ### Events
21
+
22
+ | Event | Payload | Description |
23
+ |-------|---------|-------------|
24
+ | `update:width` | `Number` | New width (use with `v-model:width`) |
25
+ | `update:height` | `Number` | New height |
26
+ | `resizestart` | `{ width, height }` | Drag began |
27
+ | `resize` | `{ width, height, handle }` | Continuous during drag |
28
+ | `resizeend` | `{ width, height }` | Drag committed |
29
+
30
+ ## Examples
31
+
32
+ ### Resizable card
33
+
34
+ ```vue
35
+ <script setup>
36
+ import { ref } from 'vue'
37
+ const w = ref(320)
38
+ const h = ref(200)
39
+ </script>
40
+
41
+ <template>
42
+ <VibeResizable
43
+ v-model:width="w"
44
+ v-model:height="h"
45
+ :handles="['se', 's', 'e']"
46
+ :grid="10"
47
+ :min-width="160"
48
+ >
49
+ <div class="card h-100">
50
+ <div class="card-body">
51
+ Content reflows: {{ w }}×{{ h }}
52
+ </div>
53
+ </div>
54
+ </VibeResizable>
55
+ </template>
56
+ ```
57
+
58
+ ## Layout caveat
59
+
60
+ Handles are positioned just outside the box (negative top/right/bottom/left) so they sit *on* the edge for easier grabbing. If the resizable lives inside a parent with `overflow: hidden`, or sits at the viewport edge, the handles may clip. Either lift `overflow` from the parent or wrap the resizable in extra padding.
61
+
62
+ ### Aspect-locked image preview
63
+
64
+ ```vue
65
+ <VibeResizable
66
+ v-model:width="w"
67
+ v-model:height="h"
68
+ :aspect-ratio="16/9"
69
+ :handles="['nw', 'ne', 'sw', 'se']"
70
+ >
71
+ <img :src="src" class="w-100 h-100" />
72
+ </VibeResizable>
73
+ ```
@@ -0,0 +1,57 @@
1
+ # VibeSlider
2
+
3
+ Cross-browser range slider with consistent look and full keyboard support. Single mode binds a number; range mode binds `[low, high]` with handles that cannot cross.
4
+
5
+ ## Props
6
+
7
+ | Prop | Type | Default | Description |
8
+ |------|------|---------|-------------|
9
+ | `modelValue` | `Number \| [Number, Number]` | `0` | Slider value (v-model) |
10
+ | `min` | `Number` | `0` | |
11
+ | `max` | `Number` | `100` | |
12
+ | `step` | `Number` | `1` | Snap step |
13
+ | `range` | `Boolean` | `false` | Two-handle range mode |
14
+ | `vertical` | `Boolean` | `false` | Vertical orientation |
15
+ | `disabled` | `Boolean` | `false` | |
16
+
17
+ ### Events
18
+
19
+ | Event | Payload | Description |
20
+ |-------|---------|-------------|
21
+ | `update:modelValue` | `Number \| [Number, Number]` | New value |
22
+ | `change` | Same | Alias |
23
+
24
+ ## Keyboard
25
+
26
+ | Key | Action |
27
+ |-----|--------|
28
+ | `ArrowRight` / `ArrowUp` | +1 step |
29
+ | `ArrowLeft` / `ArrowDown` | –1 step |
30
+ | `Home` | Jump to `min` |
31
+ | `End` | Jump to `max` |
32
+ | `PageUp` | +10 steps |
33
+ | `PageDown` | –10 steps |
34
+
35
+ ## Examples
36
+
37
+ ### Single value
38
+
39
+ ```vue
40
+ <VibeSlider v-model="volume" :min="0" :max="100" :step="5" />
41
+ ```
42
+
43
+ ### Range
44
+
45
+ ```vue
46
+ <VibeSlider v-model="priceRange" range :min="0" :max="500" />
47
+ ```
48
+
49
+ ### Vertical
50
+
51
+ ```vue
52
+ <VibeSlider v-model="value" vertical />
53
+ ```
54
+
55
+ ## Accessibility
56
+
57
+ Each handle has `role="slider"`, `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, and `tabindex="0"`. Disabled state sets `aria-disabled="true"`.
@@ -0,0 +1,52 @@
1
+ # VibeSortable
2
+
3
+ Drag-and-drop reorderable list. Generic over the row type; binds an ordered array via `v-model`.
4
+
5
+ ## Props
6
+
7
+ | Prop | Type | Default | Description |
8
+ |------|------|---------|-------------|
9
+ | `modelValue` | `T[]` | required | Ordered array (v-model) |
10
+ | `itemKey` | `String` | `undefined` | Property name on each item to use as the `:key`. When omitted, index is used as key. Set this when items have a stable unique identifier to avoid key conflicts during reorder. |
11
+ | `disabled` | `Boolean` | `false` | Disable dragging |
12
+ | `tag` | `String` | `'div'` | Container element |
13
+ | `itemTag` | `String` | `'div'` | Element wrapping each item |
14
+
15
+ ### Events
16
+
17
+ | Event | Payload | Description |
18
+ |-------|---------|-------------|
19
+ | `update:modelValue` | `T[]` | New ordered array after a successful drop |
20
+ | `reorder` | `{ from, to, item }` | Convenience event for persisting moves |
21
+
22
+ ### Slots
23
+
24
+ The default slot is scoped per item with `{ item, index }`.
25
+
26
+ ## Examples
27
+
28
+ ```vue
29
+ <script setup>
30
+ import { ref } from 'vue'
31
+ const tasks = ref(['Wireframes', 'Backend', 'QA', 'Launch'])
32
+ </script>
33
+
34
+ <template>
35
+ <VibeSortable v-model="tasks">
36
+ <template #default="{ item, index }">
37
+ <div class="card p-2 mb-2">
38
+ {{ index + 1 }}. {{ item }}
39
+ </div>
40
+ </template>
41
+ </VibeSortable>
42
+ </template>
43
+ ```
44
+
45
+ Items get `data-vibe-sortable-item` for testability and styling. The currently dragging item gets a `vibe-sortable-dragging` class.
46
+
47
+ ## When to use VibeSortable vs VibeDraggable
48
+
49
+ - **VibeSortable**: a single ordered list whose only operation is reorder.
50
+ - **VibeDraggable + VibeDroppable**: cross-list moves, kanban boards, builders, anywhere the source and target are different containers.
51
+
52
+ > **Scope:** VibeSortable handles in-list reorder only. Cross-list moves between two `<VibeSortable>` instances are not supported — use `VibeDraggable` + `VibeDroppable` for that.
@@ -0,0 +1,83 @@
1
+ # VibeStepper
2
+
3
+ Multi-step wizard with linear / non-linear modes and per-step validation guards. Use for sprint setup, initiative gates, onboarding flows.
4
+
5
+ ## Props
6
+
7
+ | Prop | Type | Default | Description |
8
+ |------|------|---------|-------------|
9
+ | `modelValue` | `Number` | `0` | Active step index (v-model) |
10
+ | `steps` | `StepperStep[]` | required | Step descriptors (see below) |
11
+ | `linear` | `Boolean` | `true` | Forbid clicking ahead of current step |
12
+ | `vertical` | `Boolean` | `false` | Vertical layout |
13
+ | `beforeNext` | `(currentIndex, 'next') => boolean \| Promise<boolean>` | `undefined` | Guard for advancing |
14
+ | `beforePrev` | `(currentIndex, 'prev') => boolean \| Promise<boolean>` | `undefined` | Guard for going back |
15
+ | `nextText` | `String` | `'Next'` | Default action button label |
16
+ | `prevText` | `String` | `'Back'` | |
17
+ | `finishText` | `String` | `'Finish'` | Replaces `nextText` on the last step |
18
+
19
+ ### Step descriptor
20
+
21
+ ```ts
22
+ interface StepperStep {
23
+ label: string
24
+ description?: string // small caption under the label
25
+ disabled?: boolean
26
+ icon?: string // reserved; use marker slot for now
27
+ }
28
+ ```
29
+
30
+ ### Events
31
+
32
+ | Event | Payload | Description |
33
+ |-------|---------|-------------|
34
+ | `update:modelValue` | `Number` | Active step changed |
35
+ | `finish` | — | Emitted when next is clicked on the last step |
36
+ | `component-error` | `unknown` | Emitted if a `beforeNext` / `beforePrev` guard throws |
37
+
38
+ ### Slots
39
+
40
+ | Slot | Scope | Description |
41
+ |------|-------|-------------|
42
+ | `step` | `{ index, step }` | Body for the active step |
43
+ | `marker` | `{ index, step, active }` | Override the step's marker (default: number) |
44
+ | `label` | `{ index, step }` | Override the step header label |
45
+ | `actions` | `{ next, prev, isFirst, isLast }` | Replace default footer buttons |
46
+
47
+ ## Examples
48
+
49
+ ```vue
50
+ <script setup>
51
+ import { ref } from 'vue'
52
+ const active = ref(0)
53
+ const steps = [
54
+ { label: 'Account', description: 'Basics' },
55
+ { label: 'Profile' },
56
+ { label: 'Confirm' }
57
+ ]
58
+
59
+ async function validateStep(index) {
60
+ // call server, validate fields...
61
+ return true
62
+ }
63
+ </script>
64
+
65
+ <template>
66
+ <VibeStepper
67
+ v-model="active"
68
+ :steps="steps"
69
+ :before-next="validateStep"
70
+ @finish="submit"
71
+ >
72
+ <template #step="{ index }">
73
+ <component :is="forms[index]" />
74
+ </template>
75
+ </VibeStepper>
76
+ </template>
77
+ ```
78
+
79
+ ### Non-linear (free navigation)
80
+
81
+ ```vue
82
+ <VibeStepper :steps="steps" v-model="active" :linear="false" />
83
+ ```
@@ -0,0 +1,66 @@
1
+ # VibeTabs / VibeTab
2
+
3
+ Self-contained tab container with content switching. Distinct from `VibeNav` (which is a pure `.nav` rendering primitive) — `VibeTabs` manages active state and renders tab panes.
4
+
5
+ ## VibeTabs Props
6
+
7
+ | Prop | Type | Default | Description |
8
+ |------|------|---------|-------------|
9
+ | `modelValue` | `String` | `undefined` | Active tab `name` (v-model). Auto-defaults to first non-disabled tab. |
10
+ | `variant` | `'tabs' \| 'pills' \| 'underline'` | `'tabs'` | Bootstrap nav style |
11
+ | `fill` | `Boolean` | `false` | Apply `.nav-fill` |
12
+ | `justified` | `Boolean` | `false` | Apply `.nav-justified` |
13
+ | `vertical` | `Boolean` | `false` | Render nav vertically alongside content |
14
+ | `lazy` | `Boolean` | `false` | Defer rendering of inactive tabs until first activation |
15
+
16
+ ### Events
17
+
18
+ | Event | Payload | Description |
19
+ |-------|---------|-------------|
20
+ | `update:modelValue` | `String` | Emitted when active tab changes |
21
+ | `change` | `String` | Same payload, alias for non-v-model usage |
22
+
23
+ ## VibeTab Props
24
+
25
+ | Prop | Type | Default | Description |
26
+ |------|------|---------|-------------|
27
+ | `name` | `String` | required | Stable identifier matching `modelValue` |
28
+ | `label` | `String` | required | Tab nav button text |
29
+ | `disabled` | `Boolean` | `false` | Skip activation; appears greyed out |
30
+
31
+ The default slot is the tab body.
32
+
33
+ ## Examples
34
+
35
+ ```vue
36
+ <script setup>
37
+ import { ref } from 'vue'
38
+ const active = ref('details')
39
+ </script>
40
+
41
+ <template>
42
+ <VibeTabs v-model="active" variant="pills">
43
+ <VibeTab name="details" label="Details">
44
+ <p>Issue body, status, assignee...</p>
45
+ </VibeTab>
46
+ <VibeTab name="activity" label="Activity">
47
+ <ActivityTimeline />
48
+ </VibeTab>
49
+ <VibeTab name="links" label="Linked Issues" disabled>
50
+ <p>Link feature coming soon.</p>
51
+ </VibeTab>
52
+ </VibeTabs>
53
+ </template>
54
+ ```
55
+
56
+ ### Lazy mounting for heavy panes
57
+
58
+ ```vue
59
+ <VibeTabs v-model="active" lazy>
60
+ <VibeTab name="metrics" label="Metrics">
61
+ <ExpensiveChart />
62
+ </VibeTab>
63
+ </VibeTabs>
64
+ ```
65
+
66
+ `<ExpensiveChart>` is not mounted until the user clicks the Metrics tab. Once activated, it stays mounted across switches so internal state isn't lost.
@@ -2,7 +2,68 @@
2
2
 
3
3
  Push notifications for lightweight alerts.
4
4
 
5
- ## Props
5
+ The recommended way to show toasts is the **`useToast()` service** with a single `<VibeToastHost />` mounted once near your app root. The `<VibeToast>` component is the lower-level primitive for when you need a toast bound directly to local component state.
6
+
7
+ ## Toast Service (recommended)
8
+
9
+ Mount `<VibeToastHost />` once (typically in `App.vue`), then call `useToast()` anywhere to enqueue toasts. The host manages containers, positioning, and dismissal for you.
10
+
11
+ ```vue
12
+ <!-- App.vue -->
13
+ <template>
14
+ <RouterView />
15
+ <VibeToastHost />
16
+ </template>
17
+ ```
18
+
19
+ ```vue
20
+ <!-- AnyComponent.vue -->
21
+ <template>
22
+ <VibeButton @click="save">Save</VibeButton>
23
+ </template>
24
+
25
+ <script setup>
26
+ import { useToast } from '@velkymx/vibeui'
27
+
28
+ const toast = useToast()
29
+
30
+ function save() {
31
+ // ...persist...
32
+ toast.success('Saved successfully')
33
+ toast.error('Something went wrong', { title: 'Error' })
34
+ toast.info('Heads up', { delay: 8000 })
35
+ }
36
+ </script>
37
+ ```
38
+
39
+ ### `useToast()` API
40
+
41
+ | Method | Signature | Description |
42
+ |--------|-----------|-------------|
43
+ | `show` | `(body, options?) => ToastSpec` | Enqueue a toast with explicit options |
44
+ | `success` | `(body, options?) => ToastSpec` | Shorthand for `variant: 'success'` |
45
+ | `error` | `(body, options?) => ToastSpec` | Shorthand for `variant: 'danger'` |
46
+ | `warn` | `(body, options?) => ToastSpec` | Shorthand for `variant: 'warning'` |
47
+ | `info` | `(body, options?) => ToastSpec` | Shorthand for `variant: 'info'` |
48
+ | `dismiss` | `(id) => boolean` | Remove a toast by id; returns `true` if found |
49
+ | `clear` | `() => void` | Remove all queued toasts |
50
+ | `toasts` | `readonly ToastSpec[]` | Reactive readonly snapshot of the queue |
51
+
52
+ `ToastShowOptions`: `{ id?, title?, variant?, placement?, autohide?, delay? }`. Passing an existing `id` updates that toast in place instead of adding a new one. The variant shorthands (`success`, `error`, `warn`, `info`) accept the same options minus `variant`.
53
+
54
+ ### `<VibeToastHost />` Props
55
+
56
+ | Prop | Type | Default | Description |
57
+ |------|------|---------|-------------|
58
+ | `defaultPlacement` | `ToastPlacement` | `'top-end'` | Placement used for toasts that don't specify one |
59
+
60
+ Toasts are grouped into one fixed container per placement, so toasts with different `placement` values render in the correct corner automatically.
61
+
62
+ ## VibeToast Component (primitive)
63
+
64
+ Use the component directly when you want a toast tied to a local `v-model`.
65
+
66
+ ### Props
6
67
 
7
68
  | Prop | Type | Default | Description |
8
69
  |------|------|---------|-------------|
@@ -14,8 +75,9 @@ Push notifications for lightweight alerts.
14
75
  | `delay` | `Number` | `5000` | Delay in milliseconds |
15
76
  | `teleport` | `Boolean\|String` | `'body'` | Destination for Vue Teleport |
16
77
  | `placement` | `ToastPlacement` | `'top-end'` | Position of the toast |
78
+ | `noContainer` | `Boolean` | `false` | Render only the `.toast` element (no Teleport/container). Used internally by `VibeToastHost` to group toasts |
17
79
 
18
- ## Events
80
+ ### Events
19
81
 
20
82
  | Event | Payload | Description |
21
83
  |-------|---------|-------------|
@@ -24,17 +86,16 @@ Push notifications for lightweight alerts.
24
86
  | `shown` | - | Emitted when toast is fully shown |
25
87
  | `hide` | - | Emitted when toast starts hiding |
26
88
  | `hidden` | - | Emitted when toast is fully hidden |
89
+ | `component-error` | `ComponentError` | Emitted if Bootstrap JS fails to load |
27
90
 
28
- ## Slots
91
+ ### Slots
29
92
 
30
93
  | Slot | Description |
31
94
  |------|-------------|
32
95
  | `default` | Toast body content |
33
96
  | `header` | Custom header content |
34
97
 
35
- ## Usage
36
-
37
- ### Reactive Usage (v-model)
98
+ ### Usage
38
99
 
39
100
  ```vue
40
101
  <template>
@@ -53,57 +114,59 @@ const showToast = ref(false)
53
114
  </script>
54
115
  ```
55
116
 
56
- ### Placed Toasts
117
+ #### Placed Toasts
57
118
 
58
119
  ```vue
59
120
  <template>
60
121
  <div>
61
- <VibeToast title="Success" variant="success" placement="bottom-end">
122
+ <VibeToast
123
+ v-model="showSuccess"
124
+ title="Success"
125
+ variant="success"
126
+ placement="bottom-end"
127
+ >
62
128
  Operation completed successfully!
63
129
  </VibeToast>
64
130
 
65
- <VibeToast title="Center" variant="info" placement="middle-center">
131
+ <VibeToast v-model="showInfo" title="Center" variant="info" placement="middle-center">
66
132
  I am in the middle of the screen
67
133
  </VibeToast>
68
134
  </div>
69
135
  </template>
70
- ```
71
-
72
- ### Colored Toasts
73
-
74
- ```vue
75
- <template>
76
- <div>
77
- <VibeToast title="Success" variant="success">
78
- Operation completed successfully!
79
- </VibeToast>
80
136
 
81
- <VibeToast title="Warning" variant="warning">
82
- Please review your changes
83
- </VibeToast>
84
- </div>
85
- </template>
137
+ <script setup>
138
+ import { ref } from 'vue'
139
+ const showSuccess = ref(false)
140
+ const showInfo = ref(false)
141
+ </script>
86
142
  ```
87
143
 
88
- ### Custom Delay
144
+ #### Custom Delay
89
145
 
90
146
  ```vue
91
147
  <template>
92
- <VibeToast title="Quick Toast" :delay="2000">
148
+ <VibeToast v-model="show" title="Quick Toast" :delay="2000">
93
149
  This will auto-hide after 2 seconds
94
150
  </VibeToast>
95
151
  </template>
152
+
153
+ <script setup>
154
+ import { ref } from 'vue'
155
+ const show = ref(true)
156
+ </script>
96
157
  ```
97
158
 
98
159
  ## Important Notes
99
160
 
100
- **Automatic Initialization:** This component automatically initializes Bootstrap's Toast functionality when it is mounted, provided that Bootstrap's JavaScript is available in your project.
161
+ **Prefer the service:** Use `useToast()` + `<VibeToastHost />` for most cases. It dedupes/updates by `id`, queues multiple toasts, and handles container placement. Reach for `<VibeToast>` only when you need a toast bound to a single local boolean.
162
+
163
+ **Automatic Container:** Unlike raw Bootstrap, `<VibeToast>` automatically manages its own `toast-container` and positioning based on the `placement` prop (unless `noContainer` is set).
101
164
 
102
- **Automatic Container:** Unlike raw Bootstrap, VibeToast automatically manages its own `toast-container` and positioning based on the `placement` prop.
165
+ **Teleportation:** By default, the component teleports to the `<body>` so it is always visible on top of other elements.
103
166
 
104
- **Teleportation:** By default, this component teleports to the `<body>` to ensure it is always visible on top of other elements.
167
+ **SSR:** The toast queue is a module singleton. In SSR runtimes call `resetToastStoreForSSR()` in your per-request reset hook to avoid leaking one request's pending toasts into another's render.
105
168
 
106
- **Instance Exposure:** You can access the underlying Bootstrap instance via template ref using the `bsInstance` property.
169
+ **Escape Hatch:** `<VibeToast>` exposes `_unsafe_bsInstance` (raw Bootstrap Toast instance) via template ref. It is **not** part of the stable API — calling `dispose()` or other lifecycle methods on it directly **will** break the component. Prefer `v-model` and the exposed `show()` / `hide()` methods.
107
170
 
108
171
  ## Bootstrap CSS Classes
109
172
 
@@ -33,12 +33,6 @@ Column component for Bootstrap's grid system. Supports responsive sizing, offset
33
33
  - **OrderValue:** `0 | 1 | 2 | 3 | 4 | 5 | 'first' | 'last'`
34
34
  - **AlignItems:** `'start' | 'center' | 'end' | 'baseline' | 'stretch'`
35
35
 
36
- ## Events
37
-
38
- | Event | Payload | Description |
39
- |-------|---------|-------------|
40
- | `component-error` | `Object` | Emitted when an error occurs |
41
-
42
36
  ## Slots
43
37
 
44
38
  | Slot | Description |
@@ -13,12 +13,6 @@ Responsive container component wrapping Bootstrap's `.container`, `.container-fl
13
13
 
14
14
  `'sm' | 'md' | 'lg' | 'xl' | 'xxl'`
15
15
 
16
- ## Events
17
-
18
- | Event | Payload | Description |
19
- |-------|---------|-------------|
20
- | `component-error` | `Object` | Emitted when an error occurs |
21
-
22
16
  ## Slots
23
17
 
24
18
  | Slot | Description |
@@ -41,12 +41,6 @@ Row component for Bootstrap's grid system. Provides gutters, row columns, and al
41
41
  - **AlignItems:** `'start' | 'center' | 'end' | 'baseline' | 'stretch'`
42
42
  - **JustifyContent:** `'start' | 'center' | 'end' | 'around' | 'between' | 'evenly'`
43
43
 
44
- ## Events
45
-
46
- | Event | Payload | Description |
47
- |-------|---------|-------------|
48
- | `component-error` | `Object` | Emitted when an error occurs |
49
-
50
44
  ## Slots
51
45
 
52
46
  | Slot | Description |
@@ -208,6 +208,10 @@ const handleClick = ({ item, index }) => {
208
208
  </script>
209
209
  ```
210
210
 
211
+ ## Important Notes
212
+
213
+ **`href` sanitization:** Each item's `href` is sanitized. Only `https://`/`http://` URLs, absolute paths (`/path`), relative paths (`./`, `../`), and anchors (`#section`) are allowed. Dangerous values such as `javascript:`, `data:`, `vbscript:`, and protocol-relative `//host` URLs are stripped — the item renders without an `href` (falling back to a non-link element). Use `to` for in-app navigation via Vue Router.
214
+
211
215
  ## Bootstrap CSS Classes
212
216
 
213
217
  - `.list-group`
@@ -109,6 +109,10 @@ const handleClick = ({ item, index }) => {
109
109
  </script>
110
110
  ```
111
111
 
112
+ ## Important Notes
113
+
114
+ **`href` sanitization:** Item `href` values are sanitized. Only `https?://`, root-relative (`/path`), relative (`./`, `../`), and anchor (`#anchor`) URLs are allowed. `javascript:`, `data:`, and protocol-relative (`//`) URLs are stripped — such items render as plain text instead of links. Use `to` for Vue Router navigation.
115
+
112
116
  ## Bootstrap CSS Classes
113
117
 
114
118
  - `.breadcrumb`
@@ -22,6 +22,8 @@ interface NavItem {
22
22
  text: string
23
23
  href?: string
24
24
  to?: string | object
25
+ /** Tab panel selector (e.g. '#panel-id') for tabs/pills mode with router-link items */
26
+ target?: string
25
27
  active?: boolean
26
28
  disabled?: boolean
27
29
  children?: NavItem[] // Support for dropdowns
@@ -46,6 +48,28 @@ interface NavItem {
46
48
 
47
49
  ## Usage
48
50
 
51
+ ### Custom item content (`#item` slot)
52
+
53
+ Use the `item` slot to render richer nav links — e.g. an icon next to the label. The slot is scoped with `{ item, index }`.
54
+
55
+ ```vue
56
+ <template>
57
+ <VibeNav :items="navItems">
58
+ <template #item="{ item }">
59
+ <VibeIcon :icon="item.icon" class="me-2" />
60
+ {{ item.text }}
61
+ </template>
62
+ </VibeNav>
63
+ </template>
64
+
65
+ <script setup>
66
+ const navItems = [
67
+ { text: 'Home', href: '#', icon: 'house', active: true },
68
+ { text: 'Profile', href: '#', icon: 'person' }
69
+ ]
70
+ </script>
71
+ ```
72
+
49
73
  ### Interactive Tabs
50
74
 
51
75
  When using the `tabs` or `pills` props, nav links with an `href` starting with `#` will automatically act as Bootstrap tab triggers.
@@ -72,9 +96,48 @@ const tabPanes = [
72
96
  </script>
73
97
  ```
74
98
 
99
+ ### Tabs with Vue Router Links
100
+
101
+ Items using `to` (router-link) also work in tabs/pills mode. Provide a `target` field pointing to the panel ID, and Bootstrap Tab events (`show`, `shown`, `hide`, `hidden`) will fire correctly:
102
+
103
+ ```vue
104
+ <template>
105
+ <VibeNav tabs :items="navItems" @shown="onShown" />
106
+ <div class="tab-content mt-3">
107
+ <div id="overview-pane" class="tab-pane fade show active">Overview content</div>
108
+ <div id="settings-pane" class="tab-pane fade">Settings content</div>
109
+ </div>
110
+ </template>
111
+
112
+ <script setup>
113
+ const navItems = [
114
+ { text: 'Overview', to: '/overview', target: '#overview-pane', active: true },
115
+ { text: 'Settings', to: '/settings', target: '#settings-pane' }
116
+ ]
117
+
118
+ const onShown = (event) => {
119
+ console.log('Tab shown:', event.target)
120
+ }
121
+ </script>
122
+ ```
123
+
124
+ Items with `to` starting with `#` are also treated as tab targets without needing an explicit `target` field.
125
+
126
+ ## Exposed Methods
127
+
128
+ | Method | Description |
129
+ |--------|-------------|
130
+ | `refresh()` | Tears down and re-initializes Bootstrap Tab instances. Call after dynamically changing items if needed. |
131
+
132
+ > **Escape hatch:** `_unsafe_bsInstances` (a `Map` of the underlying Bootstrap `Tab` instances) is also exposed. It is **not** part of the stable API — calling `dispose()` or other lifecycle methods on these directly WILL break the component.
133
+
75
134
  ## Important Notes
76
135
 
77
- **Automatic Initialization:** When using `tabs` or `pills`, this component automatically initializes Bootstrap's Tab functionality for any items that target a local ID (e.g., `href="#my-tab"`).
136
+ **`href` sanitization:** Item and child `href` values are sanitized. Only `https?://`, root-relative (`/path`), relative (`./`, `../`), and anchor (`#anchor`) URLs are allowed; `javascript:`, `data:`, and protocol-relative (`//`) URLs are stripped. Use `to` for Vue Router navigation.
137
+
138
+ **Disabled button items:** Items without `href` or `to` (plain button items) receive the HTML `disabled` attribute when `disabled: true` is set. Link and router-link items only get the disabled visual style — the browser attribute is omitted because `<a disabled>` has no native effect.
139
+
140
+ **Automatic Initialization:** When using `tabs` or `pills`, this component automatically initializes Bootstrap's Tab functionality for items that target a local panel ID via `href="#..."`, `target="#..."`, or `to="#..."`.
78
141
 
79
142
  **State Management:** For complex tab state, combine `VibeNav` with `VibeTabContent` and manage the `active` state through your data.
80
143