diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ff135924a..23b2d282a 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - "packages/bootstrap-vue-next": "0.40.5", - "packages/nuxt": "0.40.5" + "packages/bootstrap-vue-next": "0.40.6", + "packages/nuxt": "0.40.6" } diff --git a/apps/docs/.vitepress/plugins/demo-container.ts b/apps/docs/.vitepress/plugins/demo-container.ts index 0731c2578..d52937cab 100644 --- a/apps/docs/.vitepress/plugins/demo-container.ts +++ b/apps/docs/.vitepress/plugins/demo-container.ts @@ -1,6 +1,7 @@ import type {MarkdownEnv, MarkdownRenderer} from 'vitepress' import type {RuleBlock} from 'markdown-it/lib/parser_block.mjs' import path from 'path' +import fs from 'fs' // This plugin is inspired by vitepress' snippet plugin and must run before it to work // https://vitepress.dev/guide/markdown.html#import-code-snippets @@ -41,18 +42,32 @@ export const demoContainer = (md: MarkdownRenderer, srcDir: string) => { const {filepath, extension, region, lines, lang, title} = rawPathToToken(rawPath) const component = isDemo ? `<${title.substring(0, title.indexOf('.'))}/>` : '' + // Resolve path early so it can be used in the prefix token + const {realPath, path: _path} = state.env as MarkdownEnv + const resolvedPath = path.resolve(path.dirname(realPath ?? _path), filepath) + + // Read the source code file during build time + let fullFileContent = '' + try { + fullFileContent = fs.readFileSync(resolvedPath, 'utf-8') + } catch (error) { + // eslint-disable-next-line no-console + console.warn(`Failed to read source code from ${resolvedPath}:`, error) + } + + // Base64 encode the full file content for StackBlitz (always complete Vue file) + // Use explicit UTF-8 encoding to preserve Unicode characters like "é", "–", etc. + const encodedFullFile = Buffer.from(fullFileContent, 'utf8').toString('base64') + state.line += 1 const prefixToken = state.push('html_block', '', 0) - prefixToken.content = `${component} diff --git a/apps/docs/src/docs/demo/ModalHeaderCloseBSV.vue b/apps/docs/src/docs/demo/ModalHeaderCloseBSV.vue new file mode 100644 index 000000000..54fc1e729 --- /dev/null +++ b/apps/docs/src/docs/demo/ModalHeaderCloseBSV.vue @@ -0,0 +1,6 @@ + diff --git a/apps/docs/src/docs/demo/ModalHeaderCloseBSVN.vue b/apps/docs/src/docs/demo/ModalHeaderCloseBSVN.vue new file mode 100644 index 000000000..7915bcca4 --- /dev/null +++ b/apps/docs/src/docs/demo/ModalHeaderCloseBSVN.vue @@ -0,0 +1,9 @@ + diff --git a/apps/docs/src/docs/demo/ModalMessageBox.vue b/apps/docs/src/docs/demo/ModalMessageBox.vue index 562ee8d8c..961d15f76 100644 --- a/apps/docs/src/docs/demo/ModalMessageBox.vue +++ b/apps/docs/src/docs/demo/ModalMessageBox.vue @@ -6,19 +6,19 @@ diff --git a/apps/docs/src/docs/migration-guide.md b/apps/docs/src/docs/migration-guide.md index bb7797298..aa1733124 100644 --- a/apps/docs/src/docs/migration-guide.md +++ b/apps/docs/src/docs/migration-guide.md @@ -607,10 +607,163 @@ flex utility classes. See their [documentation](https://getbootstrap.com/docs/5. ### BModal - + `footer-tag` and `header-tag` + +#### Removed Global Modal Management + +**$bvModal instance methods are deprecated:** + +- `this.$bvModal.show(id)` → Use `useModal` composable or template refs with `.show()` +- `this.$bvModal.hide(id)` → Use `useModal` composable or template refs with `.hide()` +- `this.$bvModal.msgBoxOk()` → Use `useModal().create()` with `ok-only` prop - see [below](#replacement-for-modal-message-boxes) +- `this.$bvModal.msgBoxConfirm()` → Use `useModal().create()`- see [below](#replacement-for-modal-message-boxes) + +**$root event system is deprecated:** + +- `$root.$emit('bv::show::modal', id)` → Use composables or template refs +- `$root.$emit('bv::hide::modal', id)` → Use composables or template refs +- `$root.$emit('bv::toggle::modal', id)` → Use composables or template refs + +**Alternative approaches:** + +Please see [useModal](/docs/composables/useModal) and [useToggle](/docs/composables/useToggle) for alternative methods of +accessing modals globally. + +#### Modal Event System Changes + +The event system has been significantly updated in BootstrapVueNext: + +There is no concept of listening to modal changes via `$root` events. + +**New Events**: BootstrapVueNext adds several new events not present in BootstrapVue: + +- `backdrop` - Emitted when the backdrop is clicked +- `esc` - Emitted when the Esc key is pressed +- `show-prevented`, `hide-prevented`, `toggle-prevented` - Emitted when actions are prevented + +**Event Object Changes**: The event object structure has changed: + +- `BvModalEvent` is now `BvTriggerableEvent` +- `vueTarget` property is **no longer available** - use template refs or the `target` property instead +- Event properties are now more consistent across all BootstrapVueNext components + +**Trigger Values**: The `trigger` property values have been simplified: + +- BootstrapVue: `'headerclose'` → BootstrapVueNext: `'close'` +- All other trigger values remain the same: `'ok'`, `'cancel'`, `'esc'`, `'backdrop'` + +#### Modal Props Changes + +**Renamed props:** + +- `hide-header-close` → `no-header-close` +- `hide-footer` → `no-footer` +- `hide-header` → `no-header` +- `hide-backdrop` → `no-backdrop` +- `no-enforce-focus` → `no-trap` +- `title-sr-only` → `title-visually-hidden` + +**Removed props (not implemented in BootstrapVueNext):** + +- `aria-label` - Use standard HTML attributes directly on the component. **Note**: Unlike BootstrapVue, BootstrapVueNext does not automatically remove `aria-labelledby` when `aria-label` is present. If using `aria-label`, set `no-header="true"` to prevent conflicts, or ensure your `aria-label` is descriptive enough to work alongside `aria-labelledby` +- `auto-focus-button` - Use the `focus` prop with values `'ok'`, `'cancel'`, or `'close'` +- `ignore-enforce-focus-selector` - Use `no-trap` to disable focus trapping entirely +- `return-focus` - Focus return is handled automatically by the focus trap system +- `static` - All modals use teleport by default. Use `teleport-disabled` for local rendering See the [v-html](#v-html) section for information on deprecation of the `cancel-title-html`, `ok-title-html`, and `title-html` props. +**New props in BootstrapVueNext:** + +- `teleport-to` - Specify where the modal should be teleported (default: `'body'`) +- `teleport-disabled` - Render modal in place instead of teleporting +- `body-scrolling` - Allow body scrolling while modal is open +- `backdrop-first` - Control backdrop animation timing +- `no-trap` - Disable focus trapping (replaces `no-enforce-focus`) +- `focus` - Enhanced focus control replacing `auto-focus-button` +- `title-visually-hidden` - Hide title visually but keep for screen readers +- `header-close-variant` - Control close button variant +- `header-close-label` - Accessibility label for close button +- `lazy` - **Available in BootstrapVueNext** - When set, the content will not be mounted until opened +- `no-fade` - **Available in BootstrapVueNext** - Disables animation (alias for `no-animation`) +- `no-animation` - Disables animation +- `unmount-lazy` - When set and `lazy` is true, content will be unmounted when closed +- `initial-animation` - When set, enables the initial animation on mount + +**Changed behavior:** + +- `header-close-content` - This prop is **removed** in BootstrapVueNext. In BootstrapVue, this prop allowed customizing the close button content (defaulted to `'×'`). In BootstrapVueNext, use the `header-close` slot instead, which provides more flexibility than the simple string prop: + +<<< FRAGMENT ./demo/ModalHeaderCloseBSV.vue#template{vue-html} + +<<< FRAGMENT ./demo/ModalHeaderCloseBSVN.vue#template{vue-html} + +#### Modal Slot changes + +[BootstrapVue](https://bootstrap-vue.github.io/bootstrap-vue/docs/components/modal#custom-rendering-with-slots) provides different slots to configure some pieces of the modal component. These slots are slightly different in [BootstrapVueNext](/docs/components/modal.html#comp-reference-bmodal-slots): + +| BootstrapVue | BootstrapVueNext | +| ------------------ | ---------------- | +| default | default | +| modal-backdrop | backdrop | +| modal-cancel | cancel | +| modal-footer | footer | +| modal-header | header | +| modal-header-close | header-close | +| modal-ok | ok | +| modal-title | title | + +#### Modal Slot Scoped Variables Changes + +The scoped variables available to modal slots have changed between BootstrapVue and BootstrapVueNext: + +**BootstrapVue slot scope:** + +```typescript +{ + ok: () => void, // Closes modal with trigger 'ok' + cancel: () => void, // Closes modal with trigger 'cancel' + close: () => void, // Closes modal with trigger 'headerclose' + hide: (trigger?: string) => void, // Closes modal with custom trigger + visible: boolean // Boolean visibility state +} +``` + +**BootstrapVueNext slot scope (BModalSlotsData):** + +```typescript +interface BModalSlotsData extends ShowHideSlotsData { + cancel: () => void // Closes modal with trigger 'cancel' + close: () => void // Closes modal with trigger 'close' (note: 'close' not 'headerclose') + ok: () => void // Closes modal with trigger 'ok' +} + +interface ShowHideSlotsData { + id: string // Modal ID string + show: () => void // Show the modal + hide: (trigger?: string, noTriggerEmit?: boolean) => void // Enhanced hide method + toggle: () => void // Toggle modal visibility + active: boolean // Boolean - true when modal is active/visible + visible: boolean // Boolean - same as active, for compatibility +} +``` + +**Key changes:** + +- **New properties**: `id`, `show()`, `toggle()` are now available +- **Enhanced `hide()` method**: Now accepts optional `noTriggerEmit` parameter +- **Trigger value change**: `close()` now emits trigger `'close'` instead of `'headerclose'` +- **Dual visibility properties**: Both `active` and `visible` provide the same visibility state + +#### Modal Z-Index and Stacking Changes + +BootstrapVueNext has completely rewritten modal stacking: + +- **Automatic z-index management**: No manual z-index calculation needed +- **CSS variables for stacking**: Uses `--b-position`, `--b-inverse-position`, `--b-count` +- **Stack positioning classes**: Automatically applies `.stack-position-*` classes +- **Multiple modal support**: Enhanced support for multiple modals with proper layering + #### Replacement for Modal Message boxes [BootstrapVue](https://bootstrap-vue.github.io/bootstrap-vue/docs/components/modal#modal-message-boxes) provided two methods on the `this.$bvModal` object called `msgBoxOk` and `msgBoxConfirm`. @@ -635,19 +788,33 @@ The `create` method accepts all properties defined on See [Show and Hide](#show-and-hide) shared properties. -#### Replacement for Modal slots +#### Modal Focus Management Changes -[BootstrapVue](https://bootstrap-vue.github.io/bootstrap-vue/docs/components/modal#custom-rendering-with-slots) provides different slots to configure some pieces of the modal component. These slots are slightly different in [BootstrapVueNext](/docs/components/modal.html#comp-reference-bmodal-slots): +BootstrapVueNext uses a modern focus trap system with enhanced accessibility: -| BootstrapVue | BootstrapVueNext | -| ------------------ | ---------------- | -| default | default | -| modal-title | title | -| modal-header | header | -| modal-footer | footer | -| modal-ok | ok | -| modal-cancel | cancel | -| modal-header-close | header-close | +**Enhanced focus prop**: The `focus` prop replaces `auto-focus-button` and accepts: + +- `'ok'`, `'cancel'`, `'close'` - Focus built-in buttons +- Element references, selectors, or HTMLElements for custom focus targets +- `false` to disable initial focus (not recommended for accessibility) + +**Automatic focus return**: Focus is automatically returned to the triggering element when the modal closes, +eliminating the need for: + +- `return-focus` prop - Handled automatically by focus trap +- Manual focus management in most cases + +**Focus trapping**: Enabled by default using `@vueuse/integrations/useFocusTrap`: + +- `no-trap` replaces `no-enforce-focus` to disable focus trapping +- `ignore-enforce-focus-selector` is not available - use `no-trap` if needed +- Focus automatically cycles within the modal + +**ARIA improvements**: + +- `title-visually-hidden` replaces `title-sr-only` with better semantic naming +- Standard HTML `aria-*` attributes work directly on the component +- Enhanced screen reader support with proper labeling ### BNav diff --git a/apps/docs/src/template-types.d.ts b/apps/docs/src/template-types.d.ts new file mode 100644 index 000000000..5e6c79002 --- /dev/null +++ b/apps/docs/src/template-types.d.ts @@ -0,0 +1,35 @@ +// Type declarations for template raw imports +declare module '*/templates/vite/index.html?raw' { + const content: string + export default content +} + +declare module '*/templates/vite/src/main.ts?raw' { + const content: string + export default content +} + +declare module '*/templates/vite/src/App.vue?raw' { + const content: string + export default content +} + +declare module '*/templates/vite/package.json?raw' { + const content: string + export default content +} + +declare module '*/templates/vite/vite.config.mts?raw' { + const content: string + export default content +} + +declare module '*/templates/vite/tsconfig.json?raw' { + const content: string + export default content +} + +declare module '*/templates/vite/tsconfig.node.json?raw' { + const content: string + export default content +} diff --git a/apps/docs/src/utils/commonProps.ts b/apps/docs/src/utils/commonProps.ts index fde23cf11..338f7170b 100644 --- a/apps/docs/src/utils/commonProps.ts +++ b/apps/docs/src/utils/commonProps.ts @@ -77,6 +77,16 @@ const commonProps = () => description: 'Applies one of the Bootstrap theme color variants to background of the component', }, + body: { + type: 'string', + default: undefined, + description: 'The text content of the body', + }, + bodyAttrs: { + type: 'Readonly', + default: undefined, + description: 'Attributes to be applied to the body of the component', + }, bodyBgVariant: { type: 'ColorVariant | null', default: undefined, @@ -140,7 +150,6 @@ const commonProps = () => description: 'When set, renders a single control form with a floating label. This only works for forms where the immediate children are a label and one of the supported controls. See above for details.', }, - footer: { type: 'string', default: undefined, diff --git a/apps/docs/src/vite-env.d.ts b/apps/docs/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/apps/docs/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/apps/docs/tsconfig.app.json b/apps/docs/tsconfig.app.json index 509d77fe2..2a0f406bc 100644 --- a/apps/docs/tsconfig.app.json +++ b/apps/docs/tsconfig.app.json @@ -1,6 +1,6 @@ { "extends": "@vue/tsconfig/tsconfig.dom.json", - "include": [".vitepress/**/*", "**/*.vue", "**/*.ts"], + "include": [".vitepress/**/*", "**/*.vue", "**/*.ts", "src/template-types.d.ts"], "compilerOptions": { "composite": true, "baseUrl": ".", diff --git a/packages/bootstrap-vue-next/CHANGELOG.md b/packages/bootstrap-vue-next/CHANGELOG.md index 3855ef0a5..f18f7b971 100644 --- a/packages/bootstrap-vue-next/CHANGELOG.md +++ b/packages/bootstrap-vue-next/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.40.6](https://github.com/bootstrap-vue-next/bootstrap-vue-next/compare/bootstrapvuenext-v0.40.5...bootstrapvuenext-v0.40.6) (2025-10-07) + + +### Bug Fixes + +* **BApp:** wrap our test app in BApp in main.ts to enable easy verification of useModal, etc. ([#2865](https://github.com/bootstrap-vue-next/bootstrap-vue-next/issues/2865)) ([d7d3476](https://github.com/bootstrap-vue-next/bootstrap-vue-next/commit/d7d347665f92ad910fc45fc079f304c19d91c99f)) +* **useModalOrchestrator:** circular dependency ([#2874](https://github.com/bootstrap-vue-next/bootstrap-vue-next/issues/2874)) ([c0bf12f](https://github.com/bootstrap-vue-next/bootstrap-vue-next/commit/c0bf12fe34f0671ae1b8392e6b4f9aeb605726d8)) + ## [0.40.5](https://github.com/bootstrap-vue-next/bootstrap-vue-next/compare/bootstrapvuenext-v0.40.4...bootstrapvuenext-v0.40.5) (2025-09-23) diff --git a/packages/bootstrap-vue-next/package.json b/packages/bootstrap-vue-next/package.json index f9ddff853..fff274b98 100644 --- a/packages/bootstrap-vue-next/package.json +++ b/packages/bootstrap-vue-next/package.json @@ -2,7 +2,7 @@ "name": "bootstrap-vue-next", "displayName": "BootstrapVueNext", "description": "BootstrapVueNext is an early and lovely component library for Vue 3 & Nuxt 3 based on Bootstrap 5 and Typescript.", - "version": "0.40.5", + "version": "0.40.6", "license": "MIT", "main": "./dist/bootstrap-vue-next.umd.js", "module": "./dist/bootstrap-vue-next.mjs", diff --git a/packages/bootstrap-vue-next/src/App.vue b/packages/bootstrap-vue-next/src/App.vue index b59fab74c..bef8a39fa 100644 --- a/packages/bootstrap-vue-next/src/App.vue +++ b/packages/bootstrap-vue-next/src/App.vue @@ -1,15 +1,13 @@ diff --git a/packages/bootstrap-vue-next/src/components/BModal/BModalOrchestrator.vue b/packages/bootstrap-vue-next/src/components/BModal/BModalOrchestrator.vue index 5dfcb2524..8d55f666c 100644 --- a/packages/bootstrap-vue-next/src/components/BModal/BModalOrchestrator.vue +++ b/packages/bootstrap-vue-next/src/components/BModal/BModalOrchestrator.vue @@ -7,7 +7,7 @@