-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathCopilotSidebarView.tsx
More file actions
129 lines (110 loc) · 3.99 KB
/
Copy pathCopilotSidebarView.tsx
File metadata and controls
129 lines (110 loc) · 3.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import React, { useEffect, useRef, useState } from "react";
import CopilotChatView, { CopilotChatViewProps } from "./CopilotChatView";
import { useCopilotChatConfiguration } from "@/providers/CopilotChatConfigurationProvider";
import CopilotChatToggleButton from "./CopilotChatToggleButton";
import { cn } from "@/lib/utils";
import { CopilotModalHeader } from "./CopilotModalHeader";
import { renderSlot, SlotValue } from "@/lib/slots";
const DEFAULT_SIDEBAR_WIDTH = 480;
const SIDEBAR_TRANSITION_MS = 260;
export type CopilotSidebarViewProps = CopilotChatViewProps & {
header?: SlotValue<typeof CopilotModalHeader>;
width?: number | string;
};
export function CopilotSidebarView({ header, width, ...props }: CopilotSidebarViewProps) {
const configuration = useCopilotChatConfiguration();
const isSidebarOpen = configuration?.isModalOpen ?? false;
const sidebarRef = useRef<HTMLDivElement | null>(null);
const [sidebarWidth, setSidebarWidth] = useState<number | string>(width ?? DEFAULT_SIDEBAR_WIDTH);
// Helper to convert width to CSS value
const widthToCss = (w: number | string): string => {
return typeof w === "number" ? `${w}px` : w;
};
// Helper to extract numeric value for body margin (only works with px values)
const widthToMargin = (w: number | string): string => {
if (typeof w === "number") {
return `${w}px`;
}
// For string values, use as-is (assumes valid CSS unit)
return w;
};
useEffect(() => {
// If width is explicitly provided, don't measure
if (width !== undefined) {
return;
}
if (typeof window === "undefined") {
return;
}
const element = sidebarRef.current;
if (!element) {
return;
}
const updateWidth = () => {
const rect = element.getBoundingClientRect();
if (rect.width > 0) {
setSidebarWidth(rect.width);
}
};
updateWidth();
if (typeof ResizeObserver !== "undefined") {
const observer = new ResizeObserver(() => updateWidth());
observer.observe(element);
return () => observer.disconnect();
}
window.addEventListener("resize", updateWidth);
return () => window.removeEventListener("resize", updateWidth);
}, [width]);
const headerElement = renderSlot(header, CopilotModalHeader, {});
return (
<>
{isSidebarOpen && (
<style
dangerouslySetInnerHTML={{
__html: `
@media (min-width: 768px) {
body {
margin-inline-end: ${widthToMargin(sidebarWidth)};
transition: margin-inline-end ${SIDEBAR_TRANSITION_MS}ms ease;
}
}`,
}}
/>
)}
<CopilotChatToggleButton />
<aside
ref={sidebarRef}
data-copilot-sidebar
className={cn(
"fixed right-0 top-0 z-[1200] flex",
// Height with dvh fallback and safe area support
"h-[100vh] h-[100dvh] max-h-screen",
// Responsive width: full on mobile, custom on desktop
"w-full",
"border-l border-border bg-background text-foreground shadow-xl",
"transition-transform duration-300 ease-out",
isSidebarOpen ? "translate-x-0" : "translate-x-full pointer-events-none",
)}
style={{
// Use CSS custom property for responsive width
["--sidebar-width" as string]: widthToCss(sidebarWidth),
// Safe area insets for iOS
paddingTop: "env(safe-area-inset-top)",
paddingBottom: "env(safe-area-inset-bottom)",
} as React.CSSProperties}
aria-hidden={!isSidebarOpen}
aria-label="Copilot chat sidebar"
role="complementary"
>
<div className="flex h-full w-full flex-col overflow-hidden">
{headerElement}
<div className="flex-1 overflow-hidden" data-sidebar-chat>
<CopilotChatView {...props} />
</div>
</div>
</aside>
</>
);
}
CopilotSidebarView.displayName = "CopilotSidebarView";
export default CopilotSidebarView;