V1.0

Base Components

Command Menu

Command Menu empowers users to swiftly execute searches on any page, thereby streamlining their workflow.

Installation

Install the following dependencies:

terminal
npm install cmdk @radix-ui/react-dialog

This component is using with the <Modal> component. Refer to the Modal documentation for setup instructions before using this component.

Create a command-menu.tsx file and paste the following code into it.

/components/ui/command-menu.tsx
// AlignUI CommandMenu v0.0.0
 
'use client';
 
import * as React from 'react';
import { type DialogProps } from '@radix-ui/react-dialog';
import { Command } from 'cmdk';
 
import * as Modal from '@/components/ui/modal';
import { cn, cnExt } from '@/utils/cn';
import { PolymorphicComponentProps } from '@/utils/polymorphic';
import { tv, type VariantProps } from '@/utils/tv';
 
const CommandDialogTitle = Modal.Title;
const CommandDialogDescription = Modal.Description;
 
const CommandDialog = ({
  children,
  className,
  overlayClassName,
  ...rest
}: DialogProps & {
  className?: string;
  overlayClassName?: string;
}) => {
  return (
    <Modal.Root {...rest}>
      <Modal.Content
        overlayClassName={cn('justify-start pt-20', overlayClassName)}
        showClose={false}
        className={cnExt(
          'flex max-h-full max-w-[600px] flex-col overflow-hidden rounded-2xl',
          className,
        )}
      >
        <Command
          className={cnExt(
            'divide-y divide-stroke-soft-200',
            'grid min-h-0 auto-cols-auto grid-flow-row',
            '[&>[cmdk-label]+*]:!border-t-0',
          )}
        >
          {children}
        </Command>
      </Modal.Content>
    </Modal.Root>
  );
};
 
const CommandInput = React.forwardRef<
  React.ComponentRef<typeof Command.Input>,
  React.ComponentPropsWithoutRef<typeof Command.Input>
>(({ className, ...rest }, forwardedRef) => {
  return (
    <Command.Input
      ref={forwardedRef}
      className={cnExt(
        // base
        'w-full bg-transparent text-paragraph-sm text-text-strong-950 outline-none',
        'transition duration-200 ease-out',
        // placeholder
        'placeholder:[transition:inherit]',
        'placeholder:text-text-soft-400',
        // hover
        'group-hover/cmd-input:placeholder:text-text-sub-600',
        // focus
        'focus:outline-none',
        className,
      )}
      {...rest}
    />
  );
});
CommandInput.displayName = 'CommandInput';
 
const CommandList = React.forwardRef<
  React.ComponentRef<typeof Command.List>,
  React.ComponentPropsWithoutRef<typeof Command.List>
>(({ className, ...rest }, forwardedRef) => {
  return (
    <Command.List
      ref={forwardedRef}
      className={cnExt(
        'flex max-h-min min-h-0 flex-1 flex-col',
        '[&>[cmdk-list-sizer]]:divide-y [&>[cmdk-list-sizer]]:divide-stroke-soft-200',
        '[&>[cmdk-list-sizer]]:overflow-auto',
        className,
      )}
      {...rest}
    />
  );
});
CommandList.displayName = 'CommandList';
 
const CommandGroup = React.forwardRef<
  React.ComponentRef<typeof Command.Group>,
  React.ComponentPropsWithoutRef<typeof Command.Group>
>(({ className, ...rest }, forwardedRef) => {
  return (
    <Command.Group
      ref={forwardedRef}
      className={cnExt(
        'relative px-2 py-3',
        // heading
        '[&>[cmdk-group-heading]]:text-label-xs [&>[cmdk-group-heading]]:text-text-sub-600',
        '[&>[cmdk-group-heading]]:mb-2 [&>[cmdk-group-heading]]:px-3 [&>[cmdk-group-heading]]:pt-1',
        className,
      )}
      {...rest}
    />
  );
});
CommandGroup.displayName = 'CommandGroup';
 
const commandItemVariants = tv({
  base: [
    'flex items-center gap-3 rounded-10 bg-bg-white-0',
    'cursor-pointer text-paragraph-sm text-text-strong-950',
    'transition duration-200 ease-out',
    // hover/selected
    'data-[selected=true]:bg-bg-weak-50',
  ],
  variants: {
    size: {
      small: 'px-3 py-2.5',
      medium: 'px-3 py-3',
    },
  },
  defaultVariants: {
    size: 'small',
  },
});
 
type CommandItemProps = VariantProps<typeof commandItemVariants> &
  React.ComponentPropsWithoutRef<typeof Command.Item>;
 
const CommandItem = React.forwardRef<
  React.ComponentRef<typeof Command.Item>,
  CommandItemProps
>(({ className, size, ...rest }, forwardedRef) => {
  return (
    <Command.Item
      ref={forwardedRef}
      className={commandItemVariants({ size, class: className })}
      {...rest}
    />
  );
});
CommandItem.displayName = 'CommandItem';
 
function CommandItemIcon<T extends React.ElementType>({
  className,
  as,
  ...rest
}: PolymorphicComponentProps<T>) {
  const Component = as || 'div';
 
  return (
    <Component
      className={cnExt('size-5 shrink-0 text-text-sub-600', className)}
      {...rest}
    />
  );
}
 
function CommandFooter({
  className,
  ...rest
}: React.HTMLAttributes<HTMLDivElement>) {
  return (
    <div
      className={cnExt(
        'flex h-12 items-center justify-between gap-3 px-5',
        className,
      )}
      {...rest}
    />
  );
}
 
function CommandFooterKeyBox({
  className,
  ...rest
}: React.HTMLAttributes<HTMLDivElement>) {
  return (
    <div
      className={cnExt(
        'flex size-5 shrink-0 items-center justify-center rounded bg-bg-weak-50 text-text-sub-600 ring-1 ring-inset ring-stroke-soft-200',
        className,
      )}
      {...rest}
    />
  );
}
 
export {
  CommandDialog as Dialog,
  CommandDialogTitle as DialogTitle,
  CommandDialogDescription as DialogDescription,
  CommandInput as Input,
  CommandList as List,
  CommandGroup as Group,
  CommandItem as Item,
  CommandItemIcon as ItemIcon,
  CommandFooter as Footer,
  CommandFooterKeyBox as FooterKeyBox,
};

Update the import paths to match your project setup.

API Reference

This component is based on the cmdk package. Refer to their documentation for the API reference.

CommandMenu.Dialog

The CommandMenu.Dialog component is based on cmdk Command primitive. Wrapped by Modal Content.

CommandMenu.Input

The CommandMenu.Input component is based on cmdk Input primitive.

CommandMenu.Group

The CommandMenu.Group component is based on cmdk Group primitive.

CommandMenu.List

The CommandMenu.List component is based on cmdk List primitive.

CommandMenu.Item

The CommandMenu.Item component is based on cmdk Item primitive. And adds:

PropTypeDefault
size
"small"|"medium"
"small"

CommandMenu.ItemIcon

The CommandMenu.ItemIcon component is polymorphic, allowing you to change the underlying HTML element using the as prop.

PropTypeDefault
as
React.ElementType
div

CommandMenu.Footer

A footer section for the command dialog. This component is based on the <div> element and supports all of its props.

CommandMenu.FooterKeyBox

Used within the CommandMenu.Footer to display key information or buttons. This component is based on the <div> element and supports all of its props.

© 2024 AlignUI Design System. All rights reserved.