Skip to content

CommandPalette: support submenus #2003

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

Open
chris-si opened this issue Aug 1, 2024 · 2 comments
Open

CommandPalette: support submenus #2003

chris-si opened this issue Aug 1, 2024 · 2 comments
Labels
enhancement New feature or request v3 #1289

Comments

@chris-si
Copy link

chris-si commented Aug 1, 2024

Description

Hi,

I think it would be great if the command palette would 'natively' support submenus to enable an easy way to build some more feature-rich command palettes.
I implemented submenus by manually replacing the assigned command groups and saving the 'history' of groups assigned to the command palette so I can go back via a manually injected back button.

The following code is a snippet of my code that doesn't work as I put it there because of some missing code, but it outlines what I did well enough to understand my submenu implementation.

Additional context

<template>
  <UModal v-model="model">
    <UCommandPalette
      ref="commandPaletteRef"
      :loading="isDataLoading"
      :groups="selectedCommandGroups"
      :autoselect="true"
      :nullable="false"
      @update:model-value="onCommandSelect"
      @close="() => (model = false)"
      :close-button="{
        icon: 'i-heroicons-x-mark-20-solid',
        color: 'gray',
        variant: 'link',
        padded: false,
      }"
      :empty-state="{
        icon: 'i-heroicons-magnifying-glass-20-solid',
        label: `We couldn't find any items.`,
        queryLabel: `We couldn't find any items with that term. Please try again.`,
      }">
    </UCommandPalette>
  </UModal>
</template>

<script setup lang="ts">
import type { Group, Command } from '#ui/types';
import type { UCommandPalette } from '#build/components';
import { nanoid } from 'nanoid';


const toast = useToast();
const router = useRouter();

const model = defineModel<boolean>();

const commandPaletteStore = useCommandPaletteStore();
const { commandPaletteGroups, isDataLoading, realms } = storeToRefs(commandPaletteStore);
const commandPaletteRef = ref<InstanceType<typeof UCommandPalette>>();


const newEntitySubmenuGroup = computed<Group[]>(() => {
  return [
    {
      key: 'action-new-entity-sub-menu',
      label: 'Create new entity: Select a realm',
      commands: realms.value.map((realm) => ({
        id: `action-new-entity-realm-${realm.workspaceId}`,
        label: realm.ownershipType === 'PERSONAL' ? 'My Library' : realm.name,
        icon: realm.ownershipType === 'PERSONAL' ? 'i-heroicons-user' : 'i-heroicons-user-group',
        click: () => navigateTo(`${commandPaletteStore.getWorkspacePrefix(realm)}/entity/new`),
      })),
    },
  ];
});

const actionCommands = computed<Command[]>(() => {
  return [
    {
      id: 'action-new-entity',
      label: 'Create new entity',
      icon: 'i-heroicons-document',
      submenu: newEntitySubmenuGroup.value,
    },
  ];
});

const navigationCommands: Command[] = [
  {
    id: 'navigation-home',
    label: 'Home',
    icon: 'i-heroicons-home',
    click: () => navigateTo('/'),
  },
  {
    id: 'navigation-settings',
    label: 'Settings',
    icon: 'i-heroicons-cog',
    click: () => navigateTo('/settings'),
  },
  {
    id: 'navigation-back',
    label: 'Go back',
    icon: 'i-heroicons-arrow-uturn-left',
    click: () => router.back(),
  },
];


const commandGroups = computed(() => {
  return [
    ...commandPaletteGroups.value,
    {
      key: 'actions',
      label: 'Actions',
      commands: actionCommands.value.filter((command) => !command.hidden),
    },
    {
      key: 'navigation',
      label: 'Navigation',
      commands: navigationCommands,
    },
  ] satisfies Group[];
});

const selectedCommandGroups = ref<Group[]>(commandGroups.value);

const commandPaletteMenuHistory = ref<Group[][]>([commandGroups.value]);

function onCommandSelect(option: Command) {
  if (option && option.isSubmenuBackButton) {
    commandPaletteMenuHistory.value.pop();
    selectedCommandGroups.value = commandPaletteMenuHistory.value[commandPaletteMenuHistory.value.length - 1];
    return;
  }

  if (option && option.submenu) {
    const id = nanoid();
    const submenu: Group[] = [
      {
        key: `submenu-navigation-${id}`,
        static: true,
        commands: [
          {
            id: `command-back-${id}`,
            label: 'Back',
            icon: 'i-heroicons-arrow-left',
            isSubmenuBackButton: true,
          },
        ],
      },
      ...option.submenu,
    ];
    commandPaletteMenuHistory.value.push(submenu);
    selectedCommandGroups.value = submenu;

    return;
  }

  model.value = false;
  if (option && option.click) {
    option.click();
  }
}

onBeforeMount(async () => {
  await commandPaletteStore.loadData();
  selectedCommandGroups.value = commandGroups.value;
});
</script>
@chris-si chris-si added the triage label Aug 1, 2024
@benjamincanac benjamincanac added enhancement New feature or request v3 #1289 and removed triage labels Aug 7, 2024
@benjamincanac
Copy link
Member

This is something we might consider later on in v3 😊

@alvarosabu
Copy link

I second this one. In the current implementation, the command palette is useful for searching links or actions that do not require extra input from the user. For example, I would like to have a command to add a new user to my nuxt 3d game:

Image

At present, I would need to add these creation inputs to another modal that would open from the action selection; it would be nice to keep the user flow inside the command palette.

Another useful thing would be able to add / commands to the search input, like /add-character and then the inputs /add-character name key. Similar UX to Discord commands

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request v3 #1289
Projects
None yet
Development

No branches or pull requests

3 participants