Skip to content

feat(ui): add multi-select and bulk actions for packages#1672

Open
MatteoGabriele wants to merge 38 commits intonpmx-dev:mainfrom
MatteoGabriele:feat/action-bar
Open

feat(ui): add multi-select and bulk actions for packages#1672
MatteoGabriele wants to merge 38 commits intonpmx-dev:mainfrom
MatteoGabriele:feat/action-bar

Conversation

@MatteoGabriele
Copy link
Contributor

@MatteoGabriele MatteoGabriele commented Feb 26, 2026

🔗 Linked issue

resolves #1509

🧭 Context

Added a multi-select feature to the search page that allows users to select multiple packages and perform bulk actions on them. Currently supports comparing selected packages, with the possibility of adding more actions in the future.

📚 Description

  • Selection UI: Added checkboxes to package cards that appear on hover. Selected cards are visually indicated with a border highlight and checkbox state.
  • Persistent counter: New "View selected (X)" button in the toolbar shows active selections and navigates to a dedicated view for managing them.
  • Floating action bar: When items are selected, a floating action bar appears at the bottom with the selection count, primary action (Compare), and clear button.
  • Selection state management: Uses composable to maintain selections across view changes (card/table/selections view), allowing users to continue browsing while keeping their selections.
  • Selection view: It's a separate component view that retrieves each saved component similar to the Compare page request logic.
  • Accessibility: Includes aria-live announcements for selection changes and keyboard shortcuts ("b" key) to focus the action bar.

At the moment, the logic is locked at a maximum of 4 selectable items. This won't scale, but for now, to reduce complexity, it will mimic what's needed by the Compare page, which is the only current functionality available in the multi-select.
My take is to re-think this along the way, when and if another action gets added.

Screen.Recording.2026-02-27.at.18.33.58.mov

@vercel
Copy link

vercel bot commented Feb 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs.npmx.dev Ready Ready Preview, Comment Mar 1, 2026 11:58pm
npmx.dev Ready Ready Preview, Comment Mar 1, 2026 11:58pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
npmx-lunaria Ignored Ignored Mar 1, 2026 11:58pm

Request Review

@github-actions
Copy link

github-actions bot commented Feb 26, 2026

Lunaria Status Overview

🌕 This pull request will trigger status changes.

Learn more

By default, every PR changing files present in the Lunaria configuration's files property will be considered and trigger status changes accordingly.

You can change this by adding one of the keywords present in the ignoreKeywords property in your Lunaria configuration file in the PR's title (ignoring all files) or by including a tracker directive in the merged commit's description.

Tracked Files

File Note
lunaria/files/en-GB.json Localization changed, will be marked as complete. 🔄️
lunaria/files/en-US.json Source changed, localizations will be marked as outdated.
lunaria/files/it-IT.json Localization changed, will be marked as complete. 🔄️
Warnings reference
Icon Description
🔄️ The source for this localization has been updated since the creation of this pull request, make sure all changes in the source have been applied.

@codecov
Copy link

codecov bot commented Feb 27, 2026

Codecov Report

❌ Patch coverage is 62.28070% with 43 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
app/composables/usePackageSelection.ts 34.21% 19 Missing and 6 partials ⚠️
app/components/Package/ActionBar.vue 45.00% 8 Missing and 3 partials ⚠️
app/components/Package/ListToolbar.vue 50.00% 3 Missing and 1 partial ⚠️
app/router.options.ts 66.66% 2 Missing ⚠️
app/components/Package/TableRow.vue 83.33% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@MatteoGabriele MatteoGabriele changed the title feat(ui): add action bar feat(ui): add multi-select and bulk actions for packages Feb 28, 2026
@MatteoGabriele
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 1, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
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: 3

🧹 Nitpick comments (1)
app/composables/usePackageSelection.ts (1)

10-22: Canonicalise selection values before persisting to query state.

selectedPackages reads a capped list, but Line 20-Line 21 can still write duplicates or over-limit values into the URL. Keeping read/write logic symmetrical avoids query-state drift.

♻️ Suggested normalisation patch
 const selectedPackages = computed<string[]>({
   get() {
     const raw = selectedPackagesParam.value
     if (!raw) return []
-    return raw
-      .split(',')
-      .map(p => String(p).trim())
-      .filter(Boolean)
-      .slice(0, MAX_PACKAGE_SELECTION)
+    return [...new Set(
+      raw
+        .split(',')
+        .map(p => String(p).trim())
+        .filter(Boolean),
+    )].slice(0, MAX_PACKAGE_SELECTION)
   },
   set(pkgs: string[]) {
-    // Ensure all items are strings before joining
-    const validPkgs = (Array.isArray(pkgs) ? pkgs : []).map(p => String(p).trim()).filter(Boolean)
+    const validPkgs = [...new Set(
+      (Array.isArray(pkgs) ? pkgs : [])
+        .map(p => String(p).trim())
+        .filter(Boolean),
+    )].slice(0, MAX_PACKAGE_SELECTION)
     selectedPackagesParam.value = validPkgs.length > 0 ? validPkgs.join(',') : ''
   },
 })

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3856538 and 70f7615.

📒 Files selected for processing (17)
  • app/components/Package/ActionBar.vue
  • app/components/Package/Card.vue
  • app/components/Package/ListToolbar.vue
  • app/components/Package/SelectionView.vue
  • app/components/Package/Table.vue
  • app/components/Package/TableRow.vue
  • app/composables/usePackageSelection.ts
  • app/pages/search.vue
  • app/router.options.ts
  • i18n/locales/en.json
  • i18n/locales/it-IT.json
  • i18n/schema.json
  • lunaria/files/en-GB.json
  • lunaria/files/en-US.json
  • lunaria/files/it-IT.json
  • shared/types/npm-registry.ts
  • test/unit/shared/types/index.spec.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • lunaria/files/en-US.json
  • i18n/locales/en.json
  • app/components/Package/ActionBar.vue
  • app/components/Package/SelectionView.vue
  • app/components/Package/ListToolbar.vue
  • app/components/Package/Table.vue
  • i18n/schema.json

Comment on lines 157 to 160
<div
v-if="result.downloads?.weekly"
class="text-fg-subtle gap-2 flex items-center justify-end"
class="text-fg-subtle gap-2 flex items-center sm:justify-end"
>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Prevent duplicate weekly-downloads rendering on small screens.

Line 157 renders this downloads row on all breakpoints, while Lines 121-133 already render a mobile-only downloads row. On small screens, users will see the same metric twice.

Proposed fix
-        <div
-          v-if="result.downloads?.weekly"
-          class="text-fg-subtle gap-2 flex items-center sm:justify-end"
-        >
+        <div
+          v-if="result.downloads?.weekly"
+          class="hidden sm:flex text-fg-subtle gap-2 items-center sm:justify-end"
+        >
📝 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
<div
v-if="result.downloads?.weekly"
class="text-fg-subtle gap-2 flex items-center justify-end"
class="text-fg-subtle gap-2 flex items-center sm:justify-end"
>
<div
v-if="result.downloads?.weekly"
class="hidden sm:flex text-fg-subtle gap-2 items-center sm:justify-end"
>

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

Labels

ux Related to wider UX decisions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Select packages to compare from the search results view

2 participants