Skip to content
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

Added keyboard accessible shortcut menu for create modals #457

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

Blackshadow8910
Copy link

@Blackshadow8910 Blackshadow8910 commented Jan 10, 2025

What type of PR is this?

  • feature

What this PR does / why we need it:

This PR adds a command palette like modal menu that can be brought up with Ctrl+`, and allows for faster access to the other create modals for keyboard only use. Other commands can be added later but I can't think of anything, so for now its just a keyboard shortcut to those menus. The shortcut should also probably be displayed somewhere.

I also changed BaseModal.vue to allow clicking off of the modal to close it and made the close button optional, since I think these additions benefit this feature.

Which issue(s) this PR fixes:

#16

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a configurable Modal component with an optional close button.
    • Added a new Quick Menu input interface for rapid action selection.
    • Implemented a Quick Menu modal with keyboard shortcut support (Ctrl + Backquote).
  • Improvements

    • Enhanced modal component flexibility.
    • Improved user interaction with quick action selection.
    • Added keyboard navigation for modal and quick menu actions.
  • UI Changes

    • Updated dropdown content class ordering.
    • Integrated Quick Menu modal into the default layout.

Copy link
Contributor

coderabbitai bot commented Jan 10, 2025

Walkthrough

This pull request introduces enhancements to the frontend components, primarily focusing on adding a new Quick Menu feature. The changes include modifications to the Modal.vue component, introducing a new showCloseButton prop, updating the Multiselect.vue component's class ordering, and creating new components for a Quick Menu input and modal. The default layout is updated to integrate the Quick Menu functionality with keyboard shortcut support.

Changes

File Change Summary
frontend/components/Base/Modal.vue Added showCloseButton prop, modified overflow behavior, enhanced modal close logic
frontend/components/Form/Multiselect.vue Reordered CSS classes in dropdown content div
frontend/components/global/QuickMenu/Input.vue New component for Quick Menu input with action selection
frontend/components/global/QuickMenu/Modal.vue New modal component for Quick Menu actions
frontend/layouts/default.vue Added QuickMenuModal, keyboard shortcut, and quick menu actions

Sequence Diagram

sequenceDiagram
    participant User
    participant QuickMenuModal
    participant QuickMenuInput
    participant Actions

    User->>QuickMenuModal: Open Modal (Ctrl + Backquote)
    QuickMenuModal->>QuickMenuInput: Focus Input
    User->>QuickMenuInput: Type Action
    QuickMenuInput->>Actions: Filter Available Actions
    User->>QuickMenuInput: Select Action
    QuickMenuInput->>QuickMenuModal: Invoke Selected Action
    QuickMenuModal->>User: Close Modal
Loading

Possibly related PRs

Suggested reviewers

  • tankerkiller125

Security Recommendations

  1. Validate and sanitize user inputs in the Quick Menu to prevent potential injection attacks.
  2. Implement proper access controls for actions triggered through the Quick Menu.
  3. Add rate limiting to prevent potential abuse of keyboard shortcuts.
  4. Ensure that sensitive actions are not exposed through the Quick Menu without proper authentication.

📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ca33267 and e81fbdb.

📒 Files selected for processing (3)
  • frontend/components/global/QuickMenu/Input.vue (1 hunks)
  • frontend/components/global/QuickMenu/Modal.vue (1 hunks)
  • frontend/layouts/default.vue (8 hunks)
🔇 Additional comments (2)
frontend/components/global/QuickMenu/Modal.vue (1)

1-13: Consider adding an escape key handler for accessibility.

While the modal can be closed by selecting an action, it's important to ensure users can exit the modal using the Escape key for better accessibility.

Run this script to check if BaseModal handles the Escape key:

✅ Verification successful

✅ Escape key handling is already implemented securely

The BaseModal component already implements Escape key handling through its escClose function, which properly closes the modal when the Escape key is pressed. This is a secure implementation at the base component level, ensuring consistent behavior across all modal instances.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Escape key handling in BaseModal component
ast-grep --pattern 'onKeyDown($event) {
  $$$
  if ($event.key === "Escape") {
    $$$
  }
  $$$
}'

Length of output: 101


Script:

#!/bin/bash
# Find BaseModal component file
fd "BaseModal.vue"

# If found, examine its content for keyboard event handling
fd "BaseModal.vue" --exec cat {} \;

# Also search for Escape key handling with ripgrep for broader coverage
rg -i "escape" --type vue

Length of output: 111


Script:

#!/bin/bash
# Search for BaseModal component without type restriction
rg -l "BaseModal"

# Search for escape key handling in .vue files
rg -i "escape" -g "*.vue" -A 2 -B 2

# Search specifically in template sections
rg "@keydown|v-on:keydown|@keyup" -g "*.vue" -A 2 -B 2

Length of output: 1278

frontend/layouts/default.vue (1)

Line range hint 59-73: Well-implemented UI with clear keyboard shortcuts!

Good job making the keyboard shortcuts visible in both the tooltip and dropdown menu items.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

Actionable comments posted: 3

🧹 Nitpick comments (2)
frontend/components/global/QuickMenu/Input.vue (1)

96-103: Optimize action filtering performance

The current filtering implementation could be optimized:

Consider:

 const filteredActions = computed(() => {
+    const searchTerm = inputValue.value.toLowerCase();
     return (props.actions || []).filter(action => {
       return (
-        action.text.toLowerCase().includes(inputValue.value.toLowerCase()) ||
-        action.shortcut?.includes(inputValue.value.toLowerCase())
+        action.text.toLowerCase().includes(searchTerm) ||
+        action.shortcut?.toLowerCase().includes(searchTerm)
       );
     });
   });
frontend/layouts/default.vue (1)

245-261: Enhance action handling security and maintainability

The current implementation of quick menu actions has several areas for improvement:

  1. Consider using an action registry pattern instead of direct modal manipulation
  2. Add input validation for shortcuts
  3. Implement proper action logging for security auditing

Example implementation:

const actionRegistry = {
  registerAction(action: QuickMenuAction) {
    // Validate shortcut
    if (action.shortcut && !/^[1-9]$/.test(action.shortcut)) {
      throw new Error('Invalid shortcut');
    }
    // Log action registration
    console.log(`Registering action: ${action.text}`);
    return action;
  }
};

const quickMenuActions = ref([
  actionRegistry.registerAction({
    text: "Create Item",
    action: () => (modals.item = true),
    shortcut: "1",
  }),
  // ... other actions
]);
🛑 Comments failed to post (3)
frontend/components/global/QuickMenu/Modal.vue (1)

62-65: 🛠️ Refactor suggestion

Review timeout usage in action invocation

The 100ms timeout in invokeAction could lead to race conditions or user experience issues:

  1. Multiple rapid actions might queue up
  2. Actions might execute in unexpected order

Consider using a more robust approach:

-    useTimeoutFn(action.action, 100).start();
+    await nextTick();
+    action.action();

Committable suggestion skipped: line range outside the PR's diff.

frontend/components/global/QuickMenu/Input.vue (1)

87-94: 🛠️ Refactor suggestion

Enhance keyboard shortcut security and accessibility

The current keyboard shortcut implementation could be improved:

  1. No prevention of shortcut conflicts
  2. Missing ARIA labels for accessibility
  3. No rate limiting on shortcut triggers

Consider:

  1. Adding a shortcut registry to prevent conflicts
  2. Implementing proper ARIA labels
  3. Adding rate limiting for shortcut triggers
frontend/layouts/default.vue (1)

236-243: 🛠️ Refactor suggestion

Improve modal management security

The current implementation of closing all modals when opening the quick menu could lead to data loss if users have unsaved changes.

Consider:

  1. Adding confirmation dialogs for unsaved changes
  2. Implementing a modal stack
  3. Adding proper focus management when switching between modals

Copy link
Collaborator

@tonyaellie tonyaellie left a comment

Choose a reason for hiding this comment

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

Other things to consider:
Would be nice to have a Ctrl + ` by the create button to help new users spot the shortcut, maybe hide it on mobile but that isnt very necesarry.
image
Having hotkeys to directly open the create items, create labels and create create locations could be nice though not a must have.
Having hotkeys be customisable might be nice but not really a must have.

Translation keys need to added.

</button>
</li>
</ul>
<span class="text-base-300">Use number keys to quick select.</span>
Copy link
Collaborator

Choose a reason for hiding this comment

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

This text is currently kinda unreadable, I think it would be better if it was just the normal text colour?
image
It also gets blocked off by the dropdown so moving it to be above would make it more readable.
image

class="input input-bordered w-full"
@input="inputValue = $event.target.value"
></ComboboxInput>
<ComboboxOptions class="card dropdown-content absolute w-full rounded-lg border border-base-300 bg-base-100">
Copy link
Collaborator

Choose a reason for hiding this comment

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

Might be worth adding mt-2 as currently it looks a bit off and that matches what we do in Autocomplete2.tsx

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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

Actionable comments posted: 1

🧹 Nitpick comments (5)
frontend/components/global/QuickMenu/Modal.vue (2)

35-37: Consider increasing the focus timeout.

The 50ms timeout for focusing the input might be too short on slower devices. Consider increasing it to at least 100ms for better reliability.

-  }, 50).start;
+  }, 100).start;

50-57: Protect against potential race conditions.

The watch on selectedAction could trigger multiple times if the user rapidly selects different actions. Consider adding a guard:

  watch(selectedAction, action => {
-   if (action) invokeAction(action);
+   if (action && modal.value) invokeAction(action);
  });
frontend/components/global/QuickMenu/Input.vue (1)

8-10: Consider adjusting max-height for better UX.

The current max-height of 48 units might cut off items in the dropdown. Consider:

  1. Using a viewport-relative height (e.g., max-h-[80vh])
  2. Adding a scrollbar indicator
-     class="card dropdown-content absolute max-h-48 w-full overflow-y-scroll rounded-lg border border-base-300 bg-base-100"
+     class="card dropdown-content absolute max-h-[80vh] w-full overflow-y-auto rounded-lg border border-base-300 bg-base-100 scrollbar-thin scrollbar-track-base-200 scrollbar-thumb-base-300"
frontend/layouts/default.vue (2)

259-266: Consider centralizing modal state management.

The current implementation manually closes each modal. Consider using a centralized approach:

+ const closeAllModals = () => {
+   Object.keys(modals).forEach(key => {
+     if (key !== 'quickMenu') modals[key] = false;
+   });
+ };

  whenever(quickMenuShortcut, () => {
    modals.quickMenu = true;
-   modals.item = false;
-   modals.location = false;
-   modals.label = false;
-   modals.import = false;
+   closeAllModals();
  });

268-302: Consider adding keyboard shortcuts for navigation actions.

The navigation actions currently have empty shortcuts. Consider adding number shortcuts (4-9) for quick navigation:

  nav.map((v, index) => {
    return {
      text: computed(() => `${t("global.navigate")}: ${v.name.value}`),
      action: () => {
        navigateTo(v.to);
      },
-     shortcut: "",
+     shortcut: `${index + 4}`,
    };
  })
🛑 Comments failed to post (1)
frontend/components/global/QuickMenu/Input.vue (1)

89-96: ⚠️ Potential issue

Enhance input handling security.

The current implementation directly compares user input with shortcuts without sanitization. Consider:

  1. Sanitizing input to prevent XSS
  2. Making shortcut comparison case-insensitive
  3. Adding input debouncing
+ const sanitizeInput = (input: string) => input.replace(/[<>]/g, '');
+ const debouncedInput = useDebounce(inputValue, 150);

  watch(inputValue, (val, oldVal) => {
    if (!oldVal) {
+     const sanitizedVal = sanitizeInput(val);
-     const action = props.actions?.find(v => v.shortcut === val);
+     const action = props.actions?.find(v => v.shortcut?.toLowerCase() === sanitizedVal.toLowerCase());
      if (action) {
        emit("quickSelect", action);
      }
    }
  });

Committable suggestion skipped: line range outside the PR's diff.

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.

2 participants