@agent-native/toolkit 0.6.0 → 0.8.0
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.
- package/README.md +17 -1
- package/agent-native.eject.json +411 -0
- package/dist/chat-history/ChatHistoryList.d.ts +103 -0
- package/dist/chat-history/ChatHistoryList.d.ts.map +1 -0
- package/dist/chat-history/ChatHistoryList.js +109 -0
- package/dist/chat-history/ChatHistoryList.js.map +1 -0
- package/dist/chat-history/ChatHistoryList.spec.d.ts +2 -0
- package/dist/chat-history/ChatHistoryList.spec.d.ts.map +1 -0
- package/dist/chat-history/ChatHistoryList.spec.js +283 -0
- package/dist/chat-history/ChatHistoryList.spec.js.map +1 -0
- package/dist/chat-history/index.d.ts +2 -0
- package/dist/chat-history/index.d.ts.map +1 -0
- package/dist/chat-history/index.js +2 -0
- package/dist/chat-history/index.js.map +1 -0
- package/dist/chat-history.css +314 -0
- package/package.json +20 -1
- package/src/app-shell/header-actions.tsx +84 -0
- package/src/app-shell/index.ts +1 -0
- package/src/chat-history/ChatHistoryList.spec.tsx +453 -0
- package/src/chat-history/ChatHistoryList.tsx +469 -0
- package/src/chat-history/index.ts +7 -0
- package/src/chat-history.css +314 -0
- package/src/collab-ui/AgentPresenceChip.tsx +69 -0
- package/src/collab-ui/LiveCursorOverlay.tsx +241 -0
- package/src/collab-ui/PresenceBar.tsx +346 -0
- package/src/collab-ui/RecentEditHighlights.tsx +225 -0
- package/src/collab-ui/RemoteSelectionRings.tsx +227 -0
- package/src/collab-ui/agent-identity.ts +16 -0
- package/src/collab-ui/index.ts +39 -0
- package/src/collab-ui/lead-client.ts +32 -0
- package/src/collab-ui/types.spec.ts +18 -0
- package/src/collab-ui/types.ts +96 -0
- package/src/composer/AgentComposerFrame.tsx +59 -0
- package/src/composer/ComposerPlusMenu.tsx +983 -0
- package/src/composer/MentionPopover.tsx +412 -0
- package/src/composer/PastedTextChip.tsx +150 -0
- package/src/composer/PromptComposer.spec.ts +73 -0
- package/src/composer/PromptComposer.tsx +718 -0
- package/src/composer/RealtimeVoiceMode.spec.tsx +771 -0
- package/src/composer/RealtimeVoiceMode.tsx +856 -0
- package/src/composer/TiptapComposer.spec.ts +390 -0
- package/src/composer/TiptapComposer.tsx +2936 -0
- package/src/composer/VoiceButton.spec.ts +33 -0
- package/src/composer/VoiceButton.tsx +255 -0
- package/src/composer/asset-picker-url.spec.ts +56 -0
- package/src/composer/asset-picker-url.ts +80 -0
- package/src/composer/attachment-accept.spec.ts +22 -0
- package/src/composer/attachment-accept.ts +71 -0
- package/src/composer/draft-key.spec.ts +19 -0
- package/src/composer/draft-key.ts +7 -0
- package/src/composer/extensions/FileReference.tsx +55 -0
- package/src/composer/extensions/MentionReference.tsx +93 -0
- package/src/composer/extensions/SkillReference.tsx +56 -0
- package/src/composer/index.ts +79 -0
- package/src/composer/pasted-text.spec.ts +162 -0
- package/src/composer/pasted-text.ts +160 -0
- package/src/composer/prompt-attachments.spec.ts +80 -0
- package/src/composer/prompt-attachments.ts +93 -0
- package/src/composer/realtime-voice-audio-level.spec.ts +32 -0
- package/src/composer/realtime-voice-audio-level.ts +64 -0
- package/src/composer/realtime-voice-transcript.spec.ts +95 -0
- package/src/composer/realtime-voice-transcript.ts +134 -0
- package/src/composer/runtime-adapters.tsx +343 -0
- package/src/composer/types.ts +66 -0
- package/src/composer/use-file-search.ts +64 -0
- package/src/composer/use-mention-search.ts +90 -0
- package/src/composer/use-skills.ts +45 -0
- package/src/composer/useRealtimeVoiceMode.spec.ts +818 -0
- package/src/composer/useRealtimeVoiceMode.tsx +2117 -0
- package/src/composer/useVoiceDictation.spec.ts +45 -0
- package/src/composer/useVoiceDictation.ts +1191 -0
- package/src/context-ui/ContextMeter.tsx +101 -0
- package/src/context-ui/ContextSegmentRow.tsx +184 -0
- package/src/context-ui/ContextTreemap.tsx +125 -0
- package/src/context-ui/ContextXRayPanel.tsx +377 -0
- package/src/context-ui/format.ts +42 -0
- package/src/context-ui/index.ts +15 -0
- package/src/context-ui/types.ts +43 -0
- package/src/design-tweaks/index.ts +1 -0
- package/src/design-tweaks/visual-style-controls.tsx +1084 -0
- package/src/editor/BubbleToolbar.tsx +253 -0
- package/src/editor/CodeBlockNode.tsx +254 -0
- package/src/editor/DragHandle.spec.ts +588 -0
- package/src/editor/DragHandle.ts +1437 -0
- package/src/editor/ImageExtension.roundtrip.spec.ts +117 -0
- package/src/editor/ImageExtension.ts +306 -0
- package/src/editor/RegistryBlockContext.tsx +107 -0
- package/src/editor/RegistryBlockNode.spec.ts +279 -0
- package/src/editor/RegistryBlockNode.tsx +652 -0
- package/src/editor/RichMarkdownEditor.collab.spec.ts +79 -0
- package/src/editor/RichMarkdownEditor.roundtrip.spec.ts +226 -0
- package/src/editor/RichMarkdownEditor.tsx +81 -0
- package/src/editor/RunId.ts +61 -0
- package/src/editor/SharedRichEditor.tsx +321 -0
- package/src/editor/SlashCommandMenu.tsx +310 -0
- package/src/editor/disableHistory.spec.ts +85 -0
- package/src/editor/extensions.ts +402 -0
- package/src/editor/gfmDoc.spec.ts +230 -0
- package/src/editor/gfmDoc.ts +92 -0
- package/src/editor/index.ts +93 -0
- package/src/editor/registrySlashCommands.ts +131 -0
- package/src/editor/structuralReorderUndo.spec.ts +144 -0
- package/src/editor/surgical-apply.spec.ts +279 -0
- package/src/editor/surgical-apply.ts +192 -0
- package/src/editor/useCollabReconcile.concurrent.spec.ts +422 -0
- package/src/editor/useCollabReconcile.idempotent.spec.ts +324 -0
- package/src/editor/useCollabReconcile.ts +589 -0
- package/src/editor.css +510 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/use-mobile.tsx +21 -0
- package/src/hooks/use-toast.ts +188 -0
- package/src/index.ts +8 -0
- package/src/onboarding/index.ts +1 -0
- package/src/provider.tsx +47 -0
- package/src/sharing/VisibilityBadge.spec.ts +96 -0
- package/src/sharing/VisibilityBadge.tsx +37 -0
- package/src/sharing/index.ts +4 -0
- package/src/styles.css +21 -0
- package/src/ui/accordion.tsx +56 -0
- package/src/ui/alert-dialog.tsx +141 -0
- package/src/ui/alert.tsx +59 -0
- package/src/ui/aspect-ratio.tsx +5 -0
- package/src/ui/avatar.tsx +48 -0
- package/src/ui/badge.tsx +37 -0
- package/src/ui/breadcrumb.tsx +115 -0
- package/src/ui/button.tsx +78 -0
- package/src/ui/calendar.tsx +214 -0
- package/src/ui/card.tsx +86 -0
- package/src/ui/carousel.tsx +260 -0
- package/src/ui/chart.tsx +375 -0
- package/src/ui/checkbox.tsx +28 -0
- package/src/ui/collapsible.tsx +9 -0
- package/src/ui/command.spec.tsx +51 -0
- package/src/ui/command.tsx +174 -0
- package/src/ui/context-menu.tsx +202 -0
- package/src/ui/date-picker.tsx +64 -0
- package/src/ui/dialog.spec.tsx +43 -0
- package/src/ui/dialog.tsx +146 -0
- package/src/ui/drawer.tsx +126 -0
- package/src/ui/dropdown-menu.tsx +218 -0
- package/src/ui/form.tsx +177 -0
- package/src/ui/hover-card.tsx +27 -0
- package/src/ui/index.ts +52 -0
- package/src/ui/input-otp.tsx +69 -0
- package/src/ui/input.tsx +22 -0
- package/src/ui/label.tsx +24 -0
- package/src/ui/logo.tsx +36 -0
- package/src/ui/menubar.tsx +235 -0
- package/src/ui/navigation-menu.tsx +128 -0
- package/src/ui/pagination.tsx +121 -0
- package/src/ui/popover.tsx +44 -0
- package/src/ui/progress.tsx +26 -0
- package/src/ui/radio-group.tsx +42 -0
- package/src/ui/resizable.tsx +43 -0
- package/src/ui/scroll-area.tsx +46 -0
- package/src/ui/select.tsx +158 -0
- package/src/ui/separator.tsx +29 -0
- package/src/ui/sheet.tsx +160 -0
- package/src/ui/sidebar.tsx +777 -0
- package/src/ui/skeleton.tsx +18 -0
- package/src/ui/slider.tsx +26 -0
- package/src/ui/sonner.tsx +58 -0
- package/src/ui/spinner.tsx +17 -0
- package/src/ui/switch.tsx +27 -0
- package/src/ui/table.tsx +117 -0
- package/src/ui/tabs.tsx +53 -0
- package/src/ui/textarea.tsx +23 -0
- package/src/ui/toast.tsx +127 -0
- package/src/ui/toaster.tsx +33 -0
- package/src/ui/toggle-group.tsx +59 -0
- package/src/ui/toggle.tsx +43 -0
- package/src/ui/tooltip.tsx +53 -0
- package/src/ui/use-toast.ts +3 -0
- package/src/utils.ts +8 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
|
|
2
|
+
import {
|
|
3
|
+
IconCheck,
|
|
4
|
+
IconChevronRight,
|
|
5
|
+
IconCircleFilled,
|
|
6
|
+
} from "@tabler/icons-react";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
import { cn } from "../utils.js";
|
|
10
|
+
|
|
11
|
+
const ContextMenu = ContextMenuPrimitive.Root;
|
|
12
|
+
|
|
13
|
+
const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
|
|
14
|
+
|
|
15
|
+
const ContextMenuGroup = ContextMenuPrimitive.Group;
|
|
16
|
+
|
|
17
|
+
const ContextMenuPortal = ContextMenuPrimitive.Portal;
|
|
18
|
+
|
|
19
|
+
const ContextMenuSub = ContextMenuPrimitive.Sub;
|
|
20
|
+
|
|
21
|
+
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
|
|
22
|
+
|
|
23
|
+
const ContextMenuSubTrigger = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
|
|
26
|
+
inset?: boolean;
|
|
27
|
+
}
|
|
28
|
+
>(({ className, inset, children, ...props }, ref) => (
|
|
29
|
+
<ContextMenuPrimitive.SubTrigger
|
|
30
|
+
ref={ref}
|
|
31
|
+
className={cn(
|
|
32
|
+
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
|
|
33
|
+
inset && "ps-8",
|
|
34
|
+
className,
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
<IconChevronRight className="ms-auto h-4 w-4 rtl:-scale-x-100" />
|
|
40
|
+
</ContextMenuPrimitive.SubTrigger>
|
|
41
|
+
));
|
|
42
|
+
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
|
|
43
|
+
|
|
44
|
+
const ContextMenuSubContent = React.forwardRef<
|
|
45
|
+
React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
|
|
46
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
|
|
47
|
+
>(({ className, ...props }, ref) => (
|
|
48
|
+
<ContextMenuPrimitive.SubContent
|
|
49
|
+
ref={ref}
|
|
50
|
+
className={cn(
|
|
51
|
+
"z-[290] min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md origin-[var(--radix-context-menu-content-transform-origin)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 motion-reduce:data-[state=open]:zoom-in-100 motion-reduce:data-[state=closed]:zoom-out-100 motion-reduce:data-[side=bottom]:slide-in-from-top-0 motion-reduce:data-[side=left]:slide-in-from-right-0 motion-reduce:data-[side=right]:slide-in-from-left-0 motion-reduce:data-[side=top]:slide-in-from-bottom-0",
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
));
|
|
57
|
+
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
|
|
58
|
+
|
|
59
|
+
const ContextMenuContent = React.forwardRef<
|
|
60
|
+
React.ElementRef<typeof ContextMenuPrimitive.Content>,
|
|
61
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
|
|
62
|
+
>(({ className, ...props }, ref) => (
|
|
63
|
+
<ContextMenuPrimitive.Portal>
|
|
64
|
+
<ContextMenuPrimitive.Content
|
|
65
|
+
ref={ref}
|
|
66
|
+
className={cn(
|
|
67
|
+
"z-[290] min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md origin-[var(--radix-context-menu-content-transform-origin)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 motion-reduce:data-[state=open]:zoom-in-100 motion-reduce:data-[state=closed]:zoom-out-100 motion-reduce:data-[side=bottom]:slide-in-from-top-0 motion-reduce:data-[side=left]:slide-in-from-right-0 motion-reduce:data-[side=right]:slide-in-from-left-0 motion-reduce:data-[side=top]:slide-in-from-bottom-0",
|
|
68
|
+
className,
|
|
69
|
+
)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
</ContextMenuPrimitive.Portal>
|
|
73
|
+
));
|
|
74
|
+
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
|
|
75
|
+
|
|
76
|
+
const ContextMenuItem = React.forwardRef<
|
|
77
|
+
React.ElementRef<typeof ContextMenuPrimitive.Item>,
|
|
78
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
|
|
79
|
+
inset?: boolean;
|
|
80
|
+
}
|
|
81
|
+
>(({ className, inset, ...props }, ref) => (
|
|
82
|
+
<ContextMenuPrimitive.Item
|
|
83
|
+
ref={ref}
|
|
84
|
+
className={cn(
|
|
85
|
+
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
86
|
+
inset && "ps-8",
|
|
87
|
+
className,
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
));
|
|
92
|
+
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
|
|
93
|
+
|
|
94
|
+
const ContextMenuCheckboxItem = React.forwardRef<
|
|
95
|
+
React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
|
|
96
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
|
|
97
|
+
>(({ className, children, checked, ...props }, ref) => (
|
|
98
|
+
<ContextMenuPrimitive.CheckboxItem
|
|
99
|
+
ref={ref}
|
|
100
|
+
className={cn(
|
|
101
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
102
|
+
className,
|
|
103
|
+
)}
|
|
104
|
+
checked={checked}
|
|
105
|
+
{...props}
|
|
106
|
+
>
|
|
107
|
+
<span className="absolute start-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
108
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
109
|
+
<IconCheck className="h-4 w-4" />
|
|
110
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
111
|
+
</span>
|
|
112
|
+
{children}
|
|
113
|
+
</ContextMenuPrimitive.CheckboxItem>
|
|
114
|
+
));
|
|
115
|
+
ContextMenuCheckboxItem.displayName =
|
|
116
|
+
ContextMenuPrimitive.CheckboxItem.displayName;
|
|
117
|
+
|
|
118
|
+
const ContextMenuRadioItem = React.forwardRef<
|
|
119
|
+
React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
|
|
120
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
|
|
121
|
+
>(({ className, children, ...props }, ref) => (
|
|
122
|
+
<ContextMenuPrimitive.RadioItem
|
|
123
|
+
ref={ref}
|
|
124
|
+
className={cn(
|
|
125
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
126
|
+
className,
|
|
127
|
+
)}
|
|
128
|
+
{...props}
|
|
129
|
+
>
|
|
130
|
+
<span className="absolute start-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
131
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
132
|
+
<IconCircleFilled className="h-2 w-2" />
|
|
133
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
134
|
+
</span>
|
|
135
|
+
{children}
|
|
136
|
+
</ContextMenuPrimitive.RadioItem>
|
|
137
|
+
));
|
|
138
|
+
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
|
|
139
|
+
|
|
140
|
+
const ContextMenuLabel = React.forwardRef<
|
|
141
|
+
React.ElementRef<typeof ContextMenuPrimitive.Label>,
|
|
142
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
|
|
143
|
+
inset?: boolean;
|
|
144
|
+
}
|
|
145
|
+
>(({ className, inset, ...props }, ref) => (
|
|
146
|
+
<ContextMenuPrimitive.Label
|
|
147
|
+
ref={ref}
|
|
148
|
+
className={cn(
|
|
149
|
+
"px-2 py-1.5 text-sm font-semibold text-foreground",
|
|
150
|
+
inset && "ps-8",
|
|
151
|
+
className,
|
|
152
|
+
)}
|
|
153
|
+
{...props}
|
|
154
|
+
/>
|
|
155
|
+
));
|
|
156
|
+
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
|
|
157
|
+
|
|
158
|
+
const ContextMenuSeparator = React.forwardRef<
|
|
159
|
+
React.ElementRef<typeof ContextMenuPrimitive.Separator>,
|
|
160
|
+
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
|
|
161
|
+
>(({ className, ...props }, ref) => (
|
|
162
|
+
<ContextMenuPrimitive.Separator
|
|
163
|
+
ref={ref}
|
|
164
|
+
className={cn("-mx-1 my-1 h-px bg-border", className)}
|
|
165
|
+
{...props}
|
|
166
|
+
/>
|
|
167
|
+
));
|
|
168
|
+
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
|
|
169
|
+
|
|
170
|
+
const ContextMenuShortcut = ({
|
|
171
|
+
className,
|
|
172
|
+
...props
|
|
173
|
+
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
|
174
|
+
return (
|
|
175
|
+
<span
|
|
176
|
+
className={cn(
|
|
177
|
+
"ms-auto text-xs tracking-widest text-muted-foreground",
|
|
178
|
+
className,
|
|
179
|
+
)}
|
|
180
|
+
{...props}
|
|
181
|
+
/>
|
|
182
|
+
);
|
|
183
|
+
};
|
|
184
|
+
ContextMenuShortcut.displayName = "ContextMenuShortcut";
|
|
185
|
+
|
|
186
|
+
export {
|
|
187
|
+
ContextMenu,
|
|
188
|
+
ContextMenuTrigger,
|
|
189
|
+
ContextMenuContent,
|
|
190
|
+
ContextMenuItem,
|
|
191
|
+
ContextMenuCheckboxItem,
|
|
192
|
+
ContextMenuRadioItem,
|
|
193
|
+
ContextMenuLabel,
|
|
194
|
+
ContextMenuSeparator,
|
|
195
|
+
ContextMenuShortcut,
|
|
196
|
+
ContextMenuGroup,
|
|
197
|
+
ContextMenuPortal,
|
|
198
|
+
ContextMenuSub,
|
|
199
|
+
ContextMenuSubContent,
|
|
200
|
+
ContextMenuSubTrigger,
|
|
201
|
+
ContextMenuRadioGroup,
|
|
202
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { IconCalendar } from "@tabler/icons-react";
|
|
2
|
+
import { format, parse, isValid } from "date-fns";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../utils.js";
|
|
6
|
+
import { Button } from "./button.js";
|
|
7
|
+
import { Calendar } from "./calendar.js";
|
|
8
|
+
import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
|
|
9
|
+
|
|
10
|
+
interface DatePickerProps {
|
|
11
|
+
/** Date string in YYYY-MM-DD format */
|
|
12
|
+
value: string;
|
|
13
|
+
onChange: (value: string) => void;
|
|
14
|
+
className?: string;
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function DatePicker({
|
|
19
|
+
value,
|
|
20
|
+
onChange,
|
|
21
|
+
className,
|
|
22
|
+
placeholder = "Pick a date",
|
|
23
|
+
}: DatePickerProps) {
|
|
24
|
+
const [open, setOpen] = useState(false);
|
|
25
|
+
|
|
26
|
+
const date = value ? parse(value, "yyyy-MM-dd", new Date()) : undefined;
|
|
27
|
+
const validDate = date && isValid(date) ? date : undefined;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
31
|
+
<PopoverTrigger asChild>
|
|
32
|
+
<Button
|
|
33
|
+
variant="outline"
|
|
34
|
+
className={cn(
|
|
35
|
+
"h-8 justify-start text-start font-normal text-xs gap-2 px-2.5",
|
|
36
|
+
!validDate && "text-muted-foreground",
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
>
|
|
40
|
+
<IconCalendar className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
|
41
|
+
{validDate ? (
|
|
42
|
+
format(validDate, "MMM d, yyyy")
|
|
43
|
+
) : (
|
|
44
|
+
<span>{placeholder}</span>
|
|
45
|
+
)}
|
|
46
|
+
</Button>
|
|
47
|
+
</PopoverTrigger>
|
|
48
|
+
<PopoverContent className="w-auto p-0" align="start">
|
|
49
|
+
<Calendar
|
|
50
|
+
mode="single"
|
|
51
|
+
selected={validDate}
|
|
52
|
+
onSelect={(day) => {
|
|
53
|
+
if (day) {
|
|
54
|
+
onChange(format(day, "yyyy-MM-dd"));
|
|
55
|
+
setOpen(false);
|
|
56
|
+
}
|
|
57
|
+
}}
|
|
58
|
+
defaultMonth={validDate}
|
|
59
|
+
autoFocus
|
|
60
|
+
/>
|
|
61
|
+
</PopoverContent>
|
|
62
|
+
</Popover>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ForwardedRef, ReactElement } from "react";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { DialogOverlay } from "./dialog.js";
|
|
5
|
+
|
|
6
|
+
type OverlayProps = React.ComponentProps<typeof DialogOverlay>;
|
|
7
|
+
|
|
8
|
+
function renderDialogOverlay(props: OverlayProps): ReactElement<{
|
|
9
|
+
className: string;
|
|
10
|
+
}> {
|
|
11
|
+
const overlay = DialogOverlay as unknown as {
|
|
12
|
+
render: (
|
|
13
|
+
props: OverlayProps,
|
|
14
|
+
ref: ForwardedRef<HTMLDivElement>,
|
|
15
|
+
) => ReactElement<{ className: string }>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return overlay.render(props, null);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("DialogOverlay", () => {
|
|
22
|
+
it("transitions only the backdrop blur for instant dialogs", () => {
|
|
23
|
+
const overlay = renderDialogOverlay({ motion: "instant" });
|
|
24
|
+
|
|
25
|
+
expect(overlay.props.className).toContain(
|
|
26
|
+
"transition-[backdrop-filter] duration-[1000ms] ease-[var(--ease-out-strong)]",
|
|
27
|
+
);
|
|
28
|
+
expect(overlay.props.className).toContain(
|
|
29
|
+
"starting:[backdrop-filter:blur(0px)]",
|
|
30
|
+
);
|
|
31
|
+
expect(overlay.props.className).toContain("backdrop-blur-[1px]");
|
|
32
|
+
expect(overlay.props.className).not.toContain("animate-in");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("keeps standard dialog overlay motion unchanged", () => {
|
|
36
|
+
const overlay = renderDialogOverlay({});
|
|
37
|
+
|
|
38
|
+
expect(overlay.props.className).toContain("animate-in");
|
|
39
|
+
expect(overlay.props.className).not.toContain(
|
|
40
|
+
"transition-[backdrop-filter]",
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
2
|
+
import { IconX } from "@tabler/icons-react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../utils.js";
|
|
6
|
+
|
|
7
|
+
const Dialog = DialogPrimitive.Root;
|
|
8
|
+
|
|
9
|
+
const DialogTrigger = DialogPrimitive.Trigger;
|
|
10
|
+
|
|
11
|
+
const DialogPortal = DialogPrimitive.Portal;
|
|
12
|
+
|
|
13
|
+
const DialogClose = DialogPrimitive.Close;
|
|
14
|
+
|
|
15
|
+
type DialogMotion = "default" | "instant";
|
|
16
|
+
|
|
17
|
+
interface DialogOverlayProps extends React.ComponentPropsWithoutRef<
|
|
18
|
+
typeof DialogPrimitive.Overlay
|
|
19
|
+
> {
|
|
20
|
+
motion?: DialogMotion;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const DialogOverlay = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
|
25
|
+
DialogOverlayProps
|
|
26
|
+
>(({ className, motion = "default", ...props }, ref) => (
|
|
27
|
+
<DialogPrimitive.Overlay
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={cn(
|
|
30
|
+
"fixed inset-0 z-[270] bg-background/45 backdrop-blur-[1px]",
|
|
31
|
+
motion === "default" &&
|
|
32
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
33
|
+
motion === "instant" &&
|
|
34
|
+
"transition-[backdrop-filter] duration-[1000ms] ease-[var(--ease-out-strong)] starting:[backdrop-filter:blur(0px)] motion-reduce:transition-none",
|
|
35
|
+
className,
|
|
36
|
+
)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
));
|
|
40
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
41
|
+
|
|
42
|
+
interface DialogContentProps extends React.ComponentPropsWithoutRef<
|
|
43
|
+
typeof DialogPrimitive.Content
|
|
44
|
+
> {
|
|
45
|
+
// When true, the default close button is hidden; the caller is responsible for
|
|
46
|
+
// providing a dismiss affordance (e.g. a footer Cancel action).
|
|
47
|
+
hideClose?: boolean;
|
|
48
|
+
// High-frequency keyboard surfaces can opt out without changing ordinary dialogs.
|
|
49
|
+
motion?: DialogMotion;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const DialogContent = React.forwardRef<
|
|
53
|
+
React.ElementRef<typeof DialogPrimitive.Content>,
|
|
54
|
+
DialogContentProps
|
|
55
|
+
>(({ className, children, hideClose, motion = "default", ...props }, ref) => (
|
|
56
|
+
<DialogPortal>
|
|
57
|
+
<DialogOverlay motion={motion} />
|
|
58
|
+
<DialogPrimitive.Content
|
|
59
|
+
ref={ref}
|
|
60
|
+
className={cn(
|
|
61
|
+
"fixed left-1/2 top-1/2 z-[280] grid max-h-[min(760px,calc(100vh-32px))] w-[calc(100vw-24px)] max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 overflow-hidden rounded-lg border border-border bg-background p-6 text-foreground shadow-lg outline-none",
|
|
62
|
+
motion === "default" &&
|
|
63
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-150 data-[state=open]:duration-200 data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
64
|
+
className,
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
>
|
|
68
|
+
{children}
|
|
69
|
+
{!hideClose && (
|
|
70
|
+
<DialogPrimitive.Close className="absolute end-3 top-3 flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring">
|
|
71
|
+
<IconX className="h-4 w-4" />
|
|
72
|
+
<span className="sr-only">Close</span>
|
|
73
|
+
</DialogPrimitive.Close>
|
|
74
|
+
)}
|
|
75
|
+
</DialogPrimitive.Content>
|
|
76
|
+
</DialogPortal>
|
|
77
|
+
));
|
|
78
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
79
|
+
|
|
80
|
+
const DialogHeader = ({
|
|
81
|
+
className,
|
|
82
|
+
...props
|
|
83
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
84
|
+
<div
|
|
85
|
+
className={cn(
|
|
86
|
+
"flex flex-col space-y-1.5 text-center sm:text-start",
|
|
87
|
+
className,
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
DialogHeader.displayName = "DialogHeader";
|
|
93
|
+
|
|
94
|
+
const DialogFooter = ({
|
|
95
|
+
className,
|
|
96
|
+
...props
|
|
97
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
98
|
+
<div
|
|
99
|
+
className={cn(
|
|
100
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
101
|
+
className,
|
|
102
|
+
)}
|
|
103
|
+
{...props}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
DialogFooter.displayName = "DialogFooter";
|
|
107
|
+
|
|
108
|
+
const DialogTitle = React.forwardRef<
|
|
109
|
+
React.ElementRef<typeof DialogPrimitive.Title>,
|
|
110
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
|
111
|
+
>(({ className, ...props }, ref) => (
|
|
112
|
+
<DialogPrimitive.Title
|
|
113
|
+
ref={ref}
|
|
114
|
+
className={cn(
|
|
115
|
+
"text-lg font-semibold leading-none tracking-tight text-foreground",
|
|
116
|
+
className,
|
|
117
|
+
)}
|
|
118
|
+
{...props}
|
|
119
|
+
/>
|
|
120
|
+
));
|
|
121
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
122
|
+
|
|
123
|
+
const DialogDescription = React.forwardRef<
|
|
124
|
+
React.ElementRef<typeof DialogPrimitive.Description>,
|
|
125
|
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
|
126
|
+
>(({ className, ...props }, ref) => (
|
|
127
|
+
<DialogPrimitive.Description
|
|
128
|
+
ref={ref}
|
|
129
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
130
|
+
{...props}
|
|
131
|
+
/>
|
|
132
|
+
));
|
|
133
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
134
|
+
|
|
135
|
+
export {
|
|
136
|
+
Dialog,
|
|
137
|
+
DialogPortal,
|
|
138
|
+
DialogOverlay,
|
|
139
|
+
DialogClose,
|
|
140
|
+
DialogTrigger,
|
|
141
|
+
DialogContent,
|
|
142
|
+
DialogHeader,
|
|
143
|
+
DialogFooter,
|
|
144
|
+
DialogTitle,
|
|
145
|
+
DialogDescription,
|
|
146
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Drawer as DrawerPrimitive } from "vaul";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../utils.js";
|
|
5
|
+
|
|
6
|
+
const Drawer = ({
|
|
7
|
+
shouldScaleBackground = true,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
|
|
10
|
+
<DrawerPrimitive.Root
|
|
11
|
+
shouldScaleBackground={shouldScaleBackground}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
Drawer.displayName = "Drawer";
|
|
16
|
+
|
|
17
|
+
type DrawerButtonProps = React.ComponentPropsWithoutRef<"button"> & {
|
|
18
|
+
asChild?: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const DrawerTrigger = React.forwardRef<HTMLButtonElement, DrawerButtonProps>(
|
|
22
|
+
(props, ref) => <DrawerPrimitive.Trigger ref={ref} {...props} />,
|
|
23
|
+
);
|
|
24
|
+
DrawerTrigger.displayName = DrawerPrimitive.Trigger.displayName;
|
|
25
|
+
|
|
26
|
+
const DrawerPortal = DrawerPrimitive.Portal;
|
|
27
|
+
|
|
28
|
+
const DrawerClose = React.forwardRef<HTMLButtonElement, DrawerButtonProps>(
|
|
29
|
+
(props, ref) => <DrawerPrimitive.Close ref={ref} {...props} />,
|
|
30
|
+
);
|
|
31
|
+
DrawerClose.displayName = DrawerPrimitive.Close.displayName;
|
|
32
|
+
|
|
33
|
+
type DrawerDivProps = React.ComponentPropsWithoutRef<"div"> & {
|
|
34
|
+
asChild?: boolean;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const DrawerOverlay = React.forwardRef<HTMLDivElement, DrawerDivProps>(
|
|
38
|
+
({ className, ...props }, ref) => (
|
|
39
|
+
<DrawerPrimitive.Overlay
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={cn("fixed inset-0 z-[250] bg-black/80", className)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
|
|
47
|
+
|
|
48
|
+
const DrawerContent = React.forwardRef<HTMLDivElement, DrawerDivProps>(
|
|
49
|
+
({ className, children, ...props }, ref) => (
|
|
50
|
+
<DrawerPortal>
|
|
51
|
+
<DrawerOverlay />
|
|
52
|
+
<DrawerPrimitive.Content
|
|
53
|
+
ref={ref}
|
|
54
|
+
className={cn(
|
|
55
|
+
"fixed inset-x-0 bottom-0 z-[250] mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background",
|
|
56
|
+
className,
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
>
|
|
60
|
+
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
|
61
|
+
{children}
|
|
62
|
+
</DrawerPrimitive.Content>
|
|
63
|
+
</DrawerPortal>
|
|
64
|
+
),
|
|
65
|
+
);
|
|
66
|
+
DrawerContent.displayName = "DrawerContent";
|
|
67
|
+
|
|
68
|
+
const DrawerHeader = ({
|
|
69
|
+
className,
|
|
70
|
+
...props
|
|
71
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
72
|
+
<div
|
|
73
|
+
className={cn("grid gap-1.5 p-4 text-center sm:text-start", className)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
78
|
+
|
|
79
|
+
const DrawerFooter = ({
|
|
80
|
+
className,
|
|
81
|
+
...props
|
|
82
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
83
|
+
<div
|
|
84
|
+
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
|
85
|
+
{...props}
|
|
86
|
+
/>
|
|
87
|
+
);
|
|
88
|
+
DrawerFooter.displayName = "DrawerFooter";
|
|
89
|
+
|
|
90
|
+
const DrawerTitle = React.forwardRef<HTMLDivElement, DrawerDivProps>(
|
|
91
|
+
({ className, ...props }, ref) => (
|
|
92
|
+
<DrawerPrimitive.Title
|
|
93
|
+
ref={ref}
|
|
94
|
+
className={cn(
|
|
95
|
+
"text-lg font-semibold leading-none tracking-tight",
|
|
96
|
+
className,
|
|
97
|
+
)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
),
|
|
101
|
+
);
|
|
102
|
+
DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
|
|
103
|
+
|
|
104
|
+
const DrawerDescription = React.forwardRef<HTMLDivElement, DrawerDivProps>(
|
|
105
|
+
({ className, ...props }, ref) => (
|
|
106
|
+
<DrawerPrimitive.Description
|
|
107
|
+
ref={ref}
|
|
108
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
109
|
+
{...props}
|
|
110
|
+
/>
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
|
|
114
|
+
|
|
115
|
+
export {
|
|
116
|
+
Drawer,
|
|
117
|
+
DrawerPortal,
|
|
118
|
+
DrawerOverlay,
|
|
119
|
+
DrawerTrigger,
|
|
120
|
+
DrawerClose,
|
|
121
|
+
DrawerContent,
|
|
122
|
+
DrawerHeader,
|
|
123
|
+
DrawerFooter,
|
|
124
|
+
DrawerTitle,
|
|
125
|
+
DrawerDescription,
|
|
126
|
+
};
|