Skip to content

Changes to public composables #2425

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 51 commits into from
Apr 30, 2025
Merged

Changes to public composables #2425

merged 51 commits into from
Apr 30, 2025

Conversation

xvaara
Copy link
Contributor

@xvaara xvaara commented Dec 7, 2024

BEGIN_COMMIT_OVERRIDE

feat(BAlert)!: make act like toast, useShowHide.
feat(useShowHide): create triggerRegistry for adding external triggers (like in vBToggle)
fix: type popoverController
fix(useShowHide): focustrap off at the begining of leave, pass down the trigger to other hide emits.
fix(vBToggle): keep track of targets
fix(BPopover)!: change prop content to body to align with other components
fix(BTooltip)!: change prop content to body to align with other components
feat(usePopoverController): allow more options
fix(vBToggle): find late components, ie. inside ClientOnly
fix(useModalController)!: move props to main level, add slots
feat(usePopoverController): add slots
feat(useToastController)!: remove props obj, the parameters are flat now. Add slots, rename pos -> position
feat(useShowHide): show returns a promise, resolve on show or hide.
feat(useToggle): toggle any show/hide component
feat!: controller composables functions return promise, with id and chainable functions
feat(useModalController): add support for using syntax in ts
feat(BModal): add okClass and cancelClass to add classes to the buttons.
feat(useModalController)!: change of api, check the docs
fix: inline functional style to show toast,modal and dropdown
feat(useToggle): add trigger to promise resolve on hide.
fix(BCarousel): fix v-for updates

END_COMMIT_OVERRIDE

Describe the PR

Lot's of changes to composables.

Small replication

A small replication or video walkthrough can help demonstrate the changes made. This is optional, but can help observe the intended changes. A mentioned issue that contains a replication also works.

PR checklist

What kind of change does this PR introduce? (check at least one)

  • Bugfix 🐛 - fix(...)
  • Feature - feat(...)
  • ARIA accessibility - fix(...)
  • Documentation update - docs(...)
  • Other (please describe)

The PR fulfills these requirements:

  • Pull request title and all commits follow the Conventional Commits convention or has an override in this pull request body This is very important, as the CHANGELOG is generated from these messages, and determines the next version type. Pull requests that do not follow conventional commits or do not have an override will be denied

Summary by CodeRabbit

  • New Features
    • Introduced useToggle composable with documentation and Table of Contents integration.
    • Added popover orchestration component and enhanced popover/tooltip controller APIs with promise-based control.
    • Enhanced modal and toast controllers with unified create methods and richer lifecycle management.
    • Expanded BAlert, BModal, BToast, and BPopover components with new props for customization and accessibility.
    • Added new slot props and standardized event typings for improved slot usage and event handling.
  • Bug Fixes
    • Updated directives and components to use body prop consistently, replacing deprecated content.
    • Improved accessibility and ARIA attribute handling for toggle triggers.
  • Documentation
    • Added and updated documentation for new composables, controllers, and migration guidance.
    • Updated usage examples to reflect new APIs and prop changes.
  • Refactor
    • Unified and refactored orchestrator plugins and component APIs for consistency, extensibility, and promise-based workflows.
    • Improved internal typing and slot/event definitions for better type safety and developer experience.
    • Simplified event handling and reactive state management in components.
  • Tests
    • Updated and expanded test suites to align with new component behaviors, prop changes, and plugin integration.
  • Chores
    • Added new launch configuration for unit testing.
    • Updated TypeScript and plugin configurations for broader test coverage.

Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@xvaara xvaara requested a review from VividLemon December 7, 2024 00:58
@xvaara xvaara marked this pull request as draft December 7, 2024 01:06
@xvaara
Copy link
Contributor Author

xvaara commented Dec 7, 2024

@VividLemon @dwgray any ideas why the docs build hangs forever?

@VividLemon
Copy link
Member

@VividLemon @dwgray any ideas why the docs build hangs forever?

We'll figure it out after I'm done reviewing. Still not done yet, but I've submitted the first set of changes I'd like to see

Copy link
Member

@VividLemon VividLemon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it could be preferable to combine useModal into useModalController. But maybe not, they serve different purposes 💭 plus the naming of methods would seem like it would cause confusion

Copy link

pkg-pr-new bot commented Dec 7, 2024

bsvn-vite-ts

npm i https://pkg.pr.new/bootstrap-vue-next/bootstrap-vue-next@2425
npm i https://pkg.pr.new/bootstrap-vue-next/bootstrap-vue-next/@bootstrap-vue-next/nuxt@2425

commit: f678981

@xvaara
Copy link
Contributor Author

xvaara commented Dec 7, 2024

I wonder if it could be preferable to combine useModal into useModalController. But maybe not, they serve different purposes 💭 plus the naming of methods would seem like it would cause confusion

I was thinking of creating a useToggle that can control all the useShowHide components, that would make useModal kinda reduntant. Thoughts?

The documentation build was due to setInterval in script setup. Which is weird because I didn't add them 😄
I'm a little under the weather at the moment, so might not continue this right away.

@xvaara
Copy link
Contributor Author

xvaara commented Dec 7, 2024

@VividLemon I'm thinking of removing the support for "component" their slots don't work at the moment, we need to support all the slots for that to work, now we just render what ever is in modal.slots for example. I couldn't find a way to list components available slots, other that just putting them to a variable, so I did the current version.

Only thing I think it's useful for is to have a custom default for the bmodal and you don't want to list them in the function args.

-- Added later:

I figured why the slots of component wasn't rendering. In orchestrator if we have -tag inside the component it overrides all the default slots. Se we need to loop in extra template-tag, double the logic and add v-if="_component" to the first without template tags inside and another one with BModal and the template tags inside. Or do a render function, which you were against.

Copy link
Member

@VividLemon VividLemon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you share many same props between two instances of show?

const mySharedProps = computed(() => ({ foo: bar.value, buzz: aef.value }))
show({ ...mySharedProps.value, a: 1 })
show({ ...mySharedProps.value, b: 2 })

This would required show(computed(() => ({ ...
Is this valid syntax? Ideally both show({ title: ref() }) and show(computed(() => ({ title: ref() }))) would be valid

@xvaara
Copy link
Contributor Author

xvaara commented Dec 12, 2024

How do you share many same props between two instances of show?

const mySharedProps = computed(() => ({ foo: bar.value, buzz: aef.value }))
show({ ...mySharedProps.value, a: 1 })
show({ ...mySharedProps.value, b: 2 })

This would required show(computed(() => ({ ... Is this valid syntax? Ideally both show({ title: ref() }) and show(computed(() => ({ title: ref() }))) would be valid

They should all work, I haven't tested all. But the { var: ref() } part is only typed for title and body at the moment and it goest through toValue inside the watch. Maybe I'll loop all the props to be able to be refs... I'll test next week

@VividLemon
Copy link
Member

How do you share many same props between two instances of show?

const mySharedProps = computed(() => ({ foo: bar.value, buzz: aef.value }))
show({ ...mySharedProps.value, a: 1 })
show({ ...mySharedProps.value, b: 2 })

This would required show(computed(() => ({ ... Is this valid syntax? Ideally both show({ title: ref() }) and show(computed(() => ({ title: ref() }))) would be valid

They should all work, I haven't tested all. But the { var: ref() } part is only typed for title and body at the moment and it goest through toValue inside the watch. Maybe I'll loop all the props to be able to be refs... I'll test next week

show(ref({ foo: 'bar' })
show({foo: ref('bar') })
show(() => ({ foo: props.bar }))
show(computed(() => ({ ...computedValue.value }))

I think for simplicity just the root should be MaybeRef show({foo: ref('bar') }) Might be too much overhead.

How this would work with values to be updated, I'm not sure yet.

MaybeRefOrGetter has the limitation of it's implicitly readonly. So we should probably do MaybeRef with toRef so if it's not a ref we can mutate the modelValue, and when it is, we can still update the modelValue

@xvaara
Copy link
Contributor Author

xvaara commented Dec 14, 2024

export type MaybeRefOrGetter<T = any> = MaybeRef<T> | ComputedRef<T> | (() => T);

it's not readonly

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/bootstrap-vue-next/src/components/BToast/BToast.vue (1)

50-50: Remove commented-out code.

This commented-out code is no longer needed as it's been replaced by the new implementation.

-        <!-- <BCloseButton  class="ms-auto" @click="hide('close')" /> -->
packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue (2)

195-196: Resolve the TODO comment about unused 'solid' prop

There's a TODO comment indicating the 'solid' prop is never used, yet it's defined in the props (line 138). This should be addressed by either implementing the usage of this prop or removing it.

-// TODO solid is never used
+// Apply solid styles to alert if needed
 const countdownLength = computed(() =>
   typeof modelValue.value === 'boolean' ? 0 : modelValue.value
 )

Or remove the prop if truly not needed:

-  solid: false,

231-240: Consider implementing color classes or remove commented code

There's commented out code related to color classes which suggests incomplete implementation. Either implement the color variant classes functionality or remove the commented code to keep the codebase clean.

-// const colorClasses = useColorVariantClasses(props)
 const computedClasses = computed(() => [
-  // colorClasses.value,
   {
     [`alert-${props.variant}`]: props.variant !== null,
     'alert-dismissible': props.dismissible && !(slots.close || props.closeContent),
     'show': isVisible.value,
     'fade': !computedNoAnimation.value,
   },
 ])
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d98f71 and 0186ec0.

📒 Files selected for processing (2)
  • packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue (1 hunks)
  • packages/bootstrap-vue-next/src/components/BToast/BToast.vue (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (20)
packages/bootstrap-vue-next/src/components/BToast/BToast.vue (9)

14-14: Consistent display style added to root element.

Adding style="display: block" to the root element ensures consistent rendering across browsers, aligning with similar changes made to other components like BModal and BDropdown.


25-29: Improved slot binding with shared context.

The title slot now receives reactive state and control methods via the sharedSlots binding, providing slot content with access to component state and methods like toggle, show, and hide.


30-49: Enhanced close button customization.

The close button implementation has been significantly improved to:

  1. Conditionally render either a BButton (for custom content) or a BCloseButton (default)
  2. Support customization via new props (closeClass, closeContent, closeVariant, closeLabel)
  3. Maintain the ms-auto class for proper alignment

This provides much more flexibility for customizing the close button while maintaining accessibility.


53-86: Improved toast body structure with flex layout.

The toast body has been restructured to:

  1. Use a flex container for better alignment
  2. Conditionally render a close button in the body when there's no header
  3. Apply the same customization options as the header close button
  4. Use consistent styles and behavior

This improves the layout consistency and provides better support for various usage patterns.


108-109: Updated type imports for better standardization.

Type imports have been updated to use the standardized types from ComponentEmits and ComponentSlots, aligning with the broader refactoring effort to standardize component interfaces across the library.


124-127: Added new props for close button customization.

New props with sensible defaults:

  • closeClass: for additional styling
  • closeContent: for custom text/content
  • closeLabel: for screen reader accessibility (defaulting to "Close")
  • closeVariant: for button styling (defaulting to "secondary")

These provide more flexibility while maintaining backward compatibility.


176-178: Updated emit and slot definitions for better type safety.

The component now uses the standardized type definitions for emits and slots, improving type safety and consistency across the component library.


265-272: Added sharedSlots computed property for consistent slot API.

This computed property creates a unified object with methods and state to pass to slots, making the slot API consistent and predictable. It provides slots with access to:

  • Control methods: toggle, show, hide
  • State information: id, visible, active

This follows the pattern being adopted across components for more consistent slot interfaces.


295-297: Added CSS for custom close button.

The .btn-close-custom class ensures proper styling for the custom close button in the toast body, using Bootstrap CSS variables for consistency with the design system.

packages/bootstrap-vue-next/src/components/BAlert/BAlert.vue (11)

2-6: Good use of conditional rendering for the Transition wrapper

The transition wrapper now properly conditionally renders based on renderRef || contentShowing and uses dynamic binding for transition properties. The appear attribute correctly uses visibility props which provides better initial animation control.


7-17: Improved accessibility with conditional ARIA attributes

The alert container now uses v-show instead of v-if for better performance when toggling visibility. The addition of dynamic id, tabindex="0", and conditional ARIA attributes based on visibility and status mode significantly improves accessibility compliance.


18-48: Structured header component with flexible content options

The header implementation now provides better structure with conditional rendering, proper slot binding, and flexible content options. The close button handling is well-implemented with both custom and default options.


49-83: Well-structured body content with improved slot handling

The body content implementation uses a computed tag based on link status and provides good flexibility for content via slots or props. The duplicate close button logic for when there's no header is a good addition for consistent UX.


84-95: Progress bar implementation with proper props binding

The progress bar is correctly conditionally rendered based on modelValue type and progressProps. All necessary props are properly passed through to the BProgress component.


182-193: Good integration of useShowHide composable

The integration of the useShowHide composable centralizes visibility logic, provides consistent show/hide behavior, and improves maintainability. The destructured properties are used effectively throughout the component.


242-252: Well-implemented modelValue watcher with event handling

The watch on modelValue correctly handles numeric values by emitting events and restarting the countdown. The event cancellation handling is properly implemented with preventable show events.


254-261: Proper lifecycle management in isActive watcher

The watch on isActive correctly handles the alert lifecycle by hiding it when the countdown is done and not paused. It properly resets the modelValue and stops the countdown.


262-269: Comprehensive slot props for consistent slot behavior

The sharedSlots computed property provides a consistent set of slot props across all slots, making the component more predictable and easier to use with custom slot content.


271-279: Complete set of exposed methods for external control

The defineExpose call includes all necessary methods for external control of the component, providing a comprehensive API for parent components to interact with the alert.


283-285: Performance improvement for progress bar transitions

Disabling the progress bar transition improves performance during countdown by preventing unnecessary animation overhead.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/docs/src/docs/migration-guide.md (1)

730-730: Remove redundant blank lines
There are multiple consecutive blank lines here, which trigger MD012 in markdownlint. Please collapse to a single blank line to keep the document clean.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

730-730: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0186ec0 and 00e9079.

📒 Files selected for processing (6)
  • apps/docs/src/docs/migration-guide.md (2 hunks)
  • packages/bootstrap-vue-next/src/components/BAccordion/BAccordionItem.vue (2 hunks)
  • packages/bootstrap-vue-next/src/components/BDropdown/BDropdown.vue (3 hunks)
  • packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue (9 hunks)
  • packages/bootstrap-vue-next/src/composables/useShowHide.ts (11 hunks)
  • packages/bootstrap-vue-next/src/types/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/bootstrap-vue-next/src/components/BAccordion/BAccordionItem.vue
  • packages/bootstrap-vue-next/src/components/BDropdown/BDropdown.vue
  • packages/bootstrap-vue-next/src/components/BPopover/BPopover.vue
  • packages/bootstrap-vue-next/src/types/index.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/bootstrap-vue-next/src/composables/useShowHide.ts (3)
packages/bootstrap-vue-next/src/composables/useModal/index.ts (2)
  • hide (53-55)
  • show (50-52)
packages/bootstrap-vue-next/src/utils/keys.ts (1)
  • globalShowHideStorageInjectionKey (175-176)
packages/bootstrap-vue-next/src/utils/dom.ts (1)
  • isVisible (32-47)
🪛 markdownlint-cli2 (0.17.2)
apps/docs/src/docs/migration-guide.md

730-730: Multiple consecutive blank lines
Expected: 1; Actual: 2

(MD012, no-multiple-blanks)

🔇 Additional comments (26)
apps/docs/src/docs/migration-guide.md (3)

603-603: Consistent prop rename for BPopover section
The migration guide now correctly notes that the content prop has been renamed to body in the BPopover component. This aligns with the code changes in the props and tests. Please verify that any example code or fragments under BPopover reference body instead of content.


726-726: Update v-html deprecation reference in BTooltip
A deprecation note for the html prop has been added under BTooltip pointing to the #v-html section. Ensure that the anchor link is valid and that the v-html guidance covers all tooltip-related HTML deprecations.


728-728: Consistent prop rename for BTooltip section
The guide now reflects the contentbody rename for BTooltip. Confirm that code examples, demos, and the API reference for BTooltip all use body and not the deprecated content.

packages/bootstrap-vue-next/src/composables/useShowHide.ts (23)

178-180: Promise-based control flow for state management

These private variables manage promise resolution state for the new asynchronous API. This aligns with the PR's objective to introduce promise-based control flows for composables.


181-185: Improved show function with promise-based return

The show function now returns a Promise, allowing for better async control flow. The early return cases are correctly handled, preventing redundant operations when the component is already visible.


186-189: Promise initialization pattern for show operation

This creates a new promise and stores the resolver function for later resolution, which is a clean pattern for async operations.


204-205: Promise resolution for prevented show events

Properly handles rejection cases by resolving the promise with false when show events are prevented.


214-224: Simplified animation handling logic

The condition for animation has been simplified to check both local animation state and delay configuration in one statement, making the code more maintainable.


238-242: Fixed delay property access and promise return

Correctly handles the different types of delay prop (number or object) and returns the promise at the end of the function.


244-262: Promise-based hide function with trigger context

The hide function now:

  1. Returns a Promise
  2. Records the trigger that caused the hide operation
  3. Properly handles early returns and promise resolution for prevented events
  4. Provides clearer error messages with trigger context

This implementation aligns with the PR objective to improve lifecycle management and event handling.


267-268: Conditional trigger event emission

Added control over trigger event emission through the noTriggerEmit parameter, which prevents redundant events when hide is called programmatically.


273-282: Improved hide prevention with trigger context

Enhances the hide prevention logic by including the trigger information in the event and handling promise resolution appropriately.


283-283: Early focus trap deactivation

Disables the focus trap as soon as hide begins, improving accessibility by releasing focus before animation completes.


308-309: Promise return from hide function

Properly returns the promise at the end of the hide function, allowing consumers to await the completion of the hide operation.


313-324: Promise-based toggle function

Reimplemented the toggle function to return a Promise, properly handling event prevention and delegating to either show or hide as appropriate. The resolveOnHide parameter provides flexibility for controlling when the promise resolves.


325-337: Added trigger-specific toggle function

Separates the toggle logic into two functions:

  1. toggle - For programmatic control with promise return
  2. triggerToggle - For event-based toggles from UI elements

This separation improves the API by providing specialized functions for different use cases.


338-355: Added trigger registry system

Implements a robust system for registering and unregistering toggle triggers, managing event listeners, and handling accessibility attributes. This is a significant enhancement that supports the new orchestrator components mentioned in the PR.


357-366: Enhanced component registration with trigger support

Extends the global registry registration to include the new trigger management functions and component instance, improving the overall architecture and making these functions available throughout the application.


367-371: Accessibility improvements for trigger elements

Implements ARIA attributes and class toggling for trigger elements, making the components more accessible by providing proper state indication to assistive technologies.


373-377: Synchronized trigger state on model changes

Ensures all registered triggers correctly reflect the current state whenever the model value changes, maintaining consistency between UI state and component state.


380-383: Improved cleanup of trigger event listeners

Properly cleans up all registered trigger event listeners on component unmount, preventing memory leaks and ghost events.


429-434: Properly sequenced focus trap and event emission

Uses requestAnimationFrame and nextTick to ensure the focus trap is activated and the 'shown' event is emitted in the correct sequence after animations complete.


435-439: Conditional promise resolution based on operation type

Implements the _resolveOnHide flag to control when promises resolve - either immediately after the show animation or deferred until the component is hidden. This supports more complex use cases like confirmation dialogs.


445-445: Focus trap deactivation on leave

Ensures the focus trap is deactivated at the beginning of the leave transition, improving accessibility by releasing focus at the right moment.


453-453: Enhanced hidden event with trigger context

Passes the trigger information to the 'hidden' event, allowing consumers to determine what caused the component to hide, which is valuable for analytics and complex interaction flows.


466-468: Promise cleanup after hide completion

Properly resolves and cleans up the promise and resolver after the hide animation completes, preventing memory leaks and ensuring the composable can be reused.

VividLemon
VividLemon previously approved these changes Apr 29, 2025
Copy link
Member

@VividLemon VividLemon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xvaara If you think this looks fine, please double check to review, then please squash merge with the correct conventional commits message with all of the breaking changes here. Other than that reservation, I think this looks good.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/docs/src/docs/composables/useToggle.md (1)

34-35: Add article "a" before "particular component" and fix compound sentence.

Small grammatical improvements would make this paragraph clearer.

-You can also provide an id to get particular component and show/hide it. Currently, we do not support using CSS selector to
-find modal since the component in lazy mode may not render at page initial. If the modal component does not exist and you attempt to call any of the exposed methods, the methods will safely no-op without throwing errors.
+You can also provide an id to get a particular component and show/hide it. Currently, we do not support using CSS selector to
+find modal since the component in lazy mode may not render at page initial. If the modal component does not exist and you attempt to call any of the exposed methods, the methods will safely no-op without throwing errors.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~35-~35: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...l. If the modal component does not exist and you attempt to call any of the exposed ...

(COMMA_COMPOUND_SENTENCE)

🧹 Nitpick comments (4)
apps/docs/src/docs/composables/useToggle.md (4)

5-6: Add spacing after comma and fix grammatical issues.

There's a missing space after the comma in "show,hide" and the sentence structure could be improved.

-You can use `useToggle` to get the closest toggleable component in **child component** and show,hide or toggle it. It can also be supplied a target id to show, hide or toggle a specific component
+You can use `useToggle` to get the closest toggleable component in a **child component** and show, hide, or toggle it. It can also be supplied a target id to show, hide, or toggle a specific component.

69-69: Fix typo and clarify promise resolution behavior.

There's a spelling error and the paragraph would benefit from more structured explanation.

-The `show` and `toggle` methods take a boolean argument to control wether to resolve the promise on show (`false`) or on hide (`true`). On `show` the promise resolves to `true` when shown and to `'show-prevented'` if show is prevented. On `hide` the promise resolves to the trigger that caused the hide event. The promise can be awaited to get the result.
+The `show` and `toggle` methods take a boolean argument to control whether to resolve the promise on show (`false`) or on hide (`true`). The promise resolves as follows:
+- When using `show()`: resolves to `true` when shown successfully or to `'show-prevented'` if show is prevented
+- When using `hide()`: resolves to the trigger string that caused the hide event
+- The promise can be awaited to get the result

84-95: Enhance the example to show both Promise patterns.

The example demonstrates .then() but not async/await, which was mentioned as a possibility. Adding both patterns would be more educational.

const toggle = useToggle('toggleTest')
async function testToggle() {
-  toggle.show(true).then((e) => {
-    if (e === 'ok') {
-      console.log('ok pressed')
-    } else {
-      console.log('closed with', e)
-    }
-  })
+  // Using Promise.then() pattern
+  toggle.show(true).then((result) => {
+    if (result === 'ok') {
+      console.log('OK button pressed')
+    } else {
+      console.log('Modal closed with reason:', result)
+    }
+  })
+  
+  // Or using async/await pattern
+  // const result = await toggle.show(true)
+  // if (result === 'ok') {
+  //   console.log('OK button pressed')
+  // } else {
+  //   console.log('Modal closed with reason:', result)
+  // }
}

118-130: Add error handling to the Promise implementation.

The real-world implementation should include error handling for a robust async flow.

async function testToggle() {
  reason.value = ''
  toggle
    .show(true)
    .then((e) => {
      if (e === 'ok') {
        console.log('ok pressed')
      } else {
        console.log('closed with', e)
      }
      reason.value = e
    })
+    .catch((error) => {
+      console.error('An error occurred:', error)
+      reason.value = 'error'
+    })
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a77dcb and a6d73e4.

📒 Files selected for processing (5)
  • apps/docs/src/docs/composables/useToggle.md (1 hunks)
  • packages/bootstrap-vue-next/src/composables/useShowHide.ts (11 hunks)
  • packages/bootstrap-vue-next/src/composables/useToggle/index.ts (1 hunks)
  • packages/bootstrap-vue-next/src/utils/floatingUi.ts (2 hunks)
  • packages/bootstrap-vue-next/src/utils/keys.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/bootstrap-vue-next/src/composables/useToggle/index.ts
  • packages/bootstrap-vue-next/src/utils/floatingUi.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
packages/bootstrap-vue-next/src/utils/keys.ts (2)
packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts (12)
  • ToastOrchestratorCreateParam (144-144)
  • PromiseWithToast (36-36)
  • ControllerKey (12-12)
  • ModalOrchestratorMapValue (211-225)
  • ModalOrchestratorCreateParam (245-245)
  • ModalOrchestratorCreateOptions (247-254)
  • PromiseWithModal (23-25)
  • PromiseWithModalBoolean (31-34)
  • PopoverOrchestratorMapValue (178-192)
  • PopoverOrchestratorCreateParam (209-209)
  • PromiseWithPopover (47-49)
  • TooltipOrchestratorCreateParam (176-176)
packages/bootstrap-vue-next/src/types/index.ts (4)
  • ToastOrchestratorCreateParam (67-67)
  • ModalOrchestratorCreateParam (69-69)
  • PopoverOrchestratorCreateParam (68-68)
  • TooltipOrchestratorCreateParam (66-66)
packages/bootstrap-vue-next/src/composables/useShowHide.ts (3)
packages/bootstrap-vue-next/src/composables/useModal/index.ts (2)
  • hide (53-55)
  • show (50-52)
packages/bootstrap-vue-next/src/utils/keys.ts (1)
  • globalShowHideStorageInjectionKey (175-176)
packages/bootstrap-vue-next/src/utils/dom.ts (1)
  • isVisible (32-47)
🪛 LanguageTool
apps/docs/src/docs/composables/useToggle.md

[uncategorized] ~35-~35: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...l. If the modal component does not exist and you attempt to call any of the exposed ...

(COMMA_COMPOUND_SENTENCE)

🪛 markdownlint-cli2 (0.17.2)
apps/docs/src/docs/composables/useToggle.md

1-1: First line in a file should be a top-level heading
null

(MD041, first-line-heading, first-line-h1)

🔇 Additional comments (18)
packages/bootstrap-vue-next/src/utils/keys.ts (7)

18-31: Updated composable type imports align with the new lifecycle management approach.

These changes introduce new types like PromiseWithModal, PromiseWithModalBoolean, PromiseWithPopover, and PromiseWithToast for improved type safety in promise-based APIs. The naming convention change from "ShowParam" to "CreateParam" reflects the new composable API pattern discussed in the PR objectives.


148-157: Good refactoring of the show/hide component system to use promises for state management.

The RegisterShowHideFnInput interface has been enhanced to:

  1. Include the component instance for better lifecycle tracking
  2. Use promise-based methods for toggle/show/hide operations
  3. Add trigger registration methods for more flexibility

This change aligns with the PR objectives of improving composable APIs and lifecycle management.


159-168: Well-structured interface for storing show/hide component state.

The new RegisterShowHideMapValue interface properly captures the state and behavior needed for the show/hide registry. The promise return types include null to handle edge cases, and the additional noTraverse parameter in hide() provides more control over the hiding behavior.


169-174: Improved registry with Map-based storage for better performance and type safety.

The change from using a plain object to a reactive Map for storing show/hide component references offers better performance for lookups and removes string-based key limitations. This is a good practice for managing a dynamic collection of components.


220-230: Updated toast plugin with new API pattern while maintaining backward compatibility.

The toast plugin now includes:

  1. An orchestrator installation flag to detect if the required component is mounted
  2. A new promise-based create() method
  3. Properly deprecated show() method

This implementation aligns with the discussions in the PR about handling composable API design and lifecycle management.


232-249: Modal controller refactoring with improved lifecycle management.

The updated modal controller:

  1. Now uses a Map for better component instance management
  2. Exposes an orchestrator installation status flag
  3. Introduces a more flexible create() method with options for controlling the modal lifecycle
  4. Properly deprecates older methods while maintaining backward compatibility

This implementation addresses the discussions around modal lifecycle management mentioned in the PR objectives, particularly regarding whether modals should be kept alive or disposed.


251-256: Unified popover/tooltip plugin with promise-based API.

The popover plugin now:

  1. Uses a Map for tracking component instances by key
  2. Exposes distinct methods for popover and tooltip creation
  3. Includes an orchestrator installation status flag

This change aligns with the overall composable API pattern being established across other components.

packages/bootstrap-vue-next/src/composables/useShowHide.ts (11)

178-180: New promise management variables added.

These variables enable the new promise-based API, allowing asynchronous control of show/hide operations. This is a good architectural improvement that aligns with modern async patterns.


181-189: The show method now returns a Promise for better async flow control.

The method now supports early returns for efficiency and maintains proper promise state. This is a significant improvement that enables awaitable show operations and better error handling.


246-255: The hide method now returns a Promise and tracks trigger context.

Excellent enhancement to track which trigger caused the hide action. This contextual information improves debugging and enables more sophisticated UI interactions.


285-285: Focus trap deactivation moved to beginning of hide process.

Good accessibility improvement to immediately release the focus trap when hiding starts, rather than waiting for transitions to complete.


315-326: The toggle method now returns a Promise with proper resolution.

This change brings toggle in line with the other Promise-based methods and provides a clean API for async toggle operations.


340-357: New trigger registry system for improved accessibility.

This implementation properly manages ARIA attributes and CSS classes for trigger elements. Great for accessibility compliance and visual state feedback.


359-368: Enhanced global registry integration.

Now includes the component instance and trigger management functions in the registry. This improves the coordination between different parts of the UI system.


369-379: Added visibility state management for triggers.

This ensures that trigger elements correctly reflect the current state of their target components through both ARIA attributes and CSS classes.


431-442: Promise resolution logic in onAfterEnter transition hook.

The conditional resolution based on the resolveOnHide parameter provides flexibility in how promises are handled. This supports different use cases for modal interaction patterns.


455-455: Enhanced hidden event with trigger information.

Including the trigger in the hidden event provides valuable context about why the component was hidden, enabling more sophisticated event handling.


468-471: Clean promise resolution and state cleanup.

Good practice to resolve the promise with the trigger context and properly clean up state variables to prevent memory leaks.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/playground/src/components/Comps/TCarousel.vue (2)

64-78: Dynamic carousel implementation demonstrates reactive slide rendering

This new carousel example effectively demonstrates how to dynamically render slides using a reactive array. Consider adding the same configuration options as other examples (controls, indicators, interval) for consistency.

-        <BCarousel>
+        <BCarousel controls indicators :interval="5000" ride="carousel">

68-70: Using index as key may cause issues with dynamic updates

While using the array index as a key works for this example, it could cause unexpected behavior if items are reordered or removed in a real application.

- v-for="(image, index) in images"
- :key="index"
+ v-for="(image, index) in images"
+ :key="image + '-' + index"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6d73e4 and fa1bd87.

📒 Files selected for processing (2)
  • apps/playground/src/components/Comps/TCarousel.vue (1 hunks)
  • packages/bootstrap-vue-next/src/components/BCarousel/BCarousel.vue (1 hunks)
🔇 Additional comments (1)
packages/bootstrap-vue-next/src/components/BCarousel/BCarousel.vue (1)

24-24: Improved robustness with optional chaining

Adding optional chaining (?.[i]?._id) to the aria-describedby binding prevents potential runtime errors when slideValues or a specific index might be undefined or null during component initialization or dynamic updates.

@VividLemon
Copy link
Member

Lmk when you're done

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/bootstrap-vue-next/src/plugins/toastController/index.ts (2)

49-65: this‐bound methods are still fragile when the object is destructured

show, hide, and toggle are declared with the method syntax and rely on this.ref.
If a consumer later extracts the methods (const {show} = toast; show()), this becomes undefined, throwing at runtime.

The previous review highlighted this hazard, but the implementation remains unchanged.


135-137: ⚠️ Potential issue

id_self: public-type mismatch breaks type-safety

_self is the documented identifier in ToastOrchestratorParam, yet the code still reads resolvedProps.value?.id.
Callers following the public API cannot supply id, so _self always falls back to a new Symbol, making it impossible to interact with the toast by a predictable key.

-const _self = resolvedProps.value?.id || Symbol('Toast controller')
+const _self =
+  (resolvedProps.value?._self as ControllerKey | undefined) ??
+  Symbol('Toast controller')
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa1bd87 and e76a1f4.

📒 Files selected for processing (2)
  • packages/bootstrap-vue-next/src/plugins/modalController/index.ts (1 hunks)
  • packages/bootstrap-vue-next/src/plugins/toastController/index.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bootstrap-vue-next/src/plugins/modalController/index.ts
🧰 Additional context used
🧠 Learnings (1)
packages/bootstrap-vue-next/src/plugins/toastController/index.ts (3)
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
packages/bootstrap-vue-next/src/plugins/modalController/index.ts (2)

49-63: 🛠️ Refactor suggestion

Avoid context loss on destructured methods

The show, hide, and toggle methods aren't arrow functions, so they'll lose this context if destructured. This could cause subtle bugs if users extract these methods without binding.

-        show() {
+        show: () => {
           if (!this.ref) return this.set({modelValue: true})
           this.ref.show()
           return promise
         },
-        hide(trigger?: string) {
+        hide: (trigger?: string) => {
           if (!this.ref) return this.set({modelValue: false})
           this.ref.hide(trigger, true)
           return promise
         },
-        toggle() {
+        toggle: () => {
           if (!this.ref) return this.set({modelValue: !this.get()?.modelValue})
           this.ref.toggle()
           return promise
         },

150-163: 🛠️ Refactor suggestion

Watcher mutates the observed object

In the watch callback, newValue.props = undefined mutates the observed object. This could potentially create an infinite watch loop or unexpected behavior.

Consider creating a new object instead of modifying the one being watched:

-          if (newValue.props) {
-            Object.assign(v, newValue.props)
-            newValue.props = undefined
-          }
+          if (newValue.props) {
+            Object.assign(v, newValue.props)
+            // Create a new object instead of mutating newValue
+            newValue = { ...newValue, props: undefined }
+          }
🧹 Nitpick comments (1)
packages/bootstrap-vue-next/src/plugins/modalController/index.ts (1)

200-219: Well-handled deprecation of show/confirm methods

The deprecated methods include clear console warnings that guide users toward the new API pattern. The implementation preserves backward compatibility while encouraging migration to the more flexible create().show() pattern.

Consider adding a target version for removal in the deprecation message to help users plan their migration timeline.

-        '[BootstrapVueNext] useModalController: The show() method is deprecated. Use create({}).show() instead.'
+        '[BootstrapVueNext] useModalController: The show() method is deprecated and will be removed in v1.0. Use create({}).show() instead.'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e76a1f4 and d7157fa.

📒 Files selected for processing (1)
  • packages/bootstrap-vue-next/src/plugins/modalController/index.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/bootstrap-vue-next/src/plugins/modalController/index.ts (3)
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
🔇 Additional comments (7)
packages/bootstrap-vue-next/src/plugins/modalController/index.ts (7)

32-33: Great addition of the orchestrator installation check

Adding this flag helps prevent the common error of trying to use modals before the orchestrator component is mounted, which would cause silent failures. The error message in lines 120-124 clearly communicates the issue to developers.


89-98: Well-implemented Promise with cleanup

The Promise handling during modal destruction is properly implemented without using async in the constructor, which avoids unhandled rejections. The approach with nextTick and waiting for the 'hidden' event ensures the modal is completely removed from the DOM before resolving.


102-104: Excellent use of Symbol.asyncDispose

Implementing the Symbol.asyncDispose symbol allows for automatic cleanup when used with the JavaScript "using" statement, which is a modern best practice for resource management. This enables users to write code like:

using modal = await useModalController().create({title: 'My Modal'});
modal.show();
// Modal will be automatically cleaned up when the scope exits

126-134: Proper handling of components and slots

Using markRaw on components and slots is crucial to prevent Vue from making them reactive, which would cause errors or performance issues. This is a good defensive programming practice.


167-181: Good reactive handling of modelValue updates

The implementation properly handles all scenarios for modelValue updates:

  1. Calls the user's update handler
  2. Updates the ref if the input was a ref
  3. Updates the modelValue directly if it was passed as a ref
  4. Updates the internal modals map

This comprehensive approach ensures reactivity works correctly in all scenarios.


191-197: Proper use of onScopeDispose with failSilently parameter

The onScopeDispose function correctly uses the second parameter true to make it fail silently when called outside a component setup context. This is a good use of Vue 3.5+'s enhanced API.


221-227: Complete and consistent provider API

The provider API is complete, exposing both new and deprecated methods through the modal controller plugin key. This gives developers flexibility during migration.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (6)
packages/bootstrap-vue-next/src/plugins/toastController/index.ts (2)

152-159: ⚠️ Potential issue

Mutating watched object in watcher causes potential infinite loop

Setting newValue.props = undefined and newValue.pos = undefined mutates the watched source object. Since the watcher is deep, this can trigger another update cycle.

Work with a clone instead of mutating the original object:

-if (newValue.props) {
-  Object.assign(v, newValue.props)
-  newValue.props = undefined
-}
-if (newValue.pos) {
-  v.position = newValue.pos
-  newValue.pos = undefined
-}
+if (newValue.props) {
+  Object.assign(v, newValue.props)
+}
+if (newValue.pos) {
+  v.position = newValue.pos
+}

51-65: ⚠️ Potential issue

Methods will lose this context when destructured

The show(), hide(), and toggle() methods rely on this.ref and this.get() which will break if destructured (const { show } = toast; show()). This leads to runtime errors when developers use common destructuring patterns.

Consider using arrow functions that capture variables instead of relying on this:

-show() {
-  if (!this.ref) return this.set({modelValue: true})
-  this.ref.show()
-  return promise
-},
+show: () => {
+  if (!promise.ref) return promise.set({modelValue: true})
+  promise.ref.show()
+  return promise
+},
packages/bootstrap-vue-next/src/plugins/modalController/index.ts (2)

49-66: ⚠️ Potential issue

Methods will lose this context when destructured

The show(), hide(), and toggle() methods rely on this.ref and this.get() which will break if destructured. This is the same issue found in the toast controller.

Consider using arrow functions that capture variables instead of relying on this:

-show() {
-  if (!this.ref) return this.set({modelValue: true})
-  this.ref.show()
-  return promise
-},
+show: () => {
+  if (!promise.ref) return promise.set({modelValue: true})
+  promise.ref.show()
+  return promise
+},

152-155: ⚠️ Potential issue

Mutating watched object in watcher causes potential infinite loop

Setting newValue.props = undefined inside the watcher mutates the watched source object, potentially triggering additional update cycles.

Work with a clone instead of mutating the original object:

-if (newValue.props) {
-  Object.assign(v, newValue.props)
-  newValue.props = undefined
-}
+if (newValue.props) {
+  Object.assign(v, newValue.props)
+}
packages/bootstrap-vue-next/src/plugins/popoverController/index.ts (2)

52-66: ⚠️ Potential issue

Methods will lose this context when destructured

The show(), hide(), and toggle() methods rely on this.ref and this.get() which will break if destructured. This is the same issue found in the other controllers.

Consider using arrow functions that capture variables instead of relying on this:

-show() {
-  if (!this.ref) return this.set({modelValue: true})
-  this.ref.show()
-  return promise
-},
+show: () => {
+  if (!promise.ref) return promise.set({modelValue: true})
+  promise.ref.show()
+  return promise
+},

154-163: 🛠️ Refactor suggestion

Potential modification of watched objects in the watcher

While this code doesn't explicitly set properties to undefined like the other controllers, it's still manipulating properties directly on the watched object, which could lead to unwanted recursion.

Consider working with a cloned object to avoid side effects on the watched source:

-for (const key in newValue) {
-  if (key.startsWith('on')) {
-    v[key as keyof PopoverOrchestratorCreateParam] =
-      newValue[key as keyof PopoverOrchestratorCreateParam]
-  } else {
-    v[key as keyof PopoverOrchestratorCreateParam] = toValue(
-      newValue[key as keyof PopoverOrchestratorCreateParam]
-    )
-  }
-}
+// Create a clone of newValue to avoid mutating the watched object
+const clonedValue = {...newValue};
+for (const key in clonedValue) {
+  if (key.startsWith('on')) {
+    v[key as keyof PopoverOrchestratorCreateParam] =
+      clonedValue[key as keyof PopoverOrchestratorCreateParam]
+  } else {
+    v[key as keyof PopoverOrchestratorCreateParam] = toValue(
+      clonedValue[key as keyof PopoverOrchestratorCreateParam]
+    )
+  }
+}
🧹 Nitpick comments (1)
packages/bootstrap-vue-next/src/plugins/toastController/index.ts (1)

119-125: JSDoc needs to be updated to fully describe all parameters

The JSDoc comment describes the return value but doesn't document the options parameter.

Update the JSDoc to include all parameters:

/**
+ * @param {ToastOrchestratorCreateParam} obj - The toast configuration object
+ * @param {ToastOrchestratorCreateOptions} options - Additional options for toast creation
 * @returns {PromiseWithToast} Returns a promise object with methods to control the toast (show, hide, toggle, get, set, destroy)
 */
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7157fa and f678981.

📒 Files selected for processing (3)
  • packages/bootstrap-vue-next/src/plugins/modalController/index.ts (1 hunks)
  • packages/bootstrap-vue-next/src/plugins/popoverController/index.ts (1 hunks)
  • packages/bootstrap-vue-next/src/plugins/toastController/index.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
packages/bootstrap-vue-next/src/plugins/modalController/index.ts (3)
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
packages/bootstrap-vue-next/src/plugins/toastController/index.ts (3)
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/toastController/index.ts:203-208
Timestamp: 2025-04-22T20:13:41.342Z
Learning: In Vue 3.5+, the `onScopeDispose` function accepts two parameters: `function onScopeDispose(fn: () => void, failSilently?: boolean): void`. The second parameter `failSilently` controls whether to throw an error or silently ignore if the function is called outside of a component setup context.
packages/bootstrap-vue-next/src/plugins/popoverController/index.ts (1)
Learnt from: xvaara
PR: bootstrap-vue-next/bootstrap-vue-next#2425
File: packages/bootstrap-vue-next/src/plugins/popoverController/index.ts:138-140
Timestamp: 2025-04-28T22:48:46.712Z
Learning: In bootstrap-vue-next, `PopoverOrchestratorParam` includes an `id` field of type `ControllerKey` through inheritance from base types. This field is declared in the `BvControllerOptions` interface and propagated through the type hierarchy.
🧬 Code Graph Analysis (1)
packages/bootstrap-vue-next/src/plugins/popoverController/index.ts (4)
packages/bootstrap-vue-next/src/plugins/index.ts (1)
  • popoverPlugin (6-6)
packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts (9)
  • ControllerKey (12-12)
  • PopoverOrchestratorMapValue (178-192)
  • TooltipOrchestratorMapValue (146-159)
  • PromiseWithPopover (47-49)
  • PopoverOrchestratorParam (194-207)
  • TooltipOrchestratorParam (161-174)
  • PromiseWithPopoverInternal (50-59)
  • PopoverOrchestratorCreateParam (209-209)
  • TooltipOrchestratorCreateParam (176-176)
packages/bootstrap-vue-next/src/utils/classes.ts (1)
  • BvTriggerableEvent (68-90)
packages/bootstrap-vue-next/src/utils/keys.ts (1)
  • popoverPluginKey (251-256)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (15)
packages/bootstrap-vue-next/src/plugins/toastController/index.ts (5)

86-110: The destroy() method properly preserves event handlers

The implementation correctly preserves any existing onHidden handlers when adding the promise resolution handler, avoiding issues with overwriting user-supplied callbacks.


211-212: Return value properly implements the Promise with toast controller

The create function correctly returns the enhanced promise object, allowing users to both await the promise and use the fluent controller API.


205-210: Proper scope disposal for cleaning up toasts

The onScopeDispose handler correctly uses the second parameter true to silently ignore errors when called outside component setup. This was a point of discussion in previous reviews and is correctly implemented here.


214-223: Appropriate deprecation messaging for the legacy method

The deprecated show method includes a clear console warning to guide users toward the new API, while maintaining backward compatibility.


137-139:

❌ Incorrect review comment

Type inconsistency between id and _self

The code extracts id from resolvedProps.value but identifies toasts using _self internally, which can lead to type confusion.

Consider aligning the identifier names:

-const _self = resolvedProps.value?.id || Symbol('Toast controller')
+const _self = resolvedProps.value?._self || Symbol('Toast controller')

To verify this issue, check if the types match up:


🏁 Script executed:

#!/bin/bash
# Check if ToastOrchestratorParam includes an id property
grep -A 20 "export type ToastOrchestratorParam" packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts

Length of output: 768


🏁 Script executed:

#!/bin/bash
# Locate the BToastProps type definition
grep -R "export .*BToastProps" -n packages/bootstrap-vue-next/src

# Pick the first result and inspect occurrences of 'id' in that file
FILE=$(grep -R "export .*BToastProps" -n packages/bootstrap-vue-next/src | head -n1 | cut -d: -f1)
echo "Inspecting 'id' in $FILE"
grep -n "id" "$FILE"

Length of output: 2845


🏁 Script executed:

#!/bin/bash
# Locate resolvedProps usages and print context around the snippet
FILE="packages/bootstrap-vue-next/src/plugins/toastController/index.ts"
grep -n "resolvedProps" "$FILE"
echo "---- Context around lines 130-150 ----"
sed -n '130,150p' "$FILE"

Length of output: 1353


Ignore this suggestion: id vs. _self are intentionally distinct
The id prop on resolvedProps.value is a user-supplied ControllerKey (string or symbol), while _self is an internal identifier for the orchestrator (falling back to a new Symbol when no id is provided). They share the same ControllerKey type, and renaming resolvedProps.value.id to resolvedProps.value._self would be invalid (props don’t include _self). No change needed here.

Likely an incorrect or invalid review comment.

packages/bootstrap-vue-next/src/plugins/modalController/index.ts (4)

122-126: Proper validation of orchestrator installation

The check for _isOrchestratorInstalled with a helpful error message is a good practice for ensuring the plugin is properly configured before use.


84-107: Good pattern for cleaning up modal state

The destroy method properly manages the modal lifecycle - stopping the watcher, ensuring the modal is hidden, preserving event handlers, and finally removing it from the storage map.


193-199: Proper scope disposal for cleaning up modals

The onScopeDispose handler correctly uses the second parameter true to handle being called outside a component scope.


205-221: Clear deprecation approach for legacy methods

The deprecated show and confirm methods include appropriate warnings while providing backward compatibility. This supports a smooth migration path to the new API.

packages/bootstrap-vue-next/src/plugins/popoverController/index.ts (6)

31-34: Using a unified Map for both popovers and tooltips

The refactoring to use a single Map to store both popovers and tooltips simplifies the codebase and encourages code reuse. This is a good architectural decision.


91-103: Promise in destroy method correctly handles event handlers

The implementation properly preserves existing event handlers by storing the previous handler and chaining them, which prevents overwriting user callbacks.


125-129: Proper validation of orchestrator installation

The check for _isOrchestratorInstalled with a descriptive error message ensures developers have properly configured the plugin.


201-203: Clean abstraction for different component types

Creating helper functions tooltip() and popover() that wrap the create() method with the appropriate flag provides a clean abstraction for users.


191-196: Proper scope disposal for cleaning up popovers

The onScopeDispose handler correctly uses the second parameter true to handle being called outside a component scope, consistent with the other controllers.


140-141:

❌ Incorrect review comment

Accessing id property that may not be in the type definition

Based on previous discussions, there may be a discrepancy between the code accessing resolvedProps.value?.id and what's declared in the type definition.


🏁 Script executed:

#!/bin/bash
# Verify if the PopoverOrchestratorParam type includes an id field
grep -A 10 "export type PopoverOrchestratorParam" packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts
grep -A 10 "export interface BvControllerOptions" packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts || echo "BvControllerOptions not found"

Length of output: 792


Let’s verify how the id field gets injected by the orchestrator’s base types:


🏁 Script executed:

#!/bin/bash
# Search for BvControllerOptions in the entire repo
rg "BvControllerOptions" -n .

# Locate the ControllerKey type
rg "type ControllerKey" -n packages/bootstrap-vue-next/src

# Find the orchestrator setup to see what props are mixed in
rg "createOrchestrator" -n packages/bootstrap-vue-next/src
rg "createController"   -n packages/bootstrap-vue-next/src

Length of output: 321


No output was captured—let's refine our approach to locate the file. It appears the path may differ. Let’s list the directory contents under packages/bootstrap-vue-next/src/plugins to confirm the exact path.


🏁 Script executed:

#!/bin/bash
ls -R packages/bootstrap-vue-next/src/plugins

Length of output: 782


Let’s inspect how createController is imported and what resolvedProps is typed as:


🏁 Script executed:

#!/bin/bash
# Find the import of createController in popoverController
rg "createController" -n packages/bootstrap-vue-next/src/plugins/popoverController/index.ts

# Dump the first 200 lines to see how resolvedProps is defined and used
sed -n '1,200p' packages/bootstrap-vue-next/src/plugins/popoverController/index.ts

Length of output: 6436


Let’s check if id is defined on the create-param or map-value types:


🏁 Script executed:

#!/bin/bash
# Show PopoverOrchestratorCreateParam definition
grep -R "export type PopoverOrchestratorCreateParam" -n packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts -A10

# Show PopoverOrchestratorMapValue definition
grep -R "export type PopoverOrchestratorMapValue" -n packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts -A10

Length of output: 1178


To confirm whether id comes from BPopoverProps, let’s inspect the top of the orchestrator types file:


🏁 Script executed:

#!/bin/bash
sed -n '1,100p' packages/bootstrap-vue-next/src/types/ComponentOrchestratorTypes.ts

Length of output: 3813


To confirm whether id comes from BPopoverProps, let’s inspect its definition:


🏁 Script executed:

#!/bin/bash
# Show BPopoverProps to see if `id` is declared
grep -R "export interface BPopoverProps" -n packages/bootstrap-vue-next/src/types/ComponentProps.ts -A20

Length of output: 700


No issue with id access: id is defined in BPopoverProps
The PopoverOrchestratorParam type is built as Omit<BPopoverProps, 'body' | 'title' | 'modelValue'> & …, and BPopoverProps declares id?: string. Accessing resolvedProps.value?.id is therefore correct and needs no change.

Likely an incorrect or invalid review comment.

@xvaara xvaara merged commit 99718eb into main Apr 30, 2025
5 checks passed
@xvaara xvaara deleted the flatten-composables branch April 30, 2025 01:37
xvaara added a commit to xvaara/bootstrap-vue-next that referenced this pull request May 14, 2025
* upstream/main:
  chore: release main (bootstrap-vue-next#2690)
  docs(BProgress): Parity pass (bootstrap-vue-next#2689)
  fix(BTableSimple): fixed and nobordercollapse to work fixes bootstrap-vue-next#2685
  docs: fix incorrect references and missed script sections (bootstrap-vue-next#2686)
  docs: implement on this page expand/collapse with useScrollspy (bootstrap-vue-next#2679)
  chore: release main (bootstrap-vue-next#2683)
  feat(BTable): implement 'fixed' and 'noBorderCollapse' props (bootstrap-vue-next#2681)
  chore: release main (bootstrap-vue-next#2678)
  Update package.json
  fix(BFormSelect): prevent options with label from being treated as groups (bootstrap-vue-next#2666)
  fix: patch regression issue in bootstrap-vue-next#2665 (bootstrap-vue-next#2670)
  Update release-main.yaml
  chore: release main (bootstrap-vue-next#2660)
  chore: update depencies
  fix(BTabs): corrent classes on ssr (bootstrap-vue-next#2664)
  Changes to public composables (bootstrap-vue-next#2425)
  docs(BTable): parity pass (bootstrap-vue-next#2669)
xvaara added a commit to unstoppablecarl/bootstrap-vue-next that referenced this pull request May 22, 2025
feat(BAlert)!: make act like toast, useShowHide.
feat(useShowHide): create triggerRegistry for adding external triggers (like in vBToggle)
fix: type popoverController
fix(useShowHide): focustrap off at the begining of leave, pass down the trigger to other hide emits.
fix(vBToggle): keep track of targets
fix(BPopover)!: change prop content to body to align with other components
fix(BTooltip)!: change prop content to body to align with other components
feat(usePopoverController): allow more options
fix(vBToggle): find late components, ie. inside ClientOnly
fix(useModalController)!: move props to main level, add slots
feat(usePopoverController): add slots
feat(useToastController)!: remove props obj, the parameters are flat now. Add slots, rename pos -> position
feat(useShowHide): show returns a promise, resolve on show or hide.
feat(useToggle): toggle any show/hide component
feat!: controller composables functions return promise, with chainable functions
feat(useModalController): add support for using syntax in ts
feat(BModal): add okClass and cancelClass to add classes to the buttons.
feat(useModalController)!: change of api, check the docs
fix: inline functional style to show toast,modal and dropdown
feat(useToggle): add trigger to promise resolve on hide.
fix(BCarousel): fix v-for updates
xvaara added a commit to unstoppablecarl/bootstrap-vue-next that referenced this pull request May 22, 2025
commit 2a9e30b
Author: Jukka Raimovaara <roska@mentalhouse.fi>
Date:   Thu May 15 18:24:07 2025 +0300

    doc data

commit 08c89fd
Author: Jukka Raimovaara <roska@mentalhouse.fi>
Date:   Thu May 15 17:57:29 2025 +0300

    feat(BPopover): add titleClass and bodyClass, remove unneeded customClass prop since class is inherited to the same place

commit 90b578d
Author: Jukka Raimovaara <roska@mentalhouse.fi>
Date:   Wed May 14 11:39:42 2025 +0300

    feat(BToast): add noProgress prop, make progress show as default if modelValue is number. fix(useToastController): if using the deprecated show method the countdown didn't start.

commit dc85d94
Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Date:   Sun May 11 09:53:25 2025 -0500

    chore: release main (bootstrap-vue-next#2690)

    Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

commit 070cb8c
Author: David W. Gray <dwgray67@hotmail.com>
Date:   Sun May 11 07:52:30 2025 -0700

    docs(BProgress): Parity pass (bootstrap-vue-next#2689)

commit c61f532
Author: Thierry Blind <tbl0605@gmail.com>
Date:   Sun May 11 16:52:14 2025 +0200

    fix(BTableSimple): fixed and nobordercollapse to work fixes bootstrap-vue-next#2685

commit beae36f
Author: David W. Gray <dwgray67@hotmail.com>
Date:   Sun May 11 07:43:58 2025 -0700

    docs: fix incorrect references and missed script sections (bootstrap-vue-next#2686)

commit 34432d9
Author: David W. Gray <dwgray67@hotmail.com>
Date:   Sun May 11 07:42:02 2025 -0700

    docs: implement on this page expand/collapse with useScrollspy (bootstrap-vue-next#2679)

commit ce869f0
Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Date:   Wed May 7 11:16:08 2025 -0500

    chore: release main (bootstrap-vue-next#2683)

    Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

commit 9486276
Author: Mohamed Nasri <51300752+mhn147@users.noreply.github.com>
Date:   Wed May 7 09:44:38 2025 -0600

    feat(BTable): implement 'fixed' and 'noBorderCollapse' props (bootstrap-vue-next#2681)

commit a4a9294
Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Date:   Mon May 5 09:16:09 2025 -0500

    chore: release main (bootstrap-vue-next#2678)

    Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

commit 0165e94
Author: Jukka Raimovaara <roska@mentalhouse.fi>
Date:   Mon May 5 16:24:04 2025 +0300

    Update package.json

commit c1645a9
Author: Rajitha <rajithaeye@gmail.com>
Date:   Wed Apr 30 23:49:23 2025 +0530

    fix(BFormSelect): prevent options with label from being treated as groups (bootstrap-vue-next#2666)

commit 59ddc39
Author: Thierry Blind <tbl0605@gmail.com>
Date:   Wed Apr 30 20:17:34 2025 +0200

    fix: patch regression issue in bootstrap-vue-next#2665 (bootstrap-vue-next#2670)

commit d82091b
Author: Jukka Raimovaara <roska@mentalhouse.fi>
Date:   Wed Apr 30 06:01:10 2025 +0300

    Update release-main.yaml

commit 31cb4bf
Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Date:   Wed Apr 30 04:51:31 2025 +0300

    chore: release main (bootstrap-vue-next#2660)

    Co-Authored-By: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

commit 6733770
Author: Jukka Raimovaara <roska@mentalhouse.fi>
Date:   Wed Apr 30 05:15:02 2025 +0300

    chore: update depencies

commit 2b37c18
Author: Jukka Raimovaara <roska@mentalhouse.fi>
Date:   Wed Apr 30 04:36:46 2025 +0300

    fix(BTabs): corrent classes on ssr (bootstrap-vue-next#2664)

    fix(BTabs): corrent classes on ssr
    fix(BTabs): fix another recursion error

commit 99718eb
Author: Jukka Raimovaara <roska@mentalhouse.fi>
Date:   Wed Apr 30 04:20:00 2025 +0300

    Changes to public composables (bootstrap-vue-next#2425)

    feat(BAlert)!: make act like toast, useShowHide.
    feat(useShowHide): create triggerRegistry for adding external triggers (like in vBToggle)
    fix: type popoverController
    fix(useShowHide): focustrap off at the begining of leave, pass down the trigger to other hide emits.
    fix(vBToggle): keep track of targets
    fix(BPopover)!: change prop content to body to align with other components
    fix(BTooltip)!: change prop content to body to align with other components
    feat(usePopoverController): allow more options
    fix(vBToggle): find late components, ie. inside ClientOnly
    fix(useModalController)!: move props to main level, add slots
    feat(usePopoverController): add slots
    feat(useToastController)!: remove props obj, the parameters are flat now. Add slots, rename pos -> position
    feat(useShowHide): show returns a promise, resolve on show or hide.
    feat(useToggle): toggle any show/hide component
    feat!: controller composables functions return promise, with chainable functions
    feat(useModalController): add support for using syntax in ts
    feat(BModal): add okClass and cancelClass to add classes to the buttons.
    feat(useModalController)!: change of api, check the docs
    fix: inline functional style to show toast,modal and dropdown
    feat(useToggle): add trigger to promise resolve on hide.
    fix(BCarousel): fix v-for updates

commit 340edfd
Author: David W. Gray <dwgray67@hotmail.com>
Date:   Mon Apr 28 18:39:44 2025 -0700

    docs(BTable): parity pass (bootstrap-vue-next#2669)

commit 4dd6c89
Author: Jukka Raimovaara <roska@mentalhouse.fi>
Date:   Mon Apr 28 22:46:31 2025 +0300

    fix(BDropdown): don't calulcate the position when dropdown is not shown.
@coderabbitai coderabbitai bot mentioned this pull request May 24, 2025
6 tasks
@coderabbitai coderabbitai bot mentioned this pull request Jun 18, 2025
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants