Skip to content

Commit b4e3fd2

Browse files
committed
feat: add component command
1 parent 36067b0 commit b4e3fd2

File tree

21 files changed

+695
-1
lines changed

21 files changed

+695
-1
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Command as _Command } from 'cmdk';
2+
import type { ComponentRef } from 'react';
3+
import { forwardRef } from 'react';
4+
5+
import CommandEmpty from './CommandEmpty';
6+
import CommandInput from './CommandInput';
7+
import CommandList from './CommandList';
8+
import CommandOption from './CommandOption';
9+
import CommandRoot from './CommandRoot';
10+
import type { CommandProps } from './types';
11+
12+
const Command = forwardRef<ComponentRef<typeof CommandRoot>, CommandProps>((props, ref) => {
13+
const { className, classNames, empty = 'No results.', inputProps, items, size, ...rest } = props;
14+
15+
return (
16+
<CommandRoot
17+
{...rest}
18+
className={className || classNames?.root}
19+
ref={ref}
20+
>
21+
<CommandInput
22+
classNames={classNames}
23+
size={size}
24+
{...inputProps}
25+
/>
26+
27+
<CommandList
28+
className={classNames?.list}
29+
size={size}
30+
>
31+
<CommandEmpty
32+
className={classNames?.empty}
33+
size={size}
34+
>
35+
{empty}
36+
</CommandEmpty>
37+
38+
{items.map((item, index) => (
39+
<CommandOption
40+
classNames={classNames}
41+
item={item}
42+
key={String(index)}
43+
size={size}
44+
/>
45+
))}
46+
</CommandList>
47+
</CommandRoot>
48+
);
49+
});
50+
51+
Command.displayName = 'Command';
52+
53+
export default Command;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { DialogClose, DialogContent, DialogOverlay, DialogPortal, DialogRoot } from '../dialog';
2+
3+
import type { CommandDialogProps } from './types';
4+
5+
const CommandDialog = (props: CommandDialogProps) => {
6+
const { children, className, classNames, defaultOpen, onOpenChange, open, ...rest } = props;
7+
8+
return (
9+
<DialogRoot
10+
defaultOpen={defaultOpen}
11+
open={open}
12+
onOpenChange={onOpenChange}
13+
>
14+
<DialogPortal data-slot="dialog-portal">
15+
<DialogOverlay className={classNames?.overlay} />
16+
17+
<DialogContent
18+
{...rest}
19+
className={className || classNames?.content}
20+
>
21+
<DialogClose className={classNames?.close} />
22+
23+
{children}
24+
</DialogContent>
25+
</DialogPortal>
26+
</DialogRoot>
27+
);
28+
};
29+
30+
export default CommandDialog;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { CommandEmpty as _CommandEmpty } from 'cmdk';
2+
3+
import { cn } from '@/lib/utils';
4+
5+
import { commandVariants } from './command-variants';
6+
import type { CommandEmptyProps } from './types';
7+
8+
const CommandEmpty = (props: CommandEmptyProps) => {
9+
const { className, size, ...rest } = props;
10+
11+
const { empty } = commandVariants({ size });
12+
13+
const mergedClass = cn(empty(), className);
14+
return (
15+
<_CommandEmpty
16+
{...rest}
17+
className={mergedClass}
18+
data-slot="command-empty"
19+
/>
20+
);
21+
};
22+
23+
export default CommandEmpty;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { CommandGroup as _CommandGroup } from 'cmdk';
2+
3+
import { cn } from '@/lib/utils';
4+
5+
import { commandVariants } from './command-variants';
6+
import type { CommandGroupProps } from './types';
7+
8+
const CommandGroup = (props: CommandGroupProps) => {
9+
const { children, className, classNames, heading, ...rest } = props;
10+
11+
const { group, groupLabel } = commandVariants();
12+
13+
const mergedClass = cn(group(), className || classNames?.group);
14+
15+
const mergedLabelClass = cn(groupLabel(), className || classNames?.groupLabel);
16+
17+
return (
18+
<_CommandGroup
19+
{...rest}
20+
className={mergedClass}
21+
data-slot="command-group"
22+
>
23+
{heading && <div className={mergedLabelClass}>{heading}</div>}
24+
25+
{children}
26+
</_CommandGroup>
27+
);
28+
};
29+
30+
export default CommandGroup;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { CommandInput as _CommandInput } from 'cmdk';
2+
3+
import { cn } from '@/lib/utils';
4+
5+
import Icon from '../icon/Icon';
6+
7+
import { commandVariants } from './command-variants';
8+
import type { CommandInputProps } from './types';
9+
10+
const CommandInput = (props: CommandInputProps) => {
11+
const { className, classNames, leading, size, trailing, ...rest } = props;
12+
13+
const { input, inputIcon, inputWrapper } = commandVariants({ size });
14+
15+
const mergedClass = cn(input(), className || classNames?.input);
16+
const mergedIconClass = cn(inputIcon(), classNames?.inputIcon);
17+
const mergedWrapperClass = cn(inputWrapper(), classNames?.inputWrapper);
18+
19+
return (
20+
<div className={mergedWrapperClass}>
21+
{leading || (
22+
<Icon
23+
className={mergedIconClass}
24+
icon="lucide:search"
25+
/>
26+
)}
27+
28+
<_CommandInput
29+
{...rest}
30+
className={mergedClass}
31+
data-slot="command-input"
32+
/>
33+
34+
{trailing}
35+
</div>
36+
);
37+
};
38+
39+
export default CommandInput;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { CommandItem as _CommandItem } from 'cmdk';
2+
import { isValidElement } from 'react';
3+
4+
import { withClassName } from '@/lib/compose-props';
5+
import { cn } from '@/lib/utils';
6+
7+
import CommandShortcut from './CommandShortcut';
8+
import { commandVariants } from './command-variants';
9+
import type { CommandItemProps } from './types';
10+
11+
const CommandItem = (props: CommandItemProps) => {
12+
const { children, className, leading, shortcut, size, trailing, ...rest } = props;
13+
14+
const { item, itemIcon } = commandVariants({ size });
15+
16+
const mergedClass = cn(item(), className);
17+
18+
return (
19+
<_CommandItem
20+
{...rest}
21+
className={mergedClass}
22+
data-slot="command-item"
23+
>
24+
{isValidElement(leading) ? withClassName(leading, itemIcon()) : leading}
25+
26+
<span>{children}</span>
27+
28+
{trailing}
29+
30+
{shortcut && (
31+
<CommandShortcut
32+
size={size}
33+
value={shortcut}
34+
/>
35+
)}
36+
</_CommandItem>
37+
);
38+
};
39+
40+
export default CommandItem;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { CommandList as _CommandList } from 'cmdk';
2+
3+
import { cn } from '@/lib/utils';
4+
5+
import { commandVariants } from './command-variants';
6+
import type { CommandListProps } from './types';
7+
8+
const CommandList = (props: CommandListProps) => {
9+
const { className, size, ...rest } = props;
10+
11+
const { list } = commandVariants({ size });
12+
13+
const mergedClass = cn(list(), className);
14+
return (
15+
<_CommandList
16+
{...rest}
17+
className={mergedClass}
18+
data-slot="command-list"
19+
/>
20+
);
21+
};
22+
23+
export default CommandList;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import CommandGroup from './CommandGroup';
2+
import CommandItem from './CommandItem';
3+
import CommandSeparator from './CommandSeparator';
4+
import { isGroup, isSeparator } from './shared';
5+
import type { CommandOptionProps } from './types';
6+
7+
const CommandOption = (props: CommandOptionProps) => {
8+
const { classNames, item, size } = props;
9+
10+
if (isGroup(item)) {
11+
const { children, label, ...rest2 } = item;
12+
return (
13+
<CommandGroup
14+
classNames={classNames}
15+
heading={label}
16+
{...rest2}
17+
>
18+
{children.map((child, index) => (
19+
<CommandOption
20+
classNames={classNames}
21+
item={child}
22+
key={String(index)}
23+
size={size}
24+
/>
25+
))}
26+
</CommandGroup>
27+
);
28+
}
29+
30+
if (isSeparator(item)) {
31+
return (
32+
<CommandSeparator
33+
className={classNames?.separator}
34+
size={size}
35+
{...item}
36+
/>
37+
);
38+
}
39+
40+
return (
41+
<CommandItem
42+
className={classNames?.item}
43+
size={size}
44+
{...item}
45+
>
46+
{item.label}
47+
</CommandItem>
48+
);
49+
};
50+
51+
export default CommandOption;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { CommandRoot as _CommandRoot } from 'cmdk';
2+
import type { ComponentRef } from 'react';
3+
import { forwardRef } from 'react';
4+
5+
import { cn } from '@/lib/utils';
6+
7+
import { commandVariants } from './command-variants';
8+
import type { CommandRootProps } from './types';
9+
10+
const CommandRoot = forwardRef<ComponentRef<typeof _CommandRoot>, CommandRootProps>((props, ref) => {
11+
const { className, size, ...rest } = props;
12+
13+
const { root } = commandVariants({ size });
14+
15+
const mergedClass = cn(root(), className);
16+
return (
17+
<_CommandRoot
18+
{...rest}
19+
className={mergedClass}
20+
data-slot="command-root"
21+
ref={ref}
22+
/>
23+
);
24+
});
25+
26+
CommandRoot.displayName = 'CommandRoot';
27+
28+
export default CommandRoot;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { CommandSeparator as _CommandSeparator } from 'cmdk';
2+
3+
import { cn } from '@/lib/utils';
4+
5+
import { commandVariants } from './command-variants';
6+
import type { CommandSeparatorProps } from './types';
7+
8+
const CommandSeparator = (props: CommandSeparatorProps) => {
9+
const { className, size, ...rest } = props;
10+
11+
const { separator } = commandVariants({ size });
12+
13+
const mergedClass = cn(separator(), className);
14+
return (
15+
<_CommandSeparator
16+
{...rest}
17+
className={mergedClass}
18+
data-slot="command-separator"
19+
/>
20+
);
21+
};
22+
23+
export default CommandSeparator;

0 commit comments

Comments
 (0)