Skip to content

Commit 2f5ce3a

Browse files
committed
add Command
1 parent d3a727a commit 2f5ce3a

13 files changed

Lines changed: 25512 additions & 1 deletion

File tree

apps/playground/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"@opendatacapture/runtime-v1": "workspace:*",
2323
"@opendatacapture/schemas": "workspace:*",
2424
"axios": "catalog:",
25+
"cmdk": "^1.1.1",
2526
"esbuild-wasm": "catalog:",
2627
"idb-keyval": "^6.2.2",
2728
"immer": "^10.1.1",
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Meta, StoryObj } from '@storybook/react-vite';
2+
import { CalculatorIcon, CalendarIcon, CreditCardIcon, SettingsIcon, SmileIcon, UserIcon } from 'lucide-react';
3+
4+
import { Command } from './Command';
5+
6+
type Story = StoryObj<typeof Command>;
7+
8+
export default { component: Command } as Meta<typeof Command>;
9+
10+
export const Default: Story = {
11+
args: {
12+
children: (
13+
<>
14+
<Command.Input placeholder="Type a command or search..." />
15+
<Command.List>
16+
<Command.Empty>No results found.</Command.Empty>
17+
<Command.Group heading="Suggestions">
18+
<Command.Item>
19+
<CalendarIcon className="mr-2 h-4 w-4" />
20+
<span>Calendar</span>
21+
</Command.Item>
22+
<Command.Item>
23+
<SmileIcon className="mr-2 h-4 w-4" />
24+
<span>Search Emoji</span>
25+
</Command.Item>
26+
<Command.Item>
27+
<CalculatorIcon className="mr-2 h-4 w-4" />
28+
<span>Calculator</span>
29+
</Command.Item>
30+
</Command.Group>
31+
<Command.Separator />
32+
<Command.Group heading="Settings">
33+
<Command.Item>
34+
<UserIcon className="mr-2 h-4 w-4" />
35+
<span>Profile</span>
36+
<Command.Shortcut>⌘P</Command.Shortcut>
37+
</Command.Item>
38+
<Command.Item>
39+
<CreditCardIcon className="mr-2 h-4 w-4" />
40+
<span>Billing</span>
41+
<Command.Shortcut>⌘B</Command.Shortcut>
42+
</Command.Item>
43+
<Command.Item>
44+
<SettingsIcon className="mr-2 h-4 w-4" />
45+
<span>Settings</span>
46+
<Command.Shortcut>⌘S</Command.Shortcut>
47+
</Command.Item>
48+
</Command.Group>
49+
</Command.List>
50+
</>
51+
)
52+
}
53+
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import * as React from 'react';
2+
3+
import { cn } from '@douglasneuroinformatics/libui/utils';
4+
import { Command as CommandPrimitive } from 'cmdk';
5+
import type { Simplify } from 'type-fest';
6+
7+
import { CommandEmpty } from './CommandEmpty';
8+
import { CommandGroup } from './CommandGroup';
9+
import { CommandInput } from './CommandInput';
10+
import { CommandItem } from './CommandItem';
11+
import { CommandList } from './CommandList';
12+
import { CommandSeparator } from './CommandSeparator';
13+
import { CommandShortcut } from './CommandShortcut';
14+
15+
type CommandRootProps = Simplify<React.ComponentPropsWithoutRef<typeof CommandPrimitive>>;
16+
17+
const CommandRoot = React.forwardRef<React.ElementRef<typeof CommandPrimitive>, CommandRootProps>(function Command(
18+
{ className, ...props },
19+
ref
20+
) {
21+
return (
22+
<CommandPrimitive
23+
className={cn(
24+
'bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md',
25+
className
26+
)}
27+
ref={ref}
28+
{...props}
29+
/>
30+
);
31+
});
32+
33+
export const Command = Object.assign(CommandRoot, {
34+
Empty: CommandEmpty,
35+
Group: CommandGroup,
36+
Input: CommandInput,
37+
Item: CommandItem,
38+
List: CommandList,
39+
Separator: CommandSeparator,
40+
Shortcut: CommandShortcut
41+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React, { forwardRef } from 'react';
2+
3+
import { Command as CommandPrimitive } from 'cmdk';
4+
5+
export const CommandEmpty = forwardRef<
6+
React.ElementRef<typeof CommandPrimitive.Empty>,
7+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
8+
>(function CommandEmpty(props, ref) {
9+
return <CommandPrimitive.Empty className="py-6 text-center text-sm" ref={ref} {...props} />;
10+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React, { forwardRef } from 'react';
2+
3+
import { cn } from '@douglasneuroinformatics/libui/utils';
4+
import { Command as CommandPrimitive } from 'cmdk';
5+
6+
export const CommandGroup = forwardRef<
7+
React.ElementRef<typeof CommandPrimitive.Group>,
8+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
9+
>(function CommandGroup({ className, ...props }, ref) {
10+
return (
11+
<CommandPrimitive.Group
12+
className={cn(
13+
'text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium',
14+
className
15+
)}
16+
ref={ref}
17+
{...props}
18+
/>
19+
);
20+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React, { forwardRef } from 'react';
2+
3+
import { cn } from '@douglasneuroinformatics/libui/utils';
4+
import { Command as CommandPrimitive } from 'cmdk';
5+
import { SearchIcon } from 'lucide-react';
6+
7+
export const CommandInput = forwardRef<
8+
React.ElementRef<typeof CommandPrimitive.Input>,
9+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
10+
>(function CommandInput({ className, ...props }, ref) {
11+
return (
12+
// eslint-disable-next-line react/no-unknown-property
13+
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
14+
<SearchIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" />
15+
<CommandPrimitive.Input
16+
className={cn(
17+
'placeholder:text-muted-foreground outline-hidden flex h-10 w-full rounded-md bg-transparent py-3 text-sm disabled:cursor-not-allowed disabled:opacity-50',
18+
className
19+
)}
20+
ref={ref}
21+
{...props}
22+
/>
23+
</div>
24+
);
25+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React, { forwardRef } from 'react';
2+
3+
import { cn } from '@douglasneuroinformatics/libui/utils';
4+
import { Command as CommandPrimitive } from 'cmdk';
5+
6+
export const CommandItem = forwardRef<
7+
React.ElementRef<typeof CommandPrimitive.Item>,
8+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
9+
>(function CommandItem({ className, ...props }, ref) {
10+
return (
11+
<CommandPrimitive.Item
12+
className={cn(
13+
'aria-selected:bg-accent aria-selected:text-accent-foreground rounded-xs outline-hidden relative flex cursor-pointer select-none items-center px-2 py-1.5 text-sm data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50',
14+
className
15+
)}
16+
ref={ref}
17+
{...props}
18+
/>
19+
);
20+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React, { forwardRef } from 'react';
2+
3+
import { cn } from '@douglasneuroinformatics/libui/utils';
4+
import { Command as CommandPrimitive } from 'cmdk';
5+
6+
export const CommandList = forwardRef<
7+
React.ElementRef<typeof CommandPrimitive.List>,
8+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
9+
>(function CommandList({ className, ...props }, ref) {
10+
return (
11+
<CommandPrimitive.List
12+
className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
13+
ref={ref}
14+
{...props}
15+
/>
16+
);
17+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React, { forwardRef } from 'react';
2+
3+
import { cn } from '@douglasneuroinformatics/libui/utils';
4+
import { Command as CommandPrimitive } from 'cmdk';
5+
6+
export const CommandSeparator = forwardRef<
7+
React.ElementRef<typeof CommandPrimitive.Separator>,
8+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
9+
>(function CommandSeparator({ className, ...props }, ref) {
10+
return <CommandPrimitive.Separator className={cn('bg-border -mx-1 h-px', className)} ref={ref} {...props} />;
11+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import * as React from 'react';
2+
3+
import { cn } from '@douglasneuroinformatics/libui/utils';
4+
5+
export const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
6+
return <span className={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)} {...props} />;
7+
};

0 commit comments

Comments
 (0)