Skip to content

feat(Select/Listbox/Combobox/Menu/ContextMenu/DropdownMenu/Menubar/ColorSwatchPicker): add slot prop to *Item and forceMount to *ItemIndicator#2562

Open
kricsleo wants to merge 2 commits intounovue:v2from
kricsleo:feat/select-slot-props
Open

feat(Select/Listbox/Combobox/Menu/ContextMenu/DropdownMenu/Menubar/ColorSwatchPicker): add slot prop to *Item and forceMount to *ItemIndicator#2562
kricsleo wants to merge 2 commits intounovue:v2from
kricsleo:feat/select-slot-props

Conversation

@kricsleo
Copy link
Copy Markdown
Collaborator

@kricsleo kricsleo commented Apr 1, 2026

🔗 Linked issue

resolves #2547

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

📸 Screenshots (if appropriate)

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

Summary by CodeRabbit

  • New Features
    • Item components (Select, Listbox, Combobox, Menu, DropdownMenu, ContextMenu, Menubar, ColorSwatchPicker) now forward slot props (e.g., selected/checked), letting slot content react to selection state.
    • Item indicator components gained a forceMount option to control whether indicators mount regardless of selection, improving predictable rendering.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

Expose selection-state slot props across multiple item components and make item indicator components optionally force-mounted with ref forwarding; several components now declare typed/default slot signatures and forward underlying scoped slot props to their own default slots.

Changes

Cohort / File(s) Summary
Select items & indicators
packages/core/src/Select/SelectItem.vue, packages/core/src/Select/SelectItemIndicator.vue
SelectItem now passes selected into its default slot and types an inline pointerdown handler; SelectItemIndicator adds forceMount?: boolean, uses Presence for conditional mounting, and forwards ref via useForwardExpose().
Listbox / Listbox indicators
packages/core/src/Listbox/ListboxItem.vue, packages/core/src/Listbox/ListboxItemIndicator.vue
ListboxItem defines a typed default slot and exposes selected to slot consumers; ListboxItemIndicator gained forceMount?: boolean, uses Presence for mounting, and forwards ref.
Combobox / ColorSwatchPicker
packages/core/src/Combobox/ComboboxItem.vue, packages/core/src/ColorSwatchPicker/ColorSwatchPickerItem.vue
ComboboxItem declares a typed default slot ({ selected: boolean }) and forwards underlying slot props as fallback; ColorSwatchPickerItem now captures and forwards ListboxItem slot props to its default slot.
Menu / ContextMenu / Dropdown / Menubar items
packages/core/src/Menu/MenuRadioItem.vue, packages/core/src/ContextMenu/ContextMenuCheckboxItem.vue, packages/core/src/ContextMenu/ContextMenuRadioItem.vue, packages/core/src/DropdownMenu/DropdownMenuCheckboxItem.vue, packages/core/src/DropdownMenu/DropdownMenuRadioItem.vue, packages/core/src/Menubar/MenubarCheckboxItem.vue, packages/core/src/Menubar/MenubarRadioItem.vue
Multiple item wrappers now capture the internal component's scoped slot via v-slot="slotProps" and forward those props into their own default <slot v-bind="slotProps" />; MenuRadioItem also declares a typed default slot exposing checked: boolean.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

v2

Suggested reviewers

  • zernonia

Poem

🐰 I hopped through slots both near and far,
I nibble state — now selected is the star.
Indicators mount when asked to stay,
Refs passed on, the bunnies play. 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning While issue #2547 specifically requested changes to Select and Combobox, the PR extends the feature across 14+ components (Listbox, Menu, MenuRadioItem, DropdownMenu variants, ContextMenu variants, Menubar variants, ColorSwatchPickerItem). This represents scope expansion beyond the stated requirements. Clarify whether extending slot props and forceMount across all list-item components was intentional. If unintended, revert changes to out-of-scope components and focus on Select and Combobox only per issue #2547.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR fully addresses issue #2547 requirements: selected slot prop added to SelectItem and forceMount prop added to SelectItemIndicator. However, the feature was extended to multiple components beyond Select (Combobox, Listbox, Menu variants, DropdownMenu, ContextMenu, Menubar) rather than limiting to the issue's original scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title accurately describes the main feature changes across the pull request, covering the addition of slot props to item components and forceMount to indicator components across multiple component families.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 1, 2026

Open in StackBlitz

npm i https://pkg.pr.new/reka-ui@2562

commit: 750d77d

Copy link
Copy Markdown
Contributor

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/Select/SelectItem.vue`:
- Line 180: The slot in SelectItem.vue is passing the ComputedRef isSelected
instead of the raw boolean, so update the template to pass the unwrapped value
(use isSelected.value) to satisfy the slot type contract; locate the <slot
:selected="isSelected" /> usage in SelectItem.vue and replace it so consumers
receive a plain boolean, and audit similar patterns in Radio.vue and
TooltipRoot.vue where slot props are declared as unwrapped values but the Ref is
passed—change those to pass .value as well.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2db09e47-5527-4261-8bc2-81f8365c7af1

📥 Commits

Reviewing files that changed from the base of the PR and between 4383766 and dbd6103.

📒 Files selected for processing (2)
  • packages/core/src/Select/SelectItem.vue
  • packages/core/src/Select/SelectItemIndicator.vue

Comment thread packages/core/src/Select/SelectItem.vue
@DamianGlowala
Copy link
Copy Markdown
Contributor

DamianGlowala commented Apr 2, 2026

@kricsleo thank you 💙 do you think we can apply this to combobox too (and any other select-like components I might have missed where such a feature makes sense)?

Copy link
Copy Markdown
Contributor

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/Listbox/ListboxItemIndicator.vue`:
- Line 28: The Presence prop binding mixes forced and state-driven mounting;
change the Presence usage in ListboxItemIndicator.vue to pass present and
forceMount separately by setting :present="itemContext.isSelected.value" and
:forceMount="forceMount" so Presence can correctly handle transitions; locate
the Presence element (currently using :present="forceMount ||
itemContext.isSelected.value") and replace the expression with the two props,
and apply the same pattern to other components using the same combined
expression (e.g., Toast, Tooltip, Tabs, RadioGroup, Popover, Menu, HoverCard,
Dialog, Select, Checkbox, Collapsible, Combobox).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c446a63b-d950-423f-8e1b-56898d052469

📥 Commits

Reviewing files that changed from the base of the PR and between dbd6103 and 750d77d.

📒 Files selected for processing (11)
  • packages/core/src/ColorSwatchPicker/ColorSwatchPickerItem.vue
  • packages/core/src/Combobox/ComboboxItem.vue
  • packages/core/src/ContextMenu/ContextMenuCheckboxItem.vue
  • packages/core/src/ContextMenu/ContextMenuRadioItem.vue
  • packages/core/src/DropdownMenu/DropdownMenuCheckboxItem.vue
  • packages/core/src/DropdownMenu/DropdownMenuRadioItem.vue
  • packages/core/src/Listbox/ListboxItem.vue
  • packages/core/src/Listbox/ListboxItemIndicator.vue
  • packages/core/src/Menu/MenuRadioItem.vue
  • packages/core/src/Menubar/MenubarCheckboxItem.vue
  • packages/core/src/Menubar/MenubarRadioItem.vue
✅ Files skipped from review due to trivial changes (1)
  • packages/core/src/Menubar/MenubarCheckboxItem.vue

>
<slot />
</Primitive>
<Presence :present="forceMount || itemContext.isSelected.value">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Confirm Presence accepts separate 'present' and 'forceMount' props:"
sed -n '1,120p' packages/core/src/Presence/Presence.ts

echo
echo "2) Find indicator usages that fold forceMount into :present:"
rg -n --type=vue ':present="[^"]*forceMount[^"]*"' packages/core/src

echo
echo "3) Find usages that pass :force-mount explicitly:"
rg -n --type=vue ':force-mount=' packages/core/src

Repository: unovue/reka-ui

Length of output: 3102


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Read ListboxItemIndicator.vue to verify line 28:"
cat -n packages/core/src/Listbox/ListboxItemIndicator.vue | sed -n '20,35p'

echo
echo "2) Search for :present with forceMount pattern (without file type restriction):"
rg -n ':present="[^"]*forceMount' packages/core/src --type-add 'vue:*.vue'

echo
echo "3) Check if forceMount prop is available in ListboxItemIndicator:"
rg -n 'forceMount' packages/core/src/Listbox/ListboxItemIndicator.vue

echo
echo "4) Check other :present usages in Listbox components:"
rg -n ':present=' packages/core/src/Listbox/ --type-add 'vue:*.vue'

Repository: unovue/reka-ui

Length of output: 3353


Pass forceMount to Presence as a separate prop.

The Presence component explicitly supports separate present and forceMount props to distinguish between conditional rendering (state-based) and forced rendering (programmatic control). Combining them into a single :present="forceMount || itemContext.isSelected.value" masks this distinction and prevents Presence from handling animations/transitions correctly.

Proposed fix
-  <Presence :present="forceMount || itemContext.isSelected.value">
+  <Presence
+    :present="itemContext.isSelected.value"
+    :force-mount="forceMount"
+  >

Note: This pattern appears across 23+ components in the codebase (Toast, Tooltip, Tabs, ScrollArea, RadioGroup, Popover, Menu, NavigationMenu, HoverCard, Dialog, Select, Checkbox, Collapsible, Combobox, etc.). Consider applying this fix systematically across all affected components.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Presence :present="forceMount || itemContext.isSelected.value">
<Presence
:present="itemContext.isSelected.value"
:force-mount="forceMount"
>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/Listbox/ListboxItemIndicator.vue` at line 28, The Presence
prop binding mixes forced and state-driven mounting; change the Presence usage
in ListboxItemIndicator.vue to pass present and forceMount separately by setting
:present="itemContext.isSelected.value" and :forceMount="forceMount" so Presence
can correctly handle transitions; locate the Presence element (currently using
:present="forceMount || itemContext.isSelected.value") and replace the
expression with the two props, and apply the same pattern to other components
using the same combined expression (e.g., Toast, Tooltip, Tabs, RadioGroup,
Popover, Menu, HoverCard, Dialog, Select, Checkbox, Collapsible, Combobox).

@kricsleo kricsleo changed the title feat(Select): add selected slot prop to SelectItem and forceMount to SelectItemIndicator feat(Select/Listbox/Combobox/Menu/ContextMenu/DropdownMenu/Menubar/ColorSwatchPicker): add slot prop to *Item and forceMount to *ItemIndicator Apr 2, 2026
@kricsleo
Copy link
Copy Markdown
Collaborator Author

kricsleo commented Apr 2, 2026

Added relevant features to more components.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Expose selected on <SelectItem>/<SelectItemIndicator>

2 participants