@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.
- package/AGENTS.md +44 -0
- package/README.md +245 -58
- package/dist/purify.es-1D-VntFD.mjs +679 -0
- package/dist/src/App.vue.d.ts +3 -0
- package/dist/src/components/HelloWorld.vue.d.ts +6 -0
- package/dist/src/components/VibeAccordion.vue.d.ts +89 -0
- package/dist/{components → src/components}/VibeAlert.vue.d.ts +24 -30
- package/dist/src/components/VibeAutocomplete.vue.d.ts +74 -0
- package/dist/{components → src/components}/VibeBadge.vue.d.ts +10 -18
- package/dist/src/components/VibeBreadcrumb.vue.d.ts +51 -0
- package/dist/{components → src/components}/VibeButton.vue.d.ts +16 -20
- package/dist/{components → src/components}/VibeButtonGroup.vue.d.ts +10 -18
- package/dist/{components → src/components}/VibeCard.vue.d.ts +51 -24
- package/dist/{components → src/components}/VibeCarousel.vue.d.ts +27 -49
- package/dist/src/components/VibeChartBar.vue.d.ts +61 -0
- package/dist/src/components/VibeChartLine.vue.d.ts +70 -0
- package/dist/src/components/VibeChartPie.vue.d.ts +34 -0
- package/dist/{components → src/components}/VibeCloseButton.vue.d.ts +9 -7
- package/dist/{components → src/components}/VibeCol.vue.d.ts +9 -17
- package/dist/src/components/VibeCollapse.vue.d.ts +76 -0
- package/dist/src/components/VibeContainer.vue.d.ts +36 -0
- package/dist/src/components/VibeDataTable.vue.d.ts +131 -0
- package/dist/src/components/VibeDatePicker.vue.d.ts +120 -0
- package/dist/src/components/VibeDraggable.vue.d.ts +77 -0
- package/dist/{components → src/components}/VibeDropdown.vue.d.ts +46 -43
- package/dist/src/components/VibeDroppable.vue.d.ts +70 -0
- package/dist/src/components/VibeFileInput.vue.d.ts +127 -0
- package/dist/{components → src/components}/VibeFormCheckbox.vue.d.ts +30 -22
- package/dist/{components → src/components}/VibeFormDatepicker.vue.d.ts +27 -30
- package/dist/{components → src/components}/VibeFormGroup.vue.d.ts +11 -14
- package/dist/{components → src/components}/VibeFormInput.vue.d.ts +22 -23
- package/dist/{components → src/components}/VibeFormRadio.vue.d.ts +23 -24
- package/dist/{components → src/components}/VibeFormSelect.vue.d.ts +28 -34
- package/dist/{components → src/components}/VibeFormSpinbutton.vue.d.ts +31 -34
- package/dist/{components → src/components}/VibeFormSwitch.vue.d.ts +22 -23
- package/dist/{components → src/components}/VibeFormTextarea.vue.d.ts +24 -25
- package/dist/{components → src/components}/VibeFormWysiwyg.vue.d.ts +24 -23
- package/dist/src/components/VibeHero.vue.d.ts +108 -0
- package/dist/{components → src/components}/VibeIcon.vue.d.ts +27 -7
- package/dist/{components → src/components}/VibeInputGroup.vue.d.ts +11 -13
- package/dist/{components → src/components}/VibeLink.vue.d.ts +14 -18
- package/dist/{components → src/components}/VibeListGroup.vue.d.ts +26 -22
- package/dist/{components → src/components}/VibeModal.vue.d.ts +29 -45
- package/dist/{components → src/components}/VibeNav.vue.d.ts +40 -20
- package/dist/{components → src/components}/VibeNavbar.vue.d.ts +12 -19
- package/dist/src/components/VibeNavbarBrand.vue.d.ts +34 -0
- package/dist/src/components/VibeNavbarNav.vue.d.ts +80 -0
- package/dist/{components → src/components}/VibeNavbarToggle.vue.d.ts +7 -5
- package/dist/{components → src/components}/VibeOffcanvas.vue.d.ts +28 -43
- package/dist/{components → src/components}/VibePagination.vue.d.ts +38 -29
- package/dist/{components → src/components}/VibePlaceholder.vue.d.ts +9 -17
- package/dist/src/components/VibePopover.vue.d.ts +72 -0
- package/dist/src/components/VibeProgress.vue.d.ts +37 -0
- package/dist/src/components/VibeResizable.vue.d.ts +140 -0
- package/dist/{components → src/components}/VibeRow.vue.d.ts +9 -17
- package/dist/src/components/VibeScrollspy.vue.d.ts +89 -0
- package/dist/src/components/VibeSkeleton.vue.d.ts +54 -0
- package/dist/src/components/VibeSlider.vue.d.ts +77 -0
- package/dist/src/components/VibeSortable.vue.d.ts +59 -0
- package/dist/{components → src/components}/VibeSpinner.vue.d.ts +4 -7
- package/dist/src/components/VibeStepper.vue.d.ts +133 -0
- package/dist/src/components/VibeTab.vue.d.ts +41 -0
- package/dist/src/components/VibeTabContent.vue.d.ts +56 -0
- package/dist/src/components/VibeTabs.vue.d.ts +78 -0
- package/dist/{components → src/components}/VibeToast.vue.d.ts +40 -42
- package/dist/src/components/VibeToastHost.vue.d.ts +16 -0
- package/dist/src/components/VibeTooltip.vue.d.ts +63 -0
- package/dist/src/components/chart/chartColors.d.ts +3 -0
- package/dist/src/components/chart/chartResize.d.ts +2 -0
- package/dist/src/components/chart/chartTooltip.d.ts +2 -0
- package/dist/src/components/chart/chartTypes.d.ts +6 -0
- package/dist/src/components/chart/drawBar.d.ts +5 -0
- package/dist/src/components/chart/drawLine.d.ts +18 -0
- package/dist/src/components/chart/drawPie.d.ts +4 -0
- package/dist/src/components/dndStore.d.ts +7 -0
- package/dist/{components → src/components}/index.d.ts +18 -1
- package/dist/{composables → src/composables}/useBreakpoints.d.ts +1 -0
- package/dist/{composables → src/composables}/useColorMode.d.ts +1 -1
- package/dist/src/composables/useForm.d.ts +22 -0
- package/dist/src/composables/useId.d.ts +2 -0
- package/dist/src/composables/usePosition.d.ts +20 -0
- package/dist/src/composables/useToast.d.ts +52 -0
- package/dist/src/directives/vTooltip.d.ts +24 -0
- package/dist/{index.d.ts → src/index.d.ts} +6 -0
- package/dist/src/injectionKeys.d.ts +28 -0
- package/dist/{types.d.ts → src/types.d.ts} +32 -6
- package/dist/src/utils/safeCss.d.ts +16 -0
- package/dist/src/utils/safeHref.d.ts +7 -0
- package/dist/src/utils/sanitizeHtml.d.ts +13 -0
- package/dist/vibeui.css +2 -1
- package/dist/vibeui.es.js +8351 -3508
- package/dist/vibeui.umd.js +3 -1
- package/docs/README.md +85 -13
- package/docs/components/advanced/popover.md +27 -52
- package/docs/components/advanced/scrollspy.md +57 -15
- package/docs/components/advanced/tooltip.md +26 -47
- package/docs/components/card/card.md +9 -1
- 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 +27 -5
- package/docs/components/core/badge.md +0 -6
- package/docs/components/core/button-group.md +0 -6
- package/docs/components/core/button.md +1 -1
- package/docs/components/core/close-button.md +5 -5
- package/docs/components/core/link.md +60 -17
- package/docs/components/core/placeholder.md +0 -6
- package/docs/components/core/skeleton.md +40 -0
- package/docs/components/core/spinner.md +0 -6
- package/docs/components/data/datatable.md +29 -10
- package/docs/components/hero/hero.md +64 -0
- package/docs/components/interactive/accordion.md +29 -0
- package/docs/components/interactive/carousel.md +19 -4
- package/docs/components/interactive/collapse.md +3 -2
- package/docs/components/interactive/date-picker.md +78 -0
- package/docs/components/interactive/draggable.md +91 -0
- package/docs/components/interactive/dropdown.md +54 -18
- package/docs/components/interactive/modal.md +15 -20
- package/docs/components/interactive/offcanvas.md +4 -1
- 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 +93 -30
- package/docs/components/layout/col.md +0 -6
- package/docs/components/layout/container.md +0 -6
- package/docs/components/layout/row.md +0 -6
- package/docs/components/list/list-group.md +4 -0
- package/docs/components/navigation/breadcrumb.md +4 -0
- package/docs/components/navigation/nav.md +64 -1
- package/docs/components/navigation/navbar.md +2 -0
- package/docs/components/navigation/pagination.md +16 -0
- package/docs/components/progress/progress.md +7 -1
- package/docs/composables/color-mode.md +44 -6
- 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 +60 -61
- package/docs/forms/autocomplete.md +96 -0
- package/docs/forms/file-input.md +97 -0
- package/docs/forms/form-checkbox.md +75 -25
- package/docs/forms/form-datepicker.md +58 -28
- package/docs/forms/form-group.md +79 -44
- package/docs/forms/form-input.md +88 -31
- package/docs/forms/form-radio.md +60 -36
- package/docs/forms/form-select.md +75 -31
- package/docs/forms/form-spinbutton.md +72 -33
- package/docs/forms/form-switch.md +53 -25
- package/docs/forms/form-textarea.md +60 -29
- package/docs/forms/form-wysiwyg.md +73 -38
- package/docs/forms/input-group.md +53 -34
- package/docs/forms/validation.md +135 -521
- 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 +407 -47
- package/package.json +42 -13
- package/dist/App.vue.d.ts +0 -2
- package/dist/components/HelloWorld.vue.d.ts +0 -5
- package/dist/components/VibeAccordion.vue.d.ts +0 -90
- package/dist/components/VibeBreadcrumb.vue.d.ts +0 -47
- package/dist/components/VibeCollapse.vue.d.ts +0 -84
- package/dist/components/VibeContainer.vue.d.ts +0 -44
- package/dist/components/VibeDataTable.vue.d.ts +0 -76
- package/dist/components/VibeNavbarBrand.vue.d.ts +0 -42
- package/dist/components/VibeNavbarNav.vue.d.ts +0 -61
- package/dist/components/VibePopover.vue.d.ts +0 -95
- package/dist/components/VibeProgress.vue.d.ts +0 -45
- package/dist/components/VibeScrollspy.vue.d.ts +0 -77
- package/dist/components/VibeTabContent.vue.d.ts +0 -74
- package/dist/components/VibeTooltip.vue.d.ts +0 -86
- package/dist/composables/useId.d.ts +0 -5
- package/dist/types/index.d.ts +0 -6
- /package/dist/{composables → src/composables}/useBackButton.d.ts +0 -0
- /package/dist/{composables → src/composables}/useFormValidation.d.ts +0 -0
- /package/dist/{main.d.ts → src/main.d.ts} +0 -0
|
@@ -19,8 +19,22 @@ Powerful data table component with search, sorting, and pagination - similar to
|
|
|
19
19
|
|
|
20
20
|
| Prop | Type | Default | Description |
|
|
21
21
|
|------|------|---------|-------------|
|
|
22
|
-
| `items` | `
|
|
23
|
-
| `columns` | `DataTableColumn[]` | Required | Column definitions |
|
|
22
|
+
| `items` | `T[]` | `[]` | Array of data objects to display |
|
|
23
|
+
| `columns` | `DataTableColumn<T>[]` | Required | Column definitions |
|
|
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`.
|
|
24
38
|
|
|
25
39
|
### Table Styling Props
|
|
26
40
|
|
|
@@ -89,11 +103,13 @@ interface DataTableColumn {
|
|
|
89
103
|
formatter?: (value: any, row: any) => string | number // Custom formatter
|
|
90
104
|
class?: string // CSS class for td
|
|
91
105
|
headerClass?: string // CSS class for th
|
|
92
|
-
thStyle?: Record<string, string> // Inline styles for th
|
|
93
|
-
tdStyle?: Record<string, string> // Inline styles for td
|
|
106
|
+
thStyle?: Record<string, string> // Inline styles for th (sanitized — see note)
|
|
107
|
+
tdStyle?: Record<string, string> // Inline styles for td (sanitized — see note)
|
|
94
108
|
}
|
|
95
109
|
```
|
|
96
110
|
|
|
111
|
+
> **`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.
|
|
112
|
+
|
|
97
113
|
## Events
|
|
98
114
|
|
|
99
115
|
| Event | Payload | Description |
|
|
@@ -102,7 +118,7 @@ interface DataTableColumn {
|
|
|
102
118
|
| `update:perPage` | `Number` | Emitted when per-page changes |
|
|
103
119
|
| `update:sortBy` | `String` | Emitted when sort column changes |
|
|
104
120
|
| `update:sortDesc` | `Boolean` | Emitted when sort direction changes |
|
|
105
|
-
| `row-clicked` | `(item,
|
|
121
|
+
| `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). |
|
|
106
122
|
|
|
107
123
|
## Slots
|
|
108
124
|
|
|
@@ -133,10 +149,12 @@ const items = [
|
|
|
133
149
|
</script>
|
|
134
150
|
|
|
135
151
|
<template>
|
|
136
|
-
<VibeDataTable :columns="columns" :items="items" />
|
|
152
|
+
<VibeDataTable :columns="columns" :items="items" row-key="id" />
|
|
137
153
|
</template>
|
|
138
154
|
```
|
|
139
155
|
|
|
156
|
+
> Always pass `row-key` pointing at a unique field (here `id`) so Vue keys rows stably across sorting, filtering, and pagination.
|
|
157
|
+
|
|
140
158
|
### With Bootstrap Styling
|
|
141
159
|
|
|
142
160
|
```vue
|
|
@@ -232,8 +250,8 @@ const perPage = ref(25)
|
|
|
232
250
|
const sortBy = ref('name')
|
|
233
251
|
const sortDesc = ref(false)
|
|
234
252
|
|
|
235
|
-
const handleRowClick = (item,
|
|
236
|
-
console.log('Clicked row:', item,
|
|
253
|
+
const handleRowClick = (item, globalIndex) => {
|
|
254
|
+
console.log('Clicked row:', item, globalIndex)
|
|
237
255
|
}
|
|
238
256
|
</script>
|
|
239
257
|
|
|
@@ -241,6 +259,7 @@ const handleRowClick = (item, index) => {
|
|
|
241
259
|
<VibeDataTable
|
|
242
260
|
:columns="columns"
|
|
243
261
|
:items="items"
|
|
262
|
+
row-key="id"
|
|
244
263
|
v-model:current-page="currentPage"
|
|
245
264
|
v-model:per-page="perPage"
|
|
246
265
|
v-model:sort-by="sortBy"
|
|
@@ -410,7 +429,7 @@ const columns = [
|
|
|
410
429
|
## Tips
|
|
411
430
|
|
|
412
431
|
1. **Large Datasets**: For datasets with 1000+ rows, consider server-side pagination
|
|
413
|
-
2. **
|
|
432
|
+
2. **Stable keys**: Set the `row-key` prop to a unique field in your data for correct behavior during sorting/filtering and best reactivity
|
|
414
433
|
3. **Search Debounce**: Adjust `searchDebounce` prop for performance with large datasets
|
|
415
|
-
4. **Custom Styling**: Use column `class`, `headerClass`, `thStyle`, `tdStyle` for styling
|
|
434
|
+
4. **Custom Styling**: Use column `class`, `headerClass`, `thStyle`, `tdStyle` for styling (style objects are sanitized to a safe property allowlist)
|
|
416
435
|
5. **Slots**: Use slots for complex cell rendering (badges, buttons, images, etc.)
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
| `gradient` | `String` | `undefined` | CSS gradient background (e.g. `linear-gradient(135deg, #667eea, #764ba2)`); validated to `*-gradient(...)` only — no `url()`/`expression()` |
|
|
26
|
+
| `tag` | `Tag` | `'section'` | Root element tag |
|
|
27
|
+
|
|
28
|
+
## Slots
|
|
29
|
+
|
|
30
|
+
| Slot | Description |
|
|
31
|
+
|------|-------------|
|
|
32
|
+
| `default` | Hero content. You supply the inner layout (a centered column, or a `VibeRow`/`VibeCol` split). |
|
|
33
|
+
|
|
34
|
+
## Examples
|
|
35
|
+
|
|
36
|
+
**Centered (Bootstrap "centered hero")**
|
|
37
|
+
|
|
38
|
+
```vue
|
|
39
|
+
<VibeHero>
|
|
40
|
+
<VibeCol :lg="7" class="mx-auto">
|
|
41
|
+
<h1 class="display-3 fw-bold">Photo Gallery</h1>
|
|
42
|
+
<p class="lead">Curated photography from around the world.</p>
|
|
43
|
+
</VibeCol>
|
|
44
|
+
</VibeHero>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Two-column with gradient**
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<VibeHero gradient="linear-gradient(135deg, #667eea 0%, #764ba2 100%)" align="start" class="text-white">
|
|
51
|
+
<VibeRow class="align-items-center">
|
|
52
|
+
<VibeCol :lg="6"><h1 class="display-3 fw-bold">Headline</h1></VibeCol>
|
|
53
|
+
<VibeCol :lg="6"><img src="/demo.png" class="img-fluid rounded-4 shadow-lg"></VibeCol>
|
|
54
|
+
</VibeRow>
|
|
55
|
+
</VibeHero>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Full-bleed image with overlay**
|
|
59
|
+
|
|
60
|
+
```vue
|
|
61
|
+
<VibeHero bg-image="/banner.jpg" overlay min-height="400px" class="text-white">
|
|
62
|
+
<h1 class="display-3 fw-bold">Welcome</h1>
|
|
63
|
+
</VibeHero>
|
|
64
|
+
```
|
|
@@ -31,6 +31,7 @@ interface AccordionItem {
|
|
|
31
31
|
| `shown` | `id` | Emitted when an item is fully shown |
|
|
32
32
|
| `hide` | `id` | Emitted when an item starts hiding |
|
|
33
33
|
| `hidden` | `id` | Emitted when an item is fully hidden |
|
|
34
|
+
| `component-error` | `ComponentError` | Emitted if Bootstrap JS fails to load |
|
|
34
35
|
|
|
35
36
|
## Slots
|
|
36
37
|
|
|
@@ -75,12 +76,40 @@ Items stay open when another item is opened:
|
|
|
75
76
|
</template>
|
|
76
77
|
```
|
|
77
78
|
|
|
79
|
+
### Custom Title and Content (Slots)
|
|
80
|
+
|
|
81
|
+
Both the `title` and `content` slots receive `{ item, index }` so you can render rich markup while still driving the accordion from the `items` array:
|
|
82
|
+
|
|
83
|
+
```vue
|
|
84
|
+
<template>
|
|
85
|
+
<VibeAccordion :items="accordionItems">
|
|
86
|
+
<template #title="{ item }">
|
|
87
|
+
<strong>{{ item.title }}</strong>
|
|
88
|
+
</template>
|
|
89
|
+
<template #content="{ item }">
|
|
90
|
+
<div v-html="item.content" />
|
|
91
|
+
</template>
|
|
92
|
+
</VibeAccordion>
|
|
93
|
+
</template>
|
|
94
|
+
|
|
95
|
+
<script setup>
|
|
96
|
+
const accordionItems = [
|
|
97
|
+
{ id: 'one', title: 'Item #1', content: '<p>Rich <em>HTML</em> body.</p>', show: true },
|
|
98
|
+
{ id: 'two', title: 'Item #2', content: '<p>Second body.</p>' }
|
|
99
|
+
]
|
|
100
|
+
</script>
|
|
101
|
+
```
|
|
102
|
+
|
|
78
103
|
## Important Notes
|
|
79
104
|
|
|
80
105
|
**Automatic Initialization:** This component automatically initializes Bootstrap's Collapse functionality for each item when it is mounted, provided that Bootstrap's JavaScript is available in your project.
|
|
81
106
|
|
|
82
107
|
**Reactivity:** The `show` property in the `AccordionItem` objects is watched. Changing it in your parent state will trigger the corresponding Bootstrap transition.
|
|
83
108
|
|
|
109
|
+
**Item ids:** Each `item.id` (and the accordion `id`) must contain only alphanumeric characters, hyphens, and underscores — Bootstrap's internal `querySelector` breaks on CSS-special characters. In dev mode an unsafe id logs a console warning.
|
|
110
|
+
|
|
111
|
+
**Escape Hatch:** The exposed `_unsafe_bsInstances` template ref is a `Map` of the underlying Bootstrap Collapse instances keyed by item id. It is **not** part of the stable API — calling `dispose()` or other lifecycle methods on them directly **will** break the component. To re-sync after manual DOM changes, call the exposed `refresh()` method instead.
|
|
112
|
+
|
|
84
113
|
## Bootstrap CSS Classes
|
|
85
114
|
|
|
86
115
|
- `.accordion`
|
|
@@ -38,8 +38,9 @@ interface CarouselItem {
|
|
|
38
38
|
| Event | Payload | Description |
|
|
39
39
|
|-------|---------|-------------|
|
|
40
40
|
| `update:modelValue` | `Number` | Emitted when the active slide changes |
|
|
41
|
-
| `slide` |
|
|
42
|
-
| `slid` |
|
|
41
|
+
| `slide` | `Event` | Emitted when slide transition starts (Bootstrap `slide.bs.carousel`) |
|
|
42
|
+
| `slid` | `Event` | Emitted when slide transition completes (Bootstrap `slid.bs.carousel`) |
|
|
43
|
+
| `component-error` | `ComponentError` | Emitted if Bootstrap JS fails to load |
|
|
43
44
|
|
|
44
45
|
## Slots
|
|
45
46
|
|
|
@@ -66,7 +67,10 @@ interface CarouselItem {
|
|
|
66
67
|
<script setup>
|
|
67
68
|
import { ref } from 'vue'
|
|
68
69
|
const currentSlide = ref(0)
|
|
69
|
-
const carouselItems = [
|
|
70
|
+
const carouselItems = [
|
|
71
|
+
{ src: '/img/slide-1.jpg', caption: 'First slide', captionText: 'Intro copy' },
|
|
72
|
+
{ src: '/img/slide-2.jpg', caption: 'Second slide' }
|
|
73
|
+
]
|
|
70
74
|
</script>
|
|
71
75
|
```
|
|
72
76
|
|
|
@@ -76,13 +80,24 @@ const carouselItems = [...]
|
|
|
76
80
|
<template>
|
|
77
81
|
<VibeCarousel :items="carouselItems" />
|
|
78
82
|
</template>
|
|
83
|
+
|
|
84
|
+
<script setup>
|
|
85
|
+
const carouselItems = [
|
|
86
|
+
{ src: '/img/slide-1.jpg', alt: 'A scenic view' },
|
|
87
|
+
{ src: '/img/slide-2.jpg', alt: 'A city street' }
|
|
88
|
+
]
|
|
89
|
+
</script>
|
|
79
90
|
```
|
|
80
91
|
|
|
81
92
|
## Important Notes
|
|
82
93
|
|
|
83
94
|
**Automatic Initialization:** This component automatically initializes Bootstrap's Carousel functionality when it is mounted, provided that Bootstrap's JavaScript is available in your project.
|
|
84
95
|
|
|
85
|
-
|
|
96
|
+
**`item.src` is required:** Every slide is an image slide — each item must provide a `src`. Captions and alt text are optional (an accessible fallback `alt` is generated from `alt` → `caption` → `captionText` → `"Carousel slide N"`).
|
|
97
|
+
|
|
98
|
+
**Empty items:** An empty `items` array is handled gracefully — the carousel simply renders no slides.
|
|
99
|
+
|
|
100
|
+
**Escape Hatch:** The exposed `_unsafe_bsInstance` template ref gives raw access to the underlying Bootstrap Carousel instance. It is **not** part of the stable API — calling `dispose()` or other lifecycle methods on it directly **will** break the component. To re-initialize after manual changes, call the exposed `refresh()` method instead.
|
|
86
101
|
|
|
87
102
|
## Bootstrap CSS Classes
|
|
88
103
|
|
|
@@ -21,6 +21,7 @@ Toggle visibility of content with smooth transitions.
|
|
|
21
21
|
| `shown` | - | Emitted when collapse is fully shown |
|
|
22
22
|
| `hide` | - | Emitted when collapse starts hiding |
|
|
23
23
|
| `hidden` | - | Emitted when collapse is fully hidden |
|
|
24
|
+
| `component-error` | `ComponentError` | Emitted if Bootstrap JS fails to load |
|
|
24
25
|
|
|
25
26
|
## Slots
|
|
26
27
|
|
|
@@ -93,9 +94,9 @@ When used inside a `VibeNavbar`, add `is-nav` to get the `navbar-collapse` class
|
|
|
93
94
|
|
|
94
95
|
**Automatic Initialization:** This component automatically initializes Bootstrap's Collapse functionality when it is mounted, ensuring that smooth sliding transitions are used.
|
|
95
96
|
|
|
96
|
-
**State Synchronization:**
|
|
97
|
+
**State Synchronization:** Clicking a toggle (like `VibeNavbarToggle`) updates both Vue's `v-model` state and Bootstrap's JavaScript instance simultaneously, so the two never drift out of sync.
|
|
97
98
|
|
|
98
|
-
**
|
|
99
|
+
**Pre-init fallback:** Before Bootstrap JS finishes loading, an open collapse (`v-model` true) still renders visible via the `.show` class; once Bootstrap is ready it takes ownership of the class.
|
|
99
100
|
|
|
100
101
|
## Bootstrap CSS Classes
|
|
101
102
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# VibeDatePicker
|
|
2
|
+
|
|
3
|
+
Calendar-popover datepicker with single + range mode. Distinct from `VibeFormDatepicker`, which is a thin wrapper around `<input type="date">`.
|
|
4
|
+
|
|
5
|
+
Values are ISO `YYYY-MM-DD` strings.
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
| Prop | Type | Default | Description |
|
|
10
|
+
|------|------|---------|-------------|
|
|
11
|
+
| `modelValue` | `String \| null \| [String\|null, String\|null]` | `null` | ISO date(s) (v-model) |
|
|
12
|
+
| `range` | `Boolean` | `false` | Range selection mode |
|
|
13
|
+
| `min` | `String` | `undefined` | ISO lower bound |
|
|
14
|
+
| `max` | `String` | `undefined` | ISO upper bound |
|
|
15
|
+
| `disabledDates` | `String[]` | `[]` | Specific ISO dates to disable |
|
|
16
|
+
| `format` | `(iso) => string` | `undefined` | Display formatter for input value |
|
|
17
|
+
| `placeholder` | `String` | `'Select date'` | |
|
|
18
|
+
| `weekStart` | `Number` | `0` | 0 = Sunday, 1 = Monday |
|
|
19
|
+
| `id` | `String` | auto | |
|
|
20
|
+
| `label` | `String` | `undefined` | |
|
|
21
|
+
| `disabled` | `Boolean` | `false` | |
|
|
22
|
+
|
|
23
|
+
### Events
|
|
24
|
+
|
|
25
|
+
| Event | Payload | Description |
|
|
26
|
+
|-------|---------|-------------|
|
|
27
|
+
| `update:modelValue` | `String \| [String\|null, String\|null]` | Selection committed |
|
|
28
|
+
| `open` | — | Popover opened |
|
|
29
|
+
| `close` | — | Popover closed |
|
|
30
|
+
|
|
31
|
+
### Exposed methods
|
|
32
|
+
|
|
33
|
+
- `open()` — programmatically open the popover.
|
|
34
|
+
- `close()` — programmatically close.
|
|
35
|
+
|
|
36
|
+
## Single mode
|
|
37
|
+
|
|
38
|
+
```vue
|
|
39
|
+
<script setup>
|
|
40
|
+
import { ref } from 'vue'
|
|
41
|
+
const date = ref(null)
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<VibeDatePicker v-model="date" label="Due date" />
|
|
46
|
+
</template>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Range mode
|
|
50
|
+
|
|
51
|
+
```vue
|
|
52
|
+
<VibeDatePicker
|
|
53
|
+
v-model="range"
|
|
54
|
+
range
|
|
55
|
+
:min="'2026-01-01'"
|
|
56
|
+
:max="'2026-12-31'"
|
|
57
|
+
:format="iso => iso.split('-').reverse().join('/')"
|
|
58
|
+
/>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The first click sets `[start, null]`. The second click adds the end date and closes the popover; if the second pick is earlier than the first, the range is auto-swapped to stay ordered.
|
|
62
|
+
|
|
63
|
+
## Disabling dates
|
|
64
|
+
|
|
65
|
+
```vue
|
|
66
|
+
<VibeDatePicker
|
|
67
|
+
v-model="date"
|
|
68
|
+
:disabled-dates="['2026-04-29', '2026-04-30']"
|
|
69
|
+
/>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Important Notes
|
|
73
|
+
|
|
74
|
+
**Zero-padded ISO bounds:** `min`, `max`, and every entry in `disabledDates` must be zero-padded `YYYY-MM-DD` (e.g. `2026-04-05`, not `2026-4-5`). Comparisons are lexical string comparisons, so an unpadded value compares incorrectly. In dev mode, a non-conforming `min`/`max` logs a console warning.
|
|
75
|
+
|
|
76
|
+
**Read-only input:** The text input is read-only; dates are chosen through the calendar popover, which closes on outside click or `Escape`.
|
|
77
|
+
|
|
78
|
+
**Keyboard navigation:** The grid supports arrow keys (day), `PageUp`/`PageDown` (month), `Home`/`End` (week), and `Enter`/`Space` to select.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# VibeDraggable / VibeDroppable
|
|
2
|
+
|
|
3
|
+
Generic drag-and-drop primitives for kanban boards, builders, palette UIs. Communicates payloads through a shared module-level store, so payloads can be typed objects or non-serializable values (refs, functions) — no DataTransfer string round-trip.
|
|
4
|
+
|
|
5
|
+
## VibeDraggable Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
|------|------|---------|-------------|
|
|
9
|
+
| `payload` | `unknown` | `undefined` | Data passed to the matching droppable on drop |
|
|
10
|
+
| `group` | `String` | `'default'` | Tag used to match against droppable groups |
|
|
11
|
+
| `disabled` | `Boolean` | `false` | Disable dragging |
|
|
12
|
+
| `tag` | `String` | `'div'` | Element to render |
|
|
13
|
+
|
|
14
|
+
### Events
|
|
15
|
+
|
|
16
|
+
| Event | Payload | Description |
|
|
17
|
+
|-------|---------|-------------|
|
|
18
|
+
| `dragstart` | `{ payload, group, event }` | Emitted on dragstart |
|
|
19
|
+
| `dragend` | `{ payload, group, event }` | Emitted on dragend, regardless of drop success |
|
|
20
|
+
|
|
21
|
+
### Slots
|
|
22
|
+
|
|
23
|
+
Default slot scope: `{ isDragging }`.
|
|
24
|
+
|
|
25
|
+
## VibeDroppable Props
|
|
26
|
+
|
|
27
|
+
| Prop | Type | Default | Description |
|
|
28
|
+
|------|------|---------|-------------|
|
|
29
|
+
| `group` | `String` | `'default'` | Group accepted unless `acceptGroups` overrides |
|
|
30
|
+
| `acceptGroups` | `String[]` | `undefined` | Whitelist of groups that may drop here |
|
|
31
|
+
| `disabled` | `Boolean` | `false` | Reject all drops |
|
|
32
|
+
| `tag` | `String` | `'div'` | Element to render |
|
|
33
|
+
|
|
34
|
+
### Events
|
|
35
|
+
|
|
36
|
+
| Event | Payload | Description |
|
|
37
|
+
|-------|---------|-------------|
|
|
38
|
+
| `drop` | `{ payload, group, event }` | Successful drop, only emitted when group matches |
|
|
39
|
+
| `dragenter` | `DragEvent` | |
|
|
40
|
+
| `dragleave` | `DragEvent` | |
|
|
41
|
+
|
|
42
|
+
### Slots
|
|
43
|
+
|
|
44
|
+
Default slot scope: `{ isOver }`.
|
|
45
|
+
|
|
46
|
+
## Examples
|
|
47
|
+
|
|
48
|
+
### Kanban column move
|
|
49
|
+
|
|
50
|
+
```vue
|
|
51
|
+
<script setup>
|
|
52
|
+
import { ref } from 'vue'
|
|
53
|
+
const todo = ref([{ id: 1, title: 'Wireframes' }])
|
|
54
|
+
const done = ref([])
|
|
55
|
+
|
|
56
|
+
function moveCard(card, from, to) {
|
|
57
|
+
from.value = from.value.filter(c => c.id !== card.id)
|
|
58
|
+
to.value.push(card)
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<template>
|
|
63
|
+
<div class="d-flex gap-3">
|
|
64
|
+
<VibeDroppable group="cards" @drop="({ payload }) => moveCard(payload, todo, done)">
|
|
65
|
+
<template #default="{ isOver }">
|
|
66
|
+
<div :class="['col bg-light p-3', isOver ? 'border-primary' : '']">
|
|
67
|
+
<h5>To do</h5>
|
|
68
|
+
<VibeDraggable v-for="card in todo" :key="card.id" :payload="card" group="cards">
|
|
69
|
+
<div class="card mb-2 p-2">{{ card.title }}</div>
|
|
70
|
+
</VibeDraggable>
|
|
71
|
+
</div>
|
|
72
|
+
</template>
|
|
73
|
+
</VibeDroppable>
|
|
74
|
+
|
|
75
|
+
<VibeDroppable group="cards" @drop="({ payload }) => moveCard(payload, done, todo)">
|
|
76
|
+
<h5>Done</h5>
|
|
77
|
+
<VibeDraggable v-for="card in done" :key="card.id" :payload="card" group="cards">
|
|
78
|
+
<div class="card mb-2 p-2">{{ card.title }}</div>
|
|
79
|
+
</VibeDraggable>
|
|
80
|
+
</VibeDroppable>
|
|
81
|
+
</div>
|
|
82
|
+
</template>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Multi-source whitelist
|
|
86
|
+
|
|
87
|
+
```vue
|
|
88
|
+
<VibeDroppable :accept-groups="['todo', 'done']" group="trash">
|
|
89
|
+
<div class="trash-zone">Drop to delete</div>
|
|
90
|
+
</VibeDroppable>
|
|
91
|
+
```
|
|
@@ -39,6 +39,7 @@ interface DropdownItem {
|
|
|
39
39
|
| `shown` | - | Emitted when dropdown is fully shown |
|
|
40
40
|
| `hide` | - | Emitted when dropdown starts hiding |
|
|
41
41
|
| `hidden` | - | Emitted when dropdown is fully hidden |
|
|
42
|
+
| `component-error` | `ComponentError` | Emitted if Bootstrap JS fails to load |
|
|
42
43
|
|
|
43
44
|
## Slots
|
|
44
45
|
|
|
@@ -50,40 +51,75 @@ interface DropdownItem {
|
|
|
50
51
|
|
|
51
52
|
## Usage
|
|
52
53
|
|
|
53
|
-
###
|
|
54
|
+
### Basic Dropdown
|
|
54
55
|
|
|
55
56
|
```vue
|
|
56
57
|
<template>
|
|
57
|
-
<
|
|
58
|
-
<VibeButton @click="$refs.myDropdown.toggle()">External Toggle</VibeButton>
|
|
59
|
-
|
|
60
|
-
<VibeDropdown
|
|
61
|
-
ref="myDropdown"
|
|
62
|
-
text="Dropdown Menu"
|
|
63
|
-
:items="dropdownItems"
|
|
64
|
-
/>
|
|
65
|
-
</div>
|
|
58
|
+
<VibeDropdown text="Dropdown Menu" :items="dropdownItems" @item-click="onItemClick" />
|
|
66
59
|
</template>
|
|
60
|
+
|
|
61
|
+
<script setup>
|
|
62
|
+
const dropdownItems = [
|
|
63
|
+
{ header: true, text: 'Actions' },
|
|
64
|
+
{ text: 'Edit', href: '#edit' },
|
|
65
|
+
{ text: 'Duplicate', href: '#duplicate' },
|
|
66
|
+
{ divider: true },
|
|
67
|
+
{ text: 'Delete', href: '#delete', active: false }
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
function onItemClick({ item, index }) {
|
|
71
|
+
console.log('clicked', item.text, 'at', index)
|
|
72
|
+
}
|
|
73
|
+
</script>
|
|
67
74
|
```
|
|
68
75
|
|
|
69
|
-
###
|
|
76
|
+
### Custom Item Rendering (Slots)
|
|
77
|
+
|
|
78
|
+
The `item` and `header` slots receive `{ item, index }` so you can render rich markup while still driving the menu from the `items` array:
|
|
70
79
|
|
|
71
80
|
```vue
|
|
72
81
|
<template>
|
|
73
|
-
<VibeDropdown
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
<VibeDropdown text="Menu" :items="dropdownItems">
|
|
83
|
+
<template #item="{ item }">
|
|
84
|
+
<i :class="item.icon" class="me-2" />{{ item.text }}
|
|
85
|
+
</template>
|
|
86
|
+
</VibeDropdown>
|
|
77
87
|
</template>
|
|
88
|
+
|
|
89
|
+
<script setup>
|
|
90
|
+
const dropdownItems = [
|
|
91
|
+
{ text: 'Profile', href: '#', icon: 'bi bi-person' },
|
|
92
|
+
{ text: 'Settings', href: '#', icon: 'bi bi-gear' }
|
|
93
|
+
]
|
|
94
|
+
</script>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Programmatic Control
|
|
98
|
+
|
|
99
|
+
```vue
|
|
100
|
+
<template>
|
|
101
|
+
<div>
|
|
102
|
+
<VibeButton @click="myDropdown.toggle()">External Toggle</VibeButton>
|
|
103
|
+
|
|
104
|
+
<VibeDropdown ref="myDropdown" text="Dropdown Menu" :items="dropdownItems" />
|
|
105
|
+
</div>
|
|
106
|
+
</template>
|
|
107
|
+
|
|
108
|
+
<script setup>
|
|
109
|
+
import { ref } from 'vue'
|
|
110
|
+
const myDropdown = ref(null)
|
|
111
|
+
const dropdownItems = [
|
|
112
|
+
{ text: 'Edit', href: '#edit' },
|
|
113
|
+
{ text: 'Delete', href: '#delete' }
|
|
114
|
+
]
|
|
115
|
+
</script>
|
|
78
116
|
```
|
|
79
117
|
|
|
80
118
|
## Important Notes
|
|
81
119
|
|
|
82
120
|
**Automatic Initialization:** This component automatically initializes Bootstrap's Dropdown functionality when it is mounted, provided that Bootstrap's JavaScript is available in your project.
|
|
83
121
|
|
|
84
|
-
**Programmatic Methods:**
|
|
85
|
-
|
|
86
|
-
**Instance Exposure:** You can access the underlying Bootstrap instance via template ref using the `bsInstance` property.
|
|
122
|
+
**Programmatic Methods:** The component exposes `show()`, `hide()`, and `toggle()` via template refs for programmatic control.
|
|
87
123
|
|
|
88
124
|
## Bootstrap CSS Classes
|
|
89
125
|
|
|
@@ -27,6 +27,7 @@ Modal dialogs for lightboxes, user notifications, or custom content.
|
|
|
27
27
|
| `shown` | - | Emitted when modal is fully shown |
|
|
28
28
|
| `hide` | - | Emitted when modal starts hiding |
|
|
29
29
|
| `hidden` | - | Emitted when modal is fully hidden |
|
|
30
|
+
| `component-error` | `ComponentError` | Emitted if Bootstrap JS fails to load |
|
|
30
31
|
|
|
31
32
|
## Slots
|
|
32
33
|
|
|
@@ -59,23 +60,6 @@ const showModal = ref(false)
|
|
|
59
60
|
</script>
|
|
60
61
|
```
|
|
61
62
|
|
|
62
|
-
### Basic Modal (Data Attributes)
|
|
63
|
-
|
|
64
|
-
```vue
|
|
65
|
-
<template>
|
|
66
|
-
<div>
|
|
67
|
-
<!-- You can still use standard Bootstrap data attributes -->
|
|
68
|
-
<VibeButton variant="primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
|
|
69
|
-
Launch Modal
|
|
70
|
-
</VibeButton>
|
|
71
|
-
|
|
72
|
-
<VibeModal id="exampleModal" title="Modal Title">
|
|
73
|
-
<p>Modal body text goes here.</p>
|
|
74
|
-
</VibeModal>
|
|
75
|
-
</div>
|
|
76
|
-
</template>
|
|
77
|
-
```
|
|
78
|
-
|
|
79
63
|
### Sized Modals
|
|
80
64
|
|
|
81
65
|
```vue
|
|
@@ -106,15 +90,24 @@ const showModal = ref(false)
|
|
|
106
90
|
|
|
107
91
|
```vue
|
|
108
92
|
<template>
|
|
109
|
-
<VibeModal title="Custom Footer">
|
|
93
|
+
<VibeModal v-model="showModal" title="Custom Footer">
|
|
110
94
|
<p>Modal with custom footer buttons.</p>
|
|
111
95
|
|
|
112
96
|
<template #footer>
|
|
113
97
|
<VibeButton variant="secondary" @click="showModal = false">Close</VibeButton>
|
|
114
|
-
<VibeButton variant="primary">Save changes</VibeButton>
|
|
98
|
+
<VibeButton variant="primary" @click="save">Save changes</VibeButton>
|
|
115
99
|
</template>
|
|
116
100
|
</VibeModal>
|
|
117
101
|
</template>
|
|
102
|
+
|
|
103
|
+
<script setup>
|
|
104
|
+
import { ref } from 'vue'
|
|
105
|
+
const showModal = ref(true)
|
|
106
|
+
function save() {
|
|
107
|
+
// persist changes...
|
|
108
|
+
showModal.value = false
|
|
109
|
+
}
|
|
110
|
+
</script>
|
|
118
111
|
```
|
|
119
112
|
|
|
120
113
|
## Important Notes
|
|
@@ -123,7 +116,9 @@ const showModal = ref(false)
|
|
|
123
116
|
|
|
124
117
|
**Teleportation:** By default, this component teleports its DOM elements to the `<body>` to avoid stacking context issues. You can customize this with the `teleport` prop.
|
|
125
118
|
|
|
126
|
-
**
|
|
119
|
+
**Focus Return (WCAG 2.4.3):** When the modal closes, focus is automatically returned to the element that opened it, including for programmatically opened modals.
|
|
120
|
+
|
|
121
|
+
**Escape Hatch:** The exposed `_unsafe_bsInstance` template ref gives raw access to the underlying Bootstrap Modal instance. 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.
|
|
127
122
|
|
|
128
123
|
## Mobile Optimization
|
|
129
124
|
|
|
@@ -23,6 +23,7 @@ Hidden sidebar for navigation or additional content.
|
|
|
23
23
|
| `shown` | - | Emitted when offcanvas is fully shown |
|
|
24
24
|
| `hide` | - | Emitted when offcanvas starts hiding |
|
|
25
25
|
| `hidden` | - | Emitted when offcanvas is fully hidden |
|
|
26
|
+
| `component-error` | `ComponentError` | Emitted if Bootstrap JS fails to load |
|
|
26
27
|
|
|
27
28
|
## Slots
|
|
28
29
|
|
|
@@ -70,7 +71,9 @@ const showOffcanvas = ref(false)
|
|
|
70
71
|
|
|
71
72
|
**Teleportation:** By default, this component teleports its DOM elements to the `<body>` to avoid stacking context issues. You can customize this with the `teleport` prop.
|
|
72
73
|
|
|
73
|
-
**
|
|
74
|
+
**Focus Return (WCAG 2.4.3):** When the offcanvas closes, focus is automatically returned to the element that opened it, including for programmatically opened panels.
|
|
75
|
+
|
|
76
|
+
**Escape Hatch:** The exposed `_unsafe_bsInstance` template ref gives raw access to the underlying Bootstrap Offcanvas instance. 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.
|
|
74
77
|
|
|
75
78
|
## Mobile Optimization
|
|
76
79
|
|