sonance-brand-mcp 1.2.5 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/dist/assets/api/sonance-analyze/route.ts +1116 -0
  2. package/dist/assets/api/sonance-assets/route.ts +113 -0
  3. package/dist/assets/api/sonance-components/route.ts +41 -0
  4. package/dist/assets/api/sonance-inject-id/route.ts +363 -0
  5. package/dist/assets/api/sonance-save-logo/route.ts +426 -0
  6. package/dist/assets/api/sonance-theme/route.ts +106 -0
  7. package/dist/assets/brand-system.ts +1265 -0
  8. package/dist/assets/components/accordion.stories.tsx +26 -26
  9. package/dist/assets/components/accordion.tsx +3 -3
  10. package/dist/assets/components/alert-dialog.stories.tsx +142 -0
  11. package/dist/assets/components/alert-dialog.tsx +143 -0
  12. package/dist/assets/components/alert.stories.tsx +3 -3
  13. package/dist/assets/components/alert.tsx +4 -3
  14. package/dist/assets/components/aspect-ratio.stories.tsx +70 -0
  15. package/dist/assets/components/aspect-ratio.tsx +8 -0
  16. package/dist/assets/components/autocomplete.stories.tsx +9 -9
  17. package/dist/assets/components/autocomplete.tsx +3 -3
  18. package/dist/assets/components/avatar.stories.tsx +5 -5
  19. package/dist/assets/components/avatar.tsx +67 -23
  20. package/dist/assets/components/badge.stories.tsx +10 -10
  21. package/dist/assets/components/badge.tsx +3 -3
  22. package/dist/assets/components/breadcrumbs.stories.tsx +7 -7
  23. package/dist/assets/components/breadcrumbs.tsx +13 -8
  24. package/dist/assets/components/button.stories.tsx +74 -74
  25. package/dist/assets/components/button.tsx +2 -0
  26. package/dist/assets/components/calendar.stories.tsx +11 -11
  27. package/dist/assets/components/calendar.tsx +4 -4
  28. package/dist/assets/components/card.stories.tsx +22 -22
  29. package/dist/assets/components/card.tsx +7 -3
  30. package/dist/assets/components/carousel.stories.tsx +158 -0
  31. package/dist/assets/components/carousel.tsx +264 -0
  32. package/dist/assets/components/chart.stories.tsx +376 -0
  33. package/dist/assets/components/chart.tsx +384 -0
  34. package/dist/assets/components/checkbox-group.stories.tsx +6 -6
  35. package/dist/assets/components/checkbox-group.tsx +3 -3
  36. package/dist/assets/components/checkbox.stories.tsx +23 -20
  37. package/dist/assets/components/checkbox.tsx +13 -6
  38. package/dist/assets/components/code.stories.tsx +24 -24
  39. package/dist/assets/components/code.tsx +22 -27
  40. package/dist/assets/components/collapsible.stories.tsx +128 -0
  41. package/dist/assets/components/collapsible.tsx +10 -0
  42. package/dist/assets/components/command.stories.tsx +183 -0
  43. package/dist/assets/components/command.tsx +171 -0
  44. package/dist/assets/components/context-menu.stories.tsx +159 -0
  45. package/dist/assets/components/context-menu.tsx +214 -0
  46. package/dist/assets/components/date-input.stories.tsx +9 -9
  47. package/dist/assets/components/date-input.tsx +2 -2
  48. package/dist/assets/components/date-picker.stories.tsx +9 -9
  49. package/dist/assets/components/date-picker.tsx +3 -3
  50. package/dist/assets/components/date-range-picker.stories.tsx +12 -12
  51. package/dist/assets/components/date-range-picker.tsx +3 -3
  52. package/dist/assets/components/dialog.stories.tsx +40 -40
  53. package/dist/assets/components/dialog.tsx +8 -12
  54. package/dist/assets/components/divider.stories.tsx +30 -30
  55. package/dist/assets/components/divider.tsx +34 -35
  56. package/dist/assets/components/drawer.stories.tsx +32 -31
  57. package/dist/assets/components/drawer.tsx +7 -6
  58. package/dist/assets/components/dropdown-menu.tsx +213 -0
  59. package/dist/assets/components/dropdown.stories.tsx +12 -12
  60. package/dist/assets/components/dropdown.tsx +5 -5
  61. package/dist/assets/components/form.stories.tsx +30 -29
  62. package/dist/assets/components/form.tsx +5 -5
  63. package/dist/assets/components/hover-card.stories.tsx +115 -0
  64. package/dist/assets/components/hover-card.tsx +35 -0
  65. package/dist/assets/components/image.stories.tsx +48 -25
  66. package/dist/assets/components/image.tsx +8 -5
  67. package/dist/assets/components/input-otp.stories.tsx +15 -15
  68. package/dist/assets/components/input-otp.tsx +5 -5
  69. package/dist/assets/components/input.stories.tsx +30 -25
  70. package/dist/assets/components/input.tsx +7 -4
  71. package/dist/assets/components/kbd.stories.tsx +34 -34
  72. package/dist/assets/components/kbd.tsx +9 -9
  73. package/dist/assets/components/link.stories.tsx +36 -36
  74. package/dist/assets/components/link.tsx +4 -0
  75. package/dist/assets/components/listbox.stories.tsx +5 -5
  76. package/dist/assets/components/listbox.tsx +4 -4
  77. package/dist/assets/components/menubar.stories.tsx +208 -0
  78. package/dist/assets/components/menubar.tsx +247 -0
  79. package/dist/assets/components/navbar.stories.tsx +24 -24
  80. package/dist/assets/components/navbar.tsx +8 -14
  81. package/dist/assets/components/navigation-menu.stories.tsx +239 -0
  82. package/dist/assets/components/navigation-menu.tsx +135 -0
  83. package/dist/assets/components/number-input.stories.tsx +11 -11
  84. package/dist/assets/components/number-input.tsx +3 -3
  85. package/dist/assets/components/pagination.stories.tsx +13 -13
  86. package/dist/assets/components/pagination.tsx +6 -6
  87. package/dist/assets/components/popover.stories.tsx +35 -35
  88. package/dist/assets/components/popover.tsx +98 -15
  89. package/dist/assets/components/progress.stories.tsx +5 -5
  90. package/dist/assets/components/progress.tsx +5 -5
  91. package/dist/assets/components/radio-group.stories.tsx +7 -7
  92. package/dist/assets/components/radio-group.tsx +3 -3
  93. package/dist/assets/components/range-calendar.stories.tsx +18 -18
  94. package/dist/assets/components/range-calendar.tsx +3 -3
  95. package/dist/assets/components/resizable.stories.tsx +197 -0
  96. package/dist/assets/components/resizable.tsx +47 -0
  97. package/dist/assets/components/scroll-area.stories.tsx +123 -0
  98. package/dist/assets/components/scroll-area.tsx +48 -0
  99. package/dist/assets/components/scroll-shadow.stories.tsx +17 -17
  100. package/dist/assets/components/scroll-shadow.tsx +31 -9
  101. package/dist/assets/components/select.stories.tsx +20 -19
  102. package/dist/assets/components/select.tsx +10 -6
  103. package/dist/assets/components/separator.tsx +32 -0
  104. package/dist/assets/components/sheet.tsx +137 -0
  105. package/dist/assets/components/sidebar.stories.tsx +351 -0
  106. package/dist/assets/components/sidebar.tsx +757 -0
  107. package/dist/assets/components/skeleton.stories.tsx +3 -3
  108. package/dist/assets/components/skeleton.tsx +2 -2
  109. package/dist/assets/components/slider.stories.tsx +6 -6
  110. package/dist/assets/components/slider.tsx +3 -3
  111. package/dist/assets/components/spacer.stories.tsx +11 -11
  112. package/dist/assets/components/spacer.tsx +2 -2
  113. package/dist/assets/components/spinner.stories.tsx +8 -8
  114. package/dist/assets/components/spinner.tsx +5 -5
  115. package/dist/assets/components/switch.stories.tsx +24 -20
  116. package/dist/assets/components/switch.tsx +14 -6
  117. package/dist/assets/components/table.stories.tsx +7 -7
  118. package/dist/assets/components/table.tsx +8 -8
  119. package/dist/assets/components/tabs.stories.tsx +37 -37
  120. package/dist/assets/components/tabs.tsx +3 -3
  121. package/dist/assets/components/textarea.stories.tsx +13 -12
  122. package/dist/assets/components/textarea.tsx +3 -3
  123. package/dist/assets/components/theme-toggle.stories.tsx +31 -30
  124. package/dist/assets/components/theme-toggle.tsx +2 -2
  125. package/dist/assets/components/time-input.stories.tsx +16 -16
  126. package/dist/assets/components/time-input.tsx +2 -2
  127. package/dist/assets/components/toast.stories.tsx +8 -5
  128. package/dist/assets/components/toast.tsx +6 -6
  129. package/dist/assets/components/toggle-group.stories.tsx +153 -0
  130. package/dist/assets/components/toggle-group.tsx +61 -0
  131. package/dist/assets/components/toggle.stories.tsx +77 -0
  132. package/dist/assets/components/toggle.tsx +46 -0
  133. package/dist/assets/components/tooltip.stories.tsx +49 -27
  134. package/dist/assets/components/tooltip.tsx +23 -90
  135. package/dist/assets/components/user.stories.tsx +23 -23
  136. package/dist/assets/components/user.tsx +7 -4
  137. package/dist/assets/dev-tools/SonanceDevTools.tsx +4201 -0
  138. package/dist/assets/dev-tools/index.ts +10 -0
  139. package/dist/assets/globals.css +39 -0
  140. package/dist/assets/logos/40th-anniversary/Sonance_40_Logo_CMYK_BEAM_BLUE_40_AND_BEAM_DARK.png +0 -0
  141. package/dist/assets/logos/Sonance logo dark mode.png +0 -0
  142. package/dist/assets/logos/Sonance logo light mode.png +0 -0
  143. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_2C_Light_RGB_05162025.png +0 -0
  144. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_3C_Dark_RGB_05162025.png +0 -0
  145. package/dist/assets/logos/blaze/BlazeBySonance_Logo_Lockup_White_RGB_05162025.png +0 -0
  146. package/dist/assets/logos/iport/IPORT_Sonance_LockUp_2C_Dark_RGB.png +0 -0
  147. package/dist/assets/logos/iport/IPORT_Sonance_LockUp_2C_Light_RGB.png +0 -0
  148. package/dist/assets/logos/james/James_Logo_Black_CMYK.png +0 -0
  149. package/dist/assets/logos/james/James_Logo_Black_RGB.png +0 -0
  150. package/dist/assets/logos/james/James_Logo_LtGray_CMYK.png +0 -0
  151. package/dist/assets/logos/james/James_Logo_LtGray_RGB.png +0 -0
  152. package/dist/assets/logos/james/James_Logo_Polished_RGB.png +0 -0
  153. package/dist/assets/logos/james/James_Logo_Reverse_CMYK.png +0 -0
  154. package/dist/assets/logos/james/James_Logo_Reverse_RGB.png +0 -0
  155. package/dist/assets/logos/james/James_Logo_White_CMYK.png +0 -0
  156. package/dist/assets/logos/life-is-better/Sonance_LifeisBetter_Dark_RGB.png +0 -0
  157. package/dist/assets/logos/life-is-better/Sonance_LifeisBetter_Light_RGB.png +0 -0
  158. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Dark_RGB.png +0 -0
  159. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Light_RGB.png +0 -0
  160. package/dist/assets/logos/my-sonance/My.Sonance_Logo_2C_Reverse_RGB.png +0 -0
  161. package/dist/assets/logos/my-sonance/My.Sonance_Logo_Black_RGB.png +0 -0
  162. package/dist/assets/logos/my-sonance/My.Sonance_Logo_Reverse_RGB.png +0 -0
  163. package/dist/assets/logos/sonance/Sonance_Logo_2C_Dark_RGB.png +0 -0
  164. package/dist/assets/logos/sonance/Sonance_Logo_2C_Light_RGB.png +0 -0
  165. package/dist/assets/logos/sonance/Sonance_Logo_2C_Reverse_RGB.png +0 -0
  166. package/dist/assets/logos/sonance/Sonance_Logo_Black_RGB.png +0 -0
  167. package/dist/assets/logos/sonance/Sonance_Logo_Grayscale_RGB.png +0 -0
  168. package/dist/assets/logos/sonance/Sonance_Logo_Reverse_RGB.png +0 -0
  169. package/dist/assets/logos/sonance-academy/SonanceAcademy_Logo_Dark_CMYK.png +0 -0
  170. package/dist/assets/logos/sonance-academy/SonanceAcademy_Logo_Light_CMYK.png +0 -0
  171. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Dark_RGB.png +0 -0
  172. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Light_RGB.png +0 -0
  173. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_3C_Reverse_RGB.png +0 -0
  174. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Black_RGB.png +0 -0
  175. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Grayscale_RGB.png +0 -0
  176. package/dist/assets/logos/sonance-iport/Sonance_IPORT_LockUp_Reverse_RGB.png +0 -0
  177. package/dist/assets/logos/sonance-james/Sonance_James_Lockup_Dark.png +0 -0
  178. package/dist/assets/logos/sonance-james/Sonance_James_Lockup_Light.png +0 -0
  179. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_LockupStacked_Dark.png +0 -0
  180. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_LockupStacked_Light.png +0 -0
  181. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Dark.png +0 -0
  182. package/dist/assets/logos/sonance-james-iport/Sonance_James_IPORT_Lockup_Light.png +0 -0
  183. package/dist/assets/logos/trufig/TrufigLogo_Black.png +0 -0
  184. package/dist/assets/logos/trufig/TrufigLogo_Light.png +0 -0
  185. package/dist/assets/logos/trufig/TrufigWatermark_Black.png +0 -0
  186. package/dist/assets/logos/trufig/TrufigWatermark_Light.png +0 -0
  187. package/dist/assets/styles/brand-overrides.css +37 -0
  188. package/dist/index.js +2055 -15
  189. package/package.json +1 -1
@@ -0,0 +1,171 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Command as CommandPrimitive } from "cmdk";
5
+ import { Search } from "lucide-react";
6
+ import { cn } from "@/lib/utils";
7
+ import {
8
+ Dialog,
9
+ DialogContent,
10
+ DialogTitle,
11
+ } from "@/components/ui/dialog";
12
+
13
+ const Command = React.forwardRef<
14
+ React.ElementRef<typeof CommandPrimitive>,
15
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive>
16
+ >(({ className, ...props }, ref) => (
17
+ <CommandPrimitive
18
+ ref={ref}
19
+ className={cn(
20
+ "flex h-full w-full flex-col overflow-hidden rounded-sm bg-card text-foreground",
21
+ className
22
+ )} data-sonance-name="command"
23
+ {...props}
24
+ />
25
+ ));
26
+ Command.displayName = CommandPrimitive.displayName;
27
+
28
+ interface CommandDialogProps {
29
+ open?: boolean;
30
+ onOpenChange?: (open: boolean) => void;
31
+ children: React.ReactNode;
32
+ }
33
+
34
+ const CommandDialog = ({ children, open, onOpenChange }: CommandDialogProps) => {
35
+ const handleClose = React.useCallback(() => {
36
+ onOpenChange?.(false);
37
+ }, [onOpenChange]);
38
+
39
+ return (
40
+ <Dialog data-sonance-name="command" open={open ?? false} onClose={handleClose}>
41
+ <DialogContent className="overflow-hidden p-0 shadow-lg" showClose={false}>
42
+ <DialogTitle className="sr-only">Command Menu</DialogTitle>
43
+ <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-foreground-muted [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
44
+ {children}
45
+ </Command>
46
+ </DialogContent>
47
+ </Dialog>
48
+ );
49
+ };
50
+
51
+ const CommandInput = React.forwardRef<
52
+ React.ElementRef<typeof CommandPrimitive.Input>,
53
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
54
+ >(({ className, ...props }, ref) => (
55
+ <div className="flex items-center border-b border-border px-3" cmdk-input-wrapper="">
56
+ <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
57
+ <CommandPrimitive.Input
58
+ ref={ref}
59
+ className={cn(
60
+ "flex h-11 w-full rounded-sm bg-transparent py-3 text-sm outline-none placeholder:text-foreground-muted disabled:cursor-not-allowed disabled:opacity-50",
61
+ className
62
+ )}
63
+ {...props}
64
+ />
65
+ </div>
66
+ ));
67
+
68
+ CommandInput.displayName = CommandPrimitive.Input.displayName;
69
+
70
+ const CommandList = React.forwardRef<
71
+ React.ElementRef<typeof CommandPrimitive.List>,
72
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
73
+ >(({ className, ...props }, ref) => (
74
+ <CommandPrimitive.List
75
+ ref={ref}
76
+ className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
77
+ {...props}
78
+ />
79
+ ));
80
+
81
+ CommandList.displayName = CommandPrimitive.List.displayName;
82
+
83
+ const CommandEmpty = React.forwardRef<
84
+ React.ElementRef<typeof CommandPrimitive.Empty>,
85
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
86
+ >((props, ref) => (
87
+ <CommandPrimitive.Empty
88
+ ref={ref}
89
+ className="py-6 text-center text-sm text-foreground-muted"
90
+ {...props}
91
+ />
92
+ ));
93
+
94
+ CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
95
+
96
+ const CommandGroup = React.forwardRef<
97
+ React.ElementRef<typeof CommandPrimitive.Group>,
98
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
99
+ >(({ className, ...props }, ref) => (
100
+ <CommandPrimitive.Group
101
+ ref={ref}
102
+ className={cn(
103
+ "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-widest [&_[cmdk-group-heading]]:text-foreground-muted",
104
+ className
105
+ )}
106
+ {...props}
107
+ />
108
+ ));
109
+
110
+ CommandGroup.displayName = CommandPrimitive.Group.displayName;
111
+
112
+ const CommandSeparator = React.forwardRef<
113
+ React.ElementRef<typeof CommandPrimitive.Separator>,
114
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
115
+ >(({ className, ...props }, ref) => (
116
+ <CommandPrimitive.Separator
117
+ ref={ref}
118
+ className={cn("-mx-1 h-px bg-border", className)}
119
+ {...props}
120
+ />
121
+ ));
122
+ CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
123
+
124
+ const CommandItem = React.forwardRef<
125
+ React.ElementRef<typeof CommandPrimitive.Item>,
126
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
127
+ >(({ className, ...props }, ref) => (
128
+ <CommandPrimitive.Item
129
+ ref={ref}
130
+ className={cn(
131
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none",
132
+ "data-[selected=true]:bg-secondary-hover data-[selected=true]:text-foreground",
133
+ "data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
134
+ "[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
135
+ className
136
+ )}
137
+ {...props}
138
+ />
139
+ ));
140
+
141
+ CommandItem.displayName = CommandPrimitive.Item.displayName;
142
+
143
+ const CommandShortcut = ({
144
+ className,
145
+ ...props
146
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
147
+ return (
148
+ <span
149
+ id="command-shortcut-span"
150
+ className={cn(
151
+ "ml-auto text-xs tracking-widest text-foreground-muted",
152
+ className
153
+ )} data-sonance-name="command"
154
+ {...props}
155
+ />
156
+ );
157
+ };
158
+ CommandShortcut.displayName = "CommandShortcut";
159
+
160
+ export {
161
+ Command,
162
+ CommandDialog,
163
+ CommandInput,
164
+ CommandList,
165
+ CommandEmpty,
166
+ CommandGroup,
167
+ CommandItem,
168
+ CommandShortcut,
169
+ CommandSeparator,
170
+ };
171
+
@@ -0,0 +1,159 @@
1
+ "use client";
2
+
3
+ import type { Meta, StoryObj } from "@storybook/react";
4
+ import { useState } from "react";
5
+ import {
6
+ ContextMenu,
7
+ ContextMenuCheckboxItem,
8
+ ContextMenuContent,
9
+ ContextMenuItem,
10
+ ContextMenuLabel,
11
+ ContextMenuRadioGroup,
12
+ ContextMenuRadioItem,
13
+ ContextMenuSeparator,
14
+ ContextMenuShortcut,
15
+ ContextMenuSub,
16
+ ContextMenuSubContent,
17
+ ContextMenuSubTrigger,
18
+ ContextMenuTrigger,
19
+ } from "./context-menu";
20
+
21
+ const meta: Meta<typeof ContextMenu> = {
22
+ title: "Components/Navigation/ContextMenu",
23
+ component: ContextMenu,
24
+ parameters: {
25
+ layout: "centered",
26
+ },
27
+ tags: ["autodocs"],
28
+ };
29
+
30
+ export default meta;
31
+ type Story = StoryObj<typeof ContextMenu>;
32
+
33
+ export const Default: Story = {
34
+ render: () => (
35
+ <ContextMenu>
36
+ <ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-sm border border-dashed border-border text-sm text-foreground-muted">
37
+ Right click here
38
+ </ContextMenuTrigger>
39
+ <ContextMenuContent className="w-64">
40
+ <ContextMenuItem>
41
+ Back
42
+ <ContextMenuShortcut>⌘[</ContextMenuShortcut>
43
+ </ContextMenuItem>
44
+ <ContextMenuItem disabled>
45
+ Forward
46
+ <ContextMenuShortcut>⌘]</ContextMenuShortcut>
47
+ </ContextMenuItem>
48
+ <ContextMenuItem>
49
+ Reload
50
+ <ContextMenuShortcut>⌘R</ContextMenuShortcut>
51
+ </ContextMenuItem>
52
+ <ContextMenuSub>
53
+ <ContextMenuSubTrigger>More Tools</ContextMenuSubTrigger>
54
+ <ContextMenuSubContent className="w-48">
55
+ <ContextMenuItem>
56
+ Save Page As...
57
+ <ContextMenuShortcut>⇧⌘S</ContextMenuShortcut>
58
+ </ContextMenuItem>
59
+ <ContextMenuItem>Create Shortcut...</ContextMenuItem>
60
+ <ContextMenuItem>Name Window...</ContextMenuItem>
61
+ <ContextMenuSeparator />
62
+ <ContextMenuItem>Developer Tools</ContextMenuItem>
63
+ </ContextMenuSubContent>
64
+ </ContextMenuSub>
65
+ <ContextMenuSeparator />
66
+ <ContextMenuCheckboxItem checked>
67
+ Show Bookmarks Bar
68
+ <ContextMenuShortcut>⌘⇧B</ContextMenuShortcut>
69
+ </ContextMenuCheckboxItem>
70
+ <ContextMenuCheckboxItem>Show Full URLs</ContextMenuCheckboxItem>
71
+ <ContextMenuSeparator />
72
+ <ContextMenuRadioGroup value="pedro">
73
+ <ContextMenuLabel inset>People</ContextMenuLabel>
74
+ <ContextMenuRadioItem value="pedro">
75
+ Pedro Duarte
76
+ </ContextMenuRadioItem>
77
+ <ContextMenuRadioItem value="colm">Colm Tuite</ContextMenuRadioItem>
78
+ </ContextMenuRadioGroup>
79
+ </ContextMenuContent>
80
+ </ContextMenu>
81
+ ),
82
+ };
83
+
84
+ function ContextMenuWithStateDemo() {
85
+ const [bookmarksChecked, setBookmarksChecked] = useState(true);
86
+ const [urlsChecked, setUrlsChecked] = useState(false);
87
+ const [person, setPerson] = useState("pedro");
88
+
89
+ return (
90
+ <ContextMenu>
91
+ <ContextMenuTrigger className="flex h-[150px] w-[300px] items-center justify-center rounded-sm border border-dashed border-border text-sm text-foreground-muted">
92
+ Right click here
93
+ </ContextMenuTrigger>
94
+ <ContextMenuContent className="w-64">
95
+ <ContextMenuItem>New Tab</ContextMenuItem>
96
+ <ContextMenuItem>New Window</ContextMenuItem>
97
+ <ContextMenuSeparator />
98
+ <ContextMenuCheckboxItem
99
+ checked={bookmarksChecked}
100
+ onCheckedChange={setBookmarksChecked}
101
+ >
102
+ Show Bookmarks
103
+ </ContextMenuCheckboxItem>
104
+ <ContextMenuCheckboxItem
105
+ checked={urlsChecked}
106
+ onCheckedChange={setUrlsChecked}
107
+ >
108
+ Show Full URLs
109
+ </ContextMenuCheckboxItem>
110
+ <ContextMenuSeparator />
111
+ <ContextMenuLabel>Switch User</ContextMenuLabel>
112
+ <ContextMenuRadioGroup value={person} onValueChange={setPerson}>
113
+ <ContextMenuRadioItem value="pedro">Pedro</ContextMenuRadioItem>
114
+ <ContextMenuRadioItem value="colm">Colm</ContextMenuRadioItem>
115
+ <ContextMenuRadioItem value="guest">Guest</ContextMenuRadioItem>
116
+ </ContextMenuRadioGroup>
117
+ </ContextMenuContent>
118
+ </ContextMenu>
119
+ );
120
+ }
121
+
122
+ export const WithState: Story = {
123
+ render: () => <ContextMenuWithStateDemo />,
124
+ };
125
+
126
+ export const FileContextMenu: Story = {
127
+ render: () => (
128
+ <ContextMenu>
129
+ <ContextMenuTrigger className="flex h-[100px] w-[200px] flex-col items-center justify-center gap-2 rounded-sm border border-border bg-card p-4">
130
+ <div className="h-12 w-12 rounded-sm bg-secondary-hover" />
131
+ <span id="file-context-menu-span-documentpdf" className="text-sm text-foreground">Document.pdf</span>
132
+ </ContextMenuTrigger>
133
+ <ContextMenuContent className="w-48">
134
+ <ContextMenuItem>
135
+ Open
136
+ <ContextMenuShortcut>⌘O</ContextMenuShortcut>
137
+ </ContextMenuItem>
138
+ <ContextMenuItem>Open With...</ContextMenuItem>
139
+ <ContextMenuSeparator />
140
+ <ContextMenuItem>
141
+ Copy
142
+ <ContextMenuShortcut>⌘C</ContextMenuShortcut>
143
+ </ContextMenuItem>
144
+ <ContextMenuItem>
145
+ Cut
146
+ <ContextMenuShortcut>⌘X</ContextMenuShortcut>
147
+ </ContextMenuItem>
148
+ <ContextMenuItem>Rename</ContextMenuItem>
149
+ <ContextMenuSeparator />
150
+ <ContextMenuItem>Share...</ContextMenuItem>
151
+ <ContextMenuItem className="text-error">
152
+ Delete
153
+ <ContextMenuShortcut>⌘⌫</ContextMenuShortcut>
154
+ </ContextMenuItem>
155
+ </ContextMenuContent>
156
+ </ContextMenu>
157
+ ),
158
+ };
159
+
@@ -0,0 +1,214 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
5
+ import { Check, ChevronRight, Circle } from "lucide-react";
6
+ import { cn } from "@/lib/utils";
7
+
8
+ const ContextMenu = ContextMenuPrimitive.Root;
9
+ const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
10
+ const ContextMenuGroup = ContextMenuPrimitive.Group;
11
+ const ContextMenuPortal = ContextMenuPrimitive.Portal;
12
+ const ContextMenuSub = ContextMenuPrimitive.Sub;
13
+ const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
14
+
15
+ const ContextMenuSubTrigger = React.forwardRef<
16
+ React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
17
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
18
+ inset?: boolean;
19
+ }
20
+ >(({ className, inset, children, ...props }, ref) => (
21
+ <ContextMenuPrimitive.SubTrigger
22
+ ref={ref}
23
+ className={cn(
24
+ "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
25
+ "focus:bg-secondary-hover focus:text-foreground",
26
+ "data-[state=open]:bg-secondary-hover data-[state=open]:text-foreground",
27
+ inset && "pl-8",
28
+ className
29
+ )}
30
+ {...props}
31
+ >
32
+ {children}
33
+ <ChevronRight className="ml-auto h-4 w-4" />
34
+ </ContextMenuPrimitive.SubTrigger>
35
+ ));
36
+ ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
37
+
38
+ const ContextMenuSubContent = React.forwardRef<
39
+ React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
40
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
41
+ >(({ className, ...props }, ref) => (
42
+ <ContextMenuPrimitive.SubContent
43
+ ref={ref}
44
+ className={cn(
45
+ "z-50 min-w-[8rem] overflow-hidden rounded-sm border border-border bg-card p-1 text-foreground shadow-md",
46
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
47
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
48
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
49
+ "data-[side=bottom]:slide-in-from-top-2",
50
+ "data-[side=left]:slide-in-from-right-2",
51
+ "data-[side=right]:slide-in-from-left-2",
52
+ "data-[side=top]:slide-in-from-bottom-2",
53
+ className
54
+ )}
55
+ {...props}
56
+ />
57
+ ));
58
+ ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
59
+
60
+ const ContextMenuContent = React.forwardRef<
61
+ React.ElementRef<typeof ContextMenuPrimitive.Content>,
62
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
63
+ >(({ className, ...props }, ref) => (
64
+ <ContextMenuPrimitive.Portal>
65
+ <ContextMenuPrimitive.Content
66
+ ref={ref}
67
+ className={cn(
68
+ "z-50 min-w-[8rem] overflow-hidden rounded-sm border border-border bg-card p-1 text-foreground shadow-md",
69
+ "animate-in fade-in-80",
70
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
71
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
72
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
73
+ "data-[side=bottom]:slide-in-from-top-2",
74
+ "data-[side=left]:slide-in-from-right-2",
75
+ "data-[side=right]:slide-in-from-left-2",
76
+ "data-[side=top]:slide-in-from-bottom-2",
77
+ className
78
+ )}
79
+ {...props}
80
+ />
81
+ </ContextMenuPrimitive.Portal>
82
+ ));
83
+ ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
84
+
85
+ const ContextMenuItem = React.forwardRef<
86
+ React.ElementRef<typeof ContextMenuPrimitive.Item>,
87
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
88
+ inset?: boolean;
89
+ }
90
+ >(({ className, inset, ...props }, ref) => (
91
+ <ContextMenuPrimitive.Item
92
+ ref={ref}
93
+ className={cn(
94
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
95
+ "focus:bg-secondary-hover focus:text-foreground",
96
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
97
+ inset && "pl-8",
98
+ className
99
+ )}
100
+ {...props}
101
+ />
102
+ ));
103
+ ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
104
+
105
+ const ContextMenuCheckboxItem = React.forwardRef<
106
+ React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
107
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
108
+ >(({ className, children, checked, ...props }, ref) => (
109
+ <ContextMenuPrimitive.CheckboxItem
110
+ ref={ref}
111
+ className={cn(
112
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
113
+ "focus:bg-secondary-hover focus:text-foreground",
114
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
115
+ className
116
+ )}
117
+ checked={checked}
118
+ {...props}
119
+ >
120
+ <span id="context-menu-checkbox-item-span" className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
121
+ <ContextMenuPrimitive.ItemIndicator>
122
+ <Check className="h-4 w-4" />
123
+ </ContextMenuPrimitive.ItemIndicator>
124
+ </span>
125
+ {children}
126
+ </ContextMenuPrimitive.CheckboxItem>
127
+ ));
128
+ ContextMenuCheckboxItem.displayName =
129
+ ContextMenuPrimitive.CheckboxItem.displayName;
130
+
131
+ const ContextMenuRadioItem = React.forwardRef<
132
+ React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
133
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
134
+ >(({ className, children, ...props }, ref) => (
135
+ <ContextMenuPrimitive.RadioItem
136
+ ref={ref}
137
+ className={cn(
138
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
139
+ "focus:bg-secondary-hover focus:text-foreground",
140
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
141
+ className
142
+ )}
143
+ {...props}
144
+ >
145
+ <span id="context-menu-radio-item-span" className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
146
+ <ContextMenuPrimitive.ItemIndicator>
147
+ <Circle className="h-2 w-2 fill-current" />
148
+ </ContextMenuPrimitive.ItemIndicator>
149
+ </span>
150
+ {children}
151
+ </ContextMenuPrimitive.RadioItem>
152
+ ));
153
+ ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
154
+
155
+ const ContextMenuLabel = React.forwardRef<
156
+ React.ElementRef<typeof ContextMenuPrimitive.Label>,
157
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
158
+ inset?: boolean;
159
+ }
160
+ >(({ className, inset, ...props }, ref) => (
161
+ <ContextMenuPrimitive.Label
162
+ ref={ref}
163
+ className={cn(
164
+ "px-2 py-1.5 text-xs font-medium uppercase tracking-widest text-foreground-muted",
165
+ inset && "pl-8",
166
+ className
167
+ )}
168
+ {...props}
169
+ />
170
+ ));
171
+ ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
172
+
173
+ const ContextMenuSeparator = React.forwardRef<
174
+ React.ElementRef<typeof ContextMenuPrimitive.Separator>,
175
+ React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
176
+ >(({ className, ...props }, ref) => (
177
+ <ContextMenuPrimitive.Separator
178
+ ref={ref}
179
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
180
+ {...props}
181
+ />
182
+ ));
183
+ ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
184
+
185
+ const ContextMenuShortcut = ({
186
+ className,
187
+ ...props
188
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
189
+ return (
190
+ <span id="context-menu-shortcut-span" data-sonance-name="context-menu"
191
+ {...props}
192
+ />
193
+ );
194
+ };
195
+ ContextMenuShortcut.displayName = "ContextMenuShortcut";
196
+
197
+ export {
198
+ ContextMenu,
199
+ ContextMenuTrigger,
200
+ ContextMenuContent,
201
+ ContextMenuItem,
202
+ ContextMenuCheckboxItem,
203
+ ContextMenuRadioItem,
204
+ ContextMenuLabel,
205
+ ContextMenuSeparator,
206
+ ContextMenuShortcut,
207
+ ContextMenuGroup,
208
+ ContextMenuPortal,
209
+ ContextMenuSub,
210
+ ContextMenuSubContent,
211
+ ContextMenuSubTrigger,
212
+ ContextMenuRadioGroup,
213
+ };
214
+
@@ -59,13 +59,13 @@ export const Default: Story = {
59
59
  render: () => {
60
60
  const [date, setDate] = useState<Date | undefined>(undefined);
61
61
  return (
62
- <div className="w-64">
62
+ <div data-sonance-name="date-input.stories" className="w-64">
63
63
  <DateInput
64
64
  value={date}
65
65
  onValueChange={setDate}
66
66
  />
67
67
  {date && (
68
- <p className="mt-2 text-sm text-foreground-muted">
68
+ <p id="default-p-selected-datetolocal" className="mt-2 text-sm text-foreground-muted">
69
69
  Selected: {date.toLocaleDateString()}
70
70
  </p>
71
71
  )}
@@ -196,8 +196,8 @@ export const FormExample: Story = {
196
196
  error={hasError ? "End date must be after start date" : undefined}
197
197
  />
198
198
  <div className="text-sm text-foreground-muted">
199
- {startDate && <p>Start: {startDate.toLocaleDateString()}</p>}
200
- {endDate && <p>End: {endDate.toLocaleDateString()}</p>}
199
+ {startDate && <p id="form-example-p-start-startdatetoloc">Start: {startDate.toLocaleDateString()}</p>}
200
+ {endDate && <p id="form-example-p-end-enddatetolocaled">End: {endDate.toLocaleDateString()}</p>}
201
201
  </div>
202
202
  </div>
203
203
  );
@@ -222,11 +222,11 @@ export const StateMatrix: Story = {
222
222
  const states: DateInputState[] = ['default', 'hover', 'focus', 'error', 'disabled'];
223
223
  return (
224
224
  <div className="space-y-6 w-64">
225
- <h3 className="text-sm font-medium text-foreground-muted">DateInput States</h3>
225
+ <h3 id="state-matrix-h3-dateinput-states" className="text-sm font-medium text-foreground-muted">DateInput States</h3>
226
226
  <div className="space-y-4">
227
227
  {states.map((state) => (
228
228
  <div key={state}>
229
- <span className="text-xs font-medium text-foreground-muted uppercase">{state}</span>
229
+ <span id="state-matrix-span-state" className="text-xs font-medium text-foreground-muted uppercase">{state}</span>
230
230
  <DateInput
231
231
  state={state}
232
232
  placeholder="MM/DD/YYYY"
@@ -246,14 +246,14 @@ export const ResponsiveMatrix: Story = {
246
246
  <div className="space-y-8">
247
247
  {/* Mobile */}
248
248
  <div>
249
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
249
+ <h4 id="responsive-matrix-h4-mobile-375px" className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
250
250
  <div className="w-[375px] border border-dashed border-border p-4">
251
251
  <DateInput label="Date of Birth" placeholder="MM/DD/YYYY" />
252
252
  </div>
253
253
  </div>
254
254
  {/* Tablet */}
255
255
  <div>
256
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
256
+ <h4 id="responsive-matrix-h4-tablet-768px" className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
257
257
  <div className="w-[768px] border border-dashed border-border p-4">
258
258
  <div className="grid grid-cols-2 gap-4">
259
259
  <DateInput label="Start Date" placeholder="MM/DD/YYYY" />
@@ -263,7 +263,7 @@ export const ResponsiveMatrix: Story = {
263
263
  </div>
264
264
  {/* Desktop */}
265
265
  <div>
266
- <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
266
+ <h4 id="responsive-matrix-h4-desktop-1280px" className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
267
267
  <div className="w-[1280px] border border-dashed border-border p-4">
268
268
  <div className="grid grid-cols-4 gap-4">
269
269
  <DateInput label="Default" placeholder="MM/DD/YYYY" />
@@ -98,7 +98,7 @@ export const DateInput = forwardRef<HTMLInputElement, DateInputProps>(
98
98
  };
99
99
 
100
100
  return (
101
- <div className={cn("w-full", className)}>
101
+ <div data-sonance-name="date-input" className={cn("w-full", className)}>
102
102
  {label && (
103
103
  <label className="mb-2 block text-xs font-medium uppercase tracking-widest text-foreground-muted">
104
104
  {label}
@@ -125,7 +125,7 @@ export const DateInput = forwardRef<HTMLInputElement, DateInputProps>(
125
125
  />
126
126
  <CalendarIcon className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-foreground-muted" />
127
127
  </div>
128
- {error && <p className="mt-1 text-sm text-error">{error}</p>}
128
+ {error && <p id="p-error" className="mt-1 text-sm text-error">{error}</p>}
129
129
  </div>
130
130
  );
131
131
  }