@super_studio/ecforce-ai-agent-react 0.1.4 → 0.2.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.
@@ -0,0 +1,103 @@
1
+ @import "./tooltip.css";
2
+
3
+ /* Chatbot Trigger Button */
4
+ .chatbot-trigger {
5
+ position: fixed;
6
+ bottom: 1rem;
7
+ right: 1rem;
8
+ z-index: 50;
9
+ border-radius: 9999px;
10
+ border: 1px solid #f0f2f7;
11
+ background-color: #ffffff;
12
+ color: #0061ff;
13
+ box-shadow:
14
+ 0 1px 3px 0 rgba(0, 0, 0, 0.1),
15
+ 0 1px 2px 0 rgba(0, 0, 0, 0.06);
16
+ transition: colors 200ms ease-in-out;
17
+ cursor: pointer;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ height: 40px;
22
+ width: 40px;
23
+ }
24
+
25
+ .chatbot-trigger:hover {
26
+ background-color: #f0f2f7;
27
+ }
28
+
29
+ /* Sheet Content */
30
+ .chatbot-sheet-content {
31
+ position: fixed;
32
+ top: 0;
33
+ right: 0;
34
+ bottom: 0;
35
+ z-index: 50;
36
+ background-color: #ffffff;
37
+ box-shadow:
38
+ 0 10px 15px -3px rgba(0, 0, 0, 0.1),
39
+ 0 4px 6px -2px rgba(0, 0, 0, 0.05);
40
+ border-left: 1px solid #e5e7eb;
41
+ width: var(--sheet-width, 400px);
42
+ height: 100vh;
43
+ transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1);
44
+ }
45
+
46
+ .chatbot-sheet-content.chatbot-sheet-expanded {
47
+ --sheet-width: 848px;
48
+ }
49
+
50
+ /* Sheet Header */
51
+ .chatbot-sheet-header {
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: 0.375rem;
55
+ padding: 1rem;
56
+ }
57
+
58
+ /* Sheet Footer */
59
+ .chatbot-sheet-footer {
60
+ margin-top: auto;
61
+ display: flex;
62
+ flex-direction: column;
63
+ gap: 0.5rem;
64
+ padding: 1rem;
65
+ }
66
+
67
+ /* Sheet Title */
68
+ .chatbot-sheet-title {
69
+ position: absolute;
70
+ width: 1px;
71
+ height: 1px;
72
+ padding: 0;
73
+ margin: -1px;
74
+ overflow: hidden;
75
+ clip: rect(0, 0, 0, 0);
76
+ white-space: nowrap;
77
+ border: 0;
78
+ }
79
+
80
+ /* Sheet Description */
81
+ .chatbot-sheet-description {
82
+ color: #6b7280;
83
+ font-size: 0.875rem;
84
+ }
85
+
86
+ /* Sheet Close */
87
+ .chatbot-sheet-close {
88
+ cursor: pointer;
89
+ }
90
+
91
+ /* Initial mount animation */
92
+ @keyframes chatbot-sheet-enter {
93
+ from {
94
+ transform: translateX(100%);
95
+ }
96
+ to {
97
+ transform: translateX(0);
98
+ }
99
+ }
100
+
101
+ .chatbot-sheet-content[data-state="open"] {
102
+ animation: chatbot-sheet-enter 300ms cubic-bezier(0.4, 0, 0.2, 1);
103
+ }
@@ -0,0 +1,61 @@
1
+ import { forwardRef, type CSSProperties } from "react";
2
+ import {
3
+ AgentFrame,
4
+ type AgentElement,
5
+ type AgentFrameProps,
6
+ } from "./agent-frame";
7
+ import {
8
+ Sheet,
9
+ SheetContent,
10
+ SheetProvider,
11
+ SheetTitle,
12
+ SheetTrigger,
13
+ } from "./sheet";
14
+ import { Tooltip } from "./tooltip";
15
+
16
+ type ChatbotSheetProps = AgentFrameProps & {
17
+ sheetStyle?: CSSProperties;
18
+ };
19
+
20
+ export const ChatbotSheet = forwardRef<AgentElement, ChatbotSheetProps>(
21
+ ({ sheetStyle, ...props }, ref) => {
22
+ return (
23
+ <SheetProvider>
24
+ <Sheet>
25
+ <Tooltip
26
+ side="top"
27
+ align="end"
28
+ content="AIに質問してみましょう"
29
+ trigger={
30
+ <SheetTrigger>
31
+ <EcforceAiIcon />
32
+ </SheetTrigger>
33
+ }
34
+ />
35
+ <SheetContent style={sheetStyle}>
36
+ <SheetTitle>AIに質問してみましょう</SheetTitle>
37
+ <AgentFrame {...props} ref={ref} />
38
+ </SheetContent>
39
+ </Sheet>
40
+ </SheetProvider>
41
+ );
42
+ },
43
+ );
44
+
45
+ function EcforceAiIcon() {
46
+ return (
47
+ <svg
48
+ width="18"
49
+ height="18"
50
+ viewBox="0 0 24 24"
51
+ xmlns="http://www.w3.org/2000/svg"
52
+ fill="currentColor"
53
+ >
54
+ <path
55
+ fillRule="evenodd"
56
+ clipRule="evenodd"
57
+ d="M9.2 13.704s3.7-.812 4.325-3.74c.624-2.93.76-3.364 1.112-3.364.352 0 .42.309.5.618.08.309.51 2.276.613 2.745.102.47.783 2.93 3.712 3.616 2.93.686 3.338.697 3.338 1.12 0 .459-2.361.859-3.338 1.122-.976.263-3.042 1.007-3.587 3.49-.534 2.482-.681 3.489-1.238 3.489-.556 0-.545-.584-.613-.87-.068-.286-.34-1.819-.5-2.494-.147-.675-1.044-2.837-2.962-3.363-1.93-.527-3.962-.847-3.962-1.373s2.134-.824 2.6-.995ZM2.462 4.613s1.785-.39 2.08-1.798c.296-1.409.365-1.615.535-1.615.17 0 .205.149.239.298s.25 1.088.296 1.317c.045.229.375 1.409 1.785 1.74 1.41.333 1.603.333 1.603.54 0 .217-1.137.412-1.603.538-.466.126-1.467.48-1.729 1.683C5.407 8.507 5.338 9 5.078 9c-.262 0-.262-.286-.296-.424-.035-.137-.16-.882-.24-1.202-.079-.321-.5-1.363-1.432-1.615-.932-.252-1.91-.413-1.91-.665 0-.252 1.023-.4 1.25-.48h.012Z"
58
+ />
59
+ </svg>
60
+ );
61
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  export * from "./agent-frame";
2
2
  export * from "./chatbot-popover";
3
3
  export * from "./popover";
4
+ export * from "./chatbot-sheet";
5
+ export * from "./sheet";
6
+ export * from "./tooltip";
package/src/sheet.tsx ADDED
@@ -0,0 +1,198 @@
1
+ "use client";
2
+
3
+ import * as SheetPrimitive from "@radix-ui/react-dialog";
4
+ import * as React from "react";
5
+
6
+ interface SheetContextType {
7
+ hasOpened: boolean;
8
+ sheetOpen: boolean;
9
+ setSheetOpen: (open: boolean) => void;
10
+ isExpanded: boolean;
11
+ setIsExpanded: (expanded: boolean) => void;
12
+ }
13
+
14
+ const SheetContext = React.createContext<SheetContextType | undefined>(
15
+ undefined,
16
+ );
17
+
18
+ export function useSheet() {
19
+ const context = React.useContext(SheetContext);
20
+ if (!context) {
21
+ throw new Error("useSheet must be used within a SheetProvider");
22
+ }
23
+ return context;
24
+ }
25
+
26
+ interface SheetProviderProps {
27
+ children: React.ReactNode;
28
+ defaultWidth?: number;
29
+ minWidth?: number;
30
+ maxWidth?: number;
31
+ }
32
+
33
+ export function SheetProvider({ children }: SheetProviderProps) {
34
+ const [hasOpened, setHasOpened] = React.useState(false);
35
+ const [sheetOpen, setSheetOpen] = React.useState(false);
36
+ const [isExpanded, setIsExpanded] = React.useState(false);
37
+
38
+ React.useEffect(() => {
39
+ if (sheetOpen) {
40
+ setHasOpened(true);
41
+ }
42
+ }, [sheetOpen]);
43
+
44
+ const value = {
45
+ hasOpened,
46
+ sheetOpen,
47
+ setSheetOpen,
48
+ isExpanded,
49
+ setIsExpanded,
50
+ };
51
+
52
+ return (
53
+ <SheetContext.Provider value={value}>{children}</SheetContext.Provider>
54
+ );
55
+ }
56
+
57
+ function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
58
+ const { sheetOpen, setSheetOpen } = useSheet();
59
+ return (
60
+ <SheetPrimitive.Root
61
+ data-slot="sheet"
62
+ modal={false}
63
+ open={sheetOpen}
64
+ onOpenChange={setSheetOpen}
65
+ {...props}
66
+ />
67
+ );
68
+ }
69
+
70
+ const SheetTrigger = React.forwardRef<
71
+ React.ElementRef<typeof SheetPrimitive.Trigger>,
72
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Trigger>
73
+ >(({ className, ...props }, ref) => (
74
+ <SheetPrimitive.Trigger
75
+ ref={ref}
76
+ data-slot="sheet-trigger"
77
+ className={`chatbot-trigger ${className || ""}`}
78
+ {...props}
79
+ />
80
+ ));
81
+ SheetTrigger.displayName = SheetPrimitive.Trigger.displayName;
82
+
83
+ const SheetClose = React.forwardRef<
84
+ React.ElementRef<typeof SheetPrimitive.Close>,
85
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Close>
86
+ >(({ className, ...props }, ref) => (
87
+ <SheetPrimitive.Close
88
+ ref={ref}
89
+ data-slot="sheet-close"
90
+ className={`chatbot-sheet-close ${className || ""}`}
91
+ {...props}
92
+ />
93
+ ));
94
+ SheetClose.displayName = SheetPrimitive.Close.displayName;
95
+
96
+ function SheetPortal({
97
+ ...props
98
+ }: React.ComponentProps<typeof SheetPrimitive.Portal>) {
99
+ return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
100
+ }
101
+
102
+ const SheetContent = React.forwardRef<
103
+ React.ElementRef<typeof SheetPrimitive.Content>,
104
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>
105
+ >(({ className, children, style, ...props }, ref) => {
106
+ const { isExpanded, sheetOpen, hasOpened } = useSheet();
107
+ const width = isExpanded ? "848px" : "400px";
108
+
109
+ // Use transform for better performance instead of right property
110
+ const translateX = sheetOpen ? "0" : "100%";
111
+
112
+ // Create style object with CSS custom property for smoother animation
113
+ const contentStyle = {
114
+ "--sheet-width": width,
115
+ transform: `translateX(${translateX})`,
116
+ ...style,
117
+ } as React.CSSProperties & { "--sheet-width": string };
118
+
119
+ return (
120
+ <SheetPortal forceMount={hasOpened || undefined}>
121
+ <SheetPrimitive.Content
122
+ ref={ref}
123
+ data-slot="sheet-content"
124
+ forceMount={hasOpened || undefined}
125
+ onInteractOutside={(e) => e.preventDefault()}
126
+ className={`chatbot-sheet-content ${isExpanded ? "chatbot-sheet-expanded" : ""} ${className || ""}`}
127
+ style={contentStyle}
128
+ {...props}
129
+ >
130
+ {children}
131
+ </SheetPrimitive.Content>
132
+ </SheetPortal>
133
+ );
134
+ });
135
+ SheetContent.displayName = SheetPrimitive.Content.displayName;
136
+
137
+ const SheetHeader = React.forwardRef<
138
+ HTMLDivElement,
139
+ React.HTMLAttributes<HTMLDivElement>
140
+ >(({ className, ...props }, ref) => (
141
+ <div
142
+ ref={ref}
143
+ data-slot="sheet-header"
144
+ className={`chatbot-sheet-header ${className || ""}`}
145
+ {...props}
146
+ />
147
+ ));
148
+ SheetHeader.displayName = "SheetHeader";
149
+
150
+ const SheetFooter = React.forwardRef<
151
+ HTMLDivElement,
152
+ React.HTMLAttributes<HTMLDivElement>
153
+ >(({ className, ...props }, ref) => (
154
+ <div
155
+ ref={ref}
156
+ data-slot="sheet-footer"
157
+ className={`chatbot-sheet-footer ${className || ""}`}
158
+ {...props}
159
+ />
160
+ ));
161
+ SheetFooter.displayName = "SheetFooter";
162
+
163
+ const SheetTitle = React.forwardRef<
164
+ React.ElementRef<typeof SheetPrimitive.Title>,
165
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
166
+ >(({ className, ...props }, ref) => (
167
+ <SheetPrimitive.Title
168
+ ref={ref}
169
+ data-slot="sheet-title"
170
+ className={`chatbot-sheet-title ${className || ""}`}
171
+ {...props}
172
+ />
173
+ ));
174
+ SheetTitle.displayName = SheetPrimitive.Title.displayName;
175
+
176
+ const SheetDescription = React.forwardRef<
177
+ React.ElementRef<typeof SheetPrimitive.Description>,
178
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
179
+ >(({ className, ...props }, ref) => (
180
+ <SheetPrimitive.Description
181
+ ref={ref}
182
+ data-slot="sheet-description"
183
+ className={`chatbot-sheet-description ${className || ""}`}
184
+ {...props}
185
+ />
186
+ ));
187
+ SheetDescription.displayName = SheetPrimitive.Description.displayName;
188
+
189
+ export {
190
+ Sheet,
191
+ SheetTrigger,
192
+ SheetClose,
193
+ SheetContent,
194
+ SheetHeader,
195
+ SheetFooter,
196
+ SheetTitle,
197
+ SheetDescription,
198
+ };
@@ -0,0 +1,18 @@
1
+ /* Tooltip Content */
2
+ .tooltip-content {
3
+ background-color: #1f2937;
4
+ z-index: 50;
5
+ max-width: 320px;
6
+ border-radius: 0.125rem;
7
+ padding: 0.5rem;
8
+ font-size: 0.625rem;
9
+ font-weight: 400;
10
+ line-height: 1.25;
11
+ color: #ffffff;
12
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
13
+ }
14
+
15
+ /* Arrow styling can be customized via arrowClassName prop */
16
+ .tooltip-arrow {
17
+ fill: #1f2937;
18
+ }
@@ -0,0 +1,88 @@
1
+ "use client";
2
+
3
+ import {
4
+ Arrow,
5
+ Content,
6
+ Portal,
7
+ Provider,
8
+ Root,
9
+ Trigger,
10
+ } from "@radix-ui/react-tooltip";
11
+ import "./tooltip.css";
12
+
13
+ type Side = "top" | "right" | "bottom" | "left";
14
+ type Align = "start" | "center" | "end";
15
+
16
+ type TooltipProps = {
17
+ content: React.ReactNode;
18
+ side?: Side;
19
+ align?: Align;
20
+ trigger: React.ReactNode;
21
+ disabled?: boolean;
22
+ delayDuration?: number;
23
+ skipDelayDuration?: number;
24
+ className?: string;
25
+ open?: boolean;
26
+ alignOffset?: number;
27
+ sideOffset?: number;
28
+ arrowClassName?: string;
29
+ keepTooltipOpen?: boolean;
30
+ };
31
+
32
+ export function Tooltip({
33
+ content,
34
+ trigger,
35
+ side,
36
+ align,
37
+ disabled,
38
+ delayDuration,
39
+ skipDelayDuration,
40
+ className,
41
+ open,
42
+ alignOffset,
43
+ sideOffset,
44
+ arrowClassName,
45
+ keepTooltipOpen,
46
+ }: TooltipProps) {
47
+ return (
48
+ <Provider
49
+ delayDuration={delayDuration ?? 200}
50
+ skipDelayDuration={skipDelayDuration ?? 200}
51
+ >
52
+ <Root open={disabled ? false : open}>
53
+ <Trigger
54
+ onClick={
55
+ keepTooltipOpen
56
+ ? (event) => {
57
+ event.preventDefault();
58
+ }
59
+ : undefined
60
+ }
61
+ asChild
62
+ >
63
+ {trigger}
64
+ </Trigger>
65
+
66
+ <Portal>
67
+ <Content
68
+ className={`tooltip-content ${className || ""}`}
69
+ side={side}
70
+ align={align}
71
+ alignOffset={alignOffset}
72
+ sideOffset={sideOffset}
73
+ onPointerDownOutside={
74
+ keepTooltipOpen
75
+ ? (event) => {
76
+ event.preventDefault();
77
+ }
78
+ : undefined
79
+ }
80
+ >
81
+ <Arrow className={arrowClassName} />
82
+ {content}
83
+ </Content>
84
+ </Portal>
85
+ </Root>
86
+ </Provider>
87
+ );
88
+ }