# MagicCommand

MagicCommand is a flexible collection of components intended to build command palette style menus, such as Spotlight, Raycast and the like.

::component-preview{src="./demo/DefaultDemo.vue"}
::

## Anatomy

```vue
<template>
  <magic-command-provider id="your-command-id">
    <!-- modal -->
    <magic-command-modal>
      <magic-command-renderer />
    </magic-command-modal>

    <!-- drawer -->
    <magic-command-drawer>
      <magic-command-renderer />
    </magic-command-drawer>

    <!-- initial view -->
    <magic-command-view :initial="true" id="your-view-id">
      <magic-command-trigger>Open Menu</magic-command-trigger>

      <magic-command-content>
        <magic-command-item>
          <!-- your content -->
        </magic-command-item>

        <!-- nested view -->
        <magic-command-item>
          <magic-command-view>
            <magic-command-trigger>Open View</magic-command-trigger>
            <magic-command-content>
              <!-- your content -->
            </magic-command-content>
          </magic-command-view>
        </magic-command-item>
      </magic-command-content>
    </magic-command-view>

    <!-- optional -->
    <magic-command-trigger view-id="your-view-id">
      Open Menu
    </magic-command-trigger>
  </magic-command-provider>
</template>

<script setup>
const { open, close } = useMagicCommand('your-command-id')
</script>
```

### Custom Layout

Both `MagicCommandModal` and `MagicCommandDrawer` render a default layout out of the box. Use the `#layout` slot to replace the entire inner structure when you need full control. For example to remove the backdrop or customise the teleport target.

```vue
<template>
  <magic-command-modal>
    <template #layout>
      <magic-modal-teleport>
        <magic-modal-content>
          <magic-command-renderer />
        </magic-modal-content>
      </magic-modal-teleport>
    </template>
  </magic-command-modal>
</template>
```

See [MagicModal](/plugins/MagicModal) and [MagicDrawer](/plugins/MagicDrawer) for details.

## Installation

### CLI

Add `@maas/vue-equipment` to your dependencies.

::package-install{name="@maas/vue-equipment"}
::

### Vue

If you are using Vue, import and add `MagicCommandPlugin` to your app.

```js
import { createApp } from 'vue'
import { MagicCommandPlugin } from '@maas/vue-equipment/plugins/MagicCommand'

const app = createApp({})

app.use(MagicCommandPlugin)
```

### Nuxt

The command palette is available as a Nuxt module. In your Nuxt config file add `@maas/vue-equipment/nuxt` to your modules and add `MagicCommand` to the plugins in your configuration.

```js
export default defineNuxtConfig({
  modules: ['@maas/vue-equipment/nuxt'],
  vueEquipment: {
    plugins: ['MagicCommand'],
  },
})
```

### Composable

In order to interact with the command palette from anywhere within your app, we provide a `useMagicCommand` composable. Import it directly when needed.

```js
import { useMagicCommand } from '@maas/vue-equipment/plugins/MagicCommand'

const { selectView } = useMagicCommand('your-command-id')

function handleClick() {
  selectView('your-view-id')
}
```

::tip
If you have installed the command palette as a Nuxt module, the composable will be auto-imported and is automatically available in your Nuxt app.
::

## Peer Dependencies

MagicCommand relies on the following optional peer dependencies in addition to the [core peer dependencies](/overview/getting-started#core). Package managers don’t install optional peer dependencies automatically, so add them to your project.

::docs-table
---
columns:
  - label: Package
rows:
  - items:
      - label: '[@vueuse/integrations](https://www.npmjs.com/package/@vueuse/integrations)'
  - items:
      - label: '[focus-trap](https://www.npmjs.com/package/focus-trap)'
  - items:
      - label: '[wheel-gestures](https://www.npmjs.com/package/wheel-gestures)'
---
::

### Installation

::package-install{name="@vueuse/integrations focus-trap wheel-gestures"}
::

## API Reference

### MagicCommandProvider

The MagicCommandProvider wraps the command palette and configures all child components according to the provided [options](#options).

#### Props

::docs-table
---
columns:
  - label: Prop
  - label: Type
  - label: Required
rows:
  - items:
      - label: id
        description: Providing an id is required. Can either be a string or a ref.
      - label: MaybeRef<string>
        escape: true
      - label: 'true'
  - items:
      - label: asChild
        description: Prevent the component from rendering and pass all functionality to a child element.
      - label: boolean
      - label: 'false'
  - items:
      - label: options
        description: Refer to the [options table](#options) for details.
      - label: MagicCommandOptions
      - label: 'false'
---
::

### Options

To customize the command palette, override the necessary options. Any custom options will be merged with the default options.

::docs-table
---
columns:
  - label: Option
  - label: Type
  - label: Default
rows:
  - items:
      - label: debug
        description: Set to true to get verbose error logs.
      - label: boolean
      - label: 'false'
  - items:
      - label: transition.content
        description: Override the transition name of the command content.
      - label: string
      - label: magic-command-content
  - items:
      - label: keyListener.open
        description: Key combinations that will open the command menu.
      - label: string[] | false
      - label: "['Cmd+k', 'Ctrl+k']"
  - items:
      - label: keyListener.close
        description: Key combinations that will close the command menu.
      - label: string[] | false
      - label: "['Escape']"
  - items:
      - label: keyListener.next
        description: Key combinations that will select the next item.
      - label: string[] | false
      - label: "['ArrowDown']"
  - items:
      - label: keyListener.prev
        description: Key combinations that will select the previous item.
      - label: string[] | false
      - label: "['ArrowUp']"
  - items:
      - label: keyListener.enter
        description: Key combinations that will trigger `MagicCommandTrigger` when active.
      - label: string[] | false
      - label: "['Enter']"
  - items:
      - label: loop
        description: Whether to loop through items when reaching the end of the list.
      - label: boolean
      - label: 'false'
---
::

### MagicCommandView

Groups command items into a single panel and tracks which item is currently selected.

#### Props

::docs-table
---
columns:
  - label: Prop
  - label: Type
  - label: Required
rows:
  - items:
      - label: id
        description: Providing an id is optional. Neccessary for interacting with the view through `useMagicCommand`.
      - label: MaybeRef<string>
        escape: true
      - label: 'false'
  - items:
      - label: initial
        description: When set to `true` this view will be visible when opening the command palette.
      - label: boolean
      - label: 'false'
---
::

#### Slot Props

::docs-table
---
columns:
  - label: Prop
  - label: Type
  - label: Description
rows:
  - items:
      - label: view-active
      - label: boolean
      - plaintext: true
        label: Whether the command palette view is currently open.
---
::

### MagicCommandDrawer

Wraps the command palette in a `MagicDrawerProvider` and renders teleport, backdrop and content by default. Override the full inner structure using the `#layout` slot.

#### Props

::docs-table
---
columns:
  - label: Prop
  - label: Type
  - label: Required
rows:
  - items:
      - label: Options
        description: 'Refer to the MagicDrawer [options table](/plugins/MagicDrawer#options) for details. '
      - label: MagicCommandDrawerOptions
      - label: 'false'
---
::

### MagicCommandModal

Wraps the command palette in a `MagicModalProvider` and renders teleport, backdrop and content by default. Override the full inner structure using the `#layout` slot.

#### Props

::docs-table
---
columns:
  - label: Prop
  - label: Type
  - label: Required
rows:
  - items:
      - label: Options
        description: 'Refer to the MagicModal [options table](/plugins/MagicModal#options) for details. '
      - label: MagicCommandModalOptions
      - label: 'false'
---
::

### MagicCommandRenderer

Renders command items dynamically from registered commands.

#### CSS Variables

::docs-table
---
columns:
  - label: Variable
  - label: Default
rows:
  - items:
      - label: --magic-command-renderer-width
      - label: 100%
  - items:
      - label: --magic-command-renderer-height
      - label: 100%
---
::

### MagicCommandItem

A single interactive command entry.

#### Props

::docs-table
---
columns:
  - label: Prop
  - label: Type
  - label: Required
rows:
  - items:
      - label: id
        description: Providing an id is optional.
      - label: string
      - label: 'false'
  - items:
      - label: disabled
        description: Disable the command item.
      - label: boolean
      - label: 'false'
---
::

#### CSS Variables

::docs-table
---
columns:
  - label: Variable
  - label: Default
rows:
  - items:
      - label: --magic-command-item-cursor
      - label: default
---
::

#### Slot Props

::docs-table
---
columns:
  - label: Prop
  - label: Type
  - label: Description
rows:
  - items:
      - label: item-active
      - label: boolean
      - plaintext: true
        label: Whether the item is currently selected.
  - items:
      - label: item-disabled
      - label: boolean
      - plaintext: true
        label: Whether the item is currently disabled.
---
::

### MagicCommandTrigger

Opens or closes the command palette on click.

#### Props

::docs-table
---
columns:
  - label: Prop
  - label: Type
  - label: Required
rows:
  - items:
      - label: viewId
        description: Optionally pass the `viewId` of the related view. Can be omitted if the component is nested under `MagicCommandView`.
      - label: string
      - label: 'false'
  - items:
      - label: disabled
        description: Disable the trigger.
      - label: boolean
      - label: 'false'
  - items:
      - label: trigger
        description: Override the interactions that activate the trigger.
      - label: Interaction[]
        description: Array<'click' | 'mouseenter'>
      - label: 'false'
  - items:
      - label: action
        description: Choose if the trigger opens or closes the related view.
      - label: Action
        description: "'open' | 'close'"
      - label: 'false'
  - items:
      - label: asChild
        description: Prevent the component from rendering and pass all functionality to a child element.
      - label: boolean
      - label: 'false'
---
::

::docs-table
---
columns:
  - label: Variable
  - label: Default
rows:
  - items:
      - label: --magic-command-trigger-cursor
      - label: pointer
---
::

#### CSS Variables

::docs-table
---
columns:
  - label: Variable
  - label: Default
rows:
  - items:
      - label: --magic-command-trigger-cursor
      - label: pointer
---
::

#### Slot Props

::docs-table
---
columns:
  - label: Prop
  - label: Type
  - label: Description
rows:
  - items:
      - label: view-active
      - label: boolean
      - plaintext: true
        label: Whether the command palette is currently open.
  - items:
      - label: trigger-disabled
      - label: boolean
      - plaintext: true
        label: Whether the trigger is currently disabled.
---
::

## Errors

::docs-table
---
columns:
  - label: Source
  - label: Error Code
  - label: Message
rows:
  - items:
      - label: MagicCommandDrawer
      - label: missing_instance_id
      - plaintext: true
        label: MagicCommandDrawer must be nested inside MagicCommandProvider
  - items:
      - label: MagicCommandTrigger
      - label: missing_instance_id
      - plaintext: true
        label: MagicCommandTrigger must be nested inside MagicCommandProvider
  - items:
      - label: MagicCommandTrigger
      - label: missing_view_id
      - plaintext: true
        label: MagicCommandTrigger must be nested inside MagicCommandView or a viewId must be provided
  - items:
      - label: MagicCommandView
      - label: missing_instance_id
      - plaintext: true
        label: MagicCommandView must be nested inside MagicCommandProvider
  - items:
      - label: MagicCommandContent
      - label: missing_instance_id
      - plaintext: true
        label: MagicCommandContent must be nested inside MagicCommandProvider
  - items:
      - label: MagicCommandContent
      - label: missing_view_id
      - plaintext: true
        label: MagicCommandContent must be nested inside MagicCommandView
  - items:
      - label: useMagicCommand
      - label: view_id_required
      - plaintext: true
        label: viewId is required to select an item
  - items:
      - label: useMagicCommand
      - label: id_required
      - plaintext: true
        label: id is required to select an item
  - items:
      - label: useMenuItem
      - label: view_id_not_found
      - plaintext: true
        label: View {viewId} not found
  - items:
      - label: useMenuKeyListener
      - label: menu_not_active
      - plaintext: true
        label: MagicMenu {state.id} is not active
---
::

## Examples

### Modal

::component-preview{src="./demo/ModalDemo.vue"}
::

### Drawer

::component-preview{src="./demo/DrawerDemo.vue"}
::
